Merge branch 'scriptable_cutscenes' into newrenderer

This commit is contained in:
Christoph Oelckers 2021-05-02 22:37:46 +02:00
commit 8003ab6fa3
141 changed files with 10317 additions and 5320 deletions

View file

@ -749,7 +749,6 @@ set( NOT_COMPILED_SOURCE_FILES
games/blood/src/callback.cpp games/blood/src/callback.cpp
games/blood/src/choke.cpp games/blood/src/choke.cpp
games/blood/src/controls.cpp games/blood/src/controls.cpp
games/blood/src/credits.cpp
games/blood/src/db.cpp games/blood/src/db.cpp
games/blood/src/dude.cpp games/blood/src/dude.cpp
games/blood/src/d_menu.cpp games/blood/src/d_menu.cpp
@ -835,7 +834,6 @@ set( NOT_COMPILED_SOURCE_FILES
games/duke/src/spawn_r.cpp games/duke/src/spawn_r.cpp
# Shadow Warrior # Shadow Warrior
games/sw/src/2d.cpp
games/sw/src/actor.cpp games/sw/src/actor.cpp
games/sw/src/ai.cpp games/sw/src/ai.cpp
games/sw/src/break.cpp games/sw/src/break.cpp
@ -1049,6 +1047,7 @@ set (PCH_SOURCES
core/gamehud.cpp core/gamehud.cpp
core/gamefuncs.cpp core/gamefuncs.cpp
core/gameinput.cpp core/gameinput.cpp
core/g_mapinfo.cpp
core/interpolate.cpp core/interpolate.cpp
core/inputstate.cpp core/inputstate.cpp
core/maphack.cpp core/maphack.cpp

View file

@ -324,6 +324,22 @@ DEFINE_ACTION_FUNCTION(_Screen, ClearClipRect)
return 0; 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) void F2DDrawer::GetClipRect(int *x, int *y, int *w, int *h)
{ {
if (x) *x = clipleft; if (x) *x = clipleft;

View file

@ -138,6 +138,10 @@ void S_StopCustomStream(SoundStream *stream)
void S_PauseAllCustomStreams(bool on) void S_PauseAllCustomStreams(bool on)
{ {
static bool paused = false;
if (paused == on) return;
paused = on;
for (auto s : customStreams) for (auto s : customStreams)
{ {
s->SetPaused(on); s->SetPaused(on);

View file

@ -1107,4 +1107,4 @@ xy(menu_change, "menu/change")
xy(menu_advance, "menu/advance") xy(menu_advance, "menu/advance")
xx(zoomsize) xx(zoomsize)
xx(ScreenJobRunner)

View file

@ -371,7 +371,6 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer()
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE); SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE);
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
I_GetEvent();
static_cast<Win32BaseVideo *>(Video)->Shutdown(); static_cast<Win32BaseVideo *>(Video)->Shutdown();
} }

View file

@ -8706,7 +8706,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
bool writable; bool writable;
ArgList[i] = ArgList[i]->Resolve(ctx); // must be resolved before the address is requested. 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; auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType;
if (pointedType && pointedType->isDynArray()) if (pointedType && pointedType->isDynArray())

View file

@ -37,6 +37,7 @@
#include "dobject.h" #include "dobject.h"
#include "vm.h" #include "vm.h"
#include "types.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. // 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. // 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, ArrayPush<FDynArray_I32 COMMA
ACTION_RETURN_INT(self->Push(val)); ACTION_RETURN_INT(self->Push(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<FDynArray_I32>) DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Pop, ArrayPop<FDynArray_I32>)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32); PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);

View file

@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl)
ACTION_RETURN_FLOAT(self->ToDouble()); ACTION_RETURN_FLOAT(self->ToDouble());
} }
static void StringSplit(FString *self, TArray<FString> *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<FString>* tokens, const FString& delimiter, int keepEmpty)
{ {
self->Split(*tokens, delimiter, static_cast<FString::EmptyTokenType>(keepEmpty)); self->Split(*tokens, delimiter, static_cast<FString::EmptyTokenType>(keepEmpty));
} }

View file

@ -49,6 +49,7 @@
#include "s_music.h" #include "s_music.h"
#include "i_interface.h" #include "i_interface.h"
#include "base_sbar.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) 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<ETextureType>(type), flags).GetIndex();
return TexMan.CheckForTexture(name, static_cast<ETextureType>(type), (flags & ~FTextureManager::TEXMAN_ForceLookup)).GetIndex();
} }
DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckForTexture, CheckForTexture) DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckForTexture, CheckForTexture)
@ -477,6 +477,20 @@ DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, OkForLocalization, OkForLocalization_)
ACTION_RETURN_INT(OkForLocalization_(name, subst)); 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; 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) DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand)
{ {
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); 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, name);
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder);
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop);
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, handle);
DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses) DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses)
DEFINE_GLOBAL(Bindings) DEFINE_GLOBAL(Bindings)

View file

@ -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; 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))); 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; 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))); return g_cos(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
} }

View file

@ -242,7 +242,7 @@ void changeMap(int player, uint8_t** stream, bool skip)
void endScreenJob(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) 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. 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 (argv.argc() <= numparm)
{ {
if (numparm == 2) Printf(PRINT_BOLD, "%s <e> <m>: %s episode 'e' and map 'm'\n", cmdname, t2); if (numparm == 2) Printf(PRINT_BOLD, "%s <e> <m>: %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"); Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n");
return nullptr; return nullptr;
} }
auto map = FindMapByLevelNum(numparm == 1 ? m : levelnum(e - 1, m - 1)); auto map = FindMapByIndex(e, m);
if (!map) if (!map)
{ {
if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]); if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]);

1232
source/core/g_mapinfo.cpp Normal file

File diff suppressed because it is too large Load diff

108
source/core/g_mapinfo.h Normal file
View file

@ -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__

View file

@ -73,6 +73,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "automap.h" #include "automap.h"
#include "v_draw.h" #include "v_draw.h"
#include "gi.h" #include "gi.h"
#include "vm.h"
#include "g_mapinfo.h"
#include "gamefuncs.h" #include "gamefuncs.h"
#include "hw_voxels.h" #include "hw_voxels.h"
#include "hw_palmanager.h" #include "hw_palmanager.h"
@ -286,6 +288,11 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr)
UserConfig userConfig; 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() void UserConfig::ProcessOptions()
{ {
// -help etc are omitted // -help etc are omitted
@ -560,7 +567,7 @@ int GameMain()
I_ShowFatalError(err.what()); I_ShowFatalError(err.what());
r = -1; r = -1;
} }
DeleteScreenJob(); //DeleteScreenJob();
DeinitMenus(); DeinitMenus();
if (StatusBar) StatusBar->Destroy(); if (StatusBar) StatusBar->Destroy();
StatusBar = nullptr; StatusBar = nullptr;
@ -601,13 +608,17 @@ int GameMain()
void SetDefaultStrings() void SetDefaultStrings()
{ {
// Duke 1.3 does not define its episodes through CON.
if ((g_gameType & GAMEFLAG_DUKE) && fileSystem.FindFile("E4L1.MAP") < 0) 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. // Pre-Atomic releases do not define this.
gVolumeNames[0] = "$L.A. Meltdown"; vol0->name = "$L.A. Meltdown";
gVolumeNames[1] = "$Lunar Apocalypse"; vol1->name = "$Lunar Apocalypse";
gVolumeNames[2] = "$Shrapnel City"; vol2->name = "$Shrapnel City";
if (g_gameType & GAMEFLAG_SHAREWARE) gVolumeNames[3] = "$The Birth";
gSkillNames[0] = "$Piece of Cake"; gSkillNames[0] = "$Piece of Cake";
gSkillNames[1] = "$Let's Rock"; gSkillNames[1] = "$Let's Rock";
gSkillNames[2] = "$Come get Some"; gSkillNames[2] = "$Come get Some";
@ -952,6 +963,7 @@ int RunGame()
LoadScripts(); LoadScripts();
StartScreen->Progress(); StartScreen->Progress();
SetDefaultStrings(); SetDefaultStrings();
Job_Init();
if (Args->CheckParm("-sounddebug")) if (Args->CheckParm("-sounddebug"))
C_DoCommand("stat sounddebug"); C_DoCommand("stat sounddebug");
@ -967,6 +979,7 @@ int RunGame()
engineInit(); engineInit();
gi->app_init(); gi->app_init();
StartScreen->Progress(); StartScreen->Progress();
G_ParseMapInfo();
CreateStatusBar(); CreateStatusBar();
SetDefaultMenuColors(); SetDefaultMenuColors();
M_Init(); M_Init();
@ -1440,13 +1453,69 @@ DEFINE_ACTION_FUNCTION(_Screen, GetViewWindow)
return MIN(numret, 4); return MIN(numret, 4);
} }
DEFINE_ACTION_FUNCTION_NATIVE(_Build, ShadeToLight, shadeToLight) DEFINE_ACTION_FUNCTION_NATIVE(_Raze, ShadeToLight, shadeToLight)
{ {
PARAM_PROLOGUE; PARAM_PROLOGUE;
PARAM_INT(shade); PARAM_INT(shade);
ACTION_RETURN_INT(shadeToLight(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; extern bool demoplayback;
DEFINE_GLOBAL(multiplayer) DEFINE_GLOBAL(multiplayer)
DEFINE_GLOBAL(netgame) DEFINE_GLOBAL(netgame)
@ -1454,6 +1523,37 @@ DEFINE_GLOBAL(gameaction)
DEFINE_GLOBAL(gamestate) DEFINE_GLOBAL(gamestate)
DEFINE_GLOBAL(demoplayback) DEFINE_GLOBAL(demoplayback)
DEFINE_GLOBAL(consoleplayer) 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() void InitBuildTiles()

View file

@ -70,12 +70,12 @@ extern UserConfig userConfig;
extern int nomusic; extern int nomusic;
extern bool nosound; extern bool nosound;
inline bool MusicEnabled() inline int MusicEnabled() // int return is for scripting
{ {
return mus_enabled && !nomusic; return mus_enabled && !nomusic;
} }
inline bool SoundEnabled() inline int SoundEnabled()
{ {
return snd_enabled && !nosound; return snd_enabled && !nosound;
} }
@ -101,6 +101,13 @@ enum
GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher. GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher.
GAMEFLAG_WORLDTOUR = 0x00008000, GAMEFLAG_WORLDTOUR = 0x00008000,
GAMEFLAG_DUKEDC = 0x00010000, 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, GAMEFLAG_DUKECOMPAT = GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI | GAMEFLAG_RRALL,
GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo

View file

@ -43,8 +43,10 @@ enum gameaction_t : int
ga_nextlevel, // Actually start the next level. ga_nextlevel, // Actually start the next level.
ga_loadgamehidecon, ga_loadgamehidecon,
ga_newgamenostopsound, // start a new game ga_newgamenostopsound, // start a new game
ga_endscreenjob,
ga_fullconsole, ga_fullconsole,
}; };
extern gamestate_t gamestate; extern gamestate_t gamestate;
extern gameaction_t gameaction; extern gameaction_t gameaction;
extern int intermissiondelay;

View file

@ -79,7 +79,7 @@ struct GameInterface
virtual void MenuSound(EMenuSounds snd) {} virtual void MenuSound(EMenuSounds snd) {}
virtual bool CanSave() { return true; } virtual bool CanSave() { return true; }
virtual void CustomMenuSelection(int menu, int item) {} 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 FSavegameInfo GetSaveSig() { return { "", 0, 0}; }
virtual double SmallFontScale() { return 1; } virtual double SmallFontScale() { return 1; }
virtual void SerializeGameState(FSerializer& arc) {} virtual void SerializeGameState(FSerializer& arc) {}

View file

@ -105,6 +105,7 @@ bool r_NoInterpolate;
int entertic; int entertic;
int oldentertics; int oldentertics;
int gametic; int gametic;
int intermissiondelay;
FString BackupSaveGame; FString BackupSaveGame;
@ -133,6 +134,20 @@ void G_BuildTiccmd(ticcmd_t* cmd)
//========================================================================== //==========================================================================
bool newGameStarted; 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() static void GameTicker()
{ {
int i; int i;
@ -159,7 +174,7 @@ static void GameTicker()
FX_SetReverb(0); FX_SetReverb(0);
gi->FreeLevelData(); gi->FreeLevelData();
gameaction = ga_level; gameaction = ga_level;
gi->NewGame(g_nextmap, -1); NewGame(g_nextmap, -1);
BackupSaveGame = ""; BackupSaveGame = "";
} }
break; break;
@ -191,13 +206,12 @@ static void GameTicker()
FX_StopAllSounds(); FX_StopAllSounds();
case ga_newgamenostopsound: case ga_newgamenostopsound:
DeleteScreenJob(); DeleteScreenJob();
newGameStarted = true;
FX_SetReverb(0); FX_SetReverb(0);
gi->FreeLevelData(); gi->FreeLevelData();
C_FlushDisplay(); C_FlushDisplay();
gameaction = ga_level; gameaction = ga_level;
BackupSaveGame = ""; BackupSaveGame = "";
gi->NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound); NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
break; break;
case ga_startup: case ga_startup:
@ -209,6 +223,7 @@ static void GameTicker()
case ga_mainmenu: case ga_mainmenu:
FX_StopAllSounds(); FX_StopAllSounds();
if (isBlood()) Mus_Stop();
case ga_mainmenunostopsound: case ga_mainmenunostopsound:
gi->FreeLevelData(); gi->FreeLevelData();
gamestate = GS_MENUSCREEN; gamestate = GS_MENUSCREEN;
@ -253,6 +268,10 @@ static void GameTicker()
gameaction = ga_nothing; gameaction = ga_nothing;
break; break;
case ga_endscreenjob:
EndScreenJob();
break;
// for later // for later
// case ga_recordgame, // start a new demo recording (later) // case ga_recordgame, // start a new demo recording (later)
// case ga_loadgameplaydemo, // load a savegame and play a demo. // case ga_loadgameplaydemo, // load a savegame and play a demo.
@ -331,7 +350,16 @@ static void GameTicker()
break; break;
case GS_INTERMISSION: case GS_INTERMISSION:
case GS_INTRO: case GS_INTRO:
ScreenJobTick(); if (intermissiondelay > 0)
{
intermissiondelay--;
break;
}
if (ScreenJobTick())
{
// synchronize termination with the playsim.
Net_WriteByte(DEM_ENDSCREENJOB);
}
break; break;
} }
@ -371,7 +399,7 @@ void Display()
case GS_INTRO: case GS_INTRO:
case GS_INTERMISSION: case GS_INTERMISSION:
// screen jobs are not bound by the game ticker so they need to be ticked in the display loop. // screen jobs are not bound by the game ticker so they need to be ticked in the display loop.
ScreenJobDraw(); if (intermissiondelay <= 0) ScreenJobDraw();
break; break;
case GS_LEVEL: case GS_LEVEL:
@ -633,6 +661,16 @@ void MainLoop ()
// Clamp the timer to TICRATE until the playloop has been entered. // Clamp the timer to TICRATE until the playloop has been entered.
r_NoInterpolate = true; 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 (;;) for (;;)
{ {
try try

View file

@ -41,43 +41,102 @@
#include "raze_sound.h" #include "raze_sound.h"
FString gSkillNames[MAXSKILLS]; FString gSkillNames[MAXSKILLS];
FString gVolumeNames[MAXVOLUMES];
FString gVolumeSubtitles[MAXVOLUMES];
int32_t gVolumeFlags[MAXVOLUMES];
int gDefaultVolume = 0, gDefaultSkill = 1; int gDefaultVolume = 0, gDefaultSkill = 1;
MapRecord mapList[512]; GlobalCutscenes globalCutscenes;
MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.) TArray<ClusterDef> clusters;
TArray<VolumeRecord> volumes;
TArray<TPointer<MapRecord>> 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. MapRecord* lastLevel; // Same here, for the last level.
unsigned int numUsedSlots;
CCMD(listmaps) 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) if (lump >= 0)
{ {
int rfnum = fileSystem.GetFileContainer(lump); 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 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) 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 &map; return map.Data();
} }
} }
return nullptr; return nullptr;
@ -86,23 +145,78 @@ MapRecord *FindMapByName(const char *nm)
MapRecord *FindMapByLevelNum(int num) 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 &map; return map.Data();
} }
} }
return nullptr; return nullptr;
} }
MapRecord *FindNextMap(MapRecord *thismap) VolumeRecord* FindVolume(int index)
{ {
if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel); for (auto& vol : volumes)
return FindMapByLevelNum(thismap->levelNumber+1); {
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) bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
{ {
static const char* specials[] = { "intro", "briefing", "loading" }; 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') if (numMatches != 4 || toupper(b1) != 'E' || toupper(b2) != 'L')
return false; return false;
index = FindMapByLevelNum(levelnum(ep - 1, lev - 1)); index = FindMapByIndexOnly(ep, lev);
} }
if (index != nullptr) if (index != nullptr)
@ -142,18 +256,19 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
MapRecord *AllocateMap() MapRecord *AllocateMap()
{ {
return &mapList[numUsedSlots++]; auto&p = mapList[mapList.Reserve(1)];
p.Alloc();
return p.Data();
} }
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic) 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 &map; return map.Data();
} }
} }

View file

@ -3,28 +3,58 @@
#include "gstrings.h" #include "gstrings.h"
#include "cmdlib.h" #include "cmdlib.h"
#include "quotemgr.h" #include "quotemgr.h"
#include "palentry.h"
#include "vectors.h"
#ifdef GetMessage #ifdef GetMessage
#undef GetMessage // Windows strikes... #undef GetMessage // Windows strikes...
#endif #endif
enum EMax enum EMax
{ {
MAXSKILLS = 7, MAXSKILLS = 7,
MAXVOLUMES = 7,
MAXMENUGAMEPLAYENTRIES = 7, MAXMENUGAMEPLAYENTRIES = 7,
}; };
enum EVolFlags 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. // These get filled in by the map definition parsers of the front ends.
extern FString gSkillNames[MAXSKILLS]; extern FString gSkillNames[MAXSKILLS];
extern FString gVolumeNames[MAXVOLUMES];
extern FString gVolumeSubtitles[MAXVOLUMES];
extern int32_t gVolumeFlags[MAXVOLUMES];
extern int gDefaultVolume, gDefaultSkill; extern int gDefaultVolume, gDefaultSkill;
@ -46,6 +76,58 @@ enum {
MAX_MESSAGES = 32 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 struct MapRecord
{ {
int parTime = 0; int parTime = 0;
@ -54,17 +136,40 @@ struct MapRecord
FString labelName; FString labelName;
FString name; FString name;
FString music; FString music;
FString Author;
FString NextMap;
FString NextSecret;
int cdSongId = -1; int cdSongId = -1;
int musicorder = -1;
CutsceneDef intro;
CutsceneDef outro;
int flags = 0; int flags = 0;
int gameflags = 0;
int levelNumber = -1; 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<FString> PrecacheTextures;
FVector4 skyrotatevector;
// The rest is only used by Blood // The rest is only used by Blood
int nextLevel = -1;
int nextSecret = -1;
FString messages[MAX_MESSAGES]; FString messages[MAX_MESSAGES];
FString author;
int8_t fog = -1, weather = -1; // Blood defines these but they aren't used. 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 const char* LabelName() const
{ {
if (flags & MI_USERMAP) return GStrings("TXT_USERMAP"); if (flags & MI_USERMAP) return GStrings("TXT_USERMAP");
@ -97,39 +202,65 @@ struct MapRecord
{ {
messages[num] = msg; 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; extern MapRecord *currentLevel;
bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false); bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false);
MapRecord *FindMapByName(const char *nm); MapRecord *FindMapByName(const char *nm);
MapRecord *FindMapByLevelNum(int num); 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 *FindNextMap(MapRecord *thismap);
MapRecord* FindNextSecretMap(MapRecord* thismap);
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr); MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
MapRecord* AllocateMap(); 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 // 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; 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 enum
{ {
RRENDSLOT = 127 RRENDSLOT = 127

View file

@ -83,8 +83,31 @@ bool help_disabled;
FNewGameStartup NewGameStartupInfo; FNewGameStartup NewGameStartupInfo;
//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) bool M_SetSpecialMenu(FName& menu, int param)
{ {
@ -115,13 +138,19 @@ bool M_SetSpecialMenu(FName& menu, int param)
case NAME_Startgame: case NAME_Startgame:
case NAME_StartgameNoSkill: case NAME_StartgameNoSkill:
menu = NAME_Startgame;
NewGameStartupInfo.Skill = param; NewGameStartupInfo.Skill = param;
if (menu == NAME_StartgameNoSkill) NewGameStartupInfo.Episode = param; if (menu == NAME_StartgameNoSkill)
if (gi->StartGame(NewGameStartupInfo)) {
menu = NAME_Startgame;
NewGameStartupInfo.Episode = param;
NewGameStartupInfo.Skill = 1;
}
if (DoStartGame(NewGameStartupInfo))
{ {
M_ClearMenus(); 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(); inputState.ClearAllInput();
} }
return false; return false;
@ -365,6 +394,7 @@ static DMenuItemBase* CreateCustomListMenuItemText(double x, double y, int heigh
// Creates the episode menu // Creates the episode menu
// //
//============================================================================= //=============================================================================
extern TArray<VolumeRecord> volumes;
static void BuildEpisodeMenu() static void BuildEpisodeMenu()
{ {
@ -385,22 +415,22 @@ static void BuildEpisodeMenu()
ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items
int y = ld->mYpos; 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); int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && (vol.flags & VF_SHAREWARELOCK));
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gVolumeNames[i][0], auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, vol.name[0],
gVolumeNames[i], ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, i); // font colors are not used, so hijack one for the shareware flag. 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; y += ld->mLinespacing;
ld->mItems.Push(it); ld->mItems.Push(it);
addedVolumes++; addedVolumes++;
if (gVolumeSubtitles[i].IsNotEmpty()) if (vol.subtitle.IsNotEmpty())
{ {
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing * 6 / 10, 1, 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; y += ld->mLinespacing * 6 / 10;
ld->mItems.Push(it); ld->mItems.Push(it);
textadded = true; textadded = true;

View file

@ -49,15 +49,25 @@
#include <vpx/vpx_decoder.h> #include <vpx/vpx_decoder.h>
#include <vpx/vp8dx.h> #include <vpx/vp8dx.h>
#include "raze_music.h" #include "raze_music.h"
#include "vm.h"
class MoviePlayer class MoviePlayer
{ {
protected:
enum EMovieFlags
{
NOSOUNDCUTOFF = 1,
FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros.
};
int flags;
public: public:
virtual void Start() {} virtual void Start() {}
virtual bool Frame(uint64_t clock) = 0; virtual bool Frame(uint64_t clock) = 0;
virtual void Stop() {} virtual void Stop() {}
virtual ~MoviePlayer() = default; virtual ~MoviePlayer() = default;
virtual FTextureID GetTexture() = 0;
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -76,16 +86,17 @@ class AnmPlayer : public MoviePlayer
int frametime = 0; int frametime = 0;
int nextframetime = 0; int nextframetime = 0;
AnimTextures animtex; AnimTextures animtex;
const AnimSound* animSnd; const TArray<int> animSnd;
const int* frameTicks; int frameTicks[3];
bool nostopsound;
public: public:
bool isvalid() { return numframes > 0; } bool isvalid() { return numframes > 0; }
AnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff) AnmPlayer(FileReader& fr, TArray<int>& ans, const int *frameticks, int flags_)
: animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff) : animSnd(std::move(ans))
{ {
memcpy(frameTicks, frameticks, 3 * sizeof(int));
flags = flags_;
buffer = fr.ReadPadded(1); buffer = fr.ReadPadded(1);
fr.Close(); fr.Close();
@ -109,17 +120,12 @@ public:
if (currentclock < nextframetime - 1) if (currentclock < nextframetime - 1)
{ {
twod->ClearScreen();
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
return true; return true;
} }
animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe)); animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe));
frametime = currentclock; frametime = currentclock;
twod->ClearScreen();
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
int delay = 20; int delay = 20;
if (frameTicks) if (frameTicks)
{ {
@ -129,11 +135,12 @@ public:
} }
nextframetime += delay; 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) if (sound == -1)
soundEngine->StopAllChannels(); soundEngine->StopAllChannels();
else if (SoundEnabled()) else if (SoundEnabled())
@ -147,6 +154,7 @@ public:
void Stop() override void Stop() override
{ {
bool nostopsound = (flags & NOSOUNDCUTOFF);
if (!nostopsound) soundEngine->StopAllChannels(); if (!nostopsound) soundEngine->StopAllChannels();
} }
@ -156,6 +164,11 @@ public:
buffer.Reset(); buffer.Reset();
animtex.Clean(); animtex.Clean();
} }
FTextureID GetTexture() override
{
return animtex.GetFrameID();
}
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -187,8 +200,6 @@ public:
{ {
if (failed) return false; if (failed) return false;
bool playon = decoder.RunFrame(clock); bool playon = decoder.RunFrame(clock);
twod->ClearScreen();
DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
return playon; return playon;
} }
@ -196,6 +207,11 @@ public:
{ {
decoder.Close(); decoder.Close();
} }
FTextureID GetTexture() override
{
return decoder.animTex().GetFrameID();
}
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -209,7 +225,7 @@ class VpxPlayer : public MoviePlayer
bool failed = false; bool failed = false;
FileReader fr; FileReader fr;
AnimTextures animtex; AnimTextures animtex;
const AnimSound* animSnd; const TArray<int> animSnd;
unsigned width, height; unsigned width, height;
TArray<uint8_t> Pic; TArray<uint8_t> Pic;
@ -234,10 +250,10 @@ public:
public: public:
bool isvalid() { return !failed; } bool isvalid() { return !failed; }
VpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay, FString& error) VpxPlayer(FileReader& fr_, TArray<int>& animSnd_, int flags_, int origframedelay, FString& error) : animSnd(std::move(animSnd_))
{ {
fr = std::move(fr_); fr = std::move(fr_);
animSnd = animSnd_; flags = flags_;
if (!ReadIVFHeader(origframedelay)) if (!ReadIVFHeader(origframedelay))
{ {
@ -433,30 +449,35 @@ public:
framenum++; framenum++;
if (framenum >= numframes) stop = true; if (framenum >= numframes) stop = true;
bool nostopsound = (flags & NOSOUNDCUTOFF);
int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum; int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum;
if (soundframe > lastsoundframe) 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 (animSnd[i] == soundframe)
{
int sound = animSnd[i + 1];
if (sound == -1) if (sound == -1)
soundEngine->StopAllChannels(); soundEngine->StopAllChannels();
else if (SoundEnabled()) 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);
}
} }
} }
lastsoundframe = soundframe; lastsoundframe = soundframe;
} }
} }
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit, TAG_DONE);
return !stop; return !stop;
} }
void Stop() void Stop()
{ {
Mus_Stop(); Mus_Stop();
bool nostopsound = (flags & NOSOUNDCUTOFF);
if (!nostopsound) soundEngine->StopAllChannels();
} }
~VpxPlayer() ~VpxPlayer()
@ -464,6 +485,11 @@ public:
vpx_codec_destroy(&codec); vpx_codec_destroy(&codec);
animtex.Clean(); animtex.Clean();
} }
FTextureID GetTexture() override
{
return animtex.GetFrameID();
}
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -498,9 +524,10 @@ class SmkPlayer : public MoviePlayer
bool fullscreenScale; bool fullscreenScale;
uint64_t nFrameNs; uint64_t nFrameNs;
int nFrame = 0; int nFrame = 0;
const AnimSound* animSnd; const TArray<int> animSnd;
FString filename; FString filename;
SoundStream* stream = nullptr; SoundStream* stream = nullptr;
bool hassound = false;
public: public:
bool isvalid() { return hSMK.isValid; } bool isvalid() { return hSMK.isValid; }
@ -534,22 +561,21 @@ public:
} }
SmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport) SmkPlayer(const char *fn, TArray<int>& ans, int flags_) : animSnd(std::move(ans))
{ {
hSMK = Smacker_Open(fn); hSMK = Smacker_Open(fn);
if (!hSMK.isValid) if (!hSMK.isValid)
{ {
return; return;
} }
flags = flags_;
Smacker_GetFrameSize(hSMK, nWidth, nHeight); Smacker_GetFrameSize(hSMK, nWidth, nHeight);
pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight)); pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight));
nFrameRate = Smacker_GetFrameRate(hSMK); nFrameRate = Smacker_GetFrameRate(hSMK);
nFrameNs = 1'000'000'000 / nFrameRate; nFrameNs = 1'000'000'000 / nFrameRate;
nFrames = Smacker_GetNumFrames(hSMK); nFrames = Smacker_GetNumFrames(hSMK);
Smacker_GetPalette(hSMK, palette); Smacker_GetPalette(hSMK, palette);
fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480);
bool hassound = false;
numAudioTracks = Smacker_GetNumAudioTracks(hSMK); numAudioTracks = Smacker_GetNumAudioTracks(hSMK);
if (numAudioTracks) if (numAudioTracks)
{ {
@ -562,14 +588,12 @@ public:
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data()); auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read); if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
else copy16bitSamples(read); else copy16bitSamples(read);
animSnd = nullptr;
hassound = true; hassound = true;
} }
} }
if (!hassound) if (!hassound)
{ {
adata.inf = {}; 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) if (frame > nFrame)
{ {
nFrame++; nFrame++;
Smacker_GetNextFrame(hSMK); 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) if (sound == -1)
soundEngine->StopAllChannels(); soundEngine->StopAllChannels();
else if (SoundEnabled()) 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; return nFrame < nFrames;
} }
void Stop() override
{
if (stream) S_StopCustomStream(stream);
bool nostopsound = (flags & NOSOUNDCUTOFF);
if (!nostopsound && !hassound) soundEngine->StopAllChannels();
}
~SmkPlayer() ~SmkPlayer()
{ {
Smacker_Close(hSMK); Smacker_Close(hSMK);
if (stream) S_StopCustomStream(stream);
soundEngine->StopAllChannels();
animtex.Clean(); animtex.Clean();
} }
FTextureID GetTexture() override
{
return animtex.GetFrameID();
}
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -655,61 +684,11 @@ public:
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
class DMoviePlayer : public DSkippableScreenJob MoviePlayer* OpenMovie(const char* filename, TArray<int>& ans, const int* frameticks, int flags, FString& error)
{
MoviePlayer* player;
bool started = false;
public:
DMoviePlayer(MoviePlayer* mp)
{
player = mp;
pausable = false;
}
void Draw(double smoothratio) override
{
if (!player)
{
state = stopped;
return;
}
if (!started)
{
started = true;
player->Start();
}
uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate;
if (state == running && !player->Frame(clock))
{
state = finished;
}
}
void OnDestroy() override
{
if (player)
{
player->Stop();
delete player;
}
player = nullptr;
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff, FString& error)
{ {
FileReader fr; FileReader fr;
// first try as .ivf - but only if sounds are provided - the decoder is video only. // first try as .ivf - but only if sounds are provided - the decoder is video only.
if (ans) if (ans.Size())
{ {
auto fn = StripExtension(filename); auto fn = StripExtension(filename);
DefaultExtension(fn, ".ivf"); DefaultExtension(fn, ".ivf");
@ -739,7 +718,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
if (!memcmp(id, "LPF ", 4)) if (!memcmp(id, "LPF ", 4))
{ {
auto anm = new AnmPlayer(fr, ans, frameticks, nosoundcutoff); auto anm = new AnmPlayer(fr, ans, frameticks, flags);
if (!anm->isvalid()) if (!anm->isvalid())
{ {
error.Format("%s: invalid ANM file.\n", filename); 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)) else if (!memcmp(id, "SMK2", 4))
{ {
fr.Close(); 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()) if (!anm->isvalid())
{ {
error.Format("%s: invalid SMK file.\n", filename); 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)) 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()) if (!anm->isvalid())
{ {
delete anm; 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) PARAM_PROLOGUE;
{ PARAM_STRING(filename);
return Create<DBlackScreen>(1); PARAM_POINTER(sndinf, TArray<int>);
} PARAM_INT(flags);
PARAM_INT(frametime);
PARAM_INT(firstframetime);
PARAM_INT(lastframetime);
FString error; 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) if (!movie)
{ {
Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars()); Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars());
return Create<DBlackScreen>(1);
} }
return Create<DMoviePlayer>(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());
}

View file

@ -39,8 +39,10 @@
#include "hw_material.h" #include "hw_material.h"
#include "gamestruct.h" #include "gamestruct.h"
#include "gamecontrol.h" #include "gamecontrol.h"
#include "texturemanager.h"
#include "hw_models.h" #include "hw_models.h"
#include "hw_voxels.h" #include "hw_voxels.h"
#include "mapinfo.h"
BEGIN_BLD_NS BEGIN_BLD_NS
extern short voxelIndex[MAXTILES]; extern short voxelIndex[MAXTILES];
@ -129,6 +131,19 @@ void precacheMarkedTiles()
int dapalnum = pair->Key >> 32; int dapalnum = pair->Key >> 32;
doprecache(dapicnum, dapalnum); doprecache(dapicnum, dapalnum);
} }
// Cache everything the map explicitly declares.
TMap<FString, bool> 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(); cachemap.Clear();
} }

