Merge remote-tracking branch 'origin/master' into fix-gravflip-shields

This commit is contained in:
AJ Martinez 2023-11-15 23:58:21 -07:00
commit d6dc6e3541
64 changed files with 3399 additions and 682 deletions

View file

@ -64,6 +64,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
p_tick.c p_tick.c
p_user.c p_user.c
p_slopes.c p_slopes.c
p_sweep.cpp
p_test.cpp
tables.c tables.c
r_bsp.cpp r_bsp.cpp
r_data.c r_data.c

View file

@ -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_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_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_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_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"); 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_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_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_dummyprofilekickstart = MenuDummy("dummyprofilekickstart", "Off").on_off();
consvar_t cv_dummyprofilename = MenuDummy("dummyprofilename", ""); consvar_t cv_dummyprofilename = MenuDummy("dummyprofilename", "");
consvar_t cv_dummyprofileplayername = MenuDummy("dummyprofileplayername", ""); consvar_t cv_dummyprofileplayername = MenuDummy("dummyprofileplayername", "");
@ -1011,6 +1013,13 @@ consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS] = {
Player("autoroulette4", "Off").on_off().onchange(weaponPrefChange4), 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] = { consvar_t cv_cam_dist[MAXSPLITSCREENPLAYERS] = {
Player("cam_dist", "190").floating_point(), Player("cam_dist", "190").floating_point(),
Player("cam2_dist", "190").floating_point(), Player("cam2_dist", "190").floating_point(),

View file

@ -2540,7 +2540,7 @@ void CL_ClearPlayer(INT32 playernum)
} }
#define PlayerPointerRemove(field) \ #define PlayerPointerRemove(field) \
if (field) \ if (P_MobjWasRemoved(field) == false) \
{ \ { \
P_RemoveMobj(field); \ P_RemoveMobj(field); \
P_SetTarget(&field, NULL); \ P_SetTarget(&field, NULL); \
@ -2550,7 +2550,8 @@ void CL_ClearPlayer(INT32 playernum)
PlayerPointerRemove(players[playernum].mo); PlayerPointerRemove(players[playernum].mo);
PlayerPointerRemove(players[playernum].followmobj); PlayerPointerRemove(players[playernum].followmobj);
PlayerPointerRemove(players[playernum].stumbleIndicator); PlayerPointerRemove(players[playernum].stumbleIndicator);
PlayerPointerRemove(players[playernum].sliptideZipIndicator); PlayerPointerRemove(players[playernum].wavedashIndicator);
PlayerPointerRemove(players[playernum].trickIndicator);
#undef PlayerPointerRemove #undef PlayerPointerRemove
@ -5618,7 +5619,7 @@ static INT16 Consistancy(void)
if (TypeIsNetSynced(mo->type) == false) if (TypeIsNetSynced(mo->type) == false)
continue; 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->type;
ret += mo->x; ret += mo->x;

View file

@ -1229,6 +1229,7 @@ enum {
WP_KICKSTARTACCEL = 1<<0, WP_KICKSTARTACCEL = 1<<0,
WP_SHRINKME = 1<<1, WP_SHRINKME = 1<<1,
WP_AUTOROULETTE = 1<<2, WP_AUTOROULETTE = 1<<2,
WP_LITESTEER = 1<<3,
}; };
void WeaponPref_Send(UINT8 ssplayer) void WeaponPref_Send(UINT8 ssplayer)
@ -1241,6 +1242,9 @@ void WeaponPref_Send(UINT8 ssplayer)
if (cv_autoroulette[ssplayer].value) if (cv_autoroulette[ssplayer].value)
prefs |= WP_AUTOROULETTE; prefs |= WP_AUTOROULETTE;
if (cv_litesteer[ssplayer].value)
prefs |= WP_LITESTEER;
if (cv_shrinkme[ssplayer].value) if (cv_shrinkme[ssplayer].value)
prefs |= WP_SHRINKME; prefs |= WP_SHRINKME;
@ -1259,6 +1263,9 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum)
if (player->pflags & PF_AUTOROULETTE) if (player->pflags & PF_AUTOROULETTE)
prefs |= WP_AUTOROULETTE; prefs |= WP_AUTOROULETTE;
if (player->pflags & PF_LITESTEER)
prefs |= WP_LITESTEER;
if (player->pflags & PF_SHRINKME) if (player->pflags & PF_SHRINKME)
prefs |= WP_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... // Let's just guess so we don't have to specify the gametype EVERY time...
newgametype = G_GuessGametypeByTOL(mapheaderinfo[newmapnum-1]->typeoflevel); 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)); CONS_Alert(CONS_WARNING, M_GetText("%s (%s) doesn't support any known gametype!\n"), realmapname, G_BuildMapName(newmapnum));
Z_Free(realmapname); Z_Free(realmapname);
Z_Free(mapname); Z_Free(mapname);
return; return;
} }
if (newgametype == -1)
newgametype = GT_RACE; // sensible default
} }
} }

View file

