Merge branch 'scriptable_cutscenes' into newrenderer

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

View file

@ -749,7 +749,6 @@ set( NOT_COMPILED_SOURCE_FILES
games/blood/src/callback.cpp
games/blood/src/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

View file

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

View file

@ -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);

View file

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

View file

@ -371,7 +371,6 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer()
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
SetWindowLong(Window, GWL_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();
}

View file

@ -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())

View file

@ -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);

View file

@ -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));
}

View file

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

View file

@ -69,11 +69,11 @@ extern int16_t sintable[2048];
//
//---------------------------------------------------------------------------
inline int32_t bsin(const int ang, const int8_t shift = 0)
inline int bsin(const int ang, const int shift = 0)
{
return shift < 0 ? sintable[ang & 2047] >> abs(shift) : sintable[ang & 2047] << shift;
}
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)));
}

View file

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

File diff suppressed because it is too large Load diff

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

@ -0,0 +1,108 @@
/*
** g_level.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#ifndef __G_LEVEL_H__
#define __G_LEVEL_H__
#include "autosegs.h"
#include "vectors.h"
#include "sc_man.h"
#include "file_zip.h"
struct FMapInfoParser
{
FScanner sc;
bool Internal;
MapRecord* defaultinfoptr;
FMapInfoParser(bool internal = false)
{
Internal = internal;
}
bool CheckLegacyMapDefinition(FString& mapname);
bool ParseLookupName(FString &dest);
void ParseMusic(FString &name, int &order);
void ParseLumpOrTextureName(FString &name);
void ParseCutscene(CutsceneDef& cdef);
void ParseCluster();
void ParseMapName(FString &mapname);
MapRecord *ParseMapHeader(MapRecord &defaultinfo);
void ParseMapDefinition(MapRecord &leveldef);
void ParseEpisodeInfo ();
void ParseCutsceneInfo();
void ParseGameInfo();
void ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord &defaultinfo);
void ParseOpenBrace();
bool ParseCloseBrace();
bool CheckAssign();
void ParseAssign();
void MustParseAssign();
void ParseComma();
bool CheckNumber();
bool CheckFloat();
void SkipToNext();
void CheckEndOfFile(const char *block);
};
#if defined(_MSC_VER)
#pragma section(SECTION_YREG,read)
#define MSVC_YSEG __declspec(allocate(SECTION_YREG))
#define GCC_YSEG
#else
#define MSVC_YSEG
#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used))
#endif
#define DEFINE_MAP_OPTION(name, old) \
static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info); \
static FMapOptInfo MapOpt_##name = \
{ #name, MapOptHandler_##name, old }; \
MSVC_YSEG FMapOptInfo *mapopt_##name GCC_YSEG = &MapOpt_##name; \
static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info)
struct FMapOptInfo
{
const char *name;
void (*handler) (FMapInfoParser &parse, MapRecord *levelinfo);
bool old;
};
void G_ParseMapInfo();
#endif //__G_LEVEL_H__

View file

@ -73,6 +73,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "automap.h"
#include "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()

View file

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

View file

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

View file

@ -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) {}

View file

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

View file

@ -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 &map;
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 &map;
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 &map;
return map.Data();
}
}

View file

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

View file

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

View file

@ -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());
}

View file

@ -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();
}

View file

@ -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();

View file

@ -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;
*/

View file

@ -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_);

View file

@ -398,6 +398,12 @@ static TArray<GrpInfo> ParseGrpInfo(const char *fn, FileReader &fr, TMap<FString
FlagMap.Insert("GAMEFLAG_POWERSLAVE", GAMEFLAG_POWERSLAVE);
FlagMap.Insert("GAMEFLAG_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();

View file

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

View file

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

View file

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

View file

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

View file

@ -1,147 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h" // Must come before everything else!
#include "build.h"
#include "compat.h"
#include "SmackerDecoder.h"
#include "blood.h"
#include "animtexture.h"
#include "raze_sound.h"
#include "v_2ddrawer.h"
#include "screenjob.h"
#include "gamestate.h"
#include "razemenu.h"
BEGIN_BLD_NS
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void playlogos()
{
JobDesc jobs[6];
int job = 0;
static AnimSound logosound[] =
{
{ 1, -1 },
{ -1, -1 },
{ 1, -1 },
{ -1,-1 }
};
if (logosound[0].soundnum == -1)
{
logosound[0].soundnum = S_FindSound("logo.wav");
logosound[2].soundnum = S_FindSound("gt.wav");
}
if (!userConfig.nologo)
{
if (fileSystem.FindFile("logo.smk") != -1)
{
jobs[job++] = { PlayVideo("logo.smk", &logosound[0], 0) };
}
else
{
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
jobs[job++] = { Create<DImageScreen>(2050) };
}
if (fileSystem.FindFile("gti.smk") != -1)
{
jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) };
}
else
{
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
jobs[job++] = { Create<DImageScreen>(2052) };
}
}
jobs[job++] = { Create<DBlackScreen>(1), []() { sndPlaySpecialMusicOrNothing(MUS_INTRO); sndStartSample("THUNDER2", 128, -1); }};
jobs[job++] = { Create<DImageScreen>(2518, DScreenJob::fadein) };
RunScreenJob(jobs, job, [](bool) {
Mus_Stop();
gameaction = ga_mainmenu;
}, true, true);
}
void playSmk(const char *smk, const char *wav, int wavid, CompletionFunc func)
{
JobDesc jobs{};
static AnimSound smksound[] =
{
{ 1, -1 },
{ -1, -1 },
};
int id = S_FindSoundByResID(wavid);
if (id <= 0)
{
FString wavv = wav;
FixPathSeperator(wavv);
id = S_FindSound(wavv);
// Strip the drive letter and retry.
if (id <= 0 && wavv.Len() > 3 && wavv[1] == ':' && isalpha(wavv[0]) && wavv[2] == '/')
{
id = S_FindSound(wavv.GetChars() + 3);
}
}
FString smkk = smk;
FixPathSeperator(smkk);
smksound[0].soundnum = id;
jobs.job = PlayVideo(smkk, smksound, nullptr);
RunScreenJob(&jobs, 1, func);
}
void levelPlayIntroScene(int nEpisode, CompletionFunc completion)
{
Mus_Stop();
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
playSmk(pEpisode->cutsceneAName, pEpisode->cutsceneASound, pEpisode->at9028, completion);
}
void levelPlayEndScene(int nEpisode, CompletionFunc completion)
{
gGameOptions.uGameFlags &= ~GF_PlayCutscene;
Mus_Stop();
sndKillAllSounds();
sfxKillAllSounds();
ambKillAll();
seqKillAll();
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
playSmk(pEpisode->cutsceneBName, pEpisode->cutsceneBSound, pEpisode->at902c, completion);
}
END_BLD_NS

View file

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

View file

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

View file

@ -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()

View file

@ -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);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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);

View file

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

View file

@ -124,549 +124,5 @@ void InitFonts_r()
}
//==========================================================================
//
// wrappers around DrawText to allow easier reuse of the old code.
// The vertical displacements are to have the same positioning as with the original code.
//
//==========================================================================
static void BigText(double x, double y, const char* text, int align, double alpha = 1.)
{
//x *= 2.2; y *= 2.64;
if (align != -1)
x -= BigFont->StringWidth(text) * (align == 0 ? 0.2 : 0.4);
auto width = BigFont->StringWidth(text);
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, 0.4, DTA_ScaleY, 0.4, DTA_Alpha, alpha, TAG_DONE);
}
static void GameText(double x, double y, const char* t, int shade, int align = -1, int trans = 0)
{
x *= 2; y *= 2;
if (align != -1)
x -= SmallFont->StringWidth(t) * (align == 0 ? 0.5 : 1);
DrawText(twod, SmallFont, CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE);
}
static void MiniText(double x, double y, const char* t, int shade, int align = -1, int trans = 0)
{
x *= 2; y *= 2;
if (align != -1)
x -= SmallFont2->StringWidth(t) * (align == 0 ? 0.5 : 1);
DrawText(twod, SmallFont2, CR_UNDEFINED, x, y, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void Logo_r(const CompletionFunc& completion)
{
Mus_Stop();
FX_StopAllSounds(); // JBF 20031228
static const AnimSound introsound[] =
{
{ 1, 29+1 },
{ -1, -1 }
};
static const AnimSound rednecksound[] =
{
{ 1, 478+1 },
{ -1, -1 }
};
static const AnimSound xatrixsound[] =
{
{ 1, 479+1 },
{ -1, -1 }
};
static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims
JobDesc jobs[3];
int job = 0;
if (userConfig.nologo)
{
completion(false);
return;
}
else if (!isRRRA())
{
jobs[job++] = { PlayVideo("rr_intro.anm", introsound, framespeed), nullptr };
jobs[job++] = { PlayVideo("redneck.anm", rednecksound, framespeed), nullptr };
jobs[job++] = { PlayVideo("xatlogo.anm", xatrixsound, framespeed), nullptr };
}
else
{
jobs[job++] = { PlayVideo("redint.mve"), nullptr };
}
RunScreenJob(jobs, job, completion, true, true);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void bonussequence_r(int num, JobDesc* jobs, int& job)
{
static const AnimSound turdmov[] =
{
{ 1, 82 + 1 },
{ -1, -1 }
};
static const AnimSound rr_outro[] =
{
{ 1, 35 + 1 },
{ -1, -1 }
};
static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims
Mus_Stop();
FX_StopAllSounds();
switch (num)
{
case 0:
jobs[job++] = { PlayVideo("turdmov.anm", turdmov, framespeed), nullptr };
jobs[job++] = { Create<DImageScreen>(TENSCREEN), nullptr };
break;
case 1:
jobs[job++] = { PlayVideo("rr_outro.anm", rr_outro, framespeed), nullptr };
jobs[job++] = { Create<DImageScreen>(TENSCREEN), nullptr };
break;
default:
break;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DRRMultiplayerBonusScreen : public DScreenJob
{
int playerswhenstarted;
public:
DRRMultiplayerBonusScreen(int pws) : DScreenJob(fadein | fadeout)
{
playerswhenstarted = pws;
}
void Start() override
{
S_PlayBonusMusic();
}
void Draw(double) override
{
char tempbuf[32];
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
double scale = 0.36;
DrawTexture(twod, tileGetTexture(INGAMEDUKETHREEDEE, true), 160, 34, DTA_FullscreenScale, FSMode_Fit320x200,
DTA_CenterOffsetRel, true, DTA_ScaleX, scale, DTA_ScaleY, 0.36, TAG_DONE);
GameText(160, 58, GStrings("Multiplayer Totals"), 0, 0);
GameText(160, 58 + 10, currentLevel->DisplayName(), 0, 0);
GameText(160, 165, GStrings("Presskey"), 0, 0);
int t = 0;
MiniText(38, 80, GStrings("Name"), 0);
MiniText(269 + 20, 80, GStrings("Kills"), 0, 1);
for (int i = 0; i < playerswhenstarted; i++)
{
mysnprintf(tempbuf, 32, "%-4d", i + 1);
MiniText(92 + (i * 23), 80, tempbuf, 0);
}
for (int i = 0; i < playerswhenstarted; i++)
{
int xfragtotal = 0;
mysnprintf(tempbuf, 32, "%d", i + 1);
MiniText(30, 90 + t, tempbuf, 0);
MiniText(38, 90 + t, PlayerName(i), 0, -1, ps[i].palookup);
for (int y = 0; y < playerswhenstarted; y++)
{
int frag = ps[i].frags[y];
if (i == y)
{
mysnprintf(tempbuf, 32, "%-4d", ps[y].fraggedself);
MiniText(92 + (y * 23), 90 + t, tempbuf, 0);
xfragtotal -= ps[y].fraggedself;
}
else
{
mysnprintf(tempbuf, 32, "%-4d", frag);
MiniText(92 + (y * 23), 90 + t, tempbuf, 0);
xfragtotal += frag;
}
/*
if (myconnectindex == connecthead)
{
mysnprintf(tempbuf, 32, "stats %ld killed %ld %ld\n", i + 1, y + 1, frag);
sendscore(tempbuf);
}
*/
}
mysnprintf(tempbuf, 32, "%-4d", xfragtotal);
MiniText(101 + (8 * 23), 90 + t, tempbuf, 0);
t += 7;
}
for (int y = 0; y < playerswhenstarted; y++)
{
int yfragtotal = 0;
for (int i = 0; i < playerswhenstarted; i++)
{
if (i == y)
yfragtotal += ps[i].fraggedself;
int frag = ps[i].frags[y];
yfragtotal += frag;
}
mysnprintf(tempbuf, 32, "%-4d", yfragtotal);
MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0);
}
MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0);
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DRRLevelSummaryScreen : public DScreenJob
{
const char* lastmapname;
int gfx_offset;
int displaystate = 0;
int speech = -1;
int exitSoundStart;
enum
{
printTimeText = 1,
printTimeVal = 2,
printKillsText = 4,
printKillsVal = 8,
printSecretsText = 16,
printSecretsVal = 32,
printStatsAll = 63,
exitSound = 64,
exitWait = 128,
};
public:
DRRLevelSummaryScreen(bool dofadeout = true) : DScreenJob(dofadeout? (fadein | fadeout) : fadein)
{
if (currentLevel->flags & MI_USERMAP)
gfx_offset = BONUSPIC01;
else if (!isRRRA())
gfx_offset = BONUSPIC01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13);
else
gfx_offset = LEVELMAP01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13);
lastmapname = currentLevel->DisplayName();
}
void FormatTime(int time, char* tempbuf)
{
mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60);
}
bool OnEvent(event_t* ev) override
{
if (ev->type == EV_KeyDown && !specialKeyEvent(ev))
{
if ((displaystate & printStatsAll) != printStatsAll)
{
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
displaystate = printStatsAll;
}
else if (!(displaystate & exitSound))
{
displaystate |= exitSound;
exitSoundStart = ticks;
S_PlaySound(425, CHAN_AUTO, CHANF_UI);
speech = BONUS_SPEECH1 + (rand() & 3);
S_PlaySound(speech, CHAN_AUTO, CHANF_UI);
}
return true;
}
return false;
}
void Start() override
{
S_PlayBonusMusic();
}
void OnTick() override
{
if ((displaystate & printStatsAll) != printStatsAll)
{
if (ticks == 15 * 3)
{
displaystate |= printTimeText;
}
else if (ticks == 15 * 4)
{
displaystate |= printTimeVal;
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
}
else if (ticks == 15 * 6)
{
displaystate |= printKillsText;
}
else if (ticks == 15 * 7)
{
displaystate |= printKillsVal;
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
}
else if (ticks == 15 * 9)
{
displaystate |= printSecretsText;
}
else if (ticks == 15 * 10)
{
displaystate |= printSecretsVal;
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
}
}
if (displaystate & exitSound)
{
if (ticks >= exitSoundStart + 60)
{
displaystate ^= exitSound | exitWait;
}
}
if (displaystate & exitWait)
{
if (speech <= 0 || !S_CheckSoundPlaying(speech))
state = finished;
}
}
void PrintTime()
{
char tempbuf[32];
BigText(30, 48, GStrings("TXT_YerTime"), -1);
BigText(30, 64, GStrings("TXT_ParTime"), -1);
BigText(30, 80, GStrings("TXT_XTRTIME"), -1);
if (displaystate & printTimeVal)
{
FormatTime(ps[myconnectindex].player_par, tempbuf);
BigText(191, 48, tempbuf, -1);
FormatTime(currentLevel->parTime, tempbuf);
BigText(191, 64, tempbuf, -1);
FormatTime(currentLevel->designerTime, tempbuf);
BigText(191, 80, tempbuf, -1);
}
}
void PrintKills()
{
char tempbuf[32];
BigText(30, 112, GStrings("TXT_VarmintsKilled"), -1);
BigText(30, 128, GStrings("TXT_VarmintsLeft"), -1);
if (displaystate & printKillsVal)
{
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed);
BigText(231, 112, tempbuf, -1);
if (ud.player_skill > 3)
{
mysnprintf(tempbuf, 32, "%s", GStrings("TXT_N_A"));
BigText(231, 128, tempbuf, -1);
}
else
{
if ((ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed) < 0)
mysnprintf(tempbuf, 32, "%-3d", 0);
else mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed);
BigText(231, 128, tempbuf, -1);
}
}
}
void PrintSecrets()
{
char tempbuf[32];
BigText(30, 144, GStrings("TXT_SECFND"), -1);
BigText(30, 160, GStrings("TXT_SECMISS"), -1);
if (displaystate & printSecretsVal)
{
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms);
BigText(231, 144, tempbuf, -1);
if (ps[myconnectindex].secret_rooms > 0)
sprintf(tempbuf, "%-3d", (100 * ps[myconnectindex].secret_rooms / ps[myconnectindex].max_secret_rooms));
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_secret_rooms - ps[myconnectindex].secret_rooms);
BigText(231, 160, tempbuf, -1);
}
}
void Draw(double) override
{
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
if (lastmapname) BigText(80, 16, lastmapname, -1);
BigText(15, 192, GStrings("PRESSKEY"), -1);
if (displaystate & printTimeText)
{
PrintTime();
}
if (displaystate & printKillsText)
{
PrintKills();
}
if (displaystate & printSecretsText)
{
PrintSecrets();
}
}
};
class DRRRAEndOfGame : public DSkippableScreenJob
{
public:
DRRRAEndOfGame() : DSkippableScreenJob(fadein|fadeout)
{
}
void Skipped() override
{
S_StopSound(35);
}
void Start() override
{
S_PlaySound(35, CHAN_AUTO, CHANF_UI);
}
void OnTick() override
{
if (!S_CheckSoundPlaying(-1, 35) && ticks > 15 * GameTicRate) state = finished; // make sure it stays, even if sound is off.
}
void Draw(double) override
{
auto tex = tileGetTexture(ENDGAME + ((ticks >> 2) & 1));
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void dobonus_r(int bonusonly, const CompletionFunc& completion)
{
JobDesc jobs[20];
int job = 0;
FX_StopAllSounds();
Mus_Stop();
if (bonusonly < 0 && !isRRRA() && numplayers < 2 && ud.from_bonus == 0)
{
int vol = volfromlevelnum(currentLevel->levelNumber);
bonussequence_r(vol, jobs, job);
}
if (playerswhenstarted > 1 && ud.coop != 1)
{
jobs[job++] = { Create<DRRMultiplayerBonusScreen>(playerswhenstarted) };
}
else if (bonusonly <= 0 && ud.multimode <= 1)
{
if (isRRRA() && !(currentLevel->flags & MI_USERMAP) && currentLevel->levelNumber < 106) // fixme: The logic here is awful. Shift more control to the map records.
{
jobs[job++] = { Create<DRRLevelSummaryScreen>(true) };
int levnum = clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13);
char fn[20];
mysnprintf(fn, 20, "lvl%d.anm", levnum + 1);
static const int framespeed[] = { 20, 20, 7200 }; // wait for one minute on the final frame so that the video doesn't stop before the user notices.
jobs[job++] = { PlayVideo(fn, nullptr, framespeed) };
if (bonusonly < 0 && currentLevel->levelNumber > 100)
{
jobs[job++] = { Create<DRRRAEndOfGame>() };
}
}
else jobs[job++] = { Create<DRRLevelSummaryScreen>(false) };
}
if (job)
RunScreenJob(jobs, job, completion);
else if (completion) completion(false);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DRRLoadScreen : public DScreenJob
{
MapRecord* rec;
public:
DRRLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
void Draw(double) override
{
DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
int y = isRRRA()? 140 : 90;
BigText(160, y, (rec->flags & MI_USERMAP) ? GStrings("TXT_ENTRUM") : GStrings("TXT_ENTERIN"), 0);
BigText(160, y+24, rec->DisplayName(), 0);
}
};
void loadscreen_r(MapRecord* rec, CompletionFunc func)
{
JobDesc job = { Create<DRRLoadScreen>(rec) };
RunScreenJob(&job, 1, func);
}
void PrintPaused_r()
{
BigText(160, 100, GStrings("Game Paused"), 0);
}
END_DUKE_NS

View file

@ -3697,7 +3697,7 @@ void moveeffectors_d(void) //STATNUM 3
{
static int16_t list1[] = { BLOODPOOL, PUKE, FOOTPRINTS, FOOTPRINTS2, FOOTPRINTS3, FOOTPRINTS4, BULLETHOLE, BLOODSPLAT1, BLOODSPLAT2, BLOODSPLAT3, BLOODSPLAT4, -1 };
static int16_t 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:

View file

@ -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);

View file

@ -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()

View file

@ -105,12 +105,8 @@ void think_r();
void animatesprites_d(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio);
void animatesprites_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;

View file

@ -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)();

View file

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

View file

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

View file

@ -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);

