Merge remote-tracking branch 'origin/master' into message-drawer

This commit is contained in:
AJ Martinez 2024-01-14 17:58:52 -07:00
commit 6f2d467542
27 changed files with 352 additions and 123 deletions

View file

@ -712,7 +712,7 @@ consvar_t cv_kartbot = UnsavedNetVar("bots", "Off").values({
{13,"Lv.MAX"},
});
consvar_t cv_kartbumpers = UnsavedNetVar("battlebumpers", "3").min_max(1, 12);
consvar_t cv_kartbumpers = UnsavedNetVar("battlebumpers", "3").min_max(0, 12);
void KartEliminateLast_OnChange(void);
consvar_t cv_karteliminatelast = UnsavedNetVar("eliminatelast", "Yes").yes_no().onchange(KartEliminateLast_OnChange);

View file

@ -2194,6 +2194,8 @@ void D_MapChange(UINT16 mapnum, INT32 newgametype, boolean pencoremode, boolean
void D_SetupVote(INT16 newgametype)
{
const UINT32 rules = gametypes[newgametype]->rules;
UINT8 buf[(VOTE_NUM_LEVELS * 2) + 4];
UINT8 *p = buf;
@ -2203,16 +2205,37 @@ void D_SetupVote(INT16 newgametype)
memset(votebuffer, UINT16_MAX, sizeof(votebuffer));
WRITEINT16(p, newgametype);
WRITEUINT8(p, ((cv_kartencore.value == 1) && (gametyperules & GTR_ENCORE)));
WRITEUINT8(p, ((cv_kartencore.value == 1) && (rules & GTR_ENCORE)));
WRITEUINT8(p, G_SometimesGetDifferentEncore());
UINT8 numPlayers = 0;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i] || players[i].spectator)
{
continue;
}
extern consvar_t cv_forcebots; // debug
if (!(rules & GTR_BOTS) && players[i].bot && !cv_forcebots.value)
{
// Gametype doesn't support bots
continue;
}
numPlayers++;
}
for (i = 0; i < VOTE_NUM_LEVELS; i++)
{
UINT16 m = G_RandMap(
UINT16 m = G_RandMapPerPlayerCount(
G_TOLFlag(newgametype),
prevmap, false,
(i < VOTE_NUM_LEVELS-1),
votebuffer
votebuffer,
numPlayers
);
votebuffer[i] = m;
WRITEUINT16(p, m);

View file

@ -953,6 +953,8 @@ struct player_t
UINT8 lastsafelap;
fixed_t topAccel; // Reduced on straight wall collisions to give players extra recovery time
mobj_t *stumbleIndicator;
mobj_t *wavedashIndicator;
mobj_t *trickIndicator;

View file

@ -1417,6 +1417,29 @@ void readlevelheader(MYFILE *f, char * name)
mapheaderinfo[num]->destroyforchallenge_size = j;
}
}
else if (fastcmp(word, "LOBBYSIZE"))
{
if (fastcmp(word2, "DUEL"))
{
mapheaderinfo[num]->playerLimit = 2;
}
else if (fastcmp(word2, "SMALL"))
{
mapheaderinfo[num]->playerLimit = 5;
}
else if (fastcmp(word2, "MEDIUM"))
{
mapheaderinfo[num]->playerLimit = 10;
}
else if (fastcmp(word2, "LARGE"))
{
mapheaderinfo[num]->playerLimit = 16;
}
else
{
deh_warning("Level header %d: invalid lobby size '%s'", num, word2);
}
}
else
deh_warning("Level header %d: unknown word '%s'", num, word);
}

View file

@ -501,6 +501,7 @@ struct mapheader_t
UINT8 levelselect; ///< Is this map available in the level select? If so, which map list is it available in?
UINT16 menuflags; ///< LF2_flags: options that affect record attack menus
UINT8 playerLimit; ///< This map does not appear in multiplayer vote if there are too many players
// Operational metadata
UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below

View file

@ -1526,7 +1526,8 @@ boolean G_CouldView(INT32 playernum)
//
boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive)
{
if (!playeringame[playernum] || players[playernum].spectator)
// PF_ELIMINATED: Battle Overtime Barrier killed this player
if (!playeringame[playernum] || players[playernum].spectator || (players[playernum].pflags & PF_ELIMINATED))
{
return false;
}
@ -2362,6 +2363,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->ringvolume = 255;
p->topAccel = MAXTOPACCEL;
p->botvars.rubberband = FRACUNIT;
p->spectatorReentry = spectatorReentry;
@ -3499,7 +3502,7 @@ static UINT16 *g_allowedMaps = NULL;
static size_t g_randMapStack = 0;
#endif
UINT16 G_RandMap(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer)
UINT16 G_RandMapPerPlayerCount(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer, UINT8 numPlayers)
{
INT32 allowedMapsCount = 0;
INT32 extBufferCount = 0;
@ -3558,6 +3561,12 @@ tryAgain:
continue;
}
if (numPlayers > mapheaderinfo[i]->playerLimit)
{
// Too many players for this map.
continue;
}
// Only care about restrictions if the host is a listen server.
if (!dedicated)
{
@ -3665,6 +3674,11 @@ tryAgain:
return ret;
}
UINT16 G_RandMap(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer)
{
return G_RandMapPerPlayerCount(tolflags, pprevmap, ignoreBuffers, callAgainSoon, extBuffer, 0);
}
void G_AddMapToBuffer(UINT16 map)
{
if (mapheaderinfo[map]->justPlayed == 0) // Started playing a new map.

View file

@ -277,6 +277,7 @@ FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics);
UINT32 G_TOLFlag(INT32 pgametype);
UINT16 G_GetFirstMapOfGametype(UINT16 pgametype);
UINT16 G_RandMapPerPlayerCount(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer, UINT8 numPlayers);
UINT16 G_RandMap(UINT32 tolflags, UINT16 pprevmap, boolean ignoreBuffers, boolean callAgainSoon, UINT16 *extBuffer);
void G_AddMapToBuffer(UINT16 map);

View file

@ -396,9 +396,9 @@ static boolean AutomaticControllerReassignmentIsAllowed(INT32 device)
{
boolean device_is_gamepad = device > 0;
boolean device_is_unassigned = G_GetPlayerForDevice(device) == -1;
boolean gamestate_is_in_level = gamestate == GS_LEVEL;
boolean gamestate_is_in_active_play = (gamestate == GS_LEVEL || gamestate == GS_VOTING);
return device_is_gamepad && device_is_unassigned && gamestate_is_in_level;
return device_is_gamepad && device_is_unassigned && gamestate_is_in_active_play;
}
static INT32 AssignDeviceToFirstUnassignedPlayer(INT32 device)
@ -564,6 +564,18 @@ void G_MapEventsToControls(event_t *ev)
}
else
{
// We used to only allow this assignment for triggers, but it caused some confusion in vote screen.
// In case of misebhaving devices, break glass.
if (AutomaticControllerReassignmentIsAllowed(ev->device)
&& (abs(ev->data2) > JOYAXISRANGE/2 || abs(ev->data3) > JOYAXISRANGE/2))
{
INT32 assigned = AssignDeviceToFirstUnassignedPlayer(ev->device);
if (assigned >= 0)
{
CONS_Alert(CONS_NOTICE, "Player %d device was reassigned\n", assigned + 1);
}
}
// Actual analog sticks
if (ev->data2 != INT32_MAX)
{

View file

@ -542,21 +542,28 @@ static UINT32 K_BotRubberbandDistance(const player_t *player)
continue;
}
if (playeringame[i] && players[i].bot)
if (!playeringame[i] || players[i].spectator)
{
// First check difficulty levels, then score, then settle it with port priority!
if (player->botvars.difficulty < players[i].botvars.difficulty)
{
pos += 3;
}
else if (player->score < players[i].score)
{
pos += 2;
}
else if (i < portpriority)
{
pos += 1;
}
continue;
}
if (!players[i].bot)
{
continue;
}
// First check difficulty levels, then score, then settle it with port priority!
if (player->botvars.difficulty < players[i].botvars.difficulty)
{
pos += 3;
}
else if (player->score < players[i].score)
{
pos += 2;
}
else if (i < portpriority)
{
pos += 1;
}
}