@ -64,6 +64,14 @@ typedef enum
PST_REBORN PST_REBORN
} playerstate_t; } 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 // Player internal flags
// //
@ -95,11 +103,9 @@ typedef enum
PF_RINGLOCK = 1<<13, // Prevent picking up rings while SPB is locked on 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_LITESTEER = 1<<14, // Hold Down to shallow turn (digital only)
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? //15-17 free, was previously itemflags stuff
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_DRIFTINPUT = 1<<18, // Drifting! PF_DRIFTINPUT = 1<<18, // Drifting!
PF_GETSPARKS = 1<<19, // Can get sparks PF_GETSPARKS = 1<<19, // Can get sparks
@ -261,6 +267,16 @@ typedef enum
TRIPWIRE_BLASTER, TRIPWIRE_BLASTER,
} tripwirepass_t; } tripwirepass_t;
typedef enum
{
TRICKSTATE_NONE = 0,
TRICKSTATE_READY,
TRICKSTATE_FORWARD,
TRICKSTATE_RIGHT,
TRICKSTATE_LEFT,
TRICKSTATE_BACK,
} trickstate_t;
typedef enum typedef enum
{ {
// Unsynced, HUD or clientsided effects // Unsynced, HUD or clientsided effects
@ -502,6 +518,15 @@ typedef enum
BOT_ITEM_PR__MAX BOT_ITEM_PR__MAX
} botItemPriority_e; } 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 // player_t struct for loop state
typedef struct { typedef struct {
fixed_t radius; fixed_t radius;
@ -511,6 +536,7 @@ typedef struct {
vector2_t origin_shift; vector2_t origin_shift;
vector2_t shift; vector2_t shift;
boolean flip; boolean flip;
sonicloopcamvars_t camera;
} sonicloopvars_t; } sonicloopvars_t;
// player_t struct for power-ups // player_t struct for power-ups
@ -628,6 +654,7 @@ struct player_t
respawnvars_t respawn; // Respawn info respawnvars_t respawn; // Respawn info
mobj_t *ringShooter; // DEZ respawner object 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 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 startboost; // (0 to 125) - Boost you get from start of race
UINT8 dropdashboost; // Boost you get when holding A while respawning 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 UINT8 flamelength; // Flame Shield dash meter, number of segments
UINT16 ballhogcharge; // Ballhog charge up -- the higher this value, the more projectiles 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 UINT16 hyudorotimer; // Duration of the Hyudoro offroad effect itself
SINT8 stealingtimer; // if >0 you are stealing, if <0 you are being stolen from 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 confirmVictim; // Player ID that you dealt damage to
UINT8 confirmVictimDelay; // Delay before playing the sound 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 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. fixed_t trickboostpower; // Save the rough speed multiplier. Used for upwards tricks.
UINT8 trickboostdecay; // used to know how long you've waited 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) 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. UINT16 wavedash; // 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. UINT8 wavedashdelay; // 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 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; UINT8 lastsafelap;
mobj_t *stumbleIndicator; mobj_t *stumbleIndicator;
mobj_t *sliptideZipIndicator; mobj_t *wavedashIndicator;
mobj_t *trickIndicator;
mobj_t *whip; mobj_t *whip;
mobj_t *hand; mobj_t *hand;
mobj_t *flickyAttacker; 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. 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 markedfordeath;
boolean dotrickfx;
UINT8 ringboxdelay; // Delay until Ring Box auto-activates UINT8 ringboxdelay; // Delay until Ring Box auto-activates
UINT8 ringboxaward; // Where did we stop? UINT8 ringboxaward; // Where did we stop?
UINT8 itemflags; // holds IF_ flags (see itemflags_t)
fixed_t outrun; // Milky Way road effect fixed_t outrun; // Milky Way road effect
uint8_t public_key[PUBKEYLENGTH]; uint8_t public_key[PUBKEYLENGTH];

View file

@ -55,6 +55,7 @@ struct thinker_t
// killough 11/98: count of how many other objects reference // killough 11/98: count of how many other objects reference
// this one using pointers. Used for garbage collection. // this one using pointers. Used for garbage collection.
INT32 references; INT32 references;
boolean cachable;
#ifdef PARANOIA #ifdef PARANOIA
INT32 debug_mobjtype; INT32 debug_mobjtype;

View file

@ -3314,7 +3314,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_MAGICIANBOXTOP", "S_MAGICIANBOXTOP",
"S_MAGICIANBOXBOTTOM", "S_MAGICIANBOXBOTTOM",
"S_SLIPTIDEZIP", "S_WAVEDASH",
"S_INSTAWHIP", "S_INSTAWHIP",
"S_INSTAWHIP_RECHARGE1", "S_INSTAWHIP_RECHARGE1",
@ -3325,6 +3325,13 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_BLOCKRING", "S_BLOCKRING",
"S_BLOCKBODY", "S_BLOCKBODY",
"S_CHARGEAURA",
"S_CHARGEFALL",
"S_CHARGEFLICKER",
"S_CHARGESPARK",
"S_CHARGERELEASE",
"S_CHARGEEXTRA",
"S_SERVANTHAND", "S_SERVANTHAND",
"S_HORNCODE", "S_HORNCODE",
@ -3892,6 +3899,16 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_SMOOTHLANDING", "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 // DEZ Ring Shooter
"S_TIREGRABBER", "S_TIREGRABBER",
"S_RINGSHOOTER_SIDE", "S_RINGSHOOTER_SIDE",
@ -5538,7 +5555,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_MONITOR_SHARD", "MT_MONITOR_SHARD",
"MT_MAGICIANBOX", "MT_MAGICIANBOX",
"MT_SLIPTIDEZIP", "MT_WAVEDASH",
"MT_INSTAWHIP", "MT_INSTAWHIP",
"MT_INSTAWHIP_RECHARGE", "MT_INSTAWHIP_RECHARGE",
@ -5546,6 +5563,13 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_BLOCKRING", "MT_BLOCKRING",
"MT_BLOCKBODY", "MT_BLOCKBODY",
"MT_CHARGEAURA",
"MT_CHARGEFALL",
"MT_CHARGEFLICKER",
"MT_CHARGESPARK",
"MT_CHARGERELEASE",
"MT_CHARGEEXTRA",
"MT_SERVANTHAND", "MT_SERVANTHAND",
"MT_HORNCODE", "MT_HORNCODE",
@ -5649,6 +5673,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_TRIPWIREBOOST", "MT_TRIPWIREBOOST",
"MT_SMOOTHLANDING", "MT_SMOOTHLANDING",
"MT_TRICKINDICATOR",
"MT_SIDETRICK",
"MT_FORWARDTRICK",
"MT_TIREGRABBER", "MT_TIREGRABBER",
"MT_RINGSHOOTER", "MT_RINGSHOOTER",
@ -5987,13 +6014,13 @@ const char *const MOBJFLAG_LIST[] = {
"SLOPE", "SLOPE",
"MISSILE", "MISSILE",
"SPRING", "SPRING",
"MONITOR", "ELEMENTAL",
"NOTHINK", "NOTHINK",
"NOCLIPHEIGHT", "NOCLIPHEIGHT",
"ENEMY", "ENEMY",
"SCENERY", "SCENERY",
"PAIN", "PAIN",
"STICKY", "DONTPUNT",
"APPLYTERRAIN", "APPLYTERRAIN",
"NOCLIPTHING", "NOCLIPTHING",
"GRENADEBOUNCE", "GRENADEBOUNCE",
@ -6093,11 +6120,10 @@ const char *const PLAYERFLAG_LIST[] = {
"RINGLOCK", // Prevent picking up rings while SPB is locked on "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..? "LITESTEER", // Shallow digital turn with DOWN
"USERINGS", // Have to be not holding the item button to change from using rings to using items (or vice versa) - prevents weirdness "\x01", // Free
"ITEMOUT", // Are you holding an item out? "\x01", // Free
"EGGMANOUT", // Eggman mark held, separate from PF_ITEMOUT so it doesn't stop you from getting items "\x01", // Free
"HOLDREADY", // Hold button-style item is ready to activate
"DRIFTINPUT", // Drifting! "DRIFTINPUT", // Drifting!
"GETSPARKS", // Can get sparks "GETSPARKS", // Can get sparks

View file

@ -380,7 +380,7 @@ class TiccmdBuilder
// ugly with the current abstractions, though, and there's a fortunate trick here: // 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 // 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. // 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; cmd->turning /= 2;
} }

View file

@ -127,6 +127,7 @@ demoghost *ghosts = NULL;
#define DEMO_SHRINKME 0x04 #define DEMO_SHRINKME 0x04
#define DEMO_BOT 0x08 #define DEMO_BOT 0x08
#define DEMO_AUTOROULETTE 0x10 #define DEMO_AUTOROULETTE 0x10
#define DEMO_LITESTEER 0x20
// For demos // For demos
#define ZT_FWD 0x0001 #define ZT_FWD 0x0001
@ -1329,7 +1330,7 @@ readghosttic:
z = READFIXED(g->p); z = READFIXED(g->p);
angle = READANGLE(g->p); angle = READANGLE(g->p);
if (!(mobjinfo[type].flags & MF_SHOOTABLE) 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. || health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad.
continue; continue;
poof = P_SpawnMobj(x, y, z, MT_GHOST); poof = P_SpawnMobj(x, y, z, MT_GHOST);
@ -2480,6 +2481,8 @@ void G_BeginRecording(void)
i |= DEMO_KICKSTART; i |= DEMO_KICKSTART;
if (player->pflags & PF_AUTOROULETTE) if (player->pflags & PF_AUTOROULETTE)
i |= DEMO_AUTOROULETTE; i |= DEMO_AUTOROULETTE;
if (player->pflags & PF_LITESTEER)
i |= DEMO_LITESTEER;
if (player->pflags & PF_SHRINKME) if (player->pflags & PF_SHRINKME)
i |= DEMO_SHRINKME; i |= DEMO_SHRINKME;
if (player->bot == true) if (player->bot == true)
@ -3447,6 +3450,11 @@ void G_DoPlayDemo(const char *defdemoname)
else else
players[p].pflags &= ~PF_AUTOROULETTE; players[p].pflags &= ~PF_AUTOROULETTE;
if (flags & DEMO_LITESTEER)
players[p].pflags |= PF_LITESTEER;
else
players[p].pflags &= ~PF_LITESTEER;
if (flags & DEMO_SHRINKME) if (flags & DEMO_SHRINKME)
players[p].pflags |= PF_SHRINKME; players[p].pflags |= PF_SHRINKME;
else else

View file

@ -2127,7 +2127,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
totalring = players[player].totalring; totalring = players[player].totalring;
xtralife = players[player].xtralife; 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 // SRB2kart
memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette)); memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette));
@ -2145,7 +2145,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
{ {
rings = 0; rings = 0;
} }
else if (modeattacking & ATTACKING_SPB) else if (G_TimeAttackStart())
{ {
rings = 20; rings = 20;
} }
@ -2175,7 +2175,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
} }
else else
{ {
if (players[player].pflags & PF_ITEMOUT) if (players[player].itemflags & IF_ITEMOUT)
{ {
itemtype = 0; itemtype = 0;
itemamount = 0; itemamount = 0;
@ -2234,7 +2234,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
K_RemoveFollower(&players[player]); K_RemoveFollower(&players[player]);
#define PlayerPointerRemove(field) \ #define PlayerPointerRemove(field) \
if (field) \ if (P_MobjWasRemoved(field) == false) \
{ \ { \
P_RemoveMobj(field); \ P_RemoveMobj(field); \
P_SetTarget(&field, NULL); \ 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. // These are mostly subservient to the player, and may not clean themselves up.
PlayerPointerRemove(players[player].followmobj); PlayerPointerRemove(players[player].followmobj);
PlayerPointerRemove(players[player].stumbleIndicator); PlayerPointerRemove(players[player].stumbleIndicator);
PlayerPointerRemove(players[player].sliptideZipIndicator); PlayerPointerRemove(players[player].wavedashIndicator);
PlayerPointerRemove(players[player].trickIndicator);
#undef PlayerPointerRemove #undef PlayerPointerRemove

View file

@ -99,6 +99,7 @@ extern consvar_t cv_pauseifunfocused;
extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS];

View file

@ -558,7 +558,8 @@ char sprnames[NUMSPRITES + 1][5] =
"IMDB", // Item Monitor Small Shard (Debris) "IMDB", // Item Monitor Small Shard (Debris)
"MTWK", // Item Monitor Glass Twinkle "MTWK", // Item Monitor Glass Twinkle
"SLPT", // Sliptide zip indicator "SLPT", // Wavedash indicator
"TRBS", // Trickdash indicator
"IWHP", // Instawhip "IWHP", // Instawhip
"WPRE", // Instawhip Recharge "WPRE", // Instawhip Recharge
@ -566,6 +567,12 @@ char sprnames[NUMSPRITES + 1][5] =
"GRNG", // Guard ring "GRNG", // Guard ring
"GBDY", // Guard body "GBDY", // Guard body
"TRC1", // Charge aura
"TRC2", // Charge fall
"TRC3", // Charge flicker/sparks
"TRC4", // Charge release
"TRC5", // Charge extra
"DHND", // Servant Hand "DHND", // Servant Hand
"HORN", // Horncode "HORN", // Horncode
@ -631,6 +638,15 @@ char sprnames[NUMSPRITES + 1][5] =
"TWBT", // Tripwire BLASTER "TWBT", // Tripwire BLASTER
"SMLD", // Smooth landing "SMLD", // Smooth landing
// Trick Effects
"TRK1",
"TRK2",
"TRK3",
"TRK4",
"TRK5",
"TRK6",
"TRK7",
"TIRG", // Tire grabbers "TIRG", // Tire grabbers
"RSHT", // DEZ Ring Shooter "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_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_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_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 {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_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_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_DHND, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SERVANTHAND
{SPR_HORN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_HORNCODE {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_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_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_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 {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|1, 3, {NULL}, 0, 0, S_FLINGENERGY3}, // S_FLINGENERGY2,
{SPR_FLEN, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_NULL}, // S_FLINGENERGY3, {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_ADD, 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_ADD|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_ADD|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_ADD|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_ADD|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|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, 3, {NULL}, 0, 0, S_FIREDITEM2}, // S_FIREDITEM1
{SPR_PSHW, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_FIREDITEM3}, // S_FIREDITEM2 {SPR_PSHW, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_FIREDITEM3}, // S_FIREDITEM2
@ -5742,7 +5775,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
1000, // mass 1000, // mass
MT_THOK, // damage MT_THOK, // damage
sfx_None, // activesound 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 (statenum_t)MT_THOK // raisestate
}, },
@ -9327,7 +9360,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_mspogo, // deathsound sfx_mspogo, // deathsound
2*TICRATE, // speed 2*TICRATE, // speed
14*FRACUNIT, // radius 14*FRACUNIT, // radius
64*FRACUNIT, // height 90*FRACUNIT, // height
0, // display offset 0, // display offset
4, // mass 4, // mass
0, // damage 0, // damage
@ -9353,7 +9386,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_WALLSPIKED2, // xdeathstate S_WALLSPIKED2, // xdeathstate
sfx_mspogo, // deathsound sfx_mspogo, // deathsound
2*TICRATE, // speed 2*TICRATE, // speed
16*FRACUNIT, // radius 48*FRACUNIT, // radius
14*FRACUNIT, // height 14*FRACUNIT, // height
0, // display offset 0, // display offset
4, // mass 4, // mass
@ -9548,7 +9581,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_RING_ICON, // damage MT_RING_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9575,7 +9608,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_PITY_ICON, // damage MT_PITY_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9602,7 +9635,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_ATTRACT_ICON,// damage MT_ATTRACT_ICON,// damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9629,7 +9662,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_FORCE_ICON, // damage MT_FORCE_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9656,7 +9689,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_ARMAGEDDON_ICON, // damage MT_ARMAGEDDON_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9683,7 +9716,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_WHIRLWIND_ICON, // damage MT_WHIRLWIND_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9710,7 +9743,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_ELEMENTAL_ICON, // damage MT_ELEMENTAL_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9737,7 +9770,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_SNEAKERS_ICON, // damage MT_SNEAKERS_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9764,7 +9797,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_INVULN_ICON, // damage MT_INVULN_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9791,7 +9824,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_1UP_ICON, // damage MT_1UP_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9818,7 +9851,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_EGGMAN_ICON, // damage MT_EGGMAN_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9845,7 +9878,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_MIXUP_ICON, // damage MT_MIXUP_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9872,7 +9905,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_UNKNOWN, // damage MT_UNKNOWN, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9899,7 +9932,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_GRAVITY_ICON, // damage MT_GRAVITY_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9926,7 +9959,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_RECYCLER_ICON, // damage MT_RECYCLER_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9953,7 +9986,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_SCORE1K_ICON, // damage MT_SCORE1K_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -9980,7 +10013,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_SCORE10K_ICON, // damage MT_SCORE10K_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10007,7 +10040,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_FLAMEAURA_ICON, // damage MT_FLAMEAURA_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10034,7 +10067,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_BUBBLEWRAP_ICON, // damage MT_BUBBLEWRAP_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10061,7 +10094,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_THUNDERCOIN_ICON, // damage MT_THUNDERCOIN_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10088,7 +10121,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_PITY_ICON, // damage MT_PITY_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10115,7 +10148,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_ATTRACT_ICON,// damage MT_ATTRACT_ICON,// damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10142,7 +10175,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_FORCE_ICON, // damage MT_FORCE_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10169,7 +10202,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_ARMAGEDDON_ICON, // damage MT_ARMAGEDDON_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10196,7 +10229,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_WHIRLWIND_ICON, // damage MT_WHIRLWIND_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10223,7 +10256,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_ELEMENTAL_ICON, // damage MT_ELEMENTAL_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10250,7 +10283,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_SNEAKERS_ICON, // damage MT_SNEAKERS_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10277,7 +10310,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_INVULN_ICON, // damage MT_INVULN_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10304,7 +10337,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_EGGMAN_ICON, // damage MT_EGGMAN_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10331,7 +10364,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_GRAVITY_ICON, // damage MT_GRAVITY_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10358,7 +10391,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_FLAMEAURA_ICON, // damage MT_FLAMEAURA_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10385,7 +10418,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_BUBBLEWRAP_ICON, // damage MT_BUBBLEWRAP_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10412,7 +10445,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_THUNDERCOIN_ICON, // damage MT_THUNDERCOIN_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags MF_SOLID|MF_SHOOTABLE|MF_GRENADEBOUNCE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10439,7 +10472,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_RING_ICON, // damage MT_RING_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -10466,7 +10499,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
MT_RING_ICON, // damage MT_RING_ICON, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags MF_SOLID|MF_SHOOTABLE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -14618,13 +14651,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate S_NULL, // xdeathstate
sfx_None, // deathsound sfx_None, // deathsound
5*FRACUNIT, // speed 5*FRACUNIT, // speed
8*FRACUNIT, // radius 16*FRACUNIT, // radius
8*FRACUNIT, // height 32*FRACUNIT, // height
0, // display offset 0, // display offset
DMG_NORMAL, // mass DMG_NORMAL, // mass
0, // damage 0, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },
@ -23061,9 +23094,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_SLIPTIDEZIP { // MT_WAVEDASH
-1, // doomednum -1, // doomednum
S_SLIPTIDEZIP, // spawnstate S_WAVEDASH, // spawnstate
1000, // spawnhealth 1000, // spawnhealth
S_NULL, // seestate S_NULL, // seestate
sfx_None, // seesound sfx_None, // seesound
@ -23111,7 +23144,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
0, // damage 0, // damage
sfx_None, // activesound sfx_None, // activesound
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP|MF_DONTPUNT, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -23223,6 +23256,168 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate 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 { // MT_SERVANTHAND
-1, // doomednum -1, // doomednum
S_SERVANTHAND, // spawnstate S_SERVANTHAND, // spawnstate
@ -24002,7 +24197,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_peel, // activesound 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 S_NULL // raisestate
}, },
@ -24029,7 +24224,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },
@ -24083,7 +24278,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_s3k96, // activesound 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 S_NULL // raisestate
}, },
@ -24110,7 +24305,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },
@ -24137,7 +24332,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_s3kc0s, // activesound 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 S_NULL // raisestate
}, },
@ -24164,7 +24359,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },
@ -24218,7 +24413,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_s3k5c, // activesound 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 S_NULL // raisestate
}, },
@ -24245,7 +24440,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },
@ -24353,7 +24548,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass 0, // mass
0, // damage 0, // damage
sfx_s3k5c, // activesound 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 S_NULL // raisestate
}, },
@ -24380,7 +24575,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_s3k96, // activesound 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 S_NULL // raisestate
}, },
@ -24407,7 +24602,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },
@ -24461,7 +24656,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_DONTPUNT, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -24515,7 +24710,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_kc64, // activesound 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 S_NULL // raisestate
}, },
@ -24758,7 +24953,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
0, // damage 0, // damage
sfx_None, // activesound sfx_None, // activesound
MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags MF_NOCLIPTHING|MF_DONTENCOREMAP|MF_DONTPUNT, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -25055,7 +25250,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_s3k5c, // activesound sfx_s3k5c, // activesound
MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags MF_SHOOTABLE|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_DONTPUNT, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -25082,7 +25277,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },
@ -25136,7 +25331,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
1, // damage 1, // damage
sfx_s3k96, // activesound 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 S_NULL // raisestate
}, },
@ -25190,7 +25385,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass 0, // mass
0, // damage 0, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN, // flags MF_SPECIAL|MF_DONTENCOREMAP|MF_APPLYTERRAIN|MF_DONTPUNT, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -25329,6 +25524,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate 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 { // MT_TIREGRABBER
-1, // doomednum -1, // doomednum
S_TIREGRABBER, // spawnstate S_TIREGRABBER, // spawnstate
@ -30433,7 +30709,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // mass 0, // mass
0, // damage 0, // damage
sfx_None, // activesound 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 S_NULL // raisestate
}, },

View file

@ -1115,7 +1115,8 @@ typedef enum sprite
SPR_IMDB, // Item Monitor Small Shard (Debris) SPR_IMDB, // Item Monitor Small Shard (Debris)
SPR_MTWK, // Item Monitor Glass Twinkle SPR_MTWK, // Item Monitor Glass Twinkle
SPR_SLPT, // Sliptide zip indicator SPR_SLPT, // Wavedash indicator
SPR_TRBS, // Trickdash indicator
SPR_IWHP, // Instawhip SPR_IWHP, // Instawhip
SPR_WPRE, // Instawhip Recharge SPR_WPRE, // Instawhip Recharge
@ -1123,6 +1124,12 @@ typedef enum sprite
SPR_GRNG, // Guard ring SPR_GRNG, // Guard ring
SPR_GBDY, // Guard body 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_DHND, // Servant Hand
SPR_HORN, // Horncode SPR_HORN, // Horncode
@ -1188,6 +1195,15 @@ typedef enum sprite
SPR_TWBT, // Tripwire BLASTER SPR_TWBT, // Tripwire BLASTER
SPR_SMLD, // Smooth landing 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_TIRG, // Tire grabbers
SPR_RSHT, // DEZ Ring Shooter SPR_RSHT, // DEZ Ring Shooter
@ -4529,7 +4545,7 @@ typedef enum state
S_MAGICIANBOX_TOP, S_MAGICIANBOX_TOP,
S_MAGICIANBOX_BOTTOM, S_MAGICIANBOX_BOTTOM,
S_SLIPTIDEZIP, S_WAVEDASH,
S_INSTAWHIP, S_INSTAWHIP,
S_INSTAWHIP_RECHARGE1, S_INSTAWHIP_RECHARGE1,
@ -4540,6 +4556,13 @@ typedef enum state
S_BLOCKRING, S_BLOCKRING,
S_BLOCKBODY, S_BLOCKBODY,
S_CHARGEAURA,
S_CHARGEFALL,
S_CHARGEFLICKER,
S_CHARGESPARK,
S_CHARGERELEASE,
S_CHARGEEXTRA,
S_SERVANTHAND, S_SERVANTHAND,
S_HORNCODE, S_HORNCODE,
@ -5106,6 +5129,16 @@ typedef enum state
S_SMOOTHLANDING, 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 // DEZ Ring Shooter
S_TIREGRABBER, S_TIREGRABBER,
S_RINGSHOOTER_SIDE, S_RINGSHOOTER_SIDE,
@ -6791,7 +6824,7 @@ typedef enum mobj_type
MT_MONITOR_PART, MT_MONITOR_PART,
MT_MONITOR_SHARD, MT_MONITOR_SHARD,
MT_MAGICIANBOX, MT_MAGICIANBOX,
MT_SLIPTIDEZIP, MT_WAVEDASH,
MT_INSTAWHIP, MT_INSTAWHIP,
MT_INSTAWHIP_RECHARGE, MT_INSTAWHIP_RECHARGE,
@ -6799,6 +6832,13 @@ typedef enum mobj_type
MT_BLOCKRING, MT_BLOCKRING,
MT_BLOCKBODY, MT_BLOCKBODY,
MT_CHARGEAURA,
MT_CHARGEFALL,
MT_CHARGEFLICKER,
MT_CHARGESPARK,
MT_CHARGERELEASE,
MT_CHARGEEXTRA,
MT_SERVANTHAND, MT_SERVANTHAND,
MT_HORNCODE, MT_HORNCODE,
@ -6902,6 +6942,9 @@ typedef enum mobj_type
MT_TRIPWIREBOOST, MT_TRIPWIREBOOST,
MT_SMOOTHLANDING, MT_SMOOTHLANDING,
MT_TRICKINDICATOR,
MT_SIDETRICK,
MT_FORWARDTRICK,
MT_TIREGRABBER, MT_TIREGRABBER,
MT_RINGSHOOTER, MT_RINGSHOOTER,

View file

