Merge branch 'battle-release' into 'master'

Battle is (almost) ready for release

Closes #681

See merge request KartKrew/Kart!1788
This commit is contained in:
Oni 2024-01-06 06:08:28 +00:00
commit 10f8bb5a3f
33 changed files with 485 additions and 177 deletions

View file

@ -856,6 +856,7 @@ consvar_t cv_kartdebugnodes = ServerCheat("debugnodes", "Off").on_off().descript
consvar_t cv_1pswap = PlayerCheat("1pswap", "1").min_max(1, MAXSPLITSCREENPLAYERS).description("Let P1's Profile control a different splitscreen player"); consvar_t cv_1pswap = PlayerCheat("1pswap", "1").min_max(1, MAXSPLITSCREENPLAYERS).description("Let P1's Profile control a different splitscreen player");
consvar_t cv_debugfinishline = PlayerCheat("debugfinishline", "Off").on_off().description("Highlight finish lines, respawn lines, death pits and instakill planes with high contrast colors"); consvar_t cv_debugfinishline = PlayerCheat("debugfinishline", "Off").on_off().description("Highlight finish lines, respawn lines, death pits and instakill planes with high contrast colors");
consvar_t cv_debughudtracker = PlayerCheat("debughudtracker", "Off").on_off().description("Highlight overlapping HUD tracker blocks");
consvar_t cv_debugrank = PlayerCheat("debugrank", "Off").description("Show GP rank state on the HUD; optionally force a rank grade").values({ consvar_t cv_debugrank = PlayerCheat("debugrank", "Off").description("Show GP rank state on the HUD; optionally force a rank grade").values({
{0, "Off"}, {0, "Off"},

View file

@ -982,6 +982,9 @@ struct player_t
icecubevars_t icecube; icecubevars_t icecube;
level_tally_t tally; level_tally_t tally;
tic_t darkness_start;
tic_t darkness_end;
}; };
// WARNING FOR ANYONE ABOUT TO ADD SOMETHING TO THE PLAYER STRUCT, G_PlayerReborn WANTS YOU TO SUFFER // WARNING FOR ANYONE ABOUT TO ADD SOMETHING TO THE PLAYER STRUCT, G_PlayerReborn WANTS YOU TO SUFFER

View file

@ -803,8 +803,12 @@ struct exitcondition_t
extern tic_t racecountdown, exitcountdown, musiccountdown; extern tic_t racecountdown, exitcountdown, musiccountdown;
extern exitcondition_t g_exit; extern exitcondition_t g_exit;
extern tic_t darktimer; #define DARKNESS_FADE_TIME (8)
extern fixed_t darkness; extern struct darkness_t
{
tic_t start, end;
fixed_t value[MAXSPLITSCREENPLAYERS];
} g_darkness;
#define DEFAULT_GRAVITY (4*FRACUNIT/5) #define DEFAULT_GRAVITY (4*FRACUNIT/5)
extern fixed_t gravity; extern fixed_t gravity;

View file

@ -277,8 +277,7 @@ UINT8 useSeal = 1;
tic_t racecountdown, exitcountdown, musiccountdown; // for racing tic_t racecountdown, exitcountdown, musiccountdown; // for racing
exitcondition_t g_exit; exitcondition_t g_exit;
tic_t darktimer; darkness_t g_darkness;
fixed_t darkness;
fixed_t gravity; fixed_t gravity;
fixed_t mapobjectscale; fixed_t mapobjectscale;

View file

@ -85,9 +85,6 @@ extern boolean pausebreakkey;
extern boolean promptactive; extern boolean promptactive;
extern tic_t darktimer;
extern fixed_t darkness;
extern consvar_t cv_tutorialprompt; extern consvar_t cv_tutorialprompt;
extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection; extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection;

View file

@ -1861,6 +1861,7 @@ static void HU_DrawTitlecardCEcho(size_t num)
INT32 y = BASEVIDHEIGHT/2; INT32 y = BASEVIDHEIGHT/2;
INT32 pnumlines = 0; INT32 pnumlines = 0;
INT32 timeroffset = 0; INT32 timeroffset = 0;
INT32 fadeout = state->duration * 2 / 3;
char *line; char *line;
char *echoptr; char *echoptr;
@ -1916,7 +1917,7 @@ static void HU_DrawTitlecardCEcho(size_t num)
*line = '\0'; *line = '\0';
ofs = V_CenteredTitleCardStringOffset(echoptr, p4); ofs = V_CenteredTitleCardStringOffset(echoptr, p4);
V_DrawTitleCardString(x - ofs, y, echoptr, 0, false, timer, TICRATE*4, p4); V_DrawTitleCardString(x - ofs, y, echoptr, 0, false, timer, fadeout, p4);
y += p4 ? 18 : 32; y += p4 ? 18 : 32;
@ -2660,7 +2661,7 @@ void HU_ClearTitlecardCEcho(void)
} }
// Similar but for titlecard CEcho and also way less convoluted because I have no clue whatever the fuck they were trying above. // Similar but for titlecard CEcho and also way less convoluted because I have no clue whatever the fuck they were trying above.
void HU_DoTitlecardCEcho(player_t *player, const char *msg, boolean interrupt) void HU_DoTitlecardCEchoForDuration(player_t *player, const char *msg, boolean interrupt, tic_t duration)
{ {
if (player && !P_IsDisplayPlayer(player)) if (player && !P_IsDisplayPlayer(player))
{ {
@ -2687,5 +2688,10 @@ void HU_DoTitlecardCEcho(player_t *player, const char *msg, boolean interrupt)
strncat(state->text, "\\", sizeof(state->text) - strlen(state->text) - 1); strncat(state->text, "\\", sizeof(state->text) - strlen(state->text) - 1);
state->text[sizeof(state->text) - 1] = '\0'; state->text[sizeof(state->text) - 1] = '\0';
state->start = gametic; state->start = gametic;
state->duration = TICRATE*6 + strlen(state->text); state->duration = duration ? duration : TICRATE*6 + strlen(state->text);
}
void HU_DoTitlecardCEcho(player_t *player, const char *msg, boolean interrupt)
{
HU_DoTitlecardCEchoForDuration(player, msg, interrupt, 0u);
} }

View file

@ -167,6 +167,7 @@ void HU_DoCEcho(const char *msg);
// Titlecard CECHO shite // Titlecard CECHO shite
void HU_DoTitlecardCEcho(player_t *player, const char *msg, boolean interrupt); void HU_DoTitlecardCEcho(player_t *player, const char *msg, boolean interrupt);
void HU_DoTitlecardCEchoForDuration(player_t *player, const char *msg, boolean interrupt, tic_t duration);
void HU_ClearTitlecardCEcho(void); void HU_ClearTitlecardCEcho(void);
void DoSayCommand(char *message, SINT8 target, UINT8 flags, UINT8 source); void DoSayCommand(char *message, SINT8 target, UINT8 flags, UINT8 source);

View file

@ -2,4 +2,5 @@ target_sources(SRB2SDL2 PRIVATE
powerup.cpp powerup.cpp
spectator.cpp spectator.cpp
timer.cpp timer.cpp
emerald-win.cpp
) )

41
src/hud/emerald-win.cpp Normal file
View file

@ -0,0 +1,41 @@
#include "../v_draw.hpp"
#include "../doomdef.h"
#include "../i_time.h"
#include "../k_hud.h"
#include "../screen.h"
using srb2::Draw;
void K_drawEmeraldWin(void)
{
constexpr float kScale = 0.25;
constexpr int kH = 72 * kScale;
constexpr int kYPad = 12;
constexpr int kWidth = 34 + 4;
if (I_GetTime() % 3)
{
return;
}
Draw row = Draw(BASEVIDWIDTH / 2, BASEVIDHEIGHT / 2).scale(kScale).flags(V_ADD);
//Draw(0, row.y()).size(BASEVIDWIDTH, 1).fill(35);
Draw top = row.y(-kYPad);
Draw bot = row.xy(-kWidth / 2, kH + kYPad);
auto put = [](Draw& row, int x, int n)
{
row.x(x * kWidth).colormap(static_cast<skincolornum_t>(SKINCOLOR_CHAOSEMERALD1 + n)).patch("EMRCA0");
};
put(top, -1, 3);
put(top, 0, 0);
put(top, 1, 4);
put(bot, -1, 5);
put(bot, 0, 1);
put(bot, 1, 2);
put(bot, 2, 6);
}

View file

@ -30982,7 +30982,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_cdfm19, // deathsound sfx_cdfm19, // deathsound
0, // speed 0, // speed
60*FRACUNIT, // radius 60*FRACUNIT, // radius
104*FRACUNIT, // height 156*FRACUNIT, // height
0, // display offset 0, // display offset
0, // mass 0, // mass
0, // damage 0, // damage

View file

@ -21,6 +21,11 @@
#include "p_spec.h" #include "p_spec.h"
#include "k_objects.h" #include "k_objects.h"
#include "k_rank.h" #include "k_rank.h"
#include "music.h"
#include "hu_stuff.h"
#include "m_easing.h"
#define BARRIER_MIN_RADIUS (768 * mapobjectscale)
// Battle overtime info // Battle overtime info
struct battleovertime battleovertime; struct battleovertime battleovertime;
@ -37,6 +42,9 @@ INT32 numgotboxes = 0;
UINT8 maptargets = 0; // Capsules in map UINT8 maptargets = 0; // Capsules in map
UINT8 numtargets = 0; // Capsules busted UINT8 numtargets = 0; // Capsules busted
// Battle: someone won by collecting all 7 Chaos Emeralds
boolean g_emeraldWin = false;
INT32 K_StartingBumperCount(void) INT32 K_StartingBumperCount(void)
{ {
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS) if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
@ -142,6 +150,12 @@ void K_CheckBumpers(void)
} }
} }
if (numingame <= 2 && battleovertime.enabled && battleovertime.radius <= BARRIER_MIN_RADIUS)
{
Music_Stop("battle_overtime");
S_StartSound(NULL, sfx_kc4b); // Loud noise helps mask transition
}
if (K_Cooperative()) if (K_Cooperative())
{ {
if (nobumpers > 0 && nobumpers >= numingame) if (nobumpers > 0 && nobumpers >= numingame)
@ -184,6 +198,7 @@ void K_CheckEmeralds(player_t *player)
player->roundscore = 100; // lmao player->roundscore = 100; // lmao
P_DoAllPlayersExit(0, false); P_DoAllPlayersExit(0, false);
g_emeraldWin = true;
} }
UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType) UINT16 K_GetChaosEmeraldColor(UINT32 emeraldType)
@ -678,19 +693,47 @@ void K_RunBattleOvertime(void)
{ {
battleovertime.enabled++; battleovertime.enabled++;
if (battleovertime.enabled == TICRATE) if (battleovertime.enabled == TICRATE)
{
S_StartSound(NULL, sfx_bhurry); S_StartSound(NULL, sfx_bhurry);
if (battleovertime.enabled == 10*TICRATE) HU_DoTitlecardCEchoForDuration(NULL, "HURRY UP!!", true, 2*TICRATE);
Music_DelayEnd("level", 0);
}
else if (battleovertime.enabled == 10*TICRATE)
{
S_StartSound(NULL, sfx_kc40); S_StartSound(NULL, sfx_kc40);
P_StartQuake(5, 64 * mapobjectscale, 0, NULL);
battleovertime.start = leveltime;
}
if (!Music_Playing("level") && !Music_Playing("battle_overtime"))
{
Music_Play("battle_overtime");
Music_Play("battle_overtime_stress");
// Sync approximately with looping section of
// battle_overtime. (This is file dependant.)
Music_Seek("battle_overtime_stress", 1756);
}
} }
else if (battleovertime.radius > 0) else if (battleovertime.radius > 0)
{ {
const fixed_t minradius = 768 * mapobjectscale; const fixed_t minradius = BARRIER_MIN_RADIUS;
const fixed_t oldradius = battleovertime.radius;
if (battleovertime.radius > minradius) if (battleovertime.radius > minradius)
battleovertime.radius -= (battleovertime.initial_radius / (30*TICRATE)); {
tic_t t = leveltime - battleovertime.start;
const tic_t duration = 30*TICRATE;
battleovertime.radius = Easing_OutSine(min(t, duration) * FRACUNIT / duration, battleovertime.initial_radius, minradius);
}
if (battleovertime.radius < minradius) if (battleovertime.radius <= minradius && oldradius > minradius)
{
battleovertime.radius = minradius; battleovertime.radius = minradius;
K_CheckBumpers();
S_StartSound(NULL, sfx_kc40);
P_StartQuake(5, 64 * mapobjectscale, 0, NULL);
}
// Subtract the 10 second grace period of the barrier // Subtract the 10 second grace period of the barrier
if (battleovertime.enabled < 25*TICRATE) if (battleovertime.enabled < 25*TICRATE)
@ -822,6 +865,8 @@ void K_BattleInit(boolean singleplayercontext)
g_battleufo.due = starttime; g_battleufo.due = starttime;
g_battleufo.previousId = Obj_RandomBattleUFOSpawnerID() - 1; g_battleufo.previousId = Obj_RandomBattleUFOSpawnerID() - 1;
g_emeraldWin = false;
} }
UINT8 K_Bumpers(player_t *player) UINT8 K_Bumpers(player_t *player)

View file

@ -20,6 +20,7 @@ extern struct battleovertime
UINT16 enabled; ///< Has this been initalized yet? UINT16 enabled; ///< Has this been initalized yet?
fixed_t radius; ///< Radius of kill field fixed_t radius; ///< Radius of kill field
fixed_t initial_radius; ///< Starting radius of kill field fixed_t initial_radius; ///< Starting radius of kill field
tic_t start; ///< Leveltime to decrease kill field radius from
fixed_t x, y, z; ///< Position to center on fixed_t x, y, z; ///< Position to center on
} battleovertime; } battleovertime;
@ -32,6 +33,7 @@ extern struct battleufo
extern boolean battleprisons; extern boolean battleprisons;
extern INT32 nummapboxes, numgotboxes; // keep track of spawned battle mode items extern INT32 nummapboxes, numgotboxes; // keep track of spawned battle mode items
extern UINT8 maptargets, numtargets; extern UINT8 maptargets, numtargets;
extern boolean g_emeraldWin;
INT32 K_StartingBumperCount(void); INT32 K_StartingBumperCount(void);
boolean K_IsPlayerWanted(player_t *player); boolean K_IsPlayerWanted(player_t *player);

View file

@ -5663,6 +5663,9 @@ void K_drawKartHUD(void)
if ((gametyperules & GTR_KARMA) && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM * if ((gametyperules & GTR_KARMA) && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM *
V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem); V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
if (g_emeraldWin)
K_drawEmeraldWin();
// Draw FREE PLAY. // Draw FREE PLAY.
K_drawKartFreePlay(); K_drawKartFreePlay();

View file

@ -47,6 +47,7 @@ void K_drawSpectatorHUD(boolean director);
void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode); void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode);
void K_drawKart2PTimestamp(void); void K_drawKart2PTimestamp(void);
void K_drawKart4PTimestamp(void); void K_drawKart4PTimestamp(void);
void K_drawEmeraldWin(void);
void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap); void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap);
void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap); void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap);
void K_drawTargetHUD(const vector3_t *origin, player_t *player); void K_drawTargetHUD(const vector3_t *origin, player_t *player);

