Merge branch 'exp-polish' into 'master'

EXP polish

See merge request kart-krew-dev/ring-racers-internal!2631
This commit is contained in:
Oni VelocitOni 2025-06-15 22:37:10 +00:00
commit fc7b15d369
18 changed files with 292 additions and 6 deletions

View file

@ -800,6 +800,7 @@ consvar_t cv_battleufotest = OnlineCheat("battleufotest", "Off").on_off().descri
#ifdef DEVELOP
consvar_t cv_botcontrol = OnlineCheat("botcontrol", "On").on_off().description("Toggle bot AI movement");
consvar_t cv_takeover = OnlineCheat("takeover", "Off").on_off().description("Human players use bot movement");
#endif
extern CV_PossibleValue_t capsuletest_cons_t[];

View file

@ -339,6 +339,11 @@ typedef enum
// Tricks
khud_trickcool,
// Exp
khud_oldexp,
khud_exp,
khud_exptimer,
NUMKARTHUD
} karthudtype_t;

View file

@ -2686,6 +2686,7 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_EGOORB",
"S_AMPS",
"S_EXP",
"S_WATERTRAIL1",
"S_WATERTRAIL2",
@ -4009,6 +4010,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_PULLUPHOOK",
"MT_AMPS",
"MT_EXP",
"MT_FLYBOT767",

View file

@ -600,6 +600,8 @@ char sprnames[NUMSPRITES + 1][5] =
"AMPC",
"AMPD",
"EXPC",
"SOR_",
"WTRL", // Water Trail
@ -3246,6 +3248,7 @@ state_t states[NUMSTATES] =
{SPR_EGOO, 0, 1, {NULL}, 0, 0, S_NULL}, // S_EGOORB
{SPR_AMPA, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 41, 1, S_NULL}, // S_AMPS
{SPR_EXPC, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_EXP
// Water Trail
{SPR_WTRL, FF_PAPERSPRITE , 2, {NULL}, 0, 0, S_NULL}, // S_WATERTRAIL1
@ -22482,7 +22485,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_AMPS
3444, // doomednum
-1, // doomednum
S_AMPS, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
@ -22507,6 +22510,32 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags
S_NULL // raisestate
},
{ // MT_EXP
-1, // doomednum
S_EXP, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
1, // speed
32*FRACUNIT, // radius
32*FRACUNIT, // height
0, // dispoffset
0, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags
S_NULL // raisestate
},
{ // MT_FLYBOT767
-1, // doomednum
S_FLYBOT767, // spawnstate

View file

@ -1141,6 +1141,8 @@ typedef enum sprite
SPR_AMPC,
SPR_AMPD,
SPR_EXPC,
SPR_SOR_,
SPR_WTRL, // Water Trail
@ -3742,6 +3744,7 @@ typedef enum state
S_EGOORB,
S_AMPS,
S_EXP,
S_WATERTRAIL1,
S_WATERTRAIL2,
@ -5091,6 +5094,7 @@ typedef enum mobj_type
MT_PULLUPHOOK,
MT_AMPS,
MT_EXP,
MT_FLYBOT767,

View file

@ -432,6 +432,11 @@ boolean K_PlayerUsesBotMovement(const player_t *player)
if (player->bot)
return true;
#ifdef DEVELOP
if (cv_takeover.value)
return true;
#endif
return false;
}

View file

@ -24,6 +24,7 @@ extern "C" {
#ifdef DEVELOP
extern consvar_t cv_botcontrol;
extern consvar_t cv_takeover;
#endif
// Maximum value of botvars.difficulty

View file

@ -3921,7 +3921,7 @@ static boolean K_drawKartLaps(void)
INT32 bump = 0;
boolean drewsticker = false;
UINT16 displayEXP = stplyr->exp;
UINT16 displayEXP = stplyr->karthud[khud_exp];
// Jesus Christ.
// I do not understand the way this system of offsets is laid out at all,
@ -3999,6 +3999,10 @@ static boolean K_drawKartLaps(void)
}
}
boolean dance = (stplyr->exp > (UINT32)stplyr->karthud[khud_exp]);
INT32 danceflag = dance ? V_STRINGDANCE : 0;
UINT16 dancecolor = dance ? SKINCOLOR_AQUAMARINE : 0;
// EXP
if (displayEXP == UINT16_MAX)
{
@ -4048,9 +4052,15 @@ static boolean K_drawKartLaps(void)
V_DrawMappedPatch(fr, fy, transflag|V_SLIDEIN|splitflags, kp_exp[1], colormap);
// EXP
V_DrawScaledPatch(fr+11, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[displayEXP/100]);
V_DrawScaledPatch(fr+15, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[displayEXP/10%10]);
V_DrawScaledPatch(fr+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[displayEXP%10]);
using srb2::Draw;
Draw row = Draw(fr+11, fy).flags(V_HUDTRANS|V_SLIDEIN|splitflags|danceflag).font(Draw::Font::kPing).colorize(dancecolor);
row.text("{:03}", displayEXP);
//V_DrawScaledPatch(fr+11, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[displayEXP/100]);
//V_DrawScaledPatch(fr+15, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[displayEXP/10%10]);
//V_DrawScaledPatch(fr+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[displayEXP%10]);
}
else
{
@ -4065,7 +4075,7 @@ static boolean K_drawKartLaps(void)
V_DrawMappedPatch(LAPS_X+bump, LAPS_Y, transflag|V_SLIDEIN|splitflags, kp_exp[0], colormap);
using srb2::Draw;
Draw row = Draw(LAPS_X+23+bump, LAPS_Y+3).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kThinTimer);
Draw row = Draw(LAPS_X+23+bump, LAPS_Y+3).flags(V_HUDTRANS|V_SLIDEIN|splitflags|danceflag).font(Draw::Font::kThinTimer).colorize(dancecolor);
row.text("{:03}", displayEXP);
}

View file

@ -4229,6 +4229,25 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact)
}
}
void K_SpawnEXP(player_t *player, UINT8 exp, mobj_t *impact)
{
if (exp == 0)
return;
for (int i = 0; i < exp; i++)
{
mobj_t *pickup = P_SpawnMobj(impact->x, impact->y, impact->z, MT_EXP);
pickup->momx = impact->momx;
pickup->momy = impact->momy;
pickup->momz = impact->momz;
pickup->momx += P_RandomRange(PR_ITEM_DEBRIS, -20*mapobjectscale, 20*mapobjectscale);
pickup->momy += P_RandomRange(PR_ITEM_DEBRIS, -20*mapobjectscale, 20*mapobjectscale);
pickup->momz += P_RandomRange(PR_ITEM_DEBRIS, -20*mapobjectscale, 20*mapobjectscale);
// pickup->color = player->skincolor;
P_SetTarget(&pickup->target, player->mo);
}
}
void K_AwardPlayerAmps(player_t *player, UINT8 amps)
{
UINT16 getamped = player->amps + amps;
@ -8879,6 +8898,31 @@ void K_KartPlayerHUDUpdate(player_t *player)
if (player->positiondelay)
player->positiondelay--;
if (player->exp != (UINT32)player->karthud[khud_oldexp])
{
if (player->exp <= (UINT32)player->karthud[khud_oldexp])
{
player->karthud[khud_oldexp] = 0;
player->karthud[khud_exp] = 0;
player->karthud[khud_exptimer] = 0;
}
else
{
INT32 delta = player->exp - player->karthud[khud_exp];
INT32 speed = max(1, 10-delta);
player->karthud[khud_exptimer]++;
if (player->karthud[khud_exptimer] >= speed)
{
player->karthud[khud_exp]++;
player->karthud[khud_exptimer] = 0;
if (player->exp == (UINT32)player->karthud[khud_exp])
player->karthud[khud_oldexp] = player->exp;
}
}
}
if (!(player->pflags & PF_FAULT || player->pflags & PF_VOID))
player->karthud[khud_fault] = 0;
else if (player->karthud[khud_fault] > 0 && player->karthud[khud_fault] <= 2*TICRATE)

View file

@ -159,6 +159,7 @@ angle_t K_MomentumAngleReal(const mobj_t *mo);
#define K_MomentumAngle(mo) K_MomentumAngleEx(mo, 6 * mo->scale)
boolean K_PvPAmpReward(UINT32 award, player_t *attacker, player_t *defender);
void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact);
void K_SpawnEXP(player_t *player, UINT8 exp, mobj_t *impact);
void K_AwardPlayerAmps(player_t *player, UINT8 amps);
void K_CheckpointCrossAward(player_t *player);
void K_AwardPlayerRings(player_t *player, UINT16 rings, boolean overload);

View file

@ -146,6 +146,8 @@ void Obj_AmpBurstThink(mobj_t *amp);
void Obj_AmpsThink(mobj_t *amps);
void Obj_ExpThink(mobj_t *exp);
void Obj_ChargeAuraThink(mobj_t *aura);
void Obj_ChargeFallThink(mobj_t *charge);
void Obj_ChargeReleaseThink(mobj_t *release);

View file

@ -468,3 +468,18 @@ UINT8 K_RaceLapCount(INT16 mapNum)
return cv_numlaps.value;
}
void K_SpawnFinishEXP(player_t *player, UINT16 exp)
{
if (finishBeamLine != NULL)
{
mobj_t *p1 = P_SpawnMobj(finishBeamLine->v1->x, finishBeamLine->v1->y, player->mo->z, MT_THOK);
mobj_t *p2 = P_SpawnMobj(finishBeamLine->v2->x, finishBeamLine->v2->y, player->mo->z, MT_THOK);
K_SpawnEXP(player, exp, p1);
K_SpawnEXP(player, exp, p2);
}
else
{
K_SpawnEXP(player, exp*2, player->mo);
}
}

View file

@ -85,6 +85,8 @@ void K_RunFinishLineBeam(void);
UINT8 K_RaceLapCount(INT16 mapNum);
void K_SpawnFinishEXP(player_t *player, UINT16 exp);
#ifdef __cplusplus
} // extern "C"

View file

@ -65,6 +65,7 @@ target_sources(SRB2SDL2 PRIVATE
lightning-shield.cpp
flame-shield.cpp
stone-shoe.cpp
exp.c
)
add_subdirectory(versus)

View file

@ -688,8 +688,17 @@ void Obj_CrossCheckpoints(player_t* player, fixed_t old_x, fixed_t old_y)
player->checkpointId = chk->id();
UINT16 oldexp = player->exp;
K_CheckpointCrossAward(player);
if (player->exp > oldexp)
{
UINT16 expdiff = (player->exp - oldexp);
K_SpawnEXP(player, expdiff, chk);
K_SpawnEXP(player, expdiff, chk->other());
}
K_UpdatePowerLevels(player, player->laps, false);
}

143
src/objects/exp.c Normal file
View file