@ -1109,7 +1109,7 @@ static void K_BotTrick(player_t *player, ticcmd_t *cmd, const botcontroller_t *b
return; return;
} }
if (player->trickpanel == 1) if (player->trickpanel == TRICKSTATE_READY)
{ {
switch (botController->trick) switch (botController->trick)
{ {
@ -1521,7 +1521,7 @@ static void K_BuildBotTiccmdNormal(player_t *player, ticcmd_t *cmd)
// Actual gameplay behaviors below this block! // Actual gameplay behaviors below this block!
const botcontroller_t *botController = K_GetBotController(player->mo); const botcontroller_t *botController = K_GetBotController(player->mo);
if (player->trickpanel != 0) if (player->trickpanel != TRICKSTATE_NONE)
{ {
K_BotTrick(player, cmd, botController); K_BotTrick(player, cmd, botController);

View file

@ -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) 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; 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) static void K_BotItemGenericOrbitShield(player_t *player, ticcmd_t *cmd)
{ {
if (player->pflags & PF_ITEMOUT) if (player->itemflags & IF_ITEMOUT)
{ {
return; return;
} }
@ -811,7 +811,7 @@ static boolean K_BotRevealsEggbox(player_t *player)
--------------------------------------------------*/ --------------------------------------------------*/
static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd) static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd)
{ {
if (player->pflags & PF_EGGMANOUT) if (player->itemflags & IF_EGGMANOUT)
{ {
return; return;
} }
@ -1221,7 +1221,7 @@ static void K_BotItemBubble(player_t *player, ticcmd_t *cmd)
hold = true; hold = true;
} }
if (hold && (player->pflags & PF_HOLDREADY)) if (hold && (player->itemflags & IF_HOLDREADY))
{ {
cmd->buttons |= BT_ATTACK; cmd->buttons |= BT_ATTACK;
} }
@ -1245,7 +1245,7 @@ static void K_BotItemFlame(player_t *player, ticcmd_t *cmd)
{ {
player->botvars.itemconfirm--; player->botvars.itemconfirm--;
} }
else if (player->pflags & PF_HOLDREADY) else if (player->itemflags & IF_HOLDREADY)
{ {
INT32 flamemax = player->flamelength; 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) 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) if (player->rings > 0)
{ {
@ -1567,7 +1567,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
{ {
K_BotItemEggmanExplosion(player, cmd); K_BotItemEggmanExplosion(player, cmd);
} }
else if (player->pflags & PF_EGGMANOUT) else if (player->itemflags & IF_EGGMANOUT)
{ {
K_BotItemEggman(player, cmd); K_BotItemEggman(player, cmd);
} }
@ -1604,7 +1604,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
K_BotItemSneaker(player, cmd); K_BotItemSneaker(player, cmd);
break; break;
case KITEM_BANANA: case KITEM_BANANA:
if (!(player->pflags & PF_ITEMOUT)) if (!(player->itemflags & IF_ITEMOUT))
{ {
K_BotItemGenericTrapShield(player, cmd, turnamt, false); 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); K_BotItemEggmanShield(player, cmd);
break; break;
case KITEM_ORBINAUT: case KITEM_ORBINAUT:
if (!(player->pflags & PF_ITEMOUT)) if (!(player->itemflags & IF_ITEMOUT))
{ {
K_BotItemGenericOrbitShield(player, cmd); K_BotItemGenericOrbitShield(player, cmd);
} }
@ -1627,7 +1627,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
} }
break; break;
case KITEM_JAWZ: case KITEM_JAWZ:
if (!(player->pflags & PF_ITEMOUT)) if (!(player->itemflags & IF_ITEMOUT))
{ {
K_BotItemGenericOrbitShield(player, cmd); K_BotItemGenericOrbitShield(player, cmd);
} }
@ -1637,7 +1637,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
} }
break; break;
case KITEM_MINE: case KITEM_MINE:
if (!(player->pflags & PF_ITEMOUT)) if (!(player->itemflags & IF_ITEMOUT))
{ {
K_BotItemGenericTrapShield(player, cmd, turnamt, true); 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); K_BotItemBallhog(player, cmd);
break; break;
case KITEM_DROPTARGET: case KITEM_DROPTARGET:
if (!(player->pflags & PF_ITEMOUT)) if (!(player->itemflags & IF_ITEMOUT))
{ {
K_BotItemGenericTrapShield(player, cmd, turnamt, false); K_BotItemGenericTrapShield(player, cmd, turnamt, false);
} }

View file

@ -577,8 +577,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
} }
// Has held item shield // Has held item shield
else if (K_PlayerAttackSteer(thing, side, 20, else if (K_PlayerAttackSteer(thing, side, 20,
(thing->player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)), (thing->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)),
(g_nudgeSearch.botmo->player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)) (g_nudgeSearch.botmo->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT))
)) ))
{ {
break; break;

View file

@ -18,6 +18,7 @@
#include "k_podium.h" #include "k_podium.h"
#include "k_powerup.h" #include "k_powerup.h"
#include "k_hitlag.h" #include "k_hitlag.h"
#include "m_random.h"
angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) 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) if (t1->target->hnext == t1)
{ {
P_SetTarget(&t1->target->hnext, NULL); 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) else if (victim->type == MT_SUPER_FLICKY)
{ {
if (Obj_IsSuperFlickyWhippable(victim)) if (Obj_IsSuperFlickyWhippable(victim, attacker))
{ {
K_AddHitLag(victim, victimHitlag, true); K_AddHitLag(victim, victimHitlag, true);
K_AddHitLag(attacker, attackerHitlag, false); K_AddHitLag(attacker, attackerHitlag, false);
@ -1170,3 +1171,114 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
return false; 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_t>(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;
}

View file

@ -33,6 +33,9 @@ boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2);
boolean K_PvPTouchDamage(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 #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -221,7 +221,7 @@ private:
bool can_change() const bool can_change() const
{ {
if (viewplayer()->trickpanel > 0) if (viewplayer()->trickpanel != TRICKSTATE_NONE)
{ {
return false; return false;
} }

View file

@ -1453,7 +1453,7 @@ static void K_drawKartItem(void)
break; break;
} }
if ((stplyr->pflags & PF_ITEMOUT) && !(leveltime & 1)) if ((stplyr->itemflags & IF_ITEMOUT) && !(leveltime & 1))
localpatch[1] = kp_nodraw; localpatch[1] = kp_nodraw;
} }

File diff suppressed because it is too large Load diff

View file

@ -119,9 +119,11 @@ angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll);
void K_StumblePlayer(player_t *player); void K_StumblePlayer(player_t *player);
boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir); boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir);
void K_InitStumbleIndicator(player_t *player); 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_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); INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_DebtStingPlayer(player_t *player, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source);
void K_GiveBumpersToPlayer(player_t *player, player_t *victim, UINT8 amount); 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); 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 #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -979,6 +979,7 @@ extern consvar_t cv_dummyprofilename;
extern consvar_t cv_dummyprofileplayername; extern consvar_t cv_dummyprofileplayername;
extern consvar_t cv_dummyprofilekickstart; extern consvar_t cv_dummyprofilekickstart;
extern consvar_t cv_dummyprofileautoroulette; extern consvar_t cv_dummyprofileautoroulette;
extern consvar_t cv_dummyprofilelitesteer;
extern consvar_t cv_dummyprofilerumble; extern consvar_t cv_dummyprofilerumble;
void M_ResetOptions(void); void M_ResetOptions(void);

View file

@ -126,6 +126,11 @@ void Obj_BlockRingThink(mobj_t *ring);
void Obj_BlockBodyThink(mobj_t *body); void Obj_BlockBodyThink(mobj_t *body);
void Obj_GuardBreakThink(mobj_t *fx); 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 */ /* Ring Shooter */
boolean Obj_RingShooterThinker(mobj_t *mo); boolean Obj_RingShooterThinker(mobj_t *mo);
boolean Obj_PlayerRingShooterFreeze(player_t *const player); boolean Obj_PlayerRingShooterFreeze(player_t *const player);
@ -136,7 +141,7 @@ void Obj_UpdateRingShooterFace(mobj_t *part);
/* Follower Audience */ /* Follower Audience */
void Obj_AudienceInit(mobj_t * mobj, mapthing_t *mthing, INT32 followerpick); 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 */ /* Random Item Boxes */
void Obj_RandomItemVisuals(mobj_t *mobj); 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_SuperFlickyPlayerCollide(mobj_t *flicky, mobj_t *player);
void Obj_SuperFlickyLanding(mobj_t *flicky); void Obj_SuperFlickyLanding(mobj_t *flicky);
mobj_t *Obj_SuperFlickyOwner(const 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); boolean Obj_IsSuperFlickyTargettingYou(const mobj_t *flicky, mobj_t *player);
/* Battle/Power-UP UFO */ /* Battle/Power-UP UFO */

View file

@ -72,6 +72,7 @@ profile_t* PR_MakeProfile(
new->followercolor = fcol; new->followercolor = fcol;
new->kickstartaccel = false; new->kickstartaccel = false;
new->autoroulette = false; new->autoroulette = false;
new->litesteer = true;
// Copy from gamecontrol directly as we'll be setting controls up directly in the profile. // Copy from gamecontrol directly as we'll be setting controls up directly in the profile.
memcpy(new->controls, controlarray, sizeof(new->controls)); 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: // Player bound cvars:
new->kickstartaccel = cv_kickstartaccel[pnum].value; new->kickstartaccel = cv_kickstartaccel[pnum].value;
new->autoroulette = cv_autoroulette[pnum].value; new->autoroulette = cv_autoroulette[pnum].value;
new->litesteer = cv_litesteer[pnum].value;
new->rumble = cv_rumble[pnum].value; new->rumble = cv_rumble[pnum].value;
return new; return new;
@ -276,6 +278,7 @@ void PR_SaveProfiles(void)
// Consvars. // Consvars.
WRITEUINT8(save.p, profilesList[i]->kickstartaccel); WRITEUINT8(save.p, profilesList[i]->kickstartaccel);
WRITEUINT8(save.p, profilesList[i]->autoroulette); WRITEUINT8(save.p, profilesList[i]->autoroulette);
WRITEUINT8(save.p, profilesList[i]->litesteer);
WRITEUINT8(save.p, profilesList[i]->rumble); WRITEUINT8(save.p, profilesList[i]->rumble);
// Controls. // Controls.
@ -425,6 +428,17 @@ void PR_LoadProfiles(void)
profilesList[i]->autoroulette = (boolean)READUINT8(save.p); 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) if (version < 4)
{ {
profilesList[i]->rumble = true; profilesList[i]->rumble = true;
@ -478,6 +492,7 @@ static void PR_ApplyProfile_Settings(profile_t *p, UINT8 playernum)
// toggles // toggles
CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel); CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel);
CV_StealthSetValue(&cv_autoroulette[playernum], p->autoroulette); CV_StealthSetValue(&cv_autoroulette[playernum], p->autoroulette);
CV_StealthSetValue(&cv_litesteer[playernum], p->litesteer);
// set controls... // set controls...
memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault)); memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault));

View file

@ -31,7 +31,7 @@ extern "C" {
#define SKINNAMESIZE 16 #define SKINNAMESIZE 16
#define PROFILENAMELEN 6 #define PROFILENAMELEN 6
#define PROFILEVER 7 #define PROFILEVER 8
#define MAXPROFILES 16 #define MAXPROFILES 16
#define PROFILESFILE "ringprofiles.prf" #define PROFILESFILE "ringprofiles.prf"
#define PROFILE_GUEST 0 #define PROFILE_GUEST 0
@ -74,7 +74,8 @@ struct profile_t
// Player-specific consvars. // Player-specific consvars.
// @TODO: List all of those // @TODO: List all of those
boolean kickstartaccel; // cv_kickstartaccel boolean kickstartaccel; // cv_kickstartaccel
boolean autoroulette; // cv_autoroulette boolean autoroulette; // cv_autoroulette
boolean litesteer; // cv_litesteer
boolean rumble; // cv_rumble boolean rumble; // cv_rumble
// Finally, control data itself // Finally, control data itself

View file

@ -156,7 +156,9 @@ void K_DoIngameRespawn(player_t *player)
player->ringboost = 0; player->ringboost = 0;
player->driftboost = player->strongdriftboost = 0; player->driftboost = player->strongdriftboost = 0;
player->gateBoost = 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); K_TumbleInterrupt(player);
P_ResetPlayer(player); P_ResetPlayer(player);
@ -295,7 +297,7 @@ void K_DoIngameRespawn(player_t *player)
player->respawn.init = true; player->respawn.init = true;
player->respawn.fast = true; player->respawn.fast = true;
player->respawn.returnspeed = 0; player->respawn.returnspeed = 0;
player->laps = player->lastsafelap; player->laps = min(player->laps, player->lastsafelap);
player->respawn.airtimer = player->airtime; player->respawn.airtimer = player->airtime;
player->respawn.truedeath = !!(player->pflags & PF_FAULT); player->respawn.truedeath = !!(player->pflags & PF_FAULT);

View file

