Merge remote-tracking branch 'origin/master' into ta24

This commit is contained in:
Antonio Martinez 2025-05-18 14:42:59 -04:00
commit 1c6096405e
14 changed files with 264 additions and 12 deletions

View file

@ -408,6 +408,7 @@ struct botvars_t
// All entries above persist between rounds and must be recorded in demos
fixed_t rubberband; // Bot rubberband value
UINT8 bumpslow;
tic_t itemdelay; // Delay before using item at all
tic_t itemconfirm; // When high enough, they will use their item

View file

@ -2028,6 +2028,15 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_BUBBLESHIELDWAVE5",
"S_BUBBLESHIELDWAVE6",
// Bubble Shield Visuals
"S_BUBA1",
"S_BUBB1",
"S_BUBB2",
"S_BUBC1",
"S_BUBC2",
"S_BUBD1",
"S_BUBE1",
// Flame Shield
"S_FLAMESHIELD1",
"S_FLAMESHIELD2",
@ -3616,6 +3625,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_LIGHTNINGSHIELD", // Shields
"MT_BUBBLESHIELD",
"MT_BUBBLESHIELD_VISUAL",
"MT_FLAMESHIELD",
"MT_FLAMESHIELDUNDERLAY",
"MT_FLAMESHIELDPAPER",

View file

@ -376,6 +376,11 @@ char sprnames[NUMSPRITES + 1][5] =
"TRNQ", // SPB Manta Ring loop
"THNS", // Lightning Shield
"BUBS", // Bubble Shield (not Bubs)
"BUBA", // Bubble Shield Outline
"BUBB", // Bubble Shield Top Wave
"BUBC", // Bubble Shield Bottom Wave
"BUBD", // Bubble Shield Reflection
"BUBE", // Bubble Shield Underline
"BWVE", // Bubble Shield waves
"FLMS", // Flame Shield
"FLMD", // Flame Shield dash
@ -2564,6 +2569,15 @@ state_t states[NUMSTATES] =
{SPR_BWVE, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BUBBLESHIELDWAVE6}, // S_BUBBLESHIELDWAVE5
{SPR_BWVE, FF_FULLBRIGHT|5, 1, {NULL}, 0, 0, S_NULL}, // S_BUBBLESHIELDWAVE6
// Bubble Shield Visuals
{SPR_BUBA, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBA1}, // S_BUBA1
{SPR_BUBB, FF_FULLBRIGHT|FF_ANIMATE, 36, {NULL}, 8, 4, S_BUBB1}, // S_BUBB1
{SPR_NULL, 0, 5, {NULL}, 0, 0, S_BUBB1}, // S_BUBB2
{SPR_BUBC, FF_FULLBRIGHT|FF_ANIMATE, 36, {NULL}, 8, 4, S_BUBC1}, // S_BUBC1
{SPR_NULL, 0, 5, {NULL}, 0, 0, S_BUBC1}, // S_BUBC2
{SPR_BUBD, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBD1}, // S_BUBD1
{SPR_BUBE, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_BUBE1}, // S_BUBE1
{SPR_FLMS, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_FLAMESHIELD2}, // S_FLAMESHIELD1
{SPR_FLMS, FF_FULLBRIGHT|9, 2, {NULL}, 0, 0, S_FLAMESHIELD3}, // S_FLAMESHIELD2
{SPR_FLMS, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_FLAMESHIELD4}, // S_FLAMESHIELD3
@ -15395,6 +15409,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_BUBBLESHIELD_VISUAL
-1, // doomednum
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // 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
8, // speed
28*FRACUNIT, // radius
56*FRACUNIT, // height
1, // display offset
16, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_FLAMESHIELD
-1, // doomednum
S_FLAMESHIELD1, // spawnstate

View file

@ -915,6 +915,11 @@ typedef enum sprite
SPR_TRNQ, // SPB Manta Ring loop
SPR_THNS, // Thunder Shield
SPR_BUBS, // Bubble Shield (not Bubs)
SPR_BUBA, // Bubble Shield Outline
SPR_BUBB, // Bubble Shield Top Wave
SPR_BUBC, // Bubble Shield Bottom Wave
SPR_BUBD, // Bubble Shield Reflection
SPR_BUBE, // Bubble Shield Underline
SPR_BWVE, // Bubble Shield waves
SPR_FLMS, // Flame Shield
SPR_FLMD, // Flame Shield dash
@ -3059,6 +3064,15 @@ typedef enum state
S_BUBBLESHIELDWAVE5,
S_BUBBLESHIELDWAVE6,
// Bubble Shield Visuals
S_BUBA1,
S_BUBB1,
S_BUBB2,
S_BUBC1,
S_BUBC2,
S_BUBD1,
S_BUBE1,
// Flame Shield
S_FLAMESHIELD1,
S_FLAMESHIELD2,
@ -4674,6 +4688,7 @@ typedef enum mobj_type
MT_LIGHTNINGSHIELD, // Shields
MT_BUBBLESHIELD,
MT_BUBBLESHIELD_VISUAL,
MT_FLAMESHIELD,
MT_FLAMESHIELDUNDERLAY,
MT_FLAMESHIELDPAPER,

View file

@ -803,8 +803,13 @@ fixed_t K_UpdateRubberband(player_t *player)
fixed_t dest = K_BotRubberband(player);
fixed_t ret = player->botvars.rubberband;
UINT8 ease_soften = 8;
if (player->botvars.bumpslow && dest > ret)
ease_soften *= 10;
// Ease into the new value.
ret += (dest - player->botvars.rubberband) / 8;
ret += (dest - player->botvars.rubberband) / ease_soften;
return ret;
}

View file

@ -9305,6 +9305,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->flashing = K_GetKartFlashing(player);
}
if (player->spinouttimer && player->respawn.state != RESPAWNST_NONE)
player->spinouttimer = 0;
if (player->spinouttimer)
{
if (((P_IsObjectOnGround(player->mo)
@ -9509,6 +9512,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->trickboost)
player->trickboost--;
if (K_PlayerUsesBotMovement(players) && player->botvars.bumpslow && player->incontrol)
player->botvars.bumpslow--;
if (player->flamedash)
{
player->flamedash--;
@ -14126,6 +14132,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
P_SetTarget(&shield->target, player->mo);
S_StartSound(player->mo, sfx_s3k3f);
player->curshield = KSHIELD_BUBBLE;
Obj_SpawnBubbleShieldVisuals(shield);
}
if (!HOLDING_ITEM && NO_HYUDORO)
@ -15554,6 +15562,15 @@ UINT32 K_GetNumGradingPoints(void)
return numlaps * (1 + Obj_GetCheckpointCount());
}
void K_BotHitPenalty(player_t *player)
{
if (K_PlayerUsesBotMovement(player))
{
player->botvars.rubberband = max(player->botvars.rubberband/2, FRACUNIT/2);
player->botvars.bumpslow = TICRATE*2;
}
}
static boolean K_PickUp(player_t *player, mobj_t *picked)
{
SINT8 type = -1;

View file

@ -317,6 +317,9 @@ UINT16 K_GetDisplayEXP(player_t *player);
UINT32 K_GetNumGradingPoints(void);
boolean K_LegacyRingboost(player_t *player);
void K_BotHitPenalty(player_t *player);
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2);
#ifdef __cplusplus

View file

@ -449,6 +449,10 @@ UINT8 K_HogChargeToHogCount(INT32 charge, UINT8 cap);
void K_UpdateBallhogReticules(player_t *player, UINT8 num_hogs, boolean on_release);
void K_DoBallhogAttack(player_t *player, UINT8 num_hogs);
/* Bubble Shield */
void Obj_SpawnBubbleShieldVisuals(mobj_t *source);
boolean Obj_TickBubbleShieldVisual(mobj_t *mobj);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -60,6 +60,7 @@ target_sources(SRB2SDL2 PRIVATE
pulley.cpp
amps.c
ballhog.cpp
bubble-shield.cpp
flybot767.c
)

View file

@ -0,0 +1,136 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2025 by James Robert Roman
// 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.
//-----------------------------------------------------------------------------
#include <algorithm>
#include "objects.hpp"
#include "../m_easing.h"
#include "../m_fixed.h"
#include "../tables.h"
using namespace srb2::objects;
namespace
{
struct Bubble : Mobj
{
static constexpr fixed_t kBaseScale = 5*FRACUNIT/4;
static constexpr fixed_t kMaxScale = 5*FRACUNIT;
static constexpr fixed_t kScaleRange = kMaxScale - kBaseScale;
void target() = delete;
Mobj* follow() const { return Mobj::target(); }
void follow(Mobj* n) { Mobj::target(n); }
player_t* player() const { return follow()->player; }
bool valid() const { return Mobj::valid(follow()) && player(); }
};
struct Visual : Mobj
{
void target() = delete;
Bubble* bubble() const { return Mobj::target<Bubble>(); }
void bubble(Bubble* n) { Mobj::target(n); }
void extravalue1() = delete;
Fixed prev_scale() const { return Mobj::extravalue1; }
void prev_scale(Fixed n) { Mobj::extravalue1 = n; }
bool valid() const { return Mobj::valid(bubble()) && bubble()->valid(); }
static void spawn
( Bubble* bubble,
statenum_t state,
int flicker,
int offset)
{
if (!bubble->valid())
return;
Visual* x = Mobj::spawn<Visual>(bubble->pos(), MT_BUBBLESHIELD_VISUAL);
//x->scale(5 * x->scale() / 4);
x->state(state);
x->spriteyoffset(22*FRACUNIT);
x->bubble(bubble);
x->linkdraw(bubble->follow(), offset);
if (flicker)
x->renderflags |= RF_DONTDRAW;
}
bool tick()
{
if (!valid())
{
remove();
return false;
}
move_origin(bubble()->pos());
renderflags = ((renderflags ^ RF_DONTDRAW) & RF_DONTDRAW);
// ATTENTION: this object relies on the original MT_BUBBLESHIELD object for scale
fixed_t f = Fixed {bubble()->scale() / bubble()->follow()->scale() - Bubble::kBaseScale} / Fixed {Bubble::kScaleRange};
scale(Easing_Linear(f,
bubble()->follow()->scale() * Fixed {Bubble::kBaseScale},
bubble()->follow()->scale() * 4));
if (sprite != SPR_BUBB &&
sprite != SPR_BUBC &&
bubble()->player()->bubblecool &&
f == 0) // base size
renderflags |= RF_DONTDRAW;
if (scale() > prev_scale())
spritescale({ 3*FRACUNIT/2, 3*FRACUNIT/4 });
else if (scale() < prev_scale())
spritescale({ 3*FRACUNIT/4, 3*FRACUNIT/2 });
else
{
if (f == FRACUNIT) // max size
{
if (leveltime & 1)
spritescale({ 3*FRACUNIT/4, 3*FRACUNIT/2 });
else
{
spritescale({ FRACUNIT, FRACUNIT });
renderflags |= RF_ADD;
}
}
else
spritescale({ FRACUNIT, FRACUNIT });
}
prev_scale(scale());
return true;
}
};
}; // namespace
void Obj_SpawnBubbleShieldVisuals(mobj_t *bubble)
{
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBA1, 1, 2);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBB1, 0, 1);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBC1, 1, -1);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBD1, 0, -2);
Visual::spawn(static_cast<Bubble*>(bubble), S_BUBE1, 1, -3);
}
boolean Obj_TickBubbleShieldVisual(mobj_t *mobj)
{
return static_cast<Visual*>(mobj)->tick();
}

