mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge remote-tracking branch 'origin/master' into message-drawer
This commit is contained in:
commit
6f2d467542
27 changed files with 352 additions and 123 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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
18
src/g_game.c
18
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;
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
49
src/k_kart.c
49
src/k_kart.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ INT16 K_PowerLevelPlacementScore(player_t *player)
|
|||
}
|
||||
else
|
||||
{
|
||||
return player->score;
|
||||
return player->roundscore;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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