@ -192,7 +192,7 @@ static kartitems_t K_KartItemReelTimeAttack[] =
static kartitems_t K_KartItemReelSPBAttack[] = static kartitems_t K_KartItemReelSPBAttack[] =
{ {
KITEM_GACHABOM, KITEM_DROPTARGET,
KITEM_SUPERRING, KITEM_SUPERRING,
KITEM_NONE KITEM_NONE
}; };
@ -1242,7 +1242,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
return; return;
} }
if (K_TimeAttackRules() == true) if (K_TimeAttackRules() == true && !(modeattacking & ATTACKING_SPB))
{ {
// Time Attack rules; use a consistent speed. // Time Attack rules; use a consistent speed.
roulette->tics = roulette->speed = ROULETTE_SPEED_TIMEATTACK; 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. // 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. // 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. // 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) 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_itemblinkmode] = 1;
//player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT); //player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT);
if (P_IsDisplayPlayer(player)) if (K_IsPlayingDisplayPlayer(player))
{ {
S_StartSound(NULL, sfx_itrole); 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_itemblinkmode] = 0;
player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT); player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT);
if (P_IsDisplayPlayer(player)) if (K_IsPlayingDisplayPlayer(player))
{ {
if (roulette->ringbox) 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. // This makes the roulette produce the random noises.
roulette->sound = (roulette->sound + 1) % 8; roulette->sound = (roulette->sound + 1) % 8;
if (P_IsDisplayPlayer(player)) if (K_IsPlayingDisplayPlayer(player))
{ {
if (roulette->ringbox) if (roulette->ringbox)
S_StartSound(NULL, sfx_s240); S_StartSound(NULL, sfx_s240);

View file

@ -485,8 +485,6 @@ void K_ProcessTerrainEffect(mobj_t *mo)
fixed_t speed = FixedHypot(mo->momx, mo->momy); fixed_t speed = FixedHypot(mo->momx, mo->momy);
fixed_t upwards = 16 * terrain->trickPanel; fixed_t upwards = 16 * terrain->trickPanel;
player->trickpanel = 1;
player->pflags |= PF_TRICKDELAY;
K_DoPogoSpring(mo, upwards, 1); K_DoPogoSpring(mo, upwards, 1);
// Reduce speed // Reduce speed
@ -551,7 +549,7 @@ void K_ProcessTerrainEffect(mobj_t *mo)
P_InstaThrust(player->mo, thrustAngle, max(thrustSpeed, 2*playerSpeed)); P_InstaThrust(player->mo, thrustAngle, max(thrustSpeed, 2*playerSpeed));
player->dashpadcooldown = TICRATE/3; player->dashpadcooldown = TICRATE/3;
player->trickpanel = 0; player->trickpanel = TRICKSTATE_NONE;
player->floorboost = 2; player->floorboost = 2;
S_StartSound(player->mo, sfx_cdfm62); S_StartSound(player->mo, sfx_cdfm62);

View file

@ -106,7 +106,9 @@ enum mobj_e {
mobj_tid, mobj_tid,
mobj_special, mobj_special,
mobj_args, mobj_args,
mobj_stringargs mobj_stringargs,
mobj_reappear,
mobj_punt_ref,
}; };
static const char *const mobj_opt[] = { static const char *const mobj_opt[] = {
@ -194,6 +196,8 @@ static const char *const mobj_opt[] = {
"special", "special",
"args", "args",
"stringargs", "stringargs",
"reappear",
"punt_ref",
NULL}; 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]) #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: case mobj_stringargs:
LUA_PushUserdata(L, mo->thing_stringargs, META_THINGSTRINGARGS); LUA_PushUserdata(L, mo->thing_stringargs, META_THINGSTRINGARGS);
break; 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 default: // extra custom variables in Lua memory
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1)); I_Assert(lua_istable(L, -1));
@ -883,6 +898,18 @@ static int mobj_set(lua_State *L)
return NOSET; return NOSET;
case mobj_stringargs: case mobj_stringargs:
return NOSET; 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: default:
lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS);
I_Assert(lua_istable(L, -1)); I_Assert(lua_istable(L, -1));

View file

@ -233,6 +233,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->lastpickuptype); lua_pushinteger(L, plr->lastpickuptype);
else if (fastcmp(field,"airtime")) else if (fastcmp(field,"airtime"))
lua_pushinteger(L, plr->airtime); lua_pushinteger(L, plr->airtime);
else if (fastcmp(field,"lastairtime"))
lua_pushinteger(L, plr->lastairtime);
else if (fastcmp(field,"flashing")) else if (fastcmp(field,"flashing"))
lua_pushinteger(L, plr->flashing); lua_pushinteger(L, plr->flashing);
else if (fastcmp(field,"spinouttimer")) else if (fastcmp(field,"spinouttimer"))
@ -255,10 +257,14 @@ static int player_get(lua_State *L)
lua_pushboolean(L, plr->flipDI); lua_pushboolean(L, plr->flipDI);
else if (fastcmp(field,"markedfordeath")) else if (fastcmp(field,"markedfordeath"))
lua_pushboolean(L, plr->markedfordeath); lua_pushboolean(L, plr->markedfordeath);
else if (fastcmp(field,"dotrickfx"))
lua_pushboolean(L, plr->dotrickfx);
else if (fastcmp(field,"ringboxdelay")) else if (fastcmp(field,"ringboxdelay"))
lua_pushinteger(L, plr->ringboxdelay); lua_pushinteger(L, plr->ringboxdelay);
else if (fastcmp(field,"ringboxaward")) else if (fastcmp(field,"ringboxaward"))
lua_pushinteger(L, plr->ringboxaward); lua_pushinteger(L, plr->ringboxaward);
else if (fastcmp(field,"itemflags"))
lua_pushinteger(L, plr->itemflags);
else if (fastcmp(field,"drift")) else if (fastcmp(field,"drift"))
lua_pushinteger(L, plr->drift); lua_pushinteger(L, plr->drift);
else if (fastcmp(field,"driftcharge")) else if (fastcmp(field,"driftcharge"))
@ -323,12 +329,16 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->tripwireLeniency); lua_pushinteger(L, plr->tripwireLeniency);
else if (fastcmp(field,"tripwireReboundDelay")) else if (fastcmp(field,"tripwireReboundDelay"))
lua_pushinteger(L, plr->tripwireReboundDelay); lua_pushinteger(L, plr->tripwireReboundDelay);
else if (fastcmp(field,"sliptideZip")) else if (fastcmp(field,"wavedash"))
lua_pushinteger(L, plr->sliptideZip); lua_pushinteger(L, plr->wavedash);
else if (fastcmp(field,"sliptideZipDelay")) else if (fastcmp(field,"wavedashdelay"))
lua_pushinteger(L, plr->sliptideZipDelay); lua_pushinteger(L, plr->wavedashdelay);
else if (fastcmp(field,"sliptideZipBoost")) else if (fastcmp(field,"wavedashboost"))
lua_pushinteger(L, plr->sliptideZipBoost); 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")) else if (fastcmp(field,"lastsafelap"))
lua_pushinteger(L, plr->lastsafelap); lua_pushinteger(L, plr->lastsafelap);
else if (fastcmp(field,"instaWhipCharge")) else if (fastcmp(field,"instaWhipCharge"))
@ -383,6 +393,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->flamelength); lua_pushinteger(L, plr->flamelength);
else if (fastcmp(field,"ballhogcharge")) else if (fastcmp(field,"ballhogcharge"))
lua_pushinteger(L, plr->ballhogcharge); lua_pushinteger(L, plr->ballhogcharge);
else if (fastcmp(field,"ballhogtap"))
lua_pushinteger(L, plr->ballhogtap);
else if (fastcmp(field,"hyudorotimer")) else if (fastcmp(field,"hyudorotimer"))
lua_pushinteger(L, plr->hyudorotimer); lua_pushinteger(L, plr->hyudorotimer);
else if (fastcmp(field,"stealingtimer")) else if (fastcmp(field,"stealingtimer"))
@ -717,6 +729,8 @@ static int player_set(lua_State *L)
plr->airtime = luaL_checkinteger(L, 3); plr->airtime = luaL_checkinteger(L, 3);
else if (fastcmp(field,"airtime")) else if (fastcmp(field,"airtime"))
plr->airtime = luaL_checkinteger(L, 3); plr->airtime = luaL_checkinteger(L, 3);
else if (fastcmp(field,"lastairtime"))
plr->lastairtime = luaL_checkinteger(L, 3);
else if (fastcmp(field,"flashing")) else if (fastcmp(field,"flashing"))
plr->flashing = luaL_checkinteger(L, 3); plr->flashing = luaL_checkinteger(L, 3);
else if (fastcmp(field,"spinouttimer")) else if (fastcmp(field,"spinouttimer"))
@ -739,10 +753,14 @@ static int player_set(lua_State *L)
plr->flipDI = luaL_checkboolean(L, 3); plr->flipDI = luaL_checkboolean(L, 3);
else if (fastcmp(field,"markedfordeath")) else if (fastcmp(field,"markedfordeath"))
plr->markedfordeath = luaL_checkboolean(L, 3); plr->markedfordeath = luaL_checkboolean(L, 3);
else if (fastcmp(field,"dotrickfx"))
plr->dotrickfx = luaL_checkboolean(L, 3);
else if (fastcmp(field,"ringboxdelay")) else if (fastcmp(field,"ringboxdelay"))
plr->ringboxdelay = luaL_checkinteger(L, 3); plr->ringboxdelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"ringboxaward")) else if (fastcmp(field,"ringboxaward"))
plr->ringboxaward = luaL_checkinteger(L, 3); plr->ringboxaward = luaL_checkinteger(L, 3);
else if (fastcmp(field,"itemflags"))
plr->itemflags = luaL_checkinteger(L, 3);
else if (fastcmp(field,"drift")) else if (fastcmp(field,"drift"))
plr->drift = luaL_checkinteger(L, 3); plr->drift = luaL_checkinteger(L, 3);
else if (fastcmp(field,"driftcharge")) else if (fastcmp(field,"driftcharge"))
@ -807,12 +825,16 @@ static int player_set(lua_State *L)
plr->tripwireLeniency = luaL_checkinteger(L, 3); plr->tripwireLeniency = luaL_checkinteger(L, 3);
else if (fastcmp(field,"tripwireReboundDelay")) else if (fastcmp(field,"tripwireReboundDelay"))
plr->tripwireReboundDelay = luaL_checkinteger(L, 3); plr->tripwireReboundDelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"sliptideZip")) else if (fastcmp(field,"wavedash"))
plr->sliptideZip = luaL_checkinteger(L, 3); plr->wavedash = luaL_checkinteger(L, 3);
else if (fastcmp(field,"sliptideZipDelay")) else if (fastcmp(field,"wavedashdelay"))
plr->sliptideZipDelay = luaL_checkinteger(L, 3); plr->wavedashdelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"sliptideZipBoost")) else if (fastcmp(field,"wavedashboost"))
plr->sliptideZipBoost = luaL_checkinteger(L, 3); 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")) else if (fastcmp(field,"lastsafelap"))
plr->lastsafelap = luaL_checkinteger(L, 3); plr->lastsafelap = luaL_checkinteger(L, 3);
else if (fastcmp(field,"instaWhipCharge")) else if (fastcmp(field,"instaWhipCharge"))
@ -867,6 +889,8 @@ static int player_set(lua_State *L)
plr->flamelength = luaL_checkinteger(L, 3); plr->flamelength = luaL_checkinteger(L, 3);
else if (fastcmp(field,"ballhogcharge")) else if (fastcmp(field,"ballhogcharge"))
plr->ballhogcharge = luaL_checkinteger(L, 3); plr->ballhogcharge = luaL_checkinteger(L, 3);
else if (fastcmp(field,"ballhogtap"))
plr->ballhogtap = luaL_checkinteger(L, 3);
else if (fastcmp(field,"hyudorotimer")) else if (fastcmp(field,"hyudorotimer"))
plr->hyudorotimer = luaL_checkinteger(L, 3); plr->hyudorotimer = luaL_checkinteger(L, 3);
else if (fastcmp(field,"stealingtimer")) else if (fastcmp(field,"stealingtimer"))

107
src/math/fixed.hpp Normal file
View file

@ -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 <type_traits>
#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 <typename T, std::enable_if_t<std::is_floating_point_v<T>, 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 <typename T> \
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 <typename T> \
Fixed& operator op##=(const T& b) \
{ \
val_ op##= b; \
return *this; \
}
X(+)
X(-)
X(*)
X(/)
#undef X
private:
fixed_t val_;
};
template <>
struct Traits<Fixed>
{
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*/

102
src/math/line_equation.hpp Normal file
View file

@ -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 <typename T>
struct LineEquation
{
using vec2 = Vec2<T>;
using line_segment = LineSegment<T>;
// Fixed-point: shift value by this amount during
// multiplications and divisions to avoid overflows.
static constexpr std::enable_if_t<std::is_same_v<T, Fixed>, 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<Fixed>::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<Fixed>::m() const
{
return m_ * kF;
}
template <>
inline Fixed LineEquation<Fixed>::b() const
{
return b_ * kF;
}
template <>
inline Fixed LineEquation<Fixed>::y(Fixed x) const
{
return ((m_ * x) + b_) * kF;
}
template <>
inline LineEquation<Fixed>::vec2 LineEquation<Fixed>::intersect(const LineEquation& q) const
{
Fixed x = ((b_ - q.b_) / ((q.m_ - m_) * kF)) * kF;
return {x, y(x)};
}
template <typename T>
struct LineEquationX : LineEquation<T>
{
T x(T y) const { return (y - LineEquation<T>::b()) / LineEquation<T>::m(); }
};
template <>
struct LineEquationX<Fixed> : LineEquation<Fixed>
{
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*/

43
src/math/line_segment.hpp Normal file
View file

@ -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 <algorithm>
#include <utility>
#include "vec.hpp"
namespace srb2::math
{
template <typename T>
struct LineSegment
{
using vec2 = Vec2<T>;
using view = std::pair<const vec2&, const vec2&>;
vec2 a, b;
LineSegment(vec2 a_, vec2 b_) : a(a_), b(b_) {}
template <typename U>
LineSegment(const LineSegment<U>& 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*/

34
src/math/traits.hpp Normal file
View file

@ -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 <cmath>
#include <type_traits>
namespace srb2::math
{
template <typename T, typename = void>
struct Traits;
template <typename T>
struct Traits<T, std::enable_if_t<std::is_floating_point_v<T>>>
{
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*/

84
src/math/vec.hpp Normal file
View file

@ -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 <type_traits>
#include "traits.hpp"
namespace srb2::math
{
template <typename T>
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 <typename U>
Vec2(const Vec2<U>& b) : Vec2(b.x, b.y) {}
T magnitude() const { return Traits<T>::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 <typename>
struct is_vec2 : std::false_type {};
template <typename T>
struct is_vec2<Vec2<T>> : std::true_type {};
template <typename T>
inline constexpr bool is_vec2_v = is_vec2<T>::value;
#define X(op) \
template <typename T, typename U, std::enable_if_t<!is_vec2_v<T>, bool> = true> \
Vec2<T> operator op(const T& a, const Vec2<U>& b) \
{ \
return Vec2 {a} op Vec2<T> {b}; \
} \
template <typename T, typename U, std::enable_if_t<!is_vec2_v<U>, bool> = true> \
Vec2<U> operator op(const Vec2<T>& a, const U& b) \
{ \
return Vec2<U> {a} op Vec2 {b}; \
} \
X(+)
X(-)
X(*)
X(/)
#undef X
}; // namespace srb2::math
#endif/*math_vec_hpp*/

View file

@ -89,6 +89,7 @@ static void M_StartEditProfile(INT32 c)
CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername); CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername);
CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel); CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel);
CV_StealthSetValue(&cv_dummyprofileautoroulette, optionsmenu.profile->autoroulette); CV_StealthSetValue(&cv_dummyprofileautoroulette, optionsmenu.profile->autoroulette);
CV_StealthSetValue(&cv_dummyprofilelitesteer, optionsmenu.profile->litesteer);
CV_StealthSetValue(&cv_dummyprofilerumble, optionsmenu.profile->rumble); CV_StealthSetValue(&cv_dummyprofilerumble, optionsmenu.profile->rumble);
} }
else else
@ -97,6 +98,7 @@ static void M_StartEditProfile(INT32 c)
CV_StealthSet(&cv_dummyprofileplayername, ""); CV_StealthSet(&cv_dummyprofileplayername, "");
CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off
CV_StealthSetValue(&cv_dummyprofileautoroulette, 0); // off CV_StealthSetValue(&cv_dummyprofileautoroulette, 0); // off
CV_StealthSetValue(&cv_dummyprofilelitesteer, 1); // on
CV_StealthSetValue(&cv_dummyprofilerumble, 1); // on CV_StealthSetValue(&cv_dummyprofilerumble, 1); // on
} }

View file

@ -94,6 +94,9 @@ menuitem_t OPTIONS_ProfileControls[] = {
{IT_CONTROL | IT_CVAR, "AUTO ROULETTE", "Item roulette auto-stops on a random result.", {IT_CONTROL | IT_CVAR, "AUTO ROULETTE", "Item roulette auto-stops on a random result.",
NULL, {.cvar = &cv_dummyprofileautoroulette}, 0, 0}, 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", "", {IT_HEADER, "EXTRA", "",
NULL, {NULL}, 0, 0}, NULL, {NULL}, 0, 0},
@ -191,6 +194,7 @@ static void M_ProfileControlSaveResponse(INT32 choice)
// Save the profile // Save the profile
optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value; optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value;
optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value; optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value;
optionsmenu.profile->litesteer = cv_dummyprofilelitesteer.value;
optionsmenu.profile->rumble = cv_dummyprofilerumble.value; optionsmenu.profile->rumble = cv_dummyprofilerumble.value;
memcpy(&optionsmenu.profile->controls, optionsmenu.tempcontrols, sizeof(gamecontroldefault)); 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)); memcpy(&gamecontrol[belongsto], optionsmenu.tempcontrols, sizeof(gamecontroldefault));
CV_SetValue(&cv_kickstartaccel[belongsto], cv_dummyprofilekickstart.value); CV_SetValue(&cv_kickstartaccel[belongsto], cv_dummyprofilekickstart.value);
CV_SetValue(&cv_autoroulette[belongsto], cv_dummyprofileautoroulette.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); CV_SetValue(&cv_rumble[belongsto], cv_dummyprofilerumble.value);
} }

View file