View file

@ -543,6 +543,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
break;
case MT_PLAYER:
if (thing->player
&& !thing->player->spectator
&& !thing->player->hyudorotimer
&& !g_nudgeSearch.botmo->player->hyudorotimer)
{
@ -902,7 +903,7 @@ static BlockItReturn_t K_FindPlayersToBully(mobj_t *thing)
return BMIT_CONTINUE;
}
if (!thing->player)
if (!thing->player || thing->player->spectator)
{
return BMIT_CONTINUE;
}

View file

@ -746,17 +746,6 @@ void K_LightningShieldAttack(mobj_t *actor, fixed_t size)
boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
{
if (t1->type == MT_PLAYER)
{
// Bubble Shield already has a hitbox, and it gets
// teleported every tic so the Bubble itself will
// always make contact with other objects.
//
// Therefore, we don't need a second, smaller hitbox
// on the player. It'll just cause unwanted hitlag.
return true;
}
if (t2->type == MT_PLAYER)
{
// Counter desyncs
@ -781,21 +770,29 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
}
else
{
if (!t2->threshold || t2->type == MT_DROPTARGET)
mobj_t *owner = t1->player ? t1 : t1->target;
if (t2->target != owner || !t2->threshold || t2->type == MT_DROPTARGET)
{
if (t1->player && K_PlayerGuard(t1->player))
{
K_KartSolidBounce(t1, t2);
K_DoPowerClash(t1, t2);
}
if (!t2->momx && !t2->momy)
{
t2->momz += (24*t2->scale) * P_MobjFlip(t2);
}
else
{
t2->momx = -4*t2->momx;
t2->momy = -4*t2->momy;
t2->momz = -4*t2->momz;
t2->momx = -6*t2->momx;
t2->momy = -6*t2->momy;
t2->momz = -6*t2->momz;
t2->angle += ANGLE_180;
}
if (t2->type == MT_JAWZ)
P_SetTarget(&t2->tracer, t2->target); // Back to the source!
P_SetTarget(&t2->target, owner); // Let the source reflect it back again!
t2->threshold = 10;
S_StartSound(t1, sfx_s3k44);
}
@ -826,8 +823,7 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim)
{
player_t *victimPlayer = victim->player;
//if (victim != attacker && !P_PlayerInPain(victimPlayer) && victimPlayer->flashing == 0)
if (victim != attacker && victim->hitlag == 0)
if (victim != attacker && (P_PlayerInPain(victimPlayer) ? victim->hitlag == 0 : victimPlayer->flashing == 0))
{
// If both players have a whip, hits are order-of-execution dependent and that sucks.
// Player expectation is a clash here.

View file

@ -112,7 +112,9 @@ struct DirectorInfo
}
// pair finished? try the next one
if (players[playerstat[targetposition].sorted].exiting)
if (players[playerstat[targetposition].sorted].exiting ||
// Battle: player was killed by Overtime Barrier
(players[playerstat[targetposition].sorted].pflags & PF_ELIMINATED))
{
continue;
}
@ -137,12 +139,6 @@ struct DirectorInfo
break;
}
// if this is a splitscreen player, try next pair
if (P_IsDisplayPlayer(&players[target]))
{
continue;
}
// if we're certain the back half of the pair is actually in this position, try to switch
if (!players[target].positiondelay)
{

View file

@ -14,6 +14,7 @@
#include "doomdef.h"
#include "doomstat.h"
#include "k_battle.h"
#include "k_kart.h"
#include "m_random.h"
#include "p_local.h"
@ -195,6 +196,11 @@ static void K_SpawnHitLagEFX(mobj_t *victim, mobj_t *inflictor, mobj_t *source,
newScale = 3 * victim->destscale;
}
if ((gametyperules & GTR_BUMPERS) && battleprisons == false)
{
newScale = 3 * newScale / 4;
}
if (P_MobjWasRemoved(source) == false)
{
color = (source->player != NULL) ? source->player->skincolor : source->color;

View file

@ -2225,13 +2225,20 @@ struct PositionFacesInfo
void draw_1p();
void draw_4p_battle(int x, int y, INT32 flags);
UINT32 top_score() const { return players[rankplayer[0]].roundscore; }
bool near_goal() const { return g_pointlimit - 5 <= top_score(); }
player_t* top() const { return &players[rankplayer[0]]; }
UINT32 top_score() const { return top()->roundscore; }
bool near_goal() const
{
constexpr tic_t kThreshold = 5;
return std::max(kThreshold, g_pointlimit) - kThreshold <= top_score();
}
skincolornum_t vomit_color() const
{
if (!near_goal())
{
return SKINCOLOR_NONE;
return static_cast<skincolornum_t>(top()->skincolor);
}
constexpr int kCycleSpeed = 4;
@ -2309,7 +2316,11 @@ void PositionFacesInfo::draw_1p()
UINT32 skinflags;
if (gametyperules & GTR_POINTLIMIT) // playing battle
Y += (9*5) - 5; // <-- arbitrary calculation
{
Y += 40;
if (ranklines < 3)
Y -= 18;
}
else if (ranklines < 5)
Y += (9*ranklines);
else

View file

@ -3531,7 +3531,7 @@ fixed_t K_GetKartAccel(const player_t *player)
// Marble Garden Top gets 1200% accel
if (player->curshield == KSHIELD_TOP)
{
k_accel *= 12;
k_accel = FixedMul(k_accel, player->topAccel);
}
if (K_PodiumSequence() == true)
@ -3550,6 +3550,11 @@ UINT16 K_GetKartFlashing(const player_t *player)
{
UINT16 tics = flashingtics;
if (gametyperules & GTR_BUMPERS)
{
return 1;
}
if (player == NULL)
{
return tics;
@ -3559,16 +3564,6 @@ UINT16 K_GetKartFlashing(const player_t *player)
return tics;
}
void K_UpdateDamageFlashing(player_t *player, UINT16 tics)
{
if (gametyperules & GTR_BUMPERS)
{
return;
}
player->flashing = tics;
}
boolean K_PlayerShrinkCheat(const player_t *player)
{
return (
@ -3651,7 +3646,17 @@ SINT8 K_GetForwardMove(const player_t *player)
}
else
{
forwardmove = MAXPLMOVE;
// forwardmove = MAXPLMOVE;
UINT8 minmove = MAXPLMOVE/10;
fixed_t assistmove = (MAXPLMOVE - minmove) * FRACUNIT;
angle_t topdelta = player->mo->angle - K_MomentumAngle(player->mo);
fixed_t topmult = FINECOSINE(topdelta >> ANGLETOFINESHIFT);
topmult = (topmult/2) + (FRACUNIT/2);
assistmove = FixedMul(topmult, assistmove);
forwardmove = minmove + FixedInt(assistmove);
}
}
@ -8399,9 +8404,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->spinouttimer != 0)
{
if (( player->spinouttype & KSPIN_IFRAMES ) == 0)
K_UpdateDamageFlashing(player, 0);
player->flashing = 0;
else
K_UpdateDamageFlashing(player, K_GetKartFlashing(player));
player->flashing = K_GetKartFlashing(player);
}
if (player->spinouttimer)
@ -8726,6 +8731,17 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
}
}
// Players that bounce far off walls get reduced Top accel, to give them some time to get their bearings.
if ((player->mo->eflags & MFE_JUSTBOUNCEDWALL) && player->curshield == KSHIELD_TOP)
{
angle_t topdelta = player->mo->angle - K_MomentumAngle(player->mo);
fixed_t topmult = FINECOSINE(topdelta >> ANGLETOFINESHIFT);
topmult = (topmult/2) + (FRACUNIT/2); // 0 to original
player->topAccel = FixedMul(topmult, player->topAccel);
}
player->topAccel = min(player->topAccel + TOPACCELREGEN, MAXTOPACCEL);
if (player->stealingtimer == 0
&& player->rocketsneakertimer
&& onground == true)
@ -13336,6 +13352,11 @@ UINT32 K_PointLimitForGametype(void)
ptsCap += 4;
}
}
if (ptsCap > 20)
{
ptsCap = 20;
}
}
return ptsCap;

