Merge branch 'last-battle-polish' into 'master'

Last Battle polish

Closes #914, #983, #1126, #1101, and #980

See merge request KartKrew/Kart!2035
This commit is contained in:
Oni 2024-03-09 02:56:26 +00:00
commit 91a26d7854
22 changed files with 212 additions and 14 deletions

View file

@ -3455,6 +3455,7 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_RANDOMITEM",
"MT_SPHEREBOX",
"MT_FLOATINGITEM",
"MT_GOTPOWERUP",
"MT_ITEMCAPSULE",
"MT_ITEMCAPSULE_PART",
"MT_MONITOR",

View file

@ -811,6 +811,12 @@ extern struct darkness_t
fixed_t value[MAXSPLITSCREENPLAYERS];
} g_darkness;
extern struct musicfade_t
{
tic_t start, end, fade;
boolean ticked;
} g_musicfade;
#define DEFAULT_GRAVITY (4*FRACUNIT/5)
extern fixed_t gravity;
extern fixed_t mapobjectscale;

View file

@ -279,6 +279,7 @@ tic_t racecountdown, exitcountdown, musiccountdown; // for racing
exitcondition_t g_exit;
darkness_t g_darkness;
musicfade_t g_musicfade;
fixed_t gravity;
fixed_t mapobjectscale;

View file

@ -1,3 +1,12 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2023-2024 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 <functional>
@ -81,7 +90,7 @@ void K_drawKartPowerUps(void)
{
auto make_drawer = [](int x, int y, Draw::Font font) -> Draw
{
return Draw(x, y).font(font).align(Draw::Align::kRight);
return Draw(x, y).font(font).align(Draw::Align::kRight).flags(V_SLIDEIN);
};
const int viewnum = R_GetViewNumber();

View file

@ -13188,6 +13188,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_GOTPOWERUP
-1, // doomednum
S_ITEMICON, // spawnstate
1, // 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_itpick, // deathsound
0, // speed
48*FRACUNIT, // radius
64*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOSQUISH|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_ITEMCAPSULE
2010, // doomednum
S_ITEMCAPSULE, // spawnstate

View file

@ -4475,6 +4475,7 @@ typedef enum mobj_type
MT_RANDOMITEM,
MT_SPHEREBOX,
MT_FLOATINGITEM,
MT_GOTPOWERUP,
MT_ITEMCAPSULE,
MT_ITEMCAPSULE_PART,
MT_MONITOR,

View file

@ -175,7 +175,8 @@ void K_CheckBumpers(void)
{
// If every other player is eliminated, the
// last player standing wins by default.
K_EndBattleRound(kingofthehill != -1 ? &players[kingofthehill] : NULL);
if (numingame > 1)
K_EndBattleRound(kingofthehill != -1 ? &players[kingofthehill] : NULL);
return;
}
@ -653,12 +654,12 @@ static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale)
if (player->mo->eflags & MFE_VERTICALFLIP)
{
zpos = cam->z + player->mo->height;
zpos = min(zpos + heightPadding, cam->ceilingz);
zpos = min(zpos + heightPadding, cam->centerceilingz);
}
else
{
zpos = cam->z;
zpos = max(zpos - heightPadding, cam->floorz);
zpos = max(zpos - heightPadding, cam->centerfloorz);
}
flip = P_MobjFlip(player->mo);

View file