@ -39,6 +39,7 @@ target_sources(SRB2SDL2 PRIVATE
wpzothers.c wpzothers.c
shadow.cpp shadow.cpp
ball-switch.cpp ball-switch.cpp
charge.c
) )
add_subdirectory(versus) add_subdirectory(versus)

View file

@ -29,6 +29,7 @@ Obj_AudienceInit
mapthing_t *mthing, mapthing_t *mthing,
INT32 followerpick) INT32 followerpick)
{ {
const boolean ourchoiceofvisuals = (followerpick < 0 || followerpick > numfollowers);
INT16 *reflist = NULL; INT16 *reflist = NULL;
INT16 tempreflist[MAXHEADERFOLLOWERS]; INT16 tempreflist[MAXHEADERFOLLOWERS];
UINT8 numref = 0; UINT8 numref = 0;
@ -36,9 +37,9 @@ Obj_AudienceInit
audience_mainstate(mobj) = S_NULL; audience_mainstate(mobj) = S_NULL;
// Pick follower // Pick follower
if (mthing != NULL) if (ourchoiceofvisuals == true)
{ {
if (mthing->thing_stringargs[0] != NULL) if (mthing != NULL && mthing->thing_stringargs[0] != NULL)
{ {
// From mapthing // From mapthing
char *stringcopy = Z_StrDup(mthing->thing_stringargs[0]); char *stringcopy = Z_StrDup(mthing->thing_stringargs[0]);
@ -56,11 +57,23 @@ Obj_AudienceInit
*c = ' '; *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); CONS_Alert(CONS_WARNING, "Mapthing %s: Follower \"%s\" is invalid!\n", sizeu1(mthing-mapthings), tok);
}
else
numref++;
tok = strtok(NULL, " ,"); 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); Z_Free(stringcopy);
reflist = tempreflist; reflist = tempreflist;
@ -81,8 +94,8 @@ Obj_AudienceInit
if (!numref || !reflist) if (!numref || !reflist)
{ {
// This is the one thing a user should definitely be told about. // Clean up after ourselves.
CONS_Alert(CONS_WARNING, "Mapthing %s: Follower audience has no valid followers to pick from!\n", sizeu1(mthing-mapthings)); P_RemoveMobj(mobj);
return; return;
} }
@ -137,11 +150,11 @@ Obj_AudienceInit
} }
// Handle colors // Handle colors
if (mthing != NULL) if (ourchoiceofvisuals == true)
{ {
UINT16 colorpick = SKINCOLOR_NONE; 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])) if (!stricmp("Random", mthing->thing_stringargs[1]))
{ {
@ -189,8 +202,17 @@ Obj_AudienceInit
void void
Obj_AudienceThink Obj_AudienceThink
( mobj_t * mobj, ( 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) if (audience_mainstate(mobj) == S_NULL)
{ {
// Uninitialised, don't do anything funny. // Uninitialised, don't do anything funny.
@ -315,15 +337,23 @@ Obj_AudienceThink
} }
else if (mobj->flags2 & MF2_OBJECTFLIP) else if (mobj->flags2 & MF2_OBJECTFLIP)
{ {
if (mobj->z + mobj->height >= mobj->ceilingz) landed = (mobj->z + mobj->height >= mobj->ceilingz);
{
mobj->momz = -audience_bobamp(mobj);
P_SetMobjState(mobj, audience_mainstate(mobj));
}
} }
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)); P_SetMobjState(mobj, audience_mainstate(mobj));
} }
} }

147
src/objects/charge.c Normal file
View file

@ -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)<<RF_TRANSSHIFT;
if (aura->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)<<RF_TRANSSHIFT;
// Visuals
charge->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)<<RF_TRANSSHIFT;
release->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)<<RF_TRANSSHIFT;
extra->rollangle += ANG30;
}

View file

@ -66,7 +66,7 @@ void Obj_RainbowDashRingSpawn(mobj_t *mobj)
void Obj_DashRingSetup(mobj_t *mobj, mapthing_t *mthing) 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]; const UINT8 additionalThrust = mthing->thing_args[1];
statenum_t ringState, overlayState; 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) static void RainbowDashRingLaunch(player_t *player, mobj_t *ring)
{ {
player->mo->eflags &= ~MFE_SPRUNG; player->mo->eflags &= ~MFE_SPRUNG;
player->trickpanel = 1;
player->pflags |= PF_TRICKDELAY;
K_DoPogoSpring(player->mo, 0, 0); K_DoPogoSpring(player->mo, 0, 0);
DashRingLaunch(player, ring); DashRingLaunch(player, ring);
} }

View file

@ -295,6 +295,7 @@ Obj_LoopEndpointCollide
{ {
player_t *player = toucher->player; player_t *player = toucher->player;
sonicloopvars_t *s = &player->loop; sonicloopvars_t *s = &player->loop;
sonicloopcamvars_t *cam = &s->camera;
mobj_t *anchor = end_anchor(end); mobj_t *anchor = end_anchor(end);
mobj_t *center = anchor ? anchor_center(anchor) : NULL; mobj_t *center = anchor ? anchor_center(anchor) : NULL;
@ -352,6 +353,30 @@ Obj_LoopEndpointCollide
s->flip = center_has_flip(center); 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 = player->speed =
3 * (player->speed + toucher->momz) / 2; 3 * (player->speed + toucher->momz) / 2;

View file

@ -121,10 +121,14 @@ void Obj_RandomItemVisuals(mobj_t *mobj)
// //
// Then extraval1 starts ticking up and triggers the transformation from Ringbox to Random Item. // 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 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++; 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 // Don't transform stuff that isn't a Ring Box, idiot
statenum_t boxstate = mobj->state - states; statenum_t boxstate = mobj->state - states;
if (boxstate < S_RINGBOX1 || boxstate > S_RINGBOX12) if (boxstate < S_RINGBOX1 || boxstate > S_RINGBOX12)

View file

@ -765,14 +765,14 @@ mobj_t *Obj_SuperFlickyOwner(const mobj_t* mobj)
{ {
const Flicky* x = static_cast<const Flicky*>(mobj); const Flicky* x = static_cast<const Flicky*>(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<const Flicky*>(mobj); const Flicky* x = static_cast<const Flicky*>(mobj);
return mobj == x->chasing() && !x->stunned(); return target == x->chasing() && !x->stunned();
} }
boolean Obj_IsSuperFlickyTargettingYou(const mobj_t* mobj, mobj_t *player) 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<const Flicky*>(mobj); const Flicky* x = static_cast<const Flicky*>(mobj);
return player == x->chasing(); return player == x->chasing();
} }

View file

@ -1017,7 +1017,6 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
} }
// Ignore popped monitors, too. // Ignore popped monitors, too.
if (node->m_thing->health == 0 // this only really applies for monitors 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)) || (node->m_thing->type == MT_RANDOMITEM))
continue; continue;
// Okay, we found something valid. // 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; fixed_t topheight = *rover->topheight;
mariothink_t *block; mariothink_t *block;
mobj_t *thing; mobj_t *thing;
fixed_t oldx = 0, oldy = 0, oldz = 0;
I_Assert(puncher != NULL); I_Assert(puncher != NULL);
I_Assert(puncher->player != 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". S_StartSound(puncher, sfx_mario1); // "Thunk!" sound - puncher is "close enough".
else // Found something! else // Found something!
{ {
const boolean itsamonitor = (thing->flags & MF_MONITOR) == MF_MONITOR;
// create and initialize new elevator thinker // create and initialize new elevator thinker
block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL); 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, false);
R_CreateInterpolator_SectorPlane(&block->thinker, roversec, true); R_CreateInterpolator_SectorPlane(&block->thinker, roversec, true);
if (itsamonitor)
{
oldx = thing->x;
oldy = thing->y;
oldz = thing->z;
}
P_UnsetThingPosition(thing); P_UnsetThingPosition(thing);
thing->x = thing->old_x = sector->soundorg.x; thing->x = thing->old_x = sector->soundorg.x;
thing->y = thing->old_y = sector->soundorg.y; 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 // "Powerup rise" sound
S_StartSound(puncher, sfx_mario9); // Puncher is "close enough" 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);
}
} }
} }

View file

@ -43,6 +43,7 @@
#include "k_hitlag.h" #include "k_hitlag.h"
#include "acs/interface.h" #include "acs/interface.h"
#include "k_powerup.h" #include "k_powerup.h"
#include "k_collide.h"
// CTF player names // CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" #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 if (player->itemRoulette.active == true
|| player->ringboxdelay > 0 || player->ringboxdelay > 0
|| (weapon != 3 && player->itemamount) || (weapon != 3 && player->itemamount)
|| (player->pflags & PF_ITEMOUT)) || (player->itemflags & IF_ITEMOUT))
return false; return false;
if (weapon == 3 && K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE) 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; target->momx = target->momy = target->momz = 0;
// SRB2kart // 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_ORBINAUT || target->type == MT_ORBINAUT_SHIELD
|| target->type == MT_JAWZ || target->type == MT_JAWZ_SHIELD || target->type == MT_JAWZ || target->type == MT_JAWZ_SHIELD
|| target->type == MT_BANANA || target->type == MT_BANANA_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 // I wish I knew a better way to do this
if (target->target && target->target->player && target->target->player->mo) if (target->target && target->target->player && target->target->player->mo)
{ {
if ((target->target->player->pflags & PF_EGGMANOUT) && target->type == MT_EGGMANITEM_SHIELD) if ((target->target->player->itemflags & IF_EGGMANOUT) && target->type == MT_EGGMANITEM_SHIELD)
target->target->player->pflags &= ~PF_EGGMANOUT; 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 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) || (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) if (!target->target->player->itemamount)
target->target->player->pflags &= ~PF_ITEMOUT; target->target->player->itemflags &= ~IF_ITEMOUT;
if (target->target->hnext == target) if (target->target->hnext == target)
P_SetTarget(&target->target->hnext, NULL); 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 killed by a player
if (source && source->player) if (source && source->player)
{ {
if (target->flags & MF_MONITOR || target->type == MT_RANDOMITEM) if (target->type == MT_RANDOMITEM)
{ {
P_SetTarget(&target->target, source); 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->roundscore = 0;
} }
target->player->trickpanel = 0; target->player->trickpanel = TRICKSTATE_NONE;
ACS_RunPlayerDeathScript(target->player); 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; 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); S_StartSound(target, sfx_mbs60);
P_AddBrokenPrison(target, inflictor, source); 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); P_SetPlayerMobjState(player->mo, player->mo->info->deathstate);
if (player->sliptideZipIndicator && !P_MobjWasRemoved(player->sliptideZipIndicator)) #define PlayerPointerRemove(field) \
P_RemoveMobj(player->sliptideZipIndicator); if (P_MobjWasRemoved(field) == false) \
if (player->stumbleIndicator && !P_MobjWasRemoved(player->stumbleIndicator)) { \
P_RemoveMobj(player->stumbleIndicator); P_RemoveMobj(field); \
P_SetTarget(&field, NULL); \
}
PlayerPointerRemove(player->stumbleIndicator);
PlayerPointerRemove(player->wavedashIndicator);
PlayerPointerRemove(player->trickIndicator);
#undef PlayerPointerRemove
if (type == DMG_TIMEOVER) if (type == DMG_TIMEOVER)
{ {
@ -2868,6 +2938,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
invincible = false; 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) if (invincible && type != DMG_STUMBLE && type != DMG_WHUMBLE)
{ {
const INT32 oldHitlag = target->hitlag; const INT32 oldHitlag = target->hitlag;
@ -3103,6 +3181,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
break; 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_STUMBLE && type != DMG_WHUMBLE)
{ {
if (type != DMG_STING) if (type != DMG_STING)
@ -3290,7 +3374,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
return; return;
// Have a shield? You get hit, but don't lose your rings! // 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; return;
// 20 is the maximum number of rings that can be taken from you at once - half the span of your counter // 20 is the maximum number of rings that can be taken from you at once - half the span of your counter

View file

@ -77,6 +77,7 @@ typedef enum
NUM_THINKERLISTS NUM_THINKERLISTS
} thinklistnum_t; /**< Thinker lists. */ } thinklistnum_t; /**< Thinker lists. */
extern thinker_t thlist[]; extern thinker_t thlist[];
extern mobj_t *mobjcache;
void P_InitThinkers(void); void P_InitThinkers(void);
void P_AddThinker(const thinklistnum_t n, thinker_t *thinker); 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 // so missiles don't explode against sky hack walls
line_t *ceilingline; line_t *ceilingline;
// set by PIT_CheckLine() for any line that stopped the PIT_CheckLine() // P_CheckPosition: this position blocks movement
// that is, for any line which is 'solid' boolean blocking;
line_t *blockingline;
// 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; extern tm_t tm;
@ -414,6 +422,7 @@ struct TryMoveResult_t
boolean success; boolean success;
line_t *line; line_t *line;
mobj_t *mo; mobj_t *mo;
vector2_t normal;
}; };
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *result); 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_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); 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_IsLineBlocking(const line_t *ld, const mobj_t *thing);
boolean P_IsLineTripWire(const line_t *ld); boolean P_IsLineTripWire(const line_t *ld);
boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); 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); void P_DeleteMobjStringArgs(mobj_t *mobj);
tic_t P_MobjIsReappearing(const mobj_t *mobj);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -37,6 +37,7 @@ void P_HaltPlayerOrbit(player_t *player)
player->mo->flags &= ~(MF_NOCLIPHEIGHT); player->mo->flags &= ~(MF_NOCLIPHEIGHT);
player->loop.radius = 0; player->loop.radius = 0;
player->loop.camera.exit_tic = leveltime;
} }
void P_ExitPlayerOrbit(player_t *player) void P_ExitPlayerOrbit(player_t *player)

View file