View file

@ -52,6 +52,9 @@ Make sure this matches the actual number of states
#define RINGVOLUMEUSEPENALTY 15
#define RINGVOLUMEREGEN 1
#define MAXTOPACCEL (12*FRACUNIT)
#define TOPACCELREGEN (FRACUNIT/16)
// Mispredicted turns can generate phantom sliptide inputs for a few tics.
// Delay the wavedash visuals until we're reasonably sure that it's a deliberate turn.
#define HIDEWAVEDASHCHARGE (60)
@ -192,7 +195,6 @@ fixed_t K_GetKartSpeedFromStat(UINT8 kartspeed);
fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dorubberbanding);
fixed_t K_GetKartAccel(const player_t *player);
UINT16 K_GetKartFlashing(const player_t *player);
void K_UpdateDamageFlashing(player_t *player, UINT16 tics);
boolean K_PlayerShrinkCheat(const player_t *player);
void K_UpdateShrinkCheat(player_t *player);
boolean K_KartKickstart(const player_t *player);

View file

@ -131,7 +131,7 @@ INT16 K_PowerLevelPlacementScore(player_t *player)
}
else
{
return player->score;
return player->roundscore;
}
}

View file

@ -551,6 +551,16 @@ void level_tally_t::Init(player_t *player)
state = TALLY_ST_IGNORE;
delay = 0;
}
if (UINT8 pnum = player - players; G_IsPartyLocal(pnum))
{
UINT8 view = G_PartyPosition(pnum);
// Battle: if this player's viewpoint has changed
// since being eliminated, set it back so they see
// their own Tally and not someone else's.
displayplayers[view] = pnum;
G_FixCamera(1 + view);
}
}
void level_tally_t::NewLine(void)
@ -1384,8 +1394,7 @@ void K_TickPlayerTally(player_t *player)
void K_DrawPlayerTally(void)
{
// Draw the observer player's tally, not whoever they may be spectating
players[G_PartyMember(consoleplayer, R_GetViewNumber())].tally.Draw();
stplyr->tally.Draw();
}
boolean K_PlayerTallyActive(player_t *player)

View file

@ -345,6 +345,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->finalfailsafe);
else if (fastcmp(field,"lastsafelap"))
lua_pushinteger(L, plr->lastsafelap);
else if (fastcmp(field,"topAccel"))
lua_pushinteger(L, plr->topAccel);
else if (fastcmp(field,"instaWhipCharge"))
lua_pushinteger(L, plr->instaWhipCharge);
else if (fastcmp(field,"defenseLockout"))
@ -865,6 +867,8 @@ static int player_set(lua_State *L)
plr->finalfailsafe = luaL_checkinteger(L, 3);
else if (fastcmp(field,"lastsafelap"))
plr->lastsafelap = luaL_checkinteger(L, 3);
else if (fastcmp(field,"topAccel"))
plr->topAccel = luaL_checkinteger(L, 3);
else if (fastcmp(field,"instaWhipCharge"))
plr->instaWhipCharge = luaL_checkinteger(L, 3);
else if (fastcmp(field,"defenseLockout"))

View file

@ -42,21 +42,15 @@ void Obj_CloudSpawn(mobj_t *mobj)
return;
}
if (mobj->type != MT_AGZ_CLOUDCLUSTER)
{
mobj->destscale = mapobjectscale * 4;
P_SetScale(mobj, mobj->destscale);
}
mobj->destscale = mapobjectscale * 4;
P_SetScale(mobj, mobj->destscale);
mobj_t *cloud = P_SpawnMobj(mobj->x, mobj->y, mobj->z, cloudtype);
angle_t ang = mobj->angle;
UINT8 dist = 128;
if (cloudtype == MT_AGZ_CLOUD)
{
cloud->destscale = cloud->scale * 2;
P_SetScale(cloud, cloud->destscale);
}
cloud->destscale = cloud->scale * 2;
P_SetScale(cloud, cloud->destscale);
for (UINT8 i = 0; i < 4; i++)
{
@ -65,10 +59,11 @@ void Obj_CloudSpawn(mobj_t *mobj)
cloud = P_SpawnMobj(x, y, mobj->z, cloudtype);
cloud->destscale = cloud->scale * 2;
P_SetScale(cloud, cloud->destscale);
if (cloudtype == MT_AGZ_CLOUD)
{
cloud->destscale = cloud->scale * 2;
P_SetScale(cloud, cloud->destscale);
cloud->frame = P_RandomRange(PR_DECORATION, 0, 3);
}
@ -157,20 +152,8 @@ void Obj_PlayerCloudThink(player_t *player)
if (P_MobjWasRemoved(mo->tracer))
return;
switch(mo->tracer->type)
{
case MT_AHZ_CLOUD:
P_SetObjectMomZ(mo, CLOUDB_ZTHRUST, false);
break;
case MT_AGZ_CLOUD:
mo->momz = FixedMul(mapobjectscale, CLOUD_ZTHRUST * P_MobjFlip(mo->tracer));
break;
case MT_SSZ_CLOUD:
P_SetObjectMomZ(mo, CLOUD_ZTHRUST, false);
break;
default:
break;
}
mo->momz = FixedMul(mapobjectscale,
(mo->tracer->type == MT_AHZ_CLOUD ? CLOUDB_ZTHRUST : CLOUD_ZTHRUST) * P_MobjFlip(mo->tracer));
player->cloudlaunch = TICRATE;
P_InstaThrust(mo, mo->cusval, mo->cvmem);

View file

@ -1,6 +1,7 @@
#include <algorithm>
#include "../d_player.h"
#include "../k_battle.h"
#include "../k_objects.h"
#include "../m_fixed.h"
#include "../info.h"
@ -230,7 +231,10 @@ void Obj_SpawnGachaBomRebound(mobj_t* source, mobj_t* target)
x->color = target->color;
x->angle = angle;
P_InstaScale(x, 2 * x->scale);
if (!(gametyperules & GTR_BUMPERS) || battleprisons)
{
P_InstaScale(x, 2 * x->scale);
}
rebound_mode(x) = static_cast<int>(mode);
rebound_timer(x) = kReboundAcceptPause;

View file

@ -2812,6 +2812,34 @@ static void AddNullHitlag(player_t *player, tic_t oldHitlag)
}
}
static boolean P_FlashingException(const player_t *player, const mobj_t *inflictor)
{
if (!inflictor)
{
// Sector damage always behaves the same.
return false;
}
if (!P_IsKartItem(inflictor->type) && inflictor->type != MT_PLAYER)
{
// Exception only applies to player items.
// Also applies to players because of PvP collision.
// Lightning Shield also uses the player object as inflictor.
return false;
}
if (!P_PlayerInPain(player))
{
// Flashing tics is sometimes used in a way unrelated to damage.
// E.g. picking up a power-up gives you flashing tics.
// Respect this usage of flashing tics.
return false;
}
// Flashing tics are ignored.
return true;
}
/** Damages an object, which may or may not be a player.
* For melee attacks, source and inflictor are the same.
*
@ -3176,7 +3204,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
}
// DMG_EXPLODE excluded from flashtic checks to prevent dodging eggbox/SPB with weak spinout
if ((target->hitlag == 0 || allowcombo == false) && player->flashing > 0 && type != DMG_EXPLODE && type != DMG_STUMBLE && type != DMG_WHUMBLE)
if ((target->hitlag == 0 || allowcombo == false) &&
player->flashing > 0 &&
type != DMG_EXPLODE &&
type != DMG_STUMBLE &&
type != DMG_WHUMBLE &&
P_FlashingException(player, inflictor) == false)
{
// Post-hit invincibility
K_DoInstashield(player);
@ -3341,7 +3374,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (type != DMG_STUMBLE && type != DMG_WHUMBLE)
{
if (type != DMG_STING)
K_UpdateDamageFlashing(player, K_GetKartFlashing(player));
player->flashing = K_GetKartFlashing(player);
player->ringburst += ringburst;

View file

@ -522,6 +522,17 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
}
}
static boolean P_BubbleCanReflect(mobj_t *t1, mobj_t *t2)
{
return (t2->type == MT_ORBINAUT || t2->type == MT_JAWZ || t2->type == MT_GACHABOM
|| t2->type == MT_BANANA || t2->type == MT_EGGMANITEM || t2->type == MT_BALLHOG
|| t2->type == MT_SSMINE || t2->type == MT_LANDMINE || t2->type == MT_SINK
|| t2->type == MT_GARDENTOP
|| t2->type == MT_DROPTARGET
|| t2->type == MT_KART_LEFTOVER
|| (t2->type == MT_PLAYER && t1->target != t2));
}
//
// PIT_CheckThing
//
@ -993,16 +1004,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
if (tm.thing->type == MT_RANDOMITEM)
return BMIT_CONTINUE;
// Bubble Shield reflect
if (((thing->type == MT_BUBBLESHIELD && thing->target->player && thing->target->player->bubbleblowup)
|| (thing->player && thing->player->bubbleblowup))
&& (tm.thing->type == MT_ORBINAUT || tm.thing->type == MT_JAWZ || tm.thing->type == MT_GACHABOM
|| tm.thing->type == MT_BANANA || tm.thing->type == MT_EGGMANITEM || tm.thing->type == MT_BALLHOG
|| tm.thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || tm.thing->type == MT_SINK
|| tm.thing->type == MT_GARDENTOP
|| tm.thing->type == MT_DROPTARGET
|| tm.thing->type == MT_KART_LEFTOVER
|| (tm.thing->type == MT_PLAYER && thing->target != tm.thing)))
if (tm.thing->type != MT_PLAYER && thing->player && K_PlayerGuard(thing->player) && P_BubbleCanReflect(thing, tm.thing))
{
// see if it went over / under
if (tm.thing->z > thing->z + thing->height)
@ -1012,15 +1014,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return K_BubbleShieldCollide(thing, tm.thing) ? BMIT_CONTINUE : BMIT_ABORT;
}
else if (((tm.thing->type == MT_BUBBLESHIELD && tm.thing->target->player && tm.thing->target->player->bubbleblowup)
|| (tm.thing->player && tm.thing->player->bubbleblowup))
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_GACHABOM
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|| thing->type == MT_SSMINE || thing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| thing->type == MT_DROPTARGET
|| thing->type == MT_KART_LEFTOVER
|| (thing->type == MT_PLAYER && tm.thing->target != thing)))
else if (thing->type != MT_PLAYER && tm.thing->player && K_PlayerGuard(tm.thing->player) && P_BubbleCanReflect(tm.thing, thing))
{
// see if it went over / under
if (tm.thing->z > thing->z + thing->height)
@ -1031,6 +1025,52 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return K_BubbleShieldCollide(tm.thing, thing) ? BMIT_CONTINUE : BMIT_ABORT;
}
// Bubble Shield reflect
if ((thing->type == MT_BUBBLESHIELD && thing->target->player && thing->target->player->bubbleblowup)
|| (thing->player && thing->player->bubbleblowup))
{
// see if it went over / under
if (tm.thing->z > thing->z + thing->height)
return BMIT_CONTINUE; // overhead
if (tm.thing->z + tm.thing->height < thing->z)
return BMIT_CONTINUE; // underneath
if (P_BubbleCanReflect(thing, tm.thing))
{
// don't let player hitbox touch it too
if (thing->player)
return BMIT_CONTINUE;
return K_BubbleShieldCollide(thing, tm.thing) ? BMIT_CONTINUE : BMIT_ABORT;
}
else if ((tm.thing->flags & MF_SHOOTABLE) && !thing->player)
{
P_DamageMobj(tm.thing, thing, thing->target, 1, DMG_NORMAL);
return BMIT_CONTINUE;
}
}
else if ((tm.thing->type == MT_BUBBLESHIELD && tm.thing->target->player && tm.thing->target->player->bubbleblowup)
|| (tm.thing->player && tm.thing->player->bubbleblowup))
{
// see if it went over / under
if (tm.thing->z > thing->z + thing->height)
return BMIT_CONTINUE; // overhead
if (tm.thing->z + tm.thing->height < thing->z)
return BMIT_CONTINUE; // underneath
if (P_BubbleCanReflect(tm.thing, thing))
{
// don't let player hitbox touch it too
if (tm.thing->player)
return BMIT_CONTINUE;
return K_BubbleShieldCollide(tm.thing, thing) ? BMIT_CONTINUE : BMIT_ABORT;
}
else if ((thing->flags & MF_SHOOTABLE) && !tm.thing->player)
{
P_DamageMobj(thing, tm.thing, tm.thing->target, 1, DMG_NORMAL);
return BMIT_CONTINUE;
}
}
// double make sure bubbles won't collide with anything else
if (thing->type == MT_BUBBLESHIELD || tm.thing->type == MT_BUBBLESHIELD)
return BMIT_CONTINUE;

View file

@ -5317,6 +5317,39 @@ cont:
// Kartitem stuff.
// These are held/thrown by players.
boolean P_IsKartItem(INT32 type)
{
switch (type)
{
case MT_POGOSPRING:
case MT_EGGMANITEM:
case MT_EGGMANITEM_SHIELD:
case MT_BANANA:
case MT_BANANA_SHIELD:
case MT_ORBINAUT:
case MT_ORBINAUT_SHIELD:
case MT_JAWZ:
case MT_JAWZ_SHIELD:
case MT_SSMINE:
case MT_SSMINE_SHIELD:
case MT_LANDMINE:
case MT_DROPTARGET:
case MT_DROPTARGET_SHIELD:
case MT_BALLHOG:
case MT_SPB:
case MT_BUBBLESHIELDTRAP:
case MT_GARDENTOP:
case MT_HYUDORO:
case MT_SINK:
case MT_GACHABOM:
return true;
default:
return false;
}
}
// This item is never attached to a player -- it can DIE
// unconditionally in death sectors.
boolean P_IsKartFieldItem(INT32 type)
@ -12939,19 +12972,20 @@ void P_SpawnPlayer(INT32 playernum)
if (G_IsPartyLocal(playernum))
{
// Spectating always enables director cam. If there
// is no one to view, this will do nothing. If
// someone enters the game later, it will
// automatically switch to that player.
K_ToggleDirector(G_PartyPosition(playernum), p->spectator);
// Spectators can switch to freecam. This should be
// disabled when they enter the race, or when the level
// changes.
if (!demo.playback)
{
camera[G_PartyPosition(playernum)].freecam = false;
displayplayers[G_PartyPosition(playernum)] = playernum;
}
// Spectating always enables director cam. If there
// is no one to view, this will do nothing. If
// someone enters the game later, it will
// automatically switch to that player.
K_ToggleDirector(G_PartyPosition(playernum), p->spectator);
}
else if (pcount == 1 && !p->spectator)
{

View file

@ -540,6 +540,7 @@ void P_InitCachedActions(void);
void P_RunCachedActions(void);
void P_AddCachedAction(mobj_t *mobj, INT32 statenum);
boolean P_IsKartItem(INT32 type);
boolean P_IsKartFieldItem(INT32 type);
boolean K_IsMissileOrKartItem(mobj_t *mo);
boolean P_CanDeleteKartItem(INT32 type);

View file

@ -571,6 +571,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].lastsafelap);
WRITEFIXED(save->p, players[i].topAccel);
WRITEMEM(save->p, players[i].public_key, PUBKEYLENGTH);
WRITEUINT8(save->p, players[i].instaWhipCharge);
@ -1142,6 +1144,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].lastsafelap = READUINT8(save->p);
players[i].topAccel = READFIXED(save->p);
READMEM(save->p, players[i].public_key, PUBKEYLENGTH);
players[i].instaWhipCharge = READUINT8(save->p);

View file

@ -457,6 +457,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
mapheaderinfo[num]->levelselect = 0;
mapheaderinfo[num]->levelflags = 0;
mapheaderinfo[num]->menuflags = 0;
mapheaderinfo[num]->playerLimit = MAXPLAYERS;
mapheaderinfo[num]->mobj_scale = FRACUNIT;
mapheaderinfo[num]->default_waypoint_radius = 0;
P_ClearMapHeaderLighting(&mapheaderinfo[num]->lighting);