@ -0,0 +1,143 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by AJ "Tyron" Martinez.
// Copyright (C) 2025 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 amps.c
/// \brief EXP VFX code.
#include "../doomdef.h"
#include "../info.h"
#include "../k_objects.h"
#include "../p_local.h"
#include "../k_kart.h"
#include "../k_powerup.h"
#include "../m_random.h"
#include "../r_main.h"
#include "../m_easing.h"
#include "../s_sound.h"
#include "../sounds.h"
#define EXP_ARCTIME (8)
#define EXP_ORBIT (100)
static void ghostme(mobj_t *exp, player_t *player)
{
if (exp->cusval%2)
return;
mobj_t *ghost = P_SpawnGhostMobj(exp);
ghost->colorized = true;
ghost->color = player->skincolor;
ghost->renderflags |= RF_ADD;
ghost->fuse = 2;
}
void Obj_ExpThink (mobj_t *exp)
{
if (P_MobjWasRemoved(exp->target)
|| exp->target->health == 0
|| exp->target->destscale <= 1 // sealed star fall out
|| !exp->target->player)
{
P_RemoveMobj(exp);
}
else
{
mobj_t *mo = exp->target;
player_t *player = mo->player;
fixed_t dist, fakez;
angle_t hang, vang;
dist = P_AproxDistance(P_AproxDistance(exp->x - mo->x, exp->y - mo->y), exp->z - mo->z);
exp->renderflags |= RF_DONTDRAW;
exp->renderflags &= ~K_GetPlayerDontDrawFlag(player);
// K_MatchGenericExtraFlags(exp, mo);
exp->cusval++;
// bullshit copypaste orbit behavior
if (exp->threshold)
{
fixed_t orbit = (4*mo->scale) * (16 - exp->extravalue1);
P_SetScale(exp, (exp->destscale = mapobjectscale - ((mapobjectscale/28) * exp->extravalue1)));
exp->z = exp->target->z;
P_MoveOrigin(exp,
mo->x + FixedMul(orbit, FINECOSINE(exp->angle >> ANGLETOFINESHIFT)),
mo->y + FixedMul(orbit, FINESINE(exp->angle >> ANGLETOFINESHIFT)),
exp->z + mo->scale * 24 * P_MobjFlip(exp));
exp->momx = 0;
exp->momy = 0;
exp->momz = 0;
ghostme(exp, player);
exp->angle += ANG30;
exp->extravalue1++;
if (exp->extravalue1 >= 16)
P_RemoveMobj(exp);
return;
}
exp->angle += ANGLE_45/2;
UINT8 damper = 3;
fixed_t vert = dist/3;
fixed_t speed = 60*exp->scale;
if (exp->extravalue2) // Mode: going down, aim at the player and speed up / dampen stray movement
{
if (exp->extravalue1)
exp->extravalue1--;
exp->extravalue2++;
speed += exp->extravalue2 * exp->scale/2;
fakez = mo->z + (vert * exp->extravalue1 / EXP_ARCTIME);
damper = 1;
}
else // Mode: going up, aim above the player
{
exp->extravalue1++;
if (exp->extravalue1 >= EXP_ARCTIME)
exp->extravalue2 = 1;
fakez = mo->z + vert;
}
if (mo->flags & MFE_VERTICALFLIP)
fakez -= mo->height/2;
else
fakez += mo->height/2;
hang = R_PointToAngle2(exp->x, exp->y, mo->x, mo->y);
vang = R_PointToAngle2(exp->z, 0, fakez, dist);
exp->momx -= exp->momx>>(damper), exp->momy -= exp->momy>>(damper), exp->momz -= exp->momz>>(damper);
exp->momx += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hang>>ANGLETOFINESHIFT), speed));
exp->momy += FixedMul(FINESINE(vang>>ANGLETOFINESHIFT), FixedMul(FINESINE(hang>>ANGLETOFINESHIFT), speed));
exp->momz += FixedMul(FINECOSINE(vang>>ANGLETOFINESHIFT), speed);
ghostme(exp, player);
if (dist < (EXP_ORBIT * exp->scale) && exp->extravalue2)
{
exp->threshold = TICRATE;
exp->extravalue1 = 0;
exp->extravalue2 = 0;
}
}
}

View file

@ -8908,6 +8908,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
Obj_AmpsThink(mobj);
break;
}
case MT_EXP:
{
Obj_ExpThink(mobj);
break;
}
case MT_BLOCKRING:
{
Obj_BlockRingThink(mobj);

View file

@ -54,6 +54,7 @@
#include "music.h"
#include "k_battle.h" // battleprisons
#include "k_endcam.h" // K_EndCameraIsFreezing()
#include "k_race.h" // K_SpawnFinishEXP
// Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog
#include <errno.h>
@ -2153,8 +2154,14 @@ static void K_HandleLapIncrement(player_t *player)
// Update power levels for this lap.
K_UpdatePowerLevels(player, player->laps, false);
UINT16 oldexp = player->exp;
K_CheckpointCrossAward(player);
if (player->exp > oldexp)
{
K_SpawnFinishEXP(player, player->exp - oldexp);
}
if (player->position == 1 && !(gametyperules & GTR_CHECKPOINTS))
{
Obj_DeactivateCheckpoints();