@ -427,12 +427,8 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{ {
if (spring->reactiontime == 0) if (spring->reactiontime == 0)
{ {
object->player->tricktime = 0; // Reset post-hitlag timer object->eflags &= ~MFE_SPRUNG; // needed to permit the following
// Setup the boost for potential upwards trick, at worse, make it your regular max speed. (boost = curr speed*1.25) K_DoPogoSpring(object, -vertispeed, 0); // negative so momz isn't modified
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;
} }
else else
{ {
@ -512,12 +508,6 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
if (spring->thing_args[1]) if (spring->thing_args[1])
{ {
if (object->player)
{
object->player->trickpanel = 1;
object->player->pflags |= PF_TRICKDELAY;
}
K_DoPogoSpring(object, 32<<FRACBITS, 0); K_DoPogoSpring(object, 32<<FRACBITS, 0);
} }
else else
@ -538,6 +528,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
static BlockItReturn_t PIT_CheckThing(mobj_t *thing) static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
{ {
fixed_t blockdist; fixed_t blockdist;
boolean damage = false;
if (tm.thing == NULL || P_MobjWasRemoved(tm.thing) == true) if (tm.thing == NULL || P_MobjWasRemoved(tm.thing) == true)
return BMIT_STOP; // func just popped our tm.thing, cannot continue. return BMIT_STOP; // func just popped our tm.thing, cannot continue.
@ -562,6 +553,10 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING)))
return BMIT_CONTINUE; return BMIT_CONTINUE;
// Thing is respawning
if (P_MobjIsReappearing(thing))
return BMIT_CONTINUE;
blockdist = thing->radius + tm.thing->radius; blockdist = thing->radius + tm.thing->radius;
if (abs(thing->x - tm.x) >= blockdist || abs(thing->y - tm.y) >= blockdist) 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)) && (tm.thing->type == MT_BLENDEYE_MAIN || tm.thing->type == MT_BLENDEYE_EYE || tm.thing->type == MT_BLENDEYE_PUYO))
return BMIT_CONTINUE; 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) if (thing->flags & MF_PAIN)
{ // Player touches painful thing sitting on the floor { // Player touches painful thing sitting on the floor
// see if it went over / under // 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))) if (P_DamageMobj(tm.thing, thing, thing, 1, damagetype) && (damagetype = (thing->info->mass>>8)))
S_StartSound(thing, damagetype); S_StartSound(thing, damagetype);
}
if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing)) if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing))
return BMIT_CONTINUE; return BMIT_CONTINUE;
damage = true;
}
} }
else if (tm.thing->flags & MF_PAIN && thing->player) else if (tm.thing->flags & MF_PAIN && thing->player)
{ // Painful thing splats player in the face { // 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))) if (P_DamageMobj(thing, tm.thing, tm.thing, 1, damagetype) && (damagetype = (tm.thing->info->mass>>8)))
S_StartSound(tm.thing, damagetype); S_StartSound(tm.thing, damagetype);
}
if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing)) if (P_MobjWasRemoved(tm.thing) || P_MobjWasRemoved(thing))
return BMIT_CONTINUE; return BMIT_CONTINUE;
damage = true;
}
} }
// check for skulls slamming into things // check for skulls slamming into things
@ -1216,7 +1198,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
} }
// missiles can hit other things // 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); UINT8 damagetype = (tm.thing->info->mass ^ DMG_WOMBO);
@ -1327,78 +1309,65 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
} }
// Sprite Spikes! // Sprite Spikes!
// Do not return because solidity code comes below. if ((tm.thing->type == MT_SPIKE || tm.thing->type == MT_WALLSPIKE) && (tm.thing->flags & MF_SOLID)) // spike pops up
if (tm.thing->type == MT_SPIKE && tm.thing->flags & MF_SOLID && thing->player) // moving spike rams into player?!
{ {
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) if (P_MobjFlip(thing) == P_MobjFlip(tm.thing))
&& 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_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) return BMIT_CONTINUE;
&& 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);
} }
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) return BMIT_CONTINUE;
&& tm.thing->z + tm.thing->height + tm.thing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale))
P_DamageMobj(tm.thing, thing, thing, 1, DMG_TUMBLE);
} }
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); P_DamageMobj(tm.thing, thing, thing, 1, DMG_TUMBLE);
} return BMIT_CONTINUE;
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.
} }
bottomz = thing->z;
topz = thing->z + thing->height;
if (thing->eflags & MFE_VERTICALFLIP)
bottomz -= FixedMul(FRACUNIT, thing->scale);
else else
topz += FixedMul(FRACUNIT, thing->scale); {
// Do not return because solidity code comes below.
if (tm.thing->z + tm.thing->height > bottomz // above bottom P_DamageMobj(tm.thing, thing, thing, 1, DMG_NORMAL);
&& 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);
} }
} }
@ -1626,7 +1595,10 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
if (tm.thing->z + tm.thing->height < thing->z) if (tm.thing->z + tm.thing->height < thing->z)
return BMIT_CONTINUE; // underneath return BMIT_CONTINUE; // underneath
K_KartSolidBounce(tm.thing, thing); if (!K_PuntCollide(thing, tm.thing))
{
K_KartSolidBounce(tm.thing, thing);
}
return BMIT_CONTINUE; return BMIT_CONTINUE;
} }
} }
@ -1770,7 +1742,6 @@ static BlockItReturn_t PIT_CheckCameraLine(line_t *ld)
// could be crossed in either order. // could be crossed in either order.
// this line is out of the if so upper and lower textures can be hit by a splat // 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 (!ld->backsector) // one sided line
{ {
if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, ld)) if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, ld))
@ -1841,6 +1812,22 @@ boolean P_IsLineTripWire(const line_t *ld)
return ld->tripwire; 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 // PIT_CheckLine
// Adjusts tm.floorz and tm.ceilingz as lines are contacted // 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. // could be crossed in either order.
// this line is out of the if so upper and lower textures can be hit by a splat // 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)) if (P_MobjWasRemoved(tm.thing))
return BMIT_CONTINUE; // one of them was removed??? return BMIT_CONTINUE; // one of them was removed???
if (shouldCollide == 1) 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) else if (shouldCollide == 2)
return BMIT_CONTINUE; // force no collide 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)) if (P_PointOnLineSide(tm.thing->x, tm.thing->y, ld))
return BMIT_CONTINUE; // don't hit the back side 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)) 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 // set openrange, opentop, openbottom
P_LineOpening(ld, tm.thing, &open); 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 // adjust floor / ceiling heights
if (open.ceiling < tm.ceilingz) 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; tm.bbox[BOXLEFT] = x - tm.thing->radius;
newsubsec = R_PointInSubsector(x, y); newsubsec = R_PointInSubsector(x, y);
tm.ceilingline = tm.blockingline = NULL; tm.ceilingline = NULL;
tm.blocking = false;
// The base floor / ceiling is from the subsector // The base floor / ceiling is from the subsector
// that contains the point. // 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. // Check things first, possibly picking things up.
// MF_NOCLIPTHING: used by camera to not be blocked by things // 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++) 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++; validcount++;
P_ClearTestLines();
// check lines // check lines
for (bx = xl; bx <= xh; bx++) for (bx = xl; bx <= xh; bx++)
{ {
for (by = yl; by <= yh; by++) for (by = yl; by <= yh; by++)
{ {
if (!P_BlockLinesIterator(bx, by, PIT_CheckLine)) P_BlockLinesIterator(bx, by, PIT_CheckLine);
{
blockval = false;
}
} }
} }
if (tm.blocking)
{
blockval = false;
}
if (result != NULL) if (result != NULL)
{ {
result->line = tm.blockingline; result->line = NULL;
result->mo = tm.hitthing; result->mo = tm.hitthing;
} }
else
{
P_ClearTestLines();
}
tm.sweep = false;
return blockval; return blockval;
} }
@ -2379,7 +2424,7 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
tm.bbox[BOXLEFT] = x - thiscam->radius; tm.bbox[BOXLEFT] = x - thiscam->radius;
newsubsec = R_PointInSubsector(x, y); newsubsec = R_PointInSubsector(x, y);
tm.ceilingline = tm.blockingline = NULL; tm.ceilingline = NULL;
mapcampointer = thiscam; mapcampointer = thiscam;
@ -2753,22 +2798,6 @@ fixed_t P_GetThingStepUp(mobj_t *thing, fixed_t destX, fixed_t destY)
return maxstep; 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 static boolean
increment_move increment_move
( mobj_t * thing, ( mobj_t * thing,
@ -2821,7 +2850,29 @@ increment_move
tryy = y; 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 return false; // solid wall or thing
} }
@ -3466,30 +3517,27 @@ static void P_HitSlideLine(line_t *ld)
// //
// HitBounceLine, for players // 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 movelen;
fixed_t x, y; 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); movelen = P_AproxDistance(tmxmove, tmymove);
if (slidemo->player && movelen < (15*mapobjectscale)) if (slidemo->player && movelen < (15*mapobjectscale))
movelen = (15*mapobjectscale); movelen = (15*mapobjectscale);
x = FixedMul(movelen, FINECOSINE(lineangle)); if (!ld)
y = FixedMul(movelen, FINESINE(lineangle)); {
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; tmxmove = x * 4;
tmymove = y * 4; tmymove = y * 4;
@ -3958,6 +4006,8 @@ papercollision:
static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result) static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
{ {
extern consvar_t cv_showgremlins;
fixed_t mmomx = 0, mmomy = 0; fixed_t mmomx = 0, mmomy = 0;
fixed_t oldmomx = mo->momx, oldmomy = mo->momy; fixed_t oldmomx = mo->momx, oldmomy = mo->momy;
@ -3982,8 +4032,23 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
slidemo = mo; slidemo = mo;
bestslideline = result->line; bestslideline = result->line;
if (bestslideline == NULL) if (bestslideline == NULL && cv_showgremlins.value)
return; {
// 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 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))); tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
} }
if (P_IsLineTripWire(bestslideline)) if (bestslideline && P_IsLineTripWire(bestslideline))
{ {
// TRIPWIRE CANNOT BE MADE NONBOUNCY // TRIPWIRE CANNOT BE MADE NONBOUNCY
K_ApplyTripWire(mo->player, TRIPSTATE_BLOCKED); K_ApplyTripWire(mo->player, TRIPSTATE_BLOCKED);
@ -4014,7 +4079,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
K_SpawnBumpEffect(mo); K_SpawnBumpEffect(mo);
} }
P_PlayerHitBounceLine(bestslideline); P_PlayerHitBounceLine(bestslideline, &result->normal);
mo->eflags |= MFE_JUSTBOUNCEDWALL; mo->eflags |= MFE_JUSTBOUNCEDWALL;
mo->momx = tmxmove; mo->momx = tmxmove;
@ -4022,7 +4087,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
mo->player->cmomx = tmxmove; mo->player->cmomx = tmxmove;
mo->player->cmomy = tmymove; 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)) 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.) 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; return BMIT_CONTINUE;
if ((thing->flags & (MF_MONITOR|MF_SHOOTABLE)) != MF_SHOOTABLE) if ((thing->flags & MF_SHOOTABLE) != MF_SHOOTABLE)
return BMIT_CONTINUE; return BMIT_CONTINUE;
dx = abs(thing->x - bombspot->x); dx = abs(thing->x - bombspot->x);

View file

@ -63,6 +63,8 @@ mobj_t *waypointcap = NULL;
// general purpose. // general purpose.
mobj_t *trackercap = NULL; mobj_t *trackercap = NULL;
mobj_t *mobjcache = NULL;
void P_InitCachedActions(void) void P_InitCachedActions(void)
{ {
actioncachehead.prev = actioncachehead.next = &actioncachehead; actioncachehead.prev = actioncachehead.next = &actioncachehead;
@ -1147,7 +1149,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
P_PlayerFlip(mo); P_PlayerFlip(mo);
} }
if (mo->player->trickpanel >= 2) if (mo->player->trickpanel > TRICKSTATE_READY)
{ {
gravityadd = (5*gravityadd)/2; gravityadd = (5*gravityadd)/2;
} }
@ -1276,6 +1278,10 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
} }
break; break;
} }
case MT_RANDOMAUDIENCE:
if (mo->fuse)
gravityadd /= 10;
break;
default: default:
break; break;
} }
@ -1669,7 +1675,7 @@ void P_XYMovement(mobj_t *mo)
// blocked move // blocked move
moved = false; moved = false;
if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, tm.blockingline)) if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, result.line))
{ {
if (P_MobjWasRemoved(mo)) if (P_MobjWasRemoved(mo))
return; return;
@ -1677,7 +1683,7 @@ void P_XYMovement(mobj_t *mo)
else if (P_MobjWasRemoved(mo)) else if (P_MobjWasRemoved(mo))
return; return;
P_PushSpecialLine(tm.blockingline, mo); P_PushSpecialLine(result.line, mo);
if (mo->flags & MF_MISSILE) if (mo->flags & MF_MISSILE)
{ {
@ -1719,25 +1725,6 @@ void P_XYMovement(mobj_t *mo)
P_ExplodeMissile(mo); P_ExplodeMissile(mo);
return; 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 else
{ {
boolean walltransferred = false; boolean walltransferred = false;
@ -5692,12 +5679,12 @@ static void P_FlameJetSceneryThink(mobj_t *mobj)
flame->angle = mobj->angle; flame->angle = mobj->angle;
if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side 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 else
flame->angle += FixedAngle(mobj->fuse<<FRACBITS); flame->angle += FixedAngle(mobj->fuse<<FRACBITS);
strength = 20*FRACUNIT; strength = 20*mapobjectscale;
strength -= ((20*FRACUNIT)/16)*mobj->movedir; strength -= ((20*mapobjectscale)/16)*mobj->movedir;
P_InstaThrust(flame, flame->angle, strength); P_InstaThrust(flame, flame->angle, strength);
S_StartSound(flame, sfx_fire); 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); flame = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_FLAMEJETFLAME);
strength = 20*FRACUNIT; strength = 20*mapobjectscale;
strength -= ((20*FRACUNIT)/16)*mobj->movedir; strength -= ((20*mapobjectscale)/16)*mobj->movedir;
// If deaf'd, the object spawns on the ceiling. // If deaf'd, the object spawns on the ceiling.
if (mobj->flags2 & MF2_AMBUSH) if (mobj->flags2 & MF2_AMBUSH)
@ -5742,7 +5729,7 @@ static void P_VerticalFlameJetSceneryThink(mobj_t *mobj)
P_SetMobjState(flame, S_FLAMEJETFLAME7); 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); S_StartSound(flame, sfx_fire);
} }
@ -6469,7 +6456,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
break; break;
} }
if (mobj->target->player->pflags & PF_ITEMOUT) if (mobj->target->player->itemflags & IF_ITEMOUT)
{ {
if (leveltime & 1) if (leveltime & 1)
mobj->tracer->renderflags &= ~RF_DONTDRAW; mobj->tracer->renderflags &= ~RF_DONTDRAW;
@ -7268,7 +7255,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
if (mobj->flags2 & MF2_STRONGBOX) if (mobj->flags2 & MF2_STRONGBOX)
{ {
Obj_AudienceThink(mobj, true); Obj_AudienceThink(mobj, true, false);
if (P_MobjWasRemoved(mobj)) if (P_MobjWasRemoved(mobj))
return false; return false;
} }
@ -8408,10 +8395,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
return true; return true;
} }
mobj->extravalue1 += 1;
mobj->angle += ANG1*mobj->extravalue1; mobj->angle += ANG1*mobj->extravalue1;
P_SetScale(mobj, mobj->target->scale); mobj->extravalue1 += 1;
P_InstaScale(mobj, mobj->target->scale);
destx = mobj->target->x; destx = mobj->target->x;
desty = mobj->target->y; 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)); destx += FixedMul(mobj->radius*2, FINECOSINE((mobj->angle+ANGLE_90) >> ANGLETOFINESHIFT));
desty += FixedMul(mobj->radius*2, FINESINE((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 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 // Necessary to "ride" on Garden Top
zoff += mobj->target->sprzoff; 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) if (mobj->flags2 & MF2_AMBUSH)
{ {
P_SetOrigin(mobj, destx, desty, mobj->target->z + zoff); 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); 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; break;
} }
case MT_LIGHTNINGSHIELD: case MT_LIGHTNINGSHIELD:
@ -8688,6 +8838,26 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
Obj_BlockBodyThink(mobj); Obj_BlockBodyThink(mobj);
break; 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: case MT_GUARDBREAK:
{ {
Obj_GuardBreakThink(mobj); 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) static boolean P_CanFlickerFuse(mobj_t *mobj)
{ {
switch (mobj->type) switch (mobj->type)
@ -10163,11 +10291,6 @@ static boolean P_FuseThink(mobj_t *mobj)
if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)) || P_MobjWasRemoved(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) else switch (mobj->type)
{ {
// gargoyle and snowman handled in P_PushableThinker, not here // gargoyle and snowman handled in P_PushableThinker, not here
@ -10312,6 +10435,8 @@ void P_MobjThinker(mobj_t *mobj)
P_SetTarget(&mobj->hprev, NULL); P_SetTarget(&mobj->hprev, NULL);
if (mobj->itnext && P_MobjWasRemoved(mobj->itnext)) if (mobj->itnext && P_MobjWasRemoved(mobj->itnext))
P_SetTarget(&mobj->itnext, NULL); 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) if (mobj->flags & MF_NOTHINK)
return; return;
@ -10808,7 +10933,9 @@ void P_SceneryThinker(mobj_t *mobj)
if (mobj->type == MT_RANDOMAUDIENCE) 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) switch(mobj->type)
{ {
case MT_FLAMEJETFLAME:
return 3*FRACUNIT;
case MT_ITEMCLASH:
return 2*FRACUNIT;
case MT_SPECIALSTAGEARCH: case MT_SPECIALSTAGEARCH:
return 5*FRACUNIT; return 5*FRACUNIT;
case MT_SPECIALSTAGEBOMB: 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; 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. // this is officially a mobj, declared as soon as possible.
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; 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); mobj->y + FINESINE((ang>>ANGLETOFINESHIFT) & FINEMASK), mobj->z, MT_DAYTONAPINETREE_SIDE);
side->angle = ang; side->angle = ang;
P_SetTarget(&side->target, mobj); P_SetTarget(&side->target, mobj);
P_SetTarget(&side->punt_ref, mobj);
side->threshold = i; side->threshold = i;
} }
break; break;
@ -11850,6 +11991,7 @@ void P_RemoveMobj(mobj_t *mobj)
} }
P_SetTarget(&mobj->itnext, NULL); P_SetTarget(&mobj->itnext, NULL);
P_SetTarget(&mobj->punt_ref, NULL);
P_RemoveThingTID(mobj); P_RemoveThingTID(mobj);
P_DeleteMobjStringArgs(mobj); P_DeleteMobjStringArgs(mobj);
@ -11861,7 +12003,9 @@ void P_RemoveMobj(mobj_t *mobj)
INT32 prevreferences; INT32 prevreferences;
if (!mobj->thinker.references) 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; return;
} }
@ -12450,8 +12594,8 @@ void P_SpawnPlayer(INT32 playernum)
p->griefValue = 0; p->griefValue = 0;
K_InitStumbleIndicator(p); K_InitStumbleIndicator(p);
K_InitWavedashIndicator(p);
K_InitSliptideZipIndicator(p); K_InitTrickIndicator(p);
if (gametyperules & GTR_ITEMARROWS) 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. // Use per-thing collision for spikes unless the intangible flag is checked.
if (!(mthing->thing_args[2] & TMSF_INTANGIBLE) && !metalrecording) 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); P_UnsetThingPosition(mobj);
mobj->x += x;
mobj->y += y;
mobj->flags &= ~(MF_NOBLOCKMAP | MF_NOCLIPHEIGHT); mobj->flags &= ~(MF_NOBLOCKMAP | MF_NOCLIPHEIGHT);
mobj->flags |= MF_SOLID; mobj->flags |= MF_SOLID;
P_SetThingPosition(mobj); P_SetThingPosition(mobj);
@ -15423,3 +15576,9 @@ void P_DeleteMobjStringArgs(mobj_t *mobj)
mobj->script_stringargs[i] = NULL; 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);
}