View file

@ -2600,22 +2600,22 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
player->roundconditions.checkthisframe = true;
}
if (gametyperules & (GTR_BUMPERS|GTR_CHECKPOINTS))
{
if ((player->pitblame > -1) && (player->pitblame < MAXPLAYERS)
&& (playeringame[player->pitblame]) && (!players[player->pitblame].spectator)
&& (players[player->pitblame].mo) && (!P_MobjWasRemoved(players[player->pitblame].mo)))
{
if (gametyperules & (GTR_BUMPERS|GTR_CHECKPOINTS))
P_DamageMobj(player->mo, players[player->pitblame].mo, players[player->pitblame].mo, 1, DMG_KARMA);
else
K_SpawnAmps(&players[player->pitblame], 20, player->mo);
player->pitblame = -1;
}
else if (player->mo->health > 1 || K_Cooperative())
{
if (gametyperules & (GTR_BUMPERS|GTR_CHECKPOINTS))
player->mo->health--;
}
}
if (modeattacking & ATTACKING_SPB)
{
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
@ -3204,6 +3204,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (source && source != player->mo && source->player)
{
K_SpawnAmps(source->player, K_PvPAmpReward((type == DMG_WHUMBLE) ? 30 : 20, source->player, player), target);
K_BotHitPenalty(player);
// Extend the invincibility if the hit was a direct hit.
if (inflictor == source && source->player->invincibilitytimer &&

View file

@ -4118,6 +4118,8 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
if (mo->player)
mo->player->bumpUnstuck += 5;
K_BotHitPenalty(mo->player);
// Combo avoidance!
if (mo->player && P_PlayerInPain(mo->player) && gametyperules & GTR_BUMPERS && mo->health == 1)
{

View file

@ -6658,6 +6658,14 @@ static void P_MobjSceneryThink(mobj_t *mobj)
Obj_PulleyThink(mobj);
return;
}
case MT_BUBBLESHIELD_VISUAL:
{
if (!Obj_TickBubbleShieldVisual(mobj))
{
return;
}
break;
}
default:
if (mobj->fuse)
{ // Scenery object fuse! Very basic!
@ -8534,6 +8542,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
return false;
}
// FIXME: Old Bubble Shield code is still running.
// Some of it is visual, some gameplay.
// I left it alone and just tell it to go invisible~
// See objects/bubble-shield.cpp
mobj->renderflags |= RF_DONTDRAW;
scale = (5*mobj->target->scale)>>2;
curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states)));

View file

@ -737,6 +737,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].botvars.diffincrease);
WRITEUINT8(save->p, players[i].botvars.rival);
WRITEFIXED(save->p, players[i].botvars.rubberband);
WRITEUINT8(save->p, players[i].botvars.bumpslow);
WRITEUINT32(save->p, players[i].botvars.itemdelay);
WRITEUINT32(save->p, players[i].botvars.itemconfirm);
WRITESINT8(save->p, players[i].botvars.turnconfirm);
@ -1380,6 +1381,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].botvars.diffincrease = READUINT8(save->p);
players[i].botvars.rival = (boolean)READUINT8(save->p);
players[i].botvars.rubberband = READFIXED(save->p);
players[i].botvars.bumpslow = READUINT8(save->p);
players[i].botvars.itemdelay = READUINT32(save->p);
players[i].botvars.itemconfirm = READUINT32(save->p);
players[i].botvars.turnconfirm = READSINT8(save->p);