View file

@ -26,9 +26,18 @@
using namespace srb2; using namespace srb2;
extern "C" consvar_t cv_debughudtracker;
namespace namespace
{ {
enum class Visibility
{
kHidden,
kVisible,
kTransparent,
};
struct TargetTracking struct TargetTracking
{ {
static constexpr int kMaxLayers = 2; static constexpr int kMaxLayers = 2;
@ -54,8 +63,9 @@ struct TargetTracking
}; };
mobj_t* mobj; mobj_t* mobj;
vector3_t point; trackingResult_t result;
fixed_t camDist; fixed_t camDist;
bool foreground;
skincolornum_t color() const skincolornum_t color() const
{ {
@ -157,6 +167,7 @@ struct TargetTracking
private: private:
Graphics graphics() const Graphics graphics() const
{ {
using layers = decltype(Animation::layers);
switch (mobj->type) switch (mobj->type)
{ {
case MT_SUPER_FLICKY: case MT_SUPER_FLICKY:
@ -186,7 +197,9 @@ private:
{{8, 2, {kp_capsuletarget_near[1]}}}, // 4P {{8, 2, {kp_capsuletarget_near[1]}}}, // 4P
}, },
{{ // Far {{ // Far
{2, 3, {kp_capsuletarget_far[0], kp_capsuletarget_far_text}}, // 1P {2, 3, foreground ?
layers {kp_capsuletarget_far[0], kp_capsuletarget_far_text} :
layers {kp_capsuletarget_far[0]}}, // 1P
{{2, 3, {kp_capsuletarget_far[1]}}}, // 4P {{2, 3, {kp_capsuletarget_far[1]}}}, // 4P
}}, }},
}; };
@ -194,15 +207,121 @@ private:
} }
}; };
bool is_player_tracking_target(player_t *player = stplyr)
{
if ((gametyperules & (GTR_BUMPERS|GTR_CLOSERPLAYERS)) != (GTR_BUMPERS|GTR_CLOSERPLAYERS))
{
return false;
}
if (K_Cooperative())
{
return false;
}
if (player == nullptr)
{
return false;
}
if (inDuel)
{
// Always draw targets in 1v1 but don't draw player's
// own target on their own viewport.
return player != stplyr;
}
// Except for DUEL mode, Overtime hides all TARGETs except
// the kiosk.
if (battleovertime.enabled)
{
return false;
}
if (player->emeralds != 0 && K_IsPlayerWanted(stplyr))
{
// The player who is about to win because of emeralds
// gets a TARGET on them
if (K_NumEmeralds(player) == 6) // 6 out of 7
{
return true;
}
// WANTED player sees TARGETs on players holding
// emeralds
if (K_IsPlayerWanted(stplyr))
{
return true;
}
}
return K_IsPlayerWanted(player);
}
bool is_object_tracking_target(const mobj_t* mobj)
{
switch (mobj->type)
{
case MT_BATTLECAPSULE:
case MT_CDUFO:
return battleprisons;
case MT_PLAYER:
return is_player_tracking_target(mobj->player);
case MT_OVERTIME_CENTER:
return inDuel == false && battleovertime.enabled;
case MT_EMERALD:
return Obj_EmeraldCanHUDTrack(mobj) &&
((specialstageinfo.valid && specialstageinfo.ufo) || is_player_tracking_target());
case MT_MONITOR:
return is_player_tracking_target() && Obj_MonitorGetEmerald(mobj) != 0;
case MT_SUPER_FLICKY:
return Obj_IsSuperFlickyTargettingYou(mobj, stplyr->mo);
case MT_SPRAYCAN:
return !(mobj->renderflags & (RF_TRANSMASK | RF_DONTDRAW)); // the spraycan wasn't collected yet
default:
return false;
}
}
Visibility is_object_visible(mobj_t* mobj)
{
switch (mobj->type)
{
case MT_SUPER_FLICKY:
// Always flickers.
return (leveltime & 1) ? Visibility::kVisible : Visibility::kHidden;
case MT_SPRAYCAN:
// Flickers, but only when visible.
return P_CheckSight(stplyr->mo, mobj) && (leveltime & 1) ? Visibility::kVisible : Visibility::kHidden;
default:
// Transparent when not visible.
return P_CheckSight(stplyr->mo, mobj) ? Visibility::kVisible : Visibility::kTransparent;
}
}
void K_DrawTargetTracking(const TargetTracking& target) void K_DrawTargetTracking(const TargetTracking& target)
{ {
Visibility visibility = is_object_visible(target.mobj);
if (visibility == Visibility::kHidden)
{
return;
}
const uint8_t* colormap = target.colormap(); const uint8_t* colormap = target.colormap();
trackingResult_t result = {}; const trackingResult_t& result = target.result;
int32_t timer = 0; int32_t timer = 0;
K_ObjectTracking(&result, &target.point, false);
if (result.onScreen == false) if (result.onScreen == false)
{ {
// Off-screen, draw alongside the borders of the screen. // Off-screen, draw alongside the borders of the screen.
@ -363,6 +482,17 @@ void K_DrawTargetTracking(const TargetTracking& target)
{ {
// Draw simple overlay. // Draw simple overlay.
vector2_t targetPos = {result.x, result.y}; vector2_t targetPos = {result.x, result.y};
INT32 trans = [&]
{
switch (visibility)
{
case Visibility::kTransparent:
return V_30TRANS;
default:
return target.foreground ? 0 : V_80TRANS;
}
}();
TargetTracking::Animation anim = target.animation(); TargetTracking::Animation anim = target.animation();
@ -374,7 +504,7 @@ void K_DrawTargetTracking(const TargetTracking& target)
targetPos.x - ((patch->width << FRACBITS) >> 1), targetPos.x - ((patch->width << FRACBITS) >> 1),
targetPos.y - ((patch->height << FRACBITS) >> 1), targetPos.y - ((patch->height << FRACBITS) >> 1),
FRACUNIT, FRACUNIT,
V_SPLITSCREEN | anim.video_flags, V_SPLITSCREEN | anim.video_flags | trans,
patch, patch,
colormap colormap
); );
@ -382,105 +512,66 @@ void K_DrawTargetTracking(const TargetTracking& target)
} }
} }
bool is_player_tracking_target(player_t *player = stplyr) void K_CullTargetList(std::vector<TargetTracking>& targetList)
{ {
if ((gametyperules & (GTR_BUMPERS|GTR_CLOSERPLAYERS)) != (GTR_BUMPERS|GTR_CLOSERPLAYERS)) constexpr int kBlockSize = 20;
constexpr int kXBlocks = BASEVIDWIDTH / kBlockSize;
constexpr int kYBlocks = BASEVIDHEIGHT / kBlockSize;
bool map[kXBlocks][kYBlocks] = {};
constexpr fixed_t kTrackerRadius = 30*FRACUNIT/2; // just an approximation of common HUD tracker
int debugColorCycle = 0;
std::for_each(
targetList.rbegin(),
targetList.rend(),
[&](TargetTracking& tr)
{ {
return false; if (tr.result.onScreen == false)
{
return;
} }
if (K_Cooperative()) fixed_t x1 = std::max(((tr.result.x - kTrackerRadius) / kBlockSize) / FRACUNIT, 0);
{ fixed_t x2 = std::min(((tr.result.x + kTrackerRadius) / kBlockSize) / FRACUNIT, kXBlocks - 1);
return false; fixed_t y1 = std::max(((tr.result.y - kTrackerRadius) / kBlockSize) / FRACUNIT, 0);
} fixed_t y2 = std::min(((tr.result.y + kTrackerRadius) / kBlockSize) / FRACUNIT, kYBlocks - 1);
if (player == nullptr) bool allMine = true;
{
return false;
}
if (inDuel) for (fixed_t x = x1; x <= x2; ++x)
{ {
// Always draw targets in 1v1 but don't draw player's for (fixed_t y = y1; y <= y2; ++y)
// own target on their own viewport. {
return player != stplyr; if (map[x][y])
{
allMine = false;
} }
else
// Except for DUEL mode, Overtime hides all TARGETs except
// the kiosk.
if (battleovertime.enabled)
{ {
return false; map[x][y] = true;
if (cv_debughudtracker.value)
{
V_DrawFill(x * kBlockSize, y * kBlockSize, kBlockSize, kBlockSize, 39 + debugColorCycle);
} }
if (player->emeralds != 0 && K_IsPlayerWanted(stplyr))
{
// The player who is about to win because of emeralds
// gets a TARGET on them
if (K_NumEmeralds(player) == 6) // 6 out of 7
{
return true;
} }
// WANTED player sees TARGETs on players holding
// emeralds
if (K_IsPlayerWanted(stplyr))
{
return true;
} }
} }
return K_IsPlayerWanted(player); if (allMine)
{
// This tracker claims every square
tr.foreground = true;
} }
bool is_object_tracking_target(const mobj_t* mobj) if (++debugColorCycle > 8)
{ {
switch (mobj->type) debugColorCycle = 0;
{
case MT_BATTLECAPSULE:
case MT_CDUFO:
return battleprisons;
case MT_PLAYER:
return is_player_tracking_target(mobj->player);
case MT_OVERTIME_CENTER:
return inDuel == false && battleovertime.enabled;
case MT_EMERALD:
return Obj_EmeraldCanHUDTrack(mobj) &&
((specialstageinfo.valid && specialstageinfo.ufo) || is_player_tracking_target());
case MT_MONITOR:
return is_player_tracking_target() && Obj_MonitorGetEmerald(mobj) != 0;
case MT_SUPER_FLICKY:
return Obj_IsSuperFlickyTargettingYou(mobj, stplyr->mo);
case MT_SPRAYCAN:
return !(mobj->renderflags & (RF_TRANSMASK | RF_DONTDRAW)); // the spraycan wasn't collected yet
default:
return false;
} }
} }
);
bool is_object_visible(mobj_t* mobj)
{
switch (mobj->type)
{
case MT_SUPER_FLICKY:
// Always flickers.
return (leveltime & 1);
case MT_SPRAYCAN:
// Flickers, but only when visible.
return P_CheckSight(stplyr->mo, mobj) && (leveltime & 1);
default:
// Flicker when not visible.
return P_CheckSight(stplyr->mo, mobj) || (leveltime & 1);
}
} }
}; // namespace }; // namespace
@ -506,23 +597,28 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player)
continue; continue;
} }
if (is_object_visible(mobj) == false)
{
continue;
}
vector3_t pos = { vector3_t pos = {
R_InterpolateFixed(mobj->old_x, mobj->x) + mobj->sprxoff, R_InterpolateFixed(mobj->old_x, mobj->x) + mobj->sprxoff,
R_InterpolateFixed(mobj->old_y, mobj->y) + mobj->spryoff, R_InterpolateFixed(mobj->old_y, mobj->y) + mobj->spryoff,
R_InterpolateFixed(mobj->old_z, mobj->z) + mobj->sprzoff + (mobj->height >> 1), R_InterpolateFixed(mobj->old_z, mobj->z) + mobj->sprzoff + (mobj->height >> 1),
}; };
targetList.push_back({mobj, pos, R_PointToDist2(origin->x, origin->y, pos.x, pos.y)}); TargetTracking tr;
tr.mobj = mobj;
tr.camDist = R_PointToDist2(origin->x, origin->y, pos.x, pos.y);
tr.foreground = false;
K_ObjectTracking(&tr.result, &pos, false);
targetList.push_back(tr);
} }
// Sort by distance from camera. Further trackers get // Sort by distance from camera. Further trackers get
// drawn first so nearer ones draw over them. // drawn first so nearer ones draw over them.
std::sort(targetList.begin(), targetList.end(), [](const auto& a, const auto& b) { return a.camDist > b.camDist; }); std::sort(targetList.begin(), targetList.end(), [](const auto& a, const auto& b) { return a.camDist > b.camDist; });
K_CullTargetList(targetList);
std::for_each(targetList.cbegin(), targetList.cend(), K_DrawTargetTracking); std::for_each(targetList.cbegin(), targetList.cend(), K_DrawTargetTracking);
} }

