From 335a86377e6fecfc346205de032550479921cdf7 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 12 Feb 2019 18:22:58 +0100 Subject: [PATCH 001/211] A start on followers --- src/d_player.h | 3 +++ src/dehacked.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/doomdef.h | 2 +- src/r_things.c | 3 +++ src/r_things.h | 26 ++++++++++++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index 27fdef8dc..e35a79723 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -442,6 +442,9 @@ typedef struct player_s // SRB2kart UINT8 kartspeed; // Kart speed stat between 1 and 9 UINT8 kartweight; // Kart weight stat between 1 and 9 + + mobj_t *follower; // Kart: This is the follower object we have. (If any) + // fixed_t normalspeed; // Normal ground diff --git a/src/dehacked.c b/src/dehacked.c index b9e29bc47..2101e8e04 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -33,6 +33,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "d_clisrv.h" +#include "r_things.h" // for followers #include "m_cond.h" @@ -684,6 +685,71 @@ static void readfreeslots(MYFILE *f) Z_Free(s); } +// This here is our current only way to make followers. +INT32 numfollowers = 0; + +static void readfollower(MYFILE *f) +{ + + if (numfollowers > MAXSKINS) + { + CONS_Printf("Error: Too many followers, cannot add anymore.\n"); + return; + } + + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word, *word2; + char *tmp; + + CONS_Printf("Adding follower, please bear with me...\n"); + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + + if (fastcmp(word, "ATANGLE")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); + followers[numfollowers].atangle = (INT32)atoi(word2); + } + else if (fastcmp(word, "ZOFFSET") || (fastcmp(word, "ZOFFS"))) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE); + followers[numfollowers].zoffs = (INT32)atoi(word2); + } + else + deh_warning("Follower %d: unknown word '%s'", numfollowers, word); + } + } while (!myfeof(f)); // finish when the line is empty + + CONS_Printf("We are done adding the follower.\n"); + numfollowers++; + Z_Free(s); +} + static void readthing(MYFILE *f, INT32 num) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); @@ -3469,6 +3535,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) readpatch(f, word2, wad); DEH_WriteUndoline(word, word2, UNDO_HEADER); } + else if (fastcmp(word, "FOLLOWER")) + { + readfollower(f); // at the same time this will be our only way to ADD followers for now. Yikes. + DEH_WriteUndoline(word, word2, UNDO_HEADER); + } else if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT")) { if (i == 0 && word2[0] != '0') // If word2 isn't a number diff --git a/src/doomdef.h b/src/doomdef.h index 70e521b15..9555787c3 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -140,7 +140,7 @@ extern FILE *logstream; #endif -//#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 +#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 #ifdef DEVELOP #define VERSION 0 // Game version #define SUBVERSION 0 // more precise version number diff --git a/src/r_things.c b/src/r_things.c index d6234d4b3..7607bec1c 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2499,7 +2499,10 @@ void R_DrawMasked(void) // // ========================================================================== +// We can assume those are tied to skins somewhat, hence why they're defined here. INT32 numskins = 0; +follower_t followers[MAXSKINS]; + skin_t skins[MAXSKINS]; // FIXTHIS: don't work because it must be inistilised before the config load //#define SKINVALUES diff --git a/src/r_things.h b/src/r_things.h index 53784415f..1aaac284d 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -115,6 +115,30 @@ typedef struct sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table } skin_t; +// +// for followers. +// +// We'll define these here because they're really just a mobj that'll follow some rules behind a player +// +typedef struct follower_s +{ + char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this. + + // some position shenanigans: + INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. + INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. + + // from there on out, everything is STATES to allow customization + // these are only set once when the action is performed and are then free to animate however they want. + + INT32 idlestate; // state when the player is at a standstill + INT32 followstate; // state when the player is moving + INT32 hurtstate; // state when the player is being hurt + INT32 winstate; // state when the player has won + INT32 losestate; // state when the player has lost + +} follower_t; + // ----------- // NOT SKINS STUFF ! // ----------- @@ -193,6 +217,8 @@ typedef struct drawnode_s extern INT32 numskins; extern skin_t skins[MAXSKINS]; +extern INT32 numfollowers; +extern follower_t followers[MAXSKINS]; // again, use the same rules as skins, no reason not to. void SetPlayerSkin(INT32 playernum,const char *skinname); void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 From 4721243ae5346aadae896b7b17a00d7c43588b19 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 13 Feb 2019 10:06:52 +0100 Subject: [PATCH 002/211] I needed to commit here so I could switch branches to fix magnet hand xd --- src/d_netcmd.c | 129 +++++++++++++++++++++++++++++++++++--- src/d_netcmd.h | 4 ++ src/d_player.h | 1 + src/dehacked.c | 145 ++++++++++++++++++++++++++++++++++++------- src/doomdef.h | 2 +- src/m_menu.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++-- src/r_things.c | 64 ++++++++++++++++++- src/r_things.h | 15 +++-- 8 files changed, 483 insertions(+), 40 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 438cdcd54..c3596f056 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -103,6 +103,10 @@ static void Skin_OnChange(void); static void Skin2_OnChange(void); static void Skin3_OnChange(void); static void Skin4_OnChange(void); +static void Follower_OnChange(void); +static void Follower2_OnChange(void); +static void Follower3_OnChange(void); +static void Follower4_OnChange(void); static void Color_OnChange(void); static void Color2_OnChange(void); static void Color3_OnChange(void); @@ -268,6 +272,12 @@ consvar_t cv_skin2 = {"skin2", DEFAULTSKIN2, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Sk consvar_t cv_skin3 = {"skin3", DEFAULTSKIN3, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skin4 = {"skin4", DEFAULTSKIN4, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin4_OnChange, 0, NULL, NULL, 0, 0, NULL}; +// player's followers. Also saved. +consvar_t cv_follower = {"follower", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_follower2 = {"follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_follower3 = {"follower3", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower3_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_follower4 = {"follower4", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower4_OnChange, 0, NULL, NULL, 0, 0, NULL}; + consvar_t cv_skipmapcheck = {"skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; INT32 cv_debug; @@ -747,18 +757,22 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_playername); CV_RegisterVar(&cv_playercolor); CV_RegisterVar(&cv_skin); // r_things.c (skin NAME) + CV_RegisterVar(&cv_follower); // secondary player (splitscreen) CV_RegisterVar(&cv_playername2); CV_RegisterVar(&cv_playercolor2); CV_RegisterVar(&cv_skin2); + CV_RegisterVar(&cv_follower2); // third player CV_RegisterVar(&cv_playername3); CV_RegisterVar(&cv_playercolor3); CV_RegisterVar(&cv_skin3); + CV_RegisterVar(&cv_follower3); // fourth player CV_RegisterVar(&cv_playername4); CV_RegisterVar(&cv_playercolor4); CV_RegisterVar(&cv_skin4); + CV_RegisterVar(&cv_follower4); // preferred number of players CV_RegisterVar(&cv_splitplayers); @@ -1241,9 +1255,9 @@ static INT32 snacpending = 0, snac2pending = 0, snac3pending = 0, snac4pending = // static void SendNameAndColor(void) { - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; - + p = buf; // normal player colors @@ -1268,7 +1282,8 @@ static void SendNameAndColor(void) if (!strcmp(cv_playername.string, player_names[consoleplayer]) && cv_playercolor.value == players[consoleplayer].skincolor - && !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name)) + && !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name) + && cv_follower.value == players[consoleplayer].followerskin) return; // We'll handle it later if we're not playing. @@ -1351,6 +1366,7 @@ static void SendNameAndColor(void) WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor.value); WRITEUINT8(p, (UINT8)cv_skin.value); + WRITESINT8(p, (UINT8)cv_follower.value); SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1358,7 +1374,7 @@ static void SendNameAndColor(void) static void SendNameAndColor2(void) { INT32 secondplaya = -1; - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; if (splitscreen < 1 && !botingame) @@ -1475,13 +1491,14 @@ static void SendNameAndColor2(void) WRITESTRINGN(p, cv_playername2.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor2.value); WRITEUINT8(p, (UINT8)cv_skin2.value); + WRITESINT8(p, (UINT8)cv_follower2.value); SendNetXCmd2(XD_NAMEANDCOLOR, buf, p - buf); } static void SendNameAndColor3(void) { INT32 thirdplaya = -1; - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; if (splitscreen < 2) @@ -1590,13 +1607,14 @@ static void SendNameAndColor3(void) WRITESTRINGN(p, cv_playername3.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor3.value); WRITEUINT8(p, (UINT8)cv_skin3.value); + WRITESINT8(p, (UINT8)cv_follower3.value); SendNetXCmd3(XD_NAMEANDCOLOR, buf, p - buf); } static void SendNameAndColor4(void) { INT32 fourthplaya = -1; - XBOXSTATIC char buf[MAXPLAYERNAME+2]; + XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; if (splitscreen < 3) @@ -1713,6 +1731,7 @@ static void SendNameAndColor4(void) WRITESTRINGN(p, cv_playername4.zstring, MAXPLAYERNAME); WRITEUINT8(p, (UINT8)cv_playercolor4.value); WRITEUINT8(p, (UINT8)cv_skin4.value); + WRITESINT8(p, (UINT8)cv_follower4.value); SendNetXCmd4(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1721,7 +1740,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) player_t *p = &players[playernum]; char name[MAXPLAYERNAME+1]; UINT8 color, skin; - + SINT8 follower; + #ifdef PARANOIA if (playernum < 0 || playernum > MAXPLAYERS) I_Error("There is no player %d!", playernum); @@ -1744,6 +1764,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) READSTRINGN(*cp, name, MAXPLAYERNAME); color = READUINT8(*cp); skin = READUINT8(*cp); + follower = READSINT8(*cp); // set name if (strcasecmp(player_names[playernum], name) != 0) @@ -1802,6 +1823,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) } else SetPlayerSkinByNum(playernum, skin); + + // set follower: + SetFollower(playernum, follower); } void SendWeaponPref(void) @@ -5090,6 +5114,97 @@ static void Name4_OnChange(void) SendNameAndColor4(); } +// sends the follower change for players +static void Follower_OnChange(void) +{ + if (!Playing()) + return; // do whatever you want + + // there is a slight chance that we will actually use a string instead so... + // let's investigate the string... + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower.string); + strcpy(cpy, cv_follower.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor(); +} + +static void Follower2_OnChange(void) +{ + if (!Playing() || !splitscreen) + return; // do whatever you want + + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower2.string); + strcpy(cpy, cv_follower2.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower2, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor2(); +} + +static void Follower3_OnChange(void) +{ + if (!Playing() || !splitscreen) + return; // do whatever you want + + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower3.string); + strcpy(cpy, cv_follower3.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower3, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor3(); +} + +static void Follower4_OnChange(void) +{ + if (!Playing() || !splitscreen) + return; // do whatever you want + + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + strcpy(str, cv_follower4.string); + strcpy(cpy, cv_follower4.string); + strlwr(str); + if (!atoi(cpy)) // yep, that's a string alright... + { + INT32 num = R_FollowerAvailable(str); + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + + char set[10]; + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower4, set); // set it to a number. It's easier for us to send later :) + } + SendNameAndColor4(); +} + /** Sends a skin change for the console player, unless that player is moving. * \sa cv_skin, Skin2_OnChange, Color_OnChange * \author Graue diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c590eee65..0ffc65f2a 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -21,18 +21,22 @@ extern consvar_t cv_playername; extern consvar_t cv_playercolor; extern consvar_t cv_skin; +extern consvar_t cv_follower; // secondary splitscreen player extern consvar_t cv_playername2; extern consvar_t cv_playercolor2; extern consvar_t cv_skin2; +extern consvar_t cv_follower2; // third splitscreen player extern consvar_t cv_playername3; extern consvar_t cv_playercolor3; extern consvar_t cv_skin3; +extern consvar_t cv_follower3; // fourth splitscreen player extern consvar_t cv_playername4; extern consvar_t cv_playercolor4; extern consvar_t cv_skin4; +extern consvar_t cv_follower4; // preferred number of players extern consvar_t cv_splitplayers; diff --git a/src/d_player.h b/src/d_player.h index bf1a01dad..eed59d28e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -445,6 +445,7 @@ typedef struct player_s UINT8 kartspeed; // Kart speed stat between 1 and 9 UINT8 kartweight; // Kart weight stat between 1 and 9 + INT32 followerskin; // Kart: This player's follower "skin" mobj_t *follower; // Kart: This is the follower object we have. (If any) // diff --git a/src/dehacked.c b/src/dehacked.c index 7c789727a..4531769aa 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -699,18 +699,21 @@ INT32 numfollowers = 0; static void readfollower(MYFILE *f) { - + if (numfollowers > MAXSKINS) - { - CONS_Printf("Error: Too many followers, cannot add anymore.\n"); + { + deh_warning("Error: Too many followers, cannot add anymore.\n"); return; - } - + } + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2; + char *word, *word2, *dname = malloc(SKINNAMESIZE+1); char *tmp; - - CONS_Printf("Adding follower, please bear with me...\n"); + + boolean nameset; + INT32 fallbackstate = 0; + + CONS_Printf("Adding follower...\n"); do { @@ -732,14 +735,20 @@ static void readfollower(MYFILE *f) break; word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else + + if (!word2) break; + if (word2[strlen(word2)-1] == '\n') word2[strlen(word2)-1] = '\0'; - if (fastcmp(word, "ATANGLE")) + if (fastcmp(word, "NAME")) + { + DEH_WriteUndoline(word, va("%s", followers[numfollowers].name), UNDO_NONE); + strcpy(followers[numfollowers].name, word2); + nameset = true; + } + else if (fastcmp(word, "ATANGLE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); followers[numfollowers].atangle = (INT32)atoi(word2); @@ -749,15 +758,105 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE); followers[numfollowers].zoffs = (INT32)atoi(word2); } + else if (fastcmp(word, "IDLESTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].idlestate), UNDO_NONE); + followers[numfollowers].idlestate = get_number(word2); + fallbackstate = followers[numfollowers].idlestate; + } + else if (fastcmp(word, "FOLLOWSTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].followstate), UNDO_NONE); + followers[numfollowers].followstate = get_number(word2); + } + else if (fastcmp(word, "HURTSTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].hurtstate), UNDO_NONE); + followers[numfollowers].hurtstate = get_number(word2); + } + else if (fastcmp(word, "LOSESTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].losestate), UNDO_NONE); + followers[numfollowers].losestate = get_number(word2); + } + else if (fastcmp(word, "WINSTATE")) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].winstate), UNDO_NONE); + followers[numfollowers].winstate = get_number(word2); + } else deh_warning("Follower %d: unknown word '%s'", numfollowers, word); } } while (!myfeof(f)); // finish when the line is empty - - CONS_Printf("We are done adding the follower.\n"); - numfollowers++; - Z_Free(s); -} + + if (!nameset) // well this is problematic. + { + strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what + } + + // set skin name (this is just the follower's name in lowercases): + // but before we do, let's... actually check if another follower isn't doing the same shit... + + char testname[SKINNAMESIZE]; + strcpy(testname, followers[numfollowers].name); + + // lower testname for skin checks... + strlwr(testname); + INT32 res = R_FollowerAvailable(testname); + if (res > -1) // yikes, someone else has stolen our name already + { + deh_warning("There was already a follower with the same name. (%s)", testname); + INT32 startlen = strlen(testname); + char cpy[2]; + sprintf(cpy, "%d", numfollowers); + memcpy(&testname[startlen], cpy, 2); + // in that case, we'll be very lazy and copy numfollowers to the end of our skin name. + } + + strcpy(followers[numfollowers].skinname, testname); + + // get ready to print the name... + strcpy(dname, followers[numfollowers].skinname); + + // check for atangle and zoffs. But don't error if they aren't set, just give them logical values. + if (!followers[numfollowers].atangle && followers[numfollowers].atangle != 0) + followers[numfollowers].atangle = 300; + + if ((!followers[numfollowers].zoffs || followers[numfollowers].zoffs < 0) && followers[numfollowers].zoffs != 0) // yikes, no offset or negative offset + followers[numfollowers].zoffs = 64; + + + // also check if we forgot states :V +#define NOSTATE(field, field2) \ +if (!followers[numfollowers].field) \ +{ \ + followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \ + if (!fallbackstate) \ + deh_warning("Follower %s is missing state definition for %s, no idlestate fallback was found", dname, field2); \ +} \ + + NOSTATE(idlestate, "idlestate"); + NOSTATE(followstate, "followstate"); + NOSTATE(hurtstate, "hurtstate"); + NOSTATE(losestate, "losestate"); + NOSTATE(winstate, "winstate"); +#undef NOSTATE + + CONS_Printf("Added follower '%s'\n", dname); + numfollowers++; // add 1 follower + free(dname); // free name memory + Z_Free(s); +} static void readthing(MYFILE *f, INT32 num) { @@ -3508,6 +3607,13 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) // This is not a major mod. continue; } + else if (fastcmp(word, "FOLLOWER")) + { + readfollower(f); // at the same time this will be our only way to ADD followers for now. Yikes. + DEH_WriteUndoline(word, "", UNDO_HEADER); + // This is not a major mod either. + continue; // continue so that we don't error. + } word2 = strtok(NULL, " "); if (fastcmp(word, "CHARACTER")) { @@ -3549,11 +3655,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) DEH_WriteUndoline(word, word2, UNDO_HEADER); // This is not a major mod. } - else if (fastcmp(word, "FOLLOWER")) - { - readfollower(f); // at the same time this will be our only way to ADD followers for now. Yikes. - DEH_WriteUndoline(word, word2, UNDO_HEADER); - } else if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT")) { if (i == 0 && word2[0] != '0') // If word2 isn't a number diff --git a/src/doomdef.h b/src/doomdef.h index 5e7b7657b..ab863c6f6 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -140,7 +140,7 @@ extern FILE *logstream; #endif -#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 +//#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 #ifdef DEVELOP #define VERSION 0 // Game version #define SUBVERSION 0 // more precise version number diff --git a/src/m_menu.c b/src/m_menu.c index d8b1c2d7b..359ff2207 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -184,6 +184,7 @@ INT16 startmap; // Mario, NiGHTS, or just a plain old normal game? static INT16 itemOn = 1; // menu item skull is on, Hack by Tails 09-18-2002 static INT16 skullAnimCounter = 10; // skull animation counter +static tic_t followertimer = 0; // Used for smooth follower floating static UINT8 setupcontrolplayer; static INT32 (*setupcontrols)[2]; // pointer to the gamecontrols of the player being edited @@ -973,6 +974,7 @@ static menuitem_t MP_PlayerSetupMenu[] = { {IT_KEYHANDLER | IT_STRING, NULL, "Name", M_HandleSetupMultiPlayer, 0}, {IT_KEYHANDLER | IT_STRING, NULL, "Character", M_HandleSetupMultiPlayer, 16}, // Tails 01-18-2001 + {IT_KEYHANDLER | IT_STRING, NULL, "Follower", M_HandleSetupMultiPlayer, 26}, {IT_KEYHANDLER | IT_STRING, NULL, "Color", M_HandleSetupMultiPlayer, 152}, }; @@ -3139,6 +3141,8 @@ void M_Ticker(void) if (--skullAnimCounter <= 0) skullAnimCounter = 8; + followertimer++; + //added : 30-01-98 : test mode for five seconds if (vidm_testingmode > 0) { @@ -8027,9 +8031,15 @@ static void M_HandleConnectIP(INT32 choice) // ======================== // Tails 03-02-2002 +// used for skin display on player setup menu static INT32 multi_tics; static state_t *multi_state; +// used for follower display on player setup menu +static INT32 follower_tics; +static INT32 follower_frame; // used for FF_ANIMATE garbo +static state_t *follower_state; + // this is set before entering the MultiPlayer setup menu, // for either player 1 or 2 static char setupm_name[MAXPLAYERNAME+1]; @@ -8037,8 +8047,10 @@ static player_t *setupm_player; static consvar_t *setupm_cvskin; static consvar_t *setupm_cvcolor; static consvar_t *setupm_cvname; +static consvar_t *setupm_cvfollower; static INT32 setupm_fakeskin; static INT32 setupm_fakecolor; +static INT32 setupm_fakefollower; // -1 is for none, our followers start at 0 static void M_DrawSetupMultiPlayerMenu(void) { @@ -8087,11 +8099,31 @@ static void M_DrawSetupMultiPlayerMenu(void) '\x1D' | highlightflags, false); // right arrow } + // draw follower string + char *fname = malloc(SKINNAMESIZE+1); + + if (setupm_fakefollower == -1) + strcpy(fname, "None"); + else + strcpy(fname, followers[setupm_fakefollower].name); + + st = V_StringWidth(fname, 0); + V_DrawString(BASEVIDWIDTH - mx - st, my + 26, + ((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|highlightflags|V_ALLOWLOWERCASE, + fname); + if (itemOn == 2) + { + V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 26, + '\x1C' | highlightflags, false); // left arrow + V_DrawCharacter(BASEVIDWIDTH - mx + 2 + (skullAnimCounter/5), my + 26, + '\x1D' | highlightflags, false); // right arrow + } + // draw the name of the color you have chosen // Just so people don't go thinking that "Default" is Green. st = V_StringWidth(KartColor_Names[setupm_fakecolor], 0); V_DrawString(BASEVIDWIDTH - mx - st, my + 152, highlightflags|V_ALLOWLOWERCASE, KartColor_Names[setupm_fakecolor]); // SRB2kart - if (itemOn == 2) + if (itemOn == 3) { V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 152, '\x1C' | highlightflags, false); // left arrow @@ -8262,9 +8294,84 @@ static void M_DrawSetupMultiPlayerMenu(void) Z_Free(colormap); } + + // draw their follower if there is one + if (setupm_fakefollower > -1 && setupm_fakefollower < numfollowers) + { + // animate the follower + + if (--follower_tics <= 0) + { + + // FF_ANIMATE; cycle through FRAMES and get back afterwards. This will be prominent amongst followers hence why it's being supported here. + if (follower_state->frame & FF_ANIMATE) + { + follower_frame++; + follower_tics = follower_state->var2; + if (follower_frame > (follower_state->frame & FF_FRAMEMASK) + follower_state->var1) // that's how it works, right? + follower_frame = follower_state->frame & FF_FRAMEMASK; + } + else + { + st = follower_state->nextstate; + if (st != S_NULL) + follower_state = &states[st]; + follower_tics = follower_state->tics; + if (follower_tics == -1) + follower_tics = 15; // er, what? + // get spritedef: + follower_frame = follower_state->frame & FF_FRAMEMASK; + } + } + sprdef = &sprites[follower_state->sprite]; + + // draw the follower + + if (follower_frame >= sprdef->numframes) + follower_frame = 0; // frame doesn't exist, we went beyond it... what? + sprframe = &sprdef->spriteframes[follower_frame]; + patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); + if (sprframe->flip & 1) // Only for first sprite + flags |= V_FLIP; // This sprite is left/right flipped! + + // draw player sprite + if (setupm_fakecolor) // inverse should never happen + { + + // smooth floating, totally not stolen from rocket sneakers. + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + + UINT8 *colormap = R_GetTranslationColormap(-1, setupm_fakecolor, 0); + V_DrawMappedPatch(mx+65, my+90+(sine/FRACUNIT), flags, patch, colormap); + Z_Free(colormap); + } + } + #undef charw } +// follower state update. This is its own function so that it's at least somewhat clean +static void M_GetFollowerState(void) +{ + + if (setupm_fakefollower == -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none! + return; + // ^ we don't actually need to set anything since it won't be displayed anyway. + + //followertimer = 0; // reset timer. not like it'll overflow anytime soon but whatever. + + // set follower state + follower_state = &states[followers[setupm_fakefollower].followstate]; + + if (follower_state->frame & FF_ANIMATE) + follower_tics = follower_state->var2; // support for FF_ANIMATE + else + follower_tics = follower_state->tics; + + follower_frame = follower_state->frame & FF_FRAMEMASK; +} + // Handle 1P/2P MP Setup static void M_HandleSetupMultiPlayer(INT32 choice) { @@ -8289,7 +8396,13 @@ static void M_HandleSetupMultiPlayer(INT32 choice) S_StartSound(NULL,sfx_menu1); // Tails setupm_fakeskin--; } - else if (itemOn == 2) // player color + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower--; + M_GetFollowerState(); // update follower state + } + else if (itemOn == 3) // player color { S_StartSound(NULL,sfx_menu1); // Tails setupm_fakecolor--; @@ -8301,8 +8414,15 @@ static void M_HandleSetupMultiPlayer(INT32 choice) { S_StartSound(NULL,sfx_menu1); // Tails setupm_fakeskin++; + M_GetFollowerState(); // update follower state } - else if (itemOn == 2) // player color + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower++; + M_GetFollowerState(); + } + else if (itemOn == 3) // player color { S_StartSound(NULL,sfx_menu1); // Tails setupm_fakecolor++; @@ -8322,7 +8442,12 @@ static void M_HandleSetupMultiPlayer(INT32 choice) setupm_name[l-1] =0; } } - else if (itemOn == 2) + else if (itemOn == 2) // follower + { + S_StartSound(NULL,sfx_menu1); + setupm_fakefollower = -1; + } + else if (itemOn == 3) { UINT8 col = skins[setupm_fakeskin].prefcolor; if (setupm_fakecolor != col) @@ -8360,6 +8485,18 @@ static void M_HandleSetupMultiPlayer(INT32 choice) if (setupm_fakeskin > numskins-1) setupm_fakeskin = 0; + // check followers: + if (setupm_fakefollower < -1) + { + setupm_fakefollower = numfollowers-1; + M_GetFollowerState(); // update follower state + } + if (setupm_fakefollower > numfollowers-1) + { + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state + } + // check color if (setupm_fakecolor < 1) setupm_fakecolor = MAXSKINCOLORS-1; @@ -8382,6 +8519,7 @@ static void M_SetupMultiPlayer(INT32 choice) multi_state = &states[mobjinfo[MT_PLAYER].seestate]; multi_tics = multi_state->tics; + strcpy(setupm_name, cv_playername.string); // set for player 1 @@ -8389,6 +8527,10 @@ static void M_SetupMultiPlayer(INT32 choice) setupm_cvskin = &cv_skin; setupm_cvcolor = &cv_playercolor; setupm_cvname = &cv_playername; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8420,6 +8562,10 @@ static void M_SetupMultiPlayer2(INT32 choice) setupm_cvskin = &cv_skin2; setupm_cvcolor = &cv_playercolor2; setupm_cvname = &cv_playername2; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8451,6 +8597,10 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_cvskin = &cv_skin3; setupm_cvcolor = &cv_playercolor3; setupm_cvname = &cv_playername3; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8482,6 +8632,10 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_cvskin = &cv_skin4; setupm_cvcolor = &cv_playercolor4; setupm_cvname = &cv_playername4; + setupm_cvfollower = &cv_follower; + + setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); @@ -8514,6 +8668,7 @@ static boolean M_QuitMultiPlayerMenu(void) // you know what? always putting these in the buffer won't hurt anything. COM_BufAddText (va("%s \"%s\"\n",setupm_cvskin->name,skins[setupm_fakeskin].name)); COM_BufAddText (va("%s %d\n",setupm_cvcolor->name,setupm_fakecolor)); + COM_BufAddText (va("%s %d\n",setupm_cvfollower->name,setupm_fakefollower)); return true; } diff --git a/src/r_things.c b/src/r_things.c index 198bcbbfd..65041c6fa 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2637,6 +2637,19 @@ INT32 R_SkinAvailable(const char *name) return -1; } +// same thing but for followers: +INT32 R_FollowerAvailable(const char *name) +{ + INT32 i; + + for (i = 0; i < numfollowers; i++) + { + if (stricmp(followers[i].skinname,name)==0) + return i; + } + return -1; +} + // network code calls this when a 'skin change' is received boolean SetPlayerSkin(INT32 playernum, const char *skinname) { @@ -2662,13 +2675,38 @@ boolean SetPlayerSkin(INT32 playernum, const char *skinname) return false; } +// Again, same thing but for followers; +boolean SetPlayerFollower(INT32 playernum, const char *skinname) +{ + INT32 i; + player_t *player = &players[playernum]; + + for (i = 0; i < numfollowers; i++) + { + // search in the skin list + if (stricmp(followers[i].skinname, skinname) == 0) + { + SetFollower(playernum, i); + return true; + } + } + + if (P_IsLocalPlayer(player)) + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found.\n"), skinname); + else if(server || IsPlayerAdmin(consoleplayer)) + CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) follower '%s' not found\n"), playernum, player_names[playernum], skinname); + + SetFollower(playernum, -1); // reminder that -1 is nothing + return false; +} + // Same as SetPlayerSkin, but uses the skin #. // network code calls this when a 'skin change' is received void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; skin_t *skin = &skins[skinnum]; - + CONS_Printf("skin\n"); if (skinnum >= 0 && skinnum < numskins) // Make sure it exists! { player->skin = skinnum; @@ -2727,6 +2765,30 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin } +// you get the drill, now we do the same for followers: +void SetFollower(INT32 playernum, INT32 skinnum) +{ + player_t *player = &players[playernum]; + + if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! + { + player->followerskin = skinnum; + CONS_Printf("Updated player follower num\n"); + /* + We don't actually set anything there, becasuse we need to be sure that a proper player->mo is available to spawn the follower. + Moreover, the follower will self-handle itself the rest of the time, hence, its skinnum stored to the player is all we need right now. + */ + + return; + } + + if (P_IsLocalPlayer(player)) + CONS_Alert(CONS_WARNING, M_GetText("Follower %d not found\n"), skinnum); + else if(server || IsPlayerAdmin(consoleplayer)) + CONS_Alert(CONS_WARNING, "Player %d (%s) follower %d not found\n", playernum, player_names[playernum], skinnum); + SetFollower(playernum, -1); // Not found, then set -1 (nothing) as our follower. +} + // // Add skins from a pwad, each skin preceded by 'S_SKIN' marker // diff --git a/src/r_things.h b/src/r_things.h index dda1ea16e..57ecb0dd6 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -122,21 +122,22 @@ typedef struct // typedef struct follower_s { - char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this. - + char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything. + char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this. + // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. - + // from there on out, everything is STATES to allow customization // these are only set once when the action is performed and are then free to animate however they want. - + INT32 idlestate; // state when the player is at a standstill INT32 followstate; // state when the player is moving INT32 hurtstate; // state when the player is being hurt INT32 winstate; // state when the player has won INT32 losestate; // state when the player has lost - + } follower_t; // ----------- @@ -225,6 +226,10 @@ void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 INT32 R_SkinAvailable(const char *name); void R_AddSkins(UINT16 wadnum); +INT32 R_FollowerAvailable(const char *name); +boolean SetPlayerFollower(INT32 playernum,const char *skinname); +void SetFollower(INT32 playernum,INT32 skinnum); + #ifdef DELFILE void R_DelSkins(UINT16 wadnum); #endif From 92ab2845c8384384d6aff4b126da86f4620a881e Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 14 Feb 2019 03:56:28 +0100 Subject: [PATCH 003/211] Followers but they crash when you join netgames --- src/d_main.c | 1 + src/d_netcmd.c | 47 ++++++++++----- src/d_player.h | 5 +- src/dehacked.c | 41 ++++++++++--- src/doomdef.h | 2 +- src/g_game.c | 14 +++++ src/info.c | 57 +++++++++++++++++- src/info.h | 35 ++++++++++- src/m_menu.c | 20 +++++++ src/p_saveg.c | 29 +++++++-- src/p_user.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++ src/r_things.c | 17 ++++-- src/r_things.h | 1 + 13 files changed, 390 insertions(+), 38 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 5cf95f4b8..0f734f1b9 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -931,6 +931,7 @@ static void IdentifyVersion(void) D_AddFile(va(pandf,srb2waddir,"textures.kart")); D_AddFile(va(pandf,srb2waddir,"chars.kart")); D_AddFile(va(pandf,srb2waddir,"maps.kart")); + D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #ifdef USE_PATCH_KART D_AddFile(va(pandf,srb2waddir,"patch.kart")); #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index c3596f056..9ebd0237b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1257,7 +1257,7 @@ static void SendNameAndColor(void) { XBOXSTATIC char buf[MAXPLAYERNAME+3]; char *p; - + p = buf; // normal player colors @@ -1280,6 +1280,10 @@ static void SendNameAndColor(void) CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue); } + // so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game: + if (cv_follower.value > numfollowers-1 || cv_follower.value < -1) + CV_StealthSet(&cv_follower, "-1"); + if (!strcmp(cv_playername.string, player_names[consoleplayer]) && cv_playercolor.value == players[consoleplayer].skincolor && !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name) @@ -1410,6 +1414,10 @@ static void SendNameAndColor2(void) CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue); } + // so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game: + if (cv_follower2.value > numfollowers-1 || cv_follower2.value < -1) + CV_StealthSet(&cv_follower2, "-1"); + // We'll handle it later if we're not playing. if (!Playing()) return; @@ -1534,6 +1542,10 @@ static void SendNameAndColor3(void) CV_StealthSet(&cv_playercolor3, cv_playercolor3.defaultvalue); } + // so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game: + if (cv_follower3.value > numfollowers-1 || cv_follower3.value < -1) + CV_StealthSet(&cv_follower3, "-1"); + // We'll handle it later if we're not playing. if (!Playing()) return; @@ -1650,6 +1662,10 @@ static void SendNameAndColor4(void) CV_StealthSet(&cv_playercolor4, cv_playercolor4.defaultvalue); } + // so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game: + if (cv_follower4.value > numfollowers-1 || cv_follower4.value < -1) + CV_StealthSet(&cv_follower4, "-1"); + // We'll handle it later if we're not playing. if (!Playing()) return; @@ -1741,7 +1757,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) char name[MAXPLAYERNAME+1]; UINT8 color, skin; SINT8 follower; - + #ifdef PARANOIA if (playernum < 0 || playernum > MAXPLAYERS) I_Error("There is no player %d!", playernum); @@ -1823,7 +1839,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) } else SetPlayerSkinByNum(playernum, skin); - + // set follower: SetFollower(playernum, follower); } @@ -1885,6 +1901,7 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum) players[playernum].pflags |= PF_FLIPCAM; if (prefs & 2) players[playernum].pflags |= PF_ANALOGMODE; + } void D_SendPlayerConfig(void) @@ -5119,19 +5136,19 @@ static void Follower_OnChange(void) { if (!Playing()) return; // do whatever you want - + // there is a slight chance that we will actually use a string instead so... // let's investigate the string... char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; strcpy(str, cv_follower.string); strcpy(cpy, cv_follower.string); strlwr(str); - if (!atoi(cpy)) // yep, that's a string alright... + if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - + char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower, set); // set it to a number. It's easier for us to send later :) @@ -5143,17 +5160,17 @@ static void Follower2_OnChange(void) { if (!Playing() || !splitscreen) return; // do whatever you want - + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; strcpy(str, cv_follower2.string); strcpy(cpy, cv_follower2.string); strlwr(str); - if (!atoi(cpy)) // yep, that's a string alright... + if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - + char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower2, set); // set it to a number. It's easier for us to send later :) @@ -5170,16 +5187,16 @@ static void Follower3_OnChange(void) strcpy(str, cv_follower3.string); strcpy(cpy, cv_follower3.string); strlwr(str); - if (!atoi(cpy)) // yep, that's a string alright... + if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - + char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower3, set); // set it to a number. It's easier for us to send later :) - } + } SendNameAndColor3(); } @@ -5192,16 +5209,16 @@ static void Follower4_OnChange(void) strcpy(str, cv_follower4.string); strcpy(cpy, cv_follower4.string); strlwr(str); - if (!atoi(cpy)) // yep, that's a string alright... + if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - + char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower4, set); // set it to a number. It's easier for us to send later :) - } + } SendNameAndColor4(); } diff --git a/src/d_player.h b/src/d_player.h index eed59d28e..7d4aec646 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -444,10 +444,11 @@ typedef struct player_s // SRB2kart UINT8 kartspeed; // Kart speed stat between 1 and 9 UINT8 kartweight; // Kart weight stat between 1 and 9 - + INT32 followerskin; // Kart: This player's follower "skin" + boolean followerready; // Kart: Used to know when we can have a follower or not. (This is set on the first NameAndColor follower update) mobj_t *follower; // Kart: This is the follower object we have. (If any) - + // fixed_t normalspeed; // Normal ground diff --git a/src/dehacked.c b/src/dehacked.c index 4531769aa..b72a1cbc5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -707,7 +707,7 @@ static void readfollower(MYFILE *f) } char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2, *dname = malloc(SKINNAMESIZE+1); + char *word, *word2, dname[SKINNAMESIZE+1]; char *tmp; boolean nameset; @@ -758,6 +758,11 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE); followers[numfollowers].zoffs = (INT32)atoi(word2); } + else if (fastcmp(word, "DISTANCE") || (fastcmp(word, "DIST"))) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].dist), UNDO_NONE); + followers[numfollowers].dist = (INT32)atoi(word2); + } else if (fastcmp(word, "IDLESTATE")) { if (word2) @@ -828,12 +833,11 @@ static void readfollower(MYFILE *f) // get ready to print the name... strcpy(dname, followers[numfollowers].skinname); - // check for atangle and zoffs. But don't error if they aren't set, just give them logical values. - if (!followers[numfollowers].atangle && followers[numfollowers].atangle != 0) - followers[numfollowers].atangle = 300; + if (followers[numfollowers].dist < 0) + followers[numfollowers].dist = 0; - if ((!followers[numfollowers].zoffs || followers[numfollowers].zoffs < 0) && followers[numfollowers].zoffs != 0) // yikes, no offset or negative offset - followers[numfollowers].zoffs = 64; + if (followers[numfollowers].zoffs < 0) + followers[numfollowers].zoffs = 0; // also check if we forgot states :V @@ -854,7 +858,6 @@ if (!followers[numfollowers].field) \ CONS_Printf("Added follower '%s'\n", dname); numfollowers++; // add 1 follower - free(dname); // free name memory Z_Free(s); } @@ -7322,6 +7325,28 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_KARMAFIREWORK4", "S_KARMAFIREWORKTRAIL", + "S_GCHAOIDLE", + "S_GCHAOFLY", + "S_GCHAOSAD1", + "S_GCHAOSAD2", + "S_GCHAOSAD3", + "S_GCHAOSAD4", + "S_GCHAOHAPPY1", + "S_GCHAOHAPPY2", + "S_GCHAOHAPPY3", + "S_GCHAOHAPPY4", + + "S_CHEESEIDLE", + "S_CHEESEFLY", + "S_CHEESESAD1", + "S_CHEESESAD2", + "S_CHEESESAD3", + "S_CHEESESAD4", + "S_CHEESEHAPPY1", + "S_CHEESEHAPPY2", + "S_CHEESEHAPPY3", + "S_CHEESEHAPPY4", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -8109,6 +8134,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_KARMAFIREWORK", + "MT_FOLLOWER", + #ifdef SEENAMES "MT_NAMECHECK", #endif diff --git a/src/doomdef.h b/src/doomdef.h index ab863c6f6..5e7b7657b 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -140,7 +140,7 @@ extern FILE *logstream; #endif -//#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 +#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 #ifdef DEVELOP #define VERSION 0 // Game version #define SUBVERSION 0 // more precise version number diff --git a/src/g_game.c b/src/g_game.c index f0d221ff6..04d3fe20b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2327,6 +2327,9 @@ void G_PlayerReborn(INT32 player) // SRB2kart UINT8 kartspeed; UINT8 kartweight; + boolean followerready; + INT32 followerskin; + mobj_t *follower; // old follower, will probably be removed by the time we're dead but you never know. // fixed_t normalspeed; fixed_t runspeed; @@ -2398,6 +2401,9 @@ void G_PlayerReborn(INT32 player) // SRB2kart kartspeed = players[player].kartspeed; kartweight = players[player].kartweight; + follower = players[player].follower; + followerready = players[player].followerready; + followerskin = players[player].followerskin; // normalspeed = players[player].normalspeed; runspeed = players[player].runspeed; @@ -2531,6 +2537,14 @@ void G_PlayerReborn(INT32 player) p->kartstuff[k_wanted] = wanted; p->kartstuff[k_eggmanblame] = -1; + if (follower) + P_RemoveMobj(follower); + + p->followerready = followerready; + p->followerskin = followerskin; + p->follower = NULL; // respawn a new one with you, it looks better. + + // Don't do anything immediately p->pflags |= PF_USEDOWN; p->pflags |= PF_ATTACKDOWN; diff --git a/src/info.c b/src/info.c index 5701f1c9e..ac16a6edc 100644 --- a/src/info.c +++ b/src/info.c @@ -69,7 +69,7 @@ char sprnames[NUMSPRITES + 1][5] = "CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS","ZTCH", "MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH","BFRT", "OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN","FWRK", - "XMS4","XMS5","VIEW" + "XMS4","XMS5","GCHA","CHEZ","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -3393,7 +3393,33 @@ state_t states[NUMSTATES] = {SPR_FWRK, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK4}, // S_KARMAFIREWORK3 {SPR_FWRK, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK1}, // S_KARMAFIREWORK4 {SPR_FWRK, 4|FF_FULLBRIGHT, TICRATE, {NULL}, 0, 0, S_NULL}, // S_KARMAFIREWORKTRAIL - + + // followers: + + // generic chao: + {SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE + {SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY + {SPR_GCHA, 7, 5, {NULL}, 0, 0, S_GCHAOSAD2}, //S_GCHAOSAD1 + {SPR_GCHA, 8, 3, {NULL}, 0, 0, S_GCHAOSAD3}, //S_GCHAOSAD2 + {SPR_GCHA, 9, 6, {NULL}, 0, 0, S_GCHAOSAD4}, //S_GCHAOSAD3 + {SPR_GCHA, 8, 3, {NULL}, 0, 0, S_GCHAOSAD1}, //S_GCHAOSAD4 + {SPR_GCHA, 4, 8, {NULL}, 0, 0, S_GCHAOHAPPY2}, //S_GCHAOHAPPY1 + {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY3}, //S_GCHAOHAPPY2 + {SPR_GCHA, 6, 8, {NULL}, 0, 0, S_GCHAOHAPPY4}, //S_GCHAOHAPPY3 + {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY1}, //S_GCHAOHAPPY4 + + // cheese: + {SPR_CHEZ, FF_ANIMATE, -1, {NULL}, 1, 4, S_CHEESEIDLE}, //S_CHEESEIDLE + {SPR_CHEZ, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_CHEESEFLY}, //S_CHEESEFLY + {SPR_CHEZ, 7, 5, {NULL}, 0, 0, S_CHEESESAD2}, //S_CHEESESAD1 + {SPR_CHEZ, 8, 3, {NULL}, 0, 0, S_CHEESESAD3}, //S_CHEESESAD2 + {SPR_CHEZ, 9, 6, {NULL}, 0, 0, S_CHEESESAD4}, //S_CHEESESAD3 + {SPR_CHEZ, 8, 3, {NULL}, 0, 0, S_CHEESESAD1}, //S_CHEESESAD4 + {SPR_CHEZ, 4, 8, {NULL}, 0, 0, S_CHEESEHAPPY2}, //S_CHEESEHAPPY1 + {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY3}, //S_CHEESEHAPPY2 + {SPR_CHEZ, 6, 8, {NULL}, 0, 0, S_CHEESEHAPPY4}, //S_CHEESEHAPPY3 + {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY1}, //S_CHEESEHAPPY4 + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -20052,6 +20078,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_FOLLOWER + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8<string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value @@ -8565,6 +8570,11 @@ static void M_SetupMultiPlayer2(INT32 choice) setupm_cvfollower = &cv_follower; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value @@ -8600,6 +8610,11 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_cvfollower = &cv_follower; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value @@ -8635,6 +8650,11 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_cvfollower = &cv_follower; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value + + // yikes, we don't want none of that... + if (setupm_fakefollower > numfollowers-1) + setupm_fakefollower = -1; + M_GetFollowerState(); // update follower state // For whatever reason this doesn't work right if you just use ->value diff --git a/src/p_saveg.c b/src/p_saveg.c index 975a4a5d2..ae568fabf 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -52,10 +52,11 @@ typedef enum { // RFLAGPOINT = 0x01, // BFLAGPOINT = 0x02, - CAPSULE = 0x04, - AWAYVIEW = 0x08, - FIRSTAXIS = 0x10, - SECONDAXIS = 0x20, + CAPSULE = 4, + AWAYVIEW = 8, + FIRSTAXIS = 16, + SECONDAXIS = 32, + FOLLOWER = 64, } player_saveflags; // @@ -114,6 +115,8 @@ static void P_NetArchivePlayers(void) UINT16 flags; // size_t q; + CONS_Printf("SENDING NET INFO\n"); + WRITEUINT32(save_p, ARCHIVEBLOCK_PLAYERS); for (i = 0; i < MAXPLAYERS; i++) @@ -238,6 +241,9 @@ static void P_NetArchivePlayers(void) if (players[i].axis2) flags |= SECONDAXIS; + if (players[i].follower) + flags |= FOLLOWER; + WRITEINT16(save_p, players[i].lastsidehit); WRITEINT16(save_p, players[i].lastlinehit); @@ -275,6 +281,13 @@ static void P_NetArchivePlayers(void) // SRB2kart WRITEUINT8(save_p, players[i].kartspeed); WRITEUINT8(save_p, players[i].kartweight); + + WRITEUINT8(save_p, players[i].followerskin); + WRITEUINT8(save_p, players[i].followerready); // booleans are really just numbers eh?? + if (flags & FOLLOWER) + WRITEUINT32(save_p, players[i].follower->mobjnum); + + // WRITEFIXED(save_p, players[i].normalspeed); WRITEFIXED(save_p, players[i].runspeed); @@ -299,6 +312,8 @@ static void P_NetUnArchivePlayers(void) INT32 i, j; UINT16 flags; + CONS_Printf("FETCHING NET INFO\n"); + if (READUINT32(save_p) != ARCHIVEBLOCK_PLAYERS) I_Error("Bad $$$.sav at archive block Players"); @@ -455,6 +470,12 @@ static void P_NetUnArchivePlayers(void) // SRB2kart players[i].kartspeed = READUINT8(save_p); players[i].kartweight = READUINT8(save_p); + + players[i].followerskin = READUINT8(save_p); + players[i].followerready = READUINT8(save_p); + if (flags & FOLLOWER) + players[i].follower = (mobj_t *)(size_t)READUINT32(save_p); + // players[i].normalspeed = READFIXED(save_p); players[i].runspeed = READFIXED(save_p); diff --git a/src/p_user.c b/src/p_user.c index 76f57a9ba..c2e75916b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8933,6 +8933,162 @@ void P_DoTimeOver(player_t *player) countdown2 = 5*TICRATE; } +/* set follower state with our weird hacks + the reason we do this is to avoid followers ever using actions (majormods, yikes!) + without having to touch p_mobj.c. + so we give it 1more tic and change the state when tic == 1 instead of 0 + cool beans? + cool beans. +*/ +static void P_SetFollowerState(mobj_t *f, INT32 state) +{ + P_SetMobjStateNF(f, state); + if (f->state->tics > 0) + f->tics++; +} + +// +//P_HandleFllower +// +//Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker. +static void P_HandleFollower(player_t *player) +{ + + if (!player->followerready) + return; // we aren't ready to perform anything follower related yet. + + // How about making sure our follower exists and is added before trying to spawn it n' all? + if (player->followerskin > numfollowers-1 || player->followerskin < -1) + { + CONS_Printf("Follower skin invlaid. Setting to -1.\n"); + player->followerskin = -1; + return; + } + + // don't do anything if we can't have a follower to begin with. (It gets removed under those conditions) + if (player->spectator) + return; + if (player->followerskin < 0) + return; + + // Before we do anything, let's be sure of where we're supposed to be + follower_t fl = followers[player->followerskin]; + + angle_t an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... + fixed_t zoffs = (fl.zoffs)*FRACUNIT; + + // do you like angle maths? I certainly don't... + fixed_t sx, sy, sz; + sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT)); + sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT)); + + // for the z coordinate, don't be a doof like Steel and forget that MFE_VERTICALFLIP exists :P + sz = player->mo->z + FixedMul(player->mo->scale, zoffs); + /*if (player->mo->eflags & MFE_VERTICALFLIP) // it's safe to assume that VERTICALFLIP accounts for MF2_OBJECTFLIP too + sz -= (player->mo->height + FixedMul(player->mo->scale, zoffs*2));*/ + // ^ handled by K_matchgenericextraflags oops + + + // finally, add a cool floating effect to the z height, unless we have no zoffs, in which case I guess this means we WANT to be on the ground...??? + if (fl.zoffs) + { + // not stolen from k_kart I swear!! + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + sz += sine; + } + + if (!player->follower) // follower doesn't exist / isn't valid + { + CONS_Printf("Spawning follower...\n"); + // so let's spawn one! + player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); + P_SetFollowerState(player->follower, fl.idlestate); + P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear + + player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use. + /* + 0 = idle + 1 = forwards + 2 = hurt + 3 = win + 4 = lose + */ + } + else // follower exists, woo! + { + // first of all, handle states following the same model as above: + if (player->follower->tics == 1) + P_SetFollowerState(player->follower, player->follower->state->nextstate); + + // move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)! + player->follower->momx = (sx - player->follower->x)/2; + player->follower->momy = (sy - player->follower->y)/2; + player->follower->momz = (sz - player->follower->z)/6; // make z momentum a bit floatier, it'll look cute I promise! + player->follower->angle = player->mo->angle; + player->follower->color = player->mo->color; + player->follower->colorized = player->mo->colorized; + + P_SetScale(player->follower, player->mo->scale); + K_MatchGenericExtraFlags(player->follower, player->mo); + + + // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! + + // hurt or dead + if (player->kartstuff[k_spinouttimer] || player->mo->health <= 0) + { + player->follower->angle = leveltime*48*ANG1; + if (player->follower->extravalue1 != 2) + { + player->follower->extravalue1 = 2; + P_SetFollowerState(player->follower, fl.hurtstate); + } + if (player->mo->health <= 0) // if dead, snap to z pos + player->follower->z = sz; + } + else if (player->speed > 10*player->mo->scale) + { + if (player->follower->extravalue1 != 1) + { + player->follower->extravalue1 = 1; + P_SetFollowerState(player->follower, fl.followstate); + } + } + else // nvm you're slow + { + if (player->follower->extravalue1 != 0) + { + + if (player->exiting) + { + if (K_IsPlayerLosing(player)) // L + { + if (player->follower->extravalue1 != 4) + { + player->follower->extravalue1 = 4; + P_SetFollowerState(player->follower, fl.losestate); + } + } + else // W + { + if (player->follower->extravalue1 != 3) + { + player->follower->extravalue1 = 3; + P_SetFollowerState(player->follower, fl.winstate); + } + } + } + else + { + player->follower->extravalue1 = 0; + P_SetFollowerState(player->follower, fl.idlestate); + } + } + } + } +} + // // P_PlayerThink // @@ -9466,6 +9622,9 @@ void P_PlayerThink(player_t *player) K_KartPlayerThink(player, cmd); // SRB2kart + // we're done doing all of this, now take care of followers... + P_HandleFollower(player); + /* // Colormap verification { diff --git a/src/r_things.c b/src/r_things.c index 65041c6fa..7999db028 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2502,6 +2502,8 @@ void R_DrawMasked(void) // We can assume those are tied to skins somewhat, hence why they're defined here. INT32 numskins = 0; follower_t followers[MAXSKINS]; +// default followers are defined in SOC_FLWR in followers.kart / gfx.kart (depending on what exe this is, at this point) + skin_t skins[MAXSKINS]; // FIXTHIS: don't work because it must be inistilised before the config load @@ -2706,7 +2708,6 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; skin_t *skin = &skins[skinnum]; - CONS_Printf("skin\n"); if (skinnum >= 0 && skinnum < numskins) // Make sure it exists! { player->skin = skinnum; @@ -2769,16 +2770,22 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) void SetFollower(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; - + + player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! { player->followerskin = skinnum; CONS_Printf("Updated player follower num\n"); /* - We don't actually set anything there, becasuse we need to be sure that a proper player->mo is available to spawn the follower. - Moreover, the follower will self-handle itself the rest of the time, hence, its skinnum stored to the player is all we need right now. + We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself. + However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it. */ - + if (player->follower) + { + P_RemoveMobj(player->follower); + player->follower = NULL; + } + return; } diff --git a/src/r_things.h b/src/r_things.h index 57ecb0dd6..cfb862f0b 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -127,6 +127,7 @@ typedef struct follower_s // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. + INT32 dist; // distance relative to the player. (In a circle) INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. // from there on out, everything is STATES to allow customization From 2c85e79c6af173dda0b83b44fdc73cd25ac171aa Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Tue, 26 Feb 2019 17:09:24 -0600 Subject: [PATCH 004/211] Fix players crashing when joining netgames with active followers --- src/doomdef.h | 2 +- src/p_saveg.c | 7 +++++++ src/p_user.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index 5e7b7657b..ab863c6f6 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -140,7 +140,7 @@ extern FILE *logstream; #endif -#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 +//#define DEVELOP // Disable this for release builds to remove excessive cheat commands and enable MD5 checking and stuff, all in one go. :3 #ifdef DEVELOP #define VERSION 0 // Game version #define SUBVERSION 0 // more precise version number diff --git a/src/p_saveg.c b/src/p_saveg.c index ae568fabf..546280836 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3105,6 +3105,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type); } + if (mobj->player && mobj->player->follower) + { + temp = (UINT32)(size_t)mobj->player->follower; + mobj->player->follower = NULL; + if (!P_SetTarget(&mobj->player->follower, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "follower not found on %d\n", mobj->type); + } } } } diff --git a/src/p_user.c b/src/p_user.c index c2e75916b..7ee0c2d64 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8948,7 +8948,7 @@ static void P_SetFollowerState(mobj_t *f, INT32 state) } // -//P_HandleFllower +//P_HandleFollower // //Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker. static void P_HandleFollower(player_t *player) From b5b1ca6e26cf7736d7874c0600ba161cb0f8c0af Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 2 Mar 2019 22:39:42 +0100 Subject: [PATCH 005/211] Fix splitscreen, offline and death --- src/d_netcmd.c | 19 +++++++++++++++++++ src/m_menu.c | 6 +++--- src/p_user.c | 10 +++++----- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f73a53ce4..3a1c3fa2d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1317,6 +1317,10 @@ static void SendNameAndColor(void) if (players[consoleplayer].mo) players[consoleplayer].mo->color = players[consoleplayer].skincolor; + // Update follower for local games: + if (cv_follower.value >= 0 && cv_follower.value != players[consoleplayer].followerskin) + SetFollower(consoleplayer, cv_follower.value); + if (metalrecording) { // Metal Sonic is Sonic, obviously. SetPlayerSkinByNum(consoleplayer, 0); @@ -1453,6 +1457,10 @@ static void SendNameAndColor2(void) if (players[secondplaya].mo) players[secondplaya].mo->color = players[secondplaya].skincolor; + // Update follower for local games: + if (cv_follower2.value >= 0 && cv_follower2.value != players[secondplaya].followerskin) + SetFollower(secondplaya, cv_follower2.value); + if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1) { //boolean notsame; @@ -1573,6 +1581,10 @@ static void SendNameAndColor3(void) if (players[thirdplaya].mo) players[thirdplaya].mo->color = players[thirdplaya].skincolor; + // Update follower for local games: + if (cv_follower3.value >= 0 && cv_follower3.value != players[thirdplaya].followerskin) + SetFollower(thirdplaya, cv_follower3.value); + if ((foundskin = R_SkinAvailable(cv_skin3.string)) != -1) { //boolean notsame; @@ -1701,6 +1713,10 @@ static void SendNameAndColor4(void) if (players[fourthplaya].mo) players[fourthplaya].mo->color = players[fourthplaya].skincolor; + // Update follower for local games: + if (cv_follower4.value >= 0 && cv_follower4.value != players[fourthplaya].followerskin) + SetFollower(fourthplaya, cv_follower4.value); + if ((foundskin = R_SkinAvailable(cv_skin4.string)) != -1) { //boolean notsame; @@ -2129,6 +2145,9 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r } chmappending++; + + // send infos. This seems very dumb but we use this for offline games and followers as some kind of band aid + if (netgame) WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed SendNetXCmd(XD_MAP, buf, buf_p - buf); diff --git a/src/m_menu.c b/src/m_menu.c index 2c69cc70e..0db7d12a9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8576,7 +8576,7 @@ static void M_SetupMultiPlayer2(INT32 choice) setupm_cvskin = &cv_skin2; setupm_cvcolor = &cv_playercolor2; setupm_cvname = &cv_playername2; - setupm_cvfollower = &cv_follower; + setupm_cvfollower = &cv_follower2; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value @@ -8616,7 +8616,7 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_cvskin = &cv_skin3; setupm_cvcolor = &cv_playercolor3; setupm_cvname = &cv_playername3; - setupm_cvfollower = &cv_follower; + setupm_cvfollower = &cv_follower3; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value @@ -8656,7 +8656,7 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_cvskin = &cv_skin4; setupm_cvcolor = &cv_playercolor4; setupm_cvname = &cv_playername4; - setupm_cvfollower = &cv_follower; + setupm_cvfollower = &cv_follower4; setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value diff --git a/src/p_user.c b/src/p_user.c index 7ee0c2d64..43dc5663e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -9044,8 +9044,8 @@ static void P_HandleFollower(player_t *player) player->follower->extravalue1 = 2; P_SetFollowerState(player->follower, fl.hurtstate); } - if (player->mo->health <= 0) // if dead, snap to z pos - player->follower->z = sz; + if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed. + player->follower->momz = player->mo->momz; } else if (player->speed > 10*player->mo->scale) { @@ -9151,6 +9151,9 @@ void P_PlayerThink(player_t *player) player->awayviewtics = 0; // reset to zero } + // Run followes here. We need them to run even when we're dead to follow through what we're doing. + P_HandleFollower(player); + /* if (player->pflags & PF_GLIDING) { @@ -9622,9 +9625,6 @@ void P_PlayerThink(player_t *player) K_KartPlayerThink(player, cmd); // SRB2kart - // we're done doing all of this, now take care of followers... - P_HandleFollower(player); - /* // Colormap verification { From c3151039b20f262f8ab1f9172f86a4e1edbbed41 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 2 Mar 2019 22:40:58 +0100 Subject: [PATCH 006/211] leftover comment from earlier experiment --- src/d_netcmd.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3a1c3fa2d..81f14762b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2146,8 +2146,6 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r chmappending++; - // send infos. This seems very dumb but we use this for offline games and followers as some kind of band aid - if (netgame) WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed SendNetXCmd(XD_MAP, buf, buf_p - buf); From 980ab4dc771f2bc2ff19669fe113e00504ea2205 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Sat, 2 Mar 2019 18:48:00 -0500 Subject: [PATCH 007/211] follower_frame should be unsigned --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 63b1bea4e..528e9c5ff 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8046,7 +8046,7 @@ static state_t *multi_state; // used for follower display on player setup menu static INT32 follower_tics; -static INT32 follower_frame; // used for FF_ANIMATE garbo +static UINT32 follower_frame; // used for FF_ANIMATE garbo static state_t *follower_state; // this is set before entering the MultiPlayer setup menu, From 6071b78cf3054387e6c1fb809c7d6afa2a818acb Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Sat, 2 Mar 2019 18:57:52 -0500 Subject: [PATCH 008/211] fixed compiling for MSVC --- src/d_netcmd.c | 26 ++++++++++++++------------ src/dehacked.c | 20 +++++++++++--------- src/m_menu.c | 3 ++- src/p_user.c | 11 +++++++---- 4 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index ea8b0db88..0d5a93e16 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5162,22 +5162,24 @@ static void Name4_OnChange(void) // sends the follower change for players static void Follower_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; + if (!Playing()) return; // do whatever you want // there is a slight chance that we will actually use a string instead so... // let's investigate the string... - char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; strcpy(str, cv_follower.string); strcpy(cpy, cv_follower.string); strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + + if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower, set); // set it to a number. It's easier for us to send later :) } @@ -5186,20 +5188,20 @@ static void Follower_OnChange(void) static void Follower2_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; if (!Playing() || !splitscreen) return; // do whatever you want - char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; strcpy(str, cv_follower2.string); strcpy(cpy, cv_follower2.string); strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower2, set); // set it to a number. It's easier for us to send later :) } @@ -5208,20 +5210,20 @@ static void Follower2_OnChange(void) static void Follower3_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; if (!Playing() || !splitscreen) return; // do whatever you want - char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; strcpy(str, cv_follower3.string); strcpy(cpy, cv_follower3.string); strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower3, set); // set it to a number. It's easier for us to send later :) } @@ -5230,20 +5232,20 @@ static void Follower3_OnChange(void) static void Follower4_OnChange(void) { + char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; if (!Playing() || !splitscreen) return; // do whatever you want - char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; strcpy(str, cv_follower4.string); strcpy(cpy, cv_follower4.string); strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { INT32 num = R_FollowerAvailable(str); - if (num == -1) // that's an error. + char set[10]; + if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - char set[10]; sprintf(set, "%d", num); CV_StealthSet(&cv_follower4, set); // set it to a number. It's easier for us to send later :) } diff --git a/src/dehacked.c b/src/dehacked.c index 2ed609e6e..3a766df30 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -699,6 +699,14 @@ INT32 numfollowers = 0; static void readfollower(MYFILE *f) { + char *s; + char *word, *word2, dname[SKINNAMESIZE+1]; + char *tmp; + char testname[SKINNAMESIZE]; + + boolean nameset; + INT32 fallbackstate = 0; + INT32 res; if (numfollowers > MAXSKINS) { @@ -706,12 +714,7 @@ static void readfollower(MYFILE *f) return; } - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2, dname[SKINNAMESIZE+1]; - char *tmp; - - boolean nameset; - INT32 fallbackstate = 0; + s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); CONS_Printf("Adding follower...\n"); @@ -812,17 +815,16 @@ static void readfollower(MYFILE *f) // set skin name (this is just the follower's name in lowercases): // but before we do, let's... actually check if another follower isn't doing the same shit... - char testname[SKINNAMESIZE]; strcpy(testname, followers[numfollowers].name); // lower testname for skin checks... strlwr(testname); - INT32 res = R_FollowerAvailable(testname); + res = R_FollowerAvailable(testname); if (res > -1) // yikes, someone else has stolen our name already { - deh_warning("There was already a follower with the same name. (%s)", testname); INT32 startlen = strlen(testname); char cpy[2]; + deh_warning("There was already a follower with the same name. (%s)", testname); sprintf(cpy, "%d", numfollowers); memcpy(&testname[startlen], cpy, 2); // in that case, we'll be very lazy and copy numfollowers to the end of our skin name. diff --git a/src/m_menu.c b/src/m_menu.c index 528e9c5ff..82df9020e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8077,6 +8077,7 @@ static void M_DrawSetupMultiPlayerMenu(void) UINT8 i; const UINT8 *flashcol = V_GetStringColormap(highlightflags); INT32 statx, staty; + char *fname; mx = MP_PlayerSetupDef.x; my = MP_PlayerSetupDef.y; @@ -8109,7 +8110,7 @@ static void M_DrawSetupMultiPlayerMenu(void) } // draw follower string - char *fname = malloc(SKINNAMESIZE+1); + fname = malloc(SKINNAMESIZE+1); if (setupm_fakefollower == -1) strcpy(fname, "None"); diff --git a/src/p_user.c b/src/p_user.c index bfc9d8c44..7ca368b7b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8940,6 +8940,10 @@ static void P_SetFollowerState(mobj_t *f, INT32 state) //Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker. static void P_HandleFollower(player_t *player) { + follower_t fl; + angle_t an; + fixed_t zoffs; + fixed_t sx, sy, sz; if (!player->followerready) return; // we aren't ready to perform anything follower related yet. @@ -8959,13 +8963,12 @@ static void P_HandleFollower(player_t *player) return; // Before we do anything, let's be sure of where we're supposed to be - follower_t fl = followers[player->followerskin]; + fl = followers[player->followerskin]; - angle_t an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... - fixed_t zoffs = (fl.zoffs)*FRACUNIT; + an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... + zoffs = (fl.zoffs)*FRACUNIT; // do you like angle maths? I certainly don't... - fixed_t sx, sy, sz; sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT)); sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT)); From 2967638cf3b8b967408803aa4901ccb00563950c Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Sat, 2 Mar 2019 19:49:21 -0500 Subject: [PATCH 009/211] remove whitespaces --- src/info.c | 10 +++++----- src/info.h | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/info.c b/src/info.c index ac16a6edc..0367d3faf 100644 --- a/src/info.c +++ b/src/info.c @@ -3393,9 +3393,9 @@ state_t states[NUMSTATES] = {SPR_FWRK, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK4}, // S_KARMAFIREWORK3 {SPR_FWRK, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK1}, // S_KARMAFIREWORK4 {SPR_FWRK, 4|FF_FULLBRIGHT, TICRATE, {NULL}, 0, 0, S_NULL}, // S_KARMAFIREWORKTRAIL - + // followers: - + // generic chao: {SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE {SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY @@ -3407,7 +3407,7 @@ state_t states[NUMSTATES] = {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY3}, //S_GCHAOHAPPY2 {SPR_GCHA, 6, 8, {NULL}, 0, 0, S_GCHAOHAPPY4}, //S_GCHAOHAPPY3 {SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY1}, //S_GCHAOHAPPY4 - + // cheese: {SPR_CHEZ, FF_ANIMATE, -1, {NULL}, 1, 4, S_CHEESEIDLE}, //S_CHEESEIDLE {SPR_CHEZ, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_CHEESEFLY}, //S_CHEESEFLY @@ -3419,7 +3419,7 @@ state_t states[NUMSTATES] = {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY3}, //S_CHEESEHAPPY2 {SPR_CHEZ, 6, 8, {NULL}, 0, 0, S_CHEESEHAPPY4}, //S_CHEESEHAPPY3 {SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY1}, //S_CHEESEHAPPY4 - + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -20103,7 +20103,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // activesound MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags S_NULL // raisestate - }, + }, // ============================================================================================================================// diff --git a/src/info.h b/src/info.h index 418c8790f..b55795e66 100644 --- a/src/info.h +++ b/src/info.h @@ -779,10 +779,10 @@ typedef enum sprite // Xmas-specific sprites that don't fit aboxe SPR_XMS4, SPR_XMS5, - + SPR_GCHA, // follower: generic chao SPR_CHEZ, // follower: cheese - + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -4054,9 +4054,9 @@ typedef enum state S_KARMAFIREWORK3, S_KARMAFIREWORK4, S_KARMAFIREWORKTRAIL, - + // followers: - + // generic chao: S_GCHAOIDLE, S_GCHAOFLY, @@ -4068,7 +4068,7 @@ typedef enum state S_GCHAOHAPPY2, S_GCHAOHAPPY3, S_GCHAOHAPPY4, - + // cheese: S_CHEESEIDLE, S_CHEESEFLY, @@ -4882,9 +4882,9 @@ typedef enum mobj_type MT_BOOSTON, MT_LIZARDMAN, MT_LIONMAN, - + MT_KARMAFIREWORK, - + MT_FOLLOWER, #ifdef SEENAMES From d8576b0bbca6389c1113a0f432a9c9a4bb0f992b Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 3 Mar 2019 21:12:21 +0100 Subject: [PATCH 010/211] Add more customization options & fix some dumb things --- src/dehacked.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++--- src/m_menu.c | 6 +++-- src/p_user.c | 36 +++++++++++++++--------------- src/r_things.c | 2 +- src/r_things.h | 12 ++++++++-- 5 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 3a766df30..7b6fce01c 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -716,7 +716,15 @@ static void readfollower(MYFILE *f) s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - CONS_Printf("Adding follower...\n"); + // Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead. + followers[numfollowers].scale = FRACUNIT; + followers[numfollowers].atangle = 230; + followers[numfollowers].dist = 16; + followers[numfollowers].zoffs = 32; + followers[numfollowers].horzlag = 2; + followers[numfollowers].vertlag = 4; + followers[numfollowers].bobspeed = TICRATE*2; + followers[numfollowers].bobamp = 4; do { @@ -751,11 +759,36 @@ static void readfollower(MYFILE *f) strcpy(followers[numfollowers].name, word2); nameset = true; } + else if (fastcmp(word, "SCALE")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE); + followers[numfollowers].scale = get_number(word2); + } else if (fastcmp(word, "ATANGLE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); followers[numfollowers].atangle = (INT32)atoi(word2); } + else if (fastcmp(word, "HORZLAG")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].horzlag), UNDO_NONE); + followers[numfollowers].horzlag = (INT32)atoi(word2); + } + else if (fastcmp(word, "VERTLAG")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].vertlag), UNDO_NONE); + followers[numfollowers].vertlag = (INT32)atoi(word2); + } + else if (fastcmp(word, "BOBSPEED")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].bobspeed), UNDO_NONE); + followers[numfollowers].bobspeed = (INT32)atoi(word2); + } + else if (fastcmp(word, "BOBAMP")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].bobamp), UNDO_NONE); + followers[numfollowers].bobamp = (INT32)atoi(word2); + } else if (fastcmp(word, "ZOFFSET") || (fastcmp(word, "ZOFFS"))) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE); @@ -824,7 +857,7 @@ static void readfollower(MYFILE *f) { INT32 startlen = strlen(testname); char cpy[2]; - deh_warning("There was already a follower with the same name. (%s)", testname); + //deh_warning("There was already a follower with the same name. (%s)", testname); This warning probably isn't necessary anymore? sprintf(cpy, "%d", numfollowers); memcpy(&testname[startlen], cpy, 2); // in that case, we'll be very lazy and copy numfollowers to the end of our skin name. @@ -841,8 +874,28 @@ static void readfollower(MYFILE *f) if (followers[numfollowers].zoffs < 0) followers[numfollowers].zoffs = 0; + // HORZLAG and VERTLAG must ABSOLUTELY be higher than 0. If 0, the game crashes, if negative, weird shit happens! + if (followers[numfollowers].horzlag <= 0) + followers[numfollowers].horzlag = 1; + + if (followers[numfollowers].vertlag <= 0) + followers[numfollowers].vertlag = 1; + + // scale must be positive for obvious reasons, and so must both of the bob related variables + if (followers[numfollowers].scale <= 0) + followers[numfollowers].scale = 1; + + // Bob amplitude can totally be 0 + if (followers[numfollowers].bobamp < 0) + followers[numfollowers].bobamp = 1; + + // so can bob speed + if (followers[numfollowers].bobspeed < 0) + followers[numfollowers].bobspeed = 1; + + // also check if we forgot states. If we did, we will set any missing state to the follower's idlestate. + // Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable. - // also check if we forgot states :V #define NOSTATE(field, field2) \ if (!followers[numfollowers].field) \ { \ diff --git a/src/m_menu.c b/src/m_menu.c index 82df9020e..17a71d2f5 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8348,12 +8348,14 @@ static void M_DrawSetupMultiPlayerMenu(void) if (setupm_fakecolor) // inverse should never happen { + // Fake the follower's in game appearance by now also applying some of its variables! coolio, eh? + follower_t fl = followers[setupm_fakefollower]; // shortcut for our sanity // smooth floating, totally not stolen from rocket sneakers. const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK); UINT8 *colormap = R_GetTranslationColormap(-1, setupm_fakecolor, 0); - V_DrawMappedPatch(mx+65, my+90+(sine/FRACUNIT), flags, patch, colormap); + V_DrawFixedPatch((mx+65)*FRACUNIT, (my+131-fl.zoffs)*FRACUNIT+sine, fl.scale, flags, patch, colormap); Z_Free(colormap); } } diff --git a/src/p_user.c b/src/p_user.c index 7ca368b7b..1b8748b2e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8979,18 +8979,15 @@ static void P_HandleFollower(player_t *player) // ^ handled by K_matchgenericextraflags oops - // finally, add a cool floating effect to the z height, unless we have no zoffs, in which case I guess this means we WANT to be on the ground...??? - if (fl.zoffs) - { - // not stolen from k_kart I swear!! - const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); - sz += sine; - } + // finally, add a cool floating effect to the z height. + // not stolen from k_kart I swear!! + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + sz += sine; if (!player->follower) // follower doesn't exist / isn't valid { - CONS_Printf("Spawning follower...\n"); + //CONS_Printf("Spawning follower...\n"); // so let's spawn one! player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); P_SetFollowerState(player->follower, fl.idlestate); @@ -9012,14 +9009,14 @@ static void P_HandleFollower(player_t *player) P_SetFollowerState(player->follower, player->follower->state->nextstate); // move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)! - player->follower->momx = (sx - player->follower->x)/2; - player->follower->momy = (sy - player->follower->y)/2; - player->follower->momz = (sz - player->follower->z)/6; // make z momentum a bit floatier, it'll look cute I promise! + player->follower->momx = (sx - player->follower->x)/fl.horzlag; + player->follower->momy = (sy - player->follower->y)/fl.horzlag; + player->follower->momz = (sz - player->follower->z)/fl.vertlag; // make z momentum a bit floatier, it'll look cute I promise! player->follower->angle = player->mo->angle; player->follower->color = player->mo->color; player->follower->colorized = player->mo->colorized; - P_SetScale(player->follower, player->mo->scale); + P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); K_MatchGenericExtraFlags(player->follower, player->mo); @@ -9028,7 +9025,7 @@ static void P_HandleFollower(player_t *player) // hurt or dead if (player->kartstuff[k_spinouttimer] || player->mo->health <= 0) { - player->follower->angle = leveltime*48*ANG1; + player->follower->angle = -leveltime*48*ANG1; // spin out if (player->follower->extravalue1 != 2) { player->follower->extravalue1 = 2; @@ -9037,20 +9034,23 @@ static void P_HandleFollower(player_t *player) if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed. player->follower->momz = player->mo->momz; } - else if (player->speed > 10*player->mo->scale) + else if (player->speed > 10*player->mo->scale) // animation for moving fast enough { + // if we're moving fast enough, let's make the angle the direction we're moving towards. This is to avoid drifting looking awkward. + player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); + if (player->follower->extravalue1 != 1) { player->follower->extravalue1 = 1; P_SetFollowerState(player->follower, fl.followstate); } } - else // nvm you're slow + else // animations when nearly still. This includes winning and losing. { if (player->follower->extravalue1 != 0) { - if (player->exiting) + if (player->exiting) // win/ loss animations { if (K_IsPlayerLosing(player)) // L { @@ -9069,7 +9069,7 @@ static void P_HandleFollower(player_t *player) } } } - else + else // normal standstill { player->follower->extravalue1 = 0; P_SetFollowerState(player->follower, fl.idlestate); diff --git a/src/r_things.c b/src/r_things.c index b5a7f7116..f8d84b030 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2775,7 +2775,7 @@ void SetFollower(INT32 playernum, INT32 skinnum) if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! { player->followerskin = skinnum; - CONS_Printf("Updated player follower num\n"); + //CONS_Printf("Updated player follower num\n"); /* We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself. However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it. diff --git a/src/r_things.h b/src/r_things.h index cfb862f0b..ec70bef7d 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -124,11 +124,20 @@ typedef struct follower_s { char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything. char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this. - + + fixed_t scale; // Scale relative to the player's. + // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. INT32 dist; // distance relative to the player. (In a circle) INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. + + // movement options + + INT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number. + INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 4. Must be > 0 because we divide by this number. + INT32 bobamp; // Bob amplitude. Default is 4. + INT32 bobspeed; // Arbitrary modifier for bobbing speed, default is TICRATE*2 (70). // from there on out, everything is STATES to allow customization // these are only set once when the action is performed and are then free to animate however they want. @@ -138,7 +147,6 @@ typedef struct follower_s INT32 hurtstate; // state when the player is being hurt INT32 winstate; // state when the player has won INT32 losestate; // state when the player has lost - } follower_t; // ----------- From d410143ab0c02dfdc6f41b02653d2a5d4666c9c6 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 3 Mar 2019 21:16:02 +0100 Subject: [PATCH 011/211] good grief I'm an idiot --- src/r_things.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_things.h b/src/r_things.h index ec70bef7d..ca9215f9a 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -135,7 +135,7 @@ typedef struct follower_s // movement options INT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number. - INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 4. Must be > 0 because we divide by this number. + INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 6. Must be > 0 because we divide by this number. INT32 bobamp; // Bob amplitude. Default is 4. INT32 bobspeed; // Arbitrary modifier for bobbing speed, default is TICRATE*2 (70). From 3aadc5ac1be5dd7a7d07ee434a01b9867b2f2de4 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 3 Mar 2019 21:16:56 +0100 Subject: [PATCH 012/211] when you change the documentation but forget to update the code to go along --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index 7b6fce01c..f34b3b58a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -722,7 +722,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].dist = 16; followers[numfollowers].zoffs = 32; followers[numfollowers].horzlag = 2; - followers[numfollowers].vertlag = 4; + followers[numfollowers].vertlag = 6; followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobamp = 4; From 169ec43eecdaabd6faa1fafa9aafd922360fa1d1 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 Mar 2019 21:35:58 +0100 Subject: [PATCH 013/211] add some Lua support, hit confirm animations and fix some stuff --- src/dehacked.c | 18 ++++++++++++++++++ src/k_kart.c | 7 +++++++ src/lua_playerlib.c | 8 ++++++++ src/p_user.c | 31 ++++++++++++++++++++++++++++--- src/r_things.h | 10 ++++++---- 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index f34b3b58a..c038c2472 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -725,6 +725,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].vertlag = 6; followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobamp = 4; + followers[numfollowers].hitconfirmtime = TICRATE; do { @@ -835,6 +836,18 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].winstate), UNDO_NONE); followers[numfollowers].winstate = get_number(word2); } + else if (fastcmp(word, "HITSTATE") || (fastcmp(word, "HITCONFIRMSTATE"))) + { + if (word2) + strupr(word2); + DEH_WriteUndoline(word, va("%d", followers[numfollowers].hitconfirmstate), UNDO_NONE); + followers[numfollowers].hitconfirmstate = get_number(word2); + } + else if (fastcmp(word, "HITTIME") || (fastcmp(word, "HITCONFIRMTIME"))) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].hitconfirmtime), UNDO_NONE); + followers[numfollowers].hitconfirmtime = (INT32)atoi(word2); + } else deh_warning("Follower %d: unknown word '%s'", numfollowers, word); } @@ -893,6 +906,10 @@ static void readfollower(MYFILE *f) if (followers[numfollowers].bobspeed < 0) followers[numfollowers].bobspeed = 1; + // hit confirm time must be > 0 + if (followers[numfollowers].hitconfirmtime < 1) + followers[numfollowers].hitconfirmtime = 1; + // also check if we forgot states. If we did, we will set any missing state to the follower's idlestate. // Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable. @@ -909,6 +926,7 @@ if (!followers[numfollowers].field) \ NOSTATE(hurtstate, "hurtstate"); NOSTATE(losestate, "losestate"); NOSTATE(winstate, "winstate"); + NOSTATE(winstate, "hitconfirmstate"); #undef NOSTATE CONS_Printf("Added follower '%s'\n", dname); diff --git a/src/k_kart.c b/src/k_kart.c index 8e607191d..cb8b71a00 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1642,6 +1642,13 @@ void K_PlayOvertakeSound(mobj_t *source) void K_PlayHitEmSound(mobj_t *source) { + + if (source->player->follower) + { + follower_t fl = followers[source->player->followerskin]; + source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. + } + if (cv_kartvoices.value) S_StartSound(source, sfx_khitem); else diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1c37f4c45..61db7a2f7 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -319,6 +319,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->awayviewtics); else if (fastcmp(field,"awayviewaiming")) lua_pushangle(L, plr->awayviewaiming); + else if (fastcmp(field,"follower")) + LUA_PushUserdata(L, plr->follower, META_MOBJ); + else if (fastcmp(field,"followerskin")) + lua_pushinteger(L, plr->followerskin); else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); else if (fastcmp(field,"bot")) @@ -609,6 +613,10 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"awayviewaiming")) plr->awayviewaiming = luaL_checkangle(L, 3); + else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. + return NOSET; + else if (fastcmp(field,"followerskin")) + plr->followerskin = luaL_checkinteger(L, 3); else if (fastcmp(field,"spectator")) plr->spectator = lua_toboolean(L, 3); else if (fastcmp(field,"bot")) diff --git a/src/p_user.c b/src/p_user.c index 1b8748b2e..2f0998d7f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8929,6 +8929,15 @@ void P_DoTimeOver(player_t *player) */ static void P_SetFollowerState(mobj_t *f, INT32 state) { + // extravalue2 stores the last "first state" we used. + // because states default to idlestates, if we use an animation that uses an "ongoing" state line, don't reset it! + // this prevents it from looking very dumb + if (state == f->extravalue2) + return; + + // we will save the state into extravalue2. + f->extravalue2 = state; + P_SetMobjStateNF(f, state); if (f->state->tics > 0) f->tics++; @@ -9000,6 +9009,7 @@ static void P_HandleFollower(player_t *player) 2 = hurt 3 = win 4 = lose + 5 = hitconfirm (< this one uses ->movecount as timer to know when to end, and goes back to normal states afterwards, unless hurt) */ } else // follower exists, woo! @@ -9019,13 +9029,19 @@ static void P_HandleFollower(player_t *player) P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); K_MatchGenericExtraFlags(player->follower, player->mo); + // Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously. + + if (player->pflags & PF_TIMEOVER) // there is more to it than that to check for a full no contest but this isn't used for anything else. + player->follower->flags2 &= MF2_DONTDRAW; + + // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! - // hurt or dead - if (player->kartstuff[k_spinouttimer] || player->mo->health <= 0) + if (player->kartstuff[k_spinouttimer] || player->mo->state == &states[S_KART_SPIN] || player->mo->health <= 0) { - player->follower->angle = -leveltime*48*ANG1; // spin out + player->follower->movecount = 0; // cancel hit confirm. + player->follower->angle = player->frameangle; // spin out if (player->follower->extravalue1 != 2) { player->follower->extravalue1 = 2; @@ -9034,6 +9050,15 @@ static void P_HandleFollower(player_t *player) if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed. player->follower->momz = player->mo->momz; } + else if (player->follower->movecount) + { + if (player->follower->extravalue1 != 5) + { + player->follower->extravalue1 = 5; + P_SetFollowerState(player->follower, fl.hitconfirmstate); + } + player->follower->movecount--; + } else if (player->speed > 10*player->mo->scale) // animation for moving fast enough { // if we're moving fast enough, let's make the angle the direction we're moving towards. This is to avoid drifting looking awkward. diff --git a/src/r_things.h b/src/r_things.h index ca9215f9a..155c7dbd2 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -124,16 +124,16 @@ typedef struct follower_s { char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything. char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this. - + fixed_t scale; // Scale relative to the player's. - + // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. INT32 dist; // distance relative to the player. (In a circle) INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. - + // movement options - + INT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number. INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 6. Must be > 0 because we divide by this number. INT32 bobamp; // Bob amplitude. Default is 4. @@ -147,6 +147,8 @@ typedef struct follower_s INT32 hurtstate; // state when the player is being hurt INT32 winstate; // state when the player has won INT32 losestate; // state when the player has lost + INT32 hitconfirmstate; // state for hit confirm + INT32 hitconfirmtime; // time to keep the above playing for } follower_t; // ----------- From 3d5f2e4e6717f26ec2ad8f64d055136526fc61dc Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 5 Mar 2019 12:23:23 +0100 Subject: [PATCH 014/211] I'm an idiot. Fix very dumb crash caused by lazy copypaste when trying to apply fallback state on followers --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index c038c2472..acf68a582 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -926,7 +926,7 @@ if (!followers[numfollowers].field) \ NOSTATE(hurtstate, "hurtstate"); NOSTATE(losestate, "losestate"); NOSTATE(winstate, "winstate"); - NOSTATE(winstate, "hitconfirmstate"); + NOSTATE(hitconfirmstate, "hitconfirmstate"); #undef NOSTATE CONS_Printf("Added follower '%s'\n", dname); From 5475eba690316a7f1f80adcf4c4188a6ec2a5687 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Wed, 6 Mar 2019 11:36:56 -0500 Subject: [PATCH 015/211] reorder file loads --- src/d_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index 1105ba68a..e4c524079 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -931,10 +931,10 @@ static void IdentifyVersion(void) D_AddFile(va(pandf,srb2waddir,"textures.kart")); D_AddFile(va(pandf,srb2waddir,"chars.kart")); D_AddFile(va(pandf,srb2waddir,"maps.kart")); - D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #ifdef USE_PATCH_KART D_AddFile(va(pandf,srb2waddir,"patch.kart")); #endif + D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #if !defined (HAVE_SDL) || defined (HAVE_MIXER) #define MUSICTEST(str) \ From e2c72c08cd0d231178f36c55bc746bd6fba10964 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 7 Mar 2019 23:10:07 +0100 Subject: [PATCH 016/211] Fix map load crash and other additions / clean ups --- src/dehacked.c | 66 +++++++++++++++++++++++--------------------------- src/p_mobj.c | 9 +++++++ src/p_setup.c | 3 +++ src/p_user.c | 12 +++++---- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index acf68a582..bbc78d153 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -877,38 +877,32 @@ static void readfollower(MYFILE *f) } strcpy(followers[numfollowers].skinname, testname); + strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line. - // get ready to print the name... - strcpy(dname, followers[numfollowers].skinname); + // now that the skin name is ready, post process the actual name to turn the underscores into spaces! + for (INT32 i = 0; followers[numfollowers].name[i]; i++) + { + if (followers[numfollowers].name[i] == '_') + followers[numfollowers].name[i] = ' '; + } - if (followers[numfollowers].dist < 0) - followers[numfollowers].dist = 0; + // fallbacks for variables + // Print a warning if the variable is on a weird value and set it back to the minimum available if that's the case. +#define FALLBACK(field, field2, threshold, set) \ +if (followers[numfollowers].field < threshold) \ +{ \ + followers[numfollowers].field = set; \ + deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", dname, field2, set, set); \ +} \ - if (followers[numfollowers].zoffs < 0) - followers[numfollowers].zoffs = 0; - - // HORZLAG and VERTLAG must ABSOLUTELY be higher than 0. If 0, the game crashes, if negative, weird shit happens! - if (followers[numfollowers].horzlag <= 0) - followers[numfollowers].horzlag = 1; - - if (followers[numfollowers].vertlag <= 0) - followers[numfollowers].vertlag = 1; - - // scale must be positive for obvious reasons, and so must both of the bob related variables - if (followers[numfollowers].scale <= 0) - followers[numfollowers].scale = 1; - - // Bob amplitude can totally be 0 - if (followers[numfollowers].bobamp < 0) - followers[numfollowers].bobamp = 1; - - // so can bob speed - if (followers[numfollowers].bobspeed < 0) - followers[numfollowers].bobspeed = 1; - - // hit confirm time must be > 0 - if (followers[numfollowers].hitconfirmtime < 1) - followers[numfollowers].hitconfirmtime = 1; + FALLBACK(dist, "DIST", 0, 0); + FALLBACK(zoffs, "ZOFFS", 0, 0); + FALLBACK(horzlag, "HORZLAG", 1, 1); + FALLBACK(vertlag, "VERTLAG", 1, 1); + FALLBACK(bobamp, "BOBAMP", 0, 0); + FALLBACK(bobspeed, "BOBSPEED", 0, 0); + FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1); +#undef FALLBACK // also check if we forgot states. If we did, we will set any missing state to the follower's idlestate. // Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable. @@ -918,15 +912,15 @@ if (!followers[numfollowers].field) \ { \ followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \ if (!fallbackstate) \ - deh_warning("Follower %s is missing state definition for %s, no idlestate fallback was found", dname, field2); \ + deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", dname, field2); \ } \ - NOSTATE(idlestate, "idlestate"); - NOSTATE(followstate, "followstate"); - NOSTATE(hurtstate, "hurtstate"); - NOSTATE(losestate, "losestate"); - NOSTATE(winstate, "winstate"); - NOSTATE(hitconfirmstate, "hitconfirmstate"); + NOSTATE(idlestate, "IDLESTATE"); + NOSTATE(followstate, "FOLLOWSTATE"); + NOSTATE(hurtstate, "HURTSTATE"); + NOSTATE(losestate, "LOSESTATE"); + NOSTATE(winstate, "WINSTATE"); + NOSTATE(hitconfirmstate, "HITCONFIRMSTATE"); #undef NOSTATE CONS_Printf("Added follower '%s'\n", dname); diff --git a/src/p_mobj.c b/src/p_mobj.c index fd42a1d71..461f50aaa 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6556,6 +6556,13 @@ void P_MobjThinker(mobj_t *mobj) #endif switch (mobj->type) { + case MT_FOLLOWER: + // small thinker for follower: + // We cleanse ourselves from existence if our target player doesn't exist for whatever reason. (generally players leaving) + if (!mobj->target || P_MobjWasRemoved(mobj->target) || !mobj->target->player || mobj->target->player->spectator || mobj->target->player->followerskin < 0) + P_RemoveMobj(mobj); + + return; case MT_HOOP: if (mobj->fuse > 1) P_MoveHoop(mobj); @@ -10962,6 +10969,8 @@ void P_SpawnPlayer(INT32 playernum) //awayview stuff p->awayviewmobj = NULL; p->awayviewtics = 0; + + p->follower = NULL; // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. if (cv_kartdebugshrink.value && !modeattacking && !p->bot) diff --git a/src/p_setup.c b/src/p_setup.c index 1acfb7a32..7334ba78b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2356,6 +2356,9 @@ static void P_LevelInitStuff(void) // and this stupid flag as a result players[i].pflags &= ~PF_TRANSFERTOCLOSEST; + + // Wipe follower from existence to avoid crashes + players[i].follower = NULL; } // SRB2Kart: map load variables diff --git a/src/p_user.c b/src/p_user.c index f062a4618..5b187d710 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8960,7 +8960,7 @@ static void P_HandleFollower(player_t *player) // How about making sure our follower exists and is added before trying to spawn it n' all? if (player->followerskin > numfollowers-1 || player->followerskin < -1) { - CONS_Printf("Follower skin invlaid. Setting to -1.\n"); + //CONS_Printf("Follower skin invlaid. Setting to -1.\n"); player->followerskin = -1; return; } @@ -8970,7 +8970,6 @@ static void P_HandleFollower(player_t *player) return; if (player->followerskin < 0) return; - // Before we do anything, let's be sure of where we're supposed to be fl = followers[player->followerskin]; @@ -9029,12 +9028,17 @@ static void P_HandleFollower(player_t *player) P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); K_MatchGenericExtraFlags(player->follower, player->mo); + // For comeback in battle. + player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); + // Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously. if (player->pflags & PF_TIMEOVER) // there is more to it than that to check for a full no contest but this isn't used for anything else. player->follower->flags2 &= MF2_DONTDRAW; - + if (player->speed) + player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); + // if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward. // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! // hurt or dead @@ -9061,8 +9065,6 @@ static void P_HandleFollower(player_t *player) } else if (player->speed > 10*player->mo->scale) // animation for moving fast enough { - // if we're moving fast enough, let's make the angle the direction we're moving towards. This is to avoid drifting looking awkward. - player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); if (player->follower->extravalue1 != 1) { From 5418bdef01bb282dd30d767f1e2a4edf0d2fed18 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Thu, 7 Mar 2019 17:30:28 -0500 Subject: [PATCH 017/211] Always compile in GNU C89 mode --- src/Makefile.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 236d7ae2f..78f52a673 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -122,6 +122,9 @@ endif ifndef GCC295 WFLAGS+=-Wendif-labels endif +ifdef GCC40 + WFLAGS+=-std=gnu89 +endif ifdef GCC41 WFLAGS+=-Wshadow endif From d464ab5ac560da83b9c8c2d7c99bad73de83de84 Mon Sep 17 00:00:00 2001 From: Alam Arias Date: Thu, 7 Mar 2019 17:34:46 -0500 Subject: [PATCH 018/211] fixup SOC changes for C 89 mode --- src/dehacked.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index bbc78d153..870f024f9 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -707,6 +707,7 @@ static void readfollower(MYFILE *f) boolean nameset; INT32 fallbackstate = 0; INT32 res; + INT32 i; if (numfollowers > MAXSKINS) { @@ -880,7 +881,7 @@ static void readfollower(MYFILE *f) strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line. // now that the skin name is ready, post process the actual name to turn the underscores into spaces! - for (INT32 i = 0; followers[numfollowers].name[i]; i++) + for (i = 0; followers[numfollowers].name[i]; i++) { if (followers[numfollowers].name[i] == '_') followers[numfollowers].name[i] = ' '; From a8428f0ba09841d6d1fbe4765e5649388cf5b029 Mon Sep 17 00:00:00 2001 From: Wolfy Date: Sat, 6 Apr 2019 13:52:34 -0500 Subject: [PATCH 019/211] Make this compile again --- src/d_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_main.c b/src/d_main.c index da17807fc..3cc7758f5 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -940,7 +940,7 @@ static void IdentifyVersion(void) #ifdef USE_PATCH_KART D_AddFile(va(pandf,srb2waddir,"patch.kart"), startupwadfiles); #endif - D_AddFile(va(pandf,srb2waddir,"followers.kart")); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' + D_AddFile(va(pandf,srb2waddir,"followers.kart"), startupwadfiles); // merge this in GFX later, this is mostly to avoid uploading a MASSIVE patch.kart /gfx.kart for testing. -Lat' #if !defined (HAVE_SDL) || defined (HAVE_MIXER) #define MUSICTEST(str) \ From 7dff052a64c74b17cfb3b96ab3718d5cd0abc156 Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Sat, 11 May 2019 02:13:31 -0500 Subject: [PATCH 020/211] Expose followerready, match d_player.h organization --- src/lua_playerlib.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 795dab1a2..02d81f0ca 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -154,6 +154,12 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->kartspeed); else if (fastcmp(field,"kartweight")) lua_pushinteger(L, plr->kartweight); + else if (fastcmp(field,"followerskin")) + lua_pushinteger(L, plr->followerskin); + else if (fastcmp(field,"followerready")) + lua_pushboolean(L, plr->followerready); + else if (fastcmp(field,"follower")) + LUA_PushUserdata(L, plr->follower, META_MOBJ); // else if (fastcmp(field,"normalspeed")) lua_pushfixed(L, plr->normalspeed); @@ -319,10 +325,7 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->awayviewtics); else if (fastcmp(field,"awayviewaiming")) lua_pushangle(L, plr->awayviewaiming); - else if (fastcmp(field,"follower")) - LUA_PushUserdata(L, plr->follower, META_MOBJ); - else if (fastcmp(field,"followerskin")) - lua_pushinteger(L, plr->followerskin); + else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); else if (fastcmp(field,"bot")) @@ -434,6 +437,12 @@ static int player_set(lua_State *L) plr->kartspeed = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"kartweight")) plr->kartweight = (UINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"followerskin")) + plr->followerskin = luaL_checkinteger(L, 3); + else if (fastcmp(field,"followerready")) + plr->followerready = luaL_checkboolean(L, 3); + else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. + return NOSET; // else if (fastcmp(field,"normalspeed")) plr->normalspeed = luaL_checkfixed(L, 3); @@ -613,10 +622,6 @@ static int player_set(lua_State *L) } else if (fastcmp(field,"awayviewaiming")) plr->awayviewaiming = luaL_checkangle(L, 3); - else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. - return NOSET; - else if (fastcmp(field,"followerskin")) - plr->followerskin = luaL_checkinteger(L, 3); else if (fastcmp(field,"spectator")) plr->spectator = lua_toboolean(L, 3); else if (fastcmp(field,"bot")) From 54d61b188c004e5ea100e365b515d9a2404ed1fe Mon Sep 17 00:00:00 2001 From: wolfy852 Date: Sun, 12 May 2019 03:07:17 -0500 Subject: [PATCH 021/211] Have "None" work as an alias for -1 --- src/d_netcmd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4ec54d286..56e4684f5 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5335,6 +5335,13 @@ static void Follower_OnChange(void) strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { + if (stricmp(cpy, "None") == 0) + { + CV_StealthSet(&cv_follower, "-1"); + SendNameAndColor(); + return; + } + INT32 num = R_FollowerAvailable(str); char set[10]; From 1a7af92d9fe911b24638bc45e782e7952df5068a Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 12 May 2019 10:34:10 +0200 Subject: [PATCH 022/211] fix being unable to remove follower outside of netgames --- src/d_netcmd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 56e4684f5..8ea9a5028 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1346,7 +1346,7 @@ static void SendNameAndColor(void) players[consoleplayer].mo->color = players[consoleplayer].skincolor; // Update follower for local games: - if (cv_follower.value >= 0 && cv_follower.value != players[consoleplayer].followerskin) + if (cv_follower.value >= -1 && cv_follower.value != players[consoleplayer].followerskin) SetFollower(consoleplayer, cv_follower.value); if (metalrecording) @@ -1486,7 +1486,7 @@ static void SendNameAndColor2(void) players[secondplaya].mo->color = players[secondplaya].skincolor; // Update follower for local games: - if (cv_follower2.value >= 0 && cv_follower2.value != players[secondplaya].followerskin) + if (cv_follower2.value >= -1 && cv_follower2.value != players[secondplaya].followerskin) SetFollower(secondplaya, cv_follower2.value); if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1) @@ -1610,7 +1610,7 @@ static void SendNameAndColor3(void) players[thirdplaya].mo->color = players[thirdplaya].skincolor; // Update follower for local games: - if (cv_follower3.value >= 0 && cv_follower3.value != players[thirdplaya].followerskin) + if (cv_follower3.value >= -1 && cv_follower3.value != players[thirdplaya].followerskin) SetFollower(thirdplaya, cv_follower3.value); if ((foundskin = R_SkinAvailable(cv_skin3.string)) != -1) @@ -1742,7 +1742,7 @@ static void SendNameAndColor4(void) players[fourthplaya].mo->color = players[fourthplaya].skincolor; // Update follower for local games: - if (cv_follower4.value >= 0 && cv_follower4.value != players[fourthplaya].followerskin) + if (cv_follower4.value >= -1 && cv_follower4.value != players[fourthplaya].followerskin) SetFollower(fourthplaya, cv_follower4.value); if ((foundskin = R_SkinAvailable(cv_skin4.string)) != -1) From a0e27c22e7048b119c810f73e32622ea8cc4ff3e Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 12 May 2019 13:41:46 +0200 Subject: [PATCH 023/211] make followers work with replays --- src/d_netcmd.c | 25 +++++++++++++++++++ src/g_game.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++--- src/g_game.h | 1 + src/m_menu.c | 4 +-- src/p_user.c | 2 +- src/r_things.c | 4 +++ 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a7ae75db4..a4d384d77 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5577,6 +5577,15 @@ static void Follower2_OnChange(void) strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { + + if (stricmp(cpy, "None") == 0) + { + CV_StealthSet(&cv_follower2, "-1"); + SendNameAndColor2(); + return; + } + + INT32 num = R_FollowerAvailable(str); char set[10]; if (num == -1) // that's an error. @@ -5599,6 +5608,14 @@ static void Follower3_OnChange(void) strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { + + if (stricmp(cpy, "None") == 0) + { + CV_StealthSet(&cv_follower3, "-1"); + SendNameAndColor3(); + return; + } + INT32 num = R_FollowerAvailable(str); char set[10]; if (num == -1) // that's an error. @@ -5621,6 +5638,14 @@ static void Follower4_OnChange(void) strlwr(str); if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright... { + + if (stricmp(cpy, "None") == 0) + { + CV_StealthSet(&cv_follower4, "-1"); + SendNameAndColor4(); + return; + } + INT32 num = R_FollowerAvailable(str); char set[10]; if (num == -1) // that's an error. diff --git a/src/g_game.c b/src/g_game.c index 0831f7855..74e4b1906 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3251,7 +3251,7 @@ void G_AddPlayer(INT32 playernum) p->jointime = 0; p->playerstate = PST_REBORN; - demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN; // Set everything + demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything } void G_ExitLevel(void) @@ -3284,7 +3284,7 @@ const char *Gametype_Names[NUMGAMETYPES] = { "Race", // GT_RACE "Battle" // GT_MATCH - + /*"Co-op", // GT_COOP "Competition", // GT_COMPETITION "Team Match", // GT_TEAMMATCH @@ -4926,6 +4926,13 @@ void G_ReadDemoExtraData(void) M_Memcpy(player_names[p],demo_p,16); demo_p += 16; } + if (extradata & DXD_FOLLOWER) + { + // Set our follower + M_Memcpy(name, demo_p, 16); + demo_p += 16; + SetPlayerFollower(p, name); + } if (extradata & DXD_PLAYSTATE) { extradata = READUINT8(demo_p); @@ -5028,6 +5035,7 @@ void G_WriteDemoExtraData(void) WRITEUINT8(demo_p, skins[players[i].skin].kartspeed); WRITEUINT8(demo_p, skins[players[i].skin].kartweight); + } if (demo_extradata[i] & DXD_COLOR) { @@ -5045,6 +5053,15 @@ void G_WriteDemoExtraData(void) M_Memcpy(demo_p,name,16); demo_p += 16; } + if (demo_extradata[i] & DXD_FOLLOWER) + { + // write follower + memset(name, 0, 16); + strncpy(name, followers[players[i].followerskin].skinname, 16); + CONS_Printf("%s\n", name); + M_Memcpy(demo_p, name, 16); + demo_p += 16; + } if (demo_extradata[i] & DXD_PLAYSTATE) { demo_writerng = 1; @@ -5668,6 +5685,8 @@ void G_GhostTicker(void) g->p += 16; // Same tbh if (ziptic & DXD_NAME) g->p += 16; // yea + if (ziptic & DXD_FOLLOWER) + g->p += 16; // ok 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 } @@ -6396,6 +6415,12 @@ void G_BeginRecording(void) CV_SaveNetVars(&demo_p, true); // Now store some info for each in-game player + + // Lat' 12/05/19: Do note that for the first game you load, everything that gets saved here is total garbage; + // The name will always be Player , the skin sonic, the color None and the follower 0. This is only correct on subsequent games. + // In the case of said first game, the skin and the likes are updated with Got_NameAndColor, which are then saved in extradata for the demo with DXD_SKIN in r_things.c for instance. + + for (p = 0; p < MAXPLAYERS; p++) { if (playeringame[p]) { player = &players[p]; @@ -6420,6 +6445,12 @@ void G_BeginRecording(void) M_Memcpy(demo_p,name,16); demo_p += 16; + // Save follower's skin name + memset(name, 0, 16); + strncpy(name, followers[player->followerskin].skinname, 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + // Score, since Kart uses this to determine where you start on the map WRITEUINT32(demo_p, player->score); @@ -7031,7 +7062,7 @@ void G_DoPlayDemo(char *defdemoname) { UINT8 i, p; lumpnum_t l; - char skin[17],color[17],*n,*pdemoname; + char skin[17],color[17],follower[17],*n,*pdemoname; UINT8 version,subversion; UINT32 randseed; char msg[1024]; @@ -7045,6 +7076,7 @@ void G_DoPlayDemo(char *defdemoname) skin[16] = '\0'; color[16] = '\0'; + follower[16] = '\0'; // No demo name means we're restarting the current demo if (defdemoname == NULL) @@ -7280,6 +7312,10 @@ void G_DoPlayDemo(char *defdemoname) M_Memcpy(color,demo_p,16); demo_p += 16; + // Follower + M_Memcpy(follower,demo_p,16); + demo_p += 16; + demo_p += 5; // Backwards compat - some stats // SRB2kart kartspeed[0] = READUINT8(demo_p); @@ -7321,6 +7357,19 @@ void G_DoPlayDemo(char *defdemoname) break; } + // Follower + if (!SetPlayerFollower(0, follower)) + { + snprintf(msg, 1024, M_GetText("%s features a follower that is not currently loaded.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + // net var data CV_LoadNetVars(&demo_p); @@ -7474,6 +7523,11 @@ void G_DoPlayDemo(char *defdemoname) break; } + // Follower + M_Memcpy(follower, demo_p, 16); + demo_p += 16; + SetPlayerFollower(p, follower); + // Score, since Kart uses this to determine where you start on the map players[p].score = READUINT32(demo_p); @@ -7679,6 +7733,9 @@ void G_AddGhost(char *defdemoname) M_Memcpy(color, p,16); p += 16; + // Follower data was here, doesn't matter for ghosts + p += 16; + // Ghosts do not have a player structure to put this in. p++; // charability p++; // charability2 @@ -7741,6 +7798,9 @@ void G_AddGhost(char *defdemoname) M_Memcpy(color, p, 16); p += 16; + // Follower data was here, skip it, we don't care about it for ghosts. + p += 16; + p += 4; // score kartspeed = READUINT8(p); diff --git a/src/g_game.h b/src/g_game.h index a69f91421..06b9ef829 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -214,6 +214,7 @@ extern UINT8 demo_writerng; #define DXD_NAME 0x04 // name changed #define DXD_COLOR 0x08 // color changed #define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game +#define DXD_FOLLOWER 0x20 // follower was changed #define DXD_PST_PLAYING 0x01 #define DXD_PST_SPECTATING 0x02 diff --git a/src/m_menu.c b/src/m_menu.c index 0bedf62c6..e07abff9d 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -9428,8 +9428,8 @@ static void M_HandleSetupMultiPlayer(INT32 choice) size_t l; boolean exitmenu = false; // exit to previous menu and send name change - if ((choice == gamecontrol[gc_lookback][0] || choice == gamecontrol[gc_lookback][1]) && itemOn == 2) - choice == KEY_BACKSPACE // Hack to allow resetting prefcolor on controllers + if ((choice == gamecontrol[gc_fire][0] || choice == gamecontrol[gc_fire][1]) && itemOn == 2) + choice = KEY_BACKSPACE; // Hack to allow resetting prefcolor on controllers switch (choice) { diff --git a/src/p_user.c b/src/p_user.c index ea99cdcf9..8b59fd22c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -7377,7 +7377,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (P_CameraThinker(player, thiscam, resetcalled)) return true; - + if (thiscam == &camera[1]) // Camera 2 { num = 1; diff --git a/src/r_things.c b/src/r_things.c index 75d2d4349..d0b34eddd 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2719,6 +2719,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) if (player->mo) P_SetScale(player->mo, player->mo->scale); + // for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay! demo_extradata[playernum] |= DXD_SKIN; return; @@ -2751,6 +2752,9 @@ void SetFollower(INT32 playernum, INT32 skinnum) player->follower = NULL; } + // for replays: We have changed our follower mid-game; let the game know so it can do the same in the replay! + demo_extradata[playernum] |= DXD_FOLLOWER; + return; } From c54cc42e74c33678bb5a2eb5c5e25803455bdc8f Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 13 May 2019 19:53:17 +0200 Subject: [PATCH 024/211] Add command to disable followers clientside --- src/d_netcmd.c | 5 +++++ src/d_netcmd.h | 1 + src/g_game.c | 1 - src/k_kart.c | 4 ++-- src/m_menu.c | 27 +++++++++++++++------------ src/p_user.c | 6 +++--- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index a4d384d77..0b27dab98 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -282,6 +282,10 @@ consvar_t cv_follower2 = {"follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Fo consvar_t cv_follower3 = {"follower3", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_follower4 = {"follower4", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower4_OnChange, 0, NULL, NULL, 0, 0, NULL}; +// Follower toggle +static CV_PossibleValue_t followers_cons_t[] = {{0, "Yours only"}, {1, "Everyone's"}, {0, NULL}}; +consvar_t cv_showfollowers = {"showfollowers", "Everyone's", CV_SAVE, followers_cons_t, 0, 0, NULL, NULL, 0, 0, NULL}; + consvar_t cv_skipmapcheck = {"skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; INT32 cv_debug; @@ -804,6 +808,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_playercolor); CV_RegisterVar(&cv_skin); // r_things.c (skin NAME) CV_RegisterVar(&cv_follower); + CV_RegisterVar(&cv_showfollowers); // secondary player (splitscreen) CV_RegisterVar(&cv_playername2); CV_RegisterVar(&cv_playercolor2); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 9d2229c4d..966351181 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -22,6 +22,7 @@ extern consvar_t cv_playername; extern consvar_t cv_playercolor; extern consvar_t cv_skin; extern consvar_t cv_follower; +extern consvar_t cv_showfollowers; // secondary splitscreen player extern consvar_t cv_playername2; extern consvar_t cv_playercolor2; diff --git a/src/g_game.c b/src/g_game.c index 74e4b1906..dea7d6aa7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5058,7 +5058,6 @@ void G_WriteDemoExtraData(void) // write follower memset(name, 0, 16); strncpy(name, followers[players[i].followerskin].skinname, 16); - CONS_Printf("%s\n", name); M_Memcpy(demo_p, name, 16); demo_p += 16; } diff --git a/src/k_kart.c b/src/k_kart.c index bb2cbc701..1ac9fa6d7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1739,13 +1739,13 @@ void K_PlayOvertakeSound(mobj_t *source) void K_PlayHitEmSound(mobj_t *source) { - + if (source->player->follower) { follower_t fl = followers[source->player->followerskin]; source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. } - + if (cv_kartvoices.value) S_StartSound(source, sfx_khitem); else diff --git a/src/m_menu.c b/src/m_menu.c index e07abff9d..5437562d0 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1429,24 +1429,27 @@ enum static menuitem_t OP_HUDOptionsMenu[] = { - {IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 10}, - {IT_STRING | IT_CVAR | IT_CV_SLIDER, - NULL, "HUD Visibility", &cv_translucenthud, 20}, - {IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 35}, - {IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 45}, + {IT_STRING | IT_CVAR, NULL, "Show Followers", &cv_showfollowers, 10}, + + {IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 20}, + {IT_STRING | IT_CVAR | IT_CV_SLIDER, + NULL, "HUD Visibility", &cv_translucenthud, 30}, + + {IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 45}, + {IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 55}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, - NULL, "Minimap Visibility", &cv_kartminimap, 60}, - {IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 70}, - {IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 80}, + NULL, "Minimap Visibility", &cv_kartminimap, 70}, + {IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 80}, + {IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 90}, - {IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 95}, + {IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 105}, // highlight info - (GOOD HIGHLIGHT, WARNING HIGHLIGHT) - 105 (see M_DrawHUDOptions) - {IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 120}, + {IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 130}, - {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 135}, + {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 145}, }; // Ok it's still called chatoptions but we'll put ping display in here to be clean @@ -10621,7 +10624,7 @@ static void M_DrawHUDOptions(void) const char *str1 = " Warning highlight"; const char *str2 = ","; const char *str3 = "Good highlight"; - INT32 x = BASEVIDWIDTH - currentMenu->x + 2, y = currentMenu->y + 105; + INT32 x = BASEVIDWIDTH - currentMenu->x + 2, y = currentMenu->y + 115; INT32 w0 = V_StringWidth(str0, 0), w1 = V_StringWidth(str1, 0), w2 = V_StringWidth(str2, 0), w3 = V_StringWidth(str3, 0); M_DrawGenericMenu(); diff --git a/src/p_user.c b/src/p_user.c index 8b59fd22c..756d94f95 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8171,9 +8171,9 @@ static void P_HandleFollower(player_t *player) player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); // Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously. - - if (player->pflags & PF_TIMEOVER) // there is more to it than that to check for a full no contest but this isn't used for anything else. - player->follower->flags2 &= MF2_DONTDRAW; + // Also make the follower invisible if we choose not to have it displayed because it isn't ours. (also quick hacky check for f12) + if (player->pflags & PF_TIMEOVER || (!cv_showfollowers.value && (!P_IsDisplayPlayer(player) || displayplayers[0] != consoleplayer) )) + player->follower->flags2 |= MF2_DONTDRAW; if (player->speed) player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); From a6ef3c12b047f78f47efe41e5130f96b07652bd5 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Wed, 17 Jul 2019 23:55:39 -0400 Subject: [PATCH 025/211] Seperate MF2_DONTDRAW and MF2_SHADOW into it's own varied set of flags -- drawflags. - Split MF2_DONTDRAW into MFD_DONTDRAWP[1-4], also replaces MFE_DRAWONLYFORP[1-4]. - Split MF2_SHADOW into MFD_FULLBRIGHT and MFD_TRANS80. I also added an entire spectrum of options for transparency & brightness overrides, since I've found myself wishing for stuff like that before. - Tethering was updated for it's client-sided drawing to apply to splitscreens too. - Removed cv_transparency. The transparency overrides don't seem to work yet (obvious on things using MFD_SHADOW), just running out of time to look into it so I'm pushing what I have. --- src/dehacked.c | 8 +- src/g_game.c | 6 +- src/hardware/hw_main.c | 201 ++++++++++++++++++----------------------- src/hardware/hw_md2.c | 27 +++++- src/k_kart.c | 95 ++++++++----------- src/k_kart.h | 1 + src/p_enemy.c | 22 ++--- src/p_inter.c | 4 +- src/p_mobj.c | 153 ++++++++++++++----------------- src/p_mobj.h | 48 ++++++++-- src/p_user.c | 30 +++--- src/r_main.c | 2 - src/r_main.h | 1 - src/r_segs.c | 10 +- src/r_things.c | 87 +++++++----------- src/r_things.h | 2 +- 16 files changed, 324 insertions(+), 373 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index be45f3f0f..5834234bf 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8033,7 +8033,7 @@ static const char *const MOBJFLAG2_LIST[] = { "AXIS", // It's a NiGHTS axis! (For faster checking) "TWOD", // Moves like it's in a 2D level "DONTRESPAWN", // Don't respawn this object! - "DONTDRAW", // Don't generate a vissprite + "\x01", // free: 1<<3 (name un-matchable) "AUTOMATIC", // Thrown ring has automatic properties "RAILRING", // Thrown ring has rail properties "BOUNCERING", // Thrown ring has bounce properties @@ -8050,7 +8050,7 @@ static const char *const MOBJFLAG2_LIST[] = { "JUSTATTACKED", // can be pushed by other moving mobjs "FIRING", // turret fire "SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it. - "SHADOW", // Fuzzy draw, makes targeting harder. + "\x01", // free: 1<<20 (name un-matchable) "STRONGBOX", // Flag used for "strong" random monitors. "OBJECTFLIP", // Flag for objects that always have flipped gravity. "SKULLFLY", // Special handling: skull in flight. @@ -8073,10 +8073,6 @@ static const char *const MOBJEFLAG_LIST[] = { "JUSTBOUNCEDWALL", // SRB2Kart: Mobj already bounced off a wall this tic "SPRUNG", // Mobj was already sprung this tic "APPLYPMOMZ", // Platform movement - "DRAWONLYFORP1", // SRB2Kart: Splitscreen sprite draw flags - "DRAWONLYFORP2", - "DRAWONLYFORP3", - "DRAWONLYFORP4", NULL }; diff --git a/src/g_game.c b/src/g_game.c index e406e29d1..c19571ad3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2510,7 +2510,7 @@ static inline void G_PlayerFinishLevel(INT32 player) memset(p->kartstuff, 0, sizeof (p->kartstuff)); // SRB2kart p->ringweapons = 0; - p->mo->flags2 &= ~MF2_SHADOW; // cancel invisibility + p->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); // cancel invisibility P_FlashPal(p, 0, 0); // Resets p->starpostangle = 0; p->starposttime = 0; @@ -5982,7 +5982,7 @@ void G_PreviewRewind(tic_t previewtime) if (!info->playerinfo[i].ingame || !info->playerinfo[i].player.mo) { if (players[i].mo) - players[i].mo->flags2 |= MF2_DONTDRAW; + players[i].mo->drawflags |= MFD_DONTDRAW; continue; } @@ -5990,7 +5990,7 @@ void G_PreviewRewind(tic_t previewtime) if (!players[i].mo) continue; //@TODO spawn temp object to act as a player display - players[i].mo->flags2 &= ~MF2_DONTDRAW; + players[i].mo->drawflags &= ~MFD_DONTDRAW; P_UnsetThingPosition(players[i].mo); #define TWEEN(pr) info->playerinfo[i].mobj.pr + FixedMul((INT32) (next_info->playerinfo[i].mobj.pr - info->playerinfo[i].mobj.pr), tweenvalue) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 0d024dc65..28eceac6f 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -4256,45 +4256,12 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t sSurf.FlatColor.s.blue = 0x00; sSurf.FlatColor.s.green = 0x00; - /*if (spr->mobj->frame & FF_TRANSMASK || spr->mobj->flags2 & MF2_SHADOW) - { - sector_t *sector = spr->mobj->subsector->sector; - UINT8 lightlevel = 255; - extracolormap_t *colormap = sector->extra_colormap; - - if (sector->numlights) - { - INT32 light = R_GetPlaneLight(sector, spr->mobj->floorz, false); - - if (!(spr->mobj->frame & FF_FULLBRIGHT)) - { - lightlevel = *sector->lightlist[light].lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) - lightlevel = 128 + (lightlevel>>1); - } - - if (sector->lightlist[light].extra_colormap) - colormap = sector->lightlist[light].extra_colormap; - } - else - { - lightlevel = sector->lightlevel; - - if (sector->extra_colormap) - colormap = sector->extra_colormap; - } - - if (colormap) - sSurf.FlatColor.rgba = HWR_Lighting(lightlevel/2, colormap->rgba, colormap->fadergba, false, true); - else - sSurf.FlatColor.rgba = HWR_Lighting(lightlevel/2, NORMALFOG, FADEFOG, false, true); - }*/ - // shadow is always half as translucent as the sprite itself - if (!cv_translucency.value) // use default translucency (main sprite won't have any translucency) - sSurf.FlatColor.s.alpha = 0x80; // default - else if (spr->mobj->flags2 & MF2_SHADOW) - sSurf.FlatColor.s.alpha = 0x20; + if (spr->mobj->drawflags & MFD_TRANSMASK) + { + HWR_TranstableToAlpha((spr->mobj->drawflags & MFD_TRANSMASK) - MFD_TRANS10, &sSurf); + sSurf.FlatColor.s.alpha /= 2; //cut alpha in half! + } else if (spr->mobj->frame & FF_TRANSMASK) { HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &sSurf); @@ -4356,6 +4323,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) FUINT lightlevel; FBITFIELD blend = 0; UINT8 alpha; + UINT8 brightmode = 0; INT32 i; float realtop, realbot, top, bot; @@ -4468,16 +4436,8 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) // co-ordinates memcpy(wallVerts, baseWallVerts, sizeof(baseWallVerts)); - if (!cv_translucency.value) // translucency disabled - { - Surf.FlatColor.s.alpha = 0xFF; - blend = PF_Translucent|PF_Occlude; - } - else if (spr->mobj->flags2 & MF2_SHADOW) - { - Surf.FlatColor.s.alpha = 0x40; - blend = PF_Translucent; - } + if (spr->mobj->drawflags & MFD_TRANSMASK) + blend = HWR_TranstableToAlpha((spr->mobj->drawflags & MFD_TRANSMASK) - MFD_TRANS10, &Surf); else if (spr->mobj->frame & FF_TRANSMASK) blend = HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf); else @@ -4494,15 +4454,30 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) temp = FLOAT_TO_FIXED(realtop); + 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; + } + #ifdef ESLOPE // Start with the lightlevel and colormap from the top of the sprite lightlevel = 255; colormap = list[sector->numlights - 1].extra_colormap; - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) { lightlevel = *list[sector->numlights - 1].lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) + if (brightmode == 2) lightlevel = 128 + (lightlevel>>1); } @@ -4512,10 +4487,10 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) : sector->lightlist[i].height; if (h <= temp) { - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) { lightlevel = *list[i-1].lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) + if (brightmode == 2) lightlevel = 128 + (lightlevel>>1); } colormap = list[i-1].extra_colormap; @@ -4524,10 +4499,10 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) } #else i = R_GetPlaneLight(sector, temp, false); - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) { lightlevel = *list[i].lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) + if (brightmode == 2) lightlevel = 128 + (lightlevel>>1); } colormap = list[i].extra_colormap; @@ -4544,10 +4519,10 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) // even if we aren't changing colormap or lightlevel, we still need to continue drawing down the sprite if (!(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) { - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) { lightlevel = *list[i].lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) + if (brightmode == 2) lightlevel = 128 + (lightlevel>>1); } colormap = list[i].extra_colormap; @@ -4839,12 +4814,28 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) { sector_t *sector = spr->mobj->subsector->sector; UINT8 lightlevel = 255; + UINT8 brightmode = 0; extracolormap_t *colormap = sector->extra_colormap; - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + 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 (brightmode != 1) { lightlevel = sector->lightlevel; - if (spr->mobj->frame & FF_SEMIBRIGHT) + if (brightmode == 2) lightlevel = 128 + (lightlevel>>1); } @@ -4856,16 +4847,9 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) { FBITFIELD blend = 0; - if (!cv_translucency.value) // translucency disabled - { - Surf.FlatColor.s.alpha = 0xFF; - blend = PF_Translucent|PF_Occlude; - } - else if (spr->mobj->flags2 & MF2_SHADOW) - { - Surf.FlatColor.s.alpha = 0x40; - blend = PF_Translucent; - } + + if (spr->mobj->drawflags & MFD_TRANSMASK) + blend = HWR_TranstableToAlpha((spr->mobj->frame & MFD_TRANSMASK) - MFD_TRANS10, &Surf); else if (spr->mobj->frame & FF_TRANSMASK) blend = HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf); else @@ -4934,15 +4918,31 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr) { sector_t *sector = spr->mobj->subsector->sector; UINT8 lightlevel = 255; + 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 (sector->numlights) { INT32 light; light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) lightlevel = *sector->lightlist[light].lightlevel; if (sector->lightlist[light].extra_colormap) @@ -4950,14 +4950,14 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr) } else { - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) lightlevel = sector->lightlevel; if (sector->extra_colormap) colormap = sector->extra_colormap; } - if (spr->mobj->frame & FF_SEMIBRIGHT) + if (brightmode == 2) lightlevel = 128 + (lightlevel>>1); if (colormap) @@ -4966,11 +4966,8 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr) Surf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, false); } - if (spr->mobj->flags2 & MF2_SHADOW) - { - Surf.FlatColor.s.alpha = 0x40; - blend = PF_Translucent; - } + if (spr->mobj->drawflags & MFD_TRANSMASK) + blend = HWR_TranstableToAlpha((spr->mobj->frame & MFD_TRANSMASK) - MFD_TRANS10, &Surf); else if (spr->mobj->frame & FF_TRANSMASK) blend = HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf); else @@ -5062,7 +5059,7 @@ static void HWR_SortVisSprites(void) best = gr_vsprsortedhead.next; for (i = 0; i < gr_visspritecount; i++) { - if ((best->mobj->flags2 & MF2_SHADOW) || (best->mobj->frame & FF_TRANSMASK)) + if ((best->mobj->drawflags & MFD_TRANSMASK) || (best->mobj->frame & FF_TRANSMASK)) { if (best == gr_vsprsortedhead.next) { @@ -5482,27 +5479,14 @@ static void HWR_AddSprites(sector_t *sec) { for (thing = sec->thinglist; thing; thing = thing->snext) { - if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW) + if (thing->sprite == SPR_NULL) continue; - if (splitscreen) - { - if (thing->eflags & MFE_DRAWONLYFORP1) - if (viewssnum != 0) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP2) - if (viewssnum != 1) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (viewssnum != 2) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (viewssnum != 3) - continue; - } + if ((viewssnum == 0 && (thing->drawflags & MFD_DONTDRAWP1)) + || (viewssnum == 1 && (thing->drawflags & MFD_DONTDRAWP2)) + || (viewssnum == 2 && (thing->drawflags & MFD_DONTDRAWP3)) + || (viewssnum == 3 && (thing->drawflags & MFD_DONTDRAWP4))) + continue; approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); @@ -5517,27 +5501,14 @@ static void HWR_AddSprites(sector_t *sec) // Draw everything in sector, no checks for (thing = sec->thinglist; thing; thing = thing->snext) { - if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW) + if (thing->sprite == SPR_NULL) continue; - if (splitscreen) - { - if (thing->eflags & MFE_DRAWONLYFORP1) - if (viewssnum != 0) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP2) - if (viewssnum != 1) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (viewssnum != 2) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (viewssnum != 3) - continue; - } + if ((viewssnum == 0 && (thing->drawflags & MFD_DONTDRAWP1)) + || (viewssnum == 1 && (thing->drawflags & MFD_DONTDRAWP2)) + || (viewssnum == 2 && (thing->drawflags & MFD_DONTDRAWP3)) + || (viewssnum == 3 && (thing->drawflags & MFD_DONTDRAWP4))) + continue; HWR_ProjectSprite(thing); } diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index d217f4094..44a7a26e5 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -879,15 +879,31 @@ void HWR_DrawMD2(gr_vissprite_t *spr) { sector_t *sector = spr->mobj->subsector->sector; UINT8 lightlevel = 255; + 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 (sector->numlights) { INT32 light; light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) lightlevel = *sector->lightlist[light].lightlevel; if (sector->lightlist[light].extra_colormap) @@ -895,13 +911,16 @@ void HWR_DrawMD2(gr_vissprite_t *spr) } else { - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (brightmode != 1) lightlevel = sector->lightlevel; if (sector->extra_colormap) colormap = sector->extra_colormap; } + if (brightmode == 2) + lightlevel = 128 + (lightlevel>>1); + if (colormap) Surf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false); else @@ -927,8 +946,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr) //if (tics > durs) //durs = tics; - if (spr->mobj->flags2 & MF2_SHADOW) - Surf.FlatColor.s.alpha = 0x40; + if (spr->mobj->drawflags & MFD_TRANSMASK) + HWR_TranstableToAlpha((spr->mobj->drawflags & MFD_TRANSMASK) - MFD_TRANS10, &Surf); else if (spr->mobj->frame & FF_TRANSMASK) HWR_TranstableToAlpha((spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT, &Surf); else diff --git a/src/k_kart.c b/src/k_kart.c index c56c7c982..e3202903b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -595,6 +595,24 @@ void K_RegisterKartStuff(void) //} +// Not sure what's a good place for this function +// I'll get around to splitting *everything* in this file into better places soon... +UINT16 K_GetPlayerDontDrawFlag(player_t *player) +{ + UINT16 flag = 0; + + if (player == &players[displayplayers[0]]) + flag = MFD_DONTDRAWP1; + else if (player == &players[displayplayers[1]]) + flag = MFD_DONTDRAWP2; + else if (player == &players[displayplayers[2]]) + flag = MFD_DONTDRAWP3; + else if (player == &players[displayplayers[3]]) + flag = MFD_DONTDRAWP4; + + return flag; +} + boolean K_IsPlayerLosing(player_t *player) { INT32 winningpos = 1; @@ -1629,9 +1647,9 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur band->colorized = true; band->fuse = 2; if (transparent) - band->flags2 |= MF2_SHADOW; - if (!P_IsDisplayPlayer(player) && !P_IsDisplayPlayer(victim)) - band->flags2 |= MF2_DONTDRAW; + band->drawflags |= MFD_SHADOW; + + band->drawflags |= MFD_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim)); } curx += stepx; @@ -1852,11 +1870,7 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) K_FlipFromObject(mo, master); // visibility (usually for hyudoro) - mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); + mo->drawflags = (master->drawflags & MFD_DONTDRAW); } static void K_SpawnDashDustRelease(player_t *player) @@ -1915,7 +1929,7 @@ static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the m P_SetTarget(&sparks->target, player->mo); P_SetScale(sparks, (sparks->destscale = player->mo->scale)); K_MatchGenericExtraFlags(sparks, player->mo); - sparks->flags2 |= MF2_DONTDRAW; + sparks->drawflags |= MFD_DONTDRAW; } /** \brief Calculates the respawn timer and drop-boosting @@ -3390,7 +3404,7 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) } if (translucent) - dust->flags2 |= MF2_SHADOW; + dust->drawflags |= MFD_SHADOW; } void K_SpawnDraftDust(mobj_t *mo) @@ -4545,9 +4559,9 @@ static void K_MoveHeldObjects(player_t *player) cur->flags &= ~MF_NOCLIPTHING; if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1)) - cur->flags2 |= MF2_DONTDRAW; + cur->drawflags |= MFD_DONTDRAW; else - cur->flags2 &= ~MF2_DONTDRAW; + cur->drawflags &= ~MFD_DONTDRAW; if (num & 1) P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L)); @@ -5020,7 +5034,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) //ghost->momy = (3*player->mo->momy)/4; //ghost->momz = (3*player->mo->momz)/4; if (leveltime & 1) - ghost->flags2 |= MF2_DONTDRAW; + ghost->drawflags |= MFD_DONTDRAW; } if (P_IsObjectOnGround(player->mo)) @@ -5056,7 +5070,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) debtflag->color = player->skincolor; debtflag->fuse = 2; if (P_IsDisplayPlayer(player)) - debtflag->flags2 |= MF2_DONTDRAW; + debtflag->drawflags |= MFD_DONTDRAW; } } @@ -6430,61 +6444,31 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->kartstuff[k_hyudorotimer] > 0) { - if (splitscreen) - { - if (leveltime & 1) - player->mo->flags2 |= MF2_DONTDRAW; - else - player->mo->flags2 &= ~MF2_DONTDRAW; - - if (player->kartstuff[k_hyudorotimer] >= (1*TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyudorotime-(1*TICRATE/2)) - { - if (player == &players[displayplayers[1]]) - player->mo->eflags |= MFE_DRAWONLYFORP2; - else if (player == &players[displayplayers[2]] && splitscreen > 1) - player->mo->eflags |= MFE_DRAWONLYFORP3; - else if (player == &players[displayplayers[3]] && splitscreen > 2) - player->mo->eflags |= MFE_DRAWONLYFORP4; - else if (player == &players[displayplayers[0]]) - player->mo->eflags |= MFE_DRAWONLYFORP1; - else - player->mo->flags2 |= MF2_DONTDRAW; - } - else - player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); - } + if (leveltime & 1) + player->mo->drawflags |= MFD_DONTDRAW; else { - if (P_IsDisplayPlayer(player) - || (!P_IsDisplayPlayer(player) && (player->kartstuff[k_hyudorotimer] < (1*TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)))) - { - if (leveltime & 1) - player->mo->flags2 |= MF2_DONTDRAW; - else - player->mo->flags2 &= ~MF2_DONTDRAW; - } + if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyudorotime-(TICRATE/2)) + player->mo->drawflags &= ~K_GetPlayerDontDrawFlag(player); else - player->mo->flags2 |= MF2_DONTDRAW; + player->mo->drawflags &= ~MFD_DONTDRAW; } player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints } else if (player->kartstuff[k_hyudorotimer] == 0) - { - player->mo->flags2 &= ~MF2_DONTDRAW; - player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); - } + player->mo->drawflags &= ~MFD_DONTDRAW; if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) // dead in match? you da bomb { K_DropItems(player); //K_StripItems(player); K_StripOther(player); - player->mo->flags2 |= MF2_SHADOW; + player->mo->drawflags |= MFD_SHADOW; player->powers[pw_flashing] = player->kartstuff[k_comebacktimer]; } else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0) { - player->mo->flags2 &= ~MF2_SHADOW; + player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); } } @@ -9240,7 +9224,7 @@ static void K_drawKartFirstPerson(void) UINT8 *colmap = NULL; ticcmd_t *cmd = &stplyr->cmd; - if (stplyr->spectator || !stplyr->mo || (stplyr->mo->flags2 & MF2_DONTDRAW)) + if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) return; if (stplyr == &players[displayplayers[1]] && splitscreen) @@ -9262,10 +9246,7 @@ static void K_drawKartFirstPerson(void) { if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !splitscreen) y++; - // the following isn't EXPLICITLY right, it just gets the result we want, but i'm too lazy to look up the right way to do it - if (stplyr->mo->flags2 & MF2_SHADOW) - splitflags |= FF_TRANS80; - else if (stplyr->mo->frame & FF_TRANSMASK) + if (stplyr->mo->frame & FF_TRANSMASK) splitflags |= (stplyr->mo->frame & FF_TRANSMASK); } diff --git a/src/k_kart.h b/src/k_kart.h index b91e8c8a1..1d48fb928 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -20,6 +20,7 @@ UINT8 K_GetKartColorByName(const char *name); void K_RegisterKartStuff(void); +UINT16 K_GetPlayerDontDrawFlag(player_t *player); boolean K_IsPlayerLosing(player_t *player); boolean K_IsPlayerWanted(player_t *player); fixed_t K_GetKartGameSpeedScalar(SINT8 value); diff --git a/src/p_enemy.c b/src/p_enemy.c index 10bfb4d52..ff4af507e 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3409,10 +3409,10 @@ void A_BubbleSpawn(mobj_t *actor) if (!(actor->eflags & MFE_UNDERWATER)) { // Don't draw or spawn bubbles above water - actor->flags2 |= MF2_DONTDRAW; + actor->drawflags |= MFD_DONTDRAW; return; } - actor->flags2 &= ~MF2_DONTDRAW; + actor->drawflags &= ~MFD_DONTDRAW; if (!(actor->flags2 & MF2_AMBUSH)) { @@ -3544,9 +3544,9 @@ void A_BubbleCheck(mobj_t *actor) return; #endif if (actor->eflags & MFE_UNDERWATER) - actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw + actor->drawflags &= ~MFD_DONTDRAW; // underwater so draw else - actor->flags2 |= MF2_DONTDRAW; // above water so don't draw + actor->drawflags |= MFD_DONTDRAW; // above water so don't draw } // Function: A_AttractChase @@ -3659,9 +3659,9 @@ void A_AttractChase(mobj_t *actor) // Rings flicker before disappearing if (actor->fuse && actor->fuse < 5*TICRATE && (leveltime & 1)) - actor->flags2 |= MF2_DONTDRAW; + actor->drawflags |= MFD_DONTDRAW; else - actor->flags2 &= ~MF2_DONTDRAW; + actor->drawflags &= ~MFD_DONTDRAW; // spilled rings have ghost trails and get capped to a certain speed if (actor->type == (mobjtype_t)actor->info->reactiontime) @@ -3882,9 +3882,9 @@ void A_ThrownRing(mobj_t *actor) // spilled rings (and thrown bounce) flicker before disappearing if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE && actor->type != MT_THROWNGRENADE) - actor->flags2 |= MF2_DONTDRAW; + actor->drawflags |= MFD_DONTDRAW; else - actor->flags2 &= ~MF2_DONTDRAW; + actor->drawflags &= ~MFD_DONTDRAW; if (actor->tracer && actor->tracer->health <= 0) P_SetTarget(&actor->tracer, NULL); @@ -5365,9 +5365,9 @@ void A_CrawlaCommanderThink(mobj_t *actor) thefloor = actor->floorz; if (actor->fuse & 1) - actor->flags2 |= MF2_DONTDRAW; + actor->drawflags |= MFD_DONTDRAW; else - actor->flags2 &= ~MF2_DONTDRAW; + actor->drawflags &= ~MFD_DONTDRAW; if (actor->reactiontime > 0) actor->reactiontime--; @@ -8803,7 +8803,7 @@ void A_RandomShadowFrame(mobj_t *actor) fake->destscale = FRACUNIT*3/2; fake->angle = actor->angle; fake->tics = -1; - actor->flags2 |= MF2_DONTDRAW; + actor->drawflags |= MFD_DONTDRAW; actor->extravalue1 = 1; } diff --git a/src/p_inter.c b/src/p_inter.c index de14b3db9..9a053b68e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2218,7 +2218,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) target->player->score = 0; }*/ - target->flags2 &= ~MF2_DONTDRAW; + target->drawflags &= ~MFD_DONTDRAW; } // if killed by a player @@ -2917,7 +2917,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) { mobj_t *boom; player->mo->flags |= (MF_NOGRAVITY|MF_NOCLIP); - player->mo->flags2 |= MF2_DONTDRAW; + 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; diff --git a/src/p_mobj.c b/src/p_mobj.c index 970829812..c4b084c9b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6011,14 +6011,14 @@ static void P_NightsItemChase(mobj_t *thing) if (thing->info->painstate) P_SetMobjState(thing,thing->info->painstate); else - thing->flags2 |= MF2_SHADOW; + thing->drawflags |= MFD_SHADOW; } else { if (thing->info->painstate) P_SetMobjState(thing,thing->info->spawnstate); else - thing->flags2 &= ~MF2_SHADOW; + thing->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); } } @@ -6212,7 +6212,7 @@ void P_RunShadows(void) if (!mobj->target || P_MobjWasRemoved(mobj->target)) { - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; continue; // shouldn't you already be dead? } @@ -6225,7 +6225,7 @@ void P_RunShadows(void) if (((mobj->target->eflags & MFE_VERTICALFLIP) && mobj->target->z+mobj->target->height > mobj->target->ceilingz) || (!(mobj->target->eflags & MFE_VERTICALFLIP) && mobj->target->z < floorz)) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; // First scale to the same radius P_SetScale(mobj, FixedDiv(mobj->target->radius, mobj->info->radius)); @@ -6619,30 +6619,7 @@ void P_MobjThinker(mobj_t *mobj) offz = mobj->target->height / 5; } - if (mobj->target->eflags & MFE_DRAWONLYFORP1) // groooooaann... - mobj->eflags |= MFE_DRAWONLYFORP1; - else - mobj->eflags &= ~MFE_DRAWONLYFORP1; - - if (mobj->target->eflags & MFE_DRAWONLYFORP2) - mobj->eflags |= MFE_DRAWONLYFORP2; - else - mobj->eflags &= ~MFE_DRAWONLYFORP2; - - if (mobj->target->eflags & MFE_DRAWONLYFORP3) - mobj->eflags |= MFE_DRAWONLYFORP3; - else - mobj->eflags &= ~MFE_DRAWONLYFORP3; - - if (mobj->target->eflags & MFE_DRAWONLYFORP4) - mobj->eflags |= MFE_DRAWONLYFORP4; - else - mobj->eflags &= ~MFE_DRAWONLYFORP4; - - if (mobj->target->flags2 & MF2_DONTDRAW) - mobj->flags2 |= MF2_DONTDRAW; - else - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags = (mobj->target->drawflags & MFD_DONTDRAW); if (mobj->target->eflags & MFE_VERTICALFLIP) offz += 4*FRACUNIT; @@ -6705,7 +6682,7 @@ void P_MobjThinker(mobj_t *mobj) || (P_IsDisplayPlayer(mobj->target->player)) #endif ) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; P_UnsetThingPosition(mobj); mobj->x = mobj->target->x; @@ -6755,7 +6732,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->tracer->colorized = false; } - if (!(mobj->flags2 & MF2_DONTDRAW)) + if (!(mobj->drawflags & MFD_DONTDRAW)) { const INT32 numberdisplaymin = ((mobj->target->player->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2); @@ -6765,7 +6742,7 @@ void P_MobjThinker(mobj_t *mobj) P_SetMobjState(mobj, S_PLAYERARROW_BOX); mobj->tracer->sprite = SPR_ITEM; mobj->tracer->frame = FF_FULLBRIGHT|(((mobj->target->player->kartstuff[k_itemroulette] % (13*3)) / 3) + 1); - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; } else if (mobj->target->player->kartstuff[k_stolentimer] > 0) { @@ -6773,16 +6750,16 @@ void P_MobjThinker(mobj_t *mobj) mobj->tracer->sprite = SPR_ITEM; mobj->tracer->frame = FF_FULLBRIGHT|KITEM_HYUDORO; if (leveltime & 2) - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; else - mobj->tracer->flags2 |= MF2_DONTDRAW; + mobj->tracer->drawflags |= MFD_DONTDRAW; } else if ((mobj->target->player->kartstuff[k_stealingtimer] > 0) && (leveltime & 2)) { P_SetMobjState(mobj, S_PLAYERARROW_BOX); mobj->tracer->sprite = SPR_ITEM; mobj->tracer->frame = FF_FULLBRIGHT|KITEM_HYUDORO; - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; } else if (mobj->target->player->kartstuff[k_eggmanexplode] > 1) { @@ -6790,9 +6767,9 @@ void P_MobjThinker(mobj_t *mobj) mobj->tracer->sprite = SPR_ITEM; mobj->tracer->frame = FF_FULLBRIGHT|KITEM_EGGMAN; if (leveltime & 1) - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; else - mobj->tracer->flags2 |= MF2_DONTDRAW; + mobj->tracer->drawflags |= MFD_DONTDRAW; } else if (mobj->target->player->kartstuff[k_rocketsneakertimer] > 1) { @@ -6801,9 +6778,9 @@ void P_MobjThinker(mobj_t *mobj) mobj->tracer->sprite = SPR_ITEM; mobj->tracer->frame = FF_FULLBRIGHT|KITEM_ROCKETSNEAKER; if (leveltime & 1) - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; else - mobj->tracer->flags2 |= MF2_DONTDRAW; + mobj->tracer->drawflags |= MFD_DONTDRAW; } else if (mobj->target->player->kartstuff[k_growshrinktimer] > 0) { @@ -6812,9 +6789,9 @@ void P_MobjThinker(mobj_t *mobj) mobj->tracer->frame = FF_FULLBRIGHT|KITEM_GROW; if (leveltime & 1) - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; else - mobj->tracer->flags2 |= MF2_DONTDRAW; + mobj->tracer->drawflags |= MFD_DONTDRAW; } else if (mobj->target->player->kartstuff[k_itemtype] && mobj->target->player->kartstuff[k_itemamount] > 0) { @@ -6843,12 +6820,12 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target->player->kartstuff[k_itemheld]) { if (leveltime & 1) - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; else - mobj->tracer->flags2 |= MF2_DONTDRAW; + mobj->tracer->drawflags |= MFD_DONTDRAW; } else - mobj->tracer->flags2 &= ~MF2_DONTDRAW; + mobj->tracer->drawflags &= ~MFD_DONTDRAW; } else { @@ -6889,7 +6866,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->movecount = 0; } else - mobj->tracer->flags2 |= MF2_DONTDRAW; + mobj->tracer->drawflags |= MFD_DONTDRAW; } else if (mobj->health > 0) { @@ -6912,10 +6889,10 @@ void P_MobjThinker(mobj_t *mobj) return; } - if (mobj->tracer->flags2 & MF2_DONTDRAW) - mobj->flags2 |= MF2_DONTDRAW; + if (mobj->tracer->drawflags & MFD_DONTDRAW) + mobj->drawflags |= MFD_DONTDRAW; else - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; P_UnsetThingPosition(mobj); mobj->x = mobj->target->x; @@ -7015,9 +6992,10 @@ void P_MobjThinker(mobj_t *mobj) mobj->z = mobj->target->z + (mobj->target->height) + FixedMul(8*FRACUNIT, mobj->target->scale); // Adjust height for height changes if (mobj->threshold <= 35) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; else - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; + if (mobj->threshold <= 30) mobj->threshold = 40; mobj->threshold--; @@ -7302,14 +7280,15 @@ void P_MobjThinker(mobj_t *mobj) break; case MT_PLAYER: /// \todo Have the player's dead body completely finish its animation even if they've already respawned. - if (!(mobj->flags2 & MF2_DONTDRAW)) + // This feels like it has some serious potential for breakage. Is there anything else we can base this off of instead of a drawing flag? + if (!(mobj->drawflags & MFD_DONTDRAW)) { if (!mobj->fuse) { // Go away. /// \todo Actually go ahead and remove mobj completely, and fix any bugs and crashes doing this creates. Chasecam should stop moving, and F12 should never return to it. mobj->momz = 0; if (mobj->player) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; else // safe to remove, nobody's going to complain! { P_RemoveMobj(mobj); @@ -7338,7 +7317,7 @@ void P_MobjThinker(mobj_t *mobj) case MT_ORBINAUT_SHIELD: case MT_BANANA_SHIELD: case MT_EGGMANITEM_SHIELD: - mobj->flags2 ^= MF2_DONTDRAW; + mobj->drawflags ^= MFD_DONTDRAW; break; case MT_JAWZ: case MT_JAWZ_DUD: @@ -7346,7 +7325,7 @@ void P_MobjThinker(mobj_t *mobj) P_SetMobjState(mobj, mobj->info->xdeathstate); // fallthru case MT_JAWZ_SHIELD: - mobj->flags2 ^= MF2_DONTDRAW; + mobj->drawflags ^= MFD_DONTDRAW; break; case MT_SSMINE: case MT_SPBEXPLOSION: @@ -7366,7 +7345,7 @@ void P_MobjThinker(mobj_t *mobj) return; case MT_CDUFO: if (mobj->fuse > TICRATE) - mobj->flags2 ^= MF2_DONTDRAW; // only by good fortune does this end with it having MF2_DONTDRAW... don't touch! + mobj->drawflags ^= MFD_DONTDRAW; // only by good fortune does this end with it having MFD_DONTDRAW... don't touch! break; case MT_SMK_PIPE: if (mobj->flags2 & MF2_AMBUSH) @@ -7375,7 +7354,7 @@ void P_MobjThinker(mobj_t *mobj) P_SetMobjStateNF(mobj, mobj->info->spawnstate); /* FALLTHRU */ case MT_SMK_MOLE: - mobj->flags2 ^= MF2_DONTDRAW; + mobj->drawflags ^= MFD_DONTDRAW; if (P_IsObjectOnGround(mobj)) { P_RemoveMobj(mobj); @@ -7396,7 +7375,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->frame &= (~FF_FULLBRIGHT); } - mobj->flags2 ^= MF2_DONTDRAW; + mobj->drawflags ^= MFD_DONTDRAW; if (P_IsObjectOnGround(mobj)) { P_RemoveMobj(mobj); @@ -7675,7 +7654,7 @@ void P_MobjThinker(mobj_t *mobj) case MT_NIGHTSDRONE: if (mobj->state >= &states[S_NIGHTSDRONE_SPARKLING1] && mobj->state <= &states[S_NIGHTSDRONE_SPARKLING16]) { - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; mobj->z = mobj->floorz + mobj->height + (mobj->spawnpoint->options >> ZSHIFT) * FRACUNIT; mobj->angle = 0; @@ -7701,7 +7680,7 @@ void P_MobjThinker(mobj_t *mobj) { mobj->flags &= ~MF_NOGRAVITY; P_SetMobjState(mobj, S_NIGHTSDRONE1); - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; } } else if (mobj->tracer && mobj->tracer->player) @@ -7709,7 +7688,7 @@ void P_MobjThinker(mobj_t *mobj) if (!(mobj->tracer->player->pflags & PF_NIGHTSMODE)) { mobj->flags &= ~MF_NOGRAVITY; - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; P_SetMobjState(mobj, S_NIGHTSDRONE1); } else if (!mobj->tracer->player->bonustime) @@ -7746,7 +7725,7 @@ void P_MobjThinker(mobj_t *mobj) P_RemoveMobj(mobj->target); P_SetTarget(&mobj->target, NULL); } - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; } } else if (mobj->tracer && mobj->tracer->player) @@ -7766,10 +7745,10 @@ void P_MobjThinker(mobj_t *mobj) mobj->flags |= MF_NOGRAVITY; } else - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; } else // Not NiGHTS - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; } mobj->angle += ANG10; if (mobj->z <= mobj->floorz) @@ -8293,7 +8272,7 @@ void P_MobjThinker(mobj_t *mobj) K_MatchGenericExtraFlags(mobj, mobj->target); if (leveltime & 1) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; } break; case MT_PLAYERRETICULE: @@ -8305,7 +8284,7 @@ void P_MobjThinker(mobj_t *mobj) P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z); break; case MT_INSTASHIELDB: - mobj->flags2 ^= MF2_DONTDRAW; + mobj->drawflags ^= MFD_DONTDRAW; K_MatchGenericExtraFlags(mobj, mobj->target); /* FALLTHRU */ case MT_INSTASHIELDA: @@ -8459,9 +8438,9 @@ void P_MobjThinker(mobj_t *mobj) if (state < mobj->info->spawnstate || state > mobj->info->spawnstate+19) P_SetMobjState(mobj, mobj->info->spawnstate); if (mobj->target->player->kartstuff[k_comebacktimer] < TICRATE && (leveltime & 1)) - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; else - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; } else { @@ -8476,9 +8455,9 @@ void P_MobjThinker(mobj_t *mobj) P_SetMobjState(mobj, mobj->info->painstate); if (mobj->target->player->powers[pw_flashing] && (leveltime & 1)) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; else - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; } // Update mobj antigravity status: @@ -8508,10 +8487,10 @@ void P_MobjThinker(mobj_t *mobj) cur->colorized = true; K_FlipFromObject(cur, mobj->target); - if (mobj->flags2 & MF2_DONTDRAW) - cur->flags2 |= MF2_DONTDRAW; + if (mobj->drawflags & MFD_DONTDRAW) + cur->drawflags |= MFD_DONTDRAW; else - cur->flags2 &= ~MF2_DONTDRAW; + cur->drawflags &= ~MFD_DONTDRAW; cur = cur->hnext; } @@ -9357,7 +9336,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type); // Transfer flags2 (strongbox, objectflip) - newmobj->flags2 = mobj->flags2 & ~MF2_DONTDRAW; + newmobj->flags2 = mobj->flags2; } P_RemoveMobj(mobj); // make sure they disappear return; @@ -9407,7 +9386,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s return; } else if (((mobj->type == MT_RANDOMITEM && mobj->threshold == 69) || mobj->type == MT_EGGMANITEM || mobj->type == MT_FALLINGROCK) && mobj->fuse <= TICRATE) - mobj->flags2 ^= MF2_DONTDRAW; + mobj->drawflags ^= MFD_DONTDRAW; } I_Assert(mobj != NULL); @@ -9629,13 +9608,13 @@ void P_SceneryThinker(mobj_t *mobj) && mobj->extravalue1 > 0 && mobj->extravalue2 >= 2) { if (mobj->extravalue2 == 2) // I don't know why the normal logic doesn't work for this. - mobj->flags2 ^= MF2_DONTDRAW; + mobj->drawflags ^= MFD_DONTDRAW; else { if (mobj->fuse == mobj->extravalue2) - mobj->flags2 &= ~MF2_DONTDRAW; + mobj->drawflags &= ~MFD_DONTDRAW; else - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; } } @@ -10188,10 +10167,18 @@ mobj_t *P_SpawnShadowMobj(mobj_t * caster) // do not set the state with P_SetMobjState, // because action routines can not be called yet - if (caster->frame & FF_FULLBRIGHT) - st = &states[S_WHITESHADOW]; + st = &states[info->spawnstate]; + + if (caster->drawflags & MFD_BRIGHTMASK) + { + if (caster->drawflags & MFD_FULLBRIGHT) + st = &states[S_WHITESHADOW]; + } else - st = &states[info->spawnstate]; + { + if (caster->frame & FF_FULLBRIGHT) + st = &states[S_WHITESHADOW]; + } mobj->state = st; mobj->tics = st->tics; @@ -10980,7 +10967,7 @@ void P_SpawnPlayer(INT32 playernum) { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + P_GetPlayerHeight(p)+16*FRACUNIT, MT_PLAYERARROW); P_SetTarget(&overheadarrow->target, mobj); - overheadarrow->flags2 |= MF2_DONTDRAW; + overheadarrow->drawflags |= MFD_DONTDRAW; P_SetScale(overheadarrow, mobj->destscale); if (p->spectator && pcount > 1) // HEY! No being cheap... @@ -11006,10 +10993,10 @@ void P_SpawnPlayer(INT32 playernum) P_SetTarget(&mo->target, mobj); mo->angle = (diff * (i-1)); mo->color = mobj->color; - if (mobj->flags2 & MF2_DONTDRAW) - mo->flags2 |= MF2_DONTDRAW; + if (mobj->drawflags & MFD_DONTDRAW) + mo->drawflags |= MFD_DONTDRAW; else - mo->flags2 &= ~MF2_DONTDRAW; + mo->drawflags &= ~MFD_DONTDRAW; } } } diff --git a/src/p_mobj.h b/src/p_mobj.h index dfc8fc738..f82dd5e14 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -108,7 +108,7 @@ typedef enum // Don't use the blocklinks (inert but displayable) MF_NOBLOCKMAP = 1<<4, // Thin, paper-like collision bound (for visual equivalent, see FF_PAPERSPRITE) - MF_PAPERCOLLISION = 1<<5, + MF_PAPERCOLLISION = 1<<5, // You can push this object. It can activate switches and things by pushing it on top. MF_PUSHABLE = 1<<6, // Object is a boss. @@ -160,7 +160,7 @@ typedef enum MF_GRENADEBOUNCE = 1<<28, // Run the action thinker on spawn. MF_RUNSPAWNFUNC = 1<<29, - // Don't remap in Encore mode. + // Don't remap in Encore mode. (Not a drawflag so that it's settable by mobjinfo.) MF_DONTENCOREMAP = 1<<30, // free: 1<<31 } mobjflag_t; @@ -170,7 +170,7 @@ typedef enum MF2_AXIS = 1, // It's a NiGHTS axis! (For faster checking) MF2_TWOD = 1<<1, // Moves like it's in a 2D level MF2_DONTRESPAWN = 1<<2, // Don't respawn this object! - MF2_DONTDRAW = 1<<3, // Don't generate a vissprite + // free: 1<<3 MF2_AUTOMATIC = 1<<4, // Thrown ring has automatic properties MF2_RAILRING = 1<<5, // Thrown ring has rail properties MF2_BOUNCERING = 1<<6, // Thrown ring has bounce properties @@ -187,7 +187,7 @@ typedef enum MF2_JUSTATTACKED = 1<<17, // can be pushed by other moving mobjs MF2_FIRING = 1<<18, // turret fire MF2_SUPERFIRE = 1<<19, // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it. - MF2_SHADOW = 1<<20, // Fuzzy draw, makes targeting harder. + // free: 1<<20 MF2_STRONGBOX = 1<<21, // Flag used for "strong" random monitors. MF2_OBJECTFLIP = 1<<22, // Flag for objects that always have flipped gravity. MF2_SKULLFLY = 1<<23, // Special handling: skull in flight. @@ -241,14 +241,42 @@ typedef enum MFE_SPRUNG = 1<<8, // Platform movement MFE_APPLYPMOMZ = 1<<9, - // SRB2Kart: Splitscreen sprite display; very wasteful but I couldn't think of another way to do it... - MFE_DRAWONLYFORP1 = 1<<10, - MFE_DRAWONLYFORP2 = 1<<11, - MFE_DRAWONLYFORP3 = 1<<12, - MFE_DRAWONLYFORP4 = 1<<13, // free: to and including 1<<15 } mobjeflag_t; +// +// Mobj drawing flags +// Set by hex, to make masking shenanigans easier to keep track of. +// +typedef enum +{ + // Don't generate a vissprite for individual screens + MFD_DONTDRAWP1 = 0x0001, + MFD_DONTDRAWP2 = 0x0002, + MFD_DONTDRAWP3 = 0x0004, + MFD_DONTDRAWP4 = 0x0008, + // Transparency override flags + MFD_TRANS10 = 0x0010, + MFD_TRANS20 = 0x0020, + MFD_TRANS30 = 0x0030, + MFD_TRANS40 = 0x0040, + MFD_TRANS50 = 0x0050, + MFD_TRANS60 = 0x0060, + MFD_TRANS70 = 0x0070, + MFD_TRANS80 = 0x0080, + MFD_TRANS90 = 0x0090, + MFD_TRANSMASK = 0x00F0, + // Brightness override flags + MFD_FULLBRIGHT = 0x0100, + MFD_SEMIBRIGHT = 0x0200, + MFD_NOBRIGHT = 0x0300, + MFD_BRIGHTMASK = 0x0F00, + // Shortcuts + MFD_DONTDRAW = MFD_DONTDRAWP1|MFD_DONTDRAWP2|MFD_DONTDRAWP3|MFD_DONTDRAWP4, + MFD_SHADOW = MFD_TRANS80|MFD_FULLBRIGHT, + // free: to and including 0x8000 +} mobjdflag_t; + // // PRECIPITATION flags ?! ?! ?! // @@ -266,6 +294,7 @@ typedef enum { // Ran the thinker this tic. PCF_THUNK = 32, } precipflag_t; + // Map Object definition. typedef struct mobj_s { @@ -306,6 +335,7 @@ typedef struct mobj_s UINT32 flags; // flags from mobjinfo tables UINT32 flags2; // MF2_ flags UINT16 eflags; // extra flags + UINT16 drawflags; // Rendering-related flags. These are not synched. void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin) // Player and mobj sprites in multiplayer modes are modified diff --git a/src/p_user.c b/src/p_user.c index afa0966cf..d1551e3f1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -650,7 +650,7 @@ static void P_DeNightserizePlayer(player_t *player) player->mo->flags &= ~MF_NOGRAVITY; - player->mo->flags2 &= ~MF2_DONTDRAW; + player->mo->drawflags &= ~MFD_DONTDRAW; // Restore aiming angle if (player == &players[consoleplayer]) @@ -729,7 +729,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->mo->flags |= MF_NOGRAVITY; - player->mo->flags2 |= MF2_DONTDRAW; + player->mo->drawflags |= MFD_DONTDRAW; player->nightstime = player->startedtime = nighttime*TICRATE; player->bonustime = false; @@ -1605,7 +1605,7 @@ void P_SpawnShieldOrb(player_t *player) if (shieldobj->info->painstate) P_SetMobjState(shieldobj,shieldobj->info->painstate); else - shieldobj->flags2 |= MF2_SHADOW; + shieldobj->drawflags |= MFD_SHADOW; } } } @@ -3361,7 +3361,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // SRB2kart - unused. return; P_SetWeaponDelay(player, (3*TICRATE)/2); - mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW); + mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING); // Rail has no unique thrown object, therefore its sound plays here. S_StartSound(player->mo, sfx_rail1); @@ -4282,7 +4282,7 @@ static void P_SpectatorMovement(player_t *player) if (mo) { mo->flags2 |= MF2_RAILRING; - mo->flags2 |= MF2_DONTDRAW; + mo->drawflags |= MFD_DONTDRAW; mo->flags |= MF_NOCLIPHEIGHT; mo->flags |= MF_NOCLIP; mo->flags &= ~MF_MISSILE; @@ -5025,7 +5025,7 @@ static void P_NiGHTSMovement(player_t *player) radius = player->mo->target->radius; player->mo->flags |= MF_NOGRAVITY; - player->mo->flags2 |= MF2_DONTDRAW; + player->mo->drawflags |= MFD_DONTDRAW; P_SetScale(player->mo->tracer, player->mo->scale); if (player->mo->eflags & MFE_VERTICALFLIP) @@ -7042,7 +7042,7 @@ static void P_DeathThink(player_t *player) if (player->mo) { player->mo->flags |= (MF_NOGRAVITY|MF_NOCLIP); - player->mo->flags2 |= MF2_DONTDRAW; + player->mo->drawflags |= MFD_DONTDRAW; } } else @@ -8250,9 +8250,9 @@ void P_PlayerThink(player_t *player) if (player->playerstate == PST_DEAD) { if (player->spectator) - player->mo->flags2 |= MF2_SHADOW; + player->mo->drawflags |= MFD_SHADOW; else - player->mo->flags2 &= ~MF2_SHADOW; + player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); P_DeathThink(player); return; @@ -8431,7 +8431,7 @@ void P_PlayerThink(player_t *player) { if (player == &players[displayplayers[i]] && !camera[i].chase) { - gmobj->flags2 |= MF2_DONTDRAW; + gmobj->drawflags |= MFD_DONTDRAW; break; } } @@ -8571,16 +8571,16 @@ void P_PlayerThink(player_t *player) { if (player->powers[pw_flashing] > 0 && player->powers[pw_flashing] < K_GetKartFlashing(player) && (leveltime & 1)) - player->mo->flags2 |= MF2_DONTDRAW; + player->mo->drawflags |= MFD_DONTDRAW; else - player->mo->flags2 &= ~MF2_DONTDRAW; + player->mo->drawflags &= ~MFD_DONTDRAW; } /*else if (player->mo->tracer) { if (player->powers[pw_flashing] & 1) - player->mo->tracer->flags2 |= MF2_DONTDRAW; + player->mo->tracer->drawflags |= MFD_DONTDRAW; else - player->mo->tracer->flags2 &= ~MF2_DONTDRAW; + player->mo->tracer->drawflags &= ~MFD_DONTDRAW; }*/ player->pflags &= ~PF_SLIDING; @@ -8958,7 +8958,7 @@ void P_PlayerAfterThink(player_t *player) // spectator invisibility and nogravity. if ((netgame || multiplayer) && player->spectator) { - player->mo->flags2 |= MF2_DONTDRAW; + player->mo->drawflags |= MFD_DONTDRAW; player->mo->flags |= MF_NOGRAVITY; } diff --git a/src/r_main.c b/src/r_main.c index 0d14bed73..9bdaceae6 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -176,7 +176,6 @@ consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NU consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_translucency = {"translucency", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_drawdist = {"drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //consvar_t cv_drawdist_nights = {"drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_drawdist_precip = {"drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -1494,7 +1493,6 @@ void R_RegisterEngineStuff(void) return; //CV_RegisterVar(&cv_precipdensity); - CV_RegisterVar(&cv_translucency); CV_RegisterVar(&cv_drawdist); //CV_RegisterVar(&cv_drawdist_nights); CV_RegisterVar(&cv_drawdist_precip); diff --git a/src/r_main.h b/src/r_main.h index 38a589682..7ec27e5d2 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -77,7 +77,6 @@ extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2, cv_chasecam3, cv_chasecam4; extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4; extern consvar_t cv_shadow, cv_shadowoffs; -extern consvar_t cv_translucency; extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip; extern consvar_t cv_fov; extern consvar_t cv_skybox; diff --git a/src/r_segs.c b/src/r_segs.c index 399f514bc..f3fd34eb5 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -179,14 +179,8 @@ static void R_DrawWallSplats(void) colfunc = basecolfunc; break; case SPLATDRAWMODE_TRANS: - if (!cv_translucency.value) - colfunc = basecolfunc; - else - { - dc_transmap = transtables + ((tr_trans50 - 1)<extra_colormap = sector->lightlist[i].extra_colormap; -/* - if (thing->frame & FF_TRANSMASK) - ; - else if (thing->flags2 & MF2_SHADOW) - ; - else -*/ if (!((newsprite->cut & SC_FULLBRIGHT) && (!newsprite->extra_colormap || !(newsprite->extra_colormap->fog & 1)))) { @@ -1499,19 +1492,27 @@ static void R_ProjectSprite(mobj_t *thing) vis->transmap = NULL; // specific translucency - if (!cv_translucency.value) - ; // no translucency - else if (thing->flags2 & MF2_SHADOW) // actually only the player should use this (temporary invisibility) - vis->transmap = transtables + ((tr_trans80-1)<drawflags & MFD_TRANSMASK) // Object is forcing transparency to a specific value + vis->transmap = transtables + ((thing->drawflags & MFD_TRANSMASK) - MFD_TRANS10); else if (thing->frame & FF_TRANSMASK) - vis->transmap = transtables + (thing->frame & FF_TRANSMASK) - 0x10000; + vis->transmap = transtables + ((thing->frame & FF_TRANSMASK) - FF_TRANS10); - if (thing->frame & FF_FULLBRIGHT || thing->flags2 & MF2_SHADOW) - vis->cut |= SC_FULLBRIGHT; - else if (thing->frame & FF_SEMIBRIGHT) - vis->cut |= SC_SEMIBRIGHT; + if (thing->drawflags & MFD_BRIGHTMASK) + { + if (thing->drawflags & MFD_FULLBRIGHT) + vis->cut |= SC_FULLBRIGHT; + else if (thing->drawflags & MFD_SEMIBRIGHT) + vis->cut |= SC_SEMIBRIGHT; + } + else + { + if (thing->frame & FF_FULLBRIGHT) + vis->cut |= SC_FULLBRIGHT; + else if (thing->frame & FF_SEMIBRIGHT) + vis->cut |= SC_SEMIBRIGHT; + } - if (vis->cut & SC_FULLBRIGHT + if ((vis->cut & SC_FULLBRIGHT) && (!vis->extra_colormap || !(vis->extra_colormap->fog & 1))) { // full bright: goggles @@ -1702,7 +1703,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) // specific translucency if (thing->frame & FF_TRANSMASK) - vis->transmap = (thing->frame & FF_TRANSMASK) - 0x10000 + transtables; + vis->transmap = ((thing->frame & FF_TRANSMASK) - FF_TRANS10) + transtables; else vis->transmap = NULL; @@ -1758,27 +1759,14 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) { for (thing = sec->thinglist; thing; thing = thing->snext) { - if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW) + if (thing->sprite == SPR_NULL) continue; - if (splitscreen) - { - if (thing->eflags & MFE_DRAWONLYFORP1) - if (viewssnum != 0) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP2) - if (viewssnum != 1) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (viewssnum != 2) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (viewssnum != 3) - continue; - } + if ((viewssnum == 0 && (thing->drawflags & MFD_DONTDRAWP1)) + || (viewssnum == 1 && (thing->drawflags & MFD_DONTDRAWP2)) + || (viewssnum == 2 && (thing->drawflags & MFD_DONTDRAWP3)) + || (viewssnum == 3 && (thing->drawflags & MFD_DONTDRAWP4))) + continue; approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); @@ -1793,27 +1781,14 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) // Draw everything in sector, no checks for (thing = sec->thinglist; thing; thing = thing->snext) { - if (thing->sprite == SPR_NULL || thing->flags2 & MF2_DONTDRAW) + if (thing->sprite == SPR_NULL) continue; - if (splitscreen) - { - if (thing->eflags & MFE_DRAWONLYFORP1) - if (viewssnum != 0) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP2) - if (viewssnum != 1) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (viewssnum != 2) - continue; - - if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (viewssnum != 3) - continue; - } + if ((viewssnum == 0 && (thing->drawflags & MFD_DONTDRAWP1)) + || (viewssnum == 1 && (thing->drawflags & MFD_DONTDRAWP2)) + || (viewssnum == 2 && (thing->drawflags & MFD_DONTDRAWP3)) + || (viewssnum == 3 && (thing->drawflags & MFD_DONTDRAWP4))) + continue; R_ProjectSprite(thing); } diff --git a/src/r_things.h b/src/r_things.h index 4837b4aee..1632303af 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -147,7 +147,7 @@ typedef struct vissprite_s lighttable_t *colormap; // for color translation and shadow draw // maxbright frames as well - UINT8 *transmap; // for MF2_SHADOW sprites, which translucency table to use + UINT8 *transmap; // which translucency table to use INT32 mobjflags; From 40b21a60f68f9ab487899c26ee440c7563424448 Mon Sep 17 00:00:00 2001 From: Sally Cochenour Date: Mon, 24 Feb 2020 01:07:12 -0500 Subject: [PATCH 026/211] Fix merge --- src/p_mobj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index b754dda24..297132099 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8766,10 +8766,10 @@ void P_MobjThinker(mobj_t *mobj) mobj->angle = ang; if (leveltime & 1) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; if (trans >= NUMTRANSMAPS) - mobj->flags2 |= MF2_DONTDRAW; + mobj->drawflags |= MFD_DONTDRAW; else if (trans == 0) mobj->frame = (mobj->frame & ~FF_TRANSMASK); else From e099ba01180e27caab51a4ec21fad7437234674a Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 02:36:56 -0800 Subject: [PATCH 027/211] The big bad HTTP master server Cvars: http_masterserver is the url to the master server's API. masterserver_token may be an authentication token. --- src/Makefile | 3 + src/d_clisrv.c | 3 +- src/hms123311.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++ src/mserv.c | 56 ++++++++ src/mserv.h | 2 + 5 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 src/hms123311.c diff --git a/src/Makefile b/src/Makefile index 214c2bf7a..2cb35ab62 100644 --- a/src/Makefile +++ b/src/Makefile @@ -369,6 +369,8 @@ else NOPNG=1 endif +LIBS+=-lcurl + ifdef STATIC LIBS:=-static $(LIBS) endif @@ -537,6 +539,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/w_wad.o \ $(OBJDIR)/filesrch.o \ $(OBJDIR)/mserv.o \ + $(OBJDIR)/hms123311.o\ $(OBJDIR)/i_tcp.o \ $(OBJDIR)/lzf.o \ $(OBJDIR)/vid_copy.o \ diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 213f5dde0..2ab9a4f34 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1862,7 +1862,8 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) // Make sure MS version matches our own, to // thwart nefarious servers who lie to the MS. - if (strcmp(version, server_list[i].version) == 0) + /* lol bruh, that version COMES from the servers */ + //if (strcmp(version, server_list[i].version) == 0) { INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); if (node == -1) diff --git a/src/hms123311.c b/src/hms123311.c new file mode 100644 index 000000000..4ce05bfa0 --- /dev/null +++ b/src/hms123311.c @@ -0,0 +1,346 @@ +// SONIC ROBO BLAST 2 JART +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// \brief HTTP based master server + +#include + +#include "doomdef.h" +#include "d_clisrv.h" +#include "command.h" +#include "mserv.h" +#include "i_tcp.h"/* for current_port */ + +consvar_t cv_http_masterserver = { + "http_masterserver", + "http://www.jameds.org/MS/0", + CV_SAVE, + + NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + +consvar_t cv_masterserver_token = { + "masterserver_token", + "", + CV_SAVE, + + NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + +static int hms_started; + +static char hms_server_token[sizeof "xxx.xxx.xxx.xxx/xxxxx"]; + +struct HMS_buffer +{ + CURL *curl; + char *buffer; + int needle; + int end; +}; + +static size_t +HMS_on_read (char *s, size_t _1, size_t n, void *userdata) +{ + struct HMS_buffer *buffer; + buffer = userdata; + if (n < ( buffer->end - buffer->needle )) + { + memcpy(&buffer->buffer[buffer->needle], s, n); + buffer->needle += n; + return n; + } + else + return 0; +} + +static struct HMS_buffer * +HMS_connect (const char *format, ...) +{ + va_list ap; + CURL *curl; + char *url; + size_t seek; + struct HMS_buffer *buffer; + + if (! hms_started) + { + curl_global_init(CURL_GLOBAL_ALL); + hms_started = 1; + } + + curl = curl_easy_init(); + + seek = strlen(cv_http_masterserver.string) + 1;/* + '/' */ + + va_start (ap, format); + url = malloc(seek + vsnprintf(0, 0, format, ap) + 1); + va_end (ap); + + sprintf(url, "%s/", cv_http_masterserver.string); + + va_start (ap, format); + vsprintf(&url[seek], format, ap); + va_end (ap); + + CONS_Printf("HMS: connecting '%s'...\n", url); + + buffer = malloc(sizeof *buffer); + buffer->curl = curl; + /* I just allocated 4k and fuck it! */ + buffer->end = 4096; + buffer->buffer = malloc(buffer->end); + buffer->needle = 0; + + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); + + free(url); + + return buffer; +} + +static int +HMS_do (struct HMS_buffer *buffer) +{ + long status; + curl_easy_perform(buffer->curl); + curl_easy_getinfo(buffer->curl, CURLINFO_RESPONSE_CODE, &status); + buffer->buffer[buffer->needle] = '\0'; + if (status != 200) + { + CONS_Alert(CONS_ERROR, + "Master server error %ld: %s", + status, + buffer->buffer + ); + return 0; + } + else + return 1; +} + +static void +HMS_end (struct HMS_buffer *buffer) +{ + free(buffer->buffer); + curl_easy_cleanup(buffer->curl); + free(buffer); +} + +int +HMS_in_use (void) +{ + return cv_http_masterserver.string[0]; +} + +void +HMS_fetch_rooms (void) +{ + struct HMS_buffer *hms; + char *p; + char *end; + + char *id; + char *title; + char *motd; + + int i; + + hms = HMS_connect("rooms?token=%s&verbose", cv_masterserver_token.string); + if (HMS_do(hms)) + { + p = hms->buffer; + for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") ); ++i) + { + *end = '\0'; + id = strtok(p, "\n"); + title = strtok(0, "\n"); + motd = strtok(0, ""); + if (id && title && motd) + { + room_list[i].header.buffer[0] = 1; + + room_list[i].id = atoi(id); + strlcpy(room_list[i].name, title, sizeof room_list[i].name); + strlcpy(room_list[i].motd, motd, sizeof room_list[i].motd); + + p = ( end + 3 ); + } + else + break; + } + + room_list[i].header.buffer[0] = 1; + room_list[i].id = 0; + strcpy(room_list[i].name, "All"); + strcpy(room_list[i].motd, "Wildcard."); + + room_list[i + 1].header.buffer[0] = 0; + } + HMS_end(hms); +} + +int +HMS_register (void) +{ + char post[256]; + struct HMS_buffer *hms; + char *title; + int ok; + + hms = HMS_connect("rooms/%d/register?token=%s", + ms_RoomId, + cv_masterserver_token.string + ); + title = curl_easy_escape(hms->curl, cv_servername.string, 0); + sprintf(post, + "port=%d&title=%s", + current_port, + title + ); + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); + + ok = HMS_do(hms); + + if (ok) + { + strlcpy(hms_server_token, strtok(hms->buffer, "\n"), + sizeof hms_server_token); + } + + curl_free(title); + HMS_end(hms); + + return ok; +} + +void +HMS_unlist (void) +{ + struct HMS_buffer *hms; + hms = HMS_connect("servers/%s/unlist?token=%s", + hms_server_token, + cv_masterserver_token.string + ); + curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); + /*curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, + cv_masterserver_token.string);*/ + HMS_do(hms); + HMS_end(hms); +} + +void +HMS_update (void) +{ + char post[256]; + struct HMS_buffer *hms; + char *title; + + hms = HMS_connect("servers/%s/update?token=%s", + hms_server_token, + cv_masterserver_token.string + ); + + title = curl_easy_escape(hms->curl, cv_servername.string, 0); + sprintf(post, "title=%s", title); + curl_free(title); + + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); + + HMS_do(hms); + HMS_end(hms); +} + +void +HMS_list_servers (void) +{ + struct HMS_buffer *hms; + hms = HMS_connect("servers?token=%s", cv_masterserver_token.string); + HMS_do(hms); + CONS_Printf("%s", hms->buffer); + HMS_end(hms); +} + +void +HMS_fetch_servers (msg_server_t *list, int room_number) +{ + struct HMS_buffer *hms; + char *end; + char *section_end; + char *p; + + char *room; + char *address; + char *port; + char *title; + + int i; + + if (room_number > 0) + { + hms = HMS_connect("rooms/%d/servers?token=%s", + room_number, + cv_masterserver_token.string + ); + } + else + hms = HMS_connect("servers?token=%s", cv_masterserver_token.string); + + if (HMS_do(hms)) + { + p = hms->buffer; + i = 0; + do + { + section_end = strstr(p, "\n\n"); + room = strtok(p, "\n"); + p = strtok(0, ""); + for (; i < MAXSERVERLIST && ( end = strchr(p, '\n') ); ++i) + { + *end = '\0'; + + address = strtok(p, " "); + port = strtok(0, " "); + title = strtok(0, ""); + + if (address && port && title) + { + strlcpy(list[i].ip, address, sizeof list[i].ip); + strlcpy(list[i].port, port, sizeof list[i].port); + strlcpy(list[i].name, title, sizeof list[i].name); + list[i].room = atoi(room); + + list[i].header.buffer[0] = 1; + + if (end == section_end) + { + i++;/* lol */ + break; + } + + p = ( end + 1 ); + } + else + { + section_end = 0; + break; + } + } + p = ( section_end + 2 ); + } + while (section_end) ; + + list[i].header.buffer[0] = 0; + } + + HMS_end(hms); +} diff --git a/src/mserv.c b/src/mserv.c index c7344b16a..acc247e32 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -97,6 +97,15 @@ #include "i_addrinfo.h" +/* HTTP */ +int HMS_in_use (void); +void HMS_fetch_rooms (void); +int HMS_register (void); +void HMS_unlist (void); +void HMS_update (void); +void HMS_list_servers (void); +void HMS_fetch_servers (msg_server_t *list, int room); + // ================================ DEFINITIONS =============================== #define PACKET_SIZE 1024 @@ -251,6 +260,8 @@ void AddMServCommands(void) { #ifndef NONET CV_RegisterVar(&cv_masterserver); + CV_RegisterVar(&cv_http_masterserver); + CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); #endif @@ -438,6 +449,12 @@ const msg_server_t *GetShortServersList(INT32 room) msg_t msg; INT32 i; + if (HMS_in_use()) + { + HMS_fetch_servers(server_list, room); + return server_list; + } + // we must be connected to the master server before writing to it if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) { @@ -479,6 +496,12 @@ INT32 GetRoomsList(boolean hosting) msg_t msg; INT32 i; + if (HMS_in_use()) + { + HMS_fetch_rooms(); + return 1; + } + // we must be connected to the master server before writing to it if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) { @@ -544,6 +567,9 @@ const char *GetMODVersion(void) { static msg_t msg; + if (HMS_in_use()) + return NULL; + // we must be connected to the master server before writing to it if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) { @@ -587,6 +613,9 @@ void GetMODVersion_Console(void) { static msg_t msg; + if (HMS_in_use()) + return; + // we must be connected to the master server before writing to it if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) { @@ -632,6 +661,12 @@ static void Command_Listserv_f(void) CONS_Printf(M_GetText("Retrieving server list...\n")); + if (HMS_in_use()) + { + HMS_list_servers(); + return; + } + if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) { CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); @@ -686,6 +721,12 @@ static INT32 AddToMasterServer(boolean firstadd) UINT32 signature, tmp; const char *insname; + if (HMS_in_use()) + { + HMS_update(); + return MS_NO_ERROR; + } + M_Memcpy(&tset, &wset, sizeof (tset)); res = select(255, NULL, &tset, NULL, &select_timeout); if (res != ERRSOCKET && !res) @@ -869,6 +910,13 @@ void RegisterServer(void) CONS_Printf(M_GetText("Registering this server on the Master Server...\n")); + if (HMS_in_use()) + { + if (HMS_register()) + con_state = MSCS_REGISTERED; + return; + } + strcpy(registered_server.ip, GetMasterServerIP()); strcpy(registered_server.port, GetMasterServerPort()); @@ -884,6 +932,8 @@ void RegisterServer(void) static inline void SendPingToMasterServer(void) { + if (HMS_in_use()) + return; /* static tic_t next_time = 0; tic_t cur_time; char *inbuffer = (char*)netbuffer; @@ -969,6 +1019,12 @@ void UnregisterServer(void) CONS_Printf(M_GetText("Removing this server from the Master Server...\n")); + if (HMS_in_use()) + { + HMS_unlist(); + return; + } + if (MS_Connect(registered_server.ip, registered_server.port, 0)) { CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); diff --git a/src/mserv.h b/src/mserv.h index 09cd4f089..c744708ff 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -64,6 +64,8 @@ typedef struct // ================================ GLOBALS =============================== extern consvar_t cv_masterserver, cv_servername; +extern consvar_t cv_http_masterserver; +extern consvar_t cv_masterserver_token; // < 0 to not connect (usually -1) (offline mode) // == 0 to show all rooms, not a valid hosting room From fc817337ff37a59dc3eb4388b1f41cc6fc84f4a8 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 10:59:25 -0800 Subject: [PATCH 028/211] lol --- src/hms123311.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 4ce05bfa0..723c9e4a7 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -265,8 +265,8 @@ HMS_list_servers (void) { struct HMS_buffer *hms; hms = HMS_connect("servers?token=%s", cv_masterserver_token.string); - HMS_do(hms); - CONS_Printf("%s", hms->buffer); + if (HMS_do(hms)) + CONS_Printf("%s", hms->buffer); HMS_end(hms); } From 22a92a62475abf861099723965046c8ef5c0d7ba Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 11:17:56 -0800 Subject: [PATCH 029/211] I don't even know my own website --- src/hms123311.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hms123311.c b/src/hms123311.c index 723c9e4a7..10fdf6b96 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -18,7 +18,7 @@ consvar_t cv_http_masterserver = { "http_masterserver", - "http://www.jameds.org/MS/0", + "http://www.jameds.org/SRB2/MS/0", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ From 5a7ee3f3d85cd274501ae0ad9e7873476ffdd2ca Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 11:19:59 -0800 Subject: [PATCH 030/211] Don't show "All" room when hosting --- src/hms123311.c | 17 +++++++++++------ src/mserv.c | 4 ++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 10fdf6b96..d9d4fa2f8 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -143,7 +143,7 @@ HMS_in_use (void) } void -HMS_fetch_rooms (void) +HMS_fetch_rooms (int joining) { struct HMS_buffer *hms; char *p; @@ -179,12 +179,17 @@ HMS_fetch_rooms (void) break; } - room_list[i].header.buffer[0] = 1; - room_list[i].id = 0; - strcpy(room_list[i].name, "All"); - strcpy(room_list[i].motd, "Wildcard."); + if (joining) + { + room_list[i].header.buffer[0] = 1; + room_list[i].id = 0; + strcpy(room_list[i].name, "All"); + strcpy(room_list[i].motd, "Wildcard."); - room_list[i + 1].header.buffer[0] = 0; + i++; + } + + room_list[i].header.buffer[0] = 0; } HMS_end(hms); } diff --git a/src/mserv.c b/src/mserv.c index acc247e32..5a3ef1e28 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -99,7 +99,7 @@ /* HTTP */ int HMS_in_use (void); -void HMS_fetch_rooms (void); +void HMS_fetch_rooms (int joining); int HMS_register (void); void HMS_unlist (void); void HMS_update (void); @@ -498,7 +498,7 @@ INT32 GetRoomsList(boolean hosting) if (HMS_in_use()) { - HMS_fetch_rooms(); + HMS_fetch_rooms( ! hosting ); return 1; } From 73a6c583257d19ef4373daa2a6302435e4378200 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 11:39:27 -0800 Subject: [PATCH 031/211] masterserver_debug to set CURLOPT_VERBOSE --- src/hms123311.c | 7 +++++++ src/mserv.c | 1 + src/mserv.h | 1 + 3 files changed, 9 insertions(+) diff --git a/src/hms123311.c b/src/hms123311.c index d9d4fa2f8..f0eaca270 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -32,6 +32,11 @@ consvar_t cv_masterserver_token = { NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ }; +consvar_t cv_masterserver_debug = { + "masterserver_debug", "Off", CV_SAVE, CV_OnOff, + NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + static int hms_started; static char hms_server_token[sizeof "xxx.xxx.xxx.xxx/xxxxx"]; @@ -97,6 +102,8 @@ HMS_connect (const char *format, ...) buffer->buffer = malloc(buffer->end); buffer->needle = 0; + if (cv_masterserver_debug.value) + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); diff --git a/src/mserv.c b/src/mserv.c index 5a3ef1e28..137b03f56 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -262,6 +262,7 @@ void AddMServCommands(void) CV_RegisterVar(&cv_masterserver); CV_RegisterVar(&cv_http_masterserver); CV_RegisterVar(&cv_masterserver_token); + CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); #endif diff --git a/src/mserv.h b/src/mserv.h index c744708ff..88a2aabb9 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -66,6 +66,7 @@ typedef struct extern consvar_t cv_masterserver, cv_servername; extern consvar_t cv_http_masterserver; extern consvar_t cv_masterserver_token; +extern consvar_t cv_masterserver_debug; // < 0 to not connect (usually -1) (offline mode) // == 0 to show all rooms, not a valid hosting room From b34cbd2bdd6dd105ca3bbb37b85627bb8f97337b Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 11:46:02 -0800 Subject: [PATCH 032/211] Windows is weird --- src/hms123311.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hms123311.c b/src/hms123311.c index f0eaca270..d3e7adc2a 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -103,7 +103,10 @@ HMS_connect (const char *format, ...) buffer->needle = 0; if (cv_masterserver_debug.value) + { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_STDERR, logstream); + } curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); From 2de41bf000c28c518de6098805283bad657d3af3 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 11:54:21 -0800 Subject: [PATCH 033/211] Windows gaysed: the sequel --- src/hms123311.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hms123311.c b/src/hms123311.c index d3e7adc2a..fd043bb8d 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -110,6 +110,7 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);/* WTF WINDOWS!! */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); From c7a8608a9b02eb881731a898194934b461c70389 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 11:59:54 -0800 Subject: [PATCH 034/211] This time I fucked up --- src/hms123311.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hms123311.c b/src/hms123311.c index fd043bb8d..7feea8596 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -110,7 +110,7 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);/* WTF WINDOWS!! */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);/* WTF WINDOWS!! */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); From 4b0774ddf96d6224b4d300b1e65e6025dc337ba1 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 12:15:48 -0800 Subject: [PATCH 035/211] strtok can return NULL --- src/hms123311.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hms123311.c b/src/hms123311.c index 7feea8596..b05c148a5 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -320,6 +320,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number) section_end = strstr(p, "\n\n"); room = strtok(p, "\n"); p = strtok(0, ""); + if (! p) + break; for (; i < MAXSERVERLIST && ( end = strchr(p, '\n') ); ++i) { *end = '\0'; From cfdf650c3d7a07e83248eabeba5a18e8bed60fdc Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 1 Mar 2020 12:31:49 -0800 Subject: [PATCH 036/211] Use https because my web server redirects --- src/hms123311.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hms123311.c b/src/hms123311.c index b05c148a5..4b1992442 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -18,7 +18,7 @@ consvar_t cv_http_masterserver = { "http_masterserver", - "http://www.jameds.org/SRB2/MS/0", + "https://www.jameds.org/SRB2/MS/0", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ From 944034f42a58f56c8158833ecaa8ae1a3e00d1b5 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 20 Mar 2020 20:25:34 -0700 Subject: [PATCH 037/211] This will be the Master Server in 2020 --- src/hms123311.c | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 4b1992442..ceeb8394e 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -18,7 +18,7 @@ consvar_t cv_http_masterserver = { "http_masterserver", - "https://www.jameds.org/SRB2/MS/0", + "https://mb.srb2.org/MS/0", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ @@ -166,7 +166,7 @@ HMS_fetch_rooms (int joining) int i; - hms = HMS_connect("rooms?token=%s&verbose", cv_masterserver_token.string); + hms = HMS_connect("rooms"); if (HMS_do(hms)) { p = hms->buffer; @@ -190,16 +190,6 @@ HMS_fetch_rooms (int joining) break; } - if (joining) - { - room_list[i].header.buffer[0] = 1; - room_list[i].id = 0; - strcpy(room_list[i].name, "All"); - strcpy(room_list[i].motd, "Wildcard."); - - i++; - } - room_list[i].header.buffer[0] = 0; } HMS_end(hms); @@ -213,15 +203,15 @@ HMS_register (void) char *title; int ok; - hms = HMS_connect("rooms/%d/register?token=%s", - ms_RoomId, - cv_masterserver_token.string - ); + hms = HMS_connect("rooms/%d/register", ms_RoomId); title = curl_easy_escape(hms->curl, cv_servername.string, 0); sprintf(post, - "port=%d&title=%s", + "port=%d&title=%s&version=%d.%d.%d", current_port, - title + title, + VERSION/100, + VERSION%100, + SUBVERSION ); curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); @@ -243,10 +233,7 @@ void HMS_unlist (void) { struct HMS_buffer *hms; - hms = HMS_connect("servers/%s/unlist?token=%s", - hms_server_token, - cv_masterserver_token.string - ); + hms = HMS_connect("servers/%s/unlist", hms_server_token); curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); /*curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, cv_masterserver_token.string);*/ @@ -261,10 +248,7 @@ HMS_update (void) struct HMS_buffer *hms; char *title; - hms = HMS_connect("servers/%s/update?token=%s", - hms_server_token, - cv_masterserver_token.string - ); + hms = HMS_connect("servers/%s/update", hms_server_token); title = curl_easy_escape(hms->curl, cv_servername.string, 0); sprintf(post, "title=%s", title); @@ -280,7 +264,7 @@ void HMS_list_servers (void) { struct HMS_buffer *hms; - hms = HMS_connect("servers?token=%s", cv_masterserver_token.string); + hms = HMS_connect("servers"); if (HMS_do(hms)) CONS_Printf("%s", hms->buffer); HMS_end(hms); @@ -303,13 +287,10 @@ HMS_fetch_servers (msg_server_t *list, int room_number) if (room_number > 0) { - hms = HMS_connect("rooms/%d/servers?token=%s", - room_number, - cv_masterserver_token.string - ); + hms = HMS_connect("rooms/%d/servers", room_number); } else - hms = HMS_connect("servers?token=%s", cv_masterserver_token.string); + hms = HMS_connect("servers"); if (HMS_do(hms)) { From 4c8d3739624ad5a5226fb69fa69e6a08b4a6b370 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 29 Mar 2020 16:40:21 +0200 Subject: [PATCH 038/211] Fix followers in anti gravity --- src/dehacked.c | 7 +++++++ src/k_kart.c | 15 +++++++++++++++ src/k_kart.h | 1 + src/p_user.c | 19 +++++++++---------- src/r_things.h | 1 + 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 3f760da15..e8a33b0bd 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -721,6 +721,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].scale = FRACUNIT; followers[numfollowers].atangle = 230; followers[numfollowers].dist = 16; + followers[numfollowers].height = 16; followers[numfollowers].zoffs = 32; followers[numfollowers].horzlag = 2; followers[numfollowers].vertlag = 6; @@ -801,6 +802,11 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].dist), UNDO_NONE); followers[numfollowers].dist = (INT32)atoi(word2); } + else if (fastcmp(word, "HEIGHT")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].height), UNDO_NONE); + followers[numfollowers].height = (INT32)atoi(word2); + } else if (fastcmp(word, "IDLESTATE")) { if (word2) @@ -897,6 +903,7 @@ if (followers[numfollowers].field < threshold) \ } \ FALLBACK(dist, "DIST", 0, 0); + FALLBACK(height, "HEIGHT", 1, 1); FALLBACK(zoffs, "ZOFFS", 0, 0); FALLBACK(horzlag, "HORZLAG", 1, 1); FALLBACK(vertlag, "VERTLAG", 1, 1); diff --git a/src/k_kart.c b/src/k_kart.c index 7784f6a46..6b679cb2b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1930,6 +1930,21 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); } +// same as above, but does not adjust Z height when flipping +void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) +{ + // flipping + mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); + mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); + + // visibility (usually for hyudoro) + mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); +} + static void K_SpawnDashDustRelease(player_t *player) { fixed_t newx; diff --git a/src/k_kart.h b/src/k_kart.h index 273d8bcb1..331f53bcd 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -31,6 +31,7 @@ void 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); +void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master); void K_DoIngameRespawn(player_t *player); void K_RespawnChecker(player_t *player); void K_KartMoveAnimation(player_t *player); diff --git a/src/p_user.c b/src/p_user.c index 54b6a5393..d7b8a1951 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8072,17 +8072,15 @@ static void P_HandleFollower(player_t *player) sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT)); // for the z coordinate, don't be a doof like Steel and forget that MFE_VERTICALFLIP exists :P - sz = player->mo->z + FixedMul(player->mo->scale, zoffs); - /*if (player->mo->eflags & MFE_VERTICALFLIP) // it's safe to assume that VERTICALFLIP accounts for MF2_OBJECTFLIP too - sz -= (player->mo->height + FixedMul(player->mo->scale, zoffs*2));*/ - // ^ handled by K_matchgenericextraflags oops - + sz = player->mo->z + FixedMul(player->mo->scale, zoffs)*P_MobjFlip(player->mo); + if (player->mo->eflags & MFE_VERTICALFLIP) + sz += fl.height*player->mo->scale; // finally, add a cool floating effect to the z height. // not stolen from k_kart I swear!! const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); - sz += sine; + sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); if (!player->follower) // follower doesn't exist / isn't valid { @@ -8111,13 +8109,13 @@ static void P_HandleFollower(player_t *player) // move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)! player->follower->momx = (sx - player->follower->x)/fl.horzlag; player->follower->momy = (sy - player->follower->y)/fl.horzlag; - player->follower->momz = (sz - player->follower->z)/fl.vertlag; // make z momentum a bit floatier, it'll look cute I promise! + player->follower->momz = (sz - player->follower->z)/fl.vertlag; player->follower->angle = player->mo->angle; player->follower->color = player->mo->color; player->follower->colorized = player->mo->colorized; P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); - K_MatchGenericExtraFlags(player->follower, player->mo); + K_GenericExtraFlagsNoZAdjust(player->follower, player->mo); // Not K_MatchGenericExtraFlag because the Z adjust it has only works properly if master & mo have the same Z height. // For comeback in battle. player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); @@ -8127,11 +8125,12 @@ static void P_HandleFollower(player_t *player) if (player->pflags & PF_TIMEOVER || (!cv_showfollowers.value && (!P_IsDisplayPlayer(player) || displayplayers[0] != consoleplayer) )) player->follower->flags2 |= MF2_DONTDRAW; - if (player->speed) + if (player->speed && (player->follower->momx || player->follower->momy)) player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy); // 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 - // handle follower animations. Yes, it looks like very bad kiddie script so what, do you have any better idea genius? Go get a life instead of criticizing my unpaid work!!!!!! + // handle follower animations. Could probably be better... // hurt or dead if (player->kartstuff[k_spinouttimer] || player->mo->state == &states[S_KART_SPIN] || player->mo->health <= 0) { diff --git a/src/r_things.h b/src/r_things.h index 89e6fc6cf..5ee476883 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -115,6 +115,7 @@ typedef struct follower_s // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. INT32 dist; // distance relative to the player. (In a circle) + INT32 height; // height of the follower, this is mostly important for Z flipping. INT32 zoffs; // Z offset relative to the player's height. Cannot be negative. // movement options From b9f5eaa739686ef8dc854695ebfde4a7ac039c5c Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Apr 2020 17:15:39 -0700 Subject: [PATCH 039/211] Kill masterserver_token --- src/hms123311.c | 10 ---------- src/mserv.c | 1 - src/mserv.h | 1 - 3 files changed, 12 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index ceeb8394e..1de0b19c1 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -24,14 +24,6 @@ consvar_t cv_http_masterserver = { NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ }; -consvar_t cv_masterserver_token = { - "masterserver_token", - "", - CV_SAVE, - - NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ -}; - consvar_t cv_masterserver_debug = { "masterserver_debug", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ @@ -235,8 +227,6 @@ HMS_unlist (void) struct HMS_buffer *hms; hms = HMS_connect("servers/%s/unlist", hms_server_token); curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); - /*curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, - cv_masterserver_token.string);*/ HMS_do(hms); HMS_end(hms); } diff --git a/src/mserv.c b/src/mserv.c index 137b03f56..354d84d9a 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -261,7 +261,6 @@ void AddMServCommands(void) #ifndef NONET CV_RegisterVar(&cv_masterserver); CV_RegisterVar(&cv_http_masterserver); - CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); diff --git a/src/mserv.h b/src/mserv.h index 88a2aabb9..9f6f9ad30 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -65,7 +65,6 @@ typedef struct extern consvar_t cv_masterserver, cv_servername; extern consvar_t cv_http_masterserver; -extern consvar_t cv_masterserver_token; extern consvar_t cv_masterserver_debug; // < 0 to not connect (usually -1) (offline mode) From c4995a5ebaad84e3ca23e87a15878d2b9cf39c6a Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Apr 2020 17:51:05 -0700 Subject: [PATCH 040/211] Handle more errors --- src/hms123311.c | 78 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 1de0b19c1..b89cd080d 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -15,6 +15,11 @@ #include "command.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ +#include "z_zone.h" + +/* I just stop myself from making macros anymore. */ +#define Blame( ... ) \ + CONS_Printf("\x85" __VA_ARGS__) consvar_t cv_http_masterserver = { "http_masterserver", @@ -41,6 +46,14 @@ struct HMS_buffer int end; }; +static void +Contact_error (void); +{ + CONS_Alert(CONS_ERROR, + "There was a problem contacting the master server...\n" + ); +} + static size_t HMS_on_read (char *s, size_t _1, size_t n, void *userdata) { @@ -67,16 +80,32 @@ HMS_connect (const char *format, ...) if (! hms_started) { - curl_global_init(CURL_GLOBAL_ALL); - hms_started = 1; + if (curl_global_init(CURL_GLOBAL_ALL) != 0) + { + Contact_error(); + Blame("From curl_global_init.\n"); + return NULL; + } + else + { + atexit(curl_global_cleanup); + hms_started = 1; + } } curl = curl_easy_init(); + if (! curl) + { + Contact_error(); + Blame("From curl_easy_init.\n"); + return NULL; + } + seek = strlen(cv_http_masterserver.string) + 1;/* + '/' */ va_start (ap, format); - url = malloc(seek + vsnprintf(0, 0, format, ap) + 1); + url = ZZ_Alloc(seek + vsnprintf(0, 0, format, ap) + 1); va_end (ap); sprintf(url, "%s/", cv_http_masterserver.string); @@ -87,11 +116,11 @@ HMS_connect (const char *format, ...) CONS_Printf("HMS: connecting '%s'...\n", url); - buffer = malloc(sizeof *buffer); + buffer = ZZ_Alloc(sizeof *buffer); buffer->curl = curl; /* I just allocated 4k and fuck it! */ buffer->end = 4096; - buffer->buffer = malloc(buffer->end); + buffer->buffer = ZZ_Alloc(buffer->end); buffer->needle = 0; if (cv_masterserver_debug.value) @@ -114,17 +143,42 @@ HMS_connect (const char *format, ...) static int HMS_do (struct HMS_buffer *buffer) { + CURLcode cc; long status; - curl_easy_perform(buffer->curl); - curl_easy_getinfo(buffer->curl, CURLINFO_RESPONSE_CODE, &status); + + char *p; + + cc = curl_easy_perform(buffer->curl); + + if (cc != CURLE_OK) + { + Contact_error(); + Blame( + "From curl_easy_perform: %s\n", + curl_easy_strerror(cc) + ); + return 0; + } + buffer->buffer[buffer->needle] = '\0'; + + curl_easy_getinfo(buffer->curl, CURLINFO_RESPONSE_CODE, &status); + if (status != 200) { - CONS_Alert(CONS_ERROR, - "Master server error %ld: %s", + p = strchr(buffer->buffer, '\n'); + + if (p) + *p = '\0'; + + Contact_error(); + Blame( + "Master server error %ld: %s%s\n", status, - buffer->buffer + buffer->buffer, + ( (p) ? "" : " (malformed)" ) ); + return 0; } else @@ -134,9 +188,9 @@ HMS_do (struct HMS_buffer *buffer) static void HMS_end (struct HMS_buffer *buffer) { - free(buffer->buffer); curl_easy_cleanup(buffer->curl); - free(buffer); + Z_Free(buffer->buffer); + Z_Free(buffer); } int From 8a923a5b5705b18dbc9e2429c6bee80d216efd09 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Apr 2020 17:51:30 -0700 Subject: [PATCH 041/211] Windows certs do work, thanks Steel! --- src/hms123311.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hms123311.c b/src/hms123311.c index b89cd080d..4a694eeaa 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -131,7 +131,6 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);/* WTF WINDOWS!! */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); From 26c0049694d132304f2b10968daf42114f1f1087 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Apr 2020 18:25:59 -0700 Subject: [PATCH 042/211] Clean up a bunch of stuff and follow the Master Server API more closely --- src/hms123311.c | 135 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 33 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 4a694eeaa..9371a7c63 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -8,6 +8,12 @@ //----------------------------------------------------------------------------- // \brief HTTP based master server +/* +Documentation available here. + + +*/ + #include #include "doomdef.h" @@ -36,7 +42,7 @@ consvar_t cv_masterserver_debug = { static int hms_started; -static char hms_server_token[sizeof "xxx.xxx.xxx.xxx/xxxxx"]; +static char *hms_server_token; struct HMS_buffer { @@ -128,9 +134,10 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_STDERR, logstream); } - curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); @@ -202,25 +209,30 @@ void HMS_fetch_rooms (int joining) { struct HMS_buffer *hms; - char *p; - char *end; char *id; char *title; char *motd; + char *p; + char *end; + int i; hms = HMS_connect("rooms"); + if (HMS_do(hms)) { p = hms->buffer; + for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") ); ++i) { *end = '\0'; + id = strtok(p, "\n"); title = strtok(0, "\n"); motd = strtok(0, ""); + if (id && title && motd) { room_list[i].header.buffer[0] = 1; @@ -229,7 +241,7 @@ HMS_fetch_rooms (int joining) strlcpy(room_list[i].name, title, sizeof room_list[i].name); strlcpy(room_list[i].motd, motd, sizeof room_list[i].motd); - p = ( end + 3 ); + p = ( end + 3 );/* skip the three linefeeds */ } else break; @@ -237,38 +249,49 @@ HMS_fetch_rooms (int joining) room_list[i].header.buffer[0] = 0; } + HMS_end(hms); } int HMS_register (void) { - char post[256]; struct HMS_buffer *hms; - char *title; int ok; + char post[256]; + + char *title; + hms = HMS_connect("rooms/%d/register", ms_RoomId); + title = curl_easy_escape(hms->curl, cv_servername.string, 0); - sprintf(post, - "port=%d&title=%s&version=%d.%d.%d", + + snprintf(post, sizeof post, + "port=%d&" + "title=%s&" + "version=%d.%d.%d", + current_port, + title, + VERSION/100, VERSION%100, SUBVERSION ); + + curl_free(title); + curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); ok = HMS_do(hms); if (ok) { - strlcpy(hms_server_token, strtok(hms->buffer, "\n"), - sizeof hms_server_token); + hms_server_token = Z_StrDup(strtok(hms->buffer, "\n")); } - curl_free(title); HMS_end(hms); return ok; @@ -278,23 +301,35 @@ void HMS_unlist (void) { struct HMS_buffer *hms; + hms = HMS_connect("servers/%s/unlist", hms_server_token); + curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); + HMS_do(hms); HMS_end(hms); + + Z_Free(hms_server_token); } void HMS_update (void) { - char post[256]; struct HMS_buffer *hms; + + char post[256]; + char *title; hms = HMS_connect("servers/%s/update", hms_server_token); title = curl_easy_escape(hms->curl, cv_servername.string, 0); - sprintf(post, "title=%s", title); + + snprintf(post, sizeof post, + "title=%s", + title + ); + curl_free(title); curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); @@ -307,9 +342,20 @@ void HMS_list_servers (void) { struct HMS_buffer *hms; + + char *p; + hms = HMS_connect("servers"); + if (HMS_do(hms)) - CONS_Printf("%s", hms->buffer); + { + p = &hms->buffer[strlen(hms->buffer)]; + while (*--p == '\n') + ; + + CONS_Printf("%s\n", hms->buffer); + } + HMS_end(hms); } @@ -317,14 +363,19 @@ void HMS_fetch_servers (msg_server_t *list, int room_number) { struct HMS_buffer *hms; - char *end; - char *section_end; - char *p; + + char local_version[9]; char *room; + char *address; char *port; char *title; + char *version; + + char *end; + char *section_end; + char *p; int i; @@ -337,46 +388,64 @@ HMS_fetch_servers (msg_server_t *list, int room_number) if (HMS_do(hms)) { + snprintf(local_version, sizeof local_version, + "%d.%d.%d\n", + VERSION/100, + VERSION%100, + SUBVERSION + ); + p = hms->buffer; i = 0; + do { section_end = strstr(p, "\n\n"); + room = strtok(p, "\n"); + p = strtok(0, ""); + if (! p) break; - for (; i < MAXSERVERLIST && ( end = strchr(p, '\n') ); ++i) + + while (i < MAXSERVERLIST && ( end = strchr(p, '\n') )) { *end = '\0'; address = strtok(p, " "); port = strtok(0, " "); - title = strtok(0, ""); + title = strtok(0, " "); + version = strtok(0, ""); - if (address && port && title) + if (address && port && title && version) { - strlcpy(list[i].ip, address, sizeof list[i].ip); - strlcpy(list[i].port, port, sizeof list[i].port); - strlcpy(list[i].name, title, sizeof list[i].name); - list[i].room = atoi(room); - - list[i].header.buffer[0] = 1; - - if (end == section_end) + if (strcmp(version, local_version) == 0) { - i++;/* lol */ - break; + strlcpy(list[i].ip, address, sizeof list[i].ip); + strlcpy(list[i].port, port, sizeof list[i].port); + strlcpy(list[i].name, title, sizeof list[i].name); + strlcpy(list[i].version, version, sizeof list[i].version); + + list[i].room = atoi(room); + + list[i].header.buffer[0] = 1; + + i++; } - p = ( end + 1 ); + if (end == section_end)/* end of list for this room */ + break; + else + p = ( end + 1 );/* skip server delimiter */ } else { - section_end = 0; + section_end = 0;/* malformed so quit the parsing */ break; } } + p = ( section_end + 2 ); } while (section_end) ; From 7b2463a04078441dffb7c3233a46a34489240369 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Apr 2020 18:27:01 -0700 Subject: [PATCH 043/211] Bruh --- src/hms123311.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hms123311.c b/src/hms123311.c index 9371a7c63..d3be8f4dd 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -53,7 +53,7 @@ struct HMS_buffer }; static void -Contact_error (void); +Contact_error (void) { CONS_Alert(CONS_ERROR, "There was a problem contacting the master server...\n" From d8334f9a8f066ce7e7cef7031902bfcee558e53e Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Apr 2020 18:33:53 -0700 Subject: [PATCH 044/211] Fix some goofs --- src/hms123311.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index d3be8f4dd..13c681735 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -141,7 +141,7 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); - free(url); + Z_Free(url); return buffer; } @@ -389,7 +389,7 @@ HMS_fetch_servers (msg_server_t *list, int room_number) if (HMS_do(hms)) { snprintf(local_version, sizeof local_version, - "%d.%d.%d\n", + "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION From 219d8c938024e640a31f575cc0297a3772779682 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Apr 2020 20:37:46 -0700 Subject: [PATCH 045/211] HTTP update alert --- src/hms123311.c | 36 ++++++++++++++++++++++++++++++++++++ src/mserv.c | 5 ++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/hms123311.c b/src/hms123311.c index 13c681735..4e4182b76 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -455,3 +455,39 @@ HMS_fetch_servers (msg_server_t *list, int room_number) HMS_end(hms); } + +const char * +HMS_compare_mod_version (void) +{ + static char buffer[16]; + + struct HMS_buffer *hms; + + char *version; + char *version_name; + + hms = HMS_connect("versions/%d", MODID); + + if (HMS_do(hms)) + { + version = strtok(hms->buffer, " "); + version_name = strtok(0, "\n"); + + if (version && version_name) + { + if (atoi(version) != MODVERSION) + { + strlcpy(buffer, version_name, sizeof buffer); + version_name = buffer; + } + else + version_name = NULL; + } + } + else + version_name = NULL; + + HMS_end(hms); + + return version_name; +} diff --git a/src/mserv.c b/src/mserv.c index 354d84d9a..b4bea7e86 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -105,6 +105,7 @@ void HMS_unlist (void); void HMS_update (void); void HMS_list_servers (void); void HMS_fetch_servers (msg_server_t *list, int room); +const char * HMS_compare_mod_version (void); // ================================ DEFINITIONS =============================== @@ -568,7 +569,9 @@ const char *GetMODVersion(void) static msg_t msg; if (HMS_in_use()) - return NULL; + { + return HMS_compare_mod_version(); + } // we must be connected to the master server before writing to it if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) From 83464dbf1d8ef365b583f7530333b8fd914b4997 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Apr 2020 21:20:29 -0700 Subject: [PATCH 046/211] Clean up header text --- src/hms123311.c | 2 +- src/mserv.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 4e4182b76..13466dc85 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -1,4 +1,4 @@ -// SONIC ROBO BLAST 2 JART +// SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- // Copyright (C) 2020 by James R. // diff --git a/src/mserv.c b/src/mserv.c index b4bea7e86..cfa526e1e 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -8,7 +8,7 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file mserv.c -/// \brief Commands used for communicate with the master server +/// \brief Commands used to communicate with the master server #ifdef __GNUC__ #include From d15024482dc30820f4ce956da712e3e6d5df4a07 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Apr 2020 22:23:01 -0700 Subject: [PATCH 047/211] Kill the old mserv, long live HMS! --- src/d_clisrv.c | 7 - src/hms123311.c | 57 ++- src/m_menu.c | 1 - src/mserv.c | 973 +++--------------------------------------------- src/mserv.h | 16 +- 5 files changed, 86 insertions(+), 968 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2ab9a4f34..398083a79 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1764,10 +1764,6 @@ static void SendAskInfo(INT32 node, boolean viams) // 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)); - - // Also speak to the MS. - if (viams && node != 0 && node != BROADCASTADDR) - SendAskInfoViaMS(node, asktime); } serverelem_t serverlist[MAXSERVERLIST]; @@ -1830,7 +1826,6 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) if (!netgame && I_NetOpenSocket) { - MSCloseUDPSocket(); // Tidy up before wiping the slate. if (I_NetOpenSocket()) { netgame = true; @@ -2567,7 +2562,6 @@ static void Command_connect(void) } else if (I_NetOpenSocket) { - MSCloseUDPSocket(); // Tidy up before wiping the slate. I_NetOpenSocket(); netgame = true; multiplayer = true; @@ -3637,7 +3631,6 @@ boolean SV_SpawnServer(void) SV_GenContext(); if (netgame && I_NetOpenSocket) { - MSCloseUDPSocket(); // Tidy up before wiping the slate. I_NetOpenSocket(); if (ms_RoomId > 0) RegisterServer(); diff --git a/src/hms123311.c b/src/hms123311.c index 13466dc85..be0df42a3 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -27,14 +27,6 @@ Documentation available here. #define Blame( ... ) \ CONS_Printf("\x85" __VA_ARGS__) -consvar_t cv_http_masterserver = { - "http_masterserver", - "https://mb.srb2.org/MS/0", - CV_SAVE, - - NULL, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ -}; - consvar_t cv_masterserver_debug = { "masterserver_debug", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ @@ -108,13 +100,13 @@ HMS_connect (const char *format, ...) return NULL; } - seek = strlen(cv_http_masterserver.string) + 1;/* + '/' */ + seek = strlen(ms_API) + 1;/* + '/' */ va_start (ap, format); url = ZZ_Alloc(seek + vsnprintf(0, 0, format, ap) + 1); va_end (ap); - sprintf(url, "%s/", cv_http_masterserver.string); + sprintf(url, "%s/", ms_API); va_start (ap, format); vsprintf(&url[seek], format, ap); @@ -200,15 +192,10 @@ HMS_end (struct HMS_buffer *buffer) } int -HMS_in_use (void) -{ - return cv_http_masterserver.string[0]; -} - -void HMS_fetch_rooms (int joining) { struct HMS_buffer *hms; + int ok; char *id; char *title; @@ -248,9 +235,15 @@ HMS_fetch_rooms (int joining) } room_list[i].header.buffer[0] = 0; + + ok = 1; } + else + ok = 0; HMS_end(hms); + + return ok; } int @@ -312,10 +305,11 @@ HMS_unlist (void) Z_Free(hms_server_token); } -void +int HMS_update (void) { struct HMS_buffer *hms; + int ok; char post[256]; @@ -334,8 +328,10 @@ HMS_update (void) curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); - HMS_do(hms); + ok = HMS_do(hms); HMS_end(hms); + + return ok; } void @@ -359,7 +355,7 @@ HMS_list_servers (void) HMS_end(hms); } -void +msg_server_t * HMS_fetch_servers (msg_server_t *list, int room_number) { struct HMS_buffer *hms; @@ -452,22 +448,27 @@ HMS_fetch_servers (msg_server_t *list, int room_number) list[i].header.buffer[0] = 0; } + else + list = NULL; HMS_end(hms); + + return list; } -const char * -HMS_compare_mod_version (void) +int +HMS_compare_mod_version (char *buffer, size_t buffer_size) { - static char buffer[16]; - struct HMS_buffer *hms; + int ok; char *version; char *version_name; hms = HMS_connect("versions/%d", MODID); + ok = 0; + if (HMS_do(hms)) { version = strtok(hms->buffer, " "); @@ -477,17 +478,15 @@ HMS_compare_mod_version (void) { if (atoi(version) != MODVERSION) { - strlcpy(buffer, version_name, sizeof buffer); - version_name = buffer; + strlcpy(buffer, version_name, buffer_size); + ok = 1; } else - version_name = NULL; + ok = -1; } } - else - version_name = NULL; HMS_end(hms); - return version_name; + return ok; } diff --git a/src/m_menu.c b/src/m_menu.c index 97b1ce9b5..8025f91d4 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2892,7 +2892,6 @@ boolean M_Responder(event_t *ev) //make sure the game doesn't still think we're in a netgame. if (!Playing() && netgame && multiplayer) { - MSCloseUDPSocket(); // Clean up so we can re-open the connection later. netgame = false; multiplayer = false; } diff --git a/src/mserv.c b/src/mserv.c index cfa526e1e..8cf656820 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -2,6 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2018 by Sonic Team Junior. +// Copyright (C) 2020 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -10,244 +11,45 @@ /// \file mserv.c /// \brief Commands used to communicate with the master server -#ifdef __GNUC__ -#include -#include -#include -#endif - #if !defined (UNDER_CE) #include #endif -#if (defined (NOMD5) || defined (NOMSERV)) && !defined (NONET) -#define NONET -#endif - -#ifndef NONET - -#ifndef NO_IPV6 -#define HAVE_IPV6 -#endif - -#if (defined (_WIN32) || defined (_WIN32_WCE)) && !defined (_XBOX) -#define RPC_NO_WINDOWS_H -#ifdef HAVE_IPV6 -#include -#else -#include // socket(),... -#endif //!HAVE_IPV6 -#else -#ifdef __OS2__ -#include -#endif // __OS2__ - -#ifdef HAVE_LWIP -#include -#include -#include -#define ioctl lwip_ioctl -#else -#include -#ifdef __APPLE_CC__ -#ifndef _BSD_SOCKLEN_T_ -#define _BSD_SOCKLEN_T_ -#endif -#endif -#include // socket(),... -#include // sockaddr_in -#ifdef _PS3 -#include -#elif !defined(_arch_dreamcast) -#include // getaddrinfo(),... -#include -#endif -#endif - -#ifdef _arch_dreamcast -#include "sdl12/SRB2DC/dchelp.h" -#endif - -#include // timeval,... (TIMEOUT) -#include -#endif // _WIN32/_WIN32_WCE - -#ifdef __OS2__ -#include -#endif // __OS2__ -#endif // !NONET - #include "doomstat.h" #include "doomdef.h" #include "command.h" -#include "i_net.h" -#include "console.h" #include "mserv.h" -#include "d_net.h" -#include "i_tcp.h" -#include "i_system.h" -#include "byteptr.h" #include "m_menu.h" -#include "m_argv.h" // Alam is going to kill me <3 -#include "m_misc.h" // GetRevisionString() - -#ifdef _WIN32_WCE -#include "sdl12/SRB2CE/cehelp.h" -#endif - -#include "i_addrinfo.h" +#include "z_zone.h" /* HTTP */ int HMS_in_use (void); -void HMS_fetch_rooms (int joining); +int HMS_fetch_rooms (int joining); int HMS_register (void); void HMS_unlist (void); -void HMS_update (void); +int HMS_update (void); void HMS_list_servers (void); -void HMS_fetch_servers (msg_server_t *list, int room); -const char * HMS_compare_mod_version (void); +int HMS_fetch_servers (msg_server_t *list, int room); +int HMS_compare_mod_version (char *buffer, size_t size_of_buffer); -// ================================ DEFINITIONS =============================== - -#define PACKET_SIZE 1024 - - -#define MS_NO_ERROR 0 -#define MS_SOCKET_ERROR -201 -#define MS_CONNECT_ERROR -203 -#define MS_WRITE_ERROR -210 -#define MS_READ_ERROR -211 -#define MS_CLOSE_ERROR -212 -#define MS_GETHOSTBYNAME_ERROR -220 -#define MS_GETHOSTNAME_ERROR -221 -#define MS_TIMEOUT_ERROR -231 - -// see master server code for the values -#define ADD_SERVER_MSG 101 -#define REMOVE_SERVER_MSG 103 -#define ADD_SERVERv2_MSG 104 -#define GET_SERVER_MSG 200 -#define GET_SHORT_SERVER_MSG 205 -#define ASK_SERVER_MSG 206 -#define ANSWER_ASK_SERVER_MSG 207 -#define ASK_SERVER_MSG 206 -#define ANSWER_ASK_SERVER_MSG 207 -#define GET_MOTD_MSG 208 -#define SEND_MOTD_MSG 209 -#define GET_ROOMS_MSG 210 -#define SEND_ROOMS_MSG 211 -#define GET_ROOMS_HOST_MSG 212 -#define GET_VERSION_MSG 213 -#define SEND_VERSION_MSG 214 -#define GET_BANNED_MSG 215 // Someone's been baaaaaad! -#define PING_SERVER_MSG 216 - -#define HEADER_SIZE (sizeof (INT32)*4) - -#define HEADER_MSG_POS 0 -#define IP_MSG_POS 16 -#define PORT_MSG_POS 32 -#define HOSTNAME_MSG_POS 40 - - -#if defined(_MSC_VER) -#pragma pack(1) -#endif - -/** A message to be exchanged with the master server. - */ -typedef struct -{ - INT32 id; ///< Unused? - INT32 type; ///< Type of message. - INT32 room; ///< Because everyone needs a roomie. - UINT32 length; ///< Length of the message. - char buffer[PACKET_SIZE]; ///< Actual contents of the message. -} ATTRPACK msg_t; - -#if defined(_MSC_VER) -#pragma pack() -#endif - -typedef struct Copy_CVarMS_t -{ - char ip[64]; - char port[8]; - char name[64]; -} Copy_CVarMS_s; -static Copy_CVarMS_s registered_server; static time_t MSLastPing; -#if defined(_MSC_VER) -#pragma pack(1) -#endif -typedef struct -{ - char ip[16]; // Big enough to hold a full address. - UINT16 port; - UINT8 padding1[2]; - tic_t time; -} ATTRPACK ms_holepunch_packet_t; -#if defined(_MSC_VER) -#pragma pack() -#endif - -// win32 or djgpp -#if defined (_WIN32) || defined (_WIN32_WCE) || defined (__DJGPP__) -#define ioctl ioctlsocket -#define close closesocket -#ifdef WATTCP -#define strerror strerror_s -#endif -#if defined (_WIN32) || defined (_WIN32_WCE) -#undef errno -#define errno h_errno // some very strange things happen when not using h_error -#endif -#ifndef AI_ADDRCONFIG -#define AI_ADDRCONFIG 0x00000400 -#endif -#endif - #ifndef NONET static void Command_Listserv_f(void); #endif static void MasterServer_OnChange(void); static void ServerName_OnChange(void); -#define DEF_PORT "28900" -consvar_t cv_masterserver = {"masterserver", "ms.srb2.org:"DEF_PORT, CV_SAVE, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, ServerName_OnChange, 0, NULL, NULL, 0, 0, NULL}; +char *ms_API; INT16 ms_RoomId = -1; static enum { MSCS_NONE, MSCS_WAITING, MSCS_REGISTERED, MSCS_FAILED } con_state = MSCS_NONE; -static INT32 msnode = -1; UINT16 current_port = 0; -#if (defined (_WIN32) || defined (_WIN32_WCE) || defined (_WIN32)) && !defined (NONET) -typedef SOCKET SOCKET_TYPE; -#define ERRSOCKET (SOCKET_ERROR) -#else -#if (defined (__unix__) && !defined (MSDOS)) || defined (__APPLE__) || defined (__HAIKU__) || defined (_PS3) -typedef int SOCKET_TYPE; -#else -typedef unsigned long SOCKET_TYPE; -#endif -#define ERRSOCKET (-1) -#endif - -#if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (_WIN32) -typedef int socklen_t; -#endif - -#ifndef NONET -static SOCKET_TYPE socket_fd = ERRSOCKET; // WINSOCK socket -static struct timeval select_timeout; -static fd_set wset; -static size_t recvfull(SOCKET_TYPE s, char *buf, size_t len, int flags); -#endif - // Room list is an external variable now. // Avoiding having to get info ten thousand times... msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test @@ -261,304 +63,38 @@ void AddMServCommands(void) { #ifndef NONET CV_RegisterVar(&cv_masterserver); - CV_RegisterVar(&cv_http_masterserver); CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); #endif } -/** Closes the connection to the master server. - * - * \todo Fix for Windows? - */ -static void CloseConnection(void) +static void WarnGUI (void) { -#ifndef NONET - if (socket_fd != (SOCKET_TYPE)ERRSOCKET) - close(socket_fd); - socket_fd = ERRSOCKET; -#endif -} - -// -// MS_Write(): -// -static INT32 MS_Write(msg_t *msg) -{ -#ifdef NONET - (void)msg; - return MS_WRITE_ERROR; -#else - size_t len; - - if (msg->length == 0) - msg->length = (INT32)strlen(msg->buffer); - len = msg->length + HEADER_SIZE; - - msg->type = htonl(msg->type); - msg->length = htonl(msg->length); - msg->room = htonl(msg->room); - - if ((size_t)send(socket_fd, (char *)msg, (int)len, 0) != len) - return MS_WRITE_ERROR; - return 0; -#endif -} - -// -// MS_Read(): -// -static INT32 MS_Read(msg_t *msg) -{ -#ifdef NONET - (void)msg; - return MS_READ_ERROR; -#else - if (recvfull(socket_fd, (char *)msg, HEADER_SIZE, 0) != HEADER_SIZE) - return MS_READ_ERROR; - - msg->type = ntohl(msg->type); - msg->length = ntohl(msg->length); - msg->room = ntohl(msg->room); - - if (!msg->length) // fix a bug in Windows 2000 - return 0; - - if (recvfull(socket_fd, (char *)msg->buffer, msg->length, 0) != msg->length) - return MS_READ_ERROR; - return 0; -#endif -} - -#ifndef NONET -/** Gets a list of game servers from the master server. - */ -static INT32 GetServersList(void) -{ - msg_t msg; - INT32 count = 0; - - msg.type = GET_SERVER_MSG; - msg.length = 0; - msg.room = 0; - if (MS_Write(&msg) < 0) - return MS_WRITE_ERROR; - - while (MS_Read(&msg) >= 0) - { - if (!msg.length) - { - if (!count) - CONS_Alert(CONS_NOTICE, M_GetText("No servers currently running.\n")); - return MS_NO_ERROR; - } - count++; - CONS_Printf("%s",msg.buffer); - } - - return MS_READ_ERROR; -} -#endif - -// -// MS_Connect() -// -static INT32 MS_Connect(const char *ip_addr, const char *str_port, INT32 async) -{ -#ifdef NONET - (void)ip_addr; - (void)str_port; - (void)async; -#else - struct my_addrinfo *ai, *runp, hints; - int gaie; - - memset (&hints, 0x00, sizeof(hints)); -#ifdef AI_ADDRCONFIG - hints.ai_flags = AI_ADDRCONFIG; -#endif - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - //I_InitTcpNetwork(); this is already done on startup in D_SRB2Main() - if (!I_InitTcpDriver()) // this is done only if not already done - return MS_SOCKET_ERROR; - - gaie = I_getaddrinfo(ip_addr, str_port, &hints, &ai); - if (gaie != 0) - return MS_GETHOSTBYNAME_ERROR; - else - runp = ai; - - while (runp != NULL) - { - socket_fd = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); - if (socket_fd != (SOCKET_TYPE)ERRSOCKET) - { - if (async) // do asynchronous connection - { -#ifdef FIONBIO -#ifdef WATTCP - char res = 1; -#else - unsigned long res = 1; -#endif - - ioctl(socket_fd, FIONBIO, &res); -#endif - - if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) == ERRSOCKET) - { -#ifdef _WIN32 // humm, on win32/win64 it doesn't work with EINPROGRESS (stupid windows) - if (WSAGetLastError() != WSAEWOULDBLOCK) -#else - if (errno != EINPROGRESS) -#endif - { - con_state = MSCS_FAILED; - CloseConnection(); - I_freeaddrinfo(ai); - return MS_CONNECT_ERROR; - } - } - con_state = MSCS_WAITING; - FD_ZERO(&wset); - FD_SET(socket_fd, &wset); - select_timeout.tv_sec = 0, select_timeout.tv_usec = 0; - I_freeaddrinfo(ai); - return 0; - } - else if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) != ERRSOCKET) - { - I_freeaddrinfo(ai); - return 0; - } - } - runp = runp->ai_next; - } - I_freeaddrinfo(ai); -#endif - return MS_CONNECT_ERROR; + M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n\nCheck the console for details.\n"), NULL, MM_NOTHING); } #define NUM_LIST_SERVER MAXSERVERLIST const msg_server_t *GetShortServersList(INT32 room) { static msg_server_t server_list[NUM_LIST_SERVER+1]; // +1 for easy test - msg_t msg; - INT32 i; - if (HMS_in_use()) - { - HMS_fetch_servers(server_list, room); + if (HMS_fetch_servers(server_list, room)) return server_list; - } - - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING); - return NULL; - } - - msg.type = GET_SHORT_SERVER_MSG; - msg.length = 0; - msg.room = room; - if (MS_Write(&msg) < 0) - return NULL; - - for (i = 0; i < NUM_LIST_SERVER && MS_Read(&msg) >= 0; i++) - { - if (!msg.length) - { - server_list[i].header.buffer[0] = 0; - CloseConnection(); - return server_list; - } - M_Memcpy(&server_list[i], msg.buffer, sizeof (msg_server_t)); - server_list[i].header.buffer[0] = 1; - } - CloseConnection(); - if (i == NUM_LIST_SERVER) - { - server_list[i].header.buffer[0] = 0; - return server_list; - } else + { + WarnGUI(); return NULL; + } } INT32 GetRoomsList(boolean hosting) { - static msg_ban_t banned_info[1]; - msg_t msg; - INT32 i; - - if (HMS_in_use()) - { - HMS_fetch_rooms( ! hosting ); + if (HMS_fetch_rooms( ! hosting )) return 1; - } - - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING); - return -1; - } - - if (hosting) - msg.type = GET_ROOMS_HOST_MSG; - else - msg.type = GET_ROOMS_MSG; - msg.length = 0; - msg.room = 0; - if (MS_Write(&msg) < 0) - { - room_list[0].id = 1; - strcpy(room_list[0].motd,"Master Server Offline."); - strcpy(room_list[0].name,"Offline"); - return -1; - } - - for (i = 0; i < NUM_LIST_ROOMS && MS_Read(&msg) >= 0; i++) - { - if(msg.type == GET_BANNED_MSG) - { - char banmsg[1000]; - M_Memcpy(&banned_info[0], msg.buffer, sizeof (msg_ban_t)); - if (hosting) - sprintf(banmsg, M_GetText("You have been banned from\nhosting netgames.\n\nUnder the following IP Range:\n%s - %s\n\nFor the following reason:\n%s\n\nYour ban will expire on:\n%s"),banned_info[0].ipstart,banned_info[0].ipend,banned_info[0].reason,banned_info[0].endstamp); - else - sprintf(banmsg, M_GetText("You have been banned from\njoining netgames.\n\nUnder the following IP Range:\n%s - %s\n\nFor the following reason:\n%s\n\nYour ban will expire on:\n%s"),banned_info[0].ipstart,banned_info[0].ipend,banned_info[0].reason,banned_info[0].endstamp); - M_StartMessage(banmsg, NULL, MM_NOTHING); - ms_RoomId = -1; - return -2; - } - if (!msg.length) - { - room_list[i].header.buffer[0] = 0; - CloseConnection(); - return 1; - } - M_Memcpy(&room_list[i], msg.buffer, sizeof (msg_rooms_t)); - room_list[i].header.buffer[0] = 1; - } - CloseConnection(); - if (i == NUM_LIST_ROOMS) - { - room_list[i].header.buffer[0] = 0; - return 1; - } else { - room_list[0].id = 1; - strcpy(room_list[0].motd,M_GetText("Master Server Offline.")); - strcpy(room_list[0].name,M_GetText("Offline")); + WarnGUI(); return -1; } } @@ -566,88 +102,29 @@ INT32 GetRoomsList(boolean hosting) #ifdef UPDATE_ALERT const char *GetMODVersion(void) { - static msg_t msg; + static char buffer[16]; + int c; - if (HMS_in_use()) - { - return HMS_compare_mod_version(); - } + c = HMS_compare_mod_version(buffer, sizeof buffer); - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING); - return NULL; - } - - msg.type = GET_VERSION_MSG; - msg.length = sizeof MODVERSION; - msg.room = MODID; // Might as well use it for something. - sprintf(msg.buffer,"%d",MODVERSION); - if (MS_Write(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("Could not send to the Master Server\n")); - M_StartMessage(M_GetText("Could not send to the Master Server\n"), NULL, MM_NOTHING); - CloseConnection(); - return NULL; - } - - if (MS_Read(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("No reply from the Master Server\n")); - M_StartMessage(M_GetText("No reply from the Master Server\n"), NULL, MM_NOTHING); - CloseConnection(); - return NULL; - } - - CloseConnection(); - - if(strcmp(msg.buffer,"NULL") != 0) - { - return msg.buffer; - } + if (c > 0) + return buffer; else + { + if (! c) + WarnGUI(); + return NULL; + } } // Console only version of the above (used before game init) void GetMODVersion_Console(void) { - static msg_t msg; + char buffer[16]; - if (HMS_in_use()) - return; - - // we must be connected to the master server before writing to it - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; - } - - msg.type = GET_VERSION_MSG; - msg.length = sizeof MODVERSION; - msg.room = MODID; // Might as well use it for something. - sprintf(msg.buffer,"%d",MODVERSION); - if (MS_Write(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("Could not send to the Master Server\n")); - CloseConnection(); - return; - } - - if (MS_Read(&msg) < 0) - { - CONS_Alert(CONS_ERROR, M_GetText("No reply from the Master Server\n")); - CloseConnection(); - return; - } - - CloseConnection(); - - if(strcmp(msg.buffer,"NULL") != 0) - I_Error(UPDATE_ALERT_STRING_CONSOLE, VERSIONSTRING, msg.buffer); + if (HMS_compare_mod_version(buffer, sizeof buffer) > 0) + I_Error(UPDATE_ALERT_STRING_CONSOLE, VERSIONSTRING, buffer); } #endif @@ -656,390 +133,56 @@ void GetMODVersion_Console(void) */ static void Command_Listserv_f(void) { - if (con_state == MSCS_WAITING) - { - CONS_Alert(CONS_NOTICE, M_GetText("Not yet connected to the Master Server.\n")); - return; - } - CONS_Printf(M_GetText("Retrieving server list...\n")); - if (HMS_in_use()) { HMS_list_servers(); - return; } - - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; - } - - if (GetServersList()) - CONS_Alert(CONS_ERROR, M_GetText("Cannot get server list\n")); - - CloseConnection(); } #endif -FUNCMATH static const char *int2str(INT32 n) -{ - INT32 i; - static char res[16]; - - res[15] = '\0'; - res[14] = (char)((char)(n%10)+'0'); - for (i = 13; (n /= 10); i--) - res[i] = (char)((char)(n%10)+'0'); - - return &res[i+1]; -} - -#ifndef NONET -static INT32 ConnectionFailed(void) -{ - con_state = MSCS_FAILED; - CONS_Alert(CONS_ERROR, M_GetText("Connection to Master Server failed\n")); - CloseConnection(); - return MS_CONNECT_ERROR; -} -#endif - -/** Tries to register the local game server on the master server. - */ -static INT32 AddToMasterServer(boolean firstadd) -{ -#ifdef NONET - (void)firstadd; -#else - static INT32 retry = 0; - int i, res; - socklen_t j; - msg_t msg; - msg_server_t *info = (msg_server_t *)msg.buffer; - INT32 room = -1; - fd_set tset; - time_t timestamp = time(NULL); - UINT32 signature, tmp; - const char *insname; - - if (HMS_in_use()) - { - HMS_update(); - return MS_NO_ERROR; - } - - M_Memcpy(&tset, &wset, sizeof (tset)); - res = select(255, NULL, &tset, NULL, &select_timeout); - if (res != ERRSOCKET && !res) - { - if (retry++ > 30) // an about 30 second timeout - { - retry = 0; - CONS_Alert(CONS_ERROR, M_GetText("Master Server timed out\n")); - MSLastPing = timestamp; - return ConnectionFailed(); - } - return MS_CONNECT_ERROR; - } - retry = 0; - /* - Somehow we can still select our old socket despite it being closed(?). - Atleast, that's what I THINK is happening. Anyway, we have to check that we - haven't open a socket, and actually open it! - */ - /*if (res == ERRSOCKET)*//* wtf? no! */ - if (socket_fd == (SOCKET_TYPE)ERRSOCKET) - { - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno)); - MSLastPing = timestamp; - return ConnectionFailed(); - } - } - - // so, the socket is writable, but what does that mean, that the connection is - // ok, or bad... let see that! - j = (socklen_t)sizeof (i); - getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&i, &j); - /* - This is also wrong. If getsockopt fails, i doesn't have to be set. Plus, if - it is set (which it appearantly is on linux), we check errno anyway. And in - the case that i is returned as normal, we don't even report the correct - value! So we accomplish NOTHING, except returning due to dumb luck. - If you care, fix this--I don't. -James (R.) - */ - if (i) // it was bad - { - CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno)); - MSLastPing = timestamp; - return ConnectionFailed(); - } - -#ifdef PARANOIA - if (ms_RoomId <= 0) - I_Error("Attmepted to host in room \"All\"!\n"); -#endif - room = ms_RoomId; - - for(signature = 0, insname = cv_servername.string; *insname; signature += *insname++); - tmp = (UINT32)(signature * (size_t)&MSLastPing); - signature *= tmp; - signature &= 0xAAAAAAAA; - M_Memcpy(&info->header.signature, &signature, sizeof (UINT32)); - - strcpy(info->ip, ""); - strcpy(info->port, int2str(current_port)); - strcpy(info->name, cv_servername.string); - M_Memcpy(&info->room, & room, sizeof (INT32)); -#if VERSION > 0 || SUBVERSION > 0 - sprintf(info->version, "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION); -#else // Trunk build, send revision info - strcpy(info->version, GetRevisionString()); -#endif - strcpy(registered_server.name, cv_servername.string); - - if(firstadd) - msg.type = ADD_SERVER_MSG; - else - msg.type = PING_SERVER_MSG; - - msg.length = (UINT32)sizeof (msg_server_t); - msg.room = 0; - if (MS_Write(&msg) < 0) - { - MSLastPing = timestamp; - return ConnectionFailed(); - } - - if(con_state != MSCS_REGISTERED) - CONS_Printf(M_GetText("Master Server update successful.\n")); - - MSLastPing = timestamp; - con_state = MSCS_REGISTERED; - CloseConnection(); -#endif - return MS_NO_ERROR; -} - -static INT32 RemoveFromMasterSever(void) -{ - msg_t msg; - msg_server_t *info = (msg_server_t *)msg.buffer; - - strcpy(info->header.buffer, ""); - strcpy(info->ip, ""); - strcpy(info->port, int2str(current_port)); - strcpy(info->name, registered_server.name); - sprintf(info->version, "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION); - - msg.type = REMOVE_SERVER_MSG; - msg.length = (UINT32)sizeof (msg_server_t); - msg.room = 0; - if (MS_Write(&msg) < 0) - return MS_WRITE_ERROR; - - return MS_NO_ERROR; -} - -const char *GetMasterServerPort(void) -{ - const char *t = cv_masterserver.string; - - while ((*t != ':') && (*t != '\0')) - t++; - - if (*t) - return ++t; - else - return DEF_PORT; -} - -/** Gets the IP address of the master server. Actually, it seems to just - * return the hostname, instead; the lookup is done elsewhere. - * - * \return Hostname of the master server, without port number on the end. - * \todo Rename function? - */ -const char *GetMasterServerIP(void) -{ - static char str_ip[64]; - char *t = str_ip; - - if (strstr(cv_masterserver.string, "srb2.ssntails.org:28910") - || strstr(cv_masterserver.string, "srb2.servegame.org:28910") - || strstr(cv_masterserver.string, "srb2.servegame.org:28900") - ) - { - // replace it with the current default one - CV_Set(&cv_masterserver, cv_masterserver.defaultvalue); - } - - strcpy(t, cv_masterserver.string); - - while ((*t != ':') && (*t != '\0')) - t++; - *t = '\0'; - - return str_ip; -} - -void MSOpenUDPSocket(void) -{ -#ifndef NONET - if (I_NetMakeNodewPort) - { - // If it's already open, there's nothing to do. - if (msnode < 0) - msnode = I_NetMakeNodewPort(GetMasterServerIP(), GetMasterServerPort()); - } - else -#endif - msnode = -1; -} - -void MSCloseUDPSocket(void) -{ - if (msnode != INT16_MAX) I_NetFreeNodenum(msnode); - msnode = -1; -} - void RegisterServer(void) { - if (con_state == MSCS_REGISTERED || con_state == MSCS_WAITING) - return; - CONS_Printf(M_GetText("Registering this server on the Master Server...\n")); - if (HMS_in_use()) { if (HMS_register()) con_state = MSCS_REGISTERED; - return; + else + con_state = MSCS_FAILED; } +} - strcpy(registered_server.ip, GetMasterServerIP()); - strcpy(registered_server.port, GetMasterServerPort()); - - if (MS_Connect(registered_server.ip, registered_server.port, 1)) +static void UpdateServer(void) +{ + if (!( con_state == MSCS_REGISTERED && HMS_update() )) { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; + con_state = MSCS_FAILED; + RegisterServer(); } - MSOpenUDPSocket(); - // keep the TCP connection open until AddToMasterServer() is completed; + time(&MSLastPing); } static inline void SendPingToMasterServer(void) { - if (HMS_in_use()) - return; -/* static tic_t next_time = 0; - tic_t cur_time; - char *inbuffer = (char*)netbuffer; - - cur_time = I_GetTime(); - if (!netgame) - UnregisterServer(); - else if (cur_time > next_time) // ping every 2 second if possible - { - next_time = cur_time+2*TICRATE; - - if (con_state == MSCS_WAITING) - AddToMasterServer(); - - if (con_state != MSCS_REGISTERED) - return; - - // cur_time is just a dummy data to send - WRITEUINT32(inbuffer, cur_time); - doomcom->datalength = sizeof (cur_time); - doomcom->remotenode = (INT16)msnode; - I_NetSend(); - } -*/ - // Here, have a simpler MS Ping... - Cue if(time(NULL) > (MSLastPing+(60*2)) && con_state != MSCS_NONE) { - //CONS_Debug(DBG_NETPLAY, "%ld (current time) is greater than %d (Last Ping Time)\n", time(NULL), MSLastPing); - if(MSLastPing < 1) - AddToMasterServer(true); - else - AddToMasterServer(false); + UpdateServer(); } } -void SendAskInfoViaMS(INT32 node, tic_t asktime) -{ - const char *address; - UINT16 port; - char *inip; - ms_holepunch_packet_t mshpp; - - MSOpenUDPSocket(); - - // This must be called after calling MSOpenUDPSocket, due to the - // static buffer. - address = I_GetNodeAddress(node); - - // no address? - if (!address) - return; - - // Copy the IP address into the buffer. - inip = mshpp.ip; - while(*address && *address != ':') *inip++ = *address++; - *inip = '\0'; - - // Get the port. - port = (UINT16)(*address++ ? atoi(address) : 0); - mshpp.port = SHORT(port); - - // Set the time for ping calculation. - mshpp.time = LONG(asktime); - - // Send to the MS. - M_Memcpy(netbuffer, &mshpp, sizeof(mshpp)); - doomcom->datalength = sizeof(ms_holepunch_packet_t); - doomcom->remotenode = (INT16)msnode; - I_NetSend(); -} - void UnregisterServer(void) { - if (con_state != MSCS_REGISTERED) + if (con_state == MSCS_REGISTERED) { - con_state = MSCS_NONE; - CloseConnection(); - return; + CONS_Printf(M_GetText("Removing this server from the Master Server...\n")); + + HMS_unlist(); } con_state = MSCS_NONE; - - CONS_Printf(M_GetText("Removing this server from the Master Server...\n")); - - if (HMS_in_use()) - { - HMS_unlist(); - return; - } - - if (MS_Connect(registered_server.ip, registered_server.port, 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n")); - return; - } - - if (RemoveFromMasterSever() < 0) - CONS_Alert(CONS_ERROR, M_GetText("Cannot remove this server from the Master Server\n")); - - CloseConnection(); - MSCloseUDPSocket(); - MSLastPing = 0; } void MasterClient_Ticker(void) @@ -1051,33 +194,23 @@ void MasterClient_Ticker(void) static void ServerName_OnChange(void) { if (con_state == MSCS_REGISTERED) - AddToMasterServer(false); + UpdateServer(); } static void MasterServer_OnChange(void) { - UnregisterServer(); - RegisterServer(); -} + boolean auto_register; -#ifndef NONET -// Like recv, but waits until we've got enough data to fill the buffer. -static size_t recvfull(SOCKET_TYPE s, char *buf, size_t len, int flags) -{ - /* Total received. */ - size_t totallen = 0; + auto_register = ( con_state != MSCS_NONE ); - while(totallen < len) + if (ms_API) { - ssize_t ret = (ssize_t)recv(s, buf + totallen, (int)(len - totallen), flags); - - /* Error. */ - if(ret == -1) - return (size_t)-1; - - totallen += ret; + UnregisterServer(); + Z_Free(ms_API); } - return totallen; + ms_API = Z_StrDup(cv_masterserver.string); + + if (auto_register) + RegisterServer(); } -#endif diff --git a/src/mserv.h b/src/mserv.h index 9f6f9ad30..3c3b2cf31 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -2,6 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1999-2018 by Sonic Team Junior. +// Copyright (C) 2020 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -13,7 +14,7 @@ #ifndef _MSERV_H_ #define _MSERV_H_ -#define MASTERSERVERS21 // MasterServer v2.1 +#define HMS123311 // don't mess with nights, man // lowered from 32 due to menu changes #define NUM_LIST_ROOMS 16 @@ -64,21 +65,14 @@ typedef struct // ================================ GLOBALS =============================== extern consvar_t cv_masterserver, cv_servername; -extern consvar_t cv_http_masterserver; extern consvar_t cv_masterserver_debug; +extern char *ms_API; + // < 0 to not connect (usually -1) (offline mode) // == 0 to show all rooms, not a valid hosting room // anything else is whatever room the MS assigns to that number (online mode) -INT16 ms_RoomId; - -const char *GetMasterServerPort(void); -const char *GetMasterServerIP(void); - -void MSOpenUDPSocket(void); -void MSCloseUDPSocket(void); - -void SendAskInfoViaMS(INT32 node, tic_t asktime); +extern INT16 ms_RoomId; void RegisterServer(void); void UnregisterServer(void); From 3440e0307b3cba9ec1ca5790032c449aef6df0ff Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Apr 2020 22:31:11 -0700 Subject: [PATCH 048/211] Add a hack so the 'All' room isn't display for hosting --- src/hms123311.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index be0df42a3..7025aff58 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -201,6 +201,8 @@ HMS_fetch_rooms (int joining) char *title; char *motd; + int id_no; + char *p; char *end; @@ -212,7 +214,7 @@ HMS_fetch_rooms (int joining) { p = hms->buffer; - for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") ); ++i) + for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") );) { *end = '\0'; @@ -222,11 +224,22 @@ HMS_fetch_rooms (int joining) if (id && title && motd) { - room_list[i].header.buffer[0] = 1; + id_no = atoi(id); - room_list[i].id = atoi(id); - strlcpy(room_list[i].name, title, sizeof room_list[i].name); - strlcpy(room_list[i].motd, motd, sizeof room_list[i].motd); + /* + Don't show the 'All' room if hosting. And it's a hack like this + because I'm way too lazy to add another feature to the MS. + */ + if (joining || id_no != 0) + { + room_list[i].header.buffer[0] = 1; + + room_list[i].id = id_no; + strlcpy(room_list[i].name, title, sizeof room_list[i].name); + strlcpy(room_list[i].motd, motd, sizeof room_list[i].motd); + + i++; + } p = ( end + 3 );/* skip the three linefeeds */ } From 2a9ae68364448557716c810fd8ee47c1c5ab7010 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Apr 2020 22:54:58 -0700 Subject: [PATCH 049/211] Don't try to update right after registering --- src/mserv.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/mserv.c b/src/mserv.c index 8cf656820..3fbd37483 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -151,17 +151,21 @@ void RegisterServer(void) else con_state = MSCS_FAILED; } + + time(&MSLastPing); } static void UpdateServer(void) { - if (!( con_state == MSCS_REGISTERED && HMS_update() )) + if (( con_state == MSCS_REGISTERED && HMS_update() )) + { + time(&MSLastPing); + } + else { con_state = MSCS_FAILED; RegisterServer(); } - - time(&MSLastPing); } static inline void SendPingToMasterServer(void) From 0e7678a5d5e56aa60ec32f42bf759ec1eece0fa3 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Apr 2020 23:10:06 -0700 Subject: [PATCH 050/211] masterserver_update_rate cvar determines wait between updates in minutes The new default is 15 minutes as well. And if you think that's too long, I have confirmed that the Master Server delists inactive servers after a whopping 40 minutes, at least. --- src/mserv.c | 13 ++++++++++++- src/mserv.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 3fbd37483..3c60f064e 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -34,15 +34,25 @@ int HMS_compare_mod_version (char *buffer, size_t size_of_buffer); static time_t MSLastPing; +static inline void SendPingToMasterServer(void); + #ifndef NONET static void Command_Listserv_f(void); #endif static void MasterServer_OnChange(void); static void ServerName_OnChange(void); +static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { + {2, "MIN"}, + {60, "MAX"}, + {0} +}; + consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, ServerName_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, SendPingToMasterServer, 0, NULL, NULL, 0, 0, NULL}; + char *ms_API; INT16 ms_RoomId = -1; @@ -63,6 +73,7 @@ void AddMServCommands(void) { #ifndef NONET CV_RegisterVar(&cv_masterserver); + CV_RegisterVar(&cv_masterserver_update_rate); CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); @@ -171,7 +182,7 @@ static void UpdateServer(void) static inline void SendPingToMasterServer(void) { // Here, have a simpler MS Ping... - Cue - if(time(NULL) > (MSLastPing+(60*2)) && con_state != MSCS_NONE) + if(time(NULL) > (MSLastPing+(60*cv_masterserver_update_rate.value)) && con_state != MSCS_NONE) { UpdateServer(); } diff --git a/src/mserv.h b/src/mserv.h index 3c3b2cf31..3f5b703da 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -65,6 +65,7 @@ typedef struct // ================================ GLOBALS =============================== extern consvar_t cv_masterserver, cv_servername; +extern consvar_t cv_masterserver_update_rate; extern consvar_t cv_masterserver_debug; extern char *ms_API; From ce4161d9853b77073b135f438f252cad51f43b0b Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Apr 2020 23:31:26 -0700 Subject: [PATCH 051/211] Compiler errors: won't stop, can't stop --- src/d_clisrv.c | 32 +++++++++++--------------------- src/hms123311.c | 11 ++++++----- src/mserv.c | 10 ---------- src/mserv.h | 10 ++++++++++ 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 398083a79..5b439ce00 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1753,7 +1753,7 @@ static void CL_LoadReceivedSavegame(void) #endif #ifndef NONET -static void SendAskInfo(INT32 node, boolean viams) +static void SendAskInfo(INT32 node) { const tic_t asktime = I_GetTime(); netbuffer->packettype = PT_ASKINFO; @@ -1835,7 +1835,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) // search for local servers if (netgame) - SendAskInfo(BROADCASTADDR, false); + SendAskInfo(BROADCASTADDR); if (internetsearch) { @@ -1863,7 +1863,6 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); if (node == -1) break; // no more node free - SendAskInfo(node, true); // Force close the connection so that servers can't eat // up nodes forever if we never get a reply back from them // (usually when they've not forwarded their ports). @@ -1953,14 +1952,13 @@ static boolean CL_FinishedFileList(void) /** Called by CL_ServerConnectionTicker * - * \param viams ??? * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. * \return False if the connection was aborted * \sa CL_ServerConnectionTicker * \sa CL_ConnectToServer * */ -static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) +static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) { #ifndef NONET INT32 i; @@ -2014,7 +2012,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) // Ask the info to the server (askinfo packet) if (*asksent + NEWTICRATE < I_GetTime()) { - SendAskInfo(servernode, viams); + SendAskInfo(servernode); *asksent = I_GetTime(); } #else @@ -2029,7 +2027,6 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) /** Called by CL_ConnectToServer * - * \param viams ??? * \param tmpsave The name of the gamestate file??? * \param oldtic Used for knowing when to poll events and redraw * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. @@ -2038,7 +2035,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) * \sa CL_ConnectToServer * */ -static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic_t *oldtic, tic_t *asksent) +static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) { boolean waitmore; INT32 i; @@ -2050,7 +2047,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic switch (cl_mode) { case CL_SEARCHING: - if (!CL_ServerConnectionSearchTicker(viams, asksent)) + if (!CL_ServerConnectionSearchTicker(asksent)) return false; break; @@ -2254,11 +2251,10 @@ boolean CL_Responder(event_t *ev) /** Use adaptive send using net_bandwidth and stat.sendbytes * - * \param viams ??? * \todo Better description... * */ -static void CL_ConnectToServer(boolean viams) +static void CL_ConnectToServer(void) { INT32 pnumnodes, nodewaited = doomcom->numnodes, i; tic_t oldtic; @@ -2333,9 +2329,9 @@ static void CL_ConnectToServer(boolean viams) { // If the connection was aborted for some reason, leave #ifndef NONET - if (!CL_ServerConnectionTicker(viams, tmpsave, &oldtic, &asksent)) + if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) #else - if (!CL_ServerConnectionTicker(viams, (char*)NULL, &oldtic, (tic_t *)NULL)) + if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL)) #endif return; @@ -2515,9 +2511,6 @@ static void Command_ReloadBan(void) //recheck ban.txt static void Command_connect(void) { - // Assume we connect directly. - boolean viams = false; - if (COM_Argc() < 2 || *COM_Argv(1) == 0) { CONS_Printf(M_GetText( @@ -2551,9 +2544,6 @@ static void Command_connect(void) if (netgame && !stricmp(COM_Argv(1), "node")) { servernode = (SINT8)atoi(COM_Argv(2)); - - // Use MS to traverse NAT firewalls. - viams = true; } else if (netgame) { @@ -2590,7 +2580,7 @@ static void Command_connect(void) } botingame = false; botskin = 0; - CL_ConnectToServer(viams); + CL_ConnectToServer(); } #endif @@ -3638,7 +3628,7 @@ boolean SV_SpawnServer(void) // non dedicated server just connect to itself if (!dedicated) - CL_ConnectToServer(false); + CL_ConnectToServer(); else doomcom->numslots = 1; } diff --git a/src/hms123311.c b/src/hms123311.c index 7025aff58..2be8205fb 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -56,8 +56,9 @@ static size_t HMS_on_read (char *s, size_t _1, size_t n, void *userdata) { struct HMS_buffer *buffer; + (void)_1; buffer = userdata; - if (n < ( buffer->end - buffer->needle )) + if (n < (size_t)( buffer->end - buffer->needle )) { memcpy(&buffer->buffer[buffer->needle], s, n); buffer->needle += n; @@ -199,7 +200,7 @@ HMS_fetch_rooms (int joining) char *id; char *title; - char *motd; + char *room_motd; int id_no; @@ -220,9 +221,9 @@ HMS_fetch_rooms (int joining) id = strtok(p, "\n"); title = strtok(0, "\n"); - motd = strtok(0, ""); + room_motd = strtok(0, ""); - if (id && title && motd) + if (id && title && room_motd) { id_no = atoi(id); @@ -236,7 +237,7 @@ HMS_fetch_rooms (int joining) room_list[i].id = id_no; strlcpy(room_list[i].name, title, sizeof room_list[i].name); - strlcpy(room_list[i].motd, motd, sizeof room_list[i].motd); + strlcpy(room_list[i].motd, room_motd, sizeof room_list[i].motd); i++; } diff --git a/src/mserv.c b/src/mserv.c index 3c60f064e..d227d4895 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -22,16 +22,6 @@ #include "m_menu.h" #include "z_zone.h" -/* HTTP */ -int HMS_in_use (void); -int HMS_fetch_rooms (int joining); -int HMS_register (void); -void HMS_unlist (void); -int HMS_update (void); -void HMS_list_servers (void); -int HMS_fetch_servers (msg_server_t *list, int room); -int HMS_compare_mod_version (char *buffer, size_t size_of_buffer); - static time_t MSLastPing; static inline void SendPingToMasterServer(void); diff --git a/src/mserv.h b/src/mserv.h index 3f5b703da..0f954aead 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -90,4 +90,14 @@ extern msg_rooms_t room_list[NUM_LIST_ROOMS+1]; void AddMServCommands(void); +/* HTTP */ +int HMS_in_use (void); +int HMS_fetch_rooms (int joining); +int HMS_register (void); +void HMS_unlist (void); +int HMS_update (void); +void HMS_list_servers (void); +msg_server_t * HMS_fetch_servers (msg_server_t *list, int room); +int HMS_compare_mod_version (char *buffer, size_t size_of_buffer); + #endif From 238e5099ac0cb855e3e0b1c2369e77594ad34398 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 14 Apr 2020 16:55:14 -0700 Subject: [PATCH 052/211] Handle NULL hms buffer --- src/hms123311.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/hms123311.c b/src/hms123311.c index 2be8205fb..3c781c689 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -211,6 +211,9 @@ HMS_fetch_rooms (int joining) hms = HMS_connect("rooms"); + if (! hms) + return 0; + if (HMS_do(hms)) { p = hms->buffer; @@ -272,6 +275,9 @@ HMS_register (void) hms = HMS_connect("rooms/%d/register", ms_RoomId); + if (! hms) + return 0; + title = curl_easy_escape(hms->curl, cv_servername.string, 0); snprintf(post, sizeof post, @@ -311,6 +317,9 @@ HMS_unlist (void) hms = HMS_connect("servers/%s/unlist", hms_server_token); + if (! hms) + return 0; + curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); HMS_do(hms); @@ -331,6 +340,9 @@ HMS_update (void) hms = HMS_connect("servers/%s/update", hms_server_token); + if (! hms) + return 0; + title = curl_easy_escape(hms->curl, cv_servername.string, 0); snprintf(post, sizeof post, @@ -357,6 +369,9 @@ HMS_list_servers (void) hms = HMS_connect("servers"); + if (! hms) + return; + if (HMS_do(hms)) { p = &hms->buffer[strlen(hms->buffer)]; @@ -396,6 +411,9 @@ HMS_fetch_servers (msg_server_t *list, int room_number) else hms = HMS_connect("servers"); + if (! hms) + return NULL; + if (HMS_do(hms)) { snprintf(local_version, sizeof local_version, @@ -481,6 +499,9 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size) hms = HMS_connect("versions/%d", MODID); + if (! hms) + return 0; + ok = 0; if (HMS_do(hms)) From 2aca9918f07f973fa0f40aa9d60fb20843441b04 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 14 Apr 2020 19:41:32 -0700 Subject: [PATCH 053/211] Oops --- src/hms123311.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hms123311.c b/src/hms123311.c index 3c781c689..a1a35f2b9 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -318,7 +318,7 @@ HMS_unlist (void) hms = HMS_connect("servers/%s/unlist", hms_server_token); if (! hms) - return 0; + return; curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); From 94267608c48f560d91fd0767bace33223101f116 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 15 Apr 2020 22:04:29 -0700 Subject: [PATCH 054/211] WHY THE FUCK DID I DO THIS --- src/d_clisrv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5b439ce00..4aba84f26 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1863,6 +1863,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); if (node == -1) break; // no more node free + SendAskInfo(node); // Force close the connection so that servers can't eat // up nodes forever if we never get a reply back from them // (usually when they've not forwarded their ports). From a4ef8dd4011cf20be21c3c379132585a6157db8b Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 17 Apr 2020 20:05:29 -0700 Subject: [PATCH 055/211] Multithreading in my SRB2??? --- src/i_threads.h | 39 +++++ src/sdl/Makefile.cfg | 5 + src/sdl/i_system.c | 5 + src/sdl/i_threads.c | 338 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 387 insertions(+) create mode 100644 src/i_threads.h create mode 100644 src/sdl/i_threads.c diff --git a/src/i_threads.h b/src/i_threads.h new file mode 100644 index 000000000..878e8c388 --- /dev/null +++ b/src/i_threads.h @@ -0,0 +1,39 @@ +// 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 i_threads.h +/// \brief Multithreading abstraction + +#ifdef HAVE_THREADS + +#ifndef I_THREADS_H +#define I_THREADS_H + +typedef void (*I_thread_fn)(void *userdata); + +typedef void * I_mutex; +typedef void * I_cond; + +void I_start_threads (void); +void I_stop_threads (void); + +void I_spawn_thread (const char *name, I_thread_fn, void *userdata); + +/* check in your thread whether to return early */ +int I_thread_is_stopped (void); + +void I_lock_mutex (I_mutex *); +void I_unlock_mutex (I_mutex); + +void I_hold_cond (I_cond *, I_mutex); + +void I_wake_one_cond (I_cond); +void I_wake_all_cond (I_cond); + +#endif/*I_THREADS_H*/ +#endif/*HAVE_THREADS*/ diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg index 58c4d0861..b0c591ce2 100644 --- a/src/sdl/Makefile.cfg +++ b/src/sdl/Makefile.cfg @@ -83,6 +83,11 @@ else SDL_LDFLAGS+=-lSDL2_mixer endif +ifndef NOTHREADS + OPTS+=-DHAVE_THREADS + OBJS+=$(OBJDIR)/i_threads.o +endif + ifdef SDL_TTF OPTS+=-DHAVE_TTF SDL_LDFLAGS+=-lSDL2_ttf -lfreetype -lz diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index c0fca64da..9cb6cbe61 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -162,6 +162,7 @@ static char returnWadPath[256]; #include "../i_video.h" #include "../i_sound.h" #include "../i_system.h" +#include "../i_threads.h" #include "../screen.h" //vid.WndParent #include "../d_net.h" #include "../g_game.h" @@ -3028,6 +3029,10 @@ INT32 I_StartupSystem(void) SDL_version SDLlinked; SDL_VERSION(&SDLcompiled) SDL_GetVersion(&SDLlinked); +#ifdef HAVE_THREADS + I_start_threads(); + atexit(I_stop_threads); +#endif I_StartupConsole(); I_OutputMsg("Compiled for SDL version: %d.%d.%d\n", SDLcompiled.major, SDLcompiled.minor, SDLcompiled.patch); diff --git a/src/sdl/i_threads.c b/src/sdl/i_threads.c new file mode 100644 index 000000000..99e574561 --- /dev/null +++ b/src/sdl/i_threads.c @@ -0,0 +1,338 @@ +// 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 i_threads.c +/// \brief Multithreading abstraction + +#include "../doomdef.h" +#include "../i_threads.h" + +#include + +typedef void * (*Create_fn)(void); + +struct Link; +struct Thread; + +typedef struct Link * Link; +typedef struct Thread * Thread; + +struct Link +{ + void * data; + Link next; + Link prev; +}; + +struct Thread +{ + I_thread_fn entry; + void * userdata; + + SDL_Thread * thread; +}; + +static Link i_thread_pool; +static Link i_mutex_pool; +static Link i_cond_pool; + +static I_mutex i_thread_pool_mutex; +static I_mutex i_mutex_pool_mutex; +static I_mutex i_cond_pool_mutex; + +static SDL_atomic_t i_threads_running = {1}; + +static Link +Insert_link ( + Link * head, + Link link +){ + link->prev = NULL; + link->next = (*head); + if ((*head)) + (*head)->prev = link; + (*head) = link; + return link; +} + +static void +Free_link ( + Link * head, + Link link +){ + if (link->prev) + link->prev->next = link->next; + else + (*head) = link->next; + + if (link->next) + link->next->prev = link->prev; + + free(link->data); + free(link); +} + +static Link +New_link (void *data) +{ + Link link; + + link = malloc(sizeof *link); + + if (! link) + abort(); + + link->data = data; + + return link; +} + +static void * +Identity ( + Link * pool_anchor, + I_mutex pool_mutex, + + void ** anchor, + + Create_fn create_fn +){ + void * id; + + id = SDL_AtomicGetPtr(anchor); + + if (! id) + { + I_lock_mutex(&pool_mutex); + { + id = SDL_AtomicGetPtr(anchor); + + if (! id) + { + id = (*create_fn)(); + + if (! id) + abort(); + + Insert_link(pool_anchor, New_link(id)); + + SDL_AtomicSetPtr(anchor, id); + } + } + I_unlock_mutex(pool_mutex); + } + + return id; +} + +static int +Worker ( + Link link +){ + Thread th; + + th = link->data; + + (*th->entry)(th->userdata); + + if (SDL_AtomicGet(&i_threads_running)) + { + I_lock_mutex(&i_thread_pool_mutex); + { + if (SDL_AtomicGet(&i_threads_running)) + { + SDL_DetachThread(th->thread); + Free_link(&i_thread_pool, link); + } + } + I_unlock_mutex(i_thread_pool_mutex); + } + + return 0; +} + +void +I_spawn_thread ( + const char * name, + I_thread_fn entry, + void * userdata +){ + Link link; + Thread th; + + th = malloc(sizeof *th); + + if (! th) + abort();/* this is pretty GNU of me */ + + th->entry = entry; + th->userdata = userdata; + + I_lock_mutex(&i_thread_pool_mutex); + { + link = Insert_link(&i_thread_pool, New_link(th)); + + if (SDL_AtomicGet(&i_threads_running)) + { + th->thread = SDL_CreateThread( + (SDL_ThreadFunction)Worker, + name, + link + ); + + if (! th->thread) + abort(); + } + } + I_unlock_mutex(i_thread_pool_mutex); +} + +int +I_thread_is_stopped (void) +{ + return ( ! SDL_AtomicGet(&i_threads_running) ); +} + +void +I_start_threads (void) +{ + i_thread_pool_mutex = SDL_CreateMutex(); + i_mutex_pool_mutex = SDL_CreateMutex(); + i_cond_pool_mutex = SDL_CreateMutex(); + + if (!( + i_thread_pool_mutex && + i_mutex_pool_mutex && + i_cond_pool_mutex + )){ + abort(); + } +} + +void +I_stop_threads (void) +{ + Link link; + Link next; + + Thread th; + SDL_mutex * mutex; + SDL_cond * cond; + + if (i_threads_running.value) + { + /* rely on the good will of thread-san */ + SDL_AtomicSet(&i_threads_running, 0); + + I_lock_mutex(&i_thread_pool_mutex); + { + for ( + link = i_thread_pool; + link; + link = next + ){ + next = link->next; + th = link->data; + + SDL_WaitThread(th->thread, NULL); + + free(th); + free(link); + } + } + I_unlock_mutex(i_thread_pool_mutex); + + for ( + link = i_mutex_pool; + link; + link = next + ){ + next = link->next; + mutex = link->data; + + SDL_DestroyMutex(mutex); + + free(link); + } + + for ( + link = i_cond_pool; + link; + link = next + ){ + next = link->next; + cond = link->data; + + SDL_DestroyCond(cond); + + free(link); + } + + SDL_DestroyMutex(i_thread_pool_mutex); + SDL_DestroyMutex(i_mutex_pool_mutex); + SDL_DestroyMutex(i_cond_pool_mutex); + } +} + +void +I_lock_mutex ( + I_mutex * anchor +){ + SDL_mutex * mutex; + + mutex = Identity( + &i_mutex_pool, + i_mutex_pool_mutex, + anchor, + (Create_fn)SDL_CreateMutex + ); + + if (SDL_LockMutex(mutex) == -1) + abort(); +} + +void +I_unlock_mutex ( + I_mutex id +){ + if (SDL_UnlockMutex(id) == -1) + abort(); +} + +void +I_hold_cond ( + I_cond * cond_anchor, + I_mutex mutex_id +){ + SDL_cond * cond; + + cond = Identity( + &i_cond_pool, + i_cond_pool_mutex, + cond_anchor, + (Create_fn)SDL_CreateCond + ); + + if (SDL_CondWait(cond, mutex_id) == -1) + abort(); +} + +void +I_wake_one_cond ( + I_cond id +){ + if (SDL_CondSignal(id) == -1) + abort(); +} + +void +I_wake_all_cond ( + I_cond id +){ + if (SDL_CondBroadcast(id) == -1) + abort(); +} From 29bf51bc996c03d2cc54acf0ece7baa3320846dd Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 26 Apr 2020 19:46:35 -0700 Subject: [PATCH 056/211] Make mod update, room list and server list multithreaded This took fucking ages and it still fails sometimes in edge cases, but I don't give a FUCK right now. --- src/d_clisrv.c | 157 +++++++++++++++++++++++---------- src/d_clisrv.h | 2 + src/d_main.c | 21 ++++- src/f_finale.c | 7 ++ src/f_wipe.c | 9 ++ src/hms123311.c | 71 ++++++++++++++- src/m_menu.c | 226 +++++++++++++++++++++++++++++++++++++++++------- src/m_menu.h | 21 +++++ src/mserv.c | 50 +++++++++-- src/mserv.h | 20 +++-- 10 files changed, 492 insertions(+), 92 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 4aba84f26..89facd5f9 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1820,8 +1820,92 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) M_SortServerList(); } +#ifdef HAVE_THREADS +struct Fetch_servers_ctx +{ + int room; + int id; +}; + +static void +Fetch_servers_thread (struct Fetch_servers_ctx *ctx) +{ + msg_server_t *server_list; + + server_list = GetShortServersList(ctx->room, ctx->id); + + if (server_list) + { + I_lock_mutex(&ms_QueryId_mutex); + { + if (ctx->id != ms_QueryId) + { + free(server_list); + server_list = NULL; + } + } + I_unlock_mutex(ms_QueryId_mutex); + + if (server_list) + { + I_lock_mutex(&m_menu_mutex); + { + if (m_waiting_mode == M_WAITING_SERVERS) + m_waiting_mode = M_NOT_WAITING; + } + I_unlock_mutex(m_menu_mutex); + + I_lock_mutex(&ms_ServerList_mutex); + { + ms_ServerList = server_list; + } + I_unlock_mutex(ms_ServerList_mutex); + } + } + + free(ctx); +} +#endif/*HAVE_THREADS*/ + +void CL_QueryServerList (msg_server_t *server_list) +{ + INT32 i; + + for (i = 0; server_list[i].header.buffer[0]; i++) + { + // Make sure MS version matches our own, to + // thwart nefarious servers who lie to the MS. + + /* lol bruh, that version COMES from the servers */ + //if (strcmp(version, server_list[i].version) == 0) + { + INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); + if (node == -1) + break; // no more node free + SendAskInfo(node); + // Force close the connection so that servers can't eat + // up nodes forever if we never get a reply back from them + // (usually when they've not forwarded their ports). + // + // Don't worry, we'll get in contact with the working + // servers again when they send SERVERINFO to us later! + // + // (Note: as a side effect this probably means every + // server in the list will probably be using the same node (e.g. node 1), + // not that it matters which nodes they use when + // the connections are closed afterwards anyway) + // -- Monster Iestyn 12/11/18 + Net_CloseConnection(node|FORCECLOSE); + } + } +} + void CL_UpdateServerList(boolean internetsearch, INT32 room) { +#ifdef HAVE_THREADS + struct Fetch_servers_ctx *ctx; +#endif + SL_ClearServerList(0); if (!netgame && I_NetOpenSocket) @@ -1839,53 +1923,32 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) if (internetsearch) { - const msg_server_t *server_list; - INT32 i = -1; - server_list = GetShortServersList(room); +#ifdef HAVE_THREADS + ctx = malloc(sizeof *ctx); + + /* This called from M_Refresh so I don't use a mutex */ + m_waiting_mode = M_WAITING_SERVERS; + + I_lock_mutex(&ms_QueryId_mutex); + { + ctx->id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + ctx->room = room; + + I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx); +#else + msg_server_t *server_list; + + server_list = GetShortServersList(room, 0); + if (server_list) { - char version[8] = ""; -#if VERSION > 0 || SUBVERSION > 0 - snprintf(version, sizeof (version), "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION); -#else - strcpy(version, GetRevisionString()); + CL_QueryServerList(server_list); + free(server_list); + } #endif - version[sizeof (version) - 1] = '\0'; - - for (i = 0; server_list[i].header.buffer[0]; i++) - { - // Make sure MS version matches our own, to - // thwart nefarious servers who lie to the MS. - - /* lol bruh, that version COMES from the servers */ - //if (strcmp(version, server_list[i].version) == 0) - { - INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); - if (node == -1) - break; // no more node free - SendAskInfo(node); - // Force close the connection so that servers can't eat - // up nodes forever if we never get a reply back from them - // (usually when they've not forwarded their ports). - // - // Don't worry, we'll get in contact with the working - // servers again when they send SERVERINFO to us later! - // - // (Note: as a side effect this probably means every - // server in the list will probably be using the same node (e.g. node 1), - // not that it matters which nodes they use when - // the connections are closed afterwards anyway) - // -- Monster Iestyn 12/11/18 - Net_CloseConnection(node|FORCECLOSE); - } - } - } - - //no server list?(-1) or no servers?(0) - if (!i) - { - ; /// TODO: display error or warning? - } } } @@ -5528,7 +5591,13 @@ FILESTAMP if (nowtime > resptime) { resptime = nowtime; +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Ticker(); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif CON_Ticker(); } SV_FileSendTicker(); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 66d9e73ea..a3992c8f9 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -18,6 +18,7 @@ #include "d_netcmd.h" #include "tables.h" #include "d_player.h" +#include "mserv.h" #include "md5.h" @@ -568,6 +569,7 @@ void CL_RemoveSplitscreenPlayer(UINT8 p); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_RemovePlayer(INT32 playernum, INT32 reason); +void CL_QueryServerList(msg_server_t *list); void CL_UpdateServerList(boolean internetsearch, INT32 room); boolean CL_Responder(event_t *ev); // Is there a game running diff --git a/src/d_main.c b/src/d_main.c index 467976c17..a6ac63ce4 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -50,6 +50,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "hu_stuff.h" #include "i_sound.h" #include "i_system.h" +#include "i_threads.h" #include "i_video.h" #include "m_argv.h" #include "m_menu.h" @@ -222,6 +223,8 @@ void D_ProcessEvents(void) { event_t *ev; + boolean eaten; + for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { ev = &events[eventtail]; @@ -249,7 +252,17 @@ void D_ProcessEvents(void) } // Menu input - if (M_Responder(ev)) +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + eaten = M_Responder(ev); + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + + if (eaten) continue; // menu ate the event // console input @@ -541,7 +554,13 @@ static void D_Display(void) if (gamestate != GS_TIMEATTACK) CON_Drawer(); +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Drawer(); // menu is drawn even on top of everything +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif // focus lost moved to M_Drawer // diff --git a/src/f_finale.c b/src/f_finale.c index 2bf5c7438..0d38d0e70 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -24,6 +24,7 @@ #include "w_wad.h" #include "z_zone.h" #include "i_system.h" +#include "i_threads.h" #include "m_menu.h" #include "dehacked.h" #include "g_input.h" @@ -317,7 +318,13 @@ void F_IntroDrawer(void) { I_OsPolling(); I_UpdateNoBlit(); +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Drawer(); // menu is drawn even on top of wipes +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 } } diff --git a/src/f_wipe.c b/src/f_wipe.c index 3c8713d18..6104504ce 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -22,6 +22,7 @@ #include "z_zone.h" #include "i_system.h" +#include "i_threads.h" #include "m_menu.h" #include "console.h" #include "d_main.h" @@ -377,7 +378,15 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu) I_UpdateNoBlit(); if (drawMenu) + { +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_Drawer(); // menu is drawn even on top of wipes +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } I_FinishUpdate(); // page flip or blit buffer diff --git a/src/hms123311.c b/src/hms123311.c index a1a35f2b9..29af48e4c 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -19,6 +19,7 @@ Documentation available here. #include "doomdef.h" #include "d_clisrv.h" #include "command.h" +#include "m_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ #include "z_zone.h" @@ -193,11 +194,13 @@ HMS_end (struct HMS_buffer *buffer) } int -HMS_fetch_rooms (int joining) +HMS_fetch_rooms (int joining, int query_id) { struct HMS_buffer *hms; int ok; + int doing_shit; + char *id; char *title; char *room_motd; @@ -209,6 +212,8 @@ HMS_fetch_rooms (int joining) int i; + (void)query_id; + hms = HMS_connect("rooms"); if (! hms) @@ -216,6 +221,8 @@ HMS_fetch_rooms (int joining) if (HMS_do(hms)) { + doing_shit = 1; + p = hms->buffer; for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") );) @@ -236,6 +243,18 @@ HMS_fetch_rooms (int joining) */ if (joining || id_no != 0) { +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); + { + if (query_id != ms_QueryId) + doing_shit = 0; + } + I_unlock_mutex(ms_QueryId_mutex); + + if (! doing_shit) + break; +#endif + room_list[i].header.buffer[0] = 1; room_list[i].id = id_no; @@ -251,9 +270,31 @@ HMS_fetch_rooms (int joining) break; } - room_list[i].header.buffer[0] = 0; + if (doing_shit) + room_list[i].header.buffer[0] = 0; ok = 1; + + if (doing_shit) + { +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + { + for (i = 0; room_list[i].header.buffer[0]; i++) + { + if(*room_list[i].name != '\0') + { + MP_RoomMenu[i+1].text = room_list[i].name; + roomIds[i] = room_list[i].id; + MP_RoomMenu[i+1].status = IT_STRING|IT_CALL; + } + } + } +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + } } else ok = 0; @@ -385,10 +426,12 @@ HMS_list_servers (void) } msg_server_t * -HMS_fetch_servers (msg_server_t *list, int room_number) +HMS_fetch_servers (msg_server_t *list, int room_number, int query_id) { struct HMS_buffer *hms; + int doing_shit; + char local_version[9]; char *room; @@ -404,6 +447,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number) int i; + (void)query_id; + if (room_number > 0) { hms = HMS_connect("rooms/%d/servers", room_number); @@ -416,6 +461,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number) if (HMS_do(hms)) { + doing_shit = 1; + snprintf(local_version, sizeof local_version, "%d.%d.%d", VERSION/100, @@ -448,6 +495,18 @@ HMS_fetch_servers (msg_server_t *list, int room_number) if (address && port && title && version) { +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); + { + if (query_id != ms_QueryId) + doing_shit = 0; + } + I_unlock_mutex(ms_QueryId_mutex); + + if (! doing_shit) + break; +#endif + if (strcmp(version, local_version) == 0) { strlcpy(list[i].ip, address, sizeof list[i].ip); @@ -474,11 +533,15 @@ HMS_fetch_servers (msg_server_t *list, int room_number) } } + if (! doing_shit) + break; + p = ( section_end + 2 ); } while (section_end) ; - list[i].header.buffer[0] = 0; + if (doing_shit) + list[i].header.buffer[0] = 0; } else list = NULL; diff --git a/src/m_menu.c b/src/m_menu.c index 8025f91d4..606e3badc 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -32,6 +32,7 @@ #include "sounds.h" #include "s_sound.h" #include "i_system.h" +#include "i_threads.h" // Addfile #include "filesrch.h" @@ -118,6 +119,12 @@ typedef enum NUM_QUITMESSAGES } text_enum; +#ifdef HAVE_THREADS +I_mutex m_menu_mutex; +#endif + +M_waiting_mode_t m_waiting_mode = M_NOT_WAITING; + const char *quitmsg[NUM_QUITMESSAGES]; // Stuff for customizing the player select screen Tails 09-22-2003 @@ -1043,7 +1050,7 @@ enum FIRSTSERVERLINE }; -static menuitem_t MP_RoomMenu[] = +menuitem_t MP_RoomMenu[] = { {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, {IT_DISABLED, NULL, "", M_ChooseRoom, 18}, @@ -3211,6 +3218,30 @@ void M_SetupNextMenu(menu_t *menudef) { INT16 i; +#ifdef HAVE_THREADS + if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef) + { + I_lock_mutex(&ms_QueryId_mutex); + { + ms_QueryId++; + } + I_unlock_mutex(ms_QueryId_mutex); + } + + if (currentMenu == &MP_ConnectDef) + { + I_lock_mutex(&ms_ServerList_mutex); + { + if (ms_ServerList) + { + free(ms_ServerList); + ms_ServerList = NULL; + } + } + I_unlock_mutex(ms_ServerList_mutex); + } +#endif/*HAVE_THREADS*/ + if (currentMenu->quitroutine) { // If you're going from a menu to itself, why are you running the quitroutine? You're not quitting it! -SH @@ -3268,6 +3299,19 @@ void M_Ticker(void) if (--vidm_testingmode == 0) setmodeneeded = vidm_previousmode + 1; } + +#ifdef HAVE_THREADS + I_lock_mutex(&ms_ServerList_mutex); + { + if (ms_ServerList) + { + CL_QueryServerList(ms_ServerList); + free(ms_ServerList); + ms_ServerList = NULL; + } + } + I_unlock_mutex(ms_ServerList_mutex); +#endif } // @@ -8236,22 +8280,65 @@ static INT32 menuRoomIndex = 0; static void M_DrawRoomMenu(void) { + static int frame = -12; + int dot_frame; + char text[4]; + const char *rmotd; + const char *waiting_message; + + int dots; + + if (m_waiting_mode) + { + dot_frame = frame / 4; + dots = dot_frame + 3; + + strcpy(text, " "); + + if (dots > 0) + { + if (dot_frame < 0) + dot_frame = 0; + + strncpy(&text[dot_frame], "...", min(dots, 3 - dot_frame)); + } + + if (++frame == 12) + frame = -12; + + currentMenu->menuitems[0].text = text; + } // use generic drawer for cursor, items and title M_DrawGenericMenu(); V_DrawString(currentMenu->x - 16, currentMenu->y, highlightflags, M_GetText("Select a room")); - M_DrawTextBox(144, 24, 20, 20); + if (m_waiting_mode == M_NOT_WAITING) + { + M_DrawTextBox(144, 24, 20, 20); - if (itemOn == 0) - rmotd = M_GetText("Don't connect to the Master Server."); - else - rmotd = room_list[itemOn-1].motd; + if (itemOn == 0) + rmotd = M_GetText("Don't connect to the Master Server."); + else + rmotd = room_list[itemOn-1].motd; - rmotd = V_WordWrap(0, 20*8, 0, rmotd); - V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd); + rmotd = V_WordWrap(0, 20*8, 0, rmotd); + V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd); + } + + if (m_waiting_mode) + { + // Display a little "please wait" message. + M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3); + if (m_waiting_mode == M_WAITING_VERSION) + waiting_message = "Checking for updates..."; + else + waiting_message = "Fetching room info..."; + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, waiting_message); + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait."); + } } static void M_DrawConnectMenu(void) @@ -8327,6 +8414,14 @@ static void M_DrawConnectMenu(void) localservercount = serverlistcount; M_DrawGenericMenu(); + + if (m_waiting_mode) + { + // Display a little "please wait" message. + M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Searching for servers..."); + V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait."); + } } static boolean M_CancelConnect(void) @@ -8408,10 +8503,10 @@ void M_SortServerList(void) #ifndef NONET #ifdef UPDATE_ALERT -static boolean M_CheckMODVersion(void) +static boolean M_CheckMODVersion(int id) { char updatestring[500]; - const char *updatecheck = GetMODVersion(); + const char *updatecheck = GetMODVersion(id); if(updatecheck) { sprintf(updatestring, UPDATE_ALERT_STRING, VERSIONSTRING, updatecheck); @@ -8420,7 +8515,62 @@ static boolean M_CheckMODVersion(void) } else return true; } -#endif + +#ifdef HAVE_THREADS +static void +Check_new_version_thread (int *id) +{ + int hosting; + int ok; + + ok = 0; + + if (M_CheckMODVersion(*id)) + { + I_lock_mutex(&ms_QueryId_mutex); + { + ok = ( *id == ms_QueryId ); + } + I_unlock_mutex(ms_QueryId_mutex); + + if (ok) + { + I_lock_mutex(&m_menu_mutex); + { + m_waiting_mode = M_WAITING_ROOMS; + hosting = ( currentMenu->prevMenu == &MP_ServerDef ); + } + I_unlock_mutex(m_menu_mutex); + + GetRoomsList(hosting, *id); + } + } + else + { + I_lock_mutex(&ms_QueryId_mutex); + { + ok = ( *id == ms_QueryId ); + } + I_unlock_mutex(ms_QueryId_mutex); + } + + if (ok) + { + I_lock_mutex(&m_menu_mutex); + { + if (m_waiting_mode) + { + m_waiting_mode = M_NOT_WAITING; + MP_RoomMenu[0].text = ""; + } + } + I_unlock_mutex(m_menu_mutex); + } + + free(id); +} +#endif/*HAVE_THREADS*/ +#endif/*UPDATE_ALERT*/ static void M_ConnectMenu(INT32 choice) { @@ -8456,11 +8606,14 @@ static void M_ConnectMenuModChecks(INT32 choice) M_ConnectMenu(-1); } -static UINT32 roomIds[NUM_LIST_ROOMS]; +UINT32 roomIds[NUM_LIST_ROOMS]; static void M_RoomMenu(INT32 choice) { INT32 i; +#ifdef HAVE_THREADS + int *id; +#endif (void)choice; @@ -8473,34 +8626,47 @@ static void M_RoomMenu(INT32 choice) if (rendermode == render_soft) I_FinishUpdate(); // page flip or blit buffer - if (GetRoomsList(currentMenu == &MP_ServerDef) < 0) - return; - -#ifdef UPDATE_ALERT - if (!M_CheckMODVersion()) - return; -#endif - for (i = 1; i < NUM_LIST_ROOMS+1; ++i) MP_RoomMenu[i].status = IT_DISABLED; memset(roomIds, 0, sizeof(roomIds)); - for (i = 0; room_list[i].header.buffer[0]; i++) - { - if(*room_list[i].name != '\0') - { - MP_RoomMenu[i+1].text = room_list[i].name; - roomIds[i] = room_list[i].id; - MP_RoomMenu[i+1].status = IT_STRING|IT_CALL; - } - } - MP_RoomDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_RoomDef); + +#ifdef UPDATE_ALERT +#ifdef HAVE_THREADS + m_waiting_mode = M_WAITING_VERSION; + MP_RoomMenu[0].text = ""; + + id = malloc(sizeof *id); + + I_lock_mutex(&ms_QueryId_mutex); + { + *id = ms_QueryId; + } + I_unlock_mutex(ms_QueryId_mutex); + + I_spawn_thread("check-new-version", + (I_thread_fn)Check_new_version_thread, id); +#else/*HAVE_THREADS*/ + if (M_CheckMODVersion(0)) + { + GetRoomsList(currentMenu->prevMenu == &MP_ServerDef, 0); + } +#endif/*HAVE_THREADS*/ +#endif/*UPDATE_ALERT*/ } static void M_ChooseRoom(INT32 choice) { +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); + { + ms_QueryId++; + } + I_unlock_mutex(ms_QueryId_mutex); +#endif + if (choice == 0) ms_RoomId = -1; else diff --git a/src/m_menu.h b/src/m_menu.h index 62c852e4d..b00557cc9 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -17,6 +17,8 @@ #include "d_event.h" #include "command.h" +#include "i_threads.h" +#include "mserv.h" #include "r_things.h" // for SKINNAMESIZE // @@ -66,6 +68,18 @@ typedef enum } menumessagetype_t; void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); +typedef enum +{ + M_NOT_WAITING, + + M_WAITING_VERSION, + M_WAITING_ROOMS, + M_WAITING_SERVERS, +} +M_waiting_mode_t; + +extern M_waiting_mode_t m_waiting_mode; + // Called by linux_x/i_video_xshm.c void M_QuitResponse(INT32 ch); @@ -156,6 +170,9 @@ typedef struct menuitem_s extern menuitem_t PlayerMenu[MAXSKINS]; +extern menuitem_t MP_RoomMenu[]; +extern UINT32 roomIds[NUM_LIST_ROOMS]; + typedef struct menu_s { const char *menutitlepic; @@ -171,6 +188,10 @@ typedef struct menu_s void M_SetupNextMenu(menu_t *menudef); void M_ClearMenus(boolean callexitmenufunc); +#ifdef HAVE_THREADS +extern I_mutex m_menu_mutex; +#endif + extern menu_t *currentMenu; extern menu_t MainDef; diff --git a/src/mserv.c b/src/mserv.c index d227d4895..d6641a2aa 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -18,6 +18,7 @@ #include "doomstat.h" #include "doomdef.h" #include "command.h" +#include "i_threads.h" #include "mserv.h" #include "m_menu.h" #include "z_zone.h" @@ -46,6 +47,14 @@ consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SA char *ms_API; INT16 ms_RoomId = -1; +#ifdef HAVE_THREADS +int ms_QueryId; +I_mutex ms_QueryId_mutex; + +msg_server_t *ms_ServerList; +I_mutex ms_ServerList_mutex; +#endif + static enum { MSCS_NONE, MSCS_WAITING, MSCS_REGISTERED, MSCS_FAILED } con_state = MSCS_NONE; UINT16 current_port = 0; @@ -72,26 +81,36 @@ void AddMServCommands(void) static void WarnGUI (void) { +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n\nCheck the console for details.\n"), NULL, MM_NOTHING); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif } #define NUM_LIST_SERVER MAXSERVERLIST -const msg_server_t *GetShortServersList(INT32 room) +msg_server_t *GetShortServersList(INT32 room, int id) { - static msg_server_t server_list[NUM_LIST_SERVER+1]; // +1 for easy test + msg_server_t *server_list; - if (HMS_fetch_servers(server_list, room)) + // +1 for easy test + server_list = malloc(( NUM_LIST_SERVER + 1 ) * sizeof *server_list); + + if (HMS_fetch_servers(server_list, room, id)) return server_list; else { + free(server_list); WarnGUI(); return NULL; } } -INT32 GetRoomsList(boolean hosting) +INT32 GetRoomsList(boolean hosting, int id) { - if (HMS_fetch_rooms( ! hosting )) + if (HMS_fetch_rooms( ! hosting, id)) return 1; else { @@ -101,17 +120,32 @@ INT32 GetRoomsList(boolean hosting) } #ifdef UPDATE_ALERT -const char *GetMODVersion(void) +char *GetMODVersion(int id) { - static char buffer[16]; + char *buffer; int c; - c = HMS_compare_mod_version(buffer, sizeof buffer); + (void)id; + + buffer = malloc(16); + + c = HMS_compare_mod_version(buffer, 16); + +#ifdef HAVE_THREADS + I_lock_mutex(&ms_QueryId_mutex); + { + if (id != ms_QueryId) + c = -1; + } + I_unlock_mutex(ms_QueryId_mutex); +#endif if (c > 0) return buffer; else { + free(buffer); + if (! c) WarnGUI(); diff --git a/src/mserv.h b/src/mserv.h index 0f954aead..6f61719aa 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -14,6 +14,8 @@ #ifndef _MSERV_H_ #define _MSERV_H_ +#include "i_threads.h" + #define HMS123311 // don't mess with nights, man // lowered from 32 due to menu changes @@ -75,15 +77,23 @@ extern char *ms_API; // anything else is whatever room the MS assigns to that number (online mode) extern INT16 ms_RoomId; +#ifdef HAVE_THREADS +extern int ms_QueryId; +extern I_mutex ms_QueryId_mutex; + +extern msg_server_t *ms_ServerList; +extern I_mutex ms_ServerList_mutex; +#endif + void RegisterServer(void); void UnregisterServer(void); void MasterClient_Ticker(void); -const msg_server_t *GetShortServersList(INT32 room); -INT32 GetRoomsList(boolean hosting); +msg_server_t *GetShortServersList(INT32 room, int id); +INT32 GetRoomsList(boolean hosting, int id); #ifdef UPDATE_ALERT -const char *GetMODVersion(void); +char *GetMODVersion(int id); void GetMODVersion_Console(void); #endif extern msg_rooms_t room_list[NUM_LIST_ROOMS+1]; @@ -92,12 +102,12 @@ void AddMServCommands(void); /* HTTP */ int HMS_in_use (void); -int HMS_fetch_rooms (int joining); +int HMS_fetch_rooms (int joining, int id); int HMS_register (void); void HMS_unlist (void); int HMS_update (void); void HMS_list_servers (void); -msg_server_t * HMS_fetch_servers (msg_server_t *list, int room); +msg_server_t * HMS_fetch_servers (msg_server_t *list, int room, int id); int HMS_compare_mod_version (char *buffer, size_t size_of_buffer); #endif From 28e51c2d9e1782d43377530c9b92af9fac68a6fc Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 7 Apr 2020 01:48:43 -0400 Subject: [PATCH 057/211] Support for http downloads (cherry picked from commit 5e0eca9fe9b25fdddb9cdcd4e90ae5b0d5ccf86f) (cherry picked just the libcurl stuff) --- .travis.yml | 19 + debian-template/control | 1 + libs/curl/include/curl/curl.h | 1549 +++++++++++++++++------- libs/curl/include/curl/curlver.h | 34 +- libs/curl/include/curl/easy.h | 20 +- libs/curl/include/curl/mprintf.h | 43 +- libs/curl/include/curl/multi.h | 163 ++- libs/curl/include/curl/stdcheaders.h | 14 +- libs/curl/include/curl/system.h | 504 ++++++++ libs/curl/include/curl/typecheck-gcc.h | 823 +++++++------ libs/curl/include/curl/urlapi.h | 125 ++ libs/curl/lib32/libcurl.a | Bin 385996 -> 801608 bytes libs/curl/lib32/libcurl.dll | Bin 0 -> 725504 bytes libs/curl/lib32/libcurl.dll.a | Bin 0 -> 151856 bytes libs/curl/lib64/libcurl-x64.dll | Bin 0 -> 665600 bytes libs/curl/lib64/libcurl.a | Bin 380222 -> 839330 bytes libs/curl/lib64/libcurl.dll.a | Bin 0 -> 149258 bytes src/CMakeLists.txt | 22 + src/Makefile | 11 +- src/sdl/CMakeLists.txt | 3 + src/win32/Makefile.cfg | 9 + 21 files changed, 2484 insertions(+), 856 deletions(-) create mode 100644 libs/curl/include/curl/system.h create mode 100644 libs/curl/include/curl/urlapi.h create mode 100644 libs/curl/lib32/libcurl.dll create mode 100644 libs/curl/lib32/libcurl.dll.a create mode 100644 libs/curl/lib64/libcurl-x64.dll create mode 100644 libs/curl/lib64/libcurl.dll.a diff --git a/.travis.yml b/.travis.yml index 6d2e8cddf..4959e4d2d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.4 compiler: gcc-4.4 @@ -37,6 +38,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.6 compiler: gcc-4.6 @@ -51,6 +53,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.7 compiler: gcc-4.7 @@ -72,6 +75,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.8 compiler: gcc-4.8 @@ -88,6 +92,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-7 compiler: gcc-7 @@ -104,6 +109,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-8 compiler: gcc-8 @@ -124,6 +130,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - clang-3.5 compiler: clang-3.5 @@ -140,6 +147,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - clang-3.6 compiler: clang-3.6 @@ -156,6 +164,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - clang-3.7 compiler: clang-3.7 @@ -172,6 +181,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - clang-3.8 compiler: clang-3.8 @@ -188,6 +198,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - clang-3.9 compiler: clang-3.9 @@ -294,6 +305,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.8 compiler: gcc-4.8 @@ -435,6 +447,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.8 compiler: gcc-4.8 @@ -458,6 +471,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.8 compiler: gcc-4.8 @@ -481,6 +495,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.8 compiler: gcc-4.8 @@ -504,6 +519,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.8 compiler: gcc-4.8 @@ -527,6 +543,7 @@ matrix: - libpng-dev - libgl1-mesa-dev - libgme-dev + - libcurl4-openssl-dev - p7zip-full - gcc-4.8 compiler: gcc-4.8 @@ -567,6 +584,7 @@ addons: - libgl1-mesa-dev - libgme-dev - zlib1g-dev + - libcurl4-openssl-dev - p7zip-full homebrew: taps: @@ -576,6 +594,7 @@ addons: - game-music-emu - p7zip - cmake + - curl update: true diff --git a/debian-template/control b/debian-template/control index e1348d704..6614d3159 100644 --- a/debian-template/control +++ b/debian-template/control @@ -10,6 +10,7 @@ Build-Depends: debhelper (>= 7.0.50~), libpng-dev | libpng16-dev | libpng12-dev (>= 1.2.7), zlib1g-dev, libgme-dev, + libcurl4-openssl-dev, libglu1-dev | libglu-dev, libosmesa6-dev | libgl-dev, nasm [i386] diff --git a/libs/curl/include/curl/curl.h b/libs/curl/include/curl/curl.h index ee9754da8..b7cb30a58 100644 --- a/libs/curl/include/curl/curl.h +++ b/libs/curl/include/curl/curl.h @@ -1,5 +1,5 @@ -#ifndef __CURL_CURL_H -#define __CURL_CURL_H +#ifndef CURLINC_CURL_H +#define CURLINC_CURL_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -24,23 +24,26 @@ /* * If you have libcurl problems, all docs and details are found here: - * http://curl.haxx.se/libcurl/ + * https://curl.haxx.se/libcurl/ * * curl-library mailing list subscription and unsubscription web interface: - * http://cool.haxx.se/mailman/listinfo/curl-library/ + * https://cool.haxx.se/mailman/listinfo/curl-library/ */ +#ifdef CURL_NO_OLDIES +#define CURL_STRICTER +#endif + #include "curlver.h" /* libcurl version defines */ -#include "curlbuild.h" /* libcurl build definitions */ -#include "curlrules.h" /* libcurl rules enforcement */ +#include "system.h" /* determine things run-time */ /* - * Define WIN32 when build target is Win32 API + * Define CURL_WIN32 when build target is Win32 API */ -#if (defined(_WIN32) || defined(__WIN32__)) && \ - !defined(WIN32) && !defined(__SYMBIAN32__) -#define WIN32 +#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) && \ + !defined(__SYMBIAN32__) +#define CURL_WIN32 #endif #include @@ -55,74 +58,79 @@ #include #include -#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__GNUC__) && \ - !defined(__CYGWIN__) || defined(__MINGW32__) -#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H)) +#if defined(CURL_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ + defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) /* The check above prevents the winsock2 inclusion if winsock.h already was included, since they can't co-exist without problems */ #include #include #endif -#else +#endif /* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish - libc5-based Linux systems. Only include it on system that are known to + libc5-based Linux systems. Only include it on systems that are known to require it! */ #if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ - defined(ANDROID) || \ + defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \ + defined(__CYGWIN__) || \ (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) #include #endif -#ifndef _WIN32_WCE +#if !defined(CURL_WIN32) && !defined(_WIN32_WCE) #include #endif -#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) + +#if !defined(CURL_WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) #include #endif -#include -#endif #ifdef __BEOS__ #include #endif +/* Compatibility for non-Clang compilers */ +#ifndef __has_declspec_attribute +# define __has_declspec_attribute(x) 0 +#endif + #ifdef __cplusplus extern "C" { #endif +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_easy CURL; +typedef struct Curl_share CURLSH; +#else typedef void CURL; +typedef void CURLSH; +#endif /* - * Decorate exportable functions for Win32 and Symbian OS DLL linking. - * This avoids using a .def file for building libcurl.dll. + * libcurl external API function linkage decorations. */ -#if (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)) && \ - !defined(CURL_STATICLIB) -#if defined(BUILDING_LIBCURL) -#define CURL_EXTERN __declspec(dllexport) -#else -#define CURL_EXTERN __declspec(dllimport) -#endif -#else -#ifdef CURL_HIDDEN_SYMBOLS -/* - * This definition is used to make external definitions visible in the - * shared library when symbols are hidden by default. It makes no - * difference when compiling applications whether this is set or not, - * only when compiling the library. - */ -#define CURL_EXTERN CURL_EXTERN_SYMBOL +#ifdef CURL_STATICLIB +# define CURL_EXTERN +#elif defined(CURL_WIN32) || defined(__SYMBIAN32__) || \ + (__has_declspec_attribute(dllexport) && \ + __has_declspec_attribute(dllimport)) +# if defined(BUILDING_LIBCURL) +# define CURL_EXTERN __declspec(dllexport) +# else +# define CURL_EXTERN __declspec(dllimport) +# endif +#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS) +# define CURL_EXTERN CURL_EXTERN_SYMBOL #else -#define CURL_EXTERN -#endif +# define CURL_EXTERN #endif #ifndef curl_socket_typedef /* socket typedef */ -#ifdef WIN32 +#if defined(CURL_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) typedef SOCKET curl_socket_t; #define CURL_SOCKET_BAD INVALID_SOCKET #else @@ -132,46 +140,103 @@ typedef int curl_socket_t; #define curl_socket_typedef #endif /* curl_socket_typedef */ +/* enum for the different supported SSL backends */ +typedef enum { + CURLSSLBACKEND_NONE = 0, + CURLSSLBACKEND_OPENSSL = 1, + CURLSSLBACKEND_GNUTLS = 2, + CURLSSLBACKEND_NSS = 3, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ + CURLSSLBACKEND_GSKIT = 5, + CURLSSLBACKEND_POLARSSL = 6, + CURLSSLBACKEND_WOLFSSL = 7, + CURLSSLBACKEND_SCHANNEL = 8, + CURLSSLBACKEND_SECURETRANSPORT = 9, + CURLSSLBACKEND_AXTLS = 10, /* never used since 7.63.0 */ + CURLSSLBACKEND_MBEDTLS = 11, + CURLSSLBACKEND_MESALINK = 12, + CURLSSLBACKEND_BEARSSL = 13 +} curl_sslbackend; + +/* aliases for library clones and renames */ +#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL +#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL + +/* deprecated names: */ +#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL +#define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT + struct curl_httppost { struct curl_httppost *next; /* next entry in the list */ char *name; /* pointer to allocated name */ long namelength; /* length of name length */ char *contents; /* pointer to allocated data contents */ - long contentslength; /* length of contents field */ + long contentslength; /* length of contents field, see also + CURL_HTTPPOST_LARGE */ char *buffer; /* pointer to allocated buffer contents */ long bufferlength; /* length of buffer field */ char *contenttype; /* Content-Type */ - struct curl_slist* contentheader; /* list of extra headers for this form */ + struct curl_slist *contentheader; /* list of extra headers for this form */ struct curl_httppost *more; /* if one field name has more than one file, this link should link to following files */ long flags; /* as defined below */ -#define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */ -#define HTTPPOST_READFILE (1<<1) /* specified content is a file name */ -#define HTTPPOST_PTRNAME (1<<2) /* name is only stored pointer - do not free in formfree */ -#define HTTPPOST_PTRCONTENTS (1<<3) /* contents is only stored pointer - do not free in formfree */ -#define HTTPPOST_BUFFER (1<<4) /* upload file from buffer */ -#define HTTPPOST_PTRBUFFER (1<<5) /* upload file from pointer contents */ -#define HTTPPOST_CALLBACK (1<<6) /* upload file contents by using the - regular read callback to get the data - and pass the given pointer as custom - pointer */ + +/* specified content is a file name */ +#define CURL_HTTPPOST_FILENAME (1<<0) +/* specified content is a file name */ +#define CURL_HTTPPOST_READFILE (1<<1) +/* name is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRNAME (1<<2) +/* contents is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRCONTENTS (1<<3) +/* upload file from buffer */ +#define CURL_HTTPPOST_BUFFER (1<<4) +/* upload file from pointer contents */ +#define CURL_HTTPPOST_PTRBUFFER (1<<5) +/* upload file contents by using the regular read callback to get the data and + pass the given pointer as custom pointer */ +#define CURL_HTTPPOST_CALLBACK (1<<6) +/* use size in 'contentlen', added in 7.46.0 */ +#define CURL_HTTPPOST_LARGE (1<<7) char *showfilename; /* The file name to show. If not set, the actual file name will be used (if this is a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ + curl_off_t contentlen; /* alternative length of contents + field. Used if CURL_HTTPPOST_LARGE is + set. Added in 7.46.0 */ }; + +/* This is a return code for the progress callback that, when returned, will + signal libcurl to continue executing the default progress function */ +#define CURL_PROGRESSFUNC_CONTINUE 0x10000001 + +/* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now + considered deprecated but was the only choice up until 7.31.0 */ typedef int (*curl_progress_callback)(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); +/* This is the CURLOPT_XFERINFOFUNCTION callback prototype. It was introduced + in 7.32.0, avoids the use of floating point numbers and provides more + detailed information. */ +typedef int (*curl_xferinfo_callback)(void *clientp, + curl_off_t dltotal, + curl_off_t dlnow, + curl_off_t ultotal, + curl_off_t ulnow); + +#ifndef CURL_MAX_READ_SIZE + /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ +#define CURL_MAX_READ_SIZE 524288 +#endif + #ifndef CURL_MAX_WRITE_SIZE /* Tests have proven that 20K is a very bad buffer size for uploads on Windows, while 16K for some odd reason performed a lot better. @@ -189,16 +254,18 @@ typedef int (*curl_progress_callback)(void *clientp, #define CURL_MAX_HTTP_HEADER (100*1024) #endif - /* This is a magic return code for the write callback that, when returned, will signal libcurl to pause receiving on the current transfer. */ #define CURL_WRITEFUNC_PAUSE 0x10000001 + typedef size_t (*curl_write_callback)(char *buffer, size_t size, size_t nitems, void *outstream); - +/* This callback will be called when a new resolver request is made */ +typedef int (*curl_resolver_start_callback)(void *resolver_state, + void *reserved, void *userdata); /* enumeration of file types */ typedef enum { @@ -223,14 +290,11 @@ typedef enum { #define CURLFINFOFLAG_KNOWN_SIZE (1<<6) #define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) -/* Content of this structure depends on information which is known and is - achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man - page for callbacks returning this structure -- some fields are mandatory, - some others are optional. The FLAG field has special meaning. */ +/* Information about a single file, used when doing FTP wildcard matching */ struct curl_fileinfo { char *filename; curlfiletype filetype; - time_t time; + time_t time; /* always zero! */ unsigned int perm; int uid; int gid; @@ -249,7 +313,7 @@ struct curl_fileinfo { unsigned int flags; /* used internally */ - char * b_data; + char *b_data; size_t b_size; size_t b_used; }; @@ -305,14 +369,25 @@ typedef int (*curl_seek_callback)(void *instream, signal libcurl to pause sending data on the current transfer. */ #define CURL_READFUNC_PAUSE 0x10000001 +/* Return code for when the trailing headers' callback has terminated + without any errors*/ +#define CURL_TRAILERFUNC_OK 0 +/* Return code for when was an error in the trailing header's list and we + want to abort the request */ +#define CURL_TRAILERFUNC_ABORT 1 + typedef size_t (*curl_read_callback)(char *buffer, size_t size, size_t nitems, void *instream); -typedef enum { - CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ - CURLSOCKTYPE_LAST /* never use */ +typedef int (*curl_trailer_callback)(struct curl_slist **list, + void *userdata); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ + CURLSOCKTYPE_LAST /* never use */ } curlsocktype; /* The return code from the sockopt_callback can signal information back @@ -341,6 +416,9 @@ typedef curl_socket_t curlsocktype purpose, struct curl_sockaddr *address); +typedef int +(*curl_closesocket_callback)(void *clientp, curl_socket_t item); + typedef enum { CURLIOE_OK, /* I/O operation successful */ CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ @@ -348,7 +426,7 @@ typedef enum { CURLIOE_LAST /* never use */ } curlioerr; -typedef enum { +typedef enum { CURLIOCMD_NOP, /* no operation */ CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ CURLIOCMD_LAST /* never use */ @@ -358,6 +436,7 @@ typedef curlioerr (*curl_ioctl_callback)(CURL *handle, int cmd, void *clientp); +#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* * The following typedef's are signatures of malloc, free, realloc, strdup and * calloc respectively. Function pointers of these types can be passed to the @@ -370,6 +449,9 @@ typedef void *(*curl_realloc_callback)(void *ptr, size_t size); typedef char *(*curl_strdup_callback)(const char *str); typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); +#define CURL_DID_MEMORY_FUNC_TYPEDEFS +#endif + /* the kind of data that is passed to information_callback*/ typedef enum { CURLINFO_TEXT = 0, @@ -406,17 +488,22 @@ typedef enum { CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ CURLE_COULDNT_RESOLVE_HOST, /* 6 */ CURLE_COULDNT_CONNECT, /* 7 */ - CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_WEIRD_SERVER_REPLY, /* 8 */ CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server due to lack of access - when login fails this is not returned. */ - CURLE_OBSOLETE10, /* 10 - NOT USED */ + CURLE_FTP_ACCEPT_FAILED, /* 10 - [was obsoleted in April 2006 for + 7.15.4, reused in Dec 2011 for 7.24.0]*/ CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ - CURLE_OBSOLETE12, /* 12 - NOT USED */ + CURLE_FTP_ACCEPT_TIMEOUT, /* 12 - timeout occurred accepting server + [was obsoleted in August 2007 for 7.17.0, + reused in Dec 2011 for 7.24.0]*/ CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ CURLE_FTP_CANT_GET_HOST, /* 15 */ - CURLE_OBSOLETE16, /* 16 - NOT USED */ + CURLE_HTTP2, /* 16 - A problem in the http2 framing layer. + [was obsoleted in August 2007 for 7.17.0, + reused in July 2014 for 7.38.0] */ CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ CURLE_PARTIAL_FILE, /* 18 */ CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ @@ -445,18 +532,17 @@ typedef enum { CURLE_LDAP_CANNOT_BIND, /* 38 */ CURLE_LDAP_SEARCH_FAILED, /* 39 */ CURLE_OBSOLETE40, /* 40 - NOT USED */ - CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */ CURLE_ABORTED_BY_CALLBACK, /* 42 */ CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ CURLE_OBSOLETE44, /* 44 - NOT USED */ CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ CURLE_OBSOLETE46, /* 46 - NOT USED */ - CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_TOO_MANY_REDIRECTS, /* 47 - catch endless re-direct loops */ CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ - CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_TELNET_OPTION_SYNTAX, /* 49 - Malformed telnet option */ CURLE_OBSOLETE50, /* 50 - NOT USED */ - CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint - wasn't verified fine */ + CURLE_OBSOLETE51, /* 51 - NOT USED */ CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as @@ -466,7 +552,8 @@ typedef enum { CURLE_OBSOLETE57, /* 57 - NOT IN USE */ CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ - CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint + wasn't verified fine */ CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized/bad encoding */ CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ @@ -507,18 +594,41 @@ typedef enum { 7.19.0) */ CURLE_FTP_PRET_FAILED, /* 84 - a PRET command failed */ CURLE_RTSP_CSEQ_ERROR, /* 85 - mismatch of RTSP CSeq numbers */ - CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Identifiers */ + CURLE_RTSP_SESSION_ERROR, /* 86 - mismatch of RTSP Session Ids */ CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ - + CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the + session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ + CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer + */ + CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from + inside a callback */ + CURLE_AUTH_ERROR, /* 94 - an authentication function returned an + error */ + CURLE_HTTP3, /* 95 - An HTTP/3 layer problem */ + CURLE_QUIC_CONNECT_ERROR, /* 96 - QUIC connection error */ CURL_LAST /* never use! */ } CURLcode; #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ +/* Previously obsolete error code re-used in 7.38.0 */ +#define CURLE_OBSOLETE16 CURLE_HTTP2 + +/* Previously obsolete error codes re-used in 7.24.0 */ +#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED +#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT + /* compatibility with older names */ #define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING +#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY + +/* The following were added in 7.62.0 */ +#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION /* The following were added in 7.21.5, April 2011 */ #define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION @@ -567,14 +677,26 @@ typedef enum { make programs break */ #define CURLE_ALREADY_COMPLETE 99999 +/* Provide defines for really old option names */ +#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */ +#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */ +#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA + +/* Since long deprecated options with no code in the lib that does anything + with them. */ +#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 +#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 + #endif /*!CURL_NO_OLDIES*/ /* This prototype applies to all conversion callbacks */ typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ - void *ssl_ctx, /* actually an - OpenSSL SSL_CTX */ + void *ssl_ctx, /* actually an OpenSSL + or WolfSSL SSL_CTX, + or an mbedTLS + mbedtls_ssl_config */ void *userptr); typedef enum { @@ -582,6 +704,7 @@ typedef enum { CONNECT HTTP/1.1 */ CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT HTTP/1.0 */ + CURLPROXY_HTTPS = 2, /* added in 7.52.0 */ CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already in 7.10 */ CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ @@ -591,17 +714,39 @@ typedef enum { in 7.18.0 */ } curl_proxytype; /* this enum was added in 7.10 */ -#define CURLAUTH_NONE 0 /* nothing */ -#define CURLAUTH_BASIC (1<<0) /* Basic (default) */ -#define CURLAUTH_DIGEST (1<<1) /* Digest */ -#define CURLAUTH_GSSNEGOTIATE (1<<2) /* GSS-Negotiate */ -#define CURLAUTH_NTLM (1<<3) /* NTLM */ -#define CURLAUTH_DIGEST_IE (1<<4) /* Digest with IE flavour */ -#define CURLAUTH_ONLY (1<<31) /* used together with a single other - type to force no auth or just that - single type */ -#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) /* all fine types set */ -#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) +/* + * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options: + * + * CURLAUTH_NONE - No HTTP authentication + * CURLAUTH_BASIC - HTTP Basic authentication (default) + * CURLAUTH_DIGEST - HTTP Digest authentication + * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication + * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) + * CURLAUTH_NTLM - HTTP NTLM authentication + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper + * CURLAUTH_BEARER - HTTP Bearer token authentication + * CURLAUTH_ONLY - Use together with a single other type to force no + * authentication or just that single type + * CURLAUTH_ANY - All fine types set + * CURLAUTH_ANYSAFE - All fine types except Basic + */ + +#define CURLAUTH_NONE ((unsigned long)0) +#define CURLAUTH_BASIC (((unsigned long)1)<<0) +#define CURLAUTH_DIGEST (((unsigned long)1)<<1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +/* Deprecated since the advent of CURLAUTH_NEGOTIATE */ +#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE +/* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */ +#define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE +#define CURLAUTH_NTLM (((unsigned long)1)<<3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#define CURLAUTH_BEARER (((unsigned long)1)<<6) +#define CURLAUTH_ONLY (((unsigned long)1)<<31) +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) #define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ #define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ @@ -609,20 +754,30 @@ typedef enum { #define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ #define CURLSSH_AUTH_HOST (1<<2) /* host key files */ #define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1<<4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_GSSAPI (1<<5) /* gssapi (kerberos, ...) */ #define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY +#define CURLGSSAPI_DELEGATION_NONE 0 /* no delegation (default) */ +#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */ +#define CURLGSSAPI_DELEGATION_FLAG (1<<1) /* delegate always */ + #define CURL_ERROR_SIZE 256 +enum curl_khtype { + CURLKHTYPE_UNKNOWN, + CURLKHTYPE_RSA1, + CURLKHTYPE_RSA, + CURLKHTYPE_DSS, + CURLKHTYPE_ECDSA, + CURLKHTYPE_ED25519 +}; + struct curl_khkey { const char *key; /* points to a zero-terminated string encoded with base64 if len is zero, otherwise to the "raw" data */ size_t len; - enum type { - CURLKHTYPE_UNKNOWN, - CURLKHTYPE_RSA1, - CURLKHTYPE_RSA, - CURLKHTYPE_DSS - } keytype; + enum curl_khtype keytype; }; /* this is the set of return values expected from the curl_sshkeycallback @@ -661,6 +816,31 @@ typedef enum { CURLUSESSL_LAST /* not an option, never use */ } curl_usessl; +/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */ + +/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the + name of improving interoperability with older servers. Some SSL libraries + have introduced work-arounds for this flaw but those work-arounds sometimes + make the SSL communication fail. To regain functionality with those broken + servers, a user can this way allow the vulnerability back. */ +#define CURLSSLOPT_ALLOW_BEAST (1<<0) + +/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those + SSL backends where such behavior is present. */ +#define CURLSSLOPT_NO_REVOKE (1<<1) + +/* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain + if possible. The OpenSSL backend has this ability. */ +#define CURLSSLOPT_NO_PARTIALCHAIN (1<<2) + +/* The default connection attempt delay in milliseconds for happy eyeballs. + CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document + this value, keep them in sync. */ +#define CURL_HET_DEFAULT 200L + +/* The default connection upkeep interval in milliseconds. */ +#define CURL_UPKEEP_INTERVAL_DEFAULT 60000L + #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ @@ -711,6 +891,18 @@ typedef enum { CURLFTPMETHOD_LAST /* not an option, never use */ } curl_ftpmethod; +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + +/* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */ +#define CURLALTSVC_IMMEDIATELY (1<<0) + +#define CURLALTSVC_READONLYFILE (1<<2) +#define CURLALTSVC_H1 (1<<3) +#define CURLALTSVC_H2 (1<<4) +#define CURLALTSVC_H3 (1<<5) + /* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ #define CURLPROTO_HTTP (1<<0) #define CURLPROTO_HTTPS (1<<1) @@ -738,6 +930,8 @@ typedef enum { #define CURLPROTO_RTMPS (1<<23) #define CURLPROTO_RTMPTS (1<<24) #define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) #define CURLPROTO_ALL (~0) /* enable everything */ /* long may be 32 or 64 bits, but we should never depend on anything else @@ -747,71 +941,61 @@ typedef enum { #define CURLOPTTYPE_FUNCTIONPOINT 20000 #define CURLOPTTYPE_OFF_T 30000 -/* name is uppercase CURLOPT_, - type is one of the defined CURLOPTTYPE_ - number is unique identifier */ -#ifdef CINIT -#undef CINIT -#endif +/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the + string options from the header file */ -#ifdef CURL_ISOCPP -#define CINIT(name,type,number) CURLOPT_ ## name = CURLOPTTYPE_ ## type + number -#else -/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -#define LONG CURLOPTTYPE_LONG -#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT -#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT -#define OFF_T CURLOPTTYPE_OFF_T -#define CINIT(name,type,number) CURLOPT_/**/name = type + number -#endif + +#define CURLOPT(na,t,nu) na = t + nu + +/* handy aliases that make no run-time difference */ +#define CURLOPTTYPE_STRINGPOINT CURLOPTTYPE_OBJECTPOINT +#define CURLOPTTYPE_SLISTPOINT CURLOPTTYPE_OBJECTPOINT /* - * This macro-mania below setups the CURLOPT_[what] enum, to be used with - * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] - * word. + * All CURLOPT_* values. */ typedef enum { /* This is the FILE * or void * the regular output should be written to. */ - CINIT(FILE, OBJECTPOINT, 1), + CURLOPT(CURLOPT_WRITEDATA, CURLOPTTYPE_OBJECTPOINT, 1), /* The full URL to get/put */ - CINIT(URL, OBJECTPOINT, 2), + CURLOPT(CURLOPT_URL, CURLOPTTYPE_STRINGPOINT, 2), /* Port number to connect to, if other than default. */ - CINIT(PORT, LONG, 3), + CURLOPT(CURLOPT_PORT, CURLOPTTYPE_LONG, 3), /* Name of proxy to use. */ - CINIT(PROXY, OBJECTPOINT, 4), + CURLOPT(CURLOPT_PROXY, CURLOPTTYPE_STRINGPOINT, 4), - /* "name:password" to use when fetching. */ - CINIT(USERPWD, OBJECTPOINT, 5), + /* "user:password;options" to use when fetching. */ + CURLOPT(CURLOPT_USERPWD, CURLOPTTYPE_STRINGPOINT, 5), - /* "name:password" to use with proxy. */ - CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + /* "user:password" to use with proxy. */ + CURLOPT(CURLOPT_PROXYUSERPWD, CURLOPTTYPE_STRINGPOINT, 6), /* Range to get, specified as an ASCII string. */ - CINIT(RANGE, OBJECTPOINT, 7), + CURLOPT(CURLOPT_RANGE, CURLOPTTYPE_STRINGPOINT, 7), /* not used */ /* Specified file stream to upload from (use as input): */ - CINIT(INFILE, OBJECTPOINT, 9), + CURLOPT(CURLOPT_READDATA, CURLOPTTYPE_OBJECTPOINT, 9), /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE - * bytes big. If this is not used, error messages go to stderr instead: */ - CINIT(ERRORBUFFER, OBJECTPOINT, 10), + * bytes big. */ + CURLOPT(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10), /* Function that will be called to store the output (instead of fwrite). The * parameters will use fwrite() syntax, make sure to follow them. */ - CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + CURLOPT(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11), /* Function that will be called to read the input (instead of fread). The * parameters will use fread() syntax, make sure to follow them. */ - CINIT(READFUNCTION, FUNCTIONPOINT, 12), + CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12), /* Time-out the read operation after this amount of seconds */ - CINIT(TIMEOUT, LONG, 13), + CURLOPT(CURLOPT_TIMEOUT, CURLOPTTYPE_LONG, 13), /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about * how large the file being sent really is. That allows better error @@ -822,20 +1006,20 @@ typedef enum { * which takes an off_t type, allowing platforms with larger off_t * sizes to handle larger files. See below for INFILESIZE_LARGE. */ - CINIT(INFILESIZE, LONG, 14), + CURLOPT(CURLOPT_INFILESIZE, CURLOPTTYPE_LONG, 14), /* POST static input fields. */ - CINIT(POSTFIELDS, OBJECTPOINT, 15), + CURLOPT(CURLOPT_POSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 15), /* Set the referrer page (needed by some CGIs) */ - CINIT(REFERER, OBJECTPOINT, 16), + CURLOPT(CURLOPT_REFERER, CURLOPTTYPE_STRINGPOINT, 16), /* Set the FTP PORT string (interface name, named or numerical IP address) Use i.e '-' to use default address. */ - CINIT(FTPPORT, OBJECTPOINT, 17), + CURLOPT(CURLOPT_FTPPORT, CURLOPTTYPE_STRINGPOINT, 17), /* Set the User-Agent string (examined by some CGIs) */ - CINIT(USERAGENT, OBJECTPOINT, 18), + CURLOPT(CURLOPT_USERAGENT, CURLOPTTYPE_STRINGPOINT, 18), /* If the download receives less than "low speed limit" bytes/second * during "low speed time" seconds, the operations is aborted. @@ -844,10 +1028,10 @@ typedef enum { */ /* Set the "low speed limit" */ - CINIT(LOW_SPEED_LIMIT, LONG, 19), + CURLOPT(CURLOPT_LOW_SPEED_LIMIT, CURLOPTTYPE_LONG, 19), /* Set the "low speed time" */ - CINIT(LOW_SPEED_TIME, LONG, 20), + CURLOPT(CURLOPT_LOW_SPEED_TIME, CURLOPTTYPE_LONG, 20), /* Set the continuation offset. * @@ -855,47 +1039,48 @@ typedef enum { * off_t types, allowing for large file offsets on platforms which * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. */ - CINIT(RESUME_FROM, LONG, 21), + CURLOPT(CURLOPT_RESUME_FROM, CURLOPTTYPE_LONG, 21), /* Set cookie in request: */ - CINIT(COOKIE, OBJECTPOINT, 22), + CURLOPT(CURLOPT_COOKIE, CURLOPTTYPE_STRINGPOINT, 22), - /* This points to a linked list of headers, struct curl_slist kind */ - CINIT(HTTPHEADER, OBJECTPOINT, 23), + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ + CURLOPT(CURLOPT_HTTPHEADER, CURLOPTTYPE_SLISTPOINT, 23), /* This points to a linked list of post entries, struct curl_httppost */ - CINIT(HTTPPOST, OBJECTPOINT, 24), + CURLOPT(CURLOPT_HTTPPOST, CURLOPTTYPE_OBJECTPOINT, 24), /* name of the file keeping your private SSL-certificate */ - CINIT(SSLCERT, OBJECTPOINT, 25), + CURLOPT(CURLOPT_SSLCERT, CURLOPTTYPE_STRINGPOINT, 25), /* password for the SSL or SSH private key */ - CINIT(KEYPASSWD, OBJECTPOINT, 26), + CURLOPT(CURLOPT_KEYPASSWD, CURLOPTTYPE_STRINGPOINT, 26), /* send TYPE parameter? */ - CINIT(CRLF, LONG, 27), + CURLOPT(CURLOPT_CRLF, CURLOPTTYPE_LONG, 27), /* send linked-list of QUOTE commands */ - CINIT(QUOTE, OBJECTPOINT, 28), + CURLOPT(CURLOPT_QUOTE, CURLOPTTYPE_SLISTPOINT, 28), /* send FILE * or void * to store headers to, if you use a callback it is simply passed to the callback unmodified */ - CINIT(WRITEHEADER, OBJECTPOINT, 29), + CURLOPT(CURLOPT_HEADERDATA, CURLOPTTYPE_OBJECTPOINT, 29), /* point to a file to read the initial cookies from, also enables "cookie awareness" */ - CINIT(COOKIEFILE, OBJECTPOINT, 31), + CURLOPT(CURLOPT_COOKIEFILE, CURLOPTTYPE_STRINGPOINT, 31), /* What version to specifically try to use. See CURL_SSLVERSION defines below. */ - CINIT(SSLVERSION, LONG, 32), + CURLOPT(CURLOPT_SSLVERSION, CURLOPTTYPE_LONG, 32), /* What kind of HTTP time condition to use, see defines */ - CINIT(TIMECONDITION, LONG, 33), + CURLOPT(CURLOPT_TIMECONDITION, CURLOPTTYPE_LONG, 33), /* Time to use with the above condition. Specified in number of seconds since 1 Jan 1970 */ - CINIT(TIMEVALUE, LONG, 34), + CURLOPT(CURLOPT_TIMEVALUE, CURLOPTTYPE_LONG, 34), /* 35 = OBSOLETE */ @@ -903,304 +1088,326 @@ typedef enum { HTTP: DELETE, TRACE and others FTP: to use a different list command */ - CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + CURLOPT(CURLOPT_CUSTOMREQUEST, CURLOPTTYPE_STRINGPOINT, 36), - /* HTTP request, for odd commands like DELETE, TRACE and others */ - CINIT(STDERR, OBJECTPOINT, 37), + /* FILE handle to use instead of stderr */ + CURLOPT(CURLOPT_STDERR, CURLOPTTYPE_OBJECTPOINT, 37), /* 38 is not used */ /* send linked-list of post-transfer QUOTE commands */ - CINIT(POSTQUOTE, OBJECTPOINT, 39), + CURLOPT(CURLOPT_POSTQUOTE, CURLOPTTYPE_SLISTPOINT, 39), - /* Pass a pointer to string of the output using full variable-replacement - as described elsewhere. */ - CINIT(WRITEINFO, OBJECTPOINT, 40), + /* OBSOLETE, do not use! */ + CURLOPT(CURLOPT_OBSOLETE40, CURLOPTTYPE_OBJECTPOINT, 40), - CINIT(VERBOSE, LONG, 41), /* talk a lot */ - CINIT(HEADER, LONG, 42), /* throw the header out too */ - CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ - CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ - CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */ - CINIT(UPLOAD, LONG, 46), /* this is an upload */ - CINIT(POST, LONG, 47), /* HTTP POST method */ - CINIT(DIRLISTONLY, LONG, 48), /* return bare names when listing directories */ + /* talk a lot */ + CURLOPT(CURLOPT_VERBOSE, CURLOPTTYPE_LONG, 41), - CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + /* throw the header out too */ + CURLOPT(CURLOPT_HEADER, CURLOPTTYPE_LONG, 42), + + /* shut off the progress meter */ + CURLOPT(CURLOPT_NOPROGRESS, CURLOPTTYPE_LONG, 43), + + /* use HEAD to get http document */ + CURLOPT(CURLOPT_NOBODY, CURLOPTTYPE_LONG, 44), + + /* no output on http error codes >= 400 */ + CURLOPT(CURLOPT_FAILONERROR, CURLOPTTYPE_LONG, 45), + + /* this is an upload */ + CURLOPT(CURLOPT_UPLOAD, CURLOPTTYPE_LONG, 46), + + /* HTTP POST method */ + CURLOPT(CURLOPT_POST, CURLOPTTYPE_LONG, 47), + + /* bare names when listing directories */ + CURLOPT(CURLOPT_DIRLISTONLY, CURLOPTTYPE_LONG, 48), + + /* Append instead of overwrite on upload! */ + CURLOPT(CURLOPT_APPEND, CURLOPTTYPE_LONG, 50), /* Specify whether to read the user+password from the .netrc or the URL. * This must be one of the CURL_NETRC_* enums below. */ - CINIT(NETRC, LONG, 51), + CURLOPT(CURLOPT_NETRC, CURLOPTTYPE_LONG, 51), - CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + /* use Location: Luke! */ + CURLOPT(CURLOPT_FOLLOWLOCATION, CURLOPTTYPE_LONG, 52), - CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ - CINIT(PUT, LONG, 54), /* HTTP PUT */ + /* transfer data in text/ASCII format */ + CURLOPT(CURLOPT_TRANSFERTEXT, CURLOPTTYPE_LONG, 53), + + /* HTTP PUT */ + CURLOPT(CURLOPT_PUT, CURLOPTTYPE_LONG, 54), /* 55 = OBSOLETE */ - /* Function that will be called instead of the internal progress display + /* DEPRECATED + * Function that will be called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ - CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + CURLOPT(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56), - /* Data passed to the progress callback */ - CINIT(PROGRESSDATA, OBJECTPOINT, 57), + /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION + callbacks */ + CURLOPT(CURLOPT_PROGRESSDATA, CURLOPTTYPE_OBJECTPOINT, 57), +#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA /* We want the referrer field set automatically when following locations */ - CINIT(AUTOREFERER, LONG, 58), + CURLOPT(CURLOPT_AUTOREFERER, CURLOPTTYPE_LONG, 58), /* Port of the proxy, can be set in the proxy string as well with: "[host]:[port]" */ - CINIT(PROXYPORT, LONG, 59), + CURLOPT(CURLOPT_PROXYPORT, CURLOPTTYPE_LONG, 59), /* size of the POST input data, if strlen() is not good to use */ - CINIT(POSTFIELDSIZE, LONG, 60), + CURLOPT(CURLOPT_POSTFIELDSIZE, CURLOPTTYPE_LONG, 60), /* tunnel non-http operations through a HTTP proxy */ - CINIT(HTTPPROXYTUNNEL, LONG, 61), + CURLOPT(CURLOPT_HTTPPROXYTUNNEL, CURLOPTTYPE_LONG, 61), /* Set the interface string to use as outgoing network interface */ - CINIT(INTERFACE, OBJECTPOINT, 62), + CURLOPT(CURLOPT_INTERFACE, CURLOPTTYPE_STRINGPOINT, 62), /* Set the krb4/5 security level, this also enables krb4/5 awareness. This * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string * is set but doesn't match one of these, 'private' will be used. */ - CINIT(KRBLEVEL, OBJECTPOINT, 63), + CURLOPT(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63), /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ - CINIT(SSL_VERIFYPEER, LONG, 64), + CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), /* The CApath or CAfile used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - CINIT(CAINFO, OBJECTPOINT, 65), + CURLOPT(CURLOPT_CAINFO, CURLOPTTYPE_STRINGPOINT, 65), /* 66 = OBSOLETE */ /* 67 = OBSOLETE */ /* Maximum number of http redirects to follow */ - CINIT(MAXREDIRS, LONG, 68), + CURLOPT(CURLOPT_MAXREDIRS, CURLOPTTYPE_LONG, 68), /* Pass a long set to 1 to get the date of the requested document (if possible)! Pass a zero to shut it off. */ - CINIT(FILETIME, LONG, 69), + CURLOPT(CURLOPT_FILETIME, CURLOPTTYPE_LONG, 69), /* This points to a linked list of telnet options */ - CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + CURLOPT(CURLOPT_TELNETOPTIONS, CURLOPTTYPE_SLISTPOINT, 70), /* Max amount of cached alive connections */ - CINIT(MAXCONNECTS, LONG, 71), + CURLOPT(CURLOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 71), - /* What policy to use when closing connections when the cache is filled - up */ - CINIT(CLOSEPOLICY, LONG, 72), + /* OBSOLETE, do not use! */ + CURLOPT(CURLOPT_OBSOLETE72, CURLOPTTYPE_LONG, 72), /* 73 = OBSOLETE */ /* Set to explicitly use a new connection for the upcoming transfer. Do not use this unless you're absolutely sure of this, as it makes the operation slower and is less friendly for the network. */ - CINIT(FRESH_CONNECT, LONG, 74), + CURLOPT(CURLOPT_FRESH_CONNECT, CURLOPTTYPE_LONG, 74), /* Set to explicitly forbid the upcoming transfer's connection to be re-used when done. Do not use this unless you're absolutely sure of this, as it makes the operation slower and is less friendly for the network. */ - CINIT(FORBID_REUSE, LONG, 75), + CURLOPT(CURLOPT_FORBID_REUSE, CURLOPTTYPE_LONG, 75), /* Set to a file name that contains random data for libcurl to use to seed the random engine when doing SSL connects. */ - CINIT(RANDOM_FILE, OBJECTPOINT, 76), + CURLOPT(CURLOPT_RANDOM_FILE, CURLOPTTYPE_STRINGPOINT, 76), /* Set to the Entropy Gathering Daemon socket pathname */ - CINIT(EGDSOCKET, OBJECTPOINT, 77), + CURLOPT(CURLOPT_EGDSOCKET, CURLOPTTYPE_STRINGPOINT, 77), - /* Time-out connect operations after this amount of seconds, if connects - are OK within this time, then fine... This only aborts the connect - phase. [Only works on unix-style/SIGALRM operating systems] */ - CINIT(CONNECTTIMEOUT, LONG, 78), + /* Time-out connect operations after this amount of seconds, if connects are + OK within this time, then fine... This only aborts the connect phase. */ + CURLOPT(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78), /* Function that will be called to store headers (instead of fwrite). The * parameters will use fwrite() syntax, make sure to follow them. */ - CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79), /* Set this to force the HTTP request to get back to GET. Only really usable if POST, PUT or a custom request have been used first. */ - CINIT(HTTPGET, LONG, 80), + CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80), /* Set if we should verify the Common name from the peer certificate in ssl * handshake, set 1 to check existence, 2 to ensure that it matches the * provided hostname. */ - CINIT(SSL_VERIFYHOST, LONG, 81), + CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), /* Specify which file name to write all known cookies in after completed operation. Set file name to "-" (dash) to make it go to stdout. */ - CINIT(COOKIEJAR, OBJECTPOINT, 82), + CURLOPT(CURLOPT_COOKIEJAR, CURLOPTTYPE_STRINGPOINT, 82), /* Specify which SSL ciphers to use */ - CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + CURLOPT(CURLOPT_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 83), /* Specify which HTTP version to use! This must be set to one of the CURL_HTTP_VERSION* enums set below. */ - CINIT(HTTP_VERSION, LONG, 84), + CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_LONG, 84), /* Specifically switch on or off the FTP engine's use of the EPSV command. By default, that one will always be attempted before the more traditional PASV command. */ - CINIT(FTP_USE_EPSV, LONG, 85), + CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85), /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ - CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + CURLOPT(CURLOPT_SSLCERTTYPE, CURLOPTTYPE_STRINGPOINT, 86), /* name of the file keeping your private SSL-key */ - CINIT(SSLKEY, OBJECTPOINT, 87), + CURLOPT(CURLOPT_SSLKEY, CURLOPTTYPE_STRINGPOINT, 87), /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ - CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + CURLOPT(CURLOPT_SSLKEYTYPE, CURLOPTTYPE_STRINGPOINT, 88), /* crypto engine for the SSL-sub system */ - CINIT(SSLENGINE, OBJECTPOINT, 89), + CURLOPT(CURLOPT_SSLENGINE, CURLOPTTYPE_STRINGPOINT, 89), /* set the crypto engine for the SSL-sub system as default the param has no meaning... */ - CINIT(SSLENGINE_DEFAULT, LONG, 90), + CURLOPT(CURLOPT_SSLENGINE_DEFAULT, CURLOPTTYPE_LONG, 90), /* Non-zero value means to use the global dns cache */ - CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* To become OBSOLETE soon */ + /* DEPRECATED, do not use! */ + CURLOPT(CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOPTTYPE_LONG, 91), /* DNS cache timeout */ - CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + CURLOPT(CURLOPT_DNS_CACHE_TIMEOUT, CURLOPTTYPE_LONG, 92), /* send linked-list of pre-transfer QUOTE commands */ - CINIT(PREQUOTE, OBJECTPOINT, 93), + CURLOPT(CURLOPT_PREQUOTE, CURLOPTTYPE_SLISTPOINT, 93), /* set the debug function */ - CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + CURLOPT(CURLOPT_DEBUGFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 94), /* set the data for the debug function */ - CINIT(DEBUGDATA, OBJECTPOINT, 95), + CURLOPT(CURLOPT_DEBUGDATA, CURLOPTTYPE_OBJECTPOINT, 95), /* mark this as start of a cookie session */ - CINIT(COOKIESESSION, LONG, 96), + CURLOPT(CURLOPT_COOKIESESSION, CURLOPTTYPE_LONG, 96), /* The CApath directory used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - CINIT(CAPATH, OBJECTPOINT, 97), + CURLOPT(CURLOPT_CAPATH, CURLOPTTYPE_STRINGPOINT, 97), /* Instruct libcurl to use a smaller receive buffer */ - CINIT(BUFFERSIZE, LONG, 98), + CURLOPT(CURLOPT_BUFFERSIZE, CURLOPTTYPE_LONG, 98), /* Instruct libcurl to not use any signal/alarm handlers, even when using timeouts. This option is useful for multi-threaded applications. See libcurl-the-guide for more background information. */ - CINIT(NOSIGNAL, LONG, 99), + CURLOPT(CURLOPT_NOSIGNAL, CURLOPTTYPE_LONG, 99), /* Provide a CURLShare for mutexing non-ts data */ - CINIT(SHARE, OBJECTPOINT, 100), + CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100), /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), - CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ - CINIT(PROXYTYPE, LONG, 101), + CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and + CURLPROXY_SOCKS5. */ + CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_LONG, 101), /* Set the Accept-Encoding string. Use this to tell a server you would like the response to be compressed. Before 7.21.6, this was known as CURLOPT_ENCODING */ - CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102), + CURLOPT(CURLOPT_ACCEPT_ENCODING, CURLOPTTYPE_STRINGPOINT, 102), /* Set pointer to private data */ - CINIT(PRIVATE, OBJECTPOINT, 103), + CURLOPT(CURLOPT_PRIVATE, CURLOPTTYPE_OBJECTPOINT, 103), /* Set aliases for HTTP 200 in the HTTP Response header */ - CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + CURLOPT(CURLOPT_HTTP200ALIASES, CURLOPTTYPE_SLISTPOINT, 104), /* Continue to send authentication (user+password) when following locations, even when hostname changed. This can potentially send off the name and password to whatever host the server decides. */ - CINIT(UNRESTRICTED_AUTH, LONG, 105), + CURLOPT(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105), - /* Specifically switch on or off the FTP engine's use of the EPRT command ( it - also disables the LPRT attempt). By default, those ones will always be + /* Specifically switch on or off the FTP engine's use of the EPRT command ( + it also disables the LPRT attempt). By default, those ones will always be attempted before the good old traditional PORT command. */ - CINIT(FTP_USE_EPRT, LONG, 106), + CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106), /* Set this to a bitmask value to enable the particular authentications methods you like. Use this in combination with CURLOPT_USERPWD. Note that setting multiple bits may cause extra network round-trips. */ - CINIT(HTTPAUTH, LONG, 107), + CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_LONG, 107), - /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx - in second argument. The function must be matching the - curl_ssl_ctx_callback proto. */ - CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + /* Set the ssl context callback function, currently only for OpenSSL or + WolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. + The function must match the curl_ssl_ctx_callback prototype. */ + CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), /* Set the userdata for the ssl context callback function's third argument */ - CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_OBJECTPOINT, 109), /* FTP Option that causes missing dirs to be created on the remote server. In 7.19.4 we introduced the convenience enums for this option using the CURLFTP_CREATE_DIR prefix. */ - CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + CURLOPT(CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPTTYPE_LONG, 110), /* Set this to a bitmask value to enable the particular authentications methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. Note that setting multiple bits may cause extra network round-trips. */ - CINIT(PROXYAUTH, LONG, 111), + CURLOPT(CURLOPT_PROXYAUTH, CURLOPTTYPE_LONG, 111), /* FTP option that changes the timeout, in seconds, associated with getting a response. This is different from transfer timeout time and essentially places a demand on the FTP server to acknowledge commands in a timely manner. */ - CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), + CURLOPT(CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPTTYPE_LONG, 112), #define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to tell libcurl to resolve names to those IP versions only. This only has affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ - CINIT(IPRESOLVE, LONG, 113), + CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_LONG, 113), /* Set this option to limit the size of a file that will be downloaded from an HTTP or FTP server. Note there is also _LARGE version which adds large file support for platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ - CINIT(MAXFILESIZE, LONG, 114), + CURLOPT(CURLOPT_MAXFILESIZE, CURLOPTTYPE_LONG, 114), /* See the comment for INFILESIZE above, but in short, specifies * the size of the file being uploaded. -1 means unknown. */ - CINIT(INFILESIZE_LARGE, OFF_T, 115), + CURLOPT(CURLOPT_INFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 115), - /* Sets the continuation offset. There is also a LONG version of this; - * look above for RESUME_FROM. + /* Sets the continuation offset. There is also a CURLOPTTYPE_LONG version + * of this; look above for RESUME_FROM. */ - CINIT(RESUME_FROM_LARGE, OFF_T, 116), + CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), /* Sets the maximum size of data that will be downloaded from * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. */ - CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), /* Set this option to the file name of your .netrc file you want libcurl to parse (using the CURLOPT_NETRC option). If not set, libcurl will do a poor attempt to find the user's home directory and check for a .netrc file in there. */ - CINIT(NETRC_FILE, OBJECTPOINT, 118), + CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118), /* Enable SSL/TLS for FTP, pick one of: - CURLFTPSSL_TRY - try using SSL, proceed anyway otherwise - CURLFTPSSL_CONTROL - SSL for the control connection or fail - CURLFTPSSL_ALL - SSL for all communication or fail + CURLUSESSL_TRY - try using SSL, proceed anyway otherwise + CURLUSESSL_CONTROL - SSL for the control connection or fail + CURLUSESSL_ALL - SSL for all communication or fail */ - CINIT(USE_SSL, LONG, 119), + CURLOPT(CURLOPT_USE_SSL, CURLOPTTYPE_LONG, 119), /* The _LARGE version of the standard POSTFIELDSIZE option */ - CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + CURLOPT(CURLOPT_POSTFIELDSIZE_LARGE, CURLOPTTYPE_OFF_T, 120), /* Enable/disable the TCP Nagle algorithm */ - CINIT(TCP_NODELAY, LONG, 121), + CURLOPT(CURLOPT_TCP_NODELAY, CURLOPTTYPE_LONG, 121), /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ /* 123 OBSOLETE. Gone in 7.16.0 */ @@ -1220,144 +1427,143 @@ typedef enum { CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL */ - CINIT(FTPSSLAUTH, LONG, 129), + CURLOPT(CURLOPT_FTPSSLAUTH, CURLOPTTYPE_LONG, 129), - CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), - CINIT(IOCTLDATA, OBJECTPOINT, 131), + CURLOPT(CURLOPT_IOCTLFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 130), + CURLOPT(CURLOPT_IOCTLDATA, CURLOPTTYPE_OBJECTPOINT, 131), /* 132 OBSOLETE. Gone in 7.16.0 */ /* 133 OBSOLETE. Gone in 7.16.0 */ /* zero terminated string for pass on to the FTP server when asked for "account" info */ - CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + CURLOPT(CURLOPT_FTP_ACCOUNT, CURLOPTTYPE_STRINGPOINT, 134), - /* feed cookies into cookie engine */ - CINIT(COOKIELIST, OBJECTPOINT, 135), + /* feed cookie into cookie engine */ + CURLOPT(CURLOPT_COOKIELIST, CURLOPTTYPE_STRINGPOINT, 135), /* ignore Content-Length */ - CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + CURLOPT(CURLOPT_IGNORE_CONTENT_LENGTH, CURLOPTTYPE_LONG, 136), /* Set to non-zero to skip the IP address received in a 227 PASV FTP server response. Typically used for FTP-SSL purposes but is not restricted to that. libcurl will then instead use the same IP address it used for the control connection. */ - CINIT(FTP_SKIP_PASV_IP, LONG, 137), + CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137), /* Select "file method" to use when doing FTP, see the curl_ftpmethod above. */ - CINIT(FTP_FILEMETHOD, LONG, 138), + CURLOPT(CURLOPT_FTP_FILEMETHOD, CURLOPTTYPE_LONG, 138), /* Local port number to bind the socket to */ - CINIT(LOCALPORT, LONG, 139), + CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139), /* Number of ports to try, including the first one set with LOCALPORT. Thus, setting it to 1 will make no additional attempts but the first. */ - CINIT(LOCALPORTRANGE, LONG, 140), + CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), /* no transfer, set up connection and let application use the socket by extracting it with CURLINFO_LASTSOCKET */ - CINIT(CONNECT_ONLY, LONG, 141), + CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141), /* Function that will be called to convert from the network encoding (instead of using the iconv calls in libcurl) */ - CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + CURLOPT(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142), /* Function that will be called to convert to the network encoding (instead of using the iconv calls in libcurl) */ - CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + CURLOPT(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143), /* Function that will be called to convert from UTF8 (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ - CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + CURLOPT(CURLOPT_CONV_FROM_UTF8_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 144), /* if the connection proceeds too quickly then need to slow it down */ /* limit-rate: maximum number of bytes per second to send or receive */ - CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), - CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + CURLOPT(CURLOPT_MAX_SEND_SPEED_LARGE, CURLOPTTYPE_OFF_T, 145), + CURLOPT(CURLOPT_MAX_RECV_SPEED_LARGE, CURLOPTTYPE_OFF_T, 146), /* Pointer to command string to send if USER/PASS fails. */ - CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), + CURLOPT(CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPTTYPE_STRINGPOINT, 147), /* callback function for setting socket options */ - CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), - CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + CURLOPT(CURLOPT_SOCKOPTFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 148), + CURLOPT(CURLOPT_SOCKOPTDATA, CURLOPTTYPE_OBJECTPOINT, 149), /* set to 0 to disable session ID re-use for this transfer, default is enabled (== 1) */ - CINIT(SSL_SESSIONID_CACHE, LONG, 150), + CURLOPT(CURLOPT_SSL_SESSIONID_CACHE, CURLOPTTYPE_LONG, 150), /* allowed SSH authentication methods */ - CINIT(SSH_AUTH_TYPES, LONG, 151), + CURLOPT(CURLOPT_SSH_AUTH_TYPES, CURLOPTTYPE_LONG, 151), /* Used by scp/sftp to do public/private key authentication */ - CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), - CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + CURLOPT(CURLOPT_SSH_PUBLIC_KEYFILE, CURLOPTTYPE_STRINGPOINT, 152), + CURLOPT(CURLOPT_SSH_PRIVATE_KEYFILE, CURLOPTTYPE_STRINGPOINT, 153), /* Send CCC (Clear Command Channel) after authentication */ - CINIT(FTP_SSL_CCC, LONG, 154), + CURLOPT(CURLOPT_FTP_SSL_CCC, CURLOPTTYPE_LONG, 154), /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ - CINIT(TIMEOUT_MS, LONG, 155), - CINIT(CONNECTTIMEOUT_MS, LONG, 156), + CURLOPT(CURLOPT_TIMEOUT_MS, CURLOPTTYPE_LONG, 155), + CURLOPT(CURLOPT_CONNECTTIMEOUT_MS, CURLOPTTYPE_LONG, 156), /* set to zero to disable the libcurl's decoding and thus pass the raw body data to the application even when it is encoded/compressed */ - CINIT(HTTP_TRANSFER_DECODING, LONG, 157), - CINIT(HTTP_CONTENT_DECODING, LONG, 158), + CURLOPT(CURLOPT_HTTP_TRANSFER_DECODING, CURLOPTTYPE_LONG, 157), + CURLOPT(CURLOPT_HTTP_CONTENT_DECODING, CURLOPTTYPE_LONG, 158), /* Permission used when creating new files and directories on the remote server for protocols that support it, SFTP/SCP/FILE */ - CINIT(NEW_FILE_PERMS, LONG, 159), - CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + CURLOPT(CURLOPT_NEW_FILE_PERMS, CURLOPTTYPE_LONG, 159), + CURLOPT(CURLOPT_NEW_DIRECTORY_PERMS, CURLOPTTYPE_LONG, 160), /* Set the behaviour of POST when redirecting. Values must be set to one of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ - CINIT(POSTREDIR, LONG, 161), + CURLOPT(CURLOPT_POSTREDIR, CURLOPTTYPE_LONG, 161), /* used by scp/sftp to verify the host's public key */ - CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, CURLOPTTYPE_STRINGPOINT, 162), /* Callback function for opening socket (instead of socket(2)). Optionally, callback is able change the address or refuse to connect returning CURL_SOCKET_BAD. The callback should have type curl_opensocket_callback */ - CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), - CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + CURLOPT(CURLOPT_OPENSOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 163), + CURLOPT(CURLOPT_OPENSOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 164), /* POST volatile input fields. */ - CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + CURLOPT(CURLOPT_COPYPOSTFIELDS, CURLOPTTYPE_OBJECTPOINT, 165), /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ - CINIT(PROXY_TRANSFER_MODE, LONG, 166), + CURLOPT(CURLOPT_PROXY_TRANSFER_MODE, CURLOPTTYPE_LONG, 166), /* Callback function for seeking in the input stream */ - CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), - CINIT(SEEKDATA, OBJECTPOINT, 168), + CURLOPT(CURLOPT_SEEKFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 167), + CURLOPT(CURLOPT_SEEKDATA, CURLOPTTYPE_OBJECTPOINT, 168), /* CRL file */ - CINIT(CRLFILE, OBJECTPOINT, 169), + CURLOPT(CURLOPT_CRLFILE, CURLOPTTYPE_STRINGPOINT, 169), /* Issuer certificate */ - CINIT(ISSUERCERT, OBJECTPOINT, 170), + CURLOPT(CURLOPT_ISSUERCERT, CURLOPTTYPE_STRINGPOINT, 170), /* (IPv6) Address scope */ - CINIT(ADDRESS_SCOPE, LONG, 171), + CURLOPT(CURLOPT_ADDRESS_SCOPE, CURLOPTTYPE_LONG, 171), /* Collect certificate chain info and allow it to get retrievable with - CURLINFO_CERTINFO after the transfer is complete. (Unfortunately) only - working with OpenSSL-powered builds. */ - CINIT(CERTINFO, LONG, 172), + CURLINFO_CERTINFO after the transfer is complete. */ + CURLOPT(CURLOPT_CERTINFO, CURLOPTTYPE_LONG, 172), /* "name" and "pwd" to use when fetching. */ - CINIT(USERNAME, OBJECTPOINT, 173), - CINIT(PASSWORD, OBJECTPOINT, 174), + CURLOPT(CURLOPT_USERNAME, CURLOPTTYPE_STRINGPOINT, 173), + CURLOPT(CURLOPT_PASSWORD, CURLOPTTYPE_STRINGPOINT, 174), /* "name" and "pwd" to use with Proxy when fetching. */ - CINIT(PROXYUSERNAME, OBJECTPOINT, 175), - CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + CURLOPT(CURLOPT_PROXYUSERNAME, CURLOPTTYPE_STRINGPOINT, 175), + CURLOPT(CURLOPT_PROXYPASSWORD, CURLOPTTYPE_STRINGPOINT, 176), /* Comma separated list of hostnames defining no-proxy zones. These should match both hostnames directly, and hostnames within a domain. For @@ -1366,103 +1572,103 @@ typedef enum { implementations of this, .local.com will be considered to be the same as local.com. A single * is the only valid wildcard, and effectively disables the use of proxy. */ - CINIT(NOPROXY, OBJECTPOINT, 177), + CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177), /* block size for TFTP transfers */ - CINIT(TFTP_BLKSIZE, LONG, 178), + CURLOPT(CURLOPT_TFTP_BLKSIZE, CURLOPTTYPE_LONG, 178), /* Socks Service */ - CINIT(SOCKS5_GSSAPI_SERVICE, OBJECTPOINT, 179), + /* DEPRECATED, do not use! */ + CURLOPT(CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOPTTYPE_STRINGPOINT, 179), /* Socks Service */ - CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + CURLOPT(CURLOPT_SOCKS5_GSSAPI_NEC, CURLOPTTYPE_LONG, 180), /* set the bitmask for the protocols that are allowed to be used for the transfer, which thus helps the app which takes URLs from users or other external inputs and want to restrict what protocol(s) to deal with. Defaults to CURLPROTO_ALL. */ - CINIT(PROTOCOLS, LONG, 181), + CURLOPT(CURLOPT_PROTOCOLS, CURLOPTTYPE_LONG, 181), /* set the bitmask for the protocols that libcurl is allowed to follow to, as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. Defaults - to all protocols except FILE and SCP. */ - CINIT(REDIR_PROTOCOLS, LONG, 182), + to be set in both bitmasks to be allowed to get redirected to. */ + CURLOPT(CURLOPT_REDIR_PROTOCOLS, CURLOPTTYPE_LONG, 182), /* set the SSH knownhost file name to use */ - CINIT(SSH_KNOWNHOSTS, OBJECTPOINT, 183), + CURLOPT(CURLOPT_SSH_KNOWNHOSTS, CURLOPTTYPE_STRINGPOINT, 183), /* set the SSH host key callback, must point to a curl_sshkeycallback function */ - CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + CURLOPT(CURLOPT_SSH_KEYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 184), /* set the SSH host key callback custom pointer */ - CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + CURLOPT(CURLOPT_SSH_KEYDATA, CURLOPTTYPE_OBJECTPOINT, 185), /* set the SMTP mail originator */ - CINIT(MAIL_FROM, OBJECTPOINT, 186), + CURLOPT(CURLOPT_MAIL_FROM, CURLOPTTYPE_STRINGPOINT, 186), - /* set the SMTP mail receiver(s) */ - CINIT(MAIL_RCPT, OBJECTPOINT, 187), + /* set the list of SMTP mail receiver(s) */ + CURLOPT(CURLOPT_MAIL_RCPT, CURLOPTTYPE_SLISTPOINT, 187), /* FTP: send PRET before PASV */ - CINIT(FTP_USE_PRET, LONG, 188), + CURLOPT(CURLOPT_FTP_USE_PRET, CURLOPTTYPE_LONG, 188), /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */ - CINIT(RTSP_REQUEST, LONG, 189), + CURLOPT(CURLOPT_RTSP_REQUEST, CURLOPTTYPE_LONG, 189), /* The RTSP session identifier */ - CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190), + CURLOPT(CURLOPT_RTSP_SESSION_ID, CURLOPTTYPE_STRINGPOINT, 190), /* The RTSP stream URI */ - CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191), + CURLOPT(CURLOPT_RTSP_STREAM_URI, CURLOPTTYPE_STRINGPOINT, 191), /* The Transport: header to use in RTSP requests */ - CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192), + CURLOPT(CURLOPT_RTSP_TRANSPORT, CURLOPTTYPE_STRINGPOINT, 192), /* Manually initialize the client RTSP CSeq for this handle */ - CINIT(RTSP_CLIENT_CSEQ, LONG, 193), + CURLOPT(CURLOPT_RTSP_CLIENT_CSEQ, CURLOPTTYPE_LONG, 193), /* Manually initialize the server RTSP CSeq for this handle */ - CINIT(RTSP_SERVER_CSEQ, LONG, 194), + CURLOPT(CURLOPT_RTSP_SERVER_CSEQ, CURLOPTTYPE_LONG, 194), /* The stream to pass to INTERLEAVEFUNCTION. */ - CINIT(INTERLEAVEDATA, OBJECTPOINT, 195), + CURLOPT(CURLOPT_INTERLEAVEDATA, CURLOPTTYPE_OBJECTPOINT, 195), /* Let the application define a custom write method for RTP data */ - CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196), + CURLOPT(CURLOPT_INTERLEAVEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 196), /* Turn on wildcard matching */ - CINIT(WILDCARDMATCH, LONG, 197), + CURLOPT(CURLOPT_WILDCARDMATCH, CURLOPTTYPE_LONG, 197), /* Directory matching callback called before downloading of an individual file (chunk) started */ - CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198), + CURLOPT(CURLOPT_CHUNK_BGN_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 198), /* Directory matching callback called after the file (chunk) was downloaded, or skipped */ - CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199), + CURLOPT(CURLOPT_CHUNK_END_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 199), /* Change match (fnmatch-like) callback for wildcard matching */ - CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200), + CURLOPT(CURLOPT_FNMATCH_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 200), /* Let the application define custom chunk data pointer */ - CINIT(CHUNK_DATA, OBJECTPOINT, 201), + CURLOPT(CURLOPT_CHUNK_DATA, CURLOPTTYPE_OBJECTPOINT, 201), /* FNMATCH_FUNCTION user pointer */ - CINIT(FNMATCH_DATA, OBJECTPOINT, 202), + CURLOPT(CURLOPT_FNMATCH_DATA, CURLOPTTYPE_OBJECTPOINT, 202), /* send linked-list of name:port:address sets */ - CINIT(RESOLVE, OBJECTPOINT, 203), + CURLOPT(CURLOPT_RESOLVE, CURLOPTTYPE_SLISTPOINT, 203), /* Set a username for authenticated TLS */ - CINIT(TLSAUTH_USERNAME, OBJECTPOINT, 204), + CURLOPT(CURLOPT_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 204), /* Set a password for authenticated TLS */ - CINIT(TLSAUTH_PASSWORD, OBJECTPOINT, 205), + CURLOPT(CURLOPT_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 205), /* Set authentication type for authenticated TLS */ - CINIT(TLSAUTH_TYPE, OBJECTPOINT, 206), + CURLOPT(CURLOPT_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 206), /* Set to 1 to enable the "TE:" header in HTTP requests to ask for compressed transfer-encoded responses. Set to 0 to disable the use of TE: @@ -1474,7 +1680,274 @@ typedef enum { option is set to 1. */ - CINIT(TRANSFER_ENCODING, LONG, 207), + CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207), + + /* Callback function for closing socket (instead of close(2)). The callback + should have type curl_closesocket_callback */ + CURLOPT(CURLOPT_CLOSESOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 208), + CURLOPT(CURLOPT_CLOSESOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 209), + + /* allow GSSAPI credential delegation */ + CURLOPT(CURLOPT_GSSAPI_DELEGATION, CURLOPTTYPE_LONG, 210), + + /* Set the name servers to use for DNS resolution */ + CURLOPT(CURLOPT_DNS_SERVERS, CURLOPTTYPE_STRINGPOINT, 211), + + /* Time-out accept operations (currently for FTP only) after this amount + of milliseconds. */ + CURLOPT(CURLOPT_ACCEPTTIMEOUT_MS, CURLOPTTYPE_LONG, 212), + + /* Set TCP keepalive */ + CURLOPT(CURLOPT_TCP_KEEPALIVE, CURLOPTTYPE_LONG, 213), + + /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */ + CURLOPT(CURLOPT_TCP_KEEPIDLE, CURLOPTTYPE_LONG, 214), + CURLOPT(CURLOPT_TCP_KEEPINTVL, CURLOPTTYPE_LONG, 215), + + /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */ + CURLOPT(CURLOPT_SSL_OPTIONS, CURLOPTTYPE_LONG, 216), + + /* Set the SMTP auth originator */ + CURLOPT(CURLOPT_MAIL_AUTH, CURLOPTTYPE_STRINGPOINT, 217), + + /* Enable/disable SASL initial response */ + CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218), + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_xferinfo_callback + * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ + CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219), + + /* The XOAUTH2 bearer token */ + CURLOPT(CURLOPT_XOAUTH2_BEARER, CURLOPTTYPE_STRINGPOINT, 220), + + /* Set the interface string to use as outgoing network + * interface for DNS requests. + * Only supported by the c-ares DNS backend */ + CURLOPT(CURLOPT_DNS_INTERFACE, CURLOPTTYPE_STRINGPOINT, 221), + + /* Set the local IPv4 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CURLOPT(CURLOPT_DNS_LOCAL_IP4, CURLOPTTYPE_STRINGPOINT, 222), + + /* Set the local IPv6 address to use for outgoing DNS requests. + * Only supported by the c-ares DNS backend */ + CURLOPT(CURLOPT_DNS_LOCAL_IP6, CURLOPTTYPE_STRINGPOINT, 223), + + /* Set authentication options directly */ + CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CURLOPT(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), + + /* Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CURLOPT(CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOPTTYPE_LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CURLOPT(CURLOPT_PROXYHEADER, CURLOPTTYPE_SLISTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CURLOPT(CURLOPT_HEADEROPT, CURLOPTTYPE_LONG, 229), + + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 230), + + /* Path to Unix domain socket */ + CURLOPT(CURLOPT_UNIX_SOCKET_PATH, CURLOPTTYPE_STRINGPOINT, 231), + + /* Set if we should verify the certificate status. */ + CURLOPT(CURLOPT_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 232), + + /* Set if we should enable TLS false start. */ + CURLOPT(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233), + + /* Do not squash dot-dot sequences */ + CURLOPT(CURLOPT_PATH_AS_IS, CURLOPTTYPE_LONG, 234), + + /* Proxy Service Name */ + CURLOPT(CURLOPT_PROXY_SERVICE_NAME, CURLOPTTYPE_STRINGPOINT, 235), + + /* Service Name */ + CURLOPT(CURLOPT_SERVICE_NAME, CURLOPTTYPE_STRINGPOINT, 236), + + /* Wait/don't wait for pipe/mutex to clarify */ + CURLOPT(CURLOPT_PIPEWAIT, CURLOPTTYPE_LONG, 237), + + /* Set the protocol used when curl is given a URL without a protocol */ + CURLOPT(CURLOPT_DEFAULT_PROTOCOL, CURLOPTTYPE_STRINGPOINT, 238), + + /* Set stream weight, 1 - 256 (default is 16) */ + CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), + + /* Set stream dependency on another CURL handle */ + CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), + + /* Set E-xclusive stream dependency on another CURL handle */ + CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), + + /* Do not send any tftp option requests to the server */ + CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), + + /* Linked-list of host:port:connect-to-host:connect-to-port, + overrides the URL's host:port (only for the network layer) */ + CURLOPT(CURLOPT_CONNECT_TO, CURLOPTTYPE_SLISTPOINT, 243), + + /* Set TCP Fast Open */ + CURLOPT(CURLOPT_TCP_FASTOPEN, CURLOPTTYPE_LONG, 244), + + /* Continue to send data if the server responds early with an + * HTTP status code >= 300 */ + CURLOPT(CURLOPT_KEEP_SENDING_ON_ERROR, CURLOPTTYPE_LONG, 245), + + /* The CApath or CAfile used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_CAINFO, CURLOPTTYPE_STRINGPOINT, 246), + + /* The CApath directory used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247), + + /* Set if we should verify the proxy in ssl handshake, + set 1 to verify. */ + CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248), + + /* Set if we should verify the Common name from the proxy certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches + * the provided hostname. */ + CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249), + + /* What version to specifically try to use for proxy. + See CURL_SSLVERSION defines below. */ + CURLOPT(CURLOPT_PROXY_SSLVERSION, CURLOPTTYPE_LONG, 250), + + /* Set a username for authenticated TLS for proxy */ + CURLOPT(CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPTTYPE_STRINGPOINT, 251), + + /* Set a password for authenticated TLS for proxy */ + CURLOPT(CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPTTYPE_STRINGPOINT, 252), + + /* Set authentication type for authenticated TLS for proxy */ + CURLOPT(CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPTTYPE_STRINGPOINT, 253), + + /* name of the file keeping your private SSL-certificate for proxy */ + CURLOPT(CURLOPT_PROXY_SSLCERT, CURLOPTTYPE_STRINGPOINT, 254), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for + proxy */ + CURLOPT(CURLOPT_PROXY_SSLCERTTYPE, CURLOPTTYPE_STRINGPOINT, 255), + + /* name of the file keeping your private SSL-key for proxy */ + CURLOPT(CURLOPT_PROXY_SSLKEY, CURLOPTTYPE_STRINGPOINT, 256), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for + proxy */ + CURLOPT(CURLOPT_PROXY_SSLKEYTYPE, CURLOPTTYPE_STRINGPOINT, 257), + + /* password for the SSL private key for proxy */ + CURLOPT(CURLOPT_PROXY_KEYPASSWD, CURLOPTTYPE_STRINGPOINT, 258), + + /* Specify which SSL ciphers to use for proxy */ + CURLOPT(CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOPTTYPE_STRINGPOINT, 259), + + /* CRL file for proxy */ + CURLOPT(CURLOPT_PROXY_CRLFILE, CURLOPTTYPE_STRINGPOINT, 260), + + /* Enable/disable specific SSL features with a bitmask for proxy, see + CURLSSLOPT_* */ + CURLOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLOPTTYPE_LONG, 261), + + /* Name of pre proxy to use. */ + CURLOPT(CURLOPT_PRE_PROXY, CURLOPTTYPE_STRINGPOINT, 262), + + /* The public key in DER form used to validate the proxy public key + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CURLOPT(CURLOPT_PROXY_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 263), + + /* Path to an abstract Unix domain socket */ + CURLOPT(CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOPTTYPE_STRINGPOINT, 264), + + /* Suppress proxy CONNECT response headers from user callbacks */ + CURLOPT(CURLOPT_SUPPRESS_CONNECT_HEADERS, CURLOPTTYPE_LONG, 265), + + /* The request target, instead of extracted from the URL */ + CURLOPT(CURLOPT_REQUEST_TARGET, CURLOPTTYPE_STRINGPOINT, 266), + + /* bitmask of allowed auth methods for connections to SOCKS5 proxies */ + CURLOPT(CURLOPT_SOCKS5_AUTH, CURLOPTTYPE_LONG, 267), + + /* Enable/disable SSH compression */ + CURLOPT(CURLOPT_SSH_COMPRESSION, CURLOPTTYPE_LONG, 268), + + /* Post MIME data. */ + CURLOPT(CURLOPT_MIMEPOST, CURLOPTTYPE_OBJECTPOINT, 269), + + /* Time to use with the CURLOPT_TIMECONDITION. Specified in number of + seconds since 1 Jan 1970. */ + CURLOPT(CURLOPT_TIMEVALUE_LARGE, CURLOPTTYPE_OFF_T, 270), + + /* Head start in milliseconds to give happy eyeballs. */ + CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271), + + /* Function that will be called before a resolver request is made */ + CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272), + + /* User data to pass to the resolver start callback. */ + CURLOPT(CURLOPT_RESOLVER_START_DATA, CURLOPTTYPE_OBJECTPOINT, 273), + + /* send HAProxy PROXY protocol header? */ + CURLOPT(CURLOPT_HAPROXYPROTOCOL, CURLOPTTYPE_LONG, 274), + + /* shuffle addresses before use when DNS returns multiple */ + CURLOPT(CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOPTTYPE_LONG, 275), + + /* Specify which TLS 1.3 ciphers suites to use */ + CURLOPT(CURLOPT_TLS13_CIPHERS, CURLOPTTYPE_STRINGPOINT, 276), + CURLOPT(CURLOPT_PROXY_TLS13_CIPHERS, CURLOPTTYPE_STRINGPOINT, 277), + + /* Disallow specifying username/login in URL. */ + CURLOPT(CURLOPT_DISALLOW_USERNAME_IN_URL, CURLOPTTYPE_LONG, 278), + + /* DNS-over-HTTPS URL */ + CURLOPT(CURLOPT_DOH_URL, CURLOPTTYPE_STRINGPOINT, 279), + + /* Preferred buffer size to use for uploads */ + CURLOPT(CURLOPT_UPLOAD_BUFFERSIZE, CURLOPTTYPE_LONG, 280), + + /* Time in ms between connection upkeep calls for long-lived connections. */ + CURLOPT(CURLOPT_UPKEEP_INTERVAL_MS, CURLOPTTYPE_LONG, 281), + + /* Specify URL using CURL URL API. */ + CURLOPT(CURLOPT_CURLU, CURLOPTTYPE_OBJECTPOINT, 282), + + /* add trailing data just after no more data is available */ + CURLOPT(CURLOPT_TRAILERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 283), + + /* pointer to be passed to HTTP_TRAILER_FUNCTION */ + CURLOPT(CURLOPT_TRAILERDATA, CURLOPTTYPE_OBJECTPOINT, 284), + + /* set this to 1L to allow HTTP/0.9 responses or 0L to disallow */ + CURLOPT(CURLOPT_HTTP09_ALLOWED, CURLOPTTYPE_LONG, 285), + + /* alt-svc control bitmask */ + CURLOPT(CURLOPT_ALTSVC_CTRL, CURLOPTTYPE_LONG, 286), + + /* alt-svc cache file name to possibly read from/write to */ + CURLOPT(CURLOPT_ALTSVC, CURLOPTTYPE_STRINGPOINT, 287), + + /* maximum age of a connection to consider it for reuse (in seconds) */ + CURLOPT(CURLOPT_MAXAGE_CONN, CURLOPTTYPE_LONG, 288), + + /* SASL authorisation identity */ + CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289), + + /* allow RCPT TO command to fail for some recipients */ + CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290), CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -1512,13 +1985,10 @@ typedef enum { option might be handy to force libcurl to use a specific IP version. */ #define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP versions that your system allows */ -#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */ -#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */ +#define CURL_IPRESOLVE_V4 1 /* resolve to IPv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to IPv6 addresses */ /* three convenient "aliases" that follow the name scheme better */ -#define CURLOPT_WRITEDATA CURLOPT_FILE -#define CURLOPT_READDATA CURLOPT_INFILE -#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ @@ -1528,10 +1998,20 @@ enum { for us! */ CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ - + CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */ + CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ + CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 + Upgrade */ + CURL_HTTP_VERSION_3 = 30, /* Makes use of explicit HTTP/3 without fallback. + Use CURLOPT_ALTSVC to enable HTTP/3 upgrade */ CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ }; +/* Convenience definition simple because the name of the version is HTTP/2 and + not 2.0. The 2_0 version of the enum name was set while the version was + still planned to be 2.0 and we stick to it for compatibility. */ +#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0 + /* * Public API enums for RTSP requests */ @@ -1565,13 +2045,29 @@ enum CURL_NETRC_OPTION { enum { CURL_SSLVERSION_DEFAULT, - CURL_SSLVERSION_TLSv1, + CURL_SSLVERSION_TLSv1, /* TLS 1.x */ CURL_SSLVERSION_SSLv2, CURL_SSLVERSION_SSLv3, + CURL_SSLVERSION_TLSv1_0, + CURL_SSLVERSION_TLSv1_1, + CURL_SSLVERSION_TLSv1_2, + CURL_SSLVERSION_TLSv1_3, CURL_SSLVERSION_LAST /* never use, keep last */ }; +enum { + CURL_SSLVERSION_MAX_NONE = 0, + CURL_SSLVERSION_MAX_DEFAULT = (CURL_SSLVERSION_TLSv1 << 16), + CURL_SSLVERSION_MAX_TLSv1_0 = (CURL_SSLVERSION_TLSv1_0 << 16), + CURL_SSLVERSION_MAX_TLSv1_1 = (CURL_SSLVERSION_TLSv1_1 << 16), + CURL_SSLVERSION_MAX_TLSv1_2 = (CURL_SSLVERSION_TLSv1_2 << 16), + CURL_SSLVERSION_MAX_TLSv1_3 = (CURL_SSLVERSION_TLSv1_3 << 16), + + /* never use, keep last */ + CURL_SSLVERSION_MAX_LAST = (CURL_SSLVERSION_LAST << 16) +}; + enum CURL_TLSAUTH { CURL_TLSAUTH_NONE, CURL_TLSAUTH_SRP, @@ -1579,13 +2075,16 @@ enum CURL_TLSAUTH { }; /* symbols to use with CURLOPT_POSTREDIR. - CURL_REDIR_POST_301 and CURL_REDIR_POST_302 can be bitwise ORed so that - CURL_REDIR_POST_301 | CURL_REDIR_POST_302 == CURL_REDIR_POST_ALL */ + CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303 + can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302 + | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */ #define CURL_REDIR_GET_ALL 0 #define CURL_REDIR_POST_301 1 #define CURL_REDIR_POST_302 2 -#define CURL_REDIR_POST_ALL (CURL_REDIR_POST_301|CURL_REDIR_POST_302) +#define CURL_REDIR_POST_303 4 +#define CURL_REDIR_POST_ALL \ + (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) typedef enum { CURL_TIMECOND_NONE, @@ -1597,56 +2096,168 @@ typedef enum { CURL_TIMECOND_LAST } curl_TimeCond; +/* Special size_t value signaling a zero-terminated string. */ +#define CURL_ZERO_TERMINATED ((size_t) -1) /* curl_strequal() and curl_strnequal() are subject for removal in a future - libcurl, see lib/README.curlx for details */ -CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); -CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); + release */ +CURL_EXTERN int curl_strequal(const char *s1, const char *s2); +CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n); -/* name is uppercase CURLFORM_ */ -#ifdef CFINIT -#undef CFINIT -#endif +/* Mime/form handling support. */ +typedef struct curl_mime_s curl_mime; /* Mime context. */ +typedef struct curl_mimepart_s curl_mimepart; /* Mime part context. */ -#ifdef CURL_ISOCPP -#define CFINIT(name) CURLFORM_ ## name -#else -/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -#define CFINIT(name) CURLFORM_/**/name -#endif +/* + * NAME curl_mime_init() + * + * DESCRIPTION + * + * Create a mime context and return its handle. The easy parameter is the + * target handle. + */ +CURL_EXTERN curl_mime *curl_mime_init(CURL *easy); + +/* + * NAME curl_mime_free() + * + * DESCRIPTION + * + * release a mime handle and its substructures. + */ +CURL_EXTERN void curl_mime_free(curl_mime *mime); + +/* + * NAME curl_mime_addpart() + * + * DESCRIPTION + * + * Append a new empty part to the given mime context and return a handle to + * the created part. + */ +CURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime); + +/* + * NAME curl_mime_name() + * + * DESCRIPTION + * + * Set mime/form part name. + */ +CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name); + +/* + * NAME curl_mime_filename() + * + * DESCRIPTION + * + * Set mime part remote file name. + */ +CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_type() + * + * DESCRIPTION + * + * Set mime part type. + */ +CURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype); + +/* + * NAME curl_mime_encoder() + * + * DESCRIPTION + * + * Set mime data transfer encoder. + */ +CURL_EXTERN CURLcode curl_mime_encoder(curl_mimepart *part, + const char *encoding); + +/* + * NAME curl_mime_data() + * + * DESCRIPTION + * + * Set mime part data source from memory data, + */ +CURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part, + const char *data, size_t datasize); + +/* + * NAME curl_mime_filedata() + * + * DESCRIPTION + * + * Set mime part data source from named file. + */ +CURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part, + const char *filename); + +/* + * NAME curl_mime_data_cb() + * + * DESCRIPTION + * + * Set mime part data source from callback function. + */ +CURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part, + curl_off_t datasize, + curl_read_callback readfunc, + curl_seek_callback seekfunc, + curl_free_callback freefunc, + void *arg); + +/* + * NAME curl_mime_subparts() + * + * DESCRIPTION + * + * Set mime part data source from subparts. + */ +CURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part, + curl_mime *subparts); +/* + * NAME curl_mime_headers() + * + * DESCRIPTION + * + * Set mime part headers. + */ +CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part, + struct curl_slist *headers, + int take_ownership); typedef enum { - CFINIT(NOTHING), /********* the first one is unused ************/ + CURLFORM_NOTHING, /********* the first one is unused ************/ + CURLFORM_COPYNAME, + CURLFORM_PTRNAME, + CURLFORM_NAMELENGTH, + CURLFORM_COPYCONTENTS, + CURLFORM_PTRCONTENTS, + CURLFORM_CONTENTSLENGTH, + CURLFORM_FILECONTENT, + CURLFORM_ARRAY, + CURLFORM_OBSOLETE, + CURLFORM_FILE, - /* */ - CFINIT(COPYNAME), - CFINIT(PTRNAME), - CFINIT(NAMELENGTH), - CFINIT(COPYCONTENTS), - CFINIT(PTRCONTENTS), - CFINIT(CONTENTSLENGTH), - CFINIT(FILECONTENT), - CFINIT(ARRAY), - CFINIT(OBSOLETE), - CFINIT(FILE), + CURLFORM_BUFFER, + CURLFORM_BUFFERPTR, + CURLFORM_BUFFERLENGTH, - CFINIT(BUFFER), - CFINIT(BUFFERPTR), - CFINIT(BUFFERLENGTH), + CURLFORM_CONTENTTYPE, + CURLFORM_CONTENTHEADER, + CURLFORM_FILENAME, + CURLFORM_END, + CURLFORM_OBSOLETE2, - CFINIT(CONTENTTYPE), - CFINIT(CONTENTHEADER), - CFINIT(FILENAME), - CFINIT(END), - CFINIT(OBSOLETE2), - - CFINIT(STREAM), + CURLFORM_STREAM, + CURLFORM_CONTENTLEN, /* added in 7.46.0, provide a curl_off_t length */ CURLFORM_LASTENTRY /* the last unused */ } CURLformoption; -#undef CFINIT /* done */ - /* structure to be used as parameter for CURLFORM_ARRAY */ struct curl_forms { CURLformoption option; @@ -1704,7 +2315,8 @@ CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, * Should return the buffer length passed to it as the argument "len" on * success. */ -typedef size_t (*curl_formget_callback)(void *arg, const char *buf, size_t len); +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, + size_t len); /* * NAME curl_formget() @@ -1842,6 +2454,47 @@ struct curl_slist { struct curl_slist *next; }; +/* + * NAME curl_global_sslset() + * + * DESCRIPTION + * + * When built with multiple SSL backends, curl_global_sslset() allows to + * choose one. This function can only be called once, and it must be called + * *before* curl_global_init(). + * + * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The + * backend can also be specified via the name parameter (passing -1 as id). + * If both id and name are specified, the name will be ignored. If neither id + * nor name are specified, the function will fail with + * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the + * NULL-terminated list of available backends. + * + * Upon success, the function returns CURLSSLSET_OK. + * + * If the specified SSL backend is not available, the function returns + * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated + * list of available SSL backends. + * + * The SSL backend can be set only once. If it has already been set, a + * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + */ + +typedef struct { + curl_sslbackend id; + const char *name; +} curl_ssl_backend; + +typedef enum { + CURLSSLSET_OK = 0, + CURLSSLSET_UNKNOWN_BACKEND, + CURLSSLSET_TOO_LATE, + CURLSSLSET_NO_BACKENDS /* libcurl was built without any SSL support */ +} CURLsslset; + +CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, + const curl_ssl_backend ***avail); + /* * NAME curl_slist_append() * @@ -1873,8 +2526,8 @@ CURL_EXTERN void curl_slist_free_all(struct curl_slist *); */ CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); -/* info about the certificate chain, only for OpenSSL builds. Asked - for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +/* info about the certificate chain, only for OpenSSL, GnuTLS, Schannel, NSS + and GSKit builds. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ struct curl_certinfo { int num_of_certs; /* number of certificates with information */ struct curl_slist **certinfo; /* for each index in this array, there's a @@ -1882,10 +2535,21 @@ struct curl_certinfo { format "name: value" */ }; +/* Information about the SSL library used and the respective internal SSL + handle, which can be used to obtain further information regarding the + connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */ +struct curl_tlssessioninfo { + curl_sslbackend backend; + void *internals; +}; + #define CURLINFO_STRING 0x100000 #define CURLINFO_LONG 0x200000 #define CURLINFO_DOUBLE 0x300000 #define CURLINFO_SLIST 0x400000 +#define CURLINFO_PTR 0x400000 /* same as SLIST */ +#define CURLINFO_SOCKET 0x500000 +#define CURLINFO_OFF_T 0x600000 #define CURLINFO_MASK 0x0fffff #define CURLINFO_TYPEMASK 0xf00000 @@ -1898,15 +2562,22 @@ typedef enum { CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_UPLOAD_T = CURLINFO_OFF_T + 7, CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SIZE_DOWNLOAD_T = CURLINFO_OFF_T + 8, CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T + 9, CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_SPEED_UPLOAD_T = CURLINFO_OFF_T + 10, CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_FILETIME_T = CURLINFO_OFF_T + 14, CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T + 15, CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_CONTENT_LENGTH_UPLOAD_T = CURLINFO_OFF_T + 16, CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, @@ -1924,7 +2595,7 @@ typedef enum { CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, - CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_CERTINFO = CURLINFO_PTR + 34, CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, CURLINFO_RTSP_SESSION_ID = CURLINFO_STRING + 36, CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG + 37, @@ -1933,9 +2604,27 @@ typedef enum { CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_TLS_SESSION = CURLINFO_PTR + 43, + CURLINFO_ACTIVESOCKET = CURLINFO_SOCKET + 44, + CURLINFO_TLS_SSL_PTR = CURLINFO_PTR + 45, + CURLINFO_HTTP_VERSION = CURLINFO_LONG + 46, + CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47, + CURLINFO_PROTOCOL = CURLINFO_LONG + 48, + CURLINFO_SCHEME = CURLINFO_STRING + 49, /* Fill in new entries below here! */ - CURLINFO_LASTONE = 42 + /* Preferably these would be defined conditionally based on the + sizeof curl_off_t being 64-bits */ + CURLINFO_TOTAL_TIME_T = CURLINFO_OFF_T + 50, + CURLINFO_NAMELOOKUP_TIME_T = CURLINFO_OFF_T + 51, + CURLINFO_CONNECT_TIME_T = CURLINFO_OFF_T + 52, + CURLINFO_PRETRANSFER_TIME_T = CURLINFO_OFF_T + 53, + CURLINFO_STARTTRANSFER_TIME_T = CURLINFO_OFF_T + 54, + CURLINFO_REDIRECT_TIME_T = CURLINFO_OFF_T + 55, + CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56, + CURLINFO_RETRY_AFTER = CURLINFO_OFF_T + 57, + + CURLINFO_LASTONE = 57 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -1954,11 +2643,12 @@ typedef enum { CURLCLOSEPOLICY_LAST /* last, never use this */ } curl_closepolicy; -#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */ #define CURL_GLOBAL_WIN32 (1<<1) #define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) #define CURL_GLOBAL_NOTHING 0 #define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL +#define CURL_GLOBAL_ACK_EINTR (1<<2) /***************************************************************************** @@ -1977,6 +2667,7 @@ typedef enum { CURL_LOCK_DATA_DNS, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_PSL, CURL_LOCK_DATA_LAST } curl_lock_data; @@ -1996,15 +2687,15 @@ typedef void (*curl_unlock_function)(CURL *handle, curl_lock_data data, void *userptr); -typedef void CURLSH; typedef enum { CURLSHE_OK, /* all is fine */ CURLSHE_BAD_OPTION, /* 1 */ CURLSHE_IN_USE, /* 2 */ CURLSHE_INVALID, /* 3 */ - CURLSHE_NOMEM, /* out of memory */ - CURLSHE_LAST /* never use */ + CURLSHE_NOMEM, /* 4 out of memory */ + CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */ + CURLSHE_LAST /* never use */ } CURLSHcode; typedef enum { @@ -2031,6 +2722,8 @@ typedef enum { CURLVERSION_SECOND, CURLVERSION_THIRD, CURLVERSION_FOURTH, + CURLVERSION_FIFTH, + CURLVERSION_SIXTH, CURLVERSION_LAST /* never actually use this */ } CURLversion; @@ -2039,7 +2732,7 @@ typedef enum { meant to be a built-in version number for what kind of struct the caller expects. If the struct ever changes, we redefine the NOW to another enum from above. */ -#define CURLVERSION_NOW CURLVERSION_FOURTH +#define CURLVERSION_NOW CURLVERSION_SIXTH typedef struct { CURLversion age; /* age of the returned struct */ @@ -2067,25 +2760,54 @@ typedef struct { const char *libssh_version; /* human readable string */ + /* These fields were added in CURLVERSION_FIFTH */ + unsigned int brotli_ver_num; /* Numeric Brotli version + (MAJOR << 24) | (MINOR << 12) | PATCH */ + const char *brotli_version; /* human readable string. */ + + /* These fields were added in CURLVERSION_SIXTH */ + unsigned int nghttp2_ver_num; /* Numeric nghttp2 version + (MAJOR << 16) | (MINOR << 8) | PATCH */ + const char *nghttp2_version; /* human readable string. */ + const char *quic_version; /* human readable quic (+ HTTP/3) library + + version or NULL */ } curl_version_info_data; -#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ -#define CURL_VERSION_KERBEROS4 (1<<1) /* kerberos auth is supported */ -#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ -#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ -#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ -#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth support */ -#define CURL_VERSION_DEBUG (1<<6) /* built with debug capabilities */ -#define CURL_VERSION_ASYNCHDNS (1<<7) /* asynchronous dns resolves */ -#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth */ -#define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */ -#define CURL_VERSION_IDN (1<<10) /* International Domain Names support */ -#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ -#define CURL_VERSION_CONV (1<<12) /* character conversions supported */ -#define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking supported */ -#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is supported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ +#define CURL_VERSION_PSL (1<<20) /* Mozilla's Public Suffix List, used + for cookie domain verification */ +#define CURL_VERSION_HTTPS_PROXY (1<<21) /* HTTPS-proxy support built-in */ +#define CURL_VERSION_MULTI_SSL (1<<22) /* Multiple SSL backends available */ +#define CURL_VERSION_BROTLI (1<<23) /* Brotli features are present. */ +#define CURL_VERSION_ALTSVC (1<<24) /* Alt-Svc handling built-in */ +#define CURL_VERSION_HTTP3 (1<<25) /* HTTP3 support built-in */ -/* +#define CURL_VERSION_ESNI (1<<26) /* ESNI support */ + + /* * NAME curl_version_info() * * DESCRIPTION @@ -2145,6 +2867,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); stuff before they can be included! */ #include "easy.h" /* nothing in curl is fun without the easy stuff */ #include "multi.h" +#include "urlapi.h" /* the typechecker doesn't work in C++ (yet) */ #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ @@ -2163,4 +2886,4 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); #endif /* __STDC__ >= 1 */ #endif /* gcc >= 4.3 && !__cplusplus */ -#endif /* __CURL_CURL_H */ +#endif /* CURLINC_CURL_H */ diff --git a/libs/curl/include/curl/curlver.h b/libs/curl/include/curl/curlver.h index 4db21857d..5264f1986 100644 --- a/libs/curl/include/curl/curlver.h +++ b/libs/curl/include/curl/curlver.h @@ -1,5 +1,5 @@ -#ifndef __CURL_CURLVER_H -#define __CURL_CURLVER_H +#ifndef CURLINC_CURLVER_H +#define CURLINC_CURLVER_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -26,17 +26,17 @@ a script at release-time. This was made its own header file in 7.11.2 */ /* This is the global package copyright */ -#define LIBCURL_COPYRIGHT "1996 - 2011 Daniel Stenberg, ." +#define LIBCURL_COPYRIGHT "1996 - 2020 Daniel Stenberg, ." /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.21.6" +#define LIBCURL_VERSION "7.69.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 21 -#define LIBCURL_VERSION_PATCH 6 +#define LIBCURL_VERSION_MINOR 69 +#define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparions by programs. The LIBCURL_VERSION_NUM define will @@ -52,18 +52,26 @@ This 6-digit (24 bits) hexadecimal number does not show pre-release number, and it is always a greater number in a more recent release. It makes comparisons with greater than and less than work. + + Note: This define is the full hex number and _does not_ use the + CURL_VERSION_BITS() macro since curl's own configure script greps for it + and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x071506 +#define LIBCURL_VERSION_NUM 0x074500 /* * This is the date and time when the full source package was created. The * timestamp is not stored in git, as the timestamp is properly set in the * tarballs by the maketgz script. * - * The format of the date should follow this template: + * The format of the date follows this template: * - * "Mon Feb 12 11:35:33 UTC 2007" + * "2007-11-23" */ -#define LIBCURL_TIMESTAMP "Fri Apr 22 17:18:50 UTC 2011" +#define LIBCURL_TIMESTAMP "2020-03-04" -#endif /* __CURL_CURLVER_H */ +#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) +#define CURL_AT_LEAST_VERSION(x,y,z) \ + (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) + +#endif /* CURLINC_CURLVER_H */ diff --git a/libs/curl/include/curl/easy.h b/libs/curl/include/curl/easy.h index c1e3e7609..592f5d3c1 100644 --- a/libs/curl/include/curl/easy.h +++ b/libs/curl/include/curl/easy.h @@ -1,5 +1,5 @@ -#ifndef __CURL_EASY_H -#define __CURL_EASY_H +#ifndef CURLINC_EASY_H +#define CURLINC_EASY_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -58,7 +58,7 @@ CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); * curl_easy_duphandle() for each new thread to avoid a series of identical * curl_easy_setopt() invokes in every thread. */ -CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl); +CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); /* * NAME curl_easy_reset() @@ -95,6 +95,16 @@ CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n); + +/* + * NAME curl_easy_upkeep() + * + * DESCRIPTION + * + * Performs connection upkeep for the given session handle. + */ +CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl); + #ifdef __cplusplus } #endif diff --git a/libs/curl/include/curl/mprintf.h b/libs/curl/include/curl/mprintf.h index de7dd2f3c..f615ed7d6 100644 --- a/libs/curl/include/curl/mprintf.h +++ b/libs/curl/include/curl/mprintf.h @@ -1,5 +1,5 @@ -#ifndef __CURL_MPRINTF_H -#define __CURL_MPRINTF_H +#ifndef CURLINC_MPRINTF_H +#define CURLINC_MPRINTF_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -24,8 +24,7 @@ #include #include /* needed for FILE */ - -#include "curl.h" +#include "curl.h" /* for CURL_EXTERN */ #ifdef __cplusplus extern "C" { @@ -44,38 +43,8 @@ CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, CURL_EXTERN char *curl_maprintf(const char *format, ...); CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); -#ifdef _MPRINTF_REPLACE -# undef printf -# undef fprintf -# undef sprintf -# undef vsprintf -# undef snprintf -# undef vprintf -# undef vfprintf -# undef vsnprintf -# undef aprintf -# undef vaprintf -# define printf curl_mprintf -# define fprintf curl_mfprintf -#ifdef CURLDEBUG -/* When built with CURLDEBUG we define away the sprintf() functions since we - don't want internal code to be using them */ -# define sprintf sprintf_was_used -# define vsprintf vsprintf_was_used -#else -# define sprintf curl_msprintf -# define vsprintf curl_mvsprintf -#endif -# define snprintf curl_msnprintf -# define vprintf curl_mvprintf -# define vfprintf curl_mvfprintf -# define vsnprintf curl_mvsnprintf -# define aprintf curl_maprintf -# define vaprintf curl_mvaprintf -#endif - #ifdef __cplusplus } #endif -#endif /* __CURL_MPRINTF_H */ +#endif /* CURLINC_MPRINTF_H */ diff --git a/libs/curl/include/curl/multi.h b/libs/curl/include/curl/multi.h index f96566669..bda9bb7b8 100644 --- a/libs/curl/include/curl/multi.h +++ b/libs/curl/include/curl/multi.h @@ -1,5 +1,5 @@ -#ifndef __CURL_MULTI_H -#define __CURL_MULTI_H +#ifndef CURLINC_MULTI_H +#define CURLINC_MULTI_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -52,7 +52,11 @@ extern "C" { #endif +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_multi CURLM; +#else typedef void CURLM; +#endif typedef enum { CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or @@ -64,6 +68,12 @@ typedef enum { CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ + CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was + attempted to get added - again */ + CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a + callback */ + CURLM_WAKEUP_FAILURE, /* wakeup is unavailable or failed */ + CURLM_BAD_FUNCTION_ARGUMENT, /* function called with a bad parameter */ CURLM_LAST } CURLMcode; @@ -72,6 +82,11 @@ typedef enum { curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ #define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM +/* bitmask bits for CURLMOPT_PIPELINING */ +#define CURLPIPE_NOTHING 0L +#define CURLPIPE_HTTP1 1L +#define CURLPIPE_MULTIPLEX 2L + typedef enum { CURLMSG_NONE, /* first, not used */ CURLMSG_DONE, /* This easy handle has completed. 'result' contains @@ -89,6 +104,19 @@ struct CURLMsg { }; typedef struct CURLMsg CURLMsg; +/* Based on poll(2) structure and values. + * We don't use pollfd and POLL* constants explicitly + * to cover platforms without poll(). */ +#define CURL_WAIT_POLLIN 0x0001 +#define CURL_WAIT_POLLPRI 0x0002 +#define CURL_WAIT_POLLOUT 0x0004 + +struct curl_waitfd { + curl_socket_t fd; + short events; + short revents; /* not supported yet */ +}; + /* * Name: curl_multi_init() * @@ -133,6 +161,43 @@ CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, fd_set *exc_fd_set, int *max_fd); +/* + * Name: curl_multi_wait() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + +/* + * Name: curl_multi_poll() + * + * Desc: Poll on all fds within a CURLM set as well as any + * additional fds passed to the function. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + int *ret); + +/* + * Name: curl_multi_wakeup() + * + * Desc: wakes up a sleeping curl_multi_poll call. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); + /* * Name: curl_multi_perform() * @@ -146,8 +211,8 @@ CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, * * Returns: CURLMcode type, general multi error code. *NOTE* that this only * returns errors etc regarding the whole multi stack. There might - * still have occurred problems on invidual transfers even when this - * returns OK. + * still have occurred problems on individual transfers even when + * this returns OK. */ CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles); @@ -180,7 +245,7 @@ CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); * curl_multi_cleanup(). * * The 'CURLMsg' struct is meant to be very simple and only contain - * very basic informations. If more involved information is wanted, + * very basic information. If more involved information is wanted, * we will provide the particular "transfer handle" in that struct * and that should/could/would be used in subsequent * curl_easy_getinfo() calls (or similar). The point being that we @@ -279,37 +344,58 @@ CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, long *milliseconds); -#undef CINIT /* re-using the same name as in curl.h */ - -#ifdef CURL_ISOCPP -#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num -#else -/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ -#define LONG CURLOPTTYPE_LONG -#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT -#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT -#define OFF_T CURLOPTTYPE_OFF_T -#define CINIT(name,type,number) CURLMOPT_/**/name = type + number -#endif - typedef enum { /* This is the socket callback function pointer */ - CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + CURLOPT(CURLMOPT_SOCKETFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 1), /* This is the argument passed to the socket callback */ - CINIT(SOCKETDATA, OBJECTPOINT, 2), + CURLOPT(CURLMOPT_SOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 2), /* set to 1 to enable pipelining for this multi handle */ - CINIT(PIPELINING, LONG, 3), + CURLOPT(CURLMOPT_PIPELINING, CURLOPTTYPE_LONG, 3), /* This is the timer callback function pointer */ - CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + CURLOPT(CURLMOPT_TIMERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 4), /* This is the argument passed to the timer callback */ - CINIT(TIMERDATA, OBJECTPOINT, 5), + CURLOPT(CURLMOPT_TIMERDATA, CURLOPTTYPE_OBJECTPOINT, 5), /* maximum number of entries in the connection cache */ - CINIT(MAXCONNECTS, LONG, 6), + CURLOPT(CURLMOPT_MAXCONNECTS, CURLOPTTYPE_LONG, 6), + + /* maximum number of (pipelining) connections to one host */ + CURLOPT(CURLMOPT_MAX_HOST_CONNECTIONS, CURLOPTTYPE_LONG, 7), + + /* maximum number of requests in a pipeline */ + CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8), + + /* a connection with a content-length longer than this + will not be considered for pipelining */ + CURLOPT(CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 9), + + /* a connection with a chunk length longer than this + will not be considered for pipelining */ + CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10), + + /* a list of site names(+port) that are blacklisted from + pipelining */ + CURLOPT(CURLMOPT_PIPELINING_SITE_BL, CURLOPTTYPE_OBJECTPOINT, 11), + + /* a list of server types that are blacklisted from + pipelining */ + CURLOPT(CURLMOPT_PIPELINING_SERVER_BL, CURLOPTTYPE_OBJECTPOINT, 12), + + /* maximum number of open connections in total */ + CURLOPT(CURLMOPT_MAX_TOTAL_CONNECTIONS, CURLOPTTYPE_LONG, 13), + + /* This is the server push callback function pointer */ + CURLOPT(CURLMOPT_PUSHFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 14), + + /* This is the argument passed to the server push callback */ + CURLOPT(CURLMOPT_PUSHDATA, CURLOPTTYPE_OBJECTPOINT, 15), + + /* maximum number of concurrent streams to support on a connection */ + CURLOPT(CURLMOPT_MAX_CONCURRENT_STREAMS, CURLOPTTYPE_LONG, 16), CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; @@ -338,6 +424,31 @@ CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, curl_socket_t sockfd, void *sockp); + +/* + * Name: curl_push_callback + * + * Desc: This callback gets called when a new stream is being pushed by the + * server. It approves or denies the new stream. + * + * Returns: CURL_PUSH_OK or CURL_PUSH_DENY. + */ +#define CURL_PUSH_OK 0 +#define CURL_PUSH_DENY 1 + +struct curl_pushheaders; /* forward declaration only */ + +CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h, + size_t num); +CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h, + const char *name); + +typedef int (*curl_push_callback)(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *userp); + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/libs/curl/include/curl/stdcheaders.h b/libs/curl/include/curl/stdcheaders.h index ad82ef633..a6bdc1a25 100644 --- a/libs/curl/include/curl/stdcheaders.h +++ b/libs/curl/include/curl/stdcheaders.h @@ -1,5 +1,5 @@ -#ifndef __STDC_HEADERS_H -#define __STDC_HEADERS_H +#ifndef CURLINC_STDCHEADERS_H +#define CURLINC_STDCHEADERS_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -24,10 +24,10 @@ #include -size_t fread (void *, size_t, size_t, FILE *); -size_t fwrite (const void *, size_t, size_t, FILE *); +size_t fread(void *, size_t, size_t, FILE *); +size_t fwrite(const void *, size_t, size_t, FILE *); int strcasecmp(const char *, const char *); int strncasecmp(const char *, const char *, size_t); -#endif /* __STDC_HEADERS_H */ +#endif /* CURLINC_STDCHEADERS_H */ diff --git a/libs/curl/include/curl/system.h b/libs/curl/include/curl/system.h new file mode 100644 index 000000000..867af6141 --- /dev/null +++ b/libs/curl/include/curl/system.h @@ -0,0 +1,504 @@ +#ifndef CURLINC_SYSTEM_H +#define CURLINC_SYSTEM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * Try to keep one section per platform, compiler and architecture, otherwise, + * if an existing section is reused for a different one and later on the + * original is adjusted, probably the piggybacking one can be adversely + * changed. + * + * In order to differentiate between platforms/compilers/architectures use + * only compiler built in predefined preprocessor symbols. + * + * curl_off_t + * ---------- + * + * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit + * wide signed integral data type. The width of this data type must remain + * constant and independent of any possible large file support settings. + * + * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit + * wide signed integral data type if there is no 64-bit type. + * + * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall + * only be violated if off_t is the only 64-bit data type available and the + * size of off_t is independent of large file support settings. Keep your + * build on the safe side avoiding an off_t gating. If you have a 64-bit + * off_t then take for sure that another 64-bit data type exists, dig deeper + * and you will find it. + * + */ + +#if defined(__DJGPP__) || defined(__GO32__) +# if defined(__DJGPP__) && (__DJGPP__ > 1) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__SALFORDC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__BORLANDC__) +# if (__BORLANDC__ < 0x520) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TURBOC__) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__WATCOMC__) +# if defined(__386__) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__POCC__) +# if (__POCC__ < 280) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# elif defined(_MSC_VER) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__LCC__) +# if defined(__e2k__) /* MCST eLbrus C Compiler */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# else /* Local (or Little) C Compiler */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +# endif + +#elif defined(__SYMBIAN32__) +# if defined(__EABI__) /* Treat all ARM compilers equally */ +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__CW32__) +# pragma longlong on +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__VC32__) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__MWERKS__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(_WIN32_WCE) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__MINGW32__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_WS2TCPIP_H 1 + +#elif defined(__VMS) +# if defined(__VAX) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int + +#elif defined(__OS400__) +# if defined(__ILEC400__) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__MVS__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__370__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# elif defined(_LP64) +# endif +# if defined(_LONG_LONG) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(TPF) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +#elif defined(__TINYC__) /* also known as tcc */ +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Oracle Solaris Studio */ +# if !defined(__LP64) && (defined(__ILP32) || \ + defined(__i386) || \ + defined(__sparcv8) || \ + defined(__sparcv8plus)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64) || \ + defined(__amd64) || defined(__sparcv9) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#elif defined(__xlc__) /* IBM xlc compiler */ +# if !defined(_LP64) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +/* ===================================== */ +/* KEEP MSVC THE PENULTIMATE ENTRY */ +/* ===================================== */ + +#elif defined(_MSC_VER) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T int + +/* ===================================== */ +/* KEEP GENERIC GCC THE LAST ENTRY */ +/* ===================================== */ + +#elif defined(__GNUC__) && !defined(_SCO_DS) +# if !defined(__LP64__) && \ + (defined(__ILP32__) || defined(__i386__) || defined(__hppa__) || \ + defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ + defined(__sparc__) || defined(__mips__) || defined(__sh__) || \ + defined(__XTENSA__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L)) +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64__) || \ + defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \ + defined(__e2k__) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \ + (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L) +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# endif +# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURL_PULL_SYS_TYPES_H 1 +# define CURL_PULL_SYS_SOCKET_H 1 + +#else +/* generic "safe guess" on old 32 bit style */ +# define CURL_TYPEOF_CURL_OFF_T long +# define CURL_FORMAT_CURL_OFF_T "ld" +# define CURL_FORMAT_CURL_OFF_TU "lu" +# define CURL_SUFFIX_CURL_OFF_T L +# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_SOCKLEN_T int +#endif + +#ifdef _AIX +/* AIX needs */ +#define CURL_PULL_SYS_POLL_H +#endif + + +/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file */ +/* ws2tcpip.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_WS2TCPIP_H +# include +# include +# include +#endif + +/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */ +/* sys/types.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ +/* sys/socket.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file */ +/* sys/poll.h is required here to properly make type definitions below. */ +#ifdef CURL_PULL_SYS_POLL_H +# include +#endif + +/* Data type definition of curl_socklen_t. */ +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T + typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; +#endif + +/* Data type definition of curl_off_t. */ + +#ifdef CURL_TYPEOF_CURL_OFF_T + typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; +#endif + +/* + * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow + * these to be visible and exported by the external libcurl interface API, + * while also making them visible to the library internals, simply including + * curl_setup.h, without actually needing to include curl.h internally. + * If some day this section would grow big enough, all this should be moved + * to its own header file. + */ + +/* + * Figure out if we can use the ## preprocessor operator, which is supported + * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ + * or __cplusplus so we need to carefully check for them too. + */ + +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__ILEC400__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +/* + * Macros for minimum-width signed and unsigned curl_off_t integer constants. + */ + +#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) +# define CURLINC_OFF_T_C_HLPR2(x) x +# define CURLINC_OFF_T_C_HLPR1(x) CURLINC_OFF_T_C_HLPR2(x) +# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ + CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ + CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) +#else +# ifdef CURL_ISOCPP +# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix +# else +# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix +# endif +# define CURLINC_OFF_T_C_HLPR1(Val,Suffix) CURLINC_OFF_T_C_HLPR2(Val,Suffix) +# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) +# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) +#endif + +#endif /* CURLINC_SYSTEM_H */ diff --git a/libs/curl/include/curl/typecheck-gcc.h b/libs/curl/include/curl/typecheck-gcc.h index 46f92d728..03c84fc85 100644 --- a/libs/curl/include/curl/typecheck-gcc.h +++ b/libs/curl/include/curl/typecheck-gcc.h @@ -1,5 +1,5 @@ -#ifndef __CURL_TYPECHECK_GCC_H -#define __CURL_TYPECHECK_GCC_H +#ifndef CURLINC_TYPECHECK_GCC_H +#define CURLINC_TYPECHECK_GCC_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -25,10 +25,10 @@ /* wraps curl_easy_setopt() with typechecking */ /* To add a new kind of warning, add an - * if(_curl_is_sometype_option(_curl_opt)) - * if(!_curl_is_sometype(value)) + * if(curlcheck_sometype_option(_curl_opt)) + * if(!curlcheck_sometype(value)) * _curl_easy_setopt_err_sometype(); - * block and define _curl_is_sometype_option, _curl_is_sometype and + * block and define curlcheck_sometype_option, curlcheck_sometype and * _curl_easy_setopt_err_sometype below * * NOTE: We use two nested 'if' statements here instead of the && operator, in @@ -38,99 +38,115 @@ * To add an option that uses the same type as an existing option, you'll just * need to extend the appropriate _curl_*_option macro */ -#define curl_easy_setopt(handle, option, value) \ -__extension__ ({ \ - __typeof__ (option) _curl_opt = option; \ - if (__builtin_constant_p(_curl_opt)) { \ - if (_curl_is_long_option(_curl_opt)) \ - if (!_curl_is_long(value)) \ - _curl_easy_setopt_err_long(); \ - if (_curl_is_off_t_option(_curl_opt)) \ - if (!_curl_is_off_t(value)) \ - _curl_easy_setopt_err_curl_off_t(); \ - if (_curl_is_string_option(_curl_opt)) \ - if (!_curl_is_string(value)) \ - _curl_easy_setopt_err_string(); \ - if (_curl_is_write_cb_option(_curl_opt)) \ - if (!_curl_is_write_cb(value)) \ - _curl_easy_setopt_err_write_callback(); \ - if ((_curl_opt) == CURLOPT_READFUNCTION) \ - if (!_curl_is_read_cb(value)) \ - _curl_easy_setopt_err_read_cb(); \ - if ((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ - if (!_curl_is_ioctl_cb(value)) \ - _curl_easy_setopt_err_ioctl_cb(); \ - if ((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ - if (!_curl_is_sockopt_cb(value)) \ - _curl_easy_setopt_err_sockopt_cb(); \ - if ((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ - if (!_curl_is_opensocket_cb(value)) \ - _curl_easy_setopt_err_opensocket_cb(); \ - if ((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ - if (!_curl_is_progress_cb(value)) \ - _curl_easy_setopt_err_progress_cb(); \ - if ((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ - if (!_curl_is_debug_cb(value)) \ - _curl_easy_setopt_err_debug_cb(); \ - if ((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ - if (!_curl_is_ssl_ctx_cb(value)) \ - _curl_easy_setopt_err_ssl_ctx_cb(); \ - if (_curl_is_conv_cb_option(_curl_opt)) \ - if (!_curl_is_conv_cb(value)) \ - _curl_easy_setopt_err_conv_cb(); \ - if ((_curl_opt) == CURLOPT_SEEKFUNCTION) \ - if (!_curl_is_seek_cb(value)) \ - _curl_easy_setopt_err_seek_cb(); \ - if (_curl_is_cb_data_option(_curl_opt)) \ - if (!_curl_is_cb_data(value)) \ - _curl_easy_setopt_err_cb_data(); \ - if ((_curl_opt) == CURLOPT_ERRORBUFFER) \ - if (!_curl_is_error_buffer(value)) \ - _curl_easy_setopt_err_error_buffer(); \ - if ((_curl_opt) == CURLOPT_STDERR) \ - if (!_curl_is_FILE(value)) \ - _curl_easy_setopt_err_FILE(); \ - if (_curl_is_postfields_option(_curl_opt)) \ - if (!_curl_is_postfields(value)) \ - _curl_easy_setopt_err_postfields(); \ - if ((_curl_opt) == CURLOPT_HTTPPOST) \ - if (!_curl_is_arr((value), struct curl_httppost)) \ - _curl_easy_setopt_err_curl_httpost(); \ - if (_curl_is_slist_option(_curl_opt)) \ - if (!_curl_is_arr((value), struct curl_slist)) \ - _curl_easy_setopt_err_curl_slist(); \ - if ((_curl_opt) == CURLOPT_SHARE) \ - if (!_curl_is_ptr((value), CURLSH)) \ - _curl_easy_setopt_err_CURLSH(); \ - } \ - curl_easy_setopt(handle, _curl_opt, value); \ -}) +#define curl_easy_setopt(handle, option, value) \ + __extension__({ \ + __typeof__(option) _curl_opt = option; \ + if(__builtin_constant_p(_curl_opt)) { \ + if(curlcheck_long_option(_curl_opt)) \ + if(!curlcheck_long(value)) \ + _curl_easy_setopt_err_long(); \ + if(curlcheck_off_t_option(_curl_opt)) \ + if(!curlcheck_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if(curlcheck_string_option(_curl_opt)) \ + if(!curlcheck_string(value)) \ + _curl_easy_setopt_err_string(); \ + if(curlcheck_write_cb_option(_curl_opt)) \ + if(!curlcheck_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \ + if(!curlcheck_resolver_start_callback(value)) \ + _curl_easy_setopt_err_resolver_start_callback(); \ + if((_curl_opt) == CURLOPT_READFUNCTION) \ + if(!curlcheck_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \ + if(!curlcheck_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \ + if(!curlcheck_sockopt_cb(value)) \ + _curl_easy_setopt_err_sockopt_cb(); \ + if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \ + if(!curlcheck_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \ + if(!curlcheck_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \ + if(!curlcheck_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \ + if(!curlcheck_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if(curlcheck_conv_cb_option(_curl_opt)) \ + if(!curlcheck_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if((_curl_opt) == CURLOPT_SEEKFUNCTION) \ + if(!curlcheck_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if(curlcheck_cb_data_option(_curl_opt)) \ + if(!curlcheck_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if((_curl_opt) == CURLOPT_ERRORBUFFER) \ + if(!curlcheck_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if((_curl_opt) == CURLOPT_STDERR) \ + if(!curlcheck_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if(curlcheck_postfields_option(_curl_opt)) \ + if(!curlcheck_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if((_curl_opt) == CURLOPT_HTTPPOST) \ + if(!curlcheck_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if((_curl_opt) == CURLOPT_MIMEPOST) \ + if(!curlcheck_ptr((value), curl_mime)) \ + _curl_easy_setopt_err_curl_mimepost(); \ + if(curlcheck_slist_option(_curl_opt)) \ + if(!curlcheck_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if((_curl_opt) == CURLOPT_SHARE) \ + if(!curlcheck_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ + }) /* wraps curl_easy_getinfo() with typechecking */ -/* FIXME: don't allow const pointers */ -#define curl_easy_getinfo(handle, info, arg) \ -__extension__ ({ \ - __typeof__ (info) _curl_info = info; \ - if (__builtin_constant_p(_curl_info)) { \ - if (_curl_is_string_info(_curl_info)) \ - if (!_curl_is_arr((arg), char *)) \ - _curl_easy_getinfo_err_string(); \ - if (_curl_is_long_info(_curl_info)) \ - if (!_curl_is_arr((arg), long)) \ - _curl_easy_getinfo_err_long(); \ - if (_curl_is_double_info(_curl_info)) \ - if (!_curl_is_arr((arg), double)) \ - _curl_easy_getinfo_err_double(); \ - if (_curl_is_slist_info(_curl_info)) \ - if (!_curl_is_arr((arg), struct curl_slist *)) \ - _curl_easy_getinfo_err_curl_slist(); \ - } \ - curl_easy_getinfo(handle, _curl_info, arg); \ -}) +#define curl_easy_getinfo(handle, info, arg) \ + __extension__({ \ + __typeof__(info) _curl_info = info; \ + if(__builtin_constant_p(_curl_info)) { \ + if(curlcheck_string_info(_curl_info)) \ + if(!curlcheck_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if(curlcheck_long_info(_curl_info)) \ + if(!curlcheck_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if(curlcheck_double_info(_curl_info)) \ + if(!curlcheck_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if(curlcheck_slist_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + if(curlcheck_tlssessioninfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ + _curl_easy_getinfo_err_curl_tlssesssioninfo(); \ + if(curlcheck_certinfo_info(_curl_info)) \ + if(!curlcheck_arr((arg), struct curl_certinfo *)) \ + _curl_easy_getinfo_err_curl_certinfo(); \ + if(curlcheck_socket_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_socket_t)) \ + _curl_easy_getinfo_err_curl_socket(); \ + if(curlcheck_off_t_info(_curl_info)) \ + if(!curlcheck_arr((arg), curl_off_t)) \ + _curl_easy_getinfo_err_curl_off_t(); \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ + }) -/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(), - * for now just make sure that the functions are called with three - * arguments +/* + * For now, just make sure that the functions are called with three arguments */ #define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) #define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) @@ -140,61 +156,84 @@ __extension__ ({ \ * functions */ /* To define a new warning, use _CURL_WARNING(identifier, "message") */ -#define _CURL_WARNING(id, message) \ - static void __attribute__((warning(message))) __attribute__((unused)) \ - __attribute__((noinline)) id(void) { __asm__(""); } +#define CURLWARNING(id, message) \ + static void __attribute__((__warning__(message))) \ + __attribute__((__unused__)) __attribute__((__noinline__)) \ + id(void) { __asm__(""); } -_CURL_WARNING(_curl_easy_setopt_err_long, +CURLWARNING(_curl_easy_setopt_err_long, "curl_easy_setopt expects a long argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_curl_off_t, +CURLWARNING(_curl_easy_setopt_err_curl_off_t, "curl_easy_setopt expects a curl_off_t argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_string, - "curl_easy_setopt expects a string (char* or char[]) argument for this option" +CURLWARNING(_curl_easy_setopt_err_string, + "curl_easy_setopt expects a " + "string ('char *' or char[]) argument for this option" ) -_CURL_WARNING(_curl_easy_setopt_err_write_callback, +CURLWARNING(_curl_easy_setopt_err_write_callback, "curl_easy_setopt expects a curl_write_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_read_cb, - "curl_easy_setopt expects a curl_read_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb, - "curl_easy_setopt expects a curl_ioctl_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb, - "curl_easy_setopt expects a curl_sockopt_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb, - "curl_easy_setopt expects a curl_opensocket_callback argument for this option" +CURLWARNING(_curl_easy_setopt_err_resolver_start_callback, + "curl_easy_setopt expects a " + "curl_resolver_start_callback argument for this option" ) -_CURL_WARNING(_curl_easy_setopt_err_progress_cb, +CURLWARNING(_curl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument for this option") +CURLWARNING(_curl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a " + "curl_opensocket_callback argument for this option" + ) +CURLWARNING(_curl_easy_setopt_err_progress_cb, "curl_easy_setopt expects a curl_progress_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_debug_cb, +CURLWARNING(_curl_easy_setopt_err_debug_cb, "curl_easy_setopt expects a curl_debug_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb, +CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb, "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_conv_cb, +CURLWARNING(_curl_easy_setopt_err_conv_cb, "curl_easy_setopt expects a curl_conv_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_seek_cb, +CURLWARNING(_curl_easy_setopt_err_seek_cb, "curl_easy_setopt expects a curl_seek_callback argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_cb_data, - "curl_easy_setopt expects a private data pointer as argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_error_buffer, - "curl_easy_setopt expects a char buffer of CURL_ERROR_SIZE as argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_FILE, - "curl_easy_setopt expects a FILE* argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_postfields, - "curl_easy_setopt expects a void* or char* argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, - "curl_easy_setopt expects a struct curl_httppost* argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_curl_slist, - "curl_easy_setopt expects a struct curl_slist* argument for this option") -_CURL_WARNING(_curl_easy_setopt_err_CURLSH, +CURLWARNING(_curl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a " + "private data pointer as argument for this option") +CURLWARNING(_curl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a " + "char buffer of CURL_ERROR_SIZE as argument for this option") +CURLWARNING(_curl_easy_setopt_err_FILE, + "curl_easy_setopt expects a 'FILE *' argument for this option") +CURLWARNING(_curl_easy_setopt_err_postfields, + "curl_easy_setopt expects a 'void *' or 'char *' argument for this option") +CURLWARNING(_curl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a 'struct curl_httppost *' " + "argument for this option") +CURLWARNING(_curl_easy_setopt_err_curl_mimepost, + "curl_easy_setopt expects a 'curl_mime *' " + "argument for this option") +CURLWARNING(_curl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a 'struct curl_slist *' argument for this option") +CURLWARNING(_curl_easy_setopt_err_CURLSH, "curl_easy_setopt expects a CURLSH* argument for this option") -_CURL_WARNING(_curl_easy_getinfo_err_string, - "curl_easy_getinfo expects a pointer to char * for this info") -_CURL_WARNING(_curl_easy_getinfo_err_long, +CURLWARNING(_curl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to 'char *' for this info") +CURLWARNING(_curl_easy_getinfo_err_long, "curl_easy_getinfo expects a pointer to long for this info") -_CURL_WARNING(_curl_easy_getinfo_err_double, +CURLWARNING(_curl_easy_getinfo_err_double, "curl_easy_getinfo expects a pointer to double for this info") -_CURL_WARNING(_curl_easy_getinfo_err_curl_slist, - "curl_easy_getinfo expects a pointer to struct curl_slist * for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_tlssessioninfo *' for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_certinfo, + "curl_easy_getinfo expects a pointer to " + "'struct curl_certinfo *' for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_socket, + "curl_easy_getinfo expects a pointer to curl_socket_t for this info") +CURLWARNING(_curl_easy_getinfo_err_curl_off_t, + "curl_easy_getinfo expects a pointer to curl_off_t for this info") /* groups of curl_easy_setops options that take the same type of argument */ @@ -205,131 +244,188 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, */ /* evaluates to true if option takes a long argument */ -#define _curl_is_long_option(option) \ +#define curlcheck_long_option(option) \ (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) -#define _curl_is_off_t_option(option) \ +#define curlcheck_off_t_option(option) \ ((option) > CURLOPTTYPE_OFF_T) /* evaluates to true if option takes a char* argument */ -#define _curl_is_string_option(option) \ - ((option) == CURLOPT_URL || \ - (option) == CURLOPT_PROXY || \ - (option) == CURLOPT_INTERFACE || \ - (option) == CURLOPT_NETRC_FILE || \ - (option) == CURLOPT_USERPWD || \ - (option) == CURLOPT_USERNAME || \ - (option) == CURLOPT_PASSWORD || \ - (option) == CURLOPT_PROXYUSERPWD || \ - (option) == CURLOPT_PROXYUSERNAME || \ - (option) == CURLOPT_PROXYPASSWORD || \ - (option) == CURLOPT_NOPROXY || \ +#define curlcheck_string_option(option) \ + ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ (option) == CURLOPT_ACCEPT_ENCODING || \ - (option) == CURLOPT_REFERER || \ - (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_ALTSVC || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ (option) == CURLOPT_COOKIE || \ (option) == CURLOPT_COOKIEFILE || \ (option) == CURLOPT_COOKIEJAR || \ (option) == CURLOPT_COOKIELIST || \ - (option) == CURLOPT_FTPPORT || \ - (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ - (option) == CURLOPT_FTP_ACCOUNT || \ - (option) == CURLOPT_RANGE || \ - (option) == CURLOPT_CUSTOMREQUEST || \ - (option) == CURLOPT_SSLCERT || \ - (option) == CURLOPT_SSLCERTTYPE || \ - (option) == CURLOPT_SSLKEY || \ - (option) == CURLOPT_SSLKEYTYPE || \ - (option) == CURLOPT_KEYPASSWD || \ - (option) == CURLOPT_SSLENGINE || \ - (option) == CURLOPT_CAINFO || \ - (option) == CURLOPT_CAPATH || \ - (option) == CURLOPT_RANDOM_FILE || \ - (option) == CURLOPT_EGDSOCKET || \ - (option) == CURLOPT_SSL_CIPHER_LIST || \ - (option) == CURLOPT_KRBLEVEL || \ - (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ - (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ - (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_DEFAULT_PROTOCOL || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_DOH_URL || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_INTERFACE || \ (option) == CURLOPT_ISSUERCERT || \ - (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ - (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + (option) == CURLOPT_MAIL_AUTH || \ (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PRE_PROXY || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_PROXY_CAINFO || \ + (option) == CURLOPT_PROXY_CAPATH || \ + (option) == CURLOPT_PROXY_CRLFILE || \ + (option) == CURLOPT_PROXY_KEYPASSWD || \ + (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PROXY_SERVICE_NAME || \ + (option) == CURLOPT_PROXY_SSLCERT || \ + (option) == CURLOPT_PROXY_SSLCERTTYPE || \ + (option) == CURLOPT_PROXY_SSLKEY || \ + (option) == CURLOPT_PROXY_SSLKEYTYPE || \ + (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \ + (option) == CURLOPT_PROXY_TLS13_CIPHERS || \ + (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \ + (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_REQUEST_TARGET || \ (option) == CURLOPT_RTSP_SESSION_ID || \ (option) == CURLOPT_RTSP_STREAM_URI || \ (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_SASL_AUTHZID || \ + (option) == CURLOPT_SERVICE_NAME || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_TLS13_CIPHERS || \ + (option) == CURLOPT_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_TLSAUTH_TYPE || \ + (option) == CURLOPT_TLSAUTH_USERNAME || \ + (option) == CURLOPT_UNIX_SOCKET_PATH || \ + (option) == CURLOPT_URL || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_XOAUTH2_BEARER || \ 0) /* evaluates to true if option takes a curl_write_callback argument */ -#define _curl_is_write_cb_option(option) \ - ((option) == CURLOPT_HEADERFUNCTION || \ +#define curlcheck_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ (option) == CURLOPT_WRITEFUNCTION) /* evaluates to true if option takes a curl_conv_callback argument */ -#define _curl_is_conv_cb_option(option) \ - ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ - (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ +#define curlcheck_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) /* evaluates to true if option takes a data argument to pass to a callback */ -#define _curl_is_cb_data_option(option) \ - ((option) == CURLOPT_WRITEDATA || \ - (option) == CURLOPT_READDATA || \ - (option) == CURLOPT_IOCTLDATA || \ - (option) == CURLOPT_SOCKOPTDATA || \ - (option) == CURLOPT_OPENSOCKETDATA || \ - (option) == CURLOPT_PROGRESSDATA || \ - (option) == CURLOPT_WRITEHEADER || \ +#define curlcheck_cb_data_option(option) \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ (option) == CURLOPT_DEBUGDATA || \ - (option) == CURLOPT_SSL_CTX_DATA || \ - (option) == CURLOPT_SEEKDATA || \ - (option) == CURLOPT_PRIVATE || \ - (option) == CURLOPT_SSH_KEYDATA || \ - (option) == CURLOPT_INTERLEAVEDATA || \ - (option) == CURLOPT_CHUNK_DATA || \ (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PRIVATE || \ + (option) == CURLOPT_PROGRESSDATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_SSH_KEYDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_RESOLVER_START_DATA || \ + (option) == CURLOPT_TRAILERDATA || \ 0) /* evaluates to true if option takes a POST data argument (void* or char*) */ -#define _curl_is_postfields_option(option) \ +#define curlcheck_postfields_option(option) \ ((option) == CURLOPT_POSTFIELDS || \ (option) == CURLOPT_COPYPOSTFIELDS || \ 0) /* evaluates to true if option takes a struct curl_slist * argument */ -#define _curl_is_slist_option(option) \ - ((option) == CURLOPT_HTTPHEADER || \ - (option) == CURLOPT_HTTP200ALIASES || \ - (option) == CURLOPT_QUOTE || \ +#define curlcheck_slist_option(option) \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ (option) == CURLOPT_POSTQUOTE || \ (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ (option) == CURLOPT_TELNETOPTIONS || \ - (option) == CURLOPT_MAIL_RCPT || \ + (option) == CURLOPT_CONNECT_TO || \ 0) /* groups of curl_easy_getinfo infos that take the same type of argument */ /* evaluates to true if info expects a pointer to char * argument */ -#define _curl_is_string_info(info) \ +#define curlcheck_string_info(info) \ (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG) /* evaluates to true if info expects a pointer to long argument */ -#define _curl_is_long_info(info) \ +#define curlcheck_long_info(info) \ (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) /* evaluates to true if info expects a pointer to double argument */ -#define _curl_is_double_info(info) \ +#define curlcheck_double_info(info) \ (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) /* true if info expects a pointer to struct curl_slist * argument */ -#define _curl_is_slist_info(info) \ - (CURLINFO_SLIST < (info)) +#define curlcheck_slist_info(info) \ + (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST)) + +/* true if info expects a pointer to struct curl_tlssessioninfo * argument */ +#define curlcheck_tlssessioninfo_info(info) \ + (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION)) + +/* true if info expects a pointer to struct curl_certinfo * argument */ +#define curlcheck_certinfo_info(info) ((info) == CURLINFO_CERTINFO) + +/* true if info expects a pointer to struct curl_socket_t argument */ +#define curlcheck_socket_info(info) \ + (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T) + +/* true if info expects a pointer to curl_off_t argument */ +#define curlcheck_off_t_info(info) \ + (CURLINFO_OFF_T < (info)) /* typecheck helpers -- check whether given expression has requested type*/ -/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros, +/* For pointers, you can use the curlcheck_ptr/curlcheck_arr macros, * otherwise define a new macro. Search for __builtin_types_compatible_p * in the GCC manual. * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is @@ -338,36 +434,36 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, * == or whatsoever. */ -/* XXX: should evaluate to true iff expr is a pointer */ -#define _curl_is_any_ptr(expr) \ - (sizeof(expr) == sizeof(void*)) +/* XXX: should evaluate to true if expr is a pointer */ +#define curlcheck_any_ptr(expr) \ + (sizeof(expr) == sizeof(void *)) /* evaluates to true if expr is NULL */ /* XXX: must not evaluate expr, so this check is not accurate */ -#define _curl_is_NULL(expr) \ +#define curlcheck_NULL(expr) \ (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) /* evaluates to true if expr is type*, const type* or NULL */ -#define _curl_is_ptr(expr, type) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), type *) || \ +#define curlcheck_ptr(expr, type) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ __builtin_types_compatible_p(__typeof__(expr), const type *)) /* evaluates to true if expr is one of type[], type*, NULL or const type* */ -#define _curl_is_arr(expr, type) \ - (_curl_is_ptr((expr), type) || \ +#define curlcheck_arr(expr, type) \ + (curlcheck_ptr((expr), type) || \ __builtin_types_compatible_p(__typeof__(expr), type [])) /* evaluates to true if expr is a string */ -#define _curl_is_string(expr) \ - (_curl_is_arr((expr), char) || \ - _curl_is_arr((expr), signed char) || \ - _curl_is_arr((expr), unsigned char)) +#define curlcheck_string(expr) \ + (curlcheck_arr((expr), char) || \ + curlcheck_arr((expr), signed char) || \ + curlcheck_arr((expr), unsigned char)) /* evaluates to true if expr is a long (no matter the signedness) * XXX: for now, int is also accepted (and therefore short and char, which * are promoted to int when passed to a variadic function) */ -#define _curl_is_long(expr) \ +#define curlcheck_long(expr) \ (__builtin_types_compatible_p(__typeof__(expr), long) || \ __builtin_types_compatible_p(__typeof__(expr), signed long) || \ __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ @@ -382,175 +478,194 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, __builtin_types_compatible_p(__typeof__(expr), unsigned char)) /* evaluates to true if expr is of type curl_off_t */ -#define _curl_is_off_t(expr) \ +#define curlcheck_off_t(expr) \ (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) /* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ /* XXX: also check size of an char[] array? */ -#define _curl_is_error_buffer(expr) \ - (__builtin_types_compatible_p(__typeof__(expr), char *) || \ +#define curlcheck_error_buffer(expr) \ + (curlcheck_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), char *) || \ __builtin_types_compatible_p(__typeof__(expr), char[])) /* evaluates to true if expr is of type (const) void* or (const) FILE* */ #if 0 -#define _curl_is_cb_data(expr) \ - (_curl_is_ptr((expr), void) || \ - _curl_is_ptr((expr), FILE)) +#define curlcheck_cb_data(expr) \ + (curlcheck_ptr((expr), void) || \ + curlcheck_ptr((expr), FILE)) #else /* be less strict */ -#define _curl_is_cb_data(expr) \ - _curl_is_any_ptr(expr) +#define curlcheck_cb_data(expr) \ + curlcheck_any_ptr(expr) #endif /* evaluates to true if expr is of type FILE* */ -#define _curl_is_FILE(expr) \ - (__builtin_types_compatible_p(__typeof__(expr), FILE *)) +#define curlcheck_FILE(expr) \ + (curlcheck_NULL(expr) || \ + (__builtin_types_compatible_p(__typeof__(expr), FILE *))) /* evaluates to true if expr can be passed as POST data (void* or char*) */ -#define _curl_is_postfields(expr) \ - (_curl_is_ptr((expr), void) || \ - _curl_is_arr((expr), char)) +#define curlcheck_postfields(expr) \ + (curlcheck_ptr((expr), void) || \ + curlcheck_arr((expr), char) || \ + curlcheck_arr((expr), unsigned char)) -/* FIXME: the whole callback checking is messy... - * The idea is to tolerate char vs. void and const vs. not const - * pointers in arguments at least - */ /* helper: __builtin_types_compatible_p distinguishes between functions and * function pointers, hide it */ -#define _curl_callback_compatible(func, type) \ - (__builtin_types_compatible_p(__typeof__(func), type) || \ - __builtin_types_compatible_p(__typeof__(func), type*)) +#define curlcheck_cb_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ + __builtin_types_compatible_p(__typeof__(func) *, type)) + +/* evaluates to true if expr is of type curl_resolver_start_callback */ +#define curlcheck_resolver_start_callback(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_resolver_start_callback)) /* evaluates to true if expr is of type curl_read_callback or "similar" */ -#define _curl_is_read_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), __typeof__(fread)) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_read_callback) || \ - _curl_callback_compatible((expr), _curl_read_callback1) || \ - _curl_callback_compatible((expr), _curl_read_callback2) || \ - _curl_callback_compatible((expr), _curl_read_callback3) || \ - _curl_callback_compatible((expr), _curl_read_callback4) || \ - _curl_callback_compatible((expr), _curl_read_callback5) || \ - _curl_callback_compatible((expr), _curl_read_callback6)) -typedef size_t (_curl_read_callback1)(char *, size_t, size_t, void*); -typedef size_t (_curl_read_callback2)(char *, size_t, size_t, const void*); -typedef size_t (_curl_read_callback3)(char *, size_t, size_t, FILE*); -typedef size_t (_curl_read_callback4)(void *, size_t, size_t, void*); -typedef size_t (_curl_read_callback5)(void *, size_t, size_t, const void*); -typedef size_t (_curl_read_callback6)(void *, size_t, size_t, FILE*); +#define curlcheck_read_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), __typeof__(fread) *) || \ + curlcheck_cb_compatible((expr), curl_read_callback) || \ + curlcheck_cb_compatible((expr), _curl_read_callback1) || \ + curlcheck_cb_compatible((expr), _curl_read_callback2) || \ + curlcheck_cb_compatible((expr), _curl_read_callback3) || \ + curlcheck_cb_compatible((expr), _curl_read_callback4) || \ + curlcheck_cb_compatible((expr), _curl_read_callback5) || \ + curlcheck_cb_compatible((expr), _curl_read_callback6)) +typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *); +typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *); +typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *); +typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_write_callback or "similar" */ -#define _curl_is_write_cb(expr) \ - (_curl_is_read_cb(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), __typeof__(fwrite)) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_write_callback) || \ - _curl_callback_compatible((expr), _curl_write_callback1) || \ - _curl_callback_compatible((expr), _curl_write_callback2) || \ - _curl_callback_compatible((expr), _curl_write_callback3) || \ - _curl_callback_compatible((expr), _curl_write_callback4) || \ - _curl_callback_compatible((expr), _curl_write_callback5) || \ - _curl_callback_compatible((expr), _curl_write_callback6)) -typedef size_t (_curl_write_callback1)(const char *, size_t, size_t, void*); -typedef size_t (_curl_write_callback2)(const char *, size_t, size_t, - const void*); -typedef size_t (_curl_write_callback3)(const char *, size_t, size_t, FILE*); -typedef size_t (_curl_write_callback4)(const void *, size_t, size_t, void*); -typedef size_t (_curl_write_callback5)(const void *, size_t, size_t, - const void*); -typedef size_t (_curl_write_callback6)(const void *, size_t, size_t, FILE*); +#define curlcheck_write_cb(expr) \ + (curlcheck_read_cb(expr) || \ + curlcheck_cb_compatible((expr), __typeof__(fwrite) *) || \ + curlcheck_cb_compatible((expr), curl_write_callback) || \ + curlcheck_cb_compatible((expr), _curl_write_callback1) || \ + curlcheck_cb_compatible((expr), _curl_write_callback2) || \ + curlcheck_cb_compatible((expr), _curl_write_callback3) || \ + curlcheck_cb_compatible((expr), _curl_write_callback4) || \ + curlcheck_cb_compatible((expr), _curl_write_callback5) || \ + curlcheck_cb_compatible((expr), _curl_write_callback6)) +typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *); +typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *); +typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t, + const void *); +typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ -#define _curl_is_ioctl_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_ioctl_callback) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback1) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ - _curl_callback_compatible((expr), _curl_ioctl_callback4)) -typedef curlioerr (_curl_ioctl_callback1)(CURL *, int, void*); -typedef curlioerr (_curl_ioctl_callback2)(CURL *, int, const void*); -typedef curlioerr (_curl_ioctl_callback3)(CURL *, curliocmd, void*); -typedef curlioerr (_curl_ioctl_callback4)(CURL *, curliocmd, const void*); +#define curlcheck_ioctl_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_ioctl_callback) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback1) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback2) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback3) || \ + curlcheck_cb_compatible((expr), _curl_ioctl_callback4)) +typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *); +typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *); +typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *); +typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *); /* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ -#define _curl_is_sockopt_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_sockopt_callback) || \ - _curl_callback_compatible((expr), _curl_sockopt_callback1) || \ - _curl_callback_compatible((expr), _curl_sockopt_callback2)) -typedef int (_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); -typedef int (_curl_sockopt_callback2)(const void *, curl_socket_t, +#define curlcheck_sockopt_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_sockopt_callback) || \ + curlcheck_cb_compatible((expr), _curl_sockopt_callback1) || \ + curlcheck_cb_compatible((expr), _curl_sockopt_callback2)) +typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t, curlsocktype); -/* evaluates to true if expr is of type curl_opensocket_callback or "similar" */ -#define _curl_is_opensocket_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_opensocket_callback) ||\ - _curl_callback_compatible((expr), _curl_opensocket_callback1) || \ - _curl_callback_compatible((expr), _curl_opensocket_callback2) || \ - _curl_callback_compatible((expr), _curl_opensocket_callback3) || \ - _curl_callback_compatible((expr), _curl_opensocket_callback4)) -typedef curl_socket_t (_curl_opensocket_callback1) +/* evaluates to true if expr is of type curl_opensocket_callback or + "similar" */ +#define curlcheck_opensocket_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_opensocket_callback) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback1) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback2) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback3) || \ + curlcheck_cb_compatible((expr), _curl_opensocket_callback4)) +typedef curl_socket_t (*_curl_opensocket_callback1) (void *, curlsocktype, struct curl_sockaddr *); -typedef curl_socket_t (_curl_opensocket_callback2) +typedef curl_socket_t (*_curl_opensocket_callback2) (void *, curlsocktype, const struct curl_sockaddr *); -typedef curl_socket_t (_curl_opensocket_callback3) +typedef curl_socket_t (*_curl_opensocket_callback3) (const void *, curlsocktype, struct curl_sockaddr *); -typedef curl_socket_t (_curl_opensocket_callback4) +typedef curl_socket_t (*_curl_opensocket_callback4) (const void *, curlsocktype, const struct curl_sockaddr *); /* evaluates to true if expr is of type curl_progress_callback or "similar" */ -#define _curl_is_progress_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_progress_callback) || \ - _curl_callback_compatible((expr), _curl_progress_callback1) || \ - _curl_callback_compatible((expr), _curl_progress_callback2)) -typedef int (_curl_progress_callback1)(void *, +#define curlcheck_progress_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_progress_callback) || \ + curlcheck_cb_compatible((expr), _curl_progress_callback1) || \ + curlcheck_cb_compatible((expr), _curl_progress_callback2)) +typedef int (*_curl_progress_callback1)(void *, double, double, double, double); -typedef int (_curl_progress_callback2)(const void *, +typedef int (*_curl_progress_callback2)(const void *, double, double, double, double); /* evaluates to true if expr is of type curl_debug_callback or "similar" */ -#define _curl_is_debug_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_debug_callback) || \ - _curl_callback_compatible((expr), _curl_debug_callback1) || \ - _curl_callback_compatible((expr), _curl_debug_callback2) || \ - _curl_callback_compatible((expr), _curl_debug_callback3) || \ - _curl_callback_compatible((expr), _curl_debug_callback4)) -typedef int (_curl_debug_callback1) (CURL *, +#define curlcheck_debug_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_debug_callback) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback1) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback2) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback3) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback4) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback5) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback6) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback7) || \ + curlcheck_cb_compatible((expr), _curl_debug_callback8)) +typedef int (*_curl_debug_callback1) (CURL *, curl_infotype, char *, size_t, void *); -typedef int (_curl_debug_callback2) (CURL *, +typedef int (*_curl_debug_callback2) (CURL *, curl_infotype, char *, size_t, const void *); -typedef int (_curl_debug_callback3) (CURL *, +typedef int (*_curl_debug_callback3) (CURL *, curl_infotype, const char *, size_t, void *); -typedef int (_curl_debug_callback4) (CURL *, +typedef int (*_curl_debug_callback4) (CURL *, curl_infotype, const char *, size_t, const void *); +typedef int (*_curl_debug_callback5) (CURL *, + curl_infotype, unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback6) (CURL *, + curl_infotype, unsigned char *, size_t, const void *); +typedef int (*_curl_debug_callback7) (CURL *, + curl_infotype, const unsigned char *, size_t, void *); +typedef int (*_curl_debug_callback8) (CURL *, + curl_infotype, const unsigned char *, size_t, const void *); /* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ /* this is getting even messier... */ -#define _curl_is_ssl_ctx_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_ssl_ctx_callback) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \ - _curl_callback_compatible((expr), _curl_ssl_ctx_callback8)) -typedef CURLcode (_curl_ssl_ctx_callback1)(CURL *, void *, void *); -typedef CURLcode (_curl_ssl_ctx_callback2)(CURL *, void *, const void *); -typedef CURLcode (_curl_ssl_ctx_callback3)(CURL *, const void *, void *); -typedef CURLcode (_curl_ssl_ctx_callback4)(CURL *, const void *, const void *); +#define curlcheck_ssl_ctx_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_ssl_ctx_callback) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback1) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback2) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback3) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback4) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback5) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback6) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback7) || \ + curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback8)) +typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *, + const void *); #ifdef HEADER_SSL_H /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX * this will of course break if we're included before OpenSSL headers... */ -typedef CURLcode (_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *); -typedef CURLcode (_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *); -typedef CURLcode (_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *); -typedef CURLcode (_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *); +typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *); +typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *); +typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX, + const void *); #else typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; @@ -559,26 +674,26 @@ typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; #endif /* evaluates to true if expr is of type curl_conv_callback or "similar" */ -#define _curl_is_conv_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_conv_callback) || \ - _curl_callback_compatible((expr), _curl_conv_callback1) || \ - _curl_callback_compatible((expr), _curl_conv_callback2) || \ - _curl_callback_compatible((expr), _curl_conv_callback3) || \ - _curl_callback_compatible((expr), _curl_conv_callback4)) +#define curlcheck_conv_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_conv_callback) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback1) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback2) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback3) || \ + curlcheck_cb_compatible((expr), _curl_conv_callback4)) typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); /* evaluates to true if expr is of type curl_seek_callback or "similar" */ -#define _curl_is_seek_cb(expr) \ - (_curl_is_NULL(expr) || \ - __builtin_types_compatible_p(__typeof__(expr), curl_seek_callback) || \ - _curl_callback_compatible((expr), _curl_seek_callback1) || \ - _curl_callback_compatible((expr), _curl_seek_callback2)) +#define curlcheck_seek_cb(expr) \ + (curlcheck_NULL(expr) || \ + curlcheck_cb_compatible((expr), curl_seek_callback) || \ + curlcheck_cb_compatible((expr), _curl_seek_callback1) || \ + curlcheck_cb_compatible((expr), _curl_seek_callback2)) typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); -#endif /* __CURL_TYPECHECK_GCC_H */ +#endif /* CURLINC_TYPECHECK_GCC_H */ diff --git a/libs/curl/include/curl/urlapi.h b/libs/curl/include/curl/urlapi.h new file mode 100644 index 000000000..f2d06770d --- /dev/null +++ b/libs/curl/include/curl/urlapi.h @@ -0,0 +1,125 @@ +#ifndef CURLINC_URLAPI_H +#define CURLINC_URLAPI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2018 - 2019, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* the error codes for the URL API */ +typedef enum { + CURLUE_OK, + CURLUE_BAD_HANDLE, /* 1 */ + CURLUE_BAD_PARTPOINTER, /* 2 */ + CURLUE_MALFORMED_INPUT, /* 3 */ + CURLUE_BAD_PORT_NUMBER, /* 4 */ + CURLUE_UNSUPPORTED_SCHEME, /* 5 */ + CURLUE_URLDECODE, /* 6 */ + CURLUE_OUT_OF_MEMORY, /* 7 */ + CURLUE_USER_NOT_ALLOWED, /* 8 */ + CURLUE_UNKNOWN_PART, /* 9 */ + CURLUE_NO_SCHEME, /* 10 */ + CURLUE_NO_USER, /* 11 */ + CURLUE_NO_PASSWORD, /* 12 */ + CURLUE_NO_OPTIONS, /* 13 */ + CURLUE_NO_HOST, /* 14 */ + CURLUE_NO_PORT, /* 15 */ + CURLUE_NO_QUERY, /* 16 */ + CURLUE_NO_FRAGMENT /* 17 */ +} CURLUcode; + +typedef enum { + CURLUPART_URL, + CURLUPART_SCHEME, + CURLUPART_USER, + CURLUPART_PASSWORD, + CURLUPART_OPTIONS, + CURLUPART_HOST, + CURLUPART_PORT, + CURLUPART_PATH, + CURLUPART_QUERY, + CURLUPART_FRAGMENT, + CURLUPART_ZONEID /* added in 7.65.0 */ +} CURLUPart; + +#define CURLU_DEFAULT_PORT (1<<0) /* return default port number */ +#define CURLU_NO_DEFAULT_PORT (1<<1) /* act as if no port number was set, + if the port number matches the + default for the scheme */ +#define CURLU_DEFAULT_SCHEME (1<<2) /* return default scheme if + missing */ +#define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */ +#define CURLU_PATH_AS_IS (1<<4) /* leave dot sequences */ +#define CURLU_DISALLOW_USER (1<<5) /* no user+password allowed */ +#define CURLU_URLDECODE (1<<6) /* URL decode on get */ +#define CURLU_URLENCODE (1<<7) /* URL encode on set */ +#define CURLU_APPENDQUERY (1<<8) /* append a form style part */ +#define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ +#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the + scheme is unknown. */ + +typedef struct Curl_URL CURLU; + +/* + * curl_url() creates a new CURLU handle and returns a pointer to it. + * Must be freed with curl_url_cleanup(). + */ +CURL_EXTERN CURLU *curl_url(void); + +/* + * curl_url_cleanup() frees the CURLU handle and related resources used for + * the URL parsing. It will not free strings previously returned with the URL + * API. + */ +CURL_EXTERN void curl_url_cleanup(CURLU *handle); + +/* + * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new + * handle must also be freed with curl_url_cleanup(). + */ +CURL_EXTERN CURLU *curl_url_dup(CURLU *in); + +/* + * curl_url_get() extracts a specific part of the URL from a CURLU + * handle. Returns error code. The returned pointer MUST be freed with + * curl_free() afterwards. + */ +CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what, + char **part, unsigned int flags); + +/* + * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns + * error code. The passed in string will be copied. Passing a NULL instead of + * a part string, clears that part. + */ +CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, + const char *part, unsigned int flags); + + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* CURLINC_URLAPI_H */ diff --git a/libs/curl/lib32/libcurl.a b/libs/curl/lib32/libcurl.a index 2f5a29f62a792824da80e7fc18e6de2e00088c81..4e4907c5bf0637e86738c31f2d65488d4cf87548 100644 GIT binary patch literal 801608 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

gQw|Ag`1@x5p%Rf`vh;+ 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%0AQG#QpmN|JVjL)1gJu7Kk( z_*-g+&oPjA$5wd3)&m<4%<5|GTh(zA(@5uGa!r3^ZQMA8?4%QzM4YzO1KpR2t0+32 zs=H`GJtUsLs=KGJL;Y##!CCYU=OsLSMNj)x$9hD<0McfJE?Kf<`W&3?4R>Ai^VTt2 z)1MwVj2k!N2Z2w|SP^W7sZQmnWfUme! z3kJRwYig&X-AUJdJF;uCZPXg9hYsr$T$@d9kD{^|v7&3>KWTAZ^6Iw0IzJPQ3ZdW3xD1O@_iV;7r^tM(B$RhJ9pw(s51xK z`4bA9TuY^O6P%BO75gTzw#8red<$g!p3(=BRalf?CtAI4MiTHSF=m~&yJ@j;on`t2)Frc9v zXlslMEwtE%e%#{LSm;|8+6?G@BI!6Fqbm!&Y3X=wDTg5u=Y4=!Pa;kYAWiptK&sO? zAoU3|WJ7NNq-i_^=mQe>X+X;5O+X(MTq&w&RG?l!Ckd1Q^dW&xz*kP8TL5XgUj(G- ze%In2v$z?B9^Gs}N_QQglZD^2HuT>CY2KbkPrOV*Ym2$-r~MyasOg* z&s*HD0cpBCflSk_2V_bMkkJ~TQzVUJ(aL{Vpb9{of+%!q0ht(pG{yrKHwH*+&hvn@ z1or|`?Zz<2s&N}F)C@?=qut`VEOa%XDI&qWfTjvWuT7^4^fN%320b(>XL@K-spz3e z%jil#r%8+(0pUOLaVu`B=S_f=?$3afUlgksN;eIV^1IaHRszzvcU#=o0cpwn#NwU@ zq;X%dxYsT2Ow1u^Y8L^TCh~Ovnl8}iEp#s+mF0d5Jq}3q^0dYM9FV4#0(81?p|>cF z8$xTQ(7OR?+$k1!9-uQM#wRWA3P7668!hfuK$-?mqGyX8aqa-5(sN=za;-a8I zl?Gj$ zW?`Pn!t6=I&^{yGo|3@_(rEY)hLn1FX`PgXnVyBI&BE}zm(Hgz3$rc@BPW$gG%xjg zvS_}Wh523esq+YibWEPA8%Qs$cV%Ht%EDA1mp{g0ilB-YV??pSCnN zK|xPHS^ju33}wVLMr<9jefhM}w~-8c4(E!;vxo^=$^=pjNcax?`m_Yx_;AqH)v+=j zP-w1`_~=*1(_7{Ve9V-LGR!vB97aH*i%i!&q6`kRT3Xw@^n<7Hv8-GBI*dCfV_VeI zdzJB5)B>N`1D<`f@apXLeIzl(wQLqJ_*|KmJ=6~P`tWuU$(T&f88rFxvlBRDu^$H% zdey4Cr+Y;g44PQsj>W_D*8aB6&cE3p;f11Klc~|_(`R4~1n+Z}RiSC50=FaeWg|;` zQsqJyIpN*ekF&KF zWm%F9VidtC!23-_n*+MPh9y}O(iy@yBY4sBE7wk=XL8YH_zJt9eLi%=&t*p$7jNXZ zWD)E-UW~YyVU52RYdpsOrU3rA{3qRoEz47GZ4{J6-fECrI|=Klazcd0M7)t}LgUiI zg!RJjULUWmV|q9=;(~^4sr)~EgC4(HJjXZY1j=bxP>7AhVNO(A>|RrcY1bk~;`A$I zmMMI1HDIJRikb5eCW&EzuT$9~-d;xFH1XGQcMAf-hv;i;Y@kDB9hmGEjMwU{u9sa1 zN|$ESIEVgtNKWd&eE0D?>&lL&B?LvFEV{gmjp3Gg;>D8Tv8%qNSx?rM?)!2e<>YhV zy7vp1DaN{v(aw%(vSD3P(UmU_yPV|CLqCOx?@soYred6YHj4pQID#)8uPvoQQe_B3 zGO1EKV~v%9k4EH=aU={;z^vSck3M_Aj%h%r&93KO5pmw;Ed-t z@5mg@zg>dfw~15k6|heXu7ubpMbg^BU=eWjqh9~q1?R|N0kXvPs6C*L*GYsa9->x_Zv*Yt)GTcD01tk=(>(B z=8V@*W3EkV$(?S3ZpM>GyEpL;foet)BIZrxv3CoLS4fz^XAl$q8brKHwrdo_sbW7C zl6IK&orf@A<0SUPYVuCpf8g1~&cdOwEqv`^3+diO?kp$F1JKZmwnIfygQTYH8#F3h zIqGx9(M-*#)~O|F^&iPzOYl(Fuk{XkLG8LiA52R(8ME7c1S)vXV_1@*=5 zbtSC+fvU=Lp_p$;0l@IhLh^`vD<_Rae&CrGRJ8~q15|Chi_yG<8^(DU=NG|b zyk5k8lrmQw;xwp}w;PT^*lbnoHyf|Syw2AM8Xu!@qZwCfkULL{cV2q&&XeMGU6y9B zB!{1j_w4;W){eC_VT)OZ1X0~Q<|{|5>x({h+PYU?g;3%3cdN8~c$9x2EpMp{{UXSF zI=MKs??<-0!0S<@ijxUn=H{r;@Mf3mk&m%5KqG8)N)o?}4gR;S6;D2>wc^L_*0frI z!(M45>P|o#Bzi~r@%Yoyy;G0_U*_bq6^Bw=`I_*3^WF~_bGWz5OZI&H@7xQbh*+6C z7HuOch}cBSdMLcYmAjALjs{#)=n1(WF-6C6;DJP1m`(5-XX)8v<#YFJ4NLyB_++ z*T4St1HXFe_sQLN?EBv0B|I%bj*Mh=z`VnahsQ*GJXSniF_vr&MH6G8#KCCaEBLZ)J%|Jfkliv^9n*NtGdxM6>0TV- z+tmz)X{~8eo1G`v9z4z$sIF}F$aN)yUuN_5=6GTYs{k&1-;)U*CNqLJY#TTutK-s= zFm&pFC$=?=*CQu5Ofi)|UQd9_#%r3!>*Xs%zRCJYxJ8ro<+zn4>v=rmlw>`vgr_Cz zYj7)1*7N1AOxD-oR+X%8z^x`(-w2tx!eBnX^~DtTNg$Hmt36U5)ejvlD2kXmv&%Qq zWLHWE(KZeN2d?eo^;1Y@IV9^NXvVK}rMbD23>m4T4G0Ipn8ms*GKX49U8Car=E6ErH@g6dz)cEFfOHnC!QmxnRcc7aOkJW zD)f(-H%c#wW$;x(;k9o!EHeVnBk~o{c4Y6pu=3dVZSM;Q3l39ICLOm5v4h`dF@m=r z=_{Of_UyYc#44mr$~ddJa1EcrU3#b&#$`QSS72$ep9|TJgH6tu<>=$=%ao}&IEa%I zQO=BDO%V%DgF+K&)5~l2kf}` zj_m>|UnSP1*{o!_R|aGGAD>t=cVg_)i8b{TW3@R}^X5%hIkCKUVhnVk_$Vkc{rk*k zm!D&V9sD7!AK;#uUM)QYfx+^LHBA#^7f!4J%Y40kJZGw4!HPQx{OIpnI`IZK4Y+%N zW43rj@QdN$e&9|e0^Wt%pasTX3j7Pen?^!tX}$DH4%#8$%a9QH@hZpfcHruOt0ThG zy*+g(+BdPJKH!^3_3#74xfyXz#XVCG##2&x{-<+ja?XVOo3i!m+yFLF$h!lTVBg_+ z#y&Z*X8y$3eAVqx4*odN=b{|w+6nnAd3Z}IsSOW%WkW;IgyKF4NI8GaLQeszkho`~#%g{SSg6H9mjhytCgR)+Xr@3f z0s4@nb_meP0)^4%U^^IbJ_6`Wff9gb33MMIO|1b1qp7u9Xuv`n0BLF|K*}#4HBI?l z3P|~F19X;fJ^)DR-d*J7cPF3^BNV5`0Gc8Y_a0P9j8Z^T1vdjwRG^PoTnivwL0%20 zT4HRqblU)(Dlv8g(tP|Bkjngf8~Un+rlKxt45mcR5%Gen&@PMPh7rY;Td2-LTuai> zUJGrs(47|AZJ{#M1C7BJSD|8*xk9v}Q)tjaYz-B+$wCk>ZH+V6zHrja#BUJ4JSS?t z0`u`S25EduJ20b=g&5p$asBeHGz^DBY_)uve+Fhx8iqQ1ISunmV4{%OkHuBJSQ=&m zWMO;Z)0_g#80xQ&nF9{&l40yt7wx7x;+;pr;@qFTAKZ=fxTJ3qu1Hfrc}LY+ci@-x?ZXQAoIs(wo2v z9+!nVB@0uRg;|k>xjYN=nJf&ge!WYjcd{@%`#3!p&!l1Y;wiX7;(H-79`el*8!cEb zX_IwArzOxEH=!}Oz4z$3vZ9oyclP(SVqHiV@=Tw;uVqzN&x+Qr;29>FL2WQZ>>ltA zYO$e#)j5kh3~BoSj2G#UKQnP0e$&?BEd^RGmvwe!;jV1$jAMP11CdN7o&6>N_LrHN z>xgr0F$dZkz?dK&cGBM6kCoZJD{$Ov1{Q}5bo8y{F(=C3qsA&ERo4rB%c5&0e%4_M zQ+-9%Vc|F4QOl!T!Df-#q_XUE0aw?Ql{BzQN&-dJQ>q-%HMFs#^@?1~HIysRv5Hw9 zt0jx(%NaTa+j$Sk^ z?6|TS?7I*%h#nLt$coTQ;x!q+zs7p!Mx-;u&Pw1q+Q9{f$s#?+w^iF1NL>5AxH z09-HtV%>IT*h>-<%MzCQ80Q`lg+nku)h!tQIH`IQ_P(^Rw_>LYej58|%H(?zSq4uc z>MWrF>^LpU5xe8gWQm*wJ-N5Oj8sQtV~@La8OjgysO4KOA<;LM;}Od(b-Ud}Bfxb# zHx^5H)ks3JMOOw1mL=j$+p+?223HopiB+DugIE))+j}?DVoGl?CD6E9TC{R$XI%C)5pe^ARlNr9f{+F7$C5JG~wevm8#|AwSDrR~ld-X9)X1F?%uI0W3^d9|oj!6@ZkE_QQ(1(BfJFX0X-zpyLF010d!6ISX-AtT^s!;hI>)c?6KgIABBT@zyJDJs{3^MVxP2 z9GCBv%VQSWX`!D3QqCzr%H=)KFh2+I=nF_?UJXd|HejJBXz&0!uBRxp#X ziCovIKn^0zpQ~X_p$}KZ1p6{%QIK~P!X~G&I#Rv3?0J~sv~;|Oj~M`s>S5<$+6|4) z!?5Q91!r8{foBvauB);zz69P3#x0h{!+bdlvnvbpOcv(FEX;AXj6BZKEKGeGhI5{| z=NV)~q=vlg*_zRdF_{iveB)s==5LX}+)VphR%3WuXGiscqX1|koby0Y9ova{vbr0T z1X|)(^>)m_VY?VRt+InJEJS*vGKr8TQh<>jJ?Jy4@EOU7%y+9OYEWKsT%jLlrXNR$ zaQD>thW|CjcIad}&#=)CT!$Om?a4)#F}7QcJQSlrrJpZejG|+^EpLnqm zozEyn6;p~3h&#VcFXLW}xO^s>cS$bkb*^Cvi%xqu!*=4KBDARUs(K^Hw^}Mhpmcr?%}m`j}lnRWcG#)#1%>8NF@Wo}3V{_STaFCdxu~fk^yFha`XhNhZ7)ynOrUo=ek$e7F>L{T(2Q-DrZFys${|K^K zHJ0MQLa5K(%X|SzV-V(0Qx?@6$7x!yh6T@r$sn)rVsv5Y;Y zBroRBW)iqgG2F=ga2hN{YGnb=A(MK#rnj&haE1X?ZO6hn32B?_Q<=f8-ryty!>SSt z7a|x>O-lY(^5@Bed-u~Z%Yvgv78T(kB@Yuv7L_CqBAwK~^4S|J>0H1x#IFONhNQ80 zO&okzc*E@w?~#dA!S<<7aPTs6ZP~lwzNaYfHIEMCuQx*dvxi?A_*C-0lYbP;m%VG5 zz{N%InRcQgRsk_SSBj!yZ0Fnv2JT{KynW^92oC6yFMZ-yuaDQA_Bv8?rv4;vU*gxV zCteHneI0jLa~0>qn5+7^zWw{|Fp^iiw3U*jIymZzpW>j8cHjwi3c^Dih*gZCQl9o^ zY88Wx!Zwq)m)~V3zh=;nl3z12%}I`1t0G{!lRqC>nwR%8@(*u57H-avN@9Ed#tYu4 z{@K7wX6Q?UH&hAVF^0)0^XXRs&*<4R6Ec%36))*X5^vmnP>Szy-Lnp&%oogCbW-f( zvdKqcymABhacsbv_$$^we_T;ZTL)t=vwUD;W9`J{^Ky(WVCe9xi46$MpEt1%w+kmO z!{5admo`jXp39J@egeYk5tcpvLGn}ZyB9z9{JHu~d4eZ#tqEAUu&4VRew8TaUmMyS z_8+nQz8s^f+6iJSGzsaHp~7u?tN8snvfVJ@Vl`C>T9sfg0r+tAROlQ9&N>dD#{iC_ z3Zyrv`=ROD55$7^MET4#TJs`GLh-)Fbq*FSMVmK-) zbhv8veu;4tph<%J1|W^gwJ=@Nk4gwR-)Es;Sf~`T@RLYs(?=qQTOU6KbT1>8KN6uX z$|0+dVb7m6jXwy23txu7x_0pE3|1H&>;f6T$^P}18?8`7+1EOQdaKdO4TgpqE6C%w zSX%ZpX14~OpcGu+&BFXB3-g;S49DHxrF{O5sxp^ireQ2L4ji=m0jgIcNzN>n%nAhPg#Yk7Y4Q^^$&?UzlkGGKNy9g3?!EO&671}P??{sO_JuoZrTKdI zc<2xK3n}54{UCMo=I{(0@e6 zt)REyK?KF!c@RGllx$L`8bKM1(zurEf?aS>n@2{;?og`7q~C~AZZ@TilC1)94TZcP zV>**5)20POZ)K>|ZGBVcmaqm=lQ#u5x!%(xmpmS1+fRjX{ymlCAQJgMT>3E`Q?fEk z7Ud=32!b5gjHTw0!~LfKbXJbCTpyyku9X*V5J+x#dk?Z})y2K7J}>q{ZMnWimzpeB zWRfo|b;!MLZxKIg;!~litzLY-F?@66t>N23U0!3f#Wx4cV~9ajQ64MYYe0%{Xo8#v znJWs%?58dzbJjOFDo4%YBn9gnr#$48F5Ju0>!1wkgLROVS88(VO4YmJSA=*ahF|ep z)M1HFjiu-W6Ny}xV-1dy!}H^+UrY!Ej+oFm^o zJv_uvcfE+q=6U^U3eF=*hWRZ+ye1Iv-po5IqqKgv$d|0HY#Fh9|J|F*IOx%n{@j*O z;(p zzgzGXaM#m<0b)VGb|YZw-qxzX zI}!AV4?WN|b`&uZubmSfIshghZ-ksIoQjvm^O&=JiIK*#r6N)#+Julh!pVRn&iGp> zHXGR2zYJwM33u9bM3}m}o_!KP#qN6VOU1r3FhxjWDen3PCAX*6vSc7Tsg2Y{ek?Y` zLF)5!QcG%?Z$5aJk{a@%M(VqWJ?%!$=*X?6uU_gdnMA?e#YOJtP2DaA!p}uOp0rmH zDex-*j!R+3f&QX5VAu)N%Pke<=`R&Wd^`D#Qyku-8sh8o$(T8)ax zZ+aBr^qmkvIa5V!ff&R)+h^Y8B^K2qr*sEz1kO?CP<7Ml+_P+B50~j?q+0}DO)Ori z+ol7fhXX9+`-xEPsCjR3Dgl?+{BMtptNzra^3hd$3B&v_*k z^NI{-NL1|3D>cqN*5 zA(3AD9L8*Gd#Vx4*>QSXMd6LprbddzEB1bN*;MNl&#T@|tDTC%Yq~c}K04ZX(Y*N! zuq&y4X>&|p)7tXm*0B6oe&p8K5au#>@V^2I3j2I zA2Ix!a4*NN4nJ`kAnOemaL=Z!H`H@~?)li0o9i^wxf8HxkZIfG?=R+j)fbtLzlIUC zxr*h-a;yW?Pv`-bdrpHB`C@ZL`8gwg5BS#L&WpI0@p}QdlZn8^Fs2H&B-lt`2d9Nx zK&(R%=K=iHb&KtQbamlB0BH=`bt$wBxm9Qenlpty0f;3YaRveD`o=dc^l?-KrMn4` zu2I}&aRsRE8af9M*Xtur3!o1QL<>v}-3BNsxZheFJ^E^BpM^dL=p>>0vBkX?+WnB= z$^dDMC4f#AT)V}66;PSr?zgxCl+I+ql>pMXYXO}ixM7R?XFwko+@ltE4L;NwgL{4z zIvgq8-DhoFvPZ2KtfK*p^15(ZfXe~6|a{)~ix^;j~73jx+RMLL~q^bQH zkfwGnTAR~^?mUeSl6E z9CPztfzHNXcHvMr@RvQ8Lh%Zq&_;{9!9t@Jy4gavTj)*;-DRPBEVRi&_gd&a3*B#_ zEf#v%LSuk5Z$Gfma~68RLTm?=?sW@sjIKD&Oez%hAVa1wFXs_wj}3jrLgc4( z4HiP6ysNy++8RP~T`b2Ke*XGfEuO~k3*lFQAM9xP2l0KH9w6>Y!(0!{o;1uCfZ?2n zAB$SvUF2c1*YTc(Dq^7VufNf*Qy-Lm zpX2;83-bqy@hkz40WZB|wQWtunzl8)0Sv*D(rAb|BMp;#jqaQ@8pc|lg}FWp!#Qfb zJPE}&lb}4z^I0@9rcR?d6}3PwFV^{4n4T=mEm@fFW?_Dwg*lXkDbq>}0ld@~WMMAP z!tiJ{U*@~;lzSObR_3_IhD~k3t(Sr_#*pmuVYL!}c|wV22`Lm<$LsHC;f6XfVC?Co z$+=k<6oSCcO--e>TS&blID)id5+6ixcU)&T9KXJW?1x`(Of%2a4RD5eS_s!%4Zf3o zU%Lp|+27u|3d@k$Mx9t??dXF6eujCbu*D8hO{>Lrq=Os=u3X{$;#=?$d1xvduyB=HrCL^# zR-+QheY!zykgBU^)mtWIG_$7t%v|!ko#}2cB`b8MiEWU`E5{BjL}Ru3Ez)f3YhBZl zEmoQbXF5MNs?Q?t!}bre$Q|Wn?RYDq(Q?7)--`0`S+iy_d}g(vqyFwbx+03mE1$j6 zJ&IP2z4seTtAI`xT9c;b!S9o4F0yV|Ngg|WMSp)V03X4uYmju}X4em@1bDM;(G<^> z>0I7ScjcnXSm|cZ=oQetYVyG- z4P$Ojfi#8Z{vl~*L9q90p!RN3W1;W-yK#3q$JQ*^Ip?VAa>w~HpSO*bj6_aP?9AIZ z_t3VzMb*y^{AubR2EM{weKoqsixEFoeqRRbY5Shh71zCtiD|DUZ)yi=Pr{@mXNoio zlk8YWoHTwE6GsU-93n9F?>>Cp;PjK8du?f|57UEN*qgN{b75tk&vIB8yNH+(XCH6f zn}_iC%F7RMN!5vWj(qA6j+2B1Bp0cl+ADJnLuKqCGT&UOPCfyN=eReQv9pNf&Y2_e z8C-b?Z=Qg&NfX=ilDiVW43F)_hR5VdAPH|i7~Z^r(8lxL+<5*WXyKWi`8a%wQY80^ z9ymX-WX{3`^B2sU9bMedQ&26tt;BvJ60n4 zkr<8FfKMrYRHo=R_b9U|e{GJ@B!(l5zYIV2P4C1{N2znuy)Dh1cwb)r`s`sR$QhqA z5D_^WLVNue;aQ;;3*7+-1{_#)b|bVKIS|;vHvf8a(MW@!AQbbdIj4PzMHx1=z?vD zq?MQ5ngrVskuq{OMX3adyX(5gndG;*6h=h~-wQt3Y4(avnF+By}Pk^3EaO@dp;iC|^ z-tPPHw>|#0Fa`ME)!TF)-V+9aa%sKgY(@ITd8c5_@jcVi6=EBYhsd;dJ6tbdR;AfE z;`ahBMY5dH;P*Z_LM4WyS@r@r8sNni=>Mh{AWSm;jB8-jV5v;|uV>56uKEmyT)y}~2_`30%ZFV1sofy7` zW+iDm;{6iw+kMndZ2aFJzZ{DV@{&-C~-zTmnv< zdNQ1@-^}zmD`nHi7d|}nG%zEvJR4}bJq+ShX!Re$8@`P@!qxi$nBdVe;iAfCHC&k+ zWib96NW~r%mRBgU5hOe`6?iDWG`V-`bIGTs{#W(3@D1-F?LmqToSsK2^rR!NKch>M z7|)yfMzW@C>hEQ*0hU-S%V!ZRwr;5(0m5mwVC3Z!(Qg?GZyrm12SH|uWNr6!PTC#u zp2aaNyWBmC)4FlpxA>w@3hCXMZgElk@8fKNWZ{Mp3 zwl-jaZ$I@!8U5=>wX@m%1NH#2FTM%9-l~64tt6&@z_x|br(d;!M{2}zyq*W)T$bgW z$I3pX>JpO6<@GAocnHVCB+@YKuLQpqzLzc;W4Cqm!AjiwZoDO>=O0Pq1x^v_?1oWx zZ`_}^fjM>??7YpHJf_dV`~;Ag!T=GzR`2jzHEY&c_=@rJzg25dIjSVL+25)?$LhCg zllh#U1-ie+w~9UZ7udZHT!;HsMT-MO0EI5&TeS?|sYcNKh_4dv@hZrhQanIR_w-#FgiTJZ(#?1pV)!Jpg*yl zwn;F(o&rc$%2$r!IFXT9QR2l(V93u5r4*)!uHUc(JYx9t$bSeQ+oF9tHJ@IHVK{p6 zB3ZOgyjX?-eYtX)I|VV$Ld;5}AT~v~OBql#lDuy(3N*2!jAl_@ zI*FZAJi?3+qjw`;iWxzEy?CB{IFAB?MBULrgrkGOt&`$#2l>}x*->ia;G=yp6wcj> zQTHYj<3b9}#>B=n-3d2AdEMSgZbC@M0?Z8wN|nb3*aT1>UL=-1eDvEOEg+s(vF^pY zkQ(L+?&IxF8S}$RSIyGR8n66KifoO?A{@6OE8S&;)}GhCmXM8j_gI@UYxE zI7!Iqaco+x+WxJzz1rGpEndAC)M9v;p#V)^+H0@9_T%ic_S)$xp4G?M9F`SQ%%gG03+-PrF-M@N(vd<69>CqH2=fCA z8xGx}b%#Iy{PRzCow{RE06*W`@Gy^&eoHj#HvvS4LU}5;gf2+&RPHjj7+ZobrsjV@ zGY%2^y6ty>A{HXH+4U%9cGk^};3RrAXHZHY%04h!PYkL@&KFp>k6(BopzXY!@33jD zXMKQ4PIDrhUqIJe&^CP6g!<^k7u?Z?Bol|&s9BI|PO#w^0wMEuHp?K6o`K045k)Uq z^Otn~o|fo4pR%9J9l+fJ@*lRn+vFz=fPbZp-0>Dh~C@MZ5N z2;)hKn0LqcWmMJqyvjOKSKIFp*SeY2Hgc@U1;=3E!E#I#X@|smOs+#)OJITIJz^~` z&dqdcF`)`c6xB%i=5J+H+9HolQ+t0zvG0le7^XGK#_PXE`It65d=vArLa z)&r~ndQZBJrNe#`ybRP>gRE8bt*3pt>SKTL{>Zmrdt^4;C66!Y8PrB#kuM0`kB#NonDKIFw&?K4(ai(rb!Pfba%v47*?X}(M{Z!b>#~AF zk_tl8+8e#33(C$+KM{c&Rz;TS|=8 z!4p;_g0&*u3~T)a>6dUu5Rn*O5a;moILg8wp=hLMxB7*HKgKD%O$b|O90OL?)fdUJ z=p{QQ+Anq}p^q&PUf9bZr%DJW+gg;4oj(`-0g^e4{o3!@{lYZs=36l>#KAUcRvOyf z`PLPP=YPxm6Ke1jN&G~l`G8oJm0JNBLRwJKV97k1EOKA@x1DZw!0O|28nh9AF*Nh_ zjV6U-9vovHEPechT~L`EPi@3gd&pD!^vF6}a`w$W??m2ZRpub5%jN~;B`9027S0i? z5itv}gxf1;=MZBH#v8JTGA_gqB1q~;+GAF}X}g^{V(51t(%Vh=^EjJ-Tr%R*tGlP8j% z`F13PV>JL41XYzT!4VyJT%zR}>xH#a*4pdaMemF}+w7eGv=+Uc5A`r-wRP*AkVWrH zYtpPmnOojWQF$Q zj(+Tq{N*E+oj(jbXnC!=P6Tt-72?Xq zwG5Y0Gap@(s1pAdh_}T$nE^zXh4fOj`aK=M0wPr$L z$_3!dVWlQxUhILi__BIu94(fORyo^oO=39C=V-vE!D4{)X-J;~R}&x012K4vgU;i^ zj9MiK98e(+Asdm#hk43LxHDb6uLAiP^4IaOER1tnfCUe}N8N__H(_+%W~WQ?TTne= z$dz*Q7#4gNWH9~9NY{uy`cuG1SZ^s$xn+z67aTxQha@@RnWq!H_eJR+8YBBz84VM% zan?wJxup8K(+QdjFrC zg=Ly?6(Ge`H=rpJZ#y7G?^!^KULLfrP(ptLNagzpAVqH?bm~+I%?G5UaVel8!Tk)7 zO7S}#Zspx$}L*D{4Rj3>QG)YbGkt50G%Vyts1u(5Z7O_jo+Y-@&$SlxIBTp_)D28 zdp`xWY22+E8r0B}8ghf4N-b3jO+#L2gi6c4NkI>4h*km$R{)Jr5VB49 zHagbVNya%77yDM1;gzqU%Hv5O0mE;(I}O- zYlWFzqpt&AN0$0|WCUEctms(TB&WX{@>k}p_06sLNZ;Jrh3HJF=Ce8xr>t130Vri6tKu)(nR>Sn5wA1~bU6hGjmxtVax1ll!FLt|p(qt9@*4iLXQGK>bxsYryadC_(~71jXkZXA&#rj1Nn!e|Ut3E1`tCS16?n!pG=?ETjyb zty)4G`oH*=l!<&k)IAb&HVpYj#7S4&k8@6WQH{e%yiwnhIuTxgGK}XyhaZO`#&gy> zxisZ0SKz!?ZPkF)5-#;b5q(%KfX0VqF>D<~WMLr%HWQwnwIIv~t`&R(i4Z%28+V)H zGrccW5#iU2tUqwB4gAEB(_(84^pCljlWUjEpiFJsXcJea`nKkZ+L*l;G43Ku-P!2xs2KMS0|M3O}_w<$ibY!Q?LZMnq8FHV96 zoCR20Owb39tkFFtyu9G6-$Vu2jLR@zrL&(cfj=8PaFX!=Fe;3aO$_@JFr|-sdXI)s z0^fBRp5A`|gS@ig7!i^{p`xd7>4{d8ERiqI&d-lSlBJ0Kf$ydm{_BP-TvK3D8Ksqj zto^d&rGN7D{27RmCt~s=@%VypiHqx9x zStr6>MXLEs^n1)QwiaZk8?H#15}Kbf;<1CqPM?DG=O?5`?d71NT&0iuvspAx&pQd> z5DD0t2%c1yWm9~|nlGa0{jT5^7eZ0JBd}5RY@F@FTIs~-tK4&oHSPFvL;0jVN2h{z zY+Z@al*yB)82E#F_+MEoS^zrETG1P52uE6LNX;H=ZzDD#J~_r3L`{aQJ8_noEjtV+ z07{ehD33sm_^aMa_5o7yIIlkrPuWHmAijgBcWO-00r5z41^rov?gGCmo)>Y*yy^v= zy+JZ-N9@tR_&$=ssVuP5Dd1mFi-Tm|VDWV4WW& zV($R6Q3|uze&X$)v)_RE|B7XuYwUEK$F~3;dwwZ%5}1C1pX1|RzWCxdfb0Kad<;Mh z8nNxOr%o!?qT=QuoB2rpLKlrCT#9`_^BtKeDaew%mZ8AhQDU>IrD?a z@6p$rTSAq;Gh9RY+d{KkBbyeQk6#Yb5_x{;n=aS}cs7nRcbI?r#q=9m#(IB-%g%86GuUd6Y3(4P=?6!=Cj$;MY9Jque8N5e0)@=6N`)sQU^Xx#*7^ zI52>2dHrZw=f4nq>)LS#dv>qM`D6}KtL_5nr^$yg68mb=I=qtXD(Q{aN(*2p~`ZwXF9_%HyZf9QTpo2ndUHt#Z-Jv2mit0ROj;p#;Z)u zH#kDeXkPlN{{u}rmNDdcDr_()iKZ2 zU@-Xlwf^(WLp{5FlMsxraRsH@R-Q=n)Fy69L6-6%XFBSfU(e__DA7LkFP@DD^B*sL zZ`EIeQH-B|46PlXTD039U7I1#^eu0sLy^WkQF=i6g8pPHigeib*O3QMKV)afHS$|K z!N5Xj;^Rd_mzyu}t5J@bom;d!LLXL~3I2eh8HTZFtnT>P>AU#U1f7URJcu{!o@NZP z2j2V|mXk(uZNmK0YgO}OuhbyQa-xN-hiNJ?)BX(Xggk?9QT9wtQaCn3IZz~+>BPRQ zg8!nISnoC#@>KP`*lNjw5AHKP8|#h<96EW`sh*8b9L^2h#JW_bj{aG7l)L%7xu^6+@ZH29hwouePZMejDjm9HXW$U{hSm+| zzY?1FVes=~JUtk8jo?A}(0Tzi1P6D$Uq@`?asDj-=RtS#%A#k8xa$412us|nVw#b z@T{~2sKwzlBie=-%>$b-W*gBi7DMv!Y^*u#*}VF5$fnY>`5(qysd$CuTWj-{p>)qfLt9=;AGeclwE%z}hn9M+r!Ry=GY_K!TzL&d0iEp&3g+-B z7*aUjFt=`rqy^vj#xb0v2A>G@FPNPAFf+wL4+wbu8-XX&gMSS4*G}f~5Q51ZQV3VS zd$Q{}GPiOThRfh#X#97T-{8aZ} zW^@>4`;fc&85AM<5w(H(Vt#;jRjt|;Ux9J(ov#ZO*ZT}&Y^I7hk}f)I?sOWL`F?a7 zs|x!3f3pDk<>ph-gUG^e z*}I~rAgS<9;xW1?#Ug}Ya#HlM(w68G2T_b!oXQ9z`C)0(ih%;gwPmn_l=A;n^bTlb z3*P+5w2_jNN^%f|$N2jc$6j-oqc4hE7&#NUV32G!w|(5VC>`qJKMxjG$D=6l2P@OL z!;?^C!)}MLqvPfX6yCj@IDZme)>=Ke zoclv`IhIkn!_#*Pw99-3wf_LpRWueOj3Z6N5L*{d z55EhQ>n2Br_M^+;n_Hp|`+Osjs`~(bQ0?4; z{Le?mnY&QVLYEm9Mr&spp*bvZB(w_i-sm1Mq4F!*y@@Mln@I$^^B@}`=TU>`e=t0( zm5CI5S%2sRsTczbVTD?>TMY8dZ8SoxabtrM+^^k;CfYl+`Yam14x8Dm!5mc*ZjA4+^2RYJFI)`|wei?e8baFb^j&lv-gsxI(CP&F9 z+d=hgdrVeQ=cGE$4yq(&xGo3v@^FPL_=GDau0bCguE@CtH%VZ#^U4QMs;$)3V~{Mc zHg~##vu{zQNS$X^IKyz${hOXb7zv-{w*2L)Qq#MO46~U zevSqPg3^{py>DqLqbl!Egr4I(ZhGL5%YQjY=9xFLbbt!X8(Ai)zZ+RjK*i>bpDC!s zyphXu7zz4lgBhS^Eq@D3eKVnjBY5;W_!4IpX zMK9R{lJYc(y$KxpOLufg;Se- z|Bh}}^ZryKmAlmABBsJJ6=rU>iYx#IZ*F*UM+)9i`pZ@XHF5WFBtTolBNeRY5EZO4 zx5ZZAMkWFewI)<+|30t*)ndFy5g+mDti`ztP^XxoWJmviD!__~3wiaxm-~Hq#OU}= z{x-^G21i^UZwdH54u0xg=?1yFI2nF`B)WLFAMhB{ z4>E_r8Y37u$OK1g(N$#S?__rfjP0~lNvxN>En+VT&He`7$3m4U=b|vN8H%T8JR%Gm zXBWu}7bZ7Dcnd4{^n4#J!?ST<25L8yd31mF(U?xX9>S@A!^5ueB#0ei^_T=Q_KY>pwzb9|QreE+bbtuKsLJ^4<(BI2T8{jURTWP&cSE zs(f0S^dJyd-87Vh^4XmQXM1Om|1+>?~X#mC{~3PJ7zNW^IbVi?hxd_!V& z=-xAZFT)W}$KkuXlUUwEoW$oPC(fqw#3xRVOME^9oy4WdiFt@fLgF;WS*zHlRhCXX zJ2~;CqZ4}&$C(dzl@HEhap!VMjrV1|w?Pzmzf^NX(>e8sbht#5P|mkvbhxJU{}3+Kqn1btII1Sr5gqEqQ$Y7+>xFK8y>GK0L_zs^ z!mj#X$MeS*YA4|7_<%PVkeDCG+W7pU8xV)e#IK(5Jt9BOLmA7lqqP=IA89z-l$XWG zD{l#1=@Ea6ffwOUZ}l9>L?Ue%yqG$ck`&yLJL6K)opdav8QJtQ(nh8pA?0_Fl0EQ@ z(W%~Ks?Rv!6z6VwTe7#AiTn-N=r|-8aKuAfI<0$Wl2oF2PG)$#)_;P!r+9eqcf5EW z0RBkgdF*eB)gK^ELIKl2J#q<{N)(L}s%fYC=IB(JOm&i7z^{4i)yz&KLee{>qCHyRGyxzpb1#;#~5Z_1TcCj{)E0+9{n1kgf1@MJd5q_ zT5_irOlfzolv`#gHHbb+nzS8`mnMXvlskG7Bip+CBgS(0`3T#?V2?1VaO&zAM$zP{ zlS_&VC&Nb@{uWOzG8SCAu*#^ORXe}RC|qqAg@wkPixwDO{+VoejY-{;+JJ_2ynoW< zsoiJmKVCvJ^dCH)*)8!V7at+kvPsK84bjwJM5Z5)J8+fbieQjV$2w&et~^`}b1sGx zo*0L7!W=xqoN(Uv;2OY)=Y)^G*Nn=;p+~%P5jQ%X^Zb#W{uSKu8GBOq3|!-3u3z9a z=KGg^2lki7q^XTw8&;unySx2x-H7-KQdmm(vZk(XdZk2=4I-?~@69V*jo|$8#{8zP z)`m9kJpYQNib4WH5t`q+yuASii%s5HZOc304zOhf366obaC=0A*1Oh6Uguxh*1F7#Blr@t8w;;ebagk_$NAYsy>$L8Z(Q^|mUwBauN&nBXe0(3 zNS@c8FSn+Aqp`bzS6-)pc~`63a&_zUFQuU)9$El`&2}ZX#uka+lE-*}ooHG_3Gz~0 zpUvOwD!8%tR#aWOU~cu?idhS)1Z&9Os(F>kAr&xXMC*aw`RFz`jclL23bk`K3?+*a z!l$Es78y;;lu#hrQK8c&qwN~YrgpY3Ejnk~%F&70pqrLy^7CtECB`^M$ZLj*rcEy{ zom|9!v3z34sm6-Nsh5LK)I(gojgh<Deb9~MXIBVGj;NGiV)~&lZ%S+2l37! z%_5^>o`F#G)y2kq2ZR)*JFt5L6@HFUVaGhzsI{RIWBvjgEHz-Y)70df)!2wbKf7%# zgnY3B78w^Wbl|CQ&FF(0T}8SGI@{E}tgBVI&b5)}8l7op2zhrE;q*099&azU4Ux|uGP3KTtC3|OI*Lg^&l?p@7jd>qqw%?3gh|{u9tD` z#l>a*8yCg;!4FF;8y7_30EGjB3$R(98HAN7WS^9vbS?Fk~QLzWnt`-?d9I$^{H|`M-9*XsK##I?~OqKyEN;N?lz{!n~V?lxcp=1 zd(1`pi^teoT8;h3NvB|_Gefve%UA0>Tw~J2e$$kJF?g!V!0zU3Yz0l&1WQ+D!fOra zWkcT|#j~P2N1tzE?}IH&xd9YLZ==0%Y+hSH<~Gpeg56`#qa*OzxpIu+S7oH`O&NpP znvAQ*z+RX^S<~~*O_)LS16FxSUCBOPIS(G&yO)iTx;6uqnj{w58NzccXy!sdvv42H zzfE(%l{#fCR%cPJCAjWDS|1DsE(5G+i1*pp{^F_$<=2i;*~N&?SJG#Zw#`nf_qLZO z>}{{gxIDJmn{sD8R>C~_5bmS-w(GJpwTEnvK51o6MoJ~n)uXiJ!i-eIH6gpwMpuNf z_x_t(E`+JjD>T#%=roBJ0(81S?BCUXW?J*8ox|?{Qfa55i%<}k#uT(#Lwy?h0iax= z$G%?0qxFY^h5;$c?Bx~i-vOx<*PsO`9Q$4cJ)@yLfKy#Is|Bvl<0WWo}w~IL+1igQk$o7^EFhjA?`3%Y5O!Z z3`nJYS>yHsIzw`!S^C!nnhld)6|Y}I&jIoxG|l)sAVv984V9xkDBRBhDY?7_U01kw zHPn;k;BL^+ts45JhBj(wNJB3}pA}ap9Opnk($F3a9hdEd&ePBkAjQiLKuQy@f}xVa z-KC*F08)7z(9p?Wb@pJ-0;I~;3P`0rtf5nZP-&|*v{XaAfbbvk3*n|DewW7G4@k*= ztHwQ{++20U*_m=4#wR9q&p&N@@We+7C!c=qDQY3ytGhrz*F{0jb<} zYut-~RQ;X+7r-jsLJe6Ox>ZAi8X5wm^4+6xdjY9D-q*N;fK(pmU@2bZ(Wap_8v410 z?g6BzJgRZq0jb>Fm=Y-};{YjHouP440jad-0aCK6)u9UjDJnOi=DEKm&A3%Vw`u4O z4c)1syESyLhBj#EK@AOR=ur&~X=uBKc4+8H4eiv>vl`l?p_euEnuhjjD59Z#8rrX+ z4>fc^L!W8Lz{sSe;nonpb1Ph?hWMRZ;rLxxLHu&9AbzP*kXJ*w8scnEg%)V2P(#HU zD$!7xhRQWmsUgnx6unvv)oEyfhWK4m#iL=k(ww~-ifCw`hW2adLk-dQg-ZLGhPd%b z;oKTZ*HEU0cy5`Bm#v|R8p_cSy&kA|{2rsAJPj3Sh^I}f&|(dhXsAp>qIa zHB_ge1sb9$fy!gChL&ikQ9~^nYSU1shI|@Yt)aCV3TUWDL;V`MUPA*Kx=BN~YUnl% z-Jzj7HFURz?$yu+4Lzu#K@B~sp&<=z*U%0PJ*lCc8hTbkdo=X2hF;UqUJXSwv`<6( zHT0o|4ru5z4e>w*RYz_OrE4ftLs=Ti*3d)^P>F`hG*qsk zN)6R$s8&N>yC2FnmT2e>4LLnmw&Aq%Y?)KDO=laKDXMiNt%mN@&}$l+h`F*#(W4;{ z_@{iYaDKT2m&@=PNQ_@0t|jPKVwm%Qc`X5RAuw5p9*eV_anO6XaaVM%2DCFFPCqaM zm}SON{R9{-Bfm`O4Ygn%DM9-8CDa(@w+a?BG| zocd+$7tgI!D>*7oeRqSPxMrd-$vZT^oul^VF$eYg*X_`310Ody+35TGHiQ?IBYIll zNXK6qu2dJ_=P?-{^-DwTIX@*aj&aUUVZ2G0$w`>xbK&UuT>V%V;)Ofu47#O>agz3R zGX2#Faftc%B+QQ!F!YTy_HNLo_GMVcU5GVI1HM2Av-0aZmid}|Q=wl*{c@}w&!1OR z<#?SyI0I~#B9q}mq1)Hl)zL}!A_%jOVPg_e(8LIh9rgJ5f=@JXS<=)7e-0|o%6XGp zySo}lqS3#ilb^BdU8Zt~8`gqL5z^G$;BWKU-^p-NN$Irdb}Yx+2VIWS~^kG`JRSb`4RTh?kiJqoWI;BI~iXumibmTIXHAs#>AP< zle#0`(3DYrDLBJhjCjAiM8#nrPI_v0gLVfl-`i<(tgJH%ELw_1m_#4I@C#bIBAO{m=4JJAYsqF0B3S(X*xOc+p_)qgi2 z&&J>{5oE&j9PZO`u2t|gn%retK_AhMIhg$zEJUYnCvgo0U-Oz<@zexl@U>UR?d%uO z3NkY@SU_|lKin4kOmLsq+CVO>L6R#SWKRB*JsbPU3Cy!dZz$>>di$v0>#pD%u9988 zYM7X3n};H2(P$P{5>_9Xr)39AO*UbiAd4f&1{yF6AGe7O$@ z2c{k7j}CqUlj4!Rj`5S)7wq^ThItK7bcbo6w4F?=Uc8N%l1B~Q;v6fkgY`ca-9 zkyua|fJyCcR`%N5+N1qh;JRjA$qalP^Yk2tkpj=P>$YtGA;^THVFVV>mXD|Rzmb?` zSBNY!te_0F{M7DXBw7$lUzd>H8kPRag!KLnq+MKE=<_0vOm+@VJuy!_>g_p2u(#Hm z8DPFOK-OU#xM1BjikHz@zhEO&nZ+8)fqi67JS(zq8AKkt21aIAmqf@6EK({u9O`nJ4-jFxe;8LH@!t_=eU(@VB*PLOsS?g$HeXN)X?A){Cq%&2#50EB4X*bcRK55nx2C9Iil9xR4T< zHf8oLYoIaqemKWWG5TJdaVG3BM>ZkO;pmB~X53h)g-$EkMSqN_kFiIKRs#x)2${3O zO$E;OJHRa%B^Gw$P;P(=9TH?BiWGJS_c%MaKl7P=A-1xWV(>wRVNdlV1gzj*{7ntM zOeBqyVFBlm%9j|fM;Ig6%k#@!!M<{6qN{YWujGiDV%w>~Qn-B8#L~(BY8hzNZVxB| z@hkU%oibPd67s}T99LV$Z^%LYIq(@G*oL>#B0_bCP3fZH&HhD8V(Az$ z%}tn{updzS+GcKU*CRaG4m~8&P^ou=O9_-fPORpSp+si5){*P%5E{^D84M9zZJeV@zxJ zV`6#w3j~9Fi7C1+xtT#~0n>h$f!Nz*T}PRs7bp$~9wY_)!U0NAK^&35{NZqbEmq;! zUKO-QLpyB9`aWO6&3^VR*7v#A77nm4;m_=Ujo9^^otoFl-?d;54c7X-s##AD4S886 zCc&8t_Ffcvy{{?9OgbyX?w}SE)zD5vK{$71LgdAqW*@FAV_L}zA=s#p}_ zThM^j`y@GW-%8tYN5jwi8KZT*sRA_`A}k0?e9{f~G7^7~Vy_Feg79K%-b8Ca1o{2D zNf-(e4h$fY5*^iCLH!!K-i9Fhbm^z&$X-QYh4L*V!yS^)8moGu#Gq0J;$K+QHl#m5 za}k9l{}8Du6C{$7JLN@KCaS`)qcIa9%w}dQBJQ@+JF3TWwuVOhvGwq0(ynm!!8~Tx zORYc;=IMDHkHJV|@W8T_+2LMB!hZpCms+{i|c1hm;W=SbN!65gX`766;K0L3C<0I zu{MQ(_9*H_36Lw^9VLr)(V}~!9aL=J-M7a_;h8*0=VpiFq+>Oy+J}l|m^&jk(DEF} zu)GtJ4yM<#7oBjl12iz9R818jdaOVi?np1DX1xKij0EE9&;2mcI$0*rJB8haJv7+; zC*HqGti^H|G-vA=@$*n}Ol+z`*h$F(5DV4If~wYl8GxApE2)i#cVhZlaEb3L9HUM` zqH)3PB{)zE{o-Ic@*3H%hTkaZ&{MyaYT6a50(PqR>jikfuEkd96ziva-7rtU+>Mp< z+G%wEkZWC+pvqhMW`aiQ&rqX>)B`}EU4}l+%PuDbof$i4OvX39jT;UhU^)|NPlfN~!VhL3a>Df$Wx9~)1SrFF5 z-XU{%^{UdXzCu?;i*+3KclfYN124*j*8EN!A#@qqJf3FY)kV#O>`GdR&C?tOaBJah zWE*NeCoCTOWS8v7Q+7G6Aira>-c*b%JgFG>I7PK59pcie1R`{{45$PpPJ-wvcH$@s zrR=DYiUWiq8l8_gGX6sz@EY+nmN|m^%B*$l(@KM!o%zRy<<2ygw6gpl8(COj!QRMt zd<*aak5MkZZ0(|fvM|*ls0lD6HJiRgz>yw@ZUCtdOc{8;DwR@$Nf>JNCE=YH6 zK}{5t?&$irk-4^1nsF}aOjjxT<(21HC7~9cEaH|SSh=FejJ>Y;p2M7@&P)q#FB7>* zJz)Odw*&d$zbpuq>3J&jwJvm{=;fZsTbPKlFD!vnl+d4zEsu~-h>^Xh(g(iW%8A}8 z>TjHS!0SjBV#Tej1eg@*;%Xs2sd$IcHD$keqNeHsKWN0j=?;tamgExUUT0RAnqN6y zHLydGW6x16hh<7>uu@@Zmm8H^LPbt^Ln2l$sihs*%g5c$C`2fA^R@Q?)Feyr<0MBI$)_T#Uoms2u~ zAmxZ!?s%2}jXOxH78|+Txi-bfC+LDHm-v{r24eB=QQ=FShv@0Ml9}Ho;;;oF?I=)| zIvy@4JUto68_u(aSG^NF=<@VUL3H|h!8=^Y9oX_(3bM7QZWsfobZsQqqsk6J;wzBo z3{WC+N2YS-Z$ARhQUTN(@7QtCo|cxXSiWzfs3q7fCA2Pan_JIPaM)Fdb`+i^OAqEH znZvWPjCF?&(B;tjGoVeOMK1GD{)?`iBgaYk;Ib~_3cl@v7agy!9G&;O5EjQX`<+3_ zRrPG~VhpTJJ$#I(_icu=LNqA*K(PXE%|Ja+)a{=Awyaop-oA7n~dt zPHy800OzhsgTk}(u444rT)j1{Ko){OF0X{IChm_9&o1be&|;2x!?R1cyjPeC&r^s7 zan%CLdjTe%hF!e_q03Xyh^WsLs0ja4bEhq1Gdnkb3&sOZIqgloQVlLNMzB>3w^nk# zF#QpidGThBqLq#F^hgm8d4hDpYd()t@8Cyw*j4(8rzZ@IXJcB~4F9{OA9#8ODGQ)C z;0}2du(Hg*=RX5g2^Fjhe(Lu0oWue3+jIuPH5i!@A?FCoLKgToy%0;j^IV}TU4esM z|5wz!hHio}~4I%v;zJIL(<9R9Z3*Ng=%UjusHl92mHA$OleFKkw4 zy0tpfv#})$y*9lnAXRW{nN_nd7|Awkvf;bMteJ?@NzIxZoB?Lmc&(Z&vnIE6tN$hB zQh-cQkI=sx6ie!besSE zbqANcO;!FrOS>3nZ|^r>j1)$|x7`<^U!);OFp?5Hkm^}~3F2AT@pYZ63Q&b52K$sB z@iiV#iz#likNt{Gz`U7V4dx+qu!ab@XMwpwRFE4mH-&SC$FHM-z4a2+TAQFjofxeu6Egl4)!U!q-K(H;h&=uW}N2cQ}#ZLF@(dkBp zB?PhT)wdra22e5G#f)hKsW9B8!lNe=avupW2AjJb!yFwWdIee|BO$cP4fAk-#uj@E(y+zSLnVX023qC;I2}E3w5Ko2CJaqr-B3D#7-8SV!ENdA zC^|8^1L018f;LAw{{@s};d{4iobPR-ehE}rf$|e-$7a8v;@>6xLTXt7`US+j8IK5w z&SOU-{bWHGlpNG&-kTo|oGECiKElAnSGg6_ZraGU`hRs&2lKbE)j z356U;=^`!q5{rR(Qc^l{5q%#Xwww@gpdF=aFUY2x{G3eTp^pWN4qJ(Nvm1#14k=>w z=d5?ZaRNw`A%bB|v2NiMCA8k2kmXwcm50tEQYfyLd<7q`=TWzqE6ic*ugvW%dMV6= z_|`XSd5rT{XF6nVvt$Mx?sFzY>;c%6tq@;=xpwqCpZfS>th_S~^Z5q8PM?RhD!5l+ z2~j*`+JatLuezx;=zH5R-fcB+;0rdMT1-i6-9QB>{Q;-@o*ufF!lb|Vm$+LuI5Wjr zN>e)@QMJUcw$ZbRyFp&~Z{~};H7gw>X7D1Q^LL(CncL=>gFmUq!ab|Q6Gu6_*K^&@ zG|>o}0`%PK8^RN-`THyrI4!+lJ22*dd#Z02K|r}uVH3a@7&_`@iY zizTKq^FUZg6Bq6yCH^AM+P;}ukqFY_YPKqy>548SOR7T|u!JCMVn4}?>`6c*+(*d+ zYBT#pqU(#1Im_@j#`r-Ap7M93{y@G(Z9F*G750YIB+##SmrIR?y608?6N9qAx zjZ(Q>Le08GR4x*E?sYXY{x32h+6XpsBlpQ$tEcyA=6^HwBwED6K)zDP=h=09Gs0tK zF`sej{t9%Zmb~aO|8x?UF9-SS<<(GNBsHQ_+2^7e;{8pA(@Y~EnwNz&L6n-UjqzCT z7@H2?QUIj6mCF#{qkP_CXCczEL^{z|$tCGoH;W$FFP-Mr=+(?gz7smSCoTPcP>_B= z$`zdgos;xop~}3&>nVT!qL0Ta6jR2sOVsyW>t<1IWc4YFjdp6jZ@$gSI1qUpf;6{9 zFJvyxI?~unPR_o5FQSg6eUb0Lg#pXl1}36EbmYeD?6$=3XVKTuubG*-!T+W|N_#;W z00sH7GhM(W)p-|Xa`pNc{WVfD+7P2T+~hm?#QLvBOf#04p9bG_@l4U+TX5$4DsqnN zlPG_6sD=&?q5MstLz^=@aZ#Z+oS$95JOuafwf-FynHJPG)M=bt%17^`C_)+<{X>&q68h)K#~aW=f3 zi$#D69SO<|+RhC##L4IVoL#o>tKnXbLj1``FS``6CN=U38#udvW;Q@s*a|oDsQOkm z2{kVED*wXuN|eMM4%A?5WH+>p-?#9bi8{BRF~X@OnSa4om>-S$3Nye_$65Qd-;;zN zE{*VAHyoh!`Ez2yz6#SwVpJ8CQ<~{pm1gm+hC`7Xcdu#J5x$W+iB&)BhK7__Y}&=^ z1{Zk@<99E;GUPu)Eu3Qt_KnEJy!T$&T_T zV9uJzFW)}|wC)in3s3L0c<%k!_uatxhHquy{2BhV!1r8+@2XI>i#jm_!4B8ZtOeFR z9622=!Td_T_@I0_;Qbu+aR|Pq28uSQ8H;YSp58u$ge4i{#3dU*IyMG;*CtWc3S*#C)piMB zYKEFsl?nwmhSls|-W%1K+lA=< zDy?auN1o--5v)a7+ilP@ z5~lArD^*T@bm+$C5sPjQFi;bIAIgJt*}LZ;Gbj@m?Pk7)viXiH+C8%zTI3ra+@5Fd z#8(=OH#mesSy)>AvY6YyA58jQf!PASg=GZ^L71O7?8jd4VlQ@9@OqF9Sf4=P>ER$J zFOn$eOlo4EX2SAOMukS8SJ?o=qKwS`<%H}};&|%=j`Pa8Q~D{d_r zxOO|~OdvLLzcb-tdrOyS3ETrHrqlS}utoU!B6zgDpY6L{B zYCP*ZfpNOaaw&3LRo74jx%UHATVtw;zHq{q2&8h4$^-M}$0OvN=_Y`;(ze_hPtQJJ zT=%hkS@Hsbcf23&MxD^(bT2<+S~oN6kWA|%&GLFZ$SIwtYENv$$9j#-QW4)NGb&R$ za_|)DMV9Zj^@J{Kurj8WDS5)v#TXb)KZ3YRJ6_C zTJQ&!z-Jx~ftW`I(pmTMwq#Jlp1WA%oo?%v)mRipL1@8O8~TC$l#Th-rS8Blxy#@^ zYn*i~zXO^$kf4tktQ?q|xi4VLo9^Ii?~U75G}M2)1jMF|1J(s@^L|FMzJkQ>TQ}RJ z&#^m6bC(1hP#l=Ys=UMN8I3>FH8MbjlTLT!uFwq(7}=%gf0tiY^f5;GEQ=*xj1o&q zh&B^PN+F&3Jm!}t@(a{f97pkI|9f~}ILk~kohfqQKcsMB*@3|foyqHiXYgsj6nZ!n z!+z#;f~S^x?QdryT;Pz}B9l-f9 zD~1-PvJzSbO&f6rWts2eoYFzQwH*Vkf^EyT#B(fVACkAJtE;06?(pEfalA3N9S0J( z(;4By+FCF6KQ%S1IMq9SyfLG}?`xso!8P!FH)B$x{OvYoc#UHqRBngx`MZsp1qs|GcABlIq7X=q>G)acE-Xx5_oii_t}EWGs6s(B0Q=P$UlYSu;b zy)$QeC;9VF9d9(llNaLoI=ncK5N`gM7-?}#_7T{W{9>sZ8XN8Sq_G0K%32X47T(?D z^ZMayt-i6r*Wkq&({^#alN!ex)hhGuruN3x_T^rdYpRzKS*Dh$_1Fxz44&5NJ5>Z6 z#N6rk&G5D_7n#;~BMdIjcw{3^wqCX}FW;+v%T2ehaGdvy=&Mp#v+LpaYF#2?u21#jP76s8M8bjCogc zTgNJ6MrVVs1q!mP1t(e?6pz6Jm|>%D(63q>oMRgP&gETT#^71a_TlDNH8pjf4To{q z4F*}SUUPO_7}B+OH#c>iEgK6_GS&(A)Isj;jWfKLH*~cFJHxx61^VVk)x!bZ$|i4H zQ~PpXi?^d$1fj~0(ym_C)YRCGdVzz=HNK{9FO-7?n0}Qlsdrj%T+D*3wuJDV-@(XV?_jYS{w1j?_xgM>q{` z%A{`SYe#1%6qps&DOIL47|~+9di^+pyi@9}ZH<#rM>`TMy9(MP?F6m5VNF{{Ln9jc zGNz67VKcl7m8upfS)V=MPIAn6JAmarX1r8{4pcbOfjYH+-RHI4B(`4D<`cjBr*T2#eN6cf=)^WA2`0BBfx1p_mRcm`=$Ex~nNNtRi zG0jBJ*No1=%ZT3AZm3a*x10NcsZdZ7bclA3k1fI=s+x^cv7`IP86<~}V}bPR9oAfUczRBTC7`*!&^f$hFPyI;~DIjNI#)XC66GZ8WwY%&#F7(Z!<>z zFZvDO1)LXuFr*Ss2ZZZRRX+Cc!eh{uojDOd`j;oOkI~rJ^Ow=bd{?c7#gZgI{}aE8 z-m}rJ(FkUoCOf&g3tM&Y^-I^#ZRu%W7{R~dG~~?n5dK`(Txz)1rn^qcbi4b(B=KC3 z;!ErlTt>wN?^P4rf-A-&uXBip&&43z^LabrZ*JQJw-=}dxO}+!aH()OaZFjOA^^p- zt8uxJ&j9X>vp!|kgv>trZ4A1r;BxUhG-;F@)xCtK$2V_LMUEjxgF_p zv4}*OE16sXc`w)UCXW%sxf5|3u=a8U9+%M1=IBDswdI}%d1j;C@DqZ{qhdmN$~;@{ zRT+6e*CNhMh{Ii)P8{!AJIb7liV5j(T8KJfTpHqhh`4hY_Y&$*)dcS)hzkbX)ftyg zNQWmw#3k={B5oz#wP`vU$9sZ*#+1v(md^qt*^BfGV8V6p80p1FWYvValrYkd_NiEr zLC>F~{c)n!l)uL^tS52W_uIZ+Pr{4V+S7|9)9s|Cn+!RhdEi{c)kyoIR3B!RJ^ zu+H0%zB2dA^#fIH+={q&A}&-!e#G4i+=HWVL%?kZt~)N?v%pnOiuu0DMpy*6M&P8+ zLU+{#k;9|tBJQMv5+EsE{3RA~JhZ8z)$Ym50(DVt6 zg%+n7o6UB`GIFtIdx2p*0ewA^{8M+bp^WaWDl$^`je&*&vkvGl`KChU^Ea7qZ%Xqh zwr6E5OXLtVig{s<0ngyZTY$%+vu+d}wjZWzL%Q6l(1pZw=*NO?>Ok$NbnJT}2JzK= z6Vf%?>H0WU22+`2&Unag8|r3wf?MQAoqiVi;Iwe#D~yjHf#$SH# z%{FL?##WJL&?u9Bp0bU4Xcv3lY~w0G^iz;+@I;yO1$rIOHv~En8lZ630xB2WEr3*R zKi9a&H1vWFeO2S&xz?B^X)gezsH^~_sIb>l&>sNJmUwQoW)&|7kP78`yTVm!Xt54$ z)VNnP?r$2GhE}E0o(xFQJ455X1xWF?7Epz7@PLl@C?Lhv^MEQPl-|Cp1eyd$Nw)&f zxxl3vD*>r^AsxB_kji%uP_?A}6p)f`HEMK@;CcWlUQSPULT?77LZ@IPs*!jzHB=8s zaj;H9F9TAP`6jP8JqOTSN&6i@7YNj^p?d)-{x$+qI`lT63ng9#RE##MY2u4n>Bh^z z)k^4znGQ4ykm6;LhMoX)k;J1f*?9t`!}vnwmJLYd#v`2-r*i=*PVdq29spD>X+Hv_ zq%muPBdf)L6g_SnQuMCYxHTGgH=v7!%0@tn)2$k}7tmycrWyMHsX98KL$AS5r9yiE zsnE@UWJEDC(Jb)uRZq#1*NzA2$$K#KBSKq}sT zK&sx-PH=jp^8sBZDH;H&c+G&mB{+JERk+(V?h%dKu5qtw++K}yVUexKC1U4Tl3-eSxL zRUR#X6zTQ2bTS|nx)#v+LT?A4SpvNcXr4fMxlW2gK#Hq&K&mxfH3@G=5^p6SMfnCm zO9fYXhG8rd=rTZ!0-1oC1bPdQ>UTfV&tu(D*4%+OGmqX=ebMBcTBu+6zdP z;}?KbYrJ2FZqlK@+fmJYo{hqmg_H9B;i4t)-g zl7_p;$>VrHDvumMDv!$nl}Nser#gDi4Sq`EYc&+mP>+WCHFUj(1~hb&hHll+Z5p~m zLw9QEZVhopt2o%8p$9cIsG&zSL@O#)wg{kAQlfo;RtsdLJM_4Uq$qQHl7g}{G*Lq| z%~JGg04aL4fE2wYI$ooO+BDRuq177lf{#30*&=NPEz-EP8hTJedo)zza?*0$QKj9X zp?w-ENO3~zG_+PjcWWr3p>kV(*@h$MY-7L16}laIof^7LLmM=-T|+#BPw}@;L++y- z+(ZqvX^7@+Dh16d6hyle1>LHl{TiaJi3;u1(A^r^qaldzpYrXCE?d^PU53{{(eXPE zS0}C*riN(9IfnTr5Hu}|VXk5vNH&IH-|`yxj$yvfIO*}2yMUnAzUx48{dMwU9VD=|qXjE686(8qxVCs&I$D9ug4Z&h`E&zrGV=>HSOm%8JW;rpZ z$75Cl!*2z#R9wZm!^S`;`27l)4GEaXfEh}_JV!c{6djd{+Px$J^BFLABw*-JaDM{k zEMSJth^JEqOdZsQ94g9-aNB^&k72F`hW&^e+$v4rR#El@F$~=mmScR5rTRTELl~oC znC-v}p#P0w{sK%PdfFJ~@4$G`t1jLy&Du#Njv*|$ih_e?rROhJQx9m7XLwNW#Ct-X^ znCmpgsrMU_F#nN+c`OODCkgXz5+<8!B0nduGm|jqC1GlkFqbD`T9Yu}OTzpl0Yf8` zWu0qA@pD%~90tltc>>Q*CB^w533D_nJR#NBk}%VgFjpjDmM39?NtoM{FoQ{$Cz3F< zTTbNgy#$QNr=@FD9F9SWsZLJ9oRfedofR-^NlaCn5Qky)Ntli#%$g)je-h?rNtpW* zFydQ0p?mpKl|9bMg=K!?#^Pr?{%pz?E49iN2BPr}qBVf;y$dy+7J zOTwItJ}5Ew%abr>66T>K%ri-t50Wq$7=aV>nwo^UFbQ*60;U;HNASH)>$-Z_FNk%A zQLpTj8e6+X{e@>W(c_02di)b{T#gqpkXmE5Mmtdr8gimbb1*;Vjq-$FZ< zda(*~>_xO|_aoWHIVD#XeAG)=7AhJ}kSeee5RK*>JS7F|F=h>EjFqwzwq_k&E9k~v zu_#X96(!o*AQ4PpY=`Y}Xqx}Q8YLkB&;Cxx;p#E4l;IPg-1YIb@fuRPa7O7T&?x268A}B4$;q57LFI#R<(8 zvY1gN1i+{g+SW?1{&CT}n%FUp5g-?m;o+8l|E zQb)J3&^t#jwMD7TEd{15f$XfLJTa^t4aO@tklR+8Q6~&^H^7{!DZZGfo@F>vU|Gis z9G!qvnjgh}SGU8CO`VoQ&Upo~tfdQxC2sAmZ${q{Cn={VV)0nTz9wxVXLCveN1W!+ zsamz28(I*`8rOeFk;MSFp1IqtHI}oJ4-?v&H(HF+*(9e})zI#%Uj?h=cp=!8B4O+S zlao1Sxp5)rkJwY&MUXD1quqytgB?s;D^4grQeH5s^`rM>-|Gkg1Fx?G24?IbM%RzB zm5qy;P@5ty_G1u--S4SJfZj)0#HzN}4(nP4`C4Je*V>FK9+eYKGGj>eR&o%%eU3$U zd%{5$s)ANDtgc7tm-)N8SYA~fXtNICu?-~^8|s(~e^;Ax(uG3DG%lV&yOx}hvAe## z3B_@;aa1A}EXt8ME`4!ij1{plElwN1G}Wjf^?#Do)?PIUqOY|4OR6HFJGHw^R1a;;<21#oLRMUo1!uBMl7~E7xj9tUs}ecb*wC-Ag_Tl)d=A82>}Q<)hC-c^U7kSzpEZ zNncYjC*a0-*nXnUH%V2yX0boW_Q;nSb4qJf=H|i^>J{7IKh=pJd>O)v#SZSlV_1;P zATEGwcVP)m2;iA14B}5{$NURe1L=6@M5gIx)*zj_ChlKJ4@)A1Vk}KR?~`P#=wE@Pvd_>A2_E8>F`a zK6%UASoBLs!AaVZ{Sad$1q26M9TD)>;F&kU;4PG z=Uz~D1xd>a+JX}2Xvl5zB?!C1+%KY4+o`a>EQ6euuG@}xI#0FSuuHDYx}RB^_p>;N zAbzwVI1z)BDZCdSfa{)OZo7iVJZ!t`$pf7K0IS6OsOXR81LPnq+#qWsN{1lMzOyOO zPqEbs=S46Mf2t~3jdh3?Ek>w9jf~i7xSvez=Pg5qb*xmZ*d}hOgM%9JyAC=%J4iK+ zoBOT7JQM^8vT@^`=~ys2_eIGOZ=eoM95oH3TZ$>0{izoP)-jCX*_ahLG{x5*IJDT; ziin4InSE?cjKN3gRIK}m>V3y|F}cH+EA0XZ$~7MjONW8E-0j&|?&=xxVSiuQ6o2Y) z%3{yP*)AMe0#Ezz2Dg_vKKoH#9Op3dBkV&1_7V1JPLNZ7%!Xgf9Zy5p`t}pRk8rt7 zu@n|A@o0?P34hFk6@X-yBAw6_ejNM=EyLwog>yN;xroP|%Qj;-OzCvMd^2+*6|Dx3 zaFtIN)849S;kzQ@U~{Ff5^g}u;Ip8LeVm7%Go?=hKIg0lm~z-%XvDd`Q3VORTFgNY3}!I+<}ez1ET-f?z<=QXF456cP|)h84iZ!0Hokhu0shs^b4?=m>Wd1 zoS1Rq;o)$(T@NpYRPzt6+mZp4^yr9u z8oe(K(ybI~tQjrA+YH|SO*IEQ+fc#L!&Sy&IAD=2lQtGignYSPiMfeirzQK!qg>5q zjXOA$h79?AHC%q^8~Ek#C*6~SLm7}DBz!sA`nv5bqV5mX;~+`JSCH)Ir#xth2Umpm zv0YnL+KC;Si!Fmk$OJZ}!Y#$uLNgm8S5C0NzBUi!mz`E^Z zU&>|EVkOPulIzHSs&QRK8N2+!eQofF96)TOD#r~jFYNQlzKTu0CqSZ)BNuc9v#5>I z^-0S{rIaJ^Zg_Kq@*jon+kXmLM0qbfGtkXQ@ccB-`bPm+Y4BV-5rnxvmeV=5dKAp| ze+3}8y-oPH=8R)k%MU%_>dbkFx$atgOFPQ&^o(QoAOk6|XzCANjT=N!Ib4}r&0ZVf zBHK#vv;gkf9)><+WcOPR*CwU{ZBA=eEi^{Zx+%8@@vZ92a0)qmm{m10WRDd#UsVW6 zFLG1~j-o0#Iu}XP-;cPZ{iJfz!1eg+xt`zeVthrn0+~>r z-hTk2_$u=B?dL;yio=(9+rXTx2#rc|YG%FGL({;m=b`sP3-avAi>L2TtQ)&SkNHa( zECEHc8qr~Z@}}=oSbH}I^En83yO5G(opk8v>Gi~;z78a`NvaG8i-1-H68{t zq5{af5+He~;T@rssh)?*$3rbxIc!W-P>Y%9dmm=Ii0*|(b3CZdB$M&#lg-qpx{{^?&cg6|*t&d) zxsJ7E{*d(q^_2Ruqz2)e)7;E@tIkZ9z!(F{!NW#i8Jehc(GfZ*)Xl@{m!51;^k;xN zVQr8c;h)*+CzBXp%B(@=j~QTz)lVHlO+&8=flyw4IeWSQbgkpg^5qt8I(#jnJbuzz zc+P$wy>+4WBaUGw`~#$lJ+<#+=5Ou_&n^cc18)Foew}|XxTRA1#7z^VPrM-4LVHIy zAty|+j?8OjKE_1m1!vK82bDEDA7edkvqIJ0X0xlb*}UdF^0p^(8+@>$H>^ZOXR^jo zuU|4mdV2feK@oXY1`kyFveX1olFCHVl5 z15hQDldXW0J+^|m<{t2NAqLRNq~Oc_CPaC$5ikM~FFfNu%uX~K<`jjUJKh-(EIoFp zGDTb?#3mn}UOL5O_vQKxHB9$izag9dOy7kbWzCehQcjB=2evJUS~Pad{w^+hYIHxH zV``<3J_pHf3PkUE7DBzf`4R|q?MX_YU?>nT(CBXw^(Z5-J#h1_#=707&RhC9 z^$KwlXYNuZ38}@4Bl`hoK1ItMD(!!*)MFD~aiAr+tPq~F12%a5TY;()Kxp!8! zxy`+=+niaMftpxzjmnivYh-TYgf?5QNGpGz&wW47b)NIbwZ3of`#bvZeD3SM?(6>l z=ehq}H>U(J$T_>~DQ;6iPUzc(62lI%iczy%$TVuu1nW3ml$6W3{I`-HqZEJGO|x=i zyo>%4zG8@m7^)eO<-K1*m@$A^ffs*z;Bu-eHx*gSy0&EXfXXt{j9b$XM7UhSZ8vc9 zE!ZUEf#o+*wTeNy55^4qGfZ{pzN`|!m7s!QDxk_zlr=!rU-`181Fq2wGXU3Wcq`yK z4XXgxYgh}oLBl%$xl;>r=K^lla6#}F=C=W8TR`LBQ%Bw!;r?PTV$rqi6eq2uo3jR@ zE3>+h5>H?g2)?*a;Yc&pc(>2dFuq_ypVu|Esdj9iHyMYM$M<0-$-F;^pCAYm!mcuz7s|qzL!(MlYrOXYG67Qt?)I4|H$y$$ znoke$e3t4S04*5fp-CLywLb2&gLaJ)UX}PRch_@kG4mqdlVdMF@-*t(r5ciZ#bQ2#30BNRR%Y@5yZ4 z5N;K(%^dZG{>kBo?q{Rzi)qhxM(b_4cK#yF|JOl%0F;d=l?7P4#P5+5hhrUj=rlZ3 z95aA7PrtO{!M$qqmv@;1Q(V*OB<_#iwIUKM`qr<9>2*5j$rw-r^09uo z7Qp*yR(RA?zTk;6ME_X_!v9ddDkITyE%RqNFP?5XiazU|2|6Eo#7>K6+SHludVT26 z15yzlRu7(H5c`A*Ib)m#ay6cncrXpCexxz8HsWFT$e$NaC!RfcR^y?p_5^i4+c!Gx ze%F-I>46cGM|SS!Oa>}P_j6JCmQ7I1P(LM_NV z>T5sajU%$^bPOlZ+E5{V=I;)Sz#bgMI=Ew0RjSs+IrC&ek%FDAsHPfAg7;J&^c=s3KQ zWiVV;N=I)PnRpH*MCa<1cnMR0V6TNt7up^*m_Qs@54{QBmUH$A(@lsoxdHQdE(;lF zj5uSGq|PU5R4Fvh%tTK-mg$ zr@IFz$?;d9F^cv+(D{nSD|xhUmjiu8*|L|`=)c7F5YPq61-SGoK{{-SUPrBn{p`C&9p=svbH9!|C+ABah%zqVh7MkA)%Jxzqo~cQ9 zlmM}mr8{bcR(+Zk%X%O!<(opgUua`dDs?^@1|MgWw?5*t=-t z5tJo}WlGyJZ#BvlR1Kv4trJ?kAl7^Bf<28!O9U+w)GDY=P`98SAnnHvq3sg1TM&CK z?Z;k0`vnaMIw!T?=Xdvr`AvNOp2kD$55yv!1`%d^I|CX#83>O zSbJ>07eo2a7|MUeP&k99f0mr*V<>-)pm1MLZN%PTy8n9w4=L3Cq<@qgyVIO0ij{*s zYuI$D9oiTdV6UY%TH8s@?XL`!5~aM48jpWxmF--@UR;j}tO6Z+5!-JGWa@aQgapNO& zk3tooxrfu_gbu*w>9hSeDJy98qtaB<%D&Yyf^9m~(M3A48PZk~wAJmUDA08%|oo^=09Km3~pne|wWe%q+PK`>1Rw)P|Vw9zpJxPrE z@%zbx$BDnXN#^L9p$k*4g{fR9_R_=*N_5V`-^kzRHDaMuHS2>nQ}AS?k=bO#kLi)< zWceX?__qM?Uo^C4(%uWHvslSkJs=B=5nCcOmjxb;By^QZ?LbX6_jS%ey;(mXErsOF{3ekkzfGR$SH zIiFT*#utviBHZukmh>9TbLcOY*jh7yNai5u>ymZAFT(48((jpK%v8{?);Rl(*@lPD zKWaYK2KyOaQoVTU_(5g+fv+cCKe{%SFJ@ozUc{A4Lgye&_+uW^U+y!0m6@^Y3Y!s*3!pocAc2;7VA~t?=jN>_W>{|u&@jdvl!6-+n~eO zR7RiCWy!TCLuh<>6-}+1aVf%~ZsEdy8S`EoGhMC7xez&(9}pD09PHRW0@*H1O*&4z z5;&Bs_ZX3sK}&1t9QR`R$Ks-kX;wgj*qBBxB?2Y!)xe=tV|!qSbyZt8n7Jp!#hD81 zS5?9xB$ooXQa$vvE{7BZZzOAaBh}mr0uwh^>~uLRyL?V33mQZyaqTA*??mugQMaOP#+mj= zZ!l;-Lmyx*at`*n=H%@&H}k>{TRhC2@Ti)&JPgL6-BuMt@#;ohT!;L7AB*aECHSvG z5lusM6Hw?7a+j4r$y`o-8WDA4gML~OE*1Sb40C!1ljm?1xt�g`C@){ix5Bif~x< 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 WXr{5UtD_Z_(G=r{b@7APqW&MX0}RRl literal 385996 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
tLVMeX)ul!A?S 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%0DgVC<&&C$?9 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 diff --git a/libs/curl/lib32/libcurl.dll b/libs/curl/lib32/libcurl.dll new file mode 100644 index 0000000000000000000000000000000000000000..61925326fda809e108b050fd47012ecd0d0c7216 GIT binary patch literal 725504 zcmdqKdwf*Y)&D;e1{frIqC^dnYHUXvO4OucO*CjU7qOzo3Q8+lY?Vg~R;m-E7VXe8 z1Dqa4)1p;dTebE=tL>wYhzN=aU;=#Ba!_gk$XmwTf+Y-(jrYW z&3lohJ4%Y^H~(%2o{v#&BMo1XNQEt@0xHix^VqmoBw3-ak&#{2l)-i0$M84Ubuh)nM|@(K%B`Y3?y+cRjL$zLab`UnX>5~=4o z=wCymy5Y$0TzRFEs`us3J$ntx9e7Qt-ivL$MwZAl@keSWbp6k9Dt?6Hl?nFG3)^mV`2U&nW>EC~L?97?&TQRS1hjZ^S zm7QnI^wxsmP`!W!dE6k-D``dOaEWdY@IDJE-#us*@UDs@WRW8FkY8P>1=-bU5jI zDK?PxIA58qPTE&$?GdL_Hd*dZPE>X`C1O%SZ*n5ecZFA)=-=qRRqPIxETT|sD7R#{ z@A)~SJulo3E*&1}9xCQR6um37G{xJ0W9l@TzwOdHj2?6(Ck9(1!z13jL|lY{IpU2^1pnF(O?s|?`} zX$t7y8bUqlel*0%90>Z{YXf3#H>CBG(DOjPTBB^xbw|Er?v;T8h^u>O$pco6P{&et zS2TH$KPgd$jg|zb1L>|S9x$Nbw2y{G1lP0G=nt(3U`42w9*q{$GGthkwi+d7fbZ-AG36BSlcdlR_aenRQN}Wiehk5Myk-ie+netf=WL8A?^x2 zqoX_Ab*1jhW6rx^t)++Jl5~Y1@oyGHW^1z2Z;Y8*7E|7DjCVI0L+Hj0qI!M(ZKJvy z<1j|Ul+|>cx7M@+1%lcOhevw;xhF&aFY!M&{j>O|nAWX_g;u{Z+mxvC?-v^=LJ41T zb_#~{UCg`bUHwe!KBQD7jJEXL7L9n_SXM@!5=mC3jz7vtFV@H8L{&`(?qN*NQF;HJ z-t458lIM0r>!@wc0nYN$A*o$QwY||58!|0Sz3R=>@zb32?ZBx~A}OjXPXvA6#?wu3s&D=(ZT;Zf&2QaJvMkaSh-~bh__xh~n;3!i+ibkBY+@`a;b_BYoA15v_f9W$`law20QU<#($taPG&TSdhQSD zQ~S+-s~Cp;bfQ9!GG3&>{;CMtY-J(oUGe1tOs{JNmpK5=(A<>K@!9cgI+0S9RE;C- z-@ijwcdBSSONK?LFqc7aiG@81LNBF=EZ4$%%qJDK8fq(zGYhO7O_E;gO@sKbrgz=zfmCGMp^->UN6lt032#tj+H_W8 zlJ{b29%Ayd9XJ3dvjSma=+%BQG2UB$OWot6w>XPO4kb}CnM5Z)4+TPUXWlm?;&tL1 zuDf4F09Uat>hs^wDg_4u1R;+J&e2qpizG zEc8Q=8V@f2619ZIss3f`M_v9zs(JV;YK4KkbtZ3xQk~|&s>XR2cmY9%@yCAI!$3s* z`(zrTK{oCW6IVa@*6`4{;Ir0SC1X=Hw_>HKxpFf<=goh}nxiXa8EF;gKth$#9sbWF zjIR#squ9_9^WhBA)kGHisPm8xN(shA+axu&+~{1HbguKzP40)dfEkqaR|#{b-&qu( z?~qsxg?f(HGneI0c1~nms8BWAJwt_YR5FJ?doMrcll1=q%b6 ze6-4VRB613Gen{yPwXk;YZA6ahC*pDn?I;g9z5u9=^C(A8*63Li4^F`jr*O3NVShpY=LNbUW!fykK3x*Q`W?yG?vIfj#16_adX) zfa@DRYR5j=ZCXL!+41q*?t$>BeZ{dPdXE}`(6;w~bNHTq0={D+S?19o-t$uTehQY{ zWi*7<7u#g%Ng{afBqtJc^f9?lLGUHi`>}rtNItL}m1`JmC6W!-y^$yfxl)#Fw_e=-h!W2^8^aSpW!~erbbfRQ1^Pxmz4Mhz-&d~p zwAX1cN8hIpEghFklzTfmcOSCz&CXrJ#=hrmyd}DR-xoy6jP`RM5zPjl83B5agYK5G z>qozE>0`>-`LF0>)KzNIqw9Gs-}eRcAsFGXa?Ssp;yrV&*L{xnHwGe;AaHs&?SMKp zB`&gWnGJ1wc?4!hpH08jH6N$gf1equVgUB<->zX5 z0k^E?_rPU(o%Elj(@lv)d(GzT8PV*?(Yns$q0aIt5%Zzl?wRt64RmFJC*^b z$h5-?kCFbr&ce!)iHp2p?z%|~5jFJ@YK^xgR}PMXJwa1}5D3T<0|D{wPZ9FUjnPQ% z3KdQst_GCWm*lDL_!RIm0XGlS1R*QNK%}9C!c}UmT+IMLNYTJ}axxsW7PMumeG_*h z;12pKCV);lph-Bd+x^!NjIqMW?8m3G`~VFUU*|QoYl3}2GoYJ!r!`S$dGP_M4<{sJ z=?!gf*YxwsJRPug8=cJ4VC@WwytUa_!`MEkta3Y|v_nDfDplvTKx=jnE+cMN46yQS z{cj*3k+;rsKCbDn+3GC+iWk&Ra54(tJO4S%?T%7^-Ft0s5O}5kMWWjubr$SUZcho? zBiA|gk3%#ZC|?v^f%Qhl_BrXBct$%_6Y0k%6N$FpAjEQ1pb{0}b+T8>->D>2nYRjt znS7uK5G35gRNz8B^(2%FIGaH22{^VC8Mfo?Vox zKVZTmNI2qTE>zZ8MUQA`mYd9nYLY_b9B5J_n{jQ;hLwWvQ9xFyR>?gxfHTzFQV576 zUnzSWI9Xt9MFU*t&KT&Z!*(S9^Wk4c;d2M8i{IlP@S4?)`3*5Pv`o`~AJCgrKkHVmfryA+n?pD(Vua;M7@tRIZk zY(~;0VLZ}Mj)5}{p({~)YkpMW@yRi|g5*hV-+^s%-qKBpHDTm&av3gMArj*n5@Nv{ z8;THgIr>o3p=VwL=E&4Pr>v~P?T}bv!JosjmL9s@N}oYcLgqVES;6^5c zaeft??z&3mE0-NkHQ{>Dp!rk(7b@oMfT)91%^pPI_vil^)I(@;MPZ5HC&a5-14_o+ zhIrIppu%d0LY)*@O%ZopsiLQFzSYimNy8W?w({uB{1!!sJDLBXyvl0!XwNC!F17pP zDsn?&njY!IR36!RsQQP&_lQ-!dgjNzPPfz&CnMsm1!OXB-mq+oR4)`_i+sWa?2quU zn4Z&2bokB28m>Hx(HiVH=^s*1EQN-k7_rcOw|BRg*^Hi*p%fuW6=@)FA}SfyThkk6 zxBCs{-p=eCY2*j)ZlV&+{wX@`e4|L*ZbJ8^_BX76TFu#+=t^@gCVt8D5%t!X)uo=j zLd2M||BZ65bbej;kKZ@LyL@39s0@>49j!y6u)%UnbwswBeoeV}=C-NWW?A;CsLZI{ zZ@TZ}n$7;adq3XUn~nXmtt<#Dh%)!O|t0#ydA*? zMp^mNZEKj|7yZ&|Tx9ON7a9_ zN6X*0|9Aej{{gMV(2hZS^E4*Me3rvwkVBqs*`j3@|A>B6SRuUES`?YV`k>~c7i0VW zt%=)tK)IQi^4(?FY3fe7?T_qKz;6&>?l81pe)hwd;~pzZ*pMWS8osD0admHJk&{^( z1C^%yH;!wL;F`OdVpq$zjrX=lC}1-aNnU9pnB6j*{*w;$s(mRjzV5x`h}3b%Iq7!L z0$T}a#J{Gn-q*Usj9`OrGE70{3W9=P?}Lh87SrU@Vhf$=PrjWx@rdNtzz{o5wc#ti zPfgeWp=9!2uXF5&^FK7*_H|#wIP*32+$4d~pFwPOHkr6u2m+ZaZEDO4L`Q%~9m{OL zStQ0jP<}WD?py|4hL%iHc#QzX!~AuU@1ElVe>g7uYHv5lulAdqkG-biIaEAv>>95- zx@qi|&Ro&^zt7z#^ReS@CZV$rC9Xy^-VVIrMLVq*oYf3xT?JM6+}>ry?@+uhJBNaM z2*ZKzoQ?{uN zbD2Mi3SSlY)dFud4X3wo(jSNcw1M@V^Sn+c{Se<{KlC1V(ogW_e#lt#LtYTYad^4H z%=C*Cmz{Mrl^#Nx#=XGm?c&~qonQw z0tMIAh;-jxE7H-b%a>rhF0}h|MSAg_pC{7)o}`#1d35LzV)LxV!f&yu1N{$vMZagX zi8=SMC7wHtmVh~{NCi-m6neyh70jMUb{e&~m&Bvp4cHhQww2=H4M>$m1sm;s{pl}!Y zhlHbPqf7rulg8WuE0dVH9qzps9Q*-t0|iQSj+BI571STO!8*R__dq(iCjQDdzSOf| zwI>LfEq@qDXM0iR0rD$o){mH2@nLKNm19B`gXO_W6(V#%V>9Q-lv$yLml)w{oH!5Div-L6Lia>EFxUNA zs@#;gMB%J446G#&x4M`S+ZyS9Ihd?bLSzL*?isF9H&Jo$x7(5n*K7VsKCks5 z;|?ZM%Pdr}l0bX!Ue{=nlX+1wR?^}jm^?C+bmkY}%uN8)@axe}*A5>N$z7|IIdaSi z$b{Nh@G=GpznR$Q zL%q(r_eOto%~4P}o`G};Ma(qiB%A<6V%q8{a@NilIg{*QWlrSM7gK-T-M>DX?j>Y028laWPqSM)g~FO#p|_dO6*@FlAn52wMeQ-h=w`t$MKkt2>c?g)BO z{vOfQ5FaeZbG>I*-nJkbVb5Gm$JyTFUZ?w31ugQSlcnQe&gaBiBX#e2Yew(7Cb^QS z^I_!mIax);E1-3D-HsLVa7z0DX>Na+bKO!B=J&6q<(%sj?|6OQuFk$g=F{Ag~FG{s?*x?<7lL3oD9Lad-q|#eA04ypqc@W|tH5f48Pwx=ej2SbAQPaVP!P1E&7)9;uKNyknW!!ym#VhEnsz-UC zyDTIle)XU#MEgP1lA8r0>rV{+TW(?5{qEhR;CBi-+S#w!jROAt^7vuTnyaZsk4J&rR{V(w>fE*8V8P!q2(ySkU$DmcTX!Y=2O|jK_x=O^ zu`2VU5I>3-b8r8EF7&qU)OsHJ*PVKz@TNW2Htp}>Tiyj3dEs8Pf0x0b&oDN4_%*hn z?cVFEllv3*|Bm|Gv-g#ZRL?k3e}6TJzsS&-lcHx<7=mBE-rrxdp{{fErg_87oB|sL zhV6S!%r#qe^OgDcX>+x|s%a1gO(n3H7EA?$=hiA`Nj-n_C1p>ZtIgKllF6S+Pu=+% zDW7<*2=kVRuzWGwU)@(mQm!$t7uowN^nLWeccs3+l>c^?`$A}jm?x9yZYav$r+wp# zOnPotC~wc5N}{)98h`T@+NQJp1pXR>MxJI94y{u?PRoi~$?NuZ&=YBcPyLAXh_KvA zUq{P$eIqtH=`?RZmp#&Z_CJ3Q7;_(r@q4Q$g1=FJ7vl3qWZTOf@UXx)uq0{8VTy{r>Eh$0!tBl6nWi*DhZQnKRJ1;Snf|vNVNs1HL z;egWp>SZ~vJ`wpW7P#bN-sHWtxX#mtV4=a@_5#*|#wuSHRmeAc^5%IrBvKF_%moai zn9B60+`JatDAcB58EsEdJ_n4D(5$F*eG~NwD)@-ZTCUrjXYSp8}0O>Ag!p zUAIN6CZjtaBp8S|r*Cpj?^652S8)&*Su8e^FpcJ&iv3RWXCe(hH`LF}1 zE--|Q-smizj%k|h3YWfT{6kQ);d3eYxA!+uu*p!+wc1j!#W}sRK*56N4F%IlxCjcS zQ_)2=y{K8zbK_L(Eh*-2o!^0Qo%HEo@b8oTJi=aruXrZe+p+T)n`ECE0uQH?vHC4SF-dz8_rHY;boE zz4m}Y9p0v~o!Qu_)Y0X=>!%-ALH~xwP02|{TjYJU!%EKDRDoRuhrGl9N%_x?SmSlI z4L72wVHdC=#1Jtq{kT5H-0-+adhl59B)r*!v{_{ICMWw>HW57kP{MQ@VS{a(1-@ir zs`EhFq#K+l*)(N5cg&6(Pn(%~qId?nv=}5Wefa#{=4IQe09o)MToBmWo02V;IGL*n zztFx`|E9lFn-(HiN|37BVLL;}+8;9(U1%KOO~yb7k72dKUntw89DmU#6HfUHjY-`U znp81xCH_Kr3jZeKDMCl&FO19KGw3(fsyb@bFjtxxJbJAeHcy>E)0a4D%@k|&ybAGQTnnPTY;+q$Byg0vCi{SAcTcUE<=!X^HY}cZqi1O1Ir5 zig_#Ec9-bnE#|v<>XryqfjZ+zrv_i%gF3UbThoBmqAv78_!;>K|9|xlKi-1j-=}tf zY4zQccXp*(P%e=JMT3paC6vDtR!yvFZCn(QBaq9%So-g;8oL3LvrEsZ@h| z{@;YtGw(aQDtPCB$P$?us_4qlybtT19o^|HzMM92-)LZy9-M$|ILoW!bc*O)XXVFUbpOsyvG+%7@c2+p) z2c>|nQKcoJD$#_dD?%dHhzss&P#^EU(x4D1S?u3u^lO#$YY3Z8M$Jo19)cE)(&k5= zX3O=ol_g(>MvaCUdtFAIFcTM7JgwKZVL*`kyKzpkXt_wmQ`3+rud(1sn`hd}U-A|e z`mPKsyEyTNSljzOSToLxlz)(-`q3tY9K0*Gjb9~iv&~DgFHd>Tm^>L5c{vAq28j42YPu8MUFx42u^U>7+UM&^Vg~O&d)m_?0GdCj8nsuQsgk>v1xQSy@zx9 z7OK!tT>VBIRJ>k7QY`q|(*ylU?yi9)HM1XM9z9ElN*f7w)@>Qx{fO<9x}4LW4RNZe zg)?|_^FZ&Idy%PfGj39?>ZJ^21?4hr?-@PF{gJJm?0;0xc25oFOfdfZ17!mz;NE_WUpY-3MHM6Fr=(`OPm^hC-wAwX}@ngnBfFT z;ViUn%j1%ulsbK0WHB`z#un<{WZK~|C;L3I_>3)>VzJ=GP1Y7bS-eMl9ooagHvbax zXCb1gRFU*{)pd=2?3x%=Jyp|Dw?lImPcsd!fyX$Mj{(u-!p3LcH->+gG5jz<@d&xp zQj-$XgK1O%(&@C))D^Pey7f$VOim;TiL%?QK=!m^TR8aA9+aET%Y*|oF~wuS5Swp8 z5PV*;>~kc)_zy(Oh{pK5&<`5Fmn+GSBdv`VoRo14qLn(P4c?Aar+JE2wbbZ7-X`nK z$v%i!W=={>yJ&?P6{Cu?(^Tu~XiIJpneBB?0X<#Nqu=4pw7ru(jp}EvlDn`aciw5- zr81mj1KFc<~g;8V@7-Lg4alk zw*3=gV%y~<7MR`*4DY6ylzMz%D%8A%`%g7~WV-t-?{TKP9%NcEa3I3J{VEzMxb~TqR|5Oj>cJea1r6_Shcb;`<>dzoJZUb0s?wE(E2s zd{W|Qe@d0N9&J3>KRuztZnq09J_Rub578>N!8`mp2@bc$)3AIktoxTmGTZIva7lti za;=Bwe#W1?k=gxWOG}cM_|0WZ2OEJkt%2sa-&~q+rWK|RCw)2jUU#l_00I47bFDw6 zo&pt6l*nlr-sl-DoaLl1r9|#5{tUFAq}Y(0iZrMDr&l8xBP9u5Qi3x{Or358jZ((D znMV+-n@)>Ga@v_-^ej|>D)cM2Zh#_F(2!r8C;!71K!yW!5Dov?X$PZ4zyZS@w~$v>SX{C((9iV!y}FM zM|o^*-?+z7UkiG637Rs-Kf5>tFrlW&>TWdR^=Br~3m_^&hze@5^>jBH1@?bb%Ptfk z#)lB&2SL>8-9N8Mt9nuhpNYesl0y5Mn!FeMZ#9{+nXPTx2T6u~H)mAF{gz_+txJhq z+E;6}!FC^Ze(;P+4yGZ{H&FTQZ9_Q;y_Q{Y?0Ep%8l~!utj`Z%@y{90+2HF?s|rtB?9w_H&|+NZ-=LzqrBTwYa;X(o}y=lh@kh zwKQ>jy}_;_b`EIkjQ06wRHmR(YILXuwCg!N*;3}6Q=a=JVAx|zJtA%Zapn#rGKhTD z^*5^UTI#));@nJOWk1zm`7~(R_IoPU(vbVQ!M8`gOw#bbg#lv7KhTDMVa`jnz(0xq zXZ}^Pwc_*mH)fXW)Eert!{M6!UVclpczYD$29f5qROfyPnDVRO-@x3lCye1A99~OR z?h~H2pMs0M@Z%vUtpR6-OQdzXpz}Q0=0b8dol1(jTWkYW3=r5(!(`@SyZ-T*(09M>3$x4M9L5HkAD4bp&1$!w>2Dn$Z=t@4`w+>20a%KL4_nqqj4Z+}Yp z$SGxhb7eBVlzS{TljN;&UmfA@8sVhZ3E=c9bxWQUnT);MU3)p{yU19&Z*rtR#c0gm z$yCr$&(m)L}qZ+!G`W8uTg{kig3aNm)%&)cfz}}(rc{et-)(7(dQkUUob3fC@*1WOG)qCe*Ib-CdjPy$RRm+$6xtE(SxmkpvsB)sKzxfK{%+E`TP*SFA*)ma zf=1UXSA4*@3J=iSnNv7x{fGMP-2Z5z!S`yDM2f!$!kZtIVNN>S%#JcGU`IkKrHl zQ(Jpp^49jekoWI)KTwixw>No#_rCu%9}7p(SV!;o8!MN_+MGSFf2s6MC$MyC+dh6{ zv8vu|Q~btq`_)*br-6b4`;zHx`z?d!L0he@I+^t@yOyenJx$kD(^b@q_4&slsNUok z7}8d2DEqGWf*rTzPGLGQWPprtCx(?#s(!m?ae(T4fS;=*E_fAtW~xt(y#M3;eG}66Y>oSzZ#lo z-ebWXJW@8C9*(6i8^GQ!9Uyrr$uw`GuAA$6UM_Njs2lE0z}{MlYY33qo(<*UH{#4sKtI`eO-%qpi0;{?*cJWIP*KB zU7LFP@v#d-lKhrZL(h#pw^@0##LV|y-0*@q=i!}99p!R!`D5)j#N%WRC8@^?xncjk z_b6MAq@~iT;MWHtA=Xs7f+f7wbdAZ>Rw7dN$-i;q~;yie3`W3sjN8Ceo5t1o0k;l&VWFjLwPF{<&2% zq7JjvFT)p{!2)J7QPQ(d=#Lm|l3MFM=a2Cxmievabsu_LM!$W{8zVL)U!{HxqrQsD zmvF%jKF9rQ9Hh~p9w*J9X1xpEn2>X;P#%uwpghJI?PMk2ESL|p&c!WS z9h*O)ir%x*sO+ZWpq6ES!P%v$vDT5}( zt}(>-D@q0b`o6Y!O|xqb%MOR2$jUQZyCId&xQY6oK>X*Bas=Nm(#UCwH5y+f0(?q;vs>W-Qnb=%rr$$OcChtURB+4Ty=kGXHkUswji$CMS* zDs2C~nY%y6t_w|;ZSpD7?KwBmsd*K6Xd+)-Yj_!&ZtzHN(~w|sVlMCm8JSo4d_;}$ zpk{-;m%7u5RugVUBJcmfxv8T(lJ02RtGn5Z=H0b>rH=m+mtHUltZKMJ%5>Xb7^UMJ z$swqxk_{##G;AO_YtbT6b7sWE{R?hF0g1**vwavdIJ<{A3s>;M z-nPF3Y2+g8$A^_J-4EwTahS@?g9`zmLX0Y^#OI+Cf%F`PU>=$*X!zCz4?IQ!$8!h%eB7CgI*r6LO z%tUcrV!Pl(z z&o(l;D-McxN1o1z<95AvaU58Uc2tB9D!;459w0iNVqK64{|!xXDcgV&J^uTnZE6@!*KFUzbj^LkCSp`GGle}kex#Y{a`%D0 zUg+XZga&3(KBxW0vROj(wIUret3zKUZ!TIt$+u~9HE#nAf-FOFgP*=-Ws~HpS0_D| zI&^;^osN^yJ)3Uda3^yDFG|q)*|5LMl)%_h?uBmP$*dm~WQuvUR~3gb4e{^aSkQkQwcdI_oX3YyRxyP=kMO_$O9fk3(fDh?>LJ{w)EKfyL5BAv#=i>aF)jx zO-h_8rY@C6_qXkry9h1nYMP)gCq0u?uMe-lbwm@m<)$L}T58vbIQTa4 z@nFRrthGPZ^=3h^=pCBz8^GE5t<1AvB>rG#*n4^e_YOR!s*;JO2hoQ9Rs8>BZzmhh z$G$uIqq!<;^DCofh% zqOC+R4}&CUdDGGTbQeKa)A-g%&y#Wj#`K!y1NO@c?0`^UA2UmaA1KPb&x_Eq-dZ6^ zmO_9>=XqH>vx957a?5)`7i=Dh@CgVdyY6PYwD6{m3=*YwHyDe_jYp?7Zqvqg5In?% z-)c4&PwXoe%6uT+3i_Uxce{}{%R`#r_Qr+DZpw^`_wWH$E|SIVV9I?(6M0%^3QpR= zXN$1I$bV|GCz!KG5*mZ$J!YIHd~`cyb+*+bV5%L?UEwQ*2)tE^n(a1J$^AgmnwL0} zyM4~+v(@N67^&h{@{)am`+g)qt-8_X4b0CVhA`GCcxgMo(9zL{?F-7wwHrT?wD%fI`Ucpwg4YtrC6zU1f?B@krFD4nKO^}Y-7H-F>E<%Th z!OOh!%X1IC4|_eiG1T|zRuC~SlbwrdQr8w|zF=PBtZzG+U5Nfc^=7Npk8#sUtNQ-V z)bkO^;uL%X%v)%v;$)@^{cq)e*;`J&y0H^M3S2OAsEwbs zn`7KSwh6D{IV>T_(f^W7sJA#AeBiel9vg&wO?53cb(yg{J8am)792%g zjCH!2uq2+rO>ZL=^hRmv&hRWUYQ3mDjIX%;f-=z7;Y}>?R&GK+nf;!Ed=x zLlD|yOx==$hP51LXEHc=0!25I4_*E#0}*{eKfF^UsPtBvK(7A33dU_l&AD^=4kQYgh;u^<>jii*3KhIqLK{p!)m3C( zl`A?jH(Ls&pgySC9^9HUfEr}r;>^7lV&m`7D&2VT2k8H)a!Iz`zMq%UvgKAv!^8ka z>9-7wj|QaQQYj|im6}eqeiWiiUF-)_Jq^M~J#Ey(x9P=loxq2J0u}Ze3vMiu60~*x zpQN%tRc#+oxAq6sTdY<`@o6Kw3aHS?igMrM!%oS>U>2Q(?Ok|G6rd2~o7f5~Cr?3W zJZL3GH?dOgDLGdwp*eX8{rrAOh|3|2bUw9)VE0mJ8pB%1ajGAykL1z=sIJYUI>n%3 zqD#A%4Y~@1_8BK!k^2aJ_$|ZjaY&7R$Dz!>vYM`6 z!O|fShGJyf6@YR6X<8iBW^c~bJL%WtgV7A(M;1Y1nbpT9RAOd{sL(lEy1SrGzj)ED z381vOmsz_%3yzfCiT3tCX=C1Z9z}_8wkvhQQU2aqc42S-_(cCE+J9h%S0~An=i7X8 zHN_b`PIQS{vA6+wRnEe3B2)`o+U*E6cZT_DdPkiq*CNzC>!jC0c&h${qrk(Kdp`O% znSOr^BFnsicY#xF=jL`NC%u6DYzNk9E{Un+klRbx-cYyx{LeY5yzU*t#`s@B5^8 ztI~*MVOE#ib%k5}?YgCeL^+95R)oAXnGnuT8WK4RPoq{KI_ur6E%5E6FECm7w>`3q z3)lWbyRrwD|ip zPEG%LUf-Y#&N%Z5Wbk_+dY+;_YtO~f4R*OTvmN!}2#QgNS2RbMJTUJAppW&R;J<`_ zYuo$X45XOH?TXdyZrjqGF&d?Wb;Wk-K6!WqXA#F)XzgfU(P_$mIV|t4iw`W$4E8U& zc#~}!$?s+tiW*}-pI}eL?MN(c)~D*2=6k7UOkJ1L^gfG;r+9}18#x#1{>4PtRJWg5 zs%kAC*;3j%P=$?iVn>ossz;f#nzY!X33$Dj~lMyVJl|=`Ao5%HTM_f@``kyp)c$ zFnd2!&31GALd_+4SIU+J=b6qp9>j0aiocWA79sL{k!fp5ly9t zggT*O5C3v733te;$aDUUOknzb5)YCVyzu%&xJUZ&no$%o6i7D_rcU05-`Rwl$p zin7$Ai#h9+lE1T8XNLOnLd}NUArKVug^a>}c}I|<+35<{?3KJra`fn@Zs2a}o?j2> z3s+XP6`R^k%k=z2jO|dzPY`(~zup95v^KhP8>4V*0!GUT2zI!xb$CS|>5;1B( zXJ*{Ko}#{HD%XLQ=H=o(1h_wkWs~G-&T{uwzB+=T?#o&S<>-oXd5iNS3t`Cj9(5D) zb=e!pV>cU-2qc*JUm>Qp<=ht~G9=meo@c_*d{O@%gp@%Di?QZ>gY|b>=GI>7J!uBX zHRqahjn)3mA_y;XDJSuyGlU#}0gUYL;xslue2onwf9F=%`JY5+%;dhR-`3G{kFhiFqmZPS5g(*{2U2hhdv17X_v#Zt z98P;ShWf{P+tyIy$l6Yo>`Y)Dd~45Ng?Mnh=2kG`l=d^=i&()yuDK=aFcj-E@}Lbb z(;c$+8ClTMH$%H`u)t!ewhjchZey^1B93S+dAeZ4$;jRdW*Iu7_#!C z^o6{};*T#W#c%VwAP+D@YnEoV6VTK>Mt=o0vk8UFom-~sG9em5Z0#Lp6gvSW__bEA z$NP6r*xt|+r=?0*GZ8D#iU%6&8G@QZOjOrHd@51JZVM*qI67eudQ2%FY$~=oEcP1} zs}DU!uh?-iujgE;mgQ)Du%f7xE3<}WdA){z`q*E*H1 zurv{Tl~}&p*cl>|feH(CMS{M`(zNh8>v@SUlVNAtHRpvA+`Mpz+Kz4ePUc6rIw&et zmrN7gYuhjuj6V}jpj2&iE-*sAD&%s}zlSN$1Y}3r@+GQ6Iw3gIhSH5#LOKyt0$V?zVTF@h|Or=$hU&F?RHwWI8!BqU}B@E*{JV zgx)m`$uVR$kg#Xwvcm<`yQYYfVr$~b!vRt;1Jv^nU(%U9iy-ITH5JKGWLD%edwyp9 z+D2SFW3SPh=i?vy8H+@{vtqd)@NV+YW^>sucTlMJC%?htmLyM@|Izg*#I;|W|IyEQ zCQmTo!~BnQ$5z{y!o;!mJ6Yo_|LH|^%jb3EhPtmrz4dErHhAj?V#+xYQ@S_xZ`?z) zZBx&ygT9N}I(s&wO)c>q=( zJoG+OfV@~36VKN4f>b?jy&v~)v4geZk&W@>6pXXh`ETyvahA`&7=GPVw}U9^ZQ(ec z`||FZ?ISiWG8tMN&((E}Ij#7bBel;_`-a4bwsj-$|7Pz+^vaU4SH?%rggSnGqcHyP z&n^-91~S~%Fdt~&)r6z(->kn^vJ;5I`_sI}Vt-Y;QlQm4E$;oO#P=8Jn`=)JsF~7z ze{q;nNs70_bq&qku2AV;w?VBpC@Ij<-Dqsj+tJ;~_K|@+ey_8etxHd{L(l)lr08R; zQ9;2~bG^U$i#{f>_8TYGZMy1&Z0R?m8#cXJx5;VT#GN8H^^rx492>h4*Dk^L@4rX^ zUq|x$-a=&#AmT+SvoQ+)VJG#F#tXHh8?iIZ&hV$zvJ)HX!2yRvtkEuA8+R1TW4@c>P~@vqH}v`1T)p72wB4^=fjH_+DpEbYRb))&+uJsyg9J-N_MydHd zy*I$-^>Gk=y>n|v&vp0#ch}J56Yk5OEHZS8E*ls34}; z5=%?4xlKa+3K&YZ?^dI;|uNkMpM&>_Y?agb*VdV(1#rJDa}OHuA0$Xcq@Ooi^RO&&R>QW zll{N`PPVc=d5CY%lbEQ37PXyz_X1_>AFWx7B;BqGSd9hOZz52mgE(+ROj+Y`)_cjv z5&)U~kOS_hy?3GPqMY+rpI(=C|!DQ6b4K_QVcV9P&F;DqZ=>n( z8Q;u2iKjeofqtTqkI2k=VtfSXmm4ud?TIqr+!CC>*412;xZ)&OURo*E<_&+ zKa=eo2@PIqBii6a`ENo>>a^l$7?&@2E+p#fBI*(fd&hiCPd}c;2X7fsBg1Dwo zE~;j??O^zgeRoJ?UW%xd-Ju=c)!4Dn@TS`9DeNYhld;oJCdesMz>4&3+m-E3gg0I z1a$}sQ&_2*s4=+XB=ppPslq=|;mXU&GwfUgTC*&=HGo-4_UBagyp{o@OJnQxJe<3x2vdZj&YSG@rhVTW`ZJN3+%^%(j zsvu~vGU}xi90FJsB;O!rVW}jmD{O5*S4lgR@LjL6ylZ1ZFz+Vnwl_^@I#et9=8Tla zWslkf=}x6T!sDkWHfH)C6o)HL;E(-%#d^s2jX2V@qvZHa$N^2lGuz7sM6M0(`(|{f zbfs6CNH$yNEb*>vp?q$O7LY$j3{p%GBL>riMhyQTEvGvqED{q%<*oz)fC;eMWS@`R z`AQ{a;|lE`$eqDwz6U~o^Zn1XM77=Pxo^)XHFRO0CEII`YkRjo>Tbmrv>CL7Ia&QK zRem3+5#vyP6B|p=%+wC(KC;Q)%`EN6-(5&Z3B zQ|!&LFJ)Vbvfm%He?UjR_9tS^AMgr3dTkh+t&Y`3|ML9x6(CfF=&`AADIjL<9}~i8i*tRon2C zZ*aP+6ieY`f2ewlmnljfW~rH=(W{e&TZ`$(d*`udzC@Zl>90WTyNZk%EhqPL`-X9O zxFLZ(cDgr%ADZRwfcm*hW$z}DqBg@UrI{p|e6`?r1h+t`%<^YI$-CQA5zDN9S#bcV zy{;X5zmm_S|NB=_a$gQOpB(zdkPnIB$9MI*iff{6zYV!a-ZDV>b)wuOx`8@F?4C3! zqDc9scy7AUFGoSa(Xpbamw>5XIkVFr71JR*ilkzdH65 zNSwOj*X;AiE1>k>+FO;MOf=vln&@uLeXq^~3ly9C2hTXHp(uNoG-k$xh9WhoR1U3L zOFh0(B`m|S$`ffD=DE@$-*9U)6tNFA#T(jMQ@*0fsMY79WQA>sinarNw}cFe za`n7^MVzV>#?y~CcyBWN~5~x*k(E{$tt@i(l^j(LlyaFAGimT&faFoWYlZ>snj(xI1dKk z*v@7$T*kF4h6!f;!o*is_cq33PCAf0_KCTt#bb2GBT9n)$h*(m_$^dD`=@_9bIQTeY$_6>KRD3vKjW0-4J*X%6L;2Ks<8mM7C0g$N5KCsNFq|AP(#;TB8p3}klPl-YC zSn&K3+w4Cj-`%Hy8XIg>_(KwU7F&;GE;YeU8C9uv5brq+NiS*)rm#N8Xsg0Mn$cvb z`Q36GKoG6kmu`$uPGIl}X5;Uyy-mZ9CHw=4fv^k)oJ?!$LD*syWYU zF5uu%#T4km{H&tA=gEBpWsJ~yX@51Fqb9MGqCJ=;;Howu8!Dm39(^ixXz@97h}EG< zSbEhsE*h-U%}TY-DI1yQB8u$I$e}ejnLk>%TKv%5TcQff#_xMpOj>xE&8e^1JS1~F zq>j4rcl`CtyJz2-)dHE>i*wr_j4w0wF}0Sy9$5bXp~E{>^FzGZ1nmf|wSH^dTV$pJ zvu4__U9)}OA~O{@vBXzi7{T=12>WImm^mp(dtB!?8d-=a5U-QkvZ>FWh0p;U)lJ1#E{oKxGV(8koQeY>M zUz;_bnDa-(S$((I-#z5AojHx!Io?_lMNI z>HOq6FyL{%=bskKPCh2W4~^G7c2yNLG)05KGo%-A19S9B4ej%1u`PD)lj)s1FSC8_ z-01df@z-#_lmYsA&Was9($FBIU z>h5{k_{BnNT8ktA3JsJ%(Dc&A{0q~_?WQu#{8BKu{-$3PYNug=@@cb=5cT~{Z{Bl;feP~_DY6V>un)>Izr3ojFBzEvnyYnz1 z+w`sGS5yk$_cSxzh2v=W!3LloAuAHOYX*fn!U!L20%3%$AZ6LR%z%``7t6mHPmP} ziJAiGK8ofvft|M@;xj7(+o-W%yM78{ZElAQ-t-S4%U?4r+v`hAy19fgw_b2ok-~?y zNA2etPwb}O7pRrb|NRz}$4;Td%>@U+*}m-sWjfIwJ2c(9=fF>mOD`L*?t9O1>3nd} z(C0(**yp@QgHwgOyYA5|j*q_BvtLLD>-AnR_PM&gD-O#~wMp79{?w@SD|DYL5E`R1+60}S-WeLSXnA~gH$qby;M{T@prR$n(evP0+ODhl41pZP@$SMz_ z6xc?b2T~ev%>ZR7L%GxpGgtaFut1anU~ZQCE6;$!AaoV0@H@c3&@ zFVimj_sq8AuEP1(L+Kwqn#`*)I{v!(X~kkCzR&KVAz(ICw@<`xX$15N7^%xfuQ&t3 z>ceg&p7cCxNu8puH#q{kurAri+{t_sgJ%z9nc}80UB}_1zar-O@@J(hb6**72%Jx( zb&dy-+O5pq;*5SKmxZi&+hCjI0GZBq(ihnhT$68S zX^cMxxYbWhmV7$wkH)e7nctdts$_E+F`OdJ*Qh|y?h2XSsB0@E$o&R@rwF(q1P*^I zGmZu2k!eacsId9zPe>P?y(tYeinzgx)kOa%g>m>#!g08!f3UZyimW8p*;L{oqD5@} z#~}}HD(&(-9U5J>8}4!Ak#vF1hmk?<2=diFae9+(L&2)zn}u3uyeuTbMkQG z1UEf|dlop+pO-UBaJmbzFmC&FxyrEIZNh&QkV39_MO_kFc!`h?34#7eer^~|$uFqF zYXt((9OP0Yab@Q5#z)+8s{@>>3)Fh=+ro)K7XCE zOw<(iC+Le}Vt(IZ^{`)lWBT{#6;TJCjWKGCtEVa?;Edu&)q(D(26a zpwemX2BOu(Et>w0WflP?M~dIojx7Croop%AO2NPx#R|j=<_JNSv$z}p-gM~t`~l!j z(bt*F9{}#mex2jIn>-!oO(*^?j$Y7O827F)Vha*nU;P>BSiO$5!G{QBh!~WgV7RHz z-Ah}LFMmJ(t-Hm>t%(jEUFpPaJR%&*x=By|@v7NZ{qx89nO2M#T$lKn$@c9cE_;{e zaXq)6F?$!}X9mZL?kclmCt2gJGXBK5{sTOZ@3tdb{e19fw-4vlhaqu4>k9SxL!I;{ zKHCdJ?CHH3<2~AHSqRb)EFUh1l^0CMJU z>ZyX>MT#2cf?wNC_|9)hF}NAm9gn{()Yff2jVM?-)Wq~b#FI6>xNjes;CjFVPVK0f z3O?#J-)5uWJDl^8;_2wiB-`g`ed$DsILk9>VBMF8)H5Z}Yg06Z$#SMmd)1_w?G<5= zBG2x-O?#53PyO>BzMBH91PSiTj}Xt9 zSKn1+0DL&WGoN`9zx*59>~~MaB$>>SO5Bc)hFZs5QC`Pm1^o-2y4FhbDk(FCM9Z%< zA~5c|&NLX4pHAig4QdKu#=OMr)rv&StrmqlhMdesG^x8lmGgkvzA_M1MQI~(Jv$^d z3aKkaKmKJp7fQVcN~d{^cakoeL$*cQCDu2TiP*SqZJ*7B+j5CF#w7iWewoGFGNQ{# zBV5Hxi|TlW>^d2(Ci|Qpw7S^o~(q zw2S9nhU4MN6h2Uz5BU)01Dc3D2o4W&@!?yDrlh85R_8dFe8qSYo&Wo@_BnSR^xOCM z=k;RlIcM*^_S$Q$z4qE`@4a^T0EIOY>^=b2e8d8?H>d{`r-gJ`LHl&{lm9dtK&S$F zgvFr%F>$CrO#p}Z4k}f5gZxS58xD9~yj#V23=WQF$vkRp`kDvzYHf~@-NPLZTu}5K z@CXWRC>-r8;(E(@R)hoaoYcRiHb-&vqJ9o}p8I8Fo@p@^K0jZ>bC>n6` z@gxH4pT6(RYhsaNhokX?)rLcn1=fH#x55MG_-0`DP3MuDB-~&+BZYWtIkwexJUN`Y zJ2U-UZ7bi@&KKP1G^OykeG;`Zw;{^$T=vWHQcx%feRQxi0@VKpD3SAGA^9;eyH>bx zB_AS@zf}~*KXC9yn>nPQySXHnay*ykiC~bQ4U=iy#VE_q=~pPUbV1>19d;(K8{f%c zrOu?($Ev=Osu^tNftI>*ke3Pr-W0t7zdBeT^=51ApogrJ7UYM!j&W(6>kz`}kTsA7 za#O3$&o0)CX;;EWa5YH4bWsHnD1wudfZ(5$*#H%)(aC z={NIFBFiERndK#&ffSB3yMvBUa3eLJhV$uCvS_zJ9VOHnvEvr8Lu>l3L-1Hb&WrgS z!mkXP>hLo$ONl+>*}8k8Cj(E&7wsePx++j5=pQz%e&P`g`}_M0x~zPWtR1_}2fJav zIy1mqrNLceJ-RN)$D8m6WLK$|l%?kmlJGIs!0aCh^EFP8!aT=lMJNee6~K*_Flwe+ ztBf}H)gFh2+Hgvj%zwo)CB3wNU2|{cd0#`ZC?89@_Dx~3s9QNxDHZ;R1MNwjw{#|} zHnrAes|=skLU)W>v$AOjYovLO_UH$*|MiOw3|G!KQycx9O1GS4V;_4i4?_>JNt04L zZG>k%<$kHA8*CG1-Zo*fQ(wEo(Ie^HV$>$O8Ui;q+o1i0+uiuJjS~~n3jw8t>G=5j z3-R~YH1PA?d?U`MaoJm8GjuX=d3~r*o%Jv+ENy zVuTvO++lBT;Q}8wqB2umPG3btn1HL4+xN z70GUDI-h|Q;;>{E@ z>x@BD_|U(Qmtzp9#!kMt?HQ7vWSGzQb8x14GpouNYn53;6pNq2N^IY?g(vNilAP!& zUMD{Y!)kUcseae0zH5l8O-&FM0KpBn^VeU~_qlX_9Hfc~+>8^z7|*R2eX(w0+Xexg zz5ad*I1inh9Wj2oHK7{Y4yf?mlR#loH_JEQ8#xE5DGy^mxa{1j_dl3a!b%BCN$|p6 zvL#yntnaCOy{1qkMNjdragMx~njb1v`yE+rG%{?&MI`aCeHI&{zmwJCu+6=Xq0{x4 z+z>uSr$Cy-Kg9Hjy(tP#x~F&ghP6kojrxFJ$sZErN5T=G0r)*aV>?4@lc;6p*(k3P z<&be(v_(BQAbLwSG>$!J*V&}=H}cMRMmHxr@o$Q8!ScJqi0t2R4Jew8&>ni)gQIeis;&^vwV8;;k}=E8+Cf z&H?cFdxJDJmjI@7pVh-&OqWmdz=%ks{_^y72Uzk!>G=7nFyNu|jKct)7 zb)y66F3t2!39WW&v)+iYP{?13<>r@zc}d@1JkfS-ObZV1zBGX|cj?^k zG&YbsJ99uyVa9;qfaZR*n^Sk$Q))81lU4_Ld0zbLTB~;TTNP!2yRI%pj{Z_CrJPzZ zBECdL=?n4Ez(BOK)ub>>X_S%kV{E3$9oJ zPi+^Y&tdrIq~t|hnBE5L@K>J_N(QzxL$gZBSTjPL>ZYw;rT%fC_tXBV^tkQO zf5NENSX0268D&r}ox6bE?S`iPnPAa#)47evn-o$`$KXj;(NADa13vECQG!yh43$PaaM4jUU(T$oA1Z2TCPIR$JX}Dr>cV#C$Z=;r$mgTk`6jX$IqqoXS%(X4;EIa#Xq z$I4Ez@M{LD38Y1qhp`ui2#mi{Y--!|UCzvdI* zKROxhhfHk$=~2n$)jZN7dV?s>KKte@iiyX_(zl@=^{mY zmB2==w99(9zbWdibxWX36qJ#*+$kVb3gh*Y3g_@ul=*rr=V$RQgno+Vi)QjiiGlhP zY#H?jHx7IS@bPTDzxwo-^-jKhu^>JCS?ksl4-qaGV8q29=D-XQt_ZcsjsJ&HYYusl z8#Zwh9A{3H!;H@OUo77t&-s=wJ?~+12>#<@AA)*L3}4Gq(Fd^|%CWIY`=#^WLI)%k zPR^sNCi-MTP=I`s04%ahj(4VL+H&N}?fOiRYD^Lv1D-4o2<(u~U5W;?i>Na1vgrLd z1Sn1H?~n5@bXf#mV^aHVh`J(L+uo1XnBx~7?Ih|@@QeCF8;CxGk;*z6U23wBhW?>C!e)Z#+%6mT3h01rLI>BBB#gqi( z1VBdXT&a>!CXSVxk!H#AmD)JnZTj3z?$vfE`@oB_?AxgTk?&DW-x2OBzycYV7##@) zNmtI_CNa^_J*j`tSZR)cNk_^NFxhDfrz^ZY9huK1J^tuY;X6^~sv_}~v^BbMI*^JN z(6V60P__~r9DSF55&J6@kp&;QjCY+?af{NiclBLsYvz^6-&xWy)knD3<#&`Jz*Sq*+2zK${@X!5F^_*11iP)@8?5b=f}oYdOe+v6aC&cA$(Z z)GNbtuChM8c=TMH!AM?jOtHI)v{c*}J;ZPk(ZyZ4XQf~~M zvi#Txbv!hXsqdtz_4Wzh3wrss;1aXzn%hAnhFTT3pmX$!d+|RugOsg#ow5f)pT^Ai zlaR^&$swaDL)<%c3M1a2!fICRspx0AFP*!P2?u>)azA`RNq=bG&)WzApt%XT1oAsS z?V=!#QyCgmIIRlpaGIRuuF+%Jtx8Xdj>fXZNSKJye+v&`)CWg!*O}A>ac}%Iu|}hZ z(ZoH(?Rtoih}*R^R0anwl?HM~-{p^}e|;8=cyWl~}Wb@Td^4fa!@Y9~`X!SACK9O<;JNJ?Jc+me^LEml_M7zn|XMwiT_nRQ$}GOSNviG>N}{uVzF^y30S3J0!oJULIxmEI;k1%ZXKu>67dEMNTK zE&d9yx@LOm0olC+=IT=J>({cHc+&p^e$&mwrh`|HA{S{z$VF>nPoM7H$*y#Z zMgB%!1dX2r+IIzQQ4H;NdFBoA1cCewaS`xHid?B8%~r%^W}Tc|!Aje9)FE=o#A8>+ z6h3=x1J%ZHClM3ctAqii)D<+!_ioWpygeSQBYmXyF&_brC44#BW9s?izvojV9{{o@2p7Y#fN)YV=hbQ z4us@|(uu*Be`DrwHG8C*pmtSZ4$eX~yFXth@LTEkuMNNZyWY0{BL62EfCM$wr1MYn zSezrqi?)dRghDD?-d1h^g8R1X;{v&i{rg(gV9V-z3wg#roEo+1Dh}!?9k)E+`gz)I zkQwBy)b612=KixkG}+A!gt^J#Nb^Ql2#TxI!Z9d>%r zdOI1)yp2^wZ=Rm`SM*xl15Z--`sctoc16EuK9Q)9v9Lz$&i-6*Gz_hz}czmIbAx7?`nQNbLY z1dXEV#oD$1GC_U@D^_tBz8&WrLOGU9X;G}~Ec zlVHA=g?pf+Xo?W{F= zleGAZ^drb^EDozB8sPg5!+dd$UXTqFj{INw-nK- z(t&N(<|UJ-mc@Iz_NN327;*6B-S=1O(WyA(Gp5dnr-&AD!y5mOLGXfbDE5L-`N!zV ziIV_=S>TBOV)8EeXgG5_W(|7&|&mm^C~WO^fmX?>6xl>~K!uC~F<@ zPA9G9u=GRix0+4|b;{gEKlO*`627rsah=Gm?Mj7CO9@ZGFfv^t>kx45+XfC$H0Bt5AOCGb3%;K&Gs7)zQB!c(?m&A_|Y72 zw|bHT%jat#ySc|m`ucynzP(rljZsk$7j(?kN&IxaOlBzkDF2SE!a=+x%E|3(PQcy+ zcyW4e40fyN+iiI7*PQryV!YU{M1{gF8RZior^i4pVELyz5Y*raBFI)e0Ilx4c{M>V zAb%_@7+SNOW9UIXM!(@tj4=GMz8P5Gj4td<;T${NsJv*F3?xVJ5^Whh)tL>xm3wd0 zr1OvRO=@%{zsZ^R{9-v@;*NeAySQ)T-Hl1TiL1#A4NW!P0hQ1iz2cn`=o?dPz3wS5 zYroK2eHVBzdY?&Oa>`k&q4}@HrDw&Z*Rx`uoV~9nEp8y~+XuXlwMJ_A9C}OKs?Pga z7^zP$*nlhCE_5S0Be}-rcqG?(y{vhhhD60^wq1+ptWpa?X5_92Y1V*K6yq4)X0`Sa(FkXDpdTyXePCbeBsGD>6v>3!W`0LEqcRbF@PQvi_!4 z8fCXWTaI-jxrGfwm>IiHE4z-!>+~CE`oNgGRSQ^eiKSEkybW6X!5eN+X} zGkoQINHBrC<)_LSh+=ix%)JL z6#jqr{V=otsFw~|lb*K>NWL3iP5=1|r2Bj`jJgi83%*NN=F*GUZyO% zEN#h^_SD28vZff&yT6H~P}2{I+a2f7z= zgP)Bc6oF8kMea+A{FEXN8_{~7Mq!Io0?&>BJBej+c``Kq#WBhz(=n5eD?bP4FoO$ZB=7MpC zceMY%N*SiNI;+59E%;A5CX2&^N*Esp#&#YS0*D#UtfDPUy`PMC>%D{hSL&wG@v2;U zAQ`rWEfhC-uA!f~@jXVhrV~}=OEl#cjQcppUt+Oj99ZP^CJSDP{eqgxe}N3qx=sZt zvHVZ^C@7mrwTXV**{s#SW}V7(pX6J4f4Bn`V>XAYkvTFA4sZCqhD6lhdmt6t*CXDE zHVK{5dWsf~VkX*orWs##<8&E4#?@JxH|hp*NnIBaZ4lqbSP_?*O=@zBEU*m`%vBGH z%TNo7i>~O;{?Ra19Tp(k5_FkuPe}-^6 zLvZ$9lkI?bzhoNPw7ZD$CH`|S_5|QIxVI~-KUF5WVcot$2O(jFg zp7@yF6d}YB5$4n)@whwfh}k%Zovt~)j>c+QIcE?0po__n>ko}h?Lo)vy9^V?$<>U6 z(G?`MO6c;pO0Rm-tNlFb)pY)HzI5#(kN(=J#HlfhZeTR9g;r-7R7M{_O);h`-?DL` zZE>M#GQFIyp;@R~XrJg&q^f9>hmAC|v$sS|0y%RaX9x${`O2dNzv0--p*oYu2+Ns7 z8y&v5+cvf=?UQ*^s^nz%nwRgOXcKvn&(&MB>Al{g9SA6L2NTWB6%e~2JoR@F%jSaT z8)=^d(hiCG;b zX2!y$p!eyV2kV@eiR%m%cf$g8bii16B#e^xr4-Nnax%E7*c%L0LBOUq3qSk32CnMb z@TP}t-j-VoDBbOwN6$E%6&*b$`C8ML0^d!rQA>jvAz}~Tjkc~I#OwOWnto0df?PGD zv`!Tgz5Ok!0v02*2Ah5YtA>!s-Y8r3H#bEpTzO3w(oor9s>}(&a4=e>O{dW!o*

eh8cuQg_}BwK=ITt1ghc?<8}QpA>8Z6nmQUTS#{T&?|a2=1eXY zqFaFLoPE)!u>!=GfEWl|I#BBK{&2oF_|}Ia365-o?lMX{PDjH=+l7fm(2D*dH3-_L zBJ8W|;wmzITA7}SQMgJeXlEM=Qc8I^PLXk;W}b0_<{6lv1zEvm(M|MM-xKkj<=&Xi ztw&h>?$Ri^kP^l@_h|y?xDnzO@^qlPuVS4cGm1Q{qacomC>l1T<%&?0Ft+!Xqn zORw58=OQ#i2!KDiwy~ncGG^j+G-~v(@2wdCVX-acCKY$XXO*|UE{^s9D;kpyzP(A) zh~{cimq~28Z9T94S)qD?e~o}xncoR{{~CezJT-4ml7<-$8xg&WVHCNl6xf|NwR0=neIi~Eq5t|9Mpw*a0F3hcCxv&W;)`3k~ zz|{kz=JuzQR%?-}iE^>0Y75t@YuF%8@C3e3Aic7qdM?h)ihSEo20(;5s;>3+okSj$S{0n{&P{w`o0LKGcWpR7L=Eff{OXOYVyst+ zWP>;%x!QVOkijFYsXQC-TxY{IpWkV3BP~);z@VeeIW@q*4*gx|n zpba4xkSZ?$c|jBzgx)KdO@IqM3Q2B78JejqB2=g-j3vaTw*u*~7}B&D5`;2HKL=9# zlEhSS5@qokXK$1FON$%%Nm{#q;LV^UygUn-6G%bQK5f-XYQ(u5razB%mZma%GqUIK z<@vPk5i3gs2WC`^5S5uT61W3ruUBnOkgg=;Y3kGa^XCX@I4Sy`?Yo)LyK9jN<=&nY zmm)ruA(+g$WpfYSxrWG`>BlAy_-ML6dEiHJ`xt*zZ~NT%oxtIRt5e=T_W1_~lLzc0 z=RW^E|DVJ5mOnS_#Whi{9e1}sEBn};k2iWOF?zr#wK90Tc9MR~om@!W7%WuCx1D1f zD>#6#Y%tH+u<3i584zWnh+@~tbgBeLL5W?*piAlSMGPzyHio7aNTL4hfZj6H0bs&r7mTIwkv1Myi zr_5x_&d!G+9nL2Gj`}ER79JwJQYKn&f6+uLDfY3TX>S8d=AV>Sd7GbC)94Q_epk^d z5ea>3c`>gcL;o0`(nZUxx5&aI8d;)IG>ZA^wuS=AHemeF*g^ZNP<=WQDHS+DDsaSR zH;TB?gDJ8G2DrdN3It;{G#4qOpV!dHmc`J5oRQ-zouF^Vd}Q4kGgNh{Rrw zvw$Z;4Dgp@$}OwG@d+W?d_GIF$r%Ub*1w{sM!P4Kb5Es~c3gcF(xyIJw4~YVg+>xP zIPTx%Xi$!Xf3CCgwO)Bm9^>JOnCxAlc^w_i55!Wq&ZMwexAnT}B#7GGsK9T{!6ee95PS3Sh$z(_U(7Hr#5lyPr_ytiWGk<(N7q%-DC%AZtFfL zJ3s9vJ15H|@9i&??357HXc)Bv&h7U|;v4BIvRYU;nO~in*P*|f{8P<`2*o1k8h`2eT^2nQ4$4z%@)l9W&%-au#_T2OIgfQPTG)NRk`71 zNO-o2FdZ0U$mO(BHtNZAUIuIVyTjU>ua&g{B8m)NEwl17(7FVfItN;vS?VOGom8Z# zQ|=IkQk6bk?xn8vt_%PCQhC{IEpyA5(K@}VeI}jT0J)X(4Y3E{ci`w*_9+sh2Rr#1 z%vrA%5=&1riRVcOZdCp3T+ThIw>hGz(j&!6b@Wf}=c#JGl#SKe-!+)1NrVDYK)tU= zi|`!8Vk1AMOWzK1H8C5bIm%)4YiO(GaJfc>Ya6(RyK!rh({N43B_+;_8?uFT*O0%V z2VywXqjhu^u+>gn;nUI*L*yw4Lj#( z`Mfc+hV1>v^e#C3wMjV#hC3+lV$d>KojN-SD&5OR2^2*6iZ=M&$^` zHv8*Kh^5P?t_e&vV=4$A&UcES_h`oY|iMMo{gi-oVXg-29Whlk%$<^@A2#zf%) z>Zlw>9jfbd{EDg-20WdAg`BQ}qj=K#TFFGUzD3q8Yur4FSS|wC*8Fm!^zf&7iW~)P z4-XC+ea&{(3%G|VVNlT$0jrH5m#epU!^vGr54MW=P`$YYNpVs|$gjd3p zEm|E)aW%L9!pSj#)Z-G-cO=|SAh>S}I_}1Wpr(&h(`lc(qvQ9uLpw_;-P()ZUN)Ef z)>hRM9nuvZ!ZbMGW)RUysVb-6POkl~O}+TIgkc$V%%~mrJ-nPeZSObkd;ZP) zNVQwYI?r)ae*D)hh}$T%a(L`o-JK`=)ZU)$P>%2kS`b(ZHX}?-N)Ps2)OouP%6e2o zFc{TU8+b#3;@0RGJZpmc z!ZrRm)oMcd?gQTT4Kd#;4bG_wzL=n${~~hdP_5=kLsb<}jAQY8|M)kh-|&FXvvA{@ z)<)C9H}DXOAjL-W;EiZ=7z;*%G5T*#HLXx<5AWVuTDsNxJxjwtcV%n3l#TlxCC?Qh zk+NyfaG6|Lk&I!No^Xq?pfVoIsvx*Y=k;;9p(12@^+Mae=RSheocjCg~96mNU*uS-2)G(0IM#k$VUIA=dOr;KxCumulho_s7BZ%E~L>^`Y`_wd4AR$q$CC8aDT4E zC6G}wLMVS6*ow|U_d|MrcSzdN8;(8hqBb{?u~#o2Ii*%|y%ESm-D1EOH8 z#Wf3%{I8CCx@pTJMYN=+yWs_V7`FW}HnJuC==`J~mB#Uf4$fMqb#>gZeQ82p@phg{ zz}w;Oti}IRZ&&`B5oS&g?d;&4gVt9>F6boq%`yBo;4eFZ>Z6fLP1c!H7slzgm(qoT zOF!3t5{aI@O@S0yEFuCB4$p@gSh$jc2l@oEN;jPcT-Elq`1c#}@3-RL>GAJ(wt{j`=}Ngnh+$-}mz50xjp3|Ne*NfqJsdM^^QYzCXA z`PzN?0-@`t4E$d7f}M3t9j_W2@GedV-ihI4lBZKeEhg7!n)XQ`HqLS&Y5<{9wbMs! zxN;y@U$;$14c8hV3r^bt>>#h3(1KO(^S7|1_H4a04i*MKzsE>ZU5@?)3a2xble()= z85YzxXtW+$`CH3aFAZsgVBFxLSEe^w^cep&aFl*4i{|ks7W@%Up>1B7E*nD4Ig$Co zMDk6{l0}3|Xl?b%GXX{&gBox}dE5HKkbDOMnSRW1B*vTa{SDbnZ6$$zKgAS}Y75ub zJ}aQ+H%^83?J)&9luru7JxPW13}xH|UeDq%9qf3**EX=X-2ut@rm4S_LTaB>3S{}Z4zqNvJ1<_6!!f!CPj90l(u_Ch$EMbv7#%M~ zgc@t5ez^*03!_K52hPPlVrZv&t5OR*L4xW5vee*J2m7wqP6d$3Do)r)q41>>h%2#s%gK; zwS%UYn(wkqFvj#C#f1_FFcUcFT?UwrL6eMOl`dNR1un-(g!nviRi2<+VlfroQ%Ae< z?~?0K*S$cyK{AZH>6nuUPHMNgte4`(cb37=hM>P*u z773K_dC}X(jfU=KKkow+;CuikFk+z4pIfM#b$R6b32Wty zGj1336bPg}Q2pxL5suI`_k=;|r6Gh&(Wj98-Xg4lL=vf76{>U4KF(M+RRU z!e}s3-iu9we|`5Z7F&b!{^3|KzDJwhBHkHzSXI$j;k@J+5ngfg7~}d&zJIOeY5Bqq zm`M3v%|WtaemjY3wP@m4HiS3d#ir`61r+~^kVtB=Vo`)oHrsH{cvOMYEn27zhh|qn z7YnI-JsC9aHr3X;pA)0m%H*(P{p*vPdH3Hxu2bvOWTbGy!@s1!h0$vVnzzQKieKy^ZzuO8TrV z;ct#GjwOTPV1|jo{_Fm0kSevN;|Qr_E%ux_1xgTWA~R^z7XP_J_FOft11ErWO+EA*#y)pg7fH-C#G zPA`(!;1&fITDAu24arI}G#pFQ>Ux%vNs<}K{ML9rbQ&l-f9+Oy7Ebu9mIUvoH7Y}VveD1z7R9Bqa$;-xSIMMvVigX;TvpSxl<8#-t|`sTO~@C zu^Pg*$-UT^bUi{}8O+%#r*&IqY$*e!pJ)S7S9zs%fH`l;nT=|{QQ4IhP-=@`41Yo9 zd`C0up=wFI)XsSU)v8W)uoy?T4~Kzh(LRg7Z1poBYz%cIIF{0m1md5YEo;fbE&`}( z{)V@50vI2e&Rs*sz?;NNuwXKOL8sf)NbDA?yS8XRt?J&2q^Ui)JW4Yn&!*_|Z>EOE z#B1AX66oj!;x$C4MN!0o`)qh%w6 ze<;=R=KRyy#1UG|4uxv^N7vF80^M0tPd>wv_mVBjnR;gc(Xyf8PguMqwMJIVU zTj```2gqMwhx^Q*HH&MbmiFO1Q|>*!`+G+`HNCar1VTYEI_Yk`HR+|j7?`)A(sR=t ziYdv0ucbqqj(PpTP)7B?P}2x{o&TZ#di!M>Z*~l8FPvV^rBXLlDbMESuWI&DW}`LO z$@&B&Mymh`#P0<{cV4N#(DR#WYr}v35PP{r0D_xT72`}T7Zmun>67388m>5n9w{@O z7!G!kT_b+;Y!t=20r=3q=(nO^qOY+*L_$}DJ~k^Z!ucG~8MgZgA*BpzXpCce-0q(3 zWq+DzT&t6_JsX4Ss%7EUXyZ2jv4ACGyq>;VS-4u$Ss0ZHtEju^sVS;7y>z^`$bOfe zpX@!9OCk=qn7M>zeG`8l<~5N?c$q9Lr>HB?^1EL%lRz6xO2c-ItvDbEUY84{DbGqu zI-gVXJXN57NmR!>ie63p$y+6Kf2i8T1qHOL zHk|MSxWj-xwi3U&##8~ty#GqE!7|}NRnPA#@vtT$4L>!w{v;R>Et4P&CU&>`q zIl&wh2S$Y*3a z5KPv!?kxt`eh+Cej@g?Q}7!ieijTv#a7f zR|R9QtAhO>>j$%x866E!tB*6G(6J)(<6v&RpgyJh=(Qx+n5l(&2!@&C?*TX5^fLaw z(-T-up5p3RDev0Co%dP`Ut)dnS_9_rkODBW#KcA~yFiB4c?IAJ>Omd!T)Di*!E13~ z-~xvS)B^N+%6ws)gx~N4zvu!bn%!*UVX~TYT8cPu-tKgIP96}!HEKGHI5W7$JVNC` zubo+m2lwd)*HlrB?2xPcu_?3qmz9^Xj`fRbec#)W8SPj9vT$aV+=gn@^KV|zchkDo z*=ysqofk8 z0>pkxj+_jw_;GKyhcal=eA@(lHrpLh3jc7Q8l~35@4Ii$$KQJC+kd)mPv~2AW<^c& zqe6mao6=IH%Jm8-yYzc=8;xeikl)K;ac#mohhHqOxRBh%SVw2)PG`j=b0IKOwfE8W zY1D6DLIGA}@03^IgY4~+h+R%_RPz_frQAvPSpHiRo&!A3rjL&2<_ok%YFDoc^p3&T zZ_)mR!OPSsS;cl4;-AF+R@dRe((I+MT-q*JU51+uC?n$r$76xjS@>b9=qacEAt1-j z#?ARRo-@-p<=)Qwj!8@Qzmgq0MAbGi{*J6&#agye_+LVs zc86KW(jAOD7d4cO8bp_U+l9=WLchVTKb^bn8=Z?bb(Uc05Oy6Y#a5l>(Aa_p)<2<1eL^Ih={{&+WKWmU)RKtQP1u^U9p3mpt zzKLoMZcc~~3uvl56yC*rHB$@9$Yi~qRKYZ1{>Ap8ZRa%oQn>&7? zbRp26-)x|X5`i>IUYUQC_Qzy@ruZc1!G4V7M(oHsn%o2iL;ZE_pXwqnPnNr@sgHA6 zlT9u|WLuM6)7XjpDyWGBpeJnyr=7*(u)uJH(&px3SyH z=3SZlInaPE(S$NQ=>=Q`-^NknMEA;LVtWTHrhpeI#|IP)1ntUjb`KLI4eQkX31NsNRv_FF-;o zqiPr7Cy7Oa?`Bi(J5;hNMJPZmWJq*TI{y>iqVK5; zwUOlbmU8UCkW4?e8JVbDwVyZ@&#$Xh70teCGfj9{rP!e_5|P1{Ca^Ih*b;8Tx9apt zq!=HqiG37gWpZ*2Gy9Q7m(2bVoudks8Ic#NCK`Ov9bFAHf^xV)m~q4GAv&_rtvh^g zW@UMELkawe;#R4Ol&#DHy0RN+t>JZ!?=k#wiMIvsxs#Wb2Z(;;yUxI>801o z?ntj*IFe6oQHA$Ld3I0vAy0X4RC;U1j(R3@LU64bTpVlG!g8GkPcQw23SH3lb_G!& zqBk)*r?*CF}^?mi3#s zUACSQ54iUI-1GfEx;+$i+OZ&;B2O%r$?K+8g;hb{+ zMkni;{~+nQ3Nnt^+&-{VUt-`4@tw=tea|)gbCO;;M8^v3-H)_Yzf+FWyc1v{Y=&Qtf|CPC_|-i8dHw^Zm@xo!fsnqjghU#NdqB zEQegw_!m&Od0KFz=54+USk&AY+$hQ4nYXZPrQJ}Knak7;+c!k7`u(p(u5|un2++A( zC=xxOZ%QQ4_}6b3l^FQox{Kom``cCh9e-B!Vv^$wEYg+(%0(B7%+!_i;PG)?e=zck zZkpgmPw<+MBBo!w9jA)esja4gF0Ukl>QB;3Th$kLu5E1o`p&GzN95)qRT!wLZU>(H zR`PZ3%xZV4Z7**Aw$S*4Bn0Qr6I{E4x%q8pQ5;TIrlaxcpSXT~*kbgJ9)1md=xdi-c9~_h!<$X|{xybuqQ05f6=LZL;bN$HSt#1T1o0qGL37g%*rhjeN^bEuA z2M0A5*%_+%GjY~de|sl=!V4KI6BtQ<__Vh&b}X%-_T}13JEDVKN}g*sci~cTGAcp? zISP+|fRX7dv=^rN>zoBC@5d2jjm0^((vnX3w_maLXO==;Jke<4%pKB=^v2SC1l31} z@@nId+V+>YZBx{?^^MC8AV<>v<+6L6F4!Kn(BSBGgcbjf+dx?_$JOm*VM5RVEa%FN zUA4TMQw17!x@=?gQsL7#o8rmO2cCRG!o(b!2$Oo{H5WgiZDcn zyyE?}(pxvS-iFQ0==AD!!geI=k+jCFK$lGarFE?SPAAU2b)+c@^^s({=wIiwWz`A^ zp*nz!HrtiBs6@lgf4AW{P=j^Rrz!>NUX&-&lZ5>0JGF zKUAG|o!*!bE70yRPJcPXwCIDgpt}5O+a77t1T+*&2jQj=qD zQ-fCM!eckO0$R&(1v1XhlA5EO7@dQQ__2nN+_|3a)_C}Yj_H2LQ!_SL5S=JVyb@!f z{dsjz)u!4T1e02q08?lRtgF?v+pSmn&r61e2g@WiLDm-5m^{HpA-z33h7~LUgaZXupkTQ^@Ezpagku(M9Bq@nRK zE3IEY!C+<?XG3UR?^hu~(0V)ye zm0jj-kE@)Tta2w&Z*)uscGRfup`qlRbdd2J9oZD`e+iBJ6**5M{Aq( zF-o+giw9z00MW?Kbz9zg$y+HO6(-Noq;E~ zNRpmzyXIxaZP^dTKcaSF8rDMM$u4+jbbse)7CaKdG7Sg+%|}jkcaqx1#s9r&zq*d5 z*?Evs(>7bv1nf%y3p}g3EpxB-g=17)*I8|=SikWh6OG)ZRxTYcQ@-QY!7bTqB}c7SVycIz5m5Qb);0v2YyUFQqUeah=B6sXUXo@HO~g zjRt@;WFIPNbZOgOcf-h)K;-MOomI#7gwqbgvFDdr_u1S$H8@{hg>nDSEJxn{hFWun ze?%IwbGWnHV;^Drn{fv>)fT&qhNIG~{~Q>cT?z442Nms!d;JskK^)0!_LKHOlem~arMAprQ8*ooO}u|s4^3TWJ|c=9bwmgJ>~l$P+HrQ zW}T&9(Pqq_K!h@s6$jag{R^hK%apuAfAn#1I=4ea5-Dff8JR8q&r^ttsg&@MXH`Y? zIdYr*)-U+E`umt04f3DS3*PRl(z#7~kdUPFXHvzOwd6_X8+k0A<^mUWdi%8l*xGuA zZd=fGI%46FM@NCjY*d~3GV+jcEweikZXn@!C0H1? zZ{aKrDuRdPm(tk$OQe!1sc6^6Ww&|@jFR-Ot9awOV$!IO@A7yAO|$nwz|?^c!CX6B zR2hDgKJRqsFzRxw)|4_jG^si`$SS`G*>^tY!i;j&_dGS|d{1ts+VUPqfBp}K^j4|U zxi*Vuh5A$62unu`p{S{(?*$Or70 za--h+lownZQKPLcA z0cnls2wsJNbpnR&4-uzM(T~bVcH_jDx2YnXI|_b`Im>+rTXKFGZ0Qpn%)L5*BKsYIh*wx0kA<(_fz;c_7a%y0=n9LHvrz)(LC zEd+#xQ0ps!FG5RbTMn1K$}aS!cE1WCN!PrQQegazbM1rb4Hkvu&c9=E2P%I`RjSfl zG@yN2smA?FHIkjH*04Rs?11M#J4*TaWWEwe6`pczDyA9x%ky{A&QBspI5%m_LxAce zNpy!G{~dVgTnkl=c~y`3+3GApH><{}|1C!hlieqW5&H}r^jX2XcX}D#h{1S+B-zAK z2)gobQC{awTIx|XohqVe>q(Z)p46&|B7AjiA(fvQpI;0|l+fOT)$P#Mq&0e*SBCrf z(w~ygSUW#Q;+SLjLm+Yf3hE0|KXj{m9-~By`tpc(Bws55L%(?U@751~1-zIHuPFik zuSNh3!esDrYf)c8-A6wptJJs68#SL_tqHf!@O3&>6kFuP(Z6Us8L8o&KUod+yt$r< zgv{9vsiLF#Fesz3Nln>Tl@cv9^P$8EqcX}x>IkoRlHg~86-rp`yYm4 z7-$#M9NXC)k0RFrTImZ(4=Ps6S*UDdldhiJsC<@|sBBhnaOA&tt+f!% z#yi%a(y-yw*TVl;DT+yQez1$S0#cx3;%WDq6eRJxjl6$$+O>(C44wXIgnIy111#uR ztv^xxCAA}#&fSkD(y1CW8m<3XV&-eCMgNl(EcMoSmCET5zR z;>M1>=%P#F#?Htbe>snfsO_6S__n^@&*K#**L;h|hj{!+=8`LTT*Bi3v~aj%sVbp^ z=Y9x9qhn%xo)tcf{weTr%-IzkDr-@F82+x7o)hA4$z@2po)O~Vdl1O>8Y{_GEMp^k zUp5)**OSMwvjz~+qr6ILZHNwxlodAmay>Xi>N4;OWIP;)4;Z*;reN{;O|V_9t#Cql ztT6coDd3s{SLs2`p~Kz%z)m`HB06$Q_{+zQyd#zKe-V$~55)}EMQ1Bykx=eOZtUF0 zCsAXy4w?&zg2V+DuAxw;yhbmmsHB5&8pJMyw;-k9$g`rU4olPNKXLO-KsJO|146=G zlg|4tf5Ymts3tZeW&<*sK?0q!oQ_*x_%d74n~#jPAdb%7o!iIX$h~3&d3GV4)49h< zYdEU;?_5xH6dnTJHuO1)k-dG3|B>C)tTtpzX2l1KsY4MVN*o^^w{u9`t^ceXJGUDL zD*whu0Z6RI{y$6jZC7Sm02+AUR}Vu|CVB`crFzs;1nO)lR@n9<)pK2(W3`-U zN`1eC={ZXO0qL?V?upe*L)@R59yD^-$7lDm^W8NGYaqN4Pnz$t+J@*PH0^5J6J86X z;Niq-I8a=n@5>7wp3>W<(bxt*E==n1UmCW{|H$ho&zu&U4`Riz4gT}(7B{T#ugx;Z zy-7#++PUJbe0!!(dTB+&(B`dpR_gtiP4hU*gKZlO?xkf%wUCBNo>hA|fSq5KyNh#W zw9{GsONTvfu90+M7b0OUmr+kThs)pfp{t7}F5;Xek&=-`4dj(ssI6MHzz-{G_&!5Z zAjWncHKq^dS7UlLzZ%o4_?^aY|M2W58uYs!68Y0vP9mGX^ZyH%iXON)sYP>^MSJniFt1ESJ7s9Xro=3SxzSg^ zPWY6X96dw!()jAuE9!!&jL8{{1=$$*3On7k*(bCexGX*Q+lUWZ=X-L85W6>>|AWif zB8x>TrEs5?y^1ZeX!I@u@u*%RC9TEy@GreT`TKtRx$ki~bKNWd;e9^&`=h`KDmp#7 zZp%hhW`NswMlaK-=2~O^rR5s7K!4xMG5WdR>qCf69vl`Q0U8h59J8tG>LC8E#glA~ zkU2@s5m2FZrB64g2wLYs8oEYDt1>0+b+73xBQ$MJ8JVw9^pt>Jg+ihCY4FthhCXDi>YET;B392cd$~c zPRcls()9E=5Z3a0U2Dmf*BO(bH?GE06+*bwdiIsvG#6B`HV+C~azdUSv$=%X)(>sN zvMzdJ`_~56YgCxkI1w+J5S9e2HE7W+)DEw?T?$&e2Ir9{GwqQW;YM7H-=DW`Hc>`}tTZ@CN*s^%@mEhHe@ zhC%C(OCgK)_|R$NfgWTnOyW2Ze4!r2ggThuM5BHYqv{i4b&5rLaezkJX}9POFfl$m zst1!&{!OYPI4GD<>0fKG8eOkybaG�=C*Zg;>?ma&^v}sBYpV2miftK3Gnb4JuZg zrJt-&@Dg5X(!Gm6ZrpYv8{Fi$Czb@`L=h%VA_0V-AfcjNVe|`;gvY_$Hz=|w^OsWC zGVxX#y^HO={L@lE5i!2(3da)p+229~y3<5;HR9LN5M>fq!hUn;CpW2|Ks*en;3n&I z<`heHSP|tVD?}0!^vCeUJcW3+U><8ovRVIJi|2)i-7i48{!(&K=@=(;$Vv;9~vd{!=-BZ9r>wgO>HHPg2>NPR4ukghUa% zw}WFRGB#BbUEO$U_xB=}jcOymP5KQ+$Lo8n=?9OgV`=5!OjwPHojEJr@}x3h2BGh3 zA4OSQRy$KZa51Sg3g1t$0ssM;9P5>DqY=q38EqW+W8a&)=B>@9t@20HRPPB>d3T6+ z#PFcU5baka&@GC=eVPfXwg6ocXT?vbTsDsanI=SIfA}0O9ND{U*prxvO<>!&AvIK3 zV?qg9r29a-gjp=jFx5%H-R9h9e4#vP5pXlXF=kynWy}<;>>s}hLGZ!Bmr zr3SQ{x>^!4HTscUG>L^a5Jyt0r|4Ov8~fx3mHBtT%!nq%&pF4kL~=fhY?bzGU>j49 zj4^`I{2lS!h$o5QnA5(coh`SZlN-}{(Iq&GRdDVB=8-O6 zwMtvgp=ou{vQViv-NIkBtaXWv49;Ic6Y75hy7E42zp9ywC~=j2j@1@Up!*v?LziHF zQKwyI0unKvz;B|AG$bPR2xmR-~H|EVTi zK+v4#7Oe20T=#ODMq-Ue>RBZzE6KW({cs?rTrBa#Ii^T(-(c5|bNf)=BicqAy$)*; zy>#}sEM54k2BFf%oc9@kN=*XCSAWRtM21xUTVPh|M2}L`bs|`l&TRxn^Z^ir#h-5WNO}!TUUk5xmtSqMlW6Chf!{}Gm(dtI6 zYrcE-dNnQCL`Z$^gEc4KUDy4Bp!ob%cBp93mKi~t;}Ou)C;h{BP+;)S@Z2G(OA~JP`~A6nf#R(^3ba+ zfmp?N*pbeChkDX2Kj5#|mc939dW$RVW7`^A@$Y$ik4U$isVuu|(k-X)fPpyeG2)qu zF6->r_qyY$uI%29CprNPGyvdPS(a=MrTxp){x0tcKkH=?B)r7^%`2-2{MElM35}MZ z+@r*L8}*djBVix?s^}@XM}mAuT=7S8kA(h~pyVD2k`p#` zemtSc5|rGdM6D$#xktiWtg@n~n>-CLrc^=jFZP<>tXX`&M1ez9OO1Prp1WH>zMLYDVk z z@xfX)L<}Zlf2&-40ds5;6VF`_YDKE~{Gf^BGi{l($!qPm^mwz#j?b{j#rN#VWzG9B z>1aMQ81(qVXMNAgY|b1gN!zSonr)fG{0e>57dxEhx#;Z1&yGQlr?|h)sto}tG}@65 z(ZdQscKjvK@Hf9OvuJSpV~O>r@j~Z=bYl8N1Y;tF%{1e8xZ1_9&q^{;sZExNl9(RGDA5PZhO)7 zx_zYGaSN`TosE_3Rbj`85SS(b;CG0?7FIXGLjX+k&(J=Y;i|8Zgrq)YR(+RJAL%1| zrb}BXeFEu#8LmG2mT)?wlh*p~pD{Q%!@2CuF(^=H7z!%!w4j_ZBzv8LarB~|I@QC+ z5+2gks%I-y1fI@a<|exaeJt$FTSsDFD};z5k2-ee-7HI z8Z8Cc{NbGn#^Nl1UPB3#TDJ6e71$sX{Wuy8g2g1j1zd<-3R3_Yrhm@daIMv&bP158 z3CvTol>Tlj>A!LFJ|`D_6S;Wd1t%BxQ-=KVaGD^1Jc3XQ4Wjp$#kK|yWN5hNcDl_7 z$K$kKt7LsIQ=q+cQ|PW_V15=it%_XH8T^SmMKDLO)MzbSM88&w`iaj1F}*dMRpYvC z;W`Bv-_X>n!!{&yV6whEd|lH?DC-W|SpO8Mpy>fgJNjEYGF57WI23(Bv^9K`Z*Y?D zt>=YBW`OcZexfw_Vqi5%rYE{&5;5(bia66}lT5#|OwF&v-;YneU*igYuGGt#`x&{? zxoI?-+|#sl5~j4qFz4qYhKDmjd=u>xzKsN2_=WkTD)tQLRY$a0$hIMYuB2SKP|WCP z?j4+z-tsml6A07RJ;oALco;3NE~&!+SG4eVeqEeTKitya343nSX1^^(FR$3T?iejNtY!fpLE zygoLPQ6dy;AFO0<@quIEWv z6SvkZrcc>Gw>R{&lZjkOg%k+09e2|jp6>z#t>Nh%p0an|0Y)D(*X74Gpn)fa019X9 zkQJ>U7}-G7A(E6@d0jmUe$&Hm2Dtng^J#^_+!z#j3?`6UK^;D zH44IdJ<%XMQ;=~Z&qZ%?47KRxc#=bpKxaIuiOe6!ccncssaL<&FcOaH&~eBi$z`~% zAH)(yyj^ZXqZ6g3(WFGSN-D9Lec}Tyw--_D!ULZfPG;0;k43$6gfUrxDgr+ zYSn22PaBgoP~Yd0#F!*$H+!j+)G7oS{RX4?kSV?@7hkwKH+35Zbnre+gR1WWz*=_NTBN?D$N?iAu;u+s&UzX4@8J!L` zN}?KH+=Qm}^i}_)EHfM{6{kRPffux4L*g1>-ifz zbTOk^?x~sFzuVY6mN-qBrXX9xxAy&IumsuRJVVoJW;pJG6PhWq)r+L;A^61{way(4 zXWYn6QrSNQ*-HIxNctC;Fa>E&w^zIQBU?ywn`a)7URssg-29&N&8a>)9QomX*CALy zwGz9aGB~Z$KdnE-tJ(*}0K1oWF(g$`m`xLAIB<98wX{Q*XTFxuJ;CSv^HxuL;>_=T zb!FK-x^%D#iLC**CC5k_WJi4?8ZzhydW1Z z31EV)RlMVcLe=W?7%#MHxGDL*KWjfT8PNW|-@ksmUS#&OFKe&8*4k^Yz4nd$#S$F; zl@bab9(NLytbZLFTEz#twyh=tNi*!eieh^VKE#qf(UJFHwEABYpcJVc>5w6}6D4v|#_H1vPu_LX{|ZU1*k4{~&Svhoyt11oba=C}h$Hcp_AhY)t)* zct<^`3OJ>91zwxLTBN{~5^!sgU{RFZ+OLvod|8GfE&4s#4nwdhy!{N2>`jVij8d#Y z+7h4LI$~FeS^$M8E(>(R=k+NFc{|*J;V{U?sBFx`uzqM4g}skxJ$~a z=;E{2BKL#jG&Cj%$#FvX#l=pwT|QiG&JqnfK1EILoMu^#cV9+L%atRrD`43tRE|(Z zMu;%5mm?zAUm0F_$@Cds$Bmv?F%IeQ6K@hrQ(o4N@xcMXd_S(g7WY4Lzam|wRa*dT zqcF>^LcyMi0_>lUU|KV`w#DyBzQ<{W#dlF1t7lWS45XFqAkKd}Z4esQli%M!&Lz7^ z)jp5E@azBMglV-cWypN%jo7Ag9O6jPG zHs0a-N$0Ln{sC^Jqm^W=@ZGXgv0S+AaGUgvCcUjEb)?%i<&0-V36$T+B&Hb>SMi(% zN(`-=LIdeW%Bt{}Do=pSQ+iTO2b7cvioseYrhbj{?p#; zy7{M#Z5$*0J@6#|IX4}gf*wCtm>}g#IK=4bg8YfBVh&DDq<;}2EQzVf_YjKw4^Su* z`-`8Oj}axozm1&0xq5w5Px@zFAKl#amw9L9xD?WVRyA;geXYM`{0=UDR(ELn zOM0;`wD5O~-%^+}5b4yFjWd2&%G39(>qne4c%Uqr%@f@q_Is7M(+zjvSDasIw#}Wk zflZk;p2-M1>nUc}ZeHEe>TT!!Z1t~R&EIheWp9!twl7$GGjj!)%7u&#? zb6GHuf36W4wGlq~Eg)FB`Wsq^JMLCmUWIOnL*cTu``EuBD@p71QgF-P=5IQ8g-QU3 z6vOoDC-)Ukn6`6Gsayg5EiLutCrJqlHRn)%#mx<_GJ$#mztH zbTtSf?-efvAf&9$-e_-_WY^8ofRSGMH~u=;{2TqA)~@ks{x5M%8{h`ho<#V~HK04W z-oKzB@L1I(N!x_^tmtQ7YRDR4BGuPi2|2ZUx*@8^q*uGn52&eN?)ME^Lu^~ExPwUt5 zTj7%sHjV*trb!gIG8%~cliP1lDZH<Ia|gxQPsYUTaZ)UK=TrJN*32 z`1jKIH_LBtUP7M0mhH7S@?-c7Y!>cc1gf?A@;|I`G)_>WJyk`w`%W$j z>V9G9V0`{gmn;_x1-uthO4~1TA7$Pb`JVT}kip2j1d~c~`$5?+vIu3r$RZR)2Hs{$ z`}xtZ22k?T&>ZRP-ux~k0p8HCVNe$Os%j+_^70}f?$Z$cVs{QFBM@B6lH=U8jNtrobFk$fBaCV=XjN;$ z3H5s@^L$;u$Rq5eN?)>qo6h|Z>c?Zcn}(*Bye$n`X;V?OQht@8UCUppE@$9N6cwQ} zi~;ThLrcGP01nO8i^$l3K~_B&n$%PHC}QeiNoj?L$aL;KW~VNz`X*VM$vUb$8xh=l z6la54Q$?*{^=7dUhCTrSf_w^WC_FuTRwA9B$U?U&I~Zt8ZXGgH4Q6b?TGihHrZh|b zcP@uR&T??nODcIKPyy7eT)7S8qWXHP-gGF>!!w#b=V}{Vm%<#X7iRyi(s|Ql#Bd!; zO5TYUe|MPBL47mz>tbnQzaX&X=PcDB>Q{=mceNUzTKzpl=*jrIp3x$>+RT-6T86fN zwEvt*73thgVzc*34Keh;%15m1aam*wZxh`jVsDRQz=FLWT*K>fo)>nTiYj zO~q}co#BRMgFQAnPe!NNJ$}C0y1$xS@LJ(IwO6*Nc90e(w8YMk{o^NYmG@&}jO8?p zZ1Y5Z3(XV_?nT383!+rA;Mq>91E7f<@F=|5LCZA*&D&L(&Oa`cY!lUi2#6Lc^1T>= zUyw7foMi}Xj}d4Ffr8r(c7O(;fd=My1Ctrc#y+(+ReP>FvlL7VW||tY3l&-LY**I- zFi0>MFGEan=EoY+w#ST|HeAP4lL;uK?{PVETP0ZUtMTExU5$&aM(qbv<0)FAKFu*W z8Q=uVwU13R@Mk4zTI~G?lcZl63v?>N&uhDons5pLee1qV0!+)Kb63gBoPV1fQHOS= zrfP@M@^J0hR-5{6{=3J4CAy#_Yn|C&OTN!?J%q7bU;XPfn0Id<=J&`p*mlqkH{h?V zX+LOohZPui1>n_=fk%r0+v*UQxY5ItnW}!X9$GRZoWNw+00fWFQ-?VHi^X{;WaM4w zesA3_2!TTWwseYT20~Y+F)+C+A?p&#grX>w#{njmO4}D7cwKI!0>bkMP8kFxltECc z==nD=iy_zxYJs=WLEve6iC!55C6qxZ3g%6M@N3yB+pzFyjlgc_(=~!-k3k=1j&vA_ z3Z_KwBZt}H-VER(9p)5dUrtuk0CZpjY&the zh_k=l+^~#)Y!)jU|6bMJF@{U-k2hu8(ptmWdRlm&b}RThT#XX-$EB+a#)k*nk7U|d zG%As z4$QcoZhbU8X%p@u-9(ZKH+&61cE7_6R1^#izsS5!a|6h$8&CPE58u0!v<8KXLk=OB zSB1-^KKXd|^wTouT031o+PPn6aO)@5U)~I^ObPvu)!NH8xuf|F#!=keSC0=#;oNK| z1J2Ed{qgbutKJ})xv?k$b`5C-Jzj#5DwC7&Yveus&e|+V>HN2uY@D9gTqB+T2I0cj zlWsh)ch|o_3@wZ)ZAADA%+5Ams7Cu6=nvF@FqROx2;dUv6E$@m6wd5|q;{T>vre;{ zbbgw+DHtV#WrK?S66nTSjl)7A-%1aOX$B?}pL2tojgNJ9q$BllAUQ_6LAT1!&!s(J zXv5aG);SaHhcvZtm-He-q}UgZe}&d{Q*B3o)7u54NQATD;r z%hkTJiV@>p5v=K6RnuM-7vxT`So@969K5njo0%1Xe;DqJ(&)U|)v2k!voM1EECA)Z zEF4;x zMX%hu^nY*nSJU|uxGiGMS7}Lf1|ic=HrPlcI-PASK7{)&F`rugsknC@%6Lg0=qqg0 zgH?hbW1CYjs&aB{9sGrHDq6#zlK)RHP@CKWzp~&Y4Hy+ysn%XWV`wzA|IuR5tuA?P zm8Ma7u$Jxd*sS});tD#=aEd-FSB2R}u{k9ai{_NYGkg6n=v(2-!j z`B1c*TRP$f1u~6a4Tn7y%Y1H~yQpSAo0v?^GD`iTO-aOlW_z8uo*OC8DCYBttWFf^ z6DOIkEnaUME~CZ!#G)rtcd;m&@y&)q-mh46gHg?q6^@ zY#e^^rQiSVZ2w28p2>L6brgz;Te8WhYLkxja5m6%AfBL2mpJH!q)8?V^EkIan+EL~ zO=xlfH{ZY>BSmQ(!s_@mBtm~TYxwz@!d&rWGoWwkL$oT=DY}dpH;(NW-BXeuty-e= z7s}Gw{y#RP<~K_m;BR6N+M8Aaayq&-of|QXR`3D*@WtJh*vfa^#D4X@EL^2$&WMLM ze=kRSG|pN5>HAVAQ_qjoZX_;zh~g@gfuO8lG(ofZ&!zJP_!J1n-e@A=*gu;(c_{a2 z!2OExZxB-5Hpp=gUpVeoLpaw75aue4O;c)HGb6J<8u&yyzff@oso4H9i76)r4H%?* zbck#{JK3M*O8<`<}oqfXwPI1Dyfl(`mec_U&lLxGa zw7(L)syRmq9_zR$WpW>A7U^Q1*kX-t29LN%?2cQrhDNV8TkB!GUuz%aGd*bs&u>I> z(?X@&M5P-}aT0qT(@FS<=Ry7fQBAV-CM^@e=tEQ0yy#DZO@+U8P5(&eej>uBPEN0? zSc(9p7k@}Y?tMDf_fB7x?866=O>=AOGM`3(PmtusLL45vQv#gXmgQ2IPqYu!DqJ-F z?~=f@r=ypJb`$FKk|BIVbw&Onl68H5Dx*)%`iL_TD*WrK=>Y!qWAt~1WAo=^DA3`) z^sm6VJ|50GvACigP{KK1xaniD`hGmm&5Wji%_N)>?zMQ4zzu5lFoN>fT%njp!=3(R ze=TElU(^6Z^}m=l6Cbpu`tPLk{|<|3@EX}n(LwwfY)?GDy8lXObAE7jGcZw8`J7mh zpTSqJ{Z&1FUa3L8g}>l3@Mw^qsR(m+^jg?uQ-eaA$ zks8rM&Tt<-N=e)muNz^;@1UPdM_O8=t(sS$Zb;3qBsKc1dKBxHNULD9Ng)DZ)-1C; zOxL?Hegvbw*^~R_YbKvde+5j?k1bl~lg;a%uRbwmg<#(JCAF(Pt-Gl>YSE0bKGiJB zbCZY<%YDxVPWpt%?ii82Z-QjEMnJP8NHWtndYLFGUgW<+gT)=cb-K3)h{WwycTArI zLBK|&I0H>{pJJnw%$MoO!k{r!H2<-j!+&`z9%NiHVl#}1r5)y`VmjmExzrpZTI>^} z<34dF$PU`&HdFb4DNNJ|PicrvJJeQ*Rp7}jA~HSm4=~_&;%7KeSTf(t-i<~`Zk-kH zsQxH!v0urdu~y0{I)?P-W7_9rf0#Up2_lfQKOAyOI)4G7^r~}{h4YjC<52pDzUhvh ziZtS9CaKPEf6-VJ!6JRW!KNrU_N+K|F}Kt=|A^I$ibvZYEKGeoyC*rX+kdwCqV_|2 zcx3m&Wb|R07mq>Lc#;tPI;K!o zBCRLUrT6lqlS6EnUjBKE>@InjcEL>;d zr54Uwc%_9KEPRWFud?uM7H+igorHPsL4TKP2ej{XT6~ql+#;mZ(H4IlVL!OhVp=R_ zE@4(k5C%V(V)3ntU#{7sAKYv)Q!S?2Vy0TmG{wlw3Rc>8o4$i-7M^0^Ta+6?E_f-0 zkeu8DPKP}Y%?o)ux5kcomuE(tXJ(vdHF?5+VIKv_b@4ok6Z*l_+MuOI4h9Ct38Ym7 zq-zqU3f?0Bnpfa?Q#aAgM{T}hE{2}urErh^Fs;tN z)diE>1zK(rXU+=zn{2d1`ZqX~G(uz4P4K7I_-(xTYyqx z+%Xv;zh*y)jzNBmBpy<%{x4H~Ze22+>p;`7u;*sk(PezOK`B-Eev*1$VuwltyG&(26szn;5RaB& zPqByes@}L@?%Wb(Z0dy1NZf!f>a_z?E9Kme9tL7;@<|xN>$g#Z67C|Q;8e0ni6lW1 z;TRTo`(NhSJoT~&--;bIRP=HLkW-&h5t^~-wV*5}_r zF@ta;(^0Y&?llKXUOVXVZmMAZM*bG%Q(|D00o2jl-=fI`rR!tD!@`D@cz4eiNpofU z2mD>4ZFnR^kG;hrUON9FGG_0UFPa6Jzr%5timzreADSrmFH5%Koz6!CR69H4XLH(Q zA~*}WGp;Vs19$mPk`QMtYTmietHsf~l8m?;vmIEz)w>hw9ACs(^eMXYLax;-^ zO5nLag!p8p6Yz}P#@;&Zejl2?zRT}T>Lf~izqdfNPUrqrB@8SSlI@^fGJyXaL1Jc! zX7Tc0<|F;yQsYo{86sDAapE6J&MK3o_sh{&yrKu^yFl<7hphc8hQsQ|`2%y)lh#9J zyYwTcaUU--M95M#(PC3YcJR$lxd(=s&md(p=73n5E@ponXo6@fUZ@6wyIzJ7QqC*)v z83EP6?dnVQ8O`DtTeXBh$W2jx^S)d!?4QdJ%aSV|)+K+}aOIZuTHTNjXgh82g4-_2 z*?_hV^eQ&-@i@!?=5&AtdC;7@8PvXvJ+F@|mq-D-Cp=0SV{qpx<0LYYkQ$>kZwlNu zT{~{nOZRbnnjig(zDTZEoe8_$8)EEsT6`;<2>(`$dx|*pc>X2|`ukQ^WQG^+lDQIG zEidNEs&wnsa%vizGouFFBQHpWIm_Mt}QV8AkggBf_Xpfg(nE-D(j`wtn!Rj{YY5$HRj)=(0rgIWmYlQ)2Gad_~-m zwuC#r zr75u8D*0oGL3@5ISZ+I7aKs%!jLBGaZTQN|HXY1s6I$#`Kc|(%IV-d6ZGl~ffpE@M z_R>3qUQHwqbQds{v6N_MY+V5_a`dK63tiRTkU~)YkaFH=Hem&iTyW=RA$h( z(`&f}W6+V)Yn~p1yU5J3_fuEVE`v>3%SpcmJ|%h=DPI2yYnAt{Ul-OY#*G=4<*i=_ zZk=tB29Ut5#Vpbo5-ieaaR40@rGG)*5-m%wg%#`5nUCMZZy27x3P}g!?2n~rhLrkk zQU0C>&>(*YYq90`L3WoVhXkgl^lJmr2*yq3p-<;8wwMNs(O)*_ zTa23M^q7T!p1;}+3nwbw7!(BRU+A_K>#^ropnp%BRy&7H$mHN?#%L<7#`>_i^V8d% z7JL(zo$D{W9QJ{w|4fbpOaCG+t=Dvl6O#2O+jtj^HGGToo$`LNjQIj)+kDmQO{c~t z+nAQl|EtAJvl#tN=g+s8DTH0iEkw%$b~kH4JlWMQb$OC!x^rm_QQo}*%jUmdt9)!o zw_;Z5&8+wW9Su3L65e`;b5r~c7m~Mbj(QS1<|rCHt{6@X6&KB=`P)AyN%6Uko0J(` zKh|%Hn$-3X(2KP(?av>y0}x+fdL&doDOA3k2tKo*sck_ze>sq&4=avG0iwI07;<9f zPIN3us^VCx==;NLEvQWqGDDP`E!+~?Tbz3Us#F&3K}|U}U~NVBGRst+=VKSbt%_Vp zq`OkcC|O)DyWO?C52wAiuFbXlQla(?F#^&U1NoMK%)v+Ft-D!v9UcbZbUWGN!xfH2 zLnQVSekS&`A74Cn(Gs3^JNayJ|MYWO()k}!EVh<-XO@pxgtT$s=RjxUz~O;6*CauI z)A{qPqEtx|EJRh^+?_7+j<TEA-YY1T&%sNeR3$rTQ&I{b$GZ|Bm#y5q9%skxcnNi<>jx@e+d_6Ysx8@S>D7cLtTz z$5x3LHyXxCTqQ}jaIu_*iRg1yqr+hmNEu>ppy`S_D_#lSu09Lw%P1H<{QmSi9b|#U0J7NB+3cE807wj}v<(uOC@5Z+Nq2>9vhCB5w;Zw^yjp-tsOrbvyC#2B+GsbjatpfiGJ1 z9I;)Fsg`?fXqxub&U z@vj6kaO$i&f>TxtCO+6#W9v?PKE~})4`=tx$c(|CzqI;Oh0pUUi}HNDaL%^oi{=l@ z?m4b~OW}KR_xGgIGsr?Zxj4wYHMyI06KboonqMS{PZ#{tw4Bf)P0CE9}tr&xcA3nqJ&e6Frl1IpcaFkd-N&wW#dyP6!^Ddb-P?{dGezR!ljE zb_w18VeEI*TOjdhwj(vik|{hFiVVt`#4MfP9wW3L>5iX7i+e~h;fr2?WMwMzf{Blv z4+535y~ZOsaLel~PQQR(MS9hx2lqfSX9NBj8$hOLnZHJ6(J9K1QdSqMG$hN~qwjDmQ?TC?W z)g#+Rjcof+`_CpmmR)dU5`&quQM|%#_PO1K5M?CA_RYNxW!QFhf@`Wb`x+i7{p-!{ zh(pCY70S<}E`JSQZxDp~>op?n`NtNF4DM)FsK0%bb}6-=rBj*iZD)0xPQSN%U|du8 zQG2fVatX`QpEGUD-L#Ti%tZb zm9(bXyP#l_s_C|lU~{Z=E240)sxAGa+s(be@6jzbL5uUGn*U^SHMPgnyvdd1`b2QS z7&btSEK|wKKJC zI`kPQ@qGSap^LyF=W>gNW+6M;m+G_Fdox)cqe6b zhTd+58MT8vTr+1!U*aD9!FaXL6gvL`I)+H5c>BogP9}3aD3H#xZ9_8+P^erC5=EIQ zqU7)?)isYclFPl`ZE|GaegE5OZSVVCNx!)Bttje5{K_+?)6IMVc&^&Y@1@dr%!l0S z5)qaA=+J(3t^YV56)O-Jep9zdaM)EyI1`s#px^MB7RF(B?#)>Ze~Cpbyq^>mtsxtG zK%^$(r-NANleN%;4TAZMI2{pm%1S|^4$}4ZxtGWrkn8?Y1j5;F1ZvX8wl;>1XM+&!1MGREPa(6~$x&nbovvT<4UBqT{9}DG#Y;!R#9@`!Mec+f zDSwdV+eJPi)b#zveG-X!-_Q>$2f`N@QON|;&uVwLf$Qx$Hi{8N_2T+J<>4gv-NdJP z-f+ZAh2P9W4rtVms;2i(={?76jo4$w%RX-9d5ESTS<#N+!?3mEG&aJXi4sxrwK|!j zI~Go*OPO?A9<%8VBM*iWch3``H7VV%s4n2*@bC7J@AO1rfo9X?Ss9JT zkyY6af^f=qR-!PKY{U3Oj7mUdw7va^v;UO6GtPdRgL)O8wy$*r;7j_#&~+|co%C9p zg+9Ub*401eSCDIAzV~91oqw^qG!d9|CPceBg=EWw|2On+X(QY}s3!K#VH?EmSK7(N zu$Lm=4Z$g|!ESVd>4U?d2BOPfE`G~D2O}ENxw8Nv#*49T7rc54$PA2n+sUNc)v$p$ z-GiB8#4(9`svN9Z_7q}OZ;MgS!aH~s>UC|3wFNU+H)qG|$r8*NwNnZRG@#tHh1*wy zb1TVK6`WhmPi1hfY}kt6+zNhVEx0A8P%Y^ps?B?OFR!~hA$JQ4#XOso2dhXNSp}b| zq)yG{cS8JYJqDoKDY;N_`&IIgRiffEDNZxOSN=Rq#VaM){8rqq4!VI*N))44gza3b zPKo?TmC!Z&vHC5he>qNn70wBg;sxn$AeF;8u?)_{#|q~TLvE5Z;4pTm42;?WX3$Ls ziZqZG4L?Vljjr7dLfk{Lf(+8IkZmqA7;%yRkI@L&Jr(ps)OroQd^f`Cfl-@X;c3p; z+(<(Mz15QYABD&aj;)|fpBzfrmcAdv3b|7wvd}4S#2oaMh*6L26bzmCSdb_-fn5Kz z=_kdd_CT8=?rggBo=ybkr?_-cfkB|ZBCRd{n!RWEt%Q!UPQqIj?z0xWOmEgU5mTA)r=`)Ozrds$A8U6qe$Pbtz1EL=Y2kZt^WaiV{o4gQ! z)(81DeJqBG6clwk<3Q>f6d1PT$v|326Q}r}u3#*wRd@amy7$EAqK~k1fYb)RXjB#D z15j<8+)CW9KOdk38KT1JxYMF{@cC&UY7bzh8JKsG$r4P7J|7FI_b^~02Y^#$t6c(v zBg>t@u`$5%82Z%xsdv3;nz%OPYPU-de^{3Ge4Lh4<%}~Im!*A!E-SdhWA5W*nQJ`kCfB3XSI(H@Ei38Fc)UZFd5q{zv zx1|s!iK^jmP-AM$nUZNSU5|aAT}@Q(1fsMz=VPkmSXE-<{sF_r6HcFCaX&v=kQ}AjOA%$_QuUTlBJK!eqbiIEoFH`L6@YJbS9~ z*gDXy>M-4ui+e$QpC#>(@#&058JR>(34eu~GP;Wrc1H2T_Elu_@&qEht@L-kP~H+!NQ8&t3Vl-1hDWN%8}$#MUfqz#A*{mR>@&uor> znAB~Mb+R{Uj82xEj?|>>#>4+xPYqIcgL!=WB8Qg1H**8{HuzhGaS2n61G$?wF>SD5+GceA0qRZ%{xJKy13Tr|< z3UCfBs!)8Xxz_mN*uzJ?II|RW;`c@$(@#(IG?m2&f3o(eE@5Zr-*c& zc760+7Z;XNimn!lEmfKsSyPJbijJa4$@xIwg4LfuPs#l-`=(oN`@9)@eL$euf6(W! z8~Ho;Ti^Vb-eETrYXAP+PxcO5N$69bIJK!~*sX*T;oUcD4JmV>`>vr6-S-+W#`hGi zRfh1IDNJpotZZYkDfF%l_|UF|cjJ4K8{)KZ;J4iDI&1|+#3HZUb727230mK6#omgU zp!-I}@~IBx7S%Q$Y<{Ju1A2!w@K^o1=2GPc&}B4 z*GgDNS@L7;hk6?;BYcPPxWre{b!K|>R|c*tGh(#}zk&8uhSMo;pXHgI8PHj*b;Rl# zFm(At<=3b3mou9%_os@s=TCf&%TxO%bXq@ct*wCt&iho&a}s)#>9xv^Jp)(r>eMMs zJ(-E~Ys@Y0ZxgEqMlA)nO!0R7L3AJWV{c>ea#b5|VRi8kb}2=uAf&10i!)Q_f1iZ2si@kzRP>#K)SG>7R3-l+n)%&nDuiIAUI+`3LhWiI~YPPkqlrSHe1r(YHC~ zghA?C-|761np9{uxAY7e8=Rt*qjc`Cw7c-bq@IWe>8IQgWv!o}lUmU#VxToPR_fGMvU=#hcxxh8+2AN&tH!WtN{s>nDF@4wDcj4Uoq56`e6%$B)H%=Hlp-TbX@7v1rFK&uX zqh+DihyY;+Vy7+=TBTbHiJg^y%-Ed1y0vy(U!&pc?Hw}j6rHtVUNBk>Km8+YD>De`JmSj}4WkB* zfpmp)2mEc``lL=~4R1as)6+a|{=uA}8<*foWAZH}pT9*k4wLOq7FtKivqIZu(xr#% z*Vo#{48P9X*j&7d9C{#7hO~QA@+**y^wnA%H7{i+sM)@No5z&Ua*v|jJvrIaT6+TK z+>pY>?@2l|y_pRq$1}W65JmYNuO@A}7xl?+{V4X?(X@z}Xwd>&Z6lMxlU zlDrNaBV{wF)S!nXFco~JqUkfK%c_ErOIh~q@`fb6b5h9-ovSH4P!h?~edl4aE_vsBcQ`DYc`7 zyuts9|MUeD`|TC0s=oB>&h&jl3)QPI6$_cEo8wb!yN=Ru-2M_*%a6#OUYj|XeOqW} zt+H&8QMtn(7vnrz<7X^x@;-?5BByCIerq5>=wlFBzZU(~pQo-4x#1%0W=c5m_3<11 zM}kxe*%7xU6mqrO)myKt)kA#gRfiE-?_X7WttbFsBSD$HT{t`G=PWh5VBi?;m&bfb zuli!r?`CQ41ua4N6cJQ}yD;tB3I8vJ>Y~y|euK7D{{t<&Y0*{@jP-g_h062%>mJ%c z=I*`Y2)#{+JRK;1-YFatv{nAoY+mC*^G6321GRSp1~zk7N#@Xi7*su#aa8f1j*YKD?)d6b+T#DIe#D5I(IdH`@3s!xheiF{|%94dUYg~FG1cq-0P{>I=w3Tup*eZ zCZt3njS(j2!J&4edIcmIIZxtl%&E@V?=bUsOb8A_Hs$YkAP4(dq~_9kpCf^A=<0?F@nVGWnh5D zXI0A#!me($LKsKqDat|Pd~(haMolGkLZsyk_}cM`HoxzD)_X)^=95*=#DYW ze;u;T?u7m)r*ns*nuT^=v?S`(bp9+04QWW{Pa))W5A_~yiS<}?Br!$(fLlNMo5<)m z0EHH)C-ZBwXii~f`P4sxITuoJ>0t1x!C>cLa77%1^EKl9@!%-Vuh=NbFUmt}9P6Z7 z9LHmOIft$(ojC{#*#=hiUQ`*P*>ak;04daMn zxxTSu&m{7$)*lSr^yk;pu!_INcC0c{r7pl~3~y&jWCp@rniuZyk*I_BYTpv z)SsO`Ko#VV1#7jydqtPHPbf^^o6a2$1-)=M_m@7GUbY*0Nd5JFD6YtRQ^vW+wv*g8 zEvHcrI$e!6z1Y4jl51A?r-*|#D!E2MzZQQNBEyk8_QUeohSL=Jrf!bY0xP|08iy`4 z7z=q;Lo`Tmr55xSj%smX#xUKC!TG5xkBWTyZHyJ;JNISurE~4_c9h{%Y(Fp#)Mr9S z-Ts@yXvQw=kp^$O7GxT|J=9vM%^Z@lYoS1P{AMZ6E$Fs3BJrkXKB8KLEUo{TTJMl; zq#_|`^qi8;-z)mjRP;S{E&CDeRXh69%+8c=FI9bu$oJvAEqWbax4$F%P;zIFggo84 zqu@jU`RsT&0m&tzwgRYG$@1G-R&vUUMl?hhLW!L{Uc!$vH?q~V{qt;Rw=>cADYP|usb`}ma-Q|%95x~h^|*6b{B{Hv;cRG zI7jnocT2Kw{F~eZ+{fMN$HWNBzbk1_qhnE6I|A9>fK+DK;FKDNMWCnYrubVCg#23& zh%fWYcZ<;6g8}bxV^3da8O;gDE)%NXgLOP;SmRvVF2wrB?+KbBtv>qtCZ$6B;hNtr2=LU)u!Y%K(SMmL^La+kgI*3 zCb0iyCPk+*H)hK{RBnYNvly*g;(p66ZTe_4Q)xLiT5Sg&m*tQ<#aD%J53jk1KA^?H zDeMDZb&#!LH7@;Z(%S>&NT#NF&o_ z;k^mjgZIm}zy=CU;H3jm4z`W|BU|lEo;XrpubK2tf0Gl%EtL5D`s4Id`OLY~5xW#fpm4UU89+zV8I(h`GcR80&}vEBniIv53*K+t%`9EtDgGis-% zmwZ$@k|ckd4F*S|Z+nx4T=Pqc2s?yAXUN0lL{n@BaI3Paqhc%cw|D=lS zN~M2tECGKPVqk_9x|d+fb|;FO$jdd*7HJtr0EnpV1!V-LWXBO#=TDXvZty2-I@jV) zuH32Ve+ydglZuBmBn{eDy$Sj;>vSNiA9|sFY)WngHF-K&Qb*xh?~SB?X=NcbFE-5n zKMGT%UZ$BmC^{NmTVN7RPl(2{I3xUflCz^Kx-pGxR2Sga#Gha57yLFrkVN#F2KQDq zmR^&Hs?k-vB!ad6!cKq1M$HgNouKRE7-J|5TUdSUgF%33q=pEQK|FRuaT2Hf*QIEbU`)Da;^L01(lp2yS1eU_3$zmS7&VKAnG^fuCx>VOvV2338`VN*B+KT;0lqgZONsIQMjz&4X0g76syi$WM<{ZdP0k=dn zP7!^j@zdWL%~hd0k5#I>56j0%H0yWB{|r-34dXWmLi8%V2Q*|vr*m&h%xQzHy^yfK zA;$19SAUG>pOy4}Ja==>L@m_?0n4}}){Mo%jJkoo!(&Y1@?R(I1In*)XdY2i*)1i zQbp=&)}i|J`qc!QCh0Azd{gzRRI1F$gjiz5l{(Zst!>S(MZBz z7+YpvLc&CwSfMrr7KuK_vlwSj@HTg(QHen~e-uR@*dUq}|CKt;T~ zGf0#@WR|5BHBqAlIuVJOCngWCO<0>@>#U&3>L{96Bi!*9&mscVdWI&)PU;}dQS zjXpKDYFAn#n{Dq)7G~Ijn!548D7};HkEx|>&DutuH4cAFNsl)p-TDXqdW(iXMff6L zILmU4*`RdQPh>HjtTkclKdIl|&&5i!dAG!`LF2^QZn1`%bL*qXz=WM7gR@?@2Vl$gy!do4nV zRO_CdatbN#18zNW3RO?g(5>}dR;C~KH~WtiCLA{LF>b|d9N?xt_8=tx*c7(ESxB9R zB-P0jmjO?_)u!c0Tsd9tr99pgPsigB>m!?+wq`z>UUe{=s+U%>f-+F3JhJ^|uWJZ) zKNqYPDn~SJZQn-sVZj(pp>Ek3EY`Miq_9E?fM3mH5-^g=8+HAgE$uaj3$gRTfq`s( z?dnev-GlKfWPip!n+K15hA2IcuzKb}9Qq(Eb6R&34qGjKbqONgN*LsitFw0G=v*;`D zwF%yy=Cyg7 zJ^n;GYRsJpVP8O(){KTjd>{jK{_oSPy1i&L&$`xZwmEu5c6WOI;j6`-$G88kW2vyZ zpAS|1l&d(LSFAJ>q*q;#%t(d0)%3Yp>iy!VEr7oRyueb(<*9Vt*vfYN?56@9@|FdDE*VCFJPRMP<#&_D7ZV zq&VwG$;unYu3{w|94AzeU`P6N1Fjcu(u_cV*C7oyW!bw;V$AcFBCOeU_03v7`tHd?XLom1=ps=%4WCI>{WkPaG~{t972zU5{Vzcky_M-|M^4I( z31^WCBgc5Vj!Q3o1$q~jRzqNK_YvvEoqQJQ^1HG!^~Ev+$EBBTP??{ojNqx9ld%$(;L2tU+me2usPrlGKv2 zrrk|idexuzOTMU-ytJyU)Z|ihdey_OHKk(f=hc-`dlH<|GF;ZrJDj1!cMVVMaIwC3 zr<(eE<>+6cCm~zff1s0c!HIJq8w@k=3%_y`CSo;D^Pgnn7UR}ZE}GEnbF|&E6!1drS>XitKIRnBzey5YubtgF zFsi~~mpWTg2X+iw;i(*IZ4}e*X-k6eH_uT`#pZwT};%Pf8$Sj(lg8{>W{l}K3uOyz$J+6zWY9Hl#jMb>QlnL zl+3gtyyYNdbYmUXlWRfoH<*EADu}!A{I3?fN?iK;Dy>rwK=}6(1nC7Ff%S&;WFcU+ zQbska)}O_-o?0xU8HrA^7*1CxRd@omnjC#AXLqidOQhSZV|*x%x9UOKu-nqpTKkcK zrBiChnorPVf@#AzjJl5$WdknQy3i;Ij!DOY8o0va=YO}cI$VNID6!ko;RE=@X^rm) ztBoq9e_+ixSTM1RjUOGa>AKtCdi7|IZ?)2}`nP^>>4kf0H9CvU3+aa>EnC-U07EVxiHj~AFe>D&#=d!`Qw>mG_D?_afNYp zE5$lC3L7`@yGEg}IyTe+7PTHe#`SfeQ!878-x_fZQ*lPEHjLVwiQnpv6I-~BwF}Es zwZ~t@5|eW zXhzS$f&Y}njx#A22-Uf@&3afW(RXlgVM?)g@e!P^u7Alj!xC#np2FPWa(#=EdY6#B<4(iO7k6A~&CN4%=%725*qpeV- zwjfkfg$4+8Yc{vuNWo$A89+0~TQKfhChS#MT% zXNyII*yG_sy2K$4mEtN(p&UZ0)F3aoWqT&TwSHn3I_4}=!9lYsk}WeaZ~ea5O0}vs zxK_pEsQ#>O#o}1(GirrWZwn!tE-QIgz|3|4WR?#KHx!(aA%C8$01u(qUJ1)n&mbAW zaG&1s`xPv;@8Zg<(-@YO^oE3nj&M>Nz`NRKEAH($Zmh*|S|-l>UK~H%#piB?2gTYX zU|w;a{A5N)<kuXl4y!oE|`b8#sutB`sIPc9!fl>v^u%ZFiM9S3uGE_qH| zESQ(leJbOmBU}UFH}zZDfl<{qc2rPlK1-zoqf$hq?{+zxY41OXo!uRBO;xDYq-t;a z6Fp9%M&-6(t2-0|!lJ+9*wF-B1^R2Qxl@CF=VyZh=lpBeF7GZq4 zJl*E%65nEKt~?b-n#lDw&<7p|A9+)Adt6;@BGO0WhEjTh>U^3F?zZmcxyGP`2L?vf zgD=*lEs(bPcbdp+YMraiQhs367?O%h&CM?rQ!A) z`eTuXcvq4^L@Rh^3IO)*PW6Z1y8f^t?hjEPD~jCzQLOD8)mExOtkN!S+ErUto6QSP zE4IJY+Hdzb1%`1W&M(sIuy?eP%j2_C3sqD{=eti72ShpFW_yy)hc zy8ZBYK(kO0U%m34{NB=L=WqlR3U zQ`@b~C9Yn&PaW@B*$9XCvhN#3IQcs6UJxB1gG@G&0$_C_=)l#rB>LTFkW`h#C) zrVXOXkx0}T1j)I8XFPU=oh(1tdE%&3#3zlHG;fy$;m}$W&yv}d4+(` zwI)orCXB|aIgh*xROORY$JW;caoH-%E7z*F-^;kvq(aS~Wo4@=E6@D`cdUqko(gJN zGG)ZH-p-|H;YXY3?RPqoEVJHzLwRqP!xo1ZC?5~slg!w9 zN!i-y^1&K_uQRz-L0-B&wj?ExcEY0w2_1FQZhQZu*4y>PbgmHq5HvqlH981&d@r** z>z!Q(U1x}n9PON2JD1C5tShL7Y5wER^-jIh3yWow5t>B^eiE7^dlRVBy!&Liu?wqt z^!!f+i}k@S-Ag3|yP8=G*8Sgsl|P;GmsM**_HzTbXK7VTLG9#XG;99@)AAVuSl4qI z+@_`TmttyBN4>6TU_(10Z-KUZo(dIcwUktJ0j=w`YK_mka2)2fNNS*1{)TtOR`y-v z?^-Jt^qPHQ=&D^noUSqGmN$VRcduQ%Vv7fb>eazH6|&KCH_!qbthIl`bkUgv)c(PG zqb5n?t~GKrSQTZ^KmP&HQ>&rjOwI&a4M%{a)$k;MG5>xGVl%zfX6~|e+EGCfvT!*x zK=$BjEfs+Lns=#^DQ^Xf-q9t7g=?c6A!S%>dgBAzxTpe^V@POgup#jdV}Z#Hz`V86 zv()*O{pu9`sB;A^i*~{Qf0ug^VkWz3K0Q3UXHR<3l{Cfeu3k}+g(e_3AJRV3(G7^= z?hIK_ts9`njOZ<9DMcQsU1j)J{NM2ZRuTUXTYye{kwn892q<0+6>;`BS_mG$yrU^^TwCtQ?56Y+onyI9Z`z)ky;mxe{0PEaMvE9_AQn>g4WFOU0*SR zU?PdjNLhH^>vY0NiGr~$?4vP}Zpv_!fK-V{4=K1$ut`K==@a&sL5?0NB@my8)|Q~q z>_qgxrBrC0i2jRD{P$><=*U5R;^vWOG#m$obBAeH0P2xml6p<%FgkS|V>#b8I7ENJ zf9KY! zF9&Db4;SMC+`_ZwRhA?IIdNpT z^%|nGT*i+nIa5;8=roesj}z^08NXSSR|0L*s4xxcB{!>~;rcyx(@JhPY#^dGK(|JR z;NCUAoC7Ia!$zB6k&X{yC45O*EmUSC;`4^Ri_Gakcz!P>0Je(JRZp zu>^?tMAV~v=a_{Po#H?tx*GbKuR2gSxh(1kYoy2g5O9)j{*^ReI11A$T0mBii6NX+ zgz$~QN(PHYpLLn7mFe6ZDLA#XL7AbC32k3vT1Y>6?iCFJO%-Q86 zOZ$2xS<&6-0@r>uCE8BB5Qs;uxP|HbCgtA)El2QJQnXgJmt-soP-ZF=Zyt^QQ^}4t zju){sXwW_qv#IZ{{ZjqVrIWgwwv|=SBZ+|OTU=~_=FV?U?B|4`vv}ey1P}Bbt&ZW{ zbC(T!nXh{L@E7x_lFl=E`;wVmN^DCm_?bLKh3eZmH|p)iQjpc??H-!>R{p{E zbr4n36fBo)I=r^v1_&<(v%c%gOaIu$U;*fe0dRd{d(P3N42Fygw#EbF!{XdbN#0uP z7VgXu1*poXe_AOG@jj6cmj-}DG}fi5H>dL-c4>TAIa$}h)MwgWI-#60@-N!qm{V_# zhxgd#Yb7!oKG-rEzB1we$xo0y5fk+(=U?z2?cP1^bent}^)OnqE`61Drbc(MwQBHm zZfAU6bUKKHKRs?(V*EyTR#d)$F~Lc}cvZ%}4E%64tG@#F3FeRnX9$|>GO zP3uHiWh@rLQ6>b!JBo$!?1(u+*ION$f1SsXuMjK0N^RcWWaf(;XK%lg-Rlz*>0CF( zK(iKf@?Qq5$h1|^ zNKq4=Ucz7L(BtVMJ*LF@l0~9aku}b=RYW;~3@$D6MFXhXl2IflzH1+wbK>?D&+aS# zVEZ1B{Ar=f@PtAQ*1q4$z%JtsOCsTYxW?eXXsd`~t?U}Joh^1#G)OUX|G5QwJ#1E~PXss2~i#D}TK`X`Df=ulch%^<*A3-A8 z(B1CnO^fID8bvbT==aFR%aRS40CTFt>(;`h2aE@F?<=UUqEG28?349%NysmrvWDnQ zG`bbg@jI<`RH~vD>)RBgN^NXFG{UQZQQDKw@OswstPOQE__NO!0@DD|aKLn*Wi*St%8w-jnDr8bvBoj3(u zz7?g=wWZK?gw(8F@5WNx&85)FQs|aa=(bYm&QfS~916U92>J3XS=aKq`0TYvp-@Wb z@iwbOuca%c$4fyuA`<$sP(vxySPC`AA%9G5IQ7Dz32)uCC{sK6r6Q-%ujGy<_Kp zVW9T>J!Y4` znF+O3;cXmK?%xMsRigcGmlIq^hO)kS^IoPdK15+D)gF3Ox4w85D zv`_^%Ykw-Lpd`vFeC0|Etr4V~gNBHwrVmY6#LJ8}9lTy)1x|{d<}+CQ1lu6|A!OfV zsS+H(ofNIHR0)`+^2j2YcW?ogee!-VpPR6V?ei-8G~pDeE9`S6{bo%HPHAlEwtTZJ zK>}x=U*>b$tqJb_e}8S}(vFR?sFSfu)XS>!&( zt!=kTNt8uu4Bd9CdUaXk6Ba2oQ5Jc>MXIm6$gM}yr2n=^DT=b3R}Xnit&g5`UrkNdIf8sD^ zxZxqk4U4a{ctH(rs5ICvf0f0Hh`|l1viM!NfP!VBW^lvvW$?FKyjl|6Aaz%)caz17 zzQGMrjm7xeEM6oIZjfp%#$RjkqH=J9)NnCAYw;p}H4! zmWhkO4bs-d_`fQCu{53aiO1}7sRUVlW`li7e|x*Cxju$on*IqZZ+wnS=T=zYV6NOC z&^tnQD`kFSWu#9HMAkl~XzlZR_E{yQRlzsyQ>xuQ&$myF9QLV0g~4LE&h68@B(A|K zd#t6_7jZq(J~f6|<_i1NFk_$c`~-_Ti=VIZ$uN$RAswaAak1Kj&io3@=p7-JP{T$m zq~XS3+-09r@D$tUZ|w87V(K;aSyB94%4d4jVhzzuq^v-@<)|#?n9ZlnpUM&0_4OEf z8>!EP&um8#Tm?_PU1Qsi?j3=5RWv?<#NH9~9es?o54D7k!|Vfo=p)5Pnf*{apP)lJ zb?HBFQX*J318jnyPuJhGE5>hb+LE5Ug@-RHJq{{ti679weyJIHn60;HCV=sI^ku^m z$s1IvJfGd9G&;y`6iYNJ>tF!pl<%dW*->4LpWgNlmj+Po&%>grV)(2T3%>Jxrlh^9 zE8y1m>A1x)ia#L&*<3Nhiq%lnJsfu}rsN?3SCXVo^TN}(MeZQmO4a_<#K$}?XX4=x zUKV0Jguy|l>Sm0k+Z9Ld=zQ2qy0cfOkZmRunHAIri)X4y_`-6!*s_1|EJP1ob`#{3 z)&HOTdW&mG8tL^_2KQ+NG`O{nzha??u7+p_JT_{j2v&>_tQ6=EJYB|Zzc-vt<_`JK zF(2fDXRAr_#ZA&kiK`?!{Au;=ymgH=27r1k#g} zo;rpa8c8F}ClFq{4ezv(P2E3iTFz*@SVE=IU1tM(7fVd^uW+B@ixd&1F1!zv7Oy~) zVGQevwgyq?=hqTZPNfIJlgOy+_I~Ud?^YFMQ6mqN8GOZ@0%(P0?w`-?fm1v0$- zi#&p1SVqS&L=Mj9=eWt5$~8V|EeluCqSaasj;>c$f1fn3LZsX?^ihh(v(W9re_RK`sdk6elAIk?P zagtBtYIM?~b!+B2YQXQlYt!xtO?%qM2g^htCS&7VlkfwKax4IqM6`_o(D@6nTbc4q z2&+}txQod*A^9G?XVk<8)2pfvUOMd%?tMx&b$|cwN^rgK7E!V`h+SXm z+&54xU}7g#-D{FgfRH$7A;8Uw!F^8Pbo9~ci(VZR)EK_g-%^mHvp4oLcD?7_q(1zf z>)eIqb5NT7JYtV>w7Z4nO-N7L90ZyPXzf@kqgqoH|!qcrY>M#~`Q0^-n5+yjUP(@kTDscQYR*{nHq#nfSw)BZFWUOoN$+ zc#G4Nf_Ac=vjkR%85o{_kBHa&W!t(C2e^E|O%p1Wcw z?mT7C>Zr63qz*TUTa8|4?Wzi0&j z*jv%bPhrVY{-PbG2DZDQ*ZvZvxW-_Kq?)@P{yNIroopW-TwMo~z3s=FIgENDsxX15 zU`Y+cmB6f_0QYcknL}gJw^%klG^SYGB0~&s15d4hVrynJFN!y!*6v|=%HFGnB+^U& zz{g7T`EP*)0b7NP@)0FFH&}8V5eF}kKa>>?cpUt)hIs9U{NF_2{%ImGZkz~Ci#%MSf?QI+X zf+c2B;ev&;;UsqT6KK<*_aeE23>!trDfvE|n$pYKG%vl|(BlHGyU;cLOKb>B=QIlq zmbA$x0@tGtC!=Etk3SZ#T3TA=_i0pkIGy`yDSL2@n#8g6X+n?@99O7X0 zw2n@W^fnBMt7-2Jt{G#5AI)F?$&WtZy*H4#MQ1g+#$ky?T86%BC6LHiqZ)Ib>lmhI z@Yk8jkOZkeX8#T#^8Fvy-UPm`s@(t1X;0gf26uoI0z?ScqXsCLNP$FxgeEPu1T3^* zm8$hxYn3V$&Vd32681Swx4Q?Tp-@NQie9du;sr~grA-GKCZPjEWzaIHXA{s0-q4mI z@AvnteKHh$@BjUO-hLow4{JZ`Sd zsX*vNck5B>{XNV*PuGi^B^BYll91pJYz(QD5sfIUPW`AqA)TtP+dlS9FPl=?>$OWV z`BS-rxjU8?9q!JhO78drrb@9&QrEz+a6ADC1aT@X67Ey|O^b&Hapc#zrTr9|PZ=g| zxN?aJy47o8#KW1o?a?30*NKm_ymSX8*nZ^pQ@E6PbgL zHSs_ECTr-^4G;N$WV6DD)R?2uo}ns2rE^ss{;IvvF0Eqm!@5fo7rsdjwk=ri&{fc} zgVM!wb`{k>j+DOn=KeqruIGwf(4~Ki;Oub>ov-V~z>HC3(WAf^FkWS)0?=-5uKSv@ zw>EPFLM!2`xAG<}py}HHYd>a!Dg=X#;*#D=QeSEcI`ot?S%mcU5An6~-fpx8J;@#1 zDhoSyBWpA_sV?652k-uS`+NQVI(>mKer*81YUX{-Y?-_`GAba>30cwglq%YORG-Dr zbFKB%fi$V*RI&hwC57ZFC)Kv|U2H=X%0KXw4jI?OJ7j6S{GUam*_`HpFn#YH(FFi@{X0ZKe}_>9PLK_X0D{w?jDj2jQ1l1gMb(a#ZVO z|C@^a1*}^EPLrjCZ-Fcz!MVli6I}mTjrE7(9tGrE6lP7fBb;+5VRr^vHZpysq=U1uaBMxk{_P{#0d49*vs0}{Z6FIL{n>`~!G>VR;pDF^ZVtB4Ale-t7d{gAixTVl6R-WaSO0xqU%aJ{e#ZZ=z^Lkpb}6vl8UjknSS zn7q{7r{Q^clQfOm=8oMu_8(sMNpNScFXZ_ucEtNac(YVAylJp8;q|7zY^)P(KWZZz zB&Nq<%$_armenCQ5=~K81caHfNR}3^dfKVoRjSOY^!Fw%^?Eu~yZ%|$S&SR?cXBRa z+x@p7zql6#{)RURo;Fi@mOD_}G@N75DG5O=YiuV@>>y-`*UW+Oh`1C*h)t|dVaIL2 z8`p6Qe>sg2_duu@HDC!N-srPD?96v`Q-XIez(}8zhK!zUI4%7)Pe}w3)rrKLkeXHL$)fFr#e;&sx6o z0(enw_Dp)`^sm&lwrI(goMHy-`kBzz{QoQ!7fna*78)p&F5;nhFV0o$V7<@el7K-& z!{ab-M%>ULmfFjmOB1D;$i@BDh=noSuhRnVryd1Oqlt)Nu@k_5ah_+xAL)0@B@N7_4*uxe=T6sN+EY?8 z62bl=QsbO%Be)j-S_dM(RHO@Y8WR-BelsICo|>2Z0LeMwPxO{A7jVq!d@!m8At^(Z zekrM}{R;I1j1LvQF*lT7Qx<-cSEiFqbP@gI%SLNuQ%Y=$*Tc3;!^wwlN2Ajsr1v3t&M~37_((!~-7KG<7 z6d6IVESypwp08C>GD(Fe1@!s_7&1(Cag~G1QSz@>e>bAXdRV~}q4X<}`teeCUnS3$ z`vs38%wHQsi3oKUoQV-+{NJGgE&cR7Og2Lp;?T3h4y6S0HK-*EuaezOxNAxnd%h*S z!%QlyU(2N`zblrze)z9@m5`0;C$Jt}ME2VddN}{i`GQfYP_gm6Gk(=x?pMxcOfSk* zuXa?EJ#ccB&GSB*qXyY`s&1Rc)Uv0Lg&o@8Az>Bde9(|AFh!X-w9q(Y?jIZU!t}V7m^?Q zp+Siq9P`(VK*(&&-(z|xs=Hn#$qBA-G5VePDH?I1Z8JyO@P{_d4YrRoZ;eIXZ1rcC z4UYsnH2CqFH-pVwX9b1Zz$hs0*QQV8y&1UYVJOh7bNdzvs7@^t(N~n19y{H(j|sk^ z%%xf=H7!zU1ihoMiB4A+>OQb|KYK$`D(wyqOps`#FZ-FVFhjV|3}HpIH?|`{fT7p* zBJSZ$)76Q@)Z8Vl5Ov|mJnD)bqDW_W{W743zrjr48@0+wvypW0P&}4q^n6r-K9k;o zmqqq~XmKF#rlT040ikeQl169=`~vca2*I1UaKeu@lFCg?VpFsgB)Fd{;~)K0QimYZ zPUX23lAUav=O~-i(TdSkD&XSHFxt$?U0(p(f${fL_*(RWrc>DUuNEx+XRPBFZKm0c z5DMwv{>q^AA4Tz4`tL_Z3SS>dza{MV!MroZ{E(rTzrxBBfA{FL9c^<}y<^W_S&LZw zBWhJUc~ugv)CwH%7>8@WzRtoqwi5DmH-!a;#8*MTwYKN9OZgg$01Np z#;t3Q>p5KYXr+(ndHxKbm(k;thn?mLp3CmT>9H4lssAOj8%(&}sO}@!>LPvVuAq_+O``2VD*)4%RlNBTR@ zTmRCSv0Ii}q0nC@bo@mcyZfDXz0|I|rCMT&OM8+#HkjN*Kt6q*&0#L1UZjKbN(|Av zfDm@wqrWyG5cmqj9knIsQmhU&%bY}5`IA9es_@cG_WSVlSpQtX-pbIgl?&E2QSQ5a z>^=>{yJ#BU+EI-wFF>lg?$HxQI=Acls#Ial{?2o^LEUD}8gqnBaUSCCPL=LUf4NB7 zterVB*h0N;`3n`X_!UwI<&s79fq*UtuN8fU5*FD%|@=7s#JMNlEPKbWCe; zoD8c+HlmalY2bl4bhd@!O}Wth-VQXB-?4|Gjr{)h!!*h`ag@KA#tIXI*3{Evb8^qNqS`1eM7xy!}*t{;o%CUP37*EVdpW&*OWfKq7<*w`s{ zqK!~f^&i^%#mh9U_knO1<&yN$r+Op4FDPn<4;?S?#sKiB(7&wn?Sq% zS;bvd)F1j2p_wXu{1yAal1XsWRmw3m?n8v!VCkLDqt2#XOEAeK+m31bnb`36ScKA^ zQ(7R$Y68f%&@|U+t4yRYSu9TlPCd?kX;2v$ryftcyzD3dpQ^3Iz3hMR_tfL;^0WNa zXA(kv_VG$5A%8jqA8u28GL)m+zaq`1Eyx6`C=Nk_WBT9BZsh~1 zb<4|6tc96MTRSbKn;U7yHM|IXSqepx{vEjqeaQhhul)qL13o!RO@|DH4Yb{DoFqK} zR-yJ&^ei-I^98{!WmD+6Bkc%=DgK=_->Q@aa+>)rOYlV${tY3$p=2+F)T_L7Oc)Vu zq_>Od6wgDBP|&R7X6Vr8-vX+bSnZi7On)9T>1foY$BNTD4jvWFV&%cVUF`;@+1Udi zh#h>^ZX~hg_n!-xRcG;M5Qf*XK^3fZ6&N)(t%Vw}wFo*KhF?E;JN&ef2RG3L5I5M2 z|42&1?s{EEdfw@@S4)uocX=*wQuDX!WV%O`d zk@1cpBmVSs=uUyK>rVcT^kr%f>azG5`_(dNtfF<5V>e`K`Z_ca8IeBNe~~!u?0C0>mRKfYfmv>UAgMYStlz!T)^n=l2Y8r zRw@zEruQw;);*<})BUw>lB9kW5c)ZuCOsz1uGLy2<{GO=dyzL{@6zLdR~2S$AJbro zyqDf1IM0BLFw1L*KXYHW_i_vki7wAH7@B@4H!ERira#u_e+{dUNFN+7k@Sd8nHCg+ z59Qsu!XpL}-q9;H&l<-54m5p8<08bps3y$cYi=_hWoCZmK#JXkZEwBHo*4h+GU(dP zU*w>b9}I;JE@1%FZgHbaqT03tY4odamy`_2UUTQ2=BM7ApT4tg&tcxA4MqPjwg7QH zb`oV#ntYYF@?Z|}%Wg|wgg;%?duk`&vR-DyVfia|22TZ#dXxUXv^6y{KXqr@+lP70 z4+XvTf5_-$?kY};J71?XtD$U|28apyX*W}N1VP*T9E@o@y-8c^w-6!qHzhBill#(_ zzZm;7`B^tFot+v@sb{a(#*tx4pk?F5Gejeq1* zJk5N&c*4%#5-c6dcBfBeOI6MeTnVIqqg64fXwQZZYBz+ij0HDM$vUpS^lJdfPr7+& zON#V^f$Ao0?R@|}H3E5)9_;ZqQ4~b{Lm><(dYjZ-n#t!>i-R1^dp(_LHb8mVyA~lsMZ3o*>-rX z)QWj#__)6;o{bqXS6gIHhwN8fJbbd^*4uu2_J!K)a(`XW4Sa=h(kD4Rna;J4p|={P zZnpYTUFo>syX=|FYNtKZ9%FO3Djar{9UgtW?7`VHorjdEF8qw6UV(Z@@Znp4U$|k3 z`u5lZM~>E-+8?L76GNYa|IG6(sk5T5P4gJl=@f(NBr!nj|Jf6?xY>NCEvu;6*`+j!nTQWR;c%$n8L&Zd2i36Z4i2J zy~ScC*U1&cR@XvrI|3cq*RjK8p!^MJ_cfM3+|ZI5$JFNy>-TF-jmykH7(W}HYV>6X zcf^|vXuE|aCN%Rpeo&#y)<3U?hjvZQbi8!nVYj0Y3}}Y zDha=|7+PhIQIRf9f2{i=e;B~!vUuFb#1> z_ip4h#RwPKZ<+c{?&Zzummbrgaap6w!pmPZoIb(68|yUUStSASChsmnZvLJK)EMj@ zs@?co$^fO}iFtia(upl!%j6)`1fGLnvao2b-g#LinuUu>d+&rU!Nk-$iM&u*AR*oO zI|=Fg;a9nGg|3Cv&lb+P%Yb4STH>0%7l&AkX1&~;>}>e4t$%tHs-fPncLaJ#z1DwR}#nx!3KO?l}kc5PO zG{va(kC*JaUE?kkkKGPBzT%OL?tmB{vg>PCIMGW0`B(w=;w#Of>eY{UwQ4#q@~h_~ zOmy?SP{I7~bRVpL^XK`ym5NBWo6N5+SA2vMwf_2!0&;~hq(kJxGL6`M{nfu7zZUt@ zzoYli!m9T5awOJABi_7Yoyzo9*!aWmNw)V-prkv^I&3*rLYT}aPZ3GpBM$^pKFfxu z5>}q9Vc}x27Azyc%v%mkQ%=q5ek}drXEoy-_Qs;5G$DAQArvBrx!A z142G$G-mNKG$?H0te$ZBYnngVmM#msbQQQL3=yaTve@G*CZT`CoZ_o6r`0-mX8gqi zeTB!~%H{gO)%0IV$obS=xO|Q9+?GBi-leabv!PhkpaH$FS6r2b*Os8!L7c$hox!-+ z-U;omgFu)QV_?z2YSy}s&`@KG-k)XfMR?vHVU%?vNB79fevAU~@IB0l5BrpJkG!lF zJH;c+&|ih@P)-qGGZd@;%_Vo(sZF^Rh`KWqrFq|OM963Ewr$*U3fPOC9h*hES?JTR zp8I2WbR2jT{gLzPUbm1-RmjG~;oY_=6*hA90=R>?7s`%xAZ}oDjC^RiQD6^A-a0Pa zMlH_qFHc|Lzg)|*#mr%KulFs{z@|Js-QP4jKH}NgjU`GGO<)(x(29W` zh) zo{HkdRxs*jjN7n_d0*9eGJWv`I>?0_oRRc2!U^7H`OFEdls?(tSO!im?$z+8%&Sn; zMnoaSY2s*{>xv+uta?Qsqu)!W;YZk<1fcEH8a7*U1|L>HkHWQgqs$NX{-^8DXm_Qs ziY}*_L;PV@^O!Rxspwt?jvH1vFCueLo|7J#zDfl<3cp}Z3MBUPH`SA|Ottd!T6+Xg z)AQ=?%6T%CUu9IZ@T)!RQv4?Qoe+S}hC38Z&aA=x)5$EY!2u3h+#9+dod;w1fbRrB z7hX0060Z;a|A5yqza;dblY0oW9uTa@#2-EcJv*bM_vQhAZ50ualHThF?q{q%mIqqo z?Yx58_Ekl@lSzfs7uP+91wP#0pdAy-x{sYwOc%8kB7fSUBZw;LYc>{#Bs08~JAkHU zCw^?uxo@-bm&qbq5Jv#757EQEZBtT-maOM2lcwCD=89Z&{0Zt^)DeNP?m7mz0 zJSGtou=1cu9vi#eXsZBk6i% z;%yu*!q~K`Fxkp*#gQ`)=(HzomRG+%eXX+>`m|Bt=Sy_LIyT?HNOL21cJ z#pYx!hjD^l{iB%|LiB=%mBC0C<~Khne+DN=e%5O4%?^=Y`OCq^x^A{+$NfNgE77rf zMEAs__dxPG2v*X*ejM+(J5hIRpKLpTap{aVBUgst=vhVMX(XcY-0S!We@ThT=J7cj zkMVNPbB~Zz!pwylaObaNsGS+#d#qkg#iDpC&xpt0m5rxmBK9}QPf4U(w42LjcaD95 zJ+VO}K5Zs$g7k>msViOSt=!VKxI#OhTtuyHYf^O-FG+vaeK%y3F8yn~AU(TrcIFIk z<%~rB%mf4e6N5*=u%doM=5d-sNTebWw<*WqLjj&S)EaJ+m$+(MM3?|*%V`il0>l4 z<~(|TV)jSi2!Qv4-{zKNU4rFYVA$IRT^8uwZM;L3q$rnj&W;ScoO`HHy}6}I<@2+& zvYOcXub-UG_74fr`-MvS}Sk3E%xKe{eq#&lo zffcDjJ?Y#q?@_};B-@;tFX4=*TrPDC|v z!Hq)-es6d|iE|?ANAOhj!$S(rA6`&8k%*p-3qCQV;As?u%Q=YzI7(f#&r((NUlM2Q z2Qgr!JW|lCg3hS?J{g^;Z+G#%b^Zh1L?`D3>bQ^OEJ_WFE8xMKBUjv?hIQMiu(>fP zPQCUtH}Z*@GdUOiAJzC9`P9u2@SQfE;>4q1yF2r!Ba7P}DwV4WaN)G7#QI=dI4zY} z?_ZHB@mhW#oLL)A;{uPi)6dCRJ~ zu$u(J-e1Q2YSzM-_4<5=IE{rFdx9OzQXGcx`d5WffQ&Rz7W0cZ?FaL#xF0}ucmymP z7dDp*N~EsZ>$<^6^UG!U+^75uYV!QW=gs|hD7 zrLJ6(7>ZzDR92_$PP3K~uLdGoQ?o>`l-9Kv-~Wr{-pYyf3P-Y~41z2NfP|RcQb3_n|t*s(4d6vQI0{Ep6LNE4J{eZEutq|YGoOlJ zB_uMO`&i+1tcHtGBhnjp_?>XgSgt(j%g$AoHv&b0d*OMdj)BTh`UTHe$dUPN@Q=1V z)!ZYO-r9d);r2+$2yPeN{)B94=K7w=WuF6;XuV7*_KTw{EF>IGW&>-MQ(Y-g+AI4F zIv|Ps7u54fB(l@FLX>F?!l0$9fm&9on`k2M)Q=W^w3p(O%Uzsy3ot<97D~hsSV?j@ zOBjW92{h3v`gxPYRvMl_Q%QM^3KT+|*Cm z%HUZDEQEr|8Z*r+x0G_HLCAuu1! z7AsfJ^=emdtGA_C?;u@r zwLIImT?iuWs{Y`1*-Zboar~Gb^8;2N=Vi|aOn%Cm#!q-1ja0&bxj*&0 zg%l~`brx3E@Ve6=4CmKX&?7%(3;y(yJg=iYk^xt(`h)3=HyQX>`h{A=>iamz-PWj* z=s1LDINjgmQ`i3Y*ORN|nQQ;rQgCw=cXG+&;Y7ci7WFinsP;5F!Ede->roGXYJF3z z2E)xo^f(@F+>Z|0@6yYT33j1nuft_;?Jm>y{mIurPI5otgJ|rK_R8GncFR#VEB`mH zVvUJe6n#^VnoS_lKVHhC8bGJgw0(*zBj)zLKw%Z>eKKxO@1tCxt|h%2xRAB$y^jl& zrlj|-V3(pDXijtBH0Nz-9IXVNYge$#$GkKm#dq?h)cjcrNV8Gs8I72q%`GGPOJc4t zzbc(CD*H9$wX4+!(O?j#LJ&8Fif#}q5oDv(D?=#t@-dE5mk5tPsg(JZ&fu6?JS&_%liZ@8_>{S6G=>!O96Tq7TjBWa-z^{J*JkuCyO z%z$by`xWkDWy0C%_Z=FAA$4Iv#h4GSQ*K3XbD0hT0ZG z{RWSKDoKf6!*d@*L8I*$>I_#n_ox+UJf8hs>bEyCPhU#*Y;gicO zqxVK)%~UVdk4~a!d_6=-qa*EcY~-BGAHXAdofozwq7r-Upfvz(P)_3*x~7yuVGUy^o9jQ%J00 zpi*@>vDu5u!?Vi$&(t^Z6P#WdoKrPu=0p^~+=`K2ls{DWcB+{R5e8ue+?B0K?|LaWSxEe5s#1?^yPb8H4=Oa_>KODKjOFlP$FvP+J9(7 zbddcfqg}|q|4?c4d;2YqR@iTKl(FBY=v;ow+M*-)*S$8JvamcCCQNzp2<+8!&7{?0Ovw#P<9w;Q~q=GcpEe8h?*qfhW_ z`E)kR6E(92h5o8Ieuv1(h0~{8m~So7}h-29RLti2%L=K+QE3;{!}K#i}&$sHfRk@#d6)Db_wLTwvqT zo;JKl``6Q^wY2)z)23y^`d1vPI(>EdFs-0GCuFe{(!PKY=-hA|6A7Dp?34BVMM?y6^R~yzi1`d_=8}Eb??2N|x(adzvU9`bk72BI zdMozGr@TzfmHdXehj`Jj!CQ75CRJ6xz%Pq-?782qPzKoqn_O7(Jl=Nb7GE{`x(L`%)0n` zb9R&{s76hYHp?!8qRdsJp$bt!wD1HuKk7)nJW!(~Fj|2rAL4(`jVbb5K#C%t;dc^!#0VYbaPgj5RCV-93%hgEN;9yU~(}%A2TA%h>dyr^hR`~JdmYY$@ z#PnoXVJUI9H)WcK2ubE7BDhOW5SVt^ZngTOx?_nt{8ua_iGbM>|97u&E&M&@5FE|= zEr24XF$pH}!n}!=-uy&7I2Jp6xaN7EWo`Q0V==~uFHTjBeI#(@tPJB(?6;!;ge!^W zb32q?L9Zk(`(fbO4B*g>*!(CaJj@zv77kCi2a7Xun|=Y3TBjSa(n3w%9PmUS|TAcMR`$u>+-m} z?d&mBcn8Y|bl=oS&myg!6^Ew;% zPtSJW%5+5^F2a+zVgYRR2-LFJ-y8*&XW5*XtFDkV`RuoS-- zQ1qLeeA>}*2Gi1LGyy)B>PibGz>gjmGZ|NyYPSK0k&Dldiw{H}1*oI1Fm$bx-BpbO z!X#~BRhPqaxdeX(`VG8QhLV;n*u-E|;40V^?Df0*{cZh0(yapjDHBhDFFLQ{v;Js< z%F7BwtAv)bQ_1_MW8SnA*X~i5jR`Aq&vQ$vr%g#a`|Gl0w4i~>Rf2&>^j;Qo1RRsW z3z|@3jMH(1#un+ry|p8m9et(lb)wA0qz=cx&frLI?PSfJ9@uLO3iTzAD4*VDumIEK zz`U}V#drmOOadrwj!XC+*?Vhou3W!#2+I6t8{LNe(jzu@_DnMO%ZQ@&fR^l2?X6s) z)f3t07I{4hvC7|6ZBgEheH)SC>v-~MO<%*g}vb2yK13c ze|qq>?iicFkCfXFgSb3>0x+RIrqIkE7o^@9P8JKk2N5y zY4r(GHMPm^NkHUZ|M+xLJ1*#85$iL?&}j&PqN}U&wkTOiFHNIWwt{%wkubC-w=Fn` zNxk;HQ$J2ubi)RJ)5Ni4Dl;)IZSfxJ~#4QxSz0wg5tn$*DO7<-RTi znXs!uPqtlMC$9m2fv)$u4ZKhs`}->3+nz~XTF>13ap{FV?c`1T0qK6iU(~d$E{D{e>Bu-|ffsmVoJGg{s~*vx`8h>#o~2pbx<_!P7J-QgG08{C+O_ zd%rutFSnkzGQ8_z_?Yk7qUnme^*3B`DNoqeI$UvuU6h6^uC|M^aK*KDQ68>XViy(R zitpM*Ww_!xyQm6ReBUmr!xcB$1*cE{&@O7cdzb!{t9ik+2_Y5V#&y0+d$tF6>uS|kvu*yM*Db)iOa##85f6eTNrD}9nl1vCj0K@FuAiDE9Kd3NyTtVXf zg-xDZ{!II8ja2zon=LUT9nZ_PU?oG&v`^K_lOpp4IjoZIf^AKkS}f^jQ-l8T^@dg% zt|P^~+`sWS<`~~t{MPLgocG0&{A$T6Js)+b zH?6_o+MuWKZOoE-Y^}}?yG){eV_VlI@DD$i47wh&i_)O$F}o-Wx>&i%tMZ`hNxP^B zx^~z_WzhA!T~q~KJME%6=z7I2QbAY2E^30V*SPTR{oI6kLDw!Wg;U;_Op5~IFFhZZ z$--8;R3UeO5#VDq8Gj{Uj&TuveETL*ctC9j&lN1yOjEEipH0>3B2IzNEah6eXE_Sk z9B#!Q-TdLhOWqp>&e@d{C0qSZ=Wj4L5ZwV*hh&}6M+9+W|I_klnd}7Nxq|1~P=$OI zz60E7W^8{3P7nNG{GRG~KYooden)*+0Bd19vhR%V3gta>Ki}#zm@Q^bq}}e*WG5 z-VLe%2HW~Bf0cvexfzuhgG&cTtL};AFW!1P3q$~zs%`=xJrmc%`bvDH%VbC96+1`m z(g7>p-#Kbdcd&EZ4{TBm0J~-aUn)}M_WD3bxGw!ydwuwpwdoL~Tp6+}0kBXOziPI; zQu?KmLSvN%f$7SgQECLl!%`{d(|yhUBKBNVj!r{V%s!@QBe~Q{P-n%?*Td$;K=?M9 zNR@uKPO$=&-BSKq@>2FF6 zTDZYHcph7ar+17Kn-^hk!``SU95FxK*+#W}Z8IetnWnH)f zZNpKfX||cBax^+lv1HW_`9~FqIT2<9> zcNtY@SfA(LFoun5OVgk9cfPgMZe$eQR{7&Ti73b?N>M*}zLp%;{=tGTk=qE;=I**r zyXWw!0Qq-PC780mGZ!jE14}}kgQ!qX;U)?~V^$&!q3obP)&82iUiOvSp`UDO3qJs< zI6$}eq@L7IP3}3USIw^K;gn=xd6{v^SW{xEG592EL|wz*X2yh5G_cklK@~xBX?T8l z``$JHdmUQO6wD|KFRjd%L3sr)GB!S&3MA#78~1#XV> zPdUWPKF%9*PdABPX4^Z%PX||)hl>w>Cs=&IJNYXsNS!=-&(V)3wkp^3=L6X1bV!c1 zy=Z`868DwDcYNCQis3ScG6OTVpw__vuBPHh;fov5$L6v&D!*TU?EITp7`>IFmnQl) zFz>-;PHbA1KKQ+PLHdCAqSZ2>ZeJW}G4&PZzrnYM177moT;nQ2@(We)2IgP zNXRFj413ySdHAHC2Q7I{W8k+epH1B&?$IVW!?Lq?TBn#Ur~gxzS#UkzrOFzgYoD~F zE>?Nyd#C#}36#5Z31(LlTejtCSq*$b1s9-G22T$1)I^I#RaBi z4$hjFJyRf&{R=c{q}7PO*(pJKJ$>$g5H+R>SCRZqHFJ1Y3U$JlJu z{>E+q23^VnxLQt^S?$nQcscAU1Frlrq6$izxZ^w ziyi}XF3rbuc9J&-82#gGoaC7*5IHkigj%P$MEs7FXXNaJzb17X@0Ff5hhQPkX;Gt# z3bD(G&ZiW$UuPCZFNnNpD;>6=&0a&I1V%E>%0_v{pxt%=m!U3bWut_8j~!Y7h@y-AF-x zZ6jYMr~b)#bt4z!-F%f|j=pi>&HnCjqz{YiZNJ~aGzB>pOSCX+ux{s|0f{;br(W$S zA;^n&ntwN-CIRyO6P7;*_qKvA_0K^;AD|}^=E;KQH>2~4@v`t#^CzFl<4?La?&D8x zzl!fR_di326Z?~E2K_Zt$D?RbT#HrkBC;^t4A5cHj-s;v<3ysM#Q2To+S>unIZC%0 z7-`K&@7}u%{+_o&tD_!5u#Mv`*hX(G%ne4NUNvQPO55D+b!BH=PfC`_**Le^6`D7yAJqglry#-C+x-63z>9`uEyv&xzc+D zNywLPm+lQ`$fYIj9E(#2@)Pn-+`|)~7F3Yv^PgniG?yxz>*=0Z7DfN~AzCW;gx1W7 zlzBn_!oC!(SmP6^x~@+*n9j$b+^rhiGM=0ro?6Wnv7A|S<|0z7ckk@fxp4zK!RHn= z&1sPc*c4#6p7CE}0O|#=^Ui-=%cQP5mzuqMH;PiVP)dPDNBOCIP`F`{w({IK4?8A! z8en@_2JHl{;$ikY^!`71(7P-i2Q;cufvb*Hb~-SkyHFe21tLcGWB9`8{wA}5(P+K2 z@rBdp{QI>@$aFBPAiyO843g61H!lPD25Gt*-$97}EqH@Pw4yh7r}g6|=<^`fxe#qO zHHb$*ThIn7c;+*MXKdiDlL_RBN%)LrgM;rymwWfNq^u)6!Wj(}GCC~)jkZu7$cU(k z)(D_$3kG*8yXJSunB)g6;V^6To683{WoCS~fMbQmh;Zj(>*mGd;Qs?yU)=|mjW&U` zT67vcU^mxZ1`Y2+VM=KBKz@Gqf1t_#m1e$E7i;EYsL+6B9)~WQ@P>C+QWyL70^dYC zvmZN#@rVLiLo?rqPP!uuQ6C}3ILCrv5L;{)8XtVOAN`nV+Aj|X!p{#3s#JfJ?D2p_ zam1B}FH15rKy;SjGUyY*fQqeT@irxSsi3d2u;pM+wScutn%)6<3O8lUGS8Qw+Is*U zL=u4kDmi6Q(#yR7uVY;`Us#%&9>a$1wy|+Gb(<&*CK4#dj%sdU&Ly%}S3hgQWQpl( z)q&0nmZfe{aXA=MQSPBqoKyp|soV8_5S+!(4lr=ub<21+VBZY(TD_ka++$$Zs?+{J z*tcVH{0*a`bI}yX73f=HYhk2qAJung+SXB{2*`@sm(WRXHfc6|q>12EjOn#stma+w zMXNA66X31#XeXL;X={q*zB# zTKiZvHVDX6n3nEUuy7A`8=GT3};m_RGkw%M1!h=C;VNBQFro&@|xh}|0NjL%tR8;_ONg- zB3B{U;O`z2=YaDKr7D7(1r^UR^HIjdcAy*U=Nm3erhCE*fS>uNO5HDigF@-&Aid-d zic}Oflgxkwlf}*A!(fYc<%!4d;Sz^ydV{|xIpP5^{(cD?D0`D%jkNC2;y_k~mphC5 z-kai&^+NH@_SckG#dubp2401Nrf_E_U`oPMdi0~m_wYEM^uyZ~M*_7JgM;e0c-`x6 zm)`bG0#zKw0Kag@uiur|Qj8tU%9B}d?P7Q!@8SZtYi4inNVbC0No zrkg2iN8j}sZ7C0Z5r$@41XRfEQr`h4&n*O#WmX*@TKZG?nJR5$$!$HPYz%CW1Wk4X zHc$$0FxD0{5slPllpr$IUlWR&{oZ8AO^vm&%=rE65chFN1*F>G?{ul0u|XOMs>ycsgV2`B(F<*(LNbTch z(V~~#?sU0c8W=0No%fMFR6~e;OD~VWzaJumcTa$?`F}0Ns}~w&ixP~>2wvJmSwdX# zBE9d}i;!R@R#vIwYF{TsYW+AZY+;44tP2*fFt9IKcy}xH^ zG1k!^U>U0fxG)}H81uv#IN;120mZ$)Ql(86<>6S9tn6MER#ct_!% z2o-(cPK&+ytF5y11b@jYMr1O(`n)7u66;mcX;i6;%dR-mBF^y&B}Z3!zar@iyBFA2 zMkWs~J{FTR9q-3nj>?qbA*Rof0iRI!oL04dPv$3h+W$*}!x%n92*rQtJ(Qzej|b!B zVY}u*`6YM~l- zv;Ok)rNOkM=EFZC)hZP8>Cwm_$;!MMZE9fMJ ziq?y2Kjid&ky43FKZ}l5{E5*&X>Ca3EIyYxgcIgOziY+Ds}^Rd+i8|d82Rj@ z$oz{#P6e$l)*FhK`WNS$rof}xzDe&N;5D3EM&R0{q*+6Reb<=|!gKax0+yj7oL)`} z#{%Ta!gQQ#@i_TlsPOtfCr2KOPFIHCQb1R02kE)LRf_Obu=Y{L1+*$u@6*V~^V}#W zy3}MBaoj!?WoX5NeuWN%Dr2#p)yVmba_B2~uF}S2WkmIDC>w=Mdb|$GGebXfIgB^C&UEPM#F`#UQ7+-=FpYtwoeM4>J9=a7uZ2 z1mc_&au8u66zo4=JPE5tqLK&oPAaw$dAUHQqAone3NC;~`sfS|{ge4KOywxD|G(&L^f zE`C16R691pjvZz{L8rX#;ks?nk&r2_w`fCXhRwH|y<#djdRwl@;5_>b8MIZS#$*3U zWJqjpc=nP?*&R+E-H=%S;;6(!-r8yXbq_Uc&%9Q*?Ja^J7m&(0NH5k`*L4FUbqc;l z#uOOIl8+{Bt%lTvJ-C;2Oty_HWE%Lm7>AQ-Psa8hqpo!TgIVkBUW%pVT=uYzW*!hu z$42W^voYa;wv{I08-A(0Zd=2~tN$LJ)SGPBnEoqWVYf6rtT>pGuk%50db0O%v!9{| z$6J-qk}~CFs&1YUa&H*nP#limz+Fu5c;UXglYRMLzJFhROS`et*t1W{lJ|}YKU2z0 zb$DpQjEc+~qx4c8z1eW|LG%k0I?^@)l5ZzZ3$ev<-tnwd{>*plp7ZyNyX>N1bht2; zKl@#O&)CcUy+4(2!aa^zMOW^xnuRM#d{~Jw;lgD8?0$a_L+0=3cB#4jI?YpY-eAAg zic6c98n(&*(};j|;@|bxJNvkO=h%%%q8tsglg?E*AWjSWOe*(qW~$@}%!;Lq-!f{H zRl8pNvD#HZzhgigNqJX9akEaAS>d~whtt}jNXYHfVk$f#c7 zFm2%?*S|IX)%t4PE9XeRv-Dedgua4LC*;qV!_UMZGJ9!zt8U+!KdXHEOWLF>M^YOb z3V}N&eL|SNbLvrhb3NFomg4sOJ^_9^#}kpBNG4?qHK?uHBueaoCiR7@6r1Bh?_un2 zmIX29n7ljpT(I~~reJK+TfvuU3!vq7w=vS-NapcF>`8Z`;UO=}`r(p>^tCOSzlW{Y z&I)I+mqty)6X{R%txu#!`>!T~87cq85nQr}yt(GB9(m&2ZLk-A)e9v`)YLsp?aS8( zPmsv{$Ow$k17R;G``rqqU~D>vpyt=3V#@j3PCe7RKIKvNURfUK1*!IhQ%g9-oL zQI7Gnd7_LlG-wQ`FTj5~3%glM?=m3|&FWa-HHVI?K5mftQzX-_YN3;#E4KCS@A&F( zNH#p3KBwWZ%>Uq)G%Q+h%~SMO5FXl2Z}HzFP2(JBK>szyTS3;BqSF`+nUUldd zISd?tP!h~vz_GY-=cDtdTXdM?FBt|7a>!ZZdbm`0j#)#O0`Md0eJkx~st#w~H%p|P zTuq8YI_ZC_hS5(#7b%S?a>5KKsEuP(4D z?4ibgh0U*+O%p94fOq{Cgg@$&2H?MMOON!Q7$K{mh{g%#gzsdb6uf|`;B$C>*obsU51b#(#RteZnoGJG)@+2siWlmcbzR_>4 zPQ*f%^qW%&|F1-~ZUrbnlrUg(cBTtZI;ct&{_HBso+tn_Qgo4Ll7)>b5g5m+EtdcG z#E2-(uRsjcty!m#L1lv#|UMdHk+!t1-*zW;mvw3|`<(IWk!hUL|K$ZIS@ujw&ZN z4tIVG{2Dd~yAIuQ)k=fkX7IbyO|BeF({QCKX#X+wwy(d9zri`$oVYC1ZZH-FqpgKg zMIjnibi3tmHubdWEB#}=VSF`Mq-e_>5j0+Cf9)O0;&Fhi;`X(GPsxGlgTrhbH#^lj zU@w^y*;P0MNv_+LpH?39Z0`^D_%9~BpKbB3or*HyEFBa!cF87l_(Uld-`_hb^JI7u z8@_D>xm2P+EwZCE;n}nEQ@1gNf0sOtn&?rQ1!Qu*mgF01lY&*ocpB|F1u%<#?&>Vv88 zc=zt7?s}Ygk@JF5yJymKo?kkGYyZV$I>~j?-%!fdxed=|HjYe#YSsu(Ht|W6p*!d)POzj;VHqBOpwf`Ii>k*(ig ${vn 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�fot6)>cT>LU9EU;B_-%1t z)uW;Vx8>G*?pMk@Kisbg-se7Rjwp^}?{<*6bh;5|S9#X&zBnzkT6}nMrcpC;GJH37irHl(6`2cMNUko;L zsw(hqv$qEt_#K?27NjF?Es9augdJTsf*xMA=mMJ|t`eP7)LcF)`j39bRPrAeDi-5) z#VtuTcVDC_nvOMs198H`)Erg2wHF5R4>GXvm{9DC4Vb?*anK^6BrSGG>G*5;wP;gE zO!X^KzF1Ap{BN4PGtBcz@)+4LE!%B*0e-UOqL04GlmU+_CV@|KlC@ne*%AFOiWGVJd!c6aDu|Dx*c*% zTZJGv#xK}@9D+)b(zxdk?VlSpD*5Zx$$xO`RZhgA>*Qg79N1A^J~VR3wZ?qe>%l&A7ke#C@zMy z_?oyBeCiRN?+TeW^@Le253`uwr2weJ&rRtP@^h>CJe(Xw>y=i5g+u4Hq5fWT)wc3T z7z_BJb7rRk^s7)9V7QIRk$*;2eQ)BL-O4vmPC~i|qXsS9U^T!V>Z`$U*&q?oMzWyR_L93Aa++NO`K(JyuQYoj=sC zkYI!)2En+5zK&diSudkf#Xh0)W|#ufX_zR^hgPc; z7y8$$WMa)Bi8W^z_P!J!{YYZ%Bi>mhbG0|_+yk(Qm45<0exv0)hTnuZujnBZLJQjq zyrdTRL|2`vomK)~W!LT2p$9a}gjmeoy@3z>4peNW@z0N8G6R0ZOm04IM zociaK`UmwJqD_3}-h6#uB7FcLuqT{=l|Vz*l;~~-nS5rJKLS*(Y8@$l<*@NL`iR$%vho-Nq5UT^iYl$w;y%s!!rBHw*d;^+8e;oHF%$pUT0GmA&+dg*vo zb5>23XDq_5}NK_pysJxxInF%`>U)189&FYtO+f`s||2<%KvJDDHic zDK@wMEoTXn4r1wksR!JiPUG%)okmWL-U^3xvuKP1y^2Au#p#bGw&s5jLpN{(?T@|w zMvC_4AiQ3H4K>AwpE12rh~HV@yZmg4K45E3aUt8XBNh2 zSQ2B6G~;L!Hh5Fy+tV}zG%UIZ?wsL>{$)@>-)pM3!yT7BrC*ZT!M2)vfC;I-y}OC# zdR$`FI^obBLJPi*IF$X@q2-`Egg173Snw{+xrnRkZ?tDB?3#eqF~tF;m}YE*9iW6* zECwzmdOc!@4tv>m)q;&TNk+h1#3{5;c|bRXMzVTQ7c!GoP(yZqKoy-SyJZs&7ld>2 z=5S7ys@Nas0g2|}TA5l%j2bzWaITS;go2yH+~N|-T~3aHEm8jM`9S?v`M*hiQXR^I z&$Exu&UR7bMK%#=u>#FUdh#6=I++~dutxVItu8g&@h0*@l>W-=n&@qPQQd7nC0Dfb zine>mIn>@Wa7#pV=BLf}YEhPGzn$~}IjZ~_VDozS+b>TKESL%)bnZ)Q`-q0?m0&x! zb{I692+r@w=f1OdaNzg*+B2tLtpum`UWNu7w%56lIy4@9BzxF2RoL4Zf?D5Fy0o@! zRQ;u8ORK+gxjzfXiK%(@XOu2IU~y~xyt2hp!hGJ87;cXnW8`15#V#;n@z|#y~F-1S+Z5{Y^NVlcl zc^F~Eyn~U)Ik)6*Oiwp#t~B+Fw*_aL$vwxkdKmUI%Obzgb}6_!gR z-Yfti5%qGu2zDLbNXS?1gq?`s4`EMT#86z2zPfZU^;PQu7Io;LL*UPrT(wb~m4f;w z+dj_Q^Yw41zAnYWv@)D=dNau!^Crvl(n$O&;uq6fAeBg^$3+zf7=KOWE*OQs^wnj9 zZ9n8ej{4_P7x-tWxB7c}34N^85aaD6|GvII5;{4-K%+I<3_)A1C2wX7AIei>Ln8!U zu8d{B%~(oiEU=5L`LnOv#;=UAOG0*ezN+MbuhN$2(9o*kY>YcN>*1s)LaxDo-c0#Z zOySvywNcF9YoMMLc-7%hZyX%xKwzz~$k5xm{50yM-2HR+bsA`H4s(AhbG`;Z3#e^6 zwa((V&Z^LG>K}tVI~+F>;;bho;8@ZmTRG1BBy5j^#V+D#Sjv-OI|2X+zQ9vHyzw&w z8NUXOrj4cSW(9e$Ki4GFyUt170_4Xrr)wCNGG=}iru7xoPZmBsuq>Sat;spqx>k6H z4nI%iSC8h7Q^Ja$HrQ!f^qY>#C0B`mjWc=>^MKG}>D{FZ4>!tq@`Yndy9$w3dE~A;{}PXhuH^l?PGMq>_q3W3NQaW7;iW~(Rnsa zG+dDATFF-Gy^DUT0S9(8UC?=RfBQqg;kALuP6+acxKX9eUKrbq_!Q{yTs`)m9v6)k>86DN*?? z6fv|tDvS~yC|wyrjJ;IVNFsUQl4~Xf8I%fCWm`T$9@g&JM2HVnj6WpIkGp&?4{5co{Or|Dy>2%wf)SP-Y;3t+pNp~_Wy7-q!%byi$i?KYuvZ9EKdzirq zk>YP&Su(hTl7{5Wg3;dgL}v>{(^tn1@X~Xr3d_wqb!kVhYRAySMuV0_Lng(exwQ~r zj6iS%c8@T?(acfaXQLk9>R?7S)^BOMoLXb9wsjI6`Yll*s~ng#x_TwqCOyOC9!P`! z%GTypEV|BB+(5;LQZWh%T`B{}1F0vwRkHe(1zer^fuMJNal{)cyRHBJ$jrB6>3_Vh z2hk3A-)kQ8zPF`%7dImNdb34u-){HTmC^9CZW}9QPrx6I&*MxH40iKr_;WpW=A+pf zo%y(%7^{yXO7|1VUGcx1inEwj`HehGBaWsP4DCnufe&4crX1i2(pR;>V~$Aduvv%p zT622^xXqsD@a3m9WB(SLUk)QoNm!g-H>6Z&5K06RDL3rGdla(2y3Eg z@(K5HaVl9f^O>i6Earv=mBj`5H~AHd~LjXT1rv7X9RUo+vA=RK|Lrlf>>#_k5a94L%H-}vbdMmTG%@uV&yRiKQVWU8W8n!=suDwZ&+ zn2;wX9Bb7iSLnl@G10Y{(7tPNEi_B@xa_8|`Lnz7NHG>6C_avDxZM@~_V3W!u#Kkz z9>#`fIC6U$9=+2fdZ`Ye`}l~&B&Ur@|0CvwdYKOro!=p&xjE?(GdCO72Nas?oiWB= zWvXhVwJ{jb_B9vBu_?@)w|B*~(JUn`KvDvZ1Q77m*vbN6T2=ZXpxmnRdzMo&w6LJp z@8bR}Bc0w;WP{F(EvWA2mSe4F)0(T`_u9nHkl@Cdi`@&qf~@k+p&WuSuD-1fZZW|i zra9L<#%h3Id!ie#6V5g7)5F}_(=2yUS%yRFPint#?NPnL>9e%K>Ks)4)>Z`#PP8gG zRjrev+`M-D#=r)Km{5?Bk2CRTt%p!SX+9jM(C^`Jd>J?p-$v`E>p?&{j8#$Jcy0Y2 zB@?vX3Vitzl*na>=^NPrMDUY>P=abyYoP>H?FOI~2`b0bMREs&{_c=y)%{qxbzc|_ z#(@IIdCeuI`JvN@bADtp2GFqbP(cKQC_oTA%0yM=f;-pU7NPxe=&J5DD8` zzzsb|wz?SO&ZV>g7D3r)KJx3l(cJ$-qj@;hB41;z`~}iNe0Iv&UPDpplQKv-hj{tr zOf#Kym2wPU_4mGm-Eddqz{RmHUR2TAH!?LE%Fwz4e|B?4Ls96~PpRki^DXr-oJkD! z*wgF`->L64`b9)A@d{B`1S^_G%j`Wx4ILlsrQ*c0$%F;Pt`LgbAeNpJNy>H)tzNY6 z+qz~{&vlq*B$Yz0*dYyH_j4q0#=E8@Y88lU!Ht&=*L~&4TldxWD~C7?e3mONpxnx& zDdSfOT3r%^F=`L#l*d}@(MCUv%NW)EMS9@v@yDb$#xfn|0Hx&T-S8Ln#QEh=8oT0APftE}w}EztMN?%;ug`J-N6 z?C3l=4Z`XfM$BZ$BG#|1&}`4LZ9X03*0gY5isxu+NZ zY_K%u!y-M_a9Cbc*VBvO4vc2k%Roj2wSbmzplL~Vle0%b;~fdbx$f5*`xV=zIcplC zKjO~3?w2w|xh7$~ZI`==+72j*HPc4p;I->k#*^@VzAr%9buf1seQ%dpYZYnSee2R@ z+dm_g9vs8z`4dKDri@JYkIFXDd0Q{#OM7<~8%D=BRLQI11gOQp(lI_gSRcw&^WqPP_IiM+ zAp3#XjjeauAadoA2p?J~-=JAn!Jj2)sed-z8219hp)#)uTidA2jATLf8cD~i zk0V&nt@&-bvX#?iTFLp`39ykT~?+B=DVF+Z1h%snR&wd z!d$_N$;DZ;nG`gQu|rQbBW_dH5Il3TsTdkoOh0i<<~!23f3jnbZDW@6f`zs*TFe9} z5=-GO{^}JGqq`f!qJ__RyRt`$nB0H~{)!O(?Z1fN|BKLR@I%Li5laqv@u1_y@sJmb zy*(hrs;coJas1~URHZFi_M4j0c7Jl1G@gB3Bnlt4>6IN3jh`yn9FpUl96V>{;4%C7 zlzj7t4V7v_G(8 z9p-MMW38RHP)hYxg-d;pIx9lS-kz&Z=~Khy_=a41*Rb^VFnv)j{j0fj@yQpMt?n}cL{GHDZYwr zgzL+*VP(k>$|sa!0*#tUzcB_vl9wUXFg>=g&OUai30eR^U>E#GzPw; zcIKvcAJX<2e?Na=xp$VmOL|s`e@5Ipt5nAaXO$(^PQst{NH)Xhs0C2VNbJnL$wl1i zX8|blCzbdWrHDc_TBauNq6sEqIkzw4hnUKGy_!?+EA@4T!8}zI+W4AprIn=krg?V{ z1bS1-5GJmWHa#p5=ANSaV|Fg&(+$GS{fvkt01{Hg-lB@c182tjrq=kcdA^M8tPGUr7*CX=iX;Er{i*6XGf)mozYRK87K}>G#87R6T4Z7`ATq z)Fu}uNqRs>8zvn7l>ip4!Vw;62$r(-{O&^p6}5pc=&a-u9?|G7L}!UwvXdq|bAl^Q z=7v>?x1^MVr~b)~XeLXPEa;zXrznObdwpcGwbe_KkGF_19&c=({Jz&Z;r$Fp5ih3Fq@xq>*cf9Ct7WhZ{_bsyx!kv0Shpys`it{x_P^hfS``|GM&l$T*4hLL42%|40FV@ZoTU_ zXU(Jh3exnCU^^cn6F?U8QBIJM-S4gBQ%8I{HMKyIACN!Ryjjv` z()!@IS&m00)^3Gd7TbI_ChlMn9oT)p>7+&N$zESlHEsU*1=ptRM}h59^N7_|re8SS zh6MDy!yG7D`oNaVDjQXPa%X@?`1HOwFEd71Kw3zWMZ`iM-J*i4QNm?nQGE5$x8z_D zu6Z&zwLJZ5Ej%y+H+6JWTZsG*_zI=v7y`8{FuPdZ_&YI@} z(R9O-1RRDE&3|$xJ!T23teb5CncT`p+kdJy2`{heKZj*XC_^`H;V0d&l`I^STsOk8Y9E|>@ja&#s^FH68OA=9$1|;4Q1x zHi-s^1?jD>^|jP~3StYoVM6(x?z)c%IxrO!|CEMoD$$pWBWmg%#{S8*fn263>r6gM z&}P)oIFcbZy1+#SWVlQ3Ngg>w_-CP25f=YVptzDUs_I(feycbxk5_kQRDUFrv;UQl;(N3VXW5nQm~LN!StsRO(`&qNd!lG>kJcnj8EKJ+ zy5wREo|HHx)z_%{n}#&J0-=#=vrD8ijPAVW%R*UBhfGG93WuQqW8iY`(}HiOnOMqyfFjl?{ox)n?ZgwCm1`2>#0wB(~m`|j`4@UMruBArN76Sd5weoa74 z;MZ7Jvpe_`0WRE+59idS)Dyz8PZiVhs<=#5G&*GDre##cu|rj48_DQ4`~1E2Znm@3 zIx1Aq;O~^COT@eQ=Jt{N)M78T9Q>l%e3?(;cQ+6QS79=BTUbqt;Qob!8)X#^Q_cTn zs&;V8ZQ3}~(y=n_&OLKXB{!*(@lhoM_n7=^M&kod=ioaYz`)($xEOWzk5F?4(+Jh7 z5X7NsbH>(={dCW$d-%M%L1|qoBuM!wp7i5(!d7SS&(jN`2bbzC=?xX>7vA?uPUewYI~W7Ee_$zlZ;@}o4K;WW-sa$ zzr*+7!uJI!3Fz_Yy~*evLBs&kuVUi{?#7GEm&}c|W7BT##d*Gy9ym7Y5YuW318&gN zCKhv4_B@7#lU@rA$+x>S^Aw_gnPJcte<|Li9^++ceKCD zB&ckgDSq|2IZTsul5VSR5mCiY6f;#ZxtW`u&QIJ=@>lBt^PO=={69rtYu{ z?S}p!$Lspjaf-?kVgOoeTPCd7K+>QwB_MfcChh(}`IwB)WiNu3jn8+GN1e!Sx{$FM z;x~S}6lCsbY%b(|(X{SP(z%l}Y2{V@f=L+H7l4CRx%%-O`h=K4VpXp<#!Yx8TE^eRpNs zpd5>jup?;f4ncJJ7Nc4yy@=8C%=%?A&rgV7C#Tv^%|JGcP$ zko0ODh7-4K%A7uUMc_Tc6*J%t@B09FRaN!Xm3(WCJ>g4Y+nwJ6x85e?dp%pCoAD%; z8$O=#o;Bwh75X)Blk5zY_70RsIOW`NkJs;QNvs)*kK7epVOJxxRwgIBXff zA8{H`3bVLB4EG1@70V0QqgJh}qEMo1JSh{l=qkkweC`&iwxcbo?KPo8LBn2?;)o(16}T&L@fo1@8!jshj@KX{EYVyAHKkQNRQD8 zuWmg{%DIggl|QwT5kA>(Oy2X3W(i6Kz=*v|7%$*B+HUR@uLx$qUYIIl+|<#;OS|=f zJ9&UsZ{F2|K?#I?tOKWcItikpJ>k?F7oJ&xot)0JV>I&sb6ohsPd$0~^DRyH~n? z9QxareSd7^Q8_uea)joU=27c;0wpJ&h3C8OyiT{)M)@}wL*}$r4sa2Yx=!P}qx}{( zvXzO>`{;@;_2rlg)-CS1EibAhlnnBsO1YX&(IMu;k?Fqjgxm}^mF<(OA{5~A>Z-(l z`lU(+A(i&haP_p$@CIpTn2;*Nh8pX!}!EuS$pGqXDse z4awk!Ly_e5_9FD=pHbTNa9bBtdVqhoHHcU$SHlh%*&D?*;JE>%ofR|GjwHLw1&OsM zBqgpsj&jZ_L54K7v2oAS&Vlg9RIG?5_=fU5-zG<0n6lJh3d9{XYoB>q? zw`0oU_16z9oRUeMT2TMjD-+r^x!1D=ez(uew=D#Ro^&!8&$HqT)wAuNtF)ul)Z>}y zrxtPRwtt{s1rlo}>%1eFf4Obme&M|ix2zvX-4XUjuZ1hU)uoq)>23eu0;k&Jq3}ws zsfmETQRE0If*)C{25=hvs?6Pzu>AWqvJyZ3Yb@(!OW`S;lc`q=4-+BT>3%cG26qJ= z1@0F3dQ0Jzx(U%dw`0Nym2F3=%5;zB%Ki&0{B{MnCy=ak_XH4uNBL7KDSBoeX6(GO znWd|uy`j9a?U&Tif&|J#MQFvANh4Q`4gcJZ`m@hqj`NcYyqd0wXILBfPr-qW>T5(f zkZuh@I%(0(FQbEvoLG zO;AKo$mG;|*`SoOE1BkBaNwL^G+kp5xfSWkf_q-3NK`(+m4_zamTZ9rMB{VMQ=||4 zwc9F6U6;$ragTETOyE7FDyCF=4D+}?BbURXd{#X8re#iawNa}I{BfwjD|kQ=M>#)C zdVVPB;V|j*VUj`Wn)!8@q}LTx;kKbtKM0dXhe>Y@B`pe*UKjVG)Xt%#FNI0soJx9c zC~2Zg>NHi#??`ue=^;mUlYSb#It)v_Vd%~F(q(1Tu(VG&G0`QDz1gw*P7%m2MF ze|p!*D}UEfKlR*6r?uTVx8thVjOlH+wO=)|rR^rygyy*`a-oW>HYSTaWAEKmL86u5 zJ_-FEABm`oELrv<(nAK?1ZGAwa-{ z21q1ILjwkyv_S$?+Csq!sI{UXpq$XkHRa@_-E0m7p@52t4~SK6^#MySwxk!Df)`4; zRS-cy-J6t$iqIA)&HMeW+54Oms6P7tzwg`6r#XAio>{YItu<@btXVU|2NJ?cK19H* zeSWX0(T_0>F$d#3b#zEV!@B~(L?WA#^BZsL9(qjCv8#6IUMZ$@5m0CEM#pWalQKc! zJXsT>5q=4xw7WA@qx}8|d$q&7gb?G$a9Qh2)79Cj*qP`k?q3FGY=0uXOno_#ii{t9 z&)MG`D5)7b=S%vU?G^XiG~kYd8Q{{DBiF^>~l#MVc_&$x}1d)C}{YSm}yb9~9#fi0xZ7{0j#BI)|ZF?5$Il3T% zwgtUZguBLj)R|yV%RO*gTGTcS1V*RI_y!f|6OVtrb_NY}ZE8c#LJ?sxg*KNvs@8Ang?Ml-b3!m2tkSx}N5Q^T*^`I6pXYlkyn zIxnG*H0uhf?kv24Twiu|>^|#Ms-#_v2$K%#IKSz}+rWa(OC{j+Z^^#8*>Rlr<@7?H z4e2!(6r4kye(x~!J7ClA6{nu(+Y0R{$)9hi6|0h&J~tq>Yfw<^9rMi2tHLjf6Ha|-{_!3=o#uj%Uy@$D zolM244q&WkiE2NQ+JmF1cxW?v$*z`slPUnwIm+)k_-fj>1uc;qC9a3g8qZ~2qQPxUZ<9)iRO)l8!}@^5$DIp2CAc9hGS5FnK9BeL;?o^rOR?_f7U(PjQiG z!Ar^cE1P#u&R^9q*!^VcOLC1AUwW2}Z|qFPU2<$e9&(Jd9J5|c50-LGDd?vMcgG)M z4_qg*fQLC(7EL~Wa!0cJ9$x4-ydOVxN_)~)ri(vtoS~aJgurI*x9Mj~0x&9?#ay4_B0H& z_qfa5j<&QIl#NnBNECpzoNb529yQBsQp;1=BVO3RW;k=JNfsWGu-)@CotE2++PyG2 z!P)M0l7x+{k^{C*U_}o^Od;?#E4vGoBgD6Z3O8d@Ifp3HH;QkNX5P~dc_X#mzra>( zYjrAQ>F8&y_!A1l6Tod!Ue{g~+KY7g@q|c!TdED^YZ`cAA6`^eUhdosj*|Hr? zb8aUk+5I;bm6h(@*vq>&=Vty=%aFk#R>-QI&qW#(YL^V@^R}Trr+@u3Ds}410@@y| z-%O|2;P$hqtpbAB46ZoE)nq0YXa}>NMC;_W$oI9{9rYIs91YK|K;qZYINZ~Ta6Jm- zUgFv$FNtnQqxqiSC9Q*VOmH6N z2vQJ_4o|;1T=H&Wcm+y_I{rALX5tzgD+*6eNGgsS{76$u9pXttXQIdV487 z{)CRUWM&R8U00S7CNqntezVL((6E!&^x*Jh&jmb$XYprd?@a1E9B>`HbzM0EL{8v| z>!RYiTX>aU=pHlh-sn6iHt@qWSa8|grihe*^FLb?)YVR2VvOm!31t-+=x9{m0A8kenCTas3 zCGrNTOi@wSapO{F0`MUY0kSq^nrXfRtc?JjKXpU_4Vn7)JFcnecBbG=FKzA^h|ere z26t;zXLt*|rp*KY74fSBUd=!?pW823MgEms!JxC+{fA{iWG5ASPjTTiM_JwQ-!QKE zv&kE7*Y7dO8?Mvu!O`!5TzAKN)AK}T^DDx68!y3i)phyck=`FTM?;rs*RIc=B5O?B zNGgOazh;OrB$TP~V(gR?oWEgz;B<`}$6d)ne43990-@~kQmK0=U zEQBx3$9BG<=eu}D1!;BDsscO*{?@L~RjKM^4^icxk9SqxgA`d6QIm_+M4G&(erfWV z`lZR1PLtnNer7TSbtsTpL7P^W!H=4CWmLznzzdq&s_Q&#J&Dhphg5%RH@3t|bXNg3 z&LR(pvYZ`fIi*Yk@=FC-ZjPXgi?f_=$CGU;$nv8o%cwYub|E?ZMc)n= zp0@cZbC4dHj*)dNV!cmxO8H>dF-LapzIK@K92<=X6GlOYu_i@v*n$NNh#>I@E{1m7 zf#wsm+ChuURM}=~U`@ulkG$*$X#(;^`gJv4Uh-Y!KPYjG=^XPgdwLa*vPBhgC<(IN z@gHk(?EE7|{IjcI=7N`GbDNwjc&YOpNe_xI8XG56uPrI^PpKAN&rnmph$@)}Oj9}O z>*6P%tEOCysIb|O*`(ql>38(I^hjwrbAbGdqx>IEpG3Of$$>Iz<(3{TK9r|M^pE6C z6rK@v&WWPg{{DQe*B09I$8#z$WX${=mb0e+#iA`~fv935s|a~Z3hj#RlKF|=&Zv?Z za|DknV*4QkH`#rXzGej5|AFwxG0o4&py;&@q+{)%?W%+#1V!5D_Xq0pKA zk`&ItdX461S21^Wa0V8kcYsh0g!1Ndxb`$}ru0%z_%!+A@uZn@Z}L9y*Jxgl?=cl| zkJ&BmG189{L>22Vb~2gpe?aB${&&kO@W7S#!K-PEi>@o?TKZ__B&QDZ@zR53@jn;>oG_$9^dH>Mc#02v8q$JAr3f`K_;iv7Y zl4$j`QnIJ8Noi(e@QD{GVGgt*GRO=P)SVpeh8%`Woni(<-$uzSW>8*ib0TQ#5BdEC z2yb5L4Q-;c9G-u`sd=(|0bUn}3jf@H~DXw5dfuJ&iZlQqC)^!oq?A>alSSu|)5(rD~zP*nB4t zmHwp01gLf6TsJ1@#7pHn{t%b%av{q5=Xv)Ytbs3{qq^}3Z!IakceI}$o(%67E0`s0 z7qV{NJVCpgGs7`D(`zmCutne_h6#IrZlgdl`qg(A7lkiqqT%-UZx&+c({@517t0qE zZdOlB_qwJh{uB*n(+)L6yC!`~^3E59%*U#jvb&7yE2AN`38Mg(dG!6X!z-9O>a z;$+9pH~e$7C%3y6Y2fPy#5%0z`QM0V60M`1-Tz@clUpya_4;$GX)YW^s`Bggw;wGH zm|w5Y#j7y${g?4UI;S$&zZv?P!R%CVBG~>NrRZb>DDEU&VL(dIzjS<$p#VGWGkmMQ#0hm3G90ew`@dK}`quA_Isei6Q4}y{t4KwMY86SdGW~P& zME9hLy*(kQmlp~(NRI<4MB-;os2PZg27XY$e;Td$M=ql&xpd5&V-9aS?KIA%rqOQc zdRXf0nRAXAb870Mu4Bfej!=-b<8NV>y(pa8s0hl=Kg8d8t|m`;f00&$ZHTaVBbRn~ zF0JdBBRX}t3>kBal(v3$*|3v3{ei!5A6dU-bUJ}S=l%7h^8^1ZHyXbR2nD04XJ@BZ zA3#jiksj?G^LB|T+dEIx#el&L(xul5&kLz#ROf8^Em^PH#R zi0J+Q1wX~jA|+%i{h0>x&eo+OczT)e9!3DQEvpKtjY{5z(?aG(wzX@}ITGmBpTmKm zbLAEMmXqTOZ>#w(>?k(DUz{?wOi3=PrwLR~e#R2Ju!QRE~-fccVOYlbRcFI61jL} z|2ThcG=0rE2!Lb$4*!OsP_;DZ_M0I&lMPtsgZ^>W?*6&+sM*A8?mwN2Qf$-Goy(!E?zS zu!mz?p~i@=u^fB6F54sbVd1Foi%1%uKf<~~hKQ8G{vOT!Y@;aLzzQTPR5tlOQR!|P6 z7^f(XaA4^^TGJe*N*i9_o{s~ef7iR5!lpAO z`YoTn(dc6s7@A&6eVP3OpCAj1dbjhT>)j(dpRiMaj-Wt#TS@2R`BI=aiqs{$xoU!AW5*5T9iQs}~{@iS^Nk8W{G z`(GjO>MybQN?mBHQIQ)}<19#D;gC@QHSkiyr2Vf@l6^ekYhJ~hYT z@=C3_8IFaH!Z+eXMiql!=>V)vueXVwHCZx6bDuufHCH z5oMe>=ls{x@m)uLjSmN-RCAxY9C{XllD$WINP#Gq(Fj|Q9jnnxyrFMtZm$VewTHA{ zjOitZLgx%pv=eK(i$2+9)`@QF!u6^*Gtf(*(%Xm~`z#<8dthqa7WUjAB+1m1Ijy=6 zgc}k1=%?JWApU{ExRY&TQm+abTeK0OE~6$FS2FD^bC|@rt^ZPg=Xw{yWD16{QH8Ff zP)^4L7u7mBzX4=i#nL0;dA!E-^!G|{BThu&HrAv%zWLW!tO$~D2ybz`NM@8iT&hDh!SWgJ29b=SfR{8AC>iV33L^RmIz9lzfU)wr%kb`Vx(X06_!UAmPkU^xpOFZ>8iVZ8!5C7SO2shASpEOl{ ziW4B%Q#5c`9-gb9m{H`vSU9Bb9GVXy+E0!XAgp7i0z#QCyg=b_`PQ_rqGL$h2n`sr zP|y2EA{eRK8aVrV*nhbYhF<`6%UrWtlHGrDBJs}y2lD+dlpl+8m-EBC2J_hf&|rQt z4b2Va6UphPbAx%**M^MeLCK-6kq0C6t`Z1e6e^}w@*p~BYWIg2Z5+~Ih3!(YbbRYXii>a*+W1g6?W zJUNCX(g6X@=kSivRhK<=I6AS$dvs=Ra#3+wJC%}i-vam}E4;7*i^98Znvrq@h?Fk3Obc!}jTcDsdB zC7b%kdJM|~tyg&8i_ZNQDpZFE}BlQ^lwB7VS++Wz3ga4oS6IU$z-~5R=f35s? z54;lj?-I7ZkV~hwXD>d&yOaw1eX=1T8IIfvszq&A#UY-x@~c_ zi7RRG1jLpnes<6fj`b4|G1K}rM;IPyqDi9c$y75n>;*UXU@HI9{CKf=9ZVm%IZAN% zb)`3TpiItXLO#Pk!tEx}w#CmQ&wh{F=gR3ldW)hKwZPoZ-*&lvR++xxwtv%=rUzg7!OyIrg0qhv0nQT z>)?7D)mhl2+z!&M>$`R5ax9rr&Kbknq|K(2bQ9FHlZj~KK}zW%)r7Lh-Y+|pdlR}J zC&jyE6K}PvydS;BuXl@rrQKY3H@*(+k*60M;^jv$@*90s8clXH>niN5vH3Gp-Z-$2 z<3lbFJZND`;Vy#KPI$L!%5S_6%=kttH@H^P2q1=gJG-C8)FGm-2h)H$E^?PXGw0gC zFLV40#;^+MwN-gjt8>}I2ElmfPyn(oppw%6+F!g81U&5TKKh*LA}Wgg#jolc07wt- zP@nw2-2ZUnzq9|LpWzCk{@?C@c(~AiJv`q3AUc0^|AW(+Lv}&*)jEA0`?PnqAEGhR ztN9fh3BW)^(2G7{3iSsCrrQm%XgT)SdO|flPbXzGrr$5oxu59*{{vT+i8Y6CY%k;6 zt86Q?xYfPgJ5fSzo7r&p&hF$1CCyc-hV=WxQ+3UUcm6lb=SpC?C9vvK!?|O+^GBpL z7jDA30X5%Z=b_CrrKZT$A%>4f`{d+6QE z!t+Jvq5cbA{G|egzrI=16ud6QZf?4NUO1^Y-k;H>p%{OWZydaxGz=} z>A!Y#S1S;?j|9d28M{$Iam(W_f!e{p;Pp53o$mb}!=bY4m}X+wFm~&US23$~kwco7 z(Iq%~cJuQL->oIYX&={Fvet&qM2qOd(7v5-UYi`Ed+{aR#=SA}Iza5RxboQBAAL4N z5A$BIZsC|Og{$qN=}oP^#$`|q6oAC1%3vGDxyCaiK@{9o38-E3 z?%VX5R(ZhL?;pWwir{R*Nkl+R)F8JWqup53*+FkkcApDfkLSb|_VL3g=(UR+$eA?` z{dS>`ZP7xLI`r%xUrOtnkycXTrnl$|@2&c!u^WR=V{vQvU7>{7hui$f1hArtJz;6< zBNO$)*}@iILfG<>&)Zkx(kMj3%cD(Aic-|%n>8EiNK8}9P&FT;Wp3233!Y_LArxvZ zq)^?!XP}4gYq@2)Rs2}zw-ADI|6>3zjeuiF2pO#u%97k_+$zU zaU~0i8PcFO3n&ANZMAbjcPDRM+4YB_?#Gs>?a9_KygjACxE^v6OV9Z>VExB8JEI-E>Bni^0&? zS?I4D4k>Ezav{Flmjk7IU$#(onaL)>KhyOFJ}CV5Xu=imq0mI8MrbVF2TpLv5|$vd zb~h(wIBQLBE8~t6c|mqCqyH2Sl&JF-_G05H%;tv|L?LYmZ)FYh&CQq{aGU`!0%W|S4)P=;DdB6*9oFG@z z^mudNLx&2IzgPD9u`t26{rBm2zmlB03jnx29$-a&d#Qh7!fP*;Eqfr1L$qHvdFgzU$owxDzwn8-7FA#TAIzc@H zT76OW7Qsk%2VjtX?<>iktvt-xQOjReIF%@Rzrur6AzHyE)iPdm@P z+tPRG?y-}#yM{L9S23McDEPOld!jbP}lGf z;-(oW`b8_6?0%I}mfw4{@KlPKz3Lb25grU}M>T#r_Pftz{aE)4Zvz)pS(YOK+n)#a|Mk3Owy(H{& zYx3C9pQ07hWViH($-QnOMbFerLN6q*Ze2efLm=7BqKB-LcX5=4$B;`5P)>iIIhyS# zh{oXi8zO(-AP^a-8{P7ABhG>Jdn1!Q&(i|-pWCjL?n&@bFDIVW#v)Yoa`Kc($?kGU z$rrZ??qJE9I^`cw9#&8%Z8_rMR66tlWPbIM~dogOyWseg@%N-a@?|J@N75 zA-0Z|bn>$?Sn@XQ`G!1vvAfSd&o=&|H2${!Y$*KX3e zXA50!4C`r!J8>g2<9K1TY3#a8zD&39FRL9gaqQSG+!Vo6IGH~hcO%pfLqwL3k5Esl z*4Ogbkx#rn52>IZXa`WQ-)p?kk?s|8FV+XUK8e4bhius}ZImi@8>KJ^)FRc5xrfzL zAKZvugJ1P)xtv_IJ_9eN_=kll&JNn7_3Fb~f+(A%ANJC?re;;&07xw2ln9rc`x@{H>ZB zn>=zrV>r<5w2Gm5xMh?G+uu?l)$dAH;i|jFTy-eT7fAN6 zF@GL!zth_||JH4cyP7k|zh{?9XilK@cbgL6Amg!xZ&Ew0(XSbwyxTk?*bA*Aj;&Rka z{*z3_EoP}Ec!IH2-RsW!@-&iN4RIwv-n&}hy z!=%p5XfZsw3-&Fz+6yk~-MR*-4J?99)yR;|ZJv?<6taUCg)J2F=538_Hg5rj3I*+o!m-is$$ejd2pWBR_!l zrhm4O8RG}p2Zrp2m}~nXtScshGv74COc8;g_+ZwFeyKAUy;Xfab+{VzVPx|J=SyAK zt(u)59jp&kD=InP<8;%?pBAvPw@raOsOKu4y$5W=gjuPc^@sksxeXJKA{^Pdy)*#4 z5=RLf66U>Q`25mB(LqX`Gv6tBsSdYmaMui(?2p?m)%lJQPI9I~b0iK%-}$WIM4<92 zDI7ulfKf`2c3`@joY)!Yi#`k*!O97gGx%bECF<9&@iXmEIXWMVNFJ!V`3;33favizFPq(rzPl$Y(;$-hm-YE1XxAkdPx9^mJHemT1lFO*XT&)72z?;0A8F^ zWH_hSZ~(DkOSZ%e(UPvw`Dq;BwpFMeS{3ZDiRCGUJY86)jmkf+qJOX~R^p#s6Wn*P z-HtuVpP(HGmHz2c#8M-pcvYZ&B^(l?g7M$NKzX>FvbC!@+);U2k;f$X6yC!Vd;y65 z^36_JQqN_IqwBjx>$ciACVK4l011mm8(sa!RtRQmS8ISIU}!6 zv|aZma>`OjmH87AO%qC!*My`Lzz~4oJ_@U#*-@ux#2baXAagQs7R#iA7v$5PTASV| zDQG%b^ARCECe|fMh=s4fmsXj#8-te|%lP+YY3oWHsmH^i$#PjV}wH z`z9pd?!w8{={2RfP!6NJldldyoafFf-gKrXPC+VeWPkMi`h(oVDzdIkg;Kr4I|_0c7ot%9D=qKK3QF^9K+R458<>^ zNxB(CiQtqYSnJ@B;Ldh`UvO-8_I(T2z))w?+SHf4ODb#Edh5LNE4)hxvyFE4riiax z)3HQkjgQ`SY3Y zmNjvRQ7F@Us!fc#DTDH<_fJk_8esh(!!hOn#p7#-RY0PTpc ztF?8W4$c>9Q}sptZPwj0k6qnK;W~AJ=b|pK1X3)KlefRkrgU<=Mkgy@T2#A^_)qiY zTAgk)u1IrnL>ld$4GukAa$W~x2^u6L6q$u~i^C^8EE3Vo zy~il!THQXJ(6|?TVMR&N+Qhgblg_qM%GP_GWi+eVDBYtw*ZH*5G`Ec2(zWCFQ_RC_Q2-~bt`CdCaTlhK5sOox)!b4qXp1?n3fYj`2t zpXQNytk+i7@L1^Q=QG}_m9HOAyz0sKnjTMPe?`LtQX;;r1_DW3H%H>S9~7{#`SA1N zYb4AIqpzFziU6BDM>>Rxv<2AIQFwr!M9^?AN;AFy*`HMBUn{i-aY{LZe)g#1(S2-= z9vo!xt+*)OI%WD>@n7yfSc&8KQT}9MX*<;YlPYUhatwCyI*4EDUt@|Cbwdroa4@jY zbOsKM+I3CqQx&!Az40i`Mg@k)l~CS`XGuF(cb9$54KAB)cxlWRHT-h%a{0SIoa#xh z8HL@pnz|GI2^b%pd;7b>nmzp!5=|$RCcmqBIR2*(-}hkj={4feD6f3+5Q?zAVE4}? z6fVyICc?UiM(U};2hCckEsQCyk?UGMzB-3rynn_`f3;^>ONROfrnxaQzh4$as{Nx( ztT8%%`U*P#`cirA9aMiOPSI(C{0gF>rZb4GTcTmEPBz6MNWU8VL>J0Y88T9 z?;n+3(TaZAE!n1Jc01XFu3!vxyA2XS`1|dH9A`W_%IV*_omx`+_&03hx9Q2w=5)b+ zvh*^mymLVPMPhu#7{+*16t;G7DpEnaDsz_@Fh0t9L+q8c#bpgf9fplEm6i6bK+7GB^Z;lY_pCdH&FS*E$DoL*^pXVMvC`-;g z36I8L^?hAOxa|?W>6hPhUqK`n*ew9O?>w-(k^Pjoz2=H|&!PF3>PzYX>WJR! z_dM-fSsnh0je4rAUVJa@RD0)>4~wXETkz}zo8FF1f566?&(jNK^Pb4B-}6TbNg)Yg zF2*1&-(T!iO?251$R7jJI^YbB2&Rq}eI;QmhbB^#ly;>E;CJ17V!y*Ff{2|44obgP z)utQyt2d5YE2uDbPTFSyf}2`#F+0_!9W+b8e#$wP-YNiO{v_R&t``_?T*vj|o~HQe z)*+zn)j;;#3scdinx7>OiLiU9&0$xGXcJ6MQT-#wbp3AW`6|p$q{_BFn&dT^$&!JO zlk`zwPL9BQ(}Bsnf%a4SHszf*}Q=agK|<;tmR?A9R`3?S3UqTDZ#o7q*yy_$kmE~Ps2i0I!Uc0O^;uu^D-%ZnZ!#={L;cpd;Fpk7C9g(+4eXOQvg!U zZ>zV?s+P{vycDq1q&cdz_auJh*J$CloZohS%YqwUG&0pkJdL$D-|N}M8gYn5D1RHEU^#X-s!isK@8ECR z)K#&F<@N9KM6!DkUN14 z9rNq&RK_0wA-IlNi`t-+N2F+>=UB?6KVY-W0dPztctZ_>w&##}>HTC<;@c!<`bcKT zhp)EnwLNHd@LH56N(Wn%n9{?@a#Nf zIIc!c0gYhsL9!XISgIlR3*P#I-ST#SQj6e|*+z=+4?=}?XIH*kVd_f=DoU*AR#G9DghJj-RA?%AjjKIz&cd=^FsFE}tpxIqXT-TfXrp!pU>*;7qD z8n4kEc~+JBVt>zeMq{D2^1TDb(K$cPSL}Ijy(vGk3ulUqu!yuJN|_Zot7KiiwQ{;YU70O zN?|?v$z=9!cmd7WX`tf`c}j$HjMaLjTB8oXMiq(e{-{hkPjKo&JUKD7$QKUhgYcXo zJW+}}n9fjNeK?OZT_bjMeqJfm-3Y;Lo~sh?tMSd8HxT36t^Ld4B+BV?-0&|RE83(+ zX-vrsQ;xsLUJ>UY=)ir-TNMo*KZ!u4HiSA-R-amR!}^Aos&(Sxu;Ar^B}T( zUtfg3LD-yEQ4nV=d0OB{zRS%7daU20`9pR3)#A>*8eU+ofcH0^JV_totO0RO{ z`s*nj-^$}g7(dfT^Zog=`D^#)Ms!xA9+fmZf&@2n@_59a7JYkJ-$W^7X12b`^~QU@ z>L}U?HjDQq{a+&F^eiOs7ph(1o^+jF)%B3WBPfq>2o-M^;i-C~GczO+&rrtD(gL<_ zeG^WM;;U1NzKt(1RgU0u_eKI|L>fXlfVO+zlhk=8fv!;veIeH`4DO_D`DU3b{htz> z{u%ctR`;kyq^vZ8$rQ5#dSz%bT$LQjz-ChQwm-7YplV9V9#!*cRa4`tIh(UaOS6}kGA31o-Q>AKM0cw%9?ctq{X1mA= zPysIW%dAza%7sHCo2?N|o94Thc`|G`(UXOk`q@!iD+M9wExVI-&~^Wi4*C(4?ezMllyOkBHe;$8_RneFxg)> zg5{cASrh^yjSAS5z%zR@E-AT#3KlDQOg;>pg7JjQ42HbZh{PdOb9IED-WxW=1)M+e z!{6htW{(wuLk@NZuQ30I@%i8KfB5^($``^{{(tozzPf--GENqSf4G|W0+PA@-0V-U zp2V0o1rpRZ(r)rZd(Bg0UP-GuU-3O4KwKlz` zIo=8YT?}m&(X_sk$Uy)xe{xyLRF(6dL+vI8Cztw{RFEF-h7`s3OI7(CugAt09Dh7% z2u>*M81kLQEl~vmAKyQ$9nm_#a2%9s)v<*(I41_rC?$FVG)x2e<4yL@fY{PCA_f^S zy`0ZA&DFzN$#>_}%_=Loq;%jRv#+!OhDw-(Cq^CBZv`a^^IN6#QhGb;qt$MPt5?dQ z%BdA`EKXk`SMoA*Z8v0`q+*R!DhR=`G&=1((QaPBi>Z(=ME%f?dn>1HRftpd+7WUw zsda;UH;y><{I#ve4bpwNlMWTUulRC=L#qS(N|enI1tJ{{b#p89ClFyxL*{)0%nTG;mr(e&Ru&O8xUgK1 zQ`b;U9eJv~oc%-siPa?52(3@jEItO=z0A~MUE3}7YW*Z%ikziw$|(V4_3q~y> zy-f47_&dLGLxYdI#@$~~oB5lmjclruty|NIB)ZQy!h2ti>QKwORHKJqV%H~tq z>aZY;YOxlt!+Ue%A$byh?>wf*HdYhMP)3JdI;w?JT^NE^=fCT4vciw^IeRiX|A2ny zPb>A)0t37D@?th|;yFqBM?Yd36x&zR={y(v4_JO81a8Gdp;axGak;Cz`vlp&t*j&- zolVT88Mt(9dPJkTezp|?@9;CqimY7hZKX$Etak6?R-dl`T%xQ2R-6A;vN{_WrNI?Q zls~sp-6Myjy{hhDk2U5!#0#A49)<&hVpq_)J|#euj-U|#@V<34ay2uPqb}P`r7Ws< ztg_SRCp}^=L6YOpO`e40*WlmMJ_IqnVjB1sFJ8m5e|>^y=f@h$8i;OJ>kEdZUoLLy zsGgk~t;z-c7!U>b;JrJ{cAMYV-M8eolwbTff0nM}PqOvNWZSw3_ZEk{+}^WMd#-z- zGlI9?qYaomYU`!DSt}Bsi!i@?kws_Mh$#Br1H!8NEWR&OWoq6Wmnm_+D}=*yy;Z(3 z_!?i9Dx1oFpR#_B78DFkl3G`4IMFtuAL&~M3I~Y?!5aF1C5MN0pwzSkEHBO|p5-Md zSN+9}RE=6&yVm%mNxw#dTnMZH)sQHqpG$<~DN(LNt)c5*?Cp^F3EmYBb(IQEOPcDa zaowpVIA|4)Ts}npB@@GD<759{70CpZqu8kLGU;W zbcn%u(uRDyR9Mie&BSLaeG=)^oZ_4L38L|`JUdt3Z|bwV##gO09$BkY>TmE+P`;sb z!&7zkXNp?*;UCb|aWI zpc6fQ0Uw?fXFg90d9Kk| zpY9t>Zy0P^?OD0ePkB}{dEI|Qo_=dp5&!a>6-tZFb13bv>gV1J)em1sCbl&u=N=>2 zrZToa`EJDD_ye9*r>+3u*6`xie^8 z73ZO$d>lC>-rkek)Sl7d9u8Nw8z~Y~m4E(IAcyRHimJPAm_ffT^)sa{G+=h|uARFm z_SIgIfDT?LVqzy@h=f8q$b?~V)(*Cc;%CzlosP|g+}&kWXL+74u0Kg@oTafXFL&3> zlXaIPzi?dX`gL%X2xxRzELUvUtTiO_V~FaC4R&LQ?ebi%Am<)YyXy`y_5(+B%0YgK zp!$>7rKWmalDqycNuvJscS#!c&q;=$&&)v&zak&Do1^&FbNu-#Z=_Q9>^f%G)IJ2d zc$Tby$xomH!f(?qck)k#9h3TKVQvDE*i%5dEH$!xS3-$XY=dwuEi`8 zL-(YBtCP|3A_)XU54t9qJ7iCP?qTsB(1J{z!1rY&~{nf#*V)P`kd@_X_w04^4as~u7?Hko}fYl^X(Z9Uth6}XVW^$)) zz4#%gUA`)o_PniL&lwn6V91(634f^UPF@_ku2;-?M0B;wD4Y)KJ`RlYI&7C!++jx1 z@W_JlTQ#1BpOgeL4ODz%SwvG1k6LNIpzn^LuNaxao1sQjznWLoLSqVpXH2TP=YYOG zzsW%Fw$28rH#mHu_z>H1PS*MojwJ?ID4gVMPkl+N`aH_}GtAOWPA5cfO| zpQ~Bykv(4zE~l!4Q+H7JmRT`oR-L8Q4GsQVy?FTVHU42OP0{(oP7~OQ-KPL%{*Jj? zk5hl0rqZ`)EiOEn$C&=kG8vn*OhiA`fn`GD%gq>y1TMBtYF2Eg^9qKWzdw(7h)^%{ z8}(E$I<3#7pnF%y7&CsV2yb%I@WQdiXj7%Q`)N_o`%72iJW7C10s=(y=9-`tlZ(-= zhS)$IB%-ya2^Rj4*g8w{6 zDfZ^2OOpGeFF6bXQP0WqR$Jx{|EM((;Ad@=rM0;mDb;cbf@^uYFR7+vSSbn#6#J0g zE>=gPp0!k6P&3F>Tg{!{b$a|l)n@fO|K8yeZ+rLem-x}(5+yDHZZ%?l)o@dW)=oaRG)KKi`9JD~quDdnjZFQp5^|LKB?St@Ekt6D_L&l-o zqj6{pB_rvRJ2`v^iH_+Pa-e8j+SYSR*|nb6dz(ZON%}z?jVn1lflm7oz4szTgr*T& zC_}OvOW2HBB2@ritPPm|%1|eeWVo0NA02hWOujY5du??T-p(d$gOj75Su#{>Isl0@ zgYs}Zt^Rc8YIcD$l^E*%U96y43VM1lJSpG3dOw=^`$ID?602Q5`ZNs_{vt`fAMLu_ z$>eC3EcNx+sQKR=2DeP17E7eQ!r_$RJ2^QaXKCCor%@sgZ|ScMUIr78%~I01g8B_X zrSGFpT13ly{>yH1E|$z~0C$Y>%w!DbDxoKaaIYXlGl0K>TcKTY7q_shmUtj}wir+P zWBDGCm*VgR=#e*e!p~5$;B}q^%Sob>FZq||Y^sAlgygmGlU}AN=djeF$%oS#dS)Kd z(lztgmaf!S8~Wy~QN}{|ey`tM-AY=vG40>-8rcb5T$}Aq>ygQZQNragM(=;VT>8R^EpTZrCxH5UdH=0Cgq|EY{!LMg zTV+mRqSq_?E%Z%D6cU4Yl&Eg@+253)rh%SF-`rF#Tpmte%ujd)fTQ&Ec}mcx4RxDd zHI3RMqE!Y7ny320c{x{%d9BZ|ljAFNvU6%*H09vl+gMfLM3GGid$X{4=QlMa?~kt{ zXDQ!;zfYr81nh1bwDpz2y{qv^QZ9RW!iCr}2l(6e{_(A99ZN1JwXY0^{o@|3GT>u% zLKFET?LG^RMXJCJ?qA*t@}*Twz10XbrId*x(}N#B{lQ?CT@B8ki;v0)grOIF>MZl` zWqv^cJGo?V@9k>J_=MIs`Z~YraP2&VqsqoR+NinhER2AtmQier&noVag@8!SUTa&c z_tJbqC`eYEk%lYRD07~*wmj4Ko%;v(F6G4=Upa7RRI{LRNa3bL!1{UqL{#cXCmAC>k)+W+{>TvI@lRQ z1Kq;)hqQ8KA5^yNGCf1ay?u6hrpyHS@@p8}ejn+@X*}}p+41L!^%w%r`R6Mcrs{ni zHeo=aYH%>cH5~tjmJ@CLHUAz};r7;)`fZULDG{8HYq-Cyj$B1iZESivhYa%VVw3Z&4ez~Q&c=VR7&!*Ro z=}CONl@^G?b`f9iA~n{!#jZBUKi( z+`t?thneHAqnGIqSk)R)<*0tOpJiy`qA99PAnNPZ3)paSWx2#Z?)^L~szU7!4nNu) zedT^nyWCp}YrUmIPa1+Tztt8*O?wh}Oa#lx?@vX&B-{HH%KAfq^tTzhD}-~y;8Oh* zuV-mbYKx7(ZfU}tRE{E#(RWY`@p1UB>-#1_{=_<~d18*e*Kyr&|3qW2?~9v-oMivP zMRuN+<0c->Rw_i?V&F4-o7zgR8Z+m;_dqJy^Am@yURLN2*y<-_gJdj}#T&e-iGkne z_yw#L)Jk06W-T9ZzP7JX{SgTR6p_*?Pye~4>)o7GJhsG1#S{LrAyPr8v|O$JZs?$( z?LBjTDtNdusKJKJaVH&K6!`v+4Z|2FGnFQh*1pHxM`MsHViBLZS83uaZY z{#@^`xD5p!O#U=AT$J!X8?jD&&~kq89v{4wi!nN|51Q6_8%M8S%yZFF{kgJs@u&E) z!``xq$Ff25pTrx~&M_s&`ER z;1F%BSNO{xQCL>4lO)de}-Ja|mR=X%c;{eK)u=M8jIVD_>Tk1ANN~NjQT$YSZT0+AduKh0iS2RM%cV!Sb{OzP_~5n#ozdS`qihDjmd)BKP~)?F)0dWN7LjgsRV9|izNzk?IUtp^PC%$*^J^2`Z0hY7 z8M)oov7ftK7q~E`bp8Ni$#;KS6`cOF|F7@)fQfv6)-Ezz99k*h%L0Tx```lp!WJzBYcWnFy;zzC zp$+CRc6zI9nebTU$(~2NLra2irM4JF+Bw4AA3#|hgW!fwOmeZRe}p7~F0!W5%|Rek zK5pny7#zI#s)q5-u#2tYsRtvSk#KtZz=@iCbgt|lp&pzA5>aQ_z1D!c_!}MrDa!A}u;govb6Ld3PNT%!xY~Yvk6~=HzyMr9v7}=bD0PWp6C3O7L9n z=zO<`pncn4#Heb=e46a17(iTTly&Io)v2YzYw=V3LCT)z2Zt86jJC7D0V*sFj`$Pa zzY)N1_tg8XNBE!m{!9$fen*$2(9+T_H0uVsG_l7 ze)l57ceRFh!xu6)2cJBbN_p^B#)6U2eyQ;Gys_+dulo6fsDDNA&{ZGqRd0`{g+vJ{ zE+G6HFOdqe`z5_+CwsqP0ER$SNPdk*3Vx-;H}h-bIls99g!E{Hun0Uxh|c7P^zBmZ zEMhQ3a#V~cm%5p>tuDD}aZp2<-!2<`fC<;x>?Sw~W%3X#-A z4CXLz<;s_b_l3iDd3@fK>SKUlB@=yN$u8^2Gmbn75|HgJKTI+19h!AG z(mx}CX=^L5%543k#yefzZxmJ4M9>8NqZGW^AXQRZHTW=++j}CaU2^Uyy?}JH>xg!O z)H!{<(~O$W=W|60+5O<2qU-DYs|v=OQQqIZ=e=#g7NjeC%E;{GQeHxP*&{o-+}lPy z;UDxK_vd)Np>t4DbQ5ODORknfZujKe+dy_X>s&;{);_8Z##X?OA#|n1?_+e#^l3BFr1!SSHNEuSzqd4lP<+ zQnd9^ja$iXDT{_agmTP|@N4=U5zP8y++M{etGx_Nk6mS%gI)>xa!j#=bzCqcskScHz#|~EZfApTr8YnG_aT2hlgx+83!TI z>?{1sw>52@^#+ZS+_spNz(8JXbn1MDw05$&VPkC5*3?nTm0Z+37OC*I(IXECzDkq> zTIA=F6Ym9k{-Hu`4`@C-b#wDMDb|&~S>z`&f$)eOD4@alAUQ&Ph+pkKN1}1rz}J15 zu$`Ytzdvk=7_K%vawu?;6Mob5n9k7dt1QB3I;6j?>?IS0!V3D^T->s=;s=(}p>^=Q zI=3dotsV_shZd%W3d${(Y`V_r_{M=zm7Raxh2stSIK{^Hqa@%D$0fSHUEbf~ltPPD zo;yX+of+98^Pa;LwTOXLDY|RQ2)9fZY?-5=rDBe6IHC056UtTw!--zX8J`98(D{@p z8h$Z4sNFzD_D$rS_PJE;8isH`t#R0&uQ8hzvUOEnRh}BLZn}T09E3Ib+45b%%5_lx zs?vk6D)S~tg%q7zLD$L6unxq%E1B0EPYBI)1_5r(tlp zI{cCvI0KgTA`kR3VIq8rjyHH3&PLAthKW8_O!zBu^ycoEx|6r9{Q{B997&d~{h}-9 z4BNAyCA;6`FIebB5L|^{jw+Pg`}<%UU8i$E)a1U@9OmX4k(sEE4W8sA@Dk|O#5dTW zy=%?M^cN5rJ~tw{T$5rZQA}iRq2WU)lOosO8c;Q&EZHObiI+G!g!XE}S_XoHHd&pp z^5sPOijq^=6$etN__GB!_2){g3kYiL~@7Den+Lg>)U}R88h4bIbw)w7F z>=>gDV$2W8mSapWFGeS6≪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 literal 0 HcmV?d00001 diff --git a/libs/curl/lib64/libcurl.a b/libs/curl/lib64/libcurl.a index 2112cd8f0dffcc3918d8b479dc764fd6eb3195eb..d30b71aca2ae49bce3291375e00e741e2e4d5b77 100644 GIT binary patch literal 839330 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

e5A~RvS?C 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

>tjElz34+0-q(VR|Tlp)F0+y6!;L!x%1uhuz3S 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

A2#DMb2+KlmvawaZ`Q+3g@~VnItHj z@pdR|_c-tEP}r_<_S>Nx3kv7J9m*t7I1}zrIM2oTaEHQg#heu<1rXO)NkNn5$V5Ql z47o$O1r*McJFdP93TMk5%DwalZ)^^Qb(%Bh4&_NuIDhU?c7wue9DWBYnSY@_DX#FT zN9Nlde~t!)v+fS%3{W`t?of*8>Uf)CO@TDEqHH+ja2{khGw-K)1kP{=|v?%oQGfKmJD0MOfQmUgsak$K~;r}l+QG&MoWgF zjC7)o+jz-m%OAGSDsj3j3R^Zs;kP$l)I?F(C)rnqC;cp%+UkZCO*OUz z>pMPk472H0+f-9?oh{;_OsyOfVv|VWwL^zR3c^~pVwLT+xT1l0+mZOghdn8-NO?Df z0)1S^?$DpA>S~%2vo1UI&r@8Hc{uW3UAD~eX_SIAN+6B0D2-C9DIP2CO=*;`r%<>c zOls7$=H(6b*VQzv!6y>Vhq|7{2$$=5CHZ+b+;GFlM&^((2R-J7vz#NlvP_)SG*wqM zV&XW0nRa~dQ9+GpX3lB@an46$MH9~?u?d{F$7vzRQk$YW`w?m465V>kiWS#Yan`w_ zW<`T`2!;sAWHt&8OrE2}>8FFoMmQqJnP7wrn|e!?LwioVqIo$!Em}nSouV>API+c5 zg&>}CUb?~wLwhOeQEW+7^>r0X)L&%c3wL~o*9JC%qohJNYk`g1qJy~HA1M!z3G&t5&i7SS@+q*^@y8{Vcs$E-!|)eZx^+kP z_*2k_Pf(&Vw)1<+E8U|daXP}EfCr}`EOC0b5k!R4N;%irfF$PQcZaLjAuYhu5QbTI zoCx~UkegxY$B~lANiRTG)`d8{ndV=*bw`H#COvUoh({o06>;0!h42phHsjbbj4gLu zw3w|{)dgeWtn8pU4hvKhu#UIi-~5gdZ-7AnEXZy&Ol&zAkk#eB2Qy$i>SEY-qu92r zV9y)5J^d3)a(0qDWZl02IU6`=20kk*+SdF;IDT6Kn$>n;J9kMe2&X++JTaxGXJBlw zr{~a~ZN8F%Crizqf^D(jXPH4ih=+N%mEcH~o`KAsoj6iuCyWw$`^@0%P|=gk-Q63e zf3p}nc&5Y_=S?at*i!=RkKL4)7ltt`T(E3Yg1z~<_)UmK@+Ovmcl_XSU2|h4CxH2} z`D}^#M9_TR{H^(E35{#L4_kgPb`tD)_K=>Pqe}|*$-|B@!Gay;u7dp~C-oKVD)H_s z&V>6=^sy{&UkT1`@Fq&lojp%x1bcQKdQp64VX65X&T;`Qc+zvhp2S$|0V#3PzS5li zCEjh`ZK1YLIn?!aG-8!JR=H2aif5>qjWxJaO3X-}-@GYrlAP>N0`3G*jvqY4UX>Y} z;0<~gW?>Pzl}&$!>$dI8*I`Sh+nEfWVfoLgB%}M9KjvK9@!!`xqKu!I&tgHS(Y^A6 z<+IQI%cb{{n2#M>M)x-^J8b^Cx>4UFrO@d9@$TrCj(Mj4YOzf~zex|++bsu3K{O2W zvFIlOn6!aLTm%~fIgFCz4{i+rvQUKwOHt^Qex>Fgu{|U>`PpF6?@D}gKfuEIQuE`I z$sd%N`@`NL@wLQ!FOweQN3X^D7_CgfFj6A=>4mEh4>9|Yi-nsAva=FFM+XB`#)$R? zj5cl!#qb!bfS-M4h-h|^z z^TN^nfyfEgs$`s~07LUIV;0?i;i~r;`2c*82x8?wS6+V;D^mT|-1#t`J-)d;C`%0A@L`(@PM=tx#{`0(14Z-@ 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

}j(S{UKL-3~4m3?!ORz9tD>KeN7jE|{(N#6>WP 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 literal 380222 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 literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f97c173c..f995d0460 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -220,6 +220,8 @@ set(SRB2_CONFIG_HAVE_ZLIB ON CACHE BOOL "Enable zlib support.") set(SRB2_CONFIG_HAVE_GME ON CACHE BOOL "Enable GME support.") +set(SRB2_CONFIG_HAVE_GME ON CACHE BOOL + "Enable curl support, used for downloading files via HTTP.") set(SRB2_CONFIG_HWRENDER ON CACHE BOOL "Enable hardware rendering through OpenGL.") set(SRB2_CONFIG_USEASM OFF CACHE BOOL @@ -391,6 +393,26 @@ if(${SRB2_CONFIG_HAVE_PNG} AND ${SRB2_CONFIG_HAVE_ZLIB}) endif() endif() +if(${SRB2_CONFIG_HAVE_CURL}) + if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) + set(CURL_FOUND ON) + set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl) + if(${SRB2_SYSTEM_BITS} EQUAL 64) + set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib32 -lcurl") + else() # 32-bit + set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib64 -lcurl") + endif() + else() + find_package(CURL) + endif() + if(${CURL_FOUND}) + set(SRB2_HAVE_CURL ON) + add_definitions(-DHAVE_CURL) + else() + message(WARNING "You have specified that CURL is available but it was not found. SRB2Kart may not compile correctly.") + endif() +endif() + if(${SRB2_CONFIG_HWRENDER}) add_definitions(-DHWRENDER) set(SRB2_HWRENDER_SOURCES diff --git a/src/Makefile b/src/Makefile index 2cb35ab62..987636030 100644 --- a/src/Makefile +++ b/src/Makefile @@ -267,6 +267,7 @@ endif ifdef NONET OPTS+=-DNONET + NOCURL=1 else ifdef NO_IPV6 OPTS+=-DNO_IPV6 @@ -369,7 +370,15 @@ else NOPNG=1 endif -LIBS+=-lcurl +ifndef NOCURL +OPTS+=-DHAVE_CURL +CURLCONFIG?=curl-config +CURL_CFLAGS?=$(shell $(CURLCONFIG) --cflags) +CURL_LDFLAGS?=$(shell $(CURLCONFIG) --libs) + +LIBS+=$(CURL_LDFLAGS) +CFLAGS+=$(CURL_CFLAGS) +endif ifdef STATIC LIBS:=-static $(LIBS) diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index de2157055..33c83b8b3 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -152,6 +152,7 @@ if(${SDL2_FOUND}) ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${OPENGL_LIBRARIES} + ${CURL_LIBRARIES} ) set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") else() @@ -162,6 +163,7 @@ if(${SDL2_FOUND}) ${PNG_LIBRARIES} ${ZLIB_LIBRARIES} ${OPENGL_LIBRARIES} + ${CURL_LIBRARIES} ) if(${CMAKE_SYSTEM} MATCHES Linux) @@ -241,6 +243,7 @@ if(${SDL2_FOUND}) ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ${OPENGL_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} ) if(${SRB2_HAVE_MIXER}) diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg index e6675421e..46b103df9 100644 --- a/src/win32/Makefile.cfg +++ b/src/win32/Makefile.cfg @@ -138,3 +138,12 @@ else LDFLAGS+=-L../libs/miniupnpc/mingw32 endif #MINGW64 endif + +ifndef NOCURL + CURL_CFLAGS+=-I../libs/curl/include +ifdef MINGW64 + CURL_LDFLAGS+=-L../libs/curl/lib64 -lcurl +else + CURL_LDFLAGS+=-L../libs/curl/lib32 -lcurl +endif #MINGW64 +endif \ No newline at end of file From e85b6d567dde0dff3790d964dfe55531d872dd77 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 26 Apr 2020 21:56:59 -0700 Subject: [PATCH 058/211] Use malloc for hms --- src/hms123311.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 29af48e4c..1843b5a45 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -22,7 +22,6 @@ Documentation available here. #include "m_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ -#include "z_zone.h" /* I just stop myself from making macros anymore. */ #define Blame( ... ) \ @@ -105,7 +104,7 @@ HMS_connect (const char *format, ...) seek = strlen(ms_API) + 1;/* + '/' */ va_start (ap, format); - url = ZZ_Alloc(seek + vsnprintf(0, 0, format, ap) + 1); + url = malloc(seek + vsnprintf(0, 0, format, ap) + 1); va_end (ap); sprintf(url, "%s/", ms_API); @@ -116,11 +115,11 @@ HMS_connect (const char *format, ...) CONS_Printf("HMS: connecting '%s'...\n", url); - buffer = ZZ_Alloc(sizeof *buffer); + buffer = malloc(sizeof *buffer); buffer->curl = curl; /* I just allocated 4k and fuck it! */ buffer->end = 4096; - buffer->buffer = ZZ_Alloc(buffer->end); + buffer->buffer = malloc(buffer->end); buffer->needle = 0; if (cv_masterserver_debug.value) @@ -135,7 +134,7 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); - Z_Free(url); + free(url); return buffer; } @@ -189,8 +188,8 @@ static void HMS_end (struct HMS_buffer *buffer) { curl_easy_cleanup(buffer->curl); - Z_Free(buffer->buffer); - Z_Free(buffer); + free(buffer->buffer); + free(buffer); } int @@ -343,7 +342,7 @@ HMS_register (void) if (ok) { - hms_server_token = Z_StrDup(strtok(hms->buffer, "\n")); + hms_server_token = strdup(strtok(hms->buffer, "\n")); } HMS_end(hms); @@ -366,7 +365,7 @@ HMS_unlist (void) HMS_do(hms); HMS_end(hms); - Z_Free(hms_server_token); + free(hms_server_token); } int From 3a54cbd7a1335dd79497b078b239631979c887a9 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 27 Apr 2020 18:01:27 -0700 Subject: [PATCH 059/211] Create cond if it doesn't exist when signaling --- src/i_threads.h | 4 ++-- src/sdl/i_threads.c | 26 ++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/i_threads.h b/src/i_threads.h index 878e8c388..45a3dcc3e 100644 --- a/src/i_threads.h +++ b/src/i_threads.h @@ -32,8 +32,8 @@ void I_unlock_mutex (I_mutex); void I_hold_cond (I_cond *, I_mutex); -void I_wake_one_cond (I_cond); -void I_wake_all_cond (I_cond); +void I_wake_one_cond (I_cond *); +void I_wake_all_cond (I_cond *); #endif/*I_THREADS_H*/ #endif/*HAVE_THREADS*/ diff --git a/src/sdl/i_threads.c b/src/sdl/i_threads.c index 99e574561..078f4e0f4 100644 --- a/src/sdl/i_threads.c +++ b/src/sdl/i_threads.c @@ -323,16 +323,34 @@ I_hold_cond ( void I_wake_one_cond ( - I_cond id + I_cond * anchor ){ - if (SDL_CondSignal(id) == -1) + SDL_cond * cond; + + cond = Identity( + &i_cond_pool, + i_cond_pool_mutex, + anchor, + (Create_fn)SDL_CreateCond + ); + + if (SDL_CondSignal(cond) == -1) abort(); } void I_wake_all_cond ( - I_cond id + I_cond * anchor ){ - if (SDL_CondBroadcast(id) == -1) + SDL_cond * cond; + + cond = Identity( + &i_cond_pool, + i_cond_pool_mutex, + anchor, + (Create_fn)SDL_CreateCond + ); + + if (SDL_CondBroadcast(cond) == -1) abort(); } From f29e5ebf2628eb4f1aa2975b02959ddafd67a62c Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 27 Apr 2020 18:03:10 -0700 Subject: [PATCH 060/211] Wait for threads before SDL_Quit (lol) --- src/sdl/i_system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 9cb6cbe61..ec4111f61 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -3031,7 +3031,7 @@ INT32 I_StartupSystem(void) SDL_GetVersion(&SDLlinked); #ifdef HAVE_THREADS I_start_threads(); - atexit(I_stop_threads); + I_AddExitFunc(I_stop_threads); #endif I_StartupConsole(); I_OutputMsg("Compiled for SDL version: %d.%d.%d\n", From c5beebbd480d16116d3147f79c29de287a0aad50 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 28 Apr 2020 12:33:50 -0700 Subject: [PATCH 061/211] Fucking multithreading in the server registration AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA --- src/hms123311.c | 9 +- src/mserv.c | 333 +++++++++++++++++++++++++++++++++++++++++------- src/mserv.h | 2 +- 3 files changed, 296 insertions(+), 48 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 1843b5a45..6730438a7 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -350,22 +350,25 @@ HMS_register (void) return ok; } -void +int HMS_unlist (void) { struct HMS_buffer *hms; + int ok; hms = HMS_connect("servers/%s/unlist", hms_server_token); if (! hms) - return; + return 0; curl_easy_setopt(hms->curl, CURLOPT_CUSTOMREQUEST, "POST"); - HMS_do(hms); + ok = HMS_do(hms); HMS_end(hms); free(hms_server_token); + + return ok; } int diff --git a/src/mserv.c b/src/mserv.c index d6641a2aa..6acb5068e 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -23,15 +23,32 @@ #include "m_menu.h" #include "z_zone.h" -static time_t MSLastPing; +static int MSId; +static int MSRegisteredId = -1; -static inline void SendPingToMasterServer(void); +static boolean MSRegistered; +static boolean MSInProgress; +static boolean MSUpdateAgain; + +static time_t MSLastPing; + +#ifdef HAVE_THREADS +static I_mutex MSMutex; +static I_cond MSCond; + +# define Lock_state() I_lock_mutex (&MSMutex) +# define Unlock_state() I_unlock_mutex (MSMutex) +#else/*HAVE_THREADS*/ +# define Lock_state() +# define Unlock_state() +#endif/*HAVE_THREADS*/ + +static void Update_parameters (void); #ifndef NONET static void Command_Listserv_f(void); #endif static void MasterServer_OnChange(void); -static void ServerName_OnChange(void); static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { {2, "MIN"}, @@ -40,9 +57,9 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { }; consvar_t cv_masterserver = {"masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, ServerName_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, SendPingToMasterServer, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; char *ms_API; INT16 ms_RoomId = -1; @@ -55,8 +72,6 @@ msg_server_t *ms_ServerList; I_mutex ms_ServerList_mutex; #endif -static enum { MSCS_NONE, MSCS_WAITING, MSCS_REGISTERED, MSCS_FAILED } con_state = MSCS_NONE; - UINT16 current_port = 0; // Room list is an external variable now. @@ -176,71 +191,301 @@ static void Command_Listserv_f(void) } #endif -void RegisterServer(void) +static void +Finish_registration (void) { - CONS_Printf(M_GetText("Registering this server on the Master Server...\n")); + int registered; + CONS_Printf("Registering this server on the master server...\n"); + + registered = HMS_register(); + + Lock_state(); { - if (HMS_register()) - con_state = MSCS_REGISTERED; + MSRegistered = registered; + MSRegisteredId = MSId; + + time(&MSLastPing); + } + Unlock_state(); + + if (registered) + CONS_Printf("Master server registration successful.\n"); +} + +static void +Finish_update (void) +{ + int registered; + int done; + + Lock_state(); + { + registered = MSRegistered; + MSUpdateAgain = false;/* this will happen anyway */ + } + Unlock_state(); + + if (registered) + { + if (HMS_update()) + { + Lock_state(); + { + time(&MSLastPing); + MSRegistered = true; + } + Unlock_state(); + + CONS_Printf("Updated master server listing.\n"); + } else - con_state = MSCS_FAILED; + Finish_registration(); + } + else + Finish_registration(); + + Lock_state(); + { + done = ! MSUpdateAgain; + + if (done) + MSInProgress = false; + } + Unlock_state(); + + if (! done) + Finish_update(); +} + +static void +Finish_unlist (void) +{ + int registered; + + Lock_state(); + { + registered = MSRegistered; + } + Unlock_state(); + + if (registered) + { + CONS_Printf("Removing this server from the master server...\n"); + + if (HMS_unlist()) + CONS_Printf("Server deregistration request successfully sent.\n"); + + Lock_state(); + { + MSRegistered = false; + } + Unlock_state(); + +#ifdef HAVE_THREADS + I_wake_all_cond(&MSCond); +#endif } - time(&MSLastPing); + Lock_state(); + { + if (MSId == MSRegisteredId) + MSId++; + } + Unlock_state(); +} + +#ifdef HAVE_THREADS +static int * +Server_id (void) +{ + int *id; + id = malloc(sizeof *id); + Lock_state(); + { + *id = MSId; + } + Unlock_state(); + return id; +} + +static int * +New_server_id (void) +{ + int *id; + id = malloc(sizeof *id); + Lock_state(); + { + *id = ++MSId; + I_wake_all_cond(&MSCond); + } + Unlock_state(); + return id; +} + +static void +Register_server_thread (int *id) +{ + int same; + + Lock_state(); + { + /* wait for previous unlist to finish */ + while (*id == MSId && MSRegistered) + I_hold_cond(&MSCond, MSMutex); + + same = ( *id == MSId );/* it could have been a while */ + } + Unlock_state(); + + if (same)/* it could have been a while */ + Finish_registration(); + + free(id); +} + +static void +Update_server_thread (int *id) +{ + int same; + + Lock_state(); + { + same = ( *id == MSRegisteredId ); + } + Unlock_state(); + + if (same) + Finish_update(); + + free(id); +} + +static void +Unlist_server_thread (int *id) +{ + int same; + + Lock_state(); + { + same = ( *id == MSRegisteredId ); + } + Unlock_state(); + + if (same) + Finish_unlist(); + + free(id); +} +#endif/*HAVE_THREADS*/ + +void RegisterServer(void) +{ +#ifdef HAVE_THREADS + I_spawn_thread( + "register-server", + (I_thread_fn)Register_server_thread, + New_server_id() + ); +#else + Finish_registration(); +#endif } static void UpdateServer(void) { - if (( con_state == MSCS_REGISTERED && HMS_update() )) - { - time(&MSLastPing); - } - else - { - con_state = MSCS_FAILED; - RegisterServer(); - } -} - -static inline void SendPingToMasterServer(void) -{ -// Here, have a simpler MS Ping... - Cue - if(time(NULL) > (MSLastPing+(60*cv_masterserver_update_rate.value)) && con_state != MSCS_NONE) - { - UpdateServer(); - } +#ifdef HAVE_THREADS + I_spawn_thread( + "update-server", + (I_thread_fn)Update_server_thread, + Server_id() + ); +#else + Finish_update(); +#endif } void UnregisterServer(void) { - if (con_state == MSCS_REGISTERED) +#ifdef HAVE_THREADS + I_spawn_thread( + "unlist-server", + (I_thread_fn)Unlist_server_thread, + Server_id() + ); +#else + Finish_unlist(); +#endif +} + +static boolean +Online (void) +{ + return ( server && ms_RoomId > 0 ); +} + +static inline void SendPingToMasterServer(void) +{ + int ready; + time_t now; + + if (Online()) { - CONS_Printf(M_GetText("Removing this server from the Master Server...\n")); + time(&now); - HMS_unlist(); + Lock_state(); + { + ready = ( + MSRegisteredId == MSId && + ! MSInProgress && + now >= ( MSLastPing + cv_masterserver_update_rate.value ) + ); + + if (ready) + MSInProgress = true; + } + Unlock_state(); + + if (ready) + UpdateServer(); } +} - con_state = MSCS_NONE; +static void +Update_parameters (void) +{ + int registered; + int delayed; + + if (Online()) + { + Lock_state(); + { + delayed = MSInProgress; + + if (delayed)/* do another update after the current one */ + MSUpdateAgain = true; + else + registered = MSRegistered; + } + Unlock_state(); + + if (! delayed && registered) + UpdateServer(); + } } void MasterClient_Ticker(void) { - if (server && ms_RoomId > 0) - SendPingToMasterServer(); -} - -static void ServerName_OnChange(void) -{ - if (con_state == MSCS_REGISTERED) - UpdateServer(); + SendPingToMasterServer(); } static void MasterServer_OnChange(void) { boolean auto_register; - auto_register = ( con_state != MSCS_NONE ); + //auto_register = ( con_state != MSCS_NONE ); + auto_register = false; if (ms_API) { diff --git a/src/mserv.h b/src/mserv.h index 6f61719aa..71350e0de 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -104,7 +104,7 @@ void AddMServCommands(void); int HMS_in_use (void); int HMS_fetch_rooms (int joining, int id); int HMS_register (void); -void HMS_unlist (void); +int HMS_unlist (void); int HMS_update (void); void HMS_list_servers (void); msg_server_t * HMS_fetch_servers (msg_server_t *list, int room, int id); From 18ced99463ec5686869bd3ba3cdd105641fd4c85 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 28 Apr 2020 13:08:43 -0700 Subject: [PATCH 062/211] Put some mutex on CONS_Printf etc. hahaha --- src/console.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/console.h | 5 ++ src/d_main.c | 12 +++- 3 files changed, 164 insertions(+), 6 deletions(-) diff --git a/src/console.c b/src/console.c index 28958089b..09b4617ba 100644 --- a/src/console.c +++ b/src/console.c @@ -31,6 +31,7 @@ #include "i_video.h" #include "z_zone.h" #include "i_system.h" +#include "i_threads.h" #include "d_main.h" #include "m_menu.h" #include "filesrch.h" @@ -45,6 +46,16 @@ #define MAXHUDLINES 20 +#ifdef HAVE_THREADS +I_mutex con_mutex; + +# define Lock_state() I_lock_mutex(&con_mutex) +# define Unlock_state() I_unlock_mutex(con_mutex) +#else/*HAVE_THREADS*/ +# define Lock_state() +# define Unlock_state() +#endif/*HAVE_THREADS*/ + static boolean con_started = false; // console has been initialised boolean con_startup = false; // true at game startup, screen need refreshing static boolean con_forcepic = true; // at startup toggle console translucency when first off @@ -170,6 +181,8 @@ static void CONS_hudlines_Change(void) { INT32 i; + Lock_state(); + // Clear the currently displayed lines for (i = 0; i < con_hudlines; i++) con_hudtime[i] = 0; @@ -181,6 +194,8 @@ static void CONS_hudlines_Change(void) con_hudlines = cons_hudlines.value; + Unlock_state(); + CONS_Printf(M_GetText("Number of console HUD lines is now %d\n"), con_hudlines); } @@ -188,12 +203,16 @@ static void CONS_hudlines_Change(void) // static void CONS_Clear_f(void) { + Lock_state(); + memset(con_buffer, 0, CON_BUFFERSIZE); con_cx = 0; con_cy = con_totallines-1; con_line = &con_buffer[con_cy*con_width]; con_scrollup = 0; + + Unlock_state(); } // Choose english keymap @@ -369,20 +388,29 @@ void CON_Init(void) for (i = 0; i < NUMINPUTS; i++) bindtable[i] = NULL; + Lock_state(); + // clear all lines memset(con_buffer, 0, CON_BUFFERSIZE); // make sure it is ready for the loading screen con_width = 0; + + Unlock_state(); + CON_RecalcSize(); CON_SetupColormaps(); + Lock_state(); + //note: CON_Ticker should always execute at least once before D_Display() con_clipviewtop = -1; // -1 does not clip con_hudlines = atoi(cons_hudlines.defaultvalue); + Unlock_state(); + // setup console input filtering CON_InputInit(); @@ -391,15 +419,23 @@ void CON_Init(void) COM_AddCommand("cls", CONS_Clear_f); //COM_AddCommand("english", CONS_English_f); // set console full screen for game startup MAKE SURE VID_Init() done !!! + Lock_state(); + con_destlines = vid.height; con_curlines = vid.height; + Unlock_state(); if (!dedicated) { + Lock_state(); + con_started = true; con_startup = true; // need explicit screen refresh until we are in Doom loop consoletoggle = false; + + Unlock_state(); + CV_RegisterVar(&cons_msgtimeout); CV_RegisterVar(&cons_hudlines); CV_RegisterVar(&cons_speed); @@ -411,19 +447,27 @@ void CON_Init(void) } else { + Lock_state(); + con_started = true; con_startup = false; // need explicit screen refresh until we are in Doom loop consoletoggle = true; + + Unlock_state(); } } // Console input initialization // static void CON_InputInit(void) { + Lock_state(); + // prepare the first prompt line memset(inputlines, 0, sizeof (inputlines)); inputline = 0; input_cur = input_sel = input_len = 0; + + Unlock_state(); } //====================================================================== @@ -439,6 +483,8 @@ static void CON_RecalcSize(void) char *tmp_buffer; char *string; + Lock_state(); + switch (cv_constextsize.value) { case V_NOSCALEPATCH: @@ -476,11 +522,18 @@ static void CON_RecalcSize(void) // check for change of video width if (conw == con_width) + { + Unlock_state(); return; // didn't change + } + + Unlock_state(); tmp_buffer = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); string = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); // BP: it is a line but who know + Lock_state(); + oldcon_width = con_width; oldnumlines = con_totallines; oldcon_cy = con_cy; @@ -501,6 +554,8 @@ static void CON_RecalcSize(void) con_line = &con_buffer[con_cy*con_width]; con_scrollup = 0; + Unlock_state(); + // re-arrange console text buffer to keep text if (oldcon_width) // not the first time { @@ -525,7 +580,11 @@ static void CON_RecalcSize(void) static void CON_ChangeHeight(void) { - INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4 + INT32 minheight; + + Lock_state(); + + minheight = 20 * con_scalefactor; // 20 = 8+8+4 // toggle console in con_destlines = (cons_height.value*vid.height)/100; @@ -535,13 +594,19 @@ static void CON_ChangeHeight(void) con_destlines = vid.height; con_destlines &= ~0x3; // multiple of text row height + + Unlock_state(); } // Handles Console moves in/out of screen (per frame) // static void CON_MoveConsole(void) { - const fixed_t conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT); + fixed_t conspeed; + + Lock_state(); + + conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT); // instant if (!cons_speed.value) @@ -563,6 +628,8 @@ static void CON_MoveConsole(void) if (con_curlines < con_destlines) con_curlines = con_destlines; } + + Unlock_state(); } INT32 CON_ShiftChar(INT32 ch) @@ -587,27 +654,44 @@ void CON_ClearHUD(void) { INT32 i; + Lock_state(); + for (i = 0; i < con_hudlines; i++) con_hudtime[i] = 0; + + Unlock_state(); } // Force console to move out immediately // note: con_ticker will set consoleready false void CON_ToggleOff(void) { + Lock_state(); + if (!con_destlines) + { + Unlock_state(); return; + } con_destlines = 0; con_curlines = 0; CON_ClearHUD(); con_forcepic = 0; con_clipviewtop = -1; // remove console clipping of view + + Unlock_state(); } boolean CON_Ready(void) { - return consoleready; + boolean ready; + Lock_state(); + { + ready = consoleready; + } + Unlock_state(); + return ready; } // Console ticker: handles console move in/out, cursor blinking @@ -615,7 +699,11 @@ boolean CON_Ready(void) void CON_Ticker(void) { INT32 i; - INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4 + INT32 minheight; + + Lock_state(); + + minheight = 20 * con_scalefactor; // 20 = 8+8+4 // cursor blinking con_tick++; @@ -673,6 +761,8 @@ void CON_Ticker(void) if (con_hudtime[i] < 0) con_hudtime[i] = 0; } + + Unlock_state(); } // @@ -684,32 +774,51 @@ void CON_Ticker(void) static void CON_InputClear(void) { + Lock_state(); + memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); input_cur = input_sel = input_len = 0; + + Unlock_state(); } static void CON_InputSetString(const char *c) { + Lock_state(); + memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); strcpy(inputlines[inputline], c); input_cur = input_sel = input_len = strlen(c); + + Unlock_state(); } static void CON_InputAddString(const char *c) { size_t csize = strlen(c); + + Lock_state(); + if (input_len + csize > CON_MAXPROMPTCHARS-1) + { + Unlock_state(); return; + } if (input_cur != input_len) memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur); memcpy(&inputlines[inputline][input_cur], c, csize); input_len += csize; input_sel = (input_cur += csize); + + Unlock_state(); } static void CON_InputDelSelection(void) { size_t start, end, len; + + Lock_state(); + if (input_cur > input_sel) { start = input_sel; @@ -728,27 +837,39 @@ static void CON_InputDelSelection(void) input_len -= len; input_sel = input_cur = start; + + Unlock_state(); } static void CON_InputAddChar(char c) { if (input_len >= CON_MAXPROMPTCHARS-1) return; + + Lock_state(); + if (input_cur != input_len) memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur); inputlines[inputline][input_cur++] = c; inputlines[inputline][++input_len] = 0; input_sel = input_cur; + + Unlock_state(); } static void CON_InputDelChar(void) { if (!input_cur) return; + + Lock_state(); + if (input_cur != input_len) memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur); inputlines[inputline][--input_len] = 0; input_sel = --input_cur; + + Unlock_state(); } // @@ -1174,6 +1295,8 @@ static void CON_Print(char *msg) S_StartSound(NULL, sfx_radio); } + Lock_state(); + if (!(*msg & 0x80)) { con_line[con_cx++] = '\x80'; @@ -1234,7 +1357,12 @@ static void CON_Print(char *msg) } if (*msg == '\0') + { +#ifdef HAVE_THREADS + I_unlock_mutex(con_mutex); +#endif return; + } // printable character for (l = 0; l < (con_width-11) && msg[l] > ' '; l++) @@ -1252,6 +1380,8 @@ static void CON_Print(char *msg) for (; l > 0; l--) con_line[con_cx++] = *(msg++); } + + Unlock_state(); } void CON_LogMessage(const char *msg) @@ -1283,6 +1413,7 @@ void CONS_Printf(const char *fmt, ...) { va_list argptr; static char *txt = NULL; + boolean startup; if (txt == NULL) txt = malloc(8192); @@ -1315,11 +1446,16 @@ void CONS_Printf(const char *fmt, ...) CON_LogMessage(txt); #endif + Lock_state(); + // make sure new text is visible con_scrollup = 0; + startup = con_startup; + + Unlock_state(); // if not in display loop, force screen update - if (con_startup) + if (startup) { #if (defined (_WINDOWS)) || (defined (__OS2__) && !defined (HAVE_SDL)) patch_t *con_backpic = W_CachePatchName("KARTKREW", PU_CACHE); @@ -1633,8 +1769,13 @@ static void CON_DrawConsole(void) // void CON_Drawer(void) { + Lock_state(); + if (!con_started || !graphics_started) + { + Unlock_state(); return; + } if (con_recalc) CON_RecalcSize(); @@ -1644,4 +1785,6 @@ void CON_Drawer(void) else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_VOTING || gamestate == GS_EVALUATION || gamestate == GS_WAITINGPLAYERS) CON_DrawHudlines(); + + Unlock_state(); } diff --git a/src/console.h b/src/console.h index 11621746c..ba0141a0d 100644 --- a/src/console.h +++ b/src/console.h @@ -12,6 +12,7 @@ #include "d_event.h" #include "command.h" +#include "i_threads.h" #ifdef _WII void CON_InitWii(void); @@ -21,6 +22,10 @@ void CON_Init(void); boolean CON_Responder(event_t *ev); +#ifdef HAVE_THREADS +extern I_mutex con_mutex; +#endif + // set true when screen size has changed, to adapt console extern boolean con_recalc; diff --git a/src/d_main.c b/src/d_main.c index a6ac63ce4..c1b2ef171 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -266,7 +266,17 @@ void D_ProcessEvents(void) continue; // menu ate the event // console input - if (CON_Responder(ev)) +#ifdef HAVE_THREADS + I_lock_mutex(&con_mutex); +#endif + { + eaten = CON_Responder(ev); + } +#ifdef HAVE_THREADS + I_unlock_mutex(con_mutex); +#endif + + if (eaten) continue; // ate the event G_Responder(ev); From d1fb8f42f26b02084b413422d95ded32d636230f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 28 Apr 2020 14:21:57 -0700 Subject: [PATCH 063/211] Fix switching the master server --- src/d_clisrv.c | 2 +- src/d_clisrv.h | 1 + src/hms123311.c | 33 +++++++++++++++++++++++++++++++-- src/mserv.c | 45 +++++++++++++++++++++++++++++++-------------- src/mserv.h | 4 +--- 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 89facd5f9..9ab9cd7ab 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -79,7 +79,7 @@ boolean server = true; // true or false but !server == client #define client (!server) boolean nodownload = false; -static boolean serverrunning = false; +boolean serverrunning = false; INT32 serverplayer = 0; char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index a3992c8f9..294d88d7c 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -523,6 +523,7 @@ typedef enum } kickreason_t; extern boolean server; +extern boolean serverrunning; #define client (!server) extern boolean dedicated; // For dedicated server extern UINT16 software_MAXPACKETLENGTH; diff --git a/src/hms123311.c b/src/hms123311.c index 6730438a7..6e749e3bf 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -22,6 +22,7 @@ Documentation available here. #include "m_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ +#include "i_threads.h" /* I just stop myself from making macros anymore. */ #define Blame( ... ) \ @@ -34,6 +35,11 @@ consvar_t cv_masterserver_debug = { static int hms_started; +static char *hms_api; +#ifdef HAVE_THREADS +static I_mutex hms_api_mutex; +#endif + static char *hms_server_token; struct HMS_buffer @@ -101,13 +107,21 @@ HMS_connect (const char *format, ...) return NULL; } - seek = strlen(ms_API) + 1;/* + '/' */ +#ifdef HAVE_THREADS + I_lock_mutex(&hms_api_mutex); +#endif + + seek = strlen(hms_api) + 1;/* + '/' */ va_start (ap, format); url = malloc(seek + vsnprintf(0, 0, format, ap) + 1); va_end (ap); - sprintf(url, "%s/", ms_API); + sprintf(url, "%s/", hms_api); + +#ifdef HAVE_THREADS + I_unlock_mutex(hms_api_mutex); +#endif va_start (ap, format); vsprintf(&url[seek], format, ap); @@ -590,3 +604,18 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size) return ok; } + +void +HMS_set_api (char *api) +{ +#ifdef HAVE_THREADS + I_lock_mutex(&hms_api_mutex); +#endif + { + free(hms_api); + hms_api = api; + } +#ifdef HAVE_THREADS + I_unlock_mutex(hms_api_mutex); +#endif +} diff --git a/src/mserv.c b/src/mserv.c index 6acb5068e..490f79d67 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -61,7 +61,6 @@ consvar_t cv_servername = {"servername", "SRB2Kart server", CV_SAVE|CV_CALL|CV_N consvar_t cv_masterserver_update_rate = {"masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters, 0, NULL, NULL, 0, 0, NULL}; -char *ms_API; INT16 ms_RoomId = -1; #ifdef HAVE_THREADS @@ -377,6 +376,19 @@ Unlist_server_thread (int *id) free(id); } + +static void +Change_masterserver_thread (char *api) +{ + Lock_state(); + { + while (MSRegistered) + I_hold_cond(&MSCond, MSMutex); + } + Unlock_state(); + + HMS_set_api(api); +} #endif/*HAVE_THREADS*/ void RegisterServer(void) @@ -421,7 +433,7 @@ void UnregisterServer(void) static boolean Online (void) { - return ( server && ms_RoomId > 0 ); + return ( serverrunning && ms_RoomId > 0 ); } static inline void SendPingToMasterServer(void) @@ -480,21 +492,26 @@ void MasterClient_Ticker(void) SendPingToMasterServer(); } +static void +Set_api (const char *api) +{ +#ifdef HAVE_THREADS + I_spawn_thread( + "change-masterserver", + (I_thread_fn)Change_masterserver_thread, + strdup(api) + ); +#else + HMS_set_api(strdup(api)); +#endif +} + static void MasterServer_OnChange(void) { - boolean auto_register; + UnregisterServer(); - //auto_register = ( con_state != MSCS_NONE ); - auto_register = false; + Set_api(cv_masterserver.string); - if (ms_API) - { - UnregisterServer(); - Z_Free(ms_API); - } - - ms_API = Z_StrDup(cv_masterserver.string); - - if (auto_register) + if (Online()) RegisterServer(); } diff --git a/src/mserv.h b/src/mserv.h index 71350e0de..06c91b334 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -70,8 +70,6 @@ extern consvar_t cv_masterserver, cv_servername; extern consvar_t cv_masterserver_update_rate; extern consvar_t cv_masterserver_debug; -extern char *ms_API; - // < 0 to not connect (usually -1) (offline mode) // == 0 to show all rooms, not a valid hosting room // anything else is whatever room the MS assigns to that number (online mode) @@ -101,7 +99,7 @@ extern msg_rooms_t room_list[NUM_LIST_ROOMS+1]; void AddMServCommands(void); /* HTTP */ -int HMS_in_use (void); +void HMS_set_api (char *api); int HMS_fetch_rooms (int joining, int id); int HMS_register (void); int HMS_unlist (void); From 67b226c80119357d267879a543a60d1059721c61 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 28 Apr 2020 14:35:03 -0700 Subject: [PATCH 064/211] Oops a debug condition snuck in there --- src/mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mserv.c b/src/mserv.c index 490f79d67..fe11307f5 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -450,7 +450,7 @@ static inline void SendPingToMasterServer(void) ready = ( MSRegisteredId == MSId && ! MSInProgress && - now >= ( MSLastPing + cv_masterserver_update_rate.value ) + now >= ( MSLastPing + 60 * cv_masterserver_update_rate.value ) ); if (ready) From ee94e064e96615ad2da7cff7bc815ff8fed2adf3 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 15:22:14 +0200 Subject: [PATCH 065/211] Allow S_NULL to be used on followers --- src/p_user.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index c4a226c42..f49c3bc97 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8047,6 +8047,17 @@ void P_DoTimeOver(player_t *player) */ static void P_SetFollowerState(mobj_t *f, INT32 state) { + + if (!f || P_MobjWasRemoved(f)) + return; // safety net + + // No, do NOT set the follower to S_NULL. Set it to S_INVISIBLE. + if (state == S_NULL) + { + state = S_INVISIBLE; + f->threshold = 1; // Threshold = 1 means stop doing anything related to setting states, so that we don't get out of S_INVISIBLE + } + // extravalue2 stores the last "first state" we used. // because states default to idlestates, if we use an animation that uses an "ongoing" state line, don't reset it! // this prevents it from looking very dumb @@ -8129,6 +8140,15 @@ static void P_HandleFollower(player_t *player) } else // follower exists, woo! { + + // Safety net (2) + + if (P_MobjWasRemoved(player->follower)) + { + player->follower = NULL; // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing. + return; + } + // first of all, handle states following the same model as above: if (player->follower->tics == 1) P_SetFollowerState(player->follower, player->follower->state->nextstate); @@ -8157,6 +8177,12 @@ static void P_HandleFollower(player_t *player) // 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 + if (player->follower->threshold) + return; // Threshold means the follower was "despanwed" with S_NULL. + + // However with how the code is factored, this is just a special case of S_INVISBLE to avoid having to add other player variables. + + // handle follower animations. Could probably be better... // hurt or dead if (player->kartstuff[k_spinouttimer] || player->mo->state == &states[S_KART_SPIN] || player->mo->health <= 0) From 766f7f035fa64e09f0b67f54bc8c4f5f30ef07b9 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 18:08:41 +0200 Subject: [PATCH 066/211] Let followers change colour separatly from players --- src/d_netcmd.c | 118 +++++++++++++++++++++++++++++++++++++++----- src/d_player.h | 1 + src/dehacked.c | 19 ++++++- src/g_game.c | 17 ++++++- src/lua_playerlib.c | 6 ++- src/p_saveg.c | 2 + src/p_user.c | 15 +++++- src/r_data.h | 1 + src/r_draw.c | 1 + src/r_things.c | 2 +- src/r_things.h | 2 + 11 files changed, 166 insertions(+), 18 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b8b9f98a7..d4ef8f22c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -113,10 +113,16 @@ static void Skin_OnChange(void); static void Skin2_OnChange(void); static void Skin3_OnChange(void); static void Skin4_OnChange(void); + static void Follower_OnChange(void); static void Follower2_OnChange(void); static void Follower3_OnChange(void); static void Follower4_OnChange(void); +static void Followercolor_OnChange(void); +static void Followercolor2_OnChange(void); +static void Followercolor3_OnChange(void); +static void Followercolor4_OnChange(void); + static void Color_OnChange(void); static void Color2_OnChange(void); static void Color3_OnChange(void); @@ -300,6 +306,13 @@ consvar_t cv_follower2 = {"follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Fo consvar_t cv_follower3 = {"follower3", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_follower4 = {"follower4", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower4_OnChange, 0, NULL, NULL, 0, 0, NULL}; +// player's follower colors... Also saved... +consvar_t cv_followercolor = {"followercolor", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_followercolor2 = {"followercolor2", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_followercolor3 = {"followercolor3", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_followercolor4 = {"followercolor4", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange, 0, NULL, NULL, 0, 0, NULL}; + + // Follower toggle static CV_PossibleValue_t followers_cons_t[] = {{0, "Yours only"}, {1, "Everyone's"}, {0, NULL}}; consvar_t cv_showfollowers = {"showfollowers", "Everyone's", CV_SAVE, followers_cons_t, 0, 0, NULL, NULL, 0, 0, NULL}; @@ -803,11 +816,14 @@ void D_RegisterClientCommands(void) for (i = 0; i < MAXSKINCOLORS; i++) { - Color_cons_t[i].value = i; - Color_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart + Color_cons_t[i].value = Followercolor_cons_t[i].value = i; + Color_cons_t[i].strvalue = Followercolor_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart } - Color_cons_t[MAXSKINCOLORS].value = 0; - Color_cons_t[MAXSKINCOLORS].strvalue = NULL; + Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+1].value = 0; + Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+1].strvalue = NULL; + + Followercolor_cons_t[MAXSKINCOLORS].value = MAXSKINCOLORS; + Followercolor_cons_t[MAXSKINCOLORS].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's if (dedicated) return; @@ -880,22 +896,26 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_playercolor); CV_RegisterVar(&cv_skin); // r_things.c (skin NAME) CV_RegisterVar(&cv_follower); + CV_RegisterVar(&cv_followercolor); CV_RegisterVar(&cv_showfollowers); // secondary player (splitscreen) CV_RegisterVar(&cv_playername2); CV_RegisterVar(&cv_playercolor2); CV_RegisterVar(&cv_skin2); CV_RegisterVar(&cv_follower2); + CV_RegisterVar(&cv_followercolor2); // third player CV_RegisterVar(&cv_playername3); CV_RegisterVar(&cv_playercolor3); CV_RegisterVar(&cv_skin3); CV_RegisterVar(&cv_follower3); + CV_RegisterVar(&cv_followercolor3); // fourth player CV_RegisterVar(&cv_playername4); CV_RegisterVar(&cv_playercolor4); CV_RegisterVar(&cv_skin4); CV_RegisterVar(&cv_follower4); + CV_RegisterVar(&cv_followercolor4); // preferred number of players CV_RegisterVar(&cv_splitplayers); @@ -1478,16 +1498,14 @@ static void SendNameAndColor(void) CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue); } + // ditto for follower colour: + if (!cv_followercolor.value) + CV_StealthSet(&cv_followercolor, "Match"); // set it to "Match". I don't care about your stupidity! + // so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game: if (cv_follower.value > numfollowers-1 || cv_follower.value < -1) CV_StealthSet(&cv_follower, "-1"); - if (!strcmp(cv_playername.string, player_names[consoleplayer]) - && cv_playercolor.value == players[consoleplayer].skincolor - && !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name) - && cv_follower.value == players[consoleplayer].followerskin) - return; - // We'll handle it later if we're not playing. if (!Playing()) return; @@ -1573,6 +1591,7 @@ static void SendNameAndColor(void) WRITEUINT8(p, (UINT8)cv_playercolor.value); WRITEUINT8(p, (UINT8)cv_skin.value); WRITESINT8(p, (UINT8)cv_follower.value); + WRITESINT8(p, (UINT8)cv_followercolor.value); SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1616,6 +1635,10 @@ static void SendNameAndColor2(void) CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue); } + // ditto for follower colour: + if (!cv_followercolor2.value) + CV_StealthSet(&cv_followercolor2, "Match"); // set it to "Match". I don't care about your stupidity! + // so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game: if (cv_follower2.value > numfollowers-1 || cv_follower2.value < -1) CV_StealthSet(&cv_follower2, "-1"); @@ -1706,6 +1729,7 @@ static void SendNameAndColor2(void) WRITEUINT8(p, (UINT8)cv_playercolor2.value); WRITEUINT8(p, (UINT8)cv_skin2.value); WRITESINT8(p, (UINT8)cv_follower2.value); + WRITESINT8(p, (UINT8)cv_followercolor2.value); SendNetXCmd2(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1737,6 +1761,10 @@ static void SendNameAndColor3(void) CV_StealthSetValue(&cv_playercolor3, skincolor_blueteam); } + // ditto for follower colour: + if (!cv_followercolor3.value) + CV_StealthSet(&cv_followercolor3, "Match"); // set it to "Match". I don't care about your stupidity! + // never allow the color "none" if (!cv_playercolor3.value) { @@ -1830,6 +1858,7 @@ static void SendNameAndColor3(void) WRITEUINT8(p, (UINT8)cv_playercolor3.value); WRITEUINT8(p, (UINT8)cv_skin3.value); WRITESINT8(p, (UINT8)cv_follower3.value); + WRITESINT8(p, (UINT8)cv_followercolor3.value); SendNetXCmd3(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1861,6 +1890,10 @@ static void SendNameAndColor4(void) CV_StealthSetValue(&cv_playercolor4, skincolor_blueteam); } + // ditto for follower colour: + if (!cv_followercolor4.value) + CV_StealthSet(&cv_followercolor4, "Match"); // set it to "Match". I don't care about your stupidity! + // never allow the color "none" if (!cv_playercolor4.value) { @@ -1962,6 +1995,7 @@ static void SendNameAndColor4(void) WRITEUINT8(p, (UINT8)cv_playercolor4.value); WRITEUINT8(p, (UINT8)cv_skin4.value); WRITESINT8(p, (UINT8)cv_follower4.value); + WRITESINT8(p, (UINT8)cv_followercolor4.value); SendNetXCmd4(XD_NAMEANDCOLOR, buf, p - buf); } @@ -1969,7 +2003,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) { player_t *p = &players[playernum]; char name[MAXPLAYERNAME+1]; - UINT8 color, skin; + UINT8 color, skin, followercolor; SINT8 follower; #ifdef PARANOIA @@ -1995,6 +2029,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) color = READUINT8(*cp); skin = READUINT8(*cp); follower = READSINT8(*cp); + followercolor = READSINT8(*cp); // set name if (strcasecmp(player_names[playernum], name) != 0) @@ -2055,7 +2090,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) else SetPlayerSkinByNum(playernum, skin); - // set follower: + // set follower colour: + // Don't bother doing garbage and kicking if we receive None, this is both silly and a waste of time, this will be handled properly in P_HandleFollower. + p->followercolor = followercolor; + + // set follower SetFollower(playernum, follower); } @@ -6208,6 +6247,22 @@ static void Follower_OnChange(void) SendNameAndColor(); } +// About the same as Color_OnChange but for followers. +static void Followercolor_OnChange(void) +{ + + if (!Playing()) + return; // do whatever you want if you aren't in the game or don't have a follower. + + if (!P_PlayerMoving(consoleplayer)) + { + // Color change menu scrolling fix is no longer necessary + SendNameAndColor(); + } +} + +// repeat for the 3 other players + static void Follower2_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; @@ -6239,6 +6294,19 @@ static void Follower2_OnChange(void) SendNameAndColor2(); } +static void Followercolor2_OnChange(void) +{ + + if (!Playing()) + return; // do whatever you want if you aren't in the game or don't have a follower. + + if (!P_PlayerMoving(g_localplayers[1])) + { + // Color change menu scrolling fix is no longer necessary + SendNameAndColor2(); + } +} + static void Follower3_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; @@ -6269,6 +6337,19 @@ static void Follower3_OnChange(void) SendNameAndColor3(); } +static void Followercolor3_OnChange(void) +{ + + if (!Playing()) + return; // do whatever you want if you aren't in the game or don't have a follower. + + if (!P_PlayerMoving(g_localplayers[2])) + { + // Color change menu scrolling fix is no longer necessary + SendNameAndColor3(); + } +} + static void Follower4_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; @@ -6299,6 +6380,19 @@ static void Follower4_OnChange(void) SendNameAndColor4(); } +static void Followercolor4_OnChange(void) +{ + + if (!Playing()) + return; // do whatever you want if you aren't in the game or don't have a follower. + + if (!P_PlayerMoving(g_localplayers[3])) + { + // Color change menu scrolling fix is no longer necessary + SendNameAndColor4(); + } +} + /** Sends a skin change for the console player, unless that player is moving. * \sa cv_skin, Skin2_OnChange, Color_OnChange * \author Graue diff --git a/src/d_player.h b/src/d_player.h index c6288bc58..313f654b4 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -493,6 +493,7 @@ typedef struct player_s INT32 followerskin; // Kart: This player's follower "skin" boolean followerready; // Kart: Used to know when we can have a follower or not. (This is set on the first NameAndColor follower update) + UINT8 followercolor; // Kart: Used to store the follower colour the player wishes to use mobj_t *follower; // Kart: This is the follower object we have. (If any) // diff --git a/src/dehacked.c b/src/dehacked.c index 6d08e5f33..3d633e518 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -720,7 +720,7 @@ static void readfollower(MYFILE *f) // Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead. followers[numfollowers].scale = FRACUNIT; followers[numfollowers].atangle = 230; - followers[numfollowers].dist = 16; + followers[numfollowers].dist = 32; // changed from 16 to 32 to better account for ogl models followers[numfollowers].height = 16; followers[numfollowers].zoffs = 32; followers[numfollowers].horzlag = 2; @@ -728,6 +728,7 @@ static void readfollower(MYFILE *f) followers[numfollowers].bobspeed = TICRATE*2; followers[numfollowers].bobamp = 4; followers[numfollowers].hitconfirmtime = TICRATE; + followers[numfollowers].defaultcolor = 1; do { @@ -762,6 +763,12 @@ static void readfollower(MYFILE *f) strcpy(followers[numfollowers].name, word2); nameset = true; } + else if (fastcmp(word, "DEFAULTCOLOR")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].defaultcolor), UNDO_NONE); + followers[numfollowers].defaultcolor = get_number(word2); + } + else if (fastcmp(word, "SCALE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE); @@ -806,7 +813,7 @@ static void readfollower(MYFILE *f) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].height), UNDO_NONE); followers[numfollowers].height = (INT32)atoi(word2); - } + } else if (fastcmp(word, "IDLESTATE")) { if (word2) @@ -910,6 +917,14 @@ if (followers[numfollowers].field < threshold) \ FALLBACK(bobamp, "BOBAMP", 0, 0); FALLBACK(bobspeed, "BOBSPEED", 0, 0); FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1); + + // Special case for color I suppose + if (followers[numfollowers].defaultcolor < 0 || followers[numfollowers].defaultcolor > MAXSKINCOLORS-1) + { + followers[numfollowers].defaultcolor = 1; + deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, MAXSKINCOLORS-1); + } + #undef FALLBACK // also check if we forgot states. If we did, we will set any missing state to the follower's idlestate. diff --git a/src/g_game.c b/src/g_game.c index 2fa4b8e7a..838f1f244 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2561,6 +2561,7 @@ void G_PlayerReborn(INT32 player) UINT8 kartweight; boolean followerready; INT32 followerskin; + UINT8 followercolor; mobj_t *follower; // old follower, will probably be removed by the time we're dead but you never know. // INT32 charflags; @@ -2624,6 +2625,7 @@ void G_PlayerReborn(INT32 player) kartweight = players[player].kartweight; follower = players[player].follower; followerready = players[player].followerready; + followercolor = players[player].followercolor; followerskin = players[player].followerskin; // charflags = players[player].charflags; @@ -2740,6 +2742,7 @@ void G_PlayerReborn(INT32 player) p->followerready = followerready; p->followerskin = followerskin; + p->followercolor = followercolor; p->follower = NULL; // respawn a new one with you, it looks better. @@ -5010,6 +5013,18 @@ void G_ReadDemoExtraData(void) M_Memcpy(name, demo_p, 16); demo_p += 16; SetPlayerFollower(p, name); + + // Follower's color + M_Memcpy(name, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i], name)) // SRB2kart + { + players[p].followercolor = i; + break; + } + + } if (extradata & DXD_PLAYSTATE) { @@ -5740,7 +5755,7 @@ void G_GhostTicker(void) if (ziptic & DXD_NAME) g->p += 16; // yea if (ziptic & DXD_FOLLOWER) - g->p += 16; // ok + 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 } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 3276bfa73..5a5b770b3 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -158,6 +158,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->followerskin); else if (fastcmp(field,"followerready")) lua_pushboolean(L, plr->followerready); + else if (fastcmp(field,"followercolor")) + lua_pushinteger(L, plr->followercolor); else if (fastcmp(field,"follower")) LUA_PushUserdata(L, plr->follower, META_MOBJ); // @@ -297,7 +299,7 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->awayviewtics); else if (fastcmp(field,"awayviewaiming")) lua_pushangle(L, plr->awayviewaiming); - + else if (fastcmp(field,"spectator")) lua_pushboolean(L, plr->spectator); else if (fastcmp(field,"bot")) @@ -411,6 +413,8 @@ static int player_set(lua_State *L) plr->kartweight = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"followerskin")) plr->followerskin = luaL_checkinteger(L, 3); + else if (fastcmp(field,"followercolor")) + plr->followercolor = luaL_checkinteger(L, 3); else if (fastcmp(field,"followerready")) plr->followerready = luaL_checkboolean(L, 3); else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. diff --git a/src/p_saveg.c b/src/p_saveg.c index da2e77b00..a7f13a874 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -283,6 +283,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].followerskin); WRITEUINT8(save_p, players[i].followerready); // booleans are really just numbers eh?? + WRITEUINT8(save_p, players[i].followercolor); if (flags & FOLLOWER) WRITEUINT32(save_p, players[i].follower->mobjnum); @@ -463,6 +464,7 @@ static void P_NetUnArchivePlayers(void) players[i].followerskin = READUINT8(save_p); players[i].followerready = READUINT8(save_p); + players[i].followercolor = READUINT8(save_p); if (flags & FOLLOWER) players[i].follower = (mobj_t *)(size_t)READUINT32(save_p); diff --git a/src/p_user.c b/src/p_user.c index f49c3bc97..e4cd92061 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8082,6 +8082,7 @@ static void P_HandleFollower(player_t *player) angle_t an; fixed_t zoffs; fixed_t sx, sy, sz; + UINT8 color; if (!player->followerready) return; // we aren't ready to perform anything follower related yet. @@ -8120,6 +8121,12 @@ static void P_HandleFollower(player_t *player) fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK); sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); + // extra step, give the follower a color !? + color = player->followercolor; + // little extra check to make sure this isn't garbage: + if (!color || color > MAXSKINCOLORS-1) + color = player->skincolor; // "Match" option. Essentially a fallback as well. + if (!player->follower) // follower doesn't exist / isn't valid { //CONS_Printf("Spawning follower...\n"); @@ -8127,6 +8134,7 @@ static void P_HandleFollower(player_t *player) player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); P_SetFollowerState(player->follower, fl.idlestate); P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear + player->follower->angle = player->mo->angle; player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use. /* @@ -8158,7 +8166,12 @@ static void P_HandleFollower(player_t *player) player->follower->momy = (sy - player->follower->y)/fl.horzlag; player->follower->momz = (sz - player->follower->z)/fl.vertlag; player->follower->angle = player->mo->angle; - player->follower->color = player->mo->color; + + if (player->mo->colorized) + player->follower->color = player->mo->color; + else + player->follower->color = color; + player->follower->colorized = player->mo->colorized; P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); diff --git a/src/r_data.h b/src/r_data.h index 86ba0885b..420c2507c 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -60,6 +60,7 @@ extern INT16 color8to16[256]; // remap color index to highcolor extern INT16 *hicolormaps; // remap high colors to high colors.. extern CV_PossibleValue_t Color_cons_t[]; +extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option. // Load TEXTURE1/TEXTURE2/PNAMES definitions, create lookup tables void R_LoadTextures(void); diff --git a/src/r_draw.c b/src/r_draw.c index 70e487342..cede76dae 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -147,6 +147,7 @@ static UINT8** translationtablecache[TT_CACHE_SIZE] = {NULL}; // SKINCOLOR DEFINITIONS HAVE BEEN MOVED TO K_KART.C CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; +CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+2]; /** \brief The R_InitTranslationTables diff --git a/src/r_things.c b/src/r_things.c index 0a718409a..7cd15a7a9 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3045,7 +3045,7 @@ void SetFollower(INT32 playernum, INT32 skinnum) player_t *player = &players[playernum]; player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. - if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! + if (skinnum >= -1 && skinnum <= numfollowers && player->followerskin != skinnum) // Make sure it exists! { player->followerskin = skinnum; //CONS_Printf("Updated player follower num\n"); diff --git a/src/r_things.h b/src/r_things.h index 304500d6e..859d1be0f 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -110,6 +110,8 @@ typedef struct follower_s char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything. char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this. + UINT8 defaultcolor; // default color for menus. + fixed_t scale; // Scale relative to the player's. // some position shenanigans: From c687f3088e7c9acf5c00b58ac6681be8e61c2368 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 18:18:45 +0200 Subject: [PATCH 067/211] Cast defaultcolor to UINT8 --- src/dehacked.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index 3d633e518..eb3a293fc 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -766,7 +766,7 @@ static void readfollower(MYFILE *f) else if (fastcmp(word, "DEFAULTCOLOR")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].defaultcolor), UNDO_NONE); - followers[numfollowers].defaultcolor = get_number(word2); + followers[numfollowers].defaultcolor = (UINT8)get_number(word2); } else if (fastcmp(word, "SCALE")) From a8e642b39477dea8ffead359abd65a61d3512edb Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 4 May 2020 21:19:28 +0200 Subject: [PATCH 068/211] Accidentally put this check in the wrong place --- src/r_things.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/r_things.c b/src/r_things.c index 7cd15a7a9..1b8cd3fe6 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3045,20 +3045,21 @@ void SetFollower(INT32 playernum, INT32 skinnum) player_t *player = &players[playernum]; player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. - if (skinnum >= -1 && skinnum <= numfollowers && player->followerskin != skinnum) // Make sure it exists! + if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! { - player->followerskin = skinnum; - //CONS_Printf("Updated player follower num\n"); /* We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself. However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it. */ - if (player->follower) + if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins { P_RemoveMobj(player->follower); player->follower = NULL; } + player->followerskin = skinnum; + //CONS_Printf("Updated player follower num\n"); + // for replays: We have changed our follower mid-game; let the game know so it can do the same in the replay! demo_extradata[playernum] |= DXD_FOLLOWER; From 9b1ec816220c52b2eade580adc1d0bc5a16c0e89 Mon Sep 17 00:00:00 2001 From: lachwright Date: Sat, 9 May 2020 06:24:53 +0800 Subject: [PATCH 069/211] Start hill-parking --- src/g_game.c | 14 +++++++------- src/k_kart.c | 7 ++++--- src/p_mobj.c | 2 +- src/p_slopes.c | 4 ++++ src/p_user.c | 12 ------------ 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index a97143d21..e18f4788c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1248,7 +1248,7 @@ INT32 JoyAxis(axis_input_e axissel, UINT8 p) INT32 localaiming[MAXSPLITSCREENPLAYERS]; angle_t localangle[MAXSPLITSCREENPLAYERS]; -static fixed_t forwardmove[2] = {25<>16, 50<>16}; +static fixed_t forwardmove = 50<>16; static fixed_t sidemove[2] = {2<>16, 4<>16}; static fixed_t angleturn[3] = {KART_FULLTURN/2, KART_FULLTURN, KART_FULLTURN/4}; // + slow turn @@ -1418,9 +1418,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->buttons |= BT_BRAKE; axis = JoyAxis(AXISAIM, ssplayer); if (InputDown(gc_aimforward, ssplayer) || (usejoystick && axis < 0)) - forward += forwardmove[1]; + forward += forwardmove; if (InputDown(gc_aimbackward, ssplayer) || (usejoystick && axis > 0)) - forward -= forwardmove[1]; + forward -= forwardmove; } else { @@ -1429,13 +1429,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (InputDown(gc_accelerate, ssplayer) || (gamepadjoystickmove && axis > 0) || EITHERSNEAKER(player)) { cmd->buttons |= BT_ACCELERATE; - forward = forwardmove[1]; // 50 + forward = forwardmove; // 50 } else if (analogjoystickmove && axis > 0) { cmd->buttons |= BT_ACCELERATE; // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - forward += ((axis * forwardmove[1]) >> 10)*2; + forward += ((axis * forwardmove) >> 10)*2; } axis = JoyAxis(AXISBRAKE, ssplayer); @@ -1443,14 +1443,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { cmd->buttons |= BT_BRAKE; if (cmd->buttons & BT_ACCELERATE || cmd->forwardmove <= 0) - forward -= forwardmove[0]; // 25 - Halved value so clutching is possible + forward -= forwardmove; } else if (analogjoystickmove && axis > 0) { cmd->buttons |= BT_BRAKE; // JOYAXISRANGE is supposed to be 1023 (divide by 1024) if (cmd->buttons & BT_ACCELERATE || cmd->forwardmove <= 0) - forward -= ((axis * forwardmove[0]) >> 10); + forward -= ((axis * forwardmove) >> 10); } // But forward/backward IS used for aiming. diff --git a/src/k_kart.c b/src/k_kart.c index 2a672af13..bdbd37927 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2728,7 +2728,6 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove // forwardmove is: // 50 while accelerating, - // 25 while clutching, // 0 with no gas, and // -25 when only braking. @@ -7889,11 +7888,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // Friction if (!player->kartstuff[k_offroad]) { - if (player->speed > 0 && cmd->forwardmove == 0 && player->mo->friction == 59392) + if (player->speed > 0 && cmd->forwardmove == 0 && !(cmd->buttons & BT_BRAKE) && player->mo->friction == 59392) player->mo->friction += 4608; } - if (player->speed > 0 && cmd->forwardmove < 0) // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel + if ((cmd->buttons & (BT_BRAKE|BT_ACCELERATE)) == (BT_BRAKE|BT_ACCELERATE) && !(player->kartstuff[k_drift])) + player->mo->friction -= 3072; + else if (player->speed > 0 && cmd->forwardmove < 0) // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel player->mo->friction -= 2048; // Karma ice physics diff --git a/src/p_mobj.c b/src/p_mobj.c index 1a7c562a7..56edd4f59 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1439,7 +1439,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) && abs(player->rmomy) < FixedMul(STOPSPEED, mo->scale) && (!(player->cmd.forwardmove && !(twodlevel || mo->flags2 & MF2_TWOD)) && !player->cmd.sidemove && !(player->pflags & PF_SPINNING)) #ifdef ESLOPE - && !(player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && (abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)) + && !(player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)))// && (abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)) #endif ) { diff --git a/src/p_slopes.c b/src/p_slopes.c index 0825ebca6..ffb50f407 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -883,6 +883,10 @@ void P_ButteredSlope(mobj_t *mo) return; // don't slide down slopes if you can't touch them or you're not affected by gravity if (mo->player) { + // SRB2Kart - spindash negates slopes + if (((mo->player->cmd.buttons & (BT_BRAKE|BT_ACCELERATE)) == (BT_BRAKE|BT_ACCELERATE)) && !mo->player->kartstuff[k_drift]) + return; + // Changed in kart to only not apply physics on very slight slopes (I think about 4 degree angles) if (abs(mo->standingslope->zdelta) < FRACUNIT/21 && !(mo->player->pflags & PF_SPINNING)) return; // Don't slide on non-steep slopes unless spinning diff --git a/src/p_user.c b/src/p_user.c index 54e2350b6..a5bcd5653 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4108,18 +4108,6 @@ static void P_3dMovement(player_t *player) if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... movepushforward = FixedMul(movepushforward, player->mo->movefactor); - if (cmd->buttons & BT_BRAKE && !cmd->forwardmove) // SRB2kart - braking isn't instant - movepushforward /= 64; - - if (cmd->forwardmove > 0) - player->kartstuff[k_brakestop] = 0; - else if (player->kartstuff[k_brakestop] < 6) // Don't start reversing with brakes until you've made a stop first - { - if (player->speed < 8*FRACUNIT) - player->kartstuff[k_brakestop]++; - movepushforward = 0; - } - #ifdef ESLOPE totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward); totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward); From be96831645de7795e15e483a228a6d9af21c3a8d Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 9 May 2020 11:51:11 +0200 Subject: [PATCH 070/211] Forgot to save followercolor. This fixes replay crashes --- src/g_game.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 838f1f244..d42d9c7bf 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5013,7 +5013,7 @@ void G_ReadDemoExtraData(void) M_Memcpy(name, demo_p, 16); demo_p += 16; SetPlayerFollower(p, name); - + // Follower's color M_Memcpy(name, demo_p, 16); demo_p += 16; @@ -5022,9 +5022,9 @@ void G_ReadDemoExtraData(void) { players[p].followercolor = i; break; - } + } + - } if (extradata & DXD_PLAYSTATE) { @@ -5153,6 +5153,13 @@ void G_WriteDemoExtraData(void) strncpy(name, followers[players[i].followerskin].skinname, 16); M_Memcpy(demo_p, name, 16); demo_p += 16; + + // write follower color + memset(name, 0, 16); + strncpy(name, KartColor_Names[players[i].followercolor], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + } if (demo_extradata[i] & DXD_PLAYSTATE) { From 6e016b3f0bbc8c7290f1f38033a98863312595dc Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 9 May 2020 11:57:33 +0200 Subject: [PATCH 071/211] Use Followercolor_cons_t to account for extra values and avoid more dumb crashes --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index d42d9c7bf..22dc73b86 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5156,7 +5156,7 @@ void G_WriteDemoExtraData(void) // write follower color memset(name, 0, 16); - strncpy(name, KartColor_Names[players[i].followercolor], 16); + strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" M_Memcpy(demo_p,name,16); demo_p += 16; From 3f43107eea7169e41887d0c7f96292951c96d82c Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sat, 9 May 2020 12:12:29 +0200 Subject: [PATCH 072/211] Add Opposite option for followercolor --- src/d_netcmd.c | 7 +++++-- src/p_user.c | 25 ++++++++++++++++++++----- src/r_draw.c | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d4ef8f22c..09720b330 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -819,12 +819,15 @@ void D_RegisterClientCommands(void) Color_cons_t[i].value = Followercolor_cons_t[i].value = i; Color_cons_t[i].strvalue = Followercolor_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart } - Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+1].value = 0; - Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+1].strvalue = NULL; + Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0; + Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL; Followercolor_cons_t[MAXSKINCOLORS].value = MAXSKINCOLORS; Followercolor_cons_t[MAXSKINCOLORS].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's + Followercolor_cons_t[MAXSKINCOLORS+1].value = MAXSKINCOLORS+1; + Followercolor_cons_t[MAXSKINCOLORS+1].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite. + if (dedicated) return; diff --git a/src/p_user.c b/src/p_user.c index e4cd92061..1dd56eeab 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8121,11 +8121,26 @@ static void P_HandleFollower(player_t *player) fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK); sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); - // extra step, give the follower a color !? - color = player->followercolor; - // little extra check to make sure this isn't garbage: - if (!color || color > MAXSKINCOLORS-1) - color = player->skincolor; // "Match" option. Essentially a fallback as well. + // Set follower colour + + switch (player->followercolor) + { + case MAXSKINCOLORS: // "Match" + color = player->skincolor; + break; + case MAXSKINCOLORS+1: // "Opposite" + color = KartColor_Opposite[player->skincolor*2]; + break; + default: + + color = player->followercolor; + if (!color || color > MAXSKINCOLORS+2) // Make sure this isn't garbage + color = player->skincolor; // "Match" as fallback. + + break; + } + + if (!player->follower) // follower doesn't exist / isn't valid { diff --git a/src/r_draw.c b/src/r_draw.c index cede76dae..2097982d3 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -147,7 +147,7 @@ static UINT8** translationtablecache[TT_CACHE_SIZE] = {NULL}; // SKINCOLOR DEFINITIONS HAVE BEEN MOVED TO K_KART.C CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; -CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+2]; +CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL /** \brief The R_InitTranslationTables From 2bee969c6a2803247956b852f424012514337a89 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 11 May 2020 00:16:13 +0200 Subject: [PATCH 073/211] Add follower bubbles with the BUBBLESCALE field --- src/dehacked.c | 13 ++++++++++ src/info.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/info.h | 7 ++++++ src/p_mobj.c | 20 +++++++++++++--- src/p_user.c | 40 +++++++++++++++++++++++++++++++ src/r_things.c | 13 ++++++++++ src/r_things.h | 1 + 7 files changed, 153 insertions(+), 5 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index eb3a293fc..708d41cb6 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -719,6 +719,7 @@ static void readfollower(MYFILE *f) // Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead. followers[numfollowers].scale = FRACUNIT; + followers[numfollowers].bubblescale = 0; // No bubble by default followers[numfollowers].atangle = 230; followers[numfollowers].dist = 32; // changed from 16 to 32 to better account for ogl models followers[numfollowers].height = 16; @@ -774,6 +775,11 @@ static void readfollower(MYFILE *f) DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE); followers[numfollowers].scale = get_number(word2); } + else if (fastcmp(word, "BUBBLESCALE")) + { + DEH_WriteUndoline(word, va("%d", followers[numfollowers].bubblescale), UNDO_NONE); + followers[numfollowers].bubblescale = get_number(word2); + } else if (fastcmp(word, "ATANGLE")) { DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE); @@ -917,6 +923,8 @@ if (followers[numfollowers].field < threshold) \ FALLBACK(bobamp, "BOBAMP", 0, 0); FALLBACK(bobspeed, "BOBSPEED", 0, 0); FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1); + FALLBACK(scale, "SCALE", 1, 1); // No null/negative scale + FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale // Special case for color I suppose if (followers[numfollowers].defaultcolor < 0 || followers[numfollowers].defaultcolor > MAXSKINCOLORS-1) @@ -7546,6 +7554,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_GCHAOHAPPY3", "S_GCHAOHAPPY4", + "S_FOLLOWERBUBBLE_FRONT", + "S_FOLLOWERBUBBLE_BACK", + "S_CHEESEIDLE", "S_CHEESEFLY", "S_CHEESESAD1", @@ -8404,6 +8415,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BATTLECAPSULE_PIECE", "MT_FOLLOWER", + "MT_FOLLOWERBUBBLE_FRONT", + "MT_FOLLOWERBUBBLE_BACK", #ifdef SEENAMES "MT_NAMECHECK", diff --git a/src/info.c b/src/info.c index 780ec765b..75fb042be 100644 --- a/src/info.c +++ b/src/info.c @@ -73,8 +73,8 @@ char sprnames[NUMSPRITES + 1][5] = "ICEB","CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS", "ZTCH","MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH", "BFRT","OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN", - "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","XMS4","XMS5","GCHA", - "CHEZ","VIEW" + "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","XMS4","XMS5","FBUB", + "GCHA","CHEZ","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -3513,6 +3513,10 @@ state_t states[NUMSTATES] = // followers: + // bubble + {SPR_FBUB, 11|FF_ANIMATE|FF_TRANS70|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_FRONT}, // S_FOLLOWERBUBBLE_FRONT + {SPR_FBUB, FF_ANIMATE|0|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_BACK}, // S_FOLLOWERBUBBLE_BACK + // generic chao: {SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE {SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY @@ -20854,6 +20858,62 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // raisestate }, + { // MT_FOLLOWERBUBBLE_FRONT + -1, // doomednum + S_FOLLOWERBUBBLE_FRONT, // 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<target || P_MobjWasRemoved(mobj->target) || !mobj->target->player || mobj->target->player->spectator || mobj->target->player->followerskin < 0) + { + // Remove possible hnext list (bubble) + mobj_t *bub = mobj->hnext; + mobj_t *tmp; + + while (bub && !P_MobjWasRemoved(bub)) + { + tmp = bub->hnext; + P_RemoveMobj(bub); + bub = tmp; + } + P_RemoveMobj(mobj); - + } + return; + case MT_HOOP: if (mobj->fuse > 1) P_MoveHoop(mobj); @@ -8798,7 +8812,7 @@ void P_MobjThinker(mobj_t *mobj) if (curstate >= S_FLAMESHIELD1 && curstate < S_FLAMESHIELDDASH1 && ((curstate-S_FLAMESHIELD1) & 1)) viewingangle += ANGLE_180; - + destx = mobj->target->x + P_ReturnThrustX(mobj->target, viewingangle, mobj->scale>>4); desty = mobj->target->y + P_ReturnThrustY(mobj->target, viewingangle, mobj->scale>>4); } @@ -11847,7 +11861,7 @@ void P_SpawnPlayer(INT32 playernum) //awayview stuff p->awayviewmobj = NULL; p->awayviewtics = 0; - + p->follower = NULL; // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. diff --git a/src/p_user.c b/src/p_user.c index 1dd56eeab..83ad76bc5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8084,6 +8084,10 @@ static void P_HandleFollower(player_t *player) fixed_t sx, sy, sz; UINT8 color; + fixed_t bubble; // bubble scale (0 if no bubble) + mobj_t *bmobj; // temp bubble mobj + + if (!player->followerready) return; // we aren't ready to perform anything follower related yet. @@ -8105,6 +8109,7 @@ static void P_HandleFollower(player_t *player) an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things... zoffs = (fl.zoffs)*FRACUNIT; + bubble = fl.bubblescale; // 0 if no bubble to spawn. // do you like angle maths? I certainly don't... sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT)); @@ -8151,6 +8156,18 @@ static void P_HandleFollower(player_t *player) P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear player->follower->angle = player->mo->angle; + // This is safe to only spawn it here, the follower is removed then respawned when switched. + if (bubble) + { + bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_FRONT); + P_SetTarget(&player->follower->hnext, bmobj); + P_SetTarget(&bmobj->target, player->follower); // Used to know if we have to despawn at some point. + + bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_BACK); + P_SetTarget(&player->follower->hnext->hnext, bmobj); // this seems absolutely stupid, I know, but this will make updating the momentums/flags of these a bit easier. + P_SetTarget(&bmobj->target, player->follower); // Ditto + } + player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use. /* 0 = idle @@ -8205,6 +8222,29 @@ static void P_HandleFollower(player_t *player) // 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 + // Finally, if the follower has bubbles, move them, set their scale, etc.... + // This is what I meant earlier by it being easier, now we can just use this weird lil loop to get the job done! + + bmobj = player->follower->hnext; // will be NULL if there's no bubble + + while (bmobj && !P_MobjWasRemoved(bmobj)) + { + // match follower's momentums and (e)flags(2). + bmobj->momx = player->follower->momx; + bmobj->momy = player->follower->momy; + bmobj->momz = player->follower->momz; + + P_SetScale(bmobj, FixedMul(bubble, player->mo->scale)); + K_GenericExtraFlagsNoZAdjust(bmobj, player->follower); + bmobj->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); + + if (player->follower->threshold) // threshold means the follower was "despawned" with S_NULL (is actually just set to S_INVISIBLE) + P_SetMobjState(bmobj, S_INVISIBLE); // sooooo... let's do the same! + + bmobj = bmobj->hnext; // switch to other bubble layer or exit + } + + if (player->follower->threshold) return; // Threshold means the follower was "despanwed" with S_NULL. diff --git a/src/r_things.c b/src/r_things.c index 1b8cd3fe6..2eff0bd2a 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3043,6 +3043,8 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) void SetFollower(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; + mobj_t *bub; + mobj_t *tmp; player->followerready = true; // we are ready to perform follower related actions in the player thinker, now. if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists! @@ -3053,6 +3055,17 @@ void SetFollower(INT32 playernum, INT32 skinnum) */ if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins { + + // Remove follower's possible hnext list (bubble) + bub = player->follower->hnext; + + while (bub && !P_MobjWasRemoved(bub)) + { + tmp = bub->hnext; + P_RemoveMobj(bub); + bub = tmp; + } + P_RemoveMobj(player->follower); player->follower = NULL; } diff --git a/src/r_things.h b/src/r_things.h index 859d1be0f..fc2cae479 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -113,6 +113,7 @@ typedef struct follower_s UINT8 defaultcolor; // default color for menus. fixed_t scale; // Scale relative to the player's. + fixed_t bubblescale; // Bubble scale relative to the player scale. If not set, no bubble will spawn (default) // some position shenanigans: INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player. From e045672559206d9223bc61dc984948b7c0ed95ea Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 May 2020 17:51:27 -0700 Subject: [PATCH 074/211] Use new two digit version number for HMS --- src/hms123311.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index 6e749e3bf..d643d1ea7 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -337,14 +337,13 @@ HMS_register (void) snprintf(post, sizeof post, "port=%d&" "title=%s&" - "version=%d.%d.%d", + "version=%d.%d", current_port, title, - VERSION/100, - VERSION%100, + VERSION, SUBVERSION ); @@ -480,9 +479,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id) doing_shit = 1; snprintf(local_version, sizeof local_version, - "%d.%d.%d", - VERSION/100, - VERSION%100, + "%d.%d", + VERSION, SUBVERSION ); From a60229c238696db894c413299183e7fefc13e30e Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 May 2020 18:52:05 -0700 Subject: [PATCH 075/211] Use IPv4 for master server connections Your server's address is gathered from the request, so it needs to be IPv4! --- src/hms123311.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hms123311.c b/src/hms123311.c index d643d1ea7..e50d18863 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -144,6 +144,7 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); From d9368f2e6380889654a9fc699cbe9f07e6fd6433 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 May 2020 19:22:15 -0700 Subject: [PATCH 076/211] Clarify where debug prints go when using masterserver_debug --- src/hms123311.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index e50d18863..6b1044176 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -28,9 +28,11 @@ Documentation available here. #define Blame( ... ) \ CONS_Printf("\x85" __VA_ARGS__) +static void MasterServer_Debug_OnChange (void); + consvar_t cv_masterserver_debug = { - "masterserver_debug", "Off", CV_SAVE, CV_OnOff, - NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ + "masterserver_debug", "Off", CV_SAVE|CV_CALL, CV_OnOff, + MasterServer_Debug_OnChange, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ }; static int hms_started; @@ -618,3 +620,11 @@ HMS_set_api (char *api) I_unlock_mutex(hms_api_mutex); #endif } + +static void +MasterServer_Debug_OnChange (void) +{ + /* TODO: change to 'latest-log.txt' for log files revision. */ + if (cv_masterserver_debug.value) + CONS_Printf("Master server debug messages will appear in log.txt\n"); +} From 5e0d79b8334c9d41090739743d97a80c6b72f05b Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 May 2020 19:22:30 -0700 Subject: [PATCH 077/211] Reset the masterserver address if the old one was set by the config --- src/mserv.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/mserv.c b/src/mserv.c index fe11307f5..be54d6066 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -510,6 +510,17 @@ static void MasterServer_OnChange(void) { UnregisterServer(); + /* + TODO: remove this for v2, it's just a hack + for those coming in with an old config. + */ + if ( + ! cv_masterserver.changed && + strcmp(cv_masterserver.string, "ms.srb2.org:28900") == 0 + ){ + CV_StealthSet(&cv_masterserver, cv_masterserver.defaultvalue); + } + Set_api(cv_masterserver.string); if (Online()) From 47c72219e848320071fd604daad623d3058716ed Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 12 May 2020 19:27:27 -0700 Subject: [PATCH 078/211] Identify this branch with VERSIONSTRING This is temporary. --- src/doomdef.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doomdef.h b/src/doomdef.h index ed41e346e..bee6e6501 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -155,8 +155,8 @@ extern char logfilename[1024]; #else #define VERSION 1 // Game version #define SUBVERSION 2 // more precise version number -#define VERSIONSTRING "v1.2" -#define VERSIONSTRINGW L"v1.2" +#define VERSIONSTRING "v1.2 (HTTP MS)" +#define VERSIONSTRINGW L"v1.2 (HTTP MS)" // Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates! // And change CMakeLists.txt, for CMake users! // AND appveyor.yml, for the build bots! From 36071f52cf1e7a1cef5bb0bf79b870ed749860ed Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 13 May 2020 15:29:17 -0700 Subject: [PATCH 079/211] Bind HMS connection with -bindaddr --- src/hms123311.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/hms123311.c b/src/hms123311.c index 6b1044176..b7db4d909 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -19,6 +19,7 @@ Documentation available here. #include "doomdef.h" #include "d_clisrv.h" #include "command.h" +#include "m_argv.h" #include "m_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ @@ -144,6 +145,11 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_STDERR, logstream); } + if (M_CheckParm("-bindaddr") && M_IsNextParm()) + { + curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm()); + } + curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); From bf30ad91e4863232a2f63830f64a8e0c141e5829 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Thu, 14 May 2020 01:41:06 +0200 Subject: [PATCH 080/211] Don't save Luavars in record attack, especially not for ghosts --- src/g_game.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index bf0557d02..dc5874b68 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6368,7 +6368,9 @@ void G_BeginRecording(void) demoflags |= DF_ENCORE; #ifdef HAVE_BLUA - demoflags |= DF_LUAVARS; + if (!modeattacking) // Ghosts don't read luavars, and you shouldn't ever need to save Lua in replays, you doof! + // SERIOUSLY THOUGH WHY WOULD YOU LOAD HOSTMOD AND RECORD A GHOST WITH IT !???? + demoflags |= DF_LUAVARS; #endif // Setup header. @@ -6474,8 +6476,9 @@ void G_BeginRecording(void) WRITEUINT8(demo_p, 0xFF); // Denote the end of the player listing #ifdef HAVE_BLUA - // player lua vars, always saved even if empty - LUA_ArchiveDemo(); + // player lua vars, always saved even if empty... Unless it's record attack. + if (!modeattacking) + LUA_ArchiveDemo(); #endif memset(&oldcmd,0,sizeof(oldcmd)); @@ -7543,6 +7546,7 @@ void G_DoPlayDemo(char *defdemoname) if (!gL) // No Lua state! ...I guess we'll just start one... LUA_ClearState(); + // No modeattacking check, DF_LUAVARS won't be present here. LUA_UnArchiveDemo(); } #endif From a73e4f135e6126284fb17f0c2743223ccb8b808d Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 14 May 2020 17:19:25 -0700 Subject: [PATCH 081/211] Resize response body buffer as needed --- src/hms123311.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/hms123311.c b/src/hms123311.c index b7db4d909..9e17e3614 100644 --- a/src/hms123311.c +++ b/src/hms123311.c @@ -25,6 +25,9 @@ Documentation available here. #include "i_tcp.h"/* for current_port */ #include "i_threads.h" +/* reasonable default I guess?? */ +#define DEFAULT_BUFFER_SIZE (4096) + /* I just stop myself from making macros anymore. */ #define Blame( ... ) \ CONS_Printf("\x85" __VA_ARGS__) @@ -65,16 +68,25 @@ static size_t HMS_on_read (char *s, size_t _1, size_t n, void *userdata) { struct HMS_buffer *buffer; + size_t blocks; + (void)_1; + buffer = userdata; - if (n < (size_t)( buffer->end - buffer->needle )) + + if (n >= (size_t)( buffer->end - buffer->needle )) { - memcpy(&buffer->buffer[buffer->needle], s, n); - buffer->needle += n; - return n; + /* resize to next multiple of buffer size */ + blocks = ( n / DEFAULT_BUFFER_SIZE + 1 ); + buffer->end = ( blocks * DEFAULT_BUFFER_SIZE ); + + buffer->buffer = realloc(buffer->buffer, buffer->end); } - else - return 0; + + memcpy(&buffer->buffer[buffer->needle], s, n); + buffer->needle += n; + + return n; } static struct HMS_buffer * @@ -134,8 +146,7 @@ HMS_connect (const char *format, ...) buffer = malloc(sizeof *buffer); buffer->curl = curl; - /* I just allocated 4k and fuck it! */ - buffer->end = 4096; + buffer->end = DEFAULT_BUFFER_SIZE; buffer->buffer = malloc(buffer->end); buffer->needle = 0; From f82ff5c3e71e4cd36a3b5ff5ace656c021b81d0d Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 14 May 2020 17:23:06 -0700 Subject: [PATCH 082/211] Use Unlock_state here too --- src/console.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/console.c b/src/console.c index 09b4617ba..11a7961e8 100644 --- a/src/console.c +++ b/src/console.c @@ -1358,9 +1358,7 @@ static void CON_Print(char *msg) if (*msg == '\0') { -#ifdef HAVE_THREADS - I_unlock_mutex(con_mutex); -#endif + Unlock_state(); return; } From 8a006fef182fa498c5c1964ab033f570190c82bf Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 14 May 2020 17:24:49 -0700 Subject: [PATCH 083/211] Rename hms123311.c to http-mserv.c HMS lives on in our hearts! --- src/Makefile | 2 +- src/{hms123311.c => http-mserv.c} | 0 src/mserv.h | 2 -- 3 files changed, 1 insertion(+), 3 deletions(-) rename src/{hms123311.c => http-mserv.c} (100%) diff --git a/src/Makefile b/src/Makefile index 75ea0de19..dd678cb26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -548,7 +548,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/w_wad.o \ $(OBJDIR)/filesrch.o \ $(OBJDIR)/mserv.o \ - $(OBJDIR)/hms123311.o\ + $(OBJDIR)/http-mserv.o\ $(OBJDIR)/i_tcp.o \ $(OBJDIR)/lzf.o \ $(OBJDIR)/vid_copy.o \ diff --git a/src/hms123311.c b/src/http-mserv.c similarity index 100% rename from src/hms123311.c rename to src/http-mserv.c diff --git a/src/mserv.h b/src/mserv.h index 06c91b334..d13dbdd58 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -16,8 +16,6 @@ #include "i_threads.h" -#define HMS123311 // don't mess with nights, man - // lowered from 32 due to menu changes #define NUM_LIST_ROOMS 16 From d8220e6748cbee7567d3db766224f385989bb19a Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 13 May 2020 17:52:49 -0700 Subject: [PATCH 084/211] ok (cherry picked from commit 0b9c20cc7086000548e02b39c1abf94ffb56feae) --- src/m_menu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 24852285c..d05026927 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8609,19 +8609,19 @@ static void Check_new_version_thread (int *id) { int hosting; - int ok; + int okay; - ok = 0; + okay = 0; if (M_CheckMODVersion(*id)) { I_lock_mutex(&ms_QueryId_mutex); { - ok = ( *id == ms_QueryId ); + okay = ( *id == ms_QueryId ); } I_unlock_mutex(ms_QueryId_mutex); - if (ok) + if (okay) { I_lock_mutex(&m_menu_mutex); { @@ -8637,12 +8637,12 @@ Check_new_version_thread (int *id) { I_lock_mutex(&ms_QueryId_mutex); { - ok = ( *id == ms_QueryId ); + okay = ( *id == ms_QueryId ); } I_unlock_mutex(ms_QueryId_mutex); } - if (ok) + if (okay) { I_lock_mutex(&m_menu_mutex); { From 42484ae5ca2e721a448d18a26a34b8cbe07305d7 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 15 May 2020 13:20:40 -0700 Subject: [PATCH 085/211] Set timeout on HMS connections --- src/http-mserv.c | 6 ++++++ src/mserv.c | 1 + src/mserv.h | 1 + 3 files changed, 8 insertions(+) diff --git a/src/http-mserv.c b/src/http-mserv.c index 9e17e3614..db81ee1e7 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -34,6 +34,11 @@ Documentation available here. static void MasterServer_Debug_OnChange (void); +consvar_t cv_masterserver_timeout = { + "masterserver_timeout", "5", CV_SAVE, CV_Unsigned, + NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + consvar_t cv_masterserver_debug = { "masterserver_debug", "Off", CV_SAVE|CV_CALL, CV_OnOff, MasterServer_Debug_OnChange, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ @@ -165,6 +170,7 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); diff --git a/src/mserv.c b/src/mserv.c index be54d6066..99224ce23 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -87,6 +87,7 @@ void AddMServCommands(void) #ifndef NONET CV_RegisterVar(&cv_masterserver); CV_RegisterVar(&cv_masterserver_update_rate); + CV_RegisterVar(&cv_masterserver_timeout); CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); diff --git a/src/mserv.h b/src/mserv.h index d13dbdd58..943a80d08 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -66,6 +66,7 @@ typedef struct extern consvar_t cv_masterserver, cv_servername; extern consvar_t cv_masterserver_update_rate; +extern consvar_t cv_masterserver_timeout; extern consvar_t cv_masterserver_debug; // < 0 to not connect (usually -1) (offline mode) From 1f74d7533e78c203ee71095af1b641500e31acd5 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 15 May 2020 14:21:25 -0700 Subject: [PATCH 086/211] Properly bound lua displayplayers and displayplayers.iterate to splitscreen This is fixes crashes in replays because splitscreen displayplayers are initialized to INT32_MAX there. --- src/lua_playerlib.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index d9766513b..0ece0d2c6 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -118,7 +118,7 @@ static int lib_iterateDisplayplayers(lua_State *L) for (i++; i < MAXSPLITSCREENPLAYERS; i++) { - if (!playeringame[displayplayers[i]] || i > splitscreen) + 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) @@ -139,6 +139,8 @@ static int lib_getDisplayplayers(lua_State *L) lua_Integer i = luaL_checkinteger(L, 2); if (i < 0 || i >= MAXSPLITSCREENPLAYERS) return luaL_error(L, "displayplayers[] index %d out of range (0 - %d)", i, MAXSPLITSCREENPLAYERS-1); + if (i > splitscreen) + return 0; if (!playeringame[displayplayers[i]]) return 0; if (!players[displayplayers[i]].mo) From ded8ed7177854a78401085cb2cd6a0652ff80b67 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Sun, 27 Oct 2019 16:44:41 -0400 Subject: [PATCH 087/211] Don't force SPB in 1v1 (cherry picked from commit 54516aeee97b18ee307024672fe91fc60d55fd30) --- src/k_kart.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 7ac35cd31..d096a3624 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1061,6 +1061,10 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) bestbumper = players[i].kartstuff[k_bumper]; } + // No forced SPB in 1v1s, it has to be randomly rolled + if (pingame <= 2) + dontforcespb = true; + // This makes the roulette produce the random noises. if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player) && !demo.freecam) { From c55e4424c533a011d007b5b4d4601f8d95c5131a Mon Sep 17 00:00:00 2001 From: Sryder Date: Wed, 20 May 2020 11:53:14 +0100 Subject: [PATCH 088/211] Fix a fallthrough issue that could cause thundershield to be rolled less often than intended. --- src/k_kart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.c b/src/k_kart.c index b96daa5e5..165106fab 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -898,6 +898,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp newodds = 0; else POWERITEMODDS(newodds); + break; case KITEM_HYUDORO: if ((hyubgone > 0) || COOLDOWNONSTART) newodds = 0; From ef855cfc54edca5d3ebfd4d1540c32ad0b3ea8f4 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Mon, 1 Jun 2020 21:14:17 +0300 Subject: [PATCH 089/211] Guil's encore color bugfix with some additional cleanup --- src/p_setup.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index decdc5291..c0254bf4a 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1408,7 +1408,7 @@ static void P_LoadRawSideDefs2(void *data) UINT16 i; INT32 num; size_t j; - UINT32 cr, cg, cb; + RGBA_t color; for (i = 0; i < numsides; i++) { @@ -1490,23 +1490,21 @@ static void P_LoadRawSideDefs2(void *data) // encore mode colormaps! // do it like software by aproximating a color to a palette index, and then convert it to its encore variant and then back to a color code. // do this for both the start and fade colormaps. - - cr = (HEX2INT(col[1]) << 4) + (HEX2INT(col[2]) << 0); - cg = (HEX2INT(col[3]) << 12) + (HEX2INT(col[4]) << 8); - cb = (HEX2INT(col[5]) << 20) + (HEX2INT(col[6]) << 16); + + color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); + color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); + color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); #ifdef GLENCORE if (encoremap) { - j = encoremap[NearestColor((UINT8)cr, (UINT8)cg, (UINT8)cb)]; + j = encoremap[NearestColor(color.s.red, color.s.green, color.s.blue)]; //CONS_Printf("R_CreateColormap: encoremap[%d] = %d\n", j, encoremap[j]); -- moved encoremap upwards for optimisation - cr = pLocalPalette[j].s.red; - cg = pLocalPalette[j].s.green; - cb = pLocalPalette[j].s.blue; + color = pLocalPalette[j]; } #endif - - sec->extra_colormap->rgba = cr + cg + cb; + + sec->extra_colormap->rgba = color.rgba; // alpha if (msd->toptexture[7]) @@ -1533,23 +1531,21 @@ static void P_LoadRawSideDefs2(void *data) col = msd->bottomtexture; // do the exact same thing as above here. - - cr = (HEX2INT(col[1]) << 4) + (HEX2INT(col[2]) << 0); - cg = (HEX2INT(col[3]) << 12) + (HEX2INT(col[4]) << 8); - cb = (HEX2INT(col[5]) << 20) + (HEX2INT(col[6]) << 16); + + color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); + color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); + color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); #ifdef GLENCORE if (encoremap) { - j = encoremap[NearestColor((UINT8)cr, (UINT8)cg, (UINT8)cb)]; + j = encoremap[NearestColor(color.s.red, color.s.green, color.s.blue)]; //CONS_Printf("R_CreateColormap: encoremap[%d] = %d\n", j, encoremap[j]); -- moved encoremap upwards for optimisation - cr = pLocalPalette[j].s.red; - cg = pLocalPalette[j].s.green; - cb = pLocalPalette[j].s.blue; + color = pLocalPalette[j]; } #endif - sec->extra_colormap->fadergba = cr + cg + cb; + sec->extra_colormap->fadergba = color.rgba; // alpha if (msd->bottomtexture[7]) From aba1ff72bc7e357d945bd91bce11d17985075363 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 3 Jun 2020 01:02:06 +0200 Subject: [PATCH 090/211] k_color.h is a thing now --- src/p_user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/p_user.c b/src/p_user.c index e9c7e1744..35077eb1b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -46,6 +46,7 @@ // SRB2kart #include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems #include "k_kart.h" +#include "k_color.h" // KartColor_Opposite #include "console.h" // CON_LogMessage #ifdef HW3SOUND From 5412358fcf9a1e929bbb9977d41532dc949f4f61 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 3 Jun 2020 01:23:32 +0200 Subject: [PATCH 091/211] Fixed setting follower on title screen + mixed declarations --- src/d_netcmd.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f46838c06..2a0396406 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -6084,9 +6084,8 @@ static void Name4_OnChange(void) static void Follower_OnChange(void) { char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1]; - - if (!Playing()) - return; // do whatever you want + INT32 num; + char set[10]; // This isn't Lua and mixed declarations in the middle of code make caveman compilers scream. // there is a slight chance that we will actually use a string instead so... // let's investigate the string... @@ -6098,12 +6097,15 @@ static void Follower_OnChange(void) if (stricmp(cpy, "None") == 0) { CV_StealthSet(&cv_follower, "-1"); + + if (!Playing()) + return; // don't send anything there. + SendNameAndColor(); return; } - INT32 num = R_FollowerAvailable(str); - char set[10]; + num = R_FollowerAvailable(str); if (num == -1) // that's an error. CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); @@ -6111,6 +6113,10 @@ static void Follower_OnChange(void) sprintf(set, "%d", num); CV_StealthSet(&cv_follower, set); // set it to a number. It's easier for us to send later :) } + + if (!Playing()) + return; // don't send anything there. + SendNameAndColor(); } From ef459492028d9dc680ae386c5b7ab719ce4a6381 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 8 Jun 2020 20:36:48 +0200 Subject: [PATCH 092/211] Fix followercolor not being saved in replays --- src/g_game.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 712b8410d..074ddf9d4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6492,6 +6492,12 @@ void G_BeginRecording(void) M_Memcpy(demo_p,name,16); demo_p += 16; + // Save follower's colour + memset(name, 0, 16); + strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + M_Memcpy(demo_p, name, 16); + demo_p += 16; + // Score, since Kart uses this to determine where you start on the map WRITEUINT32(demo_p, player->score); @@ -7425,6 +7431,18 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 16; SetPlayerFollower(p, follower); + // Follower colour + M_Memcpy(color, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS +2; i++) // +2 because of Match and Opposite + { + if (!stricmp(Followercolor_cons_t[i].strvalue, color)) + { + players[p].followercolor = i; + break; + } + } + // Score, since Kart uses this to determine where you start on the map players[p].score = READUINT32(demo_p); @@ -7655,7 +7673,7 @@ void G_AddGhost(char *defdemoname) p += 16; // Follower data was here, skip it, we don't care about it for ghosts. - p += 16; + p += 32; // followerskin (16) + followercolor (16) p += 4; // score p += 2; // powerlevel From 406d7c0b326c85ca29a8d4b28a92e91253cb9025 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Mon, 8 Jun 2020 20:37:27 +0200 Subject: [PATCH 093/211] Remove old debug prints --- src/p_saveg.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/p_saveg.c b/src/p_saveg.c index 8b3f468e7..37f6dd80b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -104,8 +104,6 @@ static void P_NetArchivePlayers(void) UINT16 flags; // size_t q; - CONS_Printf("SENDING NET INFO\n"); - WRITEUINT32(save_p, ARCHIVEBLOCK_PLAYERS); for (i = 0; i < MAXPLAYERS; i++) @@ -308,8 +306,6 @@ static void P_NetUnArchivePlayers(void) INT32 i, j; UINT16 flags; - CONS_Printf("FETCHING NET INFO\n"); - if (READUINT32(save_p) != ARCHIVEBLOCK_PLAYERS) I_Error("Bad $$$.sav at archive block Players"); From 8087352f9e6ef949cc5b254cdd99182d34a0f588 Mon Sep 17 00:00:00 2001 From: lachwright Date: Tue, 9 Jun 2020 05:27:05 +0800 Subject: [PATCH 094/211] Spindash: new braking --- src/d_player.h | 2 +- src/d_ticcmd.h | 2 ++ src/dehacked.c | 2 +- src/k_kart.c | 22 +++++++++++++++------- src/p_mobj.c | 22 ++-------------------- src/p_slopes.c | 4 +++- src/p_user.c | 2 +- 7 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 6a06c7493..6a9dbb2fa 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -287,7 +287,7 @@ typedef enum k_jmp, // In Mario Kart, letting go of the jump button stops the drift k_offroad, // In Super Mario Kart, going offroad has lee-way of about 1 second before you start losing speed k_pogospring, // Pogo spring bounce effect - k_brakestop, // Wait until you've made a complete stop for a few tics before letting brake go in reverse. + k_spindash, // Spindash charge k_waterskip, // Water skipping counter k_dashpadcooldown, // Separate the vanilla SA-style dash pads from using pw_flashing k_numboosts, // Count of how many boosts are being stacked, for after image spawning diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index c73c161b5..1a37d1b1e 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -42,6 +42,8 @@ typedef enum BT_CUSTOM3 = 1<<15, } buttoncode_t; +#define BT_SPINDASHMASK (BT_ACCELERATE|BT_BRAKE) + // The data sampled per tick (single player) // and transmitted to other peers (multiplayer). // Mainly movements/button commands per game tick, diff --git a/src/dehacked.c b/src/dehacked.c index a0efc5cb2..59c00721a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8504,7 +8504,7 @@ static const char *const KARTSTUFF_LIST[] = { "JMP", "OFFROAD", "POGOSPRING", - "BRAKESTOP", + "SPINDASH", "WATERSKIP", "DASHPADCOOLDOWN", "NUMBOOSTS", diff --git a/src/k_kart.c b/src/k_kart.c index d3ff34d69..92aaa6b7e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7404,7 +7404,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->friction += 4608; } - if ((cmd->buttons & (BT_BRAKE|BT_ACCELERATE)) == (BT_BRAKE|BT_ACCELERATE) && !(player->kartstuff[k_drift])) + if ((cmd->buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK + && !player->kartstuff[k_drift]) player->mo->friction -= 3072; else if (player->speed > 0 && cmd->forwardmove < 0) // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel player->mo->friction -= 2048; @@ -7454,13 +7455,20 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads - // Quick Turning - // You can't turn your kart when you're not moving. - // So now it's time to burn some rubber! - if (player->speed < 2 && leveltime > starttime && cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE && cmd->driftturn != 0) + // Spindash + if ((cmd->buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK + && !player->kartstuff[k_drift] + && !player->kartstuff[k_spinouttimer] + && leveltime > starttime) { - if (leveltime % 8 == 0) - S_StartSound(player->mo, sfx_s224); + if (player->speed < 6*mapobjectscale) + { + if (cmd->driftturn != 0 && leveltime % 8 == 0) + S_StartSound(player->mo, sfx_ruburn); + } + else + if (leveltime % 4 == 0) + S_StartSound(player->mo, sfx_s3k36); } // Squishing diff --git a/src/p_mobj.c b/src/p_mobj.c index cfd14efe5..3f37d1d72 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2796,7 +2796,6 @@ static void P_PlayerZMovement(mobj_t *mo) // Check if we're on a polyobject // that triggers a linedef executor. msecnode_t *node; - boolean stopmovecut = false; for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) { @@ -2828,8 +2827,8 @@ static void P_PlayerZMovement(mobj_t *mo) polysec = po->lines[0]->backsector; // Moving polyobjects should act like conveyors if the player lands on one. (I.E. none of the momentum cut thing below) -Red - if ((mo->z == polysec->ceilingheight || mo->z+mo->height == polysec->floorheight) && po->thinker) - stopmovecut = true; + /*if ((mo->z == polysec->ceilingheight || mo->z+mo->height == polysec->floorheight) && po->thinker) + stopmovecut = true;*/ if (!(po->flags & POF_LDEXEC)) { @@ -2850,24 +2849,7 @@ static void P_PlayerZMovement(mobj_t *mo) } } } - - if (!stopmovecut) #endif - - // Cut momentum in half when you hit the ground and - // aren't pressing any controls. - if (!(mo->player->cmd.forwardmove || mo->player->cmd.sidemove) && !mo->player->cmomx && !mo->player->cmomy - && !(mo->player->kartstuff[k_spinouttimer])) - { - mo->momx = mo->momx/2; - mo->momy = mo->momy/2; - - if (mo->player->cmd.buttons & BT_BRAKE && !(mo->player->cmd.forwardmove)) // FURTHER slowdown if you're braking. - { - mo->momx = mo->momx/2; - mo->momy = mo->momy/2; - } - } } if (mo->health) diff --git a/src/p_slopes.c b/src/p_slopes.c index ffb50f407..d3faa5e4a 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -884,7 +884,9 @@ void P_ButteredSlope(mobj_t *mo) if (mo->player) { // SRB2Kart - spindash negates slopes - if (((mo->player->cmd.buttons & (BT_BRAKE|BT_ACCELERATE)) == (BT_BRAKE|BT_ACCELERATE)) && !mo->player->kartstuff[k_drift]) + if (((mo->player->cmd.buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK) + && !mo->player->kartstuff[k_drift] + && !mo->player->kartstuff[k_spinouttimer]) return; // Changed in kart to only not apply physics on very slight slopes (I think about 4 degree angles) diff --git a/src/p_user.c b/src/p_user.c index 9fba3b2d6..3cbe533cb 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5785,7 +5785,7 @@ static void P_MovePlayer(player_t *player) // Kart: store the current turn range for later use if ((player->mo && player->speed > 0) // Moving - || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn + || (leveltime > starttime && (cmd->buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK) // Rubber-burn turn || (player->respawn.state != RESPAWNST_NONE) // Respawning || (player->spectator || objectplacing)) // Not a physical player { From 396bd69e84687526a9896b85aab13a65fcb7f28a Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 9 Jun 2020 18:10:09 +0200 Subject: [PATCH 095/211] Don't save follower skin in replays if there's no follower... --- src/g_game.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 074ddf9d4..051689325 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6487,8 +6487,15 @@ void G_BeginRecording(void) demo_p += 16; // Save follower's skin name + // PS: We must check for 'follower' to determine if the followerskin is valid. It's going to be 0 if we don't have a follower, but 0 is also absolutely a valid follower! + // Doesn't really matter if the follower mobj is valid so long as it exists in a way or another. + memset(name, 0, 16); - strncpy(name, followers[player->followerskin].skinname, 16); + if (player->follower) + strncpy(name, followers[player->followerskin].skinname, 16); + else + strncpy(name, "None", 16); // Say we don't have one, then. + M_Memcpy(demo_p,name,16); demo_p += 16; From 478e9cee7c3594459785b8fdc5ae0507859c03a4 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 9 Jun 2020 19:59:46 +0200 Subject: [PATCH 096/211] Use P_SetTarget --- src/g_game.c | 6 +++++- src/p_mobj.c | 2 +- src/p_user.c | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 051689325..285a4c44f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2700,6 +2700,9 @@ void G_PlayerReborn(INT32 player) wanted = players[player].kartstuff[k_wanted]; } + // Obliterate follower from existence + P_SetTarget(&players[player].follower, NULL); + memcpy(&respawn, &players[player].respawn, sizeof (respawn)); p = &players[player]; @@ -2762,7 +2765,8 @@ void G_PlayerReborn(INT32 player) p->followerready = followerready; p->followerskin = followerskin; p->followercolor = followercolor; - p->follower = NULL; // respawn a new one with you, it looks better. + //p->follower = NULL; // respawn a new one with you, it looks better. + // ^ Not necessary anyway since it will be respawned regardless considering it doesn't exist anymore. // Don't do anything immediately diff --git a/src/p_mobj.c b/src/p_mobj.c index 37df5bb09..2547f3212 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11891,7 +11891,7 @@ void P_SpawnPlayer(INT32 playernum) p->awayviewmobj = NULL; p->awayviewtics = 0; - p->follower = NULL; // cleanse follower from existence + P_SetTarget(&p->follower, NULL); // cleanse follower from existence // set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't. if (cv_kartdebugshrink.value && !modeattacking && !p->bot) diff --git a/src/p_user.c b/src/p_user.c index 1818f21b7..657d45dda 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8521,7 +8521,7 @@ static void P_HandleFollower(player_t *player) { //CONS_Printf("Spawning follower...\n"); // so let's spawn one! - player->follower = P_SpawnMobj(sx, sy, sz, MT_FOLLOWER); + P_SetTarget(&player->follower, P_SpawnMobj(sx, sy, sz, MT_FOLLOWER)); P_SetFollowerState(player->follower, fl.idlestate); P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear player->follower->angle = player->mo->angle; @@ -8555,7 +8555,7 @@ static void P_HandleFollower(player_t *player) if (P_MobjWasRemoved(player->follower)) { - player->follower = NULL; // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing. + P_SetTarget(&player->follower, NULL); // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing. return; } From 84a31e288f98fb6b9b4e3480a2a0c1aeb7db1358 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Tue, 9 Jun 2020 20:09:18 +0200 Subject: [PATCH 097/211] Fix some V_FLIP stuff in player/follower drawing --- src/m_menu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 739433ec8..194bb8838 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7639,7 +7639,7 @@ static void M_StartGrandPrix(INT32 choice) grandprixinfo.gamespeed = KARTSPEED_HARD; grandprixinfo.masterbots = true; break; - + } grandprixinfo.encore = (boolean)(cv_dummygpencore.value); @@ -9552,7 +9552,7 @@ static void M_DrawSetupMultiPlayerMenu(void) sprframe = &sprdef->spriteframes[frame]; patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); - if (sprframe->flip & 1) // Only for first sprite + if (sprframe->flip & 2) // Only for first sprite flags |= V_FLIP; // This sprite is left/right flipped! // draw box around guy @@ -9610,10 +9610,12 @@ static void M_DrawSetupMultiPlayerMenu(void) follower_frame = 0; // frame doesn't exist, we went beyond it... what? sprframe = &sprdef->spriteframes[follower_frame]; patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); - if (sprframe->flip & 1) // Only for first sprite + if (sprframe->flip & 2) // Only for first sprite flags |= V_FLIP; // This sprite is left/right flipped! - // draw player sprite + // @TODO: Reminder that followers on the menu right now do NOT support the 'followercolor' command, considering this whole menu is getting remade anyway, I see no point in incorporating it in right now. + + // draw follower sprite if (setupm_fakecolor) // inverse should never happen { From fbd10d0e6c7d2f7c64fe5db4613e7e52f4a65f8f Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 10 Jun 2020 22:52:32 +0200 Subject: [PATCH 098/211] Fix a bruh moment where I deleted follower from relinkpointers when fixing a conflict and it broke every netgame :oh: --- src/p_saveg.c | 7 +++++++ src/r_things.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/p_saveg.c b/src/p_saveg.c index 37f6dd80b..16863ae33 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3180,6 +3180,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type); } + if (mobj->player->follower) + { + temp = (UINT32)(size_t)mobj->player->follower; + mobj->player->follower = NULL; + if (!P_SetTarget(&mobj->player->follower, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "follower not found on %d\n", mobj->type); + } if (mobj->player->nextwaypoint) { temp = (UINT32)(size_t)mobj->player->nextwaypoint; diff --git a/src/r_things.c b/src/r_things.c index 369bdfafe..5a428afc8 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -3099,7 +3099,7 @@ void SetFollower(INT32 playernum, INT32 skinnum) } P_RemoveMobj(player->follower); - player->follower = NULL; + P_SetTarget(&player->follower, NULL); } player->followerskin = skinnum; From 9fec0562c9339a4695dcdcb4c8c7a76437e9fd60 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Wed, 10 Jun 2020 23:21:25 +0200 Subject: [PATCH 099/211] Don't do anything if you somehow have a negative follower in menus --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 194bb8838..a82f97314 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -9638,7 +9638,7 @@ static void M_DrawSetupMultiPlayerMenu(void) static void M_GetFollowerState(void) { - if (setupm_fakefollower == -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none! + if (setupm_fakefollower <= -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none! return; // ^ we don't actually need to set anything since it won't be displayed anyway. From 44af4705bb0bd420543459879113576fd14eb426 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 12 Jun 2020 23:47:08 -0700 Subject: [PATCH 100/211] masterserver_token is back --- src/http-mserv.c | 26 ++++++++++++++++++++++++-- src/mserv.c | 1 + src/mserv.h | 1 + 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/http-mserv.c b/src/http-mserv.c index db81ee1e7..a9009bd24 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -44,6 +44,11 @@ consvar_t cv_masterserver_debug = { MasterServer_Debug_OnChange, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ }; +consvar_t cv_masterserver_token = { + "masterserver_token", "", CV_SAVE, NULL, + NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */ +}; + static int hms_started; static char *hms_api; @@ -100,7 +105,9 @@ HMS_connect (const char *format, ...) va_list ap; CURL *curl; char *url; + char *quack_token; size_t seek; + size_t token_length; struct HMS_buffer *buffer; if (! hms_started) @@ -127,6 +134,17 @@ HMS_connect (const char *format, ...) return NULL; } + if (cv_masterserver_token.string[0]) + { + quack_token = curl_easy_escape(curl, cv_masterserver_token.string, 0); + token_length = ( sizeof "?token="-1 )+ strlen(quack_token); + } + else + { + quack_token = NULL; + token_length = 0; + } + #ifdef HAVE_THREADS I_lock_mutex(&hms_api_mutex); #endif @@ -134,7 +152,7 @@ HMS_connect (const char *format, ...) seek = strlen(hms_api) + 1;/* + '/' */ va_start (ap, format); - url = malloc(seek + vsnprintf(0, 0, format, ap) + 1); + url = malloc(seek + vsnprintf(0, 0, format, ap) + token_length + 1); va_end (ap); sprintf(url, "%s/", hms_api); @@ -144,9 +162,12 @@ HMS_connect (const char *format, ...) #endif va_start (ap, format); - vsprintf(&url[seek], format, ap); + seek += vsprintf(&url[seek], format, ap); va_end (ap); + if (quack_token) + sprintf(&url[seek], "?token=%s", quack_token); + CONS_Printf("HMS: connecting '%s'...\n", url); buffer = malloc(sizeof *buffer); @@ -174,6 +195,7 @@ HMS_connect (const char *format, ...) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEDATA, buffer); + curl_free(quack_token); free(url); return buffer; diff --git a/src/mserv.c b/src/mserv.c index 99224ce23..f3d414c92 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -89,6 +89,7 @@ void AddMServCommands(void) CV_RegisterVar(&cv_masterserver_update_rate); CV_RegisterVar(&cv_masterserver_timeout); CV_RegisterVar(&cv_masterserver_debug); + CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_servername); COM_AddCommand("listserv", Command_Listserv_f); #endif diff --git a/src/mserv.h b/src/mserv.h index 943a80d08..9269c408a 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -68,6 +68,7 @@ extern consvar_t cv_masterserver, cv_servername; 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; // < 0 to not connect (usually -1) (offline mode) // == 0 to show all rooms, not a valid hosting room From 6e7f38475ff6ac4275c95542a3fab0cc2fca9e33 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sat, 13 Jun 2020 17:07:05 +0300 Subject: [PATCH 101/211] Add missing inflateEnd to fix memory leak in W_ReadLumpHeaderPwad --- src/w_wad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/w_wad.c b/src/w_wad.c index 54ae7fb26..d68af8f20 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1314,6 +1314,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si if (zErr == Z_STREAM_END) { M_Memcpy(dest, decData, size); + inflateEnd(&strm); } else { From fb1dae197fa73277627b01761d5c7b82fe5bba8f Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sat, 13 Jun 2020 21:37:33 +0300 Subject: [PATCH 102/211] Cleaner version of the W_ReadLumpHeaderPwad memory leak fix --- src/w_wad.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/w_wad.c b/src/w_wad.c index d68af8f20..7fd7ac125 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1314,14 +1314,13 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si if (zErr == Z_STREAM_END) { M_Memcpy(dest, decData, size); - inflateEnd(&strm); } else { size = 0; zerr(zErr); - (void)inflateEnd(&strm); } + (void)inflateEnd(&strm); } else { From 137788b0c9736970c1dc6b357ce34c01ca001e05 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sat, 13 Jun 2020 23:34:13 +0300 Subject: [PATCH 103/211] Fix uninitialized alpha when reading colormaps --- src/p_setup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_setup.c b/src/p_setup.c index c0254bf4a..37bd77b5f 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1494,6 +1494,7 @@ static void P_LoadRawSideDefs2(void *data) color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); + color.s.alpha = 0; #ifdef GLENCORE if (encoremap) @@ -1535,6 +1536,7 @@ static void P_LoadRawSideDefs2(void *data) color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); + color.s.alpha = 0; #ifdef GLENCORE if (encoremap) From 169c3cae4399d1ec52bf88d217ce4deaffbca8d2 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Sun, 14 Jun 2020 14:53:53 +0300 Subject: [PATCH 104/211] Colormap alpha handling was still wrong, hopefully it's correct now --- src/p_setup.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 37bd77b5f..85243d504 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1494,17 +1494,16 @@ static void P_LoadRawSideDefs2(void *data) color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); - color.s.alpha = 0; #ifdef GLENCORE if (encoremap) { j = encoremap[NearestColor(color.s.red, color.s.green, color.s.blue)]; //CONS_Printf("R_CreateColormap: encoremap[%d] = %d\n", j, encoremap[j]); -- moved encoremap upwards for optimisation - color = pLocalPalette[j]; + color = pLocalPalette[j]; // note: this sets alpha to 255, we will reset it below } #endif - + color.s.alpha = 0; // reset/init the alpha, so the addition below will work correctly sec->extra_colormap->rgba = color.rgba; // alpha @@ -1536,17 +1535,16 @@ static void P_LoadRawSideDefs2(void *data) color.s.red = (HEX2INT(col[1]) << 4) + HEX2INT(col[2]); color.s.green = (HEX2INT(col[3]) << 4) + HEX2INT(col[4]); color.s.blue = (HEX2INT(col[5]) << 4) + HEX2INT(col[6]); - color.s.alpha = 0; #ifdef GLENCORE if (encoremap) { j = encoremap[NearestColor(color.s.red, color.s.green, color.s.blue)]; //CONS_Printf("R_CreateColormap: encoremap[%d] = %d\n", j, encoremap[j]); -- moved encoremap upwards for optimisation - color = pLocalPalette[j]; + color = pLocalPalette[j]; // note: this sets alpha to 255, we will reset it below } #endif - + color.s.alpha = 0; // reset/init the alpha, so the addition below will work correctly sec->extra_colormap->fadergba = color.rgba; // alpha From 6d0712628356b01b50dd67e29a16f425ee1e269e Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 14 Jun 2020 16:59:46 +0200 Subject: [PATCH 105/211] Use the proper player to save followercolor in replays this time around --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 7f6177eac..fbc695f53 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6497,7 +6497,7 @@ void G_BeginRecording(void) // Save follower's colour memset(name, 0, 16); - strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + strncpy(name, Followercolor_cons_t[players->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" M_Memcpy(demo_p, name, 16); demo_p += 16; From 01ecc8573a54d0e949518888f5c49bbbf822042a Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 14 Jun 2020 17:06:46 +0200 Subject: [PATCH 106/211] Actually use the proper player for followercolor, for real this time --- src/g_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index fbc695f53..1b994ede1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -6497,7 +6497,7 @@ void G_BeginRecording(void) // Save follower's colour memset(name, 0, 16); - strncpy(name, Followercolor_cons_t[players->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" + strncpy(name, Followercolor_cons_t[player->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match" M_Memcpy(demo_p, name, 16); demo_p += 16; From 3cdc6d07d078865428ebf350d945474cf9b8fca6 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 14 Jun 2020 19:13:22 -0700 Subject: [PATCH 107/211] ADD to the buffer size --- src/http-mserv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http-mserv.c b/src/http-mserv.c index a9009bd24..9d05b80e5 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -88,7 +88,7 @@ HMS_on_read (char *s, size_t _1, size_t n, void *userdata) { /* resize to next multiple of buffer size */ blocks = ( n / DEFAULT_BUFFER_SIZE + 1 ); - buffer->end = ( blocks * DEFAULT_BUFFER_SIZE ); + buffer->end += ( blocks * DEFAULT_BUFFER_SIZE ); buffer->buffer = realloc(buffer->buffer, buffer->end); } From 6714d9b614a92ffdd6be2bdbb7fef2a401adf1e0 Mon Sep 17 00:00:00 2001 From: Steel Titanium Date: Tue, 23 Jun 2020 18:39:02 -0400 Subject: [PATCH 108/211] Fix user agent for HTTP downloads being set as SRB2Kart/v1.1.2 --- src/d_netfil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_netfil.c b/src/d_netfil.c index 3dc9da68c..f5550c060 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -1088,7 +1088,7 @@ void CURLPrepareFile(const char* url, int dfilenum) // Only allow HTTP and HTTPS curl_easy_setopt(http_handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); - curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("SRB2Kart/v%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION)); // Set user agent as some servers won't accept invalid user agents. + 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. // Follow a redirect request, if sent by the server. curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L); From f0fac04dfd7ee0bfc3fd30d0ebdb4f26a2af8e71 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Sat, 23 May 2020 17:59:31 -0400 Subject: [PATCH 109/211] Experiemntal clientside ack fix Stops the client from using reliable packets before joining a server Clients can time out during addon loading sending reliable packets before then can cause out of order acks This causes joinbug, chatbug, and chatspam, as well as general server instability due to unnecesary packetspam --- src/d_clisrv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 251f413ab..d5c4006e6 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1270,7 +1270,7 @@ static boolean CL_AskFileList(INT32 firstfile) netbuffer->packettype = PT_TELLFILESNEEDED; netbuffer->u.filesneedednum = firstfile; - return HSendPacket(servernode, true, 0, sizeof (INT32)); + return HSendPacket(servernode, false, 0, sizeof (INT32)); } /** Sends a special packet to declare how many players in local From 8ab98011f6a3a6154d5e721561174f43959ad7d2 Mon Sep 17 00:00:00 2001 From: lachwright Date: Mon, 6 Jul 2020 08:10:03 +0800 Subject: [PATCH 110/211] Spindash charge (WIP) --- src/d_ticcmd.h | 4 +- src/k_kart.c | 102 +++++++++++++++++++++++++++++++++++++++---------- src/k_kart.h | 1 + src/p_slopes.c | 4 +- src/p_user.c | 2 +- 5 files changed, 87 insertions(+), 26 deletions(-) diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 1a37d1b1e..0340b9f40 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -34,6 +34,8 @@ typedef enum BT_BACKWARD = 1<<6, // Aim Item Backward BT_LOOKBACK = 1<<7, // Look Backward + BT_EBRAKEMASK = (BT_ACCELERATE|BT_BRAKE), + // free: 1<<8 to 1<<12 // Lua garbage @@ -42,8 +44,6 @@ typedef enum BT_CUSTOM3 = 1<<15, } buttoncode_t; -#define BT_SPINDASHMASK (BT_ACCELERATE|BT_BRAKE) - // The data sampled per tick (single player) // and transmitted to other peers (multiplayer). // Mainly movements/button commands per game tick, diff --git a/src/k_kart.c b/src/k_kart.c index 92aaa6b7e..ed4736411 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1588,7 +1588,7 @@ void K_SpawnDashDustRelease(player_t *player) if (!P_IsObjectOnGround(player->mo)) return; - if (!player->speed && !player->kartstuff[k_startboost]) + if (!player->speed && !player->kartstuff[k_startboost] && !player->kartstuff[k_spindash]) return; travelangle = player->mo->angle; @@ -6682,6 +6682,83 @@ static INT32 K_FlameShieldMax(player_t *player) return min(16, 1 + (disttofinish / distv)); } +boolean K_PlayerEBrake(player_t *player) +{ + return (player->cmd.buttons & BT_EBRAKEMASK) == BT_EBRAKEMASK + && !player->kartstuff[k_drift] + && !player->kartstuff[k_spinouttimer] + && !player->kartstuff[k_boostcharge] + && !(player->kartstuff[k_spindash] < 0) + && !player->powers[pw_nocontrol] + && leveltime > starttime; +} + +tic_t K_GetSpindashChargeTime(player_t *player) +{ + return (player->kartspeed + 4)*TICRATE/3; // more charge time for higher speed: Tails = 2s, Mighty = 3s, Fang = 4s +} + +fixed_t K_GetSpindashChargeSpeed(player_t *player) +{ + return FixedMul(FRACUNIT + (player->kartweight - 5)*FRACUNIT/12, K_GetKartSpeed(player, false)); // more speed for higher weight: Tails = 75%, Fang = 100%, Mighty = 125% +} + +void K_KartSpindash(player_t *player) +{ + ticcmd_t *cmd = &player->cmd; + + if (!(K_PlayerEBrake(player) || (player->kartstuff[k_spindash] && !(cmd->buttons & BT_BRAKE)))) + { + if (player->kartstuff[k_spindash]) + player->kartstuff[k_spindash] = 0; + return; + } + + if (player->speed < 6*mapobjectscale) + { + const tic_t MAXCHARGETIME = K_GetSpindashChargeTime(player); + const fixed_t MAXCHARGESPEED = K_GetSpindashChargeSpeed(player); + + if (cmd->driftturn != 0 && leveltime % 8 == 0) + S_StartSound(player->mo, sfx_ruburn); + + if ((cmd->buttons & (BT_DRIFT|BT_BRAKE)) == (BT_DRIFT|BT_BRAKE)) + { + INT16 chargetime = MAXCHARGETIME - ++player->kartstuff[k_spindash]; + if (chargetime > 0) + { + UINT16 soundcharge = 0; + UINT8 add = 0; + while ((soundcharge += ++add) < chargetime); + if (soundcharge == chargetime) + { + K_SpawnDashDustRelease(player); + S_StartSound(player->mo, sfx_s3kab); + } + } + else if (chargetime < -TICRATE) + K_SpinPlayer(player, NULL, 0, NULL, false); + else + { + if (player->kartstuff[k_spindash] % 4 == 0) + { + K_SpawnDashDustRelease(player); + K_FlameDashLeftoverSmoke(player->mo); + } + } + } + else if (player->kartstuff[k_spindash]) + { + fixed_t speed = player->kartstuff[k_spindash]*MAXCHARGESPEED/MAXCHARGETIME; + player->kartstuff[k_spindash] = 0; + S_StartSound(player->mo, sfx_s23c); + } + } + else + if (leveltime % 4 == 0) + S_StartSound(player->mo, sfx_kc2b); +} + // // K_MoveKartPlayer // @@ -7389,6 +7466,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } } + K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads + if (onground) { fixed_t prevfriction = player->mo->friction; @@ -7404,8 +7483,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->friction += 4608; } - if ((cmd->buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK - && !player->kartstuff[k_drift]) + if (K_PlayerEBrake(player)) player->mo->friction -= 3072; else if (player->speed > 0 && cmd->forwardmove < 0) // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel player->mo->friction -= 2048; @@ -7451,24 +7529,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction); } - } - K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads - - // Spindash - if ((cmd->buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK - && !player->kartstuff[k_drift] - && !player->kartstuff[k_spinouttimer] - && leveltime > starttime) - { - if (player->speed < 6*mapobjectscale) - { - if (cmd->driftturn != 0 && leveltime % 8 == 0) - S_StartSound(player->mo, sfx_ruburn); - } - else - if (leveltime % 4 == 0) - S_StartSound(player->mo, sfx_s3k36); + K_KartSpindash(player); } // Squishing diff --git a/src/k_kart.h b/src/k_kart.h index af247029d..a5671540d 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -70,6 +70,7 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower); fixed_t K_GetKartAccel(player_t *player); UINT16 K_GetKartFlashing(player_t *player); fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove); +boolean K_PlayerEBrake(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(void); diff --git a/src/p_slopes.c b/src/p_slopes.c index d3faa5e4a..0296ef38f 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -884,9 +884,7 @@ void P_ButteredSlope(mobj_t *mo) if (mo->player) { // SRB2Kart - spindash negates slopes - if (((mo->player->cmd.buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK) - && !mo->player->kartstuff[k_drift] - && !mo->player->kartstuff[k_spinouttimer]) + if (K_PlayerEBrake(mo->player)) return; // Changed in kart to only not apply physics on very slight slopes (I think about 4 degree angles) diff --git a/src/p_user.c b/src/p_user.c index 3cbe533cb..49252712d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5785,7 +5785,7 @@ static void P_MovePlayer(player_t *player) // Kart: store the current turn range for later use if ((player->mo && player->speed > 0) // Moving - || (leveltime > starttime && (cmd->buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK) // Rubber-burn turn + || (leveltime > starttime && (cmd->buttons & BT_EBRAKEMASK) == BT_EBRAKEMASK) // Rubber-burn turn || (player->respawn.state != RESPAWNST_NONE) // Respawning || (player->spectator || objectplacing)) // Not a physical player { From 1af2ecdf1514b09e9ee20901046c3b080d951c0d Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 5 Jul 2020 18:37:55 -0700 Subject: [PATCH 111/211] Wow! (cherry picked from commit be01a5917639f4b05de99ef92bdafb50a204ee56) --- src/v_video.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/v_video.c b/src/v_video.c index 0f6479205..f51b9ab2c 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1879,6 +1879,7 @@ void V_DrawStringScaled( chw <<= FRACBITS; spacew <<= FRACBITS; + lfh <<= FRACBITS; #define Mul( id, scale ) ( id = FixedMul (scale, id) ) Mul (chw, scale); From c7a00d4800c0473df45003d32b2dd18b95b8d251 Mon Sep 17 00:00:00 2001 From: Sryder Date: Wed, 8 Jul 2020 18:01:47 +0100 Subject: [PATCH 112/211] Update pointers to nodes when the nodesarray is reallocated. Fixes crash in Gust Planet. --- src/k_pathfind.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/k_pathfind.c b/src/k_pathfind.c index 8cccd1e81..ddea477c8 100644 --- a/src/k_pathfind.c +++ b/src/k_pathfind.c @@ -472,13 +472,44 @@ boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup // Reallocate nodesarray if it's full if (nodesarraycount >= pathfindsetup->nodesarraycapacity) { + pathfindnode_t *nodesarrayrealloc = NULL; pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2; - nodesarray = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + nodesarrayrealloc = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); - if (nodesarray == NULL) + if (nodesarrayrealloc == NULL) { I_Error("K_PathfindAStar: Out of memory reallocating nodes array."); } + + // Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved. + if (nodesarray != nodesarrayrealloc) + { + size_t i = 0U; + size_t arrayindex = 0U; + for (i = 0U; i < closedsetcount; i++) + { + arrayindex = closedset[i] - nodesarray; + closedset[i] = &nodesarrayrealloc[arrayindex]; + } + for (i = 0U; i < openset.count; i++) + { + arrayindex = ((pathfindnode_t *)(openset.array[i].data)) - nodesarray; + openset.array[i].data = &nodesarrayrealloc[arrayindex]; + } + for (i = 0U; i < nodesarraycount; i++) + { + if (nodesarrayrealloc[i].camefrom != NULL) + { + arrayindex = nodesarrayrealloc[i].camefrom - nodesarray; + nodesarrayrealloc[i].camefrom = &nodesarrayrealloc[arrayindex]; + } + } + + arrayindex = currentnode - nodesarray; + currentnode = &nodesarrayrealloc[arrayindex]; + } + + nodesarray = nodesarrayrealloc; } // Create the new node and add it to the nodes array and open set From 87069a86d87ca563d706b6fcd33d79eebb9eb4f1 Mon Sep 17 00:00:00 2001 From: Sryder Date: Wed, 8 Jul 2020 21:36:28 +0100 Subject: [PATCH 113/211] Fix the cause of the waypoint debug message spam in Canyon Rush. Not calling the indexchanged callback here meant that if K_BHeapSortDown didn't make any changes, the item at index 0 would never call the callback despite changing position. --- src/k_bheap.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_bheap.c b/src/k_bheap.c index 40c652b5e..495c8ba0f 100644 --- a/src/k_bheap.c +++ b/src/k_bheap.c @@ -469,6 +469,10 @@ boolean K_BHeapPop(bheap_t *const heap, bheapitem_t *const returnitemstorage) heap->array[0] = heap->array[heap->count]; heap->array[0].heapindex = 0U; memset(&heap->array[heap->count], 0x00, sizeof(bheapitem_t)); + if (heap->array[0].indexchanged != NULL) + { + heap->array[0].indexchanged(heap->array[0].data, heap->array[0].heapindex); + } K_BHeapSortDown(heap, &heap->array[0]); popsuccess = true; From d67ad1a08fe27f20124d6562c991377ed36037de Mon Sep 17 00:00:00 2001 From: SteelT Date: Thu, 9 Jul 2020 00:29:46 -0400 Subject: [PATCH 114/211] Fully fix replay hut crashing if addons isn't loaded. --- src/m_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_menu.c b/src/m_menu.c index 9fdd70b12..5624f3214 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -5693,7 +5693,7 @@ static void M_DrawReplayStartMenu(void) // Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this) // and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid. - if (demolist[dir_on[menudepthleft]].standings[0].skin != 0xFF && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) + if (demolist[dir_on[menudepthleft]].standings[i].skin != 0xFF && W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) { patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin]; colormap = R_GetTranslationColormap( From e250796886a63e42dd1902b86d2dd5e130a6ef5d Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 8 Jul 2020 21:30:34 -0700 Subject: [PATCH 115/211] WIP "Drift explosion" --- src/k_kart.c | 24 ++++++++++++------------ src/p_mobj.c | 17 +++++++++++++---- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 32e238fa8..092bc3acf 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6323,10 +6323,10 @@ 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); - //mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); - //P_SetTarget(&overlay->target, player->mo); - //P_SetScale(overlay, (overlay->destscale = player->mo->scale)); - //K_FlipFromObject(overlay, player->mo); + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); + P_SetTarget(&overlay->target, player->mo); + P_SetScale(overlay, (overlay->destscale = FixedMul(3*FRACUNIT/2, player->mo->scale))); + K_FlipFromObject(overlay, player->mo); S_StartSound(player->mo, sfx_s23c); //K_SpawnDashDustRelease(player); @@ -6340,8 +6340,8 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 15) player->kartstuff[k_driftboost] = 15; - //overlay->color = SKINCOLOR_GOLD; - //overlay->fuse = 8; + overlay->color = SKINCOLOR_GOLD; + overlay->fuse = 8; } else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) { @@ -6352,8 +6352,8 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 20) player->kartstuff[k_driftboost] = 20; - //overlay->color = SKINCOLOR_KETCHUP; - //overlay->fuse = 16; + overlay->color = SKINCOLOR_KETCHUP; + overlay->fuse = 8; } else if (player->kartstuff[k_driftcharge] < dsthree) { @@ -6364,8 +6364,8 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 50) player->kartstuff[k_driftboost] = 50; - //overlay->color = SKINCOLOR_SAPPHIRE; - //overlay->fuse = 32; + overlay->color = SKINCOLOR_SAPPHIRE; + overlay->fuse = 16; } else if (player->kartstuff[k_driftcharge] >= dsthree) { @@ -6376,8 +6376,8 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 125) player->kartstuff[k_driftboost] = 125; - //overlay->color = SKINCOLOR_SILVER; - //overlay->fuse = 120; + overlay->color = SKINCOLOR_SILVER; + overlay->fuse = 24; } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 842d1e2b1..3c1aba3be 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8297,15 +8297,24 @@ void P_MobjThinker(mobj_t *mobj) return; } - mobj->angle = mobj->target->angle; - 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); + //mobj->angle = mobj->target->angle; + { + angle_t angle = R_PointToAngle2(0, 0, mobj->target->momx, mobj->target->momy); + mobj->angle = angle; + P_TeleportMove(mobj, mobj->target->x + P_ReturnThrustX(mobj, angle+ANGLE_180, 4*mobj->target->radius), + mobj->target->y + P_ReturnThrustY(mobj, angle+ANGLE_180, 4*mobj->target->radius), mobj->target->z); + } + P_SetScale(mobj, FixedMul(3*FRACUNIT/2, mobj->target->scale)); mobj->flags2 ^= MF2_DONTDRAW; #ifdef HWRENDER mobj->modeltilt = mobj->target->modeltilt; #endif + if (mobj->fuse <= 8) + mobj->color = SKINCOLOR_KETCHUP; + else if (mobj->fuse <= 16) + mobj->color = SKINCOLOR_SAPPHIRE; + { player_t *p = NULL; if (mobj->target->target && mobj->target->target->player) From c6c81c7fab2a60a5be64cea9ad9308e1a8a739c8 Mon Sep 17 00:00:00 2001 From: Sryder Date: Thu, 9 Jul 2020 17:54:28 +0100 Subject: [PATCH 116/211] Fix Flameshield in mapobjectscale maps. FixedMul instead of FixedDiv --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 32e238fa8..949a828dd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2098,7 +2098,7 @@ fixed_t K_GetKartAccel(player_t *player) //k_accel += 3 * (9 - kartspeed); // 36 - 60 k_accel += 4 * (9 - kartspeed); // 32 - 64 - + if (K_PlayerUsesBotMovement(player)) { // Rubberbanding acceleration is waekened since it makes hits feel more meaningful @@ -4004,7 +4004,7 @@ void K_DoSneaker(player_t *player, INT32 type) { player->pflags |= PF_ATTACKDOWN; K_PlayBoostTaunt(player->mo); - + } player->kartstuff[k_sneakertimer] = sneakertime; @@ -6679,7 +6679,7 @@ static INT32 K_FlameShieldMax(player_t *player) } disttofinish = player->distancetofinish - disttofinish; - distv = FixedDiv(distv * FRACUNIT, mapobjectscale) / FRACUNIT; + distv = FixedMul(distv * FRACUNIT, mapobjectscale) / FRACUNIT; return min(16, 1 + (disttofinish / distv)); } @@ -10025,7 +10025,7 @@ static void K_drawKartNameTags(void) { bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; } - + // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); From f1648fbf5ce76548e92227088a4c4ffef96f1e05 Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 9 Jul 2020 16:39:47 -0700 Subject: [PATCH 117/211] Followers compiler warnings --- src/d_netcmd.c | 42 ++++++++++++++++++++++++------------------ src/dehacked.c | 2 +- src/p_user.c | 8 +++++--- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2f8bb0c5c..b55441b2d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -6214,13 +6214,15 @@ static void Follower2_OnChange(void) } - INT32 num = R_FollowerAvailable(str); - char set[10]; - if (num == -1) // that's an error. - CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + { + INT32 num = R_FollowerAvailable(str); + char set[10]; + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - sprintf(set, "%d", num); - CV_StealthSet(&cv_follower2, set); // set it to a number. It's easier for us to send later :) + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower2, set); // set it to a number. It's easier for us to send later :) + } } SendNameAndColor2(); } @@ -6257,13 +6259,15 @@ static void Follower3_OnChange(void) return; } - INT32 num = R_FollowerAvailable(str); - char set[10]; - if (num == -1) // that's an error. - CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + { + INT32 num = R_FollowerAvailable(str); + char set[10]; + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - sprintf(set, "%d", num); - CV_StealthSet(&cv_follower3, set); // set it to a number. It's easier for us to send later :) + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower3, set); // set it to a number. It's easier for us to send later :) + } } SendNameAndColor3(); } @@ -6300,13 +6304,15 @@ static void Follower4_OnChange(void) return; } - INT32 num = R_FollowerAvailable(str); - char set[10]; - if (num == -1) // that's an error. - CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); + { + INT32 num = R_FollowerAvailable(str); + char set[10]; + if (num == -1) // that's an error. + CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str); - sprintf(set, "%d", num); - CV_StealthSet(&cv_follower4, set); // set it to a number. It's easier for us to send later :) + sprintf(set, "%d", num); + CV_StealthSet(&cv_follower4, set); // set it to a number. It's easier for us to send later :) + } } SendNameAndColor4(); } diff --git a/src/dehacked.c b/src/dehacked.c index d8a3f6d01..372863bd5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -908,7 +908,7 @@ if (followers[numfollowers].field < threshold) \ FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale // Special case for color I suppose - if (followers[numfollowers].defaultcolor < 0 || followers[numfollowers].defaultcolor > MAXSKINCOLORS-1) + if (followers[numfollowers].defaultcolor > MAXSKINCOLORS-1) { followers[numfollowers].defaultcolor = 1; deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, MAXSKINCOLORS-1); diff --git a/src/p_user.c b/src/p_user.c index fd5691601..e826d7cd1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8492,9 +8492,11 @@ static void P_HandleFollower(player_t *player) // finally, add a cool floating effect to the z height. // not stolen from k_kart I swear!! - const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); - sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); + { + const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo); + } // Set follower colour From 856eda9f958d9ea85017d6f15295d2ba199bf95f Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 9 Jul 2020 16:48:21 -0700 Subject: [PATCH 118/211] Don't scale drift explosion --- src/k_kart.c | 2 +- src/p_mobj.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 092bc3acf..29f484cb6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6325,7 +6325,7 @@ static void K_KartDrift(player_t *player, boolean onground) angle_t pushdir = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); P_SetTarget(&overlay->target, player->mo); - P_SetScale(overlay, (overlay->destscale = FixedMul(3*FRACUNIT/2, player->mo->scale))); + P_SetScale(overlay, (overlay->destscale = player->mo->scale)); K_FlipFromObject(overlay, player->mo); S_StartSound(player->mo, sfx_s23c); diff --git a/src/p_mobj.c b/src/p_mobj.c index 3c1aba3be..d34b0d938 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8304,7 +8304,7 @@ void P_MobjThinker(mobj_t *mobj) P_TeleportMove(mobj, mobj->target->x + P_ReturnThrustX(mobj, angle+ANGLE_180, 4*mobj->target->radius), mobj->target->y + P_ReturnThrustY(mobj, angle+ANGLE_180, 4*mobj->target->radius), mobj->target->z); } - P_SetScale(mobj, FixedMul(3*FRACUNIT/2, mobj->target->scale)); + P_SetScale(mobj, mobj->target->scale); mobj->flags2 ^= MF2_DONTDRAW; #ifdef HWRENDER mobj->modeltilt = mobj->target->modeltilt; From 376f4d3116ac9ee3ff71330ebe9156bfae7cfa56 Mon Sep 17 00:00:00 2001 From: Sryder Date: Fri, 10 Jul 2020 01:32:46 +0100 Subject: [PATCH 119/211] Fix shadowed declaration in pathfind code --- src/k_pathfind.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/k_pathfind.c b/src/k_pathfind.c index ddea477c8..316eab418 100644 --- a/src/k_pathfind.c +++ b/src/k_pathfind.c @@ -484,24 +484,24 @@ boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup // Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved. if (nodesarray != nodesarrayrealloc) { - size_t i = 0U; + size_t j = 0U; size_t arrayindex = 0U; - for (i = 0U; i < closedsetcount; i++) + for (j = 0U; j < closedsetcount; j++) { - arrayindex = closedset[i] - nodesarray; - closedset[i] = &nodesarrayrealloc[arrayindex]; + arrayindex = closedset[j] - nodesarray; + closedset[j] = &nodesarrayrealloc[arrayindex]; } - for (i = 0U; i < openset.count; i++) + for (j = 0U; j < openset.count; j++) { - arrayindex = ((pathfindnode_t *)(openset.array[i].data)) - nodesarray; - openset.array[i].data = &nodesarrayrealloc[arrayindex]; + arrayindex = ((pathfindnode_t *)(openset.array[j].data)) - nodesarray; + openset.array[j].data = &nodesarrayrealloc[arrayindex]; } - for (i = 0U; i < nodesarraycount; i++) + for (j = 0U; j < nodesarraycount; j++) { - if (nodesarrayrealloc[i].camefrom != NULL) + if (nodesarrayrealloc[j].camefrom != NULL) { - arrayindex = nodesarrayrealloc[i].camefrom - nodesarray; - nodesarrayrealloc[i].camefrom = &nodesarrayrealloc[arrayindex]; + arrayindex = nodesarrayrealloc[j].camefrom - nodesarray; + nodesarrayrealloc[j].camefrom = &nodesarrayrealloc[arrayindex]; } } From 65550cc9b7463abc2787c68de3c8c83da4bdd9ce Mon Sep 17 00:00:00 2001 From: James R Date: Thu, 9 Jul 2020 18:03:27 -0700 Subject: [PATCH 120/211] Make drift explode animation LONGER --- src/k_kart.c | 6 +++--- src/p_mobj.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 566123ea3..295cdc540 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6376,7 +6376,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 20; overlay->color = SKINCOLOR_KETCHUP; - overlay->fuse = 8; + overlay->fuse = 16; } else if (player->kartstuff[k_driftcharge] < dsthree) { @@ -6388,7 +6388,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 50; overlay->color = SKINCOLOR_SAPPHIRE; - overlay->fuse = 16; + overlay->fuse = 32; } else if (player->kartstuff[k_driftcharge] >= dsthree) { @@ -6400,7 +6400,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 125; overlay->color = SKINCOLOR_SILVER; - overlay->fuse = 24; + overlay->fuse = 48; } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 10e968c68..735000db1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8331,9 +8331,9 @@ void P_MobjThinker(mobj_t *mobj) mobj->modeltilt = mobj->target->modeltilt; #endif - if (mobj->fuse <= 8) + if (mobj->fuse <= 16) mobj->color = SKINCOLOR_KETCHUP; - else if (mobj->fuse <= 16) + else if (mobj->fuse <= 32) mobj->color = SKINCOLOR_SAPPHIRE; { From f348cd0f1fa49592815431abecad26dba64705c9 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Jul 2020 19:55:00 -0700 Subject: [PATCH 121/211] Multiply ring float height by mobjscale --- src/p_mobj.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2547f3212..963619c10 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11750,7 +11750,7 @@ void P_RespawnSpecials(void) ss->sector->ceilingheight) - (mthing->options >> ZSHIFT) * FRACUNIT; if (mthing->options & MTF_AMBUSH && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i))) - z -= 24*FRACUNIT; + z -= 24 * mapobjectscale; z -= mobjinfo[i].height; // Don't forget the height! } else @@ -11762,7 +11762,7 @@ void P_RespawnSpecials(void) ss->sector->floorheight) + (mthing->options >> ZSHIFT) * FRACUNIT; if (mthing->options & MTF_AMBUSH && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i))) - z += 24*FRACUNIT; + z += 24 * mapobjectscale; } mo = P_SpawnMobj(x, y, z, i); From 716898ebf697772810039fbd8b68d3ca68092298 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 13 Jul 2020 19:58:00 -0700 Subject: [PATCH 122/211] Probably a good idea to flip with mobjscaled height --- src/p_mobj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 963619c10..96111cda9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11751,7 +11751,7 @@ void P_RespawnSpecials(void) if (mthing->options & MTF_AMBUSH && (i == MT_RING || i == MT_REDTEAMRING || i == MT_BLUETEAMRING || i == MT_COIN || P_WeaponOrPanel(i))) z -= 24 * mapobjectscale; - z -= mobjinfo[i].height; // Don't forget the height! + z -= FixedMul(mobjinfo[i].height, mapobjectscale); // Don't forget the height! } else { From fb51f60e20746efa6aa24f90ac75c7a5eda1e13e Mon Sep 17 00:00:00 2001 From: Ashnal Date: Tue, 14 Jul 2020 00:21:46 -0400 Subject: [PATCH 123/211] Add clean up to K_PuntMine This cleans up hnext when a mine shield is P_RemoveMobj'ed while being punted Otherwise, hnext could point to a removed mobj and cause undefined behavior This also fixes the bug where if you have multiple mines in your slot, drag one and have it punted, all your unused mines would disappear. This should/may fix the crashes/desyncs I've observed in gameplay when a held mine is punted. --- src/k_kart.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index cc5504bb4..c1a0bef19 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3312,7 +3312,8 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) if (!thismine || P_MobjWasRemoved(thismine)) return; - if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine + //This guarantees you hit a mine being dragged + if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine, and clean up the old one { mine = P_SpawnMobj(thismine->x, thismine->y, thismine->z, MT_SSMINE); P_SetTarget(&mine->target, thismine->target); @@ -3320,7 +3321,19 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) mine->flags2 = thismine->flags2; mine->floorz = thismine->floorz; mine->ceilingz = thismine->ceilingz; + + //Since we aren't using P_KillMobj, we need to clean up the hnext reference + { + P_SetTarget(&thismine->target->hnext, NULL); //target is the player who owns the mine + thismine->target->player->kartstuff[k_bananadrag] = 0; + thismine->target->player->kartstuff[k_itemheld] = 0; + + if (--thismine->target->player->kartstuff[k_itemamount] <= 0) + thismine->target->player->kartstuff[k_itemtype] = KITEM_NONE; + } + P_RemoveMobj(thismine); + } else mine = thismine; From bb6a5662c2610726ebe7606f77fa7128ba3787d1 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Tue, 14 Jul 2020 18:45:45 -0400 Subject: [PATCH 124/211] Fixing RocketSneakers New function specifically for dropping rocketsneakers K_DropRocketSneaker Used by Eggbox touchspecial to properly dispose of shoes and clean up hnext Now also used by the shoe thinker to drop themselves Fixes angle of spent shoe launch --- src/k_kart.c | 36 ++++++++++++++++++++++++++++++++++++ src/k_kart.h | 1 + src/p_inter.c | 3 +++ src/p_mobj.c | 13 +------------ 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index cc5504bb4..5d4ffe3f9 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3870,6 +3870,42 @@ void K_DropItems(player_t *player) K_StripItems(player); } +void K_DropRocketSneaker(player_t *player) +{ + mobj_t *shoe = player->mo; + fixed_t flingangle; + boolean leftshoe = true; //left shoie is first + while ((shoe = shoe->hnext) && !P_MobjWasRemoved(shoe)) + { + shoe->flags2 &= ~MF2_DONTDRAW; + shoe->flags &= ~MF_NOGRAVITY; + shoe->angle += ANGLE_45; + + if (shoe->eflags & MFE_VERTICALFLIP) + shoe->z -= shoe->height; + else + shoe->z += shoe->height; + + //left shoe goes off tot eh left, right shoe off to the right + if (leftshoe) + flingangle = -(ANG60); + else + flingangle = ANG60; + + S_StartSound(shoe, shoe->info->deathsound); + P_SetObjectMomZ(shoe, 8*FRACUNIT, false); + P_InstaThrust(shoe, R_PointToAngle2(shoe->target->x, shoe->target->y, shoe->x, shoe->y)+flingangle, 16*FRACUNIT); + shoe->momx += shoe->target->momx; + shoe->momy += shoe->target->momy; + shoe->momz += shoe->target->momz; + shoe->extravalue2 = 1; + + leftshoe = false; + } + P_SetTarget(&player->mo->hnext, NULL); + player->kartstuff[k_rocketsneakertimer] = 0; +} + // When an item in the hnext chain dies. void K_RepairOrbitChain(mobj_t *orbit) { diff --git a/src/k_kart.h b/src/k_kart.h index 2ba5d1bdc..93edda8ea 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -55,6 +55,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue); INT32 K_GetKartDriftSparkValue(player_t *player); void K_KartUpdatePosition(player_t *player); void K_DropItems(player_t *player); +void K_DropRocketSneaker(player_t *player); void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); diff --git a/src/p_inter.c b/src/p_inter.c index abb12811e..54fe01030 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -408,6 +408,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } else { + if (player->kartstuff[k_rocketsneakertimer]) + K_DropRocketSneaker(player); + K_DropItems(player); //K_StripItems(player); //K_StripOther(player); player->kartstuff[k_itemroulette] = 1; diff --git a/src/p_mobj.c b/src/p_mobj.c index f7f2afe34..82a003a0e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8312,18 +8312,7 @@ void P_MobjThinker(mobj_t *mobj) if (!mobj->extravalue2) { - if (mobj->eflags & MFE_VERTICALFLIP) - mobj->z -= mobj->height; - else - mobj->z += mobj->height; - - S_StartSound(mobj, mobj->info->deathsound); - P_SetObjectMomZ(mobj, 8*FRACUNIT, false); - P_InstaThrust(mobj, R_PointToAngle2(mobj->target->x, mobj->target->y, mobj->x, mobj->y)+ANGLE_90, 16*FRACUNIT); - mobj->momx += mobj->target->momx; - mobj->momy += mobj->target->momy; - mobj->momz += mobj->target->momz; - mobj->extravalue2 = 1; + K_DropRocketSneaker(mobj->target->player); } else if (P_IsObjectOnGround(mobj)) { From fef23cf7799d81d7b7d44330523a88ccef294612 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Tue, 14 Jul 2020 19:45:15 -0400 Subject: [PATCH 125/211] Some safeguards for K_DropRocketSneaker usage --- src/k_kart.c | 6 ++++++ src/p_inter.c | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5d4ffe3f9..ea80d65c5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3872,11 +3872,17 @@ void K_DropItems(player_t *player) void K_DropRocketSneaker(player_t *player) { + if (!(player->mo && !P_MobjWasRemoved(player->mo) && player->mo->hnext && !P_MobjWasRemoved(player->mo->hnext))) + return; + mobj_t *shoe = player->mo; fixed_t flingangle; boolean leftshoe = true; //left shoie is first while ((shoe = shoe->hnext) && !P_MobjWasRemoved(shoe)) { + if (shoe->type != MT_ROCKETSNEAKER) + return; //woah, not a rocketsneaker, bail! safeguard in case this gets used when you're holding non-rocketsneakers + shoe->flags2 &= ~MF2_DONTDRAW; shoe->flags &= ~MF_NOGRAVITY; shoe->angle += ANGLE_45; diff --git a/src/p_inter.c b/src/p_inter.c index 54fe01030..2a781d0a4 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -408,9 +408,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } else { - if (player->kartstuff[k_rocketsneakertimer]) - K_DropRocketSneaker(player); - + K_DropRocketSneaker(player); K_DropItems(player); //K_StripItems(player); //K_StripOther(player); player->kartstuff[k_itemroulette] = 1; From f60b6c881d3a741fb745570510c6e4351dc75d63 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Tue, 14 Jul 2020 19:50:37 -0400 Subject: [PATCH 126/211] Properly handle rocket sneakers when shrinking --- src/k_kart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.c b/src/k_kart.c index ea80d65c5..5ad368ce7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3577,6 +3577,7 @@ static void K_DoShrink(player_t *user) && !players[i].kartstuff[k_hyudorotimer]) { // Start shrinking! + K_DropRocketSneaker(&players[i]); //Make sure we handle this K_DropItems(&players[i]); players[i].kartstuff[k_growshrinktimer] = -(20*TICRATE); From f4d648526dc67cc76eb75f54340186eb3e9292ab Mon Sep 17 00:00:00 2001 From: Ashnal Date: Wed, 15 Jul 2020 09:17:35 -0400 Subject: [PATCH 127/211] Moved K_DropRocketSneaker call into K_StripItems This should catch when DropHnextList misses it Should probably fix sinks too ... --- src/k_kart.c | 3 +-- src/p_inter.c | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5ad368ce7..5c4a00757 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3577,7 +3577,6 @@ static void K_DoShrink(player_t *user) && !players[i].kartstuff[k_hyudorotimer]) { // Start shrinking! - K_DropRocketSneaker(&players[i]); //Make sure we handle this K_DropItems(&players[i]); players[i].kartstuff[k_growshrinktimer] = -(20*TICRATE); @@ -5242,7 +5241,7 @@ void K_StripItems(player_t *player) player->kartstuff[k_itemamount] = 0; player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_rocketsneakertimer] = 0; + K_DropRocketSneaker(player); if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2) { diff --git a/src/p_inter.c b/src/p_inter.c index 2a781d0a4..abb12811e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -408,7 +408,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } else { - K_DropRocketSneaker(player); K_DropItems(player); //K_StripItems(player); //K_StripOther(player); player->kartstuff[k_itemroulette] = 1; From 261016309677140f9d8ee855996fd2ddc18c3c3c Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 17 Jun 2020 22:49:12 -0700 Subject: [PATCH 128/211] Detect the compiler version and set the correct GCC flag If the version is not supported by the Makefile, the flag for the latest version supported is set instead. (cherry picked from commit bf90fbb91f28af30ff79523681e7f73e60121535) --- src/Makefile.cfg | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 43e659d75..ec2952e62 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -1,3 +1,4 @@ +# vim: ft=make # # Makefile.cfg for SRB2 # @@ -7,6 +8,42 @@ # and other things # +# See the following variable don't start with 'GCC'. This is +# to avoid a false positive with the version detection... + +SUPPORTED_GCC_VERSIONS:=\ + 91\ + 81 82 83\ + 71 72\ + 61 62 63 64\ + 51 52 53 54\ + 40 41 42 43 44 45 46 47 48 49 + +LATEST_GCC_VERSION=9.1 + +# Automatically set version flag, but not if one was manually set +ifeq (,$(filter GCC%,$(.VARIABLES))) + ifneq (,$(findstring GCC,$(shell $(CC) --version))) # if it's GCC + version:=$(shell $(CC) -dumpversion) + + # Turn version into words of major, minor + v:=$(subst ., ,$(version)) + # concat. major minor + v:=$(word 1,$(v))$(word 2,$(v)) + + # If this version is not in the list, default to the latest supported + ifeq (,$(filter $(v),$(SUPPORTED_GCC_VERSIONS))) + $(info\ + Your compiler version, GCC $(version) is not supported by the Makefile.\ + The Makefile will assume GCC $(LATEST_GCC_VERSION).) + GCC$(subst .,,$(LATEST_GCC_VERSION))=1 + else + $(info Detected GCC $(version) (GCC$(v))) + GCC$(v)=1 + endif + endif +endif + ifdef GCC91 GCC83=1 endif From 87e55c18c40f58283bc32815cf3d346139705d7b Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 17 Jun 2020 22:52:19 -0700 Subject: [PATCH 129/211] Makefile: Move the PREFIX stuff up so version detection can take advantage of (cherry picked from commit 489bb81d0065299ffea09b20cfa06dbbedcf247a) --- src/Makefile.cfg | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index ec2952e62..92eb4f935 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -21,6 +21,30 @@ SUPPORTED_GCC_VERSIONS:=\ LATEST_GCC_VERSION=9.1 +# gcc or g++ +ifdef PREFIX + CC=$(PREFIX)-gcc + CXX=$(PREFIX)-g++ + OBJCOPY=$(PREFIX)-objcopy + OBJDUMP=$(PREFIX)-objdump + STRIP=$(PREFIX)-strip + WINDRES=$(PREFIX)-windres +else + OBJCOPY=objcopy + OBJDUMP=objdump + STRIP=strip + WINDRES=windres +endif + +# because Apple screws with us on this +# need to get bintools from homebrew +ifdef MACOSX + CC=clang + CXX=clang + OBJCOPY=gobjcopy + OBJDUMP=gobjdump +endif + # Automatically set version flag, but not if one was manually set ifeq (,$(filter GCC%,$(.VARIABLES))) ifneq (,$(findstring GCC,$(shell $(CC) --version))) # if it's GCC @@ -503,30 +527,6 @@ ifdef ARCHNAME BIN:=$(BIN)/$(ARCHNAME) endif -# gcc or g++ -ifdef PREFIX - CC=$(PREFIX)-gcc - CXX=$(PREFIX)-g++ - OBJCOPY=$(PREFIX)-objcopy - OBJDUMP=$(PREFIX)-objdump - STRIP=$(PREFIX)-strip - WINDRES=$(PREFIX)-windres -else - OBJCOPY=objcopy - OBJDUMP=objdump - STRIP=strip - WINDRES=windres -endif - -# because Apple screws with us on this -# need to get bintools from homebrew -ifdef MACOSX - CC=clang - CXX=clang - OBJCOPY=gobjcopy - OBJDUMP=gobjdump -endif - OBJDUMP_OPTS?=--wide --source --line-numbers LD=$(CC) From 7c2aa5fd1903d1195232f12b6078e92e15b97a69 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 17 Jun 2020 22:58:11 -0700 Subject: [PATCH 130/211] Forgot a comma (cherry picked from commit 193c45aa2f555b56f548f70e7fa0d74a1ce4e412) --- src/Makefile.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 92eb4f935..f9f3b64ab 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -58,7 +58,7 @@ ifeq (,$(filter GCC%,$(.VARIABLES))) # If this version is not in the list, default to the latest supported ifeq (,$(filter $(v),$(SUPPORTED_GCC_VERSIONS))) $(info\ - Your compiler version, GCC $(version) is not supported by the Makefile.\ + Your compiler version, GCC $(version), is not supported by the Makefile.\ The Makefile will assume GCC $(LATEST_GCC_VERSION).) GCC$(subst .,,$(LATEST_GCC_VERSION))=1 else From 755a0e94403bdadc93d90b331f4d1026efe83f58 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 11 Jul 2020 12:45:35 -0700 Subject: [PATCH 131/211] It's not always GCC, but it probably is gcc (I hope) (cherry picked from commit 4e1d54c3322c17276e532ab4d9e875a8c8ebc399) --- src/Makefile.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.cfg b/src/Makefile.cfg index f9f3b64ab..b418eaf36 100644 --- a/src/Makefile.cfg +++ b/src/Makefile.cfg @@ -47,7 +47,7 @@ endif # Automatically set version flag, but not if one was manually set ifeq (,$(filter GCC%,$(.VARIABLES))) - ifneq (,$(findstring GCC,$(shell $(CC) --version))) # if it's GCC + ifneq (,$(findstring gcc,$(shell $(CC) --version))) # if it's GCC version:=$(shell $(CC) -dumpversion) # Turn version into words of major, minor From 59c0f99865650841c953b5e5821ed3f30639e184 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 17 Jul 2020 23:44:00 -0700 Subject: [PATCH 132/211] 5th frame + rainbow colors cycle on the drift explody --- src/dehacked.c | 1 + src/info.c | 3 ++- src/info.h | 1 + src/k_kart.c | 8 ++++---- src/p_mobj.c | 6 ++++-- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index d8a3f6d01..0bdf43324 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6585,6 +6585,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIFTEXPLODE2", "S_DRIFTEXPLODE3", "S_DRIFTEXPLODE4", + "S_DRIFTEXPLODE5", // Sneaker boost effect "S_BOOSTFLAME", diff --git a/src/info.c b/src/info.c index ca367fa75..6a5c93ce1 100644 --- a/src/info.c +++ b/src/info.c @@ -2563,7 +2563,8 @@ state_t states[NUMSTATES] = {SPR_DBOS, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTEXPLODE2}, // S_DRIFTEXPLODE1 {SPR_DBOS, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTEXPLODE3}, // S_DRIFTEXPLODE2 {SPR_DBOS, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTEXPLODE4}, // S_DRIFTEXPLODE3 - {SPR_DBOS, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE4 + {SPR_DBOS, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTEXPLODE5}, // S_DRIFTEXPLODE4 + {SPR_DBOS, FF_FULLBRIGHT|4, 2, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE5 {SPR_BOST, FF_FULLBRIGHT|FF_ANIMATE, TICRATE, {NULL}, 6, 1, S_BOOSTSMOKESPAWNER}, // S_BOOSTFLAME {SPR_NULL, 0, TICRATE/2, {NULL}, 0, 0, S_NULL}, // S_BOOSTSMOKESPAWNER diff --git a/src/info.h b/src/info.h index c79f2ae5e..27ec301a7 100644 --- a/src/info.h +++ b/src/info.h @@ -3246,6 +3246,7 @@ typedef enum state S_DRIFTEXPLODE2, S_DRIFTEXPLODE3, S_DRIFTEXPLODE4, + S_DRIFTEXPLODE5, // Sneaker boost effect S_BOOSTFLAME, diff --git a/src/k_kart.c b/src/k_kart.c index 295cdc540..8aba6cf34 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6364,7 +6364,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 15; overlay->color = SKINCOLOR_GOLD; - overlay->fuse = 8; + overlay->fuse = 10; } else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) { @@ -6376,7 +6376,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 20; overlay->color = SKINCOLOR_KETCHUP; - overlay->fuse = 16; + overlay->fuse = 20; } else if (player->kartstuff[k_driftcharge] < dsthree) { @@ -6388,7 +6388,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 50; overlay->color = SKINCOLOR_SAPPHIRE; - overlay->fuse = 32; + overlay->fuse = 40; } else if (player->kartstuff[k_driftcharge] >= dsthree) { @@ -6400,7 +6400,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 125; overlay->color = SKINCOLOR_SILVER; - overlay->fuse = 48; + overlay->fuse = 120; } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 735000db1..971990dc0 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8331,10 +8331,12 @@ void P_MobjThinker(mobj_t *mobj) mobj->modeltilt = mobj->target->modeltilt; #endif - if (mobj->fuse <= 16) + if (mobj->fuse <= 20) mobj->color = SKINCOLOR_KETCHUP; - else if (mobj->fuse <= 32) + else if (mobj->fuse <= 40) mobj->color = SKINCOLOR_SAPPHIRE; + else if (mobj->fuse > 40) + mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); { player_t *p = NULL; From 1dae3d196bd328b18aefa7ec532e9c962dfc5a74 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 18 Jul 2020 14:52:30 -0700 Subject: [PATCH 133/211] Who said anything about a 5th frame? --- src/dehacked.c | 1 - src/info.c | 3 +-- src/info.h | 1 - src/k_kart.c | 4 ++-- src/p_mobj.c | 6 +++--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 0bdf43324..d8a3f6d01 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6585,7 +6585,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIFTEXPLODE2", "S_DRIFTEXPLODE3", "S_DRIFTEXPLODE4", - "S_DRIFTEXPLODE5", // Sneaker boost effect "S_BOOSTFLAME", diff --git a/src/info.c b/src/info.c index 6a5c93ce1..ca367fa75 100644 --- a/src/info.c +++ b/src/info.c @@ -2563,8 +2563,7 @@ state_t states[NUMSTATES] = {SPR_DBOS, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTEXPLODE2}, // S_DRIFTEXPLODE1 {SPR_DBOS, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTEXPLODE3}, // S_DRIFTEXPLODE2 {SPR_DBOS, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTEXPLODE4}, // S_DRIFTEXPLODE3 - {SPR_DBOS, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTEXPLODE5}, // S_DRIFTEXPLODE4 - {SPR_DBOS, FF_FULLBRIGHT|4, 2, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE5 + {SPR_DBOS, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE4 {SPR_BOST, FF_FULLBRIGHT|FF_ANIMATE, TICRATE, {NULL}, 6, 1, S_BOOSTSMOKESPAWNER}, // S_BOOSTFLAME {SPR_NULL, 0, TICRATE/2, {NULL}, 0, 0, S_NULL}, // S_BOOSTSMOKESPAWNER diff --git a/src/info.h b/src/info.h index 27ec301a7..c79f2ae5e 100644 --- a/src/info.h +++ b/src/info.h @@ -3246,7 +3246,6 @@ typedef enum state S_DRIFTEXPLODE2, S_DRIFTEXPLODE3, S_DRIFTEXPLODE4, - S_DRIFTEXPLODE5, // Sneaker boost effect S_BOOSTFLAME, diff --git a/src/k_kart.c b/src/k_kart.c index 8aba6cf34..d87d7f092 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6376,7 +6376,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 20; overlay->color = SKINCOLOR_KETCHUP; - overlay->fuse = 20; + overlay->fuse = 16; } else if (player->kartstuff[k_driftcharge] < dsthree) { @@ -6388,7 +6388,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftboost] = 50; overlay->color = SKINCOLOR_SAPPHIRE; - overlay->fuse = 40; + overlay->fuse = 32; } else if (player->kartstuff[k_driftcharge] >= dsthree) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 971990dc0..ac5b35121 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8331,11 +8331,11 @@ void P_MobjThinker(mobj_t *mobj) mobj->modeltilt = mobj->target->modeltilt; #endif - if (mobj->fuse <= 20) + if (mobj->fuse <= 16) mobj->color = SKINCOLOR_KETCHUP; - else if (mobj->fuse <= 40) + else if (mobj->fuse <= 32) mobj->color = SKINCOLOR_SAPPHIRE; - else if (mobj->fuse > 40) + else if (mobj->fuse > 32) mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); { From f8b19a239f29185edeb0f938c3a34c7cd428f883 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 19 Jul 2020 18:04:11 -0700 Subject: [PATCH 134/211] Drift Boost Clip, bounces on floor and sparks, then flickers until it dies --- src/dehacked.c | 15 +++++++++++ src/info.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++- src/info.h | 18 ++++++++++++++ src/k_kart.c | 29 ++++++++++++++++++++++ src/k_kart.h | 1 + src/p_mobj.c | 15 +++++++++++ 6 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/dehacked.c b/src/dehacked.c index d8a3f6d01..336a407a1 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6586,6 +6586,19 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIFTEXPLODE3", "S_DRIFTEXPLODE4", + // Drift boost clip + "S_DRIFTCLIP1", + "S_DRIFTCLIP2", + "S_DRIFTCLIP3", + "S_DRIFTCLIP4", + "S_DRIFTCLIP5", + "S_DRIFTCLIP6", + "S_DRIFTCLIP7", + "S_DRIFTCLIP8", + + // Drift boost clip spark + "S_DRIFTCLIPSPARK", + // Sneaker boost effect "S_BOOSTFLAME", "S_BOOSTSMOKESPAWNER", @@ -8093,6 +8106,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_FASTLINE", "MT_FASTDUST", "MT_DRIFTEXPLODE", + "MT_DRIFTCLIP", + "MT_DRIFTCLIPSPARK", "MT_BOOSTFLAME", "MT_BOOSTSMOKE", "MT_SNEAKERTRAIL", diff --git a/src/info.c b/src/info.c index ca367fa75..0a0f827dc 100644 --- a/src/info.c +++ b/src/info.c @@ -72,7 +72,7 @@ char sprnames[NUMSPRITES + 1][5] = "BFRT","OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN", "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","EGOO","WTRL","XMS4", - "XMS5","FBUB","GCHA","CHEZ","VIEW" + "XMS5","FBUB","GCHA","CHEZ","VIEW","DBCL","DBNC", }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -2565,6 +2565,17 @@ state_t states[NUMSTATES] = {SPR_DBOS, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTEXPLODE4}, // S_DRIFTEXPLODE3 {SPR_DBOS, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE4 + {SPR_DBCL, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTCLIP2}, // S_DRIFTCLIP1 + {SPR_DBCL, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTCLIP3}, // S_DRIFTCLIP2 + {SPR_DBCL, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTCLIP4}, // S_DRIFTCLIP3 + {SPR_DBCL, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTCLIP5}, // S_DRIFTCLIP4 + {SPR_DBCL, FF_FULLBRIGHT|4, 2, {NULL}, 6, 1, S_DRIFTCLIP6}, // S_DRIFTCLIP5 + {SPR_DBCL, FF_FULLBRIGHT|5, 2, {NULL}, 6, 1, S_DRIFTCLIP7}, // S_DRIFTCLIP6 + {SPR_DBCL, FF_FULLBRIGHT|6, 2, {NULL}, 6, 1, S_DRIFTCLIP8}, // S_DRIFTCLIP7 + {SPR_DBCL, FF_FULLBRIGHT|7, 2, {NULL}, 6, 1, S_DRIFTCLIP1}, // S_DRIFTCLIP8 + + {SPR_DBNC, FF_FULLBRIGHT|FF_ANIMATE, 14, {NULL}, 6, 1, S_NULL}, // S_DRIFTCLIPSPARK + {SPR_BOST, FF_FULLBRIGHT|FF_ANIMATE, TICRATE, {NULL}, 6, 1, S_BOOSTSMOKESPAWNER}, // S_BOOSTFLAME {SPR_NULL, 0, TICRATE/2, {NULL}, 0, 0, S_NULL}, // S_BOOSTSMOKESPAWNER @@ -15346,6 +15357,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_DRIFTCLIP + -1, // doomednum + S_DRIFTCLIP1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 8, // speed + 32*FRACUNIT, // radius + 64*FRACUNIT, // height + 1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_DONTENCOREMAP|MF_GRENADEBOUNCE|MF_BOUNCE, // flags + S_NULL // raisestate + }, + + { // MT_DRIFTCLIPSPARK + -1, // doomednum + S_DRIFTCLIPSPARK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 8, // speed + 32*FRACUNIT, // radius + 64*FRACUNIT, // height + 1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_BOOSTFLAME -1, // doomednum S_BOOSTFLAME, // spawnstate diff --git a/src/info.h b/src/info.h index c79f2ae5e..a0bc61088 100644 --- a/src/info.h +++ b/src/info.h @@ -811,6 +811,9 @@ typedef enum sprite // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, + SPR_DBCL, // Drift boost clip + SPR_DBNC, // Drift boost clip's sparks + SPR_FIRSTFREESLOT, SPR_LASTFREESLOT = SPR_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1, NUMSPRITES @@ -3247,6 +3250,19 @@ typedef enum state S_DRIFTEXPLODE3, S_DRIFTEXPLODE4, + // Drift boost clip + S_DRIFTCLIP1, + S_DRIFTCLIP2, + S_DRIFTCLIP3, + S_DRIFTCLIP4, + S_DRIFTCLIP5, + S_DRIFTCLIP6, + S_DRIFTCLIP7, + S_DRIFTCLIP8, + + // Drift boost clip sparks + S_DRIFTCLIPSPARK, + // Sneaker boost effect S_BOOSTFLAME, S_BOOSTSMOKESPAWNER, @@ -4791,6 +4807,8 @@ typedef enum mobj_type MT_FASTLINE, MT_FASTDUST, MT_DRIFTEXPLODE, + MT_DRIFTCLIP, + MT_DRIFTCLIPSPARK, MT_BOOSTFLAME, MT_BOOSTSMOKE, MT_SNEAKERTRAIL, diff --git a/src/k_kart.c b/src/k_kart.c index d87d7f092..aed96ad27 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1648,6 +1648,35 @@ static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the m sparks->flags2 |= MF2_DONTDRAW; } +static fixed_t K_RandomFlip(fixed_t f) +{ + return ( ( leveltime & 1 ) ? f : -f ); +} + +void K_SpawnDriftBoostClip(player_t *player) +{ + mobj_t *clip; + fixed_t scale = 115*FRACUNIT/100; + + clip = P_SpawnMobj( + player->mo->x, + player->mo->y, + player->mo->z + player->mo->height, + MT_DRIFTCLIP + ); + + P_SetTarget(&clip->target, player->mo); + P_SetScale(clip, ( clip->destscale = FixedMul(scale, player->mo->scale) )); + K_MatchGenericExtraFlags(clip, player->mo); + + clip->fuse = 105; + clip->momz = 4 * clip->scale; + + P_InstaThrust(clip, player->mo->angle + + K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), + FixedMul(scale, player->speed)); +} + /** \brief Handles the state changing for moving players, moved here to eliminate duplicate code \param player player data diff --git a/src/k_kart.h b/src/k_kart.h index 2508b6f63..f4bf84cc8 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -30,6 +30,7 @@ void K_FlipFromObject(mobj_t *mo, mobj_t *master); void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master); void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master); void K_SpawnDashDustRelease(player_t *player); +void K_SpawnDriftBoostClip(player_t *player); void K_KartMoveAnimation(player_t *player); void K_KartPlayerHUDUpdate(player_t *player); void K_KartPlayerThink(player_t *player, ticcmd_t *cmd); diff --git a/src/p_mobj.c b/src/p_mobj.c index ac5b35121..8e15b3507 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2457,6 +2457,18 @@ static boolean P_ZMovement(mobj_t *mo) mom.z = P_MobjFlip(mo)*FixedMul(5*FRACUNIT, mo->scale); else if (mo->type == MT_SPINFIRE) // elemental shield fire is another exception here ; + else if (mo->type == MT_DRIFTCLIP) + { + mom.z = -mom.z/2; + if (abs(mom.z) > 4 * mo->scale / 3) + { + mobj_t *spark = P_SpawnMobj(mo->x, mo->y, mo->z, MT_DRIFTCLIPSPARK); + spark->momx = mo->momx/2; + spark->momy = mo->momy/2; + } + else + mo->flags2 ^= MF2_DONTDRAW; + } else if (mo->flags & MF_MISSILE) { if (!(mo->flags & MF_NOCLIP)) @@ -8338,6 +8350,9 @@ void P_MobjThinker(mobj_t *mobj) else if (mobj->fuse > 32) mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + if (mobj->fuse == 17 || mobj->fuse == 33)/* to red/blue */ + K_SpawnDriftBoostClip(mobj->target->player); + { player_t *p = NULL; if (mobj->target->target && mobj->target->target->player) From 3ff00e851e0e352b3d86281387128b2dd3b50432 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 19 Jul 2020 18:09:52 -0700 Subject: [PATCH 135/211] Better spawning for the clip spark --- src/k_kart.c | 14 ++++++++++++++ src/k_kart.h | 1 + src/p_mobj.c | 6 +----- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index aed96ad27..495e2ca2d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1677,6 +1677,20 @@ void K_SpawnDriftBoostClip(player_t *player) FixedMul(scale, player->speed)); } +void K_SpawnDriftBoostClipSpark(mobj_t *clip) +{ + mobj_t *spark; + + spark = P_SpawnMobj(clip->x, clip->y, clip->z, MT_DRIFTCLIPSPARK); + + P_SetTarget(&spark->target, clip); + P_SetScale(spark, ( spark->destscale = clip->scale )); + K_MatchGenericExtraFlags(spark, clip); + + spark->momx = clip->momx/2; + spark->momy = clip->momx/2; +} + /** \brief Handles the state changing for moving players, moved here to eliminate duplicate code \param player player data diff --git a/src/k_kart.h b/src/k_kart.h index f4bf84cc8..be0ce6bf0 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -31,6 +31,7 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master); void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master); void K_SpawnDashDustRelease(player_t *player); void K_SpawnDriftBoostClip(player_t *player); +void K_SpawnDriftBoostClipSpark(mobj_t *clip); void K_KartMoveAnimation(player_t *player); void K_KartPlayerHUDUpdate(player_t *player); void K_KartPlayerThink(player_t *player, ticcmd_t *cmd); diff --git a/src/p_mobj.c b/src/p_mobj.c index 8e15b3507..ec25abff5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2461,11 +2461,7 @@ static boolean P_ZMovement(mobj_t *mo) { mom.z = -mom.z/2; if (abs(mom.z) > 4 * mo->scale / 3) - { - mobj_t *spark = P_SpawnMobj(mo->x, mo->y, mo->z, MT_DRIFTCLIPSPARK); - spark->momx = mo->momx/2; - spark->momy = mo->momy/2; - } + K_SpawnDriftBoostClipSpark(mo); else mo->flags2 ^= MF2_DONTDRAW; } From 280791a8327218cc99d3b9a35f7950d87378eb15 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 19 Jul 2020 19:11:19 -0700 Subject: [PATCH 136/211] Drift Boost Plumes, play every other frame of Drift Boost Explosion (normally flicker frames) --- src/dehacked.c | 4 ++++ src/info.c | 14 +++++++++----- src/info.h | 5 +++++ src/p_mobj.c | 25 ++++++++++++++++++++++--- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 336a407a1..f9cd3e962 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6585,6 +6585,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIFTEXPLODE2", "S_DRIFTEXPLODE3", "S_DRIFTEXPLODE4", + "S_DRIFTEXPLODE5", + "S_DRIFTEXPLODE6", + "S_DRIFTEXPLODE7", + "S_DRIFTEXPLODE8", // Drift boost clip "S_DRIFTCLIP1", diff --git a/src/info.c b/src/info.c index 0a0f827dc..a4c67cb8a 100644 --- a/src/info.c +++ b/src/info.c @@ -72,7 +72,7 @@ char sprnames[NUMSPRITES + 1][5] = "BFRT","OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN", "FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","EGOO","WTRL","XMS4", - "XMS5","FBUB","GCHA","CHEZ","VIEW","DBCL","DBNC", + "XMS5","FBUB","GCHA","CHEZ","VIEW","DBCL","DBNC","DBST", }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -2560,10 +2560,14 @@ state_t states[NUMSTATES] = {SPR_DSHR, FF_PAPERSPRITE|5, 1, {NULL}, 0, 0, S_FASTDUST7}, // S_FASTDUST6 {SPR_DSHR, FF_PAPERSPRITE|6, 1, {NULL}, 0, 0, S_NULL}, // S_FASTDUST7 - {SPR_DBOS, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTEXPLODE2}, // S_DRIFTEXPLODE1 - {SPR_DBOS, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTEXPLODE3}, // S_DRIFTEXPLODE2 - {SPR_DBOS, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTEXPLODE4}, // S_DRIFTEXPLODE3 - {SPR_DBOS, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE4 + {SPR_DBOS, FF_FULLBRIGHT, 1, {NULL}, 6, 1, S_DRIFTEXPLODE2}, // S_DRIFTEXPLODE1 + {SPR_DBST, FF_PAPERSPRITE|FF_FULLBRIGHT, 1, {NULL}, 6, 1, S_DRIFTEXPLODE3}, // S_DRIFTEXPLODE2 + {SPR_DBOS, FF_FULLBRIGHT|1, 1, {NULL}, 6, 1, S_DRIFTEXPLODE4}, // S_DRIFTEXPLODE3 + {SPR_DBST, FF_PAPERSPRITE|FF_FULLBRIGHT|1, 1, {NULL}, 6, 1, S_DRIFTEXPLODE5}, // S_DRIFTEXPLODE4 + {SPR_DBOS, FF_FULLBRIGHT|2, 1, {NULL}, 6, 1, S_DRIFTEXPLODE6}, // S_DRIFTEXPLODE5 + {SPR_DBST, FF_PAPERSPRITE|FF_FULLBRIGHT|2, 1, {NULL}, 6, 1, S_DRIFTEXPLODE7}, // S_DRIFTEXPLODE6 + {SPR_DBOS, FF_FULLBRIGHT|3, 1, {NULL}, 6, 1, S_DRIFTEXPLODE8}, // S_DRIFTEXPLODE7 + {SPR_DBST, FF_PAPERSPRITE|FF_FULLBRIGHT|3, 1, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE8 {SPR_DBCL, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTCLIP2}, // S_DRIFTCLIP1 {SPR_DBCL, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTCLIP3}, // S_DRIFTCLIP2 diff --git a/src/info.h b/src/info.h index a0bc61088..00c8b863a 100644 --- a/src/info.h +++ b/src/info.h @@ -813,6 +813,7 @@ typedef enum sprite SPR_DBCL, // Drift boost clip SPR_DBNC, // Drift boost clip's sparks + SPR_DBST, // Drift boost plume SPR_FIRSTFREESLOT, SPR_LASTFREESLOT = SPR_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1, @@ -3249,6 +3250,10 @@ typedef enum state S_DRIFTEXPLODE2, S_DRIFTEXPLODE3, S_DRIFTEXPLODE4, + S_DRIFTEXPLODE5, + S_DRIFTEXPLODE6, + S_DRIFTEXPLODE7, + S_DRIFTEXPLODE8, // Drift boost clip S_DRIFTCLIP1, diff --git a/src/p_mobj.c b/src/p_mobj.c index ec25abff5..e65d4aef1 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8329,18 +8329,37 @@ void P_MobjThinker(mobj_t *mobj) //mobj->angle = mobj->target->angle; { angle_t angle = R_PointToAngle2(0, 0, mobj->target->momx, mobj->target->momy); + fixed_t nudge; + mobj->angle = angle; - P_TeleportMove(mobj, mobj->target->x + P_ReturnThrustX(mobj, angle+ANGLE_180, 4*mobj->target->radius), - mobj->target->y + P_ReturnThrustY(mobj, angle+ANGLE_180, 4*mobj->target->radius), mobj->target->z); + + if (( mobj->fuse & 1 )) + { + nudge = 4*mobj->target->radius; + } + else + { + nudge = 2*mobj->target->radius; + /* rotate the papersprite frames to see the flat angle */ + mobj->angle += ANGLE_90; + } + + P_TeleportMove(mobj, + mobj->target->x + P_ReturnThrustX(mobj, angle + ANGLE_180, nudge), + mobj->target->y + P_ReturnThrustY(mobj, angle + ANGLE_180, nudge), + mobj->target->z); } P_SetScale(mobj, mobj->target->scale); - mobj->flags2 ^= MF2_DONTDRAW; #ifdef HWRENDER mobj->modeltilt = mobj->target->modeltilt; #endif if (mobj->fuse <= 16) + { mobj->color = SKINCOLOR_KETCHUP; + /* don't draw papersprite frames after blue boost */ + mobj->flags2 ^= MF2_DONTDRAW; + } else if (mobj->fuse <= 32) mobj->color = SKINCOLOR_SAPPHIRE; else if (mobj->fuse > 32) From b23581ac38810ebe56d89f23fc24f0a965e065d5 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 19 Jul 2020 21:05:29 -0700 Subject: [PATCH 137/211] The drift boost clip is fatter --- src/dehacked.c | 32 ++++++++++++++++++++++++-------- src/info.c | 37 +++++++++++++++++++++++++++---------- src/info.h | 32 ++++++++++++++++++++++++-------- src/k_kart.c | 2 +- src/p_mobj.c | 3 +++ 5 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index f9cd3e962..2915fef2f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -6591,14 +6591,30 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DRIFTEXPLODE8", // Drift boost clip - "S_DRIFTCLIP1", - "S_DRIFTCLIP2", - "S_DRIFTCLIP3", - "S_DRIFTCLIP4", - "S_DRIFTCLIP5", - "S_DRIFTCLIP6", - "S_DRIFTCLIP7", - "S_DRIFTCLIP8", + "S_DRIFTCLIPA1", + "S_DRIFTCLIPA2", + "S_DRIFTCLIPA3", + "S_DRIFTCLIPA4", + "S_DRIFTCLIPA5", + "S_DRIFTCLIPA6", + "S_DRIFTCLIPA7", + "S_DRIFTCLIPA8", + "S_DRIFTCLIPA9", + "S_DRIFTCLIPA10", + "S_DRIFTCLIPA11", + "S_DRIFTCLIPA12", + "S_DRIFTCLIPA13", + "S_DRIFTCLIPA14", + "S_DRIFTCLIPA15", + "S_DRIFTCLIPA16", + "S_DRIFTCLIPB1", + "S_DRIFTCLIPB2", + "S_DRIFTCLIPB3", + "S_DRIFTCLIPB4", + "S_DRIFTCLIPB5", + "S_DRIFTCLIPB6", + "S_DRIFTCLIPB7", + "S_DRIFTCLIPB8", // Drift boost clip spark "S_DRIFTCLIPSPARK", diff --git a/src/info.c b/src/info.c index a4c67cb8a..e67a7be69 100644 --- a/src/info.c +++ b/src/info.c @@ -2569,14 +2569,31 @@ state_t states[NUMSTATES] = {SPR_DBOS, FF_FULLBRIGHT|3, 1, {NULL}, 6, 1, S_DRIFTEXPLODE8}, // S_DRIFTEXPLODE7 {SPR_DBST, FF_PAPERSPRITE|FF_FULLBRIGHT|3, 1, {NULL}, 6, 1, S_DRIFTEXPLODE1}, // S_DRIFTEXPLODE8 - {SPR_DBCL, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTCLIP2}, // S_DRIFTCLIP1 - {SPR_DBCL, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTCLIP3}, // S_DRIFTCLIP2 - {SPR_DBCL, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTCLIP4}, // S_DRIFTCLIP3 - {SPR_DBCL, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTCLIP5}, // S_DRIFTCLIP4 - {SPR_DBCL, FF_FULLBRIGHT|4, 2, {NULL}, 6, 1, S_DRIFTCLIP6}, // S_DRIFTCLIP5 - {SPR_DBCL, FF_FULLBRIGHT|5, 2, {NULL}, 6, 1, S_DRIFTCLIP7}, // S_DRIFTCLIP6 - {SPR_DBCL, FF_FULLBRIGHT|6, 2, {NULL}, 6, 1, S_DRIFTCLIP8}, // S_DRIFTCLIP7 - {SPR_DBCL, FF_FULLBRIGHT|7, 2, {NULL}, 6, 1, S_DRIFTCLIP1}, // S_DRIFTCLIP8 + {SPR_DBCL, FF_FULLBRIGHT|0x0, 1, {NULL}, 6, 1, S_DRIFTCLIPA2}, // S_DRIFTCLIPA1 + {SPR_DBCL, FF_FULLBRIGHT|0x8, 1, {NULL}, 6, 1, S_DRIFTCLIPA3}, // S_DRIFTCLIPA2 + {SPR_DBCL, FF_FULLBRIGHT|0x1, 1, {NULL}, 6, 1, S_DRIFTCLIPA4}, // S_DRIFTCLIPA3 + {SPR_DBCL, FF_FULLBRIGHT|0x9, 1, {NULL}, 6, 1, S_DRIFTCLIPA5}, // S_DRIFTCLIPA4 + {SPR_DBCL, FF_FULLBRIGHT|0x2, 1, {NULL}, 6, 1, S_DRIFTCLIPA6}, // S_DRIFTCLIPA5 + {SPR_DBCL, FF_FULLBRIGHT|0xA, 1, {NULL}, 6, 1, S_DRIFTCLIPA7}, // S_DRIFTCLIPA6 + {SPR_DBCL, FF_FULLBRIGHT|0x3, 1, {NULL}, 6, 1, S_DRIFTCLIPA8}, // S_DRIFTCLIPA7 + {SPR_DBCL, FF_FULLBRIGHT|0xB, 1, {NULL}, 6, 1, S_DRIFTCLIPA9}, // S_DRIFTCLIPA8 + {SPR_DBCL, FF_FULLBRIGHT|0x4, 1, {NULL}, 6, 1, S_DRIFTCLIPA10}, // S_DRIFTCLIPA9 + {SPR_DBCL, FF_FULLBRIGHT|0xC, 1, {NULL}, 6, 1, S_DRIFTCLIPA11}, // S_DRIFTCLIPA10 + {SPR_DBCL, FF_FULLBRIGHT|0x5, 1, {NULL}, 6, 1, S_DRIFTCLIPA12}, // S_DRIFTCLIPA11 + {SPR_DBCL, FF_FULLBRIGHT|0xD, 1, {NULL}, 6, 1, S_DRIFTCLIPA13}, // S_DRIFTCLIPA12 + {SPR_DBCL, FF_FULLBRIGHT|0x6, 1, {NULL}, 6, 1, S_DRIFTCLIPA14}, // S_DRIFTCLIPA13 + {SPR_DBCL, FF_FULLBRIGHT|0xE, 1, {NULL}, 6, 1, S_DRIFTCLIPA15}, // S_DRIFTCLIPA14 + {SPR_DBCL, FF_FULLBRIGHT|0x7, 1, {NULL}, 6, 1, S_DRIFTCLIPA16}, // S_DRIFTCLIPA15 + {SPR_DBCL, FF_FULLBRIGHT|0xF, 1, {NULL}, 6, 1, S_DRIFTCLIPB1}, // S_DRIFTCLIPA16 + + {SPR_DBCL, FF_FULLBRIGHT, 2, {NULL}, 6, 1, S_DRIFTCLIPB2}, // S_DRIFTCLIPB1 + {SPR_DBCL, FF_FULLBRIGHT|1, 2, {NULL}, 6, 1, S_DRIFTCLIPB3}, // S_DRIFTCLIPB2 + {SPR_DBCL, FF_FULLBRIGHT|2, 2, {NULL}, 6, 1, S_DRIFTCLIPB4}, // S_DRIFTCLIPB3 + {SPR_DBCL, FF_FULLBRIGHT|3, 2, {NULL}, 6, 1, S_DRIFTCLIPB5}, // S_DRIFTCLIPB4 + {SPR_DBCL, FF_FULLBRIGHT|4, 2, {NULL}, 6, 1, S_DRIFTCLIPB6}, // S_DRIFTCLIPB5 + {SPR_DBCL, FF_FULLBRIGHT|5, 2, {NULL}, 6, 1, S_DRIFTCLIPB7}, // S_DRIFTCLIPB6 + {SPR_DBCL, FF_FULLBRIGHT|6, 2, {NULL}, 6, 1, S_DRIFTCLIPB8}, // S_DRIFTCLIPB7 + {SPR_DBCL, FF_FULLBRIGHT|7, 2, {NULL}, 6, 1, S_DRIFTCLIPB1}, // S_DRIFTCLIPB8 {SPR_DBNC, FF_FULLBRIGHT|FF_ANIMATE, 14, {NULL}, 6, 1, S_NULL}, // S_DRIFTCLIPSPARK @@ -15363,7 +15380,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_DRIFTCLIP -1, // doomednum - S_DRIFTCLIP1, // spawnstate + S_DRIFTCLIPA1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -15377,7 +15394,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound - 8, // speed + 105, // speed 32*FRACUNIT, // radius 64*FRACUNIT, // height 1, // display offset diff --git a/src/info.h b/src/info.h index 00c8b863a..e34e7bd53 100644 --- a/src/info.h +++ b/src/info.h @@ -3256,14 +3256,30 @@ typedef enum state S_DRIFTEXPLODE8, // Drift boost clip - S_DRIFTCLIP1, - S_DRIFTCLIP2, - S_DRIFTCLIP3, - S_DRIFTCLIP4, - S_DRIFTCLIP5, - S_DRIFTCLIP6, - S_DRIFTCLIP7, - S_DRIFTCLIP8, + S_DRIFTCLIPA1, + S_DRIFTCLIPA2, + S_DRIFTCLIPA3, + S_DRIFTCLIPA4, + S_DRIFTCLIPA5, + S_DRIFTCLIPA6, + S_DRIFTCLIPA7, + S_DRIFTCLIPA8, + S_DRIFTCLIPA9, + S_DRIFTCLIPA10, + S_DRIFTCLIPA11, + S_DRIFTCLIPA12, + S_DRIFTCLIPA13, + S_DRIFTCLIPA14, + S_DRIFTCLIPA15, + S_DRIFTCLIPA16, + S_DRIFTCLIPB1, + S_DRIFTCLIPB2, + S_DRIFTCLIPB3, + S_DRIFTCLIPB4, + S_DRIFTCLIPB5, + S_DRIFTCLIPB6, + S_DRIFTCLIPB7, + S_DRIFTCLIPB8, // Drift boost clip sparks S_DRIFTCLIPSPARK, diff --git a/src/k_kart.c b/src/k_kart.c index 495e2ca2d..804d9034c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1670,7 +1670,7 @@ void K_SpawnDriftBoostClip(player_t *player) K_MatchGenericExtraFlags(clip, player->mo); clip->fuse = 105; - clip->momz = 4 * clip->scale; + clip->momz = 7 * clip->scale; P_InstaThrust(clip, player->mo->angle + K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), diff --git a/src/p_mobj.c b/src/p_mobj.c index e65d4aef1..40756214a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10795,6 +10795,9 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) case MT_FLOATINGITEM: thing->shadowscale = FRACUNIT/2; break; + case MT_DRIFTCLIP: + thing->shadowscale = FRACUNIT/3; + break; default: if (thing->flags & (MF_ENEMY|MF_BOSS)) thing->shadowscale = FRACUNIT; From 37aedad2881bc0e7d2230bc321406381bdeb5f9a Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 19 Jul 2020 21:49:23 -0700 Subject: [PATCH 138/211] Sounds for big blue boost and bullet clip bouncing --- src/k_kart.c | 2 ++ src/p_mobj.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 804d9034c..f90ecca37 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6432,6 +6432,8 @@ static void K_KartDrift(player_t *player, boolean onground) overlay->color = SKINCOLOR_SAPPHIRE; overlay->fuse = 32; + + S_StartSound(player->mo, sfx_kc5b); } else if (player->kartstuff[k_driftcharge] >= dsthree) { diff --git a/src/p_mobj.c b/src/p_mobj.c index 40756214a..3bda4e05c 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2461,7 +2461,10 @@ static boolean P_ZMovement(mobj_t *mo) { mom.z = -mom.z/2; if (abs(mom.z) > 4 * mo->scale / 3) + { K_SpawnDriftBoostClipSpark(mo); + S_StartSound(mo, sfx_tink); + } else mo->flags2 ^= MF2_DONTDRAW; } From 5efc34e43b81637d0ae086cea90241bacb576ac6 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 19 Jul 2020 22:18:56 -0700 Subject: [PATCH 139/211] Cleanup code and reverse gravity --- src/k_kart.c | 70 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f90ecca37..98b81fcad 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1657,20 +1657,21 @@ void K_SpawnDriftBoostClip(player_t *player) { mobj_t *clip; fixed_t scale = 115*FRACUNIT/100; + fixed_t z; - clip = P_SpawnMobj( - player->mo->x, - player->mo->y, - player->mo->z + player->mo->height, - MT_DRIFTCLIP - ); + if (( player->mo->eflags & MFE_VERTICALFLIP )) + z = player->mo->z; + else + z = player->mo->z + player->mo->height; + + clip = P_SpawnMobj(player->mo->x, player->mo->y, z, MT_DRIFTCLIP); P_SetTarget(&clip->target, player->mo); P_SetScale(clip, ( clip->destscale = FixedMul(scale, player->mo->scale) )); K_MatchGenericExtraFlags(clip, player->mo); clip->fuse = 105; - clip->momz = 7 * clip->scale; + clip->momz = 7 * P_MobjFlip(clip) * clip->scale; P_InstaThrust(clip, player->mo->angle + K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), @@ -6373,6 +6374,43 @@ INT32 K_GetKartDriftSparkValue(player_t *player) return (26*4 + kartspeed*2 + (9 - player->kartweight))*8; } +/* +Stage 1: red sparks +Stage 2: blue sparks +Stage 3: big large rainbow sparks +*/ +static void K_SpawnDriftBoostExplosion(player_t *player, int stage) +{ + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); + + P_SetTarget(&overlay->target, player->mo); + P_SetScale(overlay, (overlay->destscale = player->mo->scale)); + K_FlipFromObject(overlay, player->mo); + + switch (stage) + { + case 1: + overlay->color = SKINCOLOR_KETCHUP; + overlay->fuse = 16; + break; + + case 2: + overlay->color = SKINCOLOR_SAPPHIRE; + overlay->fuse = 32; + + S_StartSound(player->mo, sfx_kc5b); + break; + + case 3: + overlay->color = SKINCOLOR_SILVER; + overlay->fuse = 120; + + S_StartSound(player->mo, sfx_kc5b); + S_StartSound(player->mo, sfx_s3kc4l); + break; + } +} + static void K_KartDrift(player_t *player, boolean onground) { fixed_t minspeed = (10 * player->mo->scale); @@ -6389,10 +6427,6 @@ 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); - mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); - P_SetTarget(&overlay->target, player->mo); - P_SetScale(overlay, (overlay->destscale = player->mo->scale)); - K_FlipFromObject(overlay, player->mo); S_StartSound(player->mo, sfx_s23c); //K_SpawnDashDustRelease(player); @@ -6405,9 +6439,6 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 15) player->kartstuff[k_driftboost] = 15; - - overlay->color = SKINCOLOR_GOLD; - overlay->fuse = 10; } else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) { @@ -6418,8 +6449,7 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 20) player->kartstuff[k_driftboost] = 20; - overlay->color = SKINCOLOR_KETCHUP; - overlay->fuse = 16; + K_SpawnDriftBoostExplosion(player, 1); } else if (player->kartstuff[k_driftcharge] < dsthree) { @@ -6430,10 +6460,7 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 50) player->kartstuff[k_driftboost] = 50; - overlay->color = SKINCOLOR_SAPPHIRE; - overlay->fuse = 32; - - S_StartSound(player->mo, sfx_kc5b); + K_SpawnDriftBoostExplosion(player, 2); } else if (player->kartstuff[k_driftcharge] >= dsthree) { @@ -6444,8 +6471,7 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->kartstuff[k_driftboost] < 125) player->kartstuff[k_driftboost] = 125; - overlay->color = SKINCOLOR_SILVER; - overlay->fuse = 120; + K_SpawnDriftBoostExplosion(player, 3); } } From 643a63e1e65937374b30a992fae2f6e0d1fe845b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 13:39:05 -0400 Subject: [PATCH 140/211] Finish merge --- src/k_battle.c | 2 +- src/p_mobj.h | 41 +++++++++++++++++++++-------------------- src/p_setup.c | 4 +--- src/p_user.c | 16 +++++++--------- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/k_battle.c b/src/k_battle.c index fc1141581..aac0dc8e5 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -394,7 +394,7 @@ static void K_SpawnOvertimeParticles(fixed_t x, fixed_t y, fixed_t scale, mobjty //mo->destscale = mo->scale/4; mo->frame += ((leveltime/4) % 8); /*if (battleovertime.enabled < 10*TICRATE) - mo->flags2 |= MF2_SHADOW;*/ + 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; diff --git a/src/p_mobj.h b/src/p_mobj.h index a7bfbcf72..dd4d1706e 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -251,29 +251,30 @@ typedef enum typedef enum { // Don't generate a vissprite for individual screens - MFD_DONTDRAWP1 = 0x0001, - MFD_DONTDRAWP2 = 0x0002, - MFD_DONTDRAWP3 = 0x0004, - MFD_DONTDRAWP4 = 0x0008, + MFD_DONTDRAWP1 = 0x0001, + MFD_DONTDRAWP2 = 0x0002, + MFD_DONTDRAWP3 = 0x0004, + MFD_DONTDRAWP4 = 0x0008, // Transparency override flags - MFD_TRANS10 = 0x0010, - MFD_TRANS20 = 0x0020, - MFD_TRANS30 = 0x0030, - MFD_TRANS40 = 0x0040, - MFD_TRANS50 = 0x0050, - MFD_TRANS60 = 0x0060, - MFD_TRANS70 = 0x0070, - MFD_TRANS80 = 0x0080, - MFD_TRANS90 = 0x0090, - MFD_TRANSMASK = 0x00F0, + MFD_TRANS10 = 0x0010, + MFD_TRANS20 = 0x0020, + MFD_TRANS30 = 0x0030, + MFD_TRANS40 = 0x0040, + MFD_TRANS50 = 0x0050, + MFD_TRANS60 = 0x0060, + MFD_TRANS70 = 0x0070, + MFD_TRANS80 = 0x0080, + MFD_TRANS90 = 0x0090, + MFD_TRANSMASK = 0x00F0, // Brightness override flags - MFD_FULLBRIGHT = 0x0100, - MFD_SEMIBRIGHT = 0x0200, - MFD_NOBRIGHT = 0x0300, - MFD_BRIGHTMASK = 0x0F00, + MFD_FULLBRIGHT = 0x0100, + MFD_SEMIBRIGHT = 0x0200, + MFD_NOBRIGHT = 0x0300, + MFD_BRIGHTMASK = 0x0F00, // Shortcuts - MFD_DONTDRAW = MFD_DONTDRAWP1|MFD_DONTDRAWP2|MFD_DONTDRAWP3|MFD_DONTDRAWP4, - MFD_SHADOW = MFD_TRANS80|MFD_FULLBRIGHT, + MFD_DONTDRAW = MFD_DONTDRAWP1|MFD_DONTDRAWP2|MFD_DONTDRAWP3|MFD_DONTDRAWP4, + MFD_SHADOW = MFD_TRANS80|MFD_FULLBRIGHT, + MFD_TRANSSHIFT = 4, // free: to and including 0x8000 } mobjdflag_t; diff --git a/src/p_setup.c b/src/p_setup.c index 7d2f8ab0f..0f9e98702 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1159,11 +1159,9 @@ static inline void P_SpawnEmblems(void) emblemmobj->flags |= MF_NOCLIP; emblemmobj->flags &= ~MF_SPECIAL; emblemmobj->flags |= MF_NOBLOCKMAP; - emblemmobj->frame |= (tr_trans50<drawflags |= (tr_trans50 << MFD_TRANSSHIFT); P_SetThingPosition(emblemmobj); } - else - emblemmobj->frame &= ~FF_TRANSMASK; } } diff --git a/src/p_user.c b/src/p_user.c index 5d2bd92ec..f6674644e 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1659,8 +1659,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->sprite = mobj->sprite; ghost->frame = mobj->frame; ghost->tics = -1; - ghost->frame &= ~FF_TRANSMASK; - ghost->frame |= tr_trans50<drawflags |= tr_trans50 << MFD_TRANSSHIFT; ghost->fuse = ghost->info->damage; ghost->skin = mobj->skin; ghost->standingslope = mobj->standingslope; @@ -7467,7 +7466,7 @@ void P_DemoCameraMovement(camera_t *cam) awayviewmobj_hack = P_SpawnMobj(cam->x, cam->y, cam->z, MT_THOK); awayviewmobj_hack->tics = 2; - awayviewmobj_hack->flags2 |= MF2_DONTDRAW; + awayviewmobj_hack->drawflags |= MFD_DONTDRAW; democam.soundmobj = awayviewmobj_hack; @@ -8581,13 +8580,13 @@ static void P_HandleFollower(player_t *player) P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale)); K_GenericExtraFlagsNoZAdjust(player->follower, player->mo); // Not K_MatchGenericExtraFlag because the Z adjust it has only works properly if master & mo have the same Z height. - // For comeback in battle. - player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); + // Match how the player is being drawn + player->follower->drawflags = player->mo->drawflags; // Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously. // Also make the follower invisible if we choose not to have it displayed because it isn't ours. (also quick hacky check for f12) if (player->pflags & PF_TIMEOVER || (!cv_showfollowers.value && (!P_IsDisplayPlayer(player) || displayplayers[0] != consoleplayer) )) - player->follower->flags2 |= MF2_DONTDRAW; + 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); @@ -8608,7 +8607,7 @@ static void P_HandleFollower(player_t *player) P_SetScale(bmobj, FixedMul(bubble, player->mo->scale)); K_GenericExtraFlagsNoZAdjust(bmobj, player->follower); - bmobj->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW); + bmobj->drawflags = player->mo->drawflags; if (player->follower->threshold) // threshold means the follower was "despawned" with S_NULL (is actually just set to S_INVISIBLE) P_SetMobjState(bmobj, S_INVISIBLE); // sooooo... let's do the same! @@ -9016,8 +9015,7 @@ void P_PlayerThink(player_t *player) gmobj->fuse = 2; if (leveltime & 1) { - gmobj->frame &= ~FF_TRANSMASK; - gmobj->frame |= tr_trans70<drawflags |= tr_trans70 << MFD_TRANSSHIFT; } // Hide the mobj from our sights if we're the displayplayer and chasecam is off. From 3190edd666639215f9ed9acba68d7052f0f927d5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 16:02:46 -0400 Subject: [PATCH 141/211] Show nametags in replays, fix in splitscreen --- src/k_kart.c | 59 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 98b81fcad..f2524e1f5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9863,7 +9863,8 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a { *hud_y /= 2; - if (camnum > 1) + if ((r_splitscreen == 1 && camnum == 1) + || (r_splitscreen > 1 && camnum > 1)) { *hud_y += shhalffixed; } @@ -9977,6 +9978,30 @@ static void K_drawKartPlayerCheck(void) } } +static boolean K_ShowPlayerNametag(player_t *p) +{ + if (demo.playback == true && demo.freecam == true) + { + return true; + } + + if (stplyr == p) + { + return false; + } + + if (G_RaceGametype()) + { + if ((p->kartstuff[k_position] < stplyr->kartstuff[k_position]-2) + || (p->kartstuff[k_position] > stplyr->kartstuff[k_position]+2)) + { + return false; + } + } + + return true; +} + static void K_drawKartNameTags(void) { const fixed_t maxdistance = 8192*mapobjectscale; @@ -10022,7 +10047,6 @@ static void K_drawKartNameTags(void) fixed_t y = -BASEVIDWIDTH * FRACUNIT; vertex_t v; - UINT8 j; if (!playeringame[i] || ntplayer->spectator) { @@ -10042,18 +10066,24 @@ static void K_drawKartNameTags(void) continue; } - for (j = 0; j <= r_splitscreen; j++) + if (!(demo.playback == true && demo.freecam == true)) { - if (ntplayer == &players[displayplayers[j]]) - { - break; - } - } + UINT8 j; - if (j <= r_splitscreen) - { - // Is a player that's being shown on this computer - continue; + for (j = 0; j <= r_splitscreen; j++) + { + if (ntplayer == &players[displayplayers[j]]) + { + break; + } + } + + if (j <= r_splitscreen) + { + // This is a player that's being shown on this computer + // (Remove whenever we get splitscreen ABCD indicators) + continue; + } } v.x = ntplayer->mo->x; @@ -10089,10 +10119,9 @@ static void K_drawKartNameTags(void) V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); } } - else if (netgame) + else if (netgame || demo.playback) { - if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-2) - && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+2)) + if (K_ShowPlayerNametag(ntplayer) == true) { INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); From 5e4be346d4ea4cf45fd14b7a5bd37e0c7fd3a8ac Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 16:16:56 -0400 Subject: [PATCH 142/211] Ring sting debt indicator properly supports splitscreen --- src/k_kart.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index b37238e52..9737fa3a1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5467,8 +5467,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) debtflag->frame += 4; debtflag->color = player->skincolor; debtflag->fuse = 2; - if (P_IsDisplayPlayer(player)) - debtflag->drawflags |= MFD_DONTDRAW; + debtflag->drawflags = K_GetPlayerDontDrawFlag(player); } if (player->kartstuff[k_springstars] && (leveltime & 1)) From d649be455474ab7f7c5e29a4f37be1d03e623f7a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 16:57:25 -0400 Subject: [PATCH 143/211] revert back to master yay i get to MANUALLY redo everything. --- src/k_kart.c | 109 ++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9737fa3a1..fa5be8c53 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -182,24 +182,6 @@ void K_RegisterKartStuff(void) //} -// Not sure what's a good place for this function -// I'll get around to splitting *everything* in this file into better places soon... -UINT16 K_GetPlayerDontDrawFlag(player_t *player) -{ - UINT16 flag = 0; - - if (player == &players[displayplayers[0]]) - flag = MFD_DONTDRAWP1; - else if (r_splitscreen >= 1 && player == &players[displayplayers[1]]) - flag = MFD_DONTDRAWP2; - else if (r_splitscreen >= 2 && player == &players[displayplayers[2]]) - flag = MFD_DONTDRAWP3; - else if (r_splitscreen >= 3 && player == &players[displayplayers[3]]) - flag = MFD_DONTDRAWP4; - - return flag; -} - boolean K_IsPlayerLosing(player_t *player) { INT32 winningpos = 1; @@ -1354,9 +1336,9 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur band->colorized = true; band->fuse = 2; if (transparent) - band->drawflags |= MFD_SHADOW; - - band->drawflags |= MFD_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim)); + band->flags2 |= MF2_SHADOW; + if (!P_IsDisplayPlayer(player) && !P_IsDisplayPlayer(victim)) + band->flags2 |= MF2_DONTDRAW; } curx += stepx; @@ -1584,7 +1566,11 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) K_FlipFromObject(mo, master); // visibility (usually for hyudoro) - mo->drawflags = (master->drawflags & MFD_DONTDRAW); + mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); } // same as above, but does not adjust Z height when flipping @@ -1595,7 +1581,11 @@ void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); // visibility (usually for hyudoro) - mo->drawflags = (master->drawflags & MFD_DONTDRAW); + mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); + mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); } @@ -1655,7 +1645,7 @@ static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the m P_SetTarget(&sparks->target, player->mo); P_SetScale(sparks, (sparks->destscale = player->mo->scale)); K_MatchGenericExtraFlags(sparks, player->mo); - sparks->drawflags |= MFD_DONTDRAW; + sparks->flags2 |= MF2_DONTDRAW; } static fixed_t K_RandomFlip(fixed_t f) @@ -3405,7 +3395,7 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) } if (translucent) - dust->drawflags |= MFD_SHADOW; + dust->flags2 |= MF2_SHADOW; } void K_SpawnDraftDust(mobj_t *mo) @@ -4881,9 +4871,9 @@ static void K_MoveHeldObjects(player_t *player) cur->flags &= ~MF_NOCLIPTHING; if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1)) - cur->drawflags |= MFD_DONTDRAW; + cur->flags2 |= MF2_DONTDRAW; else - cur->drawflags &= ~MFD_DONTDRAW; + cur->flags2 &= ~MF2_DONTDRAW; if (num & 1) P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L)); @@ -5432,7 +5422,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) //ghost->momy = (3*player->mo->momy)/4; //ghost->momz = (3*player->mo->momz)/4; if (leveltime & 1) - ghost->drawflags |= MFD_DONTDRAW; + ghost->flags2 |= MF2_DONTDRAW; } if (P_IsObjectOnGround(player->mo)) @@ -5467,7 +5457,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) debtflag->frame += 4; debtflag->color = player->skincolor; debtflag->fuse = 2; - debtflag->drawflags = K_GetPlayerDontDrawFlag(player); + if (P_IsDisplayPlayer(player)) + debtflag->flags2 |= MF2_DONTDRAW; } if (player->kartstuff[k_springstars] && (leveltime & 1)) @@ -7435,35 +7426,61 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (G_RaceGametype()) hyu *= 2; // double in race - if (leveltime & 1) + if (r_splitscreen) { - player->mo->drawflags |= MFD_DONTDRAW; + if (leveltime & 1) + player->mo->flags2 |= MF2_DONTDRAW; + else + player->mo->flags2 &= ~MF2_DONTDRAW; + + if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) + { + if (player == &players[displayplayers[1]]) + player->mo->eflags |= MFE_DRAWONLYFORP2; + else if (player == &players[displayplayers[2]] && r_splitscreen > 1) + player->mo->eflags |= MFE_DRAWONLYFORP3; + else if (player == &players[displayplayers[3]] && r_splitscreen > 2) + player->mo->eflags |= MFE_DRAWONLYFORP4; + else if (player == &players[displayplayers[0]]) + player->mo->eflags |= MFE_DRAWONLYFORP1; + else + player->mo->flags2 |= MF2_DONTDRAW; + } + else + player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); } else { - if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) - player->mo->drawflags &= ~K_GetPlayerDontDrawFlag(player); + if (P_IsDisplayPlayer(player) + || (!P_IsDisplayPlayer(player) && (player->kartstuff[k_hyudorotimer] < (TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)))) + { + if (leveltime & 1) + player->mo->flags2 |= MF2_DONTDRAW; + else + player->mo->flags2 &= ~MF2_DONTDRAW; + } else - player->mo->drawflags &= ~MFD_DONTDRAW; + player->mo->flags2 |= MF2_DONTDRAW; } player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints } - else + else if (player->kartstuff[k_hyudorotimer] == 0) { - player->mo->drawflags &= ~MFD_DONTDRAW; + player->mo->flags2 &= ~MF2_DONTDRAW; + player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); } if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) // dead in match? you da bomb { K_DropItems(player); //K_StripItems(player); K_StripOther(player); - player->mo->drawflags |= MFD_SHADOW; + player->mo->flags2 |= MF2_SHADOW; player->powers[pw_flashing] = player->kartstuff[k_comebacktimer]; } else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0) { - player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); + player->mo->flags2 &= ~MF2_SHADOW; } } @@ -10203,7 +10220,6 @@ static void K_drawKartMinimap(void) UINT8 *colormap = NULL; SINT8 localplayers[4]; SINT8 numlocalplayers = 0; - INT32 hyu = hyudorotime; mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!) // Draw the HUD only when playing in a level. @@ -10272,9 +10288,6 @@ static void K_drawKartMinimap(void) } } - if (G_RaceGametype()) - hyu *= 2; // double in race - // initialize for (i = 0; i < 4; i++) localplayers[i] = -1; @@ -10324,8 +10337,8 @@ static void K_drawKartMinimap(void) if (players[i].kartstuff[k_hyudorotimer] > 0) { - if (!((players[i].kartstuff[k_hyudorotimer] < TICRATE/2 - || players[i].kartstuff[k_hyudorotimer] > hyu-(1*TICRATE/2)) + if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2 + || players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)) && !(leveltime & 1))) continue; } @@ -10607,7 +10620,7 @@ static void K_drawKartFirstPerson(void) UINT8 *colmap = NULL; ticcmd_t *cmd = &stplyr->cmd; - if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) + if (stplyr->spectator || !stplyr->mo || (stplyr->mo->flags2 & MF2_DONTDRAW)) return; if (stplyr == &players[displayplayers[1]] && r_splitscreen) @@ -10629,9 +10642,9 @@ static void K_drawKartFirstPerson(void) { if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) y++; - - if (stplyr->mo->drawflags & MFD_TRANSMASK) - splitflags |= ((stplyr->mo->drawflags & MFD_TRANSMASK) >> MFD_TRANSSHIFT) << FF_TRANSSHIFT; + // the following isn't EXPLICITLY right, it just gets the result we want, but i'm too lazy to look up the right way to do it + if (stplyr->mo->flags2 & MF2_SHADOW) + splitflags |= FF_TRANS80; else if (stplyr->mo->frame & FF_TRANSMASK) splitflags |= (stplyr->mo->frame & FF_TRANSMASK); } From 509f085d46f4dee9c27efcfa56bbd580fb98452d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 17:22:07 -0400 Subject: [PATCH 144/211] This should be a proper diff for k_kart's drawflags stuff --- src/k_kart.c | 114 ++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 60 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index fa5be8c53..7816e4f8f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -40,6 +40,22 @@ // indirectitemcooldown is timer before anyone's allowed another Shrink/SPB // mapreset is set when enough players fill an empty server +UINT16 K_GetPlayerDontDrawFlag(player_t *player) +{ + UINT16 flag = 0; + + if (player == &players[displayplayers[0]]) + flag = MFD_DONTDRAWP1; + else if (r_splitscreen >= 1 && player == &players[displayplayers[1]]) + flag = MFD_DONTDRAWP2; + else if (r_splitscreen >= 2 && player == &players[displayplayers[2]]) + flag = MFD_DONTDRAWP3; + else if (r_splitscreen >= 3 && player == &players[displayplayers[3]]) + flag = MFD_DONTDRAWP4; + + return flag; +} + player_t *K_GetItemBoxPlayer(mobj_t *mobj) { fixed_t closest = INT32_MAX; @@ -1330,15 +1346,19 @@ static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t cur cury + (P_RandomRange(-12,12)*mapobjectscale), curz + (P_RandomRange(24,48)*mapobjectscale), MT_SIGNSPARKLE); + P_SetMobjState(band, S_SIGNSPARK1 + (leveltime % 11)); P_SetScale(band, (band->destscale = (3*player->mo->scale)/2)); + band->color = colors[c]; band->colorized = true; + band->fuse = 2; + if (transparent) - band->flags2 |= MF2_SHADOW; - if (!P_IsDisplayPlayer(player) && !P_IsDisplayPlayer(victim)) - band->flags2 |= MF2_DONTDRAW; + band->drawflags |= MFD_SHADOW; + + band->drawflags |= MFD_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim)); } curx += stepx; @@ -1566,11 +1586,7 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) K_FlipFromObject(mo, master); // visibility (usually for hyudoro) - mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); + mo->drawflags = (master->drawflags & MFD_DONTDRAW); } // same as above, but does not adjust Z height when flipping @@ -1581,11 +1597,7 @@ void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); // visibility (usually for hyudoro) - mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); + mo->drawflags = (master->drawflags & MFD_DONTDRAW); } @@ -1645,7 +1657,7 @@ static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the m P_SetTarget(&sparks->target, player->mo); P_SetScale(sparks, (sparks->destscale = player->mo->scale)); K_MatchGenericExtraFlags(sparks, player->mo); - sparks->flags2 |= MF2_DONTDRAW; + sparks->drawflags |= MFD_DONTDRAW; } static fixed_t K_RandomFlip(fixed_t f) @@ -3395,7 +3407,7 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) } if (translucent) - dust->flags2 |= MF2_SHADOW; + dust->drawflags |= MFD_SHADOW; } void K_SpawnDraftDust(mobj_t *mo) @@ -4871,9 +4883,9 @@ static void K_MoveHeldObjects(player_t *player) cur->flags &= ~MF_NOCLIPTHING; if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1)) - cur->flags2 |= MF2_DONTDRAW; + cur->drawflags |= MFD_DONTDRAW; else - cur->flags2 &= ~MF2_DONTDRAW; + cur->drawflags &= ~MFD_DONTDRAW; if (num & 1) P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L)); @@ -5422,7 +5434,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) //ghost->momy = (3*player->mo->momy)/4; //ghost->momz = (3*player->mo->momz)/4; if (leveltime & 1) - ghost->flags2 |= MF2_DONTDRAW; + ghost->drawflags |= MFD_DONTDRAW; } if (P_IsObjectOnGround(player->mo)) @@ -5449,16 +5461,20 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, player->mo->z + player->mo->momz + player->mo->height + (24*player->mo->scale), MT_THOK); + P_SetMobjState(debtflag, S_RINGDEBT); P_SetScale(debtflag, (debtflag->destscale = player->mo->scale)); + K_MatchGenericExtraFlags(debtflag, player->mo); debtflag->frame += (leveltime % 4); + if ((leveltime/12) & 1) debtflag->frame += 4; + debtflag->color = player->skincolor; debtflag->fuse = 2; - if (P_IsDisplayPlayer(player)) - debtflag->flags2 |= MF2_DONTDRAW; + + debtflag->drawflags = K_GetPlayerDontDrawFlag(player); } if (player->kartstuff[k_springstars] && (leveltime & 1)) @@ -7426,61 +7442,35 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (G_RaceGametype()) hyu *= 2; // double in race - if (r_splitscreen) + if (leveltime & 1) { - if (leveltime & 1) - player->mo->flags2 |= MF2_DONTDRAW; - else - player->mo->flags2 &= ~MF2_DONTDRAW; - - if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) - { - if (player == &players[displayplayers[1]]) - player->mo->eflags |= MFE_DRAWONLYFORP2; - else if (player == &players[displayplayers[2]] && r_splitscreen > 1) - player->mo->eflags |= MFE_DRAWONLYFORP3; - else if (player == &players[displayplayers[3]] && r_splitscreen > 2) - player->mo->eflags |= MFE_DRAWONLYFORP4; - else if (player == &players[displayplayers[0]]) - player->mo->eflags |= MFE_DRAWONLYFORP1; - else - player->mo->flags2 |= MF2_DONTDRAW; - } - else - player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); + player->mo->drawflags |= MFD_DONTDRAW; } else { - if (P_IsDisplayPlayer(player) - || (!P_IsDisplayPlayer(player) && (player->kartstuff[k_hyudorotimer] < (TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)))) - { - if (leveltime & 1) - player->mo->flags2 |= MF2_DONTDRAW; - else - player->mo->flags2 &= ~MF2_DONTDRAW; - } + if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) + player->mo->drawflags &= ~K_GetPlayerDontDrawFlag(player); else - player->mo->flags2 |= MF2_DONTDRAW; + player->mo->drawflags &= ~MFD_DONTDRAW; } player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints } else if (player->kartstuff[k_hyudorotimer] == 0) { - player->mo->flags2 &= ~MF2_DONTDRAW; - player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); + player->mo->drawflags &= ~MFD_DONTDRAW; } if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) // dead in match? you da bomb { K_DropItems(player); //K_StripItems(player); K_StripOther(player); - player->mo->flags2 |= MF2_SHADOW; + player->mo->drawflags |= MFD_SHADOW; player->powers[pw_flashing] = player->kartstuff[k_comebacktimer]; } else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0) { - player->mo->flags2 &= ~MF2_SHADOW; + player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); } } @@ -10220,6 +10210,7 @@ static void K_drawKartMinimap(void) UINT8 *colormap = NULL; SINT8 localplayers[4]; SINT8 numlocalplayers = 0; + INT32 hyu = hyudorotime; mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!) // Draw the HUD only when playing in a level. @@ -10292,6 +10283,9 @@ static void K_drawKartMinimap(void) for (i = 0; i < 4; i++) localplayers[i] = -1; + if (G_RaceGametype()) + hyu *= 2; // double in race + // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) if (ghosts) { @@ -10337,8 +10331,8 @@ static void K_drawKartMinimap(void) if (players[i].kartstuff[k_hyudorotimer] > 0) { - if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2 - || players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)) + if (!((players[i].kartstuff[k_hyudorotimer] < TICRATE/2 + || players[i].kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)) && !(leveltime & 1))) continue; } @@ -10620,7 +10614,7 @@ static void K_drawKartFirstPerson(void) UINT8 *colmap = NULL; ticcmd_t *cmd = &stplyr->cmd; - if (stplyr->spectator || !stplyr->mo || (stplyr->mo->flags2 & MF2_DONTDRAW)) + if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) return; if (stplyr == &players[displayplayers[1]] && r_splitscreen) @@ -10642,9 +10636,9 @@ static void K_drawKartFirstPerson(void) { if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) y++; - // the following isn't EXPLICITLY right, it just gets the result we want, but i'm too lazy to look up the right way to do it - if (stplyr->mo->flags2 & MF2_SHADOW) - splitflags |= FF_TRANS80; + + if (stplyr->mo->drawflags & MFD_TRANSMASK) + splitflags |= ((stplyr->mo->drawflags & MFD_TRANSMASK) >> MFD_TRANSSHIFT) << FF_TRANSSHIFT; else if (stplyr->mo->frame & FF_TRANSMASK) splitflags |= (stplyr->mo->frame & FF_TRANSMASK); } From ca6ccca5945ae29e5db375227680fd4999a41d59 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 17:33:13 -0400 Subject: [PATCH 145/211] Add drawflags to Lua --- src/dehacked.c | 23 +++++++++++++++++++++++ src/lua_mobjlib.c | 10 +++++++++- src/p_mobj.h | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/dehacked.c b/src/dehacked.c index 9b4082c72..e803094ad 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9432,6 +9432,29 @@ struct { {"TC_ALLWHITE",TC_ALLWHITE}, {"TC_RAINBOW",TC_RAINBOW}, {"TC_BLINK",TC_BLINK}, + + // MFD_ draw flag enum + {"MFD_DONTDRAWP1",MFD_DONTDRAWP1}, + {"MFD_DONTDRAWP2",MFD_DONTDRAWP2}, + {"MFD_DONTDRAWP3",MFD_DONTDRAWP3}, + {"MFD_DONTDRAWP4",MFD_DONTDRAWP4}, + {"MFD_TRANS10",MFD_TRANS10}, + {"MFD_TRANS20",MFD_TRANS20}, + {"MFD_TRANS30",MFD_TRANS30}, + {"MFD_TRANS40",MFD_TRANS40}, + {"MFD_TRANS50",MFD_TRANS50}, + {"MFD_TRANS60",MFD_TRANS60}, + {"MFD_TRANS70",MFD_TRANS70}, + {"MFD_TRANS80",MFD_TRANS80}, + {"MFD_TRANS90",MFD_TRANS90}, + {"MFD_TRANSMASK",MFD_TRANSMASK}, + {"MFD_FULLBRIGHT",MFD_FULLBRIGHT}, + {"MFD_SEMIBRIGHT",MFD_SEMIBRIGHT}, + {"MFD_NOBRIGHT",MFD_NOBRIGHT}, + {"MFD_BRIGHTMASK",MFD_BRIGHTMASK}, + {"MFD_DONTDRAW",MFD_DONTDRAW}, + {"MFD_SHADOW",MFD_SHADOW}, + {"MFD_TRANSSHIFT",MFD_TRANSSHIFT}, #endif {NULL,0} diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index cd5f4d267..3c85c358b 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -51,6 +51,7 @@ enum mobj_e { mobj_flags, mobj_flags2, mobj_eflags, + mobj_drawflags, mobj_skin, mobj_color, mobj_bnext, @@ -119,6 +120,7 @@ static const char *const mobj_opt[] = { "flags", "flags2", "eflags", + "drawflags", "skin", "color", "bnext", @@ -255,6 +257,9 @@ static int mobj_get(lua_State *L) case mobj_eflags: lua_pushinteger(L, mo->eflags); break; + case mobj_drawflags: + lua_pushinteger(L, mo->drawflags); + break; case mobj_skin: // skin name or nil, not struct if (!mo->skin) return 0; @@ -541,7 +546,10 @@ static int mobj_set(lua_State *L) mo->flags2 = (UINT32)luaL_checkinteger(L, 3); break; case mobj_eflags: - mo->eflags = (UINT32)luaL_checkinteger(L, 3); + mo->eflags = (UINT16)luaL_checkinteger(L, 3); + break; + case mobj_drawflags: + mo->drawflags = (UINT16)luaL_checkinteger(L, 3); break; case mobj_skin: // set skin by name { diff --git a/src/p_mobj.h b/src/p_mobj.h index dd4d1706e..dbb3f4d58 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -333,7 +333,7 @@ typedef struct mobj_s UINT32 flags; // flags from mobjinfo tables UINT32 flags2; // MF2_ flags UINT16 eflags; // extra flags - UINT16 drawflags; // Rendering-related flags. These are not synched. + UINT16 drawflags; // Rendering-related flags. These should not be used for game logic. void *skin; // overrides 'sprite' when non-NULL (for player bodies to 'remember' the skin) // Player and mobj sprites in multiplayer modes are modified From c7f19a4b83161817b4f1a1af63d72c3bef6df576 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 17:49:56 -0400 Subject: [PATCH 146/211] Lazy synch drawflags on join --- src/p_saveg.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index 16863ae33..5239837b4 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -999,6 +999,7 @@ typedef enum MD2_SLOPE = 1<<13, #endif MD2_SHADOWSCALE = 1<<14, + MD2_DRAWFLAGS = 1<<15, } mobj_diff2_t; typedef enum @@ -1197,6 +1198,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) #endif if (mobj->shadowscale) diff2 |= MD2_SHADOWSCALE; + if (mobj->drawflags) + diff2 |= MD2_DRAWFLAGS; if (mobj->colorized) diff2 |= MD2_COLORIZED; if (mobj == waypointcap) @@ -1328,6 +1331,17 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT8(save_p, mobj->colorized); if (diff2 & MD2_SHADOWSCALE) WRITEFIXED(save_p, mobj->shadowscale); + if (diff2 & MD2_DRAWFLAGS) + { + UINT16 df = mobj->drawflags; + + if ((mobj->drawflags & MFD_DONTDRAW) != MFD_DONTDRAW) + { + df = (mobj->drawflags & ~MFD_DONTDRAW); + } + + WRITEUINT16(save_p, df); + } WRITEUINT32(save_p, mobj->mobjnum); } From 1c51b93d0e0c55944ceb22f3007f371ff78e5e87 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 21 Jul 2020 17:05:35 -0700 Subject: [PATCH 147/211] Revert "Merge branch 'nametags-in-replays' into 'master'" This reverts commit 43e7e9ee7e623142f8c56cd647969b5f851d9b93, reversing changes made to 315e808634d7e75c1381d35643bfe97938158c89. --- src/k_kart.c | 22745 ++++++++++++++++++++++++------------------------- 1 file changed, 11355 insertions(+), 11390 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f2524e1f5..7816e4f8f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1,11390 +1,11355 @@ -// SONIC ROBO BLAST 2 KART ~ ZarroTsu -//----------------------------------------------------------------------------- -/// \file k_kart.c -/// \brief SRB2kart general. -/// All of the SRB2kart-unique stuff. - -#include "k_kart.h" -#include "k_battle.h" -#include "k_pwrlv.h" -#include "k_color.h" -#include "k_respawn.h" -#include "doomdef.h" -#include "hu_stuff.h" -#include "g_game.h" -#include "m_random.h" -#include "p_local.h" -#include "p_slopes.h" -#include "p_setup.h" -#include "r_draw.h" -#include "r_local.h" -#include "s_sound.h" -#include "st_stuff.h" -#include "v_video.h" -#include "z_zone.h" -#include "m_misc.h" -#include "m_cond.h" -#include "f_finale.h" -#include "lua_hud.h" // For Lua hud checks -#include "lua_hook.h" // For MobjDamage and ShouldDamage - -#include "k_waypoint.h" -#include "k_bot.h" - -// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: -// gamespeed is cc (0 for easy, 1 for normal, 2 for hard) -// franticitems is Frantic Mode items, bool -// encoremode is Encore Mode (duh), bool -// comeback is Battle Mode's karma comeback, also bool -// battlewanted is an array of the WANTED player nums, -1 for no player in that slot -// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB -// mapreset is set when enough players fill an empty server - -player_t *K_GetItemBoxPlayer(mobj_t *mobj) -{ - fixed_t closest = INT32_MAX; - player_t *player = NULL; - UINT8 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo) && !players[i].spectator)) - { - continue; - } - - // Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this - if (P_CanPickupItem(&players[i], 1)) - { - fixed_t dist = P_AproxDistance(P_AproxDistance( - players[i].mo->x - mobj->x, - players[i].mo->y - mobj->y), - players[i].mo->z - mobj->z - ); - - if (dist > 8192*mobj->scale) - { - continue; - } - - if (dist < closest) - { - player = &players[i]; - closest = dist; - } - } - } - - return player; -} - -// Angle reflection used by springs & speed pads -angle_t K_ReflectAngle(angle_t yourangle, angle_t theirangle, fixed_t yourspeed, fixed_t theirspeed) -{ - INT32 angoffset; - boolean subtract = false; - - angoffset = yourangle - theirangle; - - if ((angle_t)angoffset > ANGLE_180) - { - // Flip on wrong side - angoffset = InvAngle((angle_t)angoffset); - subtract = !subtract; - } - - // Fix going directly against the spring's angle sending you the wrong way - if ((angle_t)angoffset > ANGLE_90) - { - angoffset = ANGLE_180 - angoffset; - } - - // Offset is reduced to cap it (90 / 2 = max of 45 degrees) - angoffset /= 2; - - // Reduce further based on how slow your speed is compared to the spring's speed - // (set both to 0 to ignore this) - if (theirspeed != 0 && yourspeed != 0) - { - if (theirspeed > yourspeed) - { - angoffset = FixedDiv(angoffset, FixedDiv(theirspeed, yourspeed)); - } - } - - if (subtract) - angoffset = (signed)(theirangle) - angoffset; - else - angoffset = (signed)(theirangle) + angoffset; - - return (angle_t)angoffset; -} - -//{ SRB2kart Net Variables - -void K_RegisterKartStuff(void) -{ - CV_RegisterVar(&cv_sneaker); - CV_RegisterVar(&cv_rocketsneaker); - CV_RegisterVar(&cv_invincibility); - CV_RegisterVar(&cv_banana); - CV_RegisterVar(&cv_eggmanmonitor); - CV_RegisterVar(&cv_orbinaut); - CV_RegisterVar(&cv_jawz); - CV_RegisterVar(&cv_mine); - CV_RegisterVar(&cv_ballhog); - CV_RegisterVar(&cv_selfpropelledbomb); - CV_RegisterVar(&cv_grow); - CV_RegisterVar(&cv_shrink); - CV_RegisterVar(&cv_thundershield); - CV_RegisterVar(&cv_bubbleshield); - CV_RegisterVar(&cv_flameshield); - CV_RegisterVar(&cv_hyudoro); - CV_RegisterVar(&cv_pogospring); - CV_RegisterVar(&cv_superring); - CV_RegisterVar(&cv_kitchensink); - - CV_RegisterVar(&cv_triplesneaker); - CV_RegisterVar(&cv_triplebanana); - CV_RegisterVar(&cv_decabanana); - CV_RegisterVar(&cv_tripleorbinaut); - CV_RegisterVar(&cv_quadorbinaut); - CV_RegisterVar(&cv_dualjawz); - - CV_RegisterVar(&cv_kartminimap); - CV_RegisterVar(&cv_kartcheck); - CV_RegisterVar(&cv_kartinvinsfx); - CV_RegisterVar(&cv_kartspeed); - CV_RegisterVar(&cv_kartbumpers); - CV_RegisterVar(&cv_kartfrantic); - CV_RegisterVar(&cv_kartcomeback); - CV_RegisterVar(&cv_kartencore); - CV_RegisterVar(&cv_kartvoterulechanges); - CV_RegisterVar(&cv_kartspeedometer); - CV_RegisterVar(&cv_kartvoices); - CV_RegisterVar(&cv_kartbot); - CV_RegisterVar(&cv_karteliminatelast); - CV_RegisterVar(&cv_kartusepwrlv); - CV_RegisterVar(&cv_votetime); - - CV_RegisterVar(&cv_kartdebugitem); - CV_RegisterVar(&cv_kartdebugamount); - CV_RegisterVar(&cv_kartdebugshrink); - CV_RegisterVar(&cv_kartallowgiveitem); - CV_RegisterVar(&cv_kartdebugdistribution); - CV_RegisterVar(&cv_kartdebughuddrop); - CV_RegisterVar(&cv_kartdebugwaypoints); - - CV_RegisterVar(&cv_kartdebugcheckpoint); - CV_RegisterVar(&cv_kartdebugnodes); - CV_RegisterVar(&cv_kartdebugcolorize); -} - -//} - -boolean K_IsPlayerLosing(player_t *player) -{ - INT32 winningpos = 1; - UINT8 i, pcount = 0; - - if (battlecapsules && player->kartstuff[k_bumper] <= 0) - return true; // DNF in break the capsules - - if (player->kartstuff[k_position] == 1) - return false; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - if (players[i].kartstuff[k_position] > pcount) - pcount = players[i].kartstuff[k_position]; - } - - if (pcount <= 1) - return false; - - winningpos = pcount/2; - if (pcount % 2) // any remainder? - winningpos++; - - return (player->kartstuff[k_position] > winningpos); -} - -fixed_t K_GetKartGameSpeedScalar(SINT8 value) -{ - // Easy = 81.25% - // Normal = 100% - // Hard = 118.75% - // Nightmare = 137.5% ?!?! - return ((13 + (3*value)) << FRACBITS) / 16; -} - -//{ SRB2kart Roulette Code - Position Based - -consvar_t *KartItemCVars[NUMKARTRESULTS-1] = -{ - &cv_sneaker, - &cv_rocketsneaker, - &cv_invincibility, - &cv_banana, - &cv_eggmanmonitor, - &cv_orbinaut, - &cv_jawz, - &cv_mine, - &cv_ballhog, - &cv_selfpropelledbomb, - &cv_grow, - &cv_shrink, - &cv_thundershield, - &cv_bubbleshield, - &cv_flameshield, - &cv_hyudoro, - &cv_pogospring, - &cv_superring, - &cv_kitchensink, - &cv_triplesneaker, - &cv_triplebanana, - &cv_decabanana, - &cv_tripleorbinaut, - &cv_quadorbinaut, - &cv_dualjawz -}; - -#define NUMKARTODDS 80 - -// Less ugly 2D arrays -static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = -{ - //P-Odds 0 1 2 3 4 5 6 7 - /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker - /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker - /*Invincibility*/ { 0, 0, 0, 0, 1, 4, 7, 9 }, // Invincibility - /*Banana*/ { 7, 3, 2, 0, 0, 0, 0, 0 }, // Banana - /*Eggman Monitor*/ { 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor - /*Orbinaut*/ { 7, 4, 3, 2, 0, 0, 0, 0 }, // Orbinaut - /*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz - /*Mine*/ { 0, 2, 2, 1, 0, 0, 0, 0 }, // Mine - /*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog - /*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb - /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink - /*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield - /*Bubble Shield*/ { 0, 2, 3, 3, 1, 0, 0, 0 }, // Bubble Shield - /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield - /*Hyudoro*/ { 0, 0, 0, 1, 2, 0, 0, 0 }, // Hyudoro - /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring - /*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring - /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink - /*Sneaker x3*/ { 0, 0, 0, 2, 6,10, 5, 0 }, // Sneaker x3 - /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 - /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 - /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 - /*Orbinaut x4*/ { 0, 0, 0, 1, 1, 0, 0, 0 }, // Orbinaut x4 - /*Jawz x2*/ { 0, 0, 1, 2, 0, 0, 0, 0 } // Jawz x2 -}; - -static INT32 K_KartItemOddsBattle[NUMKARTRESULTS-1][6] = -{ - //P-Odds 0 1 2 3 4 5 - /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker - /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker - /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility - /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana - /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor - /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut - /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz - /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine - /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog - /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb - /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink - /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield - /*Bubble Shield*/ { 0, 0, 0, 0, 0, 0 }, // Bubble Shield - /*Flame Shield*/ { 0, 0, 0, 0, 0, 0 }, // Flame Shield - /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro - /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring - /*Super Ring*/ { 0, 0, 0, 0, 0, 0 }, // Super Ring - /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink - /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3 - /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3 - /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10 - /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3 - /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4 - /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 -}; - -#define DISTVAR (2048) // Magic number distance for use with item roulette tiers - -INT32 K_GetShieldFromItem(INT32 item) -{ - switch (item) - { - case KITEM_THUNDERSHIELD: return KSHIELD_THUNDER; - case KITEM_BUBBLESHIELD: return KSHIELD_BUBBLE; - case KITEM_FLAMESHIELD: return KSHIELD_FLAME; - default: return KSHIELD_NONE; - } -} - -/** \brief Item Roulette for Kart - - \param player player - \param getitem what item we're looking for - - \return void -*/ -static void K_KartGetItemResult(player_t *player, SINT8 getitem) -{ - if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) // Indirect items - indirectitemcooldown = 20*TICRATE; - - if (getitem == KITEM_HYUDORO) // Hyudoro cooldown - hyubgone = 5*TICRATE; - - player->botvars.itemdelay = TICRATE; - player->botvars.itemconfirm = 0; - - switch (getitem) - { - // Special roulettes first, then the generic ones are handled by default - case KRITEM_TRIPLESNEAKER: // Sneaker x3 - player->kartstuff[k_itemtype] = KITEM_SNEAKER; - player->kartstuff[k_itemamount] = 3; - break; - case KRITEM_TRIPLEBANANA: // Banana x3 - player->kartstuff[k_itemtype] = KITEM_BANANA; - player->kartstuff[k_itemamount] = 3; - break; - case KRITEM_TENFOLDBANANA: // Banana x10 - player->kartstuff[k_itemtype] = KITEM_BANANA; - player->kartstuff[k_itemamount] = 10; - break; - case KRITEM_TRIPLEORBINAUT: // Orbinaut x3 - player->kartstuff[k_itemtype] = KITEM_ORBINAUT; - player->kartstuff[k_itemamount] = 3; - break; - case KRITEM_QUADORBINAUT: // Orbinaut x4 - player->kartstuff[k_itemtype] = KITEM_ORBINAUT; - player->kartstuff[k_itemamount] = 4; - break; - case KRITEM_DUALJAWZ: // Jawz x2 - player->kartstuff[k_itemtype] = KITEM_JAWZ; - player->kartstuff[k_itemamount] = 2; - break; - default: - if (getitem <= 0 || getitem >= NUMKARTRESULTS) // Sad (Fallback) - { - if (getitem != 0) - CONS_Printf("ERROR: P_KartGetItemResult - Item roulette gave bad item (%d) :(\n", getitem); - player->kartstuff[k_itemtype] = KITEM_SAD; - } - else - player->kartstuff[k_itemtype] = getitem; - player->kartstuff[k_itemamount] = 1; - break; - } -} - -/** \brief Item Roulette for Kart - - \param player player object passed from P_KartPlayerThink - - \return void -*/ - -static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) -{ - INT32 newodds; - INT32 i; - UINT8 pingame = 0, pexiting = 0; - SINT8 first = -1, second = -1; - INT32 secondist = 0; - INT32 shieldtype = KSHIELD_NONE; - - I_Assert(item > KITEM_NONE); // too many off by one scenarioes. - I_Assert(KartItemCVars[NUMKARTRESULTS-2] != NULL); // Make sure this exists - - if (!KartItemCVars[item-1]->value && !modeattacking) - return 0; - - if (G_BattleGametype()) - { - I_Assert(pos < 6); // DO NOT allow positions past the bounds of the table - newodds = K_KartItemOddsBattle[item-1][pos]; - } - else - { - I_Assert(pos < 8); // Ditto - newodds = K_KartItemOddsRace[item-1][pos]; - } - - // Base multiplication to ALL item odds to simulate fractional precision - newodds *= 4; - - shieldtype = K_GetShieldFromItem(item); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!G_BattleGametype() || players[i].kartstuff[k_bumper]) - pingame++; - - if (players[i].exiting) - pexiting++; - - if (shieldtype != KSHIELD_NONE && shieldtype == K_GetShieldFromItem(players[i].kartstuff[k_itemtype])) - { - // Don't allow more than one of each shield type at a time - return 0; - } - - if (players[i].mo && G_RaceGametype()) - { - if (players[i].kartstuff[k_position] == 1 && first == -1) - first = i; - if (players[i].kartstuff[k_position] == 2 && second == -1) - second = i; - } - } - - if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB - { - secondist = players[second].distancetofinish - players[first].distancetofinish; - if (franticitems) - secondist = (15 * secondist) / 14; - secondist = ((28 + (8-pingame)) * secondist) / 28; - } - - // POWERITEMODDS handles all of the "frantic item" related functionality, for all of our powerful items. - // First, it multiplies it by 2 if franticitems is true; easy-peasy. - // Next, it multiplies it again if it's in SPB mode and 2nd needs to apply pressure to 1st. - // Then, it multiplies it further if the player count isn't equal to 8. - // This is done to make low player count races more interesting and high player count rates more fair. - // (2P normal would be about halfway between 8P normal and 8P frantic.) - // (This scaling is not done for SPB Rush, so that catchup strength is not weakened.) - // Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, for lesser items needed in a pinch. - -#define PLAYERSCALING (8 - (spbrush ? 2 : pingame)) - -#define POWERITEMODDS(odds) {\ - if (franticitems) \ - odds *= 2; \ - if (rival) \ - odds *= 2; \ - odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \ - if (mashed > 0) \ - odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \ -} - -#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) - - /* - if (bot) - { - // TODO: Item use on bots should all be passed-in functions. - // Instead of manually inserting these, it should return 0 - // for any items without an item use function supplied - - switch (item) - { - case KITEM_SNEAKER: - case KITEM_ROCKETSNEAKER: - case KITEM_INVINCIBILITY: - case KITEM_BANANA: - case KITEM_EGGMAN: - case KITEM_ORBINAUT: - case KITEM_JAWZ: - case KITEM_MINE: - case KITEM_BALLHOG: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_HYUDORO: - case KITEM_SUPERRING: - case KITEM_THUNDERSHIELD: - case KITEM_BUBBLESHIELD: - case KITEM_FLAMESHIELD: - case KRITEM_TRIPLESNEAKER: - case KRITEM_TRIPLEBANANA: - case KRITEM_TENFOLDBANANA: - case KRITEM_TRIPLEORBINAUT: - case KRITEM_QUADORBINAUT: - case KRITEM_DUALJAWZ: - break; - default: - return 0; - } - } - */ - (void)bot; - - switch (item) - { - case KITEM_ROCKETSNEAKER: - case KITEM_JAWZ: - case KITEM_BALLHOG: - case KRITEM_TRIPLESNEAKER: - case KRITEM_TRIPLEBANANA: - case KRITEM_TENFOLDBANANA: - case KRITEM_TRIPLEORBINAUT: - case KRITEM_QUADORBINAUT: - case KRITEM_DUALJAWZ: - POWERITEMODDS(newodds); - break; - case KITEM_INVINCIBILITY: - case KITEM_MINE: - case KITEM_GROW: - case KITEM_BUBBLESHIELD: - case KITEM_FLAMESHIELD: - if (COOLDOWNONSTART) - newodds = 0; - else - POWERITEMODDS(newodds); - break; - case KITEM_SPB: - if ((indirectitemcooldown > 0) || COOLDOWNONSTART - || (first != -1 && players[first].distancetofinish < 8*DISTVAR)) // No SPB near the end of the race - { - newodds = 0; - } - else - { - INT32 multiplier = (secondist - (5*DISTVAR)) / DISTVAR; - - if (multiplier < 0) - multiplier = 0; - if (multiplier > 3) - multiplier = 3; - - newodds *= multiplier; - } - break; - case KITEM_SHRINK: - if ((indirectitemcooldown > 0) || COOLDOWNONSTART || (pingame-1 <= pexiting)) - newodds = 0; - else - POWERITEMODDS(newodds); - break; - case KITEM_THUNDERSHIELD: - if (spbplace != -1 || COOLDOWNONSTART) - newodds = 0; - else - POWERITEMODDS(newodds); - break; - case KITEM_HYUDORO: - if ((hyubgone > 0) || COOLDOWNONSTART) - newodds = 0; - break; - default: - break; - } - -#undef POWERITEMODDS - - return newodds; -} - -//{ SRB2kart Roulette Code - Distance Based, yes waypoints - -static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) -{ - UINT8 i; - UINT8 n = 0; - UINT8 useodds = 0; - UINT8 disttable[14]; - UINT8 totallen = 0; - UINT8 distlen = 0; - boolean oddsvalid[8]; - - for (i = 0; i < 8; i++) - { - UINT8 j; - boolean available = false; - - if (G_BattleGametype() && i > 5) - { - oddsvalid[i] = false; - break; - } - - for (j = 1; j < NUMKARTRESULTS; j++) - { - if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0) - { - available = true; - break; - } - } - - oddsvalid[i] = available; - } - -#define SETUPDISTTABLE(odds, num) \ - if (oddsvalid[odds]) \ - for (i = num; i; --i) \ - disttable[distlen++] = odds; \ - totallen += num; - - if (G_BattleGametype()) // Battle Mode - { - SETUPDISTTABLE(0,1); - SETUPDISTTABLE(1,1); - SETUPDISTTABLE(2,1); - SETUPDISTTABLE(3,1); - SETUPDISTTABLE(4,1); - - if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items - useodds = 5; - else - { - SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc - if (K_IsPlayerWanted(player)) - wantedpos++; - if (wantedpos > 4) // Don't run off into karma items - wantedpos = 4; - if (wantedpos < 0) // Don't go below somehow - wantedpos = 0; - n = (wantedpos * distlen) / totallen; - useodds = disttable[n]; - } - } - else - { - SETUPDISTTABLE(0,1); - SETUPDISTTABLE(1,1); - SETUPDISTTABLE(2,1); - SETUPDISTTABLE(3,2); - SETUPDISTTABLE(4,2); - SETUPDISTTABLE(5,3); - SETUPDISTTABLE(6,3); - SETUPDISTTABLE(7,1); - - if (pdis == 0) - useodds = disttable[0]; - else if (pdis > DISTVAR * ((12 * distlen) / 14)) - useodds = disttable[distlen-1]; - else - { - for (i = 1; i < 13; i++) - { - if (pdis <= DISTVAR * ((i * distlen) / 14)) - { - useodds = disttable[((i * distlen) / 14)]; - break; - } - } - } - } - -#undef SETUPDISTTABLE - - return useodds; -} - -static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) -{ - INT32 i; - UINT8 pingame = 0; - UINT8 roulettestop; - UINT32 pdis = 0; - UINT8 useodds = 0; - INT32 spawnchance[NUMKARTRESULTS]; - INT32 totalspawnchance = 0; - UINT8 bestbumper = 0; - fixed_t mashed = 0; - boolean dontforcespb = false; - boolean spbrush = false; - - // This makes the roulette cycle through items - if this is 0, you shouldn't be here. - if (player->kartstuff[k_itemroulette]) - player->kartstuff[k_itemroulette]++; - else - return; - - // Gotta check how many players are active at this moment. - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - pingame++; - if (players[i].exiting) - dontforcespb = true; - if (players[i].kartstuff[k_bumper] > bestbumper) - bestbumper = players[i].kartstuff[k_bumper]; - } - - // No forced SPB in 1v1s, it has to be randomly rolled - if (pingame <= 2) - dontforcespb = true; - - // This makes the roulette produce the random noises. - if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player) && !demo.freecam) - { -#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8)) - for (i = 0; i <= r_splitscreen; i++) - { - if (player == &players[displayplayers[i]] && players[displayplayers[i]].kartstuff[k_itemroulette]) - PLAYROULETTESND; - } -#undef PLAYROULETTESND - } - - roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position])); - - // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item. - // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think. - // Finally, if you get past this check, now you can actually start calculating what item you get. - if ((cmd->buttons & BT_ATTACK) && (player->kartstuff[k_itemroulette] >= roulettestop) - && !(player->kartstuff[k_eggmanheld] || player->kartstuff[k_itemheld] || player->kartstuff[k_userings])) - { - // Mashing reduces your chances for the good items - mashed = FixedDiv((player->kartstuff[k_itemroulette])*FRACUNIT, ((TICRATE*3)+roulettestop)*FRACUNIT) - FRACUNIT; - } - else if (!(player->kartstuff[k_itemroulette] >= (TICRATE*3))) - return; - - if (cmd->buttons & BT_ATTACK) - player->pflags |= PF_ATTACKDOWN; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].kartstuff[k_position] == 1) - { - // This player is first! Yay! - - if (player->distancetofinish <= players[i].distancetofinish) - { - // Guess you're in first / tied for first? - pdis = 0; - } - else - { - // Subtract 1st's distance from your distance, to get your distance from 1st! - pdis = player->distancetofinish - players[i].distancetofinish; - } - break; - } - } - - if (mapobjectscale != FRACUNIT) - pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT; - - if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items - { - pdis = (15 * pdis) / 14; - } - - if (spbplace != -1 && player->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell - { - pdis = (3 * pdis) / 2; - spbrush = true; - } - - if (player->bot && player->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } - - pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count - - // SPECIAL CASE No. 1: - // Fake Eggman items - if (player->kartstuff[k_roulettetype] == 2) - { - player->kartstuff[k_eggmanexplode] = 4*TICRATE; - //player->karthud[khud_itemblink] = TICRATE; - //player->karthud[khud_itemblinkmode] = 1; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, sfx_itrole); - return; - } - - // SPECIAL CASE No. 2: - // Give a debug item instead if specified - if (cv_kartdebugitem.value != 0 && !modeattacking) - { - K_KartGetItemResult(player, cv_kartdebugitem.value); - player->kartstuff[k_itemamount] = cv_kartdebugamount.value; - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 2; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, sfx_dbgsal); - return; - } - - // SPECIAL CASE No. 3: - // Record Attack / alone mashing behavior - if (modeattacking || pingame == 1) - { - if (G_RaceGametype()) - { - if (mashed && (modeattacking || cv_superring.value)) // ANY mashed value? You get rings. - { - K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblinkmode] = 1; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - } - else - { - if (modeattacking || cv_sneaker.value) // Waited patiently? You get a sneaker! - K_KartGetItemResult(player, KITEM_SNEAKER); - else // Default to sad if nothing's enabled... - K_KartGetItemResult(player, KITEM_SAD); - player->karthud[khud_itemblinkmode] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolf); - } - } - else if (G_BattleGametype()) - { - if (mashed && (modeattacking || cv_banana.value)) // ANY mashed value? You get a banana. - { - K_KartGetItemResult(player, KITEM_BANANA); - player->karthud[khud_itemblinkmode] = 1; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - } - else - { - if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3! - K_KartGetItemResult(player, KRITEM_TRIPLEORBINAUT); - else // Default to sad if nothing's enabled... - K_KartGetItemResult(player, KITEM_SAD); - player->karthud[khud_itemblinkmode] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolf); - } - } - - player->karthud[khud_itemblink] = TICRATE; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - return; - } - - if (G_RaceGametype()) - { - // SPECIAL CASE No. 4: - // Being in ring debt occasionally forces Super Ring on you if you mashed - if (mashed && player->kartstuff[k_rings] < 0 && cv_superring.value) - { - INT32 debtamount = min(20, abs(player->kartstuff[k_rings])); - if (P_RandomChance((debtamount*FRACUNIT)/20)) - { - K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 1; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - return; - } - } - - // SPECIAL CASE No. 5: - // Force SPB onto 2nd if they get too far behind - if (player->kartstuff[k_position] == 2 && pdis > (DISTVAR*8) - && spbplace == -1 && !indirectitemcooldown && !dontforcespb - && cv_selfpropelledbomb.value) - { - K_KartGetItemResult(player, KITEM_SPB); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = (mashed ? 1 : 0); - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, (mashed ? sfx_itrolm : sfx_itrolf)); - return; - } - } - - // NOW that we're done with all of those specialized cases, we can move onto the REAL item roulette tables. - // Initializes existing spawnchance values - for (i = 0; i < NUMKARTRESULTS; i++) - spawnchance[i] = 0; - - // Split into another function for a debug function below - useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); - - for (i = 1; i < NUMKARTRESULTS; i++) - spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival))); - - // Award the player whatever power is rolled - if (totalspawnchance > 0) - { - totalspawnchance = P_RandomKey(totalspawnchance); - for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); - - K_KartGetItemResult(player, i); - } - else - { - player->kartstuff[k_itemtype] = KITEM_SAD; - player->kartstuff[k_itemamount] = 1; - } - - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); - - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = ((player->kartstuff[k_roulettetype] == 1) ? 2 : (mashed ? 1 : 0)); - - player->kartstuff[k_itemroulette] = 0; // Since we're done, clear the roulette number - player->kartstuff[k_roulettetype] = 0; // This too -} - -//} - -//{ SRB2kart p_user.c Stuff - -static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) -{ - fixed_t weight = 5*FRACUNIT; - - if (!mobj->player) - return weight; - - if (against && !P_MobjWasRemoved(against) && against->player - && ((!against->player->kartstuff[k_spinouttimer] && mobj->player->kartstuff[k_spinouttimer]) // You're in spinout - || (against->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD && mobj->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD))) // They have a Bubble Shield - { - weight = 0; // This player does not cause any bump action - } - else - { - weight = (mobj->player->kartweight) * FRACUNIT; - if (mobj->player->speed > K_GetKartSpeed(mobj->player, false)) - weight += (mobj->player->speed - K_GetKartSpeed(mobj->player, false))/8; - if (mobj->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) - weight += 9*FRACUNIT; - } - - return weight; -} - -fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) -{ - fixed_t weight = 5*FRACUNIT; - - switch (mobj->type) - { - case MT_PLAYER: - if (!mobj->player) - break; - weight = K_PlayerWeight(mobj, against); - break; - case MT_BUBBLESHIELD: - weight = K_PlayerWeight(mobj->target, against); - break; - case MT_FALLINGROCK: - if (against->player) - { - if (against->player->kartstuff[k_invincibilitytimer] || against->player->kartstuff[k_growshrinktimer] > 0) - weight = 0; - else - weight = K_PlayerWeight(against, NULL); - } - break; - case MT_ORBINAUT: - case MT_ORBINAUT_SHIELD: - if (against->player) - weight = K_PlayerWeight(against, NULL); - break; - case MT_JAWZ: - case MT_JAWZ_DUD: - case MT_JAWZ_SHIELD: - if (against->player) - weight = K_PlayerWeight(against, NULL) + (3*FRACUNIT); - else - weight += 3*FRACUNIT; - break; - default: - break; - } - - return FixedMul(weight, mobj->scale); -} - -// This kind of wipeout happens with no rings -- doesn't remove a bumper, has no invulnerability, and is much shorter. -static void K_DebtStingPlayer(player_t *player, INT32 length) -{ - if (player->health <= 0) - return; - - if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 - || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - return; - - player->kartstuff[k_ringboost] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - player->kartstuff[k_spinouttype] = 2; - player->kartstuff[k_spinouttimer] = length; - player->kartstuff[k_wipeoutslow] = min(length-1, wipeoutslowtime+1); - - if (player->mo->state != &states[S_KART_SPIN]) - P_SetPlayerMobjState(player->mo, S_KART_SPIN); - - K_DropHnextList(player, false); - return; -} - -void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) -{ - mobj_t *fx; - fixed_t momdifx, momdify; - fixed_t distx, disty; - fixed_t dot, force; - fixed_t mass1, mass2; - - if (!mobj1 || !mobj2) - return; - - // Don't bump when you're being reborn - if ((mobj1->player && mobj1->player->playerstate != PST_LIVE) - || (mobj2->player && mobj2->player->playerstate != PST_LIVE)) - return; - - if ((mobj1->player && mobj1->player->respawn.state != RESPAWNST_NONE) - || (mobj2->player && mobj2->player->respawn.state != RESPAWNST_NONE)) - return; - - { // Don't bump if you're flashing - INT32 flash; - - flash = K_GetKartFlashing(mobj1->player); - if (mobj1->player && mobj1->player->powers[pw_flashing] > 0 && mobj1->player->powers[pw_flashing] < flash) - { - if (mobj1->player->powers[pw_flashing] < flash-1) - mobj1->player->powers[pw_flashing]++; - return; - } - - flash = K_GetKartFlashing(mobj2->player); - if (mobj2->player && mobj2->player->powers[pw_flashing] > 0 && mobj2->player->powers[pw_flashing] < flash) - { - if (mobj2->player->powers[pw_flashing] < flash-1) - mobj2->player->powers[pw_flashing]++; - return; - } - } - - // Don't bump if you've recently bumped - if (mobj1->player && mobj1->player->kartstuff[k_justbumped]) - { - mobj1->player->kartstuff[k_justbumped] = bumptime; - return; - } - - if (mobj2->player && mobj2->player->kartstuff[k_justbumped]) - { - mobj2->player->kartstuff[k_justbumped] = bumptime; - return; - } - - mass1 = K_GetMobjWeight(mobj1, mobj2); - - if (solid == true && mass1 > 0) - mass2 = mass1; - else - mass2 = K_GetMobjWeight(mobj2, mobj1); - - momdifx = mobj1->momx - mobj2->momx; - momdify = mobj1->momy - mobj2->momy; - - // Adds the OTHER player's momentum times a bunch, for the best chance of getting the correct direction - distx = (mobj1->x + mobj2->momx*3) - (mobj2->x + mobj1->momx*3); - 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; - - { // 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); - fixed_t nx = FixedDiv(distx, dist); - fixed_t ny = FixedDiv(disty, dist); - - dist = dist ? dist : 1; - distx = FixedMul(mobj1->radius+mobj2->radius, nx); - disty = FixedMul(mobj1->radius+mobj2->radius, ny); - - if (momdifx == 0 && momdify == 0) - { - // If there's no momentum difference, they're moving at exactly the same rate. Pretend they moved into each other. - momdifx = -nx; - momdify = -ny; - } - } - - // if the speed difference is less than this let's assume they're going proportionately faster from each other - if (P_AproxDistance(momdifx, momdify) < (25*mapobjectscale)) - { - fixed_t momdiflength = P_AproxDistance(momdifx, momdify); - fixed_t normalisedx = FixedDiv(momdifx, momdiflength); - fixed_t normalisedy = FixedDiv(momdify, momdiflength); - momdifx = FixedMul((25*mapobjectscale), normalisedx); - momdify = FixedMul((25*mapobjectscale), normalisedy); - } - - dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty); - - if (dot >= 0) - { - // They're moving away from each other - return; - } - - force = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty)); - - if (bounce == true && mass2 > 0) // Perform a Goomba Bounce. - mobj1->momz = -mobj1->momz; - else - { - fixed_t newz = mobj1->momz; - if (mass2 > 0) - mobj1->momz = mobj2->momz; - if (mass1 > 0 && solid == false) - mobj2->momz = newz; - } - - if (mass2 > 0) - { - mobj1->momx = mobj1->momx - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), distx); - mobj1->momy = mobj1->momy - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), disty); - } - - if (mass1 > 0 && solid == false) - { - mobj2->momx = mobj2->momx - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -distx); - mobj2->momy = mobj2->momy - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -disty); - } - - // Do the bump fx when we've CONFIRMED we can bump. - if ((mobj1->player && mobj1->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) || (mobj2->player && mobj2->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD)) - S_StartSound(mobj1, sfx_s3k44); - else - S_StartSound(mobj1, sfx_s3k49); - - fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP); - if (mobj1->eflags & MFE_VERTICALFLIP) - fx->eflags |= MFE_VERTICALFLIP; - else - fx->eflags &= ~MFE_VERTICALFLIP; - P_SetScale(fx, mobj1->scale); - - // Because this is done during collision now, rmomx and rmomy need to be recalculated - // so that friction doesn't immediately decide to stop the player if they're at a standstill - // Also set justbumped here - if (mobj1->player) - { - mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx; - mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy; - mobj1->player->kartstuff[k_justbumped] = bumptime; - - if (mobj1->player->kartstuff[k_spinouttimer]) - { - mobj1->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; - mobj1->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj1->player->kartstuff[k_spinouttimer]); - //mobj1->player->kartstuff[k_spinouttype] = 1; // Enforce type - } - else if (mobj2->player // Player VS player bumping only - && (K_GetShieldFromItem(mobj1->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields - { - if (mobj1->player->kartstuff[k_rings] <= 0) - { - K_DebtStingPlayer(mobj1->player, TICRATE + (4 * (mobj2->player->kartweight - mobj1->player->kartweight))); - K_KartPainEnergyFling(mobj1->player); - P_PlayRinglossSound(mobj1); - } - P_PlayerRingBurst(mobj1->player, 1); - } - } - - if (mobj2->player) - { - mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx; - mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy; - mobj2->player->kartstuff[k_justbumped] = bumptime; - - if (mobj2->player->kartstuff[k_spinouttimer]) - { - mobj2->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; - mobj2->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj2->player->kartstuff[k_spinouttimer]); - //mobj2->player->kartstuff[k_spinouttype] = 1; // Enforce type - } - else if (mobj1->player // Player VS player bumping only - && (K_GetShieldFromItem(mobj2->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields - { - if (mobj2->player->kartstuff[k_rings] <= 0) - { - K_DebtStingPlayer(mobj2->player, TICRATE + (4 * (mobj1->player->kartweight - mobj2->player->kartweight))); - K_KartPainEnergyFling(mobj2->player); - P_PlayRinglossSound(mobj2); - } - P_PlayerRingBurst(mobj2->player, 1); - } - } -} - -/** \brief Checks that the player is on an offroad subsector for realsies - - \param mo player mobj object - - \return boolean -*/ -static UINT8 K_CheckOffroadCollide(mobj_t *mo) -{ - UINT8 i; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (i = 2; i < 5; i++) - { - if (P_MobjTouchingSectorSpecial(mo, 1, i, true)) - return i-1; - } - - return 0; -} - -/** \brief Updates the Player's offroad value once per frame - - \param player player object passed from K_KartPlayerThink - - \return void -*/ -static void K_UpdateOffroad(player_t *player) -{ - fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); - - // If you are in offroad, a timer starts. - if (offroadstrength) - { - if (player->kartstuff[k_offroad] < offroadstrength) - player->kartstuff[k_offroad] += offroadstrength / TICRATE; - - if (player->kartstuff[k_offroad] > offroadstrength) - player->kartstuff[k_offroad] = offroadstrength; - } - else - player->kartstuff[k_offroad] = 0; -} - -static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent) -{ -#define CHAOTIXBANDLEN 15 -#define CHAOTIXBANDCOLORS 9 - static const UINT8 colors[CHAOTIXBANDCOLORS] = { - SKINCOLOR_SAPPHIRE, - SKINCOLOR_PLATINUM, - SKINCOLOR_TEA, - SKINCOLOR_GARDEN, - SKINCOLOR_BANANA, - SKINCOLOR_GOLD, - SKINCOLOR_ORANGE, - SKINCOLOR_SCARLET, - SKINCOLOR_TAFFY - }; - fixed_t minimumdist = FixedMul(RING_DIST>>1, player->mo->scale); - UINT8 n = CHAOTIXBANDLEN; - UINT8 offset = ((leveltime / 3) % 3); - fixed_t stepx, stepy, stepz; - fixed_t curx, cury, curz; - UINT8 c; - - if (maxdist == 0) - c = 0; - else - c = FixedMul(CHAOTIXBANDCOLORS<> FRACBITS; - - stepx = (victim->mo->x - player->mo->x) / CHAOTIXBANDLEN; - stepy = (victim->mo->y - player->mo->y) / CHAOTIXBANDLEN; - stepz = ((victim->mo->z + (victim->mo->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN; - - curx = player->mo->x + stepx; - cury = player->mo->y + stepy; - curz = player->mo->z + stepz; - - while (n) - { - if (offset == 0) - { - mobj_t *band = P_SpawnMobj(curx + (P_RandomRange(-12,12)*mapobjectscale), - cury + (P_RandomRange(-12,12)*mapobjectscale), - curz + (P_RandomRange(24,48)*mapobjectscale), - MT_SIGNSPARKLE); - P_SetMobjState(band, S_SIGNSPARK1 + (leveltime % 11)); - P_SetScale(band, (band->destscale = (3*player->mo->scale)/2)); - band->color = colors[c]; - band->colorized = true; - band->fuse = 2; - if (transparent) - band->flags2 |= MF2_SHADOW; - if (!P_IsDisplayPlayer(player) && !P_IsDisplayPlayer(victim)) - band->flags2 |= MF2_DONTDRAW; - } - - curx += stepx; - cury += stepy; - curz += stepz; - - offset = abs(offset-1) % 3; - n--; - } -#undef CHAOTIXBANDLEN -} - -/** \brief Updates the player's drafting values once per frame - - \param player player object passed from K_KartPlayerThink - - \return void -*/ -static void K_UpdateDraft(player_t *player) -{ - fixed_t topspd = K_GetKartSpeed(player, false); - fixed_t draftdistance; - UINT8 leniency; - UINT8 i; - - if (player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD) - { - // Flame Shield gets infinite draft distance as its passive effect. - draftdistance = 0; - } - else - { - // Distance you have to be to draft. If you're still accelerating, then this distance is lessened. - // This distance biases toward low weight! (min weight gets 4096 units, max weight gets 3072 units) - // This distance is also scaled based on game speed. - draftdistance = (3072 + (128 * (9 - player->kartweight))) * player->mo->scale; - if (player->speed < topspd) - draftdistance = FixedMul(draftdistance, FixedDiv(player->speed, topspd)); - draftdistance = FixedMul(draftdistance, K_GetKartGameSpeedScalar(gamespeed)); - } - - // On the contrary, the leniency period biases toward high weight. - // (See also: the leniency variable in K_SpawnDraftDust) - leniency = (3*TICRATE)/4 + ((player->kartweight-1) * (TICRATE/4)); - - // Not enough speed to draft. - if (player->speed >= 20*player->mo->scale) - { -//#define EASYDRAFTTEST - // Let's hunt for players to draft off of! - for (i = 0; i < MAXPLAYERS; i++) - { - fixed_t dist, olddraft; -#ifndef EASYDRAFTTEST - angle_t yourangle, theirangle, diff; -#endif - - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - -#ifndef EASYDRAFTTEST - // Don't draft on yourself :V - if (&players[i] == player) - continue; -#endif - - // Not enough speed to draft off of. - if (players[i].speed < 20*players[i].mo->scale) - 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); - - diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle; - if (diff > ANGLE_180) - diff = InvAngle(diff); - - // Not in front of this player. - if (diff > ANG10) - continue; - - diff = yourangle - theirangle; - if (diff > ANGLE_180) - diff = InvAngle(diff); - - // Not moving in the same direction. - if (diff > ANGLE_90) - continue; -#endif - - dist = P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, players[i].mo->y - player->mo->y), players[i].mo->z - player->mo->z); - -#ifndef EASYDRAFTTEST - // TOO close to draft. - if (dist < FixedMul(RING_DIST>>1, player->mo->scale)) - continue; - - // Not close enough to draft. - if (dist > draftdistance && draftdistance > 0) - continue; -#endif - - olddraft = player->kartstuff[k_draftpower]; - - player->kartstuff[k_draftleeway] = leniency; - player->kartstuff[k_lastdraft] = i; - - // Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed. - // How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic) - if (player->kartstuff[k_draftpower] < FRACUNIT) - player->kartstuff[k_draftpower] += (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600)); - - if (player->kartstuff[k_draftpower] > FRACUNIT) - player->kartstuff[k_draftpower] = FRACUNIT; - - // Play draft finish noise - if (olddraft < FRACUNIT && player->kartstuff[k_draftpower] >= FRACUNIT) - S_StartSound(player->mo, sfx_cdfm62); - - // Spawn in the visual! - K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false); - - return; // Finished doing our draft. - } - } - - // No one to draft off of? Then you can knock that off. - if (player->kartstuff[k_draftleeway]) // Prevent small disruptions from stopping your draft. - { - player->kartstuff[k_draftleeway]--; - if (player->kartstuff[k_lastdraft] >= 0 - && player->kartstuff[k_lastdraft] < MAXPLAYERS - && playeringame[player->kartstuff[k_lastdraft]] - && !players[player->kartstuff[k_lastdraft]].spectator - && players[player->kartstuff[k_lastdraft]].mo) - { - player_t *victim = &players[player->kartstuff[k_lastdraft]]; - fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z); - K_DrawDraftCombiring(player, victim, dist, draftdistance, true); - } - } - else // Remove draft speed boost. - { - player->kartstuff[k_draftpower] = 0; - player->kartstuff[k_lastdraft] = -1; - } -} - -void K_KartPainEnergyFling(player_t *player) -{ - static const UINT8 numfling = 5; - INT32 i; - mobj_t *mo; - angle_t fa; - fixed_t ns; - fixed_t z; - - // Better safe than sorry. - if (!player) - return; - - // P_PlayerRingBurst: "There's no ring spilling in kart, so I'm hijacking this for the same thing as TD" - // :oh: - - for (i = 0; i < numfling; i++) - { - INT32 objType = mobjinfo[MT_FLINGENERGY].reactiontime; - fixed_t momxy, momz; // base horizonal/vertical thrusts - - z = player->mo->z; - if (player->mo->eflags & MFE_VERTICALFLIP) - z += player->mo->height - mobjinfo[objType].height; - - mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); - - mo->fuse = 8*TICRATE; - P_SetTarget(&mo->target, player->mo); - - mo->destscale = player->mo->scale; - P_SetScale(mo, player->mo->scale); - - // Angle offset by player angle, then slightly offset by amount of fling - fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT) - ((numfling-1)*FINEANGLES/32)) & FINEMASK; - - if (i > 15) - { - momxy = 3*FRACUNIT; - momz = 4*FRACUNIT; - } - else - { - momxy = 28*FRACUNIT; - momz = 3*FRACUNIT; - } - - ns = FixedMul(momxy, mo->scale); - mo->momx = FixedMul(FINECOSINE(fa),ns); - - ns = momz; - P_SetObjectMomZ(mo, ns, false); - - if (i & 1) - P_SetObjectMomZ(mo, ns, true); - - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->momz *= -1; - } -} - -// Adds gravity flipping to an object relative to its master and shifts the z coordinate accordingly. -void K_FlipFromObject(mobj_t *mo, mobj_t *master) -{ - mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); - mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); - - if (mo->eflags & MFE_VERTICALFLIP) - mo->z += master->height - FixedMul(master->scale, mo->height); -} - -void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) -{ - // flipping - // handle z shifting from there too. This is here since there's no reason not to flip us if needed when we do this anyway; - K_FlipFromObject(mo, master); - - // visibility (usually for hyudoro) - mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); -} - -// same as above, but does not adjust Z height when flipping -void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) -{ - // flipping - mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); - mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); - - // visibility (usually for hyudoro) - mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3); - mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4); -} - - -void K_SpawnDashDustRelease(player_t *player) -{ - fixed_t newx; - fixed_t newy; - mobj_t *dust; - angle_t travelangle; - INT32 i; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (!P_IsObjectOnGround(player->mo)) - return; - - if (!player->speed && !player->kartstuff[k_startboost]) - return; - - travelangle = player->mo->angle; - - if (player->kartstuff[k_drift] || player->kartstuff[k_driftend]) - travelangle -= (ANGLE_45/5)*player->kartstuff[k_drift]; - - for (i = 0; i < 2; i++) - { - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); - dust = P_SpawnMobj(newx, newy, player->mo->z, MT_FASTDUST); - - P_SetTarget(&dust->target, player->mo); - dust->angle = travelangle - ((i&1) ? -1 : 1)*ANGLE_45; - dust->destscale = player->mo->scale; - P_SetScale(dust, player->mo->scale); - - dust->momx = 3*player->mo->momx/5; - dust->momy = 3*player->mo->momy/5; - //dust->momz = 3*player->mo->momz/5; - - K_MatchGenericExtraFlags(dust, player->mo); - } -} - -static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the mobj thinker case too! -{ - mobj_t *sparks; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - // Position & etc are handled in its thinker, and its spawned invisible. - // This avoids needing to dupe code if we don't need it. - sparks = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BRAKEDRIFT); - P_SetTarget(&sparks->target, player->mo); - P_SetScale(sparks, (sparks->destscale = player->mo->scale)); - K_MatchGenericExtraFlags(sparks, player->mo); - sparks->flags2 |= MF2_DONTDRAW; -} - -static fixed_t K_RandomFlip(fixed_t f) -{ - return ( ( leveltime & 1 ) ? f : -f ); -} - -void K_SpawnDriftBoostClip(player_t *player) -{ - mobj_t *clip; - fixed_t scale = 115*FRACUNIT/100; - fixed_t z; - - if (( player->mo->eflags & MFE_VERTICALFLIP )) - z = player->mo->z; - else - z = player->mo->z + player->mo->height; - - clip = P_SpawnMobj(player->mo->x, player->mo->y, z, MT_DRIFTCLIP); - - P_SetTarget(&clip->target, player->mo); - P_SetScale(clip, ( clip->destscale = FixedMul(scale, player->mo->scale) )); - K_MatchGenericExtraFlags(clip, player->mo); - - clip->fuse = 105; - clip->momz = 7 * P_MobjFlip(clip) * clip->scale; - - P_InstaThrust(clip, player->mo->angle + - K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), - FixedMul(scale, player->speed)); -} - -void K_SpawnDriftBoostClipSpark(mobj_t *clip) -{ - mobj_t *spark; - - spark = P_SpawnMobj(clip->x, clip->y, clip->z, MT_DRIFTCLIPSPARK); - - P_SetTarget(&spark->target, clip); - P_SetScale(spark, ( spark->destscale = clip->scale )); - K_MatchGenericExtraFlags(spark, clip); - - spark->momx = clip->momx/2; - spark->momy = clip->momx/2; -} - -/** \brief Handles the state changing for moving players, moved here to eliminate duplicate code - - \param player player data - - \return void -*/ -void K_KartMoveAnimation(player_t *player) -{ - const INT16 minturn = KART_FULLTURN/8; - SINT8 turndir = 0; - - const fixed_t fastspeed = (K_GetKartSpeed(player, false) * 17) / 20; // 85% - const fixed_t speedthreshold = player->mo->scale / 8; - - const boolean onground = P_IsObjectOnGround(player->mo); - - ticcmd_t *cmd = &player->cmd; - const boolean spinningwheels = ((cmd->buttons & BT_ACCELERATE) || (onground && player->speed > 0)); - - if (cmd->driftturn < -minturn) - { - turndir = -1; - } - else if (cmd->driftturn > minturn) - { - turndir = 1; - } - - if (!onground) - { - // Only use certain frames in the air, to make it look like your tires are spinning fruitlessly! - - if (player->kartstuff[k_drift] > 0) - { - if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); - } - } - else if (player->kartstuff[k_drift] > 0) - { - if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); - } - } - else - { - if ((turndir == -1) - && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R]))) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST2_R); - } - else if ((turndir == 1) - && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L]))) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST2_L); - } - else if ((turndir == 0) - && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2]))) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST2); - } - } - } - else - { - if (player->kartstuff[k_drift] > 0) - { - // Drifting LEFT! - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_DRIFT1_L_OUT] && player->mo->state <= &states[S_KART_DRIFT2_L_OUT])) - { - // Right -- outwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_OUT); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_DRIFT1_L_IN] && player->mo->state <= &states[S_KART_DRIFT4_L_IN])) - { - // Left -- inwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_IN); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); - } - } - else if (player->kartstuff[k_drift] < 0) - { - // Drifting RIGHT! - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_DRIFT1_R_IN] && player->mo->state <= &states[S_KART_DRIFT4_R_IN])) - { - // Right -- inwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_IN); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_DRIFT1_R_OUT] && player->mo->state <= &states[S_KART_DRIFT2_R_OUT])) - { - // Left -- outwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_OUT); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); - } - } - else - { - if (player->speed >= fastspeed && player->speed >= (player->lastspeed - speedthreshold)) - { - // Going REAL fast! - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R])) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST1_R); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L])) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST1_L); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2])) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST1); - } - } - else - { - if (spinningwheels) - { - // Drivin' slow. - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_SLOW1_R] && player->mo->state <= &states[S_KART_SLOW2_R])) - { - P_SetPlayerMobjState(player->mo, S_KART_SLOW1_R); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_SLOW1_L] && player->mo->state <= &states[S_KART_SLOW2_L])) - { - P_SetPlayerMobjState(player->mo, S_KART_SLOW1_L); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_SLOW1] && player->mo->state <= &states[S_KART_SLOW2])) - { - P_SetPlayerMobjState(player->mo, S_KART_SLOW1); - } - } - else - { - // Completely still. - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_STILL1_R] && player->mo->state <= &states[S_KART_STILL2_R])) - { - P_SetPlayerMobjState(player->mo, S_KART_STILL1_R); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_STILL1_L] && player->mo->state <= &states[S_KART_STILL2_L])) - { - P_SetPlayerMobjState(player->mo, S_KART_STILL1_L); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_STILL1] && player->mo->state <= &states[S_KART_STILL2])) - { - P_SetPlayerMobjState(player->mo, S_KART_STILL1); - } - } - } - } - } - - // Update lastspeed value -- we use to display slow driving frames instead of fast driving when slowing down. - player->lastspeed = player->speed; -} - -static void K_TauntVoiceTimers(player_t *player) -{ - if (!player) - return; - - player->karthud[khud_tauntvoices] = 6*TICRATE; - player->karthud[khud_voices] = 4*TICRATE; -} - -static void K_RegularVoiceTimers(player_t *player) -{ - if (!player) - return; - - player->karthud[khud_voices] = 4*TICRATE; - - if (player->karthud[khud_tauntvoices] < 4*TICRATE) - player->karthud[khud_tauntvoices] = 4*TICRATE; -} - -void K_PlayAttackTaunt(mobj_t *source) -{ - sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); - - if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) - S_StartSound(source, sfx_kattk1+pick); - - if (!tasteful) - return; - - K_TauntVoiceTimers(source->player); -} - -void K_PlayBoostTaunt(mobj_t *source) -{ - sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); - - if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) - S_StartSound(source, sfx_kbost1+pick); - - if (!tasteful) - return; - - K_TauntVoiceTimers(source->player); -} - -void K_PlayOvertakeSound(mobj_t *source) -{ - boolean tasteful = (!source->player || !source->player->karthud[khud_voices]); - - if (!G_RaceGametype()) // Only in race - return; - - // 4 seconds from before race begins, 10 seconds afterwards - if (leveltime < starttime+(10*TICRATE)) - return; - - if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) - S_StartSound(source, sfx_kslow); - - if (!tasteful) - return; - - K_RegularVoiceTimers(source->player); -} - -void K_PlayPainSound(mobj_t *source) -{ - sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - - if (cv_kartvoices.value) - S_StartSound(source, sfx_khurt1 + pick); - - K_RegularVoiceTimers(source->player); -} - -void K_PlayHitEmSound(mobj_t *source) -{ - - if (source->player->follower) - { - follower_t fl = followers[source->player->followerskin]; - source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. - } - - if (cv_kartvoices.value) - S_StartSound(source, sfx_khitem); - else - S_StartSound(source, sfx_s1c9); // The only lost gameplay functionality with voices disabled - - K_RegularVoiceTimers(source->player); -} - -void K_PlayPowerGloatSound(mobj_t *source) -{ - if (cv_kartvoices.value) - S_StartSound(source, sfx_kgloat); - - K_RegularVoiceTimers(source->player); -} - -void K_MomentumToFacing(player_t *player) -{ - angle_t dangle = player->mo->angle - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - - if (dangle > ANGLE_180) - dangle = InvAngle(dangle); - - // If you aren't on the ground or are moving in too different of a direction don't do this - if (player->mo->eflags & MFE_JUSTHITFLOOR) - ; // Just hit floor ALWAYS redirects - else if (!P_IsObjectOnGround(player->mo) || dangle > ANGLE_90) - return; - - P_Thrust(player->mo, player->mo->angle, player->speed - FixedMul(player->speed, player->mo->friction)); - player->mo->momx = FixedMul(player->mo->momx - player->cmomx, player->mo->friction) + player->cmomx; - player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy; -} - -boolean K_ApplyOffroad(player_t *player) -{ - if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer]) - return false; - return true; -} - -static fixed_t K_FlameShieldDashVar(INT32 val) -{ - // 1 second = 75% + 50% top speed - return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); -} - -// sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be -static void K_GetKartBoostPower(player_t *player) -{ - fixed_t boostpower = FRACUNIT; - fixed_t speedboost = 0, accelboost = 0; - UINT8 numboosts = 0; - - if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow] == 1) // Slow down after you've been bumped - { - player->kartstuff[k_boostpower] = player->kartstuff[k_speedboost] = player->kartstuff[k_accelboost] = 0; - return; - } - - // Offroad is separate, it's difficult to factor it in with a variable value anyway. - if (K_ApplyOffroad(player) && player->kartstuff[k_offroad] >= 0) - boostpower = FixedDiv(boostpower, FixedMul(player->kartstuff[k_offroad], K_GetKartGameSpeedScalar(gamespeed)) + FRACUNIT); - - if (player->kartstuff[k_bananadrag] > TICRATE) - boostpower = (4*boostpower)/5; - -#define ADDBOOST(s,a) { \ - numboosts++; \ - speedboost += (s) / numboosts; \ - accelboost += (a) / numboosts; \ -} - - if (player->kartstuff[k_sneakertimer]) // Sneaker - { - UINT8 i; - for (i = 0; i < player->kartstuff[k_numsneakers]; i++) - { - ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration - } - } - - if (player->kartstuff[k_invincibilitytimer]) // Invincibility - { - ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration - } - - if (player->kartstuff[k_flamedash]) // Flame Shield dash - { - ADDBOOST(K_FlameShieldDashVar(player->kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration - } - - if (player->kartstuff[k_startboost]) // Startup Boost - { - ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration - } - - if (player->kartstuff[k_driftboost]) // Drift Boost - { - ADDBOOST(FRACUNIT/4, 4*FRACUNIT); // + 25% top speed, + 400% acceleration - } - - if (player->kartstuff[k_ringboost]) // Ring Boost - { - ADDBOOST(FRACUNIT/5, 4*FRACUNIT); // + 20% top speed, + 400% acceleration - } - - if (player->kartstuff[k_eggmanexplode]) // Ready-to-explode - { - ADDBOOST(3*FRACUNIT/20, FRACUNIT); // + 15% top speed, + 100% acceleration - } - - if (player->kartstuff[k_draftpower] > 0) // Drafting - { - fixed_t draftspeed = ((3*FRACUNIT)/10) + ((player->kartspeed-1) * (FRACUNIT/50)); // min is 30%, max is 46% - speedboost += FixedMul(draftspeed, player->kartstuff[k_draftpower]); // (Drafting suffers no boost stack penalty.) - numboosts++; - } - - player->kartstuff[k_boostpower] = boostpower; - - // value smoothing - if (speedboost > player->kartstuff[k_speedboost]) - { - player->kartstuff[k_speedboost] = speedboost; - } - else - { - player->kartstuff[k_speedboost] += (speedboost - player->kartstuff[k_speedboost]) / (TICRATE/2); - } - - player->kartstuff[k_accelboost] = accelboost; - player->kartstuff[k_numboosts] = numboosts; -} - -// Returns kart speed from a stat. Boost power and scale are NOT taken into account, no player or object is necessary. -fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) -{ - const fixed_t xspd = (3*FRACUNIT)/64; - fixed_t g_cc = K_GetKartGameSpeedScalar(gamespeed) + xspd; - fixed_t k_speed = 150; - fixed_t finalspeed; - - k_speed += kartspeed*3; // 153 - 177 - - finalspeed = FixedMul(k_speed<<14, g_cc); - return finalspeed; -} - -fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) -{ - fixed_t finalspeed; - UINT8 kartspeed = player->kartspeed; - - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - kartspeed = 1; - - finalspeed = K_GetKartSpeedFromStat(kartspeed); - - 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 - - if (player->botvars.rival == true) - { - speedmul += FRACUNIT/10; // +10% for rival - } - - finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul); - } - - if (player->mo && !P_MobjWasRemoved(player->mo)) - finalspeed = FixedMul(finalspeed, player->mo->scale); - - if (doboostpower) - { - if (K_PlayerUsesBotMovement(player)) - { - finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player)); - } - - return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]); - } - - return finalspeed; -} - -fixed_t K_GetKartAccel(player_t *player) -{ - fixed_t k_accel = 32; // 36; - UINT8 kartspeed = player->kartspeed; - - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - kartspeed = 1; - - //k_accel += 3 * (9 - kartspeed); // 36 - 60 - k_accel += 4 * (9 - kartspeed); // 32 - 64 - - - if (K_PlayerUsesBotMovement(player)) - { - // Rubberbanding acceleration is waekened since it makes hits feel more meaningful - fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; - k_accel = FixedMul(k_accel, FRACUNIT + (rubberband/2)); - } - - return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]); -} - -UINT16 K_GetKartFlashing(player_t *player) -{ - UINT16 tics = flashingtics; - - if (!player) - return tics; - - if (G_BattleGametype()) - tics *= 2; - - tics += (flashingtics/8) * (player->kartspeed); - - return tics; -} - -fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove) -{ - const fixed_t accelmax = 4000; - const fixed_t p_speed = K_GetKartSpeed(player, true); - const fixed_t p_accel = K_GetKartAccel(player); - fixed_t newspeed, oldspeed, finalspeed; - fixed_t orig = ORIG_FRICTION; - - if (!onground) return 0; // If the player isn't on the ground, there is no change in speed - - if (K_PlayerUsesBotMovement(player)) - { - orig = K_BotFrictionRubberband(player, ORIG_FRICTION); - } - - // ACCELCODE!!!1!11! - oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale); - // Don't calculate the acceleration as ever being above top speed - if (oldspeed > p_speed) - oldspeed = p_speed; - newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), orig); - - if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust - { - const fixed_t hscale = mapobjectscale /*+ (mapobjectscale - player->mo->scale)*/; - const fixed_t minspeed = 24*hscale; - const fixed_t maxspeed = 28*hscale; - - if (newspeed > maxspeed && player->kartstuff[k_pogospring] == 2) - newspeed = maxspeed; - if (newspeed < minspeed) - newspeed = minspeed; - } - - finalspeed = newspeed - oldspeed; - - // forwardmove is: - // 50 while accelerating, - // 25 while clutching, - // 0 with no gas, and - // -25 when only braking. - - if (player->kartstuff[k_sneakertimer]) - forwardmove = 50; - - finalspeed *= forwardmove/25; - finalspeed /= 2; - - if (forwardmove < 0 && finalspeed > mapobjectscale*2) - return finalspeed/2; - else if (forwardmove < 0) - return -mapobjectscale/2; - - if (finalspeed < 0) - finalspeed = 0; - - return finalspeed; -} - -void K_DoInstashield(player_t *player) -{ - mobj_t *layera; - mobj_t *layerb; - - if (player->kartstuff[k_instashield] > 0) - return; - - player->kartstuff[k_instashield] = 15; // length of instashield animation - S_StartSound(player->mo, sfx_cdpcm9); - - layera = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDA); - P_SetTarget(&layera->target, player->mo); - - layerb = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDB); - P_SetTarget(&layerb->target, player->mo); -} - -void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem) -{ - UINT8 scoremultiply = 1; - // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. -#ifdef HAVE_BLUA - boolean force = false; // Used to check if Lua ShouldSpin should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldSpin(player, inflictor, source); - if (P_MobjWasRemoved(player->mo)) - return; // mobj was removed (in theory that shouldn't happen) - if (shouldForce == 1) - force = true; - else if (shouldForce == 2) - return; -#else - static const boolean force = false; - (void)inflictor; // in case some weirdo doesn't want Lua. -#endif - - if (!trapitem && G_BattleGametype()) - { - if (K_IsPlayerWanted(player)) - scoremultiply = 3; - else if (player->kartstuff[k_bumper] == 1) - scoremultiply = 2; - } - - if (player->health <= 0) - return; - - if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2) - || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - { - if (!force) // if shoulddamage force, we go THROUGH that. - { - K_DoInstashield(player); - return; - } - } - -#ifdef HAVE_BLUA - if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. - return; -#endif - - if (source && source != player->mo && source->player) - K_PlayHitEmSound(source); - - player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_numsneakers] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_ringboost] = 0; - - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - if (G_BattleGametype()) - { - if (source && source->player && player != source->player) - { - P_AddPlayerScore(source->player, scoremultiply); - K_SpawnBattlePoints(source->player, player, scoremultiply); - if (!trapitem) - { - source->player->kartstuff[k_wanted] -= wantedreduce; - player->kartstuff[k_wanted] -= (wantedreduce/2); - } - } - - 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]) - { - 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(); - } - - player->kartstuff[k_spinouttype] = type; - - if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout, type 2 is no-invuln wipeout - { - // At spinout, player speed is increased to 1/4 their regular speed, moving them forward - if (player->speed < K_GetKartSpeed(player, true)/4) - P_InstaThrust(player->mo, player->mo->angle, FixedMul(K_GetKartSpeed(player, true)/4, player->mo->scale)); - S_StartSound(player->mo, sfx_slip); - } - - player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; - player->powers[pw_flashing] = K_GetKartFlashing(player); - - P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 5); - K_PlayPainSound(player->mo); - - if (player->mo->state != &states[S_KART_SPIN]) - P_SetPlayerMobjState(player->mo, S_KART_SPIN); - - player->kartstuff[k_instashield] = 15; - if (cv_kartdebughuddrop.value && !modeattacking) - K_DropItems(player); - else - K_DropHnextList(player, false); - return; -} - -static void K_RemoveGrowShrink(player_t *player) -{ - if (player->mo && !P_MobjWasRemoved(player->mo)) - { - if (player->kartstuff[k_growshrinktimer] > 0) // Play Shrink noise - S_StartSound(player->mo, sfx_kc59); - else if (player->kartstuff[k_growshrinktimer] < 0) // Play Grow noise - S_StartSound(player->mo, sfx_kc5a); - - if (player->kartstuff[k_invincibilitytimer] == 0) - player->mo->color = player->skincolor; - - player->mo->scalespeed = mapobjectscale/TICRATE; - player->mo->destscale = mapobjectscale; - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; - } - - player->kartstuff[k_growshrinktimer] = 0; - - P_RestoreMusic(player); -} - -void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) -{ - UINT8 scoremultiply = 1; - // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. -#ifdef HAVE_BLUA - boolean force = false; // Used to check if Lua ShouldSquish should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldSquish(player, inflictor, source); - if (P_MobjWasRemoved(player->mo)) - return; // mobj was removed (in theory that shouldn't happen) - if (shouldForce == 1) - force = true; - else if (shouldForce == 2) - return; -#else - static const boolean force = false; - (void)inflictor; // Please stop forgetting to put inflictor in yer functions thank -Lat' -#endif - - if (G_BattleGametype()) - { - if (K_IsPlayerWanted(player)) - scoremultiply = 3; - else if (player->kartstuff[k_bumper] == 1) - scoremultiply = 2; - } - - if (player->health <= 0) - return; - - if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_invincibilitytimer] > 0 - || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - { - if (!force) // You know the drill by now. - { - K_DoInstashield(player); - return; - } - } - -#ifdef HAVE_BLUA - if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. - return; -#endif - - player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_numsneakers] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_ringboost] = 0; - - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - if (G_BattleGametype()) - { - if (source && source->player && player != source->player) - { - P_AddPlayerScore(source->player, scoremultiply); - K_SpawnBattlePoints(source->player, player, scoremultiply); - source->player->kartstuff[k_wanted] -= wantedreduce; - player->kartstuff[k_wanted] -= (wantedreduce/2); - } - - 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]) - { - 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(); - } - - player->kartstuff[k_squishedtimer] = TICRATE; - - // Reduce Shrink timer - if (player->kartstuff[k_growshrinktimer] < 0) - { - player->kartstuff[k_growshrinktimer] += TICRATE; - if (player->kartstuff[k_growshrinktimer] >= 0) - K_RemoveGrowShrink(player); - } - - player->powers[pw_flashing] = K_GetKartFlashing(player); - - player->mo->flags |= MF_NOCLIP; - - if (player->mo->state != &states[S_KART_SQUISH]) // Squash - P_SetPlayerMobjState(player->mo, S_KART_SQUISH); - - P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 5); - K_PlayPainSound(player->mo); - - player->kartstuff[k_instashield] = 15; - if (cv_kartdebughuddrop.value && !modeattacking) - K_DropItems(player); - else - K_DropHnextList(player, false); - return; -} - -void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer -{ - UINT8 scoremultiply = 1; -#ifdef HAVE_BLUA - boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldExplode(player, inflictor, source); - - if (P_MobjWasRemoved(player->mo)) - return; // mobj was removed (in theory that shouldn't happen) - if (shouldForce == 1) - force = true; - else if (shouldForce == 2) - return; - -#else - static const boolean force = false; -#endif - - if (G_BattleGametype()) - { - if (K_IsPlayerWanted(player)) - scoremultiply = 3; - else if (player->kartstuff[k_bumper] == 1) - scoremultiply = 2; - } - - if (player->health <= 0) - return; - - if (player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 // Do not check spinout, because SPB and Eggman should combo - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - { - if (!force) // ShouldDamage can bypass that, again. - { - K_DoInstashield(player); - return; - } - } - -#ifdef HAVE_BLUA - if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah - return; -#endif - - if (source && source != player->mo && source->player) - K_PlayHitEmSound(source); - - player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! - player->mo->momx = player->mo->momy = 0; - - player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_numsneakers] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_ringboost] = 0; - - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - // This is the only part that SHOULDN'T combo :VVVVV - if (G_BattleGametype() && !(player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2))) - { - if (source && source->player && player != source->player) - { - P_AddPlayerScore(source->player, scoremultiply); - K_SpawnBattlePoints(source->player, player, scoremultiply); - source->player->kartstuff[k_wanted] -= wantedreduce; - player->kartstuff[k_wanted] -= (wantedreduce/2); - } - - 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]) - { - 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(); - } - - player->kartstuff[k_spinouttype] = 1; - player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; - - player->powers[pw_flashing] = K_GetKartFlashing(player); - - if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1) - { - player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1; - player->mo->momz *= 2; - } - - if (player->mo->eflags & MFE_UNDERWATER) - player->mo->momz = (117 * player->mo->momz) / 200; - - if (player->mo->state != &states[S_KART_SPIN]) - P_SetPlayerMobjState(player->mo, S_KART_SPIN); - - P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 5); - K_PlayPainSound(player->mo); - - if (P_IsDisplayPlayer(player)) - P_StartQuake(64<kartstuff[k_instashield] = 15; - K_DropItems(player); - - return; -} - -void K_StealBumper(player_t *player, player_t *victim, boolean force) -{ - INT32 newbumper; - angle_t newangle, diff; - fixed_t newx, newy; - mobj_t *newmo; - - if (!G_BattleGametype()) - return; - - if (player->health <= 0 || victim->health <= 0) - return; - - if (!force) - { - if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= K_StartingBumperCount()+2 - return; - - if (player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0) - return; - - if (victim->powers[pw_flashing] > 0 || victim->kartstuff[k_squishedtimer] > 0 || (victim->kartstuff[k_spinouttimer] > 0 && victim->kartstuff[k_spinouttype] != 2) - || victim->kartstuff[k_invincibilitytimer] > 0 || victim->kartstuff[k_growshrinktimer] > 0 || victim->kartstuff[k_hyudorotimer] > 0) - { - K_DoInstashield(victim); - return; - } - } - - if (netgame && player->kartstuff[k_bumper] <= 0) - CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); - - newbumper = player->kartstuff[k_bumper]; - 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); - - 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; -} - -// source is the mobj that originally threw the bomb that exploded etc. -// Spawns the sphere around the explosion that handles spinout -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) -{ - mobj_t *mobj; - mobj_t *ghost = NULL; - INT32 i; - TVector v; - TVector *res; - fixed_t finalx, finaly, finalz, dist; - //mobj_t hoopcenter; - angle_t degrees, fa, closestangle; - fixed_t mobjx, mobjy, mobjz; - - //hoopcenter.x = x; - //hoopcenter.y = y; - //hoopcenter.z = z; - - //hoopcenter.z = z - mobjinfo[type].height/2; - - degrees = FINEANGLES/number; - - closestangle = 0; - - // Create the hoop! - for (i = 0; i < number; i++) - { - fa = (i*degrees); - v[0] = FixedMul(FINECOSINE(fa),radius); - v[1] = 0; - v[2] = FixedMul(FINESINE(fa),radius); - v[3] = FRACUNIT; - - res = VectorMatrixMultiply(v, *RotateXMatrix(rotangle)); - M_Memcpy(&v, res, sizeof (v)); - res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle)); - M_Memcpy(&v, res, sizeof (v)); - - finalx = x + v[0]; - finaly = y + v[1]; - finalz = z + v[2]; - - mobj = P_SpawnMobj(finalx, finaly, finalz, type); - - mobj->z -= mobj->height>>1; - - // change angle - mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y); - - // change slope - dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z); - - if (dist < 1) - dist = 1; - - mobjx = mobj->x; - mobjy = mobj->y; - mobjz = mobj->z; - - if (ghostit) - { - ghost = P_SpawnGhostMobj(mobj); - P_SetMobjState(mobj, S_NULL); - mobj = ghost; - } - - if (spawncenter) - { - mobj->x = x; - mobj->y = y; - mobj->z = z; - } - - mobj->momx = FixedMul(FixedDiv(mobjx - x, dist), FixedDiv(dist, 6*FRACUNIT)); - mobj->momy = FixedMul(FixedDiv(mobjy - y, dist), FixedDiv(dist, 6*FRACUNIT)); - mobj->momz = FixedMul(FixedDiv(mobjz - z, dist), FixedDiv(dist, 6*FRACUNIT)); - - if (source && !P_MobjWasRemoved(source)) - P_SetTarget(&mobj->target, source); - } -} - -#define MINEQUAKEDIST 4096 - -// Spawns the purely visual explosion -void K_SpawnMineExplosion(mobj_t *source, UINT8 color) -{ - INT32 i, radius, height; - mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING); - mobj_t *dust; - mobj_t *truc; - INT32 speed, speed2; - INT32 pnum; - player_t *p; - - // check for potential display players near the source so we can have a sick earthquake / flashpal. - for (pnum = 0; pnum < MAXPLAYERS; pnum++) - { - p = &players[pnum]; - - if (!playeringame[pnum] || !P_IsDisplayPlayer(p)) - continue; - - if (R_PointToDist2(p->mo->x, p->mo->y, source->x, source->y) < mapobjectscale*MINEQUAKEDIST) - { - P_StartQuake(55<mo, source)) - { - bombflashtimer = TICRATE*2; - P_FlashPal(p, 1, 1); - } - break; // we can break right now because quakes are global to all split players somehow. - } - } - - K_MatchGenericExtraFlags(smoldering, source); - smoldering->tics = TICRATE*3; - radius = source->radius>>FRACBITS; - height = source->height>>FRACBITS; - - if (!color) - color = SKINCOLOR_KETCHUP; - - for (i = 0; i < 32; i++) - { - dust = P_SpawnMobj(source->x, source->y, source->z, MT_SMOKE); - P_SetMobjState(dust, S_OPAQUESMOKE1); - dust->angle = (ANGLE_180/16) * i; - P_SetScale(dust, source->scale); - dust->destscale = source->scale*10; - dust->scalespeed = source->scale/12; - P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, source->scale)); - - truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, - source->y + P_RandomRange(-radius, radius)*FRACUNIT, - source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMEXPLODE); - K_MatchGenericExtraFlags(truc, source); - P_SetScale(truc, source->scale); - truc->destscale = source->scale*6; - truc->scalespeed = source->scale/12; - speed = FixedMul(10*FRACUNIT, source->scale)>>FRACBITS; - truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; - truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; - speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; - truc->momz = P_RandomRange(-speed, speed)*FRACUNIT*P_MobjFlip(truc); - if (truc->eflags & MFE_UNDERWATER) - truc->momz = (117 * truc->momz) / 200; - truc->color = color; - } - - for (i = 0; i < 16; i++) - { - dust = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, - source->y + P_RandomRange(-radius, radius)*FRACUNIT, - source->z + P_RandomRange(0, height)*FRACUNIT, MT_SMOKE); - P_SetMobjState(dust, S_OPAQUESMOKE1); - P_SetScale(dust, source->scale); - dust->destscale = source->scale*10; - dust->scalespeed = source->scale/12; - dust->tics = 30; - dust->momz = P_RandomRange(FixedMul(3*FRACUNIT, source->scale)>>FRACBITS, FixedMul(7*FRACUNIT, source->scale)>>FRACBITS)*FRACUNIT; - - truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, - source->y + P_RandomRange(-radius, radius)*FRACUNIT, - source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMPARTICLE); - K_MatchGenericExtraFlags(truc, source); - P_SetScale(truc, source->scale); - truc->destscale = source->scale*5; - truc->scalespeed = source->scale/12; - speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; - truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; - truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; - speed = FixedMul(15*FRACUNIT, source->scale)>>FRACBITS; - speed2 = FixedMul(45*FRACUNIT, source->scale)>>FRACBITS; - truc->momz = P_RandomRange(speed, speed2)*FRACUNIT*P_MobjFlip(truc); - if (P_RandomChance(FRACUNIT/2)) - truc->momz = -truc->momz; - if (truc->eflags & MFE_UNDERWATER) - truc->momz = (117 * truc->momz) / 200; - truc->tics = TICRATE*2; - truc->color = color; - } -} - -#undef MINEQUAKEDIST - -static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed) -{ - mobj_t *th; - fixed_t x, y, z; - fixed_t finalspeed = speed; - mobj_t *throwmo; - - if (source->player && source->player->speed > K_GetKartSpeed(source->player, false)) - { - angle_t input = source->angle - an; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - finalspeed = max(speed, FixedMul(speed, FixedMul( - FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed. - (((180<x + source->momx + FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); - y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); - z = source->z; // spawn on the ground please - - if (P_MobjFlip(source) < 0) - { - z = source->z+source->height - mobjinfo[type].height; - } - - th = P_SpawnMobj(x, y, z, type); - - th->flags2 |= flags2; - th->threshold = 10; - - if (th->info->seesound) - S_StartSound(source, th->info->seesound); - - P_SetTarget(&th->target, source); - - P_SetScale(th, source->scale); - th->destscale = source->destscale; - - if (P_IsObjectOnGround(source)) - { - // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn - // This should set it for FOFs - P_TeleportMove(th, th->x, th->y, th->z); - // spawn on the ground if the player is on the ground - if (P_MobjFlip(source) < 0) - { - th->z = th->ceilingz - th->height; - th->eflags |= MFE_VERTICALFLIP; - } - else - th->z = th->floorz; - } - - th->angle = an; - th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); - th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); - - switch (type) - { - case MT_ORBINAUT: - if (source && source->player) - th->color = source->player->skincolor; - else - th->color = SKINCOLOR_GREY; - th->movefactor = finalspeed; - break; - case MT_JAWZ: - if (source && source->player) - { - INT32 lasttarg = source->player->kartstuff[k_lastjawztarget]; - th->cvmem = source->player->skincolor; - if ((lasttarg >= 0 && lasttarg < MAXPLAYERS) - && playeringame[lasttarg] - && !players[lasttarg].spectator - && players[lasttarg].mo) - { - P_SetTarget(&th->tracer, players[lasttarg].mo); - } - } - else - th->cvmem = SKINCOLOR_KETCHUP; - /* FALLTHRU */ - case MT_JAWZ_DUD: - S_StartSound(th, th->info->activesound); - /* FALLTHRU */ - case MT_SPB: - th->movefactor = finalspeed; - break; - case MT_BUBBLESHIELDTRAP: - P_SetScale(th, ((5*th->destscale)>>2)*4); - th->destscale = (5*th->destscale)>>2; - S_StartSound(th, sfx_s3kbfl); - S_StartSound(th, sfx_cdfm35); - break; - default: - break; - } - - if (type != MT_BUBBLESHIELDTRAP) - { - x = x + P_ReturnThrustX(source, an, source->radius + th->radius); - y = y + P_ReturnThrustY(source, an, source->radius + th->radius); - throwmo = P_SpawnMobj(x, y, z, MT_FIREDITEM); - throwmo->movecount = 1; - throwmo->movedir = source->angle - an; - P_SetTarget(&throwmo->target, source); - } - - return NULL; -} - -UINT8 K_DriftSparkColor(player_t *player, INT32 charge) -{ - INT32 ds = K_GetKartDriftSparkValue(player); - UINT8 color = SKINCOLOR_NONE; - - if (charge < 0) - { - // Stage 0: Yellow - color = SKINCOLOR_GOLD; - } - else if (charge >= ds*4) - { - // Stage 3: Rainbow - if (charge <= (ds*4)+(32*3)) - { - // transition - color = SKINCOLOR_SILVER; - } - else - { - color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - } - } - else if (charge >= ds*2) - { - // Stage 2: Blue - if (charge <= (ds*2)+(32*3)) - { - // transition - color = SKINCOLOR_PURPLE; - } - else - { - color = SKINCOLOR_SAPPHIRE; - } - } - else if (charge >= ds) - { - // Stage 1: Red - if (charge <= (ds)+(32*3)) - { - // transition - color = SKINCOLOR_TANGERINE; - } - else - { - color = SKINCOLOR_KETCHUP; - } - } - - return color; -} - -static void K_SpawnDriftSparks(player_t *player) -{ - INT32 ds = K_GetKartDriftSparkValue(player); - fixed_t newx; - fixed_t newy; - mobj_t *spark; - angle_t travelangle; - INT32 i; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (leveltime % 2 == 1) - return; - - if (!player->kartstuff[k_drift] - || (player->kartstuff[k_driftcharge] < ds && !(player->kartstuff[k_driftcharge] < 0))) - return; - - travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; - - for (i = 0; i < 2; i++) - { - SINT8 size = 1; - UINT8 trail = 0; - - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); - spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK); - - P_SetTarget(&spark->target, player->mo); - spark->angle = travelangle-(ANGLE_45/5)*player->kartstuff[k_drift]; - spark->destscale = player->mo->scale; - P_SetScale(spark, player->mo->scale); - - spark->momx = player->mo->momx/2; - spark->momy = player->mo->momy/2; - //spark->momz = player->mo->momz/2; - - spark->color = K_DriftSparkColor(player, player->kartstuff[k_driftcharge]); - - if (player->kartstuff[k_driftcharge] < 0) - { - // Stage 0: Yellow - size = 0; - } - else if (player->kartstuff[k_driftcharge] >= ds*4) - { - // Stage 3: Rainbow - size = 2; - trail = 2; - - if (player->kartstuff[k_driftcharge] <= (ds*4)+(32*3)) - { - // transition - P_SetScale(spark, (spark->destscale = spark->scale*3/2)); - } - else - { - spark->colorized = true; - } - } - else if (player->kartstuff[k_driftcharge] >= ds*2) - { - // Stage 2: Blue - size = 2; - trail = 1; - - if (player->kartstuff[k_driftcharge] <= (ds*2)+(32*3)) - { - // transition - P_SetScale(spark, (spark->destscale = spark->scale*3/2)); - } - } - else - { - // Stage 1: Red - size = 1; - - if (player->kartstuff[k_driftcharge] <= (ds)+(32*3)) - { - // transition - P_SetScale(spark, (spark->destscale = spark->scale*2)); - } - } - - if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts - || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn < 0)) - { - if ((player->kartstuff[k_drift] < 0 && (i & 1)) - || (player->kartstuff[k_drift] > 0 && !(i & 1))) - { - size++; - } - else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) - || (player->kartstuff[k_drift] > 0 && (i & 1))) - { - size--; - } - } - else if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn < 0) // Outward drifts - || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn > 0)) - { - if ((player->kartstuff[k_drift] < 0 && (i & 1)) - || (player->kartstuff[k_drift] > 0 && !(i & 1))) - { - size--; - } - else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) - || (player->kartstuff[k_drift] > 0 && (i & 1))) - { - size++; - } - } - - if (size == 2) - P_SetMobjState(spark, S_DRIFTSPARK_A1); - else if (size < 1) - P_SetMobjState(spark, S_DRIFTSPARK_C1); - else if (size > 2) - P_SetMobjState(spark, S_DRIFTSPARK_D1); - - if (trail > 0) - spark->tics += trail; - - K_MatchGenericExtraFlags(spark, player->mo); - } -} - -static void K_SpawnAIZDust(player_t *player) -{ - fixed_t newx; - fixed_t newy; - mobj_t *spark; - angle_t travelangle; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (leveltime % 2 == 1) - return; - - if (!P_IsObjectOnGround(player->mo)) - return; - - travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - //S_StartSound(player->mo, sfx_s3k47); - - { - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); - spark = P_SpawnMobj(newx, newy, player->mo->z, MT_AIZDRIFTSTRAT); - - spark->angle = travelangle+(player->kartstuff[k_aizdriftstrat]*ANGLE_90); - P_SetScale(spark, (spark->destscale = (3*player->mo->scale)>>2)); - - spark->momx = (6*player->mo->momx)/5; - spark->momy = (6*player->mo->momy)/5; - //spark->momz = player->mo->momz/2; - - K_MatchGenericExtraFlags(spark, player->mo); - } -} - -void K_SpawnBoostTrail(player_t *player) -{ - fixed_t newx; - fixed_t newy; - fixed_t ground; - mobj_t *flame; - angle_t travelangle; - INT32 i; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (!P_IsObjectOnGround(player->mo) - || player->kartstuff[k_hyudorotimer] != 0 - || (G_BattleGametype() && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer])) - return; - - if (player->mo->eflags & MFE_VERTICALFLIP) - ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); - else - ground = player->mo->floorz; - - if (player->kartstuff[k_drift] != 0) - travelangle = player->mo->angle; - else - travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); - - for (i = 0; i < 2; i++) - { - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); -#ifdef ESLOPE - if (player->mo->standingslope) - { - ground = P_GetZAt(player->mo->standingslope, newx, newy); - if (player->mo->eflags & MFE_VERTICALFLIP) - ground -= FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); - } -#endif - flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL); - - P_SetTarget(&flame->target, player->mo); - flame->angle = travelangle; - flame->fuse = TICRATE*2; - flame->destscale = player->mo->scale; - P_SetScale(flame, player->mo->scale); - // not K_MatchGenericExtraFlags so that a stolen sneaker can be seen - K_FlipFromObject(flame, player->mo); - - flame->momx = 8; - P_XYMovement(flame); - if (P_MobjWasRemoved(flame)) - continue; - - if (player->mo->eflags & MFE_VERTICALFLIP) - { - if (flame->z + flame->height < flame->ceilingz) - P_RemoveMobj(flame); - } - else if (flame->z > flame->floorz) - P_RemoveMobj(flame); - } -} - -void K_SpawnSparkleTrail(mobj_t *mo) -{ - const INT32 rad = (mo->radius*2)>>FRACBITS; - mobj_t *sparkle; - INT32 i; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (i = 0; i < 3; i++) - { - fixed_t newx = mo->x + mo->momx + (P_RandomRange(-rad, rad)<y + mo->momy + (P_RandomRange(-rad, rad)<z + mo->momz + (P_RandomRange(0, mo->height>>FRACBITS)<target, mo); - sparkle->destscale = mo->destscale; - P_SetScale(sparkle, mo->scale); - sparkle->color = mo->color; - //sparkle->colorized = mo->colorized; - } - - P_SetMobjState(sparkle, S_KARTINVULN_LARGE1); -} - -void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) -{ - mobj_t *dust; - angle_t aoff; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - if (mo->player) - aoff = (mo->player->frameangle + ANGLE_180); - else - aoff = (mo->angle + ANGLE_180); - - if ((leveltime / 2) & 1) - aoff -= ANGLE_45; - else - aoff += ANGLE_45; - - dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), - mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), - mo->z, MT_WIPEOUTTRAIL); - - P_SetTarget(&dust->target, mo); - dust->angle = R_PointToAngle2(0,0,mo->momx,mo->momy); - dust->destscale = mo->scale; - P_SetScale(dust, mo->scale); - K_FlipFromObject(dust, mo); - - if (translucent) // offroad effect - { - dust->momx = mo->momx/2; - dust->momy = mo->momy/2; - dust->momz = mo->momz/2; - } - - if (translucent) - dust->flags2 |= MF2_SHADOW; -} - -void K_SpawnDraftDust(mobj_t *mo) -{ - UINT8 i; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (i = 0; i < 2; i++) - { - angle_t ang, aoff; - SINT8 sign = 1; - UINT8 foff = 0; - mobj_t *dust; - boolean drifting = false; - - if (mo->player) - { - UINT8 leniency = (3*TICRATE)/4 + ((mo->player->kartweight-1) * (TICRATE/4)); - - ang = mo->player->frameangle; - - if (mo->player->kartstuff[k_drift] != 0) - { - drifting = true; - ang += (mo->player->kartstuff[k_drift] * ((ANGLE_270 + ANGLE_22h) / 5)); // -112.5 doesn't work. I fucking HATE SRB2 angles - if (mo->player->kartstuff[k_drift] < 0) - sign = 1; - else - sign = -1; - } - - foff = 5 - ((mo->player->kartstuff[k_draftleeway] * 5) / leniency); - - // this shouldn't happen - if (foff > 4) - foff = 4; - } - else - ang = mo->angle; - - if (!drifting) - { - if (i & 1) - sign = -1; - else - sign = 1; - } - - aoff = (ang + ANGLE_180) + (ANGLE_45 * sign); - - dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)), - mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)), - mo->z, MT_DRAFTDUST); - - P_SetMobjState(dust, S_DRAFTDUST1 + foff); - - P_SetTarget(&dust->target, mo); - dust->angle = ang - (ANGLE_90 * sign); // point completely perpendicular from the player - dust->destscale = mo->scale; - P_SetScale(dust, mo->scale); - K_FlipFromObject(dust, mo); - - if (leveltime & 1) - dust->tics++; // "randomize" animation - - dust->momx = (4*mo->momx)/5; - dust->momy = (4*mo->momy)/5; - //dust->momz = (4*mo->momz)/5; - - P_Thrust(dust, dust->angle, 4*mo->scale); - - if (drifting) // only 1 trail while drifting - break; - } -} - -// K_DriftDustHandling -// Parameters: -// spawner: The map object that is spawning the drift dust -// Description: Spawns the drift dust for objects, players use rmomx/y, other objects use regular momx/y. -// Also plays the drift sound. -// Other objects should be angled towards where they're trying to go so they don't randomly spawn dust -// Do note that most of the function won't run in odd intervals of frames -void K_DriftDustHandling(mobj_t *spawner) -{ - angle_t anglediff; - const INT16 spawnrange = spawner->radius >> FRACBITS; - - if (!P_IsObjectOnGround(spawner) || leveltime % 2 != 0) - return; - - if (spawner->player) - { - if (spawner->player->pflags & PF_SKIDDOWN) - { - anglediff = abs((signed)(spawner->angle - spawner->player->frameangle)); - if (leveltime % 6 == 0) - S_StartSound(spawner, sfx_screec); // repeated here because it doesn't always happen to be within the range when this is the case - } - else - { - angle_t playerangle = spawner->angle; - - if (spawner->player->speed < 5*spawner->scale) - return; - - if (spawner->player->cmd.forwardmove < 0) - playerangle += ANGLE_180; - - anglediff = abs((signed)(playerangle - R_PointToAngle2(0, 0, spawner->player->rmomx, spawner->player->rmomy))); - } - } - else - { - if (P_AproxDistance(spawner->momx, spawner->momy) < 5*spawner->scale) - return; - - anglediff = abs((signed)(spawner->angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy))); - } - - if (anglediff > ANGLE_180) - anglediff = InvAngle(anglediff); - - if (anglediff > ANG10*4) // Trying to turn further than 40 degrees - { - fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; - fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; - INT32 speedrange = 2; - mobj_t *dust = P_SpawnMobj(spawner->x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST); - dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); - dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); - dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale)); - P_SetScale(dust, spawner->scale/2); - dust->destscale = spawner->scale * 3; - dust->scalespeed = spawner->scale/12; - - if (leveltime % 6 == 0) - S_StartSound(spawner, sfx_screec); - - K_MatchGenericExtraFlags(dust, spawner); - - // Sparkle-y warning for when you're about to change drift sparks! - if (spawner->player && spawner->player->kartstuff[k_drift]) - { - INT32 driftval = K_GetKartDriftSparkValue(spawner->player); - INT32 warntime = driftval/3; - INT32 dc = spawner->player->kartstuff[k_driftcharge]; - UINT8 c = SKINCOLOR_NONE; - boolean rainbow = false; - - if (dc >= 0) - { - dc += warntime; - } - - c = K_DriftSparkColor(spawner->player, dc); - - if (dc > (4*driftval)+(32*3)) - { - rainbow = true; - } - - if (c != SKINCOLOR_NONE) - { - P_SetMobjState(dust, S_DRIFTWARNSPARK1); - dust->color = c; - dust->colorized = rainbow; - } - } - } -} - -static mobj_t *K_FindLastTrailMobj(player_t *player) -{ - mobj_t *trail; - - if (!player || !(trail = player->mo) || !player->mo->hnext || !player->mo->hnext->health) - return NULL; - - while (trail->hnext && !P_MobjWasRemoved(trail->hnext) && trail->hnext->health) - { - trail = trail->hnext; - } - - return trail; -} - -static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow) -{ - mobj_t *mo; - INT32 dir; - fixed_t PROJSPEED; - angle_t newangle; - fixed_t newx, newy, newz; - mobj_t *throwmo; - - if (!player) - return NULL; - - // Figure out projectile speed by game speed - if (missile) - { - // Use info->speed for missiles - PROJSPEED = FixedMul(mobjinfo[mapthing].speed, K_GetKartGameSpeedScalar(gamespeed)); - } - else - { - // Use pre-determined speed for tossing - PROJSPEED = FixedMul(82 << FRACBITS, K_GetKartGameSpeedScalar(gamespeed)); - } - - // Scale to map size - PROJSPEED = FixedMul(PROJSPEED, mapobjectscale); - - if (altthrow) - { - if (altthrow == 2) // Kitchen sink throwing - { -#if 0 - if (player->kartstuff[k_throwdir] == 1) - dir = 3; - else if (player->kartstuff[k_throwdir] == -1) - dir = 1; - else - dir = 2; -#else - if (player->kartstuff[k_throwdir] == 1) - dir = 2; - else - dir = 1; -#endif - } - else - { - if (player->kartstuff[k_throwdir] == 1) - dir = 2; - else if (player->kartstuff[k_throwdir] == -1) - dir = -1; - else - dir = 1; - } - } - else - { - if (player->kartstuff[k_throwdir] != 0) - dir = player->kartstuff[k_throwdir]; - else - dir = defaultDir; - } - - if (missile) // Shootables - { - if (mapthing == MT_BALLHOG) // Messy - { - if (dir == -1) - { - // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x03000000, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x03000000, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x06000000, 0, PROJSPEED/8); - } - else - { - // Shoot forward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x06000000, 0, PROJSPEED); - } - } - else - { - if (dir == -1 && mapthing != MT_SPB) - { - // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); - } - else - { - // Shoot forward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); - } - } - } - else - { - player->kartstuff[k_bananadrag] = 0; // RESET timer, for multiple bananas - - if (dir > 0) - { - // Shoot forward - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing); - //K_FlipFromObject(mo, player->mo); - // These are really weird so let's make it a very specific case to make SURE it works... - if (player->mo->eflags & MFE_VERTICALFLIP) - { - mo->z -= player->mo->height; - mo->flags2 |= MF2_OBJECTFLIP; - mo->eflags |= MFE_VERTICALFLIP; - } - - mo->threshold = 10; - P_SetTarget(&mo->target, player->mo); - - S_StartSound(player->mo, mo->info->seesound); - - if (mo) - { - angle_t fa = player->mo->angle>>ANGLETOFINESHIFT; - fixed_t HEIGHT = (20 + (dir*10))*FRACUNIT + (player->mo->momz*P_MobjFlip(player->mo)); - - P_SetObjectMomZ(mo, HEIGHT, false); - mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir); - mo->momy = player->mo->momy + FixedMul(FINESINE(fa), PROJSPEED*dir); - - mo->extravalue2 = dir; - - if (mo->eflags & MFE_UNDERWATER) - mo->momz = (117 * mo->momz) / 200; - } - - // this is the small graphic effect that plops in you when you throw an item: - throwmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FIREDITEM); - P_SetTarget(&throwmo->target, player->mo); - // Ditto: - if (player->mo->eflags & MFE_VERTICALFLIP) - { - throwmo->z -= player->mo->height; - throwmo->flags2 |= MF2_OBJECTFLIP; - throwmo->eflags |= MFE_VERTICALFLIP; - } - - throwmo->movecount = 0; // above player - } - else - { - mobj_t *lasttrail = K_FindLastTrailMobj(player); - - if (mapthing == MT_BUBBLESHIELDTRAP) // Drop directly on top of you. - { - newangle = player->mo->angle; - newx = player->mo->x + player->mo->momx; - newy = player->mo->y + player->mo->momy; - newz = player->mo->z; - } - else if (lasttrail) - { - newangle = lasttrail->angle; - newx = lasttrail->x; - newy = lasttrail->y; - newz = lasttrail->z; - } - else - { - // Drop it directly behind you. - fixed_t dropradius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(mobjinfo[mapthing].radius, mobjinfo[mapthing].radius); - - newangle = player->mo->angle; - - newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, dropradius); - newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, dropradius); - newz = player->mo->z; - } - - mo = P_SpawnMobj(newx, newy, newz, mapthing); // this will never return null because collision isn't processed here - K_FlipFromObject(mo, player->mo); - - mo->threshold = 10; - P_SetTarget(&mo->target, player->mo); - - P_SetScale(mo, player->mo->scale); - mo->destscale = player->mo->destscale; - - if (P_IsObjectOnGround(player->mo)) - { - // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn - // This should set it for FOFs - P_TeleportMove(mo, mo->x, mo->y, mo->z); // however, THIS can fuck up your day. just absolutely ruin you. - if (P_MobjWasRemoved(mo)) - return NULL; - - if (P_MobjFlip(mo) > 0) - { - if (mo->floorz > mo->target->z - mo->height) - { - mo->z = mo->floorz; - } - } - else - { - if (mo->ceilingz < mo->target->z + mo->target->height + mo->height) - { - mo->z = mo->ceilingz - mo->height; - } - } - } - - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->eflags |= MFE_VERTICALFLIP; - - if (mapthing == MT_SSMINE) - mo->extravalue1 = 49; // Pads the start-up length from 21 frames to a full 2 seconds - else if (mapthing == MT_BUBBLESHIELDTRAP) - { - P_SetScale(mo, ((5*mo->destscale)>>2)*4); - mo->destscale = (5*mo->destscale)>>2; - S_StartSound(mo, sfx_s3kbfl); - } - } - } - - return mo; -} - -void K_PuntMine(mobj_t *thismine, mobj_t *punter) -{ - angle_t fa = R_PointToAngle2(0, 0, punter->momx, punter->momy) >> ANGLETOFINESHIFT; - fixed_t z = 30*mapobjectscale + punter->momz; - fixed_t spd; - mobj_t *mine; - - if (!thismine || P_MobjWasRemoved(thismine)) - return; - - if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine - { - mine = P_SpawnMobj(thismine->x, thismine->y, thismine->z, MT_SSMINE); - P_SetTarget(&mine->target, thismine->target); - mine->angle = thismine->angle; - mine->flags2 = thismine->flags2; - mine->floorz = thismine->floorz; - mine->ceilingz = thismine->ceilingz; - P_RemoveMobj(thismine); - } - else - mine = thismine; - - if (!mine || P_MobjWasRemoved(mine)) - return; - - spd = (82 + ((gamespeed-1) * 14))*mapobjectscale; // Avg Speed is 41 in Normal - - mine->flags |= MF_NOCLIPTHING; - - P_SetMobjState(mine, S_SSMINE_AIR1); - mine->threshold = 10; - mine->extravalue1 = 0; - mine->reactiontime = mine->info->reactiontime; - - mine->momx = punter->momx + FixedMul(FINECOSINE(fa), spd); - mine->momy = punter->momy + FixedMul(FINESINE(fa), spd); - mine->momz = P_MobjFlip(mine) * z; - - mine->flags &= ~MF_NOCLIPTHING; -} - -#define THUNDERRADIUS 320 - -static void K_DoThunderShield(player_t *player) -{ - mobj_t *mo; - int i = 0; - fixed_t sx; - fixed_t sy; - angle_t an; - - S_StartSound(player->mo, sfx_zio3); - P_NukeEnemies(player->mo, player->mo, RING_DIST/4); - - // spawn vertical bolt - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_LZIO11); - mo->color = SKINCOLOR_TEAL; - mo->scale = player->mo->scale*3 + (player->mo->scale/2); - - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_LZIO21); - mo->color = SKINCOLOR_CYAN; - mo->scale = player->mo->scale*3 + (player->mo->scale/2); - - // spawn horizontal bolts; - for (i=0; i<7; i++) - { - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); - mo->angle = P_RandomRange(0, 359)*ANG1; - mo->fuse = P_RandomRange(20, 50); - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_KLIT1); - } - - // spawn the radius thing: - an = ANGLE_22h; - for (i=0; i<15; i++) - { - sx = player->mo->x + FixedMul((player->mo->scale*THUNDERRADIUS), FINECOSINE((an*i)>>ANGLETOFINESHIFT)); - sy = player->mo->y + FixedMul((player->mo->scale*THUNDERRADIUS), FINESINE((an*i)>>ANGLETOFINESHIFT)); - mo = P_SpawnMobj(sx, sy, player->mo->z, MT_THOK); - mo-> angle = an*i; - mo->extravalue1 = THUNDERRADIUS; // Used to know whether we should teleport by radius or something. - mo->scale = player->mo->scale*3; - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_KSPARK1); - } -} - -#undef THUNDERRADIUS - -static void K_FlameDashLeftoverSmoke(mobj_t *src) -{ - UINT8 i; - - for (i = 0; i < 2; i++) - { - mobj_t *smoke = P_SpawnMobj(src->x, src->y, src->z+(8<scale); - smoke->destscale = 3*src->scale/2; - smoke->scalespeed = src->scale/12; - - smoke->momx = 3*src->momx/4; - smoke->momy = 3*src->momy/4; - smoke->momz = 3*src->momz/4; - - P_Thrust(smoke, src->angle + FixedAngle(P_RandomRange(135, 225)<scale); - smoke->momz += P_RandomRange(0, 4) * src->scale; - } -} - -static void K_DoHyudoroSteal(player_t *player) -{ - INT32 i, numplayers = 0; - INT32 playerswappable[MAXPLAYERS]; - INT32 stealplayer = -1; // The player that's getting stolen from - INT32 prandom = 0; - boolean sink = P_RandomChance(FRACUNIT/64); - INT32 hyu = hyudorotime; - - if (G_RaceGametype()) - hyu *= 2; // double in race - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE - && player != &players[i] && !players[i].exiting && !players[i].spectator // Player in-game - - // Can steal from this player - && (G_RaceGametype() //&& players[i].kartstuff[k_position] < player->kartstuff[k_position]) - || (G_BattleGametype() && players[i].kartstuff[k_bumper] > 0)) - - // Has an item - && (players[i].kartstuff[k_itemtype] - && players[i].kartstuff[k_itemamount] - && !players[i].kartstuff[k_itemheld] - && !players[i].karthud[khud_itemblink])) - { - playerswappable[numplayers] = i; - numplayers++; - } - } - - prandom = P_RandomFixed(); - S_StartSound(player->mo, sfx_s3k92); - - if (sink && numplayers > 0 && cv_kitchensink.value) // BEHOLD THE KITCHEN SINK - { - player->kartstuff[k_hyudorotimer] = hyu; - player->kartstuff[k_stealingtimer] = stealtime; - - player->kartstuff[k_itemtype] = KITEM_KITCHENSINK; - player->kartstuff[k_itemamount] = 1; - player->kartstuff[k_itemheld] = 0; - return; - } - else if ((G_RaceGametype() && player->kartstuff[k_position] == 1) || numplayers == 0) // No-one can be stolen from? Oh well... - { - player->kartstuff[k_hyudorotimer] = hyu; - player->kartstuff[k_stealingtimer] = stealtime; - return; - } - else if (numplayers == 1) // With just 2 players, we just need to set the other player to be the one to steal from - { - stealplayer = playerswappable[numplayers-1]; - } - else if (numplayers > 1) // We need to choose between the available candidates for the 2nd player - { - stealplayer = playerswappable[prandom%(numplayers-1)]; - } - - if (stealplayer > -1) // Now here's where we do the stealing, has to be done here because we still know the player we're stealing from - { - player->kartstuff[k_hyudorotimer] = hyu; - player->kartstuff[k_stealingtimer] = stealtime; - players[stealplayer].kartstuff[k_stolentimer] = stealtime; - - player->kartstuff[k_itemtype] = players[stealplayer].kartstuff[k_itemtype]; - player->kartstuff[k_itemamount] = players[stealplayer].kartstuff[k_itemamount]; - player->kartstuff[k_itemheld] = 0; - - players[stealplayer].kartstuff[k_itemtype] = KITEM_NONE; - players[stealplayer].kartstuff[k_itemamount] = 0; - players[stealplayer].kartstuff[k_itemheld] = 0; - - if (P_IsDisplayPlayer(&players[stealplayer]) && !r_splitscreen) - S_StartSound(NULL, sfx_s3k92); - } -} - -void K_DoSneaker(player_t *player, INT32 type) -{ - const fixed_t intendedboost = FRACUNIT/2; - - if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) - { - const sfxenum_t normalsfx = sfx_cdfm01; - const sfxenum_t smallsfx = sfx_cdfm40; - sfxenum_t sfx = normalsfx; - - if (player->kartstuff[k_numsneakers]) - { - // Use a less annoying sound when stacking sneakers. - sfx = smallsfx; - } - - S_StopSoundByID(player->mo, normalsfx); - S_StopSoundByID(player->mo, smallsfx); - S_StartSound(player->mo, sfx); - - K_SpawnDashDustRelease(player); - if (intendedboost > player->kartstuff[k_speedboost]) - player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); - - player->kartstuff[k_numsneakers]++; - } - - if (!player->kartstuff[k_sneakertimer]) - { - if (type == 2) - { - if (player->mo->hnext) - { - mobj_t *cur = player->mo->hnext; - while (cur && !P_MobjWasRemoved(cur)) - { - if (!cur->tracer) - { - mobj_t *overlay = P_SpawnMobj(cur->x, cur->y, cur->z, MT_BOOSTFLAME); - P_SetTarget(&overlay->target, cur); - P_SetTarget(&cur->tracer, overlay); - P_SetScale(overlay, (overlay->destscale = 3*cur->scale/4)); - K_FlipFromObject(overlay, cur); - } - cur = cur->hnext; - } - } - } - else - { - mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BOOSTFLAME); - P_SetTarget(&overlay->target, player->mo); - P_SetScale(overlay, (overlay->destscale = player->mo->scale)); - K_FlipFromObject(overlay, player->mo); - } - } - - if (type != 0) - { - player->pflags |= PF_ATTACKDOWN; - K_PlayBoostTaunt(player->mo); - - } - - player->kartstuff[k_sneakertimer] = sneakertime; - - // set angle for spun out players: - player->kartstuff[k_boostangle] = (INT32)player->mo->angle; -} - -static void K_DoShrink(player_t *user) -{ - INT32 i; - mobj_t *mobj, *next; - - S_StartSound(user->mo, sfx_kc46); // Sound the BANG! - user->pflags |= PF_ATTACKDOWN; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - if (&players[i] == user) - continue; - if (players[i].kartstuff[k_position] < user->kartstuff[k_position]) - { - //P_FlashPal(&players[i], PAL_NUKE, 10); - - // Grow should get taken away. - if (players[i].kartstuff[k_growshrinktimer] > 0) - K_RemoveGrowShrink(&players[i]); - else - { - // Start shrinking! - K_DropItems(&players[i]); - players[i].kartstuff[k_growshrinktimer] = -(15*TICRATE); - - if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) - { - players[i].mo->scalespeed = mapobjectscale/TICRATE; - players[i].mo->destscale = (6*mapobjectscale)/8; - if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot) - players[i].mo->destscale = (6*players[i].mo->destscale)/8; - S_StartSound(players[i].mo, sfx_kc59); - } - } - } - } - - // kill everything in the kitem list while we're at it: - for (mobj = kitemcap; mobj; mobj = next) - { - next = mobj->itnext; - - // check if the item is being held by a player behind us before removing it. - // check if the item is a "shield" first, bc i'm p sure thrown items keep the player that threw em as target anyway - - if (mobj->type == MT_BANANA_SHIELD || mobj->type == MT_JAWZ_SHIELD || - mobj->type == MT_SSMINE_SHIELD || mobj->type == MT_EGGMANITEM_SHIELD || - mobj->type == MT_SINK_SHIELD || mobj->type == MT_ORBINAUT_SHIELD) - { - if (mobj->target && mobj->target->player) - { - if (mobj->target->player->kartstuff[k_position] > user->kartstuff[k_position]) - continue; // this guy's behind us, don't take his stuff away! - } - } - - mobj->destscale = 0; - mobj->flags &= ~(MF_SOLID|MF_SHOOTABLE|MF_SPECIAL); - mobj->flags |= MF_NOCLIPTHING; // Just for safety - - if (mobj->type == MT_SPB) - spbplace = -1; - } -} - - -void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) -{ - const fixed_t vscale = mapobjectscale + (mo->scale - mapobjectscale); - - if (mo->player && mo->player->spectator) - return; - - if (mo->eflags & MFE_SPRUNG) - return; - -#ifdef ESLOPE - mo->standingslope = NULL; -#endif - - mo->eflags |= MFE_SPRUNG; - - if (mo->eflags & MFE_VERTICALFLIP) - vertispeed *= -1; - - if (vertispeed == 0) - { - fixed_t thrust; - - if (mo->player) - { - thrust = 3*mo->player->speed/2; - if (thrust < 48< 72<player->kartstuff[k_pogospring] != 2) - { - if (mo->player->kartstuff[k_sneakertimer]) - thrust = FixedMul(thrust, (5*FRACUNIT)/4); - else if (mo->player->kartstuff[k_invincibilitytimer]) - thrust = FixedMul(thrust, (9*FRACUNIT)/8); - } - } - else - { - thrust = FixedDiv(3*P_AproxDistance(mo->momx, mo->momy)/2, 5*FRACUNIT/2); - if (thrust < 16< 32<momz = P_MobjFlip(mo)*FixedMul(FINESINE(ANGLE_22h>>ANGLETOFINESHIFT), FixedMul(thrust, vscale)); - } - else - mo->momz = FixedMul(vertispeed, vscale); - - if (mo->eflags & MFE_UNDERWATER) - mo->momz = (117 * mo->momz) / 200; - - if (sound) - S_StartSound(mo, (sound == 1 ? sfx_kc2f : sfx_kpogos)); -} - -void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source) -{ - mobj_t *cachenext; - -killnext: - cachenext = banana->hnext; - - if (banana->health) - { - if (banana->eflags & MFE_VERTICALFLIP) - banana->z -= banana->height; - else - banana->z += banana->height; - - S_StartSound(banana, banana->info->deathsound); - P_KillMobj(banana, inflictor, source); - - P_SetObjectMomZ(banana, 8*FRACUNIT, false); - if (inflictor) - P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT); - } - - if ((banana = cachenext)) - goto killnext; -} - -// Just for firing/dropping items. -void K_UpdateHnextList(player_t *player, boolean clean) -{ - mobj_t *work = player->mo, *nextwork; - - if (!work) - return; - - nextwork = work->hnext; - - while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) - { - nextwork = work->hnext; - - if (!clean && (!work->movedir || work->movedir <= (UINT16)player->kartstuff[k_itemamount])) - { - continue; - } - - P_RemoveMobj(work); - } - - if (player->mo->hnext == NULL || P_MobjWasRemoved(player->mo->hnext)) - { - // Like below, try to clean up the pointer if it's NULL. - // Maybe this was a cause of the shrink/eggbox fails? - P_SetTarget(&player->mo->hnext, NULL); - } -} - -// For getting hit! -void K_DropHnextList(player_t *player, boolean keepshields) -{ - mobj_t *work = player->mo, *nextwork, *dropwork; - INT32 flip; - mobjtype_t type; - boolean orbit, ponground, dropall = true; - INT32 shield = K_GetShieldFromItem(player->kartstuff[k_itemtype]); - - if (work == NULL || P_MobjWasRemoved(work)) - { - return; - } - - flip = P_MobjFlip(player->mo); - ponground = P_IsObjectOnGround(player->mo); - - if (shield != KSHIELD_NONE && !keepshields) - { - if (shield == KSHIELD_THUNDER) - { - K_DoThunderShield(player); - } - - player->kartstuff[k_curshield] = KSHIELD_NONE; - player->kartstuff[k_itemtype] = KITEM_NONE; - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - } - - nextwork = work->hnext; - - while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) - { - nextwork = work->hnext; - - switch (work->type) - { - // Kart orbit items - case MT_ORBINAUT_SHIELD: - orbit = true; - type = MT_ORBINAUT; - break; - case MT_JAWZ_SHIELD: - orbit = true; - type = MT_JAWZ_DUD; - break; - // Kart trailing items - case MT_BANANA_SHIELD: - orbit = false; - type = MT_BANANA; - break; - case MT_SSMINE_SHIELD: - orbit = false; - dropall = false; - type = MT_SSMINE; - break; - case MT_EGGMANITEM_SHIELD: - orbit = false; - type = MT_EGGMANITEM; - break; - // intentionally do nothing - case MT_ROCKETSNEAKER: - case MT_SINK_SHIELD: - return; - default: - continue; - } - - dropwork = P_SpawnMobj(work->x, work->y, work->z, type); - - P_SetTarget(&dropwork->target, player->mo); - P_AddKartItem(dropwork); // needs to be called here so shrink can bust items off players in front of the user. - - dropwork->angle = work->angle; - - dropwork->flags |= MF_NOCLIPTHING; - dropwork->flags2 = work->flags2; - - dropwork->floorz = work->floorz; - dropwork->ceilingz = work->ceilingz; - - if (ponground) - { - // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn - // This should set it for FOFs - //P_TeleportMove(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing - - if (flip == 1) - { - if (dropwork->floorz > dropwork->target->z - dropwork->height) - { - dropwork->z = dropwork->floorz; - } - } - else - { - if (dropwork->ceilingz < dropwork->target->z + dropwork->target->height + dropwork->height) - { - dropwork->z = dropwork->ceilingz - dropwork->height; - } - } - } - - if (orbit) // splay out - { - dropwork->flags2 |= MF2_AMBUSH; - - dropwork->z += flip; - - dropwork->momx = player->mo->momx>>1; - dropwork->momy = player->mo->momy>>1; - dropwork->momz = 3*flip*mapobjectscale; - - if (dropwork->eflags & MFE_UNDERWATER) - dropwork->momz = (117 * dropwork->momz) / 200; - - P_Thrust(dropwork, work->angle - ANGLE_90, 6*mapobjectscale); - - dropwork->movecount = 2; - dropwork->movedir = work->angle - ANGLE_90; - - P_SetMobjState(dropwork, dropwork->info->deathstate); - - dropwork->tics = -1; - - if (type == MT_JAWZ_DUD) - { - dropwork->z += 20*flip*dropwork->scale; - } - else - { - dropwork->color = work->color; - dropwork->angle -= ANGLE_90; - } - } - else // plop on the ground - { - dropwork->flags &= ~MF_NOCLIPTHING; - dropwork->threshold = 10; - } - - P_RemoveMobj(work); - } - - // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... - P_SetTarget(&player->mo->hnext, NULL); - - player->kartstuff[k_bananadrag] = 0; - - if (player->kartstuff[k_eggmanheld]) - { - player->kartstuff[k_eggmanheld] = 0; - } - else if (player->kartstuff[k_itemheld] - && (dropall || (--player->kartstuff[k_itemamount] <= 0))) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } -} - -// For getting EXTRA hit! -void K_DropItems(player_t *player) -{ - K_DropHnextList(player, true); - - 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]; - - drop->flags |= MF_NOCLIPTHING; - } - - K_StripItems(player); -} - -// When an item in the hnext chain dies. -void K_RepairOrbitChain(mobj_t *orbit) -{ - mobj_t *cachenext = orbit->hnext; - - // First, repair the chain - if (orbit->hnext && orbit->hnext->health && !P_MobjWasRemoved(orbit->hnext)) - { - P_SetTarget(&orbit->hnext->hprev, orbit->hprev); - P_SetTarget(&orbit->hnext, NULL); - } - - if (orbit->hprev && orbit->hprev->health && !P_MobjWasRemoved(orbit->hprev)) - { - P_SetTarget(&orbit->hprev->hnext, cachenext); - P_SetTarget(&orbit->hprev, NULL); - } - - // Then recount to make sure item amount is correct - if (orbit->target && orbit->target->player) - { - INT32 num = 0; - - mobj_t *cur = orbit->target->hnext; - mobj_t *prev = NULL; - - while (cur && !P_MobjWasRemoved(cur)) - { - prev = cur; - cur = cur->hnext; - if (++num > orbit->target->player->kartstuff[k_itemamount]) - P_RemoveMobj(prev); - else - prev->movedir = num; - } - - if (orbit->target->player->kartstuff[k_itemamount] != num) - orbit->target->player->kartstuff[k_itemamount] = num; - } -} - -// Simplified version of a code bit in P_MobjFloorZ -static fixed_t K_BananaSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) -{ - fixed_t testx, testy; - - if (slope->d.x < 0) - testx = radius; - else - testx = -radius; - - if (slope->d.y < 0) - testy = radius; - else - testy = -radius; - - if ((slope->zdelta > 0) ^ !!(ceiling)) - { - testx = -testx; - testy = -testy; - } - - testx += x; - testy += y; - - return P_GetZAt(slope, testx, testy); -} - -static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) -{ - fixed_t newz; - sector_t *sec; -#ifdef ESLOPE - pslope_t *slope = NULL; -#endif - - sec = R_PointInSubsector(x, y)->sector; - - if (flip) - { -#ifdef ESLOPE - if (sec->c_slope) - { - slope = sec->c_slope; - newz = K_BananaSlopeZ(slope, x, y, radius, true); - } - else -#endif - newz = sec->ceilingheight; - } - else - { -#ifdef ESLOPE - if (sec->f_slope) - { - slope = sec->f_slope; - newz = K_BananaSlopeZ(slope, x, y, radius, false); - } - else -#endif - newz = sec->floorheight; - } - - // Check FOFs for a better suited slope - if (sec->ffloors) - { - ffloor_t *rover; - - for (rover = sec->ffloors; rover; rover = rover->next) - { - fixed_t top, bottom; - fixed_t d1, d2; - - if (!(rover->flags & FF_EXISTS)) - continue; - - if ((!(((rover->flags & FF_BLOCKPLAYER && player) - || (rover->flags & FF_BLOCKOTHERS && !player)) - || (rover->flags & FF_QUICKSAND)) - || (rover->flags & FF_SWIMMABLE))) - continue; - -#ifdef ESLOPE - if (*rover->t_slope) - top = K_BananaSlopeZ(*rover->t_slope, x, y, radius, false); - else -#endif - top = *rover->topheight; - -#ifdef ESLOPE - if (*rover->b_slope) - bottom = K_BananaSlopeZ(*rover->b_slope, x, y, radius, true); - else -#endif - bottom = *rover->bottomheight; - - if (flip) - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz > (z + height)) - { - newz = (z + height); - slope = NULL; - } - } - continue; - } - - d1 = (z + height) - (top + ((bottom - top)/2)); - d2 = z - (top + ((bottom - top)/2)); - - if (bottom < newz && abs(d1) < abs(d2)) - { - newz = bottom; -#ifdef ESLOPE - if (*rover->b_slope) - slope = *rover->b_slope; -#endif - } - } - else - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz < z) - { - newz = z; - slope = NULL; - } - } - continue; - } - - d1 = z - (bottom + ((top - bottom)/2)); - d2 = (z + height) - (bottom + ((top - bottom)/2)); - - if (top > newz && abs(d1) < abs(d2)) - { - newz = top; -#ifdef ESLOPE - if (*rover->t_slope) - slope = *rover->t_slope; -#endif - } - } - } - } - -#if 0 - mobj->standingslope = slope; -#endif - -#ifdef HWRENDER - mobj->modeltilt = slope; -#endif -} - -// Move the hnext chain! -static void K_MoveHeldObjects(player_t *player) -{ - if (!player->mo) - return; - - if (!player->mo->hnext) - { - player->kartstuff[k_bananadrag] = 0; - if (player->kartstuff[k_eggmanheld]) - player->kartstuff[k_eggmanheld] = 0; - else if (player->kartstuff[k_itemheld]) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } - return; - } - - if (P_MobjWasRemoved(player->mo->hnext)) - { - // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... - P_SetTarget(&player->mo->hnext, NULL); - player->kartstuff[k_bananadrag] = 0; - if (player->kartstuff[k_eggmanheld]) - player->kartstuff[k_eggmanheld] = 0; - else if (player->kartstuff[k_itemheld]) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } - return; - } - - switch (player->mo->hnext->type) - { - case MT_ORBINAUT_SHIELD: // Kart orbit items - case MT_JAWZ_SHIELD: - { - mobj_t *cur = player->mo->hnext; - fixed_t speed = ((8 - min(4, player->kartstuff[k_itemamount])) * cur->info->speed) / 7; - - player->kartstuff[k_bananadrag] = 0; // Just to make sure - - while (cur && !P_MobjWasRemoved(cur)) - { - const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); // mobj's distance from its Target, or Radius. - fixed_t z; - - if (!cur->health) - { - cur = cur->hnext; - continue; - } - - cur->color = player->skincolor; - - cur->angle -= ANGLE_90; - cur->angle += FixedAngle(speed); - - if (cur->extravalue1 < radius) - cur->extravalue1 += P_AproxDistance(cur->extravalue1, radius) / 12; - if (cur->extravalue1 > radius) - cur->extravalue1 = radius; - - // If the player is on the ceiling, then flip your items as well. - if (player && player->mo->eflags & MFE_VERTICALFLIP) - cur->eflags |= MFE_VERTICALFLIP; - else - cur->eflags &= ~MFE_VERTICALFLIP; - - // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); - - if (P_MobjFlip(cur) > 0) - z = player->mo->z; - else - z = player->mo->z + player->mo->height - cur->height; - - cur->flags |= MF_NOCLIPTHING; // temporarily make them noclip other objects so they can't hit anyone while in the player - P_TeleportMove(cur, player->mo->x, player->mo->y, z); - cur->momx = FixedMul(FINECOSINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); - cur->momy = FixedMul(FINESINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); - cur->flags &= ~MF_NOCLIPTHING; - if (!P_TryMove(cur, player->mo->x + cur->momx, player->mo->y + cur->momy, true)) - P_SlideMove(cur, true); - if (P_IsObjectOnGround(player->mo)) - { - if (P_MobjFlip(cur) > 0) - { - if (cur->floorz > player->mo->z - cur->height) - z = cur->floorz; - } - else - { - if (cur->ceilingz < player->mo->z + player->mo->height + cur->height) - z = cur->ceilingz - cur->height; - } - } - - // Center it during the scale up animation - z += (FixedMul(mobjinfo[cur->type].height, player->mo->scale - cur->scale)>>1) * P_MobjFlip(cur); - - cur->z = z; - cur->momx = cur->momy = 0; - cur->angle += ANGLE_90; - - cur = cur->hnext; - } - } - break; - case MT_BANANA_SHIELD: // Kart trailing items - case MT_SSMINE_SHIELD: - case MT_EGGMANITEM_SHIELD: - case MT_SINK_SHIELD: - { - mobj_t *cur = player->mo->hnext; - mobj_t *targ = player->mo; - - if (P_IsObjectOnGround(player->mo) && player->speed > 0) - player->kartstuff[k_bananadrag]++; - - while (cur && !P_MobjWasRemoved(cur)) - { - const fixed_t radius = FixedHypot(targ->radius, targ->radius) + FixedHypot(cur->radius, cur->radius); - angle_t ang; - fixed_t targx, targy, targz; - fixed_t speed, dist; - - if (cur->type == MT_EGGMANITEM_SHIELD) - { - // Decided that this should use their "canon" color. - cur->color = SKINCOLOR_BLACK; - } - - cur->flags &= ~MF_NOCLIPTHING; - - if (!cur->health) - { - cur = cur->hnext; - continue; - } - - if (cur->extravalue1 < radius) - cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); - if (cur->extravalue1 > radius) - cur->extravalue1 = radius; - - if (cur != player->mo->hnext) - { - targ = cur->hprev; - dist = cur->extravalue1/4; - } - else - dist = cur->extravalue1/2; - - if (!targ || P_MobjWasRemoved(targ)) - continue; - - // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); - - ang = targ->angle; - targx = targ->x + P_ReturnThrustX(cur, ang + ANGLE_180, dist); - targy = targ->y + P_ReturnThrustY(cur, ang + ANGLE_180, dist); - targz = targ->z; - - speed = FixedMul(R_PointToDist2(cur->x, cur->y, targx, targy), 3*FRACUNIT/4); - if (P_IsObjectOnGround(targ)) - targz = cur->floorz; - - cur->angle = R_PointToAngle2(cur->x, cur->y, targx, targy); - - /*if (P_IsObjectOnGround(player->mo) && player->speed > 0 && player->kartstuff[k_bananadrag] > TICRATE - && P_RandomChance(min(FRACUNIT/2, FixedDiv(player->speed, K_GetKartSpeed(player, false))/2))) - { - if (leveltime & 1) - targz += 8*(2*FRACUNIT)/7; - else - targz -= 8*(2*FRACUNIT)/7; - }*/ - - if (speed > dist) - P_InstaThrust(cur, cur->angle, speed-dist); - - P_SetObjectMomZ(cur, FixedMul(targz - cur->z, 7*FRACUNIT/8) - gravity, false); - - if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT) - P_TeleportMove(cur, targx, targy, cur->z); - -#ifdef ESLOPE - if (P_IsObjectOnGround(cur)) - { - K_CalculateBananaSlope(cur, cur->x, cur->y, cur->z, - cur->radius, cur->height, (cur->eflags & MFE_VERTICALFLIP), false); - } -#endif - - cur = cur->hnext; - } - } - break; - case MT_ROCKETSNEAKER: // Special rocket sneaker stuff - { - mobj_t *cur = player->mo->hnext; - INT32 num = 0; - - while (cur && !P_MobjWasRemoved(cur)) - { - const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); - boolean vibrate = ((leveltime & 1) && !cur->tracer); - angle_t angoffset; - fixed_t targx, targy, targz; - - cur->flags &= ~MF_NOCLIPTHING; - - if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1)) - cur->flags2 |= MF2_DONTDRAW; - else - cur->flags2 &= ~MF2_DONTDRAW; - - if (num & 1) - P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L)); - else - P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_RVIBRATE : S_ROCKETSNEAKER_R)); - - if (!player->kartstuff[k_rocketsneakertimer] || cur->extravalue2 || !cur->health) - { - num = (num+1) % 2; - cur = cur->hnext; - continue; - } - - if (cur->extravalue1 < radius) - cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); - if (cur->extravalue1 > radius) - cur->extravalue1 = radius; - - // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); - -#if 1 - { - angle_t input = player->frameangle - cur->angle; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - input = FixedAngle(AngleFixed(input)/4); - if (invert) - input = InvAngle(input); - - cur->angle = cur->angle + input; - } -#else - cur->angle = player->frameangle; -#endif - - angoffset = ANGLE_90 + (ANGLE_180 * num); - - targx = player->mo->x + P_ReturnThrustX(cur, cur->angle + angoffset, cur->extravalue1); - targy = player->mo->y + P_ReturnThrustY(cur, cur->angle + angoffset, cur->extravalue1); - - { // bobbing, copy pasted from my kimokawaiii entry - const fixed_t pi = (22<mo->scale, 8 * FINESINE((((2*pi*(4*TICRATE)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK)); - targz = (player->mo->z + (player->mo->height/2)) + sine; - if (player->mo->eflags & MFE_VERTICALFLIP) - targz += (player->mo->height/2 - 32*player->mo->scale)*6; - - } - - if (cur->tracer) - { - fixed_t diffx, diffy, diffz; - - diffx = targx - cur->x; - diffy = targy - cur->y; - diffz = targz - cur->z; - - P_TeleportMove(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale), - cur->tracer->y + diffy + P_ReturnThrustY(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->z + diffz); - P_SetScale(cur->tracer, (cur->tracer->destscale = 3*cur->scale/4)); - } - - P_TeleportMove(cur, targx, targy, targz); - K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. -#ifdef HWRENDER - cur->modeltilt = player->mo->modeltilt; -#endif - num = (num+1) % 2; - cur = cur->hnext; - } - } - break; - default: - break; - } -} - -player_t *K_FindJawzTarget(mobj_t *actor, player_t *source) -{ - fixed_t best = -1; - player_t *wtarg = NULL; - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - angle_t thisang; - player_t *player; - - if (!playeringame[i]) - continue; - - player = &players[i]; - - if (player->spectator) - continue; // spectator - - if (!player->mo) - continue; - - if (player->mo->health <= 0) - continue; // dead - - // Don't target yourself, stupid. - if (player == source) - continue; - - // Don't home in on teammates. - if (G_GametypeHasTeams() && source->ctfteam == player->ctfteam) - continue; - - // Invisible, don't bother - if (player->kartstuff[k_hyudorotimer]) - continue; - - // Find the angle, see who's got the best. - thisang = actor->angle - R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y); - if (thisang > ANGLE_180) - thisang = InvAngle(thisang); - - // Jawz only go after the person directly ahead of you in race... sort of literally now! - if (G_RaceGametype()) - { - // Don't go for people who are behind you - if (thisang > ANGLE_67h) - continue; - // Don't pay attention to people who aren't above your position - if (player->kartstuff[k_position] >= source->kartstuff[k_position]) - continue; - if ((best == -1) || (player->kartstuff[k_position] > best)) - { - wtarg = player; - best = player->kartstuff[k_position]; - } - } - else - { - fixed_t thisdist; - fixed_t thisavg; - - // Don't go for people who are behind you - if (thisang > ANGLE_45) - continue; - - // Don't pay attention to dead players - if (player->kartstuff[k_bumper] <= 0) - continue; - - // Z pos too high/low - if (abs(player->mo->z - (actor->z + actor->momz)) > RING_DIST/8) - continue; - - thisdist = P_AproxDistance(player->mo->x - (actor->x + actor->momx), player->mo->y - (actor->y + actor->momy)); - - if (thisdist > 2*RING_DIST) // Don't go for people who are too far away - continue; - - thisavg = (AngleFixed(thisang) + thisdist) / 2; - - //CONS_Printf("got avg %d from player # %d\n", thisavg>>FRACBITS, i); - - if ((best == -1) || (thisavg < best)) - { - wtarg = player; - best = thisavg; - } - } - } - - return wtarg; -} - -// Engine Sounds. -static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) -{ - const INT32 numsnds = 13; - INT32 class, s, w; // engine class number - UINT8 volume = 255; - fixed_t volumedampen = 0; - INT32 targetsnd = 0; - INT32 i; - - s = (player->kartspeed-1)/3; - w = (player->kartweight-1)/3; - -#define LOCKSTAT(stat) \ - if (stat < 0) { stat = 0; } \ - if (stat > 2) { stat = 2; } - LOCKSTAT(s); - LOCKSTAT(w); -#undef LOCKSTAT - - class = s+(3*w); - - // Silence the engines - if (leveltime < 8 || player->spectator) - { - player->karthud[khud_enginesnd] = 0; // Reset sound number - return; - } - -#if 0 - if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct! -#else - if (leveltime % 8) // .25 seconds of wait time between engine sounds -#endif - return; - - if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->respawn.state == RESPAWNST_DROP)) // Startup boosts - targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0); - else - targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2; - - if (targetsnd < 0) - targetsnd = 0; - if (targetsnd > 12) - targetsnd = 12; - - if (player->karthud[khud_enginesnd] < targetsnd) - player->karthud[khud_enginesnd]++; - if (player->karthud[khud_enginesnd] > targetsnd) - player->karthud[khud_enginesnd]--; - - if (player->karthud[khud_enginesnd] < 0) - player->karthud[khud_enginesnd] = 0; - if (player->karthud[khud_enginesnd] > 12) - player->karthud[khud_enginesnd] = 12; - - for (i = 0; i < MAXPLAYERS; i++) - { - UINT8 thisvol = 0; - fixed_t dist; - - if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting) - continue; - - if (P_IsDisplayPlayer(&players[i])) - { - volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time. - continue; - } - - dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x, - player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2; - - dist = FixedDiv(dist, mapobjectscale); - - if (dist > 1536<>FRACBITS)) / (((1536<>(FRACBITS+4)); - - if (thisvol == 0) - continue; - - volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT - } - - if (volumedampen > FRACUNIT) - volume = FixedDiv(volume<>FRACBITS; - - if (volume <= 0) // Might as well - return; - - S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->karthud[khud_enginesnd]) + (class*numsnds), volume); -} - -static void K_UpdateInvincibilitySounds(player_t *player) -{ - INT32 sfxnum = sfx_None; - - if (player->mo->health > 0 && !P_IsDisplayPlayer(player)) - { - if (cv_kartinvinsfx.value) - { - if (player->kartstuff[k_invincibilitytimer] > 0) // Prioritize invincibility - sfxnum = sfx_alarmi; - else if (player->kartstuff[k_growshrinktimer] > 0) - sfxnum = sfx_alarmg; - } - else - { - if (player->kartstuff[k_invincibilitytimer] > 0) - sfxnum = sfx_kinvnc; - else if (player->kartstuff[k_growshrinktimer] > 0) - sfxnum = sfx_kgrow; - } - } - - if (sfxnum != sfx_None && !S_SoundPlaying(player->mo, sfxnum)) - S_StartSound(player->mo, sfxnum); - -#define STOPTHIS(this) \ - if (sfxnum != this && S_SoundPlaying(player->mo, this)) \ - S_StopSoundByID(player->mo, this); - STOPTHIS(sfx_alarmi); - STOPTHIS(sfx_alarmg); - STOPTHIS(sfx_kinvnc); - STOPTHIS(sfx_kgrow); -#undef STOPTHIS -} - -#define RINGANIM_NUMFRAMES 10 -#define RINGANIM_DELAYMAX 5 - -void K_KartPlayerHUDUpdate(player_t *player) -{ - if (player->karthud[khud_lapanimation]) - player->karthud[khud_lapanimation]--; - - if (player->karthud[khud_yougotem]) - player->karthud[khud_yougotem]--; - - if (player->karthud[khud_voices]) - player->karthud[khud_voices]--; - - if (player->karthud[khud_tauntvoices]) - player->karthud[khud_tauntvoices]--; - - if (G_RaceGametype()) - { - // 0 is the fast spin animation, set at 30 tics of ring boost or higher! - if (player->kartstuff[k_ringboost] >= 30) - player->karthud[khud_ringdelay] = 0; - else - player->karthud[khud_ringdelay] = ((RINGANIM_DELAYMAX+1) * (30 - player->kartstuff[k_ringboost])) / 30; - - if (player->karthud[khud_ringframe] == 0 && player->karthud[khud_ringdelay] > RINGANIM_DELAYMAX) - { - player->karthud[khud_ringframe] = 0; - player->karthud[khud_ringtics] = 0; - } - else if ((player->karthud[khud_ringtics]--) <= 0) - { - if (player->karthud[khud_ringdelay] == 0) // fast spin animation - { - player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+2) % RINGANIM_NUMFRAMES); - player->karthud[khud_ringtics] = 0; - } - else - { - player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+1) % RINGANIM_NUMFRAMES); - player->karthud[khud_ringtics] = min(RINGANIM_DELAYMAX, player->karthud[khud_ringdelay])-1; - } - } - - if (player->kartstuff[k_ringlock]) - { - UINT8 normalanim = (leveltime % 14); - UINT8 debtanim = 14 + (leveltime % 2); - - if (player->karthud[khud_ringspblock] >= 14) // debt animation - { - if ((player->kartstuff[k_rings] > 0) // Get out of 0 ring animation - && (normalanim == 3 || normalanim == 10)) // on these transition frames. - player->karthud[khud_ringspblock] = normalanim; - else - player->karthud[khud_ringspblock] = debtanim; - } - else // normal animation - { - if ((player->kartstuff[k_rings] <= 0) // Go into 0 ring animation - && (player->karthud[khud_ringspblock] == 1 || player->karthud[khud_ringspblock] == 8)) // on these transition frames. - player->karthud[khud_ringspblock] = debtanim; - else - player->karthud[khud_ringspblock] = normalanim; - } - } - else - player->karthud[khud_ringspblock] = (leveltime % 14); // reset to normal anim next time - } - - if (G_BattleGametype() && (player->exiting || player->kartstuff[k_comebacktimer])) - { - if (player->exiting) - { - if (player->exiting < 6*TICRATE) - player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; - else if (player->exiting == 6*TICRATE) - player->karthud[khud_cardanimation] = 0; - else if (player->karthud[khud_cardanimation] < 2*TICRATE) - player->karthud[khud_cardanimation]++; - } - else - { - if (player->kartstuff[k_comebacktimer] < 6*TICRATE) - player->karthud[khud_cardanimation] -= ((164-player->karthud[khud_cardanimation])/8)+1; - else if (player->kartstuff[k_comebacktimer] < 9*TICRATE) - player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; - } - - if (player->karthud[khud_cardanimation] > 164) - player->karthud[khud_cardanimation] = 164; - if (player->karthud[khud_cardanimation] < 0) - player->karthud[khud_cardanimation] = 0; - } - else if (G_RaceGametype() && player->exiting) - { - if (player->karthud[khud_cardanimation] < 2*TICRATE) - player->karthud[khud_cardanimation]++; - } - else - player->karthud[khud_cardanimation] = 0; -} - -#undef RINGANIM_DELAYMAX - -// SRB2Kart: blockmap iterate for attraction shield users -static mobj_t *attractmo; -static fixed_t attractdist; -static inline boolean PIT_AttractingRings(mobj_t *thing) -{ - if (!attractmo || P_MobjWasRemoved(attractmo)) - return false; - - if (!attractmo->player) - return false; // not a player - - if (thing->health <= 0 || !thing) - return true; // dead - - if (thing->type != MT_RING && thing->type != MT_FLINGRING) - return true; // not a ring - - if (thing->extravalue1) - return true; // in special ring animation - - if (thing->cusval) - return true; // already attracted - - // see if it went over / under - if (attractmo->z - (attractdist>>2) > thing->z + thing->height) - return true; // overhead - if (attractmo->z + attractmo->height + (attractdist>>2) < thing->z) - return true; // underneath - - if (P_AproxDistance(attractmo->x - thing->x, attractmo->y - thing->y) < attractdist) - return true; // Too far away - - // set target - P_SetTarget(&thing->tracer, attractmo); - // flag to show it's been attracted once before - thing->cusval = 1; - return true; // find other rings -} - -/** Looks for rings near a player in the blockmap. - * - * \param pmo Player object looking for rings to attract - * \sa A_AttractChase - */ -static void K_LookForRings(mobj_t *pmo) -{ - INT32 bx, by, xl, xh, yl, yh; - attractdist = FixedMul(RING_DIST, pmo->scale)>>2; - - // Use blockmap to check for nearby rings - yh = (unsigned)(pmo->y + attractdist - bmaporgy)>>MAPBLOCKSHIFT; - yl = (unsigned)(pmo->y - attractdist - bmaporgy)>>MAPBLOCKSHIFT; - xh = (unsigned)(pmo->x + attractdist - bmaporgx)>>MAPBLOCKSHIFT; - xl = (unsigned)(pmo->x - attractdist - bmaporgx)>>MAPBLOCKSHIFT; - - attractmo = pmo; - - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) - P_BlockThingsIterator(bx, by, PIT_AttractingRings); -} - -/** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c - - \param player player object passed from P_PlayerThink - \param cmd control input from player - - \return void -*/ -void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) -{ - K_UpdateOffroad(player); - K_UpdateDraft(player); - K_UpdateEngineSounds(player, cmd); // Thanks, VAda! - - // update boost angle if not spun out - if (!player->kartstuff[k_spinouttimer] && !player->kartstuff[k_wipeoutslow]) - player->kartstuff[k_boostangle] = (INT32)player->mo->angle; - - K_GetKartBoostPower(player); - - // Special effect objects! - if (player->mo && !player->spectator) - { - if (player->kartstuff[k_dashpadcooldown]) // Twinkle Circuit afterimages - { - mobj_t *ghost; - ghost = P_SpawnGhostMobj(player->mo); - ghost->fuse = player->kartstuff[k_dashpadcooldown]+1; - ghost->momx = player->mo->momx / (player->kartstuff[k_dashpadcooldown]+1); - ghost->momy = player->mo->momy / (player->kartstuff[k_dashpadcooldown]+1); - ghost->momz = player->mo->momz / (player->kartstuff[k_dashpadcooldown]+1); - player->kartstuff[k_dashpadcooldown]--; - } - - if (player->speed > 0) - { - // Speed lines - if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_ringboost] - || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost] - || player->kartstuff[k_eggmanexplode]) - { - mobj_t *fast = P_SpawnMobj(player->mo->x + (P_RandomRange(-36,36) * player->mo->scale), - player->mo->y + (P_RandomRange(-36,36) * player->mo->scale), - player->mo->z + (player->mo->height/2) + (P_RandomRange(-20,20) * player->mo->scale), - MT_FASTLINE); - - P_SetTarget(&fast->target, player->mo); - fast->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - fast->momx = 3*player->mo->momx/4; - fast->momy = 3*player->mo->momy/4; - fast->momz = 3*player->mo->momz/4; - - K_MatchGenericExtraFlags(fast, player->mo); - - // Make it red when you have the eggman speed boost - if (player->kartstuff[k_eggmanexplode]) - { - fast->color = SKINCOLOR_RED; - fast->colorized = true; - } - } - - if (player->kartstuff[k_numboosts] > 0) // Boosting after images - { - mobj_t *ghost; - ghost = P_SpawnGhostMobj(player->mo); - ghost->extravalue1 = player->kartstuff[k_numboosts]+1; - ghost->extravalue2 = (leveltime % ghost->extravalue1); - ghost->fuse = ghost->extravalue1; - ghost->frame |= FF_FULLBRIGHT; - ghost->colorized = true; - //ghost->color = player->skincolor; - //ghost->momx = (3*player->mo->momx)/4; - //ghost->momy = (3*player->mo->momy)/4; - //ghost->momz = (3*player->mo->momz)/4; - if (leveltime & 1) - ghost->flags2 |= MF2_DONTDRAW; - } - - if (P_IsObjectOnGround(player->mo)) - { - // Offroad dust - if (player->kartstuff[k_boostpower] < FRACUNIT) - { - K_SpawnWipeoutTrail(player->mo, true); - if (leveltime % 6 == 0) - S_StartSound(player->mo, sfx_cdfm70); - } - - // Draft dust - if (player->kartstuff[k_draftpower] >= FRACUNIT) - { - K_SpawnDraftDust(player->mo); - /*if (leveltime % 23 == 0 || !S_SoundPlaying(player->mo, sfx_s265)) - S_StartSound(player->mo, sfx_s265);*/ - } - } - } - - if (G_RaceGametype() && player->kartstuff[k_rings] <= 0) // spawn ring debt indicator - { - mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, - player->mo->z + player->mo->momz + player->mo->height + (24*player->mo->scale), MT_THOK); - P_SetMobjState(debtflag, S_RINGDEBT); - P_SetScale(debtflag, (debtflag->destscale = player->mo->scale)); - K_MatchGenericExtraFlags(debtflag, player->mo); - debtflag->frame += (leveltime % 4); - if ((leveltime/12) & 1) - debtflag->frame += 4; - debtflag->color = player->skincolor; - debtflag->fuse = 2; - if (P_IsDisplayPlayer(player)) - debtflag->flags2 |= MF2_DONTDRAW; - } - - if (player->kartstuff[k_springstars] && (leveltime & 1)) - { - fixed_t randx = P_RandomRange(-40, 40) * player->mo->scale; - fixed_t randy = P_RandomRange(-40, 40) * player->mo->scale; - fixed_t randz = P_RandomRange(0, player->mo->height >> FRACBITS) << FRACBITS; - mobj_t *star = P_SpawnMobj( - player->mo->x + randx, - player->mo->y + randy, - player->mo->z + randz, - MT_KARMAFIREWORK); - - star->color = player->kartstuff[k_springcolor]; - star->flags |= MF_NOGRAVITY; - star->momx = player->mo->momx / 2; - star->momy = player->mo->momy / 2; - star->momz = player->mo->momz / 2; - star->fuse = 12; - star->scale = player->mo->scale; - star->destscale = star->scale / 2; - - player->kartstuff[k_springstars]--; - } - } - - if (player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Ensure these are set correctly here - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - else if (player->kartstuff[k_eggmanexplode]) // You're gonna diiiiie - { - const INT32 flashtime = 4<<(player->kartstuff[k_eggmanexplode]/TICRATE); - if (player->kartstuff[k_eggmanexplode] == 1 || (player->kartstuff[k_eggmanexplode] % (flashtime/2) != 0)) - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - else if (player->kartstuff[k_eggmanexplode] % flashtime == 0) - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_BLACK; - } - else - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_CRIMSON; - } - } - else if (player->kartstuff[k_invincibilitytimer]) // setting players to use the star colormap and spawning afterimages - { - player->mo->colorized = true; - } - else if (player->kartstuff[k_growshrinktimer]) // Ditto, for grow/shrink - { - if (player->kartstuff[k_growshrinktimer] % 5 == 0) - { - player->mo->colorized = true; - player->mo->color = (player->kartstuff[k_growshrinktimer] < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE); - } - else - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - } - else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie - { - const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); - if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - else if (player->kartstuff[k_killfield] % flashtime == 0) - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_BYZANTIUM; - } - else - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_RUBY; - } - } - else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting - { - player->mo->colorized = true; - } - else - { - player->mo->colorized = false; - } - - if (player->kartstuff[k_itemtype] == KITEM_NONE) - player->kartstuff[k_holdready] = 0; - - // DKR style camera for boosting - if (player->karthud[khud_boostcam] != 0 || player->karthud[khud_destboostcam] != 0) - { - if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam] - && player->karthud[khud_destboostcam] != 0) - { - player->karthud[khud_boostcam] += FRACUNIT/(TICRATE/4); - if (player->karthud[khud_boostcam] >= player->karthud[khud_destboostcam]) - player->karthud[khud_destboostcam] = 0; - } - else - { - player->karthud[khud_boostcam] -= FRACUNIT/TICRATE; - if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam]) - player->karthud[khud_boostcam] = player->karthud[khud_destboostcam] = 0; - } - //CONS_Printf("cam: %d, dest: %d\n", player->karthud[khud_boostcam], player->karthud[khud_destboostcam]); - } - - player->karthud[khud_timeovercam] = 0; - - // Specific hack because it insists on setting flashing tics during this anyway... - if (player->kartstuff[k_spinouttype] == 2) - { - player->powers[pw_flashing] = 0; - } - // Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations. - else if (player->kartstuff[k_spinouttimer] != 0 - || player->kartstuff[k_wipeoutslow] != 0 - || player->kartstuff[k_squishedtimer] != 0) - { - player->powers[pw_flashing] = K_GetKartFlashing(player); - } - else if (player->powers[pw_flashing] >= K_GetKartFlashing(player)) - { - player->powers[pw_flashing]--; - } - - if (player->kartstuff[k_spinouttimer]) - { - if ((P_IsObjectOnGround(player->mo) - || (player->kartstuff[k_spinouttype] != 0)) - && (!player->kartstuff[k_sneakertimer])) - { - player->kartstuff[k_spinouttimer]--; - if (player->kartstuff[k_wipeoutslow] > 1) - player->kartstuff[k_wipeoutslow]--; - // Actually, this caused more problems than it solved. Just make sure you set type before you spinout. Which K_SpinPlayer always does. - /*if (player->kartstuff[k_spinouttimer] == 0) - player->kartstuff[k_spinouttype] = 0;*/ // Reset type - } - } - else - { - 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->kartstuff[k_rings] > 20) - player->kartstuff[k_rings] = 20; - else if (player->kartstuff[k_rings] < -20) - player->kartstuff[k_rings] = -20; - - if (player->kartstuff[k_ringdelay]) - player->kartstuff[k_ringdelay]--; - - if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_squishedtimer]) - player->kartstuff[k_ringboost] = 0; - else if (player->kartstuff[k_ringboost]) - player->kartstuff[k_ringboost]--; - - if (player->kartstuff[k_sneakertimer]) - { - player->kartstuff[k_sneakertimer]--; - - if (player->kartstuff[k_sneakertimer] <= 0) - { - player->kartstuff[k_numsneakers] = 0; - } - } - - if (player->kartstuff[k_flamedash]) - player->kartstuff[k_flamedash]--; - - if (player->kartstuff[k_sneakertimer] && player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1) - player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; - - if (player->kartstuff[k_floorboost]) - player->kartstuff[k_floorboost]--; - - if (player->kartstuff[k_driftboost]) - player->kartstuff[k_driftboost]--; - - if (player->kartstuff[k_startboost]) - player->kartstuff[k_startboost]--; - - if (player->kartstuff[k_invincibilitytimer]) - player->kartstuff[k_invincibilitytimer]--; - - if ((player->respawn.state == RESPAWNST_NONE) && player->kartstuff[k_growshrinktimer] != 0) - { - if (player->kartstuff[k_growshrinktimer] > 0) - player->kartstuff[k_growshrinktimer]--; - if (player->kartstuff[k_growshrinktimer] < 0) - player->kartstuff[k_growshrinktimer]++; - - // Back to normal - if (player->kartstuff[k_growshrinktimer] == 0) - K_RemoveGrowShrink(player); - } - - if (player->kartstuff[k_superring]) - { - if (player->kartstuff[k_superring] % 3 == 0) - { - mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); - ring->extravalue1 = 1; // Ring collect animation timer - ring->angle = player->mo->angle; // animation angle - P_SetTarget(&ring->target, player->mo); // toucher for thinker - player->kartstuff[k_pickuprings]++; - if (player->kartstuff[k_superring] <= 3) - ring->cvmem = 1; // play caching when collected - } - player->kartstuff[k_superring]--; - } - - if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0 - && player->kartstuff[k_rocketsneakertimer]) - player->kartstuff[k_rocketsneakertimer]--; - - if (player->kartstuff[k_hyudorotimer]) - player->kartstuff[k_hyudorotimer]--; - - if (player->kartstuff[k_sadtimer]) - player->kartstuff[k_sadtimer]--; - - if (player->kartstuff[k_stealingtimer]) - player->kartstuff[k_stealingtimer]--; - - if (player->kartstuff[k_stolentimer]) - player->kartstuff[k_stolentimer]--; - - if (player->kartstuff[k_squishedtimer]) - { - player->kartstuff[k_squishedtimer]--; - - if ((player->kartstuff[k_squishedtimer] == 0) && !(player->pflags & PF_NOCLIP)) - { - player->mo->flags &= ~MF_NOCLIP; - } - } - - if (player->kartstuff[k_justbumped]) - player->kartstuff[k_justbumped]--; - - if (player->kartstuff[k_tiregrease]) - player->kartstuff[k_tiregrease]--; - - // This doesn't go in HUD update because it has potential gameplay ramifications - if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0) - { - player->karthud[khud_itemblinkmode] = 0; - player->karthud[khud_itemblink] = 0; - } - - K_KartPlayerHUDUpdate(player); - - if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 - && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] - && (player->respawn.state == RESPAWNST_DROP) && !player->powers[pw_flashing]) - { - player->kartstuff[k_wanted]++; - if (battleovertime.enabled >= 10*TICRATE) - { - if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) - { - player->kartstuff[k_killfield]++; - if (player->kartstuff[k_killfield] > 4*TICRATE) - { - K_SpinPlayer(player, NULL, 0, NULL, false); - //player->kartstuff[k_killfield] = 1; - } - } - else if (player->kartstuff[k_killfield] > 0) - player->kartstuff[k_killfield]--; - } - } - else if (player->kartstuff[k_killfield] > 0) - player->kartstuff[k_killfield]--; - - if (P_IsObjectOnGround(player->mo)) - player->kartstuff[k_waterskip] = 0; - - if (player->kartstuff[k_instashield]) - player->kartstuff[k_instashield]--; - - if (player->kartstuff[k_eggmanexplode]) - { - if (player->spectator || (G_BattleGametype() && !player->kartstuff[k_bumper])) - player->kartstuff[k_eggmanexplode] = 0; - else - { - player->kartstuff[k_eggmanexplode]--; - if (player->kartstuff[k_eggmanexplode] <= 0) - { - mobj_t *eggsexplode; - //player->powers[pw_flashing] = 0; - eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION); - if (player->kartstuff[k_eggmanblame] >= 0 - && player->kartstuff[k_eggmanblame] < MAXPLAYERS - && playeringame[player->kartstuff[k_eggmanblame]] - && !players[player->kartstuff[k_eggmanblame]].spectator - && players[player->kartstuff[k_eggmanblame]].mo) - P_SetTarget(&eggsexplode->target, players[player->kartstuff[k_eggmanblame]].mo); - } - } - } - - if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) - { - if (RINGTOTAL(player) < 20 && !player->kartstuff[k_ringlock]) - K_LookForRings(player->mo); - } - - if (player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) - { - if (player->kartstuff[k_bubblecool]) - player->kartstuff[k_bubblecool]--; - } - else - { - player->kartstuff[k_bubbleblowup] = 0; - player->kartstuff[k_bubblecool] = 0; - } - - if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) - { - if (player->kartstuff[k_flamedash]) - K_FlameDashLeftoverSmoke(player->mo); - } - - if (player->kartstuff[k_comebacktimer]) - player->kartstuff[k_comebackmode] = 0; - - if (P_IsObjectOnGround(player->mo) && player->kartstuff[k_pogospring]) - { - if (P_MobjFlip(player->mo)*player->mo->momz <= 0) - player->kartstuff[k_pogospring] = 0; - } - - if (cmd->buttons & BT_DRIFT) - player->kartstuff[k_jmp] = 1; - else - player->kartstuff[k_jmp] = 0; - - // Roulette Code - K_KartItemRoulette(player, cmd); - - // Handle invincibility sfx - K_UpdateInvincibilitySounds(player); // Also thanks, VAda! - - // Plays the music after the starting countdown. - if (P_IsLocalPlayer(player) && leveltime == (starttime + (TICRATE/2))) - { - S_ChangeMusic(mapmusname, mapmusflags, true); - S_ShowMusicCredit(); - } -} - -void K_KartPlayerAfterThink(player_t *player) -{ - if (player->kartstuff[k_curshield] - || player->kartstuff[k_invincibilitytimer] - || (player->kartstuff[k_growshrinktimer] != 0 && player->kartstuff[k_growshrinktimer] % 5 == 4)) // 4 instead of 0 because this is afterthink! - { - player->mo->frame |= FF_FULLBRIGHT; - } - else - { - if (!(player->mo->state->frame & FF_FULLBRIGHT)) - player->mo->frame &= ~FF_FULLBRIGHT; - } - - // Move held objects (Bananas, Orbinaut, etc) - K_MoveHeldObjects(player); - - // Jawz reticule (seeking) - if (player->kartstuff[k_itemtype] == KITEM_JAWZ && player->kartstuff[k_itemheld]) - { - INT32 lasttarg = player->kartstuff[k_lastjawztarget]; - player_t *targ; - mobj_t *ret; - - if (player->kartstuff[k_jawztargetdelay] && playeringame[lasttarg] && !players[lasttarg].spectator) - { - targ = &players[lasttarg]; - player->kartstuff[k_jawztargetdelay]--; - } - else - targ = K_FindJawzTarget(player->mo, player); - - if (!targ || !targ->mo || P_MobjWasRemoved(targ->mo)) - { - player->kartstuff[k_lastjawztarget] = -1; - player->kartstuff[k_jawztargetdelay] = 0; - return; - } - - ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE); - P_SetTarget(&ret->target, targ->mo); - ret->frame |= ((leveltime % 10) / 2); - ret->tics = 1; - ret->color = player->skincolor; - - if (targ-players != lasttarg) - { - if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ)) - S_StartSound(NULL, sfx_s3k89); - else - S_StartSound(targ->mo, sfx_s3k89); - - player->kartstuff[k_lastjawztarget] = targ-players; - player->kartstuff[k_jawztargetdelay] = 5; - } - } - else - { - player->kartstuff[k_lastjawztarget] = -1; - player->kartstuff[k_jawztargetdelay] = 0; - } -} - -/*-------------------------------------------------- - static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) - - Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or - previous waypoints are infront of the player. - - Input Arguments:- - player - The player the next waypoint is being found for - - Return:- - The waypoint that is the player's next waypoint ---------------------------------------------------*/ -static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) -{ - waypoint_t *bestwaypoint = NULL; - - if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) - { - waypoint_t *waypoint = K_GetBestWaypointForMobj(player->mo); - boolean updaterespawn = false; - - bestwaypoint = waypoint; - - // check the waypoint's location in relation to the player - // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. - // EXCEPTION: If our best waypoint is the finishline AND we're facing towards it, don't do this. - // Otherwise it breaks the distance calculations. - if (waypoint != NULL) - { - boolean finishlinehack = false; - angle_t playerangle = player->mo->angle; - angle_t momangle = player->mo->angle; - 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) - { - angledelta = InvAngle(angledelta); - } - - momdelta = momangle - angletowaypoint; - if (momdelta > ANGLE_180) - { - momdelta = InvAngle(momdelta); - } - - if (bestwaypoint == K_GetFinishLineWaypoint()) - { - // facing towards the finishline - if (angledelta <= ANGLE_90) - { - finishlinehack = true; - } - } - - // We're using a lot of angle calculations here, because only using facing angle or only using momentum angle both have downsides. - // nextwaypoints will be picked if you're facing OR moving forward. - // prevwaypoints will be picked if you're facing AND moving backward. - if ((angledelta > ANGLE_45 || momdelta > ANGLE_45) - && (finishlinehack == false)) - { - angle_t nextbestdelta = angledelta; - angle_t nextbestmomdelta = momdelta; - size_t i = 0U; - - if (K_PlayerUsesBotMovement(player)) - { - // Try to force bots to use a next waypoint - nextbestdelta = ANGLE_MAX; - nextbestmomdelta = ANGLE_MAX; - } - - if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) - { - for (i = 0U; i < waypoint->numnextwaypoints; i++) - { - if (K_PlayerUsesBotMovement(player) == true - && K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) == true - && K_BotCanTakeCut(player) == false) - { - // Bots that aren't able to take a shortcut will ignore shortcut waypoints. - // (However, if they're already on a shortcut, then we want them to keep going.) - - if (player->nextwaypoint == NULL - || K_GetWaypointIsShortcut(player->nextwaypoint) == false) - { - continue; - } - } - - angletowaypoint = R_PointToAngle2( - player->mo->x, player->mo->y, - waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y); - - angledelta = playerangle - angletowaypoint; - if (angledelta > ANGLE_180) - { - angledelta = InvAngle(angledelta); - } - - momdelta = momangle - angletowaypoint; - if (momdelta > ANGLE_180) - { - momdelta = InvAngle(momdelta); - } - - if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) - { - bestwaypoint = waypoint->nextwaypoints[i]; - - if (angledelta < nextbestdelta) - { - nextbestdelta = angledelta; - } - if (momdelta < nextbestmomdelta) - { - nextbestmomdelta = momdelta; - } - - // Remove wrong way flag if we're using nextwaypoints - player->kartstuff[k_wrongway] = 0; - updaterespawn = true; - } - } - } - - if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U) - && !(K_PlayerUsesBotMovement(player))) // Bots do not need prev waypoints - { - for (i = 0U; i < waypoint->numprevwaypoints; i++) - { - angletowaypoint = R_PointToAngle2( - player->mo->x, player->mo->y, - waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); - - angledelta = playerangle - angletowaypoint; - if (angledelta > ANGLE_180) - { - angledelta = InvAngle(angledelta); - } - - momdelta = momangle - angletowaypoint; - if (momdelta > ANGLE_180) - { - momdelta = InvAngle(momdelta); - } - - if (angledelta < nextbestdelta && momdelta < nextbestmomdelta) - { - bestwaypoint = waypoint->prevwaypoints[i]; - - nextbestdelta = angledelta; - nextbestmomdelta = momdelta; - - // Set wrong way flag if we're using prevwaypoints - player->kartstuff[k_wrongway] = 1; - updaterespawn = false; - } - } - } - } - } - - if (!P_IsObjectOnGround(player->mo)) - { - updaterespawn = false; - } - - // Respawn point should only be updated when we're going to a nextwaypoint - if ((updaterespawn) && - (player->respawn.state == RESPAWNST_NONE) && - (bestwaypoint != NULL) && - (bestwaypoint != player->nextwaypoint) && - (K_GetWaypointIsSpawnpoint(bestwaypoint)) && - (K_GetWaypointIsEnabled(bestwaypoint) == true)) - { - player->respawn.wp = bestwaypoint; - } - } - - return bestwaypoint; -} - -static boolean K_PlayerCloserToNextWaypoints(waypoint_t *const waypoint, player_t *const player) -{ - boolean nextiscloser = true; - - if ((waypoint != NULL) && (player != NULL) && (player->mo != NULL)) - { - size_t i = 0U; - waypoint_t *currentwpcheck = NULL; - angle_t angletoplayer = ANGLE_MAX; - angle_t currentanglecheck = ANGLE_MAX; - angle_t bestangle = ANGLE_MAX; - - angletoplayer = R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, - player->mo->x, player->mo->y); - - for (i = 0U; i < waypoint->numnextwaypoints; i++) - { - currentwpcheck = waypoint->nextwaypoints[i]; - currentanglecheck = R_PointToAngle2( - waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); - - // Get delta angle - currentanglecheck = currentanglecheck - angletoplayer; - - if (currentanglecheck > ANGLE_180) - { - currentanglecheck = InvAngle(currentanglecheck); - } - - if (currentanglecheck < bestangle) - { - bestangle = currentanglecheck; - } - } - - for (i = 0U; i < waypoint->numprevwaypoints; i++) - { - currentwpcheck = waypoint->prevwaypoints[i]; - currentanglecheck = R_PointToAngle2( - waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); - - // Get delta angle - currentanglecheck = currentanglecheck - angletoplayer; - - if (currentanglecheck > ANGLE_180) - { - currentanglecheck = InvAngle(currentanglecheck); - } - - if (currentanglecheck < bestangle) - { - bestangle = currentanglecheck; - nextiscloser = false; - break; - } - } - } - - return nextiscloser; -} - -/*-------------------------------------------------- - void K_UpdateDistanceFromFinishLine(player_t *const player) - - Updates the distance a player has to the finish line. - - Input Arguments:- - player - The player the distance is being updated for - - Return:- - None ---------------------------------------------------*/ -void K_UpdateDistanceFromFinishLine(player_t *const player) -{ - if ((player != NULL) && (player->mo != NULL)) - { - waypoint_t *finishline = K_GetFinishLineWaypoint(); - waypoint_t *nextwaypoint = NULL; - - if (player->spectator) - { - // Don't update waypoints while spectating - nextwaypoint = finishline; - } - else - { - nextwaypoint = K_GetPlayerNextWaypoint(player); - } - - if (nextwaypoint != NULL) - { - // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. - // player->nextwaypoint will keep its previous value in this case. - player->nextwaypoint = nextwaypoint; - } - - // nextwaypoint is now the waypoint that is in front of us - if (player->exiting || player->spectator) - { - // Player has finished, we don't need to calculate this - player->distancetofinish = 0U; - } - else if ((player->nextwaypoint != NULL) && (finishline != NULL)) - { - const boolean useshortcuts = false; - const boolean huntbackwards = false; - boolean pathfindsuccess = false; - path_t pathtofinish = {}; - - pathfindsuccess = - K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); - - // Update the player's distance to the finish line if a path was found. - // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track - if (pathfindsuccess == true) - { - // Add euclidean distance to the next waypoint to the distancetofinish - UINT32 adddist; - fixed_t disttowaypoint = - P_AproxDistance( - (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), - (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); - disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); - - adddist = (UINT32)disttowaypoint; - - player->distancetofinish = pathtofinish.totaldist + adddist; - Z_Free(pathtofinish.array); - - // distancetofinish is currently a flat distance to the finish line, but in order to be fully - // correct we need to add to it the length of the entire circuit multiplied by the number of laps - // left after this one. This will give us the total distance to the finish line, and allow item - // distance calculation to work easily - if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) - { - const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); - - player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); - - // An additional HACK, to fix looking backwards towards the finish line - // If the player's next waypoint is the finishline and the angle distance from player to - // connectin waypoints implies they're closer to a next waypoint, add a full track distance - if (player->nextwaypoint == finishline) - { - if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) - { - player->distancetofinish += K_GetCircuitLength(); - } - } - } - } - } - } -} - -INT32 K_GetKartRingPower(player_t *player) -{ - return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2); -} - -// Returns false if this player being placed here causes them to collide with any other player -// Used in g_game.c for match etc. respawning -// This does not check along the z because the z is not correctly set for the spawnee at this point -boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y) -{ - INT32 i; - fixed_t p1radius = players[playernum].mo->radius; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playernum == i || !playeringame[i] || players[i].spectator || !players[i].mo || players[i].mo->health <= 0 - || players[i].playerstate != PST_LIVE || (players[i].mo->flags & MF_NOCLIP) || (players[i].mo->flags & MF_NOCLIPTHING)) - continue; - - if (abs(x - players[i].mo->x) < (p1radius + players[i].mo->radius) - && abs(y - players[i].mo->y) < (p1radius + players[i].mo->radius)) - { - return false; - } - } - return true; -} - -// countersteer is how strong the controls are telling us we are turning -// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left -static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) -{ - INT16 basedrift, driftadjust; - fixed_t driftweight = player->kartweight*14; // 12 - - if (player->kartstuff[k_drift] == 0 || !P_IsObjectOnGround(player->mo)) - { - // If they aren't drifting or on the ground, this doesn't apply - return 0; - } - - if (player->kartstuff[k_driftend] != 0) - { - // Drift has ended and we are tweaking their angle back a bit - return -266*player->kartstuff[k_drift]; - } - - basedrift = (83 * player->kartstuff[k_drift]) - (((driftweight - 14) * player->kartstuff[k_drift]) / 5); // 415 - 303 - driftadjust = abs((252 - driftweight) * player->kartstuff[k_drift] / 5); - - if (player->kartstuff[k_tiregrease] > 0) // Buff drift-steering while in greasemode - { - basedrift += (basedrift / greasetics) * player->kartstuff[k_tiregrease]; - } - - if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) - { - countersteer = 3*countersteer/2; - } - - return basedrift + (FixedMul(driftadjust * FRACUNIT, countersteer) / FRACUNIT); -} - -INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) -{ - fixed_t p_maxspeed = K_GetKartSpeed(player, false); - fixed_t p_speed = min(player->speed, (p_maxspeed * 2)); - fixed_t weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); - - if (player->spectator) - { - return turnvalue; - } - - if (K_PlayerUsesBotMovement(player)) - { - turnvalue = 5*turnvalue/4; // Base increase to turning - turnvalue = FixedMul( - turnvalue * FRACUNIT, - K_BotRubberband(player) - ) / FRACUNIT; - } - - if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo)) - { - fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); - - // If we're drifting we have a completely different turning value - - if (player->kartstuff[k_driftend] != 0) - { - countersteer = FRACUNIT; - } - - turnvalue = K_GetKartDriftValue(player, countersteer); - - return turnvalue; - } - - if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) - { - turnvalue = 5*turnvalue/4; - } - - if (player->kartstuff[k_flamedash] > 0) - { - fixed_t multiplier = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); - multiplier = FRACUNIT + (FixedDiv(multiplier, FRACUNIT/2) / 4); - turnvalue = FixedMul(turnvalue * FRACUNIT, multiplier) / FRACUNIT; - } - - if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) - { - turnvalue = 3*turnvalue/2; - } - - // Weight has a small effect on turning - turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; - - return turnvalue; -} - -INT32 K_GetKartDriftSparkValue(player_t *player) -{ - UINT8 kartspeed = (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - ? 1 - : player->kartspeed; - return (26*4 + kartspeed*2 + (9 - player->kartweight))*8; -} - -/* -Stage 1: red sparks -Stage 2: blue sparks -Stage 3: big large rainbow sparks -*/ -static void K_SpawnDriftBoostExplosion(player_t *player, int stage) -{ - mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); - - P_SetTarget(&overlay->target, player->mo); - P_SetScale(overlay, (overlay->destscale = player->mo->scale)); - K_FlipFromObject(overlay, player->mo); - - switch (stage) - { - case 1: - overlay->color = SKINCOLOR_KETCHUP; - overlay->fuse = 16; - break; - - case 2: - overlay->color = SKINCOLOR_SAPPHIRE; - overlay->fuse = 32; - - S_StartSound(player->mo, sfx_kc5b); - break; - - case 3: - overlay->color = SKINCOLOR_SILVER; - overlay->fuse = 120; - - S_StartSound(player->mo, sfx_kc5b); - S_StartSound(player->mo, sfx_s3kc4l); - break; - } -} - -static void K_KartDrift(player_t *player, boolean onground) -{ - fixed_t minspeed = (10 * player->mo->scale); - INT32 dsone = K_GetKartDriftSparkValue(player); - INT32 dstwo = dsone*2; - INT32 dsthree = dstwo*2; - - // Drifting is actually straffing + automatic turning. - // Holding the Jump button will enable drifting. - - // Drift Release (Moved here so you can't "chain" drifts) - if (player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5) - { - if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) - { - angle_t pushdir = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - - S_StartSound(player->mo, sfx_s23c); - //K_SpawnDashDustRelease(player); - - if (player->kartstuff[k_driftcharge] < 0) - { - // Stage 0: Yellow sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 8); - - if (player->kartstuff[k_driftboost] < 15) - player->kartstuff[k_driftboost] = 15; - } - else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) - { - // Stage 1: Red sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 4); - - if (player->kartstuff[k_driftboost] < 20) - player->kartstuff[k_driftboost] = 20; - - K_SpawnDriftBoostExplosion(player, 1); - } - else if (player->kartstuff[k_driftcharge] < dsthree) - { - // Stage 2: Blue sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 3); - - if (player->kartstuff[k_driftboost] < 50) - player->kartstuff[k_driftboost] = 50; - - K_SpawnDriftBoostExplosion(player, 2); - } - else if (player->kartstuff[k_driftcharge] >= dsthree) - { - // Stage 3: Rainbow sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 2); - - if (player->kartstuff[k_driftboost] < 125) - player->kartstuff[k_driftboost] = 125; - - K_SpawnDriftBoostExplosion(player, 3); - } - } - - // Remove charge - player->kartstuff[k_driftcharge] = 0; - } - - // Drifting: left or right? - if ((player->cmd.driftturn > 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 - && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != 1) - { - // Starting left drift - player->kartstuff[k_drift] = 1; - player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; - } - else if ((player->cmd.driftturn < 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 - && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != -1) - { - // Starting right drift - player->kartstuff[k_drift] = -1; - player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; - } - else if (player->kartstuff[k_jmp] == 0) // || player->kartstuff[k_turndir] == 0) - { - // drift is not being performed so if we're just finishing set driftend and decrement counters - if (player->kartstuff[k_drift] > 0) - { - player->kartstuff[k_drift]--; - player->kartstuff[k_driftend] = 1; - } - else if (player->kartstuff[k_drift] < 0) - { - player->kartstuff[k_drift]++; - player->kartstuff[k_driftend] = 1; - } - else - player->kartstuff[k_driftend] = 0; - } - - if (player->kartstuff[k_spinouttimer] > 0 || player->speed == 0) - { - // Stop drifting - player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; - player->kartstuff[k_getsparks] = 0; - } - else if (player->kartstuff[k_jmp] == 1 && player->kartstuff[k_drift] != 0) - { - // Incease/decrease the drift value to continue drifting in that direction - fixed_t driftadditive = 24; - boolean playsound = false; - - if (onground) - { - if (player->kartstuff[k_drift] >= 1) // Drifting to the left - { - player->kartstuff[k_drift]++; - if (player->kartstuff[k_drift] > 5) - player->kartstuff[k_drift] = 5; - - if (player->cmd.driftturn > 0) // Inward - driftadditive += abs(player->cmd.driftturn)/100; - if (player->cmd.driftturn < 0) // Outward - driftadditive -= abs(player->cmd.driftturn)/75; - } - else if (player->kartstuff[k_drift] <= -1) // Drifting to the right - { - player->kartstuff[k_drift]--; - if (player->kartstuff[k_drift] < -5) - player->kartstuff[k_drift] = -5; - - if (player->cmd.driftturn < 0) // Inward - driftadditive += abs(player->cmd.driftturn)/100; - if (player->cmd.driftturn > 0) // Outward - driftadditive -= abs(player->cmd.driftturn)/75; - } - - // Disable drift-sparks until you're going fast enough - if (player->kartstuff[k_getsparks] == 0 - || (player->kartstuff[k_offroad] && K_ApplyOffroad(player))) - driftadditive = 0; - - // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. - if (player->speed > minspeed*2) - { - player->kartstuff[k_getsparks] = 1; - - if (player->kartstuff[k_driftcharge] <= -1) - { - player->kartstuff[k_driftcharge] = dsone; // Back to red - playsound = true; - } - } - else if (player->speed <= minspeed) - { - player->kartstuff[k_getsparks] = 0; - driftadditive = 0; - - if (player->kartstuff[k_driftcharge] >= dsone) - { - player->kartstuff[k_driftcharge] = -1; // Set yellow sparks - playsound = true; - } - } - } - else - { - driftadditive = 0; - } - - // This spawns the drift sparks - if ((player->kartstuff[k_driftcharge] + driftadditive >= dsone) - || (player->kartstuff[k_driftcharge] < 0)) - { - K_SpawnDriftSparks(player); - } - - if ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone) - || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo) - || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree)) - { - playsound = true; - } - - // Sound whenever you get a different tier of sparks - if (playsound && P_IsDisplayPlayer(player)) - { - if (player->kartstuff[k_driftcharge] == -1) - S_StartSoundAtVolume(player->mo, sfx_sploss, 192); // Yellow spark sound - else - S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); - } - - player->kartstuff[k_driftcharge] += driftadditive; - player->kartstuff[k_driftend] = 0; - } - - if ((!player->kartstuff[k_sneakertimer]) - || (!player->cmd.driftturn) - || (!player->kartstuff[k_aizdriftstrat]) - || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) - { - if (!player->kartstuff[k_drift]) - player->kartstuff[k_aizdriftstrat] = 0; - else - player->kartstuff[k_aizdriftstrat] = ((player->kartstuff[k_drift] > 0) ? 1 : -1); - } - else if (player->kartstuff[k_aizdriftstrat] && !player->kartstuff[k_drift]) - K_SpawnAIZDust(player); - - if (player->kartstuff[k_drift] - && ((player->cmd.buttons & BT_BRAKE) - || !(player->cmd.buttons & BT_ACCELERATE)) - && P_IsObjectOnGround(player->mo)) - { - if (!player->kartstuff[k_brakedrift]) - K_SpawnBrakeDriftSparks(player); - player->kartstuff[k_brakedrift] = 1; - } - else - player->kartstuff[k_brakedrift] = 0; -} -// -// K_KartUpdatePosition -// -void K_KartUpdatePosition(player_t *player) -{ - fixed_t position = 1; - fixed_t oldposition = player->kartstuff[k_position]; - fixed_t i; - - if (player->spectator || !player->mo) - { - // Ensure these are reset for spectators - player->kartstuff[k_position] = 0; - player->kartstuff[k_positiondelay] = 0; - return; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - if (G_RaceGametype()) - { - if (player->exiting) // End of match standings - { - // Only time matters - if (players[i].realtime < player->realtime) - position++; - } - else - { - // I'm a lap behind this player OR - // My distance to the finish line is higher, so I'm behind - if ((players[i].laps > player->laps) - || (players[i].distancetofinish < player->distancetofinish)) - { - position++; - } - } - } - else if (G_BattleGametype()) - { - if (player->exiting) // End of match standings - { - // Only score matters - if (players[i].marescore > player->marescore) - position++; - } - 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])) - position++; - } - } - } - - if (leveltime < starttime || oldposition == 0) - oldposition = position; - - if (oldposition != position) // Changed places? - player->kartstuff[k_positiondelay] = 10; // Position number growth - - player->kartstuff[k_position] = position; -} - -// -// K_StripItems -// -void K_StripItems(player_t *player) -{ - player->kartstuff[k_itemtype] = KITEM_NONE; - player->kartstuff[k_itemamount] = 0; - player->kartstuff[k_itemheld] = 0; - - player->kartstuff[k_rocketsneakertimer] = 0; - - if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2) - { - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - } - player->kartstuff[k_eggmanheld] = 0; - - player->kartstuff[k_hyudorotimer] = 0; - player->kartstuff[k_stealingtimer] = 0; - player->kartstuff[k_stolentimer] = 0; - - player->kartstuff[k_curshield] = KSHIELD_NONE; - player->kartstuff[k_bananadrag] = 0; - - player->kartstuff[k_sadtimer] = 0; - - K_UpdateHnextList(player, true); -} - -void K_StripOther(player_t *player) -{ - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - - player->kartstuff[k_invincibilitytimer] = 0; - K_RemoveGrowShrink(player); - - if (player->kartstuff[k_eggmanexplode]) - { - player->kartstuff[k_eggmanexplode] = 0; - player->kartstuff[k_eggmanblame] = -1; - } -} - -static INT32 K_FlameShieldMax(player_t *player) -{ - UINT32 disttofinish = 0; - UINT32 distv = DISTVAR; - UINT8 numplayers = 0; - UINT8 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator) - numplayers++; - if (players[i].kartstuff[k_position] == 1) - disttofinish = players[i].distancetofinish; - } - - if (numplayers <= 1) - { - return 16; // max when alone, for testing - } - else if (player->kartstuff[k_position] == 1) - { - return 0; // minimum for first - } - - disttofinish = player->distancetofinish - disttofinish; - distv = FixedMul(distv * FRACUNIT, mapobjectscale) / FRACUNIT; - return min(16, 1 + (disttofinish / distv)); -} - -// -// K_MoveKartPlayer -// -void K_MoveKartPlayer(player_t *player, boolean onground) -{ - ticcmd_t *cmd = &player->cmd; - boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->pflags & PF_ATTACKDOWN)); - boolean HOLDING_ITEM = (player->kartstuff[k_itemheld] || player->kartstuff[k_eggmanheld]); - boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0); - - player->pflags &= ~PF_HITFINISHLINE; - - if (!player->exiting) - { - if (player->kartstuff[k_oldposition] < player->kartstuff[k_position]) // But first, if you lost a place, - { - player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // then the other player taunts. - K_RegularVoiceTimers(player); // and you can't for a bit - } - else if (player->kartstuff[k_oldposition] > player->kartstuff[k_position]) // Otherwise, - { - K_PlayOvertakeSound(player->mo); // Say "YOU'RE TOO SLOW!" - player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // Restore the old position, - } - } - - if (player->kartstuff[k_positiondelay]) - player->kartstuff[k_positiondelay]--; - - // Prevent ring misfire - if (!(cmd->buttons & BT_ATTACK)) - { - if (player->kartstuff[k_itemtype] == KITEM_NONE - && NO_HYUDORO && !(HOLDING_ITEM - || player->kartstuff[k_itemamount] - || player->kartstuff[k_itemroulette] - || player->kartstuff[k_rocketsneakertimer] - || player->kartstuff[k_eggmanexplode])) - player->kartstuff[k_userings] = 1; - else - player->kartstuff[k_userings] = 0; - } - - if ((player->pflags & PF_ATTACKDOWN) && !(cmd->buttons & BT_ATTACK)) - player->pflags &= ~PF_ATTACKDOWN; - else if (cmd->buttons & BT_ATTACK) - player->pflags |= PF_ATTACKDOWN; - - if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > starttime - && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && (player->respawn.state == RESPAWNST_NONE)) - { - // First, the really specific, finicky items that function without the item being directly in your item slot. - // Karma item dropping - if (player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer]) - { - if (ATTACK_IS_DOWN) - { - mobj_t *newitem; - - if (player->kartstuff[k_comebackmode] == 1) - { - newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RANDOMITEM); - newitem->threshold = 69; // selected "randomly". - } - else - { - newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM); - if (player->kartstuff[k_eggmanblame] >= 0 - && player->kartstuff[k_eggmanblame] < MAXPLAYERS - && playeringame[player->kartstuff[k_eggmanblame]] - && !players[player->kartstuff[k_eggmanblame]].spectator - && players[player->kartstuff[k_eggmanblame]].mo) - P_SetTarget(&newitem->target, players[player->kartstuff[k_eggmanblame]].mo); - player->kartstuff[k_eggmanblame] = -1; - } - - newitem->flags2 = (player->mo->flags2 & MF2_OBJECTFLIP); - newitem->fuse = 15*TICRATE; // selected randomly. - - player->kartstuff[k_comebackmode] = 0; - player->kartstuff[k_comebacktimer] = comebacktime; - S_StartSound(player->mo, sfx_s254); - } - } - else - { - // Ring boosting - if (player->kartstuff[k_userings]) - { - if ((player->pflags & PF_ATTACKDOWN) && !player->kartstuff[k_ringdelay] && player->kartstuff[k_rings] > 0) - { - mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); - P_SetMobjState(ring, S_FASTRING1); - ring->extravalue1 = 1; // Ring use animation timer - ring->extravalue2 = 1; // Ring use animation flag - ring->shadowscale = 0; - P_SetTarget(&ring->target, player->mo); // user - player->kartstuff[k_rings]--; - player->kartstuff[k_ringdelay] = 3; - } - } - // Other items - else - { - // Eggman Monitor exploding - if (player->kartstuff[k_eggmanexplode]) - { - if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanexplode] <= 3*TICRATE && player->kartstuff[k_eggmanexplode] > 1) - player->kartstuff[k_eggmanexplode] = 1; - } - // Eggman Monitor throwing - else if (player->kartstuff[k_eggmanheld]) - { - if (ATTACK_IS_DOWN) - { - K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_eggmanheld] = 0; - K_UpdateHnextList(player, true); - } - } - // Rocket Sneaker usage - else if (player->kartstuff[k_rocketsneakertimer] > 1) - { - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) - { - K_DoSneaker(player, 2); - K_PlayBoostTaunt(player->mo); - player->kartstuff[k_rocketsneakertimer] -= 3*TICRATE; - if (player->kartstuff[k_rocketsneakertimer] < 1) - player->kartstuff[k_rocketsneakertimer] = 1; - } - } - else if (player->kartstuff[k_itemamount] <= 0) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - } - else - { - switch (player->kartstuff[k_itemtype]) - { - case KITEM_SNEAKER: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) - { - K_DoSneaker(player, 1); - K_PlayBoostTaunt(player->mo); - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_ROCKETSNEAKER: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO - && player->kartstuff[k_rocketsneakertimer] == 0) - { - INT32 moloop; - mobj_t *mo = NULL; - mobj_t *prev = player->mo; - - K_PlayBoostTaunt(player->mo); - //player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s3k3a); - - //K_DoSneaker(player, 2); - - player->kartstuff[k_rocketsneakertimer] = (itemtime*3); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, true); - - for (moloop = 0; moloop < 2; moloop++) - { - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ROCKETSNEAKER); - K_MatchGenericExtraFlags(mo, player->mo); - mo->flags |= MF_NOCLIPTHING; - mo->angle = player->mo->angle; - mo->threshold = 10; - mo->movecount = moloop%2; - mo->movedir = mo->lastlook = moloop+1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - break; - case KITEM_INVINCIBILITY: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage normally, so you're free to waste it if you have multiple - { - if (!player->kartstuff[k_invincibilitytimer]) - { - mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INVULNFLASH); - P_SetTarget(&overlay->target, player->mo); - overlay->destscale = player->mo->scale; - P_SetScale(overlay, player->mo->scale); - } - player->kartstuff[k_invincibilitytimer] = itemtime+(2*TICRATE); // 10 seconds - if (P_IsLocalPlayer(player)) - S_ChangeMusicSpecial("kinvnc"); - if (! P_IsDisplayPlayer(player)) - S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kinvnc)); - P_RestoreMusic(player); - K_PlayPowerGloatSound(player->mo); - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_BANANA: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - INT32 moloop; - mobj_t *mo; - mobj_t *prev = player->mo; - - //K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s254); - - for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) - { - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BANANA_SHIELD); - if (!mo) - { - player->kartstuff[k_itemamount] = moloop; - break; - } - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = player->kartstuff[k_itemamount]; - mo->movedir = moloop+1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Banana x3 thrown - { - K_ThrowKartItem(player, false, MT_BANANA, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, false); - } - break; - case KITEM_EGGMAN: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - mobj_t *mo; - player->kartstuff[k_itemamount]--; - player->kartstuff[k_eggmanheld] = 1; - S_StartSound(player->mo, sfx_s254); - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD); - if (mo) - { - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = 1; - mo->movedir = 1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&player->mo->hnext, mo); - } - } - break; - case KITEM_ORBINAUT: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - angle_t newangle; - INT32 moloop; - mobj_t *mo = NULL; - mobj_t *prev = player->mo; - - //K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s3k3a); - - for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) - { - newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ORBINAUT_SHIELD); - if (!mo) - { - player->kartstuff[k_itemamount] = moloop; - break; - } - mo->flags |= MF_NOCLIPTHING; - mo->angle = newangle; - mo->threshold = 10; - mo->movecount = player->kartstuff[k_itemamount]; - mo->movedir = mo->lastlook = moloop+1; - mo->color = player->skincolor; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Orbinaut x3 thrown - { - K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, false); - } - break; - case KITEM_JAWZ: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - angle_t newangle; - INT32 moloop; - mobj_t *mo = NULL; - mobj_t *prev = player->mo; - - //K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s3k3a); - - for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) - { - newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_JAWZ_SHIELD); - if (!mo) - { - player->kartstuff[k_itemamount] = moloop; - break; - } - mo->flags |= MF_NOCLIPTHING; - mo->angle = newangle; - mo->threshold = 10; - mo->movecount = player->kartstuff[k_itemamount]; - mo->movedir = mo->lastlook = moloop+1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Jawz thrown - { - if (player->kartstuff[k_throwdir] == 1 || player->kartstuff[k_throwdir] == 0) - K_ThrowKartItem(player, true, MT_JAWZ, 1, 0); - else if (player->kartstuff[k_throwdir] == -1) // Throwing backward gives you a dud that doesn't home in - K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, false); - } - break; - case KITEM_MINE: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - mobj_t *mo; - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s254); - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SSMINE_SHIELD); - if (mo) - { - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = 1; - mo->movedir = 1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&player->mo->hnext, mo); - } - } - else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) - { - K_ThrowKartItem(player, false, MT_SSMINE, 1, 1); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - player->kartstuff[k_itemheld] = 0; - K_UpdateHnextList(player, true); - } - break; - case KITEM_BALLHOG: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_itemamount]--; - K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0); - K_PlayAttackTaunt(player->mo); - } - break; - case KITEM_SPB: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_itemamount]--; - K_ThrowKartItem(player, true, MT_SPB, 1, 0); - K_PlayAttackTaunt(player->mo); - } - break; - case KITEM_GROW: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - if (player->kartstuff[k_growshrinktimer] < 0) // If you're shrunk, then "grow" will just make you normal again. - K_RemoveGrowShrink(player); - else - { - K_PlayPowerGloatSound(player->mo); - player->mo->scalespeed = mapobjectscale/TICRATE; - player->mo->destscale = (3*mapobjectscale)/2; - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; - player->kartstuff[k_growshrinktimer] = itemtime+(4*TICRATE); // 12 seconds - if (P_IsLocalPlayer(player)) - S_ChangeMusicSpecial("kgrow"); - if (! P_IsDisplayPlayer(player)) - S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); - P_RestoreMusic(player); - S_StartSound(player->mo, sfx_kc5a); - } - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_SHRINK: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - K_DoShrink(player); - player->kartstuff[k_itemamount]--; - K_PlayPowerGloatSound(player->mo); - } - break; - case KITEM_THUNDERSHIELD: - if (player->kartstuff[k_curshield] != KSHIELD_THUNDER) - { - mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THUNDERSHIELD); - P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); - P_SetTarget(&shield->target, player->mo); - S_StartSound(player->mo, sfx_s3k41); - player->kartstuff[k_curshield] = KSHIELD_THUNDER; - } - - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - K_DoThunderShield(player); - player->kartstuff[k_itemamount]--; - K_PlayAttackTaunt(player->mo); - } - break; - case KITEM_BUBBLESHIELD: - if (player->kartstuff[k_curshield] != KSHIELD_BUBBLE) - { - mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BUBBLESHIELD); - P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); - P_SetTarget(&shield->target, player->mo); - S_StartSound(player->mo, sfx_s3k3f); - player->kartstuff[k_curshield] = KSHIELD_BUBBLE; - } - - if (!HOLDING_ITEM && NO_HYUDORO) - { - if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) - { - if (player->kartstuff[k_bubbleblowup] == 0) - S_StartSound(player->mo, sfx_s3k75); - - player->kartstuff[k_bubbleblowup]++; - player->kartstuff[k_bubblecool] = player->kartstuff[k_bubbleblowup]*4; - - if (player->kartstuff[k_bubbleblowup] > bubbletime*2) - { - K_ThrowKartItem(player, (player->kartstuff[k_throwdir] > 0), MT_BUBBLESHIELDTRAP, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_bubbleblowup] = 0; - player->kartstuff[k_bubblecool] = 0; - player->kartstuff[k_holdready] = 0; - player->kartstuff[k_itemamount]--; - } - } - else - { - if (player->kartstuff[k_bubbleblowup] > bubbletime) - player->kartstuff[k_bubbleblowup] = bubbletime; - - if (player->kartstuff[k_bubbleblowup]) - player->kartstuff[k_bubbleblowup]--; - - player->kartstuff[k_holdready] = (player->kartstuff[k_bubblecool] ? 0 : 1); - } - } - break; - case KITEM_FLAMESHIELD: - if (player->kartstuff[k_curshield] != KSHIELD_FLAME) - { - mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_FLAMESHIELD); - P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); - P_SetTarget(&shield->target, player->mo); - S_StartSound(player->mo, sfx_s3k3e); - player->kartstuff[k_curshield] = KSHIELD_FLAME; - } - - if (!HOLDING_ITEM && NO_HYUDORO) - { - INT32 destlen = K_FlameShieldMax(player); - INT32 flamemax = 0; - - if (player->kartstuff[k_flamelength] < destlen) - player->kartstuff[k_flamelength]++; // Can always go up! - - flamemax = player->kartstuff[k_flamelength] * flameseg; - if (flamemax > 0) - flamemax += TICRATE; // leniency period - - if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) - { - if (player->kartstuff[k_flamemeter] < 0) - player->kartstuff[k_flamemeter] = 0; - - if (player->kartstuff[k_flamedash] == 0) - { - S_StartSound(player->mo, sfx_s3k43); - K_PlayBoostTaunt(player->mo); - } - - player->kartstuff[k_flamedash] += 2; - player->kartstuff[k_flamemeter] += 2; - - if (!onground) - { - P_Thrust( - player->mo, R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), - FixedMul(player->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) - ); - } - - if (player->kartstuff[k_flamemeter] > flamemax) - { - P_Thrust( - player->mo, player->mo->angle, - FixedMul((50*player->mo->scale), K_GetKartGameSpeedScalar(gamespeed)) - ); - - player->kartstuff[k_flamemeter] = 0; - player->kartstuff[k_flamelength] = 0; - player->kartstuff[k_holdready] = 0; - player->kartstuff[k_itemamount]--; - } - } - else - { - player->kartstuff[k_holdready] = 1; - - if (player->kartstuff[k_flamemeter] > 0) - player->kartstuff[k_flamemeter]--; - - if (player->kartstuff[k_flamelength] > destlen) - { - player->kartstuff[k_flamelength]--; // Can ONLY go down if you're not using it - - flamemax = player->kartstuff[k_flamelength] * flameseg; - if (flamemax > 0) - flamemax += TICRATE; // leniency period - } - - if (player->kartstuff[k_flamemeter] > flamemax) - player->kartstuff[k_flamemeter] = flamemax; - } - } - break; - case KITEM_HYUDORO: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_itemamount]--; - K_DoHyudoroSteal(player); // yes. yes they do. - } - break; - case KITEM_POGOSPRING: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO - && !player->kartstuff[k_pogospring]) - { - K_PlayBoostTaunt(player->mo); - K_DoPogoSpring(player->mo, 32<kartstuff[k_pogospring] = 1; - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_SUPERRING: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_superring] += (10*3); - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_KITCHENSINK: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - mobj_t *mo; - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s254); - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SINK_SHIELD); - if (mo) - { - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = 1; - mo->movedir = 1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&player->mo->hnext, mo); - } - } - else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Sink thrown - { - K_ThrowKartItem(player, false, MT_SINK, 1, 2); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - player->kartstuff[k_itemheld] = 0; - K_UpdateHnextList(player, true); - } - break; - case KITEM_SAD: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO - && !player->kartstuff[k_sadtimer]) - { - player->kartstuff[k_sadtimer] = stealtime; - player->kartstuff[k_itemamount]--; - } - break; - default: - break; - } - } - } - } - - // No more! - if (!player->kartstuff[k_itemamount]) - { - player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } - - if (K_GetShieldFromItem(player->kartstuff[k_itemtype]) == KSHIELD_NONE) - { - player->kartstuff[k_curshield] = KSHIELD_NONE; // RESET shield type - player->kartstuff[k_bubbleblowup] = 0; - player->kartstuff[k_bubblecool] = 0; - player->kartstuff[k_flamelength] = 0; - player->kartstuff[k_flamemeter] = 0; - } - - if (spbplace == -1 || player->kartstuff[k_position] != spbplace) - player->kartstuff[k_ringlock] = 0; // reset ring lock - - if (player->kartstuff[k_itemtype] == KITEM_SPB - || player->kartstuff[k_itemtype] == KITEM_SHRINK - || player->kartstuff[k_growshrinktimer] < 0) - indirectitemcooldown = 20*TICRATE; - - if (player->kartstuff[k_hyudorotimer] > 0) - { - INT32 hyu = hyudorotime; - - if (G_RaceGametype()) - hyu *= 2; // double in race - - if (r_splitscreen) - { - if (leveltime & 1) - player->mo->flags2 |= MF2_DONTDRAW; - else - player->mo->flags2 &= ~MF2_DONTDRAW; - - if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) - { - if (player == &players[displayplayers[1]]) - player->mo->eflags |= MFE_DRAWONLYFORP2; - else if (player == &players[displayplayers[2]] && r_splitscreen > 1) - player->mo->eflags |= MFE_DRAWONLYFORP3; - else if (player == &players[displayplayers[3]] && r_splitscreen > 2) - player->mo->eflags |= MFE_DRAWONLYFORP4; - else if (player == &players[displayplayers[0]]) - player->mo->eflags |= MFE_DRAWONLYFORP1; - else - player->mo->flags2 |= MF2_DONTDRAW; - } - else - player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); - } - else - { - if (P_IsDisplayPlayer(player) - || (!P_IsDisplayPlayer(player) && (player->kartstuff[k_hyudorotimer] < (TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)))) - { - if (leveltime & 1) - player->mo->flags2 |= MF2_DONTDRAW; - else - player->mo->flags2 &= ~MF2_DONTDRAW; - } - else - player->mo->flags2 |= MF2_DONTDRAW; - } - - player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints - } - else if (player->kartstuff[k_hyudorotimer] == 0) - { - player->mo->flags2 &= ~MF2_DONTDRAW; - player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4); - } - - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) // dead in match? you da bomb - { - K_DropItems(player); //K_StripItems(player); - K_StripOther(player); - player->mo->flags2 |= MF2_SHADOW; - player->powers[pw_flashing] = player->kartstuff[k_comebacktimer]; - } - else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0) - { - player->mo->flags2 &= ~MF2_SHADOW; - } - } - - if (onground) - { - fixed_t prevfriction = player->mo->friction; - - // Reduce friction after hitting a horizontal spring - if (player->kartstuff[k_tiregrease]) - player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->kartstuff[k_tiregrease]; - - // Friction - if (!player->kartstuff[k_offroad]) - { - if (player->speed > 0 && cmd->forwardmove == 0 && player->mo->friction == 59392) - player->mo->friction += 4608; - } - - // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel - if (player->speed > 0 && cmd->forwardmove < 0) - player->mo->friction -= 2048; - - // Karma ice physics - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - player->mo->friction += 1228; - - if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) - player->mo->friction += 614; - - // Wipeout slowdown - if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow]) - { - if (player->kartstuff[k_offroad]) - player->mo->friction -= 4912; - if (player->kartstuff[k_wipeoutslow] == 1) - player->mo->friction -= 9824; - } - - // Friction was changed, so we must recalculate a bunch of stuff - if (player->mo->friction != prevfriction) - { - if (player->mo->friction > FRACUNIT) - player->mo->friction = FRACUNIT; - if (player->mo->friction < 0) - player->mo->friction = 0; - - player->mo->movefactor = FixedDiv(ORIG_FRICTION, player->mo->friction); - - if (player->mo->movefactor < FRACUNIT) - player->mo->movefactor = 19*player->mo->movefactor - 18*FRACUNIT; - else - player->mo->movefactor = FRACUNIT; - - if (player->mo->movefactor < 32) - player->mo->movefactor = 32; - } - - // Don't go too far above your top speed when rubberbanding - // Down here, because we do NOT want to modify movefactor - if (K_PlayerUsesBotMovement(player)) - { - player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction); - } - } - - K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads - - // Quick Turning - // You can't turn your kart when you're not moving. - // So now it's time to burn some rubber! - if (player->speed < 2 && leveltime > starttime && cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE && cmd->driftturn != 0) - { - if (leveltime % 8 == 0) - S_StartSound(player->mo, sfx_s224); - } - - // Squishing - // If a Grow player or a sector crushes you, get flattened instead of being killed. - - if (player->kartstuff[k_squishedtimer] > 0) - { - //player->mo->flags |= MF_NOCLIP; - player->mo->momx = 0; - player->mo->momy = 0; - } - - // Play the starting countdown sounds - if (player == &players[g_localplayers[0]]) // Don't play louder in splitscreen - { - if ((leveltime == starttime-(3*TICRATE)) || (leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) - S_StartSound(NULL, sfx_s3ka7); - if (leveltime == starttime) - { - S_StartSound(NULL, sfx_s3kad); - S_StopMusic(); // The GO! sound stops the level start ambience - } - } - - // Start charging once you're given the opportunity. - if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) - { - if (cmd->buttons & BT_ACCELERATE) - { - if (player->kartstuff[k_boostcharge] == 0) - player->kartstuff[k_boostcharge] = cmd->latency; - - player->kartstuff[k_boostcharge]++; - } - else - player->kartstuff[k_boostcharge] = 0; - } - - // Increase your size while charging your engine. - if (leveltime < starttime+10) - { - player->mo->scalespeed = mapobjectscale/12; - player->mo->destscale = mapobjectscale + (player->kartstuff[k_boostcharge]*131); - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; - } - - // Determine the outcome of your charge. - if (leveltime > starttime && player->kartstuff[k_boostcharge]) - { - // Not even trying? - if (player->kartstuff[k_boostcharge] < 35) - { - if (player->kartstuff[k_boostcharge] > 17) - S_StartSound(player->mo, sfx_cdfm00); // chosen instead of a conventional skid because it's more engine-like - } - // Get an instant boost! - else if (player->kartstuff[k_boostcharge] <= 50) - { - player->kartstuff[k_startboost] = (50-player->kartstuff[k_boostcharge])+20; - - if (player->kartstuff[k_boostcharge] <= 36) - { - player->kartstuff[k_startboost] = 0; - K_DoSneaker(player, 0); - player->kartstuff[k_sneakertimer] = 70; // PERFECT BOOST!! - - if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) // Let everyone hear this one - S_StartSound(player->mo, sfx_s25f); - } - else - { - K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker - if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsDisplayPlayer(player)) - { - if (player->kartstuff[k_boostcharge] <= 40) - S_StartSound(player->mo, sfx_cdfm01); // You were almost there! - else - S_StartSound(player->mo, sfx_s23c); // Nope, better luck next time. - } - } - } - // You overcharged your engine? Those things are expensive!!! - else if (player->kartstuff[k_boostcharge] > 50) - { - player->powers[pw_nocontrol] = 40; - //S_StartSound(player->mo, sfx_kc34); - S_StartSound(player->mo, sfx_s3k83); - player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse - } - - player->kartstuff[k_boostcharge] = 0; - } -} - -void K_CheckSpectateStatus(void) -{ - UINT8 respawnlist[MAXPLAYERS]; - UINT8 i, j, numingame = 0, numjoiners = 0; - - // Maintain spectate wait timer - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN)) - players[i].kartstuff[k_spectatewait]++; - else - players[i].kartstuff[k_spectatewait] = 0; - } - - // No one's allowed to join - if (!cv_allowteamchange.value) - return; - - // Get the number of players in game, and the players to be de-spectated. - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - if (!players[i].spectator) - { - numingame++; - if (cv_ingamecap.value && numingame >= cv_ingamecap.value) // DON'T allow if you've hit the in-game player cap - return; - if (gamestate != GS_LEVEL) // Allow if you're not in a level - continue; - if (players[i].exiting) // DON'T allow if anyone's exiting - return; - if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet - continue; - if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in - return; - if (G_RaceGametype() && players[i].laps >= 2) // DON'T allow if the race is at 2 laps - return; - continue; - } - else if (!(players[i].pflags & PF_WANTSTOJOIN)) - continue; - - respawnlist[numjoiners++] = i; - } - - // literally zero point in going any further if nobody is joining - if (!numjoiners) - return; - - // Organize by spectate wait timer - if (cv_ingamecap.value) - { - UINT8 oldrespawnlist[MAXPLAYERS]; - memcpy(oldrespawnlist, respawnlist, numjoiners); - for (i = 0; i < numjoiners; i++) - { - UINT8 pos = 0; - INT32 ispecwait = players[oldrespawnlist[i]].kartstuff[k_spectatewait]; - - for (j = 0; j < numjoiners; j++) - { - INT32 jspecwait = players[oldrespawnlist[j]].kartstuff[k_spectatewait]; - if (j == i) - continue; - if (jspecwait > ispecwait) - pos++; - else if (jspecwait == ispecwait && j < i) - pos++; - } - - respawnlist[pos] = oldrespawnlist[i]; - } - } - - // Finally, we can de-spectate everyone! - for (i = 0; i < numjoiners; i++) - { - if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people? - break; - P_SpectatorJoinGame(&players[respawnlist[i]]); - } - - // Reset the match if you're in an empty server - if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+i >= 2)) // use previous i value - { - S_ChangeMusicInternal("chalng", false); // COME ON - mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD - } -} - -//} - -//{ SRB2kart HUD Code - -#define NUMPOSNUMS 10 -#define NUMPOSFRAMES 7 // White, three blues, three reds -#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple - -//{ Patch Definitions -static patch_t *kp_nodraw; - -static patch_t *kp_timesticker; -static patch_t *kp_timestickerwide; -static patch_t *kp_lapsticker; -static patch_t *kp_lapstickerwide; -static patch_t *kp_lapstickernarrow; -static patch_t *kp_splitlapflag; -static patch_t *kp_bumpersticker; -static patch_t *kp_bumperstickerwide; -static patch_t *kp_capsulesticker; -static patch_t *kp_capsulestickerwide; -static patch_t *kp_karmasticker; -static patch_t *kp_splitkarmabomb; -static patch_t *kp_timeoutsticker; - -static patch_t *kp_startcountdown[16]; -static patch_t *kp_racefinish[6]; - -static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; -static patch_t *kp_winnernum[NUMPOSFRAMES]; - -static patch_t *kp_facenum[MAXPLAYERS+1]; -static patch_t *kp_facehighlight[8]; - -static patch_t *kp_spbminimap; - -static patch_t *kp_ringsticker[2]; -static patch_t *kp_ringstickersplit[4]; -static patch_t *kp_ring[6]; -static patch_t *kp_smallring[6]; -static patch_t *kp_ringdebtminus; -static patch_t *kp_ringdebtminussmall; -static patch_t *kp_ringspblock[16]; -static patch_t *kp_ringspblocksmall[16]; - -static patch_t *kp_speedometersticker; -static patch_t *kp_speedometerlabel[4]; - -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_battlewin; -static patch_t *kp_battlecool; -static patch_t *kp_battlelose; -static patch_t *kp_battlewait; -static patch_t *kp_battleinfo; -static patch_t *kp_wanted; -static patch_t *kp_wantedsplit; -static patch_t *kp_wantedreticle; - -static patch_t *kp_itembg[4]; -static patch_t *kp_itemtimer[2]; -static patch_t *kp_itemmulsticker[2]; -static patch_t *kp_itemx; - -static patch_t *kp_superring[2]; -static patch_t *kp_sneaker[2]; -static patch_t *kp_rocketsneaker[2]; -static patch_t *kp_invincibility[13]; -static patch_t *kp_banana[2]; -static patch_t *kp_eggman[2]; -static patch_t *kp_orbinaut[5]; -static patch_t *kp_jawz[2]; -static patch_t *kp_mine[2]; -static patch_t *kp_ballhog[2]; -static patch_t *kp_selfpropelledbomb[2]; -static patch_t *kp_grow[2]; -static patch_t *kp_shrink[2]; -static patch_t *kp_thundershield[2]; -static patch_t *kp_bubbleshield[2]; -static patch_t *kp_flameshield[2]; -static patch_t *kp_hyudoro[2]; -static patch_t *kp_pogospring[2]; -static patch_t *kp_kitchensink[2]; -static patch_t *kp_sadface[2]; - -static patch_t *kp_check[6]; - -static patch_t *kp_rival[2]; - -static patch_t *kp_eggnum[4]; - -static patch_t *kp_flameshieldmeter[104][2]; -static patch_t *kp_flameshieldmeter_bg[16][2]; - -static patch_t *kp_fpview[3]; -static patch_t *kp_inputwheel[5]; - -static patch_t *kp_challenger[25]; - -static patch_t *kp_lapanim_lap[7]; -static patch_t *kp_lapanim_final[11]; -static patch_t *kp_lapanim_number[10][3]; -static patch_t *kp_lapanim_emblem[2]; -static patch_t *kp_lapanim_hand[3]; - -static patch_t *kp_yougotem; -static patch_t *kp_itemminimap; - -static patch_t *kp_alagles[10]; -static patch_t *kp_blagles[6]; - -static patch_t *kp_cpu; - -static patch_t *kp_nametagstem; - -void K_LoadKartHUDGraphics(void) -{ - INT32 i, j; - char buffer[9]; - - // Null Stuff - kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX); - - // Stickers - kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX); - kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX); - kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX); - kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX); - kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX); - kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX); - kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX); - kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX); - 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_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); - kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); - - // Starting countdown - kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); - kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); - kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX); - kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX); - kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX); - kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX); - kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX); - kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); - // Splitscreen - kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX); - kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX); - kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX); - kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); - kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX); - kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX); - kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); - kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); - - // Finish - kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX); - kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX); - // Splitscreen - kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX); - kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX); - // 2P splitscreen - kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX); - kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX); - - // Position numbers - sprintf(buffer, "K_POSNxx"); - for (i = 0; i < NUMPOSNUMS; i++) - { - buffer[6] = '0'+i; - for (j = 0; j < NUMPOSFRAMES; j++) - { - //sprintf(buffer, "K_POSN%d%d", i, j); - buffer[7] = '0'+j; - kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - } - - sprintf(buffer, "K_POSNWx"); - for (i = 0; i < NUMWINFRAMES; i++) - { - buffer[7] = '0'+i; - kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "OPPRNKxx"); - for (i = 0; i <= MAXPLAYERS; i++) - { - buffer[6] = '0'+(i/10); - buffer[7] = '0'+(i%10); - kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_CHILIx"); - for (i = 0; i < 8; i++) - { - buffer[7] = '0'+(i+1); - kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_spbminimap = W_CachePatchName("SPBMMAP", PU_HUDGFX); - - // Rings & Lives - kp_ringsticker[0] = W_CachePatchName("RNGBACKA", PU_HUDGFX); - kp_ringsticker[1] = W_CachePatchName("RNGBACKB", PU_HUDGFX); - - sprintf(buffer, "K_RINGx"); - for (i = 0; i < 6; i++) - { - buffer[6] = '0'+(i+1); - kp_ring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringdebtminus = W_CachePatchName("RDEBTMIN", PU_HUDGFX); - - sprintf(buffer, "SPBRNGxx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1) / 10); - buffer[7] = '0'+((i+1) % 10); - kp_ringspblock[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringstickersplit[0] = W_CachePatchName("SMRNGBGA", PU_HUDGFX); - kp_ringstickersplit[1] = W_CachePatchName("SMRNGBGB", PU_HUDGFX); - - sprintf(buffer, "K_SRINGx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '0'+(i+1); - kp_smallring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringdebtminussmall = W_CachePatchName("SRDEBTMN", PU_HUDGFX); - - sprintf(buffer, "SPBRGSxx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1) / 10); - buffer[7] = '0'+((i+1) % 10); - kp_ringspblocksmall[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Speedometer - kp_speedometersticker = W_CachePatchName("K_SPDMBG", PU_HUDGFX); - - sprintf(buffer, "K_SPDMLx"); - for (i = 0; i < 4; i++) - { - buffer[7] = '0'+(i+1); - kp_speedometerlabel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Extra ranking icons - kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX); - kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX); - 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); - - // Battle graphics - kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); - kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX); - kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX); - kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX); - kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX); - kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX); - kp_wantedsplit = W_CachePatchName("4PWANTED", PU_HUDGFX); - kp_wantedreticle = W_CachePatchName("MMAPWANT", PU_HUDGFX); - - // Kart Item Windows - kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX); - kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX); - kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX); - kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX); - kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX); - - kp_superring[0] = W_CachePatchName("K_ITRING", PU_HUDGFX); - kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX); - kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX); - - sprintf(buffer, "K_ITINVx"); - for (i = 0; i < 7; i++) - { - buffer[7] = '1'+i; - kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX); - kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX); - sprintf(buffer, "K_ITORBx"); - for (i = 0; i < 4; i++) - { - buffer[7] = '1'+i; - kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX); - kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX); - kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX); - kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX); - kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX); - kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX); - kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX); - kp_bubbleshield[0] = W_CachePatchName("K_ITBUBS", PU_HUDGFX); - kp_flameshield[0] = W_CachePatchName("K_ITFLMS", PU_HUDGFX); - kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX); - kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX); - kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX); - kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX); - - sprintf(buffer, "FSMFGxxx"); - for (i = 0; i < 104; i++) - { - buffer[5] = '0'+((i+1)/100); - buffer[6] = '0'+(((i+1)/10)%10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "FSMBG0xx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter_bg[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Splitscreen - kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX); - kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX); - kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX); - kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX); - - kp_superring[1] = W_CachePatchName("K_ISRING", PU_HUDGFX); - kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX); - kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX); - sprintf(buffer, "K_ISINVx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '1'+i; - kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX); - kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX); - kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX); - kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX); - kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX); - kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX); - kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX); - kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX); - kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX); - kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX); - kp_bubbleshield[1] = W_CachePatchName("K_ISBUBS", PU_HUDGFX); - kp_flameshield[1] = W_CachePatchName("K_ISFLMS", PU_HUDGFX); - kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX); - kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX); - kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX); - kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX); - - sprintf(buffer, "FSMFSxxx"); - for (i = 0; i < 104; i++) - { - buffer[5] = '0'+((i+1)/100); - buffer[6] = '0'+(((i+1)/10)%10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "FSMBS0xx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter_bg[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // CHECK indicators - sprintf(buffer, "K_CHECKx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '1'+i; - kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Rival indicators - sprintf(buffer, "K_RIVALx"); - for (i = 0; i < 2; i++) - { - buffer[7] = '1'+i; - kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Eggman warning numbers - sprintf(buffer, "K_EGGNx"); - for (i = 0; i < 4; i++) - { - buffer[6] = '0'+i; - kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // First person mode - kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX); - kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX); - kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX); - - // Input UI Wheel - sprintf(buffer, "K_WHEELx"); - for (i = 0; i < 5; i++) - { - buffer[7] = '0'+i; - kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // HERE COMES A NEW CHALLENGER - sprintf(buffer, "K_CHALxx"); - for (i = 0; i < 25; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Lap start animation - sprintf(buffer, "K_LAP0x"); - for (i = 0; i < 7; i++) - { - buffer[6] = '0'+(i+1); - kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPFxx"); - for (i = 0; i < 11; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPNxx"); - for (i = 0; i < 10; i++) - { - buffer[6] = '0'+i; - for (j = 0; j < 3; j++) - { - buffer[7] = '0'+(j+1); - kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - } - - sprintf(buffer, "K_LAPE0x"); - for (i = 0; i < 2; i++) - { - buffer[7] = '0'+(i+1); - kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPH0x"); - for (i = 0; i < 3; i++) - { - buffer[7] = '0'+(i+1); - kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); - kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); - - sprintf(buffer, "ALAGLESx"); - for (i = 0; i < 10; ++i) - { - buffer[7] = '0'+i; - kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "BLAGLESx"); - for (i = 0; i < 6; ++i) - { - buffer[7] = '0'+i; - kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); - - kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX); -} - -// For the item toggle menu -const char *K_GetItemPatch(UINT8 item, boolean tiny) -{ - switch (item) - { - case KITEM_SNEAKER: - case KRITEM_TRIPLESNEAKER: - return (tiny ? "K_ISSHOE" : "K_ITSHOE"); - case KITEM_ROCKETSNEAKER: - return (tiny ? "K_ISRSHE" : "K_ITRSHE"); - case KITEM_INVINCIBILITY: - return (tiny ? "K_ISINV1" : "K_ITINV1"); - case KITEM_BANANA: - case KRITEM_TRIPLEBANANA: - case KRITEM_TENFOLDBANANA: - return (tiny ? "K_ISBANA" : "K_ITBANA"); - case KITEM_EGGMAN: - return (tiny ? "K_ISEGGM" : "K_ITEGGM"); - case KITEM_ORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB1"); - case KITEM_JAWZ: - case KRITEM_DUALJAWZ: - return (tiny ? "K_ISJAWZ" : "K_ITJAWZ"); - case KITEM_MINE: - return (tiny ? "K_ISMINE" : "K_ITMINE"); - case KITEM_BALLHOG: - return (tiny ? "K_ISBHOG" : "K_ITBHOG"); - case KITEM_SPB: - return (tiny ? "K_ISSPB" : "K_ITSPB"); - case KITEM_GROW: - return (tiny ? "K_ISGROW" : "K_ITGROW"); - case KITEM_SHRINK: - return (tiny ? "K_ISSHRK" : "K_ITSHRK"); - case KITEM_THUNDERSHIELD: - return (tiny ? "K_ISTHNS" : "K_ITTHNS"); - case KITEM_BUBBLESHIELD: - return (tiny ? "K_ISBUBS" : "K_ITBUBS"); - case KITEM_FLAMESHIELD: - return (tiny ? "K_ISFLMS" : "K_ITFLMS"); - case KITEM_HYUDORO: - return (tiny ? "K_ISHYUD" : "K_ITHYUD"); - case KITEM_POGOSPRING: - return (tiny ? "K_ISPOGO" : "K_ITPOGO"); - case KITEM_SUPERRING: - return (tiny ? "K_ISRING" : "K_ITRING"); - case KITEM_KITCHENSINK: - return (tiny ? "K_ISSINK" : "K_ITSINK"); - case KRITEM_TRIPLEORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB3"); - case KRITEM_QUADORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB4"); - default: - return (tiny ? "K_ISSAD" : "K_ITSAD"); - } -} - -//} - -INT32 ITEM_X, ITEM_Y; // Item Window -INT32 TIME_X, TIME_Y; // Time Sticker -INT32 LAPS_X, LAPS_Y; // Lap Sticker -INT32 POSI_X, POSI_Y; // Position Number -INT32 FACE_X, FACE_Y; // Top-four Faces -INT32 STCD_X, STCD_Y; // Starting countdown -INT32 CHEK_Y; // CHECK graphic -INT32 MINI_X, MINI_Y; // Minimap -INT32 WANT_X, WANT_Y; // Battle WANTED poster - -// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN. -INT32 ITEM2_X, ITEM2_Y; -INT32 LAPS2_X, LAPS2_Y; -INT32 POSI2_X, POSI2_Y; - - -static void K_initKartHUD(void) -{ - /* - BASEVIDWIDTH = 320 - BASEVIDHEIGHT = 200 - - Item window graphic is 41 x 33 - - Time Sticker graphic is 116 x 11 - Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14 - Therefore, timestamp is 116 x 14 altogether - - Lap Sticker is 80 x 11 - Lap flag is 22 x 20 - Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14 - Therefore, lapstamp is 80 x 20 altogether - - Position numbers are 43 x 53 - - Faces are 32 x 32 - Faces draw downscaled at 16 x 16 - Therefore, the allocated space for them is 16 x 67 altogether - - ---- - - ORIGINAL CZ64 SPLITSCREEN: - - Item window: - if (!splitscreen) { ICONX = 139; ICONY = 20; } - else { ICONX = BASEVIDWIDTH-315; ICONY = 60; } - - Time: 236, STRINGY( 12) - Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189) - - */ - - // Single Screen (defaults) - // Item Window - ITEM_X = 5; // 5 - ITEM_Y = 5; // 5 - // Level Timer - TIME_X = BASEVIDWIDTH - 148; // 172 - TIME_Y = 9; // 9 - // Level Laps - LAPS_X = 9; // 9 - LAPS_Y = BASEVIDHEIGHT - 29; // 171 - // Position Number - POSI_X = BASEVIDWIDTH - 9; // 268 - POSI_Y = BASEVIDHEIGHT - 9; // 138 - // Top-Four Faces - FACE_X = 9; // 9 - FACE_Y = 92; // 92 - // Starting countdown - STCD_X = BASEVIDWIDTH/2; // 9 - STCD_Y = BASEVIDHEIGHT/2; // 92 - // CHECK graphic - CHEK_Y = BASEVIDHEIGHT; // 200 - // Minimap - MINI_X = BASEVIDWIDTH - 50; // 270 - MINI_Y = (BASEVIDHEIGHT/2)-16; // 84 - // Battle WANTED poster - WANT_X = BASEVIDWIDTH - 55; // 270 - WANT_Y = BASEVIDHEIGHT- 71; // 176 - - if (r_splitscreen) // Splitscreen - { - ITEM_X = 5; - ITEM_Y = 3; - - LAPS_Y = (BASEVIDHEIGHT/2)-24; - - POSI_Y = (BASEVIDHEIGHT/2)- 2; - - STCD_Y = BASEVIDHEIGHT/4; - - MINI_Y = (BASEVIDHEIGHT/2); - - if (r_splitscreen > 1) // 3P/4P Small Splitscreen - { - // 1P (top left) - ITEM_X = -9; - ITEM_Y = -8; - - LAPS_X = 3; - LAPS_Y = (BASEVIDHEIGHT/2)-12; - - POSI_X = 24; - POSI_Y = (BASEVIDHEIGHT/2)-26; - - // 2P (top right) - ITEM2_X = BASEVIDWIDTH-39; - ITEM2_Y = -8; - - LAPS2_X = BASEVIDWIDTH-43; - LAPS2_Y = (BASEVIDHEIGHT/2)-12; - - POSI2_X = BASEVIDWIDTH -4; - POSI2_Y = (BASEVIDHEIGHT/2)-26; - - // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom. - - STCD_X = BASEVIDWIDTH/4; - - MINI_X = (3*BASEVIDWIDTH/4); - MINI_Y = (3*BASEVIDHEIGHT/4); - - if (r_splitscreen > 2) // 4P-only - { - MINI_X = (BASEVIDWIDTH/2); - MINI_Y = (BASEVIDHEIGHT/2); - } - } - } - - if (timeinmap > 113) - hudtrans = cv_translucenthud.value; - else if (timeinmap > 105) - hudtrans = ((((INT32)timeinmap) - 105)*cv_translucenthud.value)/(113-105); - else - hudtrans = 0; -} - -INT32 K_calcSplitFlags(INT32 snapflags) -{ - INT32 splitflags = 0; - - if (r_splitscreen == 0) - return snapflags; - - if (stplyr != &players[displayplayers[0]]) - { - if (r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - { - splitflags |= V_SPLITSCREEN; - } - else if (r_splitscreen > 1) - { - if (stplyr == &players[displayplayers[2]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) - splitflags |= V_SPLITSCREEN; - if (stplyr == &players[displayplayers[1]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) - splitflags |= V_HORZSCREEN; - } - } - - if (splitflags & V_SPLITSCREEN) - snapflags &= ~V_SNAPTOTOP; - else - snapflags &= ~V_SNAPTOBOTTOM; - - if (r_splitscreen > 1) - { - if (splitflags & V_HORZSCREEN) - snapflags &= ~V_SNAPTOLEFT; - else - snapflags &= ~V_SNAPTORIGHT; - } - - return (splitflags|snapflags); -} - -static void K_drawKartItem(void) -{ - // ITEM_X = BASEVIDWIDTH-50; // 270 - // ITEM_Y = 24; // 24 - - // Why write V_DrawScaledPatch calls over and over when they're all the same? - // Set to 'no item' just in case. - const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); - patch_t *localpatch = kp_nodraw; - patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); - patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); - INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... - //INT32 splitflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); - const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2); - INT32 itembar = 0; - INT32 maxl = 0; // itembar's normal highest value - const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); - UINT8 localcolor = SKINCOLOR_NONE; - SINT8 colormode = TC_RAINBOW; - UINT8 *colmap = NULL; - boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff - - if (stplyr->kartstuff[k_itemroulette]) - { - if (stplyr->skincolor) - localcolor = stplyr->skincolor; - - switch((stplyr->kartstuff[k_itemroulette] % (15*3)) / 3) - { - // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette - case 0: // Sneaker - localpatch = kp_sneaker[offset]; - //localcolor = SKINCOLOR_RASPBERRY; - break; - case 1: // Banana - localpatch = kp_banana[offset]; - //localcolor = SKINCOLOR_YELLOW; - break; - case 2: // Orbinaut - localpatch = kp_orbinaut[3+offset]; - //localcolor = SKINCOLOR_STEEL; - break; - case 3: // Mine - localpatch = kp_mine[offset]; - //localcolor = SKINCOLOR_JET; - break; - case 4: // Grow - localpatch = kp_grow[offset]; - //localcolor = SKINCOLOR_TEAL; - break; - case 5: // Hyudoro - localpatch = kp_hyudoro[offset]; - //localcolor = SKINCOLOR_STEEL; - break; - case 6: // Rocket Sneaker - localpatch = kp_rocketsneaker[offset]; - //localcolor = SKINCOLOR_TANGERINE; - break; - case 7: // Jawz - localpatch = kp_jawz[offset]; - //localcolor = SKINCOLOR_JAWZ; - break; - case 8: // Self-Propelled Bomb - localpatch = kp_selfpropelledbomb[offset]; - //localcolor = SKINCOLOR_JET; - break; - case 9: // Shrink - localpatch = kp_shrink[offset]; - //localcolor = SKINCOLOR_ORANGE; - break; - case 10: // Invincibility - localpatch = localinv; - //localcolor = SKINCOLOR_GREY; - break; - case 11: // Eggman Monitor - localpatch = kp_eggman[offset]; - //localcolor = SKINCOLOR_ROSE; - break; - case 12: // Ballhog - localpatch = kp_ballhog[offset]; - //localcolor = SKINCOLOR_LILAC; - break; - case 13: // Thunder Shield - localpatch = kp_thundershield[offset]; - //localcolor = SKINCOLOR_CYAN; - break; - case 14: // Super Ring - localpatch = kp_superring[offset]; - //localcolor = SKINCOLOR_GOLD; - break; - /*case 15: // Pogo Spring - localpatch = kp_pogospring[offset]; - localcolor = SKINCOLOR_TANGERINE; - break; - case 16: // Kitchen Sink - localpatch = kp_kitchensink[offset]; - localcolor = SKINCOLOR_STEEL; - break;*/ - default: - break; - } - } - else - { - // I'm doing this a little weird and drawing mostly in reverse order - // The only actual reason is to make sneakers line up this way in the code below - // This shouldn't have any actual baring over how it functions - // Hyudoro is first, because we're drawing it on top of the player's current item - if (stplyr->kartstuff[k_stolentimer] > 0) - { - if (leveltime & 2) - localpatch = kp_hyudoro[offset]; - else - localpatch = kp_nodraw; - } - else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2)) - { - localpatch = kp_hyudoro[offset]; - } - else if (stplyr->kartstuff[k_eggmanexplode] > 1) - { - if (leveltime & 1) - localpatch = kp_eggman[offset]; - else - localpatch = kp_nodraw; - } - else if (stplyr->kartstuff[k_rocketsneakertimer] > 1) - { - itembar = stplyr->kartstuff[k_rocketsneakertimer]; - maxl = (itemtime*3) - barlength; - - if (leveltime & 1) - localpatch = kp_rocketsneaker[offset]; - else - localpatch = kp_nodraw; - } - else if (stplyr->kartstuff[k_sadtimer] > 0) - { - if (leveltime & 2) - localpatch = kp_sadface[offset]; - else - localpatch = kp_nodraw; - } - else - { - if (stplyr->kartstuff[k_itemamount] <= 0) - return; - - switch(stplyr->kartstuff[k_itemtype]) - { - case KITEM_SNEAKER: - localpatch = kp_sneaker[offset]; - break; - case KITEM_ROCKETSNEAKER: - localpatch = kp_rocketsneaker[offset]; - break; - case KITEM_INVINCIBILITY: - localpatch = localinv; - localbg = kp_itembg[offset+1]; - break; - case KITEM_BANANA: - localpatch = kp_banana[offset]; - break; - case KITEM_EGGMAN: - localpatch = kp_eggman[offset]; - break; - case KITEM_ORBINAUT: - localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))]; - break; - case KITEM_JAWZ: - localpatch = kp_jawz[offset]; - break; - case KITEM_MINE: - localpatch = kp_mine[offset]; - break; - case KITEM_BALLHOG: - localpatch = kp_ballhog[offset]; - break; - case KITEM_SPB: - localpatch = kp_selfpropelledbomb[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_GROW: - localpatch = kp_grow[offset]; - break; - case KITEM_SHRINK: - localpatch = kp_shrink[offset]; - break; - case KITEM_THUNDERSHIELD: - localpatch = kp_thundershield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_BUBBLESHIELD: - localpatch = kp_bubbleshield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_FLAMESHIELD: - localpatch = kp_flameshield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_HYUDORO: - localpatch = kp_hyudoro[offset]; - break; - case KITEM_POGOSPRING: - localpatch = kp_pogospring[offset]; - break; - case KITEM_SUPERRING: - localpatch = kp_superring[offset]; - break; - case KITEM_KITCHENSINK: - localpatch = kp_kitchensink[offset]; - break; - case KITEM_SAD: - localpatch = kp_sadface[offset]; - break; - default: - return; - } - - if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1)) - localpatch = kp_nodraw; - } - - if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) - { - colormode = TC_BLINK; - - switch (stplyr->karthud[khud_itemblinkmode]) - { - case 2: - localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - break; - case 1: - localcolor = SKINCOLOR_RED; - break; - default: - localcolor = SKINCOLOR_WHITE; - break; - } - } - } - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = ITEM_X; - fy = ITEM_Y; - fflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); - } - else // now we're having a fun game. - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = ITEM_X; - fy = ITEM_Y; - fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = ITEM2_X; - fy = ITEM2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P4 to the bottom - flipamount = true; - } - } - - if (localcolor != SKINCOLOR_NONE) - colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE); - - V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, localbg); - - // Then, the numbers: - if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette]) - { - V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. - V_DrawFixedPatch(fx<kartstuff[k_itemamount])); - else - V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|fflags, va("x%d", stplyr->kartstuff[k_itemamount])); - else - { - V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|fflags, kp_itemx); - V_DrawKartString(fx+38, fy+36, V_HUDTRANS|fflags, va("%d", stplyr->kartstuff[k_itemamount])); - } - } - else - V_DrawFixedPatch(fx< 2) - { - V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one - if (height == 2) - V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside - V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine - } - } - - // Quick Eggman numbers - if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/) - V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|fflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]); - - if (stplyr->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && stplyr->kartstuff[k_flamelength] > 0) - { - INT32 numframes = 104; - INT32 absolutemax = 16 * flameseg; - INT32 flamemax = stplyr->kartstuff[k_flamelength] * flameseg; - INT32 flamemeter = min(stplyr->kartstuff[k_flamemeter], flamemax); - - INT32 bf = 16 - stplyr->kartstuff[k_flamelength]; - INT32 ff = numframes - ((flamemeter * numframes) / absolutemax); - INT32 fmin = (8 * (bf-1)); - - INT32 xo = 6, yo = 4; - INT32 flip = 0; - - if (offset) - { - xo++; - - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // Flip for P1 and P3 (yes, that's correct) - { - xo -= 62; - flip = V_FLIP; - } - } - - if (ff < fmin) - ff = fmin; - - if (bf >= 0 && bf < 16) - V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter_bg[bf][offset]); - - if (ff >= 0 && ff < numframes && stplyr->kartstuff[k_flamemeter] > 0) - { - if ((stplyr->kartstuff[k_flamemeter] > flamemax) && (leveltime & 1)) - { - UINT8 *fsflash = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); - V_DrawMappedPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset], fsflash); - } - else - { - V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset]); - } - } - } -} - -void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode) -{ - // TIME_X = BASEVIDWIDTH-124; // 196 - // TIME_Y = 6; // 6 - - tic_t worktime; - - INT32 splitflags = 0; - if (!mode) - { - splitflags = V_HUDTRANS|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTORIGHT); - if (cv_timelimit.value && timelimitintics > 0) - { - if (drawtime >= timelimitintics) - drawtime = 0; - else - drawtime = timelimitintics - drawtime; - } - } - - V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide)); - - TX += 33; - - worktime = drawtime/(60*TICRATE); - - if (mode && !drawtime) - V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--")); - else if (worktime < 100) // 99:99:99 only - { - // zero minute - if (worktime < 10) - { - V_DrawKartString(TX, TY+3, splitflags, va("0")); - // minutes time 0 __ __ - V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime)); - } - // minutes time 0 __ __ - else - V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime)); - - // apostrophe location _'__ __ - V_DrawKartString(TX+24, TY+3, splitflags, va("'")); - - worktime = (drawtime/TICRATE % 60); - - // zero second _ 0_ __ - if (worktime < 10) - { - V_DrawKartString(TX+36, TY+3, splitflags, va("0")); - // seconds time _ _0 __ - V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime)); - } - // zero second _ 00 __ - else - V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime)); - - // quotation mark location _ __"__ - V_DrawKartString(TX+60, TY+3, splitflags, va("\"")); - - worktime = G_TicsToCentiseconds(drawtime); - - // zero tick _ __ 0_ - if (worktime < 10) - { - V_DrawKartString(TX+72, TY+3, splitflags, va("0")); - // tics _ __ _0 - V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime)); - } - // zero tick _ __ 00 - else - V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime)); - } - else if ((drawtime/TICRATE) & 1) - V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); - - if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! - { - INT32 workx = TX + 96, worky = TY+18; - SINT8 curemb = 0; - patch_t *emblempic[3] = {NULL, NULL, NULL}; - UINT8 *emblemcol[3] = {NULL, NULL, NULL}; - - emblem_t *emblem = M_GetLevelEmblems(emblemmap); - while (emblem) - { - char targettext[9]; - - switch (emblem->type) - { - case ET_TIME: - { - static boolean canplaysound = true; - tic_t timetoreach = emblem->var; - - if (emblem->collected) - { - emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE); - emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE); - if (++curemb == 3) - break; - goto bademblem; - } - - snprintf(targettext, 9, "%i'%02i\"%02i", - G_TicsToMinutes(timetoreach, false), - G_TicsToSeconds(timetoreach), - G_TicsToCentiseconds(timetoreach)); - - if (!mode) - { - if (stplyr->realtime > timetoreach) - { - splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF; - if (canplaysound) - { - S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks - canplaysound = false; - } - } - else if (!canplaysound) - canplaysound = true; - } - - targettext[8] = 0; - } - break; - default: - goto bademblem; - } - - V_DrawRightAlignedString(workx, worky, splitflags, targettext); - workx -= 67; - V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE)); - - break; - - bademblem: - emblem = M_GetLevelEmblems(-1); - } - - if (!mode) - splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS; - while (curemb--) - { - workx -= 12; - V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]); - } - } -} - -static void K_DrawKartPositionNum(INT32 num) -{ - // POSI_X = BASEVIDWIDTH - 51; // 269 - // POSI_Y = BASEVIDHEIGHT- 64; // 136 - - boolean win = (stplyr->exiting && num == 1); - //INT32 X = POSI_X; - INT32 W = SHORT(kp_positionnum[0][0]->width); - fixed_t scale = FRACUNIT; - patch_t *localpatch = kp_positionnum[0][0]; - //INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT); - INT32 fx = 0, fy = 0, fflags = 0; - boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun. - boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen. - boolean overtake = false; - - if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting) - { - scale *= 2; - overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw. - } - if (r_splitscreen) - scale /= 2; - - W = FixedMul(W<>FRACBITS; - - // pain and suffering defined below - if (!r_splitscreen) - { - fx = POSI_X; - fy = BASEVIDHEIGHT - 8; - fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; - } - else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different. - { - fx = POSI_X; - if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap. - { - fy = 30; - fflags = V_SNAPTOTOP|V_SNAPTORIGHT; - if (overtake) - flipvdraw = true; // make sure overtaking doesn't explode us - } - else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap. - { - fy = BASEVIDHEIGHT - 8; - fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; - } - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = POSI_X; - fy = POSI_Y; - fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - flipdraw = true; - if (num && num >= 10) - fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen. - } - else // else, that means we're P2 or P4. - { - fx = POSI2_X; - fy = POSI2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - } - } - - // Special case for 0 - if (!num) - { - V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers - - // Draw the number - while (num) - { - if (win) // 1st place winner? You get rainbows!! - localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; - else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won - { - // Alternate frame every three frames - switch (leveltime % 9) - { - case 1: case 2: case 3: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][4]; - else - localpatch = kp_positionnum[num % 10][1]; - break; - case 4: case 5: case 6: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][5]; - else - localpatch = kp_positionnum[num % 10][2]; - break; - case 7: case 8: case 9: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][6]; - else - localpatch = kp_positionnum[num % 10][3]; - break; - default: - localpatch = kp_positionnum[num % 10][0]; - break; - } - } - else - localpatch = kp_positionnum[num % 10][0]; - - V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|fflags, localpatch, NULL); - // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen. - // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either. - - fx -= W; - num /= 10; - } -} - -static boolean K_drawKartPositionFaces(void) -{ - // FACE_X = 15; // 15 - // FACE_Y = 72; // 72 - - INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one - INT32 i, j, ranklines, strank = -1; - boolean completed[MAXPLAYERS]; - INT32 rankplayer[MAXPLAYERS]; - INT32 bumperx, numplayersingame = 0; - UINT8 *colormap; - - ranklines = 0; - memset(completed, 0, sizeof (completed)); - memset(rankplayer, 0, sizeof (rankplayer)); - - for (i = 0; i < MAXPLAYERS; i++) - { - rankplayer[i] = -1; - - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - numplayersingame++; - } - - if (numplayersingame <= 1) - return true; - -#ifdef HAVE_BLUA - if (!LUA_HudEnabled(hud_minirankings)) - return false; // Don't proceed but still return true for free play above if HUD is disabled. -#endif - - for (j = 0; j < numplayersingame; j++) - { - UINT8 lowestposition = MAXPLAYERS+1; - for (i = 0; i < MAXPLAYERS; i++) - { - if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - if (players[i].kartstuff[k_position] >= lowestposition) - continue; - - rankplayer[ranklines] = i; - lowestposition = players[i].kartstuff[k_position]; - } - - i = rankplayer[ranklines]; - - completed[i] = true; - - if (players+i == stplyr) - strank = ranklines; - - //if (ranklines == 5) - //break; // Only draw the top 5 players -- we do this a different way now... - - ranklines++; - } - - if (ranklines < 5) - Y -= (9*ranklines); - else - Y -= (9*5); - - if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) - { - i = 0; - if (ranklines > 5) // could be both... - ranklines = 5; - } - else if (strank+3 > ranklines) // too close to the bottom? - { - i = ranklines - 5; - if (i < 0) - i = 0; - } - else - { - i = strank-2; - ranklines = strank+3; - } - - for (; i < ranklines; i++) - { - if (!playeringame[rankplayer[i]]) continue; - if (players[rankplayer[i]].spectator) continue; - if (!players[rankplayer[i]].mo) continue; - - bumperx = FACE_X+19; - - if (players[rankplayer[i]].mo->color) - { - colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); - if (players[rankplayer[i]].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); - - V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap); - -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_battlebumpers)) - { -#endif - if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 0) - { - V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[0], colormap); - for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++) - { - bumperx += 5; - V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[1], colormap); - } - } -#ifdef HAVE_BLUA - } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating: -#endif - } - - if (i == strank) - V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); - - if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0) - V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SNAPTOLEFT, kp_ranknobumpers); - else - { - INT32 pos = players[rankplayer[i]].kartstuff[k_position]; - if (pos < 0 || pos > MAXPLAYERS) - pos = 0; - // Draws the little number over the face - V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SNAPTOLEFT, kp_facenum[pos]); - } - - Y += 18; - } - - return false; -} - -// -// HU_DrawTabRankings -- moved here to take advantage of kart stuff! -// -void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) -{ - static tic_t alagles_timer = 9; - INT32 i, rightoffset = 240; - const UINT8 *colormap; - INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; - int y2; - - //this function is designed for 9 or less score lines only - //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up - - V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice! - if (scorelines > 8) - { - V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides. - V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom. - rightoffset = (BASEVIDWIDTH/2) - 4 - x; - } - - for (i = 0; i < scorelines; i++) - { - char strtime[MAXPLAYERNAME+1]; - - if (players[tab[i].num].spectator || !players[tab[i].num].mo) - continue; //ignore them. - - if (netgame) // don't draw ping offline - { - if (players[tab[i].num].bot) - { - V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu); - } - else if (tab[i].num != serverplayer || !server_lagless) - { - HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); - } - } - - STRBUFCPY(strtime, tab[i].name); - - y2 = y; - - if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) - { - y2 = ( y - 4 ); - - V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); - // every 70 tics - if (( leveltime % 70 ) == 0) - { - alagles_timer = 9; - } - if (alagles_timer > 0) - { - V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); - if (( leveltime % 2 ) == 0) - alagles_timer--; - } - else - V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); - - y2 += SHORT (kp_alagles[0]->height) + 1; - } - - if (scorelines > 8) - V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); - else - V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); - - if (players[tab[i].num].mo->color) - { - colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); - if (players[tab[i].num].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); - - V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap); - /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 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++) - { - bumperx += 5; - V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap); - } - }*/ - } - - if (tab[i].num == whiteplayer) - V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); - - if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0) - V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); - else - { - INT32 pos = players[tab[i].num].kartstuff[k_position]; - if (pos < 0 || pos > MAXPLAYERS) - pos = 0; - // Draws the little number over the face - V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]); - } - - if (G_RaceGametype()) - { -#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) - if (scorelines > 8) - { - if (players[tab[i].num].exiting) - V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); - else if (players[tab[i].num].pflags & PF_TIMEOVER) - V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST."); - else if (circuitmap) - V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count)); - } - else - { - if (players[tab[i].num].exiting) - V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime)); - else if (players[tab[i].num].pflags & PF_TIMEOVER) - V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST."); - else if (circuitmap) - V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count)); - } -#undef timestring - } - else - V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); - - y += 18; - if (i == 7) - { - y = 33; - x = (BASEVIDWIDTH/2) + 4; - } - } -} - -#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) - -static void K_drawKartLapsAndRings(void) -{ - const boolean uselives = G_GametypeUsesLives(); - SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - UINT8 rn[2]; - INT32 ringflip = 0; - UINT8 *ringmap = NULL; - boolean colorring = false; - INT32 ringx = 0; - - rn[0] = ((abs(stplyr->kartstuff[k_rings]) / 10) % 10); - rn[1] = (abs(stplyr->kartstuff[k_rings]) % 10); - - if (stplyr->kartstuff[k_rings] <= 0 && (leveltime/5 & 1)) // In debt - { - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); - colorring = true; - } - else if (stplyr->kartstuff[k_rings] >= 20) // Maxed out - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); - - if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) - { - ringflip = V_FLIP; - ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; - ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); - } - - if (r_splitscreen > 1) - { - INT32 fx = 0, fy = 0, fr = 0; - INT32 flipflag = 0; - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = LAPS_X; - fy = LAPS_Y; - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = LAPS_X; - fy = LAPS_Y; - splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = LAPS2_X; - fy = LAPS2_Y; - splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipflag = V_FLIP; // make the string right aligned and other shit - } - } - - fr = fx; - - // Laps - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - - V_DrawScaledPatch(fx, fy, V_HUDTRANS|splitflags, kp_splitlapflag); - V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); - - if (cv_numlaps.value >= 10) - { - UINT8 ln[2]; - ln[0] = ((stplyr->laps / 10) % 10); - ln[1] = (stplyr->laps % 10); - - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((abs(cv_numlaps.value) / 10) % 10); - ln[1] = (abs(cv_numlaps.value) % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_numlaps.value) % 10]); - } - - // Rings - if (!uselives) - { - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]); - if (flipflag) - fr += 15; - } - else - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[0]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - - V_DrawMappedPatch(fr+ringx, fy-13, V_HUDTRANS|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL)); - - if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt - V_DrawMappedPatch(fr+7, fy-10, V_HUDTRANS|splitflags, kp_ringdebtminussmall, ringmap); - - V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); - V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); - - // SPB ring lock - if (stplyr->kartstuff[k_ringlock]) - V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); - - // Lives - if (uselives) - { - UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); - V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow - } - } - else - { - // Laps - V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_lapsticker); - - if (stplyr->exiting) - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN"); - else - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); - - // Rings - if (!uselives) - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]); - else - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]); - - V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); - - if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt - { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringdebtminus, ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); - } - else - { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); - } - - // SPB ring lock - if (stplyr->kartstuff[k_ringlock]) - V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); - - // Lives - if (uselives) - { - UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap); - V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow - } - } -} - -#undef RINGANIM_NUMFRAMES -#undef RINGANIM_FLIPFRAME - -static void K_drawKartSpeedometer(void) -{ - static fixed_t convSpeed; - UINT8 labeln = 0; - UINT8 numbers[3]; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - UINT8 battleoffset = 0; - - if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! - { - switch (cv_kartspeedometer.value) - { - case 1: // Sonic Drift 2 style percentage - default: - convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) - labeln = 0; - break; - case 2: // Kilometers - convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058 - labeln = 1; - break; - case 3: // Miles - convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774 - labeln = 2; - break; - case 4: // Fracunits - convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT; // 1.0. duh. - labeln = 3; - break; - } - } - - // Don't overflow - if (convSpeed > 999) - convSpeed = 999; - - numbers[0] = ((convSpeed / 100) % 10); - numbers[1] = ((convSpeed / 10) % 10); - numbers[2] = (convSpeed % 10); - - if (G_BattleGametype()) - battleoffset = 8; - - V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometersticker); - V_DrawScaledPatch(LAPS_X+7, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[0]]); - V_DrawScaledPatch(LAPS_X+13, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[1]]); - V_DrawScaledPatch(LAPS_X+19, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[2]]); - V_DrawScaledPatch(LAPS_X+29, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometerlabel[labeln]); -} - -static void K_drawKartBumpersOrKarma(void) -{ - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - - if (r_splitscreen > 1) - { - INT32 fx = 0, fy = 0; - INT32 flipflag = 0; - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = LAPS_X; - fy = LAPS_Y; - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = LAPS_X; - fy = LAPS_Y; - splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = LAPS2_X; - fy = LAPS2_Y; - splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipflag = V_FLIP; // make the string right aligned and other shit - } - } - - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); - - if (battlecapsules) - { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankcapsule, NULL); - - if (numtargets > 9 || maptargets > 9) - { - UINT8 ln[2]; - ln[0] = ((numtargets / 10) % 10); - ln[1] = (numtargets % 10); - - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((maptargets / 10) % 10); - ln[1] = (maptargets % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[numtargets % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[maptargets % 10]); - } - } - else - { - if (stplyr->kartstuff[k_bumper] <= 0) - { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[2]); - } - else - { - INT32 maxbumper = K_StartingBumperCount(); - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(maxbumper) % 10]); - } - } - } - } - else - { - if (battlecapsules) - { - if (numtargets > 9 && maptargets > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulestickerwide, NULL); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulesticker, NULL); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", numtargets, maptargets)); - } - else - { - if (stplyr->kartstuff[k_bumper] <= 0) - { - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); - } - else - { - INT32 maxbumper = K_StartingBumperCount(); - - if (stplyr->kartstuff[k_bumper] > 9 && maxbumper > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap); - - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], maxbumper)); - } - } - } -} - -static void K_drawKartWanted(void) -{ - UINT8 i, numwanted = 0; - UINT8 *colormap = NULL; - INT32 basex = 0, basey = 0; - - if (stplyr != &players[displayplayers[0]]) - return; - - for (i = 0; i < 4; i++) - { - if (battlewanted[i] == -1) - break; - numwanted++; - } - - if (numwanted <= 0) - return; - - // set X/Y coords depending on splitscreen. - if (r_splitscreen < 3) // 1P and 2P use the same code. - { - basex = WANT_X; - basey = WANT_Y; - if (r_splitscreen == 2) - { - basey += 16; // slight adjust for 3P - basex -= 6; - } - } - else if (r_splitscreen == 3) // 4P splitscreen... - { - basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen - basey = BASEVIDHEIGHT - 55; - //basey2 = 4; - } - - if (battlewanted[0] != -1) - colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE); - V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap); - /*if (basey2) - V_DrawFixedPatch(basex< 1 ? 13 : 8), y = basey+(r_splitscreen > 1 ? 16 : 21); - fixed_t scale = FRACUNIT/2; - player_t *p = &players[battlewanted[i]]; - - if (battlewanted[i] == -1) - break; - - if (numwanted == 1) - scale = FRACUNIT; - else - { - if (i & 1) - x += 16; - if (i > 1) - y += 16; - } - - if (players[battlewanted[i]].skincolor) - { - colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE); - V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap); - /*if (basey2) // again with 4p stuff - V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);*/ - } - } -} - -static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) -{ - const INT32 swhalf = (BASEVIDWIDTH / 2); - const fixed_t swhalffixed = swhalf * FRACUNIT; - - const INT32 shhalf = (BASEVIDHEIGHT / 2); - const fixed_t shhalffixed = shhalf * FRACUNIT; - - INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); - fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); - fixed_t factor = INT32_MAX; - - if (abs(anglediff) > ANGLE_90) - { - if (hud_x != NULL) - { - *hud_x = -BASEVIDWIDTH * FRACUNIT; - } - - if (hud_y != NULL) - { - *hud_y = -BASEVIDWIDTH * FRACUNIT; - } - - //*hud_scale = FRACUNIT; - return; - } - - factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); - -#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) - - if (hud_x != NULL) - { - *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; - - if (encoremode) - { - *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; - } - - if (r_splitscreen >= 2) - { - *hud_x /= 2; - - if (camnum & 1) - { - *hud_x += swhalffixed; - } - } - } - - if (hud_y != NULL) - { - *hud_y = campos->z - point->z; - *hud_y = FixedDiv(*hud_y, FixedMul(factor, distance)); - *hud_y = (*hud_y * swhalf) + shhalffixed; - *hud_y = *hud_y + NEWTAN(camaim) * swhalf; - - if (r_splitscreen >= 1) - { - *hud_y /= 2; - - if ((r_splitscreen == 1 && camnum == 1) - || (r_splitscreen > 1 && camnum > 1)) - { - *hud_y += shhalffixed; - } - } - } - - //*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance)); - -#undef NEWTAN -} - -static void K_drawKartPlayerCheck(void) -{ - const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - camera_t *thiscam; - vertex_t c; - UINT8 cnum = 0; - UINT8 i; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); - - if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) - { - return; - } - - if (stplyr->spectator || stplyr->awayviewtics) - { - return; - } - - if (stplyr->cmd.buttons & BT_LOOKBACK) - { - return; - } - - if (r_splitscreen) - { - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - - thiscam = &camera[cnum]; - - c.x = stplyr->mo->x; - c.y = stplyr->mo->y; - c.z = stplyr->mo->z; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *checkplayer = &players[i]; - fixed_t distance = maxdistance+1; - UINT8 *colormap = NULL; - UINT8 pnum = 0; - fixed_t x = 0; - vertex_t v; - - if (!playeringame[i] || checkplayer->spectator) - { - // Not in-game - continue; - } - - if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo)) - { - // No object - continue; - } - - if (checkplayer == stplyr) - { - // This is you! - continue; - } - - v.x = checkplayer->mo->x; - v.y = checkplayer->mo->y; - v.z = checkplayer->mo->z; - - distance = R_PointToDist2(c.x, c.y, v.x, v.y); - - if (distance > maxdistance) - { - // Too far away - continue; - } - - if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) - { - pnum++; // white frames - } - - if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0) - { - pnum += 4; - } - else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer]) - { - pnum += 2; - } - - K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); - - colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); - V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); - } -} - -static boolean K_ShowPlayerNametag(player_t *p) -{ - if (demo.playback == true && demo.freecam == true) - { - return true; - } - - if (stplyr == p) - { - return false; - } - - if (G_RaceGametype()) - { - if ((p->kartstuff[k_position] < stplyr->kartstuff[k_position]-2) - || (p->kartstuff[k_position] > stplyr->kartstuff[k_position]+2)) - { - return false; - } - } - - return true; -} - -static void K_drawKartNameTags(void) -{ - const fixed_t maxdistance = 8192*mapobjectscale; - camera_t *thiscam; - vertex_t c; - UINT8 cnum = 0; - UINT8 i; - - if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) - { - return; - } - - if (stplyr->awayviewtics) - { - return; - } - - if (r_splitscreen) - { - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - - thiscam = &camera[cnum]; - - c.x = thiscam->x; - c.y = thiscam->y; - c.z = thiscam->z; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *ntplayer = &players[i]; - fixed_t distance = maxdistance+1; - - fixed_t x = -BASEVIDWIDTH * FRACUNIT; - fixed_t y = -BASEVIDWIDTH * FRACUNIT; - - vertex_t v; - - if (!playeringame[i] || ntplayer->spectator) - { - // Not in-game - continue; - } - - if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) - { - // No object - continue; - } - - if (!P_CheckSight(stplyr->mo, ntplayer->mo)) - { - // Can't see - continue; - } - - if (!(demo.playback == true && demo.freecam == true)) - { - UINT8 j; - - for (j = 0; j <= r_splitscreen; j++) - { - if (ntplayer == &players[displayplayers[j]]) - { - break; - } - } - - if (j <= r_splitscreen) - { - // This is a player that's being shown on this computer - // (Remove whenever we get splitscreen ABCD indicators) - continue; - } - } - - v.x = ntplayer->mo->x; - v.y = ntplayer->mo->y; - v.z = ntplayer->mo->z; - - if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) - { - v.z += ntplayer->mo->height; - } - - distance = R_PointToDist2(c.x, c.y, v.x, v.y); - - if (distance > maxdistance) - { - // Too far away - continue; - } - - K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); - - if (x == -BASEVIDWIDTH * FRACUNIT) - { - // Off-screen - continue; - } - - if (ntplayer->bot) - { - if (ntplayer->botvars.rival == true) - { - UINT8 blink = ((leveltime / 7) & 1); - V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); - } - } - else if (netgame || demo.playback) - { - if (K_ShowPlayerNametag(ntplayer) == true) - { - INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); - INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); - UINT8 *colormap = V_GetStringColormap(clr); - INT32 barx = 0, bary = 0, barw = 0; - - // Since there's no "V_DrawFixedFill", and I don't feel like making it, - // fuck it, we're gonna just V_NOSCALESTART hack it - barw = (namelen * vid.dupx); - - barx = (x * vid.dupx) / FRACUNIT; - bary = (y * vid.dupy) / FRACUNIT; - - barx += (6 * vid.dupx); - bary -= (16 * vid.dupx); - - // Center it if necessary - if (vid.width != BASEVIDWIDTH * vid.dupx) - { - barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; - } - - if (vid.height != BASEVIDHEIGHT * vid.dupy) - { - bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; - } - - // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. - V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); - V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); - // END DRAWFILL DUMBNESS - - // Draw the stem - V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); - - // Draw the name itself - V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); - } - } - } -} - -static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) -{ - // amnum xpos & ypos are the icon's speed around the HUD. - // The number being divided by is for how fast it moves. - // The higher the number, the slower it moves. - - // am xpos & ypos are the icon's starting position. Withouht - // it, they wouldn't 'spawn' on the top-right side of the HUD. - - fixed_t amnumxpos, amnumypos; - INT32 amxpos, amypos; - - node_t *bsp = &nodes[numnodes-1]; - fixed_t maxx, minx, maxy, miny; - - fixed_t mapwidth, mapheight; - fixed_t xoffset, yoffset; - fixed_t xscale, yscale, zoom; - - maxx = maxy = INT32_MAX; - minx = miny = INT32_MIN; - minx = bsp->bbox[0][BOXLEFT]; - maxx = bsp->bbox[0][BOXRIGHT]; - miny = bsp->bbox[0][BOXBOTTOM]; - maxy = bsp->bbox[0][BOXTOP]; - - if (bsp->bbox[1][BOXLEFT] < minx) - minx = bsp->bbox[1][BOXLEFT]; - if (bsp->bbox[1][BOXRIGHT] > maxx) - maxx = bsp->bbox[1][BOXRIGHT]; - if (bsp->bbox[1][BOXBOTTOM] < miny) - miny = bsp->bbox[1][BOXBOTTOM]; - if (bsp->bbox[1][BOXTOP] > maxy) - maxy = bsp->bbox[1][BOXTOP]; - - // You might be wondering why these are being bitshift here - // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... - // map boundaries and sizes will ALWAYS be whole numbers thankfully - // later calculations take into consideration that these are actually not in terms of FRACUNIT though - minx >>= FRACBITS; - maxx >>= FRACBITS; - miny >>= FRACBITS; - maxy >>= FRACBITS; - - mapwidth = maxx - minx; - mapheight = maxy - miny; - - // These should always be small enough to be bitshift back right now - xoffset = (minx + mapwidth/2)<width, mapwidth); - yscale = FixedDiv(AutomapPic->height, mapheight); - zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); - - amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); - amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); - - if (encoremode) - amnumxpos = -amnumxpos; - - amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width/2); - y = MINI_Y - (AutomapPic->height/2); - - if (timeinmap > 105) - { - minimaptrans = cv_kartminimap.value; - if (timeinmap <= 113) - minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); - if (!minimaptrans) - return; - } - else - return; - - minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); - else - V_DrawScaledPatch(x, y, splitflags, AutomapPic); - - if (!(r_splitscreen == 2)) - { - splitflags &= ~minimaptrans; - splitflags |= V_HUDTRANSHALF; - } - - // let offsets transfer to the heads, too! - if (encoremode) - x += SHORT(AutomapPic->leftoffset); - else - x -= SHORT(AutomapPic->leftoffset); - y -= SHORT(AutomapPic->topoffset); - - // Draw the super item in Battle - if (G_BattleGametype() && battleovertime.enabled) - { - if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) - { - const INT32 prevsplitflags = splitflags; - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); - K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); - splitflags = prevsplitflags; - } - } - - // initialize - for (i = 0; i < 4; i++) - localplayers[i] = -1; - - // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) - if (ghosts) - { - demoghost *g = ghosts; - while (g) - { - if (g->mo->skin) - skin = ((skin_t*)g->mo->skin)-skins; - else - skin = 0; - if (g->mo->color) - { - if (g->mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); - } - else - colormap = NULL; - K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - g = g->next; - } - - if (!stplyr->mo || stplyr->spectator || stplyr->exiting) - return; - - localplayers[numlocalplayers] = stplyr-players; - numlocalplayers++; - } - else - { - for (i = MAXPLAYERS-1; i >= 0; i--) - { - if (!playeringame[i]) - continue; - if (!players[i].mo || players[i].spectator || players[i].exiting) - continue; - - if (i != displayplayers[0] || r_splitscreen) - { - if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) - continue; - - if (players[i].kartstuff[k_hyudorotimer] > 0) - { - if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2 - || players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)) - && !(leveltime & 1))) - continue; - } - } - - if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) - { - // Draw display players on top of everything else - localplayers[numlocalplayers] = i; - numlocalplayers++; - continue; - } - - if (players[i].mo->skin) - skin = ((skin_t*)players[i].mo->skin)-skins; - else - skin = 0; - - if (players[i].mo->color) - { - if (players[i].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); - } - else - colormap = NULL; - - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - // Target reticule - if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } - } - - // draw SPB(s?) - for (mobj = kitemcap; mobj; mobj = next) - { - next = mobj->itnext; - if (mobj->type == MT_SPB) - { - colormap = NULL; - - if (mobj->target && !P_MobjWasRemoved(mobj->target)) - { - if (mobj->player && mobj->player->skincolor) - colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE); - else if (mobj->color) - colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); - } - - K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); - } - } - - // draw our local players here, opaque. - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - - for (i = 0; i < numlocalplayers; i++) - { - if (i == -1) - continue; // this doesn't interest us - - if (players[localplayers[i]].mo->skin) - skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; - else - skin = 0; - - if (players[localplayers[i]].mo->color) - { - if (players[localplayers[i]].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); - } - else - colormap = NULL; - - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - - // Target reticule - if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } -} - -static void K_drawKartStartCountdown(void) -{ - INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3 - - if (leveltime >= starttime-(2*TICRATE)) // 2 - pnum++; - if (leveltime >= starttime-TICRATE) // 1 - pnum++; - if (leveltime >= starttime) // GO! - pnum++; - if ((leveltime % (2*5)) / 5) // blink - pnum += 4; - if (r_splitscreen) // splitscreen - pnum += 8; - - V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); -} - -static void K_drawKartFinish(void) -{ - INT32 pnum = 0, splitflags = K_calcSplitFlags(0); - - if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE) - return; - - if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink - pnum = 1; - - if (r_splitscreen > 1) // 3/4p, stationary FIN - { - pnum += 2; - V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]); - return; - } - - //else -- 1/2p, scrolling FINISH - { - INT32 x, xval; - - if (r_splitscreen) // wide splitscreen - pnum += 4; - - x = ((vid.width<width)<karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; - - if (r_splitscreen && stplyr == &players[displayplayers[1]]) - x = -x; - - V_DrawFixedPatch(x + (STCD_X<>1), - (STCD_Y<height)<<(FRACBITS-1)), - FRACUNIT, - splitflags, kp_racefinish[pnum], NULL); - } -} - -static void K_drawBattleFullscreen(void) -{ - INT32 x = BASEVIDWIDTH/2; - INT32 y = -64+(stplyr->karthud[khud_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen - INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead - fixed_t scale = FRACUNIT; - boolean drawcomebacktimer = true; // lazy hack because it's cleaner in the long run. -#ifdef HAVE_BLUA - if (!LUA_HudEnabled(hud_battlecomebacktimer)) - drawcomebacktimer = false; -#endif - - if (r_splitscreen) - { - if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - || (r_splitscreen > 1 && (stplyr == &players[displayplayers[2]] - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)))) - { - y = 232-(stplyr->karthud[khud_cardanimation]/2); - splitflags = V_SNAPTOBOTTOM; - } - else - y = -32+(stplyr->karthud[khud_cardanimation]/2); - - if (r_splitscreen > 1) - { - scale /= 2; - - if (stplyr == &players[displayplayers[1]] - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) - x = 3*BASEVIDWIDTH/4; - else - x = BASEVIDWIDTH/4; - } - else - { - if (stplyr->exiting) - { - if (stplyr == &players[displayplayers[1]]) - x = BASEVIDWIDTH-96; - else - x = 96; - } - else - scale /= 2; - } - } - - if (stplyr->exiting) - { - if (stplyr == &players[displayplayers[0]]) - V_DrawFadeScreen(0xFF00, 16); - if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) - { - patch_t *p = kp_battlecool; - - if (K_IsPlayerLosing(stplyr)) - p = kp_battlelose; - else if (stplyr->kartstuff[k_position] == 1) - p = kp_battlewin; - - V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator && drawcomebacktimer) - { - UINT16 t = stplyr->kartstuff[k_comebacktimer]/(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; - - txoff = adjust; - - while (t) - { - txoff += adjust; - t /= 10; - } - - if (r_splitscreen) - { - if (r_splitscreen > 1) - ty = (BASEVIDHEIGHT/4)+33; - if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - || (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) - ty += (BASEVIDHEIGHT/2); - } - else - V_DrawFadeScreen(0xFF00, 16); - - if (!comebackshowninfo) - V_DrawFixedPatch(x< 1) - V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE)); - else - { - V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE)); - } - } - - if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY? - { - UINT8 i; - - // check to see if there's anyone else at all - for (i = 0; i < MAXPLAYERS; i++) - { - if (i == displayplayers[0]) - continue; - if (playeringame[i] && !stplyr->spectator) - return; - } - -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_freeplay)) -#endif - K_drawKartFreePlay(leveltime); - } -} - -static void K_drawKartFirstPerson(void) -{ - static INT32 pnum[4], turn[4], drift[4]; - INT32 pn = 0, tn = 0, dr = 0; - INT32 target = 0, splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); - INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT; - fixed_t scale; - UINT8 *colmap = NULL; - ticcmd_t *cmd = &stplyr->cmd; - - if (stplyr->spectator || !stplyr->mo || (stplyr->mo->flags2 & MF2_DONTDRAW)) - return; - - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } - else - { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } - - if (r_splitscreen) - { - y >>= 1; - if (r_splitscreen > 1) - x >>= 1; - } - - { - if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) - y++; - // the following isn't EXPLICITLY right, it just gets the result we want, but i'm too lazy to look up the right way to do it - if (stplyr->mo->flags2 & MF2_SHADOW) - splitflags |= FF_TRANS80; - else if (stplyr->mo->frame & FF_TRANSMASK) - splitflags |= (stplyr->mo->frame & FF_TRANSMASK); - } - - if (cmd->driftturn > 400) // strong left turn - target = 2; - else if (cmd->driftturn < -400) // strong right turn - target = -2; - else if (cmd->driftturn > 0) // weak left turn - target = 1; - else if (cmd->driftturn < 0) // weak right turn - target = -1; - else // forward - target = 0; - - if (encoremode) - target = -target; - - if (pn < target) - pn++; - else if (pn > target) - pn--; - - if (pn < 0) - splitflags |= V_FLIP; // right turn - - target = abs(pn); - if (target > 2) - target = 2; - - x <<= FRACBITS; - y <<= FRACBITS; - - if (tn != cmd->driftturn/50) - tn -= (tn - (cmd->driftturn/50))/8; - - if (dr != stplyr->kartstuff[k_drift]*16) - dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8; - - if (r_splitscreen == 1) - { - scale = (2*FRACUNIT)/3; - y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view) - } - else if (r_splitscreen) - scale = FRACUNIT/2; - else - scale = FRACUNIT; - - if (stplyr->mo) - { - UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->kartstuff[k_driftcharge]); - const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; - // yes, the following is correct. no, you do not need to swap the x and y. - fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); - fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); - - if (r_splitscreen) - xoffs = FixedMul(xoffs, scale); - - xoffs -= (tn)*scale; - xoffs -= (dr)*scale; - - if (stplyr->frameangle == stplyr->mo->angle) - { - const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); - - if (mag < FRACUNIT) - { - xoffs = FixedMul(xoffs, mag); - if (!r_splitscreen) - yoffs = FixedMul(yoffs, mag); - } - } - - if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! - yoffs += stplyr->mo->momz/3; - - if (encoremode) - x -= xoffs; - else - x += xoffs; - if (!r_splitscreen) - y += yoffs; - - - if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks! - colmap = R_GetTranslationColormap(TC_RAINBOW, driftcolor, GTC_CACHE); - else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink! - colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE); - } - - V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap); - - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } - else - { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } -} - -// doesn't need to ever support 4p -static void K_drawInput(void) -{ - static INT32 pn = 0; - INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT); - INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col; - const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5]; - const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9]; - ticcmd_t *cmd = &stplyr->cmd; - - if (timeinmap <= 105) - return; - - if (timeinmap < 113) - { - INT32 count = ((INT32)(timeinmap) - 105); - offs = 64; - while (count-- > 0) - offs >>= 1; - x += offs; - } - -#define BUTTW 8 -#define BUTTH 11 - -#define drawbutt(xoffs, butt, symb)\ - if (stplyr->cmd.buttons & butt)\ - {\ - offs = 2;\ - col = accent1;\ - }\ - else\ - {\ - offs = 0;\ - col = accent2;\ - V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\ - }\ - V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\ - V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn - target = 0; - else // turning of multiple strengths! - { - target = ((abs(cmd->driftturn) - 1)/125)+1; - if (target > 4) - target = 4; - if (cmd->driftturn < 0) - target = -target; - } - - if (pn != target) - { - if (abs(pn - target) == 1) - pn = target; - else if (pn < target) - pn += 2; - else //if (pn > target) - pn -= 2; - } - - if (pn < 0) - { - splitflags |= V_FLIP; // right turn - x--; - } - - target = abs(pn); - if (target > 4) - target = 4; - - if (!stplyr->skincolor) - V_DrawFixedPatch(x<skincolor, GTC_CACHE); - V_DrawFixedPatch(x<karthud[khud_lapanimation]; - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); - - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, - (48 - (32*max(0, progress-76)))*FRACUNIT, - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap); - - if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3) - { - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, - (48 - (32*max(0, progress-76)) - + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT, - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); - } - - if (stplyr->laps == (UINT8)(cv_numlaps.value)) - { - V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_final[min(progress/2, 10)], NULL); - - if (progress/2-12 >= 0) - { - V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_lap[min(progress/2-12, 6)], NULL); - } - } - else - { - V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_lap[min(progress/2, 6)], NULL); - - if (progress/2-8 >= 0) - { - V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps) / 10)][min(progress/2-8, 2)], NULL); - - if (progress/2-10 >= 0) - { - V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps) % 10)][min(progress/2-10, 2)], NULL); - } - } - } -} - -void K_drawKartFreePlay(UINT32 flashtime) -{ - // no splitscreen support because it's not FREE PLAY if you have more than one player in-game - // (you fool, you can take splitscreen online. :V) - - if ((flashtime % TICRATE) < TICRATE/2) - return; - - V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy - LAPS_Y+3, V_HUDTRANS|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); -} - -static void -Draw_party_ping (int ss, INT32 snap) -{ - HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|snap); -} - -static void -K_drawMiniPing (void) -{ - if (cv_showping.value) - { - switch (r_splitscreen) - { - case 3: - Draw_party_ping(3, V_SNAPTORIGHT|V_SPLITSCREEN); - /*FALLTHRU*/ - case 2: - Draw_party_ping(2, V_SPLITSCREEN); - Draw_party_ping(1, V_SNAPTORIGHT); - Draw_party_ping(0, 0); - break; - case 1: - Draw_party_ping(1, V_SNAPTORIGHT|V_SPLITSCREEN); - Draw_party_ping(0, V_SNAPTORIGHT); - break; - } - } -} - -static void K_drawDistributionDebugger(void) -{ - patch_t *items[NUMKARTRESULTS] = { - kp_sadface[1], - kp_sneaker[1], - kp_rocketsneaker[1], - kp_invincibility[7], - kp_banana[1], - kp_eggman[1], - kp_orbinaut[4], - kp_jawz[1], - kp_mine[1], - kp_ballhog[1], - kp_selfpropelledbomb[1], - kp_grow[1], - kp_shrink[1], - kp_thundershield[1], - kp_bubbleshield[1], - kp_flameshield[1], - kp_hyudoro[1], - kp_pogospring[1], - kp_superring[1], - kp_kitchensink[1], - - kp_sneaker[1], - kp_banana[1], - kp_banana[1], - kp_orbinaut[4], - kp_orbinaut[4], - kp_jawz[1] - }; - UINT8 useodds = 0; - UINT8 pingame = 0, bestbumper = 0; - UINT32 pdis = 0; - INT32 i; - INT32 x = -9, y = -9; - boolean spbrush = false; - - if (stplyr != &players[displayplayers[0]]) // only for p1 - return; - - // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - pingame++; - if (players[i].kartstuff[k_bumper] > bestbumper) - bestbumper = players[i].kartstuff[k_bumper]; - } - - // lovely double loop...... - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].kartstuff[k_position] == 1) - { - // This player is first! Yay! - pdis = stplyr->distancetofinish - players[i].distancetofinish; - break; - } - } - - if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items - pdis = (15 * pdis) / 14; - - if (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell - { - pdis = (3 * pdis) / 2; - spbrush = true; - } - - if (stplyr->bot && stplyr->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } - - pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count - - useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); - - for (i = 1; i < NUMKARTRESULTS; i++) - { - const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); - if (itemodds <= 0) - continue; - - V_DrawScaledPatch(x, y, V_HUDTRANS|V_SNAPTOTOP, items[i]); - V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SNAPTOTOP, va("%d", itemodds)); - - // Display amount for multi-items - if (i >= NUMKARTITEMS) - { - INT32 amount; - switch (i) - { - case KRITEM_TENFOLDBANANA: - amount = 10; - break; - case KRITEM_QUADORBINAUT: - amount = 4; - break; - case KRITEM_DUALJAWZ: - amount = 2; - break; - default: - amount = 3; - break; - } - V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SNAPTOTOP, va("x%d", amount)); - } - - x += 32; - if (x >= 297) - { - x = -9; - y += 32; - } - } - - V_DrawString(0, 0, V_HUDTRANS|V_SNAPTOTOP, va("USEODDS %d", useodds)); -} - -static void K_drawCheckpointDebugger(void) -{ - if (stplyr != &players[displayplayers[0]]) // only for p1 - return; - - if (stplyr->starpostnum == numstarposts) - V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts)); - else - V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts)); -} - -static void K_DrawWaypointDebugger(void) -{ - if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) - { - V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); - V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); - } -} - -void K_drawKartHUD(void) -{ - boolean isfreeplay = false; - boolean battlefullscreen = false; - boolean freecam = demo.freecam; //disable some hud elements w/ freecam - UINT8 i; - - // Define the X and Y for each drawn object - // This is handled by console/menu values - K_initKartHUD(); - - // Draw that fun first person HUD! Drawn ASAP so it looks more "real". - for (i = 0; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]] && !camera[i].chase && !freecam) - K_drawKartFirstPerson(); - } - - // Draw full screen stuff that turns off the rest of the HUD - if (mapreset && stplyr == &players[displayplayers[0]]) - { - K_drawChallengerScreen(); - return; - } - - battlefullscreen = ((G_BattleGametype()) - && (stplyr->exiting - || (stplyr->kartstuff[k_bumper] <= 0 - && stplyr->kartstuff[k_comebacktimer] - && comeback - && stplyr->playerstate == PST_LIVE))); - - if (!demo.title && (!battlefullscreen || r_splitscreen)) - { - // Draw the CHECK indicator before the other items, so it's overlapped by everything else -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_check)) // delete lua when? -#endif - if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam) - K_drawKartPlayerCheck(); - - // nametags -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_names)) -#endif - K_drawKartNameTags(); - - // Draw WANTED status - if (G_BattleGametype()) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_wanted)) -#endif - K_drawKartWanted(); - } - - if (cv_kartminimap.value) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_minimap)) -#endif - K_drawKartMinimap(); - } - } - - if (battlefullscreen && !freecam) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_battlefullscreen)) -#endif - K_drawBattleFullscreen(); - return; - } - - // Draw the item window -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_item) && !freecam) -#endif - K_drawKartItem(); - - // If not splitscreen, draw... - if (!r_splitscreen && !demo.title) - { - // Draw the timestamp -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_time)) -#endif - K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0); - - if (!modeattacking) - { - // The top-four faces on the left - /*#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_minirankings)) - #endif*/ - isfreeplay = K_drawKartPositionFaces(); - } - } - - if (!stplyr->spectator && !demo.freecam) // Bottom of the screen elements, don't need in spectate mode - { - // Draw the speedometer - if (cv_kartspeedometer.value && !r_splitscreen) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_speedometer)) -#endif - K_drawKartSpeedometer(); - } - - if (demo.title) // Draw title logo instead in demo.titles - { - INT32 x = BASEVIDWIDTH - 32, y = 128, offs; - - if (r_splitscreen == 3) - { - x = BASEVIDWIDTH/2 + 10; - y = BASEVIDHEIGHT/2 - 30; - } - - if (timeinmap < 113) - { - INT32 count = ((INT32)(timeinmap) - 104); - offs = 256; - while (count-- > 0) - offs >>= 1; - x += offs; - } - - V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); - V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); - } - else if (G_RaceGametype()) // Race-only elements - { - // Draw the lap counter -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartLapsAndRings(); - - if (isfreeplay) - ; - else if (!modeattacking) - { - // Draw the numerical position -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_position)) -#endif - K_DrawKartPositionNum(stplyr->kartstuff[k_position]); - } - else //if (!(demo.playback && hu_showscores)) - { - // Draw the input UI -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_position)) -#endif - K_drawInput(); - } - } - else if (G_BattleGametype()) // Battle-only - { - // Draw the hits left! -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartBumpersOrKarma(); - } - } - - // Draw the countdowns after everything else. - if (leveltime >= starttime-(3*TICRATE) - && leveltime < starttime+TICRATE) - K_drawKartStartCountdown(); - else if (racecountdown && (!r_splitscreen || !stplyr->exiting)) - { - char *countstr = va("%d", racecountdown/TICRATE); - - if (r_splitscreen > 1) - V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, K_calcSplitFlags(0), countstr); - else - { - INT32 karlen = strlen(countstr)*6; // half of 12 - V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, K_calcSplitFlags(0), countstr); - } - } - - // Race overlays - if (G_RaceGametype() && !freecam) - { - if (stplyr->exiting) - K_drawKartFinish(); - else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen) - K_drawLapStartAnim(); - } - - if (modeattacking || freecam) // everything after here is MP and debug only - return; - - if (G_BattleGametype() && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * - V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); - - // Draw FREE PLAY. - if (isfreeplay && !stplyr->spectator && timeinmap > 113) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_freeplay)) -#endif - K_drawKartFreePlay(leveltime); - } - - if (r_splitscreen == 0 && stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) - { - V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); - } - - if (netgame && r_splitscreen) - { - K_drawMiniPing(); - } - - if (cv_kartdebugdistribution.value) - K_drawDistributionDebugger(); - - if (cv_kartdebugcheckpoint.value) - K_drawCheckpointDebugger(); - - if (cv_kartdebugnodes.value) - { - UINT8 p; - for (p = 0; p < MAXPLAYERS; p++) - V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency)); - } - - if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin) - { - INT32 x = 0, y = 0; - UINT8 c; - - for (c = 1; c < MAXSKINCOLORS; c++) - { - UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, c, GTC_CACHE); - V_DrawFixedPatch(x<>1, 0, facewantprefix[stplyr->skin], cm); - - x += 16; - if (x > BASEVIDWIDTH-16) - { - x = 0; - y += 16; - } - } - } - - K_DrawWaypointDebugger(); -} - -//} +// SONIC ROBO BLAST 2 KART ~ ZarroTsu +//----------------------------------------------------------------------------- +/// \file k_kart.c +/// \brief SRB2kart general. +/// All of the SRB2kart-unique stuff. + +#include "k_kart.h" +#include "k_battle.h" +#include "k_pwrlv.h" +#include "k_color.h" +#include "k_respawn.h" +#include "doomdef.h" +#include "hu_stuff.h" +#include "g_game.h" +#include "m_random.h" +#include "p_local.h" +#include "p_slopes.h" +#include "p_setup.h" +#include "r_draw.h" +#include "r_local.h" +#include "s_sound.h" +#include "st_stuff.h" +#include "v_video.h" +#include "z_zone.h" +#include "m_misc.h" +#include "m_cond.h" +#include "f_finale.h" +#include "lua_hud.h" // For Lua hud checks +#include "lua_hook.h" // For MobjDamage and ShouldDamage + +#include "k_waypoint.h" +#include "k_bot.h" + +// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: +// gamespeed is cc (0 for easy, 1 for normal, 2 for hard) +// franticitems is Frantic Mode items, bool +// encoremode is Encore Mode (duh), bool +// comeback is Battle Mode's karma comeback, also bool +// battlewanted is an array of the WANTED player nums, -1 for no player in that slot +// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB +// mapreset is set when enough players fill an empty server + +UINT16 K_GetPlayerDontDrawFlag(player_t *player) +{ + UINT16 flag = 0; + + if (player == &players[displayplayers[0]]) + flag = MFD_DONTDRAWP1; + else if (r_splitscreen >= 1 && player == &players[displayplayers[1]]) + flag = MFD_DONTDRAWP2; + else if (r_splitscreen >= 2 && player == &players[displayplayers[2]]) + flag = MFD_DONTDRAWP3; + else if (r_splitscreen >= 3 && player == &players[displayplayers[3]]) + flag = MFD_DONTDRAWP4; + + return flag; +} + +player_t *K_GetItemBoxPlayer(mobj_t *mobj) +{ + fixed_t closest = INT32_MAX; + player_t *player = NULL; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo) && !players[i].spectator)) + { + continue; + } + + // Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this + if (P_CanPickupItem(&players[i], 1)) + { + fixed_t dist = P_AproxDistance(P_AproxDistance( + players[i].mo->x - mobj->x, + players[i].mo->y - mobj->y), + players[i].mo->z - mobj->z + ); + + if (dist > 8192*mobj->scale) + { + continue; + } + + if (dist < closest) + { + player = &players[i]; + closest = dist; + } + } + } + + return player; +} + +// Angle reflection used by springs & speed pads +angle_t K_ReflectAngle(angle_t yourangle, angle_t theirangle, fixed_t yourspeed, fixed_t theirspeed) +{ + INT32 angoffset; + boolean subtract = false; + + angoffset = yourangle - theirangle; + + if ((angle_t)angoffset > ANGLE_180) + { + // Flip on wrong side + angoffset = InvAngle((angle_t)angoffset); + subtract = !subtract; + } + + // Fix going directly against the spring's angle sending you the wrong way + if ((angle_t)angoffset > ANGLE_90) + { + angoffset = ANGLE_180 - angoffset; + } + + // Offset is reduced to cap it (90 / 2 = max of 45 degrees) + angoffset /= 2; + + // Reduce further based on how slow your speed is compared to the spring's speed + // (set both to 0 to ignore this) + if (theirspeed != 0 && yourspeed != 0) + { + if (theirspeed > yourspeed) + { + angoffset = FixedDiv(angoffset, FixedDiv(theirspeed, yourspeed)); + } + } + + if (subtract) + angoffset = (signed)(theirangle) - angoffset; + else + angoffset = (signed)(theirangle) + angoffset; + + return (angle_t)angoffset; +} + +//{ SRB2kart Net Variables + +void K_RegisterKartStuff(void) +{ + CV_RegisterVar(&cv_sneaker); + CV_RegisterVar(&cv_rocketsneaker); + CV_RegisterVar(&cv_invincibility); + CV_RegisterVar(&cv_banana); + CV_RegisterVar(&cv_eggmanmonitor); + CV_RegisterVar(&cv_orbinaut); + CV_RegisterVar(&cv_jawz); + CV_RegisterVar(&cv_mine); + CV_RegisterVar(&cv_ballhog); + CV_RegisterVar(&cv_selfpropelledbomb); + CV_RegisterVar(&cv_grow); + CV_RegisterVar(&cv_shrink); + CV_RegisterVar(&cv_thundershield); + CV_RegisterVar(&cv_bubbleshield); + CV_RegisterVar(&cv_flameshield); + CV_RegisterVar(&cv_hyudoro); + CV_RegisterVar(&cv_pogospring); + CV_RegisterVar(&cv_superring); + CV_RegisterVar(&cv_kitchensink); + + CV_RegisterVar(&cv_triplesneaker); + CV_RegisterVar(&cv_triplebanana); + CV_RegisterVar(&cv_decabanana); + CV_RegisterVar(&cv_tripleorbinaut); + CV_RegisterVar(&cv_quadorbinaut); + CV_RegisterVar(&cv_dualjawz); + + CV_RegisterVar(&cv_kartminimap); + CV_RegisterVar(&cv_kartcheck); + CV_RegisterVar(&cv_kartinvinsfx); + CV_RegisterVar(&cv_kartspeed); + CV_RegisterVar(&cv_kartbumpers); + CV_RegisterVar(&cv_kartfrantic); + CV_RegisterVar(&cv_kartcomeback); + CV_RegisterVar(&cv_kartencore); + CV_RegisterVar(&cv_kartvoterulechanges); + CV_RegisterVar(&cv_kartspeedometer); + CV_RegisterVar(&cv_kartvoices); + CV_RegisterVar(&cv_kartbot); + CV_RegisterVar(&cv_karteliminatelast); + CV_RegisterVar(&cv_kartusepwrlv); + CV_RegisterVar(&cv_votetime); + + CV_RegisterVar(&cv_kartdebugitem); + CV_RegisterVar(&cv_kartdebugamount); + CV_RegisterVar(&cv_kartdebugshrink); + CV_RegisterVar(&cv_kartallowgiveitem); + CV_RegisterVar(&cv_kartdebugdistribution); + CV_RegisterVar(&cv_kartdebughuddrop); + CV_RegisterVar(&cv_kartdebugwaypoints); + + CV_RegisterVar(&cv_kartdebugcheckpoint); + CV_RegisterVar(&cv_kartdebugnodes); + CV_RegisterVar(&cv_kartdebugcolorize); +} + +//} + +boolean K_IsPlayerLosing(player_t *player) +{ + INT32 winningpos = 1; + UINT8 i, pcount = 0; + + if (battlecapsules && player->kartstuff[k_bumper] <= 0) + return true; // DNF in break the capsules + + if (player->kartstuff[k_position] == 1) + return false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + if (players[i].kartstuff[k_position] > pcount) + pcount = players[i].kartstuff[k_position]; + } + + if (pcount <= 1) + return false; + + winningpos = pcount/2; + if (pcount % 2) // any remainder? + winningpos++; + + return (player->kartstuff[k_position] > winningpos); +} + +fixed_t K_GetKartGameSpeedScalar(SINT8 value) +{ + // Easy = 81.25% + // Normal = 100% + // Hard = 118.75% + // Nightmare = 137.5% ?!?! + return ((13 + (3*value)) << FRACBITS) / 16; +} + +//{ SRB2kart Roulette Code - Position Based + +consvar_t *KartItemCVars[NUMKARTRESULTS-1] = +{ + &cv_sneaker, + &cv_rocketsneaker, + &cv_invincibility, + &cv_banana, + &cv_eggmanmonitor, + &cv_orbinaut, + &cv_jawz, + &cv_mine, + &cv_ballhog, + &cv_selfpropelledbomb, + &cv_grow, + &cv_shrink, + &cv_thundershield, + &cv_bubbleshield, + &cv_flameshield, + &cv_hyudoro, + &cv_pogospring, + &cv_superring, + &cv_kitchensink, + &cv_triplesneaker, + &cv_triplebanana, + &cv_decabanana, + &cv_tripleorbinaut, + &cv_quadorbinaut, + &cv_dualjawz +}; + +#define NUMKARTODDS 80 + +// Less ugly 2D arrays +static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = +{ + //P-Odds 0 1 2 3 4 5 6 7 + /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker + /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker + /*Invincibility*/ { 0, 0, 0, 0, 1, 4, 7, 9 }, // Invincibility + /*Banana*/ { 7, 3, 2, 0, 0, 0, 0, 0 }, // Banana + /*Eggman Monitor*/ { 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor + /*Orbinaut*/ { 7, 4, 3, 2, 0, 0, 0, 0 }, // Orbinaut + /*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz + /*Mine*/ { 0, 2, 2, 1, 0, 0, 0, 0 }, // Mine + /*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog + /*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb + /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow + /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink + /*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield + /*Bubble Shield*/ { 0, 2, 3, 3, 1, 0, 0, 0 }, // Bubble Shield + /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield + /*Hyudoro*/ { 0, 0, 0, 1, 2, 0, 0, 0 }, // Hyudoro + /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring + /*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring + /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + /*Sneaker x3*/ { 0, 0, 0, 2, 6,10, 5, 0 }, // Sneaker x3 + /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 + /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 + /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 + /*Orbinaut x4*/ { 0, 0, 0, 1, 1, 0, 0, 0 }, // Orbinaut x4 + /*Jawz x2*/ { 0, 0, 1, 2, 0, 0, 0, 0 } // Jawz x2 +}; + +static INT32 K_KartItemOddsBattle[NUMKARTRESULTS-1][6] = +{ + //P-Odds 0 1 2 3 4 5 + /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker + /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker + /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility + /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana + /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor + /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut + /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz + /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine + /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog + /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb + /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow + /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink + /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield + /*Bubble Shield*/ { 0, 0, 0, 0, 0, 0 }, // Bubble Shield + /*Flame Shield*/ { 0, 0, 0, 0, 0, 0 }, // Flame Shield + /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro + /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring + /*Super Ring*/ { 0, 0, 0, 0, 0, 0 }, // Super Ring + /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3 + /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3 + /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10 + /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3 + /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4 + /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 +}; + +#define DISTVAR (2048) // Magic number distance for use with item roulette tiers + +INT32 K_GetShieldFromItem(INT32 item) +{ + switch (item) + { + case KITEM_THUNDERSHIELD: return KSHIELD_THUNDER; + case KITEM_BUBBLESHIELD: return KSHIELD_BUBBLE; + case KITEM_FLAMESHIELD: return KSHIELD_FLAME; + default: return KSHIELD_NONE; + } +} + +/** \brief Item Roulette for Kart + + \param player player + \param getitem what item we're looking for + + \return void +*/ +static void K_KartGetItemResult(player_t *player, SINT8 getitem) +{ + if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) // Indirect items + indirectitemcooldown = 20*TICRATE; + + if (getitem == KITEM_HYUDORO) // Hyudoro cooldown + hyubgone = 5*TICRATE; + + player->botvars.itemdelay = TICRATE; + player->botvars.itemconfirm = 0; + + switch (getitem) + { + // Special roulettes first, then the generic ones are handled by default + case KRITEM_TRIPLESNEAKER: // Sneaker x3 + player->kartstuff[k_itemtype] = KITEM_SNEAKER; + player->kartstuff[k_itemamount] = 3; + break; + case KRITEM_TRIPLEBANANA: // Banana x3 + player->kartstuff[k_itemtype] = KITEM_BANANA; + player->kartstuff[k_itemamount] = 3; + break; + case KRITEM_TENFOLDBANANA: // Banana x10 + player->kartstuff[k_itemtype] = KITEM_BANANA; + player->kartstuff[k_itemamount] = 10; + break; + case KRITEM_TRIPLEORBINAUT: // Orbinaut x3 + player->kartstuff[k_itemtype] = KITEM_ORBINAUT; + player->kartstuff[k_itemamount] = 3; + break; + case KRITEM_QUADORBINAUT: // Orbinaut x4 + player->kartstuff[k_itemtype] = KITEM_ORBINAUT; + player->kartstuff[k_itemamount] = 4; + break; + case KRITEM_DUALJAWZ: // Jawz x2 + player->kartstuff[k_itemtype] = KITEM_JAWZ; + player->kartstuff[k_itemamount] = 2; + break; + default: + if (getitem <= 0 || getitem >= NUMKARTRESULTS) // Sad (Fallback) + { + if (getitem != 0) + CONS_Printf("ERROR: P_KartGetItemResult - Item roulette gave bad item (%d) :(\n", getitem); + player->kartstuff[k_itemtype] = KITEM_SAD; + } + else + player->kartstuff[k_itemtype] = getitem; + player->kartstuff[k_itemamount] = 1; + break; + } +} + +/** \brief Item Roulette for Kart + + \param player player object passed from P_KartPlayerThink + + \return void +*/ + +static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) +{ + INT32 newodds; + INT32 i; + UINT8 pingame = 0, pexiting = 0; + SINT8 first = -1, second = -1; + INT32 secondist = 0; + INT32 shieldtype = KSHIELD_NONE; + + I_Assert(item > KITEM_NONE); // too many off by one scenarioes. + I_Assert(KartItemCVars[NUMKARTRESULTS-2] != NULL); // Make sure this exists + + if (!KartItemCVars[item-1]->value && !modeattacking) + return 0; + + if (G_BattleGametype()) + { + I_Assert(pos < 6); // DO NOT allow positions past the bounds of the table + newodds = K_KartItemOddsBattle[item-1][pos]; + } + else + { + I_Assert(pos < 8); // Ditto + newodds = K_KartItemOddsRace[item-1][pos]; + } + + // Base multiplication to ALL item odds to simulate fractional precision + newodds *= 4; + + shieldtype = K_GetShieldFromItem(item); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!G_BattleGametype() || players[i].kartstuff[k_bumper]) + pingame++; + + if (players[i].exiting) + pexiting++; + + if (shieldtype != KSHIELD_NONE && shieldtype == K_GetShieldFromItem(players[i].kartstuff[k_itemtype])) + { + // Don't allow more than one of each shield type at a time + return 0; + } + + if (players[i].mo && G_RaceGametype()) + { + if (players[i].kartstuff[k_position] == 1 && first == -1) + first = i; + if (players[i].kartstuff[k_position] == 2 && second == -1) + second = i; + } + } + + if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB + { + secondist = players[second].distancetofinish - players[first].distancetofinish; + if (franticitems) + secondist = (15 * secondist) / 14; + secondist = ((28 + (8-pingame)) * secondist) / 28; + } + + // POWERITEMODDS handles all of the "frantic item" related functionality, for all of our powerful items. + // First, it multiplies it by 2 if franticitems is true; easy-peasy. + // Next, it multiplies it again if it's in SPB mode and 2nd needs to apply pressure to 1st. + // Then, it multiplies it further if the player count isn't equal to 8. + // This is done to make low player count races more interesting and high player count rates more fair. + // (2P normal would be about halfway between 8P normal and 8P frantic.) + // (This scaling is not done for SPB Rush, so that catchup strength is not weakened.) + // Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, for lesser items needed in a pinch. + +#define PLAYERSCALING (8 - (spbrush ? 2 : pingame)) + +#define POWERITEMODDS(odds) {\ + if (franticitems) \ + odds *= 2; \ + if (rival) \ + odds *= 2; \ + odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \ + if (mashed > 0) \ + odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \ +} + +#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) + + /* + if (bot) + { + // TODO: Item use on bots should all be passed-in functions. + // Instead of manually inserting these, it should return 0 + // for any items without an item use function supplied + + switch (item) + { + case KITEM_SNEAKER: + case KITEM_ROCKETSNEAKER: + case KITEM_INVINCIBILITY: + case KITEM_BANANA: + case KITEM_EGGMAN: + case KITEM_ORBINAUT: + case KITEM_JAWZ: + case KITEM_MINE: + case KITEM_BALLHOG: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + case KITEM_THUNDERSHIELD: + case KITEM_BUBBLESHIELD: + case KITEM_FLAMESHIELD: + case KRITEM_TRIPLESNEAKER: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + case KRITEM_TRIPLEORBINAUT: + case KRITEM_QUADORBINAUT: + case KRITEM_DUALJAWZ: + break; + default: + return 0; + } + } + */ + (void)bot; + + switch (item) + { + case KITEM_ROCKETSNEAKER: + case KITEM_JAWZ: + case KITEM_BALLHOG: + case KRITEM_TRIPLESNEAKER: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + case KRITEM_TRIPLEORBINAUT: + case KRITEM_QUADORBINAUT: + case KRITEM_DUALJAWZ: + POWERITEMODDS(newodds); + break; + case KITEM_INVINCIBILITY: + case KITEM_MINE: + case KITEM_GROW: + case KITEM_BUBBLESHIELD: + case KITEM_FLAMESHIELD: + if (COOLDOWNONSTART) + newodds = 0; + else + POWERITEMODDS(newodds); + break; + case KITEM_SPB: + if ((indirectitemcooldown > 0) || COOLDOWNONSTART + || (first != -1 && players[first].distancetofinish < 8*DISTVAR)) // No SPB near the end of the race + { + newodds = 0; + } + else + { + INT32 multiplier = (secondist - (5*DISTVAR)) / DISTVAR; + + if (multiplier < 0) + multiplier = 0; + if (multiplier > 3) + multiplier = 3; + + newodds *= multiplier; + } + break; + case KITEM_SHRINK: + if ((indirectitemcooldown > 0) || COOLDOWNONSTART || (pingame-1 <= pexiting)) + newodds = 0; + else + POWERITEMODDS(newodds); + break; + case KITEM_THUNDERSHIELD: + if (spbplace != -1 || COOLDOWNONSTART) + newodds = 0; + else + POWERITEMODDS(newodds); + break; + case KITEM_HYUDORO: + if ((hyubgone > 0) || COOLDOWNONSTART) + newodds = 0; + break; + default: + break; + } + +#undef POWERITEMODDS + + return newodds; +} + +//{ SRB2kart Roulette Code - Distance Based, yes waypoints + +static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) +{ + UINT8 i; + UINT8 n = 0; + UINT8 useodds = 0; + UINT8 disttable[14]; + UINT8 totallen = 0; + UINT8 distlen = 0; + boolean oddsvalid[8]; + + for (i = 0; i < 8; i++) + { + UINT8 j; + boolean available = false; + + if (G_BattleGametype() && i > 5) + { + oddsvalid[i] = false; + break; + } + + for (j = 1; j < NUMKARTRESULTS; j++) + { + if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0) + { + available = true; + break; + } + } + + oddsvalid[i] = available; + } + +#define SETUPDISTTABLE(odds, num) \ + if (oddsvalid[odds]) \ + for (i = num; i; --i) \ + disttable[distlen++] = odds; \ + totallen += num; + + if (G_BattleGametype()) // Battle Mode + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,1); + SETUPDISTTABLE(2,1); + SETUPDISTTABLE(3,1); + SETUPDISTTABLE(4,1); + + if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items + useodds = 5; + else + { + SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc + if (K_IsPlayerWanted(player)) + wantedpos++; + if (wantedpos > 4) // Don't run off into karma items + wantedpos = 4; + if (wantedpos < 0) // Don't go below somehow + wantedpos = 0; + n = (wantedpos * distlen) / totallen; + useodds = disttable[n]; + } + } + else + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,1); + SETUPDISTTABLE(2,1); + SETUPDISTTABLE(3,2); + SETUPDISTTABLE(4,2); + SETUPDISTTABLE(5,3); + SETUPDISTTABLE(6,3); + SETUPDISTTABLE(7,1); + + if (pdis == 0) + useodds = disttable[0]; + else if (pdis > DISTVAR * ((12 * distlen) / 14)) + useodds = disttable[distlen-1]; + else + { + for (i = 1; i < 13; i++) + { + if (pdis <= DISTVAR * ((i * distlen) / 14)) + { + useodds = disttable[((i * distlen) / 14)]; + break; + } + } + } + } + +#undef SETUPDISTTABLE + + return useodds; +} + +static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) +{ + INT32 i; + UINT8 pingame = 0; + UINT8 roulettestop; + UINT32 pdis = 0; + UINT8 useodds = 0; + INT32 spawnchance[NUMKARTRESULTS]; + INT32 totalspawnchance = 0; + UINT8 bestbumper = 0; + fixed_t mashed = 0; + boolean dontforcespb = false; + boolean spbrush = false; + + // This makes the roulette cycle through items - if this is 0, you shouldn't be here. + if (player->kartstuff[k_itemroulette]) + player->kartstuff[k_itemroulette]++; + else + return; + + // Gotta check how many players are active at this moment. + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + pingame++; + if (players[i].exiting) + dontforcespb = true; + if (players[i].kartstuff[k_bumper] > bestbumper) + bestbumper = players[i].kartstuff[k_bumper]; + } + + // No forced SPB in 1v1s, it has to be randomly rolled + if (pingame <= 2) + dontforcespb = true; + + // This makes the roulette produce the random noises. + if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player) && !demo.freecam) + { +#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8)) + for (i = 0; i <= r_splitscreen; i++) + { + if (player == &players[displayplayers[i]] && players[displayplayers[i]].kartstuff[k_itemroulette]) + PLAYROULETTESND; + } +#undef PLAYROULETTESND + } + + roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position])); + + // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item. + // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think. + // Finally, if you get past this check, now you can actually start calculating what item you get. + if ((cmd->buttons & BT_ATTACK) && (player->kartstuff[k_itemroulette] >= roulettestop) + && !(player->kartstuff[k_eggmanheld] || player->kartstuff[k_itemheld] || player->kartstuff[k_userings])) + { + // Mashing reduces your chances for the good items + mashed = FixedDiv((player->kartstuff[k_itemroulette])*FRACUNIT, ((TICRATE*3)+roulettestop)*FRACUNIT) - FRACUNIT; + } + else if (!(player->kartstuff[k_itemroulette] >= (TICRATE*3))) + return; + + if (cmd->buttons & BT_ATTACK) + player->pflags |= PF_ATTACKDOWN; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator + && players[i].kartstuff[k_position] == 1) + { + // This player is first! Yay! + + if (player->distancetofinish <= players[i].distancetofinish) + { + // Guess you're in first / tied for first? + pdis = 0; + } + else + { + // Subtract 1st's distance from your distance, to get your distance from 1st! + pdis = player->distancetofinish - players[i].distancetofinish; + } + break; + } + } + + if (mapobjectscale != FRACUNIT) + pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT; + + if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items + { + pdis = (15 * pdis) / 14; + } + + if (spbplace != -1 && player->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell + { + pdis = (3 * pdis) / 2; + spbrush = true; + } + + if (player->bot && player->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count + + // SPECIAL CASE No. 1: + // Fake Eggman items + if (player->kartstuff[k_roulettetype] == 2) + { + player->kartstuff[k_eggmanexplode] = 4*TICRATE; + //player->karthud[khud_itemblink] = TICRATE; + //player->karthud[khud_itemblinkmode] = 1; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player) && !demo.freecam) + S_StartSound(NULL, sfx_itrole); + return; + } + + // SPECIAL CASE No. 2: + // Give a debug item instead if specified + if (cv_kartdebugitem.value != 0 && !modeattacking) + { + K_KartGetItemResult(player, cv_kartdebugitem.value); + player->kartstuff[k_itemamount] = cv_kartdebugamount.value; + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = 2; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player) && !demo.freecam) + S_StartSound(NULL, sfx_dbgsal); + return; + } + + // SPECIAL CASE No. 3: + // Record Attack / alone mashing behavior + if (modeattacking || pingame == 1) + { + if (G_RaceGametype()) + { + if (mashed && (modeattacking || cv_superring.value)) // ANY mashed value? You get rings. + { + K_KartGetItemResult(player, KITEM_SUPERRING); + player->karthud[khud_itemblinkmode] = 1; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolm); + } + else + { + if (modeattacking || cv_sneaker.value) // Waited patiently? You get a sneaker! + K_KartGetItemResult(player, KITEM_SNEAKER); + else // Default to sad if nothing's enabled... + K_KartGetItemResult(player, KITEM_SAD); + player->karthud[khud_itemblinkmode] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolf); + } + } + else if (G_BattleGametype()) + { + if (mashed && (modeattacking || cv_banana.value)) // ANY mashed value? You get a banana. + { + K_KartGetItemResult(player, KITEM_BANANA); + player->karthud[khud_itemblinkmode] = 1; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolm); + } + else + { + if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3! + K_KartGetItemResult(player, KRITEM_TRIPLEORBINAUT); + else // Default to sad if nothing's enabled... + K_KartGetItemResult(player, KITEM_SAD); + player->karthud[khud_itemblinkmode] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolf); + } + } + + player->karthud[khud_itemblink] = TICRATE; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + return; + } + + if (G_RaceGametype()) + { + // SPECIAL CASE No. 4: + // Being in ring debt occasionally forces Super Ring on you if you mashed + if (mashed && player->kartstuff[k_rings] < 0 && cv_superring.value) + { + INT32 debtamount = min(20, abs(player->kartstuff[k_rings])); + if (P_RandomChance((debtamount*FRACUNIT)/20)) + { + K_KartGetItemResult(player, KITEM_SUPERRING); + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = 1; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolm); + return; + } + } + + // SPECIAL CASE No. 5: + // Force SPB onto 2nd if they get too far behind + if (player->kartstuff[k_position] == 2 && pdis > (DISTVAR*8) + && spbplace == -1 && !indirectitemcooldown && !dontforcespb + && cv_selfpropelledbomb.value) + { + K_KartGetItemResult(player, KITEM_SPB); + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = (mashed ? 1 : 0); + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, (mashed ? sfx_itrolm : sfx_itrolf)); + return; + } + } + + // NOW that we're done with all of those specialized cases, we can move onto the REAL item roulette tables. + // Initializes existing spawnchance values + for (i = 0; i < NUMKARTRESULTS; i++) + spawnchance[i] = 0; + + // Split into another function for a debug function below + useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); + + for (i = 1; i < NUMKARTRESULTS; i++) + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival))); + + // Award the player whatever power is rolled + if (totalspawnchance > 0) + { + totalspawnchance = P_RandomKey(totalspawnchance); + for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); + + K_KartGetItemResult(player, i); + } + else + { + player->kartstuff[k_itemtype] = KITEM_SAD; + player->kartstuff[k_itemamount] = 1; + } + + if (P_IsDisplayPlayer(player) && !demo.freecam) + S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); + + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = ((player->kartstuff[k_roulettetype] == 1) ? 2 : (mashed ? 1 : 0)); + + player->kartstuff[k_itemroulette] = 0; // Since we're done, clear the roulette number + player->kartstuff[k_roulettetype] = 0; // This too +} + +//} + +//{ SRB2kart p_user.c Stuff + +static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) +{ + fixed_t weight = 5*FRACUNIT; + + if (!mobj->player) + return weight; + + if (against && !P_MobjWasRemoved(against) && against->player + && ((!against->player->kartstuff[k_spinouttimer] && mobj->player->kartstuff[k_spinouttimer]) // You're in spinout + || (against->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD && mobj->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD))) // They have a Bubble Shield + { + weight = 0; // This player does not cause any bump action + } + else + { + weight = (mobj->player->kartweight) * FRACUNIT; + if (mobj->player->speed > K_GetKartSpeed(mobj->player, false)) + weight += (mobj->player->speed - K_GetKartSpeed(mobj->player, false))/8; + if (mobj->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) + weight += 9*FRACUNIT; + } + + return weight; +} + +fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) +{ + fixed_t weight = 5*FRACUNIT; + + switch (mobj->type) + { + case MT_PLAYER: + if (!mobj->player) + break; + weight = K_PlayerWeight(mobj, against); + break; + case MT_BUBBLESHIELD: + weight = K_PlayerWeight(mobj->target, against); + break; + case MT_FALLINGROCK: + if (against->player) + { + if (against->player->kartstuff[k_invincibilitytimer] || against->player->kartstuff[k_growshrinktimer] > 0) + weight = 0; + else + weight = K_PlayerWeight(against, NULL); + } + break; + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + if (against->player) + weight = K_PlayerWeight(against, NULL); + break; + case MT_JAWZ: + case MT_JAWZ_DUD: + case MT_JAWZ_SHIELD: + if (against->player) + weight = K_PlayerWeight(against, NULL) + (3*FRACUNIT); + else + weight += 3*FRACUNIT; + break; + default: + break; + } + + return FixedMul(weight, mobj->scale); +} + +// This kind of wipeout happens with no rings -- doesn't remove a bumper, has no invulnerability, and is much shorter. +static void K_DebtStingPlayer(player_t *player, INT32 length) +{ + if (player->health <= 0) + return; + + if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 + || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + return; + + player->kartstuff[k_ringboost] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + player->kartstuff[k_spinouttype] = 2; + player->kartstuff[k_spinouttimer] = length; + player->kartstuff[k_wipeoutslow] = min(length-1, wipeoutslowtime+1); + + if (player->mo->state != &states[S_KART_SPIN]) + P_SetPlayerMobjState(player->mo, S_KART_SPIN); + + K_DropHnextList(player, false); + return; +} + +void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) +{ + mobj_t *fx; + fixed_t momdifx, momdify; + fixed_t distx, disty; + fixed_t dot, force; + fixed_t mass1, mass2; + + if (!mobj1 || !mobj2) + return; + + // Don't bump when you're being reborn + if ((mobj1->player && mobj1->player->playerstate != PST_LIVE) + || (mobj2->player && mobj2->player->playerstate != PST_LIVE)) + return; + + if ((mobj1->player && mobj1->player->respawn.state != RESPAWNST_NONE) + || (mobj2->player && mobj2->player->respawn.state != RESPAWNST_NONE)) + return; + + { // Don't bump if you're flashing + INT32 flash; + + flash = K_GetKartFlashing(mobj1->player); + if (mobj1->player && mobj1->player->powers[pw_flashing] > 0 && mobj1->player->powers[pw_flashing] < flash) + { + if (mobj1->player->powers[pw_flashing] < flash-1) + mobj1->player->powers[pw_flashing]++; + return; + } + + flash = K_GetKartFlashing(mobj2->player); + if (mobj2->player && mobj2->player->powers[pw_flashing] > 0 && mobj2->player->powers[pw_flashing] < flash) + { + if (mobj2->player->powers[pw_flashing] < flash-1) + mobj2->player->powers[pw_flashing]++; + return; + } + } + + // Don't bump if you've recently bumped + if (mobj1->player && mobj1->player->kartstuff[k_justbumped]) + { + mobj1->player->kartstuff[k_justbumped] = bumptime; + return; + } + + if (mobj2->player && mobj2->player->kartstuff[k_justbumped]) + { + mobj2->player->kartstuff[k_justbumped] = bumptime; + return; + } + + mass1 = K_GetMobjWeight(mobj1, mobj2); + + if (solid == true && mass1 > 0) + mass2 = mass1; + else + mass2 = K_GetMobjWeight(mobj2, mobj1); + + momdifx = mobj1->momx - mobj2->momx; + momdify = mobj1->momy - mobj2->momy; + + // Adds the OTHER player's momentum times a bunch, for the best chance of getting the correct direction + distx = (mobj1->x + mobj2->momx*3) - (mobj2->x + mobj1->momx*3); + 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; + + { // 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); + fixed_t nx = FixedDiv(distx, dist); + fixed_t ny = FixedDiv(disty, dist); + + dist = dist ? dist : 1; + distx = FixedMul(mobj1->radius+mobj2->radius, nx); + disty = FixedMul(mobj1->radius+mobj2->radius, ny); + + if (momdifx == 0 && momdify == 0) + { + // If there's no momentum difference, they're moving at exactly the same rate. Pretend they moved into each other. + momdifx = -nx; + momdify = -ny; + } + } + + // if the speed difference is less than this let's assume they're going proportionately faster from each other + if (P_AproxDistance(momdifx, momdify) < (25*mapobjectscale)) + { + fixed_t momdiflength = P_AproxDistance(momdifx, momdify); + fixed_t normalisedx = FixedDiv(momdifx, momdiflength); + fixed_t normalisedy = FixedDiv(momdify, momdiflength); + momdifx = FixedMul((25*mapobjectscale), normalisedx); + momdify = FixedMul((25*mapobjectscale), normalisedy); + } + + dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty); + + if (dot >= 0) + { + // They're moving away from each other + return; + } + + force = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty)); + + if (bounce == true && mass2 > 0) // Perform a Goomba Bounce. + mobj1->momz = -mobj1->momz; + else + { + fixed_t newz = mobj1->momz; + if (mass2 > 0) + mobj1->momz = mobj2->momz; + if (mass1 > 0 && solid == false) + mobj2->momz = newz; + } + + if (mass2 > 0) + { + mobj1->momx = mobj1->momx - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), distx); + mobj1->momy = mobj1->momy - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), disty); + } + + if (mass1 > 0 && solid == false) + { + mobj2->momx = mobj2->momx - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -distx); + mobj2->momy = mobj2->momy - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -disty); + } + + // Do the bump fx when we've CONFIRMED we can bump. + if ((mobj1->player && mobj1->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) || (mobj2->player && mobj2->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD)) + S_StartSound(mobj1, sfx_s3k44); + else + S_StartSound(mobj1, sfx_s3k49); + + fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP); + if (mobj1->eflags & MFE_VERTICALFLIP) + fx->eflags |= MFE_VERTICALFLIP; + else + fx->eflags &= ~MFE_VERTICALFLIP; + P_SetScale(fx, mobj1->scale); + + // Because this is done during collision now, rmomx and rmomy need to be recalculated + // so that friction doesn't immediately decide to stop the player if they're at a standstill + // Also set justbumped here + if (mobj1->player) + { + mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx; + mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy; + mobj1->player->kartstuff[k_justbumped] = bumptime; + + if (mobj1->player->kartstuff[k_spinouttimer]) + { + mobj1->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; + mobj1->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj1->player->kartstuff[k_spinouttimer]); + //mobj1->player->kartstuff[k_spinouttype] = 1; // Enforce type + } + else if (mobj2->player // Player VS player bumping only + && (K_GetShieldFromItem(mobj1->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields + { + if (mobj1->player->kartstuff[k_rings] <= 0) + { + K_DebtStingPlayer(mobj1->player, TICRATE + (4 * (mobj2->player->kartweight - mobj1->player->kartweight))); + K_KartPainEnergyFling(mobj1->player); + P_PlayRinglossSound(mobj1); + } + P_PlayerRingBurst(mobj1->player, 1); + } + } + + if (mobj2->player) + { + mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx; + mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy; + mobj2->player->kartstuff[k_justbumped] = bumptime; + + if (mobj2->player->kartstuff[k_spinouttimer]) + { + mobj2->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; + mobj2->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj2->player->kartstuff[k_spinouttimer]); + //mobj2->player->kartstuff[k_spinouttype] = 1; // Enforce type + } + else if (mobj1->player // Player VS player bumping only + && (K_GetShieldFromItem(mobj2->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields + { + if (mobj2->player->kartstuff[k_rings] <= 0) + { + K_DebtStingPlayer(mobj2->player, TICRATE + (4 * (mobj1->player->kartweight - mobj2->player->kartweight))); + K_KartPainEnergyFling(mobj2->player); + P_PlayRinglossSound(mobj2); + } + P_PlayerRingBurst(mobj2->player, 1); + } + } +} + +/** \brief Checks that the player is on an offroad subsector for realsies + + \param mo player mobj object + + \return boolean +*/ +static UINT8 K_CheckOffroadCollide(mobj_t *mo) +{ + UINT8 i; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (i = 2; i < 5; i++) + { + if (P_MobjTouchingSectorSpecial(mo, 1, i, true)) + return i-1; + } + + return 0; +} + +/** \brief Updates the Player's offroad value once per frame + + \param player player object passed from K_KartPlayerThink + + \return void +*/ +static void K_UpdateOffroad(player_t *player) +{ + fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); + + // If you are in offroad, a timer starts. + if (offroadstrength) + { + if (player->kartstuff[k_offroad] < offroadstrength) + player->kartstuff[k_offroad] += offroadstrength / TICRATE; + + if (player->kartstuff[k_offroad] > offroadstrength) + player->kartstuff[k_offroad] = offroadstrength; + } + else + player->kartstuff[k_offroad] = 0; +} + +static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent) +{ +#define CHAOTIXBANDLEN 15 +#define CHAOTIXBANDCOLORS 9 + static const UINT8 colors[CHAOTIXBANDCOLORS] = { + SKINCOLOR_SAPPHIRE, + SKINCOLOR_PLATINUM, + SKINCOLOR_TEA, + SKINCOLOR_GARDEN, + SKINCOLOR_BANANA, + SKINCOLOR_GOLD, + SKINCOLOR_ORANGE, + SKINCOLOR_SCARLET, + SKINCOLOR_TAFFY + }; + fixed_t minimumdist = FixedMul(RING_DIST>>1, player->mo->scale); + UINT8 n = CHAOTIXBANDLEN; + UINT8 offset = ((leveltime / 3) % 3); + fixed_t stepx, stepy, stepz; + fixed_t curx, cury, curz; + UINT8 c; + + if (maxdist == 0) + c = 0; + else + c = FixedMul(CHAOTIXBANDCOLORS<> FRACBITS; + + stepx = (victim->mo->x - player->mo->x) / CHAOTIXBANDLEN; + stepy = (victim->mo->y - player->mo->y) / CHAOTIXBANDLEN; + stepz = ((victim->mo->z + (victim->mo->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN; + + curx = player->mo->x + stepx; + cury = player->mo->y + stepy; + curz = player->mo->z + stepz; + + while (n) + { + if (offset == 0) + { + mobj_t *band = P_SpawnMobj(curx + (P_RandomRange(-12,12)*mapobjectscale), + cury + (P_RandomRange(-12,12)*mapobjectscale), + curz + (P_RandomRange(24,48)*mapobjectscale), + MT_SIGNSPARKLE); + + P_SetMobjState(band, S_SIGNSPARK1 + (leveltime % 11)); + P_SetScale(band, (band->destscale = (3*player->mo->scale)/2)); + + band->color = colors[c]; + band->colorized = true; + + band->fuse = 2; + + if (transparent) + band->drawflags |= MFD_SHADOW; + + band->drawflags |= MFD_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim)); + } + + curx += stepx; + cury += stepy; + curz += stepz; + + offset = abs(offset-1) % 3; + n--; + } +#undef CHAOTIXBANDLEN +} + +/** \brief Updates the player's drafting values once per frame + + \param player player object passed from K_KartPlayerThink + + \return void +*/ +static void K_UpdateDraft(player_t *player) +{ + fixed_t topspd = K_GetKartSpeed(player, false); + fixed_t draftdistance; + UINT8 leniency; + UINT8 i; + + if (player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD) + { + // Flame Shield gets infinite draft distance as its passive effect. + draftdistance = 0; + } + else + { + // Distance you have to be to draft. If you're still accelerating, then this distance is lessened. + // This distance biases toward low weight! (min weight gets 4096 units, max weight gets 3072 units) + // This distance is also scaled based on game speed. + draftdistance = (3072 + (128 * (9 - player->kartweight))) * player->mo->scale; + if (player->speed < topspd) + draftdistance = FixedMul(draftdistance, FixedDiv(player->speed, topspd)); + draftdistance = FixedMul(draftdistance, K_GetKartGameSpeedScalar(gamespeed)); + } + + // On the contrary, the leniency period biases toward high weight. + // (See also: the leniency variable in K_SpawnDraftDust) + leniency = (3*TICRATE)/4 + ((player->kartweight-1) * (TICRATE/4)); + + // Not enough speed to draft. + if (player->speed >= 20*player->mo->scale) + { +//#define EASYDRAFTTEST + // Let's hunt for players to draft off of! + for (i = 0; i < MAXPLAYERS; i++) + { + fixed_t dist, olddraft; +#ifndef EASYDRAFTTEST + angle_t yourangle, theirangle, diff; +#endif + + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + +#ifndef EASYDRAFTTEST + // Don't draft on yourself :V + if (&players[i] == player) + continue; +#endif + + // Not enough speed to draft off of. + if (players[i].speed < 20*players[i].mo->scale) + 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); + + diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle; + if (diff > ANGLE_180) + diff = InvAngle(diff); + + // Not in front of this player. + if (diff > ANG10) + continue; + + diff = yourangle - theirangle; + if (diff > ANGLE_180) + diff = InvAngle(diff); + + // Not moving in the same direction. + if (diff > ANGLE_90) + continue; +#endif + + dist = P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, players[i].mo->y - player->mo->y), players[i].mo->z - player->mo->z); + +#ifndef EASYDRAFTTEST + // TOO close to draft. + if (dist < FixedMul(RING_DIST>>1, player->mo->scale)) + continue; + + // Not close enough to draft. + if (dist > draftdistance && draftdistance > 0) + continue; +#endif + + olddraft = player->kartstuff[k_draftpower]; + + player->kartstuff[k_draftleeway] = leniency; + player->kartstuff[k_lastdraft] = i; + + // Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed. + // How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic) + if (player->kartstuff[k_draftpower] < FRACUNIT) + player->kartstuff[k_draftpower] += (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600)); + + if (player->kartstuff[k_draftpower] > FRACUNIT) + player->kartstuff[k_draftpower] = FRACUNIT; + + // Play draft finish noise + if (olddraft < FRACUNIT && player->kartstuff[k_draftpower] >= FRACUNIT) + S_StartSound(player->mo, sfx_cdfm62); + + // Spawn in the visual! + K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false); + + return; // Finished doing our draft. + } + } + + // No one to draft off of? Then you can knock that off. + if (player->kartstuff[k_draftleeway]) // Prevent small disruptions from stopping your draft. + { + player->kartstuff[k_draftleeway]--; + if (player->kartstuff[k_lastdraft] >= 0 + && player->kartstuff[k_lastdraft] < MAXPLAYERS + && playeringame[player->kartstuff[k_lastdraft]] + && !players[player->kartstuff[k_lastdraft]].spectator + && players[player->kartstuff[k_lastdraft]].mo) + { + player_t *victim = &players[player->kartstuff[k_lastdraft]]; + fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z); + K_DrawDraftCombiring(player, victim, dist, draftdistance, true); + } + } + else // Remove draft speed boost. + { + player->kartstuff[k_draftpower] = 0; + player->kartstuff[k_lastdraft] = -1; + } +} + +void K_KartPainEnergyFling(player_t *player) +{ + static const UINT8 numfling = 5; + INT32 i; + mobj_t *mo; + angle_t fa; + fixed_t ns; + fixed_t z; + + // Better safe than sorry. + if (!player) + return; + + // P_PlayerRingBurst: "There's no ring spilling in kart, so I'm hijacking this for the same thing as TD" + // :oh: + + for (i = 0; i < numfling; i++) + { + INT32 objType = mobjinfo[MT_FLINGENERGY].reactiontime; + fixed_t momxy, momz; // base horizonal/vertical thrusts + + z = player->mo->z; + if (player->mo->eflags & MFE_VERTICALFLIP) + z += player->mo->height - mobjinfo[objType].height; + + mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); + + mo->fuse = 8*TICRATE; + P_SetTarget(&mo->target, player->mo); + + mo->destscale = player->mo->scale; + P_SetScale(mo, player->mo->scale); + + // Angle offset by player angle, then slightly offset by amount of fling + fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT) - ((numfling-1)*FINEANGLES/32)) & FINEMASK; + + if (i > 15) + { + momxy = 3*FRACUNIT; + momz = 4*FRACUNIT; + } + else + { + momxy = 28*FRACUNIT; + momz = 3*FRACUNIT; + } + + ns = FixedMul(momxy, mo->scale); + mo->momx = FixedMul(FINECOSINE(fa),ns); + + ns = momz; + P_SetObjectMomZ(mo, ns, false); + + if (i & 1) + P_SetObjectMomZ(mo, ns, true); + + if (player->mo->eflags & MFE_VERTICALFLIP) + mo->momz *= -1; + } +} + +// Adds gravity flipping to an object relative to its master and shifts the z coordinate accordingly. +void K_FlipFromObject(mobj_t *mo, mobj_t *master) +{ + mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); + mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); + + if (mo->eflags & MFE_VERTICALFLIP) + mo->z += master->height - FixedMul(master->scale, mo->height); +} + +void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) +{ + // flipping + // handle z shifting from there too. This is here since there's no reason not to flip us if needed when we do this anyway; + K_FlipFromObject(mo, master); + + // visibility (usually for hyudoro) + mo->drawflags = (master->drawflags & MFD_DONTDRAW); +} + +// same as above, but does not adjust Z height when flipping +void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) +{ + // flipping + mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); + mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); + + // visibility (usually for hyudoro) + mo->drawflags = (master->drawflags & MFD_DONTDRAW); +} + + +void K_SpawnDashDustRelease(player_t *player) +{ + fixed_t newx; + fixed_t newy; + mobj_t *dust; + angle_t travelangle; + INT32 i; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (!P_IsObjectOnGround(player->mo)) + return; + + if (!player->speed && !player->kartstuff[k_startboost]) + return; + + travelangle = player->mo->angle; + + if (player->kartstuff[k_drift] || player->kartstuff[k_driftend]) + travelangle -= (ANGLE_45/5)*player->kartstuff[k_drift]; + + for (i = 0; i < 2; i++) + { + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); + dust = P_SpawnMobj(newx, newy, player->mo->z, MT_FASTDUST); + + P_SetTarget(&dust->target, player->mo); + dust->angle = travelangle - ((i&1) ? -1 : 1)*ANGLE_45; + dust->destscale = player->mo->scale; + P_SetScale(dust, player->mo->scale); + + dust->momx = 3*player->mo->momx/5; + dust->momy = 3*player->mo->momy/5; + //dust->momz = 3*player->mo->momz/5; + + K_MatchGenericExtraFlags(dust, player->mo); + } +} + +static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the mobj thinker case too! +{ + mobj_t *sparks; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + // Position & etc are handled in its thinker, and its spawned invisible. + // This avoids needing to dupe code if we don't need it. + sparks = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BRAKEDRIFT); + P_SetTarget(&sparks->target, player->mo); + P_SetScale(sparks, (sparks->destscale = player->mo->scale)); + K_MatchGenericExtraFlags(sparks, player->mo); + sparks->drawflags |= MFD_DONTDRAW; +} + +static fixed_t K_RandomFlip(fixed_t f) +{ + return ( ( leveltime & 1 ) ? f : -f ); +} + +void K_SpawnDriftBoostClip(player_t *player) +{ + mobj_t *clip; + fixed_t scale = 115*FRACUNIT/100; + fixed_t z; + + if (( player->mo->eflags & MFE_VERTICALFLIP )) + z = player->mo->z; + else + z = player->mo->z + player->mo->height; + + clip = P_SpawnMobj(player->mo->x, player->mo->y, z, MT_DRIFTCLIP); + + P_SetTarget(&clip->target, player->mo); + P_SetScale(clip, ( clip->destscale = FixedMul(scale, player->mo->scale) )); + K_MatchGenericExtraFlags(clip, player->mo); + + clip->fuse = 105; + clip->momz = 7 * P_MobjFlip(clip) * clip->scale; + + P_InstaThrust(clip, player->mo->angle + + K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), + FixedMul(scale, player->speed)); +} + +void K_SpawnDriftBoostClipSpark(mobj_t *clip) +{ + mobj_t *spark; + + spark = P_SpawnMobj(clip->x, clip->y, clip->z, MT_DRIFTCLIPSPARK); + + P_SetTarget(&spark->target, clip); + P_SetScale(spark, ( spark->destscale = clip->scale )); + K_MatchGenericExtraFlags(spark, clip); + + spark->momx = clip->momx/2; + spark->momy = clip->momx/2; +} + +/** \brief Handles the state changing for moving players, moved here to eliminate duplicate code + + \param player player data + + \return void +*/ +void K_KartMoveAnimation(player_t *player) +{ + const INT16 minturn = KART_FULLTURN/8; + SINT8 turndir = 0; + + const fixed_t fastspeed = (K_GetKartSpeed(player, false) * 17) / 20; // 85% + const fixed_t speedthreshold = player->mo->scale / 8; + + const boolean onground = P_IsObjectOnGround(player->mo); + + ticcmd_t *cmd = &player->cmd; + const boolean spinningwheels = ((cmd->buttons & BT_ACCELERATE) || (onground && player->speed > 0)); + + if (cmd->driftturn < -minturn) + { + turndir = -1; + } + else if (cmd->driftturn > minturn) + { + turndir = 1; + } + + if (!onground) + { + // Only use certain frames in the air, to make it look like your tires are spinning fruitlessly! + + if (player->kartstuff[k_drift] > 0) + { + if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); + } + } + else if (player->kartstuff[k_drift] > 0) + { + if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); + } + } + else + { + if ((turndir == -1) + && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R]))) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST2_R); + } + else if ((turndir == 1) + && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L]))) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST2_L); + } + else if ((turndir == 0) + && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2]))) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST2); + } + } + } + else + { + if (player->kartstuff[k_drift] > 0) + { + // Drifting LEFT! + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_DRIFT1_L_OUT] && player->mo->state <= &states[S_KART_DRIFT2_L_OUT])) + { + // Right -- outwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_OUT); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_DRIFT1_L_IN] && player->mo->state <= &states[S_KART_DRIFT4_L_IN])) + { + // Left -- inwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_IN); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); + } + } + else if (player->kartstuff[k_drift] < 0) + { + // Drifting RIGHT! + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_DRIFT1_R_IN] && player->mo->state <= &states[S_KART_DRIFT4_R_IN])) + { + // Right -- inwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_IN); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_DRIFT1_R_OUT] && player->mo->state <= &states[S_KART_DRIFT2_R_OUT])) + { + // Left -- outwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_OUT); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); + } + } + else + { + if (player->speed >= fastspeed && player->speed >= (player->lastspeed - speedthreshold)) + { + // Going REAL fast! + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R])) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST1_R); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L])) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST1_L); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2])) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST1); + } + } + else + { + if (spinningwheels) + { + // Drivin' slow. + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_SLOW1_R] && player->mo->state <= &states[S_KART_SLOW2_R])) + { + P_SetPlayerMobjState(player->mo, S_KART_SLOW1_R); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_SLOW1_L] && player->mo->state <= &states[S_KART_SLOW2_L])) + { + P_SetPlayerMobjState(player->mo, S_KART_SLOW1_L); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_SLOW1] && player->mo->state <= &states[S_KART_SLOW2])) + { + P_SetPlayerMobjState(player->mo, S_KART_SLOW1); + } + } + else + { + // Completely still. + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_STILL1_R] && player->mo->state <= &states[S_KART_STILL2_R])) + { + P_SetPlayerMobjState(player->mo, S_KART_STILL1_R); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_STILL1_L] && player->mo->state <= &states[S_KART_STILL2_L])) + { + P_SetPlayerMobjState(player->mo, S_KART_STILL1_L); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_STILL1] && player->mo->state <= &states[S_KART_STILL2])) + { + P_SetPlayerMobjState(player->mo, S_KART_STILL1); + } + } + } + } + } + + // Update lastspeed value -- we use to display slow driving frames instead of fast driving when slowing down. + player->lastspeed = player->speed; +} + +static void K_TauntVoiceTimers(player_t *player) +{ + if (!player) + return; + + player->karthud[khud_tauntvoices] = 6*TICRATE; + player->karthud[khud_voices] = 4*TICRATE; +} + +static void K_RegularVoiceTimers(player_t *player) +{ + if (!player) + return; + + player->karthud[khud_voices] = 4*TICRATE; + + if (player->karthud[khud_tauntvoices] < 4*TICRATE) + player->karthud[khud_tauntvoices] = 4*TICRATE; +} + +void K_PlayAttackTaunt(mobj_t *source) +{ + sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons + boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); + + if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) + S_StartSound(source, sfx_kattk1+pick); + + if (!tasteful) + return; + + K_TauntVoiceTimers(source->player); +} + +void K_PlayBoostTaunt(mobj_t *source) +{ + sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons + boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); + + if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) + S_StartSound(source, sfx_kbost1+pick); + + if (!tasteful) + return; + + K_TauntVoiceTimers(source->player); +} + +void K_PlayOvertakeSound(mobj_t *source) +{ + boolean tasteful = (!source->player || !source->player->karthud[khud_voices]); + + if (!G_RaceGametype()) // Only in race + return; + + // 4 seconds from before race begins, 10 seconds afterwards + if (leveltime < starttime+(10*TICRATE)) + return; + + if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) + S_StartSound(source, sfx_kslow); + + if (!tasteful) + return; + + K_RegularVoiceTimers(source->player); +} + +void K_PlayPainSound(mobj_t *source) +{ + sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons + + if (cv_kartvoices.value) + S_StartSound(source, sfx_khurt1 + pick); + + K_RegularVoiceTimers(source->player); +} + +void K_PlayHitEmSound(mobj_t *source) +{ + + if (source->player->follower) + { + follower_t fl = followers[source->player->followerskin]; + source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. + } + + if (cv_kartvoices.value) + S_StartSound(source, sfx_khitem); + else + S_StartSound(source, sfx_s1c9); // The only lost gameplay functionality with voices disabled + + K_RegularVoiceTimers(source->player); +} + +void K_PlayPowerGloatSound(mobj_t *source) +{ + if (cv_kartvoices.value) + S_StartSound(source, sfx_kgloat); + + K_RegularVoiceTimers(source->player); +} + +void K_MomentumToFacing(player_t *player) +{ + angle_t dangle = player->mo->angle - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + + if (dangle > ANGLE_180) + dangle = InvAngle(dangle); + + // If you aren't on the ground or are moving in too different of a direction don't do this + if (player->mo->eflags & MFE_JUSTHITFLOOR) + ; // Just hit floor ALWAYS redirects + else if (!P_IsObjectOnGround(player->mo) || dangle > ANGLE_90) + return; + + P_Thrust(player->mo, player->mo->angle, player->speed - FixedMul(player->speed, player->mo->friction)); + player->mo->momx = FixedMul(player->mo->momx - player->cmomx, player->mo->friction) + player->cmomx; + player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy; +} + +boolean K_ApplyOffroad(player_t *player) +{ + if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer]) + return false; + return true; +} + +static fixed_t K_FlameShieldDashVar(INT32 val) +{ + // 1 second = 75% + 50% top speed + return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); +} + +// sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be +static void K_GetKartBoostPower(player_t *player) +{ + fixed_t boostpower = FRACUNIT; + fixed_t speedboost = 0, accelboost = 0; + UINT8 numboosts = 0; + + if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow] == 1) // Slow down after you've been bumped + { + player->kartstuff[k_boostpower] = player->kartstuff[k_speedboost] = player->kartstuff[k_accelboost] = 0; + return; + } + + // Offroad is separate, it's difficult to factor it in with a variable value anyway. + if (K_ApplyOffroad(player) && player->kartstuff[k_offroad] >= 0) + boostpower = FixedDiv(boostpower, FixedMul(player->kartstuff[k_offroad], K_GetKartGameSpeedScalar(gamespeed)) + FRACUNIT); + + if (player->kartstuff[k_bananadrag] > TICRATE) + boostpower = (4*boostpower)/5; + +#define ADDBOOST(s,a) { \ + numboosts++; \ + speedboost += (s) / numboosts; \ + accelboost += (a) / numboosts; \ +} + + if (player->kartstuff[k_sneakertimer]) // Sneaker + { + UINT8 i; + for (i = 0; i < player->kartstuff[k_numsneakers]; i++) + { + ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration + } + } + + if (player->kartstuff[k_invincibilitytimer]) // Invincibility + { + ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration + } + + if (player->kartstuff[k_flamedash]) // Flame Shield dash + { + ADDBOOST(K_FlameShieldDashVar(player->kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration + } + + if (player->kartstuff[k_startboost]) // Startup Boost + { + ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration + } + + if (player->kartstuff[k_driftboost]) // Drift Boost + { + ADDBOOST(FRACUNIT/4, 4*FRACUNIT); // + 25% top speed, + 400% acceleration + } + + if (player->kartstuff[k_ringboost]) // Ring Boost + { + ADDBOOST(FRACUNIT/5, 4*FRACUNIT); // + 20% top speed, + 400% acceleration + } + + if (player->kartstuff[k_eggmanexplode]) // Ready-to-explode + { + ADDBOOST(3*FRACUNIT/20, FRACUNIT); // + 15% top speed, + 100% acceleration + } + + if (player->kartstuff[k_draftpower] > 0) // Drafting + { + fixed_t draftspeed = ((3*FRACUNIT)/10) + ((player->kartspeed-1) * (FRACUNIT/50)); // min is 30%, max is 46% + speedboost += FixedMul(draftspeed, player->kartstuff[k_draftpower]); // (Drafting suffers no boost stack penalty.) + numboosts++; + } + + player->kartstuff[k_boostpower] = boostpower; + + // value smoothing + if (speedboost > player->kartstuff[k_speedboost]) + { + player->kartstuff[k_speedboost] = speedboost; + } + else + { + player->kartstuff[k_speedboost] += (speedboost - player->kartstuff[k_speedboost]) / (TICRATE/2); + } + + player->kartstuff[k_accelboost] = accelboost; + player->kartstuff[k_numboosts] = numboosts; +} + +// Returns kart speed from a stat. Boost power and scale are NOT taken into account, no player or object is necessary. +fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) +{ + const fixed_t xspd = (3*FRACUNIT)/64; + fixed_t g_cc = K_GetKartGameSpeedScalar(gamespeed) + xspd; + fixed_t k_speed = 150; + fixed_t finalspeed; + + k_speed += kartspeed*3; // 153 - 177 + + finalspeed = FixedMul(k_speed<<14, g_cc); + return finalspeed; +} + +fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) +{ + fixed_t finalspeed; + UINT8 kartspeed = player->kartspeed; + + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + kartspeed = 1; + + finalspeed = K_GetKartSpeedFromStat(kartspeed); + + 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 + + if (player->botvars.rival == true) + { + speedmul += FRACUNIT/10; // +10% for rival + } + + finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul); + } + + if (player->mo && !P_MobjWasRemoved(player->mo)) + finalspeed = FixedMul(finalspeed, player->mo->scale); + + if (doboostpower) + { + if (K_PlayerUsesBotMovement(player)) + { + finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player)); + } + + return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]); + } + + return finalspeed; +} + +fixed_t K_GetKartAccel(player_t *player) +{ + fixed_t k_accel = 32; // 36; + UINT8 kartspeed = player->kartspeed; + + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + kartspeed = 1; + + //k_accel += 3 * (9 - kartspeed); // 36 - 60 + k_accel += 4 * (9 - kartspeed); // 32 - 64 + + + if (K_PlayerUsesBotMovement(player)) + { + // Rubberbanding acceleration is waekened since it makes hits feel more meaningful + fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; + k_accel = FixedMul(k_accel, FRACUNIT + (rubberband/2)); + } + + return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]); +} + +UINT16 K_GetKartFlashing(player_t *player) +{ + UINT16 tics = flashingtics; + + if (!player) + return tics; + + if (G_BattleGametype()) + tics *= 2; + + tics += (flashingtics/8) * (player->kartspeed); + + return tics; +} + +fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove) +{ + const fixed_t accelmax = 4000; + const fixed_t p_speed = K_GetKartSpeed(player, true); + const fixed_t p_accel = K_GetKartAccel(player); + fixed_t newspeed, oldspeed, finalspeed; + fixed_t orig = ORIG_FRICTION; + + if (!onground) return 0; // If the player isn't on the ground, there is no change in speed + + if (K_PlayerUsesBotMovement(player)) + { + orig = K_BotFrictionRubberband(player, ORIG_FRICTION); + } + + // ACCELCODE!!!1!11! + oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale); + // Don't calculate the acceleration as ever being above top speed + if (oldspeed > p_speed) + oldspeed = p_speed; + newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), orig); + + if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust + { + const fixed_t hscale = mapobjectscale /*+ (mapobjectscale - player->mo->scale)*/; + const fixed_t minspeed = 24*hscale; + const fixed_t maxspeed = 28*hscale; + + if (newspeed > maxspeed && player->kartstuff[k_pogospring] == 2) + newspeed = maxspeed; + if (newspeed < minspeed) + newspeed = minspeed; + } + + finalspeed = newspeed - oldspeed; + + // forwardmove is: + // 50 while accelerating, + // 25 while clutching, + // 0 with no gas, and + // -25 when only braking. + + if (player->kartstuff[k_sneakertimer]) + forwardmove = 50; + + finalspeed *= forwardmove/25; + finalspeed /= 2; + + if (forwardmove < 0 && finalspeed > mapobjectscale*2) + return finalspeed/2; + else if (forwardmove < 0) + return -mapobjectscale/2; + + if (finalspeed < 0) + finalspeed = 0; + + return finalspeed; +} + +void K_DoInstashield(player_t *player) +{ + mobj_t *layera; + mobj_t *layerb; + + if (player->kartstuff[k_instashield] > 0) + return; + + player->kartstuff[k_instashield] = 15; // length of instashield animation + S_StartSound(player->mo, sfx_cdpcm9); + + layera = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDA); + P_SetTarget(&layera->target, player->mo); + + layerb = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDB); + P_SetTarget(&layerb->target, player->mo); +} + +void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem) +{ + UINT8 scoremultiply = 1; + // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. +#ifdef HAVE_BLUA + boolean force = false; // Used to check if Lua ShouldSpin should get us damaged reguardless of flashtics or heck knows what. + UINT8 shouldForce = LUAh_ShouldSpin(player, inflictor, source); + if (P_MobjWasRemoved(player->mo)) + return; // mobj was removed (in theory that shouldn't happen) + if (shouldForce == 1) + force = true; + else if (shouldForce == 2) + return; +#else + static const boolean force = false; + (void)inflictor; // in case some weirdo doesn't want Lua. +#endif + + if (!trapitem && G_BattleGametype()) + { + if (K_IsPlayerWanted(player)) + scoremultiply = 3; + else if (player->kartstuff[k_bumper] == 1) + scoremultiply = 2; + } + + if (player->health <= 0) + return; + + if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2) + || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + { + if (!force) // if shoulddamage force, we go THROUGH that. + { + K_DoInstashield(player); + return; + } + } + +#ifdef HAVE_BLUA + if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. + return; +#endif + + if (source && source != player->mo && source->player) + K_PlayHitEmSound(source); + + player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_numsneakers] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; + + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + if (G_BattleGametype()) + { + if (source && source->player && player != source->player) + { + P_AddPlayerScore(source->player, scoremultiply); + K_SpawnBattlePoints(source->player, player, scoremultiply); + if (!trapitem) + { + source->player->kartstuff[k_wanted] -= wantedreduce; + player->kartstuff[k_wanted] -= (wantedreduce/2); + } + } + + 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]) + { + 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(); + } + + player->kartstuff[k_spinouttype] = type; + + if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout, type 2 is no-invuln wipeout + { + // At spinout, player speed is increased to 1/4 their regular speed, moving them forward + if (player->speed < K_GetKartSpeed(player, true)/4) + P_InstaThrust(player->mo, player->mo->angle, FixedMul(K_GetKartSpeed(player, true)/4, player->mo->scale)); + S_StartSound(player->mo, sfx_slip); + } + + player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; + player->powers[pw_flashing] = K_GetKartFlashing(player); + + P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 5); + K_PlayPainSound(player->mo); + + if (player->mo->state != &states[S_KART_SPIN]) + P_SetPlayerMobjState(player->mo, S_KART_SPIN); + + player->kartstuff[k_instashield] = 15; + if (cv_kartdebughuddrop.value && !modeattacking) + K_DropItems(player); + else + K_DropHnextList(player, false); + return; +} + +static void K_RemoveGrowShrink(player_t *player) +{ + if (player->mo && !P_MobjWasRemoved(player->mo)) + { + if (player->kartstuff[k_growshrinktimer] > 0) // Play Shrink noise + S_StartSound(player->mo, sfx_kc59); + else if (player->kartstuff[k_growshrinktimer] < 0) // Play Grow noise + S_StartSound(player->mo, sfx_kc5a); + + if (player->kartstuff[k_invincibilitytimer] == 0) + player->mo->color = player->skincolor; + + player->mo->scalespeed = mapobjectscale/TICRATE; + player->mo->destscale = mapobjectscale; + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + player->mo->destscale = (6*player->mo->destscale)/8; + } + + player->kartstuff[k_growshrinktimer] = 0; + + P_RestoreMusic(player); +} + +void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) +{ + UINT8 scoremultiply = 1; + // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. +#ifdef HAVE_BLUA + boolean force = false; // Used to check if Lua ShouldSquish should get us damaged reguardless of flashtics or heck knows what. + UINT8 shouldForce = LUAh_ShouldSquish(player, inflictor, source); + if (P_MobjWasRemoved(player->mo)) + return; // mobj was removed (in theory that shouldn't happen) + if (shouldForce == 1) + force = true; + else if (shouldForce == 2) + return; +#else + static const boolean force = false; + (void)inflictor; // Please stop forgetting to put inflictor in yer functions thank -Lat' +#endif + + if (G_BattleGametype()) + { + if (K_IsPlayerWanted(player)) + scoremultiply = 3; + else if (player->kartstuff[k_bumper] == 1) + scoremultiply = 2; + } + + if (player->health <= 0) + return; + + if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_invincibilitytimer] > 0 + || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + { + if (!force) // You know the drill by now. + { + K_DoInstashield(player); + return; + } + } + +#ifdef HAVE_BLUA + if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. + return; +#endif + + player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_numsneakers] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; + + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + if (G_BattleGametype()) + { + if (source && source->player && player != source->player) + { + P_AddPlayerScore(source->player, scoremultiply); + K_SpawnBattlePoints(source->player, player, scoremultiply); + source->player->kartstuff[k_wanted] -= wantedreduce; + player->kartstuff[k_wanted] -= (wantedreduce/2); + } + + 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]) + { + 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(); + } + + player->kartstuff[k_squishedtimer] = TICRATE; + + // Reduce Shrink timer + if (player->kartstuff[k_growshrinktimer] < 0) + { + player->kartstuff[k_growshrinktimer] += TICRATE; + if (player->kartstuff[k_growshrinktimer] >= 0) + K_RemoveGrowShrink(player); + } + + player->powers[pw_flashing] = K_GetKartFlashing(player); + + player->mo->flags |= MF_NOCLIP; + + if (player->mo->state != &states[S_KART_SQUISH]) // Squash + P_SetPlayerMobjState(player->mo, S_KART_SQUISH); + + P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 5); + K_PlayPainSound(player->mo); + + player->kartstuff[k_instashield] = 15; + if (cv_kartdebughuddrop.value && !modeattacking) + K_DropItems(player); + else + K_DropHnextList(player, false); + return; +} + +void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer +{ + UINT8 scoremultiply = 1; +#ifdef HAVE_BLUA + boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what. + UINT8 shouldForce = LUAh_ShouldExplode(player, inflictor, source); + + if (P_MobjWasRemoved(player->mo)) + return; // mobj was removed (in theory that shouldn't happen) + if (shouldForce == 1) + force = true; + else if (shouldForce == 2) + return; + +#else + static const boolean force = false; +#endif + + if (G_BattleGametype()) + { + if (K_IsPlayerWanted(player)) + scoremultiply = 3; + else if (player->kartstuff[k_bumper] == 1) + scoremultiply = 2; + } + + if (player->health <= 0) + return; + + if (player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 // Do not check spinout, because SPB and Eggman should combo + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + { + if (!force) // ShouldDamage can bypass that, again. + { + K_DoInstashield(player); + return; + } + } + +#ifdef HAVE_BLUA + if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah + return; +#endif + + if (source && source != player->mo && source->player) + K_PlayHitEmSound(source); + + player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! + player->mo->momx = player->mo->momy = 0; + + player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_numsneakers] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; + + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + // This is the only part that SHOULDN'T combo :VVVVV + if (G_BattleGametype() && !(player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2))) + { + if (source && source->player && player != source->player) + { + P_AddPlayerScore(source->player, scoremultiply); + K_SpawnBattlePoints(source->player, player, scoremultiply); + source->player->kartstuff[k_wanted] -= wantedreduce; + player->kartstuff[k_wanted] -= (wantedreduce/2); + } + + 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]) + { + 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(); + } + + player->kartstuff[k_spinouttype] = 1; + player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; + + player->powers[pw_flashing] = K_GetKartFlashing(player); + + if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1) + { + player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1; + player->mo->momz *= 2; + } + + if (player->mo->eflags & MFE_UNDERWATER) + player->mo->momz = (117 * player->mo->momz) / 200; + + if (player->mo->state != &states[S_KART_SPIN]) + P_SetPlayerMobjState(player->mo, S_KART_SPIN); + + P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 5); + K_PlayPainSound(player->mo); + + if (P_IsDisplayPlayer(player)) + P_StartQuake(64<kartstuff[k_instashield] = 15; + K_DropItems(player); + + return; +} + +void K_StealBumper(player_t *player, player_t *victim, boolean force) +{ + INT32 newbumper; + angle_t newangle, diff; + fixed_t newx, newy; + mobj_t *newmo; + + if (!G_BattleGametype()) + return; + + if (player->health <= 0 || victim->health <= 0) + return; + + if (!force) + { + if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= K_StartingBumperCount()+2 + return; + + if (player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0) + return; + + if (victim->powers[pw_flashing] > 0 || victim->kartstuff[k_squishedtimer] > 0 || (victim->kartstuff[k_spinouttimer] > 0 && victim->kartstuff[k_spinouttype] != 2) + || victim->kartstuff[k_invincibilitytimer] > 0 || victim->kartstuff[k_growshrinktimer] > 0 || victim->kartstuff[k_hyudorotimer] > 0) + { + K_DoInstashield(victim); + return; + } + } + + if (netgame && player->kartstuff[k_bumper] <= 0) + CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); + + newbumper = player->kartstuff[k_bumper]; + 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); + + 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; +} + +// source is the mobj that originally threw the bomb that exploded etc. +// Spawns the sphere around the explosion that handles spinout +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) +{ + mobj_t *mobj; + mobj_t *ghost = NULL; + INT32 i; + TVector v; + TVector *res; + fixed_t finalx, finaly, finalz, dist; + //mobj_t hoopcenter; + angle_t degrees, fa, closestangle; + fixed_t mobjx, mobjy, mobjz; + + //hoopcenter.x = x; + //hoopcenter.y = y; + //hoopcenter.z = z; + + //hoopcenter.z = z - mobjinfo[type].height/2; + + degrees = FINEANGLES/number; + + closestangle = 0; + + // Create the hoop! + for (i = 0; i < number; i++) + { + fa = (i*degrees); + v[0] = FixedMul(FINECOSINE(fa),radius); + v[1] = 0; + v[2] = FixedMul(FINESINE(fa),radius); + v[3] = FRACUNIT; + + res = VectorMatrixMultiply(v, *RotateXMatrix(rotangle)); + M_Memcpy(&v, res, sizeof (v)); + res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle)); + M_Memcpy(&v, res, sizeof (v)); + + finalx = x + v[0]; + finaly = y + v[1]; + finalz = z + v[2]; + + mobj = P_SpawnMobj(finalx, finaly, finalz, type); + + mobj->z -= mobj->height>>1; + + // change angle + mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y); + + // change slope + dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z); + + if (dist < 1) + dist = 1; + + mobjx = mobj->x; + mobjy = mobj->y; + mobjz = mobj->z; + + if (ghostit) + { + ghost = P_SpawnGhostMobj(mobj); + P_SetMobjState(mobj, S_NULL); + mobj = ghost; + } + + if (spawncenter) + { + mobj->x = x; + mobj->y = y; + mobj->z = z; + } + + mobj->momx = FixedMul(FixedDiv(mobjx - x, dist), FixedDiv(dist, 6*FRACUNIT)); + mobj->momy = FixedMul(FixedDiv(mobjy - y, dist), FixedDiv(dist, 6*FRACUNIT)); + mobj->momz = FixedMul(FixedDiv(mobjz - z, dist), FixedDiv(dist, 6*FRACUNIT)); + + if (source && !P_MobjWasRemoved(source)) + P_SetTarget(&mobj->target, source); + } +} + +#define MINEQUAKEDIST 4096 + +// Spawns the purely visual explosion +void K_SpawnMineExplosion(mobj_t *source, UINT8 color) +{ + INT32 i, radius, height; + mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING); + mobj_t *dust; + mobj_t *truc; + INT32 speed, speed2; + INT32 pnum; + player_t *p; + + // check for potential display players near the source so we can have a sick earthquake / flashpal. + for (pnum = 0; pnum < MAXPLAYERS; pnum++) + { + p = &players[pnum]; + + if (!playeringame[pnum] || !P_IsDisplayPlayer(p)) + continue; + + if (R_PointToDist2(p->mo->x, p->mo->y, source->x, source->y) < mapobjectscale*MINEQUAKEDIST) + { + P_StartQuake(55<mo, source)) + { + bombflashtimer = TICRATE*2; + P_FlashPal(p, 1, 1); + } + break; // we can break right now because quakes are global to all split players somehow. + } + } + + K_MatchGenericExtraFlags(smoldering, source); + smoldering->tics = TICRATE*3; + radius = source->radius>>FRACBITS; + height = source->height>>FRACBITS; + + if (!color) + color = SKINCOLOR_KETCHUP; + + for (i = 0; i < 32; i++) + { + dust = P_SpawnMobj(source->x, source->y, source->z, MT_SMOKE); + P_SetMobjState(dust, S_OPAQUESMOKE1); + dust->angle = (ANGLE_180/16) * i; + P_SetScale(dust, source->scale); + dust->destscale = source->scale*10; + dust->scalespeed = source->scale/12; + P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, source->scale)); + + truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, + source->y + P_RandomRange(-radius, radius)*FRACUNIT, + source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMEXPLODE); + K_MatchGenericExtraFlags(truc, source); + P_SetScale(truc, source->scale); + truc->destscale = source->scale*6; + truc->scalespeed = source->scale/12; + speed = FixedMul(10*FRACUNIT, source->scale)>>FRACBITS; + truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; + truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; + speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; + truc->momz = P_RandomRange(-speed, speed)*FRACUNIT*P_MobjFlip(truc); + if (truc->eflags & MFE_UNDERWATER) + truc->momz = (117 * truc->momz) / 200; + truc->color = color; + } + + for (i = 0; i < 16; i++) + { + dust = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, + source->y + P_RandomRange(-radius, radius)*FRACUNIT, + source->z + P_RandomRange(0, height)*FRACUNIT, MT_SMOKE); + P_SetMobjState(dust, S_OPAQUESMOKE1); + P_SetScale(dust, source->scale); + dust->destscale = source->scale*10; + dust->scalespeed = source->scale/12; + dust->tics = 30; + dust->momz = P_RandomRange(FixedMul(3*FRACUNIT, source->scale)>>FRACBITS, FixedMul(7*FRACUNIT, source->scale)>>FRACBITS)*FRACUNIT; + + truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, + source->y + P_RandomRange(-radius, radius)*FRACUNIT, + source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMPARTICLE); + K_MatchGenericExtraFlags(truc, source); + P_SetScale(truc, source->scale); + truc->destscale = source->scale*5; + truc->scalespeed = source->scale/12; + speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; + truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; + truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; + speed = FixedMul(15*FRACUNIT, source->scale)>>FRACBITS; + speed2 = FixedMul(45*FRACUNIT, source->scale)>>FRACBITS; + truc->momz = P_RandomRange(speed, speed2)*FRACUNIT*P_MobjFlip(truc); + if (P_RandomChance(FRACUNIT/2)) + truc->momz = -truc->momz; + if (truc->eflags & MFE_UNDERWATER) + truc->momz = (117 * truc->momz) / 200; + truc->tics = TICRATE*2; + truc->color = color; + } +} + +#undef MINEQUAKEDIST + +static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed) +{ + mobj_t *th; + fixed_t x, y, z; + fixed_t finalspeed = speed; + mobj_t *throwmo; + + if (source->player && source->player->speed > K_GetKartSpeed(source->player, false)) + { + angle_t input = source->angle - an; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + finalspeed = max(speed, FixedMul(speed, FixedMul( + FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed. + (((180<x + source->momx + FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); + y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); + z = source->z; // spawn on the ground please + + if (P_MobjFlip(source) < 0) + { + z = source->z+source->height - mobjinfo[type].height; + } + + th = P_SpawnMobj(x, y, z, type); + + th->flags2 |= flags2; + th->threshold = 10; + + if (th->info->seesound) + S_StartSound(source, th->info->seesound); + + P_SetTarget(&th->target, source); + + P_SetScale(th, source->scale); + th->destscale = source->destscale; + + if (P_IsObjectOnGround(source)) + { + // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn + // This should set it for FOFs + P_TeleportMove(th, th->x, th->y, th->z); + // spawn on the ground if the player is on the ground + if (P_MobjFlip(source) < 0) + { + th->z = th->ceilingz - th->height; + th->eflags |= MFE_VERTICALFLIP; + } + else + th->z = th->floorz; + } + + th->angle = an; + th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); + th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); + + switch (type) + { + case MT_ORBINAUT: + if (source && source->player) + th->color = source->player->skincolor; + else + th->color = SKINCOLOR_GREY; + th->movefactor = finalspeed; + break; + case MT_JAWZ: + if (source && source->player) + { + INT32 lasttarg = source->player->kartstuff[k_lastjawztarget]; + th->cvmem = source->player->skincolor; + if ((lasttarg >= 0 && lasttarg < MAXPLAYERS) + && playeringame[lasttarg] + && !players[lasttarg].spectator + && players[lasttarg].mo) + { + P_SetTarget(&th->tracer, players[lasttarg].mo); + } + } + else + th->cvmem = SKINCOLOR_KETCHUP; + /* FALLTHRU */ + case MT_JAWZ_DUD: + S_StartSound(th, th->info->activesound); + /* FALLTHRU */ + case MT_SPB: + th->movefactor = finalspeed; + break; + case MT_BUBBLESHIELDTRAP: + P_SetScale(th, ((5*th->destscale)>>2)*4); + th->destscale = (5*th->destscale)>>2; + S_StartSound(th, sfx_s3kbfl); + S_StartSound(th, sfx_cdfm35); + break; + default: + break; + } + + if (type != MT_BUBBLESHIELDTRAP) + { + x = x + P_ReturnThrustX(source, an, source->radius + th->radius); + y = y + P_ReturnThrustY(source, an, source->radius + th->radius); + throwmo = P_SpawnMobj(x, y, z, MT_FIREDITEM); + throwmo->movecount = 1; + throwmo->movedir = source->angle - an; + P_SetTarget(&throwmo->target, source); + } + + return NULL; +} + +UINT8 K_DriftSparkColor(player_t *player, INT32 charge) +{ + INT32 ds = K_GetKartDriftSparkValue(player); + UINT8 color = SKINCOLOR_NONE; + + if (charge < 0) + { + // Stage 0: Yellow + color = SKINCOLOR_GOLD; + } + else if (charge >= ds*4) + { + // Stage 3: Rainbow + if (charge <= (ds*4)+(32*3)) + { + // transition + color = SKINCOLOR_SILVER; + } + else + { + color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + } + } + else if (charge >= ds*2) + { + // Stage 2: Blue + if (charge <= (ds*2)+(32*3)) + { + // transition + color = SKINCOLOR_PURPLE; + } + else + { + color = SKINCOLOR_SAPPHIRE; + } + } + else if (charge >= ds) + { + // Stage 1: Red + if (charge <= (ds)+(32*3)) + { + // transition + color = SKINCOLOR_TANGERINE; + } + else + { + color = SKINCOLOR_KETCHUP; + } + } + + return color; +} + +static void K_SpawnDriftSparks(player_t *player) +{ + INT32 ds = K_GetKartDriftSparkValue(player); + fixed_t newx; + fixed_t newy; + mobj_t *spark; + angle_t travelangle; + INT32 i; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (leveltime % 2 == 1) + return; + + if (!player->kartstuff[k_drift] + || (player->kartstuff[k_driftcharge] < ds && !(player->kartstuff[k_driftcharge] < 0))) + return; + + travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; + + for (i = 0; i < 2; i++) + { + SINT8 size = 1; + UINT8 trail = 0; + + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); + spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK); + + P_SetTarget(&spark->target, player->mo); + spark->angle = travelangle-(ANGLE_45/5)*player->kartstuff[k_drift]; + spark->destscale = player->mo->scale; + P_SetScale(spark, player->mo->scale); + + spark->momx = player->mo->momx/2; + spark->momy = player->mo->momy/2; + //spark->momz = player->mo->momz/2; + + spark->color = K_DriftSparkColor(player, player->kartstuff[k_driftcharge]); + + if (player->kartstuff[k_driftcharge] < 0) + { + // Stage 0: Yellow + size = 0; + } + else if (player->kartstuff[k_driftcharge] >= ds*4) + { + // Stage 3: Rainbow + size = 2; + trail = 2; + + if (player->kartstuff[k_driftcharge] <= (ds*4)+(32*3)) + { + // transition + P_SetScale(spark, (spark->destscale = spark->scale*3/2)); + } + else + { + spark->colorized = true; + } + } + else if (player->kartstuff[k_driftcharge] >= ds*2) + { + // Stage 2: Blue + size = 2; + trail = 1; + + if (player->kartstuff[k_driftcharge] <= (ds*2)+(32*3)) + { + // transition + P_SetScale(spark, (spark->destscale = spark->scale*3/2)); + } + } + else + { + // Stage 1: Red + size = 1; + + if (player->kartstuff[k_driftcharge] <= (ds)+(32*3)) + { + // transition + P_SetScale(spark, (spark->destscale = spark->scale*2)); + } + } + + if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts + || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn < 0)) + { + if ((player->kartstuff[k_drift] < 0 && (i & 1)) + || (player->kartstuff[k_drift] > 0 && !(i & 1))) + { + size++; + } + else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) + || (player->kartstuff[k_drift] > 0 && (i & 1))) + { + size--; + } + } + else if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn < 0) // Outward drifts + || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn > 0)) + { + if ((player->kartstuff[k_drift] < 0 && (i & 1)) + || (player->kartstuff[k_drift] > 0 && !(i & 1))) + { + size--; + } + else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) + || (player->kartstuff[k_drift] > 0 && (i & 1))) + { + size++; + } + } + + if (size == 2) + P_SetMobjState(spark, S_DRIFTSPARK_A1); + else if (size < 1) + P_SetMobjState(spark, S_DRIFTSPARK_C1); + else if (size > 2) + P_SetMobjState(spark, S_DRIFTSPARK_D1); + + if (trail > 0) + spark->tics += trail; + + K_MatchGenericExtraFlags(spark, player->mo); + } +} + +static void K_SpawnAIZDust(player_t *player) +{ + fixed_t newx; + fixed_t newy; + mobj_t *spark; + angle_t travelangle; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (leveltime % 2 == 1) + return; + + if (!P_IsObjectOnGround(player->mo)) + return; + + travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + //S_StartSound(player->mo, sfx_s3k47); + + { + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); + spark = P_SpawnMobj(newx, newy, player->mo->z, MT_AIZDRIFTSTRAT); + + spark->angle = travelangle+(player->kartstuff[k_aizdriftstrat]*ANGLE_90); + P_SetScale(spark, (spark->destscale = (3*player->mo->scale)>>2)); + + spark->momx = (6*player->mo->momx)/5; + spark->momy = (6*player->mo->momy)/5; + //spark->momz = player->mo->momz/2; + + K_MatchGenericExtraFlags(spark, player->mo); + } +} + +void K_SpawnBoostTrail(player_t *player) +{ + fixed_t newx; + fixed_t newy; + fixed_t ground; + mobj_t *flame; + angle_t travelangle; + INT32 i; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (!P_IsObjectOnGround(player->mo) + || player->kartstuff[k_hyudorotimer] != 0 + || (G_BattleGametype() && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer])) + return; + + if (player->mo->eflags & MFE_VERTICALFLIP) + ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); + else + ground = player->mo->floorz; + + if (player->kartstuff[k_drift] != 0) + travelangle = player->mo->angle; + else + travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); + + for (i = 0; i < 2; i++) + { + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); +#ifdef ESLOPE + if (player->mo->standingslope) + { + ground = P_GetZAt(player->mo->standingslope, newx, newy); + if (player->mo->eflags & MFE_VERTICALFLIP) + ground -= FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); + } +#endif + flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL); + + P_SetTarget(&flame->target, player->mo); + flame->angle = travelangle; + flame->fuse = TICRATE*2; + flame->destscale = player->mo->scale; + P_SetScale(flame, player->mo->scale); + // not K_MatchGenericExtraFlags so that a stolen sneaker can be seen + K_FlipFromObject(flame, player->mo); + + flame->momx = 8; + P_XYMovement(flame); + if (P_MobjWasRemoved(flame)) + continue; + + if (player->mo->eflags & MFE_VERTICALFLIP) + { + if (flame->z + flame->height < flame->ceilingz) + P_RemoveMobj(flame); + } + else if (flame->z > flame->floorz) + P_RemoveMobj(flame); + } +} + +void K_SpawnSparkleTrail(mobj_t *mo) +{ + const INT32 rad = (mo->radius*2)>>FRACBITS; + mobj_t *sparkle; + INT32 i; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (i = 0; i < 3; i++) + { + fixed_t newx = mo->x + mo->momx + (P_RandomRange(-rad, rad)<y + mo->momy + (P_RandomRange(-rad, rad)<z + mo->momz + (P_RandomRange(0, mo->height>>FRACBITS)<target, mo); + sparkle->destscale = mo->destscale; + P_SetScale(sparkle, mo->scale); + sparkle->color = mo->color; + //sparkle->colorized = mo->colorized; + } + + P_SetMobjState(sparkle, S_KARTINVULN_LARGE1); +} + +void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) +{ + mobj_t *dust; + angle_t aoff; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + if (mo->player) + aoff = (mo->player->frameangle + ANGLE_180); + else + aoff = (mo->angle + ANGLE_180); + + if ((leveltime / 2) & 1) + aoff -= ANGLE_45; + else + aoff += ANGLE_45; + + dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), + mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), + mo->z, MT_WIPEOUTTRAIL); + + P_SetTarget(&dust->target, mo); + dust->angle = R_PointToAngle2(0,0,mo->momx,mo->momy); + dust->destscale = mo->scale; + P_SetScale(dust, mo->scale); + K_FlipFromObject(dust, mo); + + if (translucent) // offroad effect + { + dust->momx = mo->momx/2; + dust->momy = mo->momy/2; + dust->momz = mo->momz/2; + } + + if (translucent) + dust->drawflags |= MFD_SHADOW; +} + +void K_SpawnDraftDust(mobj_t *mo) +{ + UINT8 i; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (i = 0; i < 2; i++) + { + angle_t ang, aoff; + SINT8 sign = 1; + UINT8 foff = 0; + mobj_t *dust; + boolean drifting = false; + + if (mo->player) + { + UINT8 leniency = (3*TICRATE)/4 + ((mo->player->kartweight-1) * (TICRATE/4)); + + ang = mo->player->frameangle; + + if (mo->player->kartstuff[k_drift] != 0) + { + drifting = true; + ang += (mo->player->kartstuff[k_drift] * ((ANGLE_270 + ANGLE_22h) / 5)); // -112.5 doesn't work. I fucking HATE SRB2 angles + if (mo->player->kartstuff[k_drift] < 0) + sign = 1; + else + sign = -1; + } + + foff = 5 - ((mo->player->kartstuff[k_draftleeway] * 5) / leniency); + + // this shouldn't happen + if (foff > 4) + foff = 4; + } + else + ang = mo->angle; + + if (!drifting) + { + if (i & 1) + sign = -1; + else + sign = 1; + } + + aoff = (ang + ANGLE_180) + (ANGLE_45 * sign); + + dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)), + mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)), + mo->z, MT_DRAFTDUST); + + P_SetMobjState(dust, S_DRAFTDUST1 + foff); + + P_SetTarget(&dust->target, mo); + dust->angle = ang - (ANGLE_90 * sign); // point completely perpendicular from the player + dust->destscale = mo->scale; + P_SetScale(dust, mo->scale); + K_FlipFromObject(dust, mo); + + if (leveltime & 1) + dust->tics++; // "randomize" animation + + dust->momx = (4*mo->momx)/5; + dust->momy = (4*mo->momy)/5; + //dust->momz = (4*mo->momz)/5; + + P_Thrust(dust, dust->angle, 4*mo->scale); + + if (drifting) // only 1 trail while drifting + break; + } +} + +// K_DriftDustHandling +// Parameters: +// spawner: The map object that is spawning the drift dust +// Description: Spawns the drift dust for objects, players use rmomx/y, other objects use regular momx/y. +// Also plays the drift sound. +// Other objects should be angled towards where they're trying to go so they don't randomly spawn dust +// Do note that most of the function won't run in odd intervals of frames +void K_DriftDustHandling(mobj_t *spawner) +{ + angle_t anglediff; + const INT16 spawnrange = spawner->radius >> FRACBITS; + + if (!P_IsObjectOnGround(spawner) || leveltime % 2 != 0) + return; + + if (spawner->player) + { + if (spawner->player->pflags & PF_SKIDDOWN) + { + anglediff = abs((signed)(spawner->angle - spawner->player->frameangle)); + if (leveltime % 6 == 0) + S_StartSound(spawner, sfx_screec); // repeated here because it doesn't always happen to be within the range when this is the case + } + else + { + angle_t playerangle = spawner->angle; + + if (spawner->player->speed < 5*spawner->scale) + return; + + if (spawner->player->cmd.forwardmove < 0) + playerangle += ANGLE_180; + + anglediff = abs((signed)(playerangle - R_PointToAngle2(0, 0, spawner->player->rmomx, spawner->player->rmomy))); + } + } + else + { + if (P_AproxDistance(spawner->momx, spawner->momy) < 5*spawner->scale) + return; + + anglediff = abs((signed)(spawner->angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy))); + } + + if (anglediff > ANGLE_180) + anglediff = InvAngle(anglediff); + + if (anglediff > ANG10*4) // Trying to turn further than 40 degrees + { + fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; + fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; + INT32 speedrange = 2; + mobj_t *dust = P_SpawnMobj(spawner->x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST); + dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); + dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); + dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale)); + P_SetScale(dust, spawner->scale/2); + dust->destscale = spawner->scale * 3; + dust->scalespeed = spawner->scale/12; + + if (leveltime % 6 == 0) + S_StartSound(spawner, sfx_screec); + + K_MatchGenericExtraFlags(dust, spawner); + + // Sparkle-y warning for when you're about to change drift sparks! + if (spawner->player && spawner->player->kartstuff[k_drift]) + { + INT32 driftval = K_GetKartDriftSparkValue(spawner->player); + INT32 warntime = driftval/3; + INT32 dc = spawner->player->kartstuff[k_driftcharge]; + UINT8 c = SKINCOLOR_NONE; + boolean rainbow = false; + + if (dc >= 0) + { + dc += warntime; + } + + c = K_DriftSparkColor(spawner->player, dc); + + if (dc > (4*driftval)+(32*3)) + { + rainbow = true; + } + + if (c != SKINCOLOR_NONE) + { + P_SetMobjState(dust, S_DRIFTWARNSPARK1); + dust->color = c; + dust->colorized = rainbow; + } + } + } +} + +static mobj_t *K_FindLastTrailMobj(player_t *player) +{ + mobj_t *trail; + + if (!player || !(trail = player->mo) || !player->mo->hnext || !player->mo->hnext->health) + return NULL; + + while (trail->hnext && !P_MobjWasRemoved(trail->hnext) && trail->hnext->health) + { + trail = trail->hnext; + } + + return trail; +} + +static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow) +{ + mobj_t *mo; + INT32 dir; + fixed_t PROJSPEED; + angle_t newangle; + fixed_t newx, newy, newz; + mobj_t *throwmo; + + if (!player) + return NULL; + + // Figure out projectile speed by game speed + if (missile) + { + // Use info->speed for missiles + PROJSPEED = FixedMul(mobjinfo[mapthing].speed, K_GetKartGameSpeedScalar(gamespeed)); + } + else + { + // Use pre-determined speed for tossing + PROJSPEED = FixedMul(82 << FRACBITS, K_GetKartGameSpeedScalar(gamespeed)); + } + + // Scale to map size + PROJSPEED = FixedMul(PROJSPEED, mapobjectscale); + + if (altthrow) + { + if (altthrow == 2) // Kitchen sink throwing + { +#if 0 + if (player->kartstuff[k_throwdir] == 1) + dir = 3; + else if (player->kartstuff[k_throwdir] == -1) + dir = 1; + else + dir = 2; +#else + if (player->kartstuff[k_throwdir] == 1) + dir = 2; + else + dir = 1; +#endif + } + else + { + if (player->kartstuff[k_throwdir] == 1) + dir = 2; + else if (player->kartstuff[k_throwdir] == -1) + dir = -1; + else + dir = 1; + } + } + else + { + if (player->kartstuff[k_throwdir] != 0) + dir = player->kartstuff[k_throwdir]; + else + dir = defaultDir; + } + + if (missile) // Shootables + { + if (mapthing == MT_BALLHOG) // Messy + { + if (dir == -1) + { + // Shoot backward + mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x03000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x03000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x06000000, 0, PROJSPEED/8); + } + else + { + // Shoot forward + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x06000000, 0, PROJSPEED); + } + } + else + { + if (dir == -1 && mapthing != MT_SPB) + { + // Shoot backward + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); + } + else + { + // Shoot forward + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); + } + } + } + else + { + player->kartstuff[k_bananadrag] = 0; // RESET timer, for multiple bananas + + if (dir > 0) + { + // Shoot forward + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing); + //K_FlipFromObject(mo, player->mo); + // These are really weird so let's make it a very specific case to make SURE it works... + if (player->mo->eflags & MFE_VERTICALFLIP) + { + mo->z -= player->mo->height; + mo->flags2 |= MF2_OBJECTFLIP; + mo->eflags |= MFE_VERTICALFLIP; + } + + mo->threshold = 10; + P_SetTarget(&mo->target, player->mo); + + S_StartSound(player->mo, mo->info->seesound); + + if (mo) + { + angle_t fa = player->mo->angle>>ANGLETOFINESHIFT; + fixed_t HEIGHT = (20 + (dir*10))*FRACUNIT + (player->mo->momz*P_MobjFlip(player->mo)); + + P_SetObjectMomZ(mo, HEIGHT, false); + mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir); + mo->momy = player->mo->momy + FixedMul(FINESINE(fa), PROJSPEED*dir); + + mo->extravalue2 = dir; + + if (mo->eflags & MFE_UNDERWATER) + mo->momz = (117 * mo->momz) / 200; + } + + // this is the small graphic effect that plops in you when you throw an item: + throwmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FIREDITEM); + P_SetTarget(&throwmo->target, player->mo); + // Ditto: + if (player->mo->eflags & MFE_VERTICALFLIP) + { + throwmo->z -= player->mo->height; + throwmo->flags2 |= MF2_OBJECTFLIP; + throwmo->eflags |= MFE_VERTICALFLIP; + } + + throwmo->movecount = 0; // above player + } + else + { + mobj_t *lasttrail = K_FindLastTrailMobj(player); + + if (mapthing == MT_BUBBLESHIELDTRAP) // Drop directly on top of you. + { + newangle = player->mo->angle; + newx = player->mo->x + player->mo->momx; + newy = player->mo->y + player->mo->momy; + newz = player->mo->z; + } + else if (lasttrail) + { + newangle = lasttrail->angle; + newx = lasttrail->x; + newy = lasttrail->y; + newz = lasttrail->z; + } + else + { + // Drop it directly behind you. + fixed_t dropradius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(mobjinfo[mapthing].radius, mobjinfo[mapthing].radius); + + newangle = player->mo->angle; + + newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, dropradius); + newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, dropradius); + newz = player->mo->z; + } + + mo = P_SpawnMobj(newx, newy, newz, mapthing); // this will never return null because collision isn't processed here + K_FlipFromObject(mo, player->mo); + + mo->threshold = 10; + P_SetTarget(&mo->target, player->mo); + + P_SetScale(mo, player->mo->scale); + mo->destscale = player->mo->destscale; + + if (P_IsObjectOnGround(player->mo)) + { + // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn + // This should set it for FOFs + P_TeleportMove(mo, mo->x, mo->y, mo->z); // however, THIS can fuck up your day. just absolutely ruin you. + if (P_MobjWasRemoved(mo)) + return NULL; + + if (P_MobjFlip(mo) > 0) + { + if (mo->floorz > mo->target->z - mo->height) + { + mo->z = mo->floorz; + } + } + else + { + if (mo->ceilingz < mo->target->z + mo->target->height + mo->height) + { + mo->z = mo->ceilingz - mo->height; + } + } + } + + if (player->mo->eflags & MFE_VERTICALFLIP) + mo->eflags |= MFE_VERTICALFLIP; + + if (mapthing == MT_SSMINE) + mo->extravalue1 = 49; // Pads the start-up length from 21 frames to a full 2 seconds + else if (mapthing == MT_BUBBLESHIELDTRAP) + { + P_SetScale(mo, ((5*mo->destscale)>>2)*4); + mo->destscale = (5*mo->destscale)>>2; + S_StartSound(mo, sfx_s3kbfl); + } + } + } + + return mo; +} + +void K_PuntMine(mobj_t *thismine, mobj_t *punter) +{ + angle_t fa = R_PointToAngle2(0, 0, punter->momx, punter->momy) >> ANGLETOFINESHIFT; + fixed_t z = 30*mapobjectscale + punter->momz; + fixed_t spd; + mobj_t *mine; + + if (!thismine || P_MobjWasRemoved(thismine)) + return; + + if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine + { + mine = P_SpawnMobj(thismine->x, thismine->y, thismine->z, MT_SSMINE); + P_SetTarget(&mine->target, thismine->target); + mine->angle = thismine->angle; + mine->flags2 = thismine->flags2; + mine->floorz = thismine->floorz; + mine->ceilingz = thismine->ceilingz; + P_RemoveMobj(thismine); + } + else + mine = thismine; + + if (!mine || P_MobjWasRemoved(mine)) + return; + + spd = (82 + ((gamespeed-1) * 14))*mapobjectscale; // Avg Speed is 41 in Normal + + mine->flags |= MF_NOCLIPTHING; + + P_SetMobjState(mine, S_SSMINE_AIR1); + mine->threshold = 10; + mine->extravalue1 = 0; + mine->reactiontime = mine->info->reactiontime; + + mine->momx = punter->momx + FixedMul(FINECOSINE(fa), spd); + mine->momy = punter->momy + FixedMul(FINESINE(fa), spd); + mine->momz = P_MobjFlip(mine) * z; + + mine->flags &= ~MF_NOCLIPTHING; +} + +#define THUNDERRADIUS 320 + +static void K_DoThunderShield(player_t *player) +{ + mobj_t *mo; + int i = 0; + fixed_t sx; + fixed_t sy; + angle_t an; + + S_StartSound(player->mo, sfx_zio3); + P_NukeEnemies(player->mo, player->mo, RING_DIST/4); + + // spawn vertical bolt + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_LZIO11); + mo->color = SKINCOLOR_TEAL; + mo->scale = player->mo->scale*3 + (player->mo->scale/2); + + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_LZIO21); + mo->color = SKINCOLOR_CYAN; + mo->scale = player->mo->scale*3 + (player->mo->scale/2); + + // spawn horizontal bolts; + for (i=0; i<7; i++) + { + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); + mo->angle = P_RandomRange(0, 359)*ANG1; + mo->fuse = P_RandomRange(20, 50); + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_KLIT1); + } + + // spawn the radius thing: + an = ANGLE_22h; + for (i=0; i<15; i++) + { + sx = player->mo->x + FixedMul((player->mo->scale*THUNDERRADIUS), FINECOSINE((an*i)>>ANGLETOFINESHIFT)); + sy = player->mo->y + FixedMul((player->mo->scale*THUNDERRADIUS), FINESINE((an*i)>>ANGLETOFINESHIFT)); + mo = P_SpawnMobj(sx, sy, player->mo->z, MT_THOK); + mo-> angle = an*i; + mo->extravalue1 = THUNDERRADIUS; // Used to know whether we should teleport by radius or something. + mo->scale = player->mo->scale*3; + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_KSPARK1); + } +} + +#undef THUNDERRADIUS + +static void K_FlameDashLeftoverSmoke(mobj_t *src) +{ + UINT8 i; + + for (i = 0; i < 2; i++) + { + mobj_t *smoke = P_SpawnMobj(src->x, src->y, src->z+(8<scale); + smoke->destscale = 3*src->scale/2; + smoke->scalespeed = src->scale/12; + + smoke->momx = 3*src->momx/4; + smoke->momy = 3*src->momy/4; + smoke->momz = 3*src->momz/4; + + P_Thrust(smoke, src->angle + FixedAngle(P_RandomRange(135, 225)<scale); + smoke->momz += P_RandomRange(0, 4) * src->scale; + } +} + +static void K_DoHyudoroSteal(player_t *player) +{ + INT32 i, numplayers = 0; + INT32 playerswappable[MAXPLAYERS]; + INT32 stealplayer = -1; // The player that's getting stolen from + INT32 prandom = 0; + boolean sink = P_RandomChance(FRACUNIT/64); + INT32 hyu = hyudorotime; + + if (G_RaceGametype()) + hyu *= 2; // double in race + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE + && player != &players[i] && !players[i].exiting && !players[i].spectator // Player in-game + + // Can steal from this player + && (G_RaceGametype() //&& players[i].kartstuff[k_position] < player->kartstuff[k_position]) + || (G_BattleGametype() && players[i].kartstuff[k_bumper] > 0)) + + // Has an item + && (players[i].kartstuff[k_itemtype] + && players[i].kartstuff[k_itemamount] + && !players[i].kartstuff[k_itemheld] + && !players[i].karthud[khud_itemblink])) + { + playerswappable[numplayers] = i; + numplayers++; + } + } + + prandom = P_RandomFixed(); + S_StartSound(player->mo, sfx_s3k92); + + if (sink && numplayers > 0 && cv_kitchensink.value) // BEHOLD THE KITCHEN SINK + { + player->kartstuff[k_hyudorotimer] = hyu; + player->kartstuff[k_stealingtimer] = stealtime; + + player->kartstuff[k_itemtype] = KITEM_KITCHENSINK; + player->kartstuff[k_itemamount] = 1; + player->kartstuff[k_itemheld] = 0; + return; + } + else if ((G_RaceGametype() && player->kartstuff[k_position] == 1) || numplayers == 0) // No-one can be stolen from? Oh well... + { + player->kartstuff[k_hyudorotimer] = hyu; + player->kartstuff[k_stealingtimer] = stealtime; + return; + } + else if (numplayers == 1) // With just 2 players, we just need to set the other player to be the one to steal from + { + stealplayer = playerswappable[numplayers-1]; + } + else if (numplayers > 1) // We need to choose between the available candidates for the 2nd player + { + stealplayer = playerswappable[prandom%(numplayers-1)]; + } + + if (stealplayer > -1) // Now here's where we do the stealing, has to be done here because we still know the player we're stealing from + { + player->kartstuff[k_hyudorotimer] = hyu; + player->kartstuff[k_stealingtimer] = stealtime; + players[stealplayer].kartstuff[k_stolentimer] = stealtime; + + player->kartstuff[k_itemtype] = players[stealplayer].kartstuff[k_itemtype]; + player->kartstuff[k_itemamount] = players[stealplayer].kartstuff[k_itemamount]; + player->kartstuff[k_itemheld] = 0; + + players[stealplayer].kartstuff[k_itemtype] = KITEM_NONE; + players[stealplayer].kartstuff[k_itemamount] = 0; + players[stealplayer].kartstuff[k_itemheld] = 0; + + if (P_IsDisplayPlayer(&players[stealplayer]) && !r_splitscreen) + S_StartSound(NULL, sfx_s3k92); + } +} + +void K_DoSneaker(player_t *player, INT32 type) +{ + const fixed_t intendedboost = FRACUNIT/2; + + if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) + { + const sfxenum_t normalsfx = sfx_cdfm01; + const sfxenum_t smallsfx = sfx_cdfm40; + sfxenum_t sfx = normalsfx; + + if (player->kartstuff[k_numsneakers]) + { + // Use a less annoying sound when stacking sneakers. + sfx = smallsfx; + } + + S_StopSoundByID(player->mo, normalsfx); + S_StopSoundByID(player->mo, smallsfx); + S_StartSound(player->mo, sfx); + + K_SpawnDashDustRelease(player); + if (intendedboost > player->kartstuff[k_speedboost]) + player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); + + player->kartstuff[k_numsneakers]++; + } + + if (!player->kartstuff[k_sneakertimer]) + { + if (type == 2) + { + if (player->mo->hnext) + { + mobj_t *cur = player->mo->hnext; + while (cur && !P_MobjWasRemoved(cur)) + { + if (!cur->tracer) + { + mobj_t *overlay = P_SpawnMobj(cur->x, cur->y, cur->z, MT_BOOSTFLAME); + P_SetTarget(&overlay->target, cur); + P_SetTarget(&cur->tracer, overlay); + P_SetScale(overlay, (overlay->destscale = 3*cur->scale/4)); + K_FlipFromObject(overlay, cur); + } + cur = cur->hnext; + } + } + } + else + { + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BOOSTFLAME); + P_SetTarget(&overlay->target, player->mo); + P_SetScale(overlay, (overlay->destscale = player->mo->scale)); + K_FlipFromObject(overlay, player->mo); + } + } + + if (type != 0) + { + player->pflags |= PF_ATTACKDOWN; + K_PlayBoostTaunt(player->mo); + + } + + player->kartstuff[k_sneakertimer] = sneakertime; + + // set angle for spun out players: + player->kartstuff[k_boostangle] = (INT32)player->mo->angle; +} + +static void K_DoShrink(player_t *user) +{ + INT32 i; + mobj_t *mobj, *next; + + S_StartSound(user->mo, sfx_kc46); // Sound the BANG! + user->pflags |= PF_ATTACKDOWN; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + if (&players[i] == user) + continue; + if (players[i].kartstuff[k_position] < user->kartstuff[k_position]) + { + //P_FlashPal(&players[i], PAL_NUKE, 10); + + // Grow should get taken away. + if (players[i].kartstuff[k_growshrinktimer] > 0) + K_RemoveGrowShrink(&players[i]); + else + { + // Start shrinking! + K_DropItems(&players[i]); + players[i].kartstuff[k_growshrinktimer] = -(15*TICRATE); + + if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) + { + players[i].mo->scalespeed = mapobjectscale/TICRATE; + players[i].mo->destscale = (6*mapobjectscale)/8; + if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot) + players[i].mo->destscale = (6*players[i].mo->destscale)/8; + S_StartSound(players[i].mo, sfx_kc59); + } + } + } + } + + // kill everything in the kitem list while we're at it: + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + + // check if the item is being held by a player behind us before removing it. + // check if the item is a "shield" first, bc i'm p sure thrown items keep the player that threw em as target anyway + + if (mobj->type == MT_BANANA_SHIELD || mobj->type == MT_JAWZ_SHIELD || + mobj->type == MT_SSMINE_SHIELD || mobj->type == MT_EGGMANITEM_SHIELD || + mobj->type == MT_SINK_SHIELD || mobj->type == MT_ORBINAUT_SHIELD) + { + if (mobj->target && mobj->target->player) + { + if (mobj->target->player->kartstuff[k_position] > user->kartstuff[k_position]) + continue; // this guy's behind us, don't take his stuff away! + } + } + + mobj->destscale = 0; + mobj->flags &= ~(MF_SOLID|MF_SHOOTABLE|MF_SPECIAL); + mobj->flags |= MF_NOCLIPTHING; // Just for safety + + if (mobj->type == MT_SPB) + spbplace = -1; + } +} + + +void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) +{ + const fixed_t vscale = mapobjectscale + (mo->scale - mapobjectscale); + + if (mo->player && mo->player->spectator) + return; + + if (mo->eflags & MFE_SPRUNG) + return; + +#ifdef ESLOPE + mo->standingslope = NULL; +#endif + + mo->eflags |= MFE_SPRUNG; + + if (mo->eflags & MFE_VERTICALFLIP) + vertispeed *= -1; + + if (vertispeed == 0) + { + fixed_t thrust; + + if (mo->player) + { + thrust = 3*mo->player->speed/2; + if (thrust < 48< 72<player->kartstuff[k_pogospring] != 2) + { + if (mo->player->kartstuff[k_sneakertimer]) + thrust = FixedMul(thrust, (5*FRACUNIT)/4); + else if (mo->player->kartstuff[k_invincibilitytimer]) + thrust = FixedMul(thrust, (9*FRACUNIT)/8); + } + } + else + { + thrust = FixedDiv(3*P_AproxDistance(mo->momx, mo->momy)/2, 5*FRACUNIT/2); + if (thrust < 16< 32<momz = P_MobjFlip(mo)*FixedMul(FINESINE(ANGLE_22h>>ANGLETOFINESHIFT), FixedMul(thrust, vscale)); + } + else + mo->momz = FixedMul(vertispeed, vscale); + + if (mo->eflags & MFE_UNDERWATER) + mo->momz = (117 * mo->momz) / 200; + + if (sound) + S_StartSound(mo, (sound == 1 ? sfx_kc2f : sfx_kpogos)); +} + +void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source) +{ + mobj_t *cachenext; + +killnext: + cachenext = banana->hnext; + + if (banana->health) + { + if (banana->eflags & MFE_VERTICALFLIP) + banana->z -= banana->height; + else + banana->z += banana->height; + + S_StartSound(banana, banana->info->deathsound); + P_KillMobj(banana, inflictor, source); + + P_SetObjectMomZ(banana, 8*FRACUNIT, false); + if (inflictor) + P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT); + } + + if ((banana = cachenext)) + goto killnext; +} + +// Just for firing/dropping items. +void K_UpdateHnextList(player_t *player, boolean clean) +{ + mobj_t *work = player->mo, *nextwork; + + if (!work) + return; + + nextwork = work->hnext; + + while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) + { + nextwork = work->hnext; + + if (!clean && (!work->movedir || work->movedir <= (UINT16)player->kartstuff[k_itemamount])) + { + continue; + } + + P_RemoveMobj(work); + } + + if (player->mo->hnext == NULL || P_MobjWasRemoved(player->mo->hnext)) + { + // Like below, try to clean up the pointer if it's NULL. + // Maybe this was a cause of the shrink/eggbox fails? + P_SetTarget(&player->mo->hnext, NULL); + } +} + +// For getting hit! +void K_DropHnextList(player_t *player, boolean keepshields) +{ + mobj_t *work = player->mo, *nextwork, *dropwork; + INT32 flip; + mobjtype_t type; + boolean orbit, ponground, dropall = true; + INT32 shield = K_GetShieldFromItem(player->kartstuff[k_itemtype]); + + if (work == NULL || P_MobjWasRemoved(work)) + { + return; + } + + flip = P_MobjFlip(player->mo); + ponground = P_IsObjectOnGround(player->mo); + + if (shield != KSHIELD_NONE && !keepshields) + { + if (shield == KSHIELD_THUNDER) + { + K_DoThunderShield(player); + } + + player->kartstuff[k_curshield] = KSHIELD_NONE; + player->kartstuff[k_itemtype] = KITEM_NONE; + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + } + + nextwork = work->hnext; + + while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) + { + nextwork = work->hnext; + + switch (work->type) + { + // Kart orbit items + case MT_ORBINAUT_SHIELD: + orbit = true; + type = MT_ORBINAUT; + break; + case MT_JAWZ_SHIELD: + orbit = true; + type = MT_JAWZ_DUD; + break; + // Kart trailing items + case MT_BANANA_SHIELD: + orbit = false; + type = MT_BANANA; + break; + case MT_SSMINE_SHIELD: + orbit = false; + dropall = false; + type = MT_SSMINE; + break; + case MT_EGGMANITEM_SHIELD: + orbit = false; + type = MT_EGGMANITEM; + break; + // intentionally do nothing + case MT_ROCKETSNEAKER: + case MT_SINK_SHIELD: + return; + default: + continue; + } + + dropwork = P_SpawnMobj(work->x, work->y, work->z, type); + + P_SetTarget(&dropwork->target, player->mo); + P_AddKartItem(dropwork); // needs to be called here so shrink can bust items off players in front of the user. + + dropwork->angle = work->angle; + + dropwork->flags |= MF_NOCLIPTHING; + dropwork->flags2 = work->flags2; + + dropwork->floorz = work->floorz; + dropwork->ceilingz = work->ceilingz; + + if (ponground) + { + // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn + // This should set it for FOFs + //P_TeleportMove(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing + + if (flip == 1) + { + if (dropwork->floorz > dropwork->target->z - dropwork->height) + { + dropwork->z = dropwork->floorz; + } + } + else + { + if (dropwork->ceilingz < dropwork->target->z + dropwork->target->height + dropwork->height) + { + dropwork->z = dropwork->ceilingz - dropwork->height; + } + } + } + + if (orbit) // splay out + { + dropwork->flags2 |= MF2_AMBUSH; + + dropwork->z += flip; + + dropwork->momx = player->mo->momx>>1; + dropwork->momy = player->mo->momy>>1; + dropwork->momz = 3*flip*mapobjectscale; + + if (dropwork->eflags & MFE_UNDERWATER) + dropwork->momz = (117 * dropwork->momz) / 200; + + P_Thrust(dropwork, work->angle - ANGLE_90, 6*mapobjectscale); + + dropwork->movecount = 2; + dropwork->movedir = work->angle - ANGLE_90; + + P_SetMobjState(dropwork, dropwork->info->deathstate); + + dropwork->tics = -1; + + if (type == MT_JAWZ_DUD) + { + dropwork->z += 20*flip*dropwork->scale; + } + else + { + dropwork->color = work->color; + dropwork->angle -= ANGLE_90; + } + } + else // plop on the ground + { + dropwork->flags &= ~MF_NOCLIPTHING; + dropwork->threshold = 10; + } + + P_RemoveMobj(work); + } + + // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... + P_SetTarget(&player->mo->hnext, NULL); + + player->kartstuff[k_bananadrag] = 0; + + if (player->kartstuff[k_eggmanheld]) + { + player->kartstuff[k_eggmanheld] = 0; + } + else if (player->kartstuff[k_itemheld] + && (dropall || (--player->kartstuff[k_itemamount] <= 0))) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } +} + +// For getting EXTRA hit! +void K_DropItems(player_t *player) +{ + K_DropHnextList(player, true); + + 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]; + + drop->flags |= MF_NOCLIPTHING; + } + + K_StripItems(player); +} + +// When an item in the hnext chain dies. +void K_RepairOrbitChain(mobj_t *orbit) +{ + mobj_t *cachenext = orbit->hnext; + + // First, repair the chain + if (orbit->hnext && orbit->hnext->health && !P_MobjWasRemoved(orbit->hnext)) + { + P_SetTarget(&orbit->hnext->hprev, orbit->hprev); + P_SetTarget(&orbit->hnext, NULL); + } + + if (orbit->hprev && orbit->hprev->health && !P_MobjWasRemoved(orbit->hprev)) + { + P_SetTarget(&orbit->hprev->hnext, cachenext); + P_SetTarget(&orbit->hprev, NULL); + } + + // Then recount to make sure item amount is correct + if (orbit->target && orbit->target->player) + { + INT32 num = 0; + + mobj_t *cur = orbit->target->hnext; + mobj_t *prev = NULL; + + while (cur && !P_MobjWasRemoved(cur)) + { + prev = cur; + cur = cur->hnext; + if (++num > orbit->target->player->kartstuff[k_itemamount]) + P_RemoveMobj(prev); + else + prev->movedir = num; + } + + if (orbit->target->player->kartstuff[k_itemamount] != num) + orbit->target->player->kartstuff[k_itemamount] = num; + } +} + +// Simplified version of a code bit in P_MobjFloorZ +static fixed_t K_BananaSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) +{ + fixed_t testx, testy; + + if (slope->d.x < 0) + testx = radius; + else + testx = -radius; + + if (slope->d.y < 0) + testy = radius; + else + testy = -radius; + + if ((slope->zdelta > 0) ^ !!(ceiling)) + { + testx = -testx; + testy = -testy; + } + + testx += x; + testy += y; + + return P_GetZAt(slope, testx, testy); +} + +static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) +{ + fixed_t newz; + sector_t *sec; +#ifdef ESLOPE + pslope_t *slope = NULL; +#endif + + sec = R_PointInSubsector(x, y)->sector; + + if (flip) + { +#ifdef ESLOPE + if (sec->c_slope) + { + slope = sec->c_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, true); + } + else +#endif + newz = sec->ceilingheight; + } + else + { +#ifdef ESLOPE + if (sec->f_slope) + { + slope = sec->f_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, false); + } + else +#endif + newz = sec->floorheight; + } + + // Check FOFs for a better suited slope + if (sec->ffloors) + { + ffloor_t *rover; + + for (rover = sec->ffloors; rover; rover = rover->next) + { + fixed_t top, bottom; + fixed_t d1, d2; + + if (!(rover->flags & FF_EXISTS)) + continue; + + if ((!(((rover->flags & FF_BLOCKPLAYER && player) + || (rover->flags & FF_BLOCKOTHERS && !player)) + || (rover->flags & FF_QUICKSAND)) + || (rover->flags & FF_SWIMMABLE))) + continue; + +#ifdef ESLOPE + if (*rover->t_slope) + top = K_BananaSlopeZ(*rover->t_slope, x, y, radius, false); + else +#endif + top = *rover->topheight; + +#ifdef ESLOPE + if (*rover->b_slope) + bottom = K_BananaSlopeZ(*rover->b_slope, x, y, radius, true); + else +#endif + bottom = *rover->bottomheight; + + if (flip) + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz > (z + height)) + { + newz = (z + height); + slope = NULL; + } + } + continue; + } + + d1 = (z + height) - (top + ((bottom - top)/2)); + d2 = z - (top + ((bottom - top)/2)); + + if (bottom < newz && abs(d1) < abs(d2)) + { + newz = bottom; +#ifdef ESLOPE + if (*rover->b_slope) + slope = *rover->b_slope; +#endif + } + } + else + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz < z) + { + newz = z; + slope = NULL; + } + } + continue; + } + + d1 = z - (bottom + ((top - bottom)/2)); + d2 = (z + height) - (bottom + ((top - bottom)/2)); + + if (top > newz && abs(d1) < abs(d2)) + { + newz = top; +#ifdef ESLOPE + if (*rover->t_slope) + slope = *rover->t_slope; +#endif + } + } + } + } + +#if 0 + mobj->standingslope = slope; +#endif + +#ifdef HWRENDER + mobj->modeltilt = slope; +#endif +} + +// Move the hnext chain! +static void K_MoveHeldObjects(player_t *player) +{ + if (!player->mo) + return; + + if (!player->mo->hnext) + { + player->kartstuff[k_bananadrag] = 0; + if (player->kartstuff[k_eggmanheld]) + player->kartstuff[k_eggmanheld] = 0; + else if (player->kartstuff[k_itemheld]) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } + return; + } + + if (P_MobjWasRemoved(player->mo->hnext)) + { + // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... + P_SetTarget(&player->mo->hnext, NULL); + player->kartstuff[k_bananadrag] = 0; + if (player->kartstuff[k_eggmanheld]) + player->kartstuff[k_eggmanheld] = 0; + else if (player->kartstuff[k_itemheld]) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } + return; + } + + switch (player->mo->hnext->type) + { + case MT_ORBINAUT_SHIELD: // Kart orbit items + case MT_JAWZ_SHIELD: + { + mobj_t *cur = player->mo->hnext; + fixed_t speed = ((8 - min(4, player->kartstuff[k_itemamount])) * cur->info->speed) / 7; + + player->kartstuff[k_bananadrag] = 0; // Just to make sure + + while (cur && !P_MobjWasRemoved(cur)) + { + const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); // mobj's distance from its Target, or Radius. + fixed_t z; + + if (!cur->health) + { + cur = cur->hnext; + continue; + } + + cur->color = player->skincolor; + + cur->angle -= ANGLE_90; + cur->angle += FixedAngle(speed); + + if (cur->extravalue1 < radius) + cur->extravalue1 += P_AproxDistance(cur->extravalue1, radius) / 12; + if (cur->extravalue1 > radius) + cur->extravalue1 = radius; + + // If the player is on the ceiling, then flip your items as well. + if (player && player->mo->eflags & MFE_VERTICALFLIP) + cur->eflags |= MFE_VERTICALFLIP; + else + cur->eflags &= ~MFE_VERTICALFLIP; + + // Shrink your items if the player shrunk too. + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + + if (P_MobjFlip(cur) > 0) + z = player->mo->z; + else + z = player->mo->z + player->mo->height - cur->height; + + cur->flags |= MF_NOCLIPTHING; // temporarily make them noclip other objects so they can't hit anyone while in the player + P_TeleportMove(cur, player->mo->x, player->mo->y, z); + cur->momx = FixedMul(FINECOSINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); + cur->momy = FixedMul(FINESINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); + cur->flags &= ~MF_NOCLIPTHING; + if (!P_TryMove(cur, player->mo->x + cur->momx, player->mo->y + cur->momy, true)) + P_SlideMove(cur, true); + if (P_IsObjectOnGround(player->mo)) + { + if (P_MobjFlip(cur) > 0) + { + if (cur->floorz > player->mo->z - cur->height) + z = cur->floorz; + } + else + { + if (cur->ceilingz < player->mo->z + player->mo->height + cur->height) + z = cur->ceilingz - cur->height; + } + } + + // Center it during the scale up animation + z += (FixedMul(mobjinfo[cur->type].height, player->mo->scale - cur->scale)>>1) * P_MobjFlip(cur); + + cur->z = z; + cur->momx = cur->momy = 0; + cur->angle += ANGLE_90; + + cur = cur->hnext; + } + } + break; + case MT_BANANA_SHIELD: // Kart trailing items + case MT_SSMINE_SHIELD: + case MT_EGGMANITEM_SHIELD: + case MT_SINK_SHIELD: + { + mobj_t *cur = player->mo->hnext; + mobj_t *targ = player->mo; + + if (P_IsObjectOnGround(player->mo) && player->speed > 0) + player->kartstuff[k_bananadrag]++; + + while (cur && !P_MobjWasRemoved(cur)) + { + const fixed_t radius = FixedHypot(targ->radius, targ->radius) + FixedHypot(cur->radius, cur->radius); + angle_t ang; + fixed_t targx, targy, targz; + fixed_t speed, dist; + + if (cur->type == MT_EGGMANITEM_SHIELD) + { + // Decided that this should use their "canon" color. + cur->color = SKINCOLOR_BLACK; + } + + cur->flags &= ~MF_NOCLIPTHING; + + if (!cur->health) + { + cur = cur->hnext; + continue; + } + + if (cur->extravalue1 < radius) + cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); + if (cur->extravalue1 > radius) + cur->extravalue1 = radius; + + if (cur != player->mo->hnext) + { + targ = cur->hprev; + dist = cur->extravalue1/4; + } + else + dist = cur->extravalue1/2; + + if (!targ || P_MobjWasRemoved(targ)) + continue; + + // Shrink your items if the player shrunk too. + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + + ang = targ->angle; + targx = targ->x + P_ReturnThrustX(cur, ang + ANGLE_180, dist); + targy = targ->y + P_ReturnThrustY(cur, ang + ANGLE_180, dist); + targz = targ->z; + + speed = FixedMul(R_PointToDist2(cur->x, cur->y, targx, targy), 3*FRACUNIT/4); + if (P_IsObjectOnGround(targ)) + targz = cur->floorz; + + cur->angle = R_PointToAngle2(cur->x, cur->y, targx, targy); + + /*if (P_IsObjectOnGround(player->mo) && player->speed > 0 && player->kartstuff[k_bananadrag] > TICRATE + && P_RandomChance(min(FRACUNIT/2, FixedDiv(player->speed, K_GetKartSpeed(player, false))/2))) + { + if (leveltime & 1) + targz += 8*(2*FRACUNIT)/7; + else + targz -= 8*(2*FRACUNIT)/7; + }*/ + + if (speed > dist) + P_InstaThrust(cur, cur->angle, speed-dist); + + P_SetObjectMomZ(cur, FixedMul(targz - cur->z, 7*FRACUNIT/8) - gravity, false); + + if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT) + P_TeleportMove(cur, targx, targy, cur->z); + +#ifdef ESLOPE + if (P_IsObjectOnGround(cur)) + { + K_CalculateBananaSlope(cur, cur->x, cur->y, cur->z, + cur->radius, cur->height, (cur->eflags & MFE_VERTICALFLIP), false); + } +#endif + + cur = cur->hnext; + } + } + break; + case MT_ROCKETSNEAKER: // Special rocket sneaker stuff + { + mobj_t *cur = player->mo->hnext; + INT32 num = 0; + + while (cur && !P_MobjWasRemoved(cur)) + { + const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); + boolean vibrate = ((leveltime & 1) && !cur->tracer); + angle_t angoffset; + fixed_t targx, targy, targz; + + cur->flags &= ~MF_NOCLIPTHING; + + if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1)) + cur->drawflags |= MFD_DONTDRAW; + else + cur->drawflags &= ~MFD_DONTDRAW; + + if (num & 1) + P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L)); + else + P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_RVIBRATE : S_ROCKETSNEAKER_R)); + + if (!player->kartstuff[k_rocketsneakertimer] || cur->extravalue2 || !cur->health) + { + num = (num+1) % 2; + cur = cur->hnext; + continue; + } + + if (cur->extravalue1 < radius) + cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); + if (cur->extravalue1 > radius) + cur->extravalue1 = radius; + + // Shrink your items if the player shrunk too. + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + +#if 1 + { + angle_t input = player->frameangle - cur->angle; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + input = FixedAngle(AngleFixed(input)/4); + if (invert) + input = InvAngle(input); + + cur->angle = cur->angle + input; + } +#else + cur->angle = player->frameangle; +#endif + + angoffset = ANGLE_90 + (ANGLE_180 * num); + + targx = player->mo->x + P_ReturnThrustX(cur, cur->angle + angoffset, cur->extravalue1); + targy = player->mo->y + P_ReturnThrustY(cur, cur->angle + angoffset, cur->extravalue1); + + { // bobbing, copy pasted from my kimokawaiii entry + const fixed_t pi = (22<mo->scale, 8 * FINESINE((((2*pi*(4*TICRATE)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK)); + targz = (player->mo->z + (player->mo->height/2)) + sine; + if (player->mo->eflags & MFE_VERTICALFLIP) + targz += (player->mo->height/2 - 32*player->mo->scale)*6; + + } + + if (cur->tracer) + { + fixed_t diffx, diffy, diffz; + + diffx = targx - cur->x; + diffy = targy - cur->y; + diffz = targz - cur->z; + + P_TeleportMove(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale), + cur->tracer->y + diffy + P_ReturnThrustY(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->z + diffz); + P_SetScale(cur->tracer, (cur->tracer->destscale = 3*cur->scale/4)); + } + + P_TeleportMove(cur, targx, targy, targz); + K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. +#ifdef HWRENDER + cur->modeltilt = player->mo->modeltilt; +#endif + num = (num+1) % 2; + cur = cur->hnext; + } + } + break; + default: + break; + } +} + +player_t *K_FindJawzTarget(mobj_t *actor, player_t *source) +{ + fixed_t best = -1; + player_t *wtarg = NULL; + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + angle_t thisang; + player_t *player; + + if (!playeringame[i]) + continue; + + player = &players[i]; + + if (player->spectator) + continue; // spectator + + if (!player->mo) + continue; + + if (player->mo->health <= 0) + continue; // dead + + // Don't target yourself, stupid. + if (player == source) + continue; + + // Don't home in on teammates. + if (G_GametypeHasTeams() && source->ctfteam == player->ctfteam) + continue; + + // Invisible, don't bother + if (player->kartstuff[k_hyudorotimer]) + continue; + + // Find the angle, see who's got the best. + thisang = actor->angle - R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y); + if (thisang > ANGLE_180) + thisang = InvAngle(thisang); + + // Jawz only go after the person directly ahead of you in race... sort of literally now! + if (G_RaceGametype()) + { + // Don't go for people who are behind you + if (thisang > ANGLE_67h) + continue; + // Don't pay attention to people who aren't above your position + if (player->kartstuff[k_position] >= source->kartstuff[k_position]) + continue; + if ((best == -1) || (player->kartstuff[k_position] > best)) + { + wtarg = player; + best = player->kartstuff[k_position]; + } + } + else + { + fixed_t thisdist; + fixed_t thisavg; + + // Don't go for people who are behind you + if (thisang > ANGLE_45) + continue; + + // Don't pay attention to dead players + if (player->kartstuff[k_bumper] <= 0) + continue; + + // Z pos too high/low + if (abs(player->mo->z - (actor->z + actor->momz)) > RING_DIST/8) + continue; + + thisdist = P_AproxDistance(player->mo->x - (actor->x + actor->momx), player->mo->y - (actor->y + actor->momy)); + + if (thisdist > 2*RING_DIST) // Don't go for people who are too far away + continue; + + thisavg = (AngleFixed(thisang) + thisdist) / 2; + + //CONS_Printf("got avg %d from player # %d\n", thisavg>>FRACBITS, i); + + if ((best == -1) || (thisavg < best)) + { + wtarg = player; + best = thisavg; + } + } + } + + return wtarg; +} + +// Engine Sounds. +static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) +{ + const INT32 numsnds = 13; + INT32 class, s, w; // engine class number + UINT8 volume = 255; + fixed_t volumedampen = 0; + INT32 targetsnd = 0; + INT32 i; + + s = (player->kartspeed-1)/3; + w = (player->kartweight-1)/3; + +#define LOCKSTAT(stat) \ + if (stat < 0) { stat = 0; } \ + if (stat > 2) { stat = 2; } + LOCKSTAT(s); + LOCKSTAT(w); +#undef LOCKSTAT + + class = s+(3*w); + + // Silence the engines + if (leveltime < 8 || player->spectator) + { + player->karthud[khud_enginesnd] = 0; // Reset sound number + return; + } + +#if 0 + if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct! +#else + if (leveltime % 8) // .25 seconds of wait time between engine sounds +#endif + return; + + if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->respawn.state == RESPAWNST_DROP)) // Startup boosts + targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0); + else + targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2; + + if (targetsnd < 0) + targetsnd = 0; + if (targetsnd > 12) + targetsnd = 12; + + if (player->karthud[khud_enginesnd] < targetsnd) + player->karthud[khud_enginesnd]++; + if (player->karthud[khud_enginesnd] > targetsnd) + player->karthud[khud_enginesnd]--; + + if (player->karthud[khud_enginesnd] < 0) + player->karthud[khud_enginesnd] = 0; + if (player->karthud[khud_enginesnd] > 12) + player->karthud[khud_enginesnd] = 12; + + for (i = 0; i < MAXPLAYERS; i++) + { + UINT8 thisvol = 0; + fixed_t dist; + + if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting) + continue; + + if (P_IsDisplayPlayer(&players[i])) + { + volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time. + continue; + } + + dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x, + player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2; + + dist = FixedDiv(dist, mapobjectscale); + + if (dist > 1536<>FRACBITS)) / (((1536<>(FRACBITS+4)); + + if (thisvol == 0) + continue; + + volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT + } + + if (volumedampen > FRACUNIT) + volume = FixedDiv(volume<>FRACBITS; + + if (volume <= 0) // Might as well + return; + + S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->karthud[khud_enginesnd]) + (class*numsnds), volume); +} + +static void K_UpdateInvincibilitySounds(player_t *player) +{ + INT32 sfxnum = sfx_None; + + if (player->mo->health > 0 && !P_IsDisplayPlayer(player)) + { + if (cv_kartinvinsfx.value) + { + if (player->kartstuff[k_invincibilitytimer] > 0) // Prioritize invincibility + sfxnum = sfx_alarmi; + else if (player->kartstuff[k_growshrinktimer] > 0) + sfxnum = sfx_alarmg; + } + else + { + if (player->kartstuff[k_invincibilitytimer] > 0) + sfxnum = sfx_kinvnc; + else if (player->kartstuff[k_growshrinktimer] > 0) + sfxnum = sfx_kgrow; + } + } + + if (sfxnum != sfx_None && !S_SoundPlaying(player->mo, sfxnum)) + S_StartSound(player->mo, sfxnum); + +#define STOPTHIS(this) \ + if (sfxnum != this && S_SoundPlaying(player->mo, this)) \ + S_StopSoundByID(player->mo, this); + STOPTHIS(sfx_alarmi); + STOPTHIS(sfx_alarmg); + STOPTHIS(sfx_kinvnc); + STOPTHIS(sfx_kgrow); +#undef STOPTHIS +} + +#define RINGANIM_NUMFRAMES 10 +#define RINGANIM_DELAYMAX 5 + +void K_KartPlayerHUDUpdate(player_t *player) +{ + if (player->karthud[khud_lapanimation]) + player->karthud[khud_lapanimation]--; + + if (player->karthud[khud_yougotem]) + player->karthud[khud_yougotem]--; + + if (player->karthud[khud_voices]) + player->karthud[khud_voices]--; + + if (player->karthud[khud_tauntvoices]) + player->karthud[khud_tauntvoices]--; + + if (G_RaceGametype()) + { + // 0 is the fast spin animation, set at 30 tics of ring boost or higher! + if (player->kartstuff[k_ringboost] >= 30) + player->karthud[khud_ringdelay] = 0; + else + player->karthud[khud_ringdelay] = ((RINGANIM_DELAYMAX+1) * (30 - player->kartstuff[k_ringboost])) / 30; + + if (player->karthud[khud_ringframe] == 0 && player->karthud[khud_ringdelay] > RINGANIM_DELAYMAX) + { + player->karthud[khud_ringframe] = 0; + player->karthud[khud_ringtics] = 0; + } + else if ((player->karthud[khud_ringtics]--) <= 0) + { + if (player->karthud[khud_ringdelay] == 0) // fast spin animation + { + player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+2) % RINGANIM_NUMFRAMES); + player->karthud[khud_ringtics] = 0; + } + else + { + player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+1) % RINGANIM_NUMFRAMES); + player->karthud[khud_ringtics] = min(RINGANIM_DELAYMAX, player->karthud[khud_ringdelay])-1; + } + } + + if (player->kartstuff[k_ringlock]) + { + UINT8 normalanim = (leveltime % 14); + UINT8 debtanim = 14 + (leveltime % 2); + + if (player->karthud[khud_ringspblock] >= 14) // debt animation + { + if ((player->kartstuff[k_rings] > 0) // Get out of 0 ring animation + && (normalanim == 3 || normalanim == 10)) // on these transition frames. + player->karthud[khud_ringspblock] = normalanim; + else + player->karthud[khud_ringspblock] = debtanim; + } + else // normal animation + { + if ((player->kartstuff[k_rings] <= 0) // Go into 0 ring animation + && (player->karthud[khud_ringspblock] == 1 || player->karthud[khud_ringspblock] == 8)) // on these transition frames. + player->karthud[khud_ringspblock] = debtanim; + else + player->karthud[khud_ringspblock] = normalanim; + } + } + else + player->karthud[khud_ringspblock] = (leveltime % 14); // reset to normal anim next time + } + + if (G_BattleGametype() && (player->exiting || player->kartstuff[k_comebacktimer])) + { + if (player->exiting) + { + if (player->exiting < 6*TICRATE) + player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; + else if (player->exiting == 6*TICRATE) + player->karthud[khud_cardanimation] = 0; + else if (player->karthud[khud_cardanimation] < 2*TICRATE) + player->karthud[khud_cardanimation]++; + } + else + { + if (player->kartstuff[k_comebacktimer] < 6*TICRATE) + player->karthud[khud_cardanimation] -= ((164-player->karthud[khud_cardanimation])/8)+1; + else if (player->kartstuff[k_comebacktimer] < 9*TICRATE) + player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; + } + + if (player->karthud[khud_cardanimation] > 164) + player->karthud[khud_cardanimation] = 164; + if (player->karthud[khud_cardanimation] < 0) + player->karthud[khud_cardanimation] = 0; + } + else if (G_RaceGametype() && player->exiting) + { + if (player->karthud[khud_cardanimation] < 2*TICRATE) + player->karthud[khud_cardanimation]++; + } + else + player->karthud[khud_cardanimation] = 0; +} + +#undef RINGANIM_DELAYMAX + +// SRB2Kart: blockmap iterate for attraction shield users +static mobj_t *attractmo; +static fixed_t attractdist; +static inline boolean PIT_AttractingRings(mobj_t *thing) +{ + if (!attractmo || P_MobjWasRemoved(attractmo)) + return false; + + if (!attractmo->player) + return false; // not a player + + if (thing->health <= 0 || !thing) + return true; // dead + + if (thing->type != MT_RING && thing->type != MT_FLINGRING) + return true; // not a ring + + if (thing->extravalue1) + return true; // in special ring animation + + if (thing->cusval) + return true; // already attracted + + // see if it went over / under + if (attractmo->z - (attractdist>>2) > thing->z + thing->height) + return true; // overhead + if (attractmo->z + attractmo->height + (attractdist>>2) < thing->z) + return true; // underneath + + if (P_AproxDistance(attractmo->x - thing->x, attractmo->y - thing->y) < attractdist) + return true; // Too far away + + // set target + P_SetTarget(&thing->tracer, attractmo); + // flag to show it's been attracted once before + thing->cusval = 1; + return true; // find other rings +} + +/** Looks for rings near a player in the blockmap. + * + * \param pmo Player object looking for rings to attract + * \sa A_AttractChase + */ +static void K_LookForRings(mobj_t *pmo) +{ + INT32 bx, by, xl, xh, yl, yh; + attractdist = FixedMul(RING_DIST, pmo->scale)>>2; + + // Use blockmap to check for nearby rings + yh = (unsigned)(pmo->y + attractdist - bmaporgy)>>MAPBLOCKSHIFT; + yl = (unsigned)(pmo->y - attractdist - bmaporgy)>>MAPBLOCKSHIFT; + xh = (unsigned)(pmo->x + attractdist - bmaporgx)>>MAPBLOCKSHIFT; + xl = (unsigned)(pmo->x - attractdist - bmaporgx)>>MAPBLOCKSHIFT; + + attractmo = pmo; + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + P_BlockThingsIterator(bx, by, PIT_AttractingRings); +} + +/** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c + + \param player player object passed from P_PlayerThink + \param cmd control input from player + + \return void +*/ +void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) +{ + K_UpdateOffroad(player); + K_UpdateDraft(player); + K_UpdateEngineSounds(player, cmd); // Thanks, VAda! + + // update boost angle if not spun out + if (!player->kartstuff[k_spinouttimer] && !player->kartstuff[k_wipeoutslow]) + player->kartstuff[k_boostangle] = (INT32)player->mo->angle; + + K_GetKartBoostPower(player); + + // Special effect objects! + if (player->mo && !player->spectator) + { + if (player->kartstuff[k_dashpadcooldown]) // Twinkle Circuit afterimages + { + mobj_t *ghost; + ghost = P_SpawnGhostMobj(player->mo); + ghost->fuse = player->kartstuff[k_dashpadcooldown]+1; + ghost->momx = player->mo->momx / (player->kartstuff[k_dashpadcooldown]+1); + ghost->momy = player->mo->momy / (player->kartstuff[k_dashpadcooldown]+1); + ghost->momz = player->mo->momz / (player->kartstuff[k_dashpadcooldown]+1); + player->kartstuff[k_dashpadcooldown]--; + } + + if (player->speed > 0) + { + // Speed lines + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_ringboost] + || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost] + || player->kartstuff[k_eggmanexplode]) + { + mobj_t *fast = P_SpawnMobj(player->mo->x + (P_RandomRange(-36,36) * player->mo->scale), + player->mo->y + (P_RandomRange(-36,36) * player->mo->scale), + player->mo->z + (player->mo->height/2) + (P_RandomRange(-20,20) * player->mo->scale), + MT_FASTLINE); + + P_SetTarget(&fast->target, player->mo); + fast->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + fast->momx = 3*player->mo->momx/4; + fast->momy = 3*player->mo->momy/4; + fast->momz = 3*player->mo->momz/4; + + K_MatchGenericExtraFlags(fast, player->mo); + + // Make it red when you have the eggman speed boost + if (player->kartstuff[k_eggmanexplode]) + { + fast->color = SKINCOLOR_RED; + fast->colorized = true; + } + } + + if (player->kartstuff[k_numboosts] > 0) // Boosting after images + { + mobj_t *ghost; + ghost = P_SpawnGhostMobj(player->mo); + ghost->extravalue1 = player->kartstuff[k_numboosts]+1; + ghost->extravalue2 = (leveltime % ghost->extravalue1); + ghost->fuse = ghost->extravalue1; + ghost->frame |= FF_FULLBRIGHT; + ghost->colorized = true; + //ghost->color = player->skincolor; + //ghost->momx = (3*player->mo->momx)/4; + //ghost->momy = (3*player->mo->momy)/4; + //ghost->momz = (3*player->mo->momz)/4; + if (leveltime & 1) + ghost->drawflags |= MFD_DONTDRAW; + } + + if (P_IsObjectOnGround(player->mo)) + { + // Offroad dust + if (player->kartstuff[k_boostpower] < FRACUNIT) + { + K_SpawnWipeoutTrail(player->mo, true); + if (leveltime % 6 == 0) + S_StartSound(player->mo, sfx_cdfm70); + } + + // Draft dust + if (player->kartstuff[k_draftpower] >= FRACUNIT) + { + K_SpawnDraftDust(player->mo); + /*if (leveltime % 23 == 0 || !S_SoundPlaying(player->mo, sfx_s265)) + S_StartSound(player->mo, sfx_s265);*/ + } + } + } + + if (G_RaceGametype() && player->kartstuff[k_rings] <= 0) // spawn ring debt indicator + { + mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, + player->mo->z + player->mo->momz + player->mo->height + (24*player->mo->scale), MT_THOK); + + P_SetMobjState(debtflag, S_RINGDEBT); + P_SetScale(debtflag, (debtflag->destscale = player->mo->scale)); + + K_MatchGenericExtraFlags(debtflag, player->mo); + debtflag->frame += (leveltime % 4); + + if ((leveltime/12) & 1) + debtflag->frame += 4; + + debtflag->color = player->skincolor; + debtflag->fuse = 2; + + debtflag->drawflags = K_GetPlayerDontDrawFlag(player); + } + + if (player->kartstuff[k_springstars] && (leveltime & 1)) + { + fixed_t randx = P_RandomRange(-40, 40) * player->mo->scale; + fixed_t randy = P_RandomRange(-40, 40) * player->mo->scale; + fixed_t randz = P_RandomRange(0, player->mo->height >> FRACBITS) << FRACBITS; + mobj_t *star = P_SpawnMobj( + player->mo->x + randx, + player->mo->y + randy, + player->mo->z + randz, + MT_KARMAFIREWORK); + + star->color = player->kartstuff[k_springcolor]; + star->flags |= MF_NOGRAVITY; + star->momx = player->mo->momx / 2; + star->momy = player->mo->momy / 2; + star->momz = player->mo->momz / 2; + star->fuse = 12; + star->scale = player->mo->scale; + star->destscale = star->scale / 2; + + player->kartstuff[k_springstars]--; + } + } + + if (player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Ensure these are set correctly here + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_eggmanexplode]) // You're gonna diiiiie + { + const INT32 flashtime = 4<<(player->kartstuff[k_eggmanexplode]/TICRATE); + if (player->kartstuff[k_eggmanexplode] == 1 || (player->kartstuff[k_eggmanexplode] % (flashtime/2) != 0)) + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_eggmanexplode] % flashtime == 0) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BLACK; + } + else + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_CRIMSON; + } + } + else if (player->kartstuff[k_invincibilitytimer]) // setting players to use the star colormap and spawning afterimages + { + player->mo->colorized = true; + } + else if (player->kartstuff[k_growshrinktimer]) // Ditto, for grow/shrink + { + if (player->kartstuff[k_growshrinktimer] % 5 == 0) + { + player->mo->colorized = true; + player->mo->color = (player->kartstuff[k_growshrinktimer] < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE); + } + else + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + } + else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie + { + const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); + if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_killfield] % flashtime == 0) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BYZANTIUM; + } + else + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_RUBY; + } + } + else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting + { + player->mo->colorized = true; + } + else + { + player->mo->colorized = false; + } + + if (player->kartstuff[k_itemtype] == KITEM_NONE) + player->kartstuff[k_holdready] = 0; + + // DKR style camera for boosting + if (player->karthud[khud_boostcam] != 0 || player->karthud[khud_destboostcam] != 0) + { + if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam] + && player->karthud[khud_destboostcam] != 0) + { + player->karthud[khud_boostcam] += FRACUNIT/(TICRATE/4); + if (player->karthud[khud_boostcam] >= player->karthud[khud_destboostcam]) + player->karthud[khud_destboostcam] = 0; + } + else + { + player->karthud[khud_boostcam] -= FRACUNIT/TICRATE; + if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam]) + player->karthud[khud_boostcam] = player->karthud[khud_destboostcam] = 0; + } + //CONS_Printf("cam: %d, dest: %d\n", player->karthud[khud_boostcam], player->karthud[khud_destboostcam]); + } + + player->karthud[khud_timeovercam] = 0; + + // Specific hack because it insists on setting flashing tics during this anyway... + if (player->kartstuff[k_spinouttype] == 2) + { + player->powers[pw_flashing] = 0; + } + // Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations. + else if (player->kartstuff[k_spinouttimer] != 0 + || player->kartstuff[k_wipeoutslow] != 0 + || player->kartstuff[k_squishedtimer] != 0) + { + player->powers[pw_flashing] = K_GetKartFlashing(player); + } + else if (player->powers[pw_flashing] >= K_GetKartFlashing(player)) + { + player->powers[pw_flashing]--; + } + + if (player->kartstuff[k_spinouttimer]) + { + if ((P_IsObjectOnGround(player->mo) + || (player->kartstuff[k_spinouttype] != 0)) + && (!player->kartstuff[k_sneakertimer])) + { + player->kartstuff[k_spinouttimer]--; + if (player->kartstuff[k_wipeoutslow] > 1) + player->kartstuff[k_wipeoutslow]--; + // Actually, this caused more problems than it solved. Just make sure you set type before you spinout. Which K_SpinPlayer always does. + /*if (player->kartstuff[k_spinouttimer] == 0) + player->kartstuff[k_spinouttype] = 0;*/ // Reset type + } + } + else + { + 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->kartstuff[k_rings] > 20) + player->kartstuff[k_rings] = 20; + else if (player->kartstuff[k_rings] < -20) + player->kartstuff[k_rings] = -20; + + if (player->kartstuff[k_ringdelay]) + player->kartstuff[k_ringdelay]--; + + if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_squishedtimer]) + player->kartstuff[k_ringboost] = 0; + else if (player->kartstuff[k_ringboost]) + player->kartstuff[k_ringboost]--; + + if (player->kartstuff[k_sneakertimer]) + { + player->kartstuff[k_sneakertimer]--; + + if (player->kartstuff[k_sneakertimer] <= 0) + { + player->kartstuff[k_numsneakers] = 0; + } + } + + if (player->kartstuff[k_flamedash]) + player->kartstuff[k_flamedash]--; + + if (player->kartstuff[k_sneakertimer] && player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1) + player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; + + if (player->kartstuff[k_floorboost]) + player->kartstuff[k_floorboost]--; + + if (player->kartstuff[k_driftboost]) + player->kartstuff[k_driftboost]--; + + if (player->kartstuff[k_startboost]) + player->kartstuff[k_startboost]--; + + if (player->kartstuff[k_invincibilitytimer]) + player->kartstuff[k_invincibilitytimer]--; + + if ((player->respawn.state == RESPAWNST_NONE) && player->kartstuff[k_growshrinktimer] != 0) + { + if (player->kartstuff[k_growshrinktimer] > 0) + player->kartstuff[k_growshrinktimer]--; + if (player->kartstuff[k_growshrinktimer] < 0) + player->kartstuff[k_growshrinktimer]++; + + // Back to normal + if (player->kartstuff[k_growshrinktimer] == 0) + K_RemoveGrowShrink(player); + } + + if (player->kartstuff[k_superring]) + { + if (player->kartstuff[k_superring] % 3 == 0) + { + mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); + ring->extravalue1 = 1; // Ring collect animation timer + ring->angle = player->mo->angle; // animation angle + P_SetTarget(&ring->target, player->mo); // toucher for thinker + player->kartstuff[k_pickuprings]++; + if (player->kartstuff[k_superring] <= 3) + ring->cvmem = 1; // play caching when collected + } + player->kartstuff[k_superring]--; + } + + if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0 + && player->kartstuff[k_rocketsneakertimer]) + player->kartstuff[k_rocketsneakertimer]--; + + if (player->kartstuff[k_hyudorotimer]) + player->kartstuff[k_hyudorotimer]--; + + if (player->kartstuff[k_sadtimer]) + player->kartstuff[k_sadtimer]--; + + if (player->kartstuff[k_stealingtimer]) + player->kartstuff[k_stealingtimer]--; + + if (player->kartstuff[k_stolentimer]) + player->kartstuff[k_stolentimer]--; + + if (player->kartstuff[k_squishedtimer]) + { + player->kartstuff[k_squishedtimer]--; + + if ((player->kartstuff[k_squishedtimer] == 0) && !(player->pflags & PF_NOCLIP)) + { + player->mo->flags &= ~MF_NOCLIP; + } + } + + if (player->kartstuff[k_justbumped]) + player->kartstuff[k_justbumped]--; + + if (player->kartstuff[k_tiregrease]) + player->kartstuff[k_tiregrease]--; + + // This doesn't go in HUD update because it has potential gameplay ramifications + if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0) + { + player->karthud[khud_itemblinkmode] = 0; + player->karthud[khud_itemblink] = 0; + } + + K_KartPlayerHUDUpdate(player); + + if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 + && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] + && (player->respawn.state == RESPAWNST_DROP) && !player->powers[pw_flashing]) + { + player->kartstuff[k_wanted]++; + if (battleovertime.enabled >= 10*TICRATE) + { + if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) + { + player->kartstuff[k_killfield]++; + if (player->kartstuff[k_killfield] > 4*TICRATE) + { + K_SpinPlayer(player, NULL, 0, NULL, false); + //player->kartstuff[k_killfield] = 1; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; + + if (P_IsObjectOnGround(player->mo)) + player->kartstuff[k_waterskip] = 0; + + if (player->kartstuff[k_instashield]) + player->kartstuff[k_instashield]--; + + if (player->kartstuff[k_eggmanexplode]) + { + if (player->spectator || (G_BattleGametype() && !player->kartstuff[k_bumper])) + player->kartstuff[k_eggmanexplode] = 0; + else + { + player->kartstuff[k_eggmanexplode]--; + if (player->kartstuff[k_eggmanexplode] <= 0) + { + mobj_t *eggsexplode; + //player->powers[pw_flashing] = 0; + eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION); + if (player->kartstuff[k_eggmanblame] >= 0 + && player->kartstuff[k_eggmanblame] < MAXPLAYERS + && playeringame[player->kartstuff[k_eggmanblame]] + && !players[player->kartstuff[k_eggmanblame]].spectator + && players[player->kartstuff[k_eggmanblame]].mo) + P_SetTarget(&eggsexplode->target, players[player->kartstuff[k_eggmanblame]].mo); + } + } + } + + if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) + { + if (RINGTOTAL(player) < 20 && !player->kartstuff[k_ringlock]) + K_LookForRings(player->mo); + } + + if (player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) + { + if (player->kartstuff[k_bubblecool]) + player->kartstuff[k_bubblecool]--; + } + else + { + player->kartstuff[k_bubbleblowup] = 0; + player->kartstuff[k_bubblecool] = 0; + } + + if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) + { + if (player->kartstuff[k_flamedash]) + K_FlameDashLeftoverSmoke(player->mo); + } + + if (player->kartstuff[k_comebacktimer]) + player->kartstuff[k_comebackmode] = 0; + + if (P_IsObjectOnGround(player->mo) && player->kartstuff[k_pogospring]) + { + if (P_MobjFlip(player->mo)*player->mo->momz <= 0) + player->kartstuff[k_pogospring] = 0; + } + + if (cmd->buttons & BT_DRIFT) + player->kartstuff[k_jmp] = 1; + else + player->kartstuff[k_jmp] = 0; + + // Roulette Code + K_KartItemRoulette(player, cmd); + + // Handle invincibility sfx + K_UpdateInvincibilitySounds(player); // Also thanks, VAda! + + // Plays the music after the starting countdown. + if (P_IsLocalPlayer(player) && leveltime == (starttime + (TICRATE/2))) + { + S_ChangeMusic(mapmusname, mapmusflags, true); + S_ShowMusicCredit(); + } +} + +void K_KartPlayerAfterThink(player_t *player) +{ + if (player->kartstuff[k_curshield] + || player->kartstuff[k_invincibilitytimer] + || (player->kartstuff[k_growshrinktimer] != 0 && player->kartstuff[k_growshrinktimer] % 5 == 4)) // 4 instead of 0 because this is afterthink! + { + player->mo->frame |= FF_FULLBRIGHT; + } + else + { + if (!(player->mo->state->frame & FF_FULLBRIGHT)) + player->mo->frame &= ~FF_FULLBRIGHT; + } + + // Move held objects (Bananas, Orbinaut, etc) + K_MoveHeldObjects(player); + + // Jawz reticule (seeking) + if (player->kartstuff[k_itemtype] == KITEM_JAWZ && player->kartstuff[k_itemheld]) + { + INT32 lasttarg = player->kartstuff[k_lastjawztarget]; + player_t *targ; + mobj_t *ret; + + if (player->kartstuff[k_jawztargetdelay] && playeringame[lasttarg] && !players[lasttarg].spectator) + { + targ = &players[lasttarg]; + player->kartstuff[k_jawztargetdelay]--; + } + else + targ = K_FindJawzTarget(player->mo, player); + + if (!targ || !targ->mo || P_MobjWasRemoved(targ->mo)) + { + player->kartstuff[k_lastjawztarget] = -1; + player->kartstuff[k_jawztargetdelay] = 0; + return; + } + + ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE); + P_SetTarget(&ret->target, targ->mo); + ret->frame |= ((leveltime % 10) / 2); + ret->tics = 1; + ret->color = player->skincolor; + + if (targ-players != lasttarg) + { + if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ)) + S_StartSound(NULL, sfx_s3k89); + else + S_StartSound(targ->mo, sfx_s3k89); + + player->kartstuff[k_lastjawztarget] = targ-players; + player->kartstuff[k_jawztargetdelay] = 5; + } + } + else + { + player->kartstuff[k_lastjawztarget] = -1; + player->kartstuff[k_jawztargetdelay] = 0; + } +} + +/*-------------------------------------------------- + static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) + + Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or + previous waypoints are infront of the player. + + Input Arguments:- + player - The player the next waypoint is being found for + + Return:- + The waypoint that is the player's next waypoint +--------------------------------------------------*/ +static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) +{ + waypoint_t *bestwaypoint = NULL; + + if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) + { + waypoint_t *waypoint = K_GetBestWaypointForMobj(player->mo); + boolean updaterespawn = false; + + bestwaypoint = waypoint; + + // check the waypoint's location in relation to the player + // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. + // EXCEPTION: If our best waypoint is the finishline AND we're facing towards it, don't do this. + // Otherwise it breaks the distance calculations. + if (waypoint != NULL) + { + boolean finishlinehack = false; + angle_t playerangle = player->mo->angle; + angle_t momangle = player->mo->angle; + 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) + { + angledelta = InvAngle(angledelta); + } + + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (bestwaypoint == K_GetFinishLineWaypoint()) + { + // facing towards the finishline + if (angledelta <= ANGLE_90) + { + finishlinehack = true; + } + } + + // We're using a lot of angle calculations here, because only using facing angle or only using momentum angle both have downsides. + // nextwaypoints will be picked if you're facing OR moving forward. + // prevwaypoints will be picked if you're facing AND moving backward. + if ((angledelta > ANGLE_45 || momdelta > ANGLE_45) + && (finishlinehack == false)) + { + angle_t nextbestdelta = angledelta; + angle_t nextbestmomdelta = momdelta; + size_t i = 0U; + + if (K_PlayerUsesBotMovement(player)) + { + // Try to force bots to use a next waypoint + nextbestdelta = ANGLE_MAX; + nextbestmomdelta = ANGLE_MAX; + } + + if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) + { + for (i = 0U; i < waypoint->numnextwaypoints; i++) + { + if (K_PlayerUsesBotMovement(player) == true + && K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) == true + && K_BotCanTakeCut(player) == false) + { + // Bots that aren't able to take a shortcut will ignore shortcut waypoints. + // (However, if they're already on a shortcut, then we want them to keep going.) + + if (player->nextwaypoint == NULL + || K_GetWaypointIsShortcut(player->nextwaypoint) == false) + { + continue; + } + } + + angletowaypoint = R_PointToAngle2( + player->mo->x, player->mo->y, + waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y); + + angledelta = playerangle - angletowaypoint; + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + } + + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) + { + bestwaypoint = waypoint->nextwaypoints[i]; + + if (angledelta < nextbestdelta) + { + nextbestdelta = angledelta; + } + if (momdelta < nextbestmomdelta) + { + nextbestmomdelta = momdelta; + } + + // Remove wrong way flag if we're using nextwaypoints + player->kartstuff[k_wrongway] = 0; + updaterespawn = true; + } + } + } + + if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U) + && !(K_PlayerUsesBotMovement(player))) // Bots do not need prev waypoints + { + for (i = 0U; i < waypoint->numprevwaypoints; i++) + { + angletowaypoint = R_PointToAngle2( + player->mo->x, player->mo->y, + waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); + + angledelta = playerangle - angletowaypoint; + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + } + + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (angledelta < nextbestdelta && momdelta < nextbestmomdelta) + { + bestwaypoint = waypoint->prevwaypoints[i]; + + nextbestdelta = angledelta; + nextbestmomdelta = momdelta; + + // Set wrong way flag if we're using prevwaypoints + player->kartstuff[k_wrongway] = 1; + updaterespawn = false; + } + } + } + } + } + + if (!P_IsObjectOnGround(player->mo)) + { + updaterespawn = false; + } + + // Respawn point should only be updated when we're going to a nextwaypoint + if ((updaterespawn) && + (player->respawn.state == RESPAWNST_NONE) && + (bestwaypoint != NULL) && + (bestwaypoint != player->nextwaypoint) && + (K_GetWaypointIsSpawnpoint(bestwaypoint)) && + (K_GetWaypointIsEnabled(bestwaypoint) == true)) + { + player->respawn.wp = bestwaypoint; + } + } + + return bestwaypoint; +} + +static boolean K_PlayerCloserToNextWaypoints(waypoint_t *const waypoint, player_t *const player) +{ + boolean nextiscloser = true; + + if ((waypoint != NULL) && (player != NULL) && (player->mo != NULL)) + { + size_t i = 0U; + waypoint_t *currentwpcheck = NULL; + angle_t angletoplayer = ANGLE_MAX; + angle_t currentanglecheck = ANGLE_MAX; + angle_t bestangle = ANGLE_MAX; + + angletoplayer = R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, + player->mo->x, player->mo->y); + + for (i = 0U; i < waypoint->numnextwaypoints; i++) + { + currentwpcheck = waypoint->nextwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + } + } + + for (i = 0U; i < waypoint->numprevwaypoints; i++) + { + currentwpcheck = waypoint->prevwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + nextiscloser = false; + break; + } + } + } + + return nextiscloser; +} + +/*-------------------------------------------------- + void K_UpdateDistanceFromFinishLine(player_t *const player) + + Updates the distance a player has to the finish line. + + Input Arguments:- + player - The player the distance is being updated for + + Return:- + None +--------------------------------------------------*/ +void K_UpdateDistanceFromFinishLine(player_t *const player) +{ + if ((player != NULL) && (player->mo != NULL)) + { + waypoint_t *finishline = K_GetFinishLineWaypoint(); + waypoint_t *nextwaypoint = NULL; + + if (player->spectator) + { + // Don't update waypoints while spectating + nextwaypoint = finishline; + } + else + { + nextwaypoint = K_GetPlayerNextWaypoint(player); + } + + if (nextwaypoint != NULL) + { + // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. + // player->nextwaypoint will keep its previous value in this case. + player->nextwaypoint = nextwaypoint; + } + + // nextwaypoint is now the waypoint that is in front of us + if (player->exiting || player->spectator) + { + // Player has finished, we don't need to calculate this + player->distancetofinish = 0U; + } + else if ((player->nextwaypoint != NULL) && (finishline != NULL)) + { + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {}; + + pathfindsuccess = + K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); + + // Update the player's distance to the finish line if a path was found. + // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track + if (pathfindsuccess == true) + { + // Add euclidean distance to the next waypoint to the distancetofinish + UINT32 adddist; + fixed_t disttowaypoint = + P_AproxDistance( + (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), + (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); + disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); + + adddist = (UINT32)disttowaypoint; + + player->distancetofinish = pathtofinish.totaldist + adddist; + Z_Free(pathtofinish.array); + + // distancetofinish is currently a flat distance to the finish line, but in order to be fully + // correct we need to add to it the length of the entire circuit multiplied by the number of laps + // left after this one. This will give us the total distance to the finish line, and allow item + // distance calculation to work easily + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) + { + const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); + + player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); + + // An additional HACK, to fix looking backwards towards the finish line + // If the player's next waypoint is the finishline and the angle distance from player to + // connectin waypoints implies they're closer to a next waypoint, add a full track distance + if (player->nextwaypoint == finishline) + { + if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) + { + player->distancetofinish += K_GetCircuitLength(); + } + } + } + } + } + } +} + +INT32 K_GetKartRingPower(player_t *player) +{ + return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2); +} + +// Returns false if this player being placed here causes them to collide with any other player +// Used in g_game.c for match etc. respawning +// This does not check along the z because the z is not correctly set for the spawnee at this point +boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y) +{ + INT32 i; + fixed_t p1radius = players[playernum].mo->radius; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playernum == i || !playeringame[i] || players[i].spectator || !players[i].mo || players[i].mo->health <= 0 + || players[i].playerstate != PST_LIVE || (players[i].mo->flags & MF_NOCLIP) || (players[i].mo->flags & MF_NOCLIPTHING)) + continue; + + if (abs(x - players[i].mo->x) < (p1radius + players[i].mo->radius) + && abs(y - players[i].mo->y) < (p1radius + players[i].mo->radius)) + { + return false; + } + } + return true; +} + +// countersteer is how strong the controls are telling us we are turning +// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left +static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) +{ + INT16 basedrift, driftadjust; + fixed_t driftweight = player->kartweight*14; // 12 + + if (player->kartstuff[k_drift] == 0 || !P_IsObjectOnGround(player->mo)) + { + // If they aren't drifting or on the ground, this doesn't apply + return 0; + } + + if (player->kartstuff[k_driftend] != 0) + { + // Drift has ended and we are tweaking their angle back a bit + return -266*player->kartstuff[k_drift]; + } + + basedrift = (83 * player->kartstuff[k_drift]) - (((driftweight - 14) * player->kartstuff[k_drift]) / 5); // 415 - 303 + driftadjust = abs((252 - driftweight) * player->kartstuff[k_drift] / 5); + + if (player->kartstuff[k_tiregrease] > 0) // Buff drift-steering while in greasemode + { + basedrift += (basedrift / greasetics) * player->kartstuff[k_tiregrease]; + } + + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) + { + countersteer = 3*countersteer/2; + } + + return basedrift + (FixedMul(driftadjust * FRACUNIT, countersteer) / FRACUNIT); +} + +INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) +{ + fixed_t p_maxspeed = K_GetKartSpeed(player, false); + fixed_t p_speed = min(player->speed, (p_maxspeed * 2)); + fixed_t weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); + + if (player->spectator) + { + return turnvalue; + } + + if (K_PlayerUsesBotMovement(player)) + { + turnvalue = 5*turnvalue/4; // Base increase to turning + turnvalue = FixedMul( + turnvalue * FRACUNIT, + K_BotRubberband(player) + ) / FRACUNIT; + } + + if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo)) + { + fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); + + // If we're drifting we have a completely different turning value + + if (player->kartstuff[k_driftend] != 0) + { + countersteer = FRACUNIT; + } + + turnvalue = K_GetKartDriftValue(player, countersteer); + + return turnvalue; + } + + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) + { + turnvalue = 5*turnvalue/4; + } + + if (player->kartstuff[k_flamedash] > 0) + { + fixed_t multiplier = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); + multiplier = FRACUNIT + (FixedDiv(multiplier, FRACUNIT/2) / 4); + turnvalue = FixedMul(turnvalue * FRACUNIT, multiplier) / FRACUNIT; + } + + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) + { + turnvalue = 3*turnvalue/2; + } + + // Weight has a small effect on turning + turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; + + return turnvalue; +} + +INT32 K_GetKartDriftSparkValue(player_t *player) +{ + UINT8 kartspeed = (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + ? 1 + : player->kartspeed; + return (26*4 + kartspeed*2 + (9 - player->kartweight))*8; +} + +/* +Stage 1: red sparks +Stage 2: blue sparks +Stage 3: big large rainbow sparks +*/ +static void K_SpawnDriftBoostExplosion(player_t *player, int stage) +{ + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); + + P_SetTarget(&overlay->target, player->mo); + P_SetScale(overlay, (overlay->destscale = player->mo->scale)); + K_FlipFromObject(overlay, player->mo); + + switch (stage) + { + case 1: + overlay->color = SKINCOLOR_KETCHUP; + overlay->fuse = 16; + break; + + case 2: + overlay->color = SKINCOLOR_SAPPHIRE; + overlay->fuse = 32; + + S_StartSound(player->mo, sfx_kc5b); + break; + + case 3: + overlay->color = SKINCOLOR_SILVER; + overlay->fuse = 120; + + S_StartSound(player->mo, sfx_kc5b); + S_StartSound(player->mo, sfx_s3kc4l); + break; + } +} + +static void K_KartDrift(player_t *player, boolean onground) +{ + fixed_t minspeed = (10 * player->mo->scale); + INT32 dsone = K_GetKartDriftSparkValue(player); + INT32 dstwo = dsone*2; + INT32 dsthree = dstwo*2; + + // Drifting is actually straffing + automatic turning. + // Holding the Jump button will enable drifting. + + // Drift Release (Moved here so you can't "chain" drifts) + if (player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5) + { + if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) + { + angle_t pushdir = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + + S_StartSound(player->mo, sfx_s23c); + //K_SpawnDashDustRelease(player); + + if (player->kartstuff[k_driftcharge] < 0) + { + // Stage 0: Yellow sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 8); + + if (player->kartstuff[k_driftboost] < 15) + player->kartstuff[k_driftboost] = 15; + } + else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) + { + // Stage 1: Red sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 4); + + if (player->kartstuff[k_driftboost] < 20) + player->kartstuff[k_driftboost] = 20; + + K_SpawnDriftBoostExplosion(player, 1); + } + else if (player->kartstuff[k_driftcharge] < dsthree) + { + // Stage 2: Blue sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 3); + + if (player->kartstuff[k_driftboost] < 50) + player->kartstuff[k_driftboost] = 50; + + K_SpawnDriftBoostExplosion(player, 2); + } + else if (player->kartstuff[k_driftcharge] >= dsthree) + { + // Stage 3: Rainbow sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 2); + + if (player->kartstuff[k_driftboost] < 125) + player->kartstuff[k_driftboost] = 125; + + K_SpawnDriftBoostExplosion(player, 3); + } + } + + // Remove charge + player->kartstuff[k_driftcharge] = 0; + } + + // Drifting: left or right? + if ((player->cmd.driftturn > 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 + && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != 1) + { + // Starting left drift + player->kartstuff[k_drift] = 1; + player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; + } + else if ((player->cmd.driftturn < 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 + && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != -1) + { + // Starting right drift + player->kartstuff[k_drift] = -1; + player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; + } + else if (player->kartstuff[k_jmp] == 0) // || player->kartstuff[k_turndir] == 0) + { + // drift is not being performed so if we're just finishing set driftend and decrement counters + if (player->kartstuff[k_drift] > 0) + { + player->kartstuff[k_drift]--; + player->kartstuff[k_driftend] = 1; + } + else if (player->kartstuff[k_drift] < 0) + { + player->kartstuff[k_drift]++; + player->kartstuff[k_driftend] = 1; + } + else + player->kartstuff[k_driftend] = 0; + } + + if (player->kartstuff[k_spinouttimer] > 0 || player->speed == 0) + { + // Stop drifting + player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; + player->kartstuff[k_getsparks] = 0; + } + else if (player->kartstuff[k_jmp] == 1 && player->kartstuff[k_drift] != 0) + { + // Incease/decrease the drift value to continue drifting in that direction + fixed_t driftadditive = 24; + boolean playsound = false; + + if (onground) + { + if (player->kartstuff[k_drift] >= 1) // Drifting to the left + { + player->kartstuff[k_drift]++; + if (player->kartstuff[k_drift] > 5) + player->kartstuff[k_drift] = 5; + + if (player->cmd.driftturn > 0) // Inward + driftadditive += abs(player->cmd.driftturn)/100; + if (player->cmd.driftturn < 0) // Outward + driftadditive -= abs(player->cmd.driftturn)/75; + } + else if (player->kartstuff[k_drift] <= -1) // Drifting to the right + { + player->kartstuff[k_drift]--; + if (player->kartstuff[k_drift] < -5) + player->kartstuff[k_drift] = -5; + + if (player->cmd.driftturn < 0) // Inward + driftadditive += abs(player->cmd.driftturn)/100; + if (player->cmd.driftturn > 0) // Outward + driftadditive -= abs(player->cmd.driftturn)/75; + } + + // Disable drift-sparks until you're going fast enough + if (player->kartstuff[k_getsparks] == 0 + || (player->kartstuff[k_offroad] && K_ApplyOffroad(player))) + driftadditive = 0; + + // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. + if (player->speed > minspeed*2) + { + player->kartstuff[k_getsparks] = 1; + + if (player->kartstuff[k_driftcharge] <= -1) + { + player->kartstuff[k_driftcharge] = dsone; // Back to red + playsound = true; + } + } + else if (player->speed <= minspeed) + { + player->kartstuff[k_getsparks] = 0; + driftadditive = 0; + + if (player->kartstuff[k_driftcharge] >= dsone) + { + player->kartstuff[k_driftcharge] = -1; // Set yellow sparks + playsound = true; + } + } + } + else + { + driftadditive = 0; + } + + // This spawns the drift sparks + if ((player->kartstuff[k_driftcharge] + driftadditive >= dsone) + || (player->kartstuff[k_driftcharge] < 0)) + { + K_SpawnDriftSparks(player); + } + + if ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone) + || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo) + || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree)) + { + playsound = true; + } + + // Sound whenever you get a different tier of sparks + if (playsound && P_IsDisplayPlayer(player)) + { + if (player->kartstuff[k_driftcharge] == -1) + S_StartSoundAtVolume(player->mo, sfx_sploss, 192); // Yellow spark sound + else + S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); + } + + player->kartstuff[k_driftcharge] += driftadditive; + player->kartstuff[k_driftend] = 0; + } + + if ((!player->kartstuff[k_sneakertimer]) + || (!player->cmd.driftturn) + || (!player->kartstuff[k_aizdriftstrat]) + || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) + { + if (!player->kartstuff[k_drift]) + player->kartstuff[k_aizdriftstrat] = 0; + else + player->kartstuff[k_aizdriftstrat] = ((player->kartstuff[k_drift] > 0) ? 1 : -1); + } + else if (player->kartstuff[k_aizdriftstrat] && !player->kartstuff[k_drift]) + K_SpawnAIZDust(player); + + if (player->kartstuff[k_drift] + && ((player->cmd.buttons & BT_BRAKE) + || !(player->cmd.buttons & BT_ACCELERATE)) + && P_IsObjectOnGround(player->mo)) + { + if (!player->kartstuff[k_brakedrift]) + K_SpawnBrakeDriftSparks(player); + player->kartstuff[k_brakedrift] = 1; + } + else + player->kartstuff[k_brakedrift] = 0; +} +// +// K_KartUpdatePosition +// +void K_KartUpdatePosition(player_t *player) +{ + fixed_t position = 1; + fixed_t oldposition = player->kartstuff[k_position]; + fixed_t i; + + if (player->spectator || !player->mo) + { + // Ensure these are reset for spectators + player->kartstuff[k_position] = 0; + player->kartstuff[k_positiondelay] = 0; + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + if (G_RaceGametype()) + { + if (player->exiting) // End of match standings + { + // Only time matters + if (players[i].realtime < player->realtime) + position++; + } + else + { + // I'm a lap behind this player OR + // My distance to the finish line is higher, so I'm behind + if ((players[i].laps > player->laps) + || (players[i].distancetofinish < player->distancetofinish)) + { + position++; + } + } + } + else if (G_BattleGametype()) + { + if (player->exiting) // End of match standings + { + // Only score matters + if (players[i].marescore > player->marescore) + position++; + } + 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])) + position++; + } + } + } + + if (leveltime < starttime || oldposition == 0) + oldposition = position; + + if (oldposition != position) // Changed places? + player->kartstuff[k_positiondelay] = 10; // Position number growth + + player->kartstuff[k_position] = position; +} + +// +// K_StripItems +// +void K_StripItems(player_t *player) +{ + player->kartstuff[k_itemtype] = KITEM_NONE; + player->kartstuff[k_itemamount] = 0; + player->kartstuff[k_itemheld] = 0; + + player->kartstuff[k_rocketsneakertimer] = 0; + + if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2) + { + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + } + player->kartstuff[k_eggmanheld] = 0; + + player->kartstuff[k_hyudorotimer] = 0; + player->kartstuff[k_stealingtimer] = 0; + player->kartstuff[k_stolentimer] = 0; + + player->kartstuff[k_curshield] = KSHIELD_NONE; + player->kartstuff[k_bananadrag] = 0; + + player->kartstuff[k_sadtimer] = 0; + + K_UpdateHnextList(player, true); +} + +void K_StripOther(player_t *player) +{ + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + + player->kartstuff[k_invincibilitytimer] = 0; + K_RemoveGrowShrink(player); + + if (player->kartstuff[k_eggmanexplode]) + { + player->kartstuff[k_eggmanexplode] = 0; + player->kartstuff[k_eggmanblame] = -1; + } +} + +static INT32 K_FlameShieldMax(player_t *player) +{ + UINT32 disttofinish = 0; + UINT32 distv = DISTVAR; + UINT8 numplayers = 0; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + numplayers++; + if (players[i].kartstuff[k_position] == 1) + disttofinish = players[i].distancetofinish; + } + + if (numplayers <= 1) + { + return 16; // max when alone, for testing + } + else if (player->kartstuff[k_position] == 1) + { + return 0; // minimum for first + } + + disttofinish = player->distancetofinish - disttofinish; + distv = FixedMul(distv * FRACUNIT, mapobjectscale) / FRACUNIT; + return min(16, 1 + (disttofinish / distv)); +} + +// +// K_MoveKartPlayer +// +void K_MoveKartPlayer(player_t *player, boolean onground) +{ + ticcmd_t *cmd = &player->cmd; + boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->pflags & PF_ATTACKDOWN)); + boolean HOLDING_ITEM = (player->kartstuff[k_itemheld] || player->kartstuff[k_eggmanheld]); + boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0); + + player->pflags &= ~PF_HITFINISHLINE; + + if (!player->exiting) + { + if (player->kartstuff[k_oldposition] < player->kartstuff[k_position]) // But first, if you lost a place, + { + player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // then the other player taunts. + K_RegularVoiceTimers(player); // and you can't for a bit + } + else if (player->kartstuff[k_oldposition] > player->kartstuff[k_position]) // Otherwise, + { + K_PlayOvertakeSound(player->mo); // Say "YOU'RE TOO SLOW!" + player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // Restore the old position, + } + } + + if (player->kartstuff[k_positiondelay]) + player->kartstuff[k_positiondelay]--; + + // Prevent ring misfire + if (!(cmd->buttons & BT_ATTACK)) + { + if (player->kartstuff[k_itemtype] == KITEM_NONE + && NO_HYUDORO && !(HOLDING_ITEM + || player->kartstuff[k_itemamount] + || player->kartstuff[k_itemroulette] + || player->kartstuff[k_rocketsneakertimer] + || player->kartstuff[k_eggmanexplode])) + player->kartstuff[k_userings] = 1; + else + player->kartstuff[k_userings] = 0; + } + + if ((player->pflags & PF_ATTACKDOWN) && !(cmd->buttons & BT_ATTACK)) + player->pflags &= ~PF_ATTACKDOWN; + else if (cmd->buttons & BT_ATTACK) + player->pflags |= PF_ATTACKDOWN; + + if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > starttime + && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && (player->respawn.state == RESPAWNST_NONE)) + { + // First, the really specific, finicky items that function without the item being directly in your item slot. + // Karma item dropping + if (player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer]) + { + if (ATTACK_IS_DOWN) + { + mobj_t *newitem; + + if (player->kartstuff[k_comebackmode] == 1) + { + newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RANDOMITEM); + newitem->threshold = 69; // selected "randomly". + } + else + { + newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM); + if (player->kartstuff[k_eggmanblame] >= 0 + && player->kartstuff[k_eggmanblame] < MAXPLAYERS + && playeringame[player->kartstuff[k_eggmanblame]] + && !players[player->kartstuff[k_eggmanblame]].spectator + && players[player->kartstuff[k_eggmanblame]].mo) + P_SetTarget(&newitem->target, players[player->kartstuff[k_eggmanblame]].mo); + player->kartstuff[k_eggmanblame] = -1; + } + + newitem->flags2 = (player->mo->flags2 & MF2_OBJECTFLIP); + newitem->fuse = 15*TICRATE; // selected randomly. + + player->kartstuff[k_comebackmode] = 0; + player->kartstuff[k_comebacktimer] = comebacktime; + S_StartSound(player->mo, sfx_s254); + } + } + else + { + // Ring boosting + if (player->kartstuff[k_userings]) + { + if ((player->pflags & PF_ATTACKDOWN) && !player->kartstuff[k_ringdelay] && player->kartstuff[k_rings] > 0) + { + mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); + P_SetMobjState(ring, S_FASTRING1); + ring->extravalue1 = 1; // Ring use animation timer + ring->extravalue2 = 1; // Ring use animation flag + ring->shadowscale = 0; + P_SetTarget(&ring->target, player->mo); // user + player->kartstuff[k_rings]--; + player->kartstuff[k_ringdelay] = 3; + } + } + // Other items + else + { + // Eggman Monitor exploding + if (player->kartstuff[k_eggmanexplode]) + { + if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanexplode] <= 3*TICRATE && player->kartstuff[k_eggmanexplode] > 1) + player->kartstuff[k_eggmanexplode] = 1; + } + // Eggman Monitor throwing + else if (player->kartstuff[k_eggmanheld]) + { + if (ATTACK_IS_DOWN) + { + K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_eggmanheld] = 0; + K_UpdateHnextList(player, true); + } + } + // Rocket Sneaker usage + else if (player->kartstuff[k_rocketsneakertimer] > 1) + { + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) + { + K_DoSneaker(player, 2); + K_PlayBoostTaunt(player->mo); + player->kartstuff[k_rocketsneakertimer] -= 3*TICRATE; + if (player->kartstuff[k_rocketsneakertimer] < 1) + player->kartstuff[k_rocketsneakertimer] = 1; + } + } + else if (player->kartstuff[k_itemamount] <= 0) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + } + else + { + switch (player->kartstuff[k_itemtype]) + { + case KITEM_SNEAKER: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) + { + K_DoSneaker(player, 1); + K_PlayBoostTaunt(player->mo); + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_ROCKETSNEAKER: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO + && player->kartstuff[k_rocketsneakertimer] == 0) + { + INT32 moloop; + mobj_t *mo = NULL; + mobj_t *prev = player->mo; + + K_PlayBoostTaunt(player->mo); + //player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s3k3a); + + //K_DoSneaker(player, 2); + + player->kartstuff[k_rocketsneakertimer] = (itemtime*3); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, true); + + for (moloop = 0; moloop < 2; moloop++) + { + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ROCKETSNEAKER); + K_MatchGenericExtraFlags(mo, player->mo); + mo->flags |= MF_NOCLIPTHING; + mo->angle = player->mo->angle; + mo->threshold = 10; + mo->movecount = moloop%2; + mo->movedir = mo->lastlook = moloop+1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + break; + case KITEM_INVINCIBILITY: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage normally, so you're free to waste it if you have multiple + { + if (!player->kartstuff[k_invincibilitytimer]) + { + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INVULNFLASH); + P_SetTarget(&overlay->target, player->mo); + overlay->destscale = player->mo->scale; + P_SetScale(overlay, player->mo->scale); + } + player->kartstuff[k_invincibilitytimer] = itemtime+(2*TICRATE); // 10 seconds + if (P_IsLocalPlayer(player)) + S_ChangeMusicSpecial("kinvnc"); + if (! P_IsDisplayPlayer(player)) + S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kinvnc)); + P_RestoreMusic(player); + K_PlayPowerGloatSound(player->mo); + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_BANANA: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + INT32 moloop; + mobj_t *mo; + mobj_t *prev = player->mo; + + //K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s254); + + for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) + { + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BANANA_SHIELD); + if (!mo) + { + player->kartstuff[k_itemamount] = moloop; + break; + } + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = player->kartstuff[k_itemamount]; + mo->movedir = moloop+1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Banana x3 thrown + { + K_ThrowKartItem(player, false, MT_BANANA, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, false); + } + break; + case KITEM_EGGMAN: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + mobj_t *mo; + player->kartstuff[k_itemamount]--; + player->kartstuff[k_eggmanheld] = 1; + S_StartSound(player->mo, sfx_s254); + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD); + if (mo) + { + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = 1; + mo->movedir = 1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&player->mo->hnext, mo); + } + } + break; + case KITEM_ORBINAUT: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + angle_t newangle; + INT32 moloop; + mobj_t *mo = NULL; + mobj_t *prev = player->mo; + + //K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s3k3a); + + for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) + { + newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ORBINAUT_SHIELD); + if (!mo) + { + player->kartstuff[k_itemamount] = moloop; + break; + } + mo->flags |= MF_NOCLIPTHING; + mo->angle = newangle; + mo->threshold = 10; + mo->movecount = player->kartstuff[k_itemamount]; + mo->movedir = mo->lastlook = moloop+1; + mo->color = player->skincolor; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Orbinaut x3 thrown + { + K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, false); + } + break; + case KITEM_JAWZ: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + angle_t newangle; + INT32 moloop; + mobj_t *mo = NULL; + mobj_t *prev = player->mo; + + //K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s3k3a); + + for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) + { + newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_JAWZ_SHIELD); + if (!mo) + { + player->kartstuff[k_itemamount] = moloop; + break; + } + mo->flags |= MF_NOCLIPTHING; + mo->angle = newangle; + mo->threshold = 10; + mo->movecount = player->kartstuff[k_itemamount]; + mo->movedir = mo->lastlook = moloop+1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Jawz thrown + { + if (player->kartstuff[k_throwdir] == 1 || player->kartstuff[k_throwdir] == 0) + K_ThrowKartItem(player, true, MT_JAWZ, 1, 0); + else if (player->kartstuff[k_throwdir] == -1) // Throwing backward gives you a dud that doesn't home in + K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, false); + } + break; + case KITEM_MINE: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + mobj_t *mo; + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s254); + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SSMINE_SHIELD); + if (mo) + { + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = 1; + mo->movedir = 1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&player->mo->hnext, mo); + } + } + else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) + { + K_ThrowKartItem(player, false, MT_SSMINE, 1, 1); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + player->kartstuff[k_itemheld] = 0; + K_UpdateHnextList(player, true); + } + break; + case KITEM_BALLHOG: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_itemamount]--; + K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0); + K_PlayAttackTaunt(player->mo); + } + break; + case KITEM_SPB: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_itemamount]--; + K_ThrowKartItem(player, true, MT_SPB, 1, 0); + K_PlayAttackTaunt(player->mo); + } + break; + case KITEM_GROW: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + if (player->kartstuff[k_growshrinktimer] < 0) // If you're shrunk, then "grow" will just make you normal again. + K_RemoveGrowShrink(player); + else + { + K_PlayPowerGloatSound(player->mo); + player->mo->scalespeed = mapobjectscale/TICRATE; + player->mo->destscale = (3*mapobjectscale)/2; + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + player->mo->destscale = (6*player->mo->destscale)/8; + player->kartstuff[k_growshrinktimer] = itemtime+(4*TICRATE); // 12 seconds + if (P_IsLocalPlayer(player)) + S_ChangeMusicSpecial("kgrow"); + if (! P_IsDisplayPlayer(player)) + S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); + P_RestoreMusic(player); + S_StartSound(player->mo, sfx_kc5a); + } + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_SHRINK: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_DoShrink(player); + player->kartstuff[k_itemamount]--; + K_PlayPowerGloatSound(player->mo); + } + break; + case KITEM_THUNDERSHIELD: + if (player->kartstuff[k_curshield] != KSHIELD_THUNDER) + { + mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THUNDERSHIELD); + P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); + P_SetTarget(&shield->target, player->mo); + S_StartSound(player->mo, sfx_s3k41); + player->kartstuff[k_curshield] = KSHIELD_THUNDER; + } + + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_DoThunderShield(player); + player->kartstuff[k_itemamount]--; + K_PlayAttackTaunt(player->mo); + } + break; + case KITEM_BUBBLESHIELD: + if (player->kartstuff[k_curshield] != KSHIELD_BUBBLE) + { + mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BUBBLESHIELD); + P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); + P_SetTarget(&shield->target, player->mo); + S_StartSound(player->mo, sfx_s3k3f); + player->kartstuff[k_curshield] = KSHIELD_BUBBLE; + } + + if (!HOLDING_ITEM && NO_HYUDORO) + { + if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) + { + if (player->kartstuff[k_bubbleblowup] == 0) + S_StartSound(player->mo, sfx_s3k75); + + player->kartstuff[k_bubbleblowup]++; + player->kartstuff[k_bubblecool] = player->kartstuff[k_bubbleblowup]*4; + + if (player->kartstuff[k_bubbleblowup] > bubbletime*2) + { + K_ThrowKartItem(player, (player->kartstuff[k_throwdir] > 0), MT_BUBBLESHIELDTRAP, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_bubbleblowup] = 0; + player->kartstuff[k_bubblecool] = 0; + player->kartstuff[k_holdready] = 0; + player->kartstuff[k_itemamount]--; + } + } + else + { + if (player->kartstuff[k_bubbleblowup] > bubbletime) + player->kartstuff[k_bubbleblowup] = bubbletime; + + if (player->kartstuff[k_bubbleblowup]) + player->kartstuff[k_bubbleblowup]--; + + player->kartstuff[k_holdready] = (player->kartstuff[k_bubblecool] ? 0 : 1); + } + } + break; + case KITEM_FLAMESHIELD: + if (player->kartstuff[k_curshield] != KSHIELD_FLAME) + { + mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_FLAMESHIELD); + P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); + P_SetTarget(&shield->target, player->mo); + S_StartSound(player->mo, sfx_s3k3e); + player->kartstuff[k_curshield] = KSHIELD_FLAME; + } + + if (!HOLDING_ITEM && NO_HYUDORO) + { + INT32 destlen = K_FlameShieldMax(player); + INT32 flamemax = 0; + + if (player->kartstuff[k_flamelength] < destlen) + player->kartstuff[k_flamelength]++; // Can always go up! + + flamemax = player->kartstuff[k_flamelength] * flameseg; + if (flamemax > 0) + flamemax += TICRATE; // leniency period + + if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) + { + if (player->kartstuff[k_flamemeter] < 0) + player->kartstuff[k_flamemeter] = 0; + + if (player->kartstuff[k_flamedash] == 0) + { + S_StartSound(player->mo, sfx_s3k43); + K_PlayBoostTaunt(player->mo); + } + + player->kartstuff[k_flamedash] += 2; + player->kartstuff[k_flamemeter] += 2; + + if (!onground) + { + P_Thrust( + player->mo, R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), + FixedMul(player->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) + ); + } + + if (player->kartstuff[k_flamemeter] > flamemax) + { + P_Thrust( + player->mo, player->mo->angle, + FixedMul((50*player->mo->scale), K_GetKartGameSpeedScalar(gamespeed)) + ); + + player->kartstuff[k_flamemeter] = 0; + player->kartstuff[k_flamelength] = 0; + player->kartstuff[k_holdready] = 0; + player->kartstuff[k_itemamount]--; + } + } + else + { + player->kartstuff[k_holdready] = 1; + + if (player->kartstuff[k_flamemeter] > 0) + player->kartstuff[k_flamemeter]--; + + if (player->kartstuff[k_flamelength] > destlen) + { + player->kartstuff[k_flamelength]--; // Can ONLY go down if you're not using it + + flamemax = player->kartstuff[k_flamelength] * flameseg; + if (flamemax > 0) + flamemax += TICRATE; // leniency period + } + + if (player->kartstuff[k_flamemeter] > flamemax) + player->kartstuff[k_flamemeter] = flamemax; + } + } + break; + case KITEM_HYUDORO: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_itemamount]--; + K_DoHyudoroSteal(player); // yes. yes they do. + } + break; + case KITEM_POGOSPRING: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO + && !player->kartstuff[k_pogospring]) + { + K_PlayBoostTaunt(player->mo); + K_DoPogoSpring(player->mo, 32<kartstuff[k_pogospring] = 1; + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_SUPERRING: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_superring] += (10*3); + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_KITCHENSINK: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + mobj_t *mo; + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s254); + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SINK_SHIELD); + if (mo) + { + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = 1; + mo->movedir = 1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&player->mo->hnext, mo); + } + } + else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Sink thrown + { + K_ThrowKartItem(player, false, MT_SINK, 1, 2); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + player->kartstuff[k_itemheld] = 0; + K_UpdateHnextList(player, true); + } + break; + case KITEM_SAD: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO + && !player->kartstuff[k_sadtimer]) + { + player->kartstuff[k_sadtimer] = stealtime; + player->kartstuff[k_itemamount]--; + } + break; + default: + break; + } + } + } + } + + // No more! + if (!player->kartstuff[k_itemamount]) + { + player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } + + if (K_GetShieldFromItem(player->kartstuff[k_itemtype]) == KSHIELD_NONE) + { + player->kartstuff[k_curshield] = KSHIELD_NONE; // RESET shield type + player->kartstuff[k_bubbleblowup] = 0; + player->kartstuff[k_bubblecool] = 0; + player->kartstuff[k_flamelength] = 0; + player->kartstuff[k_flamemeter] = 0; + } + + if (spbplace == -1 || player->kartstuff[k_position] != spbplace) + player->kartstuff[k_ringlock] = 0; // reset ring lock + + if (player->kartstuff[k_itemtype] == KITEM_SPB + || player->kartstuff[k_itemtype] == KITEM_SHRINK + || player->kartstuff[k_growshrinktimer] < 0) + indirectitemcooldown = 20*TICRATE; + + if (player->kartstuff[k_hyudorotimer] > 0) + { + INT32 hyu = hyudorotime; + + if (G_RaceGametype()) + hyu *= 2; // double in race + + if (leveltime & 1) + { + player->mo->drawflags |= MFD_DONTDRAW; + } + else + { + if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) + player->mo->drawflags &= ~K_GetPlayerDontDrawFlag(player); + else + player->mo->drawflags &= ~MFD_DONTDRAW; + } + + player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints + } + else if (player->kartstuff[k_hyudorotimer] == 0) + { + player->mo->drawflags &= ~MFD_DONTDRAW; + } + + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 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]; + } + else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0) + { + player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); + } + } + + if (onground) + { + fixed_t prevfriction = player->mo->friction; + + // Reduce friction after hitting a horizontal spring + if (player->kartstuff[k_tiregrease]) + player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->kartstuff[k_tiregrease]; + + // Friction + if (!player->kartstuff[k_offroad]) + { + if (player->speed > 0 && cmd->forwardmove == 0 && player->mo->friction == 59392) + player->mo->friction += 4608; + } + + // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel + if (player->speed > 0 && cmd->forwardmove < 0) + player->mo->friction -= 2048; + + // Karma ice physics + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + player->mo->friction += 1228; + + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) + player->mo->friction += 614; + + // Wipeout slowdown + if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow]) + { + if (player->kartstuff[k_offroad]) + player->mo->friction -= 4912; + if (player->kartstuff[k_wipeoutslow] == 1) + player->mo->friction -= 9824; + } + + // Friction was changed, so we must recalculate a bunch of stuff + if (player->mo->friction != prevfriction) + { + if (player->mo->friction > FRACUNIT) + player->mo->friction = FRACUNIT; + if (player->mo->friction < 0) + player->mo->friction = 0; + + player->mo->movefactor = FixedDiv(ORIG_FRICTION, player->mo->friction); + + if (player->mo->movefactor < FRACUNIT) + player->mo->movefactor = 19*player->mo->movefactor - 18*FRACUNIT; + else + player->mo->movefactor = FRACUNIT; + + if (player->mo->movefactor < 32) + player->mo->movefactor = 32; + } + + // Don't go too far above your top speed when rubberbanding + // Down here, because we do NOT want to modify movefactor + if (K_PlayerUsesBotMovement(player)) + { + player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction); + } + } + + K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads + + // Quick Turning + // You can't turn your kart when you're not moving. + // So now it's time to burn some rubber! + if (player->speed < 2 && leveltime > starttime && cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE && cmd->driftturn != 0) + { + if (leveltime % 8 == 0) + S_StartSound(player->mo, sfx_s224); + } + + // Squishing + // If a Grow player or a sector crushes you, get flattened instead of being killed. + + if (player->kartstuff[k_squishedtimer] > 0) + { + //player->mo->flags |= MF_NOCLIP; + player->mo->momx = 0; + player->mo->momy = 0; + } + + // Play the starting countdown sounds + if (player == &players[g_localplayers[0]]) // Don't play louder in splitscreen + { + if ((leveltime == starttime-(3*TICRATE)) || (leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) + S_StartSound(NULL, sfx_s3ka7); + if (leveltime == starttime) + { + S_StartSound(NULL, sfx_s3kad); + S_StopMusic(); // The GO! sound stops the level start ambience + } + } + + // Start charging once you're given the opportunity. + if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) + { + if (cmd->buttons & BT_ACCELERATE) + { + if (player->kartstuff[k_boostcharge] == 0) + player->kartstuff[k_boostcharge] = cmd->latency; + + player->kartstuff[k_boostcharge]++; + } + else + player->kartstuff[k_boostcharge] = 0; + } + + // Increase your size while charging your engine. + if (leveltime < starttime+10) + { + player->mo->scalespeed = mapobjectscale/12; + player->mo->destscale = mapobjectscale + (player->kartstuff[k_boostcharge]*131); + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + player->mo->destscale = (6*player->mo->destscale)/8; + } + + // Determine the outcome of your charge. + if (leveltime > starttime && player->kartstuff[k_boostcharge]) + { + // Not even trying? + if (player->kartstuff[k_boostcharge] < 35) + { + if (player->kartstuff[k_boostcharge] > 17) + S_StartSound(player->mo, sfx_cdfm00); // chosen instead of a conventional skid because it's more engine-like + } + // Get an instant boost! + else if (player->kartstuff[k_boostcharge] <= 50) + { + player->kartstuff[k_startboost] = (50-player->kartstuff[k_boostcharge])+20; + + if (player->kartstuff[k_boostcharge] <= 36) + { + player->kartstuff[k_startboost] = 0; + K_DoSneaker(player, 0); + player->kartstuff[k_sneakertimer] = 70; // PERFECT BOOST!! + + if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) // Let everyone hear this one + S_StartSound(player->mo, sfx_s25f); + } + else + { + K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker + if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsDisplayPlayer(player)) + { + if (player->kartstuff[k_boostcharge] <= 40) + S_StartSound(player->mo, sfx_cdfm01); // You were almost there! + else + S_StartSound(player->mo, sfx_s23c); // Nope, better luck next time. + } + } + } + // You overcharged your engine? Those things are expensive!!! + else if (player->kartstuff[k_boostcharge] > 50) + { + player->powers[pw_nocontrol] = 40; + //S_StartSound(player->mo, sfx_kc34); + S_StartSound(player->mo, sfx_s3k83); + player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse + } + + player->kartstuff[k_boostcharge] = 0; + } +} + +void K_CheckSpectateStatus(void) +{ + UINT8 respawnlist[MAXPLAYERS]; + UINT8 i, j, numingame = 0, numjoiners = 0; + + // Maintain spectate wait timer + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN)) + players[i].kartstuff[k_spectatewait]++; + else + players[i].kartstuff[k_spectatewait] = 0; + } + + // No one's allowed to join + if (!cv_allowteamchange.value) + return; + + // Get the number of players in game, and the players to be de-spectated. + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + if (!players[i].spectator) + { + numingame++; + if (cv_ingamecap.value && numingame >= cv_ingamecap.value) // DON'T allow if you've hit the in-game player cap + return; + if (gamestate != GS_LEVEL) // Allow if you're not in a level + continue; + if (players[i].exiting) // DON'T allow if anyone's exiting + return; + if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet + continue; + if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in + return; + if (G_RaceGametype() && players[i].laps >= 2) // DON'T allow if the race is at 2 laps + return; + continue; + } + else if (!(players[i].pflags & PF_WANTSTOJOIN)) + continue; + + respawnlist[numjoiners++] = i; + } + + // literally zero point in going any further if nobody is joining + if (!numjoiners) + return; + + // Organize by spectate wait timer + if (cv_ingamecap.value) + { + UINT8 oldrespawnlist[MAXPLAYERS]; + memcpy(oldrespawnlist, respawnlist, numjoiners); + for (i = 0; i < numjoiners; i++) + { + UINT8 pos = 0; + INT32 ispecwait = players[oldrespawnlist[i]].kartstuff[k_spectatewait]; + + for (j = 0; j < numjoiners; j++) + { + INT32 jspecwait = players[oldrespawnlist[j]].kartstuff[k_spectatewait]; + if (j == i) + continue; + if (jspecwait > ispecwait) + pos++; + else if (jspecwait == ispecwait && j < i) + pos++; + } + + respawnlist[pos] = oldrespawnlist[i]; + } + } + + // Finally, we can de-spectate everyone! + for (i = 0; i < numjoiners; i++) + { + if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people? + break; + P_SpectatorJoinGame(&players[respawnlist[i]]); + } + + // Reset the match if you're in an empty server + if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+i >= 2)) // use previous i value + { + S_ChangeMusicInternal("chalng", false); // COME ON + mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD + } +} + +//} + +//{ SRB2kart HUD Code + +#define NUMPOSNUMS 10 +#define NUMPOSFRAMES 7 // White, three blues, three reds +#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple + +//{ Patch Definitions +static patch_t *kp_nodraw; + +static patch_t *kp_timesticker; +static patch_t *kp_timestickerwide; +static patch_t *kp_lapsticker; +static patch_t *kp_lapstickerwide; +static patch_t *kp_lapstickernarrow; +static patch_t *kp_splitlapflag; +static patch_t *kp_bumpersticker; +static patch_t *kp_bumperstickerwide; +static patch_t *kp_capsulesticker; +static patch_t *kp_capsulestickerwide; +static patch_t *kp_karmasticker; +static patch_t *kp_splitkarmabomb; +static patch_t *kp_timeoutsticker; + +static patch_t *kp_startcountdown[16]; +static patch_t *kp_racefinish[6]; + +static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; +static patch_t *kp_winnernum[NUMPOSFRAMES]; + +static patch_t *kp_facenum[MAXPLAYERS+1]; +static patch_t *kp_facehighlight[8]; + +static patch_t *kp_spbminimap; + +static patch_t *kp_ringsticker[2]; +static patch_t *kp_ringstickersplit[4]; +static patch_t *kp_ring[6]; +static patch_t *kp_smallring[6]; +static patch_t *kp_ringdebtminus; +static patch_t *kp_ringdebtminussmall; +static patch_t *kp_ringspblock[16]; +static patch_t *kp_ringspblocksmall[16]; + +static patch_t *kp_speedometersticker; +static patch_t *kp_speedometerlabel[4]; + +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_battlewin; +static patch_t *kp_battlecool; +static patch_t *kp_battlelose; +static patch_t *kp_battlewait; +static patch_t *kp_battleinfo; +static patch_t *kp_wanted; +static patch_t *kp_wantedsplit; +static patch_t *kp_wantedreticle; + +static patch_t *kp_itembg[4]; +static patch_t *kp_itemtimer[2]; +static patch_t *kp_itemmulsticker[2]; +static patch_t *kp_itemx; + +static patch_t *kp_superring[2]; +static patch_t *kp_sneaker[2]; +static patch_t *kp_rocketsneaker[2]; +static patch_t *kp_invincibility[13]; +static patch_t *kp_banana[2]; +static patch_t *kp_eggman[2]; +static patch_t *kp_orbinaut[5]; +static patch_t *kp_jawz[2]; +static patch_t *kp_mine[2]; +static patch_t *kp_ballhog[2]; +static patch_t *kp_selfpropelledbomb[2]; +static patch_t *kp_grow[2]; +static patch_t *kp_shrink[2]; +static patch_t *kp_thundershield[2]; +static patch_t *kp_bubbleshield[2]; +static patch_t *kp_flameshield[2]; +static patch_t *kp_hyudoro[2]; +static patch_t *kp_pogospring[2]; +static patch_t *kp_kitchensink[2]; +static patch_t *kp_sadface[2]; + +static patch_t *kp_check[6]; + +static patch_t *kp_rival[2]; + +static patch_t *kp_eggnum[4]; + +static patch_t *kp_flameshieldmeter[104][2]; +static patch_t *kp_flameshieldmeter_bg[16][2]; + +static patch_t *kp_fpview[3]; +static patch_t *kp_inputwheel[5]; + +static patch_t *kp_challenger[25]; + +static patch_t *kp_lapanim_lap[7]; +static patch_t *kp_lapanim_final[11]; +static patch_t *kp_lapanim_number[10][3]; +static patch_t *kp_lapanim_emblem[2]; +static patch_t *kp_lapanim_hand[3]; + +static patch_t *kp_yougotem; +static patch_t *kp_itemminimap; + +static patch_t *kp_alagles[10]; +static patch_t *kp_blagles[6]; + +static patch_t *kp_cpu; + +static patch_t *kp_nametagstem; + +void K_LoadKartHUDGraphics(void) +{ + INT32 i, j; + char buffer[9]; + + // Null Stuff + kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX); + + // Stickers + kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX); + kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX); + kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX); + kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX); + kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX); + kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX); + kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX); + kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX); + 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_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); + kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); + + // Starting countdown + kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); + kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); + kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX); + kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX); + kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX); + kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX); + kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX); + kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); + // Splitscreen + kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX); + kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX); + kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX); + kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); + kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX); + kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX); + kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); + kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); + + // Finish + kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX); + kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX); + // Splitscreen + kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX); + kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX); + // 2P splitscreen + kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX); + kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX); + + // Position numbers + sprintf(buffer, "K_POSNxx"); + for (i = 0; i < NUMPOSNUMS; i++) + { + buffer[6] = '0'+i; + for (j = 0; j < NUMPOSFRAMES; j++) + { + //sprintf(buffer, "K_POSN%d%d", i, j); + buffer[7] = '0'+j; + kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + } + + sprintf(buffer, "K_POSNWx"); + for (i = 0; i < NUMWINFRAMES; i++) + { + buffer[7] = '0'+i; + kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "OPPRNKxx"); + for (i = 0; i <= MAXPLAYERS; i++) + { + buffer[6] = '0'+(i/10); + buffer[7] = '0'+(i%10); + kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_CHILIx"); + for (i = 0; i < 8; i++) + { + buffer[7] = '0'+(i+1); + kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_spbminimap = W_CachePatchName("SPBMMAP", PU_HUDGFX); + + // Rings & Lives + kp_ringsticker[0] = W_CachePatchName("RNGBACKA", PU_HUDGFX); + kp_ringsticker[1] = W_CachePatchName("RNGBACKB", PU_HUDGFX); + + sprintf(buffer, "K_RINGx"); + for (i = 0; i < 6; i++) + { + buffer[6] = '0'+(i+1); + kp_ring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringdebtminus = W_CachePatchName("RDEBTMIN", PU_HUDGFX); + + sprintf(buffer, "SPBRNGxx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1) / 10); + buffer[7] = '0'+((i+1) % 10); + kp_ringspblock[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringstickersplit[0] = W_CachePatchName("SMRNGBGA", PU_HUDGFX); + kp_ringstickersplit[1] = W_CachePatchName("SMRNGBGB", PU_HUDGFX); + + sprintf(buffer, "K_SRINGx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '0'+(i+1); + kp_smallring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringdebtminussmall = W_CachePatchName("SRDEBTMN", PU_HUDGFX); + + sprintf(buffer, "SPBRGSxx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1) / 10); + buffer[7] = '0'+((i+1) % 10); + kp_ringspblocksmall[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Speedometer + kp_speedometersticker = W_CachePatchName("K_SPDMBG", PU_HUDGFX); + + sprintf(buffer, "K_SPDMLx"); + for (i = 0; i < 4; i++) + { + buffer[7] = '0'+(i+1); + kp_speedometerlabel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Extra ranking icons + kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX); + kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX); + 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); + + // Battle graphics + kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); + kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX); + kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX); + kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX); + kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX); + kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX); + kp_wantedsplit = W_CachePatchName("4PWANTED", PU_HUDGFX); + kp_wantedreticle = W_CachePatchName("MMAPWANT", PU_HUDGFX); + + // Kart Item Windows + kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX); + kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX); + kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX); + kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX); + kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX); + + kp_superring[0] = W_CachePatchName("K_ITRING", PU_HUDGFX); + kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX); + kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX); + + sprintf(buffer, "K_ITINVx"); + for (i = 0; i < 7; i++) + { + buffer[7] = '1'+i; + kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX); + kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX); + sprintf(buffer, "K_ITORBx"); + for (i = 0; i < 4; i++) + { + buffer[7] = '1'+i; + kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX); + kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX); + kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX); + kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX); + kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX); + kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX); + kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX); + kp_bubbleshield[0] = W_CachePatchName("K_ITBUBS", PU_HUDGFX); + kp_flameshield[0] = W_CachePatchName("K_ITFLMS", PU_HUDGFX); + kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX); + kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX); + kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX); + kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX); + + sprintf(buffer, "FSMFGxxx"); + for (i = 0; i < 104; i++) + { + buffer[5] = '0'+((i+1)/100); + buffer[6] = '0'+(((i+1)/10)%10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "FSMBG0xx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter_bg[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Splitscreen + kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX); + kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX); + kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX); + kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX); + + kp_superring[1] = W_CachePatchName("K_ISRING", PU_HUDGFX); + kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX); + kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX); + sprintf(buffer, "K_ISINVx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '1'+i; + kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX); + kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX); + kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX); + kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX); + kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX); + kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX); + kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX); + kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX); + kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX); + kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX); + kp_bubbleshield[1] = W_CachePatchName("K_ISBUBS", PU_HUDGFX); + kp_flameshield[1] = W_CachePatchName("K_ISFLMS", PU_HUDGFX); + kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX); + kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX); + kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX); + kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX); + + sprintf(buffer, "FSMFSxxx"); + for (i = 0; i < 104; i++) + { + buffer[5] = '0'+((i+1)/100); + buffer[6] = '0'+(((i+1)/10)%10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "FSMBS0xx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter_bg[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // CHECK indicators + sprintf(buffer, "K_CHECKx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '1'+i; + kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Rival indicators + sprintf(buffer, "K_RIVALx"); + for (i = 0; i < 2; i++) + { + buffer[7] = '1'+i; + kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Eggman warning numbers + sprintf(buffer, "K_EGGNx"); + for (i = 0; i < 4; i++) + { + buffer[6] = '0'+i; + kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // First person mode + kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX); + kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX); + kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX); + + // Input UI Wheel + sprintf(buffer, "K_WHEELx"); + for (i = 0; i < 5; i++) + { + buffer[7] = '0'+i; + kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // HERE COMES A NEW CHALLENGER + sprintf(buffer, "K_CHALxx"); + for (i = 0; i < 25; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Lap start animation + sprintf(buffer, "K_LAP0x"); + for (i = 0; i < 7; i++) + { + buffer[6] = '0'+(i+1); + kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPFxx"); + for (i = 0; i < 11; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPNxx"); + for (i = 0; i < 10; i++) + { + buffer[6] = '0'+i; + for (j = 0; j < 3; j++) + { + buffer[7] = '0'+(j+1); + kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + } + + sprintf(buffer, "K_LAPE0x"); + for (i = 0; i < 2; i++) + { + buffer[7] = '0'+(i+1); + kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPH0x"); + for (i = 0; i < 3; i++) + { + buffer[7] = '0'+(i+1); + kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); + kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); + + sprintf(buffer, "ALAGLESx"); + for (i = 0; i < 10; ++i) + { + buffer[7] = '0'+i; + kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "BLAGLESx"); + for (i = 0; i < 6; ++i) + { + buffer[7] = '0'+i; + kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); + + kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX); +} + +// For the item toggle menu +const char *K_GetItemPatch(UINT8 item, boolean tiny) +{ + switch (item) + { + case KITEM_SNEAKER: + case KRITEM_TRIPLESNEAKER: + return (tiny ? "K_ISSHOE" : "K_ITSHOE"); + case KITEM_ROCKETSNEAKER: + return (tiny ? "K_ISRSHE" : "K_ITRSHE"); + case KITEM_INVINCIBILITY: + return (tiny ? "K_ISINV1" : "K_ITINV1"); + case KITEM_BANANA: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + return (tiny ? "K_ISBANA" : "K_ITBANA"); + case KITEM_EGGMAN: + return (tiny ? "K_ISEGGM" : "K_ITEGGM"); + case KITEM_ORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB1"); + case KITEM_JAWZ: + case KRITEM_DUALJAWZ: + return (tiny ? "K_ISJAWZ" : "K_ITJAWZ"); + case KITEM_MINE: + return (tiny ? "K_ISMINE" : "K_ITMINE"); + case KITEM_BALLHOG: + return (tiny ? "K_ISBHOG" : "K_ITBHOG"); + case KITEM_SPB: + return (tiny ? "K_ISSPB" : "K_ITSPB"); + case KITEM_GROW: + return (tiny ? "K_ISGROW" : "K_ITGROW"); + case KITEM_SHRINK: + return (tiny ? "K_ISSHRK" : "K_ITSHRK"); + case KITEM_THUNDERSHIELD: + return (tiny ? "K_ISTHNS" : "K_ITTHNS"); + case KITEM_BUBBLESHIELD: + return (tiny ? "K_ISBUBS" : "K_ITBUBS"); + case KITEM_FLAMESHIELD: + return (tiny ? "K_ISFLMS" : "K_ITFLMS"); + case KITEM_HYUDORO: + return (tiny ? "K_ISHYUD" : "K_ITHYUD"); + case KITEM_POGOSPRING: + return (tiny ? "K_ISPOGO" : "K_ITPOGO"); + case KITEM_SUPERRING: + return (tiny ? "K_ISRING" : "K_ITRING"); + case KITEM_KITCHENSINK: + return (tiny ? "K_ISSINK" : "K_ITSINK"); + case KRITEM_TRIPLEORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB3"); + case KRITEM_QUADORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB4"); + default: + return (tiny ? "K_ISSAD" : "K_ITSAD"); + } +} + +//} + +INT32 ITEM_X, ITEM_Y; // Item Window +INT32 TIME_X, TIME_Y; // Time Sticker +INT32 LAPS_X, LAPS_Y; // Lap Sticker +INT32 POSI_X, POSI_Y; // Position Number +INT32 FACE_X, FACE_Y; // Top-four Faces +INT32 STCD_X, STCD_Y; // Starting countdown +INT32 CHEK_Y; // CHECK graphic +INT32 MINI_X, MINI_Y; // Minimap +INT32 WANT_X, WANT_Y; // Battle WANTED poster + +// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN. +INT32 ITEM2_X, ITEM2_Y; +INT32 LAPS2_X, LAPS2_Y; +INT32 POSI2_X, POSI2_Y; + + +static void K_initKartHUD(void) +{ + /* + BASEVIDWIDTH = 320 + BASEVIDHEIGHT = 200 + + Item window graphic is 41 x 33 + + Time Sticker graphic is 116 x 11 + Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14 + Therefore, timestamp is 116 x 14 altogether + + Lap Sticker is 80 x 11 + Lap flag is 22 x 20 + Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14 + Therefore, lapstamp is 80 x 20 altogether + + Position numbers are 43 x 53 + + Faces are 32 x 32 + Faces draw downscaled at 16 x 16 + Therefore, the allocated space for them is 16 x 67 altogether + + ---- + + ORIGINAL CZ64 SPLITSCREEN: + + Item window: + if (!splitscreen) { ICONX = 139; ICONY = 20; } + else { ICONX = BASEVIDWIDTH-315; ICONY = 60; } + + Time: 236, STRINGY( 12) + Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189) + + */ + + // Single Screen (defaults) + // Item Window + ITEM_X = 5; // 5 + ITEM_Y = 5; // 5 + // Level Timer + TIME_X = BASEVIDWIDTH - 148; // 172 + TIME_Y = 9; // 9 + // Level Laps + LAPS_X = 9; // 9 + LAPS_Y = BASEVIDHEIGHT - 29; // 171 + // Position Number + POSI_X = BASEVIDWIDTH - 9; // 268 + POSI_Y = BASEVIDHEIGHT - 9; // 138 + // Top-Four Faces + FACE_X = 9; // 9 + FACE_Y = 92; // 92 + // Starting countdown + STCD_X = BASEVIDWIDTH/2; // 9 + STCD_Y = BASEVIDHEIGHT/2; // 92 + // CHECK graphic + CHEK_Y = BASEVIDHEIGHT; // 200 + // Minimap + MINI_X = BASEVIDWIDTH - 50; // 270 + MINI_Y = (BASEVIDHEIGHT/2)-16; // 84 + // Battle WANTED poster + WANT_X = BASEVIDWIDTH - 55; // 270 + WANT_Y = BASEVIDHEIGHT- 71; // 176 + + if (r_splitscreen) // Splitscreen + { + ITEM_X = 5; + ITEM_Y = 3; + + LAPS_Y = (BASEVIDHEIGHT/2)-24; + + POSI_Y = (BASEVIDHEIGHT/2)- 2; + + STCD_Y = BASEVIDHEIGHT/4; + + MINI_Y = (BASEVIDHEIGHT/2); + + if (r_splitscreen > 1) // 3P/4P Small Splitscreen + { + // 1P (top left) + ITEM_X = -9; + ITEM_Y = -8; + + LAPS_X = 3; + LAPS_Y = (BASEVIDHEIGHT/2)-12; + + POSI_X = 24; + POSI_Y = (BASEVIDHEIGHT/2)-26; + + // 2P (top right) + ITEM2_X = BASEVIDWIDTH-39; + ITEM2_Y = -8; + + LAPS2_X = BASEVIDWIDTH-43; + LAPS2_Y = (BASEVIDHEIGHT/2)-12; + + POSI2_X = BASEVIDWIDTH -4; + POSI2_Y = (BASEVIDHEIGHT/2)-26; + + // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom. + + STCD_X = BASEVIDWIDTH/4; + + MINI_X = (3*BASEVIDWIDTH/4); + MINI_Y = (3*BASEVIDHEIGHT/4); + + if (r_splitscreen > 2) // 4P-only + { + MINI_X = (BASEVIDWIDTH/2); + MINI_Y = (BASEVIDHEIGHT/2); + } + } + } + + if (timeinmap > 113) + hudtrans = cv_translucenthud.value; + else if (timeinmap > 105) + hudtrans = ((((INT32)timeinmap) - 105)*cv_translucenthud.value)/(113-105); + else + hudtrans = 0; +} + +INT32 K_calcSplitFlags(INT32 snapflags) +{ + INT32 splitflags = 0; + + if (r_splitscreen == 0) + return snapflags; + + if (stplyr != &players[displayplayers[0]]) + { + if (r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + { + splitflags |= V_SPLITSCREEN; + } + else if (r_splitscreen > 1) + { + if (stplyr == &players[displayplayers[2]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) + splitflags |= V_SPLITSCREEN; + if (stplyr == &players[displayplayers[1]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) + splitflags |= V_HORZSCREEN; + } + } + + if (splitflags & V_SPLITSCREEN) + snapflags &= ~V_SNAPTOTOP; + else + snapflags &= ~V_SNAPTOBOTTOM; + + if (r_splitscreen > 1) + { + if (splitflags & V_HORZSCREEN) + snapflags &= ~V_SNAPTOLEFT; + else + snapflags &= ~V_SNAPTORIGHT; + } + + return (splitflags|snapflags); +} + +static void K_drawKartItem(void) +{ + // ITEM_X = BASEVIDWIDTH-50; // 270 + // ITEM_Y = 24; // 24 + + // Why write V_DrawScaledPatch calls over and over when they're all the same? + // Set to 'no item' just in case. + const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); + patch_t *localpatch = kp_nodraw; + patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); + patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); + INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... + //INT32 splitflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); + const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2); + INT32 itembar = 0; + INT32 maxl = 0; // itembar's normal highest value + const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); + UINT8 localcolor = SKINCOLOR_NONE; + SINT8 colormode = TC_RAINBOW; + UINT8 *colmap = NULL; + boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff + + if (stplyr->kartstuff[k_itemroulette]) + { + if (stplyr->skincolor) + localcolor = stplyr->skincolor; + + switch((stplyr->kartstuff[k_itemroulette] % (15*3)) / 3) + { + // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette + case 0: // Sneaker + localpatch = kp_sneaker[offset]; + //localcolor = SKINCOLOR_RASPBERRY; + break; + case 1: // Banana + localpatch = kp_banana[offset]; + //localcolor = SKINCOLOR_YELLOW; + break; + case 2: // Orbinaut + localpatch = kp_orbinaut[3+offset]; + //localcolor = SKINCOLOR_STEEL; + break; + case 3: // Mine + localpatch = kp_mine[offset]; + //localcolor = SKINCOLOR_JET; + break; + case 4: // Grow + localpatch = kp_grow[offset]; + //localcolor = SKINCOLOR_TEAL; + break; + case 5: // Hyudoro + localpatch = kp_hyudoro[offset]; + //localcolor = SKINCOLOR_STEEL; + break; + case 6: // Rocket Sneaker + localpatch = kp_rocketsneaker[offset]; + //localcolor = SKINCOLOR_TANGERINE; + break; + case 7: // Jawz + localpatch = kp_jawz[offset]; + //localcolor = SKINCOLOR_JAWZ; + break; + case 8: // Self-Propelled Bomb + localpatch = kp_selfpropelledbomb[offset]; + //localcolor = SKINCOLOR_JET; + break; + case 9: // Shrink + localpatch = kp_shrink[offset]; + //localcolor = SKINCOLOR_ORANGE; + break; + case 10: // Invincibility + localpatch = localinv; + //localcolor = SKINCOLOR_GREY; + break; + case 11: // Eggman Monitor + localpatch = kp_eggman[offset]; + //localcolor = SKINCOLOR_ROSE; + break; + case 12: // Ballhog + localpatch = kp_ballhog[offset]; + //localcolor = SKINCOLOR_LILAC; + break; + case 13: // Thunder Shield + localpatch = kp_thundershield[offset]; + //localcolor = SKINCOLOR_CYAN; + break; + case 14: // Super Ring + localpatch = kp_superring[offset]; + //localcolor = SKINCOLOR_GOLD; + break; + /*case 15: // Pogo Spring + localpatch = kp_pogospring[offset]; + localcolor = SKINCOLOR_TANGERINE; + break; + case 16: // Kitchen Sink + localpatch = kp_kitchensink[offset]; + localcolor = SKINCOLOR_STEEL; + break;*/ + default: + break; + } + } + else + { + // I'm doing this a little weird and drawing mostly in reverse order + // The only actual reason is to make sneakers line up this way in the code below + // This shouldn't have any actual baring over how it functions + // Hyudoro is first, because we're drawing it on top of the player's current item + if (stplyr->kartstuff[k_stolentimer] > 0) + { + if (leveltime & 2) + localpatch = kp_hyudoro[offset]; + else + localpatch = kp_nodraw; + } + else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2)) + { + localpatch = kp_hyudoro[offset]; + } + else if (stplyr->kartstuff[k_eggmanexplode] > 1) + { + if (leveltime & 1) + localpatch = kp_eggman[offset]; + else + localpatch = kp_nodraw; + } + else if (stplyr->kartstuff[k_rocketsneakertimer] > 1) + { + itembar = stplyr->kartstuff[k_rocketsneakertimer]; + maxl = (itemtime*3) - barlength; + + if (leveltime & 1) + localpatch = kp_rocketsneaker[offset]; + else + localpatch = kp_nodraw; + } + else if (stplyr->kartstuff[k_sadtimer] > 0) + { + if (leveltime & 2) + localpatch = kp_sadface[offset]; + else + localpatch = kp_nodraw; + } + else + { + if (stplyr->kartstuff[k_itemamount] <= 0) + return; + + switch(stplyr->kartstuff[k_itemtype]) + { + case KITEM_SNEAKER: + localpatch = kp_sneaker[offset]; + break; + case KITEM_ROCKETSNEAKER: + localpatch = kp_rocketsneaker[offset]; + break; + case KITEM_INVINCIBILITY: + localpatch = localinv; + localbg = kp_itembg[offset+1]; + break; + case KITEM_BANANA: + localpatch = kp_banana[offset]; + break; + case KITEM_EGGMAN: + localpatch = kp_eggman[offset]; + break; + case KITEM_ORBINAUT: + localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))]; + break; + case KITEM_JAWZ: + localpatch = kp_jawz[offset]; + break; + case KITEM_MINE: + localpatch = kp_mine[offset]; + break; + case KITEM_BALLHOG: + localpatch = kp_ballhog[offset]; + break; + case KITEM_SPB: + localpatch = kp_selfpropelledbomb[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_GROW: + localpatch = kp_grow[offset]; + break; + case KITEM_SHRINK: + localpatch = kp_shrink[offset]; + break; + case KITEM_THUNDERSHIELD: + localpatch = kp_thundershield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_BUBBLESHIELD: + localpatch = kp_bubbleshield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_FLAMESHIELD: + localpatch = kp_flameshield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_HYUDORO: + localpatch = kp_hyudoro[offset]; + break; + case KITEM_POGOSPRING: + localpatch = kp_pogospring[offset]; + break; + case KITEM_SUPERRING: + localpatch = kp_superring[offset]; + break; + case KITEM_KITCHENSINK: + localpatch = kp_kitchensink[offset]; + break; + case KITEM_SAD: + localpatch = kp_sadface[offset]; + break; + default: + return; + } + + if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1)) + localpatch = kp_nodraw; + } + + if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) + { + colormode = TC_BLINK; + + switch (stplyr->karthud[khud_itemblinkmode]) + { + case 2: + localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + break; + case 1: + localcolor = SKINCOLOR_RED; + break; + default: + localcolor = SKINCOLOR_WHITE; + break; + } + } + } + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); + } + else // now we're having a fun game. + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P3 to the bottom. + } + else // else, that means we're P2 or P4. + { + fx = ITEM2_X; + fy = ITEM2_Y; + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P4 to the bottom + flipamount = true; + } + } + + if (localcolor != SKINCOLOR_NONE) + colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE); + + V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, localbg); + + // Then, the numbers: + if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette]) + { + V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. + V_DrawFixedPatch(fx<kartstuff[k_itemamount])); + else + V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|fflags, va("x%d", stplyr->kartstuff[k_itemamount])); + else + { + V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|fflags, kp_itemx); + V_DrawKartString(fx+38, fy+36, V_HUDTRANS|fflags, va("%d", stplyr->kartstuff[k_itemamount])); + } + } + else + V_DrawFixedPatch(fx< 2) + { + V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one + if (height == 2) + V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside + V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine + } + } + + // Quick Eggman numbers + if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/) + V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|fflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]); + + if (stplyr->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && stplyr->kartstuff[k_flamelength] > 0) + { + INT32 numframes = 104; + INT32 absolutemax = 16 * flameseg; + INT32 flamemax = stplyr->kartstuff[k_flamelength] * flameseg; + INT32 flamemeter = min(stplyr->kartstuff[k_flamemeter], flamemax); + + INT32 bf = 16 - stplyr->kartstuff[k_flamelength]; + INT32 ff = numframes - ((flamemeter * numframes) / absolutemax); + INT32 fmin = (8 * (bf-1)); + + INT32 xo = 6, yo = 4; + INT32 flip = 0; + + if (offset) + { + xo++; + + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // Flip for P1 and P3 (yes, that's correct) + { + xo -= 62; + flip = V_FLIP; + } + } + + if (ff < fmin) + ff = fmin; + + if (bf >= 0 && bf < 16) + V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter_bg[bf][offset]); + + if (ff >= 0 && ff < numframes && stplyr->kartstuff[k_flamemeter] > 0) + { + if ((stplyr->kartstuff[k_flamemeter] > flamemax) && (leveltime & 1)) + { + UINT8 *fsflash = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); + V_DrawMappedPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset], fsflash); + } + else + { + V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset]); + } + } + } +} + +void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode) +{ + // TIME_X = BASEVIDWIDTH-124; // 196 + // TIME_Y = 6; // 6 + + tic_t worktime; + + INT32 splitflags = 0; + if (!mode) + { + splitflags = V_HUDTRANS|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTORIGHT); + if (cv_timelimit.value && timelimitintics > 0) + { + if (drawtime >= timelimitintics) + drawtime = 0; + else + drawtime = timelimitintics - drawtime; + } + } + + V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide)); + + TX += 33; + + worktime = drawtime/(60*TICRATE); + + if (mode && !drawtime) + V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--")); + else if (worktime < 100) // 99:99:99 only + { + // zero minute + if (worktime < 10) + { + V_DrawKartString(TX, TY+3, splitflags, va("0")); + // minutes time 0 __ __ + V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime)); + } + // minutes time 0 __ __ + else + V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime)); + + // apostrophe location _'__ __ + V_DrawKartString(TX+24, TY+3, splitflags, va("'")); + + worktime = (drawtime/TICRATE % 60); + + // zero second _ 0_ __ + if (worktime < 10) + { + V_DrawKartString(TX+36, TY+3, splitflags, va("0")); + // seconds time _ _0 __ + V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime)); + } + // zero second _ 00 __ + else + V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime)); + + // quotation mark location _ __"__ + V_DrawKartString(TX+60, TY+3, splitflags, va("\"")); + + worktime = G_TicsToCentiseconds(drawtime); + + // zero tick _ __ 0_ + if (worktime < 10) + { + V_DrawKartString(TX+72, TY+3, splitflags, va("0")); + // tics _ __ _0 + V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime)); + } + // zero tick _ __ 00 + else + V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime)); + } + else if ((drawtime/TICRATE) & 1) + V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); + + if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! + { + INT32 workx = TX + 96, worky = TY+18; + SINT8 curemb = 0; + patch_t *emblempic[3] = {NULL, NULL, NULL}; + UINT8 *emblemcol[3] = {NULL, NULL, NULL}; + + emblem_t *emblem = M_GetLevelEmblems(emblemmap); + while (emblem) + { + char targettext[9]; + + switch (emblem->type) + { + case ET_TIME: + { + static boolean canplaysound = true; + tic_t timetoreach = emblem->var; + + if (emblem->collected) + { + emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE); + emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE); + if (++curemb == 3) + break; + goto bademblem; + } + + snprintf(targettext, 9, "%i'%02i\"%02i", + G_TicsToMinutes(timetoreach, false), + G_TicsToSeconds(timetoreach), + G_TicsToCentiseconds(timetoreach)); + + if (!mode) + { + if (stplyr->realtime > timetoreach) + { + splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF; + if (canplaysound) + { + S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks + canplaysound = false; + } + } + else if (!canplaysound) + canplaysound = true; + } + + targettext[8] = 0; + } + break; + default: + goto bademblem; + } + + V_DrawRightAlignedString(workx, worky, splitflags, targettext); + workx -= 67; + V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE)); + + break; + + bademblem: + emblem = M_GetLevelEmblems(-1); + } + + if (!mode) + splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS; + while (curemb--) + { + workx -= 12; + V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]); + } + } +} + +static void K_DrawKartPositionNum(INT32 num) +{ + // POSI_X = BASEVIDWIDTH - 51; // 269 + // POSI_Y = BASEVIDHEIGHT- 64; // 136 + + boolean win = (stplyr->exiting && num == 1); + //INT32 X = POSI_X; + INT32 W = SHORT(kp_positionnum[0][0]->width); + fixed_t scale = FRACUNIT; + patch_t *localpatch = kp_positionnum[0][0]; + //INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT); + INT32 fx = 0, fy = 0, fflags = 0; + boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun. + boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen. + boolean overtake = false; + + if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting) + { + scale *= 2; + overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw. + } + if (r_splitscreen) + scale /= 2; + + W = FixedMul(W<>FRACBITS; + + // pain and suffering defined below + if (!r_splitscreen) + { + fx = POSI_X; + fy = BASEVIDHEIGHT - 8; + fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different. + { + fx = POSI_X; + if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap. + { + fy = 30; + fflags = V_SNAPTOTOP|V_SNAPTORIGHT; + if (overtake) + flipvdraw = true; // make sure overtaking doesn't explode us + } + else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap. + { + fy = BASEVIDHEIGHT - 8; + fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = POSI_X; + fy = POSI_Y; + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + flipdraw = true; + if (num && num >= 10) + fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen. + } + else // else, that means we're P2 or P4. + { + fx = POSI2_X; + fy = POSI2_Y; + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + } + } + + // Special case for 0 + if (!num) + { + V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers + + // Draw the number + while (num) + { + if (win) // 1st place winner? You get rainbows!! + localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; + else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won + { + // Alternate frame every three frames + switch (leveltime % 9) + { + case 1: case 2: case 3: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][4]; + else + localpatch = kp_positionnum[num % 10][1]; + break; + case 4: case 5: case 6: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][5]; + else + localpatch = kp_positionnum[num % 10][2]; + break; + case 7: case 8: case 9: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][6]; + else + localpatch = kp_positionnum[num % 10][3]; + break; + default: + localpatch = kp_positionnum[num % 10][0]; + break; + } + } + else + localpatch = kp_positionnum[num % 10][0]; + + V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|fflags, localpatch, NULL); + // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen. + // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either. + + fx -= W; + num /= 10; + } +} + +static boolean K_drawKartPositionFaces(void) +{ + // FACE_X = 15; // 15 + // FACE_Y = 72; // 72 + + INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one + INT32 i, j, ranklines, strank = -1; + boolean completed[MAXPLAYERS]; + INT32 rankplayer[MAXPLAYERS]; + INT32 bumperx, numplayersingame = 0; + UINT8 *colormap; + + ranklines = 0; + memset(completed, 0, sizeof (completed)); + memset(rankplayer, 0, sizeof (rankplayer)); + + for (i = 0; i < MAXPLAYERS; i++) + { + rankplayer[i] = -1; + + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + numplayersingame++; + } + + if (numplayersingame <= 1) + return true; + +#ifdef HAVE_BLUA + if (!LUA_HudEnabled(hud_minirankings)) + return false; // Don't proceed but still return true for free play above if HUD is disabled. +#endif + + for (j = 0; j < numplayersingame; j++) + { + UINT8 lowestposition = MAXPLAYERS+1; + for (i = 0; i < MAXPLAYERS; i++) + { + if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + if (players[i].kartstuff[k_position] >= lowestposition) + continue; + + rankplayer[ranklines] = i; + lowestposition = players[i].kartstuff[k_position]; + } + + i = rankplayer[ranklines]; + + completed[i] = true; + + if (players+i == stplyr) + strank = ranklines; + + //if (ranklines == 5) + //break; // Only draw the top 5 players -- we do this a different way now... + + ranklines++; + } + + if (ranklines < 5) + Y -= (9*ranklines); + else + Y -= (9*5); + + if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) + { + i = 0; + if (ranklines > 5) // could be both... + ranklines = 5; + } + else if (strank+3 > ranklines) // too close to the bottom? + { + i = ranklines - 5; + if (i < 0) + i = 0; + } + else + { + i = strank-2; + ranklines = strank+3; + } + + for (; i < ranklines; i++) + { + if (!playeringame[rankplayer[i]]) continue; + if (players[rankplayer[i]].spectator) continue; + if (!players[rankplayer[i]].mo) continue; + + bumperx = FACE_X+19; + + if (players[rankplayer[i]].mo->color) + { + colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); + if (players[rankplayer[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); + + V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap); + +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_battlebumpers)) + { +#endif + if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 0) + { + V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[0], colormap); + for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++) + { + bumperx += 5; + V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[1], colormap); + } + } +#ifdef HAVE_BLUA + } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating: +#endif + } + + if (i == strank) + V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); + + if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0) + V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SNAPTOLEFT, kp_ranknobumpers); + else + { + INT32 pos = players[rankplayer[i]].kartstuff[k_position]; + if (pos < 0 || pos > MAXPLAYERS) + pos = 0; + // Draws the little number over the face + V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SNAPTOLEFT, kp_facenum[pos]); + } + + Y += 18; + } + + return false; +} + +// +// HU_DrawTabRankings -- moved here to take advantage of kart stuff! +// +void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) +{ + static tic_t alagles_timer = 9; + INT32 i, rightoffset = 240; + const UINT8 *colormap; + INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; + int y2; + + //this function is designed for 9 or less score lines only + //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up + + V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice! + if (scorelines > 8) + { + V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides. + V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom. + rightoffset = (BASEVIDWIDTH/2) - 4 - x; + } + + for (i = 0; i < scorelines; i++) + { + char strtime[MAXPLAYERNAME+1]; + + if (players[tab[i].num].spectator || !players[tab[i].num].mo) + continue; //ignore them. + + if (netgame) // don't draw ping offline + { + if (players[tab[i].num].bot) + { + V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu); + } + else if (tab[i].num != serverplayer || !server_lagless) + { + HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); + } + } + + STRBUFCPY(strtime, tab[i].name); + + y2 = y; + + if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) + { + y2 = ( y - 4 ); + + V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); + // every 70 tics + if (( leveltime % 70 ) == 0) + { + alagles_timer = 9; + } + if (alagles_timer > 0) + { + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); + if (( leveltime % 2 ) == 0) + alagles_timer--; + } + else + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); + + y2 += SHORT (kp_alagles[0]->height) + 1; + } + + if (scorelines > 8) + V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + else + V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); + + if (players[tab[i].num].mo->color) + { + colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); + if (players[tab[i].num].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); + + V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap); + /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 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++) + { + bumperx += 5; + V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap); + } + }*/ + } + + if (tab[i].num == whiteplayer) + V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); + + if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0) + V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); + else + { + INT32 pos = players[tab[i].num].kartstuff[k_position]; + if (pos < 0 || pos > MAXPLAYERS) + pos = 0; + // Draws the little number over the face + V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]); + } + + if (G_RaceGametype()) + { +#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) + if (scorelines > 8) + { + if (players[tab[i].num].exiting) + V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); + else if (players[tab[i].num].pflags & PF_TIMEOVER) + V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST."); + else if (circuitmap) + V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count)); + } + else + { + if (players[tab[i].num].exiting) + V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime)); + else if (players[tab[i].num].pflags & PF_TIMEOVER) + V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST."); + else if (circuitmap) + V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count)); + } +#undef timestring + } + else + V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); + + y += 18; + if (i == 7) + { + y = 33; + x = (BASEVIDWIDTH/2) + 4; + } + } +} + +#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) + +static void K_drawKartLapsAndRings(void) +{ + const boolean uselives = G_GametypeUsesLives(); + SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); + UINT8 rn[2]; + INT32 ringflip = 0; + UINT8 *ringmap = NULL; + boolean colorring = false; + INT32 ringx = 0; + + rn[0] = ((abs(stplyr->kartstuff[k_rings]) / 10) % 10); + rn[1] = (abs(stplyr->kartstuff[k_rings]) % 10); + + if (stplyr->kartstuff[k_rings] <= 0 && (leveltime/5 & 1)) // In debt + { + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); + colorring = true; + } + else if (stplyr->kartstuff[k_rings] >= 20) // Maxed out + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); + + if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) + { + ringflip = V_FLIP; + ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; + ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); + } + + if (r_splitscreen > 1) + { + INT32 fx = 0, fy = 0, fr = 0; + INT32 flipflag = 0; + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = LAPS_X; + fy = LAPS_Y; + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = LAPS_X; + fy = LAPS_Y; + splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + } + else // else, that means we're P2 or P4. + { + fx = LAPS2_X; + fy = LAPS2_Y; + splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + flipflag = V_FLIP; // make the string right aligned and other shit + } + } + + fr = fx; + + // Laps + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); + + V_DrawScaledPatch(fx, fy, V_HUDTRANS|splitflags, kp_splitlapflag); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); + + if (cv_numlaps.value >= 10) + { + UINT8 ln[2]; + ln[0] = ((stplyr->laps / 10) % 10); + ln[1] = (stplyr->laps % 10); + + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + + ln[0] = ((abs(cv_numlaps.value) / 10) % 10); + ln[1] = (abs(cv_numlaps.value) % 10); + + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_numlaps.value) % 10]); + } + + // Rings + if (!uselives) + { + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]); + if (flipflag) + fr += 15; + } + else + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[0]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); + + V_DrawMappedPatch(fr+ringx, fy-13, V_HUDTRANS|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL)); + + if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt + V_DrawMappedPatch(fr+7, fy-10, V_HUDTRANS|splitflags, kp_ringdebtminussmall, ringmap); + + V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); + V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); + + // SPB ring lock + if (stplyr->kartstuff[k_ringlock]) + V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); + + // Lives + if (uselives) + { + UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); + V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); + V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow + } + } + else + { + // Laps + V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_lapsticker); + + if (stplyr->exiting) + V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN"); + else + V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); + + // Rings + if (!uselives) + V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]); + else + V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]); + + V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); + + if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt + { + V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringdebtminus, ringmap); + V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); + } + else + { + V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); + } + + // SPB ring lock + if (stplyr->kartstuff[k_ringlock]) + V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); + + // Lives + if (uselives) + { + UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); + V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap); + V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow + } + } +} + +#undef RINGANIM_NUMFRAMES +#undef RINGANIM_FLIPFRAME + +static void K_drawKartSpeedometer(void) +{ + static fixed_t convSpeed; + UINT8 labeln = 0; + UINT8 numbers[3]; + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); + UINT8 battleoffset = 0; + + if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! + { + switch (cv_kartspeedometer.value) + { + case 1: // Sonic Drift 2 style percentage + default: + convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) + labeln = 0; + break; + case 2: // Kilometers + convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058 + labeln = 1; + break; + case 3: // Miles + convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774 + labeln = 2; + break; + case 4: // Fracunits + convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT; // 1.0. duh. + labeln = 3; + break; + } + } + + // Don't overflow + if (convSpeed > 999) + convSpeed = 999; + + numbers[0] = ((convSpeed / 100) % 10); + numbers[1] = ((convSpeed / 10) % 10); + numbers[2] = (convSpeed % 10); + + if (G_BattleGametype()) + battleoffset = 8; + + V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometersticker); + V_DrawScaledPatch(LAPS_X+7, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[0]]); + V_DrawScaledPatch(LAPS_X+13, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[1]]); + V_DrawScaledPatch(LAPS_X+19, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[2]]); + V_DrawScaledPatch(LAPS_X+29, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometerlabel[labeln]); +} + +static void K_drawKartBumpersOrKarma(void) +{ + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); + + if (r_splitscreen > 1) + { + INT32 fx = 0, fy = 0; + INT32 flipflag = 0; + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = LAPS_X; + fy = LAPS_Y; + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = LAPS_X; + fy = LAPS_Y; + splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + } + else // else, that means we're P2 or P4. + { + fx = LAPS2_X; + fy = LAPS2_Y; + splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + flipflag = V_FLIP; // make the string right aligned and other shit + } + } + + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); + + if (battlecapsules) + { + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankcapsule, NULL); + + if (numtargets > 9 || maptargets > 9) + { + UINT8 ln[2]; + ln[0] = ((numtargets / 10) % 10); + ln[1] = (numtargets % 10); + + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + + ln[0] = ((maptargets / 10) % 10); + ln[1] = (maptargets % 10); + + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[numtargets % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[maptargets % 10]); + } + } + else + { + if (stplyr->kartstuff[k_bumper] <= 0) + { + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[2]); + } + else + { + INT32 maxbumper = K_StartingBumperCount(); + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(maxbumper) % 10]); + } + } + } + } + else + { + if (battlecapsules) + { + if (numtargets > 9 && maptargets > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulestickerwide, NULL); + else + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulesticker, NULL); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", numtargets, maptargets)); + } + else + { + if (stplyr->kartstuff[k_bumper] <= 0) + { + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); + } + else + { + INT32 maxbumper = K_StartingBumperCount(); + + if (stplyr->kartstuff[k_bumper] > 9 && maxbumper > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap); + else + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap); + + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], maxbumper)); + } + } + } +} + +static void K_drawKartWanted(void) +{ + UINT8 i, numwanted = 0; + UINT8 *colormap = NULL; + INT32 basex = 0, basey = 0; + + if (stplyr != &players[displayplayers[0]]) + return; + + for (i = 0; i < 4; i++) + { + if (battlewanted[i] == -1) + break; + numwanted++; + } + + if (numwanted <= 0) + return; + + // set X/Y coords depending on splitscreen. + if (r_splitscreen < 3) // 1P and 2P use the same code. + { + basex = WANT_X; + basey = WANT_Y; + if (r_splitscreen == 2) + { + basey += 16; // slight adjust for 3P + basex -= 6; + } + } + else if (r_splitscreen == 3) // 4P splitscreen... + { + basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen + basey = BASEVIDHEIGHT - 55; + //basey2 = 4; + } + + if (battlewanted[0] != -1) + colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE); + V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap); + /*if (basey2) + V_DrawFixedPatch(basex< 1 ? 13 : 8), y = basey+(r_splitscreen > 1 ? 16 : 21); + fixed_t scale = FRACUNIT/2; + player_t *p = &players[battlewanted[i]]; + + if (battlewanted[i] == -1) + break; + + if (numwanted == 1) + scale = FRACUNIT; + else + { + if (i & 1) + x += 16; + if (i > 1) + y += 16; + } + + if (players[battlewanted[i]].skincolor) + { + colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE); + V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap); + /*if (basey2) // again with 4p stuff + V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);*/ + } + } +} + +static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) +{ + const INT32 swhalf = (BASEVIDWIDTH / 2); + const fixed_t swhalffixed = swhalf * FRACUNIT; + + const INT32 shhalf = (BASEVIDHEIGHT / 2); + const fixed_t shhalffixed = shhalf * FRACUNIT; + + INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); + fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); + fixed_t factor = INT32_MAX; + + if (abs(anglediff) > ANGLE_90) + { + if (hud_x != NULL) + { + *hud_x = -BASEVIDWIDTH * FRACUNIT; + } + + if (hud_y != NULL) + { + *hud_y = -BASEVIDWIDTH * FRACUNIT; + } + + //*hud_scale = FRACUNIT; + return; + } + + factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); + +#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) + + if (hud_x != NULL) + { + *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; + + if (encoremode) + { + *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; + } + + if (r_splitscreen >= 2) + { + *hud_x /= 2; + + if (camnum & 1) + { + *hud_x += swhalffixed; + } + } + } + + if (hud_y != NULL) + { + *hud_y = campos->z - point->z; + *hud_y = FixedDiv(*hud_y, FixedMul(factor, distance)); + *hud_y = (*hud_y * swhalf) + shhalffixed; + *hud_y = *hud_y + NEWTAN(camaim) * swhalf; + + if (r_splitscreen >= 1) + { + *hud_y /= 2; + + if (camnum > 1) + { + *hud_y += shhalffixed; + } + } + } + + //*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance)); + +#undef NEWTAN +} + +static void K_drawKartPlayerCheck(void) +{ + const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); + + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { + return; + } + + if (stplyr->spectator || stplyr->awayviewtics) + { + return; + } + + if (stplyr->cmd.buttons & BT_LOOKBACK) + { + return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = stplyr->mo->x; + c.y = stplyr->mo->y; + c.z = stplyr->mo->z; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *checkplayer = &players[i]; + fixed_t distance = maxdistance+1; + UINT8 *colormap = NULL; + UINT8 pnum = 0; + fixed_t x = 0; + vertex_t v; + + if (!playeringame[i] || checkplayer->spectator) + { + // Not in-game + continue; + } + + if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo)) + { + // No object + continue; + } + + if (checkplayer == stplyr) + { + // This is you! + continue; + } + + v.x = checkplayer->mo->x; + v.y = checkplayer->mo->y; + v.z = checkplayer->mo->z; + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) + { + pnum++; // white frames + } + + if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0) + { + pnum += 4; + } + else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer]) + { + pnum += 2; + } + + K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); + + colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); + V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); + } +} + +static void K_drawKartNameTags(void) +{ + const fixed_t maxdistance = 8192*mapobjectscale; + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; + + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { + return; + } + + if (stplyr->awayviewtics) + { + return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = thiscam->x; + c.y = thiscam->y; + c.z = thiscam->z; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *ntplayer = &players[i]; + fixed_t distance = maxdistance+1; + + fixed_t x = -BASEVIDWIDTH * FRACUNIT; + fixed_t y = -BASEVIDWIDTH * FRACUNIT; + + vertex_t v; + UINT8 j; + + if (!playeringame[i] || ntplayer->spectator) + { + // Not in-game + continue; + } + + if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) + { + // No object + continue; + } + + if (!P_CheckSight(stplyr->mo, ntplayer->mo)) + { + // Can't see + continue; + } + + for (j = 0; j <= r_splitscreen; j++) + { + if (ntplayer == &players[displayplayers[j]]) + { + break; + } + } + + if (j <= r_splitscreen) + { + // Is a player that's being shown on this computer + continue; + } + + v.x = ntplayer->mo->x; + v.y = ntplayer->mo->y; + v.z = ntplayer->mo->z; + + if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) + { + v.z += ntplayer->mo->height; + } + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); + + if (x == -BASEVIDWIDTH * FRACUNIT) + { + // Off-screen + continue; + } + + if (ntplayer->bot) + { + if (ntplayer->botvars.rival == true) + { + UINT8 blink = ((leveltime / 7) & 1); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); + } + } + else if (netgame) + { + if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-2) + && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+2)) + { + INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); + INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); + UINT8 *colormap = V_GetStringColormap(clr); + INT32 barx = 0, bary = 0, barw = 0; + + // Since there's no "V_DrawFixedFill", and I don't feel like making it, + // fuck it, we're gonna just V_NOSCALESTART hack it + barw = (namelen * vid.dupx); + + barx = (x * vid.dupx) / FRACUNIT; + bary = (y * vid.dupy) / FRACUNIT; + + barx += (6 * vid.dupx); + bary -= (16 * vid.dupx); + + // Center it if necessary + if (vid.width != BASEVIDWIDTH * vid.dupx) + { + barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; + } + + if (vid.height != BASEVIDHEIGHT * vid.dupy) + { + bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; + } + + // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. + V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); + V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); + // END DRAWFILL DUMBNESS + + // Draw the stem + V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); + + // Draw the name itself + V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); + } + } + } +} + +static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) +{ + // amnum xpos & ypos are the icon's speed around the HUD. + // The number being divided by is for how fast it moves. + // The higher the number, the slower it moves. + + // am xpos & ypos are the icon's starting position. Withouht + // it, they wouldn't 'spawn' on the top-right side of the HUD. + + fixed_t amnumxpos, amnumypos; + INT32 amxpos, amypos; + + node_t *bsp = &nodes[numnodes-1]; + fixed_t maxx, minx, maxy, miny; + + fixed_t mapwidth, mapheight; + fixed_t xoffset, yoffset; + fixed_t xscale, yscale, zoom; + + maxx = maxy = INT32_MAX; + minx = miny = INT32_MIN; + minx = bsp->bbox[0][BOXLEFT]; + maxx = bsp->bbox[0][BOXRIGHT]; + miny = bsp->bbox[0][BOXBOTTOM]; + maxy = bsp->bbox[0][BOXTOP]; + + if (bsp->bbox[1][BOXLEFT] < minx) + minx = bsp->bbox[1][BOXLEFT]; + if (bsp->bbox[1][BOXRIGHT] > maxx) + maxx = bsp->bbox[1][BOXRIGHT]; + if (bsp->bbox[1][BOXBOTTOM] < miny) + miny = bsp->bbox[1][BOXBOTTOM]; + if (bsp->bbox[1][BOXTOP] > maxy) + maxy = bsp->bbox[1][BOXTOP]; + + // You might be wondering why these are being bitshift here + // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... + // map boundaries and sizes will ALWAYS be whole numbers thankfully + // later calculations take into consideration that these are actually not in terms of FRACUNIT though + minx >>= FRACBITS; + maxx >>= FRACBITS; + miny >>= FRACBITS; + maxy >>= FRACBITS; + + mapwidth = maxx - minx; + mapheight = maxy - miny; + + // These should always be small enough to be bitshift back right now + xoffset = (minx + mapwidth/2)<width, mapwidth); + yscale = FixedDiv(AutomapPic->height, mapheight); + zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); + + amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); + amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); + + if (encoremode) + amnumxpos = -amnumxpos; + + amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width/2); + y = MINI_Y - (AutomapPic->height/2); + + if (timeinmap > 105) + { + minimaptrans = cv_kartminimap.value; + if (timeinmap <= 113) + minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); + if (!minimaptrans) + return; + } + else + return; + + minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); + else + V_DrawScaledPatch(x, y, splitflags, AutomapPic); + + if (!(r_splitscreen == 2)) + { + splitflags &= ~minimaptrans; + splitflags |= V_HUDTRANSHALF; + } + + // let offsets transfer to the heads, too! + if (encoremode) + x += SHORT(AutomapPic->leftoffset); + else + x -= SHORT(AutomapPic->leftoffset); + y -= SHORT(AutomapPic->topoffset); + + // Draw the super item in Battle + if (G_BattleGametype() && battleovertime.enabled) + { + if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) + { + const INT32 prevsplitflags = splitflags; + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); + K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); + splitflags = prevsplitflags; + } + } + + // initialize + for (i = 0; i < 4; i++) + localplayers[i] = -1; + + if (G_RaceGametype()) + hyu *= 2; // double in race + + // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) + if (ghosts) + { + demoghost *g = ghosts; + while (g) + { + if (g->mo->skin) + skin = ((skin_t*)g->mo->skin)-skins; + else + skin = 0; + if (g->mo->color) + { + if (g->mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); + } + else + colormap = NULL; + K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + g = g->next; + } + + if (!stplyr->mo || stplyr->spectator || stplyr->exiting) + return; + + localplayers[numlocalplayers] = stplyr-players; + numlocalplayers++; + } + else + { + for (i = MAXPLAYERS-1; i >= 0; i--) + { + if (!playeringame[i]) + continue; + if (!players[i].mo || players[i].spectator || players[i].exiting) + continue; + + if (i != displayplayers[0] || r_splitscreen) + { + if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) + continue; + + if (players[i].kartstuff[k_hyudorotimer] > 0) + { + if (!((players[i].kartstuff[k_hyudorotimer] < TICRATE/2 + || players[i].kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)) + && !(leveltime & 1))) + continue; + } + } + + if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) + { + // Draw display players on top of everything else + localplayers[numlocalplayers] = i; + numlocalplayers++; + continue; + } + + if (players[i].mo->skin) + skin = ((skin_t*)players[i].mo->skin)-skins; + else + skin = 0; + + if (players[i].mo->color) + { + if (players[i].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + // Target reticule + if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } + } + + // draw SPB(s?) + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + if (mobj->type == MT_SPB) + { + colormap = NULL; + + if (mobj->target && !P_MobjWasRemoved(mobj->target)) + { + if (mobj->player && mobj->player->skincolor) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE); + else if (mobj->color) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); + } + + K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); + } + } + + // draw our local players here, opaque. + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + + for (i = 0; i < numlocalplayers; i++) + { + if (i == -1) + continue; // this doesn't interest us + + if (players[localplayers[i]].mo->skin) + skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; + else + skin = 0; + + if (players[localplayers[i]].mo->color) + { + if (players[localplayers[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + + // Target reticule + if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } +} + +static void K_drawKartStartCountdown(void) +{ + INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3 + + if (leveltime >= starttime-(2*TICRATE)) // 2 + pnum++; + if (leveltime >= starttime-TICRATE) // 1 + pnum++; + if (leveltime >= starttime) // GO! + pnum++; + if ((leveltime % (2*5)) / 5) // blink + pnum += 4; + if (r_splitscreen) // splitscreen + pnum += 8; + + V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); +} + +static void K_drawKartFinish(void) +{ + INT32 pnum = 0, splitflags = K_calcSplitFlags(0); + + if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE) + return; + + if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink + pnum = 1; + + if (r_splitscreen > 1) // 3/4p, stationary FIN + { + pnum += 2; + V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]); + return; + } + + //else -- 1/2p, scrolling FINISH + { + INT32 x, xval; + + if (r_splitscreen) // wide splitscreen + pnum += 4; + + x = ((vid.width<width)<karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; + + if (r_splitscreen && stplyr == &players[displayplayers[1]]) + x = -x; + + V_DrawFixedPatch(x + (STCD_X<>1), + (STCD_Y<height)<<(FRACBITS-1)), + FRACUNIT, + splitflags, kp_racefinish[pnum], NULL); + } +} + +static void K_drawBattleFullscreen(void) +{ + INT32 x = BASEVIDWIDTH/2; + INT32 y = -64+(stplyr->karthud[khud_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen + INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead + fixed_t scale = FRACUNIT; + boolean drawcomebacktimer = true; // lazy hack because it's cleaner in the long run. +#ifdef HAVE_BLUA + if (!LUA_HudEnabled(hud_battlecomebacktimer)) + drawcomebacktimer = false; +#endif + + if (r_splitscreen) + { + if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (r_splitscreen > 1 && (stplyr == &players[displayplayers[2]] + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)))) + { + y = 232-(stplyr->karthud[khud_cardanimation]/2); + splitflags = V_SNAPTOBOTTOM; + } + else + y = -32+(stplyr->karthud[khud_cardanimation]/2); + + if (r_splitscreen > 1) + { + scale /= 2; + + if (stplyr == &players[displayplayers[1]] + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) + x = 3*BASEVIDWIDTH/4; + else + x = BASEVIDWIDTH/4; + } + else + { + if (stplyr->exiting) + { + if (stplyr == &players[displayplayers[1]]) + x = BASEVIDWIDTH-96; + else + x = 96; + } + else + scale /= 2; + } + } + + if (stplyr->exiting) + { + if (stplyr == &players[displayplayers[0]]) + V_DrawFadeScreen(0xFF00, 16); + if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) + { + patch_t *p = kp_battlecool; + + if (K_IsPlayerLosing(stplyr)) + p = kp_battlelose; + else if (stplyr->kartstuff[k_position] == 1) + p = kp_battlewin; + + V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator && drawcomebacktimer) + { + UINT16 t = stplyr->kartstuff[k_comebacktimer]/(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; + + txoff = adjust; + + while (t) + { + txoff += adjust; + t /= 10; + } + + if (r_splitscreen) + { + if (r_splitscreen > 1) + ty = (BASEVIDHEIGHT/4)+33; + if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) + ty += (BASEVIDHEIGHT/2); + } + else + V_DrawFadeScreen(0xFF00, 16); + + if (!comebackshowninfo) + V_DrawFixedPatch(x< 1) + V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE)); + else + { + V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE)); + } + } + + if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY? + { + UINT8 i; + + // check to see if there's anyone else at all + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == displayplayers[0]) + continue; + if (playeringame[i] && !stplyr->spectator) + return; + } + +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_freeplay)) +#endif + K_drawKartFreePlay(leveltime); + } +} + +static void K_drawKartFirstPerson(void) +{ + static INT32 pnum[4], turn[4], drift[4]; + INT32 pn = 0, tn = 0, dr = 0; + INT32 target = 0, splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); + INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT; + fixed_t scale; + UINT8 *colmap = NULL; + ticcmd_t *cmd = &stplyr->cmd; + + if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) + return; + + if (stplyr == &players[displayplayers[1]] && r_splitscreen) + { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } + else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } + else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) + { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } + else + { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } + + if (r_splitscreen) + { + y >>= 1; + if (r_splitscreen > 1) + x >>= 1; + } + + { + if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) + y++; + + if (stplyr->mo->drawflags & MFD_TRANSMASK) + splitflags |= ((stplyr->mo->drawflags & MFD_TRANSMASK) >> MFD_TRANSSHIFT) << FF_TRANSSHIFT; + else if (stplyr->mo->frame & FF_TRANSMASK) + splitflags |= (stplyr->mo->frame & FF_TRANSMASK); + } + + if (cmd->driftturn > 400) // strong left turn + target = 2; + else if (cmd->driftturn < -400) // strong right turn + target = -2; + else if (cmd->driftturn > 0) // weak left turn + target = 1; + else if (cmd->driftturn < 0) // weak right turn + target = -1; + else // forward + target = 0; + + if (encoremode) + target = -target; + + if (pn < target) + pn++; + else if (pn > target) + pn--; + + if (pn < 0) + splitflags |= V_FLIP; // right turn + + target = abs(pn); + if (target > 2) + target = 2; + + x <<= FRACBITS; + y <<= FRACBITS; + + if (tn != cmd->driftturn/50) + tn -= (tn - (cmd->driftturn/50))/8; + + if (dr != stplyr->kartstuff[k_drift]*16) + dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8; + + if (r_splitscreen == 1) + { + scale = (2*FRACUNIT)/3; + y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view) + } + else if (r_splitscreen) + scale = FRACUNIT/2; + else + scale = FRACUNIT; + + if (stplyr->mo) + { + UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->kartstuff[k_driftcharge]); + const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; + // yes, the following is correct. no, you do not need to swap the x and y. + fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); + fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); + + if (r_splitscreen) + xoffs = FixedMul(xoffs, scale); + + xoffs -= (tn)*scale; + xoffs -= (dr)*scale; + + if (stplyr->frameangle == stplyr->mo->angle) + { + const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); + + if (mag < FRACUNIT) + { + xoffs = FixedMul(xoffs, mag); + if (!r_splitscreen) + yoffs = FixedMul(yoffs, mag); + } + } + + if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! + yoffs += stplyr->mo->momz/3; + + if (encoremode) + x -= xoffs; + else + x += xoffs; + if (!r_splitscreen) + y += yoffs; + + + if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks! + colmap = R_GetTranslationColormap(TC_RAINBOW, driftcolor, GTC_CACHE); + else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink! + colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE); + } + + V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap); + + if (stplyr == &players[displayplayers[1]] && r_splitscreen) + { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } + else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } + else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) + { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } + else + { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } +} + +// doesn't need to ever support 4p +static void K_drawInput(void) +{ + static INT32 pn = 0; + INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT); + INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col; + const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5]; + const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9]; + ticcmd_t *cmd = &stplyr->cmd; + + if (timeinmap <= 105) + return; + + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 105); + offs = 64; + while (count-- > 0) + offs >>= 1; + x += offs; + } + +#define BUTTW 8 +#define BUTTH 11 + +#define drawbutt(xoffs, butt, symb)\ + if (stplyr->cmd.buttons & butt)\ + {\ + offs = 2;\ + col = accent1;\ + }\ + else\ + {\ + offs = 0;\ + col = accent2;\ + V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\ + }\ + V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\ + V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn + target = 0; + else // turning of multiple strengths! + { + target = ((abs(cmd->driftturn) - 1)/125)+1; + if (target > 4) + target = 4; + if (cmd->driftturn < 0) + target = -target; + } + + if (pn != target) + { + if (abs(pn - target) == 1) + pn = target; + else if (pn < target) + pn += 2; + else //if (pn > target) + pn -= 2; + } + + if (pn < 0) + { + splitflags |= V_FLIP; // right turn + x--; + } + + target = abs(pn); + if (target > 4) + target = 4; + + if (!stplyr->skincolor) + V_DrawFixedPatch(x<skincolor, GTC_CACHE); + V_DrawFixedPatch(x<karthud[khud_lapanimation]; + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); + + V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, + (48 - (32*max(0, progress-76)))*FRACUNIT, + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap); + + if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3) + { + V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, + (48 - (32*max(0, progress-76)) + + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT, + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); + } + + if (stplyr->laps == (UINT8)(cv_numlaps.value)) + { + V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_final[min(progress/2, 10)], NULL); + + if (progress/2-12 >= 0) + { + V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_lap[min(progress/2-12, 6)], NULL); + } + } + else + { + V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_lap[min(progress/2, 6)], NULL); + + if (progress/2-8 >= 0) + { + V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_number[(((UINT32)stplyr->laps) / 10)][min(progress/2-8, 2)], NULL); + + if (progress/2-10 >= 0) + { + V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_number[(((UINT32)stplyr->laps) % 10)][min(progress/2-10, 2)], NULL); + } + } + } +} + +void K_drawKartFreePlay(UINT32 flashtime) +{ + // no splitscreen support because it's not FREE PLAY if you have more than one player in-game + // (you fool, you can take splitscreen online. :V) + + if ((flashtime % TICRATE) < TICRATE/2) + return; + + V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy + LAPS_Y+3, V_HUDTRANS|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); +} + +static void +Draw_party_ping (int ss, INT32 snap) +{ + HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|snap); +} + +static void +K_drawMiniPing (void) +{ + if (cv_showping.value) + { + switch (r_splitscreen) + { + case 3: + Draw_party_ping(3, V_SNAPTORIGHT|V_SPLITSCREEN); + /*FALLTHRU*/ + case 2: + Draw_party_ping(2, V_SPLITSCREEN); + Draw_party_ping(1, V_SNAPTORIGHT); + Draw_party_ping(0, 0); + break; + case 1: + Draw_party_ping(1, V_SNAPTORIGHT|V_SPLITSCREEN); + Draw_party_ping(0, V_SNAPTORIGHT); + break; + } + } +} + +static void K_drawDistributionDebugger(void) +{ + patch_t *items[NUMKARTRESULTS] = { + kp_sadface[1], + kp_sneaker[1], + kp_rocketsneaker[1], + kp_invincibility[7], + kp_banana[1], + kp_eggman[1], + kp_orbinaut[4], + kp_jawz[1], + kp_mine[1], + kp_ballhog[1], + kp_selfpropelledbomb[1], + kp_grow[1], + kp_shrink[1], + kp_thundershield[1], + kp_bubbleshield[1], + kp_flameshield[1], + kp_hyudoro[1], + kp_pogospring[1], + kp_superring[1], + kp_kitchensink[1], + + kp_sneaker[1], + kp_banana[1], + kp_banana[1], + kp_orbinaut[4], + kp_orbinaut[4], + kp_jawz[1] + }; + UINT8 useodds = 0; + UINT8 pingame = 0, bestbumper = 0; + UINT32 pdis = 0; + INT32 i; + INT32 x = -9, y = -9; + boolean spbrush = false; + + if (stplyr != &players[displayplayers[0]]) // only for p1 + return; + + // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + pingame++; + if (players[i].kartstuff[k_bumper] > bestbumper) + bestbumper = players[i].kartstuff[k_bumper]; + } + + // lovely double loop...... + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator + && players[i].kartstuff[k_position] == 1) + { + // This player is first! Yay! + pdis = stplyr->distancetofinish - players[i].distancetofinish; + break; + } + } + + if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items + pdis = (15 * pdis) / 14; + + if (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell + { + pdis = (3 * pdis) / 2; + spbrush = true; + } + + if (stplyr->bot && stplyr->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count + + useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); + + for (i = 1; i < NUMKARTRESULTS; i++) + { + const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); + if (itemodds <= 0) + continue; + + V_DrawScaledPatch(x, y, V_HUDTRANS|V_SNAPTOTOP, items[i]); + V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SNAPTOTOP, va("%d", itemodds)); + + // Display amount for multi-items + if (i >= NUMKARTITEMS) + { + INT32 amount; + switch (i) + { + case KRITEM_TENFOLDBANANA: + amount = 10; + break; + case KRITEM_QUADORBINAUT: + amount = 4; + break; + case KRITEM_DUALJAWZ: + amount = 2; + break; + default: + amount = 3; + break; + } + V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SNAPTOTOP, va("x%d", amount)); + } + + x += 32; + if (x >= 297) + { + x = -9; + y += 32; + } + } + + V_DrawString(0, 0, V_HUDTRANS|V_SNAPTOTOP, va("USEODDS %d", useodds)); +} + +static void K_drawCheckpointDebugger(void) +{ + if (stplyr != &players[displayplayers[0]]) // only for p1 + return; + + if (stplyr->starpostnum == numstarposts) + V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts)); + else + V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts)); +} + +static void K_DrawWaypointDebugger(void) +{ + if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) + { + V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); + V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); + } +} + +void K_drawKartHUD(void) +{ + boolean isfreeplay = false; + boolean battlefullscreen = false; + boolean freecam = demo.freecam; //disable some hud elements w/ freecam + UINT8 i; + + // Define the X and Y for each drawn object + // This is handled by console/menu values + K_initKartHUD(); + + // Draw that fun first person HUD! Drawn ASAP so it looks more "real". + for (i = 0; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]] && !camera[i].chase && !freecam) + K_drawKartFirstPerson(); + } + + // Draw full screen stuff that turns off the rest of the HUD + if (mapreset && stplyr == &players[displayplayers[0]]) + { + K_drawChallengerScreen(); + return; + } + + battlefullscreen = ((G_BattleGametype()) + && (stplyr->exiting + || (stplyr->kartstuff[k_bumper] <= 0 + && stplyr->kartstuff[k_comebacktimer] + && comeback + && stplyr->playerstate == PST_LIVE))); + + if (!demo.title && (!battlefullscreen || r_splitscreen)) + { + // Draw the CHECK indicator before the other items, so it's overlapped by everything else +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_check)) // delete lua when? +#endif + if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam) + K_drawKartPlayerCheck(); + + // nametags +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_names)) +#endif + K_drawKartNameTags(); + + // Draw WANTED status + if (G_BattleGametype()) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_wanted)) +#endif + K_drawKartWanted(); + } + + if (cv_kartminimap.value) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_minimap)) +#endif + K_drawKartMinimap(); + } + } + + if (battlefullscreen && !freecam) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_battlefullscreen)) +#endif + K_drawBattleFullscreen(); + return; + } + + // Draw the item window +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_item) && !freecam) +#endif + K_drawKartItem(); + + // If not splitscreen, draw... + if (!r_splitscreen && !demo.title) + { + // Draw the timestamp +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_time)) +#endif + K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0); + + if (!modeattacking) + { + // The top-four faces on the left + /*#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_minirankings)) + #endif*/ + isfreeplay = K_drawKartPositionFaces(); + } + } + + if (!stplyr->spectator && !demo.freecam) // Bottom of the screen elements, don't need in spectate mode + { + // Draw the speedometer + if (cv_kartspeedometer.value && !r_splitscreen) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_speedometer)) +#endif + K_drawKartSpeedometer(); + } + + if (demo.title) // Draw title logo instead in demo.titles + { + INT32 x = BASEVIDWIDTH - 32, y = 128, offs; + + if (r_splitscreen == 3) + { + x = BASEVIDWIDTH/2 + 10; + y = BASEVIDHEIGHT/2 - 30; + } + + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 104); + offs = 256; + while (count-- > 0) + offs >>= 1; + x += offs; + } + + V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); + V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); + } + else if (G_RaceGametype()) // Race-only elements + { + // Draw the lap counter +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_gametypeinfo)) +#endif + K_drawKartLapsAndRings(); + + if (isfreeplay) + ; + else if (!modeattacking) + { + // Draw the numerical position +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_position)) +#endif + K_DrawKartPositionNum(stplyr->kartstuff[k_position]); + } + else //if (!(demo.playback && hu_showscores)) + { + // Draw the input UI +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_position)) +#endif + K_drawInput(); + } + } + else if (G_BattleGametype()) // Battle-only + { + // Draw the hits left! +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_gametypeinfo)) +#endif + K_drawKartBumpersOrKarma(); + } + } + + // Draw the countdowns after everything else. + if (leveltime >= starttime-(3*TICRATE) + && leveltime < starttime+TICRATE) + K_drawKartStartCountdown(); + else if (racecountdown && (!r_splitscreen || !stplyr->exiting)) + { + char *countstr = va("%d", racecountdown/TICRATE); + + if (r_splitscreen > 1) + V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, K_calcSplitFlags(0), countstr); + else + { + INT32 karlen = strlen(countstr)*6; // half of 12 + V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, K_calcSplitFlags(0), countstr); + } + } + + // Race overlays + if (G_RaceGametype() && !freecam) + { + if (stplyr->exiting) + K_drawKartFinish(); + else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen) + K_drawLapStartAnim(); + } + + if (modeattacking || freecam) // everything after here is MP and debug only + return; + + if (G_BattleGametype() && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * + V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); + + // Draw FREE PLAY. + if (isfreeplay && !stplyr->spectator && timeinmap > 113) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_freeplay)) +#endif + K_drawKartFreePlay(leveltime); + } + + if (r_splitscreen == 0 && stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) + { + V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); + } + + if (netgame && r_splitscreen) + { + K_drawMiniPing(); + } + + if (cv_kartdebugdistribution.value) + K_drawDistributionDebugger(); + + if (cv_kartdebugcheckpoint.value) + K_drawCheckpointDebugger(); + + if (cv_kartdebugnodes.value) + { + UINT8 p; + for (p = 0; p < MAXPLAYERS; p++) + V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency)); + } + + if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin) + { + INT32 x = 0, y = 0; + UINT8 c; + + for (c = 1; c < MAXSKINCOLORS; c++) + { + UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, c, GTC_CACHE); + V_DrawFixedPatch(x<>1, 0, facewantprefix[stplyr->skin], cm); + + x += 16; + if (x > BASEVIDWIDTH-16) + { + x = 0; + y += 16; + } + } + } + + K_DrawWaypointDebugger(); +} + +//} From c2cb91e3ae30ce75782ac4a64a68de27cb6cf76c Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 21 Jul 2020 17:08:20 -0700 Subject: [PATCH 148/211] Correct CRLF --- src/k_kart.c | 22710 ++++++++++++++++++++++++------------------------- 1 file changed, 11355 insertions(+), 11355 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7816e4f8f..3a0a6048d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1,11355 +1,11355 @@ -// SONIC ROBO BLAST 2 KART ~ ZarroTsu -//----------------------------------------------------------------------------- -/// \file k_kart.c -/// \brief SRB2kart general. -/// All of the SRB2kart-unique stuff. - -#include "k_kart.h" -#include "k_battle.h" -#include "k_pwrlv.h" -#include "k_color.h" -#include "k_respawn.h" -#include "doomdef.h" -#include "hu_stuff.h" -#include "g_game.h" -#include "m_random.h" -#include "p_local.h" -#include "p_slopes.h" -#include "p_setup.h" -#include "r_draw.h" -#include "r_local.h" -#include "s_sound.h" -#include "st_stuff.h" -#include "v_video.h" -#include "z_zone.h" -#include "m_misc.h" -#include "m_cond.h" -#include "f_finale.h" -#include "lua_hud.h" // For Lua hud checks -#include "lua_hook.h" // For MobjDamage and ShouldDamage - -#include "k_waypoint.h" -#include "k_bot.h" - -// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: -// gamespeed is cc (0 for easy, 1 for normal, 2 for hard) -// franticitems is Frantic Mode items, bool -// encoremode is Encore Mode (duh), bool -// comeback is Battle Mode's karma comeback, also bool -// battlewanted is an array of the WANTED player nums, -1 for no player in that slot -// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB -// mapreset is set when enough players fill an empty server - -UINT16 K_GetPlayerDontDrawFlag(player_t *player) -{ - UINT16 flag = 0; - - if (player == &players[displayplayers[0]]) - flag = MFD_DONTDRAWP1; - else if (r_splitscreen >= 1 && player == &players[displayplayers[1]]) - flag = MFD_DONTDRAWP2; - else if (r_splitscreen >= 2 && player == &players[displayplayers[2]]) - flag = MFD_DONTDRAWP3; - else if (r_splitscreen >= 3 && player == &players[displayplayers[3]]) - flag = MFD_DONTDRAWP4; - - return flag; -} - -player_t *K_GetItemBoxPlayer(mobj_t *mobj) -{ - fixed_t closest = INT32_MAX; - player_t *player = NULL; - UINT8 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo) && !players[i].spectator)) - { - continue; - } - - // Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this - if (P_CanPickupItem(&players[i], 1)) - { - fixed_t dist = P_AproxDistance(P_AproxDistance( - players[i].mo->x - mobj->x, - players[i].mo->y - mobj->y), - players[i].mo->z - mobj->z - ); - - if (dist > 8192*mobj->scale) - { - continue; - } - - if (dist < closest) - { - player = &players[i]; - closest = dist; - } - } - } - - return player; -} - -// Angle reflection used by springs & speed pads -angle_t K_ReflectAngle(angle_t yourangle, angle_t theirangle, fixed_t yourspeed, fixed_t theirspeed) -{ - INT32 angoffset; - boolean subtract = false; - - angoffset = yourangle - theirangle; - - if ((angle_t)angoffset > ANGLE_180) - { - // Flip on wrong side - angoffset = InvAngle((angle_t)angoffset); - subtract = !subtract; - } - - // Fix going directly against the spring's angle sending you the wrong way - if ((angle_t)angoffset > ANGLE_90) - { - angoffset = ANGLE_180 - angoffset; - } - - // Offset is reduced to cap it (90 / 2 = max of 45 degrees) - angoffset /= 2; - - // Reduce further based on how slow your speed is compared to the spring's speed - // (set both to 0 to ignore this) - if (theirspeed != 0 && yourspeed != 0) - { - if (theirspeed > yourspeed) - { - angoffset = FixedDiv(angoffset, FixedDiv(theirspeed, yourspeed)); - } - } - - if (subtract) - angoffset = (signed)(theirangle) - angoffset; - else - angoffset = (signed)(theirangle) + angoffset; - - return (angle_t)angoffset; -} - -//{ SRB2kart Net Variables - -void K_RegisterKartStuff(void) -{ - CV_RegisterVar(&cv_sneaker); - CV_RegisterVar(&cv_rocketsneaker); - CV_RegisterVar(&cv_invincibility); - CV_RegisterVar(&cv_banana); - CV_RegisterVar(&cv_eggmanmonitor); - CV_RegisterVar(&cv_orbinaut); - CV_RegisterVar(&cv_jawz); - CV_RegisterVar(&cv_mine); - CV_RegisterVar(&cv_ballhog); - CV_RegisterVar(&cv_selfpropelledbomb); - CV_RegisterVar(&cv_grow); - CV_RegisterVar(&cv_shrink); - CV_RegisterVar(&cv_thundershield); - CV_RegisterVar(&cv_bubbleshield); - CV_RegisterVar(&cv_flameshield); - CV_RegisterVar(&cv_hyudoro); - CV_RegisterVar(&cv_pogospring); - CV_RegisterVar(&cv_superring); - CV_RegisterVar(&cv_kitchensink); - - CV_RegisterVar(&cv_triplesneaker); - CV_RegisterVar(&cv_triplebanana); - CV_RegisterVar(&cv_decabanana); - CV_RegisterVar(&cv_tripleorbinaut); - CV_RegisterVar(&cv_quadorbinaut); - CV_RegisterVar(&cv_dualjawz); - - CV_RegisterVar(&cv_kartminimap); - CV_RegisterVar(&cv_kartcheck); - CV_RegisterVar(&cv_kartinvinsfx); - CV_RegisterVar(&cv_kartspeed); - CV_RegisterVar(&cv_kartbumpers); - CV_RegisterVar(&cv_kartfrantic); - CV_RegisterVar(&cv_kartcomeback); - CV_RegisterVar(&cv_kartencore); - CV_RegisterVar(&cv_kartvoterulechanges); - CV_RegisterVar(&cv_kartspeedometer); - CV_RegisterVar(&cv_kartvoices); - CV_RegisterVar(&cv_kartbot); - CV_RegisterVar(&cv_karteliminatelast); - CV_RegisterVar(&cv_kartusepwrlv); - CV_RegisterVar(&cv_votetime); - - CV_RegisterVar(&cv_kartdebugitem); - CV_RegisterVar(&cv_kartdebugamount); - CV_RegisterVar(&cv_kartdebugshrink); - CV_RegisterVar(&cv_kartallowgiveitem); - CV_RegisterVar(&cv_kartdebugdistribution); - CV_RegisterVar(&cv_kartdebughuddrop); - CV_RegisterVar(&cv_kartdebugwaypoints); - - CV_RegisterVar(&cv_kartdebugcheckpoint); - CV_RegisterVar(&cv_kartdebugnodes); - CV_RegisterVar(&cv_kartdebugcolorize); -} - -//} - -boolean K_IsPlayerLosing(player_t *player) -{ - INT32 winningpos = 1; - UINT8 i, pcount = 0; - - if (battlecapsules && player->kartstuff[k_bumper] <= 0) - return true; // DNF in break the capsules - - if (player->kartstuff[k_position] == 1) - return false; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - if (players[i].kartstuff[k_position] > pcount) - pcount = players[i].kartstuff[k_position]; - } - - if (pcount <= 1) - return false; - - winningpos = pcount/2; - if (pcount % 2) // any remainder? - winningpos++; - - return (player->kartstuff[k_position] > winningpos); -} - -fixed_t K_GetKartGameSpeedScalar(SINT8 value) -{ - // Easy = 81.25% - // Normal = 100% - // Hard = 118.75% - // Nightmare = 137.5% ?!?! - return ((13 + (3*value)) << FRACBITS) / 16; -} - -//{ SRB2kart Roulette Code - Position Based - -consvar_t *KartItemCVars[NUMKARTRESULTS-1] = -{ - &cv_sneaker, - &cv_rocketsneaker, - &cv_invincibility, - &cv_banana, - &cv_eggmanmonitor, - &cv_orbinaut, - &cv_jawz, - &cv_mine, - &cv_ballhog, - &cv_selfpropelledbomb, - &cv_grow, - &cv_shrink, - &cv_thundershield, - &cv_bubbleshield, - &cv_flameshield, - &cv_hyudoro, - &cv_pogospring, - &cv_superring, - &cv_kitchensink, - &cv_triplesneaker, - &cv_triplebanana, - &cv_decabanana, - &cv_tripleorbinaut, - &cv_quadorbinaut, - &cv_dualjawz -}; - -#define NUMKARTODDS 80 - -// Less ugly 2D arrays -static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = -{ - //P-Odds 0 1 2 3 4 5 6 7 - /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker - /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker - /*Invincibility*/ { 0, 0, 0, 0, 1, 4, 7, 9 }, // Invincibility - /*Banana*/ { 7, 3, 2, 0, 0, 0, 0, 0 }, // Banana - /*Eggman Monitor*/ { 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor - /*Orbinaut*/ { 7, 4, 3, 2, 0, 0, 0, 0 }, // Orbinaut - /*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz - /*Mine*/ { 0, 2, 2, 1, 0, 0, 0, 0 }, // Mine - /*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog - /*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb - /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink - /*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield - /*Bubble Shield*/ { 0, 2, 3, 3, 1, 0, 0, 0 }, // Bubble Shield - /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield - /*Hyudoro*/ { 0, 0, 0, 1, 2, 0, 0, 0 }, // Hyudoro - /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring - /*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring - /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink - /*Sneaker x3*/ { 0, 0, 0, 2, 6,10, 5, 0 }, // Sneaker x3 - /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 - /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 - /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 - /*Orbinaut x4*/ { 0, 0, 0, 1, 1, 0, 0, 0 }, // Orbinaut x4 - /*Jawz x2*/ { 0, 0, 1, 2, 0, 0, 0, 0 } // Jawz x2 -}; - -static INT32 K_KartItemOddsBattle[NUMKARTRESULTS-1][6] = -{ - //P-Odds 0 1 2 3 4 5 - /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker - /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker - /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility - /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana - /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor - /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut - /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz - /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine - /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog - /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb - /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow - /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink - /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield - /*Bubble Shield*/ { 0, 0, 0, 0, 0, 0 }, // Bubble Shield - /*Flame Shield*/ { 0, 0, 0, 0, 0, 0 }, // Flame Shield - /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro - /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring - /*Super Ring*/ { 0, 0, 0, 0, 0, 0 }, // Super Ring - /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink - /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3 - /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3 - /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10 - /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3 - /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4 - /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 -}; - -#define DISTVAR (2048) // Magic number distance for use with item roulette tiers - -INT32 K_GetShieldFromItem(INT32 item) -{ - switch (item) - { - case KITEM_THUNDERSHIELD: return KSHIELD_THUNDER; - case KITEM_BUBBLESHIELD: return KSHIELD_BUBBLE; - case KITEM_FLAMESHIELD: return KSHIELD_FLAME; - default: return KSHIELD_NONE; - } -} - -/** \brief Item Roulette for Kart - - \param player player - \param getitem what item we're looking for - - \return void -*/ -static void K_KartGetItemResult(player_t *player, SINT8 getitem) -{ - if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) // Indirect items - indirectitemcooldown = 20*TICRATE; - - if (getitem == KITEM_HYUDORO) // Hyudoro cooldown - hyubgone = 5*TICRATE; - - player->botvars.itemdelay = TICRATE; - player->botvars.itemconfirm = 0; - - switch (getitem) - { - // Special roulettes first, then the generic ones are handled by default - case KRITEM_TRIPLESNEAKER: // Sneaker x3 - player->kartstuff[k_itemtype] = KITEM_SNEAKER; - player->kartstuff[k_itemamount] = 3; - break; - case KRITEM_TRIPLEBANANA: // Banana x3 - player->kartstuff[k_itemtype] = KITEM_BANANA; - player->kartstuff[k_itemamount] = 3; - break; - case KRITEM_TENFOLDBANANA: // Banana x10 - player->kartstuff[k_itemtype] = KITEM_BANANA; - player->kartstuff[k_itemamount] = 10; - break; - case KRITEM_TRIPLEORBINAUT: // Orbinaut x3 - player->kartstuff[k_itemtype] = KITEM_ORBINAUT; - player->kartstuff[k_itemamount] = 3; - break; - case KRITEM_QUADORBINAUT: // Orbinaut x4 - player->kartstuff[k_itemtype] = KITEM_ORBINAUT; - player->kartstuff[k_itemamount] = 4; - break; - case KRITEM_DUALJAWZ: // Jawz x2 - player->kartstuff[k_itemtype] = KITEM_JAWZ; - player->kartstuff[k_itemamount] = 2; - break; - default: - if (getitem <= 0 || getitem >= NUMKARTRESULTS) // Sad (Fallback) - { - if (getitem != 0) - CONS_Printf("ERROR: P_KartGetItemResult - Item roulette gave bad item (%d) :(\n", getitem); - player->kartstuff[k_itemtype] = KITEM_SAD; - } - else - player->kartstuff[k_itemtype] = getitem; - player->kartstuff[k_itemamount] = 1; - break; - } -} - -/** \brief Item Roulette for Kart - - \param player player object passed from P_KartPlayerThink - - \return void -*/ - -static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) -{ - INT32 newodds; - INT32 i; - UINT8 pingame = 0, pexiting = 0; - SINT8 first = -1, second = -1; - INT32 secondist = 0; - INT32 shieldtype = KSHIELD_NONE; - - I_Assert(item > KITEM_NONE); // too many off by one scenarioes. - I_Assert(KartItemCVars[NUMKARTRESULTS-2] != NULL); // Make sure this exists - - if (!KartItemCVars[item-1]->value && !modeattacking) - return 0; - - if (G_BattleGametype()) - { - I_Assert(pos < 6); // DO NOT allow positions past the bounds of the table - newodds = K_KartItemOddsBattle[item-1][pos]; - } - else - { - I_Assert(pos < 8); // Ditto - newodds = K_KartItemOddsRace[item-1][pos]; - } - - // Base multiplication to ALL item odds to simulate fractional precision - newodds *= 4; - - shieldtype = K_GetShieldFromItem(item); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - - if (!G_BattleGametype() || players[i].kartstuff[k_bumper]) - pingame++; - - if (players[i].exiting) - pexiting++; - - if (shieldtype != KSHIELD_NONE && shieldtype == K_GetShieldFromItem(players[i].kartstuff[k_itemtype])) - { - // Don't allow more than one of each shield type at a time - return 0; - } - - if (players[i].mo && G_RaceGametype()) - { - if (players[i].kartstuff[k_position] == 1 && first == -1) - first = i; - if (players[i].kartstuff[k_position] == 2 && second == -1) - second = i; - } - } - - if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB - { - secondist = players[second].distancetofinish - players[first].distancetofinish; - if (franticitems) - secondist = (15 * secondist) / 14; - secondist = ((28 + (8-pingame)) * secondist) / 28; - } - - // POWERITEMODDS handles all of the "frantic item" related functionality, for all of our powerful items. - // First, it multiplies it by 2 if franticitems is true; easy-peasy. - // Next, it multiplies it again if it's in SPB mode and 2nd needs to apply pressure to 1st. - // Then, it multiplies it further if the player count isn't equal to 8. - // This is done to make low player count races more interesting and high player count rates more fair. - // (2P normal would be about halfway between 8P normal and 8P frantic.) - // (This scaling is not done for SPB Rush, so that catchup strength is not weakened.) - // Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, for lesser items needed in a pinch. - -#define PLAYERSCALING (8 - (spbrush ? 2 : pingame)) - -#define POWERITEMODDS(odds) {\ - if (franticitems) \ - odds *= 2; \ - if (rival) \ - odds *= 2; \ - odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \ - if (mashed > 0) \ - odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \ -} - -#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) - - /* - if (bot) - { - // TODO: Item use on bots should all be passed-in functions. - // Instead of manually inserting these, it should return 0 - // for any items without an item use function supplied - - switch (item) - { - case KITEM_SNEAKER: - case KITEM_ROCKETSNEAKER: - case KITEM_INVINCIBILITY: - case KITEM_BANANA: - case KITEM_EGGMAN: - case KITEM_ORBINAUT: - case KITEM_JAWZ: - case KITEM_MINE: - case KITEM_BALLHOG: - case KITEM_SPB: - case KITEM_GROW: - case KITEM_SHRINK: - case KITEM_HYUDORO: - case KITEM_SUPERRING: - case KITEM_THUNDERSHIELD: - case KITEM_BUBBLESHIELD: - case KITEM_FLAMESHIELD: - case KRITEM_TRIPLESNEAKER: - case KRITEM_TRIPLEBANANA: - case KRITEM_TENFOLDBANANA: - case KRITEM_TRIPLEORBINAUT: - case KRITEM_QUADORBINAUT: - case KRITEM_DUALJAWZ: - break; - default: - return 0; - } - } - */ - (void)bot; - - switch (item) - { - case KITEM_ROCKETSNEAKER: - case KITEM_JAWZ: - case KITEM_BALLHOG: - case KRITEM_TRIPLESNEAKER: - case KRITEM_TRIPLEBANANA: - case KRITEM_TENFOLDBANANA: - case KRITEM_TRIPLEORBINAUT: - case KRITEM_QUADORBINAUT: - case KRITEM_DUALJAWZ: - POWERITEMODDS(newodds); - break; - case KITEM_INVINCIBILITY: - case KITEM_MINE: - case KITEM_GROW: - case KITEM_BUBBLESHIELD: - case KITEM_FLAMESHIELD: - if (COOLDOWNONSTART) - newodds = 0; - else - POWERITEMODDS(newodds); - break; - case KITEM_SPB: - if ((indirectitemcooldown > 0) || COOLDOWNONSTART - || (first != -1 && players[first].distancetofinish < 8*DISTVAR)) // No SPB near the end of the race - { - newodds = 0; - } - else - { - INT32 multiplier = (secondist - (5*DISTVAR)) / DISTVAR; - - if (multiplier < 0) - multiplier = 0; - if (multiplier > 3) - multiplier = 3; - - newodds *= multiplier; - } - break; - case KITEM_SHRINK: - if ((indirectitemcooldown > 0) || COOLDOWNONSTART || (pingame-1 <= pexiting)) - newodds = 0; - else - POWERITEMODDS(newodds); - break; - case KITEM_THUNDERSHIELD: - if (spbplace != -1 || COOLDOWNONSTART) - newodds = 0; - else - POWERITEMODDS(newodds); - break; - case KITEM_HYUDORO: - if ((hyubgone > 0) || COOLDOWNONSTART) - newodds = 0; - break; - default: - break; - } - -#undef POWERITEMODDS - - return newodds; -} - -//{ SRB2kart Roulette Code - Distance Based, yes waypoints - -static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) -{ - UINT8 i; - UINT8 n = 0; - UINT8 useodds = 0; - UINT8 disttable[14]; - UINT8 totallen = 0; - UINT8 distlen = 0; - boolean oddsvalid[8]; - - for (i = 0; i < 8; i++) - { - UINT8 j; - boolean available = false; - - if (G_BattleGametype() && i > 5) - { - oddsvalid[i] = false; - break; - } - - for (j = 1; j < NUMKARTRESULTS; j++) - { - if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0) - { - available = true; - break; - } - } - - oddsvalid[i] = available; - } - -#define SETUPDISTTABLE(odds, num) \ - if (oddsvalid[odds]) \ - for (i = num; i; --i) \ - disttable[distlen++] = odds; \ - totallen += num; - - if (G_BattleGametype()) // Battle Mode - { - SETUPDISTTABLE(0,1); - SETUPDISTTABLE(1,1); - SETUPDISTTABLE(2,1); - SETUPDISTTABLE(3,1); - SETUPDISTTABLE(4,1); - - if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items - useodds = 5; - else - { - SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc - if (K_IsPlayerWanted(player)) - wantedpos++; - if (wantedpos > 4) // Don't run off into karma items - wantedpos = 4; - if (wantedpos < 0) // Don't go below somehow - wantedpos = 0; - n = (wantedpos * distlen) / totallen; - useodds = disttable[n]; - } - } - else - { - SETUPDISTTABLE(0,1); - SETUPDISTTABLE(1,1); - SETUPDISTTABLE(2,1); - SETUPDISTTABLE(3,2); - SETUPDISTTABLE(4,2); - SETUPDISTTABLE(5,3); - SETUPDISTTABLE(6,3); - SETUPDISTTABLE(7,1); - - if (pdis == 0) - useodds = disttable[0]; - else if (pdis > DISTVAR * ((12 * distlen) / 14)) - useodds = disttable[distlen-1]; - else - { - for (i = 1; i < 13; i++) - { - if (pdis <= DISTVAR * ((i * distlen) / 14)) - { - useodds = disttable[((i * distlen) / 14)]; - break; - } - } - } - } - -#undef SETUPDISTTABLE - - return useodds; -} - -static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) -{ - INT32 i; - UINT8 pingame = 0; - UINT8 roulettestop; - UINT32 pdis = 0; - UINT8 useodds = 0; - INT32 spawnchance[NUMKARTRESULTS]; - INT32 totalspawnchance = 0; - UINT8 bestbumper = 0; - fixed_t mashed = 0; - boolean dontforcespb = false; - boolean spbrush = false; - - // This makes the roulette cycle through items - if this is 0, you shouldn't be here. - if (player->kartstuff[k_itemroulette]) - player->kartstuff[k_itemroulette]++; - else - return; - - // Gotta check how many players are active at this moment. - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - pingame++; - if (players[i].exiting) - dontforcespb = true; - if (players[i].kartstuff[k_bumper] > bestbumper) - bestbumper = players[i].kartstuff[k_bumper]; - } - - // No forced SPB in 1v1s, it has to be randomly rolled - if (pingame <= 2) - dontforcespb = true; - - // This makes the roulette produce the random noises. - if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player) && !demo.freecam) - { -#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8)) - for (i = 0; i <= r_splitscreen; i++) - { - if (player == &players[displayplayers[i]] && players[displayplayers[i]].kartstuff[k_itemroulette]) - PLAYROULETTESND; - } -#undef PLAYROULETTESND - } - - roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position])); - - // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item. - // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think. - // Finally, if you get past this check, now you can actually start calculating what item you get. - if ((cmd->buttons & BT_ATTACK) && (player->kartstuff[k_itemroulette] >= roulettestop) - && !(player->kartstuff[k_eggmanheld] || player->kartstuff[k_itemheld] || player->kartstuff[k_userings])) - { - // Mashing reduces your chances for the good items - mashed = FixedDiv((player->kartstuff[k_itemroulette])*FRACUNIT, ((TICRATE*3)+roulettestop)*FRACUNIT) - FRACUNIT; - } - else if (!(player->kartstuff[k_itemroulette] >= (TICRATE*3))) - return; - - if (cmd->buttons & BT_ATTACK) - player->pflags |= PF_ATTACKDOWN; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].kartstuff[k_position] == 1) - { - // This player is first! Yay! - - if (player->distancetofinish <= players[i].distancetofinish) - { - // Guess you're in first / tied for first? - pdis = 0; - } - else - { - // Subtract 1st's distance from your distance, to get your distance from 1st! - pdis = player->distancetofinish - players[i].distancetofinish; - } - break; - } - } - - if (mapobjectscale != FRACUNIT) - pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT; - - if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items - { - pdis = (15 * pdis) / 14; - } - - if (spbplace != -1 && player->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell - { - pdis = (3 * pdis) / 2; - spbrush = true; - } - - if (player->bot && player->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } - - pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count - - // SPECIAL CASE No. 1: - // Fake Eggman items - if (player->kartstuff[k_roulettetype] == 2) - { - player->kartstuff[k_eggmanexplode] = 4*TICRATE; - //player->karthud[khud_itemblink] = TICRATE; - //player->karthud[khud_itemblinkmode] = 1; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, sfx_itrole); - return; - } - - // SPECIAL CASE No. 2: - // Give a debug item instead if specified - if (cv_kartdebugitem.value != 0 && !modeattacking) - { - K_KartGetItemResult(player, cv_kartdebugitem.value); - player->kartstuff[k_itemamount] = cv_kartdebugamount.value; - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 2; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, sfx_dbgsal); - return; - } - - // SPECIAL CASE No. 3: - // Record Attack / alone mashing behavior - if (modeattacking || pingame == 1) - { - if (G_RaceGametype()) - { - if (mashed && (modeattacking || cv_superring.value)) // ANY mashed value? You get rings. - { - K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblinkmode] = 1; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - } - else - { - if (modeattacking || cv_sneaker.value) // Waited patiently? You get a sneaker! - K_KartGetItemResult(player, KITEM_SNEAKER); - else // Default to sad if nothing's enabled... - K_KartGetItemResult(player, KITEM_SAD); - player->karthud[khud_itemblinkmode] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolf); - } - } - else if (G_BattleGametype()) - { - if (mashed && (modeattacking || cv_banana.value)) // ANY mashed value? You get a banana. - { - K_KartGetItemResult(player, KITEM_BANANA); - player->karthud[khud_itemblinkmode] = 1; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - } - else - { - if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3! - K_KartGetItemResult(player, KRITEM_TRIPLEORBINAUT); - else // Default to sad if nothing's enabled... - K_KartGetItemResult(player, KITEM_SAD); - player->karthud[khud_itemblinkmode] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolf); - } - } - - player->karthud[khud_itemblink] = TICRATE; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - return; - } - - if (G_RaceGametype()) - { - // SPECIAL CASE No. 4: - // Being in ring debt occasionally forces Super Ring on you if you mashed - if (mashed && player->kartstuff[k_rings] < 0 && cv_superring.value) - { - INT32 debtamount = min(20, abs(player->kartstuff[k_rings])); - if (P_RandomChance((debtamount*FRACUNIT)/20)) - { - K_KartGetItemResult(player, KITEM_SUPERRING); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = 1; - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, sfx_itrolm); - return; - } - } - - // SPECIAL CASE No. 5: - // Force SPB onto 2nd if they get too far behind - if (player->kartstuff[k_position] == 2 && pdis > (DISTVAR*8) - && spbplace == -1 && !indirectitemcooldown && !dontforcespb - && cv_selfpropelledbomb.value) - { - K_KartGetItemResult(player, KITEM_SPB); - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = (mashed ? 1 : 0); - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - if (P_IsDisplayPlayer(player)) - S_StartSound(NULL, (mashed ? sfx_itrolm : sfx_itrolf)); - return; - } - } - - // NOW that we're done with all of those specialized cases, we can move onto the REAL item roulette tables. - // Initializes existing spawnchance values - for (i = 0; i < NUMKARTRESULTS; i++) - spawnchance[i] = 0; - - // Split into another function for a debug function below - useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); - - for (i = 1; i < NUMKARTRESULTS; i++) - spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival))); - - // Award the player whatever power is rolled - if (totalspawnchance > 0) - { - totalspawnchance = P_RandomKey(totalspawnchance); - for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); - - K_KartGetItemResult(player, i); - } - else - { - player->kartstuff[k_itemtype] = KITEM_SAD; - player->kartstuff[k_itemamount] = 1; - } - - if (P_IsDisplayPlayer(player) && !demo.freecam) - S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); - - player->karthud[khud_itemblink] = TICRATE; - player->karthud[khud_itemblinkmode] = ((player->kartstuff[k_roulettetype] == 1) ? 2 : (mashed ? 1 : 0)); - - player->kartstuff[k_itemroulette] = 0; // Since we're done, clear the roulette number - player->kartstuff[k_roulettetype] = 0; // This too -} - -//} - -//{ SRB2kart p_user.c Stuff - -static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) -{ - fixed_t weight = 5*FRACUNIT; - - if (!mobj->player) - return weight; - - if (against && !P_MobjWasRemoved(against) && against->player - && ((!against->player->kartstuff[k_spinouttimer] && mobj->player->kartstuff[k_spinouttimer]) // You're in spinout - || (against->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD && mobj->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD))) // They have a Bubble Shield - { - weight = 0; // This player does not cause any bump action - } - else - { - weight = (mobj->player->kartweight) * FRACUNIT; - if (mobj->player->speed > K_GetKartSpeed(mobj->player, false)) - weight += (mobj->player->speed - K_GetKartSpeed(mobj->player, false))/8; - if (mobj->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) - weight += 9*FRACUNIT; - } - - return weight; -} - -fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) -{ - fixed_t weight = 5*FRACUNIT; - - switch (mobj->type) - { - case MT_PLAYER: - if (!mobj->player) - break; - weight = K_PlayerWeight(mobj, against); - break; - case MT_BUBBLESHIELD: - weight = K_PlayerWeight(mobj->target, against); - break; - case MT_FALLINGROCK: - if (against->player) - { - if (against->player->kartstuff[k_invincibilitytimer] || against->player->kartstuff[k_growshrinktimer] > 0) - weight = 0; - else - weight = K_PlayerWeight(against, NULL); - } - break; - case MT_ORBINAUT: - case MT_ORBINAUT_SHIELD: - if (against->player) - weight = K_PlayerWeight(against, NULL); - break; - case MT_JAWZ: - case MT_JAWZ_DUD: - case MT_JAWZ_SHIELD: - if (against->player) - weight = K_PlayerWeight(against, NULL) + (3*FRACUNIT); - else - weight += 3*FRACUNIT; - break; - default: - break; - } - - return FixedMul(weight, mobj->scale); -} - -// This kind of wipeout happens with no rings -- doesn't remove a bumper, has no invulnerability, and is much shorter. -static void K_DebtStingPlayer(player_t *player, INT32 length) -{ - if (player->health <= 0) - return; - - if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 - || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - return; - - player->kartstuff[k_ringboost] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - player->kartstuff[k_spinouttype] = 2; - player->kartstuff[k_spinouttimer] = length; - player->kartstuff[k_wipeoutslow] = min(length-1, wipeoutslowtime+1); - - if (player->mo->state != &states[S_KART_SPIN]) - P_SetPlayerMobjState(player->mo, S_KART_SPIN); - - K_DropHnextList(player, false); - return; -} - -void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) -{ - mobj_t *fx; - fixed_t momdifx, momdify; - fixed_t distx, disty; - fixed_t dot, force; - fixed_t mass1, mass2; - - if (!mobj1 || !mobj2) - return; - - // Don't bump when you're being reborn - if ((mobj1->player && mobj1->player->playerstate != PST_LIVE) - || (mobj2->player && mobj2->player->playerstate != PST_LIVE)) - return; - - if ((mobj1->player && mobj1->player->respawn.state != RESPAWNST_NONE) - || (mobj2->player && mobj2->player->respawn.state != RESPAWNST_NONE)) - return; - - { // Don't bump if you're flashing - INT32 flash; - - flash = K_GetKartFlashing(mobj1->player); - if (mobj1->player && mobj1->player->powers[pw_flashing] > 0 && mobj1->player->powers[pw_flashing] < flash) - { - if (mobj1->player->powers[pw_flashing] < flash-1) - mobj1->player->powers[pw_flashing]++; - return; - } - - flash = K_GetKartFlashing(mobj2->player); - if (mobj2->player && mobj2->player->powers[pw_flashing] > 0 && mobj2->player->powers[pw_flashing] < flash) - { - if (mobj2->player->powers[pw_flashing] < flash-1) - mobj2->player->powers[pw_flashing]++; - return; - } - } - - // Don't bump if you've recently bumped - if (mobj1->player && mobj1->player->kartstuff[k_justbumped]) - { - mobj1->player->kartstuff[k_justbumped] = bumptime; - return; - } - - if (mobj2->player && mobj2->player->kartstuff[k_justbumped]) - { - mobj2->player->kartstuff[k_justbumped] = bumptime; - return; - } - - mass1 = K_GetMobjWeight(mobj1, mobj2); - - if (solid == true && mass1 > 0) - mass2 = mass1; - else - mass2 = K_GetMobjWeight(mobj2, mobj1); - - momdifx = mobj1->momx - mobj2->momx; - momdify = mobj1->momy - mobj2->momy; - - // Adds the OTHER player's momentum times a bunch, for the best chance of getting the correct direction - distx = (mobj1->x + mobj2->momx*3) - (mobj2->x + mobj1->momx*3); - 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; - - { // 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); - fixed_t nx = FixedDiv(distx, dist); - fixed_t ny = FixedDiv(disty, dist); - - dist = dist ? dist : 1; - distx = FixedMul(mobj1->radius+mobj2->radius, nx); - disty = FixedMul(mobj1->radius+mobj2->radius, ny); - - if (momdifx == 0 && momdify == 0) - { - // If there's no momentum difference, they're moving at exactly the same rate. Pretend they moved into each other. - momdifx = -nx; - momdify = -ny; - } - } - - // if the speed difference is less than this let's assume they're going proportionately faster from each other - if (P_AproxDistance(momdifx, momdify) < (25*mapobjectscale)) - { - fixed_t momdiflength = P_AproxDistance(momdifx, momdify); - fixed_t normalisedx = FixedDiv(momdifx, momdiflength); - fixed_t normalisedy = FixedDiv(momdify, momdiflength); - momdifx = FixedMul((25*mapobjectscale), normalisedx); - momdify = FixedMul((25*mapobjectscale), normalisedy); - } - - dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty); - - if (dot >= 0) - { - // They're moving away from each other - return; - } - - force = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty)); - - if (bounce == true && mass2 > 0) // Perform a Goomba Bounce. - mobj1->momz = -mobj1->momz; - else - { - fixed_t newz = mobj1->momz; - if (mass2 > 0) - mobj1->momz = mobj2->momz; - if (mass1 > 0 && solid == false) - mobj2->momz = newz; - } - - if (mass2 > 0) - { - mobj1->momx = mobj1->momx - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), distx); - mobj1->momy = mobj1->momy - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), disty); - } - - if (mass1 > 0 && solid == false) - { - mobj2->momx = mobj2->momx - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -distx); - mobj2->momy = mobj2->momy - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -disty); - } - - // Do the bump fx when we've CONFIRMED we can bump. - if ((mobj1->player && mobj1->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) || (mobj2->player && mobj2->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD)) - S_StartSound(mobj1, sfx_s3k44); - else - S_StartSound(mobj1, sfx_s3k49); - - fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP); - if (mobj1->eflags & MFE_VERTICALFLIP) - fx->eflags |= MFE_VERTICALFLIP; - else - fx->eflags &= ~MFE_VERTICALFLIP; - P_SetScale(fx, mobj1->scale); - - // Because this is done during collision now, rmomx and rmomy need to be recalculated - // so that friction doesn't immediately decide to stop the player if they're at a standstill - // Also set justbumped here - if (mobj1->player) - { - mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx; - mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy; - mobj1->player->kartstuff[k_justbumped] = bumptime; - - if (mobj1->player->kartstuff[k_spinouttimer]) - { - mobj1->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; - mobj1->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj1->player->kartstuff[k_spinouttimer]); - //mobj1->player->kartstuff[k_spinouttype] = 1; // Enforce type - } - else if (mobj2->player // Player VS player bumping only - && (K_GetShieldFromItem(mobj1->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields - { - if (mobj1->player->kartstuff[k_rings] <= 0) - { - K_DebtStingPlayer(mobj1->player, TICRATE + (4 * (mobj2->player->kartweight - mobj1->player->kartweight))); - K_KartPainEnergyFling(mobj1->player); - P_PlayRinglossSound(mobj1); - } - P_PlayerRingBurst(mobj1->player, 1); - } - } - - if (mobj2->player) - { - mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx; - mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy; - mobj2->player->kartstuff[k_justbumped] = bumptime; - - if (mobj2->player->kartstuff[k_spinouttimer]) - { - mobj2->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; - mobj2->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj2->player->kartstuff[k_spinouttimer]); - //mobj2->player->kartstuff[k_spinouttype] = 1; // Enforce type - } - else if (mobj1->player // Player VS player bumping only - && (K_GetShieldFromItem(mobj2->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields - { - if (mobj2->player->kartstuff[k_rings] <= 0) - { - K_DebtStingPlayer(mobj2->player, TICRATE + (4 * (mobj1->player->kartweight - mobj2->player->kartweight))); - K_KartPainEnergyFling(mobj2->player); - P_PlayRinglossSound(mobj2); - } - P_PlayerRingBurst(mobj2->player, 1); - } - } -} - -/** \brief Checks that the player is on an offroad subsector for realsies - - \param mo player mobj object - - \return boolean -*/ -static UINT8 K_CheckOffroadCollide(mobj_t *mo) -{ - UINT8 i; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (i = 2; i < 5; i++) - { - if (P_MobjTouchingSectorSpecial(mo, 1, i, true)) - return i-1; - } - - return 0; -} - -/** \brief Updates the Player's offroad value once per frame - - \param player player object passed from K_KartPlayerThink - - \return void -*/ -static void K_UpdateOffroad(player_t *player) -{ - fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); - - // If you are in offroad, a timer starts. - if (offroadstrength) - { - if (player->kartstuff[k_offroad] < offroadstrength) - player->kartstuff[k_offroad] += offroadstrength / TICRATE; - - if (player->kartstuff[k_offroad] > offroadstrength) - player->kartstuff[k_offroad] = offroadstrength; - } - else - player->kartstuff[k_offroad] = 0; -} - -static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent) -{ -#define CHAOTIXBANDLEN 15 -#define CHAOTIXBANDCOLORS 9 - static const UINT8 colors[CHAOTIXBANDCOLORS] = { - SKINCOLOR_SAPPHIRE, - SKINCOLOR_PLATINUM, - SKINCOLOR_TEA, - SKINCOLOR_GARDEN, - SKINCOLOR_BANANA, - SKINCOLOR_GOLD, - SKINCOLOR_ORANGE, - SKINCOLOR_SCARLET, - SKINCOLOR_TAFFY - }; - fixed_t minimumdist = FixedMul(RING_DIST>>1, player->mo->scale); - UINT8 n = CHAOTIXBANDLEN; - UINT8 offset = ((leveltime / 3) % 3); - fixed_t stepx, stepy, stepz; - fixed_t curx, cury, curz; - UINT8 c; - - if (maxdist == 0) - c = 0; - else - c = FixedMul(CHAOTIXBANDCOLORS<> FRACBITS; - - stepx = (victim->mo->x - player->mo->x) / CHAOTIXBANDLEN; - stepy = (victim->mo->y - player->mo->y) / CHAOTIXBANDLEN; - stepz = ((victim->mo->z + (victim->mo->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN; - - curx = player->mo->x + stepx; - cury = player->mo->y + stepy; - curz = player->mo->z + stepz; - - while (n) - { - if (offset == 0) - { - mobj_t *band = P_SpawnMobj(curx + (P_RandomRange(-12,12)*mapobjectscale), - cury + (P_RandomRange(-12,12)*mapobjectscale), - curz + (P_RandomRange(24,48)*mapobjectscale), - MT_SIGNSPARKLE); - - P_SetMobjState(band, S_SIGNSPARK1 + (leveltime % 11)); - P_SetScale(band, (band->destscale = (3*player->mo->scale)/2)); - - band->color = colors[c]; - band->colorized = true; - - band->fuse = 2; - - if (transparent) - band->drawflags |= MFD_SHADOW; - - band->drawflags |= MFD_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim)); - } - - curx += stepx; - cury += stepy; - curz += stepz; - - offset = abs(offset-1) % 3; - n--; - } -#undef CHAOTIXBANDLEN -} - -/** \brief Updates the player's drafting values once per frame - - \param player player object passed from K_KartPlayerThink - - \return void -*/ -static void K_UpdateDraft(player_t *player) -{ - fixed_t topspd = K_GetKartSpeed(player, false); - fixed_t draftdistance; - UINT8 leniency; - UINT8 i; - - if (player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD) - { - // Flame Shield gets infinite draft distance as its passive effect. - draftdistance = 0; - } - else - { - // Distance you have to be to draft. If you're still accelerating, then this distance is lessened. - // This distance biases toward low weight! (min weight gets 4096 units, max weight gets 3072 units) - // This distance is also scaled based on game speed. - draftdistance = (3072 + (128 * (9 - player->kartweight))) * player->mo->scale; - if (player->speed < topspd) - draftdistance = FixedMul(draftdistance, FixedDiv(player->speed, topspd)); - draftdistance = FixedMul(draftdistance, K_GetKartGameSpeedScalar(gamespeed)); - } - - // On the contrary, the leniency period biases toward high weight. - // (See also: the leniency variable in K_SpawnDraftDust) - leniency = (3*TICRATE)/4 + ((player->kartweight-1) * (TICRATE/4)); - - // Not enough speed to draft. - if (player->speed >= 20*player->mo->scale) - { -//#define EASYDRAFTTEST - // Let's hunt for players to draft off of! - for (i = 0; i < MAXPLAYERS; i++) - { - fixed_t dist, olddraft; -#ifndef EASYDRAFTTEST - angle_t yourangle, theirangle, diff; -#endif - - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - -#ifndef EASYDRAFTTEST - // Don't draft on yourself :V - if (&players[i] == player) - continue; -#endif - - // Not enough speed to draft off of. - if (players[i].speed < 20*players[i].mo->scale) - 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); - - diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle; - if (diff > ANGLE_180) - diff = InvAngle(diff); - - // Not in front of this player. - if (diff > ANG10) - continue; - - diff = yourangle - theirangle; - if (diff > ANGLE_180) - diff = InvAngle(diff); - - // Not moving in the same direction. - if (diff > ANGLE_90) - continue; -#endif - - dist = P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, players[i].mo->y - player->mo->y), players[i].mo->z - player->mo->z); - -#ifndef EASYDRAFTTEST - // TOO close to draft. - if (dist < FixedMul(RING_DIST>>1, player->mo->scale)) - continue; - - // Not close enough to draft. - if (dist > draftdistance && draftdistance > 0) - continue; -#endif - - olddraft = player->kartstuff[k_draftpower]; - - player->kartstuff[k_draftleeway] = leniency; - player->kartstuff[k_lastdraft] = i; - - // Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed. - // How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic) - if (player->kartstuff[k_draftpower] < FRACUNIT) - player->kartstuff[k_draftpower] += (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600)); - - if (player->kartstuff[k_draftpower] > FRACUNIT) - player->kartstuff[k_draftpower] = FRACUNIT; - - // Play draft finish noise - if (olddraft < FRACUNIT && player->kartstuff[k_draftpower] >= FRACUNIT) - S_StartSound(player->mo, sfx_cdfm62); - - // Spawn in the visual! - K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false); - - return; // Finished doing our draft. - } - } - - // No one to draft off of? Then you can knock that off. - if (player->kartstuff[k_draftleeway]) // Prevent small disruptions from stopping your draft. - { - player->kartstuff[k_draftleeway]--; - if (player->kartstuff[k_lastdraft] >= 0 - && player->kartstuff[k_lastdraft] < MAXPLAYERS - && playeringame[player->kartstuff[k_lastdraft]] - && !players[player->kartstuff[k_lastdraft]].spectator - && players[player->kartstuff[k_lastdraft]].mo) - { - player_t *victim = &players[player->kartstuff[k_lastdraft]]; - fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z); - K_DrawDraftCombiring(player, victim, dist, draftdistance, true); - } - } - else // Remove draft speed boost. - { - player->kartstuff[k_draftpower] = 0; - player->kartstuff[k_lastdraft] = -1; - } -} - -void K_KartPainEnergyFling(player_t *player) -{ - static const UINT8 numfling = 5; - INT32 i; - mobj_t *mo; - angle_t fa; - fixed_t ns; - fixed_t z; - - // Better safe than sorry. - if (!player) - return; - - // P_PlayerRingBurst: "There's no ring spilling in kart, so I'm hijacking this for the same thing as TD" - // :oh: - - for (i = 0; i < numfling; i++) - { - INT32 objType = mobjinfo[MT_FLINGENERGY].reactiontime; - fixed_t momxy, momz; // base horizonal/vertical thrusts - - z = player->mo->z; - if (player->mo->eflags & MFE_VERTICALFLIP) - z += player->mo->height - mobjinfo[objType].height; - - mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); - - mo->fuse = 8*TICRATE; - P_SetTarget(&mo->target, player->mo); - - mo->destscale = player->mo->scale; - P_SetScale(mo, player->mo->scale); - - // Angle offset by player angle, then slightly offset by amount of fling - fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT) - ((numfling-1)*FINEANGLES/32)) & FINEMASK; - - if (i > 15) - { - momxy = 3*FRACUNIT; - momz = 4*FRACUNIT; - } - else - { - momxy = 28*FRACUNIT; - momz = 3*FRACUNIT; - } - - ns = FixedMul(momxy, mo->scale); - mo->momx = FixedMul(FINECOSINE(fa),ns); - - ns = momz; - P_SetObjectMomZ(mo, ns, false); - - if (i & 1) - P_SetObjectMomZ(mo, ns, true); - - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->momz *= -1; - } -} - -// Adds gravity flipping to an object relative to its master and shifts the z coordinate accordingly. -void K_FlipFromObject(mobj_t *mo, mobj_t *master) -{ - mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); - mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); - - if (mo->eflags & MFE_VERTICALFLIP) - mo->z += master->height - FixedMul(master->scale, mo->height); -} - -void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) -{ - // flipping - // handle z shifting from there too. This is here since there's no reason not to flip us if needed when we do this anyway; - K_FlipFromObject(mo, master); - - // visibility (usually for hyudoro) - mo->drawflags = (master->drawflags & MFD_DONTDRAW); -} - -// same as above, but does not adjust Z height when flipping -void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) -{ - // flipping - mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); - mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); - - // visibility (usually for hyudoro) - mo->drawflags = (master->drawflags & MFD_DONTDRAW); -} - - -void K_SpawnDashDustRelease(player_t *player) -{ - fixed_t newx; - fixed_t newy; - mobj_t *dust; - angle_t travelangle; - INT32 i; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (!P_IsObjectOnGround(player->mo)) - return; - - if (!player->speed && !player->kartstuff[k_startboost]) - return; - - travelangle = player->mo->angle; - - if (player->kartstuff[k_drift] || player->kartstuff[k_driftend]) - travelangle -= (ANGLE_45/5)*player->kartstuff[k_drift]; - - for (i = 0; i < 2; i++) - { - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); - dust = P_SpawnMobj(newx, newy, player->mo->z, MT_FASTDUST); - - P_SetTarget(&dust->target, player->mo); - dust->angle = travelangle - ((i&1) ? -1 : 1)*ANGLE_45; - dust->destscale = player->mo->scale; - P_SetScale(dust, player->mo->scale); - - dust->momx = 3*player->mo->momx/5; - dust->momy = 3*player->mo->momy/5; - //dust->momz = 3*player->mo->momz/5; - - K_MatchGenericExtraFlags(dust, player->mo); - } -} - -static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the mobj thinker case too! -{ - mobj_t *sparks; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - // Position & etc are handled in its thinker, and its spawned invisible. - // This avoids needing to dupe code if we don't need it. - sparks = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BRAKEDRIFT); - P_SetTarget(&sparks->target, player->mo); - P_SetScale(sparks, (sparks->destscale = player->mo->scale)); - K_MatchGenericExtraFlags(sparks, player->mo); - sparks->drawflags |= MFD_DONTDRAW; -} - -static fixed_t K_RandomFlip(fixed_t f) -{ - return ( ( leveltime & 1 ) ? f : -f ); -} - -void K_SpawnDriftBoostClip(player_t *player) -{ - mobj_t *clip; - fixed_t scale = 115*FRACUNIT/100; - fixed_t z; - - if (( player->mo->eflags & MFE_VERTICALFLIP )) - z = player->mo->z; - else - z = player->mo->z + player->mo->height; - - clip = P_SpawnMobj(player->mo->x, player->mo->y, z, MT_DRIFTCLIP); - - P_SetTarget(&clip->target, player->mo); - P_SetScale(clip, ( clip->destscale = FixedMul(scale, player->mo->scale) )); - K_MatchGenericExtraFlags(clip, player->mo); - - clip->fuse = 105; - clip->momz = 7 * P_MobjFlip(clip) * clip->scale; - - P_InstaThrust(clip, player->mo->angle + - K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), - FixedMul(scale, player->speed)); -} - -void K_SpawnDriftBoostClipSpark(mobj_t *clip) -{ - mobj_t *spark; - - spark = P_SpawnMobj(clip->x, clip->y, clip->z, MT_DRIFTCLIPSPARK); - - P_SetTarget(&spark->target, clip); - P_SetScale(spark, ( spark->destscale = clip->scale )); - K_MatchGenericExtraFlags(spark, clip); - - spark->momx = clip->momx/2; - spark->momy = clip->momx/2; -} - -/** \brief Handles the state changing for moving players, moved here to eliminate duplicate code - - \param player player data - - \return void -*/ -void K_KartMoveAnimation(player_t *player) -{ - const INT16 minturn = KART_FULLTURN/8; - SINT8 turndir = 0; - - const fixed_t fastspeed = (K_GetKartSpeed(player, false) * 17) / 20; // 85% - const fixed_t speedthreshold = player->mo->scale / 8; - - const boolean onground = P_IsObjectOnGround(player->mo); - - ticcmd_t *cmd = &player->cmd; - const boolean spinningwheels = ((cmd->buttons & BT_ACCELERATE) || (onground && player->speed > 0)); - - if (cmd->driftturn < -minturn) - { - turndir = -1; - } - else if (cmd->driftturn > minturn) - { - turndir = 1; - } - - if (!onground) - { - // Only use certain frames in the air, to make it look like your tires are spinning fruitlessly! - - if (player->kartstuff[k_drift] > 0) - { - if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); - } - } - else if (player->kartstuff[k_drift] > 0) - { - if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); - } - } - else - { - if ((turndir == -1) - && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R]))) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST2_R); - } - else if ((turndir == 1) - && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L]))) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST2_L); - } - else if ((turndir == 0) - && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2]))) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST2); - } - } - } - else - { - if (player->kartstuff[k_drift] > 0) - { - // Drifting LEFT! - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_DRIFT1_L_OUT] && player->mo->state <= &states[S_KART_DRIFT2_L_OUT])) - { - // Right -- outwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_OUT); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_DRIFT1_L_IN] && player->mo->state <= &states[S_KART_DRIFT4_L_IN])) - { - // Left -- inwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_IN); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); - } - } - else if (player->kartstuff[k_drift] < 0) - { - // Drifting RIGHT! - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_DRIFT1_R_IN] && player->mo->state <= &states[S_KART_DRIFT4_R_IN])) - { - // Right -- inwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_IN); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_DRIFT1_R_OUT] && player->mo->state <= &states[S_KART_DRIFT2_R_OUT])) - { - // Left -- outwards drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_OUT); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) - { - // Neutral drift - P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); - } - } - else - { - if (player->speed >= fastspeed && player->speed >= (player->lastspeed - speedthreshold)) - { - // Going REAL fast! - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R])) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST1_R); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L])) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST1_L); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2])) - { - P_SetPlayerMobjState(player->mo, S_KART_FAST1); - } - } - else - { - if (spinningwheels) - { - // Drivin' slow. - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_SLOW1_R] && player->mo->state <= &states[S_KART_SLOW2_R])) - { - P_SetPlayerMobjState(player->mo, S_KART_SLOW1_R); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_SLOW1_L] && player->mo->state <= &states[S_KART_SLOW2_L])) - { - P_SetPlayerMobjState(player->mo, S_KART_SLOW1_L); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_SLOW1] && player->mo->state <= &states[S_KART_SLOW2])) - { - P_SetPlayerMobjState(player->mo, S_KART_SLOW1); - } - } - else - { - // Completely still. - - if ((turndir == -1) - && !(player->mo->state >= &states[S_KART_STILL1_R] && player->mo->state <= &states[S_KART_STILL2_R])) - { - P_SetPlayerMobjState(player->mo, S_KART_STILL1_R); - } - else if ((turndir == 1) - && !(player->mo->state >= &states[S_KART_STILL1_L] && player->mo->state <= &states[S_KART_STILL2_L])) - { - P_SetPlayerMobjState(player->mo, S_KART_STILL1_L); - } - else if ((turndir == 0) - && !(player->mo->state >= &states[S_KART_STILL1] && player->mo->state <= &states[S_KART_STILL2])) - { - P_SetPlayerMobjState(player->mo, S_KART_STILL1); - } - } - } - } - } - - // Update lastspeed value -- we use to display slow driving frames instead of fast driving when slowing down. - player->lastspeed = player->speed; -} - -static void K_TauntVoiceTimers(player_t *player) -{ - if (!player) - return; - - player->karthud[khud_tauntvoices] = 6*TICRATE; - player->karthud[khud_voices] = 4*TICRATE; -} - -static void K_RegularVoiceTimers(player_t *player) -{ - if (!player) - return; - - player->karthud[khud_voices] = 4*TICRATE; - - if (player->karthud[khud_tauntvoices] < 4*TICRATE) - player->karthud[khud_tauntvoices] = 4*TICRATE; -} - -void K_PlayAttackTaunt(mobj_t *source) -{ - sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); - - if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) - S_StartSound(source, sfx_kattk1+pick); - - if (!tasteful) - return; - - K_TauntVoiceTimers(source->player); -} - -void K_PlayBoostTaunt(mobj_t *source) -{ - sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); - - if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) - S_StartSound(source, sfx_kbost1+pick); - - if (!tasteful) - return; - - K_TauntVoiceTimers(source->player); -} - -void K_PlayOvertakeSound(mobj_t *source) -{ - boolean tasteful = (!source->player || !source->player->karthud[khud_voices]); - - if (!G_RaceGametype()) // Only in race - return; - - // 4 seconds from before race begins, 10 seconds afterwards - if (leveltime < starttime+(10*TICRATE)) - return; - - if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) - S_StartSound(source, sfx_kslow); - - if (!tasteful) - return; - - K_RegularVoiceTimers(source->player); -} - -void K_PlayPainSound(mobj_t *source) -{ - sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - - if (cv_kartvoices.value) - S_StartSound(source, sfx_khurt1 + pick); - - K_RegularVoiceTimers(source->player); -} - -void K_PlayHitEmSound(mobj_t *source) -{ - - if (source->player->follower) - { - follower_t fl = followers[source->player->followerskin]; - source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. - } - - if (cv_kartvoices.value) - S_StartSound(source, sfx_khitem); - else - S_StartSound(source, sfx_s1c9); // The only lost gameplay functionality with voices disabled - - K_RegularVoiceTimers(source->player); -} - -void K_PlayPowerGloatSound(mobj_t *source) -{ - if (cv_kartvoices.value) - S_StartSound(source, sfx_kgloat); - - K_RegularVoiceTimers(source->player); -} - -void K_MomentumToFacing(player_t *player) -{ - angle_t dangle = player->mo->angle - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - - if (dangle > ANGLE_180) - dangle = InvAngle(dangle); - - // If you aren't on the ground or are moving in too different of a direction don't do this - if (player->mo->eflags & MFE_JUSTHITFLOOR) - ; // Just hit floor ALWAYS redirects - else if (!P_IsObjectOnGround(player->mo) || dangle > ANGLE_90) - return; - - P_Thrust(player->mo, player->mo->angle, player->speed - FixedMul(player->speed, player->mo->friction)); - player->mo->momx = FixedMul(player->mo->momx - player->cmomx, player->mo->friction) + player->cmomx; - player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy; -} - -boolean K_ApplyOffroad(player_t *player) -{ - if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer]) - return false; - return true; -} - -static fixed_t K_FlameShieldDashVar(INT32 val) -{ - // 1 second = 75% + 50% top speed - return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); -} - -// sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be -static void K_GetKartBoostPower(player_t *player) -{ - fixed_t boostpower = FRACUNIT; - fixed_t speedboost = 0, accelboost = 0; - UINT8 numboosts = 0; - - if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow] == 1) // Slow down after you've been bumped - { - player->kartstuff[k_boostpower] = player->kartstuff[k_speedboost] = player->kartstuff[k_accelboost] = 0; - return; - } - - // Offroad is separate, it's difficult to factor it in with a variable value anyway. - if (K_ApplyOffroad(player) && player->kartstuff[k_offroad] >= 0) - boostpower = FixedDiv(boostpower, FixedMul(player->kartstuff[k_offroad], K_GetKartGameSpeedScalar(gamespeed)) + FRACUNIT); - - if (player->kartstuff[k_bananadrag] > TICRATE) - boostpower = (4*boostpower)/5; - -#define ADDBOOST(s,a) { \ - numboosts++; \ - speedboost += (s) / numboosts; \ - accelboost += (a) / numboosts; \ -} - - if (player->kartstuff[k_sneakertimer]) // Sneaker - { - UINT8 i; - for (i = 0; i < player->kartstuff[k_numsneakers]; i++) - { - ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration - } - } - - if (player->kartstuff[k_invincibilitytimer]) // Invincibility - { - ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration - } - - if (player->kartstuff[k_flamedash]) // Flame Shield dash - { - ADDBOOST(K_FlameShieldDashVar(player->kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration - } - - if (player->kartstuff[k_startboost]) // Startup Boost - { - ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration - } - - if (player->kartstuff[k_driftboost]) // Drift Boost - { - ADDBOOST(FRACUNIT/4, 4*FRACUNIT); // + 25% top speed, + 400% acceleration - } - - if (player->kartstuff[k_ringboost]) // Ring Boost - { - ADDBOOST(FRACUNIT/5, 4*FRACUNIT); // + 20% top speed, + 400% acceleration - } - - if (player->kartstuff[k_eggmanexplode]) // Ready-to-explode - { - ADDBOOST(3*FRACUNIT/20, FRACUNIT); // + 15% top speed, + 100% acceleration - } - - if (player->kartstuff[k_draftpower] > 0) // Drafting - { - fixed_t draftspeed = ((3*FRACUNIT)/10) + ((player->kartspeed-1) * (FRACUNIT/50)); // min is 30%, max is 46% - speedboost += FixedMul(draftspeed, player->kartstuff[k_draftpower]); // (Drafting suffers no boost stack penalty.) - numboosts++; - } - - player->kartstuff[k_boostpower] = boostpower; - - // value smoothing - if (speedboost > player->kartstuff[k_speedboost]) - { - player->kartstuff[k_speedboost] = speedboost; - } - else - { - player->kartstuff[k_speedboost] += (speedboost - player->kartstuff[k_speedboost]) / (TICRATE/2); - } - - player->kartstuff[k_accelboost] = accelboost; - player->kartstuff[k_numboosts] = numboosts; -} - -// Returns kart speed from a stat. Boost power and scale are NOT taken into account, no player or object is necessary. -fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) -{ - const fixed_t xspd = (3*FRACUNIT)/64; - fixed_t g_cc = K_GetKartGameSpeedScalar(gamespeed) + xspd; - fixed_t k_speed = 150; - fixed_t finalspeed; - - k_speed += kartspeed*3; // 153 - 177 - - finalspeed = FixedMul(k_speed<<14, g_cc); - return finalspeed; -} - -fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) -{ - fixed_t finalspeed; - UINT8 kartspeed = player->kartspeed; - - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - kartspeed = 1; - - finalspeed = K_GetKartSpeedFromStat(kartspeed); - - 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 - - if (player->botvars.rival == true) - { - speedmul += FRACUNIT/10; // +10% for rival - } - - finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul); - } - - if (player->mo && !P_MobjWasRemoved(player->mo)) - finalspeed = FixedMul(finalspeed, player->mo->scale); - - if (doboostpower) - { - if (K_PlayerUsesBotMovement(player)) - { - finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player)); - } - - return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]); - } - - return finalspeed; -} - -fixed_t K_GetKartAccel(player_t *player) -{ - fixed_t k_accel = 32; // 36; - UINT8 kartspeed = player->kartspeed; - - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - kartspeed = 1; - - //k_accel += 3 * (9 - kartspeed); // 36 - 60 - k_accel += 4 * (9 - kartspeed); // 32 - 64 - - - if (K_PlayerUsesBotMovement(player)) - { - // Rubberbanding acceleration is waekened since it makes hits feel more meaningful - fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; - k_accel = FixedMul(k_accel, FRACUNIT + (rubberband/2)); - } - - return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]); -} - -UINT16 K_GetKartFlashing(player_t *player) -{ - UINT16 tics = flashingtics; - - if (!player) - return tics; - - if (G_BattleGametype()) - tics *= 2; - - tics += (flashingtics/8) * (player->kartspeed); - - return tics; -} - -fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove) -{ - const fixed_t accelmax = 4000; - const fixed_t p_speed = K_GetKartSpeed(player, true); - const fixed_t p_accel = K_GetKartAccel(player); - fixed_t newspeed, oldspeed, finalspeed; - fixed_t orig = ORIG_FRICTION; - - if (!onground) return 0; // If the player isn't on the ground, there is no change in speed - - if (K_PlayerUsesBotMovement(player)) - { - orig = K_BotFrictionRubberband(player, ORIG_FRICTION); - } - - // ACCELCODE!!!1!11! - oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale); - // Don't calculate the acceleration as ever being above top speed - if (oldspeed > p_speed) - oldspeed = p_speed; - newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), orig); - - if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust - { - const fixed_t hscale = mapobjectscale /*+ (mapobjectscale - player->mo->scale)*/; - const fixed_t minspeed = 24*hscale; - const fixed_t maxspeed = 28*hscale; - - if (newspeed > maxspeed && player->kartstuff[k_pogospring] == 2) - newspeed = maxspeed; - if (newspeed < minspeed) - newspeed = minspeed; - } - - finalspeed = newspeed - oldspeed; - - // forwardmove is: - // 50 while accelerating, - // 25 while clutching, - // 0 with no gas, and - // -25 when only braking. - - if (player->kartstuff[k_sneakertimer]) - forwardmove = 50; - - finalspeed *= forwardmove/25; - finalspeed /= 2; - - if (forwardmove < 0 && finalspeed > mapobjectscale*2) - return finalspeed/2; - else if (forwardmove < 0) - return -mapobjectscale/2; - - if (finalspeed < 0) - finalspeed = 0; - - return finalspeed; -} - -void K_DoInstashield(player_t *player) -{ - mobj_t *layera; - mobj_t *layerb; - - if (player->kartstuff[k_instashield] > 0) - return; - - player->kartstuff[k_instashield] = 15; // length of instashield animation - S_StartSound(player->mo, sfx_cdpcm9); - - layera = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDA); - P_SetTarget(&layera->target, player->mo); - - layerb = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDB); - P_SetTarget(&layerb->target, player->mo); -} - -void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem) -{ - UINT8 scoremultiply = 1; - // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. -#ifdef HAVE_BLUA - boolean force = false; // Used to check if Lua ShouldSpin should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldSpin(player, inflictor, source); - if (P_MobjWasRemoved(player->mo)) - return; // mobj was removed (in theory that shouldn't happen) - if (shouldForce == 1) - force = true; - else if (shouldForce == 2) - return; -#else - static const boolean force = false; - (void)inflictor; // in case some weirdo doesn't want Lua. -#endif - - if (!trapitem && G_BattleGametype()) - { - if (K_IsPlayerWanted(player)) - scoremultiply = 3; - else if (player->kartstuff[k_bumper] == 1) - scoremultiply = 2; - } - - if (player->health <= 0) - return; - - if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2) - || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - { - if (!force) // if shoulddamage force, we go THROUGH that. - { - K_DoInstashield(player); - return; - } - } - -#ifdef HAVE_BLUA - if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. - return; -#endif - - if (source && source != player->mo && source->player) - K_PlayHitEmSound(source); - - player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_numsneakers] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_ringboost] = 0; - - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - if (G_BattleGametype()) - { - if (source && source->player && player != source->player) - { - P_AddPlayerScore(source->player, scoremultiply); - K_SpawnBattlePoints(source->player, player, scoremultiply); - if (!trapitem) - { - source->player->kartstuff[k_wanted] -= wantedreduce; - player->kartstuff[k_wanted] -= (wantedreduce/2); - } - } - - 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]) - { - 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(); - } - - player->kartstuff[k_spinouttype] = type; - - if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout, type 2 is no-invuln wipeout - { - // At spinout, player speed is increased to 1/4 their regular speed, moving them forward - if (player->speed < K_GetKartSpeed(player, true)/4) - P_InstaThrust(player->mo, player->mo->angle, FixedMul(K_GetKartSpeed(player, true)/4, player->mo->scale)); - S_StartSound(player->mo, sfx_slip); - } - - player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; - player->powers[pw_flashing] = K_GetKartFlashing(player); - - P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 5); - K_PlayPainSound(player->mo); - - if (player->mo->state != &states[S_KART_SPIN]) - P_SetPlayerMobjState(player->mo, S_KART_SPIN); - - player->kartstuff[k_instashield] = 15; - if (cv_kartdebughuddrop.value && !modeattacking) - K_DropItems(player); - else - K_DropHnextList(player, false); - return; -} - -static void K_RemoveGrowShrink(player_t *player) -{ - if (player->mo && !P_MobjWasRemoved(player->mo)) - { - if (player->kartstuff[k_growshrinktimer] > 0) // Play Shrink noise - S_StartSound(player->mo, sfx_kc59); - else if (player->kartstuff[k_growshrinktimer] < 0) // Play Grow noise - S_StartSound(player->mo, sfx_kc5a); - - if (player->kartstuff[k_invincibilitytimer] == 0) - player->mo->color = player->skincolor; - - player->mo->scalespeed = mapobjectscale/TICRATE; - player->mo->destscale = mapobjectscale; - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; - } - - player->kartstuff[k_growshrinktimer] = 0; - - P_RestoreMusic(player); -} - -void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) -{ - UINT8 scoremultiply = 1; - // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. -#ifdef HAVE_BLUA - boolean force = false; // Used to check if Lua ShouldSquish should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldSquish(player, inflictor, source); - if (P_MobjWasRemoved(player->mo)) - return; // mobj was removed (in theory that shouldn't happen) - if (shouldForce == 1) - force = true; - else if (shouldForce == 2) - return; -#else - static const boolean force = false; - (void)inflictor; // Please stop forgetting to put inflictor in yer functions thank -Lat' -#endif - - if (G_BattleGametype()) - { - if (K_IsPlayerWanted(player)) - scoremultiply = 3; - else if (player->kartstuff[k_bumper] == 1) - scoremultiply = 2; - } - - if (player->health <= 0) - return; - - if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_invincibilitytimer] > 0 - || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - { - if (!force) // You know the drill by now. - { - K_DoInstashield(player); - return; - } - } - -#ifdef HAVE_BLUA - if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. - return; -#endif - - player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_numsneakers] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_ringboost] = 0; - - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - if (G_BattleGametype()) - { - if (source && source->player && player != source->player) - { - P_AddPlayerScore(source->player, scoremultiply); - K_SpawnBattlePoints(source->player, player, scoremultiply); - source->player->kartstuff[k_wanted] -= wantedreduce; - player->kartstuff[k_wanted] -= (wantedreduce/2); - } - - 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]) - { - 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(); - } - - player->kartstuff[k_squishedtimer] = TICRATE; - - // Reduce Shrink timer - if (player->kartstuff[k_growshrinktimer] < 0) - { - player->kartstuff[k_growshrinktimer] += TICRATE; - if (player->kartstuff[k_growshrinktimer] >= 0) - K_RemoveGrowShrink(player); - } - - player->powers[pw_flashing] = K_GetKartFlashing(player); - - player->mo->flags |= MF_NOCLIP; - - if (player->mo->state != &states[S_KART_SQUISH]) // Squash - P_SetPlayerMobjState(player->mo, S_KART_SQUISH); - - P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 5); - K_PlayPainSound(player->mo); - - player->kartstuff[k_instashield] = 15; - if (cv_kartdebughuddrop.value && !modeattacking) - K_DropItems(player); - else - K_DropHnextList(player, false); - return; -} - -void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer -{ - UINT8 scoremultiply = 1; -#ifdef HAVE_BLUA - boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what. - UINT8 shouldForce = LUAh_ShouldExplode(player, inflictor, source); - - if (P_MobjWasRemoved(player->mo)) - return; // mobj was removed (in theory that shouldn't happen) - if (shouldForce == 1) - force = true; - else if (shouldForce == 2) - return; - -#else - static const boolean force = false; -#endif - - if (G_BattleGametype()) - { - if (K_IsPlayerWanted(player)) - scoremultiply = 3; - else if (player->kartstuff[k_bumper] == 1) - scoremultiply = 2; - } - - if (player->health <= 0) - return; - - if (player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 // Do not check spinout, because SPB and Eggman should combo - || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) - { - if (!force) // ShouldDamage can bypass that, again. - { - K_DoInstashield(player); - return; - } - } - -#ifdef HAVE_BLUA - if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah - return; -#endif - - if (source && source != player->mo && source->player) - K_PlayHitEmSound(source); - - player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! - player->mo->momx = player->mo->momy = 0; - - player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_numsneakers] = 0; - player->kartstuff[k_driftboost] = 0; - player->kartstuff[k_ringboost] = 0; - - player->kartstuff[k_drift] = 0; - player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_pogospring] = 0; - - // This is the only part that SHOULDN'T combo :VVVVV - if (G_BattleGametype() && !(player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2))) - { - if (source && source->player && player != source->player) - { - P_AddPlayerScore(source->player, scoremultiply); - K_SpawnBattlePoints(source->player, player, scoremultiply); - source->player->kartstuff[k_wanted] -= wantedreduce; - player->kartstuff[k_wanted] -= (wantedreduce/2); - } - - 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]) - { - 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(); - } - - player->kartstuff[k_spinouttype] = 1; - player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; - - player->powers[pw_flashing] = K_GetKartFlashing(player); - - if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1) - { - player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1; - player->mo->momz *= 2; - } - - if (player->mo->eflags & MFE_UNDERWATER) - player->mo->momz = (117 * player->mo->momz) / 200; - - if (player->mo->state != &states[S_KART_SPIN]) - P_SetPlayerMobjState(player->mo, S_KART_SPIN); - - P_PlayRinglossSound(player->mo); - P_PlayerRingBurst(player, 5); - K_PlayPainSound(player->mo); - - if (P_IsDisplayPlayer(player)) - P_StartQuake(64<kartstuff[k_instashield] = 15; - K_DropItems(player); - - return; -} - -void K_StealBumper(player_t *player, player_t *victim, boolean force) -{ - INT32 newbumper; - angle_t newangle, diff; - fixed_t newx, newy; - mobj_t *newmo; - - if (!G_BattleGametype()) - return; - - if (player->health <= 0 || victim->health <= 0) - return; - - if (!force) - { - if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= K_StartingBumperCount()+2 - return; - - if (player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0) - return; - - if (victim->powers[pw_flashing] > 0 || victim->kartstuff[k_squishedtimer] > 0 || (victim->kartstuff[k_spinouttimer] > 0 && victim->kartstuff[k_spinouttype] != 2) - || victim->kartstuff[k_invincibilitytimer] > 0 || victim->kartstuff[k_growshrinktimer] > 0 || victim->kartstuff[k_hyudorotimer] > 0) - { - K_DoInstashield(victim); - return; - } - } - - if (netgame && player->kartstuff[k_bumper] <= 0) - CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); - - newbumper = player->kartstuff[k_bumper]; - 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); - - 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; -} - -// source is the mobj that originally threw the bomb that exploded etc. -// Spawns the sphere around the explosion that handles spinout -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) -{ - mobj_t *mobj; - mobj_t *ghost = NULL; - INT32 i; - TVector v; - TVector *res; - fixed_t finalx, finaly, finalz, dist; - //mobj_t hoopcenter; - angle_t degrees, fa, closestangle; - fixed_t mobjx, mobjy, mobjz; - - //hoopcenter.x = x; - //hoopcenter.y = y; - //hoopcenter.z = z; - - //hoopcenter.z = z - mobjinfo[type].height/2; - - degrees = FINEANGLES/number; - - closestangle = 0; - - // Create the hoop! - for (i = 0; i < number; i++) - { - fa = (i*degrees); - v[0] = FixedMul(FINECOSINE(fa),radius); - v[1] = 0; - v[2] = FixedMul(FINESINE(fa),radius); - v[3] = FRACUNIT; - - res = VectorMatrixMultiply(v, *RotateXMatrix(rotangle)); - M_Memcpy(&v, res, sizeof (v)); - res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle)); - M_Memcpy(&v, res, sizeof (v)); - - finalx = x + v[0]; - finaly = y + v[1]; - finalz = z + v[2]; - - mobj = P_SpawnMobj(finalx, finaly, finalz, type); - - mobj->z -= mobj->height>>1; - - // change angle - mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y); - - // change slope - dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z); - - if (dist < 1) - dist = 1; - - mobjx = mobj->x; - mobjy = mobj->y; - mobjz = mobj->z; - - if (ghostit) - { - ghost = P_SpawnGhostMobj(mobj); - P_SetMobjState(mobj, S_NULL); - mobj = ghost; - } - - if (spawncenter) - { - mobj->x = x; - mobj->y = y; - mobj->z = z; - } - - mobj->momx = FixedMul(FixedDiv(mobjx - x, dist), FixedDiv(dist, 6*FRACUNIT)); - mobj->momy = FixedMul(FixedDiv(mobjy - y, dist), FixedDiv(dist, 6*FRACUNIT)); - mobj->momz = FixedMul(FixedDiv(mobjz - z, dist), FixedDiv(dist, 6*FRACUNIT)); - - if (source && !P_MobjWasRemoved(source)) - P_SetTarget(&mobj->target, source); - } -} - -#define MINEQUAKEDIST 4096 - -// Spawns the purely visual explosion -void K_SpawnMineExplosion(mobj_t *source, UINT8 color) -{ - INT32 i, radius, height; - mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING); - mobj_t *dust; - mobj_t *truc; - INT32 speed, speed2; - INT32 pnum; - player_t *p; - - // check for potential display players near the source so we can have a sick earthquake / flashpal. - for (pnum = 0; pnum < MAXPLAYERS; pnum++) - { - p = &players[pnum]; - - if (!playeringame[pnum] || !P_IsDisplayPlayer(p)) - continue; - - if (R_PointToDist2(p->mo->x, p->mo->y, source->x, source->y) < mapobjectscale*MINEQUAKEDIST) - { - P_StartQuake(55<mo, source)) - { - bombflashtimer = TICRATE*2; - P_FlashPal(p, 1, 1); - } - break; // we can break right now because quakes are global to all split players somehow. - } - } - - K_MatchGenericExtraFlags(smoldering, source); - smoldering->tics = TICRATE*3; - radius = source->radius>>FRACBITS; - height = source->height>>FRACBITS; - - if (!color) - color = SKINCOLOR_KETCHUP; - - for (i = 0; i < 32; i++) - { - dust = P_SpawnMobj(source->x, source->y, source->z, MT_SMOKE); - P_SetMobjState(dust, S_OPAQUESMOKE1); - dust->angle = (ANGLE_180/16) * i; - P_SetScale(dust, source->scale); - dust->destscale = source->scale*10; - dust->scalespeed = source->scale/12; - P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, source->scale)); - - truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, - source->y + P_RandomRange(-radius, radius)*FRACUNIT, - source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMEXPLODE); - K_MatchGenericExtraFlags(truc, source); - P_SetScale(truc, source->scale); - truc->destscale = source->scale*6; - truc->scalespeed = source->scale/12; - speed = FixedMul(10*FRACUNIT, source->scale)>>FRACBITS; - truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; - truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; - speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; - truc->momz = P_RandomRange(-speed, speed)*FRACUNIT*P_MobjFlip(truc); - if (truc->eflags & MFE_UNDERWATER) - truc->momz = (117 * truc->momz) / 200; - truc->color = color; - } - - for (i = 0; i < 16; i++) - { - dust = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, - source->y + P_RandomRange(-radius, radius)*FRACUNIT, - source->z + P_RandomRange(0, height)*FRACUNIT, MT_SMOKE); - P_SetMobjState(dust, S_OPAQUESMOKE1); - P_SetScale(dust, source->scale); - dust->destscale = source->scale*10; - dust->scalespeed = source->scale/12; - dust->tics = 30; - dust->momz = P_RandomRange(FixedMul(3*FRACUNIT, source->scale)>>FRACBITS, FixedMul(7*FRACUNIT, source->scale)>>FRACBITS)*FRACUNIT; - - truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, - source->y + P_RandomRange(-radius, radius)*FRACUNIT, - source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMPARTICLE); - K_MatchGenericExtraFlags(truc, source); - P_SetScale(truc, source->scale); - truc->destscale = source->scale*5; - truc->scalespeed = source->scale/12; - speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; - truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; - truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; - speed = FixedMul(15*FRACUNIT, source->scale)>>FRACBITS; - speed2 = FixedMul(45*FRACUNIT, source->scale)>>FRACBITS; - truc->momz = P_RandomRange(speed, speed2)*FRACUNIT*P_MobjFlip(truc); - if (P_RandomChance(FRACUNIT/2)) - truc->momz = -truc->momz; - if (truc->eflags & MFE_UNDERWATER) - truc->momz = (117 * truc->momz) / 200; - truc->tics = TICRATE*2; - truc->color = color; - } -} - -#undef MINEQUAKEDIST - -static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed) -{ - mobj_t *th; - fixed_t x, y, z; - fixed_t finalspeed = speed; - mobj_t *throwmo; - - if (source->player && source->player->speed > K_GetKartSpeed(source->player, false)) - { - angle_t input = source->angle - an; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - finalspeed = max(speed, FixedMul(speed, FixedMul( - FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed. - (((180<x + source->momx + FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); - y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); - z = source->z; // spawn on the ground please - - if (P_MobjFlip(source) < 0) - { - z = source->z+source->height - mobjinfo[type].height; - } - - th = P_SpawnMobj(x, y, z, type); - - th->flags2 |= flags2; - th->threshold = 10; - - if (th->info->seesound) - S_StartSound(source, th->info->seesound); - - P_SetTarget(&th->target, source); - - P_SetScale(th, source->scale); - th->destscale = source->destscale; - - if (P_IsObjectOnGround(source)) - { - // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn - // This should set it for FOFs - P_TeleportMove(th, th->x, th->y, th->z); - // spawn on the ground if the player is on the ground - if (P_MobjFlip(source) < 0) - { - th->z = th->ceilingz - th->height; - th->eflags |= MFE_VERTICALFLIP; - } - else - th->z = th->floorz; - } - - th->angle = an; - th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); - th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); - - switch (type) - { - case MT_ORBINAUT: - if (source && source->player) - th->color = source->player->skincolor; - else - th->color = SKINCOLOR_GREY; - th->movefactor = finalspeed; - break; - case MT_JAWZ: - if (source && source->player) - { - INT32 lasttarg = source->player->kartstuff[k_lastjawztarget]; - th->cvmem = source->player->skincolor; - if ((lasttarg >= 0 && lasttarg < MAXPLAYERS) - && playeringame[lasttarg] - && !players[lasttarg].spectator - && players[lasttarg].mo) - { - P_SetTarget(&th->tracer, players[lasttarg].mo); - } - } - else - th->cvmem = SKINCOLOR_KETCHUP; - /* FALLTHRU */ - case MT_JAWZ_DUD: - S_StartSound(th, th->info->activesound); - /* FALLTHRU */ - case MT_SPB: - th->movefactor = finalspeed; - break; - case MT_BUBBLESHIELDTRAP: - P_SetScale(th, ((5*th->destscale)>>2)*4); - th->destscale = (5*th->destscale)>>2; - S_StartSound(th, sfx_s3kbfl); - S_StartSound(th, sfx_cdfm35); - break; - default: - break; - } - - if (type != MT_BUBBLESHIELDTRAP) - { - x = x + P_ReturnThrustX(source, an, source->radius + th->radius); - y = y + P_ReturnThrustY(source, an, source->radius + th->radius); - throwmo = P_SpawnMobj(x, y, z, MT_FIREDITEM); - throwmo->movecount = 1; - throwmo->movedir = source->angle - an; - P_SetTarget(&throwmo->target, source); - } - - return NULL; -} - -UINT8 K_DriftSparkColor(player_t *player, INT32 charge) -{ - INT32 ds = K_GetKartDriftSparkValue(player); - UINT8 color = SKINCOLOR_NONE; - - if (charge < 0) - { - // Stage 0: Yellow - color = SKINCOLOR_GOLD; - } - else if (charge >= ds*4) - { - // Stage 3: Rainbow - if (charge <= (ds*4)+(32*3)) - { - // transition - color = SKINCOLOR_SILVER; - } - else - { - color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - } - } - else if (charge >= ds*2) - { - // Stage 2: Blue - if (charge <= (ds*2)+(32*3)) - { - // transition - color = SKINCOLOR_PURPLE; - } - else - { - color = SKINCOLOR_SAPPHIRE; - } - } - else if (charge >= ds) - { - // Stage 1: Red - if (charge <= (ds)+(32*3)) - { - // transition - color = SKINCOLOR_TANGERINE; - } - else - { - color = SKINCOLOR_KETCHUP; - } - } - - return color; -} - -static void K_SpawnDriftSparks(player_t *player) -{ - INT32 ds = K_GetKartDriftSparkValue(player); - fixed_t newx; - fixed_t newy; - mobj_t *spark; - angle_t travelangle; - INT32 i; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (leveltime % 2 == 1) - return; - - if (!player->kartstuff[k_drift] - || (player->kartstuff[k_driftcharge] < ds && !(player->kartstuff[k_driftcharge] < 0))) - return; - - travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; - - for (i = 0; i < 2; i++) - { - SINT8 size = 1; - UINT8 trail = 0; - - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); - spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK); - - P_SetTarget(&spark->target, player->mo); - spark->angle = travelangle-(ANGLE_45/5)*player->kartstuff[k_drift]; - spark->destscale = player->mo->scale; - P_SetScale(spark, player->mo->scale); - - spark->momx = player->mo->momx/2; - spark->momy = player->mo->momy/2; - //spark->momz = player->mo->momz/2; - - spark->color = K_DriftSparkColor(player, player->kartstuff[k_driftcharge]); - - if (player->kartstuff[k_driftcharge] < 0) - { - // Stage 0: Yellow - size = 0; - } - else if (player->kartstuff[k_driftcharge] >= ds*4) - { - // Stage 3: Rainbow - size = 2; - trail = 2; - - if (player->kartstuff[k_driftcharge] <= (ds*4)+(32*3)) - { - // transition - P_SetScale(spark, (spark->destscale = spark->scale*3/2)); - } - else - { - spark->colorized = true; - } - } - else if (player->kartstuff[k_driftcharge] >= ds*2) - { - // Stage 2: Blue - size = 2; - trail = 1; - - if (player->kartstuff[k_driftcharge] <= (ds*2)+(32*3)) - { - // transition - P_SetScale(spark, (spark->destscale = spark->scale*3/2)); - } - } - else - { - // Stage 1: Red - size = 1; - - if (player->kartstuff[k_driftcharge] <= (ds)+(32*3)) - { - // transition - P_SetScale(spark, (spark->destscale = spark->scale*2)); - } - } - - if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts - || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn < 0)) - { - if ((player->kartstuff[k_drift] < 0 && (i & 1)) - || (player->kartstuff[k_drift] > 0 && !(i & 1))) - { - size++; - } - else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) - || (player->kartstuff[k_drift] > 0 && (i & 1))) - { - size--; - } - } - else if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn < 0) // Outward drifts - || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn > 0)) - { - if ((player->kartstuff[k_drift] < 0 && (i & 1)) - || (player->kartstuff[k_drift] > 0 && !(i & 1))) - { - size--; - } - else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) - || (player->kartstuff[k_drift] > 0 && (i & 1))) - { - size++; - } - } - - if (size == 2) - P_SetMobjState(spark, S_DRIFTSPARK_A1); - else if (size < 1) - P_SetMobjState(spark, S_DRIFTSPARK_C1); - else if (size > 2) - P_SetMobjState(spark, S_DRIFTSPARK_D1); - - if (trail > 0) - spark->tics += trail; - - K_MatchGenericExtraFlags(spark, player->mo); - } -} - -static void K_SpawnAIZDust(player_t *player) -{ - fixed_t newx; - fixed_t newy; - mobj_t *spark; - angle_t travelangle; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (leveltime % 2 == 1) - return; - - if (!P_IsObjectOnGround(player->mo)) - return; - - travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - //S_StartSound(player->mo, sfx_s3k47); - - { - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); - spark = P_SpawnMobj(newx, newy, player->mo->z, MT_AIZDRIFTSTRAT); - - spark->angle = travelangle+(player->kartstuff[k_aizdriftstrat]*ANGLE_90); - P_SetScale(spark, (spark->destscale = (3*player->mo->scale)>>2)); - - spark->momx = (6*player->mo->momx)/5; - spark->momy = (6*player->mo->momy)/5; - //spark->momz = player->mo->momz/2; - - K_MatchGenericExtraFlags(spark, player->mo); - } -} - -void K_SpawnBoostTrail(player_t *player) -{ - fixed_t newx; - fixed_t newy; - fixed_t ground; - mobj_t *flame; - angle_t travelangle; - INT32 i; - - I_Assert(player != NULL); - I_Assert(player->mo != NULL); - I_Assert(!P_MobjWasRemoved(player->mo)); - - if (!P_IsObjectOnGround(player->mo) - || player->kartstuff[k_hyudorotimer] != 0 - || (G_BattleGametype() && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer])) - return; - - if (player->mo->eflags & MFE_VERTICALFLIP) - ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); - else - ground = player->mo->floorz; - - if (player->kartstuff[k_drift] != 0) - travelangle = player->mo->angle; - else - travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); - - for (i = 0; i < 2; i++) - { - newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); - newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); -#ifdef ESLOPE - if (player->mo->standingslope) - { - ground = P_GetZAt(player->mo->standingslope, newx, newy); - if (player->mo->eflags & MFE_VERTICALFLIP) - ground -= FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); - } -#endif - flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL); - - P_SetTarget(&flame->target, player->mo); - flame->angle = travelangle; - flame->fuse = TICRATE*2; - flame->destscale = player->mo->scale; - P_SetScale(flame, player->mo->scale); - // not K_MatchGenericExtraFlags so that a stolen sneaker can be seen - K_FlipFromObject(flame, player->mo); - - flame->momx = 8; - P_XYMovement(flame); - if (P_MobjWasRemoved(flame)) - continue; - - if (player->mo->eflags & MFE_VERTICALFLIP) - { - if (flame->z + flame->height < flame->ceilingz) - P_RemoveMobj(flame); - } - else if (flame->z > flame->floorz) - P_RemoveMobj(flame); - } -} - -void K_SpawnSparkleTrail(mobj_t *mo) -{ - const INT32 rad = (mo->radius*2)>>FRACBITS; - mobj_t *sparkle; - INT32 i; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (i = 0; i < 3; i++) - { - fixed_t newx = mo->x + mo->momx + (P_RandomRange(-rad, rad)<y + mo->momy + (P_RandomRange(-rad, rad)<z + mo->momz + (P_RandomRange(0, mo->height>>FRACBITS)<target, mo); - sparkle->destscale = mo->destscale; - P_SetScale(sparkle, mo->scale); - sparkle->color = mo->color; - //sparkle->colorized = mo->colorized; - } - - P_SetMobjState(sparkle, S_KARTINVULN_LARGE1); -} - -void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) -{ - mobj_t *dust; - angle_t aoff; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - if (mo->player) - aoff = (mo->player->frameangle + ANGLE_180); - else - aoff = (mo->angle + ANGLE_180); - - if ((leveltime / 2) & 1) - aoff -= ANGLE_45; - else - aoff += ANGLE_45; - - dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), - mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), - mo->z, MT_WIPEOUTTRAIL); - - P_SetTarget(&dust->target, mo); - dust->angle = R_PointToAngle2(0,0,mo->momx,mo->momy); - dust->destscale = mo->scale; - P_SetScale(dust, mo->scale); - K_FlipFromObject(dust, mo); - - if (translucent) // offroad effect - { - dust->momx = mo->momx/2; - dust->momy = mo->momy/2; - dust->momz = mo->momz/2; - } - - if (translucent) - dust->drawflags |= MFD_SHADOW; -} - -void K_SpawnDraftDust(mobj_t *mo) -{ - UINT8 i; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (i = 0; i < 2; i++) - { - angle_t ang, aoff; - SINT8 sign = 1; - UINT8 foff = 0; - mobj_t *dust; - boolean drifting = false; - - if (mo->player) - { - UINT8 leniency = (3*TICRATE)/4 + ((mo->player->kartweight-1) * (TICRATE/4)); - - ang = mo->player->frameangle; - - if (mo->player->kartstuff[k_drift] != 0) - { - drifting = true; - ang += (mo->player->kartstuff[k_drift] * ((ANGLE_270 + ANGLE_22h) / 5)); // -112.5 doesn't work. I fucking HATE SRB2 angles - if (mo->player->kartstuff[k_drift] < 0) - sign = 1; - else - sign = -1; - } - - foff = 5 - ((mo->player->kartstuff[k_draftleeway] * 5) / leniency); - - // this shouldn't happen - if (foff > 4) - foff = 4; - } - else - ang = mo->angle; - - if (!drifting) - { - if (i & 1) - sign = -1; - else - sign = 1; - } - - aoff = (ang + ANGLE_180) + (ANGLE_45 * sign); - - dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)), - mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)), - mo->z, MT_DRAFTDUST); - - P_SetMobjState(dust, S_DRAFTDUST1 + foff); - - P_SetTarget(&dust->target, mo); - dust->angle = ang - (ANGLE_90 * sign); // point completely perpendicular from the player - dust->destscale = mo->scale; - P_SetScale(dust, mo->scale); - K_FlipFromObject(dust, mo); - - if (leveltime & 1) - dust->tics++; // "randomize" animation - - dust->momx = (4*mo->momx)/5; - dust->momy = (4*mo->momy)/5; - //dust->momz = (4*mo->momz)/5; - - P_Thrust(dust, dust->angle, 4*mo->scale); - - if (drifting) // only 1 trail while drifting - break; - } -} - -// K_DriftDustHandling -// Parameters: -// spawner: The map object that is spawning the drift dust -// Description: Spawns the drift dust for objects, players use rmomx/y, other objects use regular momx/y. -// Also plays the drift sound. -// Other objects should be angled towards where they're trying to go so they don't randomly spawn dust -// Do note that most of the function won't run in odd intervals of frames -void K_DriftDustHandling(mobj_t *spawner) -{ - angle_t anglediff; - const INT16 spawnrange = spawner->radius >> FRACBITS; - - if (!P_IsObjectOnGround(spawner) || leveltime % 2 != 0) - return; - - if (spawner->player) - { - if (spawner->player->pflags & PF_SKIDDOWN) - { - anglediff = abs((signed)(spawner->angle - spawner->player->frameangle)); - if (leveltime % 6 == 0) - S_StartSound(spawner, sfx_screec); // repeated here because it doesn't always happen to be within the range when this is the case - } - else - { - angle_t playerangle = spawner->angle; - - if (spawner->player->speed < 5*spawner->scale) - return; - - if (spawner->player->cmd.forwardmove < 0) - playerangle += ANGLE_180; - - anglediff = abs((signed)(playerangle - R_PointToAngle2(0, 0, spawner->player->rmomx, spawner->player->rmomy))); - } - } - else - { - if (P_AproxDistance(spawner->momx, spawner->momy) < 5*spawner->scale) - return; - - anglediff = abs((signed)(spawner->angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy))); - } - - if (anglediff > ANGLE_180) - anglediff = InvAngle(anglediff); - - if (anglediff > ANG10*4) // Trying to turn further than 40 degrees - { - fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; - fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; - INT32 speedrange = 2; - mobj_t *dust = P_SpawnMobj(spawner->x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST); - dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); - dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); - dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale)); - P_SetScale(dust, spawner->scale/2); - dust->destscale = spawner->scale * 3; - dust->scalespeed = spawner->scale/12; - - if (leveltime % 6 == 0) - S_StartSound(spawner, sfx_screec); - - K_MatchGenericExtraFlags(dust, spawner); - - // Sparkle-y warning for when you're about to change drift sparks! - if (spawner->player && spawner->player->kartstuff[k_drift]) - { - INT32 driftval = K_GetKartDriftSparkValue(spawner->player); - INT32 warntime = driftval/3; - INT32 dc = spawner->player->kartstuff[k_driftcharge]; - UINT8 c = SKINCOLOR_NONE; - boolean rainbow = false; - - if (dc >= 0) - { - dc += warntime; - } - - c = K_DriftSparkColor(spawner->player, dc); - - if (dc > (4*driftval)+(32*3)) - { - rainbow = true; - } - - if (c != SKINCOLOR_NONE) - { - P_SetMobjState(dust, S_DRIFTWARNSPARK1); - dust->color = c; - dust->colorized = rainbow; - } - } - } -} - -static mobj_t *K_FindLastTrailMobj(player_t *player) -{ - mobj_t *trail; - - if (!player || !(trail = player->mo) || !player->mo->hnext || !player->mo->hnext->health) - return NULL; - - while (trail->hnext && !P_MobjWasRemoved(trail->hnext) && trail->hnext->health) - { - trail = trail->hnext; - } - - return trail; -} - -static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow) -{ - mobj_t *mo; - INT32 dir; - fixed_t PROJSPEED; - angle_t newangle; - fixed_t newx, newy, newz; - mobj_t *throwmo; - - if (!player) - return NULL; - - // Figure out projectile speed by game speed - if (missile) - { - // Use info->speed for missiles - PROJSPEED = FixedMul(mobjinfo[mapthing].speed, K_GetKartGameSpeedScalar(gamespeed)); - } - else - { - // Use pre-determined speed for tossing - PROJSPEED = FixedMul(82 << FRACBITS, K_GetKartGameSpeedScalar(gamespeed)); - } - - // Scale to map size - PROJSPEED = FixedMul(PROJSPEED, mapobjectscale); - - if (altthrow) - { - if (altthrow == 2) // Kitchen sink throwing - { -#if 0 - if (player->kartstuff[k_throwdir] == 1) - dir = 3; - else if (player->kartstuff[k_throwdir] == -1) - dir = 1; - else - dir = 2; -#else - if (player->kartstuff[k_throwdir] == 1) - dir = 2; - else - dir = 1; -#endif - } - else - { - if (player->kartstuff[k_throwdir] == 1) - dir = 2; - else if (player->kartstuff[k_throwdir] == -1) - dir = -1; - else - dir = 1; - } - } - else - { - if (player->kartstuff[k_throwdir] != 0) - dir = player->kartstuff[k_throwdir]; - else - dir = defaultDir; - } - - if (missile) // Shootables - { - if (mapthing == MT_BALLHOG) // Messy - { - if (dir == -1) - { - // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x03000000, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x03000000, 0, PROJSPEED/8); - K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x06000000, 0, PROJSPEED/8); - } - else - { - // Shoot forward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED); - K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x06000000, 0, PROJSPEED); - } - } - else - { - if (dir == -1 && mapthing != MT_SPB) - { - // Shoot backward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); - } - else - { - // Shoot forward - mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); - } - } - } - else - { - player->kartstuff[k_bananadrag] = 0; // RESET timer, for multiple bananas - - if (dir > 0) - { - // Shoot forward - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing); - //K_FlipFromObject(mo, player->mo); - // These are really weird so let's make it a very specific case to make SURE it works... - if (player->mo->eflags & MFE_VERTICALFLIP) - { - mo->z -= player->mo->height; - mo->flags2 |= MF2_OBJECTFLIP; - mo->eflags |= MFE_VERTICALFLIP; - } - - mo->threshold = 10; - P_SetTarget(&mo->target, player->mo); - - S_StartSound(player->mo, mo->info->seesound); - - if (mo) - { - angle_t fa = player->mo->angle>>ANGLETOFINESHIFT; - fixed_t HEIGHT = (20 + (dir*10))*FRACUNIT + (player->mo->momz*P_MobjFlip(player->mo)); - - P_SetObjectMomZ(mo, HEIGHT, false); - mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir); - mo->momy = player->mo->momy + FixedMul(FINESINE(fa), PROJSPEED*dir); - - mo->extravalue2 = dir; - - if (mo->eflags & MFE_UNDERWATER) - mo->momz = (117 * mo->momz) / 200; - } - - // this is the small graphic effect that plops in you when you throw an item: - throwmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FIREDITEM); - P_SetTarget(&throwmo->target, player->mo); - // Ditto: - if (player->mo->eflags & MFE_VERTICALFLIP) - { - throwmo->z -= player->mo->height; - throwmo->flags2 |= MF2_OBJECTFLIP; - throwmo->eflags |= MFE_VERTICALFLIP; - } - - throwmo->movecount = 0; // above player - } - else - { - mobj_t *lasttrail = K_FindLastTrailMobj(player); - - if (mapthing == MT_BUBBLESHIELDTRAP) // Drop directly on top of you. - { - newangle = player->mo->angle; - newx = player->mo->x + player->mo->momx; - newy = player->mo->y + player->mo->momy; - newz = player->mo->z; - } - else if (lasttrail) - { - newangle = lasttrail->angle; - newx = lasttrail->x; - newy = lasttrail->y; - newz = lasttrail->z; - } - else - { - // Drop it directly behind you. - fixed_t dropradius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(mobjinfo[mapthing].radius, mobjinfo[mapthing].radius); - - newangle = player->mo->angle; - - newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, dropradius); - newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, dropradius); - newz = player->mo->z; - } - - mo = P_SpawnMobj(newx, newy, newz, mapthing); // this will never return null because collision isn't processed here - K_FlipFromObject(mo, player->mo); - - mo->threshold = 10; - P_SetTarget(&mo->target, player->mo); - - P_SetScale(mo, player->mo->scale); - mo->destscale = player->mo->destscale; - - if (P_IsObjectOnGround(player->mo)) - { - // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn - // This should set it for FOFs - P_TeleportMove(mo, mo->x, mo->y, mo->z); // however, THIS can fuck up your day. just absolutely ruin you. - if (P_MobjWasRemoved(mo)) - return NULL; - - if (P_MobjFlip(mo) > 0) - { - if (mo->floorz > mo->target->z - mo->height) - { - mo->z = mo->floorz; - } - } - else - { - if (mo->ceilingz < mo->target->z + mo->target->height + mo->height) - { - mo->z = mo->ceilingz - mo->height; - } - } - } - - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->eflags |= MFE_VERTICALFLIP; - - if (mapthing == MT_SSMINE) - mo->extravalue1 = 49; // Pads the start-up length from 21 frames to a full 2 seconds - else if (mapthing == MT_BUBBLESHIELDTRAP) - { - P_SetScale(mo, ((5*mo->destscale)>>2)*4); - mo->destscale = (5*mo->destscale)>>2; - S_StartSound(mo, sfx_s3kbfl); - } - } - } - - return mo; -} - -void K_PuntMine(mobj_t *thismine, mobj_t *punter) -{ - angle_t fa = R_PointToAngle2(0, 0, punter->momx, punter->momy) >> ANGLETOFINESHIFT; - fixed_t z = 30*mapobjectscale + punter->momz; - fixed_t spd; - mobj_t *mine; - - if (!thismine || P_MobjWasRemoved(thismine)) - return; - - if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine - { - mine = P_SpawnMobj(thismine->x, thismine->y, thismine->z, MT_SSMINE); - P_SetTarget(&mine->target, thismine->target); - mine->angle = thismine->angle; - mine->flags2 = thismine->flags2; - mine->floorz = thismine->floorz; - mine->ceilingz = thismine->ceilingz; - P_RemoveMobj(thismine); - } - else - mine = thismine; - - if (!mine || P_MobjWasRemoved(mine)) - return; - - spd = (82 + ((gamespeed-1) * 14))*mapobjectscale; // Avg Speed is 41 in Normal - - mine->flags |= MF_NOCLIPTHING; - - P_SetMobjState(mine, S_SSMINE_AIR1); - mine->threshold = 10; - mine->extravalue1 = 0; - mine->reactiontime = mine->info->reactiontime; - - mine->momx = punter->momx + FixedMul(FINECOSINE(fa), spd); - mine->momy = punter->momy + FixedMul(FINESINE(fa), spd); - mine->momz = P_MobjFlip(mine) * z; - - mine->flags &= ~MF_NOCLIPTHING; -} - -#define THUNDERRADIUS 320 - -static void K_DoThunderShield(player_t *player) -{ - mobj_t *mo; - int i = 0; - fixed_t sx; - fixed_t sy; - angle_t an; - - S_StartSound(player->mo, sfx_zio3); - P_NukeEnemies(player->mo, player->mo, RING_DIST/4); - - // spawn vertical bolt - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_LZIO11); - mo->color = SKINCOLOR_TEAL; - mo->scale = player->mo->scale*3 + (player->mo->scale/2); - - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_LZIO21); - mo->color = SKINCOLOR_CYAN; - mo->scale = player->mo->scale*3 + (player->mo->scale/2); - - // spawn horizontal bolts; - for (i=0; i<7; i++) - { - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); - mo->angle = P_RandomRange(0, 359)*ANG1; - mo->fuse = P_RandomRange(20, 50); - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_KLIT1); - } - - // spawn the radius thing: - an = ANGLE_22h; - for (i=0; i<15; i++) - { - sx = player->mo->x + FixedMul((player->mo->scale*THUNDERRADIUS), FINECOSINE((an*i)>>ANGLETOFINESHIFT)); - sy = player->mo->y + FixedMul((player->mo->scale*THUNDERRADIUS), FINESINE((an*i)>>ANGLETOFINESHIFT)); - mo = P_SpawnMobj(sx, sy, player->mo->z, MT_THOK); - mo-> angle = an*i; - mo->extravalue1 = THUNDERRADIUS; // Used to know whether we should teleport by radius or something. - mo->scale = player->mo->scale*3; - P_SetTarget(&mo->target, player->mo); - P_SetMobjState(mo, S_KSPARK1); - } -} - -#undef THUNDERRADIUS - -static void K_FlameDashLeftoverSmoke(mobj_t *src) -{ - UINT8 i; - - for (i = 0; i < 2; i++) - { - mobj_t *smoke = P_SpawnMobj(src->x, src->y, src->z+(8<scale); - smoke->destscale = 3*src->scale/2; - smoke->scalespeed = src->scale/12; - - smoke->momx = 3*src->momx/4; - smoke->momy = 3*src->momy/4; - smoke->momz = 3*src->momz/4; - - P_Thrust(smoke, src->angle + FixedAngle(P_RandomRange(135, 225)<scale); - smoke->momz += P_RandomRange(0, 4) * src->scale; - } -} - -static void K_DoHyudoroSteal(player_t *player) -{ - INT32 i, numplayers = 0; - INT32 playerswappable[MAXPLAYERS]; - INT32 stealplayer = -1; // The player that's getting stolen from - INT32 prandom = 0; - boolean sink = P_RandomChance(FRACUNIT/64); - INT32 hyu = hyudorotime; - - if (G_RaceGametype()) - hyu *= 2; // double in race - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE - && player != &players[i] && !players[i].exiting && !players[i].spectator // Player in-game - - // Can steal from this player - && (G_RaceGametype() //&& players[i].kartstuff[k_position] < player->kartstuff[k_position]) - || (G_BattleGametype() && players[i].kartstuff[k_bumper] > 0)) - - // Has an item - && (players[i].kartstuff[k_itemtype] - && players[i].kartstuff[k_itemamount] - && !players[i].kartstuff[k_itemheld] - && !players[i].karthud[khud_itemblink])) - { - playerswappable[numplayers] = i; - numplayers++; - } - } - - prandom = P_RandomFixed(); - S_StartSound(player->mo, sfx_s3k92); - - if (sink && numplayers > 0 && cv_kitchensink.value) // BEHOLD THE KITCHEN SINK - { - player->kartstuff[k_hyudorotimer] = hyu; - player->kartstuff[k_stealingtimer] = stealtime; - - player->kartstuff[k_itemtype] = KITEM_KITCHENSINK; - player->kartstuff[k_itemamount] = 1; - player->kartstuff[k_itemheld] = 0; - return; - } - else if ((G_RaceGametype() && player->kartstuff[k_position] == 1) || numplayers == 0) // No-one can be stolen from? Oh well... - { - player->kartstuff[k_hyudorotimer] = hyu; - player->kartstuff[k_stealingtimer] = stealtime; - return; - } - else if (numplayers == 1) // With just 2 players, we just need to set the other player to be the one to steal from - { - stealplayer = playerswappable[numplayers-1]; - } - else if (numplayers > 1) // We need to choose between the available candidates for the 2nd player - { - stealplayer = playerswappable[prandom%(numplayers-1)]; - } - - if (stealplayer > -1) // Now here's where we do the stealing, has to be done here because we still know the player we're stealing from - { - player->kartstuff[k_hyudorotimer] = hyu; - player->kartstuff[k_stealingtimer] = stealtime; - players[stealplayer].kartstuff[k_stolentimer] = stealtime; - - player->kartstuff[k_itemtype] = players[stealplayer].kartstuff[k_itemtype]; - player->kartstuff[k_itemamount] = players[stealplayer].kartstuff[k_itemamount]; - player->kartstuff[k_itemheld] = 0; - - players[stealplayer].kartstuff[k_itemtype] = KITEM_NONE; - players[stealplayer].kartstuff[k_itemamount] = 0; - players[stealplayer].kartstuff[k_itemheld] = 0; - - if (P_IsDisplayPlayer(&players[stealplayer]) && !r_splitscreen) - S_StartSound(NULL, sfx_s3k92); - } -} - -void K_DoSneaker(player_t *player, INT32 type) -{ - const fixed_t intendedboost = FRACUNIT/2; - - if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) - { - const sfxenum_t normalsfx = sfx_cdfm01; - const sfxenum_t smallsfx = sfx_cdfm40; - sfxenum_t sfx = normalsfx; - - if (player->kartstuff[k_numsneakers]) - { - // Use a less annoying sound when stacking sneakers. - sfx = smallsfx; - } - - S_StopSoundByID(player->mo, normalsfx); - S_StopSoundByID(player->mo, smallsfx); - S_StartSound(player->mo, sfx); - - K_SpawnDashDustRelease(player); - if (intendedboost > player->kartstuff[k_speedboost]) - player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); - - player->kartstuff[k_numsneakers]++; - } - - if (!player->kartstuff[k_sneakertimer]) - { - if (type == 2) - { - if (player->mo->hnext) - { - mobj_t *cur = player->mo->hnext; - while (cur && !P_MobjWasRemoved(cur)) - { - if (!cur->tracer) - { - mobj_t *overlay = P_SpawnMobj(cur->x, cur->y, cur->z, MT_BOOSTFLAME); - P_SetTarget(&overlay->target, cur); - P_SetTarget(&cur->tracer, overlay); - P_SetScale(overlay, (overlay->destscale = 3*cur->scale/4)); - K_FlipFromObject(overlay, cur); - } - cur = cur->hnext; - } - } - } - else - { - mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BOOSTFLAME); - P_SetTarget(&overlay->target, player->mo); - P_SetScale(overlay, (overlay->destscale = player->mo->scale)); - K_FlipFromObject(overlay, player->mo); - } - } - - if (type != 0) - { - player->pflags |= PF_ATTACKDOWN; - K_PlayBoostTaunt(player->mo); - - } - - player->kartstuff[k_sneakertimer] = sneakertime; - - // set angle for spun out players: - player->kartstuff[k_boostangle] = (INT32)player->mo->angle; -} - -static void K_DoShrink(player_t *user) -{ - INT32 i; - mobj_t *mobj, *next; - - S_StartSound(user->mo, sfx_kc46); // Sound the BANG! - user->pflags |= PF_ATTACKDOWN; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - if (&players[i] == user) - continue; - if (players[i].kartstuff[k_position] < user->kartstuff[k_position]) - { - //P_FlashPal(&players[i], PAL_NUKE, 10); - - // Grow should get taken away. - if (players[i].kartstuff[k_growshrinktimer] > 0) - K_RemoveGrowShrink(&players[i]); - else - { - // Start shrinking! - K_DropItems(&players[i]); - players[i].kartstuff[k_growshrinktimer] = -(15*TICRATE); - - if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) - { - players[i].mo->scalespeed = mapobjectscale/TICRATE; - players[i].mo->destscale = (6*mapobjectscale)/8; - if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot) - players[i].mo->destscale = (6*players[i].mo->destscale)/8; - S_StartSound(players[i].mo, sfx_kc59); - } - } - } - } - - // kill everything in the kitem list while we're at it: - for (mobj = kitemcap; mobj; mobj = next) - { - next = mobj->itnext; - - // check if the item is being held by a player behind us before removing it. - // check if the item is a "shield" first, bc i'm p sure thrown items keep the player that threw em as target anyway - - if (mobj->type == MT_BANANA_SHIELD || mobj->type == MT_JAWZ_SHIELD || - mobj->type == MT_SSMINE_SHIELD || mobj->type == MT_EGGMANITEM_SHIELD || - mobj->type == MT_SINK_SHIELD || mobj->type == MT_ORBINAUT_SHIELD) - { - if (mobj->target && mobj->target->player) - { - if (mobj->target->player->kartstuff[k_position] > user->kartstuff[k_position]) - continue; // this guy's behind us, don't take his stuff away! - } - } - - mobj->destscale = 0; - mobj->flags &= ~(MF_SOLID|MF_SHOOTABLE|MF_SPECIAL); - mobj->flags |= MF_NOCLIPTHING; // Just for safety - - if (mobj->type == MT_SPB) - spbplace = -1; - } -} - - -void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) -{ - const fixed_t vscale = mapobjectscale + (mo->scale - mapobjectscale); - - if (mo->player && mo->player->spectator) - return; - - if (mo->eflags & MFE_SPRUNG) - return; - -#ifdef ESLOPE - mo->standingslope = NULL; -#endif - - mo->eflags |= MFE_SPRUNG; - - if (mo->eflags & MFE_VERTICALFLIP) - vertispeed *= -1; - - if (vertispeed == 0) - { - fixed_t thrust; - - if (mo->player) - { - thrust = 3*mo->player->speed/2; - if (thrust < 48< 72<player->kartstuff[k_pogospring] != 2) - { - if (mo->player->kartstuff[k_sneakertimer]) - thrust = FixedMul(thrust, (5*FRACUNIT)/4); - else if (mo->player->kartstuff[k_invincibilitytimer]) - thrust = FixedMul(thrust, (9*FRACUNIT)/8); - } - } - else - { - thrust = FixedDiv(3*P_AproxDistance(mo->momx, mo->momy)/2, 5*FRACUNIT/2); - if (thrust < 16< 32<momz = P_MobjFlip(mo)*FixedMul(FINESINE(ANGLE_22h>>ANGLETOFINESHIFT), FixedMul(thrust, vscale)); - } - else - mo->momz = FixedMul(vertispeed, vscale); - - if (mo->eflags & MFE_UNDERWATER) - mo->momz = (117 * mo->momz) / 200; - - if (sound) - S_StartSound(mo, (sound == 1 ? sfx_kc2f : sfx_kpogos)); -} - -void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source) -{ - mobj_t *cachenext; - -killnext: - cachenext = banana->hnext; - - if (banana->health) - { - if (banana->eflags & MFE_VERTICALFLIP) - banana->z -= banana->height; - else - banana->z += banana->height; - - S_StartSound(banana, banana->info->deathsound); - P_KillMobj(banana, inflictor, source); - - P_SetObjectMomZ(banana, 8*FRACUNIT, false); - if (inflictor) - P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT); - } - - if ((banana = cachenext)) - goto killnext; -} - -// Just for firing/dropping items. -void K_UpdateHnextList(player_t *player, boolean clean) -{ - mobj_t *work = player->mo, *nextwork; - - if (!work) - return; - - nextwork = work->hnext; - - while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) - { - nextwork = work->hnext; - - if (!clean && (!work->movedir || work->movedir <= (UINT16)player->kartstuff[k_itemamount])) - { - continue; - } - - P_RemoveMobj(work); - } - - if (player->mo->hnext == NULL || P_MobjWasRemoved(player->mo->hnext)) - { - // Like below, try to clean up the pointer if it's NULL. - // Maybe this was a cause of the shrink/eggbox fails? - P_SetTarget(&player->mo->hnext, NULL); - } -} - -// For getting hit! -void K_DropHnextList(player_t *player, boolean keepshields) -{ - mobj_t *work = player->mo, *nextwork, *dropwork; - INT32 flip; - mobjtype_t type; - boolean orbit, ponground, dropall = true; - INT32 shield = K_GetShieldFromItem(player->kartstuff[k_itemtype]); - - if (work == NULL || P_MobjWasRemoved(work)) - { - return; - } - - flip = P_MobjFlip(player->mo); - ponground = P_IsObjectOnGround(player->mo); - - if (shield != KSHIELD_NONE && !keepshields) - { - if (shield == KSHIELD_THUNDER) - { - K_DoThunderShield(player); - } - - player->kartstuff[k_curshield] = KSHIELD_NONE; - player->kartstuff[k_itemtype] = KITEM_NONE; - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - } - - nextwork = work->hnext; - - while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) - { - nextwork = work->hnext; - - switch (work->type) - { - // Kart orbit items - case MT_ORBINAUT_SHIELD: - orbit = true; - type = MT_ORBINAUT; - break; - case MT_JAWZ_SHIELD: - orbit = true; - type = MT_JAWZ_DUD; - break; - // Kart trailing items - case MT_BANANA_SHIELD: - orbit = false; - type = MT_BANANA; - break; - case MT_SSMINE_SHIELD: - orbit = false; - dropall = false; - type = MT_SSMINE; - break; - case MT_EGGMANITEM_SHIELD: - orbit = false; - type = MT_EGGMANITEM; - break; - // intentionally do nothing - case MT_ROCKETSNEAKER: - case MT_SINK_SHIELD: - return; - default: - continue; - } - - dropwork = P_SpawnMobj(work->x, work->y, work->z, type); - - P_SetTarget(&dropwork->target, player->mo); - P_AddKartItem(dropwork); // needs to be called here so shrink can bust items off players in front of the user. - - dropwork->angle = work->angle; - - dropwork->flags |= MF_NOCLIPTHING; - dropwork->flags2 = work->flags2; - - dropwork->floorz = work->floorz; - dropwork->ceilingz = work->ceilingz; - - if (ponground) - { - // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn - // This should set it for FOFs - //P_TeleportMove(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing - - if (flip == 1) - { - if (dropwork->floorz > dropwork->target->z - dropwork->height) - { - dropwork->z = dropwork->floorz; - } - } - else - { - if (dropwork->ceilingz < dropwork->target->z + dropwork->target->height + dropwork->height) - { - dropwork->z = dropwork->ceilingz - dropwork->height; - } - } - } - - if (orbit) // splay out - { - dropwork->flags2 |= MF2_AMBUSH; - - dropwork->z += flip; - - dropwork->momx = player->mo->momx>>1; - dropwork->momy = player->mo->momy>>1; - dropwork->momz = 3*flip*mapobjectscale; - - if (dropwork->eflags & MFE_UNDERWATER) - dropwork->momz = (117 * dropwork->momz) / 200; - - P_Thrust(dropwork, work->angle - ANGLE_90, 6*mapobjectscale); - - dropwork->movecount = 2; - dropwork->movedir = work->angle - ANGLE_90; - - P_SetMobjState(dropwork, dropwork->info->deathstate); - - dropwork->tics = -1; - - if (type == MT_JAWZ_DUD) - { - dropwork->z += 20*flip*dropwork->scale; - } - else - { - dropwork->color = work->color; - dropwork->angle -= ANGLE_90; - } - } - else // plop on the ground - { - dropwork->flags &= ~MF_NOCLIPTHING; - dropwork->threshold = 10; - } - - P_RemoveMobj(work); - } - - // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... - P_SetTarget(&player->mo->hnext, NULL); - - player->kartstuff[k_bananadrag] = 0; - - if (player->kartstuff[k_eggmanheld]) - { - player->kartstuff[k_eggmanheld] = 0; - } - else if (player->kartstuff[k_itemheld] - && (dropall || (--player->kartstuff[k_itemamount] <= 0))) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } -} - -// For getting EXTRA hit! -void K_DropItems(player_t *player) -{ - K_DropHnextList(player, true); - - 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]; - - drop->flags |= MF_NOCLIPTHING; - } - - K_StripItems(player); -} - -// When an item in the hnext chain dies. -void K_RepairOrbitChain(mobj_t *orbit) -{ - mobj_t *cachenext = orbit->hnext; - - // First, repair the chain - if (orbit->hnext && orbit->hnext->health && !P_MobjWasRemoved(orbit->hnext)) - { - P_SetTarget(&orbit->hnext->hprev, orbit->hprev); - P_SetTarget(&orbit->hnext, NULL); - } - - if (orbit->hprev && orbit->hprev->health && !P_MobjWasRemoved(orbit->hprev)) - { - P_SetTarget(&orbit->hprev->hnext, cachenext); - P_SetTarget(&orbit->hprev, NULL); - } - - // Then recount to make sure item amount is correct - if (orbit->target && orbit->target->player) - { - INT32 num = 0; - - mobj_t *cur = orbit->target->hnext; - mobj_t *prev = NULL; - - while (cur && !P_MobjWasRemoved(cur)) - { - prev = cur; - cur = cur->hnext; - if (++num > orbit->target->player->kartstuff[k_itemamount]) - P_RemoveMobj(prev); - else - prev->movedir = num; - } - - if (orbit->target->player->kartstuff[k_itemamount] != num) - orbit->target->player->kartstuff[k_itemamount] = num; - } -} - -// Simplified version of a code bit in P_MobjFloorZ -static fixed_t K_BananaSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) -{ - fixed_t testx, testy; - - if (slope->d.x < 0) - testx = radius; - else - testx = -radius; - - if (slope->d.y < 0) - testy = radius; - else - testy = -radius; - - if ((slope->zdelta > 0) ^ !!(ceiling)) - { - testx = -testx; - testy = -testy; - } - - testx += x; - testy += y; - - return P_GetZAt(slope, testx, testy); -} - -static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) -{ - fixed_t newz; - sector_t *sec; -#ifdef ESLOPE - pslope_t *slope = NULL; -#endif - - sec = R_PointInSubsector(x, y)->sector; - - if (flip) - { -#ifdef ESLOPE - if (sec->c_slope) - { - slope = sec->c_slope; - newz = K_BananaSlopeZ(slope, x, y, radius, true); - } - else -#endif - newz = sec->ceilingheight; - } - else - { -#ifdef ESLOPE - if (sec->f_slope) - { - slope = sec->f_slope; - newz = K_BananaSlopeZ(slope, x, y, radius, false); - } - else -#endif - newz = sec->floorheight; - } - - // Check FOFs for a better suited slope - if (sec->ffloors) - { - ffloor_t *rover; - - for (rover = sec->ffloors; rover; rover = rover->next) - { - fixed_t top, bottom; - fixed_t d1, d2; - - if (!(rover->flags & FF_EXISTS)) - continue; - - if ((!(((rover->flags & FF_BLOCKPLAYER && player) - || (rover->flags & FF_BLOCKOTHERS && !player)) - || (rover->flags & FF_QUICKSAND)) - || (rover->flags & FF_SWIMMABLE))) - continue; - -#ifdef ESLOPE - if (*rover->t_slope) - top = K_BananaSlopeZ(*rover->t_slope, x, y, radius, false); - else -#endif - top = *rover->topheight; - -#ifdef ESLOPE - if (*rover->b_slope) - bottom = K_BananaSlopeZ(*rover->b_slope, x, y, radius, true); - else -#endif - bottom = *rover->bottomheight; - - if (flip) - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz > (z + height)) - { - newz = (z + height); - slope = NULL; - } - } - continue; - } - - d1 = (z + height) - (top + ((bottom - top)/2)); - d2 = z - (top + ((bottom - top)/2)); - - if (bottom < newz && abs(d1) < abs(d2)) - { - newz = bottom; -#ifdef ESLOPE - if (*rover->b_slope) - slope = *rover->b_slope; -#endif - } - } - else - { - if (rover->flags & FF_QUICKSAND) - { - if (z < top && (z + height) > bottom) - { - if (newz < z) - { - newz = z; - slope = NULL; - } - } - continue; - } - - d1 = z - (bottom + ((top - bottom)/2)); - d2 = (z + height) - (bottom + ((top - bottom)/2)); - - if (top > newz && abs(d1) < abs(d2)) - { - newz = top; -#ifdef ESLOPE - if (*rover->t_slope) - slope = *rover->t_slope; -#endif - } - } - } - } - -#if 0 - mobj->standingslope = slope; -#endif - -#ifdef HWRENDER - mobj->modeltilt = slope; -#endif -} - -// Move the hnext chain! -static void K_MoveHeldObjects(player_t *player) -{ - if (!player->mo) - return; - - if (!player->mo->hnext) - { - player->kartstuff[k_bananadrag] = 0; - if (player->kartstuff[k_eggmanheld]) - player->kartstuff[k_eggmanheld] = 0; - else if (player->kartstuff[k_itemheld]) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } - return; - } - - if (P_MobjWasRemoved(player->mo->hnext)) - { - // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... - P_SetTarget(&player->mo->hnext, NULL); - player->kartstuff[k_bananadrag] = 0; - if (player->kartstuff[k_eggmanheld]) - player->kartstuff[k_eggmanheld] = 0; - else if (player->kartstuff[k_itemheld]) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } - return; - } - - switch (player->mo->hnext->type) - { - case MT_ORBINAUT_SHIELD: // Kart orbit items - case MT_JAWZ_SHIELD: - { - mobj_t *cur = player->mo->hnext; - fixed_t speed = ((8 - min(4, player->kartstuff[k_itemamount])) * cur->info->speed) / 7; - - player->kartstuff[k_bananadrag] = 0; // Just to make sure - - while (cur && !P_MobjWasRemoved(cur)) - { - const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); // mobj's distance from its Target, or Radius. - fixed_t z; - - if (!cur->health) - { - cur = cur->hnext; - continue; - } - - cur->color = player->skincolor; - - cur->angle -= ANGLE_90; - cur->angle += FixedAngle(speed); - - if (cur->extravalue1 < radius) - cur->extravalue1 += P_AproxDistance(cur->extravalue1, radius) / 12; - if (cur->extravalue1 > radius) - cur->extravalue1 = radius; - - // If the player is on the ceiling, then flip your items as well. - if (player && player->mo->eflags & MFE_VERTICALFLIP) - cur->eflags |= MFE_VERTICALFLIP; - else - cur->eflags &= ~MFE_VERTICALFLIP; - - // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); - - if (P_MobjFlip(cur) > 0) - z = player->mo->z; - else - z = player->mo->z + player->mo->height - cur->height; - - cur->flags |= MF_NOCLIPTHING; // temporarily make them noclip other objects so they can't hit anyone while in the player - P_TeleportMove(cur, player->mo->x, player->mo->y, z); - cur->momx = FixedMul(FINECOSINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); - cur->momy = FixedMul(FINESINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); - cur->flags &= ~MF_NOCLIPTHING; - if (!P_TryMove(cur, player->mo->x + cur->momx, player->mo->y + cur->momy, true)) - P_SlideMove(cur, true); - if (P_IsObjectOnGround(player->mo)) - { - if (P_MobjFlip(cur) > 0) - { - if (cur->floorz > player->mo->z - cur->height) - z = cur->floorz; - } - else - { - if (cur->ceilingz < player->mo->z + player->mo->height + cur->height) - z = cur->ceilingz - cur->height; - } - } - - // Center it during the scale up animation - z += (FixedMul(mobjinfo[cur->type].height, player->mo->scale - cur->scale)>>1) * P_MobjFlip(cur); - - cur->z = z; - cur->momx = cur->momy = 0; - cur->angle += ANGLE_90; - - cur = cur->hnext; - } - } - break; - case MT_BANANA_SHIELD: // Kart trailing items - case MT_SSMINE_SHIELD: - case MT_EGGMANITEM_SHIELD: - case MT_SINK_SHIELD: - { - mobj_t *cur = player->mo->hnext; - mobj_t *targ = player->mo; - - if (P_IsObjectOnGround(player->mo) && player->speed > 0) - player->kartstuff[k_bananadrag]++; - - while (cur && !P_MobjWasRemoved(cur)) - { - const fixed_t radius = FixedHypot(targ->radius, targ->radius) + FixedHypot(cur->radius, cur->radius); - angle_t ang; - fixed_t targx, targy, targz; - fixed_t speed, dist; - - if (cur->type == MT_EGGMANITEM_SHIELD) - { - // Decided that this should use their "canon" color. - cur->color = SKINCOLOR_BLACK; - } - - cur->flags &= ~MF_NOCLIPTHING; - - if (!cur->health) - { - cur = cur->hnext; - continue; - } - - if (cur->extravalue1 < radius) - cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); - if (cur->extravalue1 > radius) - cur->extravalue1 = radius; - - if (cur != player->mo->hnext) - { - targ = cur->hprev; - dist = cur->extravalue1/4; - } - else - dist = cur->extravalue1/2; - - if (!targ || P_MobjWasRemoved(targ)) - continue; - - // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); - - ang = targ->angle; - targx = targ->x + P_ReturnThrustX(cur, ang + ANGLE_180, dist); - targy = targ->y + P_ReturnThrustY(cur, ang + ANGLE_180, dist); - targz = targ->z; - - speed = FixedMul(R_PointToDist2(cur->x, cur->y, targx, targy), 3*FRACUNIT/4); - if (P_IsObjectOnGround(targ)) - targz = cur->floorz; - - cur->angle = R_PointToAngle2(cur->x, cur->y, targx, targy); - - /*if (P_IsObjectOnGround(player->mo) && player->speed > 0 && player->kartstuff[k_bananadrag] > TICRATE - && P_RandomChance(min(FRACUNIT/2, FixedDiv(player->speed, K_GetKartSpeed(player, false))/2))) - { - if (leveltime & 1) - targz += 8*(2*FRACUNIT)/7; - else - targz -= 8*(2*FRACUNIT)/7; - }*/ - - if (speed > dist) - P_InstaThrust(cur, cur->angle, speed-dist); - - P_SetObjectMomZ(cur, FixedMul(targz - cur->z, 7*FRACUNIT/8) - gravity, false); - - if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT) - P_TeleportMove(cur, targx, targy, cur->z); - -#ifdef ESLOPE - if (P_IsObjectOnGround(cur)) - { - K_CalculateBananaSlope(cur, cur->x, cur->y, cur->z, - cur->radius, cur->height, (cur->eflags & MFE_VERTICALFLIP), false); - } -#endif - - cur = cur->hnext; - } - } - break; - case MT_ROCKETSNEAKER: // Special rocket sneaker stuff - { - mobj_t *cur = player->mo->hnext; - INT32 num = 0; - - while (cur && !P_MobjWasRemoved(cur)) - { - const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); - boolean vibrate = ((leveltime & 1) && !cur->tracer); - angle_t angoffset; - fixed_t targx, targy, targz; - - cur->flags &= ~MF_NOCLIPTHING; - - if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1)) - cur->drawflags |= MFD_DONTDRAW; - else - cur->drawflags &= ~MFD_DONTDRAW; - - if (num & 1) - P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L)); - else - P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_RVIBRATE : S_ROCKETSNEAKER_R)); - - if (!player->kartstuff[k_rocketsneakertimer] || cur->extravalue2 || !cur->health) - { - num = (num+1) % 2; - cur = cur->hnext; - continue; - } - - if (cur->extravalue1 < radius) - cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); - if (cur->extravalue1 > radius) - cur->extravalue1 = radius; - - // Shrink your items if the player shrunk too. - P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); - -#if 1 - { - angle_t input = player->frameangle - cur->angle; - boolean invert = (input > ANGLE_180); - if (invert) - input = InvAngle(input); - - input = FixedAngle(AngleFixed(input)/4); - if (invert) - input = InvAngle(input); - - cur->angle = cur->angle + input; - } -#else - cur->angle = player->frameangle; -#endif - - angoffset = ANGLE_90 + (ANGLE_180 * num); - - targx = player->mo->x + P_ReturnThrustX(cur, cur->angle + angoffset, cur->extravalue1); - targy = player->mo->y + P_ReturnThrustY(cur, cur->angle + angoffset, cur->extravalue1); - - { // bobbing, copy pasted from my kimokawaiii entry - const fixed_t pi = (22<mo->scale, 8 * FINESINE((((2*pi*(4*TICRATE)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK)); - targz = (player->mo->z + (player->mo->height/2)) + sine; - if (player->mo->eflags & MFE_VERTICALFLIP) - targz += (player->mo->height/2 - 32*player->mo->scale)*6; - - } - - if (cur->tracer) - { - fixed_t diffx, diffy, diffz; - - diffx = targx - cur->x; - diffy = targy - cur->y; - diffz = targz - cur->z; - - P_TeleportMove(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale), - cur->tracer->y + diffy + P_ReturnThrustY(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->z + diffz); - P_SetScale(cur->tracer, (cur->tracer->destscale = 3*cur->scale/4)); - } - - P_TeleportMove(cur, targx, targy, targz); - K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. -#ifdef HWRENDER - cur->modeltilt = player->mo->modeltilt; -#endif - num = (num+1) % 2; - cur = cur->hnext; - } - } - break; - default: - break; - } -} - -player_t *K_FindJawzTarget(mobj_t *actor, player_t *source) -{ - fixed_t best = -1; - player_t *wtarg = NULL; - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - angle_t thisang; - player_t *player; - - if (!playeringame[i]) - continue; - - player = &players[i]; - - if (player->spectator) - continue; // spectator - - if (!player->mo) - continue; - - if (player->mo->health <= 0) - continue; // dead - - // Don't target yourself, stupid. - if (player == source) - continue; - - // Don't home in on teammates. - if (G_GametypeHasTeams() && source->ctfteam == player->ctfteam) - continue; - - // Invisible, don't bother - if (player->kartstuff[k_hyudorotimer]) - continue; - - // Find the angle, see who's got the best. - thisang = actor->angle - R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y); - if (thisang > ANGLE_180) - thisang = InvAngle(thisang); - - // Jawz only go after the person directly ahead of you in race... sort of literally now! - if (G_RaceGametype()) - { - // Don't go for people who are behind you - if (thisang > ANGLE_67h) - continue; - // Don't pay attention to people who aren't above your position - if (player->kartstuff[k_position] >= source->kartstuff[k_position]) - continue; - if ((best == -1) || (player->kartstuff[k_position] > best)) - { - wtarg = player; - best = player->kartstuff[k_position]; - } - } - else - { - fixed_t thisdist; - fixed_t thisavg; - - // Don't go for people who are behind you - if (thisang > ANGLE_45) - continue; - - // Don't pay attention to dead players - if (player->kartstuff[k_bumper] <= 0) - continue; - - // Z pos too high/low - if (abs(player->mo->z - (actor->z + actor->momz)) > RING_DIST/8) - continue; - - thisdist = P_AproxDistance(player->mo->x - (actor->x + actor->momx), player->mo->y - (actor->y + actor->momy)); - - if (thisdist > 2*RING_DIST) // Don't go for people who are too far away - continue; - - thisavg = (AngleFixed(thisang) + thisdist) / 2; - - //CONS_Printf("got avg %d from player # %d\n", thisavg>>FRACBITS, i); - - if ((best == -1) || (thisavg < best)) - { - wtarg = player; - best = thisavg; - } - } - } - - return wtarg; -} - -// Engine Sounds. -static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) -{ - const INT32 numsnds = 13; - INT32 class, s, w; // engine class number - UINT8 volume = 255; - fixed_t volumedampen = 0; - INT32 targetsnd = 0; - INT32 i; - - s = (player->kartspeed-1)/3; - w = (player->kartweight-1)/3; - -#define LOCKSTAT(stat) \ - if (stat < 0) { stat = 0; } \ - if (stat > 2) { stat = 2; } - LOCKSTAT(s); - LOCKSTAT(w); -#undef LOCKSTAT - - class = s+(3*w); - - // Silence the engines - if (leveltime < 8 || player->spectator) - { - player->karthud[khud_enginesnd] = 0; // Reset sound number - return; - } - -#if 0 - if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct! -#else - if (leveltime % 8) // .25 seconds of wait time between engine sounds -#endif - return; - - if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->respawn.state == RESPAWNST_DROP)) // Startup boosts - targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0); - else - targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2; - - if (targetsnd < 0) - targetsnd = 0; - if (targetsnd > 12) - targetsnd = 12; - - if (player->karthud[khud_enginesnd] < targetsnd) - player->karthud[khud_enginesnd]++; - if (player->karthud[khud_enginesnd] > targetsnd) - player->karthud[khud_enginesnd]--; - - if (player->karthud[khud_enginesnd] < 0) - player->karthud[khud_enginesnd] = 0; - if (player->karthud[khud_enginesnd] > 12) - player->karthud[khud_enginesnd] = 12; - - for (i = 0; i < MAXPLAYERS; i++) - { - UINT8 thisvol = 0; - fixed_t dist; - - if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting) - continue; - - if (P_IsDisplayPlayer(&players[i])) - { - volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time. - continue; - } - - dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x, - player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2; - - dist = FixedDiv(dist, mapobjectscale); - - if (dist > 1536<>FRACBITS)) / (((1536<>(FRACBITS+4)); - - if (thisvol == 0) - continue; - - volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT - } - - if (volumedampen > FRACUNIT) - volume = FixedDiv(volume<>FRACBITS; - - if (volume <= 0) // Might as well - return; - - S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->karthud[khud_enginesnd]) + (class*numsnds), volume); -} - -static void K_UpdateInvincibilitySounds(player_t *player) -{ - INT32 sfxnum = sfx_None; - - if (player->mo->health > 0 && !P_IsDisplayPlayer(player)) - { - if (cv_kartinvinsfx.value) - { - if (player->kartstuff[k_invincibilitytimer] > 0) // Prioritize invincibility - sfxnum = sfx_alarmi; - else if (player->kartstuff[k_growshrinktimer] > 0) - sfxnum = sfx_alarmg; - } - else - { - if (player->kartstuff[k_invincibilitytimer] > 0) - sfxnum = sfx_kinvnc; - else if (player->kartstuff[k_growshrinktimer] > 0) - sfxnum = sfx_kgrow; - } - } - - if (sfxnum != sfx_None && !S_SoundPlaying(player->mo, sfxnum)) - S_StartSound(player->mo, sfxnum); - -#define STOPTHIS(this) \ - if (sfxnum != this && S_SoundPlaying(player->mo, this)) \ - S_StopSoundByID(player->mo, this); - STOPTHIS(sfx_alarmi); - STOPTHIS(sfx_alarmg); - STOPTHIS(sfx_kinvnc); - STOPTHIS(sfx_kgrow); -#undef STOPTHIS -} - -#define RINGANIM_NUMFRAMES 10 -#define RINGANIM_DELAYMAX 5 - -void K_KartPlayerHUDUpdate(player_t *player) -{ - if (player->karthud[khud_lapanimation]) - player->karthud[khud_lapanimation]--; - - if (player->karthud[khud_yougotem]) - player->karthud[khud_yougotem]--; - - if (player->karthud[khud_voices]) - player->karthud[khud_voices]--; - - if (player->karthud[khud_tauntvoices]) - player->karthud[khud_tauntvoices]--; - - if (G_RaceGametype()) - { - // 0 is the fast spin animation, set at 30 tics of ring boost or higher! - if (player->kartstuff[k_ringboost] >= 30) - player->karthud[khud_ringdelay] = 0; - else - player->karthud[khud_ringdelay] = ((RINGANIM_DELAYMAX+1) * (30 - player->kartstuff[k_ringboost])) / 30; - - if (player->karthud[khud_ringframe] == 0 && player->karthud[khud_ringdelay] > RINGANIM_DELAYMAX) - { - player->karthud[khud_ringframe] = 0; - player->karthud[khud_ringtics] = 0; - } - else if ((player->karthud[khud_ringtics]--) <= 0) - { - if (player->karthud[khud_ringdelay] == 0) // fast spin animation - { - player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+2) % RINGANIM_NUMFRAMES); - player->karthud[khud_ringtics] = 0; - } - else - { - player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+1) % RINGANIM_NUMFRAMES); - player->karthud[khud_ringtics] = min(RINGANIM_DELAYMAX, player->karthud[khud_ringdelay])-1; - } - } - - if (player->kartstuff[k_ringlock]) - { - UINT8 normalanim = (leveltime % 14); - UINT8 debtanim = 14 + (leveltime % 2); - - if (player->karthud[khud_ringspblock] >= 14) // debt animation - { - if ((player->kartstuff[k_rings] > 0) // Get out of 0 ring animation - && (normalanim == 3 || normalanim == 10)) // on these transition frames. - player->karthud[khud_ringspblock] = normalanim; - else - player->karthud[khud_ringspblock] = debtanim; - } - else // normal animation - { - if ((player->kartstuff[k_rings] <= 0) // Go into 0 ring animation - && (player->karthud[khud_ringspblock] == 1 || player->karthud[khud_ringspblock] == 8)) // on these transition frames. - player->karthud[khud_ringspblock] = debtanim; - else - player->karthud[khud_ringspblock] = normalanim; - } - } - else - player->karthud[khud_ringspblock] = (leveltime % 14); // reset to normal anim next time - } - - if (G_BattleGametype() && (player->exiting || player->kartstuff[k_comebacktimer])) - { - if (player->exiting) - { - if (player->exiting < 6*TICRATE) - player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; - else if (player->exiting == 6*TICRATE) - player->karthud[khud_cardanimation] = 0; - else if (player->karthud[khud_cardanimation] < 2*TICRATE) - player->karthud[khud_cardanimation]++; - } - else - { - if (player->kartstuff[k_comebacktimer] < 6*TICRATE) - player->karthud[khud_cardanimation] -= ((164-player->karthud[khud_cardanimation])/8)+1; - else if (player->kartstuff[k_comebacktimer] < 9*TICRATE) - player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; - } - - if (player->karthud[khud_cardanimation] > 164) - player->karthud[khud_cardanimation] = 164; - if (player->karthud[khud_cardanimation] < 0) - player->karthud[khud_cardanimation] = 0; - } - else if (G_RaceGametype() && player->exiting) - { - if (player->karthud[khud_cardanimation] < 2*TICRATE) - player->karthud[khud_cardanimation]++; - } - else - player->karthud[khud_cardanimation] = 0; -} - -#undef RINGANIM_DELAYMAX - -// SRB2Kart: blockmap iterate for attraction shield users -static mobj_t *attractmo; -static fixed_t attractdist; -static inline boolean PIT_AttractingRings(mobj_t *thing) -{ - if (!attractmo || P_MobjWasRemoved(attractmo)) - return false; - - if (!attractmo->player) - return false; // not a player - - if (thing->health <= 0 || !thing) - return true; // dead - - if (thing->type != MT_RING && thing->type != MT_FLINGRING) - return true; // not a ring - - if (thing->extravalue1) - return true; // in special ring animation - - if (thing->cusval) - return true; // already attracted - - // see if it went over / under - if (attractmo->z - (attractdist>>2) > thing->z + thing->height) - return true; // overhead - if (attractmo->z + attractmo->height + (attractdist>>2) < thing->z) - return true; // underneath - - if (P_AproxDistance(attractmo->x - thing->x, attractmo->y - thing->y) < attractdist) - return true; // Too far away - - // set target - P_SetTarget(&thing->tracer, attractmo); - // flag to show it's been attracted once before - thing->cusval = 1; - return true; // find other rings -} - -/** Looks for rings near a player in the blockmap. - * - * \param pmo Player object looking for rings to attract - * \sa A_AttractChase - */ -static void K_LookForRings(mobj_t *pmo) -{ - INT32 bx, by, xl, xh, yl, yh; - attractdist = FixedMul(RING_DIST, pmo->scale)>>2; - - // Use blockmap to check for nearby rings - yh = (unsigned)(pmo->y + attractdist - bmaporgy)>>MAPBLOCKSHIFT; - yl = (unsigned)(pmo->y - attractdist - bmaporgy)>>MAPBLOCKSHIFT; - xh = (unsigned)(pmo->x + attractdist - bmaporgx)>>MAPBLOCKSHIFT; - xl = (unsigned)(pmo->x - attractdist - bmaporgx)>>MAPBLOCKSHIFT; - - attractmo = pmo; - - for (by = yl; by <= yh; by++) - for (bx = xl; bx <= xh; bx++) - P_BlockThingsIterator(bx, by, PIT_AttractingRings); -} - -/** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c - - \param player player object passed from P_PlayerThink - \param cmd control input from player - - \return void -*/ -void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) -{ - K_UpdateOffroad(player); - K_UpdateDraft(player); - K_UpdateEngineSounds(player, cmd); // Thanks, VAda! - - // update boost angle if not spun out - if (!player->kartstuff[k_spinouttimer] && !player->kartstuff[k_wipeoutslow]) - player->kartstuff[k_boostangle] = (INT32)player->mo->angle; - - K_GetKartBoostPower(player); - - // Special effect objects! - if (player->mo && !player->spectator) - { - if (player->kartstuff[k_dashpadcooldown]) // Twinkle Circuit afterimages - { - mobj_t *ghost; - ghost = P_SpawnGhostMobj(player->mo); - ghost->fuse = player->kartstuff[k_dashpadcooldown]+1; - ghost->momx = player->mo->momx / (player->kartstuff[k_dashpadcooldown]+1); - ghost->momy = player->mo->momy / (player->kartstuff[k_dashpadcooldown]+1); - ghost->momz = player->mo->momz / (player->kartstuff[k_dashpadcooldown]+1); - player->kartstuff[k_dashpadcooldown]--; - } - - if (player->speed > 0) - { - // Speed lines - if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_ringboost] - || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost] - || player->kartstuff[k_eggmanexplode]) - { - mobj_t *fast = P_SpawnMobj(player->mo->x + (P_RandomRange(-36,36) * player->mo->scale), - player->mo->y + (P_RandomRange(-36,36) * player->mo->scale), - player->mo->z + (player->mo->height/2) + (P_RandomRange(-20,20) * player->mo->scale), - MT_FASTLINE); - - P_SetTarget(&fast->target, player->mo); - fast->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - fast->momx = 3*player->mo->momx/4; - fast->momy = 3*player->mo->momy/4; - fast->momz = 3*player->mo->momz/4; - - K_MatchGenericExtraFlags(fast, player->mo); - - // Make it red when you have the eggman speed boost - if (player->kartstuff[k_eggmanexplode]) - { - fast->color = SKINCOLOR_RED; - fast->colorized = true; - } - } - - if (player->kartstuff[k_numboosts] > 0) // Boosting after images - { - mobj_t *ghost; - ghost = P_SpawnGhostMobj(player->mo); - ghost->extravalue1 = player->kartstuff[k_numboosts]+1; - ghost->extravalue2 = (leveltime % ghost->extravalue1); - ghost->fuse = ghost->extravalue1; - ghost->frame |= FF_FULLBRIGHT; - ghost->colorized = true; - //ghost->color = player->skincolor; - //ghost->momx = (3*player->mo->momx)/4; - //ghost->momy = (3*player->mo->momy)/4; - //ghost->momz = (3*player->mo->momz)/4; - if (leveltime & 1) - ghost->drawflags |= MFD_DONTDRAW; - } - - if (P_IsObjectOnGround(player->mo)) - { - // Offroad dust - if (player->kartstuff[k_boostpower] < FRACUNIT) - { - K_SpawnWipeoutTrail(player->mo, true); - if (leveltime % 6 == 0) - S_StartSound(player->mo, sfx_cdfm70); - } - - // Draft dust - if (player->kartstuff[k_draftpower] >= FRACUNIT) - { - K_SpawnDraftDust(player->mo); - /*if (leveltime % 23 == 0 || !S_SoundPlaying(player->mo, sfx_s265)) - S_StartSound(player->mo, sfx_s265);*/ - } - } - } - - if (G_RaceGametype() && player->kartstuff[k_rings] <= 0) // spawn ring debt indicator - { - mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, - player->mo->z + player->mo->momz + player->mo->height + (24*player->mo->scale), MT_THOK); - - P_SetMobjState(debtflag, S_RINGDEBT); - P_SetScale(debtflag, (debtflag->destscale = player->mo->scale)); - - K_MatchGenericExtraFlags(debtflag, player->mo); - debtflag->frame += (leveltime % 4); - - if ((leveltime/12) & 1) - debtflag->frame += 4; - - debtflag->color = player->skincolor; - debtflag->fuse = 2; - - debtflag->drawflags = K_GetPlayerDontDrawFlag(player); - } - - if (player->kartstuff[k_springstars] && (leveltime & 1)) - { - fixed_t randx = P_RandomRange(-40, 40) * player->mo->scale; - fixed_t randy = P_RandomRange(-40, 40) * player->mo->scale; - fixed_t randz = P_RandomRange(0, player->mo->height >> FRACBITS) << FRACBITS; - mobj_t *star = P_SpawnMobj( - player->mo->x + randx, - player->mo->y + randy, - player->mo->z + randz, - MT_KARMAFIREWORK); - - star->color = player->kartstuff[k_springcolor]; - star->flags |= MF_NOGRAVITY; - star->momx = player->mo->momx / 2; - star->momy = player->mo->momy / 2; - star->momz = player->mo->momz / 2; - star->fuse = 12; - star->scale = player->mo->scale; - star->destscale = star->scale / 2; - - player->kartstuff[k_springstars]--; - } - } - - if (player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Ensure these are set correctly here - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - else if (player->kartstuff[k_eggmanexplode]) // You're gonna diiiiie - { - const INT32 flashtime = 4<<(player->kartstuff[k_eggmanexplode]/TICRATE); - if (player->kartstuff[k_eggmanexplode] == 1 || (player->kartstuff[k_eggmanexplode] % (flashtime/2) != 0)) - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - else if (player->kartstuff[k_eggmanexplode] % flashtime == 0) - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_BLACK; - } - else - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_CRIMSON; - } - } - else if (player->kartstuff[k_invincibilitytimer]) // setting players to use the star colormap and spawning afterimages - { - player->mo->colorized = true; - } - else if (player->kartstuff[k_growshrinktimer]) // Ditto, for grow/shrink - { - if (player->kartstuff[k_growshrinktimer] % 5 == 0) - { - player->mo->colorized = true; - player->mo->color = (player->kartstuff[k_growshrinktimer] < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE); - } - else - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - } - else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie - { - const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); - if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) - { - player->mo->colorized = false; - player->mo->color = player->skincolor; - } - else if (player->kartstuff[k_killfield] % flashtime == 0) - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_BYZANTIUM; - } - else - { - player->mo->colorized = true; - player->mo->color = SKINCOLOR_RUBY; - } - } - else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting - { - player->mo->colorized = true; - } - else - { - player->mo->colorized = false; - } - - if (player->kartstuff[k_itemtype] == KITEM_NONE) - player->kartstuff[k_holdready] = 0; - - // DKR style camera for boosting - if (player->karthud[khud_boostcam] != 0 || player->karthud[khud_destboostcam] != 0) - { - if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam] - && player->karthud[khud_destboostcam] != 0) - { - player->karthud[khud_boostcam] += FRACUNIT/(TICRATE/4); - if (player->karthud[khud_boostcam] >= player->karthud[khud_destboostcam]) - player->karthud[khud_destboostcam] = 0; - } - else - { - player->karthud[khud_boostcam] -= FRACUNIT/TICRATE; - if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam]) - player->karthud[khud_boostcam] = player->karthud[khud_destboostcam] = 0; - } - //CONS_Printf("cam: %d, dest: %d\n", player->karthud[khud_boostcam], player->karthud[khud_destboostcam]); - } - - player->karthud[khud_timeovercam] = 0; - - // Specific hack because it insists on setting flashing tics during this anyway... - if (player->kartstuff[k_spinouttype] == 2) - { - player->powers[pw_flashing] = 0; - } - // Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations. - else if (player->kartstuff[k_spinouttimer] != 0 - || player->kartstuff[k_wipeoutslow] != 0 - || player->kartstuff[k_squishedtimer] != 0) - { - player->powers[pw_flashing] = K_GetKartFlashing(player); - } - else if (player->powers[pw_flashing] >= K_GetKartFlashing(player)) - { - player->powers[pw_flashing]--; - } - - if (player->kartstuff[k_spinouttimer]) - { - if ((P_IsObjectOnGround(player->mo) - || (player->kartstuff[k_spinouttype] != 0)) - && (!player->kartstuff[k_sneakertimer])) - { - player->kartstuff[k_spinouttimer]--; - if (player->kartstuff[k_wipeoutslow] > 1) - player->kartstuff[k_wipeoutslow]--; - // Actually, this caused more problems than it solved. Just make sure you set type before you spinout. Which K_SpinPlayer always does. - /*if (player->kartstuff[k_spinouttimer] == 0) - player->kartstuff[k_spinouttype] = 0;*/ // Reset type - } - } - else - { - 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->kartstuff[k_rings] > 20) - player->kartstuff[k_rings] = 20; - else if (player->kartstuff[k_rings] < -20) - player->kartstuff[k_rings] = -20; - - if (player->kartstuff[k_ringdelay]) - player->kartstuff[k_ringdelay]--; - - if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_squishedtimer]) - player->kartstuff[k_ringboost] = 0; - else if (player->kartstuff[k_ringboost]) - player->kartstuff[k_ringboost]--; - - if (player->kartstuff[k_sneakertimer]) - { - player->kartstuff[k_sneakertimer]--; - - if (player->kartstuff[k_sneakertimer] <= 0) - { - player->kartstuff[k_numsneakers] = 0; - } - } - - if (player->kartstuff[k_flamedash]) - player->kartstuff[k_flamedash]--; - - if (player->kartstuff[k_sneakertimer] && player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1) - player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; - - if (player->kartstuff[k_floorboost]) - player->kartstuff[k_floorboost]--; - - if (player->kartstuff[k_driftboost]) - player->kartstuff[k_driftboost]--; - - if (player->kartstuff[k_startboost]) - player->kartstuff[k_startboost]--; - - if (player->kartstuff[k_invincibilitytimer]) - player->kartstuff[k_invincibilitytimer]--; - - if ((player->respawn.state == RESPAWNST_NONE) && player->kartstuff[k_growshrinktimer] != 0) - { - if (player->kartstuff[k_growshrinktimer] > 0) - player->kartstuff[k_growshrinktimer]--; - if (player->kartstuff[k_growshrinktimer] < 0) - player->kartstuff[k_growshrinktimer]++; - - // Back to normal - if (player->kartstuff[k_growshrinktimer] == 0) - K_RemoveGrowShrink(player); - } - - if (player->kartstuff[k_superring]) - { - if (player->kartstuff[k_superring] % 3 == 0) - { - mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); - ring->extravalue1 = 1; // Ring collect animation timer - ring->angle = player->mo->angle; // animation angle - P_SetTarget(&ring->target, player->mo); // toucher for thinker - player->kartstuff[k_pickuprings]++; - if (player->kartstuff[k_superring] <= 3) - ring->cvmem = 1; // play caching when collected - } - player->kartstuff[k_superring]--; - } - - if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0 - && player->kartstuff[k_rocketsneakertimer]) - player->kartstuff[k_rocketsneakertimer]--; - - if (player->kartstuff[k_hyudorotimer]) - player->kartstuff[k_hyudorotimer]--; - - if (player->kartstuff[k_sadtimer]) - player->kartstuff[k_sadtimer]--; - - if (player->kartstuff[k_stealingtimer]) - player->kartstuff[k_stealingtimer]--; - - if (player->kartstuff[k_stolentimer]) - player->kartstuff[k_stolentimer]--; - - if (player->kartstuff[k_squishedtimer]) - { - player->kartstuff[k_squishedtimer]--; - - if ((player->kartstuff[k_squishedtimer] == 0) && !(player->pflags & PF_NOCLIP)) - { - player->mo->flags &= ~MF_NOCLIP; - } - } - - if (player->kartstuff[k_justbumped]) - player->kartstuff[k_justbumped]--; - - if (player->kartstuff[k_tiregrease]) - player->kartstuff[k_tiregrease]--; - - // This doesn't go in HUD update because it has potential gameplay ramifications - if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0) - { - player->karthud[khud_itemblinkmode] = 0; - player->karthud[khud_itemblink] = 0; - } - - K_KartPlayerHUDUpdate(player); - - if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 - && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] - && (player->respawn.state == RESPAWNST_DROP) && !player->powers[pw_flashing]) - { - player->kartstuff[k_wanted]++; - if (battleovertime.enabled >= 10*TICRATE) - { - if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) - { - player->kartstuff[k_killfield]++; - if (player->kartstuff[k_killfield] > 4*TICRATE) - { - K_SpinPlayer(player, NULL, 0, NULL, false); - //player->kartstuff[k_killfield] = 1; - } - } - else if (player->kartstuff[k_killfield] > 0) - player->kartstuff[k_killfield]--; - } - } - else if (player->kartstuff[k_killfield] > 0) - player->kartstuff[k_killfield]--; - - if (P_IsObjectOnGround(player->mo)) - player->kartstuff[k_waterskip] = 0; - - if (player->kartstuff[k_instashield]) - player->kartstuff[k_instashield]--; - - if (player->kartstuff[k_eggmanexplode]) - { - if (player->spectator || (G_BattleGametype() && !player->kartstuff[k_bumper])) - player->kartstuff[k_eggmanexplode] = 0; - else - { - player->kartstuff[k_eggmanexplode]--; - if (player->kartstuff[k_eggmanexplode] <= 0) - { - mobj_t *eggsexplode; - //player->powers[pw_flashing] = 0; - eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION); - if (player->kartstuff[k_eggmanblame] >= 0 - && player->kartstuff[k_eggmanblame] < MAXPLAYERS - && playeringame[player->kartstuff[k_eggmanblame]] - && !players[player->kartstuff[k_eggmanblame]].spectator - && players[player->kartstuff[k_eggmanblame]].mo) - P_SetTarget(&eggsexplode->target, players[player->kartstuff[k_eggmanblame]].mo); - } - } - } - - if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) - { - if (RINGTOTAL(player) < 20 && !player->kartstuff[k_ringlock]) - K_LookForRings(player->mo); - } - - if (player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) - { - if (player->kartstuff[k_bubblecool]) - player->kartstuff[k_bubblecool]--; - } - else - { - player->kartstuff[k_bubbleblowup] = 0; - player->kartstuff[k_bubblecool] = 0; - } - - if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) - { - if (player->kartstuff[k_flamedash]) - K_FlameDashLeftoverSmoke(player->mo); - } - - if (player->kartstuff[k_comebacktimer]) - player->kartstuff[k_comebackmode] = 0; - - if (P_IsObjectOnGround(player->mo) && player->kartstuff[k_pogospring]) - { - if (P_MobjFlip(player->mo)*player->mo->momz <= 0) - player->kartstuff[k_pogospring] = 0; - } - - if (cmd->buttons & BT_DRIFT) - player->kartstuff[k_jmp] = 1; - else - player->kartstuff[k_jmp] = 0; - - // Roulette Code - K_KartItemRoulette(player, cmd); - - // Handle invincibility sfx - K_UpdateInvincibilitySounds(player); // Also thanks, VAda! - - // Plays the music after the starting countdown. - if (P_IsLocalPlayer(player) && leveltime == (starttime + (TICRATE/2))) - { - S_ChangeMusic(mapmusname, mapmusflags, true); - S_ShowMusicCredit(); - } -} - -void K_KartPlayerAfterThink(player_t *player) -{ - if (player->kartstuff[k_curshield] - || player->kartstuff[k_invincibilitytimer] - || (player->kartstuff[k_growshrinktimer] != 0 && player->kartstuff[k_growshrinktimer] % 5 == 4)) // 4 instead of 0 because this is afterthink! - { - player->mo->frame |= FF_FULLBRIGHT; - } - else - { - if (!(player->mo->state->frame & FF_FULLBRIGHT)) - player->mo->frame &= ~FF_FULLBRIGHT; - } - - // Move held objects (Bananas, Orbinaut, etc) - K_MoveHeldObjects(player); - - // Jawz reticule (seeking) - if (player->kartstuff[k_itemtype] == KITEM_JAWZ && player->kartstuff[k_itemheld]) - { - INT32 lasttarg = player->kartstuff[k_lastjawztarget]; - player_t *targ; - mobj_t *ret; - - if (player->kartstuff[k_jawztargetdelay] && playeringame[lasttarg] && !players[lasttarg].spectator) - { - targ = &players[lasttarg]; - player->kartstuff[k_jawztargetdelay]--; - } - else - targ = K_FindJawzTarget(player->mo, player); - - if (!targ || !targ->mo || P_MobjWasRemoved(targ->mo)) - { - player->kartstuff[k_lastjawztarget] = -1; - player->kartstuff[k_jawztargetdelay] = 0; - return; - } - - ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE); - P_SetTarget(&ret->target, targ->mo); - ret->frame |= ((leveltime % 10) / 2); - ret->tics = 1; - ret->color = player->skincolor; - - if (targ-players != lasttarg) - { - if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ)) - S_StartSound(NULL, sfx_s3k89); - else - S_StartSound(targ->mo, sfx_s3k89); - - player->kartstuff[k_lastjawztarget] = targ-players; - player->kartstuff[k_jawztargetdelay] = 5; - } - } - else - { - player->kartstuff[k_lastjawztarget] = -1; - player->kartstuff[k_jawztargetdelay] = 0; - } -} - -/*-------------------------------------------------- - static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) - - Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or - previous waypoints are infront of the player. - - Input Arguments:- - player - The player the next waypoint is being found for - - Return:- - The waypoint that is the player's next waypoint ---------------------------------------------------*/ -static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) -{ - waypoint_t *bestwaypoint = NULL; - - if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) - { - waypoint_t *waypoint = K_GetBestWaypointForMobj(player->mo); - boolean updaterespawn = false; - - bestwaypoint = waypoint; - - // check the waypoint's location in relation to the player - // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. - // EXCEPTION: If our best waypoint is the finishline AND we're facing towards it, don't do this. - // Otherwise it breaks the distance calculations. - if (waypoint != NULL) - { - boolean finishlinehack = false; - angle_t playerangle = player->mo->angle; - angle_t momangle = player->mo->angle; - 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) - { - angledelta = InvAngle(angledelta); - } - - momdelta = momangle - angletowaypoint; - if (momdelta > ANGLE_180) - { - momdelta = InvAngle(momdelta); - } - - if (bestwaypoint == K_GetFinishLineWaypoint()) - { - // facing towards the finishline - if (angledelta <= ANGLE_90) - { - finishlinehack = true; - } - } - - // We're using a lot of angle calculations here, because only using facing angle or only using momentum angle both have downsides. - // nextwaypoints will be picked if you're facing OR moving forward. - // prevwaypoints will be picked if you're facing AND moving backward. - if ((angledelta > ANGLE_45 || momdelta > ANGLE_45) - && (finishlinehack == false)) - { - angle_t nextbestdelta = angledelta; - angle_t nextbestmomdelta = momdelta; - size_t i = 0U; - - if (K_PlayerUsesBotMovement(player)) - { - // Try to force bots to use a next waypoint - nextbestdelta = ANGLE_MAX; - nextbestmomdelta = ANGLE_MAX; - } - - if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) - { - for (i = 0U; i < waypoint->numnextwaypoints; i++) - { - if (K_PlayerUsesBotMovement(player) == true - && K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) == true - && K_BotCanTakeCut(player) == false) - { - // Bots that aren't able to take a shortcut will ignore shortcut waypoints. - // (However, if they're already on a shortcut, then we want them to keep going.) - - if (player->nextwaypoint == NULL - || K_GetWaypointIsShortcut(player->nextwaypoint) == false) - { - continue; - } - } - - angletowaypoint = R_PointToAngle2( - player->mo->x, player->mo->y, - waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y); - - angledelta = playerangle - angletowaypoint; - if (angledelta > ANGLE_180) - { - angledelta = InvAngle(angledelta); - } - - momdelta = momangle - angletowaypoint; - if (momdelta > ANGLE_180) - { - momdelta = InvAngle(momdelta); - } - - if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) - { - bestwaypoint = waypoint->nextwaypoints[i]; - - if (angledelta < nextbestdelta) - { - nextbestdelta = angledelta; - } - if (momdelta < nextbestmomdelta) - { - nextbestmomdelta = momdelta; - } - - // Remove wrong way flag if we're using nextwaypoints - player->kartstuff[k_wrongway] = 0; - updaterespawn = true; - } - } - } - - if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U) - && !(K_PlayerUsesBotMovement(player))) // Bots do not need prev waypoints - { - for (i = 0U; i < waypoint->numprevwaypoints; i++) - { - angletowaypoint = R_PointToAngle2( - player->mo->x, player->mo->y, - waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); - - angledelta = playerangle - angletowaypoint; - if (angledelta > ANGLE_180) - { - angledelta = InvAngle(angledelta); - } - - momdelta = momangle - angletowaypoint; - if (momdelta > ANGLE_180) - { - momdelta = InvAngle(momdelta); - } - - if (angledelta < nextbestdelta && momdelta < nextbestmomdelta) - { - bestwaypoint = waypoint->prevwaypoints[i]; - - nextbestdelta = angledelta; - nextbestmomdelta = momdelta; - - // Set wrong way flag if we're using prevwaypoints - player->kartstuff[k_wrongway] = 1; - updaterespawn = false; - } - } - } - } - } - - if (!P_IsObjectOnGround(player->mo)) - { - updaterespawn = false; - } - - // Respawn point should only be updated when we're going to a nextwaypoint - if ((updaterespawn) && - (player->respawn.state == RESPAWNST_NONE) && - (bestwaypoint != NULL) && - (bestwaypoint != player->nextwaypoint) && - (K_GetWaypointIsSpawnpoint(bestwaypoint)) && - (K_GetWaypointIsEnabled(bestwaypoint) == true)) - { - player->respawn.wp = bestwaypoint; - } - } - - return bestwaypoint; -} - -static boolean K_PlayerCloserToNextWaypoints(waypoint_t *const waypoint, player_t *const player) -{ - boolean nextiscloser = true; - - if ((waypoint != NULL) && (player != NULL) && (player->mo != NULL)) - { - size_t i = 0U; - waypoint_t *currentwpcheck = NULL; - angle_t angletoplayer = ANGLE_MAX; - angle_t currentanglecheck = ANGLE_MAX; - angle_t bestangle = ANGLE_MAX; - - angletoplayer = R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, - player->mo->x, player->mo->y); - - for (i = 0U; i < waypoint->numnextwaypoints; i++) - { - currentwpcheck = waypoint->nextwaypoints[i]; - currentanglecheck = R_PointToAngle2( - waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); - - // Get delta angle - currentanglecheck = currentanglecheck - angletoplayer; - - if (currentanglecheck > ANGLE_180) - { - currentanglecheck = InvAngle(currentanglecheck); - } - - if (currentanglecheck < bestangle) - { - bestangle = currentanglecheck; - } - } - - for (i = 0U; i < waypoint->numprevwaypoints; i++) - { - currentwpcheck = waypoint->prevwaypoints[i]; - currentanglecheck = R_PointToAngle2( - waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); - - // Get delta angle - currentanglecheck = currentanglecheck - angletoplayer; - - if (currentanglecheck > ANGLE_180) - { - currentanglecheck = InvAngle(currentanglecheck); - } - - if (currentanglecheck < bestangle) - { - bestangle = currentanglecheck; - nextiscloser = false; - break; - } - } - } - - return nextiscloser; -} - -/*-------------------------------------------------- - void K_UpdateDistanceFromFinishLine(player_t *const player) - - Updates the distance a player has to the finish line. - - Input Arguments:- - player - The player the distance is being updated for - - Return:- - None ---------------------------------------------------*/ -void K_UpdateDistanceFromFinishLine(player_t *const player) -{ - if ((player != NULL) && (player->mo != NULL)) - { - waypoint_t *finishline = K_GetFinishLineWaypoint(); - waypoint_t *nextwaypoint = NULL; - - if (player->spectator) - { - // Don't update waypoints while spectating - nextwaypoint = finishline; - } - else - { - nextwaypoint = K_GetPlayerNextWaypoint(player); - } - - if (nextwaypoint != NULL) - { - // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. - // player->nextwaypoint will keep its previous value in this case. - player->nextwaypoint = nextwaypoint; - } - - // nextwaypoint is now the waypoint that is in front of us - if (player->exiting || player->spectator) - { - // Player has finished, we don't need to calculate this - player->distancetofinish = 0U; - } - else if ((player->nextwaypoint != NULL) && (finishline != NULL)) - { - const boolean useshortcuts = false; - const boolean huntbackwards = false; - boolean pathfindsuccess = false; - path_t pathtofinish = {}; - - pathfindsuccess = - K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); - - // Update the player's distance to the finish line if a path was found. - // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track - if (pathfindsuccess == true) - { - // Add euclidean distance to the next waypoint to the distancetofinish - UINT32 adddist; - fixed_t disttowaypoint = - P_AproxDistance( - (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), - (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); - disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); - - adddist = (UINT32)disttowaypoint; - - player->distancetofinish = pathtofinish.totaldist + adddist; - Z_Free(pathtofinish.array); - - // distancetofinish is currently a flat distance to the finish line, but in order to be fully - // correct we need to add to it the length of the entire circuit multiplied by the number of laps - // left after this one. This will give us the total distance to the finish line, and allow item - // distance calculation to work easily - if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) - { - const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); - - player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); - - // An additional HACK, to fix looking backwards towards the finish line - // If the player's next waypoint is the finishline and the angle distance from player to - // connectin waypoints implies they're closer to a next waypoint, add a full track distance - if (player->nextwaypoint == finishline) - { - if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) - { - player->distancetofinish += K_GetCircuitLength(); - } - } - } - } - } - } -} - -INT32 K_GetKartRingPower(player_t *player) -{ - return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2); -} - -// Returns false if this player being placed here causes them to collide with any other player -// Used in g_game.c for match etc. respawning -// This does not check along the z because the z is not correctly set for the spawnee at this point -boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y) -{ - INT32 i; - fixed_t p1radius = players[playernum].mo->radius; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playernum == i || !playeringame[i] || players[i].spectator || !players[i].mo || players[i].mo->health <= 0 - || players[i].playerstate != PST_LIVE || (players[i].mo->flags & MF_NOCLIP) || (players[i].mo->flags & MF_NOCLIPTHING)) - continue; - - if (abs(x - players[i].mo->x) < (p1radius + players[i].mo->radius) - && abs(y - players[i].mo->y) < (p1radius + players[i].mo->radius)) - { - return false; - } - } - return true; -} - -// countersteer is how strong the controls are telling us we are turning -// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left -static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) -{ - INT16 basedrift, driftadjust; - fixed_t driftweight = player->kartweight*14; // 12 - - if (player->kartstuff[k_drift] == 0 || !P_IsObjectOnGround(player->mo)) - { - // If they aren't drifting or on the ground, this doesn't apply - return 0; - } - - if (player->kartstuff[k_driftend] != 0) - { - // Drift has ended and we are tweaking their angle back a bit - return -266*player->kartstuff[k_drift]; - } - - basedrift = (83 * player->kartstuff[k_drift]) - (((driftweight - 14) * player->kartstuff[k_drift]) / 5); // 415 - 303 - driftadjust = abs((252 - driftweight) * player->kartstuff[k_drift] / 5); - - if (player->kartstuff[k_tiregrease] > 0) // Buff drift-steering while in greasemode - { - basedrift += (basedrift / greasetics) * player->kartstuff[k_tiregrease]; - } - - if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) - { - countersteer = 3*countersteer/2; - } - - return basedrift + (FixedMul(driftadjust * FRACUNIT, countersteer) / FRACUNIT); -} - -INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) -{ - fixed_t p_maxspeed = K_GetKartSpeed(player, false); - fixed_t p_speed = min(player->speed, (p_maxspeed * 2)); - fixed_t weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); - - if (player->spectator) - { - return turnvalue; - } - - if (K_PlayerUsesBotMovement(player)) - { - turnvalue = 5*turnvalue/4; // Base increase to turning - turnvalue = FixedMul( - turnvalue * FRACUNIT, - K_BotRubberband(player) - ) / FRACUNIT; - } - - if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo)) - { - fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); - - // If we're drifting we have a completely different turning value - - if (player->kartstuff[k_driftend] != 0) - { - countersteer = FRACUNIT; - } - - turnvalue = K_GetKartDriftValue(player, countersteer); - - return turnvalue; - } - - if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) - { - turnvalue = 5*turnvalue/4; - } - - if (player->kartstuff[k_flamedash] > 0) - { - fixed_t multiplier = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); - multiplier = FRACUNIT + (FixedDiv(multiplier, FRACUNIT/2) / 4); - turnvalue = FixedMul(turnvalue * FRACUNIT, multiplier) / FRACUNIT; - } - - if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) - { - turnvalue = 3*turnvalue/2; - } - - // Weight has a small effect on turning - turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; - - return turnvalue; -} - -INT32 K_GetKartDriftSparkValue(player_t *player) -{ - UINT8 kartspeed = (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - ? 1 - : player->kartspeed; - return (26*4 + kartspeed*2 + (9 - player->kartweight))*8; -} - -/* -Stage 1: red sparks -Stage 2: blue sparks -Stage 3: big large rainbow sparks -*/ -static void K_SpawnDriftBoostExplosion(player_t *player, int stage) -{ - mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); - - P_SetTarget(&overlay->target, player->mo); - P_SetScale(overlay, (overlay->destscale = player->mo->scale)); - K_FlipFromObject(overlay, player->mo); - - switch (stage) - { - case 1: - overlay->color = SKINCOLOR_KETCHUP; - overlay->fuse = 16; - break; - - case 2: - overlay->color = SKINCOLOR_SAPPHIRE; - overlay->fuse = 32; - - S_StartSound(player->mo, sfx_kc5b); - break; - - case 3: - overlay->color = SKINCOLOR_SILVER; - overlay->fuse = 120; - - S_StartSound(player->mo, sfx_kc5b); - S_StartSound(player->mo, sfx_s3kc4l); - break; - } -} - -static void K_KartDrift(player_t *player, boolean onground) -{ - fixed_t minspeed = (10 * player->mo->scale); - INT32 dsone = K_GetKartDriftSparkValue(player); - INT32 dstwo = dsone*2; - INT32 dsthree = dstwo*2; - - // Drifting is actually straffing + automatic turning. - // Holding the Jump button will enable drifting. - - // Drift Release (Moved here so you can't "chain" drifts) - if (player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5) - { - if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) - { - angle_t pushdir = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); - - S_StartSound(player->mo, sfx_s23c); - //K_SpawnDashDustRelease(player); - - if (player->kartstuff[k_driftcharge] < 0) - { - // Stage 0: Yellow sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 8); - - if (player->kartstuff[k_driftboost] < 15) - player->kartstuff[k_driftboost] = 15; - } - else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) - { - // Stage 1: Red sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 4); - - if (player->kartstuff[k_driftboost] < 20) - player->kartstuff[k_driftboost] = 20; - - K_SpawnDriftBoostExplosion(player, 1); - } - else if (player->kartstuff[k_driftcharge] < dsthree) - { - // Stage 2: Blue sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 3); - - if (player->kartstuff[k_driftboost] < 50) - player->kartstuff[k_driftboost] = 50; - - K_SpawnDriftBoostExplosion(player, 2); - } - else if (player->kartstuff[k_driftcharge] >= dsthree) - { - // Stage 3: Rainbow sparks - if (!onground) - P_Thrust(player->mo, pushdir, player->speed / 2); - - if (player->kartstuff[k_driftboost] < 125) - player->kartstuff[k_driftboost] = 125; - - K_SpawnDriftBoostExplosion(player, 3); - } - } - - // Remove charge - player->kartstuff[k_driftcharge] = 0; - } - - // Drifting: left or right? - if ((player->cmd.driftturn > 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 - && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != 1) - { - // Starting left drift - player->kartstuff[k_drift] = 1; - player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; - } - else if ((player->cmd.driftturn < 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 - && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != -1) - { - // Starting right drift - player->kartstuff[k_drift] = -1; - player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; - } - else if (player->kartstuff[k_jmp] == 0) // || player->kartstuff[k_turndir] == 0) - { - // drift is not being performed so if we're just finishing set driftend and decrement counters - if (player->kartstuff[k_drift] > 0) - { - player->kartstuff[k_drift]--; - player->kartstuff[k_driftend] = 1; - } - else if (player->kartstuff[k_drift] < 0) - { - player->kartstuff[k_drift]++; - player->kartstuff[k_driftend] = 1; - } - else - player->kartstuff[k_driftend] = 0; - } - - if (player->kartstuff[k_spinouttimer] > 0 || player->speed == 0) - { - // Stop drifting - player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0; - player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; - player->kartstuff[k_getsparks] = 0; - } - else if (player->kartstuff[k_jmp] == 1 && player->kartstuff[k_drift] != 0) - { - // Incease/decrease the drift value to continue drifting in that direction - fixed_t driftadditive = 24; - boolean playsound = false; - - if (onground) - { - if (player->kartstuff[k_drift] >= 1) // Drifting to the left - { - player->kartstuff[k_drift]++; - if (player->kartstuff[k_drift] > 5) - player->kartstuff[k_drift] = 5; - - if (player->cmd.driftturn > 0) // Inward - driftadditive += abs(player->cmd.driftturn)/100; - if (player->cmd.driftturn < 0) // Outward - driftadditive -= abs(player->cmd.driftturn)/75; - } - else if (player->kartstuff[k_drift] <= -1) // Drifting to the right - { - player->kartstuff[k_drift]--; - if (player->kartstuff[k_drift] < -5) - player->kartstuff[k_drift] = -5; - - if (player->cmd.driftturn < 0) // Inward - driftadditive += abs(player->cmd.driftturn)/100; - if (player->cmd.driftturn > 0) // Outward - driftadditive -= abs(player->cmd.driftturn)/75; - } - - // Disable drift-sparks until you're going fast enough - if (player->kartstuff[k_getsparks] == 0 - || (player->kartstuff[k_offroad] && K_ApplyOffroad(player))) - driftadditive = 0; - - // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. - if (player->speed > minspeed*2) - { - player->kartstuff[k_getsparks] = 1; - - if (player->kartstuff[k_driftcharge] <= -1) - { - player->kartstuff[k_driftcharge] = dsone; // Back to red - playsound = true; - } - } - else if (player->speed <= minspeed) - { - player->kartstuff[k_getsparks] = 0; - driftadditive = 0; - - if (player->kartstuff[k_driftcharge] >= dsone) - { - player->kartstuff[k_driftcharge] = -1; // Set yellow sparks - playsound = true; - } - } - } - else - { - driftadditive = 0; - } - - // This spawns the drift sparks - if ((player->kartstuff[k_driftcharge] + driftadditive >= dsone) - || (player->kartstuff[k_driftcharge] < 0)) - { - K_SpawnDriftSparks(player); - } - - if ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone) - || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo) - || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree)) - { - playsound = true; - } - - // Sound whenever you get a different tier of sparks - if (playsound && P_IsDisplayPlayer(player)) - { - if (player->kartstuff[k_driftcharge] == -1) - S_StartSoundAtVolume(player->mo, sfx_sploss, 192); // Yellow spark sound - else - S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); - } - - player->kartstuff[k_driftcharge] += driftadditive; - player->kartstuff[k_driftend] = 0; - } - - if ((!player->kartstuff[k_sneakertimer]) - || (!player->cmd.driftturn) - || (!player->kartstuff[k_aizdriftstrat]) - || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) - { - if (!player->kartstuff[k_drift]) - player->kartstuff[k_aizdriftstrat] = 0; - else - player->kartstuff[k_aizdriftstrat] = ((player->kartstuff[k_drift] > 0) ? 1 : -1); - } - else if (player->kartstuff[k_aizdriftstrat] && !player->kartstuff[k_drift]) - K_SpawnAIZDust(player); - - if (player->kartstuff[k_drift] - && ((player->cmd.buttons & BT_BRAKE) - || !(player->cmd.buttons & BT_ACCELERATE)) - && P_IsObjectOnGround(player->mo)) - { - if (!player->kartstuff[k_brakedrift]) - K_SpawnBrakeDriftSparks(player); - player->kartstuff[k_brakedrift] = 1; - } - else - player->kartstuff[k_brakedrift] = 0; -} -// -// K_KartUpdatePosition -// -void K_KartUpdatePosition(player_t *player) -{ - fixed_t position = 1; - fixed_t oldposition = player->kartstuff[k_position]; - fixed_t i; - - if (player->spectator || !player->mo) - { - // Ensure these are reset for spectators - player->kartstuff[k_position] = 0; - player->kartstuff[k_positiondelay] = 0; - return; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - if (G_RaceGametype()) - { - if (player->exiting) // End of match standings - { - // Only time matters - if (players[i].realtime < player->realtime) - position++; - } - else - { - // I'm a lap behind this player OR - // My distance to the finish line is higher, so I'm behind - if ((players[i].laps > player->laps) - || (players[i].distancetofinish < player->distancetofinish)) - { - position++; - } - } - } - else if (G_BattleGametype()) - { - if (player->exiting) // End of match standings - { - // Only score matters - if (players[i].marescore > player->marescore) - position++; - } - 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])) - position++; - } - } - } - - if (leveltime < starttime || oldposition == 0) - oldposition = position; - - if (oldposition != position) // Changed places? - player->kartstuff[k_positiondelay] = 10; // Position number growth - - player->kartstuff[k_position] = position; -} - -// -// K_StripItems -// -void K_StripItems(player_t *player) -{ - player->kartstuff[k_itemtype] = KITEM_NONE; - player->kartstuff[k_itemamount] = 0; - player->kartstuff[k_itemheld] = 0; - - player->kartstuff[k_rocketsneakertimer] = 0; - - if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2) - { - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - } - player->kartstuff[k_eggmanheld] = 0; - - player->kartstuff[k_hyudorotimer] = 0; - player->kartstuff[k_stealingtimer] = 0; - player->kartstuff[k_stolentimer] = 0; - - player->kartstuff[k_curshield] = KSHIELD_NONE; - player->kartstuff[k_bananadrag] = 0; - - player->kartstuff[k_sadtimer] = 0; - - K_UpdateHnextList(player, true); -} - -void K_StripOther(player_t *player) -{ - player->kartstuff[k_itemroulette] = 0; - player->kartstuff[k_roulettetype] = 0; - - player->kartstuff[k_invincibilitytimer] = 0; - K_RemoveGrowShrink(player); - - if (player->kartstuff[k_eggmanexplode]) - { - player->kartstuff[k_eggmanexplode] = 0; - player->kartstuff[k_eggmanblame] = -1; - } -} - -static INT32 K_FlameShieldMax(player_t *player) -{ - UINT32 disttofinish = 0; - UINT32 distv = DISTVAR; - UINT8 numplayers = 0; - UINT8 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator) - numplayers++; - if (players[i].kartstuff[k_position] == 1) - disttofinish = players[i].distancetofinish; - } - - if (numplayers <= 1) - { - return 16; // max when alone, for testing - } - else if (player->kartstuff[k_position] == 1) - { - return 0; // minimum for first - } - - disttofinish = player->distancetofinish - disttofinish; - distv = FixedMul(distv * FRACUNIT, mapobjectscale) / FRACUNIT; - return min(16, 1 + (disttofinish / distv)); -} - -// -// K_MoveKartPlayer -// -void K_MoveKartPlayer(player_t *player, boolean onground) -{ - ticcmd_t *cmd = &player->cmd; - boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->pflags & PF_ATTACKDOWN)); - boolean HOLDING_ITEM = (player->kartstuff[k_itemheld] || player->kartstuff[k_eggmanheld]); - boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0); - - player->pflags &= ~PF_HITFINISHLINE; - - if (!player->exiting) - { - if (player->kartstuff[k_oldposition] < player->kartstuff[k_position]) // But first, if you lost a place, - { - player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // then the other player taunts. - K_RegularVoiceTimers(player); // and you can't for a bit - } - else if (player->kartstuff[k_oldposition] > player->kartstuff[k_position]) // Otherwise, - { - K_PlayOvertakeSound(player->mo); // Say "YOU'RE TOO SLOW!" - player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // Restore the old position, - } - } - - if (player->kartstuff[k_positiondelay]) - player->kartstuff[k_positiondelay]--; - - // Prevent ring misfire - if (!(cmd->buttons & BT_ATTACK)) - { - if (player->kartstuff[k_itemtype] == KITEM_NONE - && NO_HYUDORO && !(HOLDING_ITEM - || player->kartstuff[k_itemamount] - || player->kartstuff[k_itemroulette] - || player->kartstuff[k_rocketsneakertimer] - || player->kartstuff[k_eggmanexplode])) - player->kartstuff[k_userings] = 1; - else - player->kartstuff[k_userings] = 0; - } - - if ((player->pflags & PF_ATTACKDOWN) && !(cmd->buttons & BT_ATTACK)) - player->pflags &= ~PF_ATTACKDOWN; - else if (cmd->buttons & BT_ATTACK) - player->pflags |= PF_ATTACKDOWN; - - if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > starttime - && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && (player->respawn.state == RESPAWNST_NONE)) - { - // First, the really specific, finicky items that function without the item being directly in your item slot. - // Karma item dropping - if (player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer]) - { - if (ATTACK_IS_DOWN) - { - mobj_t *newitem; - - if (player->kartstuff[k_comebackmode] == 1) - { - newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RANDOMITEM); - newitem->threshold = 69; // selected "randomly". - } - else - { - newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM); - if (player->kartstuff[k_eggmanblame] >= 0 - && player->kartstuff[k_eggmanblame] < MAXPLAYERS - && playeringame[player->kartstuff[k_eggmanblame]] - && !players[player->kartstuff[k_eggmanblame]].spectator - && players[player->kartstuff[k_eggmanblame]].mo) - P_SetTarget(&newitem->target, players[player->kartstuff[k_eggmanblame]].mo); - player->kartstuff[k_eggmanblame] = -1; - } - - newitem->flags2 = (player->mo->flags2 & MF2_OBJECTFLIP); - newitem->fuse = 15*TICRATE; // selected randomly. - - player->kartstuff[k_comebackmode] = 0; - player->kartstuff[k_comebacktimer] = comebacktime; - S_StartSound(player->mo, sfx_s254); - } - } - else - { - // Ring boosting - if (player->kartstuff[k_userings]) - { - if ((player->pflags & PF_ATTACKDOWN) && !player->kartstuff[k_ringdelay] && player->kartstuff[k_rings] > 0) - { - mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); - P_SetMobjState(ring, S_FASTRING1); - ring->extravalue1 = 1; // Ring use animation timer - ring->extravalue2 = 1; // Ring use animation flag - ring->shadowscale = 0; - P_SetTarget(&ring->target, player->mo); // user - player->kartstuff[k_rings]--; - player->kartstuff[k_ringdelay] = 3; - } - } - // Other items - else - { - // Eggman Monitor exploding - if (player->kartstuff[k_eggmanexplode]) - { - if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanexplode] <= 3*TICRATE && player->kartstuff[k_eggmanexplode] > 1) - player->kartstuff[k_eggmanexplode] = 1; - } - // Eggman Monitor throwing - else if (player->kartstuff[k_eggmanheld]) - { - if (ATTACK_IS_DOWN) - { - K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_eggmanheld] = 0; - K_UpdateHnextList(player, true); - } - } - // Rocket Sneaker usage - else if (player->kartstuff[k_rocketsneakertimer] > 1) - { - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) - { - K_DoSneaker(player, 2); - K_PlayBoostTaunt(player->mo); - player->kartstuff[k_rocketsneakertimer] -= 3*TICRATE; - if (player->kartstuff[k_rocketsneakertimer] < 1) - player->kartstuff[k_rocketsneakertimer] = 1; - } - } - else if (player->kartstuff[k_itemamount] <= 0) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; - } - else - { - switch (player->kartstuff[k_itemtype]) - { - case KITEM_SNEAKER: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) - { - K_DoSneaker(player, 1); - K_PlayBoostTaunt(player->mo); - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_ROCKETSNEAKER: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO - && player->kartstuff[k_rocketsneakertimer] == 0) - { - INT32 moloop; - mobj_t *mo = NULL; - mobj_t *prev = player->mo; - - K_PlayBoostTaunt(player->mo); - //player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s3k3a); - - //K_DoSneaker(player, 2); - - player->kartstuff[k_rocketsneakertimer] = (itemtime*3); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, true); - - for (moloop = 0; moloop < 2; moloop++) - { - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ROCKETSNEAKER); - K_MatchGenericExtraFlags(mo, player->mo); - mo->flags |= MF_NOCLIPTHING; - mo->angle = player->mo->angle; - mo->threshold = 10; - mo->movecount = moloop%2; - mo->movedir = mo->lastlook = moloop+1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - break; - case KITEM_INVINCIBILITY: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage normally, so you're free to waste it if you have multiple - { - if (!player->kartstuff[k_invincibilitytimer]) - { - mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INVULNFLASH); - P_SetTarget(&overlay->target, player->mo); - overlay->destscale = player->mo->scale; - P_SetScale(overlay, player->mo->scale); - } - player->kartstuff[k_invincibilitytimer] = itemtime+(2*TICRATE); // 10 seconds - if (P_IsLocalPlayer(player)) - S_ChangeMusicSpecial("kinvnc"); - if (! P_IsDisplayPlayer(player)) - S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kinvnc)); - P_RestoreMusic(player); - K_PlayPowerGloatSound(player->mo); - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_BANANA: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - INT32 moloop; - mobj_t *mo; - mobj_t *prev = player->mo; - - //K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s254); - - for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) - { - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BANANA_SHIELD); - if (!mo) - { - player->kartstuff[k_itemamount] = moloop; - break; - } - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = player->kartstuff[k_itemamount]; - mo->movedir = moloop+1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Banana x3 thrown - { - K_ThrowKartItem(player, false, MT_BANANA, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, false); - } - break; - case KITEM_EGGMAN: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - mobj_t *mo; - player->kartstuff[k_itemamount]--; - player->kartstuff[k_eggmanheld] = 1; - S_StartSound(player->mo, sfx_s254); - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD); - if (mo) - { - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = 1; - mo->movedir = 1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&player->mo->hnext, mo); - } - } - break; - case KITEM_ORBINAUT: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - angle_t newangle; - INT32 moloop; - mobj_t *mo = NULL; - mobj_t *prev = player->mo; - - //K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s3k3a); - - for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) - { - newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ORBINAUT_SHIELD); - if (!mo) - { - player->kartstuff[k_itemamount] = moloop; - break; - } - mo->flags |= MF_NOCLIPTHING; - mo->angle = newangle; - mo->threshold = 10; - mo->movecount = player->kartstuff[k_itemamount]; - mo->movedir = mo->lastlook = moloop+1; - mo->color = player->skincolor; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Orbinaut x3 thrown - { - K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, false); - } - break; - case KITEM_JAWZ: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - angle_t newangle; - INT32 moloop; - mobj_t *mo = NULL; - mobj_t *prev = player->mo; - - //K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s3k3a); - - for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) - { - newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_JAWZ_SHIELD); - if (!mo) - { - player->kartstuff[k_itemamount] = moloop; - break; - } - mo->flags |= MF_NOCLIPTHING; - mo->angle = newangle; - mo->threshold = 10; - mo->movecount = player->kartstuff[k_itemamount]; - mo->movedir = mo->lastlook = moloop+1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&mo->hprev, prev); - P_SetTarget(&prev->hnext, mo); - prev = mo; - } - } - else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Jawz thrown - { - if (player->kartstuff[k_throwdir] == 1 || player->kartstuff[k_throwdir] == 0) - K_ThrowKartItem(player, true, MT_JAWZ, 1, 0); - else if (player->kartstuff[k_throwdir] == -1) // Throwing backward gives you a dud that doesn't home in - K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - K_UpdateHnextList(player, false); - } - break; - case KITEM_MINE: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - mobj_t *mo; - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s254); - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SSMINE_SHIELD); - if (mo) - { - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = 1; - mo->movedir = 1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&player->mo->hnext, mo); - } - } - else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) - { - K_ThrowKartItem(player, false, MT_SSMINE, 1, 1); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - player->kartstuff[k_itemheld] = 0; - K_UpdateHnextList(player, true); - } - break; - case KITEM_BALLHOG: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_itemamount]--; - K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0); - K_PlayAttackTaunt(player->mo); - } - break; - case KITEM_SPB: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_itemamount]--; - K_ThrowKartItem(player, true, MT_SPB, 1, 0); - K_PlayAttackTaunt(player->mo); - } - break; - case KITEM_GROW: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - if (player->kartstuff[k_growshrinktimer] < 0) // If you're shrunk, then "grow" will just make you normal again. - K_RemoveGrowShrink(player); - else - { - K_PlayPowerGloatSound(player->mo); - player->mo->scalespeed = mapobjectscale/TICRATE; - player->mo->destscale = (3*mapobjectscale)/2; - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; - player->kartstuff[k_growshrinktimer] = itemtime+(4*TICRATE); // 12 seconds - if (P_IsLocalPlayer(player)) - S_ChangeMusicSpecial("kgrow"); - if (! P_IsDisplayPlayer(player)) - S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); - P_RestoreMusic(player); - S_StartSound(player->mo, sfx_kc5a); - } - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_SHRINK: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - K_DoShrink(player); - player->kartstuff[k_itemamount]--; - K_PlayPowerGloatSound(player->mo); - } - break; - case KITEM_THUNDERSHIELD: - if (player->kartstuff[k_curshield] != KSHIELD_THUNDER) - { - mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THUNDERSHIELD); - P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); - P_SetTarget(&shield->target, player->mo); - S_StartSound(player->mo, sfx_s3k41); - player->kartstuff[k_curshield] = KSHIELD_THUNDER; - } - - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - K_DoThunderShield(player); - player->kartstuff[k_itemamount]--; - K_PlayAttackTaunt(player->mo); - } - break; - case KITEM_BUBBLESHIELD: - if (player->kartstuff[k_curshield] != KSHIELD_BUBBLE) - { - mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BUBBLESHIELD); - P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); - P_SetTarget(&shield->target, player->mo); - S_StartSound(player->mo, sfx_s3k3f); - player->kartstuff[k_curshield] = KSHIELD_BUBBLE; - } - - if (!HOLDING_ITEM && NO_HYUDORO) - { - if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) - { - if (player->kartstuff[k_bubbleblowup] == 0) - S_StartSound(player->mo, sfx_s3k75); - - player->kartstuff[k_bubbleblowup]++; - player->kartstuff[k_bubblecool] = player->kartstuff[k_bubbleblowup]*4; - - if (player->kartstuff[k_bubbleblowup] > bubbletime*2) - { - K_ThrowKartItem(player, (player->kartstuff[k_throwdir] > 0), MT_BUBBLESHIELDTRAP, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_bubbleblowup] = 0; - player->kartstuff[k_bubblecool] = 0; - player->kartstuff[k_holdready] = 0; - player->kartstuff[k_itemamount]--; - } - } - else - { - if (player->kartstuff[k_bubbleblowup] > bubbletime) - player->kartstuff[k_bubbleblowup] = bubbletime; - - if (player->kartstuff[k_bubbleblowup]) - player->kartstuff[k_bubbleblowup]--; - - player->kartstuff[k_holdready] = (player->kartstuff[k_bubblecool] ? 0 : 1); - } - } - break; - case KITEM_FLAMESHIELD: - if (player->kartstuff[k_curshield] != KSHIELD_FLAME) - { - mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_FLAMESHIELD); - P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); - P_SetTarget(&shield->target, player->mo); - S_StartSound(player->mo, sfx_s3k3e); - player->kartstuff[k_curshield] = KSHIELD_FLAME; - } - - if (!HOLDING_ITEM && NO_HYUDORO) - { - INT32 destlen = K_FlameShieldMax(player); - INT32 flamemax = 0; - - if (player->kartstuff[k_flamelength] < destlen) - player->kartstuff[k_flamelength]++; // Can always go up! - - flamemax = player->kartstuff[k_flamelength] * flameseg; - if (flamemax > 0) - flamemax += TICRATE; // leniency period - - if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) - { - if (player->kartstuff[k_flamemeter] < 0) - player->kartstuff[k_flamemeter] = 0; - - if (player->kartstuff[k_flamedash] == 0) - { - S_StartSound(player->mo, sfx_s3k43); - K_PlayBoostTaunt(player->mo); - } - - player->kartstuff[k_flamedash] += 2; - player->kartstuff[k_flamemeter] += 2; - - if (!onground) - { - P_Thrust( - player->mo, R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), - FixedMul(player->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) - ); - } - - if (player->kartstuff[k_flamemeter] > flamemax) - { - P_Thrust( - player->mo, player->mo->angle, - FixedMul((50*player->mo->scale), K_GetKartGameSpeedScalar(gamespeed)) - ); - - player->kartstuff[k_flamemeter] = 0; - player->kartstuff[k_flamelength] = 0; - player->kartstuff[k_holdready] = 0; - player->kartstuff[k_itemamount]--; - } - } - else - { - player->kartstuff[k_holdready] = 1; - - if (player->kartstuff[k_flamemeter] > 0) - player->kartstuff[k_flamemeter]--; - - if (player->kartstuff[k_flamelength] > destlen) - { - player->kartstuff[k_flamelength]--; // Can ONLY go down if you're not using it - - flamemax = player->kartstuff[k_flamelength] * flameseg; - if (flamemax > 0) - flamemax += TICRATE; // leniency period - } - - if (player->kartstuff[k_flamemeter] > flamemax) - player->kartstuff[k_flamemeter] = flamemax; - } - } - break; - case KITEM_HYUDORO: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_itemamount]--; - K_DoHyudoroSteal(player); // yes. yes they do. - } - break; - case KITEM_POGOSPRING: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO - && !player->kartstuff[k_pogospring]) - { - K_PlayBoostTaunt(player->mo); - K_DoPogoSpring(player->mo, 32<kartstuff[k_pogospring] = 1; - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_SUPERRING: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - player->kartstuff[k_superring] += (10*3); - player->kartstuff[k_itemamount]--; - } - break; - case KITEM_KITCHENSINK: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - mobj_t *mo; - player->kartstuff[k_itemheld] = 1; - S_StartSound(player->mo, sfx_s254); - mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SINK_SHIELD); - if (mo) - { - mo->flags |= MF_NOCLIPTHING; - mo->threshold = 10; - mo->movecount = 1; - mo->movedir = 1; - P_SetTarget(&mo->target, player->mo); - P_SetTarget(&player->mo->hnext, mo); - } - } - else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Sink thrown - { - K_ThrowKartItem(player, false, MT_SINK, 1, 2); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_itemamount]--; - player->kartstuff[k_itemheld] = 0; - K_UpdateHnextList(player, true); - } - break; - case KITEM_SAD: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO - && !player->kartstuff[k_sadtimer]) - { - player->kartstuff[k_sadtimer] = stealtime; - player->kartstuff[k_itemamount]--; - } - break; - default: - break; - } - } - } - } - - // No more! - if (!player->kartstuff[k_itemamount]) - { - player->kartstuff[k_itemheld] = 0; - player->kartstuff[k_itemtype] = KITEM_NONE; - } - - if (K_GetShieldFromItem(player->kartstuff[k_itemtype]) == KSHIELD_NONE) - { - player->kartstuff[k_curshield] = KSHIELD_NONE; // RESET shield type - player->kartstuff[k_bubbleblowup] = 0; - player->kartstuff[k_bubblecool] = 0; - player->kartstuff[k_flamelength] = 0; - player->kartstuff[k_flamemeter] = 0; - } - - if (spbplace == -1 || player->kartstuff[k_position] != spbplace) - player->kartstuff[k_ringlock] = 0; // reset ring lock - - if (player->kartstuff[k_itemtype] == KITEM_SPB - || player->kartstuff[k_itemtype] == KITEM_SHRINK - || player->kartstuff[k_growshrinktimer] < 0) - indirectitemcooldown = 20*TICRATE; - - if (player->kartstuff[k_hyudorotimer] > 0) - { - INT32 hyu = hyudorotime; - - if (G_RaceGametype()) - hyu *= 2; // double in race - - if (leveltime & 1) - { - player->mo->drawflags |= MFD_DONTDRAW; - } - else - { - if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) - player->mo->drawflags &= ~K_GetPlayerDontDrawFlag(player); - else - player->mo->drawflags &= ~MFD_DONTDRAW; - } - - player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints - } - else if (player->kartstuff[k_hyudorotimer] == 0) - { - player->mo->drawflags &= ~MFD_DONTDRAW; - } - - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 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]; - } - else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0) - { - player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); - } - } - - if (onground) - { - fixed_t prevfriction = player->mo->friction; - - // Reduce friction after hitting a horizontal spring - if (player->kartstuff[k_tiregrease]) - player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->kartstuff[k_tiregrease]; - - // Friction - if (!player->kartstuff[k_offroad]) - { - if (player->speed > 0 && cmd->forwardmove == 0 && player->mo->friction == 59392) - player->mo->friction += 4608; - } - - // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel - if (player->speed > 0 && cmd->forwardmove < 0) - player->mo->friction -= 2048; - - // Karma ice physics - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) - player->mo->friction += 1228; - - if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) - player->mo->friction += 614; - - // Wipeout slowdown - if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow]) - { - if (player->kartstuff[k_offroad]) - player->mo->friction -= 4912; - if (player->kartstuff[k_wipeoutslow] == 1) - player->mo->friction -= 9824; - } - - // Friction was changed, so we must recalculate a bunch of stuff - if (player->mo->friction != prevfriction) - { - if (player->mo->friction > FRACUNIT) - player->mo->friction = FRACUNIT; - if (player->mo->friction < 0) - player->mo->friction = 0; - - player->mo->movefactor = FixedDiv(ORIG_FRICTION, player->mo->friction); - - if (player->mo->movefactor < FRACUNIT) - player->mo->movefactor = 19*player->mo->movefactor - 18*FRACUNIT; - else - player->mo->movefactor = FRACUNIT; - - if (player->mo->movefactor < 32) - player->mo->movefactor = 32; - } - - // Don't go too far above your top speed when rubberbanding - // Down here, because we do NOT want to modify movefactor - if (K_PlayerUsesBotMovement(player)) - { - player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction); - } - } - - K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads - - // Quick Turning - // You can't turn your kart when you're not moving. - // So now it's time to burn some rubber! - if (player->speed < 2 && leveltime > starttime && cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE && cmd->driftturn != 0) - { - if (leveltime % 8 == 0) - S_StartSound(player->mo, sfx_s224); - } - - // Squishing - // If a Grow player or a sector crushes you, get flattened instead of being killed. - - if (player->kartstuff[k_squishedtimer] > 0) - { - //player->mo->flags |= MF_NOCLIP; - player->mo->momx = 0; - player->mo->momy = 0; - } - - // Play the starting countdown sounds - if (player == &players[g_localplayers[0]]) // Don't play louder in splitscreen - { - if ((leveltime == starttime-(3*TICRATE)) || (leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) - S_StartSound(NULL, sfx_s3ka7); - if (leveltime == starttime) - { - S_StartSound(NULL, sfx_s3kad); - S_StopMusic(); // The GO! sound stops the level start ambience - } - } - - // Start charging once you're given the opportunity. - if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) - { - if (cmd->buttons & BT_ACCELERATE) - { - if (player->kartstuff[k_boostcharge] == 0) - player->kartstuff[k_boostcharge] = cmd->latency; - - player->kartstuff[k_boostcharge]++; - } - else - player->kartstuff[k_boostcharge] = 0; - } - - // Increase your size while charging your engine. - if (leveltime < starttime+10) - { - player->mo->scalespeed = mapobjectscale/12; - player->mo->destscale = mapobjectscale + (player->kartstuff[k_boostcharge]*131); - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; - } - - // Determine the outcome of your charge. - if (leveltime > starttime && player->kartstuff[k_boostcharge]) - { - // Not even trying? - if (player->kartstuff[k_boostcharge] < 35) - { - if (player->kartstuff[k_boostcharge] > 17) - S_StartSound(player->mo, sfx_cdfm00); // chosen instead of a conventional skid because it's more engine-like - } - // Get an instant boost! - else if (player->kartstuff[k_boostcharge] <= 50) - { - player->kartstuff[k_startboost] = (50-player->kartstuff[k_boostcharge])+20; - - if (player->kartstuff[k_boostcharge] <= 36) - { - player->kartstuff[k_startboost] = 0; - K_DoSneaker(player, 0); - player->kartstuff[k_sneakertimer] = 70; // PERFECT BOOST!! - - if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) // Let everyone hear this one - S_StartSound(player->mo, sfx_s25f); - } - else - { - K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker - if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsDisplayPlayer(player)) - { - if (player->kartstuff[k_boostcharge] <= 40) - S_StartSound(player->mo, sfx_cdfm01); // You were almost there! - else - S_StartSound(player->mo, sfx_s23c); // Nope, better luck next time. - } - } - } - // You overcharged your engine? Those things are expensive!!! - else if (player->kartstuff[k_boostcharge] > 50) - { - player->powers[pw_nocontrol] = 40; - //S_StartSound(player->mo, sfx_kc34); - S_StartSound(player->mo, sfx_s3k83); - player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse - } - - player->kartstuff[k_boostcharge] = 0; - } -} - -void K_CheckSpectateStatus(void) -{ - UINT8 respawnlist[MAXPLAYERS]; - UINT8 i, j, numingame = 0, numjoiners = 0; - - // Maintain spectate wait timer - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN)) - players[i].kartstuff[k_spectatewait]++; - else - players[i].kartstuff[k_spectatewait] = 0; - } - - // No one's allowed to join - if (!cv_allowteamchange.value) - return; - - // Get the number of players in game, and the players to be de-spectated. - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - if (!players[i].spectator) - { - numingame++; - if (cv_ingamecap.value && numingame >= cv_ingamecap.value) // DON'T allow if you've hit the in-game player cap - return; - if (gamestate != GS_LEVEL) // Allow if you're not in a level - continue; - if (players[i].exiting) // DON'T allow if anyone's exiting - return; - if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet - continue; - if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in - return; - if (G_RaceGametype() && players[i].laps >= 2) // DON'T allow if the race is at 2 laps - return; - continue; - } - else if (!(players[i].pflags & PF_WANTSTOJOIN)) - continue; - - respawnlist[numjoiners++] = i; - } - - // literally zero point in going any further if nobody is joining - if (!numjoiners) - return; - - // Organize by spectate wait timer - if (cv_ingamecap.value) - { - UINT8 oldrespawnlist[MAXPLAYERS]; - memcpy(oldrespawnlist, respawnlist, numjoiners); - for (i = 0; i < numjoiners; i++) - { - UINT8 pos = 0; - INT32 ispecwait = players[oldrespawnlist[i]].kartstuff[k_spectatewait]; - - for (j = 0; j < numjoiners; j++) - { - INT32 jspecwait = players[oldrespawnlist[j]].kartstuff[k_spectatewait]; - if (j == i) - continue; - if (jspecwait > ispecwait) - pos++; - else if (jspecwait == ispecwait && j < i) - pos++; - } - - respawnlist[pos] = oldrespawnlist[i]; - } - } - - // Finally, we can de-spectate everyone! - for (i = 0; i < numjoiners; i++) - { - if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people? - break; - P_SpectatorJoinGame(&players[respawnlist[i]]); - } - - // Reset the match if you're in an empty server - if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+i >= 2)) // use previous i value - { - S_ChangeMusicInternal("chalng", false); // COME ON - mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD - } -} - -//} - -//{ SRB2kart HUD Code - -#define NUMPOSNUMS 10 -#define NUMPOSFRAMES 7 // White, three blues, three reds -#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple - -//{ Patch Definitions -static patch_t *kp_nodraw; - -static patch_t *kp_timesticker; -static patch_t *kp_timestickerwide; -static patch_t *kp_lapsticker; -static patch_t *kp_lapstickerwide; -static patch_t *kp_lapstickernarrow; -static patch_t *kp_splitlapflag; -static patch_t *kp_bumpersticker; -static patch_t *kp_bumperstickerwide; -static patch_t *kp_capsulesticker; -static patch_t *kp_capsulestickerwide; -static patch_t *kp_karmasticker; -static patch_t *kp_splitkarmabomb; -static patch_t *kp_timeoutsticker; - -static patch_t *kp_startcountdown[16]; -static patch_t *kp_racefinish[6]; - -static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; -static patch_t *kp_winnernum[NUMPOSFRAMES]; - -static patch_t *kp_facenum[MAXPLAYERS+1]; -static patch_t *kp_facehighlight[8]; - -static patch_t *kp_spbminimap; - -static patch_t *kp_ringsticker[2]; -static patch_t *kp_ringstickersplit[4]; -static patch_t *kp_ring[6]; -static patch_t *kp_smallring[6]; -static patch_t *kp_ringdebtminus; -static patch_t *kp_ringdebtminussmall; -static patch_t *kp_ringspblock[16]; -static patch_t *kp_ringspblocksmall[16]; - -static patch_t *kp_speedometersticker; -static patch_t *kp_speedometerlabel[4]; - -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_battlewin; -static patch_t *kp_battlecool; -static patch_t *kp_battlelose; -static patch_t *kp_battlewait; -static patch_t *kp_battleinfo; -static patch_t *kp_wanted; -static patch_t *kp_wantedsplit; -static patch_t *kp_wantedreticle; - -static patch_t *kp_itembg[4]; -static patch_t *kp_itemtimer[2]; -static patch_t *kp_itemmulsticker[2]; -static patch_t *kp_itemx; - -static patch_t *kp_superring[2]; -static patch_t *kp_sneaker[2]; -static patch_t *kp_rocketsneaker[2]; -static patch_t *kp_invincibility[13]; -static patch_t *kp_banana[2]; -static patch_t *kp_eggman[2]; -static patch_t *kp_orbinaut[5]; -static patch_t *kp_jawz[2]; -static patch_t *kp_mine[2]; -static patch_t *kp_ballhog[2]; -static patch_t *kp_selfpropelledbomb[2]; -static patch_t *kp_grow[2]; -static patch_t *kp_shrink[2]; -static patch_t *kp_thundershield[2]; -static patch_t *kp_bubbleshield[2]; -static patch_t *kp_flameshield[2]; -static patch_t *kp_hyudoro[2]; -static patch_t *kp_pogospring[2]; -static patch_t *kp_kitchensink[2]; -static patch_t *kp_sadface[2]; - -static patch_t *kp_check[6]; - -static patch_t *kp_rival[2]; - -static patch_t *kp_eggnum[4]; - -static patch_t *kp_flameshieldmeter[104][2]; -static patch_t *kp_flameshieldmeter_bg[16][2]; - -static patch_t *kp_fpview[3]; -static patch_t *kp_inputwheel[5]; - -static patch_t *kp_challenger[25]; - -static patch_t *kp_lapanim_lap[7]; -static patch_t *kp_lapanim_final[11]; -static patch_t *kp_lapanim_number[10][3]; -static patch_t *kp_lapanim_emblem[2]; -static patch_t *kp_lapanim_hand[3]; - -static patch_t *kp_yougotem; -static patch_t *kp_itemminimap; - -static patch_t *kp_alagles[10]; -static patch_t *kp_blagles[6]; - -static patch_t *kp_cpu; - -static patch_t *kp_nametagstem; - -void K_LoadKartHUDGraphics(void) -{ - INT32 i, j; - char buffer[9]; - - // Null Stuff - kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX); - - // Stickers - kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX); - kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX); - kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX); - kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX); - kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX); - kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX); - kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX); - kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX); - 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_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); - kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); - - // Starting countdown - kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); - kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); - kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX); - kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX); - kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX); - kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX); - kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX); - kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); - // Splitscreen - kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX); - kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX); - kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX); - kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); - kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX); - kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX); - kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); - kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); - - // Finish - kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX); - kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX); - // Splitscreen - kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX); - kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX); - // 2P splitscreen - kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX); - kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX); - - // Position numbers - sprintf(buffer, "K_POSNxx"); - for (i = 0; i < NUMPOSNUMS; i++) - { - buffer[6] = '0'+i; - for (j = 0; j < NUMPOSFRAMES; j++) - { - //sprintf(buffer, "K_POSN%d%d", i, j); - buffer[7] = '0'+j; - kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - } - - sprintf(buffer, "K_POSNWx"); - for (i = 0; i < NUMWINFRAMES; i++) - { - buffer[7] = '0'+i; - kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "OPPRNKxx"); - for (i = 0; i <= MAXPLAYERS; i++) - { - buffer[6] = '0'+(i/10); - buffer[7] = '0'+(i%10); - kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_CHILIx"); - for (i = 0; i < 8; i++) - { - buffer[7] = '0'+(i+1); - kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_spbminimap = W_CachePatchName("SPBMMAP", PU_HUDGFX); - - // Rings & Lives - kp_ringsticker[0] = W_CachePatchName("RNGBACKA", PU_HUDGFX); - kp_ringsticker[1] = W_CachePatchName("RNGBACKB", PU_HUDGFX); - - sprintf(buffer, "K_RINGx"); - for (i = 0; i < 6; i++) - { - buffer[6] = '0'+(i+1); - kp_ring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringdebtminus = W_CachePatchName("RDEBTMIN", PU_HUDGFX); - - sprintf(buffer, "SPBRNGxx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1) / 10); - buffer[7] = '0'+((i+1) % 10); - kp_ringspblock[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringstickersplit[0] = W_CachePatchName("SMRNGBGA", PU_HUDGFX); - kp_ringstickersplit[1] = W_CachePatchName("SMRNGBGB", PU_HUDGFX); - - sprintf(buffer, "K_SRINGx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '0'+(i+1); - kp_smallring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringdebtminussmall = W_CachePatchName("SRDEBTMN", PU_HUDGFX); - - sprintf(buffer, "SPBRGSxx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1) / 10); - buffer[7] = '0'+((i+1) % 10); - kp_ringspblocksmall[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Speedometer - kp_speedometersticker = W_CachePatchName("K_SPDMBG", PU_HUDGFX); - - sprintf(buffer, "K_SPDMLx"); - for (i = 0; i < 4; i++) - { - buffer[7] = '0'+(i+1); - kp_speedometerlabel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Extra ranking icons - kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX); - kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX); - 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); - - // Battle graphics - kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); - kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX); - kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX); - kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX); - kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX); - kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX); - kp_wantedsplit = W_CachePatchName("4PWANTED", PU_HUDGFX); - kp_wantedreticle = W_CachePatchName("MMAPWANT", PU_HUDGFX); - - // Kart Item Windows - kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX); - kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX); - kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX); - kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX); - kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX); - - kp_superring[0] = W_CachePatchName("K_ITRING", PU_HUDGFX); - kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX); - kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX); - - sprintf(buffer, "K_ITINVx"); - for (i = 0; i < 7; i++) - { - buffer[7] = '1'+i; - kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX); - kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX); - sprintf(buffer, "K_ITORBx"); - for (i = 0; i < 4; i++) - { - buffer[7] = '1'+i; - kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX); - kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX); - kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX); - kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX); - kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX); - kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX); - kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX); - kp_bubbleshield[0] = W_CachePatchName("K_ITBUBS", PU_HUDGFX); - kp_flameshield[0] = W_CachePatchName("K_ITFLMS", PU_HUDGFX); - kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX); - kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX); - kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX); - kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX); - - sprintf(buffer, "FSMFGxxx"); - for (i = 0; i < 104; i++) - { - buffer[5] = '0'+((i+1)/100); - buffer[6] = '0'+(((i+1)/10)%10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "FSMBG0xx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter_bg[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Splitscreen - kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX); - kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX); - kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX); - kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX); - - kp_superring[1] = W_CachePatchName("K_ISRING", PU_HUDGFX); - kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX); - kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX); - sprintf(buffer, "K_ISINVx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '1'+i; - kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX); - kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX); - kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX); - kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX); - kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX); - kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX); - kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX); - kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX); - kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX); - kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX); - kp_bubbleshield[1] = W_CachePatchName("K_ISBUBS", PU_HUDGFX); - kp_flameshield[1] = W_CachePatchName("K_ISFLMS", PU_HUDGFX); - kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX); - kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX); - kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX); - kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX); - - sprintf(buffer, "FSMFSxxx"); - for (i = 0; i < 104; i++) - { - buffer[5] = '0'+((i+1)/100); - buffer[6] = '0'+(((i+1)/10)%10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "FSMBS0xx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter_bg[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // CHECK indicators - sprintf(buffer, "K_CHECKx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '1'+i; - kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Rival indicators - sprintf(buffer, "K_RIVALx"); - for (i = 0; i < 2; i++) - { - buffer[7] = '1'+i; - kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Eggman warning numbers - sprintf(buffer, "K_EGGNx"); - for (i = 0; i < 4; i++) - { - buffer[6] = '0'+i; - kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // First person mode - kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX); - kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX); - kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX); - - // Input UI Wheel - sprintf(buffer, "K_WHEELx"); - for (i = 0; i < 5; i++) - { - buffer[7] = '0'+i; - kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // HERE COMES A NEW CHALLENGER - sprintf(buffer, "K_CHALxx"); - for (i = 0; i < 25; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Lap start animation - sprintf(buffer, "K_LAP0x"); - for (i = 0; i < 7; i++) - { - buffer[6] = '0'+(i+1); - kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPFxx"); - for (i = 0; i < 11; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPNxx"); - for (i = 0; i < 10; i++) - { - buffer[6] = '0'+i; - for (j = 0; j < 3; j++) - { - buffer[7] = '0'+(j+1); - kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - } - - sprintf(buffer, "K_LAPE0x"); - for (i = 0; i < 2; i++) - { - buffer[7] = '0'+(i+1); - kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPH0x"); - for (i = 0; i < 3; i++) - { - buffer[7] = '0'+(i+1); - kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); - kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); - - sprintf(buffer, "ALAGLESx"); - for (i = 0; i < 10; ++i) - { - buffer[7] = '0'+i; - kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "BLAGLESx"); - for (i = 0; i < 6; ++i) - { - buffer[7] = '0'+i; - kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); - - kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX); -} - -// For the item toggle menu -const char *K_GetItemPatch(UINT8 item, boolean tiny) -{ - switch (item) - { - case KITEM_SNEAKER: - case KRITEM_TRIPLESNEAKER: - return (tiny ? "K_ISSHOE" : "K_ITSHOE"); - case KITEM_ROCKETSNEAKER: - return (tiny ? "K_ISRSHE" : "K_ITRSHE"); - case KITEM_INVINCIBILITY: - return (tiny ? "K_ISINV1" : "K_ITINV1"); - case KITEM_BANANA: - case KRITEM_TRIPLEBANANA: - case KRITEM_TENFOLDBANANA: - return (tiny ? "K_ISBANA" : "K_ITBANA"); - case KITEM_EGGMAN: - return (tiny ? "K_ISEGGM" : "K_ITEGGM"); - case KITEM_ORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB1"); - case KITEM_JAWZ: - case KRITEM_DUALJAWZ: - return (tiny ? "K_ISJAWZ" : "K_ITJAWZ"); - case KITEM_MINE: - return (tiny ? "K_ISMINE" : "K_ITMINE"); - case KITEM_BALLHOG: - return (tiny ? "K_ISBHOG" : "K_ITBHOG"); - case KITEM_SPB: - return (tiny ? "K_ISSPB" : "K_ITSPB"); - case KITEM_GROW: - return (tiny ? "K_ISGROW" : "K_ITGROW"); - case KITEM_SHRINK: - return (tiny ? "K_ISSHRK" : "K_ITSHRK"); - case KITEM_THUNDERSHIELD: - return (tiny ? "K_ISTHNS" : "K_ITTHNS"); - case KITEM_BUBBLESHIELD: - return (tiny ? "K_ISBUBS" : "K_ITBUBS"); - case KITEM_FLAMESHIELD: - return (tiny ? "K_ISFLMS" : "K_ITFLMS"); - case KITEM_HYUDORO: - return (tiny ? "K_ISHYUD" : "K_ITHYUD"); - case KITEM_POGOSPRING: - return (tiny ? "K_ISPOGO" : "K_ITPOGO"); - case KITEM_SUPERRING: - return (tiny ? "K_ISRING" : "K_ITRING"); - case KITEM_KITCHENSINK: - return (tiny ? "K_ISSINK" : "K_ITSINK"); - case KRITEM_TRIPLEORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB3"); - case KRITEM_QUADORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB4"); - default: - return (tiny ? "K_ISSAD" : "K_ITSAD"); - } -} - -//} - -INT32 ITEM_X, ITEM_Y; // Item Window -INT32 TIME_X, TIME_Y; // Time Sticker -INT32 LAPS_X, LAPS_Y; // Lap Sticker -INT32 POSI_X, POSI_Y; // Position Number -INT32 FACE_X, FACE_Y; // Top-four Faces -INT32 STCD_X, STCD_Y; // Starting countdown -INT32 CHEK_Y; // CHECK graphic -INT32 MINI_X, MINI_Y; // Minimap -INT32 WANT_X, WANT_Y; // Battle WANTED poster - -// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN. -INT32 ITEM2_X, ITEM2_Y; -INT32 LAPS2_X, LAPS2_Y; -INT32 POSI2_X, POSI2_Y; - - -static void K_initKartHUD(void) -{ - /* - BASEVIDWIDTH = 320 - BASEVIDHEIGHT = 200 - - Item window graphic is 41 x 33 - - Time Sticker graphic is 116 x 11 - Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14 - Therefore, timestamp is 116 x 14 altogether - - Lap Sticker is 80 x 11 - Lap flag is 22 x 20 - Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14 - Therefore, lapstamp is 80 x 20 altogether - - Position numbers are 43 x 53 - - Faces are 32 x 32 - Faces draw downscaled at 16 x 16 - Therefore, the allocated space for them is 16 x 67 altogether - - ---- - - ORIGINAL CZ64 SPLITSCREEN: - - Item window: - if (!splitscreen) { ICONX = 139; ICONY = 20; } - else { ICONX = BASEVIDWIDTH-315; ICONY = 60; } - - Time: 236, STRINGY( 12) - Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189) - - */ - - // Single Screen (defaults) - // Item Window - ITEM_X = 5; // 5 - ITEM_Y = 5; // 5 - // Level Timer - TIME_X = BASEVIDWIDTH - 148; // 172 - TIME_Y = 9; // 9 - // Level Laps - LAPS_X = 9; // 9 - LAPS_Y = BASEVIDHEIGHT - 29; // 171 - // Position Number - POSI_X = BASEVIDWIDTH - 9; // 268 - POSI_Y = BASEVIDHEIGHT - 9; // 138 - // Top-Four Faces - FACE_X = 9; // 9 - FACE_Y = 92; // 92 - // Starting countdown - STCD_X = BASEVIDWIDTH/2; // 9 - STCD_Y = BASEVIDHEIGHT/2; // 92 - // CHECK graphic - CHEK_Y = BASEVIDHEIGHT; // 200 - // Minimap - MINI_X = BASEVIDWIDTH - 50; // 270 - MINI_Y = (BASEVIDHEIGHT/2)-16; // 84 - // Battle WANTED poster - WANT_X = BASEVIDWIDTH - 55; // 270 - WANT_Y = BASEVIDHEIGHT- 71; // 176 - - if (r_splitscreen) // Splitscreen - { - ITEM_X = 5; - ITEM_Y = 3; - - LAPS_Y = (BASEVIDHEIGHT/2)-24; - - POSI_Y = (BASEVIDHEIGHT/2)- 2; - - STCD_Y = BASEVIDHEIGHT/4; - - MINI_Y = (BASEVIDHEIGHT/2); - - if (r_splitscreen > 1) // 3P/4P Small Splitscreen - { - // 1P (top left) - ITEM_X = -9; - ITEM_Y = -8; - - LAPS_X = 3; - LAPS_Y = (BASEVIDHEIGHT/2)-12; - - POSI_X = 24; - POSI_Y = (BASEVIDHEIGHT/2)-26; - - // 2P (top right) - ITEM2_X = BASEVIDWIDTH-39; - ITEM2_Y = -8; - - LAPS2_X = BASEVIDWIDTH-43; - LAPS2_Y = (BASEVIDHEIGHT/2)-12; - - POSI2_X = BASEVIDWIDTH -4; - POSI2_Y = (BASEVIDHEIGHT/2)-26; - - // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom. - - STCD_X = BASEVIDWIDTH/4; - - MINI_X = (3*BASEVIDWIDTH/4); - MINI_Y = (3*BASEVIDHEIGHT/4); - - if (r_splitscreen > 2) // 4P-only - { - MINI_X = (BASEVIDWIDTH/2); - MINI_Y = (BASEVIDHEIGHT/2); - } - } - } - - if (timeinmap > 113) - hudtrans = cv_translucenthud.value; - else if (timeinmap > 105) - hudtrans = ((((INT32)timeinmap) - 105)*cv_translucenthud.value)/(113-105); - else - hudtrans = 0; -} - -INT32 K_calcSplitFlags(INT32 snapflags) -{ - INT32 splitflags = 0; - - if (r_splitscreen == 0) - return snapflags; - - if (stplyr != &players[displayplayers[0]]) - { - if (r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - { - splitflags |= V_SPLITSCREEN; - } - else if (r_splitscreen > 1) - { - if (stplyr == &players[displayplayers[2]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) - splitflags |= V_SPLITSCREEN; - if (stplyr == &players[displayplayers[1]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) - splitflags |= V_HORZSCREEN; - } - } - - if (splitflags & V_SPLITSCREEN) - snapflags &= ~V_SNAPTOTOP; - else - snapflags &= ~V_SNAPTOBOTTOM; - - if (r_splitscreen > 1) - { - if (splitflags & V_HORZSCREEN) - snapflags &= ~V_SNAPTOLEFT; - else - snapflags &= ~V_SNAPTORIGHT; - } - - return (splitflags|snapflags); -} - -static void K_drawKartItem(void) -{ - // ITEM_X = BASEVIDWIDTH-50; // 270 - // ITEM_Y = 24; // 24 - - // Why write V_DrawScaledPatch calls over and over when they're all the same? - // Set to 'no item' just in case. - const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); - patch_t *localpatch = kp_nodraw; - patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); - patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); - INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... - //INT32 splitflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); - const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2); - INT32 itembar = 0; - INT32 maxl = 0; // itembar's normal highest value - const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); - UINT8 localcolor = SKINCOLOR_NONE; - SINT8 colormode = TC_RAINBOW; - UINT8 *colmap = NULL; - boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff - - if (stplyr->kartstuff[k_itemroulette]) - { - if (stplyr->skincolor) - localcolor = stplyr->skincolor; - - switch((stplyr->kartstuff[k_itemroulette] % (15*3)) / 3) - { - // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette - case 0: // Sneaker - localpatch = kp_sneaker[offset]; - //localcolor = SKINCOLOR_RASPBERRY; - break; - case 1: // Banana - localpatch = kp_banana[offset]; - //localcolor = SKINCOLOR_YELLOW; - break; - case 2: // Orbinaut - localpatch = kp_orbinaut[3+offset]; - //localcolor = SKINCOLOR_STEEL; - break; - case 3: // Mine - localpatch = kp_mine[offset]; - //localcolor = SKINCOLOR_JET; - break; - case 4: // Grow - localpatch = kp_grow[offset]; - //localcolor = SKINCOLOR_TEAL; - break; - case 5: // Hyudoro - localpatch = kp_hyudoro[offset]; - //localcolor = SKINCOLOR_STEEL; - break; - case 6: // Rocket Sneaker - localpatch = kp_rocketsneaker[offset]; - //localcolor = SKINCOLOR_TANGERINE; - break; - case 7: // Jawz - localpatch = kp_jawz[offset]; - //localcolor = SKINCOLOR_JAWZ; - break; - case 8: // Self-Propelled Bomb - localpatch = kp_selfpropelledbomb[offset]; - //localcolor = SKINCOLOR_JET; - break; - case 9: // Shrink - localpatch = kp_shrink[offset]; - //localcolor = SKINCOLOR_ORANGE; - break; - case 10: // Invincibility - localpatch = localinv; - //localcolor = SKINCOLOR_GREY; - break; - case 11: // Eggman Monitor - localpatch = kp_eggman[offset]; - //localcolor = SKINCOLOR_ROSE; - break; - case 12: // Ballhog - localpatch = kp_ballhog[offset]; - //localcolor = SKINCOLOR_LILAC; - break; - case 13: // Thunder Shield - localpatch = kp_thundershield[offset]; - //localcolor = SKINCOLOR_CYAN; - break; - case 14: // Super Ring - localpatch = kp_superring[offset]; - //localcolor = SKINCOLOR_GOLD; - break; - /*case 15: // Pogo Spring - localpatch = kp_pogospring[offset]; - localcolor = SKINCOLOR_TANGERINE; - break; - case 16: // Kitchen Sink - localpatch = kp_kitchensink[offset]; - localcolor = SKINCOLOR_STEEL; - break;*/ - default: - break; - } - } - else - { - // I'm doing this a little weird and drawing mostly in reverse order - // The only actual reason is to make sneakers line up this way in the code below - // This shouldn't have any actual baring over how it functions - // Hyudoro is first, because we're drawing it on top of the player's current item - if (stplyr->kartstuff[k_stolentimer] > 0) - { - if (leveltime & 2) - localpatch = kp_hyudoro[offset]; - else - localpatch = kp_nodraw; - } - else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2)) - { - localpatch = kp_hyudoro[offset]; - } - else if (stplyr->kartstuff[k_eggmanexplode] > 1) - { - if (leveltime & 1) - localpatch = kp_eggman[offset]; - else - localpatch = kp_nodraw; - } - else if (stplyr->kartstuff[k_rocketsneakertimer] > 1) - { - itembar = stplyr->kartstuff[k_rocketsneakertimer]; - maxl = (itemtime*3) - barlength; - - if (leveltime & 1) - localpatch = kp_rocketsneaker[offset]; - else - localpatch = kp_nodraw; - } - else if (stplyr->kartstuff[k_sadtimer] > 0) - { - if (leveltime & 2) - localpatch = kp_sadface[offset]; - else - localpatch = kp_nodraw; - } - else - { - if (stplyr->kartstuff[k_itemamount] <= 0) - return; - - switch(stplyr->kartstuff[k_itemtype]) - { - case KITEM_SNEAKER: - localpatch = kp_sneaker[offset]; - break; - case KITEM_ROCKETSNEAKER: - localpatch = kp_rocketsneaker[offset]; - break; - case KITEM_INVINCIBILITY: - localpatch = localinv; - localbg = kp_itembg[offset+1]; - break; - case KITEM_BANANA: - localpatch = kp_banana[offset]; - break; - case KITEM_EGGMAN: - localpatch = kp_eggman[offset]; - break; - case KITEM_ORBINAUT: - localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))]; - break; - case KITEM_JAWZ: - localpatch = kp_jawz[offset]; - break; - case KITEM_MINE: - localpatch = kp_mine[offset]; - break; - case KITEM_BALLHOG: - localpatch = kp_ballhog[offset]; - break; - case KITEM_SPB: - localpatch = kp_selfpropelledbomb[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_GROW: - localpatch = kp_grow[offset]; - break; - case KITEM_SHRINK: - localpatch = kp_shrink[offset]; - break; - case KITEM_THUNDERSHIELD: - localpatch = kp_thundershield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_BUBBLESHIELD: - localpatch = kp_bubbleshield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_FLAMESHIELD: - localpatch = kp_flameshield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_HYUDORO: - localpatch = kp_hyudoro[offset]; - break; - case KITEM_POGOSPRING: - localpatch = kp_pogospring[offset]; - break; - case KITEM_SUPERRING: - localpatch = kp_superring[offset]; - break; - case KITEM_KITCHENSINK: - localpatch = kp_kitchensink[offset]; - break; - case KITEM_SAD: - localpatch = kp_sadface[offset]; - break; - default: - return; - } - - if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1)) - localpatch = kp_nodraw; - } - - if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) - { - colormode = TC_BLINK; - - switch (stplyr->karthud[khud_itemblinkmode]) - { - case 2: - localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - break; - case 1: - localcolor = SKINCOLOR_RED; - break; - default: - localcolor = SKINCOLOR_WHITE; - break; - } - } - } - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = ITEM_X; - fy = ITEM_Y; - fflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); - } - else // now we're having a fun game. - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = ITEM_X; - fy = ITEM_Y; - fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = ITEM2_X; - fy = ITEM2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P4 to the bottom - flipamount = true; - } - } - - if (localcolor != SKINCOLOR_NONE) - colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE); - - V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, localbg); - - // Then, the numbers: - if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette]) - { - V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. - V_DrawFixedPatch(fx<kartstuff[k_itemamount])); - else - V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|fflags, va("x%d", stplyr->kartstuff[k_itemamount])); - else - { - V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|fflags, kp_itemx); - V_DrawKartString(fx+38, fy+36, V_HUDTRANS|fflags, va("%d", stplyr->kartstuff[k_itemamount])); - } - } - else - V_DrawFixedPatch(fx< 2) - { - V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one - if (height == 2) - V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside - V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine - } - } - - // Quick Eggman numbers - if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/) - V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|fflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]); - - if (stplyr->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && stplyr->kartstuff[k_flamelength] > 0) - { - INT32 numframes = 104; - INT32 absolutemax = 16 * flameseg; - INT32 flamemax = stplyr->kartstuff[k_flamelength] * flameseg; - INT32 flamemeter = min(stplyr->kartstuff[k_flamemeter], flamemax); - - INT32 bf = 16 - stplyr->kartstuff[k_flamelength]; - INT32 ff = numframes - ((flamemeter * numframes) / absolutemax); - INT32 fmin = (8 * (bf-1)); - - INT32 xo = 6, yo = 4; - INT32 flip = 0; - - if (offset) - { - xo++; - - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // Flip for P1 and P3 (yes, that's correct) - { - xo -= 62; - flip = V_FLIP; - } - } - - if (ff < fmin) - ff = fmin; - - if (bf >= 0 && bf < 16) - V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter_bg[bf][offset]); - - if (ff >= 0 && ff < numframes && stplyr->kartstuff[k_flamemeter] > 0) - { - if ((stplyr->kartstuff[k_flamemeter] > flamemax) && (leveltime & 1)) - { - UINT8 *fsflash = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); - V_DrawMappedPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset], fsflash); - } - else - { - V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset]); - } - } - } -} - -void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode) -{ - // TIME_X = BASEVIDWIDTH-124; // 196 - // TIME_Y = 6; // 6 - - tic_t worktime; - - INT32 splitflags = 0; - if (!mode) - { - splitflags = V_HUDTRANS|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTORIGHT); - if (cv_timelimit.value && timelimitintics > 0) - { - if (drawtime >= timelimitintics) - drawtime = 0; - else - drawtime = timelimitintics - drawtime; - } - } - - V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide)); - - TX += 33; - - worktime = drawtime/(60*TICRATE); - - if (mode && !drawtime) - V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--")); - else if (worktime < 100) // 99:99:99 only - { - // zero minute - if (worktime < 10) - { - V_DrawKartString(TX, TY+3, splitflags, va("0")); - // minutes time 0 __ __ - V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime)); - } - // minutes time 0 __ __ - else - V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime)); - - // apostrophe location _'__ __ - V_DrawKartString(TX+24, TY+3, splitflags, va("'")); - - worktime = (drawtime/TICRATE % 60); - - // zero second _ 0_ __ - if (worktime < 10) - { - V_DrawKartString(TX+36, TY+3, splitflags, va("0")); - // seconds time _ _0 __ - V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime)); - } - // zero second _ 00 __ - else - V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime)); - - // quotation mark location _ __"__ - V_DrawKartString(TX+60, TY+3, splitflags, va("\"")); - - worktime = G_TicsToCentiseconds(drawtime); - - // zero tick _ __ 0_ - if (worktime < 10) - { - V_DrawKartString(TX+72, TY+3, splitflags, va("0")); - // tics _ __ _0 - V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime)); - } - // zero tick _ __ 00 - else - V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime)); - } - else if ((drawtime/TICRATE) & 1) - V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); - - if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! - { - INT32 workx = TX + 96, worky = TY+18; - SINT8 curemb = 0; - patch_t *emblempic[3] = {NULL, NULL, NULL}; - UINT8 *emblemcol[3] = {NULL, NULL, NULL}; - - emblem_t *emblem = M_GetLevelEmblems(emblemmap); - while (emblem) - { - char targettext[9]; - - switch (emblem->type) - { - case ET_TIME: - { - static boolean canplaysound = true; - tic_t timetoreach = emblem->var; - - if (emblem->collected) - { - emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE); - emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE); - if (++curemb == 3) - break; - goto bademblem; - } - - snprintf(targettext, 9, "%i'%02i\"%02i", - G_TicsToMinutes(timetoreach, false), - G_TicsToSeconds(timetoreach), - G_TicsToCentiseconds(timetoreach)); - - if (!mode) - { - if (stplyr->realtime > timetoreach) - { - splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF; - if (canplaysound) - { - S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks - canplaysound = false; - } - } - else if (!canplaysound) - canplaysound = true; - } - - targettext[8] = 0; - } - break; - default: - goto bademblem; - } - - V_DrawRightAlignedString(workx, worky, splitflags, targettext); - workx -= 67; - V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE)); - - break; - - bademblem: - emblem = M_GetLevelEmblems(-1); - } - - if (!mode) - splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS; - while (curemb--) - { - workx -= 12; - V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]); - } - } -} - -static void K_DrawKartPositionNum(INT32 num) -{ - // POSI_X = BASEVIDWIDTH - 51; // 269 - // POSI_Y = BASEVIDHEIGHT- 64; // 136 - - boolean win = (stplyr->exiting && num == 1); - //INT32 X = POSI_X; - INT32 W = SHORT(kp_positionnum[0][0]->width); - fixed_t scale = FRACUNIT; - patch_t *localpatch = kp_positionnum[0][0]; - //INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT); - INT32 fx = 0, fy = 0, fflags = 0; - boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun. - boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen. - boolean overtake = false; - - if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting) - { - scale *= 2; - overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw. - } - if (r_splitscreen) - scale /= 2; - - W = FixedMul(W<>FRACBITS; - - // pain and suffering defined below - if (!r_splitscreen) - { - fx = POSI_X; - fy = BASEVIDHEIGHT - 8; - fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; - } - else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different. - { - fx = POSI_X; - if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap. - { - fy = 30; - fflags = V_SNAPTOTOP|V_SNAPTORIGHT; - if (overtake) - flipvdraw = true; // make sure overtaking doesn't explode us - } - else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap. - { - fy = BASEVIDHEIGHT - 8; - fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; - } - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = POSI_X; - fy = POSI_Y; - fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - flipdraw = true; - if (num && num >= 10) - fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen. - } - else // else, that means we're P2 or P4. - { - fx = POSI2_X; - fy = POSI2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - } - } - - // Special case for 0 - if (!num) - { - V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers - - // Draw the number - while (num) - { - if (win) // 1st place winner? You get rainbows!! - localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; - else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won - { - // Alternate frame every three frames - switch (leveltime % 9) - { - case 1: case 2: case 3: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][4]; - else - localpatch = kp_positionnum[num % 10][1]; - break; - case 4: case 5: case 6: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][5]; - else - localpatch = kp_positionnum[num % 10][2]; - break; - case 7: case 8: case 9: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][6]; - else - localpatch = kp_positionnum[num % 10][3]; - break; - default: - localpatch = kp_positionnum[num % 10][0]; - break; - } - } - else - localpatch = kp_positionnum[num % 10][0]; - - V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|fflags, localpatch, NULL); - // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen. - // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either. - - fx -= W; - num /= 10; - } -} - -static boolean K_drawKartPositionFaces(void) -{ - // FACE_X = 15; // 15 - // FACE_Y = 72; // 72 - - INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one - INT32 i, j, ranklines, strank = -1; - boolean completed[MAXPLAYERS]; - INT32 rankplayer[MAXPLAYERS]; - INT32 bumperx, numplayersingame = 0; - UINT8 *colormap; - - ranklines = 0; - memset(completed, 0, sizeof (completed)); - memset(rankplayer, 0, sizeof (rankplayer)); - - for (i = 0; i < MAXPLAYERS; i++) - { - rankplayer[i] = -1; - - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - numplayersingame++; - } - - if (numplayersingame <= 1) - return true; - -#ifdef HAVE_BLUA - if (!LUA_HudEnabled(hud_minirankings)) - return false; // Don't proceed but still return true for free play above if HUD is disabled. -#endif - - for (j = 0; j < numplayersingame; j++) - { - UINT8 lowestposition = MAXPLAYERS+1; - for (i = 0; i < MAXPLAYERS; i++) - { - if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - if (players[i].kartstuff[k_position] >= lowestposition) - continue; - - rankplayer[ranklines] = i; - lowestposition = players[i].kartstuff[k_position]; - } - - i = rankplayer[ranklines]; - - completed[i] = true; - - if (players+i == stplyr) - strank = ranklines; - - //if (ranklines == 5) - //break; // Only draw the top 5 players -- we do this a different way now... - - ranklines++; - } - - if (ranklines < 5) - Y -= (9*ranklines); - else - Y -= (9*5); - - if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) - { - i = 0; - if (ranklines > 5) // could be both... - ranklines = 5; - } - else if (strank+3 > ranklines) // too close to the bottom? - { - i = ranklines - 5; - if (i < 0) - i = 0; - } - else - { - i = strank-2; - ranklines = strank+3; - } - - for (; i < ranklines; i++) - { - if (!playeringame[rankplayer[i]]) continue; - if (players[rankplayer[i]].spectator) continue; - if (!players[rankplayer[i]].mo) continue; - - bumperx = FACE_X+19; - - if (players[rankplayer[i]].mo->color) - { - colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); - if (players[rankplayer[i]].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); - - V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap); - -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_battlebumpers)) - { -#endif - if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 0) - { - V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[0], colormap); - for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++) - { - bumperx += 5; - V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[1], colormap); - } - } -#ifdef HAVE_BLUA - } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating: -#endif - } - - if (i == strank) - V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); - - if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0) - V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SNAPTOLEFT, kp_ranknobumpers); - else - { - INT32 pos = players[rankplayer[i]].kartstuff[k_position]; - if (pos < 0 || pos > MAXPLAYERS) - pos = 0; - // Draws the little number over the face - V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SNAPTOLEFT, kp_facenum[pos]); - } - - Y += 18; - } - - return false; -} - -// -// HU_DrawTabRankings -- moved here to take advantage of kart stuff! -// -void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) -{ - static tic_t alagles_timer = 9; - INT32 i, rightoffset = 240; - const UINT8 *colormap; - INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; - int y2; - - //this function is designed for 9 or less score lines only - //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up - - V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice! - if (scorelines > 8) - { - V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides. - V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom. - rightoffset = (BASEVIDWIDTH/2) - 4 - x; - } - - for (i = 0; i < scorelines; i++) - { - char strtime[MAXPLAYERNAME+1]; - - if (players[tab[i].num].spectator || !players[tab[i].num].mo) - continue; //ignore them. - - if (netgame) // don't draw ping offline - { - if (players[tab[i].num].bot) - { - V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu); - } - else if (tab[i].num != serverplayer || !server_lagless) - { - HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); - } - } - - STRBUFCPY(strtime, tab[i].name); - - y2 = y; - - if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) - { - y2 = ( y - 4 ); - - V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); - // every 70 tics - if (( leveltime % 70 ) == 0) - { - alagles_timer = 9; - } - if (alagles_timer > 0) - { - V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); - if (( leveltime % 2 ) == 0) - alagles_timer--; - } - else - V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); - - y2 += SHORT (kp_alagles[0]->height) + 1; - } - - if (scorelines > 8) - V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); - else - V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); - - if (players[tab[i].num].mo->color) - { - colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); - if (players[tab[i].num].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); - - V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap); - /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 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++) - { - bumperx += 5; - V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap); - } - }*/ - } - - if (tab[i].num == whiteplayer) - V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); - - if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0) - V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); - else - { - INT32 pos = players[tab[i].num].kartstuff[k_position]; - if (pos < 0 || pos > MAXPLAYERS) - pos = 0; - // Draws the little number over the face - V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]); - } - - if (G_RaceGametype()) - { -#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) - if (scorelines > 8) - { - if (players[tab[i].num].exiting) - V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); - else if (players[tab[i].num].pflags & PF_TIMEOVER) - V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST."); - else if (circuitmap) - V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count)); - } - else - { - if (players[tab[i].num].exiting) - V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime)); - else if (players[tab[i].num].pflags & PF_TIMEOVER) - V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST."); - else if (circuitmap) - V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count)); - } -#undef timestring - } - else - V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); - - y += 18; - if (i == 7) - { - y = 33; - x = (BASEVIDWIDTH/2) + 4; - } - } -} - -#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) - -static void K_drawKartLapsAndRings(void) -{ - const boolean uselives = G_GametypeUsesLives(); - SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - UINT8 rn[2]; - INT32 ringflip = 0; - UINT8 *ringmap = NULL; - boolean colorring = false; - INT32 ringx = 0; - - rn[0] = ((abs(stplyr->kartstuff[k_rings]) / 10) % 10); - rn[1] = (abs(stplyr->kartstuff[k_rings]) % 10); - - if (stplyr->kartstuff[k_rings] <= 0 && (leveltime/5 & 1)) // In debt - { - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); - colorring = true; - } - else if (stplyr->kartstuff[k_rings] >= 20) // Maxed out - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); - - if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) - { - ringflip = V_FLIP; - ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; - ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); - } - - if (r_splitscreen > 1) - { - INT32 fx = 0, fy = 0, fr = 0; - INT32 flipflag = 0; - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = LAPS_X; - fy = LAPS_Y; - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = LAPS_X; - fy = LAPS_Y; - splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = LAPS2_X; - fy = LAPS2_Y; - splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipflag = V_FLIP; // make the string right aligned and other shit - } - } - - fr = fx; - - // Laps - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - - V_DrawScaledPatch(fx, fy, V_HUDTRANS|splitflags, kp_splitlapflag); - V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); - - if (cv_numlaps.value >= 10) - { - UINT8 ln[2]; - ln[0] = ((stplyr->laps / 10) % 10); - ln[1] = (stplyr->laps % 10); - - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((abs(cv_numlaps.value) / 10) % 10); - ln[1] = (abs(cv_numlaps.value) % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_numlaps.value) % 10]); - } - - // Rings - if (!uselives) - { - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]); - if (flipflag) - fr += 15; - } - else - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[0]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - - V_DrawMappedPatch(fr+ringx, fy-13, V_HUDTRANS|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL)); - - if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt - V_DrawMappedPatch(fr+7, fy-10, V_HUDTRANS|splitflags, kp_ringdebtminussmall, ringmap); - - V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); - V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); - - // SPB ring lock - if (stplyr->kartstuff[k_ringlock]) - V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); - - // Lives - if (uselives) - { - UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); - V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow - } - } - else - { - // Laps - V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_lapsticker); - - if (stplyr->exiting) - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN"); - else - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); - - // Rings - if (!uselives) - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]); - else - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]); - - V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); - - if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt - { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringdebtminus, ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); - } - else - { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); - } - - // SPB ring lock - if (stplyr->kartstuff[k_ringlock]) - V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); - - // Lives - if (uselives) - { - UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap); - V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow - } - } -} - -#undef RINGANIM_NUMFRAMES -#undef RINGANIM_FLIPFRAME - -static void K_drawKartSpeedometer(void) -{ - static fixed_t convSpeed; - UINT8 labeln = 0; - UINT8 numbers[3]; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - UINT8 battleoffset = 0; - - if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! - { - switch (cv_kartspeedometer.value) - { - case 1: // Sonic Drift 2 style percentage - default: - convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) - labeln = 0; - break; - case 2: // Kilometers - convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058 - labeln = 1; - break; - case 3: // Miles - convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774 - labeln = 2; - break; - case 4: // Fracunits - convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT; // 1.0. duh. - labeln = 3; - break; - } - } - - // Don't overflow - if (convSpeed > 999) - convSpeed = 999; - - numbers[0] = ((convSpeed / 100) % 10); - numbers[1] = ((convSpeed / 10) % 10); - numbers[2] = (convSpeed % 10); - - if (G_BattleGametype()) - battleoffset = 8; - - V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometersticker); - V_DrawScaledPatch(LAPS_X+7, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[0]]); - V_DrawScaledPatch(LAPS_X+13, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[1]]); - V_DrawScaledPatch(LAPS_X+19, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[2]]); - V_DrawScaledPatch(LAPS_X+29, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometerlabel[labeln]); -} - -static void K_drawKartBumpersOrKarma(void) -{ - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - - if (r_splitscreen > 1) - { - INT32 fx = 0, fy = 0; - INT32 flipflag = 0; - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = LAPS_X; - fy = LAPS_Y; - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = LAPS_X; - fy = LAPS_Y; - splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = LAPS2_X; - fy = LAPS2_Y; - splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipflag = V_FLIP; // make the string right aligned and other shit - } - } - - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); - - if (battlecapsules) - { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankcapsule, NULL); - - if (numtargets > 9 || maptargets > 9) - { - UINT8 ln[2]; - ln[0] = ((numtargets / 10) % 10); - ln[1] = (numtargets % 10); - - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((maptargets / 10) % 10); - ln[1] = (maptargets % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[numtargets % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[maptargets % 10]); - } - } - else - { - if (stplyr->kartstuff[k_bumper] <= 0) - { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[2]); - } - else - { - INT32 maxbumper = K_StartingBumperCount(); - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(maxbumper) % 10]); - } - } - } - } - else - { - if (battlecapsules) - { - if (numtargets > 9 && maptargets > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulestickerwide, NULL); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulesticker, NULL); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", numtargets, maptargets)); - } - else - { - if (stplyr->kartstuff[k_bumper] <= 0) - { - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); - } - else - { - INT32 maxbumper = K_StartingBumperCount(); - - if (stplyr->kartstuff[k_bumper] > 9 && maxbumper > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap); - - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], maxbumper)); - } - } - } -} - -static void K_drawKartWanted(void) -{ - UINT8 i, numwanted = 0; - UINT8 *colormap = NULL; - INT32 basex = 0, basey = 0; - - if (stplyr != &players[displayplayers[0]]) - return; - - for (i = 0; i < 4; i++) - { - if (battlewanted[i] == -1) - break; - numwanted++; - } - - if (numwanted <= 0) - return; - - // set X/Y coords depending on splitscreen. - if (r_splitscreen < 3) // 1P and 2P use the same code. - { - basex = WANT_X; - basey = WANT_Y; - if (r_splitscreen == 2) - { - basey += 16; // slight adjust for 3P - basex -= 6; - } - } - else if (r_splitscreen == 3) // 4P splitscreen... - { - basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen - basey = BASEVIDHEIGHT - 55; - //basey2 = 4; - } - - if (battlewanted[0] != -1) - colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE); - V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap); - /*if (basey2) - V_DrawFixedPatch(basex< 1 ? 13 : 8), y = basey+(r_splitscreen > 1 ? 16 : 21); - fixed_t scale = FRACUNIT/2; - player_t *p = &players[battlewanted[i]]; - - if (battlewanted[i] == -1) - break; - - if (numwanted == 1) - scale = FRACUNIT; - else - { - if (i & 1) - x += 16; - if (i > 1) - y += 16; - } - - if (players[battlewanted[i]].skincolor) - { - colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE); - V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap); - /*if (basey2) // again with 4p stuff - V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);*/ - } - } -} - -static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) -{ - const INT32 swhalf = (BASEVIDWIDTH / 2); - const fixed_t swhalffixed = swhalf * FRACUNIT; - - const INT32 shhalf = (BASEVIDHEIGHT / 2); - const fixed_t shhalffixed = shhalf * FRACUNIT; - - INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); - fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); - fixed_t factor = INT32_MAX; - - if (abs(anglediff) > ANGLE_90) - { - if (hud_x != NULL) - { - *hud_x = -BASEVIDWIDTH * FRACUNIT; - } - - if (hud_y != NULL) - { - *hud_y = -BASEVIDWIDTH * FRACUNIT; - } - - //*hud_scale = FRACUNIT; - return; - } - - factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); - -#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) - - if (hud_x != NULL) - { - *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; - - if (encoremode) - { - *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; - } - - if (r_splitscreen >= 2) - { - *hud_x /= 2; - - if (camnum & 1) - { - *hud_x += swhalffixed; - } - } - } - - if (hud_y != NULL) - { - *hud_y = campos->z - point->z; - *hud_y = FixedDiv(*hud_y, FixedMul(factor, distance)); - *hud_y = (*hud_y * swhalf) + shhalffixed; - *hud_y = *hud_y + NEWTAN(camaim) * swhalf; - - if (r_splitscreen >= 1) - { - *hud_y /= 2; - - if (camnum > 1) - { - *hud_y += shhalffixed; - } - } - } - - //*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance)); - -#undef NEWTAN -} - -static void K_drawKartPlayerCheck(void) -{ - const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - camera_t *thiscam; - vertex_t c; - UINT8 cnum = 0; - UINT8 i; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); - - if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) - { - return; - } - - if (stplyr->spectator || stplyr->awayviewtics) - { - return; - } - - if (stplyr->cmd.buttons & BT_LOOKBACK) - { - return; - } - - if (r_splitscreen) - { - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - - thiscam = &camera[cnum]; - - c.x = stplyr->mo->x; - c.y = stplyr->mo->y; - c.z = stplyr->mo->z; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *checkplayer = &players[i]; - fixed_t distance = maxdistance+1; - UINT8 *colormap = NULL; - UINT8 pnum = 0; - fixed_t x = 0; - vertex_t v; - - if (!playeringame[i] || checkplayer->spectator) - { - // Not in-game - continue; - } - - if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo)) - { - // No object - continue; - } - - if (checkplayer == stplyr) - { - // This is you! - continue; - } - - v.x = checkplayer->mo->x; - v.y = checkplayer->mo->y; - v.z = checkplayer->mo->z; - - distance = R_PointToDist2(c.x, c.y, v.x, v.y); - - if (distance > maxdistance) - { - // Too far away - continue; - } - - if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) - { - pnum++; // white frames - } - - if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0) - { - pnum += 4; - } - else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer]) - { - pnum += 2; - } - - K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); - - colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); - V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); - } -} - -static void K_drawKartNameTags(void) -{ - const fixed_t maxdistance = 8192*mapobjectscale; - camera_t *thiscam; - vertex_t c; - UINT8 cnum = 0; - UINT8 i; - - if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) - { - return; - } - - if (stplyr->awayviewtics) - { - return; - } - - if (r_splitscreen) - { - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - - thiscam = &camera[cnum]; - - c.x = thiscam->x; - c.y = thiscam->y; - c.z = thiscam->z; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *ntplayer = &players[i]; - fixed_t distance = maxdistance+1; - - fixed_t x = -BASEVIDWIDTH * FRACUNIT; - fixed_t y = -BASEVIDWIDTH * FRACUNIT; - - vertex_t v; - UINT8 j; - - if (!playeringame[i] || ntplayer->spectator) - { - // Not in-game - continue; - } - - if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) - { - // No object - continue; - } - - if (!P_CheckSight(stplyr->mo, ntplayer->mo)) - { - // Can't see - continue; - } - - for (j = 0; j <= r_splitscreen; j++) - { - if (ntplayer == &players[displayplayers[j]]) - { - break; - } - } - - if (j <= r_splitscreen) - { - // Is a player that's being shown on this computer - continue; - } - - v.x = ntplayer->mo->x; - v.y = ntplayer->mo->y; - v.z = ntplayer->mo->z; - - if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) - { - v.z += ntplayer->mo->height; - } - - distance = R_PointToDist2(c.x, c.y, v.x, v.y); - - if (distance > maxdistance) - { - // Too far away - continue; - } - - K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); - - if (x == -BASEVIDWIDTH * FRACUNIT) - { - // Off-screen - continue; - } - - if (ntplayer->bot) - { - if (ntplayer->botvars.rival == true) - { - UINT8 blink = ((leveltime / 7) & 1); - V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); - } - } - else if (netgame) - { - if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-2) - && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+2)) - { - INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); - INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); - UINT8 *colormap = V_GetStringColormap(clr); - INT32 barx = 0, bary = 0, barw = 0; - - // Since there's no "V_DrawFixedFill", and I don't feel like making it, - // fuck it, we're gonna just V_NOSCALESTART hack it - barw = (namelen * vid.dupx); - - barx = (x * vid.dupx) / FRACUNIT; - bary = (y * vid.dupy) / FRACUNIT; - - barx += (6 * vid.dupx); - bary -= (16 * vid.dupx); - - // Center it if necessary - if (vid.width != BASEVIDWIDTH * vid.dupx) - { - barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; - } - - if (vid.height != BASEVIDHEIGHT * vid.dupy) - { - bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; - } - - // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. - V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); - V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); - // END DRAWFILL DUMBNESS - - // Draw the stem - V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); - - // Draw the name itself - V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); - } - } - } -} - -static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) -{ - // amnum xpos & ypos are the icon's speed around the HUD. - // The number being divided by is for how fast it moves. - // The higher the number, the slower it moves. - - // am xpos & ypos are the icon's starting position. Withouht - // it, they wouldn't 'spawn' on the top-right side of the HUD. - - fixed_t amnumxpos, amnumypos; - INT32 amxpos, amypos; - - node_t *bsp = &nodes[numnodes-1]; - fixed_t maxx, minx, maxy, miny; - - fixed_t mapwidth, mapheight; - fixed_t xoffset, yoffset; - fixed_t xscale, yscale, zoom; - - maxx = maxy = INT32_MAX; - minx = miny = INT32_MIN; - minx = bsp->bbox[0][BOXLEFT]; - maxx = bsp->bbox[0][BOXRIGHT]; - miny = bsp->bbox[0][BOXBOTTOM]; - maxy = bsp->bbox[0][BOXTOP]; - - if (bsp->bbox[1][BOXLEFT] < minx) - minx = bsp->bbox[1][BOXLEFT]; - if (bsp->bbox[1][BOXRIGHT] > maxx) - maxx = bsp->bbox[1][BOXRIGHT]; - if (bsp->bbox[1][BOXBOTTOM] < miny) - miny = bsp->bbox[1][BOXBOTTOM]; - if (bsp->bbox[1][BOXTOP] > maxy) - maxy = bsp->bbox[1][BOXTOP]; - - // You might be wondering why these are being bitshift here - // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... - // map boundaries and sizes will ALWAYS be whole numbers thankfully - // later calculations take into consideration that these are actually not in terms of FRACUNIT though - minx >>= FRACBITS; - maxx >>= FRACBITS; - miny >>= FRACBITS; - maxy >>= FRACBITS; - - mapwidth = maxx - minx; - mapheight = maxy - miny; - - // These should always be small enough to be bitshift back right now - xoffset = (minx + mapwidth/2)<width, mapwidth); - yscale = FixedDiv(AutomapPic->height, mapheight); - zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); - - amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); - amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); - - if (encoremode) - amnumxpos = -amnumxpos; - - amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width/2); - y = MINI_Y - (AutomapPic->height/2); - - if (timeinmap > 105) - { - minimaptrans = cv_kartminimap.value; - if (timeinmap <= 113) - minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); - if (!minimaptrans) - return; - } - else - return; - - minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); - else - V_DrawScaledPatch(x, y, splitflags, AutomapPic); - - if (!(r_splitscreen == 2)) - { - splitflags &= ~minimaptrans; - splitflags |= V_HUDTRANSHALF; - } - - // let offsets transfer to the heads, too! - if (encoremode) - x += SHORT(AutomapPic->leftoffset); - else - x -= SHORT(AutomapPic->leftoffset); - y -= SHORT(AutomapPic->topoffset); - - // Draw the super item in Battle - if (G_BattleGametype() && battleovertime.enabled) - { - if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) - { - const INT32 prevsplitflags = splitflags; - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); - K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); - splitflags = prevsplitflags; - } - } - - // initialize - for (i = 0; i < 4; i++) - localplayers[i] = -1; - - if (G_RaceGametype()) - hyu *= 2; // double in race - - // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) - if (ghosts) - { - demoghost *g = ghosts; - while (g) - { - if (g->mo->skin) - skin = ((skin_t*)g->mo->skin)-skins; - else - skin = 0; - if (g->mo->color) - { - if (g->mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); - } - else - colormap = NULL; - K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - g = g->next; - } - - if (!stplyr->mo || stplyr->spectator || stplyr->exiting) - return; - - localplayers[numlocalplayers] = stplyr-players; - numlocalplayers++; - } - else - { - for (i = MAXPLAYERS-1; i >= 0; i--) - { - if (!playeringame[i]) - continue; - if (!players[i].mo || players[i].spectator || players[i].exiting) - continue; - - if (i != displayplayers[0] || r_splitscreen) - { - if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) - continue; - - if (players[i].kartstuff[k_hyudorotimer] > 0) - { - if (!((players[i].kartstuff[k_hyudorotimer] < TICRATE/2 - || players[i].kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)) - && !(leveltime & 1))) - continue; - } - } - - if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) - { - // Draw display players on top of everything else - localplayers[numlocalplayers] = i; - numlocalplayers++; - continue; - } - - if (players[i].mo->skin) - skin = ((skin_t*)players[i].mo->skin)-skins; - else - skin = 0; - - if (players[i].mo->color) - { - if (players[i].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); - } - else - colormap = NULL; - - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - // Target reticule - if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } - } - - // draw SPB(s?) - for (mobj = kitemcap; mobj; mobj = next) - { - next = mobj->itnext; - if (mobj->type == MT_SPB) - { - colormap = NULL; - - if (mobj->target && !P_MobjWasRemoved(mobj->target)) - { - if (mobj->player && mobj->player->skincolor) - colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE); - else if (mobj->color) - colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); - } - - K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); - } - } - - // draw our local players here, opaque. - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - - for (i = 0; i < numlocalplayers; i++) - { - if (i == -1) - continue; // this doesn't interest us - - if (players[localplayers[i]].mo->skin) - skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; - else - skin = 0; - - if (players[localplayers[i]].mo->color) - { - if (players[localplayers[i]].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); - } - else - colormap = NULL; - - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - - // Target reticule - if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } -} - -static void K_drawKartStartCountdown(void) -{ - INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3 - - if (leveltime >= starttime-(2*TICRATE)) // 2 - pnum++; - if (leveltime >= starttime-TICRATE) // 1 - pnum++; - if (leveltime >= starttime) // GO! - pnum++; - if ((leveltime % (2*5)) / 5) // blink - pnum += 4; - if (r_splitscreen) // splitscreen - pnum += 8; - - V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); -} - -static void K_drawKartFinish(void) -{ - INT32 pnum = 0, splitflags = K_calcSplitFlags(0); - - if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE) - return; - - if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink - pnum = 1; - - if (r_splitscreen > 1) // 3/4p, stationary FIN - { - pnum += 2; - V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]); - return; - } - - //else -- 1/2p, scrolling FINISH - { - INT32 x, xval; - - if (r_splitscreen) // wide splitscreen - pnum += 4; - - x = ((vid.width<width)<karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; - - if (r_splitscreen && stplyr == &players[displayplayers[1]]) - x = -x; - - V_DrawFixedPatch(x + (STCD_X<>1), - (STCD_Y<height)<<(FRACBITS-1)), - FRACUNIT, - splitflags, kp_racefinish[pnum], NULL); - } -} - -static void K_drawBattleFullscreen(void) -{ - INT32 x = BASEVIDWIDTH/2; - INT32 y = -64+(stplyr->karthud[khud_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen - INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead - fixed_t scale = FRACUNIT; - boolean drawcomebacktimer = true; // lazy hack because it's cleaner in the long run. -#ifdef HAVE_BLUA - if (!LUA_HudEnabled(hud_battlecomebacktimer)) - drawcomebacktimer = false; -#endif - - if (r_splitscreen) - { - if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - || (r_splitscreen > 1 && (stplyr == &players[displayplayers[2]] - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)))) - { - y = 232-(stplyr->karthud[khud_cardanimation]/2); - splitflags = V_SNAPTOBOTTOM; - } - else - y = -32+(stplyr->karthud[khud_cardanimation]/2); - - if (r_splitscreen > 1) - { - scale /= 2; - - if (stplyr == &players[displayplayers[1]] - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) - x = 3*BASEVIDWIDTH/4; - else - x = BASEVIDWIDTH/4; - } - else - { - if (stplyr->exiting) - { - if (stplyr == &players[displayplayers[1]]) - x = BASEVIDWIDTH-96; - else - x = 96; - } - else - scale /= 2; - } - } - - if (stplyr->exiting) - { - if (stplyr == &players[displayplayers[0]]) - V_DrawFadeScreen(0xFF00, 16); - if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) - { - patch_t *p = kp_battlecool; - - if (K_IsPlayerLosing(stplyr)) - p = kp_battlelose; - else if (stplyr->kartstuff[k_position] == 1) - p = kp_battlewin; - - V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator && drawcomebacktimer) - { - UINT16 t = stplyr->kartstuff[k_comebacktimer]/(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; - - txoff = adjust; - - while (t) - { - txoff += adjust; - t /= 10; - } - - if (r_splitscreen) - { - if (r_splitscreen > 1) - ty = (BASEVIDHEIGHT/4)+33; - if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - || (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) - ty += (BASEVIDHEIGHT/2); - } - else - V_DrawFadeScreen(0xFF00, 16); - - if (!comebackshowninfo) - V_DrawFixedPatch(x< 1) - V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE)); - else - { - V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE)); - } - } - - if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY? - { - UINT8 i; - - // check to see if there's anyone else at all - for (i = 0; i < MAXPLAYERS; i++) - { - if (i == displayplayers[0]) - continue; - if (playeringame[i] && !stplyr->spectator) - return; - } - -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_freeplay)) -#endif - K_drawKartFreePlay(leveltime); - } -} - -static void K_drawKartFirstPerson(void) -{ - static INT32 pnum[4], turn[4], drift[4]; - INT32 pn = 0, tn = 0, dr = 0; - INT32 target = 0, splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); - INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT; - fixed_t scale; - UINT8 *colmap = NULL; - ticcmd_t *cmd = &stplyr->cmd; - - if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) - return; - - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } - else - { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } - - if (r_splitscreen) - { - y >>= 1; - if (r_splitscreen > 1) - x >>= 1; - } - - { - if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) - y++; - - if (stplyr->mo->drawflags & MFD_TRANSMASK) - splitflags |= ((stplyr->mo->drawflags & MFD_TRANSMASK) >> MFD_TRANSSHIFT) << FF_TRANSSHIFT; - else if (stplyr->mo->frame & FF_TRANSMASK) - splitflags |= (stplyr->mo->frame & FF_TRANSMASK); - } - - if (cmd->driftturn > 400) // strong left turn - target = 2; - else if (cmd->driftturn < -400) // strong right turn - target = -2; - else if (cmd->driftturn > 0) // weak left turn - target = 1; - else if (cmd->driftturn < 0) // weak right turn - target = -1; - else // forward - target = 0; - - if (encoremode) - target = -target; - - if (pn < target) - pn++; - else if (pn > target) - pn--; - - if (pn < 0) - splitflags |= V_FLIP; // right turn - - target = abs(pn); - if (target > 2) - target = 2; - - x <<= FRACBITS; - y <<= FRACBITS; - - if (tn != cmd->driftturn/50) - tn -= (tn - (cmd->driftturn/50))/8; - - if (dr != stplyr->kartstuff[k_drift]*16) - dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8; - - if (r_splitscreen == 1) - { - scale = (2*FRACUNIT)/3; - y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view) - } - else if (r_splitscreen) - scale = FRACUNIT/2; - else - scale = FRACUNIT; - - if (stplyr->mo) - { - UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->kartstuff[k_driftcharge]); - const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; - // yes, the following is correct. no, you do not need to swap the x and y. - fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); - fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); - - if (r_splitscreen) - xoffs = FixedMul(xoffs, scale); - - xoffs -= (tn)*scale; - xoffs -= (dr)*scale; - - if (stplyr->frameangle == stplyr->mo->angle) - { - const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); - - if (mag < FRACUNIT) - { - xoffs = FixedMul(xoffs, mag); - if (!r_splitscreen) - yoffs = FixedMul(yoffs, mag); - } - } - - if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! - yoffs += stplyr->mo->momz/3; - - if (encoremode) - x -= xoffs; - else - x += xoffs; - if (!r_splitscreen) - y += yoffs; - - - if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks! - colmap = R_GetTranslationColormap(TC_RAINBOW, driftcolor, GTC_CACHE); - else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink! - colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE); - } - - V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap); - - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } - else - { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } -} - -// doesn't need to ever support 4p -static void K_drawInput(void) -{ - static INT32 pn = 0; - INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT); - INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col; - const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5]; - const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9]; - ticcmd_t *cmd = &stplyr->cmd; - - if (timeinmap <= 105) - return; - - if (timeinmap < 113) - { - INT32 count = ((INT32)(timeinmap) - 105); - offs = 64; - while (count-- > 0) - offs >>= 1; - x += offs; - } - -#define BUTTW 8 -#define BUTTH 11 - -#define drawbutt(xoffs, butt, symb)\ - if (stplyr->cmd.buttons & butt)\ - {\ - offs = 2;\ - col = accent1;\ - }\ - else\ - {\ - offs = 0;\ - col = accent2;\ - V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\ - }\ - V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\ - V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn - target = 0; - else // turning of multiple strengths! - { - target = ((abs(cmd->driftturn) - 1)/125)+1; - if (target > 4) - target = 4; - if (cmd->driftturn < 0) - target = -target; - } - - if (pn != target) - { - if (abs(pn - target) == 1) - pn = target; - else if (pn < target) - pn += 2; - else //if (pn > target) - pn -= 2; - } - - if (pn < 0) - { - splitflags |= V_FLIP; // right turn - x--; - } - - target = abs(pn); - if (target > 4) - target = 4; - - if (!stplyr->skincolor) - V_DrawFixedPatch(x<skincolor, GTC_CACHE); - V_DrawFixedPatch(x<karthud[khud_lapanimation]; - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); - - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, - (48 - (32*max(0, progress-76)))*FRACUNIT, - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap); - - if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3) - { - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, - (48 - (32*max(0, progress-76)) - + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT, - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); - } - - if (stplyr->laps == (UINT8)(cv_numlaps.value)) - { - V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_final[min(progress/2, 10)], NULL); - - if (progress/2-12 >= 0) - { - V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_lap[min(progress/2-12, 6)], NULL); - } - } - else - { - V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_lap[min(progress/2, 6)], NULL); - - if (progress/2-8 >= 0) - { - V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps) / 10)][min(progress/2-8, 2)], NULL); - - if (progress/2-10 >= 0) - { - V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps) % 10)][min(progress/2-10, 2)], NULL); - } - } - } -} - -void K_drawKartFreePlay(UINT32 flashtime) -{ - // no splitscreen support because it's not FREE PLAY if you have more than one player in-game - // (you fool, you can take splitscreen online. :V) - - if ((flashtime % TICRATE) < TICRATE/2) - return; - - V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy - LAPS_Y+3, V_HUDTRANS|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); -} - -static void -Draw_party_ping (int ss, INT32 snap) -{ - HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|snap); -} - -static void -K_drawMiniPing (void) -{ - if (cv_showping.value) - { - switch (r_splitscreen) - { - case 3: - Draw_party_ping(3, V_SNAPTORIGHT|V_SPLITSCREEN); - /*FALLTHRU*/ - case 2: - Draw_party_ping(2, V_SPLITSCREEN); - Draw_party_ping(1, V_SNAPTORIGHT); - Draw_party_ping(0, 0); - break; - case 1: - Draw_party_ping(1, V_SNAPTORIGHT|V_SPLITSCREEN); - Draw_party_ping(0, V_SNAPTORIGHT); - break; - } - } -} - -static void K_drawDistributionDebugger(void) -{ - patch_t *items[NUMKARTRESULTS] = { - kp_sadface[1], - kp_sneaker[1], - kp_rocketsneaker[1], - kp_invincibility[7], - kp_banana[1], - kp_eggman[1], - kp_orbinaut[4], - kp_jawz[1], - kp_mine[1], - kp_ballhog[1], - kp_selfpropelledbomb[1], - kp_grow[1], - kp_shrink[1], - kp_thundershield[1], - kp_bubbleshield[1], - kp_flameshield[1], - kp_hyudoro[1], - kp_pogospring[1], - kp_superring[1], - kp_kitchensink[1], - - kp_sneaker[1], - kp_banana[1], - kp_banana[1], - kp_orbinaut[4], - kp_orbinaut[4], - kp_jawz[1] - }; - UINT8 useodds = 0; - UINT8 pingame = 0, bestbumper = 0; - UINT32 pdis = 0; - INT32 i; - INT32 x = -9, y = -9; - boolean spbrush = false; - - if (stplyr != &players[displayplayers[0]]) // only for p1 - return; - - // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - pingame++; - if (players[i].kartstuff[k_bumper] > bestbumper) - bestbumper = players[i].kartstuff[k_bumper]; - } - - // lovely double loop...... - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].kartstuff[k_position] == 1) - { - // This player is first! Yay! - pdis = stplyr->distancetofinish - players[i].distancetofinish; - break; - } - } - - if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items - pdis = (15 * pdis) / 14; - - if (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell - { - pdis = (3 * pdis) / 2; - spbrush = true; - } - - if (stplyr->bot && stplyr->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } - - pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count - - useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); - - for (i = 1; i < NUMKARTRESULTS; i++) - { - const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); - if (itemodds <= 0) - continue; - - V_DrawScaledPatch(x, y, V_HUDTRANS|V_SNAPTOTOP, items[i]); - V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SNAPTOTOP, va("%d", itemodds)); - - // Display amount for multi-items - if (i >= NUMKARTITEMS) - { - INT32 amount; - switch (i) - { - case KRITEM_TENFOLDBANANA: - amount = 10; - break; - case KRITEM_QUADORBINAUT: - amount = 4; - break; - case KRITEM_DUALJAWZ: - amount = 2; - break; - default: - amount = 3; - break; - } - V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SNAPTOTOP, va("x%d", amount)); - } - - x += 32; - if (x >= 297) - { - x = -9; - y += 32; - } - } - - V_DrawString(0, 0, V_HUDTRANS|V_SNAPTOTOP, va("USEODDS %d", useodds)); -} - -static void K_drawCheckpointDebugger(void) -{ - if (stplyr != &players[displayplayers[0]]) // only for p1 - return; - - if (stplyr->starpostnum == numstarposts) - V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts)); - else - V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts)); -} - -static void K_DrawWaypointDebugger(void) -{ - if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) - { - V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); - V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); - } -} - -void K_drawKartHUD(void) -{ - boolean isfreeplay = false; - boolean battlefullscreen = false; - boolean freecam = demo.freecam; //disable some hud elements w/ freecam - UINT8 i; - - // Define the X and Y for each drawn object - // This is handled by console/menu values - K_initKartHUD(); - - // Draw that fun first person HUD! Drawn ASAP so it looks more "real". - for (i = 0; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]] && !camera[i].chase && !freecam) - K_drawKartFirstPerson(); - } - - // Draw full screen stuff that turns off the rest of the HUD - if (mapreset && stplyr == &players[displayplayers[0]]) - { - K_drawChallengerScreen(); - return; - } - - battlefullscreen = ((G_BattleGametype()) - && (stplyr->exiting - || (stplyr->kartstuff[k_bumper] <= 0 - && stplyr->kartstuff[k_comebacktimer] - && comeback - && stplyr->playerstate == PST_LIVE))); - - if (!demo.title && (!battlefullscreen || r_splitscreen)) - { - // Draw the CHECK indicator before the other items, so it's overlapped by everything else -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_check)) // delete lua when? -#endif - if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam) - K_drawKartPlayerCheck(); - - // nametags -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_names)) -#endif - K_drawKartNameTags(); - - // Draw WANTED status - if (G_BattleGametype()) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_wanted)) -#endif - K_drawKartWanted(); - } - - if (cv_kartminimap.value) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_minimap)) -#endif - K_drawKartMinimap(); - } - } - - if (battlefullscreen && !freecam) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_battlefullscreen)) -#endif - K_drawBattleFullscreen(); - return; - } - - // Draw the item window -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_item) && !freecam) -#endif - K_drawKartItem(); - - // If not splitscreen, draw... - if (!r_splitscreen && !demo.title) - { - // Draw the timestamp -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_time)) -#endif - K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0); - - if (!modeattacking) - { - // The top-four faces on the left - /*#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_minirankings)) - #endif*/ - isfreeplay = K_drawKartPositionFaces(); - } - } - - if (!stplyr->spectator && !demo.freecam) // Bottom of the screen elements, don't need in spectate mode - { - // Draw the speedometer - if (cv_kartspeedometer.value && !r_splitscreen) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_speedometer)) -#endif - K_drawKartSpeedometer(); - } - - if (demo.title) // Draw title logo instead in demo.titles - { - INT32 x = BASEVIDWIDTH - 32, y = 128, offs; - - if (r_splitscreen == 3) - { - x = BASEVIDWIDTH/2 + 10; - y = BASEVIDHEIGHT/2 - 30; - } - - if (timeinmap < 113) - { - INT32 count = ((INT32)(timeinmap) - 104); - offs = 256; - while (count-- > 0) - offs >>= 1; - x += offs; - } - - V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); - V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); - } - else if (G_RaceGametype()) // Race-only elements - { - // Draw the lap counter -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartLapsAndRings(); - - if (isfreeplay) - ; - else if (!modeattacking) - { - // Draw the numerical position -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_position)) -#endif - K_DrawKartPositionNum(stplyr->kartstuff[k_position]); - } - else //if (!(demo.playback && hu_showscores)) - { - // Draw the input UI -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_position)) -#endif - K_drawInput(); - } - } - else if (G_BattleGametype()) // Battle-only - { - // Draw the hits left! -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartBumpersOrKarma(); - } - } - - // Draw the countdowns after everything else. - if (leveltime >= starttime-(3*TICRATE) - && leveltime < starttime+TICRATE) - K_drawKartStartCountdown(); - else if (racecountdown && (!r_splitscreen || !stplyr->exiting)) - { - char *countstr = va("%d", racecountdown/TICRATE); - - if (r_splitscreen > 1) - V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, K_calcSplitFlags(0), countstr); - else - { - INT32 karlen = strlen(countstr)*6; // half of 12 - V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, K_calcSplitFlags(0), countstr); - } - } - - // Race overlays - if (G_RaceGametype() && !freecam) - { - if (stplyr->exiting) - K_drawKartFinish(); - else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen) - K_drawLapStartAnim(); - } - - if (modeattacking || freecam) // everything after here is MP and debug only - return; - - if (G_BattleGametype() && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * - V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); - - // Draw FREE PLAY. - if (isfreeplay && !stplyr->spectator && timeinmap > 113) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_freeplay)) -#endif - K_drawKartFreePlay(leveltime); - } - - if (r_splitscreen == 0 && stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) - { - V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); - } - - if (netgame && r_splitscreen) - { - K_drawMiniPing(); - } - - if (cv_kartdebugdistribution.value) - K_drawDistributionDebugger(); - - if (cv_kartdebugcheckpoint.value) - K_drawCheckpointDebugger(); - - if (cv_kartdebugnodes.value) - { - UINT8 p; - for (p = 0; p < MAXPLAYERS; p++) - V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency)); - } - - if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin) - { - INT32 x = 0, y = 0; - UINT8 c; - - for (c = 1; c < MAXSKINCOLORS; c++) - { - UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, c, GTC_CACHE); - V_DrawFixedPatch(x<>1, 0, facewantprefix[stplyr->skin], cm); - - x += 16; - if (x > BASEVIDWIDTH-16) - { - x = 0; - y += 16; - } - } - } - - K_DrawWaypointDebugger(); -} - -//} +// SONIC ROBO BLAST 2 KART ~ ZarroTsu +//----------------------------------------------------------------------------- +/// \file k_kart.c +/// \brief SRB2kart general. +/// All of the SRB2kart-unique stuff. + +#include "k_kart.h" +#include "k_battle.h" +#include "k_pwrlv.h" +#include "k_color.h" +#include "k_respawn.h" +#include "doomdef.h" +#include "hu_stuff.h" +#include "g_game.h" +#include "m_random.h" +#include "p_local.h" +#include "p_slopes.h" +#include "p_setup.h" +#include "r_draw.h" +#include "r_local.h" +#include "s_sound.h" +#include "st_stuff.h" +#include "v_video.h" +#include "z_zone.h" +#include "m_misc.h" +#include "m_cond.h" +#include "f_finale.h" +#include "lua_hud.h" // For Lua hud checks +#include "lua_hook.h" // For MobjDamage and ShouldDamage + +#include "k_waypoint.h" +#include "k_bot.h" + +// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: +// gamespeed is cc (0 for easy, 1 for normal, 2 for hard) +// franticitems is Frantic Mode items, bool +// encoremode is Encore Mode (duh), bool +// comeback is Battle Mode's karma comeback, also bool +// battlewanted is an array of the WANTED player nums, -1 for no player in that slot +// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB +// mapreset is set when enough players fill an empty server + +UINT16 K_GetPlayerDontDrawFlag(player_t *player) +{ + UINT16 flag = 0; + + if (player == &players[displayplayers[0]]) + flag = MFD_DONTDRAWP1; + else if (r_splitscreen >= 1 && player == &players[displayplayers[1]]) + flag = MFD_DONTDRAWP2; + else if (r_splitscreen >= 2 && player == &players[displayplayers[2]]) + flag = MFD_DONTDRAWP3; + else if (r_splitscreen >= 3 && player == &players[displayplayers[3]]) + flag = MFD_DONTDRAWP4; + + return flag; +} + +player_t *K_GetItemBoxPlayer(mobj_t *mobj) +{ + fixed_t closest = INT32_MAX; + player_t *player = NULL; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!(playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo) && !players[i].spectator)) + { + continue; + } + + // Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this + if (P_CanPickupItem(&players[i], 1)) + { + fixed_t dist = P_AproxDistance(P_AproxDistance( + players[i].mo->x - mobj->x, + players[i].mo->y - mobj->y), + players[i].mo->z - mobj->z + ); + + if (dist > 8192*mobj->scale) + { + continue; + } + + if (dist < closest) + { + player = &players[i]; + closest = dist; + } + } + } + + return player; +} + +// Angle reflection used by springs & speed pads +angle_t K_ReflectAngle(angle_t yourangle, angle_t theirangle, fixed_t yourspeed, fixed_t theirspeed) +{ + INT32 angoffset; + boolean subtract = false; + + angoffset = yourangle - theirangle; + + if ((angle_t)angoffset > ANGLE_180) + { + // Flip on wrong side + angoffset = InvAngle((angle_t)angoffset); + subtract = !subtract; + } + + // Fix going directly against the spring's angle sending you the wrong way + if ((angle_t)angoffset > ANGLE_90) + { + angoffset = ANGLE_180 - angoffset; + } + + // Offset is reduced to cap it (90 / 2 = max of 45 degrees) + angoffset /= 2; + + // Reduce further based on how slow your speed is compared to the spring's speed + // (set both to 0 to ignore this) + if (theirspeed != 0 && yourspeed != 0) + { + if (theirspeed > yourspeed) + { + angoffset = FixedDiv(angoffset, FixedDiv(theirspeed, yourspeed)); + } + } + + if (subtract) + angoffset = (signed)(theirangle) - angoffset; + else + angoffset = (signed)(theirangle) + angoffset; + + return (angle_t)angoffset; +} + +//{ SRB2kart Net Variables + +void K_RegisterKartStuff(void) +{ + CV_RegisterVar(&cv_sneaker); + CV_RegisterVar(&cv_rocketsneaker); + CV_RegisterVar(&cv_invincibility); + CV_RegisterVar(&cv_banana); + CV_RegisterVar(&cv_eggmanmonitor); + CV_RegisterVar(&cv_orbinaut); + CV_RegisterVar(&cv_jawz); + CV_RegisterVar(&cv_mine); + CV_RegisterVar(&cv_ballhog); + CV_RegisterVar(&cv_selfpropelledbomb); + CV_RegisterVar(&cv_grow); + CV_RegisterVar(&cv_shrink); + CV_RegisterVar(&cv_thundershield); + CV_RegisterVar(&cv_bubbleshield); + CV_RegisterVar(&cv_flameshield); + CV_RegisterVar(&cv_hyudoro); + CV_RegisterVar(&cv_pogospring); + CV_RegisterVar(&cv_superring); + CV_RegisterVar(&cv_kitchensink); + + CV_RegisterVar(&cv_triplesneaker); + CV_RegisterVar(&cv_triplebanana); + CV_RegisterVar(&cv_decabanana); + CV_RegisterVar(&cv_tripleorbinaut); + CV_RegisterVar(&cv_quadorbinaut); + CV_RegisterVar(&cv_dualjawz); + + CV_RegisterVar(&cv_kartminimap); + CV_RegisterVar(&cv_kartcheck); + CV_RegisterVar(&cv_kartinvinsfx); + CV_RegisterVar(&cv_kartspeed); + CV_RegisterVar(&cv_kartbumpers); + CV_RegisterVar(&cv_kartfrantic); + CV_RegisterVar(&cv_kartcomeback); + CV_RegisterVar(&cv_kartencore); + CV_RegisterVar(&cv_kartvoterulechanges); + CV_RegisterVar(&cv_kartspeedometer); + CV_RegisterVar(&cv_kartvoices); + CV_RegisterVar(&cv_kartbot); + CV_RegisterVar(&cv_karteliminatelast); + CV_RegisterVar(&cv_kartusepwrlv); + CV_RegisterVar(&cv_votetime); + + CV_RegisterVar(&cv_kartdebugitem); + CV_RegisterVar(&cv_kartdebugamount); + CV_RegisterVar(&cv_kartdebugshrink); + CV_RegisterVar(&cv_kartallowgiveitem); + CV_RegisterVar(&cv_kartdebugdistribution); + CV_RegisterVar(&cv_kartdebughuddrop); + CV_RegisterVar(&cv_kartdebugwaypoints); + + CV_RegisterVar(&cv_kartdebugcheckpoint); + CV_RegisterVar(&cv_kartdebugnodes); + CV_RegisterVar(&cv_kartdebugcolorize); +} + +//} + +boolean K_IsPlayerLosing(player_t *player) +{ + INT32 winningpos = 1; + UINT8 i, pcount = 0; + + if (battlecapsules && player->kartstuff[k_bumper] <= 0) + return true; // DNF in break the capsules + + if (player->kartstuff[k_position] == 1) + return false; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + if (players[i].kartstuff[k_position] > pcount) + pcount = players[i].kartstuff[k_position]; + } + + if (pcount <= 1) + return false; + + winningpos = pcount/2; + if (pcount % 2) // any remainder? + winningpos++; + + return (player->kartstuff[k_position] > winningpos); +} + +fixed_t K_GetKartGameSpeedScalar(SINT8 value) +{ + // Easy = 81.25% + // Normal = 100% + // Hard = 118.75% + // Nightmare = 137.5% ?!?! + return ((13 + (3*value)) << FRACBITS) / 16; +} + +//{ SRB2kart Roulette Code - Position Based + +consvar_t *KartItemCVars[NUMKARTRESULTS-1] = +{ + &cv_sneaker, + &cv_rocketsneaker, + &cv_invincibility, + &cv_banana, + &cv_eggmanmonitor, + &cv_orbinaut, + &cv_jawz, + &cv_mine, + &cv_ballhog, + &cv_selfpropelledbomb, + &cv_grow, + &cv_shrink, + &cv_thundershield, + &cv_bubbleshield, + &cv_flameshield, + &cv_hyudoro, + &cv_pogospring, + &cv_superring, + &cv_kitchensink, + &cv_triplesneaker, + &cv_triplebanana, + &cv_decabanana, + &cv_tripleorbinaut, + &cv_quadorbinaut, + &cv_dualjawz +}; + +#define NUMKARTODDS 80 + +// Less ugly 2D arrays +static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = +{ + //P-Odds 0 1 2 3 4 5 6 7 + /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker + /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker + /*Invincibility*/ { 0, 0, 0, 0, 1, 4, 7, 9 }, // Invincibility + /*Banana*/ { 7, 3, 2, 0, 0, 0, 0, 0 }, // Banana + /*Eggman Monitor*/ { 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor + /*Orbinaut*/ { 7, 4, 3, 2, 0, 0, 0, 0 }, // Orbinaut + /*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz + /*Mine*/ { 0, 2, 2, 1, 0, 0, 0, 0 }, // Mine + /*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog + /*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb + /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow + /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink + /*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield + /*Bubble Shield*/ { 0, 2, 3, 3, 1, 0, 0, 0 }, // Bubble Shield + /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield + /*Hyudoro*/ { 0, 0, 0, 1, 2, 0, 0, 0 }, // Hyudoro + /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring + /*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring + /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + /*Sneaker x3*/ { 0, 0, 0, 2, 6,10, 5, 0 }, // Sneaker x3 + /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 + /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 + /*Orbinaut x3*/ { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 + /*Orbinaut x4*/ { 0, 0, 0, 1, 1, 0, 0, 0 }, // Orbinaut x4 + /*Jawz x2*/ { 0, 0, 1, 2, 0, 0, 0, 0 } // Jawz x2 +}; + +static INT32 K_KartItemOddsBattle[NUMKARTRESULTS-1][6] = +{ + //P-Odds 0 1 2 3 4 5 + /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker + /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker + /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility + /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana + /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor + /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut + /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz + /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine + /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog + /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb + /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow + /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink + /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield + /*Bubble Shield*/ { 0, 0, 0, 0, 0, 0 }, // Bubble Shield + /*Flame Shield*/ { 0, 0, 0, 0, 0, 0 }, // Flame Shield + /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro + /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring + /*Super Ring*/ { 0, 0, 0, 0, 0, 0 }, // Super Ring + /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3 + /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3 + /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10 + /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3 + /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4 + /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2 +}; + +#define DISTVAR (2048) // Magic number distance for use with item roulette tiers + +INT32 K_GetShieldFromItem(INT32 item) +{ + switch (item) + { + case KITEM_THUNDERSHIELD: return KSHIELD_THUNDER; + case KITEM_BUBBLESHIELD: return KSHIELD_BUBBLE; + case KITEM_FLAMESHIELD: return KSHIELD_FLAME; + default: return KSHIELD_NONE; + } +} + +/** \brief Item Roulette for Kart + + \param player player + \param getitem what item we're looking for + + \return void +*/ +static void K_KartGetItemResult(player_t *player, SINT8 getitem) +{ + if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) // Indirect items + indirectitemcooldown = 20*TICRATE; + + if (getitem == KITEM_HYUDORO) // Hyudoro cooldown + hyubgone = 5*TICRATE; + + player->botvars.itemdelay = TICRATE; + player->botvars.itemconfirm = 0; + + switch (getitem) + { + // Special roulettes first, then the generic ones are handled by default + case KRITEM_TRIPLESNEAKER: // Sneaker x3 + player->kartstuff[k_itemtype] = KITEM_SNEAKER; + player->kartstuff[k_itemamount] = 3; + break; + case KRITEM_TRIPLEBANANA: // Banana x3 + player->kartstuff[k_itemtype] = KITEM_BANANA; + player->kartstuff[k_itemamount] = 3; + break; + case KRITEM_TENFOLDBANANA: // Banana x10 + player->kartstuff[k_itemtype] = KITEM_BANANA; + player->kartstuff[k_itemamount] = 10; + break; + case KRITEM_TRIPLEORBINAUT: // Orbinaut x3 + player->kartstuff[k_itemtype] = KITEM_ORBINAUT; + player->kartstuff[k_itemamount] = 3; + break; + case KRITEM_QUADORBINAUT: // Orbinaut x4 + player->kartstuff[k_itemtype] = KITEM_ORBINAUT; + player->kartstuff[k_itemamount] = 4; + break; + case KRITEM_DUALJAWZ: // Jawz x2 + player->kartstuff[k_itemtype] = KITEM_JAWZ; + player->kartstuff[k_itemamount] = 2; + break; + default: + if (getitem <= 0 || getitem >= NUMKARTRESULTS) // Sad (Fallback) + { + if (getitem != 0) + CONS_Printf("ERROR: P_KartGetItemResult - Item roulette gave bad item (%d) :(\n", getitem); + player->kartstuff[k_itemtype] = KITEM_SAD; + } + else + player->kartstuff[k_itemtype] = getitem; + player->kartstuff[k_itemamount] = 1; + break; + } +} + +/** \brief Item Roulette for Kart + + \param player player object passed from P_KartPlayerThink + + \return void +*/ + +static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) +{ + INT32 newodds; + INT32 i; + UINT8 pingame = 0, pexiting = 0; + SINT8 first = -1, second = -1; + INT32 secondist = 0; + INT32 shieldtype = KSHIELD_NONE; + + I_Assert(item > KITEM_NONE); // too many off by one scenarioes. + I_Assert(KartItemCVars[NUMKARTRESULTS-2] != NULL); // Make sure this exists + + if (!KartItemCVars[item-1]->value && !modeattacking) + return 0; + + if (G_BattleGametype()) + { + I_Assert(pos < 6); // DO NOT allow positions past the bounds of the table + newodds = K_KartItemOddsBattle[item-1][pos]; + } + else + { + I_Assert(pos < 8); // Ditto + newodds = K_KartItemOddsRace[item-1][pos]; + } + + // Base multiplication to ALL item odds to simulate fractional precision + newodds *= 4; + + shieldtype = K_GetShieldFromItem(item); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!G_BattleGametype() || players[i].kartstuff[k_bumper]) + pingame++; + + if (players[i].exiting) + pexiting++; + + if (shieldtype != KSHIELD_NONE && shieldtype == K_GetShieldFromItem(players[i].kartstuff[k_itemtype])) + { + // Don't allow more than one of each shield type at a time + return 0; + } + + if (players[i].mo && G_RaceGametype()) + { + if (players[i].kartstuff[k_position] == 1 && first == -1) + first = i; + if (players[i].kartstuff[k_position] == 2 && second == -1) + second = i; + } + } + + if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB + { + secondist = players[second].distancetofinish - players[first].distancetofinish; + if (franticitems) + secondist = (15 * secondist) / 14; + secondist = ((28 + (8-pingame)) * secondist) / 28; + } + + // POWERITEMODDS handles all of the "frantic item" related functionality, for all of our powerful items. + // First, it multiplies it by 2 if franticitems is true; easy-peasy. + // Next, it multiplies it again if it's in SPB mode and 2nd needs to apply pressure to 1st. + // Then, it multiplies it further if the player count isn't equal to 8. + // This is done to make low player count races more interesting and high player count rates more fair. + // (2P normal would be about halfway between 8P normal and 8P frantic.) + // (This scaling is not done for SPB Rush, so that catchup strength is not weakened.) + // Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, for lesser items needed in a pinch. + +#define PLAYERSCALING (8 - (spbrush ? 2 : pingame)) + +#define POWERITEMODDS(odds) {\ + if (franticitems) \ + odds *= 2; \ + if (rival) \ + odds *= 2; \ + odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \ + if (mashed > 0) \ + odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \ +} + +#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) + + /* + if (bot) + { + // TODO: Item use on bots should all be passed-in functions. + // Instead of manually inserting these, it should return 0 + // for any items without an item use function supplied + + switch (item) + { + case KITEM_SNEAKER: + case KITEM_ROCKETSNEAKER: + case KITEM_INVINCIBILITY: + case KITEM_BANANA: + case KITEM_EGGMAN: + case KITEM_ORBINAUT: + case KITEM_JAWZ: + case KITEM_MINE: + case KITEM_BALLHOG: + case KITEM_SPB: + case KITEM_GROW: + case KITEM_SHRINK: + case KITEM_HYUDORO: + case KITEM_SUPERRING: + case KITEM_THUNDERSHIELD: + case KITEM_BUBBLESHIELD: + case KITEM_FLAMESHIELD: + case KRITEM_TRIPLESNEAKER: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + case KRITEM_TRIPLEORBINAUT: + case KRITEM_QUADORBINAUT: + case KRITEM_DUALJAWZ: + break; + default: + return 0; + } + } + */ + (void)bot; + + switch (item) + { + case KITEM_ROCKETSNEAKER: + case KITEM_JAWZ: + case KITEM_BALLHOG: + case KRITEM_TRIPLESNEAKER: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + case KRITEM_TRIPLEORBINAUT: + case KRITEM_QUADORBINAUT: + case KRITEM_DUALJAWZ: + POWERITEMODDS(newodds); + break; + case KITEM_INVINCIBILITY: + case KITEM_MINE: + case KITEM_GROW: + case KITEM_BUBBLESHIELD: + case KITEM_FLAMESHIELD: + if (COOLDOWNONSTART) + newodds = 0; + else + POWERITEMODDS(newodds); + break; + case KITEM_SPB: + if ((indirectitemcooldown > 0) || COOLDOWNONSTART + || (first != -1 && players[first].distancetofinish < 8*DISTVAR)) // No SPB near the end of the race + { + newodds = 0; + } + else + { + INT32 multiplier = (secondist - (5*DISTVAR)) / DISTVAR; + + if (multiplier < 0) + multiplier = 0; + if (multiplier > 3) + multiplier = 3; + + newodds *= multiplier; + } + break; + case KITEM_SHRINK: + if ((indirectitemcooldown > 0) || COOLDOWNONSTART || (pingame-1 <= pexiting)) + newodds = 0; + else + POWERITEMODDS(newodds); + break; + case KITEM_THUNDERSHIELD: + if (spbplace != -1 || COOLDOWNONSTART) + newodds = 0; + else + POWERITEMODDS(newodds); + break; + case KITEM_HYUDORO: + if ((hyubgone > 0) || COOLDOWNONSTART) + newodds = 0; + break; + default: + break; + } + +#undef POWERITEMODDS + + return newodds; +} + +//{ SRB2kart Roulette Code - Distance Based, yes waypoints + +static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) +{ + UINT8 i; + UINT8 n = 0; + UINT8 useodds = 0; + UINT8 disttable[14]; + UINT8 totallen = 0; + UINT8 distlen = 0; + boolean oddsvalid[8]; + + for (i = 0; i < 8; i++) + { + UINT8 j; + boolean available = false; + + if (G_BattleGametype() && i > 5) + { + oddsvalid[i] = false; + break; + } + + for (j = 1; j < NUMKARTRESULTS; j++) + { + if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0) + { + available = true; + break; + } + } + + oddsvalid[i] = available; + } + +#define SETUPDISTTABLE(odds, num) \ + if (oddsvalid[odds]) \ + for (i = num; i; --i) \ + disttable[distlen++] = odds; \ + totallen += num; + + if (G_BattleGametype()) // Battle Mode + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,1); + SETUPDISTTABLE(2,1); + SETUPDISTTABLE(3,1); + SETUPDISTTABLE(4,1); + + if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items + useodds = 5; + else + { + SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc + if (K_IsPlayerWanted(player)) + wantedpos++; + if (wantedpos > 4) // Don't run off into karma items + wantedpos = 4; + if (wantedpos < 0) // Don't go below somehow + wantedpos = 0; + n = (wantedpos * distlen) / totallen; + useodds = disttable[n]; + } + } + else + { + SETUPDISTTABLE(0,1); + SETUPDISTTABLE(1,1); + SETUPDISTTABLE(2,1); + SETUPDISTTABLE(3,2); + SETUPDISTTABLE(4,2); + SETUPDISTTABLE(5,3); + SETUPDISTTABLE(6,3); + SETUPDISTTABLE(7,1); + + if (pdis == 0) + useodds = disttable[0]; + else if (pdis > DISTVAR * ((12 * distlen) / 14)) + useodds = disttable[distlen-1]; + else + { + for (i = 1; i < 13; i++) + { + if (pdis <= DISTVAR * ((i * distlen) / 14)) + { + useodds = disttable[((i * distlen) / 14)]; + break; + } + } + } + } + +#undef SETUPDISTTABLE + + return useodds; +} + +static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) +{ + INT32 i; + UINT8 pingame = 0; + UINT8 roulettestop; + UINT32 pdis = 0; + UINT8 useodds = 0; + INT32 spawnchance[NUMKARTRESULTS]; + INT32 totalspawnchance = 0; + UINT8 bestbumper = 0; + fixed_t mashed = 0; + boolean dontforcespb = false; + boolean spbrush = false; + + // This makes the roulette cycle through items - if this is 0, you shouldn't be here. + if (player->kartstuff[k_itemroulette]) + player->kartstuff[k_itemroulette]++; + else + return; + + // Gotta check how many players are active at this moment. + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + pingame++; + if (players[i].exiting) + dontforcespb = true; + if (players[i].kartstuff[k_bumper] > bestbumper) + bestbumper = players[i].kartstuff[k_bumper]; + } + + // No forced SPB in 1v1s, it has to be randomly rolled + if (pingame <= 2) + dontforcespb = true; + + // This makes the roulette produce the random noises. + if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player) && !demo.freecam) + { +#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8)) + for (i = 0; i <= r_splitscreen; i++) + { + if (player == &players[displayplayers[i]] && players[displayplayers[i]].kartstuff[k_itemroulette]) + PLAYROULETTESND; + } +#undef PLAYROULETTESND + } + + roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position])); + + // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item. + // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think. + // Finally, if you get past this check, now you can actually start calculating what item you get. + if ((cmd->buttons & BT_ATTACK) && (player->kartstuff[k_itemroulette] >= roulettestop) + && !(player->kartstuff[k_eggmanheld] || player->kartstuff[k_itemheld] || player->kartstuff[k_userings])) + { + // Mashing reduces your chances for the good items + mashed = FixedDiv((player->kartstuff[k_itemroulette])*FRACUNIT, ((TICRATE*3)+roulettestop)*FRACUNIT) - FRACUNIT; + } + else if (!(player->kartstuff[k_itemroulette] >= (TICRATE*3))) + return; + + if (cmd->buttons & BT_ATTACK) + player->pflags |= PF_ATTACKDOWN; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator + && players[i].kartstuff[k_position] == 1) + { + // This player is first! Yay! + + if (player->distancetofinish <= players[i].distancetofinish) + { + // Guess you're in first / tied for first? + pdis = 0; + } + else + { + // Subtract 1st's distance from your distance, to get your distance from 1st! + pdis = player->distancetofinish - players[i].distancetofinish; + } + break; + } + } + + if (mapobjectscale != FRACUNIT) + pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT; + + if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items + { + pdis = (15 * pdis) / 14; + } + + if (spbplace != -1 && player->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell + { + pdis = (3 * pdis) / 2; + spbrush = true; + } + + if (player->bot && player->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count + + // SPECIAL CASE No. 1: + // Fake Eggman items + if (player->kartstuff[k_roulettetype] == 2) + { + player->kartstuff[k_eggmanexplode] = 4*TICRATE; + //player->karthud[khud_itemblink] = TICRATE; + //player->karthud[khud_itemblinkmode] = 1; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player) && !demo.freecam) + S_StartSound(NULL, sfx_itrole); + return; + } + + // SPECIAL CASE No. 2: + // Give a debug item instead if specified + if (cv_kartdebugitem.value != 0 && !modeattacking) + { + K_KartGetItemResult(player, cv_kartdebugitem.value); + player->kartstuff[k_itemamount] = cv_kartdebugamount.value; + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = 2; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player) && !demo.freecam) + S_StartSound(NULL, sfx_dbgsal); + return; + } + + // SPECIAL CASE No. 3: + // Record Attack / alone mashing behavior + if (modeattacking || pingame == 1) + { + if (G_RaceGametype()) + { + if (mashed && (modeattacking || cv_superring.value)) // ANY mashed value? You get rings. + { + K_KartGetItemResult(player, KITEM_SUPERRING); + player->karthud[khud_itemblinkmode] = 1; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolm); + } + else + { + if (modeattacking || cv_sneaker.value) // Waited patiently? You get a sneaker! + K_KartGetItemResult(player, KITEM_SNEAKER); + else // Default to sad if nothing's enabled... + K_KartGetItemResult(player, KITEM_SAD); + player->karthud[khud_itemblinkmode] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolf); + } + } + else if (G_BattleGametype()) + { + if (mashed && (modeattacking || cv_banana.value)) // ANY mashed value? You get a banana. + { + K_KartGetItemResult(player, KITEM_BANANA); + player->karthud[khud_itemblinkmode] = 1; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolm); + } + else + { + if (modeattacking || cv_tripleorbinaut.value) // Waited patiently? You get Orbinaut x3! + K_KartGetItemResult(player, KRITEM_TRIPLEORBINAUT); + else // Default to sad if nothing's enabled... + K_KartGetItemResult(player, KITEM_SAD); + player->karthud[khud_itemblinkmode] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolf); + } + } + + player->karthud[khud_itemblink] = TICRATE; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + return; + } + + if (G_RaceGametype()) + { + // SPECIAL CASE No. 4: + // Being in ring debt occasionally forces Super Ring on you if you mashed + if (mashed && player->kartstuff[k_rings] < 0 && cv_superring.value) + { + INT32 debtamount = min(20, abs(player->kartstuff[k_rings])); + if (P_RandomChance((debtamount*FRACUNIT)/20)) + { + K_KartGetItemResult(player, KITEM_SUPERRING); + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = 1; + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, sfx_itrolm); + return; + } + } + + // SPECIAL CASE No. 5: + // Force SPB onto 2nd if they get too far behind + if (player->kartstuff[k_position] == 2 && pdis > (DISTVAR*8) + && spbplace == -1 && !indirectitemcooldown && !dontforcespb + && cv_selfpropelledbomb.value) + { + K_KartGetItemResult(player, KITEM_SPB); + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = (mashed ? 1 : 0); + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + if (P_IsDisplayPlayer(player)) + S_StartSound(NULL, (mashed ? sfx_itrolm : sfx_itrolf)); + return; + } + } + + // NOW that we're done with all of those specialized cases, we can move onto the REAL item roulette tables. + // Initializes existing spawnchance values + for (i = 0; i < NUMKARTRESULTS; i++) + spawnchance[i] = 0; + + // Split into another function for a debug function below + useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); + + for (i = 1; i < NUMKARTRESULTS; i++) + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival))); + + // Award the player whatever power is rolled + if (totalspawnchance > 0) + { + totalspawnchance = P_RandomKey(totalspawnchance); + for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++); + + K_KartGetItemResult(player, i); + } + else + { + player->kartstuff[k_itemtype] = KITEM_SAD; + player->kartstuff[k_itemamount] = 1; + } + + if (P_IsDisplayPlayer(player) && !demo.freecam) + S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); + + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = ((player->kartstuff[k_roulettetype] == 1) ? 2 : (mashed ? 1 : 0)); + + player->kartstuff[k_itemroulette] = 0; // Since we're done, clear the roulette number + player->kartstuff[k_roulettetype] = 0; // This too +} + +//} + +//{ SRB2kart p_user.c Stuff + +static fixed_t K_PlayerWeight(mobj_t *mobj, mobj_t *against) +{ + fixed_t weight = 5*FRACUNIT; + + if (!mobj->player) + return weight; + + if (against && !P_MobjWasRemoved(against) && against->player + && ((!against->player->kartstuff[k_spinouttimer] && mobj->player->kartstuff[k_spinouttimer]) // You're in spinout + || (against->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD && mobj->player->kartstuff[k_itemtype] != KITEM_BUBBLESHIELD))) // They have a Bubble Shield + { + weight = 0; // This player does not cause any bump action + } + else + { + weight = (mobj->player->kartweight) * FRACUNIT; + if (mobj->player->speed > K_GetKartSpeed(mobj->player, false)) + weight += (mobj->player->speed - K_GetKartSpeed(mobj->player, false))/8; + if (mobj->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) + weight += 9*FRACUNIT; + } + + return weight; +} + +fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) +{ + fixed_t weight = 5*FRACUNIT; + + switch (mobj->type) + { + case MT_PLAYER: + if (!mobj->player) + break; + weight = K_PlayerWeight(mobj, against); + break; + case MT_BUBBLESHIELD: + weight = K_PlayerWeight(mobj->target, against); + break; + case MT_FALLINGROCK: + if (against->player) + { + if (against->player->kartstuff[k_invincibilitytimer] || against->player->kartstuff[k_growshrinktimer] > 0) + weight = 0; + else + weight = K_PlayerWeight(against, NULL); + } + break; + case MT_ORBINAUT: + case MT_ORBINAUT_SHIELD: + if (against->player) + weight = K_PlayerWeight(against, NULL); + break; + case MT_JAWZ: + case MT_JAWZ_DUD: + case MT_JAWZ_SHIELD: + if (against->player) + weight = K_PlayerWeight(against, NULL) + (3*FRACUNIT); + else + weight += 3*FRACUNIT; + break; + default: + break; + } + + return FixedMul(weight, mobj->scale); +} + +// This kind of wipeout happens with no rings -- doesn't remove a bumper, has no invulnerability, and is much shorter. +static void K_DebtStingPlayer(player_t *player, INT32 length) +{ + if (player->health <= 0) + return; + + if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 + || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + return; + + player->kartstuff[k_ringboost] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + player->kartstuff[k_spinouttype] = 2; + player->kartstuff[k_spinouttimer] = length; + player->kartstuff[k_wipeoutslow] = min(length-1, wipeoutslowtime+1); + + if (player->mo->state != &states[S_KART_SPIN]) + P_SetPlayerMobjState(player->mo, S_KART_SPIN); + + K_DropHnextList(player, false); + return; +} + +void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) +{ + mobj_t *fx; + fixed_t momdifx, momdify; + fixed_t distx, disty; + fixed_t dot, force; + fixed_t mass1, mass2; + + if (!mobj1 || !mobj2) + return; + + // Don't bump when you're being reborn + if ((mobj1->player && mobj1->player->playerstate != PST_LIVE) + || (mobj2->player && mobj2->player->playerstate != PST_LIVE)) + return; + + if ((mobj1->player && mobj1->player->respawn.state != RESPAWNST_NONE) + || (mobj2->player && mobj2->player->respawn.state != RESPAWNST_NONE)) + return; + + { // Don't bump if you're flashing + INT32 flash; + + flash = K_GetKartFlashing(mobj1->player); + if (mobj1->player && mobj1->player->powers[pw_flashing] > 0 && mobj1->player->powers[pw_flashing] < flash) + { + if (mobj1->player->powers[pw_flashing] < flash-1) + mobj1->player->powers[pw_flashing]++; + return; + } + + flash = K_GetKartFlashing(mobj2->player); + if (mobj2->player && mobj2->player->powers[pw_flashing] > 0 && mobj2->player->powers[pw_flashing] < flash) + { + if (mobj2->player->powers[pw_flashing] < flash-1) + mobj2->player->powers[pw_flashing]++; + return; + } + } + + // Don't bump if you've recently bumped + if (mobj1->player && mobj1->player->kartstuff[k_justbumped]) + { + mobj1->player->kartstuff[k_justbumped] = bumptime; + return; + } + + if (mobj2->player && mobj2->player->kartstuff[k_justbumped]) + { + mobj2->player->kartstuff[k_justbumped] = bumptime; + return; + } + + mass1 = K_GetMobjWeight(mobj1, mobj2); + + if (solid == true && mass1 > 0) + mass2 = mass1; + else + mass2 = K_GetMobjWeight(mobj2, mobj1); + + momdifx = mobj1->momx - mobj2->momx; + momdify = mobj1->momy - mobj2->momy; + + // Adds the OTHER player's momentum times a bunch, for the best chance of getting the correct direction + distx = (mobj1->x + mobj2->momx*3) - (mobj2->x + mobj1->momx*3); + 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; + + { // 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); + fixed_t nx = FixedDiv(distx, dist); + fixed_t ny = FixedDiv(disty, dist); + + dist = dist ? dist : 1; + distx = FixedMul(mobj1->radius+mobj2->radius, nx); + disty = FixedMul(mobj1->radius+mobj2->radius, ny); + + if (momdifx == 0 && momdify == 0) + { + // If there's no momentum difference, they're moving at exactly the same rate. Pretend they moved into each other. + momdifx = -nx; + momdify = -ny; + } + } + + // if the speed difference is less than this let's assume they're going proportionately faster from each other + if (P_AproxDistance(momdifx, momdify) < (25*mapobjectscale)) + { + fixed_t momdiflength = P_AproxDistance(momdifx, momdify); + fixed_t normalisedx = FixedDiv(momdifx, momdiflength); + fixed_t normalisedy = FixedDiv(momdify, momdiflength); + momdifx = FixedMul((25*mapobjectscale), normalisedx); + momdify = FixedMul((25*mapobjectscale), normalisedy); + } + + dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty); + + if (dot >= 0) + { + // They're moving away from each other + return; + } + + force = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty)); + + if (bounce == true && mass2 > 0) // Perform a Goomba Bounce. + mobj1->momz = -mobj1->momz; + else + { + fixed_t newz = mobj1->momz; + if (mass2 > 0) + mobj1->momz = mobj2->momz; + if (mass1 > 0 && solid == false) + mobj2->momz = newz; + } + + if (mass2 > 0) + { + mobj1->momx = mobj1->momx - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), distx); + mobj1->momy = mobj1->momy - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), disty); + } + + if (mass1 > 0 && solid == false) + { + mobj2->momx = mobj2->momx - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -distx); + mobj2->momy = mobj2->momy - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -disty); + } + + // Do the bump fx when we've CONFIRMED we can bump. + if ((mobj1->player && mobj1->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) || (mobj2->player && mobj2->player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD)) + S_StartSound(mobj1, sfx_s3k44); + else + S_StartSound(mobj1, sfx_s3k49); + + fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP); + if (mobj1->eflags & MFE_VERTICALFLIP) + fx->eflags |= MFE_VERTICALFLIP; + else + fx->eflags &= ~MFE_VERTICALFLIP; + P_SetScale(fx, mobj1->scale); + + // Because this is done during collision now, rmomx and rmomy need to be recalculated + // so that friction doesn't immediately decide to stop the player if they're at a standstill + // Also set justbumped here + if (mobj1->player) + { + mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx; + mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy; + mobj1->player->kartstuff[k_justbumped] = bumptime; + + if (mobj1->player->kartstuff[k_spinouttimer]) + { + mobj1->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; + mobj1->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj1->player->kartstuff[k_spinouttimer]); + //mobj1->player->kartstuff[k_spinouttype] = 1; // Enforce type + } + else if (mobj2->player // Player VS player bumping only + && (K_GetShieldFromItem(mobj1->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields + { + if (mobj1->player->kartstuff[k_rings] <= 0) + { + K_DebtStingPlayer(mobj1->player, TICRATE + (4 * (mobj2->player->kartweight - mobj1->player->kartweight))); + K_KartPainEnergyFling(mobj1->player); + P_PlayRinglossSound(mobj1); + } + P_PlayerRingBurst(mobj1->player, 1); + } + } + + if (mobj2->player) + { + mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx; + mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy; + mobj2->player->kartstuff[k_justbumped] = bumptime; + + if (mobj2->player->kartstuff[k_spinouttimer]) + { + mobj2->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; + mobj2->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj2->player->kartstuff[k_spinouttimer]); + //mobj2->player->kartstuff[k_spinouttype] = 1; // Enforce type + } + else if (mobj1->player // Player VS player bumping only + && (K_GetShieldFromItem(mobj2->player->kartstuff[k_itemtype]) == KSHIELD_NONE)) // Ignore for shields + { + if (mobj2->player->kartstuff[k_rings] <= 0) + { + K_DebtStingPlayer(mobj2->player, TICRATE + (4 * (mobj1->player->kartweight - mobj2->player->kartweight))); + K_KartPainEnergyFling(mobj2->player); + P_PlayRinglossSound(mobj2); + } + P_PlayerRingBurst(mobj2->player, 1); + } + } +} + +/** \brief Checks that the player is on an offroad subsector for realsies + + \param mo player mobj object + + \return boolean +*/ +static UINT8 K_CheckOffroadCollide(mobj_t *mo) +{ + UINT8 i; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (i = 2; i < 5; i++) + { + if (P_MobjTouchingSectorSpecial(mo, 1, i, true)) + return i-1; + } + + return 0; +} + +/** \brief Updates the Player's offroad value once per frame + + \param player player object passed from K_KartPlayerThink + + \return void +*/ +static void K_UpdateOffroad(player_t *player) +{ + fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); + + // If you are in offroad, a timer starts. + if (offroadstrength) + { + if (player->kartstuff[k_offroad] < offroadstrength) + player->kartstuff[k_offroad] += offroadstrength / TICRATE; + + if (player->kartstuff[k_offroad] > offroadstrength) + player->kartstuff[k_offroad] = offroadstrength; + } + else + player->kartstuff[k_offroad] = 0; +} + +static void K_DrawDraftCombiring(player_t *player, player_t *victim, fixed_t curdist, fixed_t maxdist, boolean transparent) +{ +#define CHAOTIXBANDLEN 15 +#define CHAOTIXBANDCOLORS 9 + static const UINT8 colors[CHAOTIXBANDCOLORS] = { + SKINCOLOR_SAPPHIRE, + SKINCOLOR_PLATINUM, + SKINCOLOR_TEA, + SKINCOLOR_GARDEN, + SKINCOLOR_BANANA, + SKINCOLOR_GOLD, + SKINCOLOR_ORANGE, + SKINCOLOR_SCARLET, + SKINCOLOR_TAFFY + }; + fixed_t minimumdist = FixedMul(RING_DIST>>1, player->mo->scale); + UINT8 n = CHAOTIXBANDLEN; + UINT8 offset = ((leveltime / 3) % 3); + fixed_t stepx, stepy, stepz; + fixed_t curx, cury, curz; + UINT8 c; + + if (maxdist == 0) + c = 0; + else + c = FixedMul(CHAOTIXBANDCOLORS<> FRACBITS; + + stepx = (victim->mo->x - player->mo->x) / CHAOTIXBANDLEN; + stepy = (victim->mo->y - player->mo->y) / CHAOTIXBANDLEN; + stepz = ((victim->mo->z + (victim->mo->height / 2)) - (player->mo->z + (player->mo->height / 2))) / CHAOTIXBANDLEN; + + curx = player->mo->x + stepx; + cury = player->mo->y + stepy; + curz = player->mo->z + stepz; + + while (n) + { + if (offset == 0) + { + mobj_t *band = P_SpawnMobj(curx + (P_RandomRange(-12,12)*mapobjectscale), + cury + (P_RandomRange(-12,12)*mapobjectscale), + curz + (P_RandomRange(24,48)*mapobjectscale), + MT_SIGNSPARKLE); + + P_SetMobjState(band, S_SIGNSPARK1 + (leveltime % 11)); + P_SetScale(band, (band->destscale = (3*player->mo->scale)/2)); + + band->color = colors[c]; + band->colorized = true; + + band->fuse = 2; + + if (transparent) + band->drawflags |= MFD_SHADOW; + + band->drawflags |= MFD_DONTDRAW & ~(K_GetPlayerDontDrawFlag(player) | K_GetPlayerDontDrawFlag(victim)); + } + + curx += stepx; + cury += stepy; + curz += stepz; + + offset = abs(offset-1) % 3; + n--; + } +#undef CHAOTIXBANDLEN +} + +/** \brief Updates the player's drafting values once per frame + + \param player player object passed from K_KartPlayerThink + + \return void +*/ +static void K_UpdateDraft(player_t *player) +{ + fixed_t topspd = K_GetKartSpeed(player, false); + fixed_t draftdistance; + UINT8 leniency; + UINT8 i; + + if (player->kartstuff[k_itemtype] == KITEM_FLAMESHIELD) + { + // Flame Shield gets infinite draft distance as its passive effect. + draftdistance = 0; + } + else + { + // Distance you have to be to draft. If you're still accelerating, then this distance is lessened. + // This distance biases toward low weight! (min weight gets 4096 units, max weight gets 3072 units) + // This distance is also scaled based on game speed. + draftdistance = (3072 + (128 * (9 - player->kartweight))) * player->mo->scale; + if (player->speed < topspd) + draftdistance = FixedMul(draftdistance, FixedDiv(player->speed, topspd)); + draftdistance = FixedMul(draftdistance, K_GetKartGameSpeedScalar(gamespeed)); + } + + // On the contrary, the leniency period biases toward high weight. + // (See also: the leniency variable in K_SpawnDraftDust) + leniency = (3*TICRATE)/4 + ((player->kartweight-1) * (TICRATE/4)); + + // Not enough speed to draft. + if (player->speed >= 20*player->mo->scale) + { +//#define EASYDRAFTTEST + // Let's hunt for players to draft off of! + for (i = 0; i < MAXPLAYERS; i++) + { + fixed_t dist, olddraft; +#ifndef EASYDRAFTTEST + angle_t yourangle, theirangle, diff; +#endif + + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + +#ifndef EASYDRAFTTEST + // Don't draft on yourself :V + if (&players[i] == player) + continue; +#endif + + // Not enough speed to draft off of. + if (players[i].speed < 20*players[i].mo->scale) + 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); + + diff = R_PointToAngle2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y) - yourangle; + if (diff > ANGLE_180) + diff = InvAngle(diff); + + // Not in front of this player. + if (diff > ANG10) + continue; + + diff = yourangle - theirangle; + if (diff > ANGLE_180) + diff = InvAngle(diff); + + // Not moving in the same direction. + if (diff > ANGLE_90) + continue; +#endif + + dist = P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x, players[i].mo->y - player->mo->y), players[i].mo->z - player->mo->z); + +#ifndef EASYDRAFTTEST + // TOO close to draft. + if (dist < FixedMul(RING_DIST>>1, player->mo->scale)) + continue; + + // Not close enough to draft. + if (dist > draftdistance && draftdistance > 0) + continue; +#endif + + olddraft = player->kartstuff[k_draftpower]; + + player->kartstuff[k_draftleeway] = leniency; + player->kartstuff[k_lastdraft] = i; + + // Draft power is used later in K_GetKartBoostPower, ranging from 0 for normal speed and FRACUNIT for max draft speed. + // How much this increments every tic biases toward acceleration! (min speed gets 1.5% per tic, max speed gets 0.5% per tic) + if (player->kartstuff[k_draftpower] < FRACUNIT) + player->kartstuff[k_draftpower] += (FRACUNIT/200) + ((9 - player->kartspeed) * ((3*FRACUNIT)/1600)); + + if (player->kartstuff[k_draftpower] > FRACUNIT) + player->kartstuff[k_draftpower] = FRACUNIT; + + // Play draft finish noise + if (olddraft < FRACUNIT && player->kartstuff[k_draftpower] >= FRACUNIT) + S_StartSound(player->mo, sfx_cdfm62); + + // Spawn in the visual! + K_DrawDraftCombiring(player, &players[i], dist, draftdistance, false); + + return; // Finished doing our draft. + } + } + + // No one to draft off of? Then you can knock that off. + if (player->kartstuff[k_draftleeway]) // Prevent small disruptions from stopping your draft. + { + player->kartstuff[k_draftleeway]--; + if (player->kartstuff[k_lastdraft] >= 0 + && player->kartstuff[k_lastdraft] < MAXPLAYERS + && playeringame[player->kartstuff[k_lastdraft]] + && !players[player->kartstuff[k_lastdraft]].spectator + && players[player->kartstuff[k_lastdraft]].mo) + { + player_t *victim = &players[player->kartstuff[k_lastdraft]]; + fixed_t dist = P_AproxDistance(P_AproxDistance(victim->mo->x - player->mo->x, victim->mo->y - player->mo->y), victim->mo->z - player->mo->z); + K_DrawDraftCombiring(player, victim, dist, draftdistance, true); + } + } + else // Remove draft speed boost. + { + player->kartstuff[k_draftpower] = 0; + player->kartstuff[k_lastdraft] = -1; + } +} + +void K_KartPainEnergyFling(player_t *player) +{ + static const UINT8 numfling = 5; + INT32 i; + mobj_t *mo; + angle_t fa; + fixed_t ns; + fixed_t z; + + // Better safe than sorry. + if (!player) + return; + + // P_PlayerRingBurst: "There's no ring spilling in kart, so I'm hijacking this for the same thing as TD" + // :oh: + + for (i = 0; i < numfling; i++) + { + INT32 objType = mobjinfo[MT_FLINGENERGY].reactiontime; + fixed_t momxy, momz; // base horizonal/vertical thrusts + + z = player->mo->z; + if (player->mo->eflags & MFE_VERTICALFLIP) + z += player->mo->height - mobjinfo[objType].height; + + mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); + + mo->fuse = 8*TICRATE; + P_SetTarget(&mo->target, player->mo); + + mo->destscale = player->mo->scale; + P_SetScale(mo, player->mo->scale); + + // Angle offset by player angle, then slightly offset by amount of fling + fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT) - ((numfling-1)*FINEANGLES/32)) & FINEMASK; + + if (i > 15) + { + momxy = 3*FRACUNIT; + momz = 4*FRACUNIT; + } + else + { + momxy = 28*FRACUNIT; + momz = 3*FRACUNIT; + } + + ns = FixedMul(momxy, mo->scale); + mo->momx = FixedMul(FINECOSINE(fa),ns); + + ns = momz; + P_SetObjectMomZ(mo, ns, false); + + if (i & 1) + P_SetObjectMomZ(mo, ns, true); + + if (player->mo->eflags & MFE_VERTICALFLIP) + mo->momz *= -1; + } +} + +// Adds gravity flipping to an object relative to its master and shifts the z coordinate accordingly. +void K_FlipFromObject(mobj_t *mo, mobj_t *master) +{ + mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); + mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); + + if (mo->eflags & MFE_VERTICALFLIP) + mo->z += master->height - FixedMul(master->scale, mo->height); +} + +void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master) +{ + // flipping + // handle z shifting from there too. This is here since there's no reason not to flip us if needed when we do this anyway; + K_FlipFromObject(mo, master); + + // visibility (usually for hyudoro) + mo->drawflags = (master->drawflags & MFD_DONTDRAW); +} + +// same as above, but does not adjust Z height when flipping +void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master) +{ + // flipping + mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); + mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP); + + // visibility (usually for hyudoro) + mo->drawflags = (master->drawflags & MFD_DONTDRAW); +} + + +void K_SpawnDashDustRelease(player_t *player) +{ + fixed_t newx; + fixed_t newy; + mobj_t *dust; + angle_t travelangle; + INT32 i; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (!P_IsObjectOnGround(player->mo)) + return; + + if (!player->speed && !player->kartstuff[k_startboost]) + return; + + travelangle = player->mo->angle; + + if (player->kartstuff[k_drift] || player->kartstuff[k_driftend]) + travelangle -= (ANGLE_45/5)*player->kartstuff[k_drift]; + + for (i = 0; i < 2; i++) + { + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale)); + dust = P_SpawnMobj(newx, newy, player->mo->z, MT_FASTDUST); + + P_SetTarget(&dust->target, player->mo); + dust->angle = travelangle - ((i&1) ? -1 : 1)*ANGLE_45; + dust->destscale = player->mo->scale; + P_SetScale(dust, player->mo->scale); + + dust->momx = 3*player->mo->momx/5; + dust->momy = 3*player->mo->momy/5; + //dust->momz = 3*player->mo->momz/5; + + K_MatchGenericExtraFlags(dust, player->mo); + } +} + +static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the mobj thinker case too! +{ + mobj_t *sparks; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + // Position & etc are handled in its thinker, and its spawned invisible. + // This avoids needing to dupe code if we don't need it. + sparks = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BRAKEDRIFT); + P_SetTarget(&sparks->target, player->mo); + P_SetScale(sparks, (sparks->destscale = player->mo->scale)); + K_MatchGenericExtraFlags(sparks, player->mo); + sparks->drawflags |= MFD_DONTDRAW; +} + +static fixed_t K_RandomFlip(fixed_t f) +{ + return ( ( leveltime & 1 ) ? f : -f ); +} + +void K_SpawnDriftBoostClip(player_t *player) +{ + mobj_t *clip; + fixed_t scale = 115*FRACUNIT/100; + fixed_t z; + + if (( player->mo->eflags & MFE_VERTICALFLIP )) + z = player->mo->z; + else + z = player->mo->z + player->mo->height; + + clip = P_SpawnMobj(player->mo->x, player->mo->y, z, MT_DRIFTCLIP); + + P_SetTarget(&clip->target, player->mo); + P_SetScale(clip, ( clip->destscale = FixedMul(scale, player->mo->scale) )); + K_MatchGenericExtraFlags(clip, player->mo); + + clip->fuse = 105; + clip->momz = 7 * P_MobjFlip(clip) * clip->scale; + + P_InstaThrust(clip, player->mo->angle + + K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), + FixedMul(scale, player->speed)); +} + +void K_SpawnDriftBoostClipSpark(mobj_t *clip) +{ + mobj_t *spark; + + spark = P_SpawnMobj(clip->x, clip->y, clip->z, MT_DRIFTCLIPSPARK); + + P_SetTarget(&spark->target, clip); + P_SetScale(spark, ( spark->destscale = clip->scale )); + K_MatchGenericExtraFlags(spark, clip); + + spark->momx = clip->momx/2; + spark->momy = clip->momx/2; +} + +/** \brief Handles the state changing for moving players, moved here to eliminate duplicate code + + \param player player data + + \return void +*/ +void K_KartMoveAnimation(player_t *player) +{ + const INT16 minturn = KART_FULLTURN/8; + SINT8 turndir = 0; + + const fixed_t fastspeed = (K_GetKartSpeed(player, false) * 17) / 20; // 85% + const fixed_t speedthreshold = player->mo->scale / 8; + + const boolean onground = P_IsObjectOnGround(player->mo); + + ticcmd_t *cmd = &player->cmd; + const boolean spinningwheels = ((cmd->buttons & BT_ACCELERATE) || (onground && player->speed > 0)); + + if (cmd->driftturn < -minturn) + { + turndir = -1; + } + else if (cmd->driftturn > minturn) + { + turndir = 1; + } + + if (!onground) + { + // Only use certain frames in the air, to make it look like your tires are spinning fruitlessly! + + if (player->kartstuff[k_drift] > 0) + { + if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); + } + } + else if (player->kartstuff[k_drift] > 0) + { + if (!spinningwheels || !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); + } + } + else + { + if ((turndir == -1) + && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R]))) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST2_R); + } + else if ((turndir == 1) + && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L]))) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST2_L); + } + else if ((turndir == 0) + && (!spinningwheels || !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2]))) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST2); + } + } + } + else + { + if (player->kartstuff[k_drift] > 0) + { + // Drifting LEFT! + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_DRIFT1_L_OUT] && player->mo->state <= &states[S_KART_DRIFT2_L_OUT])) + { + // Right -- outwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_OUT); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_DRIFT1_L_IN] && player->mo->state <= &states[S_KART_DRIFT4_L_IN])) + { + // Left -- inwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L_IN); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L); + } + } + else if (player->kartstuff[k_drift] < 0) + { + // Drifting RIGHT! + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_DRIFT1_R_IN] && player->mo->state <= &states[S_KART_DRIFT4_R_IN])) + { + // Right -- inwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_IN); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_DRIFT1_R_OUT] && player->mo->state <= &states[S_KART_DRIFT2_R_OUT])) + { + // Left -- outwards drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R_OUT); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R])) + { + // Neutral drift + P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R); + } + } + else + { + if (player->speed >= fastspeed && player->speed >= (player->lastspeed - speedthreshold)) + { + // Going REAL fast! + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_FAST1_R] && player->mo->state <= &states[S_KART_FAST2_R])) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST1_R); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_FAST1_L] && player->mo->state <= &states[S_KART_FAST2_L])) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST1_L); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_FAST1] && player->mo->state <= &states[S_KART_FAST2])) + { + P_SetPlayerMobjState(player->mo, S_KART_FAST1); + } + } + else + { + if (spinningwheels) + { + // Drivin' slow. + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_SLOW1_R] && player->mo->state <= &states[S_KART_SLOW2_R])) + { + P_SetPlayerMobjState(player->mo, S_KART_SLOW1_R); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_SLOW1_L] && player->mo->state <= &states[S_KART_SLOW2_L])) + { + P_SetPlayerMobjState(player->mo, S_KART_SLOW1_L); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_SLOW1] && player->mo->state <= &states[S_KART_SLOW2])) + { + P_SetPlayerMobjState(player->mo, S_KART_SLOW1); + } + } + else + { + // Completely still. + + if ((turndir == -1) + && !(player->mo->state >= &states[S_KART_STILL1_R] && player->mo->state <= &states[S_KART_STILL2_R])) + { + P_SetPlayerMobjState(player->mo, S_KART_STILL1_R); + } + else if ((turndir == 1) + && !(player->mo->state >= &states[S_KART_STILL1_L] && player->mo->state <= &states[S_KART_STILL2_L])) + { + P_SetPlayerMobjState(player->mo, S_KART_STILL1_L); + } + else if ((turndir == 0) + && !(player->mo->state >= &states[S_KART_STILL1] && player->mo->state <= &states[S_KART_STILL2])) + { + P_SetPlayerMobjState(player->mo, S_KART_STILL1); + } + } + } + } + } + + // Update lastspeed value -- we use to display slow driving frames instead of fast driving when slowing down. + player->lastspeed = player->speed; +} + +static void K_TauntVoiceTimers(player_t *player) +{ + if (!player) + return; + + player->karthud[khud_tauntvoices] = 6*TICRATE; + player->karthud[khud_voices] = 4*TICRATE; +} + +static void K_RegularVoiceTimers(player_t *player) +{ + if (!player) + return; + + player->karthud[khud_voices] = 4*TICRATE; + + if (player->karthud[khud_tauntvoices] < 4*TICRATE) + player->karthud[khud_tauntvoices] = 4*TICRATE; +} + +void K_PlayAttackTaunt(mobj_t *source) +{ + sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons + boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); + + if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) + S_StartSound(source, sfx_kattk1+pick); + + if (!tasteful) + return; + + K_TauntVoiceTimers(source->player); +} + +void K_PlayBoostTaunt(mobj_t *source) +{ + sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons + boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); + + if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) + S_StartSound(source, sfx_kbost1+pick); + + if (!tasteful) + return; + + K_TauntVoiceTimers(source->player); +} + +void K_PlayOvertakeSound(mobj_t *source) +{ + boolean tasteful = (!source->player || !source->player->karthud[khud_voices]); + + if (!G_RaceGametype()) // Only in race + return; + + // 4 seconds from before race begins, 10 seconds afterwards + if (leveltime < starttime+(10*TICRATE)) + return; + + if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) + S_StartSound(source, sfx_kslow); + + if (!tasteful) + return; + + K_RegularVoiceTimers(source->player); +} + +void K_PlayPainSound(mobj_t *source) +{ + sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons + + if (cv_kartvoices.value) + S_StartSound(source, sfx_khurt1 + pick); + + K_RegularVoiceTimers(source->player); +} + +void K_PlayHitEmSound(mobj_t *source) +{ + + if (source->player->follower) + { + follower_t fl = followers[source->player->followerskin]; + source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers. + } + + if (cv_kartvoices.value) + S_StartSound(source, sfx_khitem); + else + S_StartSound(source, sfx_s1c9); // The only lost gameplay functionality with voices disabled + + K_RegularVoiceTimers(source->player); +} + +void K_PlayPowerGloatSound(mobj_t *source) +{ + if (cv_kartvoices.value) + S_StartSound(source, sfx_kgloat); + + K_RegularVoiceTimers(source->player); +} + +void K_MomentumToFacing(player_t *player) +{ + angle_t dangle = player->mo->angle - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + + if (dangle > ANGLE_180) + dangle = InvAngle(dangle); + + // If you aren't on the ground or are moving in too different of a direction don't do this + if (player->mo->eflags & MFE_JUSTHITFLOOR) + ; // Just hit floor ALWAYS redirects + else if (!P_IsObjectOnGround(player->mo) || dangle > ANGLE_90) + return; + + P_Thrust(player->mo, player->mo->angle, player->speed - FixedMul(player->speed, player->mo->friction)); + player->mo->momx = FixedMul(player->mo->momx - player->cmomx, player->mo->friction) + player->cmomx; + player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy; +} + +boolean K_ApplyOffroad(player_t *player) +{ + if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer]) + return false; + return true; +} + +static fixed_t K_FlameShieldDashVar(INT32 val) +{ + // 1 second = 75% + 50% top speed + return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); +} + +// sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be +static void K_GetKartBoostPower(player_t *player) +{ + fixed_t boostpower = FRACUNIT; + fixed_t speedboost = 0, accelboost = 0; + UINT8 numboosts = 0; + + if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow] == 1) // Slow down after you've been bumped + { + player->kartstuff[k_boostpower] = player->kartstuff[k_speedboost] = player->kartstuff[k_accelboost] = 0; + return; + } + + // Offroad is separate, it's difficult to factor it in with a variable value anyway. + if (K_ApplyOffroad(player) && player->kartstuff[k_offroad] >= 0) + boostpower = FixedDiv(boostpower, FixedMul(player->kartstuff[k_offroad], K_GetKartGameSpeedScalar(gamespeed)) + FRACUNIT); + + if (player->kartstuff[k_bananadrag] > TICRATE) + boostpower = (4*boostpower)/5; + +#define ADDBOOST(s,a) { \ + numboosts++; \ + speedboost += (s) / numboosts; \ + accelboost += (a) / numboosts; \ +} + + if (player->kartstuff[k_sneakertimer]) // Sneaker + { + UINT8 i; + for (i = 0; i < player->kartstuff[k_numsneakers]; i++) + { + ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration + } + } + + if (player->kartstuff[k_invincibilitytimer]) // Invincibility + { + ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration + } + + if (player->kartstuff[k_flamedash]) // Flame Shield dash + { + ADDBOOST(K_FlameShieldDashVar(player->kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration + } + + if (player->kartstuff[k_startboost]) // Startup Boost + { + ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration + } + + if (player->kartstuff[k_driftboost]) // Drift Boost + { + ADDBOOST(FRACUNIT/4, 4*FRACUNIT); // + 25% top speed, + 400% acceleration + } + + if (player->kartstuff[k_ringboost]) // Ring Boost + { + ADDBOOST(FRACUNIT/5, 4*FRACUNIT); // + 20% top speed, + 400% acceleration + } + + if (player->kartstuff[k_eggmanexplode]) // Ready-to-explode + { + ADDBOOST(3*FRACUNIT/20, FRACUNIT); // + 15% top speed, + 100% acceleration + } + + if (player->kartstuff[k_draftpower] > 0) // Drafting + { + fixed_t draftspeed = ((3*FRACUNIT)/10) + ((player->kartspeed-1) * (FRACUNIT/50)); // min is 30%, max is 46% + speedboost += FixedMul(draftspeed, player->kartstuff[k_draftpower]); // (Drafting suffers no boost stack penalty.) + numboosts++; + } + + player->kartstuff[k_boostpower] = boostpower; + + // value smoothing + if (speedboost > player->kartstuff[k_speedboost]) + { + player->kartstuff[k_speedboost] = speedboost; + } + else + { + player->kartstuff[k_speedboost] += (speedboost - player->kartstuff[k_speedboost]) / (TICRATE/2); + } + + player->kartstuff[k_accelboost] = accelboost; + player->kartstuff[k_numboosts] = numboosts; +} + +// Returns kart speed from a stat. Boost power and scale are NOT taken into account, no player or object is necessary. +fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed) +{ + const fixed_t xspd = (3*FRACUNIT)/64; + fixed_t g_cc = K_GetKartGameSpeedScalar(gamespeed) + xspd; + fixed_t k_speed = 150; + fixed_t finalspeed; + + k_speed += kartspeed*3; // 153 - 177 + + finalspeed = FixedMul(k_speed<<14, g_cc); + return finalspeed; +} + +fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) +{ + fixed_t finalspeed; + UINT8 kartspeed = player->kartspeed; + + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + kartspeed = 1; + + finalspeed = K_GetKartSpeedFromStat(kartspeed); + + 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 + + if (player->botvars.rival == true) + { + speedmul += FRACUNIT/10; // +10% for rival + } + + finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul); + } + + if (player->mo && !P_MobjWasRemoved(player->mo)) + finalspeed = FixedMul(finalspeed, player->mo->scale); + + if (doboostpower) + { + if (K_PlayerUsesBotMovement(player)) + { + finalspeed = FixedMul(finalspeed, K_BotTopSpeedRubberband(player)); + } + + return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]); + } + + return finalspeed; +} + +fixed_t K_GetKartAccel(player_t *player) +{ + fixed_t k_accel = 32; // 36; + UINT8 kartspeed = player->kartspeed; + + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + kartspeed = 1; + + //k_accel += 3 * (9 - kartspeed); // 36 - 60 + k_accel += 4 * (9 - kartspeed); // 32 - 64 + + + if (K_PlayerUsesBotMovement(player)) + { + // Rubberbanding acceleration is waekened since it makes hits feel more meaningful + fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; + k_accel = FixedMul(k_accel, FRACUNIT + (rubberband/2)); + } + + return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]); +} + +UINT16 K_GetKartFlashing(player_t *player) +{ + UINT16 tics = flashingtics; + + if (!player) + return tics; + + if (G_BattleGametype()) + tics *= 2; + + tics += (flashingtics/8) * (player->kartspeed); + + return tics; +} + +fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove) +{ + const fixed_t accelmax = 4000; + const fixed_t p_speed = K_GetKartSpeed(player, true); + const fixed_t p_accel = K_GetKartAccel(player); + fixed_t newspeed, oldspeed, finalspeed; + fixed_t orig = ORIG_FRICTION; + + if (!onground) return 0; // If the player isn't on the ground, there is no change in speed + + if (K_PlayerUsesBotMovement(player)) + { + orig = K_BotFrictionRubberband(player, ORIG_FRICTION); + } + + // ACCELCODE!!!1!11! + oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale); + // Don't calculate the acceleration as ever being above top speed + if (oldspeed > p_speed) + oldspeed = p_speed; + newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), orig); + + if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust + { + const fixed_t hscale = mapobjectscale /*+ (mapobjectscale - player->mo->scale)*/; + const fixed_t minspeed = 24*hscale; + const fixed_t maxspeed = 28*hscale; + + if (newspeed > maxspeed && player->kartstuff[k_pogospring] == 2) + newspeed = maxspeed; + if (newspeed < minspeed) + newspeed = minspeed; + } + + finalspeed = newspeed - oldspeed; + + // forwardmove is: + // 50 while accelerating, + // 25 while clutching, + // 0 with no gas, and + // -25 when only braking. + + if (player->kartstuff[k_sneakertimer]) + forwardmove = 50; + + finalspeed *= forwardmove/25; + finalspeed /= 2; + + if (forwardmove < 0 && finalspeed > mapobjectscale*2) + return finalspeed/2; + else if (forwardmove < 0) + return -mapobjectscale/2; + + if (finalspeed < 0) + finalspeed = 0; + + return finalspeed; +} + +void K_DoInstashield(player_t *player) +{ + mobj_t *layera; + mobj_t *layerb; + + if (player->kartstuff[k_instashield] > 0) + return; + + player->kartstuff[k_instashield] = 15; // length of instashield animation + S_StartSound(player->mo, sfx_cdpcm9); + + layera = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDA); + P_SetTarget(&layera->target, player->mo); + + layerb = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDB); + P_SetTarget(&layerb->target, player->mo); +} + +void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem) +{ + UINT8 scoremultiply = 1; + // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. +#ifdef HAVE_BLUA + boolean force = false; // Used to check if Lua ShouldSpin should get us damaged reguardless of flashtics or heck knows what. + UINT8 shouldForce = LUAh_ShouldSpin(player, inflictor, source); + if (P_MobjWasRemoved(player->mo)) + return; // mobj was removed (in theory that shouldn't happen) + if (shouldForce == 1) + force = true; + else if (shouldForce == 2) + return; +#else + static const boolean force = false; + (void)inflictor; // in case some weirdo doesn't want Lua. +#endif + + if (!trapitem && G_BattleGametype()) + { + if (K_IsPlayerWanted(player)) + scoremultiply = 3; + else if (player->kartstuff[k_bumper] == 1) + scoremultiply = 2; + } + + if (player->health <= 0) + return; + + if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2) + || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + { + if (!force) // if shoulddamage force, we go THROUGH that. + { + K_DoInstashield(player); + return; + } + } + +#ifdef HAVE_BLUA + if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. + return; +#endif + + if (source && source != player->mo && source->player) + K_PlayHitEmSound(source); + + player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_numsneakers] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; + + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + if (G_BattleGametype()) + { + if (source && source->player && player != source->player) + { + P_AddPlayerScore(source->player, scoremultiply); + K_SpawnBattlePoints(source->player, player, scoremultiply); + if (!trapitem) + { + source->player->kartstuff[k_wanted] -= wantedreduce; + player->kartstuff[k_wanted] -= (wantedreduce/2); + } + } + + 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]) + { + 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(); + } + + player->kartstuff[k_spinouttype] = type; + + if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout, type 2 is no-invuln wipeout + { + // At spinout, player speed is increased to 1/4 their regular speed, moving them forward + if (player->speed < K_GetKartSpeed(player, true)/4) + P_InstaThrust(player->mo, player->mo->angle, FixedMul(K_GetKartSpeed(player, true)/4, player->mo->scale)); + S_StartSound(player->mo, sfx_slip); + } + + player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; + player->powers[pw_flashing] = K_GetKartFlashing(player); + + P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 5); + K_PlayPainSound(player->mo); + + if (player->mo->state != &states[S_KART_SPIN]) + P_SetPlayerMobjState(player->mo, S_KART_SPIN); + + player->kartstuff[k_instashield] = 15; + if (cv_kartdebughuddrop.value && !modeattacking) + K_DropItems(player); + else + K_DropHnextList(player, false); + return; +} + +static void K_RemoveGrowShrink(player_t *player) +{ + if (player->mo && !P_MobjWasRemoved(player->mo)) + { + if (player->kartstuff[k_growshrinktimer] > 0) // Play Shrink noise + S_StartSound(player->mo, sfx_kc59); + else if (player->kartstuff[k_growshrinktimer] < 0) // Play Grow noise + S_StartSound(player->mo, sfx_kc5a); + + if (player->kartstuff[k_invincibilitytimer] == 0) + player->mo->color = player->skincolor; + + player->mo->scalespeed = mapobjectscale/TICRATE; + player->mo->destscale = mapobjectscale; + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + player->mo->destscale = (6*player->mo->destscale)/8; + } + + player->kartstuff[k_growshrinktimer] = 0; + + P_RestoreMusic(player); +} + +void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) +{ + UINT8 scoremultiply = 1; + // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too. +#ifdef HAVE_BLUA + boolean force = false; // Used to check if Lua ShouldSquish should get us damaged reguardless of flashtics or heck knows what. + UINT8 shouldForce = LUAh_ShouldSquish(player, inflictor, source); + if (P_MobjWasRemoved(player->mo)) + return; // mobj was removed (in theory that shouldn't happen) + if (shouldForce == 1) + force = true; + else if (shouldForce == 2) + return; +#else + static const boolean force = false; + (void)inflictor; // Please stop forgetting to put inflictor in yer functions thank -Lat' +#endif + + if (G_BattleGametype()) + { + if (K_IsPlayerWanted(player)) + scoremultiply = 3; + else if (player->kartstuff[k_bumper] == 1) + scoremultiply = 2; + } + + if (player->health <= 0) + return; + + if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_invincibilitytimer] > 0 + || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + { + if (!force) // You know the drill by now. + { + K_DoInstashield(player); + return; + } + } + +#ifdef HAVE_BLUA + if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case. + return; +#endif + + player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_numsneakers] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; + + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + if (G_BattleGametype()) + { + if (source && source->player && player != source->player) + { + P_AddPlayerScore(source->player, scoremultiply); + K_SpawnBattlePoints(source->player, player, scoremultiply); + source->player->kartstuff[k_wanted] -= wantedreduce; + player->kartstuff[k_wanted] -= (wantedreduce/2); + } + + 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]) + { + 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(); + } + + player->kartstuff[k_squishedtimer] = TICRATE; + + // Reduce Shrink timer + if (player->kartstuff[k_growshrinktimer] < 0) + { + player->kartstuff[k_growshrinktimer] += TICRATE; + if (player->kartstuff[k_growshrinktimer] >= 0) + K_RemoveGrowShrink(player); + } + + player->powers[pw_flashing] = K_GetKartFlashing(player); + + player->mo->flags |= MF_NOCLIP; + + if (player->mo->state != &states[S_KART_SQUISH]) // Squash + P_SetPlayerMobjState(player->mo, S_KART_SQUISH); + + P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 5); + K_PlayPainSound(player->mo); + + player->kartstuff[k_instashield] = 15; + if (cv_kartdebughuddrop.value && !modeattacking) + K_DropItems(player); + else + K_DropHnextList(player, false); + return; +} + +void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer +{ + UINT8 scoremultiply = 1; +#ifdef HAVE_BLUA + boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what. + UINT8 shouldForce = LUAh_ShouldExplode(player, inflictor, source); + + if (P_MobjWasRemoved(player->mo)) + return; // mobj was removed (in theory that shouldn't happen) + if (shouldForce == 1) + force = true; + else if (shouldForce == 2) + return; + +#else + static const boolean force = false; +#endif + + if (G_BattleGametype()) + { + if (K_IsPlayerWanted(player)) + scoremultiply = 3; + else if (player->kartstuff[k_bumper] == 1) + scoremultiply = 2; + } + + if (player->health <= 0) + return; + + if (player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 // Do not check spinout, because SPB and Eggman should combo + || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1))) + { + if (!force) // ShouldDamage can bypass that, again. + { + K_DoInstashield(player); + return; + } + } + +#ifdef HAVE_BLUA + if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah + return; +#endif + + if (source && source != player->mo && source->player) + K_PlayHitEmSound(source); + + player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!! + player->mo->momx = player->mo->momy = 0; + + player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_numsneakers] = 0; + player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; + + player->kartstuff[k_drift] = 0; + player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_pogospring] = 0; + + // This is the only part that SHOULDN'T combo :VVVVV + if (G_BattleGametype() && !(player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2))) + { + if (source && source->player && player != source->player) + { + P_AddPlayerScore(source->player, scoremultiply); + K_SpawnBattlePoints(source->player, player, scoremultiply); + source->player->kartstuff[k_wanted] -= wantedreduce; + player->kartstuff[k_wanted] -= (wantedreduce/2); + } + + 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]) + { + 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(); + } + + player->kartstuff[k_spinouttype] = 1; + player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2; + + player->powers[pw_flashing] = K_GetKartFlashing(player); + + if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1) + { + player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1; + player->mo->momz *= 2; + } + + if (player->mo->eflags & MFE_UNDERWATER) + player->mo->momz = (117 * player->mo->momz) / 200; + + if (player->mo->state != &states[S_KART_SPIN]) + P_SetPlayerMobjState(player->mo, S_KART_SPIN); + + P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 5); + K_PlayPainSound(player->mo); + + if (P_IsDisplayPlayer(player)) + P_StartQuake(64<kartstuff[k_instashield] = 15; + K_DropItems(player); + + return; +} + +void K_StealBumper(player_t *player, player_t *victim, boolean force) +{ + INT32 newbumper; + angle_t newangle, diff; + fixed_t newx, newy; + mobj_t *newmo; + + if (!G_BattleGametype()) + return; + + if (player->health <= 0 || victim->health <= 0) + return; + + if (!force) + { + if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= K_StartingBumperCount()+2 + return; + + if (player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0) + return; + + if (victim->powers[pw_flashing] > 0 || victim->kartstuff[k_squishedtimer] > 0 || (victim->kartstuff[k_spinouttimer] > 0 && victim->kartstuff[k_spinouttype] != 2) + || victim->kartstuff[k_invincibilitytimer] > 0 || victim->kartstuff[k_growshrinktimer] > 0 || victim->kartstuff[k_hyudorotimer] > 0) + { + K_DoInstashield(victim); + return; + } + } + + if (netgame && player->kartstuff[k_bumper] <= 0) + CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); + + newbumper = player->kartstuff[k_bumper]; + 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); + + 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; +} + +// source is the mobj that originally threw the bomb that exploded etc. +// Spawns the sphere around the explosion that handles spinout +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) +{ + mobj_t *mobj; + mobj_t *ghost = NULL; + INT32 i; + TVector v; + TVector *res; + fixed_t finalx, finaly, finalz, dist; + //mobj_t hoopcenter; + angle_t degrees, fa, closestangle; + fixed_t mobjx, mobjy, mobjz; + + //hoopcenter.x = x; + //hoopcenter.y = y; + //hoopcenter.z = z; + + //hoopcenter.z = z - mobjinfo[type].height/2; + + degrees = FINEANGLES/number; + + closestangle = 0; + + // Create the hoop! + for (i = 0; i < number; i++) + { + fa = (i*degrees); + v[0] = FixedMul(FINECOSINE(fa),radius); + v[1] = 0; + v[2] = FixedMul(FINESINE(fa),radius); + v[3] = FRACUNIT; + + res = VectorMatrixMultiply(v, *RotateXMatrix(rotangle)); + M_Memcpy(&v, res, sizeof (v)); + res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle)); + M_Memcpy(&v, res, sizeof (v)); + + finalx = x + v[0]; + finaly = y + v[1]; + finalz = z + v[2]; + + mobj = P_SpawnMobj(finalx, finaly, finalz, type); + + mobj->z -= mobj->height>>1; + + // change angle + mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y); + + // change slope + dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z); + + if (dist < 1) + dist = 1; + + mobjx = mobj->x; + mobjy = mobj->y; + mobjz = mobj->z; + + if (ghostit) + { + ghost = P_SpawnGhostMobj(mobj); + P_SetMobjState(mobj, S_NULL); + mobj = ghost; + } + + if (spawncenter) + { + mobj->x = x; + mobj->y = y; + mobj->z = z; + } + + mobj->momx = FixedMul(FixedDiv(mobjx - x, dist), FixedDiv(dist, 6*FRACUNIT)); + mobj->momy = FixedMul(FixedDiv(mobjy - y, dist), FixedDiv(dist, 6*FRACUNIT)); + mobj->momz = FixedMul(FixedDiv(mobjz - z, dist), FixedDiv(dist, 6*FRACUNIT)); + + if (source && !P_MobjWasRemoved(source)) + P_SetTarget(&mobj->target, source); + } +} + +#define MINEQUAKEDIST 4096 + +// Spawns the purely visual explosion +void K_SpawnMineExplosion(mobj_t *source, UINT8 color) +{ + INT32 i, radius, height; + mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING); + mobj_t *dust; + mobj_t *truc; + INT32 speed, speed2; + INT32 pnum; + player_t *p; + + // check for potential display players near the source so we can have a sick earthquake / flashpal. + for (pnum = 0; pnum < MAXPLAYERS; pnum++) + { + p = &players[pnum]; + + if (!playeringame[pnum] || !P_IsDisplayPlayer(p)) + continue; + + if (R_PointToDist2(p->mo->x, p->mo->y, source->x, source->y) < mapobjectscale*MINEQUAKEDIST) + { + P_StartQuake(55<mo, source)) + { + bombflashtimer = TICRATE*2; + P_FlashPal(p, 1, 1); + } + break; // we can break right now because quakes are global to all split players somehow. + } + } + + K_MatchGenericExtraFlags(smoldering, source); + smoldering->tics = TICRATE*3; + radius = source->radius>>FRACBITS; + height = source->height>>FRACBITS; + + if (!color) + color = SKINCOLOR_KETCHUP; + + for (i = 0; i < 32; i++) + { + dust = P_SpawnMobj(source->x, source->y, source->z, MT_SMOKE); + P_SetMobjState(dust, S_OPAQUESMOKE1); + dust->angle = (ANGLE_180/16) * i; + P_SetScale(dust, source->scale); + dust->destscale = source->scale*10; + dust->scalespeed = source->scale/12; + P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, source->scale)); + + truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, + source->y + P_RandomRange(-radius, radius)*FRACUNIT, + source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMEXPLODE); + K_MatchGenericExtraFlags(truc, source); + P_SetScale(truc, source->scale); + truc->destscale = source->scale*6; + truc->scalespeed = source->scale/12; + speed = FixedMul(10*FRACUNIT, source->scale)>>FRACBITS; + truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; + truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; + speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; + truc->momz = P_RandomRange(-speed, speed)*FRACUNIT*P_MobjFlip(truc); + if (truc->eflags & MFE_UNDERWATER) + truc->momz = (117 * truc->momz) / 200; + truc->color = color; + } + + for (i = 0; i < 16; i++) + { + dust = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, + source->y + P_RandomRange(-radius, radius)*FRACUNIT, + source->z + P_RandomRange(0, height)*FRACUNIT, MT_SMOKE); + P_SetMobjState(dust, S_OPAQUESMOKE1); + P_SetScale(dust, source->scale); + dust->destscale = source->scale*10; + dust->scalespeed = source->scale/12; + dust->tics = 30; + dust->momz = P_RandomRange(FixedMul(3*FRACUNIT, source->scale)>>FRACBITS, FixedMul(7*FRACUNIT, source->scale)>>FRACBITS)*FRACUNIT; + + truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT, + source->y + P_RandomRange(-radius, radius)*FRACUNIT, + source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMPARTICLE); + K_MatchGenericExtraFlags(truc, source); + P_SetScale(truc, source->scale); + truc->destscale = source->scale*5; + truc->scalespeed = source->scale/12; + speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS; + truc->momx = P_RandomRange(-speed, speed)*FRACUNIT; + truc->momy = P_RandomRange(-speed, speed)*FRACUNIT; + speed = FixedMul(15*FRACUNIT, source->scale)>>FRACBITS; + speed2 = FixedMul(45*FRACUNIT, source->scale)>>FRACBITS; + truc->momz = P_RandomRange(speed, speed2)*FRACUNIT*P_MobjFlip(truc); + if (P_RandomChance(FRACUNIT/2)) + truc->momz = -truc->momz; + if (truc->eflags & MFE_UNDERWATER) + truc->momz = (117 * truc->momz) / 200; + truc->tics = TICRATE*2; + truc->color = color; + } +} + +#undef MINEQUAKEDIST + +static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed) +{ + mobj_t *th; + fixed_t x, y, z; + fixed_t finalspeed = speed; + mobj_t *throwmo; + + if (source->player && source->player->speed > K_GetKartSpeed(source->player, false)) + { + angle_t input = source->angle - an; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + finalspeed = max(speed, FixedMul(speed, FixedMul( + FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed. + (((180<x + source->momx + FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); + y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); + z = source->z; // spawn on the ground please + + if (P_MobjFlip(source) < 0) + { + z = source->z+source->height - mobjinfo[type].height; + } + + th = P_SpawnMobj(x, y, z, type); + + th->flags2 |= flags2; + th->threshold = 10; + + if (th->info->seesound) + S_StartSound(source, th->info->seesound); + + P_SetTarget(&th->target, source); + + P_SetScale(th, source->scale); + th->destscale = source->destscale; + + if (P_IsObjectOnGround(source)) + { + // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn + // This should set it for FOFs + P_TeleportMove(th, th->x, th->y, th->z); + // spawn on the ground if the player is on the ground + if (P_MobjFlip(source) < 0) + { + th->z = th->ceilingz - th->height; + th->eflags |= MFE_VERTICALFLIP; + } + else + th->z = th->floorz; + } + + th->angle = an; + th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT)); + th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); + + switch (type) + { + case MT_ORBINAUT: + if (source && source->player) + th->color = source->player->skincolor; + else + th->color = SKINCOLOR_GREY; + th->movefactor = finalspeed; + break; + case MT_JAWZ: + if (source && source->player) + { + INT32 lasttarg = source->player->kartstuff[k_lastjawztarget]; + th->cvmem = source->player->skincolor; + if ((lasttarg >= 0 && lasttarg < MAXPLAYERS) + && playeringame[lasttarg] + && !players[lasttarg].spectator + && players[lasttarg].mo) + { + P_SetTarget(&th->tracer, players[lasttarg].mo); + } + } + else + th->cvmem = SKINCOLOR_KETCHUP; + /* FALLTHRU */ + case MT_JAWZ_DUD: + S_StartSound(th, th->info->activesound); + /* FALLTHRU */ + case MT_SPB: + th->movefactor = finalspeed; + break; + case MT_BUBBLESHIELDTRAP: + P_SetScale(th, ((5*th->destscale)>>2)*4); + th->destscale = (5*th->destscale)>>2; + S_StartSound(th, sfx_s3kbfl); + S_StartSound(th, sfx_cdfm35); + break; + default: + break; + } + + if (type != MT_BUBBLESHIELDTRAP) + { + x = x + P_ReturnThrustX(source, an, source->radius + th->radius); + y = y + P_ReturnThrustY(source, an, source->radius + th->radius); + throwmo = P_SpawnMobj(x, y, z, MT_FIREDITEM); + throwmo->movecount = 1; + throwmo->movedir = source->angle - an; + P_SetTarget(&throwmo->target, source); + } + + return NULL; +} + +UINT8 K_DriftSparkColor(player_t *player, INT32 charge) +{ + INT32 ds = K_GetKartDriftSparkValue(player); + UINT8 color = SKINCOLOR_NONE; + + if (charge < 0) + { + // Stage 0: Yellow + color = SKINCOLOR_GOLD; + } + else if (charge >= ds*4) + { + // Stage 3: Rainbow + if (charge <= (ds*4)+(32*3)) + { + // transition + color = SKINCOLOR_SILVER; + } + else + { + color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + } + } + else if (charge >= ds*2) + { + // Stage 2: Blue + if (charge <= (ds*2)+(32*3)) + { + // transition + color = SKINCOLOR_PURPLE; + } + else + { + color = SKINCOLOR_SAPPHIRE; + } + } + else if (charge >= ds) + { + // Stage 1: Red + if (charge <= (ds)+(32*3)) + { + // transition + color = SKINCOLOR_TANGERINE; + } + else + { + color = SKINCOLOR_KETCHUP; + } + } + + return color; +} + +static void K_SpawnDriftSparks(player_t *player) +{ + INT32 ds = K_GetKartDriftSparkValue(player); + fixed_t newx; + fixed_t newy; + mobj_t *spark; + angle_t travelangle; + INT32 i; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (leveltime % 2 == 1) + return; + + if (!player->kartstuff[k_drift] + || (player->kartstuff[k_driftcharge] < ds && !(player->kartstuff[k_driftcharge] < 0))) + return; + + travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; + + for (i = 0; i < 2; i++) + { + SINT8 size = 1; + UINT8 trail = 0; + + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale)); + spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK); + + P_SetTarget(&spark->target, player->mo); + spark->angle = travelangle-(ANGLE_45/5)*player->kartstuff[k_drift]; + spark->destscale = player->mo->scale; + P_SetScale(spark, player->mo->scale); + + spark->momx = player->mo->momx/2; + spark->momy = player->mo->momy/2; + //spark->momz = player->mo->momz/2; + + spark->color = K_DriftSparkColor(player, player->kartstuff[k_driftcharge]); + + if (player->kartstuff[k_driftcharge] < 0) + { + // Stage 0: Yellow + size = 0; + } + else if (player->kartstuff[k_driftcharge] >= ds*4) + { + // Stage 3: Rainbow + size = 2; + trail = 2; + + if (player->kartstuff[k_driftcharge] <= (ds*4)+(32*3)) + { + // transition + P_SetScale(spark, (spark->destscale = spark->scale*3/2)); + } + else + { + spark->colorized = true; + } + } + else if (player->kartstuff[k_driftcharge] >= ds*2) + { + // Stage 2: Blue + size = 2; + trail = 1; + + if (player->kartstuff[k_driftcharge] <= (ds*2)+(32*3)) + { + // transition + P_SetScale(spark, (spark->destscale = spark->scale*3/2)); + } + } + else + { + // Stage 1: Red + size = 1; + + if (player->kartstuff[k_driftcharge] <= (ds)+(32*3)) + { + // transition + P_SetScale(spark, (spark->destscale = spark->scale*2)); + } + } + + if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts + || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn < 0)) + { + if ((player->kartstuff[k_drift] < 0 && (i & 1)) + || (player->kartstuff[k_drift] > 0 && !(i & 1))) + { + size++; + } + else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) + || (player->kartstuff[k_drift] > 0 && (i & 1))) + { + size--; + } + } + else if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn < 0) // Outward drifts + || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn > 0)) + { + if ((player->kartstuff[k_drift] < 0 && (i & 1)) + || (player->kartstuff[k_drift] > 0 && !(i & 1))) + { + size--; + } + else if ((player->kartstuff[k_drift] < 0 && !(i & 1)) + || (player->kartstuff[k_drift] > 0 && (i & 1))) + { + size++; + } + } + + if (size == 2) + P_SetMobjState(spark, S_DRIFTSPARK_A1); + else if (size < 1) + P_SetMobjState(spark, S_DRIFTSPARK_C1); + else if (size > 2) + P_SetMobjState(spark, S_DRIFTSPARK_D1); + + if (trail > 0) + spark->tics += trail; + + K_MatchGenericExtraFlags(spark, player->mo); + } +} + +static void K_SpawnAIZDust(player_t *player) +{ + fixed_t newx; + fixed_t newy; + mobj_t *spark; + angle_t travelangle; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (leveltime % 2 == 1) + return; + + if (!P_IsObjectOnGround(player->mo)) + return; + + travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + //S_StartSound(player->mo, sfx_s3k47); + + { + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale)); + spark = P_SpawnMobj(newx, newy, player->mo->z, MT_AIZDRIFTSTRAT); + + spark->angle = travelangle+(player->kartstuff[k_aizdriftstrat]*ANGLE_90); + P_SetScale(spark, (spark->destscale = (3*player->mo->scale)>>2)); + + spark->momx = (6*player->mo->momx)/5; + spark->momy = (6*player->mo->momy)/5; + //spark->momz = player->mo->momz/2; + + K_MatchGenericExtraFlags(spark, player->mo); + } +} + +void K_SpawnBoostTrail(player_t *player) +{ + fixed_t newx; + fixed_t newy; + fixed_t ground; + mobj_t *flame; + angle_t travelangle; + INT32 i; + + I_Assert(player != NULL); + I_Assert(player->mo != NULL); + I_Assert(!P_MobjWasRemoved(player->mo)); + + if (!P_IsObjectOnGround(player->mo) + || player->kartstuff[k_hyudorotimer] != 0 + || (G_BattleGametype() && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer])) + return; + + if (player->mo->eflags & MFE_VERTICALFLIP) + ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); + else + ground = player->mo->floorz; + + if (player->kartstuff[k_drift] != 0) + travelangle = player->mo->angle; + else + travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); + + for (i = 0; i < 2; i++) + { + newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); + newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale)); +#ifdef ESLOPE + if (player->mo->standingslope) + { + ground = P_GetZAt(player->mo->standingslope, newx, newy); + if (player->mo->eflags & MFE_VERTICALFLIP) + ground -= FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale); + } +#endif + flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL); + + P_SetTarget(&flame->target, player->mo); + flame->angle = travelangle; + flame->fuse = TICRATE*2; + flame->destscale = player->mo->scale; + P_SetScale(flame, player->mo->scale); + // not K_MatchGenericExtraFlags so that a stolen sneaker can be seen + K_FlipFromObject(flame, player->mo); + + flame->momx = 8; + P_XYMovement(flame); + if (P_MobjWasRemoved(flame)) + continue; + + if (player->mo->eflags & MFE_VERTICALFLIP) + { + if (flame->z + flame->height < flame->ceilingz) + P_RemoveMobj(flame); + } + else if (flame->z > flame->floorz) + P_RemoveMobj(flame); + } +} + +void K_SpawnSparkleTrail(mobj_t *mo) +{ + const INT32 rad = (mo->radius*2)>>FRACBITS; + mobj_t *sparkle; + INT32 i; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (i = 0; i < 3; i++) + { + fixed_t newx = mo->x + mo->momx + (P_RandomRange(-rad, rad)<y + mo->momy + (P_RandomRange(-rad, rad)<z + mo->momz + (P_RandomRange(0, mo->height>>FRACBITS)<target, mo); + sparkle->destscale = mo->destscale; + P_SetScale(sparkle, mo->scale); + sparkle->color = mo->color; + //sparkle->colorized = mo->colorized; + } + + P_SetMobjState(sparkle, S_KARTINVULN_LARGE1); +} + +void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) +{ + mobj_t *dust; + angle_t aoff; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + if (mo->player) + aoff = (mo->player->frameangle + ANGLE_180); + else + aoff = (mo->angle + ANGLE_180); + + if ((leveltime / 2) & 1) + aoff -= ANGLE_45; + else + aoff += ANGLE_45; + + dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), + mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS), + mo->z, MT_WIPEOUTTRAIL); + + P_SetTarget(&dust->target, mo); + dust->angle = R_PointToAngle2(0,0,mo->momx,mo->momy); + dust->destscale = mo->scale; + P_SetScale(dust, mo->scale); + K_FlipFromObject(dust, mo); + + if (translucent) // offroad effect + { + dust->momx = mo->momx/2; + dust->momy = mo->momy/2; + dust->momz = mo->momz/2; + } + + if (translucent) + dust->drawflags |= MFD_SHADOW; +} + +void K_SpawnDraftDust(mobj_t *mo) +{ + UINT8 i; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (i = 0; i < 2; i++) + { + angle_t ang, aoff; + SINT8 sign = 1; + UINT8 foff = 0; + mobj_t *dust; + boolean drifting = false; + + if (mo->player) + { + UINT8 leniency = (3*TICRATE)/4 + ((mo->player->kartweight-1) * (TICRATE/4)); + + ang = mo->player->frameangle; + + if (mo->player->kartstuff[k_drift] != 0) + { + drifting = true; + ang += (mo->player->kartstuff[k_drift] * ((ANGLE_270 + ANGLE_22h) / 5)); // -112.5 doesn't work. I fucking HATE SRB2 angles + if (mo->player->kartstuff[k_drift] < 0) + sign = 1; + else + sign = -1; + } + + foff = 5 - ((mo->player->kartstuff[k_draftleeway] * 5) / leniency); + + // this shouldn't happen + if (foff > 4) + foff = 4; + } + else + ang = mo->angle; + + if (!drifting) + { + if (i & 1) + sign = -1; + else + sign = 1; + } + + aoff = (ang + ANGLE_180) + (ANGLE_45 * sign); + + dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)), + mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)), + mo->z, MT_DRAFTDUST); + + P_SetMobjState(dust, S_DRAFTDUST1 + foff); + + P_SetTarget(&dust->target, mo); + dust->angle = ang - (ANGLE_90 * sign); // point completely perpendicular from the player + dust->destscale = mo->scale; + P_SetScale(dust, mo->scale); + K_FlipFromObject(dust, mo); + + if (leveltime & 1) + dust->tics++; // "randomize" animation + + dust->momx = (4*mo->momx)/5; + dust->momy = (4*mo->momy)/5; + //dust->momz = (4*mo->momz)/5; + + P_Thrust(dust, dust->angle, 4*mo->scale); + + if (drifting) // only 1 trail while drifting + break; + } +} + +// K_DriftDustHandling +// Parameters: +// spawner: The map object that is spawning the drift dust +// Description: Spawns the drift dust for objects, players use rmomx/y, other objects use regular momx/y. +// Also plays the drift sound. +// Other objects should be angled towards where they're trying to go so they don't randomly spawn dust +// Do note that most of the function won't run in odd intervals of frames +void K_DriftDustHandling(mobj_t *spawner) +{ + angle_t anglediff; + const INT16 spawnrange = spawner->radius >> FRACBITS; + + if (!P_IsObjectOnGround(spawner) || leveltime % 2 != 0) + return; + + if (spawner->player) + { + if (spawner->player->pflags & PF_SKIDDOWN) + { + anglediff = abs((signed)(spawner->angle - spawner->player->frameangle)); + if (leveltime % 6 == 0) + S_StartSound(spawner, sfx_screec); // repeated here because it doesn't always happen to be within the range when this is the case + } + else + { + angle_t playerangle = spawner->angle; + + if (spawner->player->speed < 5*spawner->scale) + return; + + if (spawner->player->cmd.forwardmove < 0) + playerangle += ANGLE_180; + + anglediff = abs((signed)(playerangle - R_PointToAngle2(0, 0, spawner->player->rmomx, spawner->player->rmomy))); + } + } + else + { + if (P_AproxDistance(spawner->momx, spawner->momy) < 5*spawner->scale) + return; + + anglediff = abs((signed)(spawner->angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy))); + } + + if (anglediff > ANGLE_180) + anglediff = InvAngle(anglediff); + + if (anglediff > ANG10*4) // Trying to turn further than 40 degrees + { + fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; + fixed_t spawny = P_RandomRange(-spawnrange, spawnrange) << FRACBITS; + INT32 speedrange = 2; + mobj_t *dust = P_SpawnMobj(spawner->x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST); + dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); + dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange) * spawner->scale), 3*FRACUNIT/4); + dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale)); + P_SetScale(dust, spawner->scale/2); + dust->destscale = spawner->scale * 3; + dust->scalespeed = spawner->scale/12; + + if (leveltime % 6 == 0) + S_StartSound(spawner, sfx_screec); + + K_MatchGenericExtraFlags(dust, spawner); + + // Sparkle-y warning for when you're about to change drift sparks! + if (spawner->player && spawner->player->kartstuff[k_drift]) + { + INT32 driftval = K_GetKartDriftSparkValue(spawner->player); + INT32 warntime = driftval/3; + INT32 dc = spawner->player->kartstuff[k_driftcharge]; + UINT8 c = SKINCOLOR_NONE; + boolean rainbow = false; + + if (dc >= 0) + { + dc += warntime; + } + + c = K_DriftSparkColor(spawner->player, dc); + + if (dc > (4*driftval)+(32*3)) + { + rainbow = true; + } + + if (c != SKINCOLOR_NONE) + { + P_SetMobjState(dust, S_DRIFTWARNSPARK1); + dust->color = c; + dust->colorized = rainbow; + } + } + } +} + +static mobj_t *K_FindLastTrailMobj(player_t *player) +{ + mobj_t *trail; + + if (!player || !(trail = player->mo) || !player->mo->hnext || !player->mo->hnext->health) + return NULL; + + while (trail->hnext && !P_MobjWasRemoved(trail->hnext) && trail->hnext->health) + { + trail = trail->hnext; + } + + return trail; +} + +static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow) +{ + mobj_t *mo; + INT32 dir; + fixed_t PROJSPEED; + angle_t newangle; + fixed_t newx, newy, newz; + mobj_t *throwmo; + + if (!player) + return NULL; + + // Figure out projectile speed by game speed + if (missile) + { + // Use info->speed for missiles + PROJSPEED = FixedMul(mobjinfo[mapthing].speed, K_GetKartGameSpeedScalar(gamespeed)); + } + else + { + // Use pre-determined speed for tossing + PROJSPEED = FixedMul(82 << FRACBITS, K_GetKartGameSpeedScalar(gamespeed)); + } + + // Scale to map size + PROJSPEED = FixedMul(PROJSPEED, mapobjectscale); + + if (altthrow) + { + if (altthrow == 2) // Kitchen sink throwing + { +#if 0 + if (player->kartstuff[k_throwdir] == 1) + dir = 3; + else if (player->kartstuff[k_throwdir] == -1) + dir = 1; + else + dir = 2; +#else + if (player->kartstuff[k_throwdir] == 1) + dir = 2; + else + dir = 1; +#endif + } + else + { + if (player->kartstuff[k_throwdir] == 1) + dir = 2; + else if (player->kartstuff[k_throwdir] == -1) + dir = -1; + else + dir = 1; + } + } + else + { + if (player->kartstuff[k_throwdir] != 0) + dir = player->kartstuff[k_throwdir]; + else + dir = defaultDir; + } + + if (missile) // Shootables + { + if (mapthing == MT_BALLHOG) // Messy + { + if (dir == -1) + { + // Shoot backward + mo = K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x06000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) - 0x03000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x03000000, 0, PROJSPEED/8); + K_SpawnKartMissile(player->mo, mapthing, (player->mo->angle + ANGLE_180) + 0x06000000, 0, PROJSPEED/8); + } + else + { + // Shoot forward + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED); + K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x06000000, 0, PROJSPEED); + } + } + else + { + if (dir == -1 && mapthing != MT_SPB) + { + // Shoot backward + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/8); + } + else + { + // Shoot forward + mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED); + } + } + } + else + { + player->kartstuff[k_bananadrag] = 0; // RESET timer, for multiple bananas + + if (dir > 0) + { + // Shoot forward + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing); + //K_FlipFromObject(mo, player->mo); + // These are really weird so let's make it a very specific case to make SURE it works... + if (player->mo->eflags & MFE_VERTICALFLIP) + { + mo->z -= player->mo->height; + mo->flags2 |= MF2_OBJECTFLIP; + mo->eflags |= MFE_VERTICALFLIP; + } + + mo->threshold = 10; + P_SetTarget(&mo->target, player->mo); + + S_StartSound(player->mo, mo->info->seesound); + + if (mo) + { + angle_t fa = player->mo->angle>>ANGLETOFINESHIFT; + fixed_t HEIGHT = (20 + (dir*10))*FRACUNIT + (player->mo->momz*P_MobjFlip(player->mo)); + + P_SetObjectMomZ(mo, HEIGHT, false); + mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir); + mo->momy = player->mo->momy + FixedMul(FINESINE(fa), PROJSPEED*dir); + + mo->extravalue2 = dir; + + if (mo->eflags & MFE_UNDERWATER) + mo->momz = (117 * mo->momz) / 200; + } + + // this is the small graphic effect that plops in you when you throw an item: + throwmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FIREDITEM); + P_SetTarget(&throwmo->target, player->mo); + // Ditto: + if (player->mo->eflags & MFE_VERTICALFLIP) + { + throwmo->z -= player->mo->height; + throwmo->flags2 |= MF2_OBJECTFLIP; + throwmo->eflags |= MFE_VERTICALFLIP; + } + + throwmo->movecount = 0; // above player + } + else + { + mobj_t *lasttrail = K_FindLastTrailMobj(player); + + if (mapthing == MT_BUBBLESHIELDTRAP) // Drop directly on top of you. + { + newangle = player->mo->angle; + newx = player->mo->x + player->mo->momx; + newy = player->mo->y + player->mo->momy; + newz = player->mo->z; + } + else if (lasttrail) + { + newangle = lasttrail->angle; + newx = lasttrail->x; + newy = lasttrail->y; + newz = lasttrail->z; + } + else + { + // Drop it directly behind you. + fixed_t dropradius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(mobjinfo[mapthing].radius, mobjinfo[mapthing].radius); + + newangle = player->mo->angle; + + newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, dropradius); + newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, dropradius); + newz = player->mo->z; + } + + mo = P_SpawnMobj(newx, newy, newz, mapthing); // this will never return null because collision isn't processed here + K_FlipFromObject(mo, player->mo); + + mo->threshold = 10; + P_SetTarget(&mo->target, player->mo); + + P_SetScale(mo, player->mo->scale); + mo->destscale = player->mo->destscale; + + if (P_IsObjectOnGround(player->mo)) + { + // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn + // This should set it for FOFs + P_TeleportMove(mo, mo->x, mo->y, mo->z); // however, THIS can fuck up your day. just absolutely ruin you. + if (P_MobjWasRemoved(mo)) + return NULL; + + if (P_MobjFlip(mo) > 0) + { + if (mo->floorz > mo->target->z - mo->height) + { + mo->z = mo->floorz; + } + } + else + { + if (mo->ceilingz < mo->target->z + mo->target->height + mo->height) + { + mo->z = mo->ceilingz - mo->height; + } + } + } + + if (player->mo->eflags & MFE_VERTICALFLIP) + mo->eflags |= MFE_VERTICALFLIP; + + if (mapthing == MT_SSMINE) + mo->extravalue1 = 49; // Pads the start-up length from 21 frames to a full 2 seconds + else if (mapthing == MT_BUBBLESHIELDTRAP) + { + P_SetScale(mo, ((5*mo->destscale)>>2)*4); + mo->destscale = (5*mo->destscale)>>2; + S_StartSound(mo, sfx_s3kbfl); + } + } + } + + return mo; +} + +void K_PuntMine(mobj_t *thismine, mobj_t *punter) +{ + angle_t fa = R_PointToAngle2(0, 0, punter->momx, punter->momy) >> ANGLETOFINESHIFT; + fixed_t z = 30*mapobjectscale + punter->momz; + fixed_t spd; + mobj_t *mine; + + if (!thismine || P_MobjWasRemoved(thismine)) + return; + + if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine + { + mine = P_SpawnMobj(thismine->x, thismine->y, thismine->z, MT_SSMINE); + P_SetTarget(&mine->target, thismine->target); + mine->angle = thismine->angle; + mine->flags2 = thismine->flags2; + mine->floorz = thismine->floorz; + mine->ceilingz = thismine->ceilingz; + P_RemoveMobj(thismine); + } + else + mine = thismine; + + if (!mine || P_MobjWasRemoved(mine)) + return; + + spd = (82 + ((gamespeed-1) * 14))*mapobjectscale; // Avg Speed is 41 in Normal + + mine->flags |= MF_NOCLIPTHING; + + P_SetMobjState(mine, S_SSMINE_AIR1); + mine->threshold = 10; + mine->extravalue1 = 0; + mine->reactiontime = mine->info->reactiontime; + + mine->momx = punter->momx + FixedMul(FINECOSINE(fa), spd); + mine->momy = punter->momy + FixedMul(FINESINE(fa), spd); + mine->momz = P_MobjFlip(mine) * z; + + mine->flags &= ~MF_NOCLIPTHING; +} + +#define THUNDERRADIUS 320 + +static void K_DoThunderShield(player_t *player) +{ + mobj_t *mo; + int i = 0; + fixed_t sx; + fixed_t sy; + angle_t an; + + S_StartSound(player->mo, sfx_zio3); + P_NukeEnemies(player->mo, player->mo, RING_DIST/4); + + // spawn vertical bolt + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_LZIO11); + mo->color = SKINCOLOR_TEAL; + mo->scale = player->mo->scale*3 + (player->mo->scale/2); + + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_LZIO21); + mo->color = SKINCOLOR_CYAN; + mo->scale = player->mo->scale*3 + (player->mo->scale/2); + + // spawn horizontal bolts; + for (i=0; i<7; i++) + { + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK); + mo->angle = P_RandomRange(0, 359)*ANG1; + mo->fuse = P_RandomRange(20, 50); + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_KLIT1); + } + + // spawn the radius thing: + an = ANGLE_22h; + for (i=0; i<15; i++) + { + sx = player->mo->x + FixedMul((player->mo->scale*THUNDERRADIUS), FINECOSINE((an*i)>>ANGLETOFINESHIFT)); + sy = player->mo->y + FixedMul((player->mo->scale*THUNDERRADIUS), FINESINE((an*i)>>ANGLETOFINESHIFT)); + mo = P_SpawnMobj(sx, sy, player->mo->z, MT_THOK); + mo-> angle = an*i; + mo->extravalue1 = THUNDERRADIUS; // Used to know whether we should teleport by radius or something. + mo->scale = player->mo->scale*3; + P_SetTarget(&mo->target, player->mo); + P_SetMobjState(mo, S_KSPARK1); + } +} + +#undef THUNDERRADIUS + +static void K_FlameDashLeftoverSmoke(mobj_t *src) +{ + UINT8 i; + + for (i = 0; i < 2; i++) + { + mobj_t *smoke = P_SpawnMobj(src->x, src->y, src->z+(8<scale); + smoke->destscale = 3*src->scale/2; + smoke->scalespeed = src->scale/12; + + smoke->momx = 3*src->momx/4; + smoke->momy = 3*src->momy/4; + smoke->momz = 3*src->momz/4; + + P_Thrust(smoke, src->angle + FixedAngle(P_RandomRange(135, 225)<scale); + smoke->momz += P_RandomRange(0, 4) * src->scale; + } +} + +static void K_DoHyudoroSteal(player_t *player) +{ + INT32 i, numplayers = 0; + INT32 playerswappable[MAXPLAYERS]; + INT32 stealplayer = -1; // The player that's getting stolen from + INT32 prandom = 0; + boolean sink = P_RandomChance(FRACUNIT/64); + INT32 hyu = hyudorotime; + + if (G_RaceGametype()) + hyu *= 2; // double in race + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE + && player != &players[i] && !players[i].exiting && !players[i].spectator // Player in-game + + // Can steal from this player + && (G_RaceGametype() //&& players[i].kartstuff[k_position] < player->kartstuff[k_position]) + || (G_BattleGametype() && players[i].kartstuff[k_bumper] > 0)) + + // Has an item + && (players[i].kartstuff[k_itemtype] + && players[i].kartstuff[k_itemamount] + && !players[i].kartstuff[k_itemheld] + && !players[i].karthud[khud_itemblink])) + { + playerswappable[numplayers] = i; + numplayers++; + } + } + + prandom = P_RandomFixed(); + S_StartSound(player->mo, sfx_s3k92); + + if (sink && numplayers > 0 && cv_kitchensink.value) // BEHOLD THE KITCHEN SINK + { + player->kartstuff[k_hyudorotimer] = hyu; + player->kartstuff[k_stealingtimer] = stealtime; + + player->kartstuff[k_itemtype] = KITEM_KITCHENSINK; + player->kartstuff[k_itemamount] = 1; + player->kartstuff[k_itemheld] = 0; + return; + } + else if ((G_RaceGametype() && player->kartstuff[k_position] == 1) || numplayers == 0) // No-one can be stolen from? Oh well... + { + player->kartstuff[k_hyudorotimer] = hyu; + player->kartstuff[k_stealingtimer] = stealtime; + return; + } + else if (numplayers == 1) // With just 2 players, we just need to set the other player to be the one to steal from + { + stealplayer = playerswappable[numplayers-1]; + } + else if (numplayers > 1) // We need to choose between the available candidates for the 2nd player + { + stealplayer = playerswappable[prandom%(numplayers-1)]; + } + + if (stealplayer > -1) // Now here's where we do the stealing, has to be done here because we still know the player we're stealing from + { + player->kartstuff[k_hyudorotimer] = hyu; + player->kartstuff[k_stealingtimer] = stealtime; + players[stealplayer].kartstuff[k_stolentimer] = stealtime; + + player->kartstuff[k_itemtype] = players[stealplayer].kartstuff[k_itemtype]; + player->kartstuff[k_itemamount] = players[stealplayer].kartstuff[k_itemamount]; + player->kartstuff[k_itemheld] = 0; + + players[stealplayer].kartstuff[k_itemtype] = KITEM_NONE; + players[stealplayer].kartstuff[k_itemamount] = 0; + players[stealplayer].kartstuff[k_itemheld] = 0; + + if (P_IsDisplayPlayer(&players[stealplayer]) && !r_splitscreen) + S_StartSound(NULL, sfx_s3k92); + } +} + +void K_DoSneaker(player_t *player, INT32 type) +{ + const fixed_t intendedboost = FRACUNIT/2; + + if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) + { + const sfxenum_t normalsfx = sfx_cdfm01; + const sfxenum_t smallsfx = sfx_cdfm40; + sfxenum_t sfx = normalsfx; + + if (player->kartstuff[k_numsneakers]) + { + // Use a less annoying sound when stacking sneakers. + sfx = smallsfx; + } + + S_StopSoundByID(player->mo, normalsfx); + S_StopSoundByID(player->mo, smallsfx); + S_StartSound(player->mo, sfx); + + K_SpawnDashDustRelease(player); + if (intendedboost > player->kartstuff[k_speedboost]) + player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); + + player->kartstuff[k_numsneakers]++; + } + + if (!player->kartstuff[k_sneakertimer]) + { + if (type == 2) + { + if (player->mo->hnext) + { + mobj_t *cur = player->mo->hnext; + while (cur && !P_MobjWasRemoved(cur)) + { + if (!cur->tracer) + { + mobj_t *overlay = P_SpawnMobj(cur->x, cur->y, cur->z, MT_BOOSTFLAME); + P_SetTarget(&overlay->target, cur); + P_SetTarget(&cur->tracer, overlay); + P_SetScale(overlay, (overlay->destscale = 3*cur->scale/4)); + K_FlipFromObject(overlay, cur); + } + cur = cur->hnext; + } + } + } + else + { + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BOOSTFLAME); + P_SetTarget(&overlay->target, player->mo); + P_SetScale(overlay, (overlay->destscale = player->mo->scale)); + K_FlipFromObject(overlay, player->mo); + } + } + + if (type != 0) + { + player->pflags |= PF_ATTACKDOWN; + K_PlayBoostTaunt(player->mo); + + } + + player->kartstuff[k_sneakertimer] = sneakertime; + + // set angle for spun out players: + player->kartstuff[k_boostangle] = (INT32)player->mo->angle; +} + +static void K_DoShrink(player_t *user) +{ + INT32 i; + mobj_t *mobj, *next; + + S_StartSound(user->mo, sfx_kc46); // Sound the BANG! + user->pflags |= PF_ATTACKDOWN; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + if (&players[i] == user) + continue; + if (players[i].kartstuff[k_position] < user->kartstuff[k_position]) + { + //P_FlashPal(&players[i], PAL_NUKE, 10); + + // Grow should get taken away. + if (players[i].kartstuff[k_growshrinktimer] > 0) + K_RemoveGrowShrink(&players[i]); + else + { + // Start shrinking! + K_DropItems(&players[i]); + players[i].kartstuff[k_growshrinktimer] = -(15*TICRATE); + + if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) + { + players[i].mo->scalespeed = mapobjectscale/TICRATE; + players[i].mo->destscale = (6*mapobjectscale)/8; + if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot) + players[i].mo->destscale = (6*players[i].mo->destscale)/8; + S_StartSound(players[i].mo, sfx_kc59); + } + } + } + } + + // kill everything in the kitem list while we're at it: + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + + // check if the item is being held by a player behind us before removing it. + // check if the item is a "shield" first, bc i'm p sure thrown items keep the player that threw em as target anyway + + if (mobj->type == MT_BANANA_SHIELD || mobj->type == MT_JAWZ_SHIELD || + mobj->type == MT_SSMINE_SHIELD || mobj->type == MT_EGGMANITEM_SHIELD || + mobj->type == MT_SINK_SHIELD || mobj->type == MT_ORBINAUT_SHIELD) + { + if (mobj->target && mobj->target->player) + { + if (mobj->target->player->kartstuff[k_position] > user->kartstuff[k_position]) + continue; // this guy's behind us, don't take his stuff away! + } + } + + mobj->destscale = 0; + mobj->flags &= ~(MF_SOLID|MF_SHOOTABLE|MF_SPECIAL); + mobj->flags |= MF_NOCLIPTHING; // Just for safety + + if (mobj->type == MT_SPB) + spbplace = -1; + } +} + + +void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) +{ + const fixed_t vscale = mapobjectscale + (mo->scale - mapobjectscale); + + if (mo->player && mo->player->spectator) + return; + + if (mo->eflags & MFE_SPRUNG) + return; + +#ifdef ESLOPE + mo->standingslope = NULL; +#endif + + mo->eflags |= MFE_SPRUNG; + + if (mo->eflags & MFE_VERTICALFLIP) + vertispeed *= -1; + + if (vertispeed == 0) + { + fixed_t thrust; + + if (mo->player) + { + thrust = 3*mo->player->speed/2; + if (thrust < 48< 72<player->kartstuff[k_pogospring] != 2) + { + if (mo->player->kartstuff[k_sneakertimer]) + thrust = FixedMul(thrust, (5*FRACUNIT)/4); + else if (mo->player->kartstuff[k_invincibilitytimer]) + thrust = FixedMul(thrust, (9*FRACUNIT)/8); + } + } + else + { + thrust = FixedDiv(3*P_AproxDistance(mo->momx, mo->momy)/2, 5*FRACUNIT/2); + if (thrust < 16< 32<momz = P_MobjFlip(mo)*FixedMul(FINESINE(ANGLE_22h>>ANGLETOFINESHIFT), FixedMul(thrust, vscale)); + } + else + mo->momz = FixedMul(vertispeed, vscale); + + if (mo->eflags & MFE_UNDERWATER) + mo->momz = (117 * mo->momz) / 200; + + if (sound) + S_StartSound(mo, (sound == 1 ? sfx_kc2f : sfx_kpogos)); +} + +void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source) +{ + mobj_t *cachenext; + +killnext: + cachenext = banana->hnext; + + if (banana->health) + { + if (banana->eflags & MFE_VERTICALFLIP) + banana->z -= banana->height; + else + banana->z += banana->height; + + S_StartSound(banana, banana->info->deathsound); + P_KillMobj(banana, inflictor, source); + + P_SetObjectMomZ(banana, 8*FRACUNIT, false); + if (inflictor) + P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT); + } + + if ((banana = cachenext)) + goto killnext; +} + +// Just for firing/dropping items. +void K_UpdateHnextList(player_t *player, boolean clean) +{ + mobj_t *work = player->mo, *nextwork; + + if (!work) + return; + + nextwork = work->hnext; + + while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) + { + nextwork = work->hnext; + + if (!clean && (!work->movedir || work->movedir <= (UINT16)player->kartstuff[k_itemamount])) + { + continue; + } + + P_RemoveMobj(work); + } + + if (player->mo->hnext == NULL || P_MobjWasRemoved(player->mo->hnext)) + { + // Like below, try to clean up the pointer if it's NULL. + // Maybe this was a cause of the shrink/eggbox fails? + P_SetTarget(&player->mo->hnext, NULL); + } +} + +// For getting hit! +void K_DropHnextList(player_t *player, boolean keepshields) +{ + mobj_t *work = player->mo, *nextwork, *dropwork; + INT32 flip; + mobjtype_t type; + boolean orbit, ponground, dropall = true; + INT32 shield = K_GetShieldFromItem(player->kartstuff[k_itemtype]); + + if (work == NULL || P_MobjWasRemoved(work)) + { + return; + } + + flip = P_MobjFlip(player->mo); + ponground = P_IsObjectOnGround(player->mo); + + if (shield != KSHIELD_NONE && !keepshields) + { + if (shield == KSHIELD_THUNDER) + { + K_DoThunderShield(player); + } + + player->kartstuff[k_curshield] = KSHIELD_NONE; + player->kartstuff[k_itemtype] = KITEM_NONE; + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + } + + nextwork = work->hnext; + + while ((work = nextwork) && !(work == NULL || P_MobjWasRemoved(work))) + { + nextwork = work->hnext; + + switch (work->type) + { + // Kart orbit items + case MT_ORBINAUT_SHIELD: + orbit = true; + type = MT_ORBINAUT; + break; + case MT_JAWZ_SHIELD: + orbit = true; + type = MT_JAWZ_DUD; + break; + // Kart trailing items + case MT_BANANA_SHIELD: + orbit = false; + type = MT_BANANA; + break; + case MT_SSMINE_SHIELD: + orbit = false; + dropall = false; + type = MT_SSMINE; + break; + case MT_EGGMANITEM_SHIELD: + orbit = false; + type = MT_EGGMANITEM; + break; + // intentionally do nothing + case MT_ROCKETSNEAKER: + case MT_SINK_SHIELD: + return; + default: + continue; + } + + dropwork = P_SpawnMobj(work->x, work->y, work->z, type); + + P_SetTarget(&dropwork->target, player->mo); + P_AddKartItem(dropwork); // needs to be called here so shrink can bust items off players in front of the user. + + dropwork->angle = work->angle; + + dropwork->flags |= MF_NOCLIPTHING; + dropwork->flags2 = work->flags2; + + dropwork->floorz = work->floorz; + dropwork->ceilingz = work->ceilingz; + + if (ponground) + { + // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn + // This should set it for FOFs + //P_TeleportMove(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing + + if (flip == 1) + { + if (dropwork->floorz > dropwork->target->z - dropwork->height) + { + dropwork->z = dropwork->floorz; + } + } + else + { + if (dropwork->ceilingz < dropwork->target->z + dropwork->target->height + dropwork->height) + { + dropwork->z = dropwork->ceilingz - dropwork->height; + } + } + } + + if (orbit) // splay out + { + dropwork->flags2 |= MF2_AMBUSH; + + dropwork->z += flip; + + dropwork->momx = player->mo->momx>>1; + dropwork->momy = player->mo->momy>>1; + dropwork->momz = 3*flip*mapobjectscale; + + if (dropwork->eflags & MFE_UNDERWATER) + dropwork->momz = (117 * dropwork->momz) / 200; + + P_Thrust(dropwork, work->angle - ANGLE_90, 6*mapobjectscale); + + dropwork->movecount = 2; + dropwork->movedir = work->angle - ANGLE_90; + + P_SetMobjState(dropwork, dropwork->info->deathstate); + + dropwork->tics = -1; + + if (type == MT_JAWZ_DUD) + { + dropwork->z += 20*flip*dropwork->scale; + } + else + { + dropwork->color = work->color; + dropwork->angle -= ANGLE_90; + } + } + else // plop on the ground + { + dropwork->flags &= ~MF_NOCLIPTHING; + dropwork->threshold = 10; + } + + P_RemoveMobj(work); + } + + // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... + P_SetTarget(&player->mo->hnext, NULL); + + player->kartstuff[k_bananadrag] = 0; + + if (player->kartstuff[k_eggmanheld]) + { + player->kartstuff[k_eggmanheld] = 0; + } + else if (player->kartstuff[k_itemheld] + && (dropall || (--player->kartstuff[k_itemamount] <= 0))) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } +} + +// For getting EXTRA hit! +void K_DropItems(player_t *player) +{ + K_DropHnextList(player, true); + + 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]; + + drop->flags |= MF_NOCLIPTHING; + } + + K_StripItems(player); +} + +// When an item in the hnext chain dies. +void K_RepairOrbitChain(mobj_t *orbit) +{ + mobj_t *cachenext = orbit->hnext; + + // First, repair the chain + if (orbit->hnext && orbit->hnext->health && !P_MobjWasRemoved(orbit->hnext)) + { + P_SetTarget(&orbit->hnext->hprev, orbit->hprev); + P_SetTarget(&orbit->hnext, NULL); + } + + if (orbit->hprev && orbit->hprev->health && !P_MobjWasRemoved(orbit->hprev)) + { + P_SetTarget(&orbit->hprev->hnext, cachenext); + P_SetTarget(&orbit->hprev, NULL); + } + + // Then recount to make sure item amount is correct + if (orbit->target && orbit->target->player) + { + INT32 num = 0; + + mobj_t *cur = orbit->target->hnext; + mobj_t *prev = NULL; + + while (cur && !P_MobjWasRemoved(cur)) + { + prev = cur; + cur = cur->hnext; + if (++num > orbit->target->player->kartstuff[k_itemamount]) + P_RemoveMobj(prev); + else + prev->movedir = num; + } + + if (orbit->target->player->kartstuff[k_itemamount] != num) + orbit->target->player->kartstuff[k_itemamount] = num; + } +} + +// Simplified version of a code bit in P_MobjFloorZ +static fixed_t K_BananaSlopeZ(pslope_t *slope, fixed_t x, fixed_t y, fixed_t radius, boolean ceiling) +{ + fixed_t testx, testy; + + if (slope->d.x < 0) + testx = radius; + else + testx = -radius; + + if (slope->d.y < 0) + testy = radius; + else + testy = -radius; + + if ((slope->zdelta > 0) ^ !!(ceiling)) + { + testx = -testx; + testy = -testy; + } + + testx += x; + testy += y; + + return P_GetZAt(slope, testx, testy); +} + +static void K_CalculateBananaSlope(mobj_t *mobj, fixed_t x, fixed_t y, fixed_t z, fixed_t radius, fixed_t height, boolean flip, boolean player) +{ + fixed_t newz; + sector_t *sec; +#ifdef ESLOPE + pslope_t *slope = NULL; +#endif + + sec = R_PointInSubsector(x, y)->sector; + + if (flip) + { +#ifdef ESLOPE + if (sec->c_slope) + { + slope = sec->c_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, true); + } + else +#endif + newz = sec->ceilingheight; + } + else + { +#ifdef ESLOPE + if (sec->f_slope) + { + slope = sec->f_slope; + newz = K_BananaSlopeZ(slope, x, y, radius, false); + } + else +#endif + newz = sec->floorheight; + } + + // Check FOFs for a better suited slope + if (sec->ffloors) + { + ffloor_t *rover; + + for (rover = sec->ffloors; rover; rover = rover->next) + { + fixed_t top, bottom; + fixed_t d1, d2; + + if (!(rover->flags & FF_EXISTS)) + continue; + + if ((!(((rover->flags & FF_BLOCKPLAYER && player) + || (rover->flags & FF_BLOCKOTHERS && !player)) + || (rover->flags & FF_QUICKSAND)) + || (rover->flags & FF_SWIMMABLE))) + continue; + +#ifdef ESLOPE + if (*rover->t_slope) + top = K_BananaSlopeZ(*rover->t_slope, x, y, radius, false); + else +#endif + top = *rover->topheight; + +#ifdef ESLOPE + if (*rover->b_slope) + bottom = K_BananaSlopeZ(*rover->b_slope, x, y, radius, true); + else +#endif + bottom = *rover->bottomheight; + + if (flip) + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz > (z + height)) + { + newz = (z + height); + slope = NULL; + } + } + continue; + } + + d1 = (z + height) - (top + ((bottom - top)/2)); + d2 = z - (top + ((bottom - top)/2)); + + if (bottom < newz && abs(d1) < abs(d2)) + { + newz = bottom; +#ifdef ESLOPE + if (*rover->b_slope) + slope = *rover->b_slope; +#endif + } + } + else + { + if (rover->flags & FF_QUICKSAND) + { + if (z < top && (z + height) > bottom) + { + if (newz < z) + { + newz = z; + slope = NULL; + } + } + continue; + } + + d1 = z - (bottom + ((top - bottom)/2)); + d2 = (z + height) - (bottom + ((top - bottom)/2)); + + if (top > newz && abs(d1) < abs(d2)) + { + newz = top; +#ifdef ESLOPE + if (*rover->t_slope) + slope = *rover->t_slope; +#endif + } + } + } + } + +#if 0 + mobj->standingslope = slope; +#endif + +#ifdef HWRENDER + mobj->modeltilt = slope; +#endif +} + +// Move the hnext chain! +static void K_MoveHeldObjects(player_t *player) +{ + if (!player->mo) + return; + + if (!player->mo->hnext) + { + player->kartstuff[k_bananadrag] = 0; + if (player->kartstuff[k_eggmanheld]) + player->kartstuff[k_eggmanheld] = 0; + else if (player->kartstuff[k_itemheld]) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } + return; + } + + if (P_MobjWasRemoved(player->mo->hnext)) + { + // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic... + P_SetTarget(&player->mo->hnext, NULL); + player->kartstuff[k_bananadrag] = 0; + if (player->kartstuff[k_eggmanheld]) + player->kartstuff[k_eggmanheld] = 0; + else if (player->kartstuff[k_itemheld]) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } + return; + } + + switch (player->mo->hnext->type) + { + case MT_ORBINAUT_SHIELD: // Kart orbit items + case MT_JAWZ_SHIELD: + { + mobj_t *cur = player->mo->hnext; + fixed_t speed = ((8 - min(4, player->kartstuff[k_itemamount])) * cur->info->speed) / 7; + + player->kartstuff[k_bananadrag] = 0; // Just to make sure + + while (cur && !P_MobjWasRemoved(cur)) + { + const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); // mobj's distance from its Target, or Radius. + fixed_t z; + + if (!cur->health) + { + cur = cur->hnext; + continue; + } + + cur->color = player->skincolor; + + cur->angle -= ANGLE_90; + cur->angle += FixedAngle(speed); + + if (cur->extravalue1 < radius) + cur->extravalue1 += P_AproxDistance(cur->extravalue1, radius) / 12; + if (cur->extravalue1 > radius) + cur->extravalue1 = radius; + + // If the player is on the ceiling, then flip your items as well. + if (player && player->mo->eflags & MFE_VERTICALFLIP) + cur->eflags |= MFE_VERTICALFLIP; + else + cur->eflags &= ~MFE_VERTICALFLIP; + + // Shrink your items if the player shrunk too. + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + + if (P_MobjFlip(cur) > 0) + z = player->mo->z; + else + z = player->mo->z + player->mo->height - cur->height; + + cur->flags |= MF_NOCLIPTHING; // temporarily make them noclip other objects so they can't hit anyone while in the player + P_TeleportMove(cur, player->mo->x, player->mo->y, z); + cur->momx = FixedMul(FINECOSINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); + cur->momy = FixedMul(FINESINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1); + cur->flags &= ~MF_NOCLIPTHING; + if (!P_TryMove(cur, player->mo->x + cur->momx, player->mo->y + cur->momy, true)) + P_SlideMove(cur, true); + if (P_IsObjectOnGround(player->mo)) + { + if (P_MobjFlip(cur) > 0) + { + if (cur->floorz > player->mo->z - cur->height) + z = cur->floorz; + } + else + { + if (cur->ceilingz < player->mo->z + player->mo->height + cur->height) + z = cur->ceilingz - cur->height; + } + } + + // Center it during the scale up animation + z += (FixedMul(mobjinfo[cur->type].height, player->mo->scale - cur->scale)>>1) * P_MobjFlip(cur); + + cur->z = z; + cur->momx = cur->momy = 0; + cur->angle += ANGLE_90; + + cur = cur->hnext; + } + } + break; + case MT_BANANA_SHIELD: // Kart trailing items + case MT_SSMINE_SHIELD: + case MT_EGGMANITEM_SHIELD: + case MT_SINK_SHIELD: + { + mobj_t *cur = player->mo->hnext; + mobj_t *targ = player->mo; + + if (P_IsObjectOnGround(player->mo) && player->speed > 0) + player->kartstuff[k_bananadrag]++; + + while (cur && !P_MobjWasRemoved(cur)) + { + const fixed_t radius = FixedHypot(targ->radius, targ->radius) + FixedHypot(cur->radius, cur->radius); + angle_t ang; + fixed_t targx, targy, targz; + fixed_t speed, dist; + + if (cur->type == MT_EGGMANITEM_SHIELD) + { + // Decided that this should use their "canon" color. + cur->color = SKINCOLOR_BLACK; + } + + cur->flags &= ~MF_NOCLIPTHING; + + if (!cur->health) + { + cur = cur->hnext; + continue; + } + + if (cur->extravalue1 < radius) + cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); + if (cur->extravalue1 > radius) + cur->extravalue1 = radius; + + if (cur != player->mo->hnext) + { + targ = cur->hprev; + dist = cur->extravalue1/4; + } + else + dist = cur->extravalue1/2; + + if (!targ || P_MobjWasRemoved(targ)) + continue; + + // Shrink your items if the player shrunk too. + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + + ang = targ->angle; + targx = targ->x + P_ReturnThrustX(cur, ang + ANGLE_180, dist); + targy = targ->y + P_ReturnThrustY(cur, ang + ANGLE_180, dist); + targz = targ->z; + + speed = FixedMul(R_PointToDist2(cur->x, cur->y, targx, targy), 3*FRACUNIT/4); + if (P_IsObjectOnGround(targ)) + targz = cur->floorz; + + cur->angle = R_PointToAngle2(cur->x, cur->y, targx, targy); + + /*if (P_IsObjectOnGround(player->mo) && player->speed > 0 && player->kartstuff[k_bananadrag] > TICRATE + && P_RandomChance(min(FRACUNIT/2, FixedDiv(player->speed, K_GetKartSpeed(player, false))/2))) + { + if (leveltime & 1) + targz += 8*(2*FRACUNIT)/7; + else + targz -= 8*(2*FRACUNIT)/7; + }*/ + + if (speed > dist) + P_InstaThrust(cur, cur->angle, speed-dist); + + P_SetObjectMomZ(cur, FixedMul(targz - cur->z, 7*FRACUNIT/8) - gravity, false); + + if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT) + P_TeleportMove(cur, targx, targy, cur->z); + +#ifdef ESLOPE + if (P_IsObjectOnGround(cur)) + { + K_CalculateBananaSlope(cur, cur->x, cur->y, cur->z, + cur->radius, cur->height, (cur->eflags & MFE_VERTICALFLIP), false); + } +#endif + + cur = cur->hnext; + } + } + break; + case MT_ROCKETSNEAKER: // Special rocket sneaker stuff + { + mobj_t *cur = player->mo->hnext; + INT32 num = 0; + + while (cur && !P_MobjWasRemoved(cur)) + { + const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); + boolean vibrate = ((leveltime & 1) && !cur->tracer); + angle_t angoffset; + fixed_t targx, targy, targz; + + cur->flags &= ~MF_NOCLIPTHING; + + if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1)) + cur->drawflags |= MFD_DONTDRAW; + else + cur->drawflags &= ~MFD_DONTDRAW; + + if (num & 1) + P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L)); + else + P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_RVIBRATE : S_ROCKETSNEAKER_R)); + + if (!player->kartstuff[k_rocketsneakertimer] || cur->extravalue2 || !cur->health) + { + num = (num+1) % 2; + cur = cur->hnext; + continue; + } + + if (cur->extravalue1 < radius) + cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12); + if (cur->extravalue1 > radius) + cur->extravalue1 = radius; + + // Shrink your items if the player shrunk too. + P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale))); + +#if 1 + { + angle_t input = player->frameangle - cur->angle; + boolean invert = (input > ANGLE_180); + if (invert) + input = InvAngle(input); + + input = FixedAngle(AngleFixed(input)/4); + if (invert) + input = InvAngle(input); + + cur->angle = cur->angle + input; + } +#else + cur->angle = player->frameangle; +#endif + + angoffset = ANGLE_90 + (ANGLE_180 * num); + + targx = player->mo->x + P_ReturnThrustX(cur, cur->angle + angoffset, cur->extravalue1); + targy = player->mo->y + P_ReturnThrustY(cur, cur->angle + angoffset, cur->extravalue1); + + { // bobbing, copy pasted from my kimokawaiii entry + const fixed_t pi = (22<mo->scale, 8 * FINESINE((((2*pi*(4*TICRATE)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK)); + targz = (player->mo->z + (player->mo->height/2)) + sine; + if (player->mo->eflags & MFE_VERTICALFLIP) + targz += (player->mo->height/2 - 32*player->mo->scale)*6; + + } + + if (cur->tracer) + { + fixed_t diffx, diffy, diffz; + + diffx = targx - cur->x; + diffy = targy - cur->y; + diffz = targz - cur->z; + + P_TeleportMove(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale), + cur->tracer->y + diffy + P_ReturnThrustY(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->z + diffz); + P_SetScale(cur->tracer, (cur->tracer->destscale = 3*cur->scale/4)); + } + + P_TeleportMove(cur, targx, targy, targz); + K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. +#ifdef HWRENDER + cur->modeltilt = player->mo->modeltilt; +#endif + num = (num+1) % 2; + cur = cur->hnext; + } + } + break; + default: + break; + } +} + +player_t *K_FindJawzTarget(mobj_t *actor, player_t *source) +{ + fixed_t best = -1; + player_t *wtarg = NULL; + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + angle_t thisang; + player_t *player; + + if (!playeringame[i]) + continue; + + player = &players[i]; + + if (player->spectator) + continue; // spectator + + if (!player->mo) + continue; + + if (player->mo->health <= 0) + continue; // dead + + // Don't target yourself, stupid. + if (player == source) + continue; + + // Don't home in on teammates. + if (G_GametypeHasTeams() && source->ctfteam == player->ctfteam) + continue; + + // Invisible, don't bother + if (player->kartstuff[k_hyudorotimer]) + continue; + + // Find the angle, see who's got the best. + thisang = actor->angle - R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y); + if (thisang > ANGLE_180) + thisang = InvAngle(thisang); + + // Jawz only go after the person directly ahead of you in race... sort of literally now! + if (G_RaceGametype()) + { + // Don't go for people who are behind you + if (thisang > ANGLE_67h) + continue; + // Don't pay attention to people who aren't above your position + if (player->kartstuff[k_position] >= source->kartstuff[k_position]) + continue; + if ((best == -1) || (player->kartstuff[k_position] > best)) + { + wtarg = player; + best = player->kartstuff[k_position]; + } + } + else + { + fixed_t thisdist; + fixed_t thisavg; + + // Don't go for people who are behind you + if (thisang > ANGLE_45) + continue; + + // Don't pay attention to dead players + if (player->kartstuff[k_bumper] <= 0) + continue; + + // Z pos too high/low + if (abs(player->mo->z - (actor->z + actor->momz)) > RING_DIST/8) + continue; + + thisdist = P_AproxDistance(player->mo->x - (actor->x + actor->momx), player->mo->y - (actor->y + actor->momy)); + + if (thisdist > 2*RING_DIST) // Don't go for people who are too far away + continue; + + thisavg = (AngleFixed(thisang) + thisdist) / 2; + + //CONS_Printf("got avg %d from player # %d\n", thisavg>>FRACBITS, i); + + if ((best == -1) || (thisavg < best)) + { + wtarg = player; + best = thisavg; + } + } + } + + return wtarg; +} + +// Engine Sounds. +static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) +{ + const INT32 numsnds = 13; + INT32 class, s, w; // engine class number + UINT8 volume = 255; + fixed_t volumedampen = 0; + INT32 targetsnd = 0; + INT32 i; + + s = (player->kartspeed-1)/3; + w = (player->kartweight-1)/3; + +#define LOCKSTAT(stat) \ + if (stat < 0) { stat = 0; } \ + if (stat > 2) { stat = 2; } + LOCKSTAT(s); + LOCKSTAT(w); +#undef LOCKSTAT + + class = s+(3*w); + + // Silence the engines + if (leveltime < 8 || player->spectator) + { + player->karthud[khud_enginesnd] = 0; // Reset sound number + return; + } + +#if 0 + if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct! +#else + if (leveltime % 8) // .25 seconds of wait time between engine sounds +#endif + return; + + if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->respawn.state == RESPAWNST_DROP)) // Startup boosts + targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0); + else + targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2; + + if (targetsnd < 0) + targetsnd = 0; + if (targetsnd > 12) + targetsnd = 12; + + if (player->karthud[khud_enginesnd] < targetsnd) + player->karthud[khud_enginesnd]++; + if (player->karthud[khud_enginesnd] > targetsnd) + player->karthud[khud_enginesnd]--; + + if (player->karthud[khud_enginesnd] < 0) + player->karthud[khud_enginesnd] = 0; + if (player->karthud[khud_enginesnd] > 12) + player->karthud[khud_enginesnd] = 12; + + for (i = 0; i < MAXPLAYERS; i++) + { + UINT8 thisvol = 0; + fixed_t dist; + + if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting) + continue; + + if (P_IsDisplayPlayer(&players[i])) + { + volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time. + continue; + } + + dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x, + player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2; + + dist = FixedDiv(dist, mapobjectscale); + + if (dist > 1536<>FRACBITS)) / (((1536<>(FRACBITS+4)); + + if (thisvol == 0) + continue; + + volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT + } + + if (volumedampen > FRACUNIT) + volume = FixedDiv(volume<>FRACBITS; + + if (volume <= 0) // Might as well + return; + + S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->karthud[khud_enginesnd]) + (class*numsnds), volume); +} + +static void K_UpdateInvincibilitySounds(player_t *player) +{ + INT32 sfxnum = sfx_None; + + if (player->mo->health > 0 && !P_IsDisplayPlayer(player)) + { + if (cv_kartinvinsfx.value) + { + if (player->kartstuff[k_invincibilitytimer] > 0) // Prioritize invincibility + sfxnum = sfx_alarmi; + else if (player->kartstuff[k_growshrinktimer] > 0) + sfxnum = sfx_alarmg; + } + else + { + if (player->kartstuff[k_invincibilitytimer] > 0) + sfxnum = sfx_kinvnc; + else if (player->kartstuff[k_growshrinktimer] > 0) + sfxnum = sfx_kgrow; + } + } + + if (sfxnum != sfx_None && !S_SoundPlaying(player->mo, sfxnum)) + S_StartSound(player->mo, sfxnum); + +#define STOPTHIS(this) \ + if (sfxnum != this && S_SoundPlaying(player->mo, this)) \ + S_StopSoundByID(player->mo, this); + STOPTHIS(sfx_alarmi); + STOPTHIS(sfx_alarmg); + STOPTHIS(sfx_kinvnc); + STOPTHIS(sfx_kgrow); +#undef STOPTHIS +} + +#define RINGANIM_NUMFRAMES 10 +#define RINGANIM_DELAYMAX 5 + +void K_KartPlayerHUDUpdate(player_t *player) +{ + if (player->karthud[khud_lapanimation]) + player->karthud[khud_lapanimation]--; + + if (player->karthud[khud_yougotem]) + player->karthud[khud_yougotem]--; + + if (player->karthud[khud_voices]) + player->karthud[khud_voices]--; + + if (player->karthud[khud_tauntvoices]) + player->karthud[khud_tauntvoices]--; + + if (G_RaceGametype()) + { + // 0 is the fast spin animation, set at 30 tics of ring boost or higher! + if (player->kartstuff[k_ringboost] >= 30) + player->karthud[khud_ringdelay] = 0; + else + player->karthud[khud_ringdelay] = ((RINGANIM_DELAYMAX+1) * (30 - player->kartstuff[k_ringboost])) / 30; + + if (player->karthud[khud_ringframe] == 0 && player->karthud[khud_ringdelay] > RINGANIM_DELAYMAX) + { + player->karthud[khud_ringframe] = 0; + player->karthud[khud_ringtics] = 0; + } + else if ((player->karthud[khud_ringtics]--) <= 0) + { + if (player->karthud[khud_ringdelay] == 0) // fast spin animation + { + player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+2) % RINGANIM_NUMFRAMES); + player->karthud[khud_ringtics] = 0; + } + else + { + player->karthud[khud_ringframe] = ((player->karthud[khud_ringframe]+1) % RINGANIM_NUMFRAMES); + player->karthud[khud_ringtics] = min(RINGANIM_DELAYMAX, player->karthud[khud_ringdelay])-1; + } + } + + if (player->kartstuff[k_ringlock]) + { + UINT8 normalanim = (leveltime % 14); + UINT8 debtanim = 14 + (leveltime % 2); + + if (player->karthud[khud_ringspblock] >= 14) // debt animation + { + if ((player->kartstuff[k_rings] > 0) // Get out of 0 ring animation + && (normalanim == 3 || normalanim == 10)) // on these transition frames. + player->karthud[khud_ringspblock] = normalanim; + else + player->karthud[khud_ringspblock] = debtanim; + } + else // normal animation + { + if ((player->kartstuff[k_rings] <= 0) // Go into 0 ring animation + && (player->karthud[khud_ringspblock] == 1 || player->karthud[khud_ringspblock] == 8)) // on these transition frames. + player->karthud[khud_ringspblock] = debtanim; + else + player->karthud[khud_ringspblock] = normalanim; + } + } + else + player->karthud[khud_ringspblock] = (leveltime % 14); // reset to normal anim next time + } + + if (G_BattleGametype() && (player->exiting || player->kartstuff[k_comebacktimer])) + { + if (player->exiting) + { + if (player->exiting < 6*TICRATE) + player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; + else if (player->exiting == 6*TICRATE) + player->karthud[khud_cardanimation] = 0; + else if (player->karthud[khud_cardanimation] < 2*TICRATE) + player->karthud[khud_cardanimation]++; + } + else + { + if (player->kartstuff[k_comebacktimer] < 6*TICRATE) + player->karthud[khud_cardanimation] -= ((164-player->karthud[khud_cardanimation])/8)+1; + else if (player->kartstuff[k_comebacktimer] < 9*TICRATE) + player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; + } + + if (player->karthud[khud_cardanimation] > 164) + player->karthud[khud_cardanimation] = 164; + if (player->karthud[khud_cardanimation] < 0) + player->karthud[khud_cardanimation] = 0; + } + else if (G_RaceGametype() && player->exiting) + { + if (player->karthud[khud_cardanimation] < 2*TICRATE) + player->karthud[khud_cardanimation]++; + } + else + player->karthud[khud_cardanimation] = 0; +} + +#undef RINGANIM_DELAYMAX + +// SRB2Kart: blockmap iterate for attraction shield users +static mobj_t *attractmo; +static fixed_t attractdist; +static inline boolean PIT_AttractingRings(mobj_t *thing) +{ + if (!attractmo || P_MobjWasRemoved(attractmo)) + return false; + + if (!attractmo->player) + return false; // not a player + + if (thing->health <= 0 || !thing) + return true; // dead + + if (thing->type != MT_RING && thing->type != MT_FLINGRING) + return true; // not a ring + + if (thing->extravalue1) + return true; // in special ring animation + + if (thing->cusval) + return true; // already attracted + + // see if it went over / under + if (attractmo->z - (attractdist>>2) > thing->z + thing->height) + return true; // overhead + if (attractmo->z + attractmo->height + (attractdist>>2) < thing->z) + return true; // underneath + + if (P_AproxDistance(attractmo->x - thing->x, attractmo->y - thing->y) < attractdist) + return true; // Too far away + + // set target + P_SetTarget(&thing->tracer, attractmo); + // flag to show it's been attracted once before + thing->cusval = 1; + return true; // find other rings +} + +/** Looks for rings near a player in the blockmap. + * + * \param pmo Player object looking for rings to attract + * \sa A_AttractChase + */ +static void K_LookForRings(mobj_t *pmo) +{ + INT32 bx, by, xl, xh, yl, yh; + attractdist = FixedMul(RING_DIST, pmo->scale)>>2; + + // Use blockmap to check for nearby rings + yh = (unsigned)(pmo->y + attractdist - bmaporgy)>>MAPBLOCKSHIFT; + yl = (unsigned)(pmo->y - attractdist - bmaporgy)>>MAPBLOCKSHIFT; + xh = (unsigned)(pmo->x + attractdist - bmaporgx)>>MAPBLOCKSHIFT; + xl = (unsigned)(pmo->x - attractdist - bmaporgx)>>MAPBLOCKSHIFT; + + attractmo = pmo; + + for (by = yl; by <= yh; by++) + for (bx = xl; bx <= xh; bx++) + P_BlockThingsIterator(bx, by, PIT_AttractingRings); +} + +/** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c + + \param player player object passed from P_PlayerThink + \param cmd control input from player + + \return void +*/ +void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) +{ + K_UpdateOffroad(player); + K_UpdateDraft(player); + K_UpdateEngineSounds(player, cmd); // Thanks, VAda! + + // update boost angle if not spun out + if (!player->kartstuff[k_spinouttimer] && !player->kartstuff[k_wipeoutslow]) + player->kartstuff[k_boostangle] = (INT32)player->mo->angle; + + K_GetKartBoostPower(player); + + // Special effect objects! + if (player->mo && !player->spectator) + { + if (player->kartstuff[k_dashpadcooldown]) // Twinkle Circuit afterimages + { + mobj_t *ghost; + ghost = P_SpawnGhostMobj(player->mo); + ghost->fuse = player->kartstuff[k_dashpadcooldown]+1; + ghost->momx = player->mo->momx / (player->kartstuff[k_dashpadcooldown]+1); + ghost->momy = player->mo->momy / (player->kartstuff[k_dashpadcooldown]+1); + ghost->momz = player->mo->momz / (player->kartstuff[k_dashpadcooldown]+1); + player->kartstuff[k_dashpadcooldown]--; + } + + if (player->speed > 0) + { + // Speed lines + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_ringboost] + || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost] + || player->kartstuff[k_eggmanexplode]) + { + mobj_t *fast = P_SpawnMobj(player->mo->x + (P_RandomRange(-36,36) * player->mo->scale), + player->mo->y + (P_RandomRange(-36,36) * player->mo->scale), + player->mo->z + (player->mo->height/2) + (P_RandomRange(-20,20) * player->mo->scale), + MT_FASTLINE); + + P_SetTarget(&fast->target, player->mo); + fast->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + fast->momx = 3*player->mo->momx/4; + fast->momy = 3*player->mo->momy/4; + fast->momz = 3*player->mo->momz/4; + + K_MatchGenericExtraFlags(fast, player->mo); + + // Make it red when you have the eggman speed boost + if (player->kartstuff[k_eggmanexplode]) + { + fast->color = SKINCOLOR_RED; + fast->colorized = true; + } + } + + if (player->kartstuff[k_numboosts] > 0) // Boosting after images + { + mobj_t *ghost; + ghost = P_SpawnGhostMobj(player->mo); + ghost->extravalue1 = player->kartstuff[k_numboosts]+1; + ghost->extravalue2 = (leveltime % ghost->extravalue1); + ghost->fuse = ghost->extravalue1; + ghost->frame |= FF_FULLBRIGHT; + ghost->colorized = true; + //ghost->color = player->skincolor; + //ghost->momx = (3*player->mo->momx)/4; + //ghost->momy = (3*player->mo->momy)/4; + //ghost->momz = (3*player->mo->momz)/4; + if (leveltime & 1) + ghost->drawflags |= MFD_DONTDRAW; + } + + if (P_IsObjectOnGround(player->mo)) + { + // Offroad dust + if (player->kartstuff[k_boostpower] < FRACUNIT) + { + K_SpawnWipeoutTrail(player->mo, true); + if (leveltime % 6 == 0) + S_StartSound(player->mo, sfx_cdfm70); + } + + // Draft dust + if (player->kartstuff[k_draftpower] >= FRACUNIT) + { + K_SpawnDraftDust(player->mo); + /*if (leveltime % 23 == 0 || !S_SoundPlaying(player->mo, sfx_s265)) + S_StartSound(player->mo, sfx_s265);*/ + } + } + } + + if (G_RaceGametype() && player->kartstuff[k_rings] <= 0) // spawn ring debt indicator + { + mobj_t *debtflag = P_SpawnMobj(player->mo->x + player->mo->momx, player->mo->y + player->mo->momy, + player->mo->z + player->mo->momz + player->mo->height + (24*player->mo->scale), MT_THOK); + + P_SetMobjState(debtflag, S_RINGDEBT); + P_SetScale(debtflag, (debtflag->destscale = player->mo->scale)); + + K_MatchGenericExtraFlags(debtflag, player->mo); + debtflag->frame += (leveltime % 4); + + if ((leveltime/12) & 1) + debtflag->frame += 4; + + debtflag->color = player->skincolor; + debtflag->fuse = 2; + + debtflag->drawflags = K_GetPlayerDontDrawFlag(player); + } + + if (player->kartstuff[k_springstars] && (leveltime & 1)) + { + fixed_t randx = P_RandomRange(-40, 40) * player->mo->scale; + fixed_t randy = P_RandomRange(-40, 40) * player->mo->scale; + fixed_t randz = P_RandomRange(0, player->mo->height >> FRACBITS) << FRACBITS; + mobj_t *star = P_SpawnMobj( + player->mo->x + randx, + player->mo->y + randy, + player->mo->z + randz, + MT_KARMAFIREWORK); + + star->color = player->kartstuff[k_springcolor]; + star->flags |= MF_NOGRAVITY; + star->momx = player->mo->momx / 2; + star->momy = player->mo->momy / 2; + star->momz = player->mo->momz / 2; + star->fuse = 12; + star->scale = player->mo->scale; + star->destscale = star->scale / 2; + + player->kartstuff[k_springstars]--; + } + } + + if (player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Ensure these are set correctly here + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_eggmanexplode]) // You're gonna diiiiie + { + const INT32 flashtime = 4<<(player->kartstuff[k_eggmanexplode]/TICRATE); + if (player->kartstuff[k_eggmanexplode] == 1 || (player->kartstuff[k_eggmanexplode] % (flashtime/2) != 0)) + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_eggmanexplode] % flashtime == 0) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BLACK; + } + else + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_CRIMSON; + } + } + else if (player->kartstuff[k_invincibilitytimer]) // setting players to use the star colormap and spawning afterimages + { + player->mo->colorized = true; + } + else if (player->kartstuff[k_growshrinktimer]) // Ditto, for grow/shrink + { + if (player->kartstuff[k_growshrinktimer] % 5 == 0) + { + player->mo->colorized = true; + player->mo->color = (player->kartstuff[k_growshrinktimer] < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE); + } + else + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + } + else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie + { + const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); + if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_killfield] % flashtime == 0) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BYZANTIUM; + } + else + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_RUBY; + } + } + else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting + { + player->mo->colorized = true; + } + else + { + player->mo->colorized = false; + } + + if (player->kartstuff[k_itemtype] == KITEM_NONE) + player->kartstuff[k_holdready] = 0; + + // DKR style camera for boosting + if (player->karthud[khud_boostcam] != 0 || player->karthud[khud_destboostcam] != 0) + { + if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam] + && player->karthud[khud_destboostcam] != 0) + { + player->karthud[khud_boostcam] += FRACUNIT/(TICRATE/4); + if (player->karthud[khud_boostcam] >= player->karthud[khud_destboostcam]) + player->karthud[khud_destboostcam] = 0; + } + else + { + player->karthud[khud_boostcam] -= FRACUNIT/TICRATE; + if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam]) + player->karthud[khud_boostcam] = player->karthud[khud_destboostcam] = 0; + } + //CONS_Printf("cam: %d, dest: %d\n", player->karthud[khud_boostcam], player->karthud[khud_destboostcam]); + } + + player->karthud[khud_timeovercam] = 0; + + // Specific hack because it insists on setting flashing tics during this anyway... + if (player->kartstuff[k_spinouttype] == 2) + { + player->powers[pw_flashing] = 0; + } + // Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations. + else if (player->kartstuff[k_spinouttimer] != 0 + || player->kartstuff[k_wipeoutslow] != 0 + || player->kartstuff[k_squishedtimer] != 0) + { + player->powers[pw_flashing] = K_GetKartFlashing(player); + } + else if (player->powers[pw_flashing] >= K_GetKartFlashing(player)) + { + player->powers[pw_flashing]--; + } + + if (player->kartstuff[k_spinouttimer]) + { + if ((P_IsObjectOnGround(player->mo) + || (player->kartstuff[k_spinouttype] != 0)) + && (!player->kartstuff[k_sneakertimer])) + { + player->kartstuff[k_spinouttimer]--; + if (player->kartstuff[k_wipeoutslow] > 1) + player->kartstuff[k_wipeoutslow]--; + // Actually, this caused more problems than it solved. Just make sure you set type before you spinout. Which K_SpinPlayer always does. + /*if (player->kartstuff[k_spinouttimer] == 0) + player->kartstuff[k_spinouttype] = 0;*/ // Reset type + } + } + else + { + 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->kartstuff[k_rings] > 20) + player->kartstuff[k_rings] = 20; + else if (player->kartstuff[k_rings] < -20) + player->kartstuff[k_rings] = -20; + + if (player->kartstuff[k_ringdelay]) + player->kartstuff[k_ringdelay]--; + + if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_squishedtimer]) + player->kartstuff[k_ringboost] = 0; + else if (player->kartstuff[k_ringboost]) + player->kartstuff[k_ringboost]--; + + if (player->kartstuff[k_sneakertimer]) + { + player->kartstuff[k_sneakertimer]--; + + if (player->kartstuff[k_sneakertimer] <= 0) + { + player->kartstuff[k_numsneakers] = 0; + } + } + + if (player->kartstuff[k_flamedash]) + player->kartstuff[k_flamedash]--; + + if (player->kartstuff[k_sneakertimer] && player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1) + player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; + + if (player->kartstuff[k_floorboost]) + player->kartstuff[k_floorboost]--; + + if (player->kartstuff[k_driftboost]) + player->kartstuff[k_driftboost]--; + + if (player->kartstuff[k_startboost]) + player->kartstuff[k_startboost]--; + + if (player->kartstuff[k_invincibilitytimer]) + player->kartstuff[k_invincibilitytimer]--; + + if ((player->respawn.state == RESPAWNST_NONE) && player->kartstuff[k_growshrinktimer] != 0) + { + if (player->kartstuff[k_growshrinktimer] > 0) + player->kartstuff[k_growshrinktimer]--; + if (player->kartstuff[k_growshrinktimer] < 0) + player->kartstuff[k_growshrinktimer]++; + + // Back to normal + if (player->kartstuff[k_growshrinktimer] == 0) + K_RemoveGrowShrink(player); + } + + if (player->kartstuff[k_superring]) + { + if (player->kartstuff[k_superring] % 3 == 0) + { + mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); + ring->extravalue1 = 1; // Ring collect animation timer + ring->angle = player->mo->angle; // animation angle + P_SetTarget(&ring->target, player->mo); // toucher for thinker + player->kartstuff[k_pickuprings]++; + if (player->kartstuff[k_superring] <= 3) + ring->cvmem = 1; // play caching when collected + } + player->kartstuff[k_superring]--; + } + + if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0 + && player->kartstuff[k_rocketsneakertimer]) + player->kartstuff[k_rocketsneakertimer]--; + + if (player->kartstuff[k_hyudorotimer]) + player->kartstuff[k_hyudorotimer]--; + + if (player->kartstuff[k_sadtimer]) + player->kartstuff[k_sadtimer]--; + + if (player->kartstuff[k_stealingtimer]) + player->kartstuff[k_stealingtimer]--; + + if (player->kartstuff[k_stolentimer]) + player->kartstuff[k_stolentimer]--; + + if (player->kartstuff[k_squishedtimer]) + { + player->kartstuff[k_squishedtimer]--; + + if ((player->kartstuff[k_squishedtimer] == 0) && !(player->pflags & PF_NOCLIP)) + { + player->mo->flags &= ~MF_NOCLIP; + } + } + + if (player->kartstuff[k_justbumped]) + player->kartstuff[k_justbumped]--; + + if (player->kartstuff[k_tiregrease]) + player->kartstuff[k_tiregrease]--; + + // This doesn't go in HUD update because it has potential gameplay ramifications + if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0) + { + player->karthud[khud_itemblinkmode] = 0; + player->karthud[khud_itemblink] = 0; + } + + K_KartPlayerHUDUpdate(player); + + if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 + && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] + && (player->respawn.state == RESPAWNST_DROP) && !player->powers[pw_flashing]) + { + player->kartstuff[k_wanted]++; + if (battleovertime.enabled >= 10*TICRATE) + { + if (P_AproxDistance(player->mo->x - battleovertime.x, player->mo->y - battleovertime.y) > battleovertime.radius) + { + player->kartstuff[k_killfield]++; + if (player->kartstuff[k_killfield] > 4*TICRATE) + { + K_SpinPlayer(player, NULL, 0, NULL, false); + //player->kartstuff[k_killfield] = 1; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; + + if (P_IsObjectOnGround(player->mo)) + player->kartstuff[k_waterskip] = 0; + + if (player->kartstuff[k_instashield]) + player->kartstuff[k_instashield]--; + + if (player->kartstuff[k_eggmanexplode]) + { + if (player->spectator || (G_BattleGametype() && !player->kartstuff[k_bumper])) + player->kartstuff[k_eggmanexplode] = 0; + else + { + player->kartstuff[k_eggmanexplode]--; + if (player->kartstuff[k_eggmanexplode] <= 0) + { + mobj_t *eggsexplode; + //player->powers[pw_flashing] = 0; + eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION); + if (player->kartstuff[k_eggmanblame] >= 0 + && player->kartstuff[k_eggmanblame] < MAXPLAYERS + && playeringame[player->kartstuff[k_eggmanblame]] + && !players[player->kartstuff[k_eggmanblame]].spectator + && players[player->kartstuff[k_eggmanblame]].mo) + P_SetTarget(&eggsexplode->target, players[player->kartstuff[k_eggmanblame]].mo); + } + } + } + + if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) + { + if (RINGTOTAL(player) < 20 && !player->kartstuff[k_ringlock]) + K_LookForRings(player->mo); + } + + if (player->kartstuff[k_itemtype] == KITEM_BUBBLESHIELD) + { + if (player->kartstuff[k_bubblecool]) + player->kartstuff[k_bubblecool]--; + } + else + { + player->kartstuff[k_bubbleblowup] = 0; + player->kartstuff[k_bubblecool] = 0; + } + + if (player->kartstuff[k_itemtype] != KITEM_FLAMESHIELD) + { + if (player->kartstuff[k_flamedash]) + K_FlameDashLeftoverSmoke(player->mo); + } + + if (player->kartstuff[k_comebacktimer]) + player->kartstuff[k_comebackmode] = 0; + + if (P_IsObjectOnGround(player->mo) && player->kartstuff[k_pogospring]) + { + if (P_MobjFlip(player->mo)*player->mo->momz <= 0) + player->kartstuff[k_pogospring] = 0; + } + + if (cmd->buttons & BT_DRIFT) + player->kartstuff[k_jmp] = 1; + else + player->kartstuff[k_jmp] = 0; + + // Roulette Code + K_KartItemRoulette(player, cmd); + + // Handle invincibility sfx + K_UpdateInvincibilitySounds(player); // Also thanks, VAda! + + // Plays the music after the starting countdown. + if (P_IsLocalPlayer(player) && leveltime == (starttime + (TICRATE/2))) + { + S_ChangeMusic(mapmusname, mapmusflags, true); + S_ShowMusicCredit(); + } +} + +void K_KartPlayerAfterThink(player_t *player) +{ + if (player->kartstuff[k_curshield] + || player->kartstuff[k_invincibilitytimer] + || (player->kartstuff[k_growshrinktimer] != 0 && player->kartstuff[k_growshrinktimer] % 5 == 4)) // 4 instead of 0 because this is afterthink! + { + player->mo->frame |= FF_FULLBRIGHT; + } + else + { + if (!(player->mo->state->frame & FF_FULLBRIGHT)) + player->mo->frame &= ~FF_FULLBRIGHT; + } + + // Move held objects (Bananas, Orbinaut, etc) + K_MoveHeldObjects(player); + + // Jawz reticule (seeking) + if (player->kartstuff[k_itemtype] == KITEM_JAWZ && player->kartstuff[k_itemheld]) + { + INT32 lasttarg = player->kartstuff[k_lastjawztarget]; + player_t *targ; + mobj_t *ret; + + if (player->kartstuff[k_jawztargetdelay] && playeringame[lasttarg] && !players[lasttarg].spectator) + { + targ = &players[lasttarg]; + player->kartstuff[k_jawztargetdelay]--; + } + else + targ = K_FindJawzTarget(player->mo, player); + + if (!targ || !targ->mo || P_MobjWasRemoved(targ->mo)) + { + player->kartstuff[k_lastjawztarget] = -1; + player->kartstuff[k_jawztargetdelay] = 0; + return; + } + + ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE); + P_SetTarget(&ret->target, targ->mo); + ret->frame |= ((leveltime % 10) / 2); + ret->tics = 1; + ret->color = player->skincolor; + + if (targ-players != lasttarg) + { + if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ)) + S_StartSound(NULL, sfx_s3k89); + else + S_StartSound(targ->mo, sfx_s3k89); + + player->kartstuff[k_lastjawztarget] = targ-players; + player->kartstuff[k_jawztargetdelay] = 5; + } + } + else + { + player->kartstuff[k_lastjawztarget] = -1; + player->kartstuff[k_jawztargetdelay] = 0; + } +} + +/*-------------------------------------------------- + static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) + + Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or + previous waypoints are infront of the player. + + Input Arguments:- + player - The player the next waypoint is being found for + + Return:- + The waypoint that is the player's next waypoint +--------------------------------------------------*/ +static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) +{ + waypoint_t *bestwaypoint = NULL; + + if ((player != NULL) && (player->mo != NULL) && (P_MobjWasRemoved(player->mo) == false)) + { + waypoint_t *waypoint = K_GetBestWaypointForMobj(player->mo); + boolean updaterespawn = false; + + bestwaypoint = waypoint; + + // check the waypoint's location in relation to the player + // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. + // EXCEPTION: If our best waypoint is the finishline AND we're facing towards it, don't do this. + // Otherwise it breaks the distance calculations. + if (waypoint != NULL) + { + boolean finishlinehack = false; + angle_t playerangle = player->mo->angle; + angle_t momangle = player->mo->angle; + 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) + { + angledelta = InvAngle(angledelta); + } + + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (bestwaypoint == K_GetFinishLineWaypoint()) + { + // facing towards the finishline + if (angledelta <= ANGLE_90) + { + finishlinehack = true; + } + } + + // We're using a lot of angle calculations here, because only using facing angle or only using momentum angle both have downsides. + // nextwaypoints will be picked if you're facing OR moving forward. + // prevwaypoints will be picked if you're facing AND moving backward. + if ((angledelta > ANGLE_45 || momdelta > ANGLE_45) + && (finishlinehack == false)) + { + angle_t nextbestdelta = angledelta; + angle_t nextbestmomdelta = momdelta; + size_t i = 0U; + + if (K_PlayerUsesBotMovement(player)) + { + // Try to force bots to use a next waypoint + nextbestdelta = ANGLE_MAX; + nextbestmomdelta = ANGLE_MAX; + } + + if ((waypoint->nextwaypoints != NULL) && (waypoint->numnextwaypoints > 0U)) + { + for (i = 0U; i < waypoint->numnextwaypoints; i++) + { + if (K_PlayerUsesBotMovement(player) == true + && K_GetWaypointIsShortcut(waypoint->nextwaypoints[i]) == true + && K_BotCanTakeCut(player) == false) + { + // Bots that aren't able to take a shortcut will ignore shortcut waypoints. + // (However, if they're already on a shortcut, then we want them to keep going.) + + if (player->nextwaypoint == NULL + || K_GetWaypointIsShortcut(player->nextwaypoint) == false) + { + continue; + } + } + + angletowaypoint = R_PointToAngle2( + player->mo->x, player->mo->y, + waypoint->nextwaypoints[i]->mobj->x, waypoint->nextwaypoints[i]->mobj->y); + + angledelta = playerangle - angletowaypoint; + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + } + + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (angledelta < nextbestdelta || momdelta < nextbestmomdelta) + { + bestwaypoint = waypoint->nextwaypoints[i]; + + if (angledelta < nextbestdelta) + { + nextbestdelta = angledelta; + } + if (momdelta < nextbestmomdelta) + { + nextbestmomdelta = momdelta; + } + + // Remove wrong way flag if we're using nextwaypoints + player->kartstuff[k_wrongway] = 0; + updaterespawn = true; + } + } + } + + if ((waypoint->prevwaypoints != NULL) && (waypoint->numprevwaypoints > 0U) + && !(K_PlayerUsesBotMovement(player))) // Bots do not need prev waypoints + { + for (i = 0U; i < waypoint->numprevwaypoints; i++) + { + angletowaypoint = R_PointToAngle2( + player->mo->x, player->mo->y, + waypoint->prevwaypoints[i]->mobj->x, waypoint->prevwaypoints[i]->mobj->y); + + angledelta = playerangle - angletowaypoint; + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + } + + momdelta = momangle - angletowaypoint; + if (momdelta > ANGLE_180) + { + momdelta = InvAngle(momdelta); + } + + if (angledelta < nextbestdelta && momdelta < nextbestmomdelta) + { + bestwaypoint = waypoint->prevwaypoints[i]; + + nextbestdelta = angledelta; + nextbestmomdelta = momdelta; + + // Set wrong way flag if we're using prevwaypoints + player->kartstuff[k_wrongway] = 1; + updaterespawn = false; + } + } + } + } + } + + if (!P_IsObjectOnGround(player->mo)) + { + updaterespawn = false; + } + + // Respawn point should only be updated when we're going to a nextwaypoint + if ((updaterespawn) && + (player->respawn.state == RESPAWNST_NONE) && + (bestwaypoint != NULL) && + (bestwaypoint != player->nextwaypoint) && + (K_GetWaypointIsSpawnpoint(bestwaypoint)) && + (K_GetWaypointIsEnabled(bestwaypoint) == true)) + { + player->respawn.wp = bestwaypoint; + } + } + + return bestwaypoint; +} + +static boolean K_PlayerCloserToNextWaypoints(waypoint_t *const waypoint, player_t *const player) +{ + boolean nextiscloser = true; + + if ((waypoint != NULL) && (player != NULL) && (player->mo != NULL)) + { + size_t i = 0U; + waypoint_t *currentwpcheck = NULL; + angle_t angletoplayer = ANGLE_MAX; + angle_t currentanglecheck = ANGLE_MAX; + angle_t bestangle = ANGLE_MAX; + + angletoplayer = R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, + player->mo->x, player->mo->y); + + for (i = 0U; i < waypoint->numnextwaypoints; i++) + { + currentwpcheck = waypoint->nextwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + } + } + + for (i = 0U; i < waypoint->numprevwaypoints; i++) + { + currentwpcheck = waypoint->prevwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + nextiscloser = false; + break; + } + } + } + + return nextiscloser; +} + +/*-------------------------------------------------- + void K_UpdateDistanceFromFinishLine(player_t *const player) + + Updates the distance a player has to the finish line. + + Input Arguments:- + player - The player the distance is being updated for + + Return:- + None +--------------------------------------------------*/ +void K_UpdateDistanceFromFinishLine(player_t *const player) +{ + if ((player != NULL) && (player->mo != NULL)) + { + waypoint_t *finishline = K_GetFinishLineWaypoint(); + waypoint_t *nextwaypoint = NULL; + + if (player->spectator) + { + // Don't update waypoints while spectating + nextwaypoint = finishline; + } + else + { + nextwaypoint = K_GetPlayerNextWaypoint(player); + } + + if (nextwaypoint != NULL) + { + // If nextwaypoint is NULL, it means we don't want to update the waypoint until we touch another one. + // player->nextwaypoint will keep its previous value in this case. + player->nextwaypoint = nextwaypoint; + } + + // nextwaypoint is now the waypoint that is in front of us + if (player->exiting || player->spectator) + { + // Player has finished, we don't need to calculate this + player->distancetofinish = 0U; + } + else if ((player->nextwaypoint != NULL) && (finishline != NULL)) + { + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {}; + + pathfindsuccess = + K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); + + // Update the player's distance to the finish line if a path was found. + // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track + if (pathfindsuccess == true) + { + // Add euclidean distance to the next waypoint to the distancetofinish + UINT32 adddist; + fixed_t disttowaypoint = + P_AproxDistance( + (player->mo->x >> FRACBITS) - (player->nextwaypoint->mobj->x >> FRACBITS), + (player->mo->y >> FRACBITS) - (player->nextwaypoint->mobj->y >> FRACBITS)); + disttowaypoint = P_AproxDistance(disttowaypoint, (player->mo->z >> FRACBITS) - (player->nextwaypoint->mobj->z >> FRACBITS)); + + adddist = (UINT32)disttowaypoint; + + player->distancetofinish = pathtofinish.totaldist + adddist; + Z_Free(pathtofinish.array); + + // distancetofinish is currently a flat distance to the finish line, but in order to be fully + // correct we need to add to it the length of the entire circuit multiplied by the number of laps + // left after this one. This will give us the total distance to the finish line, and allow item + // distance calculation to work easily + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) + { + const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); + + player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); + + // An additional HACK, to fix looking backwards towards the finish line + // If the player's next waypoint is the finishline and the angle distance from player to + // connectin waypoints implies they're closer to a next waypoint, add a full track distance + if (player->nextwaypoint == finishline) + { + if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) + { + player->distancetofinish += K_GetCircuitLength(); + } + } + } + } + } + } +} + +INT32 K_GetKartRingPower(player_t *player) +{ + return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2); +} + +// Returns false if this player being placed here causes them to collide with any other player +// Used in g_game.c for match etc. respawning +// This does not check along the z because the z is not correctly set for the spawnee at this point +boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y) +{ + INT32 i; + fixed_t p1radius = players[playernum].mo->radius; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playernum == i || !playeringame[i] || players[i].spectator || !players[i].mo || players[i].mo->health <= 0 + || players[i].playerstate != PST_LIVE || (players[i].mo->flags & MF_NOCLIP) || (players[i].mo->flags & MF_NOCLIPTHING)) + continue; + + if (abs(x - players[i].mo->x) < (p1radius + players[i].mo->radius) + && abs(y - players[i].mo->y) < (p1radius + players[i].mo->radius)) + { + return false; + } + } + return true; +} + +// countersteer is how strong the controls are telling us we are turning +// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left +static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) +{ + INT16 basedrift, driftadjust; + fixed_t driftweight = player->kartweight*14; // 12 + + if (player->kartstuff[k_drift] == 0 || !P_IsObjectOnGround(player->mo)) + { + // If they aren't drifting or on the ground, this doesn't apply + return 0; + } + + if (player->kartstuff[k_driftend] != 0) + { + // Drift has ended and we are tweaking their angle back a bit + return -266*player->kartstuff[k_drift]; + } + + basedrift = (83 * player->kartstuff[k_drift]) - (((driftweight - 14) * player->kartstuff[k_drift]) / 5); // 415 - 303 + driftadjust = abs((252 - driftweight) * player->kartstuff[k_drift] / 5); + + if (player->kartstuff[k_tiregrease] > 0) // Buff drift-steering while in greasemode + { + basedrift += (basedrift / greasetics) * player->kartstuff[k_tiregrease]; + } + + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) + { + countersteer = 3*countersteer/2; + } + + return basedrift + (FixedMul(driftadjust * FRACUNIT, countersteer) / FRACUNIT); +} + +INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) +{ + fixed_t p_maxspeed = K_GetKartSpeed(player, false); + fixed_t p_speed = min(player->speed, (p_maxspeed * 2)); + fixed_t weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); + + if (player->spectator) + { + return turnvalue; + } + + if (K_PlayerUsesBotMovement(player)) + { + turnvalue = 5*turnvalue/4; // Base increase to turning + turnvalue = FixedMul( + turnvalue * FRACUNIT, + K_BotRubberband(player) + ) / FRACUNIT; + } + + if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo)) + { + fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); + + // If we're drifting we have a completely different turning value + + if (player->kartstuff[k_driftend] != 0) + { + countersteer = FRACUNIT; + } + + turnvalue = K_GetKartDriftValue(player, countersteer); + + return turnvalue; + } + + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) + { + turnvalue = 5*turnvalue/4; + } + + if (player->kartstuff[k_flamedash] > 0) + { + fixed_t multiplier = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); + multiplier = FRACUNIT + (FixedDiv(multiplier, FRACUNIT/2) / 4); + turnvalue = FixedMul(turnvalue * FRACUNIT, multiplier) / FRACUNIT; + } + + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) + { + turnvalue = 3*turnvalue/2; + } + + // Weight has a small effect on turning + turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; + + return turnvalue; +} + +INT32 K_GetKartDriftSparkValue(player_t *player) +{ + UINT8 kartspeed = (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + ? 1 + : player->kartspeed; + return (26*4 + kartspeed*2 + (9 - player->kartweight))*8; +} + +/* +Stage 1: red sparks +Stage 2: blue sparks +Stage 3: big large rainbow sparks +*/ +static void K_SpawnDriftBoostExplosion(player_t *player, int stage) +{ + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); + + P_SetTarget(&overlay->target, player->mo); + P_SetScale(overlay, (overlay->destscale = player->mo->scale)); + K_FlipFromObject(overlay, player->mo); + + switch (stage) + { + case 1: + overlay->color = SKINCOLOR_KETCHUP; + overlay->fuse = 16; + break; + + case 2: + overlay->color = SKINCOLOR_SAPPHIRE; + overlay->fuse = 32; + + S_StartSound(player->mo, sfx_kc5b); + break; + + case 3: + overlay->color = SKINCOLOR_SILVER; + overlay->fuse = 120; + + S_StartSound(player->mo, sfx_kc5b); + S_StartSound(player->mo, sfx_s3kc4l); + break; + } +} + +static void K_KartDrift(player_t *player, boolean onground) +{ + fixed_t minspeed = (10 * player->mo->scale); + INT32 dsone = K_GetKartDriftSparkValue(player); + INT32 dstwo = dsone*2; + INT32 dsthree = dstwo*2; + + // Drifting is actually straffing + automatic turning. + // Holding the Jump button will enable drifting. + + // Drift Release (Moved here so you can't "chain" drifts) + if (player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5) + { + if (player->kartstuff[k_driftcharge] < 0 || player->kartstuff[k_driftcharge] >= dsone) + { + angle_t pushdir = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); + + S_StartSound(player->mo, sfx_s23c); + //K_SpawnDashDustRelease(player); + + if (player->kartstuff[k_driftcharge] < 0) + { + // Stage 0: Yellow sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 8); + + if (player->kartstuff[k_driftboost] < 15) + player->kartstuff[k_driftboost] = 15; + } + else if (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo) + { + // Stage 1: Red sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 4); + + if (player->kartstuff[k_driftboost] < 20) + player->kartstuff[k_driftboost] = 20; + + K_SpawnDriftBoostExplosion(player, 1); + } + else if (player->kartstuff[k_driftcharge] < dsthree) + { + // Stage 2: Blue sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 3); + + if (player->kartstuff[k_driftboost] < 50) + player->kartstuff[k_driftboost] = 50; + + K_SpawnDriftBoostExplosion(player, 2); + } + else if (player->kartstuff[k_driftcharge] >= dsthree) + { + // Stage 3: Rainbow sparks + if (!onground) + P_Thrust(player->mo, pushdir, player->speed / 2); + + if (player->kartstuff[k_driftboost] < 125) + player->kartstuff[k_driftboost] = 125; + + K_SpawnDriftBoostExplosion(player, 3); + } + } + + // Remove charge + player->kartstuff[k_driftcharge] = 0; + } + + // Drifting: left or right? + if ((player->cmd.driftturn > 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 + && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != 1) + { + // Starting left drift + player->kartstuff[k_drift] = 1; + player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; + } + else if ((player->cmd.driftturn < 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1 + && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != -1) + { + // Starting right drift + player->kartstuff[k_drift] = -1; + player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0; + } + else if (player->kartstuff[k_jmp] == 0) // || player->kartstuff[k_turndir] == 0) + { + // drift is not being performed so if we're just finishing set driftend and decrement counters + if (player->kartstuff[k_drift] > 0) + { + player->kartstuff[k_drift]--; + player->kartstuff[k_driftend] = 1; + } + else if (player->kartstuff[k_drift] < 0) + { + player->kartstuff[k_drift]++; + player->kartstuff[k_driftend] = 1; + } + else + player->kartstuff[k_driftend] = 0; + } + + if (player->kartstuff[k_spinouttimer] > 0 || player->speed == 0) + { + // Stop drifting + player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0; + player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0; + player->kartstuff[k_getsparks] = 0; + } + else if (player->kartstuff[k_jmp] == 1 && player->kartstuff[k_drift] != 0) + { + // Incease/decrease the drift value to continue drifting in that direction + fixed_t driftadditive = 24; + boolean playsound = false; + + if (onground) + { + if (player->kartstuff[k_drift] >= 1) // Drifting to the left + { + player->kartstuff[k_drift]++; + if (player->kartstuff[k_drift] > 5) + player->kartstuff[k_drift] = 5; + + if (player->cmd.driftturn > 0) // Inward + driftadditive += abs(player->cmd.driftturn)/100; + if (player->cmd.driftturn < 0) // Outward + driftadditive -= abs(player->cmd.driftturn)/75; + } + else if (player->kartstuff[k_drift] <= -1) // Drifting to the right + { + player->kartstuff[k_drift]--; + if (player->kartstuff[k_drift] < -5) + player->kartstuff[k_drift] = -5; + + if (player->cmd.driftturn < 0) // Inward + driftadditive += abs(player->cmd.driftturn)/100; + if (player->cmd.driftturn > 0) // Outward + driftadditive -= abs(player->cmd.driftturn)/75; + } + + // Disable drift-sparks until you're going fast enough + if (player->kartstuff[k_getsparks] == 0 + || (player->kartstuff[k_offroad] && K_ApplyOffroad(player))) + driftadditive = 0; + + // Inbetween minspeed and minspeed*2, it'll keep your previous drift-spark state. + if (player->speed > minspeed*2) + { + player->kartstuff[k_getsparks] = 1; + + if (player->kartstuff[k_driftcharge] <= -1) + { + player->kartstuff[k_driftcharge] = dsone; // Back to red + playsound = true; + } + } + else if (player->speed <= minspeed) + { + player->kartstuff[k_getsparks] = 0; + driftadditive = 0; + + if (player->kartstuff[k_driftcharge] >= dsone) + { + player->kartstuff[k_driftcharge] = -1; // Set yellow sparks + playsound = true; + } + } + } + else + { + driftadditive = 0; + } + + // This spawns the drift sparks + if ((player->kartstuff[k_driftcharge] + driftadditive >= dsone) + || (player->kartstuff[k_driftcharge] < 0)) + { + K_SpawnDriftSparks(player); + } + + if ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone) + || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo) + || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree)) + { + playsound = true; + } + + // Sound whenever you get a different tier of sparks + if (playsound && P_IsDisplayPlayer(player)) + { + if (player->kartstuff[k_driftcharge] == -1) + S_StartSoundAtVolume(player->mo, sfx_sploss, 192); // Yellow spark sound + else + S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); + } + + player->kartstuff[k_driftcharge] += driftadditive; + player->kartstuff[k_driftend] = 0; + } + + if ((!player->kartstuff[k_sneakertimer]) + || (!player->cmd.driftturn) + || (!player->kartstuff[k_aizdriftstrat]) + || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) + { + if (!player->kartstuff[k_drift]) + player->kartstuff[k_aizdriftstrat] = 0; + else + player->kartstuff[k_aizdriftstrat] = ((player->kartstuff[k_drift] > 0) ? 1 : -1); + } + else if (player->kartstuff[k_aizdriftstrat] && !player->kartstuff[k_drift]) + K_SpawnAIZDust(player); + + if (player->kartstuff[k_drift] + && ((player->cmd.buttons & BT_BRAKE) + || !(player->cmd.buttons & BT_ACCELERATE)) + && P_IsObjectOnGround(player->mo)) + { + if (!player->kartstuff[k_brakedrift]) + K_SpawnBrakeDriftSparks(player); + player->kartstuff[k_brakedrift] = 1; + } + else + player->kartstuff[k_brakedrift] = 0; +} +// +// K_KartUpdatePosition +// +void K_KartUpdatePosition(player_t *player) +{ + fixed_t position = 1; + fixed_t oldposition = player->kartstuff[k_position]; + fixed_t i; + + if (player->spectator || !player->mo) + { + // Ensure these are reset for spectators + player->kartstuff[k_position] = 0; + player->kartstuff[k_positiondelay] = 0; + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + if (G_RaceGametype()) + { + if (player->exiting) // End of match standings + { + // Only time matters + if (players[i].realtime < player->realtime) + position++; + } + else + { + // I'm a lap behind this player OR + // My distance to the finish line is higher, so I'm behind + if ((players[i].laps > player->laps) + || (players[i].distancetofinish < player->distancetofinish)) + { + position++; + } + } + } + else if (G_BattleGametype()) + { + if (player->exiting) // End of match standings + { + // Only score matters + if (players[i].marescore > player->marescore) + position++; + } + 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])) + position++; + } + } + } + + if (leveltime < starttime || oldposition == 0) + oldposition = position; + + if (oldposition != position) // Changed places? + player->kartstuff[k_positiondelay] = 10; // Position number growth + + player->kartstuff[k_position] = position; +} + +// +// K_StripItems +// +void K_StripItems(player_t *player) +{ + player->kartstuff[k_itemtype] = KITEM_NONE; + player->kartstuff[k_itemamount] = 0; + player->kartstuff[k_itemheld] = 0; + + player->kartstuff[k_rocketsneakertimer] = 0; + + if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2) + { + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + } + player->kartstuff[k_eggmanheld] = 0; + + player->kartstuff[k_hyudorotimer] = 0; + player->kartstuff[k_stealingtimer] = 0; + player->kartstuff[k_stolentimer] = 0; + + player->kartstuff[k_curshield] = KSHIELD_NONE; + player->kartstuff[k_bananadrag] = 0; + + player->kartstuff[k_sadtimer] = 0; + + K_UpdateHnextList(player, true); +} + +void K_StripOther(player_t *player) +{ + player->kartstuff[k_itemroulette] = 0; + player->kartstuff[k_roulettetype] = 0; + + player->kartstuff[k_invincibilitytimer] = 0; + K_RemoveGrowShrink(player); + + if (player->kartstuff[k_eggmanexplode]) + { + player->kartstuff[k_eggmanexplode] = 0; + player->kartstuff[k_eggmanblame] = -1; + } +} + +static INT32 K_FlameShieldMax(player_t *player) +{ + UINT32 disttofinish = 0; + UINT32 distv = DISTVAR; + UINT8 numplayers = 0; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + numplayers++; + if (players[i].kartstuff[k_position] == 1) + disttofinish = players[i].distancetofinish; + } + + if (numplayers <= 1) + { + return 16; // max when alone, for testing + } + else if (player->kartstuff[k_position] == 1) + { + return 0; // minimum for first + } + + disttofinish = player->distancetofinish - disttofinish; + distv = FixedMul(distv * FRACUNIT, mapobjectscale) / FRACUNIT; + return min(16, 1 + (disttofinish / distv)); +} + +// +// K_MoveKartPlayer +// +void K_MoveKartPlayer(player_t *player, boolean onground) +{ + ticcmd_t *cmd = &player->cmd; + boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->pflags & PF_ATTACKDOWN)); + boolean HOLDING_ITEM = (player->kartstuff[k_itemheld] || player->kartstuff[k_eggmanheld]); + boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0); + + player->pflags &= ~PF_HITFINISHLINE; + + if (!player->exiting) + { + if (player->kartstuff[k_oldposition] < player->kartstuff[k_position]) // But first, if you lost a place, + { + player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // then the other player taunts. + K_RegularVoiceTimers(player); // and you can't for a bit + } + else if (player->kartstuff[k_oldposition] > player->kartstuff[k_position]) // Otherwise, + { + K_PlayOvertakeSound(player->mo); // Say "YOU'RE TOO SLOW!" + player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // Restore the old position, + } + } + + if (player->kartstuff[k_positiondelay]) + player->kartstuff[k_positiondelay]--; + + // Prevent ring misfire + if (!(cmd->buttons & BT_ATTACK)) + { + if (player->kartstuff[k_itemtype] == KITEM_NONE + && NO_HYUDORO && !(HOLDING_ITEM + || player->kartstuff[k_itemamount] + || player->kartstuff[k_itemroulette] + || player->kartstuff[k_rocketsneakertimer] + || player->kartstuff[k_eggmanexplode])) + player->kartstuff[k_userings] = 1; + else + player->kartstuff[k_userings] = 0; + } + + if ((player->pflags & PF_ATTACKDOWN) && !(cmd->buttons & BT_ATTACK)) + player->pflags &= ~PF_ATTACKDOWN; + else if (cmd->buttons & BT_ATTACK) + player->pflags |= PF_ATTACKDOWN; + + if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > starttime + && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && (player->respawn.state == RESPAWNST_NONE)) + { + // First, the really specific, finicky items that function without the item being directly in your item slot. + // Karma item dropping + if (player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer]) + { + if (ATTACK_IS_DOWN) + { + mobj_t *newitem; + + if (player->kartstuff[k_comebackmode] == 1) + { + newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RANDOMITEM); + newitem->threshold = 69; // selected "randomly". + } + else + { + newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM); + if (player->kartstuff[k_eggmanblame] >= 0 + && player->kartstuff[k_eggmanblame] < MAXPLAYERS + && playeringame[player->kartstuff[k_eggmanblame]] + && !players[player->kartstuff[k_eggmanblame]].spectator + && players[player->kartstuff[k_eggmanblame]].mo) + P_SetTarget(&newitem->target, players[player->kartstuff[k_eggmanblame]].mo); + player->kartstuff[k_eggmanblame] = -1; + } + + newitem->flags2 = (player->mo->flags2 & MF2_OBJECTFLIP); + newitem->fuse = 15*TICRATE; // selected randomly. + + player->kartstuff[k_comebackmode] = 0; + player->kartstuff[k_comebacktimer] = comebacktime; + S_StartSound(player->mo, sfx_s254); + } + } + else + { + // Ring boosting + if (player->kartstuff[k_userings]) + { + if ((player->pflags & PF_ATTACKDOWN) && !player->kartstuff[k_ringdelay] && player->kartstuff[k_rings] > 0) + { + mobj_t *ring = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RING); + P_SetMobjState(ring, S_FASTRING1); + ring->extravalue1 = 1; // Ring use animation timer + ring->extravalue2 = 1; // Ring use animation flag + ring->shadowscale = 0; + P_SetTarget(&ring->target, player->mo); // user + player->kartstuff[k_rings]--; + player->kartstuff[k_ringdelay] = 3; + } + } + // Other items + else + { + // Eggman Monitor exploding + if (player->kartstuff[k_eggmanexplode]) + { + if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanexplode] <= 3*TICRATE && player->kartstuff[k_eggmanexplode] > 1) + player->kartstuff[k_eggmanexplode] = 1; + } + // Eggman Monitor throwing + else if (player->kartstuff[k_eggmanheld]) + { + if (ATTACK_IS_DOWN) + { + K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_eggmanheld] = 0; + K_UpdateHnextList(player, true); + } + } + // Rocket Sneaker usage + else if (player->kartstuff[k_rocketsneakertimer] > 1) + { + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) + { + K_DoSneaker(player, 2); + K_PlayBoostTaunt(player->mo); + player->kartstuff[k_rocketsneakertimer] -= 3*TICRATE; + if (player->kartstuff[k_rocketsneakertimer] < 1) + player->kartstuff[k_rocketsneakertimer] = 1; + } + } + else if (player->kartstuff[k_itemamount] <= 0) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + } + else + { + switch (player->kartstuff[k_itemtype]) + { + case KITEM_SNEAKER: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO) + { + K_DoSneaker(player, 1); + K_PlayBoostTaunt(player->mo); + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_ROCKETSNEAKER: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO + && player->kartstuff[k_rocketsneakertimer] == 0) + { + INT32 moloop; + mobj_t *mo = NULL; + mobj_t *prev = player->mo; + + K_PlayBoostTaunt(player->mo); + //player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s3k3a); + + //K_DoSneaker(player, 2); + + player->kartstuff[k_rocketsneakertimer] = (itemtime*3); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, true); + + for (moloop = 0; moloop < 2; moloop++) + { + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ROCKETSNEAKER); + K_MatchGenericExtraFlags(mo, player->mo); + mo->flags |= MF_NOCLIPTHING; + mo->angle = player->mo->angle; + mo->threshold = 10; + mo->movecount = moloop%2; + mo->movedir = mo->lastlook = moloop+1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + break; + case KITEM_INVINCIBILITY: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage normally, so you're free to waste it if you have multiple + { + if (!player->kartstuff[k_invincibilitytimer]) + { + mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INVULNFLASH); + P_SetTarget(&overlay->target, player->mo); + overlay->destscale = player->mo->scale; + P_SetScale(overlay, player->mo->scale); + } + player->kartstuff[k_invincibilitytimer] = itemtime+(2*TICRATE); // 10 seconds + if (P_IsLocalPlayer(player)) + S_ChangeMusicSpecial("kinvnc"); + if (! P_IsDisplayPlayer(player)) + S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kinvnc)); + P_RestoreMusic(player); + K_PlayPowerGloatSound(player->mo); + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_BANANA: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + INT32 moloop; + mobj_t *mo; + mobj_t *prev = player->mo; + + //K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s254); + + for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) + { + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BANANA_SHIELD); + if (!mo) + { + player->kartstuff[k_itemamount] = moloop; + break; + } + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = player->kartstuff[k_itemamount]; + mo->movedir = moloop+1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Banana x3 thrown + { + K_ThrowKartItem(player, false, MT_BANANA, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, false); + } + break; + case KITEM_EGGMAN: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + mobj_t *mo; + player->kartstuff[k_itemamount]--; + player->kartstuff[k_eggmanheld] = 1; + S_StartSound(player->mo, sfx_s254); + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD); + if (mo) + { + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = 1; + mo->movedir = 1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&player->mo->hnext, mo); + } + } + break; + case KITEM_ORBINAUT: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + angle_t newangle; + INT32 moloop; + mobj_t *mo = NULL; + mobj_t *prev = player->mo; + + //K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s3k3a); + + for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) + { + newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ORBINAUT_SHIELD); + if (!mo) + { + player->kartstuff[k_itemamount] = moloop; + break; + } + mo->flags |= MF_NOCLIPTHING; + mo->angle = newangle; + mo->threshold = 10; + mo->movecount = player->kartstuff[k_itemamount]; + mo->movedir = mo->lastlook = moloop+1; + mo->color = player->skincolor; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Orbinaut x3 thrown + { + K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, false); + } + break; + case KITEM_JAWZ: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + angle_t newangle; + INT32 moloop; + mobj_t *mo = NULL; + mobj_t *prev = player->mo; + + //K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s3k3a); + + for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++) + { + newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90; + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_JAWZ_SHIELD); + if (!mo) + { + player->kartstuff[k_itemamount] = moloop; + break; + } + mo->flags |= MF_NOCLIPTHING; + mo->angle = newangle; + mo->threshold = 10; + mo->movecount = player->kartstuff[k_itemamount]; + mo->movedir = mo->lastlook = moloop+1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&mo->hprev, prev); + P_SetTarget(&prev->hnext, mo); + prev = mo; + } + } + else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Jawz thrown + { + if (player->kartstuff[k_throwdir] == 1 || player->kartstuff[k_throwdir] == 0) + K_ThrowKartItem(player, true, MT_JAWZ, 1, 0); + else if (player->kartstuff[k_throwdir] == -1) // Throwing backward gives you a dud that doesn't home in + K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + K_UpdateHnextList(player, false); + } + break; + case KITEM_MINE: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + mobj_t *mo; + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s254); + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SSMINE_SHIELD); + if (mo) + { + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = 1; + mo->movedir = 1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&player->mo->hnext, mo); + } + } + else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) + { + K_ThrowKartItem(player, false, MT_SSMINE, 1, 1); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + player->kartstuff[k_itemheld] = 0; + K_UpdateHnextList(player, true); + } + break; + case KITEM_BALLHOG: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_itemamount]--; + K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0); + K_PlayAttackTaunt(player->mo); + } + break; + case KITEM_SPB: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_itemamount]--; + K_ThrowKartItem(player, true, MT_SPB, 1, 0); + K_PlayAttackTaunt(player->mo); + } + break; + case KITEM_GROW: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + if (player->kartstuff[k_growshrinktimer] < 0) // If you're shrunk, then "grow" will just make you normal again. + K_RemoveGrowShrink(player); + else + { + K_PlayPowerGloatSound(player->mo); + player->mo->scalespeed = mapobjectscale/TICRATE; + player->mo->destscale = (3*mapobjectscale)/2; + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + player->mo->destscale = (6*player->mo->destscale)/8; + player->kartstuff[k_growshrinktimer] = itemtime+(4*TICRATE); // 12 seconds + if (P_IsLocalPlayer(player)) + S_ChangeMusicSpecial("kgrow"); + if (! P_IsDisplayPlayer(player)) + S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); + P_RestoreMusic(player); + S_StartSound(player->mo, sfx_kc5a); + } + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_SHRINK: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_DoShrink(player); + player->kartstuff[k_itemamount]--; + K_PlayPowerGloatSound(player->mo); + } + break; + case KITEM_THUNDERSHIELD: + if (player->kartstuff[k_curshield] != KSHIELD_THUNDER) + { + mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THUNDERSHIELD); + P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); + P_SetTarget(&shield->target, player->mo); + S_StartSound(player->mo, sfx_s3k41); + player->kartstuff[k_curshield] = KSHIELD_THUNDER; + } + + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_DoThunderShield(player); + player->kartstuff[k_itemamount]--; + K_PlayAttackTaunt(player->mo); + } + break; + case KITEM_BUBBLESHIELD: + if (player->kartstuff[k_curshield] != KSHIELD_BUBBLE) + { + mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BUBBLESHIELD); + P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); + P_SetTarget(&shield->target, player->mo); + S_StartSound(player->mo, sfx_s3k3f); + player->kartstuff[k_curshield] = KSHIELD_BUBBLE; + } + + if (!HOLDING_ITEM && NO_HYUDORO) + { + if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) + { + if (player->kartstuff[k_bubbleblowup] == 0) + S_StartSound(player->mo, sfx_s3k75); + + player->kartstuff[k_bubbleblowup]++; + player->kartstuff[k_bubblecool] = player->kartstuff[k_bubbleblowup]*4; + + if (player->kartstuff[k_bubbleblowup] > bubbletime*2) + { + K_ThrowKartItem(player, (player->kartstuff[k_throwdir] > 0), MT_BUBBLESHIELDTRAP, -1, 0); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_bubbleblowup] = 0; + player->kartstuff[k_bubblecool] = 0; + player->kartstuff[k_holdready] = 0; + player->kartstuff[k_itemamount]--; + } + } + else + { + if (player->kartstuff[k_bubbleblowup] > bubbletime) + player->kartstuff[k_bubbleblowup] = bubbletime; + + if (player->kartstuff[k_bubbleblowup]) + player->kartstuff[k_bubbleblowup]--; + + player->kartstuff[k_holdready] = (player->kartstuff[k_bubblecool] ? 0 : 1); + } + } + break; + case KITEM_FLAMESHIELD: + if (player->kartstuff[k_curshield] != KSHIELD_FLAME) + { + mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_FLAMESHIELD); + P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2)); + P_SetTarget(&shield->target, player->mo); + S_StartSound(player->mo, sfx_s3k3e); + player->kartstuff[k_curshield] = KSHIELD_FLAME; + } + + if (!HOLDING_ITEM && NO_HYUDORO) + { + INT32 destlen = K_FlameShieldMax(player); + INT32 flamemax = 0; + + if (player->kartstuff[k_flamelength] < destlen) + player->kartstuff[k_flamelength]++; // Can always go up! + + flamemax = player->kartstuff[k_flamelength] * flameseg; + if (flamemax > 0) + flamemax += TICRATE; // leniency period + + if ((cmd->buttons & BT_ATTACK) && player->kartstuff[k_holdready]) + { + if (player->kartstuff[k_flamemeter] < 0) + player->kartstuff[k_flamemeter] = 0; + + if (player->kartstuff[k_flamedash] == 0) + { + S_StartSound(player->mo, sfx_s3k43); + K_PlayBoostTaunt(player->mo); + } + + player->kartstuff[k_flamedash] += 2; + player->kartstuff[k_flamemeter] += 2; + + if (!onground) + { + P_Thrust( + player->mo, R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), + FixedMul(player->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) + ); + } + + if (player->kartstuff[k_flamemeter] > flamemax) + { + P_Thrust( + player->mo, player->mo->angle, + FixedMul((50*player->mo->scale), K_GetKartGameSpeedScalar(gamespeed)) + ); + + player->kartstuff[k_flamemeter] = 0; + player->kartstuff[k_flamelength] = 0; + player->kartstuff[k_holdready] = 0; + player->kartstuff[k_itemamount]--; + } + } + else + { + player->kartstuff[k_holdready] = 1; + + if (player->kartstuff[k_flamemeter] > 0) + player->kartstuff[k_flamemeter]--; + + if (player->kartstuff[k_flamelength] > destlen) + { + player->kartstuff[k_flamelength]--; // Can ONLY go down if you're not using it + + flamemax = player->kartstuff[k_flamelength] * flameseg; + if (flamemax > 0) + flamemax += TICRATE; // leniency period + } + + if (player->kartstuff[k_flamemeter] > flamemax) + player->kartstuff[k_flamemeter] = flamemax; + } + } + break; + case KITEM_HYUDORO: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_itemamount]--; + K_DoHyudoroSteal(player); // yes. yes they do. + } + break; + case KITEM_POGOSPRING: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO + && !player->kartstuff[k_pogospring]) + { + K_PlayBoostTaunt(player->mo); + K_DoPogoSpring(player->mo, 32<kartstuff[k_pogospring] = 1; + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_SUPERRING: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + player->kartstuff[k_superring] += (10*3); + player->kartstuff[k_itemamount]--; + } + break; + case KITEM_KITCHENSINK: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + mobj_t *mo; + player->kartstuff[k_itemheld] = 1; + S_StartSound(player->mo, sfx_s254); + mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SINK_SHIELD); + if (mo) + { + mo->flags |= MF_NOCLIPTHING; + mo->threshold = 10; + mo->movecount = 1; + mo->movedir = 1; + P_SetTarget(&mo->target, player->mo); + P_SetTarget(&player->mo->hnext, mo); + } + } + else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Sink thrown + { + K_ThrowKartItem(player, false, MT_SINK, 1, 2); + K_PlayAttackTaunt(player->mo); + player->kartstuff[k_itemamount]--; + player->kartstuff[k_itemheld] = 0; + K_UpdateHnextList(player, true); + } + break; + case KITEM_SAD: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO + && !player->kartstuff[k_sadtimer]) + { + player->kartstuff[k_sadtimer] = stealtime; + player->kartstuff[k_itemamount]--; + } + break; + default: + break; + } + } + } + } + + // No more! + if (!player->kartstuff[k_itemamount]) + { + player->kartstuff[k_itemheld] = 0; + player->kartstuff[k_itemtype] = KITEM_NONE; + } + + if (K_GetShieldFromItem(player->kartstuff[k_itemtype]) == KSHIELD_NONE) + { + player->kartstuff[k_curshield] = KSHIELD_NONE; // RESET shield type + player->kartstuff[k_bubbleblowup] = 0; + player->kartstuff[k_bubblecool] = 0; + player->kartstuff[k_flamelength] = 0; + player->kartstuff[k_flamemeter] = 0; + } + + if (spbplace == -1 || player->kartstuff[k_position] != spbplace) + player->kartstuff[k_ringlock] = 0; // reset ring lock + + if (player->kartstuff[k_itemtype] == KITEM_SPB + || player->kartstuff[k_itemtype] == KITEM_SHRINK + || player->kartstuff[k_growshrinktimer] < 0) + indirectitemcooldown = 20*TICRATE; + + if (player->kartstuff[k_hyudorotimer] > 0) + { + INT32 hyu = hyudorotime; + + if (G_RaceGametype()) + hyu *= 2; // double in race + + if (leveltime & 1) + { + player->mo->drawflags |= MFD_DONTDRAW; + } + else + { + if (player->kartstuff[k_hyudorotimer] >= (TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyu-(TICRATE/2)) + player->mo->drawflags &= ~K_GetPlayerDontDrawFlag(player); + else + player->mo->drawflags &= ~MFD_DONTDRAW; + } + + player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints + } + else if (player->kartstuff[k_hyudorotimer] == 0) + { + player->mo->drawflags &= ~MFD_DONTDRAW; + } + + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 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]; + } + else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0) + { + player->mo->drawflags &= ~(MFD_TRANSMASK|MFD_BRIGHTMASK); + } + } + + if (onground) + { + fixed_t prevfriction = player->mo->friction; + + // Reduce friction after hitting a horizontal spring + if (player->kartstuff[k_tiregrease]) + player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->kartstuff[k_tiregrease]; + + // Friction + if (!player->kartstuff[k_offroad]) + { + if (player->speed > 0 && cmd->forwardmove == 0 && player->mo->friction == 59392) + player->mo->friction += 4608; + } + + // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel + if (player->speed > 0 && cmd->forwardmove < 0) + player->mo->friction -= 2048; + + // Karma ice physics + if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) + player->mo->friction += 1228; + + if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) + player->mo->friction += 614; + + // Wipeout slowdown + if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow]) + { + if (player->kartstuff[k_offroad]) + player->mo->friction -= 4912; + if (player->kartstuff[k_wipeoutslow] == 1) + player->mo->friction -= 9824; + } + + // Friction was changed, so we must recalculate a bunch of stuff + if (player->mo->friction != prevfriction) + { + if (player->mo->friction > FRACUNIT) + player->mo->friction = FRACUNIT; + if (player->mo->friction < 0) + player->mo->friction = 0; + + player->mo->movefactor = FixedDiv(ORIG_FRICTION, player->mo->friction); + + if (player->mo->movefactor < FRACUNIT) + player->mo->movefactor = 19*player->mo->movefactor - 18*FRACUNIT; + else + player->mo->movefactor = FRACUNIT; + + if (player->mo->movefactor < 32) + player->mo->movefactor = 32; + } + + // Don't go too far above your top speed when rubberbanding + // Down here, because we do NOT want to modify movefactor + if (K_PlayerUsesBotMovement(player)) + { + player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction); + } + } + + K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads + + // Quick Turning + // You can't turn your kart when you're not moving. + // So now it's time to burn some rubber! + if (player->speed < 2 && leveltime > starttime && cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE && cmd->driftturn != 0) + { + if (leveltime % 8 == 0) + S_StartSound(player->mo, sfx_s224); + } + + // Squishing + // If a Grow player or a sector crushes you, get flattened instead of being killed. + + if (player->kartstuff[k_squishedtimer] > 0) + { + //player->mo->flags |= MF_NOCLIP; + player->mo->momx = 0; + player->mo->momy = 0; + } + + // Play the starting countdown sounds + if (player == &players[g_localplayers[0]]) // Don't play louder in splitscreen + { + if ((leveltime == starttime-(3*TICRATE)) || (leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) + S_StartSound(NULL, sfx_s3ka7); + if (leveltime == starttime) + { + S_StartSound(NULL, sfx_s3kad); + S_StopMusic(); // The GO! sound stops the level start ambience + } + } + + // Start charging once you're given the opportunity. + if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) + { + if (cmd->buttons & BT_ACCELERATE) + { + if (player->kartstuff[k_boostcharge] == 0) + player->kartstuff[k_boostcharge] = cmd->latency; + + player->kartstuff[k_boostcharge]++; + } + else + player->kartstuff[k_boostcharge] = 0; + } + + // Increase your size while charging your engine. + if (leveltime < starttime+10) + { + player->mo->scalespeed = mapobjectscale/12; + player->mo->destscale = mapobjectscale + (player->kartstuff[k_boostcharge]*131); + if (cv_kartdebugshrink.value && !modeattacking && !player->bot) + player->mo->destscale = (6*player->mo->destscale)/8; + } + + // Determine the outcome of your charge. + if (leveltime > starttime && player->kartstuff[k_boostcharge]) + { + // Not even trying? + if (player->kartstuff[k_boostcharge] < 35) + { + if (player->kartstuff[k_boostcharge] > 17) + S_StartSound(player->mo, sfx_cdfm00); // chosen instead of a conventional skid because it's more engine-like + } + // Get an instant boost! + else if (player->kartstuff[k_boostcharge] <= 50) + { + player->kartstuff[k_startboost] = (50-player->kartstuff[k_boostcharge])+20; + + if (player->kartstuff[k_boostcharge] <= 36) + { + player->kartstuff[k_startboost] = 0; + K_DoSneaker(player, 0); + player->kartstuff[k_sneakertimer] = 70; // PERFECT BOOST!! + + if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) // Let everyone hear this one + S_StartSound(player->mo, sfx_s25f); + } + else + { + K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker + if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsDisplayPlayer(player)) + { + if (player->kartstuff[k_boostcharge] <= 40) + S_StartSound(player->mo, sfx_cdfm01); // You were almost there! + else + S_StartSound(player->mo, sfx_s23c); // Nope, better luck next time. + } + } + } + // You overcharged your engine? Those things are expensive!!! + else if (player->kartstuff[k_boostcharge] > 50) + { + player->powers[pw_nocontrol] = 40; + //S_StartSound(player->mo, sfx_kc34); + S_StartSound(player->mo, sfx_s3k83); + player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse + } + + player->kartstuff[k_boostcharge] = 0; + } +} + +void K_CheckSpectateStatus(void) +{ + UINT8 respawnlist[MAXPLAYERS]; + UINT8 i, j, numingame = 0, numjoiners = 0; + + // Maintain spectate wait timer + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN)) + players[i].kartstuff[k_spectatewait]++; + else + players[i].kartstuff[k_spectatewait] = 0; + } + + // No one's allowed to join + if (!cv_allowteamchange.value) + return; + + // Get the number of players in game, and the players to be de-spectated. + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + if (!players[i].spectator) + { + numingame++; + if (cv_ingamecap.value && numingame >= cv_ingamecap.value) // DON'T allow if you've hit the in-game player cap + return; + if (gamestate != GS_LEVEL) // Allow if you're not in a level + continue; + if (players[i].exiting) // DON'T allow if anyone's exiting + return; + if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet + continue; + if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in + return; + if (G_RaceGametype() && players[i].laps >= 2) // DON'T allow if the race is at 2 laps + return; + continue; + } + else if (!(players[i].pflags & PF_WANTSTOJOIN)) + continue; + + respawnlist[numjoiners++] = i; + } + + // literally zero point in going any further if nobody is joining + if (!numjoiners) + return; + + // Organize by spectate wait timer + if (cv_ingamecap.value) + { + UINT8 oldrespawnlist[MAXPLAYERS]; + memcpy(oldrespawnlist, respawnlist, numjoiners); + for (i = 0; i < numjoiners; i++) + { + UINT8 pos = 0; + INT32 ispecwait = players[oldrespawnlist[i]].kartstuff[k_spectatewait]; + + for (j = 0; j < numjoiners; j++) + { + INT32 jspecwait = players[oldrespawnlist[j]].kartstuff[k_spectatewait]; + if (j == i) + continue; + if (jspecwait > ispecwait) + pos++; + else if (jspecwait == ispecwait && j < i) + pos++; + } + + respawnlist[pos] = oldrespawnlist[i]; + } + } + + // Finally, we can de-spectate everyone! + for (i = 0; i < numjoiners; i++) + { + if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people? + break; + P_SpectatorJoinGame(&players[respawnlist[i]]); + } + + // Reset the match if you're in an empty server + if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+i >= 2)) // use previous i value + { + S_ChangeMusicInternal("chalng", false); // COME ON + mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD + } +} + +//} + +//{ SRB2kart HUD Code + +#define NUMPOSNUMS 10 +#define NUMPOSFRAMES 7 // White, three blues, three reds +#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple + +//{ Patch Definitions +static patch_t *kp_nodraw; + +static patch_t *kp_timesticker; +static patch_t *kp_timestickerwide; +static patch_t *kp_lapsticker; +static patch_t *kp_lapstickerwide; +static patch_t *kp_lapstickernarrow; +static patch_t *kp_splitlapflag; +static patch_t *kp_bumpersticker; +static patch_t *kp_bumperstickerwide; +static patch_t *kp_capsulesticker; +static patch_t *kp_capsulestickerwide; +static patch_t *kp_karmasticker; +static patch_t *kp_splitkarmabomb; +static patch_t *kp_timeoutsticker; + +static patch_t *kp_startcountdown[16]; +static patch_t *kp_racefinish[6]; + +static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; +static patch_t *kp_winnernum[NUMPOSFRAMES]; + +static patch_t *kp_facenum[MAXPLAYERS+1]; +static patch_t *kp_facehighlight[8]; + +static patch_t *kp_spbminimap; + +static patch_t *kp_ringsticker[2]; +static patch_t *kp_ringstickersplit[4]; +static patch_t *kp_ring[6]; +static patch_t *kp_smallring[6]; +static patch_t *kp_ringdebtminus; +static patch_t *kp_ringdebtminussmall; +static patch_t *kp_ringspblock[16]; +static patch_t *kp_ringspblocksmall[16]; + +static patch_t *kp_speedometersticker; +static patch_t *kp_speedometerlabel[4]; + +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_battlewin; +static patch_t *kp_battlecool; +static patch_t *kp_battlelose; +static patch_t *kp_battlewait; +static patch_t *kp_battleinfo; +static patch_t *kp_wanted; +static patch_t *kp_wantedsplit; +static patch_t *kp_wantedreticle; + +static patch_t *kp_itembg[4]; +static patch_t *kp_itemtimer[2]; +static patch_t *kp_itemmulsticker[2]; +static patch_t *kp_itemx; + +static patch_t *kp_superring[2]; +static patch_t *kp_sneaker[2]; +static patch_t *kp_rocketsneaker[2]; +static patch_t *kp_invincibility[13]; +static patch_t *kp_banana[2]; +static patch_t *kp_eggman[2]; +static patch_t *kp_orbinaut[5]; +static patch_t *kp_jawz[2]; +static patch_t *kp_mine[2]; +static patch_t *kp_ballhog[2]; +static patch_t *kp_selfpropelledbomb[2]; +static patch_t *kp_grow[2]; +static patch_t *kp_shrink[2]; +static patch_t *kp_thundershield[2]; +static patch_t *kp_bubbleshield[2]; +static patch_t *kp_flameshield[2]; +static patch_t *kp_hyudoro[2]; +static patch_t *kp_pogospring[2]; +static patch_t *kp_kitchensink[2]; +static patch_t *kp_sadface[2]; + +static patch_t *kp_check[6]; + +static patch_t *kp_rival[2]; + +static patch_t *kp_eggnum[4]; + +static patch_t *kp_flameshieldmeter[104][2]; +static patch_t *kp_flameshieldmeter_bg[16][2]; + +static patch_t *kp_fpview[3]; +static patch_t *kp_inputwheel[5]; + +static patch_t *kp_challenger[25]; + +static patch_t *kp_lapanim_lap[7]; +static patch_t *kp_lapanim_final[11]; +static patch_t *kp_lapanim_number[10][3]; +static patch_t *kp_lapanim_emblem[2]; +static patch_t *kp_lapanim_hand[3]; + +static patch_t *kp_yougotem; +static patch_t *kp_itemminimap; + +static patch_t *kp_alagles[10]; +static patch_t *kp_blagles[6]; + +static patch_t *kp_cpu; + +static patch_t *kp_nametagstem; + +void K_LoadKartHUDGraphics(void) +{ + INT32 i, j; + char buffer[9]; + + // Null Stuff + kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX); + + // Stickers + kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX); + kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX); + kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX); + kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX); + kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX); + kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX); + kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX); + kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX); + 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_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); + kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); + + // Starting countdown + kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); + kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); + kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX); + kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX); + kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX); + kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX); + kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX); + kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); + // Splitscreen + kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX); + kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX); + kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX); + kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); + kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX); + kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX); + kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); + kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); + + // Finish + kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX); + kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX); + // Splitscreen + kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX); + kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX); + // 2P splitscreen + kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX); + kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX); + + // Position numbers + sprintf(buffer, "K_POSNxx"); + for (i = 0; i < NUMPOSNUMS; i++) + { + buffer[6] = '0'+i; + for (j = 0; j < NUMPOSFRAMES; j++) + { + //sprintf(buffer, "K_POSN%d%d", i, j); + buffer[7] = '0'+j; + kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + } + + sprintf(buffer, "K_POSNWx"); + for (i = 0; i < NUMWINFRAMES; i++) + { + buffer[7] = '0'+i; + kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "OPPRNKxx"); + for (i = 0; i <= MAXPLAYERS; i++) + { + buffer[6] = '0'+(i/10); + buffer[7] = '0'+(i%10); + kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_CHILIx"); + for (i = 0; i < 8; i++) + { + buffer[7] = '0'+(i+1); + kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_spbminimap = W_CachePatchName("SPBMMAP", PU_HUDGFX); + + // Rings & Lives + kp_ringsticker[0] = W_CachePatchName("RNGBACKA", PU_HUDGFX); + kp_ringsticker[1] = W_CachePatchName("RNGBACKB", PU_HUDGFX); + + sprintf(buffer, "K_RINGx"); + for (i = 0; i < 6; i++) + { + buffer[6] = '0'+(i+1); + kp_ring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringdebtminus = W_CachePatchName("RDEBTMIN", PU_HUDGFX); + + sprintf(buffer, "SPBRNGxx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1) / 10); + buffer[7] = '0'+((i+1) % 10); + kp_ringspblock[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringstickersplit[0] = W_CachePatchName("SMRNGBGA", PU_HUDGFX); + kp_ringstickersplit[1] = W_CachePatchName("SMRNGBGB", PU_HUDGFX); + + sprintf(buffer, "K_SRINGx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '0'+(i+1); + kp_smallring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringdebtminussmall = W_CachePatchName("SRDEBTMN", PU_HUDGFX); + + sprintf(buffer, "SPBRGSxx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1) / 10); + buffer[7] = '0'+((i+1) % 10); + kp_ringspblocksmall[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Speedometer + kp_speedometersticker = W_CachePatchName("K_SPDMBG", PU_HUDGFX); + + sprintf(buffer, "K_SPDMLx"); + for (i = 0; i < 4; i++) + { + buffer[7] = '0'+(i+1); + kp_speedometerlabel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Extra ranking icons + kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX); + kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX); + 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); + + // Battle graphics + kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); + kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX); + kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX); + kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX); + kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX); + kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX); + kp_wantedsplit = W_CachePatchName("4PWANTED", PU_HUDGFX); + kp_wantedreticle = W_CachePatchName("MMAPWANT", PU_HUDGFX); + + // Kart Item Windows + kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX); + kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX); + kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX); + kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX); + kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX); + + kp_superring[0] = W_CachePatchName("K_ITRING", PU_HUDGFX); + kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX); + kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX); + + sprintf(buffer, "K_ITINVx"); + for (i = 0; i < 7; i++) + { + buffer[7] = '1'+i; + kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX); + kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX); + sprintf(buffer, "K_ITORBx"); + for (i = 0; i < 4; i++) + { + buffer[7] = '1'+i; + kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX); + kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX); + kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX); + kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX); + kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX); + kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX); + kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX); + kp_bubbleshield[0] = W_CachePatchName("K_ITBUBS", PU_HUDGFX); + kp_flameshield[0] = W_CachePatchName("K_ITFLMS", PU_HUDGFX); + kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX); + kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX); + kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX); + kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX); + + sprintf(buffer, "FSMFGxxx"); + for (i = 0; i < 104; i++) + { + buffer[5] = '0'+((i+1)/100); + buffer[6] = '0'+(((i+1)/10)%10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "FSMBG0xx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter_bg[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Splitscreen + kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX); + kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX); + kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX); + kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX); + + kp_superring[1] = W_CachePatchName("K_ISRING", PU_HUDGFX); + kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX); + kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX); + sprintf(buffer, "K_ISINVx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '1'+i; + kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX); + kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX); + kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX); + kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX); + kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX); + kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX); + kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX); + kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX); + kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX); + kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX); + kp_bubbleshield[1] = W_CachePatchName("K_ISBUBS", PU_HUDGFX); + kp_flameshield[1] = W_CachePatchName("K_ISFLMS", PU_HUDGFX); + kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX); + kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX); + kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX); + kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX); + + sprintf(buffer, "FSMFSxxx"); + for (i = 0; i < 104; i++) + { + buffer[5] = '0'+((i+1)/100); + buffer[6] = '0'+(((i+1)/10)%10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "FSMBS0xx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter_bg[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // CHECK indicators + sprintf(buffer, "K_CHECKx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '1'+i; + kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Rival indicators + sprintf(buffer, "K_RIVALx"); + for (i = 0; i < 2; i++) + { + buffer[7] = '1'+i; + kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Eggman warning numbers + sprintf(buffer, "K_EGGNx"); + for (i = 0; i < 4; i++) + { + buffer[6] = '0'+i; + kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // First person mode + kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX); + kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX); + kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX); + + // Input UI Wheel + sprintf(buffer, "K_WHEELx"); + for (i = 0; i < 5; i++) + { + buffer[7] = '0'+i; + kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // HERE COMES A NEW CHALLENGER + sprintf(buffer, "K_CHALxx"); + for (i = 0; i < 25; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Lap start animation + sprintf(buffer, "K_LAP0x"); + for (i = 0; i < 7; i++) + { + buffer[6] = '0'+(i+1); + kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPFxx"); + for (i = 0; i < 11; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPNxx"); + for (i = 0; i < 10; i++) + { + buffer[6] = '0'+i; + for (j = 0; j < 3; j++) + { + buffer[7] = '0'+(j+1); + kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + } + + sprintf(buffer, "K_LAPE0x"); + for (i = 0; i < 2; i++) + { + buffer[7] = '0'+(i+1); + kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPH0x"); + for (i = 0; i < 3; i++) + { + buffer[7] = '0'+(i+1); + kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); + kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); + + sprintf(buffer, "ALAGLESx"); + for (i = 0; i < 10; ++i) + { + buffer[7] = '0'+i; + kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "BLAGLESx"); + for (i = 0; i < 6; ++i) + { + buffer[7] = '0'+i; + kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); + + kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX); +} + +// For the item toggle menu +const char *K_GetItemPatch(UINT8 item, boolean tiny) +{ + switch (item) + { + case KITEM_SNEAKER: + case KRITEM_TRIPLESNEAKER: + return (tiny ? "K_ISSHOE" : "K_ITSHOE"); + case KITEM_ROCKETSNEAKER: + return (tiny ? "K_ISRSHE" : "K_ITRSHE"); + case KITEM_INVINCIBILITY: + return (tiny ? "K_ISINV1" : "K_ITINV1"); + case KITEM_BANANA: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + return (tiny ? "K_ISBANA" : "K_ITBANA"); + case KITEM_EGGMAN: + return (tiny ? "K_ISEGGM" : "K_ITEGGM"); + case KITEM_ORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB1"); + case KITEM_JAWZ: + case KRITEM_DUALJAWZ: + return (tiny ? "K_ISJAWZ" : "K_ITJAWZ"); + case KITEM_MINE: + return (tiny ? "K_ISMINE" : "K_ITMINE"); + case KITEM_BALLHOG: + return (tiny ? "K_ISBHOG" : "K_ITBHOG"); + case KITEM_SPB: + return (tiny ? "K_ISSPB" : "K_ITSPB"); + case KITEM_GROW: + return (tiny ? "K_ISGROW" : "K_ITGROW"); + case KITEM_SHRINK: + return (tiny ? "K_ISSHRK" : "K_ITSHRK"); + case KITEM_THUNDERSHIELD: + return (tiny ? "K_ISTHNS" : "K_ITTHNS"); + case KITEM_BUBBLESHIELD: + return (tiny ? "K_ISBUBS" : "K_ITBUBS"); + case KITEM_FLAMESHIELD: + return (tiny ? "K_ISFLMS" : "K_ITFLMS"); + case KITEM_HYUDORO: + return (tiny ? "K_ISHYUD" : "K_ITHYUD"); + case KITEM_POGOSPRING: + return (tiny ? "K_ISPOGO" : "K_ITPOGO"); + case KITEM_SUPERRING: + return (tiny ? "K_ISRING" : "K_ITRING"); + case KITEM_KITCHENSINK: + return (tiny ? "K_ISSINK" : "K_ITSINK"); + case KRITEM_TRIPLEORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB3"); + case KRITEM_QUADORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB4"); + default: + return (tiny ? "K_ISSAD" : "K_ITSAD"); + } +} + +//} + +INT32 ITEM_X, ITEM_Y; // Item Window +INT32 TIME_X, TIME_Y; // Time Sticker +INT32 LAPS_X, LAPS_Y; // Lap Sticker +INT32 POSI_X, POSI_Y; // Position Number +INT32 FACE_X, FACE_Y; // Top-four Faces +INT32 STCD_X, STCD_Y; // Starting countdown +INT32 CHEK_Y; // CHECK graphic +INT32 MINI_X, MINI_Y; // Minimap +INT32 WANT_X, WANT_Y; // Battle WANTED poster + +// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN. +INT32 ITEM2_X, ITEM2_Y; +INT32 LAPS2_X, LAPS2_Y; +INT32 POSI2_X, POSI2_Y; + + +static void K_initKartHUD(void) +{ + /* + BASEVIDWIDTH = 320 + BASEVIDHEIGHT = 200 + + Item window graphic is 41 x 33 + + Time Sticker graphic is 116 x 11 + Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14 + Therefore, timestamp is 116 x 14 altogether + + Lap Sticker is 80 x 11 + Lap flag is 22 x 20 + Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14 + Therefore, lapstamp is 80 x 20 altogether + + Position numbers are 43 x 53 + + Faces are 32 x 32 + Faces draw downscaled at 16 x 16 + Therefore, the allocated space for them is 16 x 67 altogether + + ---- + + ORIGINAL CZ64 SPLITSCREEN: + + Item window: + if (!splitscreen) { ICONX = 139; ICONY = 20; } + else { ICONX = BASEVIDWIDTH-315; ICONY = 60; } + + Time: 236, STRINGY( 12) + Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189) + + */ + + // Single Screen (defaults) + // Item Window + ITEM_X = 5; // 5 + ITEM_Y = 5; // 5 + // Level Timer + TIME_X = BASEVIDWIDTH - 148; // 172 + TIME_Y = 9; // 9 + // Level Laps + LAPS_X = 9; // 9 + LAPS_Y = BASEVIDHEIGHT - 29; // 171 + // Position Number + POSI_X = BASEVIDWIDTH - 9; // 268 + POSI_Y = BASEVIDHEIGHT - 9; // 138 + // Top-Four Faces + FACE_X = 9; // 9 + FACE_Y = 92; // 92 + // Starting countdown + STCD_X = BASEVIDWIDTH/2; // 9 + STCD_Y = BASEVIDHEIGHT/2; // 92 + // CHECK graphic + CHEK_Y = BASEVIDHEIGHT; // 200 + // Minimap + MINI_X = BASEVIDWIDTH - 50; // 270 + MINI_Y = (BASEVIDHEIGHT/2)-16; // 84 + // Battle WANTED poster + WANT_X = BASEVIDWIDTH - 55; // 270 + WANT_Y = BASEVIDHEIGHT- 71; // 176 + + if (r_splitscreen) // Splitscreen + { + ITEM_X = 5; + ITEM_Y = 3; + + LAPS_Y = (BASEVIDHEIGHT/2)-24; + + POSI_Y = (BASEVIDHEIGHT/2)- 2; + + STCD_Y = BASEVIDHEIGHT/4; + + MINI_Y = (BASEVIDHEIGHT/2); + + if (r_splitscreen > 1) // 3P/4P Small Splitscreen + { + // 1P (top left) + ITEM_X = -9; + ITEM_Y = -8; + + LAPS_X = 3; + LAPS_Y = (BASEVIDHEIGHT/2)-12; + + POSI_X = 24; + POSI_Y = (BASEVIDHEIGHT/2)-26; + + // 2P (top right) + ITEM2_X = BASEVIDWIDTH-39; + ITEM2_Y = -8; + + LAPS2_X = BASEVIDWIDTH-43; + LAPS2_Y = (BASEVIDHEIGHT/2)-12; + + POSI2_X = BASEVIDWIDTH -4; + POSI2_Y = (BASEVIDHEIGHT/2)-26; + + // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom. + + STCD_X = BASEVIDWIDTH/4; + + MINI_X = (3*BASEVIDWIDTH/4); + MINI_Y = (3*BASEVIDHEIGHT/4); + + if (r_splitscreen > 2) // 4P-only + { + MINI_X = (BASEVIDWIDTH/2); + MINI_Y = (BASEVIDHEIGHT/2); + } + } + } + + if (timeinmap > 113) + hudtrans = cv_translucenthud.value; + else if (timeinmap > 105) + hudtrans = ((((INT32)timeinmap) - 105)*cv_translucenthud.value)/(113-105); + else + hudtrans = 0; +} + +INT32 K_calcSplitFlags(INT32 snapflags) +{ + INT32 splitflags = 0; + + if (r_splitscreen == 0) + return snapflags; + + if (stplyr != &players[displayplayers[0]]) + { + if (r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + { + splitflags |= V_SPLITSCREEN; + } + else if (r_splitscreen > 1) + { + if (stplyr == &players[displayplayers[2]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) + splitflags |= V_SPLITSCREEN; + if (stplyr == &players[displayplayers[1]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) + splitflags |= V_HORZSCREEN; + } + } + + if (splitflags & V_SPLITSCREEN) + snapflags &= ~V_SNAPTOTOP; + else + snapflags &= ~V_SNAPTOBOTTOM; + + if (r_splitscreen > 1) + { + if (splitflags & V_HORZSCREEN) + snapflags &= ~V_SNAPTOLEFT; + else + snapflags &= ~V_SNAPTORIGHT; + } + + return (splitflags|snapflags); +} + +static void K_drawKartItem(void) +{ + // ITEM_X = BASEVIDWIDTH-50; // 270 + // ITEM_Y = 24; // 24 + + // Why write V_DrawScaledPatch calls over and over when they're all the same? + // Set to 'no item' just in case. + const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); + patch_t *localpatch = kp_nodraw; + patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); + patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); + INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... + //INT32 splitflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); + const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2); + INT32 itembar = 0; + INT32 maxl = 0; // itembar's normal highest value + const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); + UINT8 localcolor = SKINCOLOR_NONE; + SINT8 colormode = TC_RAINBOW; + UINT8 *colmap = NULL; + boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff + + if (stplyr->kartstuff[k_itemroulette]) + { + if (stplyr->skincolor) + localcolor = stplyr->skincolor; + + switch((stplyr->kartstuff[k_itemroulette] % (15*3)) / 3) + { + // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette + case 0: // Sneaker + localpatch = kp_sneaker[offset]; + //localcolor = SKINCOLOR_RASPBERRY; + break; + case 1: // Banana + localpatch = kp_banana[offset]; + //localcolor = SKINCOLOR_YELLOW; + break; + case 2: // Orbinaut + localpatch = kp_orbinaut[3+offset]; + //localcolor = SKINCOLOR_STEEL; + break; + case 3: // Mine + localpatch = kp_mine[offset]; + //localcolor = SKINCOLOR_JET; + break; + case 4: // Grow + localpatch = kp_grow[offset]; + //localcolor = SKINCOLOR_TEAL; + break; + case 5: // Hyudoro + localpatch = kp_hyudoro[offset]; + //localcolor = SKINCOLOR_STEEL; + break; + case 6: // Rocket Sneaker + localpatch = kp_rocketsneaker[offset]; + //localcolor = SKINCOLOR_TANGERINE; + break; + case 7: // Jawz + localpatch = kp_jawz[offset]; + //localcolor = SKINCOLOR_JAWZ; + break; + case 8: // Self-Propelled Bomb + localpatch = kp_selfpropelledbomb[offset]; + //localcolor = SKINCOLOR_JET; + break; + case 9: // Shrink + localpatch = kp_shrink[offset]; + //localcolor = SKINCOLOR_ORANGE; + break; + case 10: // Invincibility + localpatch = localinv; + //localcolor = SKINCOLOR_GREY; + break; + case 11: // Eggman Monitor + localpatch = kp_eggman[offset]; + //localcolor = SKINCOLOR_ROSE; + break; + case 12: // Ballhog + localpatch = kp_ballhog[offset]; + //localcolor = SKINCOLOR_LILAC; + break; + case 13: // Thunder Shield + localpatch = kp_thundershield[offset]; + //localcolor = SKINCOLOR_CYAN; + break; + case 14: // Super Ring + localpatch = kp_superring[offset]; + //localcolor = SKINCOLOR_GOLD; + break; + /*case 15: // Pogo Spring + localpatch = kp_pogospring[offset]; + localcolor = SKINCOLOR_TANGERINE; + break; + case 16: // Kitchen Sink + localpatch = kp_kitchensink[offset]; + localcolor = SKINCOLOR_STEEL; + break;*/ + default: + break; + } + } + else + { + // I'm doing this a little weird and drawing mostly in reverse order + // The only actual reason is to make sneakers line up this way in the code below + // This shouldn't have any actual baring over how it functions + // Hyudoro is first, because we're drawing it on top of the player's current item + if (stplyr->kartstuff[k_stolentimer] > 0) + { + if (leveltime & 2) + localpatch = kp_hyudoro[offset]; + else + localpatch = kp_nodraw; + } + else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2)) + { + localpatch = kp_hyudoro[offset]; + } + else if (stplyr->kartstuff[k_eggmanexplode] > 1) + { + if (leveltime & 1) + localpatch = kp_eggman[offset]; + else + localpatch = kp_nodraw; + } + else if (stplyr->kartstuff[k_rocketsneakertimer] > 1) + { + itembar = stplyr->kartstuff[k_rocketsneakertimer]; + maxl = (itemtime*3) - barlength; + + if (leveltime & 1) + localpatch = kp_rocketsneaker[offset]; + else + localpatch = kp_nodraw; + } + else if (stplyr->kartstuff[k_sadtimer] > 0) + { + if (leveltime & 2) + localpatch = kp_sadface[offset]; + else + localpatch = kp_nodraw; + } + else + { + if (stplyr->kartstuff[k_itemamount] <= 0) + return; + + switch(stplyr->kartstuff[k_itemtype]) + { + case KITEM_SNEAKER: + localpatch = kp_sneaker[offset]; + break; + case KITEM_ROCKETSNEAKER: + localpatch = kp_rocketsneaker[offset]; + break; + case KITEM_INVINCIBILITY: + localpatch = localinv; + localbg = kp_itembg[offset+1]; + break; + case KITEM_BANANA: + localpatch = kp_banana[offset]; + break; + case KITEM_EGGMAN: + localpatch = kp_eggman[offset]; + break; + case KITEM_ORBINAUT: + localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))]; + break; + case KITEM_JAWZ: + localpatch = kp_jawz[offset]; + break; + case KITEM_MINE: + localpatch = kp_mine[offset]; + break; + case KITEM_BALLHOG: + localpatch = kp_ballhog[offset]; + break; + case KITEM_SPB: + localpatch = kp_selfpropelledbomb[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_GROW: + localpatch = kp_grow[offset]; + break; + case KITEM_SHRINK: + localpatch = kp_shrink[offset]; + break; + case KITEM_THUNDERSHIELD: + localpatch = kp_thundershield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_BUBBLESHIELD: + localpatch = kp_bubbleshield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_FLAMESHIELD: + localpatch = kp_flameshield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_HYUDORO: + localpatch = kp_hyudoro[offset]; + break; + case KITEM_POGOSPRING: + localpatch = kp_pogospring[offset]; + break; + case KITEM_SUPERRING: + localpatch = kp_superring[offset]; + break; + case KITEM_KITCHENSINK: + localpatch = kp_kitchensink[offset]; + break; + case KITEM_SAD: + localpatch = kp_sadface[offset]; + break; + default: + return; + } + + if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1)) + localpatch = kp_nodraw; + } + + if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) + { + colormode = TC_BLINK; + + switch (stplyr->karthud[khud_itemblinkmode]) + { + case 2: + localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + break; + case 1: + localcolor = SKINCOLOR_RED; + break; + default: + localcolor = SKINCOLOR_WHITE; + break; + } + } + } + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); + } + else // now we're having a fun game. + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P3 to the bottom. + } + else // else, that means we're P2 or P4. + { + fx = ITEM2_X; + fy = ITEM2_Y; + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P4 to the bottom + flipamount = true; + } + } + + if (localcolor != SKINCOLOR_NONE) + colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE); + + V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, localbg); + + // Then, the numbers: + if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette]) + { + V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. + V_DrawFixedPatch(fx<kartstuff[k_itemamount])); + else + V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|fflags, va("x%d", stplyr->kartstuff[k_itemamount])); + else + { + V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|fflags, kp_itemx); + V_DrawKartString(fx+38, fy+36, V_HUDTRANS|fflags, va("%d", stplyr->kartstuff[k_itemamount])); + } + } + else + V_DrawFixedPatch(fx< 2) + { + V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one + if (height == 2) + V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside + V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine + } + } + + // Quick Eggman numbers + if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/) + V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|fflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]); + + if (stplyr->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && stplyr->kartstuff[k_flamelength] > 0) + { + INT32 numframes = 104; + INT32 absolutemax = 16 * flameseg; + INT32 flamemax = stplyr->kartstuff[k_flamelength] * flameseg; + INT32 flamemeter = min(stplyr->kartstuff[k_flamemeter], flamemax); + + INT32 bf = 16 - stplyr->kartstuff[k_flamelength]; + INT32 ff = numframes - ((flamemeter * numframes) / absolutemax); + INT32 fmin = (8 * (bf-1)); + + INT32 xo = 6, yo = 4; + INT32 flip = 0; + + if (offset) + { + xo++; + + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // Flip for P1 and P3 (yes, that's correct) + { + xo -= 62; + flip = V_FLIP; + } + } + + if (ff < fmin) + ff = fmin; + + if (bf >= 0 && bf < 16) + V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter_bg[bf][offset]); + + if (ff >= 0 && ff < numframes && stplyr->kartstuff[k_flamemeter] > 0) + { + if ((stplyr->kartstuff[k_flamemeter] > flamemax) && (leveltime & 1)) + { + UINT8 *fsflash = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); + V_DrawMappedPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset], fsflash); + } + else + { + V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset]); + } + } + } +} + +void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode) +{ + // TIME_X = BASEVIDWIDTH-124; // 196 + // TIME_Y = 6; // 6 + + tic_t worktime; + + INT32 splitflags = 0; + if (!mode) + { + splitflags = V_HUDTRANS|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTORIGHT); + if (cv_timelimit.value && timelimitintics > 0) + { + if (drawtime >= timelimitintics) + drawtime = 0; + else + drawtime = timelimitintics - drawtime; + } + } + + V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide)); + + TX += 33; + + worktime = drawtime/(60*TICRATE); + + if (mode && !drawtime) + V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--")); + else if (worktime < 100) // 99:99:99 only + { + // zero minute + if (worktime < 10) + { + V_DrawKartString(TX, TY+3, splitflags, va("0")); + // minutes time 0 __ __ + V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime)); + } + // minutes time 0 __ __ + else + V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime)); + + // apostrophe location _'__ __ + V_DrawKartString(TX+24, TY+3, splitflags, va("'")); + + worktime = (drawtime/TICRATE % 60); + + // zero second _ 0_ __ + if (worktime < 10) + { + V_DrawKartString(TX+36, TY+3, splitflags, va("0")); + // seconds time _ _0 __ + V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime)); + } + // zero second _ 00 __ + else + V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime)); + + // quotation mark location _ __"__ + V_DrawKartString(TX+60, TY+3, splitflags, va("\"")); + + worktime = G_TicsToCentiseconds(drawtime); + + // zero tick _ __ 0_ + if (worktime < 10) + { + V_DrawKartString(TX+72, TY+3, splitflags, va("0")); + // tics _ __ _0 + V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime)); + } + // zero tick _ __ 00 + else + V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime)); + } + else if ((drawtime/TICRATE) & 1) + V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); + + if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! + { + INT32 workx = TX + 96, worky = TY+18; + SINT8 curemb = 0; + patch_t *emblempic[3] = {NULL, NULL, NULL}; + UINT8 *emblemcol[3] = {NULL, NULL, NULL}; + + emblem_t *emblem = M_GetLevelEmblems(emblemmap); + while (emblem) + { + char targettext[9]; + + switch (emblem->type) + { + case ET_TIME: + { + static boolean canplaysound = true; + tic_t timetoreach = emblem->var; + + if (emblem->collected) + { + emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE); + emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE); + if (++curemb == 3) + break; + goto bademblem; + } + + snprintf(targettext, 9, "%i'%02i\"%02i", + G_TicsToMinutes(timetoreach, false), + G_TicsToSeconds(timetoreach), + G_TicsToCentiseconds(timetoreach)); + + if (!mode) + { + if (stplyr->realtime > timetoreach) + { + splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF; + if (canplaysound) + { + S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks + canplaysound = false; + } + } + else if (!canplaysound) + canplaysound = true; + } + + targettext[8] = 0; + } + break; + default: + goto bademblem; + } + + V_DrawRightAlignedString(workx, worky, splitflags, targettext); + workx -= 67; + V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE)); + + break; + + bademblem: + emblem = M_GetLevelEmblems(-1); + } + + if (!mode) + splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS; + while (curemb--) + { + workx -= 12; + V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]); + } + } +} + +static void K_DrawKartPositionNum(INT32 num) +{ + // POSI_X = BASEVIDWIDTH - 51; // 269 + // POSI_Y = BASEVIDHEIGHT- 64; // 136 + + boolean win = (stplyr->exiting && num == 1); + //INT32 X = POSI_X; + INT32 W = SHORT(kp_positionnum[0][0]->width); + fixed_t scale = FRACUNIT; + patch_t *localpatch = kp_positionnum[0][0]; + //INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT); + INT32 fx = 0, fy = 0, fflags = 0; + boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun. + boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen. + boolean overtake = false; + + if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting) + { + scale *= 2; + overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw. + } + if (r_splitscreen) + scale /= 2; + + W = FixedMul(W<>FRACBITS; + + // pain and suffering defined below + if (!r_splitscreen) + { + fx = POSI_X; + fy = BASEVIDHEIGHT - 8; + fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different. + { + fx = POSI_X; + if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap. + { + fy = 30; + fflags = V_SNAPTOTOP|V_SNAPTORIGHT; + if (overtake) + flipvdraw = true; // make sure overtaking doesn't explode us + } + else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap. + { + fy = BASEVIDHEIGHT - 8; + fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = POSI_X; + fy = POSI_Y; + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + flipdraw = true; + if (num && num >= 10) + fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen. + } + else // else, that means we're P2 or P4. + { + fx = POSI2_X; + fy = POSI2_Y; + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + } + } + + // Special case for 0 + if (!num) + { + V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers + + // Draw the number + while (num) + { + if (win) // 1st place winner? You get rainbows!! + localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; + else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won + { + // Alternate frame every three frames + switch (leveltime % 9) + { + case 1: case 2: case 3: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][4]; + else + localpatch = kp_positionnum[num % 10][1]; + break; + case 4: case 5: case 6: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][5]; + else + localpatch = kp_positionnum[num % 10][2]; + break; + case 7: case 8: case 9: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][6]; + else + localpatch = kp_positionnum[num % 10][3]; + break; + default: + localpatch = kp_positionnum[num % 10][0]; + break; + } + } + else + localpatch = kp_positionnum[num % 10][0]; + + V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|fflags, localpatch, NULL); + // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen. + // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either. + + fx -= W; + num /= 10; + } +} + +static boolean K_drawKartPositionFaces(void) +{ + // FACE_X = 15; // 15 + // FACE_Y = 72; // 72 + + INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one + INT32 i, j, ranklines, strank = -1; + boolean completed[MAXPLAYERS]; + INT32 rankplayer[MAXPLAYERS]; + INT32 bumperx, numplayersingame = 0; + UINT8 *colormap; + + ranklines = 0; + memset(completed, 0, sizeof (completed)); + memset(rankplayer, 0, sizeof (rankplayer)); + + for (i = 0; i < MAXPLAYERS; i++) + { + rankplayer[i] = -1; + + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + numplayersingame++; + } + + if (numplayersingame <= 1) + return true; + +#ifdef HAVE_BLUA + if (!LUA_HudEnabled(hud_minirankings)) + return false; // Don't proceed but still return true for free play above if HUD is disabled. +#endif + + for (j = 0; j < numplayersingame; j++) + { + UINT8 lowestposition = MAXPLAYERS+1; + for (i = 0; i < MAXPLAYERS; i++) + { + if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + if (players[i].kartstuff[k_position] >= lowestposition) + continue; + + rankplayer[ranklines] = i; + lowestposition = players[i].kartstuff[k_position]; + } + + i = rankplayer[ranklines]; + + completed[i] = true; + + if (players+i == stplyr) + strank = ranklines; + + //if (ranklines == 5) + //break; // Only draw the top 5 players -- we do this a different way now... + + ranklines++; + } + + if (ranklines < 5) + Y -= (9*ranklines); + else + Y -= (9*5); + + if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) + { + i = 0; + if (ranklines > 5) // could be both... + ranklines = 5; + } + else if (strank+3 > ranklines) // too close to the bottom? + { + i = ranklines - 5; + if (i < 0) + i = 0; + } + else + { + i = strank-2; + ranklines = strank+3; + } + + for (; i < ranklines; i++) + { + if (!playeringame[rankplayer[i]]) continue; + if (players[rankplayer[i]].spectator) continue; + if (!players[rankplayer[i]].mo) continue; + + bumperx = FACE_X+19; + + if (players[rankplayer[i]].mo->color) + { + colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); + if (players[rankplayer[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); + + V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap); + +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_battlebumpers)) + { +#endif + if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 0) + { + V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[0], colormap); + for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++) + { + bumperx += 5; + V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[1], colormap); + } + } +#ifdef HAVE_BLUA + } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating: +#endif + } + + if (i == strank) + V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); + + if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0) + V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SNAPTOLEFT, kp_ranknobumpers); + else + { + INT32 pos = players[rankplayer[i]].kartstuff[k_position]; + if (pos < 0 || pos > MAXPLAYERS) + pos = 0; + // Draws the little number over the face + V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SNAPTOLEFT, kp_facenum[pos]); + } + + Y += 18; + } + + return false; +} + +// +// HU_DrawTabRankings -- moved here to take advantage of kart stuff! +// +void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) +{ + static tic_t alagles_timer = 9; + INT32 i, rightoffset = 240; + const UINT8 *colormap; + INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; + int y2; + + //this function is designed for 9 or less score lines only + //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up + + V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice! + if (scorelines > 8) + { + V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides. + V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom. + rightoffset = (BASEVIDWIDTH/2) - 4 - x; + } + + for (i = 0; i < scorelines; i++) + { + char strtime[MAXPLAYERNAME+1]; + + if (players[tab[i].num].spectator || !players[tab[i].num].mo) + continue; //ignore them. + + if (netgame) // don't draw ping offline + { + if (players[tab[i].num].bot) + { + V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu); + } + else if (tab[i].num != serverplayer || !server_lagless) + { + HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); + } + } + + STRBUFCPY(strtime, tab[i].name); + + y2 = y; + + if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) + { + y2 = ( y - 4 ); + + V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); + // every 70 tics + if (( leveltime % 70 ) == 0) + { + alagles_timer = 9; + } + if (alagles_timer > 0) + { + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); + if (( leveltime % 2 ) == 0) + alagles_timer--; + } + else + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); + + y2 += SHORT (kp_alagles[0]->height) + 1; + } + + if (scorelines > 8) + V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + else + V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); + + if (players[tab[i].num].mo->color) + { + colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); + if (players[tab[i].num].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); + + V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap); + /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 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++) + { + bumperx += 5; + V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap); + } + }*/ + } + + if (tab[i].num == whiteplayer) + V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); + + if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0) + V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); + else + { + INT32 pos = players[tab[i].num].kartstuff[k_position]; + if (pos < 0 || pos > MAXPLAYERS) + pos = 0; + // Draws the little number over the face + V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]); + } + + if (G_RaceGametype()) + { +#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) + if (scorelines > 8) + { + if (players[tab[i].num].exiting) + V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); + else if (players[tab[i].num].pflags & PF_TIMEOVER) + V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST."); + else if (circuitmap) + V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count)); + } + else + { + if (players[tab[i].num].exiting) + V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime)); + else if (players[tab[i].num].pflags & PF_TIMEOVER) + V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST."); + else if (circuitmap) + V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count)); + } +#undef timestring + } + else + V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); + + y += 18; + if (i == 7) + { + y = 33; + x = (BASEVIDWIDTH/2) + 4; + } + } +} + +#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) + +static void K_drawKartLapsAndRings(void) +{ + const boolean uselives = G_GametypeUsesLives(); + SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); + UINT8 rn[2]; + INT32 ringflip = 0; + UINT8 *ringmap = NULL; + boolean colorring = false; + INT32 ringx = 0; + + rn[0] = ((abs(stplyr->kartstuff[k_rings]) / 10) % 10); + rn[1] = (abs(stplyr->kartstuff[k_rings]) % 10); + + if (stplyr->kartstuff[k_rings] <= 0 && (leveltime/5 & 1)) // In debt + { + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); + colorring = true; + } + else if (stplyr->kartstuff[k_rings] >= 20) // Maxed out + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); + + if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) + { + ringflip = V_FLIP; + ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; + ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); + } + + if (r_splitscreen > 1) + { + INT32 fx = 0, fy = 0, fr = 0; + INT32 flipflag = 0; + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = LAPS_X; + fy = LAPS_Y; + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = LAPS_X; + fy = LAPS_Y; + splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + } + else // else, that means we're P2 or P4. + { + fx = LAPS2_X; + fy = LAPS2_Y; + splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + flipflag = V_FLIP; // make the string right aligned and other shit + } + } + + fr = fx; + + // Laps + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); + + V_DrawScaledPatch(fx, fy, V_HUDTRANS|splitflags, kp_splitlapflag); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); + + if (cv_numlaps.value >= 10) + { + UINT8 ln[2]; + ln[0] = ((stplyr->laps / 10) % 10); + ln[1] = (stplyr->laps % 10); + + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + + ln[0] = ((abs(cv_numlaps.value) / 10) % 10); + ln[1] = (abs(cv_numlaps.value) % 10); + + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_numlaps.value) % 10]); + } + + // Rings + if (!uselives) + { + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]); + if (flipflag) + fr += 15; + } + else + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[0]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); + + V_DrawMappedPatch(fr+ringx, fy-13, V_HUDTRANS|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL)); + + if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt + V_DrawMappedPatch(fr+7, fy-10, V_HUDTRANS|splitflags, kp_ringdebtminussmall, ringmap); + + V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); + V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); + + // SPB ring lock + if (stplyr->kartstuff[k_ringlock]) + V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); + + // Lives + if (uselives) + { + UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); + V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); + V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow + } + } + else + { + // Laps + V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_lapsticker); + + if (stplyr->exiting) + V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN"); + else + V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); + + // Rings + if (!uselives) + V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]); + else + V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]); + + V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); + + if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt + { + V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringdebtminus, ringmap); + V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); + } + else + { + V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); + } + + // SPB ring lock + if (stplyr->kartstuff[k_ringlock]) + V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); + + // Lives + if (uselives) + { + UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); + V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap); + V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow + } + } +} + +#undef RINGANIM_NUMFRAMES +#undef RINGANIM_FLIPFRAME + +static void K_drawKartSpeedometer(void) +{ + static fixed_t convSpeed; + UINT8 labeln = 0; + UINT8 numbers[3]; + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); + UINT8 battleoffset = 0; + + if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! + { + switch (cv_kartspeedometer.value) + { + case 1: // Sonic Drift 2 style percentage + default: + convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) + labeln = 0; + break; + case 2: // Kilometers + convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058 + labeln = 1; + break; + case 3: // Miles + convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774 + labeln = 2; + break; + case 4: // Fracunits + convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT; // 1.0. duh. + labeln = 3; + break; + } + } + + // Don't overflow + if (convSpeed > 999) + convSpeed = 999; + + numbers[0] = ((convSpeed / 100) % 10); + numbers[1] = ((convSpeed / 10) % 10); + numbers[2] = (convSpeed % 10); + + if (G_BattleGametype()) + battleoffset = 8; + + V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometersticker); + V_DrawScaledPatch(LAPS_X+7, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[0]]); + V_DrawScaledPatch(LAPS_X+13, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[1]]); + V_DrawScaledPatch(LAPS_X+19, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[2]]); + V_DrawScaledPatch(LAPS_X+29, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometerlabel[labeln]); +} + +static void K_drawKartBumpersOrKarma(void) +{ + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); + + if (r_splitscreen > 1) + { + INT32 fx = 0, fy = 0; + INT32 flipflag = 0; + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = LAPS_X; + fy = LAPS_Y; + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = LAPS_X; + fy = LAPS_Y; + splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + } + else // else, that means we're P2 or P4. + { + fx = LAPS2_X; + fy = LAPS2_Y; + splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + flipflag = V_FLIP; // make the string right aligned and other shit + } + } + + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); + + if (battlecapsules) + { + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankcapsule, NULL); + + if (numtargets > 9 || maptargets > 9) + { + UINT8 ln[2]; + ln[0] = ((numtargets / 10) % 10); + ln[1] = (numtargets % 10); + + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + + ln[0] = ((maptargets / 10) % 10); + ln[1] = (maptargets % 10); + + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[numtargets % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[maptargets % 10]); + } + } + else + { + if (stplyr->kartstuff[k_bumper] <= 0) + { + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[2]); + } + else + { + INT32 maxbumper = K_StartingBumperCount(); + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(maxbumper) % 10]); + } + } + } + } + else + { + if (battlecapsules) + { + if (numtargets > 9 && maptargets > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulestickerwide, NULL); + else + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulesticker, NULL); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", numtargets, maptargets)); + } + else + { + if (stplyr->kartstuff[k_bumper] <= 0) + { + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); + } + else + { + INT32 maxbumper = K_StartingBumperCount(); + + if (stplyr->kartstuff[k_bumper] > 9 && maxbumper > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap); + else + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap); + + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], maxbumper)); + } + } + } +} + +static void K_drawKartWanted(void) +{ + UINT8 i, numwanted = 0; + UINT8 *colormap = NULL; + INT32 basex = 0, basey = 0; + + if (stplyr != &players[displayplayers[0]]) + return; + + for (i = 0; i < 4; i++) + { + if (battlewanted[i] == -1) + break; + numwanted++; + } + + if (numwanted <= 0) + return; + + // set X/Y coords depending on splitscreen. + if (r_splitscreen < 3) // 1P and 2P use the same code. + { + basex = WANT_X; + basey = WANT_Y; + if (r_splitscreen == 2) + { + basey += 16; // slight adjust for 3P + basex -= 6; + } + } + else if (r_splitscreen == 3) // 4P splitscreen... + { + basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen + basey = BASEVIDHEIGHT - 55; + //basey2 = 4; + } + + if (battlewanted[0] != -1) + colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE); + V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap); + /*if (basey2) + V_DrawFixedPatch(basex< 1 ? 13 : 8), y = basey+(r_splitscreen > 1 ? 16 : 21); + fixed_t scale = FRACUNIT/2; + player_t *p = &players[battlewanted[i]]; + + if (battlewanted[i] == -1) + break; + + if (numwanted == 1) + scale = FRACUNIT; + else + { + if (i & 1) + x += 16; + if (i > 1) + y += 16; + } + + if (players[battlewanted[i]].skincolor) + { + colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE); + V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap); + /*if (basey2) // again with 4p stuff + V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);*/ + } + } +} + +static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) +{ + const INT32 swhalf = (BASEVIDWIDTH / 2); + const fixed_t swhalffixed = swhalf * FRACUNIT; + + const INT32 shhalf = (BASEVIDHEIGHT / 2); + const fixed_t shhalffixed = shhalf * FRACUNIT; + + INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); + fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); + fixed_t factor = INT32_MAX; + + if (abs(anglediff) > ANGLE_90) + { + if (hud_x != NULL) + { + *hud_x = -BASEVIDWIDTH * FRACUNIT; + } + + if (hud_y != NULL) + { + *hud_y = -BASEVIDWIDTH * FRACUNIT; + } + + //*hud_scale = FRACUNIT; + return; + } + + factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); + +#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) + + if (hud_x != NULL) + { + *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; + + if (encoremode) + { + *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; + } + + if (r_splitscreen >= 2) + { + *hud_x /= 2; + + if (camnum & 1) + { + *hud_x += swhalffixed; + } + } + } + + if (hud_y != NULL) + { + *hud_y = campos->z - point->z; + *hud_y = FixedDiv(*hud_y, FixedMul(factor, distance)); + *hud_y = (*hud_y * swhalf) + shhalffixed; + *hud_y = *hud_y + NEWTAN(camaim) * swhalf; + + if (r_splitscreen >= 1) + { + *hud_y /= 2; + + if (camnum > 1) + { + *hud_y += shhalffixed; + } + } + } + + //*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance)); + +#undef NEWTAN +} + +static void K_drawKartPlayerCheck(void) +{ + const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; + INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); + + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { + return; + } + + if (stplyr->spectator || stplyr->awayviewtics) + { + return; + } + + if (stplyr->cmd.buttons & BT_LOOKBACK) + { + return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = stplyr->mo->x; + c.y = stplyr->mo->y; + c.z = stplyr->mo->z; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *checkplayer = &players[i]; + fixed_t distance = maxdistance+1; + UINT8 *colormap = NULL; + UINT8 pnum = 0; + fixed_t x = 0; + vertex_t v; + + if (!playeringame[i] || checkplayer->spectator) + { + // Not in-game + continue; + } + + if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo)) + { + // No object + continue; + } + + if (checkplayer == stplyr) + { + // This is you! + continue; + } + + v.x = checkplayer->mo->x; + v.y = checkplayer->mo->y; + v.z = checkplayer->mo->z; + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) + { + pnum++; // white frames + } + + if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0) + { + pnum += 4; + } + else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer]) + { + pnum += 2; + } + + K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); + + colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); + V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); + } +} + +static void K_drawKartNameTags(void) +{ + const fixed_t maxdistance = 8192*mapobjectscale; + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; + + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { + return; + } + + if (stplyr->awayviewtics) + { + return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = thiscam->x; + c.y = thiscam->y; + c.z = thiscam->z; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *ntplayer = &players[i]; + fixed_t distance = maxdistance+1; + + fixed_t x = -BASEVIDWIDTH * FRACUNIT; + fixed_t y = -BASEVIDWIDTH * FRACUNIT; + + vertex_t v; + UINT8 j; + + if (!playeringame[i] || ntplayer->spectator) + { + // Not in-game + continue; + } + + if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) + { + // No object + continue; + } + + if (!P_CheckSight(stplyr->mo, ntplayer->mo)) + { + // Can't see + continue; + } + + for (j = 0; j <= r_splitscreen; j++) + { + if (ntplayer == &players[displayplayers[j]]) + { + break; + } + } + + if (j <= r_splitscreen) + { + // Is a player that's being shown on this computer + continue; + } + + v.x = ntplayer->mo->x; + v.y = ntplayer->mo->y; + v.z = ntplayer->mo->z; + + if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) + { + v.z += ntplayer->mo->height; + } + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); + + if (x == -BASEVIDWIDTH * FRACUNIT) + { + // Off-screen + continue; + } + + if (ntplayer->bot) + { + if (ntplayer->botvars.rival == true) + { + UINT8 blink = ((leveltime / 7) & 1); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); + } + } + else if (netgame) + { + if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-2) + && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+2)) + { + INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); + INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); + UINT8 *colormap = V_GetStringColormap(clr); + INT32 barx = 0, bary = 0, barw = 0; + + // Since there's no "V_DrawFixedFill", and I don't feel like making it, + // fuck it, we're gonna just V_NOSCALESTART hack it + barw = (namelen * vid.dupx); + + barx = (x * vid.dupx) / FRACUNIT; + bary = (y * vid.dupy) / FRACUNIT; + + barx += (6 * vid.dupx); + bary -= (16 * vid.dupx); + + // Center it if necessary + if (vid.width != BASEVIDWIDTH * vid.dupx) + { + barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; + } + + if (vid.height != BASEVIDHEIGHT * vid.dupy) + { + bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; + } + + // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. + V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); + V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); + // END DRAWFILL DUMBNESS + + // Draw the stem + V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); + + // Draw the name itself + V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); + } + } + } +} + +static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) +{ + // amnum xpos & ypos are the icon's speed around the HUD. + // The number being divided by is for how fast it moves. + // The higher the number, the slower it moves. + + // am xpos & ypos are the icon's starting position. Withouht + // it, they wouldn't 'spawn' on the top-right side of the HUD. + + fixed_t amnumxpos, amnumypos; + INT32 amxpos, amypos; + + node_t *bsp = &nodes[numnodes-1]; + fixed_t maxx, minx, maxy, miny; + + fixed_t mapwidth, mapheight; + fixed_t xoffset, yoffset; + fixed_t xscale, yscale, zoom; + + maxx = maxy = INT32_MAX; + minx = miny = INT32_MIN; + minx = bsp->bbox[0][BOXLEFT]; + maxx = bsp->bbox[0][BOXRIGHT]; + miny = bsp->bbox[0][BOXBOTTOM]; + maxy = bsp->bbox[0][BOXTOP]; + + if (bsp->bbox[1][BOXLEFT] < minx) + minx = bsp->bbox[1][BOXLEFT]; + if (bsp->bbox[1][BOXRIGHT] > maxx) + maxx = bsp->bbox[1][BOXRIGHT]; + if (bsp->bbox[1][BOXBOTTOM] < miny) + miny = bsp->bbox[1][BOXBOTTOM]; + if (bsp->bbox[1][BOXTOP] > maxy) + maxy = bsp->bbox[1][BOXTOP]; + + // You might be wondering why these are being bitshift here + // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... + // map boundaries and sizes will ALWAYS be whole numbers thankfully + // later calculations take into consideration that these are actually not in terms of FRACUNIT though + minx >>= FRACBITS; + maxx >>= FRACBITS; + miny >>= FRACBITS; + maxy >>= FRACBITS; + + mapwidth = maxx - minx; + mapheight = maxy - miny; + + // These should always be small enough to be bitshift back right now + xoffset = (minx + mapwidth/2)<width, mapwidth); + yscale = FixedDiv(AutomapPic->height, mapheight); + zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); + + amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); + amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); + + if (encoremode) + amnumxpos = -amnumxpos; + + amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width/2); + y = MINI_Y - (AutomapPic->height/2); + + if (timeinmap > 105) + { + minimaptrans = cv_kartminimap.value; + if (timeinmap <= 113) + minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); + if (!minimaptrans) + return; + } + else + return; + + minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); + else + V_DrawScaledPatch(x, y, splitflags, AutomapPic); + + if (!(r_splitscreen == 2)) + { + splitflags &= ~minimaptrans; + splitflags |= V_HUDTRANSHALF; + } + + // let offsets transfer to the heads, too! + if (encoremode) + x += SHORT(AutomapPic->leftoffset); + else + x -= SHORT(AutomapPic->leftoffset); + y -= SHORT(AutomapPic->topoffset); + + // Draw the super item in Battle + if (G_BattleGametype() && battleovertime.enabled) + { + if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) + { + const INT32 prevsplitflags = splitflags; + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); + K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); + splitflags = prevsplitflags; + } + } + + // initialize + for (i = 0; i < 4; i++) + localplayers[i] = -1; + + if (G_RaceGametype()) + hyu *= 2; // double in race + + // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) + if (ghosts) + { + demoghost *g = ghosts; + while (g) + { + if (g->mo->skin) + skin = ((skin_t*)g->mo->skin)-skins; + else + skin = 0; + if (g->mo->color) + { + if (g->mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); + } + else + colormap = NULL; + K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + g = g->next; + } + + if (!stplyr->mo || stplyr->spectator || stplyr->exiting) + return; + + localplayers[numlocalplayers] = stplyr-players; + numlocalplayers++; + } + else + { + for (i = MAXPLAYERS-1; i >= 0; i--) + { + if (!playeringame[i]) + continue; + if (!players[i].mo || players[i].spectator || players[i].exiting) + continue; + + if (i != displayplayers[0] || r_splitscreen) + { + if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) + continue; + + if (players[i].kartstuff[k_hyudorotimer] > 0) + { + if (!((players[i].kartstuff[k_hyudorotimer] < TICRATE/2 + || players[i].kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)) + && !(leveltime & 1))) + continue; + } + } + + if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) + { + // Draw display players on top of everything else + localplayers[numlocalplayers] = i; + numlocalplayers++; + continue; + } + + if (players[i].mo->skin) + skin = ((skin_t*)players[i].mo->skin)-skins; + else + skin = 0; + + if (players[i].mo->color) + { + if (players[i].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + // Target reticule + if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } + } + + // draw SPB(s?) + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + if (mobj->type == MT_SPB) + { + colormap = NULL; + + if (mobj->target && !P_MobjWasRemoved(mobj->target)) + { + if (mobj->player && mobj->player->skincolor) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE); + else if (mobj->color) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); + } + + K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); + } + } + + // draw our local players here, opaque. + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + + for (i = 0; i < numlocalplayers; i++) + { + if (i == -1) + continue; // this doesn't interest us + + if (players[localplayers[i]].mo->skin) + skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; + else + skin = 0; + + if (players[localplayers[i]].mo->color) + { + if (players[localplayers[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + + // Target reticule + if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } +} + +static void K_drawKartStartCountdown(void) +{ + INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3 + + if (leveltime >= starttime-(2*TICRATE)) // 2 + pnum++; + if (leveltime >= starttime-TICRATE) // 1 + pnum++; + if (leveltime >= starttime) // GO! + pnum++; + if ((leveltime % (2*5)) / 5) // blink + pnum += 4; + if (r_splitscreen) // splitscreen + pnum += 8; + + V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); +} + +static void K_drawKartFinish(void) +{ + INT32 pnum = 0, splitflags = K_calcSplitFlags(0); + + if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE) + return; + + if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink + pnum = 1; + + if (r_splitscreen > 1) // 3/4p, stationary FIN + { + pnum += 2; + V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]); + return; + } + + //else -- 1/2p, scrolling FINISH + { + INT32 x, xval; + + if (r_splitscreen) // wide splitscreen + pnum += 4; + + x = ((vid.width<width)<karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; + + if (r_splitscreen && stplyr == &players[displayplayers[1]]) + x = -x; + + V_DrawFixedPatch(x + (STCD_X<>1), + (STCD_Y<height)<<(FRACBITS-1)), + FRACUNIT, + splitflags, kp_racefinish[pnum], NULL); + } +} + +static void K_drawBattleFullscreen(void) +{ + INT32 x = BASEVIDWIDTH/2; + INT32 y = -64+(stplyr->karthud[khud_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen + INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead + fixed_t scale = FRACUNIT; + boolean drawcomebacktimer = true; // lazy hack because it's cleaner in the long run. +#ifdef HAVE_BLUA + if (!LUA_HudEnabled(hud_battlecomebacktimer)) + drawcomebacktimer = false; +#endif + + if (r_splitscreen) + { + if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (r_splitscreen > 1 && (stplyr == &players[displayplayers[2]] + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)))) + { + y = 232-(stplyr->karthud[khud_cardanimation]/2); + splitflags = V_SNAPTOBOTTOM; + } + else + y = -32+(stplyr->karthud[khud_cardanimation]/2); + + if (r_splitscreen > 1) + { + scale /= 2; + + if (stplyr == &players[displayplayers[1]] + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) + x = 3*BASEVIDWIDTH/4; + else + x = BASEVIDWIDTH/4; + } + else + { + if (stplyr->exiting) + { + if (stplyr == &players[displayplayers[1]]) + x = BASEVIDWIDTH-96; + else + x = 96; + } + else + scale /= 2; + } + } + + if (stplyr->exiting) + { + if (stplyr == &players[displayplayers[0]]) + V_DrawFadeScreen(0xFF00, 16); + if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) + { + patch_t *p = kp_battlecool; + + if (K_IsPlayerLosing(stplyr)) + p = kp_battlelose; + else if (stplyr->kartstuff[k_position] == 1) + p = kp_battlewin; + + V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator && drawcomebacktimer) + { + UINT16 t = stplyr->kartstuff[k_comebacktimer]/(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; + + txoff = adjust; + + while (t) + { + txoff += adjust; + t /= 10; + } + + if (r_splitscreen) + { + if (r_splitscreen > 1) + ty = (BASEVIDHEIGHT/4)+33; + if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) + ty += (BASEVIDHEIGHT/2); + } + else + V_DrawFadeScreen(0xFF00, 16); + + if (!comebackshowninfo) + V_DrawFixedPatch(x< 1) + V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE)); + else + { + V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE)); + } + } + + if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY? + { + UINT8 i; + + // check to see if there's anyone else at all + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == displayplayers[0]) + continue; + if (playeringame[i] && !stplyr->spectator) + return; + } + +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_freeplay)) +#endif + K_drawKartFreePlay(leveltime); + } +} + +static void K_drawKartFirstPerson(void) +{ + static INT32 pnum[4], turn[4], drift[4]; + INT32 pn = 0, tn = 0, dr = 0; + INT32 target = 0, splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); + INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT; + fixed_t scale; + UINT8 *colmap = NULL; + ticcmd_t *cmd = &stplyr->cmd; + + if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) + return; + + if (stplyr == &players[displayplayers[1]] && r_splitscreen) + { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } + else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } + else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) + { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } + else + { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } + + if (r_splitscreen) + { + y >>= 1; + if (r_splitscreen > 1) + x >>= 1; + } + + { + if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) + y++; + + if (stplyr->mo->drawflags & MFD_TRANSMASK) + splitflags |= ((stplyr->mo->drawflags & MFD_TRANSMASK) >> MFD_TRANSSHIFT) << FF_TRANSSHIFT; + else if (stplyr->mo->frame & FF_TRANSMASK) + splitflags |= (stplyr->mo->frame & FF_TRANSMASK); + } + + if (cmd->driftturn > 400) // strong left turn + target = 2; + else if (cmd->driftturn < -400) // strong right turn + target = -2; + else if (cmd->driftturn > 0) // weak left turn + target = 1; + else if (cmd->driftturn < 0) // weak right turn + target = -1; + else // forward + target = 0; + + if (encoremode) + target = -target; + + if (pn < target) + pn++; + else if (pn > target) + pn--; + + if (pn < 0) + splitflags |= V_FLIP; // right turn + + target = abs(pn); + if (target > 2) + target = 2; + + x <<= FRACBITS; + y <<= FRACBITS; + + if (tn != cmd->driftturn/50) + tn -= (tn - (cmd->driftturn/50))/8; + + if (dr != stplyr->kartstuff[k_drift]*16) + dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8; + + if (r_splitscreen == 1) + { + scale = (2*FRACUNIT)/3; + y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view) + } + else if (r_splitscreen) + scale = FRACUNIT/2; + else + scale = FRACUNIT; + + if (stplyr->mo) + { + UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->kartstuff[k_driftcharge]); + const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; + // yes, the following is correct. no, you do not need to swap the x and y. + fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); + fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); + + if (r_splitscreen) + xoffs = FixedMul(xoffs, scale); + + xoffs -= (tn)*scale; + xoffs -= (dr)*scale; + + if (stplyr->frameangle == stplyr->mo->angle) + { + const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); + + if (mag < FRACUNIT) + { + xoffs = FixedMul(xoffs, mag); + if (!r_splitscreen) + yoffs = FixedMul(yoffs, mag); + } + } + + if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! + yoffs += stplyr->mo->momz/3; + + if (encoremode) + x -= xoffs; + else + x += xoffs; + if (!r_splitscreen) + y += yoffs; + + + if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks! + colmap = R_GetTranslationColormap(TC_RAINBOW, driftcolor, GTC_CACHE); + else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink! + colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE); + } + + V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap); + + if (stplyr == &players[displayplayers[1]] && r_splitscreen) + { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } + else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } + else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) + { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } + else + { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } +} + +// doesn't need to ever support 4p +static void K_drawInput(void) +{ + static INT32 pn = 0; + INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT); + INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col; + const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5]; + const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9]; + ticcmd_t *cmd = &stplyr->cmd; + + if (timeinmap <= 105) + return; + + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 105); + offs = 64; + while (count-- > 0) + offs >>= 1; + x += offs; + } + +#define BUTTW 8 +#define BUTTH 11 + +#define drawbutt(xoffs, butt, symb)\ + if (stplyr->cmd.buttons & butt)\ + {\ + offs = 2;\ + col = accent1;\ + }\ + else\ + {\ + offs = 0;\ + col = accent2;\ + V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\ + }\ + V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\ + V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn + target = 0; + else // turning of multiple strengths! + { + target = ((abs(cmd->driftturn) - 1)/125)+1; + if (target > 4) + target = 4; + if (cmd->driftturn < 0) + target = -target; + } + + if (pn != target) + { + if (abs(pn - target) == 1) + pn = target; + else if (pn < target) + pn += 2; + else //if (pn > target) + pn -= 2; + } + + if (pn < 0) + { + splitflags |= V_FLIP; // right turn + x--; + } + + target = abs(pn); + if (target > 4) + target = 4; + + if (!stplyr->skincolor) + V_DrawFixedPatch(x<skincolor, GTC_CACHE); + V_DrawFixedPatch(x<karthud[khud_lapanimation]; + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); + + V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, + (48 - (32*max(0, progress-76)))*FRACUNIT, + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap); + + if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3) + { + V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, + (48 - (32*max(0, progress-76)) + + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT, + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); + } + + if (stplyr->laps == (UINT8)(cv_numlaps.value)) + { + V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_final[min(progress/2, 10)], NULL); + + if (progress/2-12 >= 0) + { + V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_lap[min(progress/2-12, 6)], NULL); + } + } + else + { + V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_lap[min(progress/2, 6)], NULL); + + if (progress/2-8 >= 0) + { + V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_number[(((UINT32)stplyr->laps) / 10)][min(progress/2-8, 2)], NULL); + + if (progress/2-10 >= 0) + { + V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_number[(((UINT32)stplyr->laps) % 10)][min(progress/2-10, 2)], NULL); + } + } + } +} + +void K_drawKartFreePlay(UINT32 flashtime) +{ + // no splitscreen support because it's not FREE PLAY if you have more than one player in-game + // (you fool, you can take splitscreen online. :V) + + if ((flashtime % TICRATE) < TICRATE/2) + return; + + V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy + LAPS_Y+3, V_HUDTRANS|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); +} + +static void +Draw_party_ping (int ss, INT32 snap) +{ + HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|snap); +} + +static void +K_drawMiniPing (void) +{ + if (cv_showping.value) + { + switch (r_splitscreen) + { + case 3: + Draw_party_ping(3, V_SNAPTORIGHT|V_SPLITSCREEN); + /*FALLTHRU*/ + case 2: + Draw_party_ping(2, V_SPLITSCREEN); + Draw_party_ping(1, V_SNAPTORIGHT); + Draw_party_ping(0, 0); + break; + case 1: + Draw_party_ping(1, V_SNAPTORIGHT|V_SPLITSCREEN); + Draw_party_ping(0, V_SNAPTORIGHT); + break; + } + } +} + +static void K_drawDistributionDebugger(void) +{ + patch_t *items[NUMKARTRESULTS] = { + kp_sadface[1], + kp_sneaker[1], + kp_rocketsneaker[1], + kp_invincibility[7], + kp_banana[1], + kp_eggman[1], + kp_orbinaut[4], + kp_jawz[1], + kp_mine[1], + kp_ballhog[1], + kp_selfpropelledbomb[1], + kp_grow[1], + kp_shrink[1], + kp_thundershield[1], + kp_bubbleshield[1], + kp_flameshield[1], + kp_hyudoro[1], + kp_pogospring[1], + kp_superring[1], + kp_kitchensink[1], + + kp_sneaker[1], + kp_banana[1], + kp_banana[1], + kp_orbinaut[4], + kp_orbinaut[4], + kp_jawz[1] + }; + UINT8 useodds = 0; + UINT8 pingame = 0, bestbumper = 0; + UINT32 pdis = 0; + INT32 i; + INT32 x = -9, y = -9; + boolean spbrush = false; + + if (stplyr != &players[displayplayers[0]]) // only for p1 + return; + + // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + pingame++; + if (players[i].kartstuff[k_bumper] > bestbumper) + bestbumper = players[i].kartstuff[k_bumper]; + } + + // lovely double loop...... + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator + && players[i].kartstuff[k_position] == 1) + { + // This player is first! Yay! + pdis = stplyr->distancetofinish - players[i].distancetofinish; + break; + } + } + + if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items + pdis = (15 * pdis) / 14; + + if (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell + { + pdis = (3 * pdis) / 2; + spbrush = true; + } + + if (stplyr->bot && stplyr->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count + + useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); + + for (i = 1; i < NUMKARTRESULTS; i++) + { + const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); + if (itemodds <= 0) + continue; + + V_DrawScaledPatch(x, y, V_HUDTRANS|V_SNAPTOTOP, items[i]); + V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SNAPTOTOP, va("%d", itemodds)); + + // Display amount for multi-items + if (i >= NUMKARTITEMS) + { + INT32 amount; + switch (i) + { + case KRITEM_TENFOLDBANANA: + amount = 10; + break; + case KRITEM_QUADORBINAUT: + amount = 4; + break; + case KRITEM_DUALJAWZ: + amount = 2; + break; + default: + amount = 3; + break; + } + V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SNAPTOTOP, va("x%d", amount)); + } + + x += 32; + if (x >= 297) + { + x = -9; + y += 32; + } + } + + V_DrawString(0, 0, V_HUDTRANS|V_SNAPTOTOP, va("USEODDS %d", useodds)); +} + +static void K_drawCheckpointDebugger(void) +{ + if (stplyr != &players[displayplayers[0]]) // only for p1 + return; + + if (stplyr->starpostnum == numstarposts) + V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts)); + else + V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts)); +} + +static void K_DrawWaypointDebugger(void) +{ + if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) + { + V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); + V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); + } +} + +void K_drawKartHUD(void) +{ + boolean isfreeplay = false; + boolean battlefullscreen = false; + boolean freecam = demo.freecam; //disable some hud elements w/ freecam + UINT8 i; + + // Define the X and Y for each drawn object + // This is handled by console/menu values + K_initKartHUD(); + + // Draw that fun first person HUD! Drawn ASAP so it looks more "real". + for (i = 0; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]] && !camera[i].chase && !freecam) + K_drawKartFirstPerson(); + } + + // Draw full screen stuff that turns off the rest of the HUD + if (mapreset && stplyr == &players[displayplayers[0]]) + { + K_drawChallengerScreen(); + return; + } + + battlefullscreen = ((G_BattleGametype()) + && (stplyr->exiting + || (stplyr->kartstuff[k_bumper] <= 0 + && stplyr->kartstuff[k_comebacktimer] + && comeback + && stplyr->playerstate == PST_LIVE))); + + if (!demo.title && (!battlefullscreen || r_splitscreen)) + { + // Draw the CHECK indicator before the other items, so it's overlapped by everything else +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_check)) // delete lua when? +#endif + if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam) + K_drawKartPlayerCheck(); + + // nametags +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_names)) +#endif + K_drawKartNameTags(); + + // Draw WANTED status + if (G_BattleGametype()) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_wanted)) +#endif + K_drawKartWanted(); + } + + if (cv_kartminimap.value) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_minimap)) +#endif + K_drawKartMinimap(); + } + } + + if (battlefullscreen && !freecam) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_battlefullscreen)) +#endif + K_drawBattleFullscreen(); + return; + } + + // Draw the item window +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_item) && !freecam) +#endif + K_drawKartItem(); + + // If not splitscreen, draw... + if (!r_splitscreen && !demo.title) + { + // Draw the timestamp +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_time)) +#endif + K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0); + + if (!modeattacking) + { + // The top-four faces on the left + /*#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_minirankings)) + #endif*/ + isfreeplay = K_drawKartPositionFaces(); + } + } + + if (!stplyr->spectator && !demo.freecam) // Bottom of the screen elements, don't need in spectate mode + { + // Draw the speedometer + if (cv_kartspeedometer.value && !r_splitscreen) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_speedometer)) +#endif + K_drawKartSpeedometer(); + } + + if (demo.title) // Draw title logo instead in demo.titles + { + INT32 x = BASEVIDWIDTH - 32, y = 128, offs; + + if (r_splitscreen == 3) + { + x = BASEVIDWIDTH/2 + 10; + y = BASEVIDHEIGHT/2 - 30; + } + + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 104); + offs = 256; + while (count-- > 0) + offs >>= 1; + x += offs; + } + + V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); + V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); + } + else if (G_RaceGametype()) // Race-only elements + { + // Draw the lap counter +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_gametypeinfo)) +#endif + K_drawKartLapsAndRings(); + + if (isfreeplay) + ; + else if (!modeattacking) + { + // Draw the numerical position +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_position)) +#endif + K_DrawKartPositionNum(stplyr->kartstuff[k_position]); + } + else //if (!(demo.playback && hu_showscores)) + { + // Draw the input UI +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_position)) +#endif + K_drawInput(); + } + } + else if (G_BattleGametype()) // Battle-only + { + // Draw the hits left! +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_gametypeinfo)) +#endif + K_drawKartBumpersOrKarma(); + } + } + + // Draw the countdowns after everything else. + if (leveltime >= starttime-(3*TICRATE) + && leveltime < starttime+TICRATE) + K_drawKartStartCountdown(); + else if (racecountdown && (!r_splitscreen || !stplyr->exiting)) + { + char *countstr = va("%d", racecountdown/TICRATE); + + if (r_splitscreen > 1) + V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, K_calcSplitFlags(0), countstr); + else + { + INT32 karlen = strlen(countstr)*6; // half of 12 + V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, K_calcSplitFlags(0), countstr); + } + } + + // Race overlays + if (G_RaceGametype() && !freecam) + { + if (stplyr->exiting) + K_drawKartFinish(); + else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen) + K_drawLapStartAnim(); + } + + if (modeattacking || freecam) // everything after here is MP and debug only + return; + + if (G_BattleGametype() && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * + V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); + + // Draw FREE PLAY. + if (isfreeplay && !stplyr->spectator && timeinmap > 113) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_freeplay)) +#endif + K_drawKartFreePlay(leveltime); + } + + if (r_splitscreen == 0 && stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) + { + V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); + } + + if (netgame && r_splitscreen) + { + K_drawMiniPing(); + } + + if (cv_kartdebugdistribution.value) + K_drawDistributionDebugger(); + + if (cv_kartdebugcheckpoint.value) + K_drawCheckpointDebugger(); + + if (cv_kartdebugnodes.value) + { + UINT8 p; + for (p = 0; p < MAXPLAYERS; p++) + V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency)); + } + + if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin) + { + INT32 x = 0, y = 0; + UINT8 c; + + for (c = 1; c < MAXSKINCOLORS; c++) + { + UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, c, GTC_CACHE); + V_DrawFixedPatch(x<>1, 0, facewantprefix[stplyr->skin], cm); + + x += 16; + if (x > BASEVIDWIDTH-16) + { + x = 0; + y += 16; + } + } + } + + K_DrawWaypointDebugger(); +} + +//} From 7b42027af8b4ee434abba8eb31796b3a70f87ebc Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 20 Jul 2020 16:02:46 -0400 Subject: [PATCH 149/211] Show nametags in replays, fix in splitscreen --- src/k_kart.c | 59 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 3a0a6048d..8c0baac04 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9853,7 +9853,8 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a { *hud_y /= 2; - if (camnum > 1) + if ((r_splitscreen == 1 && camnum == 1) + || (r_splitscreen > 1 && camnum > 1)) { *hud_y += shhalffixed; } @@ -9967,6 +9968,30 @@ static void K_drawKartPlayerCheck(void) } } +static boolean K_ShowPlayerNametag(player_t *p) +{ + if (demo.playback == true && demo.freecam == true) + { + return true; + } + + if (stplyr == p) + { + return false; + } + + if (G_RaceGametype()) + { + if ((p->kartstuff[k_position] < stplyr->kartstuff[k_position]-2) + || (p->kartstuff[k_position] > stplyr->kartstuff[k_position]+2)) + { + return false; + } + } + + return true; +} + static void K_drawKartNameTags(void) { const fixed_t maxdistance = 8192*mapobjectscale; @@ -10012,7 +10037,6 @@ static void K_drawKartNameTags(void) fixed_t y = -BASEVIDWIDTH * FRACUNIT; vertex_t v; - UINT8 j; if (!playeringame[i] || ntplayer->spectator) { @@ -10032,18 +10056,24 @@ static void K_drawKartNameTags(void) continue; } - for (j = 0; j <= r_splitscreen; j++) + if (!(demo.playback == true && demo.freecam == true)) { - if (ntplayer == &players[displayplayers[j]]) - { - break; - } - } + UINT8 j; - if (j <= r_splitscreen) - { - // Is a player that's being shown on this computer - continue; + for (j = 0; j <= r_splitscreen; j++) + { + if (ntplayer == &players[displayplayers[j]]) + { + break; + } + } + + if (j <= r_splitscreen) + { + // This is a player that's being shown on this computer + // (Remove whenever we get splitscreen ABCD indicators) + continue; + } } v.x = ntplayer->mo->x; @@ -10079,10 +10109,9 @@ static void K_drawKartNameTags(void) V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); } } - else if (netgame) + else if (netgame || demo.playback) { - if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-2) - && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+2)) + if (K_ShowPlayerNametag(ntplayer) == true) { INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); From 089cffd900c7a9fcf1cdccad5416ddc8e60d3f42 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 22 Jul 2020 18:21:50 -0400 Subject: [PATCH 150/211] Recovery stat has risen from its grave Boost stacking potential is increased for low weight. Also changed order of operations so that flashing tics differences are more significant in Battle. --- src/k_kart.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 8c0baac04..748ae4446 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2025,9 +2025,18 @@ static fixed_t K_FlameShieldDashVar(INT32 val) return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); } +// GAME OVER +// RETURN OF THE RECOVERY STAT FROM KARTZ +#define RETURN_OF_RECOVERY + // sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be static void K_GetKartBoostPower(player_t *player) { +#ifdef RETURN_OF_RECOVERY + const fixed_t maxrecoveryincrease = FRACUNIT/2; + const fixed_t recovery = FRACUNIT - ((9-player->kartweight) * maxrecoveryincrease / 8); +#endif // RETURN_OF_RECOVERY + fixed_t boostpower = FRACUNIT; fixed_t speedboost = 0, accelboost = 0; UINT8 numboosts = 0; @@ -2045,12 +2054,24 @@ static void K_GetKartBoostPower(player_t *player) if (player->kartstuff[k_bananadrag] > TICRATE) boostpower = (4*boostpower)/5; +#ifdef RETURN_OF_RECOVERY + #define ADDBOOST(s,a) { \ numboosts++; \ - speedboost += (s) / numboosts; \ - accelboost += (a) / numboosts; \ + speedboost += FixedDiv(s, FRACUNIT + (recovery * numboosts-1)); \ + accelboost += FixedDiv(a, FRACUNIT + (recovery * numboosts-1)); \ } +#else + +#define ADDBOOST(s,a) { \ + numboosts++; \ + speedboost += s / numboosts; \ + accelboost += a / numboosts; \ +} + +#endif // RETURN_OF_RECOVERY + if (player->kartstuff[k_sneakertimer]) // Sneaker { UINT8 i; @@ -2195,11 +2216,11 @@ UINT16 K_GetKartFlashing(player_t *player) if (!player) return tics; + tics += (tics/8) * (player->kartspeed); + if (G_BattleGametype()) tics *= 2; - tics += (flashingtics/8) * (player->kartspeed); - return tics; } From c942390745fabf9874f13ce15a39e8e98e2723c9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 22 Jul 2020 18:33:48 -0400 Subject: [PATCH 151/211] Rename to metabolism because it's funnier & more accurate to what it does :XD: --- src/k_kart.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 748ae4446..188006876 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2025,17 +2025,16 @@ static fixed_t K_FlameShieldDashVar(INT32 val) return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); } -// GAME OVER -// RETURN OF THE RECOVERY STAT FROM KARTZ -#define RETURN_OF_RECOVERY +// Light weights have stronger boost stacking -- aka, better metabolism than heavies XD +#define METABOLISM // sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be static void K_GetKartBoostPower(player_t *player) { -#ifdef RETURN_OF_RECOVERY - const fixed_t maxrecoveryincrease = FRACUNIT/2; - const fixed_t recovery = FRACUNIT - ((9-player->kartweight) * maxrecoveryincrease / 8); -#endif // RETURN_OF_RECOVERY +#ifdef METABOLISM + const fixed_t maxmetabolismincrease = FRACUNIT/2; + const fixed_t metabolism = FRACUNIT - ((9-player->kartweight) * maxmetabolismincrease / 8); +#endif // METABOLISM fixed_t boostpower = FRACUNIT; fixed_t speedboost = 0, accelboost = 0; @@ -2054,12 +2053,12 @@ static void K_GetKartBoostPower(player_t *player) if (player->kartstuff[k_bananadrag] > TICRATE) boostpower = (4*boostpower)/5; -#ifdef RETURN_OF_RECOVERY +#ifdef METABOLISM #define ADDBOOST(s,a) { \ numboosts++; \ - speedboost += FixedDiv(s, FRACUNIT + (recovery * numboosts-1)); \ - accelboost += FixedDiv(a, FRACUNIT + (recovery * numboosts-1)); \ + speedboost += FixedDiv(s, FRACUNIT + (metabolism * numboosts-1)); \ + accelboost += FixedDiv(a, FRACUNIT + (metabolism * numboosts-1)); \ } #else @@ -2070,7 +2069,7 @@ static void K_GetKartBoostPower(player_t *player) accelboost += a / numboosts; \ } -#endif // RETURN_OF_RECOVERY +#endif // METABOLISM if (player->kartstuff[k_sneakertimer]) // Sneaker { From 262c84f6cca8622b9600a551020ddd0a584536cb Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 22 Jul 2020 17:29:38 -0700 Subject: [PATCH 152/211] Add player z momentum to the clip if going upward --- src/k_kart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 8c0baac04..8908cb724 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1685,6 +1685,9 @@ void K_SpawnDriftBoostClip(player_t *player) clip->fuse = 105; clip->momz = 7 * P_MobjFlip(clip) * clip->scale; + if (player->mo->momz > 0) + clip->momz += player->mo->momz; + P_InstaThrust(clip, player->mo->angle + K_RandomFlip(P_RandomRange(FRACUNIT/2, FRACUNIT)), FixedMul(scale, player->speed)); From 34a7c2ee88c98cd1c76b9d3ea91e305c16aaced7 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 22 Jul 2020 19:09:14 -0700 Subject: [PATCH 153/211] Hammer cocks on rainbow sparks; hella bullet clips on rainbow boost + new sfx --- src/k_kart.c | 3 +++ src/p_mobj.c | 18 ++++++++++++++++-- src/sounds.c | 1 + src/sounds.h | 1 + 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 8908cb724..5197f6d79 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3169,6 +3169,7 @@ static void K_SpawnDriftSparks(player_t *player) { // transition P_SetScale(spark, (spark->destscale = spark->scale*3/2)); + S_StartSound(player->mo, sfx_cock); } else { @@ -6428,6 +6429,8 @@ static void K_SpawnDriftBoostExplosion(player_t *player, int stage) S_StartSound(player->mo, sfx_s3kc4l); break; } + + overlay->extravalue1 = stage; } static void K_KartDrift(player_t *player, boolean onground) diff --git a/src/p_mobj.c b/src/p_mobj.c index fb4196548..041f9ea0f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8347,8 +8347,22 @@ void P_MobjThinker(mobj_t *mobj) else if (mobj->fuse > 32) mobj->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - if (mobj->fuse == 17 || mobj->fuse == 33)/* to red/blue */ - K_SpawnDriftBoostClip(mobj->target->player); + switch (mobj->extravalue1) + { + case 3:/* rainbow boost */ + /* every 20 tics, bang! */ + if (( 120 - mobj->fuse ) % 10 == 0) + { + K_SpawnDriftBoostClip(mobj->target->player); + S_StartSound(mobj->target, sfx_s3k77); + } + break; + + case 2:/* blue boost */ + if (mobj->fuse == 16)/* to red*/ + K_SpawnDriftBoostClip(mobj->target->player); + break; + } { player_t *p = NULL; diff --git a/src/sounds.c b/src/sounds.c index b64dda5cb..5581ade45 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -820,6 +820,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"sploss", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Down to yellow sparks {"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree: {"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification + {"cock", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Hammer cocks, bang bang // SRB2Kart - Engine sounds // Engine class A diff --git a/src/sounds.h b/src/sounds.h index 3d6e4f007..d9b303044 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -897,6 +897,7 @@ typedef enum sfx_sploss, sfx_itfree, sfx_dbgsal, + sfx_cock, // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Engine class A - Low Speed, Low Weight From d70e8ac919025b69fcec08093629bf85bfb813a0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 22 Jul 2020 22:56:45 -0400 Subject: [PATCH 154/211] Less bubbles --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5197f6d79..ada35edca 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -286,7 +286,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink /*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield - /*Bubble Shield*/ { 0, 2, 3, 3, 1, 0, 0, 0 }, // Bubble Shield + /*Bubble Shield*/ { 0, 1, 2, 2, 0, 0, 0, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield /*Hyudoro*/ { 0, 0, 0, 1, 2, 0, 0, 0 }, // Hyudoro /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring From 3800603243a9183758e7420ae15f938b1a1a80ec Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 22 Jul 2020 23:49:24 -0400 Subject: [PATCH 155/211] Bruhther moment --- src/p_saveg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index 5239837b4..82affdd4b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2271,6 +2271,8 @@ static void LoadMobjThinker(actionf_p1 thinker) mobj->colorized = READUINT8(save_p); if (diff2 & MD2_SHADOWSCALE) mobj->shadowscale = READFIXED(save_p); + if (diff2 & MD2_DRAWFLAGS) + mobj->drawflags = READUINT16(save_p); if (diff & MD_REDFLAG) { From 4a9a33130ac84bf7f1b360b4f0b92c9ad42dab6b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Jul 2020 19:23:27 -0400 Subject: [PATCH 156/211] Make it compile --- src/k_kart.c | 6 +++--- src/p_slopes.c | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index e8160ac51..1dd48083d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6829,17 +6829,17 @@ boolean K_PlayerEBrake(player_t *player) && leveltime > starttime; } -tic_t K_GetSpindashChargeTime(player_t *player) +static tic_t K_GetSpindashChargeTime(player_t *player) { return (player->kartspeed + 4)*TICRATE/3; // more charge time for higher speed: Tails = 2s, Mighty = 3s, Fang = 4s } -fixed_t K_GetSpindashChargeSpeed(player_t *player) +static fixed_t K_GetSpindashChargeSpeed(player_t *player) { return FixedMul(FRACUNIT + (player->kartweight - 5)*FRACUNIT/12, K_GetKartSpeed(player, false)); // more speed for higher weight: Tails = 75%, Fang = 100%, Mighty = 125% } -void K_KartSpindash(player_t *player) +static void K_KartSpindash(player_t *player) { ticcmd_t *cmd = &player->cmd; diff --git a/src/p_slopes.c b/src/p_slopes.c index 0296ef38f..6c44d2d9e 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -22,6 +22,7 @@ #include "r_main.h" #include "p_maputl.h" #include "w_wad.h" +#include "k_kart.h" // K_PlayerEBrake #ifdef ESLOPE From 8c5e725a8d81fb8b9ff0ead166d5838df67ae4a1 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 23 Jul 2020 20:12:35 -0400 Subject: [PATCH 157/211] Also lets do this for kitchen sinks They also ignored in K_DropHnextList --- src/k_kart.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5c4a00757..a53372330 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3877,7 +3877,7 @@ void K_DropRocketSneaker(player_t *player) mobj_t *shoe = player->mo; fixed_t flingangle; - boolean leftshoe = true; //left shoie is first + boolean leftshoe = true; //left shoe is first while ((shoe = shoe->hnext) && !P_MobjWasRemoved(shoe)) { if (shoe->type != MT_ROCKETSNEAKER) @@ -3912,6 +3912,19 @@ void K_DropRocketSneaker(player_t *player) player->kartstuff[k_rocketsneakertimer] = 0; } +void K_DropKitchenSink(player_t *player) +{ + if (!(player->mo && !P_MobjWasRemoved(player->mo) && player->mo->hnext && !P_MobjWasRemoved(player->mo->hnext))) + return; + + if (player->mo->hnext->type != MT_SINK_SHIELD) + return; //so we can just call this function regardless of what is being held + + P_KillMobj(player->mo->hnext, NULL, NULL); + + P_SetTarget(&player->mo->hnext, NULL); +} + // When an item in the hnext chain dies. void K_RepairOrbitChain(mobj_t *orbit) { @@ -5237,12 +5250,12 @@ void K_KartUpdatePosition(player_t *player) // void K_StripItems(player_t *player) { + K_DropRocketSneaker(player); + K_DropKitchenSink(player); player->kartstuff[k_itemtype] = KITEM_NONE; player->kartstuff[k_itemamount] = 0; player->kartstuff[k_itemheld] = 0; - K_DropRocketSneaker(player); - if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2) { player->kartstuff[k_itemroulette] = 0; From 96e098b451001e5a9423e85721338145d1c72ea8 Mon Sep 17 00:00:00 2001 From: Ashnal Date: Thu, 23 Jul 2020 21:05:00 -0400 Subject: [PATCH 158/211] Appease C91 --- src/k_kart.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index a53372330..01531a8b0 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3872,12 +3872,13 @@ void K_DropItems(player_t *player) void K_DropRocketSneaker(player_t *player) { - if (!(player->mo && !P_MobjWasRemoved(player->mo) && player->mo->hnext && !P_MobjWasRemoved(player->mo->hnext))) - return; - mobj_t *shoe = player->mo; fixed_t flingangle; boolean leftshoe = true; //left shoe is first + + if (!(player->mo && !P_MobjWasRemoved(player->mo) && player->mo->hnext && !P_MobjWasRemoved(player->mo->hnext))) + return; + while ((shoe = shoe->hnext) && !P_MobjWasRemoved(shoe)) { if (shoe->type != MT_ROCKETSNEAKER) From db230e4d3d7da64259aff69449383ed8fa7e899c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 23 Jul 2020 21:31:10 -0400 Subject: [PATCH 159/211] Add spindash launch, prevent charging while flashing Used a timed buff instead of thrust due to friction issues, as a result I had to rebalance some values --- src/d_player.h | 4 ++- src/dehacked.c | 2 ++ src/k_kart.c | 66 +++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 786c516fb..a133cbadc 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -287,7 +287,9 @@ typedef enum k_jmp, // In Mario Kart, letting go of the jump button stops the drift k_offroad, // In Super Mario Kart, going offroad has lee-way of about 1 second before you start losing speed k_pogospring, // Pogo spring bounce effect - k_spindash, // Spindash charge + k_spindash, // Spindash charge timer + k_spindashspeed, // Spindash release speed + k_spindashboost, // Spindash release boost timer k_waterskip, // Water skipping counter k_dashpadcooldown, // Separate the vanilla SA-style dash pads from using pw_flashing k_numboosts, // Count of how many boosts are being stacked, for after image spawning diff --git a/src/dehacked.c b/src/dehacked.c index e3be7d574..b2af81cfb 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8838,6 +8838,8 @@ static const char *const KARTSTUFF_LIST[] = { "OFFROAD", "POGOSPRING", "SPINDASH", + "SPINDASHSPEED", + "SPINDASHBOOST", "WATERSKIP", "DASHPADCOOLDOWN", "NUMBOOSTS", diff --git a/src/k_kart.c b/src/k_kart.c index 1dd48083d..5ce8558ec 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2028,6 +2028,16 @@ static fixed_t K_FlameShieldDashVar(INT32 val) return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); } +static tic_t K_GetSpindashChargeTime(player_t *player) +{ + return (player->kartspeed + 4) * (TICRATE/3); // more charge time for higher speed: Tails = 2s, Mighty = 3s, Fang = 4s +} + +static fixed_t K_GetSpindashChargeSpeed(player_t *player) +{ + return player->kartweight * (FRACUNIT/12); // more speed for higher weight: Tails = 116%, Fang = 142%, Mighty = 166% +} + // Light weights have stronger boost stacking -- aka, better metabolism than heavies XD #define METABOLISM @@ -2093,6 +2103,16 @@ static void K_GetKartBoostPower(player_t *player) ADDBOOST(K_FlameShieldDashVar(player->kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration } + if (player->kartstuff[k_spindashboost]) // Spindash boost + { + const fixed_t MAXCHARGESPEED = K_GetSpindashChargeSpeed(player); + + ADDBOOST( + FixedMul(player->kartstuff[k_spindashspeed], MAXCHARGESPEED), + (8*FRACUNIT) + (24*player->kartstuff[k_spindashspeed]) + ); // character & charge time dependent + } + if (player->kartstuff[k_startboost]) // Startup Boost { ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration @@ -2267,7 +2287,7 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove // 0 with no gas, and // -25 when only braking. - if (player->kartstuff[k_sneakertimer]) + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_spindashboost]) forwardmove = 50; finalspeed *= forwardmove/25; @@ -5701,6 +5721,17 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_startboost]) player->kartstuff[k_startboost]--; + if (player->kartstuff[k_spindashboost]) + { + player->kartstuff[k_spindashboost]--; + + if (player->kartstuff[k_spindashboost] <= 0) + { + player->kartstuff[k_spindashspeed] = FRACUNIT; + player->kartstuff[k_spindashboost] = 0; + } + } + if (player->kartstuff[k_invincibilitytimer]) player->kartstuff[k_invincibilitytimer]--; @@ -6825,20 +6856,11 @@ boolean K_PlayerEBrake(player_t *player) && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_boostcharge] && !(player->kartstuff[k_spindash] < 0) + && !player->kartstuff[k_spindashboost] && !player->powers[pw_nocontrol] && leveltime > starttime; } -static tic_t K_GetSpindashChargeTime(player_t *player) -{ - return (player->kartspeed + 4)*TICRATE/3; // more charge time for higher speed: Tails = 2s, Mighty = 3s, Fang = 4s -} - -static fixed_t K_GetSpindashChargeSpeed(player_t *player) -{ - return FixedMul(FRACUNIT + (player->kartweight - 5)*FRACUNIT/12, K_GetKartSpeed(player, false)); // more speed for higher weight: Tails = 75%, Fang = 100%, Mighty = 125% -} - static void K_KartSpindash(player_t *player) { ticcmd_t *cmd = &player->cmd; @@ -6850,10 +6872,9 @@ static void K_KartSpindash(player_t *player) return; } - if (player->speed < 6*mapobjectscale) + if (player->speed < 6*mapobjectscale && player->powers[pw_flashing] == 0) { const tic_t MAXCHARGETIME = K_GetSpindashChargeTime(player); - const fixed_t MAXCHARGESPEED = K_GetSpindashChargeSpeed(player); if (cmd->driftturn != 0 && leveltime % 8 == 0) S_StartSound(player->mo, sfx_ruburn); @@ -6885,7 +6906,24 @@ static void K_KartSpindash(player_t *player) } else if (player->kartstuff[k_spindash]) { - fixed_t speed = player->kartstuff[k_spindash]*MAXCHARGESPEED/MAXCHARGETIME; + player->kartstuff[k_spindashspeed] = (player->kartstuff[k_spindash] * FRACUNIT) / MAXCHARGETIME; + player->kartstuff[k_spindashboost] = TICRATE; + + if (!player->kartstuff[k_tiregrease]) + { + UINT8 i; + for (i = 0; i < 2; i++) + { + 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->extravalue1 = i; + } + } + + player->kartstuff[k_tiregrease] = 2*TICRATE; + player->kartstuff[k_spindash] = 0; S_StartSound(player->mo, sfx_s23c); } From e6cc99c8c811d7e057746a80c0929c4e282e489a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 02:07:06 -0400 Subject: [PATCH 160/211] Rebalanced values, so speed gets better boosts too Also, prevent spindash timer from being stored when mid-air --- src/k_kart.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 5ce8558ec..0b552d42b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2030,12 +2030,17 @@ static fixed_t K_FlameShieldDashVar(INT32 val) static tic_t K_GetSpindashChargeTime(player_t *player) { - return (player->kartspeed + 4) * (TICRATE/3); // more charge time for higher speed: Tails = 2s, Mighty = 3s, Fang = 4s + // more charge time for higher speed + // Tails = 2s, Mighty = 3s, Fang = 4s, Metal = 4s + return (player->kartspeed + 4) * (TICRATE/3); } static fixed_t K_GetSpindashChargeSpeed(player_t *player) { - return player->kartweight * (FRACUNIT/12); // more speed for higher weight: Tails = 116%, Fang = 142%, Mighty = 166% + // more speed for higher weight & speed + // Tails = +6.25%, Fang = +20.31%, Mighty = +20.31%, Metal = +25% + // (can be higher than this value when overcharged) + return (player->kartspeed + player->kartweight) * (FRACUNIT/64); } // Light weights have stronger boost stacking -- aka, better metabolism than heavies XD @@ -2070,8 +2075,8 @@ static void K_GetKartBoostPower(player_t *player) #define ADDBOOST(s,a) { \ numboosts++; \ - speedboost += FixedDiv(s, FRACUNIT + (metabolism * numboosts-1)); \ - accelboost += FixedDiv(a, FRACUNIT + (metabolism * numboosts-1)); \ + speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \ + accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \ } #else @@ -2107,10 +2112,11 @@ static void K_GetKartBoostPower(player_t *player) { const fixed_t MAXCHARGESPEED = K_GetSpindashChargeSpeed(player); + // character & charge dependent ADDBOOST( - FixedMul(player->kartstuff[k_spindashspeed], MAXCHARGESPEED), - (8*FRACUNIT) + (24*player->kartstuff[k_spindashspeed]) - ); // character & charge time dependent + FixedMul(MAXCHARGESPEED, player->kartstuff[k_spindashspeed]), // + 0 to K_GetSpindashChargeSpeed()% top speed + (4*FRACUNIT) + (36*player->kartstuff[k_spindashspeed]) // + 400% to 4000% acceleration + ); } if (player->kartstuff[k_startboost]) // Startup Boost @@ -6852,6 +6858,7 @@ static INT32 K_FlameShieldMax(player_t *player) boolean K_PlayerEBrake(player_t *player) { return (player->cmd.buttons & BT_EBRAKEMASK) == BT_EBRAKEMASK + && P_IsObjectOnGround(player->mo) && !player->kartstuff[k_drift] && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_boostcharge] @@ -6929,8 +6936,10 @@ static void K_KartSpindash(player_t *player) } } else + { if (leveltime % 4 == 0) S_StartSound(player->mo, sfx_kc2b); + } } // @@ -7677,10 +7686,10 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction); } - - K_KartSpindash(player); } + K_KartSpindash(player); + // Squishing // If a Grow player or a sector crushes you, get flattened instead of being killed. From fc89f2cc47646ab395901b422f6ec2f00fb08ff4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 02:59:47 -0400 Subject: [PATCH 161/211] Remove start boosts, let you roam, kill you for crossing the line before the start --- src/g_game.c | 16 +------ src/k_bot.c | 4 +- src/k_kart.c | 113 +++++++++++++++----------------------------------- src/p_enemy.c | 4 +- src/p_spec.c | 8 ++++ src/p_user.c | 22 ++++------ 6 files changed, 55 insertions(+), 112 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 2f362f7c4..6f4a72676 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1588,12 +1588,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->angleturn *= realtics; - // SRB2kart - no additional angle if not moving - if ((player->mo && player->speed > 0) // Moving - || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn - || (player->respawn.state != RESPAWNST_NONE) // Respawning - || (player->spectator || objectplacing)) // Not a physical player - lang += (cmd->angleturn<<16); + lang += (cmd->angleturn<<16); cmd->angleturn = (INT16)(lang >> 16); cmd->latency = modeattacking ? 0 : (leveltime & 0xFF); // Send leveltime when this tic was generated to the server for control lag calculations @@ -5176,14 +5171,7 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) G_CopyTiccmd(cmd, &oldcmd[playernum], 1); - // SRB2kart: Copy-pasted from ticcmd building, removes that crappy demo cam - if (((players[displayplayers[0]].mo && players[displayplayers[0]].speed > 0) // Moving - || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn - || (players[displayplayers[0]].respawn.state != RESPAWNST_NONE) // Respawning - || (players[displayplayers[0]].spectator || objectplacing)) // Not a physical player - && !(players[displayplayers[0]].kartstuff[k_spinouttimer] - && players[displayplayers[0]].kartstuff[k_sneakertimer])) // Spinning and boosting cancels out spinout - localangle[0] += (cmd->angleturn<<16); + localangle[0] += (cmd->angleturn<<16); if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) { diff --git a/src/k_bot.c b/src/k_bot.c index 933f6d37e..285f92142 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -700,11 +700,13 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { tic_t boosthold = starttime - TICRATE; + cmd->buttons |= BT_ACCELERATE|BT_BRAKE; + boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty); if (leveltime >= boosthold) { - cmd->buttons |= BT_ACCELERATE; + cmd->buttons |= BT_DRIFT; } return; diff --git a/src/k_kart.c b/src/k_kart.c index 0b552d42b..82eec1caa 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -27,6 +27,7 @@ #include "f_finale.h" #include "lua_hud.h" // For Lua hud checks #include "lua_hook.h" // For MobjDamage and ShouldDamage +#include "m_cheat.h" // objectplacing #include "k_waypoint.h" #include "k_bot.h" @@ -5145,8 +5146,10 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) #endif return; - if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->respawn.state == RESPAWNST_DROP)) // Startup boosts + if (player->respawn.state == RESPAWNST_DROP) // Dropdashing targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0); + else if (K_PlayerEBrake(player)) // Spindashing + targetsnd = ((cmd->buttons & BT_DRIFT) ? 12 : 0); else targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2; @@ -5733,8 +5736,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_spindashboost] <= 0) { - player->kartstuff[k_spindashspeed] = FRACUNIT; - player->kartstuff[k_spindashboost] = 0; + player->kartstuff[k_spindashspeed] = player->kartstuff[k_spindashboost] = 0; } } @@ -6385,15 +6387,37 @@ static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) { - fixed_t p_maxspeed = K_GetKartSpeed(player, false); - fixed_t p_speed = min(player->speed, (p_maxspeed * 2)); - fixed_t weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); + fixed_t p_maxspeed; + fixed_t p_speed; + fixed_t weightadjust; - if (player->spectator) + if ((player->mo == NULL || P_MobjWasRemoved(player->mo))) + { + return 0; + } + + if (player->spectator || objectplacing) { return turnvalue; } + if (leveltime < introtime) + { + return 0; + } + + // SRB2kart - no additional angle if not moving + if ((player->speed <= 0) // Not moving + && ((player->cmd.buttons & BT_EBRAKEMASK) != BT_EBRAKEMASK) // not e-braking + && (player->respawn.state == RESPAWNST_NONE)) // Not respawning + { + return 0; + } + + p_maxspeed = K_GetKartSpeed(player, false); + p_speed = min(player->speed, (p_maxspeed * 2)); + weightadjust = FixedDiv((p_maxspeed * 3) - p_speed, (p_maxspeed * 3) + (player->kartweight * FRACUNIT)); + if (K_PlayerUsesBotMovement(player)) { turnvalue = 5*turnvalue/4; // Base increase to turning @@ -6864,8 +6888,7 @@ boolean K_PlayerEBrake(player_t *player) && !player->kartstuff[k_boostcharge] && !(player->kartstuff[k_spindash] < 0) && !player->kartstuff[k_spindashboost] - && !player->powers[pw_nocontrol] - && leveltime > starttime; + && !player->powers[pw_nocontrol]; } static void K_KartSpindash(player_t *player) @@ -6990,7 +7013,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) else if (cmd->buttons & BT_ATTACK) player->pflags |= PF_ATTACKDOWN; - if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > starttime + if (player && player->mo && player->mo->health > 0 && !player->spectator && !mapreset && leveltime > introtime && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && (player->respawn.state == RESPAWNST_NONE)) { // First, the really specific, finicky items that function without the item being directly in your item slot. @@ -7711,76 +7734,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground) S_StopMusic(); // The GO! sound stops the level start ambience } } - - // Start charging once you're given the opportunity. - if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) - { - if (cmd->buttons & BT_ACCELERATE) - { - if (player->kartstuff[k_boostcharge] == 0) - player->kartstuff[k_boostcharge] = cmd->latency; - - player->kartstuff[k_boostcharge]++; - } - else - player->kartstuff[k_boostcharge] = 0; - } - - // Increase your size while charging your engine. - if (leveltime < starttime+10) - { - player->mo->scalespeed = mapobjectscale/12; - player->mo->destscale = mapobjectscale + (player->kartstuff[k_boostcharge]*131); - if (cv_kartdebugshrink.value && !modeattacking && !player->bot) - player->mo->destscale = (6*player->mo->destscale)/8; - } - - // Determine the outcome of your charge. - if (leveltime > starttime && player->kartstuff[k_boostcharge]) - { - // Not even trying? - if (player->kartstuff[k_boostcharge] < 35) - { - if (player->kartstuff[k_boostcharge] > 17) - S_StartSound(player->mo, sfx_cdfm00); // chosen instead of a conventional skid because it's more engine-like - } - // Get an instant boost! - else if (player->kartstuff[k_boostcharge] <= 50) - { - player->kartstuff[k_startboost] = (50-player->kartstuff[k_boostcharge])+20; - - if (player->kartstuff[k_boostcharge] <= 36) - { - player->kartstuff[k_startboost] = 0; - K_DoSneaker(player, 0); - player->kartstuff[k_sneakertimer] = 70; // PERFECT BOOST!! - - if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) // Let everyone hear this one - S_StartSound(player->mo, sfx_s25f); - } - else - { - K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker - if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsDisplayPlayer(player)) - { - if (player->kartstuff[k_boostcharge] <= 40) - S_StartSound(player->mo, sfx_cdfm01); // You were almost there! - else - S_StartSound(player->mo, sfx_s23c); // Nope, better luck next time. - } - } - } - // You overcharged your engine? Those things are expensive!!! - else if (player->kartstuff[k_boostcharge] > 50) - { - player->powers[pw_nocontrol] = 40; - //S_StartSound(player->mo, sfx_kc34); - S_StartSound(player->mo, sfx_s3k83); - player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse - } - - player->kartstuff[k_boostcharge] = 0; - } } void K_CheckSpectateStatus(void) diff --git a/src/p_enemy.c b/src/p_enemy.c index 06232ddf0..80189f8e7 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -808,10 +808,10 @@ void A_Look(mobj_t *actor) return; #endif - if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale))) + if (leveltime < starttime) // SRB2kart - no looking before race starts return; - if (leveltime < starttime) // SRB2kart - no looking before race starts + if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale))) return; // go into chase state diff --git a/src/p_spec.c b/src/p_spec.c index aa5d52927..6dc3bd9de 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2184,6 +2184,14 @@ static void K_HandleLapIncrement(player_t *player) { S_StartSound(player->mo, sfx_s26d); } + + if (leveltime < starttime) + { + // LATER: replace with the rotatey knockback whenever we get around to it + player->powers[pw_nocontrol] = (starttime - leveltime) + 50; + player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse + S_StartSound(player->mo, sfx_s3k83); + } } } diff --git a/src/p_user.c b/src/p_user.c index 47a613994..6a91875b1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5784,18 +5784,10 @@ static void P_MovePlayer(player_t *player) boolean add_delta = true; // Kart: store the current turn range for later use - if ((player->mo && player->speed > 0) // Moving - || (leveltime > starttime && (cmd->buttons & BT_EBRAKEMASK) == BT_EBRAKEMASK) // Rubber-burn turn - || (player->respawn.state != RESPAWNST_NONE) // Respawning - || (player->spectator || objectplacing)) // Not a physical player - { - player->lturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, KART_FULLTURN)+1; - player->rturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, -KART_FULLTURN)-1; - } else { - player->lturn_max[leveltime%MAXPREDICTTICS] = player->rturn_max[leveltime%MAXPREDICTTICS] = 0; - } + player->lturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, KART_FULLTURN)+1; + player->rturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, -KART_FULLTURN)-1; - if (leveltime >= starttime) + if (leveltime >= introtime) { // KART: Don't directly apply angleturn! It may have been either A) forged by a malicious client, or B) not be a smooth turn due to a player dropping frames. // Instead, turn the player only up to the amount they're supposed to turn accounting for latency. Allow exactly 1 extra turn unit to try to keep old replays synced. @@ -7727,7 +7719,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (timeover) angle = mo->angle + FixedAngle(camrotate*FRACUNIT); - else if (leveltime < starttime) + else if (leveltime < introtime) angle = focusangle + FixedAngle(camrotate*FRACUNIT); else if (camstill || resetcalled || player->playerstate == PST_DEAD) angle = thiscam->angle; @@ -7750,7 +7742,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } } - if (!resetcalled && (leveltime > starttime && timeover != 2) + if (!resetcalled && (leveltime >= introtime && timeover != 2) && ((thiscam == &camera[0] && t_cam_rotate != -42) || (thiscam == &camera[1] && t_cam2_rotate != -42) || (thiscam == &camera[2] && t_cam3_rotate != -42) @@ -8067,7 +8059,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } else if (player->exiting || timeover == 2) thiscam->momx = thiscam->momy = thiscam->momz = 0; - else if (leveltime < starttime) + else if (leveltime < introtime) { thiscam->momx = FixedMul(x - thiscam->x, camspeed); thiscam->momy = FixedMul(y - thiscam->y, camspeed); @@ -8871,7 +8863,7 @@ void P_PlayerThink(player_t *player) } // SRB2kart 010217 - if (leveltime < starttime) + if (leveltime < introtime) { player->powers[pw_nocontrol] = 2; } From 5c1a33f95ca24ab564c17e25d88382e699d50556 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 03:13:17 -0400 Subject: [PATCH 162/211] Increase buffer time so that high speed characters have time to get in position, change ra_timeskip to introtime --- src/g_game.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 6f4a72676..f359b28f4 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -224,8 +224,8 @@ UINT16 spacetimetics = 11*TICRATE + (TICRATE/2); UINT16 extralifetics = 4*TICRATE; // SRB2kart -tic_t introtime = 108+5; // plus 5 for white fade -tic_t starttime = 6*TICRATE + (3*TICRATE/4); +tic_t introtime = (108) + 5; // 108 for rotation, + 5 for white fade +tic_t starttime = (6*TICRATE) + (2*TICRATE); // Start countdown time, + buffer time tic_t raceexittime = 5*TICRATE + (2*TICRATE/3); tic_t battleexittime = 8*TICRATE; INT32 hyudorotime = 7*TICRATE; @@ -2332,7 +2332,7 @@ void G_Ticker(boolean run) UINT32 i; INT32 buf; ticcmd_t *cmd; - UINT32 ra_timeskip = (modeattacking && !demo.playback && leveltime < starttime - TICRATE*4) ? 0 : (starttime - TICRATE*4 - 1); + UINT32 ra_timeskip = (modeattacking && !demo.playback && leveltime < introtime) ? 0 : (introtime - 1); // starttime - TICRATE*4 is where we want RA to start when we PLAY IT, so we will loop the main thinker on RA start to get it to this point, // the reason this is done is to ensure that ghosts won't look out of synch with other map elements (objects, moving platforms...) // when we REPLAY, don't skip, let the camera spin, do its thing etc~ @@ -2409,7 +2409,7 @@ void G_Ticker(boolean run) { case GS_LEVEL: - for (; ra_timeskip < starttime - TICRATE*4; ra_timeskip++) // this looks weird but this is done to not break compability with older demos for now. + for (; ra_timeskip < introtime; ra_timeskip++) // this looks weird but this is done to not break compability with older demos for now. { if (demo.title) F_TitleDemoTicker(); From b8f2fe4bcc7162714cd212c2f1b054acf5c162fc Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Fri, 24 Jul 2020 15:04:55 +0200 Subject: [PATCH 163/211] Fix respawns in antigrav --- src/k_respawn.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/k_respawn.c b/src/k_respawn.c index 6c20c8f62..71b96e085 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -34,13 +34,22 @@ fixed_t K_RespawnOffset(player_t *player, boolean flip) if (flip == true) { - player->mo->flags2 |= MF2_OBJECTFLIP; + // Lat 24/7/20: Okay so before we even think about applying this flag, check if the sector we're in doesn't already have reverse gravity for that. + // Otherwise, we would reverse the reverse gravity and cancel it out. Yes, this is absolutely fucking dumb. + // I'm honestly not sure if this flag is even necessary anymore but we'll keep it just in case. + + if (P_GetMobjGravity(player->mo) < 0) + player->mo->flags2 |= MF2_OBJECTFLIP; + player->mo->eflags |= MFE_VERTICALFLIP; - z -= (128 * mapobjectscale) - (player->mo->height); + z -= ((128 * mapobjectscale) + (player->mo->height)); } else { - player->mo->flags2 &= ~MF2_OBJECTFLIP; + + if (P_GetMobjGravity(player->mo) > 0) + player->mo->flags2 &= ~MF2_OBJECTFLIP; // See comment above, ditto. + player->mo->eflags &= ~MFE_VERTICALFLIP; z += (128 * mapobjectscale); } @@ -75,7 +84,7 @@ static void K_RespawnAtWaypoint(player_t *player, waypoint_t *waypoint) player->respawn.pointx = waypoint->mobj->x; player->respawn.pointy = waypoint->mobj->y; player->respawn.pointz = waypoint->mobj->z; - player->respawn.flip = (waypoint->mobj->flags2 & MF2_OBJECTFLIP); + player->respawn.flip = (waypoint->mobj->flags2 & MF2_OBJECTFLIP) ? true : false; // K_RespawnOffset wants a boolean! player->respawn.pointz += K_RespawnOffset(player, player->respawn.flip); } @@ -301,7 +310,7 @@ static void K_MovePlayerToRespawnPoint(player_t *player) // Reduce by the amount we needed to get to this waypoint stepamt -= dist; - // We've reached the destination point, + // We've reached the destination point, P_UnsetThingPosition(player->mo); player->mo->x = dest.x; player->mo->y = dest.y; From fcacb092b7b49c6f8679288d6f4cea9b71f702ce Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Fri, 24 Jul 2020 15:51:59 +0200 Subject: [PATCH 164/211] Various antigrav item fixes --- src/k_kart.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 810b95e24..68fbd8daa 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2909,13 +2909,10 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT)); z = source->z; // spawn on the ground please - if (P_MobjFlip(source) < 0) - { - z = source->z+source->height - mobjinfo[type].height; - } - th = P_SpawnMobj(x, y, z, type); + K_FlipFromObject(th, source); + th->flags2 |= flags2; th->threshold = 10; @@ -3654,8 +3651,8 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (player->mo->eflags & MFE_VERTICALFLIP) { mo->z -= player->mo->height; - mo->flags2 |= MF2_OBJECTFLIP; mo->eflags |= MFE_VERTICALFLIP; + mo->flags2 |= (player->mo->flags2 & MF2_OBJECTFLIP); } mo->threshold = 10; @@ -3685,8 +3682,8 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (player->mo->eflags & MFE_VERTICALFLIP) { throwmo->z -= player->mo->height; - throwmo->flags2 |= MF2_OBJECTFLIP; throwmo->eflags |= MFE_VERTICALFLIP; + mo->flags2 |= (player->mo->flags2 & MF2_OBJECTFLIP); } throwmo->movecount = 0; // above player @@ -4296,6 +4293,7 @@ void K_DropHnextList(player_t *player, boolean keepshields) dropwork->flags |= MF_NOCLIPTHING; dropwork->flags2 = work->flags2; + dropwork->eflags = work->eflags; dropwork->floorz = work->floorz; dropwork->ceilingz = work->ceilingz; @@ -4402,6 +4400,8 @@ void K_DropItems(player_t *player) drop->threshold = player->kartstuff[k_itemtype]; drop->movecount = player->kartstuff[k_itemamount]; + K_FlipFromObject(drop, player->mo); + drop->flags |= MF_NOCLIPTHING; } @@ -4747,6 +4747,9 @@ static void K_MoveHeldObjects(player_t *player) cur->flags &= ~MF_NOCLIPTHING; + if ((player->mo->eflags & MFE_VERTICALFLIP) != (cur->eflags & MFE_VERTICALFLIP)) + K_FlipFromObject(cur, player->mo); + if (!cur->health) { cur = cur->hnext; @@ -6956,6 +6959,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD); if (mo) { + K_FlipFromObject(mo, player->mo); mo->flags |= MF_NOCLIPTHING; mo->threshold = 10; mo->movecount = 1; From 06d70c1f8e247d88449690cc5ed3bc93926d3feb Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Fri, 24 Jul 2020 16:17:07 +0200 Subject: [PATCH 165/211] Push flipcam down the nearest staircase --- src/d_netcmd.c | 63 -------------------------------------------------- src/d_player.h | 62 ++++++++++++++++++++++++------------------------- src/dehacked.c | 3 --- src/g_game.c | 15 +++++------- src/p_mobj.c | 24 ------------------- src/r_main.c | 33 -------------------------- src/r_main.h | 1 - 7 files changed, 36 insertions(+), 165 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b55441b2d..9ade70301 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -65,7 +65,6 @@ // ------ static void Got_NameAndColor(UINT8 **cp, INT32 playernum); -static void Got_WeaponPref(UINT8 **cp, INT32 playernum); static void Got_PowerLevel(UINT8 **cp, INT32 playernum); static void Got_PartyInvite(UINT8 **cp, INT32 playernum); static void Got_AcceptPartyInvite(UINT8 **cp, INT32 playernum); @@ -222,11 +221,6 @@ static void Command_KartGiveItem_f(void); // CLIENT VARIABLES // ========================================================================= -void SendWeaponPref(void); -void SendWeaponPref2(void); -void SendWeaponPref3(void); -void SendWeaponPref4(void); - static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}}; #if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) static CV_PossibleValue_t mouse2port_cons_t[] = {{0, "/dev/gpmdata"}, {1, "/dev/ttyS0"}, @@ -630,7 +624,6 @@ void D_RegisterServerCommands(void) Forceskin_cons_t[i].strvalue = NULL; } RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor); - RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref); RegisterNetXCmd(XD_POWERLEVEL, Got_PowerLevel); RegisterNetXCmd(XD_PARTYINVITE, Got_PartyInvite); RegisterNetXCmd(XD_ACCEPTPARTYINVITE, Got_AcceptPartyInvite); @@ -2100,55 +2093,6 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) SetFollower(playernum, follower); } -void SendWeaponPref(void) -{ - XBOXSTATIC UINT8 buf[1]; - - buf[0] = 0; - if (cv_flipcam.value) - buf[0] |= 1; - SendNetXCmd(XD_WEAPONPREF, buf, 1); -} - -void SendWeaponPref2(void) -{ - XBOXSTATIC UINT8 buf[1]; - - buf[0] = 0; - if (cv_flipcam2.value) - buf[0] |= 1; - SendNetXCmd2(XD_WEAPONPREF, buf, 1); -} - -void SendWeaponPref3(void) -{ - XBOXSTATIC UINT8 buf[1]; - - buf[0] = 0; - if (cv_flipcam3.value) - buf[0] |= 1; - SendNetXCmd3(XD_WEAPONPREF, buf, 1); -} - -void SendWeaponPref4(void) -{ - XBOXSTATIC UINT8 buf[1]; - - buf[0] = 0; - if (cv_flipcam4.value) - buf[0] |= 1; - SendNetXCmd4(XD_WEAPONPREF, buf, 1); -} - -static void Got_WeaponPref(UINT8 **cp,INT32 playernum) -{ - UINT8 prefs = READUINT8(*cp); - - players[playernum].pflags &= ~(PF_FLIPCAM); - if (prefs & 1) - players[playernum].pflags |= PF_FLIPCAM; -} - static void Got_PowerLevel(UINT8 **cp,INT32 playernum) { UINT16 race = (UINT16)READUINT16(*cp); @@ -2356,13 +2300,6 @@ void D_SendPlayerConfig(void) SendNameAndColor3(); if (splitscreen > 2) SendNameAndColor4(); - SendWeaponPref(); - if (splitscreen) - SendWeaponPref2(); - if (splitscreen > 1) - SendWeaponPref3(); - if (splitscreen > 2) - SendWeaponPref4(); { UINT8 buf[4]; diff --git a/src/d_player.h b/src/d_player.h index 4ae420052..8ee63ca20 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -56,72 +56,70 @@ typedef enum // typedef enum { - // Flip camera angle with gravity flip prefrence. - PF_FLIPCAM = 1, // Cheats - PF_GODMODE = 1<<1, - PF_NOCLIP = 1<<2, - PF_INVIS = 1<<3, + PF_GODMODE = 1, + PF_NOCLIP = 1<<1, + PF_INVIS = 1<<2, // True if button down last tic. - PF_ATTACKDOWN = 1<<4, - PF_USEDOWN = 1<<5, - PF_JUMPDOWN = 1<<6, - PF_WPNDOWN = 1<<7, + PF_ATTACKDOWN = 1<<3, + PF_USEDOWN = 1<<4, + PF_JUMPDOWN = 1<<5, + PF_WPNDOWN = 1<<6, // Unmoving states - PF_STASIS = 1<<8, // Player is not allowed to move - PF_JUMPSTASIS = 1<<9, // and that includes jumping. + PF_STASIS = 1<<7, // Player is not allowed to move + PF_JUMPSTASIS = 1<<8, // and that includes jumping. PF_FULLSTASIS = PF_STASIS|PF_JUMPSTASIS, // Did you get a time-over? - PF_TIMEOVER = 1<<10, + PF_TIMEOVER = 1<<9, // SRB2Kart: Spectator that wants to join - PF_WANTSTOJOIN = 1<<11, + PF_WANTSTOJOIN = 1<<10, // Character action status - PF_JUMPED = 1<<12, - PF_SPINNING = 1<<13, - PF_STARTDASH = 1<<14, - PF_THOKKED = 1<<15, + PF_JUMPED = 1<<11, + PF_SPINNING = 1<<12, + PF_STARTDASH = 1<<13, + PF_THOKKED = 1<<14, // Are you gliding? - PF_GLIDING = 1<<16, + PF_GLIDING = 1<<15, // Tails pickup! - PF_CARRIED = 1<<17, + PF_CARRIED = 1<<16, // Sliding (usually in water) like Labyrinth/Oil Ocean - PF_SLIDING = 1<<18, + PF_SLIDING = 1<<17, // Hanging on a rope - PF_ROPEHANG = 1<<19, + PF_ROPEHANG = 1<<18, // Hanging on an item of some kind - zipline, chain, etc. (->tracer) - PF_ITEMHANG = 1<<20, + PF_ITEMHANG = 1<<19, // On the mace chain spinning around (->tracer) - PF_MACESPIN = 1<<21, + PF_MACESPIN = 1<<20, /*** NIGHTS STUFF ***/ // Is the player in NiGHTS mode? - PF_NIGHTSMODE = 1<<22, - PF_TRANSFERTOCLOSEST = 1<<23, + PF_NIGHTSMODE = 1<<21, + PF_TRANSFERTOCLOSEST = 1<<22, // Spill rings after falling - PF_NIGHTSFALL = 1<<24, - PF_DRILLING = 1<<25, - PF_SKIDDOWN = 1<<26, + PF_NIGHTSFALL = 1<<23, + PF_DRILLING = 1<<24, + PF_SKIDDOWN = 1<<25, /*** TAG STUFF ***/ - PF_TAGGED = 1<<27, // Player has been tagged and awaits the next round in hide and seek. - PF_TAGIT = 1<<28, // The player is it! For Tag Mode + PF_TAGGED = 1<<26, // Player has been tagged and awaits the next round in hide and seek. + PF_TAGIT = 1<<27, // The player is it! For Tag Mode /*** misc ***/ - PF_FORCESTRAFE = 1<<29, // Turning inputs are translated into strafing inputs - PF_HITFINISHLINE = 1<<30, // Already hit the finish line this tic + PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs + PF_HITFINISHLINE = 1<<29, // Already hit the finish line this tic // free: 1<<30 and 1<<31 } pflags_t; diff --git a/src/dehacked.c b/src/dehacked.c index 372863bd5..92e80a475 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8481,9 +8481,6 @@ static const char *const MAPTHINGFLAG_LIST[4] = { #endif static const char *const PLAYERFLAG_LIST[] = { - // Flip camera angle with gravity flip prefrence. - "FLIPCAM", - // Cheats "GODMODE", "NOCLIP", diff --git a/src/g_game.c b/src/g_game.c index 1b994ede1..fed573d13 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1506,10 +1506,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // spectator aiming shit, ahhhh... { INT32 player_invert = invertmouse ? -1 : 1; - INT32 screen_invert = - (player->mo && (player->mo->eflags & MFE_VERTICALFLIP) - && (!thiscam->chase || player->pflags & PF_FLIPCAM)) //because chasecam's not inverted - ? -1 : 1; // set to -1 or 1 to multiply + INT32 screen_invert = (player->mo && (player->mo->eflags & MFE_VERTICALFLIP) && (!thiscam->chase)) ? -1 : 1; // set to -1 or 1 to multiply // mouse look stuff (mouse look is not the same as mouse aim) if (mouseaiming && player->spectator) @@ -1677,7 +1674,7 @@ static void Analog_OnChange(void) } */ - SendWeaponPref(); + //SendWeaponPref(); } static void Analog2_OnChange(void) @@ -1694,7 +1691,7 @@ static void Analog2_OnChange(void) } */ - SendWeaponPref2(); + //SendWeaponPref2(); } static void Analog3_OnChange(void) @@ -1711,7 +1708,7 @@ static void Analog3_OnChange(void) } */ - SendWeaponPref3(); + //SendWeaponPref3(); } static void Analog4_OnChange(void) @@ -1728,7 +1725,7 @@ static void Analog4_OnChange(void) } */ - SendWeaponPref4(); + //SendWeaponPref4(); } // @@ -2619,7 +2616,7 @@ void G_PlayerReborn(INT32 player) jointime = players[player].jointime; splitscreenindex = players[player].splitscreenindex; spectator = players[player].spectator; - pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); + pflags = (players[player].pflags & (PF_TIMEOVER|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); // As long as we're not in multiplayer, carry over cheatcodes from map to map if (!(netgame || multiplayer)) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2547f3212..027655f47 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1168,26 +1168,6 @@ static void P_PlayerFlip(mobj_t *mo) if (mo->tracer) mo->tracer->eflags ^= MFE_VERTICALFLIP; } - else if (mo->player->pflags & PF_FLIPCAM) - { - UINT8 i; - - mo->player->aiming = InvAngle(mo->player->aiming); - - for (i = 0; i <= r_splitscreen; i++) - { - if (mo->player-players == displayplayers[i]) - { - localaiming[i] = mo->player->aiming; - if (camera[i].chase) { - camera[i].aiming = InvAngle(camera[i].aiming); - camera[i].z = mo->z - camera[i].z + mo->z; - if (mo->eflags & MFE_VERTICALFLIP) - camera[i].z += FixedMul(20*FRACUNIT, mo->scale); - } - } - } - } } // @@ -3599,8 +3579,6 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled if (encoremode) postimg = postimg_mirror; - else if (player->pflags & PF_FLIPCAM && !(player->pflags & PF_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP) - postimg = postimg_flip; else if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist { camera_t dummycam; @@ -7059,8 +7037,6 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target->eflags & MFE_VERTICALFLIP) { mobj->z = mobj->target->z - FixedMul(16*FRACUNIT, mobj->target->scale) - mobj->height; - if (mobj->target->player->pflags & PF_FLIPCAM) - mobj->eflags |= MFE_VERTICALFLIP; } else mobj->z = mobj->target->z + (mobj->target->height) + FixedMul(8*FRACUNIT, mobj->target->scale); // Adjust height for height changes diff --git a/src/r_main.c b/src/r_main.c index 5f7b0cadd..15278e66f 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -151,10 +151,6 @@ static void ChaseCam_OnChange(void); static void ChaseCam2_OnChange(void); static void ChaseCam3_OnChange(void); static void ChaseCam4_OnChange(void); -static void FlipCam_OnChange(void); -static void FlipCam2_OnChange(void); -static void FlipCam3_OnChange(void); -static void FlipCam4_OnChange(void); void SendWeaponPref(void); void SendWeaponPref2(void); void SendWeaponPref3(void); @@ -165,10 +161,6 @@ consvar_t cv_chasecam = {"chasecam", "On", CV_CALL, CV_OnOff, ChaseCam_OnChange, consvar_t cv_chasecam2 = {"chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_chasecam3 = {"chasecam3", "On", CV_CALL, CV_OnOff, ChaseCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_chasecam4 = {"chasecam4", "On", CV_CALL, CV_OnOff, ChaseCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam = {"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam3 = {"flipcam3", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam4 = {"flipcam4", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_shadow = {"shadow", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -282,27 +274,6 @@ static void ChaseCam4_OnChange(void) else CV_SetValue(&cv_analog4, 1);*/ } - -static void FlipCam_OnChange(void) -{ - SendWeaponPref(); -} - -static void FlipCam2_OnChange(void) -{ - SendWeaponPref2(); -} - -static void FlipCam3_OnChange(void) -{ - SendWeaponPref3(); -} - -static void FlipCam4_OnChange(void) -{ - SendWeaponPref4(); -} - // // R_PointOnSide // Traverse BSP (sub) tree, @@ -1488,10 +1459,6 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_soniccd); CV_RegisterVar(&cv_allowmlook); CV_RegisterVar(&cv_homremoval); - CV_RegisterVar(&cv_flipcam); - CV_RegisterVar(&cv_flipcam2); - CV_RegisterVar(&cv_flipcam3); - CV_RegisterVar(&cv_flipcam4); // Enough for dedicated server if (dedicated) diff --git a/src/r_main.h b/src/r_main.h index 879d4c6eb..ea6f6aa87 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -75,7 +75,6 @@ boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixe extern consvar_t cv_showhud, cv_translucenthud; extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2, cv_chasecam3, cv_chasecam4; -extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4; extern consvar_t cv_shadow; extern consvar_t cv_translucency; extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip; From 660596970b0571c27ee77d9237d735ec922798bd Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 16:47:18 -0400 Subject: [PATCH 166/211] Add FAULT graphic, remove momentum when faulting --- src/d_player.h | 3 +++ src/k_kart.c | 53 +++++++++++++++++++++++++++++++++++++++----------- src/p_spec.c | 2 ++ 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index a133cbadc..a357396f2 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -376,6 +376,9 @@ typedef enum khud_lapanimation, // Used to show the lap start wing logo animation khud_laphand, // Lap hand gfx to use; 0 = none, 1 = :ok_hand:, 2 = :thumbs_up:, 3 = :thumps_down: + // Start + khud_fault, // Set when faulting during the starting countdown + // Camera khud_boostcam, // Camera push forward on boost khud_destboostcam, // Ditto diff --git a/src/k_kart.c b/src/k_kart.c index 82eec1caa..f3dbade0b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7856,6 +7856,7 @@ static patch_t *kp_splitkarmabomb; static patch_t *kp_timeoutsticker; static patch_t *kp_startcountdown[16]; +static patch_t *kp_racefault[6]; static patch_t *kp_racefinish[6]; static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; @@ -7990,6 +7991,16 @@ void K_LoadKartHUDGraphics(void) kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); + // Fault + kp_racefault[0] = W_CachePatchName("K_FAULTA", PU_HUDGFX); + kp_racefault[1] = W_CachePatchName("K_FAULTB", PU_HUDGFX); + // Splitscreen + kp_racefault[2] = W_CachePatchName("K_SMFLTA", PU_HUDGFX); + kp_racefault[3] = W_CachePatchName("K_SMFLTB", PU_HUDGFX); + // 2P splitscreen + kp_racefault[4] = W_CachePatchName("K_2PFLTA", PU_HUDGFX); + kp_racefault[5] = W_CachePatchName("K_2PFLTB", PU_HUDGFX); + // Finish kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX); kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX); @@ -10551,18 +10562,38 @@ static void K_drawKartStartCountdown(void) { INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3 - if (leveltime >= starttime-(2*TICRATE)) // 2 - pnum++; - if (leveltime >= starttime-TICRATE) // 1 - pnum++; - if (leveltime >= starttime) // GO! - pnum++; - if ((leveltime % (2*5)) / 5) // blink - pnum += 4; - if (r_splitscreen) // splitscreen - pnum += 8; + if (stplyr->karthud[khud_fault] != 0) + { + if (r_splitscreen > 1) // 3/4p, stationary FIN + { + pnum += 2; + } + else if (r_splitscreen == 1) // wide splitscreen + { + pnum += 4; + } - V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); + if ((leveltime % (2*5)) / 5) // blink + pnum += 1; + + V_DrawScaledPatch(STCD_X - (SHORT(kp_racefault[pnum]->width)/2), STCD_Y - (SHORT(kp_racefault[pnum]->height)/2), splitflags, kp_racefault[pnum]); + } + else + { + + if (leveltime >= starttime-(2*TICRATE)) // 2 + pnum++; + if (leveltime >= starttime-TICRATE) // 1 + pnum++; + if (leveltime >= starttime) // GO! + pnum++; + if ((leveltime % (2*5)) / 5) // blink + pnum += 4; + if (r_splitscreen) // splitscreen + pnum += 8; + + V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); + } } static void K_drawKartFinish(void) diff --git a/src/p_spec.c b/src/p_spec.c index 6dc3bd9de..6d1df0ef0 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2191,6 +2191,8 @@ static void K_HandleLapIncrement(player_t *player) player->powers[pw_nocontrol] = (starttime - leveltime) + 50; player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse S_StartSound(player->mo, sfx_s3k83); + player->karthud[khud_fault] = 1; + player->mo->momx = player->mo->momy = 0; } } } From 8acace7a5084e6ce11d45cc1778365e735adf00b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 17:16:12 -0400 Subject: [PATCH 167/211] Fault on death --- src/k_respawn.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/k_respawn.c b/src/k_respawn.c index 6c20c8f62..ab229605a 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -96,11 +96,20 @@ void K_DoIngameRespawn(player_t *player) return; } - if (leveltime <= starttime) + if (leveltime < introtime) { return; } + if (leveltime < starttime) + { + player->powers[pw_nocontrol] = (starttime - leveltime) + 50; + player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse + S_StartSound(player->mo, sfx_s3k83); + player->karthud[khud_fault] = 1; + player->mo->momx = player->mo->momy = 0; + } + player->kartstuff[k_ringboost] = 0; player->kartstuff[k_driftboost] = 0; player->kartstuff[k_drift] = 0; @@ -108,7 +117,7 @@ void K_DoIngameRespawn(player_t *player) player->kartstuff[k_pogospring] = 0; // Set up respawn position if invalid - if (player->respawn.wp != NULL) + if (player->respawn.wp != NULL && leveltime >= starttime) { const UINT32 dist = RESPAWN_DIST + (player->airtime * 48); player->respawn.distanceleft = (dist * mapobjectscale) / FRACUNIT; From babe71cdeebf31b689bd6e7b53c14623d53ce502 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 18:01:36 -0400 Subject: [PATCH 168/211] Try to avoid respawning you past the finish line --- src/k_respawn.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/k_respawn.c b/src/k_respawn.c index ab229605a..360174fa1 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -334,8 +334,9 @@ static void K_MovePlayerToRespawnPoint(player_t *player) dest.x, dest.y ); - if ((player->respawn.distanceleft == 0) - && (K_GetWaypointIsSpawnpoint(player->respawn.wp) == true)) + if ((player->respawn.distanceleft == 0 && K_GetWaypointIsSpawnpoint(player->respawn.wp) == true) + || (player->respawn.wp == K_GetFinishLineWaypoint() + || player->respawn.wp->nextwaypoints[nwp] == K_GetFinishLineWaypoint())) // Try not to allow you to pass the finish line while respawning, because it's janky { // Alright buddy, that's the end of the ride. player->respawn.state = RESPAWNST_DROP; From 562964f242e62a73e657ce0c115bd2c309b27848 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 18:47:05 -0400 Subject: [PATCH 169/211] Properly rebalanced table to make up for it --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index ada35edca..8ad20758e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -275,18 +275,18 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = //P-Odds 0 1 2 3 4 5 6 7 /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker - /*Invincibility*/ { 0, 0, 0, 0, 1, 4, 7, 9 }, // Invincibility + /*Invincibility*/ { 0, 0, 0, 0, 2, 4, 6, 9 }, // Invincibility /*Banana*/ { 7, 3, 2, 0, 0, 0, 0, 0 }, // Banana /*Eggman Monitor*/ { 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor /*Orbinaut*/ { 7, 4, 3, 2, 0, 0, 0, 0 }, // Orbinaut /*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz - /*Mine*/ { 0, 2, 2, 1, 0, 0, 0, 0 }, // Mine - /*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog + /*Mine*/ { 0, 3, 3, 2, 0, 0, 0, 0 }, // Mine + /*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink /*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield - /*Bubble Shield*/ { 0, 1, 2, 2, 0, 0, 0, 0 }, // Bubble Shield + /*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield /*Hyudoro*/ { 0, 0, 0, 1, 2, 0, 0, 0 }, // Hyudoro /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring From ba6d5e95bad513250fd278145e8efba3ae5b05a4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 19:11:52 -0400 Subject: [PATCH 170/211] Add Sneaker x2 to pad out the item table a bit better Jeck mentioned it, we agreed it might be a fun item because stacking sneakers is possible now --- src/d_netcmd.c | 1 + src/d_netcmd.h | 2 +- src/d_player.h | 3 ++- src/dehacked.c | 3 ++- src/k_kart.c | 16 +++++++++++++--- src/m_menu.c | 2 ++ 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b55441b2d..51aaf1a61 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -395,6 +395,7 @@ consvar_t cv_hyudoro = {"hyudoro", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NU consvar_t cv_pogospring = {"pogospring", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kitchensink = {"kitchensink", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_dualsneaker = {"dualsneaker", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_triplesneaker = {"triplesneaker", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_triplebanana = {"triplebanana", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_decabanana = {"decabanana", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 56690c130..6acaf7b97 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -108,7 +108,7 @@ extern consvar_t cv_ballhog, cv_selfpropelledbomb, cv_grow, cv_shrink; extern consvar_t cv_thundershield, cv_bubbleshield, cv_flameshield; extern consvar_t cv_hyudoro, cv_pogospring, cv_kitchensink; -extern consvar_t cv_triplesneaker, cv_triplebanana, cv_decabanana; +extern consvar_t cv_dualsneaker, cv_triplesneaker, cv_triplebanana, cv_decabanana; extern consvar_t cv_tripleorbinaut, cv_quadorbinaut, cv_dualjawz; extern consvar_t cv_kartminimap; diff --git a/src/d_player.h b/src/d_player.h index 4ae420052..00697a4b9 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -237,7 +237,8 @@ typedef enum NUMKARTITEMS, // Additional roulette numbers, only used for K_KartGetItemResult - KRITEM_TRIPLESNEAKER = NUMKARTITEMS, + KRITEM_DUALSNEAKER = NUMKARTITEMS, + KRITEM_TRIPLESNEAKER, KRITEM_TRIPLEBANANA, KRITEM_TENFOLDBANANA, KRITEM_TRIPLEORBINAUT, diff --git a/src/dehacked.c b/src/dehacked.c index e803094ad..6438a8edf 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9410,7 +9410,8 @@ struct { KART_ITEM_ITERATOR, // Actual items (can be set for k_itemtype) #undef FOREACH {"NUMKARTITEMS",NUMKARTITEMS}, - {"KRITEM_TRIPLESNEAKER",KRITEM_TRIPLESNEAKER}, // Additional roulette IDs (not usable for much in Lua besides K_GetItemPatch) + {"KRITEM_DUALSNEAKER",KRITEM_DUALSNEAKER}, // Additional roulette IDs (not usable for much in Lua besides K_GetItemPatch) + {"KRITEM_TRIPLESNEAKER",KRITEM_TRIPLESNEAKER}, {"KRITEM_TRIPLEBANANA",KRITEM_TRIPLEBANANA}, {"KRITEM_TENFOLDBANANA",KRITEM_TENFOLDBANANA}, {"KRITEM_TRIPLEORBINAUT",KRITEM_TRIPLEORBINAUT}, diff --git a/src/k_kart.c b/src/k_kart.c index 8ad20758e..c290ed549 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -160,6 +160,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_superring); CV_RegisterVar(&cv_kitchensink); + CV_RegisterVar(&cv_dualsneaker); CV_RegisterVar(&cv_triplesneaker); CV_RegisterVar(&cv_triplebanana); CV_RegisterVar(&cv_decabanana); @@ -259,6 +260,7 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] = &cv_pogospring, &cv_superring, &cv_kitchensink, + &cv_dualsneaker, &cv_triplesneaker, &cv_triplebanana, &cv_decabanana, @@ -280,18 +282,19 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Eggman Monitor*/ { 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor /*Orbinaut*/ { 7, 4, 3, 2, 0, 0, 0, 0 }, // Orbinaut /*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz - /*Mine*/ { 0, 3, 3, 2, 0, 0, 0, 0 }, // Mine - /*Ballhog*/ { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog + /*Mine*/ { 0, 3, 2, 1, 0, 0, 0, 0 }, // Mine + /*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow /*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink /*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield /*Bubble Shield*/ { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield /*Flame Shield*/ { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield - /*Hyudoro*/ { 0, 0, 0, 1, 2, 0, 0, 0 }, // Hyudoro + /*Hyudoro*/ { 0, 0, 0, 1, 1, 0, 0, 0 }, // Hyudoro /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring /*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + /*Sneaker x2*/ { 0, 0, 1, 2, 1, 0, 0, 0 }, // Sneaker x2 /*Sneaker x3*/ { 0, 0, 0, 2, 6,10, 5, 0 }, // Sneaker x3 /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 @@ -322,6 +325,7 @@ static INT32 K_KartItemOddsBattle[NUMKARTRESULTS-1][6] = /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring /*Super Ring*/ { 0, 0, 0, 0, 0, 0 }, // Super Ring /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + /*Sneaker x2*/ { 0, 0, 0, 0, 0, 0 }, // Sneaker x2 /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3 /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3 /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10 @@ -364,6 +368,10 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) switch (getitem) { // Special roulettes first, then the generic ones are handled by default + case KRITEM_DUALSNEAKER: // Sneaker x2 + player->kartstuff[k_itemtype] = KITEM_SNEAKER; + player->kartstuff[k_itemamount] = 2; + break; case KRITEM_TRIPLESNEAKER: // Sneaker x3 player->kartstuff[k_itemtype] = KITEM_SNEAKER; player->kartstuff[k_itemamount] = 3; @@ -523,6 +531,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp case KITEM_THUNDERSHIELD: case KITEM_BUBBLESHIELD: case KITEM_FLAMESHIELD: + case KRITEM_DUALSNEAKER: case KRITEM_TRIPLESNEAKER: case KRITEM_TRIPLEBANANA: case KRITEM_TENFOLDBANANA: @@ -8235,6 +8244,7 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny) switch (item) { case KITEM_SNEAKER: + case KRITEM_DUALSNEAKER: case KRITEM_TRIPLESNEAKER: return (tiny ? "K_ISSHOE" : "K_ITSHOE"); case KITEM_ROCKETSNEAKER: diff --git a/src/m_menu.c b/src/m_menu.c index 46b7a319d..5a8c6e68e 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -11103,6 +11103,7 @@ static void M_DrawMonitorToggles(void) switch (currentMenu->menuitems[thisitem].alphaKey) { + case KRITEM_DUALSNEAKER: case KRITEM_DUALJAWZ: drawnum = 2; break; @@ -11172,6 +11173,7 @@ static void M_DrawMonitorToggles(void) switch (currentMenu->menuitems[itemOn].alphaKey) { + case KRITEM_DUALSNEAKER: case KRITEM_DUALJAWZ: drawnum = 2; break; From 1a61810f169c30e3825c225f8fcd1b87288d8794 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 20:19:14 -0400 Subject: [PATCH 171/211] Make ring respawn faster --- src/p_mobj.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 041f9ea0f..a5b90c569 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11728,13 +11728,19 @@ void P_RespawnSpecials(void) return; else if (pcount > 1) { - time = (180 - (pcount * 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; + } } // nothing left to respawn? From 5a59a9e8b95cb279231b81d5b702aaab2f08ea9b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 20:30:18 -0400 Subject: [PATCH 172/211] Adjust item tables again --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 6986ce5c7..8e7892e19 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -278,11 +278,11 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Sneaker*/ { 0, 0, 2, 4, 6, 0, 0, 0 }, // Sneaker /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 2, 4, 6 }, // Rocket Sneaker /*Invincibility*/ { 0, 0, 0, 0, 2, 4, 6, 9 }, // Invincibility - /*Banana*/ { 7, 3, 2, 0, 0, 0, 0, 0 }, // Banana + /*Banana*/ { 7, 3, 1, 0, 0, 0, 0, 0 }, // Banana /*Eggman Monitor*/ { 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor - /*Orbinaut*/ { 7, 4, 3, 2, 0, 0, 0, 0 }, // Orbinaut + /*Orbinaut*/ { 7, 4, 2, 2, 0, 0, 0, 0 }, // Orbinaut /*Jawz*/ { 0, 3, 2, 1, 1, 0, 0, 0 }, // Jawz - /*Mine*/ { 0, 3, 2, 1, 0, 0, 0, 0 }, // Mine + /*Mine*/ { 0, 2, 3, 1, 0, 0, 0, 0 }, // Mine /*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog /*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb /*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow @@ -294,7 +294,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring /*Super Ring*/ { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink - /*Sneaker x2*/ { 0, 0, 1, 2, 1, 0, 0, 0 }, // Sneaker x2 + /*Sneaker x2*/ { 0, 0, 2, 2, 1, 0, 0, 0 }, // Sneaker x2 /*Sneaker x3*/ { 0, 0, 0, 2, 6,10, 5, 0 }, // Sneaker x3 /*Banana x3*/ { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 /*Banana x10*/ { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 From f0044e05c1ff1d8d73b60b3b47f0e1d75248d224 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 24 Jul 2020 21:41:41 -0400 Subject: [PATCH 173/211] Crossing the finish line first before anyone else gives you a free rainbow boost --- src/doomstat.h | 1 + src/g_game.c | 1 + src/k_kart.c | 2 +- src/k_kart.h | 1 + src/p_saveg.c | 2 ++ src/p_setup.c | 13 +++++++++++++ src/p_spec.c | 7 +++++++ 7 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/doomstat.h b/src/doomstat.h index ffac81726..4e102277a 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -534,6 +534,7 @@ extern tic_t hyubgone; extern tic_t mapreset; extern boolean thwompsactive; extern SINT8 spbplace; +extern boolean rainbowstartavailable; extern tic_t bombflashtimer; // Used to avoid causing seizures if multiple mines explode close to you :) extern boolean legitimateexit; diff --git a/src/g_game.c b/src/g_game.c index f359b28f4..64e69276c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -289,6 +289,7 @@ tic_t hyubgone; // Cooldown before hyudoro is allowed to be rerolled tic_t mapreset; // Map reset delay when enough players have joined an empty game boolean thwompsactive; // Thwomps activate on lap 2 SINT8 spbplace; // SPB exists, give the person behind better items +boolean rainbowstartavailable; // Boolean, keeps track of if the rainbow start was gotten // Client-sided, unsynched variables (NEVER use in anything that needs to be synced with other players) tic_t bombflashtimer = 0; // Cooldown before another FlashPal can be intialized by a bomb exploding near a displayplayer. Avoids seizures. diff --git a/src/k_kart.c b/src/k_kart.c index 79e5e355f..d6f678527 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6488,7 +6488,7 @@ Stage 1: red sparks Stage 2: blue sparks Stage 3: big large rainbow sparks */ -static void K_SpawnDriftBoostExplosion(player_t *player, int stage) +void K_SpawnDriftBoostExplosion(player_t *player, int stage) { mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_DRIFTEXPLODE); diff --git a/src/k_kart.h b/src/k_kart.h index d03dd35f7..c06f2bef3 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -63,6 +63,7 @@ void K_UpdateDistanceFromFinishLine(player_t *const player); boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y); 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); void K_DropItems(player_t *player); void K_StripItems(player_t *player); diff --git a/src/p_saveg.c b/src/p_saveg.c index 82affdd4b..58fe82174 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3472,6 +3472,7 @@ static void P_NetArchiveMisc(void) WRITEUINT8(save_p, thwompsactive); WRITESINT8(save_p, spbplace); + WRITEUINT8(save_p, rainbowstartavailable); // Is it paused? if (paused) @@ -3597,6 +3598,7 @@ static inline boolean P_NetUnArchiveMisc(void) thwompsactive = (boolean)READUINT8(save_p); spbplace = READSINT8(save_p); + rainbowstartavailable = (boolean)READUINT8(save_p); // Is it paused? if (READUINT8(save_p) == 0x2f) diff --git a/src/p_setup.c b/src/p_setup.c index 0f9e98702..406ed347c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2368,6 +2368,7 @@ lumpnum_t lastloadedmaplumpnum; // for comparative savegame static void P_LevelInitStuff(void) { INT32 i; + UINT8 p = 0; leveltime = 0; @@ -2414,6 +2415,9 @@ static void P_LevelInitStuff(void) for (i = 0; i < MAXPLAYERS; i++) { + if (playeringame[i] && !players[i].spectator) + p++; + if (grandprixinfo.gp == false) { players[i].lives = 3; @@ -2463,6 +2467,11 @@ static void P_LevelInitStuff(void) players[i].follower = NULL; } + rainbowstartavailable = false; + + if (p >= 2) + rainbowstartavailable = true; + // SRB2Kart: map load variables if (grandprixinfo.gp == true) { @@ -2505,6 +2514,10 @@ static void P_LevelInitStuff(void) memset(&battleovertime, 0, sizeof(struct battleovertime)); speedscramble = encorescramble = -1; + + + + } // diff --git a/src/p_spec.c b/src/p_spec.c index 6d1df0ef0..ea96ab268 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2194,6 +2194,13 @@ static void K_HandleLapIncrement(player_t *player) player->karthud[khud_fault] = 1; player->mo->momx = player->mo->momy = 0; } + else if (rainbowstartavailable == true) + { + S_StartSound(player->mo, sfx_s23c); + player->kartstuff[k_driftboost] = 125; + K_SpawnDriftBoostExplosion(player, 3); + rainbowstartavailable = false; + } } } From 679f3223b6dc45225a3f36861143b95e49473544 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 25 Jul 2020 21:07:27 -0400 Subject: [PATCH 174/211] Remove silly white space --- src/p_setup.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index 406ed347c..72d3867cf 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2514,10 +2514,6 @@ static void P_LevelInitStuff(void) memset(&battleovertime, 0, sizeof(struct battleovertime)); speedscramble = encorescramble = -1; - - - - } // From 3d53adaa2b4fc33a155c47fe636ea7a8d3adf114 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 26 Jul 2020 05:47:53 -0400 Subject: [PATCH 175/211] Sliding HUD - Split HUD stuff into k_hud.c/h - V_SPLITSCREEN replaces the old function that sets V_SPLITSCREEN/V_HORZSCREEN flags system, and instead automatically moves it based on player number - V_SLIDEIN makes HUD items slide in after the intro animation. --- src/CMakeLists.txt | 2 + src/Makefile | 7 +- src/dehacked.c | 2 +- src/hardware/hw_draw.c | 96 +- src/k_hud.c | 3720 ++++++++++++++++++++++++++++++++++++++++ src/k_hud.h | 28 + src/k_kart.c | 3656 +-------------------------------------- src/k_kart.h | 9 +- src/lua_baselib.c | 1 + src/m_menu.c | 3 +- src/st_stuff.c | 21 +- src/v_video.c | 86 +- src/v_video.h | 6 +- 13 files changed, 3802 insertions(+), 3835 deletions(-) create mode 100644 src/k_hud.c create mode 100644 src/k_hud.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4bb0368a9..d653bf69a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -170,6 +170,7 @@ set(SRB2_CORE_GAME_SOURCES k_botsearch.c k_respawn.c k_grandprix.c + k_hud.c p_local.h p_maputl.h @@ -192,6 +193,7 @@ set(SRB2_CORE_GAME_SOURCES k_bot.h k_respawn.h k_grandprix.h + k_hud.h ) if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) diff --git a/src/Makefile b/src/Makefile index bdbe71f70..2cd406b1d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -564,10 +564,11 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/k_waypoint.o\ $(OBJDIR)/k_pathfind.o\ $(OBJDIR)/k_bheap.o \ - $(OBJDIR)/k_bot.o \ - $(OBJDIR)/k_botitem.o \ - $(OBJDIR)/k_botsearch.o \ + $(OBJDIR)/k_bot.o \ + $(OBJDIR)/k_botitem.o\ + $(OBJDIR)/k_botsearch.o\ $(OBJDIR)/k_grandprix.o\ + $(OBJDIR)/k_hud.o \ $(i_cdmus_o) \ $(i_net_o) \ $(i_system_o) \ diff --git a/src/dehacked.c b/src/dehacked.c index 6438a8edf..529f9c6b3 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -9385,7 +9385,7 @@ struct { {"V_WRAPY",V_WRAPY}, {"V_NOSCALESTART",V_NOSCALESTART}, {"V_SPLITSCREEN",V_SPLITSCREEN}, - {"V_HORZSCREEN",V_HORZSCREEN}, + {"V_SLIDEIN",V_SLIDEIN}, {"V_PARAMMASK",V_PARAMMASK}, {"V_SCALEPATCHMASK",V_SCALEPATCHMASK}, diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index ba069420e..c86b965a8 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -29,6 +29,7 @@ #include "../z_zone.h" #include "../v_video.h" #include "../st_stuff.h" +#include "../k_hud.h" #include #include "../i_video.h" // for rendermode != render_glide @@ -194,12 +195,6 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, cy -= offsety; } - if (option & V_SPLITSCREEN) - cy += FIXED_TO_FLOAT((BASEVIDHEIGHT/2)<= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT) - { - // Need to temporarily cache the real patch to get the colour of the top left pixel - patch_t *realpatch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC); - const column_t *column = (const column_t *)((const UINT8 *)(realpatch) + LONG((realpatch)->columnofs[0])); - const UINT8 *source = (const UINT8 *)(column) + 3; - HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); - Z_Free(realpatch); - } - // centre screen - if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f) - { - if (option & V_SNAPTORIGHT) - cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)); - else if (!(option & V_SNAPTOLEFT)) - cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/2; - } - if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f) - { - if ((option & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) - cy += ((float)vid.height/2 - ((float)BASEVIDHEIGHT/2 * dupy)); - else if (option & V_SNAPTOBOTTOM) - cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)); - else if (!(option & V_SNAPTOTOP)) - cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy))/2; - } + INT32 intx, inty; + intx = (INT32)cx; + inty = (INT32)cy; + K_AdjustXYWithSnap(&intx, &inty, option, dupx, dupy); + cx = (float)intx; + cy = (float)inty; } } @@ -377,7 +349,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal } if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f) { - if ((option & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM)) + if ((option & V_SNAPTOBOTTOM) == V_SNAPTOBOTTOM) cy += ((float)vid.height/2 - ((float)BASEVIDHEIGHT/2 * dupy)); else if (option & V_SNAPTOBOTTOM) cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)); @@ -872,6 +844,7 @@ void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 if (!(options & V_NOSCALESTART)) { float dupx = (float)vid.dupx, dupy = (float)vid.dupy; + INT32 intx, inty; if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT) { @@ -890,26 +863,11 @@ void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 fw *= dupx; fh *= dupy; - if (fabsf((float)vid.width - ((float)BASEVIDWIDTH * dupx)) > 1.0E-36f) - { - if (options & V_SNAPTORIGHT) - fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)); - else if (!(options & V_SNAPTOLEFT)) - fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2; - } - if (fabsf((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) > 1.0E-36f) - { - // same thing here - if (options & V_SNAPTOBOTTOM) - fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)); - else if (!(options & V_SNAPTOTOP)) - fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2; - } - if (options & V_SPLITSCREEN) - fy += ((float)BASEVIDHEIGHT * dupy)/2; - if (options & V_HORZSCREEN) - fx += ((float)BASEVIDWIDTH * dupx)/2; - + intx = (INT32)fx; + inty = (INT32)fy; + K_AdjustXYWithSnap(&intx, &inty, options, dupx, dupy); + fx = (float)intx; + fy = (float)inty; } if (fx >= vid.width || fy >= vid.height) @@ -980,6 +938,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) if (!(color & V_NOSCALESTART)) { float dupx = (float)vid.dupx, dupy = (float)vid.dupy; + INT32 intx, inty; if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT) { @@ -998,26 +957,11 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) fw *= dupx; fh *= dupy; - if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f) - { - if (color & V_SNAPTORIGHT) - fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)); - else if (!(color & V_SNAPTOLEFT)) - fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2; - } - if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f) - { - // same thing here - if (color & V_SNAPTOBOTTOM) - fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)); - else if (!(color & V_SNAPTOTOP)) - fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2; - } - if (color & V_SPLITSCREEN) - fy += ((float)BASEVIDHEIGHT * dupy)/2; - if (color & V_HORZSCREEN) - fx += ((float)BASEVIDWIDTH * dupx)/2; - + intx = (INT32)fx; + inty = (INT32)fy; + K_AdjustXYWithSnap(&intx, &inty, color, dupx, dupy); + fx = (float)intx; + fy = (float)inty; } if (fx >= vid.width || fy >= vid.height) diff --git a/src/k_hud.c b/src/k_hud.c new file mode 100644 index 000000000..f4d4ad525 --- /dev/null +++ b/src/k_hud.c @@ -0,0 +1,3720 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// 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 k_hud.c +/// \brief HUD drawing functions exclusive to Kart + +#include "k_hud.h" +#include "k_kart.h" +#include "k_battle.h" +#include "k_color.h" +#include "screen.h" +#include "doomtype.h" +#include "doomdef.h" +#include "hu_stuff.h" +#include "d_netcmd.h" +#include "v_video.h" +#include "r_draw.h" +#include "st_stuff.h" +#include "lua_hud.h" +#include "doomstat.h" +#include "d_clisrv.h" +#include "g_game.h" +#include "p_local.h" +#include "z_zone.h" +#include "m_cond.h" +#include "r_main.h" +#include "s_sound.h" +#include "r_things.h" + +#define NUMPOSNUMS 10 +#define NUMPOSFRAMES 7 // White, three blues, three reds +#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple + +//{ Patch Definitions +static patch_t *kp_nodraw; + +static patch_t *kp_timesticker; +static patch_t *kp_timestickerwide; +static patch_t *kp_lapsticker; +static patch_t *kp_lapstickerwide; +static patch_t *kp_lapstickernarrow; +static patch_t *kp_splitlapflag; +static patch_t *kp_bumpersticker; +static patch_t *kp_bumperstickerwide; +static patch_t *kp_capsulesticker; +static patch_t *kp_capsulestickerwide; +static patch_t *kp_karmasticker; +static patch_t *kp_splitkarmabomb; +static patch_t *kp_timeoutsticker; + +static patch_t *kp_startcountdown[16]; +static patch_t *kp_racefinish[6]; + +static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; +static patch_t *kp_winnernum[NUMPOSFRAMES]; + +static patch_t *kp_facenum[MAXPLAYERS+1]; +static patch_t *kp_facehighlight[8]; + +static patch_t *kp_spbminimap; + +static patch_t *kp_ringsticker[2]; +static patch_t *kp_ringstickersplit[4]; +static patch_t *kp_ring[6]; +static patch_t *kp_smallring[6]; +static patch_t *kp_ringdebtminus; +static patch_t *kp_ringdebtminussmall; +static patch_t *kp_ringspblock[16]; +static patch_t *kp_ringspblocksmall[16]; + +static patch_t *kp_speedometersticker; +static patch_t *kp_speedometerlabel[4]; + +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_battlewin; +static patch_t *kp_battlecool; +static patch_t *kp_battlelose; +static patch_t *kp_battlewait; +static patch_t *kp_battleinfo; +static patch_t *kp_wanted; +static patch_t *kp_wantedsplit; +static patch_t *kp_wantedreticle; + +static patch_t *kp_itembg[4]; +static patch_t *kp_itemtimer[2]; +static patch_t *kp_itemmulsticker[2]; +static patch_t *kp_itemx; + +static patch_t *kp_superring[2]; +static patch_t *kp_sneaker[2]; +static patch_t *kp_rocketsneaker[2]; +static patch_t *kp_invincibility[13]; +static patch_t *kp_banana[2]; +static patch_t *kp_eggman[2]; +static patch_t *kp_orbinaut[5]; +static patch_t *kp_jawz[2]; +static patch_t *kp_mine[2]; +static patch_t *kp_ballhog[2]; +static patch_t *kp_selfpropelledbomb[2]; +static patch_t *kp_grow[2]; +static patch_t *kp_shrink[2]; +static patch_t *kp_thundershield[2]; +static patch_t *kp_bubbleshield[2]; +static patch_t *kp_flameshield[2]; +static patch_t *kp_hyudoro[2]; +static patch_t *kp_pogospring[2]; +static patch_t *kp_kitchensink[2]; +static patch_t *kp_sadface[2]; + +static patch_t *kp_check[6]; + +static patch_t *kp_rival[2]; + +static patch_t *kp_eggnum[4]; + +static patch_t *kp_flameshieldmeter[104][2]; +static patch_t *kp_flameshieldmeter_bg[16][2]; + +static patch_t *kp_fpview[3]; +static patch_t *kp_inputwheel[5]; + +static patch_t *kp_challenger[25]; + +static patch_t *kp_lapanim_lap[7]; +static patch_t *kp_lapanim_final[11]; +static patch_t *kp_lapanim_number[10][3]; +static patch_t *kp_lapanim_emblem[2]; +static patch_t *kp_lapanim_hand[3]; + +static patch_t *kp_yougotem; +static patch_t *kp_itemminimap; + +static patch_t *kp_alagles[10]; +static patch_t *kp_blagles[6]; + +static patch_t *kp_cpu; + +static patch_t *kp_nametagstem; + +void K_LoadKartHUDGraphics(void) +{ + INT32 i, j; + char buffer[9]; + + // Null Stuff + kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX); + + // Stickers + kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX); + kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX); + kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX); + kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX); + kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX); + kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX); + kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX); + kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX); + 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_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); + kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); + + // Starting countdown + kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); + kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); + kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX); + kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX); + kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX); + kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX); + kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX); + kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); + // Splitscreen + kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX); + kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX); + kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX); + kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); + kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX); + kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX); + kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); + kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); + + // Finish + kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX); + kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX); + // Splitscreen + kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX); + kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX); + // 2P splitscreen + kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX); + kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX); + + // Position numbers + sprintf(buffer, "K_POSNxx"); + for (i = 0; i < NUMPOSNUMS; i++) + { + buffer[6] = '0'+i; + for (j = 0; j < NUMPOSFRAMES; j++) + { + //sprintf(buffer, "K_POSN%d%d", i, j); + buffer[7] = '0'+j; + kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + } + + sprintf(buffer, "K_POSNWx"); + for (i = 0; i < NUMWINFRAMES; i++) + { + buffer[7] = '0'+i; + kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "OPPRNKxx"); + for (i = 0; i <= MAXPLAYERS; i++) + { + buffer[6] = '0'+(i/10); + buffer[7] = '0'+(i%10); + kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_CHILIx"); + for (i = 0; i < 8; i++) + { + buffer[7] = '0'+(i+1); + kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_spbminimap = W_CachePatchName("SPBMMAP", PU_HUDGFX); + + // Rings & Lives + kp_ringsticker[0] = W_CachePatchName("RNGBACKA", PU_HUDGFX); + kp_ringsticker[1] = W_CachePatchName("RNGBACKB", PU_HUDGFX); + + sprintf(buffer, "K_RINGx"); + for (i = 0; i < 6; i++) + { + buffer[6] = '0'+(i+1); + kp_ring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringdebtminus = W_CachePatchName("RDEBTMIN", PU_HUDGFX); + + sprintf(buffer, "SPBRNGxx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1) / 10); + buffer[7] = '0'+((i+1) % 10); + kp_ringspblock[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringstickersplit[0] = W_CachePatchName("SMRNGBGA", PU_HUDGFX); + kp_ringstickersplit[1] = W_CachePatchName("SMRNGBGB", PU_HUDGFX); + + sprintf(buffer, "K_SRINGx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '0'+(i+1); + kp_smallring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_ringdebtminussmall = W_CachePatchName("SRDEBTMN", PU_HUDGFX); + + sprintf(buffer, "SPBRGSxx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1) / 10); + buffer[7] = '0'+((i+1) % 10); + kp_ringspblocksmall[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Speedometer + kp_speedometersticker = W_CachePatchName("K_SPDMBG", PU_HUDGFX); + + sprintf(buffer, "K_SPDMLx"); + for (i = 0; i < 4; i++) + { + buffer[7] = '0'+(i+1); + kp_speedometerlabel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Extra ranking icons + kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX); + kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX); + 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); + + // Battle graphics + kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); + kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX); + kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX); + kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX); + kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX); + kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX); + kp_wantedsplit = W_CachePatchName("4PWANTED", PU_HUDGFX); + kp_wantedreticle = W_CachePatchName("MMAPWANT", PU_HUDGFX); + + // Kart Item Windows + kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX); + kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX); + kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX); + kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX); + kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX); + + kp_superring[0] = W_CachePatchName("K_ITRING", PU_HUDGFX); + kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX); + kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX); + + sprintf(buffer, "K_ITINVx"); + for (i = 0; i < 7; i++) + { + buffer[7] = '1'+i; + kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX); + kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX); + sprintf(buffer, "K_ITORBx"); + for (i = 0; i < 4; i++) + { + buffer[7] = '1'+i; + kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX); + kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX); + kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX); + kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX); + kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX); + kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX); + kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX); + kp_bubbleshield[0] = W_CachePatchName("K_ITBUBS", PU_HUDGFX); + kp_flameshield[0] = W_CachePatchName("K_ITFLMS", PU_HUDGFX); + kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX); + kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX); + kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX); + kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX); + + sprintf(buffer, "FSMFGxxx"); + for (i = 0; i < 104; i++) + { + buffer[5] = '0'+((i+1)/100); + buffer[6] = '0'+(((i+1)/10)%10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "FSMBG0xx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter_bg[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Splitscreen + kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX); + kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX); + kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX); + kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX); + + kp_superring[1] = W_CachePatchName("K_ISRING", PU_HUDGFX); + kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX); + kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX); + sprintf(buffer, "K_ISINVx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '1'+i; + kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX); + kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX); + kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX); + kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX); + kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX); + kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX); + kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX); + kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX); + kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX); + kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX); + kp_bubbleshield[1] = W_CachePatchName("K_ISBUBS", PU_HUDGFX); + kp_flameshield[1] = W_CachePatchName("K_ISFLMS", PU_HUDGFX); + kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX); + kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX); + kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX); + kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX); + + sprintf(buffer, "FSMFSxxx"); + for (i = 0; i < 104; i++) + { + buffer[5] = '0'+((i+1)/100); + buffer[6] = '0'+(((i+1)/10)%10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "FSMBS0xx"); + for (i = 0; i < 16; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_flameshieldmeter_bg[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // CHECK indicators + sprintf(buffer, "K_CHECKx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '1'+i; + kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Rival indicators + sprintf(buffer, "K_RIVALx"); + for (i = 0; i < 2; i++) + { + buffer[7] = '1'+i; + kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Eggman warning numbers + sprintf(buffer, "K_EGGNx"); + for (i = 0; i < 4; i++) + { + buffer[6] = '0'+i; + kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // First person mode + kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX); + kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX); + kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX); + + // Input UI Wheel + sprintf(buffer, "K_WHEELx"); + for (i = 0; i < 5; i++) + { + buffer[7] = '0'+i; + kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // HERE COMES A NEW CHALLENGER + sprintf(buffer, "K_CHALxx"); + for (i = 0; i < 25; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Lap start animation + sprintf(buffer, "K_LAP0x"); + for (i = 0; i < 7; i++) + { + buffer[6] = '0'+(i+1); + kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPFxx"); + for (i = 0; i < 11; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPNxx"); + for (i = 0; i < 10; i++) + { + buffer[6] = '0'+i; + for (j = 0; j < 3; j++) + { + buffer[7] = '0'+(j+1); + kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + } + + sprintf(buffer, "K_LAPE0x"); + for (i = 0; i < 2; i++) + { + buffer[7] = '0'+(i+1); + kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "K_LAPH0x"); + for (i = 0; i < 3; i++) + { + buffer[7] = '0'+(i+1); + kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); + kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); + + sprintf(buffer, "ALAGLESx"); + for (i = 0; i < 10; ++i) + { + buffer[7] = '0'+i; + kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + sprintf(buffer, "BLAGLESx"); + for (i = 0; i < 6; ++i) + { + buffer[7] = '0'+i; + kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); + + kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX); +} + +// For the item toggle menu +const char *K_GetItemPatch(UINT8 item, boolean tiny) +{ + switch (item) + { + case KITEM_SNEAKER: + case KRITEM_DUALSNEAKER: + case KRITEM_TRIPLESNEAKER: + return (tiny ? "K_ISSHOE" : "K_ITSHOE"); + case KITEM_ROCKETSNEAKER: + return (tiny ? "K_ISRSHE" : "K_ITRSHE"); + case KITEM_INVINCIBILITY: + return (tiny ? "K_ISINV1" : "K_ITINV1"); + case KITEM_BANANA: + case KRITEM_TRIPLEBANANA: + case KRITEM_TENFOLDBANANA: + return (tiny ? "K_ISBANA" : "K_ITBANA"); + case KITEM_EGGMAN: + return (tiny ? "K_ISEGGM" : "K_ITEGGM"); + case KITEM_ORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB1"); + case KITEM_JAWZ: + case KRITEM_DUALJAWZ: + return (tiny ? "K_ISJAWZ" : "K_ITJAWZ"); + case KITEM_MINE: + return (tiny ? "K_ISMINE" : "K_ITMINE"); + case KITEM_BALLHOG: + return (tiny ? "K_ISBHOG" : "K_ITBHOG"); + case KITEM_SPB: + return (tiny ? "K_ISSPB" : "K_ITSPB"); + case KITEM_GROW: + return (tiny ? "K_ISGROW" : "K_ITGROW"); + case KITEM_SHRINK: + return (tiny ? "K_ISSHRK" : "K_ITSHRK"); + case KITEM_THUNDERSHIELD: + return (tiny ? "K_ISTHNS" : "K_ITTHNS"); + case KITEM_BUBBLESHIELD: + return (tiny ? "K_ISBUBS" : "K_ITBUBS"); + case KITEM_FLAMESHIELD: + return (tiny ? "K_ISFLMS" : "K_ITFLMS"); + case KITEM_HYUDORO: + return (tiny ? "K_ISHYUD" : "K_ITHYUD"); + case KITEM_POGOSPRING: + return (tiny ? "K_ISPOGO" : "K_ITPOGO"); + case KITEM_SUPERRING: + return (tiny ? "K_ISRING" : "K_ITRING"); + case KITEM_KITCHENSINK: + return (tiny ? "K_ISSINK" : "K_ITSINK"); + case KRITEM_TRIPLEORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB3"); + case KRITEM_QUADORBINAUT: + return (tiny ? "K_ISORBN" : "K_ITORB4"); + default: + return (tiny ? "K_ISSAD" : "K_ITSAD"); + } +} + +//} + +INT32 ITEM_X, ITEM_Y; // Item Window +INT32 TIME_X, TIME_Y; // Time Sticker +INT32 LAPS_X, LAPS_Y; // Lap Sticker +INT32 POSI_X, POSI_Y; // Position Number +INT32 FACE_X, FACE_Y; // Top-four Faces +INT32 STCD_X, STCD_Y; // Starting countdown +INT32 CHEK_Y; // CHECK graphic +INT32 MINI_X, MINI_Y; // Minimap +INT32 WANT_X, WANT_Y; // Battle WANTED poster + +// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN. +INT32 ITEM2_X, ITEM2_Y; +INT32 LAPS2_X, LAPS2_Y; +INT32 POSI2_X, POSI2_Y; + + +void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 dupy) +{ + // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx + INT32 screenwidth = BASEVIDWIDTH * dupx; + INT32 screenheight = BASEVIDHEIGHT * dupy; + SINT8 player = -1; + UINT8 i; + + if (r_splitscreen > 0) + screenheight /= 2; + + if (r_splitscreen > 1) + screenwidth /= 2; + + for (i = 0; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + player = i; + break; + } + } + + if (vid.width != (BASEVIDWIDTH * dupx)) + { + if (options & V_SNAPTORIGHT) + *x += (vid.width - screenwidth); + else if (!(options & V_SNAPTOLEFT)) + *x += (vid.width - screenwidth) / 2; + } + + if (vid.height != (BASEVIDHEIGHT * dupy)) + { + if (options & V_SNAPTOBOTTOM) + *y += (vid.height - screenheight); + else if (!(options & V_SNAPTOTOP)) + *y += (vid.height - screenheight) / 2; + } + + if (options & V_SPLITSCREEN) + { + if (r_splitscreen == 1) + { + if (player == 1) + *y += vid.height / 2; + } + else if (r_splitscreen > 1) + { + if (player == 1 || player == 3) + *x += vid.width / 2; + + if (player == 2 || player == 3) + *y += vid.height / 2; + } + } + + if (options & V_SLIDEIN) + { + tic_t length = TICRATE/3; + + if (leveltime < introtime + length) + { + INT32 offset = vid.width - (((leveltime - introtime) * vid.width) / length); + boolean slidefromright = false; + + if (r_splitscreen > 1) + { + if (stplyr == &players[displayplayers[1]] || stplyr == &players[displayplayers[3]]) + slidefromright = true; + } + + if (options & V_SNAPTORIGHT) + slidefromright = true; + else if (options & V_SNAPTOLEFT) + slidefromright = false; + + if (slidefromright == true) + { + offset = -offset; + } + + *x -= offset; + } + } +} + +static void K_initKartHUD(void) +{ + /* + BASEVIDWIDTH = 320 + BASEVIDHEIGHT = 200 + + Item window graphic is 41 x 33 + + Time Sticker graphic is 116 x 11 + Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14 + Therefore, timestamp is 116 x 14 altogether + + Lap Sticker is 80 x 11 + Lap flag is 22 x 20 + Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14 + Therefore, lapstamp is 80 x 20 altogether + + Position numbers are 43 x 53 + + Faces are 32 x 32 + Faces draw downscaled at 16 x 16 + Therefore, the allocated space for them is 16 x 67 altogether + + ---- + + ORIGINAL CZ64 SPLITSCREEN: + + Item window: + if (!splitscreen) { ICONX = 139; ICONY = 20; } + else { ICONX = BASEVIDWIDTH-315; ICONY = 60; } + + Time: 236, STRINGY( 12) + Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189) + + */ + + // Single Screen (defaults) + // Item Window + ITEM_X = 5; // 5 + ITEM_Y = 5; // 5 + // Level Timer + TIME_X = BASEVIDWIDTH - 148; // 172 + TIME_Y = 9; // 9 + // Level Laps + LAPS_X = 9; // 9 + LAPS_Y = BASEVIDHEIGHT - 29; // 171 + // Position Number + POSI_X = BASEVIDWIDTH - 9; // 268 + POSI_Y = BASEVIDHEIGHT - 9; // 138 + // Top-Four Faces + FACE_X = 9; // 9 + FACE_Y = 92; // 92 + // Starting countdown + STCD_X = BASEVIDWIDTH/2; // 9 + STCD_Y = BASEVIDHEIGHT/2; // 92 + // CHECK graphic + CHEK_Y = BASEVIDHEIGHT; // 200 + // Minimap + MINI_X = BASEVIDWIDTH - 50; // 270 + MINI_Y = (BASEVIDHEIGHT/2)-16; // 84 + // Battle WANTED poster + WANT_X = BASEVIDWIDTH - 55; // 270 + WANT_Y = BASEVIDHEIGHT- 71; // 176 + + if (r_splitscreen) // Splitscreen + { + ITEM_X = 5; + ITEM_Y = 3; + + LAPS_Y = (BASEVIDHEIGHT/2)-24; + + POSI_Y = (BASEVIDHEIGHT/2)- 2; + + STCD_Y = BASEVIDHEIGHT/4; + + MINI_Y = (BASEVIDHEIGHT/2); + + if (r_splitscreen > 1) // 3P/4P Small Splitscreen + { + // 1P (top left) + ITEM_X = -9; + ITEM_Y = -8; + + LAPS_X = 3; + LAPS_Y = (BASEVIDHEIGHT/2)-12; + + POSI_X = 24; + POSI_Y = (BASEVIDHEIGHT/2)-26; + + // 2P (top right) + ITEM2_X = (BASEVIDWIDTH/2)-39; + ITEM2_Y = -8; + + LAPS2_X = (BASEVIDWIDTH/2)-43; + LAPS2_Y = (BASEVIDHEIGHT/2)-12; + + POSI2_X = (BASEVIDWIDTH/2)-4; + POSI2_Y = (BASEVIDHEIGHT/2)-26; + + // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom. + + STCD_X = BASEVIDWIDTH/4; + + MINI_X = (3*BASEVIDWIDTH/4); + MINI_Y = (3*BASEVIDHEIGHT/4); + + if (r_splitscreen > 2) // 4P-only + { + MINI_X = (BASEVIDWIDTH/2); + MINI_Y = (BASEVIDHEIGHT/2); + } + } + } + + if (timeinmap > 105) + hudtrans = cv_translucenthud.value; + else + hudtrans = 0; +} + +static void K_drawKartItem(void) +{ + // ITEM_X = BASEVIDWIDTH-50; // 270 + // ITEM_Y = 24; // 24 + + // Why write V_DrawScaledPatch calls over and over when they're all the same? + // Set to 'no item' just in case. + const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); + patch_t *localpatch = kp_nodraw; + patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); + patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); + INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... + const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2); + INT32 itembar = 0; + INT32 maxl = 0; // itembar's normal highest value + const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); + UINT8 localcolor = SKINCOLOR_NONE; + SINT8 colormode = TC_RAINBOW; + UINT8 *colmap = NULL; + boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff + + if (stplyr->kartstuff[k_itemroulette]) + { + if (stplyr->skincolor) + localcolor = stplyr->skincolor; + + switch((stplyr->kartstuff[k_itemroulette] % (15*3)) / 3) + { + // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette + case 0: // Sneaker + localpatch = kp_sneaker[offset]; + //localcolor = SKINCOLOR_RASPBERRY; + break; + case 1: // Banana + localpatch = kp_banana[offset]; + //localcolor = SKINCOLOR_YELLOW; + break; + case 2: // Orbinaut + localpatch = kp_orbinaut[3+offset]; + //localcolor = SKINCOLOR_STEEL; + break; + case 3: // Mine + localpatch = kp_mine[offset]; + //localcolor = SKINCOLOR_JET; + break; + case 4: // Grow + localpatch = kp_grow[offset]; + //localcolor = SKINCOLOR_TEAL; + break; + case 5: // Hyudoro + localpatch = kp_hyudoro[offset]; + //localcolor = SKINCOLOR_STEEL; + break; + case 6: // Rocket Sneaker + localpatch = kp_rocketsneaker[offset]; + //localcolor = SKINCOLOR_TANGERINE; + break; + case 7: // Jawz + localpatch = kp_jawz[offset]; + //localcolor = SKINCOLOR_JAWZ; + break; + case 8: // Self-Propelled Bomb + localpatch = kp_selfpropelledbomb[offset]; + //localcolor = SKINCOLOR_JET; + break; + case 9: // Shrink + localpatch = kp_shrink[offset]; + //localcolor = SKINCOLOR_ORANGE; + break; + case 10: // Invincibility + localpatch = localinv; + //localcolor = SKINCOLOR_GREY; + break; + case 11: // Eggman Monitor + localpatch = kp_eggman[offset]; + //localcolor = SKINCOLOR_ROSE; + break; + case 12: // Ballhog + localpatch = kp_ballhog[offset]; + //localcolor = SKINCOLOR_LILAC; + break; + case 13: // Thunder Shield + localpatch = kp_thundershield[offset]; + //localcolor = SKINCOLOR_CYAN; + break; + case 14: // Super Ring + localpatch = kp_superring[offset]; + //localcolor = SKINCOLOR_GOLD; + break; + /*case 15: // Pogo Spring + localpatch = kp_pogospring[offset]; + localcolor = SKINCOLOR_TANGERINE; + break; + case 16: // Kitchen Sink + localpatch = kp_kitchensink[offset]; + localcolor = SKINCOLOR_STEEL; + break;*/ + default: + break; + } + } + else + { + // I'm doing this a little weird and drawing mostly in reverse order + // The only actual reason is to make sneakers line up this way in the code below + // This shouldn't have any actual baring over how it functions + // Hyudoro is first, because we're drawing it on top of the player's current item + if (stplyr->kartstuff[k_stolentimer] > 0) + { + if (leveltime & 2) + localpatch = kp_hyudoro[offset]; + else + localpatch = kp_nodraw; + } + else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2)) + { + localpatch = kp_hyudoro[offset]; + } + else if (stplyr->kartstuff[k_eggmanexplode] > 1) + { + if (leveltime & 1) + localpatch = kp_eggman[offset]; + else + localpatch = kp_nodraw; + } + else if (stplyr->kartstuff[k_rocketsneakertimer] > 1) + { + itembar = stplyr->kartstuff[k_rocketsneakertimer]; + maxl = (itemtime*3) - barlength; + + if (leveltime & 1) + localpatch = kp_rocketsneaker[offset]; + else + localpatch = kp_nodraw; + } + else if (stplyr->kartstuff[k_sadtimer] > 0) + { + if (leveltime & 2) + localpatch = kp_sadface[offset]; + else + localpatch = kp_nodraw; + } + else + { + if (stplyr->kartstuff[k_itemamount] <= 0) + return; + + switch(stplyr->kartstuff[k_itemtype]) + { + case KITEM_SNEAKER: + localpatch = kp_sneaker[offset]; + break; + case KITEM_ROCKETSNEAKER: + localpatch = kp_rocketsneaker[offset]; + break; + case KITEM_INVINCIBILITY: + localpatch = localinv; + localbg = kp_itembg[offset+1]; + break; + case KITEM_BANANA: + localpatch = kp_banana[offset]; + break; + case KITEM_EGGMAN: + localpatch = kp_eggman[offset]; + break; + case KITEM_ORBINAUT: + localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))]; + break; + case KITEM_JAWZ: + localpatch = kp_jawz[offset]; + break; + case KITEM_MINE: + localpatch = kp_mine[offset]; + break; + case KITEM_BALLHOG: + localpatch = kp_ballhog[offset]; + break; + case KITEM_SPB: + localpatch = kp_selfpropelledbomb[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_GROW: + localpatch = kp_grow[offset]; + break; + case KITEM_SHRINK: + localpatch = kp_shrink[offset]; + break; + case KITEM_THUNDERSHIELD: + localpatch = kp_thundershield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_BUBBLESHIELD: + localpatch = kp_bubbleshield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_FLAMESHIELD: + localpatch = kp_flameshield[offset]; + localbg = kp_itembg[offset+1]; + break; + case KITEM_HYUDORO: + localpatch = kp_hyudoro[offset]; + break; + case KITEM_POGOSPRING: + localpatch = kp_pogospring[offset]; + break; + case KITEM_SUPERRING: + localpatch = kp_superring[offset]; + break; + case KITEM_KITCHENSINK: + localpatch = kp_kitchensink[offset]; + break; + case KITEM_SAD: + localpatch = kp_sadface[offset]; + break; + default: + return; + } + + if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1)) + localpatch = kp_nodraw; + } + + if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) + { + colormode = TC_BLINK; + + switch (stplyr->karthud[khud_itemblinkmode]) + { + case 2: + localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); + break; + case 1: + localcolor = SKINCOLOR_RED; + break; + default: + localcolor = SKINCOLOR_WHITE; + break; + } + } + } + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; + } + else // now we're having a fun game. + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN; + } + else // else, that means we're P2 or P4. + { + fx = ITEM2_X; + fy = ITEM2_Y; + fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; + flipamount = true; + } + } + + if (localcolor != SKINCOLOR_NONE) + colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE); + + V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); + + // Then, the numbers: + if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette]) + { + V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. + V_DrawFixedPatch(fx<kartstuff[k_itemamount])); + else + V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->kartstuff[k_itemamount])); + else + { + V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx); + V_DrawKartString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", stplyr->kartstuff[k_itemamount])); + } + } + else + V_DrawFixedPatch(fx< 2) + { + V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one + if (height == 2) + V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside + V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine + } + } + + // Quick Eggman numbers + if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/) + V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|V_SLIDEIN|fflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]); + + if (stplyr->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && stplyr->kartstuff[k_flamelength] > 0) + { + INT32 numframes = 104; + INT32 absolutemax = 16 * flameseg; + INT32 flamemax = stplyr->kartstuff[k_flamelength] * flameseg; + INT32 flamemeter = min(stplyr->kartstuff[k_flamemeter], flamemax); + + INT32 bf = 16 - stplyr->kartstuff[k_flamelength]; + INT32 ff = numframes - ((flamemeter * numframes) / absolutemax); + INT32 fmin = (8 * (bf-1)); + + INT32 xo = 6, yo = 4; + INT32 flip = 0; + + if (offset) + { + xo++; + + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // Flip for P1 and P3 (yes, that's correct) + { + xo -= 62; + flip = V_FLIP; + } + } + + if (ff < fmin) + ff = fmin; + + if (bf >= 0 && bf < 16) + V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|V_SLIDEIN|fflags|flip, kp_flameshieldmeter_bg[bf][offset]); + + if (ff >= 0 && ff < numframes && stplyr->kartstuff[k_flamemeter] > 0) + { + if ((stplyr->kartstuff[k_flamemeter] > flamemax) && (leveltime & 1)) + { + UINT8 *fsflash = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); + V_DrawMappedPatch(fx-xo, fy-yo, V_HUDTRANS|V_SLIDEIN|fflags|flip, kp_flameshieldmeter[ff][offset], fsflash); + } + else + { + V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|V_SLIDEIN|fflags|flip, kp_flameshieldmeter[ff][offset]); + } + } + } +} + +void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode) +{ + // TIME_X = BASEVIDWIDTH-124; // 196 + // TIME_Y = 6; // 6 + + tic_t worktime; + + INT32 splitflags = 0; + if (!mode) + { + splitflags = V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP|V_SNAPTORIGHT|V_SPLITSCREEN; + if (cv_timelimit.value && timelimitintics > 0) + { + if (drawtime >= timelimitintics) + drawtime = 0; + else + drawtime = timelimitintics - drawtime; + } + } + + V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide)); + + TX += 33; + + worktime = drawtime/(60*TICRATE); + + if (mode && !drawtime) + V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--")); + else if (worktime < 100) // 99:99:99 only + { + // zero minute + if (worktime < 10) + { + V_DrawKartString(TX, TY+3, splitflags, va("0")); + // minutes time 0 __ __ + V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime)); + } + // minutes time 0 __ __ + else + V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime)); + + // apostrophe location _'__ __ + V_DrawKartString(TX+24, TY+3, splitflags, va("'")); + + worktime = (drawtime/TICRATE % 60); + + // zero second _ 0_ __ + if (worktime < 10) + { + V_DrawKartString(TX+36, TY+3, splitflags, va("0")); + // seconds time _ _0 __ + V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime)); + } + // zero second _ 00 __ + else + V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime)); + + // quotation mark location _ __"__ + V_DrawKartString(TX+60, TY+3, splitflags, va("\"")); + + worktime = G_TicsToCentiseconds(drawtime); + + // zero tick _ __ 0_ + if (worktime < 10) + { + V_DrawKartString(TX+72, TY+3, splitflags, va("0")); + // tics _ __ _0 + V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime)); + } + // zero tick _ __ 00 + else + V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime)); + } + else if ((drawtime/TICRATE) & 1) + V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); + + if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! + { + INT32 workx = TX + 96, worky = TY+18; + SINT8 curemb = 0; + patch_t *emblempic[3] = {NULL, NULL, NULL}; + UINT8 *emblemcol[3] = {NULL, NULL, NULL}; + + emblem_t *emblem = M_GetLevelEmblems(emblemmap); + while (emblem) + { + char targettext[9]; + + switch (emblem->type) + { + case ET_TIME: + { + static boolean canplaysound = true; + tic_t timetoreach = emblem->var; + + if (emblem->collected) + { + emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE); + emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE); + if (++curemb == 3) + break; + goto bademblem; + } + + snprintf(targettext, 9, "%i'%02i\"%02i", + G_TicsToMinutes(timetoreach, false), + G_TicsToSeconds(timetoreach), + G_TicsToCentiseconds(timetoreach)); + + if (!mode) + { + if (stplyr->realtime > timetoreach) + { + splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF; + if (canplaysound) + { + S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks + canplaysound = false; + } + } + else if (!canplaysound) + canplaysound = true; + } + + targettext[8] = 0; + } + break; + default: + goto bademblem; + } + + V_DrawRightAlignedString(workx, worky, splitflags, targettext); + workx -= 67; + V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE)); + + break; + + bademblem: + emblem = M_GetLevelEmblems(-1); + } + + if (!mode) + splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS; + while (curemb--) + { + workx -= 12; + V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]); + } + } +} + +static void K_DrawKartPositionNum(INT32 num) +{ + // POSI_X = BASEVIDWIDTH - 51; // 269 + // POSI_Y = BASEVIDHEIGHT- 64; // 136 + + boolean win = (stplyr->exiting && num == 1); + //INT32 X = POSI_X; + INT32 W = SHORT(kp_positionnum[0][0]->width); + fixed_t scale = FRACUNIT; + patch_t *localpatch = kp_positionnum[0][0]; + INT32 fx = 0, fy = 0, fflags = 0; + boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun. + boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen. + boolean overtake = false; + + if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting) + { + scale *= 2; + overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw. + } + if (r_splitscreen) + scale /= 2; + + W = FixedMul(W<>FRACBITS; + + // pain and suffering defined below + if (!r_splitscreen) + { + fx = POSI_X; + fy = BASEVIDHEIGHT - 8; + fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN; + } + else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different. + { + fx = POSI_X; + if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap. + { + fy = 30; + fflags = V_SNAPTOTOP|V_SNAPTORIGHT|V_SPLITSCREEN; + if (overtake) + flipvdraw = true; // make sure overtaking doesn't explode us + } + else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap. + { + fy = BASEVIDHEIGHT - 8; + fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN; + } + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = POSI_X; + fy = POSI_Y; + fflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN; + flipdraw = true; + if (num && num >= 10) + fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen. + } + else // else, that means we're P2 or P4. + { + fx = POSI2_X; + fy = POSI2_Y; + fflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN; + } + } + + // Special case for 0 + if (!num) + { + V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers + + // Draw the number + while (num) + { + if (win) // 1st place winner? You get rainbows!! + localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; + else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won + { + // Alternate frame every three frames + switch (leveltime % 9) + { + case 1: case 2: case 3: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][4]; + else + localpatch = kp_positionnum[num % 10][1]; + break; + case 4: case 5: case 6: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][5]; + else + localpatch = kp_positionnum[num % 10][2]; + break; + case 7: case 8: case 9: + if (K_IsPlayerLosing(stplyr)) + localpatch = kp_positionnum[num % 10][6]; + else + localpatch = kp_positionnum[num % 10][3]; + break; + default: + localpatch = kp_positionnum[num % 10][0]; + break; + } + } + else + localpatch = kp_positionnum[num % 10][0]; + + V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|V_SLIDEIN|fflags, localpatch, NULL); + // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen. + // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either. + + fx -= W; + num /= 10; + } +} + +static boolean K_drawKartPositionFaces(void) +{ + // FACE_X = 15; // 15 + // FACE_Y = 72; // 72 + + INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one + INT32 i, j, ranklines, strank = -1; + boolean completed[MAXPLAYERS]; + INT32 rankplayer[MAXPLAYERS]; + INT32 bumperx, numplayersingame = 0; + UINT8 *colormap; + + ranklines = 0; + memset(completed, 0, sizeof (completed)); + memset(rankplayer, 0, sizeof (rankplayer)); + + for (i = 0; i < MAXPLAYERS; i++) + { + rankplayer[i] = -1; + + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + numplayersingame++; + } + + if (numplayersingame <= 1) + return true; + +#ifdef HAVE_BLUA + if (!LUA_HudEnabled(hud_minirankings)) + return false; // Don't proceed but still return true for free play above if HUD is disabled. +#endif + + for (j = 0; j < numplayersingame; j++) + { + UINT8 lowestposition = MAXPLAYERS+1; + for (i = 0; i < MAXPLAYERS; i++) + { + if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + if (players[i].kartstuff[k_position] >= lowestposition) + continue; + + rankplayer[ranklines] = i; + lowestposition = players[i].kartstuff[k_position]; + } + + i = rankplayer[ranklines]; + + completed[i] = true; + + if (players+i == stplyr) + strank = ranklines; + + //if (ranklines == 5) + //break; // Only draw the top 5 players -- we do this a different way now... + + ranklines++; + } + + if (ranklines < 5) + Y -= (9*ranklines); + else + Y -= (9*5); + + if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) + { + i = 0; + if (ranklines > 5) // could be both... + ranklines = 5; + } + else if (strank+3 > ranklines) // too close to the bottom? + { + i = ranklines - 5; + if (i < 0) + i = 0; + } + else + { + i = strank-2; + ranklines = strank+3; + } + + for (; i < ranklines; i++) + { + if (!playeringame[rankplayer[i]]) continue; + if (players[rankplayer[i]].spectator) continue; + if (!players[rankplayer[i]].mo) continue; + + bumperx = FACE_X+19; + + if (players[rankplayer[i]].mo->color) + { + colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); + if (players[rankplayer[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); + + V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap); + +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_battlebumpers)) + { +#endif + if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 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++) + { + bumperx += 5; + V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[1], colormap); + } + } +#ifdef HAVE_BLUA + } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating: +#endif + } + + if (i == strank) + V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); + + if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0) + V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers); + else + { + INT32 pos = players[rankplayer[i]].kartstuff[k_position]; + if (pos < 0 || pos > MAXPLAYERS) + pos = 0; + // Draws the little number over the face + V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facenum[pos]); + } + + Y += 18; + } + + return false; +} + +// +// HU_DrawTabRankings -- moved here to take advantage of kart stuff! +// +void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) +{ + static tic_t alagles_timer = 9; + INT32 i, rightoffset = 240; + const UINT8 *colormap; + INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; + int y2; + + //this function is designed for 9 or less score lines only + //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up + + V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice! + if (scorelines > 8) + { + V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides. + V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom. + rightoffset = (BASEVIDWIDTH/2) - 4 - x; + } + + for (i = 0; i < scorelines; i++) + { + char strtime[MAXPLAYERNAME+1]; + + if (players[tab[i].num].spectator || !players[tab[i].num].mo) + continue; //ignore them. + + if (netgame) // don't draw ping offline + { + if (players[tab[i].num].bot) + { + V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu); + } + else if (tab[i].num != serverplayer || !server_lagless) + { + HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); + } + } + + STRBUFCPY(strtime, tab[i].name); + + y2 = y; + + if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) + { + y2 = ( y - 4 ); + + V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); + // every 70 tics + if (( leveltime % 70 ) == 0) + { + alagles_timer = 9; + } + if (alagles_timer > 0) + { + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); + if (( leveltime % 2 ) == 0) + alagles_timer--; + } + else + V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); + + y2 += SHORT (kp_alagles[0]->height) + 1; + } + + if (scorelines > 8) + V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + else + V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); + + if (players[tab[i].num].mo->color) + { + colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); + if (players[tab[i].num].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); + + V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap); + /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 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++) + { + bumperx += 5; + V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap); + } + }*/ + } + + if (tab[i].num == whiteplayer) + V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); + + if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0) + V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); + else + { + INT32 pos = players[tab[i].num].kartstuff[k_position]; + if (pos < 0 || pos > MAXPLAYERS) + pos = 0; + // Draws the little number over the face + V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]); + } + + if (G_RaceGametype()) + { +#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) + if (scorelines > 8) + { + if (players[tab[i].num].exiting) + V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); + else if (players[tab[i].num].pflags & PF_TIMEOVER) + V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST."); + else if (circuitmap) + V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count)); + } + else + { + if (players[tab[i].num].exiting) + V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime)); + else if (players[tab[i].num].pflags & PF_TIMEOVER) + V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST."); + else if (circuitmap) + V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count)); + } +#undef timestring + } + else + V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); + + y += 18; + if (i == 7) + { + y = 33; + x = (BASEVIDWIDTH/2) + 4; + } + } +} + +#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) + +static void K_drawKartLapsAndRings(void) +{ + const boolean uselives = G_GametypeUsesLives(); + SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; + INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; + UINT8 rn[2]; + INT32 ringflip = 0; + UINT8 *ringmap = NULL; + boolean colorring = false; + INT32 ringx = 0; + + rn[0] = ((abs(stplyr->kartstuff[k_rings]) / 10) % 10); + rn[1] = (abs(stplyr->kartstuff[k_rings]) % 10); + + if (stplyr->kartstuff[k_rings] <= 0 && (leveltime/5 & 1)) // In debt + { + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); + colorring = true; + } + else if (stplyr->kartstuff[k_rings] >= 20) // Maxed out + ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); + + if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) + { + ringflip = V_FLIP; + ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; + ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); + } + + if (r_splitscreen > 1) + { + INT32 fx = 0, fy = 0, fr = 0; + INT32 flipflag = 0; + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = LAPS_X; + fy = LAPS_Y; + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = LAPS_X; + fy = LAPS_Y; + splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN; + } + else // else, that means we're P2 or P4. + { + fx = LAPS2_X; + fy = LAPS2_Y; + splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN; + flipflag = V_FLIP; // make the string right aligned and other shit + } + } + + fr = fx; + + // Laps + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]); + + V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_splitlapflag); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash); + + if (cv_numlaps.value >= 10) + { + UINT8 ln[2]; + ln[0] = ((stplyr->laps / 10) % 10); + ln[1] = (stplyr->laps % 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(cv_numlaps.value) / 10) % 10); + ln[1] = (abs(cv_numlaps.value) % 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->laps) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(cv_numlaps.value) % 10]); + } + + // Rings + if (!uselives) + { + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[1]); + if (flipflag) + fr += 15; + } + else + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[0]->width) - 3) : 0), fy-10, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]); + + V_DrawMappedPatch(fr+ringx, fy-13, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL)); + + if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt + V_DrawMappedPatch(fr+7, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminussmall, ringmap); + + V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); + V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); + + // SPB ring lock + if (stplyr->kartstuff[k_ringlock]) + V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); + + // Lives + if (uselives) + { + UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); + V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|V_SLIDEIN|splitflags, facemmapprefix[stplyr->skin], colormap); + V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow + } + } + else + { + // 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)); + + // Rings + if (!uselives) + V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[1]); + else + V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringsticker[0]); + + V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); + + if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt + { + V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminus, ringmap); + V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap); + } + else + { + V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[0]], ringmap); + V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[rn[1]], ringmap); + } + + // SPB ring lock + if (stplyr->kartstuff[k_ringlock]) + V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); + + // Lives + if (uselives) + { + UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); + V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|V_SLIDEIN|splitflags, facerankprefix[stplyr->skin], colormap); + V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow + } + } +} + +#undef RINGANIM_FLIPFRAME + +static void K_drawKartSpeedometer(void) +{ + static fixed_t convSpeed; + UINT8 labeln = 0; + UINT8 numbers[3]; + INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; + UINT8 battleoffset = 0; + + if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! + { + switch (cv_kartspeedometer.value) + { + case 1: // Sonic Drift 2 style percentage + default: + convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) + labeln = 0; + break; + case 2: // Kilometers + convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058 + labeln = 1; + break; + case 3: // Miles + convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774 + labeln = 2; + break; + case 4: // Fracunits + convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT; // 1.0. duh. + labeln = 3; + break; + } + } + + // Don't overflow + if (convSpeed > 999) + convSpeed = 999; + + numbers[0] = ((convSpeed / 100) % 10); + numbers[1] = ((convSpeed / 10) % 10); + numbers[2] = (convSpeed % 10); + + if (G_BattleGametype()) + battleoffset = 8; + + 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]]); + V_DrawScaledPatch(LAPS_X+13, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[1]]); + V_DrawScaledPatch(LAPS_X+19, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[2]]); + V_DrawScaledPatch(LAPS_X+29, LAPS_Y-25 + battleoffset, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometerlabel[labeln]); +} + +static void K_drawKartBumpersOrKarma(void) +{ + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); + INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; + + if (r_splitscreen > 1) + { + INT32 fx = 0, fy = 0; + INT32 flipflag = 0; + + // pain and suffering defined below + if (r_splitscreen < 2) // don't change shit for THIS splitscreen. + { + fx = LAPS_X; + fy = LAPS_Y; + } + else + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = LAPS_X; + fy = LAPS_Y; + splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + } + else // else, that means we're P2 or P4. + { + fx = LAPS2_X; + fy = LAPS2_Y; + splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + flipflag = V_FLIP; // make the string right aligned and other shit + } + } + + V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash); + + if (battlecapsules) + { + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankcapsule, NULL); + + if (numtargets > 9 || maptargets > 9) + { + UINT8 ln[2]; + ln[0] = ((numtargets / 10) % 10); + ln[1] = (numtargets % 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] = ((maptargets / 10) % 10); + ln[1] = (maptargets % 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[numtargets % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[maptargets % 10]); + } + } + else + { + if (stplyr->kartstuff[k_bumper] <= 0) + { + 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]); + } + 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]); + } + } + } + } + else + { + if (battlecapsules) + { + if (numtargets > 9 && maptargets > 9) + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulestickerwide, NULL); + else + V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulesticker, NULL); + V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", numtargets, maptargets)); + } + 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])); + } + else + { + INT32 maxbumper = K_StartingBumperCount(); + + 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)); + } + } + } +} + +static void K_drawKartWanted(void) +{ + UINT8 i, numwanted = 0; + UINT8 *colormap = NULL; + INT32 basex = 0, basey = 0; + + if (stplyr != &players[displayplayers[0]]) + return; + + for (i = 0; i < 4; i++) + { + if (battlewanted[i] == -1) + break; + numwanted++; + } + + if (numwanted <= 0) + return; + + // set X/Y coords depending on splitscreen. + if (r_splitscreen < 3) // 1P and 2P use the same code. + { + basex = WANT_X; + basey = WANT_Y; + if (r_splitscreen == 2) + { + basey += 16; // slight adjust for 3P + basex -= 6; + } + } + else if (r_splitscreen == 3) // 4P splitscreen... + { + basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen + basey = BASEVIDHEIGHT - 55; + //basey2 = 4; + } + + if (battlewanted[0] != -1) + colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE); + V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap); + /*if (basey2) + V_DrawFixedPatch(basex< 1 ? 13 : 8), y = basey+(r_splitscreen > 1 ? 16 : 21); + fixed_t scale = FRACUNIT/2; + player_t *p = &players[battlewanted[i]]; + + if (battlewanted[i] == -1) + break; + + if (numwanted == 1) + scale = FRACUNIT; + else + { + if (i & 1) + x += 16; + if (i > 1) + y += 16; + } + + if (players[battlewanted[i]].skincolor) + { + colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE); + V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap); + /*if (basey2) // again with 4p stuff + V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);*/ + } + } +} + +static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) +{ + const INT32 swhalf = (BASEVIDWIDTH / 2); + const fixed_t swhalffixed = swhalf * FRACUNIT; + + const INT32 shhalf = (BASEVIDHEIGHT / 2); + const fixed_t shhalffixed = shhalf * FRACUNIT; + + INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); + fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); + fixed_t factor = INT32_MAX; + + if (abs(anglediff) > ANGLE_90) + { + if (hud_x != NULL) + { + *hud_x = -BASEVIDWIDTH * FRACUNIT; + } + + if (hud_y != NULL) + { + *hud_y = -BASEVIDWIDTH * FRACUNIT; + } + + //*hud_scale = FRACUNIT; + return; + } + + factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); + +#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) + + if (hud_x != NULL) + { + *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; + + if (encoremode) + { + *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; + } + + if (r_splitscreen >= 2) + { + *hud_x /= 2; + + if (camnum & 1) + { + *hud_x += swhalffixed; + } + } + } + + if (hud_y != NULL) + { + *hud_y = campos->z - point->z; + *hud_y = FixedDiv(*hud_y, FixedMul(factor, distance)); + *hud_y = (*hud_y * swhalf) + shhalffixed; + *hud_y = *hud_y + NEWTAN(camaim) * swhalf; + + if (r_splitscreen >= 1) + { + *hud_y /= 2; + + if ((r_splitscreen == 1 && camnum == 1) + || (r_splitscreen > 1 && camnum > 1)) + { + *hud_y += shhalffixed; + } + } + } + + //*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance)); + +#undef NEWTAN +} + +static void K_drawKartPlayerCheck(void) +{ + const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; + INT32 splitflags = V_SNAPTOBOTTOM|V_SPLITSCREEN; + + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { + return; + } + + if (stplyr->spectator || stplyr->awayviewtics) + { + return; + } + + if (stplyr->cmd.buttons & BT_LOOKBACK) + { + return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = stplyr->mo->x; + c.y = stplyr->mo->y; + c.z = stplyr->mo->z; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *checkplayer = &players[i]; + fixed_t distance = maxdistance+1; + UINT8 *colormap = NULL; + UINT8 pnum = 0; + fixed_t x = 0; + vertex_t v; + + if (!playeringame[i] || checkplayer->spectator) + { + // Not in-game + continue; + } + + if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo)) + { + // No object + continue; + } + + if (checkplayer == stplyr) + { + // This is you! + continue; + } + + v.x = checkplayer->mo->x; + v.y = checkplayer->mo->y; + v.z = checkplayer->mo->z; + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) + { + pnum++; // white frames + } + + if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0) + { + pnum += 4; + } + else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer]) + { + pnum += 2; + } + + K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); + + colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); + V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|V_SLIDEIN|splitflags, kp_check[pnum], colormap); + } +} + +static boolean K_ShowPlayerNametag(player_t *p) +{ + if (demo.playback == true && demo.freecam == true) + { + return true; + } + + if (stplyr == p) + { + return false; + } + + if (G_RaceGametype()) + { + if ((p->kartstuff[k_position] < stplyr->kartstuff[k_position]-2) + || (p->kartstuff[k_position] > stplyr->kartstuff[k_position]+2)) + { + return false; + } + } + + return true; +} + +static void K_drawKartNameTags(void) +{ + const fixed_t maxdistance = 8192*mapobjectscale; + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; + + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { + return; + } + + if (stplyr->awayviewtics) + { + return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = thiscam->x; + c.y = thiscam->y; + c.z = thiscam->z; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *ntplayer = &players[i]; + fixed_t distance = maxdistance+1; + + fixed_t x = -BASEVIDWIDTH * FRACUNIT; + fixed_t y = -BASEVIDWIDTH * FRACUNIT; + + vertex_t v; + + if (!playeringame[i] || ntplayer->spectator) + { + // Not in-game + continue; + } + + if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) + { + // No object + continue; + } + + if (!P_CheckSight(stplyr->mo, ntplayer->mo)) + { + // Can't see + continue; + } + + if (!(demo.playback == true && demo.freecam == true)) + { + UINT8 j; + + for (j = 0; j <= r_splitscreen; j++) + { + if (ntplayer == &players[displayplayers[j]]) + { + break; + } + } + + if (j <= r_splitscreen) + { + // This is a player that's being shown on this computer + // (Remove whenever we get splitscreen ABCD indicators) + continue; + } + } + + v.x = ntplayer->mo->x; + v.y = ntplayer->mo->y; + v.z = ntplayer->mo->z; + + if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) + { + v.z += ntplayer->mo->height; + } + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); + + if (x == -BASEVIDWIDTH * FRACUNIT) + { + // Off-screen + continue; + } + + if (ntplayer->bot) + { + if (ntplayer->botvars.rival == true) + { + UINT8 blink = ((leveltime / 7) & 1); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); + } + } + else if (netgame || demo.playback) + { + if (K_ShowPlayerNametag(ntplayer) == true) + { + INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); + INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); + UINT8 *colormap = V_GetStringColormap(clr); + INT32 barx = 0, bary = 0, barw = 0; + + // Since there's no "V_DrawFixedFill", and I don't feel like making it, + // fuck it, we're gonna just V_NOSCALESTART hack it + barw = (namelen * vid.dupx); + + barx = (x * vid.dupx) / FRACUNIT; + bary = (y * vid.dupy) / FRACUNIT; + + barx += (6 * vid.dupx); + bary -= (16 * vid.dupx); + + // Center it if necessary + if (vid.width != BASEVIDWIDTH * vid.dupx) + { + barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; + } + + if (vid.height != BASEVIDHEIGHT * vid.dupy) + { + bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; + } + + // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. + V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); + V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); + // END DRAWFILL DUMBNESS + + // Draw the stem + V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); + + // Draw the name itself + V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); + } + } + } +} + +static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) +{ + // amnum xpos & ypos are the icon's speed around the HUD. + // The number being divided by is for how fast it moves. + // The higher the number, the slower it moves. + + // am xpos & ypos are the icon's starting position. Withouht + // it, they wouldn't 'spawn' on the top-right side of the HUD. + + fixed_t amnumxpos, amnumypos; + INT32 amxpos, amypos; + + node_t *bsp = &nodes[numnodes-1]; + fixed_t maxx, minx, maxy, miny; + + fixed_t mapwidth, mapheight; + fixed_t xoffset, yoffset; + fixed_t xscale, yscale, zoom; + + maxx = maxy = INT32_MAX; + minx = miny = INT32_MIN; + minx = bsp->bbox[0][BOXLEFT]; + maxx = bsp->bbox[0][BOXRIGHT]; + miny = bsp->bbox[0][BOXBOTTOM]; + maxy = bsp->bbox[0][BOXTOP]; + + if (bsp->bbox[1][BOXLEFT] < minx) + minx = bsp->bbox[1][BOXLEFT]; + if (bsp->bbox[1][BOXRIGHT] > maxx) + maxx = bsp->bbox[1][BOXRIGHT]; + if (bsp->bbox[1][BOXBOTTOM] < miny) + miny = bsp->bbox[1][BOXBOTTOM]; + if (bsp->bbox[1][BOXTOP] > maxy) + maxy = bsp->bbox[1][BOXTOP]; + + // You might be wondering why these are being bitshift here + // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... + // map boundaries and sizes will ALWAYS be whole numbers thankfully + // later calculations take into consideration that these are actually not in terms of FRACUNIT though + minx >>= FRACBITS; + maxx >>= FRACBITS; + miny >>= FRACBITS; + maxy >>= FRACBITS; + + mapwidth = maxx - minx; + mapheight = maxy - miny; + + // These should always be small enough to be bitshift back right now + xoffset = (minx + mapwidth/2)<width, mapwidth); + yscale = FixedDiv(AutomapPic->height, mapheight); + zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); + + amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); + amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); + + if (encoremode) + amnumxpos = -amnumxpos; + + amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width/2); + y = MINI_Y - (AutomapPic->height/2); + + if (timeinmap > 105) + { + minimaptrans = cv_kartminimap.value; + if (timeinmap <= 113) + minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); + if (!minimaptrans) + return; + } + else + return; + + minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); + else + V_DrawScaledPatch(x, y, splitflags, AutomapPic); + + if (r_splitscreen != 2) + { + splitflags &= ~minimaptrans; + splitflags |= V_HUDTRANSHALF; + } + + // let offsets transfer to the heads, too! + if (encoremode) + x += SHORT(AutomapPic->leftoffset); + else + x -= SHORT(AutomapPic->leftoffset); + y -= SHORT(AutomapPic->topoffset); + + // Draw the super item in Battle + if (G_BattleGametype() && battleovertime.enabled) + { + if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) + { + const INT32 prevsplitflags = splitflags; + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); + K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); + splitflags = prevsplitflags; + } + } + + // initialize + for (i = 0; i < 4; i++) + localplayers[i] = -1; + + if (G_RaceGametype()) + hyu *= 2; // double in race + + // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) + if (ghosts) + { + demoghost *g = ghosts; + while (g) + { + if (g->mo->skin) + skin = ((skin_t*)g->mo->skin)-skins; + else + skin = 0; + if (g->mo->color) + { + if (g->mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); + } + else + colormap = NULL; + K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + g = g->next; + } + + if (!stplyr->mo || stplyr->spectator || stplyr->exiting) + return; + + localplayers[numlocalplayers] = stplyr-players; + numlocalplayers++; + } + else + { + for (i = MAXPLAYERS-1; i >= 0; i--) + { + if (!playeringame[i]) + continue; + if (!players[i].mo || players[i].spectator || players[i].exiting) + continue; + + if (i != displayplayers[0] || r_splitscreen) + { + if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) + continue; + + if (players[i].kartstuff[k_hyudorotimer] > 0) + { + if (!((players[i].kartstuff[k_hyudorotimer] < TICRATE/2 + || players[i].kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)) + && !(leveltime & 1))) + continue; + } + } + + if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) + { + // Draw display players on top of everything else + localplayers[numlocalplayers] = i; + numlocalplayers++; + continue; + } + + if (players[i].mo->skin) + skin = ((skin_t*)players[i].mo->skin)-skins; + else + skin = 0; + + if (players[i].mo->color) + { + if (players[i].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + // Target reticule + if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) + K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } + } + + // draw SPB(s?) + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + if (mobj->type == MT_SPB) + { + colormap = NULL; + + if (mobj->target && !P_MobjWasRemoved(mobj->target)) + { + if (mobj->player && mobj->player->skincolor) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE); + else if (mobj->color) + colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); + } + + K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); + } + } + + // draw our local players here, opaque. + splitflags &= ~V_HUDTRANSHALF; + splitflags |= V_HUDTRANS; + + for (i = 0; i < numlocalplayers; i++) + { + if (i == -1) + continue; // this doesn't interest us + + if (players[localplayers[i]].mo->skin) + skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; + else + skin = 0; + + if (players[localplayers[i]].mo->color) + { + if (players[localplayers[i]].mo->colorized) + colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); + else + colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); + } + else + colormap = NULL; + + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); + + // Target reticule + if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) + K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); + } +} + +static void K_drawKartStartCountdown(void) +{ + INT32 pnum = 0, splitflags = V_SPLITSCREEN; // 3 + + if (leveltime >= starttime-(2*TICRATE)) // 2 + pnum++; + if (leveltime >= starttime-TICRATE) // 1 + pnum++; + if (leveltime >= starttime) // GO! + pnum++; + if ((leveltime % (2*5)) / 5) // blink + pnum += 4; + if (r_splitscreen) // splitscreen + pnum += 8; + + V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); +} + +static void K_drawKartFinish(void) +{ + INT32 pnum = 0, splitflags = V_SPLITSCREEN; + + if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE) + return; + + if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink + pnum = 1; + + if (r_splitscreen > 1) // 3/4p, stationary FIN + { + pnum += 2; + V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]); + return; + } + + //else -- 1/2p, scrolling FINISH + { + INT32 x, xval; + + if (r_splitscreen) // wide splitscreen + pnum += 4; + + x = ((vid.width<width)<karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; + + if (r_splitscreen && stplyr == &players[displayplayers[1]]) + x = -x; + + V_DrawFixedPatch(x + (STCD_X<>1), + (STCD_Y<height)<<(FRACBITS-1)), + FRACUNIT, + splitflags, kp_racefinish[pnum], NULL); + } +} + +static void K_drawBattleFullscreen(void) +{ + INT32 x = BASEVIDWIDTH/2; + INT32 y = -64+(stplyr->karthud[khud_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen + INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead + fixed_t scale = FRACUNIT; + boolean drawcomebacktimer = true; // lazy hack because it's cleaner in the long run. +#ifdef HAVE_BLUA + if (!LUA_HudEnabled(hud_battlecomebacktimer)) + drawcomebacktimer = false; +#endif + + if (r_splitscreen) + { + if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (r_splitscreen > 1 && (stplyr == &players[displayplayers[2]] + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)))) + { + y = 232-(stplyr->karthud[khud_cardanimation]/2); + splitflags = V_SNAPTOBOTTOM; + } + else + y = -32+(stplyr->karthud[khud_cardanimation]/2); + + if (r_splitscreen > 1) + { + scale /= 2; + + if (stplyr == &players[displayplayers[1]] + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) + x = 3*BASEVIDWIDTH/4; + else + x = BASEVIDWIDTH/4; + } + else + { + if (stplyr->exiting) + { + if (stplyr == &players[displayplayers[1]]) + x = BASEVIDWIDTH-96; + else + x = 96; + } + else + scale /= 2; + } + } + + if (stplyr->exiting) + { + if (stplyr == &players[displayplayers[0]]) + V_DrawFadeScreen(0xFF00, 16); + if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) + { + patch_t *p = kp_battlecool; + + if (K_IsPlayerLosing(stplyr)) + p = kp_battlelose; + else if (stplyr->kartstuff[k_position] == 1) + p = kp_battlewin; + + V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator && drawcomebacktimer) + { + UINT16 t = stplyr->kartstuff[k_comebacktimer]/(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; + + txoff = adjust; + + while (t) + { + txoff += adjust; + t /= 10; + } + + if (r_splitscreen) + { + if (r_splitscreen > 1) + ty = (BASEVIDHEIGHT/4)+33; + if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) + ty += (BASEVIDHEIGHT/2); + } + else + V_DrawFadeScreen(0xFF00, 16); + + if (!comebackshowninfo) + V_DrawFixedPatch(x< 1) + V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE)); + else + { + V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE)); + } + } + + if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY? + { + UINT8 i; + + // check to see if there's anyone else at all + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == displayplayers[0]) + continue; + if (playeringame[i] && !stplyr->spectator) + return; + } + +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_freeplay)) +#endif + K_drawKartFreePlay(leveltime); + } +} + +static void K_drawKartFirstPerson(void) +{ + static INT32 pnum[4], turn[4], drift[4]; + INT32 pn = 0, tn = 0, dr = 0; + INT32 target = 0, splitflags = V_SNAPTOBOTTOM|V_SPLITSCREEN; + INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT; + fixed_t scale; + UINT8 *colmap = NULL; + ticcmd_t *cmd = &stplyr->cmd; + + if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) + return; + + if (stplyr == &players[displayplayers[1]] && r_splitscreen) + { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } + else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } + else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) + { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } + else + { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } + + if (r_splitscreen) + { + y >>= 1; + if (r_splitscreen > 1) + x >>= 1; + } + + { + if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) + y++; + + if (stplyr->mo->drawflags & MFD_TRANSMASK) + splitflags |= ((stplyr->mo->drawflags & MFD_TRANSMASK) >> MFD_TRANSSHIFT) << FF_TRANSSHIFT; + else if (stplyr->mo->frame & FF_TRANSMASK) + splitflags |= (stplyr->mo->frame & FF_TRANSMASK); + } + + if (cmd->driftturn > 400) // strong left turn + target = 2; + else if (cmd->driftturn < -400) // strong right turn + target = -2; + else if (cmd->driftturn > 0) // weak left turn + target = 1; + else if (cmd->driftturn < 0) // weak right turn + target = -1; + else // forward + target = 0; + + if (encoremode) + target = -target; + + if (pn < target) + pn++; + else if (pn > target) + pn--; + + if (pn < 0) + splitflags |= V_FLIP; // right turn + + target = abs(pn); + if (target > 2) + target = 2; + + x <<= FRACBITS; + y <<= FRACBITS; + + if (tn != cmd->driftturn/50) + tn -= (tn - (cmd->driftturn/50))/8; + + if (dr != stplyr->kartstuff[k_drift]*16) + dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8; + + if (r_splitscreen == 1) + { + scale = (2*FRACUNIT)/3; + y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view) + } + else if (r_splitscreen) + scale = FRACUNIT/2; + else + scale = FRACUNIT; + + if (stplyr->mo) + { + UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->kartstuff[k_driftcharge]); + const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; + // yes, the following is correct. no, you do not need to swap the x and y. + fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); + fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); + + if (r_splitscreen) + xoffs = FixedMul(xoffs, scale); + + xoffs -= (tn)*scale; + xoffs -= (dr)*scale; + + if (stplyr->frameangle == stplyr->mo->angle) + { + const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); + + if (mag < FRACUNIT) + { + xoffs = FixedMul(xoffs, mag); + if (!r_splitscreen) + yoffs = FixedMul(yoffs, mag); + } + } + + if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! + yoffs += stplyr->mo->momz/3; + + if (encoremode) + x -= xoffs; + else + x += xoffs; + if (!r_splitscreen) + y += yoffs; + + + if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks! + colmap = R_GetTranslationColormap(TC_RAINBOW, driftcolor, GTC_CACHE); + else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink! + colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE); + } + + V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap); + + if (stplyr == &players[displayplayers[1]] && r_splitscreen) + { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } + else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) + { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } + else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) + { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } + else + { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } +} + +// doesn't need to ever support 4p +static void K_drawInput(void) +{ + static INT32 pn = 0; + INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT); + INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col; + const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5]; + const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9]; + ticcmd_t *cmd = &stplyr->cmd; + + if (timeinmap <= 105) + return; + + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 105); + offs = 64; + while (count-- > 0) + offs >>= 1; + x += offs; + } + +#define BUTTW 8 +#define BUTTH 11 + +#define drawbutt(xoffs, butt, symb)\ + if (stplyr->cmd.buttons & butt)\ + {\ + offs = 2;\ + col = accent1;\ + }\ + else\ + {\ + offs = 0;\ + col = accent2;\ + V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\ + }\ + V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\ + V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn + target = 0; + else // turning of multiple strengths! + { + target = ((abs(cmd->driftturn) - 1)/125)+1; + if (target > 4) + target = 4; + if (cmd->driftturn < 0) + target = -target; + } + + if (pn != target) + { + if (abs(pn - target) == 1) + pn = target; + else if (pn < target) + pn += 2; + else //if (pn > target) + pn -= 2; + } + + if (pn < 0) + { + splitflags |= V_FLIP; // right turn + x--; + } + + target = abs(pn); + if (target > 4) + target = 4; + + if (!stplyr->skincolor) + V_DrawFixedPatch(x<skincolor, GTC_CACHE); + V_DrawFixedPatch(x<karthud[khud_lapanimation]; + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); + + V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, + (48 - (32*max(0, progress-76)))*FRACUNIT, + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap); + + if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3) + { + V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, + (48 - (32*max(0, progress-76)) + + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT, + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); + } + + if (stplyr->laps == (UINT8)(cv_numlaps.value)) + { + V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_final[min(progress/2, 10)], NULL); + + if (progress/2-12 >= 0) + { + V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_lap[min(progress/2-12, 6)], NULL); + } + } + else + { + V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_lap[min(progress/2, 6)], NULL); + + if (progress/2-8 >= 0) + { + V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_number[(((UINT32)stplyr->laps) / 10)][min(progress/2-8, 2)], NULL); + + if (progress/2-10 >= 0) + { + V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221 + 30*FRACUNIT, // 24 + FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, + kp_lapanim_number[(((UINT32)stplyr->laps) % 10)][min(progress/2-10, 2)], NULL); + } + } + } +} + +void K_drawKartFreePlay(UINT32 flashtime) +{ + // no splitscreen support because it's not FREE PLAY if you have more than one player in-game + // (you fool, you can take splitscreen online. :V) + + if ((flashtime % TICRATE) < TICRATE/2) + return; + + V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy + LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); +} + +static void +Draw_party_ping (int ss, INT32 snap) +{ + HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|V_SLIDEIN|V_SPLITSCREEN|snap); +} + +static void +K_drawMiniPing (void) +{ + if (cv_showping.value) + { + switch (r_splitscreen) + { + case 3: + Draw_party_ping(3, V_SNAPTORIGHT); + /*FALLTHRU*/ + case 2: + Draw_party_ping(2, 0); + Draw_party_ping(1, V_SNAPTORIGHT); + Draw_party_ping(0, 0); + break; + case 1: + Draw_party_ping(1, V_SNAPTORIGHT); + Draw_party_ping(0, V_SNAPTORIGHT); + break; + } + } +} + +static void K_drawDistributionDebugger(void) +{ + patch_t *items[NUMKARTRESULTS] = { + kp_sadface[1], + kp_sneaker[1], + kp_rocketsneaker[1], + kp_invincibility[7], + kp_banana[1], + kp_eggman[1], + kp_orbinaut[4], + kp_jawz[1], + kp_mine[1], + kp_ballhog[1], + kp_selfpropelledbomb[1], + kp_grow[1], + kp_shrink[1], + kp_thundershield[1], + kp_bubbleshield[1], + kp_flameshield[1], + kp_hyudoro[1], + kp_pogospring[1], + kp_superring[1], + kp_kitchensink[1], + + kp_sneaker[1], + kp_banana[1], + kp_banana[1], + kp_orbinaut[4], + kp_orbinaut[4], + kp_jawz[1] + }; + UINT8 useodds = 0; + UINT8 pingame = 0, bestbumper = 0; + UINT32 pdis = 0; + INT32 i; + INT32 x = -9, y = -9; + boolean spbrush = false; + + if (stplyr != &players[displayplayers[0]]) // only for p1 + return; + + // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + pingame++; + if (players[i].kartstuff[k_bumper] > bestbumper) + bestbumper = players[i].kartstuff[k_bumper]; + } + + // lovely double loop...... + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator + && players[i].kartstuff[k_position] == 1) + { + // This player is first! Yay! + pdis = stplyr->distancetofinish - players[i].distancetofinish; + break; + } + } + + if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items + pdis = (15 * pdis) / 14; + + if (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell + { + pdis = (3 * pdis) / 2; + spbrush = true; + } + + if (stplyr->bot && stplyr->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count + + useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); + + for (i = 1; i < NUMKARTRESULTS; i++) + { + const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); + if (itemodds <= 0) + continue; + + V_DrawScaledPatch(x, y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP, items[i]); + V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP, va("%d", itemodds)); + + // Display amount for multi-items + if (i >= NUMKARTITEMS) + { + INT32 amount; + switch (i) + { + case KRITEM_TENFOLDBANANA: + amount = 10; + break; + case KRITEM_QUADORBINAUT: + amount = 4; + break; + case KRITEM_DUALJAWZ: + amount = 2; + break; + default: + amount = 3; + break; + } + V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP, va("x%d", amount)); + } + + x += 32; + if (x >= 297) + { + x = -9; + y += 32; + } + } + + V_DrawString(0, 0, V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP, va("USEODDS %d", useodds)); +} + +static void K_drawCheckpointDebugger(void) +{ + if (stplyr != &players[displayplayers[0]]) // only for p1 + return; + + if (stplyr->starpostnum == numstarposts) + V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts)); + else + V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts)); +} + +static void K_DrawWaypointDebugger(void) +{ + if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) + { + V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); + V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); + } +} + +void K_drawKartHUD(void) +{ + boolean isfreeplay = false; + boolean battlefullscreen = false; + boolean freecam = demo.freecam; //disable some hud elements w/ freecam + UINT8 i; + + // Define the X and Y for each drawn object + // This is handled by console/menu values + K_initKartHUD(); + + // Draw that fun first person HUD! Drawn ASAP so it looks more "real". + for (i = 0; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]] && !camera[i].chase && !freecam) + K_drawKartFirstPerson(); + } + + // Draw full screen stuff that turns off the rest of the HUD + if (mapreset && stplyr == &players[displayplayers[0]]) + { + K_drawChallengerScreen(); + return; + } + + battlefullscreen = ((G_BattleGametype()) + && (stplyr->exiting + || (stplyr->kartstuff[k_bumper] <= 0 + && stplyr->kartstuff[k_comebacktimer] + && comeback + && stplyr->playerstate == PST_LIVE))); + + if (!demo.title && (!battlefullscreen || r_splitscreen)) + { + // Draw the CHECK indicator before the other items, so it's overlapped by everything else +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_check)) // delete lua when? +#endif + if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam) + K_drawKartPlayerCheck(); + + // nametags +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_names)) +#endif + K_drawKartNameTags(); + + // Draw WANTED status + if (G_BattleGametype()) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_wanted)) +#endif + K_drawKartWanted(); + } + + if (cv_kartminimap.value) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_minimap)) +#endif + K_drawKartMinimap(); + } + } + + if (battlefullscreen && !freecam) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_battlefullscreen)) +#endif + K_drawBattleFullscreen(); + return; + } + + // Draw the item window +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_item) && !freecam) +#endif + K_drawKartItem(); + + // If not splitscreen, draw... + if (!r_splitscreen && !demo.title) + { + // Draw the timestamp +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_time)) +#endif + K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0); + + if (!modeattacking) + { + // The top-four faces on the left + /*#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_minirankings)) + #endif*/ + isfreeplay = K_drawKartPositionFaces(); + } + } + + if (!stplyr->spectator && !demo.freecam) // Bottom of the screen elements, don't need in spectate mode + { + // Draw the speedometer + if (cv_kartspeedometer.value && !r_splitscreen) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_speedometer)) +#endif + K_drawKartSpeedometer(); + } + + if (demo.title) // Draw title logo instead in demo.titles + { + INT32 x = BASEVIDWIDTH - 32, y = 128, offs; + + if (r_splitscreen == 3) + { + x = BASEVIDWIDTH/2 + 10; + y = BASEVIDHEIGHT/2 - 30; + } + + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 104); + offs = 256; + while (count-- > 0) + offs >>= 1; + x += offs; + } + + V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); + V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); + } + else if (G_RaceGametype()) // Race-only elements + { + // Draw the lap counter +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_gametypeinfo)) +#endif + K_drawKartLapsAndRings(); + + if (isfreeplay) + ; + else if (!modeattacking) + { + // Draw the numerical position +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_position)) +#endif + K_DrawKartPositionNum(stplyr->kartstuff[k_position]); + } + else //if (!(demo.playback && hu_showscores)) + { + // Draw the input UI +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_position)) +#endif + K_drawInput(); + } + } + else if (G_BattleGametype()) // Battle-only + { + // Draw the hits left! +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_gametypeinfo)) +#endif + K_drawKartBumpersOrKarma(); + } + } + + // Draw the countdowns after everything else. + if (leveltime >= starttime-(3*TICRATE) + && leveltime < starttime+TICRATE) + K_drawKartStartCountdown(); + else if (racecountdown && (!r_splitscreen || !stplyr->exiting)) + { + char *countstr = va("%d", racecountdown/TICRATE); + + if (r_splitscreen > 1) + V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, V_SPLITSCREEN, countstr); + else + { + INT32 karlen = strlen(countstr)*6; // half of 12 + V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, V_SPLITSCREEN, countstr); + } + } + + // Race overlays + if (G_RaceGametype() && !freecam) + { + if (stplyr->exiting) + K_drawKartFinish(); + else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen) + K_drawLapStartAnim(); + } + + if (modeattacking || freecam) // everything after here is MP and debug only + return; + + if (G_BattleGametype() && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * + V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); + + // Draw FREE PLAY. + if (isfreeplay && !stplyr->spectator && timeinmap > 113) + { +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_freeplay)) +#endif + K_drawKartFreePlay(leveltime); + } + + if (r_splitscreen == 0 && stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) + { + V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); + } + + if (netgame && r_splitscreen) + { + K_drawMiniPing(); + } + + if (cv_kartdebugdistribution.value) + K_drawDistributionDebugger(); + + if (cv_kartdebugcheckpoint.value) + K_drawCheckpointDebugger(); + + if (cv_kartdebugnodes.value) + { + UINT8 p; + for (p = 0; p < MAXPLAYERS; p++) + V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency)); + } + + if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin) + { + INT32 x = 0, y = 0; + UINT8 c; + + for (c = 1; c < MAXSKINCOLORS; c++) + { + UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, c, GTC_CACHE); + V_DrawFixedPatch(x<>1, 0, facewantprefix[stplyr->skin], cm); + + x += 16; + if (x > BASEVIDWIDTH-16) + { + x = 0; + y += 16; + } + } + } + + K_DrawWaypointDebugger(); +} diff --git a/src/k_hud.h b/src/k_hud.h new file mode 100644 index 000000000..27f21bd23 --- /dev/null +++ b/src/k_hud.h @@ -0,0 +1,28 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Kart Krew +// +// 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 k_hud.h +/// \brief HUD drawing functions exclusive to Kart + +#include "doomtype.h" +#include "doomstat.h" + +#ifndef __K_HUD__ +#define __K_HUD__ + +#define RINGANIM_NUMFRAMES 10 +#define RINGANIM_DELAYMAX 5 + +void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 dupy); +const char *K_GetItemPatch(UINT8 item, boolean tiny); +void K_LoadKartHUDGraphics(void); +void K_drawKartHUD(void); +void K_drawKartFreePlay(UINT32 flashtime); +void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode); + +#endif diff --git a/src/k_kart.c b/src/k_kart.c index 8e7892e19..837a76945 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -30,6 +30,7 @@ #include "k_waypoint.h" #include "k_bot.h" +#include "k_hud.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -417,7 +418,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) \return void */ -static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) +INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) { INT32 newodds; INT32 i; @@ -614,7 +615,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp //{ SRB2kart Roulette Code - Distance Based, yes waypoints -static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) +UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush) { UINT8 i; UINT8 n = 0; @@ -5225,9 +5226,6 @@ static void K_UpdateInvincibilitySounds(player_t *player) #undef STOPTHIS } -#define RINGANIM_NUMFRAMES 10 -#define RINGANIM_DELAYMAX 5 - void K_KartPlayerHUDUpdate(player_t *player) { if (player->karthud[khud_lapanimation]) @@ -7770,3651 +7768,3 @@ void K_CheckSpectateStatus(void) } //} - -//{ SRB2kart HUD Code - -#define NUMPOSNUMS 10 -#define NUMPOSFRAMES 7 // White, three blues, three reds -#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple - -//{ Patch Definitions -static patch_t *kp_nodraw; - -static patch_t *kp_timesticker; -static patch_t *kp_timestickerwide; -static patch_t *kp_lapsticker; -static patch_t *kp_lapstickerwide; -static patch_t *kp_lapstickernarrow; -static patch_t *kp_splitlapflag; -static patch_t *kp_bumpersticker; -static patch_t *kp_bumperstickerwide; -static patch_t *kp_capsulesticker; -static patch_t *kp_capsulestickerwide; -static patch_t *kp_karmasticker; -static patch_t *kp_splitkarmabomb; -static patch_t *kp_timeoutsticker; - -static patch_t *kp_startcountdown[16]; -static patch_t *kp_racefinish[6]; - -static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; -static patch_t *kp_winnernum[NUMPOSFRAMES]; - -static patch_t *kp_facenum[MAXPLAYERS+1]; -static patch_t *kp_facehighlight[8]; - -static patch_t *kp_spbminimap; - -static patch_t *kp_ringsticker[2]; -static patch_t *kp_ringstickersplit[4]; -static patch_t *kp_ring[6]; -static patch_t *kp_smallring[6]; -static patch_t *kp_ringdebtminus; -static patch_t *kp_ringdebtminussmall; -static patch_t *kp_ringspblock[16]; -static patch_t *kp_ringspblocksmall[16]; - -static patch_t *kp_speedometersticker; -static patch_t *kp_speedometerlabel[4]; - -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_battlewin; -static patch_t *kp_battlecool; -static patch_t *kp_battlelose; -static patch_t *kp_battlewait; -static patch_t *kp_battleinfo; -static patch_t *kp_wanted; -static patch_t *kp_wantedsplit; -static patch_t *kp_wantedreticle; - -static patch_t *kp_itembg[4]; -static patch_t *kp_itemtimer[2]; -static patch_t *kp_itemmulsticker[2]; -static patch_t *kp_itemx; - -static patch_t *kp_superring[2]; -static patch_t *kp_sneaker[2]; -static patch_t *kp_rocketsneaker[2]; -static patch_t *kp_invincibility[13]; -static patch_t *kp_banana[2]; -static patch_t *kp_eggman[2]; -static patch_t *kp_orbinaut[5]; -static patch_t *kp_jawz[2]; -static patch_t *kp_mine[2]; -static patch_t *kp_ballhog[2]; -static patch_t *kp_selfpropelledbomb[2]; -static patch_t *kp_grow[2]; -static patch_t *kp_shrink[2]; -static patch_t *kp_thundershield[2]; -static patch_t *kp_bubbleshield[2]; -static patch_t *kp_flameshield[2]; -static patch_t *kp_hyudoro[2]; -static patch_t *kp_pogospring[2]; -static patch_t *kp_kitchensink[2]; -static patch_t *kp_sadface[2]; - -static patch_t *kp_check[6]; - -static patch_t *kp_rival[2]; - -static patch_t *kp_eggnum[4]; - -static patch_t *kp_flameshieldmeter[104][2]; -static patch_t *kp_flameshieldmeter_bg[16][2]; - -static patch_t *kp_fpview[3]; -static patch_t *kp_inputwheel[5]; - -static patch_t *kp_challenger[25]; - -static patch_t *kp_lapanim_lap[7]; -static patch_t *kp_lapanim_final[11]; -static patch_t *kp_lapanim_number[10][3]; -static patch_t *kp_lapanim_emblem[2]; -static patch_t *kp_lapanim_hand[3]; - -static patch_t *kp_yougotem; -static patch_t *kp_itemminimap; - -static patch_t *kp_alagles[10]; -static patch_t *kp_blagles[6]; - -static patch_t *kp_cpu; - -static patch_t *kp_nametagstem; - -void K_LoadKartHUDGraphics(void) -{ - INT32 i, j; - char buffer[9]; - - // Null Stuff - kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX); - - // Stickers - kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX); - kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX); - kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX); - kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX); - kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX); - kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX); - kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX); - kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX); - 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_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); - kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); - - // Starting countdown - kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); - kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); - kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX); - kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX); - kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX); - kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX); - kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX); - kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); - // Splitscreen - kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX); - kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX); - kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX); - kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); - kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX); - kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX); - kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); - kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); - - // Finish - kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX); - kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX); - // Splitscreen - kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX); - kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX); - // 2P splitscreen - kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX); - kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX); - - // Position numbers - sprintf(buffer, "K_POSNxx"); - for (i = 0; i < NUMPOSNUMS; i++) - { - buffer[6] = '0'+i; - for (j = 0; j < NUMPOSFRAMES; j++) - { - //sprintf(buffer, "K_POSN%d%d", i, j); - buffer[7] = '0'+j; - kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - } - - sprintf(buffer, "K_POSNWx"); - for (i = 0; i < NUMWINFRAMES; i++) - { - buffer[7] = '0'+i; - kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "OPPRNKxx"); - for (i = 0; i <= MAXPLAYERS; i++) - { - buffer[6] = '0'+(i/10); - buffer[7] = '0'+(i%10); - kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_CHILIx"); - for (i = 0; i < 8; i++) - { - buffer[7] = '0'+(i+1); - kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_spbminimap = W_CachePatchName("SPBMMAP", PU_HUDGFX); - - // Rings & Lives - kp_ringsticker[0] = W_CachePatchName("RNGBACKA", PU_HUDGFX); - kp_ringsticker[1] = W_CachePatchName("RNGBACKB", PU_HUDGFX); - - sprintf(buffer, "K_RINGx"); - for (i = 0; i < 6; i++) - { - buffer[6] = '0'+(i+1); - kp_ring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringdebtminus = W_CachePatchName("RDEBTMIN", PU_HUDGFX); - - sprintf(buffer, "SPBRNGxx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1) / 10); - buffer[7] = '0'+((i+1) % 10); - kp_ringspblock[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringstickersplit[0] = W_CachePatchName("SMRNGBGA", PU_HUDGFX); - kp_ringstickersplit[1] = W_CachePatchName("SMRNGBGB", PU_HUDGFX); - - sprintf(buffer, "K_SRINGx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '0'+(i+1); - kp_smallring[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_ringdebtminussmall = W_CachePatchName("SRDEBTMN", PU_HUDGFX); - - sprintf(buffer, "SPBRGSxx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1) / 10); - buffer[7] = '0'+((i+1) % 10); - kp_ringspblocksmall[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Speedometer - kp_speedometersticker = W_CachePatchName("K_SPDMBG", PU_HUDGFX); - - sprintf(buffer, "K_SPDMLx"); - for (i = 0; i < 4; i++) - { - buffer[7] = '0'+(i+1); - kp_speedometerlabel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Extra ranking icons - kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX); - kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX); - 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); - - // Battle graphics - kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX); - kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX); - kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX); - kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX); - kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX); - kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX); - kp_wantedsplit = W_CachePatchName("4PWANTED", PU_HUDGFX); - kp_wantedreticle = W_CachePatchName("MMAPWANT", PU_HUDGFX); - - // Kart Item Windows - kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX); - kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX); - kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX); - kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX); - kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX); - - kp_superring[0] = W_CachePatchName("K_ITRING", PU_HUDGFX); - kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX); - kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX); - - sprintf(buffer, "K_ITINVx"); - for (i = 0; i < 7; i++) - { - buffer[7] = '1'+i; - kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX); - kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX); - sprintf(buffer, "K_ITORBx"); - for (i = 0; i < 4; i++) - { - buffer[7] = '1'+i; - kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX); - kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX); - kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX); - kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX); - kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX); - kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX); - kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX); - kp_bubbleshield[0] = W_CachePatchName("K_ITBUBS", PU_HUDGFX); - kp_flameshield[0] = W_CachePatchName("K_ITFLMS", PU_HUDGFX); - kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX); - kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX); - kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX); - kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX); - - sprintf(buffer, "FSMFGxxx"); - for (i = 0; i < 104; i++) - { - buffer[5] = '0'+((i+1)/100); - buffer[6] = '0'+(((i+1)/10)%10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "FSMBG0xx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter_bg[i][0] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Splitscreen - kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX); - kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX); - kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX); - kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX); - - kp_superring[1] = W_CachePatchName("K_ISRING", PU_HUDGFX); - kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX); - kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX); - sprintf(buffer, "K_ISINVx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '1'+i; - kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX); - kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX); - kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX); - kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX); - kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX); - kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX); - kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX); - kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX); - kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX); - kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX); - kp_bubbleshield[1] = W_CachePatchName("K_ISBUBS", PU_HUDGFX); - kp_flameshield[1] = W_CachePatchName("K_ISFLMS", PU_HUDGFX); - kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX); - kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX); - kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX); - kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX); - - sprintf(buffer, "FSMFSxxx"); - for (i = 0; i < 104; i++) - { - buffer[5] = '0'+((i+1)/100); - buffer[6] = '0'+(((i+1)/10)%10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "FSMBS0xx"); - for (i = 0; i < 16; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_flameshieldmeter_bg[i][1] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // CHECK indicators - sprintf(buffer, "K_CHECKx"); - for (i = 0; i < 6; i++) - { - buffer[7] = '1'+i; - kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Rival indicators - sprintf(buffer, "K_RIVALx"); - for (i = 0; i < 2; i++) - { - buffer[7] = '1'+i; - kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Eggman warning numbers - sprintf(buffer, "K_EGGNx"); - for (i = 0; i < 4; i++) - { - buffer[6] = '0'+i; - kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // First person mode - kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX); - kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX); - kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX); - - // Input UI Wheel - sprintf(buffer, "K_WHEELx"); - for (i = 0; i < 5; i++) - { - buffer[7] = '0'+i; - kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // HERE COMES A NEW CHALLENGER - sprintf(buffer, "K_CHALxx"); - for (i = 0; i < 25; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - // Lap start animation - sprintf(buffer, "K_LAP0x"); - for (i = 0; i < 7; i++) - { - buffer[6] = '0'+(i+1); - kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPFxx"); - for (i = 0; i < 11; i++) - { - buffer[6] = '0'+((i+1)/10); - buffer[7] = '0'+((i+1)%10); - kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPNxx"); - for (i = 0; i < 10; i++) - { - buffer[6] = '0'+i; - for (j = 0; j < 3; j++) - { - buffer[7] = '0'+(j+1); - kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - } - - sprintf(buffer, "K_LAPE0x"); - for (i = 0; i < 2; i++) - { - buffer[7] = '0'+(i+1); - kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "K_LAPH0x"); - for (i = 0; i < 3; i++) - { - buffer[7] = '0'+(i+1); - kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX); - kp_itemminimap = (patch_t *) W_CachePatchName("MMAPITEM", PU_HUDGFX); - - sprintf(buffer, "ALAGLESx"); - for (i = 0; i < 10; ++i) - { - buffer[7] = '0'+i; - kp_alagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - sprintf(buffer, "BLAGLESx"); - for (i = 0; i < 6; ++i) - { - buffer[7] = '0'+i; - kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } - - kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); - - kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX); -} - -// For the item toggle menu -const char *K_GetItemPatch(UINT8 item, boolean tiny) -{ - switch (item) - { - case KITEM_SNEAKER: - case KRITEM_DUALSNEAKER: - case KRITEM_TRIPLESNEAKER: - return (tiny ? "K_ISSHOE" : "K_ITSHOE"); - case KITEM_ROCKETSNEAKER: - return (tiny ? "K_ISRSHE" : "K_ITRSHE"); - case KITEM_INVINCIBILITY: - return (tiny ? "K_ISINV1" : "K_ITINV1"); - case KITEM_BANANA: - case KRITEM_TRIPLEBANANA: - case KRITEM_TENFOLDBANANA: - return (tiny ? "K_ISBANA" : "K_ITBANA"); - case KITEM_EGGMAN: - return (tiny ? "K_ISEGGM" : "K_ITEGGM"); - case KITEM_ORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB1"); - case KITEM_JAWZ: - case KRITEM_DUALJAWZ: - return (tiny ? "K_ISJAWZ" : "K_ITJAWZ"); - case KITEM_MINE: - return (tiny ? "K_ISMINE" : "K_ITMINE"); - case KITEM_BALLHOG: - return (tiny ? "K_ISBHOG" : "K_ITBHOG"); - case KITEM_SPB: - return (tiny ? "K_ISSPB" : "K_ITSPB"); - case KITEM_GROW: - return (tiny ? "K_ISGROW" : "K_ITGROW"); - case KITEM_SHRINK: - return (tiny ? "K_ISSHRK" : "K_ITSHRK"); - case KITEM_THUNDERSHIELD: - return (tiny ? "K_ISTHNS" : "K_ITTHNS"); - case KITEM_BUBBLESHIELD: - return (tiny ? "K_ISBUBS" : "K_ITBUBS"); - case KITEM_FLAMESHIELD: - return (tiny ? "K_ISFLMS" : "K_ITFLMS"); - case KITEM_HYUDORO: - return (tiny ? "K_ISHYUD" : "K_ITHYUD"); - case KITEM_POGOSPRING: - return (tiny ? "K_ISPOGO" : "K_ITPOGO"); - case KITEM_SUPERRING: - return (tiny ? "K_ISRING" : "K_ITRING"); - case KITEM_KITCHENSINK: - return (tiny ? "K_ISSINK" : "K_ITSINK"); - case KRITEM_TRIPLEORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB3"); - case KRITEM_QUADORBINAUT: - return (tiny ? "K_ISORBN" : "K_ITORB4"); - default: - return (tiny ? "K_ISSAD" : "K_ITSAD"); - } -} - -//} - -INT32 ITEM_X, ITEM_Y; // Item Window -INT32 TIME_X, TIME_Y; // Time Sticker -INT32 LAPS_X, LAPS_Y; // Lap Sticker -INT32 POSI_X, POSI_Y; // Position Number -INT32 FACE_X, FACE_Y; // Top-four Faces -INT32 STCD_X, STCD_Y; // Starting countdown -INT32 CHEK_Y; // CHECK graphic -INT32 MINI_X, MINI_Y; // Minimap -INT32 WANT_X, WANT_Y; // Battle WANTED poster - -// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN. -INT32 ITEM2_X, ITEM2_Y; -INT32 LAPS2_X, LAPS2_Y; -INT32 POSI2_X, POSI2_Y; - - -static void K_initKartHUD(void) -{ - /* - BASEVIDWIDTH = 320 - BASEVIDHEIGHT = 200 - - Item window graphic is 41 x 33 - - Time Sticker graphic is 116 x 11 - Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14 - Therefore, timestamp is 116 x 14 altogether - - Lap Sticker is 80 x 11 - Lap flag is 22 x 20 - Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14 - Therefore, lapstamp is 80 x 20 altogether - - Position numbers are 43 x 53 - - Faces are 32 x 32 - Faces draw downscaled at 16 x 16 - Therefore, the allocated space for them is 16 x 67 altogether - - ---- - - ORIGINAL CZ64 SPLITSCREEN: - - Item window: - if (!splitscreen) { ICONX = 139; ICONY = 20; } - else { ICONX = BASEVIDWIDTH-315; ICONY = 60; } - - Time: 236, STRINGY( 12) - Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189) - - */ - - // Single Screen (defaults) - // Item Window - ITEM_X = 5; // 5 - ITEM_Y = 5; // 5 - // Level Timer - TIME_X = BASEVIDWIDTH - 148; // 172 - TIME_Y = 9; // 9 - // Level Laps - LAPS_X = 9; // 9 - LAPS_Y = BASEVIDHEIGHT - 29; // 171 - // Position Number - POSI_X = BASEVIDWIDTH - 9; // 268 - POSI_Y = BASEVIDHEIGHT - 9; // 138 - // Top-Four Faces - FACE_X = 9; // 9 - FACE_Y = 92; // 92 - // Starting countdown - STCD_X = BASEVIDWIDTH/2; // 9 - STCD_Y = BASEVIDHEIGHT/2; // 92 - // CHECK graphic - CHEK_Y = BASEVIDHEIGHT; // 200 - // Minimap - MINI_X = BASEVIDWIDTH - 50; // 270 - MINI_Y = (BASEVIDHEIGHT/2)-16; // 84 - // Battle WANTED poster - WANT_X = BASEVIDWIDTH - 55; // 270 - WANT_Y = BASEVIDHEIGHT- 71; // 176 - - if (r_splitscreen) // Splitscreen - { - ITEM_X = 5; - ITEM_Y = 3; - - LAPS_Y = (BASEVIDHEIGHT/2)-24; - - POSI_Y = (BASEVIDHEIGHT/2)- 2; - - STCD_Y = BASEVIDHEIGHT/4; - - MINI_Y = (BASEVIDHEIGHT/2); - - if (r_splitscreen > 1) // 3P/4P Small Splitscreen - { - // 1P (top left) - ITEM_X = -9; - ITEM_Y = -8; - - LAPS_X = 3; - LAPS_Y = (BASEVIDHEIGHT/2)-12; - - POSI_X = 24; - POSI_Y = (BASEVIDHEIGHT/2)-26; - - // 2P (top right) - ITEM2_X = BASEVIDWIDTH-39; - ITEM2_Y = -8; - - LAPS2_X = BASEVIDWIDTH-43; - LAPS2_Y = (BASEVIDHEIGHT/2)-12; - - POSI2_X = BASEVIDWIDTH -4; - POSI2_Y = (BASEVIDHEIGHT/2)-26; - - // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom. - - STCD_X = BASEVIDWIDTH/4; - - MINI_X = (3*BASEVIDWIDTH/4); - MINI_Y = (3*BASEVIDHEIGHT/4); - - if (r_splitscreen > 2) // 4P-only - { - MINI_X = (BASEVIDWIDTH/2); - MINI_Y = (BASEVIDHEIGHT/2); - } - } - } - - if (timeinmap > 113) - hudtrans = cv_translucenthud.value; - else if (timeinmap > 105) - hudtrans = ((((INT32)timeinmap) - 105)*cv_translucenthud.value)/(113-105); - else - hudtrans = 0; -} - -INT32 K_calcSplitFlags(INT32 snapflags) -{ - INT32 splitflags = 0; - - if (r_splitscreen == 0) - return snapflags; - - if (stplyr != &players[displayplayers[0]]) - { - if (r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - { - splitflags |= V_SPLITSCREEN; - } - else if (r_splitscreen > 1) - { - if (stplyr == &players[displayplayers[2]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) - splitflags |= V_SPLITSCREEN; - if (stplyr == &players[displayplayers[1]] || (r_splitscreen == 3 && stplyr == &players[displayplayers[3]])) - splitflags |= V_HORZSCREEN; - } - } - - if (splitflags & V_SPLITSCREEN) - snapflags &= ~V_SNAPTOTOP; - else - snapflags &= ~V_SNAPTOBOTTOM; - - if (r_splitscreen > 1) - { - if (splitflags & V_HORZSCREEN) - snapflags &= ~V_SNAPTOLEFT; - else - snapflags &= ~V_SNAPTORIGHT; - } - - return (splitflags|snapflags); -} - -static void K_drawKartItem(void) -{ - // ITEM_X = BASEVIDWIDTH-50; // 270 - // ITEM_Y = 24; // 24 - - // Why write V_DrawScaledPatch calls over and over when they're all the same? - // Set to 'no item' just in case. - const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); - patch_t *localpatch = kp_nodraw; - patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]); - patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]); - INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... - //INT32 splitflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); - const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2); - INT32 itembar = 0; - INT32 maxl = 0; // itembar's normal highest value - const INT32 barlength = (r_splitscreen > 1 ? 12 : 26); - UINT8 localcolor = SKINCOLOR_NONE; - SINT8 colormode = TC_RAINBOW; - UINT8 *colmap = NULL; - boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff - - if (stplyr->kartstuff[k_itemroulette]) - { - if (stplyr->skincolor) - localcolor = stplyr->skincolor; - - switch((stplyr->kartstuff[k_itemroulette] % (15*3)) / 3) - { - // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette - case 0: // Sneaker - localpatch = kp_sneaker[offset]; - //localcolor = SKINCOLOR_RASPBERRY; - break; - case 1: // Banana - localpatch = kp_banana[offset]; - //localcolor = SKINCOLOR_YELLOW; - break; - case 2: // Orbinaut - localpatch = kp_orbinaut[3+offset]; - //localcolor = SKINCOLOR_STEEL; - break; - case 3: // Mine - localpatch = kp_mine[offset]; - //localcolor = SKINCOLOR_JET; - break; - case 4: // Grow - localpatch = kp_grow[offset]; - //localcolor = SKINCOLOR_TEAL; - break; - case 5: // Hyudoro - localpatch = kp_hyudoro[offset]; - //localcolor = SKINCOLOR_STEEL; - break; - case 6: // Rocket Sneaker - localpatch = kp_rocketsneaker[offset]; - //localcolor = SKINCOLOR_TANGERINE; - break; - case 7: // Jawz - localpatch = kp_jawz[offset]; - //localcolor = SKINCOLOR_JAWZ; - break; - case 8: // Self-Propelled Bomb - localpatch = kp_selfpropelledbomb[offset]; - //localcolor = SKINCOLOR_JET; - break; - case 9: // Shrink - localpatch = kp_shrink[offset]; - //localcolor = SKINCOLOR_ORANGE; - break; - case 10: // Invincibility - localpatch = localinv; - //localcolor = SKINCOLOR_GREY; - break; - case 11: // Eggman Monitor - localpatch = kp_eggman[offset]; - //localcolor = SKINCOLOR_ROSE; - break; - case 12: // Ballhog - localpatch = kp_ballhog[offset]; - //localcolor = SKINCOLOR_LILAC; - break; - case 13: // Thunder Shield - localpatch = kp_thundershield[offset]; - //localcolor = SKINCOLOR_CYAN; - break; - case 14: // Super Ring - localpatch = kp_superring[offset]; - //localcolor = SKINCOLOR_GOLD; - break; - /*case 15: // Pogo Spring - localpatch = kp_pogospring[offset]; - localcolor = SKINCOLOR_TANGERINE; - break; - case 16: // Kitchen Sink - localpatch = kp_kitchensink[offset]; - localcolor = SKINCOLOR_STEEL; - break;*/ - default: - break; - } - } - else - { - // I'm doing this a little weird and drawing mostly in reverse order - // The only actual reason is to make sneakers line up this way in the code below - // This shouldn't have any actual baring over how it functions - // Hyudoro is first, because we're drawing it on top of the player's current item - if (stplyr->kartstuff[k_stolentimer] > 0) - { - if (leveltime & 2) - localpatch = kp_hyudoro[offset]; - else - localpatch = kp_nodraw; - } - else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2)) - { - localpatch = kp_hyudoro[offset]; - } - else if (stplyr->kartstuff[k_eggmanexplode] > 1) - { - if (leveltime & 1) - localpatch = kp_eggman[offset]; - else - localpatch = kp_nodraw; - } - else if (stplyr->kartstuff[k_rocketsneakertimer] > 1) - { - itembar = stplyr->kartstuff[k_rocketsneakertimer]; - maxl = (itemtime*3) - barlength; - - if (leveltime & 1) - localpatch = kp_rocketsneaker[offset]; - else - localpatch = kp_nodraw; - } - else if (stplyr->kartstuff[k_sadtimer] > 0) - { - if (leveltime & 2) - localpatch = kp_sadface[offset]; - else - localpatch = kp_nodraw; - } - else - { - if (stplyr->kartstuff[k_itemamount] <= 0) - return; - - switch(stplyr->kartstuff[k_itemtype]) - { - case KITEM_SNEAKER: - localpatch = kp_sneaker[offset]; - break; - case KITEM_ROCKETSNEAKER: - localpatch = kp_rocketsneaker[offset]; - break; - case KITEM_INVINCIBILITY: - localpatch = localinv; - localbg = kp_itembg[offset+1]; - break; - case KITEM_BANANA: - localpatch = kp_banana[offset]; - break; - case KITEM_EGGMAN: - localpatch = kp_eggman[offset]; - break; - case KITEM_ORBINAUT: - localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))]; - break; - case KITEM_JAWZ: - localpatch = kp_jawz[offset]; - break; - case KITEM_MINE: - localpatch = kp_mine[offset]; - break; - case KITEM_BALLHOG: - localpatch = kp_ballhog[offset]; - break; - case KITEM_SPB: - localpatch = kp_selfpropelledbomb[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_GROW: - localpatch = kp_grow[offset]; - break; - case KITEM_SHRINK: - localpatch = kp_shrink[offset]; - break; - case KITEM_THUNDERSHIELD: - localpatch = kp_thundershield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_BUBBLESHIELD: - localpatch = kp_bubbleshield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_FLAMESHIELD: - localpatch = kp_flameshield[offset]; - localbg = kp_itembg[offset+1]; - break; - case KITEM_HYUDORO: - localpatch = kp_hyudoro[offset]; - break; - case KITEM_POGOSPRING: - localpatch = kp_pogospring[offset]; - break; - case KITEM_SUPERRING: - localpatch = kp_superring[offset]; - break; - case KITEM_KITCHENSINK: - localpatch = kp_kitchensink[offset]; - break; - case KITEM_SAD: - localpatch = kp_sadface[offset]; - break; - default: - return; - } - - if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1)) - localpatch = kp_nodraw; - } - - if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) - { - colormode = TC_BLINK; - - switch (stplyr->karthud[khud_itemblinkmode]) - { - case 2: - localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); - break; - case 1: - localcolor = SKINCOLOR_RED; - break; - default: - localcolor = SKINCOLOR_WHITE; - break; - } - } - } - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = ITEM_X; - fy = ITEM_Y; - fflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT); - } - else // now we're having a fun game. - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = ITEM_X; - fy = ITEM_Y; - fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = ITEM2_X; - fy = ITEM2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P4 to the bottom - flipamount = true; - } - } - - if (localcolor != SKINCOLOR_NONE) - colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE); - - V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, localbg); - - // Then, the numbers: - if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette]) - { - V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it. - V_DrawFixedPatch(fx<kartstuff[k_itemamount])); - else - V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|fflags, va("x%d", stplyr->kartstuff[k_itemamount])); - else - { - V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|fflags, kp_itemx); - V_DrawKartString(fx+38, fy+36, V_HUDTRANS|fflags, va("%d", stplyr->kartstuff[k_itemamount])); - } - } - else - V_DrawFixedPatch(fx< 2) - { - V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one - if (height == 2) - V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside - V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine - } - } - - // Quick Eggman numbers - if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/) - V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|fflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]); - - if (stplyr->kartstuff[k_itemtype] == KITEM_FLAMESHIELD && stplyr->kartstuff[k_flamelength] > 0) - { - INT32 numframes = 104; - INT32 absolutemax = 16 * flameseg; - INT32 flamemax = stplyr->kartstuff[k_flamelength] * flameseg; - INT32 flamemeter = min(stplyr->kartstuff[k_flamemeter], flamemax); - - INT32 bf = 16 - stplyr->kartstuff[k_flamelength]; - INT32 ff = numframes - ((flamemeter * numframes) / absolutemax); - INT32 fmin = (8 * (bf-1)); - - INT32 xo = 6, yo = 4; - INT32 flip = 0; - - if (offset) - { - xo++; - - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // Flip for P1 and P3 (yes, that's correct) - { - xo -= 62; - flip = V_FLIP; - } - } - - if (ff < fmin) - ff = fmin; - - if (bf >= 0 && bf < 16) - V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter_bg[bf][offset]); - - if (ff >= 0 && ff < numframes && stplyr->kartstuff[k_flamemeter] > 0) - { - if ((stplyr->kartstuff[k_flamemeter] > flamemax) && (leveltime & 1)) - { - UINT8 *fsflash = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); - V_DrawMappedPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset], fsflash); - } - else - { - V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|fflags|flip, kp_flameshieldmeter[ff][offset]); - } - } - } -} - -void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode) -{ - // TIME_X = BASEVIDWIDTH-124; // 196 - // TIME_Y = 6; // 6 - - tic_t worktime; - - INT32 splitflags = 0; - if (!mode) - { - splitflags = V_HUDTRANS|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTORIGHT); - if (cv_timelimit.value && timelimitintics > 0) - { - if (drawtime >= timelimitintics) - drawtime = 0; - else - drawtime = timelimitintics - drawtime; - } - } - - V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide)); - - TX += 33; - - worktime = drawtime/(60*TICRATE); - - if (mode && !drawtime) - V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--")); - else if (worktime < 100) // 99:99:99 only - { - // zero minute - if (worktime < 10) - { - V_DrawKartString(TX, TY+3, splitflags, va("0")); - // minutes time 0 __ __ - V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime)); - } - // minutes time 0 __ __ - else - V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime)); - - // apostrophe location _'__ __ - V_DrawKartString(TX+24, TY+3, splitflags, va("'")); - - worktime = (drawtime/TICRATE % 60); - - // zero second _ 0_ __ - if (worktime < 10) - { - V_DrawKartString(TX+36, TY+3, splitflags, va("0")); - // seconds time _ _0 __ - V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime)); - } - // zero second _ 00 __ - else - V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime)); - - // quotation mark location _ __"__ - V_DrawKartString(TX+60, TY+3, splitflags, va("\"")); - - worktime = G_TicsToCentiseconds(drawtime); - - // zero tick _ __ 0_ - if (worktime < 10) - { - V_DrawKartString(TX+72, TY+3, splitflags, va("0")); - // tics _ __ _0 - V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime)); - } - // zero tick _ __ 00 - else - V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime)); - } - else if ((drawtime/TICRATE) & 1) - V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); - - if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! - { - INT32 workx = TX + 96, worky = TY+18; - SINT8 curemb = 0; - patch_t *emblempic[3] = {NULL, NULL, NULL}; - UINT8 *emblemcol[3] = {NULL, NULL, NULL}; - - emblem_t *emblem = M_GetLevelEmblems(emblemmap); - while (emblem) - { - char targettext[9]; - - switch (emblem->type) - { - case ET_TIME: - { - static boolean canplaysound = true; - tic_t timetoreach = emblem->var; - - if (emblem->collected) - { - emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE); - emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE); - if (++curemb == 3) - break; - goto bademblem; - } - - snprintf(targettext, 9, "%i'%02i\"%02i", - G_TicsToMinutes(timetoreach, false), - G_TicsToSeconds(timetoreach), - G_TicsToCentiseconds(timetoreach)); - - if (!mode) - { - if (stplyr->realtime > timetoreach) - { - splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF; - if (canplaysound) - { - S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks - canplaysound = false; - } - } - else if (!canplaysound) - canplaysound = true; - } - - targettext[8] = 0; - } - break; - default: - goto bademblem; - } - - V_DrawRightAlignedString(workx, worky, splitflags, targettext); - workx -= 67; - V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE)); - - break; - - bademblem: - emblem = M_GetLevelEmblems(-1); - } - - if (!mode) - splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS; - while (curemb--) - { - workx -= 12; - V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]); - } - } -} - -static void K_DrawKartPositionNum(INT32 num) -{ - // POSI_X = BASEVIDWIDTH - 51; // 269 - // POSI_Y = BASEVIDHEIGHT- 64; // 136 - - boolean win = (stplyr->exiting && num == 1); - //INT32 X = POSI_X; - INT32 W = SHORT(kp_positionnum[0][0]->width); - fixed_t scale = FRACUNIT; - patch_t *localpatch = kp_positionnum[0][0]; - //INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT); - INT32 fx = 0, fy = 0, fflags = 0; - boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun. - boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen. - boolean overtake = false; - - if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting) - { - scale *= 2; - overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw. - } - if (r_splitscreen) - scale /= 2; - - W = FixedMul(W<>FRACBITS; - - // pain and suffering defined below - if (!r_splitscreen) - { - fx = POSI_X; - fy = BASEVIDHEIGHT - 8; - fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; - } - else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different. - { - fx = POSI_X; - if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap. - { - fy = 30; - fflags = V_SNAPTOTOP|V_SNAPTORIGHT; - if (overtake) - flipvdraw = true; // make sure overtaking doesn't explode us - } - else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap. - { - fy = BASEVIDHEIGHT - 8; - fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT; - } - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = POSI_X; - fy = POSI_Y; - fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - flipdraw = true; - if (num && num >= 10) - fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen. - } - else // else, that means we're P2 or P4. - { - fx = POSI2_X; - fy = POSI2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - } - } - - // Special case for 0 - if (!num) - { - V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers - - // Draw the number - while (num) - { - if (win) // 1st place winner? You get rainbows!! - localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3]; - else if (stplyr->laps >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won - { - // Alternate frame every three frames - switch (leveltime % 9) - { - case 1: case 2: case 3: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][4]; - else - localpatch = kp_positionnum[num % 10][1]; - break; - case 4: case 5: case 6: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][5]; - else - localpatch = kp_positionnum[num % 10][2]; - break; - case 7: case 8: case 9: - if (K_IsPlayerLosing(stplyr)) - localpatch = kp_positionnum[num % 10][6]; - else - localpatch = kp_positionnum[num % 10][3]; - break; - default: - localpatch = kp_positionnum[num % 10][0]; - break; - } - } - else - localpatch = kp_positionnum[num % 10][0]; - - V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|fflags, localpatch, NULL); - // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen. - // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either. - - fx -= W; - num /= 10; - } -} - -static boolean K_drawKartPositionFaces(void) -{ - // FACE_X = 15; // 15 - // FACE_Y = 72; // 72 - - INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one - INT32 i, j, ranklines, strank = -1; - boolean completed[MAXPLAYERS]; - INT32 rankplayer[MAXPLAYERS]; - INT32 bumperx, numplayersingame = 0; - UINT8 *colormap; - - ranklines = 0; - memset(completed, 0, sizeof (completed)); - memset(rankplayer, 0, sizeof (rankplayer)); - - for (i = 0; i < MAXPLAYERS; i++) - { - rankplayer[i] = -1; - - if (!playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - numplayersingame++; - } - - if (numplayersingame <= 1) - return true; - -#ifdef HAVE_BLUA - if (!LUA_HudEnabled(hud_minirankings)) - return false; // Don't proceed but still return true for free play above if HUD is disabled. -#endif - - for (j = 0; j < numplayersingame; j++) - { - UINT8 lowestposition = MAXPLAYERS+1; - for (i = 0; i < MAXPLAYERS; i++) - { - if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo) - continue; - - if (players[i].kartstuff[k_position] >= lowestposition) - continue; - - rankplayer[ranklines] = i; - lowestposition = players[i].kartstuff[k_position]; - } - - i = rankplayer[ranklines]; - - completed[i] = true; - - if (players+i == stplyr) - strank = ranklines; - - //if (ranklines == 5) - //break; // Only draw the top 5 players -- we do this a different way now... - - ranklines++; - } - - if (ranklines < 5) - Y -= (9*ranklines); - else - Y -= (9*5); - - if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) - { - i = 0; - if (ranklines > 5) // could be both... - ranklines = 5; - } - else if (strank+3 > ranklines) // too close to the bottom? - { - i = ranklines - 5; - if (i < 0) - i = 0; - } - else - { - i = strank-2; - ranklines = strank+3; - } - - for (; i < ranklines; i++) - { - if (!playeringame[rankplayer[i]]) continue; - if (players[rankplayer[i]].spectator) continue; - if (!players[rankplayer[i]].mo) continue; - - bumperx = FACE_X+19; - - if (players[rankplayer[i]].mo->color) - { - colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); - if (players[rankplayer[i]].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE); - - V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap); - -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_battlebumpers)) - { -#endif - if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 0) - { - V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[0], colormap); - for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++) - { - bumperx += 5; - V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[1], colormap); - } - } -#ifdef HAVE_BLUA - } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating: -#endif - } - - if (i == strank) - V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]); - - if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0) - V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SNAPTOLEFT, kp_ranknobumpers); - else - { - INT32 pos = players[rankplayer[i]].kartstuff[k_position]; - if (pos < 0 || pos > MAXPLAYERS) - pos = 0; - // Draws the little number over the face - V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SNAPTOLEFT, kp_facenum[pos]); - } - - Y += 18; - } - - return false; -} - -// -// HU_DrawTabRankings -- moved here to take advantage of kart stuff! -// -void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol) -{ - static tic_t alagles_timer = 9; - INT32 i, rightoffset = 240; - const UINT8 *colormap; - INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; - int y2; - - //this function is designed for 9 or less score lines only - //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up - - V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice! - if (scorelines > 8) - { - V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides. - V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom. - rightoffset = (BASEVIDWIDTH/2) - 4 - x; - } - - for (i = 0; i < scorelines; i++) - { - char strtime[MAXPLAYERNAME+1]; - - if (players[tab[i].num].spectator || !players[tab[i].num].mo) - continue; //ignore them. - - if (netgame) // don't draw ping offline - { - if (players[tab[i].num].bot) - { - V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu); - } - else if (tab[i].num != serverplayer || !server_lagless) - { - HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); - } - } - - STRBUFCPY(strtime, tab[i].name); - - y2 = y; - - if (netgame && playerconsole[tab[i].num] == 0 && server_lagless && !players[tab[i].num].bot) - { - y2 = ( y - 4 ); - - V_DrawScaledPatch(x + 20, y2, 0, kp_blagles[(leveltime / 3) % 6]); - // every 70 tics - if (( leveltime % 70 ) == 0) - { - alagles_timer = 9; - } - if (alagles_timer > 0) - { - V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[alagles_timer]); - if (( leveltime % 2 ) == 0) - alagles_timer--; - } - else - V_DrawScaledPatch(x + 20, y2, 0, kp_alagles[0]); - - y2 += SHORT (kp_alagles[0]->height) + 1; - } - - if (scorelines > 8) - V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); - else - V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); - - if (players[tab[i].num].mo->color) - { - colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); - if (players[tab[i].num].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE); - - V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap); - /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 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++) - { - bumperx += 5; - V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap); - } - }*/ - } - - if (tab[i].num == whiteplayer) - V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]); - - if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0) - V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers); - else - { - INT32 pos = players[tab[i].num].kartstuff[k_position]; - if (pos < 0 || pos > MAXPLAYERS) - pos = 0; - // Draws the little number over the face - V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]); - } - - if (G_RaceGametype()) - { -#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) - if (scorelines > 8) - { - if (players[tab[i].num].exiting) - V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); - else if (players[tab[i].num].pflags & PF_TIMEOVER) - V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST."); - else if (circuitmap) - V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count)); - } - else - { - if (players[tab[i].num].exiting) - V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime)); - else if (players[tab[i].num].pflags & PF_TIMEOVER) - V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST."); - else if (circuitmap) - V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count)); - } -#undef timestring - } - else - V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); - - y += 18; - if (i == 7) - { - y = 33; - x = (BASEVIDWIDTH/2) + 4; - } - } -} - -#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) - -static void K_drawKartLapsAndRings(void) -{ - const boolean uselives = G_GametypeUsesLives(); - SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - UINT8 rn[2]; - INT32 ringflip = 0; - UINT8 *ringmap = NULL; - boolean colorring = false; - INT32 ringx = 0; - - rn[0] = ((abs(stplyr->kartstuff[k_rings]) / 10) % 10); - rn[1] = (abs(stplyr->kartstuff[k_rings]) % 10); - - if (stplyr->kartstuff[k_rings] <= 0 && (leveltime/5 & 1)) // In debt - { - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE); - colorring = true; - } - else if (stplyr->kartstuff[k_rings] >= 20) // Maxed out - ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE); - - if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME) - { - ringflip = V_FLIP; - ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe]; - ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); - } - - if (r_splitscreen > 1) - { - INT32 fx = 0, fy = 0, fr = 0; - INT32 flipflag = 0; - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = LAPS_X; - fy = LAPS_Y; - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = LAPS_X; - fy = LAPS_Y; - splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = LAPS2_X; - fy = LAPS2_Y; - splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipflag = V_FLIP; // make the string right aligned and other shit - } - } - - fr = fx; - - // Laps - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - - V_DrawScaledPatch(fx, fy, V_HUDTRANS|splitflags, kp_splitlapflag); - V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); - - if (cv_numlaps.value >= 10) - { - UINT8 ln[2]; - ln[0] = ((stplyr->laps / 10) % 10); - ln[1] = (stplyr->laps % 10); - - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((abs(cv_numlaps.value) / 10) % 10); - ln[1] = (abs(cv_numlaps.value) % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_numlaps.value) % 10]); - } - - // Rings - if (!uselives) - { - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]); - if (flipflag) - fr += 15; - } - else - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[0]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - - V_DrawMappedPatch(fr+ringx, fy-13, V_HUDTRANS|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL)); - - if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt - V_DrawMappedPatch(fr+7, fy-10, V_HUDTRANS|splitflags, kp_ringdebtminussmall, ringmap); - - V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); - V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); - - // SPB ring lock - if (stplyr->kartstuff[k_ringlock]) - V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); - - // Lives - if (uselives) - { - UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); - V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow - } - } - else - { - // Laps - V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_lapsticker); - - if (stplyr->exiting) - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN"); - else - V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); - - // Rings - if (!uselives) - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]); - else - V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]); - - V_DrawMappedPatch(LAPS_X+ringx+7, LAPS_Y-16, V_HUDTRANS|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL)); - - if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt - { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringdebtminus, ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+35, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); - } - else - { - V_DrawMappedPatch(LAPS_X+23, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[0]], ringmap); - V_DrawMappedPatch(LAPS_X+29, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[rn[1]], ringmap); - } - - // SPB ring lock - if (stplyr->kartstuff[k_ringlock]) - V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); - - // Lives - if (uselives) - { - UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap); - V_DrawScaledPatch(LAPS_X+63, LAPS_Y-11, V_HUDTRANS|splitflags, kp_facenum[(stplyr->lives % 10)]); // make sure this doesn't overflow - } - } -} - -#undef RINGANIM_NUMFRAMES -#undef RINGANIM_FLIPFRAME - -static void K_drawKartSpeedometer(void) -{ - static fixed_t convSpeed; - UINT8 labeln = 0; - UINT8 numbers[3]; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - UINT8 battleoffset = 0; - - if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! - { - switch (cv_kartspeedometer.value) - { - case 1: // Sonic Drift 2 style percentage - default: - convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) - labeln = 0; - break; - case 2: // Kilometers - convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058 - labeln = 1; - break; - case 3: // Miles - convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774 - labeln = 2; - break; - case 4: // Fracunits - convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT; // 1.0. duh. - labeln = 3; - break; - } - } - - // Don't overflow - if (convSpeed > 999) - convSpeed = 999; - - numbers[0] = ((convSpeed / 100) % 10); - numbers[1] = ((convSpeed / 10) % 10); - numbers[2] = (convSpeed % 10); - - if (G_BattleGametype()) - battleoffset = 8; - - V_DrawScaledPatch(LAPS_X, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometersticker); - V_DrawScaledPatch(LAPS_X+7, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[0]]); - V_DrawScaledPatch(LAPS_X+13, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[1]]); - V_DrawScaledPatch(LAPS_X+19, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_facenum[numbers[2]]); - V_DrawScaledPatch(LAPS_X+29, LAPS_Y-25 + battleoffset, V_HUDTRANS|splitflags, kp_speedometerlabel[labeln]); -} - -static void K_drawKartBumpersOrKarma(void) -{ - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - - if (r_splitscreen > 1) - { - INT32 fx = 0, fy = 0; - INT32 flipflag = 0; - - // pain and suffering defined below - if (r_splitscreen < 2) // don't change shit for THIS splitscreen. - { - fx = LAPS_X; - fy = LAPS_Y; - } - else - { - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... - { - fx = LAPS_X; - fy = LAPS_Y; - splitflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. - } - else // else, that means we're P2 or P4. - { - fx = LAPS2_X; - fy = LAPS2_Y; - splitflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipflag = V_FLIP; // make the string right aligned and other shit - } - } - - V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[0]); - V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); - - if (battlecapsules) - { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankcapsule, NULL); - - if (numtargets > 9 || maptargets > 9) - { - UINT8 ln[2]; - ln[0] = ((numtargets / 10) % 10); - ln[1] = (numtargets % 10); - - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - - ln[0] = ((maptargets / 10) % 10); - ln[1] = (maptargets % 10); - - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[numtargets % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[maptargets % 10]); - } - } - else - { - if (stplyr->kartstuff[k_bumper] <= 0) - { - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_comebackpoints]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[2]); - } - else - { - INT32 maxbumper = K_StartingBumperCount(); - V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|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|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); - } - else - { - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->kartstuff[k_bumper]) % 10]); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(maxbumper) % 10]); - } - } - } - } - else - { - if (battlecapsules) - { - if (numtargets > 9 && maptargets > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulestickerwide, NULL); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_capsulesticker, NULL); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", numtargets, maptargets)); - } - else - { - if (stplyr->kartstuff[k_bumper] <= 0) - { - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap); - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); - } - else - { - INT32 maxbumper = K_StartingBumperCount(); - - if (stplyr->kartstuff[k_bumper] > 9 && maxbumper > 9) - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap); - else - V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap); - - V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], maxbumper)); - } - } - } -} - -static void K_drawKartWanted(void) -{ - UINT8 i, numwanted = 0; - UINT8 *colormap = NULL; - INT32 basex = 0, basey = 0; - - if (stplyr != &players[displayplayers[0]]) - return; - - for (i = 0; i < 4; i++) - { - if (battlewanted[i] == -1) - break; - numwanted++; - } - - if (numwanted <= 0) - return; - - // set X/Y coords depending on splitscreen. - if (r_splitscreen < 3) // 1P and 2P use the same code. - { - basex = WANT_X; - basey = WANT_Y; - if (r_splitscreen == 2) - { - basey += 16; // slight adjust for 3P - basex -= 6; - } - } - else if (r_splitscreen == 3) // 4P splitscreen... - { - basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen - basey = BASEVIDHEIGHT - 55; - //basey2 = 4; - } - - if (battlewanted[0] != -1) - colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE); - V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap); - /*if (basey2) - V_DrawFixedPatch(basex< 1 ? 13 : 8), y = basey+(r_splitscreen > 1 ? 16 : 21); - fixed_t scale = FRACUNIT/2; - player_t *p = &players[battlewanted[i]]; - - if (battlewanted[i] == -1) - break; - - if (numwanted == 1) - scale = FRACUNIT; - else - { - if (i & 1) - x += 16; - if (i > 1) - y += 16; - } - - if (players[battlewanted[i]].skincolor) - { - colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE); - V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap); - /*if (basey2) // again with 4p stuff - V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);*/ - } - } -} - -static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) -{ - const INT32 swhalf = (BASEVIDWIDTH / 2); - const fixed_t swhalffixed = swhalf * FRACUNIT; - - const INT32 shhalf = (BASEVIDHEIGHT / 2); - const fixed_t shhalffixed = shhalf * FRACUNIT; - - INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); - fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); - fixed_t factor = INT32_MAX; - - if (abs(anglediff) > ANGLE_90) - { - if (hud_x != NULL) - { - *hud_x = -BASEVIDWIDTH * FRACUNIT; - } - - if (hud_y != NULL) - { - *hud_y = -BASEVIDWIDTH * FRACUNIT; - } - - //*hud_scale = FRACUNIT; - return; - } - - factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); - -#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) - - if (hud_x != NULL) - { - *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; - - if (encoremode) - { - *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; - } - - if (r_splitscreen >= 2) - { - *hud_x /= 2; - - if (camnum & 1) - { - *hud_x += swhalffixed; - } - } - } - - if (hud_y != NULL) - { - *hud_y = campos->z - point->z; - *hud_y = FixedDiv(*hud_y, FixedMul(factor, distance)); - *hud_y = (*hud_y * swhalf) + shhalffixed; - *hud_y = *hud_y + NEWTAN(camaim) * swhalf; - - if (r_splitscreen >= 1) - { - *hud_y /= 2; - - if ((r_splitscreen == 1 && camnum == 1) - || (r_splitscreen > 1 && camnum > 1)) - { - *hud_y += shhalffixed; - } - } - } - - //*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance)); - -#undef NEWTAN -} - -static void K_drawKartPlayerCheck(void) -{ - const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); - camera_t *thiscam; - vertex_t c; - UINT8 cnum = 0; - UINT8 i; - INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); - - if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) - { - return; - } - - if (stplyr->spectator || stplyr->awayviewtics) - { - return; - } - - if (stplyr->cmd.buttons & BT_LOOKBACK) - { - return; - } - - if (r_splitscreen) - { - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - - thiscam = &camera[cnum]; - - c.x = stplyr->mo->x; - c.y = stplyr->mo->y; - c.z = stplyr->mo->z; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *checkplayer = &players[i]; - fixed_t distance = maxdistance+1; - UINT8 *colormap = NULL; - UINT8 pnum = 0; - fixed_t x = 0; - vertex_t v; - - if (!playeringame[i] || checkplayer->spectator) - { - // Not in-game - continue; - } - - if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo)) - { - // No object - continue; - } - - if (checkplayer == stplyr) - { - // This is you! - continue; - } - - v.x = checkplayer->mo->x; - v.y = checkplayer->mo->y; - v.z = checkplayer->mo->z; - - distance = R_PointToDist2(c.x, c.y, v.x, v.y); - - if (distance > maxdistance) - { - // Too far away - continue; - } - - if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) - { - pnum++; // white frames - } - - if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0) - { - pnum += 4; - } - else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer]) - { - pnum += 2; - } - - K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); - - colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); - V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); - } -} - -static boolean K_ShowPlayerNametag(player_t *p) -{ - if (demo.playback == true && demo.freecam == true) - { - return true; - } - - if (stplyr == p) - { - return false; - } - - if (G_RaceGametype()) - { - if ((p->kartstuff[k_position] < stplyr->kartstuff[k_position]-2) - || (p->kartstuff[k_position] > stplyr->kartstuff[k_position]+2)) - { - return false; - } - } - - return true; -} - -static void K_drawKartNameTags(void) -{ - const fixed_t maxdistance = 8192*mapobjectscale; - camera_t *thiscam; - vertex_t c; - UINT8 cnum = 0; - UINT8 i; - - if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) - { - return; - } - - if (stplyr->awayviewtics) - { - return; - } - - if (r_splitscreen) - { - for (i = 1; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]]) - { - cnum = i; - break; - } - } - } - - thiscam = &camera[cnum]; - - c.x = thiscam->x; - c.y = thiscam->y; - c.z = thiscam->z; - - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *ntplayer = &players[i]; - fixed_t distance = maxdistance+1; - - fixed_t x = -BASEVIDWIDTH * FRACUNIT; - fixed_t y = -BASEVIDWIDTH * FRACUNIT; - - vertex_t v; - - if (!playeringame[i] || ntplayer->spectator) - { - // Not in-game - continue; - } - - if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) - { - // No object - continue; - } - - if (!P_CheckSight(stplyr->mo, ntplayer->mo)) - { - // Can't see - continue; - } - - if (!(demo.playback == true && demo.freecam == true)) - { - UINT8 j; - - for (j = 0; j <= r_splitscreen; j++) - { - if (ntplayer == &players[displayplayers[j]]) - { - break; - } - } - - if (j <= r_splitscreen) - { - // This is a player that's being shown on this computer - // (Remove whenever we get splitscreen ABCD indicators) - continue; - } - } - - v.x = ntplayer->mo->x; - v.y = ntplayer->mo->y; - v.z = ntplayer->mo->z; - - if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) - { - v.z += ntplayer->mo->height; - } - - distance = R_PointToDist2(c.x, c.y, v.x, v.y); - - if (distance > maxdistance) - { - // Too far away - continue; - } - - K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); - - if (x == -BASEVIDWIDTH * FRACUNIT) - { - // Off-screen - continue; - } - - if (ntplayer->bot) - { - if (ntplayer->botvars.rival == true) - { - UINT8 blink = ((leveltime / 7) & 1); - V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); - } - } - else if (netgame || demo.playback) - { - if (K_ShowPlayerNametag(ntplayer) == true) - { - INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); - INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); - UINT8 *colormap = V_GetStringColormap(clr); - INT32 barx = 0, bary = 0, barw = 0; - - // Since there's no "V_DrawFixedFill", and I don't feel like making it, - // fuck it, we're gonna just V_NOSCALESTART hack it - barw = (namelen * vid.dupx); - - barx = (x * vid.dupx) / FRACUNIT; - bary = (y * vid.dupy) / FRACUNIT; - - barx += (6 * vid.dupx); - bary -= (16 * vid.dupx); - - // Center it if necessary - if (vid.width != BASEVIDWIDTH * vid.dupx) - { - barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; - } - - if (vid.height != BASEVIDHEIGHT * vid.dupy) - { - bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; - } - - // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. - V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); - V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); - // END DRAWFILL DUMBNESS - - // Draw the stem - V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); - - // Draw the name itself - V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); - } - } - } -} - -static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap, patch_t *AutomapPic) -{ - // amnum xpos & ypos are the icon's speed around the HUD. - // The number being divided by is for how fast it moves. - // The higher the number, the slower it moves. - - // am xpos & ypos are the icon's starting position. Withouht - // it, they wouldn't 'spawn' on the top-right side of the HUD. - - fixed_t amnumxpos, amnumypos; - INT32 amxpos, amypos; - - node_t *bsp = &nodes[numnodes-1]; - fixed_t maxx, minx, maxy, miny; - - fixed_t mapwidth, mapheight; - fixed_t xoffset, yoffset; - fixed_t xscale, yscale, zoom; - - maxx = maxy = INT32_MAX; - minx = miny = INT32_MIN; - minx = bsp->bbox[0][BOXLEFT]; - maxx = bsp->bbox[0][BOXRIGHT]; - miny = bsp->bbox[0][BOXBOTTOM]; - maxy = bsp->bbox[0][BOXTOP]; - - if (bsp->bbox[1][BOXLEFT] < minx) - minx = bsp->bbox[1][BOXLEFT]; - if (bsp->bbox[1][BOXRIGHT] > maxx) - maxx = bsp->bbox[1][BOXRIGHT]; - if (bsp->bbox[1][BOXBOTTOM] < miny) - miny = bsp->bbox[1][BOXBOTTOM]; - if (bsp->bbox[1][BOXTOP] > maxy) - maxy = bsp->bbox[1][BOXTOP]; - - // You might be wondering why these are being bitshift here - // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible... - // map boundaries and sizes will ALWAYS be whole numbers thankfully - // later calculations take into consideration that these are actually not in terms of FRACUNIT though - minx >>= FRACBITS; - maxx >>= FRACBITS; - miny >>= FRACBITS; - maxy >>= FRACBITS; - - mapwidth = maxx - minx; - mapheight = maxy - miny; - - // These should always be small enough to be bitshift back right now - xoffset = (minx + mapwidth/2)<width, mapwidth); - yscale = FixedDiv(AutomapPic->height, mapheight); - zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20); - - amnumxpos = (FixedMul(objx, zoom) - FixedMul(xoffset, zoom)); - amnumypos = -(FixedMul(objy, zoom) - FixedMul(yoffset, zoom)); - - if (encoremode) - amnumxpos = -amnumxpos; - - amxpos = amnumxpos + ((hudx + AutomapPic->width/2 - (icon->width/2))<height/2 - (icon->height/2))<width/2 + (icon->width/2))<width/2); - y = MINI_Y - (AutomapPic->height/2); - - if (timeinmap > 105) - { - minimaptrans = cv_kartminimap.value; - if (timeinmap <= 113) - minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105); - if (!minimaptrans) - return; - } - else - return; - - minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic); - else - V_DrawScaledPatch(x, y, splitflags, AutomapPic); - - if (!(r_splitscreen == 2)) - { - splitflags &= ~minimaptrans; - splitflags |= V_HUDTRANSHALF; - } - - // let offsets transfer to the heads, too! - if (encoremode) - x += SHORT(AutomapPic->leftoffset); - else - x -= SHORT(AutomapPic->leftoffset); - y -= SHORT(AutomapPic->topoffset); - - // Draw the super item in Battle - if (G_BattleGametype() && battleovertime.enabled) - { - if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) - { - const INT32 prevsplitflags = splitflags; - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - colormap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE); - K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap, AutomapPic); - splitflags = prevsplitflags; - } - } - - // initialize - for (i = 0; i < 4; i++) - localplayers[i] = -1; - - if (G_RaceGametype()) - hyu *= 2; // double in race - - // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) - if (ghosts) - { - demoghost *g = ghosts; - while (g) - { - if (g->mo->skin) - skin = ((skin_t*)g->mo->skin)-skins; - else - skin = 0; - if (g->mo->color) - { - if (g->mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, g->mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, g->mo->color, GTC_CACHE); - } - else - colormap = NULL; - K_drawKartMinimapIcon(g->mo->x, g->mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - g = g->next; - } - - if (!stplyr->mo || stplyr->spectator || stplyr->exiting) - return; - - localplayers[numlocalplayers] = stplyr-players; - numlocalplayers++; - } - else - { - for (i = MAXPLAYERS-1; i >= 0; i--) - { - if (!playeringame[i]) - continue; - if (!players[i].mo || players[i].spectator || players[i].exiting) - continue; - - if (i != displayplayers[0] || r_splitscreen) - { - if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0) - continue; - - if (players[i].kartstuff[k_hyudorotimer] > 0) - { - if (!((players[i].kartstuff[k_hyudorotimer] < TICRATE/2 - || players[i].kartstuff[k_hyudorotimer] > hyu-(TICRATE/2)) - && !(leveltime & 1))) - continue; - } - } - - if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3]) - { - // Draw display players on top of everything else - localplayers[numlocalplayers] = i; - numlocalplayers++; - continue; - } - - if (players[i].mo->skin) - skin = ((skin_t*)players[i].mo->skin)-skins; - else - skin = 0; - - if (players[i].mo->color) - { - if (players[i].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE); - } - else - colormap = NULL; - - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - // Target reticule - if ((G_RaceGametype() && players[i].kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(&players[i]))) - K_drawKartMinimapIcon(players[i].mo->x, players[i].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } - } - - // draw SPB(s?) - for (mobj = kitemcap; mobj; mobj = next) - { - next = mobj->itnext; - if (mobj->type == MT_SPB) - { - colormap = NULL; - - if (mobj->target && !P_MobjWasRemoved(mobj->target)) - { - if (mobj->player && mobj->player->skincolor) - colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->player->skincolor, GTC_CACHE); - else if (mobj->color) - colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->color, GTC_CACHE); - } - - K_drawKartMinimapIcon(mobj->x, mobj->y, x, y, splitflags, kp_spbminimap, colormap, AutomapPic); - } - } - - // draw our local players here, opaque. - splitflags &= ~V_HUDTRANSHALF; - splitflags |= V_HUDTRANS; - - for (i = 0; i < numlocalplayers; i++) - { - if (i == -1) - continue; // this doesn't interest us - - if (players[localplayers[i]].mo->skin) - skin = ((skin_t*)players[localplayers[i]].mo->skin)-skins; - else - skin = 0; - - if (players[localplayers[i]].mo->color) - { - if (players[localplayers[i]].mo->colorized) - colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE); - else - colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE); - } - else - colormap = NULL; - - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, facemmapprefix[skin], colormap, AutomapPic); - - // Target reticule - if ((G_RaceGametype() && players[localplayers[i]].kartstuff[k_position] == spbplace) - || (G_BattleGametype() && K_IsPlayerWanted(&players[localplayers[i]]))) - K_drawKartMinimapIcon(players[localplayers[i]].mo->x, players[localplayers[i]].mo->y, x, y, splitflags, kp_wantedreticle, NULL, AutomapPic); - } -} - -static void K_drawKartStartCountdown(void) -{ - INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3 - - if (leveltime >= starttime-(2*TICRATE)) // 2 - pnum++; - if (leveltime >= starttime-TICRATE) // 1 - pnum++; - if (leveltime >= starttime) // GO! - pnum++; - if ((leveltime % (2*5)) / 5) // blink - pnum += 4; - if (r_splitscreen) // splitscreen - pnum += 8; - - V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); -} - -static void K_drawKartFinish(void) -{ - INT32 pnum = 0, splitflags = K_calcSplitFlags(0); - - if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE) - return; - - if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink - pnum = 1; - - if (r_splitscreen > 1) // 3/4p, stationary FIN - { - pnum += 2; - V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]); - return; - } - - //else -- 1/2p, scrolling FINISH - { - INT32 x, xval; - - if (r_splitscreen) // wide splitscreen - pnum += 4; - - x = ((vid.width<width)<karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; - - if (r_splitscreen && stplyr == &players[displayplayers[1]]) - x = -x; - - V_DrawFixedPatch(x + (STCD_X<>1), - (STCD_Y<height)<<(FRACBITS-1)), - FRACUNIT, - splitflags, kp_racefinish[pnum], NULL); - } -} - -static void K_drawBattleFullscreen(void) -{ - INT32 x = BASEVIDWIDTH/2; - INT32 y = -64+(stplyr->karthud[khud_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen - INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead - fixed_t scale = FRACUNIT; - boolean drawcomebacktimer = true; // lazy hack because it's cleaner in the long run. -#ifdef HAVE_BLUA - if (!LUA_HudEnabled(hud_battlecomebacktimer)) - drawcomebacktimer = false; -#endif - - if (r_splitscreen) - { - if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - || (r_splitscreen > 1 && (stplyr == &players[displayplayers[2]] - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)))) - { - y = 232-(stplyr->karthud[khud_cardanimation]/2); - splitflags = V_SNAPTOBOTTOM; - } - else - y = -32+(stplyr->karthud[khud_cardanimation]/2); - - if (r_splitscreen > 1) - { - scale /= 2; - - if (stplyr == &players[displayplayers[1]] - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) - x = 3*BASEVIDWIDTH/4; - else - x = BASEVIDWIDTH/4; - } - else - { - if (stplyr->exiting) - { - if (stplyr == &players[displayplayers[1]]) - x = BASEVIDWIDTH-96; - else - x = 96; - } - else - scale /= 2; - } - } - - if (stplyr->exiting) - { - if (stplyr == &players[displayplayers[0]]) - V_DrawFadeScreen(0xFF00, 16); - if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) - { - patch_t *p = kp_battlecool; - - if (K_IsPlayerLosing(stplyr)) - p = kp_battlelose; - else if (stplyr->kartstuff[k_position] == 1) - p = kp_battlewin; - - V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator && drawcomebacktimer) - { - UINT16 t = stplyr->kartstuff[k_comebacktimer]/(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; - - txoff = adjust; - - while (t) - { - txoff += adjust; - t /= 10; - } - - if (r_splitscreen) - { - if (r_splitscreen > 1) - ty = (BASEVIDHEIGHT/4)+33; - if ((r_splitscreen == 1 && stplyr == &players[displayplayers[1]]) - || (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - || (stplyr == &players[displayplayers[3]] && r_splitscreen > 2)) - ty += (BASEVIDHEIGHT/2); - } - else - V_DrawFadeScreen(0xFF00, 16); - - if (!comebackshowninfo) - V_DrawFixedPatch(x< 1) - V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE)); - else - { - V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE)); - } - } - - if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY? - { - UINT8 i; - - // check to see if there's anyone else at all - for (i = 0; i < MAXPLAYERS; i++) - { - if (i == displayplayers[0]) - continue; - if (playeringame[i] && !stplyr->spectator) - return; - } - -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_freeplay)) -#endif - K_drawKartFreePlay(leveltime); - } -} - -static void K_drawKartFirstPerson(void) -{ - static INT32 pnum[4], turn[4], drift[4]; - INT32 pn = 0, tn = 0, dr = 0; - INT32 target = 0, splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); - INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT; - fixed_t scale; - UINT8 *colmap = NULL; - ticcmd_t *cmd = &stplyr->cmd; - - if (stplyr->spectator || !stplyr->mo || (stplyr->mo->drawflags & MFD_DONTDRAW)) - return; - - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } - else - { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } - - if (r_splitscreen) - { - y >>= 1; - if (r_splitscreen > 1) - x >>= 1; - } - - { - if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen) - y++; - - if (stplyr->mo->drawflags & MFD_TRANSMASK) - splitflags |= ((stplyr->mo->drawflags & MFD_TRANSMASK) >> MFD_TRANSSHIFT) << FF_TRANSSHIFT; - else if (stplyr->mo->frame & FF_TRANSMASK) - splitflags |= (stplyr->mo->frame & FF_TRANSMASK); - } - - if (cmd->driftturn > 400) // strong left turn - target = 2; - else if (cmd->driftturn < -400) // strong right turn - target = -2; - else if (cmd->driftturn > 0) // weak left turn - target = 1; - else if (cmd->driftturn < 0) // weak right turn - target = -1; - else // forward - target = 0; - - if (encoremode) - target = -target; - - if (pn < target) - pn++; - else if (pn > target) - pn--; - - if (pn < 0) - splitflags |= V_FLIP; // right turn - - target = abs(pn); - if (target > 2) - target = 2; - - x <<= FRACBITS; - y <<= FRACBITS; - - if (tn != cmd->driftturn/50) - tn -= (tn - (cmd->driftturn/50))/8; - - if (dr != stplyr->kartstuff[k_drift]*16) - dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8; - - if (r_splitscreen == 1) - { - scale = (2*FRACUNIT)/3; - y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view) - } - else if (r_splitscreen) - scale = FRACUNIT/2; - else - scale = FRACUNIT; - - if (stplyr->mo) - { - UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->kartstuff[k_driftcharge]); - const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle; - // yes, the following is correct. no, you do not need to swap the x and y. - fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2); - fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT); - - if (r_splitscreen) - xoffs = FixedMul(xoffs, scale); - - xoffs -= (tn)*scale; - xoffs -= (dr)*scale; - - if (stplyr->frameangle == stplyr->mo->angle) - { - const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale); - - if (mag < FRACUNIT) - { - xoffs = FixedMul(xoffs, mag); - if (!r_splitscreen) - yoffs = FixedMul(yoffs, mag); - } - } - - if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if! - yoffs += stplyr->mo->momz/3; - - if (encoremode) - x -= xoffs; - else - x += xoffs; - if (!r_splitscreen) - y += yoffs; - - - if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks! - colmap = R_GetTranslationColormap(TC_RAINBOW, driftcolor, GTC_CACHE); - else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink! - colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE); - } - - V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap); - - if (stplyr == &players[displayplayers[1]] && r_splitscreen) - { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } - else if (stplyr == &players[displayplayers[2]] && r_splitscreen > 1) - { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } - else if (stplyr == &players[displayplayers[3]] && r_splitscreen > 2) - { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } - else - { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } -} - -// doesn't need to ever support 4p -static void K_drawInput(void) -{ - static INT32 pn = 0; - INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT); - INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col; - const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5]; - const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9]; - ticcmd_t *cmd = &stplyr->cmd; - - if (timeinmap <= 105) - return; - - if (timeinmap < 113) - { - INT32 count = ((INT32)(timeinmap) - 105); - offs = 64; - while (count-- > 0) - offs >>= 1; - x += offs; - } - -#define BUTTW 8 -#define BUTTH 11 - -#define drawbutt(xoffs, butt, symb)\ - if (stplyr->cmd.buttons & butt)\ - {\ - offs = 2;\ - col = accent1;\ - }\ - else\ - {\ - offs = 0;\ - col = accent2;\ - V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\ - }\ - V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\ - V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn - target = 0; - else // turning of multiple strengths! - { - target = ((abs(cmd->driftturn) - 1)/125)+1; - if (target > 4) - target = 4; - if (cmd->driftturn < 0) - target = -target; - } - - if (pn != target) - { - if (abs(pn - target) == 1) - pn = target; - else if (pn < target) - pn += 2; - else //if (pn > target) - pn -= 2; - } - - if (pn < 0) - { - splitflags |= V_FLIP; // right turn - x--; - } - - target = abs(pn); - if (target > 4) - target = 4; - - if (!stplyr->skincolor) - V_DrawFixedPatch(x<skincolor, GTC_CACHE); - V_DrawFixedPatch(x<karthud[khud_lapanimation]; - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); - - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, - (48 - (32*max(0, progress-76)))*FRACUNIT, - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap); - - if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3) - { - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->karthud[khud_lapanimation]-76)))*FRACUNIT, - (48 - (32*max(0, progress-76)) - + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT, - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); - } - - if (stplyr->laps == (UINT8)(cv_numlaps.value)) - { - V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_final[min(progress/2, 10)], NULL); - - if (progress/2-12 >= 0) - { - V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_lap[min(progress/2-12, 6)], NULL); - } - } - else - { - V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_lap[min(progress/2, 6)], NULL); - - if (progress/2-8 >= 0) - { - V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps) / 10)][min(progress/2-8, 2)], NULL); - - if (progress/2-10 >= 0) - { - V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221 - 30*FRACUNIT, // 24 - FRACUNIT, V_SNAPTOTOP|V_HUDTRANS, - kp_lapanim_number[(((UINT32)stplyr->laps) % 10)][min(progress/2-10, 2)], NULL); - } - } - } -} - -void K_drawKartFreePlay(UINT32 flashtime) -{ - // no splitscreen support because it's not FREE PLAY if you have more than one player in-game - // (you fool, you can take splitscreen online. :V) - - if ((flashtime % TICRATE) < TICRATE/2) - return; - - V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy - LAPS_Y+3, V_HUDTRANS|V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY"); -} - -static void -Draw_party_ping (int ss, INT32 snap) -{ - HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|snap); -} - -static void -K_drawMiniPing (void) -{ - if (cv_showping.value) - { - switch (r_splitscreen) - { - case 3: - Draw_party_ping(3, V_SNAPTORIGHT|V_SPLITSCREEN); - /*FALLTHRU*/ - case 2: - Draw_party_ping(2, V_SPLITSCREEN); - Draw_party_ping(1, V_SNAPTORIGHT); - Draw_party_ping(0, 0); - break; - case 1: - Draw_party_ping(1, V_SNAPTORIGHT|V_SPLITSCREEN); - Draw_party_ping(0, V_SNAPTORIGHT); - break; - } - } -} - -static void K_drawDistributionDebugger(void) -{ - patch_t *items[NUMKARTRESULTS] = { - kp_sadface[1], - kp_sneaker[1], - kp_rocketsneaker[1], - kp_invincibility[7], - kp_banana[1], - kp_eggman[1], - kp_orbinaut[4], - kp_jawz[1], - kp_mine[1], - kp_ballhog[1], - kp_selfpropelledbomb[1], - kp_grow[1], - kp_shrink[1], - kp_thundershield[1], - kp_bubbleshield[1], - kp_flameshield[1], - kp_hyudoro[1], - kp_pogospring[1], - kp_superring[1], - kp_kitchensink[1], - - kp_sneaker[1], - kp_banana[1], - kp_banana[1], - kp_orbinaut[4], - kp_orbinaut[4], - kp_jawz[1] - }; - UINT8 useodds = 0; - UINT8 pingame = 0, bestbumper = 0; - UINT32 pdis = 0; - INT32 i; - INT32 x = -9, y = -9; - boolean spbrush = false; - - if (stplyr != &players[displayplayers[0]]) // only for p1 - return; - - // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator) - continue; - pingame++; - if (players[i].kartstuff[k_bumper] > bestbumper) - bestbumper = players[i].kartstuff[k_bumper]; - } - - // lovely double loop...... - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator - && players[i].kartstuff[k_position] == 1) - { - // This player is first! Yay! - pdis = stplyr->distancetofinish - players[i].distancetofinish; - break; - } - } - - if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items - pdis = (15 * pdis) / 14; - - if (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell - { - pdis = (3 * pdis) / 2; - spbrush = true; - } - - if (stplyr->bot && stplyr->botvars.rival) - { - // Rival has better odds :) - pdis = (15 * pdis) / 14; - } - - pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count - - useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); - - for (i = 1; i < NUMKARTRESULTS; i++) - { - const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); - if (itemodds <= 0) - continue; - - V_DrawScaledPatch(x, y, V_HUDTRANS|V_SNAPTOTOP, items[i]); - V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SNAPTOTOP, va("%d", itemodds)); - - // Display amount for multi-items - if (i >= NUMKARTITEMS) - { - INT32 amount; - switch (i) - { - case KRITEM_TENFOLDBANANA: - amount = 10; - break; - case KRITEM_QUADORBINAUT: - amount = 4; - break; - case KRITEM_DUALJAWZ: - amount = 2; - break; - default: - amount = 3; - break; - } - V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SNAPTOTOP, va("x%d", amount)); - } - - x += 32; - if (x >= 297) - { - x = -9; - y += 32; - } - } - - V_DrawString(0, 0, V_HUDTRANS|V_SNAPTOTOP, va("USEODDS %d", useodds)); -} - -static void K_drawCheckpointDebugger(void) -{ - if (stplyr != &players[displayplayers[0]]) // only for p1 - return; - - if (stplyr->starpostnum == numstarposts) - V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts)); - else - V_DrawString(8, 184, 0, va("Checkpoint: %d / %d", stplyr->starpostnum, numstarposts)); -} - -static void K_DrawWaypointDebugger(void) -{ - if ((cv_kartdebugwaypoints.value != 0) && (stplyr == &players[displayplayers[0]])) - { - V_DrawString(8, 166, 0, va("'Best' Waypoint ID: %d", K_GetWaypointID(stplyr->nextwaypoint))); - V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish)); - } -} - -void K_drawKartHUD(void) -{ - boolean isfreeplay = false; - boolean battlefullscreen = false; - boolean freecam = demo.freecam; //disable some hud elements w/ freecam - UINT8 i; - - // Define the X and Y for each drawn object - // This is handled by console/menu values - K_initKartHUD(); - - // Draw that fun first person HUD! Drawn ASAP so it looks more "real". - for (i = 0; i <= r_splitscreen; i++) - { - if (stplyr == &players[displayplayers[i]] && !camera[i].chase && !freecam) - K_drawKartFirstPerson(); - } - - // Draw full screen stuff that turns off the rest of the HUD - if (mapreset && stplyr == &players[displayplayers[0]]) - { - K_drawChallengerScreen(); - return; - } - - battlefullscreen = ((G_BattleGametype()) - && (stplyr->exiting - || (stplyr->kartstuff[k_bumper] <= 0 - && stplyr->kartstuff[k_comebacktimer] - && comeback - && stplyr->playerstate == PST_LIVE))); - - if (!demo.title && (!battlefullscreen || r_splitscreen)) - { - // Draw the CHECK indicator before the other items, so it's overlapped by everything else -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_check)) // delete lua when? -#endif - if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam) - K_drawKartPlayerCheck(); - - // nametags -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_names)) -#endif - K_drawKartNameTags(); - - // Draw WANTED status - if (G_BattleGametype()) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_wanted)) -#endif - K_drawKartWanted(); - } - - if (cv_kartminimap.value) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_minimap)) -#endif - K_drawKartMinimap(); - } - } - - if (battlefullscreen && !freecam) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_battlefullscreen)) -#endif - K_drawBattleFullscreen(); - return; - } - - // Draw the item window -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_item) && !freecam) -#endif - K_drawKartItem(); - - // If not splitscreen, draw... - if (!r_splitscreen && !demo.title) - { - // Draw the timestamp -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_time)) -#endif - K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0); - - if (!modeattacking) - { - // The top-four faces on the left - /*#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_minirankings)) - #endif*/ - isfreeplay = K_drawKartPositionFaces(); - } - } - - if (!stplyr->spectator && !demo.freecam) // Bottom of the screen elements, don't need in spectate mode - { - // Draw the speedometer - if (cv_kartspeedometer.value && !r_splitscreen) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_speedometer)) -#endif - K_drawKartSpeedometer(); - } - - if (demo.title) // Draw title logo instead in demo.titles - { - INT32 x = BASEVIDWIDTH - 32, y = 128, offs; - - if (r_splitscreen == 3) - { - x = BASEVIDWIDTH/2 + 10; - y = BASEVIDHEIGHT/2 - 30; - } - - if (timeinmap < 113) - { - INT32 count = ((INT32)(timeinmap) - 104); - offs = 256; - while (count-- > 0) - offs >>= 1; - x += offs; - } - - V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); - V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); - } - else if (G_RaceGametype()) // Race-only elements - { - // Draw the lap counter -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartLapsAndRings(); - - if (isfreeplay) - ; - else if (!modeattacking) - { - // Draw the numerical position -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_position)) -#endif - K_DrawKartPositionNum(stplyr->kartstuff[k_position]); - } - else //if (!(demo.playback && hu_showscores)) - { - // Draw the input UI -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_position)) -#endif - K_drawInput(); - } - } - else if (G_BattleGametype()) // Battle-only - { - // Draw the hits left! -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartBumpersOrKarma(); - } - } - - // Draw the countdowns after everything else. - if (leveltime >= starttime-(3*TICRATE) - && leveltime < starttime+TICRATE) - K_drawKartStartCountdown(); - else if (racecountdown && (!r_splitscreen || !stplyr->exiting)) - { - char *countstr = va("%d", racecountdown/TICRATE); - - if (r_splitscreen > 1) - V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, K_calcSplitFlags(0), countstr); - else - { - INT32 karlen = strlen(countstr)*6; // half of 12 - V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, K_calcSplitFlags(0), countstr); - } - } - - // Race overlays - if (G_RaceGametype() && !freecam) - { - if (stplyr->exiting) - K_drawKartFinish(); - else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen) - K_drawLapStartAnim(); - } - - if (modeattacking || freecam) // everything after here is MP and debug only - return; - - if (G_BattleGametype() && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * - V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); - - // Draw FREE PLAY. - if (isfreeplay && !stplyr->spectator && timeinmap > 113) - { -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_freeplay)) -#endif - K_drawKartFreePlay(leveltime); - } - - if (r_splitscreen == 0 && stplyr->kartstuff[k_wrongway] && ((leveltime / 8) & 1)) - { - V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); - } - - if (netgame && r_splitscreen) - { - K_drawMiniPing(); - } - - if (cv_kartdebugdistribution.value) - K_drawDistributionDebugger(); - - if (cv_kartdebugcheckpoint.value) - K_drawCheckpointDebugger(); - - if (cv_kartdebugnodes.value) - { - UINT8 p; - for (p = 0; p < MAXPLAYERS; p++) - V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency)); - } - - if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin) - { - INT32 x = 0, y = 0; - UINT8 c; - - for (c = 1; c < MAXSKINCOLORS; c++) - { - UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, c, GTC_CACHE); - V_DrawFixedPatch(x<>1, 0, facewantprefix[stplyr->skin], cm); - - x += 16; - if (x > BASEVIDWIDTH-16) - { - x = 0; - y += 16; - } - } - } - - K_DrawWaypointDebugger(); -} - -//} diff --git a/src/k_kart.h b/src/k_kart.h index 288866208..f9b9ff72a 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -23,6 +23,8 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value); extern consvar_t *KartItemCVars[NUMKARTRESULTS-1]; +UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush); +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); @@ -85,12 +87,5 @@ void K_PlayPainSound(mobj_t *source); void K_PlayHitEmSound(mobj_t *source); void K_PlayPowerGloatSound(mobj_t *source); -const char *K_GetItemPatch(UINT8 item, boolean tiny); -INT32 K_calcSplitFlags(INT32 snapflags); -void K_LoadKartHUDGraphics(void); -void K_drawKartHUD(void); -void K_drawKartFreePlay(UINT32 flashtime); -void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode); - // ========================================================================= #endif // __K_KART__ diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 15eba4136..9916ceab8 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -28,6 +28,7 @@ #include "k_kart.h" // SRB2Kart #include "k_battle.h" #include "k_color.h" +#include "k_hud.h" #include "d_netcmd.h" // IsPlayerAdmin #include "lua_script.h" diff --git a/src/m_menu.c b/src/m_menu.c index 5a8c6e68e..b4302d90a 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -56,7 +56,8 @@ #include "byteptr.h" #include "st_stuff.h" #include "i_sound.h" -#include "k_kart.h" // SRB2kart +#include "k_hud.h" // SRB2kart +#include "k_kart.h" // KartItemCVars #include "k_pwrlv.h" #include "d_player.h" // KITEM_ constants #include "k_color.h" diff --git a/src/st_stuff.c b/src/st_stuff.c index 4b5bc7d09..5105172d2 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -28,7 +28,7 @@ #include "m_menu.h" #include "m_cheat.h" #include "p_setup.h" // NiGHTS grading -#include "k_kart.h" // SRB2kart +#include "k_hud.h" // SRB2kart //random index #include "m_random.h" @@ -699,7 +699,7 @@ static inline void ST_drawRings(void) // SRB2kart - unused. /* static void ST_drawLives(void) // SRB2kart - unused. { - const INT32 v_splitflag = (splitscreen && stplyr == &players[displayplayers[0]] ? V_SPLITSCREEN : 0); + const INT32 v_splitflag = V_SPLITSCREEN; if (!stplyr->skincolor) return; // Just joined a server, skin isn't loaded yet! @@ -1920,11 +1920,11 @@ static void ST_overlayDrawer(void) INT32 y = (stplyr == &players[displayplayers[0]]) ? 4 : BASEVIDHEIGHT/2-12; sprintf(name, "VIEWPOINT: %s", player_names[stplyr-players]); - V_DrawRightAlignedThinString(BASEVIDWIDTH-40, y, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOBOTTOM|V_SNAPTORIGHT), name); + V_DrawRightAlignedThinString(BASEVIDWIDTH-40, y, V_HUDTRANSHALF|V_ALLOWLOWERCASE|V_SNAPTOTOP|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, name); } else if (r_splitscreen) { - V_DrawCenteredThinString((vid.width/vid.dupx)/4, BASEVIDHEIGHT/2 - 12, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT), player_names[stplyr-players]); + V_DrawCenteredThinString((vid.width/vid.dupx)/4, BASEVIDHEIGHT/2 - 12, V_HUDTRANSHALF|V_ALLOWLOWERCASE|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN, player_names[stplyr-players]); } } } @@ -2000,16 +2000,15 @@ static void ST_overlayDrawer(void) // SRB2kart: changed positions & text if (r_splitscreen) { - INT32 splitflags = K_calcSplitFlags(0); - V_DrawThinString(2, (BASEVIDHEIGHT/2)-20, V_YELLOWMAP|V_HUDTRANSHALF|splitflags, M_GetText("- SPECTATING -")); - V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, itemtxt); + V_DrawThinString(2, (BASEVIDHEIGHT/2)-20, V_YELLOWMAP|V_HUDTRANSHALF|V_SPLITSCREEN, M_GetText("- SPECTATING -")); + V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|V_SPLITSCREEN, itemtxt); } else { - V_DrawString(2, BASEVIDHEIGHT-40, V_HUDTRANSHALF|V_YELLOWMAP, M_GetText("- SPECTATING -")); - V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, itemtxt); - V_DrawString(2, BASEVIDHEIGHT-20, V_HUDTRANSHALF, M_GetText("Accelerate - Float")); - V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF, M_GetText("Brake - Sink")); + V_DrawString(2, BASEVIDHEIGHT-40, V_HUDTRANSHALF|V_SPLITSCREEN|V_YELLOWMAP, M_GetText("- SPECTATING -")); + V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF|V_SPLITSCREEN, itemtxt); + V_DrawString(2, BASEVIDHEIGHT-20, V_HUDTRANSHALF|V_SPLITSCREEN, M_GetText("Accelerate - Float")); + V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF|V_SPLITSCREEN, M_GetText("Brake - Sink")); } } } diff --git a/src/v_video.c b/src/v_video.c index f51b9ab2c..3771419d5 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -26,6 +26,8 @@ #include "m_random.h" #include "doomstat.h" +#include "k_hud.h" + #ifdef HWRENDER #include "hardware/hw_main.h" #endif @@ -608,12 +610,6 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t y -= offsety; } - if (scrn & V_SPLITSCREEN) - y += (BASEVIDHEIGHT/2)<width)<>FRACBITS == BASEVIDWIDTH - && y == 0 && FixedMul(SHORT(patch->height)<>FRACBITS == BASEVIDHEIGHT) - { - column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[0])); - source = (const UINT8 *)(column) + 3; - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); - }*/ - - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if ((scrn & (V_HORZSCREEN|V_SNAPTOLEFT)) == (V_HORZSCREEN|V_SNAPTOLEFT)) - x += (vid.width/2 - (BASEVIDWIDTH/2 * dupx)); - else if (scrn & V_SNAPTORIGHT) - x += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(scrn & V_SNAPTOLEFT)) - x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; - } - if (vid.height != BASEVIDHEIGHT * dupy) - { - // same thing here - if ((scrn & (V_SPLITSCREEN|V_SNAPTOTOP)) == (V_SPLITSCREEN|V_SNAPTOTOP)) - y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)); - else if (scrn & V_SNAPTOBOTTOM) - y += (vid.height - (BASEVIDHEIGHT * dupy)); - else if (!(scrn & V_SNAPTOTOP)) - y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; - } + K_AdjustXYWithSnap(&x, &y, scrn, dupx, dupy); } desttop += (y*vid.width) + x; @@ -923,31 +888,12 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) h *= dupy; // Center it if necessary - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if (c & V_SNAPTORIGHT) - x += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(c & V_SNAPTOLEFT)) - x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; - } - if (vid.height != BASEVIDHEIGHT * dupy) - { - // same thing here - if (c & V_SNAPTOBOTTOM) - y += (vid.height - (BASEVIDHEIGHT * dupy)); - else if (!(c & V_SNAPTOTOP)) - y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; - } - if (c & V_SPLITSCREEN) - y += (BASEVIDHEIGHT * dupy)/2; - if (c & V_HORZSCREEN) - x += (BASEVIDWIDTH * dupx)/2; + K_AdjustXYWithSnap(&x, &y, c, dupx, dupy); } if (x >= vid.width || y >= vid.height) return; // off the screen + if (x < 0) { w += x; @@ -1151,27 +1097,7 @@ void V_DrawDiag(INT32 x, INT32 y, INT32 wh, INT32 c) wh *= dupx; // Center it if necessary - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if (c & V_SNAPTORIGHT) - x += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(c & V_SNAPTOLEFT)) - x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; - } - if (vid.height != BASEVIDHEIGHT * dupy) - { - // same thing here - if (c & V_SNAPTOBOTTOM) - y += (vid.height - (BASEVIDHEIGHT * dupy)); - else if (!(c & V_SNAPTOTOP)) - y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; - } - if (c & V_SPLITSCREEN) - y += (BASEVIDHEIGHT * dupy)/2; - if (c & V_HORZSCREEN) - x += (BASEVIDWIDTH * dupx)/2; + K_AdjustXYWithSnap(&x, &y, c, dupx, dupy); } if (x >= vid.width || y >= vid.height) diff --git a/src/v_video.h b/src/v_video.h index 2066d1399..95b3f16fb 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -129,9 +129,9 @@ extern UINT8 hudtrans; #define V_WRAPX 0x08000000 // Don't clamp texture on X (for HW mode) #define V_WRAPY 0x10000000 // Don't clamp texture on Y (for HW mode) -#define V_NOSCALESTART 0x20000000 // don't scale x, y, start coords -#define V_SPLITSCREEN 0x40000000 -#define V_HORZSCREEN 0x80000000 +#define V_NOSCALESTART 0x20000000 // don't scale x, y, start coords +#define V_SPLITSCREEN 0x40000000 // Add half of screen width or height automatically depending on player number +#define V_SLIDEIN 0x80000000 // Slide in from the sides on level load, depending on snap flags // defines for old functions #define V_DrawPatch(x,y,s,p) V_DrawFixedPatch((x)< Date: Sun, 26 Jul 2020 06:48:02 -0400 Subject: [PATCH 176/211] Fix nametags for splitscreen fov, sort drawing by distance --- src/k_hud.c | 205 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 140 insertions(+), 65 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index f4d4ad525..189f911d2 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2128,7 +2128,7 @@ static void K_drawKartWanted(void) } } -static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) +static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, vertex_t *point) { const INT32 swhalf = (BASEVIDWIDTH / 2); const fixed_t swhalffixed = swhalf * FRACUNIT; @@ -2136,11 +2136,28 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a const INT32 shhalf = (BASEVIDHEIGHT / 2); const fixed_t shhalffixed = shhalf * FRACUNIT; - INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); + + const UINT8 precisionloss = 4; + + fixed_t anglediff = (AngleFixed(camang) / precisionloss) - (AngleFixed(R_PointToAngle2(campos->x, campos->y, point->x, point->y)) / precisionloss); + fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); fixed_t factor = INT32_MAX; - if (abs(anglediff) > ANGLE_90) + const fixed_t fov = cv_fov.value; + fixed_t intendedfov = 90*FRACUNIT; + fixed_t fovmul = FRACUNIT; + + if (r_splitscreen == 1) // Splitscreen FOV should be adjusted to maintain expected vertical view + { + intendedfov = 17 * intendedfov / 10; + } + + fovmul = FixedDiv(fov, intendedfov); + + anglediff = FixedMul(anglediff, fovmul) * precisionloss; + + if (abs(anglediff) > 90*FRACUNIT) { if (hud_x != NULL) { @@ -2152,10 +2169,12 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a *hud_y = -BASEVIDWIDTH * FRACUNIT; } - //*hud_scale = FRACUNIT; + //*hud_scale = 0; return; } + anglediff = FixedAngle(anglediff); + factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); #define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) @@ -2172,11 +2191,6 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a if (r_splitscreen >= 2) { *hud_x /= 2; - - if (camnum & 1) - { - *hud_x += swhalffixed; - } } } @@ -2190,12 +2204,6 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a if (r_splitscreen >= 1) { *hud_y /= 2; - - if ((r_splitscreen == 1 && camnum == 1) - || (r_splitscreen > 1 && camnum > 1)) - { - *hud_y += shhalffixed; - } } } @@ -2299,10 +2307,10 @@ static void K_drawKartPlayerCheck(void) pnum += 2; } - K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); + K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, &v); colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); - V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|V_SLIDEIN|splitflags, kp_check[pnum], colormap); + V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN|splitflags, kp_check[pnum], colormap); } } @@ -2330,13 +2338,73 @@ static boolean K_ShowPlayerNametag(player_t *p) return true; } +static void K_DrawRivalTagForPlayer(fixed_t x, fixed_t y) +{ + UINT8 blink = ((leveltime / 7) & 1); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_rival[blink], NULL); +} + +static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT8 cnum) +{ + INT32 namelen = V_ThinStringWidth(player_names[p - players], V_6WIDTHSPACE|V_ALLOWLOWERCASE); + INT32 clr = K_SkincolorToTextColor(p->skincolor); + UINT8 *colormap = V_GetStringColormap(clr); + INT32 barx = 0, bary = 0, barw = 0; + + // Since there's no "V_DrawFixedFill", and I don't feel like making it, + // fuck it, we're gonna just V_NOSCALESTART hack it + if (cnum & 1) + { + x += (BASEVIDWIDTH/2) * FRACUNIT; + } + + if ((r_splitscreen == 1 && cnum == 1) + || (r_splitscreen > 1 && cnum > 1)) + { + y += (BASEVIDHEIGHT/2) * FRACUNIT; + } + + barw = (namelen * vid.dupx); + + barx = (x * vid.dupx) / FRACUNIT; + bary = (y * vid.dupy) / FRACUNIT; + + barx += (6 * vid.dupx); + bary -= (16 * vid.dupx); + + // Center it if necessary + if (vid.width != BASEVIDWIDTH * vid.dupx) + { + barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; + } + + if (vid.height != BASEVIDHEIGHT * vid.dupy) + { + bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; + } + + // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. + V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); + V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); + // END DRAWFILL DUMBNESS + + // Draw the stem + V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); + + // Draw the name itself + V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[p - players]); +} + static void K_drawKartNameTags(void) { const fixed_t maxdistance = 8192*mapobjectscale; camera_t *thiscam; vertex_t c; UINT8 cnum = 0; - UINT8 i; + UINT8 tobesorted[MAXPLAYERS]; + fixed_t sortdist[MAXPLAYERS]; + UINT8 sortlen = 0; + UINT8 i, j; if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) { @@ -2370,10 +2438,6 @@ static void K_drawKartNameTags(void) { player_t *ntplayer = &players[i]; fixed_t distance = maxdistance+1; - - fixed_t x = -BASEVIDWIDTH * FRACUNIT; - fixed_t y = -BASEVIDWIDTH * FRACUNIT; - vertex_t v; if (!playeringame[i] || ntplayer->spectator) @@ -2396,8 +2460,6 @@ static void K_drawKartNameTags(void) if (!(demo.playback == true && demo.freecam == true)) { - UINT8 j; - for (j = 0; j <= r_splitscreen; j++) { if (ntplayer == &players[displayplayers[j]]) @@ -2431,62 +2493,75 @@ static void K_drawKartNameTags(void) continue; } - K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); + tobesorted[sortlen] = ntplayer - players; + sortdist[sortlen] = distance; + sortlen++; + } - if (x == -BASEVIDWIDTH * FRACUNIT) - { - // Off-screen - continue; - } + if (sortlen > 0) + { + UINT8 sortedplayers[sortlen]; - if (ntplayer->bot) + for (i = 0; i < sortlen; i++) { - if (ntplayer->botvars.rival == true) + UINT8 pos = 0; + + for (j = 0; j < sortlen; j++) { - UINT8 blink = ((leveltime / 7) & 1); - V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); + if (j == i) + { + continue; + } + + if (sortdist[i] < sortdist[j] + || (sortdist[i] == sortdist[j] && i > j)) + { + pos++; + } } + + sortedplayers[pos] = tobesorted[i]; } - else if (netgame || demo.playback) + + for (i = 0; i < sortlen; i++) { - if (K_ShowPlayerNametag(ntplayer) == true) + player_t *ntplayer = &players[sortedplayers[i]]; + + fixed_t x = -BASEVIDWIDTH * FRACUNIT; + fixed_t y = -BASEVIDWIDTH * FRACUNIT; + + vertex_t v; + + v.x = ntplayer->mo->x; + v.y = ntplayer->mo->y; + v.z = ntplayer->mo->z; + + if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) { - INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); - INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); - UINT8 *colormap = V_GetStringColormap(clr); - INT32 barx = 0, bary = 0, barw = 0; + v.z += ntplayer->mo->height; + } - // Since there's no "V_DrawFixedFill", and I don't feel like making it, - // fuck it, we're gonna just V_NOSCALESTART hack it - barw = (namelen * vid.dupx); + K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, &v); - barx = (x * vid.dupx) / FRACUNIT; - bary = (y * vid.dupy) / FRACUNIT; + if (x == -BASEVIDWIDTH * FRACUNIT) + { + // Off-screen + continue; + } - barx += (6 * vid.dupx); - bary -= (16 * vid.dupx); - - // Center it if necessary - if (vid.width != BASEVIDWIDTH * vid.dupx) + if (ntplayer->bot) + { + if (ntplayer->botvars.rival == true) { - barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; + K_DrawRivalTagForPlayer(x, y); } - - if (vid.height != BASEVIDHEIGHT * vid.dupy) + } + else if (netgame || demo.playback) + { + if (K_ShowPlayerNametag(ntplayer) == true) { - bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; + K_DrawNameTagForPlayer(x, y, ntplayer, cnum); } - - // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. - V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); - V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); - // END DRAWFILL DUMBNESS - - // Draw the stem - V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); - - // Draw the name itself - V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); } } } From 90cde7379c4527605731dfd43a7f4e83537c24cd Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 26 Jul 2020 20:27:22 +0200 Subject: [PATCH 177/211] Revert "Push flipcam down the nearest staircase" This reverts commit 06d70c1f8e247d88449690cc5ed3bc93926d3feb. --- src/d_netcmd.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/d_player.h | 62 +++++++++++++++++++++++++------------------------ src/dehacked.c | 3 +++ src/g_game.c | 15 +++++++----- src/p_mobj.c | 24 +++++++++++++++++++ src/r_main.c | 33 ++++++++++++++++++++++++++ src/r_main.h | 1 + 7 files changed, 165 insertions(+), 36 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9ade70301..b55441b2d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -65,6 +65,7 @@ // ------ static void Got_NameAndColor(UINT8 **cp, INT32 playernum); +static void Got_WeaponPref(UINT8 **cp, INT32 playernum); static void Got_PowerLevel(UINT8 **cp, INT32 playernum); static void Got_PartyInvite(UINT8 **cp, INT32 playernum); static void Got_AcceptPartyInvite(UINT8 **cp, INT32 playernum); @@ -221,6 +222,11 @@ static void Command_KartGiveItem_f(void); // CLIENT VARIABLES // ========================================================================= +void SendWeaponPref(void); +void SendWeaponPref2(void); +void SendWeaponPref3(void); +void SendWeaponPref4(void); + static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}}; #if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) static CV_PossibleValue_t mouse2port_cons_t[] = {{0, "/dev/gpmdata"}, {1, "/dev/ttyS0"}, @@ -624,6 +630,7 @@ void D_RegisterServerCommands(void) Forceskin_cons_t[i].strvalue = NULL; } RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor); + RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref); RegisterNetXCmd(XD_POWERLEVEL, Got_PowerLevel); RegisterNetXCmd(XD_PARTYINVITE, Got_PartyInvite); RegisterNetXCmd(XD_ACCEPTPARTYINVITE, Got_AcceptPartyInvite); @@ -2093,6 +2100,55 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) SetFollower(playernum, follower); } +void SendWeaponPref(void) +{ + XBOXSTATIC UINT8 buf[1]; + + buf[0] = 0; + if (cv_flipcam.value) + buf[0] |= 1; + SendNetXCmd(XD_WEAPONPREF, buf, 1); +} + +void SendWeaponPref2(void) +{ + XBOXSTATIC UINT8 buf[1]; + + buf[0] = 0; + if (cv_flipcam2.value) + buf[0] |= 1; + SendNetXCmd2(XD_WEAPONPREF, buf, 1); +} + +void SendWeaponPref3(void) +{ + XBOXSTATIC UINT8 buf[1]; + + buf[0] = 0; + if (cv_flipcam3.value) + buf[0] |= 1; + SendNetXCmd3(XD_WEAPONPREF, buf, 1); +} + +void SendWeaponPref4(void) +{ + XBOXSTATIC UINT8 buf[1]; + + buf[0] = 0; + if (cv_flipcam4.value) + buf[0] |= 1; + SendNetXCmd4(XD_WEAPONPREF, buf, 1); +} + +static void Got_WeaponPref(UINT8 **cp,INT32 playernum) +{ + UINT8 prefs = READUINT8(*cp); + + players[playernum].pflags &= ~(PF_FLIPCAM); + if (prefs & 1) + players[playernum].pflags |= PF_FLIPCAM; +} + static void Got_PowerLevel(UINT8 **cp,INT32 playernum) { UINT16 race = (UINT16)READUINT16(*cp); @@ -2300,6 +2356,13 @@ void D_SendPlayerConfig(void) SendNameAndColor3(); if (splitscreen > 2) SendNameAndColor4(); + SendWeaponPref(); + if (splitscreen) + SendWeaponPref2(); + if (splitscreen > 1) + SendWeaponPref3(); + if (splitscreen > 2) + SendWeaponPref4(); { UINT8 buf[4]; diff --git a/src/d_player.h b/src/d_player.h index 8ee63ca20..4ae420052 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -56,70 +56,72 @@ typedef enum // typedef enum { + // Flip camera angle with gravity flip prefrence. + PF_FLIPCAM = 1, // Cheats - PF_GODMODE = 1, - PF_NOCLIP = 1<<1, - PF_INVIS = 1<<2, + PF_GODMODE = 1<<1, + PF_NOCLIP = 1<<2, + PF_INVIS = 1<<3, // True if button down last tic. - PF_ATTACKDOWN = 1<<3, - PF_USEDOWN = 1<<4, - PF_JUMPDOWN = 1<<5, - PF_WPNDOWN = 1<<6, + PF_ATTACKDOWN = 1<<4, + PF_USEDOWN = 1<<5, + PF_JUMPDOWN = 1<<6, + PF_WPNDOWN = 1<<7, // Unmoving states - PF_STASIS = 1<<7, // Player is not allowed to move - PF_JUMPSTASIS = 1<<8, // and that includes jumping. + PF_STASIS = 1<<8, // Player is not allowed to move + PF_JUMPSTASIS = 1<<9, // and that includes jumping. PF_FULLSTASIS = PF_STASIS|PF_JUMPSTASIS, // Did you get a time-over? - PF_TIMEOVER = 1<<9, + PF_TIMEOVER = 1<<10, // SRB2Kart: Spectator that wants to join - PF_WANTSTOJOIN = 1<<10, + PF_WANTSTOJOIN = 1<<11, // Character action status - PF_JUMPED = 1<<11, - PF_SPINNING = 1<<12, - PF_STARTDASH = 1<<13, - PF_THOKKED = 1<<14, + PF_JUMPED = 1<<12, + PF_SPINNING = 1<<13, + PF_STARTDASH = 1<<14, + PF_THOKKED = 1<<15, // Are you gliding? - PF_GLIDING = 1<<15, + PF_GLIDING = 1<<16, // Tails pickup! - PF_CARRIED = 1<<16, + PF_CARRIED = 1<<17, // Sliding (usually in water) like Labyrinth/Oil Ocean - PF_SLIDING = 1<<17, + PF_SLIDING = 1<<18, // Hanging on a rope - PF_ROPEHANG = 1<<18, + PF_ROPEHANG = 1<<19, // Hanging on an item of some kind - zipline, chain, etc. (->tracer) - PF_ITEMHANG = 1<<19, + PF_ITEMHANG = 1<<20, // On the mace chain spinning around (->tracer) - PF_MACESPIN = 1<<20, + PF_MACESPIN = 1<<21, /*** NIGHTS STUFF ***/ // Is the player in NiGHTS mode? - PF_NIGHTSMODE = 1<<21, - PF_TRANSFERTOCLOSEST = 1<<22, + PF_NIGHTSMODE = 1<<22, + PF_TRANSFERTOCLOSEST = 1<<23, // Spill rings after falling - PF_NIGHTSFALL = 1<<23, - PF_DRILLING = 1<<24, - PF_SKIDDOWN = 1<<25, + PF_NIGHTSFALL = 1<<24, + PF_DRILLING = 1<<25, + PF_SKIDDOWN = 1<<26, /*** TAG STUFF ***/ - PF_TAGGED = 1<<26, // Player has been tagged and awaits the next round in hide and seek. - PF_TAGIT = 1<<27, // The player is it! For Tag Mode + PF_TAGGED = 1<<27, // Player has been tagged and awaits the next round in hide and seek. + PF_TAGIT = 1<<28, // The player is it! For Tag Mode /*** misc ***/ - PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs - PF_HITFINISHLINE = 1<<29, // Already hit the finish line this tic + PF_FORCESTRAFE = 1<<29, // Turning inputs are translated into strafing inputs + PF_HITFINISHLINE = 1<<30, // Already hit the finish line this tic // free: 1<<30 and 1<<31 } pflags_t; diff --git a/src/dehacked.c b/src/dehacked.c index 92e80a475..372863bd5 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8481,6 +8481,9 @@ static const char *const MAPTHINGFLAG_LIST[4] = { #endif static const char *const PLAYERFLAG_LIST[] = { + // Flip camera angle with gravity flip prefrence. + "FLIPCAM", + // Cheats "GODMODE", "NOCLIP", diff --git a/src/g_game.c b/src/g_game.c index fed573d13..1b994ede1 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1506,7 +1506,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // spectator aiming shit, ahhhh... { INT32 player_invert = invertmouse ? -1 : 1; - INT32 screen_invert = (player->mo && (player->mo->eflags & MFE_VERTICALFLIP) && (!thiscam->chase)) ? -1 : 1; // set to -1 or 1 to multiply + INT32 screen_invert = + (player->mo && (player->mo->eflags & MFE_VERTICALFLIP) + && (!thiscam->chase || player->pflags & PF_FLIPCAM)) //because chasecam's not inverted + ? -1 : 1; // set to -1 or 1 to multiply // mouse look stuff (mouse look is not the same as mouse aim) if (mouseaiming && player->spectator) @@ -1674,7 +1677,7 @@ static void Analog_OnChange(void) } */ - //SendWeaponPref(); + SendWeaponPref(); } static void Analog2_OnChange(void) @@ -1691,7 +1694,7 @@ static void Analog2_OnChange(void) } */ - //SendWeaponPref2(); + SendWeaponPref2(); } static void Analog3_OnChange(void) @@ -1708,7 +1711,7 @@ static void Analog3_OnChange(void) } */ - //SendWeaponPref3(); + SendWeaponPref3(); } static void Analog4_OnChange(void) @@ -1725,7 +1728,7 @@ static void Analog4_OnChange(void) } */ - //SendWeaponPref4(); + SendWeaponPref4(); } // @@ -2616,7 +2619,7 @@ void G_PlayerReborn(INT32 player) jointime = players[player].jointime; splitscreenindex = players[player].splitscreenindex; spectator = players[player].spectator; - pflags = (players[player].pflags & (PF_TIMEOVER|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); + pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); // As long as we're not in multiplayer, carry over cheatcodes from map to map if (!(netgame || multiplayer)) diff --git a/src/p_mobj.c b/src/p_mobj.c index 027655f47..2547f3212 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1168,6 +1168,26 @@ static void P_PlayerFlip(mobj_t *mo) if (mo->tracer) mo->tracer->eflags ^= MFE_VERTICALFLIP; } + else if (mo->player->pflags & PF_FLIPCAM) + { + UINT8 i; + + mo->player->aiming = InvAngle(mo->player->aiming); + + for (i = 0; i <= r_splitscreen; i++) + { + if (mo->player-players == displayplayers[i]) + { + localaiming[i] = mo->player->aiming; + if (camera[i].chase) { + camera[i].aiming = InvAngle(camera[i].aiming); + camera[i].z = mo->z - camera[i].z + mo->z; + if (mo->eflags & MFE_VERTICALFLIP) + camera[i].z += FixedMul(20*FRACUNIT, mo->scale); + } + } + } + } } // @@ -3579,6 +3599,8 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled if (encoremode) postimg = postimg_mirror; + else if (player->pflags & PF_FLIPCAM && !(player->pflags & PF_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP) + postimg = postimg_flip; else if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist { camera_t dummycam; @@ -7037,6 +7059,8 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target->eflags & MFE_VERTICALFLIP) { mobj->z = mobj->target->z - FixedMul(16*FRACUNIT, mobj->target->scale) - mobj->height; + if (mobj->target->player->pflags & PF_FLIPCAM) + mobj->eflags |= MFE_VERTICALFLIP; } else mobj->z = mobj->target->z + (mobj->target->height) + FixedMul(8*FRACUNIT, mobj->target->scale); // Adjust height for height changes diff --git a/src/r_main.c b/src/r_main.c index 15278e66f..5f7b0cadd 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -151,6 +151,10 @@ static void ChaseCam_OnChange(void); static void ChaseCam2_OnChange(void); static void ChaseCam3_OnChange(void); static void ChaseCam4_OnChange(void); +static void FlipCam_OnChange(void); +static void FlipCam2_OnChange(void); +static void FlipCam3_OnChange(void); +static void FlipCam4_OnChange(void); void SendWeaponPref(void); void SendWeaponPref2(void); void SendWeaponPref3(void); @@ -161,6 +165,10 @@ consvar_t cv_chasecam = {"chasecam", "On", CV_CALL, CV_OnOff, ChaseCam_OnChange, consvar_t cv_chasecam2 = {"chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_chasecam3 = {"chasecam3", "On", CV_CALL, CV_OnOff, ChaseCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_chasecam4 = {"chasecam4", "On", CV_CALL, CV_OnOff, ChaseCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_flipcam = {"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_flipcam3 = {"flipcam3", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_flipcam4 = {"flipcam4", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_shadow = {"shadow", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -274,6 +282,27 @@ static void ChaseCam4_OnChange(void) else CV_SetValue(&cv_analog4, 1);*/ } + +static void FlipCam_OnChange(void) +{ + SendWeaponPref(); +} + +static void FlipCam2_OnChange(void) +{ + SendWeaponPref2(); +} + +static void FlipCam3_OnChange(void) +{ + SendWeaponPref3(); +} + +static void FlipCam4_OnChange(void) +{ + SendWeaponPref4(); +} + // // R_PointOnSide // Traverse BSP (sub) tree, @@ -1459,6 +1488,10 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_soniccd); CV_RegisterVar(&cv_allowmlook); CV_RegisterVar(&cv_homremoval); + CV_RegisterVar(&cv_flipcam); + CV_RegisterVar(&cv_flipcam2); + CV_RegisterVar(&cv_flipcam3); + CV_RegisterVar(&cv_flipcam4); // Enough for dedicated server if (dedicated) diff --git a/src/r_main.h b/src/r_main.h index ea6f6aa87..879d4c6eb 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -75,6 +75,7 @@ boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixe extern consvar_t cv_showhud, cv_translucenthud; extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2, cv_chasecam3, cv_chasecam4; +extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4; extern consvar_t cv_shadow; extern consvar_t cv_translucency; extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip; From b62ef5a51df7f5be67c61a464a3050a56545be2c Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Sun, 26 Jul 2020 21:15:32 +0200 Subject: [PATCH 178/211] Kill flipcam again but nothing else associated with it. --- src/d_netcmd.c | 14 +------------- src/g_game.c | 4 ++-- src/p_mobj.c | 24 ------------------------ src/r_main.c | 33 --------------------------------- src/r_main.h | 1 - 5 files changed, 3 insertions(+), 73 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b55441b2d..79a7e91d9 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2105,8 +2105,6 @@ void SendWeaponPref(void) XBOXSTATIC UINT8 buf[1]; buf[0] = 0; - if (cv_flipcam.value) - buf[0] |= 1; SendNetXCmd(XD_WEAPONPREF, buf, 1); } @@ -2115,8 +2113,6 @@ void SendWeaponPref2(void) XBOXSTATIC UINT8 buf[1]; buf[0] = 0; - if (cv_flipcam2.value) - buf[0] |= 1; SendNetXCmd2(XD_WEAPONPREF, buf, 1); } @@ -2125,8 +2121,6 @@ void SendWeaponPref3(void) XBOXSTATIC UINT8 buf[1]; buf[0] = 0; - if (cv_flipcam3.value) - buf[0] |= 1; SendNetXCmd3(XD_WEAPONPREF, buf, 1); } @@ -2135,18 +2129,12 @@ void SendWeaponPref4(void) XBOXSTATIC UINT8 buf[1]; buf[0] = 0; - if (cv_flipcam4.value) - buf[0] |= 1; SendNetXCmd4(XD_WEAPONPREF, buf, 1); } static void Got_WeaponPref(UINT8 **cp,INT32 playernum) { - UINT8 prefs = READUINT8(*cp); - - players[playernum].pflags &= ~(PF_FLIPCAM); - if (prefs & 1) - players[playernum].pflags |= PF_FLIPCAM; + /*UINT8 prefs = */READUINT8(*cp); // Read it still to avoid instant desyncs in netgames. } static void Got_PowerLevel(UINT8 **cp,INT32 playernum) diff --git a/src/g_game.c b/src/g_game.c index 1b994ede1..0bb7f89fe 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1508,7 +1508,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) INT32 player_invert = invertmouse ? -1 : 1; INT32 screen_invert = (player->mo && (player->mo->eflags & MFE_VERTICALFLIP) - && (!thiscam->chase || player->pflags & PF_FLIPCAM)) //because chasecam's not inverted + && (!thiscam->chase)) //because chasecam's not inverted ? -1 : 1; // set to -1 or 1 to multiply // mouse look stuff (mouse look is not the same as mouse aim) @@ -2619,7 +2619,7 @@ void G_PlayerReborn(INT32 player) jointime = players[player].jointime; splitscreenindex = players[player].splitscreenindex; spectator = players[player].spectator; - pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); + pflags = (players[player].pflags & (PF_TIMEOVER|PF_TAGIT|PF_TAGGED|PF_WANTSTOJOIN)); // As long as we're not in multiplayer, carry over cheatcodes from map to map if (!(netgame || multiplayer)) diff --git a/src/p_mobj.c b/src/p_mobj.c index 2547f3212..027655f47 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1168,26 +1168,6 @@ static void P_PlayerFlip(mobj_t *mo) if (mo->tracer) mo->tracer->eflags ^= MFE_VERTICALFLIP; } - else if (mo->player->pflags & PF_FLIPCAM) - { - UINT8 i; - - mo->player->aiming = InvAngle(mo->player->aiming); - - for (i = 0; i <= r_splitscreen; i++) - { - if (mo->player-players == displayplayers[i]) - { - localaiming[i] = mo->player->aiming; - if (camera[i].chase) { - camera[i].aiming = InvAngle(camera[i].aiming); - camera[i].z = mo->z - camera[i].z + mo->z; - if (mo->eflags & MFE_VERTICALFLIP) - camera[i].z += FixedMul(20*FRACUNIT, mo->scale); - } - } - } - } } // @@ -3599,8 +3579,6 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled if (encoremode) postimg = postimg_mirror; - else if (player->pflags & PF_FLIPCAM && !(player->pflags & PF_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP) - postimg = postimg_flip; else if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist { camera_t dummycam; @@ -7059,8 +7037,6 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target->eflags & MFE_VERTICALFLIP) { mobj->z = mobj->target->z - FixedMul(16*FRACUNIT, mobj->target->scale) - mobj->height; - if (mobj->target->player->pflags & PF_FLIPCAM) - mobj->eflags |= MFE_VERTICALFLIP; } else mobj->z = mobj->target->z + (mobj->target->height) + FixedMul(8*FRACUNIT, mobj->target->scale); // Adjust height for height changes diff --git a/src/r_main.c b/src/r_main.c index 5f7b0cadd..99afac81e 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -151,10 +151,6 @@ static void ChaseCam_OnChange(void); static void ChaseCam2_OnChange(void); static void ChaseCam3_OnChange(void); static void ChaseCam4_OnChange(void); -static void FlipCam_OnChange(void); -static void FlipCam2_OnChange(void); -static void FlipCam3_OnChange(void); -static void FlipCam4_OnChange(void); void SendWeaponPref(void); void SendWeaponPref2(void); void SendWeaponPref3(void); @@ -165,10 +161,6 @@ consvar_t cv_chasecam = {"chasecam", "On", CV_CALL, CV_OnOff, ChaseCam_OnChange, consvar_t cv_chasecam2 = {"chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_chasecam3 = {"chasecam3", "On", CV_CALL, CV_OnOff, ChaseCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_chasecam4 = {"chasecam4", "On", CV_CALL, CV_OnOff, ChaseCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam = {"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam3 = {"flipcam3", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam3_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_flipcam4 = {"flipcam4", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam4_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_shadow = {"shadow", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -283,26 +275,6 @@ static void ChaseCam4_OnChange(void) CV_SetValue(&cv_analog4, 1);*/ } -static void FlipCam_OnChange(void) -{ - SendWeaponPref(); -} - -static void FlipCam2_OnChange(void) -{ - SendWeaponPref2(); -} - -static void FlipCam3_OnChange(void) -{ - SendWeaponPref3(); -} - -static void FlipCam4_OnChange(void) -{ - SendWeaponPref4(); -} - // // R_PointOnSide // Traverse BSP (sub) tree, @@ -1488,11 +1460,6 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_soniccd); CV_RegisterVar(&cv_allowmlook); CV_RegisterVar(&cv_homremoval); - CV_RegisterVar(&cv_flipcam); - CV_RegisterVar(&cv_flipcam2); - CV_RegisterVar(&cv_flipcam3); - CV_RegisterVar(&cv_flipcam4); - // Enough for dedicated server if (dedicated) return; diff --git a/src/r_main.h b/src/r_main.h index 879d4c6eb..ea6f6aa87 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -75,7 +75,6 @@ boolean R_DoCulling(line_t *cullheight, line_t *viewcullheight, fixed_t vz, fixe extern consvar_t cv_showhud, cv_translucenthud; extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2, cv_chasecam3, cv_chasecam4; -extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4; extern consvar_t cv_shadow; extern consvar_t cv_translucency; extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip; From f3a607bf4f259221e67f98cb8c6af31a63957823 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 26 Jul 2020 21:35:13 -0400 Subject: [PATCH 179/211] Proper snapping for splitscreen! --- src/hu_stuff.c | 8 +++++- src/k_hud.c | 72 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index cf217ed1a..19a049b12 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2366,11 +2366,17 @@ void HU_drawMiniPing (INT32 x, INT32 y, UINT32 ping, INT32 flags) { patch_t *patch; + INT32 w = BASEVIDWIDTH; + + if (r_splitscreen > 1) + { + w /= 2; + } patch = mping[Ping_gfx_num(ping)]; if (( flags & V_SNAPTORIGHT )) - x += ( BASEVIDWIDTH - SHORT (patch->width) ); + x += ( w - SHORT (patch->width) ); V_DrawScaledPatch(x, y, flags, patch); } diff --git a/src/k_hud.c b/src/k_hud.c index 189f911d2..d11e84c72 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -595,16 +595,27 @@ INT32 POSI2_X, POSI2_Y; void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 dupy) { // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx - INT32 screenwidth = BASEVIDWIDTH * dupx; - INT32 screenheight = BASEVIDHEIGHT * dupy; + INT32 screenwidth = vid.width; + INT32 screenheight = vid.height; + INT32 basewidth = BASEVIDWIDTH * dupx; + INT32 baseheight = BASEVIDHEIGHT * dupy; SINT8 player = -1; UINT8 i; - if (r_splitscreen > 0) - screenheight /= 2; + if (options & V_SPLITSCREEN) + { + if (r_splitscreen > 0) + { + screenheight /= 2; + baseheight /= 2; + } - if (r_splitscreen > 1) - screenwidth /= 2; + if (r_splitscreen > 1) + { + screenwidth /= 2; + basewidth /= 2; + } + } for (i = 0; i <= r_splitscreen; i++) { @@ -618,17 +629,17 @@ void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du if (vid.width != (BASEVIDWIDTH * dupx)) { if (options & V_SNAPTORIGHT) - *x += (vid.width - screenwidth); + *x += (screenwidth - basewidth); else if (!(options & V_SNAPTOLEFT)) - *x += (vid.width - screenwidth) / 2; + *x += (screenwidth - basewidth) / 2; } if (vid.height != (BASEVIDHEIGHT * dupy)) { if (options & V_SNAPTOBOTTOM) - *y += (vid.height - screenheight); + *y += (screenheight - baseheight); else if (!(options & V_SNAPTOTOP)) - *y += (vid.height - screenheight) / 2; + *y += (screenheight - baseheight) / 2; } if (options & V_SPLITSCREEN) @@ -636,15 +647,15 @@ void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du if (r_splitscreen == 1) { if (player == 1) - *y += vid.height / 2; + *y += screenheight; } else if (r_splitscreen > 1) { if (player == 1 || player == 3) - *x += vid.width / 2; + *x += screenwidth; if (player == 2 || player == 3) - *y += vid.height / 2; + *y += screenheight; } } @@ -654,7 +665,7 @@ void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du if (leveltime < introtime + length) { - INT32 offset = vid.width - (((leveltime - introtime) * vid.width) / length); + INT32 offset = screenwidth - (((leveltime - introtime) * screenwidth) / length); boolean slidefromright = false; if (r_splitscreen > 1) @@ -3369,28 +3380,31 @@ void K_drawKartFreePlay(UINT32 flashtime) static void Draw_party_ping (int ss, INT32 snap) { - HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|V_SLIDEIN|V_SPLITSCREEN|snap); + HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_HUDTRANS|V_SPLITSCREEN|V_SNAPTOTOP|snap); } static void K_drawMiniPing (void) { - if (cv_showping.value) + UINT32 f = V_SNAPTORIGHT; + UINT8 i; + + if (!cv_showping.value) { - switch (r_splitscreen) + return; + } + + for (i = 0; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) { - case 3: - Draw_party_ping(3, V_SNAPTORIGHT); - /*FALLTHRU*/ - case 2: - Draw_party_ping(2, 0); - Draw_party_ping(1, V_SNAPTORIGHT); - Draw_party_ping(0, 0); - break; - case 1: - Draw_party_ping(1, V_SNAPTORIGHT); - Draw_party_ping(0, V_SNAPTORIGHT); - break; + if (r_splitscreen > 1 && !(i & 1)) + { + f = V_SNAPTOLEFT; + } + + Draw_party_ping(i, f); + break; } } } From b4ae2ea896eefd810b2b929cb94af1ca2fbf2f9d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Jul 2020 01:46:08 -0400 Subject: [PATCH 180/211] Stronger top speed for the startup boost --- src/k_kart.c | 2 +- src/p_spec.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index d6f678527..50e94d298 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2131,7 +2131,7 @@ static void K_GetKartBoostPower(player_t *player) if (player->kartstuff[k_startboost]) // Startup Boost { - ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration + ADDBOOST(FRACUNIT/3, 4*FRACUNIT); // + 33% top speed, + 400% acceleration } if (player->kartstuff[k_driftboost]) // Drift Boost diff --git a/src/p_spec.c b/src/p_spec.c index ea96ab268..315e04aa5 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2197,7 +2197,7 @@ static void K_HandleLapIncrement(player_t *player) else if (rainbowstartavailable == true) { S_StartSound(player->mo, sfx_s23c); - player->kartstuff[k_driftboost] = 125; + player->kartstuff[k_startboost] = 125; K_SpawnDriftBoostExplosion(player, 3); rainbowstartavailable = false; } From 1b85f5b5fc2d6eba1b75e2128c5f65e96d747c3f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Jul 2020 01:50:27 -0400 Subject: [PATCH 181/211] Don't Ebrake when you've just been bumped --- src/k_kart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.c b/src/k_kart.c index 50e94d298..6394b4ee7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6896,6 +6896,7 @@ boolean K_PlayerEBrake(player_t *player) && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_boostcharge] && !(player->kartstuff[k_spindash] < 0) + && !player->kartstuff[k_justbumped] && !player->kartstuff[k_spindashboost] && !player->powers[pw_nocontrol]; } From c685f45563db31747bf682feadfd75641afb6efa Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Jul 2020 02:11:18 -0400 Subject: [PATCH 182/211] Make it even stronger --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 6394b4ee7..4f33c1a35 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2131,7 +2131,7 @@ static void K_GetKartBoostPower(player_t *player) if (player->kartstuff[k_startboost]) // Startup Boost { - ADDBOOST(FRACUNIT/3, 4*FRACUNIT); // + 33% top speed, + 400% acceleration + ADDBOOST(FRACUNIT/2, 4*FRACUNIT); // + 50% top speed, + 400% acceleration } if (player->kartstuff[k_driftboost]) // Drift Boost From dfe4d4a2194d2373d4c0de662ded05540b1e2240 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Jul 2020 02:29:43 -0400 Subject: [PATCH 183/211] No tethering off of the person who got the start boost --- src/k_kart.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 4f33c1a35..7efcc697f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1439,6 +1439,10 @@ static void K_UpdateDraft(player_t *player) if (players[i].speed < 20*players[i].mo->scale) continue; + // No tethering off of the guy who got the starting bonus :P + if (players[i].kartstuff[k_startboost] > 0) + 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); From b08459cbeeb91b22533d38de4773ffe40f82720b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Mon, 27 Jul 2020 23:31:46 -0400 Subject: [PATCH 184/211] Buffer time before countdown depends on player count, no intro spin in 1v1 --- src/g_game.c | 24 ++++++++---------------- src/p_saveg.c | 6 ++++++ src/p_setup.c | 11 +++++++++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 64e69276c..dbc0b66db 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -224,8 +224,8 @@ UINT16 spacetimetics = 11*TICRATE + (TICRATE/2); UINT16 extralifetics = 4*TICRATE; // SRB2kart -tic_t introtime = (108) + 5; // 108 for rotation, + 5 for white fade -tic_t starttime = (6*TICRATE) + (2*TICRATE); // Start countdown time, + buffer time +tic_t introtime = 0; +tic_t starttime = 0; tic_t raceexittime = 5*TICRATE + (2*TICRATE/3); tic_t battleexittime = 8*TICRATE; INT32 hyudorotime = 7*TICRATE; @@ -2333,10 +2333,6 @@ void G_Ticker(boolean run) UINT32 i; INT32 buf; ticcmd_t *cmd; - UINT32 ra_timeskip = (modeattacking && !demo.playback && leveltime < introtime) ? 0 : (introtime - 1); - // starttime - TICRATE*4 is where we want RA to start when we PLAY IT, so we will loop the main thinker on RA start to get it to this point, - // the reason this is done is to ensure that ghosts won't look out of synch with other map elements (objects, moving platforms...) - // when we REPLAY, don't skip, let the camera spin, do its thing etc~ // also the -1 is to ensure that the thinker runs in the loop below. @@ -2409,16 +2405,12 @@ void G_Ticker(boolean run) switch (gamestate) { case GS_LEVEL: - - for (; ra_timeskip < introtime; ra_timeskip++) // this looks weird but this is done to not break compability with older demos for now. - { - if (demo.title) - F_TitleDemoTicker(); - P_Ticker(run); // tic the game - ST_Ticker(); - AM_Ticker(); - HU_Ticker(); - } + if (demo.title) + F_TitleDemoTicker(); + P_Ticker(run); // tic the game + ST_Ticker(); + AM_Ticker(); + HU_Ticker(); break; case GS_INTERMISSION: diff --git a/src/p_saveg.c b/src/p_saveg.c index 58fe82174..c197b5a3c 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3474,6 +3474,9 @@ static void P_NetArchiveMisc(void) WRITESINT8(save_p, spbplace); WRITEUINT8(save_p, rainbowstartavailable); + WRITEUINT32(save_p, introtime); + WRITEUINT32(save_p, starttime); + // Is it paused? if (paused) WRITEUINT8(save_p, 0x2f); @@ -3600,6 +3603,9 @@ static inline boolean P_NetUnArchiveMisc(void) spbplace = READSINT8(save_p); rainbowstartavailable = (boolean)READUINT8(save_p); + introtime = READUINT32(save_p); + starttime = READUINT32(save_p); + // Is it paused? if (READUINT8(save_p) == 0x2f) paused = true; diff --git a/src/p_setup.c b/src/p_setup.c index 72d3867cf..994f7bccb 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2472,6 +2472,17 @@ static void P_LevelInitStuff(void) if (p >= 2) rainbowstartavailable = true; + if (p <= 2) + { + introtime = 0; // No intro in Record Attack / 1v1 + } + else + { + introtime = (108) + 5; // 108 for rotation, + 5 for white fade + } + + starttime = introtime + (6*TICRATE) + (max(p-2, 0) * (TICRATE/2)); // Start countdown time, + buffer time + // SRB2Kart: map load variables if (grandprixinfo.gp == true) { From 69b6ba2f18831ed7cb7729294aaf222dde6e1c89 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Jul 2020 03:34:39 -0400 Subject: [PATCH 185/211] I tore apart the entire code to try and fix braking friction and gave up, so now it's back to v1 My rewrites clean up a LOT of weird/misleading behavior anyway, so I'm gonna push this regardless. Someone else will have to figure out another way to reimplement braking friction though -- we can't use it as is because it prevents being able to bump people. --- src/d_clisrv.c | 1 - src/d_ticcmd.h | 1 - src/g_game.c | 58 +++++----------- src/k_bot.c | 22 ++++-- src/k_kart.c | 165 ++++++++++++++++++++++++-------------------- src/k_kart.h | 5 +- src/lua_playerlib.c | 4 -- src/p_mobj.c | 18 ++--- src/p_user.c | 71 +++++-------------- 9 files changed, 145 insertions(+), 200 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a7c6e0e9a..89b96a0d7 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -4315,7 +4315,6 @@ static void HandlePacketFromAwayNode(SINT8 node) static boolean CheckForSpeedHacks(UINT8 p) { if (netcmds[maketic%TICQUEUE][p].forwardmove > MAXPLMOVE || netcmds[maketic%TICQUEUE][p].forwardmove < -MAXPLMOVE - || netcmds[maketic%TICQUEUE][p].sidemove > MAXPLMOVE || netcmds[maketic%TICQUEUE][p].sidemove < -MAXPLMOVE || netcmds[maketic%TICQUEUE][p].driftturn > KART_FULLTURN || netcmds[maketic%TICQUEUE][p].driftturn < -KART_FULLTURN) { XBOXSTATIC char buf[2]; diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h index 0340b9f40..6c14a0f4b 100644 --- a/src/d_ticcmd.h +++ b/src/d_ticcmd.h @@ -60,7 +60,6 @@ typedef enum typedef struct { SINT8 forwardmove; // -MAXPLMOVE to MAXPLMOVE (50) - SINT8 sidemove; // -MAXPLMOVE to MAXPLMOVE (50) INT16 angleturn; // <<16 for angle delta - saved as 1 byte into demos INT16 aiming; // vertical aiming, see G_BuildTicCmd UINT16 buttons; diff --git a/src/g_game.c b/src/g_game.c index dbc0b66db..eeb1e01fb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1256,13 +1256,12 @@ INT32 JoyAxis(axis_input_e axissel, UINT8 p) INT32 localaiming[MAXSPLITSCREENPLAYERS]; angle_t localangle[MAXSPLITSCREENPLAYERS]; -static fixed_t forwardmove = 50<>16; -static fixed_t sidemove[2] = {2<>16, 4<>16}; +static fixed_t forwardmove = MAXPLMOVE<>16; static fixed_t angleturn[3] = {KART_FULLTURN/2, KART_FULLTURN, KART_FULLTURN/4}; // + slow turn void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { - INT32 laim, th, tspeed, forward, side, axis; //i + INT32 laim, th, tspeed, forward, axis; //i const INT32 speed = 1; // these ones used for multiple conditions boolean turnleft, turnright, mouseaiming, analogjoystickmove, gamepadjoystickmove; @@ -1373,7 +1372,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) turnright = turnright || (axis > 0); turnleft = turnleft || (axis < 0); } - forward = side = 0; + forward = 0; // use two stage accelerative turning // on the keyboard and joystick @@ -1394,13 +1393,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed])); cmd->driftturn = (INT16)(cmd->driftturn - (angleturn[tspeed])); - side += sidemove[1]; } else if (turnleft && !(turnright)) { cmd->angleturn = (INT16)(cmd->angleturn + (angleturn[tspeed])); cmd->driftturn = (INT16)(cmd->driftturn + (angleturn[tspeed])); - side -= sidemove[1]; } if (analogjoystickmove && axis != 0) @@ -1408,7 +1405,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // JOYAXISRANGE should be 1023 (divide by 1024) cmd->angleturn = (INT16)(cmd->angleturn - (((axis * angleturn[1]) >> 10))); // ANALOG! cmd->driftturn = (INT16)(cmd->driftturn - (((axis * angleturn[1]) >> 10))); - side += ((axis * sidemove[0]) >> 10); } // Specator mouse turning @@ -1445,7 +1441,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { cmd->buttons |= BT_ACCELERATE; // JOYAXISRANGE is supposed to be 1023 (divide by 1024) - forward += ((axis * forwardmove) >> 10)*2; + forward += ((axis * forwardmove) >> 10); } axis = JoyAxis(AXISBRAKE, ssplayer); @@ -1555,21 +1551,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) mousex = mousey = mlooky = 0; - if (forward > MAXPLMOVE) - forward = MAXPLMOVE; - else if (forward < -MAXPLMOVE) - forward = -MAXPLMOVE; + cmd->forwardmove += (SINT8)forward; - if (side > MAXPLMOVE) - side = MAXPLMOVE; - else if (side < -MAXPLMOVE) - side = -MAXPLMOVE; - - if (forward || side) - { - cmd->forwardmove = (SINT8)(cmd->forwardmove + forward); - cmd->sidemove = (SINT8)(cmd->sidemove + side); - } + if (cmd->forwardmove > MAXPLMOVE) + cmd->forwardmove = MAXPLMOVE; + else if (cmd->forwardmove < -MAXPLMOVE) + cmd->forwardmove = -MAXPLMOVE; //{ SRB2kart - Drift support // Not grouped with the rest of turn stuff because it needs to know what buttons you're pressing for rubber-burn turn @@ -1619,7 +1606,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) #endif //Reset away view if a command is given. - if ((cmd->forwardmove || cmd->sidemove || cmd->buttons) + if ((cmd->forwardmove || cmd->buttons) && !r_splitscreen && displayplayers[0] != consoleplayer && ssplayer == 1) displayplayers[0] = consoleplayer; } @@ -4771,13 +4758,12 @@ char *G_BuildMapTitle(INT32 mapnum) // For demos #define ZT_FWD 0x01 -#define ZT_SIDE 0x02 -#define ZT_ANGLE 0x04 -#define ZT_BUTTONS 0x08 -#define ZT_AIMING 0x10 -#define ZT_DRIFT 0x20 -#define ZT_LATENCY 0x40 -#define DEMOMARKER 0x80 // demoend +#define ZT_ANGLE 0x02 +#define ZT_BUTTONS 0x04 +#define ZT_AIMING 0x08 +#define ZT_DRIFT 0x10 +#define ZT_LATENCY 0x20 +#define DEMOMARKER 0x40 // demoend UINT8 demo_extradata[MAXPLAYERS]; UINT8 demo_writerng; // 0=no, 1=yes, 2=yes but on a timeout @@ -4840,7 +4826,6 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) for (i = 0; i < n; i++) { dest[i].forwardmove = src[i].forwardmove; - dest[i].sidemove = src[i].sidemove; dest[i].angleturn = SHORT(src[i].angleturn); dest[i].aiming = (INT16)SHORT(src[i].aiming); dest[i].buttons = (UINT16)SHORT(src[i].buttons); @@ -5149,8 +5134,6 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) if (ziptic & ZT_FWD) oldcmd[playernum].forwardmove = READSINT8(demo_p); - if (ziptic & ZT_SIDE) - oldcmd[playernum].sidemove = READSINT8(demo_p); if (ziptic & ZT_ANGLE) oldcmd[playernum].angleturn = READINT16(demo_p); if (ziptic & ZT_BUTTONS) @@ -5190,13 +5173,6 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) ziptic |= ZT_FWD; } - if (cmd->sidemove != oldcmd[playernum].sidemove) - { - WRITEUINT8(demo_p,cmd->sidemove); - oldcmd[playernum].sidemove = cmd->sidemove; - ziptic |= ZT_SIDE; - } - if (cmd->angleturn != oldcmd[playernum].angleturn) { WRITEINT16(demo_p,cmd->angleturn); @@ -5710,8 +5686,6 @@ void G_GhostTicker(void) if (ziptic & ZT_FWD) g->p++; - if (ziptic & ZT_SIDE) - g->p++; if (ziptic & ZT_ANGLE) g->p += 2; if (ziptic & ZT_BUTTONS) diff --git a/src/k_bot.c b/src/k_bot.c index 285f92142..1cfef7fb9 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -474,6 +474,7 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) { fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; + fixed_t newfrict; if (rubberband <= 0) { @@ -481,8 +482,14 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) return frict; } - // 128 is a magic number that felt good in-game - return FixedDiv(frict, FRACUNIT + (rubberband / 2)); + newfrict = FixedDiv(frict, FRACUNIT + (rubberband / 2)); + + if (newfrict < 0) + newfrict = 0; + if (newfrict > FRACUNIT) + newfrict = FRACUNIT; + + return newfrict; } /*-------------------------------------------------- @@ -698,11 +705,12 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Start boost handler if (leveltime <= starttime) { - tic_t boosthold = starttime - TICRATE; + tic_t length = (TICRATE/6); + tic_t boosthold = starttime - K_GetSpindashChargeTime(player); - cmd->buttons |= BT_ACCELERATE|BT_BRAKE; + cmd->buttons |= BT_EBRAKEMASK; - boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty); + boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty) * length; if (leveltime >= boosthold) { @@ -743,7 +751,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (anglediff > 90) { // Wrong way! - cmd->forwardmove = -25; + cmd->forwardmove = -MAXPLMOVE; cmd->buttons |= BT_BRAKE; } else @@ -777,7 +785,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) cmd->buttons |= BT_ACCELERATE; // Full speed ahead! - cmd->forwardmove = 50; + cmd->forwardmove = MAXPLMOVE; if (dirdist <= rad) { diff --git a/src/k_kart.c b/src/k_kart.c index 7efcc697f..90afd4451 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1143,10 +1143,13 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) { // 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); - fixed_t nx = FixedDiv(distx, dist); - fixed_t ny = FixedDiv(disty, dist); + fixed_t nx, ny; dist = dist ? dist : 1; + + nx = FixedDiv(distx, dist); + ny = FixedDiv(disty, dist); + distx = FixedMul(mobj1->radius+mobj2->radius, nx); disty = FixedMul(mobj1->radius+mobj2->radius, ny); @@ -1222,6 +1225,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx; mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy; mobj1->player->kartstuff[k_justbumped] = bumptime; + mobj1->player->kartstuff[k_spindash] = 0; if (mobj1->player->kartstuff[k_spinouttimer]) { @@ -1247,6 +1251,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx; mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy; mobj2->player->kartstuff[k_justbumped] = bumptime; + mobj2->player->kartstuff[k_spindash] = 0; if (mobj2->player->kartstuff[k_spinouttimer]) { @@ -2042,14 +2047,14 @@ static fixed_t K_FlameShieldDashVar(INT32 val) return (3*FRACUNIT/4) + (((val * FRACUNIT) / TICRATE) / 2); } -static tic_t K_GetSpindashChargeTime(player_t *player) +tic_t 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); } -static fixed_t K_GetSpindashChargeSpeed(player_t *player) +fixed_t K_GetSpindashChargeSpeed(player_t *player) { // more speed for higher weight & speed // Tails = +6.25%, Fang = +20.31%, Mighty = +20.31%, Metal = +25% @@ -2266,13 +2271,32 @@ UINT16 K_GetKartFlashing(player_t *player) return tics; } -fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove) +SINT8 K_GetForwardMove(player_t *player) +{ + SINT8 forwardmove = player->cmd.forwardmove; + + if ((player->pflags & PF_STASIS) || (player->pflags & PF_SLIDING) || player->kartstuff[k_spinouttimer] || K_PlayerEBrake(player)) + { + return 0; + } + + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_spindashboost]) + { + return MAXPLMOVE; + } + + return forwardmove; +} + +fixed_t K_3dKartMovement(player_t *player, boolean onground) { const fixed_t accelmax = 4000; const fixed_t p_speed = K_GetKartSpeed(player, true); const fixed_t p_accel = K_GetKartAccel(player); fixed_t newspeed, oldspeed, finalspeed; + fixed_t movemul = FRACUNIT; fixed_t orig = ORIG_FRICTION; + SINT8 forwardmove = K_GetForwardMove(player); if (!onground) return 0; // If the player isn't on the ground, there is no change in speed @@ -2301,25 +2325,20 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove } finalspeed = newspeed - oldspeed; + movemul = abs(forwardmove * FRACUNIT) / 50; // forwardmove is: // 50 while accelerating, // 0 with no gas, and // -25 when only braking. - - if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_spindashboost]) - forwardmove = 50; - - finalspeed *= forwardmove/25; - finalspeed /= 2; - - if (forwardmove < 0 && finalspeed > mapobjectscale*2) - return finalspeed/2; - else if (forwardmove < 0) - return -mapobjectscale/2; - - if (finalspeed < 0) - finalspeed = 0; + if (forwardmove >= 0) + { + finalspeed = FixedMul(finalspeed, movemul); + } + else + { + finalspeed = FixedMul(-mapobjectscale/2, movemul); + } return finalspeed; } @@ -3579,7 +3598,7 @@ void K_DriftDustHandling(mobj_t *spawner) if (spawner->player->speed < 5*spawner->scale) return; - if (spawner->player->cmd.forwardmove < 0) + if (K_GetForwardMove(spawner->player) < 0) playerangle += ANGLE_180; anglediff = abs((signed)(playerangle - R_PointToAngle2(0, 0, spawner->player->rmomx, spawner->player->rmomy))); @@ -5161,10 +5180,10 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) if (player->respawn.state == RESPAWNST_DROP) // Dropdashing targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0); - else if (K_PlayerEBrake(player)) // Spindashing + else if (K_PlayerEBrake(player) == true) // Spindashing targetsnd = ((cmd->buttons & BT_DRIFT) ? 12 : 0); else - targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2; + targetsnd = (((6*K_GetForwardMove(player))/25) + ((player->speed / mapobjectscale)/5))/2; if (targetsnd < 0) targetsnd = 0; @@ -5809,7 +5828,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } - if (player->kartstuff[k_justbumped]) + if (player->kartstuff[k_justbumped] > 0) player->kartstuff[k_justbumped]--; if (player->kartstuff[k_tiregrease]) @@ -6895,31 +6914,52 @@ static INT32 K_FlameShieldMax(player_t *player) boolean K_PlayerEBrake(player_t *player) { return (player->cmd.buttons & BT_EBRAKEMASK) == BT_EBRAKEMASK - && P_IsObjectOnGround(player->mo) - && !player->kartstuff[k_drift] - && !player->kartstuff[k_spinouttimer] - && !player->kartstuff[k_boostcharge] - && !(player->kartstuff[k_spindash] < 0) - && !player->kartstuff[k_justbumped] - && !player->kartstuff[k_spindashboost] - && !player->powers[pw_nocontrol]; + && P_IsObjectOnGround(player->mo) == true + && player->kartstuff[k_drift] == 0 + && player->kartstuff[k_spinouttimer] == 0 + && player->kartstuff[k_justbumped] == 0 + && player->kartstuff[k_spindash] >= 0 + && player->kartstuff[k_spindashboost] == 0 + && player->powers[pw_nocontrol] == 0; } static void K_KartSpindash(player_t *player) { + const tic_t MAXCHARGETIME = K_GetSpindashChargeTime(player); ticcmd_t *cmd = &player->cmd; - if (!(K_PlayerEBrake(player) || (player->kartstuff[k_spindash] && !(cmd->buttons & BT_BRAKE)))) + if (player->kartstuff[k_spindash] > 0 && (cmd->buttons & (BT_DRIFT|BT_BRAKE)) != (BT_DRIFT|BT_BRAKE)) { - if (player->kartstuff[k_spindash]) - player->kartstuff[k_spindash] = 0; + player->kartstuff[k_spindashspeed] = (player->kartstuff[k_spindash] * FRACUNIT) / MAXCHARGETIME; + player->kartstuff[k_spindashboost] = TICRATE; + + if (!player->kartstuff[k_tiregrease]) + { + UINT8 i; + for (i = 0; i < 2; i++) + { + 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->extravalue1 = i; + } + } + + player->kartstuff[k_tiregrease] = 2*TICRATE; + + player->kartstuff[k_spindash] = 0; + S_StartSound(player->mo, sfx_s23c); + } + + if (K_PlayerEBrake(player) == false) + { + player->kartstuff[k_spindash] = 0; return; } if (player->speed < 6*mapobjectscale && player->powers[pw_flashing] == 0) { - const tic_t MAXCHARGETIME = K_GetSpindashChargeTime(player); - if (cmd->driftturn != 0 && leveltime % 8 == 0) S_StartSound(player->mo, sfx_ruburn); @@ -6948,29 +6988,6 @@ static void K_KartSpindash(player_t *player) } } } - else if (player->kartstuff[k_spindash]) - { - player->kartstuff[k_spindashspeed] = (player->kartstuff[k_spindash] * FRACUNIT) / MAXCHARGETIME; - player->kartstuff[k_spindashboost] = TICRATE; - - if (!player->kartstuff[k_tiregrease]) - { - UINT8 i; - for (i = 0; i < 2; i++) - { - 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->extravalue1 = i; - } - } - - player->kartstuff[k_tiregrease] = 2*TICRATE; - - player->kartstuff[k_spindash] = 0; - S_StartSound(player->mo, sfx_s23c); - } } else { @@ -7670,17 +7687,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->kartstuff[k_tiregrease]) player->mo->friction += ((FRACUNIT - prevfriction) / greasetics) * player->kartstuff[k_tiregrease]; - // Friction - if (!player->kartstuff[k_offroad]) - { - if (player->speed > 0 && cmd->forwardmove == 0 && !(cmd->buttons & BT_BRAKE) && player->mo->friction == 59392) - player->mo->friction += 4608; - } - - if (K_PlayerEBrake(player)) - player->mo->friction -= 3072; - else if (player->speed > 0 && cmd->forwardmove < 0) // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel - player->mo->friction -= 2048; + /* + if (K_PlayerEBrake(player) == true) + player->mo->friction -= 1024; + else if (player->speed > 0 && cmd->forwardmove < 0) + player->mo->friction -= 512; + */ // Karma ice physics if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) @@ -7698,14 +7710,15 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->friction -= 9824; } - // Friction was changed, so we must recalculate a bunch of stuff + // Cap between intended values + if (player->mo->friction > FRACUNIT) + player->mo->friction = FRACUNIT; + if (player->mo->friction < 0) + player->mo->friction = 0; + + // Friction was changed, so we must recalculate movefactor if (player->mo->friction != prevfriction) { - if (player->mo->friction > FRACUNIT) - player->mo->friction = FRACUNIT; - if (player->mo->friction < 0) - player->mo->friction = 0; - player->mo->movefactor = FixedDiv(ORIG_FRICTION, player->mo->friction); if (player->mo->movefactor < FRACUNIT) diff --git a/src/k_kart.h b/src/k_kart.h index c06f2bef3..d44be14f4 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -70,11 +70,14 @@ 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); +fixed_t K_GetSpindashChargeSpeed(player_t *player); fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed); fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower); fixed_t K_GetKartAccel(player_t *player); UINT16 K_GetKartFlashing(player_t *player); -fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove); +SINT8 K_GetForwardMove(player_t *player); +fixed_t K_3dKartMovement(player_t *player, boolean onground); boolean K_PlayerEBrake(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(void); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1e93e5687..436a7161b 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -767,8 +767,6 @@ static int ticcmd_get(lua_State *L) if (fastcmp(field,"forwardmove")) lua_pushinteger(L, cmd->forwardmove); - else if (fastcmp(field,"sidemove")) - lua_pushinteger(L, cmd->sidemove); else if (fastcmp(field,"angleturn")) lua_pushinteger(L, cmd->angleturn); else if (fastcmp(field,"aiming")) @@ -797,8 +795,6 @@ static int ticcmd_set(lua_State *L) if (fastcmp(field,"forwardmove")) cmd->forwardmove = (SINT8)luaL_checkinteger(L, 3); - else if (fastcmp(field,"sidemove")) - cmd->sidemove = (SINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"angleturn")) cmd->angleturn = (INT16)luaL_checkinteger(L, 3); else if (fastcmp(field,"aiming")) diff --git a/src/p_mobj.c b/src/p_mobj.c index c5cddef5a..734a6e3f3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1461,8 +1461,8 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) mo->momy = FixedMul(mo->momy, ns); } else if (abs(player->rmomx) < FixedMul(STOPSPEED, mo->scale) - && abs(player->rmomy) < FixedMul(STOPSPEED, mo->scale) - && (!(player->cmd.forwardmove && !(twodlevel || mo->flags2 & MF2_TWOD)) && !player->cmd.sidemove && !(player->pflags & PF_SPINNING)) + && abs(player->rmomy) < FixedMul(STOPSPEED, mo->scale) + && (!(K_GetForwardMove(player) && !(twodlevel || mo->flags2 & MF2_TWOD)) && !(player->pflags & PF_SPINNING)) #ifdef ESLOPE && !(player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)))// && (abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)) #endif @@ -1476,16 +1476,8 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) } else { - if (oldx == mo->x && oldy == mo->y) // didn't go anywhere - { - mo->momx = FixedMul(mo->momx, ORIG_FRICTION); - mo->momy = FixedMul(mo->momy, ORIG_FRICTION); - } - else - { - mo->momx = FixedMul(mo->momx, mo->friction); - mo->momy = FixedMul(mo->momy, mo->friction); - } + mo->momx = FixedMul(mo->momx, mo->friction); + mo->momy = FixedMul(mo->momy, mo->friction); mo->friction = ORIG_FRICTION; } @@ -1961,7 +1953,7 @@ void P_XYMovement(mobj_t *mo) if (mo->type == MT_FLINGRING || mo->type == MT_BALLHOG || mo->type == MT_BUBBLESHIELDTRAP) return; - if (mo->player && (mo->player->kartstuff[k_spinouttimer] && !mo->player->kartstuff[k_wipeoutslow]) && mo->player->speed <= K_GetKartSpeed(mo->player, false)/2) + if (player && (player->kartstuff[k_spinouttimer] && !player->kartstuff[k_wipeoutslow]) && player->speed <= FixedDiv(20*mapobjectscale, player->kartstuff[k_offroad] + FRACUNIT)) return; //} diff --git a/src/p_user.c b/src/p_user.c index 6a91875b1..8d6014926 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4020,9 +4020,9 @@ static void P_2dMovement(player_t *player) static void P_3dMovement(player_t *player) { ticcmd_t *cmd; - angle_t movepushangle, movepushsideangle; // Analog + angle_t movepushangle; // Analog //INT32 topspeed, acceleration, thrustfactor; - fixed_t movepushforward = 0, movepushside = 0; + fixed_t movepushforward = 0; angle_t dangle; // replaces old quadrants bits //boolean dangleflip = false; // SRB2kart - toaster //fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale); @@ -4039,14 +4039,6 @@ static void P_3dMovement(player_t *player) cmd = &player->cmd; - if (player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam? - { - cmd->forwardmove = cmd->sidemove = 0; - } - - if (!(player->pflags & PF_FORCESTRAFE) && !player->kartstuff[k_pogospring]) - cmd->sidemove = 0; - if (player->kartstuff[k_drift] != 0) movepushangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift]; else if (player->kartstuff[k_spinouttimer] || player->kartstuff[k_wipeoutslow]) // if spun out, use the boost angle @@ -4054,8 +4046,6 @@ static void P_3dMovement(player_t *player) else movepushangle = player->mo->angle; - movepushsideangle = movepushangle-ANGLE_90; - // cmomx/cmomy stands for the conveyor belt speed. if (player->onconveyor == 2) // Wind/Current { @@ -4108,10 +4098,6 @@ static void P_3dMovement(player_t *player) */ //} - // When sliding, don't allow forward/back - if (player->pflags & PF_SLIDING) - cmd->forwardmove = 0; - // Do not let the player control movement if not onground. // SRB2Kart: pogo spring and speed bumps are supposed to control like you're on the ground onground = (P_IsObjectOnGround(player->mo) || (player->kartstuff[k_pogospring])); @@ -4121,7 +4107,7 @@ static void P_3dMovement(player_t *player) // Forward movement if (!(P_PlayerInPain(player) && !onground)) { - movepushforward = K_3dKartMovement(player, onground, cmd->forwardmove); + movepushforward = K_3dKartMovement(player, onground); if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... movepushforward = FixedMul(movepushforward, player->mo->movefactor); @@ -4134,18 +4120,6 @@ static void P_3dMovement(player_t *player) K_MomentumToFacing(player); } - // Sideways movement - if (cmd->sidemove != 0 && !((player->exiting || mapreset) || player->kartstuff[k_spinouttimer])) - { - if (cmd->sidemove > 0) - movepushside = (cmd->sidemove * FRACUNIT/128) + FixedDiv(player->speed, K_GetKartSpeed(player, true)); - else - movepushside = (cmd->sidemove * FRACUNIT/128) - FixedDiv(player->speed, K_GetKartSpeed(player, true)); - - totalthrust.x += P_ReturnThrustX(player->mo, movepushsideangle, movepushside); - totalthrust.y += P_ReturnThrustY(player->mo, movepushsideangle, movepushside); - } - if ((totalthrust.x || totalthrust.y) && player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) > FRACUNIT/2) { // Factor thrust to slope, but only for the part pushing up it! @@ -7223,8 +7197,7 @@ fixed_t t_cam4_rotate = -42; // we then throw that ticcmd garbage in the camera and make it move // redefine this -static fixed_t forwardmove[2] = {25<>16, 50<>16}; -static fixed_t sidemove[2] = {2<>16, 4<>16}; +static fixed_t forwardmove = MAXPLMOVE<>16; static fixed_t angleturn[3] = {KART_FULLTURN/2, KART_FULLTURN, KART_FULLTURN/4}; // + slow turn static ticcmd_t cameracmd; @@ -7239,7 +7212,7 @@ void P_InitCameraCmd(void) static ticcmd_t *P_CameraCmd(camera_t *cam) { - INT32 laim, th, tspeed, forward, side, axis; //i + INT32 laim, th, tspeed, forward, axis; //i const INT32 speed = 1; // these ones used for multiple conditions boolean turnleft, turnright, mouseaiming; @@ -7288,7 +7261,7 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) turnright = turnright || (axis > 0); turnleft = turnleft || (axis < 0); } - forward = side = 0; + forward = 0; // use two stage accelerative turning // on the keyboard and joystick @@ -7306,12 +7279,10 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) if (turnright && !(turnleft)) { cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed])); - side += sidemove[1]; } else if (turnleft && !(turnright)) { cmd->angleturn = (INT16)(cmd->angleturn + (angleturn[tspeed])); - side -= sidemove[1]; } cmd->angleturn = (INT16)(cmd->angleturn - ((mousex*(encoremode ? -1 : 1)*8))); @@ -7324,9 +7295,9 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) cmd->buttons |= BT_BRAKE; axis = JoyAxis(AXISAIM, 1); if (InputDown(gc_aimforward, 1) || (usejoystick && axis < 0)) - forward += forwardmove[1]; + forward += forwardmove; if (InputDown(gc_aimbackward, 1) || (usejoystick && axis > 0)) - forward -= forwardmove[1]; + forward -= forwardmove; // fire with any button/key axis = JoyAxis(AXISFIRE, 1); @@ -7367,21 +7338,12 @@ static ticcmd_t *P_CameraCmd(camera_t *cam) mousex = mousey = mlooky = 0; - if (forward > MAXPLMOVE) - forward = MAXPLMOVE; - else if (forward < -MAXPLMOVE) - forward = -MAXPLMOVE; + cmd->forwardmove += (SINT8)forward; - if (side > MAXPLMOVE) - side = MAXPLMOVE; - else if (side < -MAXPLMOVE) - side = -MAXPLMOVE; - - if (forward || side) - { - cmd->forwardmove = (SINT8)(cmd->forwardmove + forward); - cmd->sidemove = (SINT8)(cmd->sidemove + side); - } + if (cmd->forwardmove > MAXPLMOVE) + cmd->forwardmove = MAXPLMOVE; + else if (cmd->forwardmove < -MAXPLMOVE) + cmd->forwardmove = -MAXPLMOVE; lang += (cmd->angleturn<<16); @@ -7426,11 +7388,10 @@ void P_DemoCameraMovement(camera_t *cam) cam->aiming = R_PointToAngle2(0, cam->z, R_PointToDist2(cam->x, cam->y, lastp->mo->x, lastp->mo->y), lastp->mo->z + lastp->mo->scale*128*P_MobjFlip(lastp->mo)); // This is still unholy. Aim a bit above their heads. } - cam->momx = cam->momy = cam->momz = 0; + if (cmd->forwardmove != 0) { - thrustangle = cam->angle >> ANGLETOFINESHIFT; cam->x += FixedMul(cmd->forwardmove*mapobjectscale, FINECOSINE(thrustangle)); @@ -9493,8 +9454,8 @@ void P_PlayerAfterThink(player_t *player) if (!(player->mo->tracer->target->flags & MF_SLIDEME) // Noclimb on chain parameters gives this && !(twodlevel || player->mo->flags2 & MF2_TWOD)) // why on earth would you want to turn them in 2D mode? { - player->mo->tracer->target->health += cmd->sidemove; - player->mo->angle += cmd->sidemove< ANGLE_MAX + //player->mo->tracer->target->health += cmd->sidemove; + //player->mo->angle += cmd->sidemove< ANGLE_MAX if (player == &players[consoleplayer]) localangle[0] = player->mo->angle; // Adjust the local control angle. From 3b6b0013c8d142c80f1af9b8141a48493fbb0e0e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Jul 2020 04:49:15 -0400 Subject: [PATCH 186/211] Add local ABCD tags for splitscreen --- src/k_hud.c | 98 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index d11e84c72..73e4d376d 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -119,6 +119,7 @@ static patch_t *kp_sadface[2]; static patch_t *kp_check[6]; static patch_t *kp_rival[2]; +static patch_t *kp_localtag[4][2]; static patch_t *kp_eggnum[4]; @@ -424,6 +425,18 @@ void K_LoadKartHUDGraphics(void) kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); } + // Rival indicators + sprintf(buffer, "K_SSPLxx"); + for (i = 0; i < 4; i++) + { + buffer[6] = 'A'+i; + for (j = 0; j < 2; j++) + { + buffer[7] = '1'+j; + kp_localtag[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + } + // Eggman warning numbers sprintf(buffer, "K_EGGNx"); for (i = 0; i < 4; i++) @@ -2172,12 +2185,12 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a { if (hud_x != NULL) { - *hud_x = -BASEVIDWIDTH * FRACUNIT; + *hud_x = -1000 * FRACUNIT; } if (hud_y != NULL) { - *hud_y = -BASEVIDWIDTH * FRACUNIT; + *hud_y = -1000 * FRACUNIT; } //*hud_scale = 0; @@ -2194,14 +2207,21 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a { *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; - if (encoremode) + if (*hud_x < -BASEVIDWIDTH * FRACUNIT || *hud_x > BASEVIDWIDTH * FRACUNIT) { - *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; + *hud_x = -1000 * FRACUNIT; } - - if (r_splitscreen >= 2) + else { - *hud_x /= 2; + if (encoremode) + { + *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; + } + + if (r_splitscreen >= 2) + { + *hud_x /= 2; + } } } @@ -2212,9 +2232,16 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a *hud_y = (*hud_y * swhalf) + shhalffixed; *hud_y = *hud_y + NEWTAN(camaim) * swhalf; - if (r_splitscreen >= 1) + if (*hud_y < -BASEVIDHEIGHT * FRACUNIT || *hud_y > BASEVIDHEIGHT * FRACUNIT) { - *hud_y /= 2; + *hud_y = -1000 * FRACUNIT; + } + else + { + if (r_splitscreen >= 1) + { + *hud_y /= 2; + } } } @@ -2349,6 +2376,13 @@ static boolean K_ShowPlayerNametag(player_t *p) return true; } +static void K_DrawLocalTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT8 id) +{ + UINT8 blink = ((leveltime / 7) & 1); + UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_localtag[id][blink], colormap); +} + static void K_DrawRivalTagForPlayer(fixed_t x, fixed_t y) { UINT8 blink = ((leveltime / 7) & 1); @@ -2469,24 +2503,6 @@ static void K_drawKartNameTags(void) continue; } - if (!(demo.playback == true && demo.freecam == true)) - { - for (j = 0; j <= r_splitscreen; j++) - { - if (ntplayer == &players[displayplayers[j]]) - { - break; - } - } - - if (j <= r_splitscreen) - { - // This is a player that's being shown on this computer - // (Remove whenever we get splitscreen ABCD indicators) - continue; - } - } - v.x = ntplayer->mo->x; v.y = ntplayer->mo->y; v.z = ntplayer->mo->z; @@ -2541,6 +2557,7 @@ static void K_drawKartNameTags(void) fixed_t x = -BASEVIDWIDTH * FRACUNIT; fixed_t y = -BASEVIDWIDTH * FRACUNIT; + SINT8 localindicator = -1; vertex_t v; v.x = ntplayer->mo->x; @@ -2554,13 +2571,36 @@ static void K_drawKartNameTags(void) K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, &v); - if (x == -BASEVIDWIDTH * FRACUNIT) + /* + if ((x < 0 || x > BASEVIDWIDTH * FRACUNIT) + || (y < 0 || y > BASEVIDHEIGHT * FRACUNIT)) { // Off-screen continue; } + */ - if (ntplayer->bot) + if (!(demo.playback == true && demo.freecam == true)) + { + for (j = 0; j <= r_splitscreen; j++) + { + if (ntplayer == &players[displayplayers[j]]) + { + break; + } + } + + if (j <= r_splitscreen && j != cnum) + { + localindicator = j; + } + } + + if (localindicator >= 0) + { + K_DrawLocalTagForPlayer(x, y, ntplayer, localindicator); + } + else if (ntplayer->bot) { if (ntplayer->botvars.rival == true) { From e53fe8c316210e0309f98b0fb44e4ed6028c1bc9 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Tue, 28 Jul 2020 04:49:34 -0400 Subject: [PATCH 187/211] Fix player 2's position num in 2P --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index 73e4d376d..1d6b79f3b 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1377,7 +1377,7 @@ static void K_DrawKartPositionNum(INT32 num) } else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap. { - fy = BASEVIDHEIGHT - 8; + fy = (BASEVIDHEIGHT/2) - 8; fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN; } } From c45a67c3f92687ee342516bf180acc41a6201491 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 01:19:53 -0400 Subject: [PATCH 188/211] Pre start bulbs --- src/doomstat.h | 5 ++ src/g_game.c | 5 ++ src/k_kart.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++- src/p_setup.c | 9 ++- 4 files changed, 170 insertions(+), 2 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index 4e102277a..e678886a8 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -482,8 +482,13 @@ extern UINT16 extralifetics; // SRB2kart extern tic_t introtime; extern tic_t starttime; + +extern const tic_t bulbtime; +extern UINT8 numbulbs; + extern tic_t raceexittime; extern tic_t battleexittime; + extern INT32 hyudorotime; extern INT32 stealtime; extern INT32 sneakertime; diff --git a/src/g_game.c b/src/g_game.c index eeb1e01fb..21bd0fa4f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -226,8 +226,13 @@ UINT16 extralifetics = 4*TICRATE; // SRB2kart tic_t introtime = 0; tic_t starttime = 0; + +const tic_t bulbtime = TICRATE/2; +UINT8 numbulbs = 0; + tic_t raceexittime = 5*TICRATE + (2*TICRATE/3); tic_t battleexittime = 8*TICRATE; + INT32 hyudorotime = 7*TICRATE; INT32 stealtime = TICRATE/2; INT32 sneakertime = TICRATE + (TICRATE/3); diff --git a/src/k_kart.c b/src/k_kart.c index 90afd4451..7e649ac43 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7882,6 +7882,9 @@ static patch_t *kp_karmasticker; static patch_t *kp_splitkarmabomb; static patch_t *kp_timeoutsticker; +static patch_t *kp_prestartbulb[15]; +static patch_t *kp_prestartletters[7]; + static patch_t *kp_startcountdown[16]; static patch_t *kp_racefault[6]; static patch_t *kp_racefinish[6]; @@ -7999,6 +8002,24 @@ void K_LoadKartHUDGraphics(void) kp_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX); kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX); + // Pre-start countdown bulbs + sprintf(buffer, "K_BULBxx"); + for (i = 0; i < 15; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_prestartbulb[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + + // Pre-start position letters + kp_prestartletters[0] = W_CachePatchName("K_PL_P", PU_HUDGFX); + kp_prestartletters[1] = W_CachePatchName("K_PL_O", PU_HUDGFX); + kp_prestartletters[2] = W_CachePatchName("K_PL_S", PU_HUDGFX); + kp_prestartletters[3] = W_CachePatchName("K_PL_I", PU_HUDGFX); + kp_prestartletters[4] = W_CachePatchName("K_PL_T", PU_HUDGFX); + kp_prestartletters[5] = W_CachePatchName("K_PL_N", PU_HUDGFX); + kp_prestartletters[6] = W_CachePatchName("K_PL_EX", PU_HUDGFX); + // Starting countdown kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); @@ -10586,6 +10607,130 @@ static void K_drawKartMinimap(void) } } +static void K_drawKartStartBulbs(void) +{ + const UINT8 start_animation[14] = { + 1, 2, 3, 4, 5, 6, 7, 8, + 7, 6, + 9, 10, 11, 12 + }; + + const UINT8 loop_animation[4] = { + 12, 13, 12, 14 + }; + + const UINT8 chillloop_animation[2] = { + 11, 12 + }; + + const UINT8 letters_order[10] = { + 0, 1, 2, 3, 4, 3, 1, 5, 6, 6 + }; + + const UINT8 letters_transparency[40] = { + 0, 2, 4, 6, 8, + 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, + 10, 8, 6, 4, 2 + }; + + fixed_t spacing = 24*FRACUNIT; + + fixed_t startx = (BASEVIDWIDTH/2)*FRACUNIT + (spacing/2); + fixed_t x, y = 48*FRACUNIT; + + UINT8 numperrow = numbulbs/2; + UINT8 i; + + if (numbulbs <= 10) + { + // No second row + numperrow = numbulbs; + } + else + { + if (numbulbs & 1) + { + numperrow++; + } + + y -= (spacing/2); + } + + startx -= (spacing/2) * numperrow; + x = startx; + + for (i = 0; i < numbulbs; i++) + { + UINT8 patchnum = 0; + INT32 bulbtic = (leveltime - introtime - TICRATE) - (bulbtime * i); + + if (i == numperrow) + { + y += spacing; + x = startx + (spacing/2); + } + + if (bulbtic > 0) + { + if (bulbtic < 14) + { + patchnum = start_animation[bulbtic]; + } + else + { + const INT32 length = (bulbtime * 3); + + bulbtic -= 14; + + if (bulbtic > length) + { + bulbtic -= length; + patchnum = chillloop_animation[bulbtic % 2]; + } + else + { + patchnum = loop_animation[bulbtic % 4]; + } + } + } + + V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP, kp_prestartbulb[patchnum], NULL); + x += spacing; + } + + x = 70*FRACUNIT; + y = 48*FRACUNIT; + + for (i = 0; i < 10; i++) + { + UINT8 patchnum = letters_order[i]; + INT32 transflag = letters_transparency[(leveltime - i) % 40]; + + if (transflag >= 10) + ; + else + { + if (transflag != 0) + transflag = transflag << FF_TRANSSHIFT; + + V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP|transflag, kp_prestartletters[patchnum], NULL); + } + + if (i < 9) + { + x += (SHORT(kp_prestartletters[patchnum]->width)/2) * FRACUNIT; + + patchnum = letters_order[i+1]; + x += (SHORT(kp_prestartletters[patchnum]->width)/2) * FRACUNIT; + } + } +} + static void K_drawKartStartCountdown(void) { INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3 @@ -11451,9 +11596,15 @@ void K_drawKartHUD(void) } // Draw the countdowns after everything else. - if (leveltime >= starttime-(3*TICRATE) + if (leveltime >= introtime && leveltime < starttime-(3*TICRATE)) + { + K_drawKartStartBulbs(); + } + else if (leveltime >= starttime-(3*TICRATE) && leveltime < starttime+TICRATE) + { K_drawKartStartCountdown(); + } else if (racecountdown && (!r_splitscreen || !stplyr->exiting)) { char *countstr = va("%d", racecountdown/TICRATE); diff --git a/src/p_setup.c b/src/p_setup.c index 994f7bccb..eb64639c2 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2481,7 +2481,14 @@ static void P_LevelInitStuff(void) introtime = (108) + 5; // 108 for rotation, + 5 for white fade } - starttime = introtime + (6*TICRATE) + (max(p-2, 0) * (TICRATE/2)); // Start countdown time, + buffer time + numbulbs = 5; + + if (p > 2) + { + numbulbs += (p-2); + } + + starttime = (introtime + (3*TICRATE)) + ((2*TICRATE) + (numbulbs * bulbtime)); // Start countdown time, + buffer time // SRB2Kart: map load variables if (grandprixinfo.gp == true) From b39c8562a6e3bd25fed680c303fd90d066bbf7c8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 01:47:00 -0400 Subject: [PATCH 189/211] Fault HUD scrolls, faulting respawns you & holds you until the countdown's over --- src/k_kart.c | 35 ++++++++++++++++++++++++++++------- src/k_respawn.c | 8 ++++---- src/p_spec.c | 32 ++++++++++++++------------------ 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7e649ac43..47bb1ad05 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5293,6 +5293,11 @@ void K_KartPlayerHUDUpdate(player_t *player) if (player->karthud[khud_tauntvoices]) player->karthud[khud_tauntvoices]--; + if (!(player->pflags & PF_SKIDDOWN)) + player->karthud[khud_fault] = 0; + else if (player->karthud[khud_fault] > 0 && player->karthud[khud_fault] < 2*TICRATE) + player->karthud[khud_fault]++; + if (G_RaceGametype()) { // 0 is the fast spin animation, set at 30 tics of ring boost or higher! @@ -10737,6 +10742,8 @@ static void K_drawKartStartCountdown(void) if (stplyr->karthud[khud_fault] != 0) { + INT32 x, xval; + if (r_splitscreen > 1) // 3/4p, stationary FIN { pnum += 2; @@ -10749,7 +10756,25 @@ static void K_drawKartStartCountdown(void) if ((leveltime % (2*5)) / 5) // blink pnum += 1; - V_DrawScaledPatch(STCD_X - (SHORT(kp_racefault[pnum]->width)/2), STCD_Y - (SHORT(kp_racefault[pnum]->height)/2), splitflags, kp_racefault[pnum]); + if (r_splitscreen == 0) + { + x = ((vid.width<width)<karthud[khud_fault])*(xval > x ? xval : x))/TICRATE; + + V_DrawFixedPatch(x + (STCD_X<>1), + (STCD_Y<height)<<(FRACBITS-1)), + FRACUNIT, + splitflags, kp_racefault[pnum], NULL); + } + else + { + V_DrawScaledPatch(STCD_X - (SHORT(kp_racefault[pnum]->width)/2), STCD_Y - (SHORT(kp_racefault[pnum]->height)/2), splitflags, kp_racefault[pnum]); + } + } + else if (leveltime >= introtime && leveltime < starttime-(3*TICRATE)) + { + K_drawKartStartBulbs(); } else { @@ -11596,12 +11621,8 @@ void K_drawKartHUD(void) } // Draw the countdowns after everything else. - if (leveltime >= introtime && leveltime < starttime-(3*TICRATE)) - { - K_drawKartStartBulbs(); - } - else if (leveltime >= starttime-(3*TICRATE) - && leveltime < starttime+TICRATE) + if (leveltime >= introtime + && leveltime < starttime+TICRATE) { K_drawKartStartCountdown(); } diff --git a/src/k_respawn.c b/src/k_respawn.c index 360174fa1..28f4b432c 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -101,13 +101,12 @@ void K_DoIngameRespawn(player_t *player) return; } - if (leveltime < starttime) + if (leveltime < starttime) // FAULT { player->powers[pw_nocontrol] = (starttime - leveltime) + 50; player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse S_StartSound(player->mo, sfx_s3k83); player->karthud[khud_fault] = 1; - player->mo->momx = player->mo->momy = 0; } player->kartstuff[k_ringboost] = 0; @@ -288,7 +287,7 @@ static void K_MovePlayerToRespawnPoint(player_t *player) player->mo->momx = player->mo->momy = player->mo->momz = 0; player->powers[pw_flashing] = 2; - player->powers[pw_nocontrol] = 2; + player->powers[pw_nocontrol] = max(2, player->powers[pw_nocontrol]); if (leveltime % 8 == 0 && !mapreset) { @@ -541,7 +540,8 @@ static void K_MovePlayerToRespawnPoint(player_t *player) --------------------------------------------------*/ static void K_DropDashWait(player_t *player) { - player->respawn.timer--; + if (player->powers[pw_nocontrol] == 0) + player->respawn.timer--; if (leveltime % 8 == 0) { diff --git a/src/p_spec.c b/src/p_spec.c index 315e04aa5..1e3c30299 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2066,7 +2066,12 @@ static void K_HandleLapIncrement(player_t *player) { if (player) { - if ((player->starpostnum == numstarposts) || (player->laps == 0)) + if (leveltime < starttime) + { + // Will fault the player + K_DoIngameRespawn(player); + } + else if ((player->starpostnum == numstarposts) || (player->laps == 0)) { size_t i = 0; UINT8 nump = 0; @@ -2101,6 +2106,14 @@ static void K_HandleLapIncrement(player_t *player) player->karthud[khud_lapanimation] = 80; } + if (rainbowstartavailable == true) + { + S_StartSound(player->mo, sfx_s23c); + player->kartstuff[k_startboost] = 125; + K_SpawnDriftBoostExplosion(player, 3); + rainbowstartavailable = false; + } + if (netgame && player->laps >= (UINT8)cv_numlaps.value) CON_LogMessage(va(M_GetText("%s has finished the race.\n"), player_names[player-players])); @@ -2184,23 +2197,6 @@ static void K_HandleLapIncrement(player_t *player) { S_StartSound(player->mo, sfx_s26d); } - - if (leveltime < starttime) - { - // LATER: replace with the rotatey knockback whenever we get around to it - player->powers[pw_nocontrol] = (starttime - leveltime) + 50; - player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse - S_StartSound(player->mo, sfx_s3k83); - player->karthud[khud_fault] = 1; - player->mo->momx = player->mo->momy = 0; - } - else if (rainbowstartavailable == true) - { - S_StartSound(player->mo, sfx_s23c); - player->kartstuff[k_startboost] = 125; - K_SpawnDriftBoostExplosion(player, 3); - rainbowstartavailable = false; - } } } From 1bbe4ede705d20b2eb44a13e9d823ffef5ee9f5c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 04:13:35 -0400 Subject: [PATCH 190/211] Duel graphic for 1v1s Yu-Gi-Oh! Prison --- src/k_kart.c | 55 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 47bb1ad05..b06c90a3b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7890,7 +7890,7 @@ static patch_t *kp_timeoutsticker; static patch_t *kp_prestartbulb[15]; static patch_t *kp_prestartletters[7]; -static patch_t *kp_startcountdown[16]; +static patch_t *kp_startcountdown[20]; static patch_t *kp_racefault[6]; static patch_t *kp_racefinish[6]; @@ -8030,19 +8030,23 @@ void K_LoadKartHUDGraphics(void) kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX); kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX); - kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX); - kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX); - kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX); - kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); + kp_startcountdown[4] = W_CachePatchName("K_DUEL1", PU_HUDGFX); + kp_startcountdown[5] = W_CachePatchName("K_CNT3B", PU_HUDGFX); + kp_startcountdown[6] = W_CachePatchName("K_CNT2B", PU_HUDGFX); + kp_startcountdown[7] = W_CachePatchName("K_CNT1B", PU_HUDGFX); + kp_startcountdown[8] = W_CachePatchName("K_CNTGOB", PU_HUDGFX); + kp_startcountdown[9] = W_CachePatchName("K_DUEL2", PU_HUDGFX); // Splitscreen - kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX); - kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX); - kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX); - kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); - kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX); - kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX); - kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX); - kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); + kp_startcountdown[10] = W_CachePatchName("K_SMC3A", PU_HUDGFX); + kp_startcountdown[11] = W_CachePatchName("K_SMC2A", PU_HUDGFX); + kp_startcountdown[12] = W_CachePatchName("K_SMC1A", PU_HUDGFX); + kp_startcountdown[13] = W_CachePatchName("K_SMCGOA", PU_HUDGFX); + kp_startcountdown[14] = W_CachePatchName("K_SDUEL1", PU_HUDGFX); + kp_startcountdown[15] = W_CachePatchName("K_SMC3B", PU_HUDGFX); + kp_startcountdown[16] = W_CachePatchName("K_SMC2B", PU_HUDGFX); + kp_startcountdown[17] = W_CachePatchName("K_SMC1B", PU_HUDGFX); + kp_startcountdown[18] = W_CachePatchName("K_SMCGOB", PU_HUDGFX); + kp_startcountdown[19] = W_CachePatchName("K_SDUEL2", PU_HUDGFX); // Fault kp_racefault[0] = W_CachePatchName("K_FAULTA", PU_HUDGFX); @@ -10783,12 +10787,33 @@ static void K_drawKartStartCountdown(void) pnum++; if (leveltime >= starttime-TICRATE) // 1 pnum++; + if (leveltime >= starttime) // GO! + { + UINT8 i; + UINT8 numplayers = 0; + pnum++; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + numplayers++; + + if (numplayers > 2) + break; + } + + if (numplayers == 2) + { + pnum++; // DUEL + } + } + if ((leveltime % (2*5)) / 5) // blink - pnum += 4; + pnum += 5; if (r_splitscreen) // splitscreen - pnum += 8; + pnum += 10; V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]); } From 4044a153caf498382b33a282104e3241afd1debe Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 05:00:12 -0400 Subject: [PATCH 191/211] Splitscreen support for the prestart counter --- src/k_kart.c | 68 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index b06c90a3b..0af1ec658 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -7890,6 +7890,9 @@ static patch_t *kp_timeoutsticker; static patch_t *kp_prestartbulb[15]; static patch_t *kp_prestartletters[7]; +static patch_t *kp_prestartbulb_split[15]; +static patch_t *kp_prestartletters_split[7]; + static patch_t *kp_startcountdown[20]; static patch_t *kp_racefault[6]; static patch_t *kp_racefinish[6]; @@ -8016,6 +8019,14 @@ void K_LoadKartHUDGraphics(void) kp_prestartbulb[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); } + sprintf(buffer, "K_SBLBxx"); + for (i = 0; i < 15; i++) + { + buffer[6] = '0'+((i+1)/10); + buffer[7] = '0'+((i+1)%10); + kp_prestartbulb_split[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + // Pre-start position letters kp_prestartletters[0] = W_CachePatchName("K_PL_P", PU_HUDGFX); kp_prestartletters[1] = W_CachePatchName("K_PL_O", PU_HUDGFX); @@ -8025,6 +8036,14 @@ void K_LoadKartHUDGraphics(void) kp_prestartletters[5] = W_CachePatchName("K_PL_N", PU_HUDGFX); kp_prestartletters[6] = W_CachePatchName("K_PL_EX", PU_HUDGFX); + kp_prestartletters_split[0] = W_CachePatchName("K_SPL_P", PU_HUDGFX); + kp_prestartletters_split[1] = W_CachePatchName("K_SPL_O", PU_HUDGFX); + kp_prestartletters_split[2] = W_CachePatchName("K_SPL_S", PU_HUDGFX); + kp_prestartletters_split[3] = W_CachePatchName("K_SPL_I", PU_HUDGFX); + kp_prestartletters_split[4] = W_CachePatchName("K_SPL_T", PU_HUDGFX); + kp_prestartletters_split[5] = W_CachePatchName("K_SPL_N", PU_HUDGFX); + kp_prestartletters_split[6] = W_CachePatchName("K_SPL_EX", PU_HUDGFX); + // Starting countdown kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX); kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX); @@ -10649,12 +10668,28 @@ static void K_drawKartStartBulbs(void) fixed_t spacing = 24*FRACUNIT; - fixed_t startx = (BASEVIDWIDTH/2)*FRACUNIT + (spacing/2); - fixed_t x, y = 48*FRACUNIT; + fixed_t startx = (BASEVIDWIDTH/2)*FRACUNIT; + fixed_t starty = 48*FRACUNIT; + fixed_t x, y; UINT8 numperrow = numbulbs/2; UINT8 i; + UINT32 splitflag = K_calcSplitFlags(0); + + if (r_splitscreen >= 1) + { + spacing /= 2; + starty /= 3; + + if (r_splitscreen > 1) + { + startx /= 2; + } + } + + startx += (spacing/2); + if (numbulbs <= 10) { // No second row @@ -10667,11 +10702,13 @@ static void K_drawKartStartBulbs(void) numperrow++; } - y -= (spacing/2); + starty -= (spacing/2); } startx -= (spacing/2) * numperrow; + x = startx; + y = starty; for (i = 0; i < numbulbs; i++) { @@ -10708,17 +10745,28 @@ static void K_drawKartStartBulbs(void) } } - V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP, kp_prestartbulb[patchnum], NULL); + V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP|splitflag, + (r_splitscreen ? kp_prestartbulb_split[patchnum] : kp_prestartbulb[patchnum]), NULL); x += spacing; } x = 70*FRACUNIT; - y = 48*FRACUNIT; + y = starty; + + if (r_splitscreen == 1) + { + x = 106*FRACUNIT; + } + else if (r_splitscreen > 1) + { + x = 28*FRACUNIT; + } for (i = 0; i < 10; i++) { UINT8 patchnum = letters_order[i]; INT32 transflag = letters_transparency[(leveltime - i) % 40]; + patch_t *patch = (r_splitscreen ? kp_prestartletters_split[patchnum] : kp_prestartletters[patchnum]); if (transflag >= 10) ; @@ -10727,15 +10775,19 @@ static void K_drawKartStartBulbs(void) if (transflag != 0) transflag = transflag << FF_TRANSSHIFT; - V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP|transflag, kp_prestartletters[patchnum], NULL); + V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP|splitflag|transflag, patch, NULL); } if (i < 9) { - x += (SHORT(kp_prestartletters[patchnum]->width)/2) * FRACUNIT; + x += (SHORT(patch->width)) * FRACUNIT/2; patchnum = letters_order[i+1]; - x += (SHORT(kp_prestartletters[patchnum]->width)/2) * FRACUNIT; + patch = (r_splitscreen ? kp_prestartletters_split[patchnum] : kp_prestartletters[patchnum]); + x += (SHORT(patch->width)) * FRACUNIT/2; + + if (r_splitscreen) + x -= FRACUNIT; } } } From a0befdce2d8e9f3214d3dc442ae2b5e8bc40301b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 05:43:40 -0400 Subject: [PATCH 192/211] Fix drawing on others screens --- src/k_hud.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index af65103f2..b2b8a56c6 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2262,7 +2262,7 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a { *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; - if (*hud_x < -BASEVIDWIDTH * FRACUNIT || *hud_x > BASEVIDWIDTH * FRACUNIT) + if (*hud_x < 0 || *hud_x > BASEVIDWIDTH * FRACUNIT) { *hud_x = -1000 * FRACUNIT; } @@ -2287,7 +2287,7 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a *hud_y = (*hud_y * swhalf) + shhalffixed; *hud_y = *hud_y + NEWTAN(camaim) * swhalf; - if (*hud_y < -BASEVIDHEIGHT * FRACUNIT || *hud_y > BASEVIDHEIGHT * FRACUNIT) + if (*hud_y < 0 || *hud_y > BASEVIDHEIGHT * FRACUNIT) { *hud_y = -1000 * FRACUNIT; } From 0ce492e10383086cc3a830d6596699dc32b10b9d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 12:15:11 -0400 Subject: [PATCH 193/211] Fix sliptiding being weakened after all of the boost stacking changes, by making handling bonuses stack using the same rules --- src/d_player.h | 1 + src/dehacked.c | 1 + src/k_kart.c | 68 +++++++++++++++++++++++++------------------------- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index f1df7ccfb..b9c44ed0e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -297,6 +297,7 @@ typedef enum k_boostpower, // Base boost value, for offroad k_speedboost, // Boost value smoothing for max speed k_accelboost, // Boost value smoothing for acceleration + k_handleboost, // Boost value smoothing for handling k_draftpower, // Drafting power (from 0 to FRACUNIT), doubles your top speed & acceleration at max k_draftleeway, // Leniency timer before removing draft power k_lastdraft, // Last player being drafted diff --git a/src/dehacked.c b/src/dehacked.c index 3474951d1..2eed6a870 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8846,6 +8846,7 @@ static const char *const KARTSTUFF_LIST[] = { "BOOSTPOWER", "SPEEDBOOST", "ACCELBOOST", + "HANDLEBOOST", "DRAFTPOWER", "DRAFTLEEWAY", "LASTDRAFT", diff --git a/src/k_kart.c b/src/k_kart.c index 0af1ec658..54d9e99b1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2065,7 +2065,7 @@ fixed_t K_GetSpindashChargeSpeed(player_t *player) // Light weights have stronger boost stacking -- aka, better metabolism than heavies XD #define METABOLISM -// sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be +// sets k_boostpower, k_speedboost, k_accelboost, and k_handleboost to whatever we need it to be static void K_GetKartBoostPower(player_t *player) { #ifdef METABOLISM @@ -2074,7 +2074,7 @@ static void K_GetKartBoostPower(player_t *player) #endif // METABOLISM fixed_t boostpower = FRACUNIT; - fixed_t speedboost = 0, accelboost = 0; + fixed_t speedboost = 0, accelboost = 0, handleboost = 0; UINT8 numboosts = 0; if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow] == 1) // Slow down after you've been bumped @@ -2092,18 +2092,20 @@ static void K_GetKartBoostPower(player_t *player) #ifdef METABOLISM -#define ADDBOOST(s,a) { \ +#define ADDBOOST(s,a,h) { \ numboosts++; \ speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \ accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \ + handleboost += FixedDiv(h, FRACUNIT + (metabolism * (numboosts-1))); \ } #else -#define ADDBOOST(s,a) { \ +#define ADDBOOST(s,a,h) { \ numboosts++; \ speedboost += s / numboosts; \ accelboost += a / numboosts; \ + handleboost += h / numboosts; \ } #endif // METABOLISM @@ -2113,18 +2115,24 @@ static void K_GetKartBoostPower(player_t *player) UINT8 i; for (i = 0; i < player->kartstuff[k_numsneakers]; i++) { - ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration + ADDBOOST(FRACUNIT/2, 8*FRACUNIT, FRACUNIT/4); // + 50% top speed, + 800% acceleration, +25% handling } } if (player->kartstuff[k_invincibilitytimer]) // Invincibility { - ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration + ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT, FRACUNIT/4); // + 37.5% top speed, + 300% acceleration, +25% handling + } + + if (player->kartstuff[k_growshrinktimer] > 0) // Grow + { + ADDBOOST(0, 0, FRACUNIT/4); // + 0% top speed, + 0% acceleration, +25% handling } if (player->kartstuff[k_flamedash]) // Flame Shield dash { - ADDBOOST(K_FlameShieldDashVar(player->kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration + fixed_t dash = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); + ADDBOOST(dash, 3*FRACUNIT, FixedDiv(dash, FRACUNIT/2) / 4); // + infinite top speed, + 300% acceleration, + infinite handling } if (player->kartstuff[k_spindashboost]) // Spindash boost @@ -2134,28 +2142,29 @@ static void K_GetKartBoostPower(player_t *player) // character & charge dependent ADDBOOST( FixedMul(MAXCHARGESPEED, player->kartstuff[k_spindashspeed]), // + 0 to K_GetSpindashChargeSpeed()% top speed - (4*FRACUNIT) + (36*player->kartstuff[k_spindashspeed]) // + 400% to 4000% acceleration + (4*FRACUNIT) + (36*player->kartstuff[k_spindashspeed]), // + 400% to 4000% acceleration + 0 // + 0% handling ); } if (player->kartstuff[k_startboost]) // Startup Boost { - ADDBOOST(FRACUNIT/2, 4*FRACUNIT); // + 50% top speed, + 400% acceleration + ADDBOOST(FRACUNIT/2, 4*FRACUNIT, 0); // + 50% top speed, + 400% acceleration, +0% handling } if (player->kartstuff[k_driftboost]) // Drift Boost { - ADDBOOST(FRACUNIT/4, 4*FRACUNIT); // + 25% top speed, + 400% acceleration + ADDBOOST(FRACUNIT/4, 4*FRACUNIT, 0); // + 25% top speed, + 400% acceleration, +0% handling } if (player->kartstuff[k_ringboost]) // Ring Boost { - ADDBOOST(FRACUNIT/5, 4*FRACUNIT); // + 20% top speed, + 400% acceleration + ADDBOOST(FRACUNIT/5, 4*FRACUNIT, 0); // + 20% top speed, + 400% acceleration, +0% handling } if (player->kartstuff[k_eggmanexplode]) // Ready-to-explode { - ADDBOOST(3*FRACUNIT/20, FRACUNIT); // + 15% top speed, + 100% acceleration + ADDBOOST(3*FRACUNIT/20, FRACUNIT, 0); // + 15% top speed, + 100% acceleration, +0% handling } if (player->kartstuff[k_draftpower] > 0) // Drafting @@ -2178,6 +2187,8 @@ static void K_GetKartBoostPower(player_t *player) } player->kartstuff[k_accelboost] = accelboost; + player->kartstuff[k_handleboost] = handleboost; + player->kartstuff[k_numboosts] = numboosts; } @@ -6416,7 +6427,7 @@ static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer) if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) { - countersteer = 3*countersteer/2; + countersteer = FixedMul(countersteer, 3*FRACUNIT/2); } return basedrift + (FixedMul(driftadjust * FRACUNIT, countersteer) / FRACUNIT); @@ -6427,6 +6438,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) fixed_t p_maxspeed; fixed_t p_speed; fixed_t weightadjust; + fixed_t turnfixed = turnvalue * FRACUNIT; if ((player->mo == NULL || P_MobjWasRemoved(player->mo))) { @@ -6457,16 +6469,13 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) if (K_PlayerUsesBotMovement(player)) { - turnvalue = 5*turnvalue/4; // Base increase to turning - turnvalue = FixedMul( - turnvalue * FRACUNIT, - K_BotRubberband(player) - ) / FRACUNIT; + turnfixed = FixedMul(turnfixed, 5*FRACUNIT/4); // Base increase to turning + turnfixed = FixedMul(turnfixed, K_BotRubberband(player)); } if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo)) { - fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, KART_FULLTURN*FRACUNIT); + fixed_t countersteer = FixedDiv(turnfixed, KART_FULLTURN*FRACUNIT); // If we're drifting we have a completely different turning value @@ -6475,32 +6484,23 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) countersteer = FRACUNIT; } - turnvalue = K_GetKartDriftValue(player, countersteer); - - return turnvalue; + return K_GetKartDriftValue(player, countersteer); } - if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) + if (player->kartstuff[k_handleboost] > 0) { - turnvalue = 5*turnvalue/4; - } - - if (player->kartstuff[k_flamedash] > 0) - { - fixed_t multiplier = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); - multiplier = FRACUNIT + (FixedDiv(multiplier, FRACUNIT/2) / 4); - turnvalue = FixedMul(turnvalue * FRACUNIT, multiplier) / FRACUNIT; + turnfixed = FixedMul(turnfixed, FRACUNIT + player->kartstuff[k_handleboost]); } if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) { - turnvalue = 3*turnvalue/2; + turnfixed = FixedMul(turnfixed, 3*FRACUNIT/2); } // Weight has a small effect on turning - turnvalue = FixedMul(turnvalue * FRACUNIT, weightadjust) / FRACUNIT; + turnfixed = FixedMul(turnfixed, weightadjust); - return turnvalue; + return (turnfixed / FRACUNIT); } INT32 K_GetKartDriftSparkValue(player_t *player) From ec9dc037254ef71185d169c0fd69b3708b107703 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 12:16:08 -0400 Subject: [PATCH 194/211] Allow sliptides to spawn for any item that gives you a handling boost, since that's what lets it happen --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 54d9e99b1..9d3efa331 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6748,7 +6748,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftend] = 0; } - if ((!player->kartstuff[k_sneakertimer]) + if ((player->kartstuff[k_handleboost] == 0) || (!player->cmd.driftturn) || (!player->kartstuff[k_aizdriftstrat]) || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) From 903f118aacf8d8e8be6a7121c64714aacc6b5d94 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 12:47:55 -0400 Subject: [PATCH 195/211] Only spawn sliptides above your top speed Looks a bit funky being able to do it at low speed using invincibility, plus it's not helping you to do this if you're going too slow :p --- src/k_kart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 9d3efa331..8962006e5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3349,6 +3349,9 @@ static void K_SpawnAIZDust(player_t *player) if (!P_IsObjectOnGround(player->mo)) return; + if (player->speed <= K_GetKartSpeed(player, false)) + return; + travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); //S_StartSound(player->mo, sfx_s3k47); From 72bfbefad37d94ac2e7e153773edb78ec923d087 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 21:28:09 -0400 Subject: [PATCH 196/211] SHORT fixes from vanilla for shadows --- src/hardware/hw_main.c | 6 +++--- src/r_things.c | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 06e02b76b..236e84b70 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -2860,7 +2860,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) HWR_GetPatch(gpatch); scalemul = FixedMul(FRACUNIT - floordiff/640, scale); - scalemul = FixedMul(scalemul, (thing->radius*2) / gpatch->height); + scalemul = FixedMul(scalemul, (thing->radius*2) / SHORT(gpatch->height)); fscale = FIXED_TO_FLOAT(scalemul); fx = FIXED_TO_FLOAT(thingxpos); @@ -2872,9 +2872,9 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) // 0--1 if (thing && fabsf(fscale - 1.0f) > 1.0E-36f) - offset = (gpatch->height/2) * fscale; + offset = (SHORT(gpatch->height)/2) * fscale; else - offset = (float)(gpatch->height/2); + offset = (float)(SHORT(gpatch->height)/2); shadowVerts[2].x = shadowVerts[3].x = fx + offset; shadowVerts[1].x = shadowVerts[0].x = fx - offset; diff --git a/src/r_things.c b/src/r_things.c index 05672ae4d..1f330827f 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1295,8 +1295,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, yscale = FixedDiv(projectiony, tz); shadowxscale = FixedMul(thing->radius*2, scalemul); shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(floorz - viewz), tz)); - shadowyscale = min(shadowyscale, shadowxscale) / patch->height; - shadowxscale /= patch->width; + shadowyscale = min(shadowyscale, shadowxscale) / SHORT(patch->height); + shadowxscale /= SHORT(patch->width); shadowskew = 0; if (floorslope) @@ -1311,24 +1311,24 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, //CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope); if (viewz < floorz) - shadowyscale += FixedMul(FixedMul(thing->radius*2 / patch->height, scalemul), zslope); + shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); else - shadowyscale -= FixedMul(FixedMul(thing->radius*2 / patch->height, scalemul), zslope); + shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); shadowyscale = abs(shadowyscale); shadowskew = xslope; } - tx -= patch->width * shadowxscale/2; + tx -= SHORT(patch->width) * shadowxscale/2; x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; if (x1 >= viewwidth) return; - tx += patch->width * shadowxscale; + tx += SHORT(patch->width) * shadowxscale; x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--; if (x2 < 0 || x2 <= x1) return; - if (shadowyscale < FRACUNIT/patch->height) return; // fix some crashes? + if (shadowyscale < FRACUNIT/SHORT(patch->height)) return; // fix some crashes? shadow = R_NewVisSprite(); @@ -1379,7 +1379,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->startfrac = 0; //shadow->xiscale = 0x7ffffff0 / (shadow->xscale/2); - shadow->xiscale = (patch->width<xiscale = (SHORT(patch->width)<x1 > x1) shadow->startfrac += shadow->xiscale*(shadow->x1-x1); From 1008dcce36627d5c1046fb0e3d0d586f0bd0bb1b Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 29 Jul 2020 23:55:36 -0400 Subject: [PATCH 197/211] Vertical flip for shadows --- src/hardware/hw_main.c | 19 ++++----- src/r_things.c | 88 +++++++++++++++++++++++++----------------- 2 files changed, 62 insertions(+), 45 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 236e84b70..47f1b4e49 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -2829,17 +2829,18 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) UINT8 lightlevel = 0; extracolormap_t *colormap = NULL; UINT8 i; + SINT8 flip = P_MobjFlip(thing); INT32 light; fixed_t scalemul; UINT16 alpha; fixed_t floordiff; - fixed_t floorz; + fixed_t groundz; fixed_t slopez; - pslope_t *floorslope; + pslope_t *groundslope; - floorz = R_GetShadowZ(thing, &floorslope); - floordiff = abs(thingzpos - floorz); + groundz = R_GetShadowZ(thing, &groundslope); + floordiff = abs((flip < 0 ? thing->height : 0) + thingzpos - groundz); alpha = floordiff / (4*FRACUNIT) + 75; if (alpha >= 255) return; @@ -2889,18 +2890,18 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) shadowVerts[i].z = fy + ((oldx - fx) * gr_viewsin) + ((oldy - fy) * gr_viewcos); } - if (floorslope) + if (groundslope) { for (i = 0; i < 4; i++) { - slopez = P_GetZAt(floorslope, FLOAT_TO_FIXED(shadowVerts[i].x), FLOAT_TO_FIXED(shadowVerts[i].z)); - shadowVerts[i].y = FIXED_TO_FLOAT(slopez) + 0.05f; + slopez = P_GetZAt(groundslope, FLOAT_TO_FIXED(shadowVerts[i].x), FLOAT_TO_FIXED(shadowVerts[i].z)); + shadowVerts[i].y = FIXED_TO_FLOAT(slopez) + flip * 0.05f; } } else { for (i = 0; i < 4; i++) - shadowVerts[i].y = FIXED_TO_FLOAT(floorz) + 0.05f; + shadowVerts[i].y = FIXED_TO_FLOAT(groundz) + flip * 0.05f; } shadowVerts[0].s = shadowVerts[3].s = 0; @@ -2911,7 +2912,7 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) if (thing->subsector->sector->numlights) { - light = R_GetPlaneLight(thing->subsector->sector, floorz, false); // Always use the light at the top instead of whatever I was doing before + light = R_GetPlaneLight(thing->subsector->sector, groundz, false); // Always use the light at the top instead of whatever I was doing before if (thing->subsector->sector->lightlist[light].extra_colormap) colormap = thing->subsector->sector->lightlist[light].extra_colormap; diff --git a/src/r_things.c b/src/r_things.c index 1f330827f..43a689545 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1157,25 +1157,34 @@ static void R_SplitSprite(vissprite_t *sprite) // fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) { - fixed_t z, floorz = INT32_MIN; - pslope_t *slope, *floorslope = NULL; + boolean isflipped = thing->eflags & MFE_VERTICALFLIP; + fixed_t z, groundz = isflipped ? INT32_MAX : INT32_MIN; + pslope_t *slope, *groundslope = NULL; msecnode_t *node; sector_t *sector; ffloor_t *rover; +#define CHECKZ (isflipped ? z > thing->z+thing->height/2 && z < groundz : z < thing->z+thing->height/2 && z > groundz) for (node = thing->touching_sectorlist; node; node = node->m_sectorlist_next) { sector = node->m_sector; - slope = (sector->heightsec != -1) ? NULL : sector->f_slope; - z = slope ? P_GetZAt(slope, thing->x, thing->y) : ( - (sector->heightsec != -1) ? sectors[sector->heightsec].floorheight : sector->floorheight - ); + slope = sector->heightsec != -1 ? NULL : (isflipped ? sector->c_slope : sector->f_slope); - if (z < thing->z+thing->height/2 && z > floorz) + if (sector->heightsec != -1) + z = isflipped ? sectors[sector->heightsec].ceilingheight : sectors[sector->heightsec].floorheight; + else { - floorz = z; - floorslope = slope; + if (isflipped) + z = sector->c_slope ? P_GetZAt(sector->c_slope, thing->x, thing->y) : sector->ceilingheight; // P_GetSectorCeilingZAt + else + z = sector->f_slope ? P_GetZAt(sector->f_slope, thing->x, thing->y) : sector->floorheight; // P_GetSectorFloorZAt + } + + if CHECKZ + { + groundz = z; + groundslope = slope; } if (sector->ffloors) @@ -1184,24 +1193,30 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES) || (rover->alpha < 90 && !(rover->flags & FF_SWIMMABLE))) continue; - z = *rover->t_slope ? P_GetZAt(*rover->t_slope, thing->x, thing->y) : *rover->topheight; - if (z < thing->z+thing->height/2 && z > floorz) + if (isflipped) + z = *rover->b_slope ? P_GetZAt(*rover->b_slope, thing->x, thing->y) : *rover->bottomheight; // P_GetFFloorBottomZAt + else + z = *rover->t_slope ? P_GetZAt(*rover->t_slope, thing->x, thing->y) : *rover->topheight; // P_GetFFloorTopZAt + + if CHECKZ { - floorz = z; - floorslope = *rover->t_slope; + groundz = z; + groundslope = isflipped ? *rover->b_slope : *rover->t_slope; } } } - if (thing->floorz > floorz + (!floorslope ? 0 : FixedMul(abs(floorslope->zdelta), thing->radius*3/2))) + if (isflipped ? (thing->ceilingz < groundz - (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2))) + : (thing->floorz > groundz + (!groundslope ? 0 : FixedMul(abs(groundslope->zdelta), thing->radius*3/2)))) { - floorz = thing->floorz; - floorslope = NULL; + groundz = isflipped ? thing->ceilingz : thing->floorz; + groundslope = NULL; } #if 0 // Unfortunately, this drops CEZ2 down to sub-17 FPS on my i7. //#ifdef POLYOBJECTS - // Check polyobjects and see if floorz needs to be altered, for rings only because they don't update floorz + // NOTE: this section was not updated to reflect reverse gravity support + // Check polyobjects and see if groundz needs to be altered, for rings only because they don't update floorz if (thing->type == MT_RING) { INT32 xl, xh, yl, yh, bx, by; @@ -1246,10 +1261,10 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) // We're inside it! Yess... z = po->lines[0]->backsector->ceilingheight; - if (z < thing->z+thing->height/2 && z > floorz) + if (z < thing->z+thing->height/2 && z > groundz) { - floorz = z; - floorslope = NULL; + groundz = z; + groundslope = NULL; } } plink = (polymaplink_t *)(plink->link.next); @@ -1259,9 +1274,9 @@ fixed_t R_GetShadowZ(mobj_t *thing, pslope_t **shadowslope) #endif if (shadowslope != NULL) - *shadowslope = floorslope; + *shadowslope = groundslope; - return floorz; + return groundz; } static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, fixed_t tx, fixed_t tz) @@ -1272,14 +1287,15 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, INT32 light = 0; fixed_t scalemul; UINT8 trans; fixed_t floordiff; - fixed_t floorz; - pslope_t *floorslope; + fixed_t groundz; + pslope_t *groundslope; + boolean isflipped = thing->eflags & MFE_VERTICALFLIP; - floorz = R_GetShadowZ(thing, &floorslope); + groundz = R_GetShadowZ(thing, &groundslope); - if (abs(floorz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes + if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes - floordiff = abs(thing->z - floorz); + floordiff = abs((isflipped ? thing->height : 0) + thing->z - groundz); trans = floordiff / (100*FRACUNIT) + 3; if (trans >= 9) return; @@ -1294,23 +1310,23 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, xscale = FixedDiv(projection, tz); yscale = FixedDiv(projectiony, tz); shadowxscale = FixedMul(thing->radius*2, scalemul); - shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(floorz - viewz), tz)); + shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz)); shadowyscale = min(shadowyscale, shadowxscale) / SHORT(patch->height); shadowxscale /= SHORT(patch->width); shadowskew = 0; - if (floorslope) + if (groundslope) { // haha let's try some dumb stuff fixed_t xslope, zslope; - angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - floorslope->xydirection) >> ANGLETOFINESHIFT; + angle_t sloperelang = (R_PointToAngle(thing->x, thing->y) - groundslope->xydirection) >> ANGLETOFINESHIFT; - xslope = FixedMul(FINESINE(sloperelang), floorslope->zdelta); - zslope = FixedMul(FINECOSINE(sloperelang), floorslope->zdelta); + xslope = FixedMul(FINESINE(sloperelang), groundslope->zdelta); + zslope = FixedMul(FINECOSINE(sloperelang), groundslope->zdelta); //CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope); - if (viewz < floorz) + if (viewz < groundz) shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); else shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); @@ -1340,7 +1356,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->heightsec = vis->heightsec; shadow->thingheight = FRACUNIT; - shadow->pz = floorz; + shadow->pz = groundz + (isflipped ? -shadow->thingheight : 0); shadow->pzt = shadow->pz + shadow->thingheight; shadow->mobjflags = 0; @@ -1348,8 +1364,8 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->dispoffset = vis->dispoffset - 5; shadow->gx = thing->x; shadow->gy = thing->y; - shadow->gzt = shadow->pz + patch->height * shadowyscale / 2; - shadow->gz = shadow->gzt - patch->height * shadowyscale; + shadow->gzt = (isflipped ? shadow->pzt : shadow->pz) + SHORT(patch->height) * shadowyscale / 2; + shadow->gz = shadow->gzt - SHORT(patch->height) * shadowyscale; shadow->texturemid = FixedMul(thing->scale, FixedDiv(shadow->gzt - viewz, shadowyscale)); if (thing->skin && ((skin_t *)thing->skin)->flags & SF_HIRES) shadow->texturemid = FixedMul(shadow->texturemid, ((skin_t *)thing->skin)->highresscale); From 092db17f84e8cc991cbb3653628ee29e6b604d5c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 31 Jul 2020 01:18:45 -0400 Subject: [PATCH 198/211] OK turns out my hunches were just totally wrong. THIS makes sliptiding really accurate to v1, and we ended up not liking handling stacking --- src/k_kart.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 8962006e5..7699c0917 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2073,6 +2073,10 @@ static void K_GetKartBoostPower(player_t *player) const fixed_t metabolism = FRACUNIT - ((9-player->kartweight) * maxmetabolismincrease / 8); #endif // METABOLISM + // v2 almost broke sliptiding when it fixed turning bugs! + // This value is fine-tuned to feel like v1 again without reverting any of those changes. + const fixed_t sliptidehandling = 7*FRACUNIT/10; + fixed_t boostpower = FRACUNIT; fixed_t speedboost = 0, accelboost = 0, handleboost = 0; UINT8 numboosts = 0; @@ -2096,7 +2100,7 @@ static void K_GetKartBoostPower(player_t *player) numboosts++; \ speedboost += FixedDiv(s, FRACUNIT + (metabolism * (numboosts-1))); \ accelboost += FixedDiv(a, FRACUNIT + (metabolism * (numboosts-1))); \ - handleboost += FixedDiv(h, FRACUNIT + (metabolism * (numboosts-1))); \ + handleboost = max(h, handleboost); \ } #else @@ -2105,7 +2109,7 @@ static void K_GetKartBoostPower(player_t *player) numboosts++; \ speedboost += s / numboosts; \ accelboost += a / numboosts; \ - handleboost += h / numboosts; \ + handleboost = max(h, handleboost); \ } #endif // METABOLISM @@ -2115,24 +2119,28 @@ static void K_GetKartBoostPower(player_t *player) UINT8 i; for (i = 0; i < player->kartstuff[k_numsneakers]; i++) { - ADDBOOST(FRACUNIT/2, 8*FRACUNIT, FRACUNIT/4); // + 50% top speed, + 800% acceleration, +25% handling + ADDBOOST(FRACUNIT/2, 8*FRACUNIT, sliptidehandling); // + 50% top speed, + 800% acceleration, +70% handling } } if (player->kartstuff[k_invincibilitytimer]) // Invincibility { - ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT, FRACUNIT/4); // + 37.5% top speed, + 300% acceleration, +25% handling + ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT, sliptidehandling/3); // + 37.5% top speed, + 300% acceleration, +23% handling } if (player->kartstuff[k_growshrinktimer] > 0) // Grow { - ADDBOOST(0, 0, FRACUNIT/4); // + 0% top speed, + 0% acceleration, +25% handling + ADDBOOST(0, 0, sliptidehandling/3); // + 0% top speed, + 0% acceleration, +23% handling } if (player->kartstuff[k_flamedash]) // Flame Shield dash { fixed_t dash = K_FlameShieldDashVar(player->kartstuff[k_flamedash]); - ADDBOOST(dash, 3*FRACUNIT, FixedDiv(dash, FRACUNIT/2) / 4); // + infinite top speed, + 300% acceleration, + infinite handling + ADDBOOST( + dash, // + infinite top speed + 3*FRACUNIT, // + 300% acceleration + FixedMul(FixedDiv(dash, FRACUNIT/2), sliptidehandling) // + infinite handling; when going at the same speed as Sneaker, you get the same handling boost as it + ); } if (player->kartstuff[k_spindashboost]) // Spindash boost From 83b85568517fb337c6da6d27a3ac0f7f1ac07b3d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 31 Jul 2020 01:41:04 -0400 Subject: [PATCH 199/211] Old Flame Shield handling --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7699c0917..a78caa7d8 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2139,7 +2139,7 @@ static void K_GetKartBoostPower(player_t *player) ADDBOOST( dash, // + infinite top speed 3*FRACUNIT, // + 300% acceleration - FixedMul(FixedDiv(dash, FRACUNIT/2), sliptidehandling) // + infinite handling; when going at the same speed as Sneaker, you get the same handling boost as it + FixedMul(FixedDiv(dash, FRACUNIT/2), sliptidehandling/3) // + infinite handling ); } From 4847f71707ef91bd15d61653c06fd74d8724665d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 31 Jul 2020 11:21:18 -0400 Subject: [PATCH 200/211] Make slower --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index b2b8a56c6..4ceb24dd9 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -729,7 +729,7 @@ void K_AdjustXYWithSnap(INT32 *x, INT32 *y, UINT32 options, INT32 dupx, INT32 du if (options & V_SLIDEIN) { - tic_t length = TICRATE/3; + tic_t length = TICRATE/2; if (leveltime < introtime + length) { From de7147008ead394a358e8ee1481c3972ac8f4850 Mon Sep 17 00:00:00 2001 From: Latapostrophe Date: Fri, 31 Jul 2020 18:13:28 +0200 Subject: [PATCH 201/211] Let's not try to outsmart the way mf2_objectflip works and just disable it entierely alongside mfe_verticalflip if the waypoint isn't flipped. --- src/k_respawn.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/k_respawn.c b/src/k_respawn.c index 71b96e085..863a309c9 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -46,10 +46,7 @@ fixed_t K_RespawnOffset(player_t *player, boolean flip) } else { - - if (P_GetMobjGravity(player->mo) > 0) - player->mo->flags2 &= ~MF2_OBJECTFLIP; // See comment above, ditto. - + player->mo->flags2 &= ~MF2_OBJECTFLIP; player->mo->eflags &= ~MFE_VERTICALFLIP; z += (128 * mapobjectscale); } From ed2d40b98e69661a87beb0d34cb792c28fc42ff4 Mon Sep 17 00:00:00 2001 From: James R Date: Fri, 31 Jul 2020 22:17:22 -0700 Subject: [PATCH 202/211] Unused parameter :) --- src/d_netcmd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b37649218..a106530cf 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2135,6 +2135,7 @@ void SendWeaponPref4(void) static void Got_WeaponPref(UINT8 **cp,INT32 playernum) { + (void)playernum; /*UINT8 prefs = */READUINT8(*cp); // Read it still to avoid instant desyncs in netgames. } From b0c6b626cf9193c8584bfd87f6fd4ed5310b2115 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 4 Aug 2020 02:11:52 -0700 Subject: [PATCH 203/211] Don't slow down on sneaker panels during spinout --- src/k_kart.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index eac90b665..ce092fa43 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2295,7 +2295,7 @@ SINT8 K_GetForwardMove(player_t *player) { SINT8 forwardmove = player->cmd.forwardmove; - if ((player->pflags & PF_STASIS) || (player->pflags & PF_SLIDING) || player->kartstuff[k_spinouttimer] || K_PlayerEBrake(player)) + if ((player->pflags & PF_STASIS) || (player->pflags & PF_SLIDING)) { return 0; } @@ -2305,6 +2305,11 @@ SINT8 K_GetForwardMove(player_t *player) return MAXPLMOVE; } + if (player->kartstuff[k_spinouttimer] || K_PlayerEBrake(player)) + { + return 0; + } + return forwardmove; } From 4f1642bbced7fdcf7b0d5aa191390f6d508124d7 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Wed, 5 Aug 2020 00:40:56 -0500 Subject: [PATCH 204/211] Clarify addon-related messages --- src/d_clisrv.c | 21 +++++++++++---------- src/m_menu.c | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 79a8e8291..7f335182e 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1923,11 +1923,11 @@ static boolean CL_FinishedFileList(void) CL_Reset(); D_StartTitle(); M_StartMessage(M_GetText( - "You have WAD files loaded or have\n" - "modified the game in some way, and\n" - "your file list does not match\n" - "the server's file list.\n" - "Please restart SRB2Kart before connecting.\n\n" + "You have the wrong addons loaded.\n\n" + "To play on this server, restart\n" + "the game and don't load any addons.\n" + "SRB2Kart will automatically add\n" + "everything you need when you join.\n\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; @@ -1948,11 +1948,12 @@ static boolean CL_FinishedFileList(void) CL_Reset(); D_StartTitle(); M_StartMessage(M_GetText( - "You cannot connect to this server\n" - "because you cannot download the files\n" - "that you are missing from the server.\n\n" - "See the console or log file for\n" - "more details.\n\n" + "An error occured when trying to\n" + "download missing addons.\n" + "(This is almost always a problem\n" + "with the server, not your game.)\n\n" + "See the console or log file\n" + "for additional details.\n\n" "Press ESC\n" ), NULL, MM_NOTHING); return false; diff --git a/src/m_menu.c b/src/m_menu.c index e08a53c56..eb19ecef3 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -4833,7 +4833,7 @@ static boolean M_AddonsRefresh(void) else if (majormods && !prevmajormods) { S_StartSound(NULL, sfx_s221); - message = va("%c%s\x80\nGameplay has now been modified.\nIf you wish to play Record Attack mode, restart the game to clear existing addons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); + message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); prevmajormods = majormods; } @@ -8538,7 +8538,7 @@ static void M_ConnectMenuModChecks(INT32 choice) if (modifiedgame) { - M_StartMessage(M_GetText("Addons are currently loaded.\n\nYou will only be able to join a server if\nit has the same ones loaded in the same order, which may be unlikely.\n\nIf you wish to play on other servers,\nrestart the game to clear existing addons.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); + M_StartMessage(M_GetText("You have addons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2Kart will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); return; } From 2422d1c5b799833c185a8ea44c4a0ebef81c5ba6 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 6 Aug 2020 10:34:16 -0500 Subject: [PATCH 205/211] Fix indirect item cooldown never resetting --- src/p_tick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_tick.c b/src/p_tick.c index eaa8b4628..177a0060e 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -726,7 +726,7 @@ void P_Ticker(boolean run) if (exitcountdown > 1) exitcountdown--; - if (indirectitemcooldown > 1) + if (indirectitemcooldown > 0) indirectitemcooldown--; if (hyubgone > 1) hyubgone--; From e5463afb58d91b113c539b52cef92da415037369 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Thu, 6 Aug 2020 10:51:53 -0500 Subject: [PATCH 206/211] Fix Hyudoro cooldown never resetting --- src/p_tick.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_tick.c b/src/p_tick.c index 177a0060e..4cc6c9ba4 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -728,7 +728,7 @@ void P_Ticker(boolean run) if (indirectitemcooldown > 0) indirectitemcooldown--; - if (hyubgone > 1) + if (hyubgone > 0) hyubgone--; if (G_BattleGametype()) From 3cb1a53b3f7d992dd4ceb0890af65b6b21bf11e7 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 9 Aug 2020 18:26:13 -0700 Subject: [PATCH 207/211] Bump MAXNETNODES to 127 --- src/d_net.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/d_net.h b/src/d_net.h index 30de29916..cc80fcaa6 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -19,7 +19,9 @@ #define __D_NET__ // Max computers in a game -#define MAXNETNODES 64 +// 127 is probably as high as this can go, because +// SINT8 is used for nodes sometimes >:( +#define MAXNETNODES 127 #define BROADCASTADDR MAXNETNODES #define NETSPLITSCREEN // Kart's splitscreen netgame feature From 8e493bddc3ce4c788b19a745b1c27d086981d874 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 9 Aug 2020 23:32:43 -0700 Subject: [PATCH 208/211] Add http-mserv to fuck --- src/CMakeLists.txt | 2 ++ src/sdl/Srb2SDL-vc10.vcxproj | 2 ++ src/sdl/Srb2SDL-vc10.vcxproj.filters | 6 ++++ src/sdl/Srb2SDL-vc9.vcproj | 44 ++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c0d673a6f..51e477818 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ set(SRB2_CORE_SOURCES m_random.c md5.c mserv.c + http-mserv.c s_sound.c screen.c sounds.c @@ -99,6 +100,7 @@ set(SRB2_CORE_HEADERS m_swap.h md5.h mserv.h + http-mserv.h p5prof.h s_sound.h screen.h diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 69b3465ac..813e31a98 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -249,6 +249,7 @@ + @@ -399,6 +400,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index 9e5b98375..f197a394b 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -291,6 +291,9 @@ I_Interface + + I_Interface + LUA @@ -663,6 +666,9 @@ I_Interface + + I_Interface + LUA diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj index b21eedb87..115e17a30 100644 --- a/src/sdl/Srb2SDL-vc9.vcproj +++ b/src/sdl/Srb2SDL-vc9.vcproj @@ -2782,6 +2782,50 @@ RelativePath="..\mserv.h" > + + + + + + + + + + + + + + + + Date: Fri, 14 Aug 2020 08:25:12 -0700 Subject: [PATCH 209/211] dumbass doesn't test changes before pushing directly to next (cherry picked from commit be14b8a564a89a5afb84ac19f1586f3db7f68367) --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51e477818..cbb0fa203 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,7 +100,6 @@ set(SRB2_CORE_HEADERS m_swap.h md5.h mserv.h - http-mserv.h p5prof.h s_sound.h screen.h From d31c33e03557c3507b1d07ee01b34ded05fb4434 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 16 Aug 2020 21:18:33 -0400 Subject: [PATCH 210/211] No previous prototype for K_DropKitchenSink --- src/k_kart.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/k_kart.h b/src/k_kart.h index e6241f8ae..796065404 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -56,6 +56,7 @@ INT32 K_GetKartDriftSparkValue(player_t *player); void K_KartUpdatePosition(player_t *player); void K_DropItems(player_t *player); void K_DropRocketSneaker(player_t *player); +void K_DropKitchenSink(player_t *player); void K_StripItems(player_t *player); void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); From 325ae01e4b4d0a6f8a1ff91f68a6bedcf6532ae6 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 19 Aug 2020 16:46:24 -0700 Subject: [PATCH 211/211] K_DropRocketSneaker: MF2_DONTDRAW -> MFD_DONTDRAW --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 1307d9201..41e33eee1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4601,7 +4601,7 @@ void K_DropRocketSneaker(player_t *player) if (shoe->type != MT_ROCKETSNEAKER) return; //woah, not a rocketsneaker, bail! safeguard in case this gets used when you're holding non-rocketsneakers - shoe->flags2 &= ~MF2_DONTDRAW; + shoe->drawflags &= ~MFD_DONTDRAW; shoe->flags &= ~MF_NOGRAVITY; shoe->angle += ANGLE_45;

~QnCe^kM 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

VLDN4Ylx?EMJl8aWUmbP!flAxY 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 literal 0 HcmV?d00001 diff --git a/libs/curl/lib32/libcurl.dll.a b/libs/curl/lib32/libcurl.dll.a new file mode 100644 index 0000000000000000000000000000000000000000..ff1003b6e0a6b62bafa9d713a8e3152f00fe7da8 GIT binary patch literal 151856 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 a`@1=dDmCLh9SarN->C{ZJedrarvDEhhM*q+ literal 0 HcmV?d00001 diff --git a/libs/curl/lib64/libcurl-x64.dll b/libs/curl/lib64/libcurl-x64.dll new file mode 100644 index 0000000000000000000000000000000000000000..7dab52541745f2e5127394e5878d70ed5fb2b9c3 GIT binary patch literal 665600 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