View file

@ -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() &&

View file

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

View file

@ -130,11 +130,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer);
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;
}

View file

@ -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);

View file

@ -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()
{

View file

@ -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;
}

View file

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

View file

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

View file

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

View file

@ -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();

View file

@ -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();

View file

@ -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;
});
}
}

View file

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

View file

@ -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++)
{

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}

View file

@ -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();

View file

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

View file

@ -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();
}

View file

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

View file

@ -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()

View file

@ -66,9 +66,9 @@ uint8_t bIsVersion6 = true;
uint8_t LoadLevel(int nMap)
uint8_t LoadLevel(MapRecord* map)
{
if (nMap == kMap20)
if (map->gameflags & LEVEL_EX_COUNTDOWN)
{
lCountDown = 81000;
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++)

View file

@ -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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2254,7 +2254,7 @@ FUNCOBJECT_GOTO:
}
}
if (currentLevel->levelNumber <= 20 || nStat != kStatExplodeTrigger)
if (!(currentLevel->gameflags & LEVEL_EX_MULTI) || nStat != kStatExplodeTrigger)
{
runlist_SubRunRec(sprite[nSprite].owner);
runlist_SubRunRec(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++)

View file

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

View file

@ -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;
}

View file

@ -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));

View file

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

View file

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

View file

@ -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();
}

View file

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

View file

@ -1,675 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment
This file is part of Shadow Warrior version 1.2
Shadow Warrior is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "screenjob.h"
#include "game.h"
#include "sounds.h"
#include "v_draw.h"
#include "menus.h"
#include "gamecontrol.h"
#include "mapinfo.h"
#include "misc.h"
#include "network.h"
#include "pal.h"
BEGIN_SW_NS
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DSWDRealmsScreen : public DSkippableScreenJob
{
public:
DSWDRealmsScreen() : DSkippableScreenJob(fadein | fadeout) {}
void OnTick() override
{
if (ticks > 5 * GameTicRate) state = finished;
}
void Draw(double) override
{
const auto tex = tileGetTexture(THREED_REALMS_PIC, true);
const int translation = TRANSLATION(Translation_BasePalettes, DREALMSPAL);
twod->ClearScreen();
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void Logo(const CompletionFunc& completion)
{
StopSound();
PlaySong(nullptr, ThemeSongs[0], ThemeTrack[0]);
static const AnimSound logosound[] =
{
{ 1, DIGI_NOMESSWITHWANG },
{ 5, DIGI_INTRO_SLASH },
{ 15, DIGI_INTRO_WHIRL },
{ -1, -1 }
};
static const int logoframetimes[] = { 360, 8, 128 };
if (!userConfig.nologo)
{
JobDesc jobs[3];
int job = 0;
jobs[job++] = { Create<DSWDRealmsScreen>() };
jobs[job++] = { PlayVideo("sw.anm", logosound, logoframetimes, true)};
RunScreenJob(jobs, job, completion, true, true);
}
else completion(false);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
DScreenJob* GetFinishAnim(int num)
{
static const AnimSound serpsound[] =
{
{ 1, DIGI_SERPTAUNTWANG },
{ 16, DIGI_SHAREND_TELEPORT },
{ 35, DIGI_WANGTAUNTSERP1 },
{ 51, DIGI_SHAREND_UGLY1 },
{ 64, DIGI_SHAREND_UGLY2 },
{ -1, -1 }
};
static const int serpzillaframetimes[] = { 16, 16, 140 };
static const AnimSound sumosound[] =
{
{ 2, DIGI_JG41012 },
{ 30, DIGI_HOTHEADSWITCH },
{ 42, DIGI_HOTHEADSWITCH },
{ 59, DIGI_JG41028 },
{ -1, -1 }
};
static const int sumoframetimes[] = { 40, 10, 130 };
static const AnimSound zillasound[] =
{
{ 1, DIGI_ZC1 },
{ 5, DIGI_JG94024 },
{ 14, DIGI_ZC2 },
{ 30, DIGI_ZC3 },
{ 32, DIGI_ZC4 },
{ 37, DIGI_ZC5 },
{ 63, DIGI_Z16043 },
{ 63, DIGI_ZC6 },
{ 63, DIGI_ZC7 },
{ 72, DIGI_ZC7 },
{ 73, DIGI_ZC4 },
{ 77, DIGI_ZC5 },
{ 87, DIGI_ZC8 },
{ 103, DIGI_ZC7 },
{ 108, DIGI_ZC9 },
{ 120, DIGI_JG94039 },
{ -1, -1 }
};
static const char* const ANIMname[] =
{
"swend.anm",
"sumocinm.anm",
"zfcin.anm",
};
switch (num)
{
case ANIM_SERP: return PlayVideo("swend.anm", serpsound, serpzillaframetimes);
case ANIM_SUMO: return PlayVideo("sumocinm.anm", sumosound, sumoframetimes);
case ANIM_ZILLA:return PlayVideo("zfcin.anm", zillasound, serpzillaframetimes);
default: return nullptr;
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DSWCreditsScreen : public DSkippableScreenJob
{
enum
{
CREDITS1_PIC = 5111,
CREDITS2_PIC = 5118
};
int state = 0;
int starttime;
int curpic;
void Skipped() override
{
StopSound();
}
void Start() override
{
// Lo Wang feel like singing!
PlaySound(DIGI_JG95012, v3df_none, CHAN_VOICE, CHANF_UI);
}
void OnTick() override
{
if (state == 0)
{
if (!soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_VOICE))
{
starttime = ticks;
state = 1;
StopSound();
curpic = CREDITS1_PIC;
// try 14 then 2 then quit
if (!PlaySong(nullptr, ThemeSongs[5], ThemeTrack[5], true))
{
PlaySong(nullptr, nullptr, 2, true);
}
}
}
else
{
if (ticks >= starttime + 8 * GameTicRate)
{
curpic = CREDITS1_PIC + CREDITS2_PIC - curpic;
starttime = ticks;
}
}
}
void Draw(double) override
{
twod->ClearScreen();
if (state == 1)
DrawTexture(twod, tileGetTexture(curpic, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
}
};
//---------------------------------------------------------------------------
//
// Summary screen
//
//---------------------------------------------------------------------------
static int BonusPunchSound(short)
{
PlaySound(DIGI_PLAYERYELL3, v3df_none);
return 0;
}
static int BonusKickSound(short)
{
PlaySound(DIGI_PLAYERYELL2, v3df_none);
return 0;
}
static int BonusGrabSound(short)
{
PlaySound(DIGI_BONUS_GRAB, v3df_none);
return 0;
}
enum
{
BONUS_SCREEN_PIC = 5120,
BONUS_ANIM = 5121,
BONUS_ANIM_FRAMES = (5159 - 5121),
BREAK_LIGHT_RATE = 18,
BONUS_PUNCH = 5121,
BONUS_KICK = 5136,
BONUS_GRAB = 5151,
BONUS_REST = 5121,
BONUS_TICS = 8,
BONUS_GRAB_TICS = 20,
BONUS_REST_TICS = 50,
};
static STATE s_BonusPunch[] =
{
{BONUS_PUNCH + 0, BONUS_TICS, NULL, &s_BonusPunch[1]},
{BONUS_PUNCH + 1, BONUS_TICS, NULL, &s_BonusPunch[2]},
{BONUS_PUNCH + 2, BONUS_TICS, NULL, &s_BonusPunch[3]},
{BONUS_PUNCH + 2, 0 | SF_QUICK_CALL, BonusPunchSound, &s_BonusPunch[4]},
{BONUS_PUNCH + 3, BONUS_TICS, NULL, &s_BonusPunch[5]},
{BONUS_PUNCH + 4, BONUS_TICS, NULL, &s_BonusPunch[6]},
{BONUS_PUNCH + 5, BONUS_TICS, NULL, &s_BonusPunch[7]},
{BONUS_PUNCH + 6, BONUS_TICS, NULL, &s_BonusPunch[8]},
{BONUS_PUNCH + 7, BONUS_TICS, NULL, &s_BonusPunch[9]},
{BONUS_PUNCH + 8, BONUS_TICS, NULL, &s_BonusPunch[10]},
{BONUS_PUNCH + 9, BONUS_TICS, NULL, &s_BonusPunch[11]},
{BONUS_PUNCH + 10, BONUS_TICS, NULL, &s_BonusPunch[12]},
{BONUS_PUNCH + 11, BONUS_TICS, NULL, &s_BonusPunch[13]},
{BONUS_PUNCH + 12, BONUS_TICS, NULL, &s_BonusPunch[14]},
{BONUS_PUNCH + 14, 90, NULL, &s_BonusPunch[15]},
{BONUS_PUNCH + 14, BONUS_TICS, NULL, &s_BonusPunch[15]},
};
static STATE s_BonusKick[] =
{
{BONUS_KICK + 0, BONUS_TICS, NULL, &s_BonusKick[1]},
{BONUS_KICK + 1, BONUS_TICS, NULL, &s_BonusKick[2]},
{BONUS_KICK + 2, BONUS_TICS, NULL, &s_BonusKick[3]},
{BONUS_KICK + 2, 0 | SF_QUICK_CALL, BonusKickSound, &s_BonusKick[4]},
{BONUS_KICK + 3, BONUS_TICS, NULL, &s_BonusKick[5]},
{BONUS_KICK + 4, BONUS_TICS, NULL, &s_BonusKick[6]},
{BONUS_KICK + 5, BONUS_TICS, NULL, &s_BonusKick[7]},
{BONUS_KICK + 6, BONUS_TICS, NULL, &s_BonusKick[8]},
{BONUS_KICK + 7, BONUS_TICS, NULL, &s_BonusKick[9]},
{BONUS_KICK + 8, BONUS_TICS, NULL, &s_BonusKick[10]},
{BONUS_KICK + 9, BONUS_TICS, NULL, &s_BonusKick[11]},
{BONUS_KICK + 10, BONUS_TICS, NULL, &s_BonusKick[12]},
{BONUS_KICK + 11, BONUS_TICS, NULL, &s_BonusKick[13]},
{BONUS_KICK + 12, BONUS_TICS, NULL, &s_BonusKick[14]},
{BONUS_KICK + 14, 90, NULL, &s_BonusKick[15]},
{BONUS_KICK + 14, BONUS_TICS, NULL, &s_BonusKick[15]},
};
static STATE s_BonusGrab[] =
{
{BONUS_GRAB + 0, BONUS_GRAB_TICS, NULL, &s_BonusGrab[1]},
{BONUS_GRAB + 1, BONUS_GRAB_TICS, NULL, &s_BonusGrab[2]},
{BONUS_GRAB + 2, BONUS_GRAB_TICS, NULL, &s_BonusGrab[3]},
{BONUS_GRAB + 2, 0 | SF_QUICK_CALL, BonusGrabSound, &s_BonusGrab[4]},
{BONUS_GRAB + 3, BONUS_GRAB_TICS, NULL, &s_BonusGrab[5]},
{BONUS_GRAB + 4, BONUS_GRAB_TICS, NULL, &s_BonusGrab[6]},
{BONUS_GRAB + 5, BONUS_GRAB_TICS, NULL, &s_BonusGrab[7]},
{BONUS_GRAB + 6, BONUS_GRAB_TICS, NULL, &s_BonusGrab[8]},
{BONUS_GRAB + 7, BONUS_GRAB_TICS, NULL, &s_BonusGrab[9]},
{BONUS_GRAB + 8, BONUS_GRAB_TICS, NULL, &s_BonusGrab[10]},
{BONUS_GRAB + 9, 90, NULL, &s_BonusGrab[11]},
{BONUS_GRAB + 9, BONUS_GRAB_TICS, NULL, &s_BonusGrab[11]},
};
static STATE s_BonusRest[] =
{
{BONUS_REST + 0, BONUS_REST_TICS, NULL, &s_BonusRest[1]},
{BONUS_REST + 1, BONUS_REST_TICS, NULL, &s_BonusRest[2]},
{BONUS_REST + 2, BONUS_REST_TICS, NULL, &s_BonusRest[3]},
{BONUS_REST + 1, BONUS_REST_TICS, NULL, &s_BonusRest[0]},
};
static STATE * s_BonusAnim[] =
{
s_BonusPunch,
s_BonusKick,
s_BonusGrab
};
class DSWLevelSummaryScreen : public DScreenJob
{
int minutes, seconds, second_tics;
int nextclock = synctics;
STATE * State = s_BonusRest;
int Tics = 0;
public:
DSWLevelSummaryScreen()
{
second_tics = (PlayClock / 120);
minutes = (second_tics / 60);
seconds = (second_tics % 60);
}
private:
static void gNextState(STATE** State)
{
// Transition to the next state
*State = (*State)->NextState;
if (TEST((*State)->Tics, SF_QUICK_CALL))
{
(*(*State)->Animator)(0);
*State = (*State)->NextState;
}
}
// Generic state control
static void gStateControl(STATE** State, int* tics)
{
*tics += synctics;
// Skip states if too much time has passed
while (*tics >= (*State)->Tics)
{
// Set Tics
*tics -= (*State)->Tics;
gNextState(State);
}
// Call the correct animator
if ((*State)->Animator)
(*(*State)->Animator)(0);
}
bool OnEvent(event_t* ev) override
{
if (ev->type == EV_KeyDown && !specialKeyEvent(ev))
{
if (State >= s_BonusRest && State < &s_BonusRest[countof(s_BonusRest)])
{
State = s_BonusAnim[STD_RANDOM_RANGE(countof(s_BonusAnim))];
Tics = 0;
nextclock = ticks;
}
}
return true;
}
void Start() override
{
PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]);
}
void OnTick() override
{
while (ticks > nextclock)
{
nextclock++;
gStateControl(&State, &Tics);
}
if (State == State->NextState)
{
state = finished;
StopSound();
}
}
void Draw(double) override
{
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(BONUS_SCREEN_PIC, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
MNU_DrawString(160, 20, currentLevel->DisplayName(), 1, 19, 0);
MNU_DrawString(170, 30, GStrings("COMPLETED"), 1, 19, 0);
DrawTexture(twod, tileGetTexture(State->Pic), 158, 86, DTA_FullscreenScale, FSMode_Fit320x200,
DTA_TopLeft, true, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
auto BONUS_LINE = [](int i) { return (50 + ((i) * 20)); };
int line = 0;
FString ds;
ds.Format("%s %2d:%02d", GStrings("TXT_YOURTIME"), minutes, seconds);
MNU_DrawString(60, BONUS_LINE(line++), ds, 1, 16);
if (currentLevel->designerTime > 0)
{
ds.Format("%s %d:%02d", GStrings("TXT_3DRTIME"), currentLevel->designerTime / 60, currentLevel->designerTime % 60);
MNU_DrawString(40, BONUS_LINE(line++), ds, 1, 16);
}
if (currentLevel->parTime > 0)
{
ds.Format("%s %d:%02d", GStrings("TXT_PARTIME"), currentLevel->parTime / 60, currentLevel->parTime % 60);
MNU_DrawString(40, BONUS_LINE(line++), ds, 1, 16);
}
// always read secrets and kills from the first player
ds.Format("%s: %d / %d", GStrings("TXT_SECRETS"), Player->SecretsFound, LevelSecrets);
MNU_DrawString(60, BONUS_LINE(line++), ds, 1, 16);
ds.Format("%s: %d / %d", GStrings("KILLS"), Player->Kills, TotalKillable);
MNU_DrawString(60, BONUS_LINE(line), ds, 1, 16);
MNU_DrawString(160, 185, GStrings("PRESSKEY"), 1, 19, 0);
}
};
//---------------------------------------------------------------------------
//
// Deathmatch summary screen
//
//---------------------------------------------------------------------------
static constexpr int SM_SIZ(int num) { return (num * 4); }
enum
{
STAT_SCREEN_PIC = 5114,
STAT_START_X = 20,
STAT_START_Y = 85,
STAT_OFF_Y = 9,
STAT_HEADER_Y = 14,
STAT_TABLE_X = (STAT_START_X + SM_SIZ(15)),
STAT_TABLE_XOFF = SM_SIZ(6)
};
class DSWMultiSummaryScreen : public DSkippableScreenJob
{
short death_total[MAX_SW_PLAYERS_REG]{};
short kills[MAX_SW_PLAYERS_REG]{};
void Skipped() override
{
StopSound();
}
void Draw(double) override
{
if (clock == 0) PlaySong(nullptr, ThemeSongs[1], ThemeTrack[1]);
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(STAT_SCREEN_PIC, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
MNU_DrawString(160, 68, GStrings("MULTIPLAYER TOTALS"), 0, 0);
MNU_DrawString(160, 189, GStrings("PRESSKEY"), 0, 0, 0);
int x = STAT_START_X;
int y = STAT_START_Y;
// Hm.... how to translate this without messing up the formatting?
MNU_DrawSmallString(x, y, " NAME 1 2 3 4 5 6 7 8 KILLS", 0, 0);
int rows = OrigCommPlayers;
int cols = OrigCommPlayers;
y += STAT_HEADER_Y;
FString ds;
for (int i = 0; i < rows; i++)
{
x = STAT_START_X;
auto pp = Player + i;
ds.Format("%d", i + 1);
MNU_DrawSmallString(x, y, ds, 0, 0);
ds.Format(" %-13s", pp->PlayerName);
MNU_DrawSmallString(x, y, ds, 0, User[pp->PlayerSprite]->spal);
x = STAT_TABLE_X;
for (int j = 0; j < cols; j++)
{
int pal = 0;
death_total[j] += pp->KilledPlayer[j];
if (i == j)
{
// don't add kill for self or team player
pal = PALETTE_PLAYER0 + 4;
kills[i] -= pp->KilledPlayer[j]; // subtract self kills
}
else if (gNet.TeamPlay)
{
if (User[pp->PlayerSprite]->spal == User[Player[j].PlayerSprite]->spal)
{
// don't add kill for self or team player
pal = PALETTE_PLAYER0 + 4;
kills[i] -= pp->KilledPlayer[j]; // subtract self kills
}
else
kills[i] += pp->KilledPlayer[j]; // kills added here
}
else
{
kills[i] += pp->KilledPlayer[j]; // kills added here
}
ds.Format("%d", pp->KilledPlayer[j]);
MNU_DrawSmallString(x, y, ds, 0, pal);
x += STAT_TABLE_XOFF;
}
y += STAT_OFF_Y;
}
// Deaths
x = STAT_START_X;
y += STAT_OFF_Y;
ds.Format(" %s", GStrings("DEATHS"));
MNU_DrawSmallString(x, y, ds, 0, 0);
x = STAT_TABLE_X;
for (int j = 0; j < cols; j++)
{
ds.Format("%d", death_total[j]);
MNU_DrawSmallString(x, y, ds, 0, 0);
x += STAT_TABLE_XOFF;
}
x = STAT_START_X;
y += STAT_OFF_Y;
// Kills
x = STAT_TABLE_X + SM_SIZ(50);
y = STAT_START_Y + STAT_HEADER_Y;
for (int i = 0; i < rows; i++)
{
auto pp = Player + i;
ds.Format("%d", kills[i]); //pp->Kills);
MNU_DrawSmallString(x, y, ds, 0, 0);
y += STAT_OFF_Y;
}
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void StatScreen(int FinishAnim, CompletionFunc completion)
{
JobDesc jobs[5];
int job = 0;
if (FinishAnim)
{
StopSound();
jobs[job++] = { GetFinishAnim(FinishAnim), []() { soundEngine->StopAllChannels(); } };
jobs[job++] = { Create<DSWLevelSummaryScreen>() };
if (FinishAnim == ANIM_ZILLA)
jobs[job++] = { Create<DSWCreditsScreen>() };
}
else if (gNet.MultiGameType != MULTI_GAME_COMMBAT)
{
jobs[job++] = { Create<DSWLevelSummaryScreen>() };
}
else
{
jobs[job++] = { Create<DSWMultiSummaryScreen>() };
}
RunScreenJob(jobs, job, completion, true);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void SybexScreen(CompletionFunc completion)
{
if (!SW_SHAREWARE || CommEnabled) completion(false);
else
{
JobDesc job = { Create<DImageScreen>(tileGetTexture(5261), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff) };
RunScreenJob(&job, 1, completion, true, true);
}
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DSWLoadScreen : public DScreenJob
{
MapRecord* rec;
public:
DSWLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
void Draw(double) override
{
const int TITLE_PIC = 2324;
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(TITLE_PIC), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
MNU_DrawString(160, 170, /*DemoMode ? GStrings("TXT_LBDEMO") :*/ GStrings("TXT_ENTERING"), 1, 16, 0);
MNU_DrawString(160, 180, rec->DisplayName(), 1, 16, 0);
}
};
void loadscreen(MapRecord* rec, CompletionFunc func)
{
JobDesc job = { Create<DSWLoadScreen>(rec) };
RunScreenJob(&job, 1, func);
}
END_SW_NS

View file

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

View file

@ -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;
}

View file

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

View file

@ -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);
}

View file

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

View file

@ -222,30 +222,4 @@ void DoPaletteFlash(PLAYERp pp)
}
bool MNU_ShareWareMessage()
{
const char* extra_text;
short w, h;
if (SW_SHAREWARE)
{
extra_text = "Be sure to call 800-3DREALMS today";
MNU_DrawString(160, 110, extra_text, 1, 16, 0);
extra_text = "and order the game.";
MNU_DrawString(160, 120, extra_text, 1, 16, 0);
extra_text = "You are only playing the first ";
MNU_DrawString(160, 130, extra_text, 1, 16, 0);
extra_text = "four levels, and are missing most";
MNU_DrawString(160, 140, extra_text, 1, 16, 0);
extra_text = "of the game, weapons and monsters.";
MNU_DrawString(160, 150, extra_text, 1, 16, 0);
extra_text = "See the ordering information.";
MNU_DrawString(160, 160, extra_text, 1, 16, 0);
//SET(item->flags, mf_disabled);
}
return true;
}
END_SW_NS

View file

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