View file

@ -116,7 +116,7 @@ static void K_SpawnDuelOnlyItems(void)
void K_TimerReset(void) void K_TimerReset(void)
{ {
starttime = introtime = 0; starttime = introtime = 0;
darkness = darktimer = 0; memset(&g_darkness, 0, sizeof g_darkness);
numbulbs = 1; numbulbs = 1;
inDuel = rainbowstartavailable = false; inDuel = rainbowstartavailable = false;
linecrossed = 0; linecrossed = 0;
@ -7044,7 +7044,11 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
drop->movecount = amount; drop->movecount = amount;
} }
if (type < FIRSTPOWERUP)
{
// Pick up power-ups immediately
drop->flags |= MF_NOCLIPTHING; drop->flags |= MF_NOCLIPTHING;
}
if (gametyperules & GTR_CLOSERPLAYERS) if (gametyperules & GTR_CLOSERPLAYERS)
{ {
@ -8796,19 +8800,39 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_UpdateTripwire(player); K_UpdateTripwire(player);
if ((battleovertime.enabled >= 10*TICRATE) && !(player->pflags & PF_ELIMINATED) && !player->exiting) if (battleovertime.enabled)
{ {
fixed_t distanceToBarrier = 0; fixed_t distanceToCenter = 0;
if (battleovertime.radius > 0) if (battleovertime.radius > 0)
{ {
distanceToBarrier = R_PointToDist2(player->mo->x, player->mo->y, battleovertime.x, battleovertime.y) - (player->mo->radius * 2); distanceToCenter = R_PointToDist2(player->mo->x, player->mo->y, battleovertime.x, battleovertime.y);
} }
if (distanceToBarrier > battleovertime.radius) if (distanceToCenter + player->mo->radius > battleovertime.radius)
{
if (distanceToCenter - (player->mo->radius * 2) > battleovertime.radius &&
(battleovertime.enabled >= 10*TICRATE) &&
!(player->pflags & PF_ELIMINATED) &&
!player->exiting)
{ {
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_TIMEOVER); P_DamageMobj(player->mo, NULL, NULL, 1, DMG_TIMEOVER);
} }
if (leveltime < player->darkness_end)
{
if (leveltime > player->darkness_end - DARKNESS_FADE_TIME)
{
player->darkness_start = leveltime - (player->darkness_end - leveltime);
}
}
else
{
player->darkness_start = leveltime;
}
player->darkness_end = leveltime + (2 * DARKNESS_FADE_TIME);
}
} }
extern consvar_t cv_fuzz; extern consvar_t cv_fuzz;

View file

@ -182,7 +182,7 @@ boolean Obj_IsSuperFlickyTargettingYou(const mobj_t *flicky, mobj_t *player);
void Obj_BattleUFOLegThink(mobj_t *leg); void Obj_BattleUFOLegThink(mobj_t *leg);
void Obj_BattleUFOThink(mobj_t *ufo); void Obj_BattleUFOThink(mobj_t *ufo);
void Obj_SpawnBattleUFOLegs(mobj_t *ufo); void Obj_SpawnBattleUFOLegs(mobj_t *ufo);
void Obj_BattleUFODeath(mobj_t *ufo); void Obj_BattleUFODeath(mobj_t *ufo, mobj_t *inflictor);
void Obj_LinkBattleUFOSpawner(mobj_t *spawner); void Obj_LinkBattleUFOSpawner(mobj_t *spawner);
void Obj_UnlinkBattleUFOSpawner(mobj_t *spawner); void Obj_UnlinkBattleUFOSpawner(mobj_t *spawner);
void Obj_SpawnBattleUFOFromSpawner(void); void Obj_SpawnBattleUFOFromSpawner(void);

View file

@ -28,6 +28,8 @@ void Music_Init(void)
Tune& tune = g_tunes.insert("level"); Tune& tune = g_tunes.insert("level");
tune.priority = 1; tune.priority = 1;
tune.fade_out = 1500;
tune.fade_out_inclusive = false;
tune.resume_fade_in = 750; tune.resume_fade_in = 750;
tune.sync = true; tune.sync = true;
tune.credit = true; tune.credit = true;
@ -48,6 +50,20 @@ void Music_Init(void)
tune.fade_out = 3500; tune.fade_out = 3500;
} }
{
Tune& tune = g_tunes.insert("battle_overtime", g_tunes.find("level"));
tune.song = "shwdwn";
tune.priority = 11;
}
{
Tune& tune = g_tunes.insert("battle_overtime_stress", g_tunes.find("battle_overtime"));
tune.song = "shwdn2";
tune.priority = 10;
}
{ {
Tune& tune = g_tunes.insert("grow"); Tune& tune = g_tunes.insert("grow");
@ -216,7 +232,7 @@ void Music_DelayEnd(const char* id, tic_t duration)
} }
} }
void Music_Seek(const char* id, tic_t set) void Music_Seek(const char* id, UINT32 set)
{ {
Tune* tune = g_tunes.find(id); Tune* tune = g_tunes.find(id);

View file

@ -89,8 +89,8 @@ void Music_UnSuspend(const char *id);
// //
// Seek to a specific time in the tune. // Seek to a specific time in milliseconds in the tune.
void Music_Seek(const char *id, tic_t set); void Music_Seek(const char *id, UINT32 set);
// Remap a tune to another song. Use the lump name, with the // Remap a tune to another song. Use the lump name, with the
// 'O_' at the beginning removed. song is case insensitive. // 'O_' at the beginning removed. song is case insensitive.

View file

@ -200,7 +200,7 @@ void TuneManager::seek(Tune* tune)
uint32_t end = I_GetSongLength(); uint32_t end = I_GetSongLength();
uint32_t loop = I_GetSongLoopPoint(); uint32_t loop = I_GetSongLoopPoint();
uint32_t pos = detail::tics_to_msec(tune->seek + tune->elapsed()) * tune->speed(); uint32_t pos = (tune->seek + detail::tics_to_msec(tune->elapsed())) * tune->speed();
if (pos > end && (end - loop) > 0u) if (pos > end && (end - loop) > 0u)
{ {

View file

@ -11,6 +11,7 @@
#define MUSIC_TUNE_HPP #define MUSIC_TUNE_HPP
#include <algorithm> #include <algorithm>
#include <cstdint>
#include <optional> #include <optional>
#include <string> #include <string>
@ -68,7 +69,7 @@ public:
bool nightcoreable = false; bool nightcoreable = false;
// Start playing this number of tics into the tune. // Start playing this number of tics into the tune.
tic_t seek = 0; std::uint32_t seek = 0;
// these track state // these track state
bool can_fade_out = true; bool can_fade_out = true;

View file

@ -45,7 +45,7 @@ struct UFO : Mobj
void spawn_beam() void spawn_beam()
{ {
Mobj *x = spawn_from<Mobj>({0, 0, height / 4}, MT_BATTLEUFO_BEAM); Mobj *x = spawn_from<Mobj>({0, 0, sprzoff() + 26}, MT_BATTLEUFO_BEAM);
x->renderflags |= RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_SLOPESPLAT|RF_NOSPLATROLLANGLE; x->renderflags |= RF_FLOORSPRITE|RF_NOSPLATBILLBOARD|RF_SLOPESPLAT|RF_NOSPLATROLLANGLE;
x->colorized = true; x->colorized = true;
@ -107,8 +107,12 @@ public:
return; return;
} }
Fixed ofs = mobjinfo[MT_BATTLEUFO].height / 4;
Spawner* spawner = next(g_battleufo.previousId); Spawner* spawner = next(g_battleufo.previousId);
UFO* ufo = static_cast<UFO*>(P_SpawnMobjFromMobj(spawner, 0, 0, 250*FRACUNIT, MT_BATTLEUFO)); UFO* ufo = static_cast<UFO*>(P_SpawnMobjFromMobj(spawner, 0, 0, 250*FRACUNIT - ofs, MT_BATTLEUFO));
ufo->sprzoff(ofs * spawner->scale());
ufo->spawner(spawner); ufo->spawner(spawner);
} }
@ -137,14 +141,14 @@ void Obj_BattleUFOThink(mobj_t *mobj)
K_BattleOvertimeKiller(mobj); K_BattleOvertimeKiller(mobj);
} }
void Obj_BattleUFODeath(mobj_t *mobj) void Obj_BattleUFODeath(mobj_t *mobj, mobj_t *inflictor)
{ {
UFO* ufo = static_cast<UFO*>(mobj); UFO* ufo = static_cast<UFO*>(mobj);
const SINT8 flip = P_MobjFlip(ufo); const SINT8 flip = P_MobjFlip(ufo);
ufo->momz = -(8*mapobjectscale)/2; ufo->momz = -(8*mapobjectscale)/2;
K_CreatePaperItem( mobj_t* drop = K_CreatePaperItem(
ufo->x, ufo->x,
ufo->y, ufo->y,
ufo->z + (flip), ufo->z + (flip),
@ -154,6 +158,14 @@ void Obj_BattleUFODeath(mobj_t *mobj)
BATTLE_POWERUP_TIME BATTLE_POWERUP_TIME
); );
if (!P_MobjWasRemoved(inflictor) && inflictor->type == MT_INSTAWHIP)
{
// Take momentum of player who whips
inflictor = inflictor->target;
}
drop->momz = !P_MobjWasRemoved(inflictor) ? inflictor->momz : 0;
if (ufo->spawner()) if (ufo->spawner())
{ {
g_battleufo.previousId = ufo->spawner()->id(); g_battleufo.previousId = ufo->spawner()->id();
@ -196,6 +208,7 @@ void Obj_BattleUFOLegThink(mobj_t *leg)
// TODO: Take gravflip into account // TODO: Take gravflip into account
P_MoveOrigin(leg, x, y, leg->z); P_MoveOrigin(leg, x, y, leg->z);
leg->sprzoff = leg->target->sprzoff;
} }
leg->momz = leg->target->momz; leg->momz = leg->target->momz;

View file

@ -749,7 +749,8 @@ void Obj_CreateShrinkPohbees(player_t *owner)
ownerPos = owner->position; ownerPos = owner->position;
darktimer = POHBEE_TIME; g_darkness.start = leveltime;
g_darkness.end = leveltime + POHBEE_TIME + DARKNESS_FADE_TIME;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {

View file

@ -56,14 +56,15 @@ constexpr int kSearchRadius = 1920;
constexpr int kFlightRadius = 1280; constexpr int kFlightRadius = 1280;
constexpr int kPeckingRadius = 256; constexpr int kPeckingRadius = 256;
constexpr int kFlightSpeed = 2; constexpr fixed_t kFlightSpeed = 2*FRACUNIT;
constexpr int kPeckingSpeed = 8; constexpr fixed_t kPeckingSpeed = FRACUNIT/2;
constexpr fixed_t kWeakSpeed = FRACUNIT/2;
constexpr fixed_t kRebound = 8*FRACUNIT/9; constexpr fixed_t kRebound = 8*FRACUNIT/9;
constexpr tic_t kDelay = 8; constexpr tic_t kDelay = 8;
constexpr tic_t kStunTime = 5*TICRATE; constexpr tic_t kStunTime = 10*TICRATE;
constexpr tic_t kBlockTime = 1*TICRATE; constexpr tic_t kBlockTime = 5*TICRATE;
constexpr int kRiseTime = 1*TICRATE; constexpr int kRiseTime = 1*TICRATE;
constexpr int kRiseSpeed = 4; constexpr int kRiseSpeed = 4;
@ -394,15 +395,20 @@ struct Flicky : mobj_t
const Fly oldFly = fly(); const Fly oldFly = fly();
if (d < ANGLE_11hh && dist < kPeckingRadius * mapobjectscale) if (mode() == Mode::kWeak)
{
P_Thrust(this, th, FixedMul(kWeakSpeed, mapobjectscale));
fly(Fly::kNormal);
}
else if (d < ANGLE_11hh && dist < kPeckingRadius * mapobjectscale)
{ {
// Drastically speed up when about to intersect // Drastically speed up when about to intersect
P_Thrust(this, th, kPeckingSpeed * mapobjectscale); P_Thrust(this, th, FixedMul(kPeckingSpeed, mapobjectscale));
fly(Fly::kZoom); fly(Fly::kZoom);
} }
else else
{ {
P_Thrust(this, th, kFlightSpeed * mapobjectscale); P_Thrust(this, th, FixedMul(kFlightSpeed, mapobjectscale));
fly(Fly::kNormal); fly(Fly::kNormal);
} }
@ -493,6 +499,7 @@ struct Flicky : mobj_t
{ {
momx = -(momx); momx = -(momx);
momy = -(momy); momy = -(momy);
P_SetObjectMomZ(this, 8*FRACUNIT, false);
} }
void nerf() void nerf()
@ -505,7 +512,6 @@ struct Flicky : mobj_t
void whip() void whip()
{ {
reflect(); reflect();
P_SetObjectMomZ(this, 8*FRACUNIT, false);
nerf(); nerf();

View file

@ -396,9 +396,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_InstaThrust(player->mo, player->mo->angle, 20<<FRACBITS); P_InstaThrust(player->mo, player->mo->angle, 20<<FRACBITS);
return; return;
case MT_FLOATINGITEM: // SRB2Kart case MT_FLOATINGITEM: // SRB2Kart
// Avoid being picked up immediately
if (special->scale < special->destscale/2)
return;
if (special->threshold >= FIRSTPOWERUP) if (special->threshold >= FIRSTPOWERUP)
{ {
if (P_PlayerInPain(player)) if (P_PlayerInPain(player))
@ -408,6 +405,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
} }
else else
{ {
// Avoid being picked up immediately
if (special->scale < special->destscale/2)
return;
if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold)) if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold))
return; return;
@ -2352,7 +2353,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
Obj_MonitorOnDeath(target); Obj_MonitorOnDeath(target);
break; break;
case MT_BATTLEUFO: case MT_BATTLEUFO:
Obj_BattleUFODeath(target); Obj_BattleUFODeath(target, inflictor);
break; break;
case MT_BLENDEYE_MAIN: case MT_BLENDEYE_MAIN:
VS_BlendEye_Death(target); VS_BlendEye_Death(target);
@ -3103,7 +3104,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (clash) if (clash)
{ {
player->spheres = max(player->spheres - 10, 0); player->spheres = max(player->spheres - 5, 0);
if (inflictor) if (inflictor)
{ {

View file

@ -785,7 +785,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return BMIT_CONTINUE; // overhead return BMIT_CONTINUE; // overhead
} }
if (!tm.thing->player || !tm.thing->player->fastfall)
{
P_SetObjectMomZ(tm.thing, FRACUNIT, true); P_SetObjectMomZ(tm.thing, FRACUNIT, true);
}
fixed_t friction = 33*FRACUNIT/35;
tm.thing->momx = FixedMul(tm.thing->momx, friction);
tm.thing->momy = FixedMul(tm.thing->momy, friction);
return BMIT_CONTINUE; return BMIT_CONTINUE;
} }

View file

@ -6400,7 +6400,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
if (!(mobj->renderflags & RF_DONTDRAW)) if (!(mobj->renderflags & RF_DONTDRAW))
{ {
const INT32 numberdisplaymin = ((mobj->target->player->itemtype == KITEM_ORBINAUT) ? 5 : 2); //const INT32 numberdisplaymin = ((mobj->target->player->itemtype == KITEM_ORBINAUT) ? 5 : 2);
// Set it to use the correct states for its condition // Set it to use the correct states for its condition
if (mobj->target->player->itemRoulette.active) if (mobj->target->player->itemRoulette.active)
@ -6490,6 +6490,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->tracer->destscale = scale; mobj->tracer->destscale = scale;
#if 0
if (mobj->target->player->itemamount >= numberdisplaymin if (mobj->target->player->itemamount >= numberdisplaymin
&& mobj->target->player->itemamount <= 10) // Meh, too difficult to support greater than this; convert this to a decent HUD object and then maybe :V && mobj->target->player->itemamount <= 10) // Meh, too difficult to support greater than this; convert this to a decent HUD object and then maybe :V
{ {
@ -6507,6 +6508,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
P_SetScale(numx, mobj->scale); P_SetScale(numx, mobj->scale);
numx->destscale = scale; numx->destscale = scale;
} }
#endif
#if 0 #if 0
if (K_IsPlayerWanted(mobj->target->player) && mobj->movecount != 1) if (K_IsPlayerWanted(mobj->target->player) && mobj->movecount != 1)
@ -7449,7 +7451,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
case MT_FLOATINGITEM: case MT_FLOATINGITEM:
{ {
P_ResetPitchRoll(mobj); P_ResetPitchRoll(mobj);
if (mobj->flags & MF_NOCLIPTHING) if (!(mobj->flags & MF_NOGRAVITY))
{ {
if (P_CheckDeathPitCollide(mobj)) if (P_CheckDeathPitCollide(mobj))
{ {

View file

@ -787,6 +787,10 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT8(save->p, players[i].icecube.wiggle); WRITEUINT8(save->p, players[i].icecube.wiggle);
WRITEUINT32(save->p, players[i].icecube.frozenat); WRITEUINT32(save->p, players[i].icecube.frozenat);
WRITEUINT8(save->p, players[i].icecube.shaketimer); WRITEUINT8(save->p, players[i].icecube.shaketimer);
// darkness
WRITEUINT32(save->p, players[i].darkness_start);
WRITEUINT32(save->p, players[i].darkness_end);
} }
TracyCZoneEnd(__zone); TracyCZoneEnd(__zone);
@ -1358,6 +1362,10 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].icecube.frozenat = READUINT32(save->p); players[i].icecube.frozenat = READUINT32(save->p);
players[i].icecube.shaketimer = READUINT8(save->p); players[i].icecube.shaketimer = READUINT8(save->p);
// darkness
players[i].darkness_start = READUINT32(save->p);
players[i].darkness_end = READUINT32(save->p);
//players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point //players[i].viewheight = P_GetPlayerViewHeight(players[i]); // scale cannot be factored in at this point
} }
@ -6433,6 +6441,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITEINT32(save->p, numgotboxes); WRITEINT32(save->p, numgotboxes);
WRITEUINT8(save->p, numtargets); WRITEUINT8(save->p, numtargets);
WRITEUINT8(save->p, battleprisons); WRITEUINT8(save->p, battleprisons);
WRITEUINT8(save->p, g_emeraldWin);
WRITEUINT8(save->p, gamespeed); WRITEUINT8(save->p, gamespeed);
WRITEUINT8(save->p, numlaps); WRITEUINT8(save->p, numlaps);
@ -6445,6 +6454,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITEUINT16(save->p, battleovertime.enabled); WRITEUINT16(save->p, battleovertime.enabled);
WRITEFIXED(save->p, battleovertime.radius); WRITEFIXED(save->p, battleovertime.radius);
WRITEFIXED(save->p, battleovertime.initial_radius); WRITEFIXED(save->p, battleovertime.initial_radius);
WRITEUINT32(save->p, battleovertime.start);
WRITEFIXED(save->p, battleovertime.x); WRITEFIXED(save->p, battleovertime.x);
WRITEFIXED(save->p, battleovertime.y); WRITEFIXED(save->p, battleovertime.y);
WRITEFIXED(save->p, battleovertime.z); WRITEFIXED(save->p, battleovertime.z);
@ -6476,8 +6486,8 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITEUINT32(save->p, g_pointlimit); WRITEUINT32(save->p, g_pointlimit);
WRITEUINT32(save->p, darktimer); WRITEUINT32(save->p, g_darkness.start);
WRITEFIXED(save->p, darkness); WRITEUINT32(save->p, g_darkness.end);
WRITEUINT16(save->p, numchallengedestructibles); WRITEUINT16(save->p, numchallengedestructibles);
@ -6617,6 +6627,7 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
numgotboxes = READINT32(save->p); numgotboxes = READINT32(save->p);
numtargets = READUINT8(save->p); numtargets = READUINT8(save->p);
battleprisons = (boolean)READUINT8(save->p); battleprisons = (boolean)READUINT8(save->p);
g_emeraldWin = (boolean)READUINT8(save->p);
gamespeed = READUINT8(save->p); gamespeed = READUINT8(save->p);
numlaps = READUINT8(save->p); numlaps = READUINT8(save->p);
@ -6629,6 +6640,7 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
battleovertime.enabled = READUINT16(save->p); battleovertime.enabled = READUINT16(save->p);
battleovertime.radius = READFIXED(save->p); battleovertime.radius = READFIXED(save->p);
battleovertime.initial_radius = READFIXED(save->p); battleovertime.initial_radius = READFIXED(save->p);
battleovertime.start = READUINT32(save->p);
battleovertime.x = READFIXED(save->p); battleovertime.x = READFIXED(save->p);
battleovertime.y = READFIXED(save->p); battleovertime.y = READFIXED(save->p);
battleovertime.z = READFIXED(save->p); battleovertime.z = READFIXED(save->p);
@ -6660,8 +6672,8 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
g_pointlimit = READUINT32(save->p); g_pointlimit = READUINT32(save->p);
darktimer = READUINT32(save->p); g_darkness.start = READUINT32(save->p);
darkness = READFIXED(save->p); g_darkness.end = READUINT32(save->p);
numchallengedestructibles = READUINT16(save->p); numchallengedestructibles = READUINT16(save->p);

View file

@ -8202,7 +8202,7 @@ void P_LoadLevelMusic(void)
Music_Remap("level", music); Music_Remap("level", music);
tic_t level_music_start = starttime + (TICRATE/2); tic_t level_music_start = starttime + (TICRATE/2);
Music_Seek("level", std::max(leveltime, level_music_start) - level_music_start); Music_Seek("level", (std::max(leveltime, level_music_start) - level_music_start) * 1000UL / TICRATE);
} }
} }

View file

@ -46,6 +46,7 @@
#include "k_objects.h" #include "k_objects.h"
#include "music.h" #include "music.h"
#include "k_dialogue.h" #include "k_dialogue.h"
#include "m_easing.h"
#include "lua_profile.h" #include "lua_profile.h"
@ -767,6 +768,43 @@ void P_RunChaseCameras(void)
} }
} }
static fixed_t P_GetDarkness(tic_t start, tic_t end)
{
if (leveltime <= end)
{
tic_t fade = end - DARKNESS_FADE_TIME;
tic_t t;
if (leveltime < fade) // dark or darkening
{
t = leveltime - start;
t = min(t, DARKNESS_FADE_TIME);
}
else // lightening
{
t = end - leveltime;
}
return Easing_Linear((t * FRACUNIT) / DARKNESS_FADE_TIME, 0, FRACUNIT/7);
}
return 0;
}
static void P_TickDarkness(void)
{
const fixed_t globalValue = P_GetDarkness(g_darkness.start, g_darkness.end);
UINT8 i;
for (i = 0; i <= r_splitscreen; ++i)
{
const player_t *p = &players[displayplayers[i]];
fixed_t value = P_GetDarkness(p->darkness_start, p->darkness_end);
g_darkness.value[i] = value ? value : globalValue;
}
}
// //
// P_Ticker // P_Ticker
// //
@ -1045,22 +1083,7 @@ void P_Ticker(boolean run)
if (racecountdown > 1) if (racecountdown > 1)
racecountdown--; racecountdown--;
const fixed_t darkdelta = FRACUNIT/50; P_TickDarkness();
const fixed_t maxdark = FRACUNIT/7;
if (darktimer) // dark or darkening
{
darktimer--;
darkness += darkdelta;
darkness = min(darkness, maxdark);
}
else if (darkness >= darkdelta) // lightening
{
darkness -= darkdelta;
}
else // light
{
darkness = 0;
}
if (exitcountdown >= 1) if (exitcountdown >= 1)
{ {

View file

@ -18,6 +18,7 @@
#include "i_time.h" #include "i_time.h"
#include "m_fixed.h" #include "m_fixed.h"
#include "r_draw.h" #include "r_draw.h"
#include "r_fps.h"
#include "r_main.h" #include "r_main.h"
#include "g_game.h" #include "g_game.h"
@ -43,7 +44,7 @@ INT32 R_AdjustLightLevel(INT32 light)
if (!debugrender_highlight && cv_debugrender_contrast.value == 0) if (!debugrender_highlight && cv_debugrender_contrast.value == 0)
{ {
const fixed_t darken = FixedMul(FixedMul(darkness, mapheaderinfo[gamemap-1]->darkness), kRange); const fixed_t darken = FixedMul(FixedMul(g_darkness.value[R_GetViewNumber()], mapheaderinfo[gamemap-1]->darkness), kRange);
return std::clamp((light * FRACUNIT) - darken, 0, kRange) / FRACUNIT; return std::clamp((light * FRACUNIT) - darken, 0, kRange) / FRACUNIT;
} }

View file

@ -2296,7 +2296,7 @@ static void Command_Tunes_f(void)
Music_Play("stereo"); Music_Play("stereo");
if (argc > 3) if (argc > 3)
Music_Seek("stereo", (atoi(COM_Argv(3)) * TICRATE) / 1000); Music_Seek("stereo", atoi(COM_Argv(3)));
if (argc > 2) if (argc > 2)
{ {

View file

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