@ -11,6 +11,7 @@ extern "C" {
#define BATTLE_SPAWN_INTERVAL (4*TICRATE)
#define BATTLE_DESPAWN_TIME (15*TICRATE)
#define BATTLE_POWERUP_TIME (30*TICRATE)
#define BATTLE_POWERUP_DROPPED_TIME (15*TICRATE)
#define BATTLE_UFO_TIME (20*TICRATE)
extern struct battleovertime

View file

@ -118,6 +118,7 @@ void K_TimerReset(void)
{
starttime = introtime = 0;
memset(&g_darkness, 0, sizeof g_darkness);
memset(&g_musicfade, 0, sizeof g_musicfade);
numbulbs = 1;
inDuel = rainbowstartavailable = false;
linecrossed = 0;
@ -1015,7 +1016,8 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2)
K_SpawnBumpForObjs(mobj1, mobj2);
if (mobj1->type == MT_PLAYER && mobj2->type == MT_PLAYER)
if (mobj1->type == MT_PLAYER && mobj2->type == MT_PLAYER
&& !mobj1->player->powerupVFXTimer && !mobj2->player->powerupVFXTimer)
{
boolean guard1 = K_PlayerGuard(mobj1->player);
boolean guard2 = K_PlayerGuard(mobj2->player);

View file

@ -398,6 +398,10 @@ void Obj_TalkPointInit(mobj_t* mo);
void Obj_TalkPointThink(mobj_t* mo);
void Obj_TalkPointOrbThink(mobj_t* mo);
/* Power-up Spinner */
void Obj_SpawnPowerUpSpinner(mobj_t *source, INT32 powerup, tic_t duration);
void Obj_TickPowerUpSpinner(mobj_t *mobj);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -2,6 +2,7 @@
#include <algorithm>
#include "k_battle.h"
#include "k_kart.h"
#include "k_objects.h"
#include "k_powerup.h"
@ -54,44 +55,45 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time)
Obj_SpawnPowerUpAura(player);
}
S_StartSound(NULL, sfx_gsha7);
S_StartSound(NULL, sfx_gsha7l);
player->flashing = 2*TICRATE;
K_AddHitLag(player->mo, BATTLE_POWERUP_VFX_TIME, false);
player->mo->hitlag += BATTLE_POWERUP_VFX_TIME;
player->powerupVFXTimer = BATTLE_POWERUP_VFX_TIME;
Obj_SpawnPowerUpSpinner(player->mo, powerup, BATTLE_POWERUP_VFX_TIME);
g_darkness.start = leveltime;
g_darkness.end = leveltime + BATTLE_POWERUP_VFX_TIME + DARKNESS_FADE_TIME;
g_musicfade.start = leveltime;
g_musicfade.end = g_musicfade.start + 90;
g_musicfade.fade = 20;
g_musicfade.ticked = false;
switch (powerup)
{
case POWERUP_SMONITOR:
S_StartSound(NULL, sfx_bpwrua);
K_AddMessageForPlayer(player, "Got S MONITOR!", true, false);
K_DoInvincibility(player, player->invincibilitytimer + time);
player->powerup.superTimer += time;
break;
case POWERUP_BARRIER:
S_StartSound(NULL, sfx_bpwrub);
K_AddMessageForPlayer(player, "Got MEGA BARRIER!", true, false);
player->powerup.barrierTimer += time;
Obj_SpawnMegaBarrier(player);
break;
case POWERUP_BUMPER:
S_StartSound(NULL, sfx_bpwruc);
K_AddMessageForPlayer(player, "Got BUMPER RESTOCK!", true, false);
K_GiveBumpersToPlayer(player, nullptr, 5);
break;
case POWERUP_BADGE:
S_StartSound(NULL, sfx_bpwrud);
K_AddMessageForPlayer(player, "Got RHYTHM BADGE!", true, false);
player->powerup.rhythmBadgeTimer += time;
break;
case POWERUP_SUPERFLICKY:
S_StartSound(NULL, sfx_bpwrue);
K_AddMessageForPlayer(player, "Got SUPER FLICKY!", true, false);
if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY))
{
@ -104,7 +106,6 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time)
break;
case POWERUP_POINTS:
S_StartSound(NULL, sfx_bpwruf);
K_AddMessageForPlayer(player, "Got 6 POINTS!", true, false);
K_GivePointsToPlayer(player, nullptr, 6);
@ -127,7 +128,7 @@ void K_DropPowerUps(player_t* player)
if (remaining)
{
K_DropPaperItem(player, powerup, remaining);
K_DropPaperItem(player, powerup, std::max<tic_t>(remaining, BATTLE_POWERUP_DROPPED_TIME));
callback();
}
};

View file

@ -71,6 +71,7 @@ void Music_Init(void)
tune.song = "kgrow";
tune.priority = 20;
tune.resume_fade_in = 200;
tune.use_level_volume = true;
}
{
@ -78,6 +79,7 @@ void Music_Init(void)
tune.song = "kinvnc";
tune.priority = 21;
tune.use_level_volume = true;
}
{

View file

@ -54,6 +54,7 @@ target_sources(SRB2SDL2 PRIVATE
waterfall-particle.c
sealed-star.c
talk-point.cpp
powerup-spinner.cpp
)
add_subdirectory(versus)

View file

@ -0,0 +1,97 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by James Robert Roman
//
// 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 Spinner : Mobj
{
static constexpr int kDuration = 40;
void extravalue1() = delete;
INT32 powerup() const { return mobj_t::extravalue1; }
void powerup(INT32 n) { mobj_t::extravalue1 = n; }
void extravalue2() = delete;
INT32 duration() const { return mobj_t::extravalue2; }
void duration(INT32 n) { mobj_t::extravalue2 = n; }
static void spawn(Mobj* source, INT32 powerup, tic_t duration)
{
Spinner* x = Mobj::spawn<Spinner>(source->pos(), MT_GOTPOWERUP);
K_UpdateMobjItemOverlay(x, powerup, 1);
x->frame |= FF_PAPERSPRITE | FF_ADD;
x->fuse = duration;
x->powerup(powerup);
x->duration(duration);
}
void think()
{
fixed_t f = FRACUNIT - std::clamp(fuse, 0, duration()) * FRACUNIT / std::max(duration(), 1);
if (fuse == duration() - 20)
{
S_StartSound(nullptr, sound());
}
angle += Easing_InQuad(f, ANGLE_11hh, ANGLE_45);
renderflags = (renderflags & ~RF_TRANSMASK) | (Easing_Linear(f, 0, 9) << RF_TRANSSHIFT);
spritescale({Easing_Linear(f, 4*FRACUNIT, FRACUNIT/4), Easing_Linear(f, FRACUNIT, 6*FRACUNIT)});
if (--fuse <= 0)
{
remove();
}
}
private:
sfxenum_t sound() const
{
switch (powerup())
{
case POWERUP_SMONITOR:
return sfx_bpwrua;
case POWERUP_BARRIER:
return sfx_bpwrub;
case POWERUP_BUMPER:
return sfx_bpwruc;
case POWERUP_BADGE:
return sfx_bpwrud;
case POWERUP_SUPERFLICKY:
return sfx_bpwrue;
case POWERUP_POINTS:
return sfx_bpwruf;
default:
return sfx_thok;
}
}
};
}; // namespace
void Obj_SpawnPowerUpSpinner(mobj_t *source, INT32 powerup, tic_t duration)
{
Spinner::spawn(static_cast<Mobj*>(source), powerup, duration);
}
void Obj_TickPowerUpSpinner(mobj_t *mobj)
{
static_cast<Spinner*>(mobj)->think();
}

