diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index cfd5a5634..55f5bcc6f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -749,7 +749,6 @@ set( NOT_COMPILED_SOURCE_FILES games/blood/src/callback.cpp games/blood/src/choke.cpp games/blood/src/controls.cpp - games/blood/src/credits.cpp games/blood/src/db.cpp games/blood/src/dude.cpp games/blood/src/d_menu.cpp @@ -835,7 +834,6 @@ set( NOT_COMPILED_SOURCE_FILES games/duke/src/spawn_r.cpp # Shadow Warrior - games/sw/src/2d.cpp games/sw/src/actor.cpp games/sw/src/ai.cpp games/sw/src/break.cpp @@ -1049,6 +1047,7 @@ set (PCH_SOURCES core/gamehud.cpp core/gamefuncs.cpp core/gameinput.cpp + core/g_mapinfo.cpp core/interpolate.cpp core/inputstate.cpp core/maphack.cpp diff --git a/source/common/2d/v_draw.cpp b/source/common/2d/v_draw.cpp index 70bf53042..e662baed4 100644 --- a/source/common/2d/v_draw.cpp +++ b/source/common/2d/v_draw.cpp @@ -324,6 +324,22 @@ DEFINE_ACTION_FUNCTION(_Screen, ClearClipRect) return 0; } +DEFINE_ACTION_FUNCTION(_Screen, ClearScreen) +{ + PARAM_PROLOGUE; + twod->ClearScreen(); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Screen, SetScreenFade) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(x); + twod->SetScreenFade(x); + return 0; +} + + void F2DDrawer::GetClipRect(int *x, int *y, int *w, int *h) { if (x) *x = clipleft; diff --git a/source/common/audio/music/music.cpp b/source/common/audio/music/music.cpp index c1c8290f6..f81b2396c 100644 --- a/source/common/audio/music/music.cpp +++ b/source/common/audio/music/music.cpp @@ -138,6 +138,10 @@ void S_StopCustomStream(SoundStream *stream) void S_PauseAllCustomStreams(bool on) { + static bool paused = false; + + if (paused == on) return; + paused = on; for (auto s : customStreams) { s->SetPaused(on); diff --git a/source/common/engine/namedef.h b/source/common/engine/namedef.h index cf8b8da62..8ec5d4081 100644 --- a/source/common/engine/namedef.h +++ b/source/common/engine/namedef.h @@ -1107,4 +1107,4 @@ xy(menu_change, "menu/change") xy(menu_advance, "menu/advance") xx(zoomsize) - +xx(ScreenJobRunner) diff --git a/source/common/platform/win32/base_sysfb.cpp b/source/common/platform/win32/base_sysfb.cpp index 9455dbae6..3cb8858a3 100644 --- a/source/common/platform/win32/base_sysfb.cpp +++ b/source/common/platform/win32/base_sysfb.cpp @@ -371,7 +371,6 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer() SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE); SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); - I_GetEvent(); static_cast(Video)->Shutdown(); } diff --git a/source/common/scripting/backend/codegen.cpp b/source/common/scripting/backend/codegen.cpp index f6f52f0bc..eaafaba98 100644 --- a/source/common/scripting/backend/codegen.cpp +++ b/source/common/scripting/backend/codegen.cpp @@ -8706,7 +8706,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) bool writable; ArgList[i] = ArgList[i]->Resolve(ctx); // must be resolved before the address is requested. - if (ArgList[i]->ValueType->isRealPointer()) + if (ArgList[i] && ArgList[i]->ValueType->isRealPointer()) { auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType; if (pointedType && pointedType->isDynArray()) diff --git a/source/common/scripting/core/dynarrays.cpp b/source/common/scripting/core/dynarrays.cpp index 3d8e87e85..6be216c12 100644 --- a/source/common/scripting/core/dynarrays.cpp +++ b/source/common/scripting/core/dynarrays.cpp @@ -37,6 +37,7 @@ #include "dobject.h" #include "vm.h" #include "types.h" +#include "v_draw.h" // We need one specific type for each of the 8 integral VM types and instantiate the needed functions for each of them. // Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible. @@ -412,6 +413,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Push, ArrayPushPush(val)); } +DEFINE_ACTION_FUNCTION(FDynArray_I32, PushV) +{ + PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32); + PARAM_VA_POINTER(va_reginfo); // Get the hidden type information array + VMVa_List args = { param + 1, 0, numparam - 2, va_reginfo + 1 }; + while (args.curindex < args.numargs) + { + if (args.reginfo[args.curindex] == REGT_INT) + { + self->Push(args.args[args.curindex++].i); + } + else if (args.reginfo[args.curindex] == REGT_FLOAT) + { + self->Push(int(args.args[args.curindex++].f)); + } + else ThrowAbortException(X_OTHER, "Invalid parameter in pushv, int expected"); + } + + + ACTION_RETURN_INT(self->Size()-1); +} + DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Pop, ArrayPop) { PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32); diff --git a/source/common/scripting/interface/stringformat.cpp b/source/common/scripting/interface/stringformat.cpp index 8e0bf0c75..da667cbbf 100644 --- a/source/common/scripting/interface/stringformat.cpp +++ b/source/common/scripting/interface/stringformat.cpp @@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl) ACTION_RETURN_FLOAT(self->ToDouble()); } -static void StringSplit(FString *self, TArray *tokens, const FString &delimiter, int keepEmpty) +static void StringSubst(FString *self, const FString &substr, const FString& replc) +{ + self->Substitute(substr, replc); +} + +DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Substitute, StringSubst) +{ + PARAM_SELF_STRUCT_PROLOGUE(FString); + PARAM_STRING(substr); + PARAM_STRING(replc); + StringSubst(self, substr, replc); + return 0; +} + +static void StringSplit(FString* self, TArray* tokens, const FString& delimiter, int keepEmpty) { self->Split(*tokens, delimiter, static_cast(keepEmpty)); } diff --git a/source/common/scripting/interface/vmnatives.cpp b/source/common/scripting/interface/vmnatives.cpp index 502d78de3..5eebabbab 100644 --- a/source/common/scripting/interface/vmnatives.cpp +++ b/source/common/scripting/interface/vmnatives.cpp @@ -49,6 +49,7 @@ #include "s_music.h" #include "i_interface.h" #include "base_sbar.h" +#include "image.h" //========================================================================== // @@ -336,8 +337,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetName) static int CheckForTexture(const FString& name, int type, int flags) { - // ForceLookup is intentionally blocked here, this flag is for internal use only. - return TexMan.CheckForTexture(name, static_cast(type), (flags & ~FTextureManager::TEXMAN_ForceLookup)).GetIndex(); + return TexMan.CheckForTexture(name, static_cast(type), flags).GetIndex(); } DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckForTexture, CheckForTexture) @@ -477,6 +477,20 @@ DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, OkForLocalization, OkForLocalization_) ACTION_RETURN_INT(OkForLocalization_(name, subst)); } +static int UseGamePalette(int index) +{ + auto tex = TexMan.GameByIndex(index, false); + if (!tex) return false; + auto image = tex->GetTexture()->GetImage(); + return image ? image->UseGamePalette() : false; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, UseGamePalette, UseGamePalette) +{ + PARAM_PROLOGUE; + PARAM_INT(texid); + ACTION_RETURN_INT(UseGamePalette(texid)); +} //===================================================================================== // @@ -867,6 +881,13 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, GetAllKeysForCommand) return 0; } +DEFINE_ACTION_FUNCTION(FKeyBindings, GetBinding) +{ + PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); + PARAM_INT(key); + ACTION_RETURN_STRING(self->GetBinding(key)); +} + DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand) { PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); @@ -914,6 +935,7 @@ DEFINE_GLOBAL_NAMED(mus_playing, musplaying); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, name); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop); +DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, handle); DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses) DEFINE_GLOBAL(Bindings) diff --git a/source/core/binaryangle.h b/source/core/binaryangle.h index b9d75593e..b843b1688 100644 --- a/source/core/binaryangle.h +++ b/source/core/binaryangle.h @@ -69,11 +69,11 @@ extern int16_t sintable[2048]; // //--------------------------------------------------------------------------- -inline int32_t bsin(const int ang, const int8_t shift = 0) +inline int bsin(const int ang, const int shift = 0) { return shift < 0 ? sintable[ang & 2047] >> abs(shift) : sintable[ang & 2047] << shift; } -inline double bsinf(const double ang, const int8_t shift = 0) +inline double bsinf(const double ang, const int shift = 0) { return g_sin(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift))); } @@ -85,11 +85,11 @@ inline double bsinf(const double ang, const int8_t shift = 0) // //--------------------------------------------------------------------------- -inline int32_t bcos(const int ang, const int8_t shift = 0) +inline int bcos(const int ang, const int shift = 0) { return shift < 0 ? sintable[(ang + 512) & 2047] >> abs(shift) : sintable[(ang + 512) & 2047] << shift; } -inline double bcosf(const double ang, const int8_t shift = 0) +inline double bcosf(const double ang, const int shift = 0) { return g_cos(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift))); } diff --git a/source/core/cheats.cpp b/source/core/cheats.cpp index 1a7086f8b..d422e5c20 100644 --- a/source/core/cheats.cpp +++ b/source/core/cheats.cpp @@ -242,7 +242,7 @@ void changeMap(int player, uint8_t** stream, bool skip) void endScreenJob(int player, uint8_t** stream, bool skip) { - if (!skip) EndScreenJob(); + if (!skip) gameaction = ga_endscreenjob; } //--------------------------------------------------------------------------- @@ -280,6 +280,7 @@ void DeferedStartGame(MapRecord* map, int skill, bool nostopsound) static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, const char *t2) { int numparm = g_gameType & (GAMEFLAG_SW | GAMEFLAG_PSEXHUMED) ? 1 : 2; // Handle games with episodic and non-episodic level order. + if (numparm == 2 && argv.argc() == 2) numparm = 1; if (argv.argc() <= numparm) { if (numparm == 2) Printf(PRINT_BOLD, "%s : %s episode 'e' and map 'm'\n", cmdname, t2); @@ -294,7 +295,7 @@ static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, cons Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n"); return nullptr; } - auto map = FindMapByLevelNum(numparm == 1 ? m : levelnum(e - 1, m - 1)); + auto map = FindMapByIndex(e, m); if (!map) { if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]); diff --git a/source/core/g_mapinfo.cpp b/source/core/g_mapinfo.cpp new file mode 100644 index 000000000..ff24af727 --- /dev/null +++ b/source/core/g_mapinfo.cpp @@ -0,0 +1,1232 @@ +/* +** g_level.cpp +** Parses MAPINFO +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2009-2021 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 +#include "mapinfo.h" +#include "g_mapinfo.h" +#include "templates.h" +#include "filesystem.h" +#include "cmdlib.h" +#include "v_video.h" +#include "gi.h" +#include "gstrings.h" +#include "autosegs.h" +#include "i_system.h" +#include "gamecontrol.h" +#include "autosegs.h" + +extern TArray clusters; +extern TArray volumes; +extern TArray> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array. + +static MapRecord TheDefaultLevelInfo; +static ClusterDef TheDefaultClusterInfo; + +TArray ParsedLumps(8); + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseOpenBrace() +{ + sc.MustGetStringName("{"); + sc.SetCMode(true); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::ParseCloseBrace() +{ + return sc.Compare("}"); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckAssign() +{ + return sc.CheckString("="); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseAssign() +{ + sc.MustGetStringName("="); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::MustParseAssign() +{ + sc.MustGetStringName("="); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseComma() +{ + sc.MustGetStringName(","); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckNumber() +{ + if (sc.CheckString(",")) + { + sc.MustGetNumber(); + return true; + } + return false; +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckFloat() +{ + if (sc.CheckString(",")) + { + sc.MustGetFloat(); + return true; + } + return false; +} + +//========================================================================== +// +// skips an entire parameter list that's separated by commas +// +//========================================================================== + +void FMapInfoParser::SkipToNext() +{ + if (sc.CheckString("=")) + { + do + { + sc.MustGetString(); + } + while (sc.CheckString(",")); + } +} + + +//========================================================================== +// +// checks if the current block was properly terminated +// +//========================================================================== + +void FMapInfoParser::CheckEndOfFile(const char *block) +{ + if (sc.End) + { + sc.ScriptError("Unexpected end of file in %s definition", block); + } +} + +//========================================================================== +// +// ParseLookupname +// +//========================================================================== + +bool FMapInfoParser::ParseLookupName(FString &dest) +{ + sc.MustGetString(); + dest = sc.String; + return true; +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseLumpOrTextureName(FString &name) +{ + sc.MustGetString(); + name = sc.String; +} + + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseMusic(FString &name, int &order) +{ + sc.MustGetString(); + name = sc.String; + if (CheckNumber()) + { + order = sc.Number; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseCutscene(CutsceneDef& cdef) +{ + FString sound; + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("video")) { ParseAssign(); sc.MustGetString(); cdef.video = sc.String; cdef.function = ""; } + else if (sc.Compare("function")) { ParseAssign(); sc.SetCMode(false); sc.MustGetString(); sc.SetCMode(true); cdef.function = sc.String; cdef.video = ""; } + else if (sc.Compare("sound")) { ParseAssign(); sc.MustGetString(); cdef.soundName = sc.String; } + else if (sc.Compare("soundid")) { ParseAssign(); sc.MustGetNumber(); cdef.soundID = sc.Number; } + else if (sc.Compare("fps")) { ParseAssign(); sc.MustGetNumber(); cdef.framespersec = sc.Number; } + else if (sc.Compare("transitiononly")) cdef.transitiononly = true; + else if (sc.Compare("delete")) { cdef.function = "none"; cdef.video = ""; } // this means 'play nothing', not 'not defined'. + else if (sc.Compare("clear")) cdef = {}; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseCluster() +{ + sc.MustGetNumber (); + auto clusterinfo = MustFindCluster(sc.Number); + + ParseOpenBrace(); + while (sc.GetString()) + { + if (sc.Compare("clear")) + { + *clusterinfo = {}; + } + else if (sc.Compare("name")) + { + ParseAssign(); + ParseLookupName(clusterinfo->name); + } + else if (sc.Compare("intro")) + { + ParseCutscene(clusterinfo->intro); + } + else if (sc.Compare("outro")) + { + ParseCutscene(clusterinfo->outro); + } + else if (sc.Compare("gameover")) + { + ParseCutscene(clusterinfo->gameover); + } + else if (sc.Compare("interbackground")) + { + ParseAssign(); + ParseLookupName(clusterinfo->InterBackground); + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in cluster definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("cluster"); +} + +//========================================================================== +// +// allow modification of maps defined through legacy means. +// +//========================================================================== + +bool FMapInfoParser::CheckLegacyMapDefinition(FString& mapname) +{ + if (Internal && (g_gameType & GAMEFLAG_BLOOD | GAMEFLAG_DUKECOMPAT | GAMEFLAG_SW) && sc.CheckString("{")) + { + sc.MustGetNumber(); + int vol = sc.Number; + if (!(g_gameType & GAMEFLAG_SW)) + { + // Blood and Duke use volume/level pairs + sc.MustGetStringName(","); + sc.MustGetNumber(); + int indx = sc.Number; + auto map = FindMapByIndexOnly(vol, indx); + if (!map) mapname = ""; + else mapname = map->labelName; + } + else + { + // SW only uses the level number + auto map = FindMapByLevelNum(vol); + if (!map) mapname = ""; + else mapname = map->labelName; + } + sc.MustGetStringName("}"); + return true; + } + return false; +} + +//========================================================================== +// +// ParseNextMap +// Parses a next map field +// +//========================================================================== + +void FMapInfoParser::ParseMapName(FString &mapname) +{ + if (!CheckLegacyMapDefinition(mapname)) + { + sc.MustGetString(); + mapname = ExtractFileBase(sc.String); + } +} + +//========================================================================== +// +// Map options +// +//========================================================================== + +DEFINE_MAP_OPTION(clear, true) +{ + // Save the names, reset and restore the names + FString fn = info->fileName; + FString dn = info->name; + FString ln = info->labelName; + *info = *parse.defaultinfoptr; + info->fileName = fn; + info->name = dn; + info->labelName = ln; +} + + +DEFINE_MAP_OPTION(levelnum, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->levelNumber = parse.sc.Number; +} + +DEFINE_MAP_OPTION(next, true) +{ + parse.ParseAssign(); + parse.ParseMapName(info->NextMap); +} + +DEFINE_MAP_OPTION(author, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->Author = parse.sc.String; +} + +DEFINE_MAP_OPTION(secretnext, true) +{ + parse.ParseAssign(); + parse.ParseMapName(info->NextSecret); +} + +DEFINE_MAP_OPTION(cluster, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->cluster = parse.sc.Number; + + // If this cluster hasn't been defined yet, add it. + MustFindCluster(info->cluster); +} + +DEFINE_MAP_OPTION(fade, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->fadeto = V_GetColor(nullptr, parse.sc); +} + +DEFINE_MAP_OPTION(partime, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->parTime = parse.sc.Number; +} + +DEFINE_MAP_OPTION(designertime, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->designerTime = parse.sc.Number; +} + +DEFINE_MAP_OPTION(music, true) +{ + parse.ParseAssign(); + parse.ParseMusic(info->music, info->musicorder); +} + +DEFINE_MAP_OPTION(cdtrack, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->cdSongId = parse.sc.Number; +} + +DEFINE_MAP_OPTION(intro, true) +{ + parse.ParseCutscene(info->intro); +} + +DEFINE_MAP_OPTION(outro, true) +{ + parse.ParseCutscene(info->outro); +} + +DEFINE_MAP_OPTION(interbackground, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->InterBackground = parse.sc.String; +} + + +/* currently all sounds are precached. This requires significant work on sound management and info collection. +DEFINE_MAP_OPTION(PrecacheSounds, true) +{ + parse.ParseAssign(); + + do + { + parse.sc.MustGetString(); + FSoundID snd = parse.sc.String; + if (snd == 0) + { + parse.sc.ScriptMessage("Unknown sound \"%s\"", parse.sc.String); + } + else + { + info->PrecacheSounds.Push(snd); + } + } while (parse.sc.CheckString(",")); +} +*/ + +DEFINE_MAP_OPTION(PrecacheTextures, true) +{ + parse.ParseAssign(); + do + { + parse.sc.MustGetString(); + //the texture manager is not initialized here so all we can do is store the texture's name. + info->PrecacheTextures.Push(parse.sc.String); + } while (parse.sc.CheckString(",")); +} + +DEFINE_MAP_OPTION(bordertexture, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->BorderTexture); +} + +DEFINE_MAP_OPTION(fogdensity, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->fogdensity = clamp(parse.sc.Number, 0, 512) >> 1; +} + +DEFINE_MAP_OPTION(skyfog, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->skyfog = parse.sc.Number; +} + +DEFINE_MAP_OPTION(message, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + if (parse.sc.Number < 1 || parse.sc.Number > MAX_MESSAGES) parse.sc.ScriptError("Invalid message ID %d - must be 1..32", parse.sc.Number); + int num = parse.sc.Number; + parse.ParseComma(); + parse.sc.MustGetString(); + info->messages[num] = parse.sc.String; +} + +/* stuff for later when the new renderer is done. +DEFINE_MAP_OPTION(lightmode, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + + if ((parse.sc.Number >= 0 && parse.sc.Number <= 4) || parse.sc.Number == 8 || parse.sc.Number == 16) + { + info->lightmode = ELightMode(parse.sc.Number); + } + else + { + parse.sc.ScriptMessage("Invalid light mode %d", parse.sc.Number); + } +} +*/ + +DEFINE_MAP_OPTION(skyrotate, false) +{ + parse.ParseAssign(); + parse.sc.MustGetFloat(); + info->skyrotatevector.X = (float)parse.sc.Float; + parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + info->skyrotatevector.Y = (float)parse.sc.Float; + parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + info->skyrotatevector.Z = (float)parse.sc.Float; + info->skyrotatevector.W = 0; + info->skyrotatevector.MakeUnit(); + parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + info->skyrotatevector.W = (float)parse.sc.Float; // W is the rotation speed. This must not be normalized +} + +DEFINE_MAP_OPTION(rr_startsound, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->rr_startsound = parse.sc.Number; +} + +DEFINE_MAP_OPTION(rr_mamaspawn, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->rr_mamaspawn = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_horiz, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->ex_ramses_horiz = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_cdtrack, false) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->ex_ramses_cdtrack = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_pup, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->ex_ramses_pup = parse.sc.Number; +} + +DEFINE_MAP_OPTION(ex_ramses_text, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->ex_ramses_text = parse.sc.Number; +} + +int ex_ramses_horiz = 11; +int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue! +FString ex_ramses_pup; +FString ex_ramses_text; + +//========================================================================== +// +// All flag based map options +// +//========================================================================== + +enum EMIType +{ + MITYPE_IGNORE, + MITYPE_EATNEXT, + MITYPE_SETFLAG, + MITYPE_CLRFLAG, + MITYPE_SCFLAGS, + MITYPE_SETFLAGG, + MITYPE_CLRFLAGG, + MITYPE_SCFLAGSG, + MITYPE_COMPATFLAG, +}; + +struct MapInfoFlagHandler +{ + const char *name; + EMIType type; + uint32_t data1, data2; + int gameflagmask; +} +MapFlagHandlers[] = +{ + { "nointermission", MITYPE_SETFLAG, LEVEL_NOINTERMISSION, 0, -1 }, + { "secretexitoverride", MITYPE_SETFLAG, LEVEL_SECRETEXITOVERRIDE, 0, -1 }, + { "clearinventory", MITYPE_SETFLAG, LEVEL_CLEARINVENTORY, 0, -1 }, + { "clearweapons", MITYPE_SETFLAG, LEVEL_CLEARWEAPONS, 0, -1 }, + { "forcenoeog", MITYPE_SETFLAG, LEVEL_FORCENOEOG, 0, -1 }, + { "rrra_hulkspawn", MITYPE_SETFLAGG,LEVEL_RR_HULKSPAWN, 0, GAMEFLAG_RRRA }, + { "rr_clearmoonshine", MITYPE_SETFLAGG,LEVEL_RR_CLEARMOONSHINE, 0, GAMEFLAG_RR }, + { "ex_training", MITYPE_SETFLAGG,LEVEL_EX_TRAINING, 0, GAMEFLAG_PSEXHUMED }, + { "ex_altsound", MITYPE_SETFLAGG,LEVEL_EX_ALTSOUND, 0, GAMEFLAG_PSEXHUMED }, + { "ex_countdown", MITYPE_SETFLAGG,LEVEL_EX_COUNTDOWN, 0, GAMEFLAG_PSEXHUMED }, + { "ex_multi", MITYPE_SETFLAGG,LEVEL_EX_MULTI, 0, GAMEFLAG_PSEXHUMED }, + { "sw_bossmeter_serpent", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_SERPENT, 0, GAMEFLAG_SW }, + { "sw_bossmeter_sumo", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_SUMO, 0, GAMEFLAG_SW }, + { "sw_bossmeter_zilla", MITYPE_SETFLAGG,LEVEL_SW_BOSSMETER_ZILLA, 0, GAMEFLAG_SW }, + { "sw_deathexit_serpent", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_SERPENT, 0, GAMEFLAG_SW }, + { "sw_deathexit_sumo", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_SUMO, 0, GAMEFLAG_SW }, + { "sw_deathexit_zilla", MITYPE_SETFLAGG,LEVEL_SW_DEATHEXIT_ZILLA, 0, GAMEFLAG_SW }, + { "sw_spawnmines", MITYPE_SETFLAGG,LEVEL_SW_SPAWNMINES, 0, GAMEFLAG_SW }, + + { NULL, MITYPE_IGNORE, 0, 0} +}; + +//========================================================================== +// +// ParseMapDefinition +// Parses the body of a map definition, including defaultmap etc. +// +//========================================================================== + +void FMapInfoParser::ParseMapDefinition(MapRecord &info) +{ + int index; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if ((index = sc.MatchString(&MapFlagHandlers->name, sizeof(*MapFlagHandlers))) >= 0) + { + MapInfoFlagHandler *handler = &MapFlagHandlers[index]; + switch (handler->type) + { + case MITYPE_EATNEXT: + ParseAssign(); + sc.MustGetString(); + break; + + case MITYPE_IGNORE: + break; + + case MITYPE_SETFLAG: + if (!CheckAssign()) + { + info.flags |= handler->data1; + } + else + { + sc.MustGetNumber(); + if (sc.Number) info.flags |= handler->data1; + else info.flags &= ~handler->data1; + } + info.flags |= handler->data2; + break; + + case MITYPE_CLRFLAG: + info.flags &= ~handler->data1; + info.flags |= handler->data2; + break; + + case MITYPE_SCFLAGS: + info.flags = (info.flags & handler->data2) | handler->data1; + break; + + case MITYPE_SETFLAGG: + if (!CheckAssign()) + { + info.gameflags |= handler->data1; + } + else + { + sc.MustGetNumber(); + if (sc.Number) info.gameflags |= handler->data1; + else info.gameflags &= ~handler->data1; + } + info.gameflags |= handler->data2; + break; + + case MITYPE_CLRFLAGG: + info.gameflags &= ~handler->data1; + info.gameflags |= handler->data2; + break; + case MITYPE_SCFLAGSG: + info.gameflags = (info.gameflags & handler->data2) | handler->data1; + break; + + default: + // should never happen + assert(false); + break; + } + } + else + { + bool success = false; + + AutoSegs::MapInfoOptions.ForEach([this, &success, &info](FMapOptInfo* option) + { + if (sc.Compare(option->name)) + { + option->handler(*this, &info); + success = true; + return false; // break + } + + return true; // continue + }); + + if (!success) + { + if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in map definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + } + } + CheckEndOfFile("map"); +} + + +//========================================================================== +// +// GetDefaultLevelNum +// Gets a default level num from a map name. +// +//========================================================================== + +static int GetDefaultLevelNum(const char *mapname) +{ + if ((!strnicmp (mapname, "MAP", 3) || !strnicmp(mapname, "LEV", 3)) && strlen(mapname) <= 5) + { + int mapnum = atoi (mapname + 3); + + if (mapnum >= 1 && mapnum <= 99) + return mapnum; + } + else if (mapname[0] == 'E' && + mapname[1] >= '0' && mapname[1] <= '9' && + (mapname[2] == 'M' || mapname[2] == 'L') && + mapname[3] >= '0' && mapname[3] <= '9') + { + int epinum = mapname[1] - '1'; + int mapnum = mapname[3] - '0'; + return makelevelnum(epinum, mapnum); + } + return 0; +} + +//========================================================================== +// +// ParseMapHeader +// Parses the header of a map definition ('map mapxx mapname') +// +//========================================================================== +static MapRecord sink; + +MapRecord *FMapInfoParser::ParseMapHeader(MapRecord &defaultinfo) +{ + FString mapname; + MapRecord* map; + + if (!CheckLegacyMapDefinition(mapname)) + { + ParseLookupName(mapname); + } + + if (mapname.IsEmpty()) + { + map = &sink; // parse over the entire definition but discard the result. + } + else + { + map = FindMapByName(mapname); + if (!map) + { + map = AllocateMap(); + *map = defaultinfo; + DefaultExtension(mapname, ".map"); + map->SetFileName(mapname); + } + } + + if (!sc.CheckString("{")) + { + sc.MustGetString(); + map->name = sc.String; + } + else + { + if (map != &sink && map->name.IsEmpty()) sc.ScriptError("Missing level name"); + sc.UnGet(); + } + map->levelNumber = GetDefaultLevelNum(map->labelName); + return map; +} + + +//========================================================================== +// +// Episode definitions start with the header "episode " +// and then can be followed by any of the following: +// +// name "Episode name as text" +// picname "Picture to display the episode name" +// key "Shortcut key for the menu" +// noskillmenu +// remove +// +//========================================================================== + +void FMapInfoParser::ParseEpisodeInfo () +{ + unsigned int i; + FString map; + FString pic; + FString name; + bool remove = false; + char key = 0; + int flags = 0; + + // Get map name + sc.MustGetString (); + map = sc.String; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare ("optional")) + { + flags |= VF_OPTIONAL; + } + else if (sc.Compare("sharewarelock")) + { + flags |= VF_SHAREWARELOCK; + } + else if (sc.Compare ("name")) + { + ParseAssign(); + sc.MustGetString (); + name = sc.String; + } + else if (sc.Compare ("remove")) + { + remove = true; + } + else if (sc.Compare ("key")) + { + ParseAssign(); + sc.MustGetString (); + key = sc.String[0]; + } + else if (sc.Compare("noskillmenu")) + { + flags |= VF_NOSKILL; + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in episode definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("episode"); + + + for (i = 0; i < volumes.Size(); i++) + { + if (volumes[i].startmap.CompareNoCase(map) == 0) + { + break; + } + } + + if (remove) + { + // If the remove property is given for an episode, remove it. + volumes.Delete(i); + } + else + { + // Only allocate a new entry if this doesn't replace an existing episode. + if (i >= volumes.Size()) + { + i = volumes.Reserve(1); + } + + auto epi = &volumes[i]; + + epi->startmap = map; + epi->name = name; + epi->shortcut = tolower(key); + epi->flags = flags; + epi->index = i; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseCutsceneInfo() +{ + FString map; + FString pic; + FString name; + bool remove = false; + char key = 0; + int flags = 0; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare("intro")) + { + ParseCutscene(globalCutscenes.Intro); + } + else if (sc.Compare("defaultmapintro")) + { + ParseCutscene(globalCutscenes.DefaultMapIntro); + } + else if (sc.Compare("defaultmapoutro")) + { + ParseCutscene(globalCutscenes.DefaultMapOutro); + } + else if (sc.Compare("defaultgameover")) + { + ParseCutscene(globalCutscenes.DefaultGameover); + } + else if (sc.Compare("sharewareend")) + { + ParseCutscene(globalCutscenes.SharewareEnd); + } + else if (sc.Compare("loadscreen")) + { + ParseCutscene(globalCutscenes.LoadingScreen); + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in cutscene definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("cutscenes"); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FMapInfoParser::ParseGameInfo() +{ + FString map; + FString pic; + FString name; + bool remove = false; + char key = 0; + int flags = 0; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare("summaryscreen")) + { + ParseAssign(); + sc.SetCMode(false); + sc.MustGetString(); + sc.SetCMode(false); + globalCutscenes.SummaryScreen = sc.String; + } + else if (sc.Compare("mpsummaryscreen")) + { + ParseAssign(); + sc.SetCMode(false); + sc.MustGetString(); + sc.SetCMode(false); + globalCutscenes.MPSummaryScreen = sc.String; + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in gameinfo definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + CheckEndOfFile("cutscenes"); +} + + +//========================================================================== +// +// SetLevelNum +// Avoid duplicate levelnums. The level being set always has precedence. +// +//========================================================================== + +void SetLevelNum (MapRecord *info, int num) +{ + for (auto& map : mapList) + { + + if (map->levelNumber == num) + map->levelNumber = 0; + } + info->levelNumber = num; +} + +//========================================================================== +// +// G_DoParseMapInfo +// Parses a single MAPINFO lump +// data for wadlevelinfos and wadclusterinfos. +// +//========================================================================== + +void FMapInfoParser::ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord &defaultinfo) +{ + sc.OpenLumpNum(lump); + Internal = (fileSystem.GetFileContainer(lump) == 0); + + defaultinfo = gamedefaults; + defaultinfoptr = &defaultinfo; + + if (ParsedLumps.Find(lump) != ParsedLumps.Size()) + { + sc.ScriptMessage("MAPINFO file is processed more than once\n"); + } + else + { + ParsedLumps.Push(lump); + } + + while (sc.GetString ()) + { + if (sc.Compare("include")) + { + sc.MustGetString(); + int inclump = fileSystem.CheckNumForFullName(sc.String, true); + if (inclump < 0) + { + sc.ScriptError("include file '%s' not found", sc.String); + } + if (fileSystem.GetFileContainer(sc.LumpNum) != fileSystem.GetFileContainer(inclump)) + { + // Do not allow overriding includes from the default MAPINFO + if (fileSystem.GetFileContainer(sc.LumpNum) == 0) + { + I_FatalError("File %s is overriding core lump %s.", + fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(inclump)), sc.String); + } + } + FScanner saved_sc = sc; + ParseMapInfo(inclump, gamedefaults, defaultinfo); + sc = saved_sc; + } + else if (sc.Compare("gamedefaults")) + { + gamedefaults = {}; + ParseMapDefinition(gamedefaults); + defaultinfo = gamedefaults; + } + else if (sc.Compare("defaultmap")) + { + defaultinfo = gamedefaults; + ParseMapDefinition(defaultinfo); + } + else if (sc.Compare("adddefaultmap")) + { + // Same as above but adds to the existing definitions instead of replacing them completely + ParseMapDefinition(defaultinfo); + } + else if (sc.Compare("map")) + { + auto levelinfo = ParseMapHeader(defaultinfo); + + ParseMapDefinition(*levelinfo); + SetLevelNum (levelinfo, levelinfo->levelNumber); // Wipe out matching levelnums from other maps. + } + // clusterdef is the old keyword but the new format has enough + // structuring that 'cluster' can be handled, too. The old format does not. + else if (sc.Compare("cluster")) + { + ParseCluster(); + } + else if (sc.Compare("episode")) + { + ParseEpisodeInfo(); + } + else if (sc.Compare("clearepisodes")) + { + volumes.Clear(); + } + else if (sc.Compare("clearall")) + { + // Wipe out all legacy content to start a fresh definition. + volumes.Clear(); + mapList.Clear(); + clusters.Clear(); + } + else if (sc.Compare("cutscenes")) + { + ParseCutsceneInfo(); + } + else if (sc.Compare("gameinfo")) + { + ParseGameInfo(); + } + else if (sc.Compare("clearall")) + { + // clears all map and progression related data, so that a mod can start with a clean slate. + mapList.Clear(); + volumes.Clear(); + clusters.Clear(); + globalCutscenes.DefaultMapIntro = {}; + globalCutscenes.DefaultMapOutro = {}; + globalCutscenes.DefaultGameover = {}; + } + else + { + sc.ScriptError("%s: Unknown top level keyword", sc.String); + } + } +} + +//========================================================================== +// +// G_ParseMapInfo +// Parses the MAPINFO lumps of all loaded WADs and generates +// data for wadlevelinfos and wadclusterinfos. +// +//========================================================================== + +void G_ParseMapInfo () +{ + int lump, lastlump = 0; + MapRecord gamedefaults; + + // first parse the internal one which sets up the needed basics and patches the legacy definitions of each game. + FMapInfoParser parse; + MapRecord defaultinfo; + + // Parse internal RMAPINFOs. + while ((lump = fileSystem.FindLumpFullName("engine/rmapinfo.txt", &lastlump, false)) != -1) + { + if (fileSystem.GetFileContainer(lump) > 0) break; // only load from raze.pk3 + + FMapInfoParser parse; + MapRecord defaultinfo; + parse.ParseMapInfo(lump, gamedefaults, defaultinfo); + } + + // Parse any extra RMAPINFOs. + while ((lump = fileSystem.FindLump ("RMAPINFO", &lastlump, false)) != -1) + { + FMapInfoParser parse; + MapRecord defaultinfo; + parse.ParseMapInfo(lump, gamedefaults, defaultinfo); + } + + if (volumes.Size() == 0) + { + I_FatalError ("No volumes defined."); + } +} diff --git a/source/core/g_mapinfo.h b/source/core/g_mapinfo.h new file mode 100644 index 000000000..6131b1e97 --- /dev/null +++ b/source/core/g_mapinfo.h @@ -0,0 +1,108 @@ +/* +** g_level.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __G_LEVEL_H__ +#define __G_LEVEL_H__ + +#include "autosegs.h" +#include "vectors.h" +#include "sc_man.h" +#include "file_zip.h" + +struct FMapInfoParser +{ + FScanner sc; + bool Internal; + MapRecord* defaultinfoptr; + + FMapInfoParser(bool internal = false) + { + Internal = internal; + } + + bool CheckLegacyMapDefinition(FString& mapname); + bool ParseLookupName(FString &dest); + void ParseMusic(FString &name, int &order); + void ParseLumpOrTextureName(FString &name); + + void ParseCutscene(CutsceneDef& cdef); + void ParseCluster(); + void ParseMapName(FString &mapname); + MapRecord *ParseMapHeader(MapRecord &defaultinfo); + void ParseMapDefinition(MapRecord &leveldef); + void ParseEpisodeInfo (); + void ParseCutsceneInfo(); + void ParseGameInfo(); + void ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord &defaultinfo); + + void ParseOpenBrace(); + bool ParseCloseBrace(); + bool CheckAssign(); + void ParseAssign(); + void MustParseAssign(); + void ParseComma(); + bool CheckNumber(); + bool CheckFloat(); + void SkipToNext(); + void CheckEndOfFile(const char *block); +}; + +#if defined(_MSC_VER) +#pragma section(SECTION_YREG,read) +#define MSVC_YSEG __declspec(allocate(SECTION_YREG)) +#define GCC_YSEG +#else +#define MSVC_YSEG +#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used)) +#endif + +#define DEFINE_MAP_OPTION(name, old) \ + static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info); \ + static FMapOptInfo MapOpt_##name = \ + { #name, MapOptHandler_##name, old }; \ + MSVC_YSEG FMapOptInfo *mapopt_##name GCC_YSEG = &MapOpt_##name; \ + static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info) + + +struct FMapOptInfo +{ + const char *name; + void (*handler) (FMapInfoParser &parse, MapRecord *levelinfo); + bool old; +}; + + +void G_ParseMapInfo(); + + +#endif //__G_LEVEL_H__ diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 6e91ae189..880cecb7f 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -73,6 +73,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "automap.h" #include "v_draw.h" #include "gi.h" +#include "vm.h" +#include "g_mapinfo.h" #include "gamefuncs.h" #include "hw_voxels.h" #include "hw_palmanager.h" @@ -286,6 +288,11 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr) UserConfig userConfig; +DEFINE_GLOBAL(userConfig) +DEFINE_FIELD_X(UserConfigStruct, UserConfig, nomonsters) +DEFINE_FIELD_X(UserConfigStruct, UserConfig, nosound) +DEFINE_FIELD_X(UserConfigStruct, UserConfig, nologo) + void UserConfig::ProcessOptions() { // -help etc are omitted @@ -560,7 +567,7 @@ int GameMain() I_ShowFatalError(err.what()); r = -1; } - DeleteScreenJob(); + //DeleteScreenJob(); DeinitMenus(); if (StatusBar) StatusBar->Destroy(); StatusBar = nullptr; @@ -601,13 +608,17 @@ int GameMain() void SetDefaultStrings() { + // Duke 1.3 does not define its episodes through CON. if ((g_gameType & GAMEFLAG_DUKE) && fileSystem.FindFile("E4L1.MAP") < 0) { + auto vol0 = AllocateVolume(); vol0->index = 0; + auto vol1 = AllocateVolume(); vol1->index = 1; vol1->flags = VF_SHAREWARELOCK; + auto vol2 = AllocateVolume(); vol2->index = 2; vol1->flags = VF_SHAREWARELOCK; // Pre-Atomic releases do not define this. - gVolumeNames[0] = "$L.A. Meltdown"; - gVolumeNames[1] = "$Lunar Apocalypse"; - gVolumeNames[2] = "$Shrapnel City"; - if (g_gameType & GAMEFLAG_SHAREWARE) gVolumeNames[3] = "$The Birth"; + vol0->name = "$L.A. Meltdown"; + vol1->name = "$Lunar Apocalypse"; + vol2->name = "$Shrapnel City"; + gSkillNames[0] = "$Piece of Cake"; gSkillNames[1] = "$Let's Rock"; gSkillNames[2] = "$Come get Some"; @@ -952,6 +963,7 @@ int RunGame() LoadScripts(); StartScreen->Progress(); SetDefaultStrings(); + Job_Init(); if (Args->CheckParm("-sounddebug")) C_DoCommand("stat sounddebug"); @@ -967,6 +979,7 @@ int RunGame() engineInit(); gi->app_init(); StartScreen->Progress(); + G_ParseMapInfo(); CreateStatusBar(); SetDefaultMenuColors(); M_Init(); @@ -1440,13 +1453,69 @@ DEFINE_ACTION_FUNCTION(_Screen, GetViewWindow) return MIN(numret, 4); } -DEFINE_ACTION_FUNCTION_NATIVE(_Build, ShadeToLight, shadeToLight) +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, ShadeToLight, shadeToLight) { PARAM_PROLOGUE; PARAM_INT(shade); ACTION_RETURN_INT(shadeToLight(shade)); } +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopAllSounds, FX_StopAllSounds) +{ + FX_StopAllSounds(); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopMusic, Mus_Stop) +{ + Mus_Stop(); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, SoundEnabled, SoundEnabled) +{ + ACTION_RETURN_INT(SoundEnabled()); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, MusicEnabled, MusicEnabled) +{ + ACTION_RETURN_INT(MusicEnabled()); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, GetTimeFrac, I_GetTimeFrac) +{ + ACTION_RETURN_INT(I_GetTimeFrac()); +} + +DEFINE_ACTION_FUNCTION(_Raze, PlayerName) +{ + PARAM_PROLOGUE; + PARAM_INT(index); + ACTION_RETURN_STRING(unsigned(index) >= MAXPLAYERS ? "" : PlayerName(index)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bsin, bsin) +{ + PARAM_PROLOGUE; + PARAM_INT(v); + PARAM_INT(shift); + ACTION_RETURN_INT(bsin(v, shift)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bcos, bcos) +{ + PARAM_PROLOGUE; + PARAM_INT(v); + PARAM_INT(shift); + ACTION_RETURN_INT(bcos(v, shift)); +} + +DEFINE_ACTION_FUNCTION(_MapRecord, GetCluster) +{ + PARAM_SELF_STRUCT_PROLOGUE(MapRecord); + ACTION_RETURN_POINTER(FindCluster(self->cluster)); +} + extern bool demoplayback; DEFINE_GLOBAL(multiplayer) DEFINE_GLOBAL(netgame) @@ -1454,6 +1523,37 @@ DEFINE_GLOBAL(gameaction) DEFINE_GLOBAL(gamestate) DEFINE_GLOBAL(demoplayback) DEFINE_GLOBAL(consoleplayer) +DEFINE_GLOBAL(currentLevel) +DEFINE_GLOBAL(paused) + +DEFINE_FIELD_X(ClusterDef, ClusterDef, name) +DEFINE_FIELD_X(ClusterDef, ClusterDef, InterBackground) + +DEFINE_FIELD_X(MapRecord, MapRecord, parTime) +DEFINE_FIELD_X(MapRecord, MapRecord, designerTime) +DEFINE_FIELD_X(MapRecord, MapRecord, fileName) +DEFINE_FIELD_X(MapRecord, MapRecord, labelName) +DEFINE_FIELD_X(MapRecord, MapRecord, name) +DEFINE_FIELD_X(MapRecord, MapRecord, music) +DEFINE_FIELD_X(MapRecord, MapRecord, cdSongId) +DEFINE_FIELD_X(MapRecord, MapRecord, flags) +DEFINE_FIELD_X(MapRecord, MapRecord, levelNumber) +DEFINE_FIELD_X(MapRecord, MapRecord, cluster) +DEFINE_FIELD_X(MapRecord, MapRecord, NextMap) +DEFINE_FIELD_X(MapRecord, MapRecord, NextSecret) +//native readonly String messages[MAX_MESSAGES]; +DEFINE_FIELD_X(MapRecord, MapRecord, Author) +DEFINE_FIELD_X(MapRecord, MapRecord, InterBackground) + +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, kills) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxkills) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, secrets) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxsecrets) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, supersecrets) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, playercount) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, time) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, cheated) +DEFINE_FIELD_X(SummaryInfo, SummaryInfo, endofgame) void InitBuildTiles() diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index b22410ad9..cbf015712 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -70,12 +70,12 @@ extern UserConfig userConfig; extern int nomusic; extern bool nosound; -inline bool MusicEnabled() +inline int MusicEnabled() // int return is for scripting { return mus_enabled && !nomusic; } -inline bool SoundEnabled() +inline int SoundEnabled() { return snd_enabled && !nosound; } @@ -99,8 +99,15 @@ enum GAMEFLAG_POWERSLAVE = 0x00002000, GAMEFLAG_EXHUMED = 0x00004000, GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher. - GAMEFLAG_WORLDTOUR = 0x00008000, - GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_WORLDTOUR = 0x00008000, + GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_DUKENW = 0x00020000, + GAMEFLAG_DUKEVACA = 0x00040000, + GAMEFLAG_BLOODCP = 0x00080000, + GAMEFLAG_ROUTE66 = 0x00100000, + GAMEFLAG_SWWANTON = 0x00200000, + GAMEFLAG_SWTWINDRAG = 0x00400000, + GAMEFLAG_DUKECOMPAT = GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI | GAMEFLAG_RRALL, GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo diff --git a/source/core/gamestate.h b/source/core/gamestate.h index 523ead5d0..431eac31c 100644 --- a/source/core/gamestate.h +++ b/source/core/gamestate.h @@ -43,8 +43,10 @@ enum gameaction_t : int ga_nextlevel, // Actually start the next level. ga_loadgamehidecon, ga_newgamenostopsound, // start a new game + ga_endscreenjob, ga_fullconsole, }; extern gamestate_t gamestate; extern gameaction_t gameaction; +extern int intermissiondelay; diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h index bba79b47b..e98e975c4 100644 --- a/source/core/gamestruct.h +++ b/source/core/gamestruct.h @@ -79,7 +79,7 @@ struct GameInterface virtual void MenuSound(EMenuSounds snd) {} virtual bool CanSave() { return true; } virtual void CustomMenuSelection(int menu, int item) {} - virtual bool StartGame(FNewGameStartup& gs) { return false; } + virtual bool StartGame(FNewGameStartup& gs) { return true; } virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; } virtual double SmallFontScale() { return 1; } virtual void SerializeGameState(FSerializer& arc) {} diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 7a65c950d..156beead2 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -105,6 +105,7 @@ bool r_NoInterpolate; int entertic; int oldentertics; int gametic; +int intermissiondelay; FString BackupSaveGame; @@ -133,6 +134,20 @@ void G_BuildTiccmd(ticcmd_t* cmd) //========================================================================== bool newGameStarted; +void NewGame(MapRecord* map, int skill, bool ns = false) +{ + newGameStarted = true; + ShowIntermission(nullptr, map, nullptr, [=](bool) { + gi->NewGame(map, skill, ns); + }); +} + +//========================================================================== +// +// +// +//========================================================================== + static void GameTicker() { int i; @@ -159,7 +174,7 @@ static void GameTicker() FX_SetReverb(0); gi->FreeLevelData(); gameaction = ga_level; - gi->NewGame(g_nextmap, -1); + NewGame(g_nextmap, -1); BackupSaveGame = ""; } break; @@ -191,13 +206,12 @@ static void GameTicker() FX_StopAllSounds(); case ga_newgamenostopsound: DeleteScreenJob(); - newGameStarted = true; FX_SetReverb(0); gi->FreeLevelData(); C_FlushDisplay(); gameaction = ga_level; BackupSaveGame = ""; - gi->NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound); + NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound); break; case ga_startup: @@ -209,6 +223,7 @@ static void GameTicker() case ga_mainmenu: FX_StopAllSounds(); + if (isBlood()) Mus_Stop(); case ga_mainmenunostopsound: gi->FreeLevelData(); gamestate = GS_MENUSCREEN; @@ -253,6 +268,10 @@ static void GameTicker() gameaction = ga_nothing; break; + case ga_endscreenjob: + EndScreenJob(); + break; + // for later // case ga_recordgame, // start a new demo recording (later) // case ga_loadgameplaydemo, // load a savegame and play a demo. @@ -331,7 +350,16 @@ static void GameTicker() break; case GS_INTERMISSION: case GS_INTRO: - ScreenJobTick(); + if (intermissiondelay > 0) + { + intermissiondelay--; + break; + } + if (ScreenJobTick()) + { + // synchronize termination with the playsim. + Net_WriteByte(DEM_ENDSCREENJOB); + } break; } @@ -371,7 +399,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. - ScreenJobDraw(); + if (intermissiondelay <= 0) ScreenJobDraw(); break; case GS_LEVEL: @@ -633,6 +661,16 @@ void MainLoop () // Clamp the timer to TICRATE until the playloop has been entered. r_NoInterpolate = true; + if (userConfig.CommandMap.IsNotEmpty()) + { + auto maprecord = FindMapByName(userConfig.CommandMap); + userConfig.CommandMap = ""; + if (maprecord) + { + NewGame(maprecord, /*userConfig.skill*/2); // todo: fix the skill. + } + } + for (;;) { try diff --git a/source/core/mapinfo.cpp b/source/core/mapinfo.cpp index 5cdd2ee1d..4b52d6677 100644 --- a/source/core/mapinfo.cpp +++ b/source/core/mapinfo.cpp @@ -41,43 +41,102 @@ #include "raze_sound.h" FString gSkillNames[MAXSKILLS]; -FString gVolumeNames[MAXVOLUMES]; -FString gVolumeSubtitles[MAXVOLUMES]; -int32_t gVolumeFlags[MAXVOLUMES]; int gDefaultVolume = 0, gDefaultSkill = 1; -MapRecord mapList[512]; -MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.) +GlobalCutscenes globalCutscenes; +TArray clusters; +TArray volumes; +TArray> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array. +MapRecord *currentLevel; // level that is currently played. MapRecord* lastLevel; // Same here, for the last level. -unsigned int numUsedSlots; CCMD(listmaps) { - for (unsigned int i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - int lump = fileSystem.FindFile(mapList[i].fileName); + int lump = fileSystem.FindFile(map->fileName); if (lump >= 0) { int rfnum = fileSystem.GetFileContainer(lump); - Printf("%s - %s (%s)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName(), fileSystem.GetResourceFileName(rfnum)); + Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum)); } else { - Printf("%s - %s (defined but does not exist)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName()); + Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName()); } } } +CCMD(mapinfo) +{ + const char* mapname = nullptr; + if (argv.argc() > 1) mapname = argv[1]; + + if (!mapname) + { + for (auto& vol : volumes) + { + Printf("Volume %d\n\tName = '%s'\n\tstartmap = '%s'\n}\n", vol.index, vol.name.GetChars(), vol.startmap.GetChars()); + } + for (auto& clus : clusters) + { + if (clus.intro.isdefined() || clus.outro.isdefined()) + { + Printf("Cluster %d\n\tName = '%s'\n", clus.index, clus.name.GetChars()); + if (clus.intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", clus.intro.function.GetChars()); + if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", clus.intro.video.GetChars()); + if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", clus.outro.function.GetChars()); + if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", clus.outro.video.GetChars()); + Printf("}\n"); + } + } + } + for (auto& map : mapList) + { + if (mapname && map->labelName.CompareNoCase(mapname)) continue; + int lump = fileSystem.FindFile(map->fileName); + if (lump >= 0) + { + int rfnum = fileSystem.GetFileContainer(lump); + Printf("%s - %s (%s)\n{\n", map->fileName.GetChars(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum)); + Printf("\tlevel number = %d\n\tCluster = %d\n\tIndex = %d\n", map->levelNumber, map->cluster, map->mapindex); + if (map->Author.IsNotEmpty()) Printf("\tAuthor = '%s'\n", map->Author.GetChars()); + if (map->NextMap.IsNotEmpty()) Printf("\tNext map = '%s'\n", map->NextMap.GetChars()); + if (map->NextSecret.IsNotEmpty()) Printf("\tNext secret map = '%s'\n", map->NextSecret.GetChars()); + if (map->music.IsNotEmpty()) Printf("\tMusic = '%s:%d'", map->music.GetChars(), map->musicorder); + if (map->cdSongId > 0) Printf("\tCD track = %d\n", map->cdSongId); + if (map->parTime) Printf("\tPar Time = %d\n", map->parTime); + if (map->designerTime) Printf("\tPar Time = %d\n", map->designerTime); + if (map->intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", map->intro.function.GetChars()); + if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", map->intro.video.GetChars()); + if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", map->outro.function.GetChars()); + if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", map->outro.video.GetChars()); + Printf("}\n"); + } + else + { + Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName()); + } + } +} + +int CutsceneDef::GetSound() +{ + int id; + if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName); + if (id <= 0) id = soundEngine->FindSoundByResID(soundID); + return id; +} + MapRecord *FindMapByName(const char *nm) { - for (unsigned i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - auto &map = mapList[i]; - if (map.labelName.CompareNoCase(nm) == 0) + if (map->labelName.CompareNoCase(nm) == 0) { - return ↦ + return map.Data(); } } return nullptr; @@ -86,23 +145,78 @@ MapRecord *FindMapByName(const char *nm) MapRecord *FindMapByLevelNum(int num) { - for (unsigned i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - auto &map = mapList[i]; - if (map.levelNumber == num) + if (map->levelNumber == num) { - return ↦ + return map.Data(); } } return nullptr; } -MapRecord *FindNextMap(MapRecord *thismap) +VolumeRecord* FindVolume(int index) { - if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel); - return FindMapByLevelNum(thismap->levelNumber+1); + for (auto& vol : volumes) + { + if (vol.index == index) return &vol; + } + return nullptr; } +ClusterDef* FindCluster(int index) +{ + for (auto& vol : clusters) + { + if (vol.index == index) return &vol; + } + return nullptr; +} + +ClusterDef* AllocateCluster() +{ + return &clusters[clusters.Reserve(1)]; +} + +VolumeRecord* AllocateVolume() +{ + return &volumes[volumes.Reserve(1)]; +} + +MapRecord* FindMapByIndexOnly(int cluster, int num) +{ + for (auto& map : mapList) + { + if (map->mapindex == num && map->cluster == cluster) return map.Data(); + } + return nullptr; +} + +MapRecord* FindMapByIndex(int cluster, int num) +{ + auto map = FindMapByLevelNum(num); + if (!map) map = FindMapByIndexOnly(cluster, num); // modern definitions take precedence. + return map; +} + +MapRecord* FindNextMap(MapRecord* thismap) +{ + MapRecord* next = nullptr; + if (!thismap->NextMap.Compare("-")) return nullptr; // '-' means to forcibly end the game here. + if (thismap->NextMap.IsNotEmpty()) next = FindMapByName(thismap->NextMap); + if (!next) next = FindMapByLevelNum(thismap->levelNumber + 1); + return next; +} + +MapRecord* FindNextSecretMap(MapRecord* thismap) +{ + MapRecord* next = nullptr; + if (!thismap->NextSecret.Compare("-")) return nullptr; // '-' means to forcibly end the game here. + if (thismap->NextSecret.IsNotEmpty()) next = FindMapByName(thismap->NextSecret); + return next? next : FindNextMap(thismap); +} + + bool SetMusicForMap(const char* mapname, const char* music, bool namehack) { static const char* specials[] = { "intro", "briefing", "loading" }; @@ -129,7 +243,7 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack) if (numMatches != 4 || toupper(b1) != 'E' || toupper(b2) != 'L') return false; - index = FindMapByLevelNum(levelnum(ep - 1, lev - 1)); + index = FindMapByIndexOnly(ep, lev); } if (index != nullptr) @@ -142,18 +256,19 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack) MapRecord *AllocateMap() { - return &mapList[numUsedSlots++]; + auto&p = mapList[mapList.Reserve(1)]; + p.Alloc(); + return p.Data(); } MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic) { - for (unsigned i = 0; i < numUsedSlots; i++) + for (auto& map : mapList) { - auto &map = mapList[i]; - if (map.fileName.CompareNoCase(boardfilename) == 0) + if (map->fileName.CompareNoCase(boardfilename) == 0) { - return ↦ + return map.Data(); } } diff --git a/source/core/mapinfo.h b/source/core/mapinfo.h index 9a1e0050d..4b4ed1ad4 100644 --- a/source/core/mapinfo.h +++ b/source/core/mapinfo.h @@ -3,28 +3,58 @@ #include "gstrings.h" #include "cmdlib.h" #include "quotemgr.h" +#include "palentry.h" +#include "vectors.h" #ifdef GetMessage #undef GetMessage // Windows strikes... #endif - enum EMax { MAXSKILLS = 7, - MAXVOLUMES = 7, MAXMENUGAMEPLAYENTRIES = 7, }; enum EVolFlags { - EF_HIDEFROMSP = 1, + VF_HIDEFROMSP = 1, + VF_OPTIONAL = 2, + VF_SHAREWARELOCK = 4, // show in shareware but lock access. + VF_NOSKILL = 8, +}; + +enum EMapFlags +{ + LEVEL_NOINTERMISSION = 1, + LEVEL_SECRETEXITOVERRIDE = 2, // when given an explicit level number, override with secret exit in the map, mainly for compiling episodes out of single levels. + LEVEL_CLEARINVENTORY = 4, + LEVEL_CLEARWEAPONS = 8, + LEVEL_FORCENOEOG = 16, // RR E1L7 needs this to override its boss's death ending the game. +}; + +enum EMapGameFlags +{ + LEVEL_RR_HULKSPAWN = 1, + LEVEL_RR_CLEARMOONSHINE = 2, + + LEVEL_EX_COUNTDOWN = 4, + LEVEL_EX_TRAINING = 8, + LEVEL_EX_ALTSOUND = 16, + LEVEL_EX_MULTI = 32, + + LEVEL_SW_SPAWNMINES = 64, + LEVEL_SW_BOSSMETER_SERPENT = 128, + LEVEL_SW_BOSSMETER_SUMO = 256, + LEVEL_SW_BOSSMETER_ZILLA = 512, + LEVEL_SW_DEATHEXIT_SERPENT = 1024, + LEVEL_SW_DEATHEXIT_SUMO = 2048, + LEVEL_SW_DEATHEXIT_ZILLA = 4096, + + }; // These get filled in by the map definition parsers of the front ends. extern FString gSkillNames[MAXSKILLS]; -extern FString gVolumeNames[MAXVOLUMES]; -extern FString gVolumeSubtitles[MAXVOLUMES]; -extern int32_t gVolumeFlags[MAXVOLUMES]; extern int gDefaultVolume, gDefaultSkill; @@ -46,6 +76,58 @@ enum { MAX_MESSAGES = 32 }; +class DObject; +struct MapRecord; + +struct CutsceneDef +{ + FString video; + FString function; + FString soundName; + int soundID = -1; // ResID not SoundID! + int framespersec = 0; // only relevant for ANM. + bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game. + + void Create(DObject* runner); + bool Create(DObject* runner, MapRecord* map, bool transition); + bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); } + int GetSound(); +}; + +struct GlobalCutscenes +{ + CutsceneDef Intro; + CutsceneDef DefaultMapIntro; + CutsceneDef DefaultMapOutro; + CutsceneDef DefaultGameover; + CutsceneDef SharewareEnd; + CutsceneDef LoadingScreen; + FString MPSummaryScreen; + FString SummaryScreen; +}; + +struct ClusterDef +{ + FString name; // What gets displayed for this cluster. In Duke this is normally the corresponding volume name but does not have to be. + CutsceneDef intro; // plays when entering this cluster + CutsceneDef outro; // plays when leaving this cluster + CutsceneDef gameover; // when defined, plays when the player dies in this cluster + FString InterBackground; + int index = -1; + int flags = 0; // engine and common flags + int gameflags = 0; // game specific flags. +}; + +struct VolumeRecord // episodes +{ + FString startmap; + FString name; + FString subtitle; + int index = -1; + int flags = 0; + int shortcut = 0; +}; + struct MapRecord { int parTime = 0; @@ -54,16 +136,39 @@ struct MapRecord FString labelName; FString name; FString music; + FString Author; + FString NextMap; + FString NextSecret; int cdSongId = -1; + int musicorder = -1; + + CutsceneDef intro; + CutsceneDef outro; int flags = 0; + int gameflags = 0; int levelNumber = -1; + int mapindex = -1; // index in the episode. This only for finding the next map in the progression when nothing explicit is defined. + int cluster = -1; + + PalEntry fadeto = 0; + int fogdensity = 0; + int skyfog = 0; + FString BorderTexture; + FString InterBackground; + TArray PrecacheTextures; + FVector4 skyrotatevector; // The rest is only used by Blood - int nextLevel = -1; - int nextSecret = -1; FString messages[MAX_MESSAGES]; - FString author; int8_t fog = -1, weather = -1; // Blood defines these but they aren't used. + + // game specific stuff + int rr_startsound = 0; + int rr_mamaspawn = 15; + int ex_ramses_horiz = 11; + int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue! + FString ex_ramses_pup; + FString ex_ramses_text; const char* LabelName() const { @@ -97,39 +202,65 @@ struct MapRecord { messages[num] = msg; } - - }; +struct SummaryInfo +{ + int kills; + int maxkills; + int secrets; + int maxsecrets; + int supersecrets; + int time; + int playercount; + bool cheated; + bool endofgame; +}; -extern MapRecord mapList[512]; +extern GlobalCutscenes globalCutscenes; extern MapRecord *currentLevel; bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false); MapRecord *FindMapByName(const char *nm); MapRecord *FindMapByLevelNum(int num); +MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable. +MapRecord* FindMapByIndex(int clst, int num); MapRecord *FindNextMap(MapRecord *thismap); +MapRecord* FindNextSecretMap(MapRecord* thismap); MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr); MapRecord* AllocateMap(); +VolumeRecord* FindVolume(int index); +ClusterDef* FindCluster(int index); +ClusterDef* AllocateCluster(); +VolumeRecord* AllocateVolume(); +void SetLevelNum(MapRecord* info, int num); + +inline VolumeRecord* MustFindVolume(int index) +{ + auto r = FindVolume(index); + if (r) return r; + r = AllocateVolume(); + r->index = index; + return r; +} +inline ClusterDef* MustFindCluster(int index) +{ + auto r = FindCluster(index); + if (r) return r; + r = AllocateCluster(); + r->index = index; + return r; +} + + // These should be the only places converting between level numbers and volume/map pairs -constexpr inline int levelnum(int vol, int map) +constexpr inline int makelevelnum(int vol, int map) { return vol * 1000 + map; } -constexpr inline int volfromlevelnum(int num) -{ - return num >= 0 ? num / 1000 : 0; -} - -constexpr inline int mapfromlevelnum(int num) -{ - return num >= 0 ? num % 1000 : -1; -} - - enum { RRENDSLOT = 127 diff --git a/source/core/menu/razemenu.cpp b/source/core/menu/razemenu.cpp index 6895f7d22..b55887f65 100644 --- a/source/core/menu/razemenu.cpp +++ b/source/core/menu/razemenu.cpp @@ -83,8 +83,31 @@ bool help_disabled; FNewGameStartup NewGameStartupInfo; + //FNewGameStartup NewGameStartupInfo; +static bool DoStartGame(FNewGameStartup& gs) +{ + auto vol = FindVolume(gs.Episode); + if (!vol) return false; + + if (isShareware() && (vol->flags & VF_SHAREWARELOCK)) + { + M_StartMessage(GStrings("SHAREWARELOCK"), 1, NAME_None); + return false; + } + + auto map = FindMapByName(vol->startmap); + if (!map) return false; + soundEngine->StopAllChannels(); + + gi->StartGame(gs); // play game specific effects (like Duke/RR/SW's voice lines when starting a game.) + + DeferedStartGame(map, gs.Skill); + return true; +} + + bool M_SetSpecialMenu(FName& menu, int param) { @@ -115,13 +138,19 @@ bool M_SetSpecialMenu(FName& menu, int param) case NAME_Startgame: case NAME_StartgameNoSkill: - menu = NAME_Startgame; NewGameStartupInfo.Skill = param; - if (menu == NAME_StartgameNoSkill) NewGameStartupInfo.Episode = param; - if (gi->StartGame(NewGameStartupInfo)) + if (menu == NAME_StartgameNoSkill) + { + menu = NAME_Startgame; + NewGameStartupInfo.Episode = param; + NewGameStartupInfo.Skill = 1; + } + if (DoStartGame(NewGameStartupInfo)) { M_ClearMenus(); - STAT_StartNewGame(gVolumeNames[NewGameStartupInfo.Episode], NewGameStartupInfo.Skill); + int ep = NewGameStartupInfo.Episode; + auto vol = FindVolume(ep); + if (vol) STAT_StartNewGame(vol->name, NewGameStartupInfo.Skill); inputState.ClearAllInput(); } return false; @@ -365,6 +394,7 @@ static DMenuItemBase* CreateCustomListMenuItemText(double x, double y, int heigh // Creates the episode menu // //============================================================================= +extern TArray volumes; static void BuildEpisodeMenu() { @@ -385,22 +415,22 @@ static void BuildEpisodeMenu() ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items int y = ld->mYpos; - for (int i = 0; i < MAXVOLUMES; i++) + // Volume definitions should be sorted by intended menu order. + for (auto &vol : volumes) { - if (gVolumeNames[i].IsNotEmpty() && !(gVolumeFlags[i] & EF_HIDEFROMSP)) - + if (vol.name.IsNotEmpty() && !(vol.flags & VF_HIDEFROMSP)) { - int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && i > 0); - auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gVolumeNames[i][0], - gVolumeNames[i], ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, i); // font colors are not used, so hijack one for the shareware flag. + int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && (vol.flags & VF_SHAREWARELOCK)); + auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, vol.name[0], + vol.name, ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, vol.index); // font colors are not used, so hijack one for the shareware flag. y += ld->mLinespacing; ld->mItems.Push(it); addedVolumes++; - if (gVolumeSubtitles[i].IsNotEmpty()) + if (vol.subtitle.IsNotEmpty()) { auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing * 6 / 10, 1, - gVolumeSubtitles[i], SmallFont, CR_GRAY, false, NAME_None, i); + vol.subtitle, SmallFont, CR_GRAY, false, NAME_None, vol.index); y += ld->mLinespacing * 6 / 10; ld->mItems.Push(it); textadded = true; diff --git a/source/core/movie/movieplayer.cpp b/source/core/movie/movieplayer.cpp index 80b6fca7b..f5dfbe0b0 100644 --- a/source/core/movie/movieplayer.cpp +++ b/source/core/movie/movieplayer.cpp @@ -49,15 +49,25 @@ #include #include #include "raze_music.h" +#include "vm.h" class MoviePlayer { +protected: + enum EMovieFlags + { + NOSOUNDCUTOFF = 1, + FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros. + }; + + int flags; public: virtual void Start() {} virtual bool Frame(uint64_t clock) = 0; virtual void Stop() {} virtual ~MoviePlayer() = default; + virtual FTextureID GetTexture() = 0; }; //--------------------------------------------------------------------------- @@ -76,16 +86,17 @@ class AnmPlayer : public MoviePlayer int frametime = 0; int nextframetime = 0; AnimTextures animtex; - const AnimSound* animSnd; - const int* frameTicks; - bool nostopsound; + const TArray animSnd; + int frameTicks[3]; public: bool isvalid() { return numframes > 0; } - AnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff) - : animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff) + AnmPlayer(FileReader& fr, TArray& ans, const int *frameticks, int flags_) + : animSnd(std::move(ans)) { + memcpy(frameTicks, frameticks, 3 * sizeof(int)); + flags = flags_; buffer = fr.ReadPadded(1); fr.Close(); @@ -109,17 +120,12 @@ public: 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) { @@ -129,11 +135,12 @@ public: } nextframetime += delay; - if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++) + bool nostopsound = (flags & NOSOUNDCUTOFF); + for (unsigned i = 0; i < animSnd.Size(); i+=2) { - if (animSnd[i].framenum == curframe) + if (animSnd[i] == curframe) { - int sound = animSnd[i].soundnum; + int sound = animSnd[i+1]; if (sound == -1) soundEngine->StopAllChannels(); else if (SoundEnabled()) @@ -147,6 +154,7 @@ public: void Stop() override { + bool nostopsound = (flags & NOSOUNDCUTOFF); if (!nostopsound) soundEngine->StopAllChannels(); } @@ -156,6 +164,11 @@ public: buffer.Reset(); animtex.Clean(); } + + FTextureID GetTexture() override + { + return animtex.GetFrameID(); + } }; //--------------------------------------------------------------------------- @@ -187,8 +200,6 @@ public: { 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; } @@ -196,6 +207,11 @@ public: { decoder.Close(); } + + FTextureID GetTexture() override + { + return decoder.animTex().GetFrameID(); + } }; //--------------------------------------------------------------------------- @@ -209,7 +225,7 @@ class VpxPlayer : public MoviePlayer bool failed = false; FileReader fr; AnimTextures animtex; - const AnimSound* animSnd; + const TArray animSnd; unsigned width, height; TArray Pic; @@ -234,10 +250,10 @@ public: public: bool isvalid() { return !failed; } - VpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay, FString& error) + VpxPlayer(FileReader& fr_, TArray& animSnd_, int flags_, int origframedelay, FString& error) : animSnd(std::move(animSnd_)) { fr = std::move(fr_); - animSnd = animSnd_; + flags = flags_; if (!ReadIVFHeader(origframedelay)) { @@ -433,30 +449,35 @@ public: framenum++; if (framenum >= numframes) stop = true; + bool nostopsound = (flags & NOSOUNDCUTOFF); 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 (soundtrack == -1) { - if (animSnd[i].framenum == soundframe) + for (unsigned i = 0; i < animSnd.Size(); i += 2) { - 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 (animSnd[i] == soundframe) + { + int sound = animSnd[i + 1]; + if (sound == -1) + soundEngine->StopAllChannels(); + else if (SoundEnabled()) + soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : 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(); + bool nostopsound = (flags & NOSOUNDCUTOFF); + if (!nostopsound) soundEngine->StopAllChannels(); } ~VpxPlayer() @@ -464,6 +485,11 @@ public: vpx_codec_destroy(&codec); animtex.Clean(); } + + FTextureID GetTexture() override + { + return animtex.GetFrameID(); + } }; //--------------------------------------------------------------------------- @@ -498,9 +524,10 @@ class SmkPlayer : public MoviePlayer bool fullscreenScale; uint64_t nFrameNs; int nFrame = 0; - const AnimSound* animSnd; + const TArray animSnd; FString filename; SoundStream* stream = nullptr; + bool hassound = false; public: bool isvalid() { return hSMK.isValid; } @@ -534,22 +561,21 @@ public: } - SmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport) + SmkPlayer(const char *fn, TArray& ans, int flags_) : animSnd(std::move(ans)) { hSMK = Smacker_Open(fn); if (!hSMK.isValid) { return; } + flags = flags_; 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) { @@ -562,14 +588,12 @@ public: 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; } } @@ -612,27 +636,21 @@ public: } } - 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++) + bool nostopsound = (flags & NOSOUNDCUTOFF); + if (!hassound) for (unsigned i = 0; i < animSnd.Size(); i += 2) { - if (animSnd[i].framenum == nFrame) + if (animSnd[i] == nFrame) { - int sound = animSnd[i].soundnum; + int sound = animSnd[i + 1]; if (sound == -1) soundEngine->StopAllChannels(); else if (SoundEnabled()) - soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE); + soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); } } } @@ -640,13 +658,24 @@ public: return nFrame < nFrames; } + void Stop() override + { + if (stream) S_StopCustomStream(stream); + bool nostopsound = (flags & NOSOUNDCUTOFF); + if (!nostopsound && !hassound) soundEngine->StopAllChannels(); + } + ~SmkPlayer() { Smacker_Close(hSMK); - if (stream) S_StopCustomStream(stream); - soundEngine->StopAllChannels(); animtex.Clean(); } + + FTextureID GetTexture() override + { + return animtex.GetFrameID(); + } + }; //--------------------------------------------------------------------------- @@ -655,61 +684,11 @@ public: // //--------------------------------------------------------------------------- -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) +MoviePlayer* OpenMovie(const char* filename, TArray& ans, const int* frameticks, int flags, FString& error) { FileReader fr; // first try as .ivf - but only if sounds are provided - the decoder is video only. - if (ans) + if (ans.Size()) { auto fn = StripExtension(filename); DefaultExtension(fn, ".ivf"); @@ -739,7 +718,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr if (!memcmp(id, "LPF ", 4)) { - auto anm = new AnmPlayer(fr, ans, frameticks, nosoundcutoff); + auto anm = new AnmPlayer(fr, ans, frameticks, flags); if (!anm->isvalid()) { error.Format("%s: invalid ANM file.\n", filename); @@ -751,7 +730,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr else if (!memcmp(id, "SMK2", 4)) { fr.Close(); - auto anm = new SmkPlayer(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently. + auto anm = new SmkPlayer(filename, ans, flags); if (!anm->isvalid()) { error.Format("%s: invalid SMK file.\n", filename); @@ -772,7 +751,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr } else if (!memcmp(id, "DKIF\0\0 \0VP80", 12)) { - auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, error); + auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, flags, error); if (!anm->isvalid()) { delete anm; @@ -795,20 +774,53 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr // //--------------------------------------------------------------------------- -DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff) +DEFINE_ACTION_FUNCTION(_MoviePlayer, Create) { - if (!filename) - { - return Create(1); - } + PARAM_PROLOGUE; + PARAM_STRING(filename); + PARAM_POINTER(sndinf, TArray); + PARAM_INT(flags); + PARAM_INT(frametime); + PARAM_INT(firstframetime); + PARAM_INT(lastframetime); + FString error; - auto movie = OpenMovie(filename, ans, frameticks, nosoundcutoff, error); + if (firstframetime == -1) firstframetime = frametime; + if (lastframetime == -1) lastframetime = frametime; + int frametimes[] = { firstframetime, frametime, lastframetime }; + auto movie = OpenMovie(filename, *sndinf, frametime == -1? nullptr : frametimes, flags, error); if (!movie) { Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars()); - return Create(1); } - return Create(movie); + ACTION_RETURN_POINTER(movie); } +DEFINE_ACTION_FUNCTION(_MoviePlayer, Start) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + self->Start(); + return 0; +} +DEFINE_ACTION_FUNCTION(_MoviePlayer, Frame) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + PARAM_FLOAT(clock); + ACTION_RETURN_INT(self->Frame(int64_t(clock))); + return 0; +} + +DEFINE_ACTION_FUNCTION(_MoviePlayer, Destroy) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + self->Stop(); + delete self; + return 0; +} + +DEFINE_ACTION_FUNCTION(_MoviePlayer, GetTexture) +{ + PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer); + ACTION_RETURN_INT(self->GetTexture().GetIndex()); +} diff --git a/source/core/precache.cpp b/source/core/precache.cpp index a0e1b33e3..f7aae123c 100644 --- a/source/core/precache.cpp +++ b/source/core/precache.cpp @@ -39,8 +39,10 @@ #include "hw_material.h" #include "gamestruct.h" #include "gamecontrol.h" +#include "texturemanager.h" #include "hw_models.h" #include "hw_voxels.h" +#include "mapinfo.h" BEGIN_BLD_NS extern short voxelIndex[MAXTILES]; @@ -129,6 +131,19 @@ void precacheMarkedTiles() int dapalnum = pair->Key >> 32; doprecache(dapicnum, dapalnum); } + + // Cache everything the map explicitly declares. + TMap cachetexmap; + for (auto& tex : currentLevel->PrecacheTextures) cachetexmap.Insert(tex, true); + + decltype(cachetexmap)::Iterator it2(cachetexmap); + decltype(cachetexmap)::Pair* pair2; + while (it2.NextPair(pair2)) + { + auto tex = TexMan.FindGameTexture(pair2->Key, ETextureType::Any); + if (tex) PrecacheTex(tex, 0); + } + cachemap.Clear(); } diff --git a/source/core/precache.h b/source/core/precache.h index bcf40813f..fab410227 100644 --- a/source/core/precache.h +++ b/source/core/precache.h @@ -2,5 +2,6 @@ void PrecacheHardwareTextures(int nTile); void markTileForPrecache(int tilenum, int palnum); +void markTextureForPrecache(const char* texname); void markVoxelForPrecache(int voxnum); void precacheMarkedTiles(); diff --git a/source/core/screenjob.cpp b/source/core/screenjob.cpp index f2417dc5b..993c97ce1 100644 --- a/source/core/screenjob.cpp +++ b/source/core/screenjob.cpp @@ -51,59 +51,371 @@ #include #include #include "raze_music.h" +#include "vm.h" +#include "mapinfo.h" +static DObject* runner; +static SummaryInfo sinfo; +static PClass* runnerclass; +static PType* runnerclasstype; +static PType* maprecordtype; +static PType* summaryinfotype; +static CompletionFunc completion; +static int ticks; +static SummaryInfo summaryinfo; -IMPLEMENT_CLASS(DScreenJob, true, false) -IMPLEMENT_CLASS(DImageScreen, true, false) - - -bool DSkippableScreenJob::OnEvent(event_t* evt) -{ - if (evt->type == EV_KeyDown && !specialKeyEvent(evt)) - { - 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(); -} - -//--------------------------------------------------------------------------- +//============================================================================= // // // -//--------------------------------------------------------------------------- +//============================================================================= -void DImageScreen::OnTick() +void Job_Init() { - if (cleared) + static bool done = false; + if (!done) { - int span = ticks * 1000 / GameTicRate; - if (span > waittime) state = finished; + done = true; + GC::AddMarkerFunc([] { GC::Mark(runner); }); + } + runnerclass = PClass::FindClass("ScreenJobRunner"); + if (!runnerclass) I_FatalError("ScreenJobRunner not defined"); + runnerclasstype = NewPointer(runnerclass); + + maprecordtype = NewPointer(NewStruct("MapRecord", nullptr, true)); + summaryinfotype = NewPointer(NewStruct("SummaryInfo", nullptr, true)); +} + +//============================================================================= +// +// +// +//============================================================================= + +static VMFunction* LookupFunction(const char* qname, bool validate = true) +{ + int p = strcspn(qname, "."); + if (p == 0) I_Error("Call to undefined function %s", qname); + FString clsname(qname, p); + FString funcname = qname + p + 1; + + auto func = PClass::FindFunction(clsname, funcname); + if (func == nullptr) I_Error("Call to undefined function %s", qname); + if (validate) + { + // these conditions must be met by all functions for this interface. + if (func->Proto->ReturnTypes.Size() != 0) I_Error("Bad cutscene function %s. Return value not allowed", qname); + if (func->ImplicitArgs != 0) I_Error("Bad cutscene function %s. Must be static", qname); + } + return func; +} + +//============================================================================= +// +// +// +//============================================================================= + +void CallCreateFunction(const char* qname, DObject* runner) +{ + auto func = LookupFunction(qname); + if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname); + if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname); + VMValue val = runner; + VMCall(func, &val, 1, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map) +{ + auto func = LookupFunction(qname); + if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here. + if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname); + if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype) + I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname); + VMValue val[2] = { runner, map }; + VMCall(func, val, 2, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* map, SummaryInfo* info, MapRecord* map2) +{ + auto func = LookupFunction(qname); + auto s = func->Proto->ArgumentTypes.Size(); + auto at = func->Proto->ArgumentTypes.Data(); + if (s != 3 && s != 4) I_Error("Bad map-cutscene function %s. Must receive precisely three or four arguments.", qname); + if (at[0] != runnerclasstype && at[1] != maprecordtype && at[2] != summaryinfotype && (s == 3 || at[3] == maprecordtype)) + I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference,", qname); + if (info) summaryinfo = *info; // must be copied to a persistent location. + else summaryinfo = {}; + VMValue val[] = { runner, map, &summaryinfo, map2 }; + VMCall(func, val, s, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +DObject* CreateRunner(bool clearbefore = true) +{ + auto obj = runnerclass->CreateNew(); + auto func = LookupFunction("ScreenJobRunner.Init", false); + VMValue val[3] = { obj, clearbefore, false }; + VMCall(func, val, 3, nullptr, 0); + return obj; +} + +//============================================================================= +// +// +// +//============================================================================= + +void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps) +{ + auto obj = runnerclass->CreateNew(); + auto func = LookupFunction("ScreenJobRunner.AddGenericVideo", false); + VMValue val[] = { runner, &fn, soundid, fps }; + VMCall(func, val, 4, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +void CutsceneDef::Create(DObject* runner) +{ + if (function.IsNotEmpty()) + { + CallCreateFunction(function, runner); + } + else if (video.IsNotEmpty()) + { + AddGenericVideo(runner, video, GetSound(), framespersec); } } +//============================================================================= +// +// +// +//============================================================================= -void DImageScreen::Draw(double smoothratio) +bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition) { - if (tilenum > 0) tex = tileGetTexture(tilenum, true); - twod->ClearScreen(); - if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE); - cleared = true; + if (!transition && transitiononly) return false; + if (function.CompareNoCase("none") == 0) + return true; // play nothing but return as being validated + if (function.IsNotEmpty()) + { + CallCreateMapFunction(function, runner, map); + return true; + } + else if (video.IsNotEmpty()) + { + AddGenericVideo(runner, video, GetSound(), framespersec); + return true; + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void DeleteScreenJob() +{ + if (runner) runner->Destroy(); + runner = nullptr; +} + +void EndScreenJob() +{ + DeleteScreenJob(); + if (completion) completion(false); + completion = nullptr; +} + + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobResponder(event_t* ev) +{ + 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; + } + } + FInputEvent evt = ev; + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent) + { + int result = 0; + VMValue parm[] = { runner, &evt }; + VMReturn ret(&result); + VMCall(func, parm, 2, &ret, 1); + return result; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobTick() +{ + ticks++; + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick) + { + int result = 0; + VMValue parm[] = { runner }; + VMReturn ret(&result); + VMCall(func, parm, 1, &ret, 1); + return result; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void ScreenJobDraw() +{ + double smoothratio = I_GetTimeFrac(); + + if (runner) + { + twod->ClearScreen(); + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame) + { + VMValue parm[] = { runner, smoothratio }; + VMCall(func, parm, 2, nullptr, 0); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobValidate() +{ + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate) + { + int res; + VMValue parm[] = { runner }; + VMReturn ret(&res); + VMCall(func, parm, 2, &ret, 1); + return res; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_) +{ + if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0) + { + completion = completion_; + runner = CreateRunner(); + GC::WriteBarrier(runner); + try + { + cs.Create(runner); + if (!ScreenJobValidate()) + { + runner->Destroy(); + runner = nullptr; + return false; + } + if (flags & SJ_DELAY) intermissiondelay = 10; // need to wait a bit at the start to let the timer catch up. + else intermissiondelay = 0; + gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission; + } + catch (...) + { + if (runner) runner->Destroy(); + runner = nullptr; + throw; + } + return true; + } + return false; +} + +bool StartCutscene(const char* s, int flags, const CompletionFunc& completion) +{ + CutsceneDef def; + def.function = s; + return StartCutscene(def, 0, completion); +} + +//============================================================================= +// +// +// +//============================================================================= + +void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic) +{ + Mus_Stop(); + FX_StopAllSounds(); // JBF 20031228 + if (userConfig.nologo) + { + gameaction = def_ga; + } + else + { + if (!StartCutscene(globalCutscenes.Intro, SJ_BLOCKUI|SJ_DELAY, [=](bool) { + gameaction = complete_ga; + })) gameaction = def_ga; + } } //--------------------------------------------------------------------------- @@ -112,269 +424,137 @@ void DImageScreen::Draw(double smoothratio) // //--------------------------------------------------------------------------- -class ScreenJobRunner +void ShowScoreboard(int numplayers, const CompletionFunc& completion_) { - enum - { - State_Clear, - State_Run, - State_Fadeout - }; - TArray jobs; - CompletionFunc completion; - int index = -1; - float screenfade; - bool clearbefore; - int actionState; - int terminateState; - int fadeticks = 0; - int last_paused_tic = -1; - -public: - ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_) - : completion(std::move(completion_)), clearbefore(clearbefore_) - { - jobs.Resize(count); - memcpy(jobs.Data(), jobs_, count * sizeof(JobDesc)); - // Release all jobs from the garbage collector - the code as it is cannot deal with them getting collected. This should be removed later once the GC is working. - for (int i = 0; i < count; i++) - { - jobs[i].job->Release(); - } - AdvanceJob(false); - } - - ~ScreenJobRunner() - { - DeleteJobs(); - } - - void DeleteJobs() - { - for (auto& job : jobs) - { - job.job->ObjectFlags |= OF_YesReallyDelete; - delete job.job; - } - jobs.Clear(); - } - - void AdvanceJob(bool skip) - { - if (index >= 0) - { - if (jobs[index].postAction) jobs[index].postAction(); - jobs[index].job->Destroy(); - } - index++; - while (index < jobs.Size() && (jobs[index].job == nullptr || (skip && jobs[index].ignoreifskipped))) - { - if (jobs[index].job != nullptr) jobs[index].job->Destroy(); - index++; - } - actionState = clearbefore ? State_Clear : State_Run; - 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(double smoothratio) - { - auto& job = jobs[index]; - auto now = I_GetTimeNS(); - bool processed = job.job->ProcessInput(); - - if (job.job->fadestate == DScreenJob::fadein) - { - double ms = (job.job->ticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime; - float screenfade = (float)clamp(ms, 0., 1.); - twod->SetScreenFade(screenfade); - if (screenfade == 1.f) job.job->fadestate = DScreenJob::visible; - } - int state = job.job->DrawFrame(smoothratio); - twod->SetScreenFade(1.f); - return state; - } - - int FadeoutFrame(double smoothratio) - { - 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() + completion = completion_; + runner = CreateRunner(); + GC::WriteBarrier(runner); + + const char* qname = globalCutscenes.MPSummaryScreen; + auto func = LookupFunction(qname); + if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname); + if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != TypeSInt32) + I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname); + VMValue val[2] = { runner, numplayers }; + VMCall(func, val, 2, nullptr, 0); + if (!ScreenJobValidate()) { + runner->Destroy(); + runner = nullptr; if (completion) completion(false); - completion = nullptr; // only finish once. + completion = nullptr; + return; } + gameaction = ga_intermission; +} - void OnTick() +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_) +{ + completion = completion_; + runner = CreateRunner(); + GC::WriteBarrier(runner); + + // retrieve cluster relations for cluster-based cutscenes. + ClusterDef* fromcluster = nullptr, *tocluster = nullptr; + if (fromMap) fromcluster = FindCluster(fromMap->cluster); + if (toMap) tocluster = FindCluster(toMap->cluster); + if (fromcluster == tocluster) fromcluster = tocluster = nullptr; + + + try { - if (paused) return; - if (index >= jobs.Size()) + if (fromMap) { - //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. + if (!fromMap->outro.Create(runner, fromMap, !!toMap)) + { + if (fromcluster == nullptr || !fromcluster->outro.Create(runner, fromMap, !!toMap)) + globalCutscenes.DefaultMapOutro.Create(runner, fromMap, !!toMap); + } + } + if (fromMap || (g_gameType & GAMEFLAG_PSEXHUMED)) + CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info, toMap); + + if (toMap) + { + if (!toMap->intro.Create(runner, toMap, !!fromMap)) + { + if (tocluster == nullptr || !tocluster->intro.Create(runner, toMap, !!fromMap)) + globalCutscenes.DefaultMapIntro.Create(runner, toMap, !!fromMap); + } + globalCutscenes.LoadingScreen.Create(runner, toMap, true); + } + else if (isShareware()) + { + globalCutscenes.SharewareEnd.Create(runner); + } + if (!ScreenJobValidate()) + { + runner->Destroy(); + runner = nullptr; + if (completion) completion(false); + completion = nullptr; + return; + } + gameaction = ga_intermission; + } + catch (...) + { + if (runner) runner->Destroy(); + runner = nullptr; + throw; + } +} + +CCMD(testcutscene) +{ + if (argv.argc() < 2) + { + Printf("Usage: testcutscene \n"); + return; + } + try + { + if (StartCutscene(argv[1], 0, [](bool) {})) + { + C_HideConsole(); + } + } + catch (const CRecoverableError& err) + { + Printf(TEXTCOLOR_RED "Unable to play cutscene: %s\n", err.what()); + } +} + + + +/* +Blood: + if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos(); else { - if (jobs[index].job->state == DScreenJob::running) + gameaction = ga_mainmenu; + } + RunScreenJob(jobs, [](bool) { + Mus_Stop(); + gameaction = ga_mainmenu; + }, SJ_BLOCKUI); + +Exhumed: + if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; }); + else gameaction = ga_mainmenu; + +SW: + if (!userConfig.nologo) Logo([](bool) { - jobs[index].job->ticks++; - jobs[index].job->OnTick(); - } - else if (jobs[index].job->state == DScreenJob::stopping) - { - fadeticks++; - } - } - } - - bool RunFrame() - { - if (index >= jobs.Size()) - { - DeleteJobs(); - twod->SetScreenFade(1); - twod->ClearScreen(); // This must not leave the 2d buffer empty. - 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; - twod->ClearScreen(); - } - else if (actionState == State_Run) - { - terminateState = DisplayFrame(smoothratio); - if (terminateState < 1) - { - // Must lock before displaying. - if (jobs[index].job->fadestyle & DScreenJob::fadeout) - { - jobs[index].job->fadestate = DScreenJob::fadeout; - jobs[index].job->state = DScreenJob::stopping; - actionState = State_Fadeout; - fadeticks = 0; - } - else - { - AdvanceJob(terminateState < 0); - } - } - } - else if (actionState == State_Fadeout) - { - int ended = FadeoutFrame(smoothratio); - if (ended < 1) - { - jobs[index].job->state = DScreenJob::stopped; - AdvanceJob(terminateState < 0); - } - } - return true; - } -}; - -ScreenJobRunner *runner; - -void RunScreenJob(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore, bool blockingui) -{ - assert(completion != nullptr); - videoclearFade(); - if (count) - { - runner = new ScreenJobRunner(jobs, count, completion, clearbefore); - gameaction = blockingui? ga_intro : ga_intermission; - } - else - { - completion(false); - } -} - -void DeleteScreenJob() -{ - if (runner) - { - delete runner; - runner = nullptr; - } - twod->SetScreenFade(1); -} - -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) - { - // 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 false; - } - auto res = runner->RunFrame(); - if (!res) - { - assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing); - DeleteScreenJob(); - } - return res; -} + gameaction = ga_mainmenunostopsound; + }); + else gameaction = ga_mainmenu; +*/ \ No newline at end of file diff --git a/source/core/screenjob.h b/source/core/screenjob.h index b0a48b058..94c445158 100644 --- a/source/core/screenjob.h +++ b/source/core/screenjob.h @@ -3,153 +3,29 @@ #include "dobject.h" #include "v_2ddrawer.h" #include "d_eventbase.h" +#include "s_soundinternal.h" +#include "gamestate.h" using CompletionFunc = std::function; -struct JobDesc; -class ScreenJobRunner; -class DScreenJob : public DObject +void Job_Init(); + +enum { - DECLARE_CLASS(DScreenJob, DObject) - 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, - fadein = 1, - fadeout = 2, - }; - - DScreenJob(int fade = 0, float fadet = 250.f) : fadestyle(fade), fadetime(fadet) {} - - virtual bool ProcessInput() - { - return false; - } - - 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) - { - 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; - } - - int GetFadeState() const { return fadestate; } - + SJ_BLOCKUI = 1, + SJ_DELAY = 2, }; -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -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) {} - void OnTick() override; - void Draw(double smooth) override; -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -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) : DSkippableScreenJob(fade), waittime(wait) - { - tex = tile; - trans = translation; - } - - DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait) - { - tilenum = tile; - trans = translation; - } - - void OnTick() override; - void Draw(double smooth) override; -}; - - - - -struct JobDesc -{ - DScreenJob* job; - void (*postAction)(); - bool ignoreifskipped; -}; - - -void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true, bool blockingui = false); void EndScreenJob(); void DeleteScreenJob(); bool ScreenJobResponder(event_t* ev); -void ScreenJobTick(); -bool ScreenJobDraw(); +bool ScreenJobTick(); +void ScreenJobDraw(); -struct AnimSound -{ - int framenum; - int soundnum; -}; - -DScreenJob *PlayVideo(const char *filename, const AnimSound *ans = nullptr, const int *frameticks = nullptr, bool nosoundstop = false); +struct CutsceneDef; +struct MapRecord; +struct SummaryInfo; +bool StartCutscene(const char* s, int flags, const CompletionFunc& completion); +void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic); +void ShowScoreboard(int numplayers, const CompletionFunc& completion_); +void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_); diff --git a/source/core/searchpaths.cpp b/source/core/searchpaths.cpp index d3f998ac2..99c974399 100644 --- a/source/core/searchpaths.cpp +++ b/source/core/searchpaths.cpp @@ -398,6 +398,12 @@ static TArray ParseGrpInfo(const char *fn, FileReader &fr, TMaplevelNumber)]; + auto cluster = FindCluster(lev->cluster); + FString volname; + if (cluster) volname = cluster->name; if (volname.IsEmpty() && am_nameontop) y = 1; DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, mapname, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200, diff --git a/source/games/blood/all.cpp b/source/games/blood/all.cpp index 0c1757af6..46e731887 100644 --- a/source/games/blood/all.cpp +++ b/source/games/blood/all.cpp @@ -31,7 +31,6 @@ #include "src/callback.cpp" #include "src/choke.cpp" #include "src/controls.cpp" -#include "src/credits.cpp" #include "src/db.cpp" #include "src/dude.cpp" #include "src/endgame.cpp" diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index a97d7bb86..2b59a0761 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "v_draw.h" #include "texturemanager.h" #include "statusbar.h" +#include "vm.h" BEGIN_BLD_NS @@ -79,7 +80,7 @@ void EndLevel(void) seqKillAll(); } -void StartLevel(MapRecord* level) +void StartLevel(MapRecord* level, bool newgame) { if (!level) return; gFrameCount = 0; @@ -96,14 +97,14 @@ void StartLevel(MapRecord* level) /////// } #if 0 - else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel)) + else if (gGameOptions.nGameType > 0 && newgame) { // todo gBlueFlagDropped = false; gRedFlagDropped = false; } #endif - if (gGameOptions.uGameFlags & GF_AdvanceLevel) + if (!newgame) { for (int i = connecthead; i >= 0; i = connectpoint2[i]) { @@ -180,13 +181,13 @@ void StartLevel(MapRecord* level) evInit(); for (int i = connecthead; i >= 0; i = connectpoint2[i]) { - if (!(gGameOptions.uGameFlags & GF_AdvanceLevel)) + if (newgame) { playerInit(i, 0); } playerStart(i, 1); } - if (gGameOptions.uGameFlags & GF_AdvanceLevel) + if (!newgame) { for (int i = connecthead; i >= 0; i = connectpoint2[i]) { @@ -203,7 +204,6 @@ void StartLevel(MapRecord* level) pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon; } } - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); PreloadCache(); InitMirrors(); trInit(); @@ -222,43 +222,24 @@ void StartLevel(MapRecord* level) } -void NewLevel(MapRecord *sng, int skill) +void NewLevel(MapRecord *sng, int skill, bool newgame) { - auto completion = [=](bool = false) - { - if (skill != -1) gGameOptions.nDifficulty = skill; - gSkill = gGameOptions.nDifficulty; - StartLevel(sng); - gameaction = ga_level; - }; - - bool startedCutscene = false; - if (!(sng->flags & MI_USERMAP)) - { - int episode = volfromlevelnum(sng->levelNumber); - int level = mapfromlevelnum(sng->levelNumber); - if (gEpisodeInfo[episode].cutALevel == level && gEpisodeInfo[episode].cutsceneAName[0]) - { - levelPlayIntroScene(episode, completion); - startedCutscene = true; - } - - } - if (!startedCutscene) completion(false); - + if (skill != -1) gGameOptions.nDifficulty = skill; + gSkill = gGameOptions.nDifficulty; + StartLevel(sng, newgame); + gameaction = ga_level; } void GameInterface::NewGame(MapRecord *sng, int skill, bool) { gGameOptions.uGameFlags = 0; cheatReset(); - NewLevel(sng, skill); + NewLevel(sng, skill, true); } void GameInterface::NextLevel(MapRecord *map, int skill) { - gGameOptions.uGameFlags = GF_AdvanceLevel; - NewLevel(map, skill); + NewLevel(map, skill, false); } void GameInterface::Ticker() @@ -328,40 +309,12 @@ void GameInterface::Ticker() team_ticker[i] = 0; } - if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0) + if (gGameOptions.uGameFlags & GF_AdvanceLevel) { + gGameOptions.uGameFlags &= ~GF_AdvanceLevel; seqKillAll(); - if (gGameOptions.uGameFlags & GF_EndGame) - { - STAT_Update(true); - if (gGameOptions.nGameType == 0) - { - auto completion = [](bool) { - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); - gameaction = ga_creditsmenu; - }; - - if (gGameOptions.uGameFlags & GF_PlayCutscene) - { - levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion); - } - else completion(false); - } - else - { - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); - } - } - else - { - STAT_Update(false); - EndLevel(); - Mus_Stop(); - // Fixme: Link maps, not episode/level pairs. - int ep = volfromlevelnum(currentLevel->levelNumber); - auto map = FindMapByLevelNum(levelnum(ep, gNextLevel)); - CompleteLevel(map); - } + STAT_Update(gNextLevel == nullptr); + CompleteLevel(gNextLevel); } r_NoInterpolate = false; } @@ -471,11 +424,12 @@ void GameInterface::app_init() levelLoadDefaults(); LoadDefinitions(); + + //--------- SetTileNames(); C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25); TileFiles.SetBackup(); - powerupInit(); Printf(PRINT_NONOTIFY, "Loading cosine table\n"); trigInit(); Printf(PRINT_NONOTIFY, "Initializing view subsystem\n"); @@ -485,15 +439,15 @@ void GameInterface::app_init() Printf(PRINT_NONOTIFY, "Initializing weapon animations\n"); WeaponInit(); + Printf(PRINT_NONOTIFY, "Initializing sound system\n"); + sndInit(); + myconnectindex = connecthead = 0; gNetPlayers = numplayers = 1; connectpoint2[0] = -1; gGameOptions.nGameType = 0; UpdateNetworkMenus(); - Printf(PRINT_NONOTIFY, "Initializing sound system\n"); - sndInit(); - gChoke.init(518, chokeCallback); UpdateDacs(0, true); @@ -518,17 +472,7 @@ static void gameInit() void GameInterface::Startup() { gameInit(); - if (userConfig.CommandMap.IsNotEmpty()) - { - } - else - { - if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos(); - else - { - gameaction = ga_mainmenu; - } - } + PlayLogos(ga_mainmenu, ga_mainmenu, true); } @@ -578,4 +522,48 @@ ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize) return new GameInterface; } +enum +{ + kLoadScreenCRC = -2051908571, + kLoadScreenWideBackWidth = 256, + kLoadScreenWideSideWidth = 128, + +}; + + +DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen) +{ + static int bLoadScreenCrcMatch = -1; + if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC; + ACTION_RETURN_INT(bLoadScreenCrcMatch); +} + +DEFINE_ACTION_FUNCTION(_Blood, PlayIntroMusic) +{ + Mus_Play(nullptr, "PESTIS.MID", false); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Blood, sndStartSample) +{ + PARAM_PROLOGUE; + PARAM_INT(id); + PARAM_INT(vol); + PARAM_INT(chan); + PARAM_BOOL(looped); + PARAM_INT(chanflags); + sndStartSample(id, vol, chan, looped, EChanFlags::FromInt(chanflags)); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Blood, sndStartSampleNamed) +{ + PARAM_PROLOGUE; + PARAM_STRING(id); + PARAM_INT(vol); + PARAM_INT(chan); + sndStartSample(id, vol, chan); + return 0; +} + END_BLD_NS diff --git a/source/games/blood/src/blood.h b/source/games/blood/src/blood.h index 317252463..b1adaec6a 100644 --- a/source/games/blood/src/blood.h +++ b/source/games/blood/src/blood.h @@ -77,7 +77,6 @@ extern int blood_globalflags; void QuitGame(void); void PreloadCache(void); -void StartLevel(MapRecord *gameOptions); void ProcessFrame(void); void ScanINIFiles(void); void EndLevel(); @@ -121,7 +120,6 @@ struct GameInterface : ::GameInterface void MenuOpened() override; void MenuClosed() override; bool CanSave() override; - bool StartGame(FNewGameStartup& gs) override; void QuitToTitle() override; FString GetCoordString() override; ReservedSpace GetReservedScreenSpace(int viewsize) override; diff --git a/source/games/blood/src/credits.cpp b/source/games/blood/src/credits.cpp deleted file mode 100644 index 90f9cbc9d..000000000 --- a/source/games/blood/src/credits.cpp +++ /dev/null @@ -1,147 +0,0 @@ -//------------------------------------------------------------------------- -/* -Copyright (C) 2010-2019 EDuke32 developers and contributors -Copyright (C) 2019 Nuke.YKT - -This file is part of NBlood. - -NBlood is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -//------------------------------------------------------------------------- - -#include "ns.h" // Must come before everything else! - -#include "build.h" -#include "compat.h" -#include "SmackerDecoder.h" -#include "blood.h" -#include "animtexture.h" -#include "raze_sound.h" -#include "v_2ddrawer.h" -#include "screenjob.h" -#include "gamestate.h" -#include "razemenu.h" - -BEGIN_BLD_NS - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void playlogos() -{ - JobDesc jobs[6]; - int job = 0; - static AnimSound logosound[] = - { - { 1, -1 }, - { -1, -1 }, - { 1, -1 }, - { -1,-1 } - }; - - if (logosound[0].soundnum == -1) - { - logosound[0].soundnum = S_FindSound("logo.wav"); - logosound[2].soundnum = S_FindSound("gt.wav"); - } - - - if (!userConfig.nologo) - { - if (fileSystem.FindFile("logo.smk") != -1) - { - jobs[job++] = { PlayVideo("logo.smk", &logosound[0], 0) }; - } - else - { - jobs[job++] = { Create(1), []() { sndStartSample("THUNDER2", 128, -1); } }; - jobs[job++] = { Create(2050) }; - } - if (fileSystem.FindFile("gti.smk") != -1) - { - jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) }; - } - else - { - jobs[job++] = { Create(1), []() { sndStartSample("THUNDER2", 128, -1); } }; - jobs[job++] = { Create(2052) }; - } - } - jobs[job++] = { Create(1), []() { sndPlaySpecialMusicOrNothing(MUS_INTRO); sndStartSample("THUNDER2", 128, -1); }}; - jobs[job++] = { Create(2518, DScreenJob::fadein) }; - - RunScreenJob(jobs, job, [](bool) { - Mus_Stop(); - gameaction = ga_mainmenu; - }, true, true); -} - -void playSmk(const char *smk, const char *wav, int wavid, CompletionFunc func) -{ - JobDesc jobs{}; - static AnimSound smksound[] = - { - { 1, -1 }, - { -1, -1 }, - }; - int id = S_FindSoundByResID(wavid); - if (id <= 0) - { - FString wavv = wav; - FixPathSeperator(wavv); - id = S_FindSound(wavv); - // Strip the drive letter and retry. - if (id <= 0 && wavv.Len() > 3 && wavv[1] == ':' && isalpha(wavv[0]) && wavv[2] == '/') - { - id = S_FindSound(wavv.GetChars() + 3); - } - } - FString smkk = smk; - FixPathSeperator(smkk); - smksound[0].soundnum = id; - jobs.job = PlayVideo(smkk, smksound, nullptr); - RunScreenJob(&jobs, 1, func); -} - -void levelPlayIntroScene(int nEpisode, CompletionFunc completion) -{ - Mus_Stop(); - sndKillAllSounds(); - sfxKillAllSounds(); - ambKillAll(); - seqKillAll(); - EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode]; - playSmk(pEpisode->cutsceneAName, pEpisode->cutsceneASound, pEpisode->at9028, completion); -} - -void levelPlayEndScene(int nEpisode, CompletionFunc completion) -{ - gGameOptions.uGameFlags &= ~GF_PlayCutscene; - Mus_Stop(); - sndKillAllSounds(); - sfxKillAllSounds(); - ambKillAll(); - seqKillAll(); - EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode]; - playSmk(pEpisode->cutsceneBName, pEpisode->cutsceneBSound, pEpisode->at902c, completion); -} - - - -END_BLD_NS diff --git a/source/games/blood/src/d_menu.cpp b/source/games/blood/src/d_menu.cpp index 8db9fc494..7207729db 100644 --- a/source/games/blood/src/d_menu.cpp +++ b/source/games/blood/src/d_menu.cpp @@ -159,23 +159,6 @@ bool GameInterface::CanSave() return (gamestate == GS_LEVEL && gPlayer[myconnectindex].pXSprite->health != 0); } -bool GameInterface::StartGame(FNewGameStartup& gs) -{ - if (gs.Episode >= 1) - { - if (g_gameType & GAMEFLAG_SHAREWARE) - { - M_StartMessage(GStrings("BUYBLOOD"), 1, NAME_None); // unreachable because we do not support Blood SW versions yet. - return false; - } - } - - sfxKillAllSounds(); - auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level)); - DeferedStartGame(map, gs.Skill); - return true; -} - FSavegameInfo GameInterface::GetSaveSig() { return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD }; diff --git a/source/games/blood/src/endgame.cpp b/source/games/blood/src/endgame.cpp index a50b2e298..38b73a489 100644 --- a/source/games/blood/src/endgame.cpp +++ b/source/games/blood/src/endgame.cpp @@ -36,152 +36,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS -enum -{ - kLoadScreenCRC = -2051908571, - kLoadScreenWideBackWidth = 256, - kLoadScreenWideSideWidth = 128, - -}; - -static int bLoadScreenCrcMatch = -1; - -static void drawTextScreenBackground(void) -{ - if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC; - - if (bLoadScreenCrcMatch) - { - if (ActiveRatio(twod->GetWidth(), twod->GetHeight()) < 1.34f) - { - DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } - else - { - int width = scale(twod->GetWidth(), 240, twod->GetHeight()); - int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth; - for (int i = 0; i < nCount; i++) - { - DrawTexture(twod, tileGetTexture(kLoadScreenWideBack), (i * kLoadScreenWideBackWidth), 0, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - } - DrawTexture(twod, tileGetTexture(kLoadScreenWideLeft), 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true, TAG_DONE); - DrawTexture(twod, tileGetTexture(kLoadScreenWideRight), width - tileWidth(kLoadScreenWideRight), 0, DTA_TopLeft, true, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - DrawTexture(twod, tileGetTexture(kLoadScreenWideMiddle), (width - tileWidth(kLoadScreenWideMiddle)) / 2, 0, DTA_TopLeft, true, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - } - } - else - { - DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } -} - -// One these screens get scriptified this should use the version in BloodMenuDelegate. -static void DrawCaption(const char* text) -{ - double scalex = 1.; // Expand the box if the text is longer - int width = BigFont->StringWidth(text); - int boxwidth = tileWidth(2038); - if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10); - - DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE); - DrawText(twod, BigFont, CR_UNDEFINED, 160 - width / 2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE); -} - - -class DBloodSummaryScreen : public DSkippableScreenJob -{ - void DrawKills(void) - { - char pBuffer[40]; - if (gGameOptions.nGameType == 0) - { - viewDrawText(1, FStringf("%s:", GStrings("KILLS")), 75, 50, -128, 0, 0, 1); - mysnprintf(pBuffer, 40,"%2d", gKillMgr.Kills); - viewDrawText(1, pBuffer, 160, 50, -128, 0, 0, 1); - viewDrawText(1, GStrings("OF"), 190, 50, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gKillMgr.TotalKills); - viewDrawText(1, pBuffer, 220, 50, -128, 0, 0, 1); - } - else - { - viewDrawText(3, "#", 85, 35, -128, 0, 0, 1); - viewDrawText(3, GStrings("NAME"), 100, 35, -128, 0, 0, 1); - viewDrawText(3, GStrings("FRAGS"), 210, 35, -128, 0, 0, 1); - int nStart = 0; - int nEnd = kMaxPlayers; - - for (int i = nStart; i < nEnd; i++) if (playeringame[i]) - { - mysnprintf(pBuffer, 40, "%-2d", i); - viewDrawText(3, pBuffer, 85, 50 + 8 * i, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%s", PlayerName(i)); - viewDrawText(3, pBuffer, 100, 50 + 8 * i, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%d", gPlayer[i].fragCount); - viewDrawText(3, pBuffer, 210, 50 + 8 * i, -128, 0, 0, 1); - } - } - } - - void DrawSecrets(void) - { - char pBuffer[40]; - viewDrawText(1, FStringf("%s:", GStrings("TXT_SECRETS")), 75, 70, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Founds); - viewDrawText(1, pBuffer, 160, 70, -128, 0, 0, 1); - viewDrawText(1, GStrings("OF"), 190, 70, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Total); - viewDrawText(1, pBuffer, 220, 70, -128, 0, 0, 1); - if (gSecretMgr.Super > 0) - viewDrawText(1, GStrings("TXT_SUPERSECRET"), 160, 100, -128, 2, 1, 1); - } - - - void Draw(double) override - { - drawTextScreenBackground(); - if (gGameOptions.nGameType == 0) - { - DrawCaption(GStrings("TXTB_LEVELSTATS")); - if (bPlayerCheated) - { - auto text = GStrings("TXTB_CHEATED"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - viewDrawText(font, text, 160, 32, -128, 0, 1, font == 3); - } - DrawKills(); - DrawSecrets(); - } - else - { - DrawCaption(GStrings("TXTB_FRAGSTATS")); - DrawKills(); - } - int myclock = ticks * 120 / GameTicRate; - if ((myclock & 32)) - { - auto text = GStrings("PRESSKEY"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3); - } - } -}; void GameInterface::LevelCompleted(MapRecord *map, int skill) { - JobDesc job = { Create() }; - sndStartSample(268, 128, -1, false, CHANF_UI); + EndLevel(); Mus_Stop(); - RunScreenJob(&job, 1, [=](bool) + + SummaryInfo info{}; + + info.kills = gKillMgr.Kills; + info.maxkills = gKillMgr.TotalKills; + info.secrets = gSecretMgr.Founds; + info.maxsecrets = gSecretMgr.Total; + info.time = gSecretMgr.Super; + info.endofgame = map == nullptr; + + ShowIntermission(currentLevel, map, &info, [=](bool) { soundEngine->StopAllChannels(); - gameaction = ga_nextlevel; + gameaction = map? ga_nextlevel : ga_creditsmenu; }); - } @@ -272,38 +146,4 @@ void SerializeGameStats(FSerializer& arc) CSecretMgr gSecretMgr; CKillMgr gKillMgr; -class DBloodLoadScreen : public DScreenJob -{ - const char* pzLoadingScreenText1; - MapRecord* rec; - -public: - DBloodLoadScreen(const char* caption, MapRecord* maprec) : DScreenJob(), rec(maprec) - { - if (gGameOptions.nGameType == 0) pzLoadingScreenText1 = GStrings("TXTB_LLEVEL"); - else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType)); - } - - void Draw(double) override - { - twod->ClearScreen(); - drawTextScreenBackground(); - DrawCaption(pzLoadingScreenText1); - viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1); - - auto text = GStrings("TXTB_PLSWAIT"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - - viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3); - } -}; - -void loadscreen(const char *caption, MapRecord* rec, CompletionFunc func) -{ - JobDesc job = { Create(caption, rec) }; - RunScreenJob(&job, 1, func); -} - - END_BLD_NS diff --git a/source/games/blood/src/levels.cpp b/source/games/blood/src/levels.cpp index 34f1c6d40..bbb2d2c3a 100644 --- a/source/games/blood/src/levels.cpp +++ b/source/games/blood/src/levels.cpp @@ -38,11 +38,8 @@ GAMEOPTIONS gSingleGameOptions = { 0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200 }; -EPISODEINFO gEpisodeInfo[kMaxEpisodes+1]; - int gSkill = 2; -int gEpisodeCount; -int gNextLevel; // fixme: let this contain a full level number. +MapRecord* gNextLevel; char BloodIniFile[BMAX_PATH] = "BLOOD.INI"; bool bINIOverride = false; @@ -94,22 +91,23 @@ void CheckKeyAbend(const char *pzSection, const char *pzKey) } -void levelLoadMapInfo(IniFile *pIni, MapRecord *pLevelInfo, const char *pzSection, int epinum, int mapnum) + +void levelLoadMapInfo(IniFile* pIni, MapRecord* pLevelInfo, const char* pzSection, int epinum, int mapnum, int* nextmap, int* nextsecret) { char buffer[16]; pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName)); - pLevelInfo->author = pIni->GetKeyString(pzSection, "Author", ""); + pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", ""); pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid"); pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1); - pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1); - pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1); + *nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0); + *nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0); pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0); pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0); for (int i = 0; i < kMaxMessages; i++) { - sprintf(buffer, "Message%d", i+1); - auto msg = pIni->GetKeyString(pzSection, buffer, ""); - pLevelInfo->AddMessage(i, msg); + sprintf(buffer, "Message%d", i + 1); + auto msg = pIni->GetKeyString(pzSection, buffer, ""); + pLevelInfo->AddMessage(i, msg); } } @@ -142,108 +140,101 @@ static const char* DefFile(void) return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini"; } +static FString cleanPath(const char* pth) +{ + FString path = pth; + FixPathSeperator(path); + if (FileExists(path)) return path; + if (path.Len() > 3 && path[1] == ':' && isalpha(path[0]) && path[2] == '/') + { + return path.Mid(3); + } + return path; +} + void levelLoadDefaults(void) { char buffer[64]; char buffer2[16]; + + int cutALevel = 0; + levelInitINI(DefFile()); - memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo)); - quoteMgr.InitializeQuote(MUS_INTRO, "PESTIS.MID"); int i; - for (i = 0; i < kMaxEpisodes; i++) + for (i = 1; i <= kMaxEpisodes; i++) { - sprintf(buffer, "Episode%d", i+1); + sprintf(buffer, "Episode%d", i); if (!BloodINI->SectionExists(buffer)) break; - EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i]; - auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer); - gVolumeNames[i] = ep_str; // only keep one table for the names. Todo: Consolidate this across games. - strncpy(pEpisodeInfo->cutsceneAName, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH); - pEpisodeInfo->at9028 = BloodINI->GetKeyInt(buffer, "CutWavA", -1); - if (pEpisodeInfo->at9028 == 0) - strncpy(pEpisodeInfo->cutsceneASound, BloodINI->GetKeyString(buffer, "CutWavA", ""), BMAX_PATH); - else - pEpisodeInfo->cutsceneASound[0] = 0; - strncpy(pEpisodeInfo->cutsceneBName, BloodINI->GetKeyString(buffer, "CutSceneB", ""), BMAX_PATH); - pEpisodeInfo->at902c = BloodINI->GetKeyInt(buffer, "CutWavB", -1); - if (pEpisodeInfo->at902c == 0) - strncpy(pEpisodeInfo->cutsceneBSound, BloodINI->GetKeyString(buffer, "CutWavB", ""), BMAX_PATH); - else - pEpisodeInfo->cutsceneBSound[0] = 0; + auto cluster = MustFindCluster(i); + auto volume = MustFindVolume(i); + CutsceneDef &csB = cluster->outro; + auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer); + cluster->name = volume->name = ep_str; + if (i > 1) volume->flags |= VF_SHAREWARELOCK; - pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); - pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0); - if (pEpisodeInfo->cutALevel > 0) - pEpisodeInfo->cutALevel--; - int j; - for (j = 0; j < kMaxLevels; j++) + csB.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneB", "")); + int soundint = BloodINI->GetKeyInt(buffer, "CutWavB", -1); + if (soundint > 0) csB.soundID = soundint + 0x40000000; + else csB.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", "")); + + //pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); + cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0); + if (cutALevel < 1) cutALevel = 1; + + int nextmaps[kMaxLevels]{}, nextsecrets[kMaxLevels]{}; + for (int j = 1; j <= kMaxLevels; j++) { - sprintf(buffer2, "Map%d", j+1); + sprintf(buffer2, "Map%d", j); if (!BloodINI->KeyExists(buffer, buffer2)) break; auto pLevelInfo = AllocateMap(); const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL); CheckSectionAbend(pMap); - pLevelInfo->levelNumber = levelnum(i, j); + SetLevelNum(pLevelInfo, makelevelnum(i, j)); + pLevelInfo->cluster = i; + pLevelInfo->mapindex = j; pLevelInfo->labelName = pMap; + if (j == 1) volume->startmap = pLevelInfo->labelName; pLevelInfo->fileName.Format("%s.map", pMap); - levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j); + levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j, &nextmaps[j - 1], &nextsecrets[j - 1]); + if (j == cutALevel) + { + CutsceneDef& csA = pLevelInfo->intro; + csA.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneA", "")); + int soundint = BloodINI->GetKeyInt(buffer, "CutWavA", -1); + if (soundint > 0) csA.soundID = soundint + 0x40000000; + else csA.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", "")); + } + } + // Now resolve the level links + for (int j = 1; j <= kMaxLevels; j++) + { + auto map = FindMapByIndexOnly(i, j); + if (map) + { + if (nextmaps[j - 1] > 0) + { + auto nmap = FindMapByIndexOnly(i, nextmaps[j - 1]); + if (nmap) map->NextMap = nmap->labelName; + else map->NextMap = "-"; + } + if (nextsecrets[j - 1] > 0) + { + auto nmap = FindMapByIndexOnly(i, nextsecrets[j - 1]); + if (nmap) map->NextSecret = nmap->labelName; + else map->NextSecret = "-"; + } + } } - pEpisodeInfo->nLevels = j; } - gEpisodeCount = i; } -void levelGetNextLevels(int *pnEndingA, int *pnEndingB) +void levelEndLevel(int secret) { - assert(pnEndingA != NULL && pnEndingB != NULL); - int nEndingA = currentLevel->nextLevel; - if (nEndingA >= 0) - nEndingA--; - int nEndingB = currentLevel->nextSecret; - if (nEndingB >= 0) - nEndingB--; - *pnEndingA = nEndingA; - *pnEndingB = nEndingB; -} - -void levelEndLevel(int arg) -{ - int nEndingA, nEndingB; - auto episode = volfromlevelnum(currentLevel->levelNumber); - EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[episode]; gGameOptions.uGameFlags |= GF_AdvanceLevel; - levelGetNextLevels(&nEndingA, &nEndingB); - switch (arg) - { - case 0: - if (nEndingA == -1) - { - if (pEpisodeInfo->cutsceneBName[0]) - gGameOptions.uGameFlags |= GF_PlayCutscene; - gGameOptions.uGameFlags |= GF_EndGame; - } - else - gNextLevel = nEndingA; - break; - case 1: - if (nEndingB == -1) - { - if (episode + 1 < gEpisodeCount) - { - if (pEpisodeInfo->cutsceneBName[0]) - gGameOptions.uGameFlags |= GF_PlayCutscene; - gGameOptions.uGameFlags |= GF_EndGame; - } - else - { - gGameOptions.uGameFlags |= GF_AdvanceLevel; - } - } - else - gNextLevel = nEndingB; - break; - } + if (!secret) gNextLevel = FindNextMap(currentLevel); + else gNextLevel = FindNextSecretMap(currentLevel); } void levelTryPlayMusic() diff --git a/source/games/blood/src/levels.h b/source/games/blood/src/levels.h index 27d65964d..fdd0fc019 100644 --- a/source/games/blood/src/levels.h +++ b/source/games/blood/src/levels.h @@ -41,7 +41,6 @@ enum enum EGameFlag { GF_AdvanceLevel = 1, - GF_EndGame = 2, // 4 was for playing intro cutscenes but is no longer used. GF_PlayCutscene = 8, }; @@ -67,38 +66,16 @@ struct GAMEOPTIONS { #pragma pack(pop) -enum { - MUS_INTRO = 0, - MUS_LOADING = 1, -}; - -struct EPISODEINFO -{ - int nLevels; - unsigned int bloodbath : 1; - unsigned int cutALevel : 4; - char cutsceneAName[BMAX_PATH]; - char cutsceneBName[BMAX_PATH]; - int at9028; - int at902c; - char cutsceneASound[BMAX_PATH]; - char cutsceneBSound[BMAX_PATH]; -}; - -extern EPISODEINFO gEpisodeInfo[]; extern GAMEOPTIONS gSingleGameOptions; extern GAMEOPTIONS gGameOptions; extern int gSkill; extern char BloodIniFile[]; extern bool bINIOverride; -extern int gEpisodeCount; -extern int gNextLevel; +extern MapRecord* gNextLevel; extern bool gGameStarted; void levelInitINI(const char *pzIni); void levelOverrideINI(const char *pzIni); -void levelPlayIntroScene(int nEpisode, CompletionFunc completion); -void levelPlayEndScene(int nEpisode, CompletionFunc completion); void levelSetupSecret(int nCount); void levelTriggerSecret(int nSecret); void CheckSectionAbend(const char *pzSection); diff --git a/source/games/blood/src/loadsave.cpp b/source/games/blood/src/loadsave.cpp index 5abf3ffda..a8436d8a2 100644 --- a/source/games/blood/src/loadsave.cpp +++ b/source/games/blood/src/loadsave.cpp @@ -641,7 +641,6 @@ void SerializeState(FSerializer& arc) ("modern", gModernMap) #endif ("cheating", bPlayerCheated) - ("nextlevel", gNextLevel) ("skyhoriz", pSky->horizfrac) ("skyy", pSky->yoffs) ("scale", pSky->yscale) diff --git a/source/games/blood/src/messages.cpp b/source/games/blood/src/messages.cpp index 3fc2fd938..8ff7ad3f7 100644 --- a/source/games/blood/src/messages.cpp +++ b/source/games/blood/src/messages.cpp @@ -267,10 +267,8 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2) { if (!nArg1 || !nArg2 || strlen(pzArgs) < 3) return -1; - *nArg1 = pzArgs[0] - '0' - 1; - *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1; - *nArg1 = ClipRange(*nArg1, 0, gEpisodeCount-1); - *nArg2 = ClipRange(*nArg2, 0, gEpisodeInfo[*nArg1].nLevels-1); + *nArg1 = pzArgs[0] - '0'; + *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0'); return 2; } @@ -423,7 +421,7 @@ static bool cheatMario(cheatseq_t* c) int nEpisode, nLevel; if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2) { - auto map = FindMapByLevelNum(levelnum(nEpisode, nLevel)); + auto map = FindMapByIndex(nEpisode, nLevel); if (map) DeferedStartGame(map, -1); } return true; diff --git a/source/games/blood/src/namelist.h b/source/games/blood/src/namelist.h index b81969d7b..89ed1a7b4 100644 --- a/source/games/blood/src/namelist.h +++ b/source/games/blood/src/namelist.h @@ -1,6 +1,9 @@ // names for everything that gets accessed by scripts. x(MENUBAR, 2038) x(BackTile, 253) +x(Monolithscreen, 2050) +x(GTIScreen, 2052) +x(TitleScreen, 2518) x(CrosshairTile, 2319) x(LoadScreen, 2049) diff --git a/source/games/blood/src/nnexts.cpp b/source/games/blood/src/nnexts.cpp index 2a300ddde..35e267875 100644 --- a/source/games/blood/src/nnexts.cpp +++ b/source/games/blood/src/nnexts.cpp @@ -5214,13 +5214,7 @@ void seqSpawnerOffSameTx(XSPRITE* pXSource) { void levelEndLevelCustom(int nLevel) { gGameOptions.uGameFlags |= GF_AdvanceLevel; - - if (nLevel >= 16 || nLevel < 0) { - gGameOptions.uGameFlags |= GF_EndGame; - return; - } - - gNextLevel = nLevel; + gNextLevel = FindMapByIndex(currentLevel->cluster, nLevel + 1); } void callbackUniMissileBurst(int nSprite) // 22 diff --git a/source/games/blood/src/player.cpp b/source/games/blood/src/player.cpp index 03336c3ac..0a87dfd11 100644 --- a/source/games/blood/src/player.cpp +++ b/source/games/blood/src/player.cpp @@ -400,10 +400,6 @@ void powerupClear(PLAYER *pPlayer) } } -void powerupInit(void) -{ -} - int packItemToPowerup(int nPack) { int nPowerUp = -1; diff --git a/source/games/blood/src/player.h b/source/games/blood/src/player.h index 53f4dc4a3..c5f72d49d 100644 --- a/source/games/blood/src/player.h +++ b/source/games/blood/src/player.h @@ -241,7 +241,6 @@ void powerupDeactivate(PLAYER *pPlayer, int nPowerUp); void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState); void powerupProcess(PLAYER *pPlayer); void powerupClear(PLAYER *pPlayer); -void powerupInit(void); int packItemToPowerup(int nPack); int powerupToPackItem(int nPowerUp); char packAddItem(PLAYER *pPlayer, unsigned int nPack); diff --git a/source/games/blood/src/sbar.cpp b/source/games/blood/src/sbar.cpp index 952e40ab8..8ec54765b 100644 --- a/source/games/blood/src/sbar.cpp +++ b/source/games/blood/src/sbar.cpp @@ -218,7 +218,7 @@ private: stats.font = SmallFont; stats.letterColor = CR_DARKRED; stats.standardColor = CR_DARKGRAY; - stats.time = Scale(gFrameCount, 1000, kTicsPerSec); + stats.time = gFrameCount / GameTicRate; if (automapMode == am_full) { @@ -248,6 +248,7 @@ private: stats.secrets = gSecretMgr.Founds; stats.supersecrets = gSecretMgr.Super; stats.maxsecrets = max(gSecretMgr.Founds, gSecretMgr.Total); // If we found more than there are, increase the total. Some levels have a bugged counter. + stats.time = Scale(PlayClock, 1000, 120); DBaseStatusBar::PrintLevelStats(stats); } diff --git a/source/games/duke/src/2d_d.cpp b/source/games/duke/src/2d_d.cpp index ffe873c59..e8f3561f6 100644 --- a/source/games/duke/src/2d_d.cpp +++ b/source/games/duke/src/2d_d.cpp @@ -133,1041 +133,4 @@ void InitFonts_d() } -//========================================================================== -// -// wrappers around DrawText to allow easier reuse of the old code. -// The vertical displacements are to have the same positioning as with the original code. -// -//========================================================================== - -static void BigText(double x, double y, const char* text, double alpha = 1.) -{ - auto width = BigFont->StringWidth(text); - DrawText(twod, BigFont, CR_UNTRANSLATED, x - width / 2, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Alpha, alpha, TAG_DONE); -} - -static void GameText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - if (align != -1) - x -= SmallFont->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont, CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -static void MiniText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - if (align != -1) - x -= SmallFont2->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont2, CR_UNDEFINED, x, y, t, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDRealmsScreen : public DSkippableScreenJob -{ -public: - DDRealmsScreen() : DSkippableScreenJob(fadein | fadeout) {} - - void OnTick() override - { - if (ticks >= 7 * GameTicRate) state = finished; - } - - void Draw(double smoothratio) override - { - 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); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DTitleScreen : public DSkippableScreenJob -{ - int soundanm = 0; - -public: - DTitleScreen() : DSkippableScreenJob(fadein | fadeout) - { - } - - void OnTick() override - { - int clock = ticks * 120 / GameTicRate; - if (soundanm == 0 && clock >= 120 && clock < 120 + 60) - { - soundanm = 1; - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - if (soundanm == 1 && clock > 220 && clock < (220 + 30)) - { - soundanm = 2; - S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); - } - if (soundanm == 2 && clock >= 280 && clock < 395) - { - soundanm = 3; - if (isPlutoPak()) S_PlaySound(FLY_BY, CHAN_AUTO, CHANF_UI); - } - else if (soundanm == 3 && clock >= 395) - { - soundanm = 4; - 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.) - { - tex = tileGetTexture(DUKENUKEM, true); - translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0; - - DrawTexture(twod, tileGetTexture(DUKENUKEM, true), 160, 104, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); - } - - scale = clamp(clock - 220, 0, 30) / 32.; - if (scale > 0.) - { - tex = tileGetTexture(THREEDEE, true); - translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0; - - DrawTexture(twod, tileGetTexture(THREEDEE, true), 160, 129, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); - } - - if (isPlutoPak()) - { - scale = (410 - clamp(clock, 280, 395)) / 16.; - if (scale > 0. && clock > 280) - { - tex = tileGetTexture(PLUTOPAKSPRITE + 1, true); - translation = tex->GetTexture()->GetImage()->UseGamePalette() ? TRANSLATION(Translation_BasePalettes, TITLEPAL) : 0; - - DrawTexture(twod, tileGetTexture(PLUTOPAKSPRITE + 1, true), 160, 151, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_TranslationIndex, translation, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); - } - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void Logo_d(const CompletionFunc &completion) -{ - Mus_Stop(); - FX_StopAllSounds(); // JBF 20031228 - - static const AnimSound logosound[] = - { - { 1, FLY_BY+1 }, - { 19, PIPEBOMB_EXPLODE+1 }, - { -1, -1 } - }; - static const int logoframetimes[] = { 9, 9, 9 }; - - JobDesc jobs[3]; - int job = 0; - if (!userConfig.nologo) - { - if (!isShareware()) jobs[job++] = { PlayVideo("logo.anm", logosound, logoframetimes), []() { S_PlaySpecialMusic(MUS_INTRO); } }; - else jobs[job++] = { Create(1), []() { S_PlaySpecialMusic(MUS_INTRO); } }; - if (!isNam()) jobs[job++] = { Create(), nullptr }; - } - else S_PlaySpecialMusic(MUS_INTRO); - jobs[job++] = { Create(), []() { S_PlaySound(NITEVISION_ONOFF, CHAN_AUTO, CHANF_UI); } }; - RunScreenJob(jobs, job, completion, true, true); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -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() : DSkippableScreenJob(fadein | fadeout) {} - - void OnTick() - { - int currentclock = ticks * 120 / GameTicRate; - - bossani = -1; - breathebg = false; - breatheani = -1; - - // boss - if (currentclock > 390 && currentclock < 780) - { - for (int t = 0; t < 35; t += 5) if (bossmove[t + 2] && (currentclock % 390) > bossmove[t] && (currentclock % 390) <= bossmove[t + 1]) - { - if (t == 10 && bonuscnt == 1) - { - S_PlaySound(SHOTGUN_FIRE, CHAN_AUTO, CHANF_UI); - S_PlaySound(SQUISHED, CHAN_AUTO, CHANF_UI); - bonuscnt++; - } - bossani = t; - } - } - - // Breathe - if (currentclock < 450 || currentclock >= 750) - { - if (currentclock >= 750) - { - breathebg = true; - if (currentclock >= 750 && bonuscnt == 2) - { - S_PlaySound(DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI); - bonuscnt++; - } - } - for (int t = 0; t < 20; t += 5) - if (breathe[t + 2] && (currentclock % 120) > breathe[t] && (currentclock % 120) <= breathe[t + 1]) - { - if (t == 5 && bonuscnt == 0) - { - S_PlaySound(BOSSTALKTODUKE, CHAN_AUTO, CHANF_UI); - bonuscnt++; - } - breatheani = t; - } - } - - } - - void Draw(double) override - { - auto translation = TRANSLATION(Translation_BasePalettes, ENDINGPAL); - - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(VICTORY1, true), 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true, TAG_DONE); - - if (bossani != -1) - { - DrawTexture(twod, tileGetTexture(bossmove[bossani + 2], true), bossmove[bossani + 3], bossmove[bossani + 4], DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); - } - - if (breathebg) - { - DrawTexture(twod, tileGetTexture(VICTORY1 + 8, true), 86, 59, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); - } - - if (breatheani != -1) - { - DrawTexture(twod, tileGetTexture(breathe[breatheani + 2], true), breathe[breatheani + 3], breathe[breatheani + 4], DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TranslationIndex, translation, DTA_TopLeft, true, TAG_DONE); - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DEpisode3End : public DImageScreen -{ - int sound = 0; - int64_t waittime = 0; - -public: - - FGameTexture* getTexture() - { - 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); - } - -public: - DEpisode3End() : DImageScreen(getTexture(), fadein|fadeout, 0x7fffffff) - { - } - - void Skipped() override - { - FX_StopAllSounds(); - } - - void OnTick() override - { - switch (sound) - { - case 0: - S_PlaySound(ENDSEQVOL3SND5, CHAN_AUTO, CHANF_UI); - sound++; - break; - - case 1: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND5)) - { - S_PlaySound(ENDSEQVOL3SND6, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 2: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND6)) - { - S_PlaySound(ENDSEQVOL3SND7, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 3: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND7)) - { - S_PlaySound(ENDSEQVOL3SND8, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 4: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND8)) - { - S_PlaySound(ENDSEQVOL3SND9, CHAN_AUTO, CHANF_UI); - sound++; - } - break; - - case 5: - if (!S_CheckSoundPlaying(ENDSEQVOL3SND9)) - { - sound++; - 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 (ticks > waittime) state = finished; - } - break; - - default: - break; - } - if (state != running) FX_StopAllSounds(); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DEpisode4Text : public DSkippableScreenJob -{ -public: - DEpisode4Text() : DSkippableScreenJob(fadein | fadeout) {} - - void Draw(double) override - { - twod->ClearScreen(); - BigText(160, 60, GStrings("Thanks to all our")); - BigText(160, 60 + 16, GStrings("fans for giving")); - 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.")); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DEpisode5End : public DImageScreen -{ - int sound = 0; - -public: - DEpisode5End() : DImageScreen(FIREFLYGROWEFFECT, fadein|fadeout) - { - } - - void OnTick() override - { - switch (sound) - { - case 0: - sound++; - break; - - case 1: - S_PlaySound(E5L7_DUKE_QUIT_YOU, CHAN_AUTO, CHANF_UI); - sound++; - break; - - default: - break; - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -static void bonussequence_d(int num, JobDesc *jobs, int &job) -{ - static const AnimSound cineov2sound[] = - { - { 1, WIND_AMBIENCE+1 }, - { 26, ENDSEQVOL2SND1+1 }, - { 36, ENDSEQVOL2SND2+1 }, - { 54, THUD+1 }, - { 62, ENDSEQVOL2SND3+1 }, - { 75, ENDSEQVOL2SND4 + 1 }, - { 81, ENDSEQVOL2SND5 + 1 }, - { 115, ENDSEQVOL2SND6 + 1 }, - { 124, ENDSEQVOL2SND7 + 1 }, - { -1, -1 } - }; - - static const AnimSound cineov3sound[] = - { - { 1, WIND_REPEAT + 1 }, - { 98, DUKE_GRUNT + 1 }, - { 102, THUD + 1 }, - { 102, SQUISHED + 1 }, - { 124, ENDSEQVOL3SND3 + 1 }, - { 134, ENDSEQVOL3SND2 + 1 }, - { 158, PIPEBOMB_EXPLODE + 1 }, - { -1,-1 } - }; - - static const AnimSound dukedcsound[] = - { - { 144, ENDSEQVOL3SND3 + 1 }, - { -1,-1 } - }; - - static const AnimSound vol4e1[] = - { - { 3, DUKE_UNDERWATER+1 }, - { 35, VOL4ENDSND1+1 }, - { -1,-1 } - }; - - static const AnimSound vol4e2[] = - { - { 11, DUKE_UNDERWATER+1 }, - { 20, VOL4ENDSND1+1 }, - { 39, VOL4ENDSND2+1 }, - { 50, -1 }, - { -1,-1 } - }; - - static const AnimSound vol4e3[] = - { - { 1, BOSS4_DEADSPEECH+1 }, - { 40, VOL4ENDSND1+1 }, - { 40, DUKE_UNDERWATER+1 }, - { 50, BIGBANG+1 }, - { -1,-1 } - }; - - - static const int framespeed_10[] = { 10, 10, 10 }; - static const int framespeed_14[] = { 14, 14, 14 }; - static const int framespeed_18[] = { 18, 18, 18 }; - - switch (num) - { - case 0: - jobs[job++] = { Create(), nullptr }; - jobs[job++] = { Create(E1ENDSCREEN, DScreenJob::fadein|DScreenJob::fadeout, 0x7fffffff), []() { Mus_Stop(); } }; - break; - - case 1: - Mus_Stop(); - jobs[job++] = { PlayVideo("cineov2.anm", cineov2sound, framespeed_18), []() { S_PlaySound(PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); } }; - jobs[job++] = { Create(E2ENDSCREEN, DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff), []() { FX_StopAllSounds(); } }; - break; - - case 2: - Mus_Stop(); - if (g_gameType & GAMEFLAG_DUKEDC) - { - jobs[job++] = { PlayVideo("radlogo.anm", dukedcsound, framespeed_10), nullptr }; - } - else - { - jobs[job++] = { PlayVideo("cineov3.anm", cineov3sound, framespeed_10), nullptr }; - jobs[job++] = { Create(200), []() { FX_StopAllSounds(); } }; - jobs[job++] = { Create(), []() { if (!isPlutoPak()) S_PlaySound(ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); } }; - if (!isPlutoPak()) jobs[job++] = { Create(TexMan.GetGameTextureByName("DUKETEAM.ANM", false, FTextureManager::TEXMAN_ForceLookup), - DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff), []() { FX_StopAllSounds(); } }; - } - break; - - case 3: - Mus_Stop(); - jobs[job++] = { PlayVideo("vol4e1.anm", vol4e1, framespeed_10), nullptr }; - jobs[job++] = { PlayVideo("vol4e2.anm", vol4e2, framespeed_10), nullptr }; - jobs[job++] = { PlayVideo("vol4e3.anm", vol4e3, framespeed_10), []() { S_PlaySound(ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); } }; - jobs[job++] = { Create(), nullptr }; - jobs[job++] = { Create(TexMan.GetGameTextureByName("DUKETEAM.ANM", false, FTextureManager::TEXMAN_ForceLookup), - DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff), []() { FX_StopAllSounds(); } }; - break; - - case 4: - Mus_Stop(); - jobs[job++] = { Create(), []() { FX_StopAllSounds(); } }; - break; - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void showtwoscreens(const CompletionFunc& completion) -{ - JobDesc jobs[2]; - int job = 0; - - jobs[job++] = { Create(3291), nullptr }; - jobs[job++] = { Create(3290), nullptr }; - RunScreenJob(jobs, job, completion); -} - -void doorders(const CompletionFunc& completion) -{ - JobDesc jobs[4]; - int job = 0; - - for (int i = 0; i < 4; i++) - jobs[job++] = { Create(ORDERING + i), nullptr }; - RunScreenJob(jobs, job, completion); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDukeMultiplayerBonusScreen : public DSkippableScreenJob -{ - int playerswhenstarted; - -public: - DDukeMultiplayerBonusScreen(int pws) : DSkippableScreenJob(fadein|fadeout) - { - playerswhenstarted = pws; - } - - void Start() override - { - S_PlayBonusMusic(); - } - - void Draw(double smoothratio) override - { - char tempbuf[32]; - 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); - if (isPlutoPak()) - DrawTexture(twod, tileGetTexture(PLUTOPAKSPRITE+2, true), 260, 36, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, TAG_DONE); - - GameText(160, 58 + 2, GStrings("Multiplayer Totals"), 0, 0); - GameText(160, 58 + 10, currentLevel->DisplayName(), 0, 0); - GameText(160, 165, GStrings("Presskey"), 8 - int(sin(currentclock / 10.) * 8), 0); - - int t = 0; - - MiniText(38, 80, GStrings("Name"), 0, -1, 8); - MiniText(269+20, 80, GStrings("Kills"), 0, 1, 8); - - for (int i = 0; i < playerswhenstarted; i++) - { - mysnprintf(tempbuf, 32, "%-4d", i + 1); - MiniText(92 + (i * 23), 80, tempbuf, 0, -1, 3); - } - - for (int i = 0; i < playerswhenstarted; i++) - { - int xfragtotal = 0; - mysnprintf(tempbuf, 32, "%d", i + 1); - - MiniText(30, 90 + t, tempbuf, 0); - MiniText(38, 90 + t, PlayerName(i), 0, -1, ps[i].palookup); - - for (int y = 0; y < playerswhenstarted; y++) - { - int frag = ps[i].frags[y]; - if (i == y) - { - mysnprintf(tempbuf, 32, "%-4d", ps[y].fraggedself); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0, -1, 2); - xfragtotal -= ps[y].fraggedself; - } - else - { - mysnprintf(tempbuf, 32, "%-4d", frag); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0); - xfragtotal += frag; - } - /* - if (myconnectindex == connecthead) - { - mysnprintf(tempbuf, 32, "stats %ld killed %ld %ld\n", i + 1, y + 1, frag); - sendscore(tempbuf); - } - */ - } - - mysnprintf(tempbuf, 32, "%-4d", xfragtotal); - MiniText(101 + (8 * 23), 90 + t, tempbuf, 0, -1, 2); - - t += 7; - } - - for (int y = 0; y < playerswhenstarted; y++) - { - int yfragtotal = 0; - for (int i = 0; i < playerswhenstarted; i++) - { - if (i == y) - yfragtotal += ps[i].fraggedself; - int frag = ps[i].frags[y]; - yfragtotal += frag; - } - mysnprintf(tempbuf, 32, "%-4d", yfragtotal); - MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0, -1, 2); - } - - MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0, -1, 8); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDukeLevelSummaryScreen : public DScreenJob -{ - const char* lastmapname; - int gfx_offset; - int speech = -1; - int displaystate = 0; - int dukeAnimStart; - - enum - { - printTimeText = 1, - printTimeVal = 2, - printKillsText = 4, - printKillsVal = 8, - printSecretsText = 16, - printSecretsVal = 32, - printStatsAll = 63, - dukeAnim = 64, - dukeWait = 128, - - }; - -public: - DDukeLevelSummaryScreen() : DScreenJob(fadein | fadeout) - { - int vol = volfromlevelnum(currentLevel->levelNumber); - gfx_offset = BONUSSCREEN + ((vol == 1) ? 5 : 0); - lastmapname = currentLevel->DisplayName(); - } - - void FormatTime(int time, char* tempbuf) - { - mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60); - } - - bool OnEvent(event_t* ev) override - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) - { - 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); - GameText(10, 69 + 9, GStrings("TXT_ParTime"), 0); - if (!isNamWW2GI()) - GameText(10, 79 + 9, GStrings("TXT_3DRTIME"), 0); - - if (displaystate & printTimeVal) - { - FormatTime(ps[myconnectindex].player_par, tempbuf); - GameText((320 >> 2) + 71, 59 + 9, tempbuf, 0); - - FormatTime(currentLevel->parTime, tempbuf); - GameText((320 >> 2) + 71, 69 + 9, tempbuf, 0); - - if (!isNamWW2GI()) - { - FormatTime(currentLevel->designerTime, tempbuf); - GameText((320 >> 2) + 71, 79 + 9, tempbuf, 0); - } - } - } - - void PrintKills() - { - char tempbuf[32]; - GameText(10, 94 + 9, GStrings("TXT_EnemiesKilled"), 0); - GameText(10, 104 + 9, GStrings("TXT_EnemiesLeft"), 0); - - if (displaystate & printKillsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed); - GameText((320 >> 2) + 70, 94 + 9, tempbuf, 0); - - if (ud.player_skill > 3) - { - mysnprintf(tempbuf, 32, "%s", GStrings("TXT_N_A")); - } - else - { - if ((ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed) < 0) - mysnprintf(tempbuf, 32, "%-3d", 0); - else mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed); - - } - GameText((320 >> 2) + 70, 104 + 9, tempbuf, 0); - } - } - - void PrintSecrets() - { - char tempbuf[32]; - GameText(10, 119 + 9, GStrings("TXT_SECFND"), 0); - GameText(10, 129 + 9, GStrings("TXT_SECMISS"), 0); - - if (displaystate & printSecretsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms); - GameText((320 >> 2) + 70, 119 + 9, tempbuf, 0); - if (ps[myconnectindex].secret_rooms > 0) - sprintf(tempbuf, "%-3d", (100 * ps[myconnectindex].secret_rooms / ps[myconnectindex].max_secret_rooms)); - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_secret_rooms - ps[myconnectindex].secret_rooms); - GameText((320 >> 2) + 70, 129 + 9, tempbuf, 0); - } - } - - void Draw(double) override - { - twod->ClearScreen(); - 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(ticks * 12 / GameTicRate) * 8), 0); - - if (displaystate & printTimeText) - { - PrintTime(); - } - if (displaystate & printKillsText) - { - PrintKills(); - } - if (displaystate & printSecretsText) - { - PrintSecrets(); - } - - if (displaystate & dukeAnim) - { - switch (((ticks - dukeAnimStart) >> 2) % 15) - { - case 0: - case 1: - case 4: - case 5: - DrawTexture(twod, tileGetTexture(gfx_offset + 3), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - case 2: - case 3: - DrawTexture(twod, tileGetTexture(gfx_offset + 4), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - } - } - else if (!(displaystate & dukeWait)) - { - switch((ticks >> 3) & 3) - { - case 1: - case 3: - DrawTexture(twod, tileGetTexture(gfx_offset + 1), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - case 2: - DrawTexture(twod, tileGetTexture(gfx_offset + 2), 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, TAG_DONE); - break; - } - } - - if (lastmapname) BigText(160, 20 - 6, lastmapname); - BigText(160, 36 - 6, GStrings("Completed")); - } - -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void dobonus_d(int bonusonly, const CompletionFunc& completion) -{ - JobDesc jobs[20]; - int job = 0; - - FX_StopAllSounds(); - - if (bonusonly < 0 && numplayers < 2 && ud.from_bonus == 0) - { - bonussequence_d(volfromlevelnum(currentLevel->levelNumber), jobs, job); - } - else - Mus_Stop(); - - if (playerswhenstarted > 1 && ud.coop != 1) - { - jobs[job++] = { Create(playerswhenstarted) }; - } - else if (bonusonly <= 0 && ud.multimode <= 1) - { - jobs[job++] = { Create() }; - } - if (job) - { - RunScreenJob(jobs, job, completion); - } - else if (completion) completion(false); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void e4intro(const CompletionFunc& completion) -{ - JobDesc jobs[5]; - int job = 0; - - static const AnimSound vol42a[] = - { - { 1, INTRO4_B + 1 }, - { 12, SHORT_CIRCUIT + 1 }, - { 18, INTRO4_5 + 1 }, - { 34, SHORT_CIRCUIT + 1 }, - { -1,-1 } - }; - - static const AnimSound vol41a[] = - { - { 1, INTRO4_1 + 1 }, - { 7, INTRO4_3 + 1 }, - { 12, INTRO4_2 + 1 }, - { 26, INTRO4_4 + 1 }, - { -1,-1 } - }; - - static const AnimSound vol43a[] = - { - { 10, INTRO4_6 + 1 }, - { -1,-1 } - }; - - static const int framespeed_10[] = { 10, 10, 10 }; - static const int framespeed_14[] = { 14, 14, 14 }; - - S_PlaySpecialMusic(MUS_BRIEFING); - jobs[job++] = { PlayVideo("vol41a.anm", vol41a, framespeed_10), nullptr }; - jobs[job++] = { PlayVideo("vol42a.anm", vol42a, framespeed_14), nullptr, true }; - jobs[job++] = { PlayVideo("vol43a.anm", vol43a, framespeed_10), nullptr, true }; - RunScreenJob(jobs, job, completion); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DDukeLoadScreen : public DScreenJob -{ - MapRecord* rec; - -public: - DDukeLoadScreen(MapRecord *maprec) : DScreenJob(0), rec(maprec) {} - - 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()); - } -}; - -void loadscreen_d(MapRecord *rec, CompletionFunc func) -{ - JobDesc job = { Create(rec) }; - RunScreenJob(&job, 1, func); -} - -void PrintPaused_d() -{ - BigText(160, 100, GStrings("Game Paused")); -} - - END_DUKE_NS diff --git a/source/games/duke/src/2d_r.cpp b/source/games/duke/src/2d_r.cpp index d718db4f0..3b84c900b 100644 --- a/source/games/duke/src/2d_r.cpp +++ b/source/games/duke/src/2d_r.cpp @@ -124,549 +124,5 @@ void InitFonts_r() } -//========================================================================== -// -// wrappers around DrawText to allow easier reuse of the old code. -// The vertical displacements are to have the same positioning as with the original code. -// -//========================================================================== - -static void BigText(double x, double y, const char* text, int align, double alpha = 1.) -{ - //x *= 2.2; y *= 2.64; - if (align != -1) - x -= BigFont->StringWidth(text) * (align == 0 ? 0.2 : 0.4); - auto width = BigFont->StringWidth(text); - DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, 0.4, DTA_ScaleY, 0.4, DTA_Alpha, alpha, TAG_DONE); -} - -static void GameText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - x *= 2; y *= 2; - if (align != -1) - x -= SmallFont->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont, CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -static void MiniText(double x, double y, const char* t, int shade, int align = -1, int trans = 0) -{ - x *= 2; y *= 2; - if (align != -1) - x -= SmallFont2->StringWidth(t) * (align == 0 ? 0.5 : 1); - DrawText(twod, SmallFont2, CR_UNDEFINED, x, y, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void Logo_r(const CompletionFunc& completion) -{ - Mus_Stop(); - FX_StopAllSounds(); // JBF 20031228 - - static const AnimSound introsound[] = - { - { 1, 29+1 }, - { -1, -1 } - }; - - static const AnimSound rednecksound[] = - { - { 1, 478+1 }, - { -1, -1 } - }; - - static const AnimSound xatrixsound[] = - { - { 1, 479+1 }, - { -1, -1 } - }; - - static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims - - JobDesc jobs[3]; - int job = 0; - - if (userConfig.nologo) - { - completion(false); - return; - } - else if (!isRRRA()) - { - jobs[job++] = { PlayVideo("rr_intro.anm", introsound, framespeed), nullptr }; - jobs[job++] = { PlayVideo("redneck.anm", rednecksound, framespeed), nullptr }; - jobs[job++] = { PlayVideo("xatlogo.anm", xatrixsound, framespeed), nullptr }; - } - else - { - jobs[job++] = { PlayVideo("redint.mve"), nullptr }; - } - RunScreenJob(jobs, job, completion, true, true); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -static void bonussequence_r(int num, JobDesc* jobs, int& job) -{ - static const AnimSound turdmov[] = - { - { 1, 82 + 1 }, - { -1, -1 } - }; - - static const AnimSound rr_outro[] = - { - { 1, 35 + 1 }, - { -1, -1 } - }; - - static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims - - Mus_Stop(); - FX_StopAllSounds(); - - switch (num) - { - case 0: - jobs[job++] = { PlayVideo("turdmov.anm", turdmov, framespeed), nullptr }; - jobs[job++] = { Create(TENSCREEN), nullptr }; - break; - - case 1: - jobs[job++] = { PlayVideo("rr_outro.anm", rr_outro, framespeed), nullptr }; - jobs[job++] = { Create(TENSCREEN), nullptr }; - break; - - default: - break; - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DRRMultiplayerBonusScreen : public DScreenJob -{ - int playerswhenstarted; - -public: - DRRMultiplayerBonusScreen(int pws) : DScreenJob(fadein | fadeout) - { - playerswhenstarted = pws; - } - - void Start() override - { - S_PlayBonusMusic(); - } - - - void Draw(double) override - { - char tempbuf[32]; - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - double scale = 0.36; - DrawTexture(twod, tileGetTexture(INGAMEDUKETHREEDEE, true), 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffsetRel, true, DTA_ScaleX, scale, DTA_ScaleY, 0.36, TAG_DONE); - - GameText(160, 58, GStrings("Multiplayer Totals"), 0, 0); - GameText(160, 58 + 10, currentLevel->DisplayName(), 0, 0); - GameText(160, 165, GStrings("Presskey"), 0, 0); - - int t = 0; - - MiniText(38, 80, GStrings("Name"), 0); - MiniText(269 + 20, 80, GStrings("Kills"), 0, 1); - - for (int i = 0; i < playerswhenstarted; i++) - { - mysnprintf(tempbuf, 32, "%-4d", i + 1); - MiniText(92 + (i * 23), 80, tempbuf, 0); - } - - for (int i = 0; i < playerswhenstarted; i++) - { - int xfragtotal = 0; - mysnprintf(tempbuf, 32, "%d", i + 1); - - MiniText(30, 90 + t, tempbuf, 0); - MiniText(38, 90 + t, PlayerName(i), 0, -1, ps[i].palookup); - - for (int y = 0; y < playerswhenstarted; y++) - { - int frag = ps[i].frags[y]; - if (i == y) - { - mysnprintf(tempbuf, 32, "%-4d", ps[y].fraggedself); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0); - xfragtotal -= ps[y].fraggedself; - } - else - { - mysnprintf(tempbuf, 32, "%-4d", frag); - MiniText(92 + (y * 23), 90 + t, tempbuf, 0); - xfragtotal += frag; - } - /* - if (myconnectindex == connecthead) - { - mysnprintf(tempbuf, 32, "stats %ld killed %ld %ld\n", i + 1, y + 1, frag); - sendscore(tempbuf); - } - */ - } - - mysnprintf(tempbuf, 32, "%-4d", xfragtotal); - MiniText(101 + (8 * 23), 90 + t, tempbuf, 0); - - t += 7; - } - - for (int y = 0; y < playerswhenstarted; y++) - { - int yfragtotal = 0; - for (int i = 0; i < playerswhenstarted; i++) - { - if (i == y) - yfragtotal += ps[i].fraggedself; - int frag = ps[i].frags[y]; - yfragtotal += frag; - } - mysnprintf(tempbuf, 32, "%-4d", yfragtotal); - MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0); - } - - MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DRRLevelSummaryScreen : public DScreenJob -{ - const char* lastmapname; - int gfx_offset; - int displaystate = 0; - int speech = -1; - int exitSoundStart; - - enum - { - 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) - { - if (currentLevel->flags & MI_USERMAP) - gfx_offset = BONUSPIC01; - else if (!isRRRA()) - gfx_offset = BONUSPIC01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13); - else - gfx_offset = LEVELMAP01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13); - - - lastmapname = currentLevel->DisplayName(); - } - - void FormatTime(int time, char* tempbuf) - { - mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60); - } - - bool OnEvent(event_t* ev) override - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) - { - 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 (displaystate & printTimeVal) - { - FormatTime(ps[myconnectindex].player_par, tempbuf); - BigText(191, 48, tempbuf, -1); - - FormatTime(currentLevel->parTime, tempbuf); - BigText(191, 64, tempbuf, -1); - - FormatTime(currentLevel->designerTime, tempbuf); - BigText(191, 80, tempbuf, -1); - } - } - - void PrintKills() - { - char tempbuf[32]; - BigText(30, 112, GStrings("TXT_VarmintsKilled"), -1); - BigText(30, 128, GStrings("TXT_VarmintsLeft"), -1); - - if (displaystate & printKillsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed); - BigText(231, 112, tempbuf, -1); - if (ud.player_skill > 3) - { - mysnprintf(tempbuf, 32, "%s", GStrings("TXT_N_A")); - BigText(231, 128, tempbuf, -1); - } - else - { - if ((ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed) < 0) - mysnprintf(tempbuf, 32, "%-3d", 0); - else mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed); - BigText(231, 128, tempbuf, -1); - } - } - } - - void PrintSecrets() - { - char tempbuf[32]; - BigText(30, 144, GStrings("TXT_SECFND"), -1); - BigText(30, 160, GStrings("TXT_SECMISS"), -1); - - if (displaystate & printSecretsVal) - { - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms); - BigText(231, 144, tempbuf, -1); - if (ps[myconnectindex].secret_rooms > 0) - sprintf(tempbuf, "%-3d", (100 * ps[myconnectindex].secret_rooms / ps[myconnectindex].max_secret_rooms)); - mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_secret_rooms - ps[myconnectindex].secret_rooms); - BigText(231, 160, tempbuf, -1); - } - } - - void Draw(double) override - { - twod->ClearScreen(); - 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 (displaystate & printTimeText) - { - PrintTime(); - } - if (displaystate & printKillsText) - { - PrintKills(); - } - if (displaystate & printSecretsText) - { - PrintSecrets(); - } - } - -}; - - -class DRRRAEndOfGame : public DSkippableScreenJob -{ -public: - DRRRAEndOfGame() : DSkippableScreenJob(fadein|fadeout) - { - } - - void Skipped() override - { - S_StopSound(35); - } - - void Start() override - { - S_PlaySound(35, CHAN_AUTO, CHANF_UI); - } - - void OnTick() override - { - 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); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void dobonus_r(int bonusonly, const CompletionFunc& completion) -{ - JobDesc jobs[20]; - int job = 0; - - FX_StopAllSounds(); - Mus_Stop(); - - if (bonusonly < 0 && !isRRRA() && numplayers < 2 && ud.from_bonus == 0) - { - int vol = volfromlevelnum(currentLevel->levelNumber); - bonussequence_r(vol, jobs, job); - } - - if (playerswhenstarted > 1 && ud.coop != 1) - { - jobs[job++] = { Create(playerswhenstarted) }; - } - else if (bonusonly <= 0 && ud.multimode <= 1) - { - if (isRRRA() && !(currentLevel->flags & MI_USERMAP) && currentLevel->levelNumber < 106) // fixme: The logic here is awful. Shift more control to the map records. - { - jobs[job++] = { Create(true) }; - int levnum = clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13); - char fn[20]; - mysnprintf(fn, 20, "lvl%d.anm", levnum + 1); - static const int framespeed[] = { 20, 20, 7200 }; // wait for one minute on the final frame so that the video doesn't stop before the user notices. - jobs[job++] = { PlayVideo(fn, nullptr, framespeed) }; - if (bonusonly < 0 && currentLevel->levelNumber > 100) - { - jobs[job++] = { Create() }; - } - } - else jobs[job++] = { Create(false) }; - } - if (job) - RunScreenJob(jobs, job, completion); - else if (completion) completion(false); -} - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DRRLoadScreen : public DScreenJob -{ - MapRecord* rec; - -public: - DRRLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {} - - 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); - } -}; - -void loadscreen_r(MapRecord* rec, CompletionFunc func) -{ - JobDesc job = { Create(rec) }; - RunScreenJob(&job, 1, func); -} - -void PrintPaused_r() -{ - BigText(160, 100, GStrings("Game Paused"), 0); -} - END_DUKE_NS diff --git a/source/games/duke/src/actors_d.cpp b/source/games/duke/src/actors_d.cpp index 10ad5dee0..0962311d8 100644 --- a/source/games/duke/src/actors_d.cpp +++ b/source/games/duke/src/actors_d.cpp @@ -3697,7 +3697,7 @@ void moveeffectors_d(void) //STATNUM 3 { static int16_t list1[] = { BLOODPOOL, PUKE, FOOTPRINTS, FOOTPRINTS2, FOOTPRINTS3, FOOTPRINTS4, BULLETHOLE, BLOODSPLAT1, BLOODSPLAT2, BLOODSPLAT3, BLOODSPLAT4, -1 }; static int16_t list2[] = { BOLT1, BOLT1 + 1,BOLT1 + 2, BOLT1 + 3, SIDEBOLT1, SIDEBOLT1 + 1, SIDEBOLT1 + 2, SIDEBOLT1 + 3, -1 }; - handle_se24(act, list1, list2, false, TRIPBOMB, LASERLINE, CRANE, 2); + handle_se24(act, list1, list2, true, TRIPBOMB, LASERLINE, CRANE, 2); break; } case SE_35: diff --git a/source/games/duke/src/cheats.cpp b/source/games/duke/src/cheats.cpp index 6c7f67bc3..c76f8c96e 100644 --- a/source/games/duke/src/cheats.cpp +++ b/source/games/duke/src/cheats.cpp @@ -292,11 +292,11 @@ static bool cheatItems(int player) static bool cheatLevel(cheatseq_t *s) { int volnume,levnume; - volnume = s->Args[0] - '0' - 1; - levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1; + volnume = s->Args[0] - '0'; + levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0'); // Instead of hard coded range checks on volume and level, let's just check if the level is defined. - auto map = FindMapByLevelNum(levelnum(volnume, levnume)); + auto map = FindMapByIndex(volnume, levnume); if (map) { ChangeLevel(map, -1); diff --git a/source/games/duke/src/d_menu.cpp b/source/games/duke/src/d_menu.cpp index 879597e58..343483ee3 100644 --- a/source/games/duke/src/d_menu.cpp +++ b/source/games/duke/src/d_menu.cpp @@ -97,19 +97,8 @@ bool GameInterface::CanSave() bool GameInterface::StartGame(FNewGameStartup& gs) { - if (gs.Episode >= 1) - { - if (g_gameType & GAMEFLAG_SHAREWARE) - { - M_StartMessage(GStrings("BUYDUKE"), 1, NAME_None); - return false; - } - } - int32_t skillsound = PISTOL_BODYHIT; - soundEngine->StopAllChannels(); - static const short sounds_d[] = { JIBBED_ACTOR6, BONUS_SPEECH1, DUKE_GETWEAPON2, JIBBED_ACTOR5, JIBBED_ACTOR5 }; static const short sounds_r[] = { 427, 428, 196, 195, 197 }; if (gs.Skill >=0 && gs.Skill <= 5) skillsound = isRR()? sounds_r[gs.Skill] : sounds_d[gs.Skill]; @@ -126,15 +115,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs) } Net_ClearFifo(); } - - auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level)); - if (map) - { - DeferedStartGame(map, gs.Skill); - return true; - } - return false; - + return true; } FSavegameInfo GameInterface::GetSaveSig() diff --git a/source/games/duke/src/dispatch.cpp b/source/games/duke/src/dispatch.cpp index aaa1260a8..39df23122 100644 --- a/source/games/duke/src/dispatch.cpp +++ b/source/games/duke/src/dispatch.cpp @@ -105,12 +105,8 @@ void think_r(); void animatesprites_d(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio); void animatesprites_r(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio); -void Logo_d(const CompletionFunc&); -void Logo_r(const CompletionFunc&); void InitFonts_d(); void InitFonts_r(); -void PrintPaused_d(); -void PrintPaused_r(); Dispatcher fi; @@ -120,9 +116,7 @@ void SetDispatcher() if (!isRR()) { fi = { - Logo_d, InitFonts_d, - PrintPaused_d, think_d, initactorflags_d, @@ -167,9 +161,7 @@ void SetDispatcher() else { fi = { - Logo_r, InitFonts_r, - PrintPaused_r, think_r, initactorflags_r, @@ -235,8 +227,6 @@ int TILE_STATIC; int TILE_BOTTOMSTATUSBAR; int TILE_THREEDEE; int TILE_INGAMEDUKETHREEDEE; -int TILE_PLUTOPAKSPRITE; -int TILE_MENUBAR; int TILE_ATOMICHEALTH; int TILE_FLOORSLIME; int TILE_JIBS6; diff --git a/source/games/duke/src/duke3d.h b/source/games/duke/src/duke3d.h index 8bf76282a..96c9ce5b9 100644 --- a/source/games/duke/src/duke3d.h +++ b/source/games/duke/src/duke3d.h @@ -76,9 +76,7 @@ struct GameInterface : public ::GameInterface struct Dispatcher { // global stuff - void (*ShowLogo)(const CompletionFunc& completion); void (*InitFonts)(); - void (*PrintPaused)(); // sectors_?.cpp void (*think)(); diff --git a/source/games/duke/src/flags_d.cpp b/source/games/duke/src/flags_d.cpp index 28b50b056..4a7029751 100644 --- a/source/games/duke/src/flags_d.cpp +++ b/source/games/duke/src/flags_d.cpp @@ -261,10 +261,6 @@ void initactorflags_d() TILE_CAMLIGHT = CAMLIGHT; TILE_STATIC = STATIC; TILE_BOTTOMSTATUSBAR = isWorldTour()? WIDESCREENSTATUSBAR : BOTTOMSTATUSBAR; - TILE_THREEDEE = THREEDEE; - TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE; - TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE; - TILE_MENUBAR = MENUBAR; TILE_ATOMICHEALTH = ATOMICHEALTH; TILE_FLOORSLIME = FLOORSLIME; TILE_JIBS6 = JIBS6; diff --git a/source/games/duke/src/flags_r.cpp b/source/games/duke/src/flags_r.cpp index e609c925f..04ef28b6e 100644 --- a/source/games/duke/src/flags_r.cpp +++ b/source/games/duke/src/flags_r.cpp @@ -235,10 +235,6 @@ void initactorflags_r() TILE_CAMLIGHT = CAMLIGHT; TILE_STATIC = STATIC; TILE_BOTTOMSTATUSBAR = BOTTOMSTATUSBAR; - TILE_THREEDEE = THREEDEE; - TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE; - TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE; - TILE_MENUBAR = MENUBAR; TILE_ATOMICHEALTH = ATOMICHEALTH; TILE_FLOORSLIME = FLOORSLIME; TILE_JIBS6 = JIBS6; diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h index 1785fabce..64b9199f6 100644 --- a/source/games/duke/src/funct.h +++ b/source/games/duke/src/funct.h @@ -162,9 +162,6 @@ int hits(DDukeActor* snum); DDukeActor* LocateTheLocator(int n, int sectnum); void clearcamera(player_struct* ps); -void showtwoscreens(const CompletionFunc& func); -void doorders(const CompletionFunc& func); - void LoadActor(DDukeActor* i, int p, int x); void execute(DDukeActor* s, int p, int d); void makeitfall(DDukeActor* s); @@ -211,8 +208,6 @@ void OffBoat(player_struct *pl); void cameratext(DDukeActor* i); void dobonus(int bonusonly, const CompletionFunc& completion); -void dobonus_d(int bonusonly, const CompletionFunc& completion); -void dobonus_r(int bonusonly, const CompletionFunc& completion); void drawoverlays(double smoothratio); void drawbackground(void); @@ -227,7 +222,6 @@ void e4intro(const CompletionFunc& completion); void exitlevel(MapRecord *next); void enterlevel(MapRecord* mi, int gm); void donewgame(MapRecord* map, int sk); -void startnewgame(MapRecord* map, int skill); int playercolor2lookup(int color); void PlayerColorChanged(void); bool movementBlocked(player_struct *p); diff --git a/source/games/duke/src/game_misc.cpp b/source/games/duke/src/game_misc.cpp index 2122389dc..89e32c0af 100644 --- a/source/games/duke/src/game_misc.cpp +++ b/source/games/duke/src/game_misc.cpp @@ -92,13 +92,12 @@ static void endthegame(bool) void GameInterface::ExitFromMenu() { +#if 0 + // do we really need this scoreboard stuff here? auto runbonus = [=](auto completion) { // MP scoreboard - if (playerswhenstarted > 1 && !ud.coop) - { - dobonus(1, completion); - } + if (playerswhenstarted > 1 && !ud.coop) ShowScoreboard(playerswhenstarted); else completion(false); }; @@ -106,11 +105,16 @@ void GameInterface::ExitFromMenu() { // shareware and TEN screens if (isShareware() && !isRR()) - showtwoscreens(completion); + StartCutscene("DukeCutscenes.BuildSharewareExit", 0, completion); else completion(false); }; runbonus([=](bool aborted) { runtwoscreens(endthegame); }); +#else + if (isShareware() && !isRR()) + StartCutscene("DukeCutscenes.BuildSharewareExit", 0, endthegame); + else endthegame(false); +#endif } //--------------------------------------------------------------------------- @@ -297,7 +301,13 @@ void drawoverlays(double smoothratio) } if (paused == 2) - fi.PrintPaused(); + { + double x = 160, y = 100; + double scale = isRR() ? 0.4 : 1.; + const char* text = GStrings("Game Paused"); + x -= BigFont->StringWidth(text) * 0.5 * scale; + DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE); + } } @@ -342,18 +352,6 @@ void cameratext(DDukeActor *cam) // //--------------------------------------------------------------------------- -void dobonus(int bonusonly, const CompletionFunc& completion) -{ - if (isRR()) dobonus_r(bonusonly, completion); - else dobonus_d(bonusonly, completion); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - int startrts(int lumpNum, int localPlayer) { if (SoundEnabled() && diff --git a/source/games/duke/src/gamedef.cpp b/source/games/duke/src/gamedef.cpp index 830e83496..51c912959 100644 --- a/source/games/duke/src/gamedef.cpp +++ b/source/games/duke/src/gamedef.cpp @@ -48,11 +48,12 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "conlabeldef.h" #include "gi.h" +extern TArray> mapList; + BEGIN_DUKE_NS enum { VERSIONCHECK = 41 }; - //--------------------------------------------------------------------------- // // definitions needed by the parser. @@ -88,7 +89,8 @@ public: struct TempMusic { - int levnum; + int volnum; + int levlnum; FString music; }; @@ -1016,7 +1018,8 @@ int ConCompiler::parsecommand() if (k >= 0) { tempMusic.Reserve(1); - tempMusic.Last().levnum = levelnum(k, i); + tempMusic.Last().volnum = k + 1; + tempMusic.Last().levlnum = i + 1; tempMusic.Last().music = parsebuffer.Data(); } else @@ -1644,6 +1647,7 @@ int ConCompiler::parsecommand() return 0; case concmd_definevolumename: + { popscriptvalue(); transnum(LABEL_DEFINE); j = popscriptvalue(); @@ -1658,8 +1662,13 @@ int ConCompiler::parsecommand() textptr++, i++; } parsebuffer.Push(0); - gVolumeNames[j] = FStringTable::MakeMacro(parsebuffer.Data(), i); + // We need both a volume and a cluster for this new episode. + auto vol = MustFindVolume(j); + auto clust = MustFindCluster(j + 1); + vol->name = clust->name = FStringTable::MakeMacro(parsebuffer.Data(), i); + if (j > 0) vol->flags |= VF_SHAREWARELOCK; return 0; + } case concmd_defineskillname: popscriptvalue(); transnum(LABEL_DEFINE); @@ -1695,25 +1704,32 @@ int ConCompiler::parsecommand() textptr++, i++; } parsebuffer.Push(0); - auto levnum = levelnum(j, k); - auto map = FindMapByLevelNum(levnum); + auto map = FindMapByIndexOnly(j + 1, k + 1); if (!map) map = AllocateMap(); map->SetFileName(parsebuffer.Data()); + if (k == 0) + { + auto vol = MustFindVolume(j); + vol->startmap = map->labelName; + } while (*textptr == ' ' || *textptr == '\t') textptr++; map->parTime = - (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) + - (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26); + (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) + + (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0'))); textptr += 5; while (*textptr == ' ' || *textptr == '\t') textptr++; map->designerTime = - (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) + - (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26); + (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) + + (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0'))); - map->levelNumber = levnum; + SetLevelNum(map, makelevelnum(j + 1, k + 1)); + + map->mapindex = k + 1; + map->cluster = j + 1; textptr += 5; while (*textptr == ' ' || *textptr == '\t') textptr++; @@ -3132,7 +3148,7 @@ void ConCompiler::setmusic() { for (auto& tm : tempMusic) { - auto map = FindMapByLevelNum(tm.levnum); + auto map = FindMapByIndexOnly(tm.volnum, tm.levlnum); if (map) map->music = tm.music; } tempMusic.Clear(); @@ -3176,6 +3192,12 @@ void loadcons() ScriptCode.Push(0); ConCompiler comp; + + if (fileSystem.FileExists("engine/engine.con")) + { + comp.compilecon("engine/engine.con"); + } + comp.compilecon(ConFile()); //Tokenize if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get()) @@ -3206,45 +3228,38 @@ void loadcons() InitGameVarPointers(); ResetSystemDefaults(); S_WorldTourMappingsForOldSounds(); // create a sound mapping for World Tour. + S_CacheAllSounds(); + comp.setmusic(); + + // RR must link the last map of E1 to the first map of E2. + if (isRR()) for (auto& map : mapList) + { + if (map->cluster == 1) + { + if (!FindMapByIndexOnly(map->cluster, map->mapindex + 1)) + { + auto nextmap = FindMapByIndexOnly(map->cluster + 1, 1); + if (nextmap) + { + map->NextMap = nextmap->labelName; + map->flags |= LEVEL_FORCENOEOG; + } + } + } + } + if (isWorldTour()) { + // fix broken secret exit in WT's super secret map. + // This cannot be done from an RMAPINFO definition because the conditions are too specific and must not override custom maps. int num = fileSystem.CheckNumForName("e1l7.map"); int file = fileSystem.GetFileContainer(num); if (file <= fileSystem.GetMaxIwadNum()) { auto maprec = FindMapByName("e1l7"); - if (maprec) maprec->nextLevel = levelnum(0, 4); + if (maprec) maprec->NextMap = "e1l5"; } } - else if (isRRRA()) - { - // RRRA goes directly to the second episode after E1L7 to continue the game. - int num = fileSystem.CheckNumForName("e1l7.map"); - int file = fileSystem.GetFileContainer(num); - if (file <= fileSystem.GetMaxIwadNum()) - { - auto maprec = FindMapByName("e1l7"); - if (maprec) maprec->nextLevel = levelnum(1, 0); - } - } - else if (isRR()) - { - // RR does not define its final level and crudely hacked it into the progression. This puts it into the E2L8 slot so that the game can naturally progress there. - auto maprec1 = FindMapByLevelNum(levelnum(1, 6)); - auto maprec2 = FindMapByLevelNum(levelnum(1, 7)); - auto maprec3 = FindMapByName("endgame"); - int num3 = fileSystem.FindFile("endgame.map"); - if (maprec1 && !maprec2 && !maprec3 && num3 >= 0) - { - auto maprec = AllocateMap(); - maprec->designerTime = 0; - maprec->parTime = 0; - maprec->SetFileName("endgame.map"); - maprec->SetName("$TXT_CLOSEENCOUNTERS"); - maprec->levelNumber = levelnum(1, 7); - } - } - comp.setmusic(); } END_DUKE_NS diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp index 73edeb5fa..1a9b7d754 100644 --- a/source/games/duke/src/gameexec.cpp +++ b/source/games/duke/src/gameexec.cpp @@ -130,11 +130,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer); break; - case USERDEFS_FROM_BONUS: - if (bSet) ud.from_bonus = lValue; - else SetGameVarID(lVar2, ud.from_bonus, sActor, sPlayer); - break; - case USERDEFS_CAMERASPRITE: if (bSet) ud.cameraactor = ScriptIndexToActor(lValue); else SetGameVarID(lVar2, ActorToScriptIndex(ud.cameraactor), sActor, sPlayer); @@ -239,14 +234,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor* else SetGameVarID(lVar2, ud.player_skill, sActor, sPlayer); break; - case USERDEFS_LEVEL_NUMBER: - if (!bSet) SetGameVarID(lVar2, mapfromlevelnum(currentLevel->levelNumber), sActor, sPlayer); - break; - - case USERDEFS_VOLUME_NUMBER: - if (!bSet) SetGameVarID(lVar2, volfromlevelnum(currentLevel->levelNumber), sActor, sPlayer); - break; - case USERDEFS_MARKER: if (bSet) ud.marker = lValue; else SetGameVarID(lVar2, ud.marker, sActor, sPlayer); @@ -3455,7 +3442,7 @@ int ParseState::parse(void) insptr++; // skip command volnume = GetGameVarID(*insptr++, g_ac, g_p); levnume = GetGameVarID(*insptr++, g_ac, g_p); - auto level = FindMapByLevelNum(levelnum(volnume - 1, levnume - 1)); + auto level = FindMapByIndex(volnume, levnume); if (level != nullptr) ChangeLevel(level, -1); break; @@ -3572,7 +3559,7 @@ int ParseState::parse(void) { insptr++; int music_select = *insptr++; - auto level = FindMapByLevelNum(levelnum(currentLevel->levelNumber, music_select)); + auto level = FindMapByIndex(currentLevel->cluster, music_select+1); // this was 0-based in EDuke 2.0... if (level) S_PlayLevelMusic(level); break; } diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp index 3b5791477..492dddf95 100644 --- a/source/games/duke/src/gameloop.cpp +++ b/source/games/duke/src/gameloop.cpp @@ -113,29 +113,8 @@ void GameInterface::Ticker() void GameInterface::Startup() { - ps[myconnectindex].ftq = 0; - - if (userConfig.CommandMap.IsNotEmpty()) - { - auto maprecord = FindMapByName(userConfig.CommandMap); - userConfig.CommandMap = ""; - if (maprecord) - { - ud.m_respawn_monsters = ud.player_skill == 4; - - for (int i = 0; i != -1; i = connectpoint2[i]) - { - resetweapons(i); - resetinventory(i); - } - startnewgame(maprecord, /*userConfig.skill*/2); - } - } - else - { - if (!userConfig.nologo) fi.ShowLogo([](bool) { gameaction = ga_mainmenunostopsound; }); - else gameaction = ga_mainmenunostopsound; - } + ps[myconnectindex].ftq = 0; + PlayLogos(ga_mainmenunostopsound, ga_mainmenunostopsound, false); } //--------------------------------------------------------------------------- @@ -160,20 +139,9 @@ void GameInterface::Render() // // //--------------------------------------------------------------------------- -void loadscreen_d(MapRecord* rec, CompletionFunc func); -void loadscreen_r(MapRecord* rec, CompletionFunc func); void GameInterface::NextLevel(MapRecord* map, int skill) { -#if 0 - // Loading is so fast on modern system so that this only serves as an irritant, not an asset. - auto loadscreen = isRR() ? loadscreen_r : loadscreen_d; - loadscreen_d(map, [=](bool) - { - enterlevel(map, 0); - gameaction = ga_level; - }); -#endif enterlevel(map, 0); } @@ -183,23 +151,6 @@ void GameInterface::NextLevel(MapRecord* map, int skill) // //--------------------------------------------------------------------------- -void GameInterface::NewGame(MapRecord* map, int skill, bool) -{ - // Hmm... What about the other players? - ps[0].last_extra = gs.max_player_health; - resetweapons(0); - resetinventory(0); - if (skill != -1) skill = skill + 1; - - startnewgame(map, skill); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - void GameInterface::LevelCompleted(MapRecord* map, int skill) { exitlevel(map); diff --git a/source/games/duke/src/gamevar.cpp b/source/games/duke/src/gamevar.cpp index 839092c33..95a031ab3 100644 --- a/source/games/duke/src/gamevar.cpp +++ b/source/games/duke/src/gamevar.cpp @@ -555,9 +555,9 @@ void InitGameVarPointers(void) // //--------------------------------------------------------------------------- -// These are deliberately not stored in accessible variables anymore -int getmap() { return mapfromlevelnum(currentLevel->levelNumber); } -int getvol() { return volfromlevelnum(currentLevel->levelNumber); } +// These are deliberately not stored in accessible variables anymore. Use is deprecated. +int getmap() { return currentLevel->levelNumber; } +int getvol() { return currentLevel->cluster; } void AddSystemVars() { diff --git a/source/games/duke/src/inlines.h b/source/games/duke/src/inlines.h index 38ed67616..59bc1c057 100644 --- a/source/games/duke/src/inlines.h +++ b/source/games/duke/src/inlines.h @@ -164,7 +164,7 @@ inline float PlayerInputAngVel(int pl) return ps[pl].sync.avel; } -inline float PlayerHorizon(int pl) +inline float GetPlayerHorizon(int pl) { return ps[pl].sync.horz; } diff --git a/source/games/duke/src/namelist_d.h b/source/games/duke/src/namelist_d.h index e098e67b4..d05332000 100644 --- a/source/games/duke/src/namelist_d.h +++ b/source/games/duke/src/namelist_d.h @@ -517,6 +517,7 @@ x(THREEDEE, 2498) x(INGAMEDUKETHREEDEE, 2499) x(TENSCREEN, 2500) x(PLUTOPAKSPRITE, 2501) +x(TITLEPLUTOPAKSPRITE, 2502) x(MENUPLUTOPAKSPRITE, 2503) x(CREDITPAGE1, 2504) x(CREDITPAGE2, 2505) @@ -587,11 +588,33 @@ x(RESPAWNMARKERRED, 3190) x(RESPAWNMARKERYELLOW, 3200) x(RESPAWNMARKERGREEN, 3210) x(BONUSSCREEN, 3240) +x(BONUSSCREEN_O1, 3241) +x(BONUSSCREEN_O2, 3242) +x(BONUSSCREEN_O3, 3243) +x(BONUSSCREEN_O4, 3244) +x(BONUSSCREEN2, 3245) +x(BONUSSCREEN2_O1, 3246) +x(BONUSSCREEN2_O2, 3247) +x(BONUSSCREEN2_O3, 3248) +x(BONUSSCREEN2_O4, 3249) x(VIEWBORDER, 3250) x(VICTORY1, 3260) +x(VICTORY2, 3261) +x(VICTORY3, 3262) +x(VICTORY4, 3263) +x(VICTORY5, 3264) +x(VICTORY6, 3265) +x(VICTORY7, 3266) +x(VICTORY8, 3267) +x(VICTORY9, 3268) x(ORDERING, 3270) +x(ORDERING1, 3271) +x(ORDERING2, 3272) +x(ORDERING3, 3273) x(TEXTSTORY, 3280) x(LOADSCREEN, 3281) +x(SWEXIT2, 3290) +x(SWEXIT1, 3291) x(E1ENDSCREEN, 3292) x(E2ENDSCREEN, 3293) x(BORNTOBEWILDSCREEN, 3370) diff --git a/source/games/duke/src/namelist_r.h b/source/games/duke/src/namelist_r.h index 9429bf35c..46696dd5e 100644 --- a/source/games/duke/src/namelist_r.h +++ b/source/games/duke/src/namelist_r.h @@ -1241,6 +1241,7 @@ y(RRTILE8640, 8640) y(RRTILE8651, 8651) y(RRTILE8660, 8660) x(ENDGAME, 8677) +x(ENDGAME2, 8678) y(RRTILE8679, 8679) y(RRTILE8680, 8680) y(RRTILE8681, 8681) diff --git a/source/games/duke/src/names.h b/source/games/duke/src/names.h index 2318c9bd1..e29e19ba6 100644 --- a/source/games/duke/src/names.h +++ b/source/games/duke/src/names.h @@ -22,10 +22,6 @@ extern int TILE_CAMCORNER; extern int TILE_CAMLIGHT; extern int TILE_STATIC; extern int TILE_BOTTOMSTATUSBAR; -extern int TILE_THREEDEE; -extern int TILE_INGAMEDUKETHREEDEE; -extern int TILE_PLUTOPAKSPRITE; -extern int TILE_MENUBAR; extern int TILE_ATOMICHEALTH; extern int TILE_FLOORSLIME; extern int TILE_JIBS6; diff --git a/source/games/duke/src/player_d.cpp b/source/games/duke/src/player_d.cpp index 37b61dfad..856703d6d 100644 --- a/source/games/duke/src/player_d.cpp +++ b/source/games/duke/src/player_d.cpp @@ -2630,27 +2630,6 @@ static void processweapon(int snum, ESyncBits actions, int psect) auto s = pact->s; int shrunk = (s->yrepeat < 32); - // Set maximum for pistol slightly higher if playing with `cl_showmagamount 1`. - if (!cl_showmagamt) - { - if (p->ammo_amount[PISTOL_WEAPON] > PISTOL_MAXDEFAULT) - p->ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT; - - if (gs.max_ammo_amount[PISTOL_WEAPON] != PISTOL_MAXDEFAULT) - gs.max_ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT; - } - else - { - short pistolAddition = 4; - short pistolNewMaximum = PISTOL_MAXDEFAULT + pistolAddition; - - if (p->ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT && gs.max_ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT) - p->ammo_amount[PISTOL_WEAPON] += pistolAddition; - - if (gs.max_ammo_amount[PISTOL_WEAPON] != pistolNewMaximum) - gs.max_ammo_amount[PISTOL_WEAPON] = pistolNewMaximum; - } - if (isNamWW2GI() && (actions & SB_HOLSTER)) // 'Holster Weapon { if (isWW2GI()) @@ -3138,7 +3117,7 @@ HORIZONLY: if (SyncInput()) { - p->horizon.applyinput(PlayerHorizon(snum), &actions); + p->horizon.applyinput(GetPlayerHorizon(snum), &actions); } p->checkhardlanding(); diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp index 2e7b11ec8..0c2befe90 100644 --- a/source/games/duke/src/player_r.cpp +++ b/source/games/duke/src/player_r.cpp @@ -1464,26 +1464,8 @@ int doincrements_r(struct player_struct* p) { if (!wupass) { - short snd; + int snd = currentLevel->rr_startsound ? currentLevel->rr_startsound : 391; wupass = 1; - switch (currentLevel->levelNumber) - { - default: snd = 391; break; - case levelnum(0, 0): snd = isRRRA() ? 63 : 391; break; - case levelnum(0, 1): snd = 64; break; - case levelnum(0, 2): snd = 77; break; - case levelnum(0, 3): snd = 80; break; - case levelnum(0, 4): snd = 102; break; - case levelnum(0, 5): snd = 103; break; - case levelnum(0, 6): snd = 104; break; - case levelnum(1, 0): snd = 105; break; - case levelnum(1, 1): snd = 176; break; - case levelnum(1, 2): snd = 177; break; - case levelnum(1, 3): snd = 198; break; - case levelnum(1, 4): snd = 230; break; - case levelnum(1, 5): snd = 255; break; - case levelnum(1, 6): snd = 283; break; - } S_PlayActorSound(snd, pact); } else if (PlayClock > 1024) @@ -3399,8 +3381,7 @@ void processinput_r(int snum) psectlotag = 2; } } - else if (psectlotag == 7777) - if (currentLevel->levelNumber == levelnum(1, 6)) + else if (psectlotag == 7777 && (currentLevel->gameflags & LEVEL_RR_HULKSPAWN)) lastlevel = 1; if (psectlotag == 848 && sector[psect].floorpicnum == WATERTILE2) @@ -4000,7 +3981,7 @@ HORIZONLY: if (SyncInput()) { - p->horizon.applyinput(PlayerHorizon(snum), &actions); + p->horizon.applyinput(GetPlayerHorizon(snum), &actions); } p->checkhardlanding(); diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp index 77571074b..af1e757fd 100644 --- a/source/games/duke/src/premap.cpp +++ b/source/games/duke/src/premap.cpp @@ -36,6 +36,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "automap.h" #include "dukeactor.h" #include "interpolate.h" +#include "precache.h" #include "render.h" BEGIN_DUKE_NS @@ -141,7 +142,7 @@ void resetplayerstats(int snum) p->jetpack_on = 0; p->holoduke_on = nullptr; - p->angle.olook_ang = p->angle.look_ang = buildang(512 - ((currentLevel->levelNumber & 1) << 10)); + p->angle.olook_ang = p->angle.look_ang = buildang(512 - (((~currentLevel->levelNumber) & 1) << 10)); p->angle.orotscrnang = p->angle.rotscrnang = buildang(0); p->newOwner =nullptr; @@ -659,7 +660,6 @@ void prelevel_common(int g) p->SlotWin = 0; enemysizecheat = 0; p->MamaEnd = 0; - mamaspawn_count = 15; banjosound = 0; RRRA_ExitedLevel = 0; @@ -672,7 +672,7 @@ void prelevel_common(int g) WindDir = 0; fakebubba_spawn = 0; RRRA_ExitedLevel = 0; - mamaspawn_count = 15; + mamaspawn_count = currentLevel->rr_mamaspawn; BellTime = 0; BellSprite = nullptr; @@ -754,7 +754,6 @@ void donewgame(MapRecord* map, int sk) auto p = &ps[0]; show_shareware = 26 * 34; - //ud.nextLevel = map; ud.player_skill = sk; ud.secretlevel = 0; ud.from_bonus = 0; @@ -813,33 +812,6 @@ void donewgame(MapRecord* map, int sk) } } -template -void newgame(MapRecord* map, int sk, func completion) -{ - auto completion1 = [=](bool res) - { - if (!isRR() && map->levelNumber == levelnum(3, 0) && (ud.multimode < 2)) - { - e4intro([=](bool) { donewgame(map, sk); completion(res); }); - } - else - { - donewgame(map, sk); - completion(res); - } - }; - - if (ud.m_recstat != 2 && ud.last_level >= 0 && ud.multimode > 1 && ud.coop != 1) - dobonus(1, completion1); - -#if 0 // this is one lousy hack job that's hopefully not needed anymore. - else if (isRR() && !isRRRA() && map->levelNumber == levelnum(0, 6)) - dobonus(0, completion1); -#endif - - else completion1(false); -} - //--------------------------------------------------------------------------- // // the setup here is very, very sloppy, because mappings are not 1:1. @@ -978,12 +950,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode) SECRET_SetMapName(mi->DisplayName(), mi->name); STAT_NewLevel(mi->fileName); - if (isRR() && !isRRRA() && mi->levelNumber == levelnum(1, 1)) - { - for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++) - ps[0].ammo_amount[i] = 0; - ps[0].gotweapon.Clear(KNEE_WEAPON); - } p->angle.ang = buildang(lbang); memset(gotpic, 0, sizeof(gotpic)); @@ -993,16 +959,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode) SpawnPortals(); - if (isRRRA() && mi->levelNumber == levelnum(2, 0)) - { - for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++) - ps[0].ammo_amount[i] = 0; - ps[0].gotweapon.Clear(KNEE_WEAPON); - ps[0].gotweapon.Set(SLINGBLADE_WEAPON); - ps[0].ammo_amount[SLINGBLADE_WEAPON] = 1; - ps[0].curr_weapon = SLINGBLADE_WEAPON; - } - allignwarpelevators(); resetpspritevars(gamemode); @@ -1047,7 +1003,6 @@ void enterlevel(MapRecord *mi, int gamemode) OnEvent(EVENT_ENTERLEVEL); // Stop all sounds - S_ResumeSound(false); FX_StopAllSounds(); FX_SetReverb(0); @@ -1064,27 +1019,32 @@ void enterlevel(MapRecord *mi, int gamemode) S_PlayLevelMusic(mi); } - if (isShareware() && mi->levelNumber == 0 && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]); - for (int i = connecthead; i >= 0; i = connectpoint2[i]) { + bool clearweapon = !!(currentLevel->flags & LEVEL_CLEARWEAPONS); int pn = sector[ps[i].GetActor()->s->sectnum].floorpicnum; if (pn == TILE_HURTRAIL || pn == TILE_FLOORSLIME || pn == TILE_FLOORPLASMA) { - resetweapons(i); resetinventory(i); + clearweapon = true; + } + if (clearweapon) + { + resetweapons(i); ps[i].gotweapon.Clear(PISTOL_WEAPON); ps[i].ammo_amount[PISTOL_WEAPON] = 0; ps[i].curr_weapon = KNEE_WEAPON; + ps[i].kickback_pic = 0; ps[i].okickback_pic = ps[i].kickback_pic = 0; } + if (currentLevel->flags & LEVEL_CLEARINVENTORY) resetinventory(i); } resetmys(); everyothertime = 0; global_random = 0; - ud.last_level = currentLevel->levelNumber; + ud.last_level = 1; ps[myconnectindex].over_shoulder_on = 0; clearfrags(); resettimevars(); // Here we go @@ -1105,9 +1065,19 @@ void enterlevel(MapRecord *mi, int gamemode) // //--------------------------------------------------------------------------- -void startnewgame(MapRecord* map, int skill) +void GameInterface::NewGame(MapRecord* map, int skill, bool) { + for (int i = 0; i != -1; i = connectpoint2[i]) + { + resetweapons(i); + resetinventory(i); + } + + ps[0].last_extra = gs.max_player_health; + + if (skill == -1) skill = ud.player_skill; + else skill++; ud.player_skill = skill; ud.m_respawn_monsters = (skill == 4); ud.m_monsters_off = ud.monsters_off = 0; @@ -1115,13 +1085,13 @@ void startnewgame(MapRecord* map, int skill) ud.m_respawn_inventory = 0; ud.multimode = 1; - newgame(map, skill, [=](bool) - { + donewgame(map, skill); enterlevel(map, 0); + if (isShareware() && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]); + PlayerColorChanged(); inputState.ClearAllInput(); gameaction = ga_level; - }); } //--------------------------------------------------------------------------- @@ -1132,25 +1102,27 @@ void startnewgame(MapRecord* map, int skill) bool setnextmap(bool checksecretexit) { - MapRecord* map = nullptr;; - int from_bonus = 0; + MapRecord* map = nullptr; + MapRecord* from_bonus = nullptr; - if (ud.eog) + if (ud.eog && !(currentLevel->flags & LEVEL_FORCENOEOG)) { } else if (checksecretexit && ud.from_bonus == 0) { if (ud.secretlevel > 0) { - int newlevnum = levelnum(volfromlevelnum(currentLevel->levelNumber), ud.secretlevel-1); - map = FindMapByLevelNum(newlevnum); + // allow overriding the secret exit destination to make episode compilation easier with maps containing secret exits. + if (currentLevel->flags & LEVEL_SECRETEXITOVERRIDE) map = FindNextSecretMap(currentLevel); + if (!map) map = FindMapByIndex(currentLevel->cluster, ud.secretlevel); + if (map) { - from_bonus = currentLevel->levelNumber + 1; + from_bonus = FindNextMap(currentLevel); } } } - else if (ud.from_bonus && currentLevel->nextLevel == -1) // if the current level has an explicit link, use that instead of ud.from_bonus. + else if (ud.from_bonus && currentLevel->NextMap.IsEmpty()) // if the current level has an explicit link, use that instead of ud.from_bonus. { map = FindMapByLevelNum(ud.from_bonus); } @@ -1169,7 +1141,7 @@ bool setnextmap(bool checksecretexit) { I_Error("Trying to open non-existent %s", map->fileName.GetChars()); } - ud.from_bonus = from_bonus; + ud.from_bonus = from_bonus? from_bonus->levelNumber : 0; } CompleteLevel(map); return false; @@ -1181,30 +1153,33 @@ bool setnextmap(bool checksecretexit) // //--------------------------------------------------------------------------- -void exitlevel(MapRecord *nextlevel) +void exitlevel(MapRecord* nextlevel) { bool endofgame = nextlevel == nullptr; STAT_Update(endofgame); StopCommentary(); - dobonus(endofgame? -1 : 0, [=](bool) - { + SummaryInfo info{}; + info.kills = ps[0].actors_killed; + info.maxkills = ps[0].max_actors_killed; + info.secrets = ps[0].secret_rooms; + info.maxsecrets = ps[0].max_secret_rooms; + info.time = ps[0].player_par / GameTicRate; + info.endofgame = endofgame; + Mus_Stop(); + + if (playerswhenstarted > 1 && ud.coop != 1) + { + // MP scoreboard + ShowScoreboard(playerswhenstarted, [=](bool) + { // Clear potentially loaded per-map ART only after the bonus screens. artClearMapArt(); gameaction = ga_level; ud.eog = false; if (endofgame) { - if (ud.multimode < 2) - { - if (isShareware()) - doorders([](bool) { gameaction = ga_startup; }); - else gameaction = ga_startup; - return; - } - else - { auto nextlevel = FindMapByLevelNum(0); if (!nextlevel) { @@ -1213,11 +1188,22 @@ void exitlevel(MapRecord *nextlevel) } else gameaction = ga_nextlevel; } - } - else + else gameaction = ga_nextlevel; }); + } + else if (ud.multimode <= 1) + { + // SP cutscene + summary + ShowIntermission(currentLevel, nextlevel, &info, [=](bool) + { + // Clear potentially loaded per-map ART only after the bonus screens. + artClearMapArt(); + ud.eog = false; + gameaction = endofgame? ga_startup : ga_nextlevel; + }); + } } diff --git a/source/games/duke/src/premap_d.cpp b/source/games/duke/src/premap_d.cpp index c484017cd..2d1c9f61a 100644 --- a/source/games/duke/src/premap_d.cpp +++ b/source/games/duke/src/premap_d.cpp @@ -232,6 +232,7 @@ static void cachegoodsprites(void) void cacheit_d(void) { + if (!r_precache) return; int i; cachegoodsprites(); diff --git a/source/games/duke/src/premap_r.cpp b/source/games/duke/src/premap_r.cpp index a7aa8d365..cb86ffc62 100644 --- a/source/games/duke/src/premap_r.cpp +++ b/source/games/duke/src/premap_r.cpp @@ -366,37 +366,6 @@ static void cachegoodsprites(void) for (i = SMALLSMOKE; i < (SMALLSMOKE + 4); i++) tloadtile(i); - if (isRRRA() && currentLevel->levelNumber == levelnum(0, 4)) - { - tloadtile(RRTILE2577); - } - if (!isRRRA() && currentLevel->levelNumber == levelnum(1, 2)) - { - tloadtile(RRTILE3190); - tloadtile(RRTILE3191); - tloadtile(RRTILE3192); - tloadtile(RRTILE3144); - tloadtile(RRTILE3139); - tloadtile(RRTILE3132); - tloadtile(RRTILE3120); - tloadtile(RRTILE3121); - tloadtile(RRTILE3122); - tloadtile(RRTILE3123); - tloadtile(RRTILE3124); - } - if (lastlevel) - { - i = isRRRA() ? UFO1_RRRA : UFO1_RR; - tloadtile(i); - i = UFO2; - tloadtile(i); - i = UFO3; - tloadtile(i); - i = UFO4; - tloadtile(i); - i = UFO5; - tloadtile(i); - } } //--------------------------------------------------------------------------- @@ -407,6 +376,7 @@ static void cachegoodsprites(void) void cacheit_r(void) { + if (!r_precache) return; int i; cachegoodsprites(); @@ -461,11 +431,11 @@ void prelevel_r(int g) prelevel_common(g); p = &ps[screenpeek]; + if (currentLevel->gameflags & LEVEL_RR_CLEARMOONSHINE) + ps[myconnectindex].steroids_amount = 0; if (isRRRA()) { - if (currentLevel->levelNumber == levelnum(1, 4)) - ps[myconnectindex].steroids_amount = 0; for (j = 0; j < MAXSPRITES; j++) { diff --git a/source/games/duke/src/sounds.cpp b/source/games/duke/src/sounds.cpp index c9214b65b..9617b033e 100644 --- a/source/games/duke/src/sounds.cpp +++ b/source/games/duke/src/sounds.cpp @@ -46,6 +46,7 @@ source as it is released. #include "gamestate.h" #include "names_d.h" #include "i_music.h" +#include "vm.h" CVAR(Bool, wt_forcemidi, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // quick hack to disable the oggs, which are of lower quality than playing the MIDIs with a good synth and sound font. CVAR(Bool, wt_forcevoc, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // The same for sound effects. The re-recordings are rather poor and disliked @@ -872,4 +873,48 @@ bool StartCommentary(int tag, DDukeActor* actor) return false; } + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySpecialMusic, S_PlaySpecialMusic) +{ + PARAM_PROLOGUE; + PARAM_INT(song); + S_PlaySpecialMusic(song); + return 0; +} + +static int PlaySound(int num, int chan, int flags, double vol) +{ + return S_PlaySound(num, chan, EChanFlags::FromInt(flags), float(vol)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySound, PlaySound) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + PARAM_INT(chan); + PARAM_INT(flags); + PARAM_FLOAT(vol); + ACTION_RETURN_INT(PlaySound(snd, chan, flags, vol)); +} +static void StopSound(int num) +{ + S_StopSound(num); +} + + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, StopSound, StopSound) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + StopSound(snd); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Duke, CheckSoundPlaying, S_CheckSoundPlaying) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + ACTION_RETURN_INT(S_CheckSoundPlaying(snd)); +} + END_DUKE_NS diff --git a/source/games/duke/src/sounds.h b/source/games/duke/src/sounds.h index 415af4406..c0c894cf1 100644 --- a/source/games/duke/src/sounds.h +++ b/source/games/duke/src/sounds.h @@ -48,7 +48,6 @@ void S_MenuSound(void); void S_StopSound(int sndNum, DDukeActor* spr = nullptr, int flags = -1); int S_CheckSoundPlaying(int soundNum); -inline int S_CheckSoundPlaying(int sprnum, int soundNum) { return S_CheckSoundPlaying(soundNum); } int S_CheckActorSoundPlaying(DDukeActor* spriteNum, int soundNum, int channel = 0); int S_CheckAnyActorSoundPlaying(DDukeActor* spriteNum); @@ -64,7 +63,6 @@ inline bool S_IsSoundValid(int num) void S_PlayRRMusic(int newTrack = -1); void S_PlayBonusMusic(); void S_PlayLevelMusic(MapRecord* mi); -void S_PlaySpecialMusic(unsigned int); void S_ContinueLevelMusic(void); // Placeholders. diff --git a/source/games/duke/src/types.h b/source/games/duke/src/types.h index 33d948929..9dbbbc151 100644 --- a/source/games/duke/src/types.h +++ b/source/games/duke/src/types.h @@ -143,7 +143,6 @@ struct user_defs int m_respawn_items, m_respawn_monsters, m_respawn_inventory, m_recstat, m_monsters_off; int m_ffire, ffire, m_player_skill, multimode; int player_skill, marker; - //MapRecord* nextLevel; DDukeActor* cameraactor; diff --git a/source/games/exhumed/src/2d.cpp b/source/games/exhumed/src/2d.cpp index 03a3f9533..c6035b582 100644 --- a/source/games/exhumed/src/2d.cpp +++ b/source/games/exhumed/src/2d.cpp @@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "gstrings.h" #include "gamefuncs.h" #include "c_bind.h" +#include "vm.h" #include @@ -47,8 +48,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_PS_NS -int selectedlevelnew; - //--------------------------------------------------------------------------- // // @@ -373,471 +372,26 @@ void menu_DoPlasma() DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150); } -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- -class DLobotomyScreen : public DImageScreen +DEFINE_ACTION_FUNCTION(_Exhumed, DrawPlasma) { -public: - DLobotomyScreen(FGameTexture *tex, int fade) : DImageScreen(tex, fade) - {} - - void Skipped() override - { - StopLocalSound(); - } - - void Start() override - { - PlayLocalSound(StaticSound[kSoundJonLaugh2], 7000, false, CHANF_UI); - } - - void OnTick() override - { - - DImageScreen::OnTick(); - if (state == finished) StopLocalSound(); - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 }; - -class DMainTitle : public DSkippableScreenJob -{ - const char* a; - const char* b; - int state = 0; - int duration; - int var_4 = 0; - int esi = 130; - int nCount = 0; - int start; - - -public: - DMainTitle() : DSkippableScreenJob(fadein) - { - a = GStrings("TXT_EX_COPYRIGHT1"); - b = GStrings("TXT_EX_COPYRIGHT2"); - duration = skullDurations[0]; - } - - void Start() override - { - PlayLocalSound(StaticSound[59], 0, true, CHANF_UI); - playCDtrack(19, true); - } - - void OnTick() override - { - int ticker = ticks * 120 / GameTicRate; - if (ticks > 1 && state == 0 && !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1)) - { - if (time(0) & 0xF) // cheap-ass random... - PlayGameOverSound(); - else - PlayLocalSound(StaticSound[61], 0, false, CHANF_UI); - state = 1; - start = ticker; - } - if (state == 1) - { - if (ticker > duration) - { - nCount++; - if (nCount > 12) - { - state = finished; - return; - } - duration = start + skullDurations[nCount]; - var_4 = var_4 == 0; - } - } - } - - void Draw(double) override - { - twod->ClearScreen(); - menu_DoPlasma(); - - DrawRel(kSkullHead, 160, 100); - if (state == 0) - { - DrawRel(kSkullJaw, 161, 130); - } - 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); - - - short nTile = kSkullJaw; - - if (var_4) - { - if (esi >= 135) nTile = kTile3583; - else esi += 5; - } - else if (esi <= 130) esi = 130; - else esi -= 2; - - int y; - - if (nTile == kTile3583) - { - y = 131; - } - else - { - y = esi; - if (y > 135) y = 135; - } - - DrawRel(nTile, 161, y); - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -DScreenJob *PlayMovie(const char* fileName); - -void DoTitle(CompletionFunc completion) -{ - JobDesc jobs[5]; - int job = 0; - - jobs[job++] = { Create(tileGetTexture(PublisherLogo()), DScreenJob::fadein | DScreenJob::fadeout) }; - jobs[job++] = { Create(tileGetTexture(seq_GetSeqPicnum(kSeqScreens, 0, 0)), DScreenJob::fadein | DScreenJob::fadeout) }; - jobs[job++] = { PlayMovie("book.mov") }; - jobs[job++] = { Create() }; - - RunScreenJob(jobs, job, completion, true, true); - + return 0; } //--------------------------------------------------------------------------- // -// pre-level map display +// text overlay (native version still needed for Ramses texts. // //--------------------------------------------------------------------------- - static const int8_t MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 }; - - struct TILEFRAMEDEF - { - short nTile; - short xOffs; - short yOffs; - }; - - // 22 bytes - struct MapNamePlaque - { - short xPos; - short yPos; - TILEFRAMEDEF tiles[2]; - TILEFRAMEDEF text; - }; - - static const MapNamePlaque mapNamePlaques[] = { - { 100, 170, kTile3376, 0, 0, kTile3377, 0, 0, kTile3411, 18, 6 }, - { 230, 10, kTile3378, 0, 0, kTile3379, 0, 0, kTile3414, 18, 6 }, // DENDUR (level 2) - { 180, 125, kTile3380, 0, 0, kTile3381, 0, 0, kTile3417, 18, 6 }, // Kalabash - { 10, 95, kTile3382, 0, 0, kTile3383, 0, 0, kTile3420, 18, 6 }, - { 210, 160, kTile3384, 0, 0, kTile3385, 0, 0, kTile3423, 18, 6 }, - { 10, 110, kTile3371, 0, 0, kTile3386, 0, 0, kTile3426, 18, 6 }, - { 10, 50, kTile3387, 0, 0, kTile3388, 0, 0, kTile3429, 18, 6 }, - { 140, 0, kTile3389, 0, 0, kTile3390, 0, 0, kTile3432, 18, 6 }, - { 30, 20, kTile3391, 0, 0, kTile3392, 0, 0, kTile3435, 18, 6 }, - { 200, 150, kTile3409, 0, 0, kTile3410, 0, 0, kTile3418, 20, 4 }, - { 145, 170, kTile3393, 0, 0, kTile3394, 0, 0, kTile3438, 18, 6 }, - { 80, 80, kTile3395, 0, 0, kTile3396, 0, 0, kTile3441, 18, 6 }, - { 15, 0, kTile3397, 0, 0, kTile3398, 0, 0, kTile3444, 18, 5 }, - { 220, 35, kTile3399, 0, 0, kTile3400, 0, 0, kTile3447, 18, 6 }, - { 190, 40, kTile3401, 0, 0, kTile3402, 0, 0, kTile3450, 18, 6 }, - { 20, 130, kTile3403, 0, 0, kTile3404, 0, 0, kTile3453, 19, 6 }, - { 220, 160, kTile3405, 0, 0, kTile3406, 0, 0, kTile3456, 18, 6 }, - { 20, 10, kTile3407, 0, 0, kTile3408, 0, 0, kTile3459, 18, 6 }, - { 200, 10, kTile3412, 0, 0, kTile3413, 0, 0, kTile3419, 18, 5 }, - { 20, 10, kTile3415, 0, 0, kTile3416, 0, 0, kTile3421, 19, 4 } - }; - - // 3 different types of fire, each with 4 frames - static const TILEFRAMEDEF FireTiles[3][4] = { - {{ kTile3484,0,3 },{ kTile3485,0,0 },{ kTile3486,0,3 },{ kTile3487,0,0 }}, - {{ kTile3488,1,0 },{ kTile3489,1,0 },{ kTile3490,0,1 },{ kTile3491,1,1 }}, - {{ kTile3492,1,2 },{ kTile3493,1,0 },{ kTile3494,1,2 },{ kTile3495,1,0 }} - }; - - struct Fire - { - short nFireType; - short xPos; - short yPos; - }; - - // 20 bytes - struct MapFire - { - short nFires; - Fire fires[3]; - }; - - /* - level 1 - 3 fires - level 2 - 3 fires - level 3 - 1 fire - - */ - - static const MapFire MapLevelFires[] = { - 3, {{0, 107, 95}, {1, 58, 140}, {2, 28, 38}}, - 3, {{2, 240, 0}, {0, 237, 32}, {1, 200, 30}}, - 2, {{2, 250, 57}, {0, 250, 43}, {2, 200, 70}}, - 2, {{1, 82, 59}, {2, 84, 16}, {0, 10, 95}}, - 2, {{2, 237, 50}, {1, 215, 42}, {1, 210, 50}}, - 3, {{0, 40, 7}, {1, 75, 6}, {2, 100, 10}}, - 3, {{0, 58, 61}, {1, 85, 80}, {2, 111, 63}}, - 3, {{0, 260, 65}, {1, 228, 0}, {2, 259, 15}}, - 2, {{0, 81, 38}, {2, 58, 38}, {2, 30, 20}}, - 3, {{0, 259, 49}, {1, 248, 76}, {2, 290, 65}}, - 3, {{2, 227, 66}, {0, 224, 98}, {1, 277, 30}}, - 2, {{0, 100, 10}, {2, 48, 76}, {2, 80, 80}}, - 3, {{0, 17, 2}, {1, 29, 49}, {2, 53, 28}}, - 3, {{0, 266, 42}, {1, 283, 99}, {2, 243, 108}}, - 2, {{0, 238, 19}, {2, 240, 92}, {2, 190, 40}}, - 2, {{0, 27, 0}, {1, 70, 40}, {0, 20, 130}}, - 3, {{0, 275, 65}, {1, 235, 8}, {2, 274, 6}}, - 3, {{0, 75, 45}, {1, 152, 105}, {2, 24, 68}}, - 3, {{0, 290, 25}, {1, 225, 63}, {2, 260, 110}}, - 0, {{1, 20, 10}, {1, 20, 10}, {1, 20, 10}} - }; - -class DMapScreen : public DScreenJob +void TextOverlay::Create(const FString& text, int pal) { - int i; - int x = 0; - int delta = 0; - int nIdleSeconds = 0; - - int curYPos, destYPos; - int nLevel, nLevelNew, nLevelBest; - -public: - DMapScreen(int nLevel_, int nLevelNew_, int nLevelBest_) : DScreenJob(fadein|fadeout), nLevel(nLevel_), nLevelNew(nLevelNew_), nLevelBest(nLevelBest_) - { - curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2)); - destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); - - if (curYPos < destYPos) { - delta = 2; - } - - if (curYPos > destYPos) { - delta = -2; - } - - // Trim smoke in widescreen -#if 0 - vec2_t mapwinxy1 = windowxy1, mapwinxy2 = windowxy2; - int32_t width = mapwinxy2.x - mapwinxy1.x + 1, height = mapwinxy2.y - mapwinxy1.y + 1; - if (3 * width > 4 * height) - { - mapwinxy1.x += (width - 4 * height / 3) / 2; - mapwinxy2.x -= (width - 4 * height / 3) / 2; - } -#endif - } - - void Draw(double smoothratio) - { - int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); - - twod->ClearScreen(); - - int tileY = curYPos; - - // Draw the background screens - for (i = 0; i < 10; i++) - { - DrawAbs(kTile3353 + i, x, tileY); - tileY -= 200; - } - - // for each level - drawing the 'level completed' on-fire smoke markers - for (i = 0; i < kMap20; i++) - { - int screenY = (i >> 1) * -200; - - if (nLevelBest >= i) // check if the player has finished this level - { - for (int j = 0; j < MapLevelFires[i].nFires; j++) - { - int nFireFrame = ((currentclock >> 4) & 3); - assert(nFireFrame >= 0 && nFireFrame < 4); - - int nFireType = MapLevelFires[i].fires[j].nFireType; - assert(nFireType >= 0 && nFireType < 3); - - int nTile = FireTiles[nFireType][nFireFrame].nTile; - int smokeX = MapLevelFires[i].fires[j].xPos + FireTiles[nFireType][nFireFrame].xOffs; - int smokeY = MapLevelFires[i].fires[j].yPos + FireTiles[nFireType][nFireFrame].yOffs + curYPos + screenY; - - // Use rotatesprite to trim smoke in widescreen - DrawAbs(nTile, smokeX, smokeY); - // Todo: mask out the sides of the screen if the background is not widescreen. - } - } - - int t = (((currentclock & 16) >> 4)); - - int nTile = mapNamePlaques[i].tiles[t].nTile; - - int nameX = mapNamePlaques[i].xPos + mapNamePlaques[i].tiles[t].xOffs; - int nameY = mapNamePlaques[i].yPos + mapNamePlaques[i].tiles[t].yOffs + curYPos + screenY; - - // Draw level name plaque - DrawAbs(nTile, nameX, nameY); - - int8_t shade = 96; - - if (nLevelNew == i) - { - shade = (bsin(16 * currentclock) + 31) >> 8; - } - else if (nLevelBest >= i) - { - shade = 31; - } - - int textY = mapNamePlaques[i].yPos + mapNamePlaques[i].text.yOffs + curYPos + screenY; - int textX = mapNamePlaques[i].xPos + mapNamePlaques[i].text.xOffs; - nTile = mapNamePlaques[i].text.nTile; - - // draw the text, alternating between red and black - DrawAbs(nTile, textX, textY, shade); - } - - selectedlevelnew = nLevelNew + 1; - } - - void OnTick() override - { - if (curYPos != destYPos) - { - // scroll the map every couple of ms - curYPos += delta; - - if (curYPos > destYPos && delta > 0) { - curYPos = destYPos; - } - - if (curYPos < destYPos && delta < 0) { - curYPos = destYPos; - } - nIdleSeconds = 0; - } - else nIdleSeconds++; - if (nIdleSeconds > 300) state = finished; - } - - bool OnEvent(event_t* ev) override - { - int key = ev->data1; - if (ev->type == EV_KeyDown) - { - 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) { - delta = 2; - } - else { - delta = -2; - } - - nIdleSeconds = 0; - } - return true; - } - - if (key == KEY_DOWNARROW || key == KEY_PAD_DPAD_DOWN || key == sc_kpad_2) - { - if (curYPos == destYPos && nLevelNew > 0) - { - nLevelNew--; - assert(nLevelNew >= 0); - - destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); - - if (curYPos <= destYPos) { - delta = 2; - } - else { - delta = -2; - } - - nIdleSeconds = 0; - } - return true; - } - if (!specialKeyEvent(ev)) state = skipped; - return true; - } - return false; - } -}; - - void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray &jobs) - { - if (nLevel > kMap20 || nLevelNew > kMap20) // max single player levels - { - return; - } -#ifdef _DEBUG - nLevelBest = kMap20; -#endif - - if (nLevel < 1) nLevel = 1; - if (nLevelNew < 1) nLevelNew = nLevel; - - // 0-offset the level numbers - jobs.Push( { Create(nLevel-1, nLevelNew-1, nLevelBest-1) }); - } - -//--------------------------------------------------------------------------- -// -// text overlay -// -//--------------------------------------------------------------------------- + lastclock = 0; + FString ttext = GStrings(text); + screentext = ttext.Split("\n"); + ComputeCinemaText(); +} void TextOverlay::Start(double starttime) { @@ -857,10 +411,9 @@ void TextOverlay::ComputeCinemaText() nHeight = screentext.Size() * 10; } -void TextOverlay::ReadyCinemaText(uint16_t nVal) +void TextOverlay::ReadyCinemaText(const char* nVal) { - FStringf label("TXT_EX_CINEMA%d", nVal); - label = GStrings(label); + FString label = nVal[0] == '$'? GStrings(nVal +1) : nVal; screentext = label.Split("\n"); ComputeCinemaText(); } @@ -903,33 +456,6 @@ bool TextOverlay::AdvanceCinemaText(double clock) // //--------------------------------------------------------------------------- -enum EScenes -{ - CINEMA_BEFORE_LEVEL_5, - CINEMA_AFTER_LEVEL_10, - CINEMA_BEFORE_LEVEL_11, - CINEMA_AFTER_LEVEL_15, - CINEMA_LOSE_SCENE, - CINEMA_AFTER_LEVEL_20, -}; - -struct CinemaDef -{ - short tile; - short palette; - short text; - short track; -}; - -static CinemaDef cinemas[] = { - { 3449, 3, 2, 2}, - { 3451, 5, 4, 3}, - { 3454, 1, 3, 4}, - { 3446, 7, 6, 6}, - { 3445, 4, 7, 7}, - { 3448, 6, 8, 8} -}; - static const char * const cinpalfname[] = { "3454.pal", "3452.pal", @@ -965,419 +491,59 @@ void uploadCinemaPalettes() //--------------------------------------------------------------------------- // -// cinema +// this accesses the tile data and needs to remain native. // //--------------------------------------------------------------------------- -class DCinema : public DSkippableScreenJob +static int DoStatic(int a, int b) { - TextOverlay text; - short cinematile; - int currentCinemaPalette; - int edx; - int check; - int cont = 1; - -public: - DCinema(int nVal, int checklevel = -1) : DSkippableScreenJob(fadein|fadeout) - { - if (nVal < 0 || nVal >5) return; - cinematile = cinemas[nVal].tile; - currentCinemaPalette = cinemas[nVal].palette; - text.Start(0); - text.ReadyCinemaText(cinemas[nVal].text); - text.SetPalette(currentCinemaPalette); - edx = cinemas[nVal].track; - check = checklevel; - } - - void Start() override - { - if (check > 0 && check != selectedlevelnew) - { - state = finished; - return; // immediately abort if the player selected a different level on the map - } - - check = -1; - StopAllSounds(); - if (edx != -1) - { - playCDtrack(edx + 2, false); - } - } - - 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(); - cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate)); - } -}; - -//--------------------------------------------------------------------------- -// -// last level cinema -// -//--------------------------------------------------------------------------- - -class DLastLevelCinema : public DScreenJob -{ - int var_24 = 16; - int var_28 = 12; - - int ebp; - int phase = 0; - int nextclock = 4; - unsigned int nStringTypeOn, nCharTypeOn; - int screencnt = 0; - bool skiprequest = false; - - TArray screentext; - -public: - DLastLevelCinema() : DScreenJob(fadein | fadeout) {} - -private: - void DoStatic(int a, int b) - { + TileFiles.tileMakeWritable(kTileLoboLaptop); + auto tex = dynamic_cast(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); + if (tex) tex->Reload(); auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop); - int v2 = 160 - a / 2; - int v4 = 81 - b / 2; + int y = 160 - a / 2; + int left = 81 - b / 2; - int var_18 = v2 + a; - int v5 = v4 + b; + int bottom = y + a; + int right = left + b; - auto pTile = (pixels + (200 * v2)) + v4; + auto pTile = (pixels + (200 * y)) + left; TileFiles.InvalidateTile(kTileLoboLaptop); - while (v2 < var_18) + for(;y < bottom; y++) { - uint8_t* pStart = pTile; + uint8_t* pixel = pTile; pTile += 200; - int v7 = v4; - - while (v7 < v5) + for (int x = left; x < right; x++) { - *pStart = RandomBit() * 16; - - v7++; - pStart++; + *pixel++ = RandomBit() * 16; } - v2++; } - } - - void Phase1() - { - if (var_24 >= 116) - { - if (var_28 < 192) - var_28 += 20; - } - else - { - var_24 += 20; - } - - DoStatic(var_28, var_24); - } - - bool InitPhase2() - { - FStringf label("TXT_EX_LASTLEVEL%d", screencnt + 1); - label = GStrings(label); - screentext = label.Split("\n"); - if (screentext.Size() == 0) return false; - - nStringTypeOn = 0; - nCharTypeOn = 0; - - ebp = screentext.Size() * 4; // half height of the entire text - ebp = 81 - ebp; // offset from the screen's center. + return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex(); +} +static int UndoStatic() +{ auto tex = dynamic_cast(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); if (tex) tex->Reload(); TileFiles.InvalidateTile(kTileLoboLaptop); - return true; - } - - bool Phase3() - { - DoStatic(var_28, var_24); - - if (var_28 > 20) { - var_28 -= 20; - return true; - } - - if (var_24 > 20) { - var_24 -= 20; - return true; - } - return false; - } - - void DisplayPhase2() - { - int yy = ebp; - - auto p = GStrings["REQUIRED_CHARACTERS"]; - if (p && *p) - { - yy *= 2; - for (int i = 0; i < nStringTypeOn; i++, yy += 10) - { - DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit640x400, TAG_DONE); - } - DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn, TAG_DONE); - } - else - { - for (int i = 0; i < nStringTypeOn; i++, yy += 8) - { - DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE); - } - DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn, TAG_DONE); - } - } - - bool OnEvent(event_t* ev) - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true; - return true; - } - - void Start() override - { - PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI); - phase = 1; - } - - void OnTick() override - { - switch (phase) - { - case 1: - Phase1(); - if (skiprequest || ticks >= nextclock) - { - InitPhase2(); - phase = 2; - skiprequest = false; - } - break; - - case 2: - if (screentext[nStringTypeOn][nCharTypeOn] != ' ') - PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI); - - nCharTypeOn++; - if (screentext[nStringTypeOn][nCharTypeOn] == 0) - { - nCharTypeOn = 0; - nStringTypeOn++; - if (nStringTypeOn >= screentext.Size()) - { - nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks; - phase = 3; - } - - } - if (skiprequest) - { - nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks; - phase = 4; - } - break; - - case 3: - if (ticks >= nextclock || skiprequest) - { - PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI); - phase = 4; - nextclock = ticks + 60; - skiprequest = false; - } - - case 4: - if (ticks >= nextclock) - { - skiprequest |= !Phase3(); - } - if (skiprequest) - { - // Go to the next text page. - if (screencnt != 2) - { - screencnt++; - nextclock = ticks + 60; - skiprequest = 0; - phase = 1; - } - else state = finished; - } - - if (skiprequest) - { - state = finished; - } - } - } - - void Draw(double) override - { - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - if (phase == 2 || phase == 3) DisplayPhase2(); - } - -}; - -//--------------------------------------------------------------------------- -// -// Credits roll -// -//--------------------------------------------------------------------------- - -class DExCredits : public DScreenJob -{ - TArray credits; - TArray pagelines; - uint64_t page; - uint64_t pagetime; - bool skiprequest = false; - -public: - DExCredits() - { - auto textdata = fileSystem.LoadFile("credits.txt", 1); - FString text = (char*)textdata.Data(); - text.Substitute("\r", ""); - credits = text.Split("\n\n"); - } - - bool OnEvent(event_t* ev) - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true; - return true; - } - - void Start() override - { - if (credits.Size() == 0) - { - state = finished; - return; - } - playCDtrack(19, false); - pagetime = 0; - page = -1; - } - - void OnTick() override - { - if (ticks >= pagetime || skiprequest) - { - page++; - if (page < credits.Size()) - pagelines = credits[page].Split("\n"); - else - { - if (skiprequest || !CDplaying()) - { - state = finished; - return; - } - pagelines.Clear(); - } - 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++) - { - int ptime = clamp((pagetime - ticks - smoothratio) * 1000 / GameTicRate, 0, 2000); // in milliseconds - int light; - - if (ptime < 255) light = ptime; - else if (ptime > 2000 - 255) light = 2000 - ptime; - else light = 255; - - auto color = PalEntry(255, light, light, light); - - int nStringWidth = SmallFont->StringWidth(pagelines[i]); - DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, color, TAG_DONE); - y += 10; - } - } -}; - -//--------------------------------------------------------------------------- -// -// player died -// -//--------------------------------------------------------------------------- - -void DoGameOverScene(bool finallevel) -{ - JobDesc job; - - if (finallevel) - { - job = { Create(CINEMA_LOSE_SCENE) }; - } - else - { - StopCD(); - PlayGameOverSound(); - job = { Create(tileGetTexture(kTile3591), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff, TRANSLATION(Translation_BasePalettes, 16)) }; - } - RunScreenJob(&job, 1, [](bool) { gameaction = ga_mainmenu; }); + return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex(); } - -void DoAfterCinemaScene(int nLevel, TArray& jobs) +DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, DoStatic, DoStatic) { - int scene = -1; - if (nLevel == 10) scene = CINEMA_AFTER_LEVEL_10; - if (nLevel == 15) scene = CINEMA_AFTER_LEVEL_15; - if (nLevel == 20) scene = CINEMA_AFTER_LEVEL_20; - if (scene > 0) jobs.Push({ Create(scene) }); - if (nLevel == 19) { jobs.Push({ Create() }); selectedlevelnew = 20; } - if (nLevel == 20) jobs.Push({ Create() }); + PARAM_PROLOGUE; + PARAM_INT(x); + PARAM_INT(y); + ACTION_RETURN_INT(DoStatic(x, y)); } -void DoBeforeCinemaScene(int nLevel, TArray& jobs) +DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, UndoStatic, UndoStatic) { - if (nLevel == 5) jobs.Push({ Create(CINEMA_BEFORE_LEVEL_5) }); - else if (nLevel == 11) jobs.Push({ Create(CINEMA_BEFORE_LEVEL_11, 11) }); + ACTION_RETURN_INT(UndoStatic()); } END_PS_NS diff --git a/source/games/exhumed/src/bubbles.cpp b/source/games/exhumed/src/bubbles.cpp index a11333528..64b6a9046 100644 --- a/source/games/exhumed/src/bubbles.cpp +++ b/source/games/exhumed/src/bubbles.cpp @@ -236,7 +236,7 @@ void DoBubbleMachines() void BuildBubbleMachine(int nSprite) { if (nMachineCount >= kMaxMachines) { - I_Error("too many bubble machines in level %d\n", currentLevel->levelNumber); + I_Error("too many bubble machines in level %s\n", currentLevel->labelName.GetChars()); exit(-1); } diff --git a/source/games/exhumed/src/d_menu.cpp b/source/games/exhumed/src/d_menu.cpp index 79061cb69..50d7282ef 100644 --- a/source/games/exhumed/src/d_menu.cpp +++ b/source/games/exhumed/src/d_menu.cpp @@ -72,13 +72,6 @@ void GameInterface::QuitToTitle() gameaction = ga_mainmenu; } -bool GameInterface::StartGame(FNewGameStartup& gs) -{ - auto map = FindMapByLevelNum(gs.Skill); // 0 is training, 1 is the regular game - the game does not have skill levels. - DeferedStartGame(map, 1, true); - return true; -} - FSavegameInfo GameInterface::GetSaveSig() { return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS }; @@ -90,12 +83,6 @@ END_PS_NS using namespace Exhumed; -DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw) -{ - menu_DoPlasma(); - return 0; -} - DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw) { auto nLogoTile = GameLogo(); diff --git a/source/games/exhumed/src/enginesubs.cpp b/source/games/exhumed/src/enginesubs.cpp index 7223f822e..76b570bd4 100644 --- a/source/games/exhumed/src/enginesubs.cpp +++ b/source/games/exhumed/src/enginesubs.cpp @@ -36,6 +36,8 @@ void resettiming() void precache() { + if (!r_precache) return; + int i; for (i = 0; i < numsectors; i++) diff --git a/source/games/exhumed/src/exhumed.cpp b/source/games/exhumed/src/exhumed.cpp index b2988a45d..0c46756c0 100644 --- a/source/games/exhumed/src/exhumed.cpp +++ b/source/games/exhumed/src/exhumed.cpp @@ -238,6 +238,15 @@ double calc_smoothratio() return I_GetTimeFrac() * MaxSmoothRatio; } +void DoGameOverScene(bool finallevel) +{ + // todo: make these customizable later. + StartCutscene(finallevel ? "ExhumedCutscenes.BuildCinemaLose" : "ExhumedCutscenes.BuildGameoverScene", 0, [](bool) + { + gameaction = ga_mainmenu; + }); +} + void GameMove(void) { FixPalette(); @@ -247,7 +256,7 @@ void GameMove(void) sprite[i].backuploc(); } - if (currentLevel->levelNumber == kMap20) + if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN) { if (lCountDown <= 0) { @@ -456,7 +465,7 @@ void GameInterface::Ticker() void LevelFinished() { - NextMap = currentLevel->levelNumber == 20 ? nullptr : FindMapByLevelNum(currentLevel->levelNumber + 1); // todo: Use the map record for progression + NextMap = FindNextMap(currentLevel); EndLevel = 13; } @@ -480,19 +489,6 @@ void GameInterface::app_init() #if 0 help_disabled = true; #endif - // Create the global level table. Parts of the engine need it, even though the game itself does not. - for (int i = 0; i <= 32; i++) - { - auto mi = AllocateMap(); - mi->fileName.Format("LEV%d.MAP", i); - mi->labelName.Format("LEV%d", i); - mi->name.Format("$TXT_EX_MAP%02d", i); - mi->levelNumber = i; - - int nTrack = i; - if (nTrack != 0) nTrack--; - mi->cdSongId = (nTrack % 8) + 11; - } InitCheats(); registerosdcommands(); @@ -615,7 +611,7 @@ void SerializeState(FSerializer& arc) int loaded = 0; if (arc.BeginObject("state")) { - if (arc.isReading() && currentLevel->levelNumber == 20) + if (arc.isReading() && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)) { InitEnergyTile(); } @@ -639,7 +635,6 @@ void SerializeState(FSerializer& arc) ("bsnakecam", bSnakeCam) ("slipmode", bSlipMode) ("PlayClock", PlayClock) - ("cinemaseen", nCinemaSeen) ("spiritsprite", nSpiritSprite) .EndObject(); } diff --git a/source/games/exhumed/src/exhumed.h b/source/games/exhumed/src/exhumed.h index fb8f6075f..005ff042f 100644 --- a/source/games/exhumed/src/exhumed.h +++ b/source/games/exhumed/src/exhumed.h @@ -54,8 +54,6 @@ void SetHiRes(); void BlackOut(); void DoGameOverScene(bool finallevel); -void DoAfterCinemaScene(int nLevel, TArray &jobs); -void DoBeforeCinemaScene(int nLevel, TArray& jobs); int Query(short n, short l, ...); @@ -83,12 +81,11 @@ void DoSpiritHead(); void CheckKeys2(); void GameTicker(); -void InitLevel(int); +void InitLevel(MapRecord*); void InitNewGame(); int showmap(short nLevel, short nLevelNew, short nLevelBest); void menu_DoPlasma(); -void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray& jobs); void DoEnergyTile(); void InitEnergyTile(); @@ -127,7 +124,6 @@ extern short nCurBodyNum; extern short nBodyTotal; extern short bSnakeCam; -extern uint8_t nCinemaSeen; extern short nButtonColor; @@ -155,11 +151,6 @@ extern short bDoFlashes; extern int bVanilla; -inline int PublisherLogo() -{ - return (g_gameType & GAMEFLAG_EXHUMED) ? kTileBMGLogo : kTilePIELogo; -} - inline int GameLogo() { return (g_gameType & GAMEFLAG_EXHUMED) ? kExhumedLogo : kPowerslaveLogo; @@ -197,10 +188,11 @@ public: void Start(double starttime); void ComputeCinemaText(); - void ReadyCinemaText(uint16_t nVal); + void ReadyCinemaText(const char* nVal); void DisplayText(); bool AdvanceCinemaText(double clock); void SetPalette(int pal) { currentCinemaPalette = pal; } + void Create(const FString& text, int pal); }; @@ -229,7 +221,6 @@ struct GameInterface : ::GameInterface bool GenerateSavePic() override; void MenuOpened() override; void MenuSound(EMenuSounds snd) override; - bool StartGame(FNewGameStartup& gs) override; FSavegameInfo GetSaveSig() override; void SerializeGameState(FSerializer& arc); bool CanSave() override; diff --git a/source/games/exhumed/src/gameloop.cpp b/source/games/exhumed/src/gameloop.cpp index d810eab56..ab08b2721 100644 --- a/source/games/exhumed/src/gameloop.cpp +++ b/source/games/exhumed/src/gameloop.cpp @@ -49,32 +49,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "automap.h" #include "raze_music.h" #include "v_draw.h" +#include "vm.h" BEGIN_PS_NS short nBestLevel; -extern uint8_t nCinemaSeen; - void RunCinemaScene(int num); void GameMove(void); void DrawClock(); double calc_smoothratio(); void DoTitle(CompletionFunc completion); -static void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray &jobs) -{ - if (nLevelNew == 5 && !(nCinemaSeen & 1)) { - nCinemaSeen |= 1; - DoBeforeCinemaScene(5, jobs); - } - - menu_DrawTheMap(nLevel, nLevelNew, nLevelBest, jobs); - - if (nLevelNew == 11 && !(nCinemaSeen & 2)) { - DoBeforeCinemaScene(11, jobs); - } -} void GameInterface::Render() @@ -83,7 +69,7 @@ void GameInterface::Render() drawtime.Reset(); drawtime.Clock(); - if (currentLevel && currentLevel->levelNumber == kMap20) + if (currentLevel && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)) { DoEnergyTile(); DrawClock(); @@ -127,104 +113,89 @@ void GameInterface::DrawBackground() DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 0); } +void GameInterface::NextLevel(MapRecord *map, int skill) +{ + InitLevel(map); + + if (map->levelNumber >= nBestLevel) + { + nBestLevel = map->levelNumber - 1; + } + + STAT_NewLevel(currentLevel->labelName); + +} + //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- -static void Intermission(MapRecord *from_map, MapRecord *to_map) -{ - TArray jobs; - - if (from_map) StopAllSounds(); - bCamera = false; - automapMode = am_off; - - if (to_map) - { - if (to_map->levelNumber != 0) - nBestLevel = to_map->levelNumber - 1; - - STAT_Update(false); - if (to_map->levelNumber == kMap20) - nPlayerLives[0] = 0; - - if (to_map->levelNumber == 0) // skip all intermission stuff when going to the training map. - { - gameaction = ga_nextlevel; - return; - } - else - { - DoAfterCinemaScene(to_map->levelNumber - 1, jobs); - } - if (to_map->levelNumber > -1 && to_map->levelNumber < kMap20) - { - // start a new game at the given level - if (!nNetPlayerCount && to_map->levelNumber > 0) - { - showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs); - } - else - jobs.Push({ Create(1) }); // we need something in here even in the multiplayer case. - } - } - else - { - DoAfterCinemaScene(20, jobs); - STAT_Update(true); - } - - - if (jobs.Size() > 0) - { - RunScreenJob(jobs.Data(), jobs.Size(), [=](bool) - { - if (!to_map) gameaction = ga_startup; // this was the end of the game - else - { - if (to_map->levelNumber != selectedlevelnew) - { - // User can switch destination on the scrolling map. - g_nextmap = FindMapByLevelNum(selectedlevelnew); - STAT_Cancel(); - } - gameaction = ga_nextlevel; - - } - }); - } - -} - -void GameInterface::NextLevel(MapRecord *map, int skill) -{ - InitLevel(map->levelNumber); - - if (map->levelNumber > nBestLevel) - { - nBestLevel = selectedlevelnew; - } - - if (map->levelNumber == 11) nCinemaSeen |= 2; - STAT_NewLevel(currentLevel->labelName); - -} - 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); - Intermission(nullptr, map); + InitLevel(map); + gameaction = ga_level; } -void GameInterface::LevelCompleted(MapRecord *map, int skill) +int selectedlevelnew; + +DEFINE_ACTION_FUNCTION(DMapScreen, SetNextLevel) +{ + PARAM_PROLOGUE; + PARAM_INT(v); + selectedlevelnew = v; + return 0; +} + +void GameInterface::LevelCompleted(MapRecord *to_map, int skill) { Mus_Stop(); - if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu; - Intermission(currentLevel, map); + if (currentLevel->gameflags & LEVEL_EX_TRAINING) + { + gameaction = ga_mainmenu; + return; + } + + StopAllSounds(); + bCamera = false; + automapMode = am_off; + + STAT_Update(to_map == nullptr); + if (to_map) + { + if (to_map->levelNumber > nBestLevel) nBestLevel = to_map->levelNumber - 1; + + if (to_map->gameflags & LEVEL_EX_COUNTDOWN) nPlayerLives[0] = 0; + if (to_map->gameflags & LEVEL_EX_TRAINING) + { + gameaction = ga_nextlevel; + return; + } + } + SummaryInfo info{}; + info.kills = nCreaturesKilled; + info.maxkills = nCreaturesTotal; + info.supersecrets = nBestLevel; + info.time = PlayClock * GameTicRate / 120; + selectedlevelnew = to_map->levelNumber; + ShowIntermission(currentLevel, to_map, &info, [=](bool) + { + if (!to_map) gameaction = ga_startup; // this was the end of the game + else + { + if (to_map->levelNumber != selectedlevelnew) + { + // User can switch destination on the scrolling map. + g_nextmap = FindMapByLevelNum(selectedlevelnew); + STAT_Cancel(); + } + gameaction = ga_nextlevel; + + } + }); } //--------------------------------------------------------------------------- @@ -237,22 +208,7 @@ void GameInterface::Startup() { resettiming(); EndLevel = 0; - - if (userConfig.CommandMap.IsNotEmpty()) - { - /* - auto map = FindMapByName(userConfig.CommandMap); - if (map) DeferedStartMap(map, 0); - userConfig.CommandMap = ""; - goto again; - */ - } - else - { - if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; }); - else gameaction = ga_mainmenu; - } - + PlayLogos(ga_mainmenu, ga_mainmenu, false); } void GameInterface::ErrorCleanup() diff --git a/source/games/exhumed/src/init.cpp b/source/games/exhumed/src/init.cpp index ad81fc807..5c2c1db7c 100644 --- a/source/games/exhumed/src/init.cpp +++ b/source/games/exhumed/src/init.cpp @@ -66,9 +66,9 @@ uint8_t bIsVersion6 = true; -uint8_t LoadLevel(int nMap) +uint8_t LoadLevel(MapRecord* map) { - if (nMap == kMap20) + if (map->gameflags & LEVEL_EX_COUNTDOWN) { lCountDown = 81000; nAlarmTicks = 30; @@ -116,12 +116,12 @@ uint8_t LoadLevel(int nMap) InitItems(); InitInput(); - if (nMap == kMap20) { + if (map->gameflags & LEVEL_EX_COUNTDOWN) { InitEnergyTile(); } } - if (nMap > 15) + if (map->gameflags & LEVEL_EX_ALTSOUND) { nSwitchSound = 35; nStoneSound = 23; @@ -136,10 +136,6 @@ uint8_t LoadLevel(int nMap) nStopSound = 66; } - if (nMap < 0) { - return false; - } - vec3_t startPos; engineLoadBoard(currentLevel->fileName, 0, &startPos, &inita, &initsect); initx = startPos.x; @@ -172,12 +168,12 @@ uint8_t LoadLevel(int nMap) return true; } -void InitLevel(int level) // todo: use a map record +void InitLevel(MapRecord* map) { StopCD(); - currentLevel = FindMapByLevelNum(level); - if (!LoadLevel(level)) { - I_Error("Can't load level %d...\n", level); + currentLevel = map; + if (!LoadLevel(map)) { + I_Error("Cannot load %s...\n", map->fileName.GetChars()); } for (int i = 0; i < nTotalPlayers; i++) @@ -200,17 +196,14 @@ void InitLevel(int level) // todo: use a map record RefreshStatus(); - int nTrack = level; - if (nTrack != 0) nTrack--; - - playCDtrack((nTrack % 8) + 11, true); + if (!mus_redbook && map->music.IsNotEmpty()) Mus_Play(map->labelName, map->music, true); // Allow non-CD music if defined for the current level + playCDtrack(map->cdSongId, true); setLevelStarted(currentLevel); } void InitNewGame() { bCamera = false; - nCinemaSeen = 0; PlayerCount = 0; for (int i = 0; i < nTotalPlayers; i++) diff --git a/source/games/exhumed/src/items.cpp b/source/games/exhumed/src/items.cpp index edcd344f6..25b2bb945 100644 --- a/source/games/exhumed/src/items.cpp +++ b/source/games/exhumed/src/items.cpp @@ -432,7 +432,7 @@ void StartRegenerate(short nSprite) pSprite->extra = 1350; pSprite->ang = nFirstRegenerate; - if (currentLevel->levelNumber <= kMap20) + if (!(currentLevel->gameflags & LEVEL_EX_MULTI)) { pSprite->ang /= 5; } diff --git a/source/games/exhumed/src/menu.cpp b/source/games/exhumed/src/menu.cpp index f47158246..52d05404e 100644 --- a/source/games/exhumed/src/menu.cpp +++ b/source/games/exhumed/src/menu.cpp @@ -43,8 +43,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_PS_NS -uint8_t nCinemaSeen; - uint8_t energytile[66 * 66] = {0}; uint8_t *cur; diff --git a/source/games/exhumed/src/move.cpp b/source/games/exhumed/src/move.cpp index 7a7ab0c94..fdd31dede 100644 --- a/source/games/exhumed/src/move.cpp +++ b/source/games/exhumed/src/move.cpp @@ -207,7 +207,7 @@ void MoveThings() DoMovingSects(); DoRegenerates(); - if (currentLevel->levelNumber == kMap20) + if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN) { DoFinale(); if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart) diff --git a/source/games/exhumed/src/movie.cpp b/source/games/exhumed/src/movie.cpp index cb632cc05..b5c57a221 100644 --- a/source/games/exhumed/src/movie.cpp +++ b/source/games/exhumed/src/movie.cpp @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "s_music.h" #include "screenjob.h" #include "v_draw.h" +#include "vm.h" BEGIN_PS_NS @@ -63,9 +64,34 @@ class LMFPlayer AudioData audio{}; AnimTextures animtex; + FileReader fp; + int nFrame = 0; + uint64_t nextclock = 0; + public: + + LMFPlayer(const char *filename) + { + fp = fileSystem.OpenFileReader(filename); + Open(fp); + } + + bool Frame(uint64_t clock) + { + if (clock >= nextclock) + { + nextclock += 100'000'000; + if (ReadFrame(fp) == 0) + { + return true; + } + } + + return false; + } + int ReadFrame(FileReader& fp) { nFrame++; @@ -188,105 +214,56 @@ public: S_StopCustomStream(stream); } - AnimTextures& animTex() + FTextureID GetTexture() { - return animtex; + return animtex.GetFrameID(); } }; -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DLmfPlayer : public DSkippableScreenJob +int IdentifyLMF(const FString* fn) { - LMFPlayer decoder; - double angle = 1536; - double z = 0; - uint64_t nextclock = 0, lastclock = 0; - FileReader fp; - -public: - - DLmfPlayer(FileReader& fr) - { - decoder.Open(fr); - lastclock = 0; - nextclock = 0; - fp = std::move(fr); - pausable = false; - } - - //--------------------------------------------------------------------------- - // - // - // - //--------------------------------------------------------------------------- - - 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) - { - state = finished; - return; - } - } - - double duration = (clock - lastclock) * double(120. / 8'000'000'000); - if (z < 65536) { // Zoom - normal zoom is 65536. - z += 2048 * duration; - } - if (z > 65536) z = 65536; - if (angle != 0) { - angle += 16. * duration; - if (angle >= 2048) { - angle = 0; - } - } - - { - twod->ClearScreen(); - DrawTexture(twod, decoder.animTex().GetFrame(), 160, 100, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffset, true, DTA_FlipY, true, DTA_ScaleX, z / 65536., DTA_ScaleY, z / 65536., DTA_Rotate, (-angle - 512) * BAngToDegree, TAG_DONE); - } - - lastclock = clock; - } - - void OnDestroy() override - { - decoder.Close(); - fp.Close(); - } -}; - - - -DScreenJob* PlayMovie(const char* fileName) -{ - // clear keys - - auto fp = fileSystem.OpenFileReader(fileName); - if (!fp.isOpen()) - { - return Create(1); - } + auto fp = fileSystem.OpenFileReader(*fn); + if (!fp.isOpen()) return false; char buffer[4]; fp.Read(buffer, 4); - if (memcmp(buffer, "LMF ", 4)) - { - fp.Close(); - // Allpw replacement with more modern formats. - return PlayVideo(fileName); - } - fp.Seek(0, FileReader::SeekSet); - return Create(fp); + return (0 == memcmp(buffer, "LMF ", 4)); } +DEFINE_ACTION_FUNCTION(_LMFDecoder, Create) +{ + PARAM_PROLOGUE; + PARAM_STRING(fn); + ACTION_RETURN_POINTER(new LMFPlayer(fn)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_LMFDecoder, Identify, IdentifyLMF) +{ + PARAM_PROLOGUE; + PARAM_STRING(fn); + ACTION_RETURN_BOOL(IdentifyLMF(&fn)); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, Frame) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + PARAM_FLOAT(clock); + ACTION_RETURN_BOOL(self->Frame(uint64_t(clock))); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, GetTexture) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + ACTION_RETURN_INT(self->GetTexture().GetIndex()); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, Close) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + self->Close(); + delete self; + return 0; +} + + + END_PS_NS diff --git a/source/games/exhumed/src/namelist.h b/source/games/exhumed/src/namelist.h index 2a6a22d87..50787ef8d 100644 --- a/source/games/exhumed/src/namelist.h +++ b/source/games/exhumed/src/namelist.h @@ -1,3 +1,4 @@ +x(SkullJaw, 3437) x(PowerslaveLogo, 3442) x(MenuNewGameTile, 3460) x(MenuLoadGameTile, 3461) @@ -6,6 +7,7 @@ x(MenuSoundFxTile, 3466) x(MenuCursorTile, 3468) x(MenuBlank, 3469) x(SkullHead, 3582) +x(SkullJaw2, 3583) x(ExhumedLogo, 3592) x(Energy1, 3604) x(Energy2, 3605) @@ -27,4 +29,100 @@ x(ClockSymbol15, 3620) x(ClockSymbol16, 3621) x(TileLoboLaptop, 3623) x(TileBMGLogo, 3368) +x(LobotomyLogo, 3348) x(TilePIELogo, 3349) +x(Gameover, 3591) + +x(MapPlaque1_01, 3376) +x(MapPlaque1_02, 3378) +x(MapPlaque1_03, 3380) +x(MapPlaque1_04, 3382) +x(MapPlaque1_05, 3384) +x(MapPlaque1_06, 3371) +x(MapPlaque1_07, 3387) +x(MapPlaque1_08, 3389) +x(MapPlaque1_09, 3391) +x(MapPlaque1_10, 3409) +x(MapPlaque1_11, 3393) +x(MapPlaque1_12, 3395) +x(MapPlaque1_13, 3397) +x(MapPlaque1_14, 3399) +x(MapPlaque1_15, 3401) +x(MapPlaque1_16, 3403) +x(MapPlaque1_17, 3405) +x(MapPlaque1_18, 3407) +x(MapPlaque1_19, 3412) +x(MapPlaque1_20, 3415) + +x(MapPlaque2_01, 3377) +x(MapPlaque2_02, 3379) +x(MapPlaque2_03, 3381) +x(MapPlaque2_04, 3383) +x(MapPlaque2_05, 3385) +x(MapPlaque2_06, 3386) +x(MapPlaque2_07, 3388) +x(MapPlaque2_08, 3390) +x(MapPlaque2_09, 3392) +x(MapPlaque2_10, 3410) +x(MapPlaque2_11, 3394) +x(MapPlaque2_12, 3396) +x(MapPlaque2_13, 3398) +x(MapPlaque2_14, 3400) +x(MapPlaque2_15, 3402) +x(MapPlaque2_16, 3404) +x(MapPlaque2_17, 3406) +x(MapPlaque2_18, 3408) +x(MapPlaque2_19, 3413) +x(MapPlaque2_20, 3416) + +x(MapPlaqueText_01, 3411) +x(MapPlaqueText_02, 3414) +x(MapPlaqueText_03, 3417) +x(MapPlaqueText_04, 3420) +x(MapPlaqueText_05, 3423) +x(MapPlaqueText_06, 3426) +x(MapPlaqueText_07, 3429) +x(MapPlaqueText_08, 3432) +x(MapPlaqueText_09, 3435) +x(MapPlaqueText_10, 3418) +x(MapPlaqueText_11, 3438) +x(MapPlaqueText_12, 3441) +x(MapPlaqueText_13, 3444) +x(MapPlaqueText_14, 3447) +x(MapPlaqueText_15, 3450) +x(MapPlaqueText_16, 3453) +x(MapPlaqueText_17, 3456) +x(MapPlaqueText_18, 3459) +x(MapPlaqueText_19, 3419) +x(MapPlaqueText_20, 3421) + +x(MapFire_11, 3484) +x(MapFire_12, 3485) +x(MapFire_13, 3486) +x(MapFire_14, 3487) +x(MapFire_21, 3488) +x(MapFire_22, 3489) +x(MapFire_23, 3490) +x(MapFire_24, 3491) +x(MapFire_31, 3492) +x(MapFire_32, 3493) +x(MapFire_33, 3494) +x(MapFire_34, 3495) + +x(MapBG01, 3353) +x(MapBG02, 3354) +x(MapBG03, 3355) +x(MapBG04, 3356) +x(MapBG05, 3357) +x(MapBG06, 3358) +x(MapBG07, 3359) +x(MapBG08, 3360) +x(MapBG09, 3361) +x(MapBG10, 3362) + +x(TileCinema5, 3449) +x(TileCinema10, 3451) +x(TileCinema11, 3454) +x(TileCinema15, 3446) +x(TileCinemaLose, 3445) +x(TileCinema20, 3448) diff --git a/source/games/exhumed/src/names.h b/source/games/exhumed/src/names.h index 6f0292256..ed087cf72 100644 --- a/source/games/exhumed/src/names.h +++ b/source/games/exhumed/src/names.h @@ -29,136 +29,10 @@ kTileStatusBar = 657, kTile985 = 985, kTile986 = 986, kTile3000 = 3000, -kTile3117 = 3117, +kQueenChunk = 3117, kTile3126 = 3126, -kTile3353 = 3353, -kTile3370 = 3370, -kTile3371 = 3371, -kTile3372 = 3372, -kTile3373 = 3373, -kTile3374 = 3374, -kTile3375 = 3375, -kTile3376 = 3376, -kTile3377 = 3377, -kTile3378 = 3378, -kTile3379 = 3379, -kTile3380 = 3380, -kTile3381 = 3381, -kTile3382 = 3382, -kTile3383 = 3383, -kTile3384 = 3384, -kTile3385 = 3385, -kTile3386 = 3386, -kTile3387 = 3387, -kTile3388 = 3388, -kTile3389 = 3389, -kTile3390 = 3390, -kTile3391 = 3391, -kTile3392 = 3392, -kTile3393 = 3393, -kTile3394 = 3394, -kTile3395 = 3395, -kTile3396 = 3396, -kTile3397 = 3397, -kTile3398 = 3398, -kTile3399 = 3399, -kTile3400 = 3400, -kTile3401 = 3401, -kTile3402 = 3402, -kTile3403 = 3403, -kTile3404 = 3404, -kTile3405 = 3405, -kTile3406 = 3406, -kTile3407 = 3407, -kTile3408 = 3408, -kTile3409 = 3409, -kTile3410 = 3410, -kTile3411 = 3411, -kTile3412 = 3412, -kTile3413 = 3413, -kTile3414 = 3414, -kTile3415 = 3415, -kTile3416 = 3416, -kTile3417 = 3417, -kTile3418 = 3418, -kTile3419 = 3419, -kTile3420 = 3420, -kTile3421 = 3421, -kTile3422 = 3422, -kTile3423 = 3423, -kTile3424 = 3424, -kTile3425 = 3425, -kTile3426 = 3426, -kTile3427 = 3427, -kTile3428 = 3428, -kTile3429 = 3429, -kTile3430 = 3430, -kTile3431 = 3431, -kTile3432 = 3432, -kTile3433 = 3433, -kTile3434 = 3434, -kTile3435 = 3435, -kTile3436 = 3436, -kSkullJaw = 3437, -kTile3438 = 3438, -kTile3439 = 3439, -kTile3440 = 3440, -kTile3441 = 3441, -kTile3443 = 3443, -kTile3444 = 3444, -kTile3445 = 3445, -kTile3446 = 3446, -kTile3447 = 3447, -kTile3448 = 3448, -kTile3449 = 3449, -kTile3450 = 3450, -kTile3451 = 3451, -kTile3452 = 3452, -kTile3453 = 3453, -kTile3454 = 3454, -kTile3455 = 3455, -kTile3456 = 3456, -kTile3457 = 3457, -kTile3458 = 3458, -kTile3459 = 3459, -kTile3462 = 3462, -kTile3463 = 3463, -kTile3464 = 3464, -kTile3467 = 3467, -kTile3470 = 3470, -kTile3471 = 3471, -kTile3472 = 3472, -kTile3473 = 3473, -kTile3474 = 3474, -kTile3475 = 3475, -kTile3476 = 3476, -kTile3477 = 3477, -kTile3478 = 3478, -kTile3479 = 3479, -kTile3480 = 3480, -kTile3481 = 3481, -kTile3482 = 3482, -kTile3483 = 3483, -kTile3484 = 3484, -kTile3485 = 3485, -kTile3486 = 3486, -kTile3487 = 3487, -kTile3488 = 3488, -kTile3489 = 3489, -kTile3490 = 3490, -kTile3491 = 3491, -kTile3492 = 3492, -kTile3493 = 3493, -kTile3494 = 3494, -kTile3495 = 3495, -kTile3496 = 3496, -kTile3497 = 3497, -kTile3498 = 3498, -kTile3499 = 3499, kTile3512 = 3512, kTile3571 = 3571, -kTile3583 = 3583, -kTile3591 = 3591, kTile3593 = 3593, kTile3603 = 3603, kTile4092 = 4092, diff --git a/source/games/exhumed/src/object.cpp b/source/games/exhumed/src/object.cpp index 69701a94e..b73c04415 100644 --- a/source/games/exhumed/src/object.cpp +++ b/source/games/exhumed/src/object.cpp @@ -2254,7 +2254,7 @@ FUNCOBJECT_GOTO: } } - if (currentLevel->levelNumber <= 20 || nStat != kStatExplodeTrigger) + if (!(currentLevel->gameflags & LEVEL_EX_MULTI) || nStat != kStatExplodeTrigger) { runlist_SubRunRec(sprite[nSprite].owner); runlist_SubRunRec(ObjectList[nObject].field_4); @@ -2689,7 +2689,7 @@ void PostProcess() } } - if (currentLevel->levelNumber != kMap20) + if (!(currentLevel->gameflags & LEVEL_EX_COUNTDOWN)) { // esi is i for (i = 0; i < numsectors; i++) diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp index 23976c9de..7bc0d3fc3 100644 --- a/source/games/exhumed/src/player.cpp +++ b/source/games/exhumed/src/player.cpp @@ -417,7 +417,7 @@ void RestartPlayer(short nPlayer) plr->nAir = 100; airpages = 0; - if (currentLevel->levelNumber <= kMap20) + if (!(currentLevel->gameflags & LEVEL_EX_MULTI)) { RestoreMinAmmo(nPlayer); } @@ -570,7 +570,7 @@ void StartDeathSeq(int nPlayer, int nVal) BuildStatusAnim((3 * (nLives - 1)) + 7, 0); } - if (currentLevel->levelNumber > 0) { // if not on the training level + if (!(currentLevel->gameflags & LEVEL_EX_TRAINING)) { // if not on the training level nPlayerLives[nPlayer]--; } @@ -679,7 +679,7 @@ void FuncPlayer(int a, int nDamage, int nRun) { int var_48 = 0; int var_40; - bool mplevel = currentLevel->levelNumber > 20; + bool mplevel = (currentLevel->gameflags & LEVEL_EX_MULTI); short nPlayer = RunData[nRun].nVal; assert(nPlayer >= 0 && nPlayer < kMaxPlayers); @@ -1033,14 +1033,7 @@ void FuncPlayer(int a, int nDamage, int nRun) InitSpiritHead(); PlayerList[nPlayer].nDestVertPan = q16horiz(0); - if (currentLevel->levelNumber == 11) - { - PlayerList[nPlayer].horizon.settarget(46); - } - else - { - PlayerList[nPlayer].horizon.settarget(11); - } + PlayerList[nPlayer].horizon.settarget(currentLevel->ex_ramses_horiz); } } else diff --git a/source/games/exhumed/src/queen.cpp b/source/games/exhumed/src/queen.cpp index 02839887d..0e4b73bda 100644 --- a/source/games/exhumed/src/queen.cpp +++ b/source/games/exhumed/src/queen.cpp @@ -1445,7 +1445,7 @@ void FuncQueen(int a, int nDamage, int nRun) { short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF; - sprite[nChunkSprite].picnum = kTile3117 + (i % 3); + sprite[nChunkSprite].picnum = kQueenChunk + (i % 3); sprite[nChunkSprite].xrepeat = 100; sprite[nChunkSprite].yrepeat = 100; } diff --git a/source/games/exhumed/src/ramses.cpp b/source/games/exhumed/src/ramses.cpp index 546acfcf2..404e9e281 100644 --- a/source/games/exhumed/src/ramses.cpp +++ b/source/games/exhumed/src/ramses.cpp @@ -64,8 +64,6 @@ short nTalkTime = 0; void InitSpiritHead() { - char filename[20]; - nPixels = 0; nSpiritRepeatX = sprite[nSpiritSprite].xrepeat; @@ -137,26 +135,15 @@ void InitSpiritHead() fadecdaudio(); - int nTrack; - - if (currentLevel->levelNumber == 1) - { - nTrack = 3; - } - else - { - nTrack = 7; - } - + int nTrack = currentLevel->ex_ramses_cdtrack; playCDtrack(nTrack, false); StartSwirlies(); - sprintf(filename, "LEV%d.PUP", currentLevel->levelNumber); lNextStateChange = PlayClock; lHeadStartClock = PlayClock; - auto headfd = fileSystem.OpenFileReader(filename); + auto headfd = fileSystem.OpenFileReader(currentLevel->ex_ramses_pup); if (!headfd.isOpen()) { memset(cPupData, 0, sizeof(cPupData)); diff --git a/source/games/exhumed/src/save.cpp b/source/games/exhumed/src/save.cpp index 61976e148..8046808f6 100644 --- a/source/games/exhumed/src/save.cpp +++ b/source/games/exhumed/src/save.cpp @@ -117,7 +117,7 @@ void GameInterface::SerializeGameState(FSerializer& arc) parallaxtype = 2; g_visibility = 1024; - if (currentLevel->levelNumber > 15) + if (currentLevel->gameflags & LEVEL_EX_ALTSOUND) { nSwitchSound = 35; nStoneSound = 23; diff --git a/source/games/exhumed/src/sound.cpp b/source/games/exhumed/src/sound.cpp index d7343d0e9..5f80bf79c 100644 --- a/source/games/exhumed/src/sound.cpp +++ b/source/games/exhumed/src/sound.cpp @@ -724,7 +724,7 @@ void CheckAmbience(short nSector) void UpdateCreepySounds() { - if (currentLevel->levelNumber == 20 || nFreeze || !SoundEnabled()) + if ((currentLevel->gameflags & LEVEL_EX_COUNTDOWN) || nFreeze || !SoundEnabled()) return; spritetype* pSprite = &sprite[PlayerList[nLocalPlayer].nSprite]; nCreepyTimer--; @@ -800,4 +800,35 @@ void PlayGameOverSound(void) PlayLocalSound(StaticSound[kSoundJonLaugh2], 0, false, CHANF_UI); } +DEFINE_ACTION_FUNCTION(_Exhumed, PlayLocalSound) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + PARAM_INT(pitch); + PARAM_BOOL(unatt); + PARAM_INT(flags); + PlayLocalSound(StaticSound[snd], pitch, unatt, EChanFlags::FromInt(flags)); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Exhumed, StopLocalSound, StopLocalSound) +{ + StopLocalSound(); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Exhumed, LocalSoundPlaying) +{ + ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1)); +} + +DEFINE_ACTION_FUNCTION(_Exhumed, PlayCDTrack) +{ + PARAM_PROLOGUE; + PARAM_INT(track); + PARAM_BOOL(loop); + playCDtrack(track, loop); + return 0; +} + END_PS_NS diff --git a/source/games/exhumed/src/view.cpp b/source/games/exhumed/src/view.cpp index 0c499d47a..a98975f63 100644 --- a/source/games/exhumed/src/view.cpp +++ b/source/games/exhumed/src/view.cpp @@ -408,10 +408,7 @@ void DrawView(double smoothRatio, bool sceneonly) if (bSubTitles) { subtitleOverlay.Start(I_GetTimeNS() * (120. / 1'000'000'000)); - if (currentLevel->levelNumber == 1) - subtitleOverlay.ReadyCinemaText(1); - else - subtitleOverlay.ReadyCinemaText(5); + subtitleOverlay.ReadyCinemaText(currentLevel->ex_ramses_text); } inputState.ClearAllInput(); } diff --git a/source/games/sw/all.cpp b/source/games/sw/all.cpp index e3a0e7df0..0e7dd2257 100644 --- a/source/games/sw/all.cpp +++ b/source/games/sw/all.cpp @@ -1,4 +1,3 @@ -#include "src/2d.cpp" #include "src/actor.cpp" #include "src/ai.cpp" #include "src/break.cpp" diff --git a/source/games/sw/src/2d.cpp b/source/games/sw/src/2d.cpp deleted file mode 100644 index 9c982f0c6..000000000 --- a/source/games/sw/src/2d.cpp +++ /dev/null @@ -1,675 +0,0 @@ -//------------------------------------------------------------------------- -/* -Copyright (C) 1997, 2005 - 3D Realms Entertainment - -This file is part of Shadow Warrior version 1.2 - -Shadow Warrior is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -Original Source: 1997 - Frank Maddin and Jim Norwood -Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms -*/ -//------------------------------------------------------------------------- - -#include "ns.h" -#include "screenjob.h" -#include "game.h" -#include "sounds.h" -#include "v_draw.h" -#include "menus.h" -#include "gamecontrol.h" -#include "mapinfo.h" -#include "misc.h" -#include "network.h" -#include "pal.h" - - -BEGIN_SW_NS - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DSWDRealmsScreen : public DSkippableScreenJob -{ -public: - DSWDRealmsScreen() : DSkippableScreenJob(fadein | fadeout) {} - - void OnTick() override - { - if (ticks > 5 * GameTicRate) state = finished; - } - - void Draw(double) override - { - 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); - } -}; - - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void Logo(const CompletionFunc& completion) -{ - StopSound(); - PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); - - static const AnimSound logosound[] = - { - { 1, DIGI_NOMESSWITHWANG }, - { 5, DIGI_INTRO_SLASH }, - { 15, DIGI_INTRO_WHIRL }, - { -1, -1 } - }; - static const int logoframetimes[] = { 360, 8, 128 }; - - if (!userConfig.nologo) - { - JobDesc jobs[3]; - int job = 0; - jobs[job++] = { Create() }; - jobs[job++] = { PlayVideo("sw.anm", logosound, logoframetimes, true)}; - RunScreenJob(jobs, job, completion, true, true); - } - else completion(false); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -DScreenJob* GetFinishAnim(int num) -{ - static const AnimSound serpsound[] = - { - { 1, DIGI_SERPTAUNTWANG }, - { 16, DIGI_SHAREND_TELEPORT }, - { 35, DIGI_WANGTAUNTSERP1 }, - { 51, DIGI_SHAREND_UGLY1 }, - { 64, DIGI_SHAREND_UGLY2 }, - { -1, -1 } - }; - static const int serpzillaframetimes[] = { 16, 16, 140 }; - - static const AnimSound sumosound[] = - { - { 2, DIGI_JG41012 }, - { 30, DIGI_HOTHEADSWITCH }, - { 42, DIGI_HOTHEADSWITCH }, - { 59, DIGI_JG41028 }, - { -1, -1 } - }; - static const int sumoframetimes[] = { 40, 10, 130 }; - - static const AnimSound zillasound[] = - { - { 1, DIGI_ZC1 }, - { 5, DIGI_JG94024 }, - { 14, DIGI_ZC2 }, - { 30, DIGI_ZC3 }, - { 32, DIGI_ZC4 }, - { 37, DIGI_ZC5 }, - { 63, DIGI_Z16043 }, - { 63, DIGI_ZC6 }, - { 63, DIGI_ZC7 }, - { 72, DIGI_ZC7 }, - { 73, DIGI_ZC4 }, - { 77, DIGI_ZC5 }, - { 87, DIGI_ZC8 }, - { 103, DIGI_ZC7 }, - { 108, DIGI_ZC9 }, - { 120, DIGI_JG94039 }, - { -1, -1 } - }; - - static const char* const ANIMname[] = - { - "swend.anm", - "sumocinm.anm", - "zfcin.anm", - }; - - switch (num) - { - case ANIM_SERP: return PlayVideo("swend.anm", serpsound, serpzillaframetimes); - case ANIM_SUMO: return PlayVideo("sumocinm.anm", sumosound, sumoframetimes); - case ANIM_ZILLA:return PlayVideo("zfcin.anm", zillasound, serpzillaframetimes); - default: return nullptr; - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DSWCreditsScreen : public DSkippableScreenJob -{ - enum - { - CREDITS1_PIC = 5111, - CREDITS2_PIC = 5118 - }; - int state = 0; - int starttime; - int curpic; - - void Skipped() override - { - StopSound(); - } - - void Start() override - { - // Lo Wang feel like singing! - PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI); - } - - void OnTick() override - { - if (state == 0) - { - if (!soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE)) - { - starttime = ticks; - state = 1; - StopSound(); - curpic = CREDITS1_PIC; - - // try 14 then 2 then quit - if (!PlaySong(nullptr, ThemeSongs[5], ThemeTrack[5], true)) - { - PlaySong(nullptr, nullptr, 2, true); - } - } - } - else - { - if (ticks >= starttime + 8 * GameTicRate) - { - curpic = CREDITS1_PIC + CREDITS2_PIC - curpic; - starttime = ticks; - } - } - } - - void Draw(double) override - { - twod->ClearScreen(); - if (state == 1) - DrawTexture(twod, tileGetTexture(curpic, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - } -}; - -//--------------------------------------------------------------------------- -// -// Summary screen -// -//--------------------------------------------------------------------------- - -static int BonusPunchSound(short) -{ - PlaySound(DIGI_PLAYERYELL3, v3df_none); - return 0; -} - -static int BonusKickSound(short) -{ - PlaySound(DIGI_PLAYERYELL2, v3df_none); - return 0; -} - -static int BonusGrabSound(short) -{ - PlaySound(DIGI_BONUS_GRAB, v3df_none); - return 0; -} - - -enum -{ - BONUS_SCREEN_PIC = 5120, - BONUS_ANIM = 5121, - BONUS_ANIM_FRAMES = (5159 - 5121), - - BREAK_LIGHT_RATE = 18, - - BONUS_PUNCH = 5121, - BONUS_KICK = 5136, - BONUS_GRAB = 5151, - BONUS_REST = 5121, - - BONUS_TICS = 8, - BONUS_GRAB_TICS = 20, - BONUS_REST_TICS = 50, -}; - -static STATE s_BonusPunch[] = -{ - {BONUS_PUNCH + 0, BONUS_TICS, NULL, &s_BonusPunch[1]}, - {BONUS_PUNCH + 1, BONUS_TICS, NULL, &s_BonusPunch[2]}, - {BONUS_PUNCH + 2, BONUS_TICS, NULL, &s_BonusPunch[3]}, - {BONUS_PUNCH + 2, 0 | SF_QUICK_CALL, BonusPunchSound, &s_BonusPunch[4]}, - {BONUS_PUNCH + 3, BONUS_TICS, NULL, &s_BonusPunch[5]}, - {BONUS_PUNCH + 4, BONUS_TICS, NULL, &s_BonusPunch[6]}, - {BONUS_PUNCH + 5, BONUS_TICS, NULL, &s_BonusPunch[7]}, - {BONUS_PUNCH + 6, BONUS_TICS, NULL, &s_BonusPunch[8]}, - {BONUS_PUNCH + 7, BONUS_TICS, NULL, &s_BonusPunch[9]}, - {BONUS_PUNCH + 8, BONUS_TICS, NULL, &s_BonusPunch[10]}, - {BONUS_PUNCH + 9, BONUS_TICS, NULL, &s_BonusPunch[11]}, - {BONUS_PUNCH + 10, BONUS_TICS, NULL, &s_BonusPunch[12]}, - {BONUS_PUNCH + 11, BONUS_TICS, NULL, &s_BonusPunch[13]}, - {BONUS_PUNCH + 12, BONUS_TICS, NULL, &s_BonusPunch[14]}, - {BONUS_PUNCH + 14, 90, NULL, &s_BonusPunch[15]}, - {BONUS_PUNCH + 14, BONUS_TICS, NULL, &s_BonusPunch[15]}, -}; - -static STATE s_BonusKick[] = -{ - {BONUS_KICK + 0, BONUS_TICS, NULL, &s_BonusKick[1]}, - {BONUS_KICK + 1, BONUS_TICS, NULL, &s_BonusKick[2]}, - {BONUS_KICK + 2, BONUS_TICS, NULL, &s_BonusKick[3]}, - {BONUS_KICK + 2, 0 | SF_QUICK_CALL, BonusKickSound, &s_BonusKick[4]}, - {BONUS_KICK + 3, BONUS_TICS, NULL, &s_BonusKick[5]}, - {BONUS_KICK + 4, BONUS_TICS, NULL, &s_BonusKick[6]}, - {BONUS_KICK + 5, BONUS_TICS, NULL, &s_BonusKick[7]}, - {BONUS_KICK + 6, BONUS_TICS, NULL, &s_BonusKick[8]}, - {BONUS_KICK + 7, BONUS_TICS, NULL, &s_BonusKick[9]}, - {BONUS_KICK + 8, BONUS_TICS, NULL, &s_BonusKick[10]}, - {BONUS_KICK + 9, BONUS_TICS, NULL, &s_BonusKick[11]}, - {BONUS_KICK + 10, BONUS_TICS, NULL, &s_BonusKick[12]}, - {BONUS_KICK + 11, BONUS_TICS, NULL, &s_BonusKick[13]}, - {BONUS_KICK + 12, BONUS_TICS, NULL, &s_BonusKick[14]}, - {BONUS_KICK + 14, 90, NULL, &s_BonusKick[15]}, - {BONUS_KICK + 14, BONUS_TICS, NULL, &s_BonusKick[15]}, -}; - -static STATE s_BonusGrab[] = -{ - {BONUS_GRAB + 0, BONUS_GRAB_TICS, NULL, &s_BonusGrab[1]}, - {BONUS_GRAB + 1, BONUS_GRAB_TICS, NULL, &s_BonusGrab[2]}, - {BONUS_GRAB + 2, BONUS_GRAB_TICS, NULL, &s_BonusGrab[3]}, - {BONUS_GRAB + 2, 0 | SF_QUICK_CALL, BonusGrabSound, &s_BonusGrab[4]}, - {BONUS_GRAB + 3, BONUS_GRAB_TICS, NULL, &s_BonusGrab[5]}, - {BONUS_GRAB + 4, BONUS_GRAB_TICS, NULL, &s_BonusGrab[6]}, - {BONUS_GRAB + 5, BONUS_GRAB_TICS, NULL, &s_BonusGrab[7]}, - {BONUS_GRAB + 6, BONUS_GRAB_TICS, NULL, &s_BonusGrab[8]}, - {BONUS_GRAB + 7, BONUS_GRAB_TICS, NULL, &s_BonusGrab[9]}, - {BONUS_GRAB + 8, BONUS_GRAB_TICS, NULL, &s_BonusGrab[10]}, - {BONUS_GRAB + 9, 90, NULL, &s_BonusGrab[11]}, - {BONUS_GRAB + 9, BONUS_GRAB_TICS, NULL, &s_BonusGrab[11]}, -}; - -static STATE s_BonusRest[] = -{ - {BONUS_REST + 0, BONUS_REST_TICS, NULL, &s_BonusRest[1]}, - {BONUS_REST + 1, BONUS_REST_TICS, NULL, &s_BonusRest[2]}, - {BONUS_REST + 2, BONUS_REST_TICS, NULL, &s_BonusRest[3]}, - {BONUS_REST + 1, BONUS_REST_TICS, NULL, &s_BonusRest[0]}, -}; - -static STATE * s_BonusAnim[] = -{ - s_BonusPunch, - s_BonusKick, - s_BonusGrab -}; - -class DSWLevelSummaryScreen : public DScreenJob -{ - int minutes, seconds, second_tics; - int nextclock = synctics; - STATE * State = s_BonusRest; - int Tics = 0; - -public: - DSWLevelSummaryScreen() - { - second_tics = (PlayClock / 120); - minutes = (second_tics / 60); - seconds = (second_tics % 60); - } -private: - static void gNextState(STATE** State) - { - // Transition to the next state - *State = (*State)->NextState; - - if (TEST((*State)->Tics, SF_QUICK_CALL)) - { - (*(*State)->Animator)(0); - *State = (*State)->NextState; - } - } - - - // Generic state control - static void gStateControl(STATE** State, int* tics) - { - *tics += synctics; - - // Skip states if too much time has passed - while (*tics >= (*State)->Tics) - { - // Set Tics - *tics -= (*State)->Tics; - gNextState(State); - } - - // Call the correct animator - if ((*State)->Animator) - (*(*State)->Animator)(0); - } - - bool OnEvent(event_t* ev) override - { - if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) - { - if (State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)]) - { - State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))]; - Tics = 0; - nextclock = ticks; - } - } - return true; - } - - 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); - MNU_DrawString(170, 30, GStrings("COMPLETED"), 1, 19, 0); - - DrawTexture(twod, tileGetTexture(State->Pic), 158, 86, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_TopLeft, true, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - auto BONUS_LINE = [](int i) { return (50 + ((i) * 20)); }; - - int line = 0; - FString ds; - ds.Format("%s %2d:%02d", GStrings("TXT_YOURTIME"), minutes, seconds); - MNU_DrawString(60, BONUS_LINE(line++), ds, 1, 16); - - if (currentLevel->designerTime > 0) - { - ds.Format("%s %d:%02d", GStrings("TXT_3DRTIME"), currentLevel->designerTime / 60, currentLevel->designerTime % 60); - MNU_DrawString(40, BONUS_LINE(line++), ds, 1, 16); - } - - if (currentLevel->parTime > 0) - { - ds.Format("%s %d:%02d", GStrings("TXT_PARTIME"), currentLevel->parTime / 60, currentLevel->parTime % 60); - MNU_DrawString(40, BONUS_LINE(line++), ds, 1, 16); - } - - // always read secrets and kills from the first player - ds.Format("%s: %d / %d", GStrings("TXT_SECRETS"), Player->SecretsFound, LevelSecrets); - MNU_DrawString(60, BONUS_LINE(line++), ds, 1, 16); - - ds.Format("%s: %d / %d", GStrings("KILLS"), Player->Kills, TotalKillable); - MNU_DrawString(60, BONUS_LINE(line), ds, 1, 16); - - MNU_DrawString(160, 185, GStrings("PRESSKEY"), 1, 19, 0); - } - -}; - -//--------------------------------------------------------------------------- -// -// Deathmatch summary screen -// -//--------------------------------------------------------------------------- - -static constexpr int SM_SIZ(int num) { return (num * 4); } - -enum -{ - STAT_SCREEN_PIC = 5114, - - STAT_START_X = 20, - STAT_START_Y = 85, - STAT_OFF_Y = 9, - STAT_HEADER_Y = 14, - STAT_TABLE_X = (STAT_START_X + SM_SIZ(15)), - STAT_TABLE_XOFF = SM_SIZ(6) -}; - - -class DSWMultiSummaryScreen : public DSkippableScreenJob -{ - short death_total[MAX_SW_PLAYERS_REG]{}; - short kills[MAX_SW_PLAYERS_REG]{}; - - void Skipped() override - { - StopSound(); - } - - void Draw(double) override - { - if (clock == 0) PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]); - - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(STAT_SCREEN_PIC, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - MNU_DrawString(160, 68, GStrings("MULTIPLAYER TOTALS"), 0, 0); - MNU_DrawString(160, 189, GStrings("PRESSKEY"), 0, 0, 0); - - int x = STAT_START_X; - int y = STAT_START_Y; - - // Hm.... how to translate this without messing up the formatting? - MNU_DrawSmallString(x, y, " NAME 1 2 3 4 5 6 7 8 KILLS", 0, 0); - int rows = OrigCommPlayers; - int cols = OrigCommPlayers; - - y += STAT_HEADER_Y; - - FString ds; - for (int i = 0; i < rows; i++) - { - x = STAT_START_X; - auto pp = Player + i; - - ds.Format("%d", i + 1); - MNU_DrawSmallString(x, y, ds, 0, 0); - - ds.Format(" %-13s", pp->PlayerName); - MNU_DrawSmallString(x, y, ds, 0, User[pp->PlayerSprite]->spal); - - x = STAT_TABLE_X; - for (int j = 0; j < cols; j++) - { - int pal = 0; - death_total[j] += pp->KilledPlayer[j]; - - if (i == j) - { - // don't add kill for self or team player - pal = PALETTE_PLAYER0 + 4; - kills[i] -= pp->KilledPlayer[j]; // subtract self kills - } - else if (gNet.TeamPlay) - { - if (User[pp->PlayerSprite]->spal == User[Player[j].PlayerSprite]->spal) - { - // don't add kill for self or team player - pal = PALETTE_PLAYER0 + 4; - kills[i] -= pp->KilledPlayer[j]; // subtract self kills - } - else - kills[i] += pp->KilledPlayer[j]; // kills added here - } - else - { - kills[i] += pp->KilledPlayer[j]; // kills added here - } - - ds.Format("%d", pp->KilledPlayer[j]); - MNU_DrawSmallString(x, y, ds, 0, pal); - x += STAT_TABLE_XOFF; - } - - y += STAT_OFF_Y; - } - - - // Deaths - - x = STAT_START_X; - y += STAT_OFF_Y; - - ds.Format(" %s", GStrings("DEATHS")); - MNU_DrawSmallString(x, y, ds, 0, 0); - x = STAT_TABLE_X; - - for (int j = 0; j < cols; j++) - { - ds.Format("%d", death_total[j]); - MNU_DrawSmallString(x, y, ds, 0, 0); - x += STAT_TABLE_XOFF; - } - - x = STAT_START_X; - y += STAT_OFF_Y; - - // Kills - x = STAT_TABLE_X + SM_SIZ(50); - y = STAT_START_Y + STAT_HEADER_Y; - - for (int i = 0; i < rows; i++) - { - auto pp = Player + i; - - ds.Format("%d", kills[i]); //pp->Kills); - MNU_DrawSmallString(x, y, ds, 0, 0); - - y += STAT_OFF_Y; - } - } -}; - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void StatScreen(int FinishAnim, CompletionFunc completion) -{ - JobDesc jobs[5]; - int job = 0; - - if (FinishAnim) - { - StopSound(); - jobs[job++] = { GetFinishAnim(FinishAnim), []() { soundEngine->StopAllChannels(); } }; - jobs[job++] = { Create() }; - if (FinishAnim == ANIM_ZILLA) - jobs[job++] = { Create() }; - } - else if (gNet.MultiGameType != MULTI_GAME_COMMBAT) - { - jobs[job++] = { Create() }; - } - else - { - jobs[job++] = { Create() }; - } - RunScreenJob(jobs, job, completion, true); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void SybexScreen(CompletionFunc completion) -{ - if (!SW_SHAREWARE || CommEnabled) completion(false); - else - { - JobDesc job = { Create(tileGetTexture(5261), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff) }; - RunScreenJob(&job, 1, completion, true, true); - } -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DSWLoadScreen : public DScreenJob -{ - MapRecord* rec; - -public: - DSWLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {} - - void Draw(double) override - { - const int TITLE_PIC = 2324; - twod->ClearScreen(); - DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE); - - MNU_DrawString(160, 170, /*DemoMode ? GStrings("TXT_LBDEMO") :*/ GStrings("TXT_ENTERING"), 1, 16, 0); - MNU_DrawString(160, 180, rec->DisplayName(), 1, 16, 0); - } -}; - -void loadscreen(MapRecord* rec, CompletionFunc func) -{ - JobDesc job = { Create(rec) }; - RunScreenJob(&job, 1, func); -} - -END_SW_NS diff --git a/source/games/sw/src/cheats.cpp b/source/games/sw/src/cheats.cpp index 6897cdd6a..dc33c96e4 100644 --- a/source/games/sw/src/cheats.cpp +++ b/source/games/sw/src/cheats.cpp @@ -116,7 +116,7 @@ bool NextCheat(cheatseq_t* c) { if (!checkCheat(c)) return false; if (!currentLevel) return true; - auto map = FindMapByLevelNum(currentLevel->levelNumber + 1); + auto map = FindNextMap(currentLevel); if (map) DeferedStartGame(map, -1); return true; } diff --git a/source/games/sw/src/d_menu.cpp b/source/games/sw/src/d_menu.cpp index eff540c1c..e1425e8ec 100644 --- a/source/games/sw/src/d_menu.cpp +++ b/source/games/sw/src/d_menu.cpp @@ -102,22 +102,8 @@ bool GameInterface::StartGame(FNewGameStartup& gs) int handle = 0; int zero = 0; - MapRecord* map; - if (gs.Episode >= 1) - { - if (g_gameType & GAMEFLAG_SHAREWARE) - { - M_StartMessage(GStrings("BUYSW"), 1, NAME_None); - return false; - } - map = FindMapByLevelNum(5); - } - else - map = FindMapByLevelNum(1); - - if (!map) return false; CameraTestMode = false; - StopFX(); + StopAmbientSound(); //InitNewGame(); @@ -140,7 +126,6 @@ bool GameInterface::StartGame(FNewGameStartup& gs) } Net_ClearFifo(); } - DeferedStartGame(map, gs.Skill); return true; } diff --git a/source/games/sw/src/draw.cpp b/source/games/sw/src/draw.cpp index 9f3af9066..524e2ef0b 100644 --- a/source/games/sw/src/draw.cpp +++ b/source/games/sw/src/draw.cpp @@ -1681,7 +1681,9 @@ drawscreen(PLAYERp pp, double smoothratio) if (paused && !M_Active()) { - MNU_DrawString(160, 100, "Game Paused", 0, 0, 0); + auto str = GStrings("Game Paused"); + int w = SmallFont->StringWidth(str); + DrawText(twod, SmallFont, CR_UNDEFINED, 160-w, 100, str, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE); } if (!CommEnabled && TEST(pp->Flags, PF_DEAD)) diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp index 35c6094ef..c49766fc9 100644 --- a/source/games/sw/src/game.cpp +++ b/source/games/sw/src/game.cpp @@ -88,10 +88,6 @@ CVAR(Bool, sw_bunnyrockets, false, CVAR_SERVERINFO | CVAR_CHEAT); // This is a BEGIN_SW_NS -void Logo(const CompletionFunc& completion); -void StatScreen(int FinishAnim, CompletionFunc completion); - - void pClearSpriteList(PLAYERp pp); extern int sw_snd_scratch; @@ -154,7 +150,6 @@ FString ThemeSongs[6]; int ThemeTrack[6]; /// L O C A L P R O T O T Y P E S ///////////////////////////////////////////////////////// -void SybexScreen(void); ///////////////////////////////////////////////////////////////////////////////////////////// #define x(a, b) registerName(#a, b); @@ -253,12 +248,6 @@ void GameInterface::DrawBackground(void) twod->ClearScreen(); DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_Color, shadeToLight(20), TAG_DONE); - - if (CommEnabled) - { - MNU_DrawString(160, 170, "Lo Wang is waiting for other players...", 1, 16, 0); - MNU_DrawString(160, 180, "They are afraid!", 1, 16, 0); - } } //--------------------------------------------------------------------------- @@ -282,9 +271,8 @@ void InitLevelGlobals(void) gNet.TimeLimitClock = gNet.TimeLimit; - serpwasseen = false; - sumowasseen = false; - zillawasseen = false; + + for (auto& b : bosswasseen) b = false; memset(BossSpriteNum,-1,sizeof(BossSpriteNum)); } @@ -533,30 +521,37 @@ static void PlayOrderSound() -void GameInterface::LevelCompleted(MapRecord *map, int skill) +void GameInterface::LevelCompleted(MapRecord* map, int skill) { - //ResetPalette(mpp); - COVER_SetReverb(0); // Reset reverb - Player[myconnectindex].Reverb = 0; - StopSound(); + //ResetPalette(mpp); + COVER_SetReverb(0); // Reset reverb + Player[myconnectindex].Reverb = 0; + StopSound(); STAT_Update(map == nullptr); - StatScreen(FinishAnim, [=](bool) - { + SummaryInfo info{}; + + info.kills = Player->Kills; + info.maxkills = TotalKillable; + info.secrets = Player->SecretsFound; + info.maxsecrets = LevelSecrets; + info.time = PlayClock / 120; + + ShowIntermission(currentLevel, map, &info, [=](bool) + { if (map == nullptr) - { - FinishAnim = false; - PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); - if (SW_SHAREWARE) + { + FinishAnim = false; + PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); + if (isShareware()) { PlayOrderSound(); gameaction = ga_creditsmenu; } - else gameaction = ga_mainmenu; - } - else gameaction = ga_nextlevel; - }); - + else gameaction = ga_mainmenu; + } + else gameaction = ga_nextlevel; + }); } //--------------------------------------------------------------------------- // @@ -584,6 +579,7 @@ void GameInterface::NewGame(MapRecord *map, int skill, bool) ShadowWarrior::NewGame = true; InitLevel(map); InitRunLevel(); + gameaction = ga_level; } //--------------------------------------------------------------------------- @@ -633,17 +629,7 @@ void GameInterface::Render() void GameInterface::Startup() { - if (userConfig.CommandMap.IsNotEmpty()) - { - } - else - { - if (!userConfig.nologo) Logo([](bool) - { - gameaction = ga_mainmenunostopsound; - }); - else gameaction = ga_mainmenu; - } + PlayLogos(ga_mainmenunostopsound, ga_mainmenu, false); } diff --git a/source/games/sw/src/game.h b/source/games/sw/src/game.h index e04f85f80..0b950d29b 100644 --- a/source/games/sw/src/game.h +++ b/source/games/sw/src/game.h @@ -77,7 +77,6 @@ extern GAME_SET gs; enum { DREALMSPAL = 1, - THREED_REALMS_PIC = 2325, MAXMIRRORS = 8, // This is just some, high, blank tile number not used @@ -2225,9 +2224,7 @@ extern short wait_active_check_offset; //extern short Zombies; extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust; extern bool left_foot; -extern bool serpwasseen; -extern bool sumowasseen; -extern bool zillawasseen; +extern bool bosswasseen[3]; extern short BossSpriteNum[3]; extern int ChopTics; extern short Bunny_Count; diff --git a/source/games/sw/src/menus.cpp b/source/games/sw/src/menus.cpp index ef66a628e..4bf74c606 100644 --- a/source/games/sw/src/menus.cpp +++ b/source/games/sw/src/menus.cpp @@ -222,30 +222,4 @@ void DoPaletteFlash(PLAYERp pp) } - -bool MNU_ShareWareMessage() -{ - const char* extra_text; - short w, h; - - if (SW_SHAREWARE) - { - extra_text = "Be sure to call 800-3DREALMS today"; - MNU_DrawString(160, 110, extra_text, 1, 16, 0); - extra_text = "and order the game."; - MNU_DrawString(160, 120, extra_text, 1, 16, 0); - extra_text = "You are only playing the first "; - MNU_DrawString(160, 130, extra_text, 1, 16, 0); - extra_text = "four levels, and are missing most"; - MNU_DrawString(160, 140, extra_text, 1, 16, 0); - extra_text = "of the game, weapons and monsters."; - MNU_DrawString(160, 150, extra_text, 1, 16, 0); - extra_text = "See the ordering information."; - MNU_DrawString(160, 160, extra_text, 1, 16, 0); - //SET(item->flags, mf_disabled); - } - return true; -} - - END_SW_NS diff --git a/source/games/sw/src/menus.h b/source/games/sw/src/menus.h index 7105d669b..6ee40e181 100644 --- a/source/games/sw/src/menus.h +++ b/source/games/sw/src/menus.h @@ -32,7 +32,6 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms BEGIN_SW_NS -void MNU_DrawString(int x, int y, const char* string, int shade, int pal, int align = -1); void MNU_DrawSmallString(int x, int y, const char* string, int shade, int pal, int align = -1, double alpha = 1); END_SW_NS diff --git a/source/games/sw/src/namelist.h b/source/games/sw/src/namelist.h index 30ffa5fc9..40809cc19 100644 --- a/source/games/sw/src/namelist.h +++ b/source/games/sw/src/namelist.h @@ -66,6 +66,7 @@ x(BLACK___, 2306) x(FragBarErase, 2375) x(FragBarErase2, 2376) x(MENUBAR, 2427) +x(THREED_REALMS_PIC, 2325) x(MIRROR, 340) x(FLOORMIRROR, 341) @@ -168,3 +169,50 @@ x(CREDITS2, 5118) x(SUPPORT, 4979) x(ONLINE, 5113) +x(STAT_SCREEN_PIC, 5114) +x(BONUS_SCREEN_PIC, 5120) + +x(BONUS_PUNCH00, 5121) +x(BONUS_PUNCH01, 5122) +x(BONUS_PUNCH02, 5123) +x(BONUS_PUNCH03, 5124) +x(BONUS_PUNCH04, 5125) +x(BONUS_PUNCH05, 5126) +x(BONUS_PUNCH06, 5127) +x(BONUS_PUNCH07, 5128) +x(BONUS_PUNCH08, 5129) +x(BONUS_PUNCH09, 5130) +x(BONUS_PUNCH10, 5131) +x(BONUS_PUNCH11, 5132) +x(BONUS_PUNCH12, 5133) +x(BONUS_PUNCH13, 5134) +x(BONUS_PUNCH14, 5135) + +x(BONUS_KICK00, 5136) +x(BONUS_KICK01, 5137) +x(BONUS_KICK02, 5138) +x(BONUS_KICK03, 5139) +x(BONUS_KICK04, 5140) +x(BONUS_KICK05, 5141) +x(BONUS_KICK06, 5142) +x(BONUS_KICK07, 5143) +x(BONUS_KICK08, 5144) +x(BONUS_KICK09, 5145) +x(BONUS_KICK10, 5146) +x(BONUS_KICK11, 5147) +x(BONUS_KICK12, 5148) +x(BONUS_KICK13, 5149) +x(BONUS_KICK14, 5150) + +x(BONUS_GRAB00, 5151) +x(BONUS_GRAB01, 5152) +x(BONUS_GRAB02, 5153) +x(BONUS_GRAB03, 5154) +x(BONUS_GRAB04, 5155) +x(BONUS_GRAB05, 5156) +x(BONUS_GRAB06, 5157) +x(BONUS_GRAB07, 5158) +x(BONUS_GRAB08, 5159) +x(BONUS_GRAB09, 5160) + +x(TITLE_PIC, 2324) diff --git a/source/games/sw/src/player.cpp b/source/games/sw/src/player.cpp index 997e1fa34..3ae5d7172 100644 --- a/source/games/sw/src/player.cpp +++ b/source/games/sw/src/player.cpp @@ -7041,9 +7041,7 @@ void MultiPlayLimits(void) gNet.TimeLimitClock = gNet.TimeLimit; MapRecord *next = nullptr; - // do not increment if level is 23 thru 28 (should be done smarter.) - if (currentLevel->levelNumber <= 22) - next = FindMapByLevelNum(currentLevel->levelNumber + 1); + next = FindNextMap(currentLevel); ChangeLevel(next, -1); } } @@ -7203,7 +7201,7 @@ domovethings(void) MapRecord *map = nullptr; if (FinishAnim == ANIM_SUMO) { - map = FindMapByLevelNum(currentLevel->levelNumber+1); + map = FindNextMap(currentLevel); } ChangeLevel(map, -1); } diff --git a/source/games/sw/src/save.cpp b/source/games/sw/src/save.cpp index e71037235..efe2be333 100644 --- a/source/games/sw/src/save.cpp +++ b/source/games/sw/src/save.cpp @@ -78,9 +78,7 @@ extern int FinishAnim; extern int GameVersion; //extern short Zombies; -extern bool serpwasseen; -extern bool sumowasseen; -extern bool zillawasseen; +extern bool bosswasseen[3]; extern short BossSpriteNum[3]; #define ANIM_SAVE 1 @@ -1269,9 +1267,7 @@ void GameInterface::SerializeGameState(FSerializer& arc) ("GodMode", GodMode) ("FinishTimer", FinishTimer) ("FinishAnim", FinishAnim) - ("serpwasseen", serpwasseen) - ("sumowasseen", sumowasseen) - ("zillawasseen", zillawasseen) + .Array("bosswasseen", bosswasseen, 3) .Array("BossSpriteNum", BossSpriteNum, 3); arc.Array("tracks", Track, countof(Track)) ; diff --git a/source/games/sw/src/scrip2.cpp b/source/games/sw/src/scrip2.cpp index 9a9aa71c1..fc619ac5d 100644 --- a/source/games/sw/src/scrip2.cpp +++ b/source/games/sw/src/scrip2.cpp @@ -448,6 +448,7 @@ void LoadCustomInfoFromScript(const char *filename) { curMap = AllocateMap(); curMap->levelNumber = mapno; + curMap->cluster = mapno < 5 ? 1 : 2; } if (sc.CheckString("{")) @@ -524,13 +525,19 @@ void LoadCustomInfoFromScript(const char *filename) case CM_TITLE: { sc.MustGetString(); - if (curep != -1) gVolumeNames[curep] = sc.String; + auto vol = MustFindVolume(curep); + auto clust = MustFindCluster(curep); + vol->name = clust->name = sc.String; break; } case CM_SUBTITLE: { sc.MustGetString(); - if (curep != -1) gVolumeSubtitles[curep] = sc.String; + if (curep != -1) + { + auto vol = MustFindVolume(curep); + vol->subtitle = sc.String; + } break; } default: @@ -801,6 +808,12 @@ void LoadCustomInfoFromScript(const char *filename) break; } } + auto vol0 = MustFindVolume(0); + auto vol1 = MustFindVolume(1); + auto map1 = FindMapByLevelNum(1); + auto map5 = FindMapByLevelNum(5); + if (vol0 && map1) vol0->startmap = map1->labelName; + if (vol1 && map5) vol1->startmap = map5->labelName; } END_SW_NS diff --git a/source/games/sw/src/sector.cpp b/source/games/sw/src/sector.cpp index 892bd91e8..dbd0f8844 100644 --- a/source/games/sw/src/sector.cpp +++ b/source/games/sw/src/sector.cpp @@ -1913,7 +1913,7 @@ OperateSprite(short SpriteNum, short player_is_operating) if (sp->hitag) map = FindMapByLevelNum(sp->hitag); else - map = FindMapByLevelNum(currentLevel->levelNumber + 1); + map = FindNextMap(currentLevel); ChangeLevel(map, -1); return true; @@ -2095,7 +2095,7 @@ OperateTripTrigger(PLAYERp pp) if (sectp->hitag) map = FindMapByLevelNum(sectp->hitag); else - map = FindMapByLevelNum(currentLevel->levelNumber + 1); + map = FindNextMap(currentLevel); ChangeLevel(map, -1); break; } diff --git a/source/games/sw/src/sounds.cpp b/source/games/sw/src/sounds.cpp index 8d0a364b3..16ce58cf3 100644 --- a/source/games/sw/src/sounds.cpp +++ b/source/games/sw/src/sounds.cpp @@ -48,6 +48,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "serializer.h" #include "gamecontrol.h" #include "gamestate.h" +#include "vm.h" BEGIN_SW_NS @@ -981,5 +982,37 @@ void StopFX() if (soundEngine) soundEngine->StopAllChannels(); } +DEFINE_ACTION_FUNCTION(_SW, PlaySound) +{ + PARAM_PROLOGUE; + PARAM_INT(sound); + PARAM_INT(vflags); + PARAM_INT(channel); + PARAM_INT(cflags); + PlaySound(sound, Voc3D_Flags(vflags), channel, EChanFlags::FromInt(cflags)); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_SW, StopSound, StopSound) +{ + StopSound(); + return 0; +} + +DEFINE_ACTION_FUNCTION(_SW, IsSoundPlaying) +{ + PARAM_PROLOGUE; + PARAM_INT(channel); + ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, channel)); + +} + +DEFINE_ACTION_FUNCTION(_SW, PlaySong) +{ + PARAM_PROLOGUE; + PARAM_INT(song); + PlaySong(nullptr, ThemeSongs[song], ThemeTrack[song], true); + return 0; +} END_SW_NS diff --git a/source/games/sw/src/sprite.cpp b/source/games/sw/src/sprite.cpp index c35ad0e52..fb93034c1 100644 --- a/source/games/sw/src/sprite.cpp +++ b/source/games/sw/src/sprite.cpp @@ -1080,10 +1080,8 @@ ActorTestSpawn(SPRITEp sp) sp->picnum == SAILORGIRL_R0) && (g_gameType & GAMEFLAG_ADDON)) return true; // spawn Bouncing Betty (mine) in TD map 09 Warehouse -#if 0 // needs to be done smarter. - if (sp->picnum == 817 && swGetAddon() == 2 && currentLevel->levelNumber == 9) + if (sp->picnum == 817 && (currentLevel->flags & LEVEL_SW_SPAWNMINES)) return true; -#endif return false; } diff --git a/source/games/sw/src/sumo.cpp b/source/games/sw/src/sumo.cpp index df5ed88e6..628770099 100644 --- a/source/games/sw/src/sumo.cpp +++ b/source/games/sw/src/sumo.cpp @@ -41,9 +41,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms BEGIN_SW_NS extern uint8_t playTrack; -bool serpwasseen = false; -bool sumowasseen = false; -bool zillawasseen = false; +bool bosswasseen[3]; short BossSpriteNum[3] = {-1,-1,-1}; @@ -819,22 +817,19 @@ BossHealthMeter(void) int y; extern bool NoMeters; short health; - bool bosswasseen; static bool triedplay = false; if (NoMeters) return; - if (currentLevel->levelNumber != 20 && currentLevel->levelNumber != 4 && currentLevel->levelNumber != 11 && currentLevel->levelNumber != 5) return; + if (!(currentLevel->gameflags & (LEVEL_SW_BOSSMETER_SERPENT | LEVEL_SW_BOSSMETER_SUMO | LEVEL_SW_BOSSMETER_ZILLA))) return; // Don't draw bar for other players if (pp != Player+myconnectindex) return; // all enemys - if ((currentLevel->levelNumber == 20 && (BossSpriteNum[0] == -1 || BossSpriteNum[1] == -1 || BossSpriteNum[2] == -1)) || - (currentLevel->levelNumber == 4 && BossSpriteNum[0] == -1) || - (currentLevel->levelNumber == 5 && BossSpriteNum[0] == -1) || - (currentLevel->levelNumber == 11 && BossSpriteNum[1] == -1)) + if (currentLevel->gameflags & (LEVEL_SW_BOSSMETER_SERPENT|LEVEL_SW_BOSSMETER_SUMO|LEVEL_SW_BOSSMETER_ZILLA) && + BossSpriteNum[0] <= -1 && BossSpriteNum[1] <= -1 && BossSpriteNum[2] <= -1) { StatIterator it(STAT_ENEMY); while ((i = it.NextIndex()) >= 0) @@ -844,11 +839,11 @@ BossHealthMeter(void) if ((u->ID == SERP_RUN_R0 || u->ID == SUMO_RUN_R0 || u->ID == ZILLA_RUN_R0) && sp->pal != 16) { - if (u->ID == SERP_RUN_R0) + if (u->ID == SERP_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_BOSSMETER_SERPENT)) BossSpriteNum[0] = i; - else if (u->ID == SUMO_RUN_R0) + else if (u->ID == SUMO_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_BOSSMETER_SUMO)) BossSpriteNum[1] = i; - else if (u->ID == ZILLA_RUN_R0) + else if (u->ID == ZILLA_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_BOSSMETER_ZILLA)) BossSpriteNum[2] = i; } } @@ -857,45 +852,39 @@ BossHealthMeter(void) if (BossSpriteNum[0] <= -1 && BossSpriteNum[1] <= -1 && BossSpriteNum[2] <= -1) return; - // Frank, good optimization for other levels, but it broke level 20. :( - // I kept this but had to add a fix. - bosswasseen = serpwasseen || sumowasseen || zillawasseen; // Only show the meter when you can see the boss - if ((currentLevel->levelNumber == 20 && (!serpwasseen || !sumowasseen || !zillawasseen)) || !bosswasseen) + for (i=0; i<3; i++) { - for (i=0; i<3; i++) + if (BossSpriteNum[i] >= 0 && !bosswasseen[i]) { - if (BossSpriteNum[i] >= 0) - { - sp = &sprite[BossSpriteNum[i]]; - u = User[BossSpriteNum[i]].Data(); + sp = &sprite[BossSpriteNum[i]]; + u = User[BossSpriteNum[i]].Data(); - if (cansee(sp->x, sp->y, SPRITEp_TOS(sp), sp->sectnum, pp->posx, pp->posy, pp->posz - Z(40), pp->cursectnum)) + if (cansee(sp->x, sp->y, SPRITEp_TOS(sp), sp->sectnum, pp->posx, pp->posy, pp->posz - Z(40), pp->cursectnum)) + { + if (i == 0 && !bosswasseen[0]) { - if (i == 0 && !serpwasseen) + bosswasseen[0] = true; + if (!SW_SHAREWARE) { - serpwasseen = true; - if (!SW_SHAREWARE) - { - PlaySong(nullptr, ThemeSongs[2], ThemeTrack[2], true); - } + PlaySong(nullptr, ThemeSongs[2], ThemeTrack[2], true); } - else if (i == 1 && !sumowasseen) + } + else if (i == 1 && !bosswasseen[1]) + { + bosswasseen[1] = true; + if (!SW_SHAREWARE) { - sumowasseen = true; - if (!SW_SHAREWARE) - { - PlaySong(nullptr, ThemeSongs[3], ThemeTrack[3], true); - } + PlaySong(nullptr, ThemeSongs[3], ThemeTrack[3], true); } - else if (i == 2 && !zillawasseen) + } + else if (i == 2 && !bosswasseen[2]) + { + bosswasseen[2] = true; + if (!SW_SHAREWARE) { - zillawasseen = true; - if (!SW_SHAREWARE) - { - PlaySong(nullptr, ThemeSongs[4], ThemeTrack[4], true); - } + PlaySong(nullptr, ThemeSongs[4], ThemeTrack[4], true); } } } @@ -906,17 +895,17 @@ BossHealthMeter(void) for (i=0; i<3; i++) { - if (i == 0 && (!serpwasseen || BossSpriteNum[0] < 0)) + if (i == 0 && (!bosswasseen[0] || BossSpriteNum[0] < 0)) continue; - if (i == 1 && (!sumowasseen || BossSpriteNum[1] < 0)) + if (i == 1 && (!bosswasseen[1] || BossSpriteNum[1] < 0)) continue; - if (i == 2 && (!zillawasseen || BossSpriteNum[2] < 0)) + if (i == 2 && (!bosswasseen[2] || BossSpriteNum[2] < 0)) continue; sp = &sprite[BossSpriteNum[i]]; u = User[BossSpriteNum[i]].Data(); - if (u->ID == SERP_RUN_R0 && serpwasseen) + if (u->ID == SERP_RUN_R0 && bosswasseen[0]) { if (Skill == 0) health = 1100; else if (Skill == 1) health = 2200; @@ -924,7 +913,7 @@ BossHealthMeter(void) health = HEALTH_SERP_GOD; meterunit = health / 30; } - else if (u->ID == SUMO_RUN_R0 && sumowasseen) + else if (u->ID == SUMO_RUN_R0 && bosswasseen[1]) { if (Skill == 0) health = 2000; else if (Skill == 1) health = 4000; @@ -932,7 +921,7 @@ BossHealthMeter(void) health = 6000; meterunit = health / 30; } - else if (u->ID == ZILLA_RUN_R0 && zillawasseen) + else if (u->ID == ZILLA_RUN_R0 && bosswasseen[2]) { if (Skill == 0) health = 2000; else if (Skill == 1) health = 4000; @@ -963,10 +952,10 @@ BossHealthMeter(void) else y = 30; - if (currentLevel->levelNumber == 20 && numplayers >= 2) + if ((currentLevel->gameflags & (LEVEL_SW_BOSSMETER_SUMO|LEVEL_SW_BOSSMETER_ZILLA)) == (LEVEL_SW_BOSSMETER_SUMO | LEVEL_SW_BOSSMETER_ZILLA) && numplayers >= 2) { - if (u->ID == SUMO_RUN_R0 && sumowasseen) y += 10; - else if (u->ID == ZILLA_RUN_R0 && zillawasseen) y += 20; + if (u->ID == SUMO_RUN_R0 && bosswasseen[1]) y += 10; + else if (u->ID == ZILLA_RUN_R0 && bosswasseen[2]) y += 20; } if (metertics <= 12 && metertics > 6) diff --git a/source/games/sw/src/text.cpp b/source/games/sw/src/text.cpp index 751e0ee3a..53d806abc 100644 --- a/source/games/sw/src/text.cpp +++ b/source/games/sw/src/text.cpp @@ -108,24 +108,6 @@ void InitFonts() // //--------------------------------------------------------------------------- -void MNU_DrawString(int x, int y, const char* string, int shade, int pal, int align) -{ - if (align > -1) - { - int w = SmallFont->StringWidth(string); - if (align == 0) x -= w / 2; - else x -= w; - } - DrawText(twod, SmallFont, CR_UNDEFINED, x, y, string, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_Color, shadeToLight(shade), DTA_TranslationIndex, TRANSLATION(Translation_Remap, pal), TAG_DONE); -} - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - void MNU_DrawSmallString(int x, int y, const char* string, int shade, int pal, int align, double alpha) { if (align > -1) diff --git a/source/games/sw/src/weapon.cpp b/source/games/sw/src/weapon.cpp index d2ea1f4d6..c0eaf4278 100644 --- a/source/games/sw/src/weapon.cpp +++ b/source/games/sw/src/weapon.cpp @@ -5322,7 +5322,7 @@ ActorHealth(short SpriteNum, short amt) u->Health += amt; - if (u->ID == SERP_RUN_R0 && sp->pal != 16 && currentLevel->levelNumber == 4) + if (u->ID == SERP_RUN_R0 && sp->pal != 16 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_SERPENT)) { if (u->Health < u->MaxHealth/2) { @@ -5332,7 +5332,7 @@ ActorHealth(short SpriteNum, short amt) } } - if (u->ID == SUMO_RUN_R0 && currentLevel->levelNumber == 11) + if (u->ID == SUMO_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_SUMO)) { if (u->Health <= 0) { @@ -5341,7 +5341,7 @@ ActorHealth(short SpriteNum, short amt) } } - if (u->ID == ZILLA_RUN_R0 && currentLevel->levelNumber == 20) + if (u->ID == ZILLA_RUN_R0 && (currentLevel->gameflags & LEVEL_SW_DEATHEXIT_ZILLA)) { if (u->Health <= 0) //if (u->Health < u->MaxHealth) diff --git a/source/glbackend/glbackend.cpp b/source/glbackend/glbackend.cpp index e594f97c1..258bad5d6 100644 --- a/source/glbackend/glbackend.cpp +++ b/source/glbackend/glbackend.cpp @@ -426,6 +426,8 @@ void videoShowFrame(int32_t w) }); screen->Update(); screen->mVertexData->Reset(); + screen->mViewpoints->Clear(); + videoSetBrightness(0); // immediately reset this after rendering so that the value doesn't stick around in the backend. // After finishing the frame, reset everything for the next frame. This needs to be done better. diff --git a/wadsrc/static/engine/grpinfo.txt b/wadsrc/static/engine/grpinfo.txt index 9f487418b..7c0bc7f73 100644 --- a/wadsrc/static/engine/grpinfo.txt +++ b/wadsrc/static/engine/grpinfo.txt @@ -249,7 +249,7 @@ grpinfo name "Duke Caribbean: Life's a Beach" size 22213819 crc DUKECB_CRC - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEVACA dependency DUKE15_CRC gamefilter "Duke.Vacation" FgColor 0x00004f @@ -263,7 +263,7 @@ grpinfo scriptname "VACATION.CON" size 22397273 crc 0x65B5F690 - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKEVACA dependency DUKE15_CRC gamefilter "Duke.Vacation" FgColor 0x00004f @@ -277,7 +277,7 @@ grpinfo scriptname "NWINTER.CON" size 16169365 crc DUKENW_CRC - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKENW dependency DUKE15_CRC gamefilter "Duke.NWinter" FgColor 0 @@ -291,7 +291,7 @@ grpinfo scriptname "NWINTER.CON" size 10965909 crc 0xC7EFBFA9 - flags GAMEFLAG_DUKE|GAMEFLAG_ADDON + flags GAMEFLAG_DUKE|GAMEFLAG_ADDON|GAMEFLAG_DUKENW dependency DUKE15_CRC gamefilter "Duke.NWinter" FgColor 0 @@ -423,7 +423,7 @@ grpinfo name "BLOOD: Cryptic Passage" scriptname "CRYPTIC.INI" mustcontain "CRYPTIC.INI", "CP01.MAP", "CP02.MAP" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" @@ -436,7 +436,7 @@ addon name "BLOOD: Cryptic Passage" scriptname "CRYPTIC.INI" mustcontain "CRYPTIC.INI", "CP01.MAP", "CP02.MAP" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" @@ -450,7 +450,7 @@ addon scriptname "CRYPTIC.INI" mustcontain "cryptic/CRYPTIC.INI", "cryptic/CP01.MAP", "cryptic/CP02.MAP" loadgrp "cryptic" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC loadart "CPART07.AR_", "CPART15.AR_" gamefilter "Blood.Cryptic" @@ -464,7 +464,7 @@ addon scriptname "CRYPTIC.INI" mustcontain "addons/Cryptic Passage/CRYPTIC.INI", "addons/Cryptic Passage/CP01.MAP", "addons/Cryptic Passage/CP02.MAP" loadgrp "addons/Cryptic Passage" - flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON + flags GAMEFLAG_BLOOD|GAMEFLAG_ADDON|GAMEFLAG_BLOODCP dependency BLOOD_CRC gamefilter "Blood.Cryptic" GameID "CrypticPassage" @@ -573,7 +573,7 @@ grpinfo grpinfo { name "Shadow Warrior: Wanton Destruction" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWWANTON crc SWWD_CRC size 48698128 defname "sw.def" @@ -585,7 +585,7 @@ grpinfo grpinfo { name "Shadow Warrior: Wanton Destruction" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWWANTON crc 0x8B6E8011 size 48698128 defname "sw.def" @@ -597,7 +597,7 @@ grpinfo grpinfo { name "Shadow Warrior: Wanton Destruction (ProAsm)" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWWANTON crc 0x39893EF4 size 9562689 defname "sw.def" @@ -610,7 +610,7 @@ grpinfo grpinfo { name "Shadow Warrior: Twin Dragon" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWTWINDRAG crc SWTD_CRC size 12499012 defname "twindrag.def" @@ -623,7 +623,7 @@ grpinfo grpinfo { name "Shadow Warrior: Twin Dragon" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWTWINDRAG crc 0xB5B71277 size 6236287 defname "twindrag.def" @@ -635,7 +635,7 @@ grpinfo grpinfo { name "Shadow Warrior: Twin Dragon" - flags GAMEFLAG_SW|GAMEFLAG_ADDON + flags GAMEFLAG_SW|GAMEFLAG_ADDON|GAMEFLAG_SWTWINDRAG crc 0xACC8DCDE size 6235578 defname "twindrag.def" @@ -678,7 +678,7 @@ grpinfo name "Redneck Rampage: Suckin' Grits on Route 66" scriptname "GAME66.CON" mustcontain "TILESA66.ART", "TILESB66.ART", "ROUTE66/CARNIVAL.MAP", "ROUTE66/TRUCKSTP.MAP", "GAME66.CON" - flags GAMEFLAG_RR|GAMEFLAG_ADDON + flags GAMEFLAG_RR|GAMEFLAG_ADDON|GAMEFLAG_ROUTE66 dependency RR_CRC loadart "TILESA66.ART", "TILESB66.ART" // replaces TILES009 and TILES023. gamefilter "Redneck.Route66" @@ -692,7 +692,7 @@ addon name "Redneck Rampage: Suckin' Grits on Route 66" scriptname "GAME66.CON" mustcontain "TILESA66.ART", "TILESB66.ART", "ROUTE66/CARNIVAL.MAP", "ROUTE66/TRUCKSTP.MAP", "GAME66.CON" - flags GAMEFLAG_RR|GAMEFLAG_ADDON + flags GAMEFLAG_RR|GAMEFLAG_ADDON|GAMEFLAG_ROUTE66 dependency RR_CRC loadart "TILESA66.ART", "TILESB66.ART" // replaces TILES009 and TILES023. gamefilter "Redneck.Route66" diff --git a/wadsrc/static/filter/blood/engine/rmapinfo.txt b/wadsrc/static/filter/blood/engine/rmapinfo.txt new file mode 100644 index 000000000..f05bb05c4 --- /dev/null +++ b/wadsrc/static/filter/blood/engine/rmapinfo.txt @@ -0,0 +1,21 @@ + +// Cutscene definitions for Blood + +cutscenes +{ + intro + { + function = BloodCutscenes.BuildIntro + } + loadscreen + { + function = BloodCutscenes.BuildLoading + } + +} + +gameinfo +{ + summaryscreen = BloodCutscenes.BuildSPSummary + mpsummaryscreen = BloodCutscenes.BuildMPSummary +} diff --git a/wadsrc/static/filter/duke/engine/rmapinfo.txt b/wadsrc/static/filter/duke/engine/rmapinfo.txt new file mode 100644 index 000000000..4462a9795 --- /dev/null +++ b/wadsrc/static/filter/duke/engine/rmapinfo.txt @@ -0,0 +1,74 @@ +//This sets up the missing things that cannot be inferred from the setup in the .CON files. + +cluster 1 +{ + outro + { + function = DukeCutscenes.BuildE1End + } +} + +cluster 2 +{ + outro + { + function = DukeCutscenes.BuildE2End + } + interbackground = "BONUSSCREEN2" +} + +cluster 3 +{ + outro + { + function = DukeCutscenes.BuildE3End + } +} + +cluster 4 +{ + outro + { + function = DukeCutscenes.BuildE4End + } +} + +cluster 5 +{ + outro + { + function = DukeCutscenes.BuildE5End + } +} + +map { 4, 1 } +{ + intro + { + function = DukeCutscenes.BuildE4Intro + } +} + +cutscenes +{ + intro + { + function = DukeCutscenes.BuildIntro + } + + sharewareend + { + function = DukeCutscenes.BuildSharewareEnd + } + + loadscreen + { + function = DukeCutscenes.BuildLoading + } +} + +gameinfo +{ + summaryscreen = DukeCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary +} \ No newline at end of file diff --git a/wadsrc/static/filter/exhumed/engine/rmapinfo.txt b/wadsrc/static/filter/exhumed/engine/rmapinfo.txt new file mode 100644 index 000000000..5fafd97b6 --- /dev/null +++ b/wadsrc/static/filter/exhumed/engine/rmapinfo.txt @@ -0,0 +1,306 @@ + +episode LEV1 +{ + name = "Exhumed" + noskillmenu +} + +episode LEV0 +{ + name = "$TXT_EX_MAP00" + noskillmenu +} + +cluster 1 +{ + name = "Exhumed" + gameover + { + function = ExhumedCutscenes.BuildGameoverScene + } +} + +cluster 2 +{ + name = "$TXT_EX_MAP00" +} + +cluster 3 +{ + name = "Multiplayer" +} + +cluster 4 +{ + gameover + { + function = ExhumedCutscenes.BuildCinemaLose + } +} + +map lev0 "$TXT_EX_MAP00" +{ + cdtrack = 11 + ex_training + cluster = 2 +} + +map lev1 "$TXT_EX_MAP01" +{ + cdtrack = 11 + cluster = 1 + ex_ramses_cdtrack = 3 + ex_ramses_pup = "lev1.pup" + ex_ramses_text = "$TXT_EX_CINEMA1" +} + +map lev2 "$TXT_EX_MAP02" +{ + cdtrack = 12 + cluster = 1 +} + +map lev3 "$TXT_EX_MAP03" +{ + cdtrack = 13 + cluster = 1 +} + +map lev4 "$TXT_EX_MAP04" +{ + cdtrack = 14 + cluster = 1 +} + +map lev5 "$TXT_EX_MAP05" +{ + cdtrack = 15 + cluster = 1 + intro + { + function = ExhumedCutscenes.BuildCinemaBefore5 + } +} + +map lev6 "$TXT_EX_MAP06" +{ + cdtrack = 16 + cluster = 1 +} + +map lev7 "$TXT_EX_MAP07" +{ + cdtrack = 17 + cluster = 1 +} + +map lev8 "$TXT_EX_MAP08" +{ + cdtrack = 18 + cluster = 1 +} + +map lev9 "$TXT_EX_MAP09" +{ + cdtrack = 11 + cluster = 1 +} + +map lev10 "$TXT_EX_MAP10" +{ + cdtrack = 12 + cluster = 1 + outro + { + function = ExhumedCutscenes.BuildCinemaAfter10 + } +} + +map lev11 "$TXT_EX_MAP11" +{ + cdtrack = 13 + cluster = 1 + ex_ramses_cdtrack = 7 + ex_ramses_pup = "lev11.pup" + ex_ramses_text = "$TXT_EX_CINEMA5" + ex_ramses_horiz = 46 + intro + { + function = ExhumedCutscenes.BuildCinemaBefore11 + } +} + +map lev12 "$TXT_EX_MAP12" +{ + cdtrack = 14 + cluster = 1 +} + +map lev13 "$TXT_EX_MAP13" +{ + cdtrack = 15 + cluster = 1 +} + +map lev14 "$TXT_EX_MAP14" +{ + cdtrack = 16 + cluster = 1 +} + +map lev15 "$TXT_EX_MAP15" +{ + cdtrack = 17 + cluster = 1 + outro + { + function = ExhumedCutscenes.BuildCinemaAfter15 + } +} + +map lev16 "$TXT_EX_MAP16" +{ + cdtrack = 18 + cluster = 1 + ex_altsound +} + +map lev17 "$TXT_EX_MAP17" +{ + cdtrack = 11 + cluster = 1 + ex_altsound +} + +map lev18 "$TXT_EX_MAP18" +{ + cdtrack = 12 + cluster = 1 + ex_altsound +} + +map lev19 "$TXT_EX_MAP19" +{ + cdtrack = 13 + cluster = 1 + ex_altsound +} + +map lev20 "$TXT_EX_MAP20" +{ + cdtrack = 14 + cluster = 4 + next = "-" + ex_altsound + ex_countdown + intro + { + function = ExhumedCutscenes.BuildCinemaBefore20 + } + outro + { + function = ExhumedCutscenes.BuildCinemaAfter20 + } +} + +map lev21 "$TXT_EX_MAP21" +{ + cdtrack = 15 + cluster = 3 + ex_multi +} + +map lev22 "$TXT_EX_MAP22" +{ + cdtrack = 16 + cluster = 3 + ex_multi +} + +map lev23 "$TXT_EX_MAP23" +{ + cdtrack = 17 + cluster = 3 + ex_multi +} + +map lev24 "$TXT_EX_MAP24" +{ + cdtrack = 18 + cluster = 3 + ex_multi +} + +map lev25 "$TXT_EX_MAP25" +{ + cdtrack = 11 + cluster = 3 + ex_multi +} + +map lev26 "$TXT_EX_MAP26" +{ + cdtrack = 12 + cluster = 3 + ex_multi +} + +map lev27 "$TXT_EX_MAP27" +{ + cdtrack = 13 + cluster = 3 + ex_multi +} + +map lev28 "$TXT_EX_MAP28" +{ + cdtrack = 14 + cluster = 3 + ex_multi +} + +map lev29 "$TXT_EX_MAP29" +{ + cdtrack = 15 + cluster = 3 + ex_multi +} + +map lev30 "$TXT_EX_MAP30" +{ + cdtrack = 16 + cluster = 3 + ex_multi +} + +map lev31 "$TXT_EX_MAP31" +{ + cdtrack = 17 + cluster = 3 + ex_multi +} + +map lev32 "$TXT_EX_MAP32" +{ + cdtrack = 18 + cluster = 3 + ex_multi +} + +cutscenes +{ + intro + { + function = ExhumedCutscenes.BuildIntro + } + + loadscreen + { + function = DukeCutscenes.BuildLoading + } +} + +gameinfo +{ + summaryscreen = ExhumedCutscenes.BuildMap +} diff --git a/wadsrc/static/filter/redneck.redneck/engine/engine.con b/wadsrc/static/filter/redneck.redneck/engine/engine.con new file mode 100644 index 000000000..1910065e9 --- /dev/null +++ b/wadsrc/static/filter/redneck.redneck/engine/engine.con @@ -0,0 +1 @@ +definelevelname 1 7 endgame.map 02:00 02:00 $TXT_CLOSEENCOUNTERS diff --git a/wadsrc/static/filter/redneck.redneck/engine/rmapinfo.txt b/wadsrc/static/filter/redneck.redneck/engine/rmapinfo.txt new file mode 100644 index 000000000..dcbd6a8d9 --- /dev/null +++ b/wadsrc/static/filter/redneck.redneck/engine/rmapinfo.txt @@ -0,0 +1,124 @@ +// Cutscene definitions for RR + +cluster 1 +{ + outro + { + function = RRCutscenes.BuildE1End + } +} + +cluster 2 +{ + outro + { + function = RRCutscenes.BuildE2End + } +} + +map { 1, 1 } +{ + interbackground = "BONUSPIC01" + rr_startsound = 391 // fixme: allow symbolic names +} + +map { 1, 2 } +{ + interbackground = "BONUSPIC02" + rr_startsound = 64 +} + +map { 1, 3 } +{ + interbackground = "BONUSPIC03" + rr_startsound = 77 +} + +map { 1, 4 } +{ + interbackground = "BONUSPIC04" + rr_startsound = 80 +} + +map { 1, 5 } +{ + interbackground = "BONUSPIC05" + rr_startsound = 102 +} + +map { 1, 6 } +{ + interbackground = "BONUSPIC06" + rr_startsound = 103 +} + +map { 1, 7 } +{ + interbackground = "BONUSPIC07" + rr_startsound = 104 +} + +map { 2, 1 } +{ + interbackground = "BONUSPIC08" + rr_startsound = 105 +} + +map { 2, 2 } +{ + interbackground = "BONUSPIC09" + rr_startsound = 176 + clearweapons +} + +map { 2, 3 } +{ + interbackground = "BONUSPIC10" + rr_startsound = 177 + PrecacheTextures = "#03190","#03191","#03192","#03144","#03139","#03132","#03120","#03121","#03122","#03123","#03124" +} + +map { 2, 4 } +{ + interbackground = "BONUSPIC11" + rr_startsound = 198 +} + +map { 2, 5 } +{ + interbackground = "BONUSPIC12" + rr_startsound = 230 +} + +map { 2, 6 } +{ + interbackground = "BONUSPIC13" + rr_startsound = 255 +} + +map { 2, 7 } +{ + interbackground = "BONUSPIC14" + rr_startsound = 283 + PrecacheTextures = "UFO1", "UFO2", "UFO3", "UFO4", "UFO5" +} + +map { 2, 8 } +{ + interbackground = "BONUSPIC14" + rr_startsound = 391 +} + +cutscenes +{ + loadscreen + { + function = DukeCutscenes.BuildLoading // identical with Duke's + } +} + +gameinfo +{ + summaryscreen = RRCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary // identical with Duke's +} diff --git a/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt b/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt new file mode 100644 index 000000000..bd8cda805 --- /dev/null +++ b/wadsrc/static/filter/redneck.ridesagain/engine/rmapinfo.txt @@ -0,0 +1,120 @@ +// Cutscene definitions for RR + +cluster 2 +{ + outro + { + function = RRCutscenes.BuildRAE2End + } +} + +map { 1, 1 } +{ + interbackground = "LEVELMAP01" + rr_startsound = 63 +} + +map { 1, 2 } +{ + interbackground = "LEVELMAP02" + rr_startsound = 64 +} + +map { 1, 3 } +{ + interbackground = "LEVELMAP03" + rr_startsound = 77 + clearweapons +} + +map { 1, 4 } +{ + interbackground = "LEVELMAP04" + rr_startsound = 80 + rr_mamaspawn = 5 +} + +map { 1, 5 } +{ + interbackground = "LEVELMAP05" + rr_startsound = 102 + PrecacheTextures = "#02577" +} + +map { 1, 6 } +{ + interbackground = "LEVELMAP06" + rr_startsound = 103 +} + +map { 1, 7 } +{ + interbackground = "LEVELMAP07" + rr_startsound = 104 +} + +map { 2, 1 } +{ + interbackground = "LEVELMAP08" + rr_startsound = 105 +} + +map { 2, 2 } +{ + interbackground = "LEVELMAP09" + rr_startsound = 176 + clearweapons +} + +map { 2, 3 } +{ + interbackground = "LEVELMAP10" + rr_startsound = 177 + rr_mamaspawn = 10 +} + +map { 2, 4 } +{ + interbackground = "LEVELMAP11" + rr_startsound = 198 +} + +map { 2, 5 } +{ + interbackground = "LEVELMAP12" + rr_startsound = 230 + rr_clearmoonshine +} + +map { 2, 6 } +{ + interbackground = "LEVELMAP13" + rr_startsound = 255 +} + +map { 2, 7 } +{ + interbackground = "LEVELMAP14" + rr_startsound = 283 + rrra_hulkspawn + PrecacheTextures = "UFO1_RRRA", "UFO2", "UFO3", "UFO4", "UFO5" +} + +cutscenes +{ + loadscreen + { + function = DukeCutscenes.BuildLoading // identical with Duke's + } + defaultmapintro + { + function = RRCutscenes.BuildMapIntro // this plays the 'travel' animation. + transitiononly + } +} + +gameinfo +{ + summaryscreen = RRCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary // identical with Duke's +} diff --git a/wadsrc/static/filter/redneck.route66/engine/rmapinfo.txt b/wadsrc/static/filter/redneck.route66/engine/rmapinfo.txt new file mode 100644 index 000000000..59246e710 --- /dev/null +++ b/wadsrc/static/filter/redneck.route66/engine/rmapinfo.txt @@ -0,0 +1,109 @@ +// Cutscene definitions for RRR66. This is a little bit different than the main game. + +cluster 2 +{ + outro + { + function = RRCutscenes.BuildE2End + } +} + +map { 1, 1 } +{ + interbackground = "BONUSPIC01" + rr_startsound = 391 // fixme: allow symbolic names +} + +map { 1, 2 } +{ + interbackground = "BONUSPIC02" + rr_startsound = 64 +} + +map { 1, 3 } +{ + interbackground = "BONUSPIC03" + rr_startsound = 77 +} + +map { 1, 4 } +{ + interbackground = "BONUSPIC04" + rr_startsound = 80 +} + +map { 1, 5 } +{ + interbackground = "BONUSPIC05" + rr_startsound = 102 +} + +map { 1, 6 } +{ + interbackground = "BONUSPIC06" + rr_startsound = 103 +} + +map { 1, 7 } +{ + interbackground = "BONUSPIC07" + rr_startsound = 104 +} + +map { 2, 1 } +{ + interbackground = "BONUSPIC08" + rr_startsound = 105 +} + +map { 2, 2 } +{ + interbackground = "BONUSPIC09" + rr_startsound = 176 + clearweapons +} + +map { 2, 3 } +{ + interbackground = "BONUSPIC10" + rr_startsound = 177 +} + +map { 2, 4 } +{ + interbackground = "BONUSPIC11" + rr_startsound = 198 +} + +map { 2, 5 } +{ + interbackground = "BONUSPIC12" + rr_startsound = 230 +} + +map { 2, 6 } +{ + interbackground = "BONUSPIC13" + rr_startsound = 255 +} + +map { 2, 7 } +{ + interbackground = "BONUSPIC14" + rr_startsound = 283 + PrecacheTextures = "UFO1", "UFO2", "UFO3", "UFO4", "UFO5" +} + +cutscenes +{ + loadscreen + { + function = DukeCutscenes.BuildLoading // identical with Duke's + } +} + +gameinfo +{ + summaryscreen = RRCutscenes.BuildSPSummary + mpsummaryscreen = DukeCutscenes.BuildMPSummary // identical with Duke's +} diff --git a/wadsrc/static/filter/shadowwarrior.twindragon/rmapinfo.txt b/wadsrc/static/filter/shadowwarrior.twindragon/rmapinfo.txt new file mode 100644 index 000000000..babfd9e11 --- /dev/null +++ b/wadsrc/static/filter/shadowwarrior.twindragon/rmapinfo.txt @@ -0,0 +1,5 @@ + +map { 9 } +{ + sw_spawnmines +} \ No newline at end of file diff --git a/wadsrc/static/filter/shadowwarrior/engine/rmapinfo.txt b/wadsrc/static/filter/shadowwarrior/engine/rmapinfo.txt new file mode 100644 index 000000000..877cda926 --- /dev/null +++ b/wadsrc/static/filter/shadowwarrior/engine/rmapinfo.txt @@ -0,0 +1,63 @@ +// Cutscene definitions for SW + +Cutscenes +{ + intro + { + function = SWCutscenes.BuildIntro + } + sharewareend + { + function = SWCutscenes.BuildSybexScreen + } + + loadscreen + { + function = SWCutscenes.BuildLoading + } +} + +map { 4 } +{ + outro + { + function = SWCutscenes.BuildSerpentAnim + } + sw_bossmeter_serpent + sw_deathexit_serpent +} + +map { 5 } +{ + sw_bossmeter_serpent +} + +map { 11 } +{ + outro + { + function = SWCutscenes.BuildSumoAnim + } + sw_bossmeter_sumo + sw_deathexit_sumo +} + +map { 20 } +{ + outro + { + function = SWCutscenes.BuildZillaAnim + } + sw_bossmeter_serpent + sw_bossmeter_sumo + sw_bossmeter_zilla + sw_deathexit_zilla + next = "-" +} + +gameinfo +{ + summaryscreen = SWCutscenes.BuildSPSummary + mpsummaryscreen = SWCutscenes.BuildMPSummary +} + diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 318cc7115..61f048173 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -66,9 +66,9 @@ LISTMENU "MainMenu" Position 160, 65 linespacing 22 ExhumedPlasma - ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 1 + ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 0 ExhumedTextItem "$MNU_LOADGAME", "l", "LoadGameMenu" - ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 0 + ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 1 ExhumedTextItem "$MNU_OPTIONS", "v", "OptionsMenu" ExhumedTextItem "$MNU_QUITGAME", "q", "QuitMenu" } @@ -137,9 +137,9 @@ LISTMENU "IngameMenu" Position 160, 65 linespacing 22 ExhumedLogo - ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 1 + ExhumedTextItem "$MNU_NEWGAME", "n", "StartGameNoSkill", 0 ExhumedTextItem "$MNU_LOADGAME", "l", "LoadGameMenu" - ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 0 + ExhumedTextItem "$TXT_EX_MAP00", "m", "StartGameNoSkill", 1 ExhumedTextItem "$MNU_OPTIONS", "v", "OptionsMenu" ExhumedTextItem "$MNU_QUITGAME", "q", "QuitMenu" } diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index da000f758..262e72b63 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -28,7 +28,17 @@ version "4.3" #include "zscript/constants.zs" #include "zscript/razebase.zs" +#include "zscript/screenjob.zs" +#include "zscript/games/duke/dukegame.zs" +#include "zscript/games/duke/ui/screens.zs" +#include "zscript/games/duke/ui/cutscenes.zs" #include "zscript/games/duke/ui/menu.zs" +#include "zscript/games/blood/bloodgame.zs" #include "zscript/games/blood/ui/menu.zs" +#include "zscript/games/blood/ui/screens.zs" +#include "zscript/games/sw/swgame.zs" #include "zscript/games/sw/ui/menu.zs" +#include "zscript/games/sw/ui/screens.zs" +#include "zscript/games/exhumed/exhumedgame.zs" #include "zscript/games/exhumed/ui/menu.zs" +#include "zscript/games/exhumed/ui/screens.zs" diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index a0b2a0c83..21115a193 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -187,6 +187,8 @@ struct MusPlayingInfo native native String name; native int baseorder; native bool loop; + native voidptr handle; + }; struct TexMan @@ -218,7 +220,9 @@ struct TexMan AllowSkins = 8, ShortNameOnly = 16, DontCreate = 32, - Localize = 64 + Localize = 64, + ForceLookup = 128, + NoAlias = 256 }; enum ETexReplaceFlags @@ -239,6 +243,7 @@ struct TexMan native static Vector2 GetScaledOffset(TextureID tex); native static int CheckRealHeight(TextureID tex); native static bool OkForLocalization(TextureID patch, String textSubstitute); + native static bool UseGamePalette(TextureID tex); } enum EScaleMode @@ -402,6 +407,8 @@ struct Screen native native static int, int, int, int GetViewWindow(); native static double, double, double, double GetFullscreenRect(double vwidth, double vheight, int fsmode); native static Vector2 SetOffset(double x, double y); + native static void ClearScreen(color col = 0); + native static void SetScreenFade(double factor); } struct Font native @@ -657,6 +664,7 @@ struct StringStruct native native void DeleteLastCharacter(); native int CodePointCount() const; native int, int GetNextCodePoint(int position) const; + native void Substitute(String str, String replace); } struct Translation version("2.4") diff --git a/wadsrc/static/zscript/engine/dynarrays.zs b/wadsrc/static/zscript/engine/dynarrays.zs index 2e80820cd..1db4a6224 100644 --- a/wadsrc/static/zscript/engine/dynarrays.zs +++ b/wadsrc/static/zscript/engine/dynarrays.zs @@ -50,6 +50,7 @@ struct DynArray_I32 native native void Append (DynArray_I32 other); native uint Find(int item) const; native uint Push (int item); + native vararg uint PushV (int item, ...); native bool Pop (); native void Delete (uint index, int deletecount = 1); native void Insert (uint index, int item); diff --git a/wadsrc/static/zscript/engine/inputevents.zs b/wadsrc/static/zscript/engine/inputevents.zs index 95a6d29ab..ba63b59e9 100644 --- a/wadsrc/static/zscript/engine/inputevents.zs +++ b/wadsrc/static/zscript/engine/inputevents.zs @@ -120,6 +120,21 @@ struct InputEvent native play version("2.4") Key_F12 = 0x58, // DIK_F12 Key_Grave = 0x29, // DIK_GRAVE + KEY_kpad_1 = 0x4f, + KEY_kpad_2 = 0x50, + KEY_kpad_3 = 0x51, + KEY_kpad_4 = 0x4b, + KEY_kpad_5 = 0x4c, + KEY_kpad_6 = 0x4d, + KEY_kpad_7 = 0x47, + KEY_kpad_8 = 0x48, + KEY_kpad_9 = 0x49, + KEY_kpad_0 = 0x52, + KEY_kpad_Minus = 0x4a, + KEY_kpad_Plus = 0x4e, + KEY_kpad_Period = 0x53, + + Key_Backspace = 0x0e, // DIK_BACK Key_Equals = 0x0d, // DIK_EQUALS @@ -140,6 +155,9 @@ struct InputEvent native play version("2.4") Key_PgUp = 0xc9, // DIK_PRIOR Key_PgDn = 0xd1, // DIK_NEXT + KEY_VOLUMEDOWN = 0xAE, // DIK_VOLUMEDOWN + KEY_VOLUMEUP = 0xB0, // DIK_VOLUMEUP + Key_Mouse1 = 0x100, Key_Mouse2 = 0x101, Key_Mouse3 = 0x102, diff --git a/wadsrc/static/zscript/engine/ui/menu/menu.zs b/wadsrc/static/zscript/engine/ui/menu/menu.zs index b40c10f0c..8e08870aa 100644 --- a/wadsrc/static/zscript/engine/ui/menu/menu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/menu.zs @@ -40,6 +40,7 @@ struct KeyBindings native version("2.4") native int, int GetKeysForCommand(String cmd); native void GetAllKeysForCommand(out array list, String cmd); + native String GetBinding(int key); native void SetBind(int key, String cmd); native void UnbindACommand (String str); diff --git a/wadsrc/static/zscript/games/blood/bloodgame.zs b/wadsrc/static/zscript/games/blood/bloodgame.zs new file mode 100644 index 000000000..839b2f6cb --- /dev/null +++ b/wadsrc/static/zscript/games/blood/bloodgame.zs @@ -0,0 +1,8 @@ +// contains all global Blood definitions +struct Blood native +{ + native static void PlayIntroMusic(); + native static bool OriginalLoadScreen(); // doing it generically would necessitate exporting the tile manage which we do not want. + native static void sndStartSample(int resid, int volume, int channel, bool loop = false, int chanflags = 0); + native static void sndStartSampleNamed(String sname, int volume, int channel); +} diff --git a/wadsrc/static/zscript/games/blood/ui/menu.zs b/wadsrc/static/zscript/games/blood/ui/menu.zs index 53035e7af..72664e9d6 100644 --- a/wadsrc/static/zscript/games/blood/ui/menu.zs +++ b/wadsrc/static/zscript/games/blood/ui/menu.zs @@ -9,25 +9,7 @@ class BloodMenuDelegate : RazeMenuDelegate { override int DrawCaption(String title, Font fnt, int y, bool drawit) { - let font = generic_ui? NewConsoleFont : BigFont; // this ignores the passed font intentionally. - let texid = TexMan.CheckForTexture("MENUBAR"); - let texsize = TexMan.GetScaledSize(texid); - let fonth = font.GetGlyphHeight("A"); - if (drawit) - { - int width = font.StringWidth(title); - if (texid.isValid()) - { - double scalex = 1.; // Expand the box if the text is longer - if (texsize.X - 10 < width) scalex = width / (texsize.X - 10); - screen.DrawTexture(texid, false, 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex); - } - screen.DrawText(font, Font.CR_UNDEFINED, 160 - width / 2, 20 - fonth / 2, title, DTA_FullscreenScale, FSMode_Fit320x200Top); - } - double fx, fy, fw, fh; - [fx, fy, fw, fh] = Screen.GetFullscreenRect(320, 200, FSMode_ScaleToFit43Top); - int h = texid.isValid() && texsize.Y < 40? texsize.Y : fonth; - return int((y+h) * fh / 200); // This must be the covered height of the header in true pixels. + return BloodScreen.DrawCaption(title, y, drawit); // this ignores the passed font intentionally. } override bool DrawSelector(ListMenuDescriptor desc) @@ -85,7 +67,7 @@ class ListMenuItemBloodTextItem : ListMenuItemTextItem if (selected) shade = 32 - ((MSTime() * 120 / 1000) & 63); Screen.DrawText(gamefont, Font.CR_UNDEFINED, xpos+1, mYpos+1, mText, DTA_Color, 0xff000000, DTA_FullscreenScale, FSMode_Fit320x200); - Screen.DrawText(gamefont, Font.CR_UNDEFINED, xpos, mYpos, mText, DTA_TranslationIndex, trans, DTA_Color, Build.shadeToLight(shade), DTA_FullscreenScale, FSMode_Fit320x200); + Screen.DrawText(gamefont, Font.CR_UNDEFINED, xpos, mYpos, mText, DTA_TranslationIndex, trans, DTA_Color, Raze.shadeToLight(shade), DTA_FullscreenScale, FSMode_Fit320x200); } } diff --git a/wadsrc/static/zscript/games/blood/ui/screens.zs b/wadsrc/static/zscript/games/blood/ui/screens.zs new file mode 100644 index 000000000..ba0b38585 --- /dev/null +++ b/wadsrc/static/zscript/games/blood/ui/screens.zs @@ -0,0 +1,370 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 Nuke.YKT +Copyright (C) 2019-2021 Christoph Oelckers + +This file is part of Raze. + +NBlood is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//------------------------------------------------------------------------- + + +class BloodIntroImage : ImageScreen +{ + bool mus; + + ScreenJob Init(String texture, int flags = 0, bool withmusic = false) + { + Super.InitNamed(texture, flags); + mus = withmusic; + return self; + } + + override void Start() + { + Blood.sndStartSampleNamed("THUNDER2", 128, -1); + if (mus) Blood.PlayIntroMusic(); // this is script defined. + } +} + +struct BloodScreen +{ + enum EConstants + { + kLoadScreenWideBackWidth = 256, + kLoadScreenWideSideWidth = 128, + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawBackground() + { + if (Blood.OriginalLoadScreen() && Screen.GetAspectRatio() >= 1.34) + { + int width = screen.GetWidth() * 240 / screen.GetHeight(); + int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth; + for (int i = 0; i < nCount; i++) + { + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreenWideBack"), false, (i * kLoadScreenWideBackWidth), 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + } + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreenWideLeft"), false, 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true); + let texid = TexMan.CheckForTexture("LoadScreenWideRight"); + let size = TexMan.GetScaledSize(texid); + Screen.DrawTexture(texid, false, width - size.x, 0, DTA_TopLeft, true, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + texid = TexMan.CheckForTexture("LoadScreenWideMiddle"); + size = TexMan.GetScaledSize(texid); + Screen.DrawTexture(texid, false, (width - size.x) / 2, 0, DTA_TopLeft, true, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + } + else + { + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreen"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static int DrawCaption(String title, int y, bool drawit) + { + let font = generic_ui? NewConsoleFont : BigFont; + let texid = TexMan.CheckForTexture("MENUBAR"); + let texsize = TexMan.GetScaledSize(texid); + let fonth = font.GetGlyphHeight("A"); + if (drawit) + { + int width = font.StringWidth(title); + if (texid.isValid()) + { + double scalex = 1.; // Expand the box if the text is longer + if (texsize.X - 10 < width) scalex = width / (texsize.X - 10); + screen.DrawTexture(texid, false, 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex); + } + screen.DrawText(font, Font.CR_UNDEFINED, 160 - width / 2, 20 - fonth / 2, title, DTA_FullscreenScale, FSMode_Fit320x200Top); + } + double fx, fy, fw, fh; + [fx, fy, fw, fh] = Screen.GetFullscreenRect(320, 200, FSMode_ScaleToFit43Top); + int h = texid.isValid() && texsize.Y < 40? texsize.Y : fonth; + return int((y+h) * fh / 200); // This must be the covered height of the header in true pixels. + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawText(Font pFont, String pString, int x, int y, int position = 0, int nShade = 0, int nPalette = 0, bool shadow = true, float alpha = 1.) + { + if (position > 0) x -= pFont.StringWidth(pString) * position / 2; + if (shadow) Screen.DrawText(pFont, Font.CR_UNDEFINED, x+1, y+1, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff000000, DTA_Alpha, 0.5); + Screen.DrawText(pFont, Font.CR_UNDEFINED, x, y, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, nPalette), + DTA_Color, Raze.shadeToLight(nShade), DTA_Alpha, alpha); + + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodSummaryScreen : SummaryScreenBase +{ + ScreenJob Init(MapRecord map, SummaryInfo info) + { + Super.Init(fadein|fadeout); + SetParameters(map, info); + return self; + } + + override void Start() + { + Blood.sndStartSample(268, 128, -1, false, CHANF_UI); + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + jobstate = skipped; + return true; + } + return false; + } + + void DrawKills() + { + String pBuffer; + BloodScreen.DrawText(BigFont, Stringtable.Localize("$KILLS") .. ":", 75, 50); + pBuffer = String.Format("%2d", stats.Kills); + BloodScreen.DrawText(BigFont, pBuffer, 160, 50); + BloodScreen.DrawText(BigFont, "$OF", 190, 50); + pBuffer = String.Format( "%2d", stats.MaxKills); + BloodScreen.DrawText(BigFont, pBuffer, 220, 50); + } + + void DrawSecrets() + { + String pBuffer; + BloodScreen.DrawText(BigFont, StringTable.Localize("$TXT_SECRETS") .. ":", 75, 70); + pBuffer = String.Format( "%2d", stats.secrets); + BloodScreen.DrawText(BigFont, pBuffer, 160, 70); + BloodScreen.DrawText(BigFont, "$OF", 190, 70); + pBuffer = String.Format( "%2d", stats.maxsecrets); + BloodScreen.DrawText(BigFont, pBuffer, 220, 70); + if (stats.SuperSecrets > 0) BloodScreen.DrawText(BigFont, "$TXT_SUPERSECRET", 160, 100, 1, 0, 2); + } + + override void Draw(double sm) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption("$TXTB_LEVELSTATS", 0, true); + if (stats.cheated) + { + let text = StringTable.Localize("$TXTB_CHEATED"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 32, 1, shadow:font == SmallFont2); + } + DrawKills(); + DrawSecrets(); + + int myclock = ticks * 120 / GameTicRate; + if ((myclock & 32)) + { + let text = StringTable.Localize("$PRESSKEY"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } + } + + override void OnDestroy() + { + Raze.StopAllSounds(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodMPSummaryScreen : SkippableScreenJob +{ + int numplayers; + ScreenJob Init(int numplayer) + { + Super.Init(fadein|fadeout); + numplayers = numplayer; + return self; + } + + override void Start() + { + Blood.sndStartSample(268, 128, -1, false, CHANF_UI); + } + + void DrawKills() + { + String pBuffer; + BloodScreen.DrawText(SmallFont2, "#", 85, 35); + BloodScreen.DrawText(SmallFont2, "$NAME", 100, 35); + BloodScreen.DrawText(SmallFont2, "$FRAGS", 210, 35); + + for (int i = 0; i < numplayers; i++) + { + pBuffer = String.Format( "%-2d", i); + BloodScreen.DrawText(SmallFont2, pBuffer, 85, 50 + 8 * i); + pBuffer = String.Format( "%s", Raze.PlayerName(i)); + BloodScreen.DrawText(SmallFont2, pBuffer, 100, 50 + 8 * i); + pBuffer = String.Format( "%d", Raze.playerFrags(i, -1)); + BloodScreen.DrawText(SmallFont2, pBuffer, 210, 50 + 8 * i); + } + } + + override void Draw(double sr) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption("$TXTB_FRAGSTATS", 0, true); + DrawKills(); + + int myclock = ticks * 120 / GameTicRate; + if ((myclock & 32)) + { + let text = StringTable.Localize("$PRESSKEY"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodLoadScreen : ScreenJob +{ + String loadtext; + MapRecord rec; + + ScreenJob Init(MapRecord maprec) + { + Super.Init(fadein); + + /*if (gGameOptions.nGameType == 0)*/ loadtext = "$TXTB_LLEVEL"; + //else loadText = String.Format("$TXTB_NETGT%d", gGameOptions.nGameType)); + + rec = maprec; + return self; + } + + override void OnTick() + { + if (fadestate == visible) jobstate = finished; + } + + override void Draw(double sr) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption(loadtext, 0, true); + BloodScreen.DrawText(BigFont, rec.DisplayName(), 160, 50, 1); + + let text = StringTable.Localize("$TXTB_PLSWAIT"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodCutscenes +{ + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + Array soundinfo; + if (Wads.CheckNumForFullName("logo.smk") != -1) + { + String s = "logo.wav"; // sound name must be converted at run-time, not compile time! + runner.Append(MoviePlayerJob.CreateWithSound("logo.smk", s, MoviePlayer.FIXEDVIEWPORT)); + } + else + { + runner.Append(new("BloodIntroImage").Init("MonolithScreen", ScreenJob.fadein|ScreenJob.fadeout)); + } + if (Wads.CheckNumForFullName("gti.smk") != -1) + { + String s = "gt.wav"; + runner.Append(MoviePlayerJob.CreateWithSound("gti.smk", s, MoviePlayer.FIXEDVIEWPORT)); + } + else + { + runner.Append(new("BloodIntroImage").Init("GTIScreen", ScreenJob.fadein|ScreenJob.fadeout)); + } + } + runner.Append(new("BloodIntroImage").Init("Titlescreen", ScreenJob.fadein, true)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("BloodMPSummaryScreen").Init(stats.playercount)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("BloodSummaryScreen").Init(map, stats)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildLoading(ScreenJobRunner runner, MapRecord map) + { + runner.Append(new("BloodLoadScreen").Init(map)); + } +} diff --git a/wadsrc/static/zscript/games/duke/dukegame.zs b/wadsrc/static/zscript/games/duke/dukegame.zs new file mode 100644 index 000000000..ef06f96bd --- /dev/null +++ b/wadsrc/static/zscript/games/duke/dukegame.zs @@ -0,0 +1,1033 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- + +// contains all global Duke definitions +struct Duke native +{ + enum ESpecialMusic + { + MUS_INTRO = 0, + MUS_BRIEFING = 1, + MUS_LOADING = 2, + }; + + enum EPalette + { + BASEPAL = 0, + WATERPAL, + SLIMEPAL, + TITLEPAL, + DREALMSPAL, + ENDINGPAL, // 5 + ANIMPAL, // not used anymore. The anim code now generates true color textures. + DRUGPAL, + BASEPALCOUNT + }; + + native static void PlaySpecialMusic(int which); + native static int PlaySound(int num, int channel = CHAN_AUTO, int flags = 0, float vol =0.8f); + native static void StopSound(int num); + native static bool CheckSoundPlaying(int num); + + static void PlayBonusMusic() + { + if (Raze.MusicEnabled()) + PlaySound(DukeSnd.BONUSMUSIC, CHAN_AUTO, CHANF_UI); + } + + //========================================================================== + // + // wrappers around DrawText to allow easier reuse of the old code. + // The vertical displacements are to have the same positioning as with the original code. + // + //========================================================================== + + static void BigText(double x, double y, String text, int align = -1, double alpha = 1.) + { + + if (!Raze.isRR()) + { + if (align != -1) x -= BigFont.StringWidth(text) * (align == 0 ? 0.5 : 1); + Screen.DrawText(BigFont, Font.CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Alpha, alpha); + } + else + { + if (align != -1) x -= BigFont.StringWidth(text) * (align == 0 ? 0.2 : 0.4); + Screen.DrawText(BigFont, Font.CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, 0.4, DTA_ScaleY, 0.4, DTA_Alpha, alpha); + } + } + + static void GameText(double x, double y, String t, int shade, int align = -1, int trans = 0) + { + int fsmode = FSMode_Fit320x200; + if (Raze.isRR()) + { + x *= 2; + y *= 2; + fsmode = FSMode_Fit640x400; + } + if (align != -1) x -= SmallFont.StringWidth(t) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, fsmode, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, trans), DTA_Color, Raze.shadeToLight(shade)); + } + + static void MiniText(double x, double y, String t, int shade, int align = -1, int trans = 0) + { + int fsmode = FSMode_Fit320x200; + if (Raze.isRR()) + { + x *= 2; + y *= 2; + fsmode = FSMode_Fit640x400; + } + if (align != -1) x -= SmallFont2.StringWidth(t) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont2, Font.CR_UNDEFINED, x, y, t, DTA_FullscreenScale, fsmode, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, trans), DTA_Color, Raze.shadeToLight(shade)); + } + + +} + +struct DukeSnd native +{ + // This really needs to be done better... + enum EDukeSounds + { + KICK_HIT = 0, + PISTOL_RICOCHET = 1, + PISTOL_BODYHIT = 2, + PISTOL_FIRE = 3, + EJECT_CLIP = 4, + INSERT_CLIP = 5, + CHAINGUN_FIRE = 6, + RPG_SHOOT = 7, + POOLBALLHIT = 8, + RPG_EXPLODE = 9, + CAT_FIRE = 10, + SHRINKER_FIRE = 11, + ACTOR_SHRINKING = 12, + PIPEBOMB_BOUNCE = 13, + PIPEBOMB_EXPLODE = 14, + LASERTRIP_ONWALL = 15, + LASERTRIP_ARMING = 16, + LASERTRIP_EXPLODE = 17, + VENT_BUST = 18, + GLASS_BREAKING = 19, + GLASS_HEAVYBREAK = 20, + SHORT_CIRCUIT = 21, + ITEM_SPLASH = 22, + DUKE_BREATHING = 23, + DUKE_EXHALING = 24, + DUKE_GASP = 25, + SLIM_RECOG = 26, + + DUKE_URINATE = 28, + ENDSEQVOL3SND2 = 29, + ENDSEQVOL3SND3 = 30, + DUKE_PASSWIND = 32, + DUKE_CRACK = 33, + SLIM_ATTACK = 34, + SOMETHINGHITFORCE = 35, + DUKE_DRINKING = 36, + DUKE_KILLED1 = 37, + DUKE_GRUNT = 38, + DUKE_HARTBEAT = 39, + DUKE_ONWATER = 40, + DUKE_DEAD = 41, + DUKE_LAND = 42, + DUKE_WALKINDUCTS = 43, + DUKE_GLAD = 44, + DUKE_YES = 45, + DUKE_HEHE = 46, + DUKE_SHUCKS = 47, + DUKE_UNDERWATER = 48, + DUKE_JETPACK_ON = 49, + DUKE_JETPACK_IDLE = 50, + DUKE_JETPACK_OFF = 51, + LIZTROOP_GROWL = 52, + LIZTROOP_TALK1 = 53, + LIZTROOP_TALK2 = 54, + LIZTROOP_TALK3 = 55, + DUKETALKTOBOSS = 56, + LIZCAPT_GROWL = 57, + LIZCAPT_TALK1 = 58, + LIZCAPT_TALK2 = 59, + LIZCAPT_TALK3 = 60, + LIZARD_BEG = 61, + LIZARD_PAIN = 62, + LIZARD_DEATH = 63, + LIZARD_SPIT = 64, + DRONE1_HISSRATTLE = 65, + DRONE1_HISSSCREECH = 66, + DUKE_TIP2 = 67, + FLESH_BURNING = 68, + SQUISHED = 69, + TELEPORTER = 70, + ELEVATOR_ON = 71, + DUKE_KILLED3 = 72, + ELEVATOR_OFF = 73, + DOOR_OPERATE1 = 74, + SUBWAY = 75, + SWITCH_ON = 76, + FAN = 77, + DUKE_GETWEAPON3 = 78, + FLUSH_TOILET = 79, + HOVER_CRAFT = 80, + EARTHQUAKE = 81, + INTRUDER_ALERT = 82, + END_OF_LEVEL_WARN = 83, + ENGINE_OPERATING = 84, + REACTOR_ON = 85, + COMPUTER_AMBIENCE = 86, + GEARS_GRINDING = 87, + BUBBLE_AMBIENCE = 88, + MACHINE_AMBIENCE = 89, + SEWER_AMBIENCE = 90, + WIND_AMBIENCE = 91, + SOMETHING_DRIPPING = 92, + STEAM_HISSING = 93, + THEATER_BREATH = 94, + BAR_MUSIC = 95, + BOS1_ROAM = 96, + BOS1_RECOG = 97, + BOS1_ATTACK1 = 98, + BOS1_PAIN = 99, + BOS1_DYING =100, + BOS2_ROAM =101, + BOS2_RECOG =102, + BOS2_ATTACK =103, + BOS2_PAIN =104, + BOS2_DYING =105, + GETATOMICHEALTH =106, + DUKE_GETWEAPON2 =107, + BOS3_DYING =108, + SHOTGUN_FIRE =109, + PRED_ROAM =110, + PRED_RECOG =111, + PRED_ATTACK =112, + PRED_PAIN =113, + PRED_DYING =114, + CAPT_ROAM =115, + CAPT_ATTACK =116, + CAPT_RECOG =117, + CAPT_PAIN =118, + CAPT_DYING =119, + PIG_ROAM =120, + PIG_RECOG =121, + PIG_ATTACK =122, + PIG_PAIN =123, + PIG_DYING =124, + RECO_ROAM =125, + RECO_RECOG =126, + RECO_ATTACK =127, + RECO_PAIN =128, + RECO_DYING =129, + DRON_ROAM =130, + DRON_RECOG =131, + DRON_ATTACK1 =132, + DRON_PAIN =133, + DRON_DYING =134, + COMM_ROAM =135, + COMM_RECOG =136, + COMM_ATTACK =137, + COMM_PAIN =138, + COMM_DYING =139, + OCTA_ROAM =140, + OCTA_RECOG =141, + OCTA_ATTACK1 =142, + OCTA_PAIN =143, + OCTA_DYING =144, + TURR_ROAM =145, + TURR_RECOG =146, + TURR_ATTACK =147, + DUMPSTER_MOVE =148, + SLIM_DYING =149, + BOS3_ROAM =150, + BOS3_RECOG =151, + BOS3_ATTACK1 =152, + BOS3_PAIN =153, + BOS1_ATTACK2 =154, + COMM_SPIN =155, + BOS1_WALK =156, + DRON_ATTACK2 =157, + THUD =158, + OCTA_ATTACK2 =159, + WIERDSHOT_FLY =160, + TURR_PAIN =161, + TURR_DYING =162, + SLIM_ROAM =163, + LADY_SCREAM =164, + DOOR_OPERATE2 =165, + DOOR_OPERATE3 =166, + DOOR_OPERATE4 =167, + BORNTOBEWILDSND =168, + SHOTGUN_COCK =169, + GENERIC_AMBIENCE1 =170, + GENERIC_AMBIENCE2 =171, + GENERIC_AMBIENCE3 =172, + GENERIC_AMBIENCE4 =173, + GENERIC_AMBIENCE5 =174, + GENERIC_AMBIENCE6 =175, + BOS3_ATTACK2 =176, + GENERIC_AMBIENCE17 =177, + GENERIC_AMBIENCE18 =178, + GENERIC_AMBIENCE19 =179, + GENERIC_AMBIENCE20 =180, + GENERIC_AMBIENCE21 =181, + GENERIC_AMBIENCE22 =182, + SECRETLEVELSND =183, + GENERIC_AMBIENCE8 =184, + GENERIC_AMBIENCE9 =185, + GENERIC_AMBIENCE10 =186, + GENERIC_AMBIENCE11 =187, + GENERIC_AMBIENCE12 =188, + GENERIC_AMBIENCE13 =189, + GENERIC_AMBIENCE14 =190, + GENERIC_AMBIENCE15 =192, + GENERIC_AMBIENCE16 =193, + FIRE_CRACKLE =194, + BONUS_SPEECH1 =195, + BONUS_SPEECH2 =196, + BONUS_SPEECH3 =197, + PIG_CAPTURE_DUKE =198, + BONUS_SPEECH4 =199, + DUKE_LAND_HURT =200, + DUKE_HIT_STRIPPER1 =201, + DUKE_TIP1 =202, + DUKE_KILLED2 =203, + PRED_ROAM2 =204, + PIG_ROAM2 =205, + DUKE_GETWEAPON1 =206, + DUKE_SEARCH2 =207, + DUKE_CRACK2 =208, + DUKE_SEARCH =209, + DUKE_GET =210, + DUKE_LONGTERM_PAIN =211, + MONITOR_ACTIVE =212, + NITEVISION_ONOFF =213, + DUKE_HIT_STRIPPER2 =214, + DUKE_CRACK_FIRST =215, + DUKE_USEMEDKIT =216, + DUKE_TAKEPILLS =217, + DUKE_PISSRELIEF =218, + SELECT_WEAPON =219, + WATER_GURGLE =220, + DUKE_GETWEAPON4 =221, + JIBBED_ACTOR1 =222, + JIBBED_ACTOR2 =223, + JIBBED_ACTOR3 =224, + JIBBED_ACTOR4 =225, + JIBBED_ACTOR5 =226, + JIBBED_ACTOR6 =227, + JIBBED_ACTOR7 =228, + DUKE_GOTHEALTHATLOW =229, + BOSSTALKTODUKE =230, + WAR_AMBIENCE1 =231, + WAR_AMBIENCE2 =232, + WAR_AMBIENCE3 =233, + WAR_AMBIENCE4 =234, + WAR_AMBIENCE5 =235, + WAR_AMBIENCE6 =236, + WAR_AMBIENCE7 =237, + WAR_AMBIENCE8 =238, + WAR_AMBIENCE9 =239, + WAR_AMBIENCE10 =240, + ALIEN_TALK1 =241, + ALIEN_TALK2 =242, + EXITMENUSOUND =243, + FLY_BY =244, + DUKE_SCREAM =245, + SHRINKER_HIT =246, + RATTY =247, + INTO_MENU =248, + BONUSMUSIC =249, + DUKE_BOOBY =250, + DUKE_TALKTOBOSSFALL =251, + DUKE_LOOKINTOMIRROR =252, + PIG_ROAM3 =253, + KILLME =254, + DRON_JETSND =255, + SPACE_DOOR1 =256, + SPACE_DOOR2 =257, + SPACE_DOOR3 =258, + SPACE_DOOR4 =259, + SPACE_DOOR5 =260, + ALIEN_ELEVATOR1 =261, + VAULT_DOOR =262, + JIBBED_ACTOR13 =263, + DUKE_GETWEAPON6 =264, + JIBBED_ACTOR8 =265, + JIBBED_ACTOR9 =266, + JIBBED_ACTOR10 =267, + JIBBED_ACTOR11 =268, + JIBBED_ACTOR12 =269, + DUKE_KILLED4 =270, + DUKE_KILLED5 =271, + ALIEN_SWITCH1 =272, + DUKE_STEPONFECES =273, + DUKE_LONGTERM_PAIN2 =274, + DUKE_LONGTERM_PAIN3 =275, + DUKE_LONGTERM_PAIN4 =276, + COMPANB2 =277, + KTIT =278, + HELICOP_IDLE =279, + STEPNIT =280, + SPACE_AMBIENCE1 =281, + SPACE_AMBIENCE2 =282, + SLIM_HATCH =283, + RIPHEADNECK =284, + FOUNDJONES =285, + ALIEN_DOOR1 =286, + ALIEN_DOOR2 =287, + ENDSEQVOL3SND4 =288, + ENDSEQVOL3SND5 =289, + ENDSEQVOL3SND6 =290, + ENDSEQVOL3SND7 =291, + ENDSEQVOL3SND8 =292, + ENDSEQVOL3SND9 =293, + WHIPYOURASS =294, + ENDSEQVOL2SND1 =295, + ENDSEQVOL2SND2 =296, + ENDSEQVOL2SND3 =297, + ENDSEQVOL2SND4 =298, + ENDSEQVOL2SND5 =299, + ENDSEQVOL2SND6 =300, + ENDSEQVOL2SND7 =301, + GENERIC_AMBIENCE23 =302, + SOMETHINGFROZE =303, + DUKE_LONGTERM_PAIN5 =304, + DUKE_LONGTERM_PAIN6 =305, + DUKE_LONGTERM_PAIN7 =306, + DUKE_LONGTERM_PAIN8 =307, + WIND_REPEAT =308, + MYENEMY_ROAM =309, + MYENEMY_HURT =310, + MYENEMY_DEAD =311, + MYENEMY_SHOOT =312, + STORE_MUSIC =313, + STORE_MUSIC_BROKE =314, + ACTOR_GROWING =315, + NEWBEAST_ROAM =316, + NEWBEAST_RECOG =317, + NEWBEAST_ATTACK =318, + NEWBEAST_PAIN =319, + NEWBEAST_DYING =320, + NEWBEAST_SPIT =321, + VOL4_1 =322, + SUPERMARKET =323, + MOUSEANNOY =324, + BOOKEM =325, + SUPERMARKETCRY =326, + DESTRUCT =327, + EATFOOD =328, + MAKEMYDAY =329, + WITNESSSTAND =330, + VACATIONSPEECH =331, + YIPPEE1 =332, + YOHOO1 =333, + YOHOO2 =334, + DOLPHINSND =335, + TOUGHGALSND1 =336, + TOUGHGALSND2 =337, + TOUGHGALSND3 =338, + TOUGHGALSND4 =339, + TANK_ROAM =340, + BOS4_ROAM =341, + BOS4_RECOG =342, + BOS4_ATTACK =343, + BOS4_PAIN =344, + BOS4_DYING =345, + NEWBEAST_ATTACKMISS =346, + VOL4_2 =347, + COOKINGDEEPFRIER =348, + WHINING_DOG =349, + DEAD_DOG =350, + LIGHTNING_SLAP =351, + THUNDER =352, + HAPPYMOUSESND1 =353, + HAPPYMOUSESND2 =354, + HAPPYMOUSESND3 =355, + HAPPYMOUSESND4 =356, + ALARM =357, + RAIN =358, + DTAG_GREENRUN =359, + DTAG_BROWNRUN =360, + DTAG_GREENSCORE =361, + DTAG_BROWNSCORE =362, + INTRO4_1 =363, + INTRO4_2 =364, + INTRO4_3 =365, + INTRO4_4 =366, + INTRO4_5 =367, + INTRO4_6 =368, + SCREECH =369, + BOSS4_DEADSPEECH =370, + BOSS4_FIRSTSEE =371, + PARTY_SPEECH =372, + POSTAL_SPEECH =373, + TGSPEECH =374, + DOGROOMSPEECH =375, + SMACKED =376, + MDEVSPEECH =377, + AREA51SPEECH =378, + JEEPSOUND =379, + BIGDOORSLAM =380, + BOS4_LAY =381, + WAVESOUND =382, + ILLBEBACK =383, + VOL4ENDSND1 =384, + VOL4ENDSND2 =385, + EXPANDERHIT =386, + SNAKESPEECH =387, + EXPANDERSHOOT =388, + GETBACKTOWORK =389, + JIBBED_ACTOR14 =390, + JIBBED_ACTOR15 =391, + INTRO4_B =392, + BIGBANG =393, + SMACKIT =394, + BELLSND =395, + GOAWAY =396, + JOKE =397, + FLAMETHROWER_INTRO =398, + FLAMETHROWER_LOOP =399, + FLAMETHROWER_END =400, + E5L7_DUKE_QUIT_YOU =401, + } +} + +struct RRSnd native +{ + enum ESnd + { + KICK_HIT = 0 , + RICOCHET = 1 , + BULITHIT = 2 , + CASUL_FIRE = 3 , + PISCOCK = 4 , + PISLOAD = 5 , + AK3 = 6 , + XBOWFIRE = 7 , + BUB_HRT1 = 8 , + XBOWEXPL = 9 , + LASERA = 10 , + SHRINKER = 11 , + CRAPFLOW = 12 , + DYNOCLMP = 13 , + DYNEW = 14 , + CRAPSTIR = 15 , + BRICDOOR = 16 , + BOMBEXPL = 17 , + VENTBUST = 18 , + GLASSSND = 19 , + GLASSHVY = 20 , + BUBBLES = 21 , + SPLASH = 22 , + BUB_HRT2 = 23 , + BUB_HRT3 = 24 , + GASP = 25 , + BUB_HRT4 = 26 , + + ONECART = 27 , // RR + MINEWIND = 28 , + URANUS = 29 , + + MIRROR1 = 27 , // RRRA + MIRROR2 = 28 , + MIRROR3 = 29 , + + COMPUTER = 30 , + NEON = 31 , + VX_FINAL = 32 , + LN_WAIT = 33 , + BUB_LN1 = 34 , + LN_FINAL = 35 , + CLOCKTK = 36 , + LN_STANK = 37 , + LNRD_GRUNT = 38 , + CLOCKCHM = 39 , + WETFEET = 40 , + LNRD_DEAD = 41 , + LAND = 42 , + END_PIPE = 43 , + ICARUMBA = 44 , + BUB_LN2 = 45 , + LN_CRAP = 46 , + WOODBREK = 47 , + SCUBA = 48 , + TRUCK_LP2 = 49 , + COW1 = 50 , + COW2 = 51 , + COW3 = 52 , + COW4 = 53 , + COW5 = 54 , + BUB_LN3 = 55 , + LATCH = 56 , + BUB_LN5 = 57 , + BUB_LN6 = 58 , + BUB_LN7 = 59 , + BUB_PIK1 = 60 , + BUB_PIK2 = 61 , + BUB_PISS = 62 , + E1L1 = 63 , + E1L2 = 64 , + UFOINSID = 65 , + LN_RODE = 66 , + CURTAIN = 67 , + FIRE09 = 68 , + SQUISHED = 69 , + TELEPORT = 70 , + GBELEV01 = 71 , + LN_BNCH = 72 , + GBELEV02 = 73 , + FROG1 = 74 , + TRUCK_LP = 75 , + SWITCH1 = 76 , + E1L3 = 77 , + LN_HOTDM = 78 , + FLUSH = 79 , + E1L4 = 80 , + QUAKE = 81 , + CHKAMMO = 82 , + MONITORA = 83 , + FROG2 = 84 , + AS_AMB2 = 85 , + AS_AMB1 = 86 , + FBOATIDL = 87 , + FBOATRUN = 88 , + FBOATUP = 89 , + FBOATDN = 90 , + FBOATTRN = 91 , + DRIP3 = 92 , + SWAMPAMB = 93 , + MORTAR = 94 , + JUKEBOX = 95 , + AS_DROPN = 96 , + AS_CRYPT = 97 , + AS_DRCLS = 98 , + LOKGATE = 99 , + METLGAT2 = 100, + METLGAT1 = 101, + E1L5 = 102, + E1L6 = 103, + E1L7 = 104, + E2L1 = 105, + PADDLE = 106, + LN_HOLD = 107, + VX_TAKIT = 108, + SHOT6 = 109, + CT_LAF2 = 110, + CT_GET = 111, + CT_LAF = 112, + CT_PAIN = 113, + CT_DIE = 114, + PIGSOUND1 = 115, + PIGSOUND2 = 116, + PIGSOUND3 = 117, + PIGSOUND4 = 118, + PIGSOUND5 = 119, + BR_ROAM1 = 120, + BR_RECOG = 121, + WHISTLE = 122, + BR_PAIN = 123, + BR_DTH = 124, + VX_ISTHT = 125, + LASERH = 126, + PIGSOUND6 = 127, + PIGSOUND7 = 128, + VX_DIE1 = 129, + MJ_JIB1 = 130, + VX_DIE4 = 131, + VX_DIE5 = 132, + VX_DIE6 = 133, + VX_DIE7 = 134, + VX_OOH = 135, + VX_PAIN1 = 136, + VX_SEX1 = 137, + VX_SEX2 = 138, + VX_SEX3 = 139, + VX_GRNT = 140, + RENO = 141, + BK_MAKE1 = 142, + BK_MAKE2 = 143, + VX_BRNG3 = 144, + VX_CLSR1 = 145, + VX_CLSR2 = 146, + VX_2FAR = 147, + KINGHUH = 148, + VX_BRING = 149, + VX_BITE = 150, + MJ_FART = 151, + VX_LAFF2 = 152, + VX_LAFF3 = 153, + VX_HMMM2 = 154, + VX_HURT2 = 155, + VX_BABY2 = 156, + VX_MHMM = 157, + THUD = 158, + VX_ITSOK = 159, + MJ_RECO2 = 160, + // VX_TPOT1 = 161, + VX_TPOT4 = 162, + // VX_TPIN1 = 163, + ROPECRK = 164, + DR_CRK8 = 165, + DR_ROLL = 166, + STEELAMB = 167, + ROULETTE = 168, + GUNCHANG = 169, + FLIES = 170, + AMB_1 = 171, + GRAVAMB = 172, + HOOTOWL = 173, + WOODS2 = 174, + CATAMB = 175, + E2L2 = 176, + E2L3 = 177, + FBOATX_1 = 178, + FBOATX_2 = 179, + FBOATX_3 = 180, + FBOATX_4 = 181, + FBOATSLW = 182, + PLANE = 183, + CNTAMB = 184, + JUNKAMB2 = 185, + BIKESTRT = 186, + BIKEIDLE = 187, + BIKELOOP = 188, + BIKEJMPS = 189, + BIKEJMPL = 190, + BIKELAND = 191, + JACKJMP1 = 192, + JACKJMP2 = 193, + FIRE_CRACKLE = 194, + BNS_SPCH1 = 195, + BNS_SPCH2 = 196, + BNS_SPCH3 = 197, + E2L4 = 198, + BNS_SPCH4 = 199, + LN_LNDHT = 200, + JACKATK2 = 201, + JACKPAIN = 202, + LN_BITCH = 203, + CT_LAND = 204, + BR_ROAM2 = 205, + LN_HUSH = 206, + LN_PAIN4 = 207, + LN_SLOW = 208, + LN_PAIN4A = 209, + JUG = 210, + LN_PAIN8 = 211, + MONITOR = 212, + JACKATK1 = 213, + BIKEUP = 214, + PLANEXP = 215, + JUGALUG7 = 216, + DIDDLP = 217, + ELVISMOD = 218, + // PISCOCK = 219, + BIKESKID = 220, + LN_STINK = 221, + JIBBED1 = 222, + JIBBED2 = 223, + JIBBED3 = 224, + JIBBED4 = 225, + JIBBED5 = 226, + JIBBED6 = 227, + JIBBED7 = 228, + LN_BACON = 229, + E2L5 = 230, + REGISTER = 231, + BBQGRILL = 232, + CRSSBELL = 233, + TRAIN = 234, + SLOTS = 235, + INDIANS = 236, + RADIO = 237, + BIKEX_1 = 238, + BIKEX_2 = 239, + BIKEX_3 = 240, + TVSNOW = 241, + WINDLITE = 242, + EXITMENU = 243, + CHKBOWFR = 244, + DSCREM04 = 245, + SHRNK_HIT = 246, + CHKBOWEX = 247, + INTOMENU = 248, + LAVAFLOW = 249, + LAVA_RK = 250, + BIKELOO2 = 251, + SLINGBL = 252, + BR_ROAM3 = 253, + KILLME = 254, + E2L6 = 255, + RINTRO = 256, + MIRROR4 = 257, + MIRROR5 = 258, + GAMBELEV = 259, + SLINGHIT = 260, + PIANO_P1 = 261, + BANJO1 = 262, + JIBBED13 = 263, + LN_BBQ = 264, + JIBBED8 = 265, + JIBBED9 = 266, + JIBBED10 = 267, + JIBBED11 = 268, + JIBBED12 = 269, + LNRD_KILLED4 = 270, + LNRD_KILLED5 = 271, + BANJO2 = 272, + BANJO3 = 273, + LN_PAIN2 = 274, + LN_PAIN3 = 275, + BK_ALIVE = 276, + BK_BOURB = 277, + BK_CHEER = 278, + BK_DIENB = 279, + BK_DNTLK = 280, + BK_FUN = 281, + BK_HEY = 282, + E2L7 = 283, + BK_HEYNB = 284, + BK_JOYRD = 285, + BK_KEEPA = 286, + BK_PLEAS = 287, + BK_RIDE = 288, + BK_ROAD = 289, + BK_SCRAT = 290, + BK_SHTUP = 291, + BK_SNORT = 292, + BK_TOHEL = 293, + WHIPYOU = 294, + BK_TRYIN = 295, + BK_PAIN1 = 296, + BK_PAIN2 = 297, + BK_PAIN3 = 298, + CH_BALD = 299, + CH_TEAS1 = 300, + CH_TEAS2 = 301, + CH_TEAS3 = 302, + CH_SANDP = 303, + LN_PAIN5 = 304, + LN_PAIN6 = 305, + LN_PAIN7 = 306, + CH_DONIT = 307, + CH_WHOOP = 308, + CH_NIPPL = 309, + CH_BARN = 310, + CH_GTEAM = 311, + CH_GOGOG = 312, + CH_REDOK = 313, + CH_2468 = 314, + CH_BIGON = 315, + HULK_ROAM = 316, + HULK_RECOG = 317, + HULK_ATTACK = 318, + HULK_PAIN = 319, + HULK_DYING = 320, + HULK_SPIT = 321, + CH_PAIN1 = 322, + CH_PAIN2 = 323, + CH_PAIN3 = 324, + CH_HURT = 325, + AK4 = 326, + CHKSCR1 = 327, + SHIPWREK = 328, + HYDROGLY = 329, + PIANO_P2 = 330, + FROGTOSS = 331, + TRAIN2 = 332, + CRICKET1 = 333, + CRICKET2 = 334, + PIGRUNT = 335, + GOCATGO = 336, + ANNOUNC1 = 337, + ANNOUNC2 = 338, + TRACTOR = 339, + PIANO_P3 = 340, + RESIZE = 341, + VX_TPIN2 = 342, + VX_TPIN4 = 343, + VX_HLPME = 344, + ATFSPEAK = 345, + WINDCAVE = 346, + ALARM = 347, + SF_THLAW = 348, + SF_TLAW2 = 349, + LN_SCREW = 350, + THUNDER1 = 351, + THUNDER2 = 352, + THUNDER3 = 353, + BOWLSTRT = 354, + BOWLPIN = 355, + BOWLLOOP = 356, + MJ_JIB2 = 357, + VX_KICK2 = 358, + VX_KICK3 = 359, + MJ_RECO1 = 360, + VX_HIYA = 361, + VX_HIYA2 = 362, + SF_ATTN = 363, + SF_DETH1 = 364, + SF_DETH2 = 365, + SF_DETH3 = 366, + TEDOUT = 367, + SF_FREZ2 = 368, + SF_GETYA = 369, + SF_HANDS = 370, + STEELAM2 = 371, + STEELAM3 = 372, + SF_HEY = 373, + SF_HOLD = 374, + SF_LAFF1 = 375, + LN_FLYOP = 376, + LN_SHTHD = 377, + SF_NAME = 378, + SF_OVER = 379, + SF_PAIN1 = 380, + SF_PAIN2 = 381, + SF_PAIN3 = 382, + SF_RLOAD = 383, + SF_RLOD2 = 384, + SF_SHOOT = 385, + JAWHARP = 386, + LN_TIGHT = 387, + DR_CLS = 388, + SCRAPE_1 = 389, + YEHAA16 = 390, + LN_WHUP = 391, + CHKNFLAP = 392, + CHKN_1 = 393, + CHKN_2 = 394, + CHIKDETH = 395, + AMB_ROOM = 396, + BR_ITCH = 397, + BR_SCRTH = 398, + BR_SNIFF = 399, + TRUKDIE = 400, + ZIPOPEN = 401, + ZIPPSTRK = 402, + MOSQUI4 = 403, + FART1 = 404, + SWITCH2 = 405, + SQUEAKY = 406, + CATDOOR = 407, + JUNKSWCH = 408, + CONVEYR = 409, + SWITCH3 = 410, + BIKEENEM = 411, + BIGDOOR = 412, + FLOODGAT = 413, + JACK_RM1 = 414, + MN_FREAK = 415, + MN_PN = 416, + MN_REC = 417, + MN_AMB = 418, + LOKDOOR = 419, + VOMIT = 420, + TOSS = 421, + FART2 = 422, + FART3 = 423, + FART4 = 424, + CHUG = 425, + CROWUSH = 426, + WUSSLAF = 427, + LN_CITY = 428, + MUNCH2 = 429, + TESLARC = 430, + BUZSAWSND = 431, + ELEVLOOP = 432, + PISSEND = 433, + PISSLOOP = 434, + PISSSTRT = 435, + CRAP = 436, + PEE = 437, + JACK_RM2 = 438, + BELL = 439, + TRAINWRK = 440, + DOOR_PKT = 441, + GAMBDOOR = 442, + OVEN = 443, + CREMATOR = 444, + JOE9000A = 445, + JOE9000B = 446, + JOE9000C = 447, + CHINESE = 448, + SIGNROT = 449, + XBOWCOCK = 450, + PWDERKEG = 451, + DG_BARK1 = 452, + DG_GRWL1 = 453, + DG_YELP = 454, + DG_DIE = 455, + UFO = 456, + UFOLET = 457, + JACKJIB1 = 458, + JACKJIB2 = 459, + JACKJIB3 = 460, + JACKJIB4 = 461, + JACKJIB5 = 462, + WTRFALL = 463, + BK_JIB1 = 464, + FRIDGEOP = 465, + FRIDGECL = 466, + DG_LUNGE = 467, + DRIVTHRU = 468, + FAN = 469, + CRUSHER = 470, + BALLOON = 471, + POOLBUD = 472, + STAMPER = 473, + BK_JIB2 = 474, + MORNING = 475, + DG_BARK2 = 476, + DG_GRWL2 = 477, + REDNECK2 = 478, + XATRIX = 479, + MJ_ATTK1 = 480, + MJ_JUMP = 485, + MJ_PAIN1 = 481, + MJ_PAIN2 = 482, + MJ_ROAM1 = 483, + MJ_ROAM2 = 484, + MJ_ROLL = 486, + DISHES = 487, + BUB_ELV1 = 488, + BUB_ELV2 = 489, + BUB_ELV3 = 490, + BK_JIB3 = 491, + CH_JIB1 = 492, + CH_JIB2 = 493, + CH_JIB3 = 494, + SIGNHIT = 495, + UMHUM = 496, + COYOTE = 497, + BUB_HEY1 = 498, + BUB_HEY2 = 499, + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/games/duke/ui/cutscenes.zs b/wadsrc/static/zscript/games/duke/ui/cutscenes.zs new file mode 100644 index 000000000..690427294 --- /dev/null +++ b/wadsrc/static/zscript/games/duke/ui/cutscenes.zs @@ -0,0 +1,366 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- + +class DukeCutscenes // Note: must be class, not struct, otherwise we cannot easily look up the methods from C++. +{ + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + if (!Raze.isShareware()) + { + Array soundinfo; + soundinfo.Pushv( + 1, DukeSnd.FLY_BY+1, + 19, DukeSnd.PIPEBOMB_EXPLODE+1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("logo.anm", soundinfo, 0, 9, 9, 9)); + } + if (!Raze.isNam()) runner.Append(new("DRealmsScreen").Init()); + } + runner.Append(new("DukeTitleScreen").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE1End(ScreenJobRunner runner) + { + runner.Append(new("Episode1End1").Init()); + runner.Append(ImageScreen.CreateNamed("E1ENDSCREEN", ScreenJob.fadein|ScreenJob.fadeout|ScreenJob.stopmusic, 0x7fffffff)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE2End(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 1, DukeSnd.WIND_AMBIENCE+1, + 26, DukeSnd.ENDSEQVOL2SND1+1, + 36, DukeSnd.ENDSEQVOL2SND2+1, + 54, DukeSnd.THUD+1, + 62, DukeSnd.ENDSEQVOL2SND3+1, + 75, DukeSnd.ENDSEQVOL2SND4 + 1, + 81, DukeSnd.ENDSEQVOL2SND5 + 1, + 115, DukeSnd.ENDSEQVOL2SND6 + 1, + 124, DukeSnd.ENDSEQVOL2SND7 + 1); + + runner.Append(MoviePlayerJob.CreateWithSoundinfo("cineov2.anm", soundinfo, 0, 18, 18, 18)); + runner.Append(new("E2EndScreen").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE3End(ScreenJobRunner runner) + { + if (gameinfo.gameType & GAMEFLAG_DUKEDC) + { + Array soundinfo; + soundinfo.Pushv(144, DukeSnd.ENDSEQVOL3SND3 + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("radlogo.anm", soundinfo, 0, 10, 10, 10)); + } + else + { + Array soundinfo; + soundinfo.Pushv( + 1, DukeSnd.WIND_REPEAT + 1, + 98, DukeSnd.DUKE_GRUNT + 1, + 102, DukeSnd.THUD + 1, + 102, DukeSnd.SQUISHED + 1, + 124, DukeSnd.ENDSEQVOL3SND3 + 1, + 134, DukeSnd.ENDSEQVOL3SND2 + 1, + 158, DukeSnd.PIPEBOMB_EXPLODE + 1); + + runner.Append(MoviePlayerJob.CreateWithSoundinfo("cineov3.anm", soundinfo, 0, 10, 10, 10)); + runner.Append(BlackScreen.Create(200, ScreenJob.stopsound)); + runner.Append(new("Episode3End").Init()); + if (!Raze.isPlutoPak()) runner.Append(ImageScreen.CreateNamed("DUKETEAM.ANM", ScreenJob.fadein | ScreenJob.fadeout | ScreenJob.stopsound, 0x7fffffff)); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE4End(ScreenJobRunner runner) + { + Array soundinfo; + + soundinfo.Pushv( + 3, DukeSnd.DUKE_UNDERWATER+1, + 35, DukeSnd.VOL4ENDSND1+1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol4e1.anm", soundinfo, 0, 10, 10, 10)); + + soundinfo.Pushv( + 11, DukeSnd.DUKE_UNDERWATER+1, + 20, DukeSnd.VOL4ENDSND1+1, + 39, DukeSnd.VOL4ENDSND2+1, + 50, -1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol4e2.anm", soundinfo, 0, 10, 10, 10)); + + soundinfo.Pushv( + 1, DukeSnd.BOSS4_DEADSPEECH+1, + 40, DukeSnd.VOL4ENDSND1+1, + 40, DukeSnd.DUKE_UNDERWATER+1, + 50, DukeSnd.BIGBANG+1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol4e3.anm", soundinfo, 0, 10, 10, 10)); + + runner.Append(new("Episode4Text").Init()); + runner.Append(ImageScreen.CreateNamed("DUKETEAM.ANM", ScreenJob.fadein | ScreenJob.fadeout | ScreenJob.stopsound, 0x7fffffff)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE5End(ScreenJobRunner runner) + { + runner.Append(new("Episode5End").Init()); + } + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + + static void BuildE4Intro(ScreenJobRunner runner) + { + Array soundinfo; + + Duke.PlaySpecialMusic(Duke.MUS_BRIEFING); + soundinfo.Pushv( + 1, DukeSnd.INTRO4_B + 1, + 12, DukeSnd.SHORT_CIRCUIT + 1, + 18, DukeSnd.INTRO4_5 + 1, + 34, DukeSnd.SHORT_CIRCUIT + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("vol41a.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 10, 10, 10)); + + soundinfo.Pushv( + 1, DukeSnd.INTRO4_1 + 1, + 7, DukeSnd.INTRO4_3 + 1, + 12, DukeSnd.INTRO4_2 + 1, + 26, DukeSnd.INTRO4_4 + 1); + let m = MoviePlayerJob.CreateWithSoundinfo("vol42a.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 14, 14, 14); + if (m) m.skipover = true; + runner.Append(m); + + soundinfo.Pushv( + 10, DukeSnd.INTRO4_6 + 1); + m = MoviePlayerJob.CreateWithSoundinfo("vol43a.anm", soundinfo, 0, 10, 10, 10); + if (m) m.skipover = true; + runner.Append(m); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("DukeMultiplayerBonusScreen").Init(stats.playercount)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + let screen = new("DukeLevelSummaryScreen").Init(map, stats); + runner.Append(screen); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSharewareExit(ScreenJobRunner runner) + { + runner.Append(ImageScreen.CreateNamed("SWEXIT1")); + runner.Append(ImageScreen.CreateNamed("SWEXIT2")); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSharewareEnd(ScreenJobRunner runner) + { + runner.Append(ImageScreen.CreateNamed("ORDERING")); + runner.Append(ImageScreen.CreateNamed("ORDERING1")); + runner.Append(ImageScreen.CreateNamed("ORDERING2")); + runner.Append(ImageScreen.CreateNamed("ORDERING3")); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildLoading(ScreenJobRunner runner, MapRecord map) + { + runner.Append(new("DukeLoadScreen").Init(map)); + } + +} + +class RRCutscenes +{ + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + if (!Raze.isRRRA()) + { + Array soundinfo; + soundinfo.Pushv(1, RRSnd.URANUS + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("rr_intro.anm", soundinfo, 0, 9, 9, 9)); + + soundinfo.Pushv(1, RRSnd.REDNECK2 + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("redneck.anm", soundinfo, 0, 9, 9, 9)); + + soundinfo.Pushv(1, RRSnd.XATRIX + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("xatlogo.anm", soundinfo, 0, 9, 9, 9)); + } + else + { + runner.Append(MoviePlayerJob.Create("redint.mve", 0)); + } + } + } + + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE1End(ScreenJobRunner runner) + { + if (!Raze.isRRRA()) + { + Array soundinfo; + soundinfo.Pushv(1, RRSnd.CHKAMMO + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("turdmov.anm", soundinfo, 0, 9, 9, 9)); + } + } + + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildE2End(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv(1, RRSnd.LN_FINAL + 1); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("rr_outro.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 9, 9, 9)); + runner.Append(ImageScreen.CreateNamed("TENSCREEN")); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildRAE2End(ScreenJobRunner runner) + { + runner.Append(new("RRRAEndOfGame").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("RRLevelSummaryScreen").Init(map, stats, !Raze.isRRRA() || stats.endOfGame)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMapIntro(ScreenJobRunner runner, MapRecord map) + { + int ln = map.levelnumber - 1; + if (ln == 0) return; + if (ln >= 1000) ln -= 1000-7; + + let fn = String.Format("lvl%d.anm", ln); + Array soundinfo; + runner.Append(MoviePlayerJob.CreateWithSoundinfo(fn, soundinfo, 0, 20, 20, 2000)); // wait for a few seconds on the final frame so that the video doesn't stop before the user notices. + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/games/duke/ui/menu.zs b/wadsrc/static/zscript/games/duke/ui/menu.zs index 6c81fbae3..fb701c7a7 100644 --- a/wadsrc/static/zscript/games/duke/ui/menu.zs +++ b/wadsrc/static/zscript/games/duke/ui/menu.zs @@ -1,3 +1,30 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- class DukeMenuDelegate : RazeMenuDelegate { @@ -38,7 +65,7 @@ class DukeMenuDelegate : RazeMenuDelegate String picname; if (!right) picname= String.Format("SPINNINGNUKEICON%d", ((mclock >> 3) % frames)); else picname = String.Format("SPINNINGNUKEICON%d", frames - 1 - ((frames - 1 + (mclock >> 3)) % frames)); - int light = 231 + (Build.calcSinTableValue(mclock<<5) / 768.); + int light = 231 + (Raze.calcSinTableValue(mclock<<5) / 768.); let pe = color(255, light, light, light); Screen.DrawTexture(TexMan.CheckForTexture(picname), false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, DTA_Color, pe, DTA_CenterOffsetRel, true); } @@ -110,7 +137,7 @@ class ListMenuItemDukeLogo : ListMenuItem if (gameinfo.gametype & GAMEFLAG_PLUTOPAK) { int mclock = MSTime() * 120 / 1000; - int light = 223 + (Build.calcSinTableValue(mclock<<4) / 512.); + int light = 223 + (Raze.calcSinTableValue(mclock<<4) / 512.); let pe = Color(255, light, light, light); Screen.DrawTexture(TexMan.CheckForTexture("MENUPLUTOPAKSPRITE"), false, x + 100, 36, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_Color, pe, DTA_CenterOffsetRel, true); } @@ -148,7 +175,7 @@ class ListMenuItemDukeTextItem : ListMenuItemTextItem if (selected) { int mclock = MSTime() * 120 / 1000; - int light = 231 + (Build.calcSinTableValue(mclock<<5) / 512.); + int light = 231 + (Raze.calcSinTableValue(mclock<<5) / 512.); pe = Color(255, light, light, light); } else diff --git a/wadsrc/static/zscript/games/duke/ui/screens.zs b/wadsrc/static/zscript/games/duke/ui/screens.zs new file mode 100644 index 000000000..440e05221 --- /dev/null +++ b/wadsrc/static/zscript/games/duke/ui/screens.zs @@ -0,0 +1,1061 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1996, 2003 - 3D Realms Entertainment +Copyright (C) 2020-2021 Christoph Oelckers + +This file is part of Raze. + +Duke Nukem 3D is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +aint with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Original Source: 1996 - Todd Replogle +Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms +( not much left of the original code, though... ;) ) +*/ +//------------------------------------------------------------------------- + + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DRealmsScreen : SkippableScreenJob +{ + ScreenJob Init() + { + Super.Init(fadein | fadeout); + return self; + } + + override void Start() + { + Duke.PlaySpecialMusic(Duke.MUS_INTRO); + } + + override void OnTick() + { + if (ticks >= 7 * GameTicRate) jobstate = finished; + } + + override void Draw(double smoothratio) + { + let tex = TexMan.CheckForTexture("DREALMS"); + int translation = TexMan.UseGamePalette(tex)? Translation.MakeID(Translation_BasePalette, Duke.DREALMSPAL) : 0; + + screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DukeTitleScreen : SkippableScreenJob +{ + int soundanm; + + ScreenJob Init() + { + Super.Init(fadein | fadeout); + soundanm = 0; + return self; + } + + override void Start() + { + if (Raze.isNam() || userConfig.nologo) Duke.PlaySpecialMusic(Duke.MUS_INTRO); + } + + override void OnTick() + { + int clock = ticks * 120 / GameTicRate; + if (soundanm == 0 && clock >= 120 && clock < 120 + 60) + { + soundanm = 1; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + if (soundanm == 1 && clock > 220 && clock < (220 + 30)) + { + soundanm = 2; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + if (soundanm == 2 && clock >= 280 && clock < 395) + { + soundanm = 3; + if (Raze.isPlutoPak()) Duke.PlaySound(DukeSnd.FLY_BY, CHAN_AUTO, CHANF_UI); + } + else if (soundanm == 3 && clock >= 395) + { + soundanm = 4; + if (Raze.isPlutoPak()) Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + + if (clock > (860 + 120)) + { + jobstate = finished; + } + } + + override void Draw(double smoothratio) + { + int clock = (ticks + smoothratio) * 120 / GameTicRate; + int etrans = Translation.MakeID(Translation_BasePalette, Duke.TITLEPAL); + + // Only translate if the image depends on the global palette. + let tex = TexMan.CheckForTexture("BETASCREEN"); + int trans = TexMan.UseGamePalette(tex)? etrans : 0; + screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal); + + double scale = clamp(clock - 120, 0, 60) / 64.; + if (scale > 0.) + { + let tex = TexMan.CheckForTexture("DUKENUKEM"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + + screen.DrawTexture(tex, true, 160, 104, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffsetRel, true, DTA_TranslationIndex, trans, DTA_ScaleX, scale, DTA_ScaleY, scale); + } + + scale = clamp(clock - 220, 0, 30) / 32.; + if (scale > 0.) + { + let tex = TexMan.CheckForTexture("THREEDEE"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + + screen.DrawTexture(tex, true, 160, 129, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffsetRel, true, DTA_TranslationIndex, trans, DTA_ScaleX, scale, DTA_ScaleY, scale); + } + + if (Raze.isPlutoPak()) + { + scale = (410 - clamp(clock, 280, 395)) / 16.; + if (scale > 0. && clock > 280) + { + let tex = TexMan.CheckForTexture("TITLEPLUTOPAKSPRITE"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + + screen.DrawTexture(tex, true, 160, 151, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffsetRel, true, DTA_TranslationIndex, trans, DTA_ScaleX, scale, DTA_ScaleY, scale); + } + } + } + + override void OnDestroy() + { + Duke.PlaySound(DukeSnd.NITEVISION_ONOFF, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode1End1 : SkippableScreenJob +{ + int bonuscnt; + TextureID bossani; + TextureID breatheani; + bool breathebg; + + const breathe_x = 176; + const breathe_y = 59; + const boss_x = 86; + const boss_y = 59; + + ScreenJob Init() + { + bonuscnt = 0; + breathebg = false; + bossani.SetInvalid(); + breatheani.SetInvalid(); + Super.Init(fadein | fadeout); + return self; + } + + + override void OnTick() + { + static const int breathe_time[] = { 0, 30, 60, 90 }; + static const int breathe_time2[] = { 30, 60, 90, 120 }; + static const String breathe_tile[] = { "VICTORY2", "VICTORY3", "VICTORY2", "" }; + + static const int boss_time[] = { 0, 220, 260, 290, 320, 350, 350 }; + static const int boss_time2[] = { 120, 260, 290, 320, 350, 380, 380 }; + static const String boss_tile[] = { "VICTORY4", "VICTORY5", "VICTORY6", "VICTORY7", "VICTORY8", "VICTORY9", "VICTORY9" }; + + int currentclock = ticks * 120 / GameTicRate; + + bossani.SetInvalid(); + breathebg = false; + breatheani.SetInvalid(); + + // boss + if (currentclock > 390 && currentclock < 780) + { + for (int t = 0, tt = 0; t < 35; t +=5, tt++) if ((currentclock % 390) > boss_time[tt] && (currentclock % 390) <= boss_time2[tt]) + { + if (t == 10 && bonuscnt == 1) + { + Duke.PlaySound(DukeSnd.SHOTGUN_FIRE, CHAN_AUTO, CHANF_UI); + Duke.PlaySound(DukeSnd.SQUISHED, CHAN_AUTO, CHANF_UI); + bonuscnt++; + } + bossani = TexMan.CheckForTexture(boss_tile[tt]); + } + } + + // Breathe + if (currentclock < 450 || currentclock >= 750) + { + if (currentclock >= 750) + { + breathebg = true; + if (currentclock >= 750 && bonuscnt == 2) + { + Duke.PlaySound(DukeSnd.DUKETALKTOBOSS, CHAN_AUTO, CHANF_UI); + bonuscnt++; + } + } + for (int t = 0, tt = 0; t < 20; t += 5, tt++) + if (breathe_tile[tt] != "" && (currentclock % 120) > breathe_time[tt] && (currentclock % 120) <= breathe_time2[tt]) + { + if (t == 5 && bonuscnt == 0) + { + Duke.PlaySound(DukeSnd.BOSSTALKTODUKE, CHAN_AUTO, CHANF_UI); + bonuscnt++; + } + breatheani = TexMan.CheckForTexture(breathe_tile[tt]); + } + } + + } + + override void Draw(double sr) + { + int etrans = Translation.MakeID(Translation_BasePalette, Duke.ENDINGPAL); + + let tex = TexMan.CheckForTexture("VICTORY1"); + int trans = TexMan.UseGamePalette(tex)? etrans : 0; + screen.DrawTexture(tex, false, 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true); + + if (bossani.isValid()) + { + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + screen.DrawTexture(bossani, false, boss_x, boss_y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_TopLeft, true); + } + + if (breathebg) + { + tex = TexMan.CheckForTexture("VICTORY9"); + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + screen.DrawTexture(tex, false, 86, 59, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_TopLeft, true); + } + + if (breatheani.isValid()) + { + trans = TexMan.UseGamePalette(tex)? etrans : 0; // re-check for different texture! + screen.DrawTexture(breatheani, false, breathe_x, breathe_y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_TopLeft, true); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class E2EndScreen : ImageScreen +{ + ScreenJob Init() + { + Super.InitNamed("E2ENDSCREEN", fadein | fadeout | stopsound, 0x7fffffff, 0); + return self; + } + + override void Start() + { + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode3End : ImageScreen +{ + int soundstate; + int finishtime; + + ScreenJob Init() + { + Super.InitNamed("radlogo.anm", fadein|fadeout, 0x7fffffff); + soundstate = 0; + finishtime = 0; + return self; + } + + override void OnSkip() + { + Raze.StopAllSounds(); + } + + override void OnTick() + { + switch (soundstate) + { + case 0: + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND5, CHAN_AUTO, CHANF_UI); + soundstate++; + break; + + case 1: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND5)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND6, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 2: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND6)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND7, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 3: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND7)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND8, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 4: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND8)) + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND9, CHAN_AUTO, CHANF_UI); + soundstate++; + } + break; + + case 5: + if (!Duke.CheckSoundPlaying(DukeSnd.ENDSEQVOL3SND9)) + { + soundstate++; + finishtime = ticks + GameTicRate * (Raze.SoundEnabled() ? 1 : 5); // if sound is off this wouldn't wait without a longer delay here. + } + break; + + case 6: + if (Raze.isPlutoPak()) + { + if (ticks > finishtime) jobstate = finished; + } + break; + + default: + break; + } + if (jobstate != running) Raze.StopAllSounds(); + } + + override void OnDestroy() + { + if (!Raze.isPlutoPak()) Duke.PlaySound(DukeSnd.ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode4Text : SkippableScreenJob +{ + ScreenJob Init() + { + Super.Init(fadein|fadeout); + return self; + } + + + override void Draw(double sm) + { + Duke.BigText(160, 60, "$Thanks to all our", 0); + Duke.BigText(160, 60 + 16, "$fans for giving", 0); + Duke.BigText(160, 60 + 16 + 16, "$us big heads.", 0); + Duke.BigText(160, 70 + 16 + 16 + 16, "$Look for a Duke Nukem 3D", 0); + Duke.BigText(160, 70 + 16 + 16 + 16 + 16, "$sequel soon.", 0); + } + + override void Start() + { + Duke.PlaySound(DukeSnd.ENDSEQVOL3SND4, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class Episode5End : ImageScreen +{ + ScreenJob Init() + { + Super.InitNamed("FIREFLYGROWEFFECT", fadein|fadeout|stopsound); + return self; + } + + override void OnTick() + { + if (ticks == 1) Duke.PlaySound(DukeSnd.E5L7_DUKE_QUIT_YOU, CHAN_AUTO, CHANF_UI); + } +} + +//--------------------------------------------------------------------------- +// +// This handles both Duke and RR. +// +//--------------------------------------------------------------------------- + +class DukeMultiplayerBonusScreen : SkippableScreenJob +{ + int playerswhenstarted; + + ScreenJob Init(int pws) + { + Super.Init(fadein|fadeout); + playerswhenstarted = pws; + return self; + } + + override void Start() + { + if (!Raze.isRR()) Duke.PlayBonusMusic(); + } + + override void Draw(double smoothratio) + { + bool isRR = Raze.isRR(); + double titlescale = isRR? 0.36 : 1; + + String tempbuf; + int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); + Screen.DrawTexture(TexMan.CheckForTexture("MENUSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal); + Screen.DrawTexture(TexMan.CheckForTexture("INGAMEDUKETHREEDEE"), true, 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, DTA_ScaleX, titlescale, DTA_ScaleY, titlescale); + if (Raze.isPlutoPak()) Screen.DrawTexture(TexMan.CheckForTexture("MENUPLUTOPAKSPRITE"), true, 260, 36, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true); + + Duke.GameText(160, isRR? 58 : 58 + 2, "$Multiplayer Totals", 0, 0); + Duke.GameText(160, 58 + 10, currentLevel.DisplayName(), 0, 0); + Duke.GameText(160, 165, "$Presskey", 8 - int(sin(currentclock / 10.) * 8), 0); + + int t = 0; + + Duke.MiniText(38, 80, "$Name", 0, -1, isRR? 0 : 8); + Duke.MiniText(269+20, 80, "$Kills", 0, 1, isRR? 0: 8); + + for (int i = 0; i < playerswhenstarted; i++) + { + tempbuf = String.Format("%-4d", i + 1); + Duke.MiniText(92 + (i * 23), 80, tempbuf, 0, -1, isRR? 0: 3); + } + + for (int i = 0; i < playerswhenstarted; i++) + { + int xfragtotal = 0; + tempbuf = String.Format("%d", i + 1); + + Duke.MiniText(30, 90 + t, tempbuf, 0); + Duke.MiniText(38, 90 + t, Raze.PlayerName(i), 0, -1, Raze.playerPalette(i)); + + for (int y = 0; y < playerswhenstarted; y++) + { + int frag = Raze.playerFrags(i, y); + if (i == y) + { + int fraggedself = Raze.playerFraggedSelf(y); + tempbuf = String.Format("%-4d", fraggedself); + Duke.MiniText(92 + (y * 23), 90 + t, tempbuf, 0, -1, isRR? 0: 2); + xfragtotal -= fraggedself; + } + else + { + tempbuf = String.Format("%-4d", frag); + Duke.MiniText(92 + (y * 23), 90 + t, tempbuf, 0); + xfragtotal += frag; + } + /* + if (myconnectindex == connecthead) + { + tempbuf = String.Format("stats %ld killed %ld %ld\n", i + 1, y + 1, frag); + sendscore(tempbuf); + } + */ + } + + tempbuf = String.Format("%-4d", xfragtotal); + Duke.MiniText(101 + (8 * 23), 90 + t, tempbuf, 0, -1, isRR? 0: 2); + + t += 7; + } + + for (int y = 0; y < playerswhenstarted; y++) + { + int yfragtotal = 0; + for (int i = 0; i < playerswhenstarted; i++) + { + if (i == y) + yfragtotal += Raze.playerFraggedself(i); + int frag = Raze.playerFrags(i, y); + yfragtotal += frag; + } + tempbuf = String.Format("%-4d", yfragtotal); + Duke.MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0, -1, isRR? 0: 2); + } + + Duke.MiniText(45, 96 + (8 * 7), "$Deaths", 0, -1, isRR? 0: 8); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DukeLevelSummaryScreen : SummaryScreenBase +{ + String lastmapname; + int speech; + int displaystate; + int dukeAnimStart; + + TextureID texBg; + TextureID texOv[4]; + + enum EScreenFlags + { + printTimeText = 1, + printTimeVal = 2, + printKillsText = 4, + printKillsVal = 8, + printSecretsText = 16, + printSecretsVal = 32, + printStatsAll = 63, + dukeAnim = 64, + dukeWait = 128, + + } + + ScreenJob Init(MapRecord m, SummaryInfo s) + { + Super.Init(fadein | fadeout); + SetParameters(m, s); + String basetex = level.InterBackground; + if (basetex.length() == 0) + { + let cluster = level.GetCluster(); + if (cluster != null) basetex = cluster.InterBackground; + } + if (basetex.length() == 0) basetex = "BONUSSCREEN"; + texBg = TexMan.CheckForTexture(basetex); + for(int i = 0; i < 4; i++) + { + String otex = String.Format("%s_O%d", basetex, i+1); + texOv[i] = TexMan.CheckForTexture(otex); + } + lastmapname = level.DisplayName(); + speech = -1; + displaystate = 0; + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + if ((displaystate & printStatsAll) != printStatsAll) + { + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + displaystate = printStatsAll; + } + else if (!(displaystate & dukeAnim)) + { + displaystate |= dukeAnim; + dukeAnimStart = ticks; + Duke.PlaySound(DukeSnd.SHOTGUN_COCK, CHAN_AUTO, CHANF_UI); + static const int speeches[] = { DukeSnd.BONUS_SPEECH1, DukeSnd.BONUS_SPEECH2, DukeSnd.BONUS_SPEECH3, DukeSnd.BONUS_SPEECH4 }; + speech = speeches[random(0, 3)]; + Duke.PlaySound(speech, CHAN_AUTO, CHANF_UI, 1); + } + return true; + } + return false; + } + + override void Start() + { + Duke.PlayBonusMusic(); + } + + override void OnTick() + { + if ((displaystate & printStatsAll) != printStatsAll) + { + if (ticks == 15 * 3) + { + displaystate |= printTimeText; + } + else if (ticks == 15 * 4) + { + displaystate |= printTimeVal; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 6) + { + displaystate |= printKillsText; + Duke.PlaySound(DukeSnd.FLY_BY, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 7) + { + displaystate |= printKillsVal; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 9) + { + displaystate |= printSecretsText; + } + else if (ticks == 15 * 10) + { + displaystate |= printSecretsVal; + Duke.PlaySound(DukeSnd.PIPEBOMB_EXPLODE, CHAN_AUTO, CHANF_UI); + } + } + if (displaystate & dukeAnim) + { + if (ticks >= dukeAnimStart + 60) + { + displaystate ^= dukeAnim | dukeWait; + } + } + if (displaystate & dukeWait) + { + if (speech <= 0 || !Duke.CheckSoundPlaying(speech)) + jobstate = finished; + } + } + + void PrintTime() + { + String tempbuf; + Duke.GameText(10, 59 + 9, "$TXT_YourTime", 0); + Duke.GameText(10, 69 + 9, "$TXT_ParTime", 0); + if (!Raze.isNamWW2GI()) + Duke.GameText(10, 79 + 9, "$TXT_3DRTIME", 0); + + if (displaystate & printTimeVal) + { + tempbuf = FormatTime(stats.time); + Duke.GameText((320 >> 2) + 71, 59 + 9, tempbuf, 0); + + tempbuf = FormatTime(level.parTime); + Duke.GameText((320 >> 2) + 71, 69 + 9, tempbuf, 0); + + if (!Raze.isNamWW2GI()) + { + tempbuf = FormatTime(level.designerTime); + Duke.GameText((320 >> 2) + 71, 79 + 9, tempbuf, 0); + } + } + } + + void PrintKills() + { + String tempbuf; + Duke.GameText(10, 94 + 9, "$TXT_EnemiesKilled", 0); + Duke.GameText(10, 104 + 9, "$TXT_EnemiesLeft", 0); + + if (displaystate & printKillsVal) + { + tempbuf = String.Format("%-3d", stats.kills); + Duke.GameText((320 >> 2) + 70, 94 + 9, tempbuf, 0); + + if (stats.maxkills < 0) + { + tempbuf = "$TXT_N_A"; + } + else + { + tempbuf = String.Format("%-3d", max(0, stats.maxkills - stats.kills)); + } + Duke.GameText((320 >> 2) + 70, 104 + 9, tempbuf, 0); + } + } + + void PrintSecrets() + { + String tempbuf; + Duke.GameText(10, 119 + 9, "$TXT_SECFND", 0); + Duke.GameText(10, 129 + 9, "$TXT_SECMISS", 0); + + if (displaystate & printSecretsVal) + { + tempbuf = String.Format("%-3d", stats.secrets); + Duke.GameText((320 >> 2) + 70, 119 + 9, tempbuf, 0); + tempbuf = String.Format("%-3d", max(0, stats.maxsecrets - stats.secrets)); + Duke.GameText((320 >> 2) + 70, 129 + 9, tempbuf, 0); + } + } + + override void Draw(double sr) + { + Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + + Duke.GameText(160, 190, "$PRESSKEY", 8 - (sin(ticks * 4) * 8), 0); + + if (displaystate & printTimeText) + { + PrintTime(); + } + if (displaystate & printKillsText) + { + PrintKills(); + } + if (displaystate & printSecretsText) + { + PrintSecrets(); + } + + if (displaystate & dukeAnim) + { + switch (((ticks - dukeAnimStart) >> 2) % 15) + { + case 0: + case 1: + case 4: + case 5: + Screen.DrawTexture(texOv[2], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + case 2: + case 3: + Screen.DrawTexture(texOv[3], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + } + } + else if (!(displaystate & dukeWait)) + { + switch((ticks >> 3) & 3) + { + case 1: + case 3: + Screen.DrawTexture(texOv[0], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + case 2: + Screen.DrawTexture(texOv[1], true, 199, 31, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true); + break; + } + } + + if (lastmapname) Duke.BigText(160, 20 - 6, lastmapname, 0); + Duke.BigText(160, 36 - 6, "$Completed", 0); + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class RRLevelSummaryScreen : SummaryScreenBase +{ + String lastmapname; + int speech; + int displaystate; + int exitSoundStart; + TextureID texBg; + + enum EFlags + { + printTimeText = 1, + printTimeVal = 2, + printKillsText = 4, + printKillsVal = 8, + printSecretsText = 16, + printSecretsVal = 32, + printStatsAll = 63, + exitSound = 64, + exitWait = 128, + + } + + ScreenJob Init(MapRecord m, SummaryInfo s, bool dofadeout = true) + { + Super.Init(dofadeout? (fadein | fadeout) : fadein); + SetParameters(m, s); + String basetex = level.InterBackground; + if (basetex.length() == 0) + { + let cluster = level.GetCluster(); + if (cluster != null) basetex = cluster.InterBackground; + } + if (basetex.length() == 0) basetex = "BONUSPIC01"; + + lastmapname = level.DisplayName(); + texBg = TexMan.CheckForTexture(basetex); + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + if ((displaystate & printStatsAll) != printStatsAll) + { + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + displaystate = printStatsAll; + } + else if (!(displaystate & exitSound)) + { + displaystate |= exitSound; + exitSoundStart = ticks; + Duke.PlaySound(RRSnd.CHUG, CHAN_AUTO, CHANF_UI); + static const int speeches[] = { RRSnd.BNS_SPCH1, RRSnd.BNS_SPCH2, RRSnd.BNS_SPCH3, RRSnd.BNS_SPCH4 }; + speech = speeches[random(0, 3)]; + Duke.PlaySound(speech, CHAN_AUTO, CHANF_UI); + } + return true; + } + return false; + } + + override void OnTick() + { + if ((displaystate & printStatsAll) != printStatsAll) + { + if (ticks == 15 * 3) + { + displaystate |= printTimeText; + } + else if (ticks == 15 * 4) + { + displaystate |= printTimeVal; + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 6) + { + displaystate |= printKillsText; + } + else if (ticks == 15 * 7) + { + displaystate |= printKillsVal; + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + } + else if (ticks == 15 * 9) + { + displaystate |= printSecretsText; + } + else if (ticks == 15 * 10) + { + displaystate |= printSecretsVal; + Duke.PlaySound(RRSnd.FART1, CHAN_AUTO, CHANF_UI); + } + } + if (displaystate & exitSound) + { + if (ticks >= exitSoundStart + 60) + { + displaystate ^= exitSound | exitWait; + } + } + if (displaystate & exitWait) + { + if (speech <= 0 || !Duke.CheckSoundPlaying(speech)) + jobstate = finished; + } + } + + void PrintTime() + { + String tempbuf; + Duke.BigText(30, 48, "$TXT_YerTime", -1); + Duke.BigText(30, 64, "$TXT_ParTime", -1); + Duke.BigText(30, 80, "$TXT_XTRTIME", -1); + + if (displaystate & printTimeVal) + { + tempbuf = FormatTime(stats.time); + Duke.BigText(191, 48, tempbuf, -1); + + tempbuf = FormatTime(level.parTime); + Duke.BigText(191, 64, tempbuf, -1); + + tempbuf = FormatTime(level.designerTime); + Duke.BigText(191, 80, tempbuf, -1); + } + } + + void PrintKills() + { + String tempbuf; + Duke.BigText(30, 112, "$TXT_VarmintsKilled", -1); + Duke.BigText(30, 128, "$TXT_VarmintsLeft", -1); + + if (displaystate & printKillsVal) + { + tempbuf = String.Format("%-3d", stats.kills); + Duke.BigText(231, 112, tempbuf, -1); + if (stats.maxkills < 0) + { + tempbuf = "$TXT_N_A"; + } + else + { + tempbuf = String.Format("%-3d", max(0, stats.maxkills - stats.kills)); + } + Duke.BigText(231, 128, tempbuf, -1); + } + } + + void PrintSecrets() + { + String tempbuf; + Duke.BigText(30, 144, "$TXT_SECFND", -1); + Duke.BigText(30, 160, "$TXT_SECMISS", -1); + + if (displaystate & printSecretsVal) + { + tempbuf = String.Format("%-3d", stats.secrets); + Duke.BigText(231, 144, tempbuf, -1); + tempbuf = String.Format("%-3d", max(0, stats.maxsecrets - stats.secrets)); + Duke.BigText(231, 160, tempbuf, -1); + } + } + + override void Draw(double sr) + { + Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + + Duke.BigText(80, 16, lastmapname, -1); + Duke.BigText(15, 192, "$PRESSKEY", -1); + + if (displaystate & printTimeText) + { + PrintTime(); + } + if (displaystate & printKillsText) + { + PrintKills(); + } + if (displaystate & printSecretsText) + { + PrintSecrets(); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class RRRAEndOfGame : SkippableScreenJob +{ + ScreenJob Init() + { + Super.Init(fadein|fadeout); + return self; + } + + override void OnSkip() + { + Duke.StopSound(RRSnd.LN_FINAL); + } + + override void Start() + { + Duke.PlaySound(RRSnd.LN_FINAL, CHAN_AUTO, CHANF_UI); + } + + override void OnTick() + { + if (!Duke.CheckSoundPlaying(RRSnd.LN_FINAL) && ticks > 15 * GameTicRate) jobstate = finished; // make sure it stays, even if sound is off. + } + + override void Draw(double sr) + { + let tex = TexMan.CheckForTexture(((ticks >> 2) & 1)? "ENDGAME2" : "ENDGAME"); + Screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class DukeLoadScreen : ScreenJob +{ + MapRecord rec; + + ScreenJob Init(MapRecord maprec) + { + Super.Init(fadein); + rec = maprec; + return self; + } + + override void OnTick() + { + if (fadestate == visible) jobstate = finished; + } + + override void Draw(double sr) + { + Screen.DrawTexture(TexMan.CheckForTexture("LOADSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + + if (!Raze.IsRR()) + { + Duke.BigText(160, 90, (rec.flags & MapRecord.USERMAP)? "$TXT_LOADUM" : "$TXT_LOADING", 0); + Duke.BigText(160, 114, rec.DisplayName(), 0); + } + else + { + int y = Raze.isRRRA()? 140 : 90; + Duke.BigText(160, y, (rec.flags & MapRecord.USERMAP)? "$TXT_ENTRUM" : "$TXT_ENTERIN", 0); + Duke.BigText(160, y+24, rec.DisplayName(), 0); + } + } +} + diff --git a/wadsrc/static/zscript/games/exhumed/exhumedgame.zs b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs new file mode 100644 index 000000000..49597d2f8 --- /dev/null +++ b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs @@ -0,0 +1,104 @@ + + +struct Exhumed native +{ + native static void PlayLocalSound(int snd, int pitch, bool b, int chanf); + native static void StopLocalSound(); + native static bool LocalSoundPlaying(); + native static void playCDTrack(int track, bool looped); + native static void DrawPlasma(); + + + static void DrawAbs(String img, int x, int y, int shade = 0) + { + Screen.DrawTexture(TexMan.CheckForTexture(img, TexMan.Type_Any), false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_Color, Raze.shadeToLight(shade)); + } + + static void DRawRel(String img, int x, int y, int shade = 0) + { + let tex = TexMan.CheckForTexture(img, TexMan.Type_Any); + if (!tex.IsValid()) return; + let size = TexMan.GetScaledSize(tex); + let offs = TexMan.GetScaledOffset(tex); + // The integer truncation here is important. Old Build versions were bugged here. + x -= (int(size.x) >> 1) + int(offs.x); + y -= (int(size.y) >> 1) + int(offs.y); + Screen.DrawTexture(tex, false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_Color, Raze.shadeToLight(shade)); + } +} + + +struct ExhumedSnd native +{ + enum ESounds + { + kSound0 = 0, + kSound1, + kSound2, + kSound3, + kSound4, + kSound5, + kSound6, + kSound7, + kSound8, + kSound9, + kSoundItemSpecial, + kSound11, + kSoundTorchOn, + kSound13, + kSound14, + kSound15, + kSound16, + kSound17, + kSound18, + kSound19, + kSound20, + kSound21, + kSound22, + kSound23, + kSound24, + kSound25, + kSound26, + kSound27, + kSoundJonLaugh2, + kSound29, + kSound30, + kSound31, + kSound32, + kSound33, + kSound34, + kSound35, + kSound36, + kSound38 = 38, + kSound39, + kSound40, + kSound41, + kSound42, + kSound43, + kSound47 = 47, + kSound48 = 48, + kSoundQTail = 50, + kSound52 = 52, + kSoundTauntStart = 53, + kSoundJonFDie = 60, + kSound61, + kSound62, + kSound63, + kSound64, + kSound65, + kSound66, + kSoundMana1, + kSoundMana2, + kSoundAmmoPickup, + kSound70, + kSound71, + kSound72, + kSoundAlarm, + kSound74, + kSound75, + kSound76, + kSound77, + kSound78, + kSound79, + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/games/exhumed/ui/menu.zs b/wadsrc/static/zscript/games/exhumed/ui/menu.zs index 52101751d..46b908fb1 100644 --- a/wadsrc/static/zscript/games/exhumed/ui/menu.zs +++ b/wadsrc/static/zscript/games/exhumed/ui/menu.zs @@ -50,7 +50,10 @@ class ListMenuItemExhumedPlasma : ListMenuItem Super.Init(0, 0); } - native override void Draw(bool selected, ListMenuDescriptor desc); + override void Draw(bool selected, ListMenuDescriptor desc) + { + Exhumed.DrawPlasma(); + } } class ListMenuItemExhumedLogo : ListMenuItem @@ -98,10 +101,10 @@ class ListMenuItemExhumedTextItem : ListMenuItemTextItem double y = mYpos + v.y / 2; int shade; - if (selected) shade = Build.CalcSinTableValue(MSTime() * 16 * 120 / 1000) >> 9; + if (selected) shade = Raze.CalcSinTableValue(MSTime() * 16 * 120 / 1000) >> 9; else if (Selectable()) shade = 0; else shade = 25; - let color = Build.shadeToLight(shade); + let color = Raze.shadeToLight(shade); double scalex = 1.; // Squash the text if it is too wide. Due to design limitations we cannot expand the box here. :( if (texsize.X - 18 < width) diff --git a/wadsrc/static/zscript/games/exhumed/ui/screens.zs b/wadsrc/static/zscript/games/exhumed/ui/screens.zs new file mode 100644 index 000000000..133e16418 --- /dev/null +++ b/wadsrc/static/zscript/games/exhumed/ui/screens.zs @@ -0,0 +1,927 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 sirlemonhead, Nuke.YKT +Copyright (C) 2020-2021 Christoph Oelckers +This file is part of Raze. +PCExhumed is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 2 +as published by the Free Software Foundation. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//------------------------------------------------------------------------- + +struct LMFDecoder native +{ + static native bool Identify(String fn); + static native LMFDecoder Create(String fn); + native bool Frame(double clock); + native TextureID GetTexture(); + native void Close(); +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LmfPlayer : SkippableScreenJob +{ + LMFDecoder decoder; + double nextclock; + String fn; + + ScreenJob Init(String filename) + { + fn = filename; + return self; + } + + override void Start() + { + decoder = LMFDecoder.Create(fn); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void Draw(double smoothratio) + { + double clock = (ticks + smoothratio) * 1000000000. / GameTicRate; + if (clock >= nextclock) + { + if (decoder.Frame(clock)) + { + jobstate = finished; + return; + } + } + + double duration = clock * (120. / 8000000000.); + double z = 2048 * duration; + if (z > 65536) z = 65536; + + double angle = 1536. + 16. * duration; + if (angle >= 2048.) angle = 0.; + + Screen.DrawTexture(decoder.getTexture(), false, 160, 100, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffset, true, DTA_FlipY, true, DTA_ScaleX, z / 65536., DTA_ScaleY, z / 65536., DTA_Rotate, (-angle - 512) * (360. / 2048.)); + } + + override void OnDestroy() + { + decoder.Close(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LobotomyScreen : ImageScreen +{ + ScreenJob Init(String texname, int fade) + { + Super.InitNamed(texname, fade); + return self; + } + + override void OnSkip() + { + Exhumed.StopLocalSound(); + } + + override void Start() + { + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 7000, false, CHANF_UI); + } + + override void OnTick() + { + Super.OnTick(); + if (jobstate == finished) Exhumed.StopLocalSound(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + + +class MainTitle : SkippableScreenJob +{ + String a, b; + int mystate; + int duration; + int var_4; + int esi; + int nCount; + int starttime; + static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 }; + + ScreenJob Init() + { + Super.Init(fadein); + a = StringTable.Localize("$TXT_EX_COPYRIGHT1"); + b = StringTable.Localize("$TXT_EX_COPYRIGHT2"); + duration = skullDurations[0]; + esi = 130; + return self; + } + + override void Start() + { + Exhumed.PlayLocalSound(59, 0, true, CHANF_UI); + Exhumed.playCDtrack(19, true); + } + + override void OnTick() + { + int ticker = ticks * 120 / GameTicRate; + if (ticks > 1 && mystate == 0 && !Exhumed.LocalSoundPlaying()) + { + if (random(0, 15)) + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 0, false, CHANF_UI); + else + Exhumed.PlayLocalSound(61, 0, false, CHANF_UI); + mystate = 1; + starttime = ticker; + } + if (mystate == 1) + { + if (ticker > duration) + { + nCount++; + if (nCount > 12) + { + jobstate = finished; + return; + } + duration = starttime + skullDurations[nCount]; + var_4 = var_4 == 0; + } + } + } + + override void Draw(double sr) + { + Exhumed.DrawPlasma(); + Exhumed.DrawRel("SkullHead", 160, 100); + if (mystate == 0) + { + Exhumed.DrawRel("SkullJaw", 161, 130); + } + else + { + int nStringWidth = SmallFont.StringWidth(a); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200); + nStringWidth = SmallFont.StringWidth(b); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200); + + + String nTile = "SkullJaw"; + + if (var_4) + { + if (esi >= 135) nTile = "SkullJaw2"; + else esi += 5; + } + else if (esi <= 130) esi = 130; + else esi -= 2; + + int y; + + if (nTile == "SkullJaw2") + { + y = 131; + } + else + { + y = esi; + if (y > 135) y = 135; + } + + Exhumed.DrawRel(nTile, 161, y); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class MapScreen : ScreenJob +{ + static const int MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 }; + static const int MapPlaqueX[] = { 100, 230, 180, 10, 210, 10, 10, 140, 30, 200, 145, 80, 15, 220, 190, 20, 220, 20, 200, 20 }; + static const int MapPlaqueY[] = { 170, 10, 125, 95, 160, 110, 50, 0, 20, 150, 170, 80, 0, 35, 40, 130, 160, 10, 10, 10 }; + static const int MapPlaqueTextX[] = { 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 18, 18, 18, 18, 18, 19, 18, 18, 18, 19 }; + static const int MapPlaqueTextY[] = { 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 6, 5, 6, 6, 6, 6, 6, 5, 4 }; + + static const int FireTilesX[] = { 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1 }; + static const int FireTilesY[] = { 3, 0, 3, 0, 0, 0, 1, 1, 2, 0, 2, 0 }; + + static const int MapLevelFires[] = { + 3, 0, 107, 95 , 1, 58, 140 , 2, 28, 38 , + 3, 2, 240, 0 , 0, 237, 32 , 1, 200, 30 , + 2, 2, 250, 57 , 0, 250, 43 , 2, 200, 70 , + 2, 1, 82, 59 , 2, 84, 16 , 0, 10, 95 , + 2, 2, 237, 50 , 1, 215, 42 , 1, 210, 50 , + 3, 0, 40, 7 , 1, 75, 6 , 2, 100, 10 , + 3, 0, 58, 61 , 1, 85, 80 , 2, 111, 63 , + 3, 0, 260, 65 , 1, 228, 0 , 2, 259, 15 , + 2, 0, 81, 38 , 2, 58, 38 , 2, 30, 20 , + 3, 0, 259, 49 , 1, 248, 76 , 2, 290, 65 , + 3, 2, 227, 66 , 0, 224, 98 , 1, 277, 30 , + 2, 0, 100, 10 , 2, 48, 76 , 2, 80, 80 , + 3, 0, 17, 2 , 1, 29, 49 , 2, 53, 28 , + 3, 0, 266, 42 , 1, 283, 99 , 2, 243, 108 , + 2, 0, 238, 19 , 2, 240, 92 , 2, 190, 40 , + 2, 0, 27, 0 , 1, 70, 40 , 0, 20, 130 , + 3, 0, 275, 65 , 1, 235, 8 , 2, 274, 6 , + 3, 0, 75, 45 , 1, 152, 105 , 2, 24, 68 , + 3, 0, 290, 25 , 1, 225, 63 , 2, 260, 110 , + 0, 1, 20, 10 , 1, 20, 10 , 1, 20, 10 + }; + + const FIRE_SIZE = 10; + const FIRE_TYPE = 1; + const FIRE_XOFS = 2; + const FIRE_YOFS = 3; + const FIRE_ELEMENT_SIZE = 3; + + int x; + int delta; + int nIdleSeconds; + + int curYPos, destYPos; + int nLevel, nLevelNew, nLevelBest; + + native static void SetNextLevel(int num); + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + ScreenJob Init(int oldlevel, int newlevel, int maxlevel) + { + Super.Init(fadein|fadeout); + nLevel = oldlevel - 1; + nLevelNew = newlevel - 1; + nLevelBest = min(maxlevel, 19) - 1; + curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2)); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + if (curYPos < destYPos) delta = 2; + else if (curYPos > destYPos) delta = -2; + // Trim smoke in widescreen + /* + vec2_t mapwinxy1 = windowxy1, mapwinxy2 = windowxy2; + int32_t width = mapwinxy2.x - mapwinxy1.x + 1, height = mapwinxy2.y - mapwinxy1.y + 1; + if (3 * width > 4 * height) + { + mapwinxy1.x += (width - 4 * height / 3) / 2; + mapwinxy2.x -= (width - 4 * height / 3) / 2; + } + */ + return self; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown) + { + int key = ev.KeyScan; + let binding = Bindings.GetBinding(key); + if (key == InputEvent.KEY_UPARROW || key == InputEvent.KEY_PAD_DPAD_UP || key == InputEvent.Key_kpad_8 || binding ~== "+move_forward") + { + if (curYPos == destYPos && nLevelNew <= nLevelBest) + { + nLevelNew++; + SetNextLevel(nLevelNew + 1); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) delta = 2; + else delta = -2; + nIdleSeconds = 0; + } + return true; + } + + if (key == InputEvent.KEY_DOWNARROW || key == InputEvent.KEY_PAD_DPAD_DOWN || key == InputEvent.Key_kpad_2 || binding ~== "+move_backward") + { + if (curYPos == destYPos && nLevelNew > 0) + { + nLevelNew--; + SetNextLevel(nLevelNew + 1); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) delta = 2; + else delta = -2; + nIdleSeconds = 0; + } + return true; + } + if (!Raze.specialKeyEvent(ev)) jobstate = skipped; + return true; + } + return false; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void OnTick() + { + if (curYPos != destYPos) + { + // scroll the map every couple of ms + curYPos += delta; + + if ((curYPos > destYPos && delta > 0) || (curYPos < destYPos && delta < 0)) + curYPos = destYPos; + + nIdleSeconds = 0; + } + else nIdleSeconds++; + if (nIdleSeconds > 300) jobstate = finished; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void Draw(double smoothratio) + { + int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); + + int tileY = curYPos; + + // Draw the background screens + for (int i = 0; i < 10; i++) + { + let tex = String.Format("MapBG%02d", i+1); + Exhumed.DrawAbs(tex, x, tileY); + tileY -= 200; + } + + // for each level - drawing the 'level completed' on-fire smoke markers + for (int i = 0; i < 20; i++) + { + int screenY = (i >> 1) * -200; + + if (nLevelBest >= i) // check if the player has finished this level + { + for (int j = 0; j < MapLevelFires[i * FIRE_SIZE]; j++) + { + int nFireFrame = ((currentclock >> 4) & 3); + int elem = i * FIRE_SIZE + FIRE_ELEMENT_SIZE * j; + int nFireType = MapLevelFires[elem + FIRE_TYPE]; + int x = MapLevelFires[elem + FIRE_XOFS]; + int y = MapLevelFires[elem + FIRE_YOFS]; + + String nTile = String.Format("MAPFIRE_%d%d", nFireType+1, nFireFrame+1); + int smokeX = x + FireTilesX[nFireType*3 + nFireFrame]; + int smokeY = y + FireTilesY[nFireType*3 + nFireFrame] + curYPos + screenY; + + // Use rotatesprite to trim smoke in widescreen + Exhumed.DrawAbs(nTile, smokeX, smokeY); + // Todo: mask out the sides of the screen if the background is not widescreen. + } + } + + int t = (((currentclock & 16) >> 4)); + + String nTile = String.Format("MapPlaque%d_%02d", t+1, i+1); + + int nameX = mapPlaqueX[i]; + int nameY = mapPlaqueY[i] + curYPos + screenY; + + // Draw level name plaque + Exhumed.DrawAbs(nTile, nameX, nameY); + + int shade = 96; + + if (nLevelNew == i) + { + shade = (Raze.bsin(16 * currentclock) + 31) >> 8; + } + else if (nLevelBest >= i) + { + shade = 31; + } + + int textY = nameY + MapPlaqueTextY[i]; + int textX = nameX + MapPlaqueTextX[i]; + nTile = String.Format("MapPlaqueText_%02d", i+1); + + // draw the text, alternating between red and black + Exhumed.DrawAbs(nTile, textX, textY, shade); + } + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class TextOverlay +{ + int nHeight; + double nCrawlY; + int palette; + BrokenLines screentext; + + void Init(String text, int pal) + { + screentext = SmallFont.BreakLines(StringTable.Localize(text), 320); + nCrawlY = 199; + nHeight = screentext.Count() * 10; + palette = pal; + } + + void DisplayText() + { + if (nHeight + nCrawlY > 0) + { + double y = nCrawlY; + for (int i = 0; i < screentext.Count() && y <= 199; i++) + { + if (y >= -10) + { + int x = 160 - screenText.StringWidth(i)/2; + Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, palette); + } + y += 10; + } + } + } + + bool AdvanceCinemaText(double clock) + { + if (nHeight + nCrawlY > 0 || musplaying.handle) + { + nCrawlY = 199 - clock / 15.; + return false; + } + return true; + } +} + +//--------------------------------------------------------------------------- +// +// cinema (this has been stripped off all game logic that was still in here) +// +//--------------------------------------------------------------------------- + +class Cinema : SkippableScreenJob +{ + TextOverlay textov; + TextureID cinematile; + int currentCinemaPalette; + int cdtrack; + int palette; + bool done; + + ScreenJob Init(String bgTexture, String text, int pal, int cdtrk) + { + Super.Init(fadein|fadeout); + cinematile = TexMan.CheckForTexture(bgTexture, TexMan.Type_Any); + textov = new("TextOverlay"); + palette = Translation.MakeID(Translation_BasePalette, pal); + textov.Init(text, palette); + cdtrack = cdtrk; + return self; + } + + override void Start() + { + Raze.StopAllSounds(); + if (cdtrack != -1) + { + Exhumed.playCDtrack(cdtrack, false); + } + } + + override void OnTick() + { + if (done) jobstate = finished; + } + + override void Draw(double smoothratio) + { + Screen.DrawTexture(cinematile, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, palette); + textov.DisplayText(); + done = textov.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate)); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LastLevelCinema : ScreenJob +{ + int var_24; + int var_28; + + int ebp; + int phase; + int nextclock; + uint nStringTypeOn, nCharTypeOn; + int screencnt; + bool skiprequest; + + BrokenLines screentext; + Font printFont; + TextureID tex; + + ScreenJob Init() + { + Super.Init(fadein | fadeout); + var_24 = 16; + var_28 = 12; + nextclock = 4; + let p = StringTable.Localize("REQUIRED_CHARACTERS", false); + if (p == "REQUIRED_CHARACTERS") printFont = SmallFont2; + else printFont = ConFont; + return self; + } + + native static TextureID DoStatic(int a, int b); + native static TextureID UndoStatic(); + + void Phase1() + { + if (var_24 >= 116) + { + if (var_28 < 192) + var_28 += 20; + } + else + { + var_24 += 20; + } + + tex = DoStatic(var_28, var_24); + } + + bool InitPhase2() + { + let label = StringTable.Localize(String.Format("$TXT_EX_LASTLEVEL%d", screencnt + 1)); + screentext = printFont.BreakLines(label, 320); + if (screentext.Count() == 0) return false; + + nStringTypeOn = 0; + nCharTypeOn = 0; + + ebp = screentext.Count() * 4; // half height of the entire text + ebp = 81 - ebp; // offset from the screen's center. + tex = UndoStatic(); + return true; + } + + bool Phase3() + { + tex = DoStatic(var_28, var_24); + + if (var_28 > 20) + { + var_28 -= 20; + return true; + } + + if (var_24 > 20) + { + var_24 -= 20; + return true; + } + return false; + } + + void DisplayPhase2() + { + int yy = ebp; + + // for international content, use the generic 8x8 font. The original one is too small for expansion. + if (printFont == ConFont) + { + yy *= 2; + for (int i = 0; i < nStringTypeOn; i++, yy += 10) Screen.DrawText(ConFont, Font.CR_GREEN, 140, yy, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit640x400); + Screen.DrawText(ConFont, Font.CR_GREEN, 140, yy, screentext.StringAt(nStringTypeOn), DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn); + } + else + { + for (int i = 0; i < nStringTypeOn; i++, yy += 8) Screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, 70, yy, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit320x200); + Screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, 70, yy, screentext.StringAt(nStringTypeOn), DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn); + } + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) skiprequest = true; + return true; + } + + override void Start() + { + Exhumed.PlayLocalSound(ExhumedSnd.kSound75, 0, false, CHANF_UI); + phase = 1; + } + + override void OnTick() + { + switch (phase) + { + case 1: + Phase1(); + if (skiprequest || ticks >= nextclock) + { + InitPhase2(); + phase = 2; + skiprequest = false; + } + break; + + case 2: + { + let text = screenText.StringAt(nStringTypeOn); + int chr; + [chr,nCharTypeOn] = text.GetNextCodePoint(nCharTypeOn); + + if (chr == 0) + { + nCharTypeOn = 0; + nStringTypeOn++; + if (nStringTypeOn >= screentext.Count()) + { + nextclock = (GameTicRate * (screentext.Count() + 2)) + ticks; + phase = 3; + } + + } + else + { + nCharTypeOn++; + if (chr != 32) Exhumed.PlayLocalSound(ExhumedSnd.kSound71, 0, false, CHANF_UI); + } + + if (skiprequest) + { + nextclock = (GameTicRate * (screentext.Count() + 2)) + ticks; + phase = 4; + } + break; + } + case 3: + if (ticks >= nextclock || skiprequest) + { + Exhumed.PlayLocalSound(ExhumedSnd.kSound75, 0, false, CHANF_UI); + phase = 4; + nextclock = ticks + 60; + skiprequest = false; + } + + case 4: + if (ticks >= nextclock) + { + skiprequest |= !Phase3(); + } + if (skiprequest) + { + // Go to the next text page. + if (screencnt != 2) + { + screencnt++; + nextclock = ticks + 60; + skiprequest = 0; + phase = 1; + } + else jobstate = finished; + } + + if (skiprequest) + { + jobstate = finished; + } + } + } + + override void Draw(double sm) + { + Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + if (phase == 2 || phase == 3) DisplayPhase2(); + } + +} + +//--------------------------------------------------------------------------- +// +// Credits roll +// +//--------------------------------------------------------------------------- + +class ExCredits : ScreenJob +{ + Array credits; + Array pagelines; + int page; + int pagetime; + bool skiprequest; + + ScreenJob Init() + { + Super.Init(); + String text; + int lump = Wads.CheckNumForFullName("credits.txt"); + if (lump > -1) text = Wads.ReadLump(lump); + text.Substitute("\r", ""); + text.Split(credits, "\n\n"); + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) skiprequest = true; + return true; + } + + override void Start() + { + if (credits.Size() == 0) + { + jobstate = finished; + return; + } + Exhumed.playCDtrack(19, false); + pagetime = 0; + page = -1; + } + + override void OnTick() + { + if (ticks >= pagetime || skiprequest) + { + page++; + if (page < credits.Size()) + credits[page].Split(pagelines, "\n"); + else + { + if (skiprequest || !musplaying.handle) + { + jobstate = finished; + return; + } + pagelines.Clear(); + } + pagetime = ticks + 60; // + } + } + + override void Draw(double smoothratio) + { + int y = 100 - ((10 * (pagelines.Size() - 1)) / 2); + + for (int i = 0; i < pagelines.Size(); i++) + { + int ptime = clamp((pagetime - ticks - smoothratio) * 1000 / GameTicRate, 0, 2000); // in milliseconds + int light; + + if (ptime < 255) light = ptime; + else if (ptime > 2000 - 255) light = 2000 - ptime; + else light = 255; + + let colr = Color(light, light, light); + + int nStringWidth = SmallFont.StringWidth(pagelines[i]); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, colr); + y += 10; + } + } +} + +class ExhumedCutscenes +{ + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + let logo = (gameinfo.gameType & GAMEFLAG_EXHUMED) ? "TileBMGLogo" : "TilePIELogo"; + runner.Append(ImageScreen.CreateNamed(logo, ScreenJob.fadein | ScreenJob.fadeout)); + runner.Append(new("LobotomyScreen").Init("LobotomyLogo", ScreenJob.fadein | ScreenJob.fadeout)); + if (LMFDecoder.Identify("book.mov")) runner.Append(new("LMFPlayer").Init("book.mov")); + else runner.Append(MoviePlayerJob.Create("book.mov", 0)); + runner.Append(new("MainTitle").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMap(ScreenJobRunner runner, MapRecord frommap, SummaryInfo info, MapRecord tomap) + { + // This is only defined for the regular levels. + int frommapnum = frommap == null? 1 : frommap.levelNumber; + if (fromMapnum < 1 || fromMapNum > 20 || tomap == null || tomap.levelNumber < 1 || tomap.levelNumber > 20) return; + + // hijack the super secret info in the summary info to convey the max. map because we won't need that field for its real purpose. + runner.Append(new("MapScreen").Init(fromMapNum, toMap.levelNumber, info.supersecrets)); + } + + //--------------------------------------------------------------------------- + // + // This removes all the insanity the original setup had with these. + // Simplicity rules! + // + //--------------------------------------------------------------------------- + + static void BuildCinemaBefore5(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema5", "$TXT_EX_CINEMA2", 3, 2)); + } + + static void BuildCinemaAfter10(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema10", "$TXT_EX_CINEMA4", 5, 3)); + } + + static void BuildCinemaBefore11(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema11", "$TXT_EX_CINEMA3", 1, 4)); + } + + static void BuildCinemaAfter15(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema15", "$TXT_EX_CINEMA6", 7, 6)); + } + + static void BuildCinemaBefore20(ScreenJobRunner runner) + { + runner.Append(new("LastLevelCinema").Init()); + } + + static void BuildCinemaAfter20(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema20", "$TXT_EX_CINEMA8", 6, 8)); + runner.Append(new("ExCredits").Init()); + } + + static void BuildCinemaLose(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinemaLose", "$TXT_EX_CINEMA7", 4, 7)); + } + + + //--------------------------------------------------------------------------- + // + // player died + // + //--------------------------------------------------------------------------- + + void BuildGameOverScene(ScreenJobRunner runner, MapRecord map) + { + Raze.StopMusic(); + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 0, false, CHANF_UI); + runner.Append(ImageScreen.CreateNamed("Gameover", ScreenJob.fadein | ScreenJob.fadeout, 0x7fffffff, Translation.MakeID(Translation_BasePalette, 16))); + } + +} diff --git a/wadsrc/static/zscript/games/sw/swgame.zs b/wadsrc/static/zscript/games/sw/swgame.zs new file mode 100644 index 000000000..3c2f60179 --- /dev/null +++ b/wadsrc/static/zscript/games/sw/swgame.zs @@ -0,0 +1,713 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1997, 2005 - 3D Realms Entertainment +Copyright (C) 2019-2021 Christoph Oelckers + +This file is part of Raze + +Shadow Warrior is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Original Source: 1997 - Frank Maddin and Jim Norwood +Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms +*/ +//------------------------------------------------------------------------- + +struct SW native +{ + enum ESWSoundFlag + { + v3df_none = 0, // Default, take no action, use all defaults + v3df_follow = 1, // 1 = Do coordinate updates on sound + // Use this only if the sprite won't be deleted soon + v3df_kill = 2, // 1 = Sound is to be deleted + v3df_doppler = 4, // 1 = Don't use doppler pitch variance + v3df_dontpan = 8, // 1 = Don't do panning of sound + v3df_ambient = 16, // 1 = Sound is ambient, use ambient struct info. + v3df_intermit = 32, // 1 = Intermittant sound + v3df_init = 64, // 1 = First pass of sound, don't play it. + // This is mainly used for intermittent sounds + v3df_nolookup = 128, // don't use ambient table lookup + } + + native static void PlaySound(int sound, int flags, int channel = CHAN_AUTO, int cflags = 0); + native static void StopSound(); + native static bool IsSoundPlaying(int channel); // soundEngine.IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE)) + native static void PlaySong(int trackid); + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawString(int x, int y, String text, int shade, int pal, int align = -1) + { + if (align != -1) x -= SmallFont.StringWidth(text) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y, text, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_Color, Raze.shadeToLight(shade), DTA_TranslationIndex, Translation.MakeID(Translation_Remap, pal)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawSmallString(int x, int y, String text, int shade, int pal, int align = -1, double alpha = 1.) + { + if (align != -1) x -= SmallFont2.StringWidth(text) * (align == 0 ? 0.5 : 1); + Screen.DrawText(SmallFont2, Font.CR_UNDEFINED, x, y, text, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_Color, Raze.shadeToLight(shade), DTA_TranslationIndex, Translation.MakeID(Translation_Remap, pal), DTA_Alpha, alpha); + } + + +} + + +struct SWSnd native +{ + enum ESounds + { + DIGI_NULL= 0, + DIGI_SWORDSWOOSH= 1, + DIGI_STAR= 2, + DIGI_STARCLINK= 3, + DIGI_STARWIZ= 4, + DIGI_UZIFIRE= 5, + DIGI_RICHOCHET1= 6, + DIGI_RICHOCHET2= 7, + DIGI_REMOVECLIP= 8, + DIGI_REPLACECLIP= 9, + DIGI_SHELL= 10, + DIGI_RIOTFIRE= 11, + DIGI_RIOTFIRE2= 12, + DIGI_RIOTRELOAD= 13, + DIGI_BOLTEXPLODE= 14, + DIGI_BOLTWIZ= 15, + DIGI_30MMFIRE= 16, + DIGI_30MMRELOAD= 17, + DIGI_30MMEXPLODE= 18, + DIGI_30MMWIZ= 19, + DIGI_HEADFIRE= 20, + DIGI_HEADSHOTWIZ= 21, + DIGI_HEADSHOTHIT= 22, + DIGI_MINETHROW= 23, + DIGI_MINEBOUNCE= 24, + DIGI_MINEBLOW= 25, + DIGI_MINEBEEP= 26, + DIGI_HEARTBEAT= 27, + DIGI_HEARTFIRE= 28, + DIGI_HEARTWIZ= 29, + DIGI_MISSLFIRE= 30, + DIGI_MISSLEXP= 31, + DIGI_RFWIZ= 32, + DIGI_NAPFIRE= 33, + DIGI_NAPWIZ= 34, + DIGI_NAPPUFF= 35, + DIGI_MIRVFIRE= 36, + DIGI_MIRVWIZ= 37, + DIGI_SPIRALFIRE= 38, + DIGI_SPIRALWIZ= 39, + DIGI_MAGIC1= 40, + DIGI_MAGIC2= 41, + DIGI_MAGIC3= 42, + DIGI_MAGIC4= 43, + DIGI_MAGIC5= 44, + DIGI_MAGIC6= 45, + DIGI_MAGIC7= 46, + DIGI_SWCLOAKUNCLOAK= 47, + DIGI_DHVOMIT= 48, + DIGI_DHCLUNK= 49, + DIGI_DHSQUISH= 50, + DIGI_NULL_DHSQUISH= 50, + DIGI_PROJECTILELAVAHIT=51, + DIGI_PROJECTILEWATERHIT=52, + DIGI_KEY= 53, + DIGI_ITEM= 54, + DIGI_BIGITEM= 55, + DIGI_BODYFALL1= 56, + DIGI_HITGROUND= 57, + DIGI_BODYSQUISH1= 58, + DIGI_BODYBURN= 59, + DIGI_BODYBURNSCREAM= 60, + DIGI_BODYCRUSHED1= 61, + DIGI_BODYHACKED1= 62, + DIGI_BODYSINGED= 63, + DIGI_DROWN= 64, + DIGI_SCREAM1= 65, + DIGI_SCREAM2= 66, + DIGI_SCREAM3= 67, + DIGI_HIT1= 68, + DIGI_ELECTRICUTE1= 69, + DIGI_REMOVEME= 70, + DIGI_IMPALED= 71, + DIGI_OOF1= 72, + DIGI_ACTORBODYFALL1= 73, + IGI_ACTORHITGROUND= 74, + DIGI_COOLIEEXPLODE= 75, + DIGI_COOLIESCREAM= 76, + DIGI_COOLIEALERT= 77, + DIGI_COOLIEAMBIENT= 78, + DIGI_COOLIEPAIN= 79, + DIGI_CGMATERIALIZE= 80, + DIGI_CGALERT= 81, + DIGI_CGTHIGHBONE= 82, + DIGI_CGAMBIENT= 83, + DIGI_CGPAIN= 84, + DIGI_CGMAGIC= 85, + DIGI_CGMAGICHIT= 86, + DIGI_CGSCREAM= 87, + DIGI_NINJAAMBIENT= 88, + DIGI_NINJASTAR= 89, + DIGI_NINJAPAIN= 90, + DIGI_NINJASCREAM= 91, + DIGI_NINJAALERT= 92, + DIGI_NINJAUZIATTACK= 93, + DIGI_NINJARIOTATTACK= 94, + DIGI_RIPPERAMBIENT= 95, + DIGI_RIPPERALERT= 96, + DIGI_RIPPERATTACK= 97, + DIGI_RIPPERPAIN= 98, + DIGI_RIPPERSCREAM= 99, + DIGI_RIPPERHEARTOUT= 100, + DIGI_GRDAMBIENT= 101, + DIGI_GRDALERT= 102, + DIGI_GRDPAIN= 103, + DIGI_GRDSCREAM= 104, + DIGI_GRDFIREBALL= 105, + DIGI_GRDSWINGAXE= 106, + DIGI_GRDAXEHIT= 107, + DIGI_SPAMBIENT= 108, + DIGI_SPALERT= 109, + DIGI_SPPAIN= 110, + DIGI_SPSCREAM= 111, + DIGI_SPBLADE= 112, + DIGI_SPELEC= 113, + DIGI_SPTELEPORT= 114, + DIGI_AHAMBIENT= 115, + DIGI_AHSCREAM= 116, + DIGI_AHEXPLODE= 117, + DIGI_AHSWOOSH= 118, + DIGI_HORNETBUZZ= 119, + DIGI_HORNETSTING= 120, + DIGI_HORNETPAIN= 121, + DIGI_HORNETDEATH= 122, + DIGI_SERPAMBIENT= 123, + DIGI_SERPALERT= 124, + DIGI_SERPPAIN= 125, + DIGI_SERPSCREAM= 126, + DIGI_SERPDEATHEXPLODE=127, + DIGI_SERPSWORDATTACK= 128, + DIGI_SERPMAGICLAUNCH= 129, + DIGI_SERPSUMMONHEADS= 130, + DIGI_SERPTAUNTYOU= 131, + DIGI_LAVABOSSAMBIENT= 132, + DIGI_LAVABOSSSWIM= 133, + DIGI_LAVABOSSRISE= 134, + DIGI_LAVABOSSALERT= 135, + DIGI_LAVABOSSFLAME= 136, + DIGI_LAVABOSSMETEOR= 137, + DIGI_LAVABOSSMETEXP= 138, + DIGI_LAVABOSSPAIN= 139, + DIGI_LAVABOSSSIZZLE= 140, + DIGI_LAVABOSSEXPLODE= 141, + DIGI_BOATSTART= 142, + DIGI_BOATRUN= 143, + DIGI_BOATSTOP= 144, + DIGI_BOATFIRE= 145, + DIGI_TANKSTART= 146, + DIGI_TANKRUN= 147, + DIGI_TANKSTOP= 148, + DIGI_TANKIDLE= 149, + DIGI_TANKFIRE= 150, + DIGI_TRUKRUN= 151, + DIGI_TRUKIDLE= 152, + DIGI_SUBRUN= 153, + DIGI_SUBIDLE= 154, + DIGI_SUBDOOR= 155, + DIGI_BOMBRFLYING= 156, + DIGI_BOMBRDROPBOMB= 157, + DIGI_BUBBLES= 158, + DIGI_CHAIN= 159, + DIGI_CHAINDOOR= 160, + DIGI_CRICKETS= 161, + DIGI_WOODDOOROPEN= 162, + DIGI_WOODDOORCLOSE= 163, + DIGI_METALDOOROPEN= 164, + DIGI_METALDOORCLOSE= 165, + DIGI_SLIDEDOOROPEN= 166, + DIGI_SLIDEDOORCLOSE= 167, + DIGI_STONEDOOROPEN= 168, + DIGI_STONEDOORCLOSE= 169, + DIGI_SQUEAKYDOOROPEN= 170, + DIGI_SQUEAKYDOORCLOSE=171, + DIGI_DRILL= 172, + DIGI_CAVEDRIP1= 173, + DIGI_CAVEDRIP2= 174, + DIGI_DRIP= 175, + DIGI_WATERFALL1= 176, + DIGI_WATERFALL2= 177, + DIGI_WATERFLOW1= 178, + DIGI_WATERFLOW2= 179, + DIGI_ELEVATOR= 180, + DIGI_SMALLEXP= 181, + DIGI_MEDIUMEXP= 182, + DIGI_LARGEEXP= 183, + DIGI_HUGEEXP= 184, + DIGI_NULL_HUGEEXP= 184, + DIGI_FIRE1= 185, + DIGI_FIRE2= 186, + DIGI_FIREBALL1= 187, + DIGI_FIREBALL2= 188, + DIGI_GEAR1= 189, + DIGI_GONG= 190, + DIGI_LAVAFLOW1= 191, + DIGI_MACHINE1= 192, + DIGI_MUBBUBBLES1= 193, + DIGI_EARTHQUAKE= 194, + DIGI_SEWERFLOW1= 195, + DIGI_SPLASH1= 196, + DIGI_STEAM1= 197, + DIGI_VOLCANOSTEAM1= 198, + DIGI_STOMPER= 199, + DIGI_SWAMP= 200, + DIGI_REGULARSWITCH= 201, + DIGI_BIGSWITCH= 202, + DIGI_STONESWITCH= 203, + DIGI_GLASSSWITCH= 204, + DIGI_HUGESWITCH= 205, + DIGI_THUNDER= 206, + DIGI_TELEPORT= 207, + DIGI_UNDERWATER= 208, + DIGI_UNLOCK= 209, + DIGI_SQUEAKYVALVE= 210, + DIGI_VOID1= 211, + DIGI_VOID2= 212, + DIGI_VOID3= 213, + DIGI_VOID4= 214, + DIGI_VOID5= 215, + DIGI_ERUPTION= 216, + DIGI_VOLCANOPROJECTILE= 217, + DIGI_LIGHTWIND= 218, + DIGI_STRONGWIND= 219, + DIGI_BREAKINGWOOD= 220, + DIGI_BREAKSTONES= 221, + DIGI_ENGROOM1= 222, + DIGI_ENGROOM2= 223, + DIGI_ENGROOM3= 224, + DIGI_ENGROOM4= 225, + DIGI_ENGROOM5= 226, + DIGI_BREAKGLASS= 227, + DIGI_MUSSTING= 228, + DIGI_HELI= 229, + DIGI_BIGHART= 230, + DIGI_WIND4= 231, + DIGI_SPOOKY1= 232, + DIGI_DRILL1= 233, + DIGI_JET= 234, + DIGI_DRUMCHANT= 235, + DIGI_BUZZZ= 236, + DIGI_CHOP_CLICK= 237, + DIGI_SWORD_UP= 238, + DIGI_UZI_UP= 239, + DIGI_SHOTGUN_UP= 240, + DIGI_ROCKET_UP= 241, + DIGI_GRENADE_UP= 242, + DIGI_RAIL_UP= 243, + DIGI_MINE_UP= 244, + DIGI_TAUNTAI1= 246, + DIGI_TAUNTAI2= 247, + DIGI_TAUNTAI3= 248, + DIGI_TAUNTAI4= 249, + DIGI_TAUNTAI5= 250, + DIGI_TAUNTAI6= 251, + DIGI_TAUNTAI7= 252, + DIGI_TAUNTAI8= 253, + DIGI_TAUNTAI9= 254, + DIGI_TAUNTAI10= 255, + DIGI_PLAYERPAIN1= 256, + DIGI_PLAYERPAIN2= 257, + DIGI_PLAYERPAIN3= 258, + DIGI_PLAYERPAIN4= 259, + DIGI_PLAYERPAIN5= 260, + DIGI_PLAYERYELL1= 261, + DIGI_PLAYERYELL2= 262, + DIGI_PLAYERYELL3= 263, + DIGI_SEARCHWALL= 264, + DIGI_NOURINAL= 265, + DIGI_FALLSCREAM= 266, + DIGI_GOTITEM1= 267, + DIGI_LASTPLAYERVOICE= 268, + DIGI_RAILFIRE= 269, + DIGI_NULL_RAILFIRE= 269, + DIGI_RAILREADY= 270, + DIGI_RAILPWRUP= 271, + DIGI_NUCLEAREXP= 272, + DIGI_NUKESTDBY= 273, + DIGI_NUKECDOWN= 274, + DIGI_NUKEREADY= 275, + DIGI_CHEMGAS= 276, + DIGI_CHEMBOUNCE= 277, + DIGI_THROW= 278, + DIGI_PULL= 279, + DIGI_MINEARM= 280, + DIGI_HEARTDOWN= 281, + DIGI_TOOLBOX= 282, + DIGI_NULL_TOOLBOX= 282, + DIGI_GASPOP= 283, + DIGI_40MMBNCE= 284, + DIGI_BURGLARALARM= 285, + DIGI_CARALARM= 286, + DIGI_CARALARMOFF= 287, + DIGI_CALTROPS= 288, + DIGI_NIGHTON= 289, + DIGI_NIGHTOFF= 290, + DIGI_SHOTSHELLSPENT= 291, + DIGI_BUSSKID= 292, + DIGI_BUSCRASH= 293, + DIGI_BUSENGINE= 294, + DIGI_ARMORHIT= 295, + DIGI_ASIREN1= 296, + DIGI_FIRETRK1= 297, + DIGI_TRAFFIC1= 298, + DIGI_TRAFFIC2= 299, + DIGI_TRAFFIC3= 300, + DIGI_TRAFFIC4= 301, + DIGI_TRAFFIC5= 302, + DIGI_TRAFFIC6= 303, + DIGI_HELI1= 304, + DIGI_JET1= 305, + DIGI_MOTO1= 306, + DIGI_MOTO2= 307, + DIGI_NEON1= 308, + DIGI_SUBWAY= 309, + DIGI_TRAIN1= 310, + DIGI_COINS= 311, + DIGI_SWORDCLANK= 312, + DIGI_RIPPER2AMBIENT= 313, + DIGI_RIPPER2ALERT= 314, + DIGI_RIPPER2ATTACK= 315, + DIGI_RIPPER2PAIN= 316, + DIGI_RIPPER2SCREAM= 317, + DIGI_RIPPER2HEARTOUT=318, + DIGI_M60= 319, + DIGI_SUMOSCREAM= 320, + DIGI_SUMOALERT= 321, + DIGI_SUMOAMBIENT= 322, + DIGI_SUMOPAIN= 323, + DIGI_RAMUNLOCK= 324, + DIGI_CARDUNLOCK= 325, + DIGI_ANCIENTSECRET= 326, + DIGI_AMERICANDRIVER= 327, + DIGI_DRIVELIKEBABOON= 328, + DIGI_BURNBABY= 329, + DIGI_LIKEBIGWEAPONS= 330, + DIGI_COWABUNGA= 331, + DIGI_NOCHARADE= 332, + DIGI_TIMETODIE= 333, + DIGI_EATTHIS= 334, + DIGI_FIRECRACKERUPASS=335, + DIGI_HOLYCOW= 336, + DIGI_HOLYPEICESOFCOW= 337, + DIGI_HOLYSHIT= 338, + DIGI_HOLYPEICESOFSHIT=339, + DIGI_PAYINGATTENTION= 340, + DIGI_EVERYBODYDEAD= 341, + DIGI_KUNGFU= 342, + DIGI_HOWYOULIKEMOVE= 343, + DIGI_NOMESSWITHWANG= 344, + DIGI_RAWREVENGE= 345, + DIGI_YOULOOKSTUPID= 346, + DIGI_TINYDICK= 347, + DIGI_NOTOURNAMENT= 348, + DIGI_WHOWANTSWANG= 349, + DIGI_MOVELIKEYAK= 350, + DIGI_ALLINREFLEXES= 351, + DIGI_EVADEFOREVER= 352, + DIGI_MRFLY= 353, + DIGI_SHISEISI= 354, + DIGI_LIKEFIREWORKS= 355, + DIGI_LIKEHIROSHIMA= 356, + DIGI_LIKENAGASAKI= 357, + DIGI_LIKEPEARL= 358, + DIGI_IAMSHADOW= 359, + DIGI_ILIKENUKES= 360, + DIGI_ILIKESWORD= 361, + DIGI_ILIKESHURIKEN= 362, + DIGI_BADLUCK= 363, + DIGI_NOMOVIEMRCHAN= 364, + DIGI_REALLIFEMRCHAN= 365, + DIGI_NOLIKEMUSIC= 366, + DIGI_NODIFFERENCE= 367, + DIGI_NOFEAR= 368, + DIGI_NOPAIN= 369, + DIGI_NOREPAIRMAN= 370, + DIGI_SONOFABITCH= 371, + DIGI_PAINFORWEAK= 372, + DIGI_GOSPEEDY= 373, + DIGI_GETTINGSTIFF= 374, + DIGI_TOMBRAIDER= 375, + DIGI_STICKYGOTU1= 376, + DIGI_STICKYGOTU2= 377, + DIGI_STICKYGOTU3= 378, + DIGI_STICKYGOTU4= 379, + DIGI_SWORDGOTU1= 380, + DIGI_SWORDGOTU2= 381, + DIGI_SWORDGOTU3= 382, + DIGI_HURTBAD1= 383, + DIGI_HURTBAD2= 384, + DIGI_HURTBAD3= 385, + DIGI_HURTBAD4= 386, + DIGI_HURTBAD5= 387, + DIGI_TOILETGIRLSCREAM= 388, + DIGI_TOILETGIRLALERT= 389, + DIGI_TOILETGIRLAMBIENT=390, + DIGI_TOILETGIRLPAIN= 391, + DIGI_TOILETGIRLTAUNT1= 392, + DIGI_TOILETGIRLTAUNT2= 393, + DIGI_SUMOFART= 394, + DIGI_GIBS1= 395, + DIGI_GIBS2= 396, + DIGI_BIRDS1= 397, + DIGI_BIRDS2= 398, + DIGI_TOILET= 399, + DIGI_FORKLIFTIDLE= 400, + DIGI_FORKLIFTRUN= 401, + DIGI_TOYCAR= 402, + DIGI_UZIMATIC= 403, + DIGI_COMPUTERPOWER= 404, + DIGI_GENERATORON= 405, + DIGI_GENERATORRUN= 406, + DIGI_BIGDRILL= 407, + DIGI_FLUORLIGHT= 408, + DIGI_AMOEBA= 409, + DIGI_BODYFALL2= 410, + DIGI_GIBS3= 411, + DIGI_NINJACHOKE= 412, + DIGI_TRAIN3= 413, + DIGI_TRAINR02= 414, + DIGI_TRAIN8= 415, + DIGI_TRASHLID= 416, + DIGI_GETMEDKIT= 417, + DIGI_AHH= 418, + DIGI_PALARM= 419, + DIGI_PFLIP= 420, + DIGI_PROLL1= 421, + DIGI_PROLL2= 422, + DIGI_PROLL3= 423, + DIGI_BUNNYATTACK= 424, + DIGI_BUNNYDIE1= 425, + DIGI_BUNNYDIE2= 426, + DIGI_BUNNYDIE3= 427, + DIGI_BUNNYAMBIENT= 428, + DIGI_STONESLIDE= 429, + DIGI_NINJAINHALF= 430, + DIGI_RIPPER2CHEST= 431, + DIGI_WHIPME= 432, + DIGI_ENDLEV= 433, + DIGI_MDALARM= 434, + DIGI_BREAKMETAL= 435, + DIGI_BREAKDEBRIS= 436, + DIGI_BREAKMARBELS= 437, + DIGI_BANZAI= 438, + DIGI_HAHA1= 439, + DIGI_HAHA2= 440, + DIGI_HAHA3= 441, + DIGI_ITEM_SPAWN= 442, + DIGI_NOREPAIRMAN2= 443, + DIGI_NOPOWER= 444, + DIGI_DOUBLEUZI= 445, + DIGI_NOTORDBUNNY= 446, + DIGI_CANBEONLYONE= 447, + DIGI_MIRROR1= 448, + DIGI_MIRROR2= 449, + DIGI_HITTINGWALLS= 450, + DIGI_GOTRAILGUN= 451, + DIGI_RABBITHUMP1= 452, + DIGI_RABBITHUMP2= 453, + DIGI_RABBITHUMP3= 454, + DIGI_RABBITHUMP4= 455, + DIGI_FAGRABBIT1= 456, + DIGI_FAGRABBIT2= 457, + DIGI_FAGRABBIT3= 458, + DIGI_STINKLIKEBABBOON= 459, + DIGI_WHATYOUEATBABY= 460, + DIGI_WHATDIEDUPTHERE= 461, + DIGI_YOUGOPOOPOO= 462, + DIGI_PULLMYFINGER= 463, + DIGI_SOAPYOUGOOD= 464, + DIGI_WASHWANG= 465, + DIGI_DROPSOAP= 466, + DIGI_REALTITS= 467, + DIGI_MSTRLEEP= 468, + DIGI_SEEKLEEPADVICE= 469, + DIGI_AVENGELEEPDEATH= 470, + DIGI_LEEPGHOST= 471, + DIGI_DOOR1= 472, + DIGI_DOOR2= 473, + DIGI_DOOR3= 474, + DIGI_FLAGWAVE= 475, + DIGI_SURFACE= 476, + DIGI_GASHURT= 477, + DIGI_BONUS_GRAB= 478, + DIGI_ANIMECRY= 479, + DIGI_ANIMESING1= 480, + DIGI_ANIMEMAD1= 481, + DIGI_ANIMESING2= 482, + DIGI_ANIMEMAD2= 483, + DIGI_PLAYER_TELEPORT= 484, + DIGI_INTRO_SLASH= 485, + DIGI_WARNING= 486, + DIGI_INTRO_WHIRL= 487, + DIGI_TOILETGIRLFART1= 488, + DIGI_TOILETGIRLFART2= 489, + DIGI_TOILETGIRLFART3= 490, + DIGI_WINDCHIMES= 491, + DIGI_MADATCARPET= 492, + DIGI_JUMPONCARPET= 493, + DIGI_USEBROKENVEHICLE= 494, + DIGI_STEPONCALTROPS= 495, + DIGI_WANGSEESERP= 496, + DIGI_SERPTAUNTWANG= 497, + DIGI_WANGTAUNTSERP1= 498, + DIGI_WANGTAUNTSERP2= 499, + DIGI_WANGORDER1= 500, + DIGI_WANGORDER2= 501, + DIGI_WANGDROWNING= 502, + DIGI_ZILLAREGARDS= 503, + DIGI_PMESSAGE= 504, + DIGI_SHAREND_UGLY1= 505, + DIGI_SHAREND_UGLY2= 506, + DIGI_SHAREND_TELEPORT= 507, + DIGI_HOTHEADSWITCH= 508, + DIGI_BOATCREAK= 509, + DIGI_BOATRUN2= 510, + DIGI_BOATIDLE= 511, + DIGI_SHIPBELL= 512, + DIGI_FOGHORN= 513, + DIGI_CANNON= 514, + DIGI_JG41001= 515, + DIGI_JG41012= 516, + DIGI_JG41018= 517, + DIGI_JG41028= 518, + DIGI_JG41048= 519, + DIGI_JG41052= 520, + DIGI_JG41058= 521, + DIGI_JG41060= 522, + DIGI_JG41075= 523, + DIGI_JG42004= 524, + DIGI_JG42019= 525, + DIGI_JG42021= 526, + DIGI_JG42028= 527, + DIGI_JG42033= 528, + DIGI_JG42034= 529, + DIGI_JG42050= 530, + DIGI_JG42056= 531, + DIGI_JG42061= 532, + DIGI_JG43004= 533, + DIGI_JG43015= 534, + DIGI_JG43019= 535, + DIGI_JG43021= 536, + DIGI_JG44011= 537, + DIGI_JG44014= 538, + DIGI_JG44027= 539, + DIGI_JG44038= 540, + DIGI_JG44039= 541, + DIGI_JG44048= 542, + DIGI_JG44052= 543, + DIGI_JG45014= 544, + DIGI_JG44068= 545, + DIGI_JG45010= 546, + DIGI_JG45018= 547, + DIGI_JG45030= 548, + DIGI_JG45033= 549, + DIGI_JG45043= 550, + DIGI_JG45053= 551, + DIGI_JG45067= 552, + DIGI_JG46005= 553, + DIGI_JG46010= 554, + DIGI_LANI049= 555, + DIGI_LANI051= 556, + DIGI_LANI052= 557, + DIGI_LANI054= 558, + DIGI_LANI060= 559, + DIGI_LANI063= 560, + DIGI_LANI065= 561, + DIGI_LANI066= 562, + DIGI_LANI073= 563, + DIGI_LANI075= 564, + DIGI_LANI077= 565, + DIGI_LANI079= 566, + DIGI_LANI089= 567, + DIGI_LANI091= 568, + DIGI_LANI093= 569, + DIGI_LANI095= 570, + DIGI_VENTWALK= 571, + DIGI_CARWALK= 572, + DIGI_JETSOAR= 573, + DIGI_VACUUM= 574, + DIGI_GIRLNINJAALERTT= 575, + DIGI_GIRLNINJASCREAM= 576, + DIGI_GIRLNINJAALERT= 577, + DIGI_PRUNECACKLE= 578, + DIGI_PRUNECACKLE2= 579, + DIGI_PRUNECACKLE3= 580, + DIGI_SUMOSTOMP= 581, + DIGI_VATOR= 582, + DIGI_JG9009= 583, + DIGI_Z16004= 584, + DIGI_Z16012= 585, + DIGI_Z16022= 586, + DIGI_Z16027= 587, + DIGI_JG93030= 588, + DIGI_JG94002= 589, + DIGI_Z17010= 590, + DIGI_Z17052= 591, + DIGI_Z17025= 592, + DIGI_ML25014= 593, + DIGI_ML250101= 594, + DIGI_JG9022= 595, + DIGI_JG9032= 596, + DIGI_JG9038= 597, + DIGI_JG9055= 598, + DIGI_JG9060= 599, + DIGI_JG92055= 600, + DIGI_ML25032= 601, + DIGI_JG92036= 602, + DIGI_JG92042= 603, + DIGI_ML26001= 604, + DIGI_JG93000= 605, + DIGI_JG93011= 606, + DIGI_JG93018= 607, + DIGI_JG93023= 608, + DIGI_ML26008= 609, + DIGI_ML26011= 610, + DIGI_JG94007= 611, + DIGI_JG94024= 612, + DIGI_JG94039= 613, + DIGI_JG95012= 614, + DIGI_ZILLASTOMP= 615, + DIGI_ZC1= 616, + DIGI_ZC2= 617, + DIGI_ZC3= 618, + DIGI_ZC4= 619, + DIGI_ZC5= 620, + DIGI_ZC6= 621, + DIGI_ZC7= 622, + DIGI_ZC8= 623, + DIGI_ZC9= 624, + DIGI_Z16043= 625, + } +} diff --git a/wadsrc/static/zscript/games/sw/ui/screens.zs b/wadsrc/static/zscript/games/sw/ui/screens.zs new file mode 100644 index 000000000..6b2a15fde --- /dev/null +++ b/wadsrc/static/zscript/games/sw/ui/screens.zs @@ -0,0 +1,591 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 1997, 2005 - 3D Realms Entertainment +Copyright (C) 2019-2021 Christoph Oelckers + +This file is part of Raze + +Shadow Warrior is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Original Source: 1997 - Frank Maddin and Jim Norwood +Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms +*/ +//------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SWDRealmsScreen : SkippableScreenJob +{ + const DREALMSPAL = 1; + + ScreenJob Init() + { + Super.Init(fadein | fadeout); + return self; + } + + override void Start() + { + SW.PlaySong(0); + } + + override void OnTick() + { + if (ticks > 5 * GameTicRate) jobstate = finished; + } + + override void Draw(double sm) + { + let tex = TexMan.CheckForTexture("THREED_REALMS_PIC", TexMan.Type_Any); + int translation = Translation.MakeID(Translation_BasePalette, DREALMSPAL); + Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SWCreditsScreen : SkippableScreenJob +{ + int mystate; + int starttime; + TextureID curpic; + TextureID pic1, pic2; + + ScreenJob Init() + { + Super.Init(fadein|fadeout); + pic1 = TexMan.CheckForTexture("CREDITS1", TexMan.Type_Any); + pic2 = TexMan.CheckForTexture("CREDITS2", TexMan.Type_Any); + return self; + + } + override void OnSkip() + { + SW.StopSound(); + } + + override void Start() + { + // Lo Wang feel like singing! + SW.PlaySound(SWSnd.DIGI_JG95012, SW.v3df_none, CHAN_VOICE, CHANF_UI); + } + + override void OnTick() + { + if (mystate == 0) + { + if (!SW.IsSoundPlaying(CHAN_VOICE)) + { + starttime = ticks; + mystate = 1; + SW.StopSound(); + curpic = pic1; + SW.PlaySong(5); //) PlaySong(2); + } + } + else + { + if (ticks >= starttime + 8 * GameTicRate) + { + curpic = curpic == pic1? pic2 : pic1; + starttime = ticks; + } + } + } + + override void Draw(double sr) + { + if (mystate == 1) + Screen.DrawTexture(curpic, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + } +} + +//--------------------------------------------------------------------------- +// +// Summary screen animation +// +//--------------------------------------------------------------------------- + +struct SWSummaryAnimation +{ + TextureID frames[15]; + Sound snd; + int delay, length; + int tics; + + int curframe; + + void Init(String nametemplate, int length_, Sound sound_, int delay_) + { + for (int i = 0; i < length_; i++) + { + String name = String.Format(nametemplate, i); + frames[i] = TexMan.CheckForTexture(name, TexMan.Type_Any); + } + snd = sound_; + delay = delay_; + length = length_; + } + + bool Tick() + { + tics += 3; + if (curframe < length-1) + { + if (tics >= delay) + { + tics -= delay; + curframe++; + if (curframe == 3) SW.PlaySound(snd, SW.v3df_none); + } + return false; + } + return tics >= 90; + } + + TextureID getTex() + { + return frames[curframe]; + } +} + +//--------------------------------------------------------------------------- +// +// Summary screen +// +//--------------------------------------------------------------------------- + +class SWSummaryScreen : SummaryScreenBase +{ + SWSummaryAnimation anim; + int animstate; + TextureID rest[4]; + + ScreenJob Init(MapRecord mr, SummaryInfo info) + { + Super.Init(fadein|fadeout); + SetParameters(mr, info); + switch (random(0, 2)) + { + case 0: + anim.Init("BONUS_PUNCH%02d", 15, SWSnd.DIGI_PLAYERYELL3, 8); + break; + case 1: + anim.Init("BONUS_KICK%02d", 15, SWSnd.DIGI_PLAYERYELL2, 8); + break; + case 2: + anim.Init("BONUS_GRAB%02d", 15, SWSnd.DIGI_BONUS_GRAB, 20); + break; + } + rest[0] = TexMan.CheckForTexture("BONUS_PUNCH00", TexMan.Type_Any); + rest[3] = rest[1] = TexMan.CheckForTexture("BONUS_PUNCH01", TexMan.Type_Any); + rest[2] = TexMan.CheckForTexture("BONUS_PUNCH02", TexMan.Type_Any); + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) + { + if (animstate == 0) animstate = 1; + } + return true; + } + + override void Start() + { + Raze.StopAllSounds(); + SW.PlaySong(1); + } + + override void OnTick() + { + if (animstate == 1) + { + if (anim.Tick()) + { + SW.StopSound(); + jobstate = finished; + } + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + private static int BONUS_LINE(int i) { return (50 + ((i) * 20)); } + + override void Draw(double sm) + { + Screen.DrawTexture(TexMan.CheckForTexture("BONUS_SCREEN_PIC", TexMan.Type_Any), true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + SW.DrawString(160, 20, currentLevel.DisplayName(), 1, 19, 0); + SW.DrawString(170, 30, "$COMPLETED", 1, 19, 0); + + Textureid animtex; + if (animstate == 0) animtex = rest[(ticks / 17) & 3]; + else animtex = anim.getTex(); + + Screen.DrawTexture(animtex, false, 158, 86, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_LegacyRenderStyle, STYLE_Normal); + + int line = 0; + String ds; + + ds = String.Format("%s %s", StringTable.Localize("$TXT_YOURTIME"), FormatTime(stats.time)); + SW.DrawString(60, BONUS_LINE(line++), ds, 1, 16); + + if (currentLevel.designerTime > 0) + { + ds = String.Format("%s %d:%02d", StringTable.Localize("$TXT_3DRTIME"), currentLevel.designerTime / 60, currentLevel.designerTime % 60); + SW.DrawString(40, BONUS_LINE(line++), ds, 1, 16); + } + + if (currentLevel.parTime > 0) + { + ds = String.Format("%s %d:%02d", StringTable.Localize("$TXT_PARTIME"), currentLevel.parTime / 60, currentLevel.parTime % 60); + SW.DrawString(40, BONUS_LINE(line++), ds, 1, 16); + } + + // always read secrets and kills from the first player + ds = String.Format("%s: %d / %d", StringTable.Localize("$TXT_SECRETS"), stats.Secrets, stats.MaxSecrets); + SW.DrawString(60, BONUS_LINE(line++), ds, 1, 16); + + ds = String.Format("%s: %d / %d", StringTable.Localize("$KILLS"), stats.Kills, stats.MaxKills); + SW.DrawString(60, BONUS_LINE(line), ds, 1, 16); + + SW.DrawString(160, 185, "$PRESSKEY", 1, 19, 0); + } + +} + +//--------------------------------------------------------------------------- +// +// Deathmatch summary screen +// +//--------------------------------------------------------------------------- + +class SWMultiSummaryScreen : SkippableScreenJob +{ + enum EConst + { + + STAT_START_X = 20, + STAT_START_Y = 85, + STAT_OFF_Y = 9, + STAT_HEADER_Y = 14, + STAT_TABLE_X = (STAT_START_X + 15*4), + STAT_TABLE_XOFF = 6*4, + MAXPLAYERS = 8, + PALETTE_PLAYER0 = 16 + + } + + int numplayers; + + ScreenJob Init(int numplayer_) + { + numplayers = numplayer_; + Super.Init(fadein | fadeout); + return self; + } + + override void Start() + { + SW.PlaySong(1); + } + + override void OnSkip() + { + SW.StopSound(); + } + + override void Draw(double sr) + { + int death_total[MAXPLAYERS]; + int kills[MAXPLAYERS]; + + Screen.DrawTexture(TexMan.CheckForTexture("STAT_SCREEN_PIC", TexMan.Type_Any), true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + SW.DrawString(160, 68, "$MULTIPLAYER TOTALS", 0, 0); + SW.DrawString(160, 189, "$PRESSKEY", 0, 0, 0); + + int x = STAT_START_X; + int y = STAT_START_Y; + + // Hm.... how to translate this without messing up the formatting? + SW.DrawSmallString(x, y, " NAME 1 2 3 4 5 6 7 8 KILLS", 0, 0); + int rows = numplayers; + int cols = numplayers; + + y += STAT_HEADER_Y; + + String ds; + for (int i = 0; i < rows; i++) + { + x = STAT_START_X; + + ds = String.Format("%d", i + 1); + SW.DrawSmallString(x, y, ds, 0, 0); + + ds = String.Format(" %-13s", Raze.PlayerName(i)); + SW.DrawSmallString(x, y, ds, 0, Raze.playerPalette(i)); + + x = STAT_TABLE_X; + for (int j = 0; j < cols; j++) + { + int pal = 0; + int frags = Raze.PlayerFrags(i, j); + death_total[j] += frags; + + if (i == j) + { + // don't add kill for self or team player + pal = PALETTE_PLAYER0 + 4; + kills[i] -= frags; // subtract self kills + } + else if (false/*gNet.TeamPlay*/) + { + if (Raze.playerPalette(i) == Raze.playerPalette(j)) + { + // don't add kill for self or team player + pal = PALETTE_PLAYER0 + 4; + kills[i] -= frags; // subtract self kills + } + else + kills[i] += frags; // kills added here + } + else + { + kills[i] += frags; // kills added here + } + + ds = String.Format("%d", frags); + SW.DrawSmallString(x, y, ds, 0, pal); + x += STAT_TABLE_XOFF; + } + + y += STAT_OFF_Y; + } + + + // Deaths + + x = STAT_START_X; + y += STAT_OFF_Y; + + ds = String.Format(" %s", StringTable.Localize("$DEATHS")); + SW.DrawSmallString(x, y, ds, 0, 0); + x = STAT_TABLE_X; + + for (int j = 0; j < cols; j++) + { + ds = String.Format("%d", death_total[j]); + SW.DrawSmallString(x, y, ds, 0, 0); + x += STAT_TABLE_XOFF; + } + + x = STAT_START_X; + y += STAT_OFF_Y; + + // Kills + x = STAT_TABLE_X + 200; + y = STAT_START_Y + STAT_HEADER_Y; + + for (int i = 0; i < rows; i++) + { + ds = String.Format("%d", kills[i]); //pp.Kills); + SW.DrawSmallString(x, y, ds, 0, 0); + + y += STAT_OFF_Y; + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SWLoadScreen : ScreenJob +{ + MapRecord rec; + + ScreenJob Init(MapRecord maprec) + { + Super.Init(fadein); + rec = maprec; + return self; + } + + override void OnTick() + { + if (fadestate == visible) jobstate = finished; + } + + override void Draw(double sr) + { + Screen.DrawTexture(TexMan.CheckForTexture("TITLE_PIC", TexMan.Type_Any), true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); + SW.DrawString(160, 170, "$TXT_ENTERING", 1, 16, 0); + SW.DrawString(160, 180, rec.DisplayName(), 1, 16, 0); + } +} + + +class SWCutscenes +{ + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + SW.StopSound(); + SW.PlaySong(0); + Array soundinfo; + soundinfo.Pushv( + 1, SWSnd.DIGI_NOMESSWITHWANG, + 5, SWSnd.DIGI_INTRO_SLASH, + 15, SWSnd.DIGI_INTRO_WHIRL); + runner.Append(new("SWDRealmsScreen").Init()); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("sw.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 8, 360, 128)); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSerpentAnim(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 1, SWSnd.DIGI_SERPTAUNTWANG, + 16, SWSnd.DIGI_SHAREND_TELEPORT, + 35, SWSnd.DIGI_WANGTAUNTSERP1, + 51, SWSnd.DIGI_SHAREND_UGLY1, + 64, SWSnd.DIGI_SHAREND_UGLY2); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("swend.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 16, 16, 140)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSumoAnim(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 2, SWSnd.DIGI_JG41012, + 30, SWSnd.DIGI_HOTHEADSWITCH, + 42, SWSnd.DIGI_HOTHEADSWITCH, + 59, SWSnd.DIGI_JG41028); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("sumocinm.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 10, 40, 130)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildZillaAnim(ScreenJobRunner runner) + { + Array soundinfo; + soundinfo.Pushv( + 1, SWSnd.DIGI_ZC1, + 5, SWSnd.DIGI_JG94024, + 14, SWSnd.DIGI_ZC2, + 30, SWSnd.DIGI_ZC3, + 32, SWSnd.DIGI_ZC4, + 37, SWSnd.DIGI_ZC5, + 63, SWSnd.DIGI_Z16043, + 63, SWSnd.DIGI_ZC6, + 63, SWSnd.DIGI_ZC7, + 72, SWSnd.DIGI_ZC7, + 73, SWSnd.DIGI_ZC4, + 77, SWSnd.DIGI_ZC5, + 87, SWSnd.DIGI_ZC8, + 103, SWSnd.DIGI_ZC7, + 108, SWSnd.DIGI_ZC9, + 120, SWSnd.DIGI_JG94039); + runner.Append(MoviePlayerJob.CreateWithSoundinfo("zfcin.anm", soundinfo, MoviePlayer.NOSOUNDCUTOFF, 16, 16, 140)); + runner.Append(new("SWCreditsScreen").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSybexScreen(ScreenJobRunner runner) + { + if (Raze.isShareware() && !netgame) + runner.Append(ImageScreen.CreateNamed("#05261", TexMan.Type_Any)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("SWMultiSummaryScreen").Init(stats.playercount)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("SWSummaryScreen").Init(map, stats)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildLoading(ScreenJobRunner runner, MapRecord map) + { + runner.Append(new("SWLoadScreen").Init(map)); + } +} diff --git a/wadsrc/static/zscript/razebase.zs b/wadsrc/static/zscript/razebase.zs index 3dc64c532..67098cc3c 100644 --- a/wadsrc/static/zscript/razebase.zs +++ b/wadsrc/static/zscript/razebase.zs @@ -17,8 +17,14 @@ enum EGameType GAMEFLAG_POWERSLAVE = 0x00002000, GAMEFLAG_EXHUMED = 0x00004000, GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher. - GAMEFLAG_WORLDTOUR = 0x00008000, - GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_WORLDTOUR = 0x00008000, + GAMEFLAG_DUKEDC = 0x00010000, + GAMEFLAG_DUKENW = 0x00020000, + GAMEFLAG_DUKEVACA = 0x00040000, + GAMEFLAG_BLOODCP = 0x00080000, + GAMEFLAG_ROUTE66 = 0x00100000, + GAMEFLAG_SWWANTON = 0x00200000, + GAMEFLAG_SWTWINDRAG = 0x00400000, GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo // We still need these for the parsers. @@ -27,8 +33,80 @@ enum EGameType }; +struct UserConfigStruct native +{ + native readonly bool nomonsters; + native readonly bool nosound; + native readonly bool nologo; +} -struct Build +extend struct _ +{ + native @UserConfigStruct userConfig; + native readonly MapRecord currentLevel; + native readonly int paused; +} + +struct MapRecord native +{ + enum MIFlags + { + FORCEEOG = 1, + USERMAP = 2, + } + + native readonly int parTime; + native readonly int designerTime; + native readonly String fileName; + native readonly String labelName; + native readonly String name; + native readonly String music; + native readonly int cdSongId; + native readonly int flags; + native readonly int levelNumber; + native readonly int cluster; + native readonly String InterBackground; + + native readonly String nextMap; + native readonly String nextSecret; + + //native readonly String messages[MAX_MESSAGES]; + native readonly String author; + + String GetLabelName() + { + if (flags & USERMAP) return "$TXT_USERMAP"; + return labelName; + } + String DisplayName() + { + if (name == "") return labelName; + return name; + } + + native ClusterDef GetCluster(); +} + +struct ClusterDef +{ + native readonly String name; + native readonly String InterBackground; +} + +struct SummaryInfo native +{ + native readonly int kills; + native readonly int maxkills; + native readonly int secrets; + native readonly int maxsecrets; + native readonly int supersecrets; + native readonly int time; + native readonly int playercount; + native readonly bool cheated; + native readonly bool endofgame; +} + +struct Raze { static int calcSinTableValue(int ang) { @@ -36,6 +114,87 @@ struct Build } native static Color shadeToLight(int shade); + native static void StopAllSounds(); + native static bool SoundEnabled(); + native static void StopMusic(); + native static bool MusicEnabled(); + native static String PlayerName(int i); + native static double GetTimeFrac(); + native static int bsin(int angle, int shift = 0); + native static int bcos(int angle, int shift = 0); + + static bool specialKeyEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown || ev.type == InputEvent.Type_KeyUp) + { + int key = ev.KeyScan; + if (key == InputEvent.KEY_VOLUMEDOWN || key == InputEvent.KEY_VOLUMEUP || (key > InputEvent.KEY_LASTJOYBUTTON && key < InputEvent.KEY_PAD_LTHUMB_RIGHT)) return true; + } + return false; + } + + // game check shortcuts + static bool isNam() + { + return gameinfo.gametype & (GAMEFLAG_NAM | GAMEFLAG_NAPALM); + } + + static bool isNamWW2GI() + { + return gameinfo.gametype & (GAMEFLAG_NAM | GAMEFLAG_NAPALM |GAMEFLAG_WW2GI); + } + + static bool isWW2GI() + { + return gameinfo.gametype & (GAMEFLAG_WW2GI); + } + + static bool isRR() + { + return gameinfo.gametype & (GAMEFLAG_RRALL); + } + + static bool isRRRA() + { + return gameinfo.gametype & (GAMEFLAG_RRRA); + } + + static bool isWorldTour() + { + return gameinfo.gametype & GAMEFLAG_WORLDTOUR; + } + + static bool isPlutoPak() + { + return gameinfo.gametype & GAMEFLAG_PLUTOPAK; + } + + static bool isShareware() + { + return gameinfo.gametype & GAMEFLAG_SHAREWARE; + } + + static bool isBlood() + { + return gameinfo.gametype & GAMEFLAG_BLOOD; + } + + // Dont know yet how to best export this, so for now these are just placeholders as MP is not operational anyway. + static int playerPalette(int i) + { + return 0; + } + + static int playerFrags(int i, int j) + { + return 0; + } + + static int playerFraggedSelf(int i) + { + return 0; + } + } /* diff --git a/wadsrc/static/zscript/screenjob.zs b/wadsrc/static/zscript/screenjob.zs new file mode 100644 index 000000000..dd7763cd9 --- /dev/null +++ b/wadsrc/static/zscript/screenjob.zs @@ -0,0 +1,565 @@ + +class ScreenJob : Object +{ + int flags; + float fadetime; // in milliseconds + int fadestate; + + int ticks; + int jobstate; + + bool skipover; + + enum EJobState + { + running = 0, // normal operation + skipped = 1, // finished by user skipping + finished = 2, // finished by completing its sequence + stopping = 3, // running ending animations / fadeout, etc. Will not accept more input. + stopped = 4, // we're done here. + }; + enum EJobFlags + { + visible = 0, + fadein = 1, + fadeout = 2, + stopmusic = 4, + stopsound = 8, + }; + + void Init(int fflags = 0, float fadet = 250.f) + { + flags = fflags; + fadetime = fadet; + jobstate = running; + } + + virtual bool ProcessInput() + { + return false; + } + + virtual void Start() {} + virtual bool OnEvent(InputEvent evt) { return false; } + virtual void OnTick() {} + virtual void Draw(double smoothratio) {} + virtual void OnSkip() {} + + int DrawFrame(double smoothratio) + { + if (jobstate != running) smoothratio = 1; // this is necessary to avoid having a negative time span because the ticker won't be incremented anymore. + Draw(smoothratio); + if (jobstate == skipped) return -1; + if (jobstate == finished) return 0; + return 1; + } + + int GetFadeState() { return fadestate; } + override void OnDestroy() + { + if (flags & stopmusic) Raze.StopMusic(); + if (flags & stopsound) Raze.StopAllSounds(); + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class SkippableScreenJob : ScreenJob +{ + void Init(int flags = 0, float fadet = 250.f) + { + Super.Init(flags, fadet); + } + + override bool OnEvent(InputEvent evt) + { + if (evt.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(evt)) + { + jobstate = skipped; + OnSkip(); + } + return true; + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BlackScreen : ScreenJob +{ + int wait; + bool cleared; + + ScreenJob Init(int w, int flags = 0) + { + Super.Init(flags & ~(fadein|fadeout)); + wait = w; + cleared = false; + return self; + } + + static ScreenJob Create(int w, int flags = 0) + { + return new("BlackScreen").Init(w, flags); + } + + override void OnTick() + { + if (cleared) + { + int span = ticks * 1000 / GameTicRate; + if (span > wait) jobstate = finished; + } + } + + override void Draw(double smooth) + { + cleared = true; + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class ImageScreen : SkippableScreenJob +{ + int tilenum; + int trans; + int waittime; // in ms. + bool cleared; + TextureID texid; + + ScreenJob Init(TextureID tile, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + Super.Init(fade); + waittime = wait; + texid = tile; + trans = translation; + cleared = false; + return self; + } + + ScreenJob InitNamed(String tex, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + Super.Init(fade); + waittime = wait; + texid = TexMan.CheckForTexture(tex, TexMan.Type_Any, TexMan.TryAny | TexMan.ForceLookup); + trans = translation; + cleared = false; + return self; + } + + static ScreenJob Create(TextureID tile, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + return new("ImageScreen").Init(tile, fade, wait, translation); + } + + static ScreenJob CreateNamed(String tex, int fade = fadein | fadeout, int wait = 3000, int translation = 0) + { + return new("ImageScreen").InitNamed(tex, fade, wait, translation); + } + + override void OnTick() + { + if (cleared) + { + int span = ticks * 1000 / GameTicRate; + if (span > waittime) jobstate = finished; + } + } + + override void Draw(double smooth) + { + if (texid.IsValid()) Screen.DrawTexture(texid, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans); + cleared = true; + } +} + +//--------------------------------------------------------------------------- +// +// this is to have a unified interface to the summary screens +// that can be set up automatically by the games to avoid direct access to game data. +// +//--------------------------------------------------------------------------- + +class SummaryScreenBase : ScreenJob +{ + MapRecord level; + SummaryInfo stats; + + void SetParameters(MapRecord map, SummaryInfo thestats) + { + level = map; + stats = thestats; + } + + String FormatTime(int time) + { + if (time >= 60 * 50) + return String.Format("%02d:%02d:%02d", time / (60*60), (time / 60) % 60, time % 60); + else + return String.Format("%02d:%02d", (time / 60) % 60, time % 60); + } + +} + +//--------------------------------------------------------------------------- +// +// internal polymorphic movie player object +// +//--------------------------------------------------------------------------- + +struct MoviePlayer native +{ + enum EMovieFlags + { + NOSOUNDCUTOFF = 1, + FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros. + } + + native static MoviePlayer Create(String filename, Array soundinfo, int flags, int frametime, int firstframetime, int lastframetime); + native void Start(); + native bool Frame(double clock); + native void Destroy(); + native TextureID GetTexture(); +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class MoviePlayerJob : SkippableScreenJob +{ + MoviePlayer player; + bool started; + int flag; + + ScreenJob Init(MoviePlayer mp, int flags) + { + Super.Init(); + flag = flags; + player = mp; + return self; + } + + static ScreenJob CreateWithSoundInfo(String filename, Array soundinfo, int flags, int frametime, int firstframetime = -1, int lastframetime = -1) + { + let movie = MoviePlayer.Create(filename, soundinfo, flags, frametime, firstframetime, lastframetime); + if (movie) return new("MoviePlayerJob").Init(movie, flags); + return null; + } + static ScreenJob Create(String filename, int flags, int frametime = -1) + { + Array empty; + return CreateWithSoundInfo(filename, empty, flags, frametime); + } + static ScreenJob CreateWithSound(String filename, Sound soundname, int flags, int frametime = -1) + { + Array empty; + empty.Push(1); + empty.Push(int(soundname)); + return CreateWithSoundInfo(filename, empty, flags, frametime); + } + + virtual void DrawFrame() + { + let tex = player.GetTexture(); + let size = TexMan.GetScaledSize(tex); + + if (!(flag & MoviePlayer.FIXEDVIEWPORT) || (size.x <= 320 && size.y <= 200) || size.x >= 640 || size.y >= 480) + { + Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false); + } + else + { + Screen.DrawTexture(tex, false, 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, DTA_Masked, false); + } + + } + + override void Draw(double smoothratio) + { + if (!player) + { + jobstate = stopped; + return; + } + if (!started) + { + started = true; + player.Start(); + } + double clock = (ticks + smoothratio) * 1000000000. / GameTicRate; + if (jobstate == running && !player.Frame(clock)) + { + jobstate = finished; + } + DrawFrame(); + } + + override void OnDestroy() + { + if (player) + { + player.Destroy(); + } + player = null; + } +} + + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class ScreenJobRunner : Object +{ + enum ERunState + { + State_Clear, + State_Run, + State_Fadeout, + } + Array jobs; + //CompletionFunc completion; + int index; + float screenfade; + bool clearbefore; + bool skipall; + bool advance; + int actionState; + int terminateState; + int fadeticks; + int last_paused_tic; + + void Init(bool clearbefore_, bool skipall_) + { + clearbefore = clearbefore_; + skipall = skipall_; + index = -1; + fadeticks = 0; + last_paused_tic = -1; + } + + override void OnDestroy() + { + DeleteJobs(); + } + + protected void DeleteJobs() + { + // Free all allocated resources now. + for (int i = 0; i < jobs.Size(); i++) + { + if (jobs[i]) jobs[i].Destroy(); + } + jobs.Clear(); + } + + void Append(ScreenJob job) + { + if (job != null) jobs.Push(job); + } + + virtual bool Validate() + { + return jobs.Size() > 0; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + protected void AdvanceJob(bool skip) + { + if (index == jobs.Size()-1) + { + index++; + return; // we need to retain the last element until the runner is done. + } + + if (index >= 0) jobs[index].Destroy(); + index++; + while (index < jobs.Size() && (jobs[index] == null || (skip && jobs[index].skipover))) + { + if (jobs[index] != null && index < jobs.Size() - 1) jobs[index].Destroy(); // may not delete the last element - we still need it for shutting down. + index++; + } + actionState = clearbefore ? State_Clear : State_Run; + if (index < jobs.Size()) + { + jobs[index].fadestate = !paused && jobs[index].flags & ScreenJob.fadein? ScreenJob.fadein : ScreenJob.visible; + jobs[index].Start(); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual int DisplayFrame(double smoothratio) + { + if (jobs.Size() == 0) + { + return 1; + } + int x = index >= jobs.Size()? jobs.Size()-1 : index; + let job = jobs[x]; + bool processed = job.ProcessInput(); + + if (job.fadestate == ScreenJob.fadein) + { + double ms = (job.ticks + smoothratio) * 1000 / GameTicRate / job.fadetime; + double screenfade = clamp(ms, 0., 1.); + Screen.SetScreenFade(screenfade); + if (screenfade == 1.) job.fadestate = ScreenJob.visible; + } + int state = job.DrawFrame(smoothratio); + Screen.SetScreenFade(1.); + return state; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual int FadeoutFrame(double smoothratio) + { + int x = index >= jobs.Size()? jobs.Size()-1 : index; + let job = jobs[x]; + double ms = (fadeticks + smoothratio) * 1000 / GameTicRate / job.fadetime; + float screenfade = 1. - clamp(ms, 0., 1.); + Screen.SetScreenFade(screenfade); + job.DrawFrame(1.); + Screen.SetScreenFade(1.); + return (screenfade > 0.); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual bool OnEvent(InputEvent ev) + { + if (paused || index < 0 || index >= jobs.Size()) return false; + if (jobs[index].jobstate != ScreenJob.running) return false; + return jobs[index].OnEvent(ev); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual bool OnTick() + { + if (paused) return false; + if (index >= jobs.Size() || jobs.Size() == 0) return true; + if (advance || index < 0) + { + advance = false; + AdvanceJob(terminateState < 0); + if (index >= jobs.Size()) + { + return true; + } + } + if (jobs[index].jobstate == ScreenJob.running) + { + jobs[index].ticks++; + jobs[index].OnTick(); + } + else if (jobs[index].jobstate == ScreenJob.stopping) + { + fadeticks++; + } + return false; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + virtual bool RunFrame(double smoothratio) + { + if (index < 0) + { + AdvanceJob(false); + } + // ensure that we won't go back in time if the menu is dismissed without advancing our ticker + if (index < jobs.Size()) + { + bool menuon = paused; + if (menuon) last_paused_tic = jobs[index].ticks; + else if (last_paused_tic == jobs[index].ticks) menuon = true; + if (menuon) smoothratio = 1.; + } + else smoothratio = 1.; + + if (actionState == State_Clear) + { + actionState = State_Run; + } + else if (actionState == State_Run) + { + terminateState = DisplayFrame(smoothratio); + if (terminateState < 1 && index < jobs.Size()) + { + if (jobs[index].flags & ScreenJob.fadeout) + { + jobs[index].fadestate = ScreenJob.fadeout; + jobs[index].jobstate = ScreenJob.stopping; + actionState = State_Fadeout; + fadeticks = 0; + } + else + { + advance = true; + } + } + } + else if (actionState == State_Fadeout) + { + int ended = FadeoutFrame(smoothratio); + if (ended < 1 && index < jobs.Size()) + { + jobs[index].jobstate = ScreenJob.stopped; + advance = true; + } + } + return true; + } + + void AddGenericVideo(String fn, int snd, int framerate) + { + Array sounds; + if (snd > 0) sounds.Pushv(1, snd); + Append(MoviePlayerJob.CreateWithSoundInfo(fn, sounds, 0, framerate)); + } +}