mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-15 08:52:00 +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
|
||||
|
||||
core/movie/playmve.cpp
|
||||
core/movie/movieplayer.cpp
|
||||
core/automap.cpp
|
||||
core/cheats.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.
|
||||
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
|
||||
|
|
|
@ -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];
|
||||
uint32_t const cstat = spr->cstat;
|
||||
|
||||
if (cstat & CSTAT_SPRITE_NOFIND)
|
||||
continue;
|
||||
|
||||
#ifdef USE_OPENGL
|
||||
if (!hitallsprites)
|
||||
#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];
|
||||
|
||||
if (spr->cstat & CSTAT_SPRITE_NOFIND)
|
||||
continue;
|
||||
if (blacklist_sprite_func && blacklist_sprite_func(z))
|
||||
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()))
|
||||
{
|
||||
// Merge with the last command.
|
||||
|
|
|
@ -118,6 +118,7 @@ public:
|
|||
ETexMode mDrawMode;
|
||||
uint8_t mLightLevel;
|
||||
uint8_t mFlags;
|
||||
float mScreenFade;
|
||||
|
||||
bool useTransform;
|
||||
DMatrix3x3 transform;
|
||||
|
@ -149,6 +150,7 @@ public:
|
|||
mLightLevel == other.mLightLevel &&
|
||||
mColor1.d == other.mColor1.d &&
|
||||
useTransform == other.useTransform &&
|
||||
mScreenFade == other.mScreenFade &&
|
||||
(
|
||||
!useTransform ||
|
||||
(
|
||||
|
@ -172,7 +174,7 @@ public:
|
|||
int fullscreenautoaspect = 3;
|
||||
int cliptop = -1, clipleft = -1, clipwidth = -1, clipheight = -1;
|
||||
|
||||
int AddCommand(const RenderCommand *data);
|
||||
int AddCommand(RenderCommand *data);
|
||||
void AddIndices(int firstvert, int count, ...);
|
||||
private:
|
||||
void AddIndices(int firstvert, TArray<int> &v);
|
||||
|
|
|
@ -110,13 +110,18 @@ void S_SetMusicCallbacks(MusicCallbacks* cb)
|
|||
//==========================================================================
|
||||
|
||||
static std::unique_ptr<SoundStream> musicStream;
|
||||
static TArray<SoundStream*> customStreams;
|
||||
|
||||
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata)
|
||||
{
|
||||
int flags = 0;
|
||||
if (numchannels < 2) flags |= SoundStream::Mono;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -125,11 +130,19 @@ void S_StopCustomStream(SoundStream *stream)
|
|||
if (stream)
|
||||
{
|
||||
stream->Stop();
|
||||
auto f = customStreams.Find(stream);
|
||||
if (f < customStreams.Size()) customStreams.Delete(f);
|
||||
delete stream;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void S_PauseAllCustomStreams(bool on)
|
||||
{
|
||||
for (auto s : customStreams)
|
||||
{
|
||||
s->SetPaused(on);
|
||||
}
|
||||
}
|
||||
|
||||
static TArray<int16_t> convert;
|
||||
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);
|
||||
SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata);
|
||||
void S_StopCustomStream(SoundStream* stream);
|
||||
void S_PauseAllCustomStreams(bool on);
|
||||
|
||||
struct MusicCallbacks
|
||||
{
|
||||
|
|
|
@ -76,12 +76,15 @@ void D_ProcessEvents (void)
|
|||
continue;
|
||||
if (ev->type == EV_DeviceChange)
|
||||
UpdateJoystickMenu(I_UpdateDeviceList());
|
||||
if (gamestate == GS_INTRO)
|
||||
continue;
|
||||
if (C_Responder (ev))
|
||||
|
||||
if (gamestate != GS_INTRO) // GS_INTRO blocks the UI.
|
||||
{
|
||||
if (C_Responder(ev))
|
||||
continue; // console ate the event
|
||||
if (M_Responder (ev))
|
||||
if (M_Responder(ev))
|
||||
continue; // menu ate the event
|
||||
}
|
||||
|
||||
G_Responder (ev);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,6 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state)
|
|||
vb.UploadData(&vertices[0], vertices.Size(), &indices[0], indices.Size());
|
||||
state.SetVertexBuffer(&vb);
|
||||
state.EnableFog(false);
|
||||
state.SetScreenFade(drawer->screenFade);
|
||||
|
||||
for(auto &cmd : commands)
|
||||
{
|
||||
|
@ -94,6 +93,7 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state)
|
|||
state.SetRenderStyle(cmd.mRenderStyle);
|
||||
state.EnableBrightmap(!(cmd.mRenderStyle.Flags & STYLEF_ColorIsFixed));
|
||||
state.EnableFog(2); // Special 2D mode 'fog'.
|
||||
state.SetScreenFade(cmd.mScreenFade);
|
||||
|
||||
state.SetTextureMode(cmd.mDrawMode);
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "mmulti.h"
|
||||
#include "gstrings.h"
|
||||
#include "gamecontrol.h"
|
||||
#include "screenjob.h"
|
||||
#include "mapinfo.h"
|
||||
|
||||
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)
|
||||
{
|
||||
Net_WriteByte(DEM_CHANGEMAP);
|
||||
|
|
|
@ -8,3 +8,4 @@ EXTERN_CVAR(Bool, sv_cheats)
|
|||
|
||||
void genericCheat(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 "uiinput.h"
|
||||
#include "automap.h"
|
||||
#include "screenjob.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -53,6 +54,11 @@
|
|||
|
||||
bool G_Responder (event_t *ev)
|
||||
{
|
||||
if (gamestate == GS_INTRO || gamestate == GS_INTERMISSION)
|
||||
{
|
||||
return ScreenJobResponder(ev);
|
||||
}
|
||||
|
||||
if (CT_Responder(ev))
|
||||
return true; // chat ate the event
|
||||
if (Cheat_Responder(ev))
|
||||
|
|
|
@ -1674,6 +1674,7 @@ bool D_CheckNetGame (void)
|
|||
|
||||
Net_SetCommandHandler(DEM_GENERICCHEAT, genericCheat);
|
||||
Net_SetCommandHandler(DEM_CHANGEMAP, changeMap);
|
||||
Net_SetCommandHandler(DEM_ENDSCREENJOB, endScreenJob);
|
||||
|
||||
for (i = 0; i < MAXNETNODES; i++)
|
||||
{
|
||||
|
|
|
@ -89,6 +89,7 @@ enum EDemoCommand
|
|||
DEM_GENERICCHEAT,
|
||||
DEM_GIVE,
|
||||
DEM_CHANGEMAP,
|
||||
DEM_ENDSCREENJOB,
|
||||
|
||||
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()
|
||||
{
|
||||
// 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);
|
||||
GSnd->SetSfxPaused (true, 0);
|
||||
S_PauseAllCustomStreams(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1161,6 +1142,7 @@ void S_ResumeSound (bool notsfx)
|
|||
{
|
||||
soundEngine->SetPaused(false);
|
||||
GSnd->SetSfxPaused (false, 0);
|
||||
S_PauseAllCustomStreams(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -328,9 +328,10 @@ static void GameTicker()
|
|||
|
||||
case GS_MENUSCREEN:
|
||||
case GS_FULLCONSOLE:
|
||||
break;
|
||||
case GS_INTERMISSION:
|
||||
case GS_INTRO:
|
||||
// These elements do not tick at game rate.
|
||||
ScreenJobTick();
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -370,7 +371,7 @@ void Display()
|
|||
case GS_INTRO:
|
||||
case GS_INTERMISSION:
|
||||
// screen jobs are not bound by the game ticker so they need to be ticked in the display loop.
|
||||
RunScreenJobFrame();
|
||||
ScreenJobDraw();
|
||||
break;
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
int span = int(clock / 1'000'000);
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE);
|
||||
// Only end after having faded out.
|
||||
return skiprequest ? -1 : span > waittime? 0 : 1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DAnmPlayer : public DScreenJob
|
||||
void DImageScreen::Draw(double smoothratio)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
if (tilenum > 0) tex = tileGetTexture(tilenum, true);
|
||||
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();
|
||||
if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE);
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
|
@ -771,11 +125,10 @@ class ScreenJobRunner
|
|||
int index = -1;
|
||||
float screenfade;
|
||||
bool clearbefore;
|
||||
int64_t startTime = -1;
|
||||
int64_t lastTime = -1;
|
||||
int actionState;
|
||||
int terminateState;
|
||||
uint64_t clock = 0;
|
||||
int fadeticks = 0;
|
||||
int last_paused_tic = -1;
|
||||
|
||||
public:
|
||||
ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_)
|
||||
|
@ -820,73 +173,90 @@ public:
|
|||
index++;
|
||||
}
|
||||
actionState = clearbefore ? State_Clear : State_Run;
|
||||
if (index < jobs.Size()) screenfade = jobs[index].job->fadestyle & DScreenJob::fadein ? 0.f : 1.f;
|
||||
lastTime= startTime = -1;
|
||||
clock = 0;
|
||||
if (index < jobs.Size())
|
||||
{
|
||||
jobs[index].job->fadestate = !paused && jobs[index].job->fadestyle & DScreenJob::fadein? DScreenJob::fadein : DScreenJob::visible;
|
||||
jobs[index].job->Start();
|
||||
}
|
||||
inputState.ClearAllInput();
|
||||
}
|
||||
|
||||
int DisplayFrame()
|
||||
int DisplayFrame(double smoothratio)
|
||||
{
|
||||
auto& job = jobs[index];
|
||||
auto now = I_GetTimeNS();
|
||||
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;
|
||||
screenfade = clamp(ms, 0.f, 1.f);
|
||||
double ms = (job.job->ticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime;
|
||||
float screenfade = (float)clamp(ms, 0., 1.);
|
||||
twod->SetScreenFade(screenfade);
|
||||
if (job.job->fadestate != DScreenJob::fadeout)
|
||||
job.job->fadestate = DScreenJob::fadein;
|
||||
if (screenfade == 1.f) job.job->fadestate = DScreenJob::visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
int state = job.job->DrawFrame(smoothratio);
|
||||
twod->SetScreenFade(1.f);
|
||||
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;
|
||||
float screenfade = 1.f - (float)clamp(ms, 0., 1.);
|
||||
twod->SetScreenFade(screenfade);
|
||||
job.job->DrawFrame(1.);
|
||||
return (screenfade > 0.f);
|
||||
}
|
||||
|
||||
if (startTime == -1)
|
||||
bool OnEvent(event_t* ev)
|
||||
{
|
||||
lastTime = startTime = now;
|
||||
}
|
||||
else if (!M_Active())
|
||||
{
|
||||
clock += now - lastTime;
|
||||
if (clock == 0) clock = 1;
|
||||
}
|
||||
lastTime = now;
|
||||
if (paused || index >= jobs.Size()) return false;
|
||||
|
||||
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)
|
||||
if (ev->type == EV_KeyDown)
|
||||
{
|
||||
twod->Unlock(); // must unlock before displaying.
|
||||
return 0;
|
||||
// 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++;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool RunFrame()
|
||||
|
@ -899,6 +269,13 @@ public:
|
|||
if (completion) completion(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)
|
||||
{
|
||||
actionState = State_Run;
|
||||
|
@ -906,18 +283,16 @@ public:
|
|||
}
|
||||
else if (actionState == State_Run)
|
||||
{
|
||||
terminateState = DisplayFrame();
|
||||
terminateState = DisplayFrame(smoothratio);
|
||||
if (terminateState < 1)
|
||||
{
|
||||
// Must lock before displaying.
|
||||
if (jobs[index].job->fadestyle & DScreenJob::fadeout)
|
||||
{
|
||||
twod->Lock();
|
||||
startTime = -1;
|
||||
clock = 0;
|
||||
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;
|
||||
fadeticks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -927,9 +302,10 @@ public:
|
|||
}
|
||||
else if (actionState == State_Fadeout)
|
||||
{
|
||||
int ended = FadeoutFrame();
|
||||
int ended = FadeoutFrame(smoothratio);
|
||||
if (ended < 1)
|
||||
{
|
||||
jobs[index].job->state = DScreenJob::stopped;
|
||||
AdvanceJob(terminateState < 0);
|
||||
}
|
||||
}
|
||||
|
@ -964,7 +340,25 @@ void DeleteScreenJob()
|
|||
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.
|
||||
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.
|
||||
if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job");
|
||||
twod->ClearScreen();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
auto res = runner->RunFrame();
|
||||
if (!res)
|
||||
|
@ -980,5 +374,6 @@ void RunScreenJobFrame()
|
|||
assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing);
|
||||
DeleteScreenJob();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <functional>
|
||||
#include "dobject.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "d_eventbase.h"
|
||||
|
||||
using CompletionFunc = std::function<void(bool)>;
|
||||
struct JobDesc;
|
||||
|
@ -10,14 +11,25 @@ class ScreenJobRunner;
|
|||
class DScreenJob : public DObject
|
||||
{
|
||||
DECLARE_CLASS(DScreenJob, DObject)
|
||||
int64_t now;
|
||||
const int fadestyle;
|
||||
const float fadetime; // in milliseconds
|
||||
int fadestate = fadein;
|
||||
|
||||
friend class ScreenJobRunner;
|
||||
protected:
|
||||
int ticks = 0;
|
||||
int state = running;
|
||||
bool pausable = true;
|
||||
|
||||
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
|
||||
{
|
||||
visible = 0,
|
||||
|
@ -32,17 +44,20 @@ public:
|
|||
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; }
|
||||
|
||||
};
|
||||
|
@ -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
|
||||
{
|
||||
int wait;
|
||||
bool cleared = false;
|
||||
|
||||
public:
|
||||
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)
|
||||
|
||||
int tilenum = -1;
|
||||
int trans;
|
||||
int waittime; // in ms.
|
||||
bool cleared = false;
|
||||
FGameTexture* tex = nullptr;
|
||||
|
||||
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;
|
||||
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;
|
||||
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 EndScreenJob();
|
||||
void DeleteScreenJob();
|
||||
void RunScreenJobFrame();
|
||||
bool ScreenJobResponder(event_t* ev);
|
||||
void ScreenJobTick();
|
||||
bool ScreenJobDraw();
|
||||
|
||||
struct AnimSound
|
||||
{
|
||||
|
|
|
@ -821,7 +821,7 @@ void tileUpdateAnimations()
|
|||
{
|
||||
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);
|
||||
|
||||
|
|
|
@ -411,25 +411,29 @@ extern PicAnm picanm;
|
|||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -444,11 +448,11 @@ int32_t animateoffs(int const tilenum, int fakevar);
|
|||
|
||||
inline FGameTexture* tileGetTexture(int tile, bool animate = false)
|
||||
{
|
||||
assert(tile < MAXTILES);
|
||||
assert((unsigned)tile < MAXTILES);
|
||||
if (tile < 0 || tile >= MAXTILES) return nullptr;
|
||||
if (animate)
|
||||
{
|
||||
if (picanm[tile].sf & PICANM_ANIMTYPE_MASK)
|
||||
if (TileFiles.tiledata[tile].picanm.sf & PICANM_ANIMTYPE_MASK)
|
||||
tile += animateoffs(tile, 0);
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -70,16 +70,16 @@ struct THINGINFO
|
|||
{
|
||||
short startHealth;
|
||||
short mass;
|
||||
unsigned char clipdist;
|
||||
uint8_t clipdist;
|
||||
short flags;
|
||||
int elastic; // elasticity
|
||||
int dmgResist; // damage resistance
|
||||
short cstat;
|
||||
short picnum;
|
||||
char shade;
|
||||
unsigned char pal;
|
||||
unsigned char xrepeat; // xrepeat
|
||||
unsigned char yrepeat; // yrepeat
|
||||
int8_t shade;
|
||||
uint8_t pal;
|
||||
uint8_t xrepeat; // xrepeat
|
||||
uint8_t yrepeat; // yrepeat
|
||||
int dmgControl[kDamageMax]; // damage
|
||||
};
|
||||
|
||||
|
@ -87,23 +87,23 @@ struct AMMOITEMDATA
|
|||
{
|
||||
short cstat;
|
||||
short picnum;
|
||||
char shade;
|
||||
char pal;
|
||||
unsigned char xrepeat;
|
||||
unsigned char yrepeat;
|
||||
int8_t shade;
|
||||
uint8_t pal;
|
||||
uint8_t xrepeat;
|
||||
uint8_t yrepeat;
|
||||
short count;
|
||||
unsigned char type;
|
||||
unsigned char weaponType;
|
||||
uint8_t type;
|
||||
uint8_t weaponType;
|
||||
};
|
||||
|
||||
struct WEAPONITEMDATA
|
||||
{
|
||||
short cstat;
|
||||
short picnum;
|
||||
char shade;
|
||||
char pal;
|
||||
unsigned char xrepeat;
|
||||
unsigned char yrepeat;
|
||||
int8_t shade;
|
||||
uint8_t pal;
|
||||
uint8_t xrepeat;
|
||||
uint8_t yrepeat;
|
||||
short type;
|
||||
short ammoType;
|
||||
short count;
|
||||
|
@ -113,10 +113,10 @@ struct ITEMDATA
|
|||
{
|
||||
short cstat;
|
||||
short picnum;
|
||||
char shade;
|
||||
char pal;
|
||||
unsigned char xrepeat;
|
||||
unsigned char yrepeat;
|
||||
int8_t shade;
|
||||
uint8_t pal;
|
||||
uint8_t xrepeat;
|
||||
uint8_t yrepeat;
|
||||
short packSlot;
|
||||
};
|
||||
|
||||
|
@ -125,15 +125,15 @@ struct MissileType
|
|||
short picnum;
|
||||
int velocity;
|
||||
int angleOfs;
|
||||
unsigned char xrepeat;
|
||||
unsigned char yrepeat;
|
||||
char shade;
|
||||
unsigned char clipDist;
|
||||
uint8_t xrepeat;
|
||||
uint8_t yrepeat;
|
||||
int8_t shade;
|
||||
uint8_t clipDist;
|
||||
};
|
||||
|
||||
struct EXPLOSION
|
||||
{
|
||||
unsigned char repeat;
|
||||
uint8_t repeat;
|
||||
char dmg;
|
||||
char dmgRng;
|
||||
int radius;
|
||||
|
|
|
@ -64,7 +64,7 @@ void playlogos()
|
|||
|
||||
if (!userConfig.nologo)
|
||||
{
|
||||
if (fileSystem.FindFile("logo.smk"))
|
||||
if (fileSystem.FindFile("logo.smk") != -1)
|
||||
{
|
||||
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<DImageScreen>(2050) };
|
||||
}
|
||||
if (fileSystem.FindFile("gti.smk"))
|
||||
if (fileSystem.FindFile("gti.smk") != -1)
|
||||
{
|
||||
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);
|
||||
auto buffer = fr.Read();
|
||||
unsigned char md4[16];
|
||||
uint8_t md4[16];
|
||||
md4once(buffer.Data(), buffer.Size(), md4);
|
||||
G_LoadMapHack(mapname, md4);
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ struct DUDEINFO {
|
|||
short startHealth; // health
|
||||
unsigned short mass; // mass
|
||||
int at6; // unused?
|
||||
unsigned char clipdist; // clipdist
|
||||
uint8_t clipdist; // clipdist
|
||||
int eyeHeight;
|
||||
int aimHeight; // used by just Cerberus
|
||||
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)
|
||||
{
|
||||
|
@ -139,7 +139,7 @@ class DBloodSummaryScreen : public DScreenJob
|
|||
}
|
||||
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest)
|
||||
void Draw(double) override
|
||||
{
|
||||
drawTextScreenBackground();
|
||||
if (gGameOptions.nGameType == 0)
|
||||
|
@ -160,7 +160,7 @@ class DBloodSummaryScreen : public DScreenJob
|
|||
DrawCaption(GStrings("TXTB_FRAGSTATS"));
|
||||
DrawKills();
|
||||
}
|
||||
int myclock = int(clock * 120 / 1'000'000'000);
|
||||
int myclock = ticks * 120 / GameTicRate;
|
||||
if ((myclock & 32))
|
||||
{
|
||||
auto text = GStrings("PRESSKEY");
|
||||
|
@ -168,7 +168,6 @@ class DBloodSummaryScreen : public DScreenJob
|
|||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3);
|
||||
}
|
||||
return skiprequest ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -285,7 +284,7 @@ public:
|
|||
else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType));
|
||||
}
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest)
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
drawTextScreenBackground();
|
||||
|
@ -297,7 +296,6 @@ public:
|
|||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
int nSquare = sizeX * sizeY;
|
||||
unsigned char *pPtr1 = (unsigned char*)pFrame;
|
||||
uint8_t *pPtr1 = (uint8_t*)pFrame;
|
||||
while (nSquare--)
|
||||
{
|
||||
unsigned char *pPtr2 = pPtr1+sizeX;
|
||||
uint8_t *pPtr2 = pPtr1+sizeX;
|
||||
int sum = *(pPtr2-1) + *pPtr2 + *(pPtr2+1) + *(pPtr2+sizeX);
|
||||
if (*(pPtr2+sizeX) > 96)
|
||||
{
|
||||
|
|
|
@ -32,18 +32,18 @@ CFX gFX;
|
|||
|
||||
struct FXDATA {
|
||||
CALLBACK_ID funcID; // callback
|
||||
char at1; // detail
|
||||
short at2; // seq
|
||||
short Kills; // flags
|
||||
int at6; // gravity
|
||||
int ata; // air drag
|
||||
uint8_t detail; // detail
|
||||
short seq; // seq
|
||||
short flags; // flags
|
||||
int gravity; // gravity
|
||||
int drag; // air drag
|
||||
int ate;
|
||||
short at12; // picnum
|
||||
unsigned char at14; // xrepeat
|
||||
unsigned char at15; // yrepeat
|
||||
short at16; // cstat
|
||||
signed char at18; // shade
|
||||
char at19; // pal
|
||||
short picnum; // picnum
|
||||
uint8_t xrepeat; // xrepeat
|
||||
uint8_t yrepeat; // yrepeat
|
||||
short cstat; // cstat
|
||||
int8_t shade; // shade
|
||||
uint8_t pal; // pal
|
||||
};
|
||||
|
||||
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);
|
||||
pSprite->type = nFx;
|
||||
pSprite->picnum = pFX->at12;
|
||||
pSprite->cstat |= pFX->at16;
|
||||
pSprite->shade = pFX->at18;
|
||||
pSprite->pal = pFX->at19;
|
||||
sprite[pSprite->index].detail = pFX->at1;
|
||||
if (pFX->at14 > 0)
|
||||
pSprite->xrepeat = pFX->at14;
|
||||
if (pFX->at15 > 0)
|
||||
pSprite->yrepeat = pFX->at15;
|
||||
if ((pFX->Kills & 1) && Chance(0x8000))
|
||||
pSprite->picnum = pFX->picnum;
|
||||
pSprite->cstat |= pFX->cstat;
|
||||
pSprite->shade = pFX->shade;
|
||||
pSprite->pal = pFX->pal;
|
||||
sprite[pSprite->index].detail = pFX->detail;
|
||||
if (pFX->xrepeat > 0)
|
||||
pSprite->xrepeat = pFX->xrepeat;
|
||||
if (pFX->yrepeat > 0)
|
||||
pSprite->yrepeat = pFX->yrepeat;
|
||||
if ((pFX->flags & 1) && Chance(0x8000))
|
||||
pSprite->cstat |= 4;
|
||||
if ((pFX->Kills & 2) && Chance(0x8000))
|
||||
if ((pFX->flags & 2) && Chance(0x8000))
|
||||
pSprite->cstat |= 8;
|
||||
if (pFX->at2)
|
||||
if (pFX->seq)
|
||||
{
|
||||
int nXSprite = dbInsertXSprite(pSprite->index);
|
||||
seqSpawn(pFX->at2, 3, nXSprite, -1);
|
||||
seqSpawn(pFX->seq, 3, nXSprite, -1);
|
||||
}
|
||||
if (a6 == 0)
|
||||
a6 = pFX->ate;
|
||||
|
@ -203,7 +203,7 @@ void CFX::fxProcess(void)
|
|||
assert(nSector >= 0 && nSector < kMaxSectors);
|
||||
assert(pSprite->type < kFXMax);
|
||||
FXDATA *pFXData = &gFXData[pSprite->type];
|
||||
actAirDrag(pSprite, pFXData->ata);
|
||||
actAirDrag(pSprite, pFXData->drag);
|
||||
if (xvel[nSprite])
|
||||
pSprite->x += xvel[nSprite]>>12;
|
||||
if (yvel[nSprite])
|
||||
|
@ -257,7 +257,7 @@ void CFX::fxProcess(void)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
zvel[nSprite] += pFXData->at6;
|
||||
zvel[nSprite] += pFXData->gravity;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,9 +349,9 @@ void fxPrecache()
|
|||
{
|
||||
for (int i = 0; i < kFXMax; i++)
|
||||
{
|
||||
tilePrecacheTile(gFXData[i].at12, 0, 0);
|
||||
if (gFXData[i].at2)
|
||||
seqPrecacheId(gFXData[i].at2, 0);
|
||||
tilePrecacheTile(gFXData[i].picnum, 0, 0);
|
||||
if (gFXData[i].seq)
|
||||
seqPrecacheId(gFXData[i].seq, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ BEGIN_BLD_NS
|
|||
|
||||
static struct {
|
||||
short nTile;
|
||||
unsigned char nStat;
|
||||
unsigned char nPal;
|
||||
uint8_t nStat;
|
||||
uint8_t nPal;
|
||||
int nScale;
|
||||
short nX, nY;
|
||||
} burnTable[9] = {
|
||||
|
|
|
@ -47,8 +47,8 @@ enum EGameFlag
|
|||
};
|
||||
|
||||
struct GAMEOPTIONS {
|
||||
unsigned char nGameType;
|
||||
unsigned char nDifficulty;
|
||||
uint8_t nGameType;
|
||||
uint8_t nDifficulty;
|
||||
char nMonsterSettings;
|
||||
int uGameFlags;
|
||||
int uNetGameFlags;
|
||||
|
|
|
@ -49,7 +49,7 @@ void WeaponInit(void);
|
|||
void WeaponDraw(PLAYER *pPlayer, int a2, double a3, double a4, int a5, int smoothratio);
|
||||
void WeaponRaise(PLAYER *pPlayer);
|
||||
void WeaponLower(PLAYER *pPlayer);
|
||||
char WeaponUpgrade(PLAYER *pPlayer, char newWeapon);
|
||||
int WeaponUpgrade(PLAYER *pPlayer, int newWeapon);
|
||||
void WeaponProcess(PLAYER *pPlayer);
|
||||
void WeaponUpdateState(PLAYER* pPlayer);
|
||||
void teslaHit(spritetype *pMissile, int a2);
|
||||
|
@ -114,7 +114,7 @@ enum SurfaceType {
|
|||
};
|
||||
|
||||
extern char surfType[MAXTILES];
|
||||
extern signed char tileShade[MAXTILES];
|
||||
extern int8_t tileShade[MAXTILES];
|
||||
extern short voxelIndex[MAXTILES];
|
||||
|
||||
extern int nPrecacheCount;
|
||||
|
|
|
@ -182,7 +182,7 @@ struct PLAYER
|
|||
struct AMMOINFO
|
||||
{
|
||||
int max;
|
||||
signed char vectorType;
|
||||
int8_t vectorType;
|
||||
};
|
||||
|
||||
struct POWERUPINFO
|
||||
|
|
|
@ -46,7 +46,7 @@ struct TILE_FRAME
|
|||
int y;
|
||||
int z;
|
||||
int stat;
|
||||
signed char shade;
|
||||
int8_t shade;
|
||||
char palnum;
|
||||
unsigned short angle;
|
||||
};
|
||||
|
@ -54,9 +54,9 @@ struct TILE_FRAME
|
|||
struct SOUNDINFO
|
||||
{
|
||||
int sound;
|
||||
unsigned char priority;
|
||||
unsigned char sndFlags; // (by NoOne) Various sound flags
|
||||
unsigned char sndRange; // (by NoOne) Random sound range
|
||||
uint8_t priority;
|
||||
uint8_t sndFlags; // (by NoOne) Various sound flags
|
||||
uint8_t sndRange; // (by NoOne) Random sound range
|
||||
char reserved[1];
|
||||
};
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ struct Seq {
|
|||
|
||||
struct ACTIVE
|
||||
{
|
||||
unsigned char type;
|
||||
uint8_t type;
|
||||
unsigned short xindex;
|
||||
};
|
||||
|
||||
|
@ -86,7 +86,7 @@ struct SEQINST
|
|||
int nSeqID;
|
||||
int callback;
|
||||
short timeCounter;
|
||||
unsigned char frameIndex;
|
||||
uint8_t frameIndex;
|
||||
void Update();
|
||||
};
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ int tileEnd[256];
|
|||
int hTileFile[256];
|
||||
|
||||
char surfType[kMaxTiles];
|
||||
signed char tileShade[kMaxTiles];
|
||||
int8_t tileShade[kMaxTiles];
|
||||
short voxelIndex[kMaxTiles];
|
||||
|
||||
int tileInit(char a1, const char *a2)
|
||||
|
|
|
@ -144,7 +144,7 @@ enum
|
|||
|
||||
QAV *weaponQAV[kQAVEnd];
|
||||
|
||||
char sub_4B1A4(PLAYER *pPlayer)
|
||||
static bool sub_4B1A4(PLAYER *pPlayer)
|
||||
{
|
||||
switch (pPlayer->curWeapon)
|
||||
{
|
||||
|
@ -169,12 +169,12 @@ char sub_4B1A4(PLAYER *pPlayer)
|
|||
return 0;
|
||||
}
|
||||
|
||||
char BannedUnderwater(int nWeapon)
|
||||
static bool BannedUnderwater(int nWeapon)
|
||||
{
|
||||
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)
|
||||
return 1;
|
||||
|
@ -187,7 +187,7 @@ char CheckWeaponAmmo(PLAYER *pPlayer, int weapon, int ammotype, int 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)
|
||||
return 1;
|
||||
|
@ -200,7 +200,7 @@ char CheckAmmo(PLAYER *pPlayer, int ammotype, int 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)
|
||||
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);
|
||||
}
|
||||
|
||||
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, 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 },
|
||||
};
|
||||
|
||||
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)))
|
||||
weapon = newWeapon;
|
||||
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 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;
|
||||
do
|
||||
|
@ -1795,9 +1795,9 @@ char WeaponFindNext(PLAYER *pPlayer, int *a2, char bDir)
|
|||
return weapon;
|
||||
}
|
||||
|
||||
char WeaponFindLoaded(PLAYER *pPlayer, int *a2)
|
||||
static int WeaponFindLoaded(PLAYER *pPlayer, int *a2)
|
||||
{
|
||||
char v4 = 1;
|
||||
int v4 = 1;
|
||||
int v14 = 0;
|
||||
if (weaponModes[pPlayer->curWeapon].update > 1)
|
||||
{
|
||||
|
@ -1874,7 +1874,7 @@ int processSprayCan(PLAYER *pPlayer)
|
|||
return 0;
|
||||
}
|
||||
|
||||
char processTNT(PLAYER *pPlayer)
|
||||
static bool processTNT(PLAYER *pPlayer)
|
||||
{
|
||||
switch (pPlayer->weaponState)
|
||||
{
|
||||
|
@ -1912,7 +1912,7 @@ char processTNT(PLAYER *pPlayer)
|
|||
return 0;
|
||||
}
|
||||
|
||||
char processProxy(PLAYER *pPlayer)
|
||||
static bool processProxy(PLAYER *pPlayer)
|
||||
{
|
||||
switch (pPlayer->weaponState)
|
||||
{
|
||||
|
@ -1929,7 +1929,7 @@ char processProxy(PLAYER *pPlayer)
|
|||
return 0;
|
||||
}
|
||||
|
||||
char processRemote(PLAYER *pPlayer)
|
||||
static bool processRemote(PLAYER *pPlayer)
|
||||
{
|
||||
switch (pPlayer->weaponState)
|
||||
{
|
||||
|
@ -1945,7 +1945,7 @@ char processRemote(PLAYER *pPlayer)
|
|||
return 0;
|
||||
}
|
||||
|
||||
char processLeech(PLAYER *pPlayer)
|
||||
static bool processLeech(PLAYER *pPlayer)
|
||||
{
|
||||
switch (pPlayer->weaponState)
|
||||
{
|
||||
|
@ -1969,7 +1969,7 @@ char processLeech(PLAYER *pPlayer)
|
|||
return 0;
|
||||
}
|
||||
|
||||
char processTesla(PLAYER *pPlayer)
|
||||
static bool processTesla(PLAYER *pPlayer)
|
||||
{
|
||||
switch (pPlayer->weaponState)
|
||||
{
|
||||
|
@ -2109,7 +2109,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
|||
}
|
||||
pPlayer->nextWeapon = 0;
|
||||
int t;
|
||||
char weapon = WeaponFindNext(pPlayer, &t, 1);
|
||||
int weapon = WeaponFindNext(pPlayer, &t, 1);
|
||||
pPlayer->weaponMode[weapon] = t;
|
||||
if (VanillaMode())
|
||||
{
|
||||
|
@ -2131,7 +2131,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
|||
}
|
||||
pPlayer->nextWeapon = 0;
|
||||
int t;
|
||||
char weapon = WeaponFindNext(pPlayer, &t, 0);
|
||||
int weapon = WeaponFindNext(pPlayer, &t, 0);
|
||||
pPlayer->weaponMode[weapon] = t;
|
||||
if (VanillaMode())
|
||||
{
|
||||
|
@ -2146,7 +2146,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
|||
}
|
||||
else if (pPlayer->input.getNewWeapon() == WeaponSel_Alt)
|
||||
{
|
||||
char weapon;
|
||||
int weapon;
|
||||
|
||||
switch (pPlayer->curWeapon)
|
||||
{
|
||||
|
@ -2189,7 +2189,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
|||
{
|
||||
pPlayer->weaponState = 0;
|
||||
int t;
|
||||
char weapon = WeaponFindLoaded(pPlayer, &t);
|
||||
int weapon = WeaponFindLoaded(pPlayer, &t);
|
||||
pPlayer->weaponMode[weapon] = t;
|
||||
if (pPlayer->curWeapon)
|
||||
{
|
||||
|
@ -2263,7 +2263,7 @@ void WeaponProcess(PLAYER *pPlayer) {
|
|||
{
|
||||
pPlayer->weaponState = 0;
|
||||
int t;
|
||||
char weapon = WeaponFindLoaded(pPlayer, &t);
|
||||
int weapon = WeaponFindLoaded(pPlayer, &t);
|
||||
pPlayer->weaponMode[weapon] = t;
|
||||
if (pPlayer->curWeapon)
|
||||
{
|
||||
|
@ -2597,11 +2597,11 @@ void teslaHit(spritetype *pMissile, int a2)
|
|||
int nSector = pMissile->sectnum;
|
||||
int nOwner = pMissile->owner;
|
||||
GetClosestSpriteSectors(nSector, x, y, nDist, va4);
|
||||
char v4 = 1;
|
||||
bool v4 = true;
|
||||
int v24 = -1;
|
||||
actHitcodeToData(a2, &gHitInfo, &v24, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (a2 == 3 && v24 >= 0 && sprite[v24].statnum == kStatDude)
|
||||
v4 = 0;
|
||||
v4 = false;
|
||||
int nSprite;
|
||||
StatIterator it(kStatDude);
|
||||
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:
|
||||
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);
|
||||
int translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, DREALMSPAL) : 0;
|
||||
|
||||
twod->ClearScreen();
|
||||
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;
|
||||
|
||||
public:
|
||||
DTitleScreen() : DScreenJob(fadein | fadeout)
|
||||
DTitleScreen() : DSkippableScreenJob(fadein | fadeout)
|
||||
{
|
||||
}
|
||||
|
||||
int Frame(uint64_t nsclock, bool skiprequest) override
|
||||
void OnTick() override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
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);
|
||||
|
||||
int clock = ticks * 120 / GameTicRate;
|
||||
if (soundanm == 0 && clock >= 120 && clock < 120 + 60)
|
||||
{
|
||||
soundanm = 1;
|
||||
|
@ -231,6 +225,25 @@ public:
|
|||
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.;
|
||||
if (scale > 0.)
|
||||
{
|
||||
|
@ -263,13 +276,6 @@ public:
|
|||
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 (!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 };
|
||||
}
|
||||
else S_PlaySpecialMusic(MUS_INTRO);
|
||||
|
@ -311,16 +317,14 @@ void Logo_d(const CompletionFunc &completion)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DEpisode1End1 : public DScreenJob
|
||||
class DEpisode1End1 : public DSkippableScreenJob
|
||||
{
|
||||
int bonuscnt = 0;
|
||||
int bossani = -1;
|
||||
int breatheani = -1;
|
||||
bool breathebg = false;
|
||||
|
||||
public:
|
||||
DEpisode1End1() : DScreenJob(fadein | fadeout) {}
|
||||
|
||||
int Frame(uint64_t nsclock, bool skiprequest) override
|
||||
{
|
||||
static const int breathe[] =
|
||||
static inline const int breathe[] =
|
||||
{
|
||||
0, 30,VICTORY1 + 1,176,59,
|
||||
30, 60,VICTORY1 + 2,176,59,
|
||||
|
@ -328,7 +332,7 @@ public:
|
|||
90, 120,0 ,176,59
|
||||
};
|
||||
|
||||
static const int bossmove[] =
|
||||
static inline const int bossmove[] =
|
||||
{
|
||||
0, 120,VICTORY1 + 3,86,59,
|
||||
220, 260,VICTORY1 + 4,86,59,
|
||||
|
@ -339,19 +343,20 @@ public:
|
|||
350, 380,VICTORY1 + 8,86,59,
|
||||
};
|
||||
|
||||
auto translation = TRANSLATION(Translation_BasePalettes, ENDINGPAL);
|
||||
public:
|
||||
DEpisode1End1() : DSkippableScreenJob(fadein | fadeout) {}
|
||||
|
||||
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);
|
||||
void OnTick()
|
||||
{
|
||||
int currentclock = ticks * 120 / GameTicRate;
|
||||
|
||||
bossani = -1;
|
||||
breathebg = false;
|
||||
breatheani = -1;
|
||||
|
||||
// boss
|
||||
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])
|
||||
{
|
||||
if (t == 10 && bonuscnt == 1)
|
||||
|
@ -360,8 +365,8 @@ public:
|
|||
S_PlaySound(SQUISHED, CHAN_AUTO, CHANF_UI);
|
||||
bonuscnt++;
|
||||
}
|
||||
DrawTexture(twod, tileGetTexture(bossmove[t + 2], true), bossmove[t + 3], bossmove[t + 4], DTA_FullscreenScale, FSMode_Fit320x200,
|
||||
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
||||
bossani = t;
|
||||
}
|
||||
}
|
||||
|
||||
// Breathe
|
||||
|
@ -369,8 +374,7 @@ public:
|
|||
{
|
||||
if (currentclock >= 750)
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(VICTORY1 + 8, true), 86, 59, DTA_FullscreenScale, FSMode_Fit320x200,
|
||||
DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE);
|
||||
breathebg = true;
|
||||
if (currentclock >= 750 && bonuscnt == 2)
|
||||
{
|
||||
S_PlaySound(DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI);
|
||||
|
@ -385,12 +389,37 @@ public:
|
|||
S_PlaySound(BOSSTALKTODUKE, CHAN_AUTO, CHANF_UI);
|
||||
bonuscnt++;
|
||||
}
|
||||
DrawTexture(twod, tileGetTexture(breathe[t + 2], true), breathe[t + 3], breathe[t + 4], DTA_FullscreenScale, FSMode_Fit320x200,
|
||||
breatheani = t;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
// Only end after having faded out.
|
||||
return skiprequest ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -409,7 +438,6 @@ public:
|
|||
|
||||
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);
|
||||
if (texid.isValid()) return TexMan.GetGameTexture(texid);
|
||||
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)
|
||||
{
|
||||
|
@ -465,23 +498,21 @@ public:
|
|||
if (!S_CheckSoundPlaying(ENDSEQVOL3SND9))
|
||||
{
|
||||
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;
|
||||
|
||||
case 6:
|
||||
if (isPlutoPak())
|
||||
{
|
||||
if (clock > waittime) skiprequest = true;
|
||||
if (ticks > waittime) state = finished;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int ret = DImageScreen::Frame(clock, skiprequest);
|
||||
if (ret != 1) FX_StopAllSounds();
|
||||
return ret;
|
||||
if (state != running) FX_StopAllSounds();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -491,12 +522,12 @@ public:
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DEpisode4Text : public DScreenJob
|
||||
class DEpisode4Text : public DSkippableScreenJob
|
||||
{
|
||||
public:
|
||||
DEpisode4Text() : DScreenJob(fadein | fadeout) {}
|
||||
DEpisode4Text() : DSkippableScreenJob(fadein | fadeout) {}
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest)
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
BigText(160, 60, GStrings("Thanks to all our"));
|
||||
|
@ -504,7 +535,6 @@ public:
|
|||
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 + 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)
|
||||
{
|
||||
|
@ -539,9 +569,6 @@ public:
|
|||
default:
|
||||
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;
|
||||
|
||||
public:
|
||||
DDukeMultiplayerBonusScreen(int pws) : DScreenJob(fadein|fadeout)
|
||||
DDukeMultiplayerBonusScreen(int pws) : DSkippableScreenJob(fadein|fadeout)
|
||||
{
|
||||
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];
|
||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
||||
int currentclock = int((ticks + smoothratio) * 120 / GameTicRate);
|
||||
twod->ClearScreen();
|
||||
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);
|
||||
|
@ -783,7 +813,6 @@ public:
|
|||
}
|
||||
|
||||
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;
|
||||
int gfx_offset;
|
||||
int bonuscnt = 0;
|
||||
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:
|
||||
DDukeLevelSummaryScreen() : DScreenJob(fadein | fadeout)
|
||||
|
@ -818,7 +857,82 @@ public:
|
|||
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];
|
||||
GameText(10, 59 + 9, GStrings("TXT_YourTime"), 0);
|
||||
|
@ -826,16 +940,8 @@ public:
|
|||
if (!isNamWW2GI())
|
||||
GameText(10, 79 + 9, GStrings("TXT_3DRTIME"), 0);
|
||||
|
||||
if (bonuscnt == 0)
|
||||
bonuscnt++;
|
||||
|
||||
if (currentclock > (60 * 4))
|
||||
if (displaystate & printTimeVal)
|
||||
{
|
||||
if (bonuscnt == 1)
|
||||
{
|
||||
bonuscnt++;
|
||||
S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
FormatTime(ps[myconnectindex].player_par, tempbuf);
|
||||
GameText((320 >> 2) + 71, 59 + 9, tempbuf, 0);
|
||||
|
||||
|
@ -850,25 +956,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void PrintKills(int currentclock)
|
||||
void PrintKills()
|
||||
{
|
||||
char tempbuf[32];
|
||||
GameText(10, 94 + 9, GStrings("TXT_EnemiesKilled"), 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);
|
||||
GameText((320 >> 2) + 70, 94 + 9, tempbuf, 0);
|
||||
|
||||
|
@ -887,20 +982,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void PrintSecrets(int currentclock)
|
||||
void PrintSecrets()
|
||||
{
|
||||
char tempbuf[32];
|
||||
GameText(10, 119 + 9, GStrings("TXT_SECFND"), 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);
|
||||
GameText((320 >> 2) + 70, 119 + 9, tempbuf, 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();
|
||||
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);
|
||||
|
||||
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:
|
||||
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 4:
|
||||
case 5:
|
||||
|
@ -956,14 +1035,9 @@ public:
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (currentclock > (10240 + 120L))
|
||||
else if (!(displaystate & dukeWait))
|
||||
{
|
||||
if (speech > 0 && !skiprequest && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, speech)) return 1;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch((currentclock >> 5) & 3)
|
||||
switch((ticks >> 3) & 3)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
|
@ -977,26 +1051,6 @@ public:
|
|||
|
||||
if (lastmapname) BigText(160, 20 - 6, lastmapname);
|
||||
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>() };
|
||||
}
|
||||
if (job)
|
||||
{
|
||||
RunScreenJob(jobs, job, completion);
|
||||
}
|
||||
else if (completion) completion(false);
|
||||
}
|
||||
|
||||
|
@ -1092,14 +1148,13 @@ class DDukeLoadScreen : public DScreenJob
|
|||
public:
|
||||
DDukeLoadScreen(MapRecord *maprec) : DScreenJob(0), rec(maprec) {}
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest)
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
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, 114, rec->DisplayName());
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -266,9 +266,14 @@ public:
|
|||
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];
|
||||
twod->ClearScreen();
|
||||
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);
|
||||
return skiprequest ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -358,13 +362,23 @@ class DRRLevelSummaryScreen : public DScreenJob
|
|||
{
|
||||
const char* lastmapname;
|
||||
int gfx_offset;
|
||||
int bonuscnt = 0;
|
||||
int displaystate = 0;
|
||||
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:
|
||||
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);
|
||||
}
|
||||
|
||||
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];
|
||||
BigText(30, 48, GStrings("TXT_YerTime"), -1);
|
||||
BigText(30, 64, GStrings("TXT_ParTime"), -1);
|
||||
BigText(30, 80, GStrings("TXT_XTRTIME"), -1);
|
||||
|
||||
if (bonuscnt == 0)
|
||||
bonuscnt++;
|
||||
|
||||
if (currentclock > (60 * 4))
|
||||
if (displaystate & printTimeVal)
|
||||
{
|
||||
if (bonuscnt == 1)
|
||||
{
|
||||
bonuscnt++;
|
||||
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
FormatTime(ps[myconnectindex].player_par, tempbuf);
|
||||
BigText(191, 48, tempbuf, -1);
|
||||
|
||||
FormatTime(currentLevel->parTime, tempbuf);
|
||||
BigText(191, 64, tempbuf, -1);
|
||||
|
||||
if (!isNamWW2GI())
|
||||
{
|
||||
FormatTime(currentLevel->designerTime, tempbuf);
|
||||
BigText(191, 80, tempbuf, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintKills(int currentclock)
|
||||
void PrintKills()
|
||||
{
|
||||
char tempbuf[32];
|
||||
BigText(30, 112, GStrings("TXT_VarmintsKilled"), -1);
|
||||
BigText(30, 128, GStrings("TXT_VarmintsLeft"), -1);
|
||||
|
||||
if (bonuscnt == 2)
|
||||
bonuscnt++;
|
||||
|
||||
if (currentclock > (60 * 7))
|
||||
if (displaystate & printKillsVal)
|
||||
{
|
||||
if (bonuscnt == 3)
|
||||
{
|
||||
bonuscnt++;
|
||||
S_PlaySound(442, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed);
|
||||
BigText(231, 112, tempbuf, -1);
|
||||
if (ud.player_skill > 3)
|
||||
|
@ -449,20 +517,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void PrintSecrets(int currentclock)
|
||||
void PrintSecrets()
|
||||
{
|
||||
char tempbuf[32];
|
||||
BigText(30, 144, GStrings("TXT_SECFND"), -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);
|
||||
BigText(231, 144, tempbuf, -1);
|
||||
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();
|
||||
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);
|
||||
|
||||
if (lastmapname) BigText(80, 16, lastmapname, -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:
|
||||
DRRRAEndOfGame() : DScreenJob(fadein|fadeout)
|
||||
DRRRAEndOfGame() : DSkippableScreenJob(fadein|fadeout)
|
||||
{
|
||||
}
|
||||
|
||||
void Skipped() override
|
||||
{
|
||||
S_StopSound(35);
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
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);
|
||||
auto tex = tileGetTexture(ENDGAME + ((currentclock >> 4) & 1));
|
||||
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;
|
||||
if (!S_CheckSoundPlaying(-1, 35) && ticks > 15 * GameTicRate) state = finished; // make sure it stays, even if sound is off.
|
||||
}
|
||||
return 1;
|
||||
void Draw(double) override
|
||||
{
|
||||
auto tex = tileGetTexture(ENDGAME + ((ticks >> 2) & 1));
|
||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -617,14 +647,13 @@ class DRRLoadScreen : public DScreenJob
|
|||
public:
|
||||
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);
|
||||
|
||||
int y = isRRRA()? 140 : 90;
|
||||
BigText(160, y, (rec->flags & MI_USERMAP) ? GStrings("TXT_ENTRUM") : GStrings("TXT_ENTERIN"), 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
|
||||
// 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->cstat |= CSTAT_SPRITE_INVISIBLE;
|
||||
spri->cstat |= CSTAT_SPRITE_INVISIBLE|CSTAT_SPRITE_NOFIND;
|
||||
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 "gstrings.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "c_bind.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -384,11 +385,21 @@ public:
|
|||
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);
|
||||
if (skiprequest) StopLocalSound();
|
||||
return DImageScreen::Frame(clock, skiprequest);
|
||||
StopLocalSound();
|
||||
}
|
||||
|
||||
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 };
|
||||
|
||||
class DMainTitle : public DScreenJob
|
||||
class DMainTitle : public DSkippableScreenJob
|
||||
{
|
||||
const char* a;
|
||||
const char* b;
|
||||
int state = 0;
|
||||
int var_18;
|
||||
int duration;
|
||||
int var_4 = 0;
|
||||
int esi = 130;
|
||||
int nCount = 0;
|
||||
|
@ -413,56 +424,65 @@ class DMainTitle : public DScreenJob
|
|||
|
||||
|
||||
public:
|
||||
DMainTitle() : DScreenJob(fadein)
|
||||
DMainTitle() : DSkippableScreenJob(fadein)
|
||||
{
|
||||
a = GStrings("TXT_EX_COPYRIGHT1");
|
||||
b = GStrings("TXT_EX_COPYRIGHT2");
|
||||
var_18 = skullDurations[0];
|
||||
duration = skullDurations[0];
|
||||
}
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest) override
|
||||
{
|
||||
int ticker = clock * 120 / 1'000'000'000;
|
||||
if (clock == 0)
|
||||
void Start() override
|
||||
{
|
||||
PlayLocalSound(StaticSound[59], 0, true, CHANF_UI);
|
||||
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();
|
||||
else
|
||||
PlayLocalSound(StaticSound[61], 0, false, CHANF_UI);
|
||||
state = 1;
|
||||
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();
|
||||
|
||||
menu_DoPlasma();
|
||||
|
||||
DrawRel(kSkullHead, 160, 100);
|
||||
switch (state)
|
||||
if (state == 0)
|
||||
{
|
||||
case 0:
|
||||
DrawRel(kSkullJaw, 161, 130);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
}
|
||||
else
|
||||
{
|
||||
int nStringWidth = SmallFont->StringWidth(a);
|
||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
nStringWidth = SmallFont->StringWidth(b);
|
||||
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;
|
||||
|
||||
|
@ -487,11 +507,8 @@ public:
|
|||
}
|
||||
|
||||
DrawRel(nTile, 161, y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return skiprequest? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -618,10 +635,8 @@ class DMapScreen : public DScreenJob
|
|||
{
|
||||
int i;
|
||||
int x = 0;
|
||||
int var_2C = 0;
|
||||
int delta = 0;
|
||||
int nIdleSeconds = 0;
|
||||
int startTime = 0;
|
||||
int runtimer = 0;
|
||||
|
||||
int curYPos, destYPos;
|
||||
int nLevel, nLevelNew, nLevelBest;
|
||||
|
@ -633,11 +648,11 @@ public:
|
|||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos < destYPos) {
|
||||
var_2C = 2;
|
||||
delta = 2;
|
||||
}
|
||||
|
||||
if (curYPos > destYPos) {
|
||||
var_2C = -2;
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
// Trim smoke in widescreen
|
||||
|
@ -652,19 +667,12 @@ public:
|
|||
#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();
|
||||
|
||||
if ((currentclock - startTime) / kTimerTicks)
|
||||
{
|
||||
nIdleSeconds++;
|
||||
startTime = currentclock;
|
||||
}
|
||||
|
||||
int tileY = curYPos;
|
||||
|
||||
// Draw the background screens
|
||||
|
@ -728,42 +736,40 @@ public:
|
|||
DrawAbs(nTile, textX, textY, shade);
|
||||
}
|
||||
|
||||
selectedlevelnew = nLevelNew + 1;
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (curYPos != destYPos)
|
||||
{
|
||||
// scroll the map every couple of ms
|
||||
if (currentclock - runtimer >= (kTimerTicks / 32)) {
|
||||
curYPos += var_2C;
|
||||
runtimer = currentclock;
|
||||
}
|
||||
curYPos += delta;
|
||||
|
||||
if (inputState.CheckAllInput())
|
||||
{
|
||||
if (var_2C < 8) {
|
||||
var_2C *= 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (curYPos > destYPos&& var_2C > 0) {
|
||||
if (curYPos > destYPos && delta > 0) {
|
||||
curYPos = destYPos;
|
||||
}
|
||||
|
||||
if (curYPos < destYPos && var_2C < 0) {
|
||||
if (curYPos < destYPos && delta < 0) {
|
||||
curYPos = destYPos;
|
||||
}
|
||||
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
selectedlevelnew = nLevelNew + 1;
|
||||
return skiprequest? -1 : nIdleSeconds < 12? 1 : 0;
|
||||
else nIdleSeconds++;
|
||||
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)
|
||||
{
|
||||
nLevelNew++;
|
||||
|
@ -772,10 +778,10 @@ public:
|
|||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos <= destYPos) {
|
||||
var_2C = 2;
|
||||
delta = 2;
|
||||
}
|
||||
else {
|
||||
var_2C = -2;
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
nIdleSeconds = 0;
|
||||
|
@ -783,10 +789,8 @@ public:
|
|||
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)
|
||||
{
|
||||
nLevelNew--;
|
||||
|
@ -795,17 +799,19 @@ public:
|
|||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos <= destYPos) {
|
||||
var_2C = 2;
|
||||
delta = 2;
|
||||
}
|
||||
else {
|
||||
var_2C = -2;
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
state = skipped;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -963,16 +969,17 @@ void uploadCinemaPalettes()
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DCinema : public DScreenJob
|
||||
class DCinema : public DSkippableScreenJob
|
||||
{
|
||||
TextOverlay text;
|
||||
short cinematile;
|
||||
int currentCinemaPalette;
|
||||
int edx;
|
||||
int check;
|
||||
int cont = 1;
|
||||
|
||||
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;
|
||||
cinematile = cinemas[nVal].tile;
|
||||
|
@ -984,12 +991,15 @@ public:
|
|||
check = checklevel;
|
||||
}
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest) override
|
||||
void Start() override
|
||||
{
|
||||
if (check > 0 && check != selectedlevelnew)
|
||||
{
|
||||
state = finished;
|
||||
return; // immediately abort if the player selected a different level on the map
|
||||
}
|
||||
|
||||
if (clock == 0)
|
||||
{
|
||||
if (check > 0 && check != selectedlevelnew) return 0; // immediately abort if the player selected a different level on the map
|
||||
check = -1;
|
||||
StopAllSounds();
|
||||
if (edx != -1)
|
||||
{
|
||||
|
@ -997,19 +1007,28 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE);
|
||||
|
||||
text.DisplayText();
|
||||
auto cont = text.AdvanceCinemaText(clock * (120. / 1'000'000'000));
|
||||
int ret = skiprequest ? -1 : cont ? 1 : 0;
|
||||
|
||||
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 && ret != 1)
|
||||
if (isShareware() && currentCinemaPalette == 3)
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
}
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
if (check == 0) return;
|
||||
DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE);
|
||||
|
||||
text.DisplayText();
|
||||
cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1029,6 +1048,7 @@ class DLastLevelCinema : public DScreenJob
|
|||
int nextclock = 4;
|
||||
unsigned int nStringTypeOn, nCharTypeOn;
|
||||
int screencnt = 0;
|
||||
bool skiprequest = false;
|
||||
|
||||
TArray<FString> screentext;
|
||||
|
||||
|
@ -1123,7 +1143,7 @@ private:
|
|||
int yy = ebp;
|
||||
|
||||
auto p = GStrings["REQUIRED_CHARACTERS"];
|
||||
if (1)//p && *p)
|
||||
if (p && *p)
|
||||
{
|
||||
yy *= 2;
|
||||
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);
|
||||
phase = 1;
|
||||
}
|
||||
int currentclock = clock * 120 / 1'000'000'000;
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case 1:
|
||||
if (currentclock >= nextclock)
|
||||
{
|
||||
Phase1();
|
||||
nextclock += 4;
|
||||
}
|
||||
if (skiprequest || currentclock >= 240)
|
||||
if (skiprequest || ticks >= nextclock)
|
||||
{
|
||||
InitPhase2();
|
||||
phase = 2;
|
||||
skiprequest = 0;
|
||||
skiprequest = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (currentclock >= nextclock)
|
||||
{
|
||||
if (screentext[nStringTypeOn][nCharTypeOn] != ' ')
|
||||
PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI);
|
||||
|
||||
nCharTypeOn++;
|
||||
nextclock += 4;
|
||||
if (screentext[nStringTypeOn][nCharTypeOn] == 0)
|
||||
{
|
||||
nCharTypeOn = 0;
|
||||
nStringTypeOn++;
|
||||
if (nStringTypeOn >= screentext.Size())
|
||||
{
|
||||
nextclock = (kTimerTicks * (screentext.Size() + 2)) + currentclock;
|
||||
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||
phase = 3;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
DisplayPhase2();
|
||||
if (skiprequest)
|
||||
{
|
||||
nextclock = (kTimerTicks * (screentext.Size() + 2)) + currentclock;
|
||||
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||
phase = 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
DisplayPhase2();
|
||||
if (currentclock >= nextclock || skiprequest)
|
||||
if (ticks >= nextclock || skiprequest)
|
||||
{
|
||||
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
||||
phase = 4;
|
||||
nextclock = currentclock + 240;
|
||||
skiprequest = 0;
|
||||
nextclock = ticks + 60;
|
||||
skiprequest = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (currentclock >= nextclock)
|
||||
if (ticks >= nextclock)
|
||||
{
|
||||
skiprequest |= !Phase3();
|
||||
nextclock += 4;
|
||||
}
|
||||
if (skiprequest || currentclock >= 240)
|
||||
if (skiprequest)
|
||||
{
|
||||
// Go to the next text page.
|
||||
if (screencnt != 2)
|
||||
{
|
||||
screencnt++;
|
||||
nextclock = currentclock + 240;
|
||||
nextclock = ticks + 60;
|
||||
skiprequest = 0;
|
||||
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;
|
||||
uint64_t page;
|
||||
uint64_t pagetime;
|
||||
bool skiprequest = false;
|
||||
|
||||
public:
|
||||
DExCredits()
|
||||
|
@ -1253,35 +1277,53 @@ public:
|
|||
credits = text.Split("\n\n");
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if (credits.Size() == 0) return 0;
|
||||
if (credits.Size() == 0)
|
||||
{
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
playCDtrack(19, false);
|
||||
pagetime = 0;
|
||||
page = -1;
|
||||
}
|
||||
if (clock >= pagetime || skiprequest)
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (ticks >= pagetime || skiprequest)
|
||||
{
|
||||
page++;
|
||||
if (page < credits.Size())
|
||||
pagelines = credits[page].Split("\n");
|
||||
else
|
||||
{
|
||||
if (skiprequest || !CDplaying()) return 0;
|
||||
if (skiprequest || !CDplaying())
|
||||
{
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
pagelines.Clear();
|
||||
}
|
||||
pagetime = clock + 2'000'000'000; //
|
||||
pagetime = ticks + 60; //
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
|
||||
int y = 100 - ((10 * (pagelines.Size() - 1)) / 2);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
@ -193,7 +193,7 @@ static void Intermission(MapRecord *from_map, MapRecord *to_map)
|
|||
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
|
||||
InitNewGame();
|
||||
if (map->levelNumber == 1) STAT_StartNewGame("Exhumed", 1);
|
||||
if (frommenu) Intermission(nullptr, map);
|
||||
else NextLevel(map, skill);
|
||||
Intermission(nullptr, map);
|
||||
}
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
||||
{
|
||||
Mus_Stop();
|
||||
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;
|
||||
double angle = 1536;
|
||||
|
@ -216,6 +216,7 @@ public:
|
|||
lastclock = 0;
|
||||
nextclock = 0;
|
||||
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)
|
||||
{
|
||||
nextclock += 100'000'000;
|
||||
if (decoder.ReadFrame(fp) == 0)
|
||||
{
|
||||
return 0;
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +257,6 @@ public:
|
|||
}
|
||||
|
||||
lastclock = clock;
|
||||
return skiprequest ? -1 : 1;
|
||||
}
|
||||
|
||||
void OnDestroy() override
|
||||
|
@ -269,12 +271,11 @@ public:
|
|||
DScreenJob* PlayMovie(const char* fileName)
|
||||
{
|
||||
// clear keys
|
||||
inputState.ClearAllInput();
|
||||
|
||||
auto fp = fileSystem.OpenFileReader(fileName);
|
||||
if (!fp.isOpen())
|
||||
{
|
||||
return Create<DScreenJob>();
|
||||
return Create<DBlackScreen>(1);
|
||||
}
|
||||
char buffer[4];
|
||||
fp.Read(buffer, 4);
|
||||
|
|
|
@ -1032,14 +1032,13 @@ void FuncPlayer(int a, int nDamage, int nRun)
|
|||
StopLocalSound();
|
||||
InitSpiritHead();
|
||||
|
||||
PlayerList[nPlayer].nDestVertPan = q16horiz(0);
|
||||
if (currentLevel->levelNumber == 11)
|
||||
{
|
||||
PlayerList[nPlayer].nDestVertPan = q16horiz(46);
|
||||
PlayerList[nPlayer].horizon.settarget(46);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -2665,12 +2664,15 @@ loc_1BD2E:
|
|||
pPlayer->bPlayerPan = false;
|
||||
}
|
||||
|
||||
if (cl_slopetilting)
|
||||
{
|
||||
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
|
||||
{
|
||||
// loc_1C0E9
|
||||
|
|
|
@ -45,20 +45,23 @@ BEGIN_SW_NS
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DSWDRealmsScreen : public DScreenJob
|
||||
class DSWDRealmsScreen : public DSkippableScreenJob
|
||||
{
|
||||
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 int translation = TRANSLATION(Translation_BasePalettes, DREALMSPAL);
|
||||
|
||||
twod->ClearScreen();
|
||||
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
|
||||
{
|
||||
|
@ -177,21 +180,24 @@ class DSWCreditsScreen : public DScreenJob
|
|||
int starttime;
|
||||
int curpic;
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest)
|
||||
void Skipped() override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
int seconds = int(clock / 1'000'000'000);
|
||||
if (clock == 0)
|
||||
StopSound();
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
// Lo Wang feel like singing!
|
||||
PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI);
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (state == 0)
|
||||
{
|
||||
if (skiprequest || !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE))
|
||||
if (!soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE))
|
||||
{
|
||||
skiprequest = false;
|
||||
starttime = seconds;
|
||||
starttime = ticks;
|
||||
state = 1;
|
||||
StopSound();
|
||||
curpic = CREDITS1_PIC;
|
||||
|
@ -205,16 +211,20 @@ class DSWCreditsScreen : public DScreenJob
|
|||
}
|
||||
else
|
||||
{
|
||||
if (seconds >= starttime + 8)
|
||||
if (ticks >= starttime + 8 * GameTicRate)
|
||||
{
|
||||
curpic = CREDITS1_PIC + CREDITS2_PIC - curpic;
|
||||
starttime = seconds;
|
||||
starttime = ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (skiprequest) StopSound();
|
||||
return skiprequest ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -377,31 +387,42 @@ private:
|
|||
(*(*State)->Animator)(0);
|
||||
}
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest)
|
||||
bool OnEvent(event_t* ev) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
int currentclock = int(clock * 120 / 1'000'000'000);
|
||||
if (ev->type == EV_KeyDown)
|
||||
{
|
||||
if (State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)])
|
||||
{
|
||||
State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))];
|
||||
Tics = 0;
|
||||
nextclock = ticks;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (clock == 0)
|
||||
void Start() override
|
||||
{
|
||||
PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]);
|
||||
}
|
||||
|
||||
if (skiprequest && State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)])
|
||||
void OnTick() override
|
||||
{
|
||||
State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))];
|
||||
Tics = 0;
|
||||
skiprequest = false;
|
||||
nextclock = currentclock;
|
||||
}
|
||||
else
|
||||
while (ticks > nextclock)
|
||||
{
|
||||
while (currentclock > nextclock)
|
||||
{
|
||||
nextclock += synctics;
|
||||
nextclock++;
|
||||
gStateControl(&State, &Tics);
|
||||
}
|
||||
|
||||
if (State == State->NextState)
|
||||
{
|
||||
state = finished;
|
||||
StopSound();
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
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);
|
||||
|
@ -437,10 +458,6 @@ private:
|
|||
MNU_DrawString(60, BONUS_LINE(line), ds, 1, 16);
|
||||
|
||||
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 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]);
|
||||
|
||||
|
@ -571,8 +593,6 @@ class DSWMultiSummaryScreen : public DScreenJob
|
|||
|
||||
y += STAT_OFF_Y;
|
||||
}
|
||||
if (skiprequest) StopSound();
|
||||
return skiprequest ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -635,7 +655,7 @@ class DSWLoadScreen : public DScreenJob
|
|||
public:
|
||||
DSWLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
|
||||
|
||||
int Frame(uint64_t clock, bool skiprequest)
|
||||
void Draw(double) override
|
||||
{
|
||||
const int TITLE_PIC = 2324;
|
||||
twod->ClearScreen();
|
||||
|
@ -643,8 +663,6 @@ public:
|
|||
|
||||
MNU_DrawString(160, 170, /*DemoMode ? GStrings("TXT_LBDEMO") :*/ GStrings("TXT_ENTERING"), 1, 16, 0);
|
||||
MNU_DrawString(160, 180, rec->DisplayName(), 1, 16, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -404,12 +404,11 @@ grpinfo
|
|||
GameID "Blood"
|
||||
}
|
||||
|
||||
/* this doesn't work with the current setup.
|
||||
grpinfo
|
||||
{
|
||||
// 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"
|
||||
mustcontain "help1.qav", "cult2d2.seq", "tombstn1.kvx", "normal.plu"
|
||||
mustcontain "help1.qav", "normal.plu", "inverse.clu", "cosine.dat", "blood.pal"
|
||||
defname "blood.def"
|
||||
scriptname "BLOOD.INI"
|
||||
flags GAMEFLAG_BLOOD
|
||||
|
@ -417,7 +416,6 @@ grpinfo
|
|||
loadgrp "SOUNDS.RFF", "GUI.RFF"
|
||||
gamefilter "Blood.Blood"
|
||||
}
|
||||
*/
|
||||
|
||||
grpinfo
|
||||
{
|
||||
|
|
|
@ -1033,10 +1033,7 @@ OptionMenu "VideoOptions" protected
|
|||
StaticText ""
|
||||
Option "$DSPLYMNU_VOXELS", "r_voxels", "OnOff"
|
||||
Option "$DSPLYMNU_SHADOWS", "r_shadows", "OnOff"
|
||||
ifnotgame(Exhumed)
|
||||
{
|
||||
Option "$DSPLYMNU_SLOPETILT", "cl_slopetilting", "OnOff"
|
||||
}
|
||||
ifnotgame(Blood, Exhumed)
|
||||
{
|
||||
Option "$DSPLYMNU_VIEWBOB", "cl_viewbob", "OnOff"
|
||||
|
|
Loading…
Reference in a new issue