View file

@ -137,8 +137,8 @@ typedef enum
MF_MISSILE = 1<<15, MF_MISSILE = 1<<15,
// Item is a spring. // Item is a spring.
MF_SPRING = 1<<16, MF_SPRING = 1<<16,
// Item box // Object is elemental. If it is punted, it will evaporate.
MF_MONITOR = 1<<17, MF_ELEMENTAL = 1<<17,
// Don't run the thinker for this object. // Don't run the thinker for this object.
MF_NOTHINK = 1<<18, MF_NOTHINK = 1<<18,
// Don't adjust z if below or above floorz/ceilingz // Don't adjust z if below or above floorz/ceilingz
@ -149,8 +149,8 @@ typedef enum
MF_SCENERY = 1<<21, MF_SCENERY = 1<<21,
// Painful (shit hurts). // Painful (shit hurts).
MF_PAIN = 1<<22, MF_PAIN = 1<<22,
// This mobj will stick to any surface or solid object it touches. // Object cannot be punted by invincible players. (Default CAN be punted, if it deals damage or is solid.)
MF_STICKY = 1<<23, MF_DONTPUNT = 1<<23,
// Object uses terrain effects. (Overlays, footsteps, etc) // Object uses terrain effects. (Overlays, footsteps, etc)
MF_APPLYTERRAIN = 1<<24, MF_APPLYTERRAIN = 1<<24,
// for chase camera, don't be blocked by things (partial clipping) // for chase camera, don't be blocked by things (partial clipping)
@ -437,6 +437,14 @@ struct mobj_t
boolean frozen; 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. // WARNING: New fields must be added separately to savegame and Lua.
}; };

View file

@ -65,7 +65,7 @@ savedata_cup_t cupsavedata;
#define ARCHIVEBLOCK_RNG 0x7FAAB5BD #define ARCHIVEBLOCK_RNG 0x7FAAB5BD
// Note: This cannot be bigger // Note: This cannot be bigger
// than an UINT16 // than an UINT16 (for now)
typedef enum typedef enum
{ {
AWAYVIEW = 0x0001, AWAYVIEW = 0x0001,
@ -75,12 +75,13 @@ typedef enum
SKYBOXCENTER = 0x0010, SKYBOXCENTER = 0x0010,
HOVERHYUDORO = 0x0020, HOVERHYUDORO = 0x0020,
STUMBLE = 0x0040, STUMBLE = 0x0040,
SLIPTIDEZIP = 0x0080, WAVEDASH = 0x0080,
RINGSHOOTER = 0x0100, RINGSHOOTER = 0x0100,
WHIP = 0x0200, WHIP = 0x0200,
HAND = 0x0400, HAND = 0x0400,
FLICKYATTACKER = 0x0800, FLICKYATTACKER = 0x0800,
FLICKYCONTROLLER = 0x1000, FLICKYCONTROLLER = 0x1000,
TRICKINDICATOR = 0x2000,
} player_saveflags; } player_saveflags;
static inline void P_ArchivePlayer(savebuffer_t *save) static inline void P_ArchivePlayer(savebuffer_t *save)
@ -310,8 +311,11 @@ static void P_NetArchivePlayers(savebuffer_t *save)
if (players[i].stumbleIndicator) if (players[i].stumbleIndicator)
flags |= STUMBLE; flags |= STUMBLE;
if (players[i].sliptideZipIndicator) if (players[i].wavedashIndicator)
flags |= SLIPTIDEZIP; flags |= WAVEDASH;
if (players[i].trickIndicator)
flags |= TRICKINDICATOR;
if (players[i].whip) if (players[i].whip)
flags |= WHIP; flags |= WHIP;
@ -348,8 +352,11 @@ static void P_NetArchivePlayers(savebuffer_t *save)
if (flags & STUMBLE) if (flags & STUMBLE)
WRITEUINT32(save->p, players[i].stumbleIndicator->mobjnum); WRITEUINT32(save->p, players[i].stumbleIndicator->mobjnum);
if (flags & SLIPTIDEZIP) if (flags & WAVEDASH)
WRITEUINT32(save->p, players[i].sliptideZipIndicator->mobjnum); WRITEUINT32(save->p, players[i].wavedashIndicator->mobjnum);
if (flags & TRICKINDICATOR)
WRITEUINT32(save->p, players[i].trickIndicator->mobjnum);
if (flags & WHIP) if (flags & WHIP)
WRITEUINT32(save->p, players[i].whip->mobjnum); 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].currentwaypoint));
WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].nextwaypoint));
WRITEUINT32(save->p, players[i].airtime); WRITEUINT32(save->p, players[i].airtime);
WRITEUINT32(save->p, players[i].lastairtime);
WRITEUINT8(save->p, players[i].startboost); WRITEUINT8(save->p, players[i].startboost);
WRITEUINT8(save->p, players[i].dropdashboost); WRITEUINT8(save->p, players[i].dropdashboost);
@ -478,6 +486,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].flamelength); WRITEUINT8(save->p, players[i].flamelength);
WRITEUINT16(save->p, players[i].ballhogcharge); WRITEUINT16(save->p, players[i].ballhogcharge);
WRITEUINT8(save->p, players[i].ballhogtap);
WRITEUINT16(save->p, players[i].hyudorotimer); WRITEUINT16(save->p, players[i].hyudorotimer);
WRITESINT8(save->p, players[i].stealingtimer); WRITESINT8(save->p, players[i].stealingtimer);
@ -535,9 +544,12 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].tripwireReboundDelay); WRITEUINT8(save->p, players[i].tripwireReboundDelay);
WRITEUINT16(save->p, players[i].sliptideZip); WRITEUINT16(save->p, players[i].wavedash);
WRITEUINT8(save->p, players[i].sliptideZipDelay); WRITEUINT8(save->p, players[i].wavedashdelay);
WRITEUINT16(save->p, players[i].sliptideZipBoost); WRITEUINT16(save->p, players[i].wavedashboost);
WRITEUINT16(save->p, players[i].trickcharge);
WRITEUINT16(save->p, players[i].infinitether);
WRITEUINT8(save->p, players[i].lastsafelap); WRITEUINT8(save->p, players[i].lastsafelap);
@ -555,9 +567,13 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEINT16(save->p, players[i].incontrol); WRITEINT16(save->p, players[i].incontrol);
WRITEUINT8(save->p, players[i].markedfordeath); WRITEUINT8(save->p, players[i].markedfordeath);
WRITEUINT8(save->p, players[i].dotrickfx);
WRITEUINT8(save->p, players[i].ringboxdelay); WRITEUINT8(save->p, players[i].ringboxdelay);
WRITEUINT8(save->p, players[i].ringboxaward); WRITEUINT8(save->p, players[i].ringboxaward);
WRITEUINT8(save->p, players[i].itemflags);
WRITEFIXED(save->p, players[i].outrun); WRITEFIXED(save->p, players[i].outrun);
WRITEUINT8(save->p, players[i].rideroid); 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); WRITEFIXED(save->p, players[i].loop.shift.y);
WRITEUINT8(save->p, players[i].loop.flip); 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. // 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. // 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); WRITEUINT32(save->p, players[i].roundconditions.unlocktriggers);
@ -863,8 +890,11 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
if (flags & STUMBLE) if (flags & STUMBLE)
players[i].stumbleIndicator = (mobj_t *)(size_t)READUINT32(save->p); players[i].stumbleIndicator = (mobj_t *)(size_t)READUINT32(save->p);
if (flags & SLIPTIDEZIP) if (flags & WAVEDASH)
players[i].sliptideZipIndicator = (mobj_t *)(size_t)READUINT32(save->p); 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) if (flags & WHIP)
players[i].whip = (mobj_t *)(size_t)READUINT32(save->p); 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].currentwaypoint = (waypoint_t *)(size_t)READUINT32(save->p);
players[i].nextwaypoint = (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].airtime = READUINT32(save->p);
players[i].lastairtime = READUINT32(save->p);
players[i].startboost = READUINT8(save->p); players[i].startboost = READUINT8(save->p);
players[i].dropdashboost = 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].flamelength = READUINT8(save->p);
players[i].ballhogcharge = READUINT16(save->p); players[i].ballhogcharge = READUINT16(save->p);
players[i].ballhogtap = READUINT8(save->p);
players[i].hyudorotimer = READUINT16(save->p); players[i].hyudorotimer = READUINT16(save->p);
players[i].stealingtimer = READSINT8(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].tripwireReboundDelay = READUINT8(save->p);
players[i].sliptideZip = READUINT16(save->p); players[i].wavedash = READUINT16(save->p);
players[i].sliptideZipDelay = READUINT8(save->p); players[i].wavedashdelay = READUINT8(save->p);
players[i].sliptideZipBoost = READUINT16(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); 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].incontrol = READINT16(save->p);
players[i].markedfordeath = READUINT8(save->p); players[i].markedfordeath = READUINT8(save->p);
players[i].dotrickfx = READUINT8(save->p);
players[i].ringboxdelay = READUINT8(save->p); players[i].ringboxdelay = READUINT8(save->p);
players[i].ringboxaward = READUINT8(save->p); players[i].ringboxaward = READUINT8(save->p);
players[i].itemflags = READUINT8(save->p);
players[i].outrun = READFIXED(save->p); players[i].outrun = READFIXED(save->p);
players[i].rideroid = (boolean)READUINT8(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.shift.y = READFIXED(save->p);
players[i].loop.flip = READUINT8(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. // 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. // 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); players[i].roundconditions.unlocktriggers = READUINT32(save->p);
@ -1999,6 +2049,8 @@ static void ArchiveSectors(savebuffer_t *save)
WRITEUINT8(save->p, diff3); WRITEUINT8(save->p, diff3);
if (diff3 & SD_DIFF4) if (diff3 & SD_DIFF4)
WRITEUINT8(save->p, diff4); WRITEUINT8(save->p, diff4);
if (diff4 & SD_DIFF5)
WRITEUINT8(save->p, diff5);
if (diff & SD_FLOORHT) if (diff & SD_FLOORHT)
WRITEFIXED(save->p, ss->floorheight); WRITEFIXED(save->p, ss->floorheight);
if (diff & SD_CEILHT) if (diff & SD_CEILHT)
@ -2638,9 +2690,16 @@ typedef enum
MD2_FROZEN = 1<<28, MD2_FROZEN = 1<<28,
MD2_TERRAIN = 1<<29, MD2_TERRAIN = 1<<29,
MD2_WATERSKIP = 1<<30, MD2_WATERSKIP = 1<<30,
MD2_LIGHTLEVEL = (INT32)(1U<<31), MD2_MORE = (INT32)(1U<<31),
} mobj_diff2_t; } mobj_diff2_t;
typedef enum
{
MD3_LIGHTLEVEL = 1,
MD3_REAPPEAR = 1<<1,
MD3_PUNT_REF = 1<<2,
} mobj_diff3_t;
typedef enum typedef enum
{ {
tc_mobj, 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; const mobj_t *mobj = (const mobj_t *)th;
UINT32 diff; UINT32 diff;
UINT32 diff2; UINT32 diff2;
UINT32 diff3;
size_t j; size_t j;
if (TypeIsNetSynced(mobj->type) == false) if (TypeIsNetSynced(mobj->type) == false)
return; return;
diff2 = 0; diff2 = 0;
diff3 = 0;
if (mobj->spawnpoint) if (mobj->spawnpoint)
{ {
@ -2938,8 +2999,6 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
|| (slope->normal.z != FRACUNIT)) || (slope->normal.z != FRACUNIT))
diff2 |= MD2_FLOORSPRITESLOPE; diff2 |= MD2_FLOORSPRITESLOPE;
} }
if (mobj->lightlevel)
diff2 |= MD2_LIGHTLEVEL;
if (mobj->hitlag) if (mobj->hitlag)
diff2 |= MD2_HITLAG; diff2 |= MD2_HITLAG;
if (mobj->waterskip) 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) if (mobj->terrain != NULL || mobj->terrainOverlay != NULL)
diff2 |= MD2_TERRAIN; 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) if (diff2 != 0)
diff |= MD_MORE; diff |= MD_MORE;
@ -2968,6 +3037,8 @@ static void SaveMobjThinker(savebuffer_t *save, const thinker_t *th, const UINT8
WRITEUINT32(save->p, diff); WRITEUINT32(save->p, diff);
if (diff & MD_MORE) if (diff & MD_MORE)
WRITEUINT32(save->p, diff2); 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->z); // Force this so 3dfloor problems don't arise.
WRITEFIXED(save->p, mobj->floorz); 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.y);
WRITEFIXED(save->p, slope->normal.z); WRITEFIXED(save->p, slope->normal.z);
} }
if (diff2 & MD2_LIGHTLEVEL)
{
WRITEINT16(save->p, mobj->lightlevel);
}
if (diff2 & MD2_HITLAG) if (diff2 & MD2_HITLAG)
{ {
WRITEINT32(save->p, mobj->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)); 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); WRITEUINT32(save->p, mobj->mobjnum);
} }
@ -4104,6 +4184,7 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker)
mobj_t *mobj; mobj_t *mobj;
UINT32 diff; UINT32 diff;
UINT32 diff2; UINT32 diff2;
UINT32 diff3;
INT32 i; INT32 i;
fixed_t z, floorz, ceilingz; fixed_t z, floorz, ceilingz;
ffloor_t *floorrover = NULL, *ceilingrover = NULL; ffloor_t *floorrover = NULL, *ceilingrover = NULL;
@ -4115,6 +4196,11 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker)
else else
diff2 = 0; 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. z = READFIXED(save->p); // Force this so 3dfloor problems don't arise.
floorz = READFIXED(save->p); floorz = READFIXED(save->p);
ceilingz = READFIXED(save->p); ceilingz = READFIXED(save->p);
@ -4440,10 +4526,6 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker)
P_UpdateSlopeLightOffset(slope); P_UpdateSlopeLightOffset(slope);
} }
if (diff2 & MD2_LIGHTLEVEL)
{
mobj->lightlevel = READINT16(save->p);
}
if (diff2 & MD2_HITLAG) if (diff2 & MD2_HITLAG)
{ {
mobj->hitlag = READINT32(save->p); mobj->hitlag = READINT32(save->p);
@ -4470,6 +4552,19 @@ static thinker_t* LoadMobjThinker(savebuffer_t *save, actionf_p1 thinker)
mobj->terrain = NULL; 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 // set sprev, snext, bprev, bnext, subsector
P_SetThingPosition(mobj); P_SetThingPosition(mobj);
@ -5509,6 +5604,13 @@ static void P_RelinkPointers(void)
if (!P_SetTarget(&mobj->terrainOverlay, P_FindNewPosition(temp))) if (!P_SetTarget(&mobj->terrainOverlay, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "terrainOverlay not found on %d\n", mobj->type); 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++) for (i = 0; i < MAXPLAYERS; i++)
@ -5592,12 +5694,19 @@ static void P_RelinkPointers(void)
if (!P_SetTarget(&players[i].stumbleIndicator, P_FindNewPosition(temp))) if (!P_SetTarget(&players[i].stumbleIndicator, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "stumbleIndicator not found on player %d\n", i); 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; temp = (UINT32)(size_t)players[i].wavedashIndicator;
players[i].sliptideZipIndicator = NULL; players[i].wavedashIndicator = NULL;
if (!P_SetTarget(&players[i].sliptideZipIndicator, P_FindNewPosition(temp))) if (!P_SetTarget(&players[i].wavedashIndicator, P_FindNewPosition(temp)))
CONS_Debug(DBG_GAMELOGIC, "sliptideZipIndicator not found on player %d\n", i); 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) if (players[i].whip)
{ {

View file

@ -8363,6 +8363,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
Patch_FreeTag(PU_PATCH_LOWPRIORITY); Patch_FreeTag(PU_PATCH_LOWPRIORITY);
Patch_FreeTag(PU_PATCH_ROTATED); Patch_FreeTag(PU_PATCH_ROTATED);
Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
mobjcache = NULL;
R_InitializeLevelInterpolators(); R_InitializeLevelInterpolators();

271
src/p_sweep.cpp Normal file
View file

@ -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 <algorithm>
#include <optional>
#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<unit> ql{l};
unit ls = copysign(kUnit, ql.m());
auto hit = [&](const vec2& k, unit xr, unit x, const vec2& n) -> Contact
{
std::optional<vec2> 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<Contact>
{
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<Contact>
{
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<vec2> 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<Contact>
{
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<unit> ql{l};
auto hit = [&](const vec2& k, unit xr, unit y, const vec2& n) -> Contact
{
std::optional<vec2> 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<Contact>
{
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);
}

131
src/p_sweep.hpp Normal file
View file

@ -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 <optional>
#include <variant>
#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<unit>;
using line_segment = math::LineSegment<unit>;
struct Contact
{
unit z; // time
vec2 n; // normal TODO REMOVE duplicate for each contact
vec2 p; // contact point 1
std::optional<vec2> q; // AABBvsLine: contact point 2
};
struct Result
{
std::optional<Contact> hit, exit; // TODO result itself should be optional, not each contact
};
namespace detail
{
template <typename T>
struct BaseAABBvsLine : protected srb2::math::Traits<unit>
{
public:
Result operator()(const line_segment& l) const
{
auto derived = static_cast<const T*>(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<Contact>&& t1, std::optional<Contact>&& 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>
{
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<unit> 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>
{
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<SlopeAABBvsLine, VerticalAABBvsLine>;
var_t var_;
};
}; // namespace srb2::sweep
#endif/*p_sweep_hpp*/

78
src/p_test.cpp Normal file
View file

@ -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 <optional>
#include <vector>
#include "math/fixed.hpp"
#include "p_sweep.hpp"
#include "p_local.h"
namespace
{
std::vector<line_t*> 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> collision;
LineSegment<Fixed> l{{ax, ay}, {bx, by}};
AABBvsLine sweep{r, l};
for (line_t* ld : g_lines)
{
LineSegment<Fixed> 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();
}

View file

@ -310,6 +310,7 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
thlist[n].prev = thinker; thlist[n].prev = thinker;
thinker->references = 0; // killough 11/98: init reference counter to 0 thinker->references = 0; // killough 11/98: init reference counter to 0
thinker->cachable = n == THINK_MOBJ;
#ifdef PARANOIA #ifdef PARANOIA
thinker->debug_mobjtype = MT_NULL; thinker->debug_mobjtype = MT_NULL;
@ -427,7 +428,16 @@ void P_UnlinkThinker(thinker_t *thinker)
I_Assert(thinker->references == 0); I_Assert(thinker->references == 0);
(next->prev = thinker->prev)->next = next; (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) if (player->mo == NULL)
continue; continue;
if (player->exiting)
continue;
if ((player->mo->eflags & MFE_DAMAGEHITLAG) && player->mo->hitlag) if ((player->mo->eflags & MFE_DAMAGEHITLAG) && player->mo->hitlag)
{ {
low = high = 65536 / 2; low = high = 65536 / 2;

View file

@ -478,7 +478,7 @@ void P_ResetPlayer(player_t *player)
player->onconveyor = 0; player->onconveyor = 0;
//player->drift = player->driftcharge = 0; //player->drift = player->driftcharge = 0;
player->trickpanel = 0; player->trickpanel = TRICKSTATE_NONE;
player->glanceDir = 0; player->glanceDir = 0;
player->fastfall = 0; player->fastfall = 0;
@ -1109,6 +1109,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
P_SetTarget(&ghost->target, mobj); P_SetTarget(&ghost->target, mobj);
P_SetScale(ghost, mobj->scale); P_SetScale(ghost, mobj->scale);
ghost->scalespeed = mobj->scalespeed;
ghost->destscale = mobj->scale; ghost->destscale = mobj->scale;
if (mobj->eflags & MFE_VERTICALFLIP) 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_angle = (mobj->player ? mobj->player->old_drawangle2 : mobj->old_angle2);
ghost->old_pitch = mobj->old_pitch2; ghost->old_pitch = mobj->old_pitch2;
ghost->old_roll = mobj->old_roll2; ghost->old_roll = mobj->old_roll2;
ghost->old_scale = mobj->old_scale2;
K_ReduceVFX(ghost, mobj->player); K_ReduceVFX(ghost, mobj->player);
ghost->reappear = mobj->reappear;
P_SetTarget(&ghost->punt_ref, mobj->punt_ref);
return ghost; return ghost;
} }
@ -2465,18 +2470,21 @@ void P_MovePlayer(player_t *player)
} }
else else
{ {
K_KartMoveAnimation(player); if (player->trickpanel > TRICKSTATE_READY)
if (player->trickpanel == 2)
{ {
player->drawangle += ANGLE_22h; if (player->trickpanel <= TRICKSTATE_RIGHT) // right/forward
} {
else if (player->trickpanel >= 3) player->drawangle += ANGLE_22h;
{ }
player->drawangle -= ANGLE_22h; else //if (player->trickpanel >= TRICKSTATE_LEFT) // left/back
{
player->drawangle -= ANGLE_22h;
}
} }
else else
{ {
K_KartMoveAnimation(player);
player->drawangle = player->mo->angle; player->drawangle = player->mo->angle;
if (player->aizdriftturn) 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 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; 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) 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 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 scaleDiff;
fixed_t cameraScale = mapobjectscale; 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_x = thiscam->x;
thiscam->old_y = thiscam->y; thiscam->old_y = thiscam->y;
thiscam->old_z = thiscam->z; 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); camdist = FixedMul(cv_cam_dist[num].value, cameraScale);
camheight = FixedMul(cv_cam_height[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) if (timeover)
{ {
const INT32 timeovercam = max(0, min(180, (player->karthud[khud_timeovercam] - 2*TICRATE)*15)); 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; fixed_t smoothing;
if (player->loop.radius)
{
return;
}
if (player->respawn.state != RESPAWNST_NONE) if (player->respawn.state != RESPAWNST_NONE)
{ {
player->tilt = 0; player->tilt = 0;
@ -3995,7 +4061,8 @@ void P_PlayerThink(player_t *player)
PlayerPointerErase(player->followmobj); PlayerPointerErase(player->followmobj);
PlayerPointerErase(player->stumbleIndicator); PlayerPointerErase(player->stumbleIndicator);
PlayerPointerErase(player->sliptideZipIndicator); PlayerPointerErase(player->wavedashIndicator);
PlayerPointerErase(player->trickIndicator);
PlayerPointerErase(player->whip); PlayerPointerErase(player->whip);
PlayerPointerErase(player->hand); PlayerPointerErase(player->hand);
PlayerPointerErase(player->ringShooter); PlayerPointerErase(player->ringShooter);
@ -4017,6 +4084,7 @@ void P_PlayerThink(player_t *player)
if (P_IsObjectOnGround(player->mo) if (P_IsObjectOnGround(player->mo)
&& !P_PlayerInPain(player)) // This isn't airtime, but it's control loss all the same. && !P_PlayerInPain(player)) // This isn't airtime, but it's control loss all the same.
{ {
player->lastairtime = player->airtime;
player->airtime = 0; player->airtime = 0;
} }
else 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. // Flash player after being hit.
if (!(player->hyudorotimer // SRB2kart - fixes Hyudoro not flashing when it should. if (!deathcontrolled && !powercontrolled)
|| 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 (player->flashing > 1 && player->flashing < K_GetKartFlashing(player) if (player->flashing > 1 && player->flashing < K_GetKartFlashing(player)
&& (leveltime & 1)) && (leveltime & 1))
@ -4342,6 +4410,10 @@ void P_PlayerThink(player_t *player)
else else
player->mo->renderflags &= ~RF_DONTDRAW; player->mo->renderflags &= ~RF_DONTDRAW;
} }
else if (!deathcontrolled)
{
player->mo->renderflags &= ~RF_DONTDRAW;
}
if (player->stairjank > 0) if (player->stairjank > 0)
{ {

View file

@ -204,29 +204,40 @@ void R_DrawThingBoundingBox(vissprite_t *vis)
.color = R_GetBoundingBoxColor(vis->mobj), .color = R_GetBoundingBoxColor(vis->mobj),
}; };
// 1--3 if (vis->mobjflags & MF_PAPERCOLLISION)
// | | {
// 0--2 // 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 // left
draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top
// right draw_bbox_col(&bb, 0, tx, ty); // bottom
draw_bbox_col(&bb, 1, tx - rc, ty + rs); // top
tx += rs; // right
ty += rc;
draw_bbox_col(&bb, 2, tx, ty); // bottom tx += rs;
draw_bbox_col(&bb, 3, tx - rc, ty + rs); // top 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); // connect all four columns
draw_bbox_row(&bb, 1, 3);
draw_bbox_row(&bb, 3, 2); draw_bbox_row(&bb, 0, 1);
draw_bbox_row(&bb, 2, 0); 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) static boolean is_tangible (mobj_t *thing)
@ -240,7 +251,7 @@ static boolean is_tangible (mobj_t *thing)
// These objects probably do nothing! :D // These objects probably do nothing! :D
if ((thing->flags & (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE if ((thing->flags & (MF_SPECIAL|MF_SOLID|MF_SHOOTABLE
|MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_SPRING |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) |MF_PICKUPFROMBELOW)) == 0U)
{ {
return false; return false;

View file

@ -1586,13 +1586,22 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis)
R_InterpolateMobjState(thing, FRACUNIT, &interp); R_InterpolateMobjState(thing, FRACUNIT, &interp);
} }
// 1--3 if (thing->flags & MF_PAPERCOLLISION)
// | | {
// 0--2 // 0--1
// start in the middle
// start in the (0) corner gx = interp.x - viewx;
gx = interp.x - thing->radius - viewx; gy = interp.y - viewy;
gy = interp.y - thing->radius - 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); 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->gx = tx;
box->gy = tz; box->gy = tz;
box->scale = 2 * FixedMul(thing->radius, viewsin); if (thing->flags & MF_PAPERCOLLISION)
box->xscale = 2 * FixedMul(thing->radius, viewcos); {
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->pz = interp.z;
box->pzt = box->pz + box->thingheight; 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))) if (r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing)))
return false; 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)) if ((viewssnum == 0 && (thing->renderflags & RF_DONTDRAWP1))
|| (viewssnum == 1 && (thing->renderflags & RF_DONTDRAWP2)) || (viewssnum == 1 && (thing->renderflags & RF_DONTDRAWP2))
|| (viewssnum == 2 && (thing->renderflags & RF_DONTDRAWP3)) || (viewssnum == 2 && (thing->renderflags & RF_DONTDRAWP3))

View file

@ -426,6 +426,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
for (i = 0; i <= r_splitscreen; i++) for (i = 0; i <= r_splitscreen; i++)
{ {
player_t *player = &players[displayplayers[i]]; player_t *player = &players[displayplayers[i]];
boolean camaway = false;
memset(&listener[i], 0, sizeof (listener[i])); memset(&listener[i], 0, sizeof (listener[i]));
listenmobj[i] = NULL; listenmobj[i] = NULL;
@ -442,9 +443,11 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
else else
{ {
listenmobj[i] = player->mo; 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; itsUs = true;
} }
@ -763,6 +766,9 @@ void S_UpdateSounds(void)
if (c->origin != listenmobj[i]) if (c->origin != listenmobj[i])
continue; continue;
if (listenmobj[i]->player && listenmobj[i]->player->exiting)
continue;
itsUs = true; itsUs = true;
} }
@ -942,6 +948,9 @@ boolean S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32
if (!listener) if (!listener)
return false; return false;
if (source->thinker.function.acp1 == (actionf_p1)P_MobjThinker && P_MobjIsReappearing(source))
return false;
// Init listensource with default listener // Init listensource with default listener
listensource.x = listener->x; listensource.x = listener->x;
listensource.y = listener->y; listensource.y = listener->y;

View file

@ -1119,6 +1119,10 @@ sfxinfo_t S_sfx[NUMSFX] =
{"fshld2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Flame Shield burst"}, {"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"}, {"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 // RR - Ballhog Charge
{"bhog00", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Ballhog charging"}, {"bhog00", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Ballhog charging"},
{"bhog01", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Ballhog charging"}, {"bhog01", false, 64, 64, -1, NULL, 0, -1, -1, LUMPERROR, "Ballhog charging"},

View file

@ -1187,6 +1187,10 @@ typedef enum
sfx_fshld2, sfx_fshld2,
sfx_fshld3, sfx_fshld3,
// RR - Trick panels
sfx_trick0,
sfx_trick1,
// RR - Ballhog Charge // RR - Ballhog Charge
sfx_bhog00, sfx_bhog00,
sfx_bhog01, sfx_bhog01,