diff --git a/.travis.yml b/.travis.yml index b6f8a7aa7..6d2e8cddf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -107,7 +107,7 @@ matrix: - p7zip-full - gcc-8 compiler: gcc-8 - env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough -Wno-error=format-overflow" GCC81=1 + env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough -Wno-error=format-overflow -Wno-error=format-truncation" GCC81=1 if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/ #gcc-8 (Ubuntu 7.2.0-1ubuntu1~14.04) 8.1.0 - os: linux diff --git a/CMakeLists.txt b/CMakeLists.txt index 7995034d3..13a6bbaca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0) # DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string. # Version change is fine. project(SRB2 - VERSION 1.1.0 + VERSION 2.0.0 LANGUAGES C) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) diff --git a/appveyor.yml b/appveyor.yml index 3d46cf6de..cc5a01741 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.1.0.{branch}-{build} +version: 2.0.0.{branch}-{build} os: MinGW environment: @@ -29,7 +29,7 @@ environment: ############################## DPL_ENABLED: 0 DPL_TAG_ENABLED: 0 - DPL_INSTALLER_NAME: srb2kart-v110 + DPL_INSTALLER_NAME: srb2kart-v200 # Asset handling is barebones vs. Travis Deployer. We operate on 7z only. # Include the README files and the OpenGL batch in the main and patch archives. # The x86/x64 archives contain the DLL binaries. diff --git a/src/command.c b/src/command.c index 3eebe32d1..6d9c86d3e 100644 --- a/src/command.c +++ b/src/command.c @@ -902,7 +902,10 @@ static void COM_Add_f(void) return; } - CV_AddValue(cvar, atoi(COM_Argv(2))); + if (( cvar->flags & CV_FLOAT )) + CV_Set(cvar, va("%f", FIXED_TO_FLOAT (cvar->value) + atof(COM_Argv(2)))); + else + CV_AddValue(cvar, atoi(COM_Argv(2))); } // ========================================================================= diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3bd02e68b..9e1f46e5a 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1433,8 +1433,14 @@ static void SV_SendPlayerInfo(INT32 node) UINT8 i; netbuffer->packettype = PT_PLAYERINFO; - for (i = 0; i < MAXPLAYERS; i++) + for (i = 0; i < MSCOMPAT_MAXPLAYERS; i++) { + if (i >= MAXPLAYERS) + { + netbuffer->u.playerinfo[i].node = 255; + continue; + } + if (!playeringame[i]) { netbuffer->u.playerinfo[i].node = 255; // This slot is empty. @@ -1482,7 +1488,7 @@ static void SV_SendPlayerInfo(INT32 node) netbuffer->u.playerinfo[i].data |= 0x80; } - HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); + HSendPacket(node, false, 0, sizeof(plrinfo) * MSCOMPAT_MAXPLAYERS); } /** Sends a PT_SERVERCFG packet diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 53fc09c94..87147aeb8 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -470,7 +470,7 @@ typedef struct serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) askinfo_pak askinfo; // 61 bytes msaskinfo_pak msaskinfo; // 22 bytes - plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?) + plrinfo playerinfo[MSCOMPAT_MAXPLAYERS];// 576 bytes(?) plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) INT32 filesneedednum; // 4 bytes filesneededconfig_pak filesneededcfg; // ??? bytes diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 5ee13b888..37d91a765 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -329,6 +329,7 @@ consvar_t cv_1up = {"tv_1up", "5", CV_NETVAR|CV_CHEAT, chanc consvar_t cv_eggmanbox = {"tv_eggman", "5", CV_NETVAR|CV_CHEAT, chances_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};*/ // SRB2kart +consvar_t cv_superring = {"superring", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_sneaker = {"sneaker", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_rocketsneaker = {"rocketsneaker", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_invincibility = {"invincibility", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -366,7 +367,7 @@ consvar_t cv_kartcomeback = {"kartcomeback", "On", CV_NETVAR|CV_CHEAT|CV_CALL|CV consvar_t cv_kartencore = {"kartencore", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Kilometers"}, {2, "Miles"}, {3, "Fracunits"}, {0, NULL}}; +static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Percentage"}, {2, "Kilometers"}, {3, "Miles"}, {4, "Fracunits"}, {0, NULL}}; consvar_t cv_kartspeedometer = {"kartdisplayspeed", "Off", CV_SAVE, kartspeedometer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; consvar_t cv_kartvoices = {"kartvoices", "Tasteful", CV_SAVE, kartvoices_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -2083,7 +2084,7 @@ static void Command_View_f(void) return; } - displayplayerp = &displayplayers[viewnum]; + displayplayerp = &displayplayers[viewnum-1]; if (COM_Argc() > 1)/* switch to player */ { diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 96b747c1b..0b88d2bc8 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -101,7 +101,7 @@ extern consvar_t cv_1up, cv_eggmanbox; extern consvar_t cv_recycler;*/ // SRB2kart items -extern consvar_t cv_sneaker, cv_rocketsneaker, cv_invincibility, cv_banana; +extern consvar_t cv_superring, cv_sneaker, cv_rocketsneaker, cv_invincibility, cv_banana; extern consvar_t cv_eggmanmonitor, cv_orbinaut, cv_jawz, cv_mine; extern consvar_t cv_ballhog, cv_selfpropelledbomb, cv_grow, cv_shrink; extern consvar_t cv_thundershield, cv_hyudoro, cv_pogospring, cv_kitchensink; diff --git a/src/d_netfil.c b/src/d_netfil.c index 0b9dc9126..155700807 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -130,7 +130,8 @@ UINT8 *PutFileNeeded(UINT16 firstfile) nameonly(strcpy(wadfilename, wadfiles[i]->filename)); - if (p + 1 + 4 + strlen(wadfilename) + 16 > p_start + MAXFILENEEDED) + // Look below at the WRITE macros to understand what these numbers mean. + if (p + 1 + 4 + min(strlen(wadfilename) + 1, MAX_WADPATH) + 16 > p_start + MAXFILENEEDED) { // Too many files to send all at once if (netbuffer->packettype == PT_MOREFILESNEEDED) diff --git a/src/d_player.h b/src/d_player.h index 114674ff5..d2ed296ce 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -216,6 +216,7 @@ typedef enum KITEM_THUNDERSHIELD, KITEM_HYUDORO, KITEM_POGOSPRING, + KITEM_SUPERRING, KITEM_KITCHENSINK, NUMKARTITEMS, @@ -234,6 +235,7 @@ typedef enum //{ SRB2kart - kartstuff typedef enum { + // TODO: Kill this giant array. Add them as actual player_t variables, or condense related timers into their own, smaller arrays. // Basic gameplay things k_position, // Used for Kart positions, mostly for deterministic stuff k_oldposition, // Used for taunting when you pass someone @@ -247,16 +249,10 @@ typedef enum k_dropdash, // Charge up for respawn Drop Dash k_throwdir, // Held dir of controls; 1 = forward, 0 = none, -1 = backward (was "player->heldDir") - k_lapanimation, // Used to show the lap start wing logo animation - k_laphand, // Lap hand gfx to use; 0 = none, 1 = :ok_hand:, 2 = :thumbs_up:, 3 = :thumps_down: - k_cardanimation, // Used to determine the position of some full-screen Battle Mode graphics - k_voices, // Used to stop the player saying more voices than it should - k_tauntvoices, // Used to specifically stop taunt voice spam k_instashield, // Instashield no-damage animation timer - k_enginesnd, // Engine sound number you're on. k_floorboost, // Prevents Sneaker sounds for a breif duration when triggered by a floor panel - k_spinouttype, // Determines whether to thrust forward or not while spinning out; 0 = move forwards, 1 = stay still + k_spinouttype, // Determines whether to thrust forward or not while spinning out; 0 = move forwards, 1 = stay still, 2 = stay still & no flashing tics k_drift, // Drifting Left or Right, plus a bigger counter = sharper turn k_driftend, // Drift has ended, used to adjust character angle after drift @@ -264,19 +260,27 @@ typedef enum k_driftboost, // Boost you get from drifting k_boostcharge, // Charge-up for boosting at the start of the race k_startboost, // Boost you get from start of race or respawn drop dash + k_rings, // Number of held rings + k_pickuprings, // Number of rings being picked up before added to the counter (prevents rings from being deleted forever over 20) + k_userings, // Have to be not holding the item button to change from using rings to using items (or vice versa), to prevent some weirdness with the button + k_ringdelay, // 3 tic delay between every ring usage + k_ringboost, // Ring boost timer + k_ringlock, // Prevent picking up rings while SPB is locked on + k_sparkleanim, // Angle offset for ring sparkle animation 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_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 k_boostpower, // Base boost value, for offroad k_speedboost, // Boost value smoothing for max speed k_accelboost, // Boost value smoothing for acceleration + 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 k_boostangle, // angle set when not spun out OR boosted to determine what direction you should keep going at if you're spun out and boosted. - k_boostcam, // Camera push forward on boost - k_destboostcam, // Ditto - k_timeovercam, // Camera timer for leaving behind or not k_aizdriftstrat, // Let go of your drift while boosting? Helper for the SICK STRATZ you have just unlocked k_brakedrift, // Helper for brake-drift spark spawning @@ -294,7 +298,9 @@ typedef enum k_hyudorotimer, // Duration of the Hyudoro offroad effect itself k_stealingtimer, // You are stealing an item, this is your timer k_stolentimer, // You are being stolen from, this is your timer + k_superring, // Spawn rings on top of you every tic! k_sneakertimer, // Duration of the Sneaker Boost itself + k_levelbooster, // Duration of a level booster's boost (same as sneaker, but separated for ) k_growshrinktimer, // > 0 = Big, < 0 = small k_squishedtimer, // Squished frame timer k_rocketsneakertimer, // Rocket Sneaker duration timer @@ -315,11 +321,8 @@ typedef enum k_comebackpoints, // Number of times you've bombed or gave an item to someone; once it's 3 it gets set back to 0 and you're given a bumper k_comebackmode, // 0 = bomb, 1 = item k_wanted, // Timer for determining WANTED status, lowers when hitting people, prevents the game turning into Camp Lazlo - k_yougotem, // "You Got Em" gfx when hitting someone as a karma player via a method that gets you back in the game instantly // v1.0.2+ vars - k_itemblink, // Item flashing after roulette, prevents Hyudoro stealing AND serves as a mashing indicator - k_itemblinkmode, // Type of flashing: 0 = white (normal), 1 = red (mashing), 2 = rainbow (enhanced items) k_getsparks, // Disable drift sparks at low speed, JUST enough to give acceleration the actual headstart above speed k_jawztargetdelay, // Delay for Jawz target switching, to make it less twitchy k_spectatewait, // How long have you been waiting as a spectator @@ -327,6 +330,44 @@ typedef enum NUMKARTSTUFF } kartstufftype_t; + +typedef enum +{ + // Unsynced, HUD or clientsided effects + // Item box + khud_itemblink, // Item flashing after roulette, prevents Hyudoro stealing AND serves as a mashing indicator + khud_itemblinkmode, // Type of flashing: 0 = white (normal), 1 = red (mashing), 2 = rainbow (enhanced items) + + // Rings + khud_ringframe, // Ring spin frame + khud_ringtics, // Tics left until next ring frame + khud_ringdelay, // Next frame's tics + khud_ringspblock, // Which frame of the SPB ring lock animation to use + + // Lap finish + 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: + + // Camera + khud_boostcam, // Camera push forward on boost + khud_destboostcam, // Ditto + khud_timeovercam, // Camera timer for leaving behind or not + + // Sounds + khud_enginesnd, // Engine sound offset this player is using. + khud_voices, // Used to stop the player saying more voices than it should + khud_tauntvoices, // Used to specifically stop taunt voice spam + + // Battle + khud_cardanimation, // Used to determine the position of some full-screen Battle Mode graphics + khud_yougotem, // "You Got Em" gfx when hitting someone as a karma player via a method that gets you back in the game instantly + + NUMKARTHUD +} karthudtype_t; + +// QUICKLY GET EITHER SNEAKER OR LEVEL BOOSTER SINCE THEY ARE FUNCTIONALLY IDENTICAL +#define EITHERSNEAKER(p) (p->kartstuff[k_sneakertimer] || p->kartstuff[k_levelbooster]) + //} #define WEP_AUTO 1 @@ -387,6 +428,7 @@ typedef struct player_s // SRB2kart stuff INT32 kartstuff[NUMKARTSTUFF]; + INT32 karthud[NUMKARTHUD]; angle_t frameangle; // for the player add the ability to have the sprite only face other angles INT16 lturn_max[MAXPREDICTTICS]; // What's the expected turn value for full-left for a number of frames back (to account for netgame latency)? INT16 rturn_max[MAXPREDICTTICS]; // Ditto but for full-right diff --git a/src/dehacked.c b/src/dehacked.c index 73f408868..383183a0f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -4809,6 +4809,18 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit // Ring "S_RING", + "S_FASTRING1", + "S_FASTRING2", + "S_FASTRING3", + "S_FASTRING4", + "S_FASTRING5", + "S_FASTRING6", + "S_FASTRING7", + "S_FASTRING8", + "S_FASTRING9", + "S_FASTRING10", + "S_FASTRING11", + "S_FASTRING12", // Blue Sphere for special stages "S_BLUEBALL", @@ -7177,6 +7189,29 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_OPAQUESMOKE4", "S_OPAQUESMOKE5", + "S_RINGDEBT", + "S_RINGSPARKS1", + "S_RINGSPARKS2", + "S_RINGSPARKS3", + "S_RINGSPARKS4", + "S_RINGSPARKS5", + "S_RINGSPARKS6", + "S_RINGSPARKS7", + "S_RINGSPARKS8", + "S_RINGSPARKS9", + "S_RINGSPARKS10", + "S_RINGSPARKS11", + "S_RINGSPARKS12", + "S_RINGSPARKS13", + "S_RINGSPARKS14", + "S_RINGSPARKS15", + + "S_DRAFTDUST1", + "S_DRAFTDUST2", + "S_DRAFTDUST3", + "S_DRAFTDUST4", + "S_DRAFTDUST5", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -7963,6 +7998,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_LIONMAN", "MT_KARMAFIREWORK", + "MT_RINGSPARKS", + "MT_DRAFTDUST", #ifdef SEENAMES "MT_NAMECHECK", @@ -8377,13 +8414,7 @@ static const char *const KARTSTUFF_LIST[] = { "DROPDASH", "THROWDIR", - "LAPANIMATION", - "LAPHAND", - "CARDANIMATION", - "VOICES", - "TAUNTVOICES", "INSTASHIELD", - "ENGINESND", "FLOORBOOST", "SPINOUTTYPE", @@ -8394,19 +8425,27 @@ static const char *const KARTSTUFF_LIST[] = { "DRIFTBOOST", "BOOSTCHARGE", "STARTBOOST", + "RINGS", + "PICKUPRINGS", + "USERINGS", + "RINGDELAY", + "RINGBOOST", + "RINGLOCK", + "SPARKLEANIM", "JMP", "OFFROAD", "POGOSPRING", "BRAKESTOP", "WATERSKIP", "DASHPADCOOLDOWN", + "NUMBOOSTS", "BOOSTPOWER", "SPEEDBOOST", "ACCELBOOST", + "DRAFTPOWER", + "DRAFTLEEWAY", + "LASTDRAFT", "BOOSTANGLE", - "BOOSTCAM", - "DESTBOOSTCAM", - "TIMEOVERCAM", "AIZDRIFTSTRAT", "BRAKEDRIFT", @@ -8421,7 +8460,9 @@ static const char *const KARTSTUFF_LIST[] = { "HYUDOROTIMER", "STEALINGTIMER", "STOLENTIMER", + "SUPERRING", "SNEAKERTIMER", + "LEVELBOOSTER", "GROWSHRINKTIMER", "SQUISHEDTIMER", "ROCKETSNEAKERTIMER", @@ -8441,10 +8482,7 @@ static const char *const KARTSTUFF_LIST[] = { "COMEBACKPOINTS", "COMEBACKMODE", "WANTED", - "YOUGOTEM", - "ITEMBLINK", - "ITEMBLINKMODE", "GETSPARKS", "JAWZTARGETDELAY", "SPECTATEWAIT", @@ -8542,9 +8580,13 @@ struct { // Frame settings {"FF_FRAMEMASK",FF_FRAMEMASK}, - {"FF_PAPERSPRITE",FF_PAPERSPRITE}, {"FF_ANIMATE",FF_ANIMATE}, + {"FF_RANDOMANIM",FF_RANDOMANIM}, + {"FF_GLOBALANIM",FF_GLOBALANIM}, {"FF_FULLBRIGHT",FF_FULLBRIGHT}, + {"FF_SEMIBRIGHT",FF_SEMIBRIGHT}, + {"FF_PAPERSPRITE",FF_PAPERSPRITE}, + {"FF_VERTICALFLIP",FF_VERTICALFLIP}, {"FF_TRANSMASK",FF_TRANSMASK}, {"FF_TRANSSHIFT",FF_TRANSSHIFT}, // new preshifted translucency (used in source) @@ -8969,6 +9011,7 @@ struct { {"KITEM_THUNDERSHIELD",KITEM_THUNDERSHIELD}, {"KITEM_HYUDORO",KITEM_HYUDORO}, {"KITEM_POGOSPRING",KITEM_POGOSPRING}, + {"KITEM_SUPERRING",KITEM_SUPERRING}, {"KITEM_KITCHENSINK",KITEM_KITCHENSINK}, {"NUMKARTITEMS",NUMKARTITEMS}, {"KRITEM_TRIPLESNEAKER",KRITEM_TRIPLESNEAKER}, // Additional roulette IDs (not usable for much in Lua besides K_GetItemPatch) @@ -9362,7 +9405,7 @@ fixed_t get_number(const char *word) void DEH_Check(void) { -#if defined(_DEBUG) || defined(PARANOIA) +//#if defined(_DEBUG) || defined(PARANOIA) const size_t dehstates = sizeof(STATE_LIST)/sizeof(const char*); const size_t dehmobjs = sizeof(MOBJTYPE_LIST)/sizeof(const char*); const size_t dehpowers = sizeof(POWERS_LIST)/sizeof(const char*); @@ -9383,7 +9426,7 @@ void DEH_Check(void) if (dehcolors != MAXTRANSLATIONS) I_Error("You forgot to update the Dehacked colors list, you dolt!\n(%d colors defined, versus %s in the Dehacked list)\n", MAXTRANSLATIONS, sizeu1(dehcolors)); -#endif +//#endif } #ifdef HAVE_BLUA diff --git a/src/doomdef.h b/src/doomdef.h index ba3810858..e1e9ab13d 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -149,10 +149,10 @@ extern FILE *logstream; // most interface strings are ignored in development mode. // we use comprevision and compbranch instead. #else -#define VERSION 110 // Game version +#define VERSION 200 // Game version #define SUBVERSION 0 // more precise version number -#define VERSIONSTRING "v1.1" -#define VERSIONSTRINGW L"v1.1" +#define VERSIONSTRING "v2.0" +#define VERSIONSTRINGW L"v2.0" // 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! @@ -160,7 +160,7 @@ extern FILE *logstream; // Does this version require an added patch file? // Comment or uncomment this as necessary. -//#define USE_PATCH_FILE +#define USE_PATCH_FILE // Use .kart extension addons #define USE_KART @@ -219,7 +219,7 @@ extern FILE *logstream; // it's only for detection of the version the player is using so the MS can alert them of an update. // Only set it higher, not lower, obviously. // Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1". -#define MODVERSION 5 +#define MODVERSION 6 // Filter consvars by version // To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically. @@ -246,6 +246,9 @@ extern FILE *logstream; #define PLAYERSMASK (MAXPLAYERS-1) #define MAXPLAYERNAME 21 +// Master Server compatibility ONLY +#define MSCOMPAT_MAXPLAYERS (32) + typedef enum { SKINCOLOR_NONE = 0, diff --git a/src/g_game.c b/src/g_game.c index 3c5cb33f6..b07b764ec 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1411,7 +1411,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { // forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward. axis = JoyAxis(AXISMOVE, ssplayer); - if (InputDown(gc_accelerate, ssplayer) || (gamepadjoystickmove && axis > 0) || player->kartstuff[k_sneakertimer]) + if (InputDown(gc_accelerate, ssplayer) || (gamepadjoystickmove && axis > 0) || EITHERSNEAKER(player)) { cmd->buttons |= BT_ACCELERATE; forward = forwardmove[1]; // 50 @@ -1560,10 +1560,10 @@ 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 + if ((player->mo && player->speed > 0) // Moving || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn || (player->kartstuff[k_respawn]) // Respawning - || (player->spectator || objectplacing))) // Not a physical player + || (player->spectator || objectplacing)) // Not a physical player lang += (cmd->angleturn<<16); cmd->angleturn = (INT16)(lang >> 16); @@ -2585,6 +2585,7 @@ void G_PlayerReborn(INT32 player) INT32 bumper; INT32 comebackpoints; INT32 wanted; + INT32 rings; INT32 respawnflip; boolean songcredit = false; @@ -2636,6 +2637,7 @@ void G_PlayerReborn(INT32 player) itemamount = 0; growshrinktimer = 0; bumper = (G_BattleGametype() ? cv_kartbumpers.value : 0); + rings = (G_BattleGametype() ? 0 : 5); comebackpoints = 0; wanted = 0; starpostwp = 0; @@ -2665,6 +2667,7 @@ void G_PlayerReborn(INT32 player) growshrinktimer = 0; bumper = players[player].kartstuff[k_bumper]; + rings = players[player].kartstuff[k_rings]; comebackpoints = players[player].kartstuff[k_comebackpoints]; wanted = players[player].kartstuff[k_wanted]; } @@ -2709,17 +2712,19 @@ void G_PlayerReborn(INT32 player) p->pity = pity; // SRB2kart - p->kartstuff[k_starpostwp] = starpostwp; // TODO: get these out of kartstuff, it causes desync + p->kartstuff[k_starpostwp] = starpostwp; // TODO: get these out of kartstuff, it causes desync (Does it...?) p->kartstuff[k_itemroulette] = itemroulette; p->kartstuff[k_roulettetype] = roulettetype; p->kartstuff[k_itemtype] = itemtype; p->kartstuff[k_itemamount] = itemamount; p->kartstuff[k_growshrinktimer] = growshrinktimer; p->kartstuff[k_bumper] = bumper; + p->kartstuff[k_rings] = rings; p->kartstuff[k_comebackpoints] = comebackpoints; p->kartstuff[k_comebacktimer] = comebacktime; p->kartstuff[k_wanted] = wanted; p->kartstuff[k_eggmanblame] = -1; + p->kartstuff[k_lastdraft] = -1; p->kartstuff[k_starpostflip] = respawnflip; // Don't do anything immediately @@ -5143,7 +5148,8 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn || (players[displayplayers[0]].kartstuff[k_respawn]) // 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 + && !(players[displayplayers[0]].kartstuff[k_spinouttimer] + && (players[displayplayers[0]].kartstuff[k_sneakertimer] || players[displayplayers[0]].kartstuff[k_levelbooster]))) // Spinning and boosting cancels out spinout localangle[0] += (cmd->angleturn<<16); if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) @@ -5391,7 +5397,7 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) } // Store the sprite frame. - frame = ghost->frame & 0xFF; + frame = ghost->frame & FF_FRAMEMASK; if (frame != oldghost[playernum].frame) { oldghost[playernum].frame = frame; @@ -6045,7 +6051,7 @@ void G_ConfirmRewind(tic_t rewindtime) G_DoPlayDemo(NULL); // Restart the current demo - for (j = 0; j < rewindtime && leveltime < rewindtime; i++) + for (j = 0; j < rewindtime && leveltime < rewindtime; j++) { //TryRunTics(1); G_Ticker((j % NEWTICRATERATIO) == 0); @@ -6488,10 +6494,10 @@ void G_WriteStanding(UINT8 ranking, char *name, INT32 skinnum, UINT8 color, UINT { char temp[16]; - if (demoinfo_p && (UINT32)(*demoinfo_p) == 0) + if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) { WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - WRITEUINT32(demoinfo_p, demo_p - demobuffer); + *(UINT32 *)demoinfo_p = demo_p - demobuffer; } WRITEUINT8(demo_p, DW_STANDING); @@ -7946,16 +7952,17 @@ boolean G_CheckDemoStatus(void) void G_SaveDemo(void) { - UINT8 *p = demobuffer+16; // checksum position + UINT8 *p = demobuffer+16; // after version + UINT32 length; #ifdef NOMD5 UINT8 i; #endif // Ensure extrainfo pointer is always available, even if no info is present. - if (demoinfo_p && (UINT32)(*demoinfo_p) == 0) + if (demoinfo_p && *(UINT32 *)demoinfo_p == 0) { WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - WRITEUINT32(demoinfo_p, (UINT32)(demo_p - demobuffer)); + *(UINT32 *)demoinfo_p = demo_p - demobuffer; } WRITEUINT8(demo_p, DW_END); // Mark end of demo extra data. @@ -8001,12 +8008,14 @@ void G_SaveDemo(void) sprintf(writepoint, "%s.lmp", demo_slug); } + length = *(UINT32 *)demoinfo_p; + WRITEUINT32(demoinfo_p, length); #ifdef NOMD5 for (i = 0; i < 16; i++, p++) *p = M_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. #else // Make a checksum of everything after the checksum in the file up to the end of the standard data. Extrainfo is freely modifiable. - md5_buffer((char *)p+16, (demobuffer + (UINT32)*demoinfo_p) - (p+16), p); + md5_buffer((char *)p+16, (demobuffer + length) - (p+16), p); #endif diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 63cde0ca0..0d024dc65 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -1319,7 +1319,7 @@ static void HWR_SplitWall(sector_t *sector, wallVert3D *wallVerts, INT32 texnum, // HWR_DrawSkyWalls // Draw walls into the depth buffer so that anything behind is culled properly -static void HWR_DrawSkyWall(wallVert3D *wallVerts, FSurfaceInfo *Surf, fixed_t bottom, fixed_t top) +static void HWR_DrawSkyWall(wallVert3D *wallVerts, FSurfaceInfo *Surf) { HWD.pfnSetTexture(NULL); // no texture @@ -1327,9 +1327,6 @@ static void HWR_DrawSkyWall(wallVert3D *wallVerts, FSurfaceInfo *Surf, fixed_t b wallVerts[0].t = wallVerts[1].t = 0; wallVerts[0].s = wallVerts[3].s = 0; wallVerts[2].s = wallVerts[1].s = 0; - // set top/bottom coords - wallVerts[2].y = wallVerts[3].y = FIXED_TO_FLOAT(top); // No real way to find the correct height of this - wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(bottom); // worldlow/bottom because it needs to cover up the lower thok barrier wall HWR_ProjectWall(wallVerts, Surf, PF_Invisible|PF_Clip|PF_NoTexture, 255, NULL); // PF_Invisible so it's not drawn into the colour buffer // PF_NoTexture for no texture @@ -1462,6 +1459,111 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) worldlow = gr_backsector->floorheight; #endif + // Sky culling + if (!gr_curline->polyseg) // Don't do it for polyobjects + { + // Sky Ceilings + wallVerts[3].y = wallVerts[2].y = FIXED_TO_FLOAT(INT32_MAX); + + if (gr_frontsector->ceilingpic == skyflatnum) + { + if (gr_backsector->ceilingpic == skyflatnum) + { + // Both front and back sectors are sky, needs skywall from the frontsector's ceiling, but only if the + // backsector is lower + if ((worldhigh <= worldtop) +#ifdef ESLOPE + && (worldhighslope <= worldtopslope) +#endif + ) + { +#ifdef ESLOPE + wallVerts[0].y = FIXED_TO_FLOAT(worldhigh); + wallVerts[1].y = FIXED_TO_FLOAT(worldhighslope); +#else + wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(worldhigh); +#endif + HWR_DrawSkyWall(wallVerts, &Surf); + } + } + else + { + // Only the frontsector is sky, just draw a skywall from the front ceiling +#ifdef ESLOPE + wallVerts[0].y = FIXED_TO_FLOAT(worldtop); + wallVerts[1].y = FIXED_TO_FLOAT(worldtopslope); +#else + wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(worldtop); +#endif + HWR_DrawSkyWall(wallVerts, &Surf); + } + } + else if (gr_backsector->ceilingpic == skyflatnum) + { + // Only the backsector is sky, just draw a skywall from the front ceiling +#ifdef ESLOPE + wallVerts[0].y = FIXED_TO_FLOAT(worldtop); + wallVerts[1].y = FIXED_TO_FLOAT(worldtopslope); +#else + wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(worldtop); +#endif + HWR_DrawSkyWall(wallVerts, &Surf); + } + + + // Sky Floors + wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(INT32_MIN); + + if (gr_frontsector->floorpic == skyflatnum) + { + if (gr_backsector->floorpic == skyflatnum) + { + // Both front and back sectors are sky, needs skywall from the backsector's floor, but only if the + // it's higher, also needs to check for bottomtexture as the floors don't usually move down + // when both sides are sky floors + if ((worldlow >= worldbottom) +#ifdef ESLOPE + && (worldlowslope >= worldbottomslope) +#endif + && !(gr_sidedef->bottomtexture)) + { +#ifdef ESLOPE + wallVerts[3].y = FIXED_TO_FLOAT(worldlow); + wallVerts[2].y = FIXED_TO_FLOAT(worldlowslope); +#else + wallVerts[3].y = wallVerts[2].y = FIXED_TO_FLOAT(worldlow); +#endif + + HWR_DrawSkyWall(wallVerts, &Surf); + } + } + else + { + // Only the backsector has sky, just draw a skywall from the back floor +#ifdef ESLOPE + wallVerts[3].y = FIXED_TO_FLOAT(worldbottom); + wallVerts[2].y = FIXED_TO_FLOAT(worldbottomslope); +#else + wallVerts[3].y = wallVerts[2].y = FIXED_TO_FLOAT(worldbottom); +#endif + + HWR_DrawSkyWall(wallVerts, &Surf); + } + } + else if ((gr_backsector->floorpic == skyflatnum) && !(gr_sidedef->bottomtexture)) + { + // Only the backsector has sky, just draw a skywall from the back floor if there's no bottomtexture +#ifdef ESLOPE + wallVerts[3].y = FIXED_TO_FLOAT(worldlow); + wallVerts[2].y = FIXED_TO_FLOAT(worldlowslope); +#else + wallVerts[3].y = wallVerts[2].y = FIXED_TO_FLOAT(worldlow); +#endif + + HWR_DrawSkyWall(wallVerts, &Surf); + } + } + // hack to allow height changes in outdoor areas // This is what gets rid of the upper textures if there should be sky if (gr_frontsector->ceilingpic == skyflatnum && @@ -1914,85 +2016,6 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) Surf.FlatColor.rgba = 0xffffffff; }*/ } - - // Isn't this just the most lovely mess - if (!gr_curline->polyseg) // Don't do it for polyobjects - { - if (gr_frontsector->ceilingpic == skyflatnum || gr_backsector->ceilingpic == skyflatnum) - { - fixed_t depthwallheight; - - if (!gr_sidedef->toptexture || (gr_frontsector->ceilingpic == skyflatnum && gr_backsector->ceilingpic == skyflatnum)) // when both sectors are sky, the top texture isn't drawn - depthwallheight = gr_frontsector->ceilingheight < gr_backsector->ceilingheight ? gr_frontsector->ceilingheight : gr_backsector->ceilingheight; - else - depthwallheight = gr_frontsector->ceilingheight > gr_backsector->ceilingheight ? gr_frontsector->ceilingheight : gr_backsector->ceilingheight; - - if (gr_frontsector->ceilingheight-gr_frontsector->floorheight <= 0) // current sector is a thok barrier - { - if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is also a thok barrier - { - if (!gr_sidedef->bottomtexture) // Only extend further down if there's no texture - HWR_DrawSkyWall(wallVerts, &Surf, worldbottom < worldlow ? worldbottom : worldlow, INT32_MAX); - else - HWR_DrawSkyWall(wallVerts, &Surf, worldbottom > worldlow ? worldbottom : worldlow, INT32_MAX); - } - // behind sector is not a thok barrier - else if (gr_backsector->ceilingheight <= gr_frontsector->ceilingheight) // behind sector ceiling is lower or equal to current sector - HWR_DrawSkyWall(wallVerts, &Surf, depthwallheight, INT32_MAX); - // gr_front/backsector heights need to be used here because of the worldtop being set to worldhigh earlier on - } - else if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is a thok barrier, current sector is not - { - if (gr_backsector->ceilingheight >= gr_frontsector->ceilingheight // thok barrier ceiling height is equal to or greater than current sector ceiling height - || gr_backsector->floorheight <= gr_frontsector->floorheight // thok barrier ceiling height is equal to or less than current sector floor height - || gr_backsector->ceilingpic != skyflatnum) // thok barrier is not a sky - HWR_DrawSkyWall(wallVerts, &Surf, depthwallheight, INT32_MAX); - } - else // neither sectors are thok barriers - { - if ((gr_backsector->ceilingheight < gr_frontsector->ceilingheight && !gr_sidedef->toptexture) // no top texture and sector behind is lower - || gr_backsector->ceilingpic != skyflatnum) // behind sector is not a sky - HWR_DrawSkyWall(wallVerts, &Surf, depthwallheight, INT32_MAX); - } - } - // And now for sky floors! - if (gr_frontsector->floorpic == skyflatnum || gr_backsector->floorpic == skyflatnum) - { - fixed_t depthwallheight; - - if (!gr_sidedef->bottomtexture) - depthwallheight = worldbottom > worldlow ? worldbottom : worldlow; - else - depthwallheight = worldbottom < worldlow ? worldbottom : worldlow; - - if (gr_frontsector->ceilingheight-gr_frontsector->floorheight <= 0) // current sector is a thok barrier - { - if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is also a thok barrier - { - if (!gr_sidedef->toptexture) // Only extend up if there's no texture - HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, worldtop > worldhigh ? worldtop : worldhigh); - else - HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, worldtop < worldhigh ? worldtop : worldhigh); - } - // behind sector is not a thok barrier - else if (gr_backsector->floorheight >= gr_frontsector->floorheight) // behind sector floor is greater or equal to current sector - HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, depthwallheight); - } - else if (gr_backsector->ceilingheight-gr_backsector->floorheight <= 0) // behind sector is a thok barrier, current sector is not - { - if (gr_backsector->floorheight <= gr_frontsector->floorheight // thok barrier floor height is equal to or less than current sector floor height - || gr_backsector->ceilingheight >= gr_frontsector->ceilingheight // thok barrier floor height is equal to or greater than current sector ceiling height - || gr_backsector->floorpic != skyflatnum) // thok barrier is not a sky - HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, depthwallheight); - } - else // neither sectors are thok barriers - { - if ((gr_backsector->floorheight > gr_frontsector->floorheight && !gr_sidedef->bottomtexture) // no bottom texture and sector behind is higher - || gr_backsector->floorpic != skyflatnum) // behind sector is not a sky - HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, depthwallheight); - } - } - } } else { @@ -2060,13 +2083,52 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap); } } + else + { +#ifdef ESLOPE + //Set textures properly on single sided walls that are sloped + wallVerts[3].y = FIXED_TO_FLOAT(worldtop); + wallVerts[0].y = FIXED_TO_FLOAT(worldbottom); + wallVerts[2].y = FIXED_TO_FLOAT(worldtopslope); + wallVerts[1].y = FIXED_TO_FLOAT(worldbottomslope); +#else + // set top/bottom coords + wallVerts[2].y = wallVerts[3].y = FIXED_TO_FLOAT(worldtop); + wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(worldbottom); +#endif + // When there's no midtexture, draw a skywall to prevent rendering behind it + HWR_DrawSkyWall(wallVerts, &Surf); + } + + + // Single sided lines are simple for skywalls, just need to draw from the top or bottom of the sector if there's + // a sky flat if (!gr_curline->polyseg) { if (gr_frontsector->ceilingpic == skyflatnum) // It's a single-sided line with sky for its sector - HWR_DrawSkyWall(wallVerts, &Surf, worldtop, INT32_MAX); + { + wallVerts[3].y = wallVerts[2].y = FIXED_TO_FLOAT(INT32_MAX); +#ifdef ESLOPE + wallVerts[0].y = FIXED_TO_FLOAT(worldtop); + wallVerts[1].y = FIXED_TO_FLOAT(worldtopslope); +#else + wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(worldtop); +#endif + HWR_DrawSkyWall(wallVerts, &Surf); + } if (gr_frontsector->floorpic == skyflatnum) - HWR_DrawSkyWall(wallVerts, &Surf, INT32_MIN, worldbottom); + { +#ifdef ESLOPE + wallVerts[3].y = FIXED_TO_FLOAT(worldbottom); + wallVerts[2].y = FIXED_TO_FLOAT(worldbottomslope); +#else + wallVerts[3].y = wallVerts[2].y = FIXED_TO_FLOAT(worldbottom); +#endif + wallVerts[0].y = wallVerts[1].y = FIXED_TO_FLOAT(INT32_MIN); + + HWR_DrawSkyWall(wallVerts, &Surf); + } } } @@ -2381,7 +2443,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) // e6y: Check whether the player can look beyond this line // #ifdef NEWCLIP -boolean checkforemptylines = true; +static boolean checkforemptylines = true; // Don't modify anything here, just check // Kalaron: Modified for sloped linedefs static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacksector) @@ -2421,62 +2483,47 @@ static boolean CheckClip(seg_t * seg, sector_t * afrontsector, sector_t * abacks backc1 = backc2 = abacksector->ceilingheight; } - // now check for closed sectors! - if (backc1 <= frontf1 && backc2 <= frontf2) + if (viewsector != abacksector && viewsector != afrontsector) { - checkforemptylines = false; - if (!seg->sidedef->toptexture) - return false; + boolean mydoorclosed = false; // My door? Closed!? (doorclosed is actually otherwise unused in openGL) - if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum) - return false; - - return true; - } - - if (backf1 >= frontc1 && backf2 >= frontc2) - { - checkforemptylines = false; - if (!seg->sidedef->bottomtexture) - return false; - - // properly render skies (consider door "open" if both floors are sky): - if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum) - return false; - - return true; - } - - if (backc1 <= backf1 && backc2 <= backf2) - { - checkforemptylines = false; - // preserve a kind of transparent door/lift special effect: - if (backc1 < frontc1 || backc2 < frontc2) - { - if (!seg->sidedef->toptexture) - return false; - } - if (backf1 > frontf1 || backf2 > frontf2) - { - if (!seg->sidedef->bottomtexture) - return false; - } - if (abacksector->ceilingpic == skyflatnum && afrontsector->ceilingpic == skyflatnum) - return false; - - if (abacksector->floorpic == skyflatnum && afrontsector->floorpic == skyflatnum) - return false; - - return true; - } - - if (backc1 != frontc1 || backc2 != frontc2 - || backf1 != frontf1 || backf2 != frontf2) + // If the sector behind the line blocks all kinds of view past it + // (back ceiling is lower than close floor, or back floor is higher than close ceiling) + if ((backc1 <= frontf1 && backc2 <= frontf2) + || (backf1 >= frontc1 && backf2 >= frontc2)) { checkforemptylines = false; - return false; + return true; } + // The door is closed if: + // backsector is 0 height or less and + // back ceiling is higher than close ceiling or we need to render a top texture and + // back floor is lower than close floor or we need to render a bottom texture and + // neither front or back sectors are using the sky ceiling + mydoorclosed = (backc1 <= backf1 && backc2 <= backf2 + && ((backc1 >= frontc1 && backc2 >= frontc2) || seg->sidedef->toptexture) + && ((backf1 <= frontf1 && backf2 >= frontf2) || seg->sidedef->bottomtexture) + && (abacksector->ceilingpic != skyflatnum || afrontsector->ceilingpic != skyflatnum)); + + if (mydoorclosed) + { + checkforemptylines = false; + return true; + } + } + + // Window. + // We know it's a window when the above isn't true and the back and front sectors don't match + if (backc1 != frontc1 || backc2 != frontc2 + || backf1 != frontf1 || backf2 != frontf2) + { + checkforemptylines = false; + return false; + } + + // In this case we just need to check whether there is actually a need to render any lines, so checkforempty lines + // stays true return false; } #else @@ -4220,7 +4267,11 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t 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; @@ -4441,16 +4492,20 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) alpha = Surf.FlatColor.s.alpha; - // Start with the lightlevel and colormap from the top of the sprite - lightlevel = *list[sector->numlights - 1].lightlevel; - colormap = list[sector->numlights - 1].extra_colormap; - i = 0; temp = FLOAT_TO_FIXED(realtop); - if (spr->mobj->frame & FF_FULLBRIGHT) - lightlevel = 255; - #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)) + { + lightlevel = *list[sector->numlights - 1].lightlevel; + if (spr->mobj->frame & FF_SEMIBRIGHT) + lightlevel = 128 + (lightlevel>>1); + } + for (i = 1; i < sector->numlights; i++) { fixed_t h = sector->lightlist[i].slope ? P_GetZAt(sector->lightlist[i].slope, spr->mobj->x, spr->mobj->y) @@ -4458,7 +4513,11 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) if (h <= temp) { if (!(spr->mobj->frame & FF_FULLBRIGHT)) + { lightlevel = *list[i-1].lightlevel; + if (spr->mobj->frame & FF_SEMIBRIGHT) + lightlevel = 128 + (lightlevel>>1); + } colormap = list[i-1].extra_colormap; break; } @@ -4466,7 +4525,11 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) #else i = R_GetPlaneLight(sector, temp, false); if (!(spr->mobj->frame & FF_FULLBRIGHT)) + { lightlevel = *list[i].lightlevel; + if (spr->mobj->frame & FF_SEMIBRIGHT) + lightlevel = 128 + (lightlevel>>1); + } colormap = list[i].extra_colormap; #endif @@ -4482,7 +4545,11 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) if (!(list[i].flags & FF_NOSHADE) && (list[i].flags & FF_CUTSPRITES)) { if (!(spr->mobj->frame & FF_FULLBRIGHT)) + { lightlevel = *list[i].lightlevel; + if (spr->mobj->frame & FF_SEMIBRIGHT) + lightlevel = 128 + (lightlevel>>1); + } colormap = list[i].extra_colormap; } @@ -4775,7 +4842,11 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) extracolormap_t *colormap = sector->extra_colormap; if (!(spr->mobj->frame & FF_FULLBRIGHT)) + { lightlevel = sector->lightlevel; + if (spr->mobj->frame & FF_SEMIBRIGHT) + lightlevel = 128 + (lightlevel>>1); + } if (colormap) Surf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false); @@ -4886,6 +4957,9 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr) colormap = sector->extra_colormap; } + if (spr->mobj->frame & FF_SEMIBRIGHT) + lightlevel = 128 + (lightlevel>>1); + if (colormap) Surf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false); else @@ -5470,7 +5544,7 @@ static void HWR_AddSprites(sector_t *sec) } #ifdef HWPRECIP - // Someone seriously wants infinite draw distance for precipitation? + // No to infinite precipitation draw distance. if ((limit_dist = (fixed_t)cv_drawdist_precip.value << FRACBITS)) { for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) @@ -5486,13 +5560,6 @@ static void HWR_AddSprites(sector_t *sec) HWR_ProjectPrecipitationSprite(precipthing); } } - else - { - // Draw everything in sector, no checks - for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) - if (!(precipthing->precipflags & PCF_INVISIBLE)) - HWR_ProjectPrecipitationSprite(precipthing); - } #endif } @@ -5515,6 +5582,7 @@ static void HWR_ProjectSprite(mobj_t *thing) size_t lumpoff; unsigned rot; UINT8 flip; + boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP)); angle_t ang; INT32 heightsec, phs; const boolean papersprite = (thing->frame & FF_PAPERSPRITE); @@ -5646,7 +5714,7 @@ static void HWR_ProjectSprite(mobj_t *thing) x1 = tr_x + x1 * rightcos; x2 = tr_x - x2 * rightcos; - if (thing->eflags & MFE_VERTICALFLIP) + if (vflip) { gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale; gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale; @@ -5733,10 +5801,7 @@ static void HWR_ProjectSprite(mobj_t *thing) //CONS_Debug(DBG_RENDER, "------------------\nH: sprite : %d\nH: frame : %x\nH: type : %d\nH: sname : %s\n\n", // thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]); - if (thing->eflags & MFE_VERTICALFLIP) - vis->vflip = true; - else - vis->vflip = false; + vis->vflip = vflip; vis->precip = false; } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 7a912e010..38324dbd9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2386,7 +2386,7 @@ void HU_Drawer(void) }*/ // draw song credits - if (cv_songcredits.value) + if (cv_songcredits.value && !( hu_showscores && (netgame || multiplayer) )) HU_DrawSongCredits(); // draw desynch text diff --git a/src/i_tcp.c b/src/i_tcp.c index fb0e5852d..f58aa22bc 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -1311,7 +1311,7 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) int gaie; if (!port || !port[0]) - port = port_name; + port = DEFAULTPORT; DEBFILE(va("Creating new node: %s@%s\n", address, port)); diff --git a/src/info.c b/src/info.c index d609bf553..31f2f8348 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" + "MXCL","RGSP","DRAF","XMS4","XMS5","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -969,7 +969,19 @@ state_t states[NUMSTATES] = {SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|11, 1, {NULL}, 0, 0, S_MSSHIELD_F1}, // S_MSSHIELD_F12 // Ring - {SPR_RING, FF_ANIMATE, -1, {NULL}, 23, 1, S_RING}, // S_RING + {SPR_RING, FF_SEMIBRIGHT|FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_RING}, // S_RING + {SPR_RING, 0, 1, {NULL}, 0, 0, S_FASTRING2}, // S_FASTRING1 + {SPR_RING, 2, 1, {NULL}, 0, 0, S_FASTRING3}, // S_FASTRING2 + {SPR_RING, 4, 1, {NULL}, 0, 0, S_FASTRING4}, // S_FASTRING3 + {SPR_RING, 6, 1, {NULL}, 0, 0, S_FASTRING5}, // S_FASTRING4 + {SPR_RING, 8, 1, {NULL}, 0, 0, S_FASTRING6}, // S_FASTRING5 + {SPR_RING, 10, 1, {NULL}, 0, 0, S_FASTRING7}, // S_FASTRING6 + {SPR_RING, 12, 1, {NULL}, 0, 0, S_FASTRING8}, // S_FASTRING7 + {SPR_RING, 14, 1, {NULL}, 0, 0, S_FASTRING9}, // S_FASTRING8 + {SPR_RING, 16, 1, {NULL}, 0, 0, S_FASTRING10}, // S_FASTRING9 + {SPR_RING, 18, 1, {NULL}, 0, 0, S_FASTRING11}, // S_FASTRING10 + {SPR_RING, 20, 1, {NULL}, 0, 0, S_FASTRING12}, // S_FASTRING11 + {SPR_RING, 22, 1, {NULL}, 0, 0, S_FASTRING1}, // S_FASTRING12 // Blue Sphere Replacement for special stages {SPR_BBAL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUEBALL @@ -3401,6 +3413,30 @@ state_t states[NUMSTATES] = {SPR_SMOK, 3, 7, {NULL}, 0, 0, S_OPAQUESMOKE5}, // S_OPAQUESMOKE4 {SPR_SMOK, 4, 8, {NULL}, 0, 0, S_NULL}, // S_OPAQUESMOKE5 + {SPR_MXCL, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_RINGDEBT + + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_RINGSPARKS2}, // S_RINGSPARKS1 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_RINGSPARKS3}, // S_RINGSPARKS2 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|2, 1, {NULL}, 0, 0, S_RINGSPARKS4}, // S_RINGSPARKS3 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|3, 1, {NULL}, 0, 0, S_RINGSPARKS5}, // S_RINGSPARKS4 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_RINGSPARKS6}, // S_RINGSPARKS5 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|5, 1, {NULL}, 0, 0, S_RINGSPARKS7}, // S_RINGSPARKS6 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|6, 1, {NULL}, 0, 0, S_RINGSPARKS8}, // S_RINGSPARKS7 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|7, 1, {NULL}, 0, 0, S_RINGSPARKS9}, // S_RINGSPARKS8 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|8, 1, {NULL}, 0, 0, S_RINGSPARKS10}, // S_RINGSPARKS9 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|9, 1, {NULL}, 0, 0, S_RINGSPARKS11}, // S_RINGSPARKS10 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|10, 1, {NULL}, 0, 0, S_RINGSPARKS12}, // S_RINGSPARKS11 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|11, 1, {NULL}, 0, 0, S_RINGSPARKS13}, // S_RINGSPARKS12 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|12, 1, {NULL}, 0, 0, S_RINGSPARKS14}, // S_RINGSPARKS13 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|13, 1, {NULL}, 0, 0, S_RINGSPARKS15}, // S_RINGSPARKS14 + {SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT|14, 1, {NULL}, 0, 0, S_NULL}, // S_RINGSPARKS15 + + {SPR_DRAF, 0, 2, {NULL}, 0, 0, S_DRAFTDUST2}, // S_DRAFTDUST1 + {SPR_DRAF, 1, 1, {NULL}, 0, 0, S_DRAFTDUST3}, // S_DRAFTDUST2 + {SPR_DRAF, 2, 1, {NULL}, 0, 0, S_DRAFTDUST4}, // S_DRAFTDUST3 + {SPR_DRAF, 3, 1, {NULL}, 0, 0, S_DRAFTDUST5}, // S_DRAFTDUST4 + {SPR_DRAF, 4, 1, {NULL}, 0, 0, S_NULL}, // S_DRAFTDUST5 + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -5447,12 +5483,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_SPRK1, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate sfx_itemup, // deathsound 38*FRACUNIT, // speed - 16*FRACUNIT, // radius - 24*FRACUNIT, // height + 48*FRACUNIT, // radius + 48*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage @@ -5478,13 +5514,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_itemup, // deathsound 38*FRACUNIT, // speed - 15*FRACUNIT, // radius - 24*FRACUNIT, // height + 24*FRACUNIT, // radius + 48*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage sfx_None, // activesound - MF_SLIDEME|MF_SPECIAL, // flags + MF_SLIDEME|MF_BOUNCE|MF_SPECIAL|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -20059,6 +20095,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_RINGSPARKS + -1, // doomednum + S_RINGSPARKS1, // 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< 0) || (pexiting > 0) || (secondist/distvar < 3)) - && (pos != 9)) // Force SPB + if ((indirectitemcooldown > 0) || (pexiting > 0) || (secondist/DISTVAR < 3)) newodds = 0; else - newodds *= min((secondist/distvar)-4, 3); // POWERITEMODDS(newodds); + newodds *= min((secondist/DISTVAR)-4, 3); // POWERITEMODDS(newodds); break; case KITEM_SHRINK: if ((indirectitemcooldown > 0) || (pingame-1 <= pexiting) || COOLDOWNONSTART) @@ -898,7 +911,7 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp POWERITEMODDS(newodds); break; case KITEM_THUNDERSHIELD: - if (thunderisout) + if (thunderisout || spbplace != -1) newodds = 0; else POWERITEMODDS(newodds); @@ -917,16 +930,17 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp //{ SRB2kart Roulette Code - Distance Based, no waypoints -static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32 bestbumper, boolean spbrush, boolean dontforcespb) +static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pdis, INT32 bestbumper, boolean spbrush) { - const INT32 distvar = (64*14); INT32 i; - INT32 pdis = 0, useodds = 0; + INT32 n = 0; + INT32 useodds = 0; UINT8 disttable[14]; + UINT8 totallen = 0; UINT8 distlen = 0; - boolean oddsvalid[10]; + boolean oddsvalid[8]; - for (i = 0; i < 10; i++) + for (i = 0; i < 8; i++) { INT32 j; boolean available = false; @@ -949,27 +963,19 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT3 oddsvalid[i] = available; } - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator && players[i].mo - && players[i].kartstuff[k_position] < player->kartstuff[k_position]) - pdis += 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) / mapobjectscale - * (pingame - players[i].kartstuff[k_position]) - / max(1, ((pingame - 1) * (pingame + 1) / 3)); - } - #define SETUPDISTTABLE(odds, num) \ - for (i = num; i; --i) disttable[distlen++] = odds + if (oddsvalid[odds]) \ + for (i = num; i; --i) \ + disttable[distlen++] = odds; \ + totallen += num; if (G_BattleGametype()) // Battle Mode { - if (oddsvalid[0]) SETUPDISTTABLE(0,1); - if (oddsvalid[1]) SETUPDISTTABLE(1,1); - if (oddsvalid[2]) SETUPDISTTABLE(2,1); - if (oddsvalid[3]) SETUPDISTTABLE(3,1); - if (oddsvalid[4]) SETUPDISTTABLE(4,1); + 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; @@ -982,43 +988,30 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT3 wantedpos = 4; if (wantedpos < 0) // Don't go below somehow wantedpos = 0; - useodds = disttable[(wantedpos * distlen) / 5]; + n = (wantedpos * distlen) / totallen; + useodds = disttable[n]; } } else { - if (oddsvalid[1]) SETUPDISTTABLE(1,1); - if (oddsvalid[2]) SETUPDISTTABLE(2,1); - if (oddsvalid[3]) SETUPDISTTABLE(3,1); - if (oddsvalid[4]) SETUPDISTTABLE(4,2); - if (oddsvalid[5]) SETUPDISTTABLE(5,2); - if (oddsvalid[6]) SETUPDISTTABLE(6,3); - if (oddsvalid[7]) SETUPDISTTABLE(7,3); - if (oddsvalid[8]) SETUPDISTTABLE(8,1); + 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 (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items - pdis = (15 * pdis) / 14; - - if (spbrush) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell - pdis = (3 * pdis) / 2; - - pdis = ((28 + (8-pingame)) * pdis) / 28; - - if (pingame == 1 && oddsvalid[0]) // Record Attack, or just alone - useodds = 0; - else if (pdis <= 0) // (64*14) * 0 = 0 + if (pdis <= 0) // (64*14) * 0 = 0 useodds = disttable[0]; - else if (player->kartstuff[k_position] == 2 && pdis > (distvar*6) - && spbplace == -1 && !indirectitemcooldown && !dontforcespb - && oddsvalid[9]) // Force SPB in 2nd - useodds = 9; - else if (pdis > distvar * ((12 * distlen) / 14)) // (64*14) * 12 = 10752 + else if (pdis > DISTVAR * ((12 * distlen) / 14)) // (64*14) * 12 = 10752 useodds = disttable[distlen-1]; else { for (i = 1; i < 13; i++) { - if (pdis <= distvar * ((i * distlen) / 14)) + if (pdis <= DISTVAR * ((i * distlen) / 14)) { useodds = disttable[((i * distlen) / 14)]; break; @@ -1029,8 +1022,6 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT3 #undef SETUPDISTTABLE - //CONS_Printf("Got useodds %d. (position: %d, distance: %d)\n", useodds, player->kartstuff[k_position], pdis); - return useodds; } @@ -1039,6 +1030,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) INT32 i; UINT8 pingame = 0; UINT8 roulettestop; + INT32 pdis = 0; INT32 useodds = 0; INT32 spawnchance[NUMKARTRESULTS]; INT32 totalspawnchance = 0; @@ -1082,7 +1074,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) // 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_eggmanheld] || player->kartstuff[k_itemheld]) && player->kartstuff[k_itemroulette] >= roulettestop && !modeattacking) + 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; @@ -1093,11 +1086,35 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (cmd->buttons & BT_ATTACK) player->pflags |= PF_ATTACKDOWN; - if (player->kartstuff[k_roulettetype] == 2) // Fake items + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator && players[i].mo + && players[i].kartstuff[k_position] < player->kartstuff[k_position]) + pdis += 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) / mapobjectscale + * (pingame - players[i].kartstuff[k_position]) + / max(1, ((pingame - 1) * (pingame + 1) / 3)); + } + + 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; + } + + 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->kartstuff[k_itemblink] = TICRATE; - //player->kartstuff[k_itemblinkmode] = 1; + //player->karthud[khud_itemblink] = TICRATE; + //player->karthud[khud_itemblinkmode] = 1; player->kartstuff[k_itemroulette] = 0; player->kartstuff[k_roulettetype] = 0; if (P_IsDisplayPlayer(player)) @@ -1105,12 +1122,14 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) 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->kartstuff[k_itemblink] = TICRATE; - player->kartstuff[k_itemblinkmode] = 2; + player->karthud[khud_itemblink] = TICRATE; + player->karthud[khud_itemblinkmode] = 2; player->kartstuff[k_itemroulette] = 0; player->kartstuff[k_roulettetype] = 0; if (P_IsDisplayPlayer(player)) @@ -1118,15 +1137,98 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) return; } - if (G_RaceGametype()) - spbrush = (spbplace != -1 && player->kartstuff[k_position] == spbplace+1); + // 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_orbinaut.value) // Waited patiently? You get an orbinaut! + K_KartGetItemResult(player, KITEM_ORBINAUT); + 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; + } + + // 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, (mashed ? sfx_itrolm : sfx_itrolf)); + return; + } + } + + // SPECIAL CASE No. 5: + // Force SPB onto 2nd if they get too far behind + if (player->kartstuff[k_position] == 2 && pdis > (DISTVAR*6) + && 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, pingame, bestbumper, spbrush, dontforcespb); + useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); for (i = 1; i < NUMKARTRESULTS; i++) spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush)); @@ -1148,8 +1250,8 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) if (P_IsDisplayPlayer(player)) S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); - player->kartstuff[k_itemblink] = TICRATE; - player->kartstuff[k_itemblinkmode] = ((player->kartstuff[k_roulettetype] == 1) ? 2 : (mashed ? 1 : 0)); + 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 @@ -1207,6 +1309,34 @@ static fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against) return weight; } +// 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); + return; +} + void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) { mobj_t *fx; @@ -1356,10 +1486,22 @@ 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; + 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 + { + 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); } } @@ -1368,10 +1510,22 @@ 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; + 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 + { + 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); } } } @@ -1419,15 +1573,11 @@ static void K_UpdateOffroad(player_t *player) if (offroadstrength) { if (K_CheckOffroadCollide(player->mo, player->mo->subsector->sector) && player->kartstuff[k_offroad] == 0) - player->kartstuff[k_offroad] = (TICRATE/2); + player->kartstuff[k_offroad] = TICRATE; if (player->kartstuff[k_offroad] > 0) { - offroad = (offroadstrength << FRACBITS) / (TICRATE/2); - - //if (player->kartstuff[k_growshrinktimer] > 1) // grow slows down half as fast - // offroad /= 2; - + offroad = (offroadstrength << FRACBITS) / TICRATE; player->kartstuff[k_offroad] += offroad; } @@ -1438,6 +1588,255 @@ static void K_UpdateOffroad(player_t *player) 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 c = FixedMul(CHAOTIXBANDCOLORS<> FRACBITS; + UINT8 n = CHAOTIXBANDLEN; + UINT8 offset = ((leveltime / 3) % 3); + fixed_t stepx, stepy, stepz; + fixed_t curx, cury, curz; + + 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 + (abs(leveltime+offset) % 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; + + // 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) + 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) { @@ -1616,6 +2015,8 @@ void K_RespawnChecker(player_t *player) player->mo->colorized = false; player->kartstuff[k_dropdash] = 0; player->kartstuff[k_respawn] = 0; + //P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 3); } } } @@ -1678,8 +2079,8 @@ static void K_TauntVoiceTimers(player_t *player) if (!player) return; - player->kartstuff[k_tauntvoices] = 6*TICRATE; - player->kartstuff[k_voices] = 4*TICRATE; + player->karthud[khud_tauntvoices] = 6*TICRATE; + player->karthud[khud_voices] = 4*TICRATE; } static void K_RegularVoiceTimers(player_t *player) @@ -1687,16 +2088,16 @@ static void K_RegularVoiceTimers(player_t *player) if (!player) return; - player->kartstuff[k_voices] = 4*TICRATE; + player->karthud[khud_voices] = 4*TICRATE; - if (player->kartstuff[k_tauntvoices] < 4*TICRATE) - player->kartstuff[k_tauntvoices] = 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->kartstuff[k_tauntvoices]); + boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) S_StartSound(source, sfx_kattk1+pick); @@ -1710,7 +2111,7 @@ void K_PlayAttackTaunt(mobj_t *source) 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->kartstuff[k_tauntvoices]); + boolean tasteful = (!source->player || !source->player->karthud[khud_tauntvoices]); if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2)) S_StartSound(source, sfx_kbost1+pick); @@ -1723,7 +2124,7 @@ void K_PlayBoostTaunt(mobj_t *source) void K_PlayOvertakeSound(mobj_t *source) { - boolean tasteful = (!source->player || !source->player->kartstuff[k_voices]); + boolean tasteful = (!source->player || !source->player->karthud[khud_voices]); if (!G_RaceGametype()) // Only in race return; @@ -1741,6 +2142,16 @@ void K_PlayOvertakeSound(mobj_t *source) 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 (cv_kartvoices.value) @@ -1782,6 +2193,7 @@ 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 { @@ -1790,108 +2202,81 @@ static void K_GetKartBoostPower(player_t *player) } // Offroad is separate, it's difficult to factor it in with a variable value anyway. - if (!(player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer]) + if (!(player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || EITHERSNEAKER(player)) && player->kartstuff[k_offroad] >= 0) - boostpower = FixedDiv(boostpower, player->kartstuff[k_offroad] + FRACUNIT); + boostpower = FixedDiv(boostpower, FixedMul(player->kartstuff[k_offroad], K_GetKartGameSpeedScalar(gamespeed)) + FRACUNIT); if (player->kartstuff[k_bananadrag] > TICRATE) boostpower = (4*boostpower)/5; - // Banana drag/offroad dust - if (boostpower < FRACUNIT - && player->mo && P_IsObjectOnGround(player->mo) - && player->speed > 0 - && !player->spectator) - { - K_SpawnWipeoutTrail(player->mo, true); - if (leveltime % 6 == 0) - S_StartSound(player->mo, sfx_cdfm70); - } +#define ADDBOOST(s,a) { \ + numboosts++; \ + speedboost += (s) / numboosts; \ + accelboost += (a) / numboosts; \ +} + + if (player->kartstuff[k_levelbooster]) // Level boosters + ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration if (player->kartstuff[k_sneakertimer]) // Sneaker - { - switch (gamespeed) - { - case 0: - speedboost = max(speedboost, 53740+768); - break; - case 2: - speedboost = max(speedboost, 17294+768); - break; - default: - speedboost = max(speedboost, 32768); - break; - } - accelboost = max(accelboost, 8*FRACUNIT); // + 800% - } + ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration if (player->kartstuff[k_invincibilitytimer]) // Invincibility - { - speedboost = max(speedboost, 3*FRACUNIT/8); // + 37.5% - accelboost = max(accelboost, 3*FRACUNIT); // + 300% - } + ADDBOOST((3*FRACUNIT)/8, 3*FRACUNIT); // + 37.5% 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_growshrinktimer] > 0) // Grow { - speedboost = max(speedboost, FRACUNIT/5); // + 20% + // Grow's design is weird with booster stacking. + // We'll see how to replace its design BEFORE v2 gets released. + speedboost += (FRACUNIT/5); // + 20% + //numboosts++; // Don't add any boost afterimages to Grow } - if (player->kartstuff[k_driftboost]) // Drift Boost + if (player->kartstuff[k_draftpower] > 0) // Drafting { - speedboost = max(speedboost, FRACUNIT/4); // + 25% - accelboost = max(accelboost, 4*FRACUNIT); // + 400% + 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++; } - if (player->kartstuff[k_startboost]) // Startup Boost - { - speedboost = max(speedboost, FRACUNIT/4); // + 25% - accelboost = max(accelboost, 6*FRACUNIT); // + 300% - } - - // don't average them anymore, this would make a small boost and a high boost less useful - // just take the highest we want instead - 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_speedboost] += (speedboost - player->kartstuff[k_speedboost]) / (TICRATE/2); player->kartstuff[k_accelboost] = accelboost; + player->kartstuff[k_numboosts] = numboosts; } fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) { + const fixed_t xspd = (3*FRACUNIT)/64; + fixed_t g_cc = K_GetKartGameSpeedScalar(gamespeed) + xspd; fixed_t k_speed = 150; - fixed_t g_cc = FRACUNIT; - fixed_t xspd = 3072; // 4.6875 aka 3/64 UINT8 kartspeed = player->kartspeed; fixed_t finalspeed; - if (doboostpower && !player->kartstuff[k_pogospring] && !P_IsObjectOnGround(player->mo)) - return (75*mapobjectscale); // air speed cap - - switch (gamespeed) - { - case 0: - g_cc = 53248 + xspd; // 50cc = 81.25 + 4.69 = 85.94% - break; - case 2: - g_cc = 77824 + xspd; // 150cc = 118.75 + 4.69 = 123.44% - break; - default: - g_cc = 65536 + xspd; // 100cc = 100.00 + 4.69 = 104.69% - break; - } - if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) kartspeed = 1; k_speed += kartspeed*3; // 153 - 177 - finalspeed = FixedMul(FixedMul(k_speed<<14, g_cc), player->mo->scale); + finalspeed = FixedMul(k_speed<<14, g_cc); + + if (player->mo && !P_MobjWasRemoved(player->mo)) + finalspeed = FixedMul(finalspeed, player->mo->scale); if (doboostpower) return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]); @@ -1938,6 +2323,9 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove // 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_FRICTION); if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust @@ -1963,10 +2351,10 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove finalspeed *= forwardmove/25; finalspeed /= 2; - if (forwardmove < 0 && finalspeed > FRACUNIT*2) + if (forwardmove < 0 && finalspeed > mapobjectscale*2) return finalspeed/2; else if (forwardmove < 0) - return -FRACUNIT/2; + return -mapobjectscale/2; if (finalspeed < 0) finalspeed = 0; @@ -2036,7 +2424,6 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto (void)inflictor; // in case some weirdo doesn't want Lua. #endif - if (!trapitem && G_BattleGametype()) { if (K_IsPlayerWanted(player)) @@ -2048,7 +2435,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto if (player->health <= 0) return; - if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 + 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))) { @@ -2067,8 +2454,10 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto if (source && source != player->mo && source->player) K_PlayHitEmSound(source); - //player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_sneakertimer] = 0; + //player->kartstuff[k_levelbooster] = 0; player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; player->kartstuff[k_drift] = 0; player->kartstuff[k_driftcharge] = 0; @@ -2118,7 +2507,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto player->kartstuff[k_spinouttype] = type; - if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout + 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) @@ -2129,6 +2518,10 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto 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); @@ -2210,7 +2603,9 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) #endif player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_levelbooster] = 0; player->kartstuff[k_driftboost] = 0; + player->kartstuff[k_ringboost] = 0; player->kartstuff[k_drift] = 0; player->kartstuff[k_driftcharge] = 0; @@ -2273,6 +2668,8 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) 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) @@ -2311,8 +2708,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b if (player->health <= 0) return; - if (/*player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 // Explosions should combo, because of SPB and Eggman - ||*/player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0 + 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. @@ -2334,14 +2730,16 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b player->mo->momx = player->mo->momy = 0; player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_levelbooster] = 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)) + 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) { @@ -2398,8 +2796,10 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b P_SetPlayerMobjState(player->mo, S_KART_SPIN); P_PlayRinglossSound(player->mo); + P_PlayerRingBurst(player, 5); + K_PlayPainSound(player->mo); - if (P_IsLocalPlayer(player)) + if (P_IsDisplayPlayer(player)) { quake.intensity = 64*FRACUNIT; quake.time = 5; @@ -2432,7 +2832,7 @@ void K_StealBumper(player_t *player, player_t *victim, boolean force) 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 + 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); @@ -2570,14 +2970,13 @@ void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 // 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 i, radius, height; - mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING); K_MatchGenericExtraFlags(smoldering, source); - smoldering->tics = TICRATE*3; radius = source->radius>>FRACBITS; height = source->height>>FRACBITS; @@ -3000,6 +3399,82 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean 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 @@ -3096,29 +3571,10 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map // Figure out projectile speed by game speed if (missile && mapthing != MT_BALLHOG) // Trying to keep compatability... - { - PROJSPEED = mobjinfo[mapthing].speed; - if (gamespeed == 0) - PROJSPEED = FixedMul(PROJSPEED, FRACUNIT-FRACUNIT/4); - else if (gamespeed == 2) - PROJSPEED = FixedMul(PROJSPEED, FRACUNIT+FRACUNIT/4); - PROJSPEED = FixedMul(PROJSPEED, mapobjectscale); - } + PROJSPEED = FixedMul(mobjinfo[mapthing].speed, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); else - { - switch (gamespeed) - { - case 0: - PROJSPEED = 68*mapobjectscale; // Avg Speed is 34 - break; - case 2: - PROJSPEED = 96*mapobjectscale; // Avg Speed is 48 - break; - default: - PROJSPEED = 82*mapobjectscale; // Avg Speed is 41 - break; - } - } + PROJSPEED = (82 + ((gamespeed-1) * 14)) * FRACUNIT; // Avg Speed is 41 in Normal + PROJSPEED = FixedMul(PROJSPEED, mapobjectscale); if (altthrow) { @@ -3332,18 +3788,7 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter) if (!mine || P_MobjWasRemoved(mine)) return; - switch (gamespeed) - { - case 0: - spd = 68*mapobjectscale; // Avg Speed is 34 - break; - case 2: - spd = 96*mapobjectscale; // Avg Speed is 48 - break; - default: - spd = 82*mapobjectscale; // Avg Speed is 41 - break; - } + spd = (82 + ((gamespeed-1) * 14))*mapobjectscale; // Avg Speed is 41 in Normal mine->flags |= MF_NOCLIPTHING; @@ -3434,7 +3879,7 @@ static void K_DoHyudoroSteal(player_t *player) && (players[i].kartstuff[k_itemtype] && players[i].kartstuff[k_itemamount] && !players[i].kartstuff[k_itemheld] - && !players[i].kartstuff[k_itemblink])) + && !players[i].karthud[khud_itemblink])) { playerswappable[numplayers] = i; numplayers++; @@ -3483,37 +3928,24 @@ static void K_DoHyudoroSteal(player_t *player) players[stealplayer].kartstuff[k_itemamount] = 0; players[stealplayer].kartstuff[k_itemheld] = 0; - if (P_IsLocalPlayer(&players[stealplayer]) && !splitscreen) + if (P_IsDisplayPlayer(&players[stealplayer]) && !splitscreen) S_StartSound(NULL, sfx_s3k92); } } void K_DoSneaker(player_t *player, INT32 type) { - fixed_t intendedboost; - - switch (gamespeed) - { - case 0: - intendedboost = 53740+768; - break; - case 2: - intendedboost = 17294+768; - break; - default: - intendedboost = 32768; - break; - } + const fixed_t intendedboost = FRACUNIT/2; if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) { S_StartSound(player->mo, sfx_cdfm01); K_SpawnDashDustRelease(player); if (intendedboost > player->kartstuff[k_speedboost]) - player->kartstuff[k_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); + player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); } - if (!player->kartstuff[k_sneakertimer]) + if (!EITHERSNEAKER(player)) { if (type == 2) { @@ -3543,16 +3975,17 @@ void K_DoSneaker(player_t *player, INT32 type) } } - player->kartstuff[k_sneakertimer] = sneakertime; - - // set angle for spun out players: - player->kartstuff[k_boostangle] = (INT32)player->mo->angle; - if (type != 0) { player->pflags |= PF_ATTACKDOWN; K_PlayBoostTaunt(player->mo); + player->kartstuff[k_sneakertimer] = sneakertime; } + else + player->kartstuff[k_levelbooster] = sneakertime; + + // set angle for spun out players: + player->kartstuff[k_boostangle] = (INT32)player->mo->angle; } static void K_DoShrink(player_t *user) @@ -3582,7 +4015,7 @@ static void K_DoShrink(player_t *user) { // Start shrinking! K_DropItems(&players[i]); - players[i].kartstuff[k_growshrinktimer] = -(20*TICRATE); + players[i].kartstuff[k_growshrinktimer] = -(15*TICRATE); if (players[i].mo && !P_MobjWasRemoved(players[i].mo)) { @@ -3630,10 +4063,10 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) thrust = 72<player->kartstuff[k_pogospring] != 2) { - if (mo->player->kartstuff[k_sneakertimer]) - thrust = FixedMul(thrust, 5*FRACUNIT/4); + if (EITHERSNEAKER(mo->player)) + thrust = FixedMul(thrust, (5*FRACUNIT)/4); else if (mo->player->kartstuff[k_invincibilitytimer]) - thrust = FixedMul(thrust, 9*FRACUNIT/8); + thrust = FixedMul(thrust, (9*FRACUNIT)/8); } } else @@ -4317,7 +4750,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) // Silence the engines if (leveltime < 8 || player->spectator || player->exiting) { - player->kartstuff[k_enginesnd] = 0; // Reset sound number + player->karthud[khud_enginesnd] = 0; // Reset sound number return; } @@ -4338,15 +4771,15 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) if (targetsnd > 12) targetsnd = 12; - if (player->kartstuff[k_enginesnd] < targetsnd) - player->kartstuff[k_enginesnd]++; - if (player->kartstuff[k_enginesnd] > targetsnd) - player->kartstuff[k_enginesnd]--; + if (player->karthud[khud_enginesnd] < targetsnd) + player->karthud[khud_enginesnd]++; + if (player->karthud[khud_enginesnd] > targetsnd) + player->karthud[khud_enginesnd]--; - if (player->kartstuff[k_enginesnd] < 0) - player->kartstuff[k_enginesnd] = 0; - if (player->kartstuff[k_enginesnd] > 12) - player->kartstuff[k_enginesnd] = 12; + 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++) { @@ -4386,14 +4819,14 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) if (volume <= 0) // Might as well return; - S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[k_enginesnd]) + (class*numsnds), volume); + 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_IsLocalPlayer(player)) + if (player->mo->health > 0 && !P_IsDisplayPlayer(player)) { if (cv_kartinvinsfx.value) { @@ -4424,47 +4857,111 @@ 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->kartstuff[k_lapanimation]) - player->kartstuff[k_lapanimation]--; + if (player->karthud[khud_lapanimation]) + player->karthud[khud_lapanimation]--; - if (player->kartstuff[k_yougotem]) - player->kartstuff[k_yougotem]--; + 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->kartstuff[k_cardanimation] += ((164-player->kartstuff[k_cardanimation])/8)+1; + player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; else if (player->exiting == 6*TICRATE) - player->kartstuff[k_cardanimation] = 0; - else if (player->kartstuff[k_cardanimation] < 2*TICRATE) - player->kartstuff[k_cardanimation]++; + 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->kartstuff[k_cardanimation] -= ((164-player->kartstuff[k_cardanimation])/8)+1; + player->karthud[khud_cardanimation] -= ((164-player->karthud[khud_cardanimation])/8)+1; else if (player->kartstuff[k_comebacktimer] < 9*TICRATE) - player->kartstuff[k_cardanimation] += ((164-player->kartstuff[k_cardanimation])/8)+1; + player->karthud[khud_cardanimation] += ((164-player->karthud[khud_cardanimation])/8)+1; } - if (player->kartstuff[k_cardanimation] > 164) - player->kartstuff[k_cardanimation] = 164; - if (player->kartstuff[k_cardanimation] < 0) - player->kartstuff[k_cardanimation] = 0; + 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->kartstuff[k_cardanimation] < 2*TICRATE) - player->kartstuff[k_cardanimation]++; + if (player->karthud[khud_cardanimation] < 2*TICRATE) + player->karthud[khud_cardanimation]++; } else - player->kartstuff[k_cardanimation] = 0; + player->karthud[khud_cardanimation] = 0; } +#undef RINGANIM_DELAYMAX + /** \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 @@ -4475,6 +4972,7 @@ void K_KartPlayerHUDUpdate(player_t *player) 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 @@ -4483,18 +4981,89 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_GetKartBoostPower(player); - // Speed lines - if ((player->kartstuff[k_sneakertimer] || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost]) && player->speed > 0) + // Special effect objects! + if (player->mo && !player->spectator) { - 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); - 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); + 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 (EITHERSNEAKER(player) || player->kartstuff[k_ringboost] + || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost]) + { + 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); + 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); + } + + 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->playerstate == PST_DEAD || player->kartstuff[k_respawn] > 1) // Ensure these are set correctly here @@ -4523,11 +5092,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } else if (player->kartstuff[k_invincibilitytimer]) // setting players to use the star colormap and spawning afterimages { - mobj_t *ghost; player->mo->colorized = true; - ghost = P_SpawnGhostMobj(player->mo); - ghost->fuse = 4; - ghost->frame |= FF_FULLBRIGHT; } else if (player->kartstuff[k_growshrinktimer]) // Ditto, for grow/shrink { @@ -4542,45 +5107,43 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->mo->color = player->skincolor; } } + else if (player->kartstuff[k_ringboost] && (leveltime & 1)) // ring boosting + { + player->mo->colorized = true; + } else { player->mo->colorized = false; } - if (player->kartstuff[k_dashpadcooldown]) // Twinkle Circuit inspired 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]--; - } - // DKR style camera for boosting - if (player->kartstuff[k_boostcam] != 0 || player->kartstuff[k_destboostcam] != 0) + if (player->karthud[khud_boostcam] != 0 || player->karthud[khud_destboostcam] != 0) { - if (player->kartstuff[k_boostcam] < player->kartstuff[k_destboostcam] - && player->kartstuff[k_destboostcam] != 0) + if (player->karthud[khud_boostcam] < player->karthud[khud_destboostcam] + && player->karthud[khud_destboostcam] != 0) { - player->kartstuff[k_boostcam] += FRACUNIT/(TICRATE/4); - if (player->kartstuff[k_boostcam] >= player->kartstuff[k_destboostcam]) - player->kartstuff[k_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->kartstuff[k_boostcam] -= FRACUNIT/TICRATE; - if (player->kartstuff[k_boostcam] < player->kartstuff[k_destboostcam]) - player->kartstuff[k_boostcam] = player->kartstuff[k_destboostcam] = 0; + 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->kartstuff[k_boostcam], player->kartstuff[k_destboostcam]); + //CONS_Printf("cam: %d, dest: %d\n", player->karthud[khud_boostcam], player->karthud[khud_destboostcam]); } - player->kartstuff[k_timeovercam] = 0; + 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. - if (player->kartstuff[k_spinouttimer] != 0 + else if (player->kartstuff[k_spinouttimer] != 0 || player->kartstuff[k_wipeoutslow] != 0 || player->kartstuff[k_squishedtimer] != 0) { @@ -4593,19 +5156,21 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_spinouttimer]) { - if ((P_IsObjectOnGround(player->mo) || player->kartstuff[k_spinouttype] == 1) - && (player->kartstuff[k_sneakertimer] == 0)) + if ((P_IsObjectOnGround(player->mo) + || (player->kartstuff[k_spinouttype] != 0)) + && (!EITHERSNEAKER(player))) { player->kartstuff[k_spinouttimer]--; if (player->kartstuff[k_wipeoutslow] > 1) player->kartstuff[k_wipeoutslow]--; - if (player->kartstuff[k_spinouttimer] == 0) - player->kartstuff[k_spinouttype] = 0; // Reset type + // 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) + if (player->kartstuff[k_wipeoutslow] >= 1) player->mo->friction = ORIG_FRICTION; player->kartstuff[k_wipeoutslow] = 0; if (!comeback) @@ -4613,20 +5178,32 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) else if (player->kartstuff[k_comebacktimer]) { player->kartstuff[k_comebacktimer]--; - if (P_IsLocalPlayer(player) && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer] <= 0) + 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_thunderanim]) - player->kartstuff[k_thunderanim]--;*/ + 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_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1) - player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; - } + + if (player->kartstuff[k_levelbooster]) + player->kartstuff[k_levelbooster]--; + + if (EITHERSNEAKER(player) && 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]--; @@ -4652,6 +5229,21 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) 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]--; @@ -4675,20 +5267,14 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->kartstuff[k_justbumped]--; // This doesn't go in HUD update because it has potential gameplay ramifications - if (player->kartstuff[k_itemblink] && player->kartstuff[k_itemblink]-- <= 0) + if (player->karthud[khud_itemblink] && player->karthud[khud_itemblink]-- <= 0) { - player->kartstuff[k_itemblinkmode] = 0; - player->kartstuff[k_itemblink] = 0; + player->karthud[khud_itemblinkmode] = 0; + player->karthud[khud_itemblink] = 0; } K_KartPlayerHUDUpdate(player); - if (player->kartstuff[k_voices]) - player->kartstuff[k_voices]--; - - if (player->kartstuff[k_tauntvoices]) - player->kartstuff[k_tauntvoices]--; - if (G_BattleGametype() && player->kartstuff[k_bumper] > 0) player->kartstuff[k_wanted]++; @@ -4810,7 +5396,7 @@ void K_KartPlayerAfterThink(player_t *player) if (targ-players != lasttarg) { - if (P_IsLocalPlayer(player) || P_IsLocalPlayer(targ)) + if (P_IsDisplayPlayer(player) || P_IsDisplayPlayer(targ)) S_StartSound(NULL, sfx_s3k89); else S_StartSound(targ->mo, sfx_s3k89); @@ -4897,7 +5483,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) turnvalue = FixedMul(turnvalue, adjustangle); // Weight has a small effect on turning - if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_sneakertimer] || player->kartstuff[k_growshrinktimer] > 0) + if (EITHERSNEAKER(player) || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) turnvalue = FixedMul(turnvalue, FixedDiv(5*FRACUNIT, 4*FRACUNIT)); return turnvalue; @@ -5025,7 +5611,11 @@ static void K_KartDrift(player_t *player, boolean onground) } // Disable drift-sparks until you're going fast enough - if (player->kartstuff[k_getsparks] == 0 || (player->kartstuff[k_offroad] && !player->kartstuff[k_invincibilitytimer] && !player->kartstuff[k_hyudorotimer] && !player->kartstuff[k_sneakertimer])) + if (player->kartstuff[k_getsparks] == 0 + || (player->kartstuff[k_offroad] + && !player->kartstuff[k_invincibilitytimer] + && !player->kartstuff[k_hyudorotimer] + && !EITHERSNEAKER(player))) driftadditive = 0; if (player->speed > minspeed*2) player->kartstuff[k_getsparks] = 1; @@ -5035,7 +5625,7 @@ static void K_KartDrift(player_t *player, boolean onground) K_SpawnDriftSparks(player); // Sound whenever you get a different tier of sparks - if (P_IsLocalPlayer(player) // UGHGHGH... + if (P_IsDisplayPlayer(player) // UGHGHGH... && ((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))) @@ -5056,7 +5646,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_getsparks] = 0; } - if ((!player->kartstuff[k_sneakertimer]) + if ((!EITHERSNEAKER(player)) || (!player->cmd.driftturn) || (!player->kartstuff[k_aizdriftstrat]) || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) @@ -5240,6 +5830,68 @@ void K_StripOther(player_t *player) } } +// 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); +} + // // K_MoveKartPlayer // @@ -5269,439 +5921,490 @@ void K_MoveKartPlayer(player_t *player, boolean onground) 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_growshrinktimer] // Being disabled during Shrink was unintended but people seemed to be okay with it sooo... + || 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 && !(player->exiting || mapreset) + if (player && player->mo && player->mo->health > 0 && !player->spectator && !(player->exiting || mapreset) && leveltime > starttime && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && player->kartstuff[k_respawn] == 0) { // First, the really specific, finicky items that function without the item being directly in your item slot. // Karma item dropping - if (ATTACK_IS_DOWN && player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer]) + if (player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer]) { - mobj_t *newitem; - - if (player->kartstuff[k_comebackmode] == 1) + if (ATTACK_IS_DOWN) { - 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; - } + mobj_t *newitem; - 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); - } - // Eggman Monitor exploding - else 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 (ATTACK_IS_DOWN && player->kartstuff[k_eggmanheld]) - { - K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0); - K_PlayAttackTaunt(player->mo); - player->kartstuff[k_eggmanheld] = 0; - K_UpdateHnextList(player, true); - } - // Rocket Sneaker - else if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO - && player->kartstuff[k_rocketsneakertimer] > 1) - { - K_DoSneaker(player, 2); - K_PlayBoostTaunt(player->mo); - player->kartstuff[k_rocketsneakertimer] -= 2*TICRATE; - if (player->kartstuff[k_rocketsneakertimer] < 1) - player->kartstuff[k_rocketsneakertimer] = 1; - } - // Grow Canceling - else if (player->kartstuff[k_growshrinktimer] > 0) - { - if (player->kartstuff[k_growcancel] >= 0) - { - if (cmd->buttons & BT_ATTACK) + if (player->kartstuff[k_comebackmode] == 1) { - player->kartstuff[k_growcancel]++; - if (player->kartstuff[k_growcancel] > 26) - K_RemoveGrowShrink(player); + newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RANDOMITEM); + newitem->threshold = 69; // selected "randomly". } else - player->kartstuff[k_growcancel] = 0; + { + 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 - { - if ((cmd->buttons & BT_ATTACK) || (player->pflags & PF_ATTACKDOWN)) - player->kartstuff[k_growcancel] = -1; - else - player->kartstuff[k_growcancel] = 0; - } - } - else if (player->kartstuff[k_itemamount] <= 0) - { - player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; } else { - switch (player->kartstuff[k_itemtype]) + // Ring boosting + if (player->kartstuff[k_userings]) { - case KITEM_SNEAKER: + 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 + 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, 1); + K_DoSneaker(player, 2); K_PlayBoostTaunt(player->mo); - player->kartstuff[k_itemamount]--; + player->kartstuff[k_rocketsneakertimer] -= 2*TICRATE; + if (player->kartstuff[k_rocketsneakertimer] < 1) + player->kartstuff[k_rocketsneakertimer] = 1; } - break; - case KITEM_ROCKETSNEAKER: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO - && player->kartstuff[k_rocketsneakertimer] == 0) + } + // Grow Canceling + else if (player->kartstuff[k_growshrinktimer] > 0) + { + if (player->kartstuff[k_growcancel] >= 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++) + if (cmd->buttons & BT_ATTACK) { - 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; + player->kartstuff[k_growcancel]++; + if (player->kartstuff[k_growcancel] > 26) + K_RemoveGrowShrink(player); } - } - 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 - P_RestoreMusic(player); - if (!P_IsLocalPlayer(player)) - S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmi : sfx_kinvnc)); - 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 - && player->kartstuff[k_growshrinktimer] <= 0) // Grow holds the item box hostage - { - 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 - P_RestoreMusic(player); - if (!P_IsLocalPlayer(player)) - S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); - S_StartSound(player->mo, sfx_kc5a); - } - player->kartstuff[k_itemamount]--; + player->kartstuff[k_growcancel] = 0; } - break; - case KITEM_SHRINK: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + else { - K_DoShrink(player); - player->kartstuff[k_itemamount]--; - K_PlayPowerGloatSound(player->mo); + if ((cmd->buttons & BT_ATTACK) || (player->pflags & PF_ATTACKDOWN)) + player->kartstuff[k_growcancel] = -1; + else + player->kartstuff[k_growcancel] = 0; } - break; - case KITEM_THUNDERSHIELD: - if (player->kartstuff[k_curshield] != 1) + } + else if (player->kartstuff[k_itemamount] <= 0) + { + player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; + } + else + { + switch (player->kartstuff[k_itemtype]) { - 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(shield, sfx_s3k41); - player->kartstuff[k_curshield] = 1; + 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 + P_RestoreMusic(player); + if (!P_IsDisplayPlayer(player)) + S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmi : sfx_kinvnc)); + 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 + && player->kartstuff[k_growshrinktimer] <= 0) // Grow holds the item box hostage + { + 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 + P_RestoreMusic(player); + if (!P_IsDisplayPlayer(player)) + S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); + 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] != 1) + { + 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(shield, sfx_s3k41); + player->kartstuff[k_curshield] = 1; + } + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_DoThunderShield(player); + player->kartstuff[k_itemamount]--; + K_PlayAttackTaunt(player->mo); + } + 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; } - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) - { - K_DoThunderShield(player); - player->kartstuff[k_itemamount]--; - K_PlayAttackTaunt(player->mo); - } - 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_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; + } } } @@ -5712,7 +6415,15 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->kartstuff[k_itemtype] = KITEM_NONE; } - if (player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD) + if (spbplace == -1 || player->kartstuff[k_position] != spbplace) + player->kartstuff[k_ringlock] = 0; // reset ring lock + + if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) + { + if ((player->kartstuff[k_rings]+player->kartstuff[k_pickuprings]) < 20 && !player->kartstuff[k_ringlock]) + K_LookForRings(player->mo); + } + else player->kartstuff[k_curshield] = 0; if (player->kartstuff[k_growshrinktimer] <= 0) @@ -5912,7 +6623,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) else { K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker - if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsLocalPlayer(player)) + 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! @@ -6399,6 +7110,18 @@ static patch_t *kp_winnernum[NUMPOSFRAMES]; static patch_t *kp_facenum[MAXPLAYERS+1]; static patch_t *kp_facehighlight[8]; +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; @@ -6417,6 +7140,7 @@ 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]; @@ -6537,6 +7261,57 @@ void K_LoadKartHUDGraphics(void) kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, 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); @@ -6560,6 +7335,7 @@ void K_LoadKartHUDGraphics(void) 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); @@ -6595,6 +7371,7 @@ void K_LoadKartHUDGraphics(void) 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"); @@ -6739,6 +7516,8 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny) 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: @@ -6755,7 +7534,6 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny) INT32 ITEM_X, ITEM_Y; // Item Window INT32 TIME_X, TIME_Y; // Time Sticker INT32 LAPS_X, LAPS_Y; // Lap Sticker -INT32 SPDM_X, SPDM_Y; // Speedometer INT32 POSI_X, POSI_Y; // Position Number INT32 FACE_X, FACE_Y; // Top-four Faces INT32 STCD_X, STCD_Y; // Starting countdown @@ -6815,9 +7593,6 @@ static void K_initKartHUD(void) // Level Laps LAPS_X = 9; // 9 LAPS_Y = BASEVIDHEIGHT - 29; // 171 - // Speedometer - SPDM_X = 9; // 9 - SPDM_Y = BASEVIDHEIGHT - 45; // 155 // Position Number POSI_X = BASEVIDWIDTH - 9; // 268 POSI_Y = BASEVIDHEIGHT - 9; // 138 @@ -6856,20 +7631,20 @@ static void K_initKartHUD(void) ITEM_Y = -8; LAPS_X = 3; - LAPS_Y = (BASEVIDHEIGHT/2)-13; + LAPS_Y = (BASEVIDHEIGHT/2)-12; POSI_X = 24; - POSI_Y = (BASEVIDHEIGHT/2)- 16; + POSI_Y = (BASEVIDHEIGHT/2)-26; // 2P (top right) ITEM2_X = BASEVIDWIDTH-39; ITEM2_Y = -8; - LAPS2_X = BASEVIDWIDTH-40; - LAPS2_Y = (BASEVIDHEIGHT/2)-13; + LAPS2_X = BASEVIDWIDTH-43; + LAPS2_Y = (BASEVIDHEIGHT/2)-12; POSI2_X = BASEVIDWIDTH -4; - POSI2_Y = (BASEVIDHEIGHT/2)- 16; + POSI2_Y = (BASEVIDHEIGHT/2)-26; // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom. @@ -6959,7 +7734,7 @@ static void K_drawKartItem(void) if (stplyr->skincolor) localcolor = stplyr->skincolor; - switch((stplyr->kartstuff[k_itemroulette] % (14*3)) / 3) + 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 @@ -7018,11 +7793,15 @@ static void K_drawKartItem(void) localpatch = kp_thundershield[offset]; //localcolor = SKINCOLOR_CYAN; break; - /*case 14: // Pogo Spring + 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 15: // Kitchen Sink + case 16: // Kitchen Sink localpatch = kp_kitchensink[offset]; localcolor = SKINCOLOR_STEEL; break;*/ @@ -7139,6 +7918,9 @@ static void K_drawKartItem(void) case KITEM_POGOSPRING: localpatch = kp_pogospring[offset]; break; + case KITEM_SUPERRING: + localpatch = kp_superring[offset]; + break; case KITEM_KITCHENSINK: localpatch = kp_kitchensink[offset]; break; @@ -7153,11 +7935,11 @@ static void K_drawKartItem(void) localpatch = kp_nodraw; } - if (stplyr->kartstuff[k_itemblink] && (leveltime & 1)) + if (stplyr->karthud[khud_itemblink] && (leveltime & 1)) { colormode = TC_BLINK; - switch (stplyr->kartstuff[k_itemblinkmode]) + switch (stplyr->karthud[khud_itemblinkmode]) { case 2: localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))); @@ -7244,7 +8026,6 @@ static void K_drawKartItem(void) // 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]))]); - } void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode) @@ -7769,22 +8550,46 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I } } -static void K_drawKartLaps(void) +#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2) + +static void K_drawKartLapsAndRings(void) { + SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); - INT32 fx = 0, fy = 0, fflags = 0; // stuff for 3p / 4p splitscreen. - boolean flipstring = false; // used for 3p or 4p - INT32 stringw = 0; // used with the above + 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((splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width); + } if (splitscreen > 1) { + INT32 fx = 0, fy = 0, fr = 0; + INT32 flipflag = 0; // pain and suffering defined below if (splitscreen < 2) // don't change shit for THIS splitscreen. { fx = LAPS_X; fy = LAPS_Y; - fflags = splitflags; } else { @@ -7792,116 +8597,238 @@ static void K_drawKartLaps(void) { fx = LAPS_X; fy = LAPS_Y; - fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom. + 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; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipstring = true; // make the string right aligned and other shit + 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]); - if (stplyr->exiting) // draw stuff as god intended. + V_DrawScaledPatch(fx, fy, V_HUDTRANS|splitflags, kp_splitlapflag); + V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|splitflags, frameslash); + + if (cv_numlaps.value >= 10) { - V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, kp_splitlapflag); - V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, "FIN"); - } - else // take flipstring into account here since we may have more laps than just 10 - if (flipstring) - { - stringw = V_StringWidth(va("%d/%d", stplyr->laps+1, cv_numlaps.value), 0); + UINT8 ln[2]; + ln[0] = ((abs(stplyr->laps+1) / 10) % 10); + ln[1] = (abs(stplyr->laps+1) % 10); - V_DrawScaledPatch(BASEVIDWIDTH-stringw-16, fy, V_HUDTRANS|fflags, kp_splitlapflag); - V_DrawRightAlignedString(BASEVIDWIDTH-3, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value)); - } - else // draw stuff NORMALLY. - { - V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, kp_splitlapflag); - V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value)); - } + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[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, pingnum[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + } + else + { + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, kp_facenum[(stplyr->laps+1) % 10]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, kp_facenum[(cv_numlaps.value) % 10]); + } + + // Rings + if (netgame) + { + 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, pingnum[rn[0]], ringmap); + V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, pingnum[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 (!netgame) + { + 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, pingnum[(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+1, cv_numlaps.value)); + + // Rings + if (netgame) + 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 (!netgame) + { + 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) { - fixed_t convSpeed; + static fixed_t convSpeed; + UINT8 labeln = 0; + UINT8 numbers[3]; INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); + UINT8 battleoffset = 0; - if (cv_kartspeedometer.value == 1) // Kilometers + if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line! { - convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058 - V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d km/h", convSpeed)); - } - else if (cv_kartspeedometer.value == 2) // Miles - { - convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774 - V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d mph", convSpeed)); - } - else if (cv_kartspeedometer.value == 3) // Fracunits - { - convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT; - V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d fu/t", convSpeed)); + 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); - INT32 fx = 0, fy = 0, fflags = 0; - boolean flipstring = false; // same as laps, used for splitscreen - INT32 stringw = 0; // used with the above if (splitscreen > 1) { + INT32 fx = 0, fy = 0; + INT32 flipflag = 0; - // we will reuse lap coords here since it's essentially the same shit. - - if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + // pain and suffering defined below + if (splitscreen < 2) // don't change shit for THIS splitscreen. { fx = LAPS_X; fy = LAPS_Y; - fflags = 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. + else { - fx = LAPS2_X; - fy = LAPS2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom - flipstring = true; + 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 (stplyr->kartstuff[k_bumper] <= 0) { - V_DrawMappedPatch(fx, fy-1, V_HUDTRANS|fflags, kp_splitkarmabomb, colormap); - V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, va("%d/2", stplyr->kartstuff[k_comebackpoints])); + 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 // the above doesn't need to account for weird stuff since the max amount of karma necessary is always 2 ^^^^ + else { - if (flipstring) // for p2 and p4, assume we can have more than 10 bumpers. It's retarded but who knows. - { - stringw = V_StringWidth(va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value), 0); + V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|splitflags, kp_rankbumper, colormap); - V_DrawMappedPatch(BASEVIDWIDTH-stringw-16, fy-1, V_HUDTRANS|fflags, kp_rankbumper, colormap); - V_DrawRightAlignedString(BASEVIDWIDTH-3, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value)); - } - else // draw bumpers normally. + if (stplyr->kartstuff[k_bumper] > 9 || cv_kartbumpers.value > 9) { - V_DrawMappedPatch(fx, fy-1, V_HUDTRANS|fflags, kp_rankbumper, colormap); - V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value)); + 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, pingnum[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + + ln[0] = ((abs(cv_kartbumpers.value) / 10) % 10); + ln[1] = (abs(cv_kartbumpers.value) % 10); + + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, pingnum[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[(cv_kartbumpers.value) % 10]); } } } @@ -8317,10 +9244,10 @@ static void K_drawKartFinish(void) { INT32 pnum = 0, splitflags = K_calcSplitFlags(0); - if (!stplyr->kartstuff[k_cardanimation] || stplyr->kartstuff[k_cardanimation] >= 2*TICRATE) + if (!stplyr->karthud[khud_cardanimation] || stplyr->karthud[khud_cardanimation] >= 2*TICRATE) return; - if ((stplyr->kartstuff[k_cardanimation] % (2*5)) / 5) // blink + if ((stplyr->karthud[khud_cardanimation] % (2*5)) / 5) // blink pnum = 1; if (splitscreen > 1) // 3/4p, stationary FIN @@ -8339,7 +9266,7 @@ static void K_drawKartFinish(void) x = ((vid.width<width)<kartstuff[k_cardanimation])*(xval > x ? xval : x))/TICRATE; + x = ((TICRATE - stplyr->karthud[khud_cardanimation])*(xval > x ? xval : x))/TICRATE; if (splitscreen && stplyr == &players[displayplayers[1]]) x = -x; @@ -8354,7 +9281,7 @@ static void K_drawKartFinish(void) static void K_drawBattleFullscreen(void) { INT32 x = BASEVIDWIDTH/2; - INT32 y = -64+(stplyr->kartstuff[k_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen + 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. @@ -8369,11 +9296,11 @@ static void K_drawBattleFullscreen(void) || (splitscreen > 1 && (stplyr == &players[displayplayers[2]] || (stplyr == &players[displayplayers[3]] && splitscreen > 2)))) { - y = 232-(stplyr->kartstuff[k_cardanimation]/2); + y = 232-(stplyr->karthud[khud_cardanimation]/2); splitflags = V_SNAPTOBOTTOM; } else - y = -32+(stplyr->kartstuff[k_cardanimation]/2); + y = -32+(stplyr->karthud[khud_cardanimation]/2); if (splitscreen > 1) { @@ -8737,21 +9664,21 @@ static void K_drawChallengerScreen(void) static void K_drawLapStartAnim(void) { // This is an EVEN MORE insanely complicated animation. - const UINT8 progress = 80-stplyr->kartstuff[k_lapanimation]; + const UINT8 progress = 80-stplyr->karthud[khud_lapanimation]; UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->kartstuff[k_lapanimation]-76)))*FRACUNIT, + 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->kartstuff[k_laphand] >= 1 && stplyr->kartstuff[k_laphand] <= 3) + if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3) { - V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->kartstuff[k_lapanimation]-76)))*FRACUNIT, + 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->kartstuff[k_laphand]-1], NULL); + kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL); } if (stplyr->laps == (UINT8)(cv_numlaps.value - 1)) @@ -8797,6 +9724,7 @@ static void K_drawLapStartAnim(void) 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; @@ -8824,6 +9752,7 @@ static void K_drawDistributionDebugger(void) kp_thundershield[1], kp_hyudoro[1], kp_pogospring[1], + kp_superring[1], kp_kitchensink[1], kp_sneaker[1], @@ -8835,9 +9764,9 @@ static void K_drawDistributionDebugger(void) }; INT32 useodds = 0; INT32 pingame = 0, bestbumper = 0; + INT32 pdis = 0; INT32 i; INT32 x = -9, y = -9; - boolean dontforcespb = false; boolean spbrush = false; if (stplyr != &players[displayplayers[0]]) // only for p1 @@ -8849,16 +9778,34 @@ static void K_drawDistributionDebugger(void) 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]; } - if (G_RaceGametype()) - spbrush = (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1); + // lovely double loop...... + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator && players[i].mo + && players[i].kartstuff[k_position] < stplyr->kartstuff[k_position]) + pdis += P_AproxDistance(P_AproxDistance(players[i].mo->x - stplyr->mo->x, + players[i].mo->y - stplyr->mo->y), + players[i].mo->z - stplyr->mo->z) / mapobjectscale + * (pingame - players[i].kartstuff[k_position]) + / max(1, ((pingame - 1) * (pingame + 1) / 3)); + } - useodds = K_FindUseodds(stplyr, 0, pingame, bestbumper, spbrush, dontforcespb); + 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; + } + + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count + + useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); for (i = 1; i < NUMKARTRESULTS; i++) { @@ -9005,6 +9952,15 @@ void K_drawKartHUD(void) if (!stplyr->spectator) // Bottom of the screen elements, don't need in spectate mode { + // Draw the speedometer + if (cv_kartspeedometer.value && !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; @@ -9033,17 +9989,7 @@ void K_drawKartHUD(void) #ifdef HAVE_BLUA if (LUA_HudEnabled(hud_gametypeinfo)) #endif - K_drawKartLaps(); - - if (!splitscreen) - { - // Draw the speedometer - // TODO: Make a better speedometer. -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_speedometer)) -#endif - K_drawKartSpeedometer(); - } + K_drawKartLapsAndRings(); if (isfreeplay) ; @@ -9096,14 +10042,14 @@ void K_drawKartHUD(void) { if (stplyr->exiting) K_drawKartFinish(); - else if (stplyr->kartstuff[k_lapanimation] && !splitscreen) + else if (stplyr->karthud[khud_lapanimation] && !splitscreen) K_drawLapStartAnim(); } if (modeattacking) // everything after here is MP and debug only return; - if (G_BattleGametype() && !splitscreen && (stplyr->kartstuff[k_yougotem] % 2)) // * YOU GOT EM * + if (G_BattleGametype() && !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. diff --git a/src/k_kart.h b/src/k_kart.h index 396ebdc55..4b9a442d1 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -22,7 +22,9 @@ void K_RegisterKartStuff(void); boolean K_IsPlayerLosing(player_t *player); boolean K_IsPlayerWanted(player_t *player); +fixed_t K_GetKartGameSpeedScalar(SINT8 value); 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_RespawnChecker(player_t *player); @@ -41,6 +43,7 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color); void K_SpawnBoostTrail(player_t *player); void K_SpawnSparkleTrail(mobj_t *mo); void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent); +void K_SpawnDraftDust(mobj_t *mo); void K_DriftDustHandling(mobj_t *spawner); void K_PuntMine(mobj_t *mine, mobj_t *punter); void K_DoSneaker(player_t *player, INT32 type); @@ -74,6 +77,7 @@ void K_CheckSpectateStatus(void); void K_PlayAttackTaunt(mobj_t *source); void K_PlayBoostTaunt(mobj_t *source); void K_PlayOvertakeSound(mobj_t *source); +void K_PlayPainSound(mobj_t *source); void K_PlayHitEmSound(mobj_t *source); void K_PlayPowerGloatSound(mobj_t *source); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 6700d5af8..38af4d2e9 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1166,37 +1166,6 @@ static int lib_pPlayerRingBurst(lua_State *L) return 0; } -static int lib_pPlayerWeaponPanelBurst(lua_State *L) -{ - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - NOHUD - if (!player) - return LUA_ErrInvalid(L, "player_t"); - P_PlayerWeaponPanelBurst(player); - return 0; -} - -static int lib_pPlayerWeaponAmmoBurst(lua_State *L) -{ - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - NOHUD - if (!player) - return LUA_ErrInvalid(L, "player_t"); - P_PlayerWeaponAmmoBurst(player); - return 0; -} - -static int lib_pPlayerEmeraldBurst(lua_State *L) -{ - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - boolean toss = lua_optboolean(L, 2); - NOHUD - if (!player) - return LUA_ErrInvalid(L, "player_t"); - P_PlayerEmeraldBurst(player, toss); - return 0; -} - static int lib_pPlayerFlagBurst(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -2229,6 +2198,16 @@ static int lib_kOvertakeSound(lua_State *L) return 0; } +static int lib_kPainSound(lua_State *L) +{ + mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + NOHUD + if (!mobj->player) + return luaL_error(L, "K_PlayPainSound: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful. + K_PlayPainSound(mobj); + return 0; +} + static int lib_kHitEmSound(lua_State *L) { mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -2744,9 +2723,6 @@ static luaL_Reg lib[] = { {"P_DamageMobj",lib_pDamageMobj}, {"P_KillMobj",lib_pKillMobj}, {"P_PlayerRingBurst",lib_pPlayerRingBurst}, - {"P_PlayerWeaponPanelBurst",lib_pPlayerWeaponPanelBurst}, - {"P_PlayerWeaponAmmoBurst",lib_pPlayerWeaponAmmoBurst}, - {"P_PlayerEmeraldBurst",lib_pPlayerEmeraldBurst}, {"P_PlayerFlagBurst",lib_pPlayerFlagBurst}, {"P_PlayRinglossSound",lib_pPlayRinglossSound}, {"P_PlayDeathSound",lib_pPlayDeathSound}, @@ -2835,6 +2811,7 @@ static luaL_Reg lib[] = { {"K_PlayPowerGloatSound", lib_kGloatSound}, {"K_PlayOvertakeSound", lib_kOvertakeSound}, {"K_PlayLossSound", lib_kLossSound}, + {"K_PlayPainSound", lib_kPainSound}, {"K_PlayHitEmSound", lib_kHitEmSound}, {"K_GetKartColorByName",lib_kGetKartColorByName}, {"K_IsPlayerLosing",lib_kIsPlayerLosing}, diff --git a/src/m_menu.c b/src/m_menu.c index 3732f6550..216eaab29 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -522,7 +522,7 @@ static menuitem_t PlaybackMenu[] = {IT_CALL | IT_STRING, "M_PREW", "Rewind", M_PlaybackRewind, 20}, {IT_CALL | IT_STRING, "M_PPAUSE", "Pause", M_PlaybackPause, 36}, - {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Foward", M_PlaybackFastForward, 52}, + {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Forward", M_PlaybackFastForward, 52}, {IT_CALL | IT_STRING, "M_PSTEPB", "Backup Frame", M_PlaybackRewind, 20}, {IT_CALL | IT_STRING, "M_PRESUM", "Resume", M_PlaybackPause, 36}, {IT_CALL | IT_STRING, "M_PFADV", "Advance Frame", M_PlaybackAdvance, 52}, @@ -1561,7 +1561,7 @@ static menuitem_t OP_AdvServerOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Karma Comeback", &cv_kartcomeback, 66}, };*/ -#define ITEMTOGGLEBOTTOMRIGHT +//#define ITEMTOGGLEBOTTOMRIGHT static menuitem_t OP_MonitorToggleMenu[] = { @@ -1589,6 +1589,7 @@ static menuitem_t OP_MonitorToggleMenu[] = {IT_KEYHANDLER | IT_NOTHING, NULL, "Thunder Shields", M_HandleMonitorToggles, KITEM_THUNDERSHIELD}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Hyudoros", M_HandleMonitorToggles, KITEM_HYUDORO}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Pogo Springs", M_HandleMonitorToggles, KITEM_POGOSPRING}, + {IT_KEYHANDLER | IT_NOTHING, NULL, "Super Rings", M_HandleMonitorToggles, KITEM_SUPERRING}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Kitchen Sinks", M_HandleMonitorToggles, KITEM_KITCHENSINK}, #ifdef ITEMTOGGLEBOTTOMRIGHT {IT_KEYHANDLER | IT_NOTHING, NULL, "---", M_HandleMonitorToggles, 255}, @@ -8444,7 +8445,11 @@ static void M_ConnectMenu(INT32 choice) // first page of servers serverlistpage = 0; if (ms_RoomId < 0) + { M_RoomMenu(0); // Select a room instead of staring at an empty list + // This prevents us from returning to the modified game alert. + currentMenu->prevMenu = &MP_MainDef; + } else M_SetupNextMenu(&MP_ConnectDef); itemOn = 0; @@ -10643,6 +10648,7 @@ static consvar_t *kartitemcvs[NUMKARTRESULTS-1] = { &cv_thundershield, &cv_hyudoro, &cv_pogospring, + &cv_superring, &cv_kitchensink, &cv_triplesneaker, &cv_triplebanana, diff --git a/src/mserv.c b/src/mserv.c index 29aa99fae..c7344b16a 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -661,19 +661,11 @@ FUNCMATH static const char *int2str(INT32 n) #ifndef NONET static INT32 ConnectionFailed(void) { - time(&MSLastPing); con_state = MSCS_FAILED; CONS_Alert(CONS_ERROR, M_GetText("Connection to Master Server failed\n")); CloseConnection(); return MS_CONNECT_ERROR; } - -static INT32 ConnectionFailedwerrno(int no) -{ - CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error: %s\n"), - strerror(no)); - return ConnectionFailed(); -} #endif /** Tries to register the local game server on the master server. @@ -690,43 +682,57 @@ static INT32 AddToMasterServer(boolean firstadd) 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 (socket_fd == (SOCKET_TYPE)ERRSOCKET)/* Woah, our socket was closed! */ - { - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - return ConnectionFailedwerrno(errno); - } - M_Memcpy(&tset, &wset, sizeof (tset)); res = select(255, NULL, &tset, NULL, &select_timeout); - if (res == ERRSOCKET) - return ConnectionFailedwerrno(errno); - if (res == 0)/* nothing selected */ + if (res != ERRSOCKET && !res) { - /* - Timeout next call because SendPingToMasterServer - (our calling function) already calls this once - every two minutes. - */ - if (retry++ == 1) + 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); - if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&i, &j) == ERRSOCKET) - return ConnectionFailedwerrno(errno); + 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 - return ConnectionFailedwerrno(i); + { + 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) @@ -759,12 +765,15 @@ static INT32 AddToMasterServer(boolean firstadd) 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")); - time(&MSLastPing); + MSLastPing = timestamp; con_state = MSCS_REGISTERED; CloseConnection(); #endif diff --git a/src/p_enemy.c b/src/p_enemy.c index 1795a304b..9569b2c4c 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -710,59 +710,6 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed //return false; } -/** Looks for a player with a ring shield. - * Used by rings. - * - * \param actor Ring looking for a shield to be attracted to. - * \return True if a player with ring shield is found, otherwise false. - * \sa A_AttractChase - */ -static boolean P_LookForShield(mobj_t *actor) -{ - INT32 c = 0, stop; - player_t *player; - - // BP: first time init, this allow minimum lastlook changes - if (actor->lastlook < 0) - actor->lastlook = P_RandomByte(); - - actor->lastlook %= MAXPLAYERS; - - stop = (actor->lastlook - 1) & PLAYERSMASK; - - for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK)) - { - // done looking - if (actor->lastlook == stop) - return false; - - if (!playeringame[actor->lastlook]) - continue; - - if (c++ == 2) - return false; - - player = &players[actor->lastlook]; - - if (player->health <= 0 || !player->mo) - continue; // dead - - //When in CTF, don't pull rings that you cannot pick up. - if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) || - (actor->type == MT_BLUETEAMRING && player->ctfteam != 2)) - continue; - - if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT - && (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST/4, player->mo->scale))) - { - P_SetTarget(&actor->tracer, player->mo); - return true; - } - } - - //return false; -} - #ifdef WEIGHTEDRECYCLER // Compares players to see who currently has the "best" items, etc. static int P_RecycleCompare(const void *p1, const void *p2) @@ -3611,61 +3558,179 @@ void A_BubbleCheck(mobj_t *actor) // void A_AttractChase(mobj_t *actor) { + fixed_t z; + #ifdef HAVE_BLUA if (LUA_CallAction("A_AttractChase", actor)) return; #endif + if (actor->flags2 & MF2_NIGHTSPULL || !actor->health) return; - // spilled rings flicker before disappearing - if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE) - actor->flags2 |= MF2_DONTDRAW; + if (actor->extravalue1) // SRB2Kart + { +#define RINGBOOSTPWR (((9 - actor->target->player->kartspeed) + (9 - actor->target->player->kartweight)) / 2) + if (!actor->target || P_MobjWasRemoved(actor->target) || !actor->target->player) + { + P_RemoveMobj(actor); + return; + } + + if (actor->extravalue2) // Using for ring boost + { + // Always fullbright + actor->frame |= FF_FULLBRIGHT; + + if (actor->extravalue1 >= 21) + { + mobj_t *sparkle; + angle_t offset = FixedAngle(18<target->player->kartstuff[k_ringboost] += RINGBOOSTPWR+3; + S_StartSound(actor->target, sfx_s1b5); + + sparkle = P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, MT_RINGSPARKS); + P_SetTarget(&sparkle->target, actor->target); + sparkle->angle = (actor->target->angle + (offset>>1)) + (offset * actor->target->player->kartstuff[k_sparkleanim]); + actor->target->player->kartstuff[k_sparkleanim] = (actor->target->player->kartstuff[k_sparkleanim]+1) % 20; + + P_KillMobj(actor, actor->target, actor->target); + return; + } + else + { + fixed_t offz = FixedMul(80*actor->target->scale, FINESINE(FixedAngle((90 - (9 * abs(10 - actor->extravalue1))) << FRACBITS) >> ANGLETOFINESHIFT)); + //P_SetScale(actor, (actor->destscale = actor->target->scale)); + z = actor->target->z; + if (( actor->eflags & MFE_VERTICALFLIP )) + z -= actor->height + offz; + else + z += actor->target->height + offz; + P_TeleportMove(actor, actor->target->x, actor->target->y, z); + actor->extravalue1++; + } + } + else // Collecting + { + if (actor->extravalue1 >= 16) + { + if (actor->target->player->kartstuff[k_rings] >= 20) + actor->target->player->kartstuff[k_ringboost] += RINGBOOSTPWR+3; + else + P_GivePlayerRings(actor->target->player, 1); + + if (actor->cvmem) // caching + S_StartSound(actor->target, sfx_s1c5); + else + S_StartSound(actor->target, sfx_s227); + + actor->target->player->kartstuff[k_pickuprings]--; + P_RemoveMobj(actor); + return; + } + else + { + fixed_t dist = (actor->target->radius/4) * (16 - actor->extravalue1); + + P_SetScale(actor, (actor->destscale = actor->target->scale - ((actor->target->scale/14) * actor->extravalue1))); + z = actor->target->z; + if (( actor->eflags & MFE_VERTICALFLIP )) + z += actor->target->height - actor->height - 24 * actor->target->scale; + else + z += 24 * actor->target->scale; + P_TeleportMove(actor, + actor->target->x + FixedMul(dist, FINECOSINE(actor->angle >> ANGLETOFINESHIFT)), + actor->target->y + FixedMul(dist, FINESINE(actor->angle >> ANGLETOFINESHIFT)), + z); + + actor->angle += ANG30; + actor->extravalue1++; + } + } +#undef RINGBOOSTPWR + } else - actor->flags2 &= ~MF2_DONTDRAW; - - // Turn flingrings back into regular rings if attracted. - if (actor->tracer && actor->tracer->player - && (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT - && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime) { - mobj_t *newring; - newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime); - newring->momx = actor->momx; - newring->momy = actor->momy; - newring->momz = actor->momz; - P_RemoveMobj(actor); - return; + // Don't immediately pick up spilled rings + if (actor->threshold > 0) + actor->threshold--; + + // Rings flicker before disappearing + if (actor->fuse && actor->fuse < 5*TICRATE && (leveltime & 1)) + actor->flags2 |= MF2_DONTDRAW; + else + actor->flags2 &= ~MF2_DONTDRAW; + + // spilled rings have ghost trails and get capped to a certain speed + if (actor->type == (mobjtype_t)actor->info->reactiontime) + { + const fixed_t maxspeed = 4<momx, actor->momy); + + if (oldspeed > maxspeed) + { + fixed_t newspeed = max(maxspeed, oldspeed-FRACUNIT); + actor->momx = FixedMul(FixedDiv(actor->momx, oldspeed), newspeed); + actor->momy = FixedMul(FixedDiv(actor->momy, oldspeed), newspeed); + } + + if (!P_IsObjectOnGround(actor)) + P_SpawnGhostMobj(actor)->tics = 3; + } + + if (actor->tracer && actor->tracer->player && actor->tracer->health + //&& P_CheckSight(actor, actor->tracer) + && actor->tracer->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD + && (actor->tracer->player->kartstuff[k_rings]+actor->tracer->player->kartstuff[k_pickuprings]) < 20 + && !actor->tracer->player->kartstuff[k_ringlock]) + { + fixed_t dist; + angle_t hang, vang; + + // If a flung ring gets attracted by a shield, change it into a normal ring. + if (actor->type == (mobjtype_t)actor->info->reactiontime) + { + P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance); + P_RemoveMobj(actor); + return; + } + + // Keep stuff from going down inside floors and junk + actor->flags &= ~MF_NOCLIPHEIGHT; + + // Let attracted rings move through walls and such. + actor->flags |= MF_NOCLIP; + + // P_Attract is too "smart" for Kart; keep it simple, stupid! + dist = P_AproxDistance(P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y), actor->z - actor->tracer->z); + hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); + vang = R_PointToAngle2(actor->z , 0, actor->tracer->z, dist); + + actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4; + actor->momx += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hang>>ANGLETOFINESHIFT), 4*actor->scale)); + actor->momy += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINESINE(hang>>ANGLETOFINESHIFT), 4*actor->scale)); + actor->momz += FixedMul(FINECOSINE(vang>>ANGLETOFINESHIFT), 4*actor->scale); + } + else + { + // Turn rings back into flung rings if lost + if (actor->cusval && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime) + { + mobj_t *newring; + newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime); + P_InstaThrust(newring, P_RandomRange(0,7) * ANGLE_45, 2<momz = 8<fuse = 120*TICRATE; + P_RemoveMobj(actor); + return; + } + /*else + P_LookForShield(actor);*/ + // SRB2Kart: now it's the PLAYER'S job to use the blockmap to find rings, not the ring's. + } } - - P_LookForShield(actor); // Go find 'em, boy! - - if (!actor->tracer - || !actor->tracer->player - || !actor->tracer->health - || !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta - { - // Lost attracted rings don't through walls anymore. - actor->flags &= ~MF_NOCLIP; - P_SetTarget(&actor->tracer, NULL); - return; - } - - // If a FlingRing gets attracted by a shield, change it into a normal ring. - if (actor->type == (mobjtype_t)actor->info->reactiontime) - { - P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance); - P_RemoveMobj(actor); - return; - } - - // Keep stuff from going down inside floors and junk - actor->flags &= ~MF_NOCLIPHEIGHT; - - // Let attracted rings move through walls and such. - actor->flags |= MF_NOCLIP; - - P_Attract(actor, actor->tracer, false); } // Function: A_DropMine @@ -8256,7 +8321,11 @@ void A_ItemPop(mobj_t *actor) void A_JawzChase(mobj_t *actor) { + const fixed_t currentspeed = R_PointToDist2(0, 0, actor->momx, actor->momy); player_t *player; + fixed_t thrustamount = 0; + fixed_t frictionsafety = (actor->friction == 0) ? 1 : actor->friction; + fixed_t topspeed = actor->movefactor; #ifdef HAVE_BLUA if (LUA_CallAction("A_JawzChase", actor)) return; @@ -8269,20 +8338,100 @@ void A_JawzChase(mobj_t *actor) if (actor->tracer->health) { + const angle_t targetangle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); mobj_t *ret; + angle_t angledelta = actor->angle - targetangle; + boolean turnclockwise = true; + + if (G_RaceGametype()) + { + const fixed_t distbarrier = FixedMul(512*mapobjectscale, FRACUNIT + ((gamespeed-1) * (FRACUNIT/4))); + const fixed_t distaway = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y); + if (distaway < distbarrier) + { + if (actor->tracer->player) + { + fixed_t speeddifference = abs(topspeed - min(actor->tracer->player->speed, K_GetKartSpeed(actor->tracer->player, false))); + topspeed = topspeed - FixedMul(speeddifference, FRACUNIT-FixedDiv(distaway, distbarrier)); + } + } + } + + if (angledelta != 0) + { + angle_t MAX_JAWZ_TURN = ANGLE_90/15; // We can turn a maximum of 6 degrees per frame at regular max speed + // MAX_JAWZ_TURN gets stronger the slower the top speed of jawz + if (topspeed < actor->movefactor) + { + if (topspeed == 0) + { + MAX_JAWZ_TURN = ANGLE_180; + } + else + { + fixed_t anglemultiplier = FixedDiv(actor->movefactor, topspeed); + MAX_JAWZ_TURN += FixedAngle(FixedMul(AngleFixed(MAX_JAWZ_TURN), anglemultiplier)); + } + } + + if (angledelta > ANGLE_180) + { + angledelta = InvAngle(angledelta); + turnclockwise = false; + } + + if (angledelta > MAX_JAWZ_TURN) + { + angledelta = MAX_JAWZ_TURN; + } + + if (turnclockwise) + { + actor->angle -= angledelta; + } + else + { + actor->angle += angledelta; + } + } ret = P_SpawnMobj(actor->tracer->x, actor->tracer->y, actor->tracer->z, MT_PLAYERRETICULE); P_SetTarget(&ret->target, actor->tracer); ret->frame |= ((leveltime % 10) / 2) + 5; ret->color = actor->cvmem; - - P_Thrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), (7*actor->movefactor)/64); - return; } else P_SetTarget(&actor->tracer, NULL); } + if (!P_IsObjectOnGround(actor)) + { + // No friction in the air + frictionsafety = FRACUNIT; + } + + if (currentspeed >= topspeed) + { + // Thrust as if you were at top speed, slow down naturally + thrustamount = FixedDiv(topspeed, frictionsafety) - topspeed; + } + else + { + const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; + // Thrust to immediately get to top speed + thrustamount = beatfriction + FixedDiv(topspeed - currentspeed, frictionsafety); + } + + if (!actor->tracer) + { + actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy); + } + + P_Thrust(actor, actor->angle, thrustamount); + + if ((actor->tracer != NULL) && (actor->tracer->health > 0)) + return; + if (actor->extravalue1) // Disable looking by setting this return; @@ -8405,6 +8554,7 @@ void A_SPBChase(mobj_t *actor) fixed_t easiness = ((actor->tracer->player->kartspeed + (10-spark)) << FRACBITS) / 2; actor->lastlook = actor->tracer->player-players; // Save the player num for death scumming... + actor->tracer->player->kartstuff[k_ringlock] = 1; // set ring lock if (!P_IsObjectOnGround(actor->tracer) /*&& !actor->tracer->player->kartstuff[k_pogospring]*/) { @@ -8488,6 +8638,15 @@ void A_SPBChase(mobj_t *actor) actor->momy = cy + FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT)); actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT)); + // Spawn a trail of rings behind the SPB! + if (leveltime % 6 == 0) + { + mobj_t *ring = P_SpawnMobj(actor->x - actor->momx, actor->y - actor->momx, + actor->z - actor->momz + (24*mapobjectscale), MT_RING); + ring->threshold = 10; + ring->fuse = 120*TICRATE; + } + // Red speed lines for when it's gaining on its target. A tell for when you're starting to lose too much speed! if (R_PointToDist2(0, 0, actor->momx, actor->momy) > (actor->tracer->player ? (16*actor->tracer->player->speed)/15 : (16*R_PointToDist2(0, 0, actor->tracer->momx, actor->tracer->momy))/15) // Going faster than the target @@ -8529,6 +8688,7 @@ void A_SPBChase(mobj_t *actor) && !players[actor->lastlook].exiting) { spbplace = players[actor->lastlook].kartstuff[k_position]; + players[actor->lastlook].kartstuff[k_ringlock] = 1; if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo) { P_SetTarget(&actor->tracer, players[actor->lastlook].mo); diff --git a/src/p_inter.c b/src/p_inter.c index 5f17e9530..b4d5c51dc 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -116,7 +116,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) { // Invulnerable if (player->powers[pw_flashing] > 0 - || player->kartstuff[k_spinouttimer] > 0 + || (player->kartstuff[k_spinouttimer] > 0 && player->kartstuff[k_spinouttype] != 2) || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 @@ -494,7 +494,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->target->player->kartstuff[k_comebackpoints] += ptadd; if (ptadd > 1) - special->target->player->kartstuff[k_yougotem] = 2*TICRATE; + special->target->player->karthud[khud_yougotem] = 2*TICRATE; if (special->target->player->kartstuff[k_comebackpoints] >= 2) K_StealBumper(special->target->player, player, true); @@ -557,7 +557,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->target->player->kartstuff[k_comebackpoints] += ptadd; if (ptadd > 1) - special->target->player->kartstuff[k_yougotem] = 2*TICRATE; + special->target->player->karthud[khud_yougotem] = 2*TICRATE; if (special->target->player->kartstuff[k_comebackpoints] >= 2) K_StealBumper(special->target->player, player, true); @@ -687,15 +687,34 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) /* FALLTHRU */ case MT_RING: case MT_FLINGRING: + if (special->extravalue1) + return; + + // No picking up rings while SPB is targetting you + if (player->kartstuff[k_ringlock]) + return; + + // Don't immediately pick up spilled rings + if (special->threshold > 0 + || player->kartstuff[k_squishedtimer] + || player->kartstuff[k_spinouttimer]) + return; + if (!(P_CanPickupItem(player, 0))) return; - special->momx = special->momy = special->momz = 0; - P_GivePlayerRings(player, 1); + // Reached the cap, don't waste 'em! + if ((player->kartstuff[k_rings] + player->kartstuff[k_pickuprings]) >= 20) + return; - if ((maptol & TOL_NIGHTS) && special->type != MT_FLINGRING) - P_DoNightsScore(player); - break; + special->momx = special->momy = special->momz = 0; + + special->extravalue1 = 1; // Ring collect animation timer + special->angle = R_PointToAngle2(toucher->x, toucher->y, special->x, special->y); // animation angle + P_SetTarget(&special->target, toucher); // toucher for thinker + player->kartstuff[k_pickuprings]++; + + return; case MT_COIN: case MT_FLINGCOIN: @@ -2816,16 +2835,8 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou return true; } - if (target->health <= 1) // Death - { - P_PlayDeathSound(target); - P_PlayVictorySound(source); // Killer laughs at you! LAUGHS! BWAHAHAHHAHAA!! - } - else if (target->health > 1) // Ring loss - { - P_PlayRinglossSound(target); - P_PlayerRingBurst(player, player->mo->health - 1); - } + P_PlayRinglossSound(target); + P_PlayerRingBurst(player, 5); if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])) { @@ -2881,14 +2892,9 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) { - player->pflags &= ~(PF_CARRIED|PF_SLIDING|PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG|PF_NIGHTSMODE); + (void)source; - // Burst weapons and emeralds in Match/CTF only - if (source && (G_BattleGametype())) - { - P_PlayerRingBurst(player, player->health - 1); - P_PlayerEmeraldBurst(player, false); - } + player->pflags &= ~(PF_CARRIED|PF_SLIDING|PF_ITEMHANG|PF_MACESPIN|PF_ROPEHANG|PF_NIGHTSMODE); // Get rid of shield player->powers[pw_shield] = SH_NONE; @@ -2904,32 +2910,6 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) P_SetPlayerMobjState(player->mo, player->mo->info->deathstate); - /*if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) - { - P_PlayerFlagBurst(player, false); - if (source && source->player) - { - // Award no points when players shoot each other when cv_friendlyfire is on. - if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) - P_AddPlayerScore(source->player, 1); - } - } - if (source && source->player && !player->powers[pw_super]) //don't score points against super players - { - // Award no points when players shoot each other when cv_friendlyfire is on. - if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) - P_AddPlayerScore(source->player, 1); - } - - // If the player was super, tell them he/she ain't so super nomore. - if (gametype != GT_COOP && player->powers[pw_super]) - { - S_StartSound(NULL, sfx_s3k66); //let all players hear it. - HU_SetCEchoFlags(0); - HU_SetCEchoDuration(5); - HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players])); - }*/ - if (player->pflags & PF_TIMEOVER) { mobj_t *boom; @@ -2990,75 +2970,6 @@ void P_RemoveShield(player_t *player) player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK; } -/* -static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage) // SRB2kart - unused. -{ - // Must do pain first to set flashing -- P_RemoveShield can cause damage - P_DoPlayerPain(player, source, inflictor); - - P_RemoveShield(player); - - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); - - if (source && (source->type == MT_SPIKE || (source->type == MT_NULL && source->threshold == 43))) // spikes - S_StartSound(player->mo, sfx_spkdth); - else - S_StartSound (player->mo, sfx_shldls); // Ba-Dum! Shield loss. - - if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) - { - P_PlayerFlagBurst(player, false); - if (source && source->player) - { - // Award no points when players shoot each other when cv_friendlyfire is on. - if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) - P_AddPlayerScore(source->player, 25); - } - } - if (source && source->player && !player->powers[pw_super]) //don't score points against super players - { - // Award no points when players shoot each other when cv_friendlyfire is on. - if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) - P_AddPlayerScore(source->player, cv_match_scoring.value == 1 ? 25 : 50); - } -} -*/ - -static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage) -{ - //const UINT8 scoremultiply = ((K_IsWantedPlayer(player) && !trapitem) : 2 ? 1); - - if (!(inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) - { - P_DoPlayerPain(player, source, inflictor); - - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); - - if (source && (source->type == MT_SPIKE || (source->type == MT_NULL && source->threshold == 43))) // spikes - S_StartSound(player->mo, sfx_spkdth); - } - - /*if (source && source->player && !player->powers[pw_super]) //don't score points against super players - { - // Award no points when players shoot each other when cv_friendlyfire is on. - if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) - P_AddPlayerScore(source->player, scoremultiply); - } - - if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) - { - P_PlayerFlagBurst(player, false); - if (source && source->player) - { - // Award no points when players shoot each other when cv_friendlyfire is on. - if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) - P_AddPlayerScore(source->player, scoremultiply); - } - }*/ - - // Ring loss sound plays despite hitting spikes - P_PlayRinglossSound(player->mo); // Ringledingle! -} /** Damages an object, which may or may not be a player. * For melee attacks, source and inflictor are the same. @@ -3276,121 +3187,21 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da || inflictor->type == MT_SMK_THWOMP || inflictor->player)) { player->kartstuff[k_sneakertimer] = 0; + K_SpinPlayer(player, source, 1, inflictor, false); - damage = player->mo->health - 1; - P_RingDamage(player, inflictor, source, damage); - P_PlayerRingBurst(player, 5); - if (P_IsLocalPlayer(player)) + K_KartPainEnergyFling(player); + + if (P_IsDisplayPlayer(player)) { quake.intensity = 32*FRACUNIT; quake.time = 5; } } else - { K_SpinPlayer(player, source, 0, inflictor, false); - } + return true; } - /* // SRB2kart - don't need these - else if (metalrecording) - { - if (!inflictor) - inflictor = source; - if (inflictor && inflictor->flags & MF_ENEMY) - { // Metal Sonic destroy enemy !! - P_KillMobj(inflictor, NULL, target); - return false; - } - else if (inflictor && inflictor->flags & MF_MISSILE) - return false; // Metal Sonic walk through flame !! - else - { // Oh no! Metal Sonic is hit !! - P_ShieldDamage(player, inflictor, source, damage); - return true; - } - } - else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] // ignore bouncing & such in invulnerability - || (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player)))) - { - if (force || (inflictor && (inflictor->flags & MF_MISSILE) - && (inflictor->flags2 & MF2_SUPERFIRE) - && player->powers[pw_super])) - { -#ifdef HAVE_BLUA - if (!LUAh_MobjDamage(target, inflictor, source, damage)) -#endif - P_SuperDamage(player, inflictor, source, damage); - return true; - } - else - return false; - } -#ifdef HAVE_BLUA - else if (LUAh_MobjDamage(target, inflictor, source, damage)) - return true; -#endif - else if (!player->powers[pw_super] && (player->powers[pw_shield] || player->bot)) //If One-Hit Shield - { - P_ShieldDamage(player, inflictor, source, damage); - damage = 0; - } - else if (player->mo->health > 1) // No shield but have rings. - { - damage = player->mo->health - 1; - P_RingDamage(player, inflictor, source, damage); - } - else // No shield, no rings, no invincibility. - { - // To reduce griefing potential, don't allow players to be killed - // by friendly fire. Spilling their rings and other items is enough. - if (force || !(G_GametypeHasTeams() - && source && source->player && (source->player->ctfteam == player->ctfteam) - && cv_friendlyfire.value)) - { - damage = 1; - P_KillPlayer(player, source, damage); - } - else - { - damage = 0; - P_ShieldDamage(player, inflictor, source, damage); - } - } - */ - - if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])) - { - if (player->powers[pw_shield]) - { - P_RemoveShield(player); - return true; - } - else - { - player->health -= (10 * (1 << (INT32)(player->powers[pw_super] / 10500))); - if (player->health < 2) - player->health = 2; - } - - if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) - P_PlayerFlagBurst(player, false); - } - else - { - player->health -= damage; // mirror mobj health here - if (damage < 10000) - { - target->player->powers[pw_flashing] = K_GetKartFlashing(target->player); - if (damage > 0) // don't spill emeralds/ammo/panels for shield damage - P_PlayerRingBurst(player, damage); - } - } - - if (player->health < 0) - player->health = 0; - - P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); } // Killing dead. Just for kicks. @@ -3465,7 +3276,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da /** Spills an injured player's rings. * * \param player The player who is losing rings. - * \param num_rings Number of rings lost. A maximum of 32 rings will be + * \param num_rings Number of rings lost. A maximum of 20 rings will be * spawned. * \sa P_PlayerFlagBurst */ @@ -3476,32 +3287,38 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) angle_t fa; fixed_t ns; fixed_t z; + fixed_t momxy = 5<mo->health <= 1) - num_rings = 5; + // Has a shield? Don't lose your rings! + if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) + return; - if (num_rings > 32 && !(player->pflags & PF_NIGHTSFALL)) - num_rings = 32; + // 20 is the ring cap in kart + if (num_rings > 20) + num_rings = 20; + else if (num_rings <= 0) + return; - if (player->powers[pw_emeralds]) - P_PlayerEmeraldBurst(player, false); + // Cap the maximum loss automatically to 2 in ring debt + if (player->kartstuff[k_rings] <= 0 && num_rings > 2) + num_rings = 2; - // Spill weapons first - if (player->ringweapons) - P_PlayerWeaponPanelBurst(player); + P_GivePlayerRings(player, -num_rings); - // Spill the ammo - P_PlayerWeaponAmmoBurst(player); + // determine first angle + fa = player->mo->angle + ((P_RandomByte() & 1) ? -ANGLE_90 : ANGLE_90); - // There's no ring spilling in kart, so I'm hijacking this for the same thing as TD for (i = 0; i < num_rings; i++) { - INT32 objType = mobjinfo[MT_FLINGENERGY].reactiontime; + INT32 objType = mobjinfo[MT_RING].reactiontime; z = player->mo->z; if (player->mo->eflags & MFE_VERTICALFLIP) @@ -3509,379 +3326,34 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) mo = P_SpawnMobj(player->mo->x, player->mo->y, z, objType); - mo->fuse = 8*TICRATE; + mo->threshold = 10; + mo->fuse = 120*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 rings - fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT) - ((num_rings-1)*FINEANGLES/32)) & FINEMASK; - - // Make rings spill out around the player in 16 directions like SA, but spill like Sonic 2. - // Technically a non-SA way of spilling rings. They just so happen to be a little similar. - if (player->pflags & PF_NIGHTSFALL) + // Angle / height offset changes every other ring + if (i != 0) { - ns = FixedMul(((i*FRACUNIT)/16)+2*FRACUNIT, mo->scale); - mo->momx = FixedMul(FINECOSINE(fa),ns); - - if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) - mo->momy = FixedMul(FINESINE(fa),ns); - - P_SetObjectMomZ(mo, 8*FRACUNIT, false); - mo->fuse = 20*TICRATE; // Adjust fuse for NiGHTS - } - else - { - fixed_t momxy, momz; // base horizonal/vertical thrusts - - 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); - - if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) - mo->momy = FixedMul(FINESINE(fa),ns); - - ns = momz; - P_SetObjectMomZ(mo, ns, false); - if (i & 1) - P_SetObjectMomZ(mo, ns, true); + { + momxy -= FRACUNIT; + momz += 2<scale); + mo->momx = (mo->target->momx/2) + FixedMul(FINECOSINE(fa>>ANGLETOFINESHIFT), ns); + mo->momy = (mo->target->momy/2) + FixedMul(FINESINE(fa>>ANGLETOFINESHIFT), ns); + + ns = FixedMul(momz, mo->scale); + P_SetObjectMomZ(mo, (mo->target->momz/2) + ns, false); + if (player->mo->eflags & MFE_VERTICALFLIP) mo->momz *= -1; } - - player->losstime += 10*TICRATE; - - if (P_IsObjectOnGround(player->mo)) - player->pflags &= ~PF_NIGHTSFALL; - - return; -} - -void P_PlayerWeaponPanelBurst(player_t *player) -{ - mobj_t *mo; - angle_t fa; - fixed_t ns; - INT32 i; - fixed_t z; - - INT32 num_weapons = M_CountBits((UINT32)player->ringweapons, NUM_WEAPONS-1); - UINT16 ammoamt = 0; - - for (i = 0; i < num_weapons; i++) - { - mobjtype_t weptype = 0; - powertype_t power = 0; - - if (player->ringweapons & RW_BOUNCE) // Bounce - { - weptype = MT_BOUNCEPICKUP; - player->ringweapons &= ~RW_BOUNCE; - power = pw_bouncering; - } - else if (player->ringweapons & RW_RAIL) // Rail - { - weptype = MT_RAILPICKUP; - player->ringweapons &= ~RW_RAIL; - power = pw_railring; - } - else if (player->ringweapons & RW_AUTO) // Auto - { - weptype = MT_AUTOPICKUP; - player->ringweapons &= ~RW_AUTO; - power = pw_automaticring; - } - else if (player->ringweapons & RW_EXPLODE) // Explode - { - weptype = MT_EXPLODEPICKUP; - player->ringweapons &= ~RW_EXPLODE; - power = pw_explosionring; - } - else if (player->ringweapons & RW_SCATTER) // Scatter - { - weptype = MT_SCATTERPICKUP; - player->ringweapons &= ~RW_SCATTER; - power = pw_scatterring; - } - else if (player->ringweapons & RW_GRENADE) // Grenade - { - weptype = MT_GRENADEPICKUP; - player->ringweapons &= ~RW_GRENADE; - power = pw_grenadering; - } - - if (!weptype) // ??? - continue; - - if (player->powers[power] >= mobjinfo[weptype].reactiontime) - ammoamt = (UINT16)mobjinfo[weptype].reactiontime; - else - ammoamt = player->powers[power]; - - player->powers[power] -= ammoamt; - - z = player->mo->z; - if (player->mo->eflags & MFE_VERTICALFLIP) - z += player->mo->height - mobjinfo[weptype].height; - - mo = P_SpawnMobj(player->mo->x, player->mo->y, z, weptype); - mo->reactiontime = ammoamt; - mo->flags2 |= MF2_DONTRESPAWN; - mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); - P_SetTarget(&mo->target, player->mo); - mo->fuse = 12*TICRATE; - mo->destscale = player->mo->scale; - P_SetScale(mo, player->mo->scale); - - // Angle offset by player angle - fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT)) & FINEMASK; - - // Make rings spill out around the player in 16 directions like SA, but spill like Sonic 2. - // Technically a non-SA way of spilling rings. They just so happen to be a little similar. - - // >16 ring type spillout - ns = FixedMul(3*FRACUNIT, mo->scale); - mo->momx = FixedMul(FINECOSINE(fa),ns); - - if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) - mo->momy = FixedMul(FINESINE(fa),ns); - - P_SetObjectMomZ(mo, 4*FRACUNIT, false); - - if (i & 1) - P_SetObjectMomZ(mo, 4*FRACUNIT, true); - } -} - -void P_PlayerWeaponAmmoBurst(player_t *player) -{ - mobj_t *mo; - angle_t fa; - fixed_t ns; - INT32 i = 0; - fixed_t z; - - mobjtype_t weptype = 0; - powertype_t power = 0; - - while (true) - { - if (player->powers[pw_bouncering]) - { - weptype = MT_BOUNCERING; - power = pw_bouncering; - } - else if (player->powers[pw_railring]) - { - weptype = MT_RAILRING; - power = pw_railring; - } - else if (player->powers[pw_infinityring]) - { - weptype = MT_INFINITYRING; - power = pw_infinityring; - } - else if (player->powers[pw_automaticring]) - { - weptype = MT_AUTOMATICRING; - power = pw_automaticring; - } - else if (player->powers[pw_explosionring]) - { - weptype = MT_EXPLOSIONRING; - power = pw_explosionring; - } - else if (player->powers[pw_scatterring]) - { - weptype = MT_SCATTERRING; - power = pw_scatterring; - } - else if (player->powers[pw_grenadering]) - { - weptype = MT_GRENADERING; - power = pw_grenadering; - } - else - break; // All done! - - z = player->mo->z; - if (player->mo->eflags & MFE_VERTICALFLIP) - z += player->mo->height - mobjinfo[weptype].height; - - mo = P_SpawnMobj(player->mo->x, player->mo->y, z, weptype); - mo->health = player->powers[power]; - mo->flags2 |= MF2_DONTRESPAWN; - mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); - P_SetTarget(&mo->target, player->mo); - - player->powers[power] = 0; - mo->fuse = 12*TICRATE; - - mo->destscale = player->mo->scale; - P_SetScale(mo, player->mo->scale); - - // Angle offset by player angle - fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT)) & FINEMASK; - - // Spill them! - ns = FixedMul(2*FRACUNIT, mo->scale); - mo->momx = FixedMul(FINECOSINE(fa), ns); - - if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) - mo->momy = FixedMul(FINESINE(fa),ns); - - P_SetObjectMomZ(mo, 3*FRACUNIT, false); - - if (i & 1) - P_SetObjectMomZ(mo, 3*FRACUNIT, true); - - i++; - } -} - -// -// P_PlayerEmeraldBurst -// -// Spills ONLY emeralds. -// -void P_PlayerEmeraldBurst(player_t *player, boolean toss) -{ - INT32 i; - angle_t fa; - fixed_t ns; - fixed_t z = 0, momx = 0, momy = 0; - - // Better safe than sorry. - if (!player) - return; - - // Spill power stones - if (player->powers[pw_emeralds]) - { - INT32 num_stones = 0; - - if (player->powers[pw_emeralds] & EMERALD1) - num_stones++; - if (player->powers[pw_emeralds] & EMERALD2) - num_stones++; - if (player->powers[pw_emeralds] & EMERALD3) - num_stones++; - if (player->powers[pw_emeralds] & EMERALD4) - num_stones++; - if (player->powers[pw_emeralds] & EMERALD5) - num_stones++; - if (player->powers[pw_emeralds] & EMERALD6) - num_stones++; - if (player->powers[pw_emeralds] & EMERALD7) - num_stones++; - - for (i = 0; i < num_stones; i++) - { - INT32 stoneflag = 0; - statenum_t statenum = S_CEMG1; - mobj_t *mo; - - if (player->powers[pw_emeralds] & EMERALD1) - { - stoneflag = EMERALD1; - statenum = S_CEMG1; - } - else if (player->powers[pw_emeralds] & EMERALD2) - { - stoneflag = EMERALD2; - statenum = S_CEMG2; - } - else if (player->powers[pw_emeralds] & EMERALD3) - { - stoneflag = EMERALD3; - statenum = S_CEMG3; - } - else if (player->powers[pw_emeralds] & EMERALD4) - { - stoneflag = EMERALD4; - statenum = S_CEMG4; - } - else if (player->powers[pw_emeralds] & EMERALD5) - { - stoneflag = EMERALD5; - statenum = S_CEMG5; - } - else if (player->powers[pw_emeralds] & EMERALD6) - { - stoneflag = EMERALD6; - statenum = S_CEMG6; - } - else if (player->powers[pw_emeralds] & EMERALD7) - { - stoneflag = EMERALD7; - statenum = S_CEMG7; - } - - if (!stoneflag) // ??? - continue; - - player->powers[pw_emeralds] &= ~stoneflag; - - if (toss) - { - fa = player->mo->angle>>ANGLETOFINESHIFT; - - z = player->mo->z + player->mo->height; - if (player->mo->eflags & MFE_VERTICALFLIP) - z -= mobjinfo[MT_FLINGEMERALD].height + player->mo->height; - ns = FixedMul(8*FRACUNIT, player->mo->scale); - } - else - { - fa = ((255 / num_stones) * i) * FINEANGLES/256; - - z = player->mo->z + (player->mo->height / 2); - if (player->mo->eflags & MFE_VERTICALFLIP) - z -= mobjinfo[MT_FLINGEMERALD].height; - ns = FixedMul(4*FRACUNIT, player->mo->scale); - } - - momx = FixedMul(FINECOSINE(fa), ns); - - if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) - momy = FixedMul(FINESINE(fa),ns); - else - momy = 0; - - mo = P_SpawnMobj(player->mo->x, player->mo->y, z, MT_FLINGEMERALD); - mo->health = 1; - mo->threshold = stoneflag; - mo->flags2 |= (MF2_DONTRESPAWN|MF2_SLIDEPUSH); - mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); - P_SetTarget(&mo->target, player->mo); - mo->fuse = 12*TICRATE; - P_SetMobjState(mo, statenum); - - mo->momx = momx; - mo->momy = momy; - - P_SetObjectMomZ(mo, 3*FRACUNIT, false); - - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->momz = -mo->momz; - - if (toss) - player->tossdelay = 2*TICRATE; - } - } } /** Makes an injured or dead player lose possession of the flag. diff --git a/src/p_local.h b/src/p_local.h index 0d0ddc89b..bc25affd4 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -390,9 +390,6 @@ void P_RemoveShield(player_t *player); boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source); void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c -void P_PlayerWeaponPanelBurst(player_t *player); -void P_PlayerWeaponAmmoBurst(player_t *player); -void P_PlayerEmeraldBurst(player_t *player, boolean toss); void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck); void P_PlayerFlagBurst(player_t *player, boolean toss); diff --git a/src/p_map.c b/src/p_map.c index d9b723650..d99105005 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1557,39 +1557,50 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; } - if (P_IsObjectOnGround(thing) && tmthing->momz < 0) { - K_KartBouncing(tmthing, thing, true, false); - if (G_BattleGametype() && tmthing->player->kartstuff[k_pogospring]) - { - K_StealBumper(tmthing->player, thing->player, false); - K_SpinPlayer(thing->player, tmthing, 0, tmthing, false); - } - } - else if (P_IsObjectOnGround(tmthing) && thing->momz < 0) - { - K_KartBouncing(thing, tmthing, true, false); - if (G_BattleGametype() && thing->player->kartstuff[k_pogospring]) - { - K_StealBumper(thing->player, tmthing->player, false); - K_SpinPlayer(tmthing->player, thing, 0, thing, false); - } - } - else - K_KartBouncing(tmthing, thing, false, false); + // The bump has to happen last + mobj_t *mo1 = tmthing; + mobj_t *mo2 = thing; + boolean zbounce = false; - if (G_BattleGametype()) - { - if (thing->player->kartstuff[k_sneakertimer] && !(tmthing->player->kartstuff[k_sneakertimer]) && !(thing->player->powers[pw_flashing])) // Don't steal bumpers while intangible + if (P_IsObjectOnGround(thing) && tmthing->momz < 0) { - K_StealBumper(thing->player, tmthing->player, false); - K_SpinPlayer(tmthing->player, thing, 0, tmthing, false); + zbounce = true; + mo1 = thing; + mo2 = tmthing; + + if (G_BattleGametype() && tmthing->player->kartstuff[k_pogospring]) + { + K_StealBumper(tmthing->player, thing->player, false); + K_SpinPlayer(thing->player, tmthing, 0, tmthing, false); + } } - else if (tmthing->player->kartstuff[k_sneakertimer] && !(thing->player->kartstuff[k_sneakertimer]) && !(tmthing->player->powers[pw_flashing])) + else if (P_IsObjectOnGround(tmthing) && thing->momz < 0) { - K_StealBumper(tmthing->player, thing->player, false); - K_SpinPlayer(thing->player, tmthing, 0, thing, false); + zbounce = true; + + if (G_BattleGametype() && thing->player->kartstuff[k_pogospring]) + { + K_StealBumper(thing->player, tmthing->player, false); + K_SpinPlayer(tmthing->player, thing, 0, thing, false); + } } + + if (G_BattleGametype()) + { + if (thing->player->kartstuff[k_sneakertimer] && !(tmthing->player->kartstuff[k_sneakertimer]) && !(thing->player->powers[pw_flashing])) // Don't steal bumpers while intangible + { + K_StealBumper(thing->player, tmthing->player, false); + K_SpinPlayer(tmthing->player, thing, 0, tmthing, false); + } + else if (tmthing->player->kartstuff[k_sneakertimer] && !(thing->player->kartstuff[k_sneakertimer]) && !(tmthing->player->powers[pw_flashing])) + { + K_StealBumper(tmthing->player, thing->player, false); + K_SpinPlayer(thing->player, tmthing, 0, thing, false); + } + } + + K_KartBouncing(mo1, mo2, zbounce, false); } return true; diff --git a/src/p_mobj.c b/src/p_mobj.c index 1d0224590..e8880f07b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -85,6 +85,37 @@ void P_AddCachedAction(mobj_t *mobj, INT32 statenum) actioncachehead.prev = newaction; } +// +// P_SetupStateAnimation +// +FUNCINLINE static ATTRINLINE void P_SetupStateAnimation(mobj_t *mobj, state_t *st) +{ + if (!(st->frame & FF_ANIMATE)) + return; + + if (st->var1 <= 0 || st->var2 == 0) + { + mobj->frame &= ~FF_ANIMATE; + return; // Crash/stupidity prevention + } + + mobj->anim_duration = (UINT16)st->var2; + + if (st->frame & FF_GLOBALANIM) + { + // Attempt to account for the pre-ticker for objects spawned on load + if (!leveltime) return; + + mobj->anim_duration -= (leveltime + 2) % st->var2; // Duration synced to timer + mobj->frame += ((leveltime + 2) / st->var2) % (st->var1 + 1); // Frame synced to timer (duration taken into account) + } + else if (st->frame & FF_RANDOMANIM) + { + mobj->frame += P_RandomKey(st->var1 + 1); // Random starting frame + mobj->anim_duration -= P_RandomKey(st->var2); // Random duration for first frame + } +} + // // P_CycleStateAnimation // @@ -93,6 +124,7 @@ FUNCINLINE static ATTRINLINE void P_CycleStateAnimation(mobj_t *mobj) // var2 determines delay between animation frames if (!(mobj->frame & FF_ANIMATE) || --mobj->anim_duration != 0) return; + mobj->anim_duration = (UINT16)mobj->state->var2; // compare the current sprite frame to the one we started from @@ -212,7 +244,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; - mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set + P_SetupStateAnimation(mobj, st); // Modified handling. // Call action functions when the state is set @@ -280,7 +312,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state) mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; - mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set + P_SetupStateAnimation(mobj, st); // Modified handling. // Call action functions when the state is set @@ -334,7 +366,7 @@ boolean P_SetMobjStateNF(mobj_t *mobj, statenum_t state) mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; - mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set + P_SetupStateAnimation(mobj, st); return true; } @@ -353,7 +385,7 @@ static boolean P_SetPrecipMobjState(precipmobj_t *mobj, statenum_t state) mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; - mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set + P_SetupStateAnimation((mobj_t*)mobj, st); return true; } @@ -1905,7 +1937,7 @@ void P_XYMovement(mobj_t *mo) #endif //{ SRB2kart stuff - if (mo->type == MT_ORBINAUT || mo->type == MT_JAWZ_DUD || mo->type == MT_JAWZ || mo->type == MT_BALLHOG) //(mo->type == MT_JAWZ && !mo->tracer)) + if (mo->type == MT_BALLHOG || mo->type == MT_FLINGRING) //(mo->type == MT_JAWZ && !mo->tracer)) return; if (mo->player && (mo->player->kartstuff[k_spinouttimer] && !mo->player->kartstuff[k_wipeoutslow]) && mo->player->speed <= K_GetKartSpeed(mo->player, false)/2) @@ -2456,11 +2488,11 @@ static boolean P_ZMovement(mobj_t *mo) mom.z = -mom.z; else // Flingrings bounce - if (mo->type == MT_FLINGRING + if (/*mo->type == MT_FLINGRING || mo->type == MT_FLINGCOIN || P_WeaponOrPanel(mo->type) || mo->type == MT_FLINGEMERALD - || mo->type == MT_BIGTUMBLEWEED + ||*/ mo->type == MT_BIGTUMBLEWEED || mo->type == MT_LITTLETUMBLEWEED || mo->type == MT_CANNONBALLDECOR || mo->type == MT_FALLINGROCK) @@ -3592,7 +3624,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled dummy.z = thiscam->z; dummy.height = thiscam->height; if (player->pflags & PF_TIMEOVER) - player->kartstuff[k_timeovercam] = (2*TICRATE)+1; + player->karthud[khud_timeovercam] = (2*TICRATE)+1; if (!resetcalled && !(player->pflags & PF_NOCLIP || leveltime < introtime) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead. P_ResetCamera(player, thiscam); else @@ -6670,7 +6702,7 @@ void P_MobjThinker(mobj_t *mobj) if ((G_RaceGametype() || mobj->target->player->kartstuff[k_bumper] <= 0) #if 1 // Set to 0 to test without needing to host - || ((mobj->target->player == &players[displayplayers[0]]) || P_IsLocalPlayer(mobj->target->player)) + || (P_IsDisplayPlayer(mobj->target->player)) #endif ) mobj->flags2 |= MF2_DONTDRAW; @@ -7903,8 +7935,17 @@ void P_MobjThinker(mobj_t *mobj) else { fixed_t finalspeed = mobj->movefactor; + const fixed_t currentspeed = R_PointToDist2(0, 0, mobj->momx, mobj->momy); + fixed_t thrustamount = 0; + fixed_t frictionsafety = (mobj->friction == 0) ? 1 : mobj->friction; + mobj_t *ghost = P_SpawnGhostMobj(mobj); + ghost->colorized = true; // already has color! - P_SpawnGhostMobj(mobj); + if (!grounded) + { + // No friction in the air + frictionsafety = FRACUNIT; + } mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); if (mobj->health <= 5) @@ -7914,7 +7955,19 @@ void P_MobjThinker(mobj_t *mobj) finalspeed = FixedMul(finalspeed, FRACUNIT-FRACUNIT/4); } - P_InstaThrust(mobj, mobj->angle, finalspeed); + if (currentspeed >= finalspeed) + { + // Thrust as if you were at top speed, slow down naturally + thrustamount = FixedDiv(finalspeed, frictionsafety) - finalspeed; + } + else + { + const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; + // Thrust to immediately get to top speed + thrustamount = beatfriction + FixedDiv(finalspeed - currentspeed, frictionsafety); + } + + P_Thrust(mobj, mobj->angle, thrustamount); if (grounded) { @@ -7936,51 +7989,20 @@ void P_MobjThinker(mobj_t *mobj) case MT_JAWZ: { sector_t *sec2; - fixed_t topspeed = mobj->movefactor; - fixed_t distbarrier = 512*mapobjectscale; - fixed_t distaway; + mobj_t *ghost = P_SpawnGhostMobj(mobj); - P_SpawnGhostMobj(mobj); + if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) + { + ghost->color = mobj->target->player->skincolor; + ghost->colorized = true; + } if (mobj->threshold > 0) mobj->threshold--; if (leveltime % TICRATE == 0) S_StartSound(mobj, mobj->info->activesound); - if (gamespeed == 0) - distbarrier = FixedMul(distbarrier, FRACUNIT-FRACUNIT/4); - else if (gamespeed == 2) - distbarrier = FixedMul(distbarrier, FRACUNIT+FRACUNIT/4); - - if (G_RaceGametype() && mobj->tracer) - { - distaway = P_AproxDistance(mobj->tracer->x - mobj->x, mobj->tracer->y - mobj->y); - if (distaway < distbarrier) - { - if (mobj->tracer->player) - { - fixed_t speeddifference = abs(topspeed - min(mobj->tracer->player->speed, K_GetKartSpeed(mobj->tracer->player, false))); - topspeed = topspeed - FixedMul(speeddifference, FRACUNIT-FixedDiv(distaway, distbarrier)); - } - } - } - - if (G_BattleGametype()) - { - mobj->friction -= 1228; - if (mobj->friction > FRACUNIT) - mobj->friction = FRACUNIT; - if (mobj->friction < 0) - mobj->friction = 0; - } - - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); - P_InstaThrust(mobj, mobj->angle, topspeed); - - if (mobj->tracer) - mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->tracer->x, mobj->tracer->y); - else - mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); + // Movement handling has ALL been moved to A_JawzChase K_DriftDustHandling(mobj); @@ -8007,9 +8029,37 @@ void P_MobjThinker(mobj_t *mobj) } else { - P_SpawnGhostMobj(mobj); + mobj_t *ghost = P_SpawnGhostMobj(mobj); + const fixed_t currentspeed = R_PointToDist2(0, 0, mobj->momx, mobj->momy); + fixed_t frictionsafety = (mobj->friction == 0) ? 1 : mobj->friction; + fixed_t thrustamount = 0; + + if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) + { + ghost->color = mobj->target->player->skincolor; + ghost->colorized = true; + } + + if (!grounded) + { + // No friction in the air + frictionsafety = FRACUNIT; + } + + if (currentspeed >= mobj->movefactor) + { + // Thrust as if you were at top speed, slow down naturally + thrustamount = FixedDiv(mobj->movefactor, frictionsafety) - mobj->movefactor; + } + else + { + const fixed_t beatfriction = FixedDiv(currentspeed, frictionsafety) - currentspeed; + // Thrust to immediately get to top speed + thrustamount = beatfriction + FixedDiv(mobj->movefactor - currentspeed, frictionsafety); + } + mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy); - P_InstaThrust(mobj, mobj->angle, mobj->movefactor); + P_Thrust(mobj, mobj->angle, thrustamount); if (grounded) { @@ -8032,14 +8082,25 @@ void P_MobjThinker(mobj_t *mobj) case MT_BANANA: case MT_EGGMANITEM: mobj->friction = ORIG_FRICTION/4; + if (mobj->momx || mobj->momy) - P_SpawnGhostMobj(mobj); + { + mobj_t *ghost = P_SpawnGhostMobj(mobj); + + if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) + { + ghost->color = mobj->target->player->skincolor; + ghost->colorized = true; + } + } + if (P_IsObjectOnGround(mobj) && mobj->health > 1) { S_StartSound(mobj, mobj->info->activesound); mobj->momx = mobj->momy = 0; mobj->health = 1; } + if (mobj->threshold > 0) mobj->threshold--; break; @@ -8047,18 +8108,38 @@ void P_MobjThinker(mobj_t *mobj) indirectitemcooldown = 20*TICRATE; /* FALLTHRU */ case MT_BALLHOG: - P_SpawnGhostMobj(mobj)->fuse = 3; - if (mobj->threshold > 0) - mobj->threshold--; + { + mobj_t *ghost = P_SpawnGhostMobj(mobj); + ghost->fuse = 3; + + if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) + { + ghost->color = mobj->target->player->skincolor; + ghost->colorized = true; + } + + if (mobj->threshold > 0) + mobj->threshold--; + } break; case MT_SINK: if (mobj->momx || mobj->momy) - P_SpawnGhostMobj(mobj); + { + mobj_t *ghost = P_SpawnGhostMobj(mobj); + + if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player) + { + ghost->color = mobj->target->player->skincolor; + ghost->colorized = true; + } + } + if (P_IsObjectOnGround(mobj)) { S_StartSound(mobj, mobj->info->deathsound); P_SetMobjState(mobj, S_NULL); } + if (mobj->threshold > 0) mobj->threshold--; break; @@ -8069,7 +8150,10 @@ void P_MobjThinker(mobj_t *mobj) mobj->color = SKINCOLOR_KETCHUP; if (mobj->momx || mobj->momy) - P_SpawnGhostMobj(mobj); + { + mobj_t *ghost = P_SpawnGhostMobj(mobj); + ghost->colorized = true; // already has color! + } if (P_IsObjectOnGround(mobj) && (mobj->state == &states[S_SSMINE_AIR1] || mobj->state == &states[S_SSMINE_AIR2])) { @@ -8142,9 +8226,10 @@ void P_MobjThinker(mobj_t *mobj) if (p) { - if (p->kartstuff[k_sneakertimer] > mobj->movecount) + if (p->kartstuff[k_sneakertimer] > mobj->movecount + || p->kartstuff[k_levelbooster] > mobj->movecount) P_SetMobjState(mobj, S_BOOSTFLAME); - mobj->movecount = p->kartstuff[k_sneakertimer]; + mobj->movecount = max(p->kartstuff[k_sneakertimer], p->kartstuff[k_levelbooster]); } } @@ -8263,6 +8348,26 @@ void P_MobjThinker(mobj_t *mobj) K_MatchGenericExtraFlags(mobj, mobj->target); P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height/2) + mobj->movefactor); break; + case MT_RINGSPARKS: + if (!mobj->target || P_MobjWasRemoved(mobj->target)) + { + P_RemoveMobj(mobj); + return; + } + + K_MatchGenericExtraFlags(mobj, mobj->target); + { + fixed_t z; + z = mobj->target->z; + if (( mobj->eflags & MFE_VERTICALFLIP )) + z -= mobj->height; + else + z += mobj->target->height; + P_TeleportMove(mobj, mobj->target->x + FINECOSINE(mobj->angle >> ANGLETOFINESHIFT), + mobj->target->y + FINESINE(mobj->angle >> ANGLETOFINESHIFT), + z); + } + break; case MT_THUNDERSHIELD: { fixed_t destx, desty; @@ -9351,7 +9456,9 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s || mobj->type == MT_BIGTUMBLEWEED || mobj->type == MT_LITTLETUMBLEWEED || mobj->type == MT_CANNONBALLDECOR - || mobj->type == MT_FALLINGROCK) { + || mobj->type == MT_FALLINGROCK + || mobj->type == MT_ORBINAUT + || mobj->type == MT_JAWZ || mobj->type == MT_JAWZ_DUD) { P_TryMove(mobj, mobj->x, mobj->y, true); // Sets mo->standingslope correctly //if (mobj->standingslope) CONS_Printf("slope physics on mobj\n"); P_ButteredSlope(mobj); @@ -9528,6 +9635,21 @@ void P_SceneryThinker(mobj_t *mobj) } } + // Sonic Advance 2 flashing afterimages + if (mobj->type == MT_GHOST && mobj->fuse > 0 + && 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; + else + { + if (mobj->fuse == mobj->extravalue2) + mobj->flags2 &= ~MF2_DONTDRAW; + else + mobj->flags2 |= MF2_DONTDRAW; + } + } + // momentum movement if (mobj->momx || mobj->momy) { @@ -9635,7 +9757,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits.. - mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set + P_SetupStateAnimation(mobj, st); mobj->friction = ORIG_FRICTION; @@ -10086,6 +10208,7 @@ mobj_t *P_SpawnShadowMobj(mobj_t * caster) mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits.. + P_SetupStateAnimation(mobj, st); mobj->friction = ORIG_FRICTION; @@ -10178,7 +10301,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits.. - mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set + P_SetupStateAnimation((mobj_t*)mobj, st); // set subsector and/or block links P_SetPrecipitationThingPosition(mobj); @@ -10608,6 +10731,8 @@ void P_PrecipitationEffects(void) // void P_RespawnSpecials(void) { + UINT8 p, pcount = 0; + tic_t time = 30*TICRATE; // Respawn things in empty dedicated servers fixed_t x, y, z; subsector_t *ss; mobj_t *mo = NULL; @@ -10654,21 +10779,28 @@ void P_RespawnSpecials(void) numgotboxes = 0; } - // only respawn items when cv_itemrespawn is on - if (!cv_itemrespawn.value) - return; + // wait time depends on player count + for (p = 0; p < MAXPLAYERS; p++) + { + if (playeringame[p] && !players[p].spectator) + pcount++; + } - // Don't respawn in special stages! - if (G_IsSpecialStage(gamemap)) + if (pcount == 1) // No respawn when alone return; + else if (pcount > 1) + time = (180 - (pcount * 10))*TICRATE; + + // only respawn items when cv_itemrespawn is on + //if (!cv_itemrespawn.value) // TODO: remove this cvar + //return; // nothing left to respawn? if (iquehead == iquetail) return; // the first item in the queue is the first to respawn - // wait at least 30 seconds - if (leveltime - itemrespawntime[iquetail] < (tic_t)cv_itemrespawntime.value*TICRATE) + if (leveltime - itemrespawntime[iquetail] < time) return; mthing = itemrespawnque[iquetail]; @@ -11953,7 +12085,7 @@ ML_NOCLIMB : Direction not controllable void P_SpawnHoopsAndRings(mapthing_t *mthing) { mobj_t *mobj = NULL; - INT32 /*r,*/ i; + INT32 r, i; fixed_t x, y, z, finalx, finaly, finalz; sector_t *sec; TVector v, *res; @@ -12240,8 +12372,6 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) return; } - else return; // srb2kart - no rings or ring-like objects in R1 - /* // Wing logo item. else if (mthing->type == mobjinfo[MT_NIGHTSWING].doomednum) { @@ -12535,7 +12665,8 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing) } } return; - }*/ + } + else return; } // diff --git a/src/p_pspr.h b/src/p_pspr.h index e74266002..b42692462 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -35,14 +35,9 @@ #pragma interface #endif -/// \brief Frame flags: only the frame number -#define FF_FRAMEMASK 0x1ff -/// \brief Frame flags: Thin, paper-like sprite (for collision equivalent, see MF_PAPERCOLLISION) -#define FF_PAPERSPRITE 0x800 -/// \brief Frame flags: Simple stateless animation -#define FF_ANIMATE 0x4000 -/// \brief Frame flags: frame always appears full bright -#define FF_FULLBRIGHT 0x8000 +/// \brief Frame flags: only the frame number (frames from 0 to 63, but a bit of headroom for 2.2 compat) +#define FF_FRAMEMASK 0xff + /// \brief Frame flags: 0 = no trans(opaque), 1-15 = transl. table #define FF_TRANSMASK 0xf0000 /// \brief shift for FF_TRANSMASK @@ -58,6 +53,23 @@ #define FF_TRANS80 (tr_trans80<player) { - if (abs(mo->standingslope->zdelta) < FRACUNIT/4 && !(mo->player->pflags & PF_SPINNING)) + // 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 + // This only means you can be stopped on slopes that aren't steeper than 45 degrees if (abs(mo->standingslope->zdelta) < FRACUNIT/2 && !(mo->player->rmomx || mo->player->rmomy)) return; // Allow the player to stand still on slopes below a certain steepness } - thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 15 / 16 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1); + thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 4 / 5 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1); - if (mo->player && (mo->player->pflags & PF_SPINNING)) { - fixed_t mult = 0; + if (mo->player) { + fixed_t mult = FRACUNIT; if (mo->momx || mo->momy) { angle_t angle = R_PointToAngle2(0, 0, mo->momx, mo->momy) - mo->standingslope->xydirection; if (P_MobjFlip(mo) * mo->standingslope->zdelta < 0) angle ^= ANGLE_180; - mult = FINECOSINE(angle >> ANGLETOFINESHIFT); + mult = FRACUNIT + (FRACUNIT + FINECOSINE(angle>>ANGLETOFINESHIFT))*3/2; } - thrust = FixedMul(thrust, FRACUNIT*2/3 + mult/8); + thrust = FixedMul(thrust, mult); } - if (mo->momx || mo->momy) // Slightly increase thrust based on the object's speed - thrust = FixedMul(thrust, FRACUNIT+P_AproxDistance(mo->momx, mo->momy)/16); - // This makes it harder to zigzag up steep slopes, as well as allows greater top speed when rolling down - // Let's get the gravity strength for the object... thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo))); diff --git a/src/p_spec.c b/src/p_spec.c index a2e18567d..3e654036f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4247,19 +4247,19 @@ DoneSection2: if (nump > 1) { if (K_IsPlayerLosing(player)) - player->kartstuff[k_laphand] = 3; + player->karthud[khud_laphand] = 3; else { if (nump > 2 && player->kartstuff[k_position] == 1) // 1st place in 1v1 uses thumbs up - player->kartstuff[k_laphand] = 1; + player->karthud[khud_laphand] = 1; else - player->kartstuff[k_laphand] = 2; + player->karthud[khud_laphand] = 2; } } else - player->kartstuff[k_laphand] = 0; // No hands in FREE PLAY + player->karthud[khud_laphand] = 0; // No hands in FREE PLAY - player->kartstuff[k_lapanimation] = 80; + player->karthud[khud_lapanimation] = 80; if (player->pflags & PF_NIGHTSMODE) player->drillmeter += 48*20; @@ -4295,7 +4295,7 @@ DoneSection2: player->starpostangle = player->starpostx = player->starposty = player->starpostz = player->kartstuff[k_starpostflip] = 0; } - if (P_IsLocalPlayer(player)) + if (P_IsDisplayPlayer(player)) { if (player->laps == (UINT8)(cv_numlaps.value - 1)) S_StartSound(NULL, sfx_s3k68); @@ -4321,7 +4321,7 @@ DoneSection2: if (player->laps >= (unsigned)cv_numlaps.value) { - if (P_IsLocalPlayer(player)) + if (P_IsDisplayPlayer(player)) S_StartSound(NULL, sfx_s3k6a); else if (player->kartstuff[k_position] == 1) S_StartSound(NULL, sfx_s253); @@ -6689,14 +6689,6 @@ void P_SpawnSpecials(INT32 fromnetsave) sectors[s].midmap = lines[i].frontsector->midmap; break; -#ifdef ESLOPE // Slope copy specials. Handled here for sanity. - case 720: - case 721: - case 722: - P_CopySectorSlope(&lines[i]); - break; -#endif - default: break; } @@ -7352,9 +7344,7 @@ void T_Friction(friction_t *f) // apparently, all I had to do was comment out part of the next line and // friction works for all mobj's // (or at least MF_PUSHABLEs, which is all I care about anyway) - if ((!(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) && thing->z == thing->floorz) && (thing->player - && (thing->player->kartstuff[k_invincibilitytimer] == 0 && thing->player->kartstuff[k_hyudorotimer] == 0 - && thing->player->kartstuff[k_sneakertimer] == 0 && thing->player->kartstuff[k_growshrinktimer] <= 0))) + if (!(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) && thing->z == thing->floorz) { if (f->roverfriction) { diff --git a/src/p_user.c b/src/p_user.c index 0b938b0b0..8932a49c8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -946,50 +946,19 @@ void P_ResetPlayer(player_t *player) // void P_GivePlayerRings(player_t *player, INT32 num_rings) { - if (player->bot) - player = &players[consoleplayer]; - if (!player->mo) return; - player->mo->health += num_rings; - player->health += num_rings; + if (G_BattleGametype()) // No rings in Battle Mode + return; - if (!G_IsSpecialStage(gamemap) || !useNightsSS) - player->totalring += num_rings; + player->kartstuff[k_rings] += num_rings; + //player->totalring += num_rings; // Used for GP lives later - //{ SRB2kart - rings don't really do anything, but we don't want the player spilling them later. - /* - // Can only get up to 9999 rings, sorry! - if (player->mo->health > 10000) - { - player->mo->health = 10000; - player->health = 10000; - } - else if (player->mo->health < 1)*/ - { - player->mo->health = 1; - player->health = 1; - } - //} - - // Now extra life bonuses are handled here instead of in P_MovePlayer, since why not? - if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives()) - { - INT32 gainlives = 0; - - while (player->xtralife < maxXtraLife && player->health > 100 * (player->xtralife+1)) - { - ++gainlives; - ++player->xtralife; - } - - if (gainlives) - { - P_GivePlayerLives(player, gainlives); - P_PlayLivesJingle(player); - } - } + if (player->kartstuff[k_rings] > 20) + player->kartstuff[k_rings] = 20; // Caps at 20 rings, sorry! + else if (player->kartstuff[k_rings] < -20) + player->kartstuff[k_rings] = -20; // Chaotix ring debt! } // @@ -1114,11 +1083,12 @@ void P_PlayLivesJingle(player_t *player) void P_PlayRinglossSound(mobj_t *source) { - sfxenum_t key = P_RandomKey(2); - if (cv_kartvoices.value) - S_StartSound(source, (mariomode) ? sfx_mario8 : sfx_khurt1 + key); + if (source->player && source->player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) + S_StartSound(source, sfx_s1a3); // Shield hit (no ring loss) + else if (source->player && source->player->kartstuff[k_rings] <= 0) + S_StartSound(source, sfx_s1a6); // Ring debt (lessened ring loss) else - S_StartSound(source, sfx_slip); + S_StartSound(source, sfx_s1c6); // Normal ring loss sound } void P_PlayDeathSound(mobj_t *source) @@ -1704,7 +1674,7 @@ void P_DoPlayerExit(player_t *player) if (cv_kartvoices.value) { - if (P_IsLocalPlayer(player)) + if (P_IsDisplayPlayer(player)) { sfxenum_t sfx_id; if (K_IsPlayerLosing(player)) @@ -1751,7 +1721,7 @@ void P_DoPlayerExit(player_t *player) */ player->powers[pw_underwater] = 0; player->powers[pw_spacetime] = 0; - player->kartstuff[k_cardanimation] = 0; // srb2kart: reset battle animation + player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation if (player == &players[consoleplayer]) demo.savebutton = leveltime; @@ -4039,7 +4009,7 @@ static void P_3dMovement(player_t *player) if ((player->exiting || mapreset) || player->pflags & PF_STASIS || player->kartstuff[k_spinouttimer]) // pw_introcam? { cmd->forwardmove = cmd->sidemove = 0; - if (player->kartstuff[k_sneakertimer]) + if (EITHERSNEAKER(player)) cmd->forwardmove = 50; } @@ -4129,13 +4099,6 @@ static void P_3dMovement(player_t *player) //movepushforward = cmd->forwardmove * (thrustfactor * acceleration); movepushforward = K_3dKartMovement(player, onground, cmd->forwardmove); - // allow very small movement while in air for gameplay - if (!onground) - movepushforward >>= 2; // proper air movement - - // don't need to account for scale here with kart accel code - //movepushforward = FixedMul(movepushforward, player->mo->scale); - if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... movepushforward = FixedMul(movepushforward, player->mo->movefactor); @@ -4199,6 +4162,18 @@ static void P_3dMovement(player_t *player) player->mo->momx += totalthrust.x; player->mo->momy += totalthrust.y; + + if (!onground) + { + fixed_t airspeedcap = (50*mapobjectscale); + fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy); + if (speed > airspeedcap) + { + fixed_t newspeed = speed - ((speed - airspeedcap) / 32); + player->mo->momx = FixedMul(FixedDiv(player->mo->momx, speed), newspeed); + player->mo->momy = FixedMul(FixedDiv(player->mo->momy, speed), newspeed); + } + } #endif // Time to ask three questions: @@ -4211,27 +4186,33 @@ static void P_3dMovement(player_t *player) // If "no" to 2, normalize to topspeed, so we can't suddenly run faster than it of our own accord. // If "no" to 1, we're not reaching any limits yet, so ignore this entirely! // -Shadow Hog - newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0); - if (newMagnitude > K_GetKartSpeed(player, true)) //topspeed) + // Only do this forced cap of speed when in midair, the kart acceleration code takes into account friction, and + // doesn't let you accelerate past top speed, so this is unnecessary on the ground, but in the air is needed to + // allow for being able to change direction on spring jumps without being accelerated into the void - Sryder + if (!P_IsObjectOnGround(player->mo)) { - fixed_t tempmomx, tempmomy; - if (oldMagnitude > K_GetKartSpeed(player, true) && onground) // SRB2Kart: onground check for air speed cap + newMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0); + if (newMagnitude > K_GetKartSpeed(player, true)) //topspeed) { - if (newMagnitude > oldMagnitude) + fixed_t tempmomx, tempmomy; + if (oldMagnitude > K_GetKartSpeed(player, true)) { - tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude); - tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude); + if (newMagnitude > oldMagnitude) + { + tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), oldMagnitude); + tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), oldMagnitude); + player->mo->momx = tempmomx + player->cmomx; + player->mo->momy = tempmomy + player->cmomy; + } + // else do nothing + } + else + { + tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) + tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) player->mo->momx = tempmomx + player->cmomx; player->mo->momy = tempmomy + player->cmomy; } - // else do nothing - } - else - { - tempmomx = FixedMul(FixedDiv(player->mo->momx - player->cmomx, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) - tempmomy = FixedMul(FixedDiv(player->mo->momy - player->cmomy, newMagnitude), K_GetKartSpeed(player, true)); //topspeed) - player->mo->momx = tempmomx + player->cmomx; - player->mo->momy = tempmomy + player->cmomy; } } } @@ -5778,11 +5759,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 + if ((player->mo && player->speed > 0) // Moving || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn || (player->kartstuff[k_respawn]) // Respawning || (player->spectator || objectplacing)) // Not a physical player - ) // ~~Spinning and boosting cancels out turning~~ Not anymore given spinout is more slippery and more prone to get you killed because of boosters. { player->lturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, KART_FULLTURN)+1; player->rturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, -KART_FULLTURN)-1; @@ -6172,7 +6152,7 @@ static void P_MovePlayer(player_t *player) //////////////////////////// // SRB2kart - Drifting smoke and fire - if (player->kartstuff[k_sneakertimer] > 0 && onground && (leveltime & 1)) + if (EITHERSNEAKER(player) && onground && (leveltime & 1)) K_SpawnBoostTrail(player); if (player->kartstuff[k_invincibilitytimer] > 0) @@ -7064,7 +7044,7 @@ static void P_DeathThink(player_t *player) if (player->pflags & PF_TIMEOVER) { - player->kartstuff[k_timeovercam]++; + player->karthud[khud_timeovercam]++; if (player->mo) { player->mo->flags |= (MF_NOGRAVITY|MF_NOCLIP); @@ -7072,7 +7052,7 @@ static void P_DeathThink(player_t *player) } } else - player->kartstuff[k_timeovercam] = 0; + player->karthud[khud_timeovercam] = 0; K_KartPlayerHUDUpdate(player); @@ -7280,7 +7260,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall #endif if (player->pflags & PF_TIMEOVER) // 1 for momentum keep, 2 for turnaround - timeover = (player->kartstuff[k_timeovercam] > 2*TICRATE ? 2 : 1); + timeover = (player->karthud[khud_timeovercam] > 2*TICRATE ? 2 : 1); else timeover = 0; @@ -7423,7 +7403,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (timeover) { - const INT32 timeovercam = max(0, min(180, (player->kartstuff[k_timeovercam] - 2*TICRATE)*15)); + const INT32 timeovercam = max(0, min(180, (player->karthud[khud_timeovercam] - 2*TICRATE)*15)); camrotate += timeovercam; } else if (leveltime < introtime) // Whoooshy camera! @@ -7497,10 +7477,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall dist += 4*(player->speed - K_GetKartSpeed(player, false)); dist += abs(thiscam->momz)/4; - if (player->kartstuff[k_boostcam]) + if (player->karthud[khud_boostcam]) { - dist -= FixedMul(11*dist/16, player->kartstuff[k_boostcam]); - height -= FixedMul(height, player->kartstuff[k_boostcam]); + dist -= FixedMul(11*dist/16, player->karthud[khud_boostcam]); + height -= FixedMul(height, player->karthud[khud_boostcam]); } x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist); @@ -7913,7 +7893,7 @@ boolean P_SpectatorJoinGame(player_t *player) static void P_CalcPostImg(player_t *player) { sector_t *sector = player->mo->subsector->sector; - postimg_t *type = postimg_none; + postimg_t *type = NULL; INT32 *param; fixed_t pviewheight; UINT8 i; @@ -8437,8 +8417,7 @@ void P_PlayerThink(player_t *player) #if 1 // "Blur" a bit when you have speed shoes and are going fast enough - if ((player->powers[pw_super] || player->powers[pw_sneakers] - || player->kartstuff[k_driftboost] || player->kartstuff[k_sneakertimer] || player->kartstuff[k_startboost]) && !player->kartstuff[k_invincibilitytimer] // SRB2kart + if ((player->powers[pw_super] || player->powers[pw_sneakers]) && (player->speed + abs(player->mo->momz)) > FixedMul(20*FRACUNIT,player->mo->scale)) { UINT8 i; diff --git a/src/r_things.c b/src/r_things.c index a11b61669..1afbb125c 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -828,7 +828,7 @@ static void R_DrawVisSprite(vissprite_t *vis) colfunc = basecolfunc; // hack: this isn't resetting properly somewhere. dc_colormap = vis->colormap; - if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash" + if (!(vis->cut & SC_PRECIP) && (vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash" { // translate certain pixels to white colfunc = transcolfunc; @@ -899,18 +899,18 @@ static void R_DrawVisSprite(vissprite_t *vis) frac = vis->startfrac; windowtop = windowbottom = sprbotscreen = INT32_MAX; - if (vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES) + if (!(vis->cut & SC_PRECIP) && vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES) this_scale = FixedMul(this_scale, ((skin_t *)vis->mobj->skin)->highresscale); if (this_scale <= 0) this_scale = 1; if (this_scale != FRACUNIT) { - if (!vis->isScaled) + if (!(vis->cut & SC_ISSCALED)) { vis->scale = FixedMul(vis->scale, this_scale); vis->scalestep = FixedMul(vis->scalestep, this_scale); vis->xiscale = FixedDiv(vis->xiscale,this_scale); - vis->isScaled = true; + vis->cut |= SC_ISSCALED; } dc_texturemid = FixedDiv(dc_texturemid,this_scale); } @@ -963,7 +963,7 @@ static void R_DrawVisSprite(vissprite_t *vis) #else column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS])); #endif - if (vis->vflip) + if (vis->cut & SC_VFLIP) R_DrawFlippedMaskedColumn(column, patch->height); else R_DrawMaskedColumn(column); @@ -1043,7 +1043,7 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis) // // R_SplitSprite // runs through a sector's lightlist and -static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing) +static void R_SplitSprite(vissprite_t *sprite) { INT32 i, lightnum, lindex; INT16 cutfrac; @@ -1079,6 +1079,8 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing) // adjust the heights. newsprite = M_Memcpy(R_NewVisSprite(), sprite, sizeof (vissprite_t)); + newsprite->cut |= (sprite->cut & SC_FLAGMASK); + sprite->cut |= SC_BOTTOM; sprite->gz = testheight; @@ -1118,13 +1120,17 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing) ; else */ - if (!((thing->frame & (FF_FULLBRIGHT|FF_TRANSMASK) || thing->flags2 & MF2_SHADOW) + if (!((newsprite->cut & SC_FULLBRIGHT) && (!newsprite->extra_colormap || !(newsprite->extra_colormap->fog & 1)))) { lindex = FixedMul(sprite->xscale, FixedDiv(640, vid.width))>>(LIGHTSCALESHIFT); if (lindex >= MAXLIGHTSCALE) lindex = MAXLIGHTSCALE-1; + + if (newsprite->cut & SC_SEMIBRIGHT) + lindex = (MAXLIGHTSCALE/2) + (lindex >> 1); + newsprite->colormap = spritelights[lindex]; } } @@ -1152,6 +1158,7 @@ static void R_ProjectSprite(mobj_t *thing) size_t rot; UINT8 flip; + boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP)); INT32 lindex; @@ -1357,7 +1364,7 @@ static void R_ProjectSprite(mobj_t *thing) } //SoM: 3/17/2000: Disregard sprites that are out of view.. - if (thing->eflags & MFE_VERTICALFLIP) + if (vflip) { // When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned. // sprite height - sprite topoffset is the proper inverse of the vertical offset, of course. @@ -1499,7 +1506,12 @@ static void R_ProjectSprite(mobj_t *thing) else if (thing->frame & FF_TRANSMASK) vis->transmap = transtables + (thing->frame & FF_TRANSMASK) - 0x10000; - if (((thing->frame & FF_FULLBRIGHT) || (thing->flags2 & MF2_SHADOW)) + if (thing->frame & FF_FULLBRIGHT || thing->flags2 & MF2_SHADOW) + vis->cut |= SC_FULLBRIGHT; + else if (thing->frame & FF_SEMIBRIGHT) + vis->cut |= SC_SEMIBRIGHT; + + if (vis->cut & SC_FULLBRIGHT && (!vis->extra_colormap || !(vis->extra_colormap->fog & 1))) { // full bright: goggles @@ -1513,20 +1525,17 @@ static void R_ProjectSprite(mobj_t *thing) if (lindex >= MAXLIGHTSCALE) lindex = MAXLIGHTSCALE-1; + if (vis->cut & SC_SEMIBRIGHT) + lindex = (MAXLIGHTSCALE/2) + (lindex >> 1); + vis->colormap = spritelights[lindex]; } - vis->precip = false; - - if (thing->eflags & MFE_VERTICALFLIP) - vis->vflip = true; - else - vis->vflip = false; - - vis->isScaled = false; + if (vflip) + vis->cut |= SC_VFLIP; if (thing->subsector->sector->numlights) - R_SplitSprite(vis, thing); + R_SplitSprite(vis); // Debug ++objectsdrawn; @@ -1698,15 +1707,12 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis->transmap = NULL; vis->mobjflags = 0; - vis->cut = SC_NONE; + vis->cut = SC_PRECIP; vis->extra_colormap = thing->subsector->sector->extra_colormap; vis->heightsec = thing->subsector->sector->heightsec; // Fullbright vis->colormap = colormaps; - vis->precip = true; - vis->vflip = false; - vis->isScaled = false; } // R_AddSprites @@ -2492,7 +2498,7 @@ void R_DrawMasked(void) next = r2->prev; // Tails 08-18-2002 - if (r2->sprite->precip == true) + if (r2->sprite->cut & SC_PRECIP) R_DrawPrecipitationSprite(r2->sprite); else R_DrawSprite(r2->sprite); diff --git a/src/r_things.h b/src/r_things.h index 697cde256..4837b4aee 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -104,9 +104,20 @@ extern CV_PossibleValue_t Forceskin_cons_t[]; // ----------- typedef enum { + // actual cuts SC_NONE = 0, SC_TOP = 1, - SC_BOTTOM = 2 + SC_BOTTOM = 1<<1, + // other flags + SC_PRECIP = 1<<2, + //SC_LINKDRAW = 1<<3, -- 2.2 compat + SC_FULLBRIGHT = 1<<4, + SC_SEMIBRIGHT = 1<<5, + SC_VFLIP = 1<<6, + SC_ISSCALED = 1>>7, + // masks + SC_CUTMASK = SC_TOP|SC_BOTTOM, + SC_FLAGMASK = ~SC_CUTMASK } spritecut_e; // A vissprite_t is a thing that will be drawn during a refresh, @@ -155,9 +166,6 @@ typedef struct vissprite_s INT16 clipbot[MAXVIDWIDTH], cliptop[MAXVIDWIDTH]; - boolean precip; - boolean vflip; // Flip vertically - boolean isScaled; INT32 dispoffset; // copy of info->dispoffset, affects ordering but not drawing } vissprite_t;