mirror of
https://github.com/ZDoom/raze-gles.git
synced 2025-01-14 11:50:49 +00:00
Merge branch 'master' into newrenderer
# Conflicts: # source/games/exhumed/src/2d.cpp
This commit is contained in:
commit
82194bbf6b
48 changed files with 1954 additions and 1533 deletions
|
@ -1034,6 +1034,7 @@ set (PCH_SOURCES
|
||||||
build/src/polymost.cpp
|
build/src/polymost.cpp
|
||||||
|
|
||||||
core/movie/playmve.cpp
|
core/movie/playmve.cpp
|
||||||
|
core/movie/movieplayer.cpp
|
||||||
core/automap.cpp
|
core/automap.cpp
|
||||||
core/cheats.cpp
|
core/cheats.cpp
|
||||||
core/cheathandler.cpp
|
core/cheathandler.cpp
|
||||||
|
|
|
@ -188,6 +188,7 @@ enum
|
||||||
|
|
||||||
// Raze extensions, using the higher bits to avoid conflitcs with the reserved and undocumented bits above.
|
// Raze extensions, using the higher bits to avoid conflitcs with the reserved and undocumented bits above.
|
||||||
CSTAT_SPRITE_MDLROTATE = 1u<<16u, // Only for tsprites: rotate if this is a model or voxel.
|
CSTAT_SPRITE_MDLROTATE = 1u<<16u, // Only for tsprites: rotate if this is a model or voxel.
|
||||||
|
CSTAT_SPRITE_NOFIND = 1u<<17u, // Invisible to neartag and hitscan
|
||||||
|
|
||||||
};
|
};
|
||||||
enum
|
enum
|
||||||
|
|
|
@ -1412,6 +1412,10 @@ int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32
|
||||||
{
|
{
|
||||||
auto const spr = (uspriteptr_t)&sprite[z];
|
auto const spr = (uspriteptr_t)&sprite[z];
|
||||||
uint32_t const cstat = spr->cstat;
|
uint32_t const cstat = spr->cstat;
|
||||||
|
|
||||||
|
if (cstat & CSTAT_SPRITE_NOFIND)
|
||||||
|
continue;
|
||||||
|
|
||||||
#ifdef USE_OPENGL
|
#ifdef USE_OPENGL
|
||||||
if (!hitallsprites)
|
if (!hitallsprites)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1167,6 +1167,8 @@ void neartag(int32_t xs, int32_t ys, int32_t zs, int16_t sectnum, int16_t ange,
|
||||||
{
|
{
|
||||||
auto const spr = (uspriteptr_t)&sprite[z];
|
auto const spr = (uspriteptr_t)&sprite[z];
|
||||||
|
|
||||||
|
if (spr->cstat & CSTAT_SPRITE_NOFIND)
|
||||||
|
continue;
|
||||||
if (blacklist_sprite_func && blacklist_sprite_func(z))
|
if (blacklist_sprite_func && blacklist_sprite_func(z))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
|
@ -189,8 +189,9 @@ DEFINE_ACTION_FUNCTION_NATIVE(DShape2D, PushTriangle, Shape2D_PushTriangle)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
int F2DDrawer::AddCommand(const RenderCommand *data)
|
int F2DDrawer::AddCommand(RenderCommand *data)
|
||||||
{
|
{
|
||||||
|
data->mScreenFade = screenFade;
|
||||||
if (mData.Size() > 0 && data->isCompatible(mData.Last()))
|
if (mData.Size() > 0 && data->isCompatible(mData.Last()))
|
||||||
{
|
{
|
||||||
// Merge with the last command.
|
// Merge with the last command.
|
||||||
|
|
|
@ -118,6 +118,7 @@ public:
|
||||||
ETexMode mDrawMode;
|
ETexMode mDrawMode;
|
||||||
uint8_t mLightLevel;
|
uint8_t mLightLevel;
|
||||||
uint8_t mFlags;
|
uint8_t mFlags;
|
||||||
|
float mScreenFade;
|
||||||
|
|
||||||
bool useTransform;
|
bool useTransform;
|
||||||
DMatrix3x3 transform;
|
DMatrix3x3 transform;
|
||||||
|
@ -149,6 +150,7 @@ public:
|
||||||
mLightLevel == other.mLightLevel &&
|
mLightLevel == other.mLightLevel &&
|
||||||
mColor1.d == other.mColor1.d &&
|
mColor1.d == other.mColor1.d &&
|
||||||
useTransform == other.useTransform &&
|
useTransform == other.useTransform &&
|
||||||
|
mScreenFade == other.mScreenFade &&
|
||||||
(
|
(
|
||||||
!useTransform ||
|
!useTransform ||
|
||||||
(
|
(
|
||||||
|
@ -172,7 +174,7 @@ public:
|
||||||
int fullscreenautoaspect = 3;
|
int fullscreenautoaspect = 3;
|
||||||
int cliptop = -1, clipleft = -1, clipwidth = -1, clipheight = -1;
|
int cliptop = -1, clipleft = -1, clipwidth = -1, clipheight = -1;
|
||||||
|
|
||||||
int AddCommand(const RenderCommand *data);
|
int AddCommand(RenderCommand *data);
|
||||||
void AddIndices(int firstvert, int count, ...);
|
void AddIndices(int firstvert, int count, ...);
|
||||||
private:
|
private:
|
||||||
void AddIndices(int firstvert, TArray<int> &v);
|
void AddIndices(int firstvert, TArray<int> &v);
|
||||||
|
|
|
@ -110,13 +110,18 @@ void S_SetMusicCallbacks(MusicCallbacks* cb)
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
static std::unique_ptr<SoundStream> musicStream;
|
static std::unique_ptr<SoundStream> musicStream;
|
||||||
|
static TArray<SoundStream*> customStreams;
|
||||||
|
|
||||||
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata)
|
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata)
|
||||||
{
|
{
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (numchannels < 2) flags |= SoundStream::Mono;
|
if (numchannels < 2) flags |= SoundStream::Mono;
|
||||||
auto stream = GSnd->CreateStream(cb, int(size), flags, samplerate, userdata);
|
auto stream = GSnd->CreateStream(cb, int(size), flags, samplerate, userdata);
|
||||||
if (stream) stream->Play(true, 1);
|
if (stream)
|
||||||
|
{
|
||||||
|
stream->Play(true, 1);
|
||||||
|
customStreams.Push(stream);
|
||||||
|
}
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,11 +130,19 @@ void S_StopCustomStream(SoundStream *stream)
|
||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
stream->Stop();
|
stream->Stop();
|
||||||
|
auto f = customStreams.Find(stream);
|
||||||
|
if (f < customStreams.Size()) customStreams.Delete(f);
|
||||||
delete stream;
|
delete stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void S_PauseAllCustomStreams(bool on)
|
||||||
|
{
|
||||||
|
for (auto s : customStreams)
|
||||||
|
{
|
||||||
|
s->SetPaused(on);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static TArray<int16_t> convert;
|
static TArray<int16_t> convert;
|
||||||
static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata)
|
static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata)
|
||||||
|
|
|
@ -14,6 +14,7 @@ class SoundStream;
|
||||||
typedef bool(*StreamCallback)(SoundStream* stream, void* buff, int len, void* userdata);
|
typedef bool(*StreamCallback)(SoundStream* stream, void* buff, int len, void* userdata);
|
||||||
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata);
|
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata);
|
||||||
void S_StopCustomStream(SoundStream* stream);
|
void S_StopCustomStream(SoundStream* stream);
|
||||||
|
void S_PauseAllCustomStreams(bool on);
|
||||||
|
|
||||||
struct MusicCallbacks
|
struct MusicCallbacks
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,12 +76,15 @@ void D_ProcessEvents (void)
|
||||||
continue;
|
continue;
|
||||||
if (ev->type == EV_DeviceChange)
|
if (ev->type == EV_DeviceChange)
|
||||||
UpdateJoystickMenu(I_UpdateDeviceList());
|
UpdateJoystickMenu(I_UpdateDeviceList());
|
||||||
if (gamestate == GS_INTRO)
|
|
||||||
continue;
|
if (gamestate != GS_INTRO) // GS_INTRO blocks the UI.
|
||||||
if (C_Responder (ev))
|
{
|
||||||
continue; // console ate the event
|
if (C_Responder(ev))
|
||||||
if (M_Responder (ev))
|
continue; // console ate the event
|
||||||
continue; // menu ate the event
|
if (M_Responder(ev))
|
||||||
|
continue; // menu ate the event
|
||||||
|
}
|
||||||
|
|
||||||
G_Responder (ev);
|
G_Responder (ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,6 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state)
|
||||||
vb.UploadData(&vertices[0], vertices.Size(), &indices[0], indices.Size());
|
vb.UploadData(&vertices[0], vertices.Size(), &indices[0], indices.Size());
|
||||||
state.SetVertexBuffer(&vb);
|
state.SetVertexBuffer(&vb);
|
||||||
state.EnableFog(false);
|
state.EnableFog(false);
|
||||||
state.SetScreenFade(drawer->screenFade);
|
|
||||||
|
|
||||||
for(auto &cmd : commands)
|
for(auto &cmd : commands)
|
||||||
{
|
{
|
||||||
|
@ -94,6 +93,7 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state)
|
||||||
state.SetRenderStyle(cmd.mRenderStyle);
|
state.SetRenderStyle(cmd.mRenderStyle);
|
||||||
state.EnableBrightmap(!(cmd.mRenderStyle.Flags & STYLEF_ColorIsFixed));
|
state.EnableBrightmap(!(cmd.mRenderStyle.Flags & STYLEF_ColorIsFixed));
|
||||||
state.EnableFog(2); // Special 2D mode 'fog'.
|
state.EnableFog(2); // Special 2D mode 'fog'.
|
||||||
|
state.SetScreenFade(cmd.mScreenFade);
|
||||||
|
|
||||||
state.SetTextureMode(cmd.mDrawMode);
|
state.SetTextureMode(cmd.mDrawMode);
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#include "mmulti.h"
|
#include "mmulti.h"
|
||||||
#include "gstrings.h"
|
#include "gstrings.h"
|
||||||
#include "gamecontrol.h"
|
#include "gamecontrol.h"
|
||||||
|
#include "screenjob.h"
|
||||||
#include "mapinfo.h"
|
#include "mapinfo.h"
|
||||||
|
|
||||||
CVAR(Bool, sv_cheats, true, CVAR_ARCHIVE|CVAR_SERVERINFO)
|
CVAR(Bool, sv_cheats, true, CVAR_ARCHIVE|CVAR_SERVERINFO)
|
||||||
|
@ -239,6 +240,17 @@ void changeMap(int player, uint8_t** stream, bool skip)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void endScreenJob(int player, uint8_t** stream, bool skip)
|
||||||
|
{
|
||||||
|
if (!skip) EndScreenJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void ChangeLevel(MapRecord* map, int skill)
|
void ChangeLevel(MapRecord* map, int skill)
|
||||||
{
|
{
|
||||||
Net_WriteByte(DEM_CHANGEMAP);
|
Net_WriteByte(DEM_CHANGEMAP);
|
||||||
|
|
|
@ -8,3 +8,4 @@ EXTERN_CVAR(Bool, sv_cheats)
|
||||||
|
|
||||||
void genericCheat(int player, uint8_t** stream, bool skip);
|
void genericCheat(int player, uint8_t** stream, bool skip);
|
||||||
void changeMap(int player, uint8_t** stream, bool skip);
|
void changeMap(int player, uint8_t** stream, bool skip);
|
||||||
|
void endScreenJob(int player, uint8_t** stream, bool skip);
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "gamecontrol.h"
|
#include "gamecontrol.h"
|
||||||
#include "uiinput.h"
|
#include "uiinput.h"
|
||||||
#include "automap.h"
|
#include "automap.h"
|
||||||
|
#include "screenjob.h"
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
|
@ -53,6 +54,11 @@
|
||||||
|
|
||||||
bool G_Responder (event_t *ev)
|
bool G_Responder (event_t *ev)
|
||||||
{
|
{
|
||||||
|
if (gamestate == GS_INTRO || gamestate == GS_INTERMISSION)
|
||||||
|
{
|
||||||
|
return ScreenJobResponder(ev);
|
||||||
|
}
|
||||||
|
|
||||||
if (CT_Responder(ev))
|
if (CT_Responder(ev))
|
||||||
return true; // chat ate the event
|
return true; // chat ate the event
|
||||||
if (Cheat_Responder(ev))
|
if (Cheat_Responder(ev))
|
||||||
|
|
|
@ -1674,6 +1674,7 @@ bool D_CheckNetGame (void)
|
||||||
|
|
||||||
Net_SetCommandHandler(DEM_GENERICCHEAT, genericCheat);
|
Net_SetCommandHandler(DEM_GENERICCHEAT, genericCheat);
|
||||||
Net_SetCommandHandler(DEM_CHANGEMAP, changeMap);
|
Net_SetCommandHandler(DEM_CHANGEMAP, changeMap);
|
||||||
|
Net_SetCommandHandler(DEM_ENDSCREENJOB, endScreenJob);
|
||||||
|
|
||||||
for (i = 0; i < MAXNETNODES; i++)
|
for (i = 0; i < MAXNETNODES; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -89,6 +89,7 @@ enum EDemoCommand
|
||||||
DEM_GENERICCHEAT,
|
DEM_GENERICCHEAT,
|
||||||
DEM_GIVE,
|
DEM_GIVE,
|
||||||
DEM_CHANGEMAP,
|
DEM_CHANGEMAP,
|
||||||
|
DEM_ENDSCREENJOB,
|
||||||
|
|
||||||
DEM_MAX
|
DEM_MAX
|
||||||
};
|
};
|
||||||
|
|
|
@ -1008,29 +1008,6 @@ int RunGame()
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
void TickSubsystems()
|
|
||||||
{
|
|
||||||
// run these on an independent timer until we got something working for the games.
|
|
||||||
static const uint64_t tickInterval = 1'000'000'000 / 30;
|
|
||||||
static uint64_t nexttick = 0;
|
|
||||||
|
|
||||||
auto nowtick = I_nsTime();
|
|
||||||
if (nexttick == 0) nexttick = nowtick;
|
|
||||||
int cnt = 0;
|
|
||||||
while (nexttick <= nowtick && cnt < 5)
|
|
||||||
{
|
|
||||||
nexttick += tickInterval;
|
|
||||||
C_Ticker();
|
|
||||||
M_Ticker();
|
|
||||||
C_RunDelayedCommands();
|
|
||||||
cnt++;
|
|
||||||
}
|
|
||||||
// If this took too long the engine was most likely suspended so recalibrate the timer.
|
|
||||||
// Perfect precision is not needed here.
|
|
||||||
if (cnt == 5) nexttick = nowtick + tickInterval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updatePauseStatus()
|
void updatePauseStatus()
|
||||||
{
|
{
|
||||||
// This must go through the network in multiplayer games.
|
// This must go through the network in multiplayer games.
|
||||||
|
@ -1053,7 +1030,10 @@ void updatePauseStatus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paused ? S_PauseSound(!pausedWithKey, !paused) : S_ResumeSound(paused);
|
if (paused)
|
||||||
|
S_PauseSound(!pausedWithKey, !paused);
|
||||||
|
else
|
||||||
|
S_ResumeSound(paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -1143,6 +1123,7 @@ void S_PauseSound (bool notmusic, bool notsfx)
|
||||||
{
|
{
|
||||||
soundEngine->SetPaused(true);
|
soundEngine->SetPaused(true);
|
||||||
GSnd->SetSfxPaused (true, 0);
|
GSnd->SetSfxPaused (true, 0);
|
||||||
|
S_PauseAllCustomStreams(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1161,6 +1142,7 @@ void S_ResumeSound (bool notsfx)
|
||||||
{
|
{
|
||||||
soundEngine->SetPaused(false);
|
soundEngine->SetPaused(false);
|
||||||
GSnd->SetSfxPaused (false, 0);
|
GSnd->SetSfxPaused (false, 0);
|
||||||
|
S_PauseAllCustomStreams(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -328,9 +328,10 @@ static void GameTicker()
|
||||||
|
|
||||||
case GS_MENUSCREEN:
|
case GS_MENUSCREEN:
|
||||||
case GS_FULLCONSOLE:
|
case GS_FULLCONSOLE:
|
||||||
|
break;
|
||||||
case GS_INTERMISSION:
|
case GS_INTERMISSION:
|
||||||
case GS_INTRO:
|
case GS_INTRO:
|
||||||
// These elements do not tick at game rate.
|
ScreenJobTick();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -370,7 +371,7 @@ void Display()
|
||||||
case GS_INTRO:
|
case GS_INTRO:
|
||||||
case GS_INTERMISSION:
|
case GS_INTERMISSION:
|
||||||
// screen jobs are not bound by the game ticker so they need to be ticked in the display loop.
|
// screen jobs are not bound by the game ticker so they need to be ticked in the display loop.
|
||||||
RunScreenJobFrame();
|
ScreenJobDraw();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GS_LEVEL:
|
case GS_LEVEL:
|
||||||
|
|
814
source/core/movie/movieplayer.cpp
Normal file
814
source/core/movie/movieplayer.cpp
Normal file
|
@ -0,0 +1,814 @@
|
||||||
|
/*
|
||||||
|
** movieplayer.cpp
|
||||||
|
**
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
** Copyright 2020 Christoph Oelckers
|
||||||
|
** All rights reserved.
|
||||||
|
**
|
||||||
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions
|
||||||
|
** are met:
|
||||||
|
**
|
||||||
|
** 1. Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in the
|
||||||
|
** documentation and/or other materials provided with the distribution.
|
||||||
|
** 3. The name of the author may not be used to endorse or promote products
|
||||||
|
** derived from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
**---------------------------------------------------------------------------
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "build.h"
|
||||||
|
#include "screenjob.h"
|
||||||
|
#include "i_time.h"
|
||||||
|
#include "v_2ddrawer.h"
|
||||||
|
#include "animlib.h"
|
||||||
|
#include "v_draw.h"
|
||||||
|
#include "s_soundinternal.h"
|
||||||
|
#include "animtexture.h"
|
||||||
|
#include "gamestate.h"
|
||||||
|
#include "razemenu.h"
|
||||||
|
#include "raze_sound.h"
|
||||||
|
#include "SmackerDecoder.h"
|
||||||
|
#include "movie/playmve.h"
|
||||||
|
#include "gamecontrol.h"
|
||||||
|
#include <vpx/vpx_decoder.h>
|
||||||
|
#include <vpx/vp8dx.h>
|
||||||
|
#include "raze_music.h"
|
||||||
|
|
||||||
|
|
||||||
|
class MoviePlayer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void Start() {}
|
||||||
|
virtual bool Frame(uint64_t clock) = 0;
|
||||||
|
virtual void Stop() {}
|
||||||
|
virtual ~MoviePlayer() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class AnmPlayer : public MoviePlayer
|
||||||
|
{
|
||||||
|
// This doesn't need its own class type
|
||||||
|
anim_t anim;
|
||||||
|
TArray<uint8_t> buffer;
|
||||||
|
int numframes = 0;
|
||||||
|
int curframe = 1;
|
||||||
|
int frametime = 0;
|
||||||
|
int nextframetime = 0;
|
||||||
|
AnimTextures animtex;
|
||||||
|
const AnimSound* animSnd;
|
||||||
|
const int* frameTicks;
|
||||||
|
bool nostopsound;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isvalid() { return numframes > 0; }
|
||||||
|
|
||||||
|
AnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff)
|
||||||
|
: animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff)
|
||||||
|
{
|
||||||
|
buffer = fr.ReadPadded(1);
|
||||||
|
fr.Close();
|
||||||
|
|
||||||
|
if (ANIM_LoadAnim(&anim, buffer.Data(), buffer.Size() - 1) < 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
numframes = ANIM_NumFrames(&anim);
|
||||||
|
animtex.SetSize(AnimTexture::Paletted, 320, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Frame(uint64_t clock) override
|
||||||
|
{
|
||||||
|
int currentclock = int(clock * 120 / 1'000'000'000);
|
||||||
|
|
||||||
|
if (currentclock < nextframetime - 1)
|
||||||
|
{
|
||||||
|
twod->ClearScreen();
|
||||||
|
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe));
|
||||||
|
frametime = currentclock;
|
||||||
|
|
||||||
|
twod->ClearScreen();
|
||||||
|
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
||||||
|
|
||||||
|
int delay = 20;
|
||||||
|
if (frameTicks)
|
||||||
|
{
|
||||||
|
if (curframe == 1) delay = frameTicks[0];
|
||||||
|
else if (curframe < numframes - 2) delay = frameTicks[1];
|
||||||
|
else delay = frameTicks[2];
|
||||||
|
}
|
||||||
|
nextframetime += delay;
|
||||||
|
|
||||||
|
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||||
|
{
|
||||||
|
if (animSnd[i].framenum == curframe)
|
||||||
|
{
|
||||||
|
int sound = animSnd[i].soundnum;
|
||||||
|
if (sound == -1)
|
||||||
|
soundEngine->StopAllChannels();
|
||||||
|
else if (SoundEnabled())
|
||||||
|
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nostopsound && curframe == numframes && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, -1)) return true;
|
||||||
|
curframe++;
|
||||||
|
return curframe < numframes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop() override
|
||||||
|
{
|
||||||
|
if (!nostopsound) soundEngine->StopAllChannels();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
~AnmPlayer()
|
||||||
|
{
|
||||||
|
buffer.Reset();
|
||||||
|
animtex.Clean();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class MvePlayer : public MoviePlayer
|
||||||
|
{
|
||||||
|
InterplayDecoder decoder;
|
||||||
|
bool failed = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isvalid() { return !failed; }
|
||||||
|
|
||||||
|
MvePlayer(FileReader& fr) : decoder(SoundEnabled())
|
||||||
|
{
|
||||||
|
failed = !decoder.Open(fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Frame(uint64_t clock) override
|
||||||
|
{
|
||||||
|
if (failed) return false;
|
||||||
|
bool playon = decoder.RunFrame(clock);
|
||||||
|
twod->ClearScreen();
|
||||||
|
DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||||
|
return playon;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MvePlayer()
|
||||||
|
{
|
||||||
|
decoder.Close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class VpxPlayer : public MoviePlayer
|
||||||
|
{
|
||||||
|
bool failed = false;
|
||||||
|
FileReader fr;
|
||||||
|
AnimTextures animtex;
|
||||||
|
const AnimSound* animSnd;
|
||||||
|
|
||||||
|
unsigned width, height;
|
||||||
|
TArray<uint8_t> Pic;
|
||||||
|
TArray<uint8_t> readBuf;
|
||||||
|
vpx_codec_ctx_t codec{};
|
||||||
|
vpx_codec_iter_t iter = nullptr;
|
||||||
|
|
||||||
|
uint32_t convnumer;
|
||||||
|
uint32_t convdenom;
|
||||||
|
|
||||||
|
uint64_t nsecsperframe;
|
||||||
|
uint64_t nextframetime;
|
||||||
|
|
||||||
|
int decstate = 0;
|
||||||
|
int framenum = 0;
|
||||||
|
int numframes;
|
||||||
|
int lastsoundframe = -1;
|
||||||
|
public:
|
||||||
|
int soundtrack = -1;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isvalid() { return !failed; }
|
||||||
|
|
||||||
|
VpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay, FString& error)
|
||||||
|
{
|
||||||
|
fr = std::move(fr_);
|
||||||
|
animSnd = animSnd_;
|
||||||
|
|
||||||
|
if (!ReadIVFHeader(origframedelay))
|
||||||
|
{
|
||||||
|
// We should never get here, because any file failing this has been eliminated before this constructor got called.
|
||||||
|
error.Format("Failed reading IVF header\n");
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pic.Resize(width * height * 4);
|
||||||
|
|
||||||
|
|
||||||
|
// Todo: Support VP9 as well?
|
||||||
|
vpx_codec_dec_cfg_t cfg = { 1, width, height };
|
||||||
|
if (vpx_codec_dec_init(&codec, &vpx_codec_vp8_dx_algo, &cfg, 0))
|
||||||
|
{
|
||||||
|
error.Format("Error initializing VPX codec.\n");
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool ReadIVFHeader(int origframedelay)
|
||||||
|
{
|
||||||
|
// IVF format: http://wiki.multimedia.cx/index.php?title=IVF
|
||||||
|
uint32_t magic; fr.Read(&magic, 4); // do not byte swap!
|
||||||
|
if (magic != MAKE_ID('D', 'K', 'I', 'F')) return false;
|
||||||
|
uint16_t version = fr.ReadUInt16();
|
||||||
|
if (version != 0) return false;
|
||||||
|
uint16_t length = fr.ReadUInt16();
|
||||||
|
if (length != 32) return false;
|
||||||
|
fr.Read(&magic, 4);
|
||||||
|
if (magic != MAKE_ID('V', 'P', '8', '0')) return false;
|
||||||
|
|
||||||
|
width = fr.ReadUInt16();
|
||||||
|
height = fr.ReadUInt16();
|
||||||
|
uint32_t fpsdenominator = fr.ReadUInt32();
|
||||||
|
uint32_t fpsnumerator = fr.ReadUInt32();
|
||||||
|
numframes = fr.ReadUInt32();
|
||||||
|
if (numframes == 0) return false;
|
||||||
|
fr.Seek(4, FileReader::SeekCur);
|
||||||
|
|
||||||
|
if (fpsdenominator > 1000 || fpsnumerator == 0 || fpsdenominator == 0)
|
||||||
|
{
|
||||||
|
// default to 30 fps if the header does not provide useful info.
|
||||||
|
fpsdenominator = 30;
|
||||||
|
fpsnumerator = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
convnumer = 120 * fpsnumerator;
|
||||||
|
convdenom = fpsdenominator * origframedelay;
|
||||||
|
|
||||||
|
nsecsperframe = int64_t(fpsnumerator) * 1'000'000'000 / fpsdenominator;
|
||||||
|
nextframetime = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool ReadFrame()
|
||||||
|
{
|
||||||
|
int corrupted = 0;
|
||||||
|
int framesize = fr.ReadInt32();
|
||||||
|
fr.Seek(8, FileReader::SeekCur);
|
||||||
|
if (framesize == 0) return false;
|
||||||
|
|
||||||
|
readBuf.Resize(framesize);
|
||||||
|
if (fr.Read(readBuf.Data(), framesize) != framesize) return false;
|
||||||
|
if (vpx_codec_decode(&codec, readBuf.Data(), readBuf.Size(), NULL, 0) != VPX_CODEC_OK) return false;
|
||||||
|
if (vpx_codec_control(&codec, VP8D_GET_FRAME_CORRUPTED, &corrupted) != VPX_CODEC_OK) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
vpx_image_t *GetFrameData()
|
||||||
|
{
|
||||||
|
vpx_image_t *img;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (decstate == 0) // first time / begin
|
||||||
|
{
|
||||||
|
if (!ReadFrame()) return nullptr;
|
||||||
|
decstate = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
img = vpx_codec_get_frame(&codec, &iter);
|
||||||
|
if (img == nullptr)
|
||||||
|
{
|
||||||
|
decstate = 0;
|
||||||
|
iter = nullptr;
|
||||||
|
}
|
||||||
|
} while (img == nullptr);
|
||||||
|
|
||||||
|
return img->d_w == width && img->d_h == height? img : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void SetPixel(uint8_t* dest, uint8_t y, uint8_t u, uint8_t v)
|
||||||
|
{
|
||||||
|
dest[0] = y;
|
||||||
|
dest[1] = u;
|
||||||
|
dest[2] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateNextFrame()
|
||||||
|
{
|
||||||
|
auto img = GetFrameData();
|
||||||
|
if (!img) return false;
|
||||||
|
uint8_t const* const yplane = img->planes[VPX_PLANE_Y];
|
||||||
|
uint8_t const* const uplane = img->planes[VPX_PLANE_U];
|
||||||
|
uint8_t const* const vplane = img->planes[VPX_PLANE_V];
|
||||||
|
|
||||||
|
const int ystride = img->stride[VPX_PLANE_Y];
|
||||||
|
const int ustride = img->stride[VPX_PLANE_U];
|
||||||
|
const int vstride = img->stride[VPX_PLANE_V];
|
||||||
|
|
||||||
|
for (unsigned int y = 0; y < height; y += 2)
|
||||||
|
{
|
||||||
|
unsigned int y1 = y + 1;
|
||||||
|
unsigned int wy = width * y;
|
||||||
|
unsigned int wy1 = width * y1;
|
||||||
|
|
||||||
|
for (unsigned int x = 0; x < width; x += 2)
|
||||||
|
{
|
||||||
|
uint8_t u = uplane[ustride * (y >> 1) + (x >> 1)];
|
||||||
|
uint8_t v = vplane[vstride * (y >> 1) + (x >> 1)];
|
||||||
|
|
||||||
|
SetPixel(&Pic[(wy + x) << 2], yplane[ystride * y + x], u, v);
|
||||||
|
SetPixel(&Pic[(wy + x + 1) << 2], yplane[ystride * y + x + 1], u, v);
|
||||||
|
SetPixel(&Pic[(wy1 + x) << 2], yplane[ystride * y1 + x], u, v);
|
||||||
|
SetPixel(&Pic[(wy1 + x + 1) << 2], yplane[ystride * y1 + x + 1], u, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
if (soundtrack > 0)
|
||||||
|
{
|
||||||
|
Mus_Play(nullptr, fileSystem.GetFileFullName(soundtrack, false), false);
|
||||||
|
}
|
||||||
|
animtex.SetSize(AnimTexture::YUV, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Frame(uint64_t clock) override
|
||||||
|
{
|
||||||
|
bool stop = false;
|
||||||
|
if (clock > nextframetime)
|
||||||
|
{
|
||||||
|
nextframetime += nsecsperframe;
|
||||||
|
|
||||||
|
if (!CreateNextFrame())
|
||||||
|
{
|
||||||
|
Printf(PRINT_BOLD, "Failed reading next frame\n");
|
||||||
|
stop = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
animtex.SetFrame(nullptr, Pic.Data());
|
||||||
|
}
|
||||||
|
framenum++;
|
||||||
|
if (framenum >= numframes) stop = true;
|
||||||
|
|
||||||
|
int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum;
|
||||||
|
if (soundframe > lastsoundframe)
|
||||||
|
{
|
||||||
|
if (animSnd && soundtrack == -1) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||||
|
{
|
||||||
|
if (animSnd[i].framenum == soundframe)
|
||||||
|
{
|
||||||
|
int sound = animSnd[i].soundnum;
|
||||||
|
if (sound == -1)
|
||||||
|
soundEngine->StopAllChannels();
|
||||||
|
else if (SoundEnabled())
|
||||||
|
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastsoundframe = soundframe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit, TAG_DONE);
|
||||||
|
return !stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop()
|
||||||
|
{
|
||||||
|
Mus_Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
~VpxPlayer()
|
||||||
|
{
|
||||||
|
vpx_codec_destroy(&codec);
|
||||||
|
animtex.Clean();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct AudioData
|
||||||
|
{
|
||||||
|
int hFx;
|
||||||
|
SmackerAudioInfo inf;
|
||||||
|
|
||||||
|
int16_t samples[6000 * 20]; // must be a multiple of the stream buffer size and larger than the initial chunk of audio
|
||||||
|
|
||||||
|
int nWrite;
|
||||||
|
int nRead;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SmkPlayer : public MoviePlayer
|
||||||
|
{
|
||||||
|
SmackerHandle hSMK{};
|
||||||
|
int numAudioTracks;
|
||||||
|
AudioData adata;
|
||||||
|
uint32_t nWidth, nHeight;
|
||||||
|
uint8_t palette[768];
|
||||||
|
AnimTextures animtex;
|
||||||
|
TArray<uint8_t> pFrame;
|
||||||
|
TArray<uint8_t> audioBuffer;
|
||||||
|
int nFrameRate;
|
||||||
|
int nFrames;
|
||||||
|
bool fullscreenScale;
|
||||||
|
uint64_t nFrameNs;
|
||||||
|
int nFrame = 0;
|
||||||
|
const AnimSound* animSnd;
|
||||||
|
FString filename;
|
||||||
|
SoundStream* stream = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool isvalid() { return hSMK.isValid; }
|
||||||
|
|
||||||
|
static bool StreamCallbackFunc(SoundStream* stream, void* buff, int len, void* userdata)
|
||||||
|
{
|
||||||
|
SmkPlayer* pId = (SmkPlayer*)userdata;
|
||||||
|
memcpy(buff, &pId->adata.samples[pId->adata.nRead], len);
|
||||||
|
pId->adata.nRead += len / 2;
|
||||||
|
if (pId->adata.nRead >= countof(pId->adata.samples)) pId->adata.nRead = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy8bitSamples(unsigned count)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
adata.samples[adata.nWrite] = (audioBuffer[i] - 128) << 8;
|
||||||
|
if (++adata.nWrite >= countof(adata.samples)) adata.nWrite = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy16bitSamples(unsigned count)
|
||||||
|
{
|
||||||
|
auto ptr = (uint16_t*)audioBuffer.Data();
|
||||||
|
for (unsigned i = 0; i < count/2; i++)
|
||||||
|
{
|
||||||
|
adata.samples[adata.nWrite] = *ptr++;
|
||||||
|
if (++adata.nWrite >= countof(adata.samples)) adata.nWrite = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport)
|
||||||
|
{
|
||||||
|
hSMK = Smacker_Open(fn);
|
||||||
|
if (!hSMK.isValid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Smacker_GetFrameSize(hSMK, nWidth, nHeight);
|
||||||
|
pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight));
|
||||||
|
nFrameRate = Smacker_GetFrameRate(hSMK);
|
||||||
|
nFrameNs = 1'000'000'000 / nFrameRate;
|
||||||
|
nFrames = Smacker_GetNumFrames(hSMK);
|
||||||
|
Smacker_GetPalette(hSMK, palette);
|
||||||
|
fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480);
|
||||||
|
|
||||||
|
bool hassound = false;
|
||||||
|
numAudioTracks = Smacker_GetNumAudioTracks(hSMK);
|
||||||
|
if (numAudioTracks)
|
||||||
|
{
|
||||||
|
adata.nWrite = 0;
|
||||||
|
adata.nRead = 0;
|
||||||
|
adata.inf = Smacker_GetAudioTrackDetails(hSMK, 0);
|
||||||
|
if (adata.inf.idealBufferSize > 0)
|
||||||
|
{
|
||||||
|
audioBuffer.Resize(adata.inf.idealBufferSize);
|
||||||
|
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
||||||
|
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
||||||
|
else copy16bitSamples(read);
|
||||||
|
animSnd = nullptr;
|
||||||
|
hassound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hassound)
|
||||||
|
{
|
||||||
|
adata.inf = {};
|
||||||
|
animSnd = ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
animtex.SetSize(AnimTexture::Paletted, nWidth, nHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Frame(uint64_t clock) override
|
||||||
|
{
|
||||||
|
int frame = clock / nFrameNs;
|
||||||
|
|
||||||
|
twod->ClearScreen();
|
||||||
|
if (frame > nFrame)
|
||||||
|
{
|
||||||
|
Smacker_GetPalette(hSMK, palette);
|
||||||
|
Smacker_GetFrame(hSMK, pFrame.Data());
|
||||||
|
animtex.SetFrame(palette, pFrame.Data());
|
||||||
|
if (numAudioTracks)
|
||||||
|
{
|
||||||
|
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
||||||
|
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
||||||
|
else copy16bitSamples(read);
|
||||||
|
if (!stream && read) // the sound may not start in the first frame, but the stream cannot start without any sound data present.
|
||||||
|
stream = S_CreateCustomStream(6000, adata.inf.sampleRate, adata.inf.nChannels, StreamCallbackFunc, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (fullscreenScale)
|
||||||
|
{
|
||||||
|
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawTexture(twod, animtex.GetFrame(), 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
if (frame > nFrame)
|
||||||
|
{
|
||||||
|
nFrame++;
|
||||||
|
Smacker_GetNextFrame(hSMK);
|
||||||
|
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||||
|
{
|
||||||
|
if (animSnd[i].framenum == nFrame)
|
||||||
|
{
|
||||||
|
int sound = animSnd[i].soundnum;
|
||||||
|
if (sound == -1)
|
||||||
|
soundEngine->StopAllChannels();
|
||||||
|
else if (SoundEnabled())
|
||||||
|
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nFrame < nFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
~SmkPlayer()
|
||||||
|
{
|
||||||
|
Smacker_Close(hSMK);
|
||||||
|
if (stream) S_StopCustomStream(stream);
|
||||||
|
soundEngine->StopAllChannels();
|
||||||
|
animtex.Clean();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class DMoviePlayer : public DSkippableScreenJob
|
||||||
|
{
|
||||||
|
MoviePlayer* player;
|
||||||
|
bool started = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DMoviePlayer(MoviePlayer* mp)
|
||||||
|
{
|
||||||
|
player = mp;
|
||||||
|
pausable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Draw(double smoothratio) override
|
||||||
|
{
|
||||||
|
if (!player)
|
||||||
|
{
|
||||||
|
state = stopped;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!started)
|
||||||
|
{
|
||||||
|
started = true;
|
||||||
|
player->Start();
|
||||||
|
}
|
||||||
|
uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate;
|
||||||
|
if (state == running && !player->Frame(clock))
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy() override
|
||||||
|
{
|
||||||
|
if (player)
|
||||||
|
{
|
||||||
|
player->Stop();
|
||||||
|
delete player;
|
||||||
|
}
|
||||||
|
player = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff, FString& error)
|
||||||
|
{
|
||||||
|
FileReader fr;
|
||||||
|
// first try as .ivf - but only if sounds are provided - the decoder is video only.
|
||||||
|
if (ans)
|
||||||
|
{
|
||||||
|
auto fn = StripExtension(filename);
|
||||||
|
DefaultExtension(fn, ".ivf");
|
||||||
|
fr = fileSystem.OpenFileReader(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fr.isOpen()) fr = fileSystem.OpenFileReader(filename);
|
||||||
|
if (!fr.isOpen())
|
||||||
|
{
|
||||||
|
int nLen = strlen(filename);
|
||||||
|
// Strip the drive letter and retry.
|
||||||
|
if (nLen >= 3 && isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
|
||||||
|
{
|
||||||
|
filename += 3;
|
||||||
|
fr = fileSystem.OpenFileReader(filename);
|
||||||
|
}
|
||||||
|
if (!fr.isOpen())
|
||||||
|
{
|
||||||
|
error.Format("%s: Unable to open video\n", filename);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char id[20] = {};
|
||||||
|
|
||||||
|
fr.Read(&id, 20);
|
||||||
|
fr.Seek(-20, FileReader::SeekCur);
|
||||||
|
|
||||||
|
if (!memcmp(id, "LPF ", 4))
|
||||||
|
{
|
||||||
|
auto anm = new AnmPlayer(fr, ans, frameticks, nosoundcutoff);
|
||||||
|
if (!anm->isvalid())
|
||||||
|
{
|
||||||
|
error.Format("%s: invalid ANM file.\n", filename);
|
||||||
|
delete anm;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return anm;
|
||||||
|
}
|
||||||
|
else if (!memcmp(id, "SMK2", 4))
|
||||||
|
{
|
||||||
|
fr.Close();
|
||||||
|
auto anm = new SmkPlayer(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently.
|
||||||
|
if (!anm->isvalid())
|
||||||
|
{
|
||||||
|
error.Format("%s: invalid SMK file.\n", filename);
|
||||||
|
delete anm;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return anm;
|
||||||
|
}
|
||||||
|
else if (!memcmp(id, "Interplay MVE File", 18))
|
||||||
|
{
|
||||||
|
auto anm = new MvePlayer(fr);
|
||||||
|
if (!anm->isvalid())
|
||||||
|
{
|
||||||
|
delete anm;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return anm;
|
||||||
|
}
|
||||||
|
else if (!memcmp(id, "DKIF\0\0 \0VP80", 12))
|
||||||
|
{
|
||||||
|
auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, error);
|
||||||
|
if (!anm->isvalid())
|
||||||
|
{
|
||||||
|
delete anm;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
anm->soundtrack = LookupMusic(filename, true);
|
||||||
|
return anm;
|
||||||
|
}
|
||||||
|
// add more formats here.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error.Format("%s: Unknown video format\n", filename);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff)
|
||||||
|
{
|
||||||
|
if (!filename)
|
||||||
|
{
|
||||||
|
return Create<DBlackScreen>(1);
|
||||||
|
}
|
||||||
|
FString error;
|
||||||
|
auto movie = OpenMovie(filename, ans, frameticks, nosoundcutoff, error);
|
||||||
|
if (!movie)
|
||||||
|
{
|
||||||
|
Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars());
|
||||||
|
return Create<DBlackScreen>(1);
|
||||||
|
}
|
||||||
|
return Create<DMoviePlayer>(movie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,11 +57,29 @@ IMPLEMENT_CLASS(DScreenJob, true, false)
|
||||||
IMPLEMENT_CLASS(DImageScreen, true, false)
|
IMPLEMENT_CLASS(DImageScreen, true, false)
|
||||||
|
|
||||||
|
|
||||||
int DBlackScreen::Frame(uint64_t clock, bool skiprequest)
|
bool DSkippableScreenJob::OnEvent(event_t* evt)
|
||||||
{
|
{
|
||||||
int span = int(clock / 1'000'000);
|
if (evt->type == EV_KeyDown)
|
||||||
|
{
|
||||||
|
state = skipped;
|
||||||
|
Skipped();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBlackScreen::OnTick()
|
||||||
|
{
|
||||||
|
if (cleared)
|
||||||
|
{
|
||||||
|
int span = ticks * 1000 / GameTicRate;
|
||||||
|
if (span > wait) state = finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBlackScreen::Draw(double)
|
||||||
|
{
|
||||||
|
cleared = true;
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
return span < wait ? 1 : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -70,688 +88,24 @@ int DBlackScreen::Frame(uint64_t clock, bool skiprequest)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
int DImageScreen::Frame(uint64_t clock, bool skiprequest)
|
void DImageScreen::OnTick()
|
||||||
{
|
{
|
||||||
if (tilenum > 0)
|
if (cleared)
|
||||||
{
|
{
|
||||||
tex = tileGetTexture(tilenum, true);
|
int span = ticks * 1000 / GameTicRate;
|
||||||
|
if (span > waittime) state = finished;
|
||||||
}
|
}
|
||||||
if (!tex)
|
}
|
||||||
{
|
|
||||||
twod->ClearScreen();
|
|
||||||
return 0;
|
void DImageScreen::Draw(double smoothratio)
|
||||||
}
|
{
|
||||||
int span = int(clock / 1'000'000);
|
if (tilenum > 0) tex = tileGetTexture(tilenum, true);
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE);
|
if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE);
|
||||||
// Only end after having faded out.
|
cleared = true;
|
||||||
return skiprequest ? -1 : span > waittime? 0 : 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class DAnmPlayer : public DScreenJob
|
|
||||||
{
|
|
||||||
// This doesn't need its own class type
|
|
||||||
anim_t anim;
|
|
||||||
TArray<uint8_t> buffer;
|
|
||||||
int numframes = 0;
|
|
||||||
int curframe = 1;
|
|
||||||
int frametime = 0;
|
|
||||||
int nextframetime = 0;
|
|
||||||
AnimTextures animtex;
|
|
||||||
const AnimSound* animSnd;
|
|
||||||
const int* frameTicks;
|
|
||||||
bool nostopsound;
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool isvalid() { return numframes > 0; }
|
|
||||||
|
|
||||||
DAnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff)
|
|
||||||
: animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff)
|
|
||||||
{
|
|
||||||
buffer = fr.ReadPadded(1);
|
|
||||||
fr.Close();
|
|
||||||
|
|
||||||
if (ANIM_LoadAnim(&anim, buffer.Data(), buffer.Size() - 1) < 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
numframes = ANIM_NumFrames(&anim);
|
|
||||||
animtex.SetSize(AnimTexture::Paletted, 320, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
|
||||||
{
|
|
||||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
|
||||||
|
|
||||||
if (currentclock < nextframetime - 1)
|
|
||||||
{
|
|
||||||
twod->ClearScreen();
|
|
||||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
|
||||||
if (skiprequest && !nostopsound) soundEngine->StopAllChannels();
|
|
||||||
return skiprequest? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe));
|
|
||||||
frametime = currentclock;
|
|
||||||
|
|
||||||
twod->ClearScreen();
|
|
||||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
|
||||||
|
|
||||||
int delay = 20;
|
|
||||||
if (frameTicks)
|
|
||||||
{
|
|
||||||
if (curframe == 1) delay = frameTicks[0];
|
|
||||||
else if (curframe < numframes - 2) delay = frameTicks[1];
|
|
||||||
else delay = frameTicks[2];
|
|
||||||
}
|
|
||||||
nextframetime += delay;
|
|
||||||
|
|
||||||
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
|
||||||
{
|
|
||||||
if (animSnd[i].framenum == curframe)
|
|
||||||
{
|
|
||||||
int sound = animSnd[i].soundnum;
|
|
||||||
if (sound == -1)
|
|
||||||
soundEngine->StopAllChannels();
|
|
||||||
else if (SoundEnabled())
|
|
||||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_UI, sound, 1.f, ATTN_NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!skiprequest && !nostopsound && curframe == numframes && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, -1)) return 1;
|
|
||||||
curframe++;
|
|
||||||
if (skiprequest && !nostopsound) soundEngine->StopAllChannels();
|
|
||||||
return skiprequest ? -1 : curframe < numframes? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDestroy() override
|
|
||||||
{
|
|
||||||
buffer.Reset();
|
|
||||||
animtex.Clean();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class DMvePlayer : public DScreenJob
|
|
||||||
{
|
|
||||||
InterplayDecoder decoder;
|
|
||||||
bool failed = false;
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool isvalid() { return !failed; }
|
|
||||||
|
|
||||||
DMvePlayer(FileReader& fr) : decoder(SoundEnabled())
|
|
||||||
{
|
|
||||||
failed = !decoder.Open(fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
|
||||||
{
|
|
||||||
if (failed) return -1;
|
|
||||||
bool playon = decoder.RunFrame(clock);
|
|
||||||
twod->ClearScreen();
|
|
||||||
DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
|
||||||
|
|
||||||
return skiprequest ? -1 : playon ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDestroy() override
|
|
||||||
{
|
|
||||||
decoder.Close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class DVpxPlayer : public DScreenJob
|
|
||||||
{
|
|
||||||
bool failed = false;
|
|
||||||
FileReader fr;
|
|
||||||
AnimTextures animtex;
|
|
||||||
const AnimSound* animSnd;
|
|
||||||
|
|
||||||
unsigned width, height;
|
|
||||||
TArray<uint8_t> Pic;
|
|
||||||
TArray<uint8_t> readBuf;
|
|
||||||
vpx_codec_ctx_t codec{};
|
|
||||||
vpx_codec_iter_t iter = nullptr;
|
|
||||||
|
|
||||||
uint32_t convnumer;
|
|
||||||
uint32_t convdenom;
|
|
||||||
|
|
||||||
uint64_t nsecsperframe;
|
|
||||||
uint64_t nextframetime;
|
|
||||||
|
|
||||||
int decstate = 0;
|
|
||||||
int framenum = 0;
|
|
||||||
int numframes;
|
|
||||||
int lastsoundframe = -1;
|
|
||||||
public:
|
|
||||||
int soundtrack = -1;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool isvalid() { return !failed; }
|
|
||||||
|
|
||||||
DVpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay)
|
|
||||||
{
|
|
||||||
fr = std::move(fr_);
|
|
||||||
animSnd = animSnd_;
|
|
||||||
|
|
||||||
if (!ReadIVFHeader(origframedelay))
|
|
||||||
{
|
|
||||||
// We should never get here, because any file failing this has been eliminated before this constructor got called.
|
|
||||||
Printf(PRINT_BOLD, "Failed reading IVF header\n");
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pic.Resize(width * height * 4);
|
|
||||||
|
|
||||||
|
|
||||||
// Todo: Support VP9 as well?
|
|
||||||
vpx_codec_dec_cfg_t cfg = { 1, width, height };
|
|
||||||
if (vpx_codec_dec_init(&codec, &vpx_codec_vp8_dx_algo, &cfg, 0))
|
|
||||||
{
|
|
||||||
Printf(PRINT_BOLD, "Error initializing VPX codec.\n");
|
|
||||||
failed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
bool ReadIVFHeader(int origframedelay)
|
|
||||||
{
|
|
||||||
// IVF format: http://wiki.multimedia.cx/index.php?title=IVF
|
|
||||||
uint32_t magic; fr.Read(&magic, 4); // do not byte swap!
|
|
||||||
if (magic != MAKE_ID('D', 'K', 'I', 'F')) return false;
|
|
||||||
uint16_t version = fr.ReadUInt16();
|
|
||||||
if (version != 0) return false;
|
|
||||||
uint16_t length = fr.ReadUInt16();
|
|
||||||
if (length != 32) return false;
|
|
||||||
fr.Read(&magic, 4);
|
|
||||||
if (magic != MAKE_ID('V', 'P', '8', '0')) return false;
|
|
||||||
|
|
||||||
width = fr.ReadUInt16();
|
|
||||||
height = fr.ReadUInt16();
|
|
||||||
uint32_t fpsdenominator = fr.ReadUInt32();
|
|
||||||
uint32_t fpsnumerator = fr.ReadUInt32();
|
|
||||||
numframes = fr.ReadUInt32();
|
|
||||||
if (numframes == 0) return false;
|
|
||||||
fr.Seek(4, FileReader::SeekCur);
|
|
||||||
|
|
||||||
if (fpsdenominator > 1000 || fpsnumerator == 0 || fpsdenominator == 0)
|
|
||||||
{
|
|
||||||
// default to 30 fps if the header does not provide useful info.
|
|
||||||
fpsdenominator = 30;
|
|
||||||
fpsnumerator = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
convnumer = 120 * fpsnumerator;
|
|
||||||
convdenom = fpsdenominator * origframedelay;
|
|
||||||
|
|
||||||
nsecsperframe = int64_t(fpsnumerator) * 1'000'000'000 / fpsdenominator;
|
|
||||||
nextframetime = 0;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
bool ReadFrame()
|
|
||||||
{
|
|
||||||
int corrupted = 0;
|
|
||||||
int framesize = fr.ReadInt32();
|
|
||||||
fr.Seek(8, FileReader::SeekCur);
|
|
||||||
if (framesize == 0) return false;
|
|
||||||
|
|
||||||
readBuf.Resize(framesize);
|
|
||||||
if (fr.Read(readBuf.Data(), framesize) != framesize) return false;
|
|
||||||
if (vpx_codec_decode(&codec, readBuf.Data(), readBuf.Size(), NULL, 0) != VPX_CODEC_OK) return false;
|
|
||||||
if (vpx_codec_control(&codec, VP8D_GET_FRAME_CORRUPTED, &corrupted) != VPX_CODEC_OK) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
vpx_image_t *GetFrameData()
|
|
||||||
{
|
|
||||||
vpx_image_t *img;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (decstate == 0) // first time / begin
|
|
||||||
{
|
|
||||||
if (!ReadFrame()) return nullptr;
|
|
||||||
decstate = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
img = vpx_codec_get_frame(&codec, &iter);
|
|
||||||
if (img == nullptr)
|
|
||||||
{
|
|
||||||
decstate = 0;
|
|
||||||
iter = nullptr;
|
|
||||||
}
|
|
||||||
} while (img == nullptr);
|
|
||||||
|
|
||||||
return img->d_w == width && img->d_h == height? img : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void SetPixel(uint8_t* dest, uint8_t y, uint8_t u, uint8_t v)
|
|
||||||
{
|
|
||||||
dest[0] = y;
|
|
||||||
dest[1] = u;
|
|
||||||
dest[2] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateNextFrame()
|
|
||||||
{
|
|
||||||
auto img = GetFrameData();
|
|
||||||
if (!img) return false;
|
|
||||||
uint8_t const* const yplane = img->planes[VPX_PLANE_Y];
|
|
||||||
uint8_t const* const uplane = img->planes[VPX_PLANE_U];
|
|
||||||
uint8_t const* const vplane = img->planes[VPX_PLANE_V];
|
|
||||||
|
|
||||||
const int ystride = img->stride[VPX_PLANE_Y];
|
|
||||||
const int ustride = img->stride[VPX_PLANE_U];
|
|
||||||
const int vstride = img->stride[VPX_PLANE_V];
|
|
||||||
|
|
||||||
for (unsigned int y = 0; y < height; y += 2)
|
|
||||||
{
|
|
||||||
unsigned int y1 = y + 1;
|
|
||||||
unsigned int wy = width * y;
|
|
||||||
unsigned int wy1 = width * y1;
|
|
||||||
|
|
||||||
for (unsigned int x = 0; x < width; x += 2)
|
|
||||||
{
|
|
||||||
uint8_t u = uplane[ustride * (y >> 1) + (x >> 1)];
|
|
||||||
uint8_t v = vplane[vstride * (y >> 1) + (x >> 1)];
|
|
||||||
|
|
||||||
SetPixel(&Pic[(wy + x) << 2], yplane[ystride * y + x], u, v);
|
|
||||||
SetPixel(&Pic[(wy + x + 1) << 2], yplane[ystride * y + x + 1], u, v);
|
|
||||||
SetPixel(&Pic[(wy1 + x) << 2], yplane[ystride * y1 + x], u, v);
|
|
||||||
SetPixel(&Pic[(wy1 + x + 1) << 2], yplane[ystride * y1 + x + 1], u, v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
|
||||||
{
|
|
||||||
if (clock == 0)
|
|
||||||
{
|
|
||||||
if (soundtrack > 0)
|
|
||||||
{
|
|
||||||
Mus_Play(nullptr, fileSystem.GetFileFullName(soundtrack, false), false);
|
|
||||||
}
|
|
||||||
animtex.SetSize(AnimTexture::YUV, width, height);
|
|
||||||
}
|
|
||||||
bool stop = false;
|
|
||||||
if (clock > nextframetime)
|
|
||||||
{
|
|
||||||
nextframetime += nsecsperframe;
|
|
||||||
|
|
||||||
if (!CreateNextFrame())
|
|
||||||
{
|
|
||||||
Printf(PRINT_BOLD, "Failed reading next frame\n");
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
animtex.SetFrame(nullptr, Pic.Data());
|
|
||||||
}
|
|
||||||
framenum++;
|
|
||||||
if (framenum >= numframes) stop = true;
|
|
||||||
|
|
||||||
int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum;
|
|
||||||
if (soundframe > lastsoundframe)
|
|
||||||
{
|
|
||||||
if (animSnd && soundtrack == -1) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
|
||||||
{
|
|
||||||
if (animSnd[i].framenum == soundframe)
|
|
||||||
{
|
|
||||||
int sound = animSnd[i].soundnum;
|
|
||||||
if (sound == -1)
|
|
||||||
soundEngine->StopAllChannels();
|
|
||||||
else if (SoundEnabled())
|
|
||||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_UI, sound, 1.f, ATTN_NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastsoundframe = soundframe;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit, TAG_DONE);
|
|
||||||
if (stop || skiprequest) Mus_Stop();
|
|
||||||
if (stop) return 0;
|
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDestroy() override
|
|
||||||
{
|
|
||||||
vpx_codec_destroy(&codec);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
struct AudioData
|
|
||||||
{
|
|
||||||
int hFx;
|
|
||||||
SmackerAudioInfo inf;
|
|
||||||
|
|
||||||
int16_t samples[6000 * 20]; // must be a multiple of the stream buffer size and larger than the initial chunk of audio
|
|
||||||
|
|
||||||
int nWrite;
|
|
||||||
int nRead;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DSmkPlayer : public DScreenJob
|
|
||||||
{
|
|
||||||
SmackerHandle hSMK{};
|
|
||||||
int numAudioTracks;
|
|
||||||
AudioData adata;
|
|
||||||
uint32_t nWidth, nHeight;
|
|
||||||
uint8_t palette[768];
|
|
||||||
AnimTextures animtex;
|
|
||||||
TArray<uint8_t> pFrame;
|
|
||||||
TArray<uint8_t> audioBuffer;
|
|
||||||
int nFrameRate;
|
|
||||||
int nFrames;
|
|
||||||
bool fullscreenScale;
|
|
||||||
uint64_t nFrameNs;
|
|
||||||
int nFrame = 0;
|
|
||||||
const AnimSound* animSnd;
|
|
||||||
FString filename;
|
|
||||||
SoundStream* stream = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool isvalid() { return hSMK.isValid; }
|
|
||||||
|
|
||||||
static bool StreamCallbackFunc(SoundStream* stream, void* buff, int len, void* userdata)
|
|
||||||
{
|
|
||||||
DSmkPlayer* pId = (DSmkPlayer*)userdata;
|
|
||||||
memcpy(buff, &pId->adata.samples[pId->adata.nRead], len);
|
|
||||||
pId->adata.nRead += len / 2;
|
|
||||||
if (pId->adata.nRead >= countof(pId->adata.samples)) pId->adata.nRead = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy8bitSamples(unsigned count)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
adata.samples[adata.nWrite] = (audioBuffer[i] - 128) << 8;
|
|
||||||
if (++adata.nWrite >= countof(adata.samples)) adata.nWrite = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void copy16bitSamples(unsigned count)
|
|
||||||
{
|
|
||||||
auto ptr = (uint16_t*)audioBuffer.Data();
|
|
||||||
for (unsigned i = 0; i < count/2; i++)
|
|
||||||
{
|
|
||||||
adata.samples[adata.nWrite] = *ptr++;
|
|
||||||
if (++adata.nWrite >= countof(adata.samples)) adata.nWrite = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DSmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport)
|
|
||||||
{
|
|
||||||
hSMK = Smacker_Open(fn);
|
|
||||||
if (!hSMK.isValid)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Smacker_GetFrameSize(hSMK, nWidth, nHeight);
|
|
||||||
pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight));
|
|
||||||
nFrameRate = Smacker_GetFrameRate(hSMK);
|
|
||||||
nFrameNs = 1'000'000'000 / nFrameRate;
|
|
||||||
nFrames = Smacker_GetNumFrames(hSMK);
|
|
||||||
Smacker_GetPalette(hSMK, palette);
|
|
||||||
fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480);
|
|
||||||
|
|
||||||
bool hassound = false;
|
|
||||||
numAudioTracks = Smacker_GetNumAudioTracks(hSMK);
|
|
||||||
if (numAudioTracks)
|
|
||||||
{
|
|
||||||
adata.nWrite = 0;
|
|
||||||
adata.nRead = 0;
|
|
||||||
adata.inf = Smacker_GetAudioTrackDetails(hSMK, 0);
|
|
||||||
if (adata.inf.idealBufferSize > 0)
|
|
||||||
{
|
|
||||||
audioBuffer.Resize(adata.inf.idealBufferSize);
|
|
||||||
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
|
||||||
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
|
||||||
else copy16bitSamples(read);
|
|
||||||
animSnd = nullptr;
|
|
||||||
hassound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hassound)
|
|
||||||
{
|
|
||||||
adata.inf = {};
|
|
||||||
animSnd = ans;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
|
||||||
{
|
|
||||||
int frame = clock / nFrameNs;
|
|
||||||
|
|
||||||
if (clock == 0)
|
|
||||||
{
|
|
||||||
animtex.SetSize(AnimTexture::Paletted, nWidth, nHeight);
|
|
||||||
}
|
|
||||||
twod->ClearScreen();
|
|
||||||
if (frame > nFrame)
|
|
||||||
{
|
|
||||||
Smacker_GetPalette(hSMK, palette);
|
|
||||||
Smacker_GetFrame(hSMK, pFrame.Data());
|
|
||||||
animtex.SetFrame(palette, pFrame.Data());
|
|
||||||
if (numAudioTracks)
|
|
||||||
{
|
|
||||||
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
|
||||||
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
|
||||||
else copy16bitSamples(read);
|
|
||||||
if (!stream && read) // the sound may not start in the first frame, but the stream cannot start without any sound data present.
|
|
||||||
stream = S_CreateCustomStream(6000, adata.inf.sampleRate, adata.inf.nChannels, StreamCallbackFunc, this);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (fullscreenScale)
|
|
||||||
{
|
|
||||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DrawTexture(twod, animtex.GetFrame(), 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, TAG_DONE);
|
|
||||||
}
|
|
||||||
if (frame > nFrame)
|
|
||||||
{
|
|
||||||
nFrame++;
|
|
||||||
Smacker_GetNextFrame(hSMK);
|
|
||||||
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
|
||||||
{
|
|
||||||
if (animSnd[i].framenum == nFrame)
|
|
||||||
{
|
|
||||||
int sound = animSnd[i].soundnum;
|
|
||||||
if (sound == -1)
|
|
||||||
soundEngine->StopAllChannels();
|
|
||||||
else if (SoundEnabled())
|
|
||||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_UI, sound, 1.f, ATTN_NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return skiprequest ? -1 : nFrame < nFrames ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnDestroy() override
|
|
||||||
{
|
|
||||||
Smacker_Close(hSMK);
|
|
||||||
if (stream) S_StopCustomStream(stream);
|
|
||||||
soundEngine->StopAllChannels();
|
|
||||||
animtex.Clean();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff)
|
|
||||||
{
|
|
||||||
auto nothing = []()->DScreenJob* { return Create<DScreenJob>(); };
|
|
||||||
if (!filename)
|
|
||||||
{
|
|
||||||
return nothing();
|
|
||||||
}
|
|
||||||
FileReader fr;
|
|
||||||
// first try as .ivf - but only if sounds are provided - the decoder is video only.
|
|
||||||
if (ans)
|
|
||||||
{
|
|
||||||
auto fn = StripExtension(filename);
|
|
||||||
DefaultExtension(fn, ".ivf");
|
|
||||||
fr = fileSystem.OpenFileReader(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fr.isOpen()) fr = fileSystem.OpenFileReader(filename);
|
|
||||||
if (!fr.isOpen())
|
|
||||||
{
|
|
||||||
int nLen = strlen(filename);
|
|
||||||
// Strip the drive letter and retry.
|
|
||||||
if (nLen >= 3 && isalpha(filename[0]) && filename[1] == ':' && filename[2] == '/')
|
|
||||||
{
|
|
||||||
filename += 3;
|
|
||||||
fr = fileSystem.OpenFileReader(filename);
|
|
||||||
}
|
|
||||||
if (!fr.isOpen())
|
|
||||||
{
|
|
||||||
Printf(PRINT_BOLD, "%s: Unable to open video\n", filename);
|
|
||||||
return nothing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
char id[20] = {};
|
|
||||||
|
|
||||||
fr.Read(&id, 20);
|
|
||||||
fr.Seek(-20, FileReader::SeekCur);
|
|
||||||
|
|
||||||
if (!memcmp(id, "LPF ", 4))
|
|
||||||
{
|
|
||||||
auto anm = Create<DAnmPlayer>(fr, ans, frameticks, nosoundcutoff);
|
|
||||||
if (!anm->isvalid())
|
|
||||||
{
|
|
||||||
Printf(PRINT_BOLD, "%s: invalid ANM file.\n", filename);
|
|
||||||
anm->Destroy();
|
|
||||||
return nothing();
|
|
||||||
}
|
|
||||||
return anm;
|
|
||||||
}
|
|
||||||
else if (!memcmp(id, "SMK2", 4))
|
|
||||||
{
|
|
||||||
fr.Close();
|
|
||||||
auto anm = Create<DSmkPlayer>(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently.
|
|
||||||
if (!anm->isvalid())
|
|
||||||
{
|
|
||||||
Printf(PRINT_BOLD, "%s: invalid SMK file.\n", filename);
|
|
||||||
anm->Destroy();
|
|
||||||
return nothing();
|
|
||||||
}
|
|
||||||
return anm;
|
|
||||||
}
|
|
||||||
else if (!memcmp(id, "Interplay MVE File", 18))
|
|
||||||
{
|
|
||||||
auto anm = Create<DMvePlayer>(fr);
|
|
||||||
if (!anm->isvalid())
|
|
||||||
{
|
|
||||||
anm->Destroy();
|
|
||||||
return nothing();
|
|
||||||
}
|
|
||||||
return anm;
|
|
||||||
}
|
|
||||||
else if (!memcmp(id, "DKIF\0\0 \0VP80", 12))
|
|
||||||
{
|
|
||||||
auto anm = Create<DVpxPlayer>(fr, ans, frameticks? frameticks[1] : 0 );
|
|
||||||
if (!anm->isvalid())
|
|
||||||
{
|
|
||||||
anm->Destroy();
|
|
||||||
return nothing();
|
|
||||||
}
|
|
||||||
anm->soundtrack = LookupMusic(filename, true);
|
|
||||||
return anm;
|
|
||||||
}
|
|
||||||
// add more formats here.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Printf(PRINT_BOLD, "%s: Unknown video format\n", filename);
|
|
||||||
}
|
|
||||||
return nothing();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -771,11 +125,10 @@ class ScreenJobRunner
|
||||||
int index = -1;
|
int index = -1;
|
||||||
float screenfade;
|
float screenfade;
|
||||||
bool clearbefore;
|
bool clearbefore;
|
||||||
int64_t startTime = -1;
|
|
||||||
int64_t lastTime = -1;
|
|
||||||
int actionState;
|
int actionState;
|
||||||
int terminateState;
|
int terminateState;
|
||||||
uint64_t clock = 0;
|
int fadeticks = 0;
|
||||||
|
int last_paused_tic = -1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_)
|
ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_)
|
||||||
|
@ -820,75 +173,92 @@ public:
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
actionState = clearbefore ? State_Clear : State_Run;
|
actionState = clearbefore ? State_Clear : State_Run;
|
||||||
if (index < jobs.Size()) screenfade = jobs[index].job->fadestyle & DScreenJob::fadein ? 0.f : 1.f;
|
if (index < jobs.Size())
|
||||||
lastTime= startTime = -1;
|
{
|
||||||
clock = 0;
|
jobs[index].job->fadestate = !paused && jobs[index].job->fadestyle & DScreenJob::fadein? DScreenJob::fadein : DScreenJob::visible;
|
||||||
|
jobs[index].job->Start();
|
||||||
|
}
|
||||||
inputState.ClearAllInput();
|
inputState.ClearAllInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
int DisplayFrame()
|
int DisplayFrame(double smoothratio)
|
||||||
{
|
{
|
||||||
auto& job = jobs[index];
|
auto& job = jobs[index];
|
||||||
auto now = I_GetTimeNS();
|
auto now = I_GetTimeNS();
|
||||||
bool processed = job.job->ProcessInput();
|
bool processed = job.job->ProcessInput();
|
||||||
if (startTime == -1)
|
|
||||||
{
|
|
||||||
lastTime = startTime = now;
|
|
||||||
}
|
|
||||||
else if (!M_Active())
|
|
||||||
{
|
|
||||||
clock += now - lastTime;
|
|
||||||
if (clock == 0) clock = 1;
|
|
||||||
}
|
|
||||||
bool skiprequest = clock > 100'000'000 && inputState.CheckAllInput() && !processed && job.job->fadestate != DScreenJob::fadeout;
|
|
||||||
lastTime = now;
|
|
||||||
|
|
||||||
if (screenfade < 1.f && !M_Active())
|
if (job.job->fadestate == DScreenJob::fadein)
|
||||||
{
|
{
|
||||||
float ms = (clock / 1'000'000) / job.job->fadetime;
|
double ms = (job.job->ticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime;
|
||||||
screenfade = clamp(ms, 0.f, 1.f);
|
float screenfade = (float)clamp(ms, 0., 1.);
|
||||||
twod->SetScreenFade(screenfade);
|
twod->SetScreenFade(screenfade);
|
||||||
if (job.job->fadestate != DScreenJob::fadeout)
|
if (screenfade == 1.f) job.job->fadestate = DScreenJob::visible;
|
||||||
job.job->fadestate = DScreenJob::fadein;
|
|
||||||
}
|
}
|
||||||
else
|
int state = job.job->DrawFrame(smoothratio);
|
||||||
{
|
twod->SetScreenFade(1.f);
|
||||||
job.job->fadestate = DScreenJob::visible;
|
|
||||||
screenfade = 1.f;
|
|
||||||
}
|
|
||||||
job.job->SetClock(clock);
|
|
||||||
int state = job.job->Frame(clock, skiprequest);
|
|
||||||
clock = job.job->GetClock();
|
|
||||||
if (clock == 0) clock = 1;
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FadeoutFrame()
|
int FadeoutFrame(double smoothratio)
|
||||||
{
|
{
|
||||||
auto now = I_GetTimeNS();
|
auto& job = jobs[index];
|
||||||
|
double ms = (fadeticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime;
|
||||||
if (startTime == -1)
|
float screenfade = 1.f - (float)clamp(ms, 0., 1.);
|
||||||
{
|
twod->SetScreenFade(screenfade);
|
||||||
lastTime = startTime = now;
|
job.job->DrawFrame(1.);
|
||||||
}
|
return (screenfade > 0.f);
|
||||||
else if (!M_Active())
|
|
||||||
{
|
|
||||||
clock += now - lastTime;
|
|
||||||
if (clock == 0) clock = 1;
|
|
||||||
}
|
|
||||||
lastTime = now;
|
|
||||||
|
|
||||||
float ms = (clock / 1'000'000) / jobs[index].job->fadetime;
|
|
||||||
float screenfade2 = clamp(screenfade - ms, 0.f, 1.f);
|
|
||||||
if (!M_Active()) twod->SetScreenFade(screenfade2);
|
|
||||||
if (screenfade2 <= 0.f)
|
|
||||||
{
|
|
||||||
twod->Unlock(); // must unlock before displaying.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OnEvent(event_t* ev)
|
||||||
|
{
|
||||||
|
if (paused || index >= jobs.Size()) return false;
|
||||||
|
|
||||||
|
if (ev->type == EV_KeyDown)
|
||||||
|
{
|
||||||
|
// We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
|
||||||
|
auto binding = Bindings.GetBinding(ev->data1);
|
||||||
|
if (binding.CompareNoCase("toggleconsole") == 0)
|
||||||
|
{
|
||||||
|
C_ToggleConsole();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jobs[index].job->state != DScreenJob::running) return false;
|
||||||
|
return jobs[index].job->OnEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFinished()
|
||||||
|
{
|
||||||
|
if (completion) completion(false);
|
||||||
|
completion = nullptr; // only finish once.
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick()
|
||||||
|
{
|
||||||
|
if (paused) return;
|
||||||
|
if (index >= jobs.Size())
|
||||||
|
{
|
||||||
|
//DeleteJobs();
|
||||||
|
//twod->SetScreenFade(1);
|
||||||
|
//twod->ClearScreen(); // This must not leave the 2d buffer empty.
|
||||||
|
//if (gamestate == GS_INTRO) OnFinished();
|
||||||
|
//else Net_WriteByte(DEM_ENDSCREENJOB); // intermissions must be terminated synchronously.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (jobs[index].job->state == DScreenJob::running)
|
||||||
|
{
|
||||||
|
jobs[index].job->ticks++;
|
||||||
|
jobs[index].job->OnTick();
|
||||||
|
}
|
||||||
|
else if (jobs[index].job->state == DScreenJob::stopping)
|
||||||
|
{
|
||||||
|
fadeticks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool RunFrame()
|
bool RunFrame()
|
||||||
{
|
{
|
||||||
if (index >= jobs.Size())
|
if (index >= jobs.Size())
|
||||||
|
@ -899,6 +269,13 @@ public:
|
||||||
if (completion) completion(false);
|
if (completion) completion(false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure that we won't go back in time if the menu is dismissed without advancing our ticker
|
||||||
|
bool menuon = paused;
|
||||||
|
if (menuon) last_paused_tic = jobs[index].job->ticks;
|
||||||
|
else if (last_paused_tic == jobs[index].job->ticks) menuon = true;
|
||||||
|
double smoothratio = menuon ? 1. : I_GetTimeFrac();
|
||||||
|
|
||||||
if (actionState == State_Clear)
|
if (actionState == State_Clear)
|
||||||
{
|
{
|
||||||
actionState = State_Run;
|
actionState = State_Run;
|
||||||
|
@ -906,18 +283,16 @@ public:
|
||||||
}
|
}
|
||||||
else if (actionState == State_Run)
|
else if (actionState == State_Run)
|
||||||
{
|
{
|
||||||
terminateState = DisplayFrame();
|
terminateState = DisplayFrame(smoothratio);
|
||||||
if (terminateState < 1)
|
if (terminateState < 1)
|
||||||
{
|
{
|
||||||
// Must lock before displaying.
|
// Must lock before displaying.
|
||||||
if (jobs[index].job->fadestyle & DScreenJob::fadeout)
|
if (jobs[index].job->fadestyle & DScreenJob::fadeout)
|
||||||
{
|
{
|
||||||
twod->Lock();
|
|
||||||
startTime = -1;
|
|
||||||
clock = 0;
|
|
||||||
jobs[index].job->fadestate = DScreenJob::fadeout;
|
jobs[index].job->fadestate = DScreenJob::fadeout;
|
||||||
gamestate = GS_INTRO; // block menu and console during fadeout - this can cause timing problems.
|
jobs[index].job->state = DScreenJob::stopping;
|
||||||
actionState = State_Fadeout;
|
actionState = State_Fadeout;
|
||||||
|
fadeticks = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -927,9 +302,10 @@ public:
|
||||||
}
|
}
|
||||||
else if (actionState == State_Fadeout)
|
else if (actionState == State_Fadeout)
|
||||||
{
|
{
|
||||||
int ended = FadeoutFrame();
|
int ended = FadeoutFrame(smoothratio);
|
||||||
if (ended < 1)
|
if (ended < 1)
|
||||||
{
|
{
|
||||||
|
jobs[index].job->state = DScreenJob::stopped;
|
||||||
AdvanceJob(terminateState < 0);
|
AdvanceJob(terminateState < 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -964,7 +340,25 @@ void DeleteScreenJob()
|
||||||
twod->SetScreenFade(1);
|
twod->SetScreenFade(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunScreenJobFrame()
|
void EndScreenJob()
|
||||||
|
{
|
||||||
|
if (runner) runner->OnFinished();
|
||||||
|
DeleteScreenJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ScreenJobResponder(event_t* ev)
|
||||||
|
{
|
||||||
|
if (runner) return runner->OnEvent(ev);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenJobTick()
|
||||||
|
{
|
||||||
|
if (runner) runner->OnTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenJobDraw()
|
||||||
{
|
{
|
||||||
// we cannot recover from this because we have no completion callback to call.
|
// we cannot recover from this because we have no completion callback to call.
|
||||||
if (!runner)
|
if (!runner)
|
||||||
|
@ -972,7 +366,7 @@ void RunScreenJobFrame()
|
||||||
// We can get here before a gameaction has been processed. In that case just draw a black screen and wait.
|
// We can get here before a gameaction has been processed. In that case just draw a black screen and wait.
|
||||||
if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job");
|
if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job");
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
auto res = runner->RunFrame();
|
auto res = runner->RunFrame();
|
||||||
if (!res)
|
if (!res)
|
||||||
|
@ -980,5 +374,6 @@ void RunScreenJobFrame()
|
||||||
assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing);
|
assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing);
|
||||||
DeleteScreenJob();
|
DeleteScreenJob();
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "dobject.h"
|
#include "dobject.h"
|
||||||
#include "v_2ddrawer.h"
|
#include "v_2ddrawer.h"
|
||||||
|
#include "d_eventbase.h"
|
||||||
|
|
||||||
using CompletionFunc = std::function<void(bool)>;
|
using CompletionFunc = std::function<void(bool)>;
|
||||||
struct JobDesc;
|
struct JobDesc;
|
||||||
|
@ -10,14 +11,25 @@ class ScreenJobRunner;
|
||||||
class DScreenJob : public DObject
|
class DScreenJob : public DObject
|
||||||
{
|
{
|
||||||
DECLARE_CLASS(DScreenJob, DObject)
|
DECLARE_CLASS(DScreenJob, DObject)
|
||||||
int64_t now;
|
|
||||||
const int fadestyle;
|
const int fadestyle;
|
||||||
const float fadetime; // in milliseconds
|
const float fadetime; // in milliseconds
|
||||||
int fadestate = fadein;
|
int fadestate = fadein;
|
||||||
|
|
||||||
friend class ScreenJobRunner;
|
friend class ScreenJobRunner;
|
||||||
|
protected:
|
||||||
|
int ticks = 0;
|
||||||
|
int state = running;
|
||||||
|
bool pausable = true;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
running = 1, // normal operation
|
||||||
|
skipped = 2, // finished by user skipping
|
||||||
|
finished = 3, // finished by completing its sequence
|
||||||
|
stopping = 4, // running ending animations / fadeout, etc. Will not accept more input.
|
||||||
|
stopped = 5, // we're done here.
|
||||||
|
};
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
visible = 0,
|
visible = 0,
|
||||||
|
@ -32,17 +44,20 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetClock(int64_t nsnow)
|
virtual void Start() {}
|
||||||
|
virtual bool OnEvent(event_t* evt) { return false; }
|
||||||
|
virtual void OnTick() { /*state = finished;*/ }
|
||||||
|
virtual void Draw(double smoothratio) {}
|
||||||
|
|
||||||
|
int DrawFrame(double smoothratio)
|
||||||
{
|
{
|
||||||
now = nsnow;
|
if (state != running) smoothratio = 1; // this is necessary because the ticker won't be incremented anymore to avoid having a negative time span.
|
||||||
|
Draw(smoothratio);
|
||||||
|
if (state == skipped) return -1;
|
||||||
|
if (state == finished) return 0;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t GetClock() const
|
|
||||||
{
|
|
||||||
return now;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int Frame(uint64_t clock, bool skiprequest) { return 0; }
|
|
||||||
int GetFadeState() const { return fadestate; }
|
int GetFadeState() const { return fadestate; }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -53,13 +68,31 @@ public:
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class DSkippableScreenJob : public DScreenJob
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
DSkippableScreenJob(int fade = 0, float fadet = 250.f) : DScreenJob(fade, fadet)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool OnEvent(event_t* evt) override;
|
||||||
|
virtual void Skipped() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DBlackScreen : public DScreenJob
|
class DBlackScreen : public DScreenJob
|
||||||
{
|
{
|
||||||
int wait;
|
int wait;
|
||||||
|
bool cleared = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DBlackScreen(int w) : wait(w) {}
|
DBlackScreen(int w) : wait(w) {}
|
||||||
int Frame(uint64_t clock, bool skiprequest) override;
|
void OnTick() override;
|
||||||
|
void Draw(double smooth) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -68,29 +101,31 @@ public:
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DImageScreen : public DScreenJob
|
class DImageScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
DECLARE_CLASS(DImageScreen, DScreenJob)
|
DECLARE_CLASS(DImageScreen, DScreenJob)
|
||||||
|
|
||||||
int tilenum = -1;
|
int tilenum = -1;
|
||||||
int trans;
|
int trans;
|
||||||
int waittime; // in ms.
|
int waittime; // in ms.
|
||||||
|
bool cleared = false;
|
||||||
FGameTexture* tex = nullptr;
|
FGameTexture* tex = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DScreenJob(fade), waittime(wait)
|
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
|
||||||
{
|
{
|
||||||
tex = tile;
|
tex = tile;
|
||||||
trans = translation;
|
trans = translation;
|
||||||
}
|
}
|
||||||
|
|
||||||
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DScreenJob(fade), waittime(wait)
|
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
|
||||||
{
|
{
|
||||||
tilenum = tile;
|
tilenum = tile;
|
||||||
trans = translation;
|
trans = translation;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override;
|
void OnTick() override;
|
||||||
|
void Draw(double smooth) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,8 +140,11 @@ struct JobDesc
|
||||||
|
|
||||||
|
|
||||||
void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true, bool blockingui = false);
|
void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true, bool blockingui = false);
|
||||||
|
void EndScreenJob();
|
||||||
void DeleteScreenJob();
|
void DeleteScreenJob();
|
||||||
void RunScreenJobFrame();
|
bool ScreenJobResponder(event_t* ev);
|
||||||
|
void ScreenJobTick();
|
||||||
|
bool ScreenJobDraw();
|
||||||
|
|
||||||
struct AnimSound
|
struct AnimSound
|
||||||
{
|
{
|
||||||
|
|
|
@ -821,7 +821,7 @@ void tileUpdateAnimations()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MAXTILES; i++)
|
for (int i = 0; i < MAXTILES; i++)
|
||||||
{
|
{
|
||||||
if (picanm[i].sf & PICANM_ANIMTYPE_MASK)
|
if (TileFiles.tiledata[i].picanm.sf & PICANM_ANIMTYPE_MASK)
|
||||||
{
|
{
|
||||||
int j = i + animateoffs(i, 0);
|
int j = i + animateoffs(i, 0);
|
||||||
|
|
||||||
|
|
|
@ -411,25 +411,29 @@ extern PicAnm picanm;
|
||||||
|
|
||||||
inline int tileWidth(int num)
|
inline int tileWidth(int num)
|
||||||
{
|
{
|
||||||
assert(num < MAXTILES);
|
assert((unsigned)num < MAXTILES);
|
||||||
|
if ((unsigned)num >= MAXTILES) return 1;
|
||||||
return (int)TileFiles.tiledata[num].texture->GetDisplayWidth();
|
return (int)TileFiles.tiledata[num].texture->GetDisplayWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tileHeight(int num)
|
inline int tileHeight(int num)
|
||||||
{
|
{
|
||||||
assert(num < MAXTILES);
|
assert((unsigned)num < MAXTILES);
|
||||||
|
if ((unsigned)num >= MAXTILES) return 1;
|
||||||
return (int)TileFiles.tiledata[num].texture->GetDisplayHeight();
|
return (int)TileFiles.tiledata[num].texture->GetDisplayHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tileLeftOffset(int num)
|
inline int tileLeftOffset(int num)
|
||||||
{
|
{
|
||||||
assert(num < MAXTILES);
|
assert((unsigned)num < MAXTILES);
|
||||||
|
if ((unsigned)num >= MAXTILES) return 0;
|
||||||
return (int)TileFiles.tiledata[num].texture->GetDisplayLeftOffset();
|
return (int)TileFiles.tiledata[num].texture->GetDisplayLeftOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int tileTopOffset(int num)
|
inline int tileTopOffset(int num)
|
||||||
{
|
{
|
||||||
assert(num < MAXTILES);
|
assert((unsigned)num < MAXTILES);
|
||||||
|
if ((unsigned)num >= MAXTILES) return 0;
|
||||||
return (int)TileFiles.tiledata[num].texture->GetDisplayTopOffset();
|
return (int)TileFiles.tiledata[num].texture->GetDisplayTopOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,11 +448,11 @@ int32_t animateoffs(int const tilenum, int fakevar);
|
||||||
|
|
||||||
inline FGameTexture* tileGetTexture(int tile, bool animate = false)
|
inline FGameTexture* tileGetTexture(int tile, bool animate = false)
|
||||||
{
|
{
|
||||||
assert(tile < MAXTILES);
|
assert((unsigned)tile < MAXTILES);
|
||||||
if (tile < 0 || tile >= MAXTILES) return nullptr;
|
if (tile < 0 || tile >= MAXTILES) return nullptr;
|
||||||
if (animate)
|
if (animate)
|
||||||
{
|
{
|
||||||
if (picanm[tile].sf & PICANM_ANIMTYPE_MASK)
|
if (TileFiles.tiledata[tile].picanm.sf & PICANM_ANIMTYPE_MASK)
|
||||||
tile += animateoffs(tile, 0);
|
tile += animateoffs(tile, 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -70,16 +70,16 @@ struct THINGINFO
|
||||||
{
|
{
|
||||||
short startHealth;
|
short startHealth;
|
||||||
short mass;
|
short mass;
|
||||||
unsigned char clipdist;
|
uint8_t clipdist;
|
||||||
short flags;
|
short flags;
|
||||||
int elastic; // elasticity
|
int elastic; // elasticity
|
||||||
int dmgResist; // damage resistance
|
int dmgResist; // damage resistance
|
||||||
short cstat;
|
short cstat;
|
||||||
short picnum;
|
short picnum;
|
||||||
char shade;
|
int8_t shade;
|
||||||
unsigned char pal;
|
uint8_t pal;
|
||||||
unsigned char xrepeat; // xrepeat
|
uint8_t xrepeat; // xrepeat
|
||||||
unsigned char yrepeat; // yrepeat
|
uint8_t yrepeat; // yrepeat
|
||||||
int dmgControl[kDamageMax]; // damage
|
int dmgControl[kDamageMax]; // damage
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,23 +87,23 @@ struct AMMOITEMDATA
|
||||||
{
|
{
|
||||||
short cstat;
|
short cstat;
|
||||||
short picnum;
|
short picnum;
|
||||||
char shade;
|
int8_t shade;
|
||||||
char pal;
|
uint8_t pal;
|
||||||
unsigned char xrepeat;
|
uint8_t xrepeat;
|
||||||
unsigned char yrepeat;
|
uint8_t yrepeat;
|
||||||
short count;
|
short count;
|
||||||
unsigned char type;
|
uint8_t type;
|
||||||
unsigned char weaponType;
|
uint8_t weaponType;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WEAPONITEMDATA
|
struct WEAPONITEMDATA
|
||||||
{
|
{
|
||||||
short cstat;
|
short cstat;
|
||||||
short picnum;
|
short picnum;
|
||||||
char shade;
|
int8_t shade;
|
||||||
char pal;
|
uint8_t pal;
|
||||||
unsigned char xrepeat;
|
uint8_t xrepeat;
|
||||||
unsigned char yrepeat;
|
uint8_t yrepeat;
|
||||||
short type;
|
short type;
|
||||||
short ammoType;
|
short ammoType;
|
||||||
short count;
|
short count;
|
||||||
|
@ -113,10 +113,10 @@ struct ITEMDATA
|
||||||
{
|
{
|
||||||
short cstat;
|
short cstat;
|
||||||
short picnum;
|
short picnum;
|
||||||
char shade;
|
int8_t shade;
|
||||||
char pal;
|
uint8_t pal;
|
||||||
unsigned char xrepeat;
|
uint8_t xrepeat;
|
||||||
unsigned char yrepeat;
|
uint8_t yrepeat;
|
||||||
short packSlot;
|
short packSlot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,15 +125,15 @@ struct MissileType
|
||||||
short picnum;
|
short picnum;
|
||||||
int velocity;
|
int velocity;
|
||||||
int angleOfs;
|
int angleOfs;
|
||||||
unsigned char xrepeat;
|
uint8_t xrepeat;
|
||||||
unsigned char yrepeat;
|
uint8_t yrepeat;
|
||||||
char shade;
|
int8_t shade;
|
||||||
unsigned char clipDist;
|
uint8_t clipDist;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EXPLOSION
|
struct EXPLOSION
|
||||||
{
|
{
|
||||||
unsigned char repeat;
|
uint8_t repeat;
|
||||||
char dmg;
|
char dmg;
|
||||||
char dmgRng;
|
char dmgRng;
|
||||||
int radius;
|
int radius;
|
||||||
|
|
|
@ -64,7 +64,7 @@ void playlogos()
|
||||||
|
|
||||||
if (!userConfig.nologo)
|
if (!userConfig.nologo)
|
||||||
{
|
{
|
||||||
if (fileSystem.FindFile("logo.smk"))
|
if (fileSystem.FindFile("logo.smk") != -1)
|
||||||
{
|
{
|
||||||
jobs[job++] = { PlayVideo("logo.smk", &logosound[0], 0) };
|
jobs[job++] = { PlayVideo("logo.smk", &logosound[0], 0) };
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ void playlogos()
|
||||||
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
|
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
|
||||||
jobs[job++] = { Create<DImageScreen>(2050) };
|
jobs[job++] = { Create<DImageScreen>(2050) };
|
||||||
}
|
}
|
||||||
if (fileSystem.FindFile("gti.smk"))
|
if (fileSystem.FindFile("gti.smk") != -1)
|
||||||
{
|
{
|
||||||
jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) };
|
jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) };
|
||||||
}
|
}
|
||||||
|
|
|
@ -987,7 +987,7 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor
|
||||||
|
|
||||||
fr.Seek(0, FileReader::SeekSet);
|
fr.Seek(0, FileReader::SeekSet);
|
||||||
auto buffer = fr.Read();
|
auto buffer = fr.Read();
|
||||||
unsigned char md4[16];
|
uint8_t md4[16];
|
||||||
md4once(buffer.Data(), buffer.Size(), md4);
|
md4once(buffer.Data(), buffer.Size(), md4);
|
||||||
G_LoadMapHack(mapname, md4);
|
G_LoadMapHack(mapname, md4);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ struct DUDEINFO {
|
||||||
short startHealth; // health
|
short startHealth; // health
|
||||||
unsigned short mass; // mass
|
unsigned short mass; // mass
|
||||||
int at6; // unused?
|
int at6; // unused?
|
||||||
unsigned char clipdist; // clipdist
|
uint8_t clipdist; // clipdist
|
||||||
int eyeHeight;
|
int eyeHeight;
|
||||||
int aimHeight; // used by just Cerberus
|
int aimHeight; // used by just Cerberus
|
||||||
int hearDist; // hear radius
|
int hearDist; // hear radius
|
||||||
|
|
|
@ -91,7 +91,7 @@ static void DrawCaption(const char* text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DBloodSummaryScreen : public DScreenJob
|
class DBloodSummaryScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
void DrawKills(void)
|
void DrawKills(void)
|
||||||
{
|
{
|
||||||
|
@ -139,7 +139,7 @@ class DBloodSummaryScreen : public DScreenJob
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
drawTextScreenBackground();
|
drawTextScreenBackground();
|
||||||
if (gGameOptions.nGameType == 0)
|
if (gGameOptions.nGameType == 0)
|
||||||
|
@ -160,7 +160,7 @@ class DBloodSummaryScreen : public DScreenJob
|
||||||
DrawCaption(GStrings("TXTB_FRAGSTATS"));
|
DrawCaption(GStrings("TXTB_FRAGSTATS"));
|
||||||
DrawKills();
|
DrawKills();
|
||||||
}
|
}
|
||||||
int myclock = int(clock * 120 / 1'000'000'000);
|
int myclock = ticks * 120 / GameTicRate;
|
||||||
if ((myclock & 32))
|
if ((myclock & 32))
|
||||||
{
|
{
|
||||||
auto text = GStrings("PRESSKEY");
|
auto text = GStrings("PRESSKEY");
|
||||||
|
@ -168,7 +168,6 @@ class DBloodSummaryScreen : public DScreenJob
|
||||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||||
viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3);
|
viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3);
|
||||||
}
|
}
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -274,7 +273,7 @@ CSecretMgr gSecretMgr;
|
||||||
CKillMgr gKillMgr;
|
CKillMgr gKillMgr;
|
||||||
|
|
||||||
class DBloodLoadScreen : public DScreenJob
|
class DBloodLoadScreen : public DScreenJob
|
||||||
{
|
{
|
||||||
const char* pzLoadingScreenText1;
|
const char* pzLoadingScreenText1;
|
||||||
MapRecord* rec;
|
MapRecord* rec;
|
||||||
|
|
||||||
|
@ -285,7 +284,7 @@ public:
|
||||||
else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType));
|
else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
drawTextScreenBackground();
|
drawTextScreenBackground();
|
||||||
|
@ -297,7 +296,6 @@ public:
|
||||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||||
|
|
||||||
viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3);
|
viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -109,10 +109,10 @@ void FireProcess(void)
|
||||||
void CellularFrame(char *pFrame, int sizeX, int sizeY)
|
void CellularFrame(char *pFrame, int sizeX, int sizeY)
|
||||||
{
|
{
|
||||||
int nSquare = sizeX * sizeY;
|
int nSquare = sizeX * sizeY;
|
||||||
unsigned char *pPtr1 = (unsigned char*)pFrame;
|
uint8_t *pPtr1 = (uint8_t*)pFrame;
|
||||||
while (nSquare--)
|
while (nSquare--)
|
||||||
{
|
{
|
||||||
unsigned char *pPtr2 = pPtr1+sizeX;
|
uint8_t *pPtr2 = pPtr1+sizeX;
|
||||||
int sum = *(pPtr2-1) + *pPtr2 + *(pPtr2+1) + *(pPtr2+sizeX);
|
int sum = *(pPtr2-1) + *pPtr2 + *(pPtr2+1) + *(pPtr2+sizeX);
|
||||||
if (*(pPtr2+sizeX) > 96)
|
if (*(pPtr2+sizeX) > 96)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,18 +32,18 @@ CFX gFX;
|
||||||
|
|
||||||
struct FXDATA {
|
struct FXDATA {
|
||||||
CALLBACK_ID funcID; // callback
|
CALLBACK_ID funcID; // callback
|
||||||
char at1; // detail
|
uint8_t detail; // detail
|
||||||
short at2; // seq
|
short seq; // seq
|
||||||
short Kills; // flags
|
short flags; // flags
|
||||||
int at6; // gravity
|
int gravity; // gravity
|
||||||
int ata; // air drag
|
int drag; // air drag
|
||||||
int ate;
|
int ate;
|
||||||
short at12; // picnum
|
short picnum; // picnum
|
||||||
unsigned char at14; // xrepeat
|
uint8_t xrepeat; // xrepeat
|
||||||
unsigned char at15; // yrepeat
|
uint8_t yrepeat; // yrepeat
|
||||||
short at16; // cstat
|
short cstat; // cstat
|
||||||
signed char at18; // shade
|
int8_t shade; // shade
|
||||||
char at19; // pal
|
uint8_t pal; // pal
|
||||||
};
|
};
|
||||||
|
|
||||||
FXDATA gFXData[] = {
|
FXDATA gFXData[] = {
|
||||||
|
@ -166,23 +166,23 @@ spritetype * CFX::fxSpawn(FX_ID nFx, int nSector, int x, int y, int z, unsigned
|
||||||
}
|
}
|
||||||
spritetype *pSprite = actSpawnSprite(nSector, x, y, z, 1, 0);
|
spritetype *pSprite = actSpawnSprite(nSector, x, y, z, 1, 0);
|
||||||
pSprite->type = nFx;
|
pSprite->type = nFx;
|
||||||
pSprite->picnum = pFX->at12;
|
pSprite->picnum = pFX->picnum;
|
||||||
pSprite->cstat |= pFX->at16;
|
pSprite->cstat |= pFX->cstat;
|
||||||
pSprite->shade = pFX->at18;
|
pSprite->shade = pFX->shade;
|
||||||
pSprite->pal = pFX->at19;
|
pSprite->pal = pFX->pal;
|
||||||
sprite[pSprite->index].detail = pFX->at1;
|
sprite[pSprite->index].detail = pFX->detail;
|
||||||
if (pFX->at14 > 0)
|
if (pFX->xrepeat > 0)
|
||||||
pSprite->xrepeat = pFX->at14;
|
pSprite->xrepeat = pFX->xrepeat;
|
||||||
if (pFX->at15 > 0)
|
if (pFX->yrepeat > 0)
|
||||||
pSprite->yrepeat = pFX->at15;
|
pSprite->yrepeat = pFX->yrepeat;
|
||||||
if ((pFX->Kills & 1) && Chance(0x8000))
|
if ((pFX->flags & 1) && Chance(0x8000))
|
||||||
pSprite->cstat |= 4;
|
pSprite->cstat |= 4;
|
||||||
if ((pFX->Kills & 2) && Chance(0x8000))
|
if ((pFX->flags & 2) && Chance(0x8000))
|
||||||
pSprite->cstat |= 8;
|
pSprite->cstat |= 8;
|
||||||
if (pFX->at2)
|
if (pFX->seq)
|
||||||
{
|
{
|
||||||
int nXSprite = dbInsertXSprite(pSprite->index);
|
int nXSprite = dbInsertXSprite(pSprite->index);
|
||||||
seqSpawn(pFX->at2, 3, nXSprite, -1);
|
seqSpawn(pFX->seq, 3, nXSprite, -1);
|
||||||
}
|
}
|
||||||
if (a6 == 0)
|
if (a6 == 0)
|
||||||
a6 = pFX->ate;
|
a6 = pFX->ate;
|
||||||
|
@ -203,7 +203,7 @@ void CFX::fxProcess(void)
|
||||||
assert(nSector >= 0 && nSector < kMaxSectors);
|
assert(nSector >= 0 && nSector < kMaxSectors);
|
||||||
assert(pSprite->type < kFXMax);
|
assert(pSprite->type < kFXMax);
|
||||||
FXDATA *pFXData = &gFXData[pSprite->type];
|
FXDATA *pFXData = &gFXData[pSprite->type];
|
||||||
actAirDrag(pSprite, pFXData->ata);
|
actAirDrag(pSprite, pFXData->drag);
|
||||||
if (xvel[nSprite])
|
if (xvel[nSprite])
|
||||||
pSprite->x += xvel[nSprite]>>12;
|
pSprite->x += xvel[nSprite]>>12;
|
||||||
if (yvel[nSprite])
|
if (yvel[nSprite])
|
||||||
|
@ -257,7 +257,7 @@ void CFX::fxProcess(void)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zvel[nSprite] += pFXData->at6;
|
zvel[nSprite] += pFXData->gravity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,9 +349,9 @@ void fxPrecache()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < kFXMax; i++)
|
for (int i = 0; i < kFXMax; i++)
|
||||||
{
|
{
|
||||||
tilePrecacheTile(gFXData[i].at12, 0, 0);
|
tilePrecacheTile(gFXData[i].picnum, 0, 0);
|
||||||
if (gFXData[i].at2)
|
if (gFXData[i].seq)
|
||||||
seqPrecacheId(gFXData[i].at2, 0);
|
seqPrecacheId(gFXData[i].seq, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,8 @@ BEGIN_BLD_NS
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
short nTile;
|
short nTile;
|
||||||
unsigned char nStat;
|
uint8_t nStat;
|
||||||
unsigned char nPal;
|
uint8_t nPal;
|
||||||
int nScale;
|
int nScale;
|
||||||
short nX, nY;
|
short nX, nY;
|
||||||
} burnTable[9] = {
|
} burnTable[9] = {
|
||||||
|
|
|
@ -47,8 +47,8 @@ enum EGameFlag
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GAMEOPTIONS {
|
struct GAMEOPTIONS {
|
||||||
unsigned char nGameType;
|
uint8_t nGameType;
|
||||||
unsigned char nDifficulty;
|
uint8_t nDifficulty;
|
||||||
char nMonsterSettings;
|
char nMonsterSettings;
|
||||||
int uGameFlags;
|
int uGameFlags;
|
||||||
int uNetGameFlags;
|
int uNetGameFlags;
|
||||||
|
|
|
@ -49,7 +49,7 @@ void WeaponInit(void);
|
||||||
void WeaponDraw(PLAYER *pPlayer, int a2, double a3, double a4, int a5, int smoothratio);
|
void WeaponDraw(PLAYER *pPlayer, int a2, double a3, double a4, int a5, int smoothratio);
|
||||||
void WeaponRaise(PLAYER *pPlayer);
|
void WeaponRaise(PLAYER *pPlayer);
|
||||||
void WeaponLower(PLAYER *pPlayer);
|
void WeaponLower(PLAYER *pPlayer);
|
||||||
char WeaponUpgrade(PLAYER *pPlayer, char newWeapon);
|
int WeaponUpgrade(PLAYER *pPlayer, int newWeapon);
|
||||||
void WeaponProcess(PLAYER *pPlayer);
|
void WeaponProcess(PLAYER *pPlayer);
|
||||||
void WeaponUpdateState(PLAYER* pPlayer);
|
void WeaponUpdateState(PLAYER* pPlayer);
|
||||||
void teslaHit(spritetype *pMissile, int a2);
|
void teslaHit(spritetype *pMissile, int a2);
|
||||||
|
@ -114,7 +114,7 @@ enum SurfaceType {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern char surfType[MAXTILES];
|
extern char surfType[MAXTILES];
|
||||||
extern signed char tileShade[MAXTILES];
|
extern int8_t tileShade[MAXTILES];
|
||||||
extern short voxelIndex[MAXTILES];
|
extern short voxelIndex[MAXTILES];
|
||||||
|
|
||||||
extern int nPrecacheCount;
|
extern int nPrecacheCount;
|
||||||
|
|
|
@ -182,7 +182,7 @@ struct PLAYER
|
||||||
struct AMMOINFO
|
struct AMMOINFO
|
||||||
{
|
{
|
||||||
int max;
|
int max;
|
||||||
signed char vectorType;
|
int8_t vectorType;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct POWERUPINFO
|
struct POWERUPINFO
|
||||||
|
|
|
@ -46,7 +46,7 @@ struct TILE_FRAME
|
||||||
int y;
|
int y;
|
||||||
int z;
|
int z;
|
||||||
int stat;
|
int stat;
|
||||||
signed char shade;
|
int8_t shade;
|
||||||
char palnum;
|
char palnum;
|
||||||
unsigned short angle;
|
unsigned short angle;
|
||||||
};
|
};
|
||||||
|
@ -54,9 +54,9 @@ struct TILE_FRAME
|
||||||
struct SOUNDINFO
|
struct SOUNDINFO
|
||||||
{
|
{
|
||||||
int sound;
|
int sound;
|
||||||
unsigned char priority;
|
uint8_t priority;
|
||||||
unsigned char sndFlags; // (by NoOne) Various sound flags
|
uint8_t sndFlags; // (by NoOne) Various sound flags
|
||||||
unsigned char sndRange; // (by NoOne) Random sound range
|
uint8_t sndRange; // (by NoOne) Random sound range
|
||||||
char reserved[1];
|
char reserved[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ struct Seq {
|
||||||
|
|
||||||
struct ACTIVE
|
struct ACTIVE
|
||||||
{
|
{
|
||||||
unsigned char type;
|
uint8_t type;
|
||||||
unsigned short xindex;
|
unsigned short xindex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ struct SEQINST
|
||||||
int nSeqID;
|
int nSeqID;
|
||||||
int callback;
|
int callback;
|
||||||
short timeCounter;
|
short timeCounter;
|
||||||
unsigned char frameIndex;
|
uint8_t frameIndex;
|
||||||
void Update();
|
void Update();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ int tileEnd[256];
|
||||||
int hTileFile[256];
|
int hTileFile[256];
|
||||||
|
|
||||||
char surfType[kMaxTiles];
|
char surfType[kMaxTiles];
|
||||||
signed char tileShade[kMaxTiles];
|
int8_t tileShade[kMaxTiles];
|
||||||
short voxelIndex[kMaxTiles];
|
short voxelIndex[kMaxTiles];
|
||||||
|
|
||||||
int tileInit(char a1, const char *a2)
|
int tileInit(char a1, const char *a2)
|
||||||
|
|
|
@ -144,7 +144,7 @@ enum
|
||||||
|
|
||||||
QAV *weaponQAV[kQAVEnd];
|
QAV *weaponQAV[kQAVEnd];
|
||||||
|
|
||||||
char sub_4B1A4(PLAYER *pPlayer)
|
static bool sub_4B1A4(PLAYER *pPlayer)
|
||||||
{
|
{
|
||||||
switch (pPlayer->curWeapon)
|
switch (pPlayer->curWeapon)
|
||||||
{
|
{
|
||||||
|
@ -169,12 +169,12 @@ char sub_4B1A4(PLAYER *pPlayer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char BannedUnderwater(int nWeapon)
|
static bool BannedUnderwater(int nWeapon)
|
||||||
{
|
{
|
||||||
return nWeapon == 7 || nWeapon == 6;
|
return nWeapon == 7 || nWeapon == 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
char CheckWeaponAmmo(PLAYER *pPlayer, int weapon, int ammotype, int count)
|
static bool CheckWeaponAmmo(PLAYER *pPlayer, int weapon, int ammotype, int count)
|
||||||
{
|
{
|
||||||
if (gInfiniteAmmo)
|
if (gInfiniteAmmo)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -187,7 +187,7 @@ char CheckWeaponAmmo(PLAYER *pPlayer, int weapon, int ammotype, int count)
|
||||||
return pPlayer->ammoCount[ammotype] >= count;
|
return pPlayer->ammoCount[ammotype] >= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
char CheckAmmo(PLAYER *pPlayer, int ammotype, int count)
|
static bool CheckAmmo(PLAYER *pPlayer, int ammotype, int count)
|
||||||
{
|
{
|
||||||
if (gInfiniteAmmo)
|
if (gInfiniteAmmo)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -200,7 +200,7 @@ char CheckAmmo(PLAYER *pPlayer, int ammotype, int count)
|
||||||
return pPlayer->ammoCount[ammotype] >= count;
|
return pPlayer->ammoCount[ammotype] >= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
char checkAmmo2(PLAYER *pPlayer, int ammotype, int amount)
|
static bool checkAmmo2(PLAYER *pPlayer, int ammotype, int amount)
|
||||||
{
|
{
|
||||||
if (gInfiniteAmmo)
|
if (gInfiniteAmmo)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1735,7 +1735,7 @@ void FireBeast(int nTrigger, PLAYER * pPlayer)
|
||||||
actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, VECTOR_TYPE_9);
|
actFireVector(pPlayer->pSprite, 0, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->aim.dx+r1, pPlayer->aim.dy+r2, pPlayer->aim.dz+r3, VECTOR_TYPE_9);
|
||||||
}
|
}
|
||||||
|
|
||||||
char gWeaponUpgrade[][13] = {
|
uint8_t gWeaponUpgrade[][13] = {
|
||||||
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
|
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
|
||||||
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
|
{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
|
||||||
{ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
|
{ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||||
|
@ -1751,9 +1751,9 @@ char gWeaponUpgrade[][13] = {
|
||||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
char WeaponUpgrade(PLAYER *pPlayer, char newWeapon)
|
int WeaponUpgrade(PLAYER *pPlayer, int newWeapon)
|
||||||
{
|
{
|
||||||
char weapon = pPlayer->curWeapon;
|
int weapon = pPlayer->curWeapon;
|
||||||
if (!sub_4B1A4(pPlayer) && (cl_weaponswitch&1) && (gWeaponUpgrade[pPlayer->curWeapon][newWeapon] || (cl_weaponswitch&2)))
|
if (!sub_4B1A4(pPlayer) && (cl_weaponswitch&1) && (gWeaponUpgrade[pPlayer->curWeapon][newWeapon] || (cl_weaponswitch&2)))
|
||||||
weapon = newWeapon;
|
weapon = newWeapon;
|
||||||
return weapon;
|
return weapon;
|
||||||
|
@ -1762,7 +1762,7 @@ char WeaponUpgrade(PLAYER *pPlayer, char newWeapon)
|
||||||
int OrderNext[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 1 };
|
int OrderNext[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 1 };
|
||||||
int OrderPrev[] = { 12, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1 };
|
int OrderPrev[] = { 12, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1 };
|
||||||
|
|
||||||
char WeaponFindNext(PLAYER *pPlayer, int *a2, char bDir)
|
static int WeaponFindNext(PLAYER *pPlayer, int *a2, int bDir)
|
||||||
{
|
{
|
||||||
int weapon = pPlayer->curWeapon;
|
int weapon = pPlayer->curWeapon;
|
||||||
do
|
do
|
||||||
|
@ -1795,9 +1795,9 @@ char WeaponFindNext(PLAYER *pPlayer, int *a2, char bDir)
|
||||||
return weapon;
|
return weapon;
|
||||||
}
|
}
|
||||||
|
|
||||||
char WeaponFindLoaded(PLAYER *pPlayer, int *a2)
|
static int WeaponFindLoaded(PLAYER *pPlayer, int *a2)
|
||||||
{
|
{
|
||||||
char v4 = 1;
|
int v4 = 1;
|
||||||
int v14 = 0;
|
int v14 = 0;
|
||||||
if (weaponModes[pPlayer->curWeapon].update > 1)
|
if (weaponModes[pPlayer->curWeapon].update > 1)
|
||||||
{
|
{
|
||||||
|
@ -1874,7 +1874,7 @@ int processSprayCan(PLAYER *pPlayer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char processTNT(PLAYER *pPlayer)
|
static bool processTNT(PLAYER *pPlayer)
|
||||||
{
|
{
|
||||||
switch (pPlayer->weaponState)
|
switch (pPlayer->weaponState)
|
||||||
{
|
{
|
||||||
|
@ -1912,7 +1912,7 @@ char processTNT(PLAYER *pPlayer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char processProxy(PLAYER *pPlayer)
|
static bool processProxy(PLAYER *pPlayer)
|
||||||
{
|
{
|
||||||
switch (pPlayer->weaponState)
|
switch (pPlayer->weaponState)
|
||||||
{
|
{
|
||||||
|
@ -1929,7 +1929,7 @@ char processProxy(PLAYER *pPlayer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char processRemote(PLAYER *pPlayer)
|
static bool processRemote(PLAYER *pPlayer)
|
||||||
{
|
{
|
||||||
switch (pPlayer->weaponState)
|
switch (pPlayer->weaponState)
|
||||||
{
|
{
|
||||||
|
@ -1945,7 +1945,7 @@ char processRemote(PLAYER *pPlayer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char processLeech(PLAYER *pPlayer)
|
static bool processLeech(PLAYER *pPlayer)
|
||||||
{
|
{
|
||||||
switch (pPlayer->weaponState)
|
switch (pPlayer->weaponState)
|
||||||
{
|
{
|
||||||
|
@ -1969,7 +1969,7 @@ char processLeech(PLAYER *pPlayer)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char processTesla(PLAYER *pPlayer)
|
static bool processTesla(PLAYER *pPlayer)
|
||||||
{
|
{
|
||||||
switch (pPlayer->weaponState)
|
switch (pPlayer->weaponState)
|
||||||
{
|
{
|
||||||
|
@ -2109,7 +2109,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
||||||
}
|
}
|
||||||
pPlayer->nextWeapon = 0;
|
pPlayer->nextWeapon = 0;
|
||||||
int t;
|
int t;
|
||||||
char weapon = WeaponFindNext(pPlayer, &t, 1);
|
int weapon = WeaponFindNext(pPlayer, &t, 1);
|
||||||
pPlayer->weaponMode[weapon] = t;
|
pPlayer->weaponMode[weapon] = t;
|
||||||
if (VanillaMode())
|
if (VanillaMode())
|
||||||
{
|
{
|
||||||
|
@ -2131,7 +2131,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
||||||
}
|
}
|
||||||
pPlayer->nextWeapon = 0;
|
pPlayer->nextWeapon = 0;
|
||||||
int t;
|
int t;
|
||||||
char weapon = WeaponFindNext(pPlayer, &t, 0);
|
int weapon = WeaponFindNext(pPlayer, &t, 0);
|
||||||
pPlayer->weaponMode[weapon] = t;
|
pPlayer->weaponMode[weapon] = t;
|
||||||
if (VanillaMode())
|
if (VanillaMode())
|
||||||
{
|
{
|
||||||
|
@ -2146,7 +2146,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
||||||
}
|
}
|
||||||
else if (pPlayer->input.getNewWeapon() == WeaponSel_Alt)
|
else if (pPlayer->input.getNewWeapon() == WeaponSel_Alt)
|
||||||
{
|
{
|
||||||
char weapon;
|
int weapon;
|
||||||
|
|
||||||
switch (pPlayer->curWeapon)
|
switch (pPlayer->curWeapon)
|
||||||
{
|
{
|
||||||
|
@ -2189,7 +2189,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
||||||
{
|
{
|
||||||
pPlayer->weaponState = 0;
|
pPlayer->weaponState = 0;
|
||||||
int t;
|
int t;
|
||||||
char weapon = WeaponFindLoaded(pPlayer, &t);
|
int weapon = WeaponFindLoaded(pPlayer, &t);
|
||||||
pPlayer->weaponMode[weapon] = t;
|
pPlayer->weaponMode[weapon] = t;
|
||||||
if (pPlayer->curWeapon)
|
if (pPlayer->curWeapon)
|
||||||
{
|
{
|
||||||
|
@ -2263,7 +2263,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
||||||
{
|
{
|
||||||
pPlayer->weaponState = 0;
|
pPlayer->weaponState = 0;
|
||||||
int t;
|
int t;
|
||||||
char weapon = WeaponFindLoaded(pPlayer, &t);
|
int weapon = WeaponFindLoaded(pPlayer, &t);
|
||||||
pPlayer->weaponMode[weapon] = t;
|
pPlayer->weaponMode[weapon] = t;
|
||||||
if (pPlayer->curWeapon)
|
if (pPlayer->curWeapon)
|
||||||
{
|
{
|
||||||
|
@ -2597,11 +2597,11 @@ void teslaHit(spritetype *pMissile, int a2)
|
||||||
int nSector = pMissile->sectnum;
|
int nSector = pMissile->sectnum;
|
||||||
int nOwner = pMissile->owner;
|
int nOwner = pMissile->owner;
|
||||||
GetClosestSpriteSectors(nSector, x, y, nDist, va4);
|
GetClosestSpriteSectors(nSector, x, y, nDist, va4);
|
||||||
char v4 = 1;
|
bool v4 = true;
|
||||||
int v24 = -1;
|
int v24 = -1;
|
||||||
actHitcodeToData(a2, &gHitInfo, &v24, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
actHitcodeToData(a2, &gHitInfo, &v24, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||||
if (a2 == 3 && v24 >= 0 && sprite[v24].statnum == kStatDude)
|
if (a2 == 3 && v24 >= 0 && sprite[v24].statnum == kStatDude)
|
||||||
v4 = 0;
|
v4 = false;
|
||||||
int nSprite;
|
int nSprite;
|
||||||
StatIterator it(kStatDude);
|
StatIterator it(kStatDude);
|
||||||
while ((nSprite = it.NextIndex()) >= 0)
|
while ((nSprite = it.NextIndex()) >= 0)
|
||||||
|
|
|
@ -166,20 +166,23 @@ static void MiniText(double x, double y, const char* t, int shade, int align = -
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DDRealmsScreen : public DScreenJob
|
class DDRealmsScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DDRealmsScreen() : DScreenJob(fadein | fadeout) {}
|
DDRealmsScreen() : DSkippableScreenJob(fadein | fadeout) {}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
void OnTick() override
|
||||||
|
{
|
||||||
|
if (ticks >= 7 * GameTicRate) state = finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double smoothratio) override
|
||||||
{
|
{
|
||||||
const uint64_t duration = 7'000'000'000;
|
|
||||||
const auto tex = tileGetTexture(DREALMS, true);
|
const auto tex = tileGetTexture(DREALMS, true);
|
||||||
int translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, DREALMSPAL) : 0;
|
int translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, DREALMSPAL) : 0;
|
||||||
|
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
return skiprequest ? -1 : clock < duration ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,27 +192,18 @@ public:
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DTitleScreen : public DScreenJob
|
class DTitleScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
int soundanm = 0;
|
int soundanm = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DTitleScreen() : DScreenJob(fadein | fadeout)
|
DTitleScreen() : DSkippableScreenJob(fadein | fadeout)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t nsclock, bool skiprequest) override
|
void OnTick() override
|
||||||
{
|
{
|
||||||
twod->ClearScreen();
|
int clock = ticks * 120 / GameTicRate;
|
||||||
int clock = nsclock * 120 / 1'000'000'000;
|
|
||||||
|
|
||||||
twod->ClearScreen();
|
|
||||||
|
|
||||||
// Only translate if the image depends on the global palette.
|
|
||||||
auto tex = tileGetTexture(BETASCREEN, true);
|
|
||||||
int translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0;
|
|
||||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
|
||||||
|
|
||||||
if (soundanm == 0 && clock >= 120 && clock < 120 + 60)
|
if (soundanm == 0 && clock >= 120 && clock < 120 + 60)
|
||||||
{
|
{
|
||||||
soundanm = 1;
|
soundanm = 1;
|
||||||
|
@ -231,6 +225,25 @@ public:
|
||||||
if (isPlutoPak()) S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
if (isPlutoPak()) S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clock > (860 + 120))
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double smoothratio) override
|
||||||
|
{
|
||||||
|
twod->ClearScreen();
|
||||||
|
int clock = (ticks + smoothratio) * 120 / GameTicRate;
|
||||||
|
|
||||||
|
twod->ClearScreen();
|
||||||
|
|
||||||
|
// Only translate if the image depends on the global palette.
|
||||||
|
auto tex = tileGetTexture(BETASCREEN, true);
|
||||||
|
int translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0;
|
||||||
|
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
|
|
||||||
|
|
||||||
double scale = clamp(clock - 120, 0, 60) / 64.;
|
double scale = clamp(clock - 120, 0, 60) / 64.;
|
||||||
if (scale > 0.)
|
if (scale > 0.)
|
||||||
{
|
{
|
||||||
|
@ -263,13 +276,6 @@ public:
|
||||||
DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
|
DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clock > (860 + 120))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -297,7 +303,7 @@ void Logo_d(const CompletionFunc &completion)
|
||||||
if (!userConfig.nologo)
|
if (!userConfig.nologo)
|
||||||
{
|
{
|
||||||
if (!isShareware()) jobs[job++] = { PlayVideo("logo.anm", logosound, logoframetimes), []() { S_PlaySpecialMusic(MUS_INTRO); } };
|
if (!isShareware()) jobs[job++] = { PlayVideo("logo.anm", logosound, logoframetimes), []() { S_PlaySpecialMusic(MUS_INTRO); } };
|
||||||
else jobs[job++] = { Create<DScreenJob>(), []() { S_PlaySpecialMusic(MUS_INTRO); } };
|
else jobs[job++] = { Create<DBlackScreen>(1), []() { S_PlaySpecialMusic(MUS_INTRO); } };
|
||||||
if (!isNam()) jobs[job++] = { Create<DDRealmsScreen>(), nullptr };
|
if (!isNam()) jobs[job++] = { Create<DDRealmsScreen>(), nullptr };
|
||||||
}
|
}
|
||||||
else S_PlaySpecialMusic(MUS_INTRO);
|
else S_PlaySpecialMusic(MUS_INTRO);
|
||||||
|
@ -311,70 +317,68 @@ void Logo_d(const CompletionFunc &completion)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DEpisode1End1 : public DScreenJob
|
class DEpisode1End1 : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
int bonuscnt = 0;
|
int bonuscnt = 0;
|
||||||
|
int bossani = -1;
|
||||||
|
int breatheani = -1;
|
||||||
|
bool breathebg = false;
|
||||||
|
|
||||||
|
static inline const int breathe[] =
|
||||||
|
{
|
||||||
|
0, 30,VICTORY1 + 1,176,59,
|
||||||
|
30, 60,VICTORY1 + 2,176,59,
|
||||||
|
60, 90,VICTORY1 + 1,176,59,
|
||||||
|
90, 120,0 ,176,59
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline const int bossmove[] =
|
||||||
|
{
|
||||||
|
0, 120,VICTORY1 + 3,86,59,
|
||||||
|
220, 260,VICTORY1 + 4,86,59,
|
||||||
|
260, 290,VICTORY1 + 5,86,59,
|
||||||
|
290, 320,VICTORY1 + 6,86,59,
|
||||||
|
320, 350,VICTORY1 + 7,86,59,
|
||||||
|
350, 380,VICTORY1 + 8,86,59,
|
||||||
|
350, 380,VICTORY1 + 8,86,59,
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DEpisode1End1() : DScreenJob(fadein | fadeout) {}
|
DEpisode1End1() : DSkippableScreenJob(fadein | fadeout) {}
|
||||||
|
|
||||||
int Frame(uint64_t nsclock, bool skiprequest) override
|
void OnTick()
|
||||||
{
|
{
|
||||||
static const int breathe[] =
|
int currentclock = ticks * 120 / GameTicRate;
|
||||||
{
|
|
||||||
0, 30,VICTORY1 + 1,176,59,
|
|
||||||
30, 60,VICTORY1 + 2,176,59,
|
|
||||||
60, 90,VICTORY1 + 1,176,59,
|
|
||||||
90, 120,0 ,176,59
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int bossmove[] =
|
|
||||||
{
|
|
||||||
0, 120,VICTORY1 + 3,86,59,
|
|
||||||
220, 260,VICTORY1 + 4,86,59,
|
|
||||||
260, 290,VICTORY1 + 5,86,59,
|
|
||||||
290, 320,VICTORY1 + 6,86,59,
|
|
||||||
320, 350,VICTORY1 + 7,86,59,
|
|
||||||
350, 380,VICTORY1 + 8,86,59,
|
|
||||||
350, 380,VICTORY1 + 8,86,59,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto translation = TRANSLATION(Translation_BasePalettes, ENDINGPAL);
|
|
||||||
|
|
||||||
int currentclock = nsclock * 120 / 1'000'000'000;
|
|
||||||
|
|
||||||
uint64_t span = nsclock / 1'000'000;
|
|
||||||
|
|
||||||
twod->ClearScreen();
|
|
||||||
DrawTexture(twod, tileGetTexture(VICTORY1, true), 0, 50, DTA_FullscreenScale, FSMode_Fit320x200,
|
|
||||||
DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true, TAG_DONE);
|
|
||||||
|
|
||||||
|
bossani = -1;
|
||||||
|
breathebg = false;
|
||||||
|
breatheani = -1;
|
||||||
|
|
||||||
// boss
|
// boss
|
||||||
if (currentclock > 390 && currentclock < 780)
|
if (currentclock > 390 && currentclock < 780)
|
||||||
|
{
|
||||||
for (int t = 0; t < 35; t += 5) if (bossmove[t + 2] && (currentclock % 390) > bossmove[t] && (currentclock % 390) <= bossmove[t + 1])
|
for (int t = 0; t < 35; t += 5) if (bossmove[t + 2] && (currentclock % 390) > bossmove[t] && (currentclock % 390) <= bossmove[t + 1])
|
||||||
{
|
{
|
||||||
if (t == 10 && bonuscnt == 1)
|
if (t == 10 && bonuscnt == 1)
|
||||||
{
|
{
|
||||||
S_PlaySound(SHOTGUN_FIRE, CHAN_AUTO, CHANF_UI);
|
S_PlaySound(SHOTGUN_FIRE, CHAN_AUTO, CHANF_UI);
|
||||||
S_PlaySound(SQUISHED, CHAN_AUTO, CHANF_UI);
|
S_PlaySound(SQUISHED, CHAN_AUTO, CHANF_UI);
|
||||||
bonuscnt++;
|
bonuscnt++;
|
||||||
}
|
}
|
||||||
DrawTexture(twod, tileGetTexture(bossmove[t + 2], true), bossmove[t + 3], bossmove[t + 4], DTA_FullscreenScale, FSMode_Fit320x200,
|
bossani = t;
|
||||||
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Breathe
|
// Breathe
|
||||||
if (currentclock < 450 || currentclock >= 750)
|
if (currentclock < 450 || currentclock >= 750)
|
||||||
{
|
{
|
||||||
if (currentclock >= 750)
|
if (currentclock >= 750)
|
||||||
{
|
{
|
||||||
DrawTexture(twod, tileGetTexture(VICTORY1 + 8, true), 86, 59, DTA_FullscreenScale, FSMode_Fit320x200,
|
breathebg = true;
|
||||||
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
if (currentclock >= 750 && bonuscnt == 2)
|
||||||
if (currentclock >= 750 && bonuscnt == 2)
|
{
|
||||||
{
|
|
||||||
S_PlaySound(DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI);
|
S_PlaySound(DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI);
|
||||||
bonuscnt++;
|
bonuscnt++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int t = 0; t < 20; t += 5)
|
for (int t = 0; t < 20; t += 5)
|
||||||
|
@ -385,12 +389,37 @@ public:
|
||||||
S_PlaySound(BOSSTALKTODUKE, CHAN_AUTO, CHANF_UI);
|
S_PlaySound(BOSSTALKTODUKE, CHAN_AUTO, CHANF_UI);
|
||||||
bonuscnt++;
|
bonuscnt++;
|
||||||
}
|
}
|
||||||
DrawTexture(twod, tileGetTexture(breathe[t + 2], true), breathe[t + 3], breathe[t + 4], DTA_FullscreenScale, FSMode_Fit320x200,
|
breatheani = t;
|
||||||
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Only end after having faded out.
|
|
||||||
return skiprequest ? -1 : 1;
|
}
|
||||||
|
|
||||||
|
void Draw(double) override
|
||||||
|
{
|
||||||
|
auto translation = TRANSLATION(Translation_BasePalettes, ENDINGPAL);
|
||||||
|
|
||||||
|
twod->ClearScreen();
|
||||||
|
DrawTexture(twod, tileGetTexture(VICTORY1, true), 0, 50, DTA_FullscreenScale, FSMode_Fit320x200,
|
||||||
|
DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true, TAG_DONE);
|
||||||
|
|
||||||
|
if (bossani != -1)
|
||||||
|
{
|
||||||
|
DrawTexture(twod, tileGetTexture(bossmove[bossani + 2], true), bossmove[bossani + 3], bossmove[bossani + 4], DTA_FullscreenScale, FSMode_Fit320x200,
|
||||||
|
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breathebg)
|
||||||
|
{
|
||||||
|
DrawTexture(twod, tileGetTexture(VICTORY1 + 8, true), 86, 59, DTA_FullscreenScale, FSMode_Fit320x200,
|
||||||
|
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breatheani != -1)
|
||||||
|
{
|
||||||
|
DrawTexture(twod, tileGetTexture(breathe[breatheani + 2], true), breathe[breatheani + 3], breathe[breatheani + 4], DTA_FullscreenScale, FSMode_Fit320x200,
|
||||||
|
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -409,7 +438,6 @@ public:
|
||||||
|
|
||||||
FGameTexture* getTexture()
|
FGameTexture* getTexture()
|
||||||
{
|
{
|
||||||
// Here we must provide a real texture, even if invalid, so that the sounds play.
|
|
||||||
auto texid = TexMan.CheckForTexture("radlogo.anm", ETextureType::Any, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ForceLookup);
|
auto texid = TexMan.CheckForTexture("radlogo.anm", ETextureType::Any, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ForceLookup);
|
||||||
if (texid.isValid()) return TexMan.GetGameTexture(texid);
|
if (texid.isValid()) return TexMan.GetGameTexture(texid);
|
||||||
else return TexMan.GameByIndex(0);
|
else return TexMan.GameByIndex(0);
|
||||||
|
@ -420,7 +448,12 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Skipped() override
|
||||||
|
{
|
||||||
|
FX_StopAllSounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
{
|
{
|
||||||
switch (sound)
|
switch (sound)
|
||||||
{
|
{
|
||||||
|
@ -465,23 +498,21 @@ public:
|
||||||
if (!S_CheckSoundPlaying(ENDSEQVOL3SND9))
|
if (!S_CheckSoundPlaying(ENDSEQVOL3SND9))
|
||||||
{
|
{
|
||||||
sound++;
|
sound++;
|
||||||
waittime = clock + (SoundEnabled()? 1'000'000'000 : 5'000'000'000); // if sound is off this wouldn't wait without a longer delay here.
|
waittime = ticks + GameTicRate * (SoundEnabled() ? 1 : 5); // if sound is off this wouldn't wait without a longer delay here.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case 6:
|
||||||
if (isPlutoPak())
|
if (isPlutoPak())
|
||||||
{
|
{
|
||||||
if (clock > waittime) skiprequest = true;
|
if (ticks > waittime) state = finished;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int ret = DImageScreen::Frame(clock, skiprequest);
|
if (state != running) FX_StopAllSounds();
|
||||||
if (ret != 1) FX_StopAllSounds();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -491,12 +522,12 @@ public:
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DEpisode4Text : public DScreenJob
|
class DEpisode4Text : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DEpisode4Text() : DScreenJob(fadein | fadeout) {}
|
DEpisode4Text() : DSkippableScreenJob(fadein | fadeout) {}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
BigText(160, 60, GStrings("Thanks to all our"));
|
BigText(160, 60, GStrings("Thanks to all our"));
|
||||||
|
@ -504,7 +535,6 @@ public:
|
||||||
BigText(160, 60 + 16 + 16, GStrings("us big heads."));
|
BigText(160, 60 + 16 + 16, GStrings("us big heads."));
|
||||||
BigText(160, 70 + 16 + 16 + 16, GStrings("Look for a Duke Nukem 3D"));
|
BigText(160, 70 + 16 + 16 + 16, GStrings("Look for a Duke Nukem 3D"));
|
||||||
BigText(160, 70 + 16 + 16 + 16 + 16, GStrings("sequel soon."));
|
BigText(160, 70 + 16 + 16 + 16 + 16, GStrings("sequel soon."));
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -523,7 +553,7 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void OnTick() override
|
||||||
{
|
{
|
||||||
switch (sound)
|
switch (sound)
|
||||||
{
|
{
|
||||||
|
@ -539,9 +569,6 @@ public:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int ret = DImageScreen::Frame(clock, skiprequest);
|
|
||||||
if (ret != 1) FX_StopAllSounds();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -693,22 +720,25 @@ void doorders(const CompletionFunc& completion)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DDukeMultiplayerBonusScreen : public DScreenJob
|
class DDukeMultiplayerBonusScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
int playerswhenstarted;
|
int playerswhenstarted;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DDukeMultiplayerBonusScreen(int pws) : DScreenJob(fadein|fadeout)
|
DDukeMultiplayerBonusScreen(int pws) : DSkippableScreenJob(fadein|fadeout)
|
||||||
{
|
{
|
||||||
playerswhenstarted = pws;
|
playerswhenstarted = pws;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Start() override
|
||||||
{
|
{
|
||||||
if (clock == 0) S_PlayBonusMusic();
|
S_PlayBonusMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double smoothratio) override
|
||||||
|
{
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
int currentclock = int((ticks + smoothratio) * 120 / GameTicRate);
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
DrawTexture(twod, tileGetTexture(INGAMEDUKETHREEDEE, true), 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(INGAMEDUKETHREEDEE, true), 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, TAG_DONE);
|
||||||
|
@ -783,7 +813,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0, -1, 8);
|
MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0, -1, 8);
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -797,13 +826,23 @@ class DDukeLevelSummaryScreen : public DScreenJob
|
||||||
{
|
{
|
||||||
const char* lastmapname;
|
const char* lastmapname;
|
||||||
int gfx_offset;
|
int gfx_offset;
|
||||||
int bonuscnt = 0;
|
|
||||||
int speech = -1;
|
int speech = -1;
|
||||||
|
int displaystate = 0;
|
||||||
|
int dukeAnimStart;
|
||||||
|
|
||||||
void SetTotalClock(int tc)
|
enum
|
||||||
{
|
{
|
||||||
SetClock(tc * (uint64_t)1'000'000'000 / 120);
|
printTimeText = 1,
|
||||||
}
|
printTimeVal = 2,
|
||||||
|
printKillsText = 4,
|
||||||
|
printKillsVal = 8,
|
||||||
|
printSecretsText = 16,
|
||||||
|
printSecretsVal = 32,
|
||||||
|
printStatsAll = 63,
|
||||||
|
dukeAnim = 64,
|
||||||
|
dukeWait = 128,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DDukeLevelSummaryScreen() : DScreenJob(fadein | fadeout)
|
DDukeLevelSummaryScreen() : DScreenJob(fadein | fadeout)
|
||||||
|
@ -818,7 +857,82 @@ public:
|
||||||
mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60);
|
mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintTime(int currentclock)
|
bool OnEvent(event_t* ev) override
|
||||||
|
{
|
||||||
|
if (ev->type == EV_KeyDown)
|
||||||
|
{
|
||||||
|
if ((displaystate & printStatsAll) != printStatsAll)
|
||||||
|
{
|
||||||
|
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
||||||
|
displaystate = printStatsAll;
|
||||||
|
}
|
||||||
|
else if (!(displaystate & dukeAnim))
|
||||||
|
{
|
||||||
|
displaystate |= dukeAnim;
|
||||||
|
dukeAnimStart = ticks;
|
||||||
|
S_PlaySound(SHOTGUN_COCK, CHAN_AUTO, CHANF_UI);
|
||||||
|
static const uint16_t speeches[] = { BONUS_SPEECH1, BONUS_SPEECH2, BONUS_SPEECH3, BONUS_SPEECH4 };
|
||||||
|
speech = speeches[(rand() & 3)];
|
||||||
|
S_PlaySound(speech, CHAN_AUTO, CHANF_UI, 1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
S_PlayBonusMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
|
{
|
||||||
|
if ((displaystate & printStatsAll) != printStatsAll)
|
||||||
|
{
|
||||||
|
if (ticks == 15 * 3)
|
||||||
|
{
|
||||||
|
displaystate |= printTimeText;
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 4)
|
||||||
|
{
|
||||||
|
displaystate |= printTimeVal;
|
||||||
|
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 6)
|
||||||
|
{
|
||||||
|
displaystate |= printKillsText;
|
||||||
|
S_PlaySound(FLY_BY, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 7)
|
||||||
|
{
|
||||||
|
displaystate |= printKillsVal;
|
||||||
|
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 9)
|
||||||
|
{
|
||||||
|
displaystate |= printSecretsText;
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 10)
|
||||||
|
{
|
||||||
|
displaystate |= printSecretsVal;
|
||||||
|
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (displaystate & dukeAnim)
|
||||||
|
{
|
||||||
|
if (ticks >= dukeAnimStart + 60)
|
||||||
|
{
|
||||||
|
displaystate ^= dukeAnim | dukeWait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (displaystate & dukeWait)
|
||||||
|
{
|
||||||
|
if (speech <= 0 || !S_CheckSoundPlaying(speech))
|
||||||
|
state = finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintTime()
|
||||||
{
|
{
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
GameText(10, 59 + 9, GStrings("TXT_YourTime"), 0);
|
GameText(10, 59 + 9, GStrings("TXT_YourTime"), 0);
|
||||||
|
@ -826,16 +940,8 @@ public:
|
||||||
if (!isNamWW2GI())
|
if (!isNamWW2GI())
|
||||||
GameText(10, 79 + 9, GStrings("TXT_3DRTIME"), 0);
|
GameText(10, 79 + 9, GStrings("TXT_3DRTIME"), 0);
|
||||||
|
|
||||||
if (bonuscnt == 0)
|
if (displaystate & printTimeVal)
|
||||||
bonuscnt++;
|
|
||||||
|
|
||||||
if (currentclock > (60 * 4))
|
|
||||||
{
|
{
|
||||||
if (bonuscnt == 1)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
FormatTime(ps[myconnectindex].player_par, tempbuf);
|
FormatTime(ps[myconnectindex].player_par, tempbuf);
|
||||||
GameText((320 >> 2) + 71, 59 + 9, tempbuf, 0);
|
GameText((320 >> 2) + 71, 59 + 9, tempbuf, 0);
|
||||||
|
|
||||||
|
@ -850,25 +956,14 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintKills(int currentclock)
|
void PrintKills()
|
||||||
{
|
{
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
GameText(10, 94 + 9, GStrings("TXT_EnemiesKilled"), 0);
|
GameText(10, 94 + 9, GStrings("TXT_EnemiesKilled"), 0);
|
||||||
GameText(10, 104 + 9, GStrings("TXT_EnemiesLeft"), 0);
|
GameText(10, 104 + 9, GStrings("TXT_EnemiesLeft"), 0);
|
||||||
|
|
||||||
if (bonuscnt == 2)
|
if (displaystate & printKillsVal)
|
||||||
{
|
{
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(FLY_BY, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentclock > (60 * 7))
|
|
||||||
{
|
|
||||||
if (bonuscnt == 3)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed);
|
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed);
|
||||||
GameText((320 >> 2) + 70, 94 + 9, tempbuf, 0);
|
GameText((320 >> 2) + 70, 94 + 9, tempbuf, 0);
|
||||||
|
|
||||||
|
@ -887,20 +982,14 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintSecrets(int currentclock)
|
void PrintSecrets()
|
||||||
{
|
{
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
GameText(10, 119 + 9, GStrings("TXT_SECFND"), 0);
|
GameText(10, 119 + 9, GStrings("TXT_SECFND"), 0);
|
||||||
GameText(10, 129 + 9, GStrings("TXT_SECMISS"), 0);
|
GameText(10, 129 + 9, GStrings("TXT_SECMISS"), 0);
|
||||||
if (bonuscnt == 4) bonuscnt++;
|
|
||||||
|
|
||||||
if (currentclock > (60 * 10))
|
if (displaystate & printSecretsVal)
|
||||||
{
|
{
|
||||||
if (bonuscnt == 5)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms);
|
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms);
|
||||||
GameText((320 >> 2) + 70, 119 + 9, tempbuf, 0);
|
GameText((320 >> 2) + 70, 119 + 9, tempbuf, 0);
|
||||||
if (ps[myconnectindex].secret_rooms > 0)
|
if (ps[myconnectindex].secret_rooms > 0)
|
||||||
|
@ -910,41 +999,31 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
if (clock == 0) S_PlayBonusMusic();
|
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
|
||||||
DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
|
|
||||||
GameText(160, 190, GStrings("PRESSKEY"), 8 - int(sin(currentclock / 10.) * 8), 0);
|
GameText(160, 190, GStrings("PRESSKEY"), 8 - int(sin(ticks * 12 / GameTicRate) * 8), 0);
|
||||||
|
|
||||||
if (currentclock > (60 * 3))
|
if (displaystate & printTimeText)
|
||||||
{
|
{
|
||||||
PrintTime(currentclock);
|
PrintTime();
|
||||||
}
|
}
|
||||||
if (currentclock > (60 * 6))
|
if (displaystate & printKillsText)
|
||||||
{
|
{
|
||||||
PrintKills(currentclock);
|
PrintKills();
|
||||||
}
|
}
|
||||||
if (currentclock > (60 * 9))
|
if (displaystate & printSecretsText)
|
||||||
{
|
{
|
||||||
PrintSecrets(currentclock);
|
PrintSecrets();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentclock >= (1000000000L) && currentclock < (1000000320L))
|
if (displaystate & dukeAnim)
|
||||||
{
|
{
|
||||||
switch ((currentclock >> 4) % 15)
|
switch (((ticks - dukeAnimStart) >> 2) % 15)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
if (bonuscnt == 6)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(SHOTGUN_COCK, CHAN_AUTO, CHANF_UI);
|
|
||||||
static const uint16_t speeches[] = { BONUS_SPEECH1, BONUS_SPEECH2, BONUS_SPEECH3, BONUS_SPEECH4};
|
|
||||||
speech = speeches[(rand() & 3)];
|
|
||||||
S_PlaySound(speech, CHAN_AUTO, CHANF_UI, 1);
|
|
||||||
}
|
|
||||||
case 1:
|
case 1:
|
||||||
case 4:
|
case 4:
|
||||||
case 5:
|
case 5:
|
||||||
|
@ -956,14 +1035,9 @@ public:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (currentclock > (10240 + 120L))
|
else if (!(displaystate & dukeWait))
|
||||||
{
|
{
|
||||||
if (speech > 0 && !skiprequest && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, speech)) return 1;
|
switch((ticks >> 3) & 3)
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch((currentclock >> 5) & 3)
|
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
case 3:
|
case 3:
|
||||||
|
@ -977,26 +1051,6 @@ public:
|
||||||
|
|
||||||
if (lastmapname) BigText(160, 20 - 6, lastmapname);
|
if (lastmapname) BigText(160, 20 - 6, lastmapname);
|
||||||
BigText(160, 36 - 6, GStrings("Completed"));
|
BigText(160, 36 - 6, GStrings("Completed"));
|
||||||
|
|
||||||
if (currentclock > 10240 && currentclock < 10240 + 10240)
|
|
||||||
SetTotalClock(1024);
|
|
||||||
|
|
||||||
if (skiprequest && currentclock > (60 * 2))
|
|
||||||
{
|
|
||||||
skiprequest = false;
|
|
||||||
if (currentclock < (60 * 13))
|
|
||||||
{
|
|
||||||
SetTotalClock(60 * 13);
|
|
||||||
}
|
|
||||||
else if (currentclock < (1000000000))
|
|
||||||
{
|
|
||||||
// force-set bonuscnt here so that it won't desync with the rest of the logic and Duke's voice can be heard.
|
|
||||||
if (bonuscnt < 6) bonuscnt = 6;
|
|
||||||
SetTotalClock(1000000000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1030,7 +1084,9 @@ void dobonus_d(int bonusonly, const CompletionFunc& completion)
|
||||||
jobs[job++] = { Create<DDukeLevelSummaryScreen>() };
|
jobs[job++] = { Create<DDukeLevelSummaryScreen>() };
|
||||||
}
|
}
|
||||||
if (job)
|
if (job)
|
||||||
|
{
|
||||||
RunScreenJob(jobs, job, completion);
|
RunScreenJob(jobs, job, completion);
|
||||||
|
}
|
||||||
else if (completion) completion(false);
|
else if (completion) completion(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,14 +1148,13 @@ class DDukeLoadScreen : public DScreenJob
|
||||||
public:
|
public:
|
||||||
DDukeLoadScreen(MapRecord *maprec) : DScreenJob(0), rec(maprec) {}
|
DDukeLoadScreen(MapRecord *maprec) : DScreenJob(0), rec(maprec) {}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
|
|
||||||
BigText(160, 90, (rec->flags & MI_USERMAP)? GStrings("TXT_LOADUM") : GStrings("TXT_LOADING"));
|
BigText(160, 90, (rec->flags & MI_USERMAP)? GStrings("TXT_LOADUM") : GStrings("TXT_LOADING"));
|
||||||
BigText(160, 114, rec->DisplayName());
|
BigText(160, 114, rec->DisplayName());
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -266,9 +266,14 @@ public:
|
||||||
playerswhenstarted = pws;
|
playerswhenstarted = pws;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Start() override
|
||||||
|
{
|
||||||
|
S_PlayBonusMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
if (clock == 0) S_PlayBonusMusic();
|
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
|
@ -344,7 +349,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0);
|
MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0);
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -358,13 +362,23 @@ class DRRLevelSummaryScreen : public DScreenJob
|
||||||
{
|
{
|
||||||
const char* lastmapname;
|
const char* lastmapname;
|
||||||
int gfx_offset;
|
int gfx_offset;
|
||||||
int bonuscnt = 0;
|
int displaystate = 0;
|
||||||
int speech = -1;
|
int speech = -1;
|
||||||
|
int exitSoundStart;
|
||||||
|
|
||||||
void SetTotalClock(int tc)
|
enum
|
||||||
{
|
{
|
||||||
SetClock(tc * (uint64_t)1'000'000'000 / 120);
|
printTimeText = 1,
|
||||||
}
|
printTimeVal = 2,
|
||||||
|
printKillsText = 4,
|
||||||
|
printKillsVal = 8,
|
||||||
|
printSecretsText = 16,
|
||||||
|
printSecretsVal = 32,
|
||||||
|
printStatsAll = 63,
|
||||||
|
exitSound = 64,
|
||||||
|
exitWait = 128,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DRRLevelSummaryScreen(bool dofadeout = true) : DScreenJob(dofadeout? (fadein | fadeout) : fadein)
|
DRRLevelSummaryScreen(bool dofadeout = true) : DScreenJob(dofadeout? (fadein | fadeout) : fadein)
|
||||||
|
@ -385,53 +399,107 @@ public:
|
||||||
mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60);
|
mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintTime(int currentclock)
|
bool OnEvent(event_t* ev) override
|
||||||
|
{
|
||||||
|
if (ev->type == EV_KeyDown)
|
||||||
|
{
|
||||||
|
if ((displaystate & printStatsAll) != printStatsAll)
|
||||||
|
{
|
||||||
|
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||||
|
displaystate = printStatsAll;
|
||||||
|
}
|
||||||
|
else if (!(displaystate & exitSound))
|
||||||
|
{
|
||||||
|
displaystate |= exitSound;
|
||||||
|
exitSoundStart = ticks;
|
||||||
|
S_PlaySound(425, CHAN_AUTO, CHANF_UI);
|
||||||
|
speech = BONUS_SPEECH1 + (rand() & 3);
|
||||||
|
S_PlaySound(speech, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
S_PlayBonusMusic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
|
{
|
||||||
|
if ((displaystate & printStatsAll) != printStatsAll)
|
||||||
|
{
|
||||||
|
if (ticks == 15 * 3)
|
||||||
|
{
|
||||||
|
displaystate |= printTimeText;
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 4)
|
||||||
|
{
|
||||||
|
displaystate |= printTimeVal;
|
||||||
|
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 6)
|
||||||
|
{
|
||||||
|
displaystate |= printKillsText;
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 7)
|
||||||
|
{
|
||||||
|
displaystate |= printKillsVal;
|
||||||
|
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 9)
|
||||||
|
{
|
||||||
|
displaystate |= printSecretsText;
|
||||||
|
}
|
||||||
|
else if (ticks == 15 * 10)
|
||||||
|
{
|
||||||
|
displaystate |= printSecretsVal;
|
||||||
|
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (displaystate & exitSound)
|
||||||
|
{
|
||||||
|
if (ticks >= exitSoundStart + 60)
|
||||||
|
{
|
||||||
|
displaystate ^= exitSound | exitWait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (displaystate & exitWait)
|
||||||
|
{
|
||||||
|
if (speech <= 0 || !S_CheckSoundPlaying(speech))
|
||||||
|
state = finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintTime()
|
||||||
{
|
{
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
BigText(30, 48, GStrings("TXT_YerTime"), -1);
|
BigText(30, 48, GStrings("TXT_YerTime"), -1);
|
||||||
BigText(30, 64, GStrings("TXT_ParTime"), -1);
|
BigText(30, 64, GStrings("TXT_ParTime"), -1);
|
||||||
BigText(30, 80, GStrings("TXT_XTRTIME"), -1);
|
BigText(30, 80, GStrings("TXT_XTRTIME"), -1);
|
||||||
|
|
||||||
if (bonuscnt == 0)
|
if (displaystate & printTimeVal)
|
||||||
bonuscnt++;
|
|
||||||
|
|
||||||
if (currentclock > (60 * 4))
|
|
||||||
{
|
{
|
||||||
if (bonuscnt == 1)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
FormatTime(ps[myconnectindex].player_par, tempbuf);
|
FormatTime(ps[myconnectindex].player_par, tempbuf);
|
||||||
BigText(191, 48, tempbuf, -1);
|
BigText(191, 48, tempbuf, -1);
|
||||||
|
|
||||||
FormatTime(currentLevel->parTime, tempbuf);
|
FormatTime(currentLevel->parTime, tempbuf);
|
||||||
BigText(191, 64, tempbuf, -1);
|
BigText(191, 64, tempbuf, -1);
|
||||||
|
|
||||||
if (!isNamWW2GI())
|
FormatTime(currentLevel->designerTime, tempbuf);
|
||||||
{
|
BigText(191, 80, tempbuf, -1);
|
||||||
FormatTime(currentLevel->designerTime, tempbuf);
|
|
||||||
BigText(191, 80, tempbuf, -1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintKills(int currentclock)
|
void PrintKills()
|
||||||
{
|
{
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
BigText(30, 112, GStrings("TXT_VarmintsKilled"), -1);
|
BigText(30, 112, GStrings("TXT_VarmintsKilled"), -1);
|
||||||
BigText(30, 128, GStrings("TXT_VarmintsLeft"), -1);
|
BigText(30, 128, GStrings("TXT_VarmintsLeft"), -1);
|
||||||
|
|
||||||
if (bonuscnt == 2)
|
if (displaystate & printKillsVal)
|
||||||
bonuscnt++;
|
|
||||||
|
|
||||||
if (currentclock > (60 * 7))
|
|
||||||
{
|
{
|
||||||
if (bonuscnt == 3)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(442, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed);
|
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed);
|
||||||
BigText(231, 112, tempbuf, -1);
|
BigText(231, 112, tempbuf, -1);
|
||||||
if (ud.player_skill > 3)
|
if (ud.player_skill > 3)
|
||||||
|
@ -449,20 +517,14 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintSecrets(int currentclock)
|
void PrintSecrets()
|
||||||
{
|
{
|
||||||
char tempbuf[32];
|
char tempbuf[32];
|
||||||
BigText(30, 144, GStrings("TXT_SECFND"), -1);
|
BigText(30, 144, GStrings("TXT_SECFND"), -1);
|
||||||
BigText(30, 160, GStrings("TXT_SECMISS"), -1);
|
BigText(30, 160, GStrings("TXT_SECMISS"), -1);
|
||||||
if (bonuscnt == 4) bonuscnt++;
|
|
||||||
|
|
||||||
if (currentclock > (60 * 10))
|
if (displaystate & printSecretsVal)
|
||||||
{
|
{
|
||||||
if (bonuscnt == 5)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms);
|
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms);
|
||||||
BigText(231, 144, tempbuf, -1);
|
BigText(231, 144, tempbuf, -1);
|
||||||
if (ps[myconnectindex].secret_rooms > 0)
|
if (ps[myconnectindex].secret_rooms > 0)
|
||||||
|
@ -472,88 +534,56 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
if (clock == 0) S_PlayBonusMusic();
|
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
|
||||||
DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
|
|
||||||
if (lastmapname) BigText(80, 16, lastmapname, -1);
|
if (lastmapname) BigText(80, 16, lastmapname, -1);
|
||||||
BigText(15, 192, GStrings("PRESSKEY"), -1);
|
BigText(15, 192, GStrings("PRESSKEY"), -1);
|
||||||
|
|
||||||
if (currentclock > (60 * 3))
|
if (displaystate & printTimeText)
|
||||||
{
|
{
|
||||||
PrintTime(currentclock);
|
PrintTime();
|
||||||
}
|
}
|
||||||
if (currentclock > (60 * 6))
|
if (displaystate & printKillsText)
|
||||||
{
|
{
|
||||||
PrintKills(currentclock);
|
PrintKills();
|
||||||
}
|
}
|
||||||
if (currentclock > (60 * 9))
|
if (displaystate & printSecretsText)
|
||||||
{
|
{
|
||||||
PrintSecrets(currentclock);
|
PrintSecrets();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentclock > (1000000000L) && currentclock < (1000000320L))
|
|
||||||
{
|
|
||||||
int val = (currentclock >> 4) % 15;
|
|
||||||
if (val == 0)
|
|
||||||
{
|
|
||||||
if (bonuscnt == 6)
|
|
||||||
{
|
|
||||||
bonuscnt++;
|
|
||||||
S_PlaySound(425, CHAN_AUTO, CHANF_UI);
|
|
||||||
speech = BONUS_SPEECH1 + (rand() & 3);
|
|
||||||
S_PlaySound(speech, CHAN_AUTO, CHANF_UI);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (currentclock > (10240 + 120L))
|
|
||||||
{
|
|
||||||
if (speech > 0 && !skiprequest && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, speech)) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentclock > 10240 && currentclock < 10240 + 10240)
|
|
||||||
SetTotalClock(1024);
|
|
||||||
|
|
||||||
if (skiprequest && currentclock > (60 * 2))
|
|
||||||
{
|
|
||||||
skiprequest = false;
|
|
||||||
if (currentclock < (60 * 13))
|
|
||||||
{
|
|
||||||
SetTotalClock(60 * 13);
|
|
||||||
}
|
|
||||||
else if (currentclock < (1000000000))
|
|
||||||
SetTotalClock(1000000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class DRRRAEndOfGame : public DScreenJob
|
class DRRRAEndOfGame : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DRRRAEndOfGame() : DScreenJob(fadein|fadeout)
|
DRRRAEndOfGame() : DSkippableScreenJob(fadein|fadeout)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skipped() override
|
||||||
|
{
|
||||||
|
S_StopSound(35);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
{
|
{
|
||||||
S_PlaySound(35, CHAN_AUTO, CHANF_UI);
|
S_PlaySound(35, CHAN_AUTO, CHANF_UI);
|
||||||
}
|
}
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
|
||||||
|
void OnTick() override
|
||||||
{
|
{
|
||||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
if (!S_CheckSoundPlaying(-1, 35) && ticks > 15 * GameTicRate) state = finished; // make sure it stays, even if sound is off.
|
||||||
auto tex = tileGetTexture(ENDGAME + ((currentclock >> 4) & 1));
|
}
|
||||||
|
void Draw(double) override
|
||||||
|
{
|
||||||
|
auto tex = tileGetTexture(ENDGAME + ((ticks >> 2) & 1));
|
||||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||||
if (!S_CheckSoundPlaying(-1, 35) && currentclock > 15*120) return 0; // make sure it stays, even if sound is off.
|
|
||||||
if (skiprequest)
|
|
||||||
{
|
|
||||||
S_StopSound(35);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -617,14 +647,13 @@ class DRRLoadScreen : public DScreenJob
|
||||||
public:
|
public:
|
||||||
DRRLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
|
DRRLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
|
|
||||||
int y = isRRRA()? 140 : 90;
|
int y = isRRRA()? 140 : 90;
|
||||||
BigText(160, y, (rec->flags & MI_USERMAP) ? GStrings("TXT_ENTRUM") : GStrings("TXT_ENTERIN"), 0);
|
BigText(160, y, (rec->flags & MI_USERMAP) ? GStrings("TXT_ENTRUM") : GStrings("TXT_ENTERIN"), 0);
|
||||||
BigText(160, y+24, rec->DisplayName(), 0);
|
BigText(160, y+24, rec->DisplayName(), 0);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1014,7 +1014,7 @@ void movemasterswitch(DDukeActor *actor, int spectype1, int spectype2)
|
||||||
// This originally depended on undefined behavior as the deleted sprite was still used for the sound
|
// This originally depended on undefined behavior as the deleted sprite was still used for the sound
|
||||||
// with no checking if it got reused in the mean time.
|
// with no checking if it got reused in the mean time.
|
||||||
spri->picnum = 0; // give it a picnum without any behavior attached, just in case
|
spri->picnum = 0; // give it a picnum without any behavior attached, just in case
|
||||||
spri->cstat |= CSTAT_SPRITE_INVISIBLE;
|
spri->cstat |= CSTAT_SPRITE_INVISIBLE|CSTAT_SPRITE_NOFIND;
|
||||||
changespritestat(actor->GetIndex(), STAT_REMOVED);
|
changespritestat(actor->GetIndex(), STAT_REMOVED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
#include "m_random.h"
|
#include "m_random.h"
|
||||||
#include "gstrings.h"
|
#include "gstrings.h"
|
||||||
#include "gamefuncs.h"
|
#include "gamefuncs.h"
|
||||||
|
#include "c_bind.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -383,13 +384,23 @@ class DLobotomyScreen : public DImageScreen
|
||||||
public:
|
public:
|
||||||
DLobotomyScreen(FGameTexture *tex, int fade) : DImageScreen(tex, fade)
|
DLobotomyScreen(FGameTexture *tex, int fade) : DImageScreen(tex, fade)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
void Skipped() override
|
||||||
{
|
{
|
||||||
if (clock == 0) PlayLocalSound(StaticSound[kSoundJonLaugh2], 7000, false, CHANF_UI);
|
StopLocalSound();
|
||||||
if (skiprequest) StopLocalSound();
|
|
||||||
return DImageScreen::Frame(clock, skiprequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
PlayLocalSound(StaticSound[kSoundJonLaugh2], 7000, false, CHANF_UI);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
|
{
|
||||||
|
|
||||||
|
DImageScreen::OnTick();
|
||||||
|
if (state == finished) StopLocalSound();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -400,12 +411,12 @@ public:
|
||||||
|
|
||||||
static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 };
|
static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 };
|
||||||
|
|
||||||
class DMainTitle : public DScreenJob
|
class DMainTitle : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
const char* a;
|
const char* a;
|
||||||
const char* b;
|
const char* b;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
int var_18;
|
int duration;
|
||||||
int var_4 = 0;
|
int var_4 = 0;
|
||||||
int esi = 130;
|
int esi = 130;
|
||||||
int nCount = 0;
|
int nCount = 0;
|
||||||
|
@ -413,56 +424,65 @@ class DMainTitle : public DScreenJob
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DMainTitle() : DScreenJob(fadein)
|
DMainTitle() : DSkippableScreenJob(fadein)
|
||||||
{
|
{
|
||||||
a = GStrings("TXT_EX_COPYRIGHT1");
|
a = GStrings("TXT_EX_COPYRIGHT1");
|
||||||
b = GStrings("TXT_EX_COPYRIGHT2");
|
b = GStrings("TXT_EX_COPYRIGHT2");
|
||||||
var_18 = skullDurations[0];
|
duration = skullDurations[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
void Start() override
|
||||||
{
|
{
|
||||||
int ticker = clock * 120 / 1'000'000'000;
|
|
||||||
if (clock == 0)
|
|
||||||
{
|
|
||||||
PlayLocalSound(StaticSound[59], 0, true, CHANF_UI);
|
PlayLocalSound(StaticSound[59], 0, true, CHANF_UI);
|
||||||
playCDtrack(19, true);
|
playCDtrack(19, true);
|
||||||
}
|
}
|
||||||
if (clock > 1'000'000 && state == 0 && !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr,CHAN_AUTO, -1))
|
|
||||||
|
void OnTick() override
|
||||||
{
|
{
|
||||||
if (time(0) & 0xF)
|
int ticker = ticks * 120 / GameTicRate;
|
||||||
|
if (ticks > 1 && state == 0 && !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1))
|
||||||
|
{
|
||||||
|
if (time(0) & 0xF) // cheap-ass random...
|
||||||
PlayGameOverSound();
|
PlayGameOverSound();
|
||||||
else
|
else
|
||||||
PlayLocalSound(StaticSound[61], 0, false, CHANF_UI);
|
PlayLocalSound(StaticSound[61], 0, false, CHANF_UI);
|
||||||
state = 1;
|
state = 1;
|
||||||
start = ticker;
|
start = ticker;
|
||||||
}
|
}
|
||||||
|
if (state == 1)
|
||||||
|
{
|
||||||
|
if (ticker > duration)
|
||||||
|
{
|
||||||
|
nCount++;
|
||||||
|
if (nCount > 12)
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
duration = start + skullDurations[nCount];
|
||||||
|
var_4 = var_4 == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double) override
|
||||||
|
{
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
|
|
||||||
menu_DoPlasma();
|
menu_DoPlasma();
|
||||||
|
|
||||||
DrawRel(kSkullHead, 160, 100);
|
DrawRel(kSkullHead, 160, 100);
|
||||||
switch (state)
|
if (state == 0)
|
||||||
{
|
{
|
||||||
case 0:
|
|
||||||
DrawRel(kSkullJaw, 161, 130);
|
DrawRel(kSkullJaw, 161, 130);
|
||||||
break;
|
}
|
||||||
|
else
|
||||||
case 1:
|
|
||||||
{
|
{
|
||||||
int nStringWidth = SmallFont->StringWidth(a);
|
int nStringWidth = SmallFont->StringWidth(a);
|
||||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||||
nStringWidth = SmallFont->StringWidth(b);
|
nStringWidth = SmallFont->StringWidth(b);
|
||||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||||
|
|
||||||
if (ticker > var_18)
|
|
||||||
{
|
|
||||||
nCount++;
|
|
||||||
if (nCount > 12) return 0;
|
|
||||||
var_18 = start + skullDurations[nCount];
|
|
||||||
var_4 = var_4 == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
short nTile = kSkullJaw;
|
short nTile = kSkullJaw;
|
||||||
|
|
||||||
|
@ -487,11 +507,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawRel(nTile, 161, y);
|
DrawRel(nTile, 161, y);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return skiprequest? -1 : 1;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -618,10 +635,8 @@ class DMapScreen : public DScreenJob
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int var_2C = 0;
|
int delta = 0;
|
||||||
int nIdleSeconds = 0;
|
int nIdleSeconds = 0;
|
||||||
int startTime = 0;
|
|
||||||
int runtimer = 0;
|
|
||||||
|
|
||||||
int curYPos, destYPos;
|
int curYPos, destYPos;
|
||||||
int nLevel, nLevelNew, nLevelBest;
|
int nLevel, nLevelNew, nLevelBest;
|
||||||
|
@ -633,11 +648,11 @@ public:
|
||||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||||
|
|
||||||
if (curYPos < destYPos) {
|
if (curYPos < destYPos) {
|
||||||
var_2C = 2;
|
delta = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curYPos > destYPos) {
|
if (curYPos > destYPos) {
|
||||||
var_2C = -2;
|
delta = -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim smoke in widescreen
|
// Trim smoke in widescreen
|
||||||
|
@ -651,20 +666,13 @@ public:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
void Draw(double smoothratio)
|
||||||
|
|
||||||
{
|
{
|
||||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
int currentclock = int((ticks + smoothratio) * 120 / GameTicRate);
|
||||||
|
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
|
|
||||||
if ((currentclock - startTime) / kTimerTicks)
|
|
||||||
{
|
|
||||||
nIdleSeconds++;
|
|
||||||
startTime = currentclock;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tileY = curYPos;
|
int tileY = curYPos;
|
||||||
|
|
||||||
// Draw the background screens
|
// Draw the background screens
|
||||||
|
@ -728,84 +736,82 @@ public:
|
||||||
DrawAbs(nTile, textX, textY, shade);
|
DrawAbs(nTile, textX, textY, shade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectedlevelnew = nLevelNew + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
|
{
|
||||||
if (curYPos != destYPos)
|
if (curYPos != destYPos)
|
||||||
{
|
{
|
||||||
// scroll the map every couple of ms
|
// scroll the map every couple of ms
|
||||||
if (currentclock - runtimer >= (kTimerTicks / 32)) {
|
curYPos += delta;
|
||||||
curYPos += var_2C;
|
|
||||||
runtimer = currentclock;
|
if (curYPos > destYPos && delta > 0) {
|
||||||
}
|
|
||||||
|
|
||||||
if (inputState.CheckAllInput())
|
|
||||||
{
|
|
||||||
if (var_2C < 8) {
|
|
||||||
var_2C *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curYPos > destYPos&& var_2C > 0) {
|
|
||||||
curYPos = destYPos;
|
curYPos = destYPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (curYPos < destYPos && var_2C < 0) {
|
if (curYPos < destYPos && delta < 0) {
|
||||||
curYPos = destYPos;
|
curYPos = destYPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
nIdleSeconds = 0;
|
nIdleSeconds = 0;
|
||||||
}
|
}
|
||||||
selectedlevelnew = nLevelNew + 1;
|
else nIdleSeconds++;
|
||||||
return skiprequest? -1 : nIdleSeconds < 12? 1 : 0;
|
if (nIdleSeconds > 300) state = finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessInput() override
|
bool OnEvent(event_t* ev) override
|
||||||
{
|
{
|
||||||
if (buttonMap.ButtonDown(gamefunc_Move_Forward))
|
int key = ev->data1;
|
||||||
|
if (ev->type == EV_KeyDown)
|
||||||
{
|
{
|
||||||
buttonMap.ClearButton(gamefunc_Move_Forward);
|
auto binding = Bindings.GetBinding(ev->data1);
|
||||||
|
if (!binding.CompareNoCase("+move_forward")) key = KEY_UPARROW;
|
||||||
|
if (!binding.CompareNoCase("+move_backward")) key = KEY_DOWNARROW;
|
||||||
|
|
||||||
|
if (key == KEY_UPARROW || key == KEY_PAD_DPAD_UP || key == sc_kpad_8)
|
||||||
|
{
|
||||||
if (curYPos == destYPos && nLevelNew <= nLevelBest)
|
if (curYPos == destYPos && nLevelNew <= nLevelBest)
|
||||||
{
|
{
|
||||||
nLevelNew++;
|
nLevelNew++;
|
||||||
assert(nLevelNew < 20);
|
assert(nLevelNew < 20);
|
||||||
|
|
||||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||||
|
|
||||||
if (curYPos <= destYPos) {
|
if (curYPos <= destYPos) {
|
||||||
var_2C = 2;
|
delta = 2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var_2C = -2;
|
delta = -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
nIdleSeconds = 0;
|
nIdleSeconds = 0;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonMap.ButtonDown(gamefunc_Move_Backward))
|
if (key == KEY_DOWNARROW || key == KEY_PAD_DPAD_DOWN || key == sc_kpad_2)
|
||||||
{
|
{
|
||||||
buttonMap.ClearButton(gamefunc_Move_Backward);
|
|
||||||
|
|
||||||
if (curYPos == destYPos && nLevelNew > 0)
|
if (curYPos == destYPos && nLevelNew > 0)
|
||||||
{
|
{
|
||||||
nLevelNew--;
|
nLevelNew--;
|
||||||
assert(nLevelNew >= 0);
|
assert(nLevelNew >= 0);
|
||||||
|
|
||||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||||
|
|
||||||
if (curYPos <= destYPos) {
|
if (curYPos <= destYPos) {
|
||||||
var_2C = 2;
|
delta = 2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var_2C = -2;
|
delta = -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
nIdleSeconds = 0;
|
nIdleSeconds = 0;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
state = skipped;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -963,16 +969,17 @@ void uploadCinemaPalettes()
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DCinema : public DScreenJob
|
class DCinema : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
TextOverlay text;
|
TextOverlay text;
|
||||||
short cinematile;
|
short cinematile;
|
||||||
int currentCinemaPalette;
|
int currentCinemaPalette;
|
||||||
int edx;
|
int edx;
|
||||||
int check;
|
int check;
|
||||||
|
int cont = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DCinema(int nVal, int checklevel = -1) : DScreenJob(fadein|fadeout)
|
DCinema(int nVal, int checklevel = -1) : DSkippableScreenJob(fadein|fadeout)
|
||||||
{
|
{
|
||||||
if (nVal < 0 || nVal >5) return;
|
if (nVal < 0 || nVal >5) return;
|
||||||
cinematile = cinemas[nVal].tile;
|
cinematile = cinemas[nVal].tile;
|
||||||
|
@ -984,12 +991,15 @@ public:
|
||||||
check = checklevel;
|
check = checklevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
void Start() override
|
||||||
{
|
{
|
||||||
|
if (check > 0 && check != selectedlevelnew)
|
||||||
if (clock == 0)
|
|
||||||
{
|
{
|
||||||
if (check > 0 && check != selectedlevelnew) return 0; // immediately abort if the player selected a different level on the map
|
state = finished;
|
||||||
|
return; // immediately abort if the player selected a different level on the map
|
||||||
|
}
|
||||||
|
|
||||||
|
check = -1;
|
||||||
StopAllSounds();
|
StopAllSounds();
|
||||||
if (edx != -1)
|
if (edx != -1)
|
||||||
{
|
{
|
||||||
|
@ -997,20 +1007,29 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
|
{
|
||||||
|
if (!cont)
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
|
// quit the game if we've finished level 4 and displayed the advert text
|
||||||
|
if (isShareware() && currentCinemaPalette == 3)
|
||||||
|
{
|
||||||
|
gameaction = ga_mainmenu;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double smoothratio) override
|
||||||
|
{
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
|
if (check == 0) return;
|
||||||
DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE);
|
DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE);
|
||||||
|
|
||||||
text.DisplayText();
|
text.DisplayText();
|
||||||
auto cont = text.AdvanceCinemaText(clock * (120. / 1'000'000'000));
|
cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate));
|
||||||
int ret = skiprequest ? -1 : cont ? 1 : 0;
|
|
||||||
|
|
||||||
// quit the game if we've finished level 4 and displayed the advert text
|
|
||||||
if (isShareware() && currentCinemaPalette == 3 && ret != 1)
|
|
||||||
{
|
|
||||||
gameaction = ga_mainmenu;
|
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -1029,6 +1048,7 @@ class DLastLevelCinema : public DScreenJob
|
||||||
int nextclock = 4;
|
int nextclock = 4;
|
||||||
unsigned int nStringTypeOn, nCharTypeOn;
|
unsigned int nStringTypeOn, nCharTypeOn;
|
||||||
int screencnt = 0;
|
int screencnt = 0;
|
||||||
|
bool skiprequest = false;
|
||||||
|
|
||||||
TArray<FString> screentext;
|
TArray<FString> screentext;
|
||||||
|
|
||||||
|
@ -1123,7 +1143,7 @@ private:
|
||||||
int yy = ebp;
|
int yy = ebp;
|
||||||
|
|
||||||
auto p = GStrings["REQUIRED_CHARACTERS"];
|
auto p = GStrings["REQUIRED_CHARACTERS"];
|
||||||
if (1)//p && *p)
|
if (p && *p)
|
||||||
{
|
{
|
||||||
yy *= 2;
|
yy *= 2;
|
||||||
for (int i = 0; i < nStringTypeOn; i++, yy += 10)
|
for (int i = 0; i < nStringTypeOn; i++, yy += 10)
|
||||||
|
@ -1142,91 +1162,94 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
bool OnEvent(event_t* ev)
|
||||||
{
|
{
|
||||||
if (clock == 0)
|
if (ev->type == EV_KeyDown) skiprequest = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
{
|
{
|
||||||
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
||||||
phase = 1;
|
phase = 1;
|
||||||
}
|
}
|
||||||
int currentclock = clock * 120 / 1'000'000'000;
|
|
||||||
twod->ClearScreen();
|
void OnTick() override
|
||||||
DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
{
|
||||||
switch (phase)
|
switch (phase)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
if (currentclock >= nextclock)
|
|
||||||
{
|
|
||||||
Phase1();
|
Phase1();
|
||||||
nextclock += 4;
|
if (skiprequest || ticks >= nextclock)
|
||||||
}
|
|
||||||
if (skiprequest || currentclock >= 240)
|
|
||||||
{
|
{
|
||||||
InitPhase2();
|
InitPhase2();
|
||||||
phase = 2;
|
phase = 2;
|
||||||
skiprequest = 0;
|
skiprequest = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
if (currentclock >= nextclock)
|
|
||||||
{
|
|
||||||
if (screentext[nStringTypeOn][nCharTypeOn] != ' ')
|
if (screentext[nStringTypeOn][nCharTypeOn] != ' ')
|
||||||
PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI);
|
PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI);
|
||||||
|
|
||||||
nCharTypeOn++;
|
nCharTypeOn++;
|
||||||
nextclock += 4;
|
|
||||||
if (screentext[nStringTypeOn][nCharTypeOn] == 0)
|
if (screentext[nStringTypeOn][nCharTypeOn] == 0)
|
||||||
{
|
{
|
||||||
nCharTypeOn = 0;
|
nCharTypeOn = 0;
|
||||||
nStringTypeOn++;
|
nStringTypeOn++;
|
||||||
if (nStringTypeOn >= screentext.Size())
|
if (nStringTypeOn >= screentext.Size())
|
||||||
{
|
{
|
||||||
nextclock = (kTimerTicks * (screentext.Size() + 2)) + currentclock;
|
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||||
phase = 3;
|
phase = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
DisplayPhase2();
|
|
||||||
if (skiprequest)
|
if (skiprequest)
|
||||||
{
|
{
|
||||||
nextclock = (kTimerTicks * (screentext.Size() + 2)) + currentclock;
|
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||||
phase = 4;
|
phase = 4;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
DisplayPhase2();
|
if (ticks >= nextclock || skiprequest)
|
||||||
if (currentclock >= nextclock || skiprequest)
|
|
||||||
{
|
{
|
||||||
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
||||||
phase = 4;
|
phase = 4;
|
||||||
nextclock = currentclock + 240;
|
nextclock = ticks + 60;
|
||||||
skiprequest = 0;
|
skiprequest = false;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
if (currentclock >= nextclock)
|
if (ticks >= nextclock)
|
||||||
{
|
{
|
||||||
skiprequest |= !Phase3();
|
skiprequest |= !Phase3();
|
||||||
nextclock += 4;
|
|
||||||
}
|
}
|
||||||
if (skiprequest || currentclock >= 240)
|
if (skiprequest)
|
||||||
{
|
{
|
||||||
// Go to the next text page.
|
// Go to the next text page.
|
||||||
if (screencnt != 2)
|
if (screencnt != 2)
|
||||||
{
|
{
|
||||||
screencnt++;
|
screencnt++;
|
||||||
nextclock = currentclock + 240;
|
nextclock = ticks + 60;
|
||||||
skiprequest = 0;
|
skiprequest = 0;
|
||||||
phase = 1;
|
phase = 1;
|
||||||
}
|
}
|
||||||
else return skiprequest ? -1 : 0;
|
else state = finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (skiprequest)
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
}
|
}
|
||||||
return 1;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double) override
|
||||||
|
{
|
||||||
|
twod->ClearScreen();
|
||||||
|
DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||||
|
if (phase == 2 || phase == 3) DisplayPhase2();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -1243,6 +1266,7 @@ class DExCredits : public DScreenJob
|
||||||
TArray<FString> pagelines;
|
TArray<FString> pagelines;
|
||||||
uint64_t page;
|
uint64_t page;
|
||||||
uint64_t pagetime;
|
uint64_t pagetime;
|
||||||
|
bool skiprequest = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DExCredits()
|
DExCredits()
|
||||||
|
@ -1253,35 +1277,53 @@ public:
|
||||||
credits = text.Split("\n\n");
|
credits = text.Split("\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
bool OnEvent(event_t* ev)
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
|
||||||
{
|
{
|
||||||
if (clock == 0)
|
if (ev->type == EV_KeyDown) skiprequest = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
{
|
{
|
||||||
if (credits.Size() == 0) return 0;
|
if (credits.Size() == 0)
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
|
return;
|
||||||
|
}
|
||||||
playCDtrack(19, false);
|
playCDtrack(19, false);
|
||||||
pagetime = 0;
|
pagetime = 0;
|
||||||
page = -1;
|
page = -1;
|
||||||
}
|
}
|
||||||
if (clock >= pagetime || skiprequest)
|
|
||||||
|
void OnTick() override
|
||||||
|
{
|
||||||
|
if (ticks >= pagetime || skiprequest)
|
||||||
{
|
{
|
||||||
page++;
|
page++;
|
||||||
if (page < credits.Size())
|
if (page < credits.Size())
|
||||||
pagelines = credits[page].Split("\n");
|
pagelines = credits[page].Split("\n");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (skiprequest || !CDplaying()) return 0;
|
if (skiprequest || !CDplaying())
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
|
return;
|
||||||
|
}
|
||||||
pagelines.Clear();
|
pagelines.Clear();
|
||||||
}
|
}
|
||||||
pagetime = clock + 2'000'000'000; //
|
pagetime = ticks + 60; //
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double smoothratio) override
|
||||||
|
{
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
|
|
||||||
int y = 100 - ((10 * (pagelines.Size() - 1)) / 2);
|
int y = 100 - ((10 * (pagelines.Size() - 1)) / 2);
|
||||||
|
|
||||||
for (unsigned i = 0; i < pagelines.Size(); i++)
|
for (unsigned i = 0; i < pagelines.Size(); i++)
|
||||||
{
|
{
|
||||||
uint64_t ptime = (pagetime-clock) / 1'000'000;
|
int ptime = clamp((pagetime - ticks - smoothratio) * 1000 / GameTicRate, 0, 2000); // in milliseconds
|
||||||
int light;
|
int light;
|
||||||
|
|
||||||
if (ptime < 255) light = ptime;
|
if (ptime < 255) light = ptime;
|
||||||
|
@ -1294,7 +1336,6 @@ private:
|
||||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, color, TAG_DONE);
|
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, color, TAG_DONE);
|
||||||
y += 10;
|
y += 10;
|
||||||
}
|
}
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,7 @@ static void Intermission(MapRecord *from_map, MapRecord *to_map)
|
||||||
showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs);
|
showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
jobs.Push({ Create<DScreenJob>() }); // we need something in here even in the multiplayer case.
|
jobs.Push({ Create<DBlackScreen>(1) }); // we need something in here even in the multiplayer case.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -193,7 +193,7 @@ static void Intermission(MapRecord *from_map, MapRecord *to_map)
|
||||||
gameaction = ga_nextlevel;
|
gameaction = ga_nextlevel;
|
||||||
|
|
||||||
}
|
}
|
||||||
}, true, true);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -217,15 +217,14 @@ void GameInterface::NewGame(MapRecord *map, int skill, bool frommenu)
|
||||||
// start a new game on the given level
|
// start a new game on the given level
|
||||||
InitNewGame();
|
InitNewGame();
|
||||||
if (map->levelNumber == 1) STAT_StartNewGame("Exhumed", 1);
|
if (map->levelNumber == 1) STAT_StartNewGame("Exhumed", 1);
|
||||||
if (frommenu) Intermission(nullptr, map);
|
Intermission(nullptr, map);
|
||||||
else NextLevel(map, skill);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
||||||
{
|
{
|
||||||
Mus_Stop();
|
Mus_Stop();
|
||||||
if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu;
|
if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu;
|
||||||
else Intermission(currentLevel, map);
|
Intermission(currentLevel, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
|
@ -200,7 +200,7 @@ public:
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DLmfPlayer : public DScreenJob
|
class DLmfPlayer : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
LMFPlayer decoder;
|
LMFPlayer decoder;
|
||||||
double angle = 1536;
|
double angle = 1536;
|
||||||
|
@ -216,6 +216,7 @@ public:
|
||||||
lastclock = 0;
|
lastclock = 0;
|
||||||
nextclock = 0;
|
nextclock = 0;
|
||||||
fp = std::move(fr);
|
fp = std::move(fr);
|
||||||
|
pausable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -224,14 +225,16 @@ public:
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
void Draw(double smoothratio) override
|
||||||
{
|
{
|
||||||
|
uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate;
|
||||||
if (clock >= nextclock)
|
if (clock >= nextclock)
|
||||||
{
|
{
|
||||||
nextclock += 100'000'000;
|
nextclock += 100'000'000;
|
||||||
if (decoder.ReadFrame(fp) == 0)
|
if (decoder.ReadFrame(fp) == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
state = finished;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +257,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
lastclock = clock;
|
lastclock = clock;
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnDestroy() override
|
void OnDestroy() override
|
||||||
|
@ -269,12 +271,11 @@ public:
|
||||||
DScreenJob* PlayMovie(const char* fileName)
|
DScreenJob* PlayMovie(const char* fileName)
|
||||||
{
|
{
|
||||||
// clear keys
|
// clear keys
|
||||||
inputState.ClearAllInput();
|
|
||||||
|
|
||||||
auto fp = fileSystem.OpenFileReader(fileName);
|
auto fp = fileSystem.OpenFileReader(fileName);
|
||||||
if (!fp.isOpen())
|
if (!fp.isOpen())
|
||||||
{
|
{
|
||||||
return Create<DScreenJob>();
|
return Create<DBlackScreen>(1);
|
||||||
}
|
}
|
||||||
char buffer[4];
|
char buffer[4];
|
||||||
fp.Read(buffer, 4);
|
fp.Read(buffer, 4);
|
||||||
|
|
|
@ -1032,14 +1032,13 @@ void FuncPlayer(int a, int nDamage, int nRun)
|
||||||
StopLocalSound();
|
StopLocalSound();
|
||||||
InitSpiritHead();
|
InitSpiritHead();
|
||||||
|
|
||||||
PlayerList[nPlayer].nDestVertPan = q16horiz(0);
|
|
||||||
if (currentLevel->levelNumber == 11)
|
if (currentLevel->levelNumber == 11)
|
||||||
{
|
{
|
||||||
PlayerList[nPlayer].nDestVertPan = q16horiz(46);
|
PlayerList[nPlayer].horizon.settarget(46);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PlayerList[nPlayer].nDestVertPan = q16horiz(11);
|
PlayerList[nPlayer].horizon.settarget(11);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1067,7 +1066,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
|
||||||
zVelB = -zVelB;
|
zVelB = -zVelB;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zVelB > 512 && !PlayerList[nPlayer].horizon.horiz.asq16() && !(sPlayerInput[nPlayer].actions & SB_AIMMODE)) {
|
if (zVelB > 512 && !PlayerList[nPlayer].horizon.horiz.asq16() && cl_slopetilting) {
|
||||||
sPlayerInput[nPlayer].actions |= SB_CENTERVIEW;
|
sPlayerInput[nPlayer].actions |= SB_CENTERVIEW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2665,10 +2664,13 @@ loc_1BD2E:
|
||||||
pPlayer->bPlayerPan = false;
|
pPlayer->bPlayerPan = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
double nVertPan = (pPlayer->nDestVertPan - pPlayer->horizon.horiz).asq16() * (1. / (FRACUNIT << 2));
|
if (cl_slopetilting)
|
||||||
if (nVertPan != 0)
|
|
||||||
{
|
{
|
||||||
pPlayer->horizon.addadjustment(abs(nVertPan) >= 4 ? clamp(nVertPan, -4, 4) : nVertPan * 2.);
|
double nVertPan = (pPlayer->nDestVertPan - pPlayer->horizon.horiz).asq16() * (1. / (FRACUNIT << 2));
|
||||||
|
if (nVertPan != 0)
|
||||||
|
{
|
||||||
|
pPlayer->horizon.addadjustment(abs(nVertPan) >= 4 ? clamp(nVertPan, -4, 4) : nVertPan * 2.);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // else, player's health is less than 0
|
else // else, player's health is less than 0
|
||||||
|
|
|
@ -45,20 +45,23 @@ BEGIN_SW_NS
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DSWDRealmsScreen : public DScreenJob
|
class DSWDRealmsScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DSWDRealmsScreen() : DScreenJob(fadein | fadeout) {}
|
DSWDRealmsScreen() : DSkippableScreenJob(fadein | fadeout) {}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest) override
|
void OnTick() override
|
||||||
|
{
|
||||||
|
if (ticks > 5 * GameTicRate) state = finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
const uint64_t duration = 5'000'000'000;
|
|
||||||
const auto tex = tileGetTexture(THREED_REALMS_PIC, true);
|
const auto tex = tileGetTexture(THREED_REALMS_PIC, true);
|
||||||
const int translation = TRANSLATION(Translation_BasePalettes, DREALMSPAL);
|
const int translation = TRANSLATION(Translation_BasePalettes, DREALMSPAL);
|
||||||
|
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
return skiprequest ? -1 : clock < duration ? 1 : 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -166,7 +169,7 @@ DScreenJob* GetFinishAnim(int num)
|
||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class DSWCreditsScreen : public DScreenJob
|
class DSWCreditsScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -177,21 +180,24 @@ class DSWCreditsScreen : public DScreenJob
|
||||||
int starttime;
|
int starttime;
|
||||||
int curpic;
|
int curpic;
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Skipped() override
|
||||||
|
{
|
||||||
|
StopSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
// Lo Wang feel like singing!
|
||||||
|
PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
{
|
{
|
||||||
twod->ClearScreen();
|
|
||||||
int seconds = int(clock / 1'000'000'000);
|
|
||||||
if (clock == 0)
|
|
||||||
{
|
|
||||||
// Lo Wang feel like singing!
|
|
||||||
PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI);
|
|
||||||
}
|
|
||||||
if (state == 0)
|
if (state == 0)
|
||||||
{
|
{
|
||||||
if (skiprequest || !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE))
|
if (!soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE))
|
||||||
{
|
{
|
||||||
skiprequest = false;
|
starttime = ticks;
|
||||||
starttime = seconds;
|
|
||||||
state = 1;
|
state = 1;
|
||||||
StopSound();
|
StopSound();
|
||||||
curpic = CREDITS1_PIC;
|
curpic = CREDITS1_PIC;
|
||||||
|
@ -205,15 +211,19 @@ class DSWCreditsScreen : public DScreenJob
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (seconds >= starttime + 8)
|
if (ticks >= starttime + 8 * GameTicRate)
|
||||||
{
|
{
|
||||||
curpic = CREDITS1_PIC + CREDITS2_PIC - curpic;
|
curpic = CREDITS1_PIC + CREDITS2_PIC - curpic;
|
||||||
starttime = seconds;
|
starttime = ticks;
|
||||||
}
|
}
|
||||||
DrawTexture(twod, tileGetTexture(curpic, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
|
||||||
}
|
}
|
||||||
if (skiprequest) StopSound();
|
}
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
|
void Draw(double) override
|
||||||
|
{
|
||||||
|
twod->ClearScreen();
|
||||||
|
if (state == 1)
|
||||||
|
DrawTexture(twod, tileGetTexture(curpic, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -377,31 +387,42 @@ private:
|
||||||
(*(*State)->Animator)(0);
|
(*(*State)->Animator)(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
bool OnEvent(event_t* ev) override
|
||||||
{
|
{
|
||||||
twod->ClearScreen();
|
if (ev->type == EV_KeyDown)
|
||||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
|
||||||
|
|
||||||
if (clock == 0)
|
|
||||||
{
|
{
|
||||||
PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]);
|
if (State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)])
|
||||||
}
|
|
||||||
|
|
||||||
if (skiprequest && State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)])
|
|
||||||
{
|
|
||||||
State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))];
|
|
||||||
Tics = 0;
|
|
||||||
skiprequest = false;
|
|
||||||
nextclock = currentclock;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (currentclock > nextclock)
|
|
||||||
{
|
{
|
||||||
nextclock += synctics;
|
State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))];
|
||||||
gStateControl(&State, &Tics);
|
Tics = 0;
|
||||||
|
nextclock = ticks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start() override
|
||||||
|
{
|
||||||
|
PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTick() override
|
||||||
|
{
|
||||||
|
while (ticks > nextclock)
|
||||||
|
{
|
||||||
|
nextclock++;
|
||||||
|
gStateControl(&State, &Tics);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (State == State->NextState)
|
||||||
|
{
|
||||||
|
state = finished;
|
||||||
|
StopSound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double) override
|
||||||
|
{
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
DrawTexture(twod, tileGetTexture(BONUS_SCREEN_PIC, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
DrawTexture(twod, tileGetTexture(BONUS_SCREEN_PIC, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||||
MNU_DrawString(160, 20, currentLevel->DisplayName(), 1, 19, 0);
|
MNU_DrawString(160, 20, currentLevel->DisplayName(), 1, 19, 0);
|
||||||
|
@ -437,10 +458,6 @@ private:
|
||||||
MNU_DrawString(60, BONUS_LINE(line), ds, 1, 16);
|
MNU_DrawString(60, BONUS_LINE(line), ds, 1, 16);
|
||||||
|
|
||||||
MNU_DrawString(160, 185, GStrings("PRESSKEY"), 1, 19, 0);
|
MNU_DrawString(160, 185, GStrings("PRESSKEY"), 1, 19, 0);
|
||||||
|
|
||||||
int ret = (State == State->NextState)? 0 : skiprequest ? -1 : 1;
|
|
||||||
if (ret != 1) StopSound();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -466,12 +483,17 @@ enum
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class DSWMultiSummaryScreen : public DScreenJob
|
class DSWMultiSummaryScreen : public DSkippableScreenJob
|
||||||
{
|
{
|
||||||
short death_total[MAX_SW_PLAYERS_REG]{};
|
short death_total[MAX_SW_PLAYERS_REG]{};
|
||||||
short kills[MAX_SW_PLAYERS_REG]{};
|
short kills[MAX_SW_PLAYERS_REG]{};
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Skipped() override
|
||||||
|
{
|
||||||
|
StopSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
if (clock == 0) PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]);
|
if (clock == 0) PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]);
|
||||||
|
|
||||||
|
@ -571,8 +593,6 @@ class DSWMultiSummaryScreen : public DScreenJob
|
||||||
|
|
||||||
y += STAT_OFF_Y;
|
y += STAT_OFF_Y;
|
||||||
}
|
}
|
||||||
if (skiprequest) StopSound();
|
|
||||||
return skiprequest ? -1 : 1;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -635,7 +655,7 @@ class DSWLoadScreen : public DScreenJob
|
||||||
public:
|
public:
|
||||||
DSWLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
|
DSWLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
|
||||||
|
|
||||||
int Frame(uint64_t clock, bool skiprequest)
|
void Draw(double) override
|
||||||
{
|
{
|
||||||
const int TITLE_PIC = 2324;
|
const int TITLE_PIC = 2324;
|
||||||
twod->ClearScreen();
|
twod->ClearScreen();
|
||||||
|
@ -643,8 +663,6 @@ public:
|
||||||
|
|
||||||
MNU_DrawString(160, 170, /*DemoMode ? GStrings("TXT_LBDEMO") :*/ GStrings("TXT_ENTERING"), 1, 16, 0);
|
MNU_DrawString(160, 170, /*DemoMode ? GStrings("TXT_LBDEMO") :*/ GStrings("TXT_ENTERING"), 1, 16, 0);
|
||||||
MNU_DrawString(160, 180, rec->DisplayName(), 1, 16, 0);
|
MNU_DrawString(160, 180, rec->DisplayName(), 1, 16, 0);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -404,12 +404,11 @@ grpinfo
|
||||||
GameID "Blood"
|
GameID "Blood"
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this doesn't work with the current setup.
|
|
||||||
grpinfo
|
grpinfo
|
||||||
{
|
{
|
||||||
// This is for identifying older Blood versions. Since I have no information, all I can do is testing for a few known files.
|
// This is for identifying older Blood versions. Since I have no information, all I can do is testing for a few known files.
|
||||||
name "BLOOD: Unknown Version"
|
name "BLOOD: Unknown Version"
|
||||||
mustcontain "help1.qav", "cult2d2.seq", "tombstn1.kvx", "normal.plu"
|
mustcontain "help1.qav", "normal.plu", "inverse.clu", "cosine.dat", "blood.pal"
|
||||||
defname "blood.def"
|
defname "blood.def"
|
||||||
scriptname "BLOOD.INI"
|
scriptname "BLOOD.INI"
|
||||||
flags GAMEFLAG_BLOOD
|
flags GAMEFLAG_BLOOD
|
||||||
|
@ -417,7 +416,6 @@ grpinfo
|
||||||
loadgrp "SOUNDS.RFF", "GUI.RFF"
|
loadgrp "SOUNDS.RFF", "GUI.RFF"
|
||||||
gamefilter "Blood.Blood"
|
gamefilter "Blood.Blood"
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
grpinfo
|
grpinfo
|
||||||
{
|
{
|
||||||
|
|
|
@ -1033,10 +1033,7 @@ OptionMenu "VideoOptions" protected
|
||||||
StaticText ""
|
StaticText ""
|
||||||
Option "$DSPLYMNU_VOXELS", "r_voxels", "OnOff"
|
Option "$DSPLYMNU_VOXELS", "r_voxels", "OnOff"
|
||||||
Option "$DSPLYMNU_SHADOWS", "r_shadows", "OnOff"
|
Option "$DSPLYMNU_SHADOWS", "r_shadows", "OnOff"
|
||||||
ifnotgame(Exhumed)
|
Option "$DSPLYMNU_SLOPETILT", "cl_slopetilting", "OnOff"
|
||||||
{
|
|
||||||
Option "$DSPLYMNU_SLOPETILT", "cl_slopetilting", "OnOff"
|
|
||||||
}
|
|
||||||
ifnotgame(Blood, Exhumed)
|
ifnotgame(Blood, Exhumed)
|
||||||
{
|
{
|
||||||
Option "$DSPLYMNU_VIEWBOB", "cl_viewbob", "OnOff"
|
Option "$DSPLYMNU_VIEWBOB", "cl_viewbob", "OnOff"
|
||||||
|
|
Loading…
Reference in a new issue