View file

@ -112,6 +112,10 @@ struct camera_t
fixed_t floorz;
fixed_t ceilingz;
// From the player
fixed_t centerfloorz;
fixed_t centerceilingz;
// For movement checking.
fixed_t radius;
fixed_t height;

View file

@ -6596,6 +6596,11 @@ static void P_MobjSceneryThink(mobj_t *mobj)
Obj_SSGobletMobjThink(mobj);
return;
}
case MT_GOTPOWERUP:
{
Obj_TickPowerUpSpinner(mobj);
return;
}
default:
if (mobj->fuse)
{ // Scenery object fuse! Very basic!

View file

@ -6567,6 +6567,11 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITEUINT32(save->p, g_darkness.start);
WRITEUINT32(save->p, g_darkness.end);
WRITEUINT32(save->p, g_musicfade.start);
WRITEUINT32(save->p, g_musicfade.end);
WRITEUINT32(save->p, g_musicfade.fade);
WRITEUINT8(save->p, g_musicfade.ticked);
WRITEUINT16(save->p, numchallengedestructibles);
// Is it paused?
@ -6753,6 +6758,11 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
g_darkness.start = READUINT32(save->p);
g_darkness.end = READUINT32(save->p);
g_musicfade.start = READUINT32(save->p);
g_musicfade.end = READUINT32(save->p);
g_musicfade.fade = READUINT32(save->p);
g_musicfade.ticked = READUINT8(save->p);
numchallengedestructibles = READUINT16(save->p);
// Is it paused?

View file

@ -808,6 +808,22 @@ static void P_TickDarkness(void)
}
}
static void P_TickMusicFade(void)
{
if (leveltime >= g_musicfade.start && leveltime <= g_musicfade.end)
{
INT32 half = (g_musicfade.end - g_musicfade.start) / 2;
INT32 fade = max(1, g_musicfade.fade);
INT32 mid = half - fade;
INT32 t = abs((INT32)leveltime - (INT32)(g_musicfade.start + half));
Music_LevelVolume((max(t, mid) - mid) * 100 / fade);
}
else if (!g_musicfade.ticked)
Music_LevelVolume(100);
g_musicfade.ticked = true;
}
//
// P_Ticker
//
@ -1102,6 +1118,7 @@ void P_Ticker(boolean run)
racecountdown--;
P_TickDarkness();
P_TickMusicFade();
if (exitcountdown >= 1)
{

View file

@ -3053,6 +3053,8 @@ void P_ResetCamera(player_t *player, camera_t *thiscam)
thiscam->x = x;
thiscam->y = y;
thiscam->z = z;
thiscam->centerfloorz = player->mo->floorz;
thiscam->centerceilingz = player->mo->ceilingz;
thiscam->angle = player->mo->angle;
thiscam->aiming = 0;
@ -3570,6 +3572,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
R_ResetViewInterpolation(num + 1);
}
thiscam->centerfloorz = mo->floorz;
thiscam->centerceilingz = mo->ceilingz;
return (x == thiscam->x && y == thiscam->y && z == thiscam->z && angle == thiscam->aiming);
}

View file

@ -1420,6 +1420,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"gsha5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"gsha6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"gsha7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"gsha7l",false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"gsha8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"gsha9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"gshaa", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},

View file

@ -1496,6 +1496,7 @@ typedef enum
sfx_gsha5,
sfx_gsha6,
sfx_gsha7,
sfx_gsha7l,
sfx_gsha8,
sfx_gsha9,
sfx_gshaa,

View file

@ -143,6 +143,7 @@ TYPEDEF (cupheader_t);
TYPEDEF (unloaded_cupheader_t);
TYPEDEF (exitcondition_t);
TYPEDEF (darkness_t);
TYPEDEF (musicfade_t);
// font.h
TYPEDEF (font_t);