diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index a04d75481..20f6710ae 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -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 diff --git a/source/build/include/buildtypes.h b/source/build/include/buildtypes.h index 9cb4289e9..ba52cb80c 100644 --- a/source/build/include/buildtypes.h +++ b/source/build/include/buildtypes.h @@ -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 diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index c16ecf467..344117052 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -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 diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp index 9ce27d667..5fe8a5a8e 100644 --- a/source/build/src/engine.cpp +++ b/source/build/src/engine.cpp @@ -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; diff --git a/source/common/2d/v_2ddrawer.cpp b/source/common/2d/v_2ddrawer.cpp index bd341a551..34b6edf4e 100644 --- a/source/common/2d/v_2ddrawer.cpp +++ b/source/common/2d/v_2ddrawer.cpp @@ -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. diff --git a/source/common/2d/v_2ddrawer.h b/source/common/2d/v_2ddrawer.h index 30fba1ce6..feac2e4d7 100644 --- a/source/common/2d/v_2ddrawer.h +++ b/source/common/2d/v_2ddrawer.h @@ -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 &v); diff --git a/source/common/audio/music/music.cpp b/source/common/audio/music/music.cpp index 9ab41c776..c1c8290f6 100644 --- a/source/common/audio/music/music.cpp +++ b/source/common/audio/music/music.cpp @@ -110,13 +110,18 @@ void S_SetMusicCallbacks(MusicCallbacks* cb) //========================================================================== static std::unique_ptr musicStream; +static TArray 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 convert; static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata) diff --git a/source/common/audio/music/s_music.h b/source/common/audio/music/s_music.h index ef852273c..a76189fc1 100644 --- a/source/common/audio/music/s_music.h +++ b/source/common/audio/music/s_music.h @@ -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 { diff --git a/source/common/engine/d_event.cpp b/source/common/engine/d_event.cpp index b42915c7b..3a779a5fc 100644 --- a/source/common/engine/d_event.cpp +++ b/source/common/engine/d_event.cpp @@ -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)) - continue; // console ate the event - if (M_Responder (ev)) - continue; // menu ate the event + + if (gamestate != GS_INTRO) // GS_INTRO blocks the UI. + { + if (C_Responder(ev)) + continue; // console ate the event + if (M_Responder(ev)) + continue; // menu ate the event + } + G_Responder (ev); } } diff --git a/source/common/rendering/hwrenderer/hw_draw2d.cpp b/source/common/rendering/hwrenderer/hw_draw2d.cpp index 36fd8f369..cf3399432 100644 --- a/source/common/rendering/hwrenderer/hw_draw2d.cpp +++ b/source/common/rendering/hwrenderer/hw_draw2d.cpp @@ -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); diff --git a/source/core/cheats.cpp b/source/core/cheats.cpp index d8695173a..1a7086f8b 100644 --- a/source/core/cheats.cpp +++ b/source/core/cheats.cpp @@ -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); diff --git a/source/core/cheats.h b/source/core/cheats.h index bc4245ede..7398073ae 100644 --- a/source/core/cheats.h +++ b/source/core/cheats.h @@ -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); diff --git a/source/core/console/d_event.cpp b/source/core/console/d_event.cpp index 2af44f0b8..007d0c46d 100644 --- a/source/core/console/d_event.cpp +++ b/source/core/console/d_event.cpp @@ -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)) diff --git a/source/core/d_net.cpp b/source/core/d_net.cpp index 32c7864e1..5650ea43f 100644 --- a/source/core/d_net.cpp +++ b/source/core/d_net.cpp @@ -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++) { diff --git a/source/core/d_protocol.h b/source/core/d_protocol.h index 50cc29555..cac55b56e 100644 --- a/source/core/d_protocol.h +++ b/source/core/d_protocol.h @@ -89,6 +89,7 @@ enum EDemoCommand DEM_GENERICCHEAT, DEM_GIVE, DEM_CHANGEMAP, + DEM_ENDSCREENJOB, DEM_MAX }; diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 108693080..edee2e936 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -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); } } diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 8a7ed3e12..7a65c950d 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -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: diff --git a/source/core/movie/movieplayer.cpp b/source/core/movie/movieplayer.cpp new file mode 100644 index 000000000..10e3e52c2 --- /dev/null +++ b/source/core/movie/movieplayer.cpp @@ -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 +#include +#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 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 Pic; + TArray 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 pFrame; + TArray 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(1); + } + FString error; + auto movie = OpenMovie(filename, ans, frameticks, nosoundcutoff, error); + if (!movie) + { + Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars()); + return Create(1); + } + return Create(movie); +} + + diff --git a/source/core/screenjob.cpp b/source/core/screenjob.cpp index 943178a68..02cdd3ede 100644 --- a/source/core/screenjob.cpp +++ b/source/core/screenjob.cpp @@ -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); +} + + +void DImageScreen::Draw(double smoothratio) +{ + if (tilenum > 0) tex = tileGetTexture(tilenum, true); 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; + if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE); + cleared = true; } -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DAnmPlayer : public DScreenJob -{ - // This doesn't need its own class type - anim_t anim; - TArray buffer; - int numframes = 0; - int curframe = 1; - int frametime = 0; - int nextframetime = 0; - AnimTextures animtex; - const AnimSound* animSnd; - const int* frameTicks; - bool nostopsound; - -public: - bool isvalid() { return numframes > 0; } - - DAnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff) - : animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff) - { - buffer = fr.ReadPadded(1); - fr.Close(); - - if (ANIM_LoadAnim(&anim, buffer.Data(), buffer.Size() - 1) < 0) - { - return; - } - numframes = ANIM_NumFrames(&anim); - animtex.SetSize(AnimTexture::Paletted, 320, 200); - } - - //--------------------------------------------------------------------------- - // - // - // - //--------------------------------------------------------------------------- - - int Frame(uint64_t clock, bool skiprequest) override - { - int currentclock = int(clock * 120 / 1'000'000'000); - - if (currentclock < nextframetime - 1) - { - twod->ClearScreen(); - DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE); - if (skiprequest && !nostopsound) soundEngine->StopAllChannels(); - return skiprequest? -1 : 1; - } - - animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe)); - frametime = currentclock; - - twod->ClearScreen(); - DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE); - - int delay = 20; - if (frameTicks) - { - if (curframe == 1) delay = frameTicks[0]; - else if (curframe < numframes - 2) delay = frameTicks[1]; - else delay = frameTicks[2]; - } - nextframetime += delay; - - if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++) - { - if (animSnd[i].framenum == curframe) - { - int sound = animSnd[i].soundnum; - if (sound == -1) - soundEngine->StopAllChannels(); - else if (SoundEnabled()) - soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_UI, sound, 1.f, ATTN_NONE); - } - } - if (!skiprequest && !nostopsound && curframe == numframes && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, -1)) return 1; - curframe++; - if (skiprequest && !nostopsound) soundEngine->StopAllChannels(); - return skiprequest ? -1 : curframe < numframes? 1 : 0; - } - - void OnDestroy() override - { - buffer.Reset(); - animtex.Clean(); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DMvePlayer : public DScreenJob -{ - InterplayDecoder decoder; - bool failed = false; - -public: - bool isvalid() { return !failed; } - - DMvePlayer(FileReader& fr) : decoder(SoundEnabled()) - { - failed = !decoder.Open(fr); - } - - //--------------------------------------------------------------------------- - // - // - // - //--------------------------------------------------------------------------- - - int Frame(uint64_t clock, bool skiprequest) override - { - if (failed) return -1; - bool playon = decoder.RunFrame(clock); - twod->ClearScreen(); - DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - - return skiprequest ? -1 : playon ? 1 : 0; - } - - void OnDestroy() override - { - decoder.Close(); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DVpxPlayer : public DScreenJob -{ - bool failed = false; - FileReader fr; - AnimTextures animtex; - const AnimSound* animSnd; - - unsigned width, height; - TArray Pic; - TArray 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 pFrame; - TArray 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(); }; - 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(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(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(fr); - if (!anm->isvalid()) - { - anm->Destroy(); - return nothing(); - } - return anm; - } - else if (!memcmp(id, "DKIF\0\0 \0VP80", 12)) - { - auto anm = Create(fr, ans, frameticks? frameticks[1] : 0 ); - if (!anm->isvalid()) - { - anm->Destroy(); - return nothing(); - } - anm->soundtrack = LookupMusic(filename, true); - return anm; - } - // add more formats here. - else - { - Printf(PRINT_BOLD, "%s: Unknown video format\n", filename); - } - return nothing(); -} - - //--------------------------------------------------------------------------- // // @@ -771,11 +125,10 @@ class ScreenJobRunner int index = -1; 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,75 +173,92 @@ 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(); - - if (startTime == -1) - { - lastTime = startTime = now; - } - else if (!M_Active()) - { - clock += now - lastTime; - if (clock == 0) clock = 1; - } - lastTime = now; - - float ms = (clock / 1'000'000) / jobs[index].job->fadetime; - float screenfade2 = clamp(screenfade - ms, 0.f, 1.f); - if (!M_Active()) twod->SetScreenFade(screenfade2); - if (screenfade2 <= 0.f) - { - twod->Unlock(); // must unlock before displaying. - return 0; - } - return 1; + 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); } + bool OnEvent(event_t* ev) + { + if (paused || index >= jobs.Size()) return false; + + if (ev->type == EV_KeyDown) + { + // We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here. + auto binding = Bindings.GetBinding(ev->data1); + if (binding.CompareNoCase("toggleconsole") == 0) + { + C_ToggleConsole(); + return true; + } + } + + if (jobs[index].job->state != DScreenJob::running) return false; + return jobs[index].job->OnEvent(ev); + } + + void OnFinished() + { + if (completion) completion(false); + completion = nullptr; // only finish once. + } + + void OnTick() + { + if (paused) return; + if (index >= jobs.Size()) + { + //DeleteJobs(); + //twod->SetScreenFade(1); + //twod->ClearScreen(); // This must not leave the 2d buffer empty. + //if (gamestate == GS_INTRO) OnFinished(); + //else Net_WriteByte(DEM_ENDSCREENJOB); // intermissions must be terminated synchronously. + } + else + { + if (jobs[index].job->state == DScreenJob::running) + { + jobs[index].job->ticks++; + jobs[index].job->OnTick(); + } + else if (jobs[index].job->state == DScreenJob::stopping) + { + fadeticks++; + } + } + } + bool RunFrame() { if (index >= jobs.Size()) @@ -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; } diff --git a/source/core/screenjob.h b/source/core/screenjob.h index 64467490e..b0a48b058 100644 --- a/source/core/screenjob.h +++ b/source/core/screenjob.h @@ -2,6 +2,7 @@ #include #include "dobject.h" #include "v_2ddrawer.h" +#include "d_eventbase.h" using CompletionFunc = std::function; 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 { diff --git a/source/core/textures/buildtiles.cpp b/source/core/textures/buildtiles.cpp index 49234754e..1000dd711 100644 --- a/source/core/textures/buildtiles.cpp +++ b/source/core/textures/buildtiles.cpp @@ -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); diff --git a/source/core/textures/buildtiles.h b/source/core/textures/buildtiles.h index d0d9d9cc8..77b928f2e 100644 --- a/source/core/textures/buildtiles.h +++ b/source/core/textures/buildtiles.h @@ -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); } diff --git a/source/games/blood/src/actor.cpp b/source/games/blood/src/actor.cpp index 364373526..f78571942 100644 --- a/source/games/blood/src/actor.cpp +++ b/source/games/blood/src/actor.cpp @@ -662,7 +662,7 @@ const ITEMDATA gItemData[] = { { 0, 2552, - (char)-8, + -8, 0, 32, 32, @@ -671,7 +671,7 @@ const ITEMDATA gItemData[] = { { 0, 2553, - (char)-8, + -8, 0, 32, 32, @@ -680,7 +680,7 @@ const ITEMDATA gItemData[] = { { 0, 2554, - (char)-8, + -8, 0, 32, 32, @@ -689,7 +689,7 @@ const ITEMDATA gItemData[] = { { 0, 2555, - (char)-8, + -8, 0, 32, 32, @@ -698,7 +698,7 @@ const ITEMDATA gItemData[] = { { 0, 2556, - (char)-8, + -8, 0, 32, 32, @@ -707,7 +707,7 @@ const ITEMDATA gItemData[] = { { 0, 2557, - (char)-8, + -8, 0, 32, 32, @@ -715,17 +715,17 @@ const ITEMDATA gItemData[] = { }, { 0, - -1, - (char)-8, + 2558, + -8, 0, - 255, - 255, + 32, + 32, -1, }, { 0, 519, - (char)-8, + -8, 0, 48, 48, @@ -734,7 +734,7 @@ const ITEMDATA gItemData[] = { { 0, 822, - (char)-8, + -8, 0, 40, 40, @@ -743,7 +743,7 @@ const ITEMDATA gItemData[] = { { 0, 2169, - (char)-8, + -8, 0, 40, 40, @@ -752,7 +752,7 @@ const ITEMDATA gItemData[] = { { 0, 2433, - (char)-8, + -8, 0, 40, 40, @@ -761,7 +761,7 @@ const ITEMDATA gItemData[] = { { 0, 517, - (char)-8, + -8, 0, 40, 40, @@ -770,7 +770,7 @@ const ITEMDATA gItemData[] = { { 0, 783, - (char)-8, + -8, 0, 40, 40, @@ -779,7 +779,7 @@ const ITEMDATA gItemData[] = { { 0, 896, - (char)-8, + -8, 0, 40, 40, @@ -788,7 +788,7 @@ const ITEMDATA gItemData[] = { { 0, 825, - (char)-8, + -8, 0, 40, 40, @@ -797,7 +797,7 @@ const ITEMDATA gItemData[] = { { 0, 827, - (char)-8, + -8, 0, 40, 40, @@ -806,7 +806,7 @@ const ITEMDATA gItemData[] = { { 0, 828, - (char)-8, + -8, 0, 40, 40, @@ -815,7 +815,7 @@ const ITEMDATA gItemData[] = { { 0, 829, - (char)-8, + -8, 0, 40, 40, @@ -824,7 +824,7 @@ const ITEMDATA gItemData[] = { { 0, 830, - (char)-8, + -8, 0, 80, 64, @@ -833,7 +833,7 @@ const ITEMDATA gItemData[] = { { 0, 831, - (char)-8, + -8, 0, 40, 40, @@ -842,7 +842,7 @@ const ITEMDATA gItemData[] = { { 0, 863, - (char)-8, + -8, 0, 40, 40, @@ -851,7 +851,7 @@ const ITEMDATA gItemData[] = { { 0, 760, - (char)-8, + -8, 0, 40, 40, @@ -860,7 +860,7 @@ const ITEMDATA gItemData[] = { { 0, 836, - (char)-8, + -8, 0, 40, 40, @@ -869,7 +869,7 @@ const ITEMDATA gItemData[] = { { 0, 851, - (char)-8, + -8, 0, 40, 40, @@ -878,7 +878,7 @@ const ITEMDATA gItemData[] = { { 0, 2428, - (char)-8, + -8, 0, 40, 40, @@ -887,7 +887,7 @@ const ITEMDATA gItemData[] = { { 0, 839, - (char)-8, + -8, 0, 40, 40, @@ -896,7 +896,7 @@ const ITEMDATA gItemData[] = { { 0, 768, - (char)-8, + -8, 0, 64, 64, @@ -905,7 +905,7 @@ const ITEMDATA gItemData[] = { { 0, 840, - (char)-8, + -8, 0, 48, 48, @@ -914,7 +914,7 @@ const ITEMDATA gItemData[] = { { 0, 841, - (char)-8, + -8, 0, 48, 48, @@ -923,7 +923,7 @@ const ITEMDATA gItemData[] = { { 0, 842, - (char)-8, + -8, 0, 48, 48, @@ -932,7 +932,7 @@ const ITEMDATA gItemData[] = { { 0, 843, - (char)-8, + -8, 0, 48, 48, @@ -941,7 +941,7 @@ const ITEMDATA gItemData[] = { { 0, 683, - (char)-8, + -8, 0, 40, 40, @@ -950,7 +950,7 @@ const ITEMDATA gItemData[] = { { 0, 521, - (char)-8, + -8, 0, 40, 40, @@ -959,7 +959,7 @@ const ITEMDATA gItemData[] = { { 0, 604, - (char)-8, + -8, 0, 40, 40, @@ -968,7 +968,7 @@ const ITEMDATA gItemData[] = { { 0, 520, - (char)-8, + -8, 0, 40, 40, @@ -977,7 +977,7 @@ const ITEMDATA gItemData[] = { { 0, 803, - (char)-8, + -8, 0, 40, 40, @@ -986,7 +986,7 @@ const ITEMDATA gItemData[] = { { 0, 518, - (char)-8, + -8, 0, 40, 40, @@ -995,7 +995,7 @@ const ITEMDATA gItemData[] = { { 0, 522, - (char)-8, + -8, 0, 40, 40, @@ -1004,7 +1004,7 @@ const ITEMDATA gItemData[] = { { 0, 523, - (char)-8, + -8, 0, 40, 40, @@ -1013,7 +1013,7 @@ const ITEMDATA gItemData[] = { { 0, 837, - (char)-8, + -8, 0, 80, 64, @@ -1022,7 +1022,7 @@ const ITEMDATA gItemData[] = { { 0, 2628, - (char)-8, + -8, 0, 64, 64, @@ -1031,7 +1031,7 @@ const ITEMDATA gItemData[] = { { 0, 2586, - (char)-8, + -8, 0, 64, 64, @@ -1040,7 +1040,7 @@ const ITEMDATA gItemData[] = { { 0, 2578, - (char)-8, + -8, 0, 64, 64, @@ -1049,7 +1049,7 @@ const ITEMDATA gItemData[] = { { 0, 2602, - (char)-8, + -8, 0, 64, 64, @@ -1058,7 +1058,7 @@ const ITEMDATA gItemData[] = { { 0, 2594, - (char)-8, + -8, 0, 64, 64, @@ -1067,7 +1067,7 @@ const ITEMDATA gItemData[] = { { 0, 753, - (char)-8, + -8, 0, 64, 64, @@ -1076,7 +1076,7 @@ const ITEMDATA gItemData[] = { { 0, 753, - (char)-8, + -8, 7, 64, 64, @@ -1085,7 +1085,7 @@ const ITEMDATA gItemData[] = { { 0, 3558, - (char)-128, + -128, 0, 64, 64, @@ -1094,7 +1094,7 @@ const ITEMDATA gItemData[] = { { 0, 3558, - (char)-128, + -128, 7, 64, 64, @@ -1106,7 +1106,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 618, - (char)-8, + -8, 0, 40, 40, @@ -1117,7 +1117,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 589, - (char)-8, + -8, 0, 48, 48, @@ -1128,7 +1128,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 589, - (char)-8, + -8, 0, 48, 48, @@ -1139,7 +1139,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 809, - (char)-8, + -8, 0, 48, 48, @@ -1150,7 +1150,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 811, - (char)-8, + -8, 0, 48, 48, @@ -1161,7 +1161,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 810, - (char)-8, + -8, 0, 48, 48, @@ -1172,7 +1172,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 820, - (char)-8, + -8, 0, 24, 24, @@ -1183,7 +1183,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 619, - (char)-8, + -8, 0, 48, 48, @@ -1194,7 +1194,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 812, - (char)-8, + -8, 0, 48, 48, @@ -1205,7 +1205,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 813, - (char)-8, + -8, 0, 48, 48, @@ -1216,7 +1216,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 525, - (char)-8, + -8, 0, 48, 48, @@ -1227,7 +1227,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 814, - (char)-8, + -8, 0, 48, 48, @@ -1238,7 +1238,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 817, - (char)-8, + -8, 0, 48, 48, @@ -1249,7 +1249,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 548, - (char)-8, + -8, 0, 24, 24, @@ -1260,7 +1260,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 0, - (char)-8, + -8, 0, 48, 48, @@ -1271,7 +1271,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 0, - (char)-8, + -8, 0, 48, 48, @@ -1282,7 +1282,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 816, - (char)-8, + -8, 0, 48, 48, @@ -1293,7 +1293,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 818, - (char)-8, + -8, 0, 48, 48, @@ -1304,7 +1304,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 819, - (char)-8, + -8, 0, 48, 48, @@ -1315,7 +1315,7 @@ const AMMOITEMDATA gAmmoItemData[] = { { 0, 801, - (char)-8, + -8, 0, 48, 48, @@ -1340,7 +1340,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1351,7 +1351,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 559, - (char)-8, + -8, 0, 48, 48, @@ -1362,7 +1362,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 558, - (char)-8, + -8, 0, 48, 48, @@ -1373,7 +1373,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 524, - (char)-8, + -8, 0, 48, 48, @@ -1384,7 +1384,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 525, - (char)-8, + -8, 0, 48, 48, @@ -1395,7 +1395,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 539, - (char)-8, + -8, 0, 48, 48, @@ -1406,7 +1406,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 526, - (char)-8, + -8, 0, 48, 48, @@ -1417,7 +1417,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1428,7 +1428,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 618, - (char)-8, + -8, 0, 48, 48, @@ -1439,7 +1439,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 589, - (char)-8, + -8, 0, 48, 48, @@ -1450,7 +1450,7 @@ const WEAPONITEMDATA gWeaponItemData[] = { { 0, 800, - (char)-8, + -8, 0, 48, 48, @@ -1468,7 +1468,7 @@ const MissileType missileInfo[] = { 512, 40, 40, - (char)-16, + -16, 16, }, // Regular flare @@ -1478,7 +1478,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 32, }, // Tesla alt @@ -1488,7 +1488,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 32, }, // Flare alt @@ -1498,7 +1498,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 4, }, // Spray flame @@ -1508,7 +1508,7 @@ const MissileType missileInfo[] = { 0, 24, 24, - (char)-128, + -128, 16, }, // Fireball @@ -1518,7 +1518,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 32, }, // Tesla regular @@ -1528,7 +1528,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 16, }, // EctoSkull @@ -1538,7 +1538,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-24, + -24, 32, }, // Hellhound flame @@ -1548,7 +1548,7 @@ const MissileType missileInfo[] = { 0, 24, 24, - (char)-128, + -128, 16, }, // Puke @@ -1558,7 +1558,7 @@ const MissileType missileInfo[] = { 0, 16, 16, - (char)-16, + -16, 16, }, // Reserved @@ -1568,7 +1568,7 @@ const MissileType missileInfo[] = { 0, 8, 8, - (char)0, + 0, 16, }, // Stone gargoyle projectile @@ -1578,7 +1578,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 16, }, // Napalm launcher @@ -1588,7 +1588,7 @@ const MissileType missileInfo[] = { 0, 30, 30, - (char)-128, + -128, 24, }, // Cerberus fireball @@ -1598,7 +1598,7 @@ const MissileType missileInfo[] = { 0, 30, 30, - (char)-128, + -128, 24, }, // Tchernobog fireball @@ -1608,7 +1608,7 @@ const MissileType missileInfo[] = { 0, 24, 24, - (char)-128, + -128, 16, }, // Regular life leech @@ -1618,7 +1618,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 16, }, // Dropped life leech (enough ammo) @@ -1628,7 +1628,7 @@ const MissileType missileInfo[] = { 0, 16, 16, - (char)-128, + -128, 16, }, // Dropped life leech (no ammo) @@ -1638,7 +1638,7 @@ const MissileType missileInfo[] = { 0, 32, 32, - (char)-128, + -128, 16, } }; @@ -1654,7 +1654,7 @@ const THINGINFO thingInfo[] = { 80, 384, 907, - (char)0, + 0, 0, 0, 0, @@ -1671,7 +1671,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 3444, - (char)-16, + -16, 0, 32, 32, @@ -1687,7 +1687,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 3457, - (char)-16, + -16, 0, 32, 32, @@ -1703,7 +1703,7 @@ const THINGINFO thingInfo[] = { 80, 0, 739, - (char)0, + 0, 0, 0, 0, @@ -1719,7 +1719,7 @@ const THINGINFO thingInfo[] = { 80, 0, 642, - (char)0, + 0, 0, 0, 0, @@ -1735,7 +1735,7 @@ const THINGINFO thingInfo[] = { 0, 0, 462, - (char)0, + 0, 0, 0, 0, @@ -1751,7 +1751,7 @@ const THINGINFO thingInfo[] = { 0, 0, 266, - (char)0, + 0, 0, 0, 0, @@ -1767,7 +1767,7 @@ const THINGINFO thingInfo[] = { 0, 0, 796, - (char)0, + 0, 0, 0, 0, @@ -1783,7 +1783,7 @@ const THINGINFO thingInfo[] = { 0, 0, 1127, - (char)0, + 0, 0, 0, 0, @@ -1799,7 +1799,7 @@ const THINGINFO thingInfo[] = { 0, 0, 1142, - (char)0, + 0, 0, 0, 0, @@ -1815,7 +1815,7 @@ const THINGINFO thingInfo[] = { 0, 0, 1069, - (char)0, + 0, 0, 0, 0, @@ -1831,7 +1831,7 @@ const THINGINFO thingInfo[] = { 0, 0, 483, - (char)0, + 0, 0, 0, 0, @@ -1847,7 +1847,7 @@ const THINGINFO thingInfo[] = { 0, 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1863,7 +1863,7 @@ const THINGINFO thingInfo[] = { 0, 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1879,7 +1879,7 @@ const THINGINFO thingInfo[] = { 0, 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1895,7 +1895,7 @@ const THINGINFO thingInfo[] = { 0, 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1911,7 +1911,7 @@ const THINGINFO thingInfo[] = { 0, 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1927,7 +1927,7 @@ const THINGINFO thingInfo[] = { 0, 0, -1, - (char)0, + 0, 0, 0, 0, @@ -1943,7 +1943,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 3422, - (char)-32, + -32, 0, 32, 32, @@ -1959,7 +1959,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 3433, - (char)-32, + -32, 0, 32, 32, @@ -1975,7 +1975,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 3467, - (char)-128, + -128, 0, 32, 32, @@ -1991,7 +1991,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 1462, - (char)0, + 0, 0, 32, 32, @@ -2007,7 +2007,7 @@ const THINGINFO thingInfo[] = { 1600, 256, -1, - (char)0, + 0, 0, 0, 0, @@ -2023,7 +2023,7 @@ const THINGINFO thingInfo[] = { 0, 0, 1147, - (char)0, + 0, 10, 0, 0, @@ -2039,7 +2039,7 @@ const THINGINFO thingInfo[] = { 0, 0, 1160, - (char)0, + 0, 2, 0, 0, @@ -2055,7 +2055,7 @@ const THINGINFO thingInfo[] = { 0, 257, -1, - (char)0, + 0, 0, 0, 0, @@ -2071,7 +2071,7 @@ const THINGINFO thingInfo[] = { 0, 257, -1, - (char)0, + 0, 0, 0, 0, @@ -2087,7 +2087,7 @@ const THINGINFO thingInfo[] = { 1280, 257, 3405, - (char)0, + 0, 0, 40, 40, @@ -2103,7 +2103,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 3281, - (char)-128, + -128, 0, 32, 32, @@ -2119,7 +2119,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 2020, - (char)-128, + -128, 0, 32, 32, @@ -2135,7 +2135,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 1860, - (char)-128, + -128, 0, 32, 32, @@ -2151,7 +2151,7 @@ const THINGINFO thingInfo[] = { 1600, 257, 800, - (char)-128, + -128, 0, 48, 48, @@ -2167,7 +2167,7 @@ const THINGINFO thingInfo[] = { 1600, 0, 2443, - (char)-128, + -128, 0, 16, 16, @@ -2183,7 +2183,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 3444, - (char)-16, + -16, 7, 32, 32, @@ -2199,7 +2199,7 @@ const THINGINFO thingInfo[] = { 1600, 256, 1462, - (char)0, + 0, 0, 32, 32, @@ -2215,7 +2215,7 @@ const THINGINFO thingInfo[] = { 1600, 257, 800, - (char)-128, + -128, 0, 44, 44, @@ -6533,7 +6533,7 @@ void actFireVector(spritetype *pShooter, int a2, int a3, int a4, int a5, int a6, int y = gHitInfo.hity-MulScale(a5, 16, 14); int z = gHitInfo.hitz-MulScale(a6, 256, 14); short nSector = gHitInfo.hitsect; - unsigned char nSurf = kSurfNone; + uint8_t nSurf = kSurfNone; if (nRange == 0 || approxDist(gHitInfo.hitx-pShooter->x, gHitInfo.hity-pShooter->y) < nRange) { switch (hit) diff --git a/source/games/blood/src/actor.h b/source/games/blood/src/actor.h index de2f2788c..28a80be2c 100644 --- a/source/games/blood/src/actor.h +++ b/source/games/blood/src/actor.h @@ -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; diff --git a/source/games/blood/src/credits.cpp b/source/games/blood/src/credits.cpp index c18453352..90f9cbc9d 100644 --- a/source/games/blood/src/credits.cpp +++ b/source/games/blood/src/credits.cpp @@ -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(1), []() { sndStartSample("THUNDER2", 128, -1); } }; jobs[job++] = { Create(2050) }; } - if (fileSystem.FindFile("gti.smk")) + if (fileSystem.FindFile("gti.smk") != -1) { jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) }; } diff --git a/source/games/blood/src/db.cpp b/source/games/blood/src/db.cpp index f6bb16f7f..52c0f0273 100644 --- a/source/games/blood/src/db.cpp +++ b/source/games/blood/src/db.cpp @@ -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); diff --git a/source/games/blood/src/dude.h b/source/games/blood/src/dude.h index dd899dd0f..5bf74b744 100644 --- a/source/games/blood/src/dude.h +++ b/source/games/blood/src/dude.h @@ -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 diff --git a/source/games/blood/src/endgame.cpp b/source/games/blood/src/endgame.cpp index 497a7f55d..a50b2e298 100644 --- a/source/games/blood/src/endgame.cpp +++ b/source/games/blood/src/endgame.cpp @@ -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; } }; @@ -274,7 +273,7 @@ CSecretMgr gSecretMgr; CKillMgr gKillMgr; class DBloodLoadScreen : public DScreenJob -{ +{ const char* pzLoadingScreenText1; MapRecord* rec; @@ -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; } }; diff --git a/source/games/blood/src/fire.cpp b/source/games/blood/src/fire.cpp index de4f759cb..9b76b20cd 100644 --- a/source/games/blood/src/fire.cpp +++ b/source/games/blood/src/fire.cpp @@ -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) { diff --git a/source/games/blood/src/fx.cpp b/source/games/blood/src/fx.cpp index 9ee8ec4fe..074b71f03 100644 --- a/source/games/blood/src/fx.cpp +++ b/source/games/blood/src/fx.cpp @@ -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); } } diff --git a/source/games/blood/src/hudsprites.cpp b/source/games/blood/src/hudsprites.cpp index 668e9ee69..c1fe2103b 100644 --- a/source/games/blood/src/hudsprites.cpp +++ b/source/games/blood/src/hudsprites.cpp @@ -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] = { diff --git a/source/games/blood/src/levels.h b/source/games/blood/src/levels.h index ac0be6f78..27d65964d 100644 --- a/source/games/blood/src/levels.h +++ b/source/games/blood/src/levels.h @@ -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; diff --git a/source/games/blood/src/misc.h b/source/games/blood/src/misc.h index ea41e5c53..c77979eea 100644 --- a/source/games/blood/src/misc.h +++ b/source/games/blood/src/misc.h @@ -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; diff --git a/source/games/blood/src/player.h b/source/games/blood/src/player.h index 84bd5030c..53f4dc4a3 100644 --- a/source/games/blood/src/player.h +++ b/source/games/blood/src/player.h @@ -182,7 +182,7 @@ struct PLAYER struct AMMOINFO { int max; - signed char vectorType; + int8_t vectorType; }; struct POWERUPINFO diff --git a/source/games/blood/src/qav.h b/source/games/blood/src/qav.h index 8b73ca51c..9c1cdde93 100644 --- a/source/games/blood/src/qav.h +++ b/source/games/blood/src/qav.h @@ -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]; }; diff --git a/source/games/blood/src/seq.h b/source/games/blood/src/seq.h index 724f5b081..7e0baed6f 100644 --- a/source/games/blood/src/seq.h +++ b/source/games/blood/src/seq.h @@ -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(); }; diff --git a/source/games/blood/src/tile.cpp b/source/games/blood/src/tile.cpp index a58fd94f6..2ee8fc334 100644 --- a/source/games/blood/src/tile.cpp +++ b/source/games/blood/src/tile.cpp @@ -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) diff --git a/source/games/blood/src/weapon.cpp b/source/games/blood/src/weapon.cpp index fd2424770..3d4bb9ca1 100644 --- a/source/games/blood/src/weapon.cpp +++ b/source/games/blood/src/weapon.cpp @@ -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) diff --git a/source/games/duke/src/2d_d.cpp b/source/games/duke/src/2d_d.cpp index 846b66429..5d54e6589 100644 --- a/source/games/duke/src/2d_d.cpp +++ b/source/games/duke/src/2d_d.cpp @@ -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(), []() { S_PlaySpecialMusic(MUS_INTRO); } }; + else jobs[job++] = { Create(1), []() { S_PlaySpecialMusic(MUS_INTRO); } }; if (!isNam()) jobs[job++] = { Create(), nullptr }; } else S_PlaySpecialMusic(MUS_INTRO); @@ -311,70 +317,68 @@ void Logo_d(const CompletionFunc &completion) // //--------------------------------------------------------------------------- -class DEpisode1End1 : public DScreenJob +class DEpisode1End1 : public DSkippableScreenJob { int bonuscnt = 0; + int bossani = -1; + int breatheani = -1; + bool breathebg = false; + + static inline const int breathe[] = + { + 0, 30,VICTORY1 + 1,176,59, + 30, 60,VICTORY1 + 2,176,59, + 60, 90,VICTORY1 + 1,176,59, + 90, 120,0 ,176,59 + }; + + static inline const int bossmove[] = + { + 0, 120,VICTORY1 + 3,86,59, + 220, 260,VICTORY1 + 4,86,59, + 260, 290,VICTORY1 + 5,86,59, + 290, 320,VICTORY1 + 6,86,59, + 320, 350,VICTORY1 + 7,86,59, + 350, 380,VICTORY1 + 8,86,59, + 350, 380,VICTORY1 + 8,86,59, + }; public: - DEpisode1End1() : DScreenJob(fadein | fadeout) {} + DEpisode1End1() : DSkippableScreenJob(fadein | fadeout) {} - int Frame(uint64_t nsclock, bool skiprequest) override + void OnTick() { - static const int breathe[] = - { - 0, 30,VICTORY1 + 1,176,59, - 30, 60,VICTORY1 + 2,176,59, - 60, 90,VICTORY1 + 1,176,59, - 90, 120,0 ,176,59 - }; - - static const int bossmove[] = - { - 0, 120,VICTORY1 + 3,86,59, - 220, 260,VICTORY1 + 4,86,59, - 260, 290,VICTORY1 + 5,86,59, - 290, 320,VICTORY1 + 6,86,59, - 320, 350,VICTORY1 + 7,86,59, - 350, 380,VICTORY1 + 8,86,59, - 350, 380,VICTORY1 + 8,86,59, - }; - - auto translation = TRANSLATION(Translation_BasePalettes, ENDINGPAL); - - int currentclock = nsclock * 120 / 1'000'000'000; - - uint64_t span = nsclock / 1'000'000; - - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(VICTORY1, true), 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true, TAG_DONE); + 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) - { + if (t == 10 && bonuscnt == 1) + { S_PlaySound(SHOTGUN_FIRE, CHAN_AUTO, CHANF_UI); S_PlaySound(SQUISHED, CHAN_AUTO, CHANF_UI); - bonuscnt++; + bonuscnt++; } - DrawTexture(twod, tileGetTexture(bossmove[t + 2], true), bossmove[t + 3], bossmove[t + 4], DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); + bossani = t; } + } // Breathe if (currentclock < 450 || currentclock >= 750) { if (currentclock >= 750) { - DrawTexture(twod, tileGetTexture(VICTORY1 + 8, true), 86, 59, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); - if (currentclock >= 750 && bonuscnt == 2) - { + breathebg = true; + if (currentclock >= 750 && bonuscnt == 2) + { S_PlaySound(DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI); - bonuscnt++; + bonuscnt++; } } for (int t = 0; t < 20; t += 5) @@ -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, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); + breatheani = t; } } - // Only end after having faded out. - return skiprequest ? -1 : 1; + + } + + void Draw(double) override + { + auto translation = TRANSLATION(Translation_BasePalettes, ENDINGPAL); + + twod->ClearScreen(); + DrawTexture(twod, tileGetTexture(VICTORY1, true), 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true, TAG_DONE); + + if (bossani != -1) + { + DrawTexture(twod, tileGetTexture(bossmove[bossani + 2], true), bossmove[bossani + 3], bossmove[bossani + 4], DTA_FullscreenScale, FSMode_Fit320x200, + DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); + } + + if (breathebg) + { + DrawTexture(twod, tileGetTexture(VICTORY1 + 8, true), 86, 59, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); + } + + if (breatheani != -1) + { + DrawTexture(twod, tileGetTexture(breathe[breatheani + 2], true), breathe[breatheani + 3], breathe[breatheani + 4], DTA_FullscreenScale, FSMode_Fit320x200, + DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); + } } }; @@ -409,7 +438,6 @@ public: FGameTexture* getTexture() { - // 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() }; } 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; } }; diff --git a/source/games/duke/src/2d_r.cpp b/source/games/duke/src/2d_r.cpp index e31a65af3..e89880d22 100644 --- a/source/games/duke/src/2d_r.cpp +++ b/source/games/duke/src/2d_r.cpp @@ -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); - } + 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)); + if (!S_CheckSoundPlaying(-1, 35) && ticks > 15 * GameTicRate) state = finished; // make sure it stays, even if sound is off. + } + void Draw(double) override + { + auto tex = tileGetTexture(ENDGAME + ((ticks >> 2) & 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; - } - return 1; } }; @@ -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; } }; diff --git a/source/games/duke/src/actors.cpp b/source/games/duke/src/actors.cpp index 05e2dc458..89ba3c3fa 100644 --- a/source/games/duke/src/actors.cpp +++ b/source/games/duke/src/actors.cpp @@ -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); } } diff --git a/source/games/exhumed/src/2d.cpp b/source/games/exhumed/src/2d.cpp index 5621ae4ac..43661c7cd 100644 --- a/source/games/exhumed/src/2d.cpp +++ b/source/games/exhumed/src/2d.cpp @@ -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 @@ -383,13 +384,23 @@ class DLobotomyScreen : public DImageScreen 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 + void Start() override { - int ticker = clock * 120 / 1'000'000'000; - if (clock == 0) - { 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 + 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 @@ -651,20 +666,13 @@ 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,84 +736,82 @@ 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; - } - - if (inputState.CheckAllInput()) - { - if (var_2C < 8) { - var_2C *= 2; - } - - } - - if (curYPos > destYPos&& var_2C > 0) { + curYPos += delta; + + 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++; assert(nLevelNew < 20); - + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); - + if (curYPos <= destYPos) { - var_2C = 2; + delta = 2; } else { - var_2C = -2; + delta = -2; } - + nIdleSeconds = 0; } 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--; assert(nLevelNew >= 0); - + 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 (clock == 0) + if (check > 0 && check != selectedlevelnew) { - if (check > 0 && check != selectedlevelnew) return 0; // immediately abort if the player selected a different level on the map + state = finished; + return; // immediately abort if the player selected a different level on the map + } + + check = -1; StopAllSounds(); if (edx != -1) { @@ -997,20 +1007,29 @@ public: } } + void OnTick() override + { + if (!cont) + { + state = finished; + // quit the game if we've finished level 4 and displayed the advert text + if (isShareware() && currentCinemaPalette == 3) + { + gameaction = ga_mainmenu; + } + return; + } + } + + void Draw(double smoothratio) override + { twod->ClearScreen(); + if (check == 0) return; 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; - - // quit the game if we've finished level 4 and displayed the advert text - if (isShareware() && currentCinemaPalette == 3 && ret != 1) - { - gameaction = ga_mainmenu; + cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate)); } - return ret; - } }; //--------------------------------------------------------------------------- @@ -1029,6 +1048,7 @@ class DLastLevelCinema : public DScreenJob int nextclock = 4; unsigned int nStringTypeOn, nCharTypeOn; int screencnt = 0; + bool skiprequest = false; TArray 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 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; } }; diff --git a/source/games/exhumed/src/gameloop.cpp b/source/games/exhumed/src/gameloop.cpp index 82172390d..ac7c97326 100644 --- a/source/games/exhumed/src/gameloop.cpp +++ b/source/games/exhumed/src/gameloop.cpp @@ -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() }); // we need something in here even in the multiplayer case. + jobs.Push({ Create(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); } //--------------------------------------------------------------------------- diff --git a/source/games/exhumed/src/movie.cpp b/source/games/exhumed/src/movie.cpp index d26b9ce52..cb632cc05 100644 --- a/source/games/exhumed/src/movie.cpp +++ b/source/games/exhumed/src/movie.cpp @@ -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(); + return Create(1); } char buffer[4]; fp.Read(buffer, 4); diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp index 96df6d5b5..1578366b8 100644 --- a/source/games/exhumed/src/player.cpp +++ b/source/games/exhumed/src/player.cpp @@ -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,10 +2664,13 @@ loc_1BD2E: pPlayer->bPlayerPan = false; } - double nVertPan = (pPlayer->nDestVertPan - pPlayer->horizon.horiz).asq16() * (1. / (FRACUNIT << 2)); - if (nVertPan != 0) + if (cl_slopetilting) { - pPlayer->horizon.addadjustment(abs(nVertPan) >= 4 ? clamp(nVertPan, -4, 4) : nVertPan * 2.); + double nVertPan = (pPlayer->nDestVertPan - pPlayer->horizon.horiz).asq16() * (1. / (FRACUNIT << 2)); + if (nVertPan != 0) + { + pPlayer->horizon.addadjustment(abs(nVertPan) >= 4 ? clamp(nVertPan, -4, 4) : nVertPan * 2.); + } } } else // else, player's health is less than 0 diff --git a/source/games/sw/src/2d.cpp b/source/games/sw/src/2d.cpp index cfe9a88b2..ce85b1bbf 100644 --- a/source/games/sw/src/2d.cpp +++ b/source/games/sw/src/2d.cpp @@ -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 + { + StopSound(); + } + + void Start() override + { + // Lo Wang feel like singing! + PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI); + } + + void OnTick() override { - twod->ClearScreen(); - int seconds = int(clock / 1'000'000'000); - if (clock == 0) - { - // Lo Wang feel like singing! - PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI); - } if (state == 0) { - if (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,15 +211,19 @@ class DSWCreditsScreen : public DScreenJob } else { - if (seconds >= starttime + 8) + if (ticks >= starttime + 8 * GameTicRate) { curpic = CREDITS1_PIC + CREDITS2_PIC - curpic; - starttime = seconds; + starttime = ticks; } - DrawTexture(twod, tileGetTexture(curpic, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); } - if (skiprequest) StopSound(); - return skiprequest ? -1 : 1; + } + + void Draw(double) override + { + twod->ClearScreen(); + if (state == 1) + DrawTexture(twod, tileGetTexture(curpic, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); } }; @@ -377,31 +387,42 @@ private: (*(*State)->Animator)(0); } - 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 (clock == 0) + if (ev->type == EV_KeyDown) { - PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]); - } - - if (skiprequest && State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)]) - { - State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))]; - Tics = 0; - skiprequest = false; - nextclock = currentclock; - } - else - { - while (currentclock > nextclock) + if (State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)]) { - nextclock += synctics; - gStateControl(&State, &Tics); + State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))]; + Tics = 0; + nextclock = ticks; } } + return true; + } + + void Start() override + { + PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]); + } + + void OnTick() override + { + while (ticks > nextclock) + { + nextclock++; + gStateControl(&State, &Tics); + } + + if (State == State->NextState) + { + state = finished; + StopSound(); + } + } + + void Draw(double) override + { twod->ClearScreen(); 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; } }; diff --git a/wadsrc/static/engine/grpinfo.txt b/wadsrc/static/engine/grpinfo.txt index 9dcf1b2d7..9bddfdb43 100644 --- a/wadsrc/static/engine/grpinfo.txt +++ b/wadsrc/static/engine/grpinfo.txt @@ -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 { diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 4ddf92bca..f19c4ccea 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -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" - } + Option "$DSPLYMNU_SLOPETILT", "cl_slopetilting", "OnOff" ifnotgame(Blood, Exhumed) { Option "$DSPLYMNU_VIEWBOB", "cl_viewbob", "OnOff"