mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into other-progression
This commit is contained in:
commit
4379c62276
49 changed files with 1537 additions and 190 deletions
|
|
@ -64,6 +64,8 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
|||
p_tick.c
|
||||
p_user.c
|
||||
p_slopes.c
|
||||
p_sweep.cpp
|
||||
p_test.cpp
|
||||
tables.c
|
||||
r_bsp.cpp
|
||||
r_data.c
|
||||
|
|
|
|||
|
|
@ -806,6 +806,7 @@ consvar_t cv_numlaps = OnlineCheat("numlaps", "Map default").values(numlaps_cons
|
|||
|
||||
consvar_t cv_restrictskinchange = OnlineCheat("restrictskinchange", "Yes").yes_no().description("Don't let players change their skin in the middle of gameplay");
|
||||
consvar_t cv_spbtest = OnlineCheat("spbtest", "Off").on_off().description("SPB can never target a player");
|
||||
consvar_t cv_showgremlins = OnlineCheat("showgremlins", "No").yes_no().description("Show line collision errors");
|
||||
consvar_t cv_timescale = OnlineCheat(cvlist_timer)("timescale", "1.0").floating_point().min_max(FRACUNIT/20, 20*FRACUNIT).description("Overclock or slow down the game");
|
||||
consvar_t cv_ufo_follow = OnlineCheat("ufo_follow", "0").min_max(0, MAXPLAYERS).description("Make UFO Catcher folow this player");
|
||||
consvar_t cv_ufo_health = OnlineCheat("ufo_health", "-1").min_max(-1, 100).description("Override UFO Catcher health -- applied at spawn or when value is changed");
|
||||
|
|
@ -905,6 +906,7 @@ void Dummymenuplayer_OnChange(void);
|
|||
consvar_t cv_dummymenuplayer = MenuDummy("dummymenuplayer", "P1").onchange(Dummymenuplayer_OnChange).values({{0, "NOPE"}, {1, "P1"}, {2, "P2"}, {3, "P3"}, {4, "P4"}});
|
||||
|
||||
consvar_t cv_dummyprofileautoroulette = MenuDummy("dummyprofileautoroulette", "Off").on_off();
|
||||
consvar_t cv_dummyprofilelitesteer = MenuDummy("dummyprofilelitesteer", "On").on_off();
|
||||
consvar_t cv_dummyprofilekickstart = MenuDummy("dummyprofilekickstart", "Off").on_off();
|
||||
consvar_t cv_dummyprofilename = MenuDummy("dummyprofilename", "");
|
||||
consvar_t cv_dummyprofileplayername = MenuDummy("dummyprofileplayername", "");
|
||||
|
|
@ -1011,6 +1013,13 @@ consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS] = {
|
|||
Player("autoroulette4", "Off").on_off().onchange(weaponPrefChange4),
|
||||
};
|
||||
|
||||
consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS] = {
|
||||
Player("litesteer", "On").on_off().onchange(weaponPrefChange),
|
||||
Player("litesteer2", "On").on_off().onchange(weaponPrefChange2),
|
||||
Player("litesteer3", "On").on_off().onchange(weaponPrefChange3),
|
||||
Player("litesteer4", "On").on_off().onchange(weaponPrefChange4),
|
||||
};
|
||||
|
||||
consvar_t cv_cam_dist[MAXSPLITSCREENPLAYERS] = {
|
||||
Player("cam_dist", "190").floating_point(),
|
||||
Player("cam2_dist", "190").floating_point(),
|
||||
|
|
|
|||
|
|
@ -976,20 +976,9 @@ void D_SRB2Loop(void)
|
|||
|
||||
// Fully completed frame made.
|
||||
finishprecise = I_GetPreciseTime();
|
||||
if (!singletics)
|
||||
{
|
||||
INT64 elapsed = (INT64)(finishprecise - enterprecise);
|
||||
|
||||
// in the case of "match refresh rate" + vsync, don't sleep at all
|
||||
const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0;
|
||||
|
||||
if (elapsed > 0 && (INT64)capbudget > elapsed && !vsync_with_match_refresh)
|
||||
{
|
||||
I_SleepDuration(capbudget - (finishprecise - enterprecise));
|
||||
}
|
||||
}
|
||||
// Capture the time once more to get the real delta time.
|
||||
finishprecise = I_GetPreciseTime();
|
||||
// Use the time before sleep for frameskip calculations:
|
||||
// post-sleep time is literally being intentionally wasted
|
||||
deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
|
||||
deltatics = deltasecs * NEWTICRATE;
|
||||
|
||||
|
|
@ -1009,6 +998,23 @@ void D_SRB2Loop(void)
|
|||
{
|
||||
frameskip = 0;
|
||||
}
|
||||
|
||||
if (!singletics)
|
||||
{
|
||||
INT64 elapsed = (INT64)(finishprecise - enterprecise);
|
||||
|
||||
// in the case of "match refresh rate" + vsync, don't sleep at all
|
||||
const boolean vsync_with_match_refresh = cv_vidwait.value && cv_fpscap.value == 0;
|
||||
|
||||
if (elapsed > 0 && (INT64)capbudget > elapsed && !vsync_with_match_refresh)
|
||||
{
|
||||
I_SleepDuration(capbudget - (finishprecise - enterprecise));
|
||||
}
|
||||
}
|
||||
// Capture the time once more to get the real delta time.
|
||||
finishprecise = I_GetPreciseTime();
|
||||
deltasecs = (double)((INT64)(finishprecise - enterprecise)) / I_GetPrecisePrecision();
|
||||
deltatics = deltasecs * NEWTICRATE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1229,6 +1229,7 @@ enum {
|
|||
WP_KICKSTARTACCEL = 1<<0,
|
||||
WP_SHRINKME = 1<<1,
|
||||
WP_AUTOROULETTE = 1<<2,
|
||||
WP_LITESTEER = 1<<3,
|
||||
};
|
||||
|
||||
void WeaponPref_Send(UINT8 ssplayer)
|
||||
|
|
@ -1241,6 +1242,9 @@ void WeaponPref_Send(UINT8 ssplayer)
|
|||
if (cv_autoroulette[ssplayer].value)
|
||||
prefs |= WP_AUTOROULETTE;
|
||||
|
||||
if (cv_litesteer[ssplayer].value)
|
||||
prefs |= WP_LITESTEER;
|
||||
|
||||
if (cv_shrinkme[ssplayer].value)
|
||||
prefs |= WP_SHRINKME;
|
||||
|
||||
|
|
@ -1259,6 +1263,9 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum)
|
|||
if (player->pflags & PF_AUTOROULETTE)
|
||||
prefs |= WP_AUTOROULETTE;
|
||||
|
||||
if (player->pflags & PF_LITESTEER)
|
||||
prefs |= WP_LITESTEER;
|
||||
|
||||
if (player->pflags & PF_SHRINKME)
|
||||
prefs |= WP_SHRINKME;
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,14 @@ typedef enum
|
|||
PST_REBORN
|
||||
} playerstate_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
IF_USERINGS = 1, // Have to be not holding the item button to change from using rings to using items (or vice versa) - prevents weirdness
|
||||
IF_ITEMOUT = 1<<1, // Are you holding an item out?
|
||||
IF_EGGMANOUT = 1<<2, // Eggman mark held, separate from IF_ITEMOUT so it doesn't stop you from getting items
|
||||
IF_HOLDREADY = 1<<3, // Hold button-style item is ready to activate
|
||||
} itemflags_t;
|
||||
|
||||
//
|
||||
// Player internal flags
|
||||
//
|
||||
|
|
@ -95,11 +103,9 @@ typedef enum
|
|||
|
||||
PF_RINGLOCK = 1<<13, // Prevent picking up rings while SPB is locked on
|
||||
|
||||
// The following four flags are mutually exclusive, although they can also all be off at the same time. If we ever run out of pflags, eventually turn them into a seperate five(+) mode UINT8..?
|
||||
PF_USERINGS = 1<<14, // Have to be not holding the item button to change from using rings to using items (or vice versa) - prevents weirdness
|
||||
PF_ITEMOUT = 1<<15, // Are you holding an item out?
|
||||
PF_EGGMANOUT = 1<<16, // Eggman mark held, separate from PF_ITEMOUT so it doesn't stop you from getting items
|
||||
PF_HOLDREADY = 1<<17, // Hold button-style item is ready to activate
|
||||
PF_LITESTEER = 1<<14, // Hold Down to shallow turn (digital only)
|
||||
|
||||
//15-17 free, was previously itemflags stuff
|
||||
|
||||
PF_DRIFTINPUT = 1<<18, // Drifting!
|
||||
PF_GETSPARKS = 1<<19, // Can get sparks
|
||||
|
|
@ -503,6 +509,15 @@ typedef enum
|
|||
BOT_ITEM_PR__MAX
|
||||
} botItemPriority_e;
|
||||
|
||||
typedef struct {
|
||||
tic_t enter_tic, exit_tic;
|
||||
tic_t zoom_in_speed, zoom_out_speed;
|
||||
fixed_t dist;
|
||||
angle_t pan;
|
||||
fixed_t pan_speed; // in degrees
|
||||
tic_t pan_accel, pan_back;
|
||||
} sonicloopcamvars_t;
|
||||
|
||||
// player_t struct for loop state
|
||||
typedef struct {
|
||||
fixed_t radius;
|
||||
|
|
@ -512,6 +527,7 @@ typedef struct {
|
|||
vector2_t origin_shift;
|
||||
vector2_t shift;
|
||||
boolean flip;
|
||||
sonicloopcamvars_t camera;
|
||||
} sonicloopvars_t;
|
||||
|
||||
// player_t struct for power-ups
|
||||
|
|
@ -717,6 +733,7 @@ struct player_t
|
|||
UINT8 flamelength; // Flame Shield dash meter, number of segments
|
||||
|
||||
UINT16 ballhogcharge; // Ballhog charge up -- the higher this value, the more projectiles
|
||||
boolean ballhogtap; // Ballhog released during charge: used to allow semirapid tapfire
|
||||
|
||||
UINT16 hyudorotimer; // Duration of the Hyudoro offroad effect itself
|
||||
SINT8 stealingtimer; // if >0 you are stealing, if <0 you are being stolen from
|
||||
|
|
@ -903,6 +920,8 @@ struct player_t
|
|||
UINT8 instaWhipChargeLockout;
|
||||
UINT8 guardCooldown;
|
||||
|
||||
UINT8 preventfailsafe; // Set when taking damage to prevent cheesing eggboxes
|
||||
|
||||
UINT8 handtimer;
|
||||
angle_t besthanddirection;
|
||||
|
||||
|
|
@ -913,6 +932,8 @@ struct player_t
|
|||
UINT8 ringboxdelay; // Delay until Ring Box auto-activates
|
||||
UINT8 ringboxaward; // Where did we stop?
|
||||
|
||||
UINT8 itemflags; // holds IF_ flags (see itemflags_t)
|
||||
|
||||
fixed_t outrun; // Milky Way road effect
|
||||
|
||||
uint8_t public_key[PUBKEYLENGTH];
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ struct thinker_t
|
|||
// killough 11/98: count of how many other objects reference
|
||||
// this one using pointers. Used for garbage collection.
|
||||
INT32 references;
|
||||
boolean cachable;
|
||||
|
||||
#ifdef PARANOIA
|
||||
INT32 debug_mobjtype;
|
||||
|
|
|
|||
|
|
@ -6093,11 +6093,10 @@ const char *const PLAYERFLAG_LIST[] = {
|
|||
|
||||
"RINGLOCK", // Prevent picking up rings while SPB is locked on
|
||||
|
||||
// The following four flags are mutually exclusive, although they can also all be off at the same time. If we ever run out of pflags, eventually turn them into a seperate five(+) mode UINT8..?
|
||||
"USERINGS", // Have to be not holding the item button to change from using rings to using items (or vice versa) - prevents weirdness
|
||||
"ITEMOUT", // Are you holding an item out?
|
||||
"EGGMANOUT", // Eggman mark held, separate from PF_ITEMOUT so it doesn't stop you from getting items
|
||||
"HOLDREADY", // Hold button-style item is ready to activate
|
||||
"LITESTEER", // Shallow digital turn with DOWN
|
||||
"\x01", // Free
|
||||
"\x01", // Free
|
||||
"\x01", // Free
|
||||
|
||||
"DRIFTINPUT", // Drifting!
|
||||
"GETSPARKS", // Can get sparks
|
||||
|
|
|
|||
|
|
@ -494,6 +494,11 @@ void F_RunWipe(UINT8 wipemode, UINT8 wipetype, boolean drawMenu, const char *col
|
|||
WipeInAction = true;
|
||||
wipe_scr = screens[0];
|
||||
|
||||
// FIXME: Wipes SUCK and drop input events for some reason, causing stuck gamepad inputs.
|
||||
// It's better to ignore an intentional hold than to turn a tap into a phantom hold.
|
||||
// (If you're removing this, remove the one after the inner loop too!)
|
||||
memset(gamekeydown, 0, sizeof(gamekeydown));
|
||||
|
||||
// lastwipetic should either be 0 or the tic we last wiped
|
||||
// on for fade-to-black
|
||||
for (;;)
|
||||
|
|
@ -581,6 +586,11 @@ void F_RunWipe(UINT8 wipemode, UINT8 wipetype, boolean drawMenu, const char *col
|
|||
|
||||
WipeInAction = false;
|
||||
|
||||
// FIXME: Wipes SUCK and drop input events for some reason, causing stuck gamepad inputs.
|
||||
// It's better to ignore an intentional hold than to turn a tap into a phantom hold.
|
||||
// (If you're removing this, remove the one before the inner loop too!)
|
||||
memset(gamekeydown, 0, sizeof(gamekeydown));
|
||||
|
||||
if (fcolor)
|
||||
{
|
||||
Z_Free(fcolor);
|
||||
|
|
|
|||
|
|
@ -380,7 +380,7 @@ class TiccmdBuilder
|
|||
// ugly with the current abstractions, though, and there's a fortunate trick here:
|
||||
// if you can input full strength turns on both axes, either you're using a fucking
|
||||
// square gate, or you're not on an analog device.
|
||||
if (joystickvector.yaxis >= JOYAXISRANGE && abs(cmd->turning) == KART_FULLTURN) // >= beacuse some analog devices can go past JOYAXISRANGE (?!)
|
||||
if (cv_litesteer[ssplayer - 1].value && joystickvector.yaxis >= JOYAXISRANGE && abs(cmd->turning) == KART_FULLTURN) // >= beacuse some analog devices can go past JOYAXISRANGE (?!)
|
||||
cmd->turning /= 2;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ demoghost *ghosts = NULL;
|
|||
#define DEMO_SHRINKME 0x04
|
||||
#define DEMO_BOT 0x08
|
||||
#define DEMO_AUTOROULETTE 0x10
|
||||
#define DEMO_LITESTEER 0x20
|
||||
|
||||
// For demos
|
||||
#define ZT_FWD 0x0001
|
||||
|
|
@ -2480,6 +2481,8 @@ void G_BeginRecording(void)
|
|||
i |= DEMO_KICKSTART;
|
||||
if (player->pflags & PF_AUTOROULETTE)
|
||||
i |= DEMO_AUTOROULETTE;
|
||||
if (player->pflags & PF_LITESTEER)
|
||||
i |= DEMO_LITESTEER;
|
||||
if (player->pflags & PF_SHRINKME)
|
||||
i |= DEMO_SHRINKME;
|
||||
if (player->bot == true)
|
||||
|
|
@ -3447,6 +3450,11 @@ void G_DoPlayDemo(const char *defdemoname)
|
|||
else
|
||||
players[p].pflags &= ~PF_AUTOROULETTE;
|
||||
|
||||
if (flags & DEMO_LITESTEER)
|
||||
players[p].pflags |= PF_LITESTEER;
|
||||
else
|
||||
players[p].pflags &= ~PF_LITESTEER;
|
||||
|
||||
if (flags & DEMO_SHRINKME)
|
||||
players[p].pflags |= PF_SHRINKME;
|
||||
else
|
||||
|
|
|
|||
|
|
@ -2127,7 +2127,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
totalring = players[player].totalring;
|
||||
xtralife = players[player].xtralife;
|
||||
|
||||
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE));
|
||||
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_LITESTEER));
|
||||
|
||||
// SRB2kart
|
||||
memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette));
|
||||
|
|
@ -2175,7 +2175,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (players[player].pflags & PF_ITEMOUT)
|
||||
if (players[player].itemflags & IF_ITEMOUT)
|
||||
{
|
||||
itemtype = 0;
|
||||
itemamount = 0;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ extern consvar_t cv_pauseifunfocused;
|
|||
|
||||
extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS];
|
||||
|
||||
extern consvar_t cv_deadzone[MAXSPLITSCREENPLAYERS];
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean
|
|||
--------------------------------------------------*/
|
||||
static void K_BotItemGenericTrapShield(player_t *player, ticcmd_t *cmd, INT16 turnamt, boolean mine)
|
||||
{
|
||||
if (player->pflags & PF_ITEMOUT)
|
||||
if (player->itemflags & IF_ITEMOUT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -465,7 +465,7 @@ static void K_BotItemGenericTrapShield(player_t *player, ticcmd_t *cmd, INT16 tu
|
|||
--------------------------------------------------*/
|
||||
static void K_BotItemGenericOrbitShield(player_t *player, ticcmd_t *cmd)
|
||||
{
|
||||
if (player->pflags & PF_ITEMOUT)
|
||||
if (player->itemflags & IF_ITEMOUT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -811,7 +811,7 @@ static boolean K_BotRevealsEggbox(player_t *player)
|
|||
--------------------------------------------------*/
|
||||
static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd)
|
||||
{
|
||||
if (player->pflags & PF_EGGMANOUT)
|
||||
if (player->itemflags & IF_EGGMANOUT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -1221,7 +1221,7 @@ static void K_BotItemBubble(player_t *player, ticcmd_t *cmd)
|
|||
hold = true;
|
||||
}
|
||||
|
||||
if (hold && (player->pflags & PF_HOLDREADY))
|
||||
if (hold && (player->itemflags & IF_HOLDREADY))
|
||||
{
|
||||
cmd->buttons |= BT_ATTACK;
|
||||
}
|
||||
|
|
@ -1245,7 +1245,7 @@ static void K_BotItemFlame(player_t *player, ticcmd_t *cmd)
|
|||
{
|
||||
player->botvars.itemconfirm--;
|
||||
}
|
||||
else if (player->pflags & PF_HOLDREADY)
|
||||
else if (player->itemflags & IF_HOLDREADY)
|
||||
{
|
||||
INT32 flamemax = player->flamelength;
|
||||
|
||||
|
|
@ -1532,7 +1532,7 @@ static void K_BotItemRouletteMash(player_t *player, ticcmd_t *cmd)
|
|||
--------------------------------------------------*/
|
||||
void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
||||
{
|
||||
if (player->pflags & PF_USERINGS)
|
||||
if (player->itemflags & IF_USERINGS)
|
||||
{
|
||||
if (player->rings > 0)
|
||||
{
|
||||
|
|
@ -1567,7 +1567,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
{
|
||||
K_BotItemEggmanExplosion(player, cmd);
|
||||
}
|
||||
else if (player->pflags & PF_EGGMANOUT)
|
||||
else if (player->itemflags & IF_EGGMANOUT)
|
||||
{
|
||||
K_BotItemEggman(player, cmd);
|
||||
}
|
||||
|
|
@ -1604,7 +1604,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
K_BotItemSneaker(player, cmd);
|
||||
break;
|
||||
case KITEM_BANANA:
|
||||
if (!(player->pflags & PF_ITEMOUT))
|
||||
if (!(player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
K_BotItemGenericTrapShield(player, cmd, turnamt, false);
|
||||
}
|
||||
|
|
@ -1617,7 +1617,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
K_BotItemEggmanShield(player, cmd);
|
||||
break;
|
||||
case KITEM_ORBINAUT:
|
||||
if (!(player->pflags & PF_ITEMOUT))
|
||||
if (!(player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
K_BotItemGenericOrbitShield(player, cmd);
|
||||
}
|
||||
|
|
@ -1627,7 +1627,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
}
|
||||
break;
|
||||
case KITEM_JAWZ:
|
||||
if (!(player->pflags & PF_ITEMOUT))
|
||||
if (!(player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
K_BotItemGenericOrbitShield(player, cmd);
|
||||
}
|
||||
|
|
@ -1637,7 +1637,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
}
|
||||
break;
|
||||
case KITEM_MINE:
|
||||
if (!(player->pflags & PF_ITEMOUT))
|
||||
if (!(player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
K_BotItemGenericTrapShield(player, cmd, turnamt, true);
|
||||
}
|
||||
|
|
@ -1654,7 +1654,7 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
|
|||
K_BotItemBallhog(player, cmd);
|
||||
break;
|
||||
case KITEM_DROPTARGET:
|
||||
if (!(player->pflags & PF_ITEMOUT))
|
||||
if (!(player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
K_BotItemGenericTrapShield(player, cmd, turnamt, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -577,8 +577,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
}
|
||||
// Has held item shield
|
||||
else if (K_PlayerAttackSteer(thing, side, 20,
|
||||
(thing->player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)),
|
||||
(g_nudgeSearch.botmo->player->pflags & (PF_ITEMOUT|PF_EGGMANOUT))
|
||||
(thing->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)),
|
||||
(g_nudgeSearch.botmo->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT))
|
||||
))
|
||||
{
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t1->target->hnext == t1)
|
||||
{
|
||||
P_SetTarget(&t1->target->hnext, NULL);
|
||||
t1->target->player->pflags &= ~PF_EGGMANOUT;
|
||||
t1->target->player->itemflags &= ~IF_EGGMANOUT;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1453,7 +1453,7 @@ static void K_drawKartItem(void)
|
|||
break;
|
||||
}
|
||||
|
||||
if ((stplyr->pflags & PF_ITEMOUT) && !(leveltime & 1))
|
||||
if ((stplyr->itemflags & IF_ITEMOUT) && !(leveltime & 1))
|
||||
localpatch[1] = kp_nodraw;
|
||||
}
|
||||
|
||||
|
|
|
|||
122
src/k_kart.c
122
src/k_kart.c
|
|
@ -6629,11 +6629,11 @@ void K_DropHnextList(player_t *player)
|
|||
|
||||
player->bananadrag = 0;
|
||||
|
||||
if (player->pflags & PF_EGGMANOUT)
|
||||
if (player->itemflags & IF_EGGMANOUT)
|
||||
{
|
||||
player->pflags &= ~PF_EGGMANOUT;
|
||||
player->itemflags &= ~IF_EGGMANOUT;
|
||||
}
|
||||
else if ((player->pflags & PF_ITEMOUT)
|
||||
else if ((player->itemflags & IF_ITEMOUT)
|
||||
&& (dropall || (--player->itemamount <= 0)))
|
||||
{
|
||||
player->itemamount = 0;
|
||||
|
|
@ -7026,11 +7026,11 @@ static void K_MoveHeldObjects(player_t *player)
|
|||
{
|
||||
player->bananadrag = 0;
|
||||
|
||||
if (player->pflags & PF_EGGMANOUT)
|
||||
if (player->itemflags & IF_EGGMANOUT)
|
||||
{
|
||||
player->pflags &= ~PF_EGGMANOUT;
|
||||
player->itemflags &= ~IF_EGGMANOUT;
|
||||
}
|
||||
else if (player->pflags & PF_ITEMOUT)
|
||||
else if (player->itemflags & IF_ITEMOUT)
|
||||
{
|
||||
player->itemamount = 0;
|
||||
K_UnsetItemOut(player);
|
||||
|
|
@ -7045,11 +7045,11 @@ static void K_MoveHeldObjects(player_t *player)
|
|||
P_SetTarget(&player->mo->hnext, NULL);
|
||||
player->bananadrag = 0;
|
||||
|
||||
if (player->pflags & PF_EGGMANOUT)
|
||||
if (player->itemflags & IF_EGGMANOUT)
|
||||
{
|
||||
player->pflags &= ~PF_EGGMANOUT;
|
||||
player->itemflags &= ~IF_EGGMANOUT;
|
||||
}
|
||||
else if (player->pflags & PF_ITEMOUT)
|
||||
else if (player->itemflags & IF_ITEMOUT)
|
||||
{
|
||||
player->itemamount = 0;
|
||||
K_UnsetItemOut(player);
|
||||
|
|
@ -8033,7 +8033,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
}
|
||||
|
||||
if (player->itemtype == KITEM_NONE)
|
||||
player->pflags &= ~PF_HOLDREADY;
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
|
||||
// DKR style camera for boosting
|
||||
if (player->karthud[khud_boostcam] != 0 || player->karthud[khud_destboostcam] != 0)
|
||||
|
|
@ -8250,6 +8250,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
if (player->invincibilitytimer && onground == true)
|
||||
player->invincibilitytimer--;
|
||||
|
||||
if (player->preventfailsafe)
|
||||
player->preventfailsafe--;
|
||||
|
||||
if ((player->respawn.state == RESPAWNST_NONE) && player->growshrinktimer != 0)
|
||||
{
|
||||
if (player->growshrinktimer > 0 && onground == true)
|
||||
|
|
@ -8375,7 +8378,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
S_StopSoundByID(player->mo, sfx_wchrg2);
|
||||
}
|
||||
|
||||
if (player->itemamount || player->respawn.state != RESPAWNST_NONE || player->pflags & (PF_ITEMOUT|PF_EGGMANOUT) || player->rocketsneakertimer || player->ringboxdelay)
|
||||
if (player->itemamount || player->respawn.state != RESPAWNST_NONE || player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT) || player->rocketsneakertimer || player->ringboxdelay)
|
||||
player->instaWhipCharge = 0;
|
||||
|
||||
if (player->tiregrease)
|
||||
|
|
@ -8704,7 +8707,7 @@ void K_KartPlayerAfterThink(player_t *player)
|
|||
K_MoveHeldObjects(player);
|
||||
|
||||
// Jawz reticule (seeking)
|
||||
if (player->itemtype == KITEM_JAWZ && (player->pflags & PF_ITEMOUT))
|
||||
if (player->itemtype == KITEM_JAWZ && (player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
const INT32 lastTargID = player->lastjawztarget;
|
||||
mobj_t *lastTarg = NULL;
|
||||
|
|
@ -10175,7 +10178,7 @@ void K_StripItems(player_t *player)
|
|||
K_DropKitchenSink(player);
|
||||
player->itemtype = KITEM_NONE;
|
||||
player->itemamount = 0;
|
||||
player->pflags &= ~(PF_ITEMOUT|PF_EGGMANOUT);
|
||||
player->itemflags &= ~(IF_ITEMOUT|IF_EGGMANOUT);
|
||||
|
||||
if (player->itemRoulette.eggman == false)
|
||||
{
|
||||
|
|
@ -10783,7 +10786,8 @@ static void K_AirFailsafe(player_t *player)
|
|||
const fixed_t thrustSpeed = 6*player->mo->scale; // 10*player->mo->scale
|
||||
|
||||
if (player->speed > maxSpeed // Above the max speed that you're allowed to use this technique.
|
||||
|| player->respawn.state != RESPAWNST_NONE) // Respawning, you don't need this AND drop dash :V
|
||||
|| player->respawn.state != RESPAWNST_NONE // Respawning, you don't need this AND drop dash :V
|
||||
|| player->preventfailsafe) // You just got hit or interacted with something committal, no mashing for distance
|
||||
{
|
||||
player->pflags &= ~PF_AIRFAILSAFE;
|
||||
return;
|
||||
|
|
@ -10793,9 +10797,12 @@ static void K_AirFailsafe(player_t *player)
|
|||
if (leveltime < introtime)
|
||||
return;
|
||||
|
||||
if ((K_GetKartButtons(player) & BT_ACCELERATE) || K_GetForwardMove(player) != 0)
|
||||
UINT8 buttons = K_GetKartButtons(player);
|
||||
|
||||
// Accel inputs queue air-failsafe for when they're released,
|
||||
// as long as they're not part of a fastfall attempt.
|
||||
if ((buttons & (BT_ACCELERATE|BT_BRAKE)) == BT_ACCELERATE || K_GetForwardMove(player) != 0)
|
||||
{
|
||||
// Queue up later
|
||||
player->pflags |= PF_AIRFAILSAFE;
|
||||
return;
|
||||
}
|
||||
|
|
@ -11014,7 +11021,7 @@ static void K_trickPanelTimingVisual(player_t *player, fixed_t momz)
|
|||
|
||||
void K_SetItemOut(player_t *player)
|
||||
{
|
||||
player->pflags |= PF_ITEMOUT;
|
||||
player->itemflags |= IF_ITEMOUT;
|
||||
|
||||
if (player->mo->scale >= FixedMul(GROW_PHYSICS_SCALE, mapobjectscale))
|
||||
{
|
||||
|
|
@ -11032,7 +11039,7 @@ void K_SetItemOut(player_t *player)
|
|||
|
||||
void K_UnsetItemOut(player_t *player)
|
||||
{
|
||||
player->pflags &= ~PF_ITEMOUT;
|
||||
player->itemflags &= ~IF_ITEMOUT;
|
||||
player->itemscale = ITEMSCALE_NORMAL;
|
||||
player->bananadrag = 0;
|
||||
}
|
||||
|
|
@ -11044,7 +11051,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
ticcmd_t *cmd = &player->cmd;
|
||||
boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->oldcmd.buttons & BT_ATTACK) && (player->respawn.state == RESPAWNST_NONE));
|
||||
boolean HOLDING_ITEM = (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT));
|
||||
boolean HOLDING_ITEM = (player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT));
|
||||
boolean NO_HYUDORO = (player->stealingtimer == 0);
|
||||
|
||||
if (!player->exiting)
|
||||
|
|
@ -11070,9 +11077,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
|| player->itemRoulette.active == true
|
||||
|| player->rocketsneakertimer
|
||||
|| player->eggmanexplode))
|
||||
player->pflags |= PF_USERINGS;
|
||||
player->itemflags |= IF_USERINGS;
|
||||
else
|
||||
player->pflags &= ~PF_USERINGS;
|
||||
player->itemflags &= ~IF_USERINGS;
|
||||
}
|
||||
|
||||
if (player->ringboxdelay)
|
||||
|
|
@ -11113,7 +11120,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->instaWhipCooldown = 0;
|
||||
}
|
||||
|
||||
if (leveltime < starttime || player->pflags & (PF_ITEMOUT|PF_EGGMANOUT) || player->rocketsneakertimer || player->instaWhipCooldown)
|
||||
if (leveltime < starttime || player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT) || player->rocketsneakertimer || player->instaWhipCooldown)
|
||||
{
|
||||
chargingwhip = false;
|
||||
player->instaWhipCharge = 0;
|
||||
|
|
@ -11187,7 +11194,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
// First, the really specific, finicky items that function without the item being directly in your item slot.
|
||||
{
|
||||
// Ring boosting
|
||||
if (player->pflags & PF_USERINGS)
|
||||
if (player->itemflags & IF_USERINGS)
|
||||
{
|
||||
if ((cmd->buttons & BT_ATTACK) && !player->ringdelay && player->rings > 0)
|
||||
{
|
||||
|
|
@ -11211,13 +11218,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->eggmanexplode = 1;
|
||||
}
|
||||
// Eggman Monitor throwing
|
||||
else if (player->pflags & PF_EGGMANOUT)
|
||||
else if (player->itemflags & IF_EGGMANOUT)
|
||||
{
|
||||
if (ATTACK_IS_DOWN)
|
||||
{
|
||||
K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->pflags &= ~PF_EGGMANOUT;
|
||||
player->itemflags &= ~IF_EGGMANOUT;
|
||||
K_UpdateHnextList(player, true);
|
||||
}
|
||||
}
|
||||
|
|
@ -11321,7 +11328,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
prev = mo;
|
||||
}
|
||||
}
|
||||
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) // Banana x3 thrown
|
||||
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Banana x3 thrown
|
||||
{
|
||||
K_ThrowKartItem(player, false, MT_BANANA, -1, 0, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
|
|
@ -11334,7 +11341,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
mobj_t *mo;
|
||||
player->itemamount--;
|
||||
player->pflags |= PF_EGGMANOUT;
|
||||
player->itemflags |= IF_EGGMANOUT;
|
||||
S_StartSound(player->mo, sfx_s254);
|
||||
mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD);
|
||||
if (mo)
|
||||
|
|
@ -11384,7 +11391,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
prev = mo;
|
||||
}
|
||||
}
|
||||
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT)) // Orbinaut x3 thrown
|
||||
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Orbinaut x3 thrown
|
||||
{
|
||||
K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
|
|
@ -11425,7 +11432,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
prev = mo;
|
||||
}
|
||||
}
|
||||
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->pflags & PF_ITEMOUT)) // Jawz thrown
|
||||
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Jawz thrown
|
||||
{
|
||||
K_ThrowKartItem(player, true, MT_JAWZ, 1, 0, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
|
|
@ -11451,12 +11458,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
P_SetTarget(&player->mo->hnext, mo);
|
||||
}
|
||||
}
|
||||
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT))
|
||||
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
K_ThrowKartItem(player, false, MT_SSMINE, 1, 1, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->itemamount--;
|
||||
player->pflags &= ~PF_ITEMOUT;
|
||||
player->itemflags &= ~IF_ITEMOUT;
|
||||
K_UpdateHnextList(player, true);
|
||||
}
|
||||
break;
|
||||
|
|
@ -11486,12 +11493,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
P_SetTarget(&player->mo->hnext, mo);
|
||||
}
|
||||
}
|
||||
else if (ATTACK_IS_DOWN && (player->pflags & PF_ITEMOUT))
|
||||
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
K_ThrowKartItem(player, (player->throwdir > 0), MT_DROPTARGET, -1, 0, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->itemamount--;
|
||||
player->pflags &= ~PF_ITEMOUT;
|
||||
player->itemflags &= ~IF_ITEMOUT;
|
||||
K_UpdateHnextList(player, true);
|
||||
}
|
||||
break;
|
||||
|
|
@ -11500,8 +11507,19 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
INT32 ballhogmax = (player->itemamount) * BALLHOGINCREMENT;
|
||||
|
||||
if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY)
|
||||
&& (player->ballhogcharge < ballhogmax))
|
||||
// This construct looks a little goofy, but we're basically just
|
||||
// trying to prevent rapid taps from restarting a charge, while
|
||||
// still allowing quick tapfire.
|
||||
// (The player still has to pace their shots like this, it's not
|
||||
// semi-auto, but that's probably kind of okay.)
|
||||
if (player->ballhogcharge && !(cmd->buttons & BT_ATTACK))
|
||||
player->ballhogtap = true;
|
||||
|
||||
if (player->ballhogcharge == 0)
|
||||
player->ballhogtap = false;
|
||||
|
||||
boolean realcharge = (cmd->buttons & BT_ATTACK) && (player->itemflags & IF_HOLDREADY) && (player->ballhogcharge < ballhogmax);
|
||||
if ((realcharge && !player->ballhogtap) || (player->ballhogtap && player->ballhogcharge < BALLHOGINCREMENT))
|
||||
{
|
||||
player->ballhogcharge++;
|
||||
if (player->ballhogcharge % BALLHOGINCREMENT == 0)
|
||||
|
|
@ -11523,11 +11541,11 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
if (cmd->buttons & BT_ATTACK)
|
||||
{
|
||||
player->pflags &= ~PF_HOLDREADY;
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->pflags |= PF_HOLDREADY;
|
||||
player->itemflags |= IF_HOLDREADY;
|
||||
}
|
||||
|
||||
if (player->ballhogcharge > 0)
|
||||
|
|
@ -11563,7 +11581,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
}
|
||||
|
||||
player->ballhogcharge = 0;
|
||||
player->pflags &= ~PF_HOLDREADY;
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11697,7 +11715,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
|
||||
if (!HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY))
|
||||
if ((cmd->buttons & BT_ATTACK) && (player->itemflags & IF_HOLDREADY))
|
||||
{
|
||||
if (player->bubbleblowup == 0)
|
||||
S_StartSound(player->mo, sfx_s3k75);
|
||||
|
|
@ -11717,7 +11735,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
K_PlayAttackTaunt(player->mo);
|
||||
player->bubbleblowup = 0;
|
||||
player->bubblecool = 0;
|
||||
player->pflags &= ~PF_HOLDREADY;
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
player->itemamount--;
|
||||
}
|
||||
}
|
||||
|
|
@ -11730,9 +11748,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->bubbleblowup--;
|
||||
|
||||
if (player->bubblecool)
|
||||
player->pflags &= ~PF_HOLDREADY;
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
else
|
||||
player->pflags |= PF_HOLDREADY;
|
||||
player->itemflags |= IF_HOLDREADY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -11756,7 +11774,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
|
||||
flamemax = player->flamelength + TICRATE; // TICRATE leniency period, but we block most effects at flamelength 0 down below
|
||||
|
||||
if ((cmd->buttons & BT_ATTACK) && (player->pflags & PF_HOLDREADY))
|
||||
if ((cmd->buttons & BT_ATTACK) && (player->itemflags & IF_HOLDREADY))
|
||||
{
|
||||
const INT32 incr = (gametyperules & GTR_CLOSERPLAYERS) ? 4 : 2;
|
||||
player->flamemeter += incr;
|
||||
|
|
@ -11798,13 +11816,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
|
||||
player->flamemeter = 0;
|
||||
player->flamelength = 0;
|
||||
player->pflags &= ~PF_HOLDREADY;
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
player->itemamount--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player->pflags |= PF_HOLDREADY;
|
||||
player->itemflags |= IF_HOLDREADY;
|
||||
|
||||
if (!(gametyperules & GTR_CLOSERPLAYERS) || leveltime % 6 == 0)
|
||||
{
|
||||
|
|
@ -11874,12 +11892,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
P_SetTarget(&player->mo->hnext, mo);
|
||||
}
|
||||
}
|
||||
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->pflags & PF_ITEMOUT)) // Sink thrown
|
||||
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Sink thrown
|
||||
{
|
||||
K_ThrowKartItem(player, false, MT_SINK, 1, 2, 0);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->itemamount--;
|
||||
player->pflags &= ~PF_ITEMOUT;
|
||||
player->itemflags &= ~IF_ITEMOUT;
|
||||
K_UpdateHnextList(player, true);
|
||||
}
|
||||
break;
|
||||
|
|
@ -11914,7 +11932,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
// No more!
|
||||
if (!player->itemamount)
|
||||
{
|
||||
player->pflags &= ~PF_ITEMOUT;
|
||||
player->itemflags &= ~IF_ITEMOUT;
|
||||
player->itemtype = KITEM_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -12009,7 +12027,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->trickpanel = 0;
|
||||
K_trickPanelTimingVisual(player, momz); // fail trick visual
|
||||
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
|
||||
if (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT))
|
||||
if (player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT))
|
||||
{
|
||||
//K_PopPlayerShield(player); // shield is just being yeeted, don't pop
|
||||
K_DropHnextList(player);
|
||||
|
|
@ -12618,4 +12636,10 @@ void K_SetTireGrease(player_t *player, tic_t tics)
|
|||
player->tiregrease = tics;
|
||||
}
|
||||
|
||||
// somewhat sensible check for HUD sounds in a post-bot-takeover world
|
||||
boolean K_IsPlayingDisplayPlayer(player_t *player)
|
||||
{
|
||||
return P_IsDisplayPlayer(player) && (!player->exiting);
|
||||
}
|
||||
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -243,6 +243,8 @@ boolean K_isPlayerInSpecialState(player_t *p);
|
|||
|
||||
void K_SetTireGrease(player_t *player, tic_t tics);
|
||||
|
||||
boolean K_IsPlayingDisplayPlayer(player_t *player);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -979,6 +979,7 @@ extern consvar_t cv_dummyprofilename;
|
|||
extern consvar_t cv_dummyprofileplayername;
|
||||
extern consvar_t cv_dummyprofilekickstart;
|
||||
extern consvar_t cv_dummyprofileautoroulette;
|
||||
extern consvar_t cv_dummyprofilelitesteer;
|
||||
extern consvar_t cv_dummyprofilerumble;
|
||||
|
||||
void M_ResetOptions(void);
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ void Obj_UpdateRingShooterFace(mobj_t *part);
|
|||
|
||||
/* Follower Audience */
|
||||
void Obj_AudienceInit(mobj_t * mobj, mapthing_t *mthing, INT32 followerpick);
|
||||
void Obj_AudienceThink(mobj_t * mobj, boolean focusonplayer);
|
||||
void Obj_AudienceThink(mobj_t * mobj, boolean focusonplayer, boolean checkdeathpit);
|
||||
|
||||
/* Random Item Boxes */
|
||||
void Obj_RandomItemVisuals(mobj_t *mobj);
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ profile_t* PR_MakeProfile(
|
|||
new->followercolor = fcol;
|
||||
new->kickstartaccel = false;
|
||||
new->autoroulette = false;
|
||||
new->litesteer = true;
|
||||
|
||||
// Copy from gamecontrol directly as we'll be setting controls up directly in the profile.
|
||||
memcpy(new->controls, controlarray, sizeof(new->controls));
|
||||
|
|
@ -89,6 +90,7 @@ profile_t* PR_MakeProfileFromPlayer(const char *prname, const char *pname, const
|
|||
// Player bound cvars:
|
||||
new->kickstartaccel = cv_kickstartaccel[pnum].value;
|
||||
new->autoroulette = cv_autoroulette[pnum].value;
|
||||
new->litesteer = cv_litesteer[pnum].value;
|
||||
new->rumble = cv_rumble[pnum].value;
|
||||
|
||||
return new;
|
||||
|
|
@ -276,6 +278,7 @@ void PR_SaveProfiles(void)
|
|||
// Consvars.
|
||||
WRITEUINT8(save.p, profilesList[i]->kickstartaccel);
|
||||
WRITEUINT8(save.p, profilesList[i]->autoroulette);
|
||||
WRITEUINT8(save.p, profilesList[i]->litesteer);
|
||||
WRITEUINT8(save.p, profilesList[i]->rumble);
|
||||
|
||||
// Controls.
|
||||
|
|
@ -425,6 +428,17 @@ void PR_LoadProfiles(void)
|
|||
profilesList[i]->autoroulette = (boolean)READUINT8(save.p);
|
||||
}
|
||||
|
||||
// 7->8, add litesteer
|
||||
if (version < 8)
|
||||
{
|
||||
profilesList[i]->litesteer = true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
profilesList[i]->litesteer = (boolean)READUINT8(save.p);
|
||||
}
|
||||
|
||||
if (version < 4)
|
||||
{
|
||||
profilesList[i]->rumble = true;
|
||||
|
|
@ -478,6 +492,7 @@ static void PR_ApplyProfile_Settings(profile_t *p, UINT8 playernum)
|
|||
// toggles
|
||||
CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel);
|
||||
CV_StealthSetValue(&cv_autoroulette[playernum], p->autoroulette);
|
||||
CV_StealthSetValue(&cv_litesteer[playernum], p->litesteer);
|
||||
|
||||
// set controls...
|
||||
memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault));
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ extern "C" {
|
|||
#define SKINNAMESIZE 16
|
||||
|
||||
#define PROFILENAMELEN 6
|
||||
#define PROFILEVER 7
|
||||
#define PROFILEVER 8
|
||||
#define MAXPROFILES 16
|
||||
#define PROFILESFILE "ringprofiles.prf"
|
||||
#define PROFILE_GUEST 0
|
||||
|
|
@ -74,7 +74,8 @@ struct profile_t
|
|||
// Player-specific consvars.
|
||||
// @TODO: List all of those
|
||||
boolean kickstartaccel; // cv_kickstartaccel
|
||||
boolean autoroulette; // cv_autoroulette
|
||||
boolean autoroulette; // cv_autoroulette
|
||||
boolean litesteer; // cv_litesteer
|
||||
boolean rumble; // cv_rumble
|
||||
|
||||
// Finally, control data itself
|
||||
|
|
|
|||
|
|
@ -1622,7 +1622,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
// If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item.
|
||||
// I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think.
|
||||
// Finally, if you get past this check, now you can actually start calculating what item you get.
|
||||
if (confirmItem == true && (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT|PF_USERINGS)) == 0)
|
||||
if (confirmItem == true && (player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT|IF_USERINGS)) == 0)
|
||||
{
|
||||
if (roulette->eggman == true)
|
||||
{
|
||||
|
|
@ -1633,7 +1633,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
//player->karthud[khud_itemblinkmode] = 1;
|
||||
//player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT);
|
||||
|
||||
if (P_IsDisplayPlayer(player))
|
||||
if (K_IsPlayingDisplayPlayer(player))
|
||||
{
|
||||
S_StartSound(NULL, sfx_itrole);
|
||||
}
|
||||
|
|
@ -1680,7 +1680,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
player->karthud[khud_itemblinkmode] = 0;
|
||||
player->karthud[khud_rouletteoffset] = K_GetRouletteOffset(roulette, FRACUNIT);
|
||||
|
||||
if (P_IsDisplayPlayer(player))
|
||||
if (K_IsPlayingDisplayPlayer(player))
|
||||
{
|
||||
if (roulette->ringbox)
|
||||
{
|
||||
|
|
@ -1714,7 +1714,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
// This makes the roulette produce the random noises.
|
||||
roulette->sound = (roulette->sound + 1) % 8;
|
||||
|
||||
if (P_IsDisplayPlayer(player))
|
||||
if (K_IsPlayingDisplayPlayer(player))
|
||||
{
|
||||
if (roulette->ringbox)
|
||||
S_StartSound(NULL, sfx_s240);
|
||||
|
|
|
|||
|
|
@ -259,6 +259,8 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->ringboxdelay);
|
||||
else if (fastcmp(field,"ringboxaward"))
|
||||
lua_pushinteger(L, plr->ringboxaward);
|
||||
else if (fastcmp(field,"itemflags"))
|
||||
lua_pushinteger(L, plr->itemflags);
|
||||
else if (fastcmp(field,"drift"))
|
||||
lua_pushinteger(L, plr->drift);
|
||||
else if (fastcmp(field,"driftcharge"))
|
||||
|
|
@ -337,6 +339,8 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->instaWhipCooldown);
|
||||
else if (fastcmp(field,"guardCooldown"))
|
||||
lua_pushinteger(L, plr->guardCooldown);
|
||||
else if (fastcmp(field,"preventfailsafe"))
|
||||
lua_pushinteger(L, plr->preventfailsafe);
|
||||
/*
|
||||
else if (fastcmp(field,"itemroulette"))
|
||||
lua_pushinteger(L, plr->itemroulette);
|
||||
|
|
@ -381,6 +385,8 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->flamelength);
|
||||
else if (fastcmp(field,"ballhogcharge"))
|
||||
lua_pushinteger(L, plr->ballhogcharge);
|
||||
else if (fastcmp(field,"ballhogtap"))
|
||||
lua_pushinteger(L, plr->ballhogtap);
|
||||
else if (fastcmp(field,"hyudorotimer"))
|
||||
lua_pushinteger(L, plr->hyudorotimer);
|
||||
else if (fastcmp(field,"stealingtimer"))
|
||||
|
|
@ -741,6 +747,8 @@ static int player_set(lua_State *L)
|
|||
plr->ringboxdelay = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"ringboxaward"))
|
||||
plr->ringboxaward = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"itemflags"))
|
||||
plr->itemflags = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"drift"))
|
||||
plr->drift = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"driftcharge"))
|
||||
|
|
@ -819,6 +827,8 @@ static int player_set(lua_State *L)
|
|||
plr->instaWhipCharge = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"guardCooldown"))
|
||||
plr->guardCooldown = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"preventfailsafe"))
|
||||
plr->preventfailsafe = luaL_checkinteger(L, 3);
|
||||
/*
|
||||
else if (fastcmp(field,"itemroulette"))
|
||||
plr->itemroulette = luaL_checkinteger(L, 3);
|
||||
|
|
@ -863,6 +873,8 @@ static int player_set(lua_State *L)
|
|||
plr->flamelength = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"ballhogcharge"))
|
||||
plr->ballhogcharge = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"ballhogtap"))
|
||||
plr->ballhogtap = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"hyudorotimer"))
|
||||
plr->hyudorotimer = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"stealingtimer"))
|
||||
|
|
|
|||
107
src/math/fixed.hpp
Normal file
107
src/math/fixed.hpp
Normal 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
102
src/math/line_equation.hpp
Normal 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
43
src/math/line_segment.hpp
Normal 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
34
src/math/traits.hpp
Normal 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
84
src/math/vec.hpp
Normal 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*/
|
||||
|
|
@ -89,6 +89,7 @@ static void M_StartEditProfile(INT32 c)
|
|||
CV_StealthSet(&cv_dummyprofileplayername, optionsmenu.profile->playername);
|
||||
CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel);
|
||||
CV_StealthSetValue(&cv_dummyprofileautoroulette, optionsmenu.profile->autoroulette);
|
||||
CV_StealthSetValue(&cv_dummyprofilelitesteer, optionsmenu.profile->litesteer);
|
||||
CV_StealthSetValue(&cv_dummyprofilerumble, optionsmenu.profile->rumble);
|
||||
}
|
||||
else
|
||||
|
|
@ -97,6 +98,7 @@ static void M_StartEditProfile(INT32 c)
|
|||
CV_StealthSet(&cv_dummyprofileplayername, "");
|
||||
CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off
|
||||
CV_StealthSetValue(&cv_dummyprofileautoroulette, 0); // off
|
||||
CV_StealthSetValue(&cv_dummyprofilelitesteer, 1); // on
|
||||
CV_StealthSetValue(&cv_dummyprofilerumble, 1); // on
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ menuitem_t OPTIONS_ProfileControls[] = {
|
|||
{IT_CONTROL | IT_CVAR, "AUTO ROULETTE", "Item roulette auto-stops on a random result.",
|
||||
NULL, {.cvar = &cv_dummyprofileautoroulette}, 0, 0},
|
||||
|
||||
{IT_CONTROL | IT_CVAR, "LITE STEER", "Hold DOWN on d-pad/keyboard for shallow turns.",
|
||||
NULL, {.cvar = &cv_dummyprofilelitesteer}, 0, 0},
|
||||
|
||||
{IT_HEADER, "EXTRA", "",
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
|
|
@ -191,6 +194,7 @@ static void M_ProfileControlSaveResponse(INT32 choice)
|
|||
// Save the profile
|
||||
optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value;
|
||||
optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value;
|
||||
optionsmenu.profile->litesteer = cv_dummyprofilelitesteer.value;
|
||||
optionsmenu.profile->rumble = cv_dummyprofilerumble.value;
|
||||
memcpy(&optionsmenu.profile->controls, optionsmenu.tempcontrols, sizeof(gamecontroldefault));
|
||||
|
||||
|
|
@ -201,6 +205,7 @@ static void M_ProfileControlSaveResponse(INT32 choice)
|
|||
memcpy(&gamecontrol[belongsto], optionsmenu.tempcontrols, sizeof(gamecontroldefault));
|
||||
CV_SetValue(&cv_kickstartaccel[belongsto], cv_dummyprofilekickstart.value);
|
||||
CV_SetValue(&cv_autoroulette[belongsto], cv_dummyprofileautoroulette.value);
|
||||
CV_SetValue(&cv_litesteer[belongsto], cv_dummyprofilelitesteer.value);
|
||||
CV_SetValue(&cv_rumble[belongsto], cv_dummyprofilerumble.value);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ Obj_AudienceInit
|
|||
mapthing_t *mthing,
|
||||
INT32 followerpick)
|
||||
{
|
||||
const boolean ourchoiceofvisuals = (followerpick < 0 || followerpick > numfollowers);
|
||||
INT16 *reflist = NULL;
|
||||
INT16 tempreflist[MAXHEADERFOLLOWERS];
|
||||
UINT8 numref = 0;
|
||||
|
|
@ -36,9 +37,9 @@ Obj_AudienceInit
|
|||
audience_mainstate(mobj) = S_NULL;
|
||||
|
||||
// Pick follower
|
||||
if (mthing != NULL)
|
||||
if (ourchoiceofvisuals == true)
|
||||
{
|
||||
if (mthing->thing_stringargs[0] != NULL)
|
||||
if (mthing != NULL && mthing->thing_stringargs[0] != NULL)
|
||||
{
|
||||
// From mapthing
|
||||
char *stringcopy = Z_StrDup(mthing->thing_stringargs[0]);
|
||||
|
|
@ -56,11 +57,23 @@ Obj_AudienceInit
|
|||
*c = ' ';
|
||||
}
|
||||
|
||||
if ((tempreflist[numref++] = K_FollowerAvailable(tok)) == -1)
|
||||
if ((tempreflist[numref] = K_FollowerAvailable(tok)) == -1)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, "Mapthing %s: Follower \"%s\" is invalid!\n", sizeu1(mthing-mapthings), tok);
|
||||
}
|
||||
else
|
||||
numref++;
|
||||
|
||||
tok = strtok(NULL, " ,");
|
||||
}
|
||||
|
||||
if (!numref)
|
||||
{
|
||||
// This is the one thing a user should definitely be told about.
|
||||
CONS_Alert(CONS_WARNING, "Mapthing %s: Follower audience has no valid followers to pick from!\n", sizeu1(mthing-mapthings));
|
||||
// DO NOT RETURN HERE
|
||||
}
|
||||
|
||||
Z_Free(stringcopy);
|
||||
|
||||
reflist = tempreflist;
|
||||
|
|
@ -81,8 +94,8 @@ Obj_AudienceInit
|
|||
|
||||
if (!numref || !reflist)
|
||||
{
|
||||
// This is the one thing a user should definitely be told about.
|
||||
CONS_Alert(CONS_WARNING, "Mapthing %s: Follower audience has no valid followers to pick from!\n", sizeu1(mthing-mapthings));
|
||||
// Clean up after ourselves.
|
||||
P_RemoveMobj(mobj);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -137,11 +150,11 @@ Obj_AudienceInit
|
|||
}
|
||||
|
||||
// Handle colors
|
||||
if (mthing != NULL)
|
||||
if (ourchoiceofvisuals == true)
|
||||
{
|
||||
UINT16 colorpick = SKINCOLOR_NONE;
|
||||
|
||||
if (mthing->thing_stringargs[1] != NULL)
|
||||
if (mthing != NULL && mthing->thing_stringargs[1] != NULL)
|
||||
{
|
||||
if (!stricmp("Random", mthing->thing_stringargs[1]))
|
||||
{
|
||||
|
|
@ -189,8 +202,17 @@ Obj_AudienceInit
|
|||
void
|
||||
Obj_AudienceThink
|
||||
( mobj_t * mobj,
|
||||
boolean focusonplayer)
|
||||
boolean focusonplayer,
|
||||
boolean checkdeathpit)
|
||||
{
|
||||
boolean landed = false;
|
||||
|
||||
if (mobj->fuse && mobj->fuse < (TICRATE/2))
|
||||
{
|
||||
mobj->renderflags ^= RF_DONTDRAW;
|
||||
return; // no jumping when you hit the floor, your gravity is weird
|
||||
}
|
||||
|
||||
if (audience_mainstate(mobj) == S_NULL)
|
||||
{
|
||||
// Uninitialised, don't do anything funny.
|
||||
|
|
@ -315,15 +337,23 @@ Obj_AudienceThink
|
|||
}
|
||||
else if (mobj->flags2 & MF2_OBJECTFLIP)
|
||||
{
|
||||
if (mobj->z + mobj->height >= mobj->ceilingz)
|
||||
{
|
||||
mobj->momz = -audience_bobamp(mobj);
|
||||
P_SetMobjState(mobj, audience_mainstate(mobj));
|
||||
}
|
||||
landed = (mobj->z + mobj->height >= mobj->ceilingz);
|
||||
}
|
||||
else if (mobj->z <= mobj->floorz)
|
||||
else
|
||||
{
|
||||
mobj->momz = audience_bobamp(mobj);
|
||||
landed = (mobj->z <= mobj->floorz);
|
||||
}
|
||||
|
||||
if (landed == true)
|
||||
{
|
||||
if (checkdeathpit && P_CheckDeathPitCollide(mobj))
|
||||
{
|
||||
P_RemoveMobj(mobj);
|
||||
return;
|
||||
}
|
||||
|
||||
mobj->momx = mobj->momy = 0;
|
||||
mobj->momz = P_MobjFlip(mobj)*audience_bobamp(mobj);
|
||||
P_SetMobjState(mobj, audience_mainstate(mobj));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -295,6 +295,7 @@ Obj_LoopEndpointCollide
|
|||
{
|
||||
player_t *player = toucher->player;
|
||||
sonicloopvars_t *s = &player->loop;
|
||||
sonicloopcamvars_t *cam = &s->camera;
|
||||
|
||||
mobj_t *anchor = end_anchor(end);
|
||||
mobj_t *center = anchor ? anchor_center(anchor) : NULL;
|
||||
|
|
@ -352,6 +353,30 @@ Obj_LoopEndpointCollide
|
|||
|
||||
s->flip = center_has_flip(center);
|
||||
|
||||
cam->enter_tic = leveltime;
|
||||
cam->exit_tic = INFTICS;
|
||||
|
||||
if (center->thing_args[4]) // is camera distance set?
|
||||
{
|
||||
cam->zoom_out_speed = center->thing_args[2];
|
||||
cam->zoom_in_speed = center->thing_args[3];
|
||||
cam->dist = center->thing_args[4] * FRACUNIT;
|
||||
cam->pan = FixedAngle(center->thing_args[5] * FRACUNIT);
|
||||
cam->pan_speed = center->thing_args[6] * FRACUNIT;
|
||||
cam->pan_accel = center->thing_args[7];
|
||||
cam->pan_back = center->thing_args[8];
|
||||
}
|
||||
else
|
||||
{
|
||||
cam->zoom_out_speed = 20;
|
||||
cam->zoom_in_speed = 60;
|
||||
cam->dist = radius;
|
||||
cam->pan = ANGLE_22h;
|
||||
cam->pan_speed = 6*FRACUNIT;
|
||||
cam->pan_accel = 10;
|
||||
cam->pan_back = 40;
|
||||
}
|
||||
|
||||
player->speed =
|
||||
3 * (player->speed + toucher->momz) / 2;
|
||||
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|
|||
if (player->itemRoulette.active == true
|
||||
|| player->ringboxdelay > 0
|
||||
|| (weapon != 3 && player->itemamount)
|
||||
|| (player->pflags & PF_ITEMOUT))
|
||||
|| (player->itemflags & IF_ITEMOUT))
|
||||
return false;
|
||||
|
||||
if (weapon == 3 && K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE)
|
||||
|
|
@ -1577,10 +1577,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
// I wish I knew a better way to do this
|
||||
if (target->target && target->target->player && target->target->player->mo)
|
||||
{
|
||||
if ((target->target->player->pflags & PF_EGGMANOUT) && target->type == MT_EGGMANITEM_SHIELD)
|
||||
target->target->player->pflags &= ~PF_EGGMANOUT;
|
||||
if ((target->target->player->itemflags & IF_EGGMANOUT) && target->type == MT_EGGMANITEM_SHIELD)
|
||||
target->target->player->itemflags &= ~IF_EGGMANOUT;
|
||||
|
||||
if (target->target->player->pflags & PF_ITEMOUT)
|
||||
if (target->target->player->itemflags & IF_ITEMOUT)
|
||||
{
|
||||
if ((target->type == MT_BANANA_SHIELD && target->target->player->itemtype == KITEM_BANANA) // trail items
|
||||
|| (target->type == MT_SSMINE_SHIELD && target->target->player->itemtype == KITEM_MINE)
|
||||
|
|
@ -1608,7 +1608,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
}
|
||||
|
||||
if (!target->target->player->itemamount)
|
||||
target->target->player->pflags &= ~PF_ITEMOUT;
|
||||
target->target->player->itemflags &= ~IF_ITEMOUT;
|
||||
|
||||
if (target->target->hnext == target)
|
||||
P_SetTarget(&target->target->hnext, NULL);
|
||||
|
|
@ -2103,6 +2103,67 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
cur = cur->hnext;
|
||||
}
|
||||
|
||||
// Spawn three Followers (if possible)
|
||||
if (mapheaderinfo[gamemap-1]->numFollowers)
|
||||
{
|
||||
dir = FixedAngle(P_RandomKey(PR_RANDOMAUDIENCE, 360)*FRACUNIT);
|
||||
|
||||
const fixed_t launchmomentum = 7 * mapobjectscale;
|
||||
const fixed_t jaggedness = 4;
|
||||
angle_t launchangle;
|
||||
UINT8 i;
|
||||
for (i = 0; i < 6; i++, dir += ANG60)
|
||||
{
|
||||
cur = P_SpawnMobj(
|
||||
target->x, target->y,
|
||||
target->z + target->height/2,
|
||||
MT_RANDOMAUDIENCE
|
||||
);
|
||||
|
||||
// We check if you have some horrible Lua
|
||||
if (P_MobjWasRemoved(cur))
|
||||
break;
|
||||
|
||||
Obj_AudienceInit(cur, NULL, -1);
|
||||
|
||||
// We check again if the list is invalid
|
||||
if (P_MobjWasRemoved(cur))
|
||||
break;
|
||||
|
||||
cur->hitlag = target->hitlag;
|
||||
|
||||
cur->destscale /= 2;
|
||||
P_SetScale(cur, cur->destscale/TICRATE);
|
||||
cur->scalespeed = cur->destscale/TICRATE;
|
||||
cur->z -= cur->height/2;
|
||||
|
||||
// flags are NOT from the target - just in case it's just been placed on the ceiling as a gimmick
|
||||
cur->flags2 |= (source->flags2 & MF2_OBJECTFLIP);
|
||||
cur->eflags |= (source->eflags & MFE_VERTICALFLIP);
|
||||
|
||||
launchangle = FixedAngle(
|
||||
(
|
||||
(
|
||||
P_RandomRange(PR_RANDOMAUDIENCE, 12/jaggedness, 24/jaggedness) * jaggedness
|
||||
) + (i & 1)*16
|
||||
) * FRACUNIT
|
||||
);
|
||||
|
||||
cur->momz = P_MobjFlip(target) // THIS one uses target!
|
||||
* P_ReturnThrustY(cur, launchangle, launchmomentum);
|
||||
|
||||
cur->angle = dir;
|
||||
|
||||
P_InstaThrust(
|
||||
cur, cur->angle,
|
||||
P_ReturnThrustX(cur, launchangle, launchmomentum)
|
||||
);
|
||||
|
||||
cur->fuse = (3*TICRATE)/2;
|
||||
cur->flags |= MF_NOCLIPHEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
S_StartSound(target, sfx_mbs60);
|
||||
|
||||
P_AddBrokenPrison(target, inflictor, source);
|
||||
|
|
@ -3046,6 +3107,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
player->fastfall = 0;
|
||||
player->ringboost = 0;
|
||||
player->glanceDir = 0;
|
||||
player->preventfailsafe = TICRATE*3;
|
||||
player->pflags &= ~PF_GAINAX;
|
||||
|
||||
if (player->spectator == false && !(player->charflags & SF_IRONMAN))
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ typedef enum
|
|||
NUM_THINKERLISTS
|
||||
} thinklistnum_t; /**< Thinker lists. */
|
||||
extern thinker_t thlist[];
|
||||
extern mobj_t *mobjcache;
|
||||
|
||||
void P_InitThinkers(void);
|
||||
void P_InvalidateThinkersWithoutInit(void);
|
||||
|
|
@ -387,9 +388,16 @@ struct tm_t
|
|||
// so missiles don't explode against sky hack walls
|
||||
line_t *ceilingline;
|
||||
|
||||
// set by PIT_CheckLine() for any line that stopped the PIT_CheckLine()
|
||||
// that is, for any line which is 'solid'
|
||||
line_t *blockingline;
|
||||
// P_CheckPosition: this position blocks movement
|
||||
boolean blocking;
|
||||
|
||||
// P_CheckPosition: set this before each call to
|
||||
// P_CheckPosition to enable a line sweep on collided
|
||||
// lines
|
||||
boolean sweep;
|
||||
|
||||
// sweep: max step up at tm.x, tm.y
|
||||
fixed_t maxstep;
|
||||
};
|
||||
|
||||
extern tm_t tm;
|
||||
|
|
@ -415,6 +423,7 @@ struct TryMoveResult_t
|
|||
boolean success;
|
||||
line_t *line;
|
||||
mobj_t *mo;
|
||||
vector2_t normal;
|
||||
};
|
||||
|
||||
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *result);
|
||||
|
|
@ -422,6 +431,10 @@ boolean P_CheckMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, T
|
|||
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff, TryMoveResult_t *result);
|
||||
boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *result);
|
||||
|
||||
void P_TestLine(line_t *ld);
|
||||
void P_ClearTestLines(void);
|
||||
line_t *P_SweepTestLines(fixed_t ax, fixed_t ay, fixed_t bx, fixed_t by, fixed_t r, vector2_t *return_normal);
|
||||
|
||||
boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing);
|
||||
boolean P_IsLineTripWire(const line_t *ld);
|
||||
boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ void P_HaltPlayerOrbit(player_t *player)
|
|||
player->mo->flags &= ~(MF_NOCLIPHEIGHT);
|
||||
|
||||
player->loop.radius = 0;
|
||||
player->loop.camera.exit_tic = leveltime;
|
||||
}
|
||||
|
||||
void P_ExitPlayerOrbit(player_t *player)
|
||||
|
|
|
|||
190
src/p_map.c
190
src/p_map.c
|
|
@ -1770,7 +1770,6 @@ static BlockItReturn_t PIT_CheckCameraLine(line_t *ld)
|
|||
// could be crossed in either order.
|
||||
|
||||
// this line is out of the if so upper and lower textures can be hit by a splat
|
||||
tm.blockingline = ld;
|
||||
if (!ld->backsector) // one sided line
|
||||
{
|
||||
if (P_PointOnLineSide(mapcampointer->x, mapcampointer->y, ld))
|
||||
|
|
@ -1841,6 +1840,22 @@ boolean P_IsLineTripWire(const line_t *ld)
|
|||
return ld->tripwire;
|
||||
}
|
||||
|
||||
static boolean P_UsingStepUp(mobj_t *thing)
|
||||
{
|
||||
if (thing->flags & MF_NOCLIP)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// orbits have no collision
|
||||
if (thing->player && thing->player->loop.radius)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// PIT_CheckLine
|
||||
// Adjusts tm.floorz and tm.ceilingz as lines are contacted
|
||||
|
|
@ -1898,14 +1913,20 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld)
|
|||
// could be crossed in either order.
|
||||
|
||||
// this line is out of the if so upper and lower textures can be hit by a splat
|
||||
tm.blockingline = ld;
|
||||
|
||||
{
|
||||
UINT8 shouldCollide = LUA_HookMobjLineCollide(tm.thing, tm.blockingline); // checks hook for thing's type
|
||||
UINT8 shouldCollide = LUA_HookMobjLineCollide(tm.thing, ld); // checks hook for thing's type
|
||||
if (P_MobjWasRemoved(tm.thing))
|
||||
return BMIT_CONTINUE; // one of them was removed???
|
||||
if (shouldCollide == 1)
|
||||
return BMIT_ABORT; // force collide
|
||||
{
|
||||
if (tm.sweep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
tm.blocking = true; // force collide
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
else if (shouldCollide == 2)
|
||||
return BMIT_CONTINUE; // force no collide
|
||||
}
|
||||
|
|
@ -1914,15 +1935,55 @@ static BlockItReturn_t PIT_CheckLine(line_t *ld)
|
|||
{
|
||||
if (P_PointOnLineSide(tm.thing->x, tm.thing->y, ld))
|
||||
return BMIT_CONTINUE; // don't hit the back side
|
||||
return BMIT_ABORT;
|
||||
|
||||
if (tm.sweep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
tm.blocking = true;
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
if (P_IsLineBlocking(ld, tm.thing))
|
||||
return BMIT_ABORT;
|
||||
{
|
||||
if (tm.sweep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
tm.blocking = true;
|
||||
return BMIT_CONTINUE;
|
||||
}
|
||||
|
||||
// set openrange, opentop, openbottom
|
||||
P_LineOpening(ld, tm.thing, &open);
|
||||
|
||||
if (tm.sweep && P_UsingStepUp(tm.thing))
|
||||
{
|
||||
// copied from P_TryMove
|
||||
// TODO: refactor this into one place
|
||||
if (open.range < tm.thing->height)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
else if (tm.maxstep > 0)
|
||||
{
|
||||
if (tm.thing->z < open.floor)
|
||||
{
|
||||
if (open.floorstep > tm.maxstep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
}
|
||||
else if (open.ceiling < tm.thing->z + tm.thing->height)
|
||||
{
|
||||
if (open.ceilingstep > tm.maxstep)
|
||||
{
|
||||
P_TestLine(ld);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjust floor / ceiling heights
|
||||
if (open.ceiling < tm.ceilingz)
|
||||
{
|
||||
|
|
@ -2042,7 +2103,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re
|
|||
tm.bbox[BOXLEFT] = x - tm.thing->radius;
|
||||
|
||||
newsubsec = R_PointInSubsector(x, y);
|
||||
tm.ceilingline = tm.blockingline = NULL;
|
||||
tm.ceilingline = NULL;
|
||||
tm.blocking = false;
|
||||
|
||||
// The base floor / ceiling is from the subsector
|
||||
// that contains the point.
|
||||
|
|
@ -2314,23 +2376,33 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re
|
|||
|
||||
validcount++;
|
||||
|
||||
P_ClearTestLines();
|
||||
|
||||
// check lines
|
||||
for (bx = xl; bx <= xh; bx++)
|
||||
{
|
||||
for (by = yl; by <= yh; by++)
|
||||
{
|
||||
if (!P_BlockLinesIterator(bx, by, PIT_CheckLine))
|
||||
{
|
||||
blockval = false;
|
||||
}
|
||||
P_BlockLinesIterator(bx, by, PIT_CheckLine);
|
||||
}
|
||||
}
|
||||
|
||||
if (tm.blocking)
|
||||
{
|
||||
blockval = false;
|
||||
}
|
||||
|
||||
if (result != NULL)
|
||||
{
|
||||
result->line = tm.blockingline;
|
||||
result->line = NULL;
|
||||
result->mo = tm.hitthing;
|
||||
}
|
||||
else
|
||||
{
|
||||
P_ClearTestLines();
|
||||
}
|
||||
|
||||
tm.sweep = false;
|
||||
|
||||
return blockval;
|
||||
}
|
||||
|
|
@ -2379,7 +2451,7 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
|
|||
tm.bbox[BOXLEFT] = x - thiscam->radius;
|
||||
|
||||
newsubsec = R_PointInSubsector(x, y);
|
||||
tm.ceilingline = tm.blockingline = NULL;
|
||||
tm.ceilingline = NULL;
|
||||
|
||||
mapcampointer = thiscam;
|
||||
|
||||
|
|
@ -2753,22 +2825,6 @@ fixed_t P_GetThingStepUp(mobj_t *thing, fixed_t destX, fixed_t destY)
|
|||
return maxstep;
|
||||
}
|
||||
|
||||
static boolean P_UsingStepUp(mobj_t *thing)
|
||||
{
|
||||
if (thing->flags & MF_NOCLIP)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// orbits have no collision
|
||||
if (thing->player && thing->player->loop.radius)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean
|
||||
increment_move
|
||||
( mobj_t * thing,
|
||||
|
|
@ -2821,7 +2877,29 @@ increment_move
|
|||
tryy = y;
|
||||
}
|
||||
|
||||
if (!P_CheckPosition(thing, tryx, tryy, result))
|
||||
if (P_UsingStepUp(thing))
|
||||
{
|
||||
tm.maxstep = P_GetThingStepUp(thing, tryx, tryy);
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
tm.sweep = true;
|
||||
}
|
||||
|
||||
boolean move_ok = P_CheckPosition(thing, tryx, tryy, result);
|
||||
|
||||
if (P_MobjWasRemoved(thing))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
{
|
||||
result->line = P_SweepTestLines(thing->x, thing->y, x, y, thing->radius, &result->normal);
|
||||
}
|
||||
|
||||
if (!move_ok)
|
||||
{
|
||||
return false; // solid wall or thing
|
||||
}
|
||||
|
|
@ -3466,30 +3544,27 @@ static void P_HitSlideLine(line_t *ld)
|
|||
//
|
||||
// HitBounceLine, for players
|
||||
//
|
||||
static void P_PlayerHitBounceLine(line_t *ld)
|
||||
static void P_PlayerHitBounceLine(line_t *ld, vector2_t* normal)
|
||||
{
|
||||
INT32 side;
|
||||
angle_t lineangle;
|
||||
fixed_t movelen;
|
||||
fixed_t x, y;
|
||||
|
||||
side = P_PointOnLineSide(slidemo->x, slidemo->y, ld);
|
||||
lineangle = ld->angle - ANGLE_90;
|
||||
|
||||
if (side == 1)
|
||||
lineangle += ANGLE_180;
|
||||
|
||||
lineangle >>= ANGLETOFINESHIFT;
|
||||
|
||||
movelen = P_AproxDistance(tmxmove, tmymove);
|
||||
|
||||
if (slidemo->player && movelen < (15*mapobjectscale))
|
||||
movelen = (15*mapobjectscale);
|
||||
|
||||
x = FixedMul(movelen, FINECOSINE(lineangle));
|
||||
y = FixedMul(movelen, FINESINE(lineangle));
|
||||
if (!ld)
|
||||
{
|
||||
angle_t th = R_PointToAngle2(0, 0, tmxmove, tmymove);
|
||||
normal->x = -FCOS(th);
|
||||
normal->y = -FSIN(th);
|
||||
}
|
||||
|
||||
if (P_IsLineTripWire(ld))
|
||||
x = FixedMul(movelen, normal->x);
|
||||
y = FixedMul(movelen, normal->y);
|
||||
|
||||
if (ld && P_IsLineTripWire(ld))
|
||||
{
|
||||
tmxmove = x * 4;
|
||||
tmymove = y * 4;
|
||||
|
|
@ -3958,6 +4033,8 @@ papercollision:
|
|||
|
||||
static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
||||
{
|
||||
extern consvar_t cv_showgremlins;
|
||||
|
||||
fixed_t mmomx = 0, mmomy = 0;
|
||||
fixed_t oldmomx = mo->momx, oldmomy = mo->momy;
|
||||
|
||||
|
|
@ -3982,8 +4059,23 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
slidemo = mo;
|
||||
bestslideline = result->line;
|
||||
|
||||
if (bestslideline == NULL)
|
||||
return;
|
||||
if (bestslideline == NULL && cv_showgremlins.value)
|
||||
{
|
||||
// debug
|
||||
mobj_t*x = P_SpawnMobj(mo->x, mo->y, mo->z, MT_THOK);
|
||||
x->frame = FF_FULLBRIGHT | FF_ADD;
|
||||
x->renderflags = RF_ALWAYSONTOP;
|
||||
x->color = SKINCOLOR_RED;
|
||||
|
||||
CONS_Printf(
|
||||
"GREMLIN: leveltime=%u x=%f y=%f z=%f angle=%f\n",
|
||||
leveltime,
|
||||
FixedToFloat(mo->x),
|
||||
FixedToFloat(mo->y),
|
||||
FixedToFloat(mo->z),
|
||||
AngleToFloat(R_PointToAngle2(0, 0, oldmomx, oldmomy))
|
||||
);
|
||||
}
|
||||
|
||||
if (mo->eflags & MFE_JUSTBOUNCEDWALL) // Stronger push-out
|
||||
{
|
||||
|
|
@ -3996,7 +4088,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
tmymove = FixedMul(mmomy, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
|
||||
}
|
||||
|
||||
if (P_IsLineTripWire(bestslideline))
|
||||
if (bestslideline && P_IsLineTripWire(bestslideline))
|
||||
{
|
||||
// TRIPWIRE CANNOT BE MADE NONBOUNCY
|
||||
K_ApplyTripWire(mo->player, TRIPSTATE_BLOCKED);
|
||||
|
|
@ -4014,7 +4106,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
K_SpawnBumpEffect(mo);
|
||||
}
|
||||
|
||||
P_PlayerHitBounceLine(bestslideline);
|
||||
P_PlayerHitBounceLine(bestslideline, &result->normal);
|
||||
mo->eflags |= MFE_JUSTBOUNCEDWALL;
|
||||
|
||||
mo->momx = tmxmove;
|
||||
|
|
@ -4022,7 +4114,7 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
mo->player->cmomx = tmxmove;
|
||||
mo->player->cmomy = tmymove;
|
||||
|
||||
if (!P_IsLineTripWire(bestslideline))
|
||||
if (!bestslideline || !P_IsLineTripWire(bestslideline))
|
||||
{
|
||||
if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove, true, NULL))
|
||||
{
|
||||
|
|
|
|||
33
src/p_mobj.c
33
src/p_mobj.c
|
|
@ -63,6 +63,8 @@ mobj_t *waypointcap = NULL;
|
|||
// general purpose.
|
||||
mobj_t *trackercap = NULL;
|
||||
|
||||
mobj_t *mobjcache = NULL;
|
||||
|
||||
void P_InitCachedActions(void)
|
||||
{
|
||||
actioncachehead.prev = actioncachehead.next = &actioncachehead;
|
||||
|
|
@ -1276,6 +1278,10 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case MT_RANDOMAUDIENCE:
|
||||
if (mo->fuse)
|
||||
gravityadd /= 10;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1669,7 +1675,7 @@ void P_XYMovement(mobj_t *mo)
|
|||
// blocked move
|
||||
moved = false;
|
||||
|
||||
if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, tm.blockingline))
|
||||
if (LUA_HookMobjMoveBlocked(mo, tm.hitthing, result.line))
|
||||
{
|
||||
if (P_MobjWasRemoved(mo))
|
||||
return;
|
||||
|
|
@ -1677,7 +1683,7 @@ void P_XYMovement(mobj_t *mo)
|
|||
else if (P_MobjWasRemoved(mo))
|
||||
return;
|
||||
|
||||
P_PushSpecialLine(tm.blockingline, mo);
|
||||
P_PushSpecialLine(result.line, mo);
|
||||
|
||||
if (mo->flags & MF_MISSILE)
|
||||
{
|
||||
|
|
@ -6469,7 +6475,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
|
|||
break;
|
||||
}
|
||||
|
||||
if (mobj->target->player->pflags & PF_ITEMOUT)
|
||||
if (mobj->target->player->itemflags & IF_ITEMOUT)
|
||||
{
|
||||
if (leveltime & 1)
|
||||
mobj->tracer->renderflags &= ~RF_DONTDRAW;
|
||||
|
|
@ -7268,7 +7274,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
|
||||
if (mobj->flags2 & MF2_STRONGBOX)
|
||||
{
|
||||
Obj_AudienceThink(mobj, true);
|
||||
Obj_AudienceThink(mobj, true, false);
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return false;
|
||||
}
|
||||
|
|
@ -10806,7 +10812,9 @@ void P_SceneryThinker(mobj_t *mobj)
|
|||
|
||||
if (mobj->type == MT_RANDOMAUDIENCE)
|
||||
{
|
||||
Obj_AudienceThink(mobj, !!(mobj->flags2 & MF2_AMBUSH));
|
||||
Obj_AudienceThink(mobj, !!(mobj->flags2 & MF2_AMBUSH), !!(mobj->flags2 & MF2_DONTRESPAWN));
|
||||
if (P_MobjWasRemoved(mobj))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10951,7 +10959,16 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
|
|||
type = MT_RAY;
|
||||
}
|
||||
|
||||
mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
|
||||
if (mobjcache != NULL)
|
||||
{
|
||||
mobj = mobjcache;
|
||||
mobjcache = mobjcache->hnext;
|
||||
memset(mobj, 0, sizeof(*mobj));
|
||||
}
|
||||
else
|
||||
{
|
||||
mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL);
|
||||
}
|
||||
|
||||
// this is officially a mobj, declared as soon as possible.
|
||||
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker;
|
||||
|
|
@ -11859,7 +11876,9 @@ void P_RemoveMobj(mobj_t *mobj)
|
|||
INT32 prevreferences;
|
||||
if (!mobj->thinker.references)
|
||||
{
|
||||
Z_Free(mobj); // No refrrences? Can be removed immediately! :D
|
||||
// no references, dump it directly in the mobj cache
|
||||
mobj->hnext = mobjcache;
|
||||
mobjcache = mobj;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -478,6 +478,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].flamelength);
|
||||
|
||||
WRITEUINT16(save->p, players[i].ballhogcharge);
|
||||
WRITEUINT8(save->p, players[i].ballhogtap);
|
||||
|
||||
WRITEUINT16(save->p, players[i].hyudorotimer);
|
||||
WRITESINT8(save->p, players[i].stealingtimer);
|
||||
|
|
@ -547,6 +548,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].instaWhipCooldown);
|
||||
WRITEUINT8(save->p, players[i].guardCooldown);
|
||||
|
||||
WRITEUINT8(save->p, players[i].preventfailsafe);
|
||||
|
||||
WRITEUINT8(save->p, players[i].handtimer);
|
||||
WRITEANGLE(save->p, players[i].besthanddirection);
|
||||
|
||||
|
|
@ -556,6 +559,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
|
||||
WRITEUINT8(save->p, players[i].ringboxdelay);
|
||||
WRITEUINT8(save->p, players[i].ringboxaward);
|
||||
|
||||
WRITEUINT8(save->p, players[i].itemflags);
|
||||
|
||||
WRITEFIXED(save->p, players[i].outrun);
|
||||
|
||||
WRITEUINT8(save->p, players[i].rideroid);
|
||||
|
|
@ -681,6 +687,17 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEFIXED(save->p, players[i].loop.shift.y);
|
||||
WRITEUINT8(save->p, players[i].loop.flip);
|
||||
|
||||
// sonicloopcamvars_t
|
||||
WRITEUINT32(save->p, players[i].loop.camera.enter_tic);
|
||||
WRITEUINT32(save->p, players[i].loop.camera.exit_tic);
|
||||
WRITEUINT32(save->p, players[i].loop.camera.zoom_in_speed);
|
||||
WRITEUINT32(save->p, players[i].loop.camera.zoom_out_speed);
|
||||
WRITEFIXED(save->p, players[i].loop.camera.dist);
|
||||
WRITEANGLE(save->p, players[i].loop.camera.pan);
|
||||
WRITEFIXED(save->p, players[i].loop.camera.pan_speed);
|
||||
WRITEUINT32(save->p, players[i].loop.camera.pan_accel);
|
||||
WRITEUINT32(save->p, players[i].loop.camera.pan_back);
|
||||
|
||||
// ACS has read access to this, so it has to be net-communicated.
|
||||
// It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way.
|
||||
WRITEUINT32(save->p, players[i].roundconditions.unlocktriggers);
|
||||
|
|
@ -992,6 +1009,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].flamelength = READUINT8(save->p);
|
||||
|
||||
players[i].ballhogcharge = READUINT16(save->p);
|
||||
players[i].ballhogtap = READUINT8(save->p);
|
||||
|
||||
players[i].hyudorotimer = READUINT16(save->p);
|
||||
players[i].stealingtimer = READSINT8(save->p);
|
||||
|
|
@ -1061,6 +1079,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].instaWhipCooldown = READUINT8(save->p);
|
||||
players[i].guardCooldown = READUINT8(save->p);
|
||||
|
||||
players[i].preventfailsafe = READUINT8(save->p);
|
||||
|
||||
players[i].handtimer = READUINT8(save->p);
|
||||
players[i].besthanddirection = READANGLE(save->p);
|
||||
|
||||
|
|
@ -1070,6 +1090,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
|
||||
players[i].ringboxdelay = READUINT8(save->p);
|
||||
players[i].ringboxaward = READUINT8(save->p);
|
||||
|
||||
players[i].itemflags = READUINT8(save->p);
|
||||
|
||||
players[i].outrun = READFIXED(save->p);
|
||||
|
||||
players[i].rideroid = (boolean)READUINT8(save->p);
|
||||
|
|
@ -1206,6 +1229,17 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].loop.shift.y = READFIXED(save->p);
|
||||
players[i].loop.flip = READUINT8(save->p);
|
||||
|
||||
// sonicloopcamvars_t
|
||||
players[i].loop.camera.enter_tic = READUINT32(save->p);
|
||||
players[i].loop.camera.exit_tic = READUINT32(save->p);
|
||||
players[i].loop.camera.zoom_in_speed = READUINT32(save->p);
|
||||
players[i].loop.camera.zoom_out_speed = READUINT32(save->p);
|
||||
players[i].loop.camera.dist = READFIXED(save->p);
|
||||
players[i].loop.camera.pan = READANGLE(save->p);
|
||||
players[i].loop.camera.pan_speed = READFIXED(save->p);
|
||||
players[i].loop.camera.pan_accel = READUINT32(save->p);
|
||||
players[i].loop.camera.pan_back = READUINT32(save->p);
|
||||
|
||||
// ACS has read access to this, so it has to be net-communicated.
|
||||
// It is the ONLY roundcondition that is sent over the wire and I'd like it to stay that way.
|
||||
players[i].roundconditions.unlocktriggers = READUINT32(save->p);
|
||||
|
|
|
|||
|
|
@ -8367,6 +8367,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
Patch_FreeTag(PU_PATCH_LOWPRIORITY);
|
||||
Patch_FreeTag(PU_PATCH_ROTATED);
|
||||
Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
|
||||
mobjcache = NULL;
|
||||
|
||||
R_InitializeLevelInterpolators();
|
||||
|
||||
|
|
|
|||
271
src/p_sweep.cpp
Normal file
271
src/p_sweep.cpp
Normal 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
131
src/p_sweep.hpp
Normal 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
78
src/p_test.cpp
Normal 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();
|
||||
}
|
||||
15
src/p_tick.c
15
src/p_tick.c
|
|
@ -323,6 +323,7 @@ void P_AddThinker(const thinklistnum_t n, thinker_t *thinker)
|
|||
thlist[n].prev = thinker;
|
||||
|
||||
thinker->references = 0; // killough 11/98: init reference counter to 0
|
||||
thinker->cachable = n == THINK_MOBJ;
|
||||
|
||||
#ifdef PARANOIA
|
||||
thinker->debug_mobjtype = MT_NULL;
|
||||
|
|
@ -440,7 +441,16 @@ void P_UnlinkThinker(thinker_t *thinker)
|
|||
I_Assert(thinker->references == 0);
|
||||
|
||||
(next->prev = thinker->prev)->next = next;
|
||||
Z_Free(thinker);
|
||||
if (thinker->cachable)
|
||||
{
|
||||
// put cachable thinkers in the mobj cache, so we can avoid allocations
|
||||
((mobj_t *)thinker)->hnext = mobjcache;
|
||||
mobjcache = (mobj_t *)thinker;
|
||||
}
|
||||
else
|
||||
{
|
||||
Z_Free(thinker);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -721,6 +731,9 @@ static inline void P_DeviceRumbleTick(void)
|
|||
if (player->mo == NULL)
|
||||
continue;
|
||||
|
||||
if (player->exiting)
|
||||
continue;
|
||||
|
||||
if ((player->mo->eflags & MFE_DAMAGEHITLAG) && player->mo->hitlag)
|
||||
{
|
||||
low = high = 65536 / 2;
|
||||
|
|
|
|||
75
src/p_user.c
75
src/p_user.c
|
|
@ -3083,6 +3083,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
|
|||
fixed_t scaleDiff;
|
||||
fixed_t cameraScale = mapobjectscale;
|
||||
|
||||
sonicloopcamvars_t *loop = &player->loop.camera;
|
||||
tic_t loop_out = leveltime - loop->enter_tic;
|
||||
tic_t loop_in = max(leveltime, loop->exit_tic) - loop->exit_tic;
|
||||
|
||||
thiscam->old_x = thiscam->x;
|
||||
thiscam->old_y = thiscam->y;
|
||||
thiscam->old_z = thiscam->z;
|
||||
|
|
@ -3207,6 +3211,58 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
|
|||
camdist = FixedMul(cv_cam_dist[num].value, cameraScale);
|
||||
camheight = FixedMul(cv_cam_height[num].value, cameraScale);
|
||||
|
||||
if (loop_in < loop->zoom_in_speed)
|
||||
{
|
||||
fixed_t f = loop_out < loop->zoom_out_speed
|
||||
? (loop_out * FRACUNIT) / loop->zoom_out_speed
|
||||
: FRACUNIT - ((loop_in * FRACUNIT) / loop->zoom_in_speed);
|
||||
|
||||
camspeed -= FixedMul(f, camspeed - (FRACUNIT/10));
|
||||
camdist += FixedMul(f, loop->dist);
|
||||
}
|
||||
|
||||
if (loop_in < max(loop->pan_back, 1))
|
||||
{
|
||||
fixed_t f = (loop_in * FRACUNIT) / max(loop->pan_back, 1);
|
||||
|
||||
fixed_t dx = mo->x - thiscam->x;
|
||||
fixed_t dy = mo->y - thiscam->y;
|
||||
|
||||
angle_t th = R_PointToAngle2(0, 0, dx, dy);
|
||||
fixed_t d = AngleFixed(focusangle - th);
|
||||
|
||||
if (d > 180*FRACUNIT)
|
||||
{
|
||||
d -= (360*FRACUNIT);
|
||||
}
|
||||
|
||||
focusangle = th + FixedAngle(FixedMul(f, d));
|
||||
|
||||
if (loop_in == 0)
|
||||
{
|
||||
focusaiming = R_PointToAngle2(0, thiscam->z, FixedHypot(dx, dy), mo->z);
|
||||
}
|
||||
}
|
||||
|
||||
if (loop_in == 0)
|
||||
{
|
||||
tic_t accel = max(loop->pan_accel, 1);
|
||||
fixed_t f = (min(loop_out, accel) * FRACUNIT) / accel;
|
||||
|
||||
INT32 turn = AngleDeltaSigned(focusangle, player->loop.yaw - loop->pan);
|
||||
INT32 turnspeed = FixedAngle(FixedMul(f, loop->pan_speed));
|
||||
|
||||
if (turn > turnspeed)
|
||||
{
|
||||
if (turn < ANGLE_90)
|
||||
{
|
||||
turnspeed = -(turnspeed);
|
||||
}
|
||||
|
||||
focusangle += turnspeed;
|
||||
}
|
||||
}
|
||||
|
||||
if (timeover)
|
||||
{
|
||||
const INT32 timeovercam = max(0, min(180, (player->karthud[khud_timeovercam] - 2*TICRATE)*15));
|
||||
|
|
@ -3889,6 +3945,11 @@ DoABarrelRoll (player_t *player)
|
|||
|
||||
fixed_t smoothing;
|
||||
|
||||
if (player->loop.radius)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->respawn.state != RESPAWNST_NONE)
|
||||
{
|
||||
player->tilt = 0;
|
||||
|
|
@ -4329,12 +4390,12 @@ void P_PlayerThink(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
boolean deathcontrolled = (player->respawn.state != RESPAWNST_NONE && player->respawn.truedeath == true)
|
||||
|| (player->pflags & PF_NOCONTEST) || (player->karmadelay);
|
||||
boolean powercontrolled = (player->hyudorotimer) || (player->growshrinktimer > 0);
|
||||
|
||||
// Flash player after being hit.
|
||||
if (!(player->hyudorotimer // SRB2kart - fixes Hyudoro not flashing when it should.
|
||||
|| player->growshrinktimer > 0 // Grow doesn't flash either.
|
||||
|| (player->respawn.state != RESPAWNST_NONE && player->respawn.truedeath == true) // Respawn timer (for drop dash effect)
|
||||
|| (player->pflags & PF_NOCONTEST) // NO CONTEST explosion
|
||||
|| player->karmadelay))
|
||||
if (!deathcontrolled && !powercontrolled)
|
||||
{
|
||||
if (player->flashing > 1 && player->flashing < K_GetKartFlashing(player)
|
||||
&& (leveltime & 1))
|
||||
|
|
@ -4342,6 +4403,10 @@ void P_PlayerThink(player_t *player)
|
|||
else
|
||||
player->mo->renderflags &= ~RF_DONTDRAW;
|
||||
}
|
||||
else if (!deathcontrolled)
|
||||
{
|
||||
player->mo->renderflags &= ~RF_DONTDRAW;
|
||||
}
|
||||
|
||||
if (player->stairjank > 0)
|
||||
{
|
||||
|
|
|
|||
10
src/r_fps.c
10
src/r_fps.c
|
|
@ -140,10 +140,6 @@ void R_InterpolateView(fixed_t frac)
|
|||
prevview = newview;
|
||||
}
|
||||
|
||||
viewx = R_LerpFixed(prevview->x, newview->x, frac);
|
||||
viewy = R_LerpFixed(prevview->y, newview->y, frac);
|
||||
viewz = R_LerpFixed(prevview->z, newview->z, frac);
|
||||
|
||||
viewangle = R_LerpAngle(prevview->angle, newview->angle, frac);
|
||||
aimingangle = R_LerpAngle(prevview->aim, newview->aim, frac);
|
||||
viewroll = R_LerpAngle(prevview->roll, newview->roll, frac);
|
||||
|
|
@ -151,6 +147,12 @@ void R_InterpolateView(fixed_t frac)
|
|||
viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
|
||||
viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
|
||||
|
||||
fixed_t zoom = R_LerpFixed(prevview->zoom, newview->zoom, frac);
|
||||
|
||||
viewx = R_LerpFixed(prevview->x, newview->x, frac) - FixedMul(viewcos, zoom);
|
||||
viewy = R_LerpFixed(prevview->y, newview->y, frac) - FixedMul(viewsin, zoom);
|
||||
viewz = R_LerpFixed(prevview->z, newview->z, frac);
|
||||
|
||||
viewplayer = newview->player;
|
||||
viewsector = R_PointInSubsector(viewx, viewy)->sector;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ struct viewvars_t {
|
|||
fixed_t x;
|
||||
fixed_t y;
|
||||
fixed_t z;
|
||||
fixed_t zoom;
|
||||
boolean sky;
|
||||
sector_t *sector;
|
||||
player_t *player;
|
||||
|
|
|
|||
|
|
@ -1244,6 +1244,7 @@ void R_SetupFrame(int s)
|
|||
newview->x = r_viewmobj->x;
|
||||
newview->y = r_viewmobj->y;
|
||||
newview->z = r_viewmobj->z;
|
||||
newview->zoom = 0;
|
||||
|
||||
R_SetupCommonFrame(player, r_viewmobj->subsector);
|
||||
}
|
||||
|
|
@ -1252,9 +1253,13 @@ void R_SetupFrame(int s)
|
|||
{
|
||||
r_viewmobj = NULL;
|
||||
|
||||
newview->x = thiscam->x;
|
||||
newview->y = thiscam->y;
|
||||
fixed_t x = player->mo ? player->mo->x : thiscam->x;
|
||||
fixed_t y = player->mo ? player->mo->y : thiscam->y;
|
||||
|
||||
newview->x = x;
|
||||
newview->y = y;
|
||||
newview->z = thiscam->z + (thiscam->height>>1);
|
||||
newview->zoom = FixedHypot(thiscam->x - x, thiscam->y - y);
|
||||
|
||||
R_SetupCommonFrame(player, thiscam->subsector);
|
||||
}
|
||||
|
|
@ -1267,6 +1272,7 @@ void R_SetupFrame(int s)
|
|||
newview->x = r_viewmobj->x;
|
||||
newview->y = r_viewmobj->y;
|
||||
newview->z = player->viewz;
|
||||
newview->zoom = 0;
|
||||
|
||||
R_SetupCommonFrame(player, r_viewmobj->subsector);
|
||||
}
|
||||
|
|
@ -1297,6 +1303,7 @@ void R_SkyboxFrame(int s)
|
|||
newview->x = r_viewmobj->x;
|
||||
newview->y = r_viewmobj->y;
|
||||
newview->z = r_viewmobj->z; // 26/04/17: use actual Z position instead of spawnpoint angle!
|
||||
newview->zoom = 0;
|
||||
|
||||
if (mapheaderinfo[gamemap-1])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
|
|||
for (i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
player_t *player = &players[displayplayers[i]];
|
||||
boolean camaway = false;
|
||||
|
||||
memset(&listener[i], 0, sizeof (listener[i]));
|
||||
listenmobj[i] = NULL;
|
||||
|
|
@ -442,9 +443,11 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume)
|
|||
else
|
||||
{
|
||||
listenmobj[i] = player->mo;
|
||||
if (player->exiting)
|
||||
camaway = true;
|
||||
}
|
||||
|
||||
if (origin && origin == listenmobj[i] && !camera[i].freecam)
|
||||
if (origin && origin == listenmobj[i] && !camera[i].freecam && !camaway)
|
||||
{
|
||||
itsUs = true;
|
||||
}
|
||||
|
|
@ -763,6 +766,9 @@ void S_UpdateSounds(void)
|
|||
if (c->origin != listenmobj[i])
|
||||
continue;
|
||||
|
||||
if (listenmobj[i]->player && listenmobj[i]->player->exiting)
|
||||
continue;
|
||||
|
||||
itsUs = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue