mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
- numlaps 0 now sets the race to 0 laps.
- Players will finish upon crossing the finishing line
immediately after POSITION.
- gptest merged with numlaps.
- numlaps works in GP.
- Warping to sprint maps with numlaps already set now lets
numlaps exceed the map default. It was previous capped
at map default ONLY when loading into the level (so you
could still exceed it by setting numlaps during
POSITION). Now it's consistent.
- numlaps can now be set at any time during the race.
446 lines
11 KiB
C
446 lines
11 KiB
C
// SONIC ROBO BLAST 2 KART
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2018-2020 by Kart Krew
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file k_race.c
|
|
/// \brief Race Mode specific code.
|
|
|
|
#include "k_race.h"
|
|
|
|
#include "k_kart.h"
|
|
#include "k_battle.h"
|
|
#include "k_pwrlv.h"
|
|
#include "k_color.h"
|
|
#include "k_respawn.h"
|
|
#include "doomdef.h"
|
|
#include "hu_stuff.h"
|
|
#include "g_game.h"
|
|
#include "m_random.h"
|
|
#include "p_local.h"
|
|
#include "p_slopes.h"
|
|
#include "p_setup.h"
|
|
#include "r_draw.h"
|
|
#include "r_local.h"
|
|
#include "s_sound.h"
|
|
#include "st_stuff.h"
|
|
#include "v_video.h"
|
|
#include "z_zone.h"
|
|
#include "m_misc.h"
|
|
#include "m_cond.h"
|
|
#include "f_finale.h"
|
|
#include "lua_hud.h" // For Lua hud checks
|
|
#include "lua_hook.h" // For MobjDamage and ShouldDamage
|
|
#include "m_cheat.h" // objectplacing
|
|
#include "p_spec.h"
|
|
|
|
#include "k_waypoint.h"
|
|
#include "k_bot.h"
|
|
#include "k_hud.h"
|
|
|
|
line_t *finishBeamLine = NULL;
|
|
|
|
static mobj_t *beamPoints[2];
|
|
static UINT8 numBeamPoints = 0;
|
|
|
|
/*--------------------------------------------------
|
|
void K_ClearFinishBeamLine(void)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
void K_ClearFinishBeamLine(void)
|
|
{
|
|
size_t i;
|
|
|
|
finishBeamLine = NULL;
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
beamPoints[i] = NULL;
|
|
}
|
|
|
|
numBeamPoints = 0;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_FreeFinishBeamLine(void)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
static void K_FreeFinishBeamLine(void)
|
|
{
|
|
if (finishBeamLine != NULL)
|
|
{
|
|
if (finishBeamLine->v1 != NULL)
|
|
{
|
|
Z_Free(finishBeamLine->v1);
|
|
}
|
|
|
|
if (finishBeamLine->v2 != NULL)
|
|
{
|
|
Z_Free(finishBeamLine->v2);
|
|
}
|
|
|
|
Z_Free(finishBeamLine);
|
|
}
|
|
|
|
K_ClearFinishBeamLine();
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_CreateFinishLineFromPoints(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
static void K_CreateFinishLineFromPoints(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2)
|
|
{
|
|
I_Assert(finishBeamLine == NULL); // Needs to be NULL
|
|
|
|
finishBeamLine = Z_Calloc(sizeof (*finishBeamLine), PU_LEVEL, NULL);
|
|
|
|
finishBeamLine->v1 = Z_Calloc(sizeof (*finishBeamLine->v1), PU_LEVEL, NULL);
|
|
finishBeamLine->v1->x = x1;
|
|
finishBeamLine->v1->y = y1;
|
|
|
|
finishBeamLine->v2 = Z_Calloc(sizeof (*finishBeamLine->v2), PU_LEVEL, NULL);
|
|
finishBeamLine->v2->x = x2;
|
|
finishBeamLine->v2->y = y2;
|
|
|
|
finishBeamLine->dx = x2 - x1;
|
|
finishBeamLine->dy = y2 - y1;
|
|
|
|
finishBeamLine->angle = R_PointToAngle2(0, 0, finishBeamLine->dx, finishBeamLine->dy);
|
|
|
|
finishBeamLine->flags = 0;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
boolean K_GenerateFinishBeamLine(void)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
boolean K_GenerateFinishBeamLine(void)
|
|
{
|
|
mapthing_t *mt;
|
|
|
|
INT64 bounds[4];
|
|
angle_t angle = 0;
|
|
|
|
boolean valid = false;
|
|
size_t i;
|
|
|
|
// Ensure everything's freed by this time.
|
|
K_FreeFinishBeamLine();
|
|
|
|
// First: attempt to create from beam point objects
|
|
for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
|
|
{
|
|
if (numBeamPoints >= 2)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (mt->type == mobjinfo[MT_BEAMPOINT].doomednum)
|
|
{
|
|
beamPoints[numBeamPoints] = mt->mobj;
|
|
numBeamPoints++;
|
|
}
|
|
}
|
|
|
|
if (numBeamPoints == 2)
|
|
{
|
|
// Found 'em! Really easy to generate a line out of these :)
|
|
|
|
K_CreateFinishLineFromPoints(
|
|
beamPoints[0]->x, beamPoints[0]->y,
|
|
beamPoints[1]->x, beamPoints[1]->y
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
bounds[0] = INT64_MAX; // min x
|
|
bounds[1] = INT64_MIN; // max x
|
|
bounds[2] = INT64_MAX; // min y
|
|
bounds[3] = INT64_MIN; // max y
|
|
|
|
for (i = 0; i < numlines; i++)
|
|
{
|
|
angle_t thisAngle;
|
|
|
|
if (lines[i].special != 2001)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
thisAngle = R_PointToAngle2(0, 0, lines[i].dx, lines[i].dy);
|
|
|
|
bounds[0] = min(bounds[0], min(lines[i].v1->x, lines[i].v2->x)); // min x
|
|
bounds[1] = max(bounds[1], max(lines[i].v1->x, lines[i].v2->x)); // max x
|
|
bounds[2] = min(bounds[2], min(lines[i].v1->y, lines[i].v2->y)); // min y
|
|
bounds[3] = max(bounds[3], max(lines[i].v1->y, lines[i].v2->y)); // max y
|
|
|
|
if (valid == false)
|
|
{
|
|
angle = thisAngle;
|
|
valid = true;
|
|
}
|
|
else if (angle != thisAngle)
|
|
{
|
|
// Do not even attempt to bother with curved finish lines.
|
|
// Will likely just crash.
|
|
valid = false;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid == true)
|
|
{
|
|
fixed_t span = P_AproxDistance(bounds[1] - bounds[0], bounds[3] - bounds[2]) / 2;
|
|
|
|
fixed_t cx = (bounds[0] + bounds[1]) / 2;
|
|
fixed_t cy = (bounds[2] + bounds[3]) / 2;
|
|
|
|
fixed_t spanC = FixedMul(span, FINECOSINE(angle >> ANGLETOFINESHIFT));
|
|
fixed_t spanS = FixedMul(span, FINESINE(angle >> ANGLETOFINESHIFT));
|
|
|
|
K_CreateFinishLineFromPoints(
|
|
cx - spanC, cy - spanS,
|
|
cx + spanC, cy + spanS
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t *line, boolean reverse)
|
|
|
|
Draws a helix out of rainbow colored orbs along a line, unique for each display player.
|
|
Called twice for the finish line beam effect.
|
|
|
|
Input Arguments:-
|
|
offset - Offset value for positioning. Changed every tick to make it animate.
|
|
aiming - Starting vertical angle value. Changed every tick to make it animate.
|
|
line - Linedef to draw along.
|
|
reverse - Draw in reverse. Call twice with this toggled to make a double helix.
|
|
|
|
Return:-
|
|
None
|
|
--------------------------------------------------*/
|
|
|
|
static void K_DrawFinishLineBeamForLine(fixed_t offset, angle_t aiming, line_t *line, boolean reverse)
|
|
{
|
|
const fixed_t linelength = P_AproxDistance(line->dx, line->dy);
|
|
const fixed_t xstep = FixedDiv(line->dx, linelength);
|
|
const fixed_t ystep = FixedDiv(line->dy, linelength);
|
|
|
|
fixed_t linex = line->v1->x;
|
|
fixed_t liney = line->v1->y;
|
|
angle_t lineangle = line->angle + ANGLE_90;
|
|
|
|
UINT8 i;
|
|
|
|
if (line->flags & ML_NOCLIMB)
|
|
{
|
|
// Line is flipped
|
|
lineangle += ANGLE_180;
|
|
}
|
|
|
|
linex += FixedMul(offset, xstep);
|
|
liney += FixedMul(offset, ystep);
|
|
|
|
while (offset < linelength)
|
|
{
|
|
#define COLORCYCLELEN 10
|
|
const UINT8 colorcycle[COLORCYCLELEN] = {
|
|
SKINCOLOR_PERIWINKLE,
|
|
SKINCOLOR_SLATE,
|
|
SKINCOLOR_BLOSSOM,
|
|
SKINCOLOR_RASPBERRY,
|
|
SKINCOLOR_ORANGE,
|
|
SKINCOLOR_YELLOW,
|
|
SKINCOLOR_LIME,
|
|
SKINCOLOR_TURTLE,
|
|
SKINCOLOR_ROBIN,
|
|
SKINCOLOR_JAWZ
|
|
};
|
|
|
|
const UINT8 numframes = 5;
|
|
const angle_t framethreshold = ANGLE_180 / (numframes-1);
|
|
const angle_t frameaim = aiming + (framethreshold / 2);
|
|
|
|
fixed_t x, y, z;
|
|
UINT8 spriteframe = 0;
|
|
|
|
x = linex + FixedMul(FixedMul(FINISHLINEBEAM_SPACING, FINECOSINE(lineangle >> ANGLETOFINESHIFT)), FINECOSINE(aiming >> ANGLETOFINESHIFT));
|
|
y = liney + FixedMul(FixedMul(FINISHLINEBEAM_SPACING, FINESINE(lineangle >> ANGLETOFINESHIFT)), FINECOSINE(aiming >> ANGLETOFINESHIFT));
|
|
z = FINISHLINEBEAM_SPACING + FixedMul(FINISHLINEBEAM_SPACING, FINESINE(aiming >> ANGLETOFINESHIFT));
|
|
|
|
if (leveltime >= starttime)
|
|
{
|
|
spriteframe = 4; // Weakest sprite when passable
|
|
}
|
|
else if (frameaim > ANGLE_180)
|
|
{
|
|
spriteframe = (ANGLE_MAX - frameaim) / framethreshold;
|
|
}
|
|
else
|
|
{
|
|
spriteframe = frameaim / framethreshold;
|
|
}
|
|
|
|
for (i = 0; i <= r_splitscreen; i++)
|
|
{
|
|
if (playeringame[displayplayers[i]] && players[displayplayers[i]].mo && !P_MobjWasRemoved(players[displayplayers[i]].mo))
|
|
{
|
|
mobj_t *beam;
|
|
|
|
beam = P_SpawnMobj(x, y, players[displayplayers[i]].mo->z + z, MT_THOK);
|
|
P_SetMobjState(beam, S_FINISHBEAM1 + spriteframe);
|
|
|
|
beam->colorized = true;
|
|
beam->renderflags = RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]);
|
|
|
|
if (reverse)
|
|
{
|
|
beam->color = colorcycle[((leveltime / 4) + (COLORCYCLELEN/2)) % COLORCYCLELEN];
|
|
}
|
|
else
|
|
{
|
|
beam->color = colorcycle[(leveltime / 4) % COLORCYCLELEN];
|
|
}
|
|
}
|
|
}
|
|
|
|
offset += FINISHLINEBEAM_SPACING;
|
|
linex += FixedMul(FINISHLINEBEAM_SPACING, xstep);
|
|
liney += FixedMul(FINISHLINEBEAM_SPACING, ystep);
|
|
|
|
if (reverse)
|
|
{
|
|
aiming -= ANGLE_45;
|
|
}
|
|
else
|
|
{
|
|
aiming += ANGLE_45;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i <= r_splitscreen; i++)
|
|
{
|
|
if (playeringame[displayplayers[i]] && players[displayplayers[i]].mo && !P_MobjWasRemoved(players[displayplayers[i]].mo))
|
|
{
|
|
UINT8 j;
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
vertex_t *v = line->v1;
|
|
mobj_t *end1, *end2;
|
|
angle_t a = R_PointToAngle2(0, 0, line->dx, line->dy);
|
|
fixed_t sx;
|
|
fixed_t sy;
|
|
|
|
/*
|
|
if (line->flags & ML_NOCLIMB)
|
|
{
|
|
a += ANGLE_180;
|
|
}
|
|
*/
|
|
|
|
sx = FixedMul(3*mapobjectscale, FINECOSINE(a >> ANGLETOFINESHIFT));
|
|
sy = FixedMul(3*mapobjectscale, FINESINE(a >> ANGLETOFINESHIFT));
|
|
|
|
if (j == 1)
|
|
{
|
|
v = line->v2;
|
|
sx = -sx;
|
|
sy = -sy;
|
|
}
|
|
|
|
end1 = P_SpawnMobj(
|
|
v->x + sx,
|
|
v->y + sy,
|
|
players[displayplayers[i]].mo->z + FINISHLINEBEAM_SPACING,
|
|
MT_THOK
|
|
);
|
|
|
|
P_SetMobjState(end1, S_FINISHBEAMEND1);
|
|
end1->renderflags = RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]);
|
|
end1->angle = lineangle;
|
|
|
|
end2 = P_SpawnMobj(
|
|
v->x + (8*sx),
|
|
v->y + (8*sy),
|
|
players[displayplayers[i]].mo->z + FINISHLINEBEAM_SPACING,
|
|
MT_THOK
|
|
);
|
|
|
|
P_SetMobjState(end2, S_FINISHBEAMEND2);
|
|
end2->renderflags = RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(&players[displayplayers[i]]);
|
|
end2->angle = lineangle;
|
|
|
|
P_SetTarget(&end2->tracer, end1);
|
|
end2->flags2 |= MF2_LINKDRAW;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
void K_RunFinishLineBeam(void)
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
|
|
void K_RunFinishLineBeam(void)
|
|
{
|
|
if ((gametyperules & GTR_ROLLINGSTART) || !(leveltime < starttime || rainbowstartavailable == true))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (finishBeamLine != NULL)
|
|
{
|
|
const angle_t angoffset = ANGLE_45;
|
|
const angle_t angadd = ANGLE_11hh;
|
|
const fixed_t speed = 6 * mapobjectscale;
|
|
|
|
fixed_t offseta = (leveltime * speed) % FINISHLINEBEAM_SPACING;
|
|
angle_t aiminga = (angoffset * -((leveltime * speed) / FINISHLINEBEAM_SPACING)) + (angadd * leveltime);
|
|
|
|
fixed_t offsetb = FINISHLINEBEAM_SPACING - offseta;
|
|
angle_t aimingb = (angoffset * -((leveltime * speed) / FINISHLINEBEAM_SPACING)) - (angadd * leveltime);
|
|
|
|
K_DrawFinishLineBeamForLine(offseta, aiminga, finishBeamLine, false);
|
|
K_DrawFinishLineBeamForLine(offsetb, aimingb, finishBeamLine, true);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------
|
|
UINT8 K_RaceLapCount(void);
|
|
|
|
See header file for description.
|
|
--------------------------------------------------*/
|
|
|
|
UINT8 K_RaceLapCount(INT16 mapNum)
|
|
{
|
|
if (!(gametyperules & GTR_CIRCUIT))
|
|
{
|
|
// Not in Race mode
|
|
return 0;
|
|
}
|
|
|
|
if (cv_numlaps.value == -1)
|
|
{
|
|
// Use map default
|
|
return mapheaderinfo[mapNum]->numlaps;
|
|
}
|
|
|
|
return cv_numlaps.value;
|
|
}
|