View file

@ -2,5 +2,6 @@
void PrecacheHardwareTextures(int nTile); void PrecacheHardwareTextures(int nTile);
void markTileForPrecache(int tilenum, int palnum); void markTileForPrecache(int tilenum, int palnum);
void markTextureForPrecache(const char* texname);
void markVoxelForPrecache(int voxnum); void markVoxelForPrecache(int voxnum);
void precacheMarkedTiles(); void precacheMarkedTiles();

View file

@ -51,168 +51,216 @@
#include <vpx/vpx_decoder.h> #include <vpx/vpx_decoder.h>
#include <vpx/vp8dx.h> #include <vpx/vp8dx.h>
#include "raze_music.h" #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) //
//
//
//=============================================================================
void Job_Init()
bool DSkippableScreenJob::OnEvent(event_t* evt)
{ {
if (evt->type == EV_KeyDown && !specialKeyEvent(evt)) static bool done = false;
if (!done)
{ {
state = skipped; done = true;
Skipped(); 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);
}
}
//=============================================================================
//
//
//
//=============================================================================
bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition)
{
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; return true;
}
void DBlackScreen::OnTick()
{
if (cleared)
{
int span = ticks * 1000 / GameTicRate;
if (span > wait) state = finished;
} }
} else if (video.IsNotEmpty())
void DBlackScreen::Draw(double)
{
cleared = true;
twod->ClearScreen();
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void DImageScreen::OnTick()
{
if (cleared)
{ {
int span = ticks * 1000 / GameTicRate; AddGenericVideo(runner, video, GetSound(), framespersec);
if (span > waittime) state = finished; return true;
} }
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DeleteScreenJob()
{
if (runner) runner->Destroy();
runner = nullptr;
}
void EndScreenJob()
{
DeleteScreenJob();
if (completion) completion(false);
completion = nullptr;
} }
void DImageScreen::Draw(double smoothratio) //=============================================================================
//
//
//
//=============================================================================
bool ScreenJobResponder(event_t* ev)
{ {
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;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class ScreenJobRunner
{
enum
{
State_Clear,
State_Run,
State_Fadeout
};
TArray<JobDesc> 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) 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. // We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
@ -223,158 +271,290 @@ public:
return true; return true;
} }
} }
FInputEvent evt = ev;
if (jobs[index].job->state != DScreenJob::running) return false; if (runner)
return jobs[index].job->OnEvent(ev);
}
void OnFinished()
{ {
if (completion) completion(false); IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent)
completion = nullptr; // only finish once.
}
void OnTick()
{ {
if (paused) return; int result = 0;
if (index >= jobs.Size()) VMValue parm[] = { runner, &evt };
{ VMReturn ret(&result);
//DeleteJobs(); VMCall(func, parm, 2, &ret, 1);
//twod->SetScreenFade(1); return result;
//twod->ClearScreen(); // This must not leave the 2d buffer empty.
//if (gamestate == GS_INTRO) OnFinished();
//else Net_WriteByte(DEM_ENDSCREENJOB); // intermissions must be terminated synchronously.
}
else
{
if (jobs[index].job->state == DScreenJob::running)
{
jobs[index].job->ticks++;
jobs[index].job->OnTick();
}
else if (jobs[index].job->state == DScreenJob::stopping)
{
fadeticks++;
} }
} }
}
bool RunFrame()
{
if (index >= jobs.Size())
{
DeleteJobs();
twod->SetScreenFade(1);
twod->ClearScreen(); // This must not leave the 2d buffer empty.
if (completion) completion(false);
return false; return false;
} }
// ensure that we won't go back in time if the menu is dismissed without advancing our ticker //=============================================================================
bool menuon = paused; //
if (menuon) last_paused_tic = jobs[index].job->ticks; //
else if (last_paused_tic == jobs[index].job->ticks) menuon = true; //
double smoothratio = menuon ? 1. : I_GetTimeFrac(); //=============================================================================
if (actionState == State_Clear) bool ScreenJobTick()
{
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); ticks++;
videoclearFade(); if (runner)
if (count)
{ {
runner = new ScreenJobRunner(jobs, count, completion, clearbefore); IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick)
gameaction = blockingui? ga_intro : ga_intermission; {
int result = 0;
VMValue parm[] = { runner };
VMReturn ret(&result);
VMCall(func, parm, 1, &ret, 1);
return result;
} }
else }
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void ScreenJobDraw()
{
double smoothratio = I_GetTimeFrac();
if (runner)
{ {
completion(false); twod->ClearScreen();
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame)
{
VMValue parm[] = { runner, smoothratio };
VMCall(func, parm, 2, nullptr, 0);
}
} }
} }
void DeleteScreenJob() //=============================================================================
//
//
//
//=============================================================================
bool ScreenJobValidate()
{ {
if (runner) if (runner)
{ {
delete runner; IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate)
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. int res;
if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job"); VMValue parm[] = { runner };
twod->ClearScreen(); VMReturn ret(&res);
return false; VMCall(func, parm, 2, &ret, 1);
}
auto res = runner->RunFrame();
if (!res)
{
assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing);
DeleteScreenJob();
}
return res; 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;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
{
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;
return;
}
gameaction = ga_intermission;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
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 (fromMap)
{
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 <buildfunction>\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
{
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)
{
gameaction = ga_mainmenunostopsound;
});
else gameaction = ga_mainmenu;
*/

View file

@ -3,153 +3,29 @@
#include "dobject.h" #include "dobject.h"
#include "v_2ddrawer.h" #include "v_2ddrawer.h"
#include "d_eventbase.h" #include "d_eventbase.h"
#include "s_soundinternal.h"
#include "gamestate.h"
using CompletionFunc = std::function<void(bool)>; using CompletionFunc = std::function<void(bool)>;
struct JobDesc;
class ScreenJobRunner;
class DScreenJob : public DObject void Job_Init();
enum
{ {
DECLARE_CLASS(DScreenJob, DObject) SJ_BLOCKUI = 1,
const int fadestyle; SJ_DELAY = 2,
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; }
}; };
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
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 EndScreenJob();
void DeleteScreenJob(); void DeleteScreenJob();
bool ScreenJobResponder(event_t* ev); bool ScreenJobResponder(event_t* ev);
void ScreenJobTick(); bool ScreenJobTick();
bool ScreenJobDraw(); void ScreenJobDraw();
struct AnimSound struct CutsceneDef;
{ struct MapRecord;
int framenum; struct SummaryInfo;
int soundnum; 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_);
DScreenJob *PlayVideo(const char *filename, const AnimSound *ans = nullptr, const int *frameticks = nullptr, bool nosoundstop = false); void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_);

View file

@ -398,6 +398,12 @@ static TArray<GrpInfo> ParseGrpInfo(const char *fn, FileReader &fr, TMap<FString
FlagMap.Insert("GAMEFLAG_POWERSLAVE", GAMEFLAG_POWERSLAVE); FlagMap.Insert("GAMEFLAG_POWERSLAVE", GAMEFLAG_POWERSLAVE);
FlagMap.Insert("GAMEFLAG_EXHUMED", GAMEFLAG_EXHUMED); FlagMap.Insert("GAMEFLAG_EXHUMED", GAMEFLAG_EXHUMED);
FlagMap.Insert("GAMEFLAG_DUKEDC", GAMEFLAG_DUKEDC); FlagMap.Insert("GAMEFLAG_DUKEDC", GAMEFLAG_DUKEDC);
FlagMap.Insert("GAMEFLAG_DUKENW", GAMEFLAG_DUKENW);
FlagMap.Insert("GAMEFLAG_DUKEVACA", GAMEFLAG_DUKEVACA);
FlagMap.Insert("GAMEFLAG_BLOODCP", GAMEFLAG_BLOODCP);
FlagMap.Insert("GAMEFLAG_ROUTE66", GAMEFLAG_ROUTE66);
FlagMap.Insert("GAMEFLAG_SWWANTON", GAMEFLAG_SWWANTON);
FlagMap.Insert("GAMEFLAG_SWTWINDRAG", GAMEFLAG_SWTWINDRAG);
FScanner sc; FScanner sc;
auto mem = fr.Read(); auto mem = fr.Read();

View file

@ -240,7 +240,9 @@ void DBaseStatusBar::PrintAutomapInfo(FLevelStats& stats, bool forcetextfont)
{ {
y = 200 - stats.screenbottomspace - spacing; y = 200 - stats.screenbottomspace - spacing;
} }
const auto &volname = gVolumeNames[volfromlevelnum(lev->levelNumber)]; auto cluster = FindCluster(lev->cluster);
FString volname;
if (cluster) volname = cluster->name;
if (volname.IsEmpty() && am_nameontop) y = 1; 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, DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, mapname, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,

View file

@ -31,7 +31,6 @@
#include "src/callback.cpp" #include "src/callback.cpp"
#include "src/choke.cpp" #include "src/choke.cpp"
#include "src/controls.cpp" #include "src/controls.cpp"
#include "src/credits.cpp"
#include "src/db.cpp" #include "src/db.cpp"
#include "src/dude.cpp" #include "src/dude.cpp"
#include "src/endgame.cpp" #include "src/endgame.cpp"

View file

@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "v_draw.h" #include "v_draw.h"
#include "texturemanager.h" #include "texturemanager.h"
#include "statusbar.h" #include "statusbar.h"
#include "vm.h"
BEGIN_BLD_NS BEGIN_BLD_NS
@ -79,7 +80,7 @@ void EndLevel(void)
seqKillAll(); seqKillAll();
} }
void StartLevel(MapRecord* level) void StartLevel(MapRecord* level, bool newgame)
{ {
if (!level) return; if (!level) return;
gFrameCount = 0; gFrameCount = 0;
@ -96,14 +97,14 @@ void StartLevel(MapRecord* level)
/////// ///////
} }
#if 0 #if 0
else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel)) else if (gGameOptions.nGameType > 0 && newgame)
{ {
// todo // todo
gBlueFlagDropped = false; gBlueFlagDropped = false;
gRedFlagDropped = false; gRedFlagDropped = false;
} }
#endif #endif
if (gGameOptions.uGameFlags & GF_AdvanceLevel) if (!newgame)
{ {
for (int i = connecthead; i >= 0; i = connectpoint2[i]) for (int i = connecthead; i >= 0; i = connectpoint2[i])
{ {
@ -180,13 +181,13 @@ void StartLevel(MapRecord* level)
evInit(); evInit();
for (int i = connecthead; i >= 0; i = connectpoint2[i]) for (int i = connecthead; i >= 0; i = connectpoint2[i])
{ {
if (!(gGameOptions.uGameFlags & GF_AdvanceLevel)) if (newgame)
{ {
playerInit(i, 0); playerInit(i, 0);
} }
playerStart(i, 1); playerStart(i, 1);
} }
if (gGameOptions.uGameFlags & GF_AdvanceLevel) if (!newgame)
{ {
for (int i = connecthead; i >= 0; i = connectpoint2[i]) for (int i = connecthead; i >= 0; i = connectpoint2[i])
{ {
@ -203,7 +204,6 @@ void StartLevel(MapRecord* level)
pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon; pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon;
} }
} }
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
PreloadCache(); PreloadCache();
InitMirrors(); InitMirrors();
trInit(); 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; if (skill != -1) gGameOptions.nDifficulty = skill;
gSkill = gGameOptions.nDifficulty; gSkill = gGameOptions.nDifficulty;
StartLevel(sng); StartLevel(sng, newgame);
gameaction = ga_level; 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);
} }
void GameInterface::NewGame(MapRecord *sng, int skill, bool) void GameInterface::NewGame(MapRecord *sng, int skill, bool)
{ {
gGameOptions.uGameFlags = 0; gGameOptions.uGameFlags = 0;
cheatReset(); cheatReset();
NewLevel(sng, skill); NewLevel(sng, skill, true);
} }
void GameInterface::NextLevel(MapRecord *map, int skill) void GameInterface::NextLevel(MapRecord *map, int skill)
{ {
gGameOptions.uGameFlags = GF_AdvanceLevel; NewLevel(map, skill, false);
NewLevel(map, skill);
} }
void GameInterface::Ticker() void GameInterface::Ticker()
@ -328,40 +309,12 @@ void GameInterface::Ticker()
team_ticker[i] = 0; team_ticker[i] = 0;
} }
if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0) if (gGameOptions.uGameFlags & GF_AdvanceLevel)
{ {
gGameOptions.uGameFlags &= ~GF_AdvanceLevel;
seqKillAll(); seqKillAll();
if (gGameOptions.uGameFlags & GF_EndGame) STAT_Update(gNextLevel == nullptr);
{ CompleteLevel(gNextLevel);
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);
}
} }
r_NoInterpolate = false; r_NoInterpolate = false;
} }
@ -471,11 +424,12 @@ void GameInterface::app_init()
levelLoadDefaults(); levelLoadDefaults();
LoadDefinitions(); LoadDefinitions();
//---------
SetTileNames(); SetTileNames();
C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25); C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25);
TileFiles.SetBackup(); TileFiles.SetBackup();
powerupInit();
Printf(PRINT_NONOTIFY, "Loading cosine table\n"); Printf(PRINT_NONOTIFY, "Loading cosine table\n");
trigInit(); trigInit();
Printf(PRINT_NONOTIFY, "Initializing view subsystem\n"); Printf(PRINT_NONOTIFY, "Initializing view subsystem\n");
@ -485,15 +439,15 @@ void GameInterface::app_init()
Printf(PRINT_NONOTIFY, "Initializing weapon animations\n"); Printf(PRINT_NONOTIFY, "Initializing weapon animations\n");
WeaponInit(); WeaponInit();
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
sndInit();
myconnectindex = connecthead = 0; myconnectindex = connecthead = 0;
gNetPlayers = numplayers = 1; gNetPlayers = numplayers = 1;
connectpoint2[0] = -1; connectpoint2[0] = -1;
gGameOptions.nGameType = 0; gGameOptions.nGameType = 0;
UpdateNetworkMenus(); UpdateNetworkMenus();
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
sndInit();
gChoke.init(518, chokeCallback); gChoke.init(518, chokeCallback);
UpdateDacs(0, true); UpdateDacs(0, true);
@ -518,17 +472,7 @@ static void gameInit()
void GameInterface::Startup() void GameInterface::Startup()
{ {
gameInit(); gameInit();
if (userConfig.CommandMap.IsNotEmpty()) PlayLogos(ga_mainmenu, ga_mainmenu, true);
{
}
else
{
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
else
{
gameaction = ga_mainmenu;
}
}
} }
@ -578,4 +522,48 @@ ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize)
return new GameInterface; 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 END_BLD_NS

View file

@ -77,7 +77,6 @@ extern int blood_globalflags;
void QuitGame(void); void QuitGame(void);
void PreloadCache(void); void PreloadCache(void);
void StartLevel(MapRecord *gameOptions);
void ProcessFrame(void); void ProcessFrame(void);
void ScanINIFiles(void); void ScanINIFiles(void);
void EndLevel(); void EndLevel();
@ -121,7 +120,6 @@ struct GameInterface : ::GameInterface
void MenuOpened() override; void MenuOpened() override;
void MenuClosed() override; void MenuClosed() override;
bool CanSave() override; bool CanSave() override;
bool StartGame(FNewGameStartup& gs) override;
void QuitToTitle() override; void QuitToTitle() override;
FString GetCoordString() override; FString GetCoordString() override;
ReservedSpace GetReservedScreenSpace(int viewsize) override; ReservedSpace GetReservedScreenSpace(int viewsize) override;

View file

@ -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<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
jobs[job++] = { Create<DImageScreen>(2050) };
}
if (fileSystem.FindFile("gti.smk") != -1)
{
jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) };
}
else
{
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
jobs[job++] = { Create<DImageScreen>(2052) };
}
}
jobs[job++] = { Create<DBlackScreen>(1), []() { sndPlaySpecialMusicOrNothing(MUS_INTRO); sndStartSample("THUNDER2", 128, -1); }};
jobs[job++] = { Create<DImageScreen>(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

View file

@ -159,23 +159,6 @@ bool GameInterface::CanSave()
return (gamestate == GS_LEVEL && gPlayer[myconnectindex].pXSprite->health != 0); 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() FSavegameInfo GameInterface::GetSaveSig()
{ {
return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD }; return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD };

View file

@ -36,152 +36,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_BLD_NS 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) void GameInterface::LevelCompleted(MapRecord *map, int skill)
{ {
JobDesc job = { Create<DBloodSummaryScreen>() }; EndLevel();
sndStartSample(268, 128, -1, false, CHANF_UI);
Mus_Stop(); 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(); soundEngine->StopAllChannels();
gameaction = ga_nextlevel; gameaction = map? ga_nextlevel : ga_creditsmenu;
}); });
} }
@ -272,38 +146,4 @@ void SerializeGameStats(FSerializer& arc)
CSecretMgr gSecretMgr; CSecretMgr gSecretMgr;
CKillMgr gKillMgr; 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<DBloodLoadScreen>(caption, rec) };
RunScreenJob(&job, 1, func);
}
END_BLD_NS END_BLD_NS

View file

@ -38,11 +38,8 @@ GAMEOPTIONS gSingleGameOptions = {
0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200 0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200
}; };
EPISODEINFO gEpisodeInfo[kMaxEpisodes+1];
int gSkill = 2; int gSkill = 2;
int gEpisodeCount; MapRecord* gNextLevel;
int gNextLevel; // fixme: let this contain a full level number.
char BloodIniFile[BMAX_PATH] = "BLOOD.INI"; char BloodIniFile[BMAX_PATH] = "BLOOD.INI";
bool bINIOverride = false; bool bINIOverride = false;
@ -94,20 +91,21 @@ 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]; char buffer[16];
pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName)); 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->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid");
pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1); pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1);
pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1); *nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0);
pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1); *nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0);
pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0); pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0);
pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0); pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0);
for (int i = 0; i < kMaxMessages; i++) for (int i = 0; i < kMaxMessages; i++)
{ {
sprintf(buffer, "Message%d", i+1); sprintf(buffer, "Message%d", i + 1);
auto msg = pIni->GetKeyString(pzSection, buffer, ""); auto msg = pIni->GetKeyString(pzSection, buffer, "");
pLevelInfo->AddMessage(i, msg); pLevelInfo->AddMessage(i, msg);
} }
@ -142,108 +140,101 @@ static const char* DefFile(void)
return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini"; 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) void levelLoadDefaults(void)
{ {
char buffer[64]; char buffer[64];
char buffer2[16]; char buffer2[16];
int cutALevel = 0;
levelInitINI(DefFile()); levelInitINI(DefFile());
memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo));
quoteMgr.InitializeQuote(MUS_INTRO, "PESTIS.MID");
int i; 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)) if (!BloodINI->SectionExists(buffer))
break; break;
EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i]; auto cluster = MustFindCluster(i);
auto volume = MustFindVolume(i);
CutsceneDef &csB = cluster->outro;
auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer); auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer);
gVolumeNames[i] = ep_str; // only keep one table for the names. Todo: Consolidate this across games. cluster->name = volume->name = ep_str;
strncpy(pEpisodeInfo->cutsceneAName, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH); if (i > 1) volume->flags |= VF_SHAREWARELOCK;
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;
pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); csB.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneB", ""));
pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0); int soundint = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
if (pEpisodeInfo->cutALevel > 0) if (soundint > 0) csB.soundID = soundint + 0x40000000;
pEpisodeInfo->cutALevel--; else csB.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", ""));
int j;
for (j = 0; j < kMaxLevels; j++) //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)) if (!BloodINI->KeyExists(buffer, buffer2))
break; break;
auto pLevelInfo = AllocateMap(); auto pLevelInfo = AllocateMap();
const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL); const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL);
CheckSectionAbend(pMap); CheckSectionAbend(pMap);
pLevelInfo->levelNumber = levelnum(i, j); SetLevelNum(pLevelInfo, makelevelnum(i, j));
pLevelInfo->cluster = i;
pLevelInfo->mapindex = j;
pLevelInfo->labelName = pMap; pLevelInfo->labelName = pMap;
if (j == 1) volume->startmap = pLevelInfo->labelName;
pLevelInfo->fileName.Format("%s.map", pMap); 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; gGameOptions.uGameFlags |= GF_AdvanceLevel;
levelGetNextLevels(&nEndingA, &nEndingB); if (!secret) gNextLevel = FindNextMap(currentLevel);
switch (arg) else gNextLevel = FindNextSecretMap(currentLevel);
{
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;
}
} }
void levelTryPlayMusic() void levelTryPlayMusic()

View file

@ -41,7 +41,6 @@ enum
enum EGameFlag enum EGameFlag
{ {
GF_AdvanceLevel = 1, GF_AdvanceLevel = 1,
GF_EndGame = 2,
// 4 was for playing intro cutscenes but is no longer used. // 4 was for playing intro cutscenes but is no longer used.
GF_PlayCutscene = 8, GF_PlayCutscene = 8,
}; };
@ -67,38 +66,16 @@ struct GAMEOPTIONS {
#pragma pack(pop) #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 gSingleGameOptions;
extern GAMEOPTIONS gGameOptions; extern GAMEOPTIONS gGameOptions;
extern int gSkill; extern int gSkill;
extern char BloodIniFile[]; extern char BloodIniFile[];
extern bool bINIOverride; extern bool bINIOverride;
extern int gEpisodeCount; extern MapRecord* gNextLevel;
extern int gNextLevel;
extern bool gGameStarted; extern bool gGameStarted;
void levelInitINI(const char *pzIni); void levelInitINI(const char *pzIni);
void levelOverrideINI(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 levelSetupSecret(int nCount);
void levelTriggerSecret(int nSecret); void levelTriggerSecret(int nSecret);
void CheckSectionAbend(const char *pzSection); void CheckSectionAbend(const char *pzSection);

View file

@ -641,7 +641,6 @@ void SerializeState(FSerializer& arc)
("modern", gModernMap) ("modern", gModernMap)
#endif #endif
("cheating", bPlayerCheated) ("cheating", bPlayerCheated)
("nextlevel", gNextLevel)
("skyhoriz", pSky->horizfrac) ("skyhoriz", pSky->horizfrac)
("skyy", pSky->yoffs) ("skyy", pSky->yoffs)
("scale", pSky->yscale) ("scale", pSky->yscale)

View file

@ -267,10 +267,8 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2)
{ {
if (!nArg1 || !nArg2 || strlen(pzArgs) < 3) if (!nArg1 || !nArg2 || strlen(pzArgs) < 3)
return -1; return -1;
*nArg1 = pzArgs[0] - '0' - 1; *nArg1 = pzArgs[0] - '0';
*nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1; *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0');
*nArg1 = ClipRange(*nArg1, 0, gEpisodeCount-1);
*nArg2 = ClipRange(*nArg2, 0, gEpisodeInfo[*nArg1].nLevels-1);
return 2; return 2;
} }
@ -423,7 +421,7 @@ static bool cheatMario(cheatseq_t* c)
int nEpisode, nLevel; int nEpisode, nLevel;
if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2) if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2)
{ {
auto map = FindMapByLevelNum(levelnum(nEpisode, nLevel)); auto map = FindMapByIndex(nEpisode, nLevel);
if (map) DeferedStartGame(map, -1); if (map) DeferedStartGame(map, -1);
} }
return true; return true;

View file

@ -1,6 +1,9 @@
// names for everything that gets accessed by scripts. // names for everything that gets accessed by scripts.
x(MENUBAR, 2038) x(MENUBAR, 2038)
x(BackTile, 253) x(BackTile, 253)
x(Monolithscreen, 2050)
x(GTIScreen, 2052)
x(TitleScreen, 2518)
x(CrosshairTile, 2319) x(CrosshairTile, 2319)
x(LoadScreen, 2049) x(LoadScreen, 2049)

View file

@ -5214,13 +5214,7 @@ void seqSpawnerOffSameTx(XSPRITE* pXSource) {
void levelEndLevelCustom(int nLevel) { void levelEndLevelCustom(int nLevel) {
gGameOptions.uGameFlags |= GF_AdvanceLevel; gGameOptions.uGameFlags |= GF_AdvanceLevel;
gNextLevel = FindMapByIndex(currentLevel->cluster, nLevel + 1);
if (nLevel >= 16 || nLevel < 0) {
gGameOptions.uGameFlags |= GF_EndGame;
return;
}
gNextLevel = nLevel;
} }
void callbackUniMissileBurst(int nSprite) // 22 void callbackUniMissileBurst(int nSprite) // 22

View file

@ -400,10 +400,6 @@ void powerupClear(PLAYER *pPlayer)
} }
} }
void powerupInit(void)
{
}
int packItemToPowerup(int nPack) int packItemToPowerup(int nPack)
{ {
int nPowerUp = -1; int nPowerUp = -1;

View file

@ -241,7 +241,6 @@ void powerupDeactivate(PLAYER *pPlayer, int nPowerUp);
void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState); void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState);
void powerupProcess(PLAYER *pPlayer); void powerupProcess(PLAYER *pPlayer);
void powerupClear(PLAYER *pPlayer); void powerupClear(PLAYER *pPlayer);
void powerupInit(void);
int packItemToPowerup(int nPack); int packItemToPowerup(int nPack);
int powerupToPackItem(int nPowerUp); int powerupToPackItem(int nPowerUp);
char packAddItem(PLAYER *pPlayer, unsigned int nPack); char packAddItem(PLAYER *pPlayer, unsigned int nPack);

View file

@ -218,7 +218,7 @@ private:
stats.font = SmallFont; stats.font = SmallFont;
stats.letterColor = CR_DARKRED; stats.letterColor = CR_DARKRED;
stats.standardColor = CR_DARKGRAY; stats.standardColor = CR_DARKGRAY;
stats.time = Scale(gFrameCount, 1000, kTicsPerSec); stats.time = gFrameCount / GameTicRate;
if (automapMode == am_full) if (automapMode == am_full)
{ {
@ -248,6 +248,7 @@ private:
stats.secrets = gSecretMgr.Founds; stats.secrets = gSecretMgr.Founds;
stats.supersecrets = gSecretMgr.Super; 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.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); DBaseStatusBar::PrintLevelStats(stats);
} }

File diff suppressed because it is too large Load diff

View file

@ -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<DImageScreen>(TENSCREEN), nullptr };
break;
case 1:
jobs[job++] = { PlayVideo("rr_outro.anm", rr_outro, framespeed), nullptr };
jobs[job++] = { Create<DImageScreen>(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<DRRMultiplayerBonusScreen>(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<DRRLevelSummaryScreen>(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<DRRRAEndOfGame>() };
}
}
else jobs[job++] = { Create<DRRLevelSummaryScreen>(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<DRRLoadScreen>(rec) };
RunScreenJob(&job, 1, func);
}
void PrintPaused_r()
{
BigText(160, 100, GStrings("Game Paused"), 0);
}
END_DUKE_NS END_DUKE_NS

View file

@ -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 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 }; 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; break;
} }
case SE_35: case SE_35:

View file

@ -292,11 +292,11 @@ static bool cheatItems(int player)
static bool cheatLevel(cheatseq_t *s) static bool cheatLevel(cheatseq_t *s)
{ {
int volnume,levnume; int volnume,levnume;
volnume = s->Args[0] - '0' - 1; volnume = s->Args[0] - '0';
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1; 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. // 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) if (map)
{ {
ChangeLevel(map, -1); ChangeLevel(map, -1);

View file

@ -97,19 +97,8 @@ bool GameInterface::CanSave()
bool GameInterface::StartGame(FNewGameStartup& gs) 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; 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_d[] = { JIBBED_ACTOR6, BONUS_SPEECH1, DUKE_GETWEAPON2, JIBBED_ACTOR5, JIBBED_ACTOR5 };
static const short sounds_r[] = { 427, 428, 196, 195, 197 }; 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]; 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(); Net_ClearFifo();
} }
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
if (map)
{
DeferedStartGame(map, gs.Skill);
return true; return true;
}
return false;
} }
FSavegameInfo GameInterface::GetSaveSig() FSavegameInfo GameInterface::GetSaveSig()

View file

@ -105,12 +105,8 @@ void think_r();
void animatesprites_d(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio); 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 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_d();
void InitFonts_r(); void InitFonts_r();
void PrintPaused_d();
void PrintPaused_r();
Dispatcher fi; Dispatcher fi;
@ -120,9 +116,7 @@ void SetDispatcher()
if (!isRR()) if (!isRR())
{ {
fi = { fi = {
Logo_d,
InitFonts_d, InitFonts_d,
PrintPaused_d,
think_d, think_d,
initactorflags_d, initactorflags_d,
@ -167,9 +161,7 @@ void SetDispatcher()
else else
{ {
fi = { fi = {
Logo_r,
InitFonts_r, InitFonts_r,
PrintPaused_r,
think_r, think_r,
initactorflags_r, initactorflags_r,
@ -235,8 +227,6 @@ int TILE_STATIC;
int TILE_BOTTOMSTATUSBAR; int TILE_BOTTOMSTATUSBAR;
int TILE_THREEDEE; int TILE_THREEDEE;
int TILE_INGAMEDUKETHREEDEE; int TILE_INGAMEDUKETHREEDEE;
int TILE_PLUTOPAKSPRITE;
int TILE_MENUBAR;
int TILE_ATOMICHEALTH; int TILE_ATOMICHEALTH;
int TILE_FLOORSLIME; int TILE_FLOORSLIME;
int TILE_JIBS6; int TILE_JIBS6;

View file

@ -76,9 +76,7 @@ struct GameInterface : public ::GameInterface
struct Dispatcher struct Dispatcher
{ {
// global stuff // global stuff
void (*ShowLogo)(const CompletionFunc& completion);
void (*InitFonts)(); void (*InitFonts)();
void (*PrintPaused)();
// sectors_?.cpp // sectors_?.cpp
void (*think)(); void (*think)();

View file

@ -261,10 +261,6 @@ void initactorflags_d()
TILE_CAMLIGHT = CAMLIGHT; TILE_CAMLIGHT = CAMLIGHT;
TILE_STATIC = STATIC; TILE_STATIC = STATIC;
TILE_BOTTOMSTATUSBAR = isWorldTour()? WIDESCREENSTATUSBAR : BOTTOMSTATUSBAR; TILE_BOTTOMSTATUSBAR = isWorldTour()? WIDESCREENSTATUSBAR : BOTTOMSTATUSBAR;
TILE_THREEDEE = THREEDEE;
TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE;
TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE;
TILE_MENUBAR = MENUBAR;
TILE_ATOMICHEALTH = ATOMICHEALTH; TILE_ATOMICHEALTH = ATOMICHEALTH;
TILE_FLOORSLIME = FLOORSLIME; TILE_FLOORSLIME = FLOORSLIME;
TILE_JIBS6 = JIBS6; TILE_JIBS6 = JIBS6;

View file

@ -235,10 +235,6 @@ void initactorflags_r()
TILE_CAMLIGHT = CAMLIGHT; TILE_CAMLIGHT = CAMLIGHT;
TILE_STATIC = STATIC; TILE_STATIC = STATIC;
TILE_BOTTOMSTATUSBAR = BOTTOMSTATUSBAR; TILE_BOTTOMSTATUSBAR = BOTTOMSTATUSBAR;
TILE_THREEDEE = THREEDEE;
TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE;
TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE;
TILE_MENUBAR = MENUBAR;
TILE_ATOMICHEALTH = ATOMICHEALTH; TILE_ATOMICHEALTH = ATOMICHEALTH;
TILE_FLOORSLIME = FLOORSLIME; TILE_FLOORSLIME = FLOORSLIME;
TILE_JIBS6 = JIBS6; TILE_JIBS6 = JIBS6;

View file

@ -162,9 +162,6 @@ int hits(DDukeActor* snum);
DDukeActor* LocateTheLocator(int n, int sectnum); DDukeActor* LocateTheLocator(int n, int sectnum);
void clearcamera(player_struct* ps); void clearcamera(player_struct* ps);
void showtwoscreens(const CompletionFunc& func);
void doorders(const CompletionFunc& func);
void LoadActor(DDukeActor* i, int p, int x); void LoadActor(DDukeActor* i, int p, int x);
void execute(DDukeActor* s, int p, int d); void execute(DDukeActor* s, int p, int d);
void makeitfall(DDukeActor* s); void makeitfall(DDukeActor* s);
@ -211,8 +208,6 @@ void OffBoat(player_struct *pl);
void cameratext(DDukeActor* i); void cameratext(DDukeActor* i);
void dobonus(int bonusonly, const CompletionFunc& completion); 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 drawoverlays(double smoothratio);
void drawbackground(void); void drawbackground(void);
@ -227,7 +222,6 @@ void e4intro(const CompletionFunc& completion);
void exitlevel(MapRecord *next); void exitlevel(MapRecord *next);
void enterlevel(MapRecord* mi, int gm); void enterlevel(MapRecord* mi, int gm);
void donewgame(MapRecord* map, int sk); void donewgame(MapRecord* map, int sk);
void startnewgame(MapRecord* map, int skill);
int playercolor2lookup(int color); int playercolor2lookup(int color);
void PlayerColorChanged(void); void PlayerColorChanged(void);
bool movementBlocked(player_struct *p); bool movementBlocked(player_struct *p);

View file

@ -92,13 +92,12 @@ static void endthegame(bool)
void GameInterface::ExitFromMenu() void GameInterface::ExitFromMenu()
{ {
#if 0
// do we really need this scoreboard stuff here?
auto runbonus = [=](auto completion) auto runbonus = [=](auto completion)
{ {
// MP scoreboard // MP scoreboard
if (playerswhenstarted > 1 && !ud.coop) if (playerswhenstarted > 1 && !ud.coop) ShowScoreboard(playerswhenstarted);
{
dobonus(1, completion);
}
else completion(false); else completion(false);
}; };
@ -106,11 +105,16 @@ void GameInterface::ExitFromMenu()
{ {
// shareware and TEN screens // shareware and TEN screens
if (isShareware() && !isRR()) if (isShareware() && !isRR())
showtwoscreens(completion); StartCutscene("DukeCutscenes.BuildSharewareExit", 0, completion);
else completion(false); else completion(false);
}; };
runbonus([=](bool aborted) { runtwoscreens(endthegame); }); 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) 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) int startrts(int lumpNum, int localPlayer)
{ {
if (SoundEnabled() && if (SoundEnabled() &&

View file

@ -48,11 +48,12 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
#include "conlabeldef.h" #include "conlabeldef.h"
#include "gi.h" #include "gi.h"
extern TArray<TPointer<MapRecord>> mapList;
BEGIN_DUKE_NS BEGIN_DUKE_NS
enum { VERSIONCHECK = 41 }; enum { VERSIONCHECK = 41 };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// definitions needed by the parser. // definitions needed by the parser.
@ -88,7 +89,8 @@ public:
struct TempMusic struct TempMusic
{ {
int levnum; int volnum;
int levlnum;
FString music; FString music;
}; };
@ -1016,7 +1018,8 @@ int ConCompiler::parsecommand()
if (k >= 0) if (k >= 0)
{ {
tempMusic.Reserve(1); tempMusic.Reserve(1);
tempMusic.Last().levnum = levelnum(k, i); tempMusic.Last().volnum = k + 1;
tempMusic.Last().levlnum = i + 1;
tempMusic.Last().music = parsebuffer.Data(); tempMusic.Last().music = parsebuffer.Data();
} }
else else
@ -1644,6 +1647,7 @@ int ConCompiler::parsecommand()
return 0; return 0;
case concmd_definevolumename: case concmd_definevolumename:
{
popscriptvalue(); popscriptvalue();
transnum(LABEL_DEFINE); transnum(LABEL_DEFINE);
j = popscriptvalue(); j = popscriptvalue();
@ -1658,8 +1662,13 @@ int ConCompiler::parsecommand()
textptr++, i++; textptr++, i++;
} }
parsebuffer.Push(0); 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; return 0;
}
case concmd_defineskillname: case concmd_defineskillname:
popscriptvalue(); popscriptvalue();
transnum(LABEL_DEFINE); transnum(LABEL_DEFINE);
@ -1695,25 +1704,32 @@ int ConCompiler::parsecommand()
textptr++, i++; textptr++, i++;
} }
parsebuffer.Push(0); parsebuffer.Push(0);
auto levnum = levelnum(j, k); auto map = FindMapByIndexOnly(j + 1, k + 1);
auto map = FindMapByLevelNum(levnum);
if (!map) map = AllocateMap(); if (!map) map = AllocateMap();
map->SetFileName(parsebuffer.Data()); map->SetFileName(parsebuffer.Data());
if (k == 0)
{
auto vol = MustFindVolume(j);
vol->startmap = map->labelName;
}
while (*textptr == ' ' || *textptr == '\t') textptr++; while (*textptr == ' ' || *textptr == '\t') textptr++;
map->parTime = map->parTime =
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) + (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26); (((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
textptr += 5; textptr += 5;
while (*textptr == ' ' || *textptr == '\t') textptr++; while (*textptr == ' ' || *textptr == '\t') textptr++;
map->designerTime = map->designerTime =
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) + (((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26); (((*(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; textptr += 5;
while (*textptr == ' ' || *textptr == '\t') textptr++; while (*textptr == ' ' || *textptr == '\t') textptr++;
@ -3132,7 +3148,7 @@ void ConCompiler::setmusic()
{ {
for (auto& tm : tempMusic) for (auto& tm : tempMusic)
{ {
auto map = FindMapByLevelNum(tm.levnum); auto map = FindMapByIndexOnly(tm.volnum, tm.levlnum);
if (map) map->music = tm.music; if (map) map->music = tm.music;
} }
tempMusic.Clear(); tempMusic.Clear();
@ -3176,6 +3192,12 @@ void loadcons()
ScriptCode.Push(0); ScriptCode.Push(0);
ConCompiler comp; ConCompiler comp;
if (fileSystem.FileExists("engine/engine.con"))
{
comp.compilecon("engine/engine.con");
}
comp.compilecon(ConFile()); //Tokenize comp.compilecon(ConFile()); //Tokenize
if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get()) if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get())
@ -3206,45 +3228,38 @@ void loadcons()
InitGameVarPointers(); InitGameVarPointers();
ResetSystemDefaults(); ResetSystemDefaults();
S_WorldTourMappingsForOldSounds(); // create a sound mapping for World Tour. 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()) 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 num = fileSystem.CheckNumForName("e1l7.map");
int file = fileSystem.GetFileContainer(num); int file = fileSystem.GetFileContainer(num);
if (file <= fileSystem.GetMaxIwadNum()) if (file <= fileSystem.GetMaxIwadNum())
{ {
auto maprec = FindMapByName("e1l7"); 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 END_DUKE_NS

View file

@ -130,11 +130,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer); if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer);
break; break;
case USERDEFS_FROM_BONUS:
if (bSet) ud.from_bonus = lValue;
else SetGameVarID(lVar2, ud.from_bonus, sActor, sPlayer);
break;
case USERDEFS_CAMERASPRITE: case USERDEFS_CAMERASPRITE:
if (bSet) ud.cameraactor = ScriptIndexToActor(lValue); if (bSet) ud.cameraactor = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ud.cameraactor), sActor, sPlayer); 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); else SetGameVarID(lVar2, ud.player_skill, sActor, sPlayer);
break; 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: case USERDEFS_MARKER:
if (bSet) ud.marker = lValue; if (bSet) ud.marker = lValue;
else SetGameVarID(lVar2, ud.marker, sActor, sPlayer); else SetGameVarID(lVar2, ud.marker, sActor, sPlayer);
@ -3455,7 +3442,7 @@ int ParseState::parse(void)
insptr++; // skip command insptr++; // skip command
volnume = GetGameVarID(*insptr++, g_ac, g_p); volnume = GetGameVarID(*insptr++, g_ac, g_p);
levnume = 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) if (level != nullptr)
ChangeLevel(level, -1); ChangeLevel(level, -1);
break; break;
@ -3572,7 +3559,7 @@ int ParseState::parse(void)
{ {
insptr++; insptr++;
int music_select = *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); if (level) S_PlayLevelMusic(level);
break; break;
} }

View file

@ -114,28 +114,7 @@ void GameInterface::Ticker()
void GameInterface::Startup() void GameInterface::Startup()
{ {
ps[myconnectindex].ftq = 0; ps[myconnectindex].ftq = 0;
PlayLogos(ga_mainmenunostopsound, ga_mainmenunostopsound, false);
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;
}
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -160,38 +139,10 @@ void GameInterface::Render()
// //
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void loadscreen_d(MapRecord* rec, CompletionFunc func);
void loadscreen_r(MapRecord* rec, CompletionFunc func);
void GameInterface::NextLevel(MapRecord* map, int skill) 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); enterlevel(map, 0);
gameaction = ga_level;
});
#endif
enterlevel(map, 0);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
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);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View file

@ -555,9 +555,9 @@ void InitGameVarPointers(void)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// These are deliberately not stored in accessible variables anymore // These are deliberately not stored in accessible variables anymore. Use is deprecated.
int getmap() { return mapfromlevelnum(currentLevel->levelNumber); } int getmap() { return currentLevel->levelNumber; }
int getvol() { return volfromlevelnum(currentLevel->levelNumber); } int getvol() { return currentLevel->cluster; }
void AddSystemVars() void AddSystemVars()
{ {

View file

@ -164,7 +164,7 @@ inline float PlayerInputAngVel(int pl)
return ps[pl].sync.avel; return ps[pl].sync.avel;
} }
inline float PlayerHorizon(int pl) inline float GetPlayerHorizon(int pl)
{ {
return ps[pl].sync.horz; return ps[pl].sync.horz;
} }

View file

@ -517,6 +517,7 @@ x(THREEDEE, 2498)
x(INGAMEDUKETHREEDEE, 2499) x(INGAMEDUKETHREEDEE, 2499)
x(TENSCREEN, 2500) x(TENSCREEN, 2500)
x(PLUTOPAKSPRITE, 2501) x(PLUTOPAKSPRITE, 2501)
x(TITLEPLUTOPAKSPRITE, 2502)
x(MENUPLUTOPAKSPRITE, 2503) x(MENUPLUTOPAKSPRITE, 2503)
x(CREDITPAGE1, 2504) x(CREDITPAGE1, 2504)
x(CREDITPAGE2, 2505) x(CREDITPAGE2, 2505)
@ -587,11 +588,33 @@ x(RESPAWNMARKERRED, 3190)
x(RESPAWNMARKERYELLOW, 3200) x(RESPAWNMARKERYELLOW, 3200)
x(RESPAWNMARKERGREEN, 3210) x(RESPAWNMARKERGREEN, 3210)
x(BONUSSCREEN, 3240) 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(VIEWBORDER, 3250)
x(VICTORY1, 3260) 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(ORDERING, 3270)
x(ORDERING1, 3271)
x(ORDERING2, 3272)
x(ORDERING3, 3273)
x(TEXTSTORY, 3280) x(TEXTSTORY, 3280)
x(LOADSCREEN, 3281) x(LOADSCREEN, 3281)
x(SWEXIT2, 3290)
x(SWEXIT1, 3291)
x(E1ENDSCREEN, 3292) x(E1ENDSCREEN, 3292)
x(E2ENDSCREEN, 3293) x(E2ENDSCREEN, 3293)
x(BORNTOBEWILDSCREEN, 3370) x(BORNTOBEWILDSCREEN, 3370)

View file

@ -1241,6 +1241,7 @@ y(RRTILE8640, 8640)
y(RRTILE8651, 8651) y(RRTILE8651, 8651)
y(RRTILE8660, 8660) y(RRTILE8660, 8660)
x(ENDGAME, 8677) x(ENDGAME, 8677)
x(ENDGAME2, 8678)
y(RRTILE8679, 8679) y(RRTILE8679, 8679)
y(RRTILE8680, 8680) y(RRTILE8680, 8680)
y(RRTILE8681, 8681) y(RRTILE8681, 8681)

View file

@ -22,10 +22,6 @@ extern int TILE_CAMCORNER;
extern int TILE_CAMLIGHT; extern int TILE_CAMLIGHT;
extern int TILE_STATIC; extern int TILE_STATIC;
extern int TILE_BOTTOMSTATUSBAR; 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_ATOMICHEALTH;
extern int TILE_FLOORSLIME; extern int TILE_FLOORSLIME;
extern int TILE_JIBS6; extern int TILE_JIBS6;

View file

@ -2630,27 +2630,6 @@ static void processweapon(int snum, ESyncBits actions, int psect)
auto s = pact->s; auto s = pact->s;
int shrunk = (s->yrepeat < 32); 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 (isNamWW2GI() && (actions & SB_HOLSTER)) // 'Holster Weapon
{ {
if (isWW2GI()) if (isWW2GI())
@ -3138,7 +3117,7 @@ HORIZONLY:
if (SyncInput()) if (SyncInput())
{ {
p->horizon.applyinput(PlayerHorizon(snum), &actions); p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
} }
p->checkhardlanding(); p->checkhardlanding();

View file

@ -1464,26 +1464,8 @@ int doincrements_r(struct player_struct* p)
{ {
if (!wupass) if (!wupass)
{ {
short snd; int snd = currentLevel->rr_startsound ? currentLevel->rr_startsound : 391;
wupass = 1; 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); S_PlayActorSound(snd, pact);
} }
else if (PlayClock > 1024) else if (PlayClock > 1024)
@ -3399,8 +3381,7 @@ void processinput_r(int snum)
psectlotag = 2; psectlotag = 2;
} }
} }
else if (psectlotag == 7777) else if (psectlotag == 7777 && (currentLevel->gameflags & LEVEL_RR_HULKSPAWN))
if (currentLevel->levelNumber == levelnum(1, 6))
lastlevel = 1; lastlevel = 1;
if (psectlotag == 848 && sector[psect].floorpicnum == WATERTILE2) if (psectlotag == 848 && sector[psect].floorpicnum == WATERTILE2)
@ -4000,7 +3981,7 @@ HORIZONLY:
if (SyncInput()) if (SyncInput())
{ {
p->horizon.applyinput(PlayerHorizon(snum), &actions); p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
} }
p->checkhardlanding(); p->checkhardlanding();

View file

@ -36,6 +36,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
#include "automap.h" #include "automap.h"
#include "dukeactor.h" #include "dukeactor.h"
#include "interpolate.h" #include "interpolate.h"
#include "precache.h"
#include "render.h" #include "render.h"
BEGIN_DUKE_NS BEGIN_DUKE_NS
@ -141,7 +142,7 @@ void resetplayerstats(int snum)
p->jetpack_on = 0; p->jetpack_on = 0;
p->holoduke_on = nullptr; 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->angle.orotscrnang = p->angle.rotscrnang = buildang(0);
p->newOwner =nullptr; p->newOwner =nullptr;
@ -659,7 +660,6 @@ void prelevel_common(int g)
p->SlotWin = 0; p->SlotWin = 0;
enemysizecheat = 0; enemysizecheat = 0;
p->MamaEnd = 0; p->MamaEnd = 0;
mamaspawn_count = 15;
banjosound = 0; banjosound = 0;
RRRA_ExitedLevel = 0; RRRA_ExitedLevel = 0;
@ -672,7 +672,7 @@ void prelevel_common(int g)
WindDir = 0; WindDir = 0;
fakebubba_spawn = 0; fakebubba_spawn = 0;
RRRA_ExitedLevel = 0; RRRA_ExitedLevel = 0;
mamaspawn_count = 15; mamaspawn_count = currentLevel->rr_mamaspawn;
BellTime = 0; BellTime = 0;
BellSprite = nullptr; BellSprite = nullptr;
@ -754,7 +754,6 @@ void donewgame(MapRecord* map, int sk)
auto p = &ps[0]; auto p = &ps[0];
show_shareware = 26 * 34; show_shareware = 26 * 34;
//ud.nextLevel = map;
ud.player_skill = sk; ud.player_skill = sk;
ud.secretlevel = 0; ud.secretlevel = 0;
ud.from_bonus = 0; ud.from_bonus = 0;
@ -813,33 +812,6 @@ void donewgame(MapRecord* map, int sk)
} }
} }
template<class func>
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. // 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); SECRET_SetMapName(mi->DisplayName(), mi->name);
STAT_NewLevel(mi->fileName); 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); p->angle.ang = buildang(lbang);
memset(gotpic, 0, sizeof(gotpic)); memset(gotpic, 0, sizeof(gotpic));
@ -993,16 +959,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
SpawnPortals(); 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(); allignwarpelevators();
resetpspritevars(gamemode); resetpspritevars(gamemode);
@ -1047,7 +1003,6 @@ void enterlevel(MapRecord *mi, int gamemode)
OnEvent(EVENT_ENTERLEVEL); OnEvent(EVENT_ENTERLEVEL);
// Stop all sounds // Stop all sounds
S_ResumeSound(false);
FX_StopAllSounds(); FX_StopAllSounds();
FX_SetReverb(0); FX_SetReverb(0);
@ -1064,27 +1019,32 @@ void enterlevel(MapRecord *mi, int gamemode)
S_PlayLevelMusic(mi); 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]) for (int i = connecthead; i >= 0; i = connectpoint2[i])
{ {
bool clearweapon = !!(currentLevel->flags & LEVEL_CLEARWEAPONS);
int pn = sector[ps[i].GetActor()->s->sectnum].floorpicnum; int pn = sector[ps[i].GetActor()->s->sectnum].floorpicnum;
if (pn == TILE_HURTRAIL || pn == TILE_FLOORSLIME || pn == TILE_FLOORPLASMA) if (pn == TILE_HURTRAIL || pn == TILE_FLOORSLIME || pn == TILE_FLOORPLASMA)
{ {
resetweapons(i);
resetinventory(i); resetinventory(i);
clearweapon = true;
}
if (clearweapon)
{
resetweapons(i);
ps[i].gotweapon.Clear(PISTOL_WEAPON); ps[i].gotweapon.Clear(PISTOL_WEAPON);
ps[i].ammo_amount[PISTOL_WEAPON] = 0; ps[i].ammo_amount[PISTOL_WEAPON] = 0;
ps[i].curr_weapon = KNEE_WEAPON; ps[i].curr_weapon = KNEE_WEAPON;
ps[i].kickback_pic = 0;
ps[i].okickback_pic = ps[i].kickback_pic = 0; ps[i].okickback_pic = ps[i].kickback_pic = 0;
} }
if (currentLevel->flags & LEVEL_CLEARINVENTORY) resetinventory(i);
} }
resetmys(); resetmys();
everyothertime = 0; everyothertime = 0;
global_random = 0; global_random = 0;
ud.last_level = currentLevel->levelNumber; ud.last_level = 1;
ps[myconnectindex].over_shoulder_on = 0; ps[myconnectindex].over_shoulder_on = 0;
clearfrags(); clearfrags();
resettimevars(); // Here we go 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; if (skill == -1) skill = ud.player_skill;
else skill++;
ud.player_skill = skill; ud.player_skill = skill;
ud.m_respawn_monsters = (skill == 4); ud.m_respawn_monsters = (skill == 4);
ud.m_monsters_off = ud.monsters_off = 0; ud.m_monsters_off = ud.monsters_off = 0;
@ -1115,13 +1085,13 @@ void startnewgame(MapRecord* map, int skill)
ud.m_respawn_inventory = 0; ud.m_respawn_inventory = 0;
ud.multimode = 1; ud.multimode = 1;
newgame(map, skill, [=](bool) donewgame(map, skill);
{
enterlevel(map, 0); enterlevel(map, 0);
if (isShareware() && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
PlayerColorChanged(); PlayerColorChanged();
inputState.ClearAllInput(); inputState.ClearAllInput();
gameaction = ga_level; gameaction = ga_level;
});
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -1132,25 +1102,27 @@ void startnewgame(MapRecord* map, int skill)
bool setnextmap(bool checksecretexit) bool setnextmap(bool checksecretexit)
{ {
MapRecord* map = nullptr;; MapRecord* map = nullptr;
int from_bonus = 0; MapRecord* from_bonus = nullptr;
if (ud.eog) if (ud.eog && !(currentLevel->flags & LEVEL_FORCENOEOG))
{ {
} }
else if (checksecretexit && ud.from_bonus == 0) else if (checksecretexit && ud.from_bonus == 0)
{ {
if (ud.secretlevel > 0) if (ud.secretlevel > 0)
{ {
int newlevnum = levelnum(volfromlevelnum(currentLevel->levelNumber), ud.secretlevel-1); // allow overriding the secret exit destination to make episode compilation easier with maps containing secret exits.
map = FindMapByLevelNum(newlevnum); if (currentLevel->flags & LEVEL_SECRETEXITOVERRIDE) map = FindNextSecretMap(currentLevel);
if (!map) map = FindMapByIndex(currentLevel->cluster, ud.secretlevel);
if (map) 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); map = FindMapByLevelNum(ud.from_bonus);
} }
@ -1169,7 +1141,7 @@ bool setnextmap(bool checksecretexit)
{ {
I_Error("Trying to open non-existent %s", map->fileName.GetChars()); 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); CompleteLevel(map);
return false; return false;
@ -1181,29 +1153,32 @@ bool setnextmap(bool checksecretexit)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void exitlevel(MapRecord *nextlevel) void exitlevel(MapRecord* nextlevel)
{ {
bool endofgame = nextlevel == nullptr; bool endofgame = nextlevel == nullptr;
STAT_Update(endofgame); STAT_Update(endofgame);
StopCommentary(); 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. // Clear potentially loaded per-map ART only after the bonus screens.
artClearMapArt(); artClearMapArt();
gameaction = ga_level; gameaction = ga_level;
ud.eog = false; ud.eog = false;
if (endofgame) if (endofgame)
{
if (ud.multimode < 2)
{
if (isShareware())
doorders([](bool) { gameaction = ga_startup; });
else gameaction = ga_startup;
return;
}
else
{ {
auto nextlevel = FindMapByLevelNum(0); auto nextlevel = FindMapByLevelNum(0);
if (!nextlevel) if (!nextlevel)
@ -1213,11 +1188,22 @@ void exitlevel(MapRecord *nextlevel)
} }
else gameaction = ga_nextlevel; else gameaction = ga_nextlevel;
} }
}
else else
gameaction = ga_nextlevel; 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;
});
}
} }

View file

@ -232,6 +232,7 @@ static void cachegoodsprites(void)
void cacheit_d(void) void cacheit_d(void)
{ {
if (!r_precache) return;
int i; int i;
cachegoodsprites(); cachegoodsprites();

View file

@ -366,37 +366,6 @@ static void cachegoodsprites(void)
for (i = SMALLSMOKE; i < (SMALLSMOKE + 4); i++) for (i = SMALLSMOKE; i < (SMALLSMOKE + 4); i++)
tloadtile(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) void cacheit_r(void)
{ {
if (!r_precache) return;
int i; int i;
cachegoodsprites(); cachegoodsprites();
@ -461,11 +431,11 @@ void prelevel_r(int g)
prelevel_common(g); prelevel_common(g);
p = &ps[screenpeek]; p = &ps[screenpeek];
if (currentLevel->gameflags & LEVEL_RR_CLEARMOONSHINE)
ps[myconnectindex].steroids_amount = 0;
if (isRRRA()) if (isRRRA())
{ {
if (currentLevel->levelNumber == levelnum(1, 4))
ps[myconnectindex].steroids_amount = 0;
for (j = 0; j < MAXSPRITES; j++) for (j = 0; j < MAXSPRITES; j++)
{ {

View file

@ -46,6 +46,7 @@ source as it is released.
#include "gamestate.h" #include "gamestate.h"
#include "names_d.h" #include "names_d.h"
#include "i_music.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_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 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; 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 END_DUKE_NS

View file

@ -48,7 +48,6 @@ void S_MenuSound(void);
void S_StopSound(int sndNum, DDukeActor* spr = nullptr, int flags = -1); void S_StopSound(int sndNum, DDukeActor* spr = nullptr, int flags = -1);
int S_CheckSoundPlaying(int soundNum); 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_CheckActorSoundPlaying(DDukeActor* spriteNum, int soundNum, int channel = 0);
int S_CheckAnyActorSoundPlaying(DDukeActor* spriteNum); int S_CheckAnyActorSoundPlaying(DDukeActor* spriteNum);
@ -64,7 +63,6 @@ inline bool S_IsSoundValid(int num)
void S_PlayRRMusic(int newTrack = -1); void S_PlayRRMusic(int newTrack = -1);
void S_PlayBonusMusic(); void S_PlayBonusMusic();
void S_PlayLevelMusic(MapRecord* mi); void S_PlayLevelMusic(MapRecord* mi);
void S_PlaySpecialMusic(unsigned int);
void S_ContinueLevelMusic(void); void S_ContinueLevelMusic(void);
// Placeholders. // Placeholders.

View file

@ -143,7 +143,6 @@ struct user_defs
int m_respawn_items, m_respawn_monsters, m_respawn_inventory, m_recstat, m_monsters_off; int m_respawn_items, m_respawn_monsters, m_respawn_inventory, m_recstat, m_monsters_off;
int m_ffire, ffire, m_player_skill, multimode; int m_ffire, ffire, m_player_skill, multimode;
int player_skill, marker; int player_skill, marker;
//MapRecord* nextLevel;
DDukeActor* cameraactor; DDukeActor* cameraactor;

View file

@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gstrings.h" #include "gstrings.h"
#include "gamefuncs.h" #include "gamefuncs.h"
#include "c_bind.h" #include "c_bind.h"
#include "vm.h"
#include <string> #include <string>
@ -47,8 +48,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_PS_NS BEGIN_PS_NS
int selectedlevelnew;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// //
@ -373,471 +372,26 @@ void menu_DoPlasma()
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150); 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(); menu_DoPlasma();
return 0;
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<DImageScreen>(tileGetTexture(PublisherLogo()), DScreenJob::fadein | DScreenJob::fadeout) };
jobs[job++] = { Create<DLobotomyScreen>(tileGetTexture(seq_GetSeqPicnum(kSeqScreens, 0, 0)), DScreenJob::fadein | DScreenJob::fadeout) };
jobs[job++] = { PlayMovie("book.mov") };
jobs[job++] = { Create<DMainTitle>() };
RunScreenJob(jobs, job, completion, true, true);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// 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 }; void TextOverlay::Create(const FString& text, int pal)
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
{ {
int i; lastclock = 0;
int x = 0; FString ttext = GStrings(text);
int delta = 0; screentext = ttext.Split("\n");
int nIdleSeconds = 0; ComputeCinemaText();
}
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<JobDesc> &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<DMapScreen>(nLevel-1, nLevelNew-1, nLevelBest-1) });
}
//---------------------------------------------------------------------------
//
// text overlay
//
//---------------------------------------------------------------------------
void TextOverlay::Start(double starttime) void TextOverlay::Start(double starttime)
{ {
@ -857,10 +411,9 @@ void TextOverlay::ComputeCinemaText()
nHeight = screentext.Size() * 10; nHeight = screentext.Size() * 10;
} }
void TextOverlay::ReadyCinemaText(uint16_t nVal) void TextOverlay::ReadyCinemaText(const char* nVal)
{ {
FStringf label("TXT_EX_CINEMA%d", nVal); FString label = nVal[0] == '$'? GStrings(nVal +1) : nVal;
label = GStrings(label);
screentext = label.Split("\n"); screentext = label.Split("\n");
ComputeCinemaText(); 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[] = { static const char * const cinpalfname[] = {
"3454.pal", "3454.pal",
"3452.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; TileFiles.tileMakeWritable(kTileLoboLaptop);
short cinematile; auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
int currentCinemaPalette; if (tex) tex->Reload();
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<FString> screentext;
public:
DLastLevelCinema() : DScreenJob(fadein | fadeout) {}
private:
void DoStatic(int a, int b)
{
auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop); auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop);
int v2 = 160 - a / 2; int y = 160 - a / 2;
int v4 = 81 - b / 2; int left = 81 - b / 2;
int var_18 = v2 + a; int bottom = y + a;
int v5 = v4 + b; int right = left + b;
auto pTile = (pixels + (200 * v2)) + v4; auto pTile = (pixels + (200 * y)) + left;
TileFiles.InvalidateTile(kTileLoboLaptop); TileFiles.InvalidateTile(kTileLoboLaptop);
while (v2 < var_18) for(;y < bottom; y++)
{ {
uint8_t* pStart = pTile; uint8_t* pixel = pTile;
pTile += 200; pTile += 200;
int v7 = v4; for (int x = left; x < right; x++)
while (v7 < v5)
{ {
*pStart = RandomBit() * 16; *pixel++ = RandomBit() * 16;
v7++;
pStart++;
}
v2++;
} }
} }
return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
}
void Phase1() static int UndoStatic()
{ {
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.
auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
if (tex) tex->Reload(); if (tex) tex->Reload();
TileFiles.InvalidateTile(kTileLoboLaptop); TileFiles.InvalidateTile(kTileLoboLaptop);
return true; return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
}
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<FString> credits;
TArray<FString> 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<DCinema>(CINEMA_LOSE_SCENE) };
}
else
{
StopCD();
PlayGameOverSound();
job = { Create<DImageScreen>(tileGetTexture(kTile3591), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff, TRANSLATION(Translation_BasePalettes, 16)) };
}
RunScreenJob(&job, 1, [](bool) { gameaction = ga_mainmenu; });
} }
DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, DoStatic, DoStatic)
void DoAfterCinemaScene(int nLevel, TArray<JobDesc>& jobs)
{ {
int scene = -1; PARAM_PROLOGUE;
if (nLevel == 10) scene = CINEMA_AFTER_LEVEL_10; PARAM_INT(x);
if (nLevel == 15) scene = CINEMA_AFTER_LEVEL_15; PARAM_INT(y);
if (nLevel == 20) scene = CINEMA_AFTER_LEVEL_20; ACTION_RETURN_INT(DoStatic(x, y));
if (scene > 0) jobs.Push({ Create<DCinema>(scene) });
if (nLevel == 19) { jobs.Push({ Create<DLastLevelCinema>() }); selectedlevelnew = 20; }
if (nLevel == 20) jobs.Push({ Create<DExCredits>() });
} }
void DoBeforeCinemaScene(int nLevel, TArray<JobDesc>& jobs) DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, UndoStatic, UndoStatic)
{ {
if (nLevel == 5) jobs.Push({ Create<DCinema>(CINEMA_BEFORE_LEVEL_5) }); ACTION_RETURN_INT(UndoStatic());
else if (nLevel == 11) jobs.Push({ Create<DCinema>(CINEMA_BEFORE_LEVEL_11, 11) });
} }
END_PS_NS END_PS_NS

View file

@ -236,7 +236,7 @@ void DoBubbleMachines()
void BuildBubbleMachine(int nSprite) void BuildBubbleMachine(int nSprite)
{ {
if (nMachineCount >= kMaxMachines) { 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); exit(-1);
} }

View file

@ -72,13 +72,6 @@ void GameInterface::QuitToTitle()
gameaction = ga_mainmenu; 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() FSavegameInfo GameInterface::GetSaveSig()
{ {
return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS }; return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS };
@ -90,12 +83,6 @@ END_PS_NS
using namespace Exhumed; using namespace Exhumed;
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw)
{
menu_DoPlasma();
return 0;
}
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw) DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw)
{ {
auto nLogoTile = GameLogo(); auto nLogoTile = GameLogo();

View file

@ -36,6 +36,8 @@ void resettiming()
void precache() void precache()
{ {
if (!r_precache) return;
int i; int i;
for (i = 0; i < numsectors; i++) for (i = 0; i < numsectors; i++)

View file

@ -238,6 +238,15 @@ double calc_smoothratio()
return I_GetTimeFrac() * MaxSmoothRatio; 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) void GameMove(void)
{ {
FixPalette(); FixPalette();
@ -247,7 +256,7 @@ void GameMove(void)
sprite[i].backuploc(); sprite[i].backuploc();
} }
if (currentLevel->levelNumber == kMap20) if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)
{ {
if (lCountDown <= 0) if (lCountDown <= 0)
{ {
@ -456,7 +465,7 @@ void GameInterface::Ticker()
void LevelFinished() void LevelFinished()
{ {
NextMap = currentLevel->levelNumber == 20 ? nullptr : FindMapByLevelNum(currentLevel->levelNumber + 1); // todo: Use the map record for progression NextMap = FindNextMap(currentLevel);
EndLevel = 13; EndLevel = 13;
} }
@ -480,19 +489,6 @@ void GameInterface::app_init()
#if 0 #if 0
help_disabled = true; help_disabled = true;
#endif #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(); InitCheats();
registerosdcommands(); registerosdcommands();
@ -615,7 +611,7 @@ void SerializeState(FSerializer& arc)
int loaded = 0; int loaded = 0;
if (arc.BeginObject("state")) if (arc.BeginObject("state"))
{ {
if (arc.isReading() && currentLevel->levelNumber == 20) if (arc.isReading() && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
{ {
InitEnergyTile(); InitEnergyTile();
} }
@ -639,7 +635,6 @@ void SerializeState(FSerializer& arc)
("bsnakecam", bSnakeCam) ("bsnakecam", bSnakeCam)
("slipmode", bSlipMode) ("slipmode", bSlipMode)
("PlayClock", PlayClock) ("PlayClock", PlayClock)
("cinemaseen", nCinemaSeen)
("spiritsprite", nSpiritSprite) ("spiritsprite", nSpiritSprite)
.EndObject(); .EndObject();
} }

View file

@ -54,8 +54,6 @@ void SetHiRes();
void BlackOut(); void BlackOut();
void DoGameOverScene(bool finallevel); void DoGameOverScene(bool finallevel);
void DoAfterCinemaScene(int nLevel, TArray<JobDesc> &jobs);
void DoBeforeCinemaScene(int nLevel, TArray<JobDesc>& jobs);
int Query(short n, short l, ...); int Query(short n, short l, ...);
@ -83,12 +81,11 @@ void DoSpiritHead();
void CheckKeys2(); void CheckKeys2();
void GameTicker(); void GameTicker();
void InitLevel(int); void InitLevel(MapRecord*);
void InitNewGame(); void InitNewGame();
int showmap(short nLevel, short nLevelNew, short nLevelBest); int showmap(short nLevel, short nLevelNew, short nLevelBest);
void menu_DoPlasma(); void menu_DoPlasma();
void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray<JobDesc>& jobs);
void DoEnergyTile(); void DoEnergyTile();
void InitEnergyTile(); void InitEnergyTile();
@ -127,7 +124,6 @@ extern short nCurBodyNum;
extern short nBodyTotal; extern short nBodyTotal;
extern short bSnakeCam; extern short bSnakeCam;
extern uint8_t nCinemaSeen;
extern short nButtonColor; extern short nButtonColor;
@ -155,11 +151,6 @@ extern short bDoFlashes;
extern int bVanilla; extern int bVanilla;
inline int PublisherLogo()
{
return (g_gameType & GAMEFLAG_EXHUMED) ? kTileBMGLogo : kTilePIELogo;
}
inline int GameLogo() inline int GameLogo()
{ {
return (g_gameType & GAMEFLAG_EXHUMED) ? kExhumedLogo : kPowerslaveLogo; return (g_gameType & GAMEFLAG_EXHUMED) ? kExhumedLogo : kPowerslaveLogo;
@ -197,10 +188,11 @@ public:
void Start(double starttime); void Start(double starttime);
void ComputeCinemaText(); void ComputeCinemaText();
void ReadyCinemaText(uint16_t nVal); void ReadyCinemaText(const char* nVal);
void DisplayText(); void DisplayText();
bool AdvanceCinemaText(double clock); bool AdvanceCinemaText(double clock);
void SetPalette(int pal) { currentCinemaPalette = pal; } void SetPalette(int pal) { currentCinemaPalette = pal; }
void Create(const FString& text, int pal);
}; };
@ -229,7 +221,6 @@ struct GameInterface : ::GameInterface
bool GenerateSavePic() override; bool GenerateSavePic() override;
void MenuOpened() override; void MenuOpened() override;
void MenuSound(EMenuSounds snd) override; void MenuSound(EMenuSounds snd) override;
bool StartGame(FNewGameStartup& gs) override;
FSavegameInfo GetSaveSig() override; FSavegameInfo GetSaveSig() override;
void SerializeGameState(FSerializer& arc); void SerializeGameState(FSerializer& arc);
bool CanSave() override; bool CanSave() override;

View file

@ -49,32 +49,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "automap.h" #include "automap.h"
#include "raze_music.h" #include "raze_music.h"
#include "v_draw.h" #include "v_draw.h"
#include "vm.h"
BEGIN_PS_NS BEGIN_PS_NS
short nBestLevel; short nBestLevel;
extern uint8_t nCinemaSeen;
void RunCinemaScene(int num); void RunCinemaScene(int num);
void GameMove(void); void GameMove(void);
void DrawClock(); void DrawClock();
double calc_smoothratio(); double calc_smoothratio();
void DoTitle(CompletionFunc completion); void DoTitle(CompletionFunc completion);
static void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray<JobDesc> &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() void GameInterface::Render()
@ -83,7 +69,7 @@ void GameInterface::Render()
drawtime.Reset(); drawtime.Reset();
drawtime.Clock(); drawtime.Clock();
if (currentLevel && currentLevel->levelNumber == kMap20) if (currentLevel && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
{ {
DoEnergyTile(); DoEnergyTile();
DrawClock(); DrawClock();
@ -127,59 +113,75 @@ void GameInterface::DrawBackground()
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 0); DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 0);
} }
//--------------------------------------------------------------------------- void GameInterface::NextLevel(MapRecord *map, int skill)
//
//
//
//---------------------------------------------------------------------------
static void Intermission(MapRecord *from_map, MapRecord *to_map)
{ {
TArray<JobDesc> jobs; InitLevel(map);
if (from_map) StopAllSounds(); if (map->levelNumber >= nBestLevel)
{
nBestLevel = map->levelNumber - 1;
}
STAT_NewLevel(currentLevel->labelName);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::NewGame(MapRecord *map, int skill, bool frommenu)
{
// start a new game on the given level
InitNewGame();
InitLevel(map);
gameaction = ga_level;
}
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->gameflags & LEVEL_EX_TRAINING)
{
gameaction = ga_mainmenu;
return;
}
StopAllSounds();
bCamera = false; bCamera = false;
automapMode = am_off; automapMode = am_off;
STAT_Update(to_map == nullptr);
if (to_map) if (to_map)
{ {
if (to_map->levelNumber != 0) if (to_map->levelNumber > nBestLevel) nBestLevel = to_map->levelNumber - 1;
nBestLevel = to_map->levelNumber - 1;
STAT_Update(false); if (to_map->gameflags & LEVEL_EX_COUNTDOWN) nPlayerLives[0] = 0;
if (to_map->levelNumber == kMap20) if (to_map->gameflags & LEVEL_EX_TRAINING)
nPlayerLives[0] = 0;
if (to_map->levelNumber == 0) // skip all intermission stuff when going to the training map.
{ {
gameaction = ga_nextlevel; gameaction = ga_nextlevel;
return; return;
} }
else
{
DoAfterCinemaScene(to_map->levelNumber - 1, jobs);
} }
if (to_map->levelNumber > -1 && to_map->levelNumber < kMap20) SummaryInfo info{};
{ info.kills = nCreaturesKilled;
// start a new game at the given level info.maxkills = nCreaturesTotal;
if (!nNetPlayerCount && to_map->levelNumber > 0) info.supersecrets = nBestLevel;
{ info.time = PlayClock * GameTicRate / 120;
showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs); selectedlevelnew = to_map->levelNumber;
} ShowIntermission(currentLevel, to_map, &info, [=](bool)
else
jobs.Push({ Create<DBlackScreen>(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 if (!to_map) gameaction = ga_startup; // this was the end of the game
else else
@ -194,37 +196,6 @@ static void Intermission(MapRecord *from_map, MapRecord *to_map)
} }
}); });
}
}
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);
}
void GameInterface::LevelCompleted(MapRecord *map, int skill)
{
Mus_Stop();
if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu;
Intermission(currentLevel, map);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -237,22 +208,7 @@ void GameInterface::Startup()
{ {
resettiming(); resettiming();
EndLevel = 0; EndLevel = 0;
PlayLogos(ga_mainmenu, ga_mainmenu, false);
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;
}
} }
void GameInterface::ErrorCleanup() void GameInterface::ErrorCleanup()

View file

@ -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; lCountDown = 81000;
nAlarmTicks = 30; nAlarmTicks = 30;
@ -116,12 +116,12 @@ uint8_t LoadLevel(int nMap)
InitItems(); InitItems();
InitInput(); InitInput();
if (nMap == kMap20) { if (map->gameflags & LEVEL_EX_COUNTDOWN) {
InitEnergyTile(); InitEnergyTile();
} }
} }
if (nMap > 15) if (map->gameflags & LEVEL_EX_ALTSOUND)
{ {
nSwitchSound = 35; nSwitchSound = 35;
nStoneSound = 23; nStoneSound = 23;
@ -136,10 +136,6 @@ uint8_t LoadLevel(int nMap)
nStopSound = 66; nStopSound = 66;
} }
if (nMap < 0) {
return false;
}
vec3_t startPos; vec3_t startPos;
engineLoadBoard(currentLevel->fileName, 0, &startPos, &inita, &initsect); engineLoadBoard(currentLevel->fileName, 0, &startPos, &inita, &initsect);
initx = startPos.x; initx = startPos.x;
@ -172,12 +168,12 @@ uint8_t LoadLevel(int nMap)
return true; return true;
} }
void InitLevel(int level) // todo: use a map record void InitLevel(MapRecord* map)
{ {
StopCD(); StopCD();
currentLevel = FindMapByLevelNum(level); currentLevel = map;
if (!LoadLevel(level)) { if (!LoadLevel(map)) {
I_Error("Can't load level %d...\n", level); I_Error("Cannot load %s...\n", map->fileName.GetChars());
} }
for (int i = 0; i < nTotalPlayers; i++) for (int i = 0; i < nTotalPlayers; i++)
@ -200,17 +196,14 @@ void InitLevel(int level) // todo: use a map record
RefreshStatus(); RefreshStatus();
int nTrack = level; if (!mus_redbook && map->music.IsNotEmpty()) Mus_Play(map->labelName, map->music, true); // Allow non-CD music if defined for the current level
if (nTrack != 0) nTrack--; playCDtrack(map->cdSongId, true);
playCDtrack((nTrack % 8) + 11, true);
setLevelStarted(currentLevel); setLevelStarted(currentLevel);
} }
void InitNewGame() void InitNewGame()
{ {
bCamera = false; bCamera = false;
nCinemaSeen = 0;
PlayerCount = 0; PlayerCount = 0;
for (int i = 0; i < nTotalPlayers; i++) for (int i = 0; i < nTotalPlayers; i++)

View file

@ -432,7 +432,7 @@ void StartRegenerate(short nSprite)
pSprite->extra = 1350; pSprite->extra = 1350;
pSprite->ang = nFirstRegenerate; pSprite->ang = nFirstRegenerate;
if (currentLevel->levelNumber <= kMap20) if (!(currentLevel->gameflags & LEVEL_EX_MULTI))
{ {
pSprite->ang /= 5; pSprite->ang /= 5;
} }

View file

@ -43,8 +43,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_PS_NS BEGIN_PS_NS
uint8_t nCinemaSeen;
uint8_t energytile[66 * 66] = {0}; uint8_t energytile[66 * 66] = {0};
uint8_t *cur; uint8_t *cur;

View file

@ -207,7 +207,7 @@ void MoveThings()
DoMovingSects(); DoMovingSects();
DoRegenerates(); DoRegenerates();
if (currentLevel->levelNumber == kMap20) if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)
{ {
DoFinale(); DoFinale();
if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart) if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart)

View file

@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "s_music.h" #include "s_music.h"
#include "screenjob.h" #include "screenjob.h"
#include "v_draw.h" #include "v_draw.h"
#include "vm.h"
BEGIN_PS_NS BEGIN_PS_NS
@ -63,9 +64,34 @@ class LMFPlayer
AudioData audio{}; AudioData audio{};
AnimTextures animtex; AnimTextures animtex;
FileReader fp;
int nFrame = 0; int nFrame = 0;
uint64_t nextclock = 0;
public: 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) int ReadFrame(FileReader& fp)
{ {
nFrame++; nFrame++;
@ -188,105 +214,56 @@ public:
S_StopCustomStream(stream); S_StopCustomStream(stream);
} }
AnimTextures& animTex() FTextureID GetTexture()
{ {
return animtex; return animtex.GetFrameID();
} }
}; };
//--------------------------------------------------------------------------- int IdentifyLMF(const FString* fn)
//
//
//
//---------------------------------------------------------------------------
class DLmfPlayer : public DSkippableScreenJob
{ {
LMFPlayer decoder; auto fp = fileSystem.OpenFileReader(*fn);
double angle = 1536; if (!fp.isOpen()) return false;
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<DBlackScreen>(1);
}
char buffer[4]; char buffer[4];
fp.Read(buffer, 4); fp.Read(buffer, 4);
if (memcmp(buffer, "LMF ", 4)) return (0 == memcmp(buffer, "LMF ", 4));
{
fp.Close();
// Allpw replacement with more modern formats.
return PlayVideo(fileName);
}
fp.Seek(0, FileReader::SeekSet);
return Create<DLmfPlayer>(fp);
} }
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 END_PS_NS

View file

@ -1,3 +1,4 @@
x(SkullJaw, 3437)
x(PowerslaveLogo, 3442) x(PowerslaveLogo, 3442)
x(MenuNewGameTile, 3460) x(MenuNewGameTile, 3460)
x(MenuLoadGameTile, 3461) x(MenuLoadGameTile, 3461)
@ -6,6 +7,7 @@ x(MenuSoundFxTile, 3466)
x(MenuCursorTile, 3468) x(MenuCursorTile, 3468)
x(MenuBlank, 3469) x(MenuBlank, 3469)
x(SkullHead, 3582) x(SkullHead, 3582)
x(SkullJaw2, 3583)
x(ExhumedLogo, 3592) x(ExhumedLogo, 3592)
x(Energy1, 3604) x(Energy1, 3604)
x(Energy2, 3605) x(Energy2, 3605)
@ -27,4 +29,100 @@ x(ClockSymbol15, 3620)
x(ClockSymbol16, 3621) x(ClockSymbol16, 3621)
x(TileLoboLaptop, 3623) x(TileLoboLaptop, 3623)
x(TileBMGLogo, 3368) x(TileBMGLogo, 3368)
x(LobotomyLogo, 3348)
x(TilePIELogo, 3349) 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)

View file

@ -29,136 +29,10 @@ kTileStatusBar = 657,
kTile985 = 985, kTile985 = 985,
kTile986 = 986, kTile986 = 986,
kTile3000 = 3000, kTile3000 = 3000,
kTile3117 = 3117, kQueenChunk = 3117,
kTile3126 = 3126, 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, kTile3512 = 3512,
kTile3571 = 3571, kTile3571 = 3571,
kTile3583 = 3583,
kTile3591 = 3591,
kTile3593 = 3593, kTile3593 = 3593,
kTile3603 = 3603, kTile3603 = 3603,
kTile4092 = 4092, kTile4092 = 4092,

View file

@ -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(sprite[nSprite].owner);
runlist_SubRunRec(ObjectList[nObject].field_4); 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 // esi is i
for (i = 0; i < numsectors; i++) for (i = 0; i < numsectors; i++)

View file

@ -417,7 +417,7 @@ void RestartPlayer(short nPlayer)
plr->nAir = 100; plr->nAir = 100;
airpages = 0; airpages = 0;
if (currentLevel->levelNumber <= kMap20) if (!(currentLevel->gameflags & LEVEL_EX_MULTI))
{ {
RestoreMinAmmo(nPlayer); RestoreMinAmmo(nPlayer);
} }
@ -570,7 +570,7 @@ void StartDeathSeq(int nPlayer, int nVal)
BuildStatusAnim((3 * (nLives - 1)) + 7, 0); 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]--; nPlayerLives[nPlayer]--;
} }
@ -679,7 +679,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
{ {
int var_48 = 0; int var_48 = 0;
int var_40; int var_40;
bool mplevel = currentLevel->levelNumber > 20; bool mplevel = (currentLevel->gameflags & LEVEL_EX_MULTI);
short nPlayer = RunData[nRun].nVal; short nPlayer = RunData[nRun].nVal;
assert(nPlayer >= 0 && nPlayer < kMaxPlayers); assert(nPlayer >= 0 && nPlayer < kMaxPlayers);
@ -1033,14 +1033,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
InitSpiritHead(); InitSpiritHead();
PlayerList[nPlayer].nDestVertPan = q16horiz(0); PlayerList[nPlayer].nDestVertPan = q16horiz(0);
if (currentLevel->levelNumber == 11) PlayerList[nPlayer].horizon.settarget(currentLevel->ex_ramses_horiz);
{
PlayerList[nPlayer].horizon.settarget(46);
}
else
{
PlayerList[nPlayer].horizon.settarget(11);
}
} }
} }
else else

View file

@ -1445,7 +1445,7 @@ void FuncQueen(int a, int nDamage, int nRun)
{ {
short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF; 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].xrepeat = 100;
sprite[nChunkSprite].yrepeat = 100; sprite[nChunkSprite].yrepeat = 100;
} }

View file

@ -64,8 +64,6 @@ short nTalkTime = 0;
void InitSpiritHead() void InitSpiritHead()
{ {
char filename[20];
nPixels = 0; nPixels = 0;
nSpiritRepeatX = sprite[nSpiritSprite].xrepeat; nSpiritRepeatX = sprite[nSpiritSprite].xrepeat;
@ -137,26 +135,15 @@ void InitSpiritHead()
fadecdaudio(); fadecdaudio();
int nTrack; int nTrack = currentLevel->ex_ramses_cdtrack;
if (currentLevel->levelNumber == 1)
{
nTrack = 3;
}
else
{
nTrack = 7;
}
playCDtrack(nTrack, false); playCDtrack(nTrack, false);
StartSwirlies(); StartSwirlies();
sprintf(filename, "LEV%d.PUP", currentLevel->levelNumber);
lNextStateChange = PlayClock; lNextStateChange = PlayClock;
lHeadStartClock = PlayClock; lHeadStartClock = PlayClock;
auto headfd = fileSystem.OpenFileReader(filename); auto headfd = fileSystem.OpenFileReader(currentLevel->ex_ramses_pup);
if (!headfd.isOpen()) if (!headfd.isOpen())
{ {
memset(cPupData, 0, sizeof(cPupData)); memset(cPupData, 0, sizeof(cPupData));

View file

@ -117,7 +117,7 @@ void GameInterface::SerializeGameState(FSerializer& arc)
parallaxtype = 2; parallaxtype = 2;
g_visibility = 1024; g_visibility = 1024;
if (currentLevel->levelNumber > 15) if (currentLevel->gameflags & LEVEL_EX_ALTSOUND)
{ {
nSwitchSound = 35; nSwitchSound = 35;
nStoneSound = 23; nStoneSound = 23;

View file

@ -724,7 +724,7 @@ void CheckAmbience(short nSector)
void UpdateCreepySounds() void UpdateCreepySounds()
{ {
if (currentLevel->levelNumber == 20 || nFreeze || !SoundEnabled()) if ((currentLevel->gameflags & LEVEL_EX_COUNTDOWN) || nFreeze || !SoundEnabled())
return; return;
spritetype* pSprite = &sprite[PlayerList[nLocalPlayer].nSprite]; spritetype* pSprite = &sprite[PlayerList[nLocalPlayer].nSprite];
nCreepyTimer--; nCreepyTimer--;
@ -800,4 +800,35 @@ void PlayGameOverSound(void)
PlayLocalSound(StaticSound[kSoundJonLaugh2], 0, false, CHANF_UI); 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 END_PS_NS

View file

@ -408,10 +408,7 @@ void DrawView(double smoothRatio, bool sceneonly)
if (bSubTitles) if (bSubTitles)
{ {
subtitleOverlay.Start(I_GetTimeNS() * (120. / 1'000'000'000)); subtitleOverlay.Start(I_GetTimeNS() * (120. / 1'000'000'000));
if (currentLevel->levelNumber == 1) subtitleOverlay.ReadyCinemaText(currentLevel->ex_ramses_text);
subtitleOverlay.ReadyCinemaText(1);
else
subtitleOverlay.ReadyCinemaText(5);
} }
inputState.ClearAllInput(); inputState.ClearAllInput();
} }

View file

@ -1,4 +1,3 @@
#include "src/2d.cpp"
#include "src/actor.cpp" #include "src/actor.cpp"
#include "src/ai.cpp" #include "src/ai.cpp"
#include "src/break.cpp" #include "src/break.cpp"

View file

@ -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<DSWDRealmsScreen>() };
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<DSWLevelSummaryScreen>() };
if (FinishAnim == ANIM_ZILLA)
jobs[job++] = { Create<DSWCreditsScreen>() };
}
else if (gNet.MultiGameType != MULTI_GAME_COMMBAT)
{
jobs[job++] = { Create<DSWLevelSummaryScreen>() };
}
else
{
jobs[job++] = { Create<DSWMultiSummaryScreen>() };
}
RunScreenJob(jobs, job, completion, true);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void SybexScreen(CompletionFunc completion)
{
if (!SW_SHAREWARE || CommEnabled) completion(false);
else
{
JobDesc job = { Create<DImageScreen>(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<DSWLoadScreen>(rec) };
RunScreenJob(&job, 1, func);
}
END_SW_NS

View file

@ -116,7 +116,7 @@ bool NextCheat(cheatseq_t* c)
{ {
if (!checkCheat(c)) return false; if (!checkCheat(c)) return false;
if (!currentLevel) return true; if (!currentLevel) return true;
auto map = FindMapByLevelNum(currentLevel->levelNumber + 1); auto map = FindNextMap(currentLevel);
if (map) DeferedStartGame(map, -1); if (map) DeferedStartGame(map, -1);
return true; return true;
} }

View file

@ -102,22 +102,8 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
int handle = 0; int handle = 0;
int zero = 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; CameraTestMode = false;
StopFX(); StopAmbientSound();
//InitNewGame(); //InitNewGame();
@ -140,7 +126,6 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
} }
Net_ClearFifo(); Net_ClearFifo();
} }
DeferedStartGame(map, gs.Skill);
return true; return true;
} }

View file

@ -1681,7 +1681,9 @@ drawscreen(PLAYERp pp, double smoothratio)
if (paused && !M_Active()) 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)) if (!CommEnabled && TEST(pp->Flags, PF_DEAD))

View file

@ -88,10 +88,6 @@ CVAR(Bool, sw_bunnyrockets, false, CVAR_SERVERINFO | CVAR_CHEAT); // This is a
BEGIN_SW_NS BEGIN_SW_NS
void Logo(const CompletionFunc& completion);
void StatScreen(int FinishAnim, CompletionFunc completion);
void pClearSpriteList(PLAYERp pp); void pClearSpriteList(PLAYERp pp);
extern int sw_snd_scratch; extern int sw_snd_scratch;
@ -154,7 +150,6 @@ FString ThemeSongs[6];
int ThemeTrack[6]; int ThemeTrack[6];
/// L O C A L P R O T O T Y P E S ///////////////////////////////////////////////////////// /// 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); #define x(a, b) registerName(#a, b);
@ -253,12 +248,6 @@ void GameInterface::DrawBackground(void)
twod->ClearScreen(); twod->ClearScreen();
DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal,
DTA_Color, shadeToLight(20), TAG_DONE); 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; gNet.TimeLimitClock = gNet.TimeLimit;
serpwasseen = false;
sumowasseen = false; for (auto& b : bosswasseen) b = false;
zillawasseen = false;
memset(BossSpriteNum,-1,sizeof(BossSpriteNum)); memset(BossSpriteNum,-1,sizeof(BossSpriteNum));
} }
@ -533,7 +521,7 @@ static void PlayOrderSound()
void GameInterface::LevelCompleted(MapRecord *map, int skill) void GameInterface::LevelCompleted(MapRecord* map, int skill)
{ {
//ResetPalette(mpp); //ResetPalette(mpp);
COVER_SetReverb(0); // Reset reverb COVER_SetReverb(0); // Reset reverb
@ -541,13 +529,21 @@ void GameInterface::LevelCompleted(MapRecord *map, int skill)
StopSound(); StopSound();
STAT_Update(map == nullptr); 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) if (map == nullptr)
{ {
FinishAnim = false; FinishAnim = false;
PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]); PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]);
if (SW_SHAREWARE) if (isShareware())
{ {
PlayOrderSound(); PlayOrderSound();
gameaction = ga_creditsmenu; gameaction = ga_creditsmenu;
@ -556,7 +552,6 @@ void GameInterface::LevelCompleted(MapRecord *map, int skill)
} }
else gameaction = ga_nextlevel; else gameaction = ga_nextlevel;
}); });
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
@ -584,6 +579,7 @@ void GameInterface::NewGame(MapRecord *map, int skill, bool)
ShadowWarrior::NewGame = true; ShadowWarrior::NewGame = true;
InitLevel(map); InitLevel(map);
InitRunLevel(); InitRunLevel();
gameaction = ga_level;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -633,17 +629,7 @@ void GameInterface::Render()
void GameInterface::Startup() void GameInterface::Startup()
{ {
if (userConfig.CommandMap.IsNotEmpty()) PlayLogos(ga_mainmenunostopsound, ga_mainmenu, false);
{
}
else
{
if (!userConfig.nologo) Logo([](bool)
{
gameaction = ga_mainmenunostopsound;
});
else gameaction = ga_mainmenu;
}
} }

View file

@ -77,7 +77,6 @@ extern GAME_SET gs;
enum enum
{ {
DREALMSPAL = 1, DREALMSPAL = 1,
THREED_REALMS_PIC = 2325,
MAXMIRRORS = 8, MAXMIRRORS = 8,
// This is just some, high, blank tile number not used // This is just some, high, blank tile number not used
@ -2225,9 +2224,7 @@ extern short wait_active_check_offset;
//extern short Zombies; //extern short Zombies;
extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust; extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust;
extern bool left_foot; extern bool left_foot;
extern bool serpwasseen; extern bool bosswasseen[3];
extern bool sumowasseen;
extern bool zillawasseen;
extern short BossSpriteNum[3]; extern short BossSpriteNum[3];
extern int ChopTics; extern int ChopTics;
extern short Bunny_Count; extern short Bunny_Count;

View file

@ -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 END_SW_NS

View file

@ -32,7 +32,6 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
BEGIN_SW_NS 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); void MNU_DrawSmallString(int x, int y, const char* string, int shade, int pal, int align = -1, double alpha = 1);
END_SW_NS END_SW_NS

Some files were not shown because too many files have changed in this diff Show more