diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cfba2d4c..a1f3d29e6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -64,6 +64,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 p_tick.c p_user.c p_slopes.c + p_sweep.cpp + p_test.cpp tables.c r_bsp.cpp r_data.c diff --git a/src/cvars.cpp b/src/cvars.cpp index 4b0897dcf..1c0812598 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -806,6 +806,7 @@ consvar_t cv_numlaps = OnlineCheat("numlaps", "Map default").values(numlaps_cons consvar_t cv_restrictskinchange = OnlineCheat("restrictskinchange", "Yes").yes_no().description("Don't let players change their skin in the middle of gameplay"); consvar_t cv_spbtest = OnlineCheat("spbtest", "Off").on_off().description("SPB can never target a player"); +consvar_t cv_showgremlins = OnlineCheat("showgremlins", "No").yes_no().description("Show line collision errors"); consvar_t cv_timescale = OnlineCheat(cvlist_timer)("timescale", "1.0").floating_point().min_max(FRACUNIT/20, 20*FRACUNIT).description("Overclock or slow down the game"); consvar_t cv_ufo_follow = OnlineCheat("ufo_follow", "0").min_max(0, MAXPLAYERS).description("Make UFO Catcher folow this player"); consvar_t cv_ufo_health = OnlineCheat("ufo_health", "-1").min_max(-1, 100).description("Override UFO Catcher health -- applied at spawn or when value is changed"); @@ -905,6 +906,7 @@ void Dummymenuplayer_OnChange(void); consvar_t cv_dummymenuplayer = MenuDummy("dummymenuplayer", "P1").onchange(Dummymenuplayer_OnChange).values({{0, "NOPE"}, {1, "P1"}, {2, "P2"}, {3, "P3"}, {4, "P4"}}); consvar_t cv_dummyprofileautoroulette = MenuDummy("dummyprofileautoroulette", "Off").on_off(); +consvar_t cv_dummyprofilelitesteer = MenuDummy("dummyprofilelitesteer", "On").on_off(); consvar_t cv_dummyprofilekickstart = MenuDummy("dummyprofilekickstart", "Off").on_off(); consvar_t cv_dummyprofilename = MenuDummy("dummyprofilename", ""); consvar_t cv_dummyprofileplayername = MenuDummy("dummyprofileplayername", ""); @@ -1011,6 +1013,13 @@ consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS] = { Player("autoroulette4", "Off").on_off().onchange(weaponPrefChange4), }; +consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS] = { + Player("litesteer", "On").on_off().onchange(weaponPrefChange), + Player("litesteer2", "On").on_off().onchange(weaponPrefChange2), + Player("litesteer3", "On").on_off().onchange(weaponPrefChange3), + Player("litesteer4", "On").on_off().onchange(weaponPrefChange4), +}; + consvar_t cv_cam_dist[MAXSPLITSCREENPLAYERS] = { Player("cam_dist", "190").floating_point(), Player("cam2_dist", "190").floating_point(), diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2bfc47f24..1e01c8779 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2540,7 +2540,7 @@ void CL_ClearPlayer(INT32 playernum) } #define PlayerPointerRemove(field) \ - if (field) \ + if (P_MobjWasRemoved(field) == false) \ { \ P_RemoveMobj(field); \ P_SetTarget(&field, NULL); \ @@ -2550,7 +2550,8 @@ void CL_ClearPlayer(INT32 playernum) PlayerPointerRemove(players[playernum].mo); PlayerPointerRemove(players[playernum].followmobj); PlayerPointerRemove(players[playernum].stumbleIndicator); - PlayerPointerRemove(players[playernum].sliptideZipIndicator); + PlayerPointerRemove(players[playernum].wavedashIndicator); + PlayerPointerRemove(players[playernum].trickIndicator); #undef PlayerPointerRemove @@ -5618,7 +5619,7 @@ static INT16 Consistancy(void) if (TypeIsNetSynced(mo->type) == false) continue; - if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_ELEMENTAL | MF_FIRE | MF_ENEMY | MF_PAIN | MF_DONTPUNT)) { ret -= mo->type; ret += mo->x; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index b51f1408a..10ff36129 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1229,6 +1229,7 @@ enum { WP_KICKSTARTACCEL = 1<<0, WP_SHRINKME = 1<<1, WP_AUTOROULETTE = 1<<2, + WP_LITESTEER = 1<<3, }; void WeaponPref_Send(UINT8 ssplayer) @@ -1241,6 +1242,9 @@ void WeaponPref_Send(UINT8 ssplayer) if (cv_autoroulette[ssplayer].value) prefs |= WP_AUTOROULETTE; + if (cv_litesteer[ssplayer].value) + prefs |= WP_LITESTEER; + if (cv_shrinkme[ssplayer].value) prefs |= WP_SHRINKME; @@ -1259,6 +1263,9 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum) if (player->pflags & PF_AUTOROULETTE) prefs |= WP_AUTOROULETTE; + if (player->pflags & PF_LITESTEER) + prefs |= WP_LITESTEER; + if (player->pflags & PF_SHRINKME) prefs |= WP_SHRINKME; @@ -2422,13 +2429,16 @@ static void Command_Map_f(void) // Let's just guess so we don't have to specify the gametype EVERY time... newgametype = G_GuessGametypeByTOL(mapheaderinfo[newmapnum-1]->typeoflevel); - if (newgametype == -1) + if (!option_force && newgametype == -1) { CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support any known gametype!\n"), realmapname, G_BuildMapName(newmapnum)); Z_Free(realmapname); Z_Free(mapname); return; } + + if (newgametype == -1) + newgametype = GT_RACE; // sensible default } } diff --git a/src/d_player.h b/src/d_player.h index 8eba378ff..d9d46979d 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -64,6 +64,14 @@ typedef enum PST_REBORN } playerstate_t; +typedef enum +{ + IF_USERINGS = 1, // Have to be not holding the item button to change from using rings to using items (or vice versa) - prevents weirdness + IF_ITEMOUT = 1<<1, // Are you holding an item out? + IF_EGGMANOUT = 1<<2, // Eggman mark held, separate from IF_ITEMOUT so it doesn't stop you from getting items + IF_HOLDREADY = 1<<3, // Hold button-style item is ready to activate +} itemflags_t; + // // Player internal flags // @@ -95,11 +103,9 @@ typedef enum PF_RINGLOCK = 1<<13, // Prevent picking up rings while SPB is locked on - // The following four flags are mutually exclusive, although they can also all be off at the same time. If we ever run out of pflags, eventually turn them into a seperate five(+) mode UINT8..? - PF_USERINGS = 1<<14, // Have to be not holding the item button to change from using rings to using items (or vice versa) - prevents weirdness - PF_ITEMOUT = 1<<15, // Are you holding an item out? - PF_EGGMANOUT = 1<<16, // Eggman mark held, separate from PF_ITEMOUT so it doesn't stop you from getting items - PF_HOLDREADY = 1<<17, // Hold button-style item is ready to activate + PF_LITESTEER = 1<<14, // Hold Down to shallow turn (digital only) + + //15-17 free, was previously itemflags stuff PF_DRIFTINPUT = 1<<18, // Drifting! PF_GETSPARKS = 1<<19, // Can get sparks @@ -261,6 +267,16 @@ typedef enum TRIPWIRE_BLASTER, } tripwirepass_t; +typedef enum +{ + TRICKSTATE_NONE = 0, + TRICKSTATE_READY, + TRICKSTATE_FORWARD, + TRICKSTATE_RIGHT, + TRICKSTATE_LEFT, + TRICKSTATE_BACK, +} trickstate_t; + typedef enum { // Unsynced, HUD or clientsided effects @@ -502,6 +518,15 @@ typedef enum BOT_ITEM_PR__MAX } botItemPriority_e; +typedef struct { + tic_t enter_tic, exit_tic; + tic_t zoom_in_speed, zoom_out_speed; + fixed_t dist; + angle_t pan; + fixed_t pan_speed; // in degrees + tic_t pan_accel, pan_back; +} sonicloopcamvars_t; + // player_t struct for loop state typedef struct { fixed_t radius; @@ -511,6 +536,7 @@ typedef struct { vector2_t origin_shift; vector2_t shift; boolean flip; + sonicloopcamvars_t camera; } sonicloopvars_t; // player_t struct for power-ups @@ -628,6 +654,7 @@ struct player_t respawnvars_t respawn; // Respawn info mobj_t *ringShooter; // DEZ respawner object tic_t airtime; // Used to track just air time, but has evolved over time into a general "karted" timer. Rename this variable? + tic_t lastairtime; UINT8 startboost; // (0 to 125) - Boost you get from start of race UINT8 dropdashboost; // Boost you get when holding A while respawning @@ -716,6 +743,7 @@ struct player_t UINT8 flamelength; // Flame Shield dash meter, number of segments UINT16 ballhogcharge; // Ballhog charge up -- the higher this value, the more projectiles + boolean ballhogtap; // Ballhog released during charge: used to allow semirapid tapfire UINT16 hyudorotimer; // Duration of the Hyudoro offroad effect itself SINT8 stealingtimer; // if >0 you are stealing, if <0 you are being stolen from @@ -740,7 +768,7 @@ struct player_t UINT8 confirmVictim; // Player ID that you dealt damage to UINT8 confirmVictimDelay; // Delay before playing the sound - UINT8 trickpanel; // Trick panel state + UINT8 trickpanel; // Trick panel state - see trickstate_t UINT8 tricktime; // Increases while you're tricking. You can't input any trick until it's reached a certain threshold fixed_t trickboostpower; // Save the rough speed multiplier. Used for upwards tricks. UINT8 trickboostdecay; // used to know how long you've waited @@ -885,14 +913,19 @@ struct player_t UINT8 tripwireReboundDelay; // When failing Tripwire, brieftly lock out speed-based tripwire pass (anti-cheese) - UINT16 sliptideZip; // How long is our chained sliptide? Grant a proportional boost when it's over. - UINT8 sliptideZipDelay; // How long since the last sliptide? Only boost once you've been straightened out for a bit. - UINT16 sliptideZipBoost; // The actual boost granted from sliptideZip. + UINT16 wavedash; // How long is our chained sliptide? Grant a proportional boost when it's over. + UINT8 wavedashdelay; // How long since the last sliptide? Only boost once you've been straightened out for a bit. + UINT16 wavedashboost; // The actual boost granted from wavedash. + + UINT16 trickcharge; // Landed normally from a trick panel? Get the benefits package! + + UINT16 infinitether; // Generic infinitether time, used for infinitether leniency. UINT8 lastsafelap; mobj_t *stumbleIndicator; - mobj_t *sliptideZipIndicator; + mobj_t *wavedashIndicator; + mobj_t *trickIndicator; mobj_t *whip; mobj_t *hand; mobj_t *flickyAttacker; @@ -910,10 +943,13 @@ struct player_t INT16 incontrol; // -1 to -175 when spinning out or tumbling, 1 to 175 when not. Use to check for combo hits or emergency inputs. boolean markedfordeath; + boolean dotrickfx; UINT8 ringboxdelay; // Delay until Ring Box auto-activates UINT8 ringboxaward; // Where did we stop? + UINT8 itemflags; // holds IF_ flags (see itemflags_t) + fixed_t outrun; // Milky Way road effect uint8_t public_key[PUBKEYLENGTH]; diff --git a/src/d_think.h b/src/d_think.h index d833f4ca0..29b67718e 100644 --- a/src/d_think.h +++ b/src/d_think.h @@ -55,6 +55,7 @@ struct thinker_t // killough 11/98: count of how many other objects reference // this one using pointers. Used for garbage collection. INT32 references; + boolean cachable; #ifdef PARANOIA INT32 debug_mobjtype; diff --git a/src/deh_tables.c b/src/deh_tables.c index 3023b70eb..7175b7a35 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3314,7 +3314,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_MAGICIANBOXTOP", "S_MAGICIANBOXBOTTOM", - "S_SLIPTIDEZIP", + "S_WAVEDASH", "S_INSTAWHIP", "S_INSTAWHIP_RECHARGE1", @@ -3325,6 +3325,13 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_BLOCKRING", "S_BLOCKBODY", + "S_CHARGEAURA", + "S_CHARGEFALL", + "S_CHARGEFLICKER", + "S_CHARGESPARK", + "S_CHARGERELEASE", + "S_CHARGEEXTRA", + "S_SERVANTHAND", "S_HORNCODE", @@ -3892,6 +3899,16 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_SMOOTHLANDING", + "S_TRICKINDICATOR_OVERLAY", + "S_TRICKINDICATOR_UNDERLAY", + "S_TRICKINDICATOR_OVERLAY_ARROW", + "S_TRICKINDICATOR_UNDERLAY_ARROW", + "S_TRICKINDICATOR_UNDERLAY_ARROW2", + + "S_SIDETRICK", + "S_BACKTRICK", + "S_FORWARDTRICK", + // DEZ Ring Shooter "S_TIREGRABBER", "S_RINGSHOOTER_SIDE", @@ -5538,7 +5555,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_MONITOR_SHARD", "MT_MAGICIANBOX", - "MT_SLIPTIDEZIP", + "MT_WAVEDASH", "MT_INSTAWHIP", "MT_INSTAWHIP_RECHARGE", @@ -5546,6 +5563,13 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BLOCKRING", "MT_BLOCKBODY", + "MT_CHARGEAURA", + "MT_CHARGEFALL", + "MT_CHARGEFLICKER", + "MT_CHARGESPARK", + "MT_CHARGERELEASE", + "MT_CHARGEEXTRA", + "MT_SERVANTHAND", "MT_HORNCODE", @@ -5649,6 +5673,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_TRIPWIREBOOST", "MT_SMOOTHLANDING", + "MT_TRICKINDICATOR", + "MT_SIDETRICK", + "MT_FORWARDTRICK", "MT_TIREGRABBER", "MT_RINGSHOOTER", @@ -5987,13 +6014,13 @@ const char *const MOBJFLAG_LIST[] = { "SLOPE", "MISSILE", "SPRING", - "MONITOR", + "ELEMENTAL", "NOTHINK", "NOCLIPHEIGHT", "ENEMY", "SCENERY", "PAIN", - "STICKY", + "DONTPUNT", "APPLYTERRAIN", "NOCLIPTHING", "GRENADEBOUNCE", @@ -6093,11 +6120,10 @@ const char *const PLAYERFLAG_LIST[] = { "RINGLOCK", // Prevent picking up rings while SPB is locked on - // The following four flags are mutually exclusive, although they can also all be off at the same time. If we ever run out of pflags, eventually turn them into a seperate five(+) mode UINT8..? - "USERINGS", // Have to be not holding the item button to change from using rings to using items (or vice versa) - prevents weirdness - "ITEMOUT", // Are you holding an item out? - "EGGMANOUT", // Eggman mark held, separate from PF_ITEMOUT so it doesn't stop you from getting items - "HOLDREADY", // Hold button-style item is ready to activate + "LITESTEER", // Shallow digital turn with DOWN + "\x01", // Free + "\x01", // Free + "\x01", // Free "DRIFTINPUT", // Drifting! "GETSPARKS", // Can get sparks diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp index 81d431a12..6dca175da 100644 --- a/src/g_build_ticcmd.cpp +++ b/src/g_build_ticcmd.cpp @@ -380,7 +380,7 @@ class TiccmdBuilder // ugly with the current abstractions, though, and there's a fortunate trick here: // if you can input full strength turns on both axes, either you're using a fucking // square gate, or you're not on an analog device. - if (joystickvector.yaxis >= JOYAXISRANGE && abs(cmd->turning) == KART_FULLTURN) // >= beacuse some analog devices can go past JOYAXISRANGE (?!) + if (cv_litesteer[ssplayer - 1].value && joystickvector.yaxis >= JOYAXISRANGE && abs(cmd->turning) == KART_FULLTURN) // >= beacuse some analog devices can go past JOYAXISRANGE (?!) cmd->turning /= 2; } diff --git a/src/g_demo.c b/src/g_demo.c index d039ddfdd..3a6bd1430 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -127,6 +127,7 @@ demoghost *ghosts = NULL; #define DEMO_SHRINKME 0x04 #define DEMO_BOT 0x08 #define DEMO_AUTOROULETTE 0x10 +#define DEMO_LITESTEER 0x20 // For demos #define ZT_FWD 0x0001 @@ -1329,7 +1330,7 @@ readghosttic: z = READFIXED(g->p); angle = READANGLE(g->p); if (!(mobjinfo[type].flags & MF_SHOOTABLE) - || !(mobjinfo[type].flags & (MF_ENEMY|MF_MONITOR)) + || !(mobjinfo[type].flags & MF_ENEMY) || health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad. continue; poof = P_SpawnMobj(x, y, z, MT_GHOST); @@ -2480,6 +2481,8 @@ void G_BeginRecording(void) i |= DEMO_KICKSTART; if (player->pflags & PF_AUTOROULETTE) i |= DEMO_AUTOROULETTE; + if (player->pflags & PF_LITESTEER) + i |= DEMO_LITESTEER; if (player->pflags & PF_SHRINKME) i |= DEMO_SHRINKME; if (player->bot == true) @@ -3447,6 +3450,11 @@ void G_DoPlayDemo(const char *defdemoname) else players[p].pflags &= ~PF_AUTOROULETTE; + if (flags & DEMO_LITESTEER) + players[p].pflags |= PF_LITESTEER; + else + players[p].pflags &= ~PF_LITESTEER; + if (flags & DEMO_SHRINKME) players[p].pflags |= PF_SHRINKME; else diff --git a/src/g_game.c b/src/g_game.c index b42085fc9..c66923d2a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2127,7 +2127,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) totalring = players[player].totalring; xtralife = players[player].xtralife; - pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE)); + pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_LITESTEER)); // SRB2kart memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette)); @@ -2145,7 +2145,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) { rings = 0; } - else if (modeattacking & ATTACKING_SPB) + else if (G_TimeAttackStart()) { rings = 20; } @@ -2175,7 +2175,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) } else { - if (players[player].pflags & PF_ITEMOUT) + if (players[player].itemflags & IF_ITEMOUT) { itemtype = 0; itemamount = 0; @@ -2234,7 +2234,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) K_RemoveFollower(&players[player]); #define PlayerPointerRemove(field) \ - if (field) \ + if (P_MobjWasRemoved(field) == false) \ { \ P_RemoveMobj(field); \ P_SetTarget(&field, NULL); \ @@ -2243,7 +2243,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) // These are mostly subservient to the player, and may not clean themselves up. PlayerPointerRemove(players[player].followmobj); PlayerPointerRemove(players[player].stumbleIndicator); - PlayerPointerRemove(players[player].sliptideZipIndicator); + PlayerPointerRemove(players[player].wavedashIndicator); + PlayerPointerRemove(players[player].trickIndicator); #undef PlayerPointerRemove diff --git a/src/g_game.h b/src/g_game.h index fc747ae7b..9464a6e8a 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -99,6 +99,7 @@ extern consvar_t cv_pauseifunfocused; extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS]; +extern consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS]; diff --git a/src/info.c b/src/info.c index 23c3225ee..71644da80 100644 --- a/src/info.c +++ b/src/info.c @@ -558,7 +558,8 @@ char sprnames[NUMSPRITES + 1][5] = "IMDB", // Item Monitor Small Shard (Debris) "MTWK", // Item Monitor Glass Twinkle - "SLPT", // Sliptide zip indicator + "SLPT", // Wavedash indicator + "TRBS", // Trickdash indicator "IWHP", // Instawhip "WPRE", // Instawhip Recharge @@ -566,6 +567,12 @@ char sprnames[NUMSPRITES + 1][5] = "GRNG", // Guard ring "GBDY", // Guard body + "TRC1", // Charge aura + "TRC2", // Charge fall + "TRC3", // Charge flicker/sparks + "TRC4", // Charge release + "TRC5", // Charge extra + "DHND", // Servant Hand "HORN", // Horncode @@ -631,6 +638,15 @@ char sprnames[NUMSPRITES + 1][5] = "TWBT", // Tripwire BLASTER "SMLD", // Smooth landing + // Trick Effects + "TRK1", + "TRK2", + "TRK3", + "TRK4", + "TRK5", + "TRK6", + "TRK7", + "TIRG", // Tire grabbers "RSHT", // DEZ Ring Shooter @@ -4118,7 +4134,7 @@ state_t states[NUMSTATES] = {SPR_MGBT, FF_FLOORSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_MAGICIANBOX_TOP {SPR_MGBB, FF_FLOORSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_MAGICIANBOX_BOTTOM - {SPR_SLPT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_SLIPTIDEZIP + {SPR_SLPT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_WAVEDASH {SPR_IWHP, FF_FLOORSPRITE|FF_ANIMATE|0, -1, {NULL}, 6, 2, S_NULL}, // S_INSTAWHIP {SPR_NULL, 0, 1, {NULL}, 0, 0, S_INSTAWHIP_RECHARGE2}, // S_INSTAWHIP_RECHARGE1 @@ -4129,6 +4145,13 @@ state_t states[NUMSTATES] = {SPR_GRNG, FF_FULLBRIGHT|FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_BLOCKRING {SPR_GBDY, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_BLOCKBODY + {SPR_TRC1, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 2, S_NULL}, // S_CHARGEAURA + {SPR_TRC2, FF_FULLBRIGHT|FF_ANIMATE|0, 20, {NULL}, 19, 1, S_NULL}, // S_CHARGEFALL + {SPR_TRC3, FF_FULLBRIGHT|FF_ADD|0, 2, {NULL}, 0, 0, S_NULL}, // S_CHARGEFLICKER + {SPR_TRC3, FF_FULLBRIGHT|FF_ADD|1, 2, {NULL}, 0, 0, S_NULL}, // S_CHARGESPARK + {SPR_TRC4, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE|0, -1, {NULL}, 4, 1, S_NULL}, // S_CHARGERELEASE + {SPR_TRC5, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE|0, -1, {NULL}, 4, 1, S_NULL}, // S_CHARGEEXTRA + {SPR_DHND, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SERVANTHAND {SPR_HORN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_HORNCODE @@ -4662,6 +4685,16 @@ state_t states[NUMSTATES] = {SPR_SMLD, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE, -1, {NULL}, 7, 2, S_NULL}, // S_SMOOTHLANDING + {SPR_TRK1, FF_FULLBRIGHT|FF_ANIMATE|FF_PAPERSPRITE|FF_ADD, -1, {NULL}, 3, 3, S_NULL}, // S_TRICKINDICATOR_OVERLAY, + {SPR_TRK2, FF_FULLBRIGHT|FF_ANIMATE|FF_PAPERSPRITE, -1, {NULL}, 3, 3, S_NULL}, // S_TRICKINDICATOR_UNDERLAY, + {SPR_TRK3, FF_FULLBRIGHT|FF_ANIMATE|FF_PAPERSPRITE|FF_ADD, 13, {NULL}, 12, 1, S_INVISIBLE}, // S_TRICKINDICATOR_OVERLAY_ARROW, + {SPR_NULL, 0, 1, {NULL}, 12, 1, S_TRICKINDICATOR_UNDERLAY_ARROW2}, // S_TRICKINDICATOR_UNDERLAY_ARROW, + {SPR_TRK4, FF_FULLBRIGHT|FF_ANIMATE|FF_PAPERSPRITE, 13, {NULL}, 12, 1, S_INVISIBLE}, // S_TRICKINDICATOR_UNDERLAY_ARROW2, + + {SPR_TRK5, FF_FULLBRIGHT|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SIDETRICK, + {SPR_TRK6, FF_FULLBRIGHT|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_BACKTRICK, + {SPR_TRK7, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE, -1, {NULL}, 6, 1, S_NULL}, // S_FORWARDTRICK, + {SPR_TIRG, FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_TIREGRABBER {SPR_RSHT, FF_PAPERSPRITE|0, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_SIDE {SPR_RSHT, FF_SEMIBRIGHT|FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_RINGSHOOTER_NIPPLES @@ -4815,12 +4848,12 @@ state_t states[NUMSTATES] = {SPR_FLEN, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_FLINGENERGY3}, // S_FLINGENERGY2, {SPR_FLEN, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_NULL}, // S_FLINGENERGY3, - {SPR_CLAS, FF_FULLBRIGHT|FF_TRANS30, 2, {A_PlayActiveSound}, 0, 0, S_CLASH2}, // S_CLASH1 - {SPR_CLAS, FF_FULLBRIGHT|FF_TRANS30|1, 2, {NULL}, 0, 0, S_CLASH3}, // S_CLASH2 - {SPR_CLAS, FF_FULLBRIGHT|FF_TRANS30|2, 2, {NULL}, 0, 0, S_CLASH4}, // S_CLASH3 - {SPR_CLAS, FF_FULLBRIGHT|FF_TRANS30|3, 2, {NULL}, 0, 0, S_CLASH5}, // S_CLASH4 - {SPR_CLAS, FF_FULLBRIGHT|FF_TRANS30|4, 2, {NULL}, 0, 0, S_CLASH6}, // S_CLASH5 - {SPR_CLAS, FF_FULLBRIGHT|FF_TRANS30|5, 2, {NULL}, 0, 0, S_NULL}, // S_CLASH6 + {SPR_CLAS, FF_FULLBRIGHT|FF_ADD, 2, {A_PlayActiveSound}, 0, 0, S_CLASH2}, // S_CLASH1 + {SPR_CLAS, FF_FULLBRIGHT|FF_ADD|1, 2, {NULL}, 0, 0, S_CLASH3}, // S_CLASH2 + {SPR_CLAS, FF_FULLBRIGHT|FF_ADD|2, 2, {NULL}, 0, 0, S_CLASH4}, // S_CLASH3 + {SPR_CLAS, FF_FULLBRIGHT|FF_ADD|3, 2, {NULL}, 0, 0, S_CLASH5}, // S_CLASH4 + {SPR_CLAS, FF_FULLBRIGHT|FF_ADD|4, 2, {NULL}, 0, 0, S_CLASH6}, // S_CLASH5 + {SPR_CLAS, FF_FULLBRIGHT|FF_ADD|5, 2, {NULL}, 0, 0, S_NULL}, // S_CLASH6 {SPR_PSHW, FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_FIREDITEM2}, // S_FIREDITEM1 {SPR_PSHW, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_FIREDITEM3}, // S_FIREDITEM2 @@ -5742,7 +5775,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 1000, // mass MT_THOK, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SOLID|MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags (statenum_t)MT_THOK // raisestate }, @@ -9327,7 +9360,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_mspogo, // deathsound 2*TICRATE, // speed 14*FRACUNIT, // radius - 64*FRACUNIT, // height + 90*FRACUNIT, // height 0, // display offset 4, // mass 0, // damage @@ -9353,7 +9386,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_WALLSPIKED2, // xdeathstate sfx_mspogo, // deathsound 2*TICRATE, // speed - 16*FRACUNIT, // radius + 48*FRACUNIT, // radius 14*FRACUNIT, // height 0, // display offset 4, // mass @@ -9548,7 +9581,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_RING_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9575,7 +9608,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_PITY_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9602,7 +9635,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_ATTRACT_ICON,// damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9629,7 +9662,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_FORCE_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9656,7 +9689,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_ARMAGEDDON_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9683,7 +9716,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_WHIRLWIND_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9710,7 +9743,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_ELEMENTAL_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9737,7 +9770,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_SNEAKERS_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9764,7 +9797,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_INVULN_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9791,7 +9824,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_1UP_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9818,7 +9851,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_EGGMAN_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9845,7 +9878,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_MIXUP_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9872,7 +9905,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_UNKNOWN, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9899,7 +9932,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_GRAVITY_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9926,7 +9959,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_RECYCLER_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9953,7 +9986,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_SCORE1K_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -9980,7 +10013,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_SCORE10K_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -10007,7 +10040,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_FLAMEAURA_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -10034,7 +10067,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_BUBBLEWRAP_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -10061,7 +10094,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_THUNDERCOIN_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -10088,7 +10121,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_PITY_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10115,7 +10148,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_ATTRACT_ICON,// damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10142,7 +10175,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_FORCE_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10169,7 +10202,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_ARMAGEDDON_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10196,7 +10229,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_WHIRLWIND_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10223,7 +10256,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_ELEMENTAL_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10250,7 +10283,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_SNEAKERS_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10277,7 +10310,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_INVULN_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10304,7 +10337,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_EGGMAN_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10331,7 +10364,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_GRAVITY_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10358,7 +10391,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_FLAMEAURA_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10385,7 +10418,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_BUBBLEWRAP_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10412,7 +10445,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_THUNDERCOIN_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags + MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags S_NULL // raisestate }, @@ -10439,7 +10472,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_RING_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -10466,7 +10499,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass MT_RING_ICON, // damage sfx_None, // activesound - MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags + MF_SOLID|MF_SHOOTABLE, // flags S_NULL // raisestate }, @@ -14618,13 +14651,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 5*FRACUNIT, // speed - 8*FRACUNIT, // radius - 8*FRACUNIT, // height + 16*FRACUNIT, // radius + 32*FRACUNIT, // height 0, // display offset DMG_NORMAL, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_MISSILE|MF_PAIN, // flags + MF_NOGRAVITY|MF_MISSILE|MF_PAIN|MF_NOSQUISH|MF_NOHITLAGFORME|MF_ELEMENTAL, // flags S_NULL // raisestate }, @@ -23061,9 +23094,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_SLIPTIDEZIP + { // MT_WAVEDASH -1, // doomednum - S_SLIPTIDEZIP, // spawnstate + S_WAVEDASH, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -23111,7 +23144,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -23223,6 +23256,168 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_CHARGEAURA + -1, // doomednum + S_CHARGEAURA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + + { // MT_CHARGEFALL + -1, // doomednum + S_CHARGEFALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + + { // MT_CHARGEFLICKER + -1, // doomednum + S_CHARGEFLICKER, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_CHARGESPARK + -1, // doomednum + S_CHARGESPARK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_CHARGERELEASE + -1, // doomednum + S_CHARGERELEASE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + + { // MT_CHARGEEXTRA + -1, // doomednum + S_CHARGEEXTRA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 67*FRACUNIT, // radius + 67*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + { // MT_SERVANTHAND -1, // doomednum S_SERVANTHAND, // spawnstate @@ -24002,7 +24197,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_peel, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24029,7 +24224,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24083,7 +24278,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_s3k96, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24110,7 +24305,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24137,7 +24332,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_s3kc0s, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24164,7 +24359,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24218,7 +24413,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_s3k5c, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24245,7 +24440,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24353,7 +24548,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_s3k5c, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24380,7 +24575,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_s3k96, // activesound - MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24407,7 +24602,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SPECIAL|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24461,7 +24656,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24515,7 +24710,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_kc64, // activesound - MF_SOLID|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + MF_SOLID|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -24758,7 +24953,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags + MF_NOCLIPTHING|MF_DONTENCOREMAP|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -25055,7 +25250,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_s3k5c, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -25082,7 +25277,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -25136,7 +25331,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 1, // damage sfx_s3k96, // activesound - MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE, // flags + MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_SLOPE|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -25190,7 +25385,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags + MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_DONTPUNT, // flags S_NULL // raisestate }, @@ -25329,6 +25524,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_TRICKINDICATOR + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 128*FRACUNIT, // radius + 128*FRACUNIT, // height + -1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_SIDETRICK + -1, // doomednum + S_SIDETRICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 36*FRACUNIT, // radius + 40*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + + { // MT_FORWARDTRICK + -1, // doomednum + S_FORWARDTRICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 60*FRACUNIT, // radius + 86*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_TIREGRABBER -1, // doomednum S_TIREGRABBER, // spawnstate @@ -30433,7 +30709,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 0, // mass 0, // damage sfx_None, // activesound - MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags + MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_DONTPUNT, // flags S_NULL // raisestate }, diff --git a/src/info.h b/src/info.h index b57082a5f..3d83aebed 100644 --- a/src/info.h +++ b/src/info.h @@ -1115,7 +1115,8 @@ typedef enum sprite SPR_IMDB, // Item Monitor Small Shard (Debris) SPR_MTWK, // Item Monitor Glass Twinkle - SPR_SLPT, // Sliptide zip indicator + SPR_SLPT, // Wavedash indicator + SPR_TRBS, // Trickdash indicator SPR_IWHP, // Instawhip SPR_WPRE, // Instawhip Recharge @@ -1123,6 +1124,12 @@ typedef enum sprite SPR_GRNG, // Guard ring SPR_GBDY, // Guard body + SPR_TRC1, // Charge aura + SPR_TRC2, // Charge fall + SPR_TRC3, // Charge flicker/sparks + SPR_TRC4, // Charge release + SPR_TRC5, // Charge extra + SPR_DHND, // Servant Hand SPR_HORN, // Horncode @@ -1188,6 +1195,15 @@ typedef enum sprite SPR_TWBT, // Tripwire BLASTER SPR_SMLD, // Smooth landing + // Trick Effects + SPR_TRK1, + SPR_TRK2, + SPR_TRK3, + SPR_TRK4, + SPR_TRK5, + SPR_TRK6, + SPR_TRK7, + SPR_TIRG, // Tire grabbers SPR_RSHT, // DEZ Ring Shooter @@ -4529,7 +4545,7 @@ typedef enum state S_MAGICIANBOX_TOP, S_MAGICIANBOX_BOTTOM, - S_SLIPTIDEZIP, + S_WAVEDASH, S_INSTAWHIP, S_INSTAWHIP_RECHARGE1, @@ -4540,6 +4556,13 @@ typedef enum state S_BLOCKRING, S_BLOCKBODY, + S_CHARGEAURA, + S_CHARGEFALL, + S_CHARGEFLICKER, + S_CHARGESPARK, + S_CHARGERELEASE, + S_CHARGEEXTRA, + S_SERVANTHAND, S_HORNCODE, @@ -5106,6 +5129,16 @@ typedef enum state S_SMOOTHLANDING, + S_TRICKINDICATOR_OVERLAY, + S_TRICKINDICATOR_UNDERLAY, + S_TRICKINDICATOR_OVERLAY_ARROW, + S_TRICKINDICATOR_UNDERLAY_ARROW, + S_TRICKINDICATOR_UNDERLAY_ARROW2, + + S_SIDETRICK, + S_BACKTRICK, + S_FORWARDTRICK, + // DEZ Ring Shooter S_TIREGRABBER, S_RINGSHOOTER_SIDE, @@ -6791,7 +6824,7 @@ typedef enum mobj_type MT_MONITOR_PART, MT_MONITOR_SHARD, MT_MAGICIANBOX, - MT_SLIPTIDEZIP, + MT_WAVEDASH, MT_INSTAWHIP, MT_INSTAWHIP_RECHARGE, @@ -6799,6 +6832,13 @@ typedef enum mobj_type MT_BLOCKRING, MT_BLOCKBODY, + MT_CHARGEAURA, + MT_CHARGEFALL, + MT_CHARGEFLICKER, + MT_CHARGESPARK, + MT_CHARGERELEASE, + MT_CHARGEEXTRA, + MT_SERVANTHAND, MT_HORNCODE, @@ -6902,6 +6942,9 @@ typedef enum mobj_type MT_TRIPWIREBOOST, MT_SMOOTHLANDING, + MT_TRICKINDICATOR, + MT_SIDETRICK, + MT_FORWARDTRICK, MT_TIREGRABBER, MT_RINGSHOOTER, diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 2d796758e..8c1894fba 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -1109,7 +1109,7 @@ static void K_BotTrick(player_t *player, ticcmd_t *cmd, const botcontroller_t *b return; } - if (player->trickpanel == 1) + if (player->trickpanel == TRICKSTATE_READY) { switch (botController->trick) { @@ -1521,7 +1521,7 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd) // Actual gameplay behaviors below this block! const botcontroller_t *botController = K_GetBotController(player->mo); - if (player->trickpanel != 0) + if (player->trickpanel != TRICKSTATE_NONE) { K_BotTrick(player, cmd, botController); diff --git a/src/k_botitem.c b/src/k_botitem.c index c483afdb5..ee9fe577e 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -440,7 +440,7 @@ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean --------------------------------------------------*/ static void K_BotItemGenericTrapShield(player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine) { - if (player->pflags & PF_ITEMOUT) + if (player->itemflags & IF_ITEMOUT) { return; } @@ -465,7 +465,7 @@ static void K_BotItemGenericTrapShield(player_t *player, ticcmd_t *cmd, INT16 tu --------------------------------------------------*/ static void K_BotItemGenericOrbitShield(player_t *player, ticcmd_t *cmd) { - if (player->pflags & PF_ITEMOUT) + if (player->itemflags & IF_ITEMOUT) { return; } @@ -811,7 +811,7 @@ static boolean K_BotRevealsEggbox(player_t *player) --------------------------------------------------*/ static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd) { - if (player->pflags & PF_EGGMANOUT) + if (player->itemflags & IF_EGGMANOUT) { return; } @@ -1221,7 +1221,7 @@ static void K_BotItemBubble(player_t *player, ticcmd_t *cmd) hold = true; } - if (hold && (player->pflags & PF_HOLDREADY)) + if (hold && (player->itemflags & IF_HOLDREADY)) { cmd->buttons |= BT_ATTACK; } @@ -1245,7 +1245,7 @@ static void K_BotItemFlame(player_t *player, ticcmd_t *cmd) { player->botvars.itemconfirm--; } - else if (player->pflags & PF_HOLDREADY) + else if (player->itemflags & IF_HOLDREADY) { INT32 flamemax = player->flamelength; @@ -1532,7 +1532,7 @@ static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) { - if (player->pflags & PF_USERINGS) + if (player->itemflags & IF_USERINGS) { if (player->rings > 0) { @@ -1567,7 +1567,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) { K_BotItemEggmanExplosion(player, cmd); } - else if (player->pflags & PF_EGGMANOUT) + else if (player->itemflags & IF_EGGMANOUT) { K_BotItemEggman(player, cmd); } @@ -1604,7 +1604,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemSneaker(player, cmd); break; case KITEM_BANANA: - if (!(player->pflags & PF_ITEMOUT)) + if (!(player->itemflags & IF_ITEMOUT)) { K_BotItemGenericTrapShield(player, cmd, turnamt, false); } @@ -1617,7 +1617,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemEggmanShield(player, cmd); break; case KITEM_ORBINAUT: - if (!(player->pflags & PF_ITEMOUT)) + if (!(player->itemflags & IF_ITEMOUT)) { K_BotItemGenericOrbitShield(player, cmd); } @@ -1627,7 +1627,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) } break; case KITEM_JAWZ: - if (!(player->pflags & PF_ITEMOUT)) + if (!(player->itemflags & IF_ITEMOUT)) { K_BotItemGenericOrbitShield(player, cmd); } @@ -1637,7 +1637,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) } break; case KITEM_MINE: - if (!(player->pflags & PF_ITEMOUT)) + if (!(player->itemflags & IF_ITEMOUT)) { K_BotItemGenericTrapShield(player, cmd, turnamt, true); } @@ -1654,7 +1654,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemBallhog(player, cmd); break; case KITEM_DROPTARGET: - if (!(player->pflags & PF_ITEMOUT)) + if (!(player->itemflags & IF_ITEMOUT)) { K_BotItemGenericTrapShield(player, cmd, turnamt, false); } diff --git a/src/k_botsearch.c b/src/k_botsearch.c index bdc7ecf62..8d0fc1bc7 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -577,8 +577,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) } // Has held item shield else if (K_PlayerAttackSteer(thing, side, 20, - (thing->player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)), - (g_nudgeSearch.botmo->player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)) + (thing->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)), + (g_nudgeSearch.botmo->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)) )) { break; diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 87a4a6862..568f87338 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -18,6 +18,7 @@ #include "k_podium.h" #include "k_powerup.h" #include "k_hitlag.h" +#include "m_random.h" angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { @@ -190,7 +191,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2) if (t1->target->hnext == t1) { P_SetTarget(&t1->target->hnext, NULL); - t1->target->player->pflags &= ~PF_EGGMANOUT; + t1->target->player->itemflags &= ~IF_EGGMANOUT; } } @@ -924,7 +925,7 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) } else if (victim->type == MT_SUPER_FLICKY) { - if (Obj_IsSuperFlickyWhippable(victim)) + if (Obj_IsSuperFlickyWhippable(victim, attacker)) { K_AddHitLag(victim, victimHitlag, true); K_AddHitLag(attacker, attackerHitlag, false); @@ -1170,3 +1171,114 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) return false; } + +void K_PuntHazard(mobj_t *t1, mobj_t *t2) +{ + // TODO: spawn a unique mobjtype other than MT_GHOST + mobj_t *img = P_SpawnGhostMobj(t1); + + K_MakeObjectReappear(t1); + + img->flags &= ~MF_NOGRAVITY; + img->renderflags = t1->renderflags & ~RF_DONTDRAW; + img->extravalue1 = 1; + img->extravalue2 = 2; + img->fuse = 2*TICRATE; + + struct Vector + { + fixed_t x_, y_, z_; + fixed_t h_ = FixedHypot(x_, y_); + fixed_t speed_ = std::max(60 * mapobjectscale, FixedHypot(h_, z_) * 2); + + explicit Vector(fixed_t x, fixed_t y, fixed_t z) : x_(x), y_(y), z_(z) {} + explicit Vector(const mobj_t* mo) : + Vector(std::max( + Vector(mo->x - mo->old_x, mo->y - mo->old_y, mo->z - mo->old_z), + Vector(mo->momx, mo->momy, mo->momz) + )) + { + } + explicit Vector(const Vector&) = default; + + bool operator<(const Vector& b) const { return speed_ < b.speed_; } + + void invert() + { + x_ = -x_; + y_ = -y_; + z_ = -z_; + } + + void thrust(mobj_t* mo) const + { + angle_t yaw = R_PointToAngle2(0, 0, h_, z_); + yaw = std::max(AbsAngle(yaw), static_cast(ANGLE_11hh)) + (yaw & ANGLE_180); + + P_InstaThrust(mo, R_PointToAngle2(0, 0, x_, y_), FixedMul(speed_, FCOS(yaw))); + mo->momz = FixedMul(speed_, FSIN(yaw)); + } + }; + + Vector h_vector(t1); + Vector p_vector(t2); + + h_vector.invert(); + + std::max(h_vector, p_vector).thrust(img); + + K_DoPowerClash(img, t2); // applies hitlag + P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH); +} + +boolean K_PuntCollide(mobj_t *t1, mobj_t *t2) +{ + if (t1->flags & MF_DONTPUNT) + { + return false; + } + + if (!t2->player) + { + return false; + } + + if (!K_PlayerCanPunt(t2->player)) + { + return false; + } + + if (t1->flags & MF_ELEMENTAL) + { + K_MakeObjectReappear(t1); + + // copied from MT_ITEMCAPSULE + UINT8 i; + INT16 spacing = (t1->radius >> 1) / t1->scale; + // dust effects + for (i = 0; i < 10; i++) + { + mobj_t *puff = P_SpawnMobjFromMobj( + t1, + P_RandomRange(PR_ITEM_DEBRIS, -spacing, spacing) * FRACUNIT, + P_RandomRange(PR_ITEM_DEBRIS, -spacing, spacing) * FRACUNIT, + P_RandomRange(PR_ITEM_DEBRIS, 0, 4*spacing) * FRACUNIT, + MT_SPINDASHDUST + ); + + puff->momz = puff->scale * P_MobjFlip(puff); + + P_Thrust(puff, R_PointToAngle2(t2->x, t2->y, puff->x, puff->y), 3*puff->scale); + + puff->momx += t2->momx / 2; + puff->momy += t2->momy / 2; + puff->momz += t2->momz / 2; + } + } + else + { + K_PuntHazard(t1, t2); + } + + return true; +} diff --git a/src/k_collide.h b/src/k_collide.h index 84d9a324e..525e5b9c3 100644 --- a/src/k_collide.h +++ b/src/k_collide.h @@ -33,6 +33,9 @@ boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2); boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2); +void K_PuntHazard(mobj_t *t1, mobj_t *t2); +boolean K_PuntCollide(mobj_t *t1, mobj_t *t2); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_director.cpp b/src/k_director.cpp index f04d2dfca..118786d63 100644 --- a/src/k_director.cpp +++ b/src/k_director.cpp @@ -221,7 +221,7 @@ private: bool can_change() const { - if (viewplayer()->trickpanel > 0) + if (viewplayer()->trickpanel != TRICKSTATE_NONE) { return false; } diff --git a/src/k_hud.c b/src/k_hud.c index ab26ae388..4f9d94729 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1453,7 +1453,7 @@ static void K_drawKartItem(void) break; } - if ((stplyr->pflags & PF_ITEMOUT) && !(leveltime & 1)) + if ((stplyr->itemflags & IF_ITEMOUT) && !(leveltime & 1)) localpatch[1] = kp_nodraw; } diff --git a/src/k_kart.c b/src/k_kart.c index b3b0956a4..371c54227 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -998,7 +998,7 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2) boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj) { const fixed_t minBump = 25*mapobjectscale; - fixed_t distx, disty, dist; + fixed_t distx, disty; fixed_t force; if ((!bounceMobj || P_MobjWasRemoved(bounceMobj)) @@ -1034,17 +1034,30 @@ boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj) return false; } - // Multiply by force - distx = FixedMul(force, distx); - disty = FixedMul(force, disty); - dist = FixedHypot(distx, disty); - { // Normalize to the desired push value. - fixed_t normalisedx = FixedDiv(distx, dist); - fixed_t normalisedy = FixedDiv(disty, dist); + fixed_t normalisedx; + fixed_t normalisedy; fixed_t bounceSpeed; + // Multiply by force + distx = FixedMul(force, distx); + disty = FixedMul(force, disty); + fixed_t dist = FixedHypot(distx, disty); + + normalisedx = FixedDiv(distx, dist); + normalisedy = FixedDiv(disty, dist); + + if (solidMobj->type == MT_WALLSPIKE) + { + fixed_t co = FCOS(solidMobj->angle); + fixed_t si = FSIN(solidMobj->angle); + + // Always thrust out toward the tip + normalisedx = FixedMul(normalisedx, abs(si)) - co; + normalisedy = FixedMul(normalisedy, abs(co)) - si; + } + bounceSpeed = FixedHypot(bounceMobj->momx, bounceMobj->momy); bounceSpeed = FixedMul(bounceSpeed, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3))); bounceSpeed += minBump; @@ -1263,6 +1276,12 @@ static boolean K_HasInfiniteTether(player_t *player) if (player->eggmanexplode > 0) return true; + if (player->trickcharge) + return true; + + if (player->infinitether) + return true; + return false; } @@ -1881,7 +1900,7 @@ static void K_SpawnGenericSpeedLines(player_t *player, boolean top) fast->colorized = true; fast->renderflags |= RF_ADD; } - else if (player->sliptideZipBoost) + else if (player->wavedashboost) { fast->color = SKINCOLOR_WHITE; fast->colorized = true; @@ -3231,9 +3250,9 @@ static void K_GetKartBoostPower(player_t *player) ); } - if (player->sliptideZipBoost) + if (player->wavedashboost) { - // NB: This is intentionally under the 25% threshold required to initiate a sliptide + // NB: This is intentionally under the 25% handleboost threshold required to initiate a sliptide ADDBOOST(8*FRACUNIT/10, 4*FRACUNIT, 2*SLIPTIDEHANDLING/5); // + 80% top speed, + 400% acceleration, +20% handling } @@ -3302,6 +3321,13 @@ static void K_GetKartBoostPower(player_t *player) ADDBOOST(6*FRACUNIT/20, FRACUNIT, 0); // + 30% top speed, + 100% acceleration, +0% handling } + if (player->trickcharge) + { + // NB: This is an acceleration-only boost. + // If this is applied earlier in the chain, it will diminish real speed boosts. + ADDBOOST(0, FRACUNIT, 2*SLIPTIDEHANDLING/10); // 0% speed 100% accel 20% handle + } + if (player->draftpower > 0) // Drafting { // 30% - 44%, each point of speed adds 1.75% @@ -4063,7 +4089,7 @@ void K_InitStumbleIndicator(player_t *player) return; } - if (player->stumbleIndicator != NULL && P_MobjWasRemoved(player->stumbleIndicator) == false) + if (P_MobjWasRemoved(player->stumbleIndicator) == false) { P_RemoveMobj(player->stumbleIndicator); } @@ -4074,7 +4100,7 @@ void K_InitStumbleIndicator(player_t *player) P_SetTarget(&new->target, player->mo); } -void K_InitSliptideZipIndicator(player_t *player) +void K_InitWavedashIndicator(player_t *player) { mobj_t *new = NULL; @@ -4088,17 +4114,61 @@ void K_InitSliptideZipIndicator(player_t *player) return; } - if (player->stumbleIndicator != NULL && P_MobjWasRemoved(player->sliptideZipIndicator) == false) + if (P_MobjWasRemoved(player->wavedashIndicator) == false) { - P_RemoveMobj(player->sliptideZipIndicator); + P_RemoveMobj(player->wavedashIndicator); } - new = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SLIPTIDEZIP); + new = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_WAVEDASH); - P_SetTarget(&player->sliptideZipIndicator, new); + P_SetTarget(&player->wavedashIndicator, new); P_SetTarget(&new->target, player->mo); } +void K_InitTrickIndicator(player_t *player) +{ + mobj_t *new = NULL; + + if (player == NULL) + { + return; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + return; + } + + if (P_MobjWasRemoved(player->trickIndicator) == false) + { + if (P_MobjWasRemoved(player->trickIndicator->tracer) == false) + { + P_RemoveMobj(player->trickIndicator->tracer); + } + + P_RemoveMobj(player->trickIndicator); + } + + UINT32 invis = (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player)); + + new = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TRICKINDICATOR); + + P_SetMobjState(new, S_INVISIBLE); + P_SetTarget(&player->trickIndicator, new); + P_SetTarget(&new->target, player->mo); + new->renderflags |= invis; + + mobj_t *secondlayer = P_SpawnMobjFromMobj(new, 0, 0, 0, MT_OVERLAY); + + P_SetMobjState(secondlayer, S_INVISIBLE); + P_SetTarget(&new->tracer, secondlayer); + P_SetTarget(&secondlayer->target, new); + secondlayer->renderflags |= invis; + + secondlayer->dispoffset = 1; + secondlayer->flags |= MF_DONTENCOREMAP; +} + void K_UpdateStumbleIndicator(player_t *player) { const angle_t fudge = ANG15; @@ -4206,11 +4276,11 @@ void K_UpdateStumbleIndicator(player_t *player) #define MIN_WAVEDASH_CHARGE ((7*TICRATE/16)*9) -static boolean K_IsLosingSliptideZip(player_t *player) +static boolean K_IsLosingWavedash(player_t *player) { if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) return true; - if (!K_Sliptiding(player) && player->sliptideZip < MIN_WAVEDASH_CHARGE) + if (!K_Sliptiding(player) && player->wavedash < MIN_WAVEDASH_CHARGE) return true; if (!K_Sliptiding(player) && player->drift == 0 && P_IsObjectOnGround(player->mo) && player->sneakertimer == 0 @@ -4219,7 +4289,7 @@ static boolean K_IsLosingSliptideZip(player_t *player) return false; } -void K_UpdateSliptideZipIndicator(player_t *player) +void K_UpdateWavedashIndicator(player_t *player) { mobj_t *mobj = NULL; @@ -4233,13 +4303,13 @@ void K_UpdateSliptideZipIndicator(player_t *player) return; } - if (player->sliptideZipIndicator == NULL || P_MobjWasRemoved(player->sliptideZipIndicator) == true) + if (player->wavedashIndicator == NULL || P_MobjWasRemoved(player->wavedashIndicator) == true) { - K_InitSliptideZipIndicator(player); + K_InitWavedashIndicator(player); return; } - mobj = player->sliptideZipIndicator; + mobj = player->wavedashIndicator; angle_t momentumAngle = K_MomentumAngle(player->mo); P_MoveOrigin(mobj, player->mo->x - FixedMul(40*mapobjectscale, FINECOSINE(momentumAngle >> ANGLETOFINESHIFT)), @@ -4249,7 +4319,7 @@ void K_UpdateSliptideZipIndicator(player_t *player) P_SetScale(mobj, 3 * player->mo->scale / 2); // No stored boost (or negligible enough that it might be a mistake) - if (player->sliptideZip <= HIDEWAVEDASHCHARGE) + if (player->wavedash <= HIDEWAVEDASHCHARGE) { mobj->renderflags |= RF_DONTDRAW; mobj->frame = 7; @@ -4258,8 +4328,8 @@ void K_UpdateSliptideZipIndicator(player_t *player) mobj->renderflags &= ~RF_DONTDRAW; - UINT32 chargeFrame = 7 - min(7, player->sliptideZip / 100); - UINT32 decayFrame = min(7, player->sliptideZipDelay / 2); + UINT32 chargeFrame = 7 - min(7, player->wavedash / 100); + UINT32 decayFrame = min(7, player->wavedashdelay / 2); if (max(chargeFrame, decayFrame) > mobj->frame) mobj->frame++; else if (max(chargeFrame, decayFrame) < mobj->frame) @@ -4268,7 +4338,7 @@ void K_UpdateSliptideZipIndicator(player_t *player) mobj->renderflags &= ~RF_TRANSMASK; mobj->renderflags |= RF_PAPERSPRITE; - if (K_IsLosingSliptideZip(player)) + if (K_IsLosingWavedash(player)) { // Decay timer's ticking mobj->rollangle += 3*ANG30/4; @@ -4282,6 +4352,69 @@ void K_UpdateSliptideZipIndicator(player_t *player) } } +static mobj_t *K_TrickCatholocismBlast(mobj_t *trickIndicator, fixed_t destscale, angle_t rollangle) +{ + // It's my last minute visual effect and I get to choose the ridiculous function name - toast 061123 + + mobj_t *catholocismBlast = P_SpawnGhostMobj(trickIndicator); // HOLY? + catholocismBlast->height = 1; + catholocismBlast->destscale = destscale; + catholocismBlast->fuse = 12; + catholocismBlast->rollangle = rollangle; + catholocismBlast->dispoffset = -1; + + return catholocismBlast; +} + +void K_UpdateTrickIndicator(player_t *player) +{ + mobj_t *mobj = NULL; + + if (player == NULL) + { + return; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + return; + } + + if (player->trickIndicator == NULL + || P_MobjWasRemoved(player->trickIndicator) == true + || player->trickIndicator->tracer == NULL + || P_MobjWasRemoved(player->trickIndicator->tracer) == true) + { + K_InitTrickIndicator(player); + return; + } + + mobj = player->trickIndicator; + + statenum_t test = (mobj->state-states); + + if (test >= S_TRICKINDICATOR_UNDERLAY_ARROW) + return; + + const fixed_t onidistance = 150*mapobjectscale; + + P_MoveOrigin( + mobj, + player->mo->x + P_ReturnThrustX(player->mo, player->mo->angle, onidistance), + player->mo->y + P_ReturnThrustY(player->mo, player->mo->angle, onidistance), + player->mo->z + (player->mo->height / 2)); + mobj->angle = player->mo->angle + ANGLE_90; + + if (player->trickpanel == TRICKSTATE_NONE + && test != S_INVISIBLE) + { + K_TrickCatholocismBlast(mobj, 1, ANGLE_22h); + + P_SetMobjState(mobj, S_INVISIBLE); + P_SetMobjState(mobj->tracer, S_INVISIBLE); + } +} + static boolean K_LastTumbleBounceCondition(player_t *player) { return (player->tumbleBounces > TUMBLEBOUNCES && player->tumbleHeight < 60); @@ -5100,6 +5233,16 @@ void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave) fixed_t sparkspeed = mobjinfo[MT_DRIFTELECTRICSPARK].speed; fixed_t sparkradius = 2 * shockscale * mobjinfo[MT_DRIFTELECTRICSPARK].radius; + if (player->trickcharge && !shockwave) + { + mobj_t *release = P_SpawnMobjFromMobj(mo, 0, 0, 0, MT_CHARGERELEASE); + P_SetTarget(&release->target, mo); + release->tics = 40; + release->scale /= 5; + release->destscale *= 2; + release->scalespeed = release->scale/2; + } + for (hdir = -1; hdir <= 1; hdir += 2) { for (vdir = -1; vdir <= 1; vdir += 2) @@ -5107,12 +5250,16 @@ void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave) fixed_t hspeed = FixedMul(hdir * sparkspeed, mo->scale); // P_InstaThrust treats speed as absolute fixed_t vspeed = vdir * sparkspeed; // P_SetObjectMomZ scales speed with object scale angle_t sparkangle = mo->angle + ANGLE_45; + mobj_t *spark; for (i = 0; i < 4; i++) { fixed_t xoff = P_ReturnThrustX(mo, sparkangle, sparkradius); fixed_t yoff = P_ReturnThrustY(mo, sparkangle, sparkradius); - mobj_t *spark = P_SpawnMobjFromMobj(mo, x + xoff, y + yoff, z, MT_DRIFTELECTRICSPARK); + if (player->trickcharge && !shockwave) + spark = P_SpawnMobjFromMobj(mo, x + xoff, y + yoff, z, MT_CHARGEEXTRA); + else + spark = P_SpawnMobjFromMobj(mo, x + xoff, y + yoff, z, MT_DRIFTELECTRICSPARK); spark->angle = sparkangle; spark->color = color; @@ -5125,7 +5272,14 @@ void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave) P_SetScale(spark, shockscale * spark->scale); if (shockwave) + { spark->frame |= FF_ADD; + } + else if (player->trickcharge) + { + spark->tics = 20; + } + sparkangle += ANGLE_90; K_ReduceVFX(spark, player); @@ -6229,6 +6383,7 @@ static void K_DoShrink(player_t *user) void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) { fixed_t thrust = 0; + boolean dontapplymomz = false; if (mo->player && mo->player->spectator) return; @@ -6243,23 +6398,73 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) if (vertispeed == 0) { - thrust = P_AproxDistance(mo->momx, mo->momy) * P_MobjFlip(mo); - thrust = FixedMul(thrust, FINESINE(ANGLE_22h >> ANGLETOFINESHIFT)); + vertispeed = P_AproxDistance(mo->momx, mo->momy); + vertispeed = FixedMul(vertispeed, FINESINE(ANGLE_22h >> ANGLETOFINESHIFT)); } - else + else if (vertispeed < 0) { - thrust = vertispeed * P_MobjFlip(mo); + dontapplymomz = 0; + vertispeed = -vertispeed; } + thrust = vertispeed * P_MobjFlip(mo); + if (mo->player) { - if (mo->player->sneakertimer) + if (!P_PlayerInPain(mo->player)) { - thrust = FixedMul(thrust, 5*FRACUNIT/4); + mo->player->trickpanel = TRICKSTATE_READY; + mo->player->pflags |= PF_TRICKDELAY; + + if (P_MobjWasRemoved(mo->player->trickIndicator) == false) + { + mobj_t *trickIndicator = mo->player->trickIndicator; + + P_SetScale(trickIndicator, + trickIndicator->destscale + = trickIndicator->old_scale + = trickIndicator->old_scale2 + = mo->scale/4); + trickIndicator->rollangle = 0; + + static const skincolornum_t trick_colors[] = { + SKINCOLOR_WHITE, // trickPanel == 1 -- was SKINCOLOR_GREY + SKINCOLOR_TAN, + SKINCOLOR_YELLOW, // trickPanel == 2 + SKINCOLOR_TANGERINE, + SKINCOLOR_KETCHUP, // trickPanel == 3 + SKINCOLOR_MOONSET, + SKINCOLOR_ULTRAMARINE, // trickPanel == 4 + }; + static const UINT8 numColors = sizeof(trick_colors) / sizeof(skincolornum_t); + + const fixed_t step = 8*FRACUNIT; + fixed_t trickcol = ((vertispeed - (step/2)) / step) - 1; + if (trickcol < 0) + trickcol = 0; + trickIndicator->color = trick_colors[min(trickcol, numColors - 1)]; + + P_SetMobjState(trickIndicator, S_TRICKINDICATOR_UNDERLAY); + + if (P_MobjWasRemoved(trickIndicator->tracer) == false) + { + P_SetScale(trickIndicator->tracer, + trickIndicator->tracer->destscale + = trickIndicator->tracer->old_scale + = trickIndicator->tracer->old_scale2 + = trickIndicator->destscale); + trickIndicator->tracer->rollangle = 0; + + trickIndicator->tracer->color = mo->player->trickIndicator->color; + + P_SetMobjState(trickIndicator->tracer, S_TRICKINDICATOR_OVERLAY); + } + } } - else if (mo->player->invincibilitytimer) + + if (mo->player->sneakertimer || mo->player->invincibilitytimer) { - thrust = FixedMul(thrust, 9*FRACUNIT/8); + thrust = FixedMul(thrust, (3*FRACUNIT)/2); } mo->player->tricktime = 0; // Reset post-hitlag timer @@ -6270,11 +6475,14 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) mo->player->fastfall = 0; } - mo->momz = FixedMul(thrust, mapobjectscale); - - if (mo->eflags & MFE_UNDERWATER) + if (dontapplymomz == false) { - mo->momz = FixedDiv(mo->momz, FixedSqrt(3*FRACUNIT)); + mo->momz = FixedMul(thrust, mapobjectscale); + + if (mo->eflags & MFE_UNDERWATER) + { + mo->momz = FixedDiv(mo->momz, FixedSqrt(3*FRACUNIT)); + } } P_ResetPitchRoll(mo); @@ -6414,7 +6622,10 @@ void K_PopPlayerShield(player_t *player) return; case KSHIELD_TOP: - Obj_GardenTopDestroy(player); + if (player->curshield == KSHIELD_TOP) + { + Obj_GardenTopDestroy(player); + } return; // everything is handled by Obj_GardenTopDestroy case KSHIELD_LIGHTNING: @@ -6629,11 +6840,11 @@ void K_DropHnextList(player_t *player) player->bananadrag = 0; - if (player->pflags & PF_EGGMANOUT) + if (player->itemflags & IF_EGGMANOUT) { - player->pflags &= ~PF_EGGMANOUT; + player->itemflags &= ~IF_EGGMANOUT; } - else if ((player->pflags & PF_ITEMOUT) + else if ((player->itemflags & IF_ITEMOUT) && (dropall || (--player->itemamount <= 0))) { player->itemamount = 0; @@ -7026,11 +7237,11 @@ static void K_MoveHeldObjects(player_t *player) { player->bananadrag = 0; - if (player->pflags & PF_EGGMANOUT) + if (player->itemflags & IF_EGGMANOUT) { - player->pflags &= ~PF_EGGMANOUT; + player->itemflags &= ~IF_EGGMANOUT; } - else if (player->pflags & PF_ITEMOUT) + else if (player->itemflags & IF_ITEMOUT) { player->itemamount = 0; K_UnsetItemOut(player); @@ -7045,11 +7256,11 @@ static void K_MoveHeldObjects(player_t *player) P_SetTarget(&player->mo->hnext, NULL); player->bananadrag = 0; - if (player->pflags & PF_EGGMANOUT) + if (player->itemflags & IF_EGGMANOUT) { - player->pflags &= ~PF_EGGMANOUT; + player->itemflags &= ~IF_EGGMANOUT; } - else if (player->pflags & PF_ITEMOUT) + else if (player->itemflags & IF_ITEMOUT) { player->itemamount = 0; K_UnsetItemOut(player); @@ -7946,7 +8157,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->sneakertimer || player->ringboost || player->driftboost || player->startboost || player->eggmanexplode || player->trickboost - || player->gateBoost || player->sliptideZipBoost) + || player->gateBoost || player->wavedashboost) { #if 0 if (player->invincibilitytimer) @@ -8033,7 +8244,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } if (player->itemtype == KITEM_NONE) - player->pflags &= ~PF_HOLDREADY; + player->itemflags &= ~IF_HOLDREADY; // DKR style camera for boosting if (player->karthud[khud_boostcam] != 0 || player->karthud[khud_destboostcam] != 0) @@ -8232,9 +8443,21 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->dropdashboost) player->dropdashboost--; - if (player->sliptideZipBoost > 0 && onground == true) + if (player->wavedashboost > 0 && onground == true) { - player->sliptideZipBoost--; + player->wavedashboost--; + } + + if (player->trickcharge > 0 && onground == true) + { + player->trickcharge--; + if (player->drift) + player->trickcharge = max(player->trickcharge, 1); + } + + if (player->infinitether > 0) + { + player->infinitether--; } if (player->spindashboost) @@ -8349,6 +8572,31 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->instaWhipCooldown--; } + if (player->dotrickfx && !player->mo->hitlag) + { + int i; + S_StartSoundAtVolume(player->mo, sfx_trick1, 255/2); + + if (!player->trickcharge) + { + for(i = 0;i < 5;i++) + { + mobj_t *aura = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height/2, MT_CHARGEAURA); + aura->angle = player->mo->angle + i*ANG15; + P_SetTarget(&aura->target, player->mo); + if (i == 0) + aura->extravalue2 = 1; + else + aura->renderflags |= RF_TRANS50; + aura->cvmem = leveltime; + } + } + + player->trickcharge = 8*TICRATE; + + player->dotrickfx = false; + } + // Don't screw up chain ring pickup/usage with instawhip charge. // If the button stays held, delay charge a bit. if (player->instaWhipChargeLockout) @@ -8378,7 +8626,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) S_StopSoundByID(player->mo, sfx_wchrg2); } - if (player->itemamount || player->respawn.state != RESPAWNST_NONE || player->pflags & (PF_ITEMOUT|PF_EGGMANOUT) || player->rocketsneakertimer || player->ringboxdelay) + if (player->itemamount || player->respawn.state != RESPAWNST_NONE || player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT) || player->rocketsneakertimer || player->ringboxdelay) player->instaWhipCharge = 0; if (player->tiregrease) @@ -8510,11 +8758,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_FlameDashLeftoverSmoke(player->mo); } - if (P_IsObjectOnGround(player->mo) && player->trickpanel != 0) + if (P_IsObjectOnGround(player->mo) && player->trickpanel != TRICKSTATE_NONE) { if (P_MobjFlip(player->mo) * player->mo->momz <= 0) { - player->trickpanel = 0; + player->trickpanel = TRICKSTATE_NONE; } } @@ -8700,14 +8948,14 @@ void K_KartPlayerAfterThink(player_t *player) K_KartResetPlayerColor(player); K_UpdateStumbleIndicator(player); - - K_UpdateSliptideZipIndicator(player); + K_UpdateWavedashIndicator(player); + K_UpdateTrickIndicator(player); // Move held objects (Bananas, Orbinaut, etc) K_MoveHeldObjects(player); // Jawz reticule (seeking) - if (player->itemtype == KITEM_JAWZ && (player->pflags & PF_ITEMOUT)) + if (player->itemtype == KITEM_JAWZ && (player->itemflags & IF_ITEMOUT)) { const INT32 lastTargID = player->lastjawztarget; mobj_t *lastTarg = NULL; @@ -9425,9 +9673,9 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) return 0; } - if (player->trickpanel != 0 && player->trickpanel < 4) + if (player->trickpanel == TRICKSTATE_READY || player->trickpanel == TRICKSTATE_FORWARD) { - // No turning during trick panel unless you did the upwards trick (4) + // Forward trick or rising from trickpanel return 0; } @@ -9523,6 +9771,12 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) // Weight has a small effect on turning turnfixed = FixedMul(turnfixed, weightadjust); + // Side trick + if (player->trickpanel == TRICKSTATE_LEFT || player->trickpanel == TRICKSTATE_RIGHT) + { + turnfixed /= 2; + } + return (turnfixed / FRACUNIT); } @@ -9628,6 +9882,8 @@ static void K_KartDrift(player_t *player, boolean onground) const UINT16 buttons = K_GetKartButtons(player); + boolean dokicker = false; + // Drifting is actually straffing + automatic turning. // Holding the Jump button will enable drifting. // (This comment is extremely funny) @@ -9690,6 +9946,7 @@ static void K_KartDrift(player_t *player, boolean onground) K_SpawnDriftBoostExplosion(player, 3); K_SpawnDriftElectricSparks(player, K_DriftSparkColor(player, player->driftcharge), false); + dokicker = true; } else if (player->driftcharge >= dsfour) { @@ -9704,6 +9961,17 @@ static void K_KartDrift(player_t *player, boolean onground) K_SpawnDriftBoostExplosion(player, 4); K_SpawnDriftElectricSparks(player, K_DriftSparkColor(player, player->driftcharge), false); + dokicker = true; + } + + if (player->trickcharge && dokicker) + { + player->driftboost += 20; + player->wavedashboost += 10; + P_Thrust(player->mo, pushdir, player->speed / 2); + S_StartSound(player->mo, sfx_gshba); + player->trickcharge = 0; + player->infinitether = TICRATE*2; } } @@ -9755,8 +10023,9 @@ static void K_KartDrift(player_t *player, boolean onground) player->drift = player->driftcharge = player->aizdriftstrat = 0; player->pflags &= ~(PF_BRAKEDRIFT|PF_GETSPARKS); // And take away wavedash properties: advanced cornering demands advanced finesse - player->sliptideZip = 0; - player->sliptideZipBoost = 0; + player->wavedash = 0; + player->wavedashboost = 0; + player->trickcharge = 0; } else if ((player->pflags & PF_DRIFTINPUT) && player->drift != 0) { @@ -9822,6 +10091,9 @@ static void K_KartDrift(player_t *player, boolean onground) driftadditive = 0; } + if (player->trickcharge && driftadditive) + driftadditive += 16; + // This spawns the drift sparks if ((player->driftcharge + driftadditive >= dsone) || (player->driftcharge < 0)) @@ -9858,11 +10130,11 @@ static void K_KartDrift(player_t *player, boolean onground) || (!player->aizdriftstrat) || (player->steering > 0) != (player->aizdriftstrat > 0)) { - if (!player->drift && player->steering && player->aizdriftstrat && player->sliptideZip // If we were sliptiding last tic, + if (!player->drift && player->steering && player->aizdriftstrat && player->wavedash // If we were sliptiding last tic, && (player->steering > 0) == (player->aizdriftstrat > 0) // we're steering in the right direction, && player->speed >= K_GetKartSpeed(player, false, true)) // and we're above the threshold to spawn dust... { - keepsliptide = true; // Then keep your current sliptide, but note the behavior change for sliptidezip handling. + keepsliptide = true; // Then keep your current sliptide, but note the behavior change for wavedash handling. } else { @@ -9890,9 +10162,9 @@ static void K_KartDrift(player_t *player, boolean onground) // This makes wavedash charge noticeably slower on even modest delay, despite the magnitude of the turn seeming the same. // So we only require 90% of a turn to get full charge strength. - player->sliptideZip += addCharge; + player->wavedash += addCharge; - if (player->sliptideZip >= MIN_WAVEDASH_CHARGE && (player->sliptideZip - addCharge) < MIN_WAVEDASH_CHARGE) + if (player->wavedash >= MIN_WAVEDASH_CHARGE && (player->wavedash - addCharge) < MIN_WAVEDASH_CHARGE) S_StartSound(player->mo, sfx_waved5); } @@ -9918,16 +10190,16 @@ static void K_KartDrift(player_t *player, boolean onground) if (!K_Sliptiding(player) || keepsliptide) { - if (!keepsliptide && K_IsLosingSliptideZip(player) && player->sliptideZip > 0) + if (!keepsliptide && K_IsLosingWavedash(player) && player->wavedash > 0) { - if (player->sliptideZip > HIDEWAVEDASHCHARGE && !S_SoundPlaying(player->mo, sfx_waved2)) + if (player->wavedash > HIDEWAVEDASHCHARGE && !S_SoundPlaying(player->mo, sfx_waved2)) S_StartSoundAtVolume(player->mo, sfx_waved2, 255); // Losing combo time, going to boost S_StopSoundByID(player->mo, sfx_waved1); S_StopSoundByID(player->mo, sfx_waved4); - player->sliptideZipDelay++; - if (player->sliptideZipDelay > TICRATE/2) + player->wavedashdelay++; + if (player->wavedashdelay > TICRATE/2) { - if (player->sliptideZip >= MIN_WAVEDASH_CHARGE) + if (player->wavedash >= MIN_WAVEDASH_CHARGE) { fixed_t maxZipPower = 2*FRACUNIT; fixed_t minZipPower = 1*FRACUNIT; @@ -9942,30 +10214,26 @@ static void K_KartDrift(player_t *player, boolean onground) fixed_t yourPowerReduction = FixedDiv(yourPenalty * FRACUNIT, penaltySpread * FRACUNIT); fixed_t yourPower = maxZipPower - FixedMul(yourPowerReduction, powerSpread); - int yourBoost = FixedInt(FixedMul(yourPower, player->sliptideZip/10 * FRACUNIT)); + int yourBoost = FixedInt(FixedMul(yourPower, player->wavedash/10 * FRACUNIT)); - /* - CONS_Printf("SZ %d MZ %d mZ %d pwS %d mP %d MP %d peS %d yPe %d yPR %d yPw %d yB %d\n", - player->sliptideZip, maxZipPower, minZipPower, powerSpread, minPenalty, maxPenalty, penaltySpread, yourPenalty, yourPowerReduction, yourPower, yourBoost); - */ + player->wavedashboost += yourBoost; - player->sliptideZipBoost += yourBoost; + S_StartSoundAtVolume(player->mo, sfx_waved3, 255); // Boost K_SpawnDriftBoostExplosion(player, 0); - S_StartSoundAtVolume(player->mo, sfx_waved3, 255); // Boost } S_StopSoundByID(player->mo, sfx_waved1); S_StopSoundByID(player->mo, sfx_waved2); S_StopSoundByID(player->mo, sfx_waved4); - player->sliptideZip = 0; - player->sliptideZipDelay = 0; + player->wavedash = 0; + player->wavedashdelay = 0; } } else { S_StopSoundByID(player->mo, sfx_waved1); S_StopSoundByID(player->mo, sfx_waved2); - if (player->sliptideZip > 0 && !S_SoundPlaying(player->mo, sfx_waved4)) + if (player->wavedash > 0 && !S_SoundPlaying(player->mo, sfx_waved4)) S_StartSoundAtVolume(player->mo, sfx_waved4, 255); // Passive woosh } @@ -9979,10 +10247,10 @@ static void K_KartDrift(player_t *player, boolean onground) } else { - player->sliptideZipDelay = 0; + player->wavedashdelay = 0; S_StopSoundByID(player->mo, sfx_waved2); S_StopSoundByID(player->mo, sfx_waved4); - if (player->sliptideZip > HIDEWAVEDASHCHARGE && !S_SoundPlaying(player->mo, sfx_waved1)) + if (player->wavedash > HIDEWAVEDASHCHARGE && !S_SoundPlaying(player->mo, sfx_waved1)) S_StartSoundAtVolume(player->mo, sfx_waved1, 255); // Charging } @@ -10178,7 +10446,7 @@ void K_StripItems(player_t *player) K_DropKitchenSink(player); player->itemtype = KITEM_NONE; player->itemamount = 0; - player->pflags &= ~(PF_ITEMOUT|PF_EGGMANOUT); + player->itemflags &= ~(IF_ITEMOUT|IF_EGGMANOUT); if (player->itemRoulette.eggman == false) { @@ -10541,7 +10809,7 @@ static void K_KartSpindashWind(mobj_t *parent) P_SetTarget(&wind->target, parent); - if (parent->player && parent->player->sliptideZipBoost) + if (parent->player && parent->player->wavedashboost) P_SetScale(wind, wind->scale * 2); if (parent->momx || parent->momy) @@ -10605,7 +10873,7 @@ static void K_KartSpindash(player_t *player) K_KartSpindashWind(player->mo); } - if ((player->sliptideZipBoost > 0) && (spawnWind == true)) + if ((player->wavedashboost > 0) && (spawnWind == true)) { K_KartSpindashWind(player->mo); } @@ -10771,9 +11039,10 @@ boolean K_FastFallBounce(player_t *player) player->pflags |= PF_UPDATEMYRESPAWN; + player->fastfall = 0; + player->mo->momz = bounce * P_MobjFlip(player->mo); - player->fastfall = 0; return true; } @@ -10973,7 +11242,7 @@ static void K_trickPanelTimingVisual(player_t *player, fixed_t momz) flame->sprite = SPR_TRCK; flame->frame = i|FF_FULLBRIGHT; - if (player->trickpanel <= 1 && !player->tumbleBounces) + if (player->trickpanel <= TRICKSTATE_READY && !player->tumbleBounces) { flame->tics = 2; flame->momx = player->mo->momx; @@ -10984,7 +11253,7 @@ static void K_trickPanelTimingVisual(player_t *player, fixed_t momz) { flame->tics = TICRATE; - if (player->trickpanel > 1) // we tricked + if (player->trickpanel > TRICKSTATE_READY) // we tricked { // Send the thing outwards via ghetto maths which involves redoing the whole 3d sphere again, witht the "vertical" angle shifted by 90 degrees. // There's probably a simplier way to do this the way I want to but this works. @@ -11021,7 +11290,7 @@ static void K_trickPanelTimingVisual(player_t *player, fixed_t momz) void K_SetItemOut(player_t *player) { - player->pflags |= PF_ITEMOUT; + player->itemflags |= IF_ITEMOUT; if (player->mo->scale >= FixedMul(GROW_PHYSICS_SCALE, mapobjectscale)) { @@ -11039,7 +11308,7 @@ void K_SetItemOut(player_t *player) void K_UnsetItemOut(player_t *player) { - player->pflags &= ~PF_ITEMOUT; + player->itemflags &= ~IF_ITEMOUT; player->itemscale = ITEMSCALE_NORMAL; player->bananadrag = 0; } @@ -11051,7 +11320,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { ticcmd_t *cmd = &player->cmd; boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->oldcmd.buttons & BT_ATTACK) && (player->respawn.state == RESPAWNST_NONE)); - boolean HOLDING_ITEM = (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)); + boolean HOLDING_ITEM = (player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)); boolean NO_HYUDORO = (player->stealingtimer == 0); if (!player->exiting) @@ -11077,9 +11346,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) || player->itemRoulette.active == true || player->rocketsneakertimer || player->eggmanexplode)) - player->pflags |= PF_USERINGS; + player->itemflags |= IF_USERINGS; else - player->pflags &= ~PF_USERINGS; + player->itemflags &= ~IF_USERINGS; } if (player->ringboxdelay) @@ -11120,7 +11389,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->instaWhipCooldown = 0; } - if (leveltime < starttime || player->pflags & (PF_ITEMOUT|PF_EGGMANOUT) || player->rocketsneakertimer || player->instaWhipCooldown) + if (leveltime < starttime || player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT) || player->rocketsneakertimer || player->instaWhipCooldown) { chargingwhip = false; player->instaWhipCharge = 0; @@ -11194,7 +11463,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // First, the really specific, finicky items that function without the item being directly in your item slot. { // Ring boosting - if (player->pflags & PF_USERINGS) + if (player->itemflags & IF_USERINGS) { if ((cmd->buttons & BT_ATTACK) && !player->ringdelay && player->rings > 0) { @@ -11218,13 +11487,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->eggmanexplode = 1; } // Eggman Monitor throwing - else if (player->pflags & PF_EGGMANOUT) + else if (player->itemflags & IF_EGGMANOUT) { if (ATTACK_IS_DOWN) { K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0, 0); K_PlayAttackTaunt(player->mo); - player->pflags &= ~PF_EGGMANOUT; + player->itemflags &= ~IF_EGGMANOUT; K_UpdateHnextList(player, true); } } @@ -11328,7 +11597,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) prev = mo; } } - else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) // Banana x3 thrown + else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Banana x3 thrown { K_ThrowKartItem(player, false, MT_BANANA, -1, 0, 0); K_PlayAttackTaunt(player->mo); @@ -11341,7 +11610,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { mobj_t *mo; player->itemamount--; - player->pflags |= PF_EGGMANOUT; + player->itemflags |= IF_EGGMANOUT; S_StartSound(player->mo, sfx_s254); mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD); if (mo) @@ -11391,7 +11660,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) prev = mo; } } - else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) // Orbinaut x3 thrown + else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Orbinaut x3 thrown { K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0, 0); K_PlayAttackTaunt(player->mo); @@ -11432,7 +11701,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) prev = mo; } } - else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->pflags & PF_ITEMOUT)) // Jawz thrown + else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Jawz thrown { K_ThrowKartItem(player, true, MT_JAWZ, 1, 0, 0); K_PlayAttackTaunt(player->mo); @@ -11458,12 +11727,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&player->mo->hnext, mo); } } - else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) + else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) { K_ThrowKartItem(player, false, MT_SSMINE, 1, 1, 0); K_PlayAttackTaunt(player->mo); player->itemamount--; - player->pflags &= ~PF_ITEMOUT; + player->itemflags &= ~IF_ITEMOUT; K_UpdateHnextList(player, true); } break; @@ -11493,12 +11762,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetTarget(&player->mo->hnext, mo); } } - else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) + else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) { K_ThrowKartItem(player, (player->throwdir > 0), MT_DROPTARGET, -1, 0, 0); K_PlayAttackTaunt(player->mo); player->itemamount--; - player->pflags &= ~PF_ITEMOUT; + player->itemflags &= ~IF_ITEMOUT; K_UpdateHnextList(player, true); } break; @@ -11507,8 +11776,19 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { INT32 ballhogmax = (player->itemamount) * BALLHOGINCREMENT; - if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY) - && (player->ballhogcharge < ballhogmax)) + // This construct looks a little goofy, but we're basically just + // trying to prevent rapid taps from restarting a charge, while + // still allowing quick tapfire. + // (The player still has to pace their shots like this, it's not + // semi-auto, but that's probably kind of okay.) + if (player->ballhogcharge && !(cmd->buttons & BT_ATTACK)) + player->ballhogtap = true; + + if (player->ballhogcharge == 0) + player->ballhogtap = false; + + boolean realcharge = (cmd->buttons & BT_ATTACK) && (player->itemflags & IF_HOLDREADY) && (player->ballhogcharge < ballhogmax); + if ((realcharge && !player->ballhogtap) || (player->ballhogtap && player->ballhogcharge < BALLHOGINCREMENT)) { player->ballhogcharge++; if (player->ballhogcharge % BALLHOGINCREMENT == 0) @@ -11530,11 +11810,11 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { if (cmd->buttons & BT_ATTACK) { - player->pflags &= ~PF_HOLDREADY; + player->itemflags &= ~IF_HOLDREADY; } else { - player->pflags |= PF_HOLDREADY; + player->itemflags |= IF_HOLDREADY; } if (player->ballhogcharge > 0) @@ -11570,7 +11850,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } player->ballhogcharge = 0; - player->pflags &= ~PF_HOLDREADY; + player->itemflags &= ~IF_HOLDREADY; } } } @@ -11704,7 +11984,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (!HOLDING_ITEM && NO_HYUDORO) { - if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY)) + if ((cmd->buttons & BT_ATTACK) && (player->itemflags & IF_HOLDREADY)) { if (player->bubbleblowup == 0) S_StartSound(player->mo, sfx_s3k75); @@ -11718,13 +11998,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->throwdir == -1) { P_InstaThrust(player->mo, player->mo->angle, player->speed + (80 * mapobjectscale)); - player->sliptideZipBoost += TICRATE; // Just for keeping speed briefly vs. tripwire etc. + player->wavedashboost += TICRATE; // Just for keeping speed briefly vs. tripwire etc. // If this doesn't turn out to be reliable, I'll change it to directly set leniency or something. } K_PlayAttackTaunt(player->mo); player->bubbleblowup = 0; player->bubblecool = 0; - player->pflags &= ~PF_HOLDREADY; + player->itemflags &= ~IF_HOLDREADY; player->itemamount--; } } @@ -11737,9 +12017,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->bubbleblowup--; if (player->bubblecool) - player->pflags &= ~PF_HOLDREADY; + player->itemflags &= ~IF_HOLDREADY; else - player->pflags |= PF_HOLDREADY; + player->itemflags |= IF_HOLDREADY; } } break; @@ -11763,7 +12043,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) flamemax = player->flamelength + TICRATE; // TICRATE leniency period, but we block most effects at flamelength 0 down below - if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY)) + if ((cmd->buttons & BT_ATTACK) && (player->itemflags & IF_HOLDREADY)) { const INT32 incr = (gametyperules & GTR_CLOSERPLAYERS) ? 4 : 2; player->flamemeter += incr; @@ -11805,13 +12085,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->flamemeter = 0; player->flamelength = 0; - player->pflags &= ~PF_HOLDREADY; + player->itemflags &= ~IF_HOLDREADY; player->itemamount--; } } else { - player->pflags |= PF_HOLDREADY; + player->itemflags |= IF_HOLDREADY; if (!(gametyperules & GTR_CLOSERPLAYERS) || leveltime % 6 == 0) { @@ -11848,7 +12128,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } break; case KITEM_POGOSPRING: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO && player->trickpanel == 0) + if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO && player->trickpanel == TRICKSTATE_NONE) { K_PlayBoostTaunt(player->mo); //K_DoPogoSpring(player->mo, 32<mo->hnext, mo); } } - else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->pflags & PF_ITEMOUT)) // Sink thrown + else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Sink thrown { K_ThrowKartItem(player, false, MT_SINK, 1, 2, 0); K_PlayAttackTaunt(player->mo); player->itemamount--; - player->pflags &= ~PF_ITEMOUT; + player->itemflags &= ~IF_ITEMOUT; K_UpdateHnextList(player, true); } break; @@ -11921,7 +12201,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) // No more! if (!player->itemamount) { - player->pflags &= ~PF_ITEMOUT; + player->itemflags &= ~IF_ITEMOUT; player->itemtype = KITEM_NONE; } @@ -11959,7 +12239,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->renderflags &= ~RF_BLENDMASK; } - if (player->trickpanel == 1) + if (player->trickpanel == TRICKSTATE_READY) { const angle_t lr = ANGLE_45; fixed_t momz = FixedDiv(player->mo->momz, mapobjectscale); // bring momz back to scale... @@ -11968,7 +12248,21 @@ void K_MoveKartPlayer(player_t *player, boolean onground) fixed_t basespeed = FixedMul(invertscale, K_GetKartSpeed(player, false, false)); // at WORSE, keep your normal speed when tricking. fixed_t speed = FixedMul(invertscale, FixedMul(speedmult, P_AproxDistance(player->mo->momx, player->mo->momy))); - K_trickPanelTimingVisual(player, momz); + if (P_MobjWasRemoved(player->trickIndicator) == false) + { + const fixed_t indicatormult = 3*(mapobjectscale/5); + player->trickIndicator->destscale = FixedMul(speedmult + FRACUNIT, indicatormult); + + fixed_t trans = ((player->trickIndicator->scale * 9)/indicatormult) - 9; + if (trans < 10) // it's fine if it stays barely visible imo + { + UINT32 renderflags = player->trickIndicator->renderflags & ~RF_TRANSMASK; + if (trans > 0) + renderflags |= (trans << RF_TRANSSHIFT); + + player->trickIndicator->renderflags = renderflags; + } + } // streaks: if (momz*P_MobjFlip(player->mo) > 0) // only spawn those while you're going upwards relative to your current gravity @@ -12013,10 +12307,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->tumbleBounces = 1; player->pflags &= ~PF_TUMBLESOUND; player->tumbleHeight = 30; // Base tumble bounce height - player->trickpanel = 0; - K_trickPanelTimingVisual(player, momz); // fail trick visual + player->trickpanel = TRICKSTATE_NONE; P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); - if (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)) + if (player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)) { //K_PopPlayerShield(player); // shield is just being yeeted, don't pop K_DropHnextList(player); @@ -12025,36 +12318,83 @@ void K_MoveKartPlayer(player_t *player, boolean onground) else if (!(player->pflags & PF_TRICKDELAY)) // don't allow tricking at the same frame you tumble obv { + // For tornado trick effects + angle_t tornadotrickspeed = ANG30; + const angle_t angledelta = FixedAngle(36*FRACUNIT); + angle_t baseangle = player->mo->angle + angledelta/2; + INT16 aimingcompare = abs(cmd->throwdir) - abs(cmd->turning); // Uses cmd->turning over steering intentionally. #define TRICKTHRESHOLD (KART_FULLTURN/4) if (aimingcompare < -TRICKTHRESHOLD) // side trick { + S_StartSoundAtVolume(player->mo, sfx_trick0, 255/2); + player->dotrickfx = true; + + // Calculate speed boost decay: + // Base speed boost duration is 35 tics. + // At most, lose 3/4th of your boost. + player->trickboostdecay = min(TICRATE*3/4, abs(momz/FRACUNIT)); + if (cmd->turning > 0) { P_InstaThrust(player->mo, player->mo->angle + lr, max(basespeed, speed*5/2)); - player->trickpanel = 2; + player->trickpanel = TRICKSTATE_RIGHT; + + if (P_MobjWasRemoved(player->trickIndicator) == false) + { + player->trickIndicator->rollangle = ANGLE_270; + } + + player->drawangle -= ANGLE_45; + P_SetPlayerMobjState(player->mo, S_KART_FAST_LOOK_L); } else { P_InstaThrust(player->mo, player->mo->angle - lr, max(basespeed, speed*5/2)); - player->trickpanel = 3; + player->trickpanel = TRICKSTATE_LEFT; + + if (P_MobjWasRemoved(player->trickIndicator) == false) + { + player->trickIndicator->rollangle = ANGLE_90; + } + + tornadotrickspeed = InvAngle(tornadotrickspeed); + + player->drawangle += ANGLE_45; + P_SetPlayerMobjState(player->mo, S_KART_FAST_LOOK_R); } } else if (aimingcompare > TRICKTHRESHOLD) // forward/back trick { - if (cmd->throwdir > 0) // back trick + S_StartSoundAtVolume(player->mo, sfx_trick0, 255/2); + player->dotrickfx = true; + + // Calculate speed boost decay: + // Base speed boost duration is 35 tics. + // At most, lose 3/4th of your boost. + player->trickboostdecay = min(TICRATE*3/4, abs(momz/FRACUNIT)); + + if (cmd->throwdir > 0) // forward trick { + if (player->mo->momz * P_MobjFlip(player->mo) > 0) { player->mo->momz = 0; } P_InstaThrust(player->mo, player->mo->angle, max(basespeed, speed*3)); - player->trickpanel = 2; + player->trickpanel = TRICKSTATE_FORWARD; + + if (P_MobjWasRemoved(player->trickIndicator) == false) + { + player->trickIndicator->rollangle = 0; + } + + P_SetPlayerMobjState(player->mo, S_KART_FAST); } - else if (cmd->throwdir < 0) + else if (cmd->throwdir < 0) // back trick { player->mo->momx /= 3; player->mo->momy /= 3; @@ -12064,41 +12404,154 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->momz = 0; // relative = false; } - // Calculate speed boost decay: - // Base speed boost duration is 35 tics. - // At most, lose 3/4th of your boost. - player->trickboostdecay = min(TICRATE*3/4, abs(momz/FRACUNIT)); - //CONS_Printf("decay: %d\n", player->trickboostdecay); - player->mo->momz += P_MobjFlip(player->mo)*48*mapobjectscale; - player->trickpanel = 4; + player->trickpanel = TRICKSTATE_BACK; + + if (P_MobjWasRemoved(player->trickIndicator) == false) + { + player->trickIndicator->rollangle = ANGLE_180; + } + + //tornadotrickspeed = InvAngle(tornadotrickspeed); + + //player->drawangle += ANGLE_45; + P_SetPlayerMobjState(player->mo, S_KART_FAST); } } #undef TRICKTHRESHOLD // Finalise everything. - if (player->trickpanel != 1) // just changed from 1? + if (player->trickpanel != TRICKSTATE_READY) // just changed from 1? { player->mo->hitlag = TRICKLAG; player->mo->eflags &= ~MFE_DAMAGEHITLAG; - K_trickPanelTimingVisual(player, momz); - if (abs(momz) < FRACUNIT*99) // Let's use that as baseline for PERFECT trick. { player->karthud[khud_trickcool] = TICRATE; } + + INT32 j; + skincolornum_t trickcolor = SKINCOLOR_NONE; + + if (P_MobjWasRemoved(player->trickIndicator) == false) + trickcolor = player->trickIndicator->color; + + if (player->trickpanel == TRICKSTATE_FORWARD) + { + for (j = 0; j < 2; j++) + { + mobj_t *fwush = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_FORWARDTRICK); + + P_SetTarget(&fwush->target, player->mo); + fwush->hitlag = TRICKLAG; + fwush->color = trickcolor; + fwush->renderflags |= RF_DONTDRAW; + fwush->flags2 |= MF2_AMBUSH; // don't interp on first think + fwush->threshold = 0; + + fwush->movedir = player->mo->angle; + if (j == 0) + { + fwush->angle = fwush->old_angle = fwush->movedir + ANGLE_135; + fwush->movefactor = 1; + } + else + { + fwush->angle = fwush->old_angle = fwush->movedir - ANGLE_135; + fwush->movefactor = -1; + } + } + } + else for (j = 0; j < 8; j++, baseangle += angledelta) + { + mobj_t *swipe = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SIDETRICK); + + if (player->trickpanel == TRICKSTATE_BACK) + P_SetMobjState(swipe, S_BACKTRICK); + + P_SetTarget(&swipe->target, player->mo); + swipe->hitlag = TRICKLAG; + swipe->color = trickcolor; + swipe->angle = baseangle + ANGLE_90; + swipe->renderflags |= RF_DONTDRAW; + swipe->flags2 |= MF2_AMBUSH; // don't interp on first think + swipe->movedir = tornadotrickspeed; + swipe->frame |= (j % 4); + swipe->threshold = 0; + + // This is so they make a 10-sided shape with one-sprite gap + if (j != 3) + continue; + + baseangle += angledelta; + } + + if (P_MobjWasRemoved(player->trickIndicator) == false) + { + K_TrickCatholocismBlast(player->trickIndicator, player->trickIndicator->scale*10, 0); + + player->trickIndicator->renderflags &= ~RF_TRANSMASK; + + P_InstaScale(player->trickIndicator, 3*mapobjectscale/2); + player->trickIndicator->old_scale = player->trickIndicator->scale; + + P_SetMobjState(player->trickIndicator, S_TRICKINDICATOR_UNDERLAY_ARROW); + if (P_MobjWasRemoved(player->trickIndicator->tracer) == false) + { + P_InstaScale(player->trickIndicator->tracer, player->trickIndicator->scale); + player->trickIndicator->tracer->old_scale = player->trickIndicator->tracer->scale; + + P_SetMobjState(player->trickIndicator->tracer, S_TRICKINDICATOR_OVERLAY_ARROW); + } + } } } - } - else if (player->trickpanel == 4 && P_IsObjectOnGround(player->mo)) // Upwards trick landed! - { - //CONS_Printf("apply boost\n"); - S_StartSound(player->mo, sfx_s23c); - K_SpawnDashDustRelease(player); - player->trickboost = TICRATE - player->trickboostdecay; - player->trickpanel = player->trickboostdecay = 0; + K_trickPanelTimingVisual(player, momz); + } + else if ((player->trickpanel != TRICKSTATE_NONE) && P_IsObjectOnGround(player->mo)) // Landed from trick + { + K_SpawnDashDustRelease(player); + + if (player->fastfall) + { + if (player->curshield != KSHIELD_BUBBLE) // Allow bubblebounce (it's cute) but don't give standard trick rewards + { + P_InstaThrust(player->mo, player->mo->angle, 2*abs(player->fastfall)/3 + 15*FRACUNIT); + player->mo->hitlag = 3; + S_StartSound(player->mo, sfx_gshba); + player->fastfall = 0; // intentionally skip bounce + player->trickcharge = 0; + + UINT8 i; + for (i = 0; i < 4; i++) + { + mobj_t *arc = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_CHARGEFALL); + P_SetTarget(&arc->target, player->mo); + arc->extravalue1 = i; + } + } + } + else + { + S_StartSound(player->mo, sfx_s23c); + + UINT8 award = TICRATE - player->trickboostdecay; + + player->trickboost = award; + K_AwardPlayerRings(player, + (TICRATE-player->trickboostdecay) * player->lastairtime/3 / TICRATE, // Scale ring award by same amount as trickboost + true); + + if (player->trickpanel == TRICKSTATE_FORWARD) + player->trickboostpower /= 18; + else if (player->trickpanel != TRICKSTATE_BACK) + player->trickboostpower /= 9; + } + + player->trickpanel = TRICKSTATE_NONE; + player->trickboostdecay = 0; } // Wait until we let go off the control stick to remove the delay @@ -12619,4 +13072,40 @@ void K_SetTireGrease(player_t *player, tic_t tics) player->tiregrease = tics; } +// somewhat sensible check for HUD sounds in a post-bot-takeover world +boolean K_IsPlayingDisplayPlayer(player_t *player) +{ + return P_IsDisplayPlayer(player) && (!player->exiting); +} + +boolean K_PlayerCanPunt(player_t *player) +{ + if (player->invincibilitytimer > 0) + { + return true; + } + + if (player->flamedash > 0 && player->itemtype == KITEM_FLAMESHIELD) + { + return true; + } + + if (player->growshrinktimer > 0) + { + return true; + } + + if (player->tripwirePass >= TRIPWIRE_BLASTER && player->speed >= 2 * K_GetKartSpeed(player, false, false)) + { + return true; + } + + return false; +} + +void K_MakeObjectReappear(mobj_t *mo) +{ + (!P_MobjWasRemoved(mo->punt_ref) ? mo->punt_ref : mo)->reappear = leveltime + (30*TICRATE); +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index 7a4424f08..44b7283dc 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -119,9 +119,11 @@ angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll); void K_StumblePlayer(player_t *player); boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir); void K_InitStumbleIndicator(player_t *player); -void K_InitSliptideZipIndicator(player_t *player); +void K_InitWavedashIndicator(player_t *player); +void K_InitTrickIndicator(player_t *player); void K_UpdateStumbleIndicator(player_t *player); -void K_UpdateSliptideZipIndicator(player_t *player); +void K_UpdateWavedashIndicator(player_t *player); +void K_UpdateTrickIndicator(player_t *player); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); void K_GiveBumpersToPlayer(player_t *player, player_t *victim, UINT8 amount); @@ -243,6 +245,11 @@ boolean K_isPlayerInSpecialState(player_t *p); void K_SetTireGrease(player_t *player, tic_t tics); +boolean K_IsPlayingDisplayPlayer(player_t *player); + +boolean K_PlayerCanPunt(player_t *player); +void K_MakeObjectReappear(mobj_t *mo); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_menu.h b/src/k_menu.h index c02f29d50..f4144ac39 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -979,6 +979,7 @@ extern consvar_t cv_dummyprofilename; extern consvar_t cv_dummyprofileplayername; extern consvar_t cv_dummyprofilekickstart; extern consvar_t cv_dummyprofileautoroulette; +extern consvar_t cv_dummyprofilelitesteer; extern consvar_t cv_dummyprofilerumble; void M_ResetOptions(void); diff --git a/src/k_objects.h b/src/k_objects.h index c88bf3320..619b8f81c 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -126,6 +126,11 @@ void Obj_BlockRingThink(mobj_t *ring); void Obj_BlockBodyThink(mobj_t *body); void Obj_GuardBreakThink(mobj_t *fx); +void Obj_ChargeAuraThink(mobj_t *aura); +void Obj_ChargeFallThink(mobj_t *charge); +void Obj_ChargeReleaseThink(mobj_t *release); +void Obj_ChargeExtraThink(mobj_t *extra); + /* Ring Shooter */ boolean Obj_RingShooterThinker(mobj_t *mo); boolean Obj_PlayerRingShooterFreeze(player_t *const player); @@ -136,7 +141,7 @@ void Obj_UpdateRingShooterFace(mobj_t *part); /* Follower Audience */ void Obj_AudienceInit(mobj_t * mobj, mapthing_t *mthing, INT32 followerpick); -void Obj_AudienceThink(mobj_t * mobj, boolean focusonplayer); +void Obj_AudienceThink(mobj_t * mobj, boolean focusonplayer, boolean checkdeathpit); /* Random Item Boxes */ void Obj_RandomItemVisuals(mobj_t *mobj); @@ -168,7 +173,7 @@ void Obj_BlockSuperFlicky(mobj_t *flicky); void Obj_SuperFlickyPlayerCollide(mobj_t *flicky, mobj_t *player); void Obj_SuperFlickyLanding(mobj_t *flicky); mobj_t *Obj_SuperFlickyOwner(const mobj_t *flicky); -boolean Obj_IsSuperFlickyWhippable(const mobj_t *flicky); +boolean Obj_IsSuperFlickyWhippable(const mobj_t *flicky, const mobj_t *target); boolean Obj_IsSuperFlickyTargettingYou(const mobj_t *flicky, mobj_t *player); /* Battle/Power-UP UFO */ diff --git a/src/k_profiles.c b/src/k_profiles.c index f1ff15210..cd2491731 100644 --- a/src/k_profiles.c +++ b/src/k_profiles.c @@ -72,6 +72,7 @@ profile_t* PR_MakeProfile( new->followercolor = fcol; new->kickstartaccel = false; new->autoroulette = false; + new->litesteer = true; // Copy from gamecontrol directly as we'll be setting controls up directly in the profile. memcpy(new->controls, controlarray, sizeof(new->controls)); @@ -89,6 +90,7 @@ profile_t* PR_MakeProfileFromPlayer(const char *prname, const char *pname, const // Player bound cvars: new->kickstartaccel = cv_kickstartaccel[pnum].value; new->autoroulette = cv_autoroulette[pnum].value; + new->litesteer = cv_litesteer[pnum].value; new->rumble = cv_rumble[pnum].value; return new; @@ -276,6 +278,7 @@ void PR_SaveProfiles(void) // Consvars. WRITEUINT8(save.p, profilesList[i]->kickstartaccel); WRITEUINT8(save.p, profilesList[i]->autoroulette); + WRITEUINT8(save.p, profilesList[i]->litesteer); WRITEUINT8(save.p, profilesList[i]->rumble); // Controls. @@ -425,6 +428,17 @@ void PR_LoadProfiles(void) profilesList[i]->autoroulette = (boolean)READUINT8(save.p); } + // 7->8, add litesteer + if (version < 8) + { + profilesList[i]->litesteer = true; + + } + else + { + profilesList[i]->litesteer = (boolean)READUINT8(save.p); + } + if (version < 4) { profilesList[i]->rumble = true; @@ -478,6 +492,7 @@ static void PR_ApplyProfile_Settings(profile_t *p, UINT8 playernum) // toggles CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel); CV_StealthSetValue(&cv_autoroulette[playernum], p->autoroulette); + CV_StealthSetValue(&cv_litesteer[playernum], p->litesteer); // set controls... memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); diff --git a/src/k_profiles.h b/src/k_profiles.h index 3b941500f..e920fe9be 100644 --- a/src/k_profiles.h +++ b/src/k_profiles.h @@ -31,7 +31,7 @@ extern "C" { #define SKINNAMESIZE 16 #define PROFILENAMELEN 6 -#define PROFILEVER 7 +#define PROFILEVER 8 #define MAXPROFILES 16 #define PROFILESFILE "ringprofiles.prf" #define PROFILE_GUEST 0 @@ -74,7 +74,8 @@ struct profile_t // Player-specific consvars. // @TODO: List all of those boolean kickstartaccel; // cv_kickstartaccel - boolean autoroulette; // cv_autoroulette + boolean autoroulette; // cv_autoroulette + boolean litesteer; // cv_litesteer boolean rumble; // cv_rumble // Finally, control data itself diff --git a/src/k_respawn.c b/src/k_respawn.c index 21af8253d..f9c03e499 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -156,7 +156,9 @@ void K_DoIngameRespawn(player_t *player) player->ringboost = 0; player->driftboost = player->strongdriftboost = 0; player->gateBoost = 0; - player->sliptideZip = player->sliptideZipBoost = player->sliptideZipDelay = 0; + player->trickcharge = 0; + player->infinitether = 0; + player->wavedash = player->wavedashboost = player->wavedashdelay = 0; K_TumbleInterrupt(player); P_ResetPlayer(player); @@ -295,7 +297,7 @@ void K_DoIngameRespawn(player_t *player) player->respawn.init = true; player->respawn.fast = true; player->respawn.returnspeed = 0; - player->laps = player->lastsafelap; + player->laps = min(player->laps, player->lastsafelap); player->respawn.airtimer = player->airtime; player->respawn.truedeath = !!(player->pflags & PF_FAULT); diff --git a/src/k_roulette.c b/src/k_roulette.c index 1b378aeae..107908fd7 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -192,7 +192,7 @@ static kartitems_t K_KartItemReelTimeAttack[] = static kartitems_t K_KartItemReelSPBAttack[] = { - KITEM_GACHABOM, + KITEM_DROPTARGET, KITEM_SUPERRING, KITEM_NONE }; @@ -1242,7 +1242,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) return; } - if (K_TimeAttackRules() == true) + if (K_TimeAttackRules() == true && !(modeattacking & ATTACKING_SPB)) { // Time Attack rules; use a consistent speed. roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK; @@ -1622,7 +1622,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const 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 (confirmItem == true && (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT|PF_USERINGS)) == 0) + if (confirmItem == true && (player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT|IF_USERINGS)) == 0) { if (roulette->eggman == true) { @@ -1633,7 +1633,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) //player->karthud[khud_itemblinkmode] = 1; //player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT); - if (P_IsDisplayPlayer(player)) + if (K_IsPlayingDisplayPlayer(player)) { S_StartSound(NULL, sfx_itrole); } @@ -1680,7 +1680,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) player->karthud[khud_itemblinkmode] = 0; player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT); - if (P_IsDisplayPlayer(player)) + if (K_IsPlayingDisplayPlayer(player)) { if (roulette->ringbox) { @@ -1714,7 +1714,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) // This makes the roulette produce the random noises. roulette->sound = (roulette->sound + 1) % 8; - if (P_IsDisplayPlayer(player)) + if (K_IsPlayingDisplayPlayer(player)) { if (roulette->ringbox) S_StartSound(NULL, sfx_s240); diff --git a/src/k_terrain.c b/src/k_terrain.c index 36edb5261..d2d9a3f8a 100644 --- a/src/k_terrain.c +++ b/src/k_terrain.c @@ -485,8 +485,6 @@ void K_ProcessTerrainEffect(mobj_t *mo) fixed_t speed = FixedHypot(mo->momx, mo->momy); fixed_t upwards = 16 * terrain->trickPanel; - player->trickpanel = 1; - player->pflags |= PF_TRICKDELAY; K_DoPogoSpring(mo, upwards, 1); // Reduce speed @@ -551,7 +549,7 @@ void K_ProcessTerrainEffect(mobj_t *mo) P_InstaThrust(player->mo, thrustAngle, max(thrustSpeed, 2*playerSpeed)); player->dashpadcooldown = TICRATE/3; - player->trickpanel = 0; + player->trickpanel = TRICKSTATE_NONE; player->floorboost = 2; S_StartSound(player->mo, sfx_cdfm62); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index ce4668038..51751f996 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -106,7 +106,9 @@ enum mobj_e { mobj_tid, mobj_special, mobj_args, - mobj_stringargs + mobj_stringargs, + mobj_reappear, + mobj_punt_ref, }; static const char *const mobj_opt[] = { @@ -194,6 +196,8 @@ static const char *const mobj_opt[] = { "special", "args", "stringargs", + "reappear", + "punt_ref", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -493,6 +497,17 @@ static int mobj_get(lua_State *L) case mobj_stringargs: LUA_PushUserdata(L, mo->thing_stringargs, META_THINGSTRINGARGS); break; + case mobj_reappear: + lua_pushinteger(L, mo->reappear); + break; + case mobj_punt_ref: + if (mo->punt_ref && P_MobjWasRemoved(mo->punt_ref)) + { // don't put invalid mobj back into Lua. + P_SetTarget(&mo->punt_ref, NULL); + return 0; + } + LUA_PushUserdata(L, mo->punt_ref, META_MOBJ); + break; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -883,6 +898,18 @@ static int mobj_set(lua_State *L) return NOSET; case mobj_stringargs: return NOSET; + case mobj_reappear: + mo->reappear = luaL_checkinteger(L, 3); + break; + case mobj_punt_ref: + if (lua_isnil(L, 3)) + P_SetTarget(&mo->punt_ref, NULL); + else + { + mobj_t *punt_ref = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&mo->punt_ref, punt_ref); + } + break; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 7cd2ef130..2d39cc7c9 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -233,6 +233,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->lastpickuptype); else if (fastcmp(field,"airtime")) lua_pushinteger(L, plr->airtime); + else if (fastcmp(field,"lastairtime")) + lua_pushinteger(L, plr->lastairtime); else if (fastcmp(field,"flashing")) lua_pushinteger(L, plr->flashing); else if (fastcmp(field,"spinouttimer")) @@ -255,10 +257,14 @@ static int player_get(lua_State *L) lua_pushboolean(L, plr->flipDI); else if (fastcmp(field,"markedfordeath")) lua_pushboolean(L, plr->markedfordeath); + else if (fastcmp(field,"dotrickfx")) + lua_pushboolean(L, plr->dotrickfx); else if (fastcmp(field,"ringboxdelay")) lua_pushinteger(L, plr->ringboxdelay); else if (fastcmp(field,"ringboxaward")) lua_pushinteger(L, plr->ringboxaward); + else if (fastcmp(field,"itemflags")) + lua_pushinteger(L, plr->itemflags); else if (fastcmp(field,"drift")) lua_pushinteger(L, plr->drift); else if (fastcmp(field,"driftcharge")) @@ -323,12 +329,16 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->tripwireLeniency); else if (fastcmp(field,"tripwireReboundDelay")) lua_pushinteger(L, plr->tripwireReboundDelay); - else if (fastcmp(field,"sliptideZip")) - lua_pushinteger(L, plr->sliptideZip); - else if (fastcmp(field,"sliptideZipDelay")) - lua_pushinteger(L, plr->sliptideZipDelay); - else if (fastcmp(field,"sliptideZipBoost")) - lua_pushinteger(L, plr->sliptideZipBoost); + else if (fastcmp(field,"wavedash")) + lua_pushinteger(L, plr->wavedash); + else if (fastcmp(field,"wavedashdelay")) + lua_pushinteger(L, plr->wavedashdelay); + else if (fastcmp(field,"wavedashboost")) + lua_pushinteger(L, plr->wavedashboost); + else if (fastcmp(field,"trickcharge")) + lua_pushinteger(L, plr->trickcharge); + else if (fastcmp(field,"infinitether")) + lua_pushinteger(L, plr->infinitether); else if (fastcmp(field,"lastsafelap")) lua_pushinteger(L, plr->lastsafelap); else if (fastcmp(field,"instaWhipCharge")) @@ -383,6 +393,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->flamelength); else if (fastcmp(field,"ballhogcharge")) lua_pushinteger(L, plr->ballhogcharge); + else if (fastcmp(field,"ballhogtap")) + lua_pushinteger(L, plr->ballhogtap); else if (fastcmp(field,"hyudorotimer")) lua_pushinteger(L, plr->hyudorotimer); else if (fastcmp(field,"stealingtimer")) @@ -717,6 +729,8 @@ static int player_set(lua_State *L) plr->airtime = luaL_checkinteger(L, 3); else if (fastcmp(field,"airtime")) plr->airtime = luaL_checkinteger(L, 3); + else if (fastcmp(field,"lastairtime")) + plr->lastairtime = luaL_checkinteger(L, 3); else if (fastcmp(field,"flashing")) plr->flashing = luaL_checkinteger(L, 3); else if (fastcmp(field,"spinouttimer")) @@ -739,10 +753,14 @@ static int player_set(lua_State *L) plr->flipDI = luaL_checkboolean(L, 3); else if (fastcmp(field,"markedfordeath")) plr->markedfordeath = luaL_checkboolean(L, 3); + else if (fastcmp(field,"dotrickfx")) + plr->dotrickfx = luaL_checkboolean(L, 3); else if (fastcmp(field,"ringboxdelay")) plr->ringboxdelay = luaL_checkinteger(L, 3); else if (fastcmp(field,"ringboxaward")) plr->ringboxaward = luaL_checkinteger(L, 3); + else if (fastcmp(field,"itemflags")) + plr->itemflags = luaL_checkinteger(L, 3); else if (fastcmp(field,"drift")) plr->drift = luaL_checkinteger(L, 3); else if (fastcmp(field,"driftcharge")) @@ -807,12 +825,16 @@ static int player_set(lua_State *L) plr->tripwireLeniency = luaL_checkinteger(L, 3); else if (fastcmp(field,"tripwireReboundDelay")) plr->tripwireReboundDelay = luaL_checkinteger(L, 3); - else if (fastcmp(field,"sliptideZip")) - plr->sliptideZip = luaL_checkinteger(L, 3); - else if (fastcmp(field,"sliptideZipDelay")) - plr->sliptideZipDelay = luaL_checkinteger(L, 3); - else if (fastcmp(field,"sliptideZipBoost")) - plr->sliptideZipBoost = luaL_checkinteger(L, 3); + else if (fastcmp(field,"wavedash")) + plr->wavedash = luaL_checkinteger(L, 3); + else if (fastcmp(field,"wavedashdelay")) + plr->wavedashdelay = luaL_checkinteger(L, 3); + else if (fastcmp(field,"wavedashboost")) + plr->wavedashboost = luaL_checkinteger(L, 3); + else if (fastcmp(field,"trickcharge")) + plr->trickcharge = luaL_checkinteger(L, 3); + else if (fastcmp(field,"infinitether")) + plr->infinitether = luaL_checkinteger(L, 3); else if (fastcmp(field,"lastsafelap")) plr->lastsafelap = luaL_checkinteger(L, 3); else if (fastcmp(field,"instaWhipCharge")) @@ -867,6 +889,8 @@ static int player_set(lua_State *L) plr->flamelength = luaL_checkinteger(L, 3); else if (fastcmp(field,"ballhogcharge")) plr->ballhogcharge = luaL_checkinteger(L, 3); + else if (fastcmp(field,"ballhogtap")) + plr->ballhogtap = luaL_checkinteger(L, 3); else if (fastcmp(field,"hyudorotimer")) plr->hyudorotimer = luaL_checkinteger(L, 3); else if (fastcmp(field,"stealingtimer")) diff --git a/src/math/fixed.hpp b/src/math/fixed.hpp new file mode 100644 index 000000000..0e5cae4c1 --- /dev/null +++ b/src/math/fixed.hpp @@ -0,0 +1,107 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_fixed_hpp +#define math_fixed_hpp + +#include + +#include "traits.hpp" + +#include "../m_fixed.h" + +namespace srb2::math +{ + +struct Fixed +{ + static Fixed copysign(fixed_t x, fixed_t y) { return (x < 0) != (y < 0) ? -x : x; } + static Fixed hypot(fixed_t x, fixed_t y) { return FixedHypot(x, y); } + + constexpr Fixed() : val_(0) {} + constexpr Fixed(fixed_t val) : val_(val) {} + + template , bool> = true> + Fixed(T val) : val_(FloatToFixed(val)) {} + + Fixed(const Fixed& b) = default; + Fixed& operator=(const Fixed& b) = default; + + fixed_t value() const { return val_; } + int sign() const { return val_ < 0 ? -1 : 1; } + + operator fixed_t() const { return val_; } + explicit operator float() const { return FixedToFloat(val_); } + + Fixed& operator+=(const Fixed& b) + { + val_ += b.val_; + return *this; + } + + Fixed& operator-=(const Fixed& b) + { + val_ -= b.val_; + return *this; + } + + Fixed& operator*=(const Fixed& b) + { + val_ = FixedMul(val_, b.val_); + return *this; + } + + Fixed& operator/=(const Fixed& b) + { + val_ = FixedDiv(val_, b.val_); + return *this; + } + + Fixed operator-() const { return -val_; } + +#define X(op) \ + template \ + Fixed operator op(const T& b) const { return val_ op b; } \ + Fixed operator op(const Fixed& b) const \ + { \ + Fixed f{val_};\ + f op##= b;\ + return f;\ + } \ + template \ + Fixed& operator op##=(const T& b) \ + { \ + val_ op##= b; \ + return *this; \ + } + + X(+) + X(-) + X(*) + X(/) + +#undef X + +private: + fixed_t val_; +}; + +template <> +struct Traits +{ + static constexpr Fixed kZero = 0; + static constexpr Fixed kUnit = FRACUNIT; + + static constexpr auto copysign = Fixed::copysign; + static constexpr auto hypot = Fixed::hypot; +}; + +}; // namespace srb2::math + +#endif/*math_fixed_hpp*/ diff --git a/src/math/line_equation.hpp b/src/math/line_equation.hpp new file mode 100644 index 000000000..ec4b69d52 --- /dev/null +++ b/src/math/line_equation.hpp @@ -0,0 +1,102 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_line_equation_hpp +#define math_line_equation_hpp + +#include "fixed.hpp" +#include "line_segment.hpp" +#include "vec.hpp" + +namespace srb2::math +{ + +template +struct LineEquation +{ + using vec2 = Vec2; + using line_segment = LineSegment; + + // Fixed-point: shift value by this amount during + // multiplications and divisions to avoid overflows. + static constexpr std::enable_if_t, fixed_t> kF = 1024; // fixed_t, not Fixed + + LineEquation() {} + LineEquation(const vec2& p, const vec2& d) : d_(d), m_(d.y / d.x), b_(p.y - (p.x * m())) {} + LineEquation(const line_segment& l) : LineEquation(l.a, l.b - l.a) {} + + const vec2& d() const { return d_; } + T m() const { return m_; } + T b() const { return b_; } + T y(T x) const { return (m() * x) + b(); } + + vec2 intersect(const LineEquation& q) const + { + T x = (b() - q.b()) / (q.m() - m()); + return {x, y(x)}; + } + +protected: + vec2 d_{}; + T m_{}, b_{}; +}; + +template <> +inline LineEquation::LineEquation(const vec2& p, const vec2& d) : + d_(d), m_((d.y / d.x) / kF), b_((p.y / kF) - (p.x * m_)) +{ +} + +template <> +inline Fixed LineEquation::m() const +{ + return m_ * kF; +} + +template <> +inline Fixed LineEquation::b() const +{ + return b_ * kF; +} + +template <> +inline Fixed LineEquation::y(Fixed x) const +{ + return ((m_ * x) + b_) * kF; +} + +template <> +inline LineEquation::vec2 LineEquation::intersect(const LineEquation& q) const +{ + Fixed x = ((b_ - q.b_) / ((q.m_ - m_) * kF)) * kF; + return {x, y(x)}; +} + +template +struct LineEquationX : LineEquation +{ + T x(T y) const { return (y - LineEquation::b()) / LineEquation::m(); } +}; + +template <> +struct LineEquationX : LineEquation +{ + LineEquationX() {} + LineEquationX(const vec2& p, const vec2& d) : LineEquation(p, d), w_((d.x / d.y) / kF), a_((p.x / kF) - (p.y * w_)) {} + LineEquationX(const line_segment& l) : LineEquationX(l.a, l.b - l.a) {} + + Fixed x(Fixed y) const { return ((w_ * y) + a_) * kF; } + +protected: + Fixed w_{}, a_{}; +}; + +}; // namespace srb2::math + +#endif/*math_line_equation_hpp*/ diff --git a/src/math/line_segment.hpp b/src/math/line_segment.hpp new file mode 100644 index 000000000..0094acf60 --- /dev/null +++ b/src/math/line_segment.hpp @@ -0,0 +1,43 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_line_segment_hpp +#define math_line_segment_hpp + +#include +#include + +#include "vec.hpp" + +namespace srb2::math +{ + +template +struct LineSegment +{ + using vec2 = Vec2; + using view = std::pair; + + vec2 a, b; + + LineSegment(vec2 a_, vec2 b_) : a(a_), b(b_) {} + + template + LineSegment(const LineSegment& b) : LineSegment(b.a, b.b) {} + + bool horizontal() const { return a.y == b.y; } + bool vertical() const { return a.x == b.x; } + + view by_x() const { return std::minmax(a, b, [](auto& a, auto& b) { return a.x < b.x; }); } + view by_y() const { return std::minmax(a, b, [](auto& a, auto& b) { return a.y < b.y; }); } +}; + +}; // namespace srb2 + +#endif/*math_line_segment_hpp*/ diff --git a/src/math/traits.hpp b/src/math/traits.hpp new file mode 100644 index 000000000..fbb6aaa5b --- /dev/null +++ b/src/math/traits.hpp @@ -0,0 +1,34 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_traits_hpp +#define math_traits_hpp + +#include +#include + +namespace srb2::math +{ + +template +struct Traits; + +template +struct Traits>> +{ + static constexpr T kZero = 0.0; + static constexpr T kUnit = 1.0; + + static T copysign(T x, T y) { return std::copysign(x, y); } + static T hypot(T x, T y) { return std::hypot(x, y); } +}; + +}; // namespace srb2::math + +#endif/*math_traits_hpp*/ diff --git a/src/math/vec.hpp b/src/math/vec.hpp new file mode 100644 index 000000000..670a3677c --- /dev/null +++ b/src/math/vec.hpp @@ -0,0 +1,84 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef math_vec_hpp +#define math_vec_hpp + +#include + +#include "traits.hpp" + +namespace srb2::math +{ + +template +struct Vec2 +{ + T x, y; + + Vec2() : x{}, y{} {} + Vec2(T x_, T y_) : x(x_), y(y_) {} + Vec2(T z) : x(z), y(z) {} + + template + Vec2(const Vec2& b) : Vec2(b.x, b.y) {} + + T magnitude() const { return Traits::hypot(x, y); } + Vec2 normal() const { return {-y, x}; } + +#define X(op) \ + Vec2& operator op##=(const Vec2& b) \ + { \ + x op##= b.x; \ + y op##= b.y; \ + return *this; \ + } \ + Vec2 operator op(const Vec2& b) const { return Vec2(x op b.x, y op b.y); } \ + + X(+) + X(-) + X(*) + X(/) + +#undef X + + Vec2 operator-() const { return Vec2(-x, -y); } +}; + +template +struct is_vec2 : std::false_type {}; + +template +struct is_vec2> : std::true_type {}; + +template +inline constexpr bool is_vec2_v = is_vec2::value; + +#define X(op) \ + template , bool> = true> \ + Vec2 operator op(const T& a, const Vec2& b) \ + { \ + return Vec2 {a} op Vec2 {b}; \ + } \ + template , bool> = true> \ + Vec2 operator op(const Vec2& a, const U& b) \ + { \ + return Vec2 {a} op Vec2 {b}; \ + } \ + +X(+) +X(-) +X(*) +X(/) + +#undef X + +}; // namespace srb2::math + +#endif/*math_vec_hpp*/ diff --git a/src/menus/options-profiles-1.c b/src/menus/options-profiles-1.c index ee9f3a40d..bb194a2ee 100644 --- a/src/menus/options-profiles-1.c +++ b/src/menus/options-profiles-1.c @@ -89,6 +89,7 @@ static void M_StartEditProfile(INT32 c) CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername); CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel); CV_StealthSetValue(&cv_dummyprofileautoroulette, optionsmenu.profile->autoroulette); + CV_StealthSetValue(&cv_dummyprofilelitesteer, optionsmenu.profile->litesteer); CV_StealthSetValue(&cv_dummyprofilerumble, optionsmenu.profile->rumble); } else @@ -97,6 +98,7 @@ static void M_StartEditProfile(INT32 c) CV_StealthSet(&cv_dummyprofileplayername, ""); CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off CV_StealthSetValue(&cv_dummyprofileautoroulette, 0); // off + CV_StealthSetValue(&cv_dummyprofilelitesteer, 1); // on CV_StealthSetValue(&cv_dummyprofilerumble, 1); // on } diff --git a/src/menus/options-profiles-edit-controls.c b/src/menus/options-profiles-edit-controls.c index dc648abd6..0b7e8dcf6 100644 --- a/src/menus/options-profiles-edit-controls.c +++ b/src/menus/options-profiles-edit-controls.c @@ -94,6 +94,9 @@ menuitem_t OPTIONS_ProfileControls[] = { {IT_CONTROL | IT_CVAR, "AUTO ROULETTE", "Item roulette auto-stops on a random result.", NULL, {.cvar = &cv_dummyprofileautoroulette}, 0, 0}, + {IT_CONTROL | IT_CVAR, "LITE STEER", "Hold DOWN on d-pad/keyboard for shallow turns.", + NULL, {.cvar = &cv_dummyprofilelitesteer}, 0, 0}, + {IT_HEADER, "EXTRA", "", NULL, {NULL}, 0, 0}, @@ -191,6 +194,7 @@ static void M_ProfileControlSaveResponse(INT32 choice) // Save the profile optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value; + optionsmenu.profile->litesteer = cv_dummyprofilelitesteer.value; optionsmenu.profile->rumble = cv_dummyprofilerumble.value; memcpy(&optionsmenu.profile->controls, optionsmenu.tempcontrols, sizeof(gamecontroldefault)); @@ -201,6 +205,7 @@ static void M_ProfileControlSaveResponse(INT32 choice) memcpy(&gamecontrol[belongsto], optionsmenu.tempcontrols, sizeof(gamecontroldefault)); CV_SetValue(&cv_kickstartaccel[belongsto], cv_dummyprofilekickstart.value); CV_SetValue(&cv_autoroulette[belongsto], cv_dummyprofileautoroulette.value); + CV_SetValue(&cv_litesteer[belongsto], cv_dummyprofilelitesteer.value); CV_SetValue(&cv_rumble[belongsto], cv_dummyprofilerumble.value); } diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 1e035d52d..8abbd34ee 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -39,6 +39,7 @@ target_sources(SRB2SDL2 PRIVATE wpzothers.c shadow.cpp ball-switch.cpp + charge.c ) add_subdirectory(versus) diff --git a/src/objects/audience.c b/src/objects/audience.c index f1c1a0131..b5814ae16 100644 --- a/src/objects/audience.c +++ b/src/objects/audience.c @@ -29,6 +29,7 @@ Obj_AudienceInit mapthing_t *mthing, INT32 followerpick) { + const boolean ourchoiceofvisuals = (followerpick < 0 || followerpick > numfollowers); INT16 *reflist = NULL; INT16 tempreflist[MAXHEADERFOLLOWERS]; UINT8 numref = 0; @@ -36,9 +37,9 @@ Obj_AudienceInit audience_mainstate(mobj) = S_NULL; // Pick follower - if (mthing != NULL) + if (ourchoiceofvisuals == true) { - if (mthing->thing_stringargs[0] != NULL) + if (mthing != NULL && mthing->thing_stringargs[0] != NULL) { // From mapthing char *stringcopy = Z_StrDup(mthing->thing_stringargs[0]); @@ -56,11 +57,23 @@ Obj_AudienceInit *c = ' '; } - if ((tempreflist[numref++] = K_FollowerAvailable(tok)) == -1) + if ((tempreflist[numref] = K_FollowerAvailable(tok)) == -1) + { CONS_Alert(CONS_WARNING, "Mapthing %s: Follower \"%s\" is invalid!\n", sizeu1(mthing-mapthings), tok); + } + else + numref++; + tok = strtok(NULL, " ,"); } + if (!numref) + { + // This is the one thing a user should definitely be told about. + CONS_Alert(CONS_WARNING, "Mapthing %s: Follower audience has no valid followers to pick from!\n", sizeu1(mthing-mapthings)); + // DO NOT RETURN HERE + } + Z_Free(stringcopy); reflist = tempreflist; @@ -81,8 +94,8 @@ Obj_AudienceInit if (!numref || !reflist) { - // This is the one thing a user should definitely be told about. - CONS_Alert(CONS_WARNING, "Mapthing %s: Follower audience has no valid followers to pick from!\n", sizeu1(mthing-mapthings)); + // Clean up after ourselves. + P_RemoveMobj(mobj); return; } @@ -137,11 +150,11 @@ Obj_AudienceInit } // Handle colors - if (mthing != NULL) + if (ourchoiceofvisuals == true) { UINT16 colorpick = SKINCOLOR_NONE; - if (mthing->thing_stringargs[1] != NULL) + if (mthing != NULL && mthing->thing_stringargs[1] != NULL) { if (!stricmp("Random", mthing->thing_stringargs[1])) { @@ -189,8 +202,17 @@ Obj_AudienceInit void Obj_AudienceThink ( mobj_t * mobj, - boolean focusonplayer) + boolean focusonplayer, + boolean checkdeathpit) { + boolean landed = false; + + if (mobj->fuse && mobj->fuse < (TICRATE/2)) + { + mobj->renderflags ^= RF_DONTDRAW; + return; // no jumping when you hit the floor, your gravity is weird + } + if (audience_mainstate(mobj) == S_NULL) { // Uninitialised, don't do anything funny. @@ -315,15 +337,23 @@ Obj_AudienceThink } else if (mobj->flags2 & MF2_OBJECTFLIP) { - if (mobj->z + mobj->height >= mobj->ceilingz) - { - mobj->momz = -audience_bobamp(mobj); - P_SetMobjState(mobj, audience_mainstate(mobj)); - } + landed = (mobj->z + mobj->height >= mobj->ceilingz); } - else if (mobj->z <= mobj->floorz) + else { - mobj->momz = audience_bobamp(mobj); + landed = (mobj->z <= mobj->floorz); + } + + if (landed == true) + { + if (checkdeathpit && P_CheckDeathPitCollide(mobj)) + { + P_RemoveMobj(mobj); + return; + } + + mobj->momx = mobj->momy = 0; + mobj->momz = P_MobjFlip(mobj)*audience_bobamp(mobj); P_SetMobjState(mobj, audience_mainstate(mobj)); } } diff --git a/src/objects/charge.c b/src/objects/charge.c new file mode 100644 index 000000000..7623b6ece --- /dev/null +++ b/src/objects/charge.c @@ -0,0 +1,147 @@ +#include "../doomdef.h" +#include "../info.h" +#include "../k_objects.h" +#include "../p_local.h" +#include "../k_kart.h" +#include "../k_powerup.h" +#include "../m_random.h" + +#define CHARGEAURA_BURSTTIME (9) +#define CHARGEAURA_SPARKRADIUS (40) + +// xval1: destruction timer +// xval2: master (spawns other visuals) +// cvmem: spawn time (used to offset flash) +void Obj_ChargeAuraThink (mobj_t *aura) +{ + if (P_MobjWasRemoved(aura->target) || !aura->target->player || (aura->extravalue1 >= CHARGEAURA_BURSTTIME)) + { + P_RemoveMobj(aura); + } + else + { + mobj_t *mo = aura->target; + player_t *player = mo->player; + + // Follow player + aura->flags &= ~(MF_NOCLIPTHING); + P_MoveOrigin(aura, mo->x, mo->y, mo->z + mo->height/2); + aura->flags |= MF_NOCLIPTHING; + aura->color = mo->color; + + aura->renderflags &= ~RF_DONTDRAW; + + fixed_t baseScale = 12*mo->scale/10; + + if (aura->extravalue1 || !player->trickcharge) + { + aura->extravalue1++; + baseScale += (mo->scale / 3) * aura->extravalue1; + aura->renderflags &= ~RF_TRANSMASK; + aura->renderflags |= (aura->extravalue1)<extravalue1 % 2) + aura->renderflags |= RF_DONTDRAW; + } + + P_SetScale(aura, baseScale); + + // Twirl + aura->angle = aura->angle - ANG1*(player->trickcharge/TICRATE + 4); + // Visuals + aura->renderflags |= RF_PAPERSPRITE|RF_ADD; + + // fuck + boolean forceinvisible = !!!((leveltime - aura->cvmem) % 4); + if (aura->extravalue1 || !(player->driftcharge > K_GetKartDriftSparkValueForStage(player, 3))) + forceinvisible = false; + + if (forceinvisible) + aura->renderflags |= RF_DONTDRAW; + + if (aura->extravalue2) + { + if (player->driftcharge) + { + mobj_t *spark = P_SpawnMobjFromMobj(aura, + FRACUNIT*P_RandomRange(PR_DECORATION, -1*CHARGEAURA_SPARKRADIUS, CHARGEAURA_SPARKRADIUS), + FRACUNIT*P_RandomRange(PR_DECORATION, -1*CHARGEAURA_SPARKRADIUS, CHARGEAURA_SPARKRADIUS), + FRACUNIT*P_RandomRange(PR_DECORATION, -1*CHARGEAURA_SPARKRADIUS, CHARGEAURA_SPARKRADIUS), + MT_CHARGESPARK); + spark->frame = P_RandomRange(PR_DECORATION, 1, 5); + spark->renderflags |= RF_FULLBRIGHT|RF_ADD; + P_SetTarget(&spark->target, aura); + P_SetScale(spark, 15*aura->scale/10); + } + + if (forceinvisible) + { + mobj_t *flicker = P_SpawnMobjFromMobj(aura, 0, 0, 0, MT_CHARGEFLICKER); + P_SetTarget(&flicker->target, aura); + P_SetScale(flicker, aura->scale); + } + } + } +} + +void Obj_ChargeFallThink (mobj_t *charge) +{ + if (P_MobjWasRemoved(charge->target) || !charge->target->player) + { + P_RemoveMobj(charge); + } + else + { + mobj_t *mo = charge->target; + + // Follow player + charge->flags &= ~(MF_NOCLIPTHING); + P_MoveOrigin(charge, mo->x, mo->y, mo->z); + charge->flags |= MF_NOCLIPTHING; + charge->color = mo->color; + charge->angle = mo->angle + ANGLE_45 + (ANGLE_90 * charge->extravalue1); + + if (!P_IsObjectOnGround(mo)) + charge->renderflags |= RF_DONTDRAW; + else + charge->renderflags &= ~RF_DONTDRAW; + + fixed_t baseScale = 12*mo->scale/10; + P_SetScale(charge, baseScale); + + charge->renderflags &= ~RF_TRANSMASK; + if (charge->tics < 10) + charge->renderflags |= (9 - charge->tics)<renderflags |= RF_PAPERSPRITE|RF_ADD; + } +} + +// xval1: lifetime (used to offset from tracked player) +void Obj_ChargeReleaseThink (mobj_t *release) +{ + release->renderflags &= ~RF_TRANSMASK; + if (release->tics < 36) + release->renderflags |= (9 - release->tics/4)<rollangle += ANG15/2; + + if (P_MobjWasRemoved(release->target) || !release->target->player) + return; + + release->extravalue1++; + + fixed_t off = 8 * release->extravalue1 * release->target->scale; + angle_t ang = K_MomentumAngle(release->target) + ANGLE_180; + fixed_t xoff = FixedMul(off, FINECOSINE(ang >> ANGLETOFINESHIFT)); + fixed_t yoff = FixedMul(off, FINESINE(ang >> ANGLETOFINESHIFT)); + + P_MoveOrigin(release, release->target->x + xoff, release->target->y + yoff, release->target->z + release->target->height/2); +} + +void Obj_ChargeExtraThink (mobj_t *extra) +{ + extra->renderflags &= ~RF_TRANSMASK; + if (extra->tics < 18) + extra->renderflags |= (9 - extra->tics/2)<rollangle += ANG30; +} \ No newline at end of file diff --git a/src/objects/dash-rings.c b/src/objects/dash-rings.c index d40e341bb..ba9fed39e 100644 --- a/src/objects/dash-rings.c +++ b/src/objects/dash-rings.c @@ -66,7 +66,7 @@ void Obj_RainbowDashRingSpawn(mobj_t *mobj) void Obj_DashRingSetup(mobj_t *mobj, mapthing_t *mthing) { - static const UINT8 numColors = sizeof(rainbow_colors) / sizeof(skincolornum_t); + static const UINT8 numColors = sizeof(ring_colors) / sizeof(skincolornum_t); const UINT8 additionalThrust = mthing->thing_args[1]; statenum_t ringState, overlayState; @@ -199,8 +199,6 @@ static void RegularDashRingLaunch(player_t *player, mobj_t *ring) static void RainbowDashRingLaunch(player_t *player, mobj_t *ring) { player->mo->eflags &= ~MFE_SPRUNG; - player->trickpanel = 1; - player->pflags |= PF_TRICKDELAY; K_DoPogoSpring(player->mo, 0, 0); DashRingLaunch(player, ring); } diff --git a/src/objects/loops.cpp b/src/objects/loops.cpp index c54103f9d..930fc6cd3 100644 --- a/src/objects/loops.cpp +++ b/src/objects/loops.cpp @@ -295,6 +295,7 @@ Obj_LoopEndpointCollide { player_t *player = toucher->player; sonicloopvars_t *s = &player->loop; + sonicloopcamvars_t *cam = &s->camera; mobj_t *anchor = end_anchor(end); mobj_t *center = anchor ? anchor_center(anchor) : NULL; @@ -352,6 +353,30 @@ Obj_LoopEndpointCollide s->flip = center_has_flip(center); + cam->enter_tic = leveltime; + cam->exit_tic = INFTICS; + + if (center->thing_args[4]) // is camera distance set? + { + cam->zoom_out_speed = center->thing_args[2]; + cam->zoom_in_speed = center->thing_args[3]; + cam->dist = center->thing_args[4] * FRACUNIT; + cam->pan = FixedAngle(center->thing_args[5] * FRACUNIT); + cam->pan_speed = center->thing_args[6] * FRACUNIT; + cam->pan_accel = center->thing_args[7]; + cam->pan_back = center->thing_args[8]; + } + else + { + cam->zoom_out_speed = 20; + cam->zoom_in_speed = 60; + cam->dist = radius; + cam->pan = ANGLE_22h; + cam->pan_speed = 6*FRACUNIT; + cam->pan_accel = 10; + cam->pan_back = 40; + } + player->speed = 3 * (player->speed + toucher->momz) / 2; diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 8df3fb937..fcdbe6b4b 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -121,10 +121,14 @@ void Obj_RandomItemVisuals(mobj_t *mobj) // // Then extraval1 starts ticking up and triggers the transformation from Ringbox to Random Item. if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING) && !(mobj->flags2 & MF2_BOSSDEAD) && !cv_thunderdome.value - && (modeattacking == 0 || specialstageinfo.valid)) // Time Attacking in Special is a fucked-looking exception + && (modeattacking == ATTACKING_NONE || !!(modeattacking & ATTACKING_SPB) || specialstageinfo.valid)) // Time Attacking in Special is a fucked-looking exception { mobj->extravalue1++; + // Dumb, but in Attack starts (or POSITION from hell) you can reach early boxes before they transform. + if (leveltime == 0) + mobj->extravalue1 = RINGBOX_TIME; + // Don't transform stuff that isn't a Ring Box, idiot statenum_t boxstate = mobj->state - states; if (boxstate < S_RINGBOX1 || boxstate > S_RINGBOX12) diff --git a/src/objects/super-flicky.cpp b/src/objects/super-flicky.cpp index d78de32fd..344cbd46c 100644 --- a/src/objects/super-flicky.cpp +++ b/src/objects/super-flicky.cpp @@ -765,14 +765,14 @@ mobj_t *Obj_SuperFlickyOwner(const mobj_t* mobj) { const Flicky* x = static_cast(mobj); - return x->source(); + return x->valid() ? x->source() : nullptr; } -boolean Obj_IsSuperFlickyWhippable(const mobj_t* mobj) +boolean Obj_IsSuperFlickyWhippable(const mobj_t* mobj, const mobj_t* target) { const Flicky* x = static_cast(mobj); - return mobj == x->chasing() && !x->stunned(); + return target == x->chasing() && !x->stunned(); } boolean Obj_IsSuperFlickyTargettingYou(const mobj_t* mobj, mobj_t *player) @@ -780,4 +780,4 @@ boolean Obj_IsSuperFlickyTargettingYou(const mobj_t* mobj, mobj_t *player) const Flicky* x = static_cast(mobj); return player == x->chasing(); -} \ No newline at end of file +} diff --git a/src/p_floor.c b/src/p_floor.c index 4b96192c8..1a7f2285a 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -1017,7 +1017,6 @@ static mobj_t *SearchMarioNode(msecnode_t *node) } // Ignore popped monitors, too. if (node->m_thing->health == 0 // this only really applies for monitors - || (!(node->m_thing->flags & MF_MONITOR) && (mobjinfo[node->m_thing->type].flags & MF_MONITOR)) // gold monitor support || (node->m_thing->type == MT_RANDOMITEM)) continue; // Okay, we found something valid. @@ -2344,7 +2343,6 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) fixed_t topheight = *rover->topheight; mariothink_t *block; mobj_t *thing; - fixed_t oldx = 0, oldy = 0, oldz = 0; I_Assert(puncher != NULL); I_Assert(puncher->player != NULL); @@ -2362,7 +2360,6 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) S_StartSound(puncher, sfx_mario1); // "Thunk!" sound - puncher is "close enough". else // Found something! { - const boolean itsamonitor = (thing->flags & MF_MONITOR) == MF_MONITOR; // create and initialize new elevator thinker block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL); @@ -2383,13 +2380,6 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) R_CreateInterpolator_SectorPlane(&block->thinker, roversec, false); R_CreateInterpolator_SectorPlane(&block->thinker, roversec, true); - if (itsamonitor) - { - oldx = thing->x; - oldy = thing->y; - oldz = thing->z; - } - P_UnsetThingPosition(thing); thing->x = thing->old_x = sector->soundorg.x; thing->y = thing->old_y = sector->soundorg.y; @@ -2410,16 +2400,5 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) // "Powerup rise" sound S_StartSound(puncher, sfx_mario9); // Puncher is "close enough" } - - if (itsamonitor && thing) - { - P_UnsetThingPosition(thing); - thing->x = thing->old_x = oldx; - thing->y = thing->old_y = oldy; - thing->z = thing->old_z = oldz; - thing->momx = 1; - thing->momy = 1; - P_SetThingPosition(thing); - } } } diff --git a/src/p_inter.c b/src/p_inter.c index e5feda38a..43b5f2ab8 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -43,6 +43,7 @@ #include "k_hitlag.h" #include "acs/interface.h" #include "k_powerup.h" +#include "k_collide.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -153,7 +154,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) if (player->itemRoulette.active == true || player->ringboxdelay > 0 || (weapon != 3 && player->itemamount) - || (player->pflags & PF_ITEMOUT)) + || (player->itemflags & IF_ITEMOUT)) return false; if (weapon == 3 && K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE) @@ -1537,7 +1538,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->momx = target->momy = target->momz = 0; // SRB2kart - if (target->type != MT_PLAYER && !(target->flags & MF_MONITOR) + if (target->type != MT_PLAYER && !(target->type == MT_ORBINAUT || target->type == MT_ORBINAUT_SHIELD || target->type == MT_JAWZ || target->type == MT_JAWZ_SHIELD || target->type == MT_BANANA || target->type == MT_BANANA_SHIELD @@ -1577,10 +1578,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget // I wish I knew a better way to do this if (target->target && target->target->player && target->target->player->mo) { - if ((target->target->player->pflags & PF_EGGMANOUT) && target->type == MT_EGGMANITEM_SHIELD) - target->target->player->pflags &= ~PF_EGGMANOUT; + if ((target->target->player->itemflags & IF_EGGMANOUT) && target->type == MT_EGGMANITEM_SHIELD) + target->target->player->itemflags &= ~IF_EGGMANOUT; - if (target->target->player->pflags & PF_ITEMOUT) + if (target->target->player->itemflags & IF_ITEMOUT) { if ((target->type == MT_BANANA_SHIELD && target->target->player->itemtype == KITEM_BANANA) // trail items || (target->type == MT_SSMINE_SHIELD && target->target->player->itemtype == KITEM_MINE) @@ -1608,7 +1609,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget } if (!target->target->player->itemamount) - target->target->player->pflags &= ~PF_ITEMOUT; + target->target->player->itemflags &= ~IF_ITEMOUT; if (target->target->hnext == target) P_SetTarget(&target->target->hnext, NULL); @@ -1630,7 +1631,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget // if killed by a player if (source && source->player) { - if (target->flags & MF_MONITOR || target->type == MT_RANDOMITEM) + if (target->type == MT_RANDOMITEM) { P_SetTarget(&target->target, source); @@ -1692,7 +1693,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->player->roundscore = 0; } - target->player->trickpanel = 0; + target->player->trickpanel = TRICKSTATE_NONE; ACS_RunPlayerDeathScript(target->player); } @@ -2103,6 +2104,67 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget cur = cur->hnext; } + // Spawn three Followers (if possible) + if (mapheaderinfo[gamemap-1]->numFollowers) + { + dir = FixedAngle(P_RandomKey(PR_RANDOMAUDIENCE, 360)*FRACUNIT); + + const fixed_t launchmomentum = 7 * mapobjectscale; + const fixed_t jaggedness = 4; + angle_t launchangle; + UINT8 i; + for (i = 0; i < 6; i++, dir += ANG60) + { + cur = P_SpawnMobj( + target->x, target->y, + target->z + target->height/2, + MT_RANDOMAUDIENCE + ); + + // We check if you have some horrible Lua + if (P_MobjWasRemoved(cur)) + break; + + Obj_AudienceInit(cur, NULL, -1); + + // We check again if the list is invalid + if (P_MobjWasRemoved(cur)) + break; + + cur->hitlag = target->hitlag; + + cur->destscale /= 2; + P_SetScale(cur, cur->destscale/TICRATE); + cur->scalespeed = cur->destscale/TICRATE; + cur->z -= cur->height/2; + + // flags are NOT from the target - just in case it's just been placed on the ceiling as a gimmick + cur->flags2 |= (source->flags2 & MF2_OBJECTFLIP); + cur->eflags |= (source->eflags & MFE_VERTICALFLIP); + + launchangle = FixedAngle( + ( + ( + P_RandomRange(PR_RANDOMAUDIENCE, 12/jaggedness, 24/jaggedness) * jaggedness + ) + (i & 1)*16 + ) * FRACUNIT + ); + + cur->momz = P_MobjFlip(target) // THIS one uses target! + * P_ReturnThrustY(cur, launchangle, launchmomentum); + + cur->angle = dir; + + P_InstaThrust( + cur, cur->angle, + P_ReturnThrustX(cur, launchangle, launchmomentum) + ); + + cur->fuse = (3*TICRATE)/2; + cur->flags |= MF_NOCLIPHEIGHT; + } + } + S_StartSound(target, sfx_mbs60); P_AddBrokenPrison(target, inflictor, source); @@ -2550,10 +2612,18 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, P_SetPlayerMobjState(player->mo, player->mo->info->deathstate); - if (player->sliptideZipIndicator && !P_MobjWasRemoved(player->sliptideZipIndicator)) - P_RemoveMobj(player->sliptideZipIndicator); - if (player->stumbleIndicator && !P_MobjWasRemoved(player->stumbleIndicator)) - P_RemoveMobj(player->stumbleIndicator); +#define PlayerPointerRemove(field) \ + if (P_MobjWasRemoved(field) == false) \ + { \ + P_RemoveMobj(field); \ + P_SetTarget(&field, NULL); \ + } + + PlayerPointerRemove(player->stumbleIndicator); + PlayerPointerRemove(player->wavedashIndicator); + PlayerPointerRemove(player->trickIndicator); + +#undef PlayerPointerRemove if (type == DMG_TIMEOVER) { @@ -2868,6 +2938,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da invincible = false; } + // TODO: doing this from P_DamageMobj limits punting to objects that damage the player. + // And it may be kind of yucky. + // But this is easier than accounting for every condition in PIT_CheckThing! + if (inflictor && K_PuntCollide(inflictor, target)) + { + return false; + } + if (invincible && type != DMG_STUMBLE && type != DMG_WHUMBLE) { const INT32 oldHitlag = target->hitlag; @@ -3103,6 +3181,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da break; } + // Have a shield? You get hit, but don't lose your rings! + if (player->curshield != KSHIELD_NONE) + { + ringburst = 0; + } + if (type != DMG_STUMBLE && type != DMG_WHUMBLE) { if (type != DMG_STING) @@ -3290,7 +3374,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) return; // Have a shield? You get hit, but don't lose your rings! - if (K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE) + if (player->curshield != KSHIELD_NONE) return; // 20 is the maximum number of rings that can be taken from you at once - half the span of your counter diff --git a/src/p_local.h b/src/p_local.h index fccc0287f..4cd26c9fd 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -77,6 +77,7 @@ typedef enum NUM_THINKERLISTS } thinklistnum_t; /**< Thinker lists. */ extern thinker_t thlist[]; +extern mobj_t *mobjcache; void P_InitThinkers(void); void P_AddThinker(const thinklistnum_t n, thinker_t *thinker); @@ -386,9 +387,16 @@ struct tm_t // so missiles don't explode against sky hack walls line_t *ceilingline; - // set by PIT_CheckLine() for any line that stopped the PIT_CheckLine() - // that is, for any line which is 'solid' - line_t *blockingline; + // P_CheckPosition: this position blocks movement + boolean blocking; + + // P_CheckPosition: set this before each call to + // P_CheckPosition to enable a line sweep on collided + // lines + boolean sweep; + + // sweep: max step up at tm.x, tm.y + fixed_t maxstep; }; extern tm_t tm; @@ -414,6 +422,7 @@ struct TryMoveResult_t boolean success; line_t *line; mobj_t *mo; + vector2_t normal; }; boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *result); @@ -421,6 +430,10 @@ boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, T boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, TryMoveResult_t *result); boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *result); +void P_TestLine(line_t *ld); +void P_ClearTestLines(void); +line_t *P_SweepTestLines(fixed_t ax, fixed_t ay, fixed_t bx, fixed_t by, fixed_t r, vector2_t *return_normal); + boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing); boolean P_IsLineTripWire(const line_t *ld); boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); @@ -582,6 +595,8 @@ mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator); void P_DeleteMobjStringArgs(mobj_t *mobj); +tic_t P_MobjIsReappearing(const mobj_t *mobj); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_loop.c b/src/p_loop.c index bc1454a5d..67ba60f02 100644 --- a/src/p_loop.c +++ b/src/p_loop.c @@ -37,6 +37,7 @@ void P_HaltPlayerOrbit(player_t *player) player->mo->flags &= ~(MF_NOCLIPHEIGHT); player->loop.radius = 0; + player->loop.camera.exit_tic = leveltime; } void P_ExitPlayerOrbit(player_t *player) diff --git a/src/p_map.c b/src/p_map.c index 79b9ba59e..7f1b3bf84 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -427,12 +427,8 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) { if (spring->reactiontime == 0) { - object->player->tricktime = 0; // Reset post-hitlag timer - // Setup the boost for potential upwards trick, at worse, make it your regular max speed. (boost = curr speed*1.25) - object->player->trickboostpower = max(FixedDiv(object->player->speed, K_GetKartSpeed(object->player, false, false)) - FRACUNIT, 0)*125/100; - //CONS_Printf("Got boost: %d%\n", mo->player->trickboostpower*100 / FRACUNIT); - object->player->trickpanel = 1; - object->player->pflags |= PF_TRICKDELAY; + object->eflags &= ~MFE_SPRUNG; // needed to permit the following + K_DoPogoSpring(object, -vertispeed, 0); // negative so momz isn't modified } else { @@ -512,12 +508,6 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) if (spring->thing_args[1]) { - if (object->player) - { - object->player->trickpanel = 1; - object->player->pflags |= PF_TRICKDELAY; - } - K_DoPogoSpring(object, 32<flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) return BMIT_CONTINUE; + // Thing is respawning + if (P_MobjIsReappearing(thing)) + return BMIT_CONTINUE; + blockdist = thing->radius + tm.thing->radius; if (abs(thing->x - tm.x) >= blockdist || abs(thing->y - tm.y) >= blockdist) @@ -664,23 +659,6 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) && (tm.thing->type == MT_BLENDEYE_MAIN || tm.thing->type == MT_BLENDEYE_EYE || tm.thing->type == MT_BLENDEYE_PUYO)) return BMIT_CONTINUE; - // When solid spikes move, assume they just popped up and teleport things on top of them to hurt. - if (tm.thing->type == MT_SPIKE && tm.thing->flags & MF_SOLID) - { - if (thing->z > tm.thing->z + tm.thing->height) - return BMIT_CONTINUE; // overhead - if (thing->z + thing->height < tm.thing->z) - return BMIT_CONTINUE; // underneath - - if (tm.thing->eflags & MFE_VERTICALFLIP) - P_SetOrigin(thing, thing->x, thing->y, tm.thing->z - thing->height - FixedMul(FRACUNIT, tm.thing->scale)); - else - P_SetOrigin(thing, thing->x, thing->y, tm.thing->z + tm.thing->height + FixedMul(FRACUNIT, tm.thing->scale)); - if (thing->flags & MF_SHOOTABLE) - P_DamageMobj(thing, tm.thing, tm.thing, 1, 0); - return BMIT_CONTINUE; - } - if (thing->flags & MF_PAIN) { // Player touches painful thing sitting on the floor // see if it went over / under @@ -694,10 +672,12 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) if (P_DamageMobj(tm.thing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8))) S_StartSound(thing, damagetype); - } - if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing)) - return BMIT_CONTINUE; + if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing)) + return BMIT_CONTINUE; + + damage = true; + } } else if (tm.thing->flags & MF_PAIN && thing->player) { // Painful thing splats player in the face @@ -712,10 +692,12 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) if (P_DamageMobj(thing, tm.thing, tm.thing, 1, damagetype) && (damagetype = (tm.thing->info->mass>>8))) S_StartSound(tm.thing, damagetype); - } - if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing)) - return BMIT_CONTINUE; + if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing)) + return BMIT_CONTINUE; + + damage = true; + } } // check for skulls slamming into things @@ -1216,7 +1198,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) } // missiles can hit other things - if (tm.thing->flags & MF_MISSILE) + if ((tm.thing->flags & MF_MISSILE) && !damage) // if something was already damaged, don't run this { UINT8 damagetype = (tm.thing->info->mass ^ DMG_WOMBO); @@ -1327,78 +1309,65 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) } // Sprite Spikes! - // Do not return because solidity code comes below. - if (tm.thing->type == MT_SPIKE && tm.thing->flags & MF_SOLID && thing->player) // moving spike rams into player?! + if ((tm.thing->type == MT_SPIKE || tm.thing->type == MT_WALLSPIKE) && (tm.thing->flags & MF_SOLID)) // spike pops up { - if (tm.thing->eflags & MFE_VERTICALFLIP) + // see if it went over / under + if (tm.thing->z > thing->z + thing->height) + return BMIT_CONTINUE; // overhead + if (tm.thing->z + tm.thing->height < thing->z) + return BMIT_CONTINUE; // underneath + + if (thing->flags & MF_SHOOTABLE) { - if (thing->z + thing->height <= tm.thing->z + FixedMul(FRACUNIT, tm.thing->scale) - && thing->z + thing->height + thing->momz >= tm.thing->z + FixedMul(FRACUNIT, tm.thing->scale) + tm.thing->momz) - P_DamageMobj(thing, tm.thing, tm.thing, 1, DMG_TUMBLE); + if (P_MobjFlip(thing) == P_MobjFlip(tm.thing)) + { + if (P_DamageMobj(thing, tm.thing, tm.thing, 1, DMG_TUMBLE)) + { + // FIXME: None of this is correct for wall spikes, + // but I don't feel like testing that right now. + + // Increase vertical momentum for a strong effect + thing->momz += (tm.thing->height / 2) * P_MobjFlip(tm.thing); + + // Teleport on top of the spikes + P_MoveOrigin( + thing, + thing->x, + thing->y, + tm.thing->z + (P_MobjFlip(thing) > 0 ? tm.thing->height : -thing->height) + ); + } + } + else + { + P_DamageMobj(thing, tm.thing, tm.thing, 1, DMG_NORMAL); + } } - else if (thing->z >= tm.thing->z + tm.thing->height - FixedMul(FRACUNIT, tm.thing->scale) - && thing->z + thing->momz <= tm.thing->z + tm.thing->height - FixedMul(FRACUNIT, tm.thing->scale) + tm.thing->momz) - P_DamageMobj(thing, tm.thing, tm.thing, 1, DMG_TUMBLE); + return BMIT_CONTINUE; } - else if (thing->type == MT_SPIKE && thing->flags & MF_SOLID && tm.thing->player) // unfortunate player falls into spike?! + else if ((thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE) && + (thing->flags & MF_SOLID) && (tm.thing->flags & MF_SHOOTABLE)) // stationary spike { - if (thing->eflags & MFE_VERTICALFLIP) + // see if it went over / under + if (tm.thing->z > thing->z + thing->height) + return BMIT_CONTINUE; // overhead + if (tm.thing->z + tm.thing->height < thing->z) + return BMIT_CONTINUE; // underneath + + if (tm.thing->player && tm.thing->player && tm.thing->player->tumbleBounces > 0) { - if (tm.thing->z + tm.thing->height <= thing->z - FixedMul(FRACUNIT, thing->scale) - && tm.thing->z + tm.thing->height + tm.thing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale)) - P_DamageMobj(tm.thing, thing, thing, 1, DMG_TUMBLE); + return BMIT_CONTINUE; } - else if (tm.thing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) - && tm.thing->z + tm.thing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)) + + if (!P_IsObjectOnGround(tm.thing) && tm.thing->momz * P_MobjFlip(tm.thing) < 0) // fell into it + { P_DamageMobj(tm.thing, thing, thing, 1, DMG_TUMBLE); - } - - if (tm.thing->type == MT_WALLSPIKE && tm.thing->flags & MF_SOLID && thing->player) // wall spike impales player - { - fixed_t bottomz, topz; - bottomz = tm.thing->z; - topz = tm.thing->z + tm.thing->height; - if (tm.thing->eflags & MFE_VERTICALFLIP) - bottomz -= FixedMul(FRACUNIT, tm.thing->scale); - else - topz += FixedMul(FRACUNIT, tm.thing->scale); - - if (thing->z + thing->height > bottomz // above bottom - && thing->z < topz) // below top - // don't check angle, the player was clearly in the way in this case - P_DamageMobj(thing, tm.thing, tm.thing, 1, DMG_NORMAL); - } - else if (thing->type == MT_WALLSPIKE && thing->flags & MF_SOLID && tm.thing->player) - { - fixed_t bottomz, topz; - angle_t touchangle = R_PointToAngle2(thing->tracer->x, thing->tracer->y, tm.thing->x, tm.thing->y); - - if (P_PlayerInPain(tm.thing->player) && (tm.thing->momx || tm.thing->momy)) - { - angle_t playerangle = R_PointToAngle2(0, 0, tm.thing->momx, tm.thing->momy) - touchangle; - if (playerangle > ANGLE_180) - playerangle = InvAngle(playerangle); - if (playerangle < ANGLE_90) - return BMIT_CONTINUE; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them. + return BMIT_CONTINUE; } - - bottomz = thing->z; - topz = thing->z + thing->height; - - if (thing->eflags & MFE_VERTICALFLIP) - bottomz -= FixedMul(FRACUNIT, thing->scale); else - topz += FixedMul(FRACUNIT, thing->scale); - - if (tm.thing->z + tm.thing->height > bottomz // above bottom - && tm.thing->z < topz // below top - && !P_MobjWasRemoved(thing->tracer)) // this probably wouldn't work if we didn't have a tracer - { // use base as a reference point to determine what angle you touched the spike at - touchangle = thing->angle - touchangle; - if (touchangle > ANGLE_180) - touchangle = InvAngle(touchangle); - if (touchangle <= ANGLE_22h) // if you touched it at this close an angle, you get poked! - P_DamageMobj(tm.thing, thing, thing, 1, DMG_NORMAL); + { + // Do not return because solidity code comes below. + P_DamageMobj(tm.thing, thing, thing, 1, DMG_NORMAL); } } @@ -1626,7 +1595,10 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) if (tm.thing->z + tm.thing->height < thing->z) return BMIT_CONTINUE; // underneath - K_KartSolidBounce(tm.thing, thing); + if (!K_PuntCollide(thing, tm.thing)) + { + K_KartSolidBounce(tm.thing, thing); + } return BMIT_CONTINUE; } } @@ -1770,7 +1742,6 @@ static BlockItReturn_t PIT_CheckCameraLine(line_t *ld) // could be crossed in either order. // this line is out of the if so upper and lower textures can be hit by a splat - tm.blockingline = ld; if (!ld->backsector) // one sided line { if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, ld)) @@ -1841,6 +1812,22 @@ boolean P_IsLineTripWire(const line_t *ld) return ld->tripwire; } +static boolean P_UsingStepUp(mobj_t *thing) +{ + if (thing->flags & MF_NOCLIP) + { + return false; + } + + // orbits have no collision + if (thing->player && thing->player->loop.radius) + { + return false; + } + + return true; +} + // // PIT_CheckLine // Adjusts tm.floorz and tm.ceilingz as lines are contacted @@ -1898,14 +1885,20 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld) // could be crossed in either order. // this line is out of the if so upper and lower textures can be hit by a splat - tm.blockingline = ld; { - UINT8 shouldCollide = LUA_HookMobjLineCollide(tm.thing, tm.blockingline); // checks hook for thing's type + UINT8 shouldCollide = LUA_HookMobjLineCollide(tm.thing, ld); // checks hook for thing's type if (P_MobjWasRemoved(tm.thing)) return BMIT_CONTINUE; // one of them was removed??? if (shouldCollide == 1) - return BMIT_ABORT; // force collide + { + if (tm.sweep) + { + P_TestLine(ld); + } + tm.blocking = true; // force collide + return BMIT_CONTINUE; + } else if (shouldCollide == 2) return BMIT_CONTINUE; // force no collide } @@ -1914,15 +1907,55 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld) { if (P_PointOnLineSide(tm.thing->x, tm.thing->y, ld)) return BMIT_CONTINUE; // don't hit the back side - return BMIT_ABORT; + + if (tm.sweep) + { + P_TestLine(ld); + } + tm.blocking = true; + return BMIT_CONTINUE; } if (P_IsLineBlocking(ld, tm.thing)) - return BMIT_ABORT; + { + if (tm.sweep) + { + P_TestLine(ld); + } + tm.blocking = true; + return BMIT_CONTINUE; + } // set openrange, opentop, openbottom P_LineOpening(ld, tm.thing, &open); + if (tm.sweep && P_UsingStepUp(tm.thing)) + { + // copied from P_TryMove + // TODO: refactor this into one place + if (open.range < tm.thing->height) + { + P_TestLine(ld); + } + else if (tm.maxstep > 0) + { + if (tm.thing->z < open.floor) + { + if (open.floorstep > tm.maxstep) + { + P_TestLine(ld); + } + } + else if (open.ceiling < tm.thing->z + tm.thing->height) + { + if (open.ceilingstep > tm.maxstep) + { + P_TestLine(ld); + } + } + } + } + // adjust floor / ceiling heights if (open.ceiling < tm.ceilingz) { @@ -2042,7 +2075,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re tm.bbox[BOXLEFT] = x - tm.thing->radius; newsubsec = R_PointInSubsector(x, y); - tm.ceilingline = tm.blockingline = NULL; + tm.ceilingline = NULL; + tm.blocking = false; // The base floor / ceiling is from the subsector // that contains the point. @@ -2279,7 +2313,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re // Check things first, possibly picking things up. // MF_NOCLIPTHING: used by camera to not be blocked by things - if (!(thing->flags & MF_NOCLIPTHING)) + // Respawning things should also be intangible to other things + if (!(thing->flags & MF_NOCLIPTHING) && !P_MobjIsReappearing(thing)) { for (bx = xl; bx <= xh; bx++) { @@ -2314,23 +2349,33 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re validcount++; + P_ClearTestLines(); + // check lines for (bx = xl; bx <= xh; bx++) { for (by = yl; by <= yh; by++) { - if (!P_BlockLinesIterator(bx, by, PIT_CheckLine)) - { - blockval = false; - } + P_BlockLinesIterator(bx, by, PIT_CheckLine); } } + if (tm.blocking) + { + blockval = false; + } + if (result != NULL) { - result->line = tm.blockingline; + result->line = NULL; result->mo = tm.hitthing; } + else + { + P_ClearTestLines(); + } + + tm.sweep = false; return blockval; } @@ -2379,7 +2424,7 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam) tm.bbox[BOXLEFT] = x - thiscam->radius; newsubsec = R_PointInSubsector(x, y); - tm.ceilingline = tm.blockingline = NULL; + tm.ceilingline = NULL; mapcampointer = thiscam; @@ -2753,22 +2798,6 @@ fixed_t P_GetThingStepUp(mobj_t *thing, fixed_t destX, fixed_t destY) return maxstep; } -static boolean P_UsingStepUp(mobj_t *thing) -{ - if (thing->flags & MF_NOCLIP) - { - return false; - } - - // orbits have no collision - if (thing->player && thing->player->loop.radius) - { - return false; - } - - return true; -} - static boolean increment_move ( mobj_t * thing, @@ -2821,7 +2850,29 @@ increment_move tryy = y; } - if (!P_CheckPosition(thing, tryx, tryy, result)) + if (P_UsingStepUp(thing)) + { + tm.maxstep = P_GetThingStepUp(thing, tryx, tryy); + } + + if (result) + { + tm.sweep = true; + } + + boolean move_ok = P_CheckPosition(thing, tryx, tryy, result); + + if (P_MobjWasRemoved(thing)) + { + return false; + } + + if (result) + { + result->line = P_SweepTestLines(thing->x, thing->y, x, y, thing->radius, &result->normal); + } + + if (!move_ok) { return false; // solid wall or thing } @@ -3466,30 +3517,27 @@ static void P_HitSlideLine(line_t *ld) // // HitBounceLine, for players // -static void P_PlayerHitBounceLine(line_t *ld) +static void P_PlayerHitBounceLine(line_t *ld, vector2_t* normal) { - INT32 side; - angle_t lineangle; fixed_t movelen; fixed_t x, y; - side = P_PointOnLineSide(slidemo->x, slidemo->y, ld); - lineangle = ld->angle - ANGLE_90; - - if (side == 1) - lineangle += ANGLE_180; - - lineangle >>= ANGLETOFINESHIFT; - movelen = P_AproxDistance(tmxmove, tmymove); if (slidemo->player && movelen < (15*mapobjectscale)) movelen = (15*mapobjectscale); - x = FixedMul(movelen, FINECOSINE(lineangle)); - y = FixedMul(movelen, FINESINE(lineangle)); + if (!ld) + { + angle_t th = R_PointToAngle2(0, 0, tmxmove, tmymove); + normal->x = -FCOS(th); + normal->y = -FSIN(th); + } - if (P_IsLineTripWire(ld)) + x = FixedMul(movelen, normal->x); + y = FixedMul(movelen, normal->y); + + if (ld && P_IsLineTripWire(ld)) { tmxmove = x * 4; tmymove = y * 4; @@ -3958,6 +4006,8 @@ papercollision: static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result) { + extern consvar_t cv_showgremlins; + fixed_t mmomx = 0, mmomy = 0; fixed_t oldmomx = mo->momx, oldmomy = mo->momy; @@ -3982,8 +4032,23 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result) slidemo = mo; bestslideline = result->line; - if (bestslideline == NULL) - return; + if (bestslideline == NULL && cv_showgremlins.value) + { + // debug + mobj_t*x = P_SpawnMobj(mo->x, mo->y, mo->z, MT_THOK); + x->frame = FF_FULLBRIGHT | FF_ADD; + x->renderflags = RF_ALWAYSONTOP; + x->color = SKINCOLOR_RED; + + CONS_Printf( + "GREMLIN: leveltime=%u x=%f y=%f z=%f angle=%f\n", + leveltime, + FixedToFloat(mo->x), + FixedToFloat(mo->y), + FixedToFloat(mo->z), + AngleToFloat(R_PointToAngle2(0, 0, oldmomx, oldmomy)) + ); + } if (mo->eflags & MFE_JUSTBOUNCEDWALL) // Stronger push-out { @@ -3996,7 +4061,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result) tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3))); } - if (P_IsLineTripWire(bestslideline)) + if (bestslideline && P_IsLineTripWire(bestslideline)) { // TRIPWIRE CANNOT BE MADE NONBOUNCY K_ApplyTripWire(mo->player, TRIPSTATE_BLOCKED); @@ -4014,7 +4079,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result) K_SpawnBumpEffect(mo); } - P_PlayerHitBounceLine(bestslideline); + P_PlayerHitBounceLine(bestslideline, &result->normal); mo->eflags |= MFE_JUSTBOUNCEDWALL; mo->momx = tmxmove; @@ -4022,7 +4087,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result) mo->player->cmomx = tmxmove; mo->player->cmomy = tmymove; - if (!P_IsLineTripWire(bestslideline)) + if (!bestslideline || !P_IsLineTripWire(bestslideline)) { if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true, NULL)) { @@ -4128,7 +4193,7 @@ static BlockItReturn_t PIT_RadiusAttack(mobj_t *thing) if ((bombdamagetype & DMG_CANTHURTSELF) && bombsource && thing->type == bombsource->type) // ignore the type of guys who dropped the bomb (Jetty-Syn Bomber or Skim can bomb eachother, but not themselves.) return BMIT_CONTINUE; - if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) != MF_SHOOTABLE) + if ((thing->flags & MF_SHOOTABLE) != MF_SHOOTABLE) return BMIT_CONTINUE; dx = abs(thing->x - bombspot->x); diff --git a/src/p_mobj.c b/src/p_mobj.c index bb7b03360..0e2bb2042 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -63,6 +63,8 @@ mobj_t *waypointcap = NULL; // general purpose. mobj_t *trackercap = NULL; +mobj_t *mobjcache = NULL; + void P_InitCachedActions(void) { actioncachehead.prev = actioncachehead.next = &actioncachehead; @@ -1147,7 +1149,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo) P_PlayerFlip(mo); } - if (mo->player->trickpanel >= 2) + if (mo->player->trickpanel > TRICKSTATE_READY) { gravityadd = (5*gravityadd)/2; } @@ -1276,6 +1278,10 @@ fixed_t P_GetMobjGravity(mobj_t *mo) } break; } + case MT_RANDOMAUDIENCE: + if (mo->fuse) + gravityadd /= 10; + break; default: break; } @@ -1669,7 +1675,7 @@ void P_XYMovement(mobj_t *mo) // blocked move moved = false; - if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, tm.blockingline)) + if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, result.line)) { if (P_MobjWasRemoved(mo)) return; @@ -1677,7 +1683,7 @@ void P_XYMovement(mobj_t *mo) else if (P_MobjWasRemoved(mo)) return; - P_PushSpecialLine(tm.blockingline, mo); + P_PushSpecialLine(result.line, mo); if (mo->flags & MF_MISSILE) { @@ -1719,25 +1725,6 @@ void P_XYMovement(mobj_t *mo) P_ExplodeMissile(mo); return; } - else if (mo->flags & MF_STICKY) - { - S_StartSound(mo, mo->info->activesound); - mo->momx = mo->momy = mo->momz = 0; //Full stop! - mo->flags |= MF_NOGRAVITY; //Stay there! - mo->flags &= ~MF_STICKY; //Don't check again! - - // Check for hit against sky here - if (P_CheckSkyHit(mo)) - { - // Hack to prevent missiles exploding - // against the sky. - // Does not handle sky floors. - // Check frontsector as well. - - P_RemoveMobj(mo); - return; - } - } else { boolean walltransferred = false; @@ -5692,12 +5679,12 @@ static void P_FlameJetSceneryThink(mobj_t *mobj) flame->angle = mobj->angle; if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side - flame->momz = mobj->fuse << (FRACBITS - 2); + flame->momz = (mobj->fuse * mapobjectscale) / 4; else flame->angle += FixedAngle(mobj->fuse<movedir; + strength = 20*mapobjectscale; + strength -= ((20*mapobjectscale)/16)*mobj->movedir; P_InstaThrust(flame, flame->angle, strength); S_StartSound(flame, sfx_fire); @@ -5727,8 +5714,8 @@ static void P_VerticalFlameJetSceneryThink(mobj_t *mobj) flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME); - strength = 20*FRACUNIT; - strength -= ((20*FRACUNIT)/16)*mobj->movedir; + strength = 20*mapobjectscale; + strength -= ((20*mapobjectscale)/16)*mobj->movedir; // If deaf'd, the object spawns on the ceiling. if (mobj->flags2 & MF2_AMBUSH) @@ -5742,7 +5729,7 @@ static void P_VerticalFlameJetSceneryThink(mobj_t *mobj) P_SetMobjState(flame, S_FLAMEJETFLAME7); } - P_InstaThrust(flame, mobj->angle, FixedDiv(mobj->fuse*FRACUNIT, 3*FRACUNIT)); + P_InstaThrust(flame, mobj->angle, (mobj->fuse * mapobjectscale) / 3); S_StartSound(flame, sfx_fire); } @@ -6469,7 +6456,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) break; } - if (mobj->target->player->pflags & PF_ITEMOUT) + if (mobj->target->player->itemflags & IF_ITEMOUT) { if (leveltime & 1) mobj->tracer->renderflags &= ~RF_DONTDRAW; @@ -7268,7 +7255,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->flags2 & MF2_STRONGBOX) { - Obj_AudienceThink(mobj, true); + Obj_AudienceThink(mobj, true, false); if (P_MobjWasRemoved(mobj)) return false; } @@ -8408,10 +8395,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj) return true; } - mobj->extravalue1 += 1; - mobj->angle += ANG1*mobj->extravalue1; - P_SetScale(mobj, mobj->target->scale); + mobj->extravalue1 += 1; + P_InstaScale(mobj, mobj->target->scale); destx = mobj->target->x; desty = mobj->target->y; @@ -8420,6 +8406,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { destx += FixedMul(mobj->radius*2, FINECOSINE((mobj->angle+ANGLE_90) >> ANGLETOFINESHIFT)); desty += FixedMul(mobj->radius*2, FINESINE((mobj->angle+ANGLE_90) >> ANGLETOFINESHIFT)); + + mobj->eflags = (mobj->eflags & ~MFE_VERTICALFLIP)|(mobj->target->eflags & MFE_VERTICALFLIP); + mobj->flags2 = (mobj->flags2 & ~MF2_OBJECTFLIP)|(mobj->target->flags2 & MF2_OBJECTFLIP); + + if (mobj->eflags & MFE_VERTICALFLIP) + zoff += mobj->target->height - mobj->height; } else if (mobj->state == &states[S_MAGICIANBOX_TOP]) // top { @@ -8429,6 +8421,150 @@ static boolean P_MobjRegularThink(mobj_t *mobj) // Necessary to "ride" on Garden Top zoff += mobj->target->sprzoff; + if (mobj->flags2 & MF2_AMBUSH) + { + P_SetOrigin(mobj, destx, desty, mobj->target->z + zoff); + mobj->old_angle = mobj->angle; + mobj->flags2 &= ~MF2_AMBUSH; + } + else + { + P_MoveOrigin(mobj, destx, desty, mobj->target->z + zoff); + } + break; + } + case MT_SIDETRICK: + { + fixed_t destx, desty; + fixed_t zoff = 0; + + if (!mobj->target + || !mobj->target->health + || !mobj->target->player + || mobj->target->player->trickpanel <= TRICKSTATE_FORWARD) + { + P_RemoveMobj(mobj); + return false; + } + + // Flicker every other frame from first visibility + if (mobj->flags2 & MF2_BOSSDEAD) + { + mobj->renderflags |= RF_DONTDRAW; + } + else + { + mobj->renderflags &= ~RF_DONTDRAW; + mobj->renderflags |= (mobj->target->renderflags & RF_DONTDRAW); + } + + mobj->eflags = (mobj->eflags & ~MFE_VERTICALFLIP)|(mobj->target->eflags & MFE_VERTICALFLIP); + mobj->flags2 = ((mobj->flags2 & ~MF2_OBJECTFLIP)|(mobj->target->flags2 & MF2_OBJECTFLIP)) ^ MF2_BOSSDEAD; + + fixed_t scale = mobj->target->scale; + + // sweeping effect + if (mobj->target->player->trickpanel == TRICKSTATE_BACK) + { + const fixed_t saferange = (20*FRACUNIT)/21; + if (mobj->threshold < -saferange) + { + mobj->threshold = -saferange; + mobj->flags2 |= MF2_AMBUSH; + } + else while (mobj->threshold > saferange) + { + mobj->threshold -= 2*saferange; + mobj->flags2 |= MF2_AMBUSH; + } + + scale = P_ReturnThrustX(mobj, FixedAngle(90*mobj->threshold), scale); + + // This funny dealie is to make it so default + // scale is placed as standard, + // but variant threshold shifts upwards + fixed_t extraoffset = FixedMul(mobj->info->height, mobj->target->scale - scale); + if (mobj->threshold < 0) + extraoffset /= 2; + + // And this makes it swooce across the object. + extraoffset += FixedMul(mobj->threshold, mobj->target->height); + + zoff += P_MobjFlip(mobj) * extraoffset; + + mobj->threshold += (saferange/8); + } + + mobj->angle += mobj->movedir; + P_InstaScale(mobj, scale); + + destx = mobj->target->x; + desty = mobj->target->y; + + destx += P_ReturnThrustX(mobj, mobj->angle - ANGLE_90, mobj->radius*2); + desty += P_ReturnThrustY(mobj, mobj->angle - ANGLE_90, mobj->radius*2); + + if (mobj->eflags & MFE_VERTICALFLIP) + zoff += mobj->target->height - mobj->height; + + // Necessary to "ride" on Garden Top + zoff += mobj->target->sprzoff; + + if (mobj->flags2 & MF2_AMBUSH) + { + P_SetOrigin(mobj, destx, desty, mobj->target->z + zoff); + mobj->old_angle = mobj->angle; + mobj->flags2 &= ~MF2_AMBUSH; + } + else + { + P_MoveOrigin(mobj, destx, desty, mobj->target->z + zoff); + } + break; + } + case MT_FORWARDTRICK: + { + fixed_t destx, desty; + fixed_t zoff = 0; + + if (!mobj->target + || !mobj->target->health + || !mobj->target->player + || mobj->target->player->trickpanel != TRICKSTATE_FORWARD) + { + P_RemoveMobj(mobj); + return false; + } + + mobj->renderflags &= ~RF_DONTDRAW; + mobj->renderflags |= (mobj->target->renderflags & RF_DONTDRAW); + + mobj->eflags = (mobj->eflags & ~MFE_VERTICALFLIP)|(mobj->target->eflags & MFE_VERTICALFLIP); + mobj->flags2 = ((mobj->flags2 & ~MF2_OBJECTFLIP)|(mobj->target->flags2 & MF2_OBJECTFLIP)) ^ MF2_BOSSDEAD; + + // sweeping effect + P_InstaScale(mobj, (6*mobj->target->scale)/5); + + const fixed_t sweep = FixedMul(FRACUNIT - (mobj->threshold * 2), mobj->radius); + + destx = mobj->target->x; + desty = mobj->target->y; + + destx += P_ReturnThrustX(mobj, mobj->movedir, sweep); + desty += P_ReturnThrustY(mobj, mobj->movedir, sweep); + + const fixed_t sideways = P_ReturnThrustY(mobj, mobj->angle - mobj->movedir, mobj->radius); + destx += P_ReturnThrustX(mobj, mobj->movedir + ANGLE_90, sideways); + desty += P_ReturnThrustY(mobj, mobj->movedir + ANGLE_90, sideways); + + if (mobj->eflags & MFE_VERTICALFLIP) + zoff += mobj->target->height - (mobj->height + 18*mobj->target->scale); + else + zoff += 18*mobj->target->scale; + + // Necessary to "ride" on Garden Top + zoff += mobj->target->sprzoff; + if (mobj->flags2 & MF2_AMBUSH) { P_SetOrigin(mobj, destx, desty, mobj->target->z + zoff); @@ -8438,6 +8574,20 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { P_MoveOrigin(mobj, destx, desty, mobj->target->z + zoff); } + + mobj->threshold += FRACUNIT/6; + if (mobj->threshold > FRACUNIT) + { + mobj_t *puff = P_SpawnGhostMobj(mobj); + if (puff) + { + puff->renderflags = (puff->renderflags & ~RF_TRANSMASK)|RF_ADD; + } + + mobj->threshold -= FRACUNIT; + mobj->flags2 |= MF2_AMBUSH; + } + break; } case MT_LIGHTNINGSHIELD: @@ -8688,6 +8838,26 @@ static boolean P_MobjRegularThink(mobj_t *mobj) Obj_BlockBodyThink(mobj); break; } + case MT_CHARGEAURA: + { + Obj_ChargeAuraThink(mobj); + break; + } + case MT_CHARGEFALL: + { + Obj_ChargeFallThink(mobj); + break; + } + case MT_CHARGERELEASE: + { + Obj_ChargeReleaseThink(mobj); + break; + } + case MT_CHARGEEXTRA: + { + Obj_ChargeExtraThink(mobj); + break; + } case MT_GUARDBREAK: { Obj_GuardBreakThink(mobj); @@ -10073,48 +10243,6 @@ static void K_MineExplodeThink(mobj_t *mobj) } } -static void P_MonitorFuseThink(mobj_t *mobj) -{ - mobj_t *newmobj; - - // Special case for ALL monitors. - // If a box's speed is nonzero, it's allowed to respawn as a WRM/SRM. - if (mobj->info->speed != 0 && (mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX))) - { - mobjtype_t spawnchance[64]; - INT32 numchoices = 0, i = 0; - - // This define should make it a lot easier to organize and change monitor weights -#define SETMONITORCHANCES(type, strongboxamt, weakboxamt) \ -for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) spawnchance[numchoices++] = type - - // Type SRM WRM - SETMONITORCHANCES(MT_SNEAKERS_BOX, 0, 10); // Super Sneakers - SETMONITORCHANCES(MT_INVULN_BOX, 2, 0); // Invincibility - SETMONITORCHANCES(MT_WHIRLWIND_BOX, 3, 8); // Whirlwind Shield - SETMONITORCHANCES(MT_ELEMENTAL_BOX, 3, 8); // Elemental Shield - SETMONITORCHANCES(MT_ATTRACT_BOX, 2, 0); // Attraction Shield - SETMONITORCHANCES(MT_FORCE_BOX, 3, 3); // Force Shield - SETMONITORCHANCES(MT_ARMAGEDDON_BOX, 2, 0); // Armageddon Shield - SETMONITORCHANCES(MT_MIXUP_BOX, 0, 1); // Teleporters - SETMONITORCHANCES(MT_RECYCLER_BOX, 0, 1); // Recycler - SETMONITORCHANCES(MT_1UP_BOX, 1, 1); // 1-Up - // ======================================= - // Total 16 32 - -#undef SETMONITORCHANCES - - i = P_RandomKey(PR_UNDEFINED, numchoices); // Gotta love those random numbers! - newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, spawnchance[i]); - } - else - newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type); - - // Transfer flags2 (ambush, strongbox, objectflip) - newmobj->flags2 = mobj->flags2; - P_RemoveMobj(mobj); // make sure they disappear -} - static boolean P_CanFlickerFuse(mobj_t *mobj) { switch (mobj->type) @@ -10163,11 +10291,6 @@ static boolean P_FuseThink(mobj_t *mobj) if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)) || P_MobjWasRemoved(mobj)) ; - else if (mobj->info->flags & MF_MONITOR) - { - P_MonitorFuseThink(mobj); - return false; - } else switch (mobj->type) { // gargoyle and snowman handled in P_PushableThinker, not here @@ -10312,6 +10435,8 @@ void P_MobjThinker(mobj_t *mobj) P_SetTarget(&mobj->hprev, NULL); if (mobj->itnext && P_MobjWasRemoved(mobj->itnext)) P_SetTarget(&mobj->itnext, NULL); + if (mobj->punt_ref && P_MobjWasRemoved(mobj->punt_ref)) + P_SetTarget(&mobj->punt_ref, NULL); if (mobj->flags & MF_NOTHINK) return; @@ -10808,7 +10933,9 @@ void P_SceneryThinker(mobj_t *mobj) if (mobj->type == MT_RANDOMAUDIENCE) { - Obj_AudienceThink(mobj, !!(mobj->flags2 & MF2_AMBUSH)); + Obj_AudienceThink(mobj, !!(mobj->flags2 & MF2_AMBUSH), !!(mobj->flags2 & MF2_DONTRESPAWN)); + if (P_MobjWasRemoved(mobj)) + return; } } @@ -10820,6 +10947,10 @@ fixed_t P_GetMobjDefaultScale(mobj_t *mobj) { switch(mobj->type) { + case MT_FLAMEJETFLAME: + return 3*FRACUNIT; + case MT_ITEMCLASH: + return 2*FRACUNIT; case MT_SPECIALSTAGEARCH: return 5*FRACUNIT; case MT_SPECIALSTAGEBOMB: @@ -10953,7 +11084,16 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) type = MT_RAY; } - mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); + if (mobjcache != NULL) + { + mobj = mobjcache; + mobjcache = mobjcache->hnext; + memset(mobj, 0, sizeof(*mobj)); + } + else + { + mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); + } // this is officially a mobj, declared as soon as possible. mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; @@ -11366,6 +11506,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->y + FINESINE((ang>>ANGLETOFINESHIFT) & FINEMASK), mobj->z, MT_DAYTONAPINETREE_SIDE); side->angle = ang; P_SetTarget(&side->target, mobj); + P_SetTarget(&side->punt_ref, mobj); side->threshold = i; } break; @@ -11850,6 +11991,7 @@ void P_RemoveMobj(mobj_t *mobj) } P_SetTarget(&mobj->itnext, NULL); + P_SetTarget(&mobj->punt_ref, NULL); P_RemoveThingTID(mobj); P_DeleteMobjStringArgs(mobj); @@ -11861,7 +12003,9 @@ void P_RemoveMobj(mobj_t *mobj) INT32 prevreferences; if (!mobj->thinker.references) { - Z_Free(mobj); // No refrrences? Can be removed immediately! :D + // no references, dump it directly in the mobj cache + mobj->hnext = mobjcache; + mobjcache = mobj; return; } @@ -12450,8 +12594,8 @@ void P_SpawnPlayer(INT32 playernum) p->griefValue = 0; K_InitStumbleIndicator(p); - - K_InitSliptideZipIndicator(p); + K_InitWavedashIndicator(p); + K_InitTrickIndicator(p); if (gametyperules & GTR_ITEMARROWS) { @@ -13725,7 +13869,16 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj) // Use per-thing collision for spikes unless the intangible flag is checked. if (!(mthing->thing_args[2] & TMSF_INTANGIBLE) && !metalrecording) { + const fixed_t kSpriteRadius = 16 * mobj->scale; + fixed_t x = FixedMul(mobj->radius - kSpriteRadius, FCOS(mobj->angle)); + fixed_t y = FixedMul(mobj->radius - kSpriteRadius, FSIN(mobj->angle)); + + mobj->sprxoff -= x; + mobj->spryoff -= y; + P_UnsetThingPosition(mobj); + mobj->x += x; + mobj->y += y; mobj->flags &= ~(MF_NOBLOCKMAP | MF_NOCLIPHEIGHT); mobj->flags |= MF_SOLID; P_SetThingPosition(mobj); @@ -15423,3 +15576,9 @@ void P_DeleteMobjStringArgs(mobj_t *mobj) mobj->script_stringargs[i] = NULL; } } + +tic_t P_MobjIsReappearing(const mobj_t *mobj) +{ + tic_t t = (!P_MobjWasRemoved(mobj->punt_ref) ? mobj->punt_ref : mobj)->reappear; + return t - min(leveltime, t); +} diff --git a/src/p_mobj.h b/src/p_mobj.h index 382bfcd9e..1fbb676bf 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -137,8 +137,8 @@ typedef enum MF_MISSILE = 1<<15, // Item is a spring. MF_SPRING = 1<<16, - // Item box - MF_MONITOR = 1<<17, + // Object is elemental. If it is punted, it will evaporate. + MF_ELEMENTAL = 1<<17, // Don't run the thinker for this object. MF_NOTHINK = 1<<18, // Don't adjust z if below or above floorz/ceilingz @@ -149,8 +149,8 @@ typedef enum MF_SCENERY = 1<<21, // Painful (shit hurts). MF_PAIN = 1<<22, - // This mobj will stick to any surface or solid object it touches. - MF_STICKY = 1<<23, + // Object cannot be punted by invincible players. (Default CAN be punted, if it deals damage or is solid.) + MF_DONTPUNT = 1<<23, // Object uses terrain effects. (Overlays, footsteps, etc) MF_APPLYTERRAIN = 1<<24, // for chase camera, don't be blocked by things (partial clipping) @@ -437,6 +437,14 @@ struct mobj_t boolean frozen; + // Object was punted and is temporarily invisible and + // intangible. This is the leveltime that it will + // reappear. + tic_t reappear; + + // If punt_ref, set punt_ref->reappear, treat as if this->reappear + mobj_t *punt_ref; + // WARNING: New fields must be added separately to savegame and Lua. }; diff --git a/src/p_saveg.c b/src/p_saveg.c index 1325c7440..6fa7a9363 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -65,7 +65,7 @@ savedata_cup_t cupsavedata; #define ARCHIVEBLOCK_RNG 0x7FAAB5BD // Note: This cannot be bigger -// than an UINT16 +// than an UINT16 (for now) typedef enum { AWAYVIEW = 0x0001, @@ -75,12 +75,13 @@ typedef enum SKYBOXCENTER = 0x0010, HOVERHYUDORO = 0x0020, STUMBLE = 0x0040, - SLIPTIDEZIP = 0x0080, + WAVEDASH = 0x0080, RINGSHOOTER = 0x0100, WHIP = 0x0200, HAND = 0x0400, FLICKYATTACKER = 0x0800, FLICKYCONTROLLER = 0x1000, + TRICKINDICATOR = 0x2000, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -310,8 +311,11 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].stumbleIndicator) flags |= STUMBLE; - if (players[i].sliptideZipIndicator) - flags |= SLIPTIDEZIP; + if (players[i].wavedashIndicator) + flags |= WAVEDASH; + + if (players[i].trickIndicator) + flags |= TRICKINDICATOR; if (players[i].whip) flags |= WHIP; @@ -348,8 +352,11 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & STUMBLE) WRITEUINT32(save->p, players[i].stumbleIndicator->mobjnum); - if (flags & SLIPTIDEZIP) - WRITEUINT32(save->p, players[i].sliptideZipIndicator->mobjnum); + if (flags & WAVEDASH) + WRITEUINT32(save->p, players[i].wavedashIndicator->mobjnum); + + if (flags & TRICKINDICATOR) + WRITEUINT32(save->p, players[i].trickIndicator->mobjnum); if (flags & WHIP) WRITEUINT32(save->p, players[i].whip->mobjnum); @@ -394,6 +401,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].currentwaypoint)); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT32(save->p, players[i].airtime); + WRITEUINT32(save->p, players[i].lastairtime); WRITEUINT8(save->p, players[i].startboost); WRITEUINT8(save->p, players[i].dropdashboost); @@ -478,6 +486,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].flamelength); WRITEUINT16(save->p, players[i].ballhogcharge); + WRITEUINT8(save->p, players[i].ballhogtap); WRITEUINT16(save->p, players[i].hyudorotimer); WRITESINT8(save->p, players[i].stealingtimer); @@ -535,9 +544,12 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].tripwireReboundDelay); - WRITEUINT16(save->p, players[i].sliptideZip); - WRITEUINT8(save->p, players[i].sliptideZipDelay); - WRITEUINT16(save->p, players[i].sliptideZipBoost); + WRITEUINT16(save->p, players[i].wavedash); + WRITEUINT8(save->p, players[i].wavedashdelay); + WRITEUINT16(save->p, players[i].wavedashboost); + WRITEUINT16(save->p, players[i].trickcharge); + + WRITEUINT16(save->p, players[i].infinitether); WRITEUINT8(save->p, players[i].lastsafelap); @@ -555,9 +567,13 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEINT16(save->p, players[i].incontrol); WRITEUINT8(save->p, players[i].markedfordeath); + WRITEUINT8(save->p, players[i].dotrickfx); WRITEUINT8(save->p, players[i].ringboxdelay); WRITEUINT8(save->p, players[i].ringboxaward); + + WRITEUINT8(save->p, players[i].itemflags); + WRITEFIXED(save->p, players[i].outrun); WRITEUINT8(save->p, players[i].rideroid); @@ -683,6 +699,17 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEFIXED(save->p, players[i].loop.shift.y); WRITEUINT8(save->p, players[i].loop.flip); + // sonicloopcamvars_t + WRITEUINT32(save->p, players[i].loop.camera.enter_tic); + WRITEUINT32(save->p, players[i].loop.camera.exit_tic); + WRITEUINT32(save->p, players[i].loop.camera.zoom_in_speed); + WRITEUINT32(save->p, players[i].loop.camera.zoom_out_speed); + WRITEFIXED(save->p, players[i].loop.camera.dist); + WRITEANGLE(save->p, players[i].loop.camera.pan); + WRITEFIXED(save->p, players[i].loop.camera.pan_speed); + WRITEUINT32(save->p, players[i].loop.camera.pan_accel); + WRITEUINT32(save->p, players[i].loop.camera.pan_back); + // ACS has read access to this, so it has to be net-communicated. // It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way. WRITEUINT32(save->p, players[i].roundconditions.unlocktriggers); @@ -863,8 +890,11 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & STUMBLE) players[i].stumbleIndicator = (mobj_t *)(size_t)READUINT32(save->p); - if (flags & SLIPTIDEZIP) - players[i].sliptideZipIndicator = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & WAVEDASH) + players[i].wavedashIndicator = (mobj_t *)(size_t)READUINT32(save->p); + + if (flags & TRICKINDICATOR) + players[i].trickIndicator = (mobj_t *)(size_t)READUINT32(save->p); if (flags & WHIP) players[i].whip = (mobj_t *)(size_t)READUINT32(save->p); @@ -910,6 +940,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].currentwaypoint = (waypoint_t *)(size_t)READUINT32(save->p); players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save->p); players[i].airtime = READUINT32(save->p); + players[i].lastairtime = READUINT32(save->p); players[i].startboost = READUINT8(save->p); players[i].dropdashboost = READUINT8(save->p); @@ -994,6 +1025,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].flamelength = READUINT8(save->p); players[i].ballhogcharge = READUINT16(save->p); + players[i].ballhogtap = READUINT8(save->p); players[i].hyudorotimer = READUINT16(save->p); players[i].stealingtimer = READSINT8(save->p); @@ -1051,9 +1083,12 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].tripwireReboundDelay = READUINT8(save->p); - players[i].sliptideZip = READUINT16(save->p); - players[i].sliptideZipDelay = READUINT8(save->p); - players[i].sliptideZipBoost = READUINT16(save->p); + players[i].wavedash = READUINT16(save->p); + players[i].wavedashdelay = READUINT8(save->p); + players[i].wavedashboost = READUINT16(save->p); + players[i].trickcharge = READUINT16(save->p); + + players[i].infinitether = READUINT16(save->p); players[i].lastsafelap = READUINT8(save->p); @@ -1071,9 +1106,13 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].incontrol = READINT16(save->p); players[i].markedfordeath = READUINT8(save->p); + players[i].dotrickfx = READUINT8(save->p); players[i].ringboxdelay = READUINT8(save->p); players[i].ringboxaward = READUINT8(save->p); + + players[i].itemflags = READUINT8(save->p); + players[i].outrun = READFIXED(save->p); players[i].rideroid = (boolean)READUINT8(save->p); @@ -1210,6 +1249,17 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].loop.shift.y = READFIXED(save->p); players[i].loop.flip = READUINT8(save->p); + // sonicloopcamvars_t + players[i].loop.camera.enter_tic = READUINT32(save->p); + players[i].loop.camera.exit_tic = READUINT32(save->p); + players[i].loop.camera.zoom_in_speed = READUINT32(save->p); + players[i].loop.camera.zoom_out_speed = READUINT32(save->p); + players[i].loop.camera.dist = READFIXED(save->p); + players[i].loop.camera.pan = READANGLE(save->p); + players[i].loop.camera.pan_speed = READFIXED(save->p); + players[i].loop.camera.pan_accel = READUINT32(save->p); + players[i].loop.camera.pan_back = READUINT32(save->p); + // ACS has read access to this, so it has to be net-communicated. // It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way. players[i].roundconditions.unlocktriggers = READUINT32(save->p); @@ -1999,6 +2049,8 @@ static void ArchiveSectors(savebuffer_t *save) WRITEUINT8(save->p, diff3); if (diff3 & SD_DIFF4) WRITEUINT8(save->p, diff4); + if (diff4 & SD_DIFF5) + WRITEUINT8(save->p, diff5); if (diff & SD_FLOORHT) WRITEFIXED(save->p, ss->floorheight); if (diff & SD_CEILHT) @@ -2638,9 +2690,16 @@ typedef enum MD2_FROZEN = 1<<28, MD2_TERRAIN = 1<<29, MD2_WATERSKIP = 1<<30, - MD2_LIGHTLEVEL = (INT32)(1U<<31), + MD2_MORE = (INT32)(1U<<31), } mobj_diff2_t; +typedef enum +{ + MD3_LIGHTLEVEL = 1, + MD3_REAPPEAR = 1<<1, + MD3_PUNT_REF = 1<<2, +} mobj_diff3_t; + typedef enum { tc_mobj, @@ -2751,12 +2810,14 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 const mobj_t *mobj = (const mobj_t *)th; UINT32 diff; UINT32 diff2; + UINT32 diff3; size_t j; if (TypeIsNetSynced(mobj->type) == false) return; diff2 = 0; + diff3 = 0; if (mobj->spawnpoint) { @@ -2938,8 +2999,6 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 || (slope->normal.z != FRACUNIT)) diff2 |= MD2_FLOORSPRITESLOPE; } - if (mobj->lightlevel) - diff2 |= MD2_LIGHTLEVEL; if (mobj->hitlag) diff2 |= MD2_HITLAG; if (mobj->waterskip) @@ -2957,6 +3016,16 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 if (mobj->terrain != NULL || mobj->terrainOverlay != NULL) diff2 |= MD2_TERRAIN; + if (mobj->lightlevel) + diff3 |= MD3_LIGHTLEVEL; + if (mobj->reappear) + diff3 |= MD3_REAPPEAR; + if (mobj->punt_ref) + diff3 |= MD3_PUNT_REF; + + if (diff3 != 0) + diff2 |= MD2_MORE; + if (diff2 != 0) diff |= MD_MORE; @@ -2968,6 +3037,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 WRITEUINT32(save->p, diff); if (diff & MD_MORE) WRITEUINT32(save->p, diff2); + if (diff2 & MD2_MORE) + WRITEUINT32(save->p, diff3); WRITEFIXED(save->p, mobj->z); // Force this so 3dfloor problems don't arise. WRITEFIXED(save->p, mobj->floorz); @@ -3205,10 +3276,6 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 WRITEFIXED(save->p, slope->normal.y); WRITEFIXED(save->p, slope->normal.z); } - if (diff2 & MD2_LIGHTLEVEL) - { - WRITEINT16(save->p, mobj->lightlevel); - } if (diff2 & MD2_HITLAG) { WRITEINT32(save->p, mobj->hitlag); @@ -3231,6 +3298,19 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8 WRITEUINT32(save->p, SaveMobjnum(mobj->terrainOverlay)); } + if (diff3 & MD3_LIGHTLEVEL) + { + WRITEINT16(save->p, mobj->lightlevel); + } + if (diff3 & MD3_REAPPEAR) + { + WRITEUINT32(save->p, mobj->reappear); + } + if (diff3 & MD3_PUNT_REF) + { + WRITEUINT32(save->p, mobj->punt_ref->mobjnum); + } + WRITEUINT32(save->p, mobj->mobjnum); } @@ -4104,6 +4184,7 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) mobj_t *mobj; UINT32 diff; UINT32 diff2; + UINT32 diff3; INT32 i; fixed_t z, floorz, ceilingz; ffloor_t *floorrover = NULL, *ceilingrover = NULL; @@ -4115,6 +4196,11 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) else diff2 = 0; + if (diff2 & MD2_MORE) + diff3 = READUINT32(save->p); + else + diff3 = 0; + z = READFIXED(save->p); // Force this so 3dfloor problems don't arise. floorz = READFIXED(save->p); ceilingz = READFIXED(save->p); @@ -4440,10 +4526,6 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) P_UpdateSlopeLightOffset(slope); } - if (diff2 & MD2_LIGHTLEVEL) - { - mobj->lightlevel = READINT16(save->p); - } if (diff2 & MD2_HITLAG) { mobj->hitlag = READINT32(save->p); @@ -4470,6 +4552,19 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker) mobj->terrain = NULL; } + if (diff3 & MD3_LIGHTLEVEL) + { + mobj->lightlevel = READINT16(save->p); + } + if (diff3 & MD3_REAPPEAR) + { + mobj->reappear = READUINT32(save->p); + } + if (diff3 & MD3_PUNT_REF) + { + mobj->punt_ref = (mobj_t *)(size_t)READUINT32(save->p); + } + // set sprev, snext, bprev, bnext, subsector P_SetThingPosition(mobj); @@ -5509,6 +5604,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->terrainOverlay, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "terrainOverlay not found on %d\n", mobj->type); } + if (mobj->punt_ref) + { + temp = (UINT32)(size_t)mobj->punt_ref; + mobj->punt_ref = NULL; + if (!P_SetTarget(&mobj->punt_ref, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "punt_ref not found on %d\n", mobj->type); + } } for (i = 0; i < MAXPLAYERS; i++) @@ -5592,12 +5694,19 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&players[i].stumbleIndicator, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "stumbleIndicator not found on player %d\n", i); } - if (players[i].sliptideZipIndicator) + if (players[i].wavedashIndicator) { - temp = (UINT32)(size_t)players[i].sliptideZipIndicator; - players[i].sliptideZipIndicator = NULL; - if (!P_SetTarget(&players[i].sliptideZipIndicator, P_FindNewPosition(temp))) - CONS_Debug(DBG_GAMELOGIC, "sliptideZipIndicator not found on player %d\n", i); + temp = (UINT32)(size_t)players[i].wavedashIndicator; + players[i].wavedashIndicator = NULL; + if (!P_SetTarget(&players[i].wavedashIndicator, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "wavedashIndicator not found on player %d\n", i); + } + if (players[i].trickIndicator) + { + temp = (UINT32)(size_t)players[i].trickIndicator; + players[i].trickIndicator = NULL; + if (!P_SetTarget(&players[i].trickIndicator, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "trickIndicator not found on player %d\n", i); } if (players[i].whip) { diff --git a/src/p_setup.c b/src/p_setup.c index 4ba02f340..0bc686292 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8363,6 +8363,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) Patch_FreeTag(PU_PATCH_LOWPRIORITY); Patch_FreeTag(PU_PATCH_ROTATED); Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); + mobjcache = NULL; R_InitializeLevelInterpolators(); diff --git a/src/p_sweep.cpp b/src/p_sweep.cpp new file mode 100644 index 000000000..d4e9d22ef --- /dev/null +++ b/src/p_sweep.cpp @@ -0,0 +1,271 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include +#include + +#include "p_sweep.hpp" + +using namespace srb2::math; +using namespace srb2::sweep; + +Result SlopeAABBvsLine::vs_slope(const line_segment& l) const +{ + auto [a, b] = l.by_x(); // left, right + LineEquation ql{l}; + unit ls = copysign(kUnit, ql.m()); + + auto hit = [&](const vec2& k, unit xr, unit x, const vec2& n) -> Contact + { + std::optional k2; + + if (l.horizontal()) + { + // Horizontal line: create second contact point on opposite corner. + // TODO: avoid duplicate point + k2 = vec2(std::clamp(x + xr, a.x, b.x), k.y); + } + + return {time(x), n, k, k2}; + }; + + auto slide = [&](const vec2& k, const vec2& s) -> std::optional + { + vec2 kf = k * s; + vec2 r = r_ * s; + vec2 p = k - r; + + // Slide vertically along AABB left/right edge. + unit f = q_.y(p.x) * s.y; + + if (f - r_ > kf.y) + { + // Out of bounds detection. + // This should never slide in front. + // If it does, there was never a hit. + return {}; + } + + if (f + r_ < kf.y) + { + // Slid behind contact point. + // Try sliding horizontally along AABB top/bottom + // edge. + + if (q_.m() == kZero) + { + // Sweep is horizontal. + // It is impossible to slide against a line's + // end by the X axis because the line segment + // lies on that axis. + return {}; + } + + p.x = q_.x(p.y); + f = p.x * s.x; + + if (f - r_ > kf.x) + { + // Slid beyond contact point. + return {}; + } + + if (f + r_ < kf.x) + { + // Out of bounds detection. + // This should never slide behind. + // If it does, there was never a hit. + return {}; + } + + return hit(k, r.x, p.x, {kZero, -s.y}); + } + + return hit(k, r.x, p.x, {-s.x, kZero}); + }; + + // xrs.x = x radius + // xrs.y = x sign + auto bind = [&](const vec2& k, const vec2& xrs, unit ns) -> std::optional + { + if (k.x < a.x) + { + return slide(a, {xrs.y, ls}); + } + + if (k.x > b.x) + { + return slide(b, {xrs.y, -ls}); + } + + return hit(k, xrs.x, k.x + xrs.x, normal(l) * ns); + }; + + if (ql.m() == q_.m()) + { + // Parallel lines can only cross at the ends. + vec2 s{kUnit, ls}; + return order(slide(a, s), slide(b, -s), ds_.x); + } + + vec2 i = ql.intersect(q_); + + // Compare slopes to determine if ray is moving upward or + // downward into line. + // For a positive line, AABB top left corner hits the + // line first if the ray is moving upward. + // Swap diagonal corners to bottom right if moving + // downward. + unit ys = q_.m() * ds_.x < ql.m() * ds_.x ? -kUnit : kUnit; + unit yr = r_ * ys; + + // Swap left/right corners if line is negative. + unit xr = yr * ls; + + // Intersection as if ray were offset -r, +r. + vec2 v = [&] + { + unit y = (q_.m() * xr) + yr; + unit x = y / (ql.m() - q_.m()); + return vec2 {x, (x * q_.m()) + y}; + }(); + + // Find the intersection along diagonally oppposing AABB + // corners. + vec2 xrs{xr, ds_.x}; + return {bind(i + v, xrs, -ys), bind(i - v, -xrs, -ys)}; +} + +// TODO: Comments. Bitch. +Result SlopeAABBvsLine::vs_vertical(const line_segment& l) const +{ + auto [a, b] = l.by_y(); // bottom, top + + auto hit = [&](const vec2& p, std::optional q, unit x, const vec2& n) -> Contact { return {time(x), n, p, q}; }; + + auto bind = [&](const vec2& k, const vec2& a, const vec2& b, const vec2& s, auto limit) -> std::optional + { + vec2 r = r_ * s; + vec2 af = a * s; + unit kyf = k.y * s.y; + + if (kyf + r_ < af.y) + { + if (q_.m() == kZero) + { + return {}; + } + + unit x = q_.x(a.y - r.y); + + if ((x * s.x) - r_ > af.x) + { + return {}; + } + + return hit(a, {}, x, {kZero, -s.y}); + } + + // TODO: avoid duplicate point + vec2 k2{k.x, limit(k.y - r.y, a.y)}; + unit byf = b.y * s.y; + vec2 n{-s.x, kZero}; + + if (kyf + r_ > byf) + { + if (kyf - r_ > byf) + { + return {}; + } + + return hit(b, k2, k.x - r.x, n); + } + + return hit(vec2(k.x, k.y + r.y), k2, k.x - r.x, n); + }; + + vec2 i{a.x, q_.y(a.x)}; + vec2 v{kZero, q_.m() * r_ * ds_.x * ds_.y}; + vec2 s = ds_ * ds_.y; + + // Damn you, template overloads! + auto min = [](unit x, unit y) { return std::min(x, y); }; + auto max = [](unit x, unit y) { return std::max(x, y); }; + + return order(bind(i - v, a, b, s, max), bind(i + v, b, a, -s, min), ds_.y); +} + +Result VerticalAABBvsLine::vs_slope(const line_segment& l) const +{ + auto [a, b] = l.by_x(); // left, right + LineEquation ql{l}; + + auto hit = [&](const vec2& k, unit xr, unit y, const vec2& n) -> Contact + { + std::optional k2; + + if (l.horizontal()) + { + // Horizontal line: create second contact point on opposite corner. + // TODO: avoid duplicate point + k2 = vec2(std::clamp(x_ + xr, a.x, b.x), k.y); + } + + return {time(y), n, k, k2}; + }; + + auto bind = [&](const vec2& a, const vec2& b, const vec2& s) -> std::optional + { + vec2 r = r_ * s; + unit xf = x_ * s.x; + + if (xf - r_ > b.x * s.x) + { + return {}; + } + + unit axf = a.x * s.x; + + if (xf - r_ < axf) + { + if (xf + r_ < axf) + { + return {}; + } + + return hit(a, r.x, a.y - r.y, {kZero, -s.y}); + } + + vec2 i{x_, ql.y(x_)}; + vec2 v{r.x, ql.m() * r.x}; + vec2 k = i - v; + return hit(k, r.x, k.y - r.y, normal(l) * -s.y); + }; + + unit mys = copysign(kUnit, ql.m() * ds_.y); + vec2 s{kUnit, ds_.y * mys}; + return order(bind(a, b, s), bind(b, a, -s), mys); +} + +Result VerticalAABBvsLine::vs_vertical(const line_segment& l) const +{ + // Box does not overlap Y plane. + if (x_ + r_ < l.a.x || x_ - r_ > l.a.x) + { + return {}; + } + + auto [a, b] = l.by_y(); // bottom, top + + auto hit = [&](const vec2& k, unit yr) -> Contact { return {time(k.y + yr), {kZero, -ds_.y}, k}; }; + + // Offset away from line ends. + // Contacts are opposite when swept downward. + return order(hit(a, -r_), hit(b, r_), ds_.y); +} diff --git a/src/p_sweep.hpp b/src/p_sweep.hpp new file mode 100644 index 000000000..68c6ac359 --- /dev/null +++ b/src/p_sweep.hpp @@ -0,0 +1,131 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef p_sweep_hpp +#define p_sweep_hpp + +#include +#include + +#include "math/fixed.hpp" +#include "math/line_equation.hpp" +#include "math/line_segment.hpp" +#include "math/vec.hpp" + +namespace srb2::sweep +{ + +using unit = math::Fixed; +using vec2 = math::Vec2; +using line_segment = math::LineSegment; + +struct Contact +{ + unit z; // time + vec2 n; // normal TODO REMOVE duplicate for each contact + vec2 p; // contact point 1 + std::optional q; // AABBvsLine: contact point 2 +}; + +struct Result +{ + std::optional hit, exit; // TODO result itself should be optional, not each contact +}; + +namespace detail +{ + +template +struct BaseAABBvsLine : protected srb2::math::Traits +{ +public: + Result operator()(const line_segment& l) const + { + auto derived = static_cast(this); + return l.vertical() ? derived->vs_vertical(l) : derived->vs_slope(l); + } + +protected: + unit r_; // AABB radius + vec2 ds_; // sweep direction signs + + BaseAABBvsLine(unit r, const vec2& d, unit pz, unit dz) : + r_(r), ds_(copysign(kUnit, d.x), copysign(kUnit, d.y)), t_(pz, dz) {} + + unit time(unit x) const { return (x - t_.x) / t_.y; } + + static Result order(std::optional&& t1, std::optional&& t2, unit s) + { + return s > kZero ? Result {t1, t2} : Result {t2, t1}; + } + + static vec2 normal(const vec2& v) + { + // Normalize vector so that x is positive -- normal always points up. + return v.normal() * (copysign(kUnit, v.x) / v.magnitude()); + } + + static vec2 normal(const line_segment& l) { return normal(l.b - l.a); } + +private: + vec2 t_; // origin and length for calculating time +}; + +}; // namespace detail + +// Sweep can be represented as y = mx + b +struct SlopeAABBvsLine : detail::BaseAABBvsLine +{ + SlopeAABBvsLine(unit r, const line_segment& l) : SlopeAABBvsLine(r, l.a, l.b - l.a) {} + + Result vs_slope(const line_segment& l) const; + Result vs_vertical(const line_segment& l) const; + +private: + math::LineEquationX q_; + + SlopeAABBvsLine(unit r, const vec2& p, const vec2& d) : BaseAABBvsLine(r, d, p.x, d.x), q_(p, d) {} +}; + +// Sweep is vertical +struct VerticalAABBvsLine : detail::BaseAABBvsLine +{ + VerticalAABBvsLine(unit r, const line_segment& l) : VerticalAABBvsLine(r, l.a, l.b - l.a) {} + + Result vs_slope(const line_segment& l) const; + Result vs_vertical(const line_segment& l) const; + +private: + unit x_; + + VerticalAABBvsLine(unit r, const vec2& p, const vec2& d) : BaseAABBvsLine(r, d, p.y, d.y), x_(p.x) {} +}; + +struct AABBvsLine +{ + AABBvsLine(unit r, const line_segment& l) : + var_(l.vertical() ? var_t {VerticalAABBvsLine(r, l)} : var_t {SlopeAABBvsLine(r, l)}) + { + } + + Result operator()(const line_segment& l) const + { + Result rs; + std::visit([&](auto& sweeper) { rs = sweeper(l); }, var_); + return rs; + } + +private: + using var_t = std::variant; + var_t var_; +}; + +}; // namespace srb2::sweep + +#endif/*p_sweep_hpp*/ diff --git a/src/p_test.cpp b/src/p_test.cpp new file mode 100644 index 000000000..e362f33a5 --- /dev/null +++ b/src/p_test.cpp @@ -0,0 +1,78 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include +#include + +#include "math/fixed.hpp" +#include "p_sweep.hpp" + +#include "p_local.h" + +namespace +{ + +std::vector g_lines; + +}; + +void P_TestLine(line_t* ld) +{ + g_lines.emplace_back(ld); +} + +line_t* P_SweepTestLines(fixed_t ax, fixed_t ay, fixed_t bx, fixed_t by, fixed_t r, vector2_t* return_normal) +{ + using namespace srb2::math; + using namespace srb2::sweep; + + struct Collision + { + unit z; + vec2 normal; + line_t* ld; + + bool operator<(const Collision& b) const { return z < b.z; } + }; + + std::optional collision; + + LineSegment l{{ax, ay}, {bx, by}}; + AABBvsLine sweep{r, l}; + + for (line_t* ld : g_lines) + { + LineSegment ls{{ld->v1->x, ld->v1->y}, {ld->v2->x, ld->v2->y}}; + Result rs = sweep(ls); + if (rs.hit) + { + if (!collision || rs.hit->z < collision->z) + { + collision = {rs.hit->z, rs.hit->n, ld}; + } + } + } + + g_lines.clear(); + + if (!collision) + { + return nullptr; + } + + return_normal->x = Fixed {collision->normal.x}; + return_normal->y = Fixed {collision->normal.y}; + + return collision->ld; +} + +void P_ClearTestLines(void) +{ + g_lines.clear(); +} diff --git a/src/p_tick.c b/src/p_tick.c index 1ad1c8dd2..0023d2a2c 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -310,6 +310,7 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker) thlist[n].prev = thinker; thinker->references = 0; // killough 11/98: init reference counter to 0 + thinker->cachable = n == THINK_MOBJ; #ifdef PARANOIA thinker->debug_mobjtype = MT_NULL; @@ -427,7 +428,16 @@ void P_UnlinkThinker(thinker_t *thinker) I_Assert(thinker->references == 0); (next->prev = thinker->prev)->next = next; - Z_Free(thinker); + if (thinker->cachable) + { + // put cachable thinkers in the mobj cache, so we can avoid allocations + ((mobj_t *)thinker)->hnext = mobjcache; + mobjcache = (mobj_t *)thinker; + } + else + { + Z_Free(thinker); + } } // @@ -708,6 +718,9 @@ static inline void P_DeviceRumbleTick(void) if (player->mo == NULL) continue; + if (player->exiting) + continue; + if ((player->mo->eflags & MFE_DAMAGEHITLAG) && player->mo->hitlag) { low = high = 65536 / 2; diff --git a/src/p_user.c b/src/p_user.c index 419e78ab6..fbb03d497 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -478,7 +478,7 @@ void P_ResetPlayer(player_t *player) player->onconveyor = 0; //player->drift = player->driftcharge = 0; - player->trickpanel = 0; + player->trickpanel = TRICKSTATE_NONE; player->glanceDir = 0; player->fastfall = 0; @@ -1109,6 +1109,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) P_SetTarget(&ghost->target, mobj); P_SetScale(ghost, mobj->scale); + ghost->scalespeed = mobj->scalespeed; ghost->destscale = mobj->scale; if (mobj->eflags & MFE_VERTICALFLIP) @@ -1163,9 +1164,13 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) ghost->old_angle = (mobj->player ? mobj->player->old_drawangle2 : mobj->old_angle2); ghost->old_pitch = mobj->old_pitch2; ghost->old_roll = mobj->old_roll2; + ghost->old_scale = mobj->old_scale2; K_ReduceVFX(ghost, mobj->player); + ghost->reappear = mobj->reappear; + P_SetTarget(&ghost->punt_ref, mobj->punt_ref); + return ghost; } @@ -2465,18 +2470,21 @@ void P_MovePlayer(player_t *player) } else { - K_KartMoveAnimation(player); - - if (player->trickpanel == 2) + if (player->trickpanel > TRICKSTATE_READY) { - player->drawangle += ANGLE_22h; - } - else if (player->trickpanel >= 3) - { - player->drawangle -= ANGLE_22h; + if (player->trickpanel <= TRICKSTATE_RIGHT) // right/forward + { + player->drawangle += ANGLE_22h; + } + else //if (player->trickpanel >= TRICKSTATE_LEFT) // left/back + { + player->drawangle -= ANGLE_22h; + } } else { + K_KartMoveAnimation(player); + player->drawangle = player->mo->angle; if (player->aizdriftturn) @@ -2709,9 +2717,6 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius) if (!(mo->flags & MF_SHOOTABLE) && (mo->type != MT_SPB)) // Don't want to give SPB MF_SHOOTABLE, to ensure it's undamagable through other means continue; - if (mo->flags & MF_MONITOR) - continue; // Monitors cannot be 'nuked'. - if (abs(inflictor->x - mo->x) > radius || abs(inflictor->y - mo->y) > radius || abs(inflictor->z - mo->z) > radius) continue; // Workaround for possible integer overflow in the below -Red @@ -3083,6 +3088,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall fixed_t scaleDiff; fixed_t cameraScale = mapobjectscale; + sonicloopcamvars_t *loop = &player->loop.camera; + tic_t loop_out = leveltime - loop->enter_tic; + tic_t loop_in = max(leveltime, loop->exit_tic) - loop->exit_tic; + thiscam->old_x = thiscam->x; thiscam->old_y = thiscam->y; thiscam->old_z = thiscam->z; @@ -3207,6 +3216,58 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camdist = FixedMul(cv_cam_dist[num].value, cameraScale); camheight = FixedMul(cv_cam_height[num].value, cameraScale); + if (loop_in < loop->zoom_in_speed) + { + fixed_t f = loop_out < loop->zoom_out_speed + ? (loop_out * FRACUNIT) / loop->zoom_out_speed + : FRACUNIT - ((loop_in * FRACUNIT) / loop->zoom_in_speed); + + camspeed -= FixedMul(f, camspeed - (FRACUNIT/10)); + camdist += FixedMul(f, loop->dist); + } + + if (loop_in < max(loop->pan_back, 1)) + { + fixed_t f = (loop_in * FRACUNIT) / max(loop->pan_back, 1); + + fixed_t dx = mo->x - thiscam->x; + fixed_t dy = mo->y - thiscam->y; + + angle_t th = R_PointToAngle2(0, 0, dx, dy); + fixed_t d = AngleFixed(focusangle - th); + + if (d > 180*FRACUNIT) + { + d -= (360*FRACUNIT); + } + + focusangle = th + FixedAngle(FixedMul(f, d)); + + if (loop_in == 0) + { + focusaiming = R_PointToAngle2(0, thiscam->z, FixedHypot(dx, dy), mo->z); + } + } + + if (loop_in == 0) + { + tic_t accel = max(loop->pan_accel, 1); + fixed_t f = (min(loop_out, accel) * FRACUNIT) / accel; + + INT32 turn = AngleDeltaSigned(focusangle, player->loop.yaw - loop->pan); + INT32 turnspeed = FixedAngle(FixedMul(f, loop->pan_speed)); + + if (turn > turnspeed) + { + if (turn < ANGLE_90) + { + turnspeed = -(turnspeed); + } + + focusangle += turnspeed; + } + } + if (timeover) { const INT32 timeovercam = max(0, min(180, (player->karthud[khud_timeovercam] - 2*TICRATE)*15)); @@ -3889,6 +3950,11 @@ DoABarrelRoll (player_t *player) fixed_t smoothing; + if (player->loop.radius) + { + return; + } + if (player->respawn.state != RESPAWNST_NONE) { player->tilt = 0; @@ -3995,7 +4061,8 @@ void P_PlayerThink(player_t *player) PlayerPointerErase(player->followmobj); PlayerPointerErase(player->stumbleIndicator); - PlayerPointerErase(player->sliptideZipIndicator); + PlayerPointerErase(player->wavedashIndicator); + PlayerPointerErase(player->trickIndicator); PlayerPointerErase(player->whip); PlayerPointerErase(player->hand); PlayerPointerErase(player->ringShooter); @@ -4017,6 +4084,7 @@ void P_PlayerThink(player_t *player) if (P_IsObjectOnGround(player->mo) && !P_PlayerInPain(player)) // This isn't airtime, but it's control loss all the same. { + player->lastairtime = player->airtime; player->airtime = 0; } else @@ -4329,12 +4397,12 @@ void P_PlayerThink(player_t *player) } } + boolean deathcontrolled = (player->respawn.state != RESPAWNST_NONE && player->respawn.truedeath == true) + || (player->pflags & PF_NOCONTEST) || (player->karmadelay); + boolean powercontrolled = (player->hyudorotimer) || (player->growshrinktimer > 0); + // Flash player after being hit. - if (!(player->hyudorotimer // SRB2kart - fixes Hyudoro not flashing when it should. - || player->growshrinktimer > 0 // Grow doesn't flash either. - || (player->respawn.state != RESPAWNST_NONE && player->respawn.truedeath == true) // Respawn timer (for drop dash effect) - || (player->pflags & PF_NOCONTEST) // NO CONTEST explosion - || player->karmadelay)) + if (!deathcontrolled && !powercontrolled) { if (player->flashing > 1 && player->flashing < K_GetKartFlashing(player) && (leveltime & 1)) @@ -4342,6 +4410,10 @@ void P_PlayerThink(player_t *player) else player->mo->renderflags &= ~RF_DONTDRAW; } + else if (!deathcontrolled) + { + player->mo->renderflags &= ~RF_DONTDRAW; + } if (player->stairjank > 0) { diff --git a/src/r_bbox.c b/src/r_bbox.c index 3654565a8..d484c3b51 100644 --- a/src/r_bbox.c +++ b/src/r_bbox.c @@ -204,29 +204,40 @@ void R_DrawThingBoundingBox(vissprite_t *vis) .color = R_GetBoundingBoxColor(vis->mobj), }; - // 1--3 - // | | - // 0--2 + if (vis->mobjflags & MF_PAPERCOLLISION) + { + // 0--1 - // left + draw_bbox_col(&bb, 0, tx - rc, ty - rs); // left + draw_bbox_col(&bb, 1, tx + rc, ty + rs); // right + draw_bbox_row(&bb, 0, 1); // connect both + } + else + { + // 1--3 + // | | + // 0--2 - draw_bbox_col(&bb, 0, tx, ty); // bottom - draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top + // left - // right + draw_bbox_col(&bb, 0, tx, ty); // bottom + draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top - tx += rs; - ty += rc; + // right - draw_bbox_col(&bb, 2, tx, ty); // bottom - draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top + tx += rs; + ty += rc; - // connect all four columns + draw_bbox_col(&bb, 2, tx, ty); // bottom + draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top - draw_bbox_row(&bb, 0, 1); - draw_bbox_row(&bb, 1, 3); - draw_bbox_row(&bb, 3, 2); - draw_bbox_row(&bb, 2, 0); + // connect all four columns + + draw_bbox_row(&bb, 0, 1); + draw_bbox_row(&bb, 1, 3); + draw_bbox_row(&bb, 3, 2); + draw_bbox_row(&bb, 2, 0); + } } static boolean is_tangible (mobj_t *thing) @@ -240,7 +251,7 @@ static boolean is_tangible (mobj_t *thing) // These objects probably do nothing! :D if ((thing->flags & (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE |MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_SPRING - |MF_MONITOR|MF_ENEMY|MF_PAIN|MF_STICKY + |MF_ELEMENTAL|MF_ENEMY|MF_PAIN|MF_DONTPUNT |MF_PICKUPFROMBELOW)) == 0U) { return false; diff --git a/src/r_things.cpp b/src/r_things.cpp index 0ecf02c12..37dcc3bf4 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -1586,13 +1586,22 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis) R_InterpolateMobjState(thing, FRACUNIT, &interp); } - // 1--3 - // | | - // 0--2 - - // start in the (0) corner - gx = interp.x - thing->radius - viewx; - gy = interp.y - thing->radius - viewy; + if (thing->flags & MF_PAPERCOLLISION) + { + // 0--1 + // start in the middle + gx = interp.x - viewx; + gy = interp.y - viewy; + } + else + { + // 1--3 + // | | + // 0--2 + // start in the (0) corner + gx = interp.x - thing->radius - viewx; + gy = interp.y - thing->radius - viewy; + } tz = FixedMul(gx, viewcos) + FixedMul(gy, viewsin); @@ -1620,8 +1629,16 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis) box->gx = tx; box->gy = tz; - box->scale = 2 * FixedMul(thing->radius, viewsin); - box->xscale = 2 * FixedMul(thing->radius, viewcos); + if (thing->flags & MF_PAPERCOLLISION) + { + box->scale = FixedMul(thing->radius, FCOS(viewangle - interp.angle)); + box->xscale = FixedMul(thing->radius, FSIN(viewangle - interp.angle)); + } + else + { + box->scale = 2 * FixedMul(thing->radius, viewsin); + box->xscale = 2 * FixedMul(thing->radius, viewcos); + } box->pz = interp.z; box->pzt = box->pz + box->thingheight; @@ -3746,6 +3763,12 @@ boolean R_ThingVisible (mobj_t *thing) if (r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing))) return false; + if (tic_t t = P_MobjIsReappearing(thing)) + { + // Flicker back in + return t <= 2*TICRATE && (leveltime & 1); + } + if ((viewssnum == 0 && (thing->renderflags & RF_DONTDRAWP1)) || (viewssnum == 1 && (thing->renderflags & RF_DONTDRAWP2)) || (viewssnum == 2 && (thing->renderflags & RF_DONTDRAWP3)) diff --git a/src/s_sound.c b/src/s_sound.c index c0bec85fc..33cf4c3be 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -426,6 +426,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) for (i = 0; i <= r_splitscreen; i++) { player_t *player = &players[displayplayers[i]]; + boolean camaway = false; memset(&listener[i], 0, sizeof (listener[i])); listenmobj[i] = NULL; @@ -442,9 +443,11 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) else { listenmobj[i] = player->mo; + if (player->exiting) + camaway = true; } - if (origin && origin == listenmobj[i] && !camera[i].freecam) + if (origin && origin == listenmobj[i] && !camera[i].freecam && !camaway) { itsUs = true; } @@ -763,6 +766,9 @@ void S_UpdateSounds(void) if (c->origin != listenmobj[i]) continue; + if (listenmobj[i]->player && listenmobj[i]->player->exiting) + continue; + itsUs = true; } @@ -942,6 +948,9 @@ boolean S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 if (!listener) return false; + if (source->thinker.function.acp1 == (actionf_p1)P_MobjThinker && P_MobjIsReappearing(source)) + return false; + // Init listensource with default listener listensource.x = listener->x; listensource.y = listener->y; diff --git a/src/sounds.c b/src/sounds.c index cbb5836a2..dcfa01cc3 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1119,6 +1119,10 @@ sfxinfo_t S_sfx[NUMSFX] = {"fshld2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flame Shield burst"}, {"fshld3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flame Shield cooldown"}, + // RR - Trick Panel + {"trick0", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Trick confirm"}, + {"trick1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Trick"}, + // RR - Ballhog Charge {"bhog00", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Ballhog charging"}, {"bhog01", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Ballhog charging"}, diff --git a/src/sounds.h b/src/sounds.h index a19d2b4ed..0d686b3c2 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1187,6 +1187,10 @@ typedef enum sfx_fshld2, sfx_fshld3, + // RR - Trick panels + sfx_trick0, + sfx_trick1, + // RR - Ballhog Charge sfx_bhog00, sfx_bhog01,