mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-12-21 07:22:33 +00:00
Add round end camera system
- Spins and zooms around a center point - Freezes the level while spinning - Pans over to follow and object after spinning ends - Timing and speed completely customizable
This commit is contained in:
parent
446f5d23f3
commit
7d6239e06c
8 changed files with 310 additions and 4 deletions
|
|
@ -157,6 +157,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
||||||
k_dialogue.cpp
|
k_dialogue.cpp
|
||||||
k_tally.cpp
|
k_tally.cpp
|
||||||
k_bans.cpp
|
k_bans.cpp
|
||||||
|
k_endcam.cpp
|
||||||
music.cpp
|
music.cpp
|
||||||
music_manager.cpp
|
music_manager.cpp
|
||||||
)
|
)
|
||||||
|
|
|
||||||
197
src/k_endcam.cpp
Normal file
197
src/k_endcam.cpp
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
// 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 "archive_wrapper.hpp"
|
||||||
|
|
||||||
|
#include "byteptr.h"
|
||||||
|
#include "doomdef.h"
|
||||||
|
#include "doomtype.h"
|
||||||
|
#include "g_game.h"
|
||||||
|
#include "k_endcam.h"
|
||||||
|
#include "m_easing.h"
|
||||||
|
#include "p_local.h"
|
||||||
|
#include "p_mobj.h"
|
||||||
|
#include "p_saveg.h"
|
||||||
|
#include "p_tick.h"
|
||||||
|
#include "r_fps.h"
|
||||||
|
#include "r_main.h"
|
||||||
|
|
||||||
|
endcam_t g_endcam;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
fixed_t interval(tic_t t, tic_t d)
|
||||||
|
{
|
||||||
|
return (std::min(t, d) * FRACUNIT) / std::max(d, 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_t interval(tic_t t, tic_t s, tic_t d)
|
||||||
|
{
|
||||||
|
return interval(std::max(t, s) - s, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
INT32 lerp(fixed_t f, INT32 a, INT32 b)
|
||||||
|
{
|
||||||
|
return a + FixedMul(f, b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Camera : camera_t
|
||||||
|
{
|
||||||
|
void pos(const vector3_t& p)
|
||||||
|
{
|
||||||
|
x = p.x;
|
||||||
|
y = p.y;
|
||||||
|
z = p.z;
|
||||||
|
subsector = R_PointInSubsector(x, y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EndCam : endcam_t
|
||||||
|
{
|
||||||
|
tic_t Time() const { return leveltime - begin; }
|
||||||
|
bool Freezing() const { return Time() <= swirlDuration; }
|
||||||
|
|
||||||
|
void GC()
|
||||||
|
{
|
||||||
|
if (P_MobjWasRemoved(panMobj))
|
||||||
|
{
|
||||||
|
P_SetTarget(&panMobj, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
active = true;
|
||||||
|
begin = leveltime;
|
||||||
|
|
||||||
|
// Reset all viewpoints
|
||||||
|
for (int i = 0; i < MAXSPLITSCREENPLAYERS; ++i)
|
||||||
|
{
|
||||||
|
Move(static_cast<Camera&>(camera[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_ResetViewInterpolation(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Move(Camera& cam)
|
||||||
|
{
|
||||||
|
tic_t t = Time();
|
||||||
|
fixed_t pan = Easing_OutQuint(interval(t, swirlDuration, panDuration), FRACUNIT, 0);
|
||||||
|
|
||||||
|
auto aim = [&](vector3_t& p, vector3_t& q)
|
||||||
|
{
|
||||||
|
cam.aiming = lerp(pan, cam.aiming, R_PointToAngle2(0, p.z, FixedHypot(q.x - p.x, q.y - p.y), q.z));
|
||||||
|
cam.pos(p);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (t <= swirlDuration)
|
||||||
|
{
|
||||||
|
fixed_t swirl = interval(t, swirlDuration);
|
||||||
|
|
||||||
|
angle_t ang = FixedAngle(swirl < FRACUNIT/2 ?
|
||||||
|
Easing_InOutQuint(swirl, startAngle, endAngle) :
|
||||||
|
Easing_InOutQuad(swirl, startAngle, endAngle));
|
||||||
|
|
||||||
|
fixed_t hDist = Easing_OutQuad(swirl, startRadius.x, endRadius.x);
|
||||||
|
|
||||||
|
vector3_t p = {
|
||||||
|
origin.x - FixedMul(FCOS(ang), hDist),
|
||||||
|
origin.y - FixedMul(FSIN(ang), hDist),
|
||||||
|
origin.z + Easing_OutQuad(swirl, startRadius.y, endRadius.y)
|
||||||
|
};
|
||||||
|
|
||||||
|
aim(p, origin);
|
||||||
|
cam.angle = ang;
|
||||||
|
}
|
||||||
|
else if (!P_MobjWasRemoved(panMobj))
|
||||||
|
{
|
||||||
|
vector3_t q = {panMobj->x, panMobj->y, P_GetMobjHead(panMobj)};
|
||||||
|
vector3_t p = {cam.x, cam.y, cam.z};
|
||||||
|
Follow(FixedMul(pan, panSpeed), p, q); // modifies p
|
||||||
|
|
||||||
|
aim(p, q);
|
||||||
|
cam.angle = lerp(
|
||||||
|
Easing_Linear(pan, FRACUNIT/4, FRACUNIT),
|
||||||
|
cam.angle,
|
||||||
|
R_PointToAngle2(p.x, p.y, q.x, q.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Archive(T&& ar)
|
||||||
|
{
|
||||||
|
static_assert(srb2::is_archive_wrapper_v<T>);
|
||||||
|
#define X(T, var) SRB2_ARCHIVE_WRAPPER_CALL(ar, T, var)
|
||||||
|
X(vector3_t, origin);
|
||||||
|
X(vector2_t, startRadius);
|
||||||
|
X(vector2_t, endRadius);
|
||||||
|
X(tic_t, swirlDuration);
|
||||||
|
X(fixed_t, startAngle);
|
||||||
|
X(fixed_t, endAngle);
|
||||||
|
// panMobj is handled in p_saveg.c
|
||||||
|
X(tic_t, panDuration);
|
||||||
|
X(fixed_t, panSpeed);
|
||||||
|
X(bool, active);
|
||||||
|
X(tic_t, begin);
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Follow(fixed_t f, vector3_t& p, vector3_t q) const
|
||||||
|
{
|
||||||
|
FV3_Sub(&q, &p);
|
||||||
|
q.x = FixedMul(q.x, f);
|
||||||
|
q.y = FixedMul(q.y, f);
|
||||||
|
q.z = FixedMul(q.z, f);
|
||||||
|
|
||||||
|
FV3_Add(&p, &q);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EndCam& endcam_cast()
|
||||||
|
{
|
||||||
|
return static_cast<EndCam&>(g_endcam);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
|
void K_CommitEndCamera(void)
|
||||||
|
{
|
||||||
|
endcam_cast().Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void K_MoveEndCamera(camera_t *thiscam)
|
||||||
|
{
|
||||||
|
endcam_cast().Move(static_cast<Camera&>(*thiscam));
|
||||||
|
}
|
||||||
|
|
||||||
|
void K_EndCameraGC(void)
|
||||||
|
{
|
||||||
|
endcam_cast().GC();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean K_EndCameraIsFreezing(void)
|
||||||
|
{
|
||||||
|
return endcam_cast().Freezing();
|
||||||
|
}
|
||||||
|
|
||||||
|
void K_SaveEndCamera(savebuffer_t *save)
|
||||||
|
{
|
||||||
|
endcam_cast().Archive(srb2::ArchiveWrapper(save));
|
||||||
|
}
|
||||||
|
|
||||||
|
void K_LoadEndCamera(savebuffer_t *save)
|
||||||
|
{
|
||||||
|
endcam_cast().Archive(srb2::UnArchiveWrapper(save));
|
||||||
|
}
|
||||||
74
src/k_endcam.h
Normal file
74
src/k_endcam.h
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// 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.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef k_endcam_h
|
||||||
|
#define k_endcam_h
|
||||||
|
|
||||||
|
#include "typedef.h"
|
||||||
|
|
||||||
|
#include "doomtype.h"
|
||||||
|
#include "m_fixed.h"
|
||||||
|
#include "tables.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct endcam_t
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Configurable properties
|
||||||
|
//
|
||||||
|
|
||||||
|
vector3_t origin; // center point
|
||||||
|
vector2_t startRadius; // X = horizontal, Y = vertical
|
||||||
|
vector2_t endRadius;
|
||||||
|
|
||||||
|
tic_t swirlDuration;
|
||||||
|
fixed_t startAngle; // 180*FRACUNIT, NOT ANGLE_180
|
||||||
|
fixed_t endAngle;
|
||||||
|
|
||||||
|
// 1) Camera pans vertically to keep this object centered
|
||||||
|
// 2) After swirling ends, pan horizontally too
|
||||||
|
mobj_t *panMobj;
|
||||||
|
tic_t panDuration; // dropoff after swirling ends
|
||||||
|
fixed_t panSpeed; // 0-FRACUNIT
|
||||||
|
|
||||||
|
/// ...
|
||||||
|
|
||||||
|
// You should not set these yourself.
|
||||||
|
// Use K_CommitEndCamera.
|
||||||
|
boolean active;
|
||||||
|
tic_t begin; // leveltime
|
||||||
|
};
|
||||||
|
|
||||||
|
extern endcam_t g_endcam;
|
||||||
|
|
||||||
|
// Sets endcam_t.active and endcam_t.begin.
|
||||||
|
//
|
||||||
|
// VERY IMPORTANT:
|
||||||
|
//
|
||||||
|
// Set the OTHER fields in endcam_t BEFORE calling this
|
||||||
|
// function, so the camera can cut away cleanly.
|
||||||
|
void K_CommitEndCamera(void);
|
||||||
|
|
||||||
|
/// ...
|
||||||
|
|
||||||
|
// Low-level functions
|
||||||
|
void K_MoveEndCamera(camera_t *thiscam);
|
||||||
|
void K_EndCameraGC(void);
|
||||||
|
boolean K_EndCameraIsFreezing(void);
|
||||||
|
void K_SaveEndCamera(savebuffer_t *save);
|
||||||
|
void K_LoadEndCamera(savebuffer_t *save);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif/*k_endcam_h*/
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
#include "g_party.h"
|
#include "g_party.h"
|
||||||
#include "k_vote.h"
|
#include "k_vote.h"
|
||||||
#include "k_zvote.h"
|
#include "k_zvote.h"
|
||||||
|
#include "k_endcam.h"
|
||||||
|
|
||||||
#include <tracy/tracy/TracyC.h>
|
#include <tracy/tracy/TracyC.h>
|
||||||
|
|
||||||
|
|
@ -5745,6 +5746,12 @@ static void P_RelinkPointers(void)
|
||||||
|
|
||||||
P_LoadMobjPointers(RelinkMobjVoid);
|
P_LoadMobjPointers(RelinkMobjVoid);
|
||||||
|
|
||||||
|
if (g_endcam.panMobj)
|
||||||
|
{
|
||||||
|
if (!RelinkMobj(&g_endcam.panMobj))
|
||||||
|
CONS_Debug(DBG_GAMELOGIC, "g_endcam.panMobj not found\n");
|
||||||
|
}
|
||||||
|
|
||||||
// use info field (value = oldposition) to relink mobjs
|
// use info field (value = oldposition) to relink mobjs
|
||||||
for (currentthinker = thlist[THINK_MOBJ].next; currentthinker != &thlist[THINK_MOBJ];
|
for (currentthinker = thlist[THINK_MOBJ].next; currentthinker != &thlist[THINK_MOBJ];
|
||||||
currentthinker = currentthinker->next)
|
currentthinker = currentthinker->next)
|
||||||
|
|
@ -6868,6 +6875,9 @@ void P_SaveNetGame(savebuffer_t *save, boolean resending)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
K_SaveEndCamera(save);
|
||||||
|
WriteMobjPointer(g_endcam.panMobj);
|
||||||
|
|
||||||
P_NetArchivePlayers(save);
|
P_NetArchivePlayers(save);
|
||||||
P_NetArchiveParties(save);
|
P_NetArchiveParties(save);
|
||||||
P_NetArchiveRoundQueue(save);
|
P_NetArchiveRoundQueue(save);
|
||||||
|
|
@ -6933,6 +6943,9 @@ boolean P_LoadNetGame(savebuffer_t *save, boolean reloading)
|
||||||
if (!P_NetUnArchiveMisc(save, reloading))
|
if (!P_NetUnArchiveMisc(save, reloading))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
K_LoadEndCamera(save);
|
||||||
|
ReadMobjPointer(&g_endcam.panMobj);
|
||||||
|
|
||||||
P_NetUnArchivePlayers(save);
|
P_NetUnArchivePlayers(save);
|
||||||
P_NetUnArchiveParties(save);
|
P_NetUnArchiveParties(save);
|
||||||
P_NetUnArchiveRoundQueue(save);
|
P_NetUnArchiveRoundQueue(save);
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@
|
||||||
#include "music.h"
|
#include "music.h"
|
||||||
#include "k_dialogue.h"
|
#include "k_dialogue.h"
|
||||||
#include "k_hud.h" // K_ClearPersistentMessages
|
#include "k_hud.h" // K_ClearPersistentMessages
|
||||||
|
#include "k_endcam.h"
|
||||||
|
|
||||||
// Replay names have time
|
// Replay names have time
|
||||||
#if !defined (UNDER_CE)
|
#if !defined (UNDER_CE)
|
||||||
|
|
@ -7696,6 +7697,8 @@ static void P_InitLevelSettings(void)
|
||||||
|
|
||||||
K_ResetSpecialStage();
|
K_ResetSpecialStage();
|
||||||
K_ResetBossInfo();
|
K_ResetBossInfo();
|
||||||
|
|
||||||
|
memset(&g_endcam, 0, sizeof g_endcam);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@
|
||||||
#include "k_dialogue.h"
|
#include "k_dialogue.h"
|
||||||
#include "m_easing.h"
|
#include "m_easing.h"
|
||||||
#include "k_hud.h" // messagetimer
|
#include "k_hud.h" // messagetimer
|
||||||
|
#include "k_endcam.h"
|
||||||
|
|
||||||
#include "lua_profile.h"
|
#include "lua_profile.h"
|
||||||
|
|
||||||
|
|
@ -65,12 +66,12 @@ static boolean g_freezeLevel;
|
||||||
|
|
||||||
boolean P_LevelIsFrozen(void)
|
boolean P_LevelIsFrozen(void)
|
||||||
{
|
{
|
||||||
return (g_freezeLevel || g_freezeCheat);
|
return (g_freezeLevel || g_freezeCheat || K_EndCameraIsFreezing());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean P_FreezeCheat(void)
|
boolean P_FreezeCheat(void)
|
||||||
{
|
{
|
||||||
return (g_freezeLevel || g_freezeCheat);
|
return (g_freezeLevel || g_freezeCheat || K_EndCameraIsFreezing());
|
||||||
}
|
}
|
||||||
|
|
||||||
void P_SetFreezeCheat(boolean value)
|
void P_SetFreezeCheat(boolean value)
|
||||||
|
|
@ -101,7 +102,7 @@ boolean P_MobjIsFrozen(mobj_t *mobj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_freezeLevel == true)
|
if (g_freezeLevel == true || K_EndCameraIsFreezing())
|
||||||
{
|
{
|
||||||
// level totally frozen
|
// level totally frozen
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -767,6 +768,8 @@ void P_RunChaseCameras(void)
|
||||||
P_MoveChaseCamera(&players[displayplayers[i]], &camera[i], false);
|
P_MoveChaseCamera(&players[displayplayers[i]], &camera[i], false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
K_EndCameraGC();
|
||||||
}
|
}
|
||||||
|
|
||||||
static fixed_t P_GetDarkness(tic_t start, tic_t end)
|
static fixed_t P_GetDarkness(tic_t start, tic_t end)
|
||||||
|
|
|
||||||
14
src/p_user.c
14
src/p_user.c
|
|
@ -67,6 +67,7 @@
|
||||||
#include "music.h"
|
#include "music.h"
|
||||||
#include "k_tally.h"
|
#include "k_tally.h"
|
||||||
#include "k_objects.h"
|
#include "k_objects.h"
|
||||||
|
#include "k_endcam.h"
|
||||||
|
|
||||||
#ifdef HWRENDER
|
#ifdef HWRENDER
|
||||||
#include "hardware/hw_light.h"
|
#include "hardware/hw_light.h"
|
||||||
|
|
@ -3062,6 +3063,11 @@ void P_ToggleDemoCamera(UINT8 viewnum)
|
||||||
|
|
||||||
void P_ResetCamera(player_t *player, camera_t *thiscam)
|
void P_ResetCamera(player_t *player, camera_t *thiscam)
|
||||||
{
|
{
|
||||||
|
if (g_endcam.active)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tic_t tries = 0;
|
tic_t tries = 0;
|
||||||
fixed_t x, y, z;
|
fixed_t x, y, z;
|
||||||
|
|
||||||
|
|
@ -3155,7 +3161,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
|
||||||
num = 0;
|
num = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thiscam->freecam || player->spectator)
|
if ((thiscam->freecam || player->spectator) && !g_endcam.active)
|
||||||
{
|
{
|
||||||
P_DemoCameraMovement(thiscam, num);
|
P_DemoCameraMovement(thiscam, num);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -3164,6 +3170,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
|
||||||
if (paused || P_AutoPause())
|
if (paused || P_AutoPause())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (g_endcam.active)
|
||||||
|
{
|
||||||
|
K_MoveEndCamera(thiscam);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
playerScale = FixedDiv(player->mo->scale, mapobjectscale);
|
playerScale = FixedDiv(player->mo->scale, mapobjectscale);
|
||||||
scaleDiff = playerScale - FRACUNIT;
|
scaleDiff = playerScale - FRACUNIT;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,9 @@ TYPEDEF (botcontroller_t);
|
||||||
// k_brightmap.h
|
// k_brightmap.h
|
||||||
TYPEDEF (brightmapStorage_t);
|
TYPEDEF (brightmapStorage_t);
|
||||||
|
|
||||||
|
// k_endcam.h
|
||||||
|
TYPEDEF (endcam_t);
|
||||||
|
|
||||||
// k_follower.h
|
// k_follower.h
|
||||||
TYPEDEF (follower_t);
|
TYPEDEF (follower_t);
|
||||||
TYPEDEF (followercategory_t);
|
TYPEDEF (followercategory_t);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue