mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-26 11:40:44 +00:00
Merge branch 'scriptable_cutscenes' into newrenderer
This commit is contained in:
commit
8003ab6fa3
141 changed files with 10317 additions and 5320 deletions
|
@ -749,7 +749,6 @@ set( NOT_COMPILED_SOURCE_FILES
|
|||
games/blood/src/callback.cpp
|
||||
games/blood/src/choke.cpp
|
||||
games/blood/src/controls.cpp
|
||||
games/blood/src/credits.cpp
|
||||
games/blood/src/db.cpp
|
||||
games/blood/src/dude.cpp
|
||||
games/blood/src/d_menu.cpp
|
||||
|
@ -835,7 +834,6 @@ set( NOT_COMPILED_SOURCE_FILES
|
|||
games/duke/src/spawn_r.cpp
|
||||
|
||||
# Shadow Warrior
|
||||
games/sw/src/2d.cpp
|
||||
games/sw/src/actor.cpp
|
||||
games/sw/src/ai.cpp
|
||||
games/sw/src/break.cpp
|
||||
|
@ -1049,6 +1047,7 @@ set (PCH_SOURCES
|
|||
core/gamehud.cpp
|
||||
core/gamefuncs.cpp
|
||||
core/gameinput.cpp
|
||||
core/g_mapinfo.cpp
|
||||
core/interpolate.cpp
|
||||
core/inputstate.cpp
|
||||
core/maphack.cpp
|
||||
|
|
|
@ -324,6 +324,22 @@ DEFINE_ACTION_FUNCTION(_Screen, ClearClipRect)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, ClearScreen)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
twod->ClearScreen();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, SetScreenFade)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_FLOAT(x);
|
||||
twod->SetScreenFade(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void F2DDrawer::GetClipRect(int *x, int *y, int *w, int *h)
|
||||
{
|
||||
if (x) *x = clipleft;
|
||||
|
|
|
@ -138,6 +138,10 @@ void S_StopCustomStream(SoundStream *stream)
|
|||
|
||||
void S_PauseAllCustomStreams(bool on)
|
||||
{
|
||||
static bool paused = false;
|
||||
|
||||
if (paused == on) return;
|
||||
paused = on;
|
||||
for (auto s : customStreams)
|
||||
{
|
||||
s->SetPaused(on);
|
||||
|
|
|
@ -1107,4 +1107,4 @@ xy(menu_change, "menu/change")
|
|||
xy(menu_advance, "menu/advance")
|
||||
|
||||
xx(zoomsize)
|
||||
|
||||
xx(ScreenJobRunner)
|
||||
|
|
|
@ -371,7 +371,6 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer()
|
|||
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
|
||||
SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE);
|
||||
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
I_GetEvent();
|
||||
|
||||
static_cast<Win32BaseVideo *>(Video)->Shutdown();
|
||||
}
|
||||
|
|
|
@ -8706,7 +8706,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
|||
bool writable;
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx); // must be resolved before the address is requested.
|
||||
|
||||
if (ArgList[i]->ValueType->isRealPointer())
|
||||
if (ArgList[i] && ArgList[i]->ValueType->isRealPointer())
|
||||
{
|
||||
auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType;
|
||||
if (pointedType && pointedType->isDynArray())
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "dobject.h"
|
||||
#include "vm.h"
|
||||
#include "types.h"
|
||||
#include "v_draw.h"
|
||||
|
||||
// We need one specific type for each of the 8 integral VM types and instantiate the needed functions for each of them.
|
||||
// Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible.
|
||||
|
@ -412,6 +413,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Push, ArrayPush<FDynArray_I32 COMMA
|
|||
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>)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
|
|
|
@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl)
|
|||
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));
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "s_music.h"
|
||||
#include "i_interface.h"
|
||||
#include "base_sbar.h"
|
||||
#include "image.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -336,8 +337,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetName)
|
|||
|
||||
static int CheckForTexture(const FString& name, int type, int flags)
|
||||
{
|
||||
// ForceLookup is intentionally blocked here, this flag is for internal use only.
|
||||
return TexMan.CheckForTexture(name, static_cast<ETextureType>(type), (flags & ~FTextureManager::TEXMAN_ForceLookup)).GetIndex();
|
||||
return TexMan.CheckForTexture(name, static_cast<ETextureType>(type), flags).GetIndex();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckForTexture, CheckForTexture)
|
||||
|
@ -477,6 +477,20 @@ DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, OkForLocalization, OkForLocalization_)
|
|||
ACTION_RETURN_INT(OkForLocalization_(name, subst));
|
||||
}
|
||||
|
||||
static int UseGamePalette(int index)
|
||||
{
|
||||
auto tex = TexMan.GameByIndex(index, false);
|
||||
if (!tex) return false;
|
||||
auto image = tex->GetTexture()->GetImage();
|
||||
return image ? image->UseGamePalette() : false;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, UseGamePalette, UseGamePalette)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(texid);
|
||||
ACTION_RETURN_INT(UseGamePalette(texid));
|
||||
}
|
||||
|
||||
//=====================================================================================
|
||||
//
|
||||
|
@ -867,6 +881,13 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, GetAllKeysForCommand)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FKeyBindings, GetBinding)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings);
|
||||
PARAM_INT(key);
|
||||
ACTION_RETURN_STRING(self->GetBinding(key));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings);
|
||||
|
@ -914,6 +935,7 @@ DEFINE_GLOBAL_NAMED(mus_playing, musplaying);
|
|||
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, name);
|
||||
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder);
|
||||
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop);
|
||||
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, handle);
|
||||
|
||||
DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses)
|
||||
DEFINE_GLOBAL(Bindings)
|
||||
|
|
|
@ -69,11 +69,11 @@ extern int16_t sintable[2048];
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
inline int32_t bsin(const int ang, const int8_t shift = 0)
|
||||
inline int bsin(const int ang, const int shift = 0)
|
||||
{
|
||||
return shift < 0 ? sintable[ang & 2047] >> abs(shift) : sintable[ang & 2047] << shift;
|
||||
}
|
||||
inline double bsinf(const double ang, const int8_t shift = 0)
|
||||
inline double bsinf(const double ang, const int shift = 0)
|
||||
{
|
||||
return g_sin(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
|
||||
}
|
||||
|
@ -85,11 +85,11 @@ inline double bsinf(const double ang, const int8_t shift = 0)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
inline int32_t bcos(const int ang, const int8_t shift = 0)
|
||||
inline int bcos(const int ang, const int shift = 0)
|
||||
{
|
||||
return shift < 0 ? sintable[(ang + 512) & 2047] >> abs(shift) : sintable[(ang + 512) & 2047] << shift;
|
||||
}
|
||||
inline double bcosf(const double ang, const int8_t shift = 0)
|
||||
inline double bcosf(const double ang, const int shift = 0)
|
||||
{
|
||||
return g_cos(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ void changeMap(int player, uint8_t** stream, bool skip)
|
|||
|
||||
void endScreenJob(int player, uint8_t** stream, bool skip)
|
||||
{
|
||||
if (!skip) EndScreenJob();
|
||||
if (!skip) gameaction = ga_endscreenjob;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -280,6 +280,7 @@ void DeferedStartGame(MapRecord* map, int skill, bool nostopsound)
|
|||
static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, const char *t2)
|
||||
{
|
||||
int numparm = g_gameType & (GAMEFLAG_SW | GAMEFLAG_PSEXHUMED) ? 1 : 2; // Handle games with episodic and non-episodic level order.
|
||||
if (numparm == 2 && argv.argc() == 2) numparm = 1;
|
||||
if (argv.argc() <= numparm)
|
||||
{
|
||||
if (numparm == 2) Printf(PRINT_BOLD, "%s <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");
|
||||
return nullptr;
|
||||
}
|
||||
auto map = FindMapByLevelNum(numparm == 1 ? m : levelnum(e - 1, m - 1));
|
||||
auto map = FindMapByIndex(e, m);
|
||||
if (!map)
|
||||
{
|
||||
if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]);
|
||||
|
|
1232
source/core/g_mapinfo.cpp
Normal file
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
108
source/core/g_mapinfo.h
Normal 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__
|
|
@ -73,6 +73,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "automap.h"
|
||||
#include "v_draw.h"
|
||||
#include "gi.h"
|
||||
#include "vm.h"
|
||||
#include "g_mapinfo.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "hw_voxels.h"
|
||||
#include "hw_palmanager.h"
|
||||
|
@ -286,6 +288,11 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr)
|
|||
|
||||
UserConfig userConfig;
|
||||
|
||||
DEFINE_GLOBAL(userConfig)
|
||||
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nomonsters)
|
||||
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nosound)
|
||||
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nologo)
|
||||
|
||||
void UserConfig::ProcessOptions()
|
||||
{
|
||||
// -help etc are omitted
|
||||
|
@ -560,7 +567,7 @@ int GameMain()
|
|||
I_ShowFatalError(err.what());
|
||||
r = -1;
|
||||
}
|
||||
DeleteScreenJob();
|
||||
//DeleteScreenJob();
|
||||
DeinitMenus();
|
||||
if (StatusBar) StatusBar->Destroy();
|
||||
StatusBar = nullptr;
|
||||
|
@ -601,13 +608,17 @@ int GameMain()
|
|||
|
||||
void SetDefaultStrings()
|
||||
{
|
||||
// Duke 1.3 does not define its episodes through CON.
|
||||
if ((g_gameType & GAMEFLAG_DUKE) && fileSystem.FindFile("E4L1.MAP") < 0)
|
||||
{
|
||||
auto vol0 = AllocateVolume(); vol0->index = 0;
|
||||
auto vol1 = AllocateVolume(); vol1->index = 1; vol1->flags = VF_SHAREWARELOCK;
|
||||
auto vol2 = AllocateVolume(); vol2->index = 2; vol1->flags = VF_SHAREWARELOCK;
|
||||
// Pre-Atomic releases do not define this.
|
||||
gVolumeNames[0] = "$L.A. Meltdown";
|
||||
gVolumeNames[1] = "$Lunar Apocalypse";
|
||||
gVolumeNames[2] = "$Shrapnel City";
|
||||
if (g_gameType & GAMEFLAG_SHAREWARE) gVolumeNames[3] = "$The Birth";
|
||||
vol0->name = "$L.A. Meltdown";
|
||||
vol1->name = "$Lunar Apocalypse";
|
||||
vol2->name = "$Shrapnel City";
|
||||
|
||||
gSkillNames[0] = "$Piece of Cake";
|
||||
gSkillNames[1] = "$Let's Rock";
|
||||
gSkillNames[2] = "$Come get Some";
|
||||
|
@ -952,6 +963,7 @@ int RunGame()
|
|||
LoadScripts();
|
||||
StartScreen->Progress();
|
||||
SetDefaultStrings();
|
||||
Job_Init();
|
||||
if (Args->CheckParm("-sounddebug"))
|
||||
C_DoCommand("stat sounddebug");
|
||||
|
||||
|
@ -967,6 +979,7 @@ int RunGame()
|
|||
engineInit();
|
||||
gi->app_init();
|
||||
StartScreen->Progress();
|
||||
G_ParseMapInfo();
|
||||
CreateStatusBar();
|
||||
SetDefaultMenuColors();
|
||||
M_Init();
|
||||
|
@ -1440,13 +1453,69 @@ DEFINE_ACTION_FUNCTION(_Screen, GetViewWindow)
|
|||
return MIN(numret, 4);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Build, ShadeToLight, shadeToLight)
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, ShadeToLight, shadeToLight)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(shade);
|
||||
ACTION_RETURN_INT(shadeToLight(shade));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopAllSounds, FX_StopAllSounds)
|
||||
{
|
||||
FX_StopAllSounds();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopMusic, Mus_Stop)
|
||||
{
|
||||
Mus_Stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, SoundEnabled, SoundEnabled)
|
||||
{
|
||||
ACTION_RETURN_INT(SoundEnabled());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, MusicEnabled, MusicEnabled)
|
||||
{
|
||||
ACTION_RETURN_INT(MusicEnabled());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, GetTimeFrac, I_GetTimeFrac)
|
||||
{
|
||||
ACTION_RETURN_INT(I_GetTimeFrac());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Raze, PlayerName)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(index);
|
||||
ACTION_RETURN_STRING(unsigned(index) >= MAXPLAYERS ? "" : PlayerName(index));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bsin, bsin)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(v);
|
||||
PARAM_INT(shift);
|
||||
ACTION_RETURN_INT(bsin(v, shift));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bcos, bcos)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(v);
|
||||
PARAM_INT(shift);
|
||||
ACTION_RETURN_INT(bcos(v, shift));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_MapRecord, GetCluster)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(MapRecord);
|
||||
ACTION_RETURN_POINTER(FindCluster(self->cluster));
|
||||
}
|
||||
|
||||
extern bool demoplayback;
|
||||
DEFINE_GLOBAL(multiplayer)
|
||||
DEFINE_GLOBAL(netgame)
|
||||
|
@ -1454,6 +1523,37 @@ DEFINE_GLOBAL(gameaction)
|
|||
DEFINE_GLOBAL(gamestate)
|
||||
DEFINE_GLOBAL(demoplayback)
|
||||
DEFINE_GLOBAL(consoleplayer)
|
||||
DEFINE_GLOBAL(currentLevel)
|
||||
DEFINE_GLOBAL(paused)
|
||||
|
||||
DEFINE_FIELD_X(ClusterDef, ClusterDef, name)
|
||||
DEFINE_FIELD_X(ClusterDef, ClusterDef, InterBackground)
|
||||
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, parTime)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, designerTime)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, fileName)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, labelName)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, name)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, music)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, cdSongId)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, flags)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, levelNumber)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, cluster)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, NextMap)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, NextSecret)
|
||||
//native readonly String messages[MAX_MESSAGES];
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, Author)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, InterBackground)
|
||||
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, kills)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxkills)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, secrets)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxsecrets)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, supersecrets)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, playercount)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, time)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, cheated)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, endofgame)
|
||||
|
||||
|
||||
void InitBuildTiles()
|
||||
|
|
|
@ -70,12 +70,12 @@ extern UserConfig userConfig;
|
|||
|
||||
extern int nomusic;
|
||||
extern bool nosound;
|
||||
inline bool MusicEnabled()
|
||||
inline int MusicEnabled() // int return is for scripting
|
||||
{
|
||||
return mus_enabled && !nomusic;
|
||||
}
|
||||
|
||||
inline bool SoundEnabled()
|
||||
inline int SoundEnabled()
|
||||
{
|
||||
return snd_enabled && !nosound;
|
||||
}
|
||||
|
@ -99,8 +99,15 @@ enum
|
|||
GAMEFLAG_POWERSLAVE = 0x00002000,
|
||||
GAMEFLAG_EXHUMED = 0x00004000,
|
||||
GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher.
|
||||
GAMEFLAG_WORLDTOUR = 0x00008000,
|
||||
GAMEFLAG_DUKEDC = 0x00010000,
|
||||
GAMEFLAG_WORLDTOUR = 0x00008000,
|
||||
GAMEFLAG_DUKEDC = 0x00010000,
|
||||
GAMEFLAG_DUKENW = 0x00020000,
|
||||
GAMEFLAG_DUKEVACA = 0x00040000,
|
||||
GAMEFLAG_BLOODCP = 0x00080000,
|
||||
GAMEFLAG_ROUTE66 = 0x00100000,
|
||||
GAMEFLAG_SWWANTON = 0x00200000,
|
||||
GAMEFLAG_SWTWINDRAG = 0x00400000,
|
||||
|
||||
GAMEFLAG_DUKECOMPAT = GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI | GAMEFLAG_RRALL,
|
||||
GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo
|
||||
|
||||
|
|
|
@ -43,8 +43,10 @@ enum gameaction_t : int
|
|||
ga_nextlevel, // Actually start the next level.
|
||||
ga_loadgamehidecon,
|
||||
ga_newgamenostopsound, // start a new game
|
||||
ga_endscreenjob,
|
||||
|
||||
ga_fullconsole,
|
||||
};
|
||||
extern gamestate_t gamestate;
|
||||
extern gameaction_t gameaction;
|
||||
extern int intermissiondelay;
|
||||
|
|
|
@ -79,7 +79,7 @@ struct GameInterface
|
|||
virtual void MenuSound(EMenuSounds snd) {}
|
||||
virtual bool CanSave() { return true; }
|
||||
virtual void CustomMenuSelection(int menu, int item) {}
|
||||
virtual bool StartGame(FNewGameStartup& gs) { return false; }
|
||||
virtual bool StartGame(FNewGameStartup& gs) { return true; }
|
||||
virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; }
|
||||
virtual double SmallFontScale() { return 1; }
|
||||
virtual void SerializeGameState(FSerializer& arc) {}
|
||||
|
|
|
@ -105,6 +105,7 @@ bool r_NoInterpolate;
|
|||
int entertic;
|
||||
int oldentertics;
|
||||
int gametic;
|
||||
int intermissiondelay;
|
||||
|
||||
FString BackupSaveGame;
|
||||
|
||||
|
@ -133,6 +134,20 @@ void G_BuildTiccmd(ticcmd_t* cmd)
|
|||
//==========================================================================
|
||||
bool newGameStarted;
|
||||
|
||||
void NewGame(MapRecord* map, int skill, bool ns = false)
|
||||
{
|
||||
newGameStarted = true;
|
||||
ShowIntermission(nullptr, map, nullptr, [=](bool) {
|
||||
gi->NewGame(map, skill, ns);
|
||||
});
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void GameTicker()
|
||||
{
|
||||
int i;
|
||||
|
@ -159,7 +174,7 @@ static void GameTicker()
|
|||
FX_SetReverb(0);
|
||||
gi->FreeLevelData();
|
||||
gameaction = ga_level;
|
||||
gi->NewGame(g_nextmap, -1);
|
||||
NewGame(g_nextmap, -1);
|
||||
BackupSaveGame = "";
|
||||
}
|
||||
break;
|
||||
|
@ -191,13 +206,12 @@ static void GameTicker()
|
|||
FX_StopAllSounds();
|
||||
case ga_newgamenostopsound:
|
||||
DeleteScreenJob();
|
||||
newGameStarted = true;
|
||||
FX_SetReverb(0);
|
||||
gi->FreeLevelData();
|
||||
C_FlushDisplay();
|
||||
gameaction = ga_level;
|
||||
BackupSaveGame = "";
|
||||
gi->NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
|
||||
NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
|
||||
break;
|
||||
|
||||
case ga_startup:
|
||||
|
@ -209,6 +223,7 @@ static void GameTicker()
|
|||
|
||||
case ga_mainmenu:
|
||||
FX_StopAllSounds();
|
||||
if (isBlood()) Mus_Stop();
|
||||
case ga_mainmenunostopsound:
|
||||
gi->FreeLevelData();
|
||||
gamestate = GS_MENUSCREEN;
|
||||
|
@ -253,6 +268,10 @@ static void GameTicker()
|
|||
gameaction = ga_nothing;
|
||||
break;
|
||||
|
||||
case ga_endscreenjob:
|
||||
EndScreenJob();
|
||||
break;
|
||||
|
||||
// for later
|
||||
// case ga_recordgame, // start a new demo recording (later)
|
||||
// case ga_loadgameplaydemo, // load a savegame and play a demo.
|
||||
|
@ -331,7 +350,16 @@ static void GameTicker()
|
|||
break;
|
||||
case GS_INTERMISSION:
|
||||
case GS_INTRO:
|
||||
ScreenJobTick();
|
||||
if (intermissiondelay > 0)
|
||||
{
|
||||
intermissiondelay--;
|
||||
break;
|
||||
}
|
||||
if (ScreenJobTick())
|
||||
{
|
||||
// synchronize termination with the playsim.
|
||||
Net_WriteByte(DEM_ENDSCREENJOB);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -371,7 +399,7 @@ void Display()
|
|||
case GS_INTRO:
|
||||
case GS_INTERMISSION:
|
||||
// screen jobs are not bound by the game ticker so they need to be ticked in the display loop.
|
||||
ScreenJobDraw();
|
||||
if (intermissiondelay <= 0) ScreenJobDraw();
|
||||
break;
|
||||
|
||||
case GS_LEVEL:
|
||||
|
@ -633,6 +661,16 @@ void MainLoop ()
|
|||
// Clamp the timer to TICRATE until the playloop has been entered.
|
||||
r_NoInterpolate = true;
|
||||
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
auto maprecord = FindMapByName(userConfig.CommandMap);
|
||||
userConfig.CommandMap = "";
|
||||
if (maprecord)
|
||||
{
|
||||
NewGame(maprecord, /*userConfig.skill*/2); // todo: fix the skill.
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -41,43 +41,102 @@
|
|||
#include "raze_sound.h"
|
||||
|
||||
FString gSkillNames[MAXSKILLS];
|
||||
FString gVolumeNames[MAXVOLUMES];
|
||||
FString gVolumeSubtitles[MAXVOLUMES];
|
||||
int32_t gVolumeFlags[MAXVOLUMES];
|
||||
int gDefaultVolume = 0, gDefaultSkill = 1;
|
||||
|
||||
MapRecord mapList[512];
|
||||
MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
|
||||
GlobalCutscenes globalCutscenes;
|
||||
TArray<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.
|
||||
unsigned int numUsedSlots;
|
||||
|
||||
|
||||
CCMD(listmaps)
|
||||
{
|
||||
for (unsigned int i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
int lump = fileSystem.FindFile(mapList[i].fileName);
|
||||
int lump = fileSystem.FindFile(map->fileName);
|
||||
if (lump >= 0)
|
||||
{
|
||||
int rfnum = fileSystem.GetFileContainer(lump);
|
||||
Printf("%s - %s (%s)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName(), fileSystem.GetResourceFileName(rfnum));
|
||||
Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("%s - %s (defined but does not exist)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName());
|
||||
Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCMD(mapinfo)
|
||||
{
|
||||
const char* mapname = nullptr;
|
||||
if (argv.argc() > 1) mapname = argv[1];
|
||||
|
||||
if (!mapname)
|
||||
{
|
||||
for (auto& vol : volumes)
|
||||
{
|
||||
Printf("Volume %d\n\tName = '%s'\n\tstartmap = '%s'\n}\n", vol.index, vol.name.GetChars(), vol.startmap.GetChars());
|
||||
}
|
||||
for (auto& clus : clusters)
|
||||
{
|
||||
if (clus.intro.isdefined() || clus.outro.isdefined())
|
||||
{
|
||||
Printf("Cluster %d\n\tName = '%s'\n", clus.index, clus.name.GetChars());
|
||||
if (clus.intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", clus.intro.function.GetChars());
|
||||
if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", clus.intro.video.GetChars());
|
||||
if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", clus.outro.function.GetChars());
|
||||
if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", clus.outro.video.GetChars());
|
||||
Printf("}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
if (mapname && map->labelName.CompareNoCase(mapname)) continue;
|
||||
int lump = fileSystem.FindFile(map->fileName);
|
||||
if (lump >= 0)
|
||||
{
|
||||
int rfnum = fileSystem.GetFileContainer(lump);
|
||||
Printf("%s - %s (%s)\n{\n", map->fileName.GetChars(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
|
||||
Printf("\tlevel number = %d\n\tCluster = %d\n\tIndex = %d\n", map->levelNumber, map->cluster, map->mapindex);
|
||||
if (map->Author.IsNotEmpty()) Printf("\tAuthor = '%s'\n", map->Author.GetChars());
|
||||
if (map->NextMap.IsNotEmpty()) Printf("\tNext map = '%s'\n", map->NextMap.GetChars());
|
||||
if (map->NextSecret.IsNotEmpty()) Printf("\tNext secret map = '%s'\n", map->NextSecret.GetChars());
|
||||
if (map->music.IsNotEmpty()) Printf("\tMusic = '%s:%d'", map->music.GetChars(), map->musicorder);
|
||||
if (map->cdSongId > 0) Printf("\tCD track = %d\n", map->cdSongId);
|
||||
if (map->parTime) Printf("\tPar Time = %d\n", map->parTime);
|
||||
if (map->designerTime) Printf("\tPar Time = %d\n", map->designerTime);
|
||||
if (map->intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", map->intro.function.GetChars());
|
||||
if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", map->intro.video.GetChars());
|
||||
if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", map->outro.function.GetChars());
|
||||
if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", map->outro.video.GetChars());
|
||||
Printf("}\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CutsceneDef::GetSound()
|
||||
{
|
||||
int id;
|
||||
if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName);
|
||||
if (id <= 0) id = soundEngine->FindSoundByResID(soundID);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
MapRecord *FindMapByName(const char *nm)
|
||||
{
|
||||
for (unsigned i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
auto &map = mapList[i];
|
||||
if (map.labelName.CompareNoCase(nm) == 0)
|
||||
if (map->labelName.CompareNoCase(nm) == 0)
|
||||
{
|
||||
return ↦
|
||||
return map.Data();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -86,23 +145,78 @@ MapRecord *FindMapByName(const char *nm)
|
|||
|
||||
MapRecord *FindMapByLevelNum(int num)
|
||||
{
|
||||
for (unsigned i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
auto &map = mapList[i];
|
||||
if (map.levelNumber == num)
|
||||
if (map->levelNumber == num)
|
||||
{
|
||||
return ↦
|
||||
return map.Data();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MapRecord *FindNextMap(MapRecord *thismap)
|
||||
VolumeRecord* FindVolume(int index)
|
||||
{
|
||||
if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel);
|
||||
return FindMapByLevelNum(thismap->levelNumber+1);
|
||||
for (auto& vol : volumes)
|
||||
{
|
||||
if (vol.index == index) return &vol;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClusterDef* FindCluster(int index)
|
||||
{
|
||||
for (auto& vol : clusters)
|
||||
{
|
||||
if (vol.index == index) return &vol;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClusterDef* AllocateCluster()
|
||||
{
|
||||
return &clusters[clusters.Reserve(1)];
|
||||
}
|
||||
|
||||
VolumeRecord* AllocateVolume()
|
||||
{
|
||||
return &volumes[volumes.Reserve(1)];
|
||||
}
|
||||
|
||||
MapRecord* FindMapByIndexOnly(int cluster, int num)
|
||||
{
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
if (map->mapindex == num && map->cluster == cluster) return map.Data();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MapRecord* FindMapByIndex(int cluster, int num)
|
||||
{
|
||||
auto map = FindMapByLevelNum(num);
|
||||
if (!map) map = FindMapByIndexOnly(cluster, num); // modern definitions take precedence.
|
||||
return map;
|
||||
}
|
||||
|
||||
MapRecord* FindNextMap(MapRecord* thismap)
|
||||
{
|
||||
MapRecord* next = nullptr;
|
||||
if (!thismap->NextMap.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
|
||||
if (thismap->NextMap.IsNotEmpty()) next = FindMapByName(thismap->NextMap);
|
||||
if (!next) next = FindMapByLevelNum(thismap->levelNumber + 1);
|
||||
return next;
|
||||
}
|
||||
|
||||
MapRecord* FindNextSecretMap(MapRecord* thismap)
|
||||
{
|
||||
MapRecord* next = nullptr;
|
||||
if (!thismap->NextSecret.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
|
||||
if (thismap->NextSecret.IsNotEmpty()) next = FindMapByName(thismap->NextSecret);
|
||||
return next? next : FindNextMap(thismap);
|
||||
}
|
||||
|
||||
|
||||
bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
|
||||
{
|
||||
static const char* specials[] = { "intro", "briefing", "loading" };
|
||||
|
@ -129,7 +243,7 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
|
|||
if (numMatches != 4 || toupper(b1) != 'E' || toupper(b2) != 'L')
|
||||
return false;
|
||||
|
||||
index = FindMapByLevelNum(levelnum(ep - 1, lev - 1));
|
||||
index = FindMapByIndexOnly(ep, lev);
|
||||
|
||||
}
|
||||
if (index != nullptr)
|
||||
|
@ -142,18 +256,19 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
|
|||
|
||||
MapRecord *AllocateMap()
|
||||
{
|
||||
return &mapList[numUsedSlots++];
|
||||
auto&p = mapList[mapList.Reserve(1)];
|
||||
p.Alloc();
|
||||
return p.Data();
|
||||
}
|
||||
|
||||
|
||||
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic)
|
||||
{
|
||||
for (unsigned i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
auto &map = mapList[i];
|
||||
if (map.fileName.CompareNoCase(boardfilename) == 0)
|
||||
if (map->fileName.CompareNoCase(boardfilename) == 0)
|
||||
{
|
||||
return ↦
|
||||
return map.Data();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,28 +3,58 @@
|
|||
#include "gstrings.h"
|
||||
#include "cmdlib.h"
|
||||
#include "quotemgr.h"
|
||||
#include "palentry.h"
|
||||
#include "vectors.h"
|
||||
#ifdef GetMessage
|
||||
#undef GetMessage // Windows strikes...
|
||||
#endif
|
||||
|
||||
|
||||
enum EMax
|
||||
{
|
||||
MAXSKILLS = 7,
|
||||
MAXVOLUMES = 7,
|
||||
MAXMENUGAMEPLAYENTRIES = 7,
|
||||
};
|
||||
|
||||
enum EVolFlags
|
||||
{
|
||||
EF_HIDEFROMSP = 1,
|
||||
VF_HIDEFROMSP = 1,
|
||||
VF_OPTIONAL = 2,
|
||||
VF_SHAREWARELOCK = 4, // show in shareware but lock access.
|
||||
VF_NOSKILL = 8,
|
||||
};
|
||||
|
||||
enum EMapFlags
|
||||
{
|
||||
LEVEL_NOINTERMISSION = 1,
|
||||
LEVEL_SECRETEXITOVERRIDE = 2, // when given an explicit level number, override with secret exit in the map, mainly for compiling episodes out of single levels.
|
||||
LEVEL_CLEARINVENTORY = 4,
|
||||
LEVEL_CLEARWEAPONS = 8,
|
||||
LEVEL_FORCENOEOG = 16, // RR E1L7 needs this to override its boss's death ending the game.
|
||||
};
|
||||
|
||||
enum EMapGameFlags
|
||||
{
|
||||
LEVEL_RR_HULKSPAWN = 1,
|
||||
LEVEL_RR_CLEARMOONSHINE = 2,
|
||||
|
||||
LEVEL_EX_COUNTDOWN = 4,
|
||||
LEVEL_EX_TRAINING = 8,
|
||||
LEVEL_EX_ALTSOUND = 16,
|
||||
LEVEL_EX_MULTI = 32,
|
||||
|
||||
LEVEL_SW_SPAWNMINES = 64,
|
||||
LEVEL_SW_BOSSMETER_SERPENT = 128,
|
||||
LEVEL_SW_BOSSMETER_SUMO = 256,
|
||||
LEVEL_SW_BOSSMETER_ZILLA = 512,
|
||||
LEVEL_SW_DEATHEXIT_SERPENT = 1024,
|
||||
LEVEL_SW_DEATHEXIT_SUMO = 2048,
|
||||
LEVEL_SW_DEATHEXIT_ZILLA = 4096,
|
||||
|
||||
|
||||
};
|
||||
|
||||
// These get filled in by the map definition parsers of the front ends.
|
||||
extern FString gSkillNames[MAXSKILLS];
|
||||
extern FString gVolumeNames[MAXVOLUMES];
|
||||
extern FString gVolumeSubtitles[MAXVOLUMES];
|
||||
extern int32_t gVolumeFlags[MAXVOLUMES];
|
||||
extern int gDefaultVolume, gDefaultSkill;
|
||||
|
||||
|
||||
|
@ -46,6 +76,58 @@ enum {
|
|||
MAX_MESSAGES = 32
|
||||
};
|
||||
|
||||
class DObject;
|
||||
struct MapRecord;
|
||||
|
||||
struct CutsceneDef
|
||||
{
|
||||
FString video;
|
||||
FString function;
|
||||
FString soundName;
|
||||
int soundID = -1; // ResID not SoundID!
|
||||
int framespersec = 0; // only relevant for ANM.
|
||||
bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game.
|
||||
|
||||
void Create(DObject* runner);
|
||||
bool Create(DObject* runner, MapRecord* map, bool transition);
|
||||
bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); }
|
||||
int GetSound();
|
||||
};
|
||||
|
||||
struct GlobalCutscenes
|
||||
{
|
||||
CutsceneDef Intro;
|
||||
CutsceneDef DefaultMapIntro;
|
||||
CutsceneDef DefaultMapOutro;
|
||||
CutsceneDef DefaultGameover;
|
||||
CutsceneDef SharewareEnd;
|
||||
CutsceneDef LoadingScreen;
|
||||
FString MPSummaryScreen;
|
||||
FString SummaryScreen;
|
||||
};
|
||||
|
||||
struct ClusterDef
|
||||
{
|
||||
FString name; // What gets displayed for this cluster. In Duke this is normally the corresponding volume name but does not have to be.
|
||||
CutsceneDef intro; // plays when entering this cluster
|
||||
CutsceneDef outro; // plays when leaving this cluster
|
||||
CutsceneDef gameover; // when defined, plays when the player dies in this cluster
|
||||
FString InterBackground;
|
||||
int index = -1;
|
||||
int flags = 0; // engine and common flags
|
||||
int gameflags = 0; // game specific flags.
|
||||
};
|
||||
|
||||
struct VolumeRecord // episodes
|
||||
{
|
||||
FString startmap;
|
||||
FString name;
|
||||
FString subtitle;
|
||||
int index = -1;
|
||||
int flags = 0;
|
||||
int shortcut = 0;
|
||||
};
|
||||
|
||||
struct MapRecord
|
||||
{
|
||||
int parTime = 0;
|
||||
|
@ -54,16 +136,39 @@ struct MapRecord
|
|||
FString labelName;
|
||||
FString name;
|
||||
FString music;
|
||||
FString Author;
|
||||
FString NextMap;
|
||||
FString NextSecret;
|
||||
int cdSongId = -1;
|
||||
int musicorder = -1;
|
||||
|
||||
CutsceneDef intro;
|
||||
CutsceneDef outro;
|
||||
int flags = 0;
|
||||
int gameflags = 0;
|
||||
int levelNumber = -1;
|
||||
int mapindex = -1; // index in the episode. This only for finding the next map in the progression when nothing explicit is defined.
|
||||
int cluster = -1;
|
||||
|
||||
PalEntry fadeto = 0;
|
||||
int fogdensity = 0;
|
||||
int skyfog = 0;
|
||||
FString BorderTexture;
|
||||
FString InterBackground;
|
||||
TArray<FString> PrecacheTextures;
|
||||
FVector4 skyrotatevector;
|
||||
|
||||
// The rest is only used by Blood
|
||||
int nextLevel = -1;
|
||||
int nextSecret = -1;
|
||||
FString messages[MAX_MESSAGES];
|
||||
FString author;
|
||||
int8_t fog = -1, weather = -1; // Blood defines these but they aren't used.
|
||||
|
||||
// game specific stuff
|
||||
int rr_startsound = 0;
|
||||
int rr_mamaspawn = 15;
|
||||
int ex_ramses_horiz = 11;
|
||||
int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue!
|
||||
FString ex_ramses_pup;
|
||||
FString ex_ramses_text;
|
||||
|
||||
const char* LabelName() const
|
||||
{
|
||||
|
@ -97,39 +202,65 @@ struct MapRecord
|
|||
{
|
||||
messages[num] = msg;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
struct SummaryInfo
|
||||
{
|
||||
int kills;
|
||||
int maxkills;
|
||||
int secrets;
|
||||
int maxsecrets;
|
||||
int supersecrets;
|
||||
int time;
|
||||
int playercount;
|
||||
bool cheated;
|
||||
bool endofgame;
|
||||
};
|
||||
|
||||
extern MapRecord mapList[512];
|
||||
extern GlobalCutscenes globalCutscenes;
|
||||
extern MapRecord *currentLevel;
|
||||
|
||||
bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false);
|
||||
|
||||
MapRecord *FindMapByName(const char *nm);
|
||||
MapRecord *FindMapByLevelNum(int num);
|
||||
MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable.
|
||||
MapRecord* FindMapByIndex(int clst, int num);
|
||||
MapRecord *FindNextMap(MapRecord *thismap);
|
||||
MapRecord* FindNextSecretMap(MapRecord* thismap);
|
||||
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
|
||||
MapRecord* AllocateMap();
|
||||
|
||||
VolumeRecord* FindVolume(int index);
|
||||
ClusterDef* FindCluster(int index);
|
||||
ClusterDef* AllocateCluster();
|
||||
VolumeRecord* AllocateVolume();
|
||||
void SetLevelNum(MapRecord* info, int num);
|
||||
|
||||
inline VolumeRecord* MustFindVolume(int index)
|
||||
{
|
||||
auto r = FindVolume(index);
|
||||
if (r) return r;
|
||||
r = AllocateVolume();
|
||||
r->index = index;
|
||||
return r;
|
||||
}
|
||||
inline ClusterDef* MustFindCluster(int index)
|
||||
{
|
||||
auto r = FindCluster(index);
|
||||
if (r) return r;
|
||||
r = AllocateCluster();
|
||||
r->index = index;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// These should be the only places converting between level numbers and volume/map pairs
|
||||
constexpr inline int levelnum(int vol, int map)
|
||||
constexpr inline int makelevelnum(int vol, int map)
|
||||
{
|
||||
return vol * 1000 + map;
|
||||
}
|
||||
|
||||
constexpr inline int volfromlevelnum(int num)
|
||||
{
|
||||
return num >= 0 ? num / 1000 : 0;
|
||||
}
|
||||
|
||||
constexpr inline int mapfromlevelnum(int num)
|
||||
{
|
||||
return num >= 0 ? num % 1000 : -1;
|
||||
}
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
RRENDSLOT = 127
|
||||
|
|
|
@ -83,8 +83,31 @@ bool help_disabled;
|
|||
FNewGameStartup NewGameStartupInfo;
|
||||
|
||||
|
||||
|
||||
//FNewGameStartup NewGameStartupInfo;
|
||||
|
||||
static bool DoStartGame(FNewGameStartup& gs)
|
||||
{
|
||||
auto vol = FindVolume(gs.Episode);
|
||||
if (!vol) return false;
|
||||
|
||||
if (isShareware() && (vol->flags & VF_SHAREWARELOCK))
|
||||
{
|
||||
M_StartMessage(GStrings("SHAREWARELOCK"), 1, NAME_None);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto map = FindMapByName(vol->startmap);
|
||||
if (!map) return false;
|
||||
soundEngine->StopAllChannels();
|
||||
|
||||
gi->StartGame(gs); // play game specific effects (like Duke/RR/SW's voice lines when starting a game.)
|
||||
|
||||
DeferedStartGame(map, gs.Skill);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool M_SetSpecialMenu(FName& menu, int param)
|
||||
{
|
||||
|
@ -115,13 +138,19 @@ bool M_SetSpecialMenu(FName& menu, int param)
|
|||
|
||||
case NAME_Startgame:
|
||||
case NAME_StartgameNoSkill:
|
||||
menu = NAME_Startgame;
|
||||
NewGameStartupInfo.Skill = param;
|
||||
if (menu == NAME_StartgameNoSkill) NewGameStartupInfo.Episode = param;
|
||||
if (gi->StartGame(NewGameStartupInfo))
|
||||
if (menu == NAME_StartgameNoSkill)
|
||||
{
|
||||
menu = NAME_Startgame;
|
||||
NewGameStartupInfo.Episode = param;
|
||||
NewGameStartupInfo.Skill = 1;
|
||||
}
|
||||
if (DoStartGame(NewGameStartupInfo))
|
||||
{
|
||||
M_ClearMenus();
|
||||
STAT_StartNewGame(gVolumeNames[NewGameStartupInfo.Episode], NewGameStartupInfo.Skill);
|
||||
int ep = NewGameStartupInfo.Episode;
|
||||
auto vol = FindVolume(ep);
|
||||
if (vol) STAT_StartNewGame(vol->name, NewGameStartupInfo.Skill);
|
||||
inputState.ClearAllInput();
|
||||
}
|
||||
return false;
|
||||
|
@ -365,6 +394,7 @@ static DMenuItemBase* CreateCustomListMenuItemText(double x, double y, int heigh
|
|||
// Creates the episode menu
|
||||
//
|
||||
//=============================================================================
|
||||
extern TArray<VolumeRecord> volumes;
|
||||
|
||||
static void BuildEpisodeMenu()
|
||||
{
|
||||
|
@ -385,22 +415,22 @@ static void BuildEpisodeMenu()
|
|||
ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items
|
||||
int y = ld->mYpos;
|
||||
|
||||
for (int i = 0; i < MAXVOLUMES; i++)
|
||||
// Volume definitions should be sorted by intended menu order.
|
||||
for (auto &vol : volumes)
|
||||
{
|
||||
if (gVolumeNames[i].IsNotEmpty() && !(gVolumeFlags[i] & EF_HIDEFROMSP))
|
||||
|
||||
if (vol.name.IsNotEmpty() && !(vol.flags & VF_HIDEFROMSP))
|
||||
{
|
||||
int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && i > 0);
|
||||
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gVolumeNames[i][0],
|
||||
gVolumeNames[i], ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, i); // font colors are not used, so hijack one for the shareware flag.
|
||||
int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && (vol.flags & VF_SHAREWARELOCK));
|
||||
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, vol.name[0],
|
||||
vol.name, ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, vol.index); // font colors are not used, so hijack one for the shareware flag.
|
||||
|
||||
y += ld->mLinespacing;
|
||||
ld->mItems.Push(it);
|
||||
addedVolumes++;
|
||||
if (gVolumeSubtitles[i].IsNotEmpty())
|
||||
if (vol.subtitle.IsNotEmpty())
|
||||
{
|
||||
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing * 6 / 10, 1,
|
||||
gVolumeSubtitles[i], SmallFont, CR_GRAY, false, NAME_None, i);
|
||||
vol.subtitle, SmallFont, CR_GRAY, false, NAME_None, vol.index);
|
||||
y += ld->mLinespacing * 6 / 10;
|
||||
ld->mItems.Push(it);
|
||||
textadded = true;
|
||||
|
|
|
@ -49,15 +49,25 @@
|
|||
#include <vpx/vpx_decoder.h>
|
||||
#include <vpx/vp8dx.h>
|
||||
#include "raze_music.h"
|
||||
#include "vm.h"
|
||||
|
||||
|
||||
class MoviePlayer
|
||||
{
|
||||
protected:
|
||||
enum EMovieFlags
|
||||
{
|
||||
NOSOUNDCUTOFF = 1,
|
||||
FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros.
|
||||
};
|
||||
|
||||
int flags;
|
||||
public:
|
||||
virtual void Start() {}
|
||||
virtual bool Frame(uint64_t clock) = 0;
|
||||
virtual void Stop() {}
|
||||
virtual ~MoviePlayer() = default;
|
||||
virtual FTextureID GetTexture() = 0;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -76,16 +86,17 @@ class AnmPlayer : public MoviePlayer
|
|||
int frametime = 0;
|
||||
int nextframetime = 0;
|
||||
AnimTextures animtex;
|
||||
const AnimSound* animSnd;
|
||||
const int* frameTicks;
|
||||
bool nostopsound;
|
||||
const TArray<int> animSnd;
|
||||
int frameTicks[3];
|
||||
|
||||
public:
|
||||
bool isvalid() { return numframes > 0; }
|
||||
|
||||
AnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff)
|
||||
: animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff)
|
||||
AnmPlayer(FileReader& fr, TArray<int>& ans, const int *frameticks, int flags_)
|
||||
: animSnd(std::move(ans))
|
||||
{
|
||||
memcpy(frameTicks, frameticks, 3 * sizeof(int));
|
||||
flags = flags_;
|
||||
buffer = fr.ReadPadded(1);
|
||||
fr.Close();
|
||||
|
||||
|
@ -109,17 +120,12 @@ public:
|
|||
|
||||
if (currentclock < nextframetime - 1)
|
||||
{
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe));
|
||||
frametime = currentclock;
|
||||
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
||||
|
||||
int delay = 20;
|
||||
if (frameTicks)
|
||||
{
|
||||
|
@ -129,11 +135,12 @@ public:
|
|||
}
|
||||
nextframetime += delay;
|
||||
|
||||
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
for (unsigned i = 0; i < animSnd.Size(); i+=2)
|
||||
{
|
||||
if (animSnd[i].framenum == curframe)
|
||||
if (animSnd[i] == curframe)
|
||||
{
|
||||
int sound = animSnd[i].soundnum;
|
||||
int sound = animSnd[i+1];
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
|
@ -147,6 +154,7 @@ public:
|
|||
|
||||
void Stop() override
|
||||
{
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
|
@ -156,6 +164,11 @@ public:
|
|||
buffer.Reset();
|
||||
animtex.Clean();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return animtex.GetFrameID();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -187,8 +200,6 @@ public:
|
|||
{
|
||||
if (failed) return false;
|
||||
bool playon = decoder.RunFrame(clock);
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
return playon;
|
||||
}
|
||||
|
||||
|
@ -196,6 +207,11 @@ public:
|
|||
{
|
||||
decoder.Close();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return decoder.animTex().GetFrameID();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -209,7 +225,7 @@ class VpxPlayer : public MoviePlayer
|
|||
bool failed = false;
|
||||
FileReader fr;
|
||||
AnimTextures animtex;
|
||||
const AnimSound* animSnd;
|
||||
const TArray<int> animSnd;
|
||||
|
||||
unsigned width, height;
|
||||
TArray<uint8_t> Pic;
|
||||
|
@ -234,10 +250,10 @@ public:
|
|||
public:
|
||||
bool isvalid() { return !failed; }
|
||||
|
||||
VpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay, FString& error)
|
||||
VpxPlayer(FileReader& fr_, TArray<int>& animSnd_, int flags_, int origframedelay, FString& error) : animSnd(std::move(animSnd_))
|
||||
{
|
||||
fr = std::move(fr_);
|
||||
animSnd = animSnd_;
|
||||
flags = flags_;
|
||||
|
||||
if (!ReadIVFHeader(origframedelay))
|
||||
{
|
||||
|
@ -433,30 +449,35 @@ public:
|
|||
framenum++;
|
||||
if (framenum >= numframes) stop = true;
|
||||
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum;
|
||||
if (soundframe > lastsoundframe)
|
||||
{
|
||||
if (animSnd && soundtrack == -1) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||
if (soundtrack == -1)
|
||||
{
|
||||
if (animSnd[i].framenum == soundframe)
|
||||
for (unsigned i = 0; i < animSnd.Size(); i += 2)
|
||||
{
|
||||
int sound = animSnd[i].soundnum;
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
if (animSnd[i] == soundframe)
|
||||
{
|
||||
int sound = animSnd[i + 1];
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastsoundframe = soundframe;
|
||||
}
|
||||
}
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit, TAG_DONE);
|
||||
return !stop;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
Mus_Stop();
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
~VpxPlayer()
|
||||
|
@ -464,6 +485,11 @@ public:
|
|||
vpx_codec_destroy(&codec);
|
||||
animtex.Clean();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return animtex.GetFrameID();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -498,9 +524,10 @@ class SmkPlayer : public MoviePlayer
|
|||
bool fullscreenScale;
|
||||
uint64_t nFrameNs;
|
||||
int nFrame = 0;
|
||||
const AnimSound* animSnd;
|
||||
const TArray<int> animSnd;
|
||||
FString filename;
|
||||
SoundStream* stream = nullptr;
|
||||
bool hassound = false;
|
||||
|
||||
public:
|
||||
bool isvalid() { return hSMK.isValid; }
|
||||
|
@ -534,22 +561,21 @@ public:
|
|||
}
|
||||
|
||||
|
||||
SmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport)
|
||||
SmkPlayer(const char *fn, TArray<int>& ans, int flags_) : animSnd(std::move(ans))
|
||||
{
|
||||
hSMK = Smacker_Open(fn);
|
||||
if (!hSMK.isValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
flags = flags_;
|
||||
Smacker_GetFrameSize(hSMK, nWidth, nHeight);
|
||||
pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight));
|
||||
nFrameRate = Smacker_GetFrameRate(hSMK);
|
||||
nFrameNs = 1'000'000'000 / nFrameRate;
|
||||
nFrames = Smacker_GetNumFrames(hSMK);
|
||||
Smacker_GetPalette(hSMK, palette);
|
||||
fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480);
|
||||
|
||||
bool hassound = false;
|
||||
numAudioTracks = Smacker_GetNumAudioTracks(hSMK);
|
||||
if (numAudioTracks)
|
||||
{
|
||||
|
@ -562,14 +588,12 @@ public:
|
|||
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
||||
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
||||
else copy16bitSamples(read);
|
||||
animSnd = nullptr;
|
||||
hassound = true;
|
||||
}
|
||||
}
|
||||
if (!hassound)
|
||||
{
|
||||
adata.inf = {};
|
||||
animSnd = ans;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -612,27 +636,21 @@ public:
|
|||
}
|
||||
|
||||
}
|
||||
if (fullscreenScale)
|
||||
{
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawTexture(twod, animtex.GetFrame(), 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, TAG_DONE);
|
||||
}
|
||||
|
||||
if (frame > nFrame)
|
||||
{
|
||||
nFrame++;
|
||||
Smacker_GetNextFrame(hSMK);
|
||||
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!hassound) for (unsigned i = 0; i < animSnd.Size(); i += 2)
|
||||
{
|
||||
if (animSnd[i].framenum == nFrame)
|
||||
if (animSnd[i] == nFrame)
|
||||
{
|
||||
int sound = animSnd[i].soundnum;
|
||||
int sound = animSnd[i + 1];
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -640,13 +658,24 @@ public:
|
|||
return nFrame < nFrames;
|
||||
}
|
||||
|
||||
void Stop() override
|
||||
{
|
||||
if (stream) S_StopCustomStream(stream);
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound && !hassound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
~SmkPlayer()
|
||||
{
|
||||
Smacker_Close(hSMK);
|
||||
if (stream) S_StopCustomStream(stream);
|
||||
soundEngine->StopAllChannels();
|
||||
animtex.Clean();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return animtex.GetFrameID();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -655,61 +684,11 @@ public:
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DMoviePlayer : public DSkippableScreenJob
|
||||
{
|
||||
MoviePlayer* player;
|
||||
bool started = false;
|
||||
|
||||
public:
|
||||
DMoviePlayer(MoviePlayer* mp)
|
||||
{
|
||||
player = mp;
|
||||
pausable = false;
|
||||
}
|
||||
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
if (!player)
|
||||
{
|
||||
state = stopped;
|
||||
return;
|
||||
}
|
||||
if (!started)
|
||||
{
|
||||
started = true;
|
||||
player->Start();
|
||||
}
|
||||
uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate;
|
||||
if (state == running && !player->Frame(clock))
|
||||
{
|
||||
state = finished;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy() override
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
player->Stop();
|
||||
delete player;
|
||||
}
|
||||
player = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff, FString& error)
|
||||
MoviePlayer* OpenMovie(const char* filename, TArray<int>& ans, const int* frameticks, int flags, FString& error)
|
||||
{
|
||||
FileReader fr;
|
||||
// first try as .ivf - but only if sounds are provided - the decoder is video only.
|
||||
if (ans)
|
||||
if (ans.Size())
|
||||
{
|
||||
auto fn = StripExtension(filename);
|
||||
DefaultExtension(fn, ".ivf");
|
||||
|
@ -739,7 +718,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
|
||||
if (!memcmp(id, "LPF ", 4))
|
||||
{
|
||||
auto anm = new AnmPlayer(fr, ans, frameticks, nosoundcutoff);
|
||||
auto anm = new AnmPlayer(fr, ans, frameticks, flags);
|
||||
if (!anm->isvalid())
|
||||
{
|
||||
error.Format("%s: invalid ANM file.\n", filename);
|
||||
|
@ -751,7 +730,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
else if (!memcmp(id, "SMK2", 4))
|
||||
{
|
||||
fr.Close();
|
||||
auto anm = new SmkPlayer(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently.
|
||||
auto anm = new SmkPlayer(filename, ans, flags);
|
||||
if (!anm->isvalid())
|
||||
{
|
||||
error.Format("%s: invalid SMK file.\n", filename);
|
||||
|
@ -772,7 +751,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
}
|
||||
else if (!memcmp(id, "DKIF\0\0 \0VP80", 12))
|
||||
{
|
||||
auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, error);
|
||||
auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, flags, error);
|
||||
if (!anm->isvalid())
|
||||
{
|
||||
delete anm;
|
||||
|
@ -795,20 +774,53 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff)
|
||||
DEFINE_ACTION_FUNCTION(_MoviePlayer, Create)
|
||||
{
|
||||
if (!filename)
|
||||
{
|
||||
return Create<DBlackScreen>(1);
|
||||
}
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(filename);
|
||||
PARAM_POINTER(sndinf, TArray<int>);
|
||||
PARAM_INT(flags);
|
||||
PARAM_INT(frametime);
|
||||
PARAM_INT(firstframetime);
|
||||
PARAM_INT(lastframetime);
|
||||
|
||||
FString error;
|
||||
auto movie = OpenMovie(filename, ans, frameticks, nosoundcutoff, error);
|
||||
if (firstframetime == -1) firstframetime = frametime;
|
||||
if (lastframetime == -1) lastframetime = frametime;
|
||||
int frametimes[] = { firstframetime, frametime, lastframetime };
|
||||
auto movie = OpenMovie(filename, *sndinf, frametime == -1? nullptr : frametimes, flags, error);
|
||||
if (!movie)
|
||||
{
|
||||
Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars());
|
||||
return Create<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());
|
||||
}
|
||||
|
|
|
@ -39,8 +39,10 @@
|
|||
#include "hw_material.h"
|
||||
#include "gamestruct.h"
|
||||
#include "gamecontrol.h"
|
||||
#include "texturemanager.h"
|
||||
#include "hw_models.h"
|
||||
#include "hw_voxels.h"
|
||||
#include "mapinfo.h"
|
||||
|
||||
BEGIN_BLD_NS
|
||||
extern short voxelIndex[MAXTILES];
|
||||
|
@ -129,6 +131,19 @@ void precacheMarkedTiles()
|
|||
int dapalnum = pair->Key >> 32;
|
||||
doprecache(dapicnum, dapalnum);
|
||||
}
|
||||
|
||||
// Cache everything the map explicitly declares.
|
||||
TMap<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();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
|
||||
void PrecacheHardwareTextures(int nTile);
|
||||
void markTileForPrecache(int tilenum, int palnum);
|
||||
void markTextureForPrecache(const char* texname);
|
||||
void markVoxelForPrecache(int voxnum);
|
||||
void precacheMarkedTiles();
|
||||
|
|
|
@ -51,59 +51,371 @@
|
|||
#include <vpx/vpx_decoder.h>
|
||||
#include <vpx/vp8dx.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)
|
||||
|
||||
|
||||
bool DSkippableScreenJob::OnEvent(event_t* evt)
|
||||
{
|
||||
if (evt->type == EV_KeyDown && !specialKeyEvent(evt))
|
||||
{
|
||||
state = skipped;
|
||||
Skipped();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DBlackScreen::OnTick()
|
||||
{
|
||||
if (cleared)
|
||||
{
|
||||
int span = ticks * 1000 / GameTicRate;
|
||||
if (span > wait) state = finished;
|
||||
}
|
||||
}
|
||||
|
||||
void DBlackScreen::Draw(double)
|
||||
{
|
||||
cleared = true;
|
||||
twod->ClearScreen();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//=============================================================================
|
||||
|
||||
void DImageScreen::OnTick()
|
||||
void Job_Init()
|
||||
{
|
||||
if (cleared)
|
||||
static bool done = false;
|
||||
if (!done)
|
||||
{
|
||||
int span = ticks * 1000 / GameTicRate;
|
||||
if (span > waittime) state = finished;
|
||||
done = true;
|
||||
GC::AddMarkerFunc([] { GC::Mark(runner); });
|
||||
}
|
||||
runnerclass = PClass::FindClass("ScreenJobRunner");
|
||||
if (!runnerclass) I_FatalError("ScreenJobRunner not defined");
|
||||
runnerclasstype = NewPointer(runnerclass);
|
||||
|
||||
maprecordtype = NewPointer(NewStruct("MapRecord", nullptr, true));
|
||||
summaryinfotype = NewPointer(NewStruct("SummaryInfo", nullptr, true));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
static VMFunction* LookupFunction(const char* qname, bool validate = true)
|
||||
{
|
||||
int p = strcspn(qname, ".");
|
||||
if (p == 0) I_Error("Call to undefined function %s", qname);
|
||||
FString clsname(qname, p);
|
||||
FString funcname = qname + p + 1;
|
||||
|
||||
auto func = PClass::FindFunction(clsname, funcname);
|
||||
if (func == nullptr) I_Error("Call to undefined function %s", qname);
|
||||
if (validate)
|
||||
{
|
||||
// these conditions must be met by all functions for this interface.
|
||||
if (func->Proto->ReturnTypes.Size() != 0) I_Error("Bad cutscene function %s. Return value not allowed", qname);
|
||||
if (func->ImplicitArgs != 0) I_Error("Bad cutscene function %s. Must be static", qname);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CallCreateFunction(const char* qname, DObject* runner)
|
||||
{
|
||||
auto func = LookupFunction(qname);
|
||||
if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname);
|
||||
if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname);
|
||||
VMValue val = runner;
|
||||
VMCall(func, &val, 1, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map)
|
||||
{
|
||||
auto func = LookupFunction(qname);
|
||||
if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here.
|
||||
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
|
||||
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype)
|
||||
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname);
|
||||
VMValue val[2] = { runner, map };
|
||||
VMCall(func, val, 2, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* map, SummaryInfo* info, MapRecord* map2)
|
||||
{
|
||||
auto func = LookupFunction(qname);
|
||||
auto s = func->Proto->ArgumentTypes.Size();
|
||||
auto at = func->Proto->ArgumentTypes.Data();
|
||||
if (s != 3 && s != 4) I_Error("Bad map-cutscene function %s. Must receive precisely three or four arguments.", qname);
|
||||
if (at[0] != runnerclasstype && at[1] != maprecordtype && at[2] != summaryinfotype && (s == 3 || at[3] == maprecordtype))
|
||||
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference,", qname);
|
||||
if (info) summaryinfo = *info; // must be copied to a persistent location.
|
||||
else summaryinfo = {};
|
||||
VMValue val[] = { runner, map, &summaryinfo, map2 };
|
||||
VMCall(func, val, s, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
DObject* CreateRunner(bool clearbefore = true)
|
||||
{
|
||||
auto obj = runnerclass->CreateNew();
|
||||
auto func = LookupFunction("ScreenJobRunner.Init", false);
|
||||
VMValue val[3] = { obj, clearbefore, false };
|
||||
VMCall(func, val, 3, nullptr, 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps)
|
||||
{
|
||||
auto obj = runnerclass->CreateNew();
|
||||
auto func = LookupFunction("ScreenJobRunner.AddGenericVideo", false);
|
||||
VMValue val[] = { runner, &fn, soundid, fps };
|
||||
VMCall(func, val, 4, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CutsceneDef::Create(DObject* runner)
|
||||
{
|
||||
if (function.IsNotEmpty())
|
||||
{
|
||||
CallCreateFunction(function, runner);
|
||||
}
|
||||
else if (video.IsNotEmpty())
|
||||
{
|
||||
AddGenericVideo(runner, video, GetSound(), framespersec);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void DImageScreen::Draw(double smoothratio)
|
||||
bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition)
|
||||
{
|
||||
if (tilenum > 0) tex = tileGetTexture(tilenum, true);
|
||||
twod->ClearScreen();
|
||||
if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE);
|
||||
cleared = true;
|
||||
if (!transition && transitiononly) return false;
|
||||
if (function.CompareNoCase("none") == 0)
|
||||
return true; // play nothing but return as being validated
|
||||
if (function.IsNotEmpty())
|
||||
{
|
||||
CallCreateMapFunction(function, runner, map);
|
||||
return true;
|
||||
}
|
||||
else if (video.IsNotEmpty())
|
||||
{
|
||||
AddGenericVideo(runner, video, GetSound(), framespersec);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void DeleteScreenJob()
|
||||
{
|
||||
if (runner) runner->Destroy();
|
||||
runner = nullptr;
|
||||
}
|
||||
|
||||
void EndScreenJob()
|
||||
{
|
||||
DeleteScreenJob();
|
||||
if (completion) completion(false);
|
||||
completion = nullptr;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool ScreenJobResponder(event_t* ev)
|
||||
{
|
||||
if (ev->type == EV_KeyDown)
|
||||
{
|
||||
// We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
|
||||
auto binding = Bindings.GetBinding(ev->data1);
|
||||
if (binding.CompareNoCase("toggleconsole") == 0)
|
||||
{
|
||||
C_ToggleConsole();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FInputEvent evt = ev;
|
||||
if (runner)
|
||||
{
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent)
|
||||
{
|
||||
int result = 0;
|
||||
VMValue parm[] = { runner, &evt };
|
||||
VMReturn ret(&result);
|
||||
VMCall(func, parm, 2, &ret, 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool ScreenJobTick()
|
||||
{
|
||||
ticks++;
|
||||
if (runner)
|
||||
{
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick)
|
||||
{
|
||||
int result = 0;
|
||||
VMValue parm[] = { runner };
|
||||
VMReturn ret(&result);
|
||||
VMCall(func, parm, 1, &ret, 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void ScreenJobDraw()
|
||||
{
|
||||
double smoothratio = I_GetTimeFrac();
|
||||
|
||||
if (runner)
|
||||
{
|
||||
twod->ClearScreen();
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame)
|
||||
{
|
||||
VMValue parm[] = { runner, smoothratio };
|
||||
VMCall(func, parm, 2, nullptr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool ScreenJobValidate()
|
||||
{
|
||||
if (runner)
|
||||
{
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate)
|
||||
{
|
||||
int res;
|
||||
VMValue parm[] = { runner };
|
||||
VMReturn ret(&res);
|
||||
VMCall(func, parm, 2, &ret, 1);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_)
|
||||
{
|
||||
if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0)
|
||||
{
|
||||
completion = completion_;
|
||||
runner = CreateRunner();
|
||||
GC::WriteBarrier(runner);
|
||||
try
|
||||
{
|
||||
cs.Create(runner);
|
||||
if (!ScreenJobValidate())
|
||||
{
|
||||
runner->Destroy();
|
||||
runner = nullptr;
|
||||
return false;
|
||||
}
|
||||
if (flags & SJ_DELAY) intermissiondelay = 10; // need to wait a bit at the start to let the timer catch up.
|
||||
else intermissiondelay = 0;
|
||||
gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (runner) runner->Destroy();
|
||||
runner = nullptr;
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StartCutscene(const char* s, int flags, const CompletionFunc& completion)
|
||||
{
|
||||
CutsceneDef def;
|
||||
def.function = s;
|
||||
return StartCutscene(def, 0, completion);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic)
|
||||
{
|
||||
Mus_Stop();
|
||||
FX_StopAllSounds(); // JBF 20031228
|
||||
if (userConfig.nologo)
|
||||
{
|
||||
gameaction = def_ga;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!StartCutscene(globalCutscenes.Intro, SJ_BLOCKUI|SJ_DELAY, [=](bool) {
|
||||
gameaction = complete_ga;
|
||||
})) gameaction = def_ga;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -112,269 +424,137 @@ void DImageScreen::Draw(double smoothratio)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class ScreenJobRunner
|
||||
void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
|
||||
{
|
||||
enum
|
||||
{
|
||||
State_Clear,
|
||||
State_Run,
|
||||
State_Fadeout
|
||||
};
|
||||
TArray<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)
|
||||
{
|
||||
// We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
|
||||
auto binding = Bindings.GetBinding(ev->data1);
|
||||
if (binding.CompareNoCase("toggleconsole") == 0)
|
||||
{
|
||||
C_ToggleConsole();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (jobs[index].job->state != DScreenJob::running) return false;
|
||||
|
||||
return jobs[index].job->OnEvent(ev);
|
||||
}
|
||||
|
||||
void OnFinished()
|
||||
completion = completion_;
|
||||
runner = CreateRunner();
|
||||
GC::WriteBarrier(runner);
|
||||
|
||||
const char* qname = globalCutscenes.MPSummaryScreen;
|
||||
auto func = LookupFunction(qname);
|
||||
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
|
||||
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != TypeSInt32)
|
||||
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname);
|
||||
VMValue val[2] = { runner, numplayers };
|
||||
VMCall(func, val, 2, nullptr, 0);
|
||||
if (!ScreenJobValidate())
|
||||
{
|
||||
runner->Destroy();
|
||||
runner = nullptr;
|
||||
if (completion) completion(false);
|
||||
completion = nullptr; // only finish once.
|
||||
completion = nullptr;
|
||||
return;
|
||||
}
|
||||
gameaction = ga_intermission;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_)
|
||||
{
|
||||
completion = completion_;
|
||||
runner = CreateRunner();
|
||||
GC::WriteBarrier(runner);
|
||||
|
||||
// retrieve cluster relations for cluster-based cutscenes.
|
||||
ClusterDef* fromcluster = nullptr, *tocluster = nullptr;
|
||||
if (fromMap) fromcluster = FindCluster(fromMap->cluster);
|
||||
if (toMap) tocluster = FindCluster(toMap->cluster);
|
||||
if (fromcluster == tocluster) fromcluster = tocluster = nullptr;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (paused) return;
|
||||
if (index >= jobs.Size())
|
||||
if (fromMap)
|
||||
{
|
||||
//DeleteJobs();
|
||||
//twod->SetScreenFade(1);
|
||||
//twod->ClearScreen(); // This must not leave the 2d buffer empty.
|
||||
//if (gamestate == GS_INTRO) OnFinished();
|
||||
//else Net_WriteByte(DEM_ENDSCREENJOB); // intermissions must be terminated synchronously.
|
||||
if (!fromMap->outro.Create(runner, fromMap, !!toMap))
|
||||
{
|
||||
if (fromcluster == nullptr || !fromcluster->outro.Create(runner, fromMap, !!toMap))
|
||||
globalCutscenes.DefaultMapOutro.Create(runner, fromMap, !!toMap);
|
||||
}
|
||||
|
||||
}
|
||||
if (fromMap || (g_gameType & GAMEFLAG_PSEXHUMED))
|
||||
CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info, toMap);
|
||||
|
||||
if (toMap)
|
||||
{
|
||||
if (!toMap->intro.Create(runner, toMap, !!fromMap))
|
||||
{
|
||||
if (tocluster == nullptr || !tocluster->intro.Create(runner, toMap, !!fromMap))
|
||||
globalCutscenes.DefaultMapIntro.Create(runner, toMap, !!fromMap);
|
||||
}
|
||||
globalCutscenes.LoadingScreen.Create(runner, toMap, true);
|
||||
}
|
||||
else if (isShareware())
|
||||
{
|
||||
globalCutscenes.SharewareEnd.Create(runner);
|
||||
}
|
||||
if (!ScreenJobValidate())
|
||||
{
|
||||
runner->Destroy();
|
||||
runner = nullptr;
|
||||
if (completion) completion(false);
|
||||
completion = nullptr;
|
||||
return;
|
||||
}
|
||||
gameaction = ga_intermission;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (runner) runner->Destroy();
|
||||
runner = nullptr;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CCMD(testcutscene)
|
||||
{
|
||||
if (argv.argc() < 2)
|
||||
{
|
||||
Printf("Usage: testcutscene <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
|
||||
{
|
||||
if (jobs[index].job->state == DScreenJob::running)
|
||||
gameaction = ga_mainmenu;
|
||||
}
|
||||
RunScreenJob(jobs, [](bool) {
|
||||
Mus_Stop();
|
||||
gameaction = ga_mainmenu;
|
||||
}, SJ_BLOCKUI);
|
||||
|
||||
Exhumed:
|
||||
if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; });
|
||||
else gameaction = ga_mainmenu;
|
||||
|
||||
SW:
|
||||
if (!userConfig.nologo) Logo([](bool)
|
||||
{
|
||||
jobs[index].job->ticks++;
|
||||
jobs[index].job->OnTick();
|
||||
}
|
||||
else if (jobs[index].job->state == DScreenJob::stopping)
|
||||
{
|
||||
fadeticks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RunFrame()
|
||||
{
|
||||
if (index >= jobs.Size())
|
||||
{
|
||||
DeleteJobs();
|
||||
twod->SetScreenFade(1);
|
||||
twod->ClearScreen(); // This must not leave the 2d buffer empty.
|
||||
if (completion) completion(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensure that we won't go back in time if the menu is dismissed without advancing our ticker
|
||||
bool menuon = paused;
|
||||
if (menuon) last_paused_tic = jobs[index].job->ticks;
|
||||
else if (last_paused_tic == jobs[index].job->ticks) menuon = true;
|
||||
double smoothratio = menuon ? 1. : I_GetTimeFrac();
|
||||
|
||||
if (actionState == State_Clear)
|
||||
{
|
||||
actionState = State_Run;
|
||||
twod->ClearScreen();
|
||||
}
|
||||
else if (actionState == State_Run)
|
||||
{
|
||||
terminateState = DisplayFrame(smoothratio);
|
||||
if (terminateState < 1)
|
||||
{
|
||||
// Must lock before displaying.
|
||||
if (jobs[index].job->fadestyle & DScreenJob::fadeout)
|
||||
{
|
||||
jobs[index].job->fadestate = DScreenJob::fadeout;
|
||||
jobs[index].job->state = DScreenJob::stopping;
|
||||
actionState = State_Fadeout;
|
||||
fadeticks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AdvanceJob(terminateState < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (actionState == State_Fadeout)
|
||||
{
|
||||
int ended = FadeoutFrame(smoothratio);
|
||||
if (ended < 1)
|
||||
{
|
||||
jobs[index].job->state = DScreenJob::stopped;
|
||||
AdvanceJob(terminateState < 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
ScreenJobRunner *runner;
|
||||
|
||||
void RunScreenJob(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore, bool blockingui)
|
||||
{
|
||||
assert(completion != nullptr);
|
||||
videoclearFade();
|
||||
if (count)
|
||||
{
|
||||
runner = new ScreenJobRunner(jobs, count, completion, clearbefore);
|
||||
gameaction = blockingui? ga_intro : ga_intermission;
|
||||
}
|
||||
else
|
||||
{
|
||||
completion(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteScreenJob()
|
||||
{
|
||||
if (runner)
|
||||
{
|
||||
delete runner;
|
||||
runner = nullptr;
|
||||
}
|
||||
twod->SetScreenFade(1);
|
||||
}
|
||||
|
||||
void EndScreenJob()
|
||||
{
|
||||
if (runner) runner->OnFinished();
|
||||
DeleteScreenJob();
|
||||
}
|
||||
|
||||
|
||||
bool ScreenJobResponder(event_t* ev)
|
||||
{
|
||||
if (runner) return runner->OnEvent(ev);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScreenJobTick()
|
||||
{
|
||||
if (runner) runner->OnTick();
|
||||
}
|
||||
|
||||
bool ScreenJobDraw()
|
||||
{
|
||||
// we cannot recover from this because we have no completion callback to call.
|
||||
if (!runner)
|
||||
{
|
||||
// We can get here before a gameaction has been processed. In that case just draw a black screen and wait.
|
||||
if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job");
|
||||
twod->ClearScreen();
|
||||
return false;
|
||||
}
|
||||
auto res = runner->RunFrame();
|
||||
if (!res)
|
||||
{
|
||||
assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing);
|
||||
DeleteScreenJob();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
gameaction = ga_mainmenunostopsound;
|
||||
});
|
||||
else gameaction = ga_mainmenu;
|
||||
|
||||
*/
|
|
@ -3,153 +3,29 @@
|
|||
#include "dobject.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "d_eventbase.h"
|
||||
#include "s_soundinternal.h"
|
||||
#include "gamestate.h"
|
||||
|
||||
using CompletionFunc = std::function<void(bool)>;
|
||||
struct JobDesc;
|
||||
class ScreenJobRunner;
|
||||
|
||||
class DScreenJob : public DObject
|
||||
void Job_Init();
|
||||
|
||||
enum
|
||||
{
|
||||
DECLARE_CLASS(DScreenJob, DObject)
|
||||
const int fadestyle;
|
||||
const float fadetime; // in milliseconds
|
||||
int fadestate = fadein;
|
||||
|
||||
friend class ScreenJobRunner;
|
||||
protected:
|
||||
int ticks = 0;
|
||||
int state = running;
|
||||
bool pausable = true;
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
running = 1, // normal operation
|
||||
skipped = 2, // finished by user skipping
|
||||
finished = 3, // finished by completing its sequence
|
||||
stopping = 4, // running ending animations / fadeout, etc. Will not accept more input.
|
||||
stopped = 5, // we're done here.
|
||||
};
|
||||
enum
|
||||
{
|
||||
visible = 0,
|
||||
fadein = 1,
|
||||
fadeout = 2,
|
||||
};
|
||||
|
||||
DScreenJob(int fade = 0, float fadet = 250.f) : fadestyle(fade), fadetime(fadet) {}
|
||||
|
||||
virtual bool ProcessInput()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void Start() {}
|
||||
virtual bool OnEvent(event_t* evt) { return false; }
|
||||
virtual void OnTick() { /*state = finished;*/ }
|
||||
virtual void Draw(double smoothratio) {}
|
||||
|
||||
int DrawFrame(double smoothratio)
|
||||
{
|
||||
if (state != running) smoothratio = 1; // this is necessary because the ticker won't be incremented anymore to avoid having a negative time span.
|
||||
Draw(smoothratio);
|
||||
if (state == skipped) return -1;
|
||||
if (state == finished) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetFadeState() const { return fadestate; }
|
||||
|
||||
SJ_BLOCKUI = 1,
|
||||
SJ_DELAY = 2,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DSkippableScreenJob : public DScreenJob
|
||||
{
|
||||
protected:
|
||||
DSkippableScreenJob(int fade = 0, float fadet = 250.f) : DScreenJob(fade, fadet)
|
||||
{}
|
||||
|
||||
bool OnEvent(event_t* evt) override;
|
||||
virtual void Skipped() {}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DBlackScreen : public DScreenJob
|
||||
{
|
||||
int wait;
|
||||
bool cleared = false;
|
||||
|
||||
public:
|
||||
DBlackScreen(int w) : wait(w) {}
|
||||
void OnTick() override;
|
||||
void Draw(double smooth) override;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DImageScreen : public DSkippableScreenJob
|
||||
{
|
||||
DECLARE_CLASS(DImageScreen, DScreenJob)
|
||||
|
||||
int tilenum = -1;
|
||||
int trans;
|
||||
int waittime; // in ms.
|
||||
bool cleared = false;
|
||||
FGameTexture* tex = nullptr;
|
||||
|
||||
public:
|
||||
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
|
||||
{
|
||||
tex = tile;
|
||||
trans = translation;
|
||||
}
|
||||
|
||||
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
|
||||
{
|
||||
tilenum = tile;
|
||||
trans = translation;
|
||||
}
|
||||
|
||||
void OnTick() override;
|
||||
void Draw(double smooth) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct JobDesc
|
||||
{
|
||||
DScreenJob* job;
|
||||
void (*postAction)();
|
||||
bool ignoreifskipped;
|
||||
};
|
||||
|
||||
|
||||
void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true, bool blockingui = false);
|
||||
void EndScreenJob();
|
||||
void DeleteScreenJob();
|
||||
bool ScreenJobResponder(event_t* ev);
|
||||
void ScreenJobTick();
|
||||
bool ScreenJobDraw();
|
||||
bool ScreenJobTick();
|
||||
void ScreenJobDraw();
|
||||
|
||||
struct AnimSound
|
||||
{
|
||||
int framenum;
|
||||
int soundnum;
|
||||
};
|
||||
|
||||
DScreenJob *PlayVideo(const char *filename, const AnimSound *ans = nullptr, const int *frameticks = nullptr, bool nosoundstop = false);
|
||||
struct CutsceneDef;
|
||||
struct MapRecord;
|
||||
struct SummaryInfo;
|
||||
bool StartCutscene(const char* s, int flags, const CompletionFunc& completion);
|
||||
void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic);
|
||||
void ShowScoreboard(int numplayers, const CompletionFunc& completion_);
|
||||
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_);
|
||||
|
|
|
@ -398,6 +398,12 @@ static TArray<GrpInfo> ParseGrpInfo(const char *fn, FileReader &fr, TMap<FString
|
|||
FlagMap.Insert("GAMEFLAG_POWERSLAVE", GAMEFLAG_POWERSLAVE);
|
||||
FlagMap.Insert("GAMEFLAG_EXHUMED", GAMEFLAG_EXHUMED);
|
||||
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;
|
||||
auto mem = fr.Read();
|
||||
|
|
|
@ -240,7 +240,9 @@ void DBaseStatusBar::PrintAutomapInfo(FLevelStats& stats, bool forcetextfont)
|
|||
{
|
||||
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;
|
||||
|
||||
DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, mapname, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "src/callback.cpp"
|
||||
#include "src/choke.cpp"
|
||||
#include "src/controls.cpp"
|
||||
#include "src/credits.cpp"
|
||||
#include "src/db.cpp"
|
||||
#include "src/dude.cpp"
|
||||
#include "src/endgame.cpp"
|
||||
|
|
|
@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "v_draw.h"
|
||||
#include "texturemanager.h"
|
||||
#include "statusbar.h"
|
||||
#include "vm.h"
|
||||
|
||||
BEGIN_BLD_NS
|
||||
|
||||
|
@ -79,7 +80,7 @@ void EndLevel(void)
|
|||
seqKillAll();
|
||||
}
|
||||
|
||||
void StartLevel(MapRecord* level)
|
||||
void StartLevel(MapRecord* level, bool newgame)
|
||||
{
|
||||
if (!level) return;
|
||||
gFrameCount = 0;
|
||||
|
@ -96,14 +97,14 @@ void StartLevel(MapRecord* level)
|
|||
///////
|
||||
}
|
||||
#if 0
|
||||
else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel))
|
||||
else if (gGameOptions.nGameType > 0 && newgame)
|
||||
{
|
||||
// todo
|
||||
gBlueFlagDropped = false;
|
||||
gRedFlagDropped = false;
|
||||
}
|
||||
#endif
|
||||
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
|
||||
if (!newgame)
|
||||
{
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
|
@ -180,13 +181,13 @@ void StartLevel(MapRecord* level)
|
|||
evInit();
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
if (!(gGameOptions.uGameFlags & GF_AdvanceLevel))
|
||||
if (newgame)
|
||||
{
|
||||
playerInit(i, 0);
|
||||
}
|
||||
playerStart(i, 1);
|
||||
}
|
||||
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
|
||||
if (!newgame)
|
||||
{
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
|
@ -203,7 +204,6 @@ void StartLevel(MapRecord* level)
|
|||
pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon;
|
||||
}
|
||||
}
|
||||
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
|
||||
PreloadCache();
|
||||
InitMirrors();
|
||||
trInit();
|
||||
|
@ -222,43 +222,24 @@ void StartLevel(MapRecord* level)
|
|||
}
|
||||
|
||||
|
||||
void NewLevel(MapRecord *sng, int skill)
|
||||
void NewLevel(MapRecord *sng, int skill, bool newgame)
|
||||
{
|
||||
auto completion = [=](bool = false)
|
||||
{
|
||||
if (skill != -1) gGameOptions.nDifficulty = skill;
|
||||
gSkill = gGameOptions.nDifficulty;
|
||||
StartLevel(sng);
|
||||
gameaction = ga_level;
|
||||
};
|
||||
|
||||
bool startedCutscene = false;
|
||||
if (!(sng->flags & MI_USERMAP))
|
||||
{
|
||||
int episode = volfromlevelnum(sng->levelNumber);
|
||||
int level = mapfromlevelnum(sng->levelNumber);
|
||||
if (gEpisodeInfo[episode].cutALevel == level && gEpisodeInfo[episode].cutsceneAName[0])
|
||||
{
|
||||
levelPlayIntroScene(episode, completion);
|
||||
startedCutscene = true;
|
||||
}
|
||||
|
||||
}
|
||||
if (!startedCutscene) completion(false);
|
||||
|
||||
if (skill != -1) gGameOptions.nDifficulty = skill;
|
||||
gSkill = gGameOptions.nDifficulty;
|
||||
StartLevel(sng, newgame);
|
||||
gameaction = ga_level;
|
||||
}
|
||||
|
||||
void GameInterface::NewGame(MapRecord *sng, int skill, bool)
|
||||
{
|
||||
gGameOptions.uGameFlags = 0;
|
||||
cheatReset();
|
||||
NewLevel(sng, skill);
|
||||
NewLevel(sng, skill, true);
|
||||
}
|
||||
|
||||
void GameInterface::NextLevel(MapRecord *map, int skill)
|
||||
{
|
||||
gGameOptions.uGameFlags = GF_AdvanceLevel;
|
||||
NewLevel(map, skill);
|
||||
NewLevel(map, skill, false);
|
||||
}
|
||||
|
||||
void GameInterface::Ticker()
|
||||
|
@ -328,40 +309,12 @@ void GameInterface::Ticker()
|
|||
team_ticker[i] = 0;
|
||||
}
|
||||
|
||||
if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0)
|
||||
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
|
||||
{
|
||||
gGameOptions.uGameFlags &= ~GF_AdvanceLevel;
|
||||
seqKillAll();
|
||||
if (gGameOptions.uGameFlags & GF_EndGame)
|
||||
{
|
||||
STAT_Update(true);
|
||||
if (gGameOptions.nGameType == 0)
|
||||
{
|
||||
auto completion = [](bool) {
|
||||
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
|
||||
gameaction = ga_creditsmenu;
|
||||
};
|
||||
|
||||
if (gGameOptions.uGameFlags & GF_PlayCutscene)
|
||||
{
|
||||
levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion);
|
||||
}
|
||||
else completion(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
STAT_Update(false);
|
||||
EndLevel();
|
||||
Mus_Stop();
|
||||
// Fixme: Link maps, not episode/level pairs.
|
||||
int ep = volfromlevelnum(currentLevel->levelNumber);
|
||||
auto map = FindMapByLevelNum(levelnum(ep, gNextLevel));
|
||||
CompleteLevel(map);
|
||||
}
|
||||
STAT_Update(gNextLevel == nullptr);
|
||||
CompleteLevel(gNextLevel);
|
||||
}
|
||||
r_NoInterpolate = false;
|
||||
}
|
||||
|
@ -471,11 +424,12 @@ void GameInterface::app_init()
|
|||
|
||||
levelLoadDefaults();
|
||||
LoadDefinitions();
|
||||
|
||||
//---------
|
||||
SetTileNames();
|
||||
C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25);
|
||||
|
||||
TileFiles.SetBackup();
|
||||
powerupInit();
|
||||
Printf(PRINT_NONOTIFY, "Loading cosine table\n");
|
||||
trigInit();
|
||||
Printf(PRINT_NONOTIFY, "Initializing view subsystem\n");
|
||||
|
@ -485,15 +439,15 @@ void GameInterface::app_init()
|
|||
Printf(PRINT_NONOTIFY, "Initializing weapon animations\n");
|
||||
WeaponInit();
|
||||
|
||||
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
|
||||
sndInit();
|
||||
|
||||
myconnectindex = connecthead = 0;
|
||||
gNetPlayers = numplayers = 1;
|
||||
connectpoint2[0] = -1;
|
||||
gGameOptions.nGameType = 0;
|
||||
UpdateNetworkMenus();
|
||||
|
||||
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
|
||||
sndInit();
|
||||
|
||||
gChoke.init(518, chokeCallback);
|
||||
UpdateDacs(0, true);
|
||||
|
||||
|
@ -518,17 +472,7 @@ static void gameInit()
|
|||
void GameInterface::Startup()
|
||||
{
|
||||
gameInit();
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
|
||||
else
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
}
|
||||
}
|
||||
PlayLogos(ga_mainmenu, ga_mainmenu, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -578,4 +522,48 @@ ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize)
|
|||
return new GameInterface;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
kLoadScreenCRC = -2051908571,
|
||||
kLoadScreenWideBackWidth = 256,
|
||||
kLoadScreenWideSideWidth = 128,
|
||||
|
||||
};
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen)
|
||||
{
|
||||
static int bLoadScreenCrcMatch = -1;
|
||||
if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
|
||||
ACTION_RETURN_INT(bLoadScreenCrcMatch);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, PlayIntroMusic)
|
||||
{
|
||||
Mus_Play(nullptr, "PESTIS.MID", false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, sndStartSample)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(id);
|
||||
PARAM_INT(vol);
|
||||
PARAM_INT(chan);
|
||||
PARAM_BOOL(looped);
|
||||
PARAM_INT(chanflags);
|
||||
sndStartSample(id, vol, chan, looped, EChanFlags::FromInt(chanflags));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, sndStartSampleNamed)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(id);
|
||||
PARAM_INT(vol);
|
||||
PARAM_INT(chan);
|
||||
sndStartSample(id, vol, chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
END_BLD_NS
|
||||
|
|
|
@ -77,7 +77,6 @@ extern int blood_globalflags;
|
|||
|
||||
void QuitGame(void);
|
||||
void PreloadCache(void);
|
||||
void StartLevel(MapRecord *gameOptions);
|
||||
void ProcessFrame(void);
|
||||
void ScanINIFiles(void);
|
||||
void EndLevel();
|
||||
|
@ -121,7 +120,6 @@ struct GameInterface : ::GameInterface
|
|||
void MenuOpened() override;
|
||||
void MenuClosed() override;
|
||||
bool CanSave() override;
|
||||
bool StartGame(FNewGameStartup& gs) override;
|
||||
void QuitToTitle() override;
|
||||
FString GetCoordString() override;
|
||||
ReservedSpace GetReservedScreenSpace(int viewsize) override;
|
||||
|
|
|
@ -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
|
|
@ -159,23 +159,6 @@ bool GameInterface::CanSave()
|
|||
return (gamestate == GS_LEVEL && gPlayer[myconnectindex].pXSprite->health != 0);
|
||||
}
|
||||
|
||||
bool GameInterface::StartGame(FNewGameStartup& gs)
|
||||
{
|
||||
if (gs.Episode >= 1)
|
||||
{
|
||||
if (g_gameType & GAMEFLAG_SHAREWARE)
|
||||
{
|
||||
M_StartMessage(GStrings("BUYBLOOD"), 1, NAME_None); // unreachable because we do not support Blood SW versions yet.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sfxKillAllSounds();
|
||||
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
|
||||
DeferedStartGame(map, gs.Skill);
|
||||
return true;
|
||||
}
|
||||
|
||||
FSavegameInfo GameInterface::GetSaveSig()
|
||||
{
|
||||
return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD };
|
||||
|
|
|
@ -36,152 +36,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
BEGIN_BLD_NS
|
||||
|
||||
enum
|
||||
{
|
||||
kLoadScreenCRC = -2051908571,
|
||||
kLoadScreenWideBackWidth = 256,
|
||||
kLoadScreenWideSideWidth = 128,
|
||||
|
||||
};
|
||||
|
||||
static int bLoadScreenCrcMatch = -1;
|
||||
|
||||
static void drawTextScreenBackground(void)
|
||||
{
|
||||
if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
|
||||
|
||||
if (bLoadScreenCrcMatch)
|
||||
{
|
||||
if (ActiveRatio(twod->GetWidth(), twod->GetHeight()) < 1.34f)
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
int width = scale(twod->GetWidth(), 240, twod->GetHeight());
|
||||
int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth;
|
||||
for (int i = 0; i < nCount; i++)
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideBack), (i * kLoadScreenWideBackWidth), 0,
|
||||
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
|
||||
}
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideLeft), 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true, TAG_DONE);
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideRight), width - tileWidth(kLoadScreenWideRight), 0, DTA_TopLeft, true,
|
||||
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideMiddle), (width - tileWidth(kLoadScreenWideMiddle)) / 2, 0, DTA_TopLeft, true,
|
||||
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
// One these screens get scriptified this should use the version in BloodMenuDelegate.
|
||||
static void DrawCaption(const char* text)
|
||||
{
|
||||
double scalex = 1.; // Expand the box if the text is longer
|
||||
int width = BigFont->StringWidth(text);
|
||||
int boxwidth = tileWidth(2038);
|
||||
if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10);
|
||||
|
||||
DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE);
|
||||
DrawText(twod, BigFont, CR_UNDEFINED, 160 - width / 2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE);
|
||||
}
|
||||
|
||||
|
||||
class DBloodSummaryScreen : public DSkippableScreenJob
|
||||
{
|
||||
void DrawKills(void)
|
||||
{
|
||||
char pBuffer[40];
|
||||
if (gGameOptions.nGameType == 0)
|
||||
{
|
||||
viewDrawText(1, FStringf("%s:", GStrings("KILLS")), 75, 50, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40,"%2d", gKillMgr.Kills);
|
||||
viewDrawText(1, pBuffer, 160, 50, -128, 0, 0, 1);
|
||||
viewDrawText(1, GStrings("OF"), 190, 50, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%2d", gKillMgr.TotalKills);
|
||||
viewDrawText(1, pBuffer, 220, 50, -128, 0, 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewDrawText(3, "#", 85, 35, -128, 0, 0, 1);
|
||||
viewDrawText(3, GStrings("NAME"), 100, 35, -128, 0, 0, 1);
|
||||
viewDrawText(3, GStrings("FRAGS"), 210, 35, -128, 0, 0, 1);
|
||||
int nStart = 0;
|
||||
int nEnd = kMaxPlayers;
|
||||
|
||||
for (int i = nStart; i < nEnd; i++) if (playeringame[i])
|
||||
{
|
||||
mysnprintf(pBuffer, 40, "%-2d", i);
|
||||
viewDrawText(3, pBuffer, 85, 50 + 8 * i, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%s", PlayerName(i));
|
||||
viewDrawText(3, pBuffer, 100, 50 + 8 * i, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%d", gPlayer[i].fragCount);
|
||||
viewDrawText(3, pBuffer, 210, 50 + 8 * i, -128, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawSecrets(void)
|
||||
{
|
||||
char pBuffer[40];
|
||||
viewDrawText(1, FStringf("%s:", GStrings("TXT_SECRETS")), 75, 70, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Founds);
|
||||
viewDrawText(1, pBuffer, 160, 70, -128, 0, 0, 1);
|
||||
viewDrawText(1, GStrings("OF"), 190, 70, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Total);
|
||||
viewDrawText(1, pBuffer, 220, 70, -128, 0, 0, 1);
|
||||
if (gSecretMgr.Super > 0)
|
||||
viewDrawText(1, GStrings("TXT_SUPERSECRET"), 160, 100, -128, 2, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
drawTextScreenBackground();
|
||||
if (gGameOptions.nGameType == 0)
|
||||
{
|
||||
DrawCaption(GStrings("TXTB_LEVELSTATS"));
|
||||
if (bPlayerCheated)
|
||||
{
|
||||
auto text = GStrings("TXTB_CHEATED");
|
||||
int font = 3;
|
||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
viewDrawText(font, text, 160, 32, -128, 0, 1, font == 3);
|
||||
}
|
||||
DrawKills();
|
||||
DrawSecrets();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawCaption(GStrings("TXTB_FRAGSTATS"));
|
||||
DrawKills();
|
||||
}
|
||||
int myclock = ticks * 120 / GameTicRate;
|
||||
if ((myclock & 32))
|
||||
{
|
||||
auto text = GStrings("PRESSKEY");
|
||||
int font = 3;
|
||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
||||
{
|
||||
JobDesc job = { Create<DBloodSummaryScreen>() };
|
||||
sndStartSample(268, 128, -1, false, CHANF_UI);
|
||||
EndLevel();
|
||||
Mus_Stop();
|
||||
RunScreenJob(&job, 1, [=](bool)
|
||||
|
||||
SummaryInfo info{};
|
||||
|
||||
info.kills = gKillMgr.Kills;
|
||||
info.maxkills = gKillMgr.TotalKills;
|
||||
info.secrets = gSecretMgr.Founds;
|
||||
info.maxsecrets = gSecretMgr.Total;
|
||||
info.time = gSecretMgr.Super;
|
||||
info.endofgame = map == nullptr;
|
||||
|
||||
ShowIntermission(currentLevel, map, &info, [=](bool)
|
||||
{
|
||||
soundEngine->StopAllChannels();
|
||||
gameaction = ga_nextlevel;
|
||||
gameaction = map? ga_nextlevel : ga_creditsmenu;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -272,38 +146,4 @@ void SerializeGameStats(FSerializer& arc)
|
|||
CSecretMgr gSecretMgr;
|
||||
CKillMgr gKillMgr;
|
||||
|
||||
class DBloodLoadScreen : public DScreenJob
|
||||
{
|
||||
const char* pzLoadingScreenText1;
|
||||
MapRecord* rec;
|
||||
|
||||
public:
|
||||
DBloodLoadScreen(const char* caption, MapRecord* maprec) : DScreenJob(), rec(maprec)
|
||||
{
|
||||
if (gGameOptions.nGameType == 0) pzLoadingScreenText1 = GStrings("TXTB_LLEVEL");
|
||||
else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType));
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
drawTextScreenBackground();
|
||||
DrawCaption(pzLoadingScreenText1);
|
||||
viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1);
|
||||
|
||||
auto text = GStrings("TXTB_PLSWAIT");
|
||||
int font = 3;
|
||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
|
||||
viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3);
|
||||
}
|
||||
};
|
||||
|
||||
void loadscreen(const char *caption, MapRecord* rec, CompletionFunc func)
|
||||
{
|
||||
JobDesc job = { Create<DBloodLoadScreen>(caption, rec) };
|
||||
RunScreenJob(&job, 1, func);
|
||||
}
|
||||
|
||||
|
||||
END_BLD_NS
|
||||
|
|
|
@ -38,11 +38,8 @@ GAMEOPTIONS gSingleGameOptions = {
|
|||
0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200
|
||||
};
|
||||
|
||||
EPISODEINFO gEpisodeInfo[kMaxEpisodes+1];
|
||||
|
||||
int gSkill = 2;
|
||||
int gEpisodeCount;
|
||||
int gNextLevel; // fixme: let this contain a full level number.
|
||||
MapRecord* gNextLevel;
|
||||
|
||||
char BloodIniFile[BMAX_PATH] = "BLOOD.INI";
|
||||
bool bINIOverride = false;
|
||||
|
@ -94,22 +91,23 @@ void CheckKeyAbend(const char *pzSection, const char *pzKey)
|
|||
}
|
||||
|
||||
|
||||
void levelLoadMapInfo(IniFile *pIni, MapRecord *pLevelInfo, const char *pzSection, int epinum, int mapnum)
|
||||
|
||||
void levelLoadMapInfo(IniFile* pIni, MapRecord* pLevelInfo, const char* pzSection, int epinum, int mapnum, int* nextmap, int* nextsecret)
|
||||
{
|
||||
char buffer[16];
|
||||
pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName));
|
||||
pLevelInfo->author = pIni->GetKeyString(pzSection, "Author", "");
|
||||
pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", "");
|
||||
pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid");
|
||||
pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1);
|
||||
pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1);
|
||||
pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1);
|
||||
*nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0);
|
||||
*nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0);
|
||||
pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0);
|
||||
pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0);
|
||||
for (int i = 0; i < kMaxMessages; i++)
|
||||
{
|
||||
sprintf(buffer, "Message%d", i+1);
|
||||
auto msg = pIni->GetKeyString(pzSection, buffer, "");
|
||||
pLevelInfo->AddMessage(i, msg);
|
||||
sprintf(buffer, "Message%d", i + 1);
|
||||
auto msg = pIni->GetKeyString(pzSection, buffer, "");
|
||||
pLevelInfo->AddMessage(i, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,108 +140,101 @@ static const char* DefFile(void)
|
|||
return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini";
|
||||
}
|
||||
|
||||
static FString cleanPath(const char* pth)
|
||||
{
|
||||
FString path = pth;
|
||||
FixPathSeperator(path);
|
||||
if (FileExists(path)) return path;
|
||||
if (path.Len() > 3 && path[1] == ':' && isalpha(path[0]) && path[2] == '/')
|
||||
{
|
||||
return path.Mid(3);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void levelLoadDefaults(void)
|
||||
{
|
||||
char buffer[64];
|
||||
char buffer2[16];
|
||||
|
||||
int cutALevel = 0;
|
||||
|
||||
levelInitINI(DefFile());
|
||||
memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo));
|
||||
quoteMgr.InitializeQuote(MUS_INTRO, "PESTIS.MID");
|
||||
int i;
|
||||
for (i = 0; i < kMaxEpisodes; i++)
|
||||
for (i = 1; i <= kMaxEpisodes; i++)
|
||||
{
|
||||
sprintf(buffer, "Episode%d", i+1);
|
||||
sprintf(buffer, "Episode%d", i);
|
||||
if (!BloodINI->SectionExists(buffer))
|
||||
break;
|
||||
EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i];
|
||||
auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer);
|
||||
gVolumeNames[i] = ep_str; // only keep one table for the names. Todo: Consolidate this across games.
|
||||
strncpy(pEpisodeInfo->cutsceneAName, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH);
|
||||
pEpisodeInfo->at9028 = BloodINI->GetKeyInt(buffer, "CutWavA", -1);
|
||||
if (pEpisodeInfo->at9028 == 0)
|
||||
strncpy(pEpisodeInfo->cutsceneASound, BloodINI->GetKeyString(buffer, "CutWavA", ""), BMAX_PATH);
|
||||
else
|
||||
pEpisodeInfo->cutsceneASound[0] = 0;
|
||||
strncpy(pEpisodeInfo->cutsceneBName, BloodINI->GetKeyString(buffer, "CutSceneB", ""), BMAX_PATH);
|
||||
pEpisodeInfo->at902c = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
|
||||
if (pEpisodeInfo->at902c == 0)
|
||||
strncpy(pEpisodeInfo->cutsceneBSound, BloodINI->GetKeyString(buffer, "CutWavB", ""), BMAX_PATH);
|
||||
else
|
||||
pEpisodeInfo->cutsceneBSound[0] = 0;
|
||||
auto cluster = MustFindCluster(i);
|
||||
auto volume = MustFindVolume(i);
|
||||
CutsceneDef &csB = cluster->outro;
|
||||
auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer);
|
||||
cluster->name = volume->name = ep_str;
|
||||
if (i > 1) volume->flags |= VF_SHAREWARELOCK;
|
||||
|
||||
pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
|
||||
pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
|
||||
if (pEpisodeInfo->cutALevel > 0)
|
||||
pEpisodeInfo->cutALevel--;
|
||||
int j;
|
||||
for (j = 0; j < kMaxLevels; j++)
|
||||
csB.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneB", ""));
|
||||
int soundint = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
|
||||
if (soundint > 0) csB.soundID = soundint + 0x40000000;
|
||||
else csB.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", ""));
|
||||
|
||||
//pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
|
||||
cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
|
||||
if (cutALevel < 1) cutALevel = 1;
|
||||
|
||||
int nextmaps[kMaxLevels]{}, nextsecrets[kMaxLevels]{};
|
||||
for (int j = 1; j <= kMaxLevels; j++)
|
||||
{
|
||||
sprintf(buffer2, "Map%d", j+1);
|
||||
sprintf(buffer2, "Map%d", j);
|
||||
if (!BloodINI->KeyExists(buffer, buffer2))
|
||||
break;
|
||||
auto pLevelInfo = AllocateMap();
|
||||
const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL);
|
||||
CheckSectionAbend(pMap);
|
||||
pLevelInfo->levelNumber = levelnum(i, j);
|
||||
SetLevelNum(pLevelInfo, makelevelnum(i, j));
|
||||
pLevelInfo->cluster = i;
|
||||
pLevelInfo->mapindex = j;
|
||||
pLevelInfo->labelName = pMap;
|
||||
if (j == 1) volume->startmap = pLevelInfo->labelName;
|
||||
pLevelInfo->fileName.Format("%s.map", pMap);
|
||||
levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j);
|
||||
levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j, &nextmaps[j - 1], &nextsecrets[j - 1]);
|
||||
if (j == cutALevel)
|
||||
{
|
||||
CutsceneDef& csA = pLevelInfo->intro;
|
||||
csA.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneA", ""));
|
||||
int soundint = BloodINI->GetKeyInt(buffer, "CutWavA", -1);
|
||||
if (soundint > 0) csA.soundID = soundint + 0x40000000;
|
||||
else csA.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", ""));
|
||||
}
|
||||
}
|
||||
// Now resolve the level links
|
||||
for (int j = 1; j <= kMaxLevels; j++)
|
||||
{
|
||||
auto map = FindMapByIndexOnly(i, j);
|
||||
if (map)
|
||||
{
|
||||
if (nextmaps[j - 1] > 0)
|
||||
{
|
||||
auto nmap = FindMapByIndexOnly(i, nextmaps[j - 1]);
|
||||
if (nmap) map->NextMap = nmap->labelName;
|
||||
else map->NextMap = "-";
|
||||
}
|
||||
if (nextsecrets[j - 1] > 0)
|
||||
{
|
||||
auto nmap = FindMapByIndexOnly(i, nextsecrets[j - 1]);
|
||||
if (nmap) map->NextSecret = nmap->labelName;
|
||||
else map->NextSecret = "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
pEpisodeInfo->nLevels = j;
|
||||
}
|
||||
gEpisodeCount = i;
|
||||
}
|
||||
|
||||
void levelGetNextLevels(int *pnEndingA, int *pnEndingB)
|
||||
void levelEndLevel(int secret)
|
||||
{
|
||||
assert(pnEndingA != NULL && pnEndingB != NULL);
|
||||
int nEndingA = currentLevel->nextLevel;
|
||||
if (nEndingA >= 0)
|
||||
nEndingA--;
|
||||
int nEndingB = currentLevel->nextSecret;
|
||||
if (nEndingB >= 0)
|
||||
nEndingB--;
|
||||
*pnEndingA = nEndingA;
|
||||
*pnEndingB = nEndingB;
|
||||
}
|
||||
|
||||
void levelEndLevel(int arg)
|
||||
{
|
||||
int nEndingA, nEndingB;
|
||||
auto episode = volfromlevelnum(currentLevel->levelNumber);
|
||||
EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[episode];
|
||||
gGameOptions.uGameFlags |= GF_AdvanceLevel;
|
||||
levelGetNextLevels(&nEndingA, &nEndingB);
|
||||
switch (arg)
|
||||
{
|
||||
case 0:
|
||||
if (nEndingA == -1)
|
||||
{
|
||||
if (pEpisodeInfo->cutsceneBName[0])
|
||||
gGameOptions.uGameFlags |= GF_PlayCutscene;
|
||||
gGameOptions.uGameFlags |= GF_EndGame;
|
||||
}
|
||||
else
|
||||
gNextLevel = nEndingA;
|
||||
break;
|
||||
case 1:
|
||||
if (nEndingB == -1)
|
||||
{
|
||||
if (episode + 1 < gEpisodeCount)
|
||||
{
|
||||
if (pEpisodeInfo->cutsceneBName[0])
|
||||
gGameOptions.uGameFlags |= GF_PlayCutscene;
|
||||
gGameOptions.uGameFlags |= GF_EndGame;
|
||||
}
|
||||
else
|
||||
{
|
||||
gGameOptions.uGameFlags |= GF_AdvanceLevel;
|
||||
}
|
||||
}
|
||||
else
|
||||
gNextLevel = nEndingB;
|
||||
break;
|
||||
}
|
||||
if (!secret) gNextLevel = FindNextMap(currentLevel);
|
||||
else gNextLevel = FindNextSecretMap(currentLevel);
|
||||
}
|
||||
|
||||
void levelTryPlayMusic()
|
||||
|
|
|
@ -41,7 +41,6 @@ enum
|
|||
enum EGameFlag
|
||||
{
|
||||
GF_AdvanceLevel = 1,
|
||||
GF_EndGame = 2,
|
||||
// 4 was for playing intro cutscenes but is no longer used.
|
||||
GF_PlayCutscene = 8,
|
||||
};
|
||||
|
@ -67,38 +66,16 @@ struct GAMEOPTIONS {
|
|||
|
||||
#pragma pack(pop)
|
||||
|
||||
enum {
|
||||
MUS_INTRO = 0,
|
||||
MUS_LOADING = 1,
|
||||
};
|
||||
|
||||
struct EPISODEINFO
|
||||
{
|
||||
int nLevels;
|
||||
unsigned int bloodbath : 1;
|
||||
unsigned int cutALevel : 4;
|
||||
char cutsceneAName[BMAX_PATH];
|
||||
char cutsceneBName[BMAX_PATH];
|
||||
int at9028;
|
||||
int at902c;
|
||||
char cutsceneASound[BMAX_PATH];
|
||||
char cutsceneBSound[BMAX_PATH];
|
||||
};
|
||||
|
||||
extern EPISODEINFO gEpisodeInfo[];
|
||||
extern GAMEOPTIONS gSingleGameOptions;
|
||||
extern GAMEOPTIONS gGameOptions;
|
||||
extern int gSkill;
|
||||
extern char BloodIniFile[];
|
||||
extern bool bINIOverride;
|
||||
extern int gEpisodeCount;
|
||||
extern int gNextLevel;
|
||||
extern MapRecord* gNextLevel;
|
||||
extern bool gGameStarted;
|
||||
|
||||
void levelInitINI(const char *pzIni);
|
||||
void levelOverrideINI(const char *pzIni);
|
||||
void levelPlayIntroScene(int nEpisode, CompletionFunc completion);
|
||||
void levelPlayEndScene(int nEpisode, CompletionFunc completion);
|
||||
void levelSetupSecret(int nCount);
|
||||
void levelTriggerSecret(int nSecret);
|
||||
void CheckSectionAbend(const char *pzSection);
|
||||
|
|
|
@ -641,7 +641,6 @@ void SerializeState(FSerializer& arc)
|
|||
("modern", gModernMap)
|
||||
#endif
|
||||
("cheating", bPlayerCheated)
|
||||
("nextlevel", gNextLevel)
|
||||
("skyhoriz", pSky->horizfrac)
|
||||
("skyy", pSky->yoffs)
|
||||
("scale", pSky->yscale)
|
||||
|
|
|
@ -267,10 +267,8 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2)
|
|||
{
|
||||
if (!nArg1 || !nArg2 || strlen(pzArgs) < 3)
|
||||
return -1;
|
||||
*nArg1 = pzArgs[0] - '0' - 1;
|
||||
*nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1;
|
||||
*nArg1 = ClipRange(*nArg1, 0, gEpisodeCount-1);
|
||||
*nArg2 = ClipRange(*nArg2, 0, gEpisodeInfo[*nArg1].nLevels-1);
|
||||
*nArg1 = pzArgs[0] - '0';
|
||||
*nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0');
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
@ -423,7 +421,7 @@ static bool cheatMario(cheatseq_t* c)
|
|||
int nEpisode, nLevel;
|
||||
if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2)
|
||||
{
|
||||
auto map = FindMapByLevelNum(levelnum(nEpisode, nLevel));
|
||||
auto map = FindMapByIndex(nEpisode, nLevel);
|
||||
if (map) DeferedStartGame(map, -1);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// names for everything that gets accessed by scripts.
|
||||
x(MENUBAR, 2038)
|
||||
x(BackTile, 253)
|
||||
x(Monolithscreen, 2050)
|
||||
x(GTIScreen, 2052)
|
||||
x(TitleScreen, 2518)
|
||||
|
||||
x(CrosshairTile, 2319)
|
||||
x(LoadScreen, 2049)
|
||||
|
|
|
@ -5214,13 +5214,7 @@ void seqSpawnerOffSameTx(XSPRITE* pXSource) {
|
|||
void levelEndLevelCustom(int nLevel) {
|
||||
|
||||
gGameOptions.uGameFlags |= GF_AdvanceLevel;
|
||||
|
||||
if (nLevel >= 16 || nLevel < 0) {
|
||||
gGameOptions.uGameFlags |= GF_EndGame;
|
||||
return;
|
||||
}
|
||||
|
||||
gNextLevel = nLevel;
|
||||
gNextLevel = FindMapByIndex(currentLevel->cluster, nLevel + 1);
|
||||
}
|
||||
|
||||
void callbackUniMissileBurst(int nSprite) // 22
|
||||
|
|
|
@ -400,10 +400,6 @@ void powerupClear(PLAYER *pPlayer)
|
|||
}
|
||||
}
|
||||
|
||||
void powerupInit(void)
|
||||
{
|
||||
}
|
||||
|
||||
int packItemToPowerup(int nPack)
|
||||
{
|
||||
int nPowerUp = -1;
|
||||
|
|
|
@ -241,7 +241,6 @@ void powerupDeactivate(PLAYER *pPlayer, int nPowerUp);
|
|||
void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState);
|
||||
void powerupProcess(PLAYER *pPlayer);
|
||||
void powerupClear(PLAYER *pPlayer);
|
||||
void powerupInit(void);
|
||||
int packItemToPowerup(int nPack);
|
||||
int powerupToPackItem(int nPowerUp);
|
||||
char packAddItem(PLAYER *pPlayer, unsigned int nPack);
|
||||
|
|
|
@ -218,7 +218,7 @@ private:
|
|||
stats.font = SmallFont;
|
||||
stats.letterColor = CR_DARKRED;
|
||||
stats.standardColor = CR_DARKGRAY;
|
||||
stats.time = Scale(gFrameCount, 1000, kTicsPerSec);
|
||||
stats.time = gFrameCount / GameTicRate;
|
||||
|
||||
if (automapMode == am_full)
|
||||
{
|
||||
|
@ -248,6 +248,7 @@ private:
|
|||
stats.secrets = gSecretMgr.Founds;
|
||||
stats.supersecrets = gSecretMgr.Super;
|
||||
stats.maxsecrets = max(gSecretMgr.Founds, gSecretMgr.Total); // If we found more than there are, increase the total. Some levels have a bugged counter.
|
||||
stats.time = Scale(PlayClock, 1000, 120);
|
||||
|
||||
DBaseStatusBar::PrintLevelStats(stats);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
@ -3697,7 +3697,7 @@ void moveeffectors_d(void) //STATNUM 3
|
|||
{
|
||||
static int16_t list1[] = { BLOODPOOL, PUKE, FOOTPRINTS, FOOTPRINTS2, FOOTPRINTS3, FOOTPRINTS4, BULLETHOLE, BLOODSPLAT1, BLOODSPLAT2, BLOODSPLAT3, BLOODSPLAT4, -1 };
|
||||
static int16_t list2[] = { BOLT1, BOLT1 + 1,BOLT1 + 2, BOLT1 + 3, SIDEBOLT1, SIDEBOLT1 + 1, SIDEBOLT1 + 2, SIDEBOLT1 + 3, -1 };
|
||||
handle_se24(act, list1, list2, false, TRIPBOMB, LASERLINE, CRANE, 2);
|
||||
handle_se24(act, list1, list2, true, TRIPBOMB, LASERLINE, CRANE, 2);
|
||||
break;
|
||||
}
|
||||
case SE_35:
|
||||
|
|
|
@ -292,11 +292,11 @@ static bool cheatItems(int player)
|
|||
static bool cheatLevel(cheatseq_t *s)
|
||||
{
|
||||
int volnume,levnume;
|
||||
volnume = s->Args[0] - '0' - 1;
|
||||
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1;
|
||||
volnume = s->Args[0] - '0';
|
||||
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0');
|
||||
|
||||
// Instead of hard coded range checks on volume and level, let's just check if the level is defined.
|
||||
auto map = FindMapByLevelNum(levelnum(volnume, levnume));
|
||||
auto map = FindMapByIndex(volnume, levnume);
|
||||
if (map)
|
||||
{
|
||||
ChangeLevel(map, -1);
|
||||
|
|
|
@ -97,19 +97,8 @@ bool GameInterface::CanSave()
|
|||
|
||||
bool GameInterface::StartGame(FNewGameStartup& gs)
|
||||
{
|
||||
if (gs.Episode >= 1)
|
||||
{
|
||||
if (g_gameType & GAMEFLAG_SHAREWARE)
|
||||
{
|
||||
M_StartMessage(GStrings("BUYDUKE"), 1, NAME_None);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t skillsound = PISTOL_BODYHIT;
|
||||
|
||||
soundEngine->StopAllChannels();
|
||||
|
||||
static const short sounds_d[] = { JIBBED_ACTOR6, BONUS_SPEECH1, DUKE_GETWEAPON2, JIBBED_ACTOR5, JIBBED_ACTOR5 };
|
||||
static const short sounds_r[] = { 427, 428, 196, 195, 197 };
|
||||
if (gs.Skill >=0 && gs.Skill <= 5) skillsound = isRR()? sounds_r[gs.Skill] : sounds_d[gs.Skill];
|
||||
|
@ -126,15 +115,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
|
|||
}
|
||||
Net_ClearFifo();
|
||||
}
|
||||
|
||||
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
|
||||
if (map)
|
||||
{
|
||||
DeferedStartGame(map, gs.Skill);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FSavegameInfo GameInterface::GetSaveSig()
|
||||
|
|
|
@ -105,12 +105,8 @@ void think_r();
|
|||
void animatesprites_d(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio);
|
||||
void animatesprites_r(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio);
|
||||
|
||||
void Logo_d(const CompletionFunc&);
|
||||
void Logo_r(const CompletionFunc&);
|
||||
void InitFonts_d();
|
||||
void InitFonts_r();
|
||||
void PrintPaused_d();
|
||||
void PrintPaused_r();
|
||||
|
||||
|
||||
Dispatcher fi;
|
||||
|
@ -120,9 +116,7 @@ void SetDispatcher()
|
|||
if (!isRR())
|
||||
{
|
||||
fi = {
|
||||
Logo_d,
|
||||
InitFonts_d,
|
||||
PrintPaused_d,
|
||||
|
||||
think_d,
|
||||
initactorflags_d,
|
||||
|
@ -167,9 +161,7 @@ void SetDispatcher()
|
|||
else
|
||||
{
|
||||
fi = {
|
||||
Logo_r,
|
||||
InitFonts_r,
|
||||
PrintPaused_r,
|
||||
|
||||
think_r,
|
||||
initactorflags_r,
|
||||
|
@ -235,8 +227,6 @@ int TILE_STATIC;
|
|||
int TILE_BOTTOMSTATUSBAR;
|
||||
int TILE_THREEDEE;
|
||||
int TILE_INGAMEDUKETHREEDEE;
|
||||
int TILE_PLUTOPAKSPRITE;
|
||||
int TILE_MENUBAR;
|
||||
int TILE_ATOMICHEALTH;
|
||||
int TILE_FLOORSLIME;
|
||||
int TILE_JIBS6;
|
||||
|
|
|
@ -76,9 +76,7 @@ struct GameInterface : public ::GameInterface
|
|||
struct Dispatcher
|
||||
{
|
||||
// global stuff
|
||||
void (*ShowLogo)(const CompletionFunc& completion);
|
||||
void (*InitFonts)();
|
||||
void (*PrintPaused)();
|
||||
|
||||
// sectors_?.cpp
|
||||
void (*think)();
|
||||
|
|
|
@ -261,10 +261,6 @@ void initactorflags_d()
|
|||
TILE_CAMLIGHT = CAMLIGHT;
|
||||
TILE_STATIC = STATIC;
|
||||
TILE_BOTTOMSTATUSBAR = isWorldTour()? WIDESCREENSTATUSBAR : BOTTOMSTATUSBAR;
|
||||
TILE_THREEDEE = THREEDEE;
|
||||
TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE;
|
||||
TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE;
|
||||
TILE_MENUBAR = MENUBAR;
|
||||
TILE_ATOMICHEALTH = ATOMICHEALTH;
|
||||
TILE_FLOORSLIME = FLOORSLIME;
|
||||
TILE_JIBS6 = JIBS6;
|
||||
|
|
|
@ -235,10 +235,6 @@ void initactorflags_r()
|
|||
TILE_CAMLIGHT = CAMLIGHT;
|
||||
TILE_STATIC = STATIC;
|
||||
TILE_BOTTOMSTATUSBAR = BOTTOMSTATUSBAR;
|
||||
TILE_THREEDEE = THREEDEE;
|
||||
TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE;
|
||||
TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE;
|
||||
TILE_MENUBAR = MENUBAR;
|
||||
TILE_ATOMICHEALTH = ATOMICHEALTH;
|
||||
TILE_FLOORSLIME = FLOORSLIME;
|
||||
TILE_JIBS6 = JIBS6;
|
||||
|
|
|
@ -162,9 +162,6 @@ int hits(DDukeActor* snum);
|
|||
DDukeActor* LocateTheLocator(int n, int sectnum);
|
||||
void clearcamera(player_struct* ps);
|
||||
|
||||
void showtwoscreens(const CompletionFunc& func);
|
||||
void doorders(const CompletionFunc& func);
|
||||
|
||||
void LoadActor(DDukeActor* i, int p, int x);
|
||||
void execute(DDukeActor* s, int p, int d);
|
||||
void makeitfall(DDukeActor* s);
|
||||
|
@ -211,8 +208,6 @@ void OffBoat(player_struct *pl);
|
|||
|
||||
void cameratext(DDukeActor* i);
|
||||
void dobonus(int bonusonly, const CompletionFunc& completion);
|
||||
void dobonus_d(int bonusonly, const CompletionFunc& completion);
|
||||
void dobonus_r(int bonusonly, const CompletionFunc& completion);
|
||||
|
||||
void drawoverlays(double smoothratio);
|
||||
void drawbackground(void);
|
||||
|
@ -227,7 +222,6 @@ void e4intro(const CompletionFunc& completion);
|
|||
void exitlevel(MapRecord *next);
|
||||
void enterlevel(MapRecord* mi, int gm);
|
||||
void donewgame(MapRecord* map, int sk);
|
||||
void startnewgame(MapRecord* map, int skill);
|
||||
int playercolor2lookup(int color);
|
||||
void PlayerColorChanged(void);
|
||||
bool movementBlocked(player_struct *p);
|
||||
|
|
|
@ -92,13 +92,12 @@ static void endthegame(bool)
|
|||
|
||||
void GameInterface::ExitFromMenu()
|
||||
{
|
||||
#if 0
|
||||
// do we really need this scoreboard stuff here?
|
||||
auto runbonus = [=](auto completion)
|
||||
{
|
||||
// MP scoreboard
|
||||
if (playerswhenstarted > 1 && !ud.coop)
|
||||
{
|
||||
dobonus(1, completion);
|
||||
}
|
||||
if (playerswhenstarted > 1 && !ud.coop) ShowScoreboard(playerswhenstarted);
|
||||
else completion(false);
|
||||
};
|
||||
|
||||
|
@ -106,11 +105,16 @@ void GameInterface::ExitFromMenu()
|
|||
{
|
||||
// shareware and TEN screens
|
||||
if (isShareware() && !isRR())
|
||||
showtwoscreens(completion);
|
||||
StartCutscene("DukeCutscenes.BuildSharewareExit", 0, completion);
|
||||
else completion(false);
|
||||
};
|
||||
|
||||
runbonus([=](bool aborted) { runtwoscreens(endthegame); });
|
||||
#else
|
||||
if (isShareware() && !isRR())
|
||||
StartCutscene("DukeCutscenes.BuildSharewareExit", 0, endthegame);
|
||||
else endthegame(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -297,7 +301,13 @@ void drawoverlays(double smoothratio)
|
|||
}
|
||||
|
||||
if (paused == 2)
|
||||
fi.PrintPaused();
|
||||
{
|
||||
double x = 160, y = 100;
|
||||
double scale = isRR() ? 0.4 : 1.;
|
||||
const char* text = GStrings("Game Paused");
|
||||
x -= BigFont->StringWidth(text) * 0.5 * scale;
|
||||
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -342,18 +352,6 @@ void cameratext(DDukeActor *cam)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void dobonus(int bonusonly, const CompletionFunc& completion)
|
||||
{
|
||||
if (isRR()) dobonus_r(bonusonly, completion);
|
||||
else dobonus_d(bonusonly, completion);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
int startrts(int lumpNum, int localPlayer)
|
||||
{
|
||||
if (SoundEnabled() &&
|
||||
|
|
|
@ -48,11 +48,12 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
|
|||
#include "conlabeldef.h"
|
||||
#include "gi.h"
|
||||
|
||||
extern TArray<TPointer<MapRecord>> mapList;
|
||||
|
||||
BEGIN_DUKE_NS
|
||||
|
||||
enum { VERSIONCHECK = 41 };
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// definitions needed by the parser.
|
||||
|
@ -88,7 +89,8 @@ public:
|
|||
|
||||
struct TempMusic
|
||||
{
|
||||
int levnum;
|
||||
int volnum;
|
||||
int levlnum;
|
||||
FString music;
|
||||
};
|
||||
|
||||
|
@ -1016,7 +1018,8 @@ int ConCompiler::parsecommand()
|
|||
if (k >= 0)
|
||||
{
|
||||
tempMusic.Reserve(1);
|
||||
tempMusic.Last().levnum = levelnum(k, i);
|
||||
tempMusic.Last().volnum = k + 1;
|
||||
tempMusic.Last().levlnum = i + 1;
|
||||
tempMusic.Last().music = parsebuffer.Data();
|
||||
}
|
||||
else
|
||||
|
@ -1644,6 +1647,7 @@ int ConCompiler::parsecommand()
|
|||
return 0;
|
||||
|
||||
case concmd_definevolumename:
|
||||
{
|
||||
popscriptvalue();
|
||||
transnum(LABEL_DEFINE);
|
||||
j = popscriptvalue();
|
||||
|
@ -1658,8 +1662,13 @@ int ConCompiler::parsecommand()
|
|||
textptr++, i++;
|
||||
}
|
||||
parsebuffer.Push(0);
|
||||
gVolumeNames[j] = FStringTable::MakeMacro(parsebuffer.Data(), i);
|
||||
// We need both a volume and a cluster for this new episode.
|
||||
auto vol = MustFindVolume(j);
|
||||
auto clust = MustFindCluster(j + 1);
|
||||
vol->name = clust->name = FStringTable::MakeMacro(parsebuffer.Data(), i);
|
||||
if (j > 0) vol->flags |= VF_SHAREWARELOCK;
|
||||
return 0;
|
||||
}
|
||||
case concmd_defineskillname:
|
||||
popscriptvalue();
|
||||
transnum(LABEL_DEFINE);
|
||||
|
@ -1695,25 +1704,32 @@ int ConCompiler::parsecommand()
|
|||
textptr++, i++;
|
||||
}
|
||||
parsebuffer.Push(0);
|
||||
auto levnum = levelnum(j, k);
|
||||
auto map = FindMapByLevelNum(levnum);
|
||||
auto map = FindMapByIndexOnly(j + 1, k + 1);
|
||||
if (!map) map = AllocateMap();
|
||||
map->SetFileName(parsebuffer.Data());
|
||||
if (k == 0)
|
||||
{
|
||||
auto vol = MustFindVolume(j);
|
||||
vol->startmap = map->labelName;
|
||||
}
|
||||
|
||||
while (*textptr == ' ' || *textptr == '\t') textptr++;
|
||||
|
||||
map->parTime =
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
|
||||
|
||||
textptr += 5;
|
||||
while (*textptr == ' ' || *textptr == '\t') textptr++;
|
||||
|
||||
map->designerTime =
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
|
||||
|
||||
map->levelNumber = levnum;
|
||||
SetLevelNum(map, makelevelnum(j + 1, k + 1));
|
||||
|
||||
map->mapindex = k + 1;
|
||||
map->cluster = j + 1;
|
||||
|
||||
textptr += 5;
|
||||
while (*textptr == ' ' || *textptr == '\t') textptr++;
|
||||
|
@ -3132,7 +3148,7 @@ void ConCompiler::setmusic()
|
|||
{
|
||||
for (auto& tm : tempMusic)
|
||||
{
|
||||
auto map = FindMapByLevelNum(tm.levnum);
|
||||
auto map = FindMapByIndexOnly(tm.volnum, tm.levlnum);
|
||||
if (map) map->music = tm.music;
|
||||
}
|
||||
tempMusic.Clear();
|
||||
|
@ -3176,6 +3192,12 @@ void loadcons()
|
|||
|
||||
ScriptCode.Push(0);
|
||||
ConCompiler comp;
|
||||
|
||||
if (fileSystem.FileExists("engine/engine.con"))
|
||||
{
|
||||
comp.compilecon("engine/engine.con");
|
||||
}
|
||||
|
||||
comp.compilecon(ConFile()); //Tokenize
|
||||
|
||||
if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get())
|
||||
|
@ -3206,45 +3228,38 @@ void loadcons()
|
|||
InitGameVarPointers();
|
||||
ResetSystemDefaults();
|
||||
S_WorldTourMappingsForOldSounds(); // create a sound mapping for World Tour.
|
||||
S_CacheAllSounds();
|
||||
comp.setmusic();
|
||||
|
||||
// RR must link the last map of E1 to the first map of E2.
|
||||
if (isRR()) for (auto& map : mapList)
|
||||
{
|
||||
if (map->cluster == 1)
|
||||
{
|
||||
if (!FindMapByIndexOnly(map->cluster, map->mapindex + 1))
|
||||
{
|
||||
auto nextmap = FindMapByIndexOnly(map->cluster + 1, 1);
|
||||
if (nextmap)
|
||||
{
|
||||
map->NextMap = nextmap->labelName;
|
||||
map->flags |= LEVEL_FORCENOEOG;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isWorldTour())
|
||||
{
|
||||
// fix broken secret exit in WT's super secret map.
|
||||
// This cannot be done from an RMAPINFO definition because the conditions are too specific and must not override custom maps.
|
||||
int num = fileSystem.CheckNumForName("e1l7.map");
|
||||
int file = fileSystem.GetFileContainer(num);
|
||||
if (file <= fileSystem.GetMaxIwadNum())
|
||||
{
|
||||
auto maprec = FindMapByName("e1l7");
|
||||
if (maprec) maprec->nextLevel = levelnum(0, 4);
|
||||
if (maprec) maprec->NextMap = "e1l5";
|
||||
}
|
||||
}
|
||||
else if (isRRRA())
|
||||
{
|
||||
// RRRA goes directly to the second episode after E1L7 to continue the game.
|
||||
int num = fileSystem.CheckNumForName("e1l7.map");
|
||||
int file = fileSystem.GetFileContainer(num);
|
||||
if (file <= fileSystem.GetMaxIwadNum())
|
||||
{
|
||||
auto maprec = FindMapByName("e1l7");
|
||||
if (maprec) maprec->nextLevel = levelnum(1, 0);
|
||||
}
|
||||
}
|
||||
else if (isRR())
|
||||
{
|
||||
// RR does not define its final level and crudely hacked it into the progression. This puts it into the E2L8 slot so that the game can naturally progress there.
|
||||
auto maprec1 = FindMapByLevelNum(levelnum(1, 6));
|
||||
auto maprec2 = FindMapByLevelNum(levelnum(1, 7));
|
||||
auto maprec3 = FindMapByName("endgame");
|
||||
int num3 = fileSystem.FindFile("endgame.map");
|
||||
if (maprec1 && !maprec2 && !maprec3 && num3 >= 0)
|
||||
{
|
||||
auto maprec = AllocateMap();
|
||||
maprec->designerTime = 0;
|
||||
maprec->parTime = 0;
|
||||
maprec->SetFileName("endgame.map");
|
||||
maprec->SetName("$TXT_CLOSEENCOUNTERS");
|
||||
maprec->levelNumber = levelnum(1, 7);
|
||||
}
|
||||
}
|
||||
comp.setmusic();
|
||||
}
|
||||
|
||||
END_DUKE_NS
|
||||
|
|
|
@ -130,11 +130,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
|
|||
if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_FROM_BONUS:
|
||||
if (bSet) ud.from_bonus = lValue;
|
||||
else SetGameVarID(lVar2, ud.from_bonus, sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_CAMERASPRITE:
|
||||
if (bSet) ud.cameraactor = ScriptIndexToActor(lValue);
|
||||
else SetGameVarID(lVar2, ActorToScriptIndex(ud.cameraactor), sActor, sPlayer);
|
||||
|
@ -239,14 +234,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
|
|||
else SetGameVarID(lVar2, ud.player_skill, sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_LEVEL_NUMBER:
|
||||
if (!bSet) SetGameVarID(lVar2, mapfromlevelnum(currentLevel->levelNumber), sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_VOLUME_NUMBER:
|
||||
if (!bSet) SetGameVarID(lVar2, volfromlevelnum(currentLevel->levelNumber), sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_MARKER:
|
||||
if (bSet) ud.marker = lValue;
|
||||
else SetGameVarID(lVar2, ud.marker, sActor, sPlayer);
|
||||
|
@ -3455,7 +3442,7 @@ int ParseState::parse(void)
|
|||
insptr++; // skip command
|
||||
volnume = GetGameVarID(*insptr++, g_ac, g_p);
|
||||
levnume = GetGameVarID(*insptr++, g_ac, g_p);
|
||||
auto level = FindMapByLevelNum(levelnum(volnume - 1, levnume - 1));
|
||||
auto level = FindMapByIndex(volnume, levnume);
|
||||
if (level != nullptr)
|
||||
ChangeLevel(level, -1);
|
||||
break;
|
||||
|
@ -3572,7 +3559,7 @@ int ParseState::parse(void)
|
|||
{
|
||||
insptr++;
|
||||
int music_select = *insptr++;
|
||||
auto level = FindMapByLevelNum(levelnum(currentLevel->levelNumber, music_select));
|
||||
auto level = FindMapByIndex(currentLevel->cluster, music_select+1); // this was 0-based in EDuke 2.0...
|
||||
if (level) S_PlayLevelMusic(level);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -113,29 +113,8 @@ void GameInterface::Ticker()
|
|||
|
||||
void GameInterface::Startup()
|
||||
{
|
||||
ps[myconnectindex].ftq = 0;
|
||||
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
auto maprecord = FindMapByName(userConfig.CommandMap);
|
||||
userConfig.CommandMap = "";
|
||||
if (maprecord)
|
||||
{
|
||||
ud.m_respawn_monsters = ud.player_skill == 4;
|
||||
|
||||
for (int i = 0; i != -1; i = connectpoint2[i])
|
||||
{
|
||||
resetweapons(i);
|
||||
resetinventory(i);
|
||||
}
|
||||
startnewgame(maprecord, /*userConfig.skill*/2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!userConfig.nologo) fi.ShowLogo([](bool) { gameaction = ga_mainmenunostopsound; });
|
||||
else gameaction = ga_mainmenunostopsound;
|
||||
}
|
||||
ps[myconnectindex].ftq = 0;
|
||||
PlayLogos(ga_mainmenunostopsound, ga_mainmenunostopsound, false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -160,20 +139,9 @@ void GameInterface::Render()
|
|||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void loadscreen_d(MapRecord* rec, CompletionFunc func);
|
||||
void loadscreen_r(MapRecord* rec, CompletionFunc func);
|
||||
|
||||
void GameInterface::NextLevel(MapRecord* map, int skill)
|
||||
{
|
||||
#if 0
|
||||
// Loading is so fast on modern system so that this only serves as an irritant, not an asset.
|
||||
auto loadscreen = isRR() ? loadscreen_r : loadscreen_d;
|
||||
loadscreen_d(map, [=](bool)
|
||||
{
|
||||
enterlevel(map, 0);
|
||||
gameaction = ga_level;
|
||||
});
|
||||
#endif
|
||||
enterlevel(map, 0);
|
||||
}
|
||||
|
||||
|
@ -183,23 +151,6 @@ void GameInterface::NextLevel(MapRecord* map, int skill)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void GameInterface::NewGame(MapRecord* map, int skill, bool)
|
||||
{
|
||||
// Hmm... What about the other players?
|
||||
ps[0].last_extra = gs.max_player_health;
|
||||
resetweapons(0);
|
||||
resetinventory(0);
|
||||
if (skill != -1) skill = skill + 1;
|
||||
|
||||
startnewgame(map, skill);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord* map, int skill)
|
||||
{
|
||||
exitlevel(map);
|
||||
|
|
|
@ -555,9 +555,9 @@ void InitGameVarPointers(void)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// These are deliberately not stored in accessible variables anymore
|
||||
int getmap() { return mapfromlevelnum(currentLevel->levelNumber); }
|
||||
int getvol() { return volfromlevelnum(currentLevel->levelNumber); }
|
||||
// These are deliberately not stored in accessible variables anymore. Use is deprecated.
|
||||
int getmap() { return currentLevel->levelNumber; }
|
||||
int getvol() { return currentLevel->cluster; }
|
||||
|
||||
void AddSystemVars()
|
||||
{
|
||||
|
|
|
@ -164,7 +164,7 @@ inline float PlayerInputAngVel(int pl)
|
|||
return ps[pl].sync.avel;
|
||||
}
|
||||
|
||||
inline float PlayerHorizon(int pl)
|
||||
inline float GetPlayerHorizon(int pl)
|
||||
{
|
||||
return ps[pl].sync.horz;
|
||||
}
|
||||
|
|
|
@ -517,6 +517,7 @@ x(THREEDEE, 2498)
|
|||
x(INGAMEDUKETHREEDEE, 2499)
|
||||
x(TENSCREEN, 2500)
|
||||
x(PLUTOPAKSPRITE, 2501)
|
||||
x(TITLEPLUTOPAKSPRITE, 2502)
|
||||
x(MENUPLUTOPAKSPRITE, 2503)
|
||||
x(CREDITPAGE1, 2504)
|
||||
x(CREDITPAGE2, 2505)
|
||||
|
@ -587,11 +588,33 @@ x(RESPAWNMARKERRED, 3190)
|
|||
x(RESPAWNMARKERYELLOW, 3200)
|
||||
x(RESPAWNMARKERGREEN, 3210)
|
||||
x(BONUSSCREEN, 3240)
|
||||
x(BONUSSCREEN_O1, 3241)
|
||||
x(BONUSSCREEN_O2, 3242)
|
||||
x(BONUSSCREEN_O3, 3243)
|
||||
x(BONUSSCREEN_O4, 3244)
|
||||
x(BONUSSCREEN2, 3245)
|
||||
x(BONUSSCREEN2_O1, 3246)
|
||||
x(BONUSSCREEN2_O2, 3247)
|
||||
x(BONUSSCREEN2_O3, 3248)
|
||||
x(BONUSSCREEN2_O4, 3249)
|
||||
x(VIEWBORDER, 3250)
|
||||
x(VICTORY1, 3260)
|
||||
x(VICTORY2, 3261)
|
||||
x(VICTORY3, 3262)
|
||||
x(VICTORY4, 3263)
|
||||
x(VICTORY5, 3264)
|
||||
x(VICTORY6, 3265)
|
||||
x(VICTORY7, 3266)
|
||||
x(VICTORY8, 3267)
|
||||
x(VICTORY9, 3268)
|
||||
x(ORDERING, 3270)
|
||||
x(ORDERING1, 3271)
|
||||
x(ORDERING2, 3272)
|
||||
x(ORDERING3, 3273)
|
||||
x(TEXTSTORY, 3280)
|
||||
x(LOADSCREEN, 3281)
|
||||
x(SWEXIT2, 3290)
|
||||
x(SWEXIT1, 3291)
|
||||
x(E1ENDSCREEN, 3292)
|
||||
x(E2ENDSCREEN, 3293)
|
||||
x(BORNTOBEWILDSCREEN, 3370)
|
||||
|
|
|
@ -1241,6 +1241,7 @@ y(RRTILE8640, 8640)
|
|||
y(RRTILE8651, 8651)
|
||||
y(RRTILE8660, 8660)
|
||||
x(ENDGAME, 8677)
|
||||
x(ENDGAME2, 8678)
|
||||
y(RRTILE8679, 8679)
|
||||
y(RRTILE8680, 8680)
|
||||
y(RRTILE8681, 8681)
|
||||
|
|
|
@ -22,10 +22,6 @@ extern int TILE_CAMCORNER;
|
|||
extern int TILE_CAMLIGHT;
|
||||
extern int TILE_STATIC;
|
||||
extern int TILE_BOTTOMSTATUSBAR;
|
||||
extern int TILE_THREEDEE;
|
||||
extern int TILE_INGAMEDUKETHREEDEE;
|
||||
extern int TILE_PLUTOPAKSPRITE;
|
||||
extern int TILE_MENUBAR;
|
||||
extern int TILE_ATOMICHEALTH;
|
||||
extern int TILE_FLOORSLIME;
|
||||
extern int TILE_JIBS6;
|
||||
|
|
|
@ -2630,27 +2630,6 @@ static void processweapon(int snum, ESyncBits actions, int psect)
|
|||
auto s = pact->s;
|
||||
int shrunk = (s->yrepeat < 32);
|
||||
|
||||
// Set maximum for pistol slightly higher if playing with `cl_showmagamount 1`.
|
||||
if (!cl_showmagamt)
|
||||
{
|
||||
if (p->ammo_amount[PISTOL_WEAPON] > PISTOL_MAXDEFAULT)
|
||||
p->ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT;
|
||||
|
||||
if (gs.max_ammo_amount[PISTOL_WEAPON] != PISTOL_MAXDEFAULT)
|
||||
gs.max_ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
short pistolAddition = 4;
|
||||
short pistolNewMaximum = PISTOL_MAXDEFAULT + pistolAddition;
|
||||
|
||||
if (p->ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT && gs.max_ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT)
|
||||
p->ammo_amount[PISTOL_WEAPON] += pistolAddition;
|
||||
|
||||
if (gs.max_ammo_amount[PISTOL_WEAPON] != pistolNewMaximum)
|
||||
gs.max_ammo_amount[PISTOL_WEAPON] = pistolNewMaximum;
|
||||
}
|
||||
|
||||
if (isNamWW2GI() && (actions & SB_HOLSTER)) // 'Holster Weapon
|
||||
{
|
||||
if (isWW2GI())
|
||||
|
@ -3138,7 +3117,7 @@ HORIZONLY:
|
|||
|
||||
if (SyncInput())
|
||||
{
|
||||
p->horizon.applyinput(PlayerHorizon(snum), &actions);
|
||||
p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
|
||||
}
|
||||
|
||||
p->checkhardlanding();
|
||||
|
|
|
@ -1464,26 +1464,8 @@ int doincrements_r(struct player_struct* p)
|
|||
{
|
||||
if (!wupass)
|
||||
{
|
||||
short snd;
|
||||
int snd = currentLevel->rr_startsound ? currentLevel->rr_startsound : 391;
|
||||
wupass = 1;
|
||||
switch (currentLevel->levelNumber)
|
||||
{
|
||||
default: snd = 391; break;
|
||||
case levelnum(0, 0): snd = isRRRA() ? 63 : 391; break;
|
||||
case levelnum(0, 1): snd = 64; break;
|
||||
case levelnum(0, 2): snd = 77; break;
|
||||
case levelnum(0, 3): snd = 80; break;
|
||||
case levelnum(0, 4): snd = 102; break;
|
||||
case levelnum(0, 5): snd = 103; break;
|
||||
case levelnum(0, 6): snd = 104; break;
|
||||
case levelnum(1, 0): snd = 105; break;
|
||||
case levelnum(1, 1): snd = 176; break;
|
||||
case levelnum(1, 2): snd = 177; break;
|
||||
case levelnum(1, 3): snd = 198; break;
|
||||
case levelnum(1, 4): snd = 230; break;
|
||||
case levelnum(1, 5): snd = 255; break;
|
||||
case levelnum(1, 6): snd = 283; break;
|
||||
}
|
||||
S_PlayActorSound(snd, pact);
|
||||
}
|
||||
else if (PlayClock > 1024)
|
||||
|
@ -3399,8 +3381,7 @@ void processinput_r(int snum)
|
|||
psectlotag = 2;
|
||||
}
|
||||
}
|
||||
else if (psectlotag == 7777)
|
||||
if (currentLevel->levelNumber == levelnum(1, 6))
|
||||
else if (psectlotag == 7777 && (currentLevel->gameflags & LEVEL_RR_HULKSPAWN))
|
||||
lastlevel = 1;
|
||||
|
||||
if (psectlotag == 848 && sector[psect].floorpicnum == WATERTILE2)
|
||||
|
@ -4000,7 +3981,7 @@ HORIZONLY:
|
|||
|
||||
if (SyncInput())
|
||||
{
|
||||
p->horizon.applyinput(PlayerHorizon(snum), &actions);
|
||||
p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
|
||||
}
|
||||
|
||||
p->checkhardlanding();
|
||||
|
|
|
@ -36,6 +36,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
|
|||
#include "automap.h"
|
||||
#include "dukeactor.h"
|
||||
#include "interpolate.h"
|
||||
#include "precache.h"
|
||||
#include "render.h"
|
||||
|
||||
BEGIN_DUKE_NS
|
||||
|
@ -141,7 +142,7 @@ void resetplayerstats(int snum)
|
|||
p->jetpack_on = 0;
|
||||
p->holoduke_on = nullptr;
|
||||
|
||||
p->angle.olook_ang = p->angle.look_ang = buildang(512 - ((currentLevel->levelNumber & 1) << 10));
|
||||
p->angle.olook_ang = p->angle.look_ang = buildang(512 - (((~currentLevel->levelNumber) & 1) << 10));
|
||||
p->angle.orotscrnang = p->angle.rotscrnang = buildang(0);
|
||||
|
||||
p->newOwner =nullptr;
|
||||
|
@ -659,7 +660,6 @@ void prelevel_common(int g)
|
|||
p->SlotWin = 0;
|
||||
enemysizecheat = 0;
|
||||
p->MamaEnd = 0;
|
||||
mamaspawn_count = 15;
|
||||
banjosound = 0;
|
||||
RRRA_ExitedLevel = 0;
|
||||
|
||||
|
@ -672,7 +672,7 @@ void prelevel_common(int g)
|
|||
WindDir = 0;
|
||||
fakebubba_spawn = 0;
|
||||
RRRA_ExitedLevel = 0;
|
||||
mamaspawn_count = 15;
|
||||
mamaspawn_count = currentLevel->rr_mamaspawn;
|
||||
BellTime = 0;
|
||||
BellSprite = nullptr;
|
||||
|
||||
|
@ -754,7 +754,6 @@ void donewgame(MapRecord* map, int sk)
|
|||
auto p = &ps[0];
|
||||
show_shareware = 26 * 34;
|
||||
|
||||
//ud.nextLevel = map;
|
||||
ud.player_skill = sk;
|
||||
ud.secretlevel = 0;
|
||||
ud.from_bonus = 0;
|
||||
|
@ -813,33 +812,6 @@ void donewgame(MapRecord* map, int sk)
|
|||
}
|
||||
}
|
||||
|
||||
template<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.
|
||||
|
@ -978,12 +950,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
|
|||
SECRET_SetMapName(mi->DisplayName(), mi->name);
|
||||
STAT_NewLevel(mi->fileName);
|
||||
|
||||
if (isRR() && !isRRRA() && mi->levelNumber == levelnum(1, 1))
|
||||
{
|
||||
for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++)
|
||||
ps[0].ammo_amount[i] = 0;
|
||||
ps[0].gotweapon.Clear(KNEE_WEAPON);
|
||||
}
|
||||
p->angle.ang = buildang(lbang);
|
||||
|
||||
memset(gotpic, 0, sizeof(gotpic));
|
||||
|
@ -993,16 +959,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
|
|||
|
||||
SpawnPortals();
|
||||
|
||||
if (isRRRA() && mi->levelNumber == levelnum(2, 0))
|
||||
{
|
||||
for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++)
|
||||
ps[0].ammo_amount[i] = 0;
|
||||
ps[0].gotweapon.Clear(KNEE_WEAPON);
|
||||
ps[0].gotweapon.Set(SLINGBLADE_WEAPON);
|
||||
ps[0].ammo_amount[SLINGBLADE_WEAPON] = 1;
|
||||
ps[0].curr_weapon = SLINGBLADE_WEAPON;
|
||||
}
|
||||
|
||||
allignwarpelevators();
|
||||
resetpspritevars(gamemode);
|
||||
|
||||
|
@ -1047,7 +1003,6 @@ void enterlevel(MapRecord *mi, int gamemode)
|
|||
OnEvent(EVENT_ENTERLEVEL);
|
||||
|
||||
// Stop all sounds
|
||||
S_ResumeSound(false);
|
||||
FX_StopAllSounds();
|
||||
FX_SetReverb(0);
|
||||
|
||||
|
@ -1064,27 +1019,32 @@ void enterlevel(MapRecord *mi, int gamemode)
|
|||
S_PlayLevelMusic(mi);
|
||||
}
|
||||
|
||||
if (isShareware() && mi->levelNumber == 0 && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
|
||||
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
bool clearweapon = !!(currentLevel->flags & LEVEL_CLEARWEAPONS);
|
||||
int pn = sector[ps[i].GetActor()->s->sectnum].floorpicnum;
|
||||
if (pn == TILE_HURTRAIL || pn == TILE_FLOORSLIME || pn == TILE_FLOORPLASMA)
|
||||
{
|
||||
resetweapons(i);
|
||||
resetinventory(i);
|
||||
clearweapon = true;
|
||||
}
|
||||
if (clearweapon)
|
||||
{
|
||||
resetweapons(i);
|
||||
ps[i].gotweapon.Clear(PISTOL_WEAPON);
|
||||
ps[i].ammo_amount[PISTOL_WEAPON] = 0;
|
||||
ps[i].curr_weapon = KNEE_WEAPON;
|
||||
ps[i].kickback_pic = 0;
|
||||
ps[i].okickback_pic = ps[i].kickback_pic = 0;
|
||||
}
|
||||
if (currentLevel->flags & LEVEL_CLEARINVENTORY) resetinventory(i);
|
||||
}
|
||||
resetmys();
|
||||
|
||||
everyothertime = 0;
|
||||
global_random = 0;
|
||||
|
||||
ud.last_level = currentLevel->levelNumber;
|
||||
ud.last_level = 1;
|
||||
ps[myconnectindex].over_shoulder_on = 0;
|
||||
clearfrags();
|
||||
resettimevars(); // Here we go
|
||||
|
@ -1105,9 +1065,19 @@ void enterlevel(MapRecord *mi, int gamemode)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void startnewgame(MapRecord* map, int skill)
|
||||
void GameInterface::NewGame(MapRecord* map, int skill, bool)
|
||||
{
|
||||
for (int i = 0; i != -1; i = connectpoint2[i])
|
||||
{
|
||||
resetweapons(i);
|
||||
resetinventory(i);
|
||||
}
|
||||
|
||||
ps[0].last_extra = gs.max_player_health;
|
||||
|
||||
|
||||
if (skill == -1) skill = ud.player_skill;
|
||||
else skill++;
|
||||
ud.player_skill = skill;
|
||||
ud.m_respawn_monsters = (skill == 4);
|
||||
ud.m_monsters_off = ud.monsters_off = 0;
|
||||
|
@ -1115,13 +1085,13 @@ void startnewgame(MapRecord* map, int skill)
|
|||
ud.m_respawn_inventory = 0;
|
||||
ud.multimode = 1;
|
||||
|
||||
newgame(map, skill, [=](bool)
|
||||
{
|
||||
donewgame(map, skill);
|
||||
enterlevel(map, 0);
|
||||
if (isShareware() && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
|
||||
|
||||
PlayerColorChanged();
|
||||
inputState.ClearAllInput();
|
||||
gameaction = ga_level;
|
||||
});
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1132,25 +1102,27 @@ void startnewgame(MapRecord* map, int skill)
|
|||
|
||||
bool setnextmap(bool checksecretexit)
|
||||
{
|
||||
MapRecord* map = nullptr;;
|
||||
int from_bonus = 0;
|
||||
MapRecord* map = nullptr;
|
||||
MapRecord* from_bonus = nullptr;
|
||||
|
||||
if (ud.eog)
|
||||
if (ud.eog && !(currentLevel->flags & LEVEL_FORCENOEOG))
|
||||
{
|
||||
}
|
||||
else if (checksecretexit && ud.from_bonus == 0)
|
||||
{
|
||||
if (ud.secretlevel > 0)
|
||||
{
|
||||
int newlevnum = levelnum(volfromlevelnum(currentLevel->levelNumber), ud.secretlevel-1);
|
||||
map = FindMapByLevelNum(newlevnum);
|
||||
// allow overriding the secret exit destination to make episode compilation easier with maps containing secret exits.
|
||||
if (currentLevel->flags & LEVEL_SECRETEXITOVERRIDE) map = FindNextSecretMap(currentLevel);
|
||||
if (!map) map = FindMapByIndex(currentLevel->cluster, ud.secretlevel);
|
||||
|
||||
if (map)
|
||||
{
|
||||
from_bonus = currentLevel->levelNumber + 1;
|
||||
from_bonus = FindNextMap(currentLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ud.from_bonus && currentLevel->nextLevel == -1) // if the current level has an explicit link, use that instead of ud.from_bonus.
|
||||
else if (ud.from_bonus && currentLevel->NextMap.IsEmpty()) // if the current level has an explicit link, use that instead of ud.from_bonus.
|
||||
{
|
||||
map = FindMapByLevelNum(ud.from_bonus);
|
||||
}
|
||||
|
@ -1169,7 +1141,7 @@ bool setnextmap(bool checksecretexit)
|
|||
{
|
||||
I_Error("Trying to open non-existent %s", map->fileName.GetChars());
|
||||
}
|
||||
ud.from_bonus = from_bonus;
|
||||
ud.from_bonus = from_bonus? from_bonus->levelNumber : 0;
|
||||
}
|
||||
CompleteLevel(map);
|
||||
return false;
|
||||
|
@ -1181,30 +1153,33 @@ bool setnextmap(bool checksecretexit)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void exitlevel(MapRecord *nextlevel)
|
||||
void exitlevel(MapRecord* nextlevel)
|
||||
{
|
||||
bool endofgame = nextlevel == nullptr;
|
||||
STAT_Update(endofgame);
|
||||
StopCommentary();
|
||||
|
||||
dobonus(endofgame? -1 : 0, [=](bool)
|
||||
{
|
||||
SummaryInfo info{};
|
||||
|
||||
info.kills = ps[0].actors_killed;
|
||||
info.maxkills = ps[0].max_actors_killed;
|
||||
info.secrets = ps[0].secret_rooms;
|
||||
info.maxsecrets = ps[0].max_secret_rooms;
|
||||
info.time = ps[0].player_par / GameTicRate;
|
||||
info.endofgame = endofgame;
|
||||
Mus_Stop();
|
||||
|
||||
if (playerswhenstarted > 1 && ud.coop != 1)
|
||||
{
|
||||
// MP scoreboard
|
||||
ShowScoreboard(playerswhenstarted, [=](bool)
|
||||
{
|
||||
// Clear potentially loaded per-map ART only after the bonus screens.
|
||||
artClearMapArt();
|
||||
gameaction = ga_level;
|
||||
ud.eog = false;
|
||||
if (endofgame)
|
||||
{
|
||||
if (ud.multimode < 2)
|
||||
{
|
||||
if (isShareware())
|
||||
doorders([](bool) { gameaction = ga_startup; });
|
||||
else gameaction = ga_startup;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto nextlevel = FindMapByLevelNum(0);
|
||||
if (!nextlevel)
|
||||
{
|
||||
|
@ -1213,11 +1188,22 @@ void exitlevel(MapRecord *nextlevel)
|
|||
}
|
||||
else gameaction = ga_nextlevel;
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
gameaction = ga_nextlevel;
|
||||
|
||||
});
|
||||
}
|
||||
else if (ud.multimode <= 1)
|
||||
{
|
||||
// SP cutscene + summary
|
||||
ShowIntermission(currentLevel, nextlevel, &info, [=](bool)
|
||||
{
|
||||
// Clear potentially loaded per-map ART only after the bonus screens.
|
||||
artClearMapArt();
|
||||
ud.eog = false;
|
||||
gameaction = endofgame? ga_startup : ga_nextlevel;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -232,6 +232,7 @@ static void cachegoodsprites(void)
|
|||
|
||||
void cacheit_d(void)
|
||||
{
|
||||
if (!r_precache) return;
|
||||
int i;
|
||||
|
||||
cachegoodsprites();
|
||||
|
|
|
@ -366,37 +366,6 @@ static void cachegoodsprites(void)
|
|||
for (i = SMALLSMOKE; i < (SMALLSMOKE + 4); i++)
|
||||
tloadtile(i);
|
||||
|
||||
if (isRRRA() && currentLevel->levelNumber == levelnum(0, 4))
|
||||
{
|
||||
tloadtile(RRTILE2577);
|
||||
}
|
||||
if (!isRRRA() && currentLevel->levelNumber == levelnum(1, 2))
|
||||
{
|
||||
tloadtile(RRTILE3190);
|
||||
tloadtile(RRTILE3191);
|
||||
tloadtile(RRTILE3192);
|
||||
tloadtile(RRTILE3144);
|
||||
tloadtile(RRTILE3139);
|
||||
tloadtile(RRTILE3132);
|
||||
tloadtile(RRTILE3120);
|
||||
tloadtile(RRTILE3121);
|
||||
tloadtile(RRTILE3122);
|
||||
tloadtile(RRTILE3123);
|
||||
tloadtile(RRTILE3124);
|
||||
}
|
||||
if (lastlevel)
|
||||
{
|
||||
i = isRRRA() ? UFO1_RRRA : UFO1_RR;
|
||||
tloadtile(i);
|
||||
i = UFO2;
|
||||
tloadtile(i);
|
||||
i = UFO3;
|
||||
tloadtile(i);
|
||||
i = UFO4;
|
||||
tloadtile(i);
|
||||
i = UFO5;
|
||||
tloadtile(i);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -407,6 +376,7 @@ static void cachegoodsprites(void)
|
|||
|
||||
void cacheit_r(void)
|
||||
{
|
||||
if (!r_precache) return;
|
||||
int i;
|
||||
|
||||
cachegoodsprites();
|
||||
|
@ -461,11 +431,11 @@ void prelevel_r(int g)
|
|||
prelevel_common(g);
|
||||
p = &ps[screenpeek];
|
||||
|
||||
if (currentLevel->gameflags & LEVEL_RR_CLEARMOONSHINE)
|
||||
ps[myconnectindex].steroids_amount = 0;
|
||||
|
||||
if (isRRRA())
|
||||
{
|
||||
if (currentLevel->levelNumber == levelnum(1, 4))
|
||||
ps[myconnectindex].steroids_amount = 0;
|
||||
|
||||
for (j = 0; j < MAXSPRITES; j++)
|
||||
{
|
||||
|
|
|
@ -46,6 +46,7 @@ source as it is released.
|
|||
#include "gamestate.h"
|
||||
#include "names_d.h"
|
||||
#include "i_music.h"
|
||||
#include "vm.h"
|
||||
|
||||
CVAR(Bool, wt_forcemidi, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // quick hack to disable the oggs, which are of lower quality than playing the MIDIs with a good synth and sound font.
|
||||
CVAR(Bool, wt_forcevoc, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // The same for sound effects. The re-recordings are rather poor and disliked
|
||||
|
@ -872,4 +873,48 @@ bool StartCommentary(int tag, DDukeActor* actor)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySpecialMusic, S_PlaySpecialMusic)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(song);
|
||||
S_PlaySpecialMusic(song);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int PlaySound(int num, int chan, int flags, double vol)
|
||||
{
|
||||
return S_PlaySound(num, chan, EChanFlags::FromInt(flags), float(vol));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySound, PlaySound)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(snd);
|
||||
PARAM_INT(chan);
|
||||
PARAM_INT(flags);
|
||||
PARAM_FLOAT(vol);
|
||||
ACTION_RETURN_INT(PlaySound(snd, chan, flags, vol));
|
||||
}
|
||||
static void StopSound(int num)
|
||||
{
|
||||
S_StopSound(num);
|
||||
}
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, StopSound, StopSound)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(snd);
|
||||
StopSound(snd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, CheckSoundPlaying, S_CheckSoundPlaying)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(snd);
|
||||
ACTION_RETURN_INT(S_CheckSoundPlaying(snd));
|
||||
}
|
||||
|
||||
END_DUKE_NS
|
||||
|
|
|
@ -48,7 +48,6 @@ void S_MenuSound(void);
|
|||
void S_StopSound(int sndNum, DDukeActor* spr = nullptr, int flags = -1);
|
||||
|
||||
int S_CheckSoundPlaying(int soundNum);
|
||||
inline int S_CheckSoundPlaying(int sprnum, int soundNum) { return S_CheckSoundPlaying(soundNum); }
|
||||
int S_CheckActorSoundPlaying(DDukeActor* spriteNum, int soundNum, int channel = 0);
|
||||
int S_CheckAnyActorSoundPlaying(DDukeActor* spriteNum);
|
||||
|
||||
|
@ -64,7 +63,6 @@ inline bool S_IsSoundValid(int num)
|
|||
void S_PlayRRMusic(int newTrack = -1);
|
||||
void S_PlayBonusMusic();
|
||||
void S_PlayLevelMusic(MapRecord* mi);
|
||||
void S_PlaySpecialMusic(unsigned int);
|
||||
void S_ContinueLevelMusic(void);
|
||||
|
||||
// Placeholders.
|
||||
|
|
|
@ -143,7 +143,6 @@ struct user_defs
|
|||
int m_respawn_items, m_respawn_monsters, m_respawn_inventory, m_recstat, m_monsters_off;
|
||||
int m_ffire, ffire, m_player_skill, multimode;
|
||||
int player_skill, marker;
|
||||
//MapRecord* nextLevel;
|
||||
|
||||
DDukeActor* cameraactor;
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "gstrings.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "c_bind.h"
|
||||
#include "vm.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -47,8 +48,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
BEGIN_PS_NS
|
||||
|
||||
int selectedlevelnew;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
|
@ -373,471 +372,26 @@ void menu_DoPlasma()
|
|||
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DLobotomyScreen : public DImageScreen
|
||||
DEFINE_ACTION_FUNCTION(_Exhumed, DrawPlasma)
|
||||
{
|
||||
public:
|
||||
DLobotomyScreen(FGameTexture *tex, int fade) : DImageScreen(tex, fade)
|
||||
{}
|
||||
|
||||
void Skipped() override
|
||||
{
|
||||
StopLocalSound();
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
PlayLocalSound(StaticSound[kSoundJonLaugh2], 7000, false, CHANF_UI);
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
|
||||
DImageScreen::OnTick();
|
||||
if (state == finished) StopLocalSound();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 };
|
||||
|
||||
class DMainTitle : public DSkippableScreenJob
|
||||
{
|
||||
const char* a;
|
||||
const char* b;
|
||||
int state = 0;
|
||||
int duration;
|
||||
int var_4 = 0;
|
||||
int esi = 130;
|
||||
int nCount = 0;
|
||||
int start;
|
||||
|
||||
|
||||
public:
|
||||
DMainTitle() : DSkippableScreenJob(fadein)
|
||||
{
|
||||
a = GStrings("TXT_EX_COPYRIGHT1");
|
||||
b = GStrings("TXT_EX_COPYRIGHT2");
|
||||
duration = skullDurations[0];
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
PlayLocalSound(StaticSound[59], 0, true, CHANF_UI);
|
||||
playCDtrack(19, true);
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
int ticker = ticks * 120 / GameTicRate;
|
||||
if (ticks > 1 && state == 0 && !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1))
|
||||
{
|
||||
if (time(0) & 0xF) // cheap-ass random...
|
||||
PlayGameOverSound();
|
||||
else
|
||||
PlayLocalSound(StaticSound[61], 0, false, CHANF_UI);
|
||||
state = 1;
|
||||
start = ticker;
|
||||
}
|
||||
if (state == 1)
|
||||
{
|
||||
if (ticker > duration)
|
||||
{
|
||||
nCount++;
|
||||
if (nCount > 12)
|
||||
{
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
duration = start + skullDurations[nCount];
|
||||
var_4 = var_4 == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
|
||||
menu_DoPlasma();
|
||||
|
||||
DrawRel(kSkullHead, 160, 100);
|
||||
if (state == 0)
|
||||
{
|
||||
DrawRel(kSkullJaw, 161, 130);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nStringWidth = SmallFont->StringWidth(a);
|
||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
nStringWidth = SmallFont->StringWidth(b);
|
||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
|
||||
|
||||
short nTile = kSkullJaw;
|
||||
|
||||
if (var_4)
|
||||
{
|
||||
if (esi >= 135) nTile = kTile3583;
|
||||
else esi += 5;
|
||||
}
|
||||
else if (esi <= 130) esi = 130;
|
||||
else esi -= 2;
|
||||
|
||||
int y;
|
||||
|
||||
if (nTile == kTile3583)
|
||||
{
|
||||
y = 131;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = esi;
|
||||
if (y > 135) y = 135;
|
||||
}
|
||||
|
||||
DrawRel(nTile, 161, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
DScreenJob *PlayMovie(const char* fileName);
|
||||
|
||||
void DoTitle(CompletionFunc completion)
|
||||
{
|
||||
JobDesc jobs[5];
|
||||
int job = 0;
|
||||
|
||||
jobs[job++] = { Create<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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// pre-level map display
|
||||
// text overlay (native version still needed for Ramses texts.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static const int8_t MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 };
|
||||
|
||||
struct TILEFRAMEDEF
|
||||
{
|
||||
short nTile;
|
||||
short xOffs;
|
||||
short yOffs;
|
||||
};
|
||||
|
||||
// 22 bytes
|
||||
struct MapNamePlaque
|
||||
{
|
||||
short xPos;
|
||||
short yPos;
|
||||
TILEFRAMEDEF tiles[2];
|
||||
TILEFRAMEDEF text;
|
||||
};
|
||||
|
||||
static const MapNamePlaque mapNamePlaques[] = {
|
||||
{ 100, 170, kTile3376, 0, 0, kTile3377, 0, 0, kTile3411, 18, 6 },
|
||||
{ 230, 10, kTile3378, 0, 0, kTile3379, 0, 0, kTile3414, 18, 6 }, // DENDUR (level 2)
|
||||
{ 180, 125, kTile3380, 0, 0, kTile3381, 0, 0, kTile3417, 18, 6 }, // Kalabash
|
||||
{ 10, 95, kTile3382, 0, 0, kTile3383, 0, 0, kTile3420, 18, 6 },
|
||||
{ 210, 160, kTile3384, 0, 0, kTile3385, 0, 0, kTile3423, 18, 6 },
|
||||
{ 10, 110, kTile3371, 0, 0, kTile3386, 0, 0, kTile3426, 18, 6 },
|
||||
{ 10, 50, kTile3387, 0, 0, kTile3388, 0, 0, kTile3429, 18, 6 },
|
||||
{ 140, 0, kTile3389, 0, 0, kTile3390, 0, 0, kTile3432, 18, 6 },
|
||||
{ 30, 20, kTile3391, 0, 0, kTile3392, 0, 0, kTile3435, 18, 6 },
|
||||
{ 200, 150, kTile3409, 0, 0, kTile3410, 0, 0, kTile3418, 20, 4 },
|
||||
{ 145, 170, kTile3393, 0, 0, kTile3394, 0, 0, kTile3438, 18, 6 },
|
||||
{ 80, 80, kTile3395, 0, 0, kTile3396, 0, 0, kTile3441, 18, 6 },
|
||||
{ 15, 0, kTile3397, 0, 0, kTile3398, 0, 0, kTile3444, 18, 5 },
|
||||
{ 220, 35, kTile3399, 0, 0, kTile3400, 0, 0, kTile3447, 18, 6 },
|
||||
{ 190, 40, kTile3401, 0, 0, kTile3402, 0, 0, kTile3450, 18, 6 },
|
||||
{ 20, 130, kTile3403, 0, 0, kTile3404, 0, 0, kTile3453, 19, 6 },
|
||||
{ 220, 160, kTile3405, 0, 0, kTile3406, 0, 0, kTile3456, 18, 6 },
|
||||
{ 20, 10, kTile3407, 0, 0, kTile3408, 0, 0, kTile3459, 18, 6 },
|
||||
{ 200, 10, kTile3412, 0, 0, kTile3413, 0, 0, kTile3419, 18, 5 },
|
||||
{ 20, 10, kTile3415, 0, 0, kTile3416, 0, 0, kTile3421, 19, 4 }
|
||||
};
|
||||
|
||||
// 3 different types of fire, each with 4 frames
|
||||
static const TILEFRAMEDEF FireTiles[3][4] = {
|
||||
{{ kTile3484,0,3 },{ kTile3485,0,0 },{ kTile3486,0,3 },{ kTile3487,0,0 }},
|
||||
{{ kTile3488,1,0 },{ kTile3489,1,0 },{ kTile3490,0,1 },{ kTile3491,1,1 }},
|
||||
{{ kTile3492,1,2 },{ kTile3493,1,0 },{ kTile3494,1,2 },{ kTile3495,1,0 }}
|
||||
};
|
||||
|
||||
struct Fire
|
||||
{
|
||||
short nFireType;
|
||||
short xPos;
|
||||
short yPos;
|
||||
};
|
||||
|
||||
// 20 bytes
|
||||
struct MapFire
|
||||
{
|
||||
short nFires;
|
||||
Fire fires[3];
|
||||
};
|
||||
|
||||
/*
|
||||
level 1 - 3 fires
|
||||
level 2 - 3 fires
|
||||
level 3 - 1 fire
|
||||
|
||||
*/
|
||||
|
||||
static const MapFire MapLevelFires[] = {
|
||||
3, {{0, 107, 95}, {1, 58, 140}, {2, 28, 38}},
|
||||
3, {{2, 240, 0}, {0, 237, 32}, {1, 200, 30}},
|
||||
2, {{2, 250, 57}, {0, 250, 43}, {2, 200, 70}},
|
||||
2, {{1, 82, 59}, {2, 84, 16}, {0, 10, 95}},
|
||||
2, {{2, 237, 50}, {1, 215, 42}, {1, 210, 50}},
|
||||
3, {{0, 40, 7}, {1, 75, 6}, {2, 100, 10}},
|
||||
3, {{0, 58, 61}, {1, 85, 80}, {2, 111, 63}},
|
||||
3, {{0, 260, 65}, {1, 228, 0}, {2, 259, 15}},
|
||||
2, {{0, 81, 38}, {2, 58, 38}, {2, 30, 20}},
|
||||
3, {{0, 259, 49}, {1, 248, 76}, {2, 290, 65}},
|
||||
3, {{2, 227, 66}, {0, 224, 98}, {1, 277, 30}},
|
||||
2, {{0, 100, 10}, {2, 48, 76}, {2, 80, 80}},
|
||||
3, {{0, 17, 2}, {1, 29, 49}, {2, 53, 28}},
|
||||
3, {{0, 266, 42}, {1, 283, 99}, {2, 243, 108}},
|
||||
2, {{0, 238, 19}, {2, 240, 92}, {2, 190, 40}},
|
||||
2, {{0, 27, 0}, {1, 70, 40}, {0, 20, 130}},
|
||||
3, {{0, 275, 65}, {1, 235, 8}, {2, 274, 6}},
|
||||
3, {{0, 75, 45}, {1, 152, 105}, {2, 24, 68}},
|
||||
3, {{0, 290, 25}, {1, 225, 63}, {2, 260, 110}},
|
||||
0, {{1, 20, 10}, {1, 20, 10}, {1, 20, 10}}
|
||||
};
|
||||
|
||||
class DMapScreen : public DScreenJob
|
||||
void TextOverlay::Create(const FString& text, int pal)
|
||||
{
|
||||
int i;
|
||||
int x = 0;
|
||||
int delta = 0;
|
||||
int nIdleSeconds = 0;
|
||||
|
||||
int curYPos, destYPos;
|
||||
int nLevel, nLevelNew, nLevelBest;
|
||||
|
||||
public:
|
||||
DMapScreen(int nLevel_, int nLevelNew_, int nLevelBest_) : DScreenJob(fadein|fadeout), nLevel(nLevel_), nLevelNew(nLevelNew_), nLevelBest(nLevelBest_)
|
||||
{
|
||||
curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2));
|
||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos < destYPos) {
|
||||
delta = 2;
|
||||
}
|
||||
|
||||
if (curYPos > destYPos) {
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
// Trim smoke in widescreen
|
||||
#if 0
|
||||
vec2_t mapwinxy1 = windowxy1, mapwinxy2 = windowxy2;
|
||||
int32_t width = mapwinxy2.x - mapwinxy1.x + 1, height = mapwinxy2.y - mapwinxy1.y + 1;
|
||||
if (3 * width > 4 * height)
|
||||
{
|
||||
mapwinxy1.x += (width - 4 * height / 3) / 2;
|
||||
mapwinxy2.x -= (width - 4 * height / 3) / 2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Draw(double smoothratio)
|
||||
{
|
||||
int currentclock = int((ticks + smoothratio) * 120 / GameTicRate);
|
||||
|
||||
twod->ClearScreen();
|
||||
|
||||
int tileY = curYPos;
|
||||
|
||||
// Draw the background screens
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
DrawAbs(kTile3353 + i, x, tileY);
|
||||
tileY -= 200;
|
||||
}
|
||||
|
||||
// for each level - drawing the 'level completed' on-fire smoke markers
|
||||
for (i = 0; i < kMap20; i++)
|
||||
{
|
||||
int screenY = (i >> 1) * -200;
|
||||
|
||||
if (nLevelBest >= i) // check if the player has finished this level
|
||||
{
|
||||
for (int j = 0; j < MapLevelFires[i].nFires; j++)
|
||||
{
|
||||
int nFireFrame = ((currentclock >> 4) & 3);
|
||||
assert(nFireFrame >= 0 && nFireFrame < 4);
|
||||
|
||||
int nFireType = MapLevelFires[i].fires[j].nFireType;
|
||||
assert(nFireType >= 0 && nFireType < 3);
|
||||
|
||||
int nTile = FireTiles[nFireType][nFireFrame].nTile;
|
||||
int smokeX = MapLevelFires[i].fires[j].xPos + FireTiles[nFireType][nFireFrame].xOffs;
|
||||
int smokeY = MapLevelFires[i].fires[j].yPos + FireTiles[nFireType][nFireFrame].yOffs + curYPos + screenY;
|
||||
|
||||
// Use rotatesprite to trim smoke in widescreen
|
||||
DrawAbs(nTile, smokeX, smokeY);
|
||||
// Todo: mask out the sides of the screen if the background is not widescreen.
|
||||
}
|
||||
}
|
||||
|
||||
int t = (((currentclock & 16) >> 4));
|
||||
|
||||
int nTile = mapNamePlaques[i].tiles[t].nTile;
|
||||
|
||||
int nameX = mapNamePlaques[i].xPos + mapNamePlaques[i].tiles[t].xOffs;
|
||||
int nameY = mapNamePlaques[i].yPos + mapNamePlaques[i].tiles[t].yOffs + curYPos + screenY;
|
||||
|
||||
// Draw level name plaque
|
||||
DrawAbs(nTile, nameX, nameY);
|
||||
|
||||
int8_t shade = 96;
|
||||
|
||||
if (nLevelNew == i)
|
||||
{
|
||||
shade = (bsin(16 * currentclock) + 31) >> 8;
|
||||
}
|
||||
else if (nLevelBest >= i)
|
||||
{
|
||||
shade = 31;
|
||||
}
|
||||
|
||||
int textY = mapNamePlaques[i].yPos + mapNamePlaques[i].text.yOffs + curYPos + screenY;
|
||||
int textX = mapNamePlaques[i].xPos + mapNamePlaques[i].text.xOffs;
|
||||
nTile = mapNamePlaques[i].text.nTile;
|
||||
|
||||
// draw the text, alternating between red and black
|
||||
DrawAbs(nTile, textX, textY, shade);
|
||||
}
|
||||
|
||||
selectedlevelnew = nLevelNew + 1;
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (curYPos != destYPos)
|
||||
{
|
||||
// scroll the map every couple of ms
|
||||
curYPos += delta;
|
||||
|
||||
if (curYPos > destYPos && delta > 0) {
|
||||
curYPos = destYPos;
|
||||
}
|
||||
|
||||
if (curYPos < destYPos && delta < 0) {
|
||||
curYPos = destYPos;
|
||||
}
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
else nIdleSeconds++;
|
||||
if (nIdleSeconds > 300) state = finished;
|
||||
}
|
||||
|
||||
bool OnEvent(event_t* ev) override
|
||||
{
|
||||
int key = ev->data1;
|
||||
if (ev->type == EV_KeyDown)
|
||||
{
|
||||
auto binding = Bindings.GetBinding(ev->data1);
|
||||
if (!binding.CompareNoCase("+move_forward")) key = KEY_UPARROW;
|
||||
if (!binding.CompareNoCase("+move_backward")) key = KEY_DOWNARROW;
|
||||
|
||||
if (key == KEY_UPARROW || key == KEY_PAD_DPAD_UP || key == sc_kpad_8)
|
||||
{
|
||||
if (curYPos == destYPos && nLevelNew <= nLevelBest)
|
||||
{
|
||||
nLevelNew++;
|
||||
assert(nLevelNew < 20);
|
||||
|
||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos <= destYPos) {
|
||||
delta = 2;
|
||||
}
|
||||
else {
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == KEY_DOWNARROW || key == KEY_PAD_DPAD_DOWN || key == sc_kpad_2)
|
||||
{
|
||||
if (curYPos == destYPos && nLevelNew > 0)
|
||||
{
|
||||
nLevelNew--;
|
||||
assert(nLevelNew >= 0);
|
||||
|
||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos <= destYPos) {
|
||||
delta = 2;
|
||||
}
|
||||
else {
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!specialKeyEvent(ev)) state = skipped;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray<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
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
lastclock = 0;
|
||||
FString ttext = GStrings(text);
|
||||
screentext = ttext.Split("\n");
|
||||
ComputeCinemaText();
|
||||
}
|
||||
|
||||
void TextOverlay::Start(double starttime)
|
||||
{
|
||||
|
@ -857,10 +411,9 @@ void TextOverlay::ComputeCinemaText()
|
|||
nHeight = screentext.Size() * 10;
|
||||
}
|
||||
|
||||
void TextOverlay::ReadyCinemaText(uint16_t nVal)
|
||||
void TextOverlay::ReadyCinemaText(const char* nVal)
|
||||
{
|
||||
FStringf label("TXT_EX_CINEMA%d", nVal);
|
||||
label = GStrings(label);
|
||||
FString label = nVal[0] == '$'? GStrings(nVal +1) : nVal;
|
||||
screentext = label.Split("\n");
|
||||
ComputeCinemaText();
|
||||
}
|
||||
|
@ -903,33 +456,6 @@ bool TextOverlay::AdvanceCinemaText(double clock)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
enum EScenes
|
||||
{
|
||||
CINEMA_BEFORE_LEVEL_5,
|
||||
CINEMA_AFTER_LEVEL_10,
|
||||
CINEMA_BEFORE_LEVEL_11,
|
||||
CINEMA_AFTER_LEVEL_15,
|
||||
CINEMA_LOSE_SCENE,
|
||||
CINEMA_AFTER_LEVEL_20,
|
||||
};
|
||||
|
||||
struct CinemaDef
|
||||
{
|
||||
short tile;
|
||||
short palette;
|
||||
short text;
|
||||
short track;
|
||||
};
|
||||
|
||||
static CinemaDef cinemas[] = {
|
||||
{ 3449, 3, 2, 2},
|
||||
{ 3451, 5, 4, 3},
|
||||
{ 3454, 1, 3, 4},
|
||||
{ 3446, 7, 6, 6},
|
||||
{ 3445, 4, 7, 7},
|
||||
{ 3448, 6, 8, 8}
|
||||
};
|
||||
|
||||
static const char * const cinpalfname[] = {
|
||||
"3454.pal",
|
||||
"3452.pal",
|
||||
|
@ -965,419 +491,59 @@ void uploadCinemaPalettes()
|
|||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// cinema
|
||||
// this accesses the tile data and needs to remain native.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DCinema : public DSkippableScreenJob
|
||||
static int DoStatic(int a, int b)
|
||||
{
|
||||
TextOverlay text;
|
||||
short cinematile;
|
||||
int currentCinemaPalette;
|
||||
int edx;
|
||||
int check;
|
||||
int cont = 1;
|
||||
|
||||
public:
|
||||
DCinema(int nVal, int checklevel = -1) : DSkippableScreenJob(fadein|fadeout)
|
||||
{
|
||||
if (nVal < 0 || nVal >5) return;
|
||||
cinematile = cinemas[nVal].tile;
|
||||
currentCinemaPalette = cinemas[nVal].palette;
|
||||
text.Start(0);
|
||||
text.ReadyCinemaText(cinemas[nVal].text);
|
||||
text.SetPalette(currentCinemaPalette);
|
||||
edx = cinemas[nVal].track;
|
||||
check = checklevel;
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
if (check > 0 && check != selectedlevelnew)
|
||||
{
|
||||
state = finished;
|
||||
return; // immediately abort if the player selected a different level on the map
|
||||
}
|
||||
|
||||
check = -1;
|
||||
StopAllSounds();
|
||||
if (edx != -1)
|
||||
{
|
||||
playCDtrack(edx + 2, false);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (!cont)
|
||||
{
|
||||
state = finished;
|
||||
// quit the game if we've finished level 4 and displayed the advert text
|
||||
if (isShareware() && currentCinemaPalette == 3)
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
if (check == 0) return;
|
||||
DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE);
|
||||
|
||||
text.DisplayText();
|
||||
cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate));
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// last level cinema
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DLastLevelCinema : public DScreenJob
|
||||
{
|
||||
int var_24 = 16;
|
||||
int var_28 = 12;
|
||||
|
||||
int ebp;
|
||||
int phase = 0;
|
||||
int nextclock = 4;
|
||||
unsigned int nStringTypeOn, nCharTypeOn;
|
||||
int screencnt = 0;
|
||||
bool skiprequest = false;
|
||||
|
||||
TArray<FString> screentext;
|
||||
|
||||
public:
|
||||
DLastLevelCinema() : DScreenJob(fadein | fadeout) {}
|
||||
|
||||
private:
|
||||
void DoStatic(int a, int b)
|
||||
{
|
||||
TileFiles.tileMakeWritable(kTileLoboLaptop);
|
||||
auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
|
||||
if (tex) tex->Reload();
|
||||
auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop);
|
||||
|
||||
int v2 = 160 - a / 2;
|
||||
int v4 = 81 - b / 2;
|
||||
int y = 160 - a / 2;
|
||||
int left = 81 - b / 2;
|
||||
|
||||
int var_18 = v2 + a;
|
||||
int v5 = v4 + b;
|
||||
int bottom = y + a;
|
||||
int right = left + b;
|
||||
|
||||
auto pTile = (pixels + (200 * v2)) + v4;
|
||||
auto pTile = (pixels + (200 * y)) + left;
|
||||
|
||||
TileFiles.InvalidateTile(kTileLoboLaptop);
|
||||
|
||||
while (v2 < var_18)
|
||||
for(;y < bottom; y++)
|
||||
{
|
||||
uint8_t* pStart = pTile;
|
||||
uint8_t* pixel = pTile;
|
||||
pTile += 200;
|
||||
|
||||
int v7 = v4;
|
||||
|
||||
while (v7 < v5)
|
||||
for (int x = left; x < right; x++)
|
||||
{
|
||||
*pStart = RandomBit() * 16;
|
||||
|
||||
v7++;
|
||||
pStart++;
|
||||
*pixel++ = RandomBit() * 16;
|
||||
}
|
||||
v2++;
|
||||
}
|
||||
}
|
||||
|
||||
void Phase1()
|
||||
{
|
||||
if (var_24 >= 116)
|
||||
{
|
||||
if (var_28 < 192)
|
||||
var_28 += 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
var_24 += 20;
|
||||
}
|
||||
|
||||
DoStatic(var_28, var_24);
|
||||
}
|
||||
|
||||
bool InitPhase2()
|
||||
{
|
||||
FStringf label("TXT_EX_LASTLEVEL%d", screencnt + 1);
|
||||
label = GStrings(label);
|
||||
screentext = label.Split("\n");
|
||||
if (screentext.Size() == 0) return false;
|
||||
|
||||
nStringTypeOn = 0;
|
||||
nCharTypeOn = 0;
|
||||
|
||||
ebp = screentext.Size() * 4; // half height of the entire text
|
||||
ebp = 81 - ebp; // offset from the screen's center.
|
||||
return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
|
||||
}
|
||||
|
||||
static int UndoStatic()
|
||||
{
|
||||
auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
|
||||
if (tex) tex->Reload();
|
||||
TileFiles.InvalidateTile(kTileLoboLaptop);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Phase3()
|
||||
{
|
||||
DoStatic(var_28, var_24);
|
||||
|
||||
if (var_28 > 20) {
|
||||
var_28 -= 20;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (var_24 > 20) {
|
||||
var_24 -= 20;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayPhase2()
|
||||
{
|
||||
int yy = ebp;
|
||||
|
||||
auto p = GStrings["REQUIRED_CHARACTERS"];
|
||||
if (p && *p)
|
||||
{
|
||||
yy *= 2;
|
||||
for (int i = 0; i < nStringTypeOn; i++, yy += 10)
|
||||
{
|
||||
DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit640x400, TAG_DONE);
|
||||
}
|
||||
DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn, TAG_DONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < nStringTypeOn; i++, yy += 8)
|
||||
{
|
||||
DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
}
|
||||
DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn, TAG_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
bool OnEvent(event_t* ev)
|
||||
{
|
||||
if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
||||
phase = 1;
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case 1:
|
||||
Phase1();
|
||||
if (skiprequest || ticks >= nextclock)
|
||||
{
|
||||
InitPhase2();
|
||||
phase = 2;
|
||||
skiprequest = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (screentext[nStringTypeOn][nCharTypeOn] != ' ')
|
||||
PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI);
|
||||
|
||||
nCharTypeOn++;
|
||||
if (screentext[nStringTypeOn][nCharTypeOn] == 0)
|
||||
{
|
||||
nCharTypeOn = 0;
|
||||
nStringTypeOn++;
|
||||
if (nStringTypeOn >= screentext.Size())
|
||||
{
|
||||
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||
phase = 3;
|
||||
}
|
||||
|
||||
}
|
||||
if (skiprequest)
|
||||
{
|
||||
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||
phase = 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (ticks >= nextclock || skiprequest)
|
||||
{
|
||||
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
||||
phase = 4;
|
||||
nextclock = ticks + 60;
|
||||
skiprequest = false;
|
||||
}
|
||||
|
||||
case 4:
|
||||
if (ticks >= nextclock)
|
||||
{
|
||||
skiprequest |= !Phase3();
|
||||
}
|
||||
if (skiprequest)
|
||||
{
|
||||
// Go to the next text page.
|
||||
if (screencnt != 2)
|
||||
{
|
||||
screencnt++;
|
||||
nextclock = ticks + 60;
|
||||
skiprequest = 0;
|
||||
phase = 1;
|
||||
}
|
||||
else state = finished;
|
||||
}
|
||||
|
||||
if (skiprequest)
|
||||
{
|
||||
state = finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
if (phase == 2 || phase == 3) DisplayPhase2();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Credits roll
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DExCredits : public DScreenJob
|
||||
{
|
||||
TArray<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; });
|
||||
return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
|
||||
}
|
||||
|
||||
|
||||
void DoAfterCinemaScene(int nLevel, TArray<JobDesc>& jobs)
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, DoStatic, DoStatic)
|
||||
{
|
||||
int scene = -1;
|
||||
if (nLevel == 10) scene = CINEMA_AFTER_LEVEL_10;
|
||||
if (nLevel == 15) scene = CINEMA_AFTER_LEVEL_15;
|
||||
if (nLevel == 20) scene = CINEMA_AFTER_LEVEL_20;
|
||||
if (scene > 0) jobs.Push({ Create<DCinema>(scene) });
|
||||
if (nLevel == 19) { jobs.Push({ Create<DLastLevelCinema>() }); selectedlevelnew = 20; }
|
||||
if (nLevel == 20) jobs.Push({ Create<DExCredits>() });
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(x);
|
||||
PARAM_INT(y);
|
||||
ACTION_RETURN_INT(DoStatic(x, y));
|
||||
}
|
||||
|
||||
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) });
|
||||
else if (nLevel == 11) jobs.Push({ Create<DCinema>(CINEMA_BEFORE_LEVEL_11, 11) });
|
||||
ACTION_RETURN_INT(UndoStatic());
|
||||
}
|
||||
|
||||
END_PS_NS
|
||||
|
|
|
@ -236,7 +236,7 @@ void DoBubbleMachines()
|
|||
void BuildBubbleMachine(int nSprite)
|
||||
{
|
||||
if (nMachineCount >= kMaxMachines) {
|
||||
I_Error("too many bubble machines in level %d\n", currentLevel->levelNumber);
|
||||
I_Error("too many bubble machines in level %s\n", currentLevel->labelName.GetChars());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,13 +72,6 @@ void GameInterface::QuitToTitle()
|
|||
gameaction = ga_mainmenu;
|
||||
}
|
||||
|
||||
bool GameInterface::StartGame(FNewGameStartup& gs)
|
||||
{
|
||||
auto map = FindMapByLevelNum(gs.Skill); // 0 is training, 1 is the regular game - the game does not have skill levels.
|
||||
DeferedStartGame(map, 1, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
FSavegameInfo GameInterface::GetSaveSig()
|
||||
{
|
||||
return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS };
|
||||
|
@ -90,12 +83,6 @@ END_PS_NS
|
|||
|
||||
using namespace Exhumed;
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw)
|
||||
{
|
||||
menu_DoPlasma();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw)
|
||||
{
|
||||
auto nLogoTile = GameLogo();
|
||||
|
|
|
@ -36,6 +36,8 @@ void resettiming()
|
|||
|
||||
void precache()
|
||||
{
|
||||
if (!r_precache) return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numsectors; i++)
|
||||
|
|
|
@ -238,6 +238,15 @@ double calc_smoothratio()
|
|||
return I_GetTimeFrac() * MaxSmoothRatio;
|
||||
}
|
||||
|
||||
void DoGameOverScene(bool finallevel)
|
||||
{
|
||||
// todo: make these customizable later.
|
||||
StartCutscene(finallevel ? "ExhumedCutscenes.BuildCinemaLose" : "ExhumedCutscenes.BuildGameoverScene", 0, [](bool)
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
});
|
||||
}
|
||||
|
||||
void GameMove(void)
|
||||
{
|
||||
FixPalette();
|
||||
|
@ -247,7 +256,7 @@ void GameMove(void)
|
|||
sprite[i].backuploc();
|
||||
}
|
||||
|
||||
if (currentLevel->levelNumber == kMap20)
|
||||
if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)
|
||||
{
|
||||
if (lCountDown <= 0)
|
||||
{
|
||||
|
@ -456,7 +465,7 @@ void GameInterface::Ticker()
|
|||
|
||||
void LevelFinished()
|
||||
{
|
||||
NextMap = currentLevel->levelNumber == 20 ? nullptr : FindMapByLevelNum(currentLevel->levelNumber + 1); // todo: Use the map record for progression
|
||||
NextMap = FindNextMap(currentLevel);
|
||||
EndLevel = 13;
|
||||
}
|
||||
|
||||
|
@ -480,19 +489,6 @@ void GameInterface::app_init()
|
|||
#if 0
|
||||
help_disabled = true;
|
||||
#endif
|
||||
// Create the global level table. Parts of the engine need it, even though the game itself does not.
|
||||
for (int i = 0; i <= 32; i++)
|
||||
{
|
||||
auto mi = AllocateMap();
|
||||
mi->fileName.Format("LEV%d.MAP", i);
|
||||
mi->labelName.Format("LEV%d", i);
|
||||
mi->name.Format("$TXT_EX_MAP%02d", i);
|
||||
mi->levelNumber = i;
|
||||
|
||||
int nTrack = i;
|
||||
if (nTrack != 0) nTrack--;
|
||||
mi->cdSongId = (nTrack % 8) + 11;
|
||||
}
|
||||
|
||||
InitCheats();
|
||||
registerosdcommands();
|
||||
|
@ -615,7 +611,7 @@ void SerializeState(FSerializer& arc)
|
|||
int loaded = 0;
|
||||
if (arc.BeginObject("state"))
|
||||
{
|
||||
if (arc.isReading() && currentLevel->levelNumber == 20)
|
||||
if (arc.isReading() && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
|
||||
{
|
||||
InitEnergyTile();
|
||||
}
|
||||
|
@ -639,7 +635,6 @@ void SerializeState(FSerializer& arc)
|
|||
("bsnakecam", bSnakeCam)
|
||||
("slipmode", bSlipMode)
|
||||
("PlayClock", PlayClock)
|
||||
("cinemaseen", nCinemaSeen)
|
||||
("spiritsprite", nSpiritSprite)
|
||||
.EndObject();
|
||||
}
|
||||
|
|
|
@ -54,8 +54,6 @@ void SetHiRes();
|
|||
void BlackOut();
|
||||
|
||||
void DoGameOverScene(bool finallevel);
|
||||
void DoAfterCinemaScene(int nLevel, TArray<JobDesc> &jobs);
|
||||
void DoBeforeCinemaScene(int nLevel, TArray<JobDesc>& jobs);
|
||||
|
||||
int Query(short n, short l, ...);
|
||||
|
||||
|
@ -83,12 +81,11 @@ void DoSpiritHead();
|
|||
|
||||
void CheckKeys2();
|
||||
void GameTicker();
|
||||
void InitLevel(int);
|
||||
void InitLevel(MapRecord*);
|
||||
void InitNewGame();
|
||||
|
||||
int showmap(short nLevel, short nLevelNew, short nLevelBest);
|
||||
void menu_DoPlasma();
|
||||
void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray<JobDesc>& jobs);
|
||||
void DoEnergyTile();
|
||||
void InitEnergyTile();
|
||||
|
||||
|
@ -127,7 +124,6 @@ extern short nCurBodyNum;
|
|||
extern short nBodyTotal;
|
||||
|
||||
extern short bSnakeCam;
|
||||
extern uint8_t nCinemaSeen;
|
||||
|
||||
extern short nButtonColor;
|
||||
|
||||
|
@ -155,11 +151,6 @@ extern short bDoFlashes;
|
|||
|
||||
extern int bVanilla;
|
||||
|
||||
inline int PublisherLogo()
|
||||
{
|
||||
return (g_gameType & GAMEFLAG_EXHUMED) ? kTileBMGLogo : kTilePIELogo;
|
||||
}
|
||||
|
||||
inline int GameLogo()
|
||||
{
|
||||
return (g_gameType & GAMEFLAG_EXHUMED) ? kExhumedLogo : kPowerslaveLogo;
|
||||
|
@ -197,10 +188,11 @@ public:
|
|||
|
||||
void Start(double starttime);
|
||||
void ComputeCinemaText();
|
||||
void ReadyCinemaText(uint16_t nVal);
|
||||
void ReadyCinemaText(const char* nVal);
|
||||
void DisplayText();
|
||||
bool AdvanceCinemaText(double clock);
|
||||
void SetPalette(int pal) { currentCinemaPalette = pal; }
|
||||
void Create(const FString& text, int pal);
|
||||
};
|
||||
|
||||
|
||||
|
@ -229,7 +221,6 @@ struct GameInterface : ::GameInterface
|
|||
bool GenerateSavePic() override;
|
||||
void MenuOpened() override;
|
||||
void MenuSound(EMenuSounds snd) override;
|
||||
bool StartGame(FNewGameStartup& gs) override;
|
||||
FSavegameInfo GetSaveSig() override;
|
||||
void SerializeGameState(FSerializer& arc);
|
||||
bool CanSave() override;
|
||||
|
|
|
@ -49,32 +49,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "automap.h"
|
||||
#include "raze_music.h"
|
||||
#include "v_draw.h"
|
||||
#include "vm.h"
|
||||
|
||||
BEGIN_PS_NS
|
||||
|
||||
short nBestLevel;
|
||||
|
||||
extern uint8_t nCinemaSeen;
|
||||
|
||||
void RunCinemaScene(int num);
|
||||
void GameMove(void);
|
||||
void DrawClock();
|
||||
double calc_smoothratio();
|
||||
void DoTitle(CompletionFunc completion);
|
||||
|
||||
static void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray<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()
|
||||
|
@ -83,7 +69,7 @@ void GameInterface::Render()
|
|||
drawtime.Reset();
|
||||
drawtime.Clock();
|
||||
|
||||
if (currentLevel && currentLevel->levelNumber == kMap20)
|
||||
if (currentLevel && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
|
||||
{
|
||||
DoEnergyTile();
|
||||
DrawClock();
|
||||
|
@ -127,104 +113,89 @@ void GameInterface::DrawBackground()
|
|||
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 0);
|
||||
}
|
||||
|
||||
void GameInterface::NextLevel(MapRecord *map, int skill)
|
||||
{
|
||||
InitLevel(map);
|
||||
|
||||
if (map->levelNumber >= nBestLevel)
|
||||
{
|
||||
nBestLevel = map->levelNumber - 1;
|
||||
}
|
||||
|
||||
STAT_NewLevel(currentLevel->labelName);
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void Intermission(MapRecord *from_map, MapRecord *to_map)
|
||||
{
|
||||
TArray<JobDesc> jobs;
|
||||
|
||||
if (from_map) StopAllSounds();
|
||||
bCamera = false;
|
||||
automapMode = am_off;
|
||||
|
||||
if (to_map)
|
||||
{
|
||||
if (to_map->levelNumber != 0)
|
||||
nBestLevel = to_map->levelNumber - 1;
|
||||
|
||||
STAT_Update(false);
|
||||
if (to_map->levelNumber == kMap20)
|
||||
nPlayerLives[0] = 0;
|
||||
|
||||
if (to_map->levelNumber == 0) // skip all intermission stuff when going to the training map.
|
||||
{
|
||||
gameaction = ga_nextlevel;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoAfterCinemaScene(to_map->levelNumber - 1, jobs);
|
||||
}
|
||||
if (to_map->levelNumber > -1 && to_map->levelNumber < kMap20)
|
||||
{
|
||||
// start a new game at the given level
|
||||
if (!nNetPlayerCount && to_map->levelNumber > 0)
|
||||
{
|
||||
showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs);
|
||||
}
|
||||
else
|
||||
jobs.Push({ Create<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
|
||||
else
|
||||
{
|
||||
if (to_map->levelNumber != selectedlevelnew)
|
||||
{
|
||||
// User can switch destination on the scrolling map.
|
||||
g_nextmap = FindMapByLevelNum(selectedlevelnew);
|
||||
STAT_Cancel();
|
||||
}
|
||||
gameaction = ga_nextlevel;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GameInterface::NextLevel(MapRecord *map, int skill)
|
||||
{
|
||||
InitLevel(map->levelNumber);
|
||||
|
||||
if (map->levelNumber > nBestLevel)
|
||||
{
|
||||
nBestLevel = selectedlevelnew;
|
||||
}
|
||||
|
||||
if (map->levelNumber == 11) nCinemaSeen |= 2;
|
||||
STAT_NewLevel(currentLevel->labelName);
|
||||
|
||||
}
|
||||
|
||||
void GameInterface::NewGame(MapRecord *map, int skill, bool frommenu)
|
||||
{
|
||||
// start a new game on the given level
|
||||
InitNewGame();
|
||||
if (map->levelNumber == 1) STAT_StartNewGame("Exhumed", 1);
|
||||
Intermission(nullptr, map);
|
||||
InitLevel(map);
|
||||
gameaction = ga_level;
|
||||
}
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
||||
int selectedlevelnew;
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DMapScreen, SetNextLevel)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(v);
|
||||
selectedlevelnew = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *to_map, int skill)
|
||||
{
|
||||
Mus_Stop();
|
||||
if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu;
|
||||
Intermission(currentLevel, map);
|
||||
if (currentLevel->gameflags & LEVEL_EX_TRAINING)
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
return;
|
||||
}
|
||||
|
||||
StopAllSounds();
|
||||
bCamera = false;
|
||||
automapMode = am_off;
|
||||
|
||||
STAT_Update(to_map == nullptr);
|
||||
if (to_map)
|
||||
{
|
||||
if (to_map->levelNumber > nBestLevel) nBestLevel = to_map->levelNumber - 1;
|
||||
|
||||
if (to_map->gameflags & LEVEL_EX_COUNTDOWN) nPlayerLives[0] = 0;
|
||||
if (to_map->gameflags & LEVEL_EX_TRAINING)
|
||||
{
|
||||
gameaction = ga_nextlevel;
|
||||
return;
|
||||
}
|
||||
}
|
||||
SummaryInfo info{};
|
||||
info.kills = nCreaturesKilled;
|
||||
info.maxkills = nCreaturesTotal;
|
||||
info.supersecrets = nBestLevel;
|
||||
info.time = PlayClock * GameTicRate / 120;
|
||||
selectedlevelnew = to_map->levelNumber;
|
||||
ShowIntermission(currentLevel, to_map, &info, [=](bool)
|
||||
{
|
||||
if (!to_map) gameaction = ga_startup; // this was the end of the game
|
||||
else
|
||||
{
|
||||
if (to_map->levelNumber != selectedlevelnew)
|
||||
{
|
||||
// User can switch destination on the scrolling map.
|
||||
g_nextmap = FindMapByLevelNum(selectedlevelnew);
|
||||
STAT_Cancel();
|
||||
}
|
||||
gameaction = ga_nextlevel;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -237,22 +208,7 @@ void GameInterface::Startup()
|
|||
{
|
||||
resettiming();
|
||||
EndLevel = 0;
|
||||
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
/*
|
||||
auto map = FindMapByName(userConfig.CommandMap);
|
||||
if (map) DeferedStartMap(map, 0);
|
||||
userConfig.CommandMap = "";
|
||||
goto again;
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; });
|
||||
else gameaction = ga_mainmenu;
|
||||
}
|
||||
|
||||
PlayLogos(ga_mainmenu, ga_mainmenu, false);
|
||||
}
|
||||
|
||||
void GameInterface::ErrorCleanup()
|
||||
|
|
|
@ -66,9 +66,9 @@ uint8_t bIsVersion6 = true;
|
|||
|
||||
|
||||
|
||||
uint8_t LoadLevel(int nMap)
|
||||
uint8_t LoadLevel(MapRecord* map)
|
||||
{
|
||||
if (nMap == kMap20)
|
||||
if (map->gameflags & LEVEL_EX_COUNTDOWN)
|
||||
{
|
||||
lCountDown = 81000;
|
||||
nAlarmTicks = 30;
|
||||
|
@ -116,12 +116,12 @@ uint8_t LoadLevel(int nMap)
|
|||
InitItems();
|
||||
InitInput();
|
||||
|
||||
if (nMap == kMap20) {
|
||||
if (map->gameflags & LEVEL_EX_COUNTDOWN) {
|
||||
InitEnergyTile();
|
||||
}
|
||||
}
|
||||
|
||||
if (nMap > 15)
|
||||
if (map->gameflags & LEVEL_EX_ALTSOUND)
|
||||
{
|
||||
nSwitchSound = 35;
|
||||
nStoneSound = 23;
|
||||
|
@ -136,10 +136,6 @@ uint8_t LoadLevel(int nMap)
|
|||
nStopSound = 66;
|
||||
}
|
||||
|
||||
if (nMap < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vec3_t startPos;
|
||||
engineLoadBoard(currentLevel->fileName, 0, &startPos, &inita, &initsect);
|
||||
initx = startPos.x;
|
||||
|
@ -172,12 +168,12 @@ uint8_t LoadLevel(int nMap)
|
|||
return true;
|
||||
}
|
||||
|
||||
void InitLevel(int level) // todo: use a map record
|
||||
void InitLevel(MapRecord* map)
|
||||
{
|
||||
StopCD();
|
||||
currentLevel = FindMapByLevelNum(level);
|
||||
if (!LoadLevel(level)) {
|
||||
I_Error("Can't load level %d...\n", level);
|
||||
currentLevel = map;
|
||||
if (!LoadLevel(map)) {
|
||||
I_Error("Cannot load %s...\n", map->fileName.GetChars());
|
||||
}
|
||||
|
||||
for (int i = 0; i < nTotalPlayers; i++)
|
||||
|
@ -200,17 +196,14 @@ void InitLevel(int level) // todo: use a map record
|
|||
|
||||
RefreshStatus();
|
||||
|
||||
int nTrack = level;
|
||||
if (nTrack != 0) nTrack--;
|
||||
|
||||
playCDtrack((nTrack % 8) + 11, true);
|
||||
if (!mus_redbook && map->music.IsNotEmpty()) Mus_Play(map->labelName, map->music, true); // Allow non-CD music if defined for the current level
|
||||
playCDtrack(map->cdSongId, true);
|
||||
setLevelStarted(currentLevel);
|
||||
}
|
||||
|
||||
void InitNewGame()
|
||||
{
|
||||
bCamera = false;
|
||||
nCinemaSeen = 0;
|
||||
PlayerCount = 0;
|
||||
|
||||
for (int i = 0; i < nTotalPlayers; i++)
|
||||
|
|
|
@ -432,7 +432,7 @@ void StartRegenerate(short nSprite)
|
|||
pSprite->extra = 1350;
|
||||
pSprite->ang = nFirstRegenerate;
|
||||
|
||||
if (currentLevel->levelNumber <= kMap20)
|
||||
if (!(currentLevel->gameflags & LEVEL_EX_MULTI))
|
||||
{
|
||||
pSprite->ang /= 5;
|
||||
}
|
||||
|
|
|
@ -43,8 +43,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
BEGIN_PS_NS
|
||||
|
||||
|
||||
uint8_t nCinemaSeen;
|
||||
|
||||
uint8_t energytile[66 * 66] = {0};
|
||||
|
||||
uint8_t *cur;
|
||||
|
|
|
@ -207,7 +207,7 @@ void MoveThings()
|
|||
DoMovingSects();
|
||||
DoRegenerates();
|
||||
|
||||
if (currentLevel->levelNumber == kMap20)
|
||||
if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)
|
||||
{
|
||||
DoFinale();
|
||||
if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart)
|
||||
|
|
|
@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "s_music.h"
|
||||
#include "screenjob.h"
|
||||
#include "v_draw.h"
|
||||
#include "vm.h"
|
||||
|
||||
BEGIN_PS_NS
|
||||
|
||||
|
@ -63,9 +64,34 @@ class LMFPlayer
|
|||
AudioData audio{};
|
||||
AnimTextures animtex;
|
||||
|
||||
FileReader fp;
|
||||
|
||||
int nFrame = 0;
|
||||
|
||||
uint64_t nextclock = 0;
|
||||
|
||||
public:
|
||||
|
||||
LMFPlayer(const char *filename)
|
||||
{
|
||||
fp = fileSystem.OpenFileReader(filename);
|
||||
Open(fp);
|
||||
}
|
||||
|
||||
bool Frame(uint64_t clock)
|
||||
{
|
||||
if (clock >= nextclock)
|
||||
{
|
||||
nextclock += 100'000'000;
|
||||
if (ReadFrame(fp) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int ReadFrame(FileReader& fp)
|
||||
{
|
||||
nFrame++;
|
||||
|
@ -188,105 +214,56 @@ public:
|
|||
S_StopCustomStream(stream);
|
||||
}
|
||||
|
||||
AnimTextures& animTex()
|
||||
FTextureID GetTexture()
|
||||
{
|
||||
return animtex;
|
||||
return animtex.GetFrameID();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DLmfPlayer : public DSkippableScreenJob
|
||||
int IdentifyLMF(const FString* fn)
|
||||
{
|
||||
LMFPlayer decoder;
|
||||
double angle = 1536;
|
||||
double z = 0;
|
||||
uint64_t nextclock = 0, lastclock = 0;
|
||||
FileReader fp;
|
||||
|
||||
public:
|
||||
|
||||
DLmfPlayer(FileReader& fr)
|
||||
{
|
||||
decoder.Open(fr);
|
||||
lastclock = 0;
|
||||
nextclock = 0;
|
||||
fp = std::move(fr);
|
||||
pausable = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate;
|
||||
if (clock >= nextclock)
|
||||
{
|
||||
nextclock += 100'000'000;
|
||||
if (decoder.ReadFrame(fp) == 0)
|
||||
{
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
double duration = (clock - lastclock) * double(120. / 8'000'000'000);
|
||||
if (z < 65536) { // Zoom - normal zoom is 65536.
|
||||
z += 2048 * duration;
|
||||
}
|
||||
if (z > 65536) z = 65536;
|
||||
if (angle != 0) {
|
||||
angle += 16. * duration;
|
||||
if (angle >= 2048) {
|
||||
angle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, decoder.animTex().GetFrame(), 160, 100, DTA_FullscreenScale, FSMode_Fit320x200,
|
||||
DTA_CenterOffset, true, DTA_FlipY, true, DTA_ScaleX, z / 65536., DTA_ScaleY, z / 65536., DTA_Rotate, (-angle - 512) * BAngToDegree, TAG_DONE);
|
||||
}
|
||||
|
||||
lastclock = clock;
|
||||
}
|
||||
|
||||
void OnDestroy() override
|
||||
{
|
||||
decoder.Close();
|
||||
fp.Close();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
DScreenJob* PlayMovie(const char* fileName)
|
||||
{
|
||||
// clear keys
|
||||
|
||||
auto fp = fileSystem.OpenFileReader(fileName);
|
||||
if (!fp.isOpen())
|
||||
{
|
||||
return Create<DBlackScreen>(1);
|
||||
}
|
||||
auto fp = fileSystem.OpenFileReader(*fn);
|
||||
if (!fp.isOpen()) return false;
|
||||
char buffer[4];
|
||||
fp.Read(buffer, 4);
|
||||
if (memcmp(buffer, "LMF ", 4))
|
||||
{
|
||||
fp.Close();
|
||||
// Allpw replacement with more modern formats.
|
||||
return PlayVideo(fileName);
|
||||
}
|
||||
fp.Seek(0, FileReader::SeekSet);
|
||||
return Create<DLmfPlayer>(fp);
|
||||
return (0 == memcmp(buffer, "LMF ", 4));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_LMFDecoder, Create)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(fn);
|
||||
ACTION_RETURN_POINTER(new LMFPlayer(fn));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_LMFDecoder, Identify, IdentifyLMF)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(fn);
|
||||
ACTION_RETURN_BOOL(IdentifyLMF(&fn));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_LMFDecoder, Frame)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer);
|
||||
PARAM_FLOAT(clock);
|
||||
ACTION_RETURN_BOOL(self->Frame(uint64_t(clock)));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_LMFDecoder, GetTexture)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer);
|
||||
ACTION_RETURN_INT(self->GetTexture().GetIndex());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_LMFDecoder, Close)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer);
|
||||
self->Close();
|
||||
delete self;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
END_PS_NS
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
x(SkullJaw, 3437)
|
||||
x(PowerslaveLogo, 3442)
|
||||
x(MenuNewGameTile, 3460)
|
||||
x(MenuLoadGameTile, 3461)
|
||||
|
@ -6,6 +7,7 @@ x(MenuSoundFxTile, 3466)
|
|||
x(MenuCursorTile, 3468)
|
||||
x(MenuBlank, 3469)
|
||||
x(SkullHead, 3582)
|
||||
x(SkullJaw2, 3583)
|
||||
x(ExhumedLogo, 3592)
|
||||
x(Energy1, 3604)
|
||||
x(Energy2, 3605)
|
||||
|
@ -27,4 +29,100 @@ x(ClockSymbol15, 3620)
|
|||
x(ClockSymbol16, 3621)
|
||||
x(TileLoboLaptop, 3623)
|
||||
x(TileBMGLogo, 3368)
|
||||
x(LobotomyLogo, 3348)
|
||||
x(TilePIELogo, 3349)
|
||||
x(Gameover, 3591)
|
||||
|
||||
x(MapPlaque1_01, 3376)
|
||||
x(MapPlaque1_02, 3378)
|
||||
x(MapPlaque1_03, 3380)
|
||||
x(MapPlaque1_04, 3382)
|
||||
x(MapPlaque1_05, 3384)
|
||||
x(MapPlaque1_06, 3371)
|
||||
x(MapPlaque1_07, 3387)
|
||||
x(MapPlaque1_08, 3389)
|
||||
x(MapPlaque1_09, 3391)
|
||||
x(MapPlaque1_10, 3409)
|
||||
x(MapPlaque1_11, 3393)
|
||||
x(MapPlaque1_12, 3395)
|
||||
x(MapPlaque1_13, 3397)
|
||||
x(MapPlaque1_14, 3399)
|
||||
x(MapPlaque1_15, 3401)
|
||||
x(MapPlaque1_16, 3403)
|
||||
x(MapPlaque1_17, 3405)
|
||||
x(MapPlaque1_18, 3407)
|
||||
x(MapPlaque1_19, 3412)
|
||||
x(MapPlaque1_20, 3415)
|
||||
|
||||
x(MapPlaque2_01, 3377)
|
||||
x(MapPlaque2_02, 3379)
|
||||
x(MapPlaque2_03, 3381)
|
||||
x(MapPlaque2_04, 3383)
|
||||
x(MapPlaque2_05, 3385)
|
||||
x(MapPlaque2_06, 3386)
|
||||
x(MapPlaque2_07, 3388)
|
||||
x(MapPlaque2_08, 3390)
|
||||
x(MapPlaque2_09, 3392)
|
||||
x(MapPlaque2_10, 3410)
|
||||
x(MapPlaque2_11, 3394)
|
||||
x(MapPlaque2_12, 3396)
|
||||
x(MapPlaque2_13, 3398)
|
||||
x(MapPlaque2_14, 3400)
|
||||
x(MapPlaque2_15, 3402)
|
||||
x(MapPlaque2_16, 3404)
|
||||
x(MapPlaque2_17, 3406)
|
||||
x(MapPlaque2_18, 3408)
|
||||
x(MapPlaque2_19, 3413)
|
||||
x(MapPlaque2_20, 3416)
|
||||
|
||||
x(MapPlaqueText_01, 3411)
|
||||
x(MapPlaqueText_02, 3414)
|
||||
x(MapPlaqueText_03, 3417)
|
||||
x(MapPlaqueText_04, 3420)
|
||||
x(MapPlaqueText_05, 3423)
|
||||
x(MapPlaqueText_06, 3426)
|
||||
x(MapPlaqueText_07, 3429)
|
||||
x(MapPlaqueText_08, 3432)
|
||||
x(MapPlaqueText_09, 3435)
|
||||
x(MapPlaqueText_10, 3418)
|
||||
x(MapPlaqueText_11, 3438)
|
||||
x(MapPlaqueText_12, 3441)
|
||||
x(MapPlaqueText_13, 3444)
|
||||
x(MapPlaqueText_14, 3447)
|
||||
x(MapPlaqueText_15, 3450)
|
||||
x(MapPlaqueText_16, 3453)
|
||||
x(MapPlaqueText_17, 3456)
|
||||
x(MapPlaqueText_18, 3459)
|
||||
x(MapPlaqueText_19, 3419)
|
||||
x(MapPlaqueText_20, 3421)
|
||||
|
||||
x(MapFire_11, 3484)
|
||||
x(MapFire_12, 3485)
|
||||
x(MapFire_13, 3486)
|
||||
x(MapFire_14, 3487)
|
||||
x(MapFire_21, 3488)
|
||||
x(MapFire_22, 3489)
|
||||
x(MapFire_23, 3490)
|
||||
x(MapFire_24, 3491)
|
||||
x(MapFire_31, 3492)
|
||||
x(MapFire_32, 3493)
|
||||
x(MapFire_33, 3494)
|
||||
x(MapFire_34, 3495)
|
||||
|
||||
x(MapBG01, 3353)
|
||||
x(MapBG02, 3354)
|
||||
x(MapBG03, 3355)
|
||||
x(MapBG04, 3356)
|
||||
x(MapBG05, 3357)
|
||||
x(MapBG06, 3358)
|
||||
x(MapBG07, 3359)
|
||||
x(MapBG08, 3360)
|
||||
x(MapBG09, 3361)
|
||||
x(MapBG10, 3362)
|
||||
|
||||
x(TileCinema5, 3449)
|
||||
x(TileCinema10, 3451)
|
||||
x(TileCinema11, 3454)
|
||||
x(TileCinema15, 3446)
|
||||
x(TileCinemaLose, 3445)
|
||||
x(TileCinema20, 3448)
|
||||
|
|
|
@ -29,136 +29,10 @@ kTileStatusBar = 657,
|
|||
kTile985 = 985,
|
||||
kTile986 = 986,
|
||||
kTile3000 = 3000,
|
||||
kTile3117 = 3117,
|
||||
kQueenChunk = 3117,
|
||||
kTile3126 = 3126,
|
||||
kTile3353 = 3353,
|
||||
kTile3370 = 3370,
|
||||
kTile3371 = 3371,
|
||||
kTile3372 = 3372,
|
||||
kTile3373 = 3373,
|
||||
kTile3374 = 3374,
|
||||
kTile3375 = 3375,
|
||||
kTile3376 = 3376,
|
||||
kTile3377 = 3377,
|
||||
kTile3378 = 3378,
|
||||
kTile3379 = 3379,
|
||||
kTile3380 = 3380,
|
||||
kTile3381 = 3381,
|
||||
kTile3382 = 3382,
|
||||
kTile3383 = 3383,
|
||||
kTile3384 = 3384,
|
||||
kTile3385 = 3385,
|
||||
kTile3386 = 3386,
|
||||
kTile3387 = 3387,
|
||||
kTile3388 = 3388,
|
||||
kTile3389 = 3389,
|
||||
kTile3390 = 3390,
|
||||
kTile3391 = 3391,
|
||||
kTile3392 = 3392,
|
||||
kTile3393 = 3393,
|
||||
kTile3394 = 3394,
|
||||
kTile3395 = 3395,
|
||||
kTile3396 = 3396,
|
||||
kTile3397 = 3397,
|
||||
kTile3398 = 3398,
|
||||
kTile3399 = 3399,
|
||||
kTile3400 = 3400,
|
||||
kTile3401 = 3401,
|
||||
kTile3402 = 3402,
|
||||
kTile3403 = 3403,
|
||||
kTile3404 = 3404,
|
||||
kTile3405 = 3405,
|
||||
kTile3406 = 3406,
|
||||
kTile3407 = 3407,
|
||||
kTile3408 = 3408,
|
||||
kTile3409 = 3409,
|
||||
kTile3410 = 3410,
|
||||
kTile3411 = 3411,
|
||||
kTile3412 = 3412,
|
||||
kTile3413 = 3413,
|
||||
kTile3414 = 3414,
|
||||
kTile3415 = 3415,
|
||||
kTile3416 = 3416,
|
||||
kTile3417 = 3417,
|
||||
kTile3418 = 3418,
|
||||
kTile3419 = 3419,
|
||||
kTile3420 = 3420,
|
||||
kTile3421 = 3421,
|
||||
kTile3422 = 3422,
|
||||
kTile3423 = 3423,
|
||||
kTile3424 = 3424,
|
||||
kTile3425 = 3425,
|
||||
kTile3426 = 3426,
|
||||
kTile3427 = 3427,
|
||||
kTile3428 = 3428,
|
||||
kTile3429 = 3429,
|
||||
kTile3430 = 3430,
|
||||
kTile3431 = 3431,
|
||||
kTile3432 = 3432,
|
||||
kTile3433 = 3433,
|
||||
kTile3434 = 3434,
|
||||
kTile3435 = 3435,
|
||||
kTile3436 = 3436,
|
||||
kSkullJaw = 3437,
|
||||
kTile3438 = 3438,
|
||||
kTile3439 = 3439,
|
||||
kTile3440 = 3440,
|
||||
kTile3441 = 3441,
|
||||
kTile3443 = 3443,
|
||||
kTile3444 = 3444,
|
||||
kTile3445 = 3445,
|
||||
kTile3446 = 3446,
|
||||
kTile3447 = 3447,
|
||||
kTile3448 = 3448,
|
||||
kTile3449 = 3449,
|
||||
kTile3450 = 3450,
|
||||
kTile3451 = 3451,
|
||||
kTile3452 = 3452,
|
||||
kTile3453 = 3453,
|
||||
kTile3454 = 3454,
|
||||
kTile3455 = 3455,
|
||||
kTile3456 = 3456,
|
||||
kTile3457 = 3457,
|
||||
kTile3458 = 3458,
|
||||
kTile3459 = 3459,
|
||||
kTile3462 = 3462,
|
||||
kTile3463 = 3463,
|
||||
kTile3464 = 3464,
|
||||
kTile3467 = 3467,
|
||||
kTile3470 = 3470,
|
||||
kTile3471 = 3471,
|
||||
kTile3472 = 3472,
|
||||
kTile3473 = 3473,
|
||||
kTile3474 = 3474,
|
||||
kTile3475 = 3475,
|
||||
kTile3476 = 3476,
|
||||
kTile3477 = 3477,
|
||||
kTile3478 = 3478,
|
||||
kTile3479 = 3479,
|
||||
kTile3480 = 3480,
|
||||
kTile3481 = 3481,
|
||||
kTile3482 = 3482,
|
||||
kTile3483 = 3483,
|
||||
kTile3484 = 3484,
|
||||
kTile3485 = 3485,
|
||||
kTile3486 = 3486,
|
||||
kTile3487 = 3487,
|
||||
kTile3488 = 3488,
|
||||
kTile3489 = 3489,
|
||||
kTile3490 = 3490,
|
||||
kTile3491 = 3491,
|
||||
kTile3492 = 3492,
|
||||
kTile3493 = 3493,
|
||||
kTile3494 = 3494,
|
||||
kTile3495 = 3495,
|
||||
kTile3496 = 3496,
|
||||
kTile3497 = 3497,
|
||||
kTile3498 = 3498,
|
||||
kTile3499 = 3499,
|
||||
kTile3512 = 3512,
|
||||
kTile3571 = 3571,
|
||||
kTile3583 = 3583,
|
||||
kTile3591 = 3591,
|
||||
kTile3593 = 3593,
|
||||
kTile3603 = 3603,
|
||||
kTile4092 = 4092,
|
||||
|
|
|
@ -2254,7 +2254,7 @@ FUNCOBJECT_GOTO:
|
|||
}
|
||||
}
|
||||
|
||||
if (currentLevel->levelNumber <= 20 || nStat != kStatExplodeTrigger)
|
||||
if (!(currentLevel->gameflags & LEVEL_EX_MULTI) || nStat != kStatExplodeTrigger)
|
||||
{
|
||||
runlist_SubRunRec(sprite[nSprite].owner);
|
||||
runlist_SubRunRec(ObjectList[nObject].field_4);
|
||||
|
@ -2689,7 +2689,7 @@ void PostProcess()
|
|||
}
|
||||
}
|
||||
|
||||
if (currentLevel->levelNumber != kMap20)
|
||||
if (!(currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
|
||||
{
|
||||
// esi is i
|
||||
for (i = 0; i < numsectors; i++)
|
||||
|
|
|
@ -417,7 +417,7 @@ void RestartPlayer(short nPlayer)
|
|||
plr->nAir = 100;
|
||||
airpages = 0;
|
||||
|
||||
if (currentLevel->levelNumber <= kMap20)
|
||||
if (!(currentLevel->gameflags & LEVEL_EX_MULTI))
|
||||
{
|
||||
RestoreMinAmmo(nPlayer);
|
||||
}
|
||||
|
@ -570,7 +570,7 @@ void StartDeathSeq(int nPlayer, int nVal)
|
|||
BuildStatusAnim((3 * (nLives - 1)) + 7, 0);
|
||||
}
|
||||
|
||||
if (currentLevel->levelNumber > 0) { // if not on the training level
|
||||
if (!(currentLevel->gameflags & LEVEL_EX_TRAINING)) { // if not on the training level
|
||||
nPlayerLives[nPlayer]--;
|
||||
}
|
||||
|
||||
|
@ -679,7 +679,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
|
|||
{
|
||||
int var_48 = 0;
|
||||
int var_40;
|
||||
bool mplevel = currentLevel->levelNumber > 20;
|
||||
bool mplevel = (currentLevel->gameflags & LEVEL_EX_MULTI);
|
||||
|
||||
short nPlayer = RunData[nRun].nVal;
|
||||
assert(nPlayer >= 0 && nPlayer < kMaxPlayers);
|
||||
|
@ -1033,14 +1033,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
|
|||
InitSpiritHead();
|
||||
|
||||
PlayerList[nPlayer].nDestVertPan = q16horiz(0);
|
||||
if (currentLevel->levelNumber == 11)
|
||||
{
|
||||
PlayerList[nPlayer].horizon.settarget(46);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerList[nPlayer].horizon.settarget(11);
|
||||
}
|
||||
PlayerList[nPlayer].horizon.settarget(currentLevel->ex_ramses_horiz);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1445,7 +1445,7 @@ void FuncQueen(int a, int nDamage, int nRun)
|
|||
{
|
||||
short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF;
|
||||
|
||||
sprite[nChunkSprite].picnum = kTile3117 + (i % 3);
|
||||
sprite[nChunkSprite].picnum = kQueenChunk + (i % 3);
|
||||
sprite[nChunkSprite].xrepeat = 100;
|
||||
sprite[nChunkSprite].yrepeat = 100;
|
||||
}
|
||||
|
|
|
@ -64,8 +64,6 @@ short nTalkTime = 0;
|
|||
|
||||
void InitSpiritHead()
|
||||
{
|
||||
char filename[20];
|
||||
|
||||
nPixels = 0;
|
||||
|
||||
nSpiritRepeatX = sprite[nSpiritSprite].xrepeat;
|
||||
|
@ -137,26 +135,15 @@ void InitSpiritHead()
|
|||
|
||||
fadecdaudio();
|
||||
|
||||
int nTrack;
|
||||
|
||||
if (currentLevel->levelNumber == 1)
|
||||
{
|
||||
nTrack = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
nTrack = 7;
|
||||
}
|
||||
|
||||
int nTrack = currentLevel->ex_ramses_cdtrack;
|
||||
playCDtrack(nTrack, false);
|
||||
|
||||
StartSwirlies();
|
||||
|
||||
sprintf(filename, "LEV%d.PUP", currentLevel->levelNumber);
|
||||
lNextStateChange = PlayClock;
|
||||
lHeadStartClock = PlayClock;
|
||||
|
||||
auto headfd = fileSystem.OpenFileReader(filename);
|
||||
auto headfd = fileSystem.OpenFileReader(currentLevel->ex_ramses_pup);
|
||||
if (!headfd.isOpen())
|
||||
{
|
||||
memset(cPupData, 0, sizeof(cPupData));
|
||||
|
|
|
@ -117,7 +117,7 @@ void GameInterface::SerializeGameState(FSerializer& arc)
|
|||
parallaxtype = 2;
|
||||
g_visibility = 1024;
|
||||
|
||||
if (currentLevel->levelNumber > 15)
|
||||
if (currentLevel->gameflags & LEVEL_EX_ALTSOUND)
|
||||
{
|
||||
nSwitchSound = 35;
|
||||
nStoneSound = 23;
|
||||
|
|
|
@ -724,7 +724,7 @@ void CheckAmbience(short nSector)
|
|||
|
||||
void UpdateCreepySounds()
|
||||
{
|
||||
if (currentLevel->levelNumber == 20 || nFreeze || !SoundEnabled())
|
||||
if ((currentLevel->gameflags & LEVEL_EX_COUNTDOWN) || nFreeze || !SoundEnabled())
|
||||
return;
|
||||
spritetype* pSprite = &sprite[PlayerList[nLocalPlayer].nSprite];
|
||||
nCreepyTimer--;
|
||||
|
@ -800,4 +800,35 @@ void PlayGameOverSound(void)
|
|||
PlayLocalSound(StaticSound[kSoundJonLaugh2], 0, false, CHANF_UI);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Exhumed, PlayLocalSound)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(snd);
|
||||
PARAM_INT(pitch);
|
||||
PARAM_BOOL(unatt);
|
||||
PARAM_INT(flags);
|
||||
PlayLocalSound(StaticSound[snd], pitch, unatt, EChanFlags::FromInt(flags));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Exhumed, StopLocalSound, StopLocalSound)
|
||||
{
|
||||
StopLocalSound();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Exhumed, LocalSoundPlaying)
|
||||
{
|
||||
ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Exhumed, PlayCDTrack)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(track);
|
||||
PARAM_BOOL(loop);
|
||||
playCDtrack(track, loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
END_PS_NS
|
||||
|
|
|
@ -408,10 +408,7 @@ void DrawView(double smoothRatio, bool sceneonly)
|
|||
if (bSubTitles)
|
||||
{
|
||||
subtitleOverlay.Start(I_GetTimeNS() * (120. / 1'000'000'000));
|
||||
if (currentLevel->levelNumber == 1)
|
||||
subtitleOverlay.ReadyCinemaText(1);
|
||||
else
|
||||
subtitleOverlay.ReadyCinemaText(5);
|
||||
subtitleOverlay.ReadyCinemaText(currentLevel->ex_ramses_text);
|
||||
}
|
||||
inputState.ClearAllInput();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include "src/2d.cpp"
|
||||
#include "src/actor.cpp"
|
||||
#include "src/ai.cpp"
|
||||
#include "src/break.cpp"
|
||||
|
|
|
@ -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
|
|
@ -116,7 +116,7 @@ bool NextCheat(cheatseq_t* c)
|
|||
{
|
||||
if (!checkCheat(c)) return false;
|
||||
if (!currentLevel) return true;
|
||||
auto map = FindMapByLevelNum(currentLevel->levelNumber + 1);
|
||||
auto map = FindNextMap(currentLevel);
|
||||
if (map) DeferedStartGame(map, -1);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -102,22 +102,8 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
|
|||
int handle = 0;
|
||||
int zero = 0;
|
||||
|
||||
MapRecord* map;
|
||||
if (gs.Episode >= 1)
|
||||
{
|
||||
if (g_gameType & GAMEFLAG_SHAREWARE)
|
||||
{
|
||||
M_StartMessage(GStrings("BUYSW"), 1, NAME_None);
|
||||
return false;
|
||||
}
|
||||
map = FindMapByLevelNum(5);
|
||||
}
|
||||
else
|
||||
map = FindMapByLevelNum(1);
|
||||
|
||||
if (!map) return false;
|
||||
CameraTestMode = false;
|
||||
StopFX();
|
||||
StopAmbientSound();
|
||||
|
||||
//InitNewGame();
|
||||
|
||||
|
@ -140,7 +126,6 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
|
|||
}
|
||||
Net_ClearFifo();
|
||||
}
|
||||
DeferedStartGame(map, gs.Skill);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1681,7 +1681,9 @@ drawscreen(PLAYERp pp, double smoothratio)
|
|||
|
||||
if (paused && !M_Active())
|
||||
{
|
||||
MNU_DrawString(160, 100, "Game Paused", 0, 0, 0);
|
||||
auto str = GStrings("Game Paused");
|
||||
int w = SmallFont->StringWidth(str);
|
||||
DrawText(twod, SmallFont, CR_UNDEFINED, 160-w, 100, str, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
}
|
||||
|
||||
if (!CommEnabled && TEST(pp->Flags, PF_DEAD))
|
||||
|
|
|
@ -88,10 +88,6 @@ CVAR(Bool, sw_bunnyrockets, false, CVAR_SERVERINFO | CVAR_CHEAT); // This is a
|
|||
|
||||
BEGIN_SW_NS
|
||||
|
||||
void Logo(const CompletionFunc& completion);
|
||||
void StatScreen(int FinishAnim, CompletionFunc completion);
|
||||
|
||||
|
||||
void pClearSpriteList(PLAYERp pp);
|
||||
|
||||
extern int sw_snd_scratch;
|
||||
|
@ -154,7 +150,6 @@ FString ThemeSongs[6];
|
|||
int ThemeTrack[6];
|
||||
|
||||
/// L O C A L P R O T O T Y P E S /////////////////////////////////////////////////////////
|
||||
void SybexScreen(void);
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define x(a, b) registerName(#a, b);
|
||||
|
@ -253,12 +248,6 @@ void GameInterface::DrawBackground(void)
|
|||
twod->ClearScreen();
|
||||
DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal,
|
||||
DTA_Color, shadeToLight(20), TAG_DONE);
|
||||
|
||||
if (CommEnabled)
|
||||
{
|
||||
MNU_DrawString(160, 170, "Lo Wang is waiting for other players...", 1, 16, 0);
|
||||
MNU_DrawString(160, 180, "They are afraid!", 1, 16, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -282,9 +271,8 @@ void InitLevelGlobals(void)
|
|||
|
||||
gNet.TimeLimitClock = gNet.TimeLimit;
|
||||
|
||||
serpwasseen = false;
|
||||
sumowasseen = false;
|
||||
zillawasseen = false;
|
||||
|
||||
for (auto& b : bosswasseen) b = false;
|
||||
memset(BossSpriteNum,-1,sizeof(BossSpriteNum));
|
||||
}
|
||||
|
||||
|
@ -533,30 +521,37 @@ static void PlayOrderSound()
|
|||
|
||||
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
||||
void GameInterface::LevelCompleted(MapRecord* map, int skill)
|
||||
{
|
||||
//ResetPalette(mpp);
|
||||
COVER_SetReverb(0); // Reset reverb
|
||||
Player[myconnectindex].Reverb = 0;
|
||||
StopSound();
|
||||
//ResetPalette(mpp);
|
||||
COVER_SetReverb(0); // Reset reverb
|
||||
Player[myconnectindex].Reverb = 0;
|
||||
StopSound();
|
||||
STAT_Update(map == nullptr);
|
||||
|
||||
StatScreen(FinishAnim, [=](bool)
|
||||
{
|
||||
SummaryInfo info{};
|
||||
|
||||
info.kills = Player->Kills;
|
||||
info.maxkills = TotalKillable;
|
||||
info.secrets = Player->SecretsFound;
|
||||
info.maxsecrets = LevelSecrets;
|
||||
info.time = PlayClock / 120;
|
||||
|
||||
ShowIntermission(currentLevel, map, &info, [=](bool)
|
||||
{
|
||||
if (map == nullptr)
|
||||
{
|
||||
FinishAnim = false;
|
||||
PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]);
|
||||
if (SW_SHAREWARE)
|
||||
{
|
||||
FinishAnim = false;
|
||||
PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]);
|
||||
if (isShareware())
|
||||
{
|
||||
PlayOrderSound();
|
||||
gameaction = ga_creditsmenu;
|
||||
}
|
||||
else gameaction = ga_mainmenu;
|
||||
}
|
||||
else gameaction = ga_nextlevel;
|
||||
});
|
||||
|
||||
else gameaction = ga_mainmenu;
|
||||
}
|
||||
else gameaction = ga_nextlevel;
|
||||
});
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
@ -584,6 +579,7 @@ void GameInterface::NewGame(MapRecord *map, int skill, bool)
|
|||
ShadowWarrior::NewGame = true;
|
||||
InitLevel(map);
|
||||
InitRunLevel();
|
||||
gameaction = ga_level;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -633,17 +629,7 @@ void GameInterface::Render()
|
|||
|
||||
void GameInterface::Startup()
|
||||
{
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!userConfig.nologo) Logo([](bool)
|
||||
{
|
||||
gameaction = ga_mainmenunostopsound;
|
||||
});
|
||||
else gameaction = ga_mainmenu;
|
||||
}
|
||||
PlayLogos(ga_mainmenunostopsound, ga_mainmenu, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -77,7 +77,6 @@ extern GAME_SET gs;
|
|||
enum
|
||||
{
|
||||
DREALMSPAL = 1,
|
||||
THREED_REALMS_PIC = 2325,
|
||||
|
||||
MAXMIRRORS = 8,
|
||||
// This is just some, high, blank tile number not used
|
||||
|
@ -2225,9 +2224,7 @@ extern short wait_active_check_offset;
|
|||
//extern short Zombies;
|
||||
extern int PlaxCeilGlobZadjust, PlaxFloorGlobZadjust;
|
||||
extern bool left_foot;
|
||||
extern bool serpwasseen;
|
||||
extern bool sumowasseen;
|
||||
extern bool zillawasseen;
|
||||
extern bool bosswasseen[3];
|
||||
extern short BossSpriteNum[3];
|
||||
extern int ChopTics;
|
||||
extern short Bunny_Count;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -32,7 +32,6 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
|
|||
|
||||
BEGIN_SW_NS
|
||||
|
||||
void MNU_DrawString(int x, int y, const char* string, int shade, int pal, int align = -1);
|
||||
void MNU_DrawSmallString(int x, int y, const char* string, int shade, int pal, int align = -1, double alpha = 1);
|
||||
|
||||
END_SW_NS
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue