mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge remote-tracking branch 'origin/race-checkpoint'
This commit is contained in:
commit
3670695df6
22 changed files with 1009 additions and 607 deletions
|
|
@ -819,7 +819,7 @@ consvar_t cv_fuzz = OnlineCheat("fuzz", "Off").on_off().description("Human playe
|
|||
|
||||
consvar_t cv_kartdebugamount = OnlineCheat("debugitemamount", "1").min_max(1, 255).description("If debugitem, give multiple copies of an item");
|
||||
consvar_t cv_kartdebugbots = OnlineCheat("debugbots", "Off").on_off().description("Bot AI debugger");
|
||||
consvar_t cv_kartdebugdistribution = OnlineCheat("debugitemodds", "Off").on_off().description("Show items that the roulette can roll");
|
||||
consvar_t cv_kartdebugdistribution = OnlineCheat("debugitemodds", "0").min_max(0, 2).description("Show items that the roulette can roll");
|
||||
consvar_t cv_kartdebughuddrop = OnlineCheat("debugitemdrop", "Off").on_off().description("Players drop paper items when damaged in any way");
|
||||
|
||||
consvar_t cv_kartdebugbotwhip = OnlineCheat("debugbotwhip", "Off").on_off().description("Disable bot ring and item pickups");
|
||||
|
|
|
|||
|
|
@ -6805,6 +6805,22 @@ INT32 D_NumPlayers(void)
|
|||
return num;
|
||||
}
|
||||
|
||||
/** Returns the number of players racing, not spectating and includes bots
|
||||
* \return Number of players. Can be zero if we're running a ::dedicated
|
||||
* server.
|
||||
*/
|
||||
INT32 D_NumPlayersInRace(void)
|
||||
{
|
||||
INT32 numPlayers = 0;
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && !players[i].spectator)
|
||||
numPlayers++;
|
||||
}
|
||||
return numPlayers;
|
||||
}
|
||||
|
||||
/** Return whether a player is a real person (not a CPU) and not spectating.
|
||||
*/
|
||||
boolean D_IsPlayerHumanAndGaming (INT32 player_number)
|
||||
|
|
|
|||
|
|
@ -657,6 +657,7 @@ extern UINT8 playernode[MAXPLAYERS];
|
|||
extern UINT8 playerconsole[MAXPLAYERS];
|
||||
|
||||
INT32 D_NumPlayers(void);
|
||||
INT32 D_NumPlayersInRace(void);
|
||||
boolean D_IsPlayerHumanAndGaming(INT32 player_number);
|
||||
|
||||
void D_ResetTiccmds(void);
|
||||
|
|
|
|||
|
|
@ -495,9 +495,8 @@ struct itemroulette_t
|
|||
SINT8 *itemList;
|
||||
#endif
|
||||
|
||||
UINT8 useOdds;
|
||||
UINT8 playing, exiting;
|
||||
UINT32 dist, baseDist;
|
||||
UINT32 preexpdist, dist, baseDist;
|
||||
UINT32 firstDist, secondDist;
|
||||
UINT32 secondToFirst;
|
||||
|
||||
|
|
@ -914,6 +913,7 @@ struct player_t
|
|||
UINT8 laps; // Number of laps (optional)
|
||||
UINT8 latestlap;
|
||||
UINT32 lapPoints; // Points given from laps
|
||||
INT32 exp;
|
||||
INT32 cheatchecknum; // The number of the last cheatcheck you hit
|
||||
INT32 checkpointId; // Players respawn here, objects/checkpoint.cpp
|
||||
|
||||
|
|
|
|||
|
|
@ -2131,6 +2131,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
UINT8 laps;
|
||||
UINT8 latestlap;
|
||||
UINT32 lapPoints;
|
||||
INT32 exp;
|
||||
UINT16 skincolor;
|
||||
INT32 skin;
|
||||
UINT8 availabilities[MAXAVAILABILITY];
|
||||
|
|
@ -2319,6 +2320,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
laps = 0;
|
||||
latestlap = 0;
|
||||
lapPoints = 0;
|
||||
exp = FRACUNIT;
|
||||
roundscore = 0;
|
||||
exiting = 0;
|
||||
khudfinish = 0;
|
||||
|
|
@ -2356,6 +2358,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
laps = players[player].laps;
|
||||
latestlap = players[player].latestlap;
|
||||
lapPoints = players[player].lapPoints;
|
||||
exp = players[player].exp;
|
||||
|
||||
roundscore = players[player].roundscore;
|
||||
|
||||
|
|
@ -2470,6 +2473,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->laps = laps;
|
||||
p->latestlap = latestlap;
|
||||
p->lapPoints = lapPoints;
|
||||
p->exp = exp;
|
||||
p->totalring = totalring;
|
||||
|
||||
for (i = 0; i < LAP__MAX; i++)
|
||||
|
|
@ -5248,7 +5252,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
|
|||
players[i].score = 0;
|
||||
}
|
||||
|
||||
if (resetplayer || map != gamemap)
|
||||
if (resetplayer || !(gametyperules & GTR_CHECKPOINTS && map == gamemap))
|
||||
{
|
||||
players[i].checkpointId = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1061,7 +1061,7 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static patch_t *K_GetSmallStaticCachedItemPatch(kartitems_t item)
|
||||
patch_t *K_GetSmallStaticCachedItemPatch(kartitems_t item)
|
||||
{
|
||||
UINT8 offset;
|
||||
|
||||
|
|
@ -5736,60 +5736,28 @@ static void K_drawDistributionDebugger(void)
|
|||
itemroulette_t rouletteData = {0};
|
||||
|
||||
const fixed_t scale = (FRACUNIT >> 1);
|
||||
const fixed_t space = 24 * scale;
|
||||
const fixed_t pad = 9 * scale;
|
||||
|
||||
fixed_t x = -pad;
|
||||
fixed_t y = -pad;
|
||||
size_t i;
|
||||
|
||||
if (R_GetViewNumber() != 0) // only for p1
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
K_FillItemRouletteData(stplyr, &rouletteData, false);
|
||||
K_FillItemRouletteData(stplyr, &rouletteData, false, true);
|
||||
|
||||
for (i = 0; i < rouletteData.itemListLen; i++)
|
||||
{
|
||||
const kartitems_t item = static_cast<kartitems_t>(rouletteData.itemList[i]);
|
||||
UINT8 amount = 1;
|
||||
if (cv_kartdebugdistribution.value <= 1)
|
||||
return;
|
||||
|
||||
if (y > (BASEVIDHEIGHT << FRACBITS) - space - pad)
|
||||
{
|
||||
x += space;
|
||||
y = -pad;
|
||||
}
|
||||
V_DrawRightAlignedThinString(320-(x >> FRACBITS), 100+10, V_SNAPTOTOP|V_SNAPTORIGHT, va("speed = %u", rouletteData.speed));
|
||||
|
||||
V_DrawFixedPatch(x, y, scale, V_SNAPTOTOP,
|
||||
K_GetSmallStaticCachedItemPatch(item), NULL);
|
||||
V_DrawRightAlignedThinString(320-(x >> FRACBITS), 100+22, V_SNAPTOTOP|V_SNAPTORIGHT, va("baseDist = %u", rouletteData.baseDist));
|
||||
V_DrawRightAlignedThinString(320-(x >> FRACBITS), 100+30, V_SNAPTOTOP|V_SNAPTORIGHT, va("dist = %u", rouletteData.dist));
|
||||
|
||||
// Display amount for multi-items
|
||||
amount = K_ItemResultToAmount(item);
|
||||
if (amount > 1)
|
||||
{
|
||||
V_DrawStringScaled(
|
||||
x + (18 * scale),
|
||||
y + (23 * scale),
|
||||
scale, FRACUNIT, FRACUNIT,
|
||||
V_SNAPTOTOP,
|
||||
NULL, HU_FONT,
|
||||
va("x%d", amount)
|
||||
);
|
||||
}
|
||||
|
||||
y += space;
|
||||
}
|
||||
|
||||
V_DrawString((x >> FRACBITS) + 20, 2, V_SNAPTOTOP, va("useOdds[%u]", rouletteData.useOdds));
|
||||
V_DrawString((x >> FRACBITS) + 20, 10, V_SNAPTOTOP, va("speed = %u", rouletteData.speed));
|
||||
|
||||
V_DrawString((x >> FRACBITS) + 20, 22, V_SNAPTOTOP, va("baseDist = %u", rouletteData.baseDist));
|
||||
V_DrawString((x >> FRACBITS) + 20, 30, V_SNAPTOTOP, va("dist = %u", rouletteData.dist));
|
||||
|
||||
V_DrawString((x >> FRACBITS) + 20, 42, V_SNAPTOTOP, va("firstDist = %u", rouletteData.firstDist));
|
||||
V_DrawString((x >> FRACBITS) + 20, 50, V_SNAPTOTOP, va("secondDist = %u", rouletteData.secondDist));
|
||||
V_DrawString((x >> FRACBITS) + 20, 58, V_SNAPTOTOP, va("secondToFirst = %u", rouletteData.secondToFirst));
|
||||
V_DrawRightAlignedThinString(320-(x >> FRACBITS), 100+42, V_SNAPTOTOP|V_SNAPTORIGHT, va("firstDist = %u", rouletteData.firstDist));
|
||||
V_DrawRightAlignedThinString(320-(x >> FRACBITS), 100+50, V_SNAPTOTOP|V_SNAPTORIGHT, va("secondDist = %u", rouletteData.secondDist));
|
||||
V_DrawRightAlignedThinString(320-(x >> FRACBITS), 100+58, V_SNAPTOTOP|V_SNAPTORIGHT, va("secondToFirst = %u", rouletteData.secondToFirst));
|
||||
|
||||
#ifndef ITEM_LIST_SIZE
|
||||
Z_Free(rouletteData.itemList);
|
||||
|
|
@ -6096,7 +6064,7 @@ void K_ClearPersistentMessages()
|
|||
}
|
||||
|
||||
// Return value can be used for "paired" splitscreen messages, true = was displayed
|
||||
void K_AddMessageForPlayer(player_t *player, const char *msg, boolean interrupt, boolean persist)
|
||||
void K_AddMessageForPlayer(const player_t *player, const char *msg, boolean interrupt, boolean persist)
|
||||
{
|
||||
if (!player)
|
||||
return;
|
||||
|
|
@ -6564,6 +6532,10 @@ void K_drawKartHUD(void)
|
|||
if (cv_kartdebugdistribution.value)
|
||||
K_drawDistributionDebugger();
|
||||
|
||||
// temp debug
|
||||
V_DrawSmallString(8, 2, V_SNAPTOTOP, va("Exp/Dist mult: %.2f", FixedToFloat(stplyr->exp)));
|
||||
// V_DrawSmallString(8, 4, V_SNAPTOTOP, va("Exp/Dist mult: %.2f", FixedToFloat(stplyr->exp)));
|
||||
|
||||
if (cv_kartdebugnodes.value)
|
||||
{
|
||||
UINT8 p;
|
||||
|
|
|
|||
|
|
@ -108,11 +108,13 @@ extern patch_t *kp_facenum[MAXPLAYERS+1];
|
|||
extern patch_t *kp_unknownminimap;
|
||||
|
||||
void K_AddMessage(const char *msg, boolean interrupt, boolean persist);
|
||||
void K_AddMessageForPlayer(player_t *player, const char *msg, boolean interrupt, boolean persist);
|
||||
void K_AddMessageForPlayer(const player_t *player, const char *msg, boolean interrupt, boolean persist);
|
||||
void K_ClearPersistentMessages(void);
|
||||
void K_ClearPersistentMessageForPlayer(player_t *player);
|
||||
void K_TickMessages(void);
|
||||
|
||||
patch_t *K_GetSmallStaticCachedItemPatch(kartitems_t item);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PLAYERTAG_NONE,
|
||||
|
|
|
|||
62
src/k_kart.c
62
src/k_kart.c
|
|
@ -4007,8 +4007,10 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact)
|
|||
if (gametyperules & GTR_SPHERES)
|
||||
return;
|
||||
|
||||
// Give that Sonic guy some help.
|
||||
UINT16 scaledamps = min(amps, amps * (10 + player->kartspeed - player->kartweight) / 10);
|
||||
UINT16 scaledamps = min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10);
|
||||
|
||||
if (player->position <= 1)
|
||||
scaledamps /= 2;
|
||||
|
||||
for (int i = 0; i < (scaledamps/2); i++)
|
||||
{
|
||||
|
|
@ -4052,6 +4054,13 @@ void K_AwardPlayerAmps(player_t *player, UINT8 amps)
|
|||
|
||||
if (player->rings <= 0 && player->ampspending == 0)
|
||||
{
|
||||
// Auto Overdrive!
|
||||
// If this is a fresh OD, give 'em some extra juice to make up for lack of flexibility.
|
||||
if (!player->overdrive && player->mo && !P_MobjWasRemoved(player->mo))
|
||||
{
|
||||
S_StartSound(player->mo, sfx_gshac);
|
||||
player->amps *= 2;
|
||||
}
|
||||
K_Overdrive(player);
|
||||
}
|
||||
}
|
||||
|
|
@ -4091,7 +4100,7 @@ boolean K_Overdrive(player_t *player)
|
|||
S_StartSound(player->mo, sfx_cdfm35);
|
||||
S_StartSound(player->mo, sfx_cdfm13);
|
||||
|
||||
player->overdrive += (player->amps)*6;
|
||||
player->overdrive += (player->amps)*5;
|
||||
player->overshield += (player->amps)*2;
|
||||
player->overdrivepower = FRACUNIT;
|
||||
|
||||
|
|
@ -4112,7 +4121,7 @@ boolean K_DefensiveOverdrive(player_t *player)
|
|||
S_StartSound(player->mo, sfx_cdfm35);
|
||||
S_StartSound(player->mo, sfx_cdfm13);
|
||||
|
||||
player->overdrive += (player->amps)*4;
|
||||
player->overdrive += (player->amps)*3;
|
||||
player->overshield += (player->amps)*2 + TICRATE*2;
|
||||
player->overdrivepower = FRACUNIT;
|
||||
|
||||
|
|
@ -7424,7 +7433,7 @@ SINT8 K_GetTotallyRandomResult(UINT8 useodds)
|
|||
// Avoid calling K_FillItemRouletteData since that
|
||||
// function resets PR_ITEM_ROULETTE.
|
||||
spawnchance[i] = (
|
||||
totalspawnchance += K_KartGetItemOdds(NULL, NULL, useodds, i)
|
||||
totalspawnchance += K_KartGetBattleOdds(NULL, useodds, i)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -12712,6 +12721,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
else
|
||||
{
|
||||
UINT32 behind = K_GetItemRouletteDistance(player, player->itemRoulette.playing);
|
||||
behind = FixedMul(behind, max(player->exp, FRACUNIT/2));
|
||||
UINT32 behindMulti = behind / 500;
|
||||
behindMulti = min(behindMulti, 60);
|
||||
award = award * (behindMulti + 10) / 10;
|
||||
|
|
@ -12982,6 +12992,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
else
|
||||
player->rocketsneakertimer -= 3*TICRATE;
|
||||
player->botvars.itemconfirm = 2*TICRATE;
|
||||
player->overshield += TICRATE/2; // TEMP prototype
|
||||
}
|
||||
}
|
||||
else if (player->itemamount == 0)
|
||||
|
|
@ -12997,6 +13008,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
K_DoSneaker(player, 1);
|
||||
K_PlayBoostTaunt(player->mo);
|
||||
player->overshield += TICRATE/2; // TEMP prototype
|
||||
player->sneakertimer += TICRATE; // TEMP prototype
|
||||
player->itemamount--;
|
||||
player->botvars.itemconfirm = 0;
|
||||
}
|
||||
|
|
@ -14780,4 +14793,43 @@ boolean K_PlayerCanUseItem(player_t *player)
|
|||
return (player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime);
|
||||
}
|
||||
|
||||
fixed_t K_GetExpAdjustment(player_t *player)
|
||||
{
|
||||
fixed_t exp_power = 3*FRACUNIT/100; // adjust to change overall xp volatility
|
||||
fixed_t exp_stablerate = 3*FRACUNIT/10; // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
|
||||
fixed_t result = 0;
|
||||
|
||||
INT32 live_players = 0;
|
||||
|
||||
for (INT32 i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || player == players+i)
|
||||
continue;
|
||||
|
||||
live_players++;
|
||||
}
|
||||
|
||||
if (live_players < 8)
|
||||
{
|
||||
exp_power += (8 - live_players) * exp_power/4;
|
||||
}
|
||||
|
||||
// Increase XP for each player you're beating...
|
||||
for (INT32 i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || player == players+i)
|
||||
continue;
|
||||
|
||||
if (player->position < players[i].position)
|
||||
result += exp_power;
|
||||
}
|
||||
|
||||
// ...then take all of the XP you could possibly have earned,
|
||||
// and lose it proportional to the stable rate. If you're below
|
||||
// the stable threshold, this results in you losing XP.
|
||||
result -= FixedMul(exp_power, FixedMul(live_players*FRACUNIT, FRACUNIT - exp_stablerate));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -288,6 +288,8 @@ boolean K_ThunderDome(void);
|
|||
|
||||
boolean K_PlayerCanUseItem(player_t *player);
|
||||
|
||||
fixed_t K_GetExpAdjustment(player_t *player);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -263,6 +263,9 @@ mobj_t *Obj_FindCheckpoint(INT32 id);
|
|||
boolean Obj_GetCheckpointRespawnPosition(const mobj_t *checkpoint, vector3_t *return_pos);
|
||||
angle_t Obj_GetCheckpointRespawnAngle(const mobj_t *checkpoint);
|
||||
void Obj_ActivateCheckpointInstantly(mobj_t* mobj);
|
||||
UINT32 Obj_GetCheckpointCount();
|
||||
void Obj_ClearCheckpoints();
|
||||
void Obj_DeactivateCheckpoints();
|
||||
|
||||
/* Rideroid / Rideroid Node */
|
||||
void Obj_RideroidThink(mobj_t *mo);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "byteptr.h"
|
||||
#include "k_race.h"
|
||||
#include "command.h"
|
||||
#include "k_objects.h"
|
||||
|
||||
// I was ALMOST tempted to start tearing apart all
|
||||
// of the map loading code and turning it into C++
|
||||
|
|
@ -510,7 +511,8 @@ void gpRank_t::Update(void)
|
|||
}
|
||||
|
||||
lvl->time = UINT32_MAX;
|
||||
lvl->totalLapPoints = K_RaceLapCount(gamemap - 1) * 2;
|
||||
|
||||
lvl->totalLapPoints = ( K_RaceLapCount(gamemap - 1) + Obj_GetCheckpointCount() )* 2;
|
||||
lvl->totalPrisons = maptargets;
|
||||
|
||||
UINT8 i;
|
||||
|
|
|
|||
1080
src/k_roulette.c
1080
src/k_roulette.c
File diff suppressed because it is too large
Load diff
|
|
@ -78,17 +78,14 @@ botItemPriority_e K_GetBotItemPriority(kartitems_t result);
|
|||
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item);
|
||||
INT32 K_KartGetBattleOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item);
|
||||
|
||||
Gets the frequency an item should show up in
|
||||
an item bracket, and adjusted for special
|
||||
factors (such as Frantic Items).
|
||||
Gets legacy item priority.
|
||||
Currently used only for Battle monitors/spawners.
|
||||
|
||||
Input Arguments:-
|
||||
player - The player we intend to give the item to later.
|
||||
Can be NULL for generic use.
|
||||
roulette - The roulette data that we intend to
|
||||
insert this item into.
|
||||
pos - The item bracket we are in.
|
||||
item - The item to give.
|
||||
|
||||
|
|
@ -97,11 +94,11 @@ botItemPriority_e K_GetBotItemPriority(kartitems_t result);
|
|||
into the roulette.
|
||||
--------------------------------------------------*/
|
||||
|
||||
INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item);
|
||||
INT32 K_KartGetBattleOdds(const player_t *player, UINT8 pos, kartitems_t item);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox);
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun);
|
||||
|
||||
Fills out the item roulette struct when it is
|
||||
initially created. This function needs to be
|
||||
|
|
@ -113,12 +110,13 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
|
|||
Can be NULL for generic use.
|
||||
roulette - The roulette data struct to fill out.
|
||||
ringbox - Is this roulette fill triggered by a just-respawned Ring Box?
|
||||
dryrun - Are we calling this from the distribution debugger? Don't call RNG or write roulette data!
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox);
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include "r_fps.h"
|
||||
#include "g_party.h"
|
||||
#include "g_input.h"
|
||||
#include "k_objects.h"
|
||||
|
||||
boolean level_tally_t::UseBonuses(void)
|
||||
{
|
||||
|
|
@ -343,7 +344,7 @@ void level_tally_t::Init(player_t *player)
|
|||
if ((gametypes[gt]->rules & GTR_CIRCUIT) == GTR_CIRCUIT)
|
||||
{
|
||||
laps = player->lapPoints;
|
||||
totalLaps = numlaps;
|
||||
totalLaps = numlaps + numlaps * Obj_GetCheckpointCount();
|
||||
|
||||
if (inDuel == false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ struct MobjList
|
|||
{
|
||||
ptr->next(front());
|
||||
front(ptr);
|
||||
count_++;
|
||||
}
|
||||
|
||||
void erase(T* node)
|
||||
|
|
@ -45,6 +46,7 @@ struct MobjList
|
|||
{
|
||||
front(node->next());
|
||||
node->next(nullptr);
|
||||
count_--;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -69,6 +71,7 @@ struct MobjList
|
|||
{
|
||||
prev->next(node->next());
|
||||
node->next(nullptr);
|
||||
count_--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -77,9 +80,12 @@ struct MobjList
|
|||
auto begin() const { return view().begin(); }
|
||||
auto end() const { return view().end(); }
|
||||
|
||||
auto count() { return count_; }
|
||||
|
||||
private:
|
||||
void front(T* ptr) { Mobj::ManagedPtr {Head} = ptr; }
|
||||
auto view() const { return MobjListView(front(), [](T* node) { return node->next(); }); }
|
||||
UINT32 count_ = 0;
|
||||
};
|
||||
|
||||
}; // namespace srb2
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ void Obj_AmpsThink (mobj_t *amps)
|
|||
|
||||
amps->extravalue2++;
|
||||
|
||||
speed += amps->extravalue1 * amps->scale/2;
|
||||
speed += amps->extravalue2 * amps->scale/2;
|
||||
|
||||
fakez = mo->z + (vert * amps->extravalue1 / AMP_ARCTIME);
|
||||
damper = 1;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "../doomdef.h"
|
||||
#include "../doomtype.h"
|
||||
#include "../info.h"
|
||||
#include "../g_game.h"
|
||||
#include "../k_color.h"
|
||||
#include "../k_kart.h"
|
||||
#include "../k_objects.h"
|
||||
|
|
@ -34,9 +35,17 @@
|
|||
#include "../sounds.h"
|
||||
#include "../tables.h"
|
||||
|
||||
using std::vector;
|
||||
using std::pair;
|
||||
using std::min;
|
||||
using std::max;
|
||||
using std::clamp;
|
||||
|
||||
extern mobj_t* svg_checkpoints;
|
||||
|
||||
#define checkpoint_id(o) ((o)->thing_args[0])
|
||||
#define checkpoint_linetag(o) ((o)->thing_args[1])
|
||||
#define checkpoint_extralength(o) ((o)->thing_args[2])
|
||||
#define checkpoint_other(o) ((o)->target)
|
||||
#define checkpoint_orb(o) ((o)->tracer)
|
||||
#define checkpoint_arm(o) ((o)->hnext)
|
||||
|
|
@ -51,12 +60,14 @@ namespace
|
|||
|
||||
struct LineOnDemand : line_t
|
||||
{
|
||||
LineOnDemand(const line_t* line) {}
|
||||
|
||||
LineOnDemand(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) :
|
||||
line_t {
|
||||
.v1 = &v1_data_,
|
||||
.dx = x2 - x1,
|
||||
.dy = y2 - y1,
|
||||
.bbox = {std::max(y1, y2), std::min(y1, y2), std::min(x1, x2), std::max(x1, x2)},
|
||||
.bbox = {max(y1, y2), min(y1, y2), min(x1, x2), max(x1, x2)},
|
||||
},
|
||||
v1_data_ {.x = x1, .y = y1}
|
||||
{
|
||||
|
|
@ -76,6 +87,12 @@ struct LineOnDemand : line_t
|
|||
bbox[BOXLEFT] <= other.bbox[BOXRIGHT] && bbox[BOXRIGHT] >= other.bbox[BOXLEFT];
|
||||
}
|
||||
|
||||
bool overlaps(const line_t& other) const
|
||||
{
|
||||
return bbox[BOXTOP] >= other.bbox[BOXBOTTOM] && bbox[BOXBOTTOM] <= other.bbox[BOXTOP] &&
|
||||
bbox[BOXLEFT] <= other.bbox[BOXRIGHT] && bbox[BOXRIGHT] >= other.bbox[BOXLEFT];
|
||||
}
|
||||
|
||||
private:
|
||||
vertex_t v1_data_;
|
||||
};
|
||||
|
|
@ -170,6 +187,30 @@ struct Checkpoint : mobj_t
|
|||
deactivate();
|
||||
}
|
||||
|
||||
// will not work properly after a player enters intoa new lap
|
||||
INT32 players_passed()
|
||||
{
|
||||
INT32 pcount = 0;
|
||||
for (INT32 i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && !players[i].spectator && players[i].checkpointId >= id())
|
||||
pcount++;
|
||||
}
|
||||
return pcount;
|
||||
}
|
||||
|
||||
boolean top_half_has_passed()
|
||||
{
|
||||
INT32 pcount = 0;
|
||||
INT32 winningpos = 1;
|
||||
|
||||
INT32 nump = D_NumPlayersInRace();
|
||||
winningpos = nump / 2;
|
||||
winningpos += nump % 2;
|
||||
|
||||
return players_passed() >= winningpos;
|
||||
}
|
||||
|
||||
void animate()
|
||||
{
|
||||
orient();
|
||||
|
|
@ -181,10 +222,11 @@ struct Checkpoint : mobj_t
|
|||
|
||||
if (!clip_var())
|
||||
{
|
||||
speed(speed() - FixedDiv(speed() / 50, std::max<fixed_t>(speed_multiplier(), 1)));
|
||||
speed(speed() - FixedDiv(speed() / 50, max<fixed_t>(speed_multiplier(), 1)));
|
||||
}
|
||||
}
|
||||
else if (!activated())
|
||||
|
||||
if (!top_half_has_passed())
|
||||
{
|
||||
sparkle_between(0);
|
||||
}
|
||||
|
|
@ -193,7 +235,7 @@ struct Checkpoint : mobj_t
|
|||
void twirl(angle_t dir, fixed_t multiplier)
|
||||
{
|
||||
var(0);
|
||||
speed_multiplier(std::clamp(multiplier, kMinSpeedMultiplier, kMaxSpeedMultiplier));
|
||||
speed_multiplier(clamp(multiplier, kMinSpeedMultiplier, kMaxSpeedMultiplier));
|
||||
speed(FixedDiv(kBaseSpeed, speed_multiplier()));
|
||||
reverse(AngleDeltaSigned(angle_to_other(), dir) > 0);
|
||||
|
||||
|
|
@ -266,7 +308,7 @@ private:
|
|||
kMinPivotDelay
|
||||
);
|
||||
|
||||
return to_angle(FixedDiv(std::max(var(), pos) - pos, FRACUNIT - pos)) / 4;
|
||||
return to_angle(FixedDiv(max(var(), pos) - pos, FRACUNIT - pos)) / 4;
|
||||
}
|
||||
|
||||
void orient()
|
||||
|
|
@ -304,7 +346,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void spawn_sparkle(const vector3_t& pos, fixed_t xy_momentum, fixed_t z_momentum, angle_t dir)
|
||||
void spawn_sparkle(const vector3_t& pos, fixed_t xy_momentum, fixed_t z_momentum, angle_t dir, skincolornum_t color = SKINCOLOR_ULTRAMARINE)
|
||||
{
|
||||
auto rng = [=](int units) { return P_RandomRange(PR_DECORATION, -(units) * scale, +(units) * scale); };
|
||||
|
||||
|
|
@ -324,10 +366,10 @@ private:
|
|||
if (xy_momentum)
|
||||
{
|
||||
P_Thrust(p, dir, xy_momentum);
|
||||
p->momz = P_RandomKey(PR_DECORATION, std::max<fixed_t>(z_momentum, 1));
|
||||
p->momz = P_RandomKey(PR_DECORATION, max<fixed_t>(z_momentum, 1));
|
||||
p->destscale = 0;
|
||||
p->scalespeed = p->scale / 35;
|
||||
p->color = SKINCOLOR_ULTRAMARINE;
|
||||
p->color = color;
|
||||
p->fuse = 0;
|
||||
|
||||
// Something lags at the start of the level. The
|
||||
|
|
@ -342,7 +384,7 @@ private:
|
|||
}
|
||||
else
|
||||
{
|
||||
p->color = K_RainbowColor(leveltime);
|
||||
p->color = color;
|
||||
p->fuse = 2;
|
||||
}
|
||||
}
|
||||
|
|
@ -369,7 +411,8 @@ private:
|
|||
{x + FixedMul(ofs, FCOS(a)), y + FixedMul(ofs, FSIN(a)), z + (kSparkleZ * scale)},
|
||||
momentum,
|
||||
momentum / 2,
|
||||
dir
|
||||
dir,
|
||||
activated() ? SKINCOLOR_GREEN : SKINCOLOR_ULTRAMARINE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -402,14 +445,93 @@ struct CheckpointManager
|
|||
auto begin() { return list_.begin(); }
|
||||
auto end() { return list_.end(); }
|
||||
|
||||
auto find(INT32 id) { return std::find_if(begin(), end(), [id](Checkpoint* chk) { return chk->id() == id; }); }
|
||||
auto find_checkpoint(INT32 id) {
|
||||
auto it = find_if(list_.begin(), list_.end(), [id](auto pair) { return pair.first->id() == id; });
|
||||
if (it != list_.end())
|
||||
{
|
||||
return it->first;
|
||||
}
|
||||
return static_cast<Checkpoint*>(nullptr);
|
||||
}
|
||||
|
||||
void push_front(Checkpoint* chk) { list_.push_front(chk); }
|
||||
// auto find_pair(Checkpoint* chk) {
|
||||
// pair<Checkpoint*, vector<line_t*>> retpair;
|
||||
// auto it = find_if(list_.begin(), list_.end(), [chk](auto pair) { return pair.first == chk; });
|
||||
// if (it != list_.end())
|
||||
// {
|
||||
// retpair = *it;
|
||||
// return retpair;
|
||||
// }
|
||||
// return static_cast<pair<Checkpoint*, vector<line_t*>>>(nullptr);
|
||||
// }
|
||||
|
||||
void erase(Checkpoint* chk) { list_.erase(chk); }
|
||||
void remove_checkpoint(mobj_t* end)
|
||||
{
|
||||
auto chk = static_cast<Checkpoint*>(end);
|
||||
auto it = find_if(list_.begin(), list_.end(), [&](auto pair) { return pair.first == chk; });
|
||||
if (it != list_.end())
|
||||
{
|
||||
list_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void link_checkpoint(mobj_t* end)
|
||||
{
|
||||
auto chk = static_cast<Checkpoint*>(end);
|
||||
auto id = chk->id();
|
||||
if (chk->spawnpoint && id == 0)
|
||||
{
|
||||
auto msg = fmt::format(
|
||||
"Checkpoint thing (index #{}, thing type {}) has an invalid ID! ID must not be 0.\n",
|
||||
chk->spawnpoint - mapthings,
|
||||
chk->spawnpoint->type
|
||||
);
|
||||
CONS_Alert(CONS_WARNING, "%s", msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto other = find_checkpoint(id))
|
||||
{
|
||||
if (chk->spawnpoint && other->spawnpoint && chk->spawnpoint->angle != other->spawnpoint->angle)
|
||||
{
|
||||
auto msg = fmt::format(
|
||||
"Checkpoints things with ID {} (index #{} and #{}, thing type {}) do not have matching angles.\n",
|
||||
chk->id(),
|
||||
chk->spawnpoint - mapthings,
|
||||
other->spawnpoint - mapthings,
|
||||
chk->spawnpoint->type
|
||||
);
|
||||
CONS_Alert(CONS_WARNING, "%s", msg.c_str());
|
||||
return;
|
||||
}
|
||||
other->other(chk);
|
||||
chk->other(other);
|
||||
}
|
||||
else // Checkpoint isn't in the list, find any associated tagged lines and make the pair
|
||||
{
|
||||
vector<line_t*> checklines;
|
||||
if (checkpoint_linetag(chk))
|
||||
{
|
||||
INT32 li;
|
||||
INT32 tag = checkpoint_linetag(chk);
|
||||
TAG_ITER_LINES(tag, li)
|
||||
{
|
||||
line_t* line = lines + li;
|
||||
checklines.push_back(line);
|
||||
}
|
||||
}
|
||||
list_.emplace_back(chk, move(checklines));
|
||||
}
|
||||
|
||||
chk->gingerbread();
|
||||
}
|
||||
|
||||
void clear() { list_.clear(); }
|
||||
|
||||
auto count() { return list_.size(); }
|
||||
|
||||
private:
|
||||
srb2::MobjList<Checkpoint, svg_checkpoints> list_;
|
||||
vector<pair<Checkpoint*, vector<line_t*>>> list_;
|
||||
};
|
||||
|
||||
CheckpointManager g_checkpoints;
|
||||
|
|
@ -418,54 +540,15 @@ CheckpointManager g_checkpoints;
|
|||
|
||||
void Obj_LinkCheckpoint(mobj_t* end)
|
||||
{
|
||||
auto chk = static_cast<Checkpoint*>(end);
|
||||
|
||||
if (chk->spawnpoint && chk->id() == 0)
|
||||
{
|
||||
auto msg = fmt::format(
|
||||
"Checkpoint thing (index #{}, thing type {}) has an invalid ID! ID must not be 0.\n",
|
||||
chk->spawnpoint - mapthings,
|
||||
chk->spawnpoint->type
|
||||
);
|
||||
CONS_Alert(CONS_WARNING, "%s", msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto it = g_checkpoints.find(chk->id()); it != g_checkpoints.end())
|
||||
{
|
||||
Checkpoint* other = *it;
|
||||
|
||||
if (chk->spawnpoint && other->spawnpoint && chk->spawnpoint->angle != other->spawnpoint->angle)
|
||||
{
|
||||
auto msg = fmt::format(
|
||||
"Checkpoints things with ID {} (index #{} and #{}, thing type {}) do not have matching angles.\n",
|
||||
chk->id(),
|
||||
chk->spawnpoint - mapthings,
|
||||
other->spawnpoint - mapthings,
|
||||
chk->spawnpoint->type
|
||||
);
|
||||
CONS_Alert(CONS_WARNING, "%s", msg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
other->other(chk);
|
||||
chk->other(other);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_checkpoints.push_front(chk);
|
||||
}
|
||||
|
||||
chk->gingerbread();
|
||||
g_checkpoints.link_checkpoint(end);
|
||||
}
|
||||
|
||||
void Obj_UnlinkCheckpoint(mobj_t* end)
|
||||
{
|
||||
auto chk = static_cast<Checkpoint*>(end);
|
||||
|
||||
g_checkpoints.erase(chk);
|
||||
|
||||
g_checkpoints.remove_checkpoint(end);
|
||||
P_RemoveMobj(chk->orb());
|
||||
P_RemoveMobj(chk->arm());
|
||||
}
|
||||
|
||||
void Obj_CheckpointThink(mobj_t* end)
|
||||
|
|
@ -480,39 +563,64 @@ void Obj_CheckpointThink(mobj_t* end)
|
|||
chk->animate();
|
||||
}
|
||||
|
||||
void Obj_CrossCheckpoints(player_t* player, fixed_t old_x, fixed_t old_y)
|
||||
void __attribute__((optimize("O0"))) Obj_CrossCheckpoints(player_t* player, fixed_t old_x, fixed_t old_y)
|
||||
{
|
||||
LineOnDemand ray(old_x, old_y, player->mo->x, player->mo->y, player->mo->radius);
|
||||
|
||||
auto it = std::find_if(
|
||||
auto it = find_if(
|
||||
g_checkpoints.begin(),
|
||||
g_checkpoints.end(),
|
||||
[&](const Checkpoint* chk)
|
||||
[&](auto chkpair)
|
||||
{
|
||||
Checkpoint* chk = chkpair.first;
|
||||
if (!chk->valid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LineOnDemand gate = chk->crossing_line();
|
||||
LineOnDemand* gate;
|
||||
|
||||
if (chkpair.second.empty())
|
||||
{
|
||||
LineOnDemand dyngate = chk->crossing_line();
|
||||
if (!ray.overlaps(dyngate))
|
||||
return false;
|
||||
gate = &dyngate;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = find_if(
|
||||
chkpair.second.begin(),
|
||||
chkpair.second.end(),
|
||||
[&](const line_t* line)
|
||||
{
|
||||
return ray.overlaps(*line);
|
||||
}
|
||||
);
|
||||
|
||||
if (it == chkpair.second.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
line_t* line = *it;
|
||||
gate = static_cast<LineOnDemand*>(line);
|
||||
}
|
||||
|
||||
// Check if the bounding boxes of the two lines
|
||||
// overlap. This relies on the player movement not
|
||||
// being so large that it creates an oversized box,
|
||||
// but thankfully that doesn't seem to happen, under
|
||||
// normal circumstances.
|
||||
if (!ray.overlaps(gate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
INT32 side = P_PointOnLineSide(player->mo->x, player->mo->y, &gate);
|
||||
INT32 oldside = P_PointOnLineSide(old_x, old_y, &gate);
|
||||
INT32 side = P_PointOnLineSide(player->mo->x, player->mo->y, gate);
|
||||
INT32 oldside = P_PointOnLineSide(old_x, old_y, gate);
|
||||
|
||||
if (side == oldside)
|
||||
{
|
||||
// Did not cross.
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -524,41 +632,58 @@ void Obj_CrossCheckpoints(player_t* player, fixed_t old_x, fixed_t old_y)
|
|||
return;
|
||||
}
|
||||
|
||||
Checkpoint* chk = *it;
|
||||
Checkpoint* chk = it->first;
|
||||
|
||||
if (chk->activated())
|
||||
if (player->checkpointId == chk->id())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (Checkpoint* chk : g_checkpoints)
|
||||
if (player->position <= 1)
|
||||
{
|
||||
if (chk->valid())
|
||||
{
|
||||
// Swing down any previously passed checkpoints.
|
||||
// TODO: this could look weird in multiplayer if
|
||||
// other players cross different checkpoints.
|
||||
chk->untwirl();
|
||||
chk->other()->untwirl();
|
||||
}
|
||||
angle_t direction = R_PointToAngle2(old_x, old_y, player->mo->x, player->mo->y);
|
||||
fixed_t speed_multiplier = FixedDiv(player->speed, K_GetKartSpeed(player, false, false));
|
||||
chk->twirl(direction, speed_multiplier);
|
||||
chk->other()->twirl(direction, speed_multiplier);
|
||||
}
|
||||
|
||||
angle_t direction = R_PointToAngle2(old_x, old_y, player->mo->x, player->mo->y);
|
||||
fixed_t speed_multiplier = FixedDiv(player->speed, K_GetKartSpeed(player, false, false));
|
||||
|
||||
chk->twirl(direction, speed_multiplier);
|
||||
chk->other()->twirl(direction, speed_multiplier);
|
||||
if (gametyperules & GTR_CHECKPOINTS)
|
||||
{
|
||||
for (auto chkpair : g_checkpoints)
|
||||
{
|
||||
Checkpoint* chk = chkpair.first;
|
||||
if (chk->valid())
|
||||
{
|
||||
chk->untwirl();
|
||||
chk->other()->untwirl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
S_StartSound(player->mo, sfx_s3k63);
|
||||
|
||||
player->checkpointId = chk->id();
|
||||
|
||||
if (D_NumPlayersInRace() > 1 && !K_IsPlayerLosing(player))
|
||||
{
|
||||
if (player->position == 1)
|
||||
{
|
||||
player->lapPoints += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->lapPoints += 1;
|
||||
}
|
||||
}
|
||||
|
||||
player->exp += K_GetExpAdjustment(player);
|
||||
|
||||
K_UpdatePowerLevels(player, player->laps, false);
|
||||
}
|
||||
|
||||
mobj_t *Obj_FindCheckpoint(INT32 id)
|
||||
mobj_t* Obj_FindCheckpoint(INT32 id)
|
||||
{
|
||||
auto it = g_checkpoints.find(id);
|
||||
|
||||
return it != g_checkpoints.end() ? *it : nullptr;
|
||||
return g_checkpoints.find_checkpoint(id);
|
||||
}
|
||||
|
||||
boolean Obj_GetCheckpointRespawnPosition(const mobj_t* mobj, vector3_t* return_pos)
|
||||
|
|
@ -593,3 +718,27 @@ void Obj_ActivateCheckpointInstantly(mobj_t* mobj)
|
|||
chk->other()->activate();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a count of checkpoint gates, not objects
|
||||
UINT32 Obj_GetCheckpointCount()
|
||||
{
|
||||
return g_checkpoints.count();
|
||||
}
|
||||
|
||||
void Obj_ClearCheckpoints()
|
||||
{
|
||||
g_checkpoints.clear();
|
||||
}
|
||||
|
||||
void Obj_DeactivateCheckpoints()
|
||||
{
|
||||
for (auto chkpair : g_checkpoints)
|
||||
{
|
||||
Checkpoint* chk = chkpair.first;
|
||||
if (chk->valid())
|
||||
{
|
||||
chk->untwirl();
|
||||
chk->other()->untwirl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3092,11 +3092,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
{
|
||||
K_DoPowerClash(target, inflictor);
|
||||
|
||||
if (inflictor->type != MT_PLAYER)
|
||||
{
|
||||
K_SpawnAmps(player, 5, inflictor);
|
||||
}
|
||||
|
||||
if (inflictor->type == MT_SUPER_FLICKY)
|
||||
{
|
||||
Obj_BlockSuperFlicky(inflictor);
|
||||
|
|
@ -3565,4 +3560,6 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
|
|||
{
|
||||
P_FlingBurst(player, fa, MT_DEBTSPIKE, 0, 3 * FRACUNIT / 2, i++);
|
||||
}
|
||||
|
||||
K_DefensiveOverdrive(player);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12572,8 +12572,6 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
|
|||
return false;
|
||||
break;
|
||||
case MT_CHECKPOINT_END:
|
||||
if (!(gametyperules & GTR_CHECKPOINTS))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -281,6 +281,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].laps);
|
||||
WRITEUINT8(save->p, players[i].latestlap);
|
||||
WRITEUINT32(save->p, players[i].lapPoints);
|
||||
WRITEINT32(save->p, players[i].exp);
|
||||
WRITEINT32(save->p, players[i].cheatchecknum);
|
||||
WRITEINT32(save->p, players[i].checkpointId);
|
||||
|
||||
|
|
@ -743,7 +744,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
}
|
||||
#endif
|
||||
|
||||
WRITEUINT8(save->p, players[i].itemRoulette.useOdds);
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.preexpdist);
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.dist);
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.index);
|
||||
WRITEUINT8(save->p, players[i].itemRoulette.sound);
|
||||
|
|
@ -937,6 +938,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].laps = READUINT8(save->p); // Number of laps (optional)
|
||||
players[i].latestlap = READUINT8(save->p);
|
||||
players[i].lapPoints = READUINT32(save->p);
|
||||
players[i].exp = READINT32(save->p);
|
||||
players[i].cheatchecknum = READINT32(save->p);
|
||||
players[i].checkpointId = READINT32(save->p);
|
||||
|
||||
|
|
@ -1366,7 +1368,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
}
|
||||
#endif
|
||||
|
||||
players[i].itemRoulette.useOdds = READUINT8(save->p);
|
||||
players[i].itemRoulette.preexpdist = READUINT32(save->p);
|
||||
players[i].itemRoulette.dist = READUINT32(save->p);
|
||||
players[i].itemRoulette.index = (size_t)READUINT32(save->p);
|
||||
players[i].itemRoulette.sound = READUINT8(save->p);
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@
|
|||
#include "k_hud.h" // K_ClearPersistentMessages
|
||||
#include "k_endcam.h"
|
||||
#include "k_credits.h"
|
||||
#include "k_objects.h"
|
||||
|
||||
// Replay names have time
|
||||
#if !defined (UNDER_CE)
|
||||
|
|
@ -8586,6 +8587,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
|
|||
|
||||
LUA_InvalidateLevel();
|
||||
|
||||
Obj_ClearCheckpoints();
|
||||
|
||||
for (ss = sectors; sectors+numsectors != ss; ss++)
|
||||
{
|
||||
Z_Free(ss->attached);
|
||||
|
|
|
|||
|
|
@ -2117,6 +2117,14 @@ static void K_HandleLapIncrement(player_t *player)
|
|||
player->lapPoints++;
|
||||
}
|
||||
}
|
||||
|
||||
player->exp += K_GetExpAdjustment(player);
|
||||
|
||||
if (player->position == 1 && !(gametyperules & GTR_CHECKPOINTS))
|
||||
{
|
||||
Obj_DeactivateCheckpoints();
|
||||
}
|
||||
player->checkpointId = 0;
|
||||
}
|
||||
|
||||
// Set up lap animation vars
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue