mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-04-27 12:31:54 +00:00
Merge branch 'battle-fixes-five-nights' into 'master'
Battle Fixes at Freddy's (includes gameplay changes that affect Race too!) Closes #879, #893, #900, #918, #906, #903, #913, #907, and #905 See merge request KartKrew/Kart!1837
This commit is contained in:
commit
271a5a44ed
19 changed files with 257 additions and 76 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
16
src/g_game.c
16
src/g_game.c
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -3499,7 +3500,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 +3559,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 +3672,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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -745,17 +745,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
|
||||
|
|
@ -780,21 +769,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);
|
||||
}
|
||||
|
|
@ -825,8 +822,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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -2224,13 +2224,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;
|
||||
|
|
@ -2308,7 +2315,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
|
||||
|
|
|
|||
24
src/k_kart.c
24
src/k_kart.c
|
|
@ -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 (
|
||||
|
|
@ -8396,9 +8391,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)
|
||||
|
|
@ -13333,6 +13328,11 @@ UINT32 K_PointLimitForGametype(void)
|
|||
ptsCap += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptsCap > 20)
|
||||
{
|
||||
ptsCap = 20;
|
||||
}
|
||||
}
|
||||
|
||||
return ptsCap;
|
||||
|
|
|
|||
|
|
@ -192,7 +192,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);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ INT16 K_PowerLevelPlacementScore(player_t *player)
|
|||
}
|
||||
else
|
||||
{
|
||||
return player->score;
|
||||
return player->roundscore;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
78
src/p_map.c
78
src/p_map.c
|
|
@ -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;
|
||||
|
|
|
|||
46
src/p_mobj.c
46
src/p_mobj.c
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue