mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-26 05:51:30 +00:00
Merge commit '6589222da6e2ac09d90681dbc6f97154cd60ef0b' into whaven
# Conflicts: # source/core/gamecontrol.h # source/core/searchpaths.cpp # wadsrc/static/zscript.txt # wadsrc/static/zscript/razebase.zs
This commit is contained in:
commit
6f19c415d6
166 changed files with 10829 additions and 5566 deletions
|
@ -750,7 +750,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
|
||||
|
@ -836,7 +835,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
|
||||
|
@ -1102,6 +1100,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
|
||||
|
@ -1136,6 +1135,7 @@ set (PCH_SOURCES
|
|||
core/rendering/hw_models.cpp
|
||||
core/rendering/hw_voxels.cpp
|
||||
core/rendering/hw_palmanager.cpp
|
||||
core/rendering/hw_sections.cpp
|
||||
core/rendering/scene/hw_clipper.cpp
|
||||
core/rendering/scene/hw_walls.cpp
|
||||
core/rendering/scene/hw_flats.cpp
|
||||
|
|
|
@ -787,7 +787,7 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, const u
|
|||
mIndices.Reserve(vtcount);
|
||||
for (size_t i = 0; i < vtcount; i++)
|
||||
{
|
||||
mIndices[dg.mIndexIndex + i] = i + dg.mVertIndex;
|
||||
mIndices[dg.mIndexIndex + i] = int(i + dg.mVertIndex);
|
||||
}
|
||||
dg.mIndexCount = (int)vtcount;
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ CVAR(Bool, ui_screenborder_classic_scaling, true, CVAR_ARCHIVE)
|
|||
// nonsense that graphics should not be able to actually use that extra screen space.
|
||||
|
||||
extern bool setsizeneeded;
|
||||
CUSTOM_CVAR(Bool, vid_allowtrueultrawide, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL)
|
||||
CUSTOM_CVAR(Int, vid_allowtrueultrawide, 1, CVAR_ARCHIVE|CVAR_NOINITCALL)
|
||||
{
|
||||
setsizeneeded = true;
|
||||
}
|
||||
|
@ -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(float(x));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void F2DDrawer::GetClipRect(int *x, int *y, int *w, int *h)
|
||||
{
|
||||
if (x) *x = clipleft;
|
||||
|
@ -1263,11 +1279,20 @@ static void VirtualToRealCoords(F2DDrawer *drawer, double Width, double Height,
|
|||
{
|
||||
float myratio = float(handleaspect ? ActiveRatio (Width, Height) : (4.0 / 3.0));
|
||||
|
||||
// if 21:9 AR, map to 16:9 for all callers.
|
||||
// this allows for black bars and stops the stretching of fullscreen images
|
||||
if ((myratio > 1.7f) && !vid_allowtrueultrawide) {
|
||||
myratio = 16.0f / 9.0f;
|
||||
}
|
||||
// if 21:9 AR, map to 16:9 for all callers.
|
||||
// this allows for black bars and stops the stretching of fullscreen images
|
||||
|
||||
switch (vid_allowtrueultrawide)
|
||||
{
|
||||
case 1:
|
||||
default:
|
||||
myratio = MIN(64.0f / 27.0f, myratio);
|
||||
break;
|
||||
case 0:
|
||||
myratio = MIN(16.0f / 9.0f, myratio);
|
||||
case -1:
|
||||
break;
|
||||
}
|
||||
|
||||
double right = x + w;
|
||||
double bottom = y + h;
|
||||
|
|
|
@ -138,6 +138,10 @@ void S_StopCustomStream(SoundStream *stream)
|
|||
|
||||
void S_PauseAllCustomStreams(bool on)
|
||||
{
|
||||
static bool paused = false;
|
||||
|
||||
if (paused == on) return;
|
||||
paused = on;
|
||||
for (auto s : customStreams)
|
||||
{
|
||||
s->SetPaused(on);
|
||||
|
|
|
@ -1107,4 +1107,4 @@ xy(menu_change, "menu/change")
|
|||
xy(menu_advance, "menu/advance")
|
||||
|
||||
xx(zoomsize)
|
||||
|
||||
xx(ScreenJobRunner)
|
||||
|
|
|
@ -61,12 +61,9 @@
|
|||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
// Requires SDL 2.0.6 or newer
|
||||
//#define SDL2_STATIC_LIBRARY
|
||||
|
||||
#if defined SDL2_STATIC_LIBRARY && defined HAVE_VULKAN
|
||||
#if defined HAVE_VULKAN
|
||||
#include <SDL_vulkan.h>
|
||||
#endif // SDL2_STATIC_LIBRARY && HAVE_VULKAN
|
||||
#endif // HAVE_VULKAN
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
|
@ -118,30 +115,7 @@ CCMD(vid_list_sdl_render_drivers)
|
|||
|
||||
namespace Priv
|
||||
{
|
||||
#ifdef SDL2_STATIC_LIBRARY
|
||||
|
||||
#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \
|
||||
RESULT(*NAME)(__VA_ARGS__) = SDL_ ## NAME
|
||||
|
||||
#else // !SDL2_STATIC_LIBRARY
|
||||
|
||||
FModule library("SDL2");
|
||||
|
||||
#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \
|
||||
static TOptProc<library, RESULT(*)(__VA_ARGS__)> NAME("SDL_" #NAME)
|
||||
|
||||
#endif // SDL2_STATIC_LIBRARY
|
||||
|
||||
SDL2_OPTIONAL_FUNCTION(int, GetWindowBordersSize, SDL_Window *window, int *top, int *left, int *bottom, int *right);
|
||||
#ifdef HAVE_VULKAN
|
||||
SDL2_OPTIONAL_FUNCTION(void, Vulkan_GetDrawableSize, SDL_Window *window, int *width, int *height);
|
||||
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_GetInstanceExtensions, SDL_Window *window, unsigned int *count, const char **names);
|
||||
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_CreateSurface, SDL_Window *window, VkInstance instance, VkSurfaceKHR *surface);
|
||||
#endif
|
||||
|
||||
#undef SDL2_OPTIONAL_FUNCTION
|
||||
|
||||
static const uint32_t VulkanWindowFlag = 0x1000'0000;
|
||||
static const uint32_t VulkanWindowFlag = SDL_WINDOW_VULKAN;
|
||||
|
||||
SDL_Window *window;
|
||||
bool vulkanEnabled;
|
||||
|
@ -240,22 +214,21 @@ void I_GetVulkanDrawableSize(int *width, int *height)
|
|||
{
|
||||
assert(Priv::vulkanEnabled);
|
||||
assert(Priv::window != nullptr);
|
||||
assert(Priv::Vulkan_GetDrawableSize);
|
||||
Priv::Vulkan_GetDrawableSize(Priv::window, width, height);
|
||||
SDL_Vulkan_GetDrawableSize(Priv::window, width, height);
|
||||
}
|
||||
|
||||
bool I_GetVulkanPlatformExtensions(unsigned int *count, const char **names)
|
||||
{
|
||||
assert(Priv::vulkanEnabled);
|
||||
assert(Priv::window != nullptr);
|
||||
return Priv::Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE;
|
||||
return SDL_Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE;
|
||||
}
|
||||
|
||||
bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
|
||||
{
|
||||
assert(Priv::vulkanEnabled);
|
||||
assert(Priv::window != nullptr);
|
||||
return Priv::Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE;
|
||||
return SDL_Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -419,20 +392,19 @@ SDLVideo::SDLVideo ()
|
|||
return;
|
||||
}
|
||||
|
||||
#ifndef SDL2_STATIC_LIBRARY
|
||||
// Load optional SDL functions
|
||||
if (!Priv::library.IsLoaded())
|
||||
// Fail gracefully if we somehow reach here after linking against a SDL2 library older than 2.0.6.
|
||||
SDL_version sdlver;
|
||||
SDL_GetVersion(&sdlver);
|
||||
if (!(sdlver.patch >= 6))
|
||||
{
|
||||
Priv::library.Load({ "libSDL2-2.0.so.0", "libSDL2-2.0.so", "libSDL2.so" });
|
||||
I_FatalError("Only SDL 2.0.6 or later is supported.");
|
||||
}
|
||||
#endif // !SDL2_STATIC_LIBRARY
|
||||
|
||||
#ifdef HAVE_SOFTPOLY
|
||||
Priv::softpolyEnabled = vid_preferbackend == 2;
|
||||
#endif
|
||||
#ifdef HAVE_VULKAN
|
||||
Priv::vulkanEnabled = vid_preferbackend == 1
|
||||
&& Priv::Vulkan_GetDrawableSize && Priv::Vulkan_GetInstanceExtensions && Priv::Vulkan_CreateSurface;
|
||||
Priv::vulkanEnabled = vid_preferbackend == 1;
|
||||
|
||||
if (Priv::vulkanEnabled)
|
||||
{
|
||||
|
@ -539,7 +511,7 @@ int SystemBaseFrameBuffer::GetClientWidth()
|
|||
|
||||
#ifdef HAVE_VULKAN
|
||||
assert(Priv::vulkanEnabled);
|
||||
Priv::Vulkan_GetDrawableSize(Priv::window, &width, nullptr);
|
||||
SDL_Vulkan_GetDrawableSize(Priv::window, &width, nullptr);
|
||||
#endif
|
||||
|
||||
return width;
|
||||
|
@ -562,7 +534,7 @@ int SystemBaseFrameBuffer::GetClientHeight()
|
|||
|
||||
#ifdef HAVE_VULKAN
|
||||
assert(Priv::vulkanEnabled);
|
||||
Priv::Vulkan_GetDrawableSize(Priv::window, nullptr, &height);
|
||||
SDL_Vulkan_GetDrawableSize(Priv::window, nullptr, &height);
|
||||
#endif
|
||||
|
||||
return height;
|
||||
|
@ -745,10 +717,10 @@ void ProcessSDLWindowEvent(const SDL_WindowEvent &event)
|
|||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
if (!vid_fullscreen && Priv::GetWindowBordersSize)
|
||||
if (!vid_fullscreen)
|
||||
{
|
||||
int top = 0, left = 0;
|
||||
Priv::GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr);
|
||||
SDL_GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr);
|
||||
win_x = event.data1-left;
|
||||
win_y = event.data2-top;
|
||||
}
|
||||
|
|
|
@ -371,7 +371,6 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer()
|
|||
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
|
||||
SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE);
|
||||
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
I_GetEvent();
|
||||
|
||||
static_cast<Win32BaseVideo *>(Video)->Shutdown();
|
||||
}
|
||||
|
|
|
@ -8706,7 +8706,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
|||
bool writable;
|
||||
ArgList[i] = ArgList[i]->Resolve(ctx); // must be resolved before the address is requested.
|
||||
|
||||
if (ArgList[i]->ValueType->isRealPointer())
|
||||
if (ArgList[i] && ArgList[i]->ValueType->isRealPointer())
|
||||
{
|
||||
auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType;
|
||||
if (pointedType && pointedType->isDynArray())
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "dobject.h"
|
||||
#include "vm.h"
|
||||
#include "types.h"
|
||||
#include "v_draw.h"
|
||||
|
||||
// We need one specific type for each of the 8 integral VM types and instantiate the needed functions for each of them.
|
||||
// Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible.
|
||||
|
@ -412,6 +413,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Push, ArrayPush<FDynArray_I32 COMMA
|
|||
ACTION_RETURN_INT(self->Push(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, PushV)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_VA_POINTER(va_reginfo); // Get the hidden type information array
|
||||
VMVa_List args = { param + 1, 0, numparam - 2, va_reginfo + 1 };
|
||||
while (args.curindex < args.numargs)
|
||||
{
|
||||
if (args.reginfo[args.curindex] == REGT_INT)
|
||||
{
|
||||
self->Push(args.args[args.curindex++].i);
|
||||
}
|
||||
else if (args.reginfo[args.curindex] == REGT_FLOAT)
|
||||
{
|
||||
self->Push(int(args.args[args.curindex++].f));
|
||||
}
|
||||
else ThrowAbortException(X_OTHER, "Invalid parameter in pushv, int expected");
|
||||
}
|
||||
|
||||
|
||||
ACTION_RETURN_INT(self->Size()-1);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Pop, ArrayPop<FDynArray_I32>)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
|
|
|
@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl)
|
|||
ACTION_RETURN_FLOAT(self->ToDouble());
|
||||
}
|
||||
|
||||
static void StringSplit(FString *self, TArray<FString> *tokens, const FString &delimiter, int keepEmpty)
|
||||
static void StringSubst(FString *self, const FString &substr, const FString& replc)
|
||||
{
|
||||
self->Substitute(substr, replc);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Substitute, StringSubst)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FString);
|
||||
PARAM_STRING(substr);
|
||||
PARAM_STRING(replc);
|
||||
StringSubst(self, substr, replc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void StringSplit(FString* self, TArray<FString>* tokens, const FString& delimiter, int keepEmpty)
|
||||
{
|
||||
self->Split(*tokens, delimiter, static_cast<FString::EmptyTokenType>(keepEmpty));
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "s_music.h"
|
||||
#include "i_interface.h"
|
||||
#include "base_sbar.h"
|
||||
#include "image.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
@ -80,10 +81,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, StatusbarToRealCoords, StatusbarTo
|
|||
return MIN(4, numret);
|
||||
}
|
||||
|
||||
void SBar_DrawTexture(DStatusBarCore* self, int texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY)
|
||||
void SBar_DrawTexture(DStatusBarCore* self, int texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY, int style)
|
||||
{
|
||||
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||
self->DrawGraphic(FSetTextureID(texid), x, y, flags, alpha, w, h, scaleX, scaleY);
|
||||
self->DrawGraphic(FSetTextureID(texid), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawTexture, SBar_DrawTexture)
|
||||
|
@ -98,14 +99,15 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawTexture, SBar_DrawTexture)
|
|||
PARAM_FLOAT(h);
|
||||
PARAM_FLOAT(scaleX);
|
||||
PARAM_FLOAT(scaleY);
|
||||
SBar_DrawTexture(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY);
|
||||
PARAM_INT(style);
|
||||
SBar_DrawTexture(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY, style);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SBar_DrawImage(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY)
|
||||
void SBar_DrawImage(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY, int style)
|
||||
{
|
||||
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||
self->DrawGraphic(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY);
|
||||
self->DrawGraphic(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage)
|
||||
|
@ -120,11 +122,12 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage)
|
|||
PARAM_FLOAT(h);
|
||||
PARAM_FLOAT(scaleX);
|
||||
PARAM_FLOAT(scaleY);
|
||||
SBar_DrawImage(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY);
|
||||
PARAM_INT(style);
|
||||
SBar_DrawImage(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY, style);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int translation);
|
||||
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int translation, int style);
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawString, SBar_DrawString)
|
||||
{
|
||||
|
@ -141,7 +144,8 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawString, SBar_DrawString)
|
|||
PARAM_FLOAT(scaleX);
|
||||
PARAM_FLOAT(scaleY);
|
||||
PARAM_INT(pt);
|
||||
SBar_DrawString(self, font, string, x, y, flags, trans, alpha, wrapwidth, linespacing, scaleX, scaleY, pt);
|
||||
PARAM_INT(style);
|
||||
SBar_DrawString(self, font, string, x, y, flags, trans, alpha, wrapwidth, linespacing, scaleX, scaleY, pt, style);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -336,8 +340,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 +480,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 +884,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 +938,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)
|
||||
|
|
|
@ -452,16 +452,16 @@ void DStatusBarCore::StatusbarToRealCoords(double& x, double& y, double& w, doub
|
|||
//
|
||||
//============================================================================
|
||||
|
||||
void DStatusBarCore::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style, double clipwidth)
|
||||
void DStatusBarCore::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style, PalEntry color, int translation, double clipwidth)
|
||||
{
|
||||
if (!texture.isValid())
|
||||
return;
|
||||
|
||||
FGameTexture* tex = TexMan.GetGameTexture(texture, !(flags & DI_DONTANIMATE));
|
||||
DrawGraphic(tex, x, y, flags, Alpha, boxwidth, boxheight, scaleX, scaleY, color, translation, style);
|
||||
DrawGraphic(tex, x, y, flags, Alpha, boxwidth, boxheight, scaleX, scaleY, style, color, translation);
|
||||
}
|
||||
|
||||
void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style, double clipwidth)
|
||||
void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style, PalEntry color, int translation, double clipwidth)
|
||||
{
|
||||
double texwidth = tex->GetDisplayWidth() * scaleX;
|
||||
double texheight = tex->GetDisplayHeight() * scaleY;
|
||||
|
@ -695,7 +695,7 @@ void DStatusBarCore::DrawRotated(FGameTexture* tex, double x, double y, int flag
|
|||
//
|
||||
//============================================================================
|
||||
|
||||
void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt)
|
||||
void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt, int style)
|
||||
{
|
||||
bool monospaced = monospacing != EMonospacing::Off;
|
||||
double dx = 0;
|
||||
|
@ -822,6 +822,7 @@ void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, d
|
|||
DTA_DestHeightF, rh,
|
||||
DTA_Alpha, Alpha,
|
||||
DTA_TranslationIndex, pt,
|
||||
DTA_LegacyRenderStyle, ERenderStyle(style),
|
||||
TAG_DONE);
|
||||
|
||||
dx = monospaced
|
||||
|
@ -833,7 +834,7 @@ void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, d
|
|||
}
|
||||
}
|
||||
|
||||
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt)
|
||||
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt, int style)
|
||||
{
|
||||
if (font == nullptr) ThrowAbortException(X_READ_NIL, nullptr);
|
||||
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
|
||||
|
@ -852,13 +853,13 @@ void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string
|
|||
auto brk = V_BreakLines(font->mFont, int(wrapwidth * scaleX), string, true);
|
||||
for (auto& line : brk)
|
||||
{
|
||||
self->DrawString(font->mFont, line.Text, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt);
|
||||
self->DrawString(font->mFont, line.Text, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt, style);
|
||||
y += (font->mFont->GetHeight() + linespacing) * scaleY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->DrawString(font->mFont, string, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt);
|
||||
self->DrawString(font->mFont, string, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt, style);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -184,11 +184,11 @@ public:
|
|||
virtual void SetScale();
|
||||
void ValidateResolution(int& hres, int& vres) const;
|
||||
void StatusbarToRealCoords(double& x, double& y, double& w, double& h) const;
|
||||
void DrawGraphic(FGameTexture* texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0);
|
||||
void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0);
|
||||
void DrawGraphic(FGameTexture* texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style = STYLE_Translucent, PalEntry color = 0xffffffff, int translation = 0, double clipwidth = -1.0);
|
||||
void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style = STYLE_Translucent, PalEntry color = 0xffffffff, int translation = 0, double clipwidth = -1.0);
|
||||
void DrawRotated(FTextureID texture, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent);
|
||||
void DrawRotated(FGameTexture* tex, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent);
|
||||
void DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt);
|
||||
void DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt, int style);
|
||||
void TransformRect(double& x, double& y, double& w, double& h, int flags = 0);
|
||||
void Fill(PalEntry color, double x, double y, double w, double h, int flags = 0);
|
||||
void SetClipRect(double x, double y, double w, double h, int flags = 0);
|
||||
|
|
|
@ -45,6 +45,7 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
|
|||
#include "v_draw.h"
|
||||
#include "sectorgeometry.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "hw_sections.h"
|
||||
|
||||
CVAR(Bool, am_followplayer, true, CVAR_ARCHIVE)
|
||||
CVAR(Bool, am_rotate, true, CVAR_ARCHIVE)
|
||||
|
@ -590,15 +591,18 @@ void renderDrawMapView(int cposx, int cposy, int czoom, int cang)
|
|||
int picnum = sector[i].floorpicnum;
|
||||
if ((unsigned)picnum >= (unsigned)MAXTILES) continue;
|
||||
|
||||
auto mesh = sectorGeometry.get(i, 0, { 0.f,0.f });
|
||||
vertices.Resize(mesh->vertices.Size());
|
||||
for (unsigned j = 0; j < mesh->vertices.Size(); j++)
|
||||
for (auto ii : sectionspersector[i])
|
||||
{
|
||||
int ox = int(mesh->vertices[j].X * 16.f) - cposx;
|
||||
int oy = int(mesh->vertices[j].Y * -16.f) - cposy;
|
||||
int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
|
||||
int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
|
||||
vertices[j] = { x1 / 4096.f, y1 / 4096.f, mesh->texcoords[j].X, mesh->texcoords[j].Y };
|
||||
auto mesh = sectorGeometry.get(ii, 0, { 0.f,0.f });
|
||||
vertices.Resize(mesh->vertices.Size());
|
||||
for (unsigned j = 0; j < mesh->vertices.Size(); j++)
|
||||
{
|
||||
int ox = int(mesh->vertices[j].X * 16.f) - cposx;
|
||||
int oy = int(mesh->vertices[j].Y * -16.f) - cposy;
|
||||
int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
|
||||
int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
|
||||
vertices[j] = { x1 / 4096.f, y1 / 4096.f, mesh->texcoords[j].X, mesh->texcoords[j].Y };
|
||||
}
|
||||
}
|
||||
|
||||
int translation = TRANSLATION(Translation_Remap + curbasepal, sector[i].floorpal);
|
||||
|
|
|
@ -69,11 +69,11 @@ extern int16_t sintable[2048];
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
inline int32_t bsin(const int ang, const int8_t shift = 0)
|
||||
inline int bsin(const int ang, const int shift = 0)
|
||||
{
|
||||
return shift < 0 ? sintable[ang & 2047] >> abs(shift) : sintable[ang & 2047] << shift;
|
||||
}
|
||||
inline double bsinf(const double ang, const int8_t shift = 0)
|
||||
inline double bsinf(const double ang, const int shift = 0)
|
||||
{
|
||||
return g_sin(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
|
||||
}
|
||||
|
@ -85,11 +85,11 @@ inline double bsinf(const double ang, const int8_t shift = 0)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
inline int32_t bcos(const int ang, const int8_t shift = 0)
|
||||
inline int bcos(const int ang, const int shift = 0)
|
||||
{
|
||||
return shift < 0 ? sintable[(ang + 512) & 2047] >> abs(shift) : sintable[(ang + 512) & 2047] << shift;
|
||||
}
|
||||
inline double bcosf(const double ang, const int8_t shift = 0)
|
||||
inline double bcosf(const double ang, const int shift = 0)
|
||||
{
|
||||
return g_cos(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ void changeMap(int player, uint8_t** stream, bool skip)
|
|||
|
||||
void endScreenJob(int player, uint8_t** stream, bool skip)
|
||||
{
|
||||
if (!skip) EndScreenJob();
|
||||
if (!skip) gameaction = ga_endscreenjob;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -280,6 +280,7 @@ void DeferedStartGame(MapRecord* map, int skill, bool nostopsound)
|
|||
static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, const char *t2)
|
||||
{
|
||||
int numparm = g_gameType & (GAMEFLAG_SW | GAMEFLAG_PSEXHUMED) ? 1 : 2; // Handle games with episodic and non-episodic level order.
|
||||
if (numparm == 2 && argv.argc() == 2) numparm = 1;
|
||||
if (argv.argc() <= numparm)
|
||||
{
|
||||
if (numparm == 2) Printf(PRINT_BOLD, "%s <e> <m>: %s episode 'e' and map 'm'\n", cmdname, t2);
|
||||
|
@ -294,7 +295,7 @@ static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, cons
|
|||
Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n");
|
||||
return nullptr;
|
||||
}
|
||||
auto map = FindMapByLevelNum(numparm == 1 ? m : levelnum(e - 1, m - 1));
|
||||
auto map = FindMapByIndex(e, m);
|
||||
if (!map)
|
||||
{
|
||||
if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]);
|
||||
|
|
1232
source/core/g_mapinfo.cpp
Normal file
1232
source/core/g_mapinfo.cpp
Normal file
File diff suppressed because it is too large
Load diff
108
source/core/g_mapinfo.h
Normal file
108
source/core/g_mapinfo.h
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
** g_level.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef __G_LEVEL_H__
|
||||
#define __G_LEVEL_H__
|
||||
|
||||
#include "autosegs.h"
|
||||
#include "vectors.h"
|
||||
#include "sc_man.h"
|
||||
#include "file_zip.h"
|
||||
|
||||
struct FMapInfoParser
|
||||
{
|
||||
FScanner sc;
|
||||
bool Internal;
|
||||
MapRecord* defaultinfoptr;
|
||||
|
||||
FMapInfoParser(bool internal = false)
|
||||
{
|
||||
Internal = internal;
|
||||
}
|
||||
|
||||
bool CheckLegacyMapDefinition(FString& mapname);
|
||||
bool ParseLookupName(FString &dest);
|
||||
void ParseMusic(FString &name, int &order);
|
||||
void ParseLumpOrTextureName(FString &name);
|
||||
|
||||
void ParseCutscene(CutsceneDef& cdef);
|
||||
void ParseCluster();
|
||||
void ParseMapName(FString &mapname);
|
||||
MapRecord *ParseMapHeader(MapRecord &defaultinfo);
|
||||
void ParseMapDefinition(MapRecord &leveldef);
|
||||
void ParseEpisodeInfo ();
|
||||
void ParseCutsceneInfo();
|
||||
void ParseGameInfo();
|
||||
void ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord &defaultinfo);
|
||||
|
||||
void ParseOpenBrace();
|
||||
bool ParseCloseBrace();
|
||||
bool CheckAssign();
|
||||
void ParseAssign();
|
||||
void MustParseAssign();
|
||||
void ParseComma();
|
||||
bool CheckNumber();
|
||||
bool CheckFloat();
|
||||
void SkipToNext();
|
||||
void CheckEndOfFile(const char *block);
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma section(SECTION_YREG,read)
|
||||
#define MSVC_YSEG __declspec(allocate(SECTION_YREG))
|
||||
#define GCC_YSEG
|
||||
#else
|
||||
#define MSVC_YSEG
|
||||
#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used))
|
||||
#endif
|
||||
|
||||
#define DEFINE_MAP_OPTION(name, old) \
|
||||
static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info); \
|
||||
static FMapOptInfo MapOpt_##name = \
|
||||
{ #name, MapOptHandler_##name, old }; \
|
||||
MSVC_YSEG FMapOptInfo *mapopt_##name GCC_YSEG = &MapOpt_##name; \
|
||||
static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info)
|
||||
|
||||
|
||||
struct FMapOptInfo
|
||||
{
|
||||
const char *name;
|
||||
void (*handler) (FMapInfoParser &parse, MapRecord *levelinfo);
|
||||
bool old;
|
||||
};
|
||||
|
||||
|
||||
void G_ParseMapInfo();
|
||||
|
||||
|
||||
#endif //__G_LEVEL_H__
|
|
@ -73,6 +73,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "automap.h"
|
||||
#include "v_draw.h"
|
||||
#include "gi.h"
|
||||
#include "vm.h"
|
||||
#include "g_mapinfo.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "hw_voxels.h"
|
||||
#include "hw_palmanager.h"
|
||||
|
@ -286,6 +288,11 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr)
|
|||
|
||||
UserConfig userConfig;
|
||||
|
||||
DEFINE_GLOBAL(userConfig)
|
||||
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nomonsters)
|
||||
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nosound)
|
||||
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nologo)
|
||||
|
||||
void UserConfig::ProcessOptions()
|
||||
{
|
||||
// -help etc are omitted
|
||||
|
@ -569,7 +576,7 @@ int GameMain()
|
|||
I_ShowFatalError(err.what());
|
||||
r = -1;
|
||||
}
|
||||
DeleteScreenJob();
|
||||
//DeleteScreenJob();
|
||||
DeinitMenus();
|
||||
if (StatusBar) StatusBar->Destroy();
|
||||
StatusBar = nullptr;
|
||||
|
@ -610,13 +617,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";
|
||||
|
@ -668,13 +679,16 @@ static TArray<GrpEntry> SetupGame()
|
|||
{
|
||||
for (auto& str : game)
|
||||
{
|
||||
int g = 0;
|
||||
for (auto& grp : groups)
|
||||
{
|
||||
if (grp.FileInfo.gameid.CompareNoCase(str) == 0)
|
||||
{
|
||||
userConfig.gamegrp = grp.FileName;
|
||||
groupno = g;
|
||||
goto foundit;
|
||||
}
|
||||
g++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -682,16 +696,18 @@ static TArray<GrpEntry> SetupGame()
|
|||
|
||||
// If the user has specified a file name, let's see if we know it.
|
||||
//
|
||||
if (userConfig.gamegrp.Len())
|
||||
if (groupno == -1 && userConfig.gamegrp.Len())
|
||||
{
|
||||
FString gamegrplower = "/" + userConfig.gamegrp.MakeLower();
|
||||
FString gamegrplower = userConfig.gamegrp.MakeLower();
|
||||
if (gamegrplower[1] != ':' || gamegrplower[2] != '/') gamegrplower.Insert(0, "/");
|
||||
|
||||
int g = 0;
|
||||
for (auto& grp : groups)
|
||||
{
|
||||
auto grplower = grp.FileName.MakeLower();
|
||||
grplower.Substitute("\\", "/");
|
||||
if (grplower.LastIndexOf(gamegrplower) == grplower.Len() - gamegrplower.Len())
|
||||
FixPathSeperator(grplower);
|
||||
int pos = grplower.LastIndexOf(gamegrplower);
|
||||
if (pos >= 0 && pos == grplower.Len() - gamegrplower.Len())
|
||||
{
|
||||
groupno = g;
|
||||
break;
|
||||
|
@ -961,6 +977,7 @@ int RunGame()
|
|||
LoadScripts();
|
||||
StartScreen->Progress();
|
||||
SetDefaultStrings();
|
||||
Job_Init();
|
||||
if (Args->CheckParm("-sounddebug"))
|
||||
C_DoCommand("stat sounddebug");
|
||||
|
||||
|
@ -976,6 +993,7 @@ int RunGame()
|
|||
engineInit();
|
||||
gi->app_init();
|
||||
StartScreen->Progress();
|
||||
G_ParseMapInfo();
|
||||
CreateStatusBar();
|
||||
SetDefaultMenuColors();
|
||||
M_Init();
|
||||
|
@ -1451,13 +1469,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)
|
||||
|
@ -1465,6 +1539,37 @@ DEFINE_GLOBAL(gameaction)
|
|||
DEFINE_GLOBAL(gamestate)
|
||||
DEFINE_GLOBAL(demoplayback)
|
||||
DEFINE_GLOBAL(consoleplayer)
|
||||
DEFINE_GLOBAL(currentLevel)
|
||||
DEFINE_GLOBAL(paused)
|
||||
|
||||
DEFINE_FIELD_X(ClusterDef, ClusterDef, name)
|
||||
DEFINE_FIELD_X(ClusterDef, ClusterDef, InterBackground)
|
||||
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, parTime)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, designerTime)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, fileName)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, labelName)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, name)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, music)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, cdSongId)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, flags)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, levelNumber)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, cluster)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, NextMap)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, NextSecret)
|
||||
//native readonly String messages[MAX_MESSAGES];
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, Author)
|
||||
DEFINE_FIELD_X(MapRecord, MapRecord, InterBackground)
|
||||
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, kills)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxkills)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, secrets)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxsecrets)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, supersecrets)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, playercount)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, time)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, cheated)
|
||||
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, endofgame)
|
||||
|
||||
|
||||
void InitBuildTiles()
|
||||
|
|
|
@ -70,12 +70,12 @@ extern UserConfig userConfig;
|
|||
|
||||
extern int nomusic;
|
||||
extern bool nosound;
|
||||
inline bool MusicEnabled()
|
||||
inline int MusicEnabled() // int return is for scripting
|
||||
{
|
||||
return mus_enabled && !nomusic;
|
||||
}
|
||||
|
||||
inline bool SoundEnabled()
|
||||
inline int SoundEnabled()
|
||||
{
|
||||
return snd_enabled && !nosound;
|
||||
}
|
||||
|
@ -101,6 +101,13 @@ enum
|
|||
GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher.
|
||||
GAMEFLAG_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,
|
||||
GAMEFLAG_WH = 0x00020000,
|
||||
GAMEFLAG_WH2 = 0x00040000,
|
||||
|
|
|
@ -43,8 +43,10 @@ enum gameaction_t : int
|
|||
ga_nextlevel, // Actually start the next level.
|
||||
ga_loadgamehidecon,
|
||||
ga_newgamenostopsound, // start a new game
|
||||
ga_endscreenjob,
|
||||
|
||||
ga_fullconsole,
|
||||
};
|
||||
extern gamestate_t gamestate;
|
||||
extern gameaction_t gameaction;
|
||||
extern int intermissiondelay;
|
||||
|
|
|
@ -79,7 +79,7 @@ struct GameInterface
|
|||
virtual void MenuSound(EMenuSounds snd) {}
|
||||
virtual bool CanSave() { return true; }
|
||||
virtual void CustomMenuSelection(int menu, int item) {}
|
||||
virtual bool StartGame(FNewGameStartup& gs) { return false; }
|
||||
virtual bool StartGame(FNewGameStartup& gs) { return true; }
|
||||
virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; }
|
||||
virtual double SmallFontScale() { return 1; }
|
||||
virtual void SerializeGameState(FSerializer& arc) {}
|
||||
|
|
|
@ -105,6 +105,7 @@ bool r_NoInterpolate;
|
|||
int entertic;
|
||||
int oldentertics;
|
||||
int gametic;
|
||||
int intermissiondelay;
|
||||
|
||||
FString BackupSaveGame;
|
||||
|
||||
|
@ -133,6 +134,20 @@ void G_BuildTiccmd(ticcmd_t* cmd)
|
|||
//==========================================================================
|
||||
bool newGameStarted;
|
||||
|
||||
void NewGame(MapRecord* map, int skill, bool ns = false)
|
||||
{
|
||||
newGameStarted = true;
|
||||
ShowIntermission(nullptr, map, nullptr, [=](bool) {
|
||||
gi->NewGame(map, skill, ns);
|
||||
});
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void GameTicker()
|
||||
{
|
||||
int i;
|
||||
|
@ -159,7 +174,7 @@ static void GameTicker()
|
|||
FX_SetReverb(0);
|
||||
gi->FreeLevelData();
|
||||
gameaction = ga_level;
|
||||
gi->NewGame(g_nextmap, -1);
|
||||
NewGame(g_nextmap, -1);
|
||||
BackupSaveGame = "";
|
||||
}
|
||||
break;
|
||||
|
@ -191,13 +206,12 @@ static void GameTicker()
|
|||
FX_StopAllSounds();
|
||||
case ga_newgamenostopsound:
|
||||
DeleteScreenJob();
|
||||
newGameStarted = true;
|
||||
FX_SetReverb(0);
|
||||
gi->FreeLevelData();
|
||||
C_FlushDisplay();
|
||||
gameaction = ga_level;
|
||||
BackupSaveGame = "";
|
||||
gi->NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
|
||||
NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
|
||||
break;
|
||||
|
||||
case ga_startup:
|
||||
|
@ -209,6 +223,7 @@ static void GameTicker()
|
|||
|
||||
case ga_mainmenu:
|
||||
FX_StopAllSounds();
|
||||
if (isBlood()) Mus_Stop();
|
||||
case ga_mainmenunostopsound:
|
||||
gi->FreeLevelData();
|
||||
gamestate = GS_MENUSCREEN;
|
||||
|
@ -253,6 +268,10 @@ static void GameTicker()
|
|||
gameaction = ga_nothing;
|
||||
break;
|
||||
|
||||
case ga_endscreenjob:
|
||||
EndScreenJob();
|
||||
break;
|
||||
|
||||
// for later
|
||||
// case ga_recordgame, // start a new demo recording (later)
|
||||
// case ga_loadgameplaydemo, // load a savegame and play a demo.
|
||||
|
@ -331,7 +350,16 @@ static void GameTicker()
|
|||
break;
|
||||
case GS_INTERMISSION:
|
||||
case GS_INTRO:
|
||||
ScreenJobTick();
|
||||
if (intermissiondelay > 0)
|
||||
{
|
||||
intermissiondelay--;
|
||||
break;
|
||||
}
|
||||
if (ScreenJobTick())
|
||||
{
|
||||
// synchronize termination with the playsim.
|
||||
Net_WriteByte(DEM_ENDSCREENJOB);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -371,7 +399,7 @@ void Display()
|
|||
case GS_INTRO:
|
||||
case GS_INTERMISSION:
|
||||
// screen jobs are not bound by the game ticker so they need to be ticked in the display loop.
|
||||
ScreenJobDraw();
|
||||
if (intermissiondelay <= 0) ScreenJobDraw();
|
||||
break;
|
||||
|
||||
case GS_LEVEL:
|
||||
|
@ -633,6 +661,16 @@ void MainLoop ()
|
|||
// Clamp the timer to TICRATE until the playloop has been entered.
|
||||
r_NoInterpolate = true;
|
||||
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
auto maprecord = FindMapByName(userConfig.CommandMap);
|
||||
userConfig.CommandMap = "";
|
||||
if (maprecord)
|
||||
{
|
||||
NewGame(maprecord, /*userConfig.skill*/2); // todo: fix the skill.
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "printf.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "md4.h"
|
||||
#include "hw_sections.h"
|
||||
|
||||
static TArray<usermaphack_t> usermaphacks;
|
||||
TArray<int> blockingpairs[MAXWALLS];
|
||||
|
@ -173,6 +174,16 @@ static int32_t LoadMapHack(const char *filename)
|
|||
wall[currentwall].overpicnum = sc.Number;
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("split"))
|
||||
{
|
||||
int start = -1, end = -1;
|
||||
if (sc.CheckNumber()) start = sc.Number;
|
||||
if (sc.CheckNumber()) end = sc.Number;
|
||||
if (end >= 0 && validateSector())
|
||||
{
|
||||
hw_SetSplitSector(currentsector, start, end);
|
||||
}
|
||||
}
|
||||
else if (sc.Compare("clearflags"))
|
||||
{
|
||||
if (currentsector != -1 && validateSector())
|
||||
|
|
|
@ -41,43 +41,102 @@
|
|||
#include "raze_sound.h"
|
||||
|
||||
FString gSkillNames[MAXSKILLS];
|
||||
FString gVolumeNames[MAXVOLUMES];
|
||||
FString gVolumeSubtitles[MAXVOLUMES];
|
||||
int32_t gVolumeFlags[MAXVOLUMES];
|
||||
int gDefaultVolume = 0, gDefaultSkill = 1;
|
||||
|
||||
MapRecord mapList[512];
|
||||
MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
|
||||
GlobalCutscenes globalCutscenes;
|
||||
TArray<ClusterDef> clusters;
|
||||
TArray<VolumeRecord> volumes;
|
||||
TArray<TPointer<MapRecord>> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array.
|
||||
MapRecord *currentLevel; // level that is currently played.
|
||||
MapRecord* lastLevel; // Same here, for the last level.
|
||||
unsigned int numUsedSlots;
|
||||
|
||||
|
||||
CCMD(listmaps)
|
||||
{
|
||||
for (unsigned int i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
int lump = fileSystem.FindFile(mapList[i].fileName);
|
||||
int lump = fileSystem.FindFile(map->fileName);
|
||||
if (lump >= 0)
|
||||
{
|
||||
int rfnum = fileSystem.GetFileContainer(lump);
|
||||
Printf("%s - %s (%s)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName(), fileSystem.GetResourceFileName(rfnum));
|
||||
Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("%s - %s (defined but does not exist)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName());
|
||||
Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCMD(mapinfo)
|
||||
{
|
||||
const char* mapname = nullptr;
|
||||
if (argv.argc() > 1) mapname = argv[1];
|
||||
|
||||
if (!mapname)
|
||||
{
|
||||
for (auto& vol : volumes)
|
||||
{
|
||||
Printf("Volume %d\n\tName = '%s'\n\tstartmap = '%s'\n}\n", vol.index, vol.name.GetChars(), vol.startmap.GetChars());
|
||||
}
|
||||
for (auto& clus : clusters)
|
||||
{
|
||||
if (clus.intro.isdefined() || clus.outro.isdefined())
|
||||
{
|
||||
Printf("Cluster %d\n\tName = '%s'\n", clus.index, clus.name.GetChars());
|
||||
if (clus.intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", clus.intro.function.GetChars());
|
||||
if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", clus.intro.video.GetChars());
|
||||
if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", clus.outro.function.GetChars());
|
||||
if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", clus.outro.video.GetChars());
|
||||
Printf("}\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
if (mapname && map->labelName.CompareNoCase(mapname)) continue;
|
||||
int lump = fileSystem.FindFile(map->fileName);
|
||||
if (lump >= 0)
|
||||
{
|
||||
int rfnum = fileSystem.GetFileContainer(lump);
|
||||
Printf("%s - %s (%s)\n{\n", map->fileName.GetChars(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
|
||||
Printf("\tlevel number = %d\n\tCluster = %d\n\tIndex = %d\n", map->levelNumber, map->cluster, map->mapindex);
|
||||
if (map->Author.IsNotEmpty()) Printf("\tAuthor = '%s'\n", map->Author.GetChars());
|
||||
if (map->NextMap.IsNotEmpty()) Printf("\tNext map = '%s'\n", map->NextMap.GetChars());
|
||||
if (map->NextSecret.IsNotEmpty()) Printf("\tNext secret map = '%s'\n", map->NextSecret.GetChars());
|
||||
if (map->music.IsNotEmpty()) Printf("\tMusic = '%s:%d'", map->music.GetChars(), map->musicorder);
|
||||
if (map->cdSongId > 0) Printf("\tCD track = %d\n", map->cdSongId);
|
||||
if (map->parTime) Printf("\tPar Time = %d\n", map->parTime);
|
||||
if (map->designerTime) Printf("\tPar Time = %d\n", map->designerTime);
|
||||
if (map->intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", map->intro.function.GetChars());
|
||||
if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", map->intro.video.GetChars());
|
||||
if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", map->outro.function.GetChars());
|
||||
if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", map->outro.video.GetChars());
|
||||
Printf("}\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CutsceneDef::GetSound()
|
||||
{
|
||||
int id;
|
||||
if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName);
|
||||
if (id <= 0) id = soundEngine->FindSoundByResID(soundID);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
MapRecord *FindMapByName(const char *nm)
|
||||
{
|
||||
for (unsigned i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
auto &map = mapList[i];
|
||||
if (map.labelName.CompareNoCase(nm) == 0)
|
||||
if (map->labelName.CompareNoCase(nm) == 0)
|
||||
{
|
||||
return ↦
|
||||
return map.Data();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -86,23 +145,78 @@ MapRecord *FindMapByName(const char *nm)
|
|||
|
||||
MapRecord *FindMapByLevelNum(int num)
|
||||
{
|
||||
for (unsigned i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
auto &map = mapList[i];
|
||||
if (map.levelNumber == num)
|
||||
if (map->levelNumber == num)
|
||||
{
|
||||
return ↦
|
||||
return map.Data();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MapRecord *FindNextMap(MapRecord *thismap)
|
||||
VolumeRecord* FindVolume(int index)
|
||||
{
|
||||
if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel);
|
||||
return FindMapByLevelNum(thismap->levelNumber+1);
|
||||
for (auto& vol : volumes)
|
||||
{
|
||||
if (vol.index == index) return &vol;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClusterDef* FindCluster(int index)
|
||||
{
|
||||
for (auto& vol : clusters)
|
||||
{
|
||||
if (vol.index == index) return &vol;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ClusterDef* AllocateCluster()
|
||||
{
|
||||
return &clusters[clusters.Reserve(1)];
|
||||
}
|
||||
|
||||
VolumeRecord* AllocateVolume()
|
||||
{
|
||||
return &volumes[volumes.Reserve(1)];
|
||||
}
|
||||
|
||||
MapRecord* FindMapByIndexOnly(int cluster, int num)
|
||||
{
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
if (map->mapindex == num && map->cluster == cluster) return map.Data();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MapRecord* FindMapByIndex(int cluster, int num)
|
||||
{
|
||||
auto map = FindMapByLevelNum(num);
|
||||
if (!map) map = FindMapByIndexOnly(cluster, num); // modern definitions take precedence.
|
||||
return map;
|
||||
}
|
||||
|
||||
MapRecord* FindNextMap(MapRecord* thismap)
|
||||
{
|
||||
MapRecord* next = nullptr;
|
||||
if (!thismap->NextMap.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
|
||||
if (thismap->NextMap.IsNotEmpty()) next = FindMapByName(thismap->NextMap);
|
||||
if (!next) next = FindMapByLevelNum(thismap->levelNumber + 1);
|
||||
return next;
|
||||
}
|
||||
|
||||
MapRecord* FindNextSecretMap(MapRecord* thismap)
|
||||
{
|
||||
MapRecord* next = nullptr;
|
||||
if (!thismap->NextSecret.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
|
||||
if (thismap->NextSecret.IsNotEmpty()) next = FindMapByName(thismap->NextSecret);
|
||||
return next? next : FindNextMap(thismap);
|
||||
}
|
||||
|
||||
|
||||
bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
|
||||
{
|
||||
static const char* specials[] = { "intro", "briefing", "loading" };
|
||||
|
@ -129,7 +243,7 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
|
|||
if (numMatches != 4 || toupper(b1) != 'E' || toupper(b2) != 'L')
|
||||
return false;
|
||||
|
||||
index = FindMapByLevelNum(levelnum(ep - 1, lev - 1));
|
||||
index = FindMapByIndexOnly(ep, lev);
|
||||
|
||||
}
|
||||
if (index != nullptr)
|
||||
|
@ -142,18 +256,19 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
|
|||
|
||||
MapRecord *AllocateMap()
|
||||
{
|
||||
return &mapList[numUsedSlots++];
|
||||
auto&p = mapList[mapList.Reserve(1)];
|
||||
p.Alloc();
|
||||
return p.Data();
|
||||
}
|
||||
|
||||
|
||||
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic)
|
||||
{
|
||||
for (unsigned i = 0; i < numUsedSlots; i++)
|
||||
for (auto& map : mapList)
|
||||
{
|
||||
auto &map = mapList[i];
|
||||
if (map.fileName.CompareNoCase(boardfilename) == 0)
|
||||
if (map->fileName.CompareNoCase(boardfilename) == 0)
|
||||
{
|
||||
return ↦
|
||||
return map.Data();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,28 +3,58 @@
|
|||
#include "gstrings.h"
|
||||
#include "cmdlib.h"
|
||||
#include "quotemgr.h"
|
||||
#include "palentry.h"
|
||||
#include "vectors.h"
|
||||
#ifdef GetMessage
|
||||
#undef GetMessage // Windows strikes...
|
||||
#endif
|
||||
|
||||
|
||||
enum EMax
|
||||
{
|
||||
MAXSKILLS = 7,
|
||||
MAXVOLUMES = 7,
|
||||
MAXMENUGAMEPLAYENTRIES = 7,
|
||||
};
|
||||
|
||||
enum EVolFlags
|
||||
{
|
||||
EF_HIDEFROMSP = 1,
|
||||
VF_HIDEFROMSP = 1,
|
||||
VF_OPTIONAL = 2,
|
||||
VF_SHAREWARELOCK = 4, // show in shareware but lock access.
|
||||
VF_NOSKILL = 8,
|
||||
};
|
||||
|
||||
enum EMapFlags
|
||||
{
|
||||
LEVEL_NOINTERMISSION = 1,
|
||||
LEVEL_SECRETEXITOVERRIDE = 2, // when given an explicit level number, override with secret exit in the map, mainly for compiling episodes out of single levels.
|
||||
LEVEL_CLEARINVENTORY = 4,
|
||||
LEVEL_CLEARWEAPONS = 8,
|
||||
LEVEL_FORCENOEOG = 16, // RR E1L7 needs this to override its boss's death ending the game.
|
||||
};
|
||||
|
||||
enum EMapGameFlags
|
||||
{
|
||||
LEVEL_RR_HULKSPAWN = 1,
|
||||
LEVEL_RR_CLEARMOONSHINE = 2,
|
||||
|
||||
LEVEL_EX_COUNTDOWN = 4,
|
||||
LEVEL_EX_TRAINING = 8,
|
||||
LEVEL_EX_ALTSOUND = 16,
|
||||
LEVEL_EX_MULTI = 32,
|
||||
|
||||
LEVEL_SW_SPAWNMINES = 64,
|
||||
LEVEL_SW_BOSSMETER_SERPENT = 128,
|
||||
LEVEL_SW_BOSSMETER_SUMO = 256,
|
||||
LEVEL_SW_BOSSMETER_ZILLA = 512,
|
||||
LEVEL_SW_DEATHEXIT_SERPENT = 1024,
|
||||
LEVEL_SW_DEATHEXIT_SUMO = 2048,
|
||||
LEVEL_SW_DEATHEXIT_ZILLA = 4096,
|
||||
|
||||
|
||||
};
|
||||
|
||||
// These get filled in by the map definition parsers of the front ends.
|
||||
extern FString gSkillNames[MAXSKILLS];
|
||||
extern FString gVolumeNames[MAXVOLUMES];
|
||||
extern FString gVolumeSubtitles[MAXVOLUMES];
|
||||
extern int32_t gVolumeFlags[MAXVOLUMES];
|
||||
extern int gDefaultVolume, gDefaultSkill;
|
||||
|
||||
|
||||
|
@ -46,6 +76,58 @@ enum {
|
|||
MAX_MESSAGES = 32
|
||||
};
|
||||
|
||||
class DObject;
|
||||
struct MapRecord;
|
||||
|
||||
struct CutsceneDef
|
||||
{
|
||||
FString video;
|
||||
FString function;
|
||||
FString soundName;
|
||||
int soundID = -1; // ResID not SoundID!
|
||||
int framespersec = 0; // only relevant for ANM.
|
||||
bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game.
|
||||
|
||||
void Create(DObject* runner);
|
||||
bool Create(DObject* runner, MapRecord* map, bool transition);
|
||||
bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); }
|
||||
int GetSound();
|
||||
};
|
||||
|
||||
struct GlobalCutscenes
|
||||
{
|
||||
CutsceneDef Intro;
|
||||
CutsceneDef DefaultMapIntro;
|
||||
CutsceneDef DefaultMapOutro;
|
||||
CutsceneDef DefaultGameover;
|
||||
CutsceneDef SharewareEnd;
|
||||
CutsceneDef LoadingScreen;
|
||||
FString MPSummaryScreen;
|
||||
FString SummaryScreen;
|
||||
};
|
||||
|
||||
struct ClusterDef
|
||||
{
|
||||
FString name; // What gets displayed for this cluster. In Duke this is normally the corresponding volume name but does not have to be.
|
||||
CutsceneDef intro; // plays when entering this cluster
|
||||
CutsceneDef outro; // plays when leaving this cluster
|
||||
CutsceneDef gameover; // when defined, plays when the player dies in this cluster
|
||||
FString InterBackground;
|
||||
int index = -1;
|
||||
int flags = 0; // engine and common flags
|
||||
int gameflags = 0; // game specific flags.
|
||||
};
|
||||
|
||||
struct VolumeRecord // episodes
|
||||
{
|
||||
FString startmap;
|
||||
FString name;
|
||||
FString subtitle;
|
||||
int index = -1;
|
||||
int flags = 0;
|
||||
int shortcut = 0;
|
||||
};
|
||||
|
||||
struct MapRecord
|
||||
{
|
||||
int parTime = 0;
|
||||
|
@ -54,16 +136,39 @@ struct MapRecord
|
|||
FString labelName;
|
||||
FString name;
|
||||
FString music;
|
||||
FString Author;
|
||||
FString NextMap;
|
||||
FString NextSecret;
|
||||
int cdSongId = -1;
|
||||
int musicorder = -1;
|
||||
|
||||
CutsceneDef intro;
|
||||
CutsceneDef outro;
|
||||
int flags = 0;
|
||||
int gameflags = 0;
|
||||
int levelNumber = -1;
|
||||
int mapindex = -1; // index in the episode. This only for finding the next map in the progression when nothing explicit is defined.
|
||||
int cluster = -1;
|
||||
|
||||
PalEntry fadeto = 0;
|
||||
int fogdensity = 0;
|
||||
int skyfog = 0;
|
||||
FString BorderTexture;
|
||||
FString InterBackground;
|
||||
TArray<FString> PrecacheTextures;
|
||||
FVector4 skyrotatevector;
|
||||
|
||||
// The rest is only used by Blood
|
||||
int nextLevel = -1;
|
||||
int nextSecret = -1;
|
||||
FString messages[MAX_MESSAGES];
|
||||
FString author;
|
||||
int8_t fog = -1, weather = -1; // Blood defines these but they aren't used.
|
||||
|
||||
// game specific stuff
|
||||
int rr_startsound = 0;
|
||||
int rr_mamaspawn = 15;
|
||||
int ex_ramses_horiz = 11;
|
||||
int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue!
|
||||
FString ex_ramses_pup;
|
||||
FString ex_ramses_text;
|
||||
|
||||
const char* LabelName() const
|
||||
{
|
||||
|
@ -97,39 +202,65 @@ struct MapRecord
|
|||
{
|
||||
messages[num] = msg;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
struct SummaryInfo
|
||||
{
|
||||
int kills;
|
||||
int maxkills;
|
||||
int secrets;
|
||||
int maxsecrets;
|
||||
int supersecrets;
|
||||
int time;
|
||||
int playercount;
|
||||
bool cheated;
|
||||
bool endofgame;
|
||||
};
|
||||
|
||||
extern MapRecord mapList[512];
|
||||
extern GlobalCutscenes globalCutscenes;
|
||||
extern MapRecord *currentLevel;
|
||||
|
||||
bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false);
|
||||
|
||||
MapRecord *FindMapByName(const char *nm);
|
||||
MapRecord *FindMapByLevelNum(int num);
|
||||
MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable.
|
||||
MapRecord* FindMapByIndex(int clst, int num);
|
||||
MapRecord *FindNextMap(MapRecord *thismap);
|
||||
MapRecord* FindNextSecretMap(MapRecord* thismap);
|
||||
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
|
||||
MapRecord* AllocateMap();
|
||||
|
||||
VolumeRecord* FindVolume(int index);
|
||||
ClusterDef* FindCluster(int index);
|
||||
ClusterDef* AllocateCluster();
|
||||
VolumeRecord* AllocateVolume();
|
||||
void SetLevelNum(MapRecord* info, int num);
|
||||
|
||||
inline VolumeRecord* MustFindVolume(int index)
|
||||
{
|
||||
auto r = FindVolume(index);
|
||||
if (r) return r;
|
||||
r = AllocateVolume();
|
||||
r->index = index;
|
||||
return r;
|
||||
}
|
||||
inline ClusterDef* MustFindCluster(int index)
|
||||
{
|
||||
auto r = FindCluster(index);
|
||||
if (r) return r;
|
||||
r = AllocateCluster();
|
||||
r->index = index;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// These should be the only places converting between level numbers and volume/map pairs
|
||||
constexpr inline int levelnum(int vol, int map)
|
||||
constexpr inline int makelevelnum(int vol, int map)
|
||||
{
|
||||
return vol * 1000 + map;
|
||||
}
|
||||
|
||||
constexpr inline int volfromlevelnum(int num)
|
||||
{
|
||||
return num >= 0 ? num / 1000 : 0;
|
||||
}
|
||||
|
||||
constexpr inline int mapfromlevelnum(int num)
|
||||
{
|
||||
return num >= 0 ? num % 1000 : -1;
|
||||
}
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
RRENDSLOT = 127
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "gamefuncs.h"
|
||||
#include "sectorgeometry.h"
|
||||
#include "render.h"
|
||||
#include "hw_sections.h"
|
||||
|
||||
static void ReadSectorV7(FileReader& fr, sectortype& sect)
|
||||
{
|
||||
|
@ -451,6 +452,9 @@ void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang,
|
|||
md4once(buffer.Data(), buffer.Size(), md4);
|
||||
G_LoadMapHack(filename, md4);
|
||||
setWallSectors();
|
||||
hw_BuildSections();
|
||||
sectorGeometry.SetSize(numsections);
|
||||
|
||||
|
||||
memcpy(wallbackup, wall, sizeof(wallbackup));
|
||||
memcpy(sectorbackup, sector, sizeof(sectorbackup));
|
||||
|
@ -489,5 +493,4 @@ void setWallSectors()
|
|||
wall[sector[i].wallptr + w].sector = i;
|
||||
}
|
||||
}
|
||||
sectorGeometry.SetSize(numsectors);
|
||||
}
|
|
@ -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;
|
||||
|
@ -366,6 +395,7 @@ static DMenuItemBase* CreateCustomListMenuItemText(double x, double y, int heigh
|
|||
// Creates the episode menu
|
||||
//
|
||||
//=============================================================================
|
||||
extern TArray<VolumeRecord> volumes;
|
||||
|
||||
static void BuildEpisodeMenu()
|
||||
{
|
||||
|
@ -386,22 +416,22 @@ static void BuildEpisodeMenu()
|
|||
ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items
|
||||
int y = ld->mYpos;
|
||||
|
||||
for (int i = 0; i < MAXVOLUMES; i++)
|
||||
// Volume definitions should be sorted by intended menu order.
|
||||
for (auto &vol : volumes)
|
||||
{
|
||||
if (gVolumeNames[i].IsNotEmpty() && !(gVolumeFlags[i] & EF_HIDEFROMSP))
|
||||
|
||||
if (vol.name.IsNotEmpty() && !(vol.flags & VF_HIDEFROMSP))
|
||||
{
|
||||
int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && i > 0);
|
||||
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gVolumeNames[i][0],
|
||||
gVolumeNames[i], ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, i); // font colors are not used, so hijack one for the shareware flag.
|
||||
int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && (vol.flags & VF_SHAREWARELOCK));
|
||||
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, vol.name[0],
|
||||
vol.name, ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, vol.index); // font colors are not used, so hijack one for the shareware flag.
|
||||
|
||||
y += ld->mLinespacing;
|
||||
ld->mItems.Push(it);
|
||||
addedVolumes++;
|
||||
if (gVolumeSubtitles[i].IsNotEmpty())
|
||||
if (vol.subtitle.IsNotEmpty())
|
||||
{
|
||||
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing * 6 / 10, 1,
|
||||
gVolumeSubtitles[i], SmallFont, CR_GRAY, false, NAME_None, i);
|
||||
vol.subtitle, SmallFont, CR_GRAY, false, NAME_None, vol.index);
|
||||
y += ld->mLinespacing * 6 / 10;
|
||||
ld->mItems.Push(it);
|
||||
textadded = true;
|
||||
|
|
|
@ -49,15 +49,25 @@
|
|||
#include <vpx/vpx_decoder.h>
|
||||
#include <vpx/vp8dx.h>
|
||||
#include "raze_music.h"
|
||||
#include "vm.h"
|
||||
|
||||
|
||||
class MoviePlayer
|
||||
{
|
||||
protected:
|
||||
enum EMovieFlags
|
||||
{
|
||||
NOSOUNDCUTOFF = 1,
|
||||
FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros.
|
||||
};
|
||||
|
||||
int flags;
|
||||
public:
|
||||
virtual void Start() {}
|
||||
virtual bool Frame(uint64_t clock) = 0;
|
||||
virtual void Stop() {}
|
||||
virtual ~MoviePlayer() = default;
|
||||
virtual FTextureID GetTexture() = 0;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -76,16 +86,17 @@ class AnmPlayer : public MoviePlayer
|
|||
int frametime = 0;
|
||||
int nextframetime = 0;
|
||||
AnimTextures animtex;
|
||||
const AnimSound* animSnd;
|
||||
const int* frameTicks;
|
||||
bool nostopsound;
|
||||
const TArray<int> animSnd;
|
||||
int frameTicks[3];
|
||||
|
||||
public:
|
||||
bool isvalid() { return numframes > 0; }
|
||||
|
||||
AnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff)
|
||||
: animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff)
|
||||
AnmPlayer(FileReader& fr, TArray<int>& ans, const int *frameticks, int flags_)
|
||||
: animSnd(std::move(ans))
|
||||
{
|
||||
memcpy(frameTicks, frameticks, 3 * sizeof(int));
|
||||
flags = flags_;
|
||||
buffer = fr.ReadPadded(1);
|
||||
fr.Close();
|
||||
|
||||
|
@ -109,17 +120,12 @@ public:
|
|||
|
||||
if (currentclock < nextframetime - 1)
|
||||
{
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe));
|
||||
frametime = currentclock;
|
||||
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
|
||||
|
||||
int delay = 20;
|
||||
if (frameTicks)
|
||||
{
|
||||
|
@ -129,11 +135,12 @@ public:
|
|||
}
|
||||
nextframetime += delay;
|
||||
|
||||
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
for (unsigned i = 0; i < animSnd.Size(); i+=2)
|
||||
{
|
||||
if (animSnd[i].framenum == curframe)
|
||||
if (animSnd[i] == curframe)
|
||||
{
|
||||
int sound = animSnd[i].soundnum;
|
||||
int sound = animSnd[i+1];
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
|
@ -147,6 +154,7 @@ public:
|
|||
|
||||
void Stop() override
|
||||
{
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
|
@ -156,6 +164,11 @@ public:
|
|||
buffer.Reset();
|
||||
animtex.Clean();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return animtex.GetFrameID();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -187,8 +200,6 @@ public:
|
|||
{
|
||||
if (failed) return false;
|
||||
bool playon = decoder.RunFrame(clock);
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
return playon;
|
||||
}
|
||||
|
||||
|
@ -196,6 +207,11 @@ public:
|
|||
{
|
||||
decoder.Close();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return decoder.animTex().GetFrameID();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -209,7 +225,7 @@ class VpxPlayer : public MoviePlayer
|
|||
bool failed = false;
|
||||
FileReader fr;
|
||||
AnimTextures animtex;
|
||||
const AnimSound* animSnd;
|
||||
const TArray<int> animSnd;
|
||||
|
||||
unsigned width, height;
|
||||
TArray<uint8_t> Pic;
|
||||
|
@ -234,10 +250,10 @@ public:
|
|||
public:
|
||||
bool isvalid() { return !failed; }
|
||||
|
||||
VpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay, FString& error)
|
||||
VpxPlayer(FileReader& fr_, TArray<int>& animSnd_, int flags_, int origframedelay, FString& error) : animSnd(std::move(animSnd_))
|
||||
{
|
||||
fr = std::move(fr_);
|
||||
animSnd = animSnd_;
|
||||
flags = flags_;
|
||||
|
||||
if (!ReadIVFHeader(origframedelay))
|
||||
{
|
||||
|
@ -433,30 +449,35 @@ public:
|
|||
framenum++;
|
||||
if (framenum >= numframes) stop = true;
|
||||
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum;
|
||||
if (soundframe > lastsoundframe)
|
||||
{
|
||||
if (animSnd && soundtrack == -1) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||
if (soundtrack == -1)
|
||||
{
|
||||
if (animSnd[i].framenum == soundframe)
|
||||
for (unsigned i = 0; i < animSnd.Size(); i += 2)
|
||||
{
|
||||
int sound = animSnd[i].soundnum;
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
if (animSnd[i] == soundframe)
|
||||
{
|
||||
int sound = animSnd[i + 1];
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
lastsoundframe = soundframe;
|
||||
}
|
||||
}
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit, TAG_DONE);
|
||||
return !stop;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
Mus_Stop();
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
~VpxPlayer()
|
||||
|
@ -464,6 +485,11 @@ public:
|
|||
vpx_codec_destroy(&codec);
|
||||
animtex.Clean();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return animtex.GetFrameID();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -498,9 +524,10 @@ class SmkPlayer : public MoviePlayer
|
|||
bool fullscreenScale;
|
||||
uint64_t nFrameNs;
|
||||
int nFrame = 0;
|
||||
const AnimSound* animSnd;
|
||||
const TArray<int> animSnd;
|
||||
FString filename;
|
||||
SoundStream* stream = nullptr;
|
||||
bool hassound = false;
|
||||
|
||||
public:
|
||||
bool isvalid() { return hSMK.isValid; }
|
||||
|
@ -534,22 +561,21 @@ public:
|
|||
}
|
||||
|
||||
|
||||
SmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport)
|
||||
SmkPlayer(const char *fn, TArray<int>& ans, int flags_) : animSnd(std::move(ans))
|
||||
{
|
||||
hSMK = Smacker_Open(fn);
|
||||
if (!hSMK.isValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
flags = flags_;
|
||||
Smacker_GetFrameSize(hSMK, nWidth, nHeight);
|
||||
pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight));
|
||||
nFrameRate = Smacker_GetFrameRate(hSMK);
|
||||
nFrameNs = 1'000'000'000 / nFrameRate;
|
||||
nFrames = Smacker_GetNumFrames(hSMK);
|
||||
Smacker_GetPalette(hSMK, palette);
|
||||
fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480);
|
||||
|
||||
bool hassound = false;
|
||||
numAudioTracks = Smacker_GetNumAudioTracks(hSMK);
|
||||
if (numAudioTracks)
|
||||
{
|
||||
|
@ -562,14 +588,12 @@ public:
|
|||
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
|
||||
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
|
||||
else copy16bitSamples(read);
|
||||
animSnd = nullptr;
|
||||
hassound = true;
|
||||
}
|
||||
}
|
||||
if (!hassound)
|
||||
{
|
||||
adata.inf = {};
|
||||
animSnd = ans;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -612,27 +636,21 @@ public:
|
|||
}
|
||||
|
||||
}
|
||||
if (fullscreenScale)
|
||||
{
|
||||
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawTexture(twod, animtex.GetFrame(), 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, TAG_DONE);
|
||||
}
|
||||
|
||||
if (frame > nFrame)
|
||||
{
|
||||
nFrame++;
|
||||
Smacker_GetNextFrame(hSMK);
|
||||
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!hassound) for (unsigned i = 0; i < animSnd.Size(); i += 2)
|
||||
{
|
||||
if (animSnd[i].framenum == nFrame)
|
||||
if (animSnd[i] == nFrame)
|
||||
{
|
||||
int sound = animSnd[i].soundnum;
|
||||
int sound = animSnd[i + 1];
|
||||
if (sound == -1)
|
||||
soundEngine->StopAllChannels();
|
||||
else if (SoundEnabled())
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -640,13 +658,24 @@ public:
|
|||
return nFrame < nFrames;
|
||||
}
|
||||
|
||||
void Stop() override
|
||||
{
|
||||
if (stream) S_StopCustomStream(stream);
|
||||
bool nostopsound = (flags & NOSOUNDCUTOFF);
|
||||
if (!nostopsound && !hassound) soundEngine->StopAllChannels();
|
||||
}
|
||||
|
||||
~SmkPlayer()
|
||||
{
|
||||
Smacker_Close(hSMK);
|
||||
if (stream) S_StopCustomStream(stream);
|
||||
soundEngine->StopAllChannels();
|
||||
animtex.Clean();
|
||||
}
|
||||
|
||||
FTextureID GetTexture() override
|
||||
{
|
||||
return animtex.GetFrameID();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -655,61 +684,11 @@ public:
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DMoviePlayer : public DSkippableScreenJob
|
||||
{
|
||||
MoviePlayer* player;
|
||||
bool started = false;
|
||||
|
||||
public:
|
||||
DMoviePlayer(MoviePlayer* mp)
|
||||
{
|
||||
player = mp;
|
||||
pausable = false;
|
||||
}
|
||||
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
if (!player)
|
||||
{
|
||||
state = stopped;
|
||||
return;
|
||||
}
|
||||
if (!started)
|
||||
{
|
||||
started = true;
|
||||
player->Start();
|
||||
}
|
||||
uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate;
|
||||
if (state == running && !player->Frame(clock))
|
||||
{
|
||||
state = finished;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy() override
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
player->Stop();
|
||||
delete player;
|
||||
}
|
||||
player = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff, FString& error)
|
||||
MoviePlayer* OpenMovie(const char* filename, TArray<int>& ans, const int* frameticks, int flags, FString& error)
|
||||
{
|
||||
FileReader fr;
|
||||
// first try as .ivf - but only if sounds are provided - the decoder is video only.
|
||||
if (ans)
|
||||
if (ans.Size())
|
||||
{
|
||||
auto fn = StripExtension(filename);
|
||||
DefaultExtension(fn, ".ivf");
|
||||
|
@ -739,7 +718,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
|
||||
if (!memcmp(id, "LPF ", 4))
|
||||
{
|
||||
auto anm = new AnmPlayer(fr, ans, frameticks, nosoundcutoff);
|
||||
auto anm = new AnmPlayer(fr, ans, frameticks, flags);
|
||||
if (!anm->isvalid())
|
||||
{
|
||||
error.Format("%s: invalid ANM file.\n", filename);
|
||||
|
@ -751,7 +730,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
else if (!memcmp(id, "SMK2", 4))
|
||||
{
|
||||
fr.Close();
|
||||
auto anm = new SmkPlayer(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently.
|
||||
auto anm = new SmkPlayer(filename, ans, flags);
|
||||
if (!anm->isvalid())
|
||||
{
|
||||
error.Format("%s: invalid SMK file.\n", filename);
|
||||
|
@ -772,7 +751,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
}
|
||||
else if (!memcmp(id, "DKIF\0\0 \0VP80", 12))
|
||||
{
|
||||
auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, error);
|
||||
auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, flags, error);
|
||||
if (!anm->isvalid())
|
||||
{
|
||||
delete anm;
|
||||
|
@ -795,20 +774,53 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff)
|
||||
DEFINE_ACTION_FUNCTION(_MoviePlayer, Create)
|
||||
{
|
||||
if (!filename)
|
||||
{
|
||||
return Create<DBlackScreen>(1);
|
||||
}
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(filename);
|
||||
PARAM_POINTER(sndinf, TArray<int>);
|
||||
PARAM_INT(flags);
|
||||
PARAM_INT(frametime);
|
||||
PARAM_INT(firstframetime);
|
||||
PARAM_INT(lastframetime);
|
||||
|
||||
FString error;
|
||||
auto movie = OpenMovie(filename, ans, frameticks, nosoundcutoff, error);
|
||||
if (firstframetime == -1) firstframetime = frametime;
|
||||
if (lastframetime == -1) lastframetime = frametime;
|
||||
int frametimes[] = { firstframetime, frametime, lastframetime };
|
||||
auto movie = OpenMovie(filename, *sndinf, frametime == -1? nullptr : frametimes, flags, error);
|
||||
if (!movie)
|
||||
{
|
||||
Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars());
|
||||
return Create<DBlackScreen>(1);
|
||||
}
|
||||
return Create<DMoviePlayer>(movie);
|
||||
ACTION_RETURN_POINTER(movie);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_MoviePlayer, Start)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
|
||||
self->Start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_MoviePlayer, Frame)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
|
||||
PARAM_FLOAT(clock);
|
||||
ACTION_RETURN_INT(self->Frame(int64_t(clock)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_MoviePlayer, Destroy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
|
||||
self->Stop();
|
||||
delete self;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_MoviePlayer, GetTexture)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
|
||||
ACTION_RETURN_INT(self->GetTexture().GetIndex());
|
||||
}
|
||||
|
|
|
@ -39,8 +39,10 @@
|
|||
#include "hw_material.h"
|
||||
#include "gamestruct.h"
|
||||
#include "gamecontrol.h"
|
||||
#include "texturemanager.h"
|
||||
#include "hw_models.h"
|
||||
#include "hw_voxels.h"
|
||||
#include "mapinfo.h"
|
||||
|
||||
BEGIN_BLD_NS
|
||||
extern short voxelIndex[MAXTILES];
|
||||
|
@ -129,6 +131,19 @@ void precacheMarkedTiles()
|
|||
int dapalnum = pair->Key >> 32;
|
||||
doprecache(dapicnum, dapalnum);
|
||||
}
|
||||
|
||||
// Cache everything the map explicitly declares.
|
||||
TMap<FString, bool> cachetexmap;
|
||||
for (auto& tex : currentLevel->PrecacheTextures) cachetexmap.Insert(tex, true);
|
||||
|
||||
decltype(cachetexmap)::Iterator it2(cachetexmap);
|
||||
decltype(cachetexmap)::Pair* pair2;
|
||||
while (it2.NextPair(pair2))
|
||||
{
|
||||
auto tex = TexMan.FindGameTexture(pair2->Key, ETextureType::Any);
|
||||
if (tex) PrecacheTex(tex, 0);
|
||||
}
|
||||
|
||||
cachemap.Clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
|
||||
void PrecacheHardwareTextures(int nTile);
|
||||
void markTileForPrecache(int tilenum, int palnum);
|
||||
void markTextureForPrecache(const char* texname);
|
||||
void markVoxelForPrecache(int voxnum);
|
||||
void precacheMarkedTiles();
|
||||
|
|
221
source/core/rendering/hw_sections.cpp
Normal file
221
source/core/rendering/hw_sections.cpp
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
** hw_sectiona.cpp
|
||||
** For decoupling the renderer from internal Build structures
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2021 Christoph Oelckers
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
** The sole reason for existence of this file is that Build's sector setup
|
||||
** does not allow for easy splitting of sectors, either for having disjoint parts
|
||||
** or requiring partial rendering. So we need to add a superstructure
|
||||
** where we can shuffle around some content without disturbing the original
|
||||
** order...
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#include "hw_sections.h"
|
||||
|
||||
|
||||
SectionLine sectionLines[MAXWALLS + (MAXWALLS >> 2)];
|
||||
Section sections[MAXSECTORS + (MAXSECTORS >> 2)];
|
||||
TArray<int> sectionspersector[MAXSECTORS]; // reverse map, mainly for the automap
|
||||
int numsections;
|
||||
int numsectionlines;
|
||||
|
||||
void hw_SplitSector(int sector, int startpos, int endpos);
|
||||
|
||||
TArray<int> splits;
|
||||
|
||||
|
||||
void hw_BuildSections()
|
||||
{
|
||||
// Initial setup just creates a 1:1 mapping of walls to section lines and sectors to sections.
|
||||
numsectionlines = numwalls;
|
||||
numsections = numsectors;
|
||||
for (int i = 0; i < numwalls; i++)
|
||||
{
|
||||
sectionLines[i].startpoint = sectionLines[i].wall = i;
|
||||
sectionLines[i].endpoint = wall[i].point2;
|
||||
sectionLines[i].partner = wall[i].nextwall;
|
||||
sectionLines[i].section = wall[i].sector;
|
||||
sectionLines[i].partnersection = wall[i].nextsector;
|
||||
sectionLines[i].point2index = wall[i].point2 - sector[wall[i].sector].wallptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numsectors; i++)
|
||||
{
|
||||
sections[i].sector = i;
|
||||
sections[i].lines.Resize(sector[i].wallnum);
|
||||
for (int j = 0; j < sector[i].wallnum; j++) sections[i].lines[j] = sector[i].wallptr + j;
|
||||
sectionspersector[i].Resize(1);
|
||||
sectionspersector[i][0] = i;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < splits.Size(); i += 3)
|
||||
hw_SplitSector(splits[i], splits[i + 1], splits[i + 2]);
|
||||
}
|
||||
|
||||
|
||||
static void SplitSection(int section, int start, int end)
|
||||
{
|
||||
// note: to do this, the sector's lines must be well ordered and there must only be one outline and no holes.
|
||||
// This also can only apply a single split to a given sector.
|
||||
int firstsection = numsections++;
|
||||
int secondsection = numsections++;
|
||||
|
||||
auto& sect = sections[section];
|
||||
Section* sect1 = §ions[firstsection];
|
||||
Section* sect2 = §ions[secondsection];
|
||||
sect1->sector = sect.sector;
|
||||
sect2->sector = sect.sector;
|
||||
sect1->lines.Clear();
|
||||
sect2->lines.Clear();
|
||||
for (int aline : sect.lines)
|
||||
{
|
||||
int line = sectionLines[aline].wall;
|
||||
if (line < start || line >= end)
|
||||
{
|
||||
sect1->lines.Push(aline);
|
||||
}
|
||||
if (line == start)
|
||||
{
|
||||
sect1->lines.Push(-1);
|
||||
sect2->lines.Push(-1);
|
||||
}
|
||||
if (line >= start && line < end)
|
||||
{
|
||||
sect2->lines.Push(aline);
|
||||
}
|
||||
}
|
||||
|
||||
int firstnewline = numsectionlines;
|
||||
int thisline = numsectionlines;
|
||||
int splitline1, splitline2;
|
||||
//numsectionlines += sect1->lines.Size() + 1;
|
||||
for (unsigned i = 0; i < sect1->lines.Size(); i++)// auto& sline : sect1->lines)
|
||||
{
|
||||
int sline = sect1->lines[i];
|
||||
sect1->lines[i] = thisline;
|
||||
if (sline != -1)
|
||||
{
|
||||
SectionLine& newline = sectionLines[thisline];
|
||||
newline = sectionLines[sline];
|
||||
newline.section = int16_t(sect1 - sections);
|
||||
if (i != sect1->lines.Size() - 1) newline.point2index = thisline + 1 - firstnewline;
|
||||
else newline.point2index = 0;
|
||||
assert(newline.point2index >= 0);
|
||||
|
||||
// relink the partner
|
||||
auto& partnerline = sectionLines[newline.partner];
|
||||
partnerline.partner = thisline;
|
||||
partnerline.partnersection = newline.section;
|
||||
thisline++;
|
||||
}
|
||||
else
|
||||
{
|
||||
splitline1 = thisline++;
|
||||
sectionLines[splitline1].wall = -1;
|
||||
sectionLines[splitline1].section = int16_t(sect1 - sections);
|
||||
sectionLines[splitline1].partnersection = int16_t(sect2 - sections);
|
||||
sectionLines[splitline1].startpoint = start;
|
||||
sectionLines[splitline1].endpoint = end;
|
||||
sectionLines[splitline1].point2index = splitline1 + 1 - firstnewline;
|
||||
}
|
||||
}
|
||||
|
||||
firstnewline = thisline;
|
||||
for (unsigned i = 0; i < sect2->lines.Size(); i++)// auto& sline : sect1->lines)
|
||||
{
|
||||
int sline = sect2->lines[i];
|
||||
sect2->lines[i] = thisline;
|
||||
if (sline != -1)
|
||||
{
|
||||
SectionLine& newline = sectionLines[thisline];
|
||||
newline = sectionLines[sline];
|
||||
newline.section = int16_t(sect2 - sections);
|
||||
if (i != sect2->lines.Size() - 1) newline.point2index = thisline + 1 - firstnewline;
|
||||
else newline.point2index = 0;
|
||||
assert(newline.point2index >= 0);
|
||||
|
||||
// relink the partner
|
||||
auto& partnerline = sectionLines[newline.partner];
|
||||
partnerline.partner = thisline;
|
||||
partnerline.partnersection = newline.section;
|
||||
thisline++;
|
||||
}
|
||||
else
|
||||
{
|
||||
splitline2 = thisline++;
|
||||
sectionLines[splitline2].wall = -1;
|
||||
sectionLines[splitline2].section = int16_t(sect2 - sections);
|
||||
sectionLines[splitline2].partnersection = int16_t(sect1 - sections);
|
||||
sectionLines[splitline2].startpoint = end;
|
||||
sectionLines[splitline2].endpoint = start;
|
||||
sectionLines[splitline2].point2index = splitline2 + 1 - firstnewline;
|
||||
}
|
||||
}
|
||||
sectionLines[splitline1].partner = splitline2;
|
||||
sectionLines[splitline2].partner = splitline1;
|
||||
|
||||
sectionspersector[sect.sector].Resize(2);
|
||||
sectionspersector[sect.sector][0] = int16_t(sect1 - sections);
|
||||
sectionspersector[sect.sector][1] = int16_t(sect2 - sections);
|
||||
}
|
||||
|
||||
void hw_SplitSector(int sectnum, int start, int end)
|
||||
{
|
||||
int wallstart = sector[sectnum].wallptr;
|
||||
int wallend = wallstart + sector[sectnum].wallnum;
|
||||
if (start < wallstart || start >= wallend || end < wallstart || end >= wallend || end < start) return;
|
||||
|
||||
for (unsigned i = 0; i < sectionspersector[sectnum].Size(); i++)
|
||||
{
|
||||
int sect = sectionspersector[sectnum][i];
|
||||
bool foundstart = false, foundend = false;
|
||||
for (int aline : sections[sect].lines)
|
||||
{
|
||||
int line = sectionLines[aline].wall;
|
||||
if (line == start) foundstart = true;
|
||||
if (line == end) foundend = true;
|
||||
}
|
||||
if (foundstart && foundend)
|
||||
{
|
||||
sectionspersector->Delete(i);
|
||||
SplitSection(sect, start, end);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hw_SetSplitSector(int sectnum, int start, int end)
|
||||
{
|
||||
splits.Push(sectnum);
|
||||
splits.Push(start);
|
||||
splits.Push(end);
|
||||
}
|
32
source/core/rendering/hw_sections.h
Normal file
32
source/core/rendering/hw_sections.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "build.h"
|
||||
|
||||
struct SectionLine
|
||||
{
|
||||
int16_t section;
|
||||
int16_t partnersection;
|
||||
int16_t startpoint;
|
||||
int16_t endpoint;
|
||||
int16_t wall;
|
||||
int16_t partner;
|
||||
int16_t point2index;
|
||||
};
|
||||
|
||||
struct Section
|
||||
{
|
||||
int sector;
|
||||
// this is the whole point of sections - instead of just having a start index and count, we have an explicit list of lines that's a lot easier to change when needed.
|
||||
TArray<int16_t> lines;
|
||||
};
|
||||
|
||||
// giving 25% more may be a bit high as normally this should be small numbers only.
|
||||
extern SectionLine sectionLines[MAXWALLS + (MAXWALLS >> 2)];
|
||||
extern Section sections[MAXSECTORS + (MAXSECTORS >> 2)];
|
||||
extern TArray<int> sectionspersector[MAXSECTORS]; // reverse map, mainly for the automap
|
||||
extern int numsections;
|
||||
extern int numsectionlines;
|
||||
|
||||
|
||||
void hw_BuildSections();
|
||||
void hw_SetSplitSector(int sector, int startpos, int endpos);
|
|
@ -43,6 +43,7 @@
|
|||
#include "hw_voxels.h"
|
||||
#include "mapinfo.h"
|
||||
#include "gamecontrol.h"
|
||||
#include "hw_sections.h"
|
||||
|
||||
extern TArray<int> blockingpairs[MAXWALLS];
|
||||
|
||||
|
@ -73,11 +74,8 @@ void BunchDrawer::Init(HWDrawInfo *_di, Clipper* c, vec2_t& view, binangle a1, b
|
|||
// Reverse the orientation so that startangle and endangle are properly ordered.
|
||||
wall[i].clipangle = clipper->PointToAngle(wall[i].pos);
|
||||
}
|
||||
for (int i = 0; i < numsectors; i++)
|
||||
{
|
||||
sectstartang[i] = -1;
|
||||
sectendang[i] = -1;
|
||||
}
|
||||
memset(sectionstartang, -1, sizeof(sectionstartang));
|
||||
memset(sectionendang, -1, sizeof(sectionendang));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -93,7 +91,7 @@ void BunchDrawer::StartScene()
|
|||
Bunches.Clear();
|
||||
CompareData.Clear();
|
||||
gotsector.Zero();
|
||||
gotsector2.Zero();
|
||||
gotsection2.Zero();
|
||||
gotwall.Zero();
|
||||
blockwall.Zero();
|
||||
}
|
||||
|
@ -198,20 +196,22 @@ bool BunchDrawer::CheckClip(walltype* wal)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int BunchDrawer::ClipLine(int line, bool portal)
|
||||
int BunchDrawer::ClipLine(int aline, bool portal)
|
||||
{
|
||||
if (blockwall[line]) return CL_Draw;
|
||||
auto cline = §ionLines[aline];
|
||||
int section = cline->section;
|
||||
int line = cline->wall;
|
||||
|
||||
auto wal = &wall[line];
|
||||
|
||||
auto startAngleBam = wal->clipangle;
|
||||
auto endAngleBam = wall[wal->point2].clipangle;
|
||||
auto startAngleBam = wall[cline->startpoint].clipangle;
|
||||
auto endAngleBam = wall[cline->endpoint].clipangle;
|
||||
|
||||
// Back side, i.e. backface culling - read: endAngle <= startAngle!
|
||||
if (startAngleBam.asbam() - endAngleBam.asbam() < ANGLE_180)
|
||||
{
|
||||
return CL_Skip;
|
||||
}
|
||||
if (line >= 0 && blockwall[line]) return CL_Draw;
|
||||
|
||||
// convert to clipper coordinates and clamp to valid range.
|
||||
int startAngle = startAngleBam.asbam() - ang1.asbam();
|
||||
int endAngle = endAngleBam.asbam() - ang1.asbam();
|
||||
|
@ -219,9 +219,8 @@ int BunchDrawer::ClipLine(int line, bool portal)
|
|||
if (endAngle < 0) endAngle = INT_MAX;
|
||||
|
||||
// since these values are derived from previous calls of this function they cannot be out of range.
|
||||
int sect = wal->sector;
|
||||
int sectStartAngle = sectstartang[sect];
|
||||
auto sectEndAngle = sectendang[sect];
|
||||
int sectStartAngle = sectionstartang[section];
|
||||
auto sectEndAngle = sectionendang[section];
|
||||
|
||||
// check against the maximum possible viewing range of the sector.
|
||||
// Todo: check if this is sufficient or if we really have to do a more costly check against the single visible segments.
|
||||
|
@ -239,7 +238,8 @@ int BunchDrawer::ClipLine(int line, bool portal)
|
|||
return CL_Skip;
|
||||
}
|
||||
|
||||
if (wal->nextwall == -1 || (wal->cstat & CSTAT_WALL_1WAY) || CheckClip(wal))
|
||||
auto wal = &wall[line];
|
||||
if (cline->partner == -1 || (wal->cstat & CSTAT_WALL_1WAY) || CheckClip(wal))
|
||||
{
|
||||
// one-sided
|
||||
if (!portal) clipper->AddClipRange(startAngle, endAngle);
|
||||
|
@ -250,16 +250,16 @@ int BunchDrawer::ClipLine(int line, bool portal)
|
|||
if (portal) clipper->RemoveClipRange(startAngle, endAngle);
|
||||
|
||||
// set potentially visible viewing range for this line's back sector.
|
||||
int nsect = wal->nextsector;
|
||||
if (sectstartang[nsect] == -1)
|
||||
int nsection = cline->partnersection;
|
||||
if (sectionstartang[nsection] == -1)
|
||||
{
|
||||
sectstartang[nsect] = startAngle;
|
||||
sectendang[nsect] = endAngle;
|
||||
sectionstartang[nsection] = startAngle;
|
||||
sectionendang[nsection] = endAngle;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (startAngle < sectstartang[nsect]) sectstartang[nsect] = startAngle;
|
||||
if (endAngle > sectendang[nsect]) sectendang[nsect] = endAngle;
|
||||
if (startAngle < sectionstartang[nsection]) sectionstartang[nsection] = startAngle;
|
||||
if (endAngle > sectionendang[nsection]) sectionendang[nsection] = endAngle;
|
||||
}
|
||||
|
||||
return CL_Draw | CL_Pass;
|
||||
|
@ -283,30 +283,34 @@ void BunchDrawer::ProcessBunch(int bnch)
|
|||
|
||||
if (clipped & CL_Draw)
|
||||
{
|
||||
for (auto p : blockingpairs[i]) blockwall.Set(p);
|
||||
show2dwall.Set(i);
|
||||
|
||||
if (!gotwall[i])
|
||||
int ww = sectionLines[i].wall;
|
||||
if (ww != -1)
|
||||
{
|
||||
gotwall.Set(i);
|
||||
ClipWall.Unclock();
|
||||
Bsp.Unclock();
|
||||
SetupWall.Clock();
|
||||
for (auto p : blockingpairs[ww]) blockwall.Set(sectionLines[p].wall);
|
||||
show2dwall.Set(ww);
|
||||
|
||||
HWWall hwwall;
|
||||
hwwall.Process(di, &wall[i], §or[bunch->sectnum], wall[i].nextsector < 0 ? nullptr : §or[wall[i].nextsector]);
|
||||
rendered_lines++;
|
||||
if (!gotwall[i])
|
||||
{
|
||||
gotwall.Set(i);
|
||||
ClipWall.Unclock();
|
||||
Bsp.Unclock();
|
||||
SetupWall.Clock();
|
||||
|
||||
SetupWall.Unclock();
|
||||
Bsp.Clock();
|
||||
ClipWall.Clock();
|
||||
HWWall hwwall;
|
||||
hwwall.Process(di, &wall[ww], §or[bunch->sectnum], wall[ww].nextsector < 0 ? nullptr : §or[wall[ww].nextsector]);
|
||||
rendered_lines++;
|
||||
|
||||
SetupWall.Unclock();
|
||||
Bsp.Clock();
|
||||
ClipWall.Clock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clipped & CL_Pass)
|
||||
{
|
||||
ClipWall.Unclock();
|
||||
ProcessSector(wall[i].nextsector, false);
|
||||
ProcessSection(sectionLines[i].partnersection, false);
|
||||
ClipWall.Clock();
|
||||
}
|
||||
}
|
||||
|
@ -319,16 +323,21 @@ void BunchDrawer::ProcessBunch(int bnch)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int BunchDrawer::WallInFront(int wall1, int wall2)
|
||||
int BunchDrawer::WallInFront(int line1, int line2)
|
||||
{
|
||||
double x1s = WallStartX(wall1);
|
||||
double y1s = WallStartY(wall1);
|
||||
double x1e = WallEndX(wall1);
|
||||
double y1e = WallEndY(wall1);
|
||||
double x2s = WallStartX(wall2);
|
||||
double y2s = WallStartY(wall2);
|
||||
double x2e = WallEndX(wall2);
|
||||
double y2e = WallEndY(wall2);
|
||||
int wall1s = sectionLines[line1].startpoint;
|
||||
int wall1e = sectionLines[line1].endpoint;
|
||||
int wall2s = sectionLines[line2].startpoint;
|
||||
int wall2e = sectionLines[line2].endpoint;
|
||||
|
||||
double x1s = WallStartX(wall1s);
|
||||
double y1s = WallStartY(wall1s);
|
||||
double x1e = WallStartX(wall1e);
|
||||
double y1e = WallStartY(wall1e);
|
||||
double x2s = WallStartX(wall2s);
|
||||
double y2s = WallStartY(wall2s);
|
||||
double x2e = WallStartX(wall2e);
|
||||
double y2e = WallStartY(wall2e);
|
||||
|
||||
double dx = x1e - x1s;
|
||||
double dy = y1e - y1s;
|
||||
|
@ -482,18 +491,18 @@ int BunchDrawer::FindClosestBunch()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void BunchDrawer::ProcessSector(int sectnum, bool portal)
|
||||
void BunchDrawer::ProcessSection(int sectionnum, bool portal)
|
||||
{
|
||||
if (gotsector2[sectnum]) return;
|
||||
gotsector2.Set(sectnum);
|
||||
if (gotsection2[sectionnum]) return;
|
||||
gotsection2.Set(sectionnum);
|
||||
|
||||
auto sect = §or[sectnum];
|
||||
bool inbunch;
|
||||
binangle startangle;
|
||||
|
||||
SetupSprite.Clock();
|
||||
|
||||
int z;
|
||||
int sectnum = sections[sectionnum].sector;
|
||||
if (!gotsector[sectnum])
|
||||
{
|
||||
gotsector.Set(sectnum);
|
||||
|
@ -526,22 +535,23 @@ void BunchDrawer::ProcessSector(int sectnum, bool portal)
|
|||
|
||||
SetupFlat.Clock();
|
||||
HWFlat flat;
|
||||
flat.ProcessSector(di, §or[sectnum]);
|
||||
flat.ProcessSector(di, §or[sectnum], sectionnum);
|
||||
SetupFlat.Unclock();
|
||||
|
||||
//Todo: process subsectors
|
||||
inbunch = false;
|
||||
for (int i = 0; i < sect->wallnum; i++)
|
||||
auto section = §ions[sectionnum];
|
||||
for (unsigned i = 0; i < section->lines.Size(); i++)
|
||||
{
|
||||
auto thiswall = &wall[sect->wallptr + i];
|
||||
auto thisline = §ionLines[section->lines[i]];
|
||||
|
||||
#ifdef _DEBUG
|
||||
// For displaying positions in debugger
|
||||
DVector2 start = { WallStartX(thiswall), WallStartY(thiswall) };
|
||||
DVector2 end = { WallStartX(thiswall->point2), WallStartY(thiswall->point2) };
|
||||
//DVector2 start = { WallStartX(thiswall), WallStartY(thiswall) };
|
||||
//DVector2 end = { WallStartX(thiswall->point2), WallStartY(thiswall->point2) };
|
||||
#endif
|
||||
binangle walang1 = thiswall->clipangle;
|
||||
binangle walang2 = wall[thiswall->point2].clipangle;
|
||||
binangle walang1 = wall[thisline->startpoint].clipangle;
|
||||
binangle walang2 = wall[thisline->endpoint].clipangle;
|
||||
|
||||
// outside the visible area or seen from the backside.
|
||||
if ((walang1.asbam() - ang1.asbam() > ANGLE_180 && walang2.asbam() - ang1.asbam() > ANGLE_180) ||
|
||||
|
@ -554,14 +564,14 @@ void BunchDrawer::ProcessSector(int sectnum, bool portal)
|
|||
{
|
||||
startangle = walang1;
|
||||
//Printf("Starting bunch:\n\tWall %d\n", sect->wallptr + i);
|
||||
inbunch = StartBunch(sectnum, sect->wallptr + i, walang1, walang2, portal);
|
||||
inbunch = StartBunch(sectnum, section->lines[i], walang1, walang2, portal);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Printf("\tWall %d\n", sect->wallptr + i);
|
||||
inbunch = AddLineToBunch(sect->wallptr + i, walang2);
|
||||
inbunch = AddLineToBunch(section->lines[i], walang2);
|
||||
}
|
||||
if (thiswall->point2 != sect->wallptr + i + 1) inbunch = false;
|
||||
if (thisline->endpoint != section->lines[i] + 1) inbunch = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -580,12 +590,19 @@ void BunchDrawer::RenderScene(const int* viewsectors, unsigned sectcount, bool p
|
|||
|
||||
for (unsigned i = 0; i < sectcount; i++)
|
||||
{
|
||||
sectstartang[viewsectors[i]] = 0;
|
||||
sectendang[viewsectors[i]] = int (ang2.asbam() - ang1.asbam());
|
||||
for (auto j : sectionspersector[viewsectors[i]])
|
||||
{
|
||||
sectionstartang[j] = 0;
|
||||
sectionendang[j] = int(ang2.asbam() - ang1.asbam());
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < sectcount; i++)
|
||||
ProcessSector(viewsectors[i], portal);
|
||||
{
|
||||
for (auto j : sectionspersector[viewsectors[i]])
|
||||
{
|
||||
ProcessSection(j, portal);
|
||||
}
|
||||
}
|
||||
while (Bunches.Size() > 0)
|
||||
{
|
||||
int closest = FindClosestBunch();
|
||||
|
@ -607,7 +624,7 @@ void BunchDrawer::RenderScene(const int* viewsectors, unsigned sectcount, bool p
|
|||
ang1 = bamang(rotang - ANGLE_90);
|
||||
ang2 = bamang(rotang + ANGLE_90 - 1);
|
||||
process();
|
||||
gotsector2.Zero();
|
||||
gotsection2.Zero();
|
||||
ang1 = bamang(rotang + ANGLE_90);
|
||||
ang2 = bamang(rotang - ANGLE_90 - 1);
|
||||
process();
|
||||
|
|
|
@ -28,12 +28,12 @@ class BunchDrawer
|
|||
vec2_t iview;
|
||||
float gcosang, gsinang;
|
||||
FixedBitArray<MAXSECTORS> gotsector;
|
||||
FixedBitArray<MAXSECTORS> gotsector2;
|
||||
FixedBitArray<MAXSECTORS*5/4> gotsection2;
|
||||
FixedBitArray<MAXWALLS> gotwall;
|
||||
FixedBitArray<MAXWALLS> blockwall;
|
||||
binangle ang1, ang2;
|
||||
|
||||
int sectstartang[MAXSECTORS], sectendang[MAXSECTORS];
|
||||
int sectionstartang[MAXSECTORS*5/4], sectionendang[MAXSECTORS*5/4];
|
||||
|
||||
private:
|
||||
|
||||
|
@ -54,7 +54,7 @@ private:
|
|||
int WallInFront(int wall1, int wall2);
|
||||
int BunchInFront(FBunch* b1, FBunch* b2);
|
||||
int FindClosestBunch();
|
||||
void ProcessSector(int sectnum, bool portal);
|
||||
void ProcessSection(int sectnum, bool portal);
|
||||
|
||||
public:
|
||||
void Init(HWDrawInfo* _di, Clipper* c, vec2_t& view, binangle a1, binangle a2);
|
||||
|
|
|
@ -249,6 +249,7 @@ public:
|
|||
class HWFlat
|
||||
{
|
||||
public:
|
||||
int section;
|
||||
sectortype * sec;
|
||||
spritetype* sprite; // for flat sprites.
|
||||
FGameTexture *texture;
|
||||
|
@ -275,7 +276,7 @@ public:
|
|||
//void SetupLights(HWDrawInfo *di, FLightNode *head, FDynLightData &lightdata, int portalgroup);
|
||||
|
||||
void PutFlat(HWDrawInfo* di, int whichplane);
|
||||
void ProcessSector(HWDrawInfo *di, sectortype * frontsector, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies.
|
||||
void ProcessSector(HWDrawInfo *di, sectortype * frontsector, int sectionnum, int which = 7 /*SSRF_RENDERALL*/); // cannot use constant due to circular dependencies.
|
||||
void ProcessFlatSprite(HWDrawInfo* di, spritetype* sprite, sectortype* sector);
|
||||
|
||||
void DrawSubsectors(HWDrawInfo *di, FRenderState &state);
|
||||
|
|
|
@ -98,7 +98,7 @@ void HWFlat::MakeVertices()
|
|||
bool canvas = texture->isHardwareCanvas();
|
||||
if (sprite == nullptr)
|
||||
{
|
||||
auto mesh = sectorGeometry.get(sec - sector, plane, geoofs);
|
||||
auto mesh = sectorGeometry.get(section, plane, geoofs);
|
||||
if (!mesh) return;
|
||||
auto ret = screen->mVertexData->AllocVertices(mesh->vertices.Size());
|
||||
auto vp = ret.first;
|
||||
|
@ -158,7 +158,7 @@ void HWFlat::DrawFlat(HWDrawInfo *di, FRenderState &state, bool translucent)
|
|||
|
||||
if (!sprite)
|
||||
{
|
||||
auto mesh = sectorGeometry.get(sec - sector, plane, geoofs);
|
||||
auto mesh = sectorGeometry.get(section, plane, geoofs);
|
||||
state.SetNormal(mesh->normal);
|
||||
}
|
||||
else
|
||||
|
@ -230,7 +230,7 @@ void HWFlat::PutFlat(HWDrawInfo *di, int whichplane)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void HWFlat::ProcessSector(HWDrawInfo *di, sectortype * frontsector, int which)
|
||||
void HWFlat::ProcessSector(HWDrawInfo *di, sectortype * frontsector, int section_, int which)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (frontsector - sector == gl_breaksec)
|
||||
|
@ -249,6 +249,7 @@ void HWFlat::ProcessSector(HWDrawInfo *di, sectortype * frontsector, int which)
|
|||
fade = lookups.getFade(frontsector->floorpal); // fog is per sector.
|
||||
visibility = sectorVisibility(frontsector);
|
||||
sec = frontsector;
|
||||
section = section_;
|
||||
sprite = nullptr;
|
||||
geoofs = di->geoofs;
|
||||
|
||||
|
|
|
@ -110,53 +110,21 @@ void FPortalSceneState::EndFrame(HWDrawInfo *di, FRenderState &state)
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Renders one sky portal without a stencil.
|
||||
// In more complex scenes using a stencil for skies can severely stall
|
||||
// the GPU and there's rarely more than one sky visible at a time.
|
||||
// Renders one sky portal without a stencil. Only useful if this is the only portal in view.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di, FRenderState &state)
|
||||
{
|
||||
HWPortal * p;
|
||||
HWPortal * best = nullptr;
|
||||
HWPortal* best = nullptr;
|
||||
unsigned bestindex = 0;
|
||||
|
||||
// Find the one with the highest amount of lines.
|
||||
// Normally this is also the one that saves the largest amount
|
||||
// of time by drawing it before the scene itself.
|
||||
auto &portals = outer_di->Portals;
|
||||
for (int i = portals.Size() - 1; i >= 0; --i)
|
||||
{
|
||||
p = portals[i];
|
||||
if (p->lines.Size() > 0 && p->IsSky())
|
||||
{
|
||||
// Cannot clear the depth buffer inside a portal recursion
|
||||
if (recursion && p->NeedDepthBuffer()) continue;
|
||||
if (recursion > 0 || outer_di->Portals.Size() != 1 || !outer_di->Portals[0]->IsSky()) return false;
|
||||
|
||||
if (!best || p->lines.Size() > best->lines.Size())
|
||||
{
|
||||
best = p;
|
||||
bestindex = i;
|
||||
}
|
||||
|
||||
// If the portal area contains the current camera viewpoint, let's always use it because it's likely to give the largest area.
|
||||
if (p->boundingBox.contains(outer_di->Viewpoint.Pos))
|
||||
{
|
||||
best = p;
|
||||
bestindex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best)
|
||||
{
|
||||
portals.Delete(bestindex);
|
||||
RenderPortal(best, state, false, outer_di);
|
||||
delete best;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
best = outer_di->Portals[0];
|
||||
outer_di->Portals.Clear();
|
||||
RenderPortal(best, state, false, outer_di);
|
||||
delete best;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -352,7 +352,6 @@ protected:
|
|||
bool Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clipper) override;
|
||||
void Shutdown(HWDrawInfo *di, FRenderState &rstate) override;
|
||||
virtual void * GetSource() const { return origin; }
|
||||
virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one.
|
||||
virtual const char *GetName();
|
||||
virtual int GetType() { return PORTAL_SECTOR_CEILING; }
|
||||
PortalDesc *origin;
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
#include "interpolate.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "render.h"
|
||||
#include "hw_sections.h"
|
||||
#include "sectorgeometry.h"
|
||||
#include <zlib.h>
|
||||
|
||||
|
||||
|
@ -639,6 +641,8 @@ void SerializeMap(FSerializer& arc)
|
|||
if (arc.isReading())
|
||||
{
|
||||
setWallSectors();
|
||||
hw_BuildSections();
|
||||
sectorGeometry.SetSize(numsections);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,59 +51,371 @@
|
|||
#include <vpx/vpx_decoder.h>
|
||||
#include <vpx/vp8dx.h>
|
||||
#include "raze_music.h"
|
||||
#include "vm.h"
|
||||
#include "mapinfo.h"
|
||||
|
||||
static DObject* runner;
|
||||
static SummaryInfo sinfo;
|
||||
static PClass* runnerclass;
|
||||
static PType* runnerclasstype;
|
||||
static PType* maprecordtype;
|
||||
static PType* summaryinfotype;
|
||||
static CompletionFunc completion;
|
||||
static int ticks;
|
||||
static SummaryInfo summaryinfo;
|
||||
|
||||
IMPLEMENT_CLASS(DScreenJob, true, false)
|
||||
IMPLEMENT_CLASS(DImageScreen, true, false)
|
||||
|
||||
|
||||
bool DSkippableScreenJob::OnEvent(event_t* evt)
|
||||
{
|
||||
if (evt->type == EV_KeyDown && !specialKeyEvent(evt))
|
||||
{
|
||||
state = skipped;
|
||||
Skipped();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DBlackScreen::OnTick()
|
||||
{
|
||||
if (cleared)
|
||||
{
|
||||
int span = ticks * 1000 / GameTicRate;
|
||||
if (span > wait) state = finished;
|
||||
}
|
||||
}
|
||||
|
||||
void DBlackScreen::Draw(double)
|
||||
{
|
||||
cleared = true;
|
||||
twod->ClearScreen();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//=============================================================================
|
||||
|
||||
void DImageScreen::OnTick()
|
||||
void Job_Init()
|
||||
{
|
||||
if (cleared)
|
||||
static bool done = false;
|
||||
if (!done)
|
||||
{
|
||||
int span = ticks * 1000 / GameTicRate;
|
||||
if (span > waittime) state = finished;
|
||||
done = true;
|
||||
GC::AddMarkerFunc([] { GC::Mark(runner); });
|
||||
}
|
||||
runnerclass = PClass::FindClass("ScreenJobRunner");
|
||||
if (!runnerclass) I_FatalError("ScreenJobRunner not defined");
|
||||
runnerclasstype = NewPointer(runnerclass);
|
||||
|
||||
maprecordtype = NewPointer(NewStruct("MapRecord", nullptr, true));
|
||||
summaryinfotype = NewPointer(NewStruct("SummaryInfo", nullptr, true));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
static VMFunction* LookupFunction(const char* qname, bool validate = true)
|
||||
{
|
||||
int p = strcspn(qname, ".");
|
||||
if (p == 0) I_Error("Call to undefined function %s", qname);
|
||||
FString clsname(qname, p);
|
||||
FString funcname = qname + p + 1;
|
||||
|
||||
auto func = PClass::FindFunction(clsname, funcname);
|
||||
if (func == nullptr) I_Error("Call to undefined function %s", qname);
|
||||
if (validate)
|
||||
{
|
||||
// these conditions must be met by all functions for this interface.
|
||||
if (func->Proto->ReturnTypes.Size() != 0) I_Error("Bad cutscene function %s. Return value not allowed", qname);
|
||||
if (func->ImplicitArgs != 0) I_Error("Bad cutscene function %s. Must be static", qname);
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CallCreateFunction(const char* qname, DObject* runner)
|
||||
{
|
||||
auto func = LookupFunction(qname);
|
||||
if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname);
|
||||
if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname);
|
||||
VMValue val = runner;
|
||||
VMCall(func, &val, 1, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map)
|
||||
{
|
||||
auto func = LookupFunction(qname);
|
||||
if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here.
|
||||
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
|
||||
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype)
|
||||
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname);
|
||||
VMValue val[2] = { runner, map };
|
||||
VMCall(func, val, 2, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* map, SummaryInfo* info, MapRecord* map2)
|
||||
{
|
||||
auto func = LookupFunction(qname);
|
||||
auto s = func->Proto->ArgumentTypes.Size();
|
||||
auto at = func->Proto->ArgumentTypes.Data();
|
||||
if (s != 3 && s != 4) I_Error("Bad map-cutscene function %s. Must receive precisely three or four arguments.", qname);
|
||||
if (at[0] != runnerclasstype && at[1] != maprecordtype && at[2] != summaryinfotype && (s == 3 || at[3] == maprecordtype))
|
||||
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference,", qname);
|
||||
if (info) summaryinfo = *info; // must be copied to a persistent location.
|
||||
else summaryinfo = {};
|
||||
VMValue val[] = { runner, map, &summaryinfo, map2 };
|
||||
VMCall(func, val, s, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
DObject* CreateRunner(bool clearbefore = true)
|
||||
{
|
||||
auto obj = runnerclass->CreateNew();
|
||||
auto func = LookupFunction("ScreenJobRunner.Init", false);
|
||||
VMValue val[3] = { obj, clearbefore, false };
|
||||
VMCall(func, val, 3, nullptr, 0);
|
||||
return obj;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps)
|
||||
{
|
||||
auto obj = runnerclass->CreateNew();
|
||||
auto func = LookupFunction("ScreenJobRunner.AddGenericVideo", false);
|
||||
VMValue val[] = { runner, &fn, soundid, fps };
|
||||
VMCall(func, val, 4, nullptr, 0);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void CutsceneDef::Create(DObject* runner)
|
||||
{
|
||||
if (function.IsNotEmpty())
|
||||
{
|
||||
CallCreateFunction(function, runner);
|
||||
}
|
||||
else if (video.IsNotEmpty())
|
||||
{
|
||||
AddGenericVideo(runner, video, GetSound(), framespersec);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void DImageScreen::Draw(double smoothratio)
|
||||
bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition)
|
||||
{
|
||||
if (tilenum > 0) tex = tileGetTexture(tilenum, true);
|
||||
twod->ClearScreen();
|
||||
if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE);
|
||||
cleared = true;
|
||||
if (!transition && transitiononly) return false;
|
||||
if (function.CompareNoCase("none") == 0)
|
||||
return true; // play nothing but return as being validated
|
||||
if (function.IsNotEmpty())
|
||||
{
|
||||
CallCreateMapFunction(function, runner, map);
|
||||
return true;
|
||||
}
|
||||
else if (video.IsNotEmpty())
|
||||
{
|
||||
AddGenericVideo(runner, video, GetSound(), framespersec);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void DeleteScreenJob()
|
||||
{
|
||||
if (runner) runner->Destroy();
|
||||
runner = nullptr;
|
||||
}
|
||||
|
||||
void EndScreenJob()
|
||||
{
|
||||
DeleteScreenJob();
|
||||
if (completion) completion(false);
|
||||
completion = nullptr;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool ScreenJobResponder(event_t* ev)
|
||||
{
|
||||
if (ev->type == EV_KeyDown)
|
||||
{
|
||||
// We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
|
||||
auto binding = Bindings.GetBinding(ev->data1);
|
||||
if (binding.CompareNoCase("toggleconsole") == 0)
|
||||
{
|
||||
C_ToggleConsole();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FInputEvent evt = ev;
|
||||
if (runner)
|
||||
{
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent)
|
||||
{
|
||||
int result = 0;
|
||||
VMValue parm[] = { runner, &evt };
|
||||
VMReturn ret(&result);
|
||||
VMCall(func, parm, 2, &ret, 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool ScreenJobTick()
|
||||
{
|
||||
ticks++;
|
||||
if (runner)
|
||||
{
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick)
|
||||
{
|
||||
int result = 0;
|
||||
VMValue parm[] = { runner };
|
||||
VMReturn ret(&result);
|
||||
VMCall(func, parm, 1, &ret, 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void ScreenJobDraw()
|
||||
{
|
||||
double smoothratio = I_GetTimeFrac();
|
||||
|
||||
if (runner)
|
||||
{
|
||||
twod->ClearScreen();
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame)
|
||||
{
|
||||
VMValue parm[] = { runner, smoothratio };
|
||||
VMCall(func, parm, 2, nullptr, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool ScreenJobValidate()
|
||||
{
|
||||
if (runner)
|
||||
{
|
||||
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate)
|
||||
{
|
||||
int res;
|
||||
VMValue parm[] = { runner };
|
||||
VMReturn ret(&res);
|
||||
VMCall(func, parm, 2, &ret, 1);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_)
|
||||
{
|
||||
if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0)
|
||||
{
|
||||
completion = completion_;
|
||||
runner = CreateRunner();
|
||||
GC::WriteBarrier(runner);
|
||||
try
|
||||
{
|
||||
cs.Create(runner);
|
||||
if (!ScreenJobValidate())
|
||||
{
|
||||
runner->Destroy();
|
||||
runner = nullptr;
|
||||
return false;
|
||||
}
|
||||
if (flags & SJ_DELAY) intermissiondelay = 10; // need to wait a bit at the start to let the timer catch up.
|
||||
else intermissiondelay = 0;
|
||||
gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (runner) runner->Destroy();
|
||||
runner = nullptr;
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StartCutscene(const char* s, int flags, const CompletionFunc& completion)
|
||||
{
|
||||
CutsceneDef def;
|
||||
def.function = s;
|
||||
return StartCutscene(def, 0, completion);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic)
|
||||
{
|
||||
Mus_Stop();
|
||||
FX_StopAllSounds(); // JBF 20031228
|
||||
if (userConfig.nologo)
|
||||
{
|
||||
gameaction = def_ga;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!StartCutscene(globalCutscenes.Intro, SJ_BLOCKUI|SJ_DELAY, [=](bool) {
|
||||
gameaction = complete_ga;
|
||||
})) gameaction = def_ga;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -112,269 +424,137 @@ void DImageScreen::Draw(double smoothratio)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class ScreenJobRunner
|
||||
void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
|
||||
{
|
||||
enum
|
||||
{
|
||||
State_Clear,
|
||||
State_Run,
|
||||
State_Fadeout
|
||||
};
|
||||
TArray<JobDesc> jobs;
|
||||
CompletionFunc completion;
|
||||
int index = -1;
|
||||
float screenfade;
|
||||
bool clearbefore;
|
||||
int actionState;
|
||||
int terminateState;
|
||||
int fadeticks = 0;
|
||||
int last_paused_tic = -1;
|
||||
|
||||
public:
|
||||
ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_)
|
||||
: completion(std::move(completion_)), clearbefore(clearbefore_)
|
||||
{
|
||||
jobs.Resize(count);
|
||||
memcpy(jobs.Data(), jobs_, count * sizeof(JobDesc));
|
||||
// Release all jobs from the garbage collector - the code as it is cannot deal with them getting collected. This should be removed later once the GC is working.
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
jobs[i].job->Release();
|
||||
}
|
||||
AdvanceJob(false);
|
||||
}
|
||||
|
||||
~ScreenJobRunner()
|
||||
{
|
||||
DeleteJobs();
|
||||
}
|
||||
|
||||
void DeleteJobs()
|
||||
{
|
||||
for (auto& job : jobs)
|
||||
{
|
||||
job.job->ObjectFlags |= OF_YesReallyDelete;
|
||||
delete job.job;
|
||||
}
|
||||
jobs.Clear();
|
||||
}
|
||||
|
||||
void AdvanceJob(bool skip)
|
||||
{
|
||||
if (index >= 0)
|
||||
{
|
||||
if (jobs[index].postAction) jobs[index].postAction();
|
||||
jobs[index].job->Destroy();
|
||||
}
|
||||
index++;
|
||||
while (index < jobs.Size() && (jobs[index].job == nullptr || (skip && jobs[index].ignoreifskipped)))
|
||||
{
|
||||
if (jobs[index].job != nullptr) jobs[index].job->Destroy();
|
||||
index++;
|
||||
}
|
||||
actionState = clearbefore ? State_Clear : State_Run;
|
||||
if (index < jobs.Size())
|
||||
{
|
||||
jobs[index].job->fadestate = !paused && jobs[index].job->fadestyle & DScreenJob::fadein? DScreenJob::fadein : DScreenJob::visible;
|
||||
jobs[index].job->Start();
|
||||
}
|
||||
inputState.ClearAllInput();
|
||||
}
|
||||
|
||||
int DisplayFrame(double smoothratio)
|
||||
{
|
||||
auto& job = jobs[index];
|
||||
auto now = I_GetTimeNS();
|
||||
bool processed = job.job->ProcessInput();
|
||||
|
||||
if (job.job->fadestate == DScreenJob::fadein)
|
||||
{
|
||||
double ms = (job.job->ticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime;
|
||||
float screenfade = (float)clamp(ms, 0., 1.);
|
||||
twod->SetScreenFade(screenfade);
|
||||
if (screenfade == 1.f) job.job->fadestate = DScreenJob::visible;
|
||||
}
|
||||
int state = job.job->DrawFrame(smoothratio);
|
||||
twod->SetScreenFade(1.f);
|
||||
return state;
|
||||
}
|
||||
|
||||
int FadeoutFrame(double smoothratio)
|
||||
{
|
||||
auto& job = jobs[index];
|
||||
double ms = (fadeticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime;
|
||||
float screenfade = 1.f - (float)clamp(ms, 0., 1.);
|
||||
twod->SetScreenFade(screenfade);
|
||||
job.job->DrawFrame(1.);
|
||||
return (screenfade > 0.f);
|
||||
}
|
||||
|
||||
bool OnEvent(event_t* ev)
|
||||
{
|
||||
if (paused || index >= jobs.Size()) return false;
|
||||
|
||||
if (ev->type == EV_KeyDown)
|
||||
{
|
||||
// We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
|
||||
auto binding = Bindings.GetBinding(ev->data1);
|
||||
if (binding.CompareNoCase("toggleconsole") == 0)
|
||||
{
|
||||
C_ToggleConsole();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (jobs[index].job->state != DScreenJob::running) return false;
|
||||
|
||||
return jobs[index].job->OnEvent(ev);
|
||||
}
|
||||
|
||||
void OnFinished()
|
||||
completion = completion_;
|
||||
runner = CreateRunner();
|
||||
GC::WriteBarrier(runner);
|
||||
|
||||
const char* qname = globalCutscenes.MPSummaryScreen;
|
||||
auto func = LookupFunction(qname);
|
||||
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
|
||||
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != TypeSInt32)
|
||||
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname);
|
||||
VMValue val[2] = { runner, numplayers };
|
||||
VMCall(func, val, 2, nullptr, 0);
|
||||
if (!ScreenJobValidate())
|
||||
{
|
||||
runner->Destroy();
|
||||
runner = nullptr;
|
||||
if (completion) completion(false);
|
||||
completion = nullptr; // only finish once.
|
||||
completion = nullptr;
|
||||
return;
|
||||
}
|
||||
gameaction = ga_intermission;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_)
|
||||
{
|
||||
completion = completion_;
|
||||
runner = CreateRunner();
|
||||
GC::WriteBarrier(runner);
|
||||
|
||||
// retrieve cluster relations for cluster-based cutscenes.
|
||||
ClusterDef* fromcluster = nullptr, *tocluster = nullptr;
|
||||
if (fromMap) fromcluster = FindCluster(fromMap->cluster);
|
||||
if (toMap) tocluster = FindCluster(toMap->cluster);
|
||||
if (fromcluster == tocluster) fromcluster = tocluster = nullptr;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (paused) return;
|
||||
if (index >= jobs.Size())
|
||||
if (fromMap)
|
||||
{
|
||||
//DeleteJobs();
|
||||
//twod->SetScreenFade(1);
|
||||
//twod->ClearScreen(); // This must not leave the 2d buffer empty.
|
||||
//if (gamestate == GS_INTRO) OnFinished();
|
||||
//else Net_WriteByte(DEM_ENDSCREENJOB); // intermissions must be terminated synchronously.
|
||||
if (!fromMap->outro.Create(runner, fromMap, !!toMap))
|
||||
{
|
||||
if (fromcluster == nullptr || !fromcluster->outro.Create(runner, fromMap, !!toMap))
|
||||
globalCutscenes.DefaultMapOutro.Create(runner, fromMap, !!toMap);
|
||||
}
|
||||
|
||||
}
|
||||
if (fromMap || (g_gameType & GAMEFLAG_PSEXHUMED))
|
||||
CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info, toMap);
|
||||
|
||||
if (toMap)
|
||||
{
|
||||
if (!toMap->intro.Create(runner, toMap, !!fromMap))
|
||||
{
|
||||
if (tocluster == nullptr || !tocluster->intro.Create(runner, toMap, !!fromMap))
|
||||
globalCutscenes.DefaultMapIntro.Create(runner, toMap, !!fromMap);
|
||||
}
|
||||
globalCutscenes.LoadingScreen.Create(runner, toMap, true);
|
||||
}
|
||||
else if (isShareware())
|
||||
{
|
||||
globalCutscenes.SharewareEnd.Create(runner);
|
||||
}
|
||||
if (!ScreenJobValidate())
|
||||
{
|
||||
runner->Destroy();
|
||||
runner = nullptr;
|
||||
if (completion) completion(false);
|
||||
completion = nullptr;
|
||||
return;
|
||||
}
|
||||
gameaction = ga_intermission;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (runner) runner->Destroy();
|
||||
runner = nullptr;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
CCMD(testcutscene)
|
||||
{
|
||||
if (argv.argc() < 2)
|
||||
{
|
||||
Printf("Usage: testcutscene <buildfunction>\n");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (StartCutscene(argv[1], 0, [](bool) {}))
|
||||
{
|
||||
C_HideConsole();
|
||||
}
|
||||
}
|
||||
catch (const CRecoverableError& err)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "Unable to play cutscene: %s\n", err.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Blood:
|
||||
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
|
||||
else
|
||||
{
|
||||
if (jobs[index].job->state == DScreenJob::running)
|
||||
gameaction = ga_mainmenu;
|
||||
}
|
||||
RunScreenJob(jobs, [](bool) {
|
||||
Mus_Stop();
|
||||
gameaction = ga_mainmenu;
|
||||
}, SJ_BLOCKUI);
|
||||
|
||||
Exhumed:
|
||||
if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; });
|
||||
else gameaction = ga_mainmenu;
|
||||
|
||||
SW:
|
||||
if (!userConfig.nologo) Logo([](bool)
|
||||
{
|
||||
jobs[index].job->ticks++;
|
||||
jobs[index].job->OnTick();
|
||||
}
|
||||
else if (jobs[index].job->state == DScreenJob::stopping)
|
||||
{
|
||||
fadeticks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RunFrame()
|
||||
{
|
||||
if (index >= jobs.Size())
|
||||
{
|
||||
DeleteJobs();
|
||||
twod->SetScreenFade(1);
|
||||
twod->ClearScreen(); // This must not leave the 2d buffer empty.
|
||||
if (completion) completion(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensure that we won't go back in time if the menu is dismissed without advancing our ticker
|
||||
bool menuon = paused;
|
||||
if (menuon) last_paused_tic = jobs[index].job->ticks;
|
||||
else if (last_paused_tic == jobs[index].job->ticks) menuon = true;
|
||||
double smoothratio = menuon ? 1. : I_GetTimeFrac();
|
||||
|
||||
if (actionState == State_Clear)
|
||||
{
|
||||
actionState = State_Run;
|
||||
twod->ClearScreen();
|
||||
}
|
||||
else if (actionState == State_Run)
|
||||
{
|
||||
terminateState = DisplayFrame(smoothratio);
|
||||
if (terminateState < 1)
|
||||
{
|
||||
// Must lock before displaying.
|
||||
if (jobs[index].job->fadestyle & DScreenJob::fadeout)
|
||||
{
|
||||
jobs[index].job->fadestate = DScreenJob::fadeout;
|
||||
jobs[index].job->state = DScreenJob::stopping;
|
||||
actionState = State_Fadeout;
|
||||
fadeticks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
AdvanceJob(terminateState < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (actionState == State_Fadeout)
|
||||
{
|
||||
int ended = FadeoutFrame(smoothratio);
|
||||
if (ended < 1)
|
||||
{
|
||||
jobs[index].job->state = DScreenJob::stopped;
|
||||
AdvanceJob(terminateState < 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
ScreenJobRunner *runner;
|
||||
|
||||
void RunScreenJob(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore, bool blockingui)
|
||||
{
|
||||
assert(completion != nullptr);
|
||||
videoclearFade();
|
||||
if (count)
|
||||
{
|
||||
runner = new ScreenJobRunner(jobs, count, completion, clearbefore);
|
||||
gameaction = blockingui? ga_intro : ga_intermission;
|
||||
}
|
||||
else
|
||||
{
|
||||
completion(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteScreenJob()
|
||||
{
|
||||
if (runner)
|
||||
{
|
||||
delete runner;
|
||||
runner = nullptr;
|
||||
}
|
||||
twod->SetScreenFade(1);
|
||||
}
|
||||
|
||||
void EndScreenJob()
|
||||
{
|
||||
if (runner) runner->OnFinished();
|
||||
DeleteScreenJob();
|
||||
}
|
||||
|
||||
|
||||
bool ScreenJobResponder(event_t* ev)
|
||||
{
|
||||
if (runner) return runner->OnEvent(ev);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScreenJobTick()
|
||||
{
|
||||
if (runner) runner->OnTick();
|
||||
}
|
||||
|
||||
bool ScreenJobDraw()
|
||||
{
|
||||
// we cannot recover from this because we have no completion callback to call.
|
||||
if (!runner)
|
||||
{
|
||||
// We can get here before a gameaction has been processed. In that case just draw a black screen and wait.
|
||||
if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job");
|
||||
twod->ClearScreen();
|
||||
return false;
|
||||
}
|
||||
auto res = runner->RunFrame();
|
||||
if (!res)
|
||||
{
|
||||
assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing);
|
||||
DeleteScreenJob();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
gameaction = ga_mainmenunostopsound;
|
||||
});
|
||||
else gameaction = ga_mainmenu;
|
||||
|
||||
*/
|
|
@ -3,153 +3,29 @@
|
|||
#include "dobject.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "d_eventbase.h"
|
||||
#include "s_soundinternal.h"
|
||||
#include "gamestate.h"
|
||||
|
||||
using CompletionFunc = std::function<void(bool)>;
|
||||
struct JobDesc;
|
||||
class ScreenJobRunner;
|
||||
|
||||
class DScreenJob : public DObject
|
||||
void Job_Init();
|
||||
|
||||
enum
|
||||
{
|
||||
DECLARE_CLASS(DScreenJob, DObject)
|
||||
const int fadestyle;
|
||||
const float fadetime; // in milliseconds
|
||||
int fadestate = fadein;
|
||||
|
||||
friend class ScreenJobRunner;
|
||||
protected:
|
||||
int ticks = 0;
|
||||
int state = running;
|
||||
bool pausable = true;
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
running = 1, // normal operation
|
||||
skipped = 2, // finished by user skipping
|
||||
finished = 3, // finished by completing its sequence
|
||||
stopping = 4, // running ending animations / fadeout, etc. Will not accept more input.
|
||||
stopped = 5, // we're done here.
|
||||
};
|
||||
enum
|
||||
{
|
||||
visible = 0,
|
||||
fadein = 1,
|
||||
fadeout = 2,
|
||||
};
|
||||
|
||||
DScreenJob(int fade = 0, float fadet = 250.f) : fadestyle(fade), fadetime(fadet) {}
|
||||
|
||||
virtual bool ProcessInput()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void Start() {}
|
||||
virtual bool OnEvent(event_t* evt) { return false; }
|
||||
virtual void OnTick() { /*state = finished;*/ }
|
||||
virtual void Draw(double smoothratio) {}
|
||||
|
||||
int DrawFrame(double smoothratio)
|
||||
{
|
||||
if (state != running) smoothratio = 1; // this is necessary because the ticker won't be incremented anymore to avoid having a negative time span.
|
||||
Draw(smoothratio);
|
||||
if (state == skipped) return -1;
|
||||
if (state == finished) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int GetFadeState() const { return fadestate; }
|
||||
|
||||
SJ_BLOCKUI = 1,
|
||||
SJ_DELAY = 2,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DSkippableScreenJob : public DScreenJob
|
||||
{
|
||||
protected:
|
||||
DSkippableScreenJob(int fade = 0, float fadet = 250.f) : DScreenJob(fade, fadet)
|
||||
{}
|
||||
|
||||
bool OnEvent(event_t* evt) override;
|
||||
virtual void Skipped() {}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DBlackScreen : public DScreenJob
|
||||
{
|
||||
int wait;
|
||||
bool cleared = false;
|
||||
|
||||
public:
|
||||
DBlackScreen(int w) : wait(w) {}
|
||||
void OnTick() override;
|
||||
void Draw(double smooth) override;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DImageScreen : public DSkippableScreenJob
|
||||
{
|
||||
DECLARE_CLASS(DImageScreen, DScreenJob)
|
||||
|
||||
int tilenum = -1;
|
||||
int trans;
|
||||
int waittime; // in ms.
|
||||
bool cleared = false;
|
||||
FGameTexture* tex = nullptr;
|
||||
|
||||
public:
|
||||
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
|
||||
{
|
||||
tex = tile;
|
||||
trans = translation;
|
||||
}
|
||||
|
||||
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
|
||||
{
|
||||
tilenum = tile;
|
||||
trans = translation;
|
||||
}
|
||||
|
||||
void OnTick() override;
|
||||
void Draw(double smooth) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct JobDesc
|
||||
{
|
||||
DScreenJob* job;
|
||||
void (*postAction)();
|
||||
bool ignoreifskipped;
|
||||
};
|
||||
|
||||
|
||||
void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true, bool blockingui = false);
|
||||
void EndScreenJob();
|
||||
void DeleteScreenJob();
|
||||
bool ScreenJobResponder(event_t* ev);
|
||||
void ScreenJobTick();
|
||||
bool ScreenJobDraw();
|
||||
bool ScreenJobTick();
|
||||
void ScreenJobDraw();
|
||||
|
||||
struct AnimSound
|
||||
{
|
||||
int framenum;
|
||||
int soundnum;
|
||||
};
|
||||
|
||||
DScreenJob *PlayVideo(const char *filename, const AnimSound *ans = nullptr, const int *frameticks = nullptr, bool nosoundstop = false);
|
||||
struct CutsceneDef;
|
||||
struct MapRecord;
|
||||
struct SummaryInfo;
|
||||
bool StartCutscene(const char* s, int flags, const CompletionFunc& completion);
|
||||
void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic);
|
||||
void ShowScoreboard(int numplayers, const CompletionFunc& completion_);
|
||||
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_);
|
||||
|
|
|
@ -398,6 +398,12 @@ static TArray<GrpDefInfo> ParseGrpInfo(const char *fn, FileReader &fr, TMap<FStr
|
|||
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);
|
||||
FlagMap.Insert("GAMEFLAG_WITCHAVEN", GAMEFLAG_WH);
|
||||
FlagMap.Insert("GAMEFLAG_WITCHAVEN2", GAMEFLAG_WH2);
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "gamefuncs.h"
|
||||
#include "texturemanager.h"
|
||||
#include "earcut.hpp"
|
||||
#include "hw_sections.h"
|
||||
#include "nodebuilder/nodebuild.h"
|
||||
|
||||
SectorGeometry sectorGeometry;
|
||||
|
@ -217,8 +218,9 @@ public:
|
|||
|
||||
bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2& offset)
|
||||
{
|
||||
auto sec = §or[secnum];
|
||||
int numvertices = sec->wallnum;
|
||||
auto sec = §ions[secnum];
|
||||
auto sectorp = §or[sec->sector];
|
||||
int numvertices = sec->lines.Size();
|
||||
|
||||
TArray<FVector3> points(numvertices, true);
|
||||
using Point = std::pair<float, float>;
|
||||
|
@ -229,7 +231,7 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
curPoly = &polygon.back();
|
||||
FixedBitArray<MAXWALLSB> done;
|
||||
|
||||
int fz = sec->floorz, cz = sec->ceilingz;
|
||||
int fz = sectorp->floorz, cz = sectorp->ceilingz;
|
||||
|
||||
int vertstoadd = numvertices;
|
||||
|
||||
|
@ -243,7 +245,8 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
{
|
||||
while (!done[start])
|
||||
{
|
||||
auto wallp = &wall[sec->wallptr + start];
|
||||
auto sline = §ionLines[sec->lines[start]];
|
||||
auto wallp = &wall[sline->startpoint];
|
||||
float X = WallStartX(wallp);
|
||||
float Y = WallStartY(wallp);
|
||||
if (fabs(X) > 32768. || fabs(Y) > 32768.)
|
||||
|
@ -255,7 +258,7 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
curPoly->push_back(std::make_pair(X, Y));
|
||||
done.Set(start);
|
||||
vertstoadd--;
|
||||
start = wallp->point2 - sec->wallptr;
|
||||
start = sline->point2index;
|
||||
}
|
||||
polygon.resize(polygon.size() + 1);
|
||||
curPoly = &polygon.back();
|
||||
|
@ -280,12 +283,12 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
}
|
||||
if (outer != 0) std::swap(polygon[0], polygon[outer]);
|
||||
auto indices = mapbox::earcut(polygon);
|
||||
if (indices.size() < 3 * (sec->wallnum - 2))
|
||||
if (indices.size() < 3 * (sec->lines.Size() - 2))
|
||||
{
|
||||
// this means that full triangulation failed.
|
||||
return false;
|
||||
}
|
||||
sec->floorz = sec->ceilingz = 0;
|
||||
sectorp->floorz = sectorp->ceilingz = 0;
|
||||
|
||||
int p = 0;
|
||||
for (size_t a = 0; a < polygon.size(); a++)
|
||||
|
@ -293,7 +296,7 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
for (auto& pt : polygon[a])
|
||||
{
|
||||
float planez;
|
||||
PlanesAtPoint(sec, (pt.first * 16), (pt.second * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr);
|
||||
PlanesAtPoint(sectorp, (pt.first * 16), (pt.second * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr);
|
||||
FVector3 point = { pt.first, pt.second, planez };
|
||||
points[p++] = point;
|
||||
}
|
||||
|
@ -302,11 +305,11 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
auto& entry = data[secnum].planes[plane];
|
||||
entry.vertices.Resize(indices.size());
|
||||
entry.texcoords.Resize(indices.size());
|
||||
entry.normal = CalcNormal(sec, plane);
|
||||
entry.normal = CalcNormal(sectorp, plane);
|
||||
|
||||
auto texture = tileGetTexture(plane ? sec->ceilingpicnum : sec->floorpicnum);
|
||||
auto texture = tileGetTexture(plane ? sectorp->ceilingpicnum : sectorp->floorpicnum);
|
||||
|
||||
UVCalculator uvcalc(sec, plane, texture, offset);
|
||||
UVCalculator uvcalc(sectorp, plane, texture, offset);
|
||||
|
||||
for(unsigned i = 0; i < entry.vertices.Size(); i++)
|
||||
{
|
||||
|
@ -315,8 +318,8 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
entry.texcoords[i] = uvcalc.GetUV(int(pt.X * 16), int(pt.Y * -16), pt.Z);
|
||||
}
|
||||
|
||||
sec->floorz = fz;
|
||||
sec->ceilingz = cz;
|
||||
sectorp->floorz = fz;
|
||||
sectorp->ceilingz = cz;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -330,26 +333,30 @@ bool SectorGeometry::MakeVertices(unsigned int secnum, int plane, const FVector2
|
|||
|
||||
bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector2& offset)
|
||||
{
|
||||
auto sec = §ions[secnum];
|
||||
auto sectorp = §or[sec->sector];
|
||||
int numvertices = sec->lines.Size();
|
||||
|
||||
// Convert our sector into something the node builder understands
|
||||
auto sect = §or[secnum];
|
||||
TArray<vertex_t> vertexes(sect->wallnum, true);
|
||||
TArray<line_t> lines(sect->wallnum, true);
|
||||
TArray<side_t> sides(sect->wallnum, true);
|
||||
for (int i = 0; i < sect->wallnum; i++)
|
||||
TArray<vertex_t> vertexes(sectorp->wallnum, true);
|
||||
TArray<line_t> lines(numvertices, true);
|
||||
TArray<side_t> sides(numvertices, true);
|
||||
for (int i = 0; i < numvertices; i++)
|
||||
{
|
||||
auto wal = &wall[sect->wallptr + i];
|
||||
auto sline = §ionLines[sec->lines[i]];
|
||||
auto wal = &wall[sline->startpoint];
|
||||
vertexes[i].p = { wal->x * (1 / 16.), wal->y * (1 / -16.) };
|
||||
|
||||
lines[i].backsector = nullptr;
|
||||
lines[i].frontsector = sect;
|
||||
lines[i].frontsector = sectorp;
|
||||
lines[i].linenum = i;
|
||||
lines[i].sidedef[0] = &sides[i];
|
||||
lines[i].sidedef[1] = nullptr;
|
||||
lines[i].v1 = &vertexes[i];
|
||||
lines[i].v2 = &vertexes[wal->point2 - sect->wallptr];
|
||||
lines[i].v2 = &vertexes[sline->point2index];
|
||||
|
||||
sides[i].sidenum = i;
|
||||
sides[i].sector = sect;
|
||||
sides[i].sector = sectorp;
|
||||
}
|
||||
|
||||
|
||||
|
@ -372,8 +379,8 @@ bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector
|
|||
entry.vertices.Clear();
|
||||
entry.texcoords.Clear();
|
||||
|
||||
int fz = sect->floorz, cz = sect->ceilingz;
|
||||
sect->floorz = sect->ceilingz = 0;
|
||||
int fz = sectorp->floorz, cz = sectorp->ceilingz;
|
||||
sectorp->floorz = sectorp->ceilingz = 0;
|
||||
|
||||
for (auto& sub : Level.subsectors)
|
||||
{
|
||||
|
@ -391,9 +398,9 @@ bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector
|
|||
}
|
||||
|
||||
// calculate the rest.
|
||||
auto texture = tileGetTexture(plane ? sect->ceilingpicnum : sect->floorpicnum);
|
||||
auto texture = tileGetTexture(plane ? sectorp->ceilingpicnum : sectorp->floorpicnum);
|
||||
|
||||
UVCalculator uvcalc(sect, plane, texture, offset);
|
||||
UVCalculator uvcalc(sectorp, plane, texture, offset);
|
||||
|
||||
entry.texcoords.Resize(entry.vertices.Size());
|
||||
for (unsigned i = 0; i < entry.vertices.Size(); i++)
|
||||
|
@ -401,13 +408,13 @@ bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector
|
|||
auto& pt = entry.vertices[i];
|
||||
|
||||
float planez;
|
||||
PlanesAtPoint(sect, (pt.X * 16), (pt.Y * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr);
|
||||
PlanesAtPoint(sectorp, (pt.X * 16), (pt.Y * -16), plane ? &planez : nullptr, !plane ? &planez : nullptr);
|
||||
entry.vertices[i].Z = planez;
|
||||
entry.texcoords[i] = uvcalc.GetUV(int(pt.X * 16.), int(pt.Y * -16.), pt.Z);
|
||||
}
|
||||
entry.normal = CalcNormal(sect, plane);
|
||||
sect->floorz = fz;
|
||||
sect->ceilingz = cz;
|
||||
entry.normal = CalcNormal(sectorp, plane);
|
||||
sectorp->floorz = fz;
|
||||
sectorp->ceilingz = cz;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -419,7 +426,8 @@ bool SectorGeometry::MakeVertices2(unsigned int secnum, int plane, const FVector
|
|||
|
||||
void SectorGeometry::ValidateSector(unsigned int secnum, int plane, const FVector2& offset)
|
||||
{
|
||||
auto sec = §or[secnum];
|
||||
auto sec = §or[sections[secnum].sector];
|
||||
|
||||
auto compare = &data[secnum].compare[plane];
|
||||
if (plane == 0)
|
||||
{
|
||||
|
|
|
@ -136,7 +136,7 @@ void ST_Clear();
|
|||
extern FGameTexture *CrosshairImage;
|
||||
|
||||
|
||||
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt = 0);
|
||||
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt = 0, int style = STYLE_Translucent);
|
||||
void setViewport(int viewSize);
|
||||
struct MapRecord;
|
||||
void setLevelStarted(MapRecord *);
|
||||
|
|
|
@ -240,7 +240,9 @@ void DBaseStatusBar::PrintAutomapInfo(FLevelStats& stats, bool forcetextfont)
|
|||
{
|
||||
y = 200 - stats.screenbottomspace - spacing;
|
||||
}
|
||||
const auto &volname = gVolumeNames[volfromlevelnum(lev->levelNumber)];
|
||||
auto cluster = FindCluster(lev->cluster);
|
||||
FString volname;
|
||||
if (cluster) volname = cluster->name;
|
||||
if (volname.IsEmpty() && am_nameontop) y = 1;
|
||||
|
||||
DrawText(twod, stats.font, stats.standardColor, 2 * hud_statscale, y, mapname, DTA_FullscreenScale, FSMode_ScaleToHeight, DTA_VirtualWidth, 320, DTA_VirtualHeight, 200,
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "src/callback.cpp"
|
||||
#include "src/choke.cpp"
|
||||
#include "src/controls.cpp"
|
||||
#include "src/credits.cpp"
|
||||
#include "src/db.cpp"
|
||||
#include "src/dude.cpp"
|
||||
#include "src/endgame.cpp"
|
||||
|
|
|
@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "v_draw.h"
|
||||
#include "texturemanager.h"
|
||||
#include "statusbar.h"
|
||||
#include "vm.h"
|
||||
|
||||
BEGIN_BLD_NS
|
||||
|
||||
|
@ -79,7 +80,7 @@ void EndLevel(void)
|
|||
seqKillAll();
|
||||
}
|
||||
|
||||
void StartLevel(MapRecord* level)
|
||||
void StartLevel(MapRecord* level, bool newgame)
|
||||
{
|
||||
if (!level) return;
|
||||
gFrameCount = 0;
|
||||
|
@ -96,14 +97,14 @@ void StartLevel(MapRecord* level)
|
|||
///////
|
||||
}
|
||||
#if 0
|
||||
else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel))
|
||||
else if (gGameOptions.nGameType > 0 && newgame)
|
||||
{
|
||||
// todo
|
||||
gBlueFlagDropped = false;
|
||||
gRedFlagDropped = false;
|
||||
}
|
||||
#endif
|
||||
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
|
||||
if (!newgame)
|
||||
{
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
|
@ -180,13 +181,13 @@ void StartLevel(MapRecord* level)
|
|||
evInit();
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
if (!(gGameOptions.uGameFlags & GF_AdvanceLevel))
|
||||
if (newgame)
|
||||
{
|
||||
playerInit(i, 0);
|
||||
}
|
||||
playerStart(i, 1);
|
||||
}
|
||||
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
|
||||
if (!newgame)
|
||||
{
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
|
@ -203,7 +204,6 @@ void StartLevel(MapRecord* level)
|
|||
pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon;
|
||||
}
|
||||
}
|
||||
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
|
||||
PreloadCache();
|
||||
InitMirrors();
|
||||
trInit();
|
||||
|
@ -222,43 +222,24 @@ void StartLevel(MapRecord* level)
|
|||
}
|
||||
|
||||
|
||||
void NewLevel(MapRecord *sng, int skill)
|
||||
void NewLevel(MapRecord *sng, int skill, bool newgame)
|
||||
{
|
||||
auto completion = [=](bool = false)
|
||||
{
|
||||
if (skill != -1) gGameOptions.nDifficulty = skill;
|
||||
gSkill = gGameOptions.nDifficulty;
|
||||
StartLevel(sng);
|
||||
gameaction = ga_level;
|
||||
};
|
||||
|
||||
bool startedCutscene = false;
|
||||
if (!(sng->flags & MI_USERMAP))
|
||||
{
|
||||
int episode = volfromlevelnum(sng->levelNumber);
|
||||
int level = mapfromlevelnum(sng->levelNumber);
|
||||
if (gEpisodeInfo[episode].cutALevel == level && gEpisodeInfo[episode].cutsceneAName[0])
|
||||
{
|
||||
levelPlayIntroScene(episode, completion);
|
||||
startedCutscene = true;
|
||||
}
|
||||
|
||||
}
|
||||
if (!startedCutscene) completion(false);
|
||||
|
||||
if (skill != -1) gGameOptions.nDifficulty = skill;
|
||||
gSkill = gGameOptions.nDifficulty;
|
||||
StartLevel(sng, newgame);
|
||||
gameaction = ga_level;
|
||||
}
|
||||
|
||||
void GameInterface::NewGame(MapRecord *sng, int skill, bool)
|
||||
{
|
||||
gGameOptions.uGameFlags = 0;
|
||||
cheatReset();
|
||||
NewLevel(sng, skill);
|
||||
NewLevel(sng, skill, true);
|
||||
}
|
||||
|
||||
void GameInterface::NextLevel(MapRecord *map, int skill)
|
||||
{
|
||||
gGameOptions.uGameFlags = GF_AdvanceLevel;
|
||||
NewLevel(map, skill);
|
||||
NewLevel(map, skill, false);
|
||||
}
|
||||
|
||||
void GameInterface::Ticker()
|
||||
|
@ -328,40 +309,12 @@ void GameInterface::Ticker()
|
|||
team_ticker[i] = 0;
|
||||
}
|
||||
|
||||
if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0)
|
||||
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
|
||||
{
|
||||
gGameOptions.uGameFlags &= ~GF_AdvanceLevel;
|
||||
seqKillAll();
|
||||
if (gGameOptions.uGameFlags & GF_EndGame)
|
||||
{
|
||||
STAT_Update(true);
|
||||
if (gGameOptions.nGameType == 0)
|
||||
{
|
||||
auto completion = [](bool) {
|
||||
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
|
||||
gameaction = ga_creditsmenu;
|
||||
};
|
||||
|
||||
if (gGameOptions.uGameFlags & GF_PlayCutscene)
|
||||
{
|
||||
levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion);
|
||||
}
|
||||
else completion(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
STAT_Update(false);
|
||||
EndLevel();
|
||||
Mus_Stop();
|
||||
// Fixme: Link maps, not episode/level pairs.
|
||||
int ep = volfromlevelnum(currentLevel->levelNumber);
|
||||
auto map = FindMapByLevelNum(levelnum(ep, gNextLevel));
|
||||
CompleteLevel(map);
|
||||
}
|
||||
STAT_Update(gNextLevel == nullptr);
|
||||
CompleteLevel(gNextLevel);
|
||||
}
|
||||
r_NoInterpolate = false;
|
||||
}
|
||||
|
@ -471,11 +424,12 @@ void GameInterface::app_init()
|
|||
|
||||
levelLoadDefaults();
|
||||
LoadDefinitions();
|
||||
|
||||
//---------
|
||||
SetTileNames();
|
||||
C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25);
|
||||
|
||||
TileFiles.SetBackup();
|
||||
powerupInit();
|
||||
Printf(PRINT_NONOTIFY, "Loading cosine table\n");
|
||||
trigInit();
|
||||
Printf(PRINT_NONOTIFY, "Initializing view subsystem\n");
|
||||
|
@ -485,15 +439,15 @@ void GameInterface::app_init()
|
|||
Printf(PRINT_NONOTIFY, "Initializing weapon animations\n");
|
||||
WeaponInit();
|
||||
|
||||
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
|
||||
sndInit();
|
||||
|
||||
myconnectindex = connecthead = 0;
|
||||
gNetPlayers = numplayers = 1;
|
||||
connectpoint2[0] = -1;
|
||||
gGameOptions.nGameType = 0;
|
||||
UpdateNetworkMenus();
|
||||
|
||||
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
|
||||
sndInit();
|
||||
|
||||
gChoke.init(518, chokeCallback);
|
||||
UpdateDacs(0, true);
|
||||
|
||||
|
@ -518,17 +472,7 @@ static void gameInit()
|
|||
void GameInterface::Startup()
|
||||
{
|
||||
gameInit();
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
|
||||
else
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
}
|
||||
}
|
||||
PlayLogos(ga_mainmenu, ga_mainmenu, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -578,4 +522,48 @@ ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize)
|
|||
return new GameInterface;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
kLoadScreenCRC = -2051908571,
|
||||
kLoadScreenWideBackWidth = 256,
|
||||
kLoadScreenWideSideWidth = 128,
|
||||
|
||||
};
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen)
|
||||
{
|
||||
static int bLoadScreenCrcMatch = -1;
|
||||
if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
|
||||
ACTION_RETURN_INT(bLoadScreenCrcMatch);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, PlayIntroMusic)
|
||||
{
|
||||
Mus_Play(nullptr, "PESTIS.MID", false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, sndStartSample)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(id);
|
||||
PARAM_INT(vol);
|
||||
PARAM_INT(chan);
|
||||
PARAM_BOOL(looped);
|
||||
PARAM_INT(chanflags);
|
||||
sndStartSample(id, vol, chan, looped, EChanFlags::FromInt(chanflags));
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Blood, sndStartSampleNamed)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_STRING(id);
|
||||
PARAM_INT(vol);
|
||||
PARAM_INT(chan);
|
||||
sndStartSample(id, vol, chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
END_BLD_NS
|
||||
|
|
|
@ -77,7 +77,6 @@ extern int blood_globalflags;
|
|||
|
||||
void QuitGame(void);
|
||||
void PreloadCache(void);
|
||||
void StartLevel(MapRecord *gameOptions);
|
||||
void ProcessFrame(void);
|
||||
void ScanINIFiles(void);
|
||||
void EndLevel();
|
||||
|
@ -121,7 +120,6 @@ struct GameInterface : ::GameInterface
|
|||
void MenuOpened() override;
|
||||
void MenuClosed() override;
|
||||
bool CanSave() override;
|
||||
bool StartGame(FNewGameStartup& gs) override;
|
||||
void QuitToTitle() override;
|
||||
FString GetCoordString() override;
|
||||
ReservedSpace GetReservedScreenSpace(int viewsize) override;
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
//-------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
||||
Copyright (C) 2019 Nuke.YKT
|
||||
|
||||
This file is part of NBlood.
|
||||
|
||||
NBlood is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
#include "ns.h" // Must come before everything else!
|
||||
|
||||
#include "build.h"
|
||||
#include "compat.h"
|
||||
#include "SmackerDecoder.h"
|
||||
#include "blood.h"
|
||||
#include "animtexture.h"
|
||||
#include "raze_sound.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "screenjob.h"
|
||||
#include "gamestate.h"
|
||||
#include "razemenu.h"
|
||||
|
||||
BEGIN_BLD_NS
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void playlogos()
|
||||
{
|
||||
JobDesc jobs[6];
|
||||
int job = 0;
|
||||
static AnimSound logosound[] =
|
||||
{
|
||||
{ 1, -1 },
|
||||
{ -1, -1 },
|
||||
{ 1, -1 },
|
||||
{ -1,-1 }
|
||||
};
|
||||
|
||||
if (logosound[0].soundnum == -1)
|
||||
{
|
||||
logosound[0].soundnum = S_FindSound("logo.wav");
|
||||
logosound[2].soundnum = S_FindSound("gt.wav");
|
||||
}
|
||||
|
||||
|
||||
if (!userConfig.nologo)
|
||||
{
|
||||
if (fileSystem.FindFile("logo.smk") != -1)
|
||||
{
|
||||
jobs[job++] = { PlayVideo("logo.smk", &logosound[0], 0) };
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
|
||||
jobs[job++] = { Create<DImageScreen>(2050) };
|
||||
}
|
||||
if (fileSystem.FindFile("gti.smk") != -1)
|
||||
{
|
||||
jobs[job++] = { PlayVideo("gti.smk", &logosound[2], 0) };
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs[job++] = { Create<DBlackScreen>(1), []() { sndStartSample("THUNDER2", 128, -1); } };
|
||||
jobs[job++] = { Create<DImageScreen>(2052) };
|
||||
}
|
||||
}
|
||||
jobs[job++] = { Create<DBlackScreen>(1), []() { sndPlaySpecialMusicOrNothing(MUS_INTRO); sndStartSample("THUNDER2", 128, -1); }};
|
||||
jobs[job++] = { Create<DImageScreen>(2518, DScreenJob::fadein) };
|
||||
|
||||
RunScreenJob(jobs, job, [](bool) {
|
||||
Mus_Stop();
|
||||
gameaction = ga_mainmenu;
|
||||
}, true, true);
|
||||
}
|
||||
|
||||
void playSmk(const char *smk, const char *wav, int wavid, CompletionFunc func)
|
||||
{
|
||||
JobDesc jobs{};
|
||||
static AnimSound smksound[] =
|
||||
{
|
||||
{ 1, -1 },
|
||||
{ -1, -1 },
|
||||
};
|
||||
int id = S_FindSoundByResID(wavid);
|
||||
if (id <= 0)
|
||||
{
|
||||
FString wavv = wav;
|
||||
FixPathSeperator(wavv);
|
||||
id = S_FindSound(wavv);
|
||||
// Strip the drive letter and retry.
|
||||
if (id <= 0 && wavv.Len() > 3 && wavv[1] == ':' && isalpha(wavv[0]) && wavv[2] == '/')
|
||||
{
|
||||
id = S_FindSound(wavv.GetChars() + 3);
|
||||
}
|
||||
}
|
||||
FString smkk = smk;
|
||||
FixPathSeperator(smkk);
|
||||
smksound[0].soundnum = id;
|
||||
jobs.job = PlayVideo(smkk, smksound, nullptr);
|
||||
RunScreenJob(&jobs, 1, func);
|
||||
}
|
||||
|
||||
void levelPlayIntroScene(int nEpisode, CompletionFunc completion)
|
||||
{
|
||||
Mus_Stop();
|
||||
sndKillAllSounds();
|
||||
sfxKillAllSounds();
|
||||
ambKillAll();
|
||||
seqKillAll();
|
||||
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
|
||||
playSmk(pEpisode->cutsceneAName, pEpisode->cutsceneASound, pEpisode->at9028, completion);
|
||||
}
|
||||
|
||||
void levelPlayEndScene(int nEpisode, CompletionFunc completion)
|
||||
{
|
||||
gGameOptions.uGameFlags &= ~GF_PlayCutscene;
|
||||
Mus_Stop();
|
||||
sndKillAllSounds();
|
||||
sfxKillAllSounds();
|
||||
ambKillAll();
|
||||
seqKillAll();
|
||||
EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode];
|
||||
playSmk(pEpisode->cutsceneBName, pEpisode->cutsceneBSound, pEpisode->at902c, completion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
END_BLD_NS
|
|
@ -159,23 +159,6 @@ bool GameInterface::CanSave()
|
|||
return (gamestate == GS_LEVEL && gPlayer[myconnectindex].pXSprite->health != 0);
|
||||
}
|
||||
|
||||
bool GameInterface::StartGame(FNewGameStartup& gs)
|
||||
{
|
||||
if (gs.Episode >= 1)
|
||||
{
|
||||
if (g_gameType & GAMEFLAG_SHAREWARE)
|
||||
{
|
||||
M_StartMessage(GStrings("BUYBLOOD"), 1, NAME_None); // unreachable because we do not support Blood SW versions yet.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sfxKillAllSounds();
|
||||
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
|
||||
DeferedStartGame(map, gs.Skill);
|
||||
return true;
|
||||
}
|
||||
|
||||
FSavegameInfo GameInterface::GetSaveSig()
|
||||
{
|
||||
return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD };
|
||||
|
|
|
@ -32,6 +32,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "automap.h"
|
||||
#include "raze_sound.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "hw_sections.h"
|
||||
#include "sectorgeometry.h"
|
||||
|
||||
#include "blood.h"
|
||||
|
||||
|
@ -1067,6 +1069,8 @@ void dbLoadMap(const char *pPath, int *pX, int *pY, int *pZ, short *pAngle, shor
|
|||
}
|
||||
|
||||
setWallSectors();
|
||||
hw_BuildSections();
|
||||
sectorGeometry.SetSize(numsections);
|
||||
memcpy(wallbackup, wall, sizeof(wallbackup));
|
||||
memcpy(sectorbackup, sector, sizeof(sectorbackup));
|
||||
}
|
||||
|
|
|
@ -36,152 +36,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
BEGIN_BLD_NS
|
||||
|
||||
enum
|
||||
{
|
||||
kLoadScreenCRC = -2051908571,
|
||||
kLoadScreenWideBackWidth = 256,
|
||||
kLoadScreenWideSideWidth = 128,
|
||||
|
||||
};
|
||||
|
||||
static int bLoadScreenCrcMatch = -1;
|
||||
|
||||
static void drawTextScreenBackground(void)
|
||||
{
|
||||
if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
|
||||
|
||||
if (bLoadScreenCrcMatch)
|
||||
{
|
||||
if (ActiveRatio(twod->GetWidth(), twod->GetHeight()) < 1.34f)
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
int width = scale(twod->GetWidth(), 240, twod->GetHeight());
|
||||
int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth;
|
||||
for (int i = 0; i < nCount; i++)
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideBack), (i * kLoadScreenWideBackWidth), 0,
|
||||
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
|
||||
}
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideLeft), 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true, TAG_DONE);
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideRight), width - tileWidth(kLoadScreenWideRight), 0, DTA_TopLeft, true,
|
||||
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreenWideMiddle), (width - tileWidth(kLoadScreenWideMiddle)) / 2, 0, DTA_TopLeft, true,
|
||||
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
// One these screens get scriptified this should use the version in BloodMenuDelegate.
|
||||
static void DrawCaption(const char* text)
|
||||
{
|
||||
double scalex = 1.; // Expand the box if the text is longer
|
||||
int width = BigFont->StringWidth(text);
|
||||
int boxwidth = tileWidth(2038);
|
||||
if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10);
|
||||
|
||||
DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE);
|
||||
DrawText(twod, BigFont, CR_UNDEFINED, 160 - width / 2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE);
|
||||
}
|
||||
|
||||
|
||||
class DBloodSummaryScreen : public DSkippableScreenJob
|
||||
{
|
||||
void DrawKills(void)
|
||||
{
|
||||
char pBuffer[40];
|
||||
if (gGameOptions.nGameType == 0)
|
||||
{
|
||||
viewDrawText(1, FStringf("%s:", GStrings("KILLS")), 75, 50, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40,"%2d", gKillMgr.Kills);
|
||||
viewDrawText(1, pBuffer, 160, 50, -128, 0, 0, 1);
|
||||
viewDrawText(1, GStrings("OF"), 190, 50, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%2d", gKillMgr.TotalKills);
|
||||
viewDrawText(1, pBuffer, 220, 50, -128, 0, 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewDrawText(3, "#", 85, 35, -128, 0, 0, 1);
|
||||
viewDrawText(3, GStrings("NAME"), 100, 35, -128, 0, 0, 1);
|
||||
viewDrawText(3, GStrings("FRAGS"), 210, 35, -128, 0, 0, 1);
|
||||
int nStart = 0;
|
||||
int nEnd = kMaxPlayers;
|
||||
|
||||
for (int i = nStart; i < nEnd; i++) if (playeringame[i])
|
||||
{
|
||||
mysnprintf(pBuffer, 40, "%-2d", i);
|
||||
viewDrawText(3, pBuffer, 85, 50 + 8 * i, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%s", PlayerName(i));
|
||||
viewDrawText(3, pBuffer, 100, 50 + 8 * i, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%d", gPlayer[i].fragCount);
|
||||
viewDrawText(3, pBuffer, 210, 50 + 8 * i, -128, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawSecrets(void)
|
||||
{
|
||||
char pBuffer[40];
|
||||
viewDrawText(1, FStringf("%s:", GStrings("TXT_SECRETS")), 75, 70, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Founds);
|
||||
viewDrawText(1, pBuffer, 160, 70, -128, 0, 0, 1);
|
||||
viewDrawText(1, GStrings("OF"), 190, 70, -128, 0, 0, 1);
|
||||
mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Total);
|
||||
viewDrawText(1, pBuffer, 220, 70, -128, 0, 0, 1);
|
||||
if (gSecretMgr.Super > 0)
|
||||
viewDrawText(1, GStrings("TXT_SUPERSECRET"), 160, 100, -128, 2, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
drawTextScreenBackground();
|
||||
if (gGameOptions.nGameType == 0)
|
||||
{
|
||||
DrawCaption(GStrings("TXTB_LEVELSTATS"));
|
||||
if (bPlayerCheated)
|
||||
{
|
||||
auto text = GStrings("TXTB_CHEATED");
|
||||
int font = 3;
|
||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
viewDrawText(font, text, 160, 32, -128, 0, 1, font == 3);
|
||||
}
|
||||
DrawKills();
|
||||
DrawSecrets();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawCaption(GStrings("TXTB_FRAGSTATS"));
|
||||
DrawKills();
|
||||
}
|
||||
int myclock = ticks * 120 / GameTicRate;
|
||||
if ((myclock & 32))
|
||||
{
|
||||
auto text = GStrings("PRESSKEY");
|
||||
int font = 3;
|
||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
||||
{
|
||||
JobDesc job = { Create<DBloodSummaryScreen>() };
|
||||
sndStartSample(268, 128, -1, false, CHANF_UI);
|
||||
EndLevel();
|
||||
Mus_Stop();
|
||||
RunScreenJob(&job, 1, [=](bool)
|
||||
|
||||
SummaryInfo info{};
|
||||
|
||||
info.kills = gKillMgr.Kills;
|
||||
info.maxkills = gKillMgr.TotalKills;
|
||||
info.secrets = gSecretMgr.Founds;
|
||||
info.maxsecrets = gSecretMgr.Total;
|
||||
info.time = gSecretMgr.Super;
|
||||
info.endofgame = map == nullptr;
|
||||
|
||||
ShowIntermission(currentLevel, map, &info, [=](bool)
|
||||
{
|
||||
soundEngine->StopAllChannels();
|
||||
gameaction = ga_nextlevel;
|
||||
gameaction = map? ga_nextlevel : ga_creditsmenu;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -272,38 +146,4 @@ void SerializeGameStats(FSerializer& arc)
|
|||
CSecretMgr gSecretMgr;
|
||||
CKillMgr gKillMgr;
|
||||
|
||||
class DBloodLoadScreen : public DScreenJob
|
||||
{
|
||||
const char* pzLoadingScreenText1;
|
||||
MapRecord* rec;
|
||||
|
||||
public:
|
||||
DBloodLoadScreen(const char* caption, MapRecord* maprec) : DScreenJob(), rec(maprec)
|
||||
{
|
||||
if (gGameOptions.nGameType == 0) pzLoadingScreenText1 = GStrings("TXTB_LLEVEL");
|
||||
else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType));
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
drawTextScreenBackground();
|
||||
DrawCaption(pzLoadingScreenText1);
|
||||
viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1);
|
||||
|
||||
auto text = GStrings("TXTB_PLSWAIT");
|
||||
int font = 3;
|
||||
if (!SmallFont2->CanPrint(text)) font = 0;
|
||||
|
||||
viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3);
|
||||
}
|
||||
};
|
||||
|
||||
void loadscreen(const char *caption, MapRecord* rec, CompletionFunc func)
|
||||
{
|
||||
JobDesc job = { Create<DBloodLoadScreen>(caption, rec) };
|
||||
RunScreenJob(&job, 1, func);
|
||||
}
|
||||
|
||||
|
||||
END_BLD_NS
|
||||
|
|
|
@ -38,11 +38,8 @@ GAMEOPTIONS gSingleGameOptions = {
|
|||
0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200
|
||||
};
|
||||
|
||||
EPISODEINFO gEpisodeInfo[kMaxEpisodes+1];
|
||||
|
||||
int gSkill = 2;
|
||||
int gEpisodeCount;
|
||||
int gNextLevel; // fixme: let this contain a full level number.
|
||||
MapRecord* gNextLevel;
|
||||
|
||||
char BloodIniFile[BMAX_PATH] = "BLOOD.INI";
|
||||
bool bINIOverride = false;
|
||||
|
@ -94,22 +91,23 @@ void CheckKeyAbend(const char *pzSection, const char *pzKey)
|
|||
}
|
||||
|
||||
|
||||
void levelLoadMapInfo(IniFile *pIni, MapRecord *pLevelInfo, const char *pzSection, int epinum, int mapnum)
|
||||
|
||||
void levelLoadMapInfo(IniFile* pIni, MapRecord* pLevelInfo, const char* pzSection, int epinum, int mapnum, int* nextmap, int* nextsecret)
|
||||
{
|
||||
char buffer[16];
|
||||
pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName));
|
||||
pLevelInfo->author = pIni->GetKeyString(pzSection, "Author", "");
|
||||
pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", "");
|
||||
pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid");
|
||||
pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1);
|
||||
pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1);
|
||||
pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1);
|
||||
*nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0);
|
||||
*nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0);
|
||||
pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0);
|
||||
pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0);
|
||||
for (int i = 0; i < kMaxMessages; i++)
|
||||
{
|
||||
sprintf(buffer, "Message%d", i+1);
|
||||
auto msg = pIni->GetKeyString(pzSection, buffer, "");
|
||||
pLevelInfo->AddMessage(i, msg);
|
||||
sprintf(buffer, "Message%d", i + 1);
|
||||
auto msg = pIni->GetKeyString(pzSection, buffer, "");
|
||||
pLevelInfo->AddMessage(i, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,108 +140,101 @@ static const char* DefFile(void)
|
|||
return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini";
|
||||
}
|
||||
|
||||
static FString cleanPath(const char* pth)
|
||||
{
|
||||
FString path = pth;
|
||||
FixPathSeperator(path);
|
||||
if (FileExists(path)) return path;
|
||||
if (path.Len() > 3 && path[1] == ':' && isalpha(path[0]) && path[2] == '/')
|
||||
{
|
||||
return path.Mid(3);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void levelLoadDefaults(void)
|
||||
{
|
||||
char buffer[64];
|
||||
char buffer2[16];
|
||||
|
||||
int cutALevel = 0;
|
||||
|
||||
levelInitINI(DefFile());
|
||||
memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo));
|
||||
quoteMgr.InitializeQuote(MUS_INTRO, "PESTIS.MID");
|
||||
int i;
|
||||
for (i = 0; i < kMaxEpisodes; i++)
|
||||
for (i = 1; i <= kMaxEpisodes; i++)
|
||||
{
|
||||
sprintf(buffer, "Episode%d", i+1);
|
||||
sprintf(buffer, "Episode%d", i);
|
||||
if (!BloodINI->SectionExists(buffer))
|
||||
break;
|
||||
EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i];
|
||||
auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer);
|
||||
gVolumeNames[i] = ep_str; // only keep one table for the names. Todo: Consolidate this across games.
|
||||
strncpy(pEpisodeInfo->cutsceneAName, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH);
|
||||
pEpisodeInfo->at9028 = BloodINI->GetKeyInt(buffer, "CutWavA", -1);
|
||||
if (pEpisodeInfo->at9028 == 0)
|
||||
strncpy(pEpisodeInfo->cutsceneASound, BloodINI->GetKeyString(buffer, "CutWavA", ""), BMAX_PATH);
|
||||
else
|
||||
pEpisodeInfo->cutsceneASound[0] = 0;
|
||||
strncpy(pEpisodeInfo->cutsceneBName, BloodINI->GetKeyString(buffer, "CutSceneB", ""), BMAX_PATH);
|
||||
pEpisodeInfo->at902c = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
|
||||
if (pEpisodeInfo->at902c == 0)
|
||||
strncpy(pEpisodeInfo->cutsceneBSound, BloodINI->GetKeyString(buffer, "CutWavB", ""), BMAX_PATH);
|
||||
else
|
||||
pEpisodeInfo->cutsceneBSound[0] = 0;
|
||||
auto cluster = MustFindCluster(i);
|
||||
auto volume = MustFindVolume(i);
|
||||
CutsceneDef &csB = cluster->outro;
|
||||
auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer);
|
||||
cluster->name = volume->name = ep_str;
|
||||
if (i > 1) volume->flags |= VF_SHAREWARELOCK;
|
||||
|
||||
pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
|
||||
pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
|
||||
if (pEpisodeInfo->cutALevel > 0)
|
||||
pEpisodeInfo->cutALevel--;
|
||||
int j;
|
||||
for (j = 0; j < kMaxLevels; j++)
|
||||
csB.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneB", ""));
|
||||
int soundint = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
|
||||
if (soundint > 0) csB.soundID = soundint + 0x40000000;
|
||||
else csB.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", ""));
|
||||
|
||||
//pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
|
||||
cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
|
||||
if (cutALevel < 1) cutALevel = 1;
|
||||
|
||||
int nextmaps[kMaxLevels]{}, nextsecrets[kMaxLevels]{};
|
||||
for (int j = 1; j <= kMaxLevels; j++)
|
||||
{
|
||||
sprintf(buffer2, "Map%d", j+1);
|
||||
sprintf(buffer2, "Map%d", j);
|
||||
if (!BloodINI->KeyExists(buffer, buffer2))
|
||||
break;
|
||||
auto pLevelInfo = AllocateMap();
|
||||
const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL);
|
||||
CheckSectionAbend(pMap);
|
||||
pLevelInfo->levelNumber = levelnum(i, j);
|
||||
SetLevelNum(pLevelInfo, makelevelnum(i, j));
|
||||
pLevelInfo->cluster = i;
|
||||
pLevelInfo->mapindex = j;
|
||||
pLevelInfo->labelName = pMap;
|
||||
if (j == 1) volume->startmap = pLevelInfo->labelName;
|
||||
pLevelInfo->fileName.Format("%s.map", pMap);
|
||||
levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j);
|
||||
levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j, &nextmaps[j - 1], &nextsecrets[j - 1]);
|
||||
if (j == cutALevel)
|
||||
{
|
||||
CutsceneDef& csA = pLevelInfo->intro;
|
||||
csA.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneA", ""));
|
||||
int soundint = BloodINI->GetKeyInt(buffer, "CutWavA", -1);
|
||||
if (soundint > 0) csA.soundID = soundint + 0x40000000;
|
||||
else csA.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", ""));
|
||||
}
|
||||
}
|
||||
// Now resolve the level links
|
||||
for (int j = 1; j <= kMaxLevels; j++)
|
||||
{
|
||||
auto map = FindMapByIndexOnly(i, j);
|
||||
if (map)
|
||||
{
|
||||
if (nextmaps[j - 1] > 0)
|
||||
{
|
||||
auto nmap = FindMapByIndexOnly(i, nextmaps[j - 1]);
|
||||
if (nmap) map->NextMap = nmap->labelName;
|
||||
else map->NextMap = "-";
|
||||
}
|
||||
if (nextsecrets[j - 1] > 0)
|
||||
{
|
||||
auto nmap = FindMapByIndexOnly(i, nextsecrets[j - 1]);
|
||||
if (nmap) map->NextSecret = nmap->labelName;
|
||||
else map->NextSecret = "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
pEpisodeInfo->nLevels = j;
|
||||
}
|
||||
gEpisodeCount = i;
|
||||
}
|
||||
|
||||
void levelGetNextLevels(int *pnEndingA, int *pnEndingB)
|
||||
void levelEndLevel(int secret)
|
||||
{
|
||||
assert(pnEndingA != NULL && pnEndingB != NULL);
|
||||
int nEndingA = currentLevel->nextLevel;
|
||||
if (nEndingA >= 0)
|
||||
nEndingA--;
|
||||
int nEndingB = currentLevel->nextSecret;
|
||||
if (nEndingB >= 0)
|
||||
nEndingB--;
|
||||
*pnEndingA = nEndingA;
|
||||
*pnEndingB = nEndingB;
|
||||
}
|
||||
|
||||
void levelEndLevel(int arg)
|
||||
{
|
||||
int nEndingA, nEndingB;
|
||||
auto episode = volfromlevelnum(currentLevel->levelNumber);
|
||||
EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[episode];
|
||||
gGameOptions.uGameFlags |= GF_AdvanceLevel;
|
||||
levelGetNextLevels(&nEndingA, &nEndingB);
|
||||
switch (arg)
|
||||
{
|
||||
case 0:
|
||||
if (nEndingA == -1)
|
||||
{
|
||||
if (pEpisodeInfo->cutsceneBName[0])
|
||||
gGameOptions.uGameFlags |= GF_PlayCutscene;
|
||||
gGameOptions.uGameFlags |= GF_EndGame;
|
||||
}
|
||||
else
|
||||
gNextLevel = nEndingA;
|
||||
break;
|
||||
case 1:
|
||||
if (nEndingB == -1)
|
||||
{
|
||||
if (episode + 1 < gEpisodeCount)
|
||||
{
|
||||
if (pEpisodeInfo->cutsceneBName[0])
|
||||
gGameOptions.uGameFlags |= GF_PlayCutscene;
|
||||
gGameOptions.uGameFlags |= GF_EndGame;
|
||||
}
|
||||
else
|
||||
{
|
||||
gGameOptions.uGameFlags |= GF_AdvanceLevel;
|
||||
}
|
||||
}
|
||||
else
|
||||
gNextLevel = nEndingB;
|
||||
break;
|
||||
}
|
||||
if (!secret) gNextLevel = FindNextMap(currentLevel);
|
||||
else gNextLevel = FindNextSecretMap(currentLevel);
|
||||
}
|
||||
|
||||
void levelTryPlayMusic()
|
||||
|
|
|
@ -41,7 +41,6 @@ enum
|
|||
enum EGameFlag
|
||||
{
|
||||
GF_AdvanceLevel = 1,
|
||||
GF_EndGame = 2,
|
||||
// 4 was for playing intro cutscenes but is no longer used.
|
||||
GF_PlayCutscene = 8,
|
||||
};
|
||||
|
@ -67,38 +66,16 @@ struct GAMEOPTIONS {
|
|||
|
||||
#pragma pack(pop)
|
||||
|
||||
enum {
|
||||
MUS_INTRO = 0,
|
||||
MUS_LOADING = 1,
|
||||
};
|
||||
|
||||
struct EPISODEINFO
|
||||
{
|
||||
int nLevels;
|
||||
unsigned int bloodbath : 1;
|
||||
unsigned int cutALevel : 4;
|
||||
char cutsceneAName[BMAX_PATH];
|
||||
char cutsceneBName[BMAX_PATH];
|
||||
int at9028;
|
||||
int at902c;
|
||||
char cutsceneASound[BMAX_PATH];
|
||||
char cutsceneBSound[BMAX_PATH];
|
||||
};
|
||||
|
||||
extern EPISODEINFO gEpisodeInfo[];
|
||||
extern GAMEOPTIONS gSingleGameOptions;
|
||||
extern GAMEOPTIONS gGameOptions;
|
||||
extern int gSkill;
|
||||
extern char BloodIniFile[];
|
||||
extern bool bINIOverride;
|
||||
extern int gEpisodeCount;
|
||||
extern int gNextLevel;
|
||||
extern MapRecord* gNextLevel;
|
||||
extern bool gGameStarted;
|
||||
|
||||
void levelInitINI(const char *pzIni);
|
||||
void levelOverrideINI(const char *pzIni);
|
||||
void levelPlayIntroScene(int nEpisode, CompletionFunc completion);
|
||||
void levelPlayEndScene(int nEpisode, CompletionFunc completion);
|
||||
void levelSetupSecret(int nCount);
|
||||
void levelTriggerSecret(int nSecret);
|
||||
void CheckSectionAbend(const char *pzSection);
|
||||
|
|
|
@ -641,7 +641,6 @@ void SerializeState(FSerializer& arc)
|
|||
("modern", gModernMap)
|
||||
#endif
|
||||
("cheating", bPlayerCheated)
|
||||
("nextlevel", gNextLevel)
|
||||
("skyhoriz", pSky->horizfrac)
|
||||
("skyy", pSky->yoffs)
|
||||
("scale", pSky->yscale)
|
||||
|
|
|
@ -267,10 +267,8 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2)
|
|||
{
|
||||
if (!nArg1 || !nArg2 || strlen(pzArgs) < 3)
|
||||
return -1;
|
||||
*nArg1 = pzArgs[0] - '0' - 1;
|
||||
*nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1;
|
||||
*nArg1 = ClipRange(*nArg1, 0, gEpisodeCount-1);
|
||||
*nArg2 = ClipRange(*nArg2, 0, gEpisodeInfo[*nArg1].nLevels-1);
|
||||
*nArg1 = pzArgs[0] - '0';
|
||||
*nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0');
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
@ -423,7 +421,7 @@ static bool cheatMario(cheatseq_t* c)
|
|||
int nEpisode, nLevel;
|
||||
if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2)
|
||||
{
|
||||
auto map = FindMapByLevelNum(levelnum(nEpisode, nLevel));
|
||||
auto map = FindMapByIndex(nEpisode, nLevel);
|
||||
if (map) DeferedStartGame(map, -1);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// names for everything that gets accessed by scripts.
|
||||
x(MENUBAR, 2038)
|
||||
x(BackTile, 253)
|
||||
x(Monolithscreen, 2050)
|
||||
x(GTIScreen, 2052)
|
||||
x(TitleScreen, 2518)
|
||||
|
||||
x(CrosshairTile, 2319)
|
||||
x(LoadScreen, 2049)
|
||||
|
|
|
@ -5214,13 +5214,7 @@ void seqSpawnerOffSameTx(XSPRITE* pXSource) {
|
|||
void levelEndLevelCustom(int nLevel) {
|
||||
|
||||
gGameOptions.uGameFlags |= GF_AdvanceLevel;
|
||||
|
||||
if (nLevel >= 16 || nLevel < 0) {
|
||||
gGameOptions.uGameFlags |= GF_EndGame;
|
||||
return;
|
||||
}
|
||||
|
||||
gNextLevel = nLevel;
|
||||
gNextLevel = FindMapByIndex(currentLevel->cluster, nLevel + 1);
|
||||
}
|
||||
|
||||
void callbackUniMissileBurst(int nSprite) // 22
|
||||
|
|
|
@ -400,10 +400,6 @@ void powerupClear(PLAYER *pPlayer)
|
|||
}
|
||||
}
|
||||
|
||||
void powerupInit(void)
|
||||
{
|
||||
}
|
||||
|
||||
int packItemToPowerup(int nPack)
|
||||
{
|
||||
int nPowerUp = -1;
|
||||
|
|
|
@ -241,7 +241,6 @@ void powerupDeactivate(PLAYER *pPlayer, int nPowerUp);
|
|||
void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState);
|
||||
void powerupProcess(PLAYER *pPlayer);
|
||||
void powerupClear(PLAYER *pPlayer);
|
||||
void powerupInit(void);
|
||||
int packItemToPowerup(int nPack);
|
||||
int powerupToPackItem(int nPowerUp);
|
||||
char packAddItem(PLAYER *pPlayer, unsigned int nPack);
|
||||
|
|
|
@ -130,7 +130,7 @@ private:
|
|||
int flags = align | ((nStat & RS_CENTERBOTTOM)? DI_ITEM_CENTER_BOTTOM : (nStat & RS_TOPLEFT)? DI_ITEM_LEFT_TOP : DI_ITEM_RELCENTER);
|
||||
double alpha = 1.;
|
||||
double scale = nScale / 65536.;
|
||||
DrawGraphic(tileGetTexture(nTile, true), x, y, flags, alpha, -1, -1, scale, scale, shadeToLight(nShade), TRANSLATION(Translation_Remap, nPalette), style);
|
||||
DrawGraphic(tileGetTexture(nTile, true), x, y, flags, alpha, -1, -1, scale, scale, STYLE_Translucent, shadeToLight(nShade), TRANSLATION(Translation_Remap, nPalette), style);
|
||||
}
|
||||
void DrawStatMaskedSprite(int nTile, double x, double y, int nShade = 0, int nPalette = 0, unsigned int nStat = 0, int nScale = 65536, int align = DI_SCREEN_AUTO)
|
||||
{
|
||||
|
@ -197,7 +197,7 @@ private:
|
|||
int bx = scale(MulScale(w, nScale, 16), nMult, nDiv) + x;
|
||||
double scale = double(bx - x) / w;
|
||||
double sc = nScale / 65536.;
|
||||
DrawGraphic(tileGetTexture(nTile, true), x, y, DI_ITEM_LEFT_TOP, 1., -1, -1, sc, sc, 0xffffffff, 0, STYLE_Translucent, scale);
|
||||
DrawGraphic(tileGetTexture(nTile, true), x, y, DI_ITEM_LEFT_TOP, 1., -1, -1, sc, sc, STYLE_Translucent, 0xffffffff, 0, scale);
|
||||
}
|
||||
|
||||
|
||||
|
@ -218,7 +218,7 @@ private:
|
|||
stats.font = SmallFont;
|
||||
stats.letterColor = CR_DARKRED;
|
||||
stats.standardColor = CR_DARKGRAY;
|
||||
stats.time = Scale(gFrameCount, 1000, kTicsPerSec);
|
||||
stats.time = gFrameCount / GameTicRate;
|
||||
|
||||
if (automapMode == am_full)
|
||||
{
|
||||
|
@ -248,6 +248,7 @@ private:
|
|||
stats.secrets = gSecretMgr.Founds;
|
||||
stats.supersecrets = gSecretMgr.Super;
|
||||
stats.maxsecrets = max(gSecretMgr.Founds, gSecretMgr.Total); // If we found more than there are, increase the total. Some levels have a bugged counter.
|
||||
stats.time = Scale(PlayClock, 1000, 120);
|
||||
|
||||
DBaseStatusBar::PrintLevelStats(stats);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -124,549 +124,5 @@ void InitFonts_r()
|
|||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// wrappers around DrawText to allow easier reuse of the old code.
|
||||
// The vertical displacements are to have the same positioning as with the original code.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void BigText(double x, double y, const char* text, int align, double alpha = 1.)
|
||||
{
|
||||
//x *= 2.2; y *= 2.64;
|
||||
if (align != -1)
|
||||
x -= BigFont->StringWidth(text) * (align == 0 ? 0.2 : 0.4);
|
||||
auto width = BigFont->StringWidth(text);
|
||||
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, 0.4, DTA_ScaleY, 0.4, DTA_Alpha, alpha, TAG_DONE);
|
||||
}
|
||||
|
||||
static void GameText(double x, double y, const char* t, int shade, int align = -1, int trans = 0)
|
||||
{
|
||||
x *= 2; y *= 2;
|
||||
if (align != -1)
|
||||
x -= SmallFont->StringWidth(t) * (align == 0 ? 0.5 : 1);
|
||||
DrawText(twod, SmallFont, CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE);
|
||||
}
|
||||
|
||||
static void MiniText(double x, double y, const char* t, int shade, int align = -1, int trans = 0)
|
||||
{
|
||||
x *= 2; y *= 2;
|
||||
if (align != -1)
|
||||
x -= SmallFont2->StringWidth(t) * (align == 0 ? 0.5 : 1);
|
||||
DrawText(twod, SmallFont2, CR_UNDEFINED, x, y, t, DTA_FullscreenScale, FSMode_Fit640x400, DTA_TranslationIndex, TRANSLATION(Translation_Remap, trans), DTA_Color, shadeToLight(shade), TAG_DONE);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void Logo_r(const CompletionFunc& completion)
|
||||
{
|
||||
Mus_Stop();
|
||||
FX_StopAllSounds(); // JBF 20031228
|
||||
|
||||
static const AnimSound introsound[] =
|
||||
{
|
||||
{ 1, 29+1 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
static const AnimSound rednecksound[] =
|
||||
{
|
||||
{ 1, 478+1 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
static const AnimSound xatrixsound[] =
|
||||
{
|
||||
{ 1, 479+1 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims
|
||||
|
||||
JobDesc jobs[3];
|
||||
int job = 0;
|
||||
|
||||
if (userConfig.nologo)
|
||||
{
|
||||
completion(false);
|
||||
return;
|
||||
}
|
||||
else if (!isRRRA())
|
||||
{
|
||||
jobs[job++] = { PlayVideo("rr_intro.anm", introsound, framespeed), nullptr };
|
||||
jobs[job++] = { PlayVideo("redneck.anm", rednecksound, framespeed), nullptr };
|
||||
jobs[job++] = { PlayVideo("xatlogo.anm", xatrixsound, framespeed), nullptr };
|
||||
}
|
||||
else
|
||||
{
|
||||
jobs[job++] = { PlayVideo("redint.mve"), nullptr };
|
||||
}
|
||||
RunScreenJob(jobs, job, completion, true, true);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void bonussequence_r(int num, JobDesc* jobs, int& job)
|
||||
{
|
||||
static const AnimSound turdmov[] =
|
||||
{
|
||||
{ 1, 82 + 1 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
static const AnimSound rr_outro[] =
|
||||
{
|
||||
{ 1, 35 + 1 },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims
|
||||
|
||||
Mus_Stop();
|
||||
FX_StopAllSounds();
|
||||
|
||||
switch (num)
|
||||
{
|
||||
case 0:
|
||||
jobs[job++] = { PlayVideo("turdmov.anm", turdmov, framespeed), nullptr };
|
||||
jobs[job++] = { Create<DImageScreen>(TENSCREEN), nullptr };
|
||||
break;
|
||||
|
||||
case 1:
|
||||
jobs[job++] = { PlayVideo("rr_outro.anm", rr_outro, framespeed), nullptr };
|
||||
jobs[job++] = { Create<DImageScreen>(TENSCREEN), nullptr };
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DRRMultiplayerBonusScreen : public DScreenJob
|
||||
{
|
||||
int playerswhenstarted;
|
||||
|
||||
public:
|
||||
DRRMultiplayerBonusScreen(int pws) : DScreenJob(fadein | fadeout)
|
||||
{
|
||||
playerswhenstarted = pws;
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
S_PlayBonusMusic();
|
||||
}
|
||||
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
char tempbuf[32];
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, tileGetTexture(MENUSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||
double scale = 0.36;
|
||||
DrawTexture(twod, tileGetTexture(INGAMEDUKETHREEDEE, true), 160, 34, DTA_FullscreenScale, FSMode_Fit320x200,
|
||||
DTA_CenterOffsetRel, true, DTA_ScaleX, scale, DTA_ScaleY, 0.36, TAG_DONE);
|
||||
|
||||
GameText(160, 58, GStrings("Multiplayer Totals"), 0, 0);
|
||||
GameText(160, 58 + 10, currentLevel->DisplayName(), 0, 0);
|
||||
GameText(160, 165, GStrings("Presskey"), 0, 0);
|
||||
|
||||
int t = 0;
|
||||
|
||||
MiniText(38, 80, GStrings("Name"), 0);
|
||||
MiniText(269 + 20, 80, GStrings("Kills"), 0, 1);
|
||||
|
||||
for (int i = 0; i < playerswhenstarted; i++)
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "%-4d", i + 1);
|
||||
MiniText(92 + (i * 23), 80, tempbuf, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < playerswhenstarted; i++)
|
||||
{
|
||||
int xfragtotal = 0;
|
||||
mysnprintf(tempbuf, 32, "%d", i + 1);
|
||||
|
||||
MiniText(30, 90 + t, tempbuf, 0);
|
||||
MiniText(38, 90 + t, PlayerName(i), 0, -1, ps[i].palookup);
|
||||
|
||||
for (int y = 0; y < playerswhenstarted; y++)
|
||||
{
|
||||
int frag = ps[i].frags[y];
|
||||
if (i == y)
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "%-4d", ps[y].fraggedself);
|
||||
MiniText(92 + (y * 23), 90 + t, tempbuf, 0);
|
||||
xfragtotal -= ps[y].fraggedself;
|
||||
}
|
||||
else
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "%-4d", frag);
|
||||
MiniText(92 + (y * 23), 90 + t, tempbuf, 0);
|
||||
xfragtotal += frag;
|
||||
}
|
||||
/*
|
||||
if (myconnectindex == connecthead)
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "stats %ld killed %ld %ld\n", i + 1, y + 1, frag);
|
||||
sendscore(tempbuf);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
mysnprintf(tempbuf, 32, "%-4d", xfragtotal);
|
||||
MiniText(101 + (8 * 23), 90 + t, tempbuf, 0);
|
||||
|
||||
t += 7;
|
||||
}
|
||||
|
||||
for (int y = 0; y < playerswhenstarted; y++)
|
||||
{
|
||||
int yfragtotal = 0;
|
||||
for (int i = 0; i < playerswhenstarted; i++)
|
||||
{
|
||||
if (i == y)
|
||||
yfragtotal += ps[i].fraggedself;
|
||||
int frag = ps[i].frags[y];
|
||||
yfragtotal += frag;
|
||||
}
|
||||
mysnprintf(tempbuf, 32, "%-4d", yfragtotal);
|
||||
MiniText(92 + (y * 23), 96 + (8 * 7), tempbuf, 0);
|
||||
}
|
||||
|
||||
MiniText(45, 96 + (8 * 7), GStrings("Deaths"), 0);
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DRRLevelSummaryScreen : public DScreenJob
|
||||
{
|
||||
const char* lastmapname;
|
||||
int gfx_offset;
|
||||
int displaystate = 0;
|
||||
int speech = -1;
|
||||
int exitSoundStart;
|
||||
|
||||
enum
|
||||
{
|
||||
printTimeText = 1,
|
||||
printTimeVal = 2,
|
||||
printKillsText = 4,
|
||||
printKillsVal = 8,
|
||||
printSecretsText = 16,
|
||||
printSecretsVal = 32,
|
||||
printStatsAll = 63,
|
||||
exitSound = 64,
|
||||
exitWait = 128,
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
DRRLevelSummaryScreen(bool dofadeout = true) : DScreenJob(dofadeout? (fadein | fadeout) : fadein)
|
||||
{
|
||||
if (currentLevel->flags & MI_USERMAP)
|
||||
gfx_offset = BONUSPIC01;
|
||||
else if (!isRRRA())
|
||||
gfx_offset = BONUSPIC01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13);
|
||||
else
|
||||
gfx_offset = LEVELMAP01 + clamp((currentLevel->levelNumber / 1000) * 7 + (currentLevel->levelNumber % 1000), 0, 13);
|
||||
|
||||
|
||||
lastmapname = currentLevel->DisplayName();
|
||||
}
|
||||
|
||||
void FormatTime(int time, char* tempbuf)
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "%02d:%02d", (time / (26 * 60)) % 60, (time / 26) % 60);
|
||||
}
|
||||
|
||||
bool OnEvent(event_t* ev) override
|
||||
{
|
||||
if (ev->type == EV_KeyDown && !specialKeyEvent(ev))
|
||||
{
|
||||
if ((displaystate & printStatsAll) != printStatsAll)
|
||||
{
|
||||
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||
displaystate = printStatsAll;
|
||||
}
|
||||
else if (!(displaystate & exitSound))
|
||||
{
|
||||
displaystate |= exitSound;
|
||||
exitSoundStart = ticks;
|
||||
S_PlaySound(425, CHAN_AUTO, CHANF_UI);
|
||||
speech = BONUS_SPEECH1 + (rand() & 3);
|
||||
S_PlaySound(speech, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
S_PlayBonusMusic();
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if ((displaystate & printStatsAll) != printStatsAll)
|
||||
{
|
||||
if (ticks == 15 * 3)
|
||||
{
|
||||
displaystate |= printTimeText;
|
||||
}
|
||||
else if (ticks == 15 * 4)
|
||||
{
|
||||
displaystate |= printTimeVal;
|
||||
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
else if (ticks == 15 * 6)
|
||||
{
|
||||
displaystate |= printKillsText;
|
||||
}
|
||||
else if (ticks == 15 * 7)
|
||||
{
|
||||
displaystate |= printKillsVal;
|
||||
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
else if (ticks == 15 * 9)
|
||||
{
|
||||
displaystate |= printSecretsText;
|
||||
}
|
||||
else if (ticks == 15 * 10)
|
||||
{
|
||||
displaystate |= printSecretsVal;
|
||||
S_PlaySound(404, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
}
|
||||
if (displaystate & exitSound)
|
||||
{
|
||||
if (ticks >= exitSoundStart + 60)
|
||||
{
|
||||
displaystate ^= exitSound | exitWait;
|
||||
}
|
||||
}
|
||||
if (displaystate & exitWait)
|
||||
{
|
||||
if (speech <= 0 || !S_CheckSoundPlaying(speech))
|
||||
state = finished;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintTime()
|
||||
{
|
||||
char tempbuf[32];
|
||||
BigText(30, 48, GStrings("TXT_YerTime"), -1);
|
||||
BigText(30, 64, GStrings("TXT_ParTime"), -1);
|
||||
BigText(30, 80, GStrings("TXT_XTRTIME"), -1);
|
||||
|
||||
if (displaystate & printTimeVal)
|
||||
{
|
||||
FormatTime(ps[myconnectindex].player_par, tempbuf);
|
||||
BigText(191, 48, tempbuf, -1);
|
||||
|
||||
FormatTime(currentLevel->parTime, tempbuf);
|
||||
BigText(191, 64, tempbuf, -1);
|
||||
|
||||
FormatTime(currentLevel->designerTime, tempbuf);
|
||||
BigText(191, 80, tempbuf, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintKills()
|
||||
{
|
||||
char tempbuf[32];
|
||||
BigText(30, 112, GStrings("TXT_VarmintsKilled"), -1);
|
||||
BigText(30, 128, GStrings("TXT_VarmintsLeft"), -1);
|
||||
|
||||
if (displaystate & printKillsVal)
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].actors_killed);
|
||||
BigText(231, 112, tempbuf, -1);
|
||||
if (ud.player_skill > 3)
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "%s", GStrings("TXT_N_A"));
|
||||
BigText(231, 128, tempbuf, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed) < 0)
|
||||
mysnprintf(tempbuf, 32, "%-3d", 0);
|
||||
else mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_actors_killed - ps[myconnectindex].actors_killed);
|
||||
BigText(231, 128, tempbuf, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintSecrets()
|
||||
{
|
||||
char tempbuf[32];
|
||||
BigText(30, 144, GStrings("TXT_SECFND"), -1);
|
||||
BigText(30, 160, GStrings("TXT_SECMISS"), -1);
|
||||
|
||||
if (displaystate & printSecretsVal)
|
||||
{
|
||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].secret_rooms);
|
||||
BigText(231, 144, tempbuf, -1);
|
||||
if (ps[myconnectindex].secret_rooms > 0)
|
||||
sprintf(tempbuf, "%-3d", (100 * ps[myconnectindex].secret_rooms / ps[myconnectindex].max_secret_rooms));
|
||||
mysnprintf(tempbuf, 32, "%-3d", ps[myconnectindex].max_secret_rooms - ps[myconnectindex].secret_rooms);
|
||||
BigText(231, 160, tempbuf, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, tileGetTexture(gfx_offset, true), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||
|
||||
if (lastmapname) BigText(80, 16, lastmapname, -1);
|
||||
BigText(15, 192, GStrings("PRESSKEY"), -1);
|
||||
|
||||
if (displaystate & printTimeText)
|
||||
{
|
||||
PrintTime();
|
||||
}
|
||||
if (displaystate & printKillsText)
|
||||
{
|
||||
PrintKills();
|
||||
}
|
||||
if (displaystate & printSecretsText)
|
||||
{
|
||||
PrintSecrets();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class DRRRAEndOfGame : public DSkippableScreenJob
|
||||
{
|
||||
public:
|
||||
DRRRAEndOfGame() : DSkippableScreenJob(fadein|fadeout)
|
||||
{
|
||||
}
|
||||
|
||||
void Skipped() override
|
||||
{
|
||||
S_StopSound(35);
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
S_PlaySound(35, CHAN_AUTO, CHANF_UI);
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (!S_CheckSoundPlaying(-1, 35) && ticks > 15 * GameTicRate) state = finished; // make sure it stays, even if sound is off.
|
||||
}
|
||||
void Draw(double) override
|
||||
{
|
||||
auto tex = tileGetTexture(ENDGAME + ((ticks >> 2) & 1));
|
||||
DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void dobonus_r(int bonusonly, const CompletionFunc& completion)
|
||||
{
|
||||
JobDesc jobs[20];
|
||||
int job = 0;
|
||||
|
||||
FX_StopAllSounds();
|
||||
Mus_Stop();
|
||||
|
||||
if (bonusonly < 0 && !isRRRA() && numplayers < 2 && ud.from_bonus == 0)
|
||||
{
|
||||
int vol = volfromlevelnum(currentLevel->levelNumber);
|
||||
bonussequence_r(vol, jobs, job);
|
||||
}
|
||||
|
||||
if (playerswhenstarted > 1 && ud.coop != 1)
|
||||
{
|
||||
jobs[job++] = { Create<DRRMultiplayerBonusScreen>(playerswhenstarted) };
|
||||
}
|
||||
else if (bonusonly <= 0 && ud.multimode <= 1)
|
||||
{
|
||||
if (isRRRA() && !(currentLevel->flags & MI_USERMAP) && currentLevel->levelNumber < 106) // fixme: The logic here is awful. Shift more control to the map records.
|
||||
{
|
||||
jobs[job++] = { Create<DRRLevelSummaryScreen>(true) };
|
||||
int levnum = clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13);
|
||||
char fn[20];
|
||||
mysnprintf(fn, 20, "lvl%d.anm", levnum + 1);
|
||||
static const int framespeed[] = { 20, 20, 7200 }; // wait for one minute on the final frame so that the video doesn't stop before the user notices.
|
||||
jobs[job++] = { PlayVideo(fn, nullptr, framespeed) };
|
||||
if (bonusonly < 0 && currentLevel->levelNumber > 100)
|
||||
{
|
||||
jobs[job++] = { Create<DRRRAEndOfGame>() };
|
||||
}
|
||||
}
|
||||
else jobs[job++] = { Create<DRRLevelSummaryScreen>(false) };
|
||||
}
|
||||
if (job)
|
||||
RunScreenJob(jobs, job, completion);
|
||||
else if (completion) completion(false);
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DRRLoadScreen : public DScreenJob
|
||||
{
|
||||
MapRecord* rec;
|
||||
|
||||
public:
|
||||
DRRLoadScreen(MapRecord* maprec) : DScreenJob(0), rec(maprec) {}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
DrawTexture(twod, tileGetTexture(LOADSCREEN), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, TAG_DONE);
|
||||
|
||||
int y = isRRRA()? 140 : 90;
|
||||
BigText(160, y, (rec->flags & MI_USERMAP) ? GStrings("TXT_ENTRUM") : GStrings("TXT_ENTERIN"), 0);
|
||||
BigText(160, y+24, rec->DisplayName(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
void loadscreen_r(MapRecord* rec, CompletionFunc func)
|
||||
{
|
||||
JobDesc job = { Create<DRRLoadScreen>(rec) };
|
||||
RunScreenJob(&job, 1, func);
|
||||
}
|
||||
|
||||
void PrintPaused_r()
|
||||
{
|
||||
BigText(160, 100, GStrings("Game Paused"), 0);
|
||||
}
|
||||
|
||||
|
||||
END_DUKE_NS
|
||||
|
|
|
@ -3697,7 +3697,7 @@ void moveeffectors_d(void) //STATNUM 3
|
|||
{
|
||||
static int16_t list1[] = { BLOODPOOL, PUKE, FOOTPRINTS, FOOTPRINTS2, FOOTPRINTS3, FOOTPRINTS4, BULLETHOLE, BLOODSPLAT1, BLOODSPLAT2, BLOODSPLAT3, BLOODSPLAT4, -1 };
|
||||
static int16_t list2[] = { BOLT1, BOLT1 + 1,BOLT1 + 2, BOLT1 + 3, SIDEBOLT1, SIDEBOLT1 + 1, SIDEBOLT1 + 2, SIDEBOLT1 + 3, -1 };
|
||||
handle_se24(act, list1, list2, false, TRIPBOMB, LASERLINE, CRANE, 2);
|
||||
handle_se24(act, list1, list2, true, TRIPBOMB, LASERLINE, CRANE, 2);
|
||||
break;
|
||||
}
|
||||
case SE_35:
|
||||
|
|
|
@ -292,11 +292,11 @@ static bool cheatItems(int player)
|
|||
static bool cheatLevel(cheatseq_t *s)
|
||||
{
|
||||
int volnume,levnume;
|
||||
volnume = s->Args[0] - '0' - 1;
|
||||
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1;
|
||||
volnume = s->Args[0] - '0';
|
||||
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0');
|
||||
|
||||
// Instead of hard coded range checks on volume and level, let's just check if the level is defined.
|
||||
auto map = FindMapByLevelNum(levelnum(volnume, levnume));
|
||||
auto map = FindMapByIndex(volnume, levnume);
|
||||
if (map)
|
||||
{
|
||||
ChangeLevel(map, -1);
|
||||
|
|
|
@ -97,19 +97,8 @@ bool GameInterface::CanSave()
|
|||
|
||||
bool GameInterface::StartGame(FNewGameStartup& gs)
|
||||
{
|
||||
if (gs.Episode >= 1)
|
||||
{
|
||||
if (g_gameType & GAMEFLAG_SHAREWARE)
|
||||
{
|
||||
M_StartMessage(GStrings("BUYDUKE"), 1, NAME_None);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t skillsound = PISTOL_BODYHIT;
|
||||
|
||||
soundEngine->StopAllChannels();
|
||||
|
||||
static const short sounds_d[] = { JIBBED_ACTOR6, BONUS_SPEECH1, DUKE_GETWEAPON2, JIBBED_ACTOR5, JIBBED_ACTOR5 };
|
||||
static const short sounds_r[] = { 427, 428, 196, 195, 197 };
|
||||
if (gs.Skill >=0 && gs.Skill <= 5) skillsound = isRR()? sounds_r[gs.Skill] : sounds_d[gs.Skill];
|
||||
|
@ -126,15 +115,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
|
|||
}
|
||||
Net_ClearFifo();
|
||||
}
|
||||
|
||||
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
|
||||
if (map)
|
||||
{
|
||||
DeferedStartGame(map, gs.Skill);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FSavegameInfo GameInterface::GetSaveSig()
|
||||
|
|
|
@ -105,12 +105,8 @@ void think_r();
|
|||
void animatesprites_d(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio);
|
||||
void animatesprites_r(spritetype* tsprite, int& spritesortcnt, int x, int y, int a, int smoothratio);
|
||||
|
||||
void Logo_d(const CompletionFunc&);
|
||||
void Logo_r(const CompletionFunc&);
|
||||
void InitFonts_d();
|
||||
void InitFonts_r();
|
||||
void PrintPaused_d();
|
||||
void PrintPaused_r();
|
||||
|
||||
|
||||
Dispatcher fi;
|
||||
|
@ -120,9 +116,7 @@ void SetDispatcher()
|
|||
if (!isRR())
|
||||
{
|
||||
fi = {
|
||||
Logo_d,
|
||||
InitFonts_d,
|
||||
PrintPaused_d,
|
||||
|
||||
think_d,
|
||||
initactorflags_d,
|
||||
|
@ -167,9 +161,7 @@ void SetDispatcher()
|
|||
else
|
||||
{
|
||||
fi = {
|
||||
Logo_r,
|
||||
InitFonts_r,
|
||||
PrintPaused_r,
|
||||
|
||||
think_r,
|
||||
initactorflags_r,
|
||||
|
@ -235,8 +227,6 @@ int TILE_STATIC;
|
|||
int TILE_BOTTOMSTATUSBAR;
|
||||
int TILE_THREEDEE;
|
||||
int TILE_INGAMEDUKETHREEDEE;
|
||||
int TILE_PLUTOPAKSPRITE;
|
||||
int TILE_MENUBAR;
|
||||
int TILE_ATOMICHEALTH;
|
||||
int TILE_FLOORSLIME;
|
||||
int TILE_JIBS6;
|
||||
|
|
|
@ -76,9 +76,7 @@ struct GameInterface : public ::GameInterface
|
|||
struct Dispatcher
|
||||
{
|
||||
// global stuff
|
||||
void (*ShowLogo)(const CompletionFunc& completion);
|
||||
void (*InitFonts)();
|
||||
void (*PrintPaused)();
|
||||
|
||||
// sectors_?.cpp
|
||||
void (*think)();
|
||||
|
|
|
@ -261,10 +261,6 @@ void initactorflags_d()
|
|||
TILE_CAMLIGHT = CAMLIGHT;
|
||||
TILE_STATIC = STATIC;
|
||||
TILE_BOTTOMSTATUSBAR = isWorldTour()? WIDESCREENSTATUSBAR : BOTTOMSTATUSBAR;
|
||||
TILE_THREEDEE = THREEDEE;
|
||||
TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE;
|
||||
TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE;
|
||||
TILE_MENUBAR = MENUBAR;
|
||||
TILE_ATOMICHEALTH = ATOMICHEALTH;
|
||||
TILE_FLOORSLIME = FLOORSLIME;
|
||||
TILE_JIBS6 = JIBS6;
|
||||
|
|
|
@ -235,10 +235,6 @@ void initactorflags_r()
|
|||
TILE_CAMLIGHT = CAMLIGHT;
|
||||
TILE_STATIC = STATIC;
|
||||
TILE_BOTTOMSTATUSBAR = BOTTOMSTATUSBAR;
|
||||
TILE_THREEDEE = THREEDEE;
|
||||
TILE_INGAMEDUKETHREEDEE = INGAMEDUKETHREEDEE;
|
||||
TILE_PLUTOPAKSPRITE = PLUTOPAKSPRITE;
|
||||
TILE_MENUBAR = MENUBAR;
|
||||
TILE_ATOMICHEALTH = ATOMICHEALTH;
|
||||
TILE_FLOORSLIME = FLOORSLIME;
|
||||
TILE_JIBS6 = JIBS6;
|
||||
|
|
|
@ -162,9 +162,6 @@ int hits(DDukeActor* snum);
|
|||
DDukeActor* LocateTheLocator(int n, int sectnum);
|
||||
void clearcamera(player_struct* ps);
|
||||
|
||||
void showtwoscreens(const CompletionFunc& func);
|
||||
void doorders(const CompletionFunc& func);
|
||||
|
||||
void LoadActor(DDukeActor* i, int p, int x);
|
||||
void execute(DDukeActor* s, int p, int d);
|
||||
void makeitfall(DDukeActor* s);
|
||||
|
@ -211,8 +208,6 @@ void OffBoat(player_struct *pl);
|
|||
|
||||
void cameratext(DDukeActor* i);
|
||||
void dobonus(int bonusonly, const CompletionFunc& completion);
|
||||
void dobonus_d(int bonusonly, const CompletionFunc& completion);
|
||||
void dobonus_r(int bonusonly, const CompletionFunc& completion);
|
||||
|
||||
void drawoverlays(double smoothratio);
|
||||
void drawbackground(void);
|
||||
|
@ -227,7 +222,6 @@ void e4intro(const CompletionFunc& completion);
|
|||
void exitlevel(MapRecord *next);
|
||||
void enterlevel(MapRecord* mi, int gm);
|
||||
void donewgame(MapRecord* map, int sk);
|
||||
void startnewgame(MapRecord* map, int skill);
|
||||
int playercolor2lookup(int color);
|
||||
void PlayerColorChanged(void);
|
||||
bool movementBlocked(player_struct *p);
|
||||
|
|
|
@ -92,13 +92,12 @@ static void endthegame(bool)
|
|||
|
||||
void GameInterface::ExitFromMenu()
|
||||
{
|
||||
#if 0
|
||||
// do we really need this scoreboard stuff here?
|
||||
auto runbonus = [=](auto completion)
|
||||
{
|
||||
// MP scoreboard
|
||||
if (playerswhenstarted > 1 && !ud.coop)
|
||||
{
|
||||
dobonus(1, completion);
|
||||
}
|
||||
if (playerswhenstarted > 1 && !ud.coop) ShowScoreboard(playerswhenstarted);
|
||||
else completion(false);
|
||||
};
|
||||
|
||||
|
@ -106,11 +105,16 @@ void GameInterface::ExitFromMenu()
|
|||
{
|
||||
// shareware and TEN screens
|
||||
if (isShareware() && !isRR())
|
||||
showtwoscreens(completion);
|
||||
StartCutscene("DukeCutscenes.BuildSharewareExit", 0, completion);
|
||||
else completion(false);
|
||||
};
|
||||
|
||||
runbonus([=](bool aborted) { runtwoscreens(endthegame); });
|
||||
#else
|
||||
if (isShareware() && !isRR())
|
||||
StartCutscene("DukeCutscenes.BuildSharewareExit", 0, endthegame);
|
||||
else endthegame(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -297,7 +301,13 @@ void drawoverlays(double smoothratio)
|
|||
}
|
||||
|
||||
if (paused == 2)
|
||||
fi.PrintPaused();
|
||||
{
|
||||
double x = 160, y = 100;
|
||||
double scale = isRR() ? 0.4 : 1.;
|
||||
const char* text = GStrings("Game Paused");
|
||||
x -= BigFont->StringWidth(text) * 0.5 * scale;
|
||||
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -342,18 +352,6 @@ void cameratext(DDukeActor *cam)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void dobonus(int bonusonly, const CompletionFunc& completion)
|
||||
{
|
||||
if (isRR()) dobonus_r(bonusonly, completion);
|
||||
else dobonus_d(bonusonly, completion);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
int startrts(int lumpNum, int localPlayer)
|
||||
{
|
||||
if (SoundEnabled() &&
|
||||
|
|
|
@ -48,11 +48,12 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
|
|||
#include "conlabeldef.h"
|
||||
#include "gi.h"
|
||||
|
||||
extern TArray<TPointer<MapRecord>> mapList;
|
||||
|
||||
BEGIN_DUKE_NS
|
||||
|
||||
enum { VERSIONCHECK = 41 };
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// definitions needed by the parser.
|
||||
|
@ -88,7 +89,8 @@ public:
|
|||
|
||||
struct TempMusic
|
||||
{
|
||||
int levnum;
|
||||
int volnum;
|
||||
int levlnum;
|
||||
FString music;
|
||||
};
|
||||
|
||||
|
@ -1016,7 +1018,8 @@ int ConCompiler::parsecommand()
|
|||
if (k >= 0)
|
||||
{
|
||||
tempMusic.Reserve(1);
|
||||
tempMusic.Last().levnum = levelnum(k, i);
|
||||
tempMusic.Last().volnum = k + 1;
|
||||
tempMusic.Last().levlnum = i + 1;
|
||||
tempMusic.Last().music = parsebuffer.Data();
|
||||
}
|
||||
else
|
||||
|
@ -1644,6 +1647,7 @@ int ConCompiler::parsecommand()
|
|||
return 0;
|
||||
|
||||
case concmd_definevolumename:
|
||||
{
|
||||
popscriptvalue();
|
||||
transnum(LABEL_DEFINE);
|
||||
j = popscriptvalue();
|
||||
|
@ -1658,8 +1662,13 @@ int ConCompiler::parsecommand()
|
|||
textptr++, i++;
|
||||
}
|
||||
parsebuffer.Push(0);
|
||||
gVolumeNames[j] = FStringTable::MakeMacro(parsebuffer.Data(), i);
|
||||
// We need both a volume and a cluster for this new episode.
|
||||
auto vol = MustFindVolume(j);
|
||||
auto clust = MustFindCluster(j + 1);
|
||||
vol->name = clust->name = FStringTable::MakeMacro(parsebuffer.Data(), i);
|
||||
if (j > 0) vol->flags |= VF_SHAREWARELOCK;
|
||||
return 0;
|
||||
}
|
||||
case concmd_defineskillname:
|
||||
popscriptvalue();
|
||||
transnum(LABEL_DEFINE);
|
||||
|
@ -1695,25 +1704,32 @@ int ConCompiler::parsecommand()
|
|||
textptr++, i++;
|
||||
}
|
||||
parsebuffer.Push(0);
|
||||
auto levnum = levelnum(j, k);
|
||||
auto map = FindMapByLevelNum(levnum);
|
||||
auto map = FindMapByIndexOnly(j + 1, k + 1);
|
||||
if (!map) map = AllocateMap();
|
||||
map->SetFileName(parsebuffer.Data());
|
||||
if (k == 0)
|
||||
{
|
||||
auto vol = MustFindVolume(j);
|
||||
vol->startmap = map->labelName;
|
||||
}
|
||||
|
||||
while (*textptr == ' ' || *textptr == '\t') textptr++;
|
||||
|
||||
map->parTime =
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
|
||||
|
||||
textptr += 5;
|
||||
while (*textptr == ' ' || *textptr == '\t') textptr++;
|
||||
|
||||
map->designerTime =
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
|
||||
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
|
||||
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
|
||||
|
||||
map->levelNumber = levnum;
|
||||
SetLevelNum(map, makelevelnum(j + 1, k + 1));
|
||||
|
||||
map->mapindex = k + 1;
|
||||
map->cluster = j + 1;
|
||||
|
||||
textptr += 5;
|
||||
while (*textptr == ' ' || *textptr == '\t') textptr++;
|
||||
|
@ -3132,7 +3148,7 @@ void ConCompiler::setmusic()
|
|||
{
|
||||
for (auto& tm : tempMusic)
|
||||
{
|
||||
auto map = FindMapByLevelNum(tm.levnum);
|
||||
auto map = FindMapByIndexOnly(tm.volnum, tm.levlnum);
|
||||
if (map) map->music = tm.music;
|
||||
}
|
||||
tempMusic.Clear();
|
||||
|
@ -3176,6 +3192,12 @@ void loadcons()
|
|||
|
||||
ScriptCode.Push(0);
|
||||
ConCompiler comp;
|
||||
|
||||
if (fileSystem.FileExists("engine/engine.con"))
|
||||
{
|
||||
comp.compilecon("engine/engine.con");
|
||||
}
|
||||
|
||||
comp.compilecon(ConFile()); //Tokenize
|
||||
|
||||
if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get())
|
||||
|
@ -3206,45 +3228,38 @@ void loadcons()
|
|||
InitGameVarPointers();
|
||||
ResetSystemDefaults();
|
||||
S_WorldTourMappingsForOldSounds(); // create a sound mapping for World Tour.
|
||||
S_CacheAllSounds();
|
||||
comp.setmusic();
|
||||
|
||||
// RR must link the last map of E1 to the first map of E2.
|
||||
if (isRR()) for (auto& map : mapList)
|
||||
{
|
||||
if (map->cluster == 1)
|
||||
{
|
||||
if (!FindMapByIndexOnly(map->cluster, map->mapindex + 1))
|
||||
{
|
||||
auto nextmap = FindMapByIndexOnly(map->cluster + 1, 1);
|
||||
if (nextmap)
|
||||
{
|
||||
map->NextMap = nextmap->labelName;
|
||||
map->flags |= LEVEL_FORCENOEOG;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isWorldTour())
|
||||
{
|
||||
// fix broken secret exit in WT's super secret map.
|
||||
// This cannot be done from an RMAPINFO definition because the conditions are too specific and must not override custom maps.
|
||||
int num = fileSystem.CheckNumForName("e1l7.map");
|
||||
int file = fileSystem.GetFileContainer(num);
|
||||
if (file <= fileSystem.GetMaxIwadNum())
|
||||
{
|
||||
auto maprec = FindMapByName("e1l7");
|
||||
if (maprec) maprec->nextLevel = levelnum(0, 4);
|
||||
if (maprec) maprec->NextMap = "e1l5";
|
||||
}
|
||||
}
|
||||
else if (isRRRA())
|
||||
{
|
||||
// RRRA goes directly to the second episode after E1L7 to continue the game.
|
||||
int num = fileSystem.CheckNumForName("e1l7.map");
|
||||
int file = fileSystem.GetFileContainer(num);
|
||||
if (file <= fileSystem.GetMaxIwadNum())
|
||||
{
|
||||
auto maprec = FindMapByName("e1l7");
|
||||
if (maprec) maprec->nextLevel = levelnum(1, 0);
|
||||
}
|
||||
}
|
||||
else if (isRR())
|
||||
{
|
||||
// RR does not define its final level and crudely hacked it into the progression. This puts it into the E2L8 slot so that the game can naturally progress there.
|
||||
auto maprec1 = FindMapByLevelNum(levelnum(1, 6));
|
||||
auto maprec2 = FindMapByLevelNum(levelnum(1, 7));
|
||||
auto maprec3 = FindMapByName("endgame");
|
||||
int num3 = fileSystem.FindFile("endgame.map");
|
||||
if (maprec1 && !maprec2 && !maprec3 && num3 >= 0)
|
||||
{
|
||||
auto maprec = AllocateMap();
|
||||
maprec->designerTime = 0;
|
||||
maprec->parTime = 0;
|
||||
maprec->SetFileName("endgame.map");
|
||||
maprec->SetName("$TXT_CLOSEENCOUNTERS");
|
||||
maprec->levelNumber = levelnum(1, 7);
|
||||
}
|
||||
}
|
||||
comp.setmusic();
|
||||
}
|
||||
|
||||
END_DUKE_NS
|
||||
|
|
|
@ -130,11 +130,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
|
|||
if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_FROM_BONUS:
|
||||
if (bSet) ud.from_bonus = lValue;
|
||||
else SetGameVarID(lVar2, ud.from_bonus, sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_CAMERASPRITE:
|
||||
if (bSet) ud.cameraactor = ScriptIndexToActor(lValue);
|
||||
else SetGameVarID(lVar2, ActorToScriptIndex(ud.cameraactor), sActor, sPlayer);
|
||||
|
@ -239,14 +234,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
|
|||
else SetGameVarID(lVar2, ud.player_skill, sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_LEVEL_NUMBER:
|
||||
if (!bSet) SetGameVarID(lVar2, mapfromlevelnum(currentLevel->levelNumber), sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_VOLUME_NUMBER:
|
||||
if (!bSet) SetGameVarID(lVar2, volfromlevelnum(currentLevel->levelNumber), sActor, sPlayer);
|
||||
break;
|
||||
|
||||
case USERDEFS_MARKER:
|
||||
if (bSet) ud.marker = lValue;
|
||||
else SetGameVarID(lVar2, ud.marker, sActor, sPlayer);
|
||||
|
@ -3455,7 +3442,7 @@ int ParseState::parse(void)
|
|||
insptr++; // skip command
|
||||
volnume = GetGameVarID(*insptr++, g_ac, g_p);
|
||||
levnume = GetGameVarID(*insptr++, g_ac, g_p);
|
||||
auto level = FindMapByLevelNum(levelnum(volnume - 1, levnume - 1));
|
||||
auto level = FindMapByIndex(volnume, levnume);
|
||||
if (level != nullptr)
|
||||
ChangeLevel(level, -1);
|
||||
break;
|
||||
|
@ -3572,7 +3559,7 @@ int ParseState::parse(void)
|
|||
{
|
||||
insptr++;
|
||||
int music_select = *insptr++;
|
||||
auto level = FindMapByLevelNum(levelnum(currentLevel->levelNumber, music_select));
|
||||
auto level = FindMapByIndex(currentLevel->cluster, music_select+1); // this was 0-based in EDuke 2.0...
|
||||
if (level) S_PlayLevelMusic(level);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -113,29 +113,8 @@ void GameInterface::Ticker()
|
|||
|
||||
void GameInterface::Startup()
|
||||
{
|
||||
ps[myconnectindex].ftq = 0;
|
||||
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
auto maprecord = FindMapByName(userConfig.CommandMap);
|
||||
userConfig.CommandMap = "";
|
||||
if (maprecord)
|
||||
{
|
||||
ud.m_respawn_monsters = ud.player_skill == 4;
|
||||
|
||||
for (int i = 0; i != -1; i = connectpoint2[i])
|
||||
{
|
||||
resetweapons(i);
|
||||
resetinventory(i);
|
||||
}
|
||||
startnewgame(maprecord, /*userConfig.skill*/2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!userConfig.nologo) fi.ShowLogo([](bool) { gameaction = ga_mainmenunostopsound; });
|
||||
else gameaction = ga_mainmenunostopsound;
|
||||
}
|
||||
ps[myconnectindex].ftq = 0;
|
||||
PlayLogos(ga_mainmenunostopsound, ga_mainmenunostopsound, false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -160,20 +139,9 @@ void GameInterface::Render()
|
|||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void loadscreen_d(MapRecord* rec, CompletionFunc func);
|
||||
void loadscreen_r(MapRecord* rec, CompletionFunc func);
|
||||
|
||||
void GameInterface::NextLevel(MapRecord* map, int skill)
|
||||
{
|
||||
#if 0
|
||||
// Loading is so fast on modern system so that this only serves as an irritant, not an asset.
|
||||
auto loadscreen = isRR() ? loadscreen_r : loadscreen_d;
|
||||
loadscreen_d(map, [=](bool)
|
||||
{
|
||||
enterlevel(map, 0);
|
||||
gameaction = ga_level;
|
||||
});
|
||||
#endif
|
||||
enterlevel(map, 0);
|
||||
}
|
||||
|
||||
|
@ -183,23 +151,6 @@ void GameInterface::NextLevel(MapRecord* map, int skill)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void GameInterface::NewGame(MapRecord* map, int skill, bool)
|
||||
{
|
||||
// Hmm... What about the other players?
|
||||
ps[0].last_extra = gs.max_player_health;
|
||||
resetweapons(0);
|
||||
resetinventory(0);
|
||||
if (skill != -1) skill = skill + 1;
|
||||
|
||||
startnewgame(map, skill);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord* map, int skill)
|
||||
{
|
||||
exitlevel(map);
|
||||
|
|
|
@ -555,9 +555,9 @@ void InitGameVarPointers(void)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// These are deliberately not stored in accessible variables anymore
|
||||
int getmap() { return mapfromlevelnum(currentLevel->levelNumber); }
|
||||
int getvol() { return volfromlevelnum(currentLevel->levelNumber); }
|
||||
// These are deliberately not stored in accessible variables anymore. Use is deprecated.
|
||||
int getmap() { return currentLevel->levelNumber; }
|
||||
int getvol() { return currentLevel->cluster; }
|
||||
|
||||
void AddSystemVars()
|
||||
{
|
||||
|
|
|
@ -164,7 +164,7 @@ inline float PlayerInputAngVel(int pl)
|
|||
return ps[pl].sync.avel;
|
||||
}
|
||||
|
||||
inline float PlayerHorizon(int pl)
|
||||
inline float GetPlayerHorizon(int pl)
|
||||
{
|
||||
return ps[pl].sync.horz;
|
||||
}
|
||||
|
|
|
@ -517,6 +517,7 @@ x(THREEDEE, 2498)
|
|||
x(INGAMEDUKETHREEDEE, 2499)
|
||||
x(TENSCREEN, 2500)
|
||||
x(PLUTOPAKSPRITE, 2501)
|
||||
x(TITLEPLUTOPAKSPRITE, 2502)
|
||||
x(MENUPLUTOPAKSPRITE, 2503)
|
||||
x(CREDITPAGE1, 2504)
|
||||
x(CREDITPAGE2, 2505)
|
||||
|
@ -587,11 +588,33 @@ x(RESPAWNMARKERRED, 3190)
|
|||
x(RESPAWNMARKERYELLOW, 3200)
|
||||
x(RESPAWNMARKERGREEN, 3210)
|
||||
x(BONUSSCREEN, 3240)
|
||||
x(BONUSSCREEN_O1, 3241)
|
||||
x(BONUSSCREEN_O2, 3242)
|
||||
x(BONUSSCREEN_O3, 3243)
|
||||
x(BONUSSCREEN_O4, 3244)
|
||||
x(BONUSSCREEN2, 3245)
|
||||
x(BONUSSCREEN2_O1, 3246)
|
||||
x(BONUSSCREEN2_O2, 3247)
|
||||
x(BONUSSCREEN2_O3, 3248)
|
||||
x(BONUSSCREEN2_O4, 3249)
|
||||
x(VIEWBORDER, 3250)
|
||||
x(VICTORY1, 3260)
|
||||
x(VICTORY2, 3261)
|
||||
x(VICTORY3, 3262)
|
||||
x(VICTORY4, 3263)
|
||||
x(VICTORY5, 3264)
|
||||
x(VICTORY6, 3265)
|
||||
x(VICTORY7, 3266)
|
||||
x(VICTORY8, 3267)
|
||||
x(VICTORY9, 3268)
|
||||
x(ORDERING, 3270)
|
||||
x(ORDERING1, 3271)
|
||||
x(ORDERING2, 3272)
|
||||
x(ORDERING3, 3273)
|
||||
x(TEXTSTORY, 3280)
|
||||
x(LOADSCREEN, 3281)
|
||||
x(SWEXIT2, 3290)
|
||||
x(SWEXIT1, 3291)
|
||||
x(E1ENDSCREEN, 3292)
|
||||
x(E2ENDSCREEN, 3293)
|
||||
x(BORNTOBEWILDSCREEN, 3370)
|
||||
|
|
|
@ -1241,6 +1241,7 @@ y(RRTILE8640, 8640)
|
|||
y(RRTILE8651, 8651)
|
||||
y(RRTILE8660, 8660)
|
||||
x(ENDGAME, 8677)
|
||||
x(ENDGAME2, 8678)
|
||||
y(RRTILE8679, 8679)
|
||||
y(RRTILE8680, 8680)
|
||||
y(RRTILE8681, 8681)
|
||||
|
|
|
@ -22,10 +22,6 @@ extern int TILE_CAMCORNER;
|
|||
extern int TILE_CAMLIGHT;
|
||||
extern int TILE_STATIC;
|
||||
extern int TILE_BOTTOMSTATUSBAR;
|
||||
extern int TILE_THREEDEE;
|
||||
extern int TILE_INGAMEDUKETHREEDEE;
|
||||
extern int TILE_PLUTOPAKSPRITE;
|
||||
extern int TILE_MENUBAR;
|
||||
extern int TILE_ATOMICHEALTH;
|
||||
extern int TILE_FLOORSLIME;
|
||||
extern int TILE_JIBS6;
|
||||
|
|
|
@ -2630,27 +2630,6 @@ static void processweapon(int snum, ESyncBits actions, int psect)
|
|||
auto s = pact->s;
|
||||
int shrunk = (s->yrepeat < 32);
|
||||
|
||||
// Set maximum for pistol slightly higher if playing with `cl_showmagamount 1`.
|
||||
if (!cl_showmagamt)
|
||||
{
|
||||
if (p->ammo_amount[PISTOL_WEAPON] > PISTOL_MAXDEFAULT)
|
||||
p->ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT;
|
||||
|
||||
if (gs.max_ammo_amount[PISTOL_WEAPON] != PISTOL_MAXDEFAULT)
|
||||
gs.max_ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
short pistolAddition = 4;
|
||||
short pistolNewMaximum = PISTOL_MAXDEFAULT + pistolAddition;
|
||||
|
||||
if (p->ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT && gs.max_ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT)
|
||||
p->ammo_amount[PISTOL_WEAPON] += pistolAddition;
|
||||
|
||||
if (gs.max_ammo_amount[PISTOL_WEAPON] != pistolNewMaximum)
|
||||
gs.max_ammo_amount[PISTOL_WEAPON] = pistolNewMaximum;
|
||||
}
|
||||
|
||||
if (isNamWW2GI() && (actions & SB_HOLSTER)) // 'Holster Weapon
|
||||
{
|
||||
if (isWW2GI())
|
||||
|
@ -3138,7 +3117,7 @@ HORIZONLY:
|
|||
|
||||
if (SyncInput())
|
||||
{
|
||||
p->horizon.applyinput(PlayerHorizon(snum), &actions);
|
||||
p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
|
||||
}
|
||||
|
||||
p->checkhardlanding();
|
||||
|
|
|
@ -1464,26 +1464,8 @@ int doincrements_r(struct player_struct* p)
|
|||
{
|
||||
if (!wupass)
|
||||
{
|
||||
short snd;
|
||||
int snd = currentLevel->rr_startsound ? currentLevel->rr_startsound : 391;
|
||||
wupass = 1;
|
||||
switch (currentLevel->levelNumber)
|
||||
{
|
||||
default: snd = 391; break;
|
||||
case levelnum(0, 0): snd = isRRRA() ? 63 : 391; break;
|
||||
case levelnum(0, 1): snd = 64; break;
|
||||
case levelnum(0, 2): snd = 77; break;
|
||||
case levelnum(0, 3): snd = 80; break;
|
||||
case levelnum(0, 4): snd = 102; break;
|
||||
case levelnum(0, 5): snd = 103; break;
|
||||
case levelnum(0, 6): snd = 104; break;
|
||||
case levelnum(1, 0): snd = 105; break;
|
||||
case levelnum(1, 1): snd = 176; break;
|
||||
case levelnum(1, 2): snd = 177; break;
|
||||
case levelnum(1, 3): snd = 198; break;
|
||||
case levelnum(1, 4): snd = 230; break;
|
||||
case levelnum(1, 5): snd = 255; break;
|
||||
case levelnum(1, 6): snd = 283; break;
|
||||
}
|
||||
S_PlayActorSound(snd, pact);
|
||||
}
|
||||
else if (PlayClock > 1024)
|
||||
|
@ -3399,8 +3381,7 @@ void processinput_r(int snum)
|
|||
psectlotag = 2;
|
||||
}
|
||||
}
|
||||
else if (psectlotag == 7777)
|
||||
if (currentLevel->levelNumber == levelnum(1, 6))
|
||||
else if (psectlotag == 7777 && (currentLevel->gameflags & LEVEL_RR_HULKSPAWN))
|
||||
lastlevel = 1;
|
||||
|
||||
if (psectlotag == 848 && sector[psect].floorpicnum == WATERTILE2)
|
||||
|
@ -4000,7 +3981,7 @@ HORIZONLY:
|
|||
|
||||
if (SyncInput())
|
||||
{
|
||||
p->horizon.applyinput(PlayerHorizon(snum), &actions);
|
||||
p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
|
||||
}
|
||||
|
||||
p->checkhardlanding();
|
||||
|
|
|
@ -36,6 +36,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
|
|||
#include "automap.h"
|
||||
#include "dukeactor.h"
|
||||
#include "interpolate.h"
|
||||
#include "precache.h"
|
||||
#include "render.h"
|
||||
|
||||
BEGIN_DUKE_NS
|
||||
|
@ -141,7 +142,7 @@ void resetplayerstats(int snum)
|
|||
p->jetpack_on = 0;
|
||||
p->holoduke_on = nullptr;
|
||||
|
||||
p->angle.olook_ang = p->angle.look_ang = buildang(512 - ((currentLevel->levelNumber & 1) << 10));
|
||||
p->angle.olook_ang = p->angle.look_ang = buildang(512 - (((~currentLevel->levelNumber) & 1) << 10));
|
||||
p->angle.orotscrnang = p->angle.rotscrnang = buildang(0);
|
||||
|
||||
p->newOwner =nullptr;
|
||||
|
@ -659,7 +660,6 @@ void prelevel_common(int g)
|
|||
p->SlotWin = 0;
|
||||
enemysizecheat = 0;
|
||||
p->MamaEnd = 0;
|
||||
mamaspawn_count = 15;
|
||||
banjosound = 0;
|
||||
RRRA_ExitedLevel = 0;
|
||||
|
||||
|
@ -672,7 +672,7 @@ void prelevel_common(int g)
|
|||
WindDir = 0;
|
||||
fakebubba_spawn = 0;
|
||||
RRRA_ExitedLevel = 0;
|
||||
mamaspawn_count = 15;
|
||||
mamaspawn_count = currentLevel->rr_mamaspawn;
|
||||
BellTime = 0;
|
||||
BellSprite = nullptr;
|
||||
|
||||
|
@ -754,7 +754,6 @@ void donewgame(MapRecord* map, int sk)
|
|||
auto p = &ps[0];
|
||||
show_shareware = 26 * 34;
|
||||
|
||||
//ud.nextLevel = map;
|
||||
ud.player_skill = sk;
|
||||
ud.secretlevel = 0;
|
||||
ud.from_bonus = 0;
|
||||
|
@ -813,33 +812,6 @@ void donewgame(MapRecord* map, int sk)
|
|||
}
|
||||
}
|
||||
|
||||
template<class func>
|
||||
void newgame(MapRecord* map, int sk, func completion)
|
||||
{
|
||||
auto completion1 = [=](bool res)
|
||||
{
|
||||
if (!isRR() && map->levelNumber == levelnum(3, 0) && (ud.multimode < 2))
|
||||
{
|
||||
e4intro([=](bool) { donewgame(map, sk); completion(res); });
|
||||
}
|
||||
else
|
||||
{
|
||||
donewgame(map, sk);
|
||||
completion(res);
|
||||
}
|
||||
};
|
||||
|
||||
if (ud.m_recstat != 2 && ud.last_level >= 0 && ud.multimode > 1 && ud.coop != 1)
|
||||
dobonus(1, completion1);
|
||||
|
||||
#if 0 // this is one lousy hack job that's hopefully not needed anymore.
|
||||
else if (isRR() && !isRRRA() && map->levelNumber == levelnum(0, 6))
|
||||
dobonus(0, completion1);
|
||||
#endif
|
||||
|
||||
else completion1(false);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// the setup here is very, very sloppy, because mappings are not 1:1.
|
||||
|
@ -978,12 +950,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
|
|||
SECRET_SetMapName(mi->DisplayName(), mi->name);
|
||||
STAT_NewLevel(mi->fileName);
|
||||
|
||||
if (isRR() && !isRRRA() && mi->levelNumber == levelnum(1, 1))
|
||||
{
|
||||
for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++)
|
||||
ps[0].ammo_amount[i] = 0;
|
||||
ps[0].gotweapon.Clear(KNEE_WEAPON);
|
||||
}
|
||||
p->angle.ang = buildang(lbang);
|
||||
|
||||
memset(gotpic, 0, sizeof(gotpic));
|
||||
|
@ -993,16 +959,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
|
|||
|
||||
SpawnPortals();
|
||||
|
||||
if (isRRRA() && mi->levelNumber == levelnum(2, 0))
|
||||
{
|
||||
for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++)
|
||||
ps[0].ammo_amount[i] = 0;
|
||||
ps[0].gotweapon.Clear(KNEE_WEAPON);
|
||||
ps[0].gotweapon.Set(SLINGBLADE_WEAPON);
|
||||
ps[0].ammo_amount[SLINGBLADE_WEAPON] = 1;
|
||||
ps[0].curr_weapon = SLINGBLADE_WEAPON;
|
||||
}
|
||||
|
||||
allignwarpelevators();
|
||||
resetpspritevars(gamemode);
|
||||
|
||||
|
@ -1047,7 +1003,6 @@ void enterlevel(MapRecord *mi, int gamemode)
|
|||
OnEvent(EVENT_ENTERLEVEL);
|
||||
|
||||
// Stop all sounds
|
||||
S_ResumeSound(false);
|
||||
FX_StopAllSounds();
|
||||
FX_SetReverb(0);
|
||||
|
||||
|
@ -1064,27 +1019,32 @@ void enterlevel(MapRecord *mi, int gamemode)
|
|||
S_PlayLevelMusic(mi);
|
||||
}
|
||||
|
||||
if (isShareware() && mi->levelNumber == 0 && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
|
||||
|
||||
for (int i = connecthead; i >= 0; i = connectpoint2[i])
|
||||
{
|
||||
bool clearweapon = !!(currentLevel->flags & LEVEL_CLEARWEAPONS);
|
||||
int pn = sector[ps[i].GetActor()->s->sectnum].floorpicnum;
|
||||
if (pn == TILE_HURTRAIL || pn == TILE_FLOORSLIME || pn == TILE_FLOORPLASMA)
|
||||
{
|
||||
resetweapons(i);
|
||||
resetinventory(i);
|
||||
clearweapon = true;
|
||||
}
|
||||
if (clearweapon)
|
||||
{
|
||||
resetweapons(i);
|
||||
ps[i].gotweapon.Clear(PISTOL_WEAPON);
|
||||
ps[i].ammo_amount[PISTOL_WEAPON] = 0;
|
||||
ps[i].curr_weapon = KNEE_WEAPON;
|
||||
ps[i].kickback_pic = 0;
|
||||
ps[i].okickback_pic = ps[i].kickback_pic = 0;
|
||||
}
|
||||
if (currentLevel->flags & LEVEL_CLEARINVENTORY) resetinventory(i);
|
||||
}
|
||||
resetmys();
|
||||
|
||||
everyothertime = 0;
|
||||
global_random = 0;
|
||||
|
||||
ud.last_level = currentLevel->levelNumber;
|
||||
ud.last_level = 1;
|
||||
ps[myconnectindex].over_shoulder_on = 0;
|
||||
clearfrags();
|
||||
resettimevars(); // Here we go
|
||||
|
@ -1105,9 +1065,19 @@ void enterlevel(MapRecord *mi, int gamemode)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void startnewgame(MapRecord* map, int skill)
|
||||
void GameInterface::NewGame(MapRecord* map, int skill, bool)
|
||||
{
|
||||
for (int i = 0; i != -1; i = connectpoint2[i])
|
||||
{
|
||||
resetweapons(i);
|
||||
resetinventory(i);
|
||||
}
|
||||
|
||||
ps[0].last_extra = gs.max_player_health;
|
||||
|
||||
|
||||
if (skill == -1) skill = ud.player_skill;
|
||||
else skill++;
|
||||
ud.player_skill = skill;
|
||||
ud.m_respawn_monsters = (skill == 4);
|
||||
ud.m_monsters_off = ud.monsters_off = 0;
|
||||
|
@ -1115,13 +1085,13 @@ void startnewgame(MapRecord* map, int skill)
|
|||
ud.m_respawn_inventory = 0;
|
||||
ud.multimode = 1;
|
||||
|
||||
newgame(map, skill, [=](bool)
|
||||
{
|
||||
donewgame(map, skill);
|
||||
enterlevel(map, 0);
|
||||
if (isShareware() && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
|
||||
|
||||
PlayerColorChanged();
|
||||
inputState.ClearAllInput();
|
||||
gameaction = ga_level;
|
||||
});
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -1132,25 +1102,27 @@ void startnewgame(MapRecord* map, int skill)
|
|||
|
||||
bool setnextmap(bool checksecretexit)
|
||||
{
|
||||
MapRecord* map = nullptr;;
|
||||
int from_bonus = 0;
|
||||
MapRecord* map = nullptr;
|
||||
MapRecord* from_bonus = nullptr;
|
||||
|
||||
if (ud.eog)
|
||||
if (ud.eog && !(currentLevel->flags & LEVEL_FORCENOEOG))
|
||||
{
|
||||
}
|
||||
else if (checksecretexit && ud.from_bonus == 0)
|
||||
{
|
||||
if (ud.secretlevel > 0)
|
||||
{
|
||||
int newlevnum = levelnum(volfromlevelnum(currentLevel->levelNumber), ud.secretlevel-1);
|
||||
map = FindMapByLevelNum(newlevnum);
|
||||
// allow overriding the secret exit destination to make episode compilation easier with maps containing secret exits.
|
||||
if (currentLevel->flags & LEVEL_SECRETEXITOVERRIDE) map = FindNextSecretMap(currentLevel);
|
||||
if (!map) map = FindMapByIndex(currentLevel->cluster, ud.secretlevel);
|
||||
|
||||
if (map)
|
||||
{
|
||||
from_bonus = currentLevel->levelNumber + 1;
|
||||
from_bonus = FindNextMap(currentLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ud.from_bonus && currentLevel->nextLevel == -1) // if the current level has an explicit link, use that instead of ud.from_bonus.
|
||||
else if (ud.from_bonus && currentLevel->NextMap.IsEmpty()) // if the current level has an explicit link, use that instead of ud.from_bonus.
|
||||
{
|
||||
map = FindMapByLevelNum(ud.from_bonus);
|
||||
}
|
||||
|
@ -1169,7 +1141,7 @@ bool setnextmap(bool checksecretexit)
|
|||
{
|
||||
I_Error("Trying to open non-existent %s", map->fileName.GetChars());
|
||||
}
|
||||
ud.from_bonus = from_bonus;
|
||||
ud.from_bonus = from_bonus? from_bonus->levelNumber : 0;
|
||||
}
|
||||
CompleteLevel(map);
|
||||
return false;
|
||||
|
@ -1181,30 +1153,33 @@ bool setnextmap(bool checksecretexit)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void exitlevel(MapRecord *nextlevel)
|
||||
void exitlevel(MapRecord* nextlevel)
|
||||
{
|
||||
bool endofgame = nextlevel == nullptr;
|
||||
STAT_Update(endofgame);
|
||||
StopCommentary();
|
||||
|
||||
dobonus(endofgame? -1 : 0, [=](bool)
|
||||
{
|
||||
SummaryInfo info{};
|
||||
|
||||
info.kills = ps[0].actors_killed;
|
||||
info.maxkills = ps[0].max_actors_killed;
|
||||
info.secrets = ps[0].secret_rooms;
|
||||
info.maxsecrets = ps[0].max_secret_rooms;
|
||||
info.time = ps[0].player_par / GameTicRate;
|
||||
info.endofgame = endofgame;
|
||||
Mus_Stop();
|
||||
|
||||
if (playerswhenstarted > 1 && ud.coop != 1)
|
||||
{
|
||||
// MP scoreboard
|
||||
ShowScoreboard(playerswhenstarted, [=](bool)
|
||||
{
|
||||
// Clear potentially loaded per-map ART only after the bonus screens.
|
||||
artClearMapArt();
|
||||
gameaction = ga_level;
|
||||
ud.eog = false;
|
||||
if (endofgame)
|
||||
{
|
||||
if (ud.multimode < 2)
|
||||
{
|
||||
if (isShareware())
|
||||
doorders([](bool) { gameaction = ga_startup; });
|
||||
else gameaction = ga_startup;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto nextlevel = FindMapByLevelNum(0);
|
||||
if (!nextlevel)
|
||||
{
|
||||
|
@ -1213,11 +1188,22 @@ void exitlevel(MapRecord *nextlevel)
|
|||
}
|
||||
else gameaction = ga_nextlevel;
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
gameaction = ga_nextlevel;
|
||||
|
||||
});
|
||||
}
|
||||
else if (ud.multimode <= 1)
|
||||
{
|
||||
// SP cutscene + summary
|
||||
ShowIntermission(currentLevel, nextlevel, &info, [=](bool)
|
||||
{
|
||||
// Clear potentially loaded per-map ART only after the bonus screens.
|
||||
artClearMapArt();
|
||||
ud.eog = false;
|
||||
gameaction = endofgame? ga_startup : ga_nextlevel;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -232,6 +232,7 @@ static void cachegoodsprites(void)
|
|||
|
||||
void cacheit_d(void)
|
||||
{
|
||||
if (!r_precache) return;
|
||||
int i;
|
||||
|
||||
cachegoodsprites();
|
||||
|
|
|
@ -366,37 +366,6 @@ static void cachegoodsprites(void)
|
|||
for (i = SMALLSMOKE; i < (SMALLSMOKE + 4); i++)
|
||||
tloadtile(i);
|
||||
|
||||
if (isRRRA() && currentLevel->levelNumber == levelnum(0, 4))
|
||||
{
|
||||
tloadtile(RRTILE2577);
|
||||
}
|
||||
if (!isRRRA() && currentLevel->levelNumber == levelnum(1, 2))
|
||||
{
|
||||
tloadtile(RRTILE3190);
|
||||
tloadtile(RRTILE3191);
|
||||
tloadtile(RRTILE3192);
|
||||
tloadtile(RRTILE3144);
|
||||
tloadtile(RRTILE3139);
|
||||
tloadtile(RRTILE3132);
|
||||
tloadtile(RRTILE3120);
|
||||
tloadtile(RRTILE3121);
|
||||
tloadtile(RRTILE3122);
|
||||
tloadtile(RRTILE3123);
|
||||
tloadtile(RRTILE3124);
|
||||
}
|
||||
if (lastlevel)
|
||||
{
|
||||
i = isRRRA() ? UFO1_RRRA : UFO1_RR;
|
||||
tloadtile(i);
|
||||
i = UFO2;
|
||||
tloadtile(i);
|
||||
i = UFO3;
|
||||
tloadtile(i);
|
||||
i = UFO4;
|
||||
tloadtile(i);
|
||||
i = UFO5;
|
||||
tloadtile(i);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -407,6 +376,7 @@ static void cachegoodsprites(void)
|
|||
|
||||
void cacheit_r(void)
|
||||
{
|
||||
if (!r_precache) return;
|
||||
int i;
|
||||
|
||||
cachegoodsprites();
|
||||
|
@ -461,11 +431,11 @@ void prelevel_r(int g)
|
|||
prelevel_common(g);
|
||||
p = &ps[screenpeek];
|
||||
|
||||
if (currentLevel->gameflags & LEVEL_RR_CLEARMOONSHINE)
|
||||
ps[myconnectindex].steroids_amount = 0;
|
||||
|
||||
if (isRRRA())
|
||||
{
|
||||
if (currentLevel->levelNumber == levelnum(1, 4))
|
||||
ps[myconnectindex].steroids_amount = 0;
|
||||
|
||||
for (j = 0; j < MAXSPRITES; j++)
|
||||
{
|
||||
|
|
|
@ -243,9 +243,9 @@ public:
|
|||
//
|
||||
// keys
|
||||
//
|
||||
if (p->got_access & 1) DrawGraphic(tileGetTexture(ACCESSCARD), -12, -23.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
if (p->got_access & 4) DrawGraphic(tileGetTexture(ACCESSCARD), -7 , -21.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->got_access & 2) DrawGraphic(tileGetTexture(ACCESSCARD), -2 , -19.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
if (p->got_access & 1) DrawGraphic(tileGetTexture(ACCESSCARD), -12, -23.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
if (p->got_access & 4) DrawGraphic(tileGetTexture(ACCESSCARD), -7 , -21.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->got_access & 2) DrawGraphic(tileGetTexture(ACCESSCARD), -2 , -19.5, DI_ITEM_RIGHT, 1, -1, -1, 0.5, 0.5, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
}
|
||||
|
||||
|
||||
|
@ -355,15 +355,15 @@ public:
|
|||
format.Format("%3d/%d", num1, num2);
|
||||
}
|
||||
y--;
|
||||
DrawGraphic(tileGetTexture(THREEBYFIVE + index), x - 7, y, DI_ITEM_LEFT|DI_ITEM_VCENTER, 1, 0, 0, 1, 1, LightForShade(shade - 10), TRANSLATION(Translation_Remap, 7));
|
||||
DrawGraphic(tileGetTexture(THREEBYFIVE + index), x - 7, y, DI_ITEM_LEFT|DI_ITEM_VCENTER, 1, 0, 0, 1, 1, STYLE_Translucent, LightForShade(shade - 10), TRANSLATION(Translation_Remap, 7));
|
||||
auto pe = LightForShade(shade);
|
||||
DrawGraphic(tileGetTexture(THREEBYFIVE + 10), x - 3, y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, pe);
|
||||
DrawGraphic(tileGetTexture(THREEBYFIVE + 10), x - 3, y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, STYLE_Translucent, pe);
|
||||
for (size_t i = 0; i < format.Len(); i++)
|
||||
{
|
||||
if (format[i] != ' ')
|
||||
{
|
||||
char c = format[i] == '/' ? 11 : format[i] - '0';
|
||||
DrawGraphic(tileGetTexture(THREEBYFIVE + c), x + 4 * i + (parsedDivisor ? 1 : 0), y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, pe);
|
||||
DrawGraphic(tileGetTexture(THREEBYFIVE + c), x + 4 * i + (parsedDivisor ? 1 : 0), y, DI_ITEM_LEFT | DI_ITEM_VCENTER, 1, 0, 0, 1, 1, STYLE_Translucent, pe);
|
||||
}
|
||||
if (format[i] == '/')
|
||||
{
|
||||
|
@ -431,9 +431,9 @@ public:
|
|||
else
|
||||
{
|
||||
auto key = tileGetTexture(ACCESS_ICON);
|
||||
if (p->got_access & 4) DrawGraphic(key, 275.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->got_access & 2) DrawGraphic(key, 288.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
if (p->got_access & 1) DrawGraphic(key, 282, top + 23, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
if (p->got_access & 4) DrawGraphic(key, 275.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->got_access & 2) DrawGraphic(key, 288.5, top + 16, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
if (p->got_access & 1) DrawGraphic(key, 282, top + 23, DI_ITEM_OFFSETS, 1, -1, -1, 1, 1, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
}
|
||||
DrawWeaponAmounts(p, 96, top + 15.5);
|
||||
|
||||
|
|
|
@ -212,9 +212,9 @@ public:
|
|||
//
|
||||
// keys
|
||||
//
|
||||
if (p->keys[1]) DrawGraphic(tileGetTexture(ACCESSCARD), -28.5, -32 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
if (p->keys[3]) DrawGraphic(tileGetTexture(ACCESSCARD), -21.25, -28.375, DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->keys[2]) DrawGraphic(tileGetTexture(ACCESSCARD), -14, -24.75 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
if (p->keys[1]) DrawGraphic(tileGetTexture(ACCESSCARD), -28.5, -32 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
if (p->keys[3]) DrawGraphic(tileGetTexture(ACCESSCARD), -21.25, -28.375, DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->keys[2]) DrawGraphic(tileGetTexture(ACCESSCARD), -14, -24.75 , DI_ITEM_BOTTOM, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
}
|
||||
|
||||
|
||||
|
@ -374,9 +374,9 @@ public:
|
|||
else
|
||||
{
|
||||
auto key = tileGetTexture(ACCESS_ICON);
|
||||
if (p->keys[3]) DrawGraphic(key, 138, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->keys[2]) DrawGraphic(key, 152, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
if (p->keys[1]) DrawGraphic(key, 145, top + 21, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
if (p->keys[3]) DrawGraphic(key, 138, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 23));
|
||||
if (p->keys[2]) DrawGraphic(key, 152, top + 13, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 21));
|
||||
if (p->keys[1]) DrawGraphic(key, 145, top + 21, DI_ITEM_OFFSETS, 1, -1, -1, scale, scale, STYLE_Translucent, 0xffffffff, TRANSLATION(Translation_Remap, 0));
|
||||
}
|
||||
|
||||
int num = (p->GetActor()->s->pal == 1 && p->last_extra < 2) ? 1 : p->last_extra;
|
||||
|
|
|
@ -46,6 +46,7 @@ source as it is released.
|
|||
#include "gamestate.h"
|
||||
#include "names_d.h"
|
||||
#include "i_music.h"
|
||||
#include "vm.h"
|
||||
|
||||
CVAR(Bool, wt_forcemidi, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // quick hack to disable the oggs, which are of lower quality than playing the MIDIs with a good synth and sound font.
|
||||
CVAR(Bool, wt_forcevoc, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // The same for sound effects. The re-recordings are rather poor and disliked
|
||||
|
@ -872,4 +873,48 @@ bool StartCommentary(int tag, DDukeActor* actor)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySpecialMusic, S_PlaySpecialMusic)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(song);
|
||||
S_PlaySpecialMusic(song);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int PlaySound(int num, int chan, int flags, double vol)
|
||||
{
|
||||
return S_PlaySound(num, chan, EChanFlags::FromInt(flags), float(vol));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySound, PlaySound)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(snd);
|
||||
PARAM_INT(chan);
|
||||
PARAM_INT(flags);
|
||||
PARAM_FLOAT(vol);
|
||||
ACTION_RETURN_INT(PlaySound(snd, chan, flags, vol));
|
||||
}
|
||||
static void StopSound(int num)
|
||||
{
|
||||
S_StopSound(num);
|
||||
}
|
||||
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, StopSound, StopSound)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(snd);
|
||||
StopSound(snd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, CheckSoundPlaying, S_CheckSoundPlaying)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(snd);
|
||||
ACTION_RETURN_INT(S_CheckSoundPlaying(snd));
|
||||
}
|
||||
|
||||
END_DUKE_NS
|
||||
|
|
|
@ -48,7 +48,6 @@ void S_MenuSound(void);
|
|||
void S_StopSound(int sndNum, DDukeActor* spr = nullptr, int flags = -1);
|
||||
|
||||
int S_CheckSoundPlaying(int soundNum);
|
||||
inline int S_CheckSoundPlaying(int sprnum, int soundNum) { return S_CheckSoundPlaying(soundNum); }
|
||||
int S_CheckActorSoundPlaying(DDukeActor* spriteNum, int soundNum, int channel = 0);
|
||||
int S_CheckAnyActorSoundPlaying(DDukeActor* spriteNum);
|
||||
|
||||
|
@ -64,7 +63,6 @@ inline bool S_IsSoundValid(int num)
|
|||
void S_PlayRRMusic(int newTrack = -1);
|
||||
void S_PlayBonusMusic();
|
||||
void S_PlayLevelMusic(MapRecord* mi);
|
||||
void S_PlaySpecialMusic(unsigned int);
|
||||
void S_ContinueLevelMusic(void);
|
||||
|
||||
// Placeholders.
|
||||
|
|
|
@ -143,7 +143,6 @@ struct user_defs
|
|||
int m_respawn_items, m_respawn_monsters, m_respawn_inventory, m_recstat, m_monsters_off;
|
||||
int m_ffire, ffire, m_player_skill, multimode;
|
||||
int player_skill, marker;
|
||||
//MapRecord* nextLevel;
|
||||
|
||||
DDukeActor* cameraactor;
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "gstrings.h"
|
||||
#include "gamefuncs.h"
|
||||
#include "c_bind.h"
|
||||
#include "vm.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -47,8 +48,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
BEGIN_PS_NS
|
||||
|
||||
int selectedlevelnew;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
|
@ -373,471 +372,26 @@ void menu_DoPlasma()
|
|||
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DLobotomyScreen : public DImageScreen
|
||||
DEFINE_ACTION_FUNCTION(_Exhumed, DrawPlasma)
|
||||
{
|
||||
public:
|
||||
DLobotomyScreen(FGameTexture *tex, int fade) : DImageScreen(tex, fade)
|
||||
{}
|
||||
|
||||
void Skipped() override
|
||||
{
|
||||
StopLocalSound();
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
PlayLocalSound(StaticSound[kSoundJonLaugh2], 7000, false, CHANF_UI);
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
|
||||
DImageScreen::OnTick();
|
||||
if (state == finished) StopLocalSound();
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 };
|
||||
|
||||
class DMainTitle : public DSkippableScreenJob
|
||||
{
|
||||
const char* a;
|
||||
const char* b;
|
||||
int state = 0;
|
||||
int duration;
|
||||
int var_4 = 0;
|
||||
int esi = 130;
|
||||
int nCount = 0;
|
||||
int start;
|
||||
|
||||
|
||||
public:
|
||||
DMainTitle() : DSkippableScreenJob(fadein)
|
||||
{
|
||||
a = GStrings("TXT_EX_COPYRIGHT1");
|
||||
b = GStrings("TXT_EX_COPYRIGHT2");
|
||||
duration = skullDurations[0];
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
PlayLocalSound(StaticSound[59], 0, true, CHANF_UI);
|
||||
playCDtrack(19, true);
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
int ticker = ticks * 120 / GameTicRate;
|
||||
if (ticks > 1 && state == 0 && !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1))
|
||||
{
|
||||
if (time(0) & 0xF) // cheap-ass random...
|
||||
PlayGameOverSound();
|
||||
else
|
||||
PlayLocalSound(StaticSound[61], 0, false, CHANF_UI);
|
||||
state = 1;
|
||||
start = ticker;
|
||||
}
|
||||
if (state == 1)
|
||||
{
|
||||
if (ticker > duration)
|
||||
{
|
||||
nCount++;
|
||||
if (nCount > 12)
|
||||
{
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
duration = start + skullDurations[nCount];
|
||||
var_4 = var_4 == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
|
||||
menu_DoPlasma();
|
||||
|
||||
DrawRel(kSkullHead, 160, 100);
|
||||
if (state == 0)
|
||||
{
|
||||
DrawRel(kSkullJaw, 161, 130);
|
||||
}
|
||||
else
|
||||
{
|
||||
int nStringWidth = SmallFont->StringWidth(a);
|
||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
nStringWidth = SmallFont->StringWidth(b);
|
||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
|
||||
|
||||
short nTile = kSkullJaw;
|
||||
|
||||
if (var_4)
|
||||
{
|
||||
if (esi >= 135) nTile = kTile3583;
|
||||
else esi += 5;
|
||||
}
|
||||
else if (esi <= 130) esi = 130;
|
||||
else esi -= 2;
|
||||
|
||||
int y;
|
||||
|
||||
if (nTile == kTile3583)
|
||||
{
|
||||
y = 131;
|
||||
}
|
||||
else
|
||||
{
|
||||
y = esi;
|
||||
if (y > 135) y = 135;
|
||||
}
|
||||
|
||||
DrawRel(nTile, 161, y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
DScreenJob *PlayMovie(const char* fileName);
|
||||
|
||||
void DoTitle(CompletionFunc completion)
|
||||
{
|
||||
JobDesc jobs[5];
|
||||
int job = 0;
|
||||
|
||||
jobs[job++] = { Create<DImageScreen>(tileGetTexture(PublisherLogo()), DScreenJob::fadein | DScreenJob::fadeout) };
|
||||
jobs[job++] = { Create<DLobotomyScreen>(tileGetTexture(seq_GetSeqPicnum(kSeqScreens, 0, 0)), DScreenJob::fadein | DScreenJob::fadeout) };
|
||||
jobs[job++] = { PlayMovie("book.mov") };
|
||||
jobs[job++] = { Create<DMainTitle>() };
|
||||
|
||||
RunScreenJob(jobs, job, completion, true, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// pre-level map display
|
||||
// text overlay (native version still needed for Ramses texts.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static const int8_t MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 };
|
||||
|
||||
struct TILEFRAMEDEF
|
||||
{
|
||||
short nTile;
|
||||
short xOffs;
|
||||
short yOffs;
|
||||
};
|
||||
|
||||
// 22 bytes
|
||||
struct MapNamePlaque
|
||||
{
|
||||
short xPos;
|
||||
short yPos;
|
||||
TILEFRAMEDEF tiles[2];
|
||||
TILEFRAMEDEF text;
|
||||
};
|
||||
|
||||
static const MapNamePlaque mapNamePlaques[] = {
|
||||
{ 100, 170, kTile3376, 0, 0, kTile3377, 0, 0, kTile3411, 18, 6 },
|
||||
{ 230, 10, kTile3378, 0, 0, kTile3379, 0, 0, kTile3414, 18, 6 }, // DENDUR (level 2)
|
||||
{ 180, 125, kTile3380, 0, 0, kTile3381, 0, 0, kTile3417, 18, 6 }, // Kalabash
|
||||
{ 10, 95, kTile3382, 0, 0, kTile3383, 0, 0, kTile3420, 18, 6 },
|
||||
{ 210, 160, kTile3384, 0, 0, kTile3385, 0, 0, kTile3423, 18, 6 },
|
||||
{ 10, 110, kTile3371, 0, 0, kTile3386, 0, 0, kTile3426, 18, 6 },
|
||||
{ 10, 50, kTile3387, 0, 0, kTile3388, 0, 0, kTile3429, 18, 6 },
|
||||
{ 140, 0, kTile3389, 0, 0, kTile3390, 0, 0, kTile3432, 18, 6 },
|
||||
{ 30, 20, kTile3391, 0, 0, kTile3392, 0, 0, kTile3435, 18, 6 },
|
||||
{ 200, 150, kTile3409, 0, 0, kTile3410, 0, 0, kTile3418, 20, 4 },
|
||||
{ 145, 170, kTile3393, 0, 0, kTile3394, 0, 0, kTile3438, 18, 6 },
|
||||
{ 80, 80, kTile3395, 0, 0, kTile3396, 0, 0, kTile3441, 18, 6 },
|
||||
{ 15, 0, kTile3397, 0, 0, kTile3398, 0, 0, kTile3444, 18, 5 },
|
||||
{ 220, 35, kTile3399, 0, 0, kTile3400, 0, 0, kTile3447, 18, 6 },
|
||||
{ 190, 40, kTile3401, 0, 0, kTile3402, 0, 0, kTile3450, 18, 6 },
|
||||
{ 20, 130, kTile3403, 0, 0, kTile3404, 0, 0, kTile3453, 19, 6 },
|
||||
{ 220, 160, kTile3405, 0, 0, kTile3406, 0, 0, kTile3456, 18, 6 },
|
||||
{ 20, 10, kTile3407, 0, 0, kTile3408, 0, 0, kTile3459, 18, 6 },
|
||||
{ 200, 10, kTile3412, 0, 0, kTile3413, 0, 0, kTile3419, 18, 5 },
|
||||
{ 20, 10, kTile3415, 0, 0, kTile3416, 0, 0, kTile3421, 19, 4 }
|
||||
};
|
||||
|
||||
// 3 different types of fire, each with 4 frames
|
||||
static const TILEFRAMEDEF FireTiles[3][4] = {
|
||||
{{ kTile3484,0,3 },{ kTile3485,0,0 },{ kTile3486,0,3 },{ kTile3487,0,0 }},
|
||||
{{ kTile3488,1,0 },{ kTile3489,1,0 },{ kTile3490,0,1 },{ kTile3491,1,1 }},
|
||||
{{ kTile3492,1,2 },{ kTile3493,1,0 },{ kTile3494,1,2 },{ kTile3495,1,0 }}
|
||||
};
|
||||
|
||||
struct Fire
|
||||
{
|
||||
short nFireType;
|
||||
short xPos;
|
||||
short yPos;
|
||||
};
|
||||
|
||||
// 20 bytes
|
||||
struct MapFire
|
||||
{
|
||||
short nFires;
|
||||
Fire fires[3];
|
||||
};
|
||||
|
||||
/*
|
||||
level 1 - 3 fires
|
||||
level 2 - 3 fires
|
||||
level 3 - 1 fire
|
||||
|
||||
*/
|
||||
|
||||
static const MapFire MapLevelFires[] = {
|
||||
3, {{0, 107, 95}, {1, 58, 140}, {2, 28, 38}},
|
||||
3, {{2, 240, 0}, {0, 237, 32}, {1, 200, 30}},
|
||||
2, {{2, 250, 57}, {0, 250, 43}, {2, 200, 70}},
|
||||
2, {{1, 82, 59}, {2, 84, 16}, {0, 10, 95}},
|
||||
2, {{2, 237, 50}, {1, 215, 42}, {1, 210, 50}},
|
||||
3, {{0, 40, 7}, {1, 75, 6}, {2, 100, 10}},
|
||||
3, {{0, 58, 61}, {1, 85, 80}, {2, 111, 63}},
|
||||
3, {{0, 260, 65}, {1, 228, 0}, {2, 259, 15}},
|
||||
2, {{0, 81, 38}, {2, 58, 38}, {2, 30, 20}},
|
||||
3, {{0, 259, 49}, {1, 248, 76}, {2, 290, 65}},
|
||||
3, {{2, 227, 66}, {0, 224, 98}, {1, 277, 30}},
|
||||
2, {{0, 100, 10}, {2, 48, 76}, {2, 80, 80}},
|
||||
3, {{0, 17, 2}, {1, 29, 49}, {2, 53, 28}},
|
||||
3, {{0, 266, 42}, {1, 283, 99}, {2, 243, 108}},
|
||||
2, {{0, 238, 19}, {2, 240, 92}, {2, 190, 40}},
|
||||
2, {{0, 27, 0}, {1, 70, 40}, {0, 20, 130}},
|
||||
3, {{0, 275, 65}, {1, 235, 8}, {2, 274, 6}},
|
||||
3, {{0, 75, 45}, {1, 152, 105}, {2, 24, 68}},
|
||||
3, {{0, 290, 25}, {1, 225, 63}, {2, 260, 110}},
|
||||
0, {{1, 20, 10}, {1, 20, 10}, {1, 20, 10}}
|
||||
};
|
||||
|
||||
class DMapScreen : public DScreenJob
|
||||
void TextOverlay::Create(const FString& text, int pal)
|
||||
{
|
||||
int i;
|
||||
int x = 0;
|
||||
int delta = 0;
|
||||
int nIdleSeconds = 0;
|
||||
|
||||
int curYPos, destYPos;
|
||||
int nLevel, nLevelNew, nLevelBest;
|
||||
|
||||
public:
|
||||
DMapScreen(int nLevel_, int nLevelNew_, int nLevelBest_) : DScreenJob(fadein|fadeout), nLevel(nLevel_), nLevelNew(nLevelNew_), nLevelBest(nLevelBest_)
|
||||
{
|
||||
curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2));
|
||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos < destYPos) {
|
||||
delta = 2;
|
||||
}
|
||||
|
||||
if (curYPos > destYPos) {
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
// Trim smoke in widescreen
|
||||
#if 0
|
||||
vec2_t mapwinxy1 = windowxy1, mapwinxy2 = windowxy2;
|
||||
int32_t width = mapwinxy2.x - mapwinxy1.x + 1, height = mapwinxy2.y - mapwinxy1.y + 1;
|
||||
if (3 * width > 4 * height)
|
||||
{
|
||||
mapwinxy1.x += (width - 4 * height / 3) / 2;
|
||||
mapwinxy2.x -= (width - 4 * height / 3) / 2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Draw(double smoothratio)
|
||||
{
|
||||
int currentclock = int((ticks + smoothratio) * 120 / GameTicRate);
|
||||
|
||||
twod->ClearScreen();
|
||||
|
||||
int tileY = curYPos;
|
||||
|
||||
// Draw the background screens
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
DrawAbs(kTile3353 + i, x, tileY);
|
||||
tileY -= 200;
|
||||
}
|
||||
|
||||
// for each level - drawing the 'level completed' on-fire smoke markers
|
||||
for (i = 0; i < kMap20; i++)
|
||||
{
|
||||
int screenY = (i >> 1) * -200;
|
||||
|
||||
if (nLevelBest >= i) // check if the player has finished this level
|
||||
{
|
||||
for (int j = 0; j < MapLevelFires[i].nFires; j++)
|
||||
{
|
||||
int nFireFrame = ((currentclock >> 4) & 3);
|
||||
assert(nFireFrame >= 0 && nFireFrame < 4);
|
||||
|
||||
int nFireType = MapLevelFires[i].fires[j].nFireType;
|
||||
assert(nFireType >= 0 && nFireType < 3);
|
||||
|
||||
int nTile = FireTiles[nFireType][nFireFrame].nTile;
|
||||
int smokeX = MapLevelFires[i].fires[j].xPos + FireTiles[nFireType][nFireFrame].xOffs;
|
||||
int smokeY = MapLevelFires[i].fires[j].yPos + FireTiles[nFireType][nFireFrame].yOffs + curYPos + screenY;
|
||||
|
||||
// Use rotatesprite to trim smoke in widescreen
|
||||
DrawAbs(nTile, smokeX, smokeY);
|
||||
// Todo: mask out the sides of the screen if the background is not widescreen.
|
||||
}
|
||||
}
|
||||
|
||||
int t = (((currentclock & 16) >> 4));
|
||||
|
||||
int nTile = mapNamePlaques[i].tiles[t].nTile;
|
||||
|
||||
int nameX = mapNamePlaques[i].xPos + mapNamePlaques[i].tiles[t].xOffs;
|
||||
int nameY = mapNamePlaques[i].yPos + mapNamePlaques[i].tiles[t].yOffs + curYPos + screenY;
|
||||
|
||||
// Draw level name plaque
|
||||
DrawAbs(nTile, nameX, nameY);
|
||||
|
||||
int8_t shade = 96;
|
||||
|
||||
if (nLevelNew == i)
|
||||
{
|
||||
shade = (bsin(16 * currentclock) + 31) >> 8;
|
||||
}
|
||||
else if (nLevelBest >= i)
|
||||
{
|
||||
shade = 31;
|
||||
}
|
||||
|
||||
int textY = mapNamePlaques[i].yPos + mapNamePlaques[i].text.yOffs + curYPos + screenY;
|
||||
int textX = mapNamePlaques[i].xPos + mapNamePlaques[i].text.xOffs;
|
||||
nTile = mapNamePlaques[i].text.nTile;
|
||||
|
||||
// draw the text, alternating between red and black
|
||||
DrawAbs(nTile, textX, textY, shade);
|
||||
}
|
||||
|
||||
selectedlevelnew = nLevelNew + 1;
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (curYPos != destYPos)
|
||||
{
|
||||
// scroll the map every couple of ms
|
||||
curYPos += delta;
|
||||
|
||||
if (curYPos > destYPos && delta > 0) {
|
||||
curYPos = destYPos;
|
||||
}
|
||||
|
||||
if (curYPos < destYPos && delta < 0) {
|
||||
curYPos = destYPos;
|
||||
}
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
else nIdleSeconds++;
|
||||
if (nIdleSeconds > 300) state = finished;
|
||||
}
|
||||
|
||||
bool OnEvent(event_t* ev) override
|
||||
{
|
||||
int key = ev->data1;
|
||||
if (ev->type == EV_KeyDown)
|
||||
{
|
||||
auto binding = Bindings.GetBinding(ev->data1);
|
||||
if (!binding.CompareNoCase("+move_forward")) key = KEY_UPARROW;
|
||||
if (!binding.CompareNoCase("+move_backward")) key = KEY_DOWNARROW;
|
||||
|
||||
if (key == KEY_UPARROW || key == KEY_PAD_DPAD_UP || key == sc_kpad_8)
|
||||
{
|
||||
if (curYPos == destYPos && nLevelNew <= nLevelBest)
|
||||
{
|
||||
nLevelNew++;
|
||||
assert(nLevelNew < 20);
|
||||
|
||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos <= destYPos) {
|
||||
delta = 2;
|
||||
}
|
||||
else {
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == KEY_DOWNARROW || key == KEY_PAD_DPAD_DOWN || key == sc_kpad_2)
|
||||
{
|
||||
if (curYPos == destYPos && nLevelNew > 0)
|
||||
{
|
||||
nLevelNew--;
|
||||
assert(nLevelNew >= 0);
|
||||
|
||||
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
|
||||
|
||||
if (curYPos <= destYPos) {
|
||||
delta = 2;
|
||||
}
|
||||
else {
|
||||
delta = -2;
|
||||
}
|
||||
|
||||
nIdleSeconds = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!specialKeyEvent(ev)) state = skipped;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray<JobDesc> &jobs)
|
||||
{
|
||||
if (nLevel > kMap20 || nLevelNew > kMap20) // max single player levels
|
||||
{
|
||||
return;
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
nLevelBest = kMap20;
|
||||
#endif
|
||||
|
||||
if (nLevel < 1) nLevel = 1;
|
||||
if (nLevelNew < 1) nLevelNew = nLevel;
|
||||
|
||||
// 0-offset the level numbers
|
||||
jobs.Push( { Create<DMapScreen>(nLevel-1, nLevelNew-1, nLevelBest-1) });
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// text overlay
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
lastclock = 0;
|
||||
FString ttext = GStrings(text);
|
||||
screentext = ttext.Split("\n");
|
||||
ComputeCinemaText();
|
||||
}
|
||||
|
||||
void TextOverlay::Start(double starttime)
|
||||
{
|
||||
|
@ -857,10 +411,9 @@ void TextOverlay::ComputeCinemaText()
|
|||
nHeight = screentext.Size() * 10;
|
||||
}
|
||||
|
||||
void TextOverlay::ReadyCinemaText(uint16_t nVal)
|
||||
void TextOverlay::ReadyCinemaText(const char* nVal)
|
||||
{
|
||||
FStringf label("TXT_EX_CINEMA%d", nVal);
|
||||
label = GStrings(label);
|
||||
FString label = nVal[0] == '$'? GStrings(nVal +1) : nVal;
|
||||
screentext = label.Split("\n");
|
||||
ComputeCinemaText();
|
||||
}
|
||||
|
@ -903,33 +456,6 @@ bool TextOverlay::AdvanceCinemaText(double clock)
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
enum EScenes
|
||||
{
|
||||
CINEMA_BEFORE_LEVEL_5,
|
||||
CINEMA_AFTER_LEVEL_10,
|
||||
CINEMA_BEFORE_LEVEL_11,
|
||||
CINEMA_AFTER_LEVEL_15,
|
||||
CINEMA_LOSE_SCENE,
|
||||
CINEMA_AFTER_LEVEL_20,
|
||||
};
|
||||
|
||||
struct CinemaDef
|
||||
{
|
||||
short tile;
|
||||
short palette;
|
||||
short text;
|
||||
short track;
|
||||
};
|
||||
|
||||
static CinemaDef cinemas[] = {
|
||||
{ 3449, 3, 2, 2},
|
||||
{ 3451, 5, 4, 3},
|
||||
{ 3454, 1, 3, 4},
|
||||
{ 3446, 7, 6, 6},
|
||||
{ 3445, 4, 7, 7},
|
||||
{ 3448, 6, 8, 8}
|
||||
};
|
||||
|
||||
static const char * const cinpalfname[] = {
|
||||
"3454.pal",
|
||||
"3452.pal",
|
||||
|
@ -965,419 +491,59 @@ void uploadCinemaPalettes()
|
|||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// cinema
|
||||
// this accesses the tile data and needs to remain native.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DCinema : public DSkippableScreenJob
|
||||
static int DoStatic(int a, int b)
|
||||
{
|
||||
TextOverlay text;
|
||||
short cinematile;
|
||||
int currentCinemaPalette;
|
||||
int edx;
|
||||
int check;
|
||||
int cont = 1;
|
||||
|
||||
public:
|
||||
DCinema(int nVal, int checklevel = -1) : DSkippableScreenJob(fadein|fadeout)
|
||||
{
|
||||
if (nVal < 0 || nVal >5) return;
|
||||
cinematile = cinemas[nVal].tile;
|
||||
currentCinemaPalette = cinemas[nVal].palette;
|
||||
text.Start(0);
|
||||
text.ReadyCinemaText(cinemas[nVal].text);
|
||||
text.SetPalette(currentCinemaPalette);
|
||||
edx = cinemas[nVal].track;
|
||||
check = checklevel;
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
if (check > 0 && check != selectedlevelnew)
|
||||
{
|
||||
state = finished;
|
||||
return; // immediately abort if the player selected a different level on the map
|
||||
}
|
||||
|
||||
check = -1;
|
||||
StopAllSounds();
|
||||
if (edx != -1)
|
||||
{
|
||||
playCDtrack(edx + 2, false);
|
||||
}
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (!cont)
|
||||
{
|
||||
state = finished;
|
||||
// quit the game if we've finished level 4 and displayed the advert text
|
||||
if (isShareware() && currentCinemaPalette == 3)
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
if (check == 0) return;
|
||||
DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE);
|
||||
|
||||
text.DisplayText();
|
||||
cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate));
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// last level cinema
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DLastLevelCinema : public DScreenJob
|
||||
{
|
||||
int var_24 = 16;
|
||||
int var_28 = 12;
|
||||
|
||||
int ebp;
|
||||
int phase = 0;
|
||||
int nextclock = 4;
|
||||
unsigned int nStringTypeOn, nCharTypeOn;
|
||||
int screencnt = 0;
|
||||
bool skiprequest = false;
|
||||
|
||||
TArray<FString> screentext;
|
||||
|
||||
public:
|
||||
DLastLevelCinema() : DScreenJob(fadein | fadeout) {}
|
||||
|
||||
private:
|
||||
void DoStatic(int a, int b)
|
||||
{
|
||||
TileFiles.tileMakeWritable(kTileLoboLaptop);
|
||||
auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
|
||||
if (tex) tex->Reload();
|
||||
auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop);
|
||||
|
||||
int v2 = 160 - a / 2;
|
||||
int v4 = 81 - b / 2;
|
||||
int y = 160 - a / 2;
|
||||
int left = 81 - b / 2;
|
||||
|
||||
int var_18 = v2 + a;
|
||||
int v5 = v4 + b;
|
||||
int bottom = y + a;
|
||||
int right = left + b;
|
||||
|
||||
auto pTile = (pixels + (200 * v2)) + v4;
|
||||
auto pTile = (pixels + (200 * y)) + left;
|
||||
|
||||
TileFiles.InvalidateTile(kTileLoboLaptop);
|
||||
|
||||
while (v2 < var_18)
|
||||
for(;y < bottom; y++)
|
||||
{
|
||||
uint8_t* pStart = pTile;
|
||||
uint8_t* pixel = pTile;
|
||||
pTile += 200;
|
||||
|
||||
int v7 = v4;
|
||||
|
||||
while (v7 < v5)
|
||||
for (int x = left; x < right; x++)
|
||||
{
|
||||
*pStart = RandomBit() * 16;
|
||||
|
||||
v7++;
|
||||
pStart++;
|
||||
*pixel++ = RandomBit() * 16;
|
||||
}
|
||||
v2++;
|
||||
}
|
||||
}
|
||||
|
||||
void Phase1()
|
||||
{
|
||||
if (var_24 >= 116)
|
||||
{
|
||||
if (var_28 < 192)
|
||||
var_28 += 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
var_24 += 20;
|
||||
}
|
||||
|
||||
DoStatic(var_28, var_24);
|
||||
}
|
||||
|
||||
bool InitPhase2()
|
||||
{
|
||||
FStringf label("TXT_EX_LASTLEVEL%d", screencnt + 1);
|
||||
label = GStrings(label);
|
||||
screentext = label.Split("\n");
|
||||
if (screentext.Size() == 0) return false;
|
||||
|
||||
nStringTypeOn = 0;
|
||||
nCharTypeOn = 0;
|
||||
|
||||
ebp = screentext.Size() * 4; // half height of the entire text
|
||||
ebp = 81 - ebp; // offset from the screen's center.
|
||||
return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
|
||||
}
|
||||
|
||||
static int UndoStatic()
|
||||
{
|
||||
auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
|
||||
if (tex) tex->Reload();
|
||||
TileFiles.InvalidateTile(kTileLoboLaptop);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Phase3()
|
||||
{
|
||||
DoStatic(var_28, var_24);
|
||||
|
||||
if (var_28 > 20) {
|
||||
var_28 -= 20;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (var_24 > 20) {
|
||||
var_24 -= 20;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DisplayPhase2()
|
||||
{
|
||||
int yy = ebp;
|
||||
|
||||
auto p = GStrings["REQUIRED_CHARACTERS"];
|
||||
if (p && *p)
|
||||
{
|
||||
yy *= 2;
|
||||
for (int i = 0; i < nStringTypeOn; i++, yy += 10)
|
||||
{
|
||||
DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit640x400, TAG_DONE);
|
||||
}
|
||||
DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn, TAG_DONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < nStringTypeOn; i++, yy += 8)
|
||||
{
|
||||
DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
|
||||
}
|
||||
DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn, TAG_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
bool OnEvent(event_t* ev)
|
||||
{
|
||||
if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
||||
phase = 1;
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case 1:
|
||||
Phase1();
|
||||
if (skiprequest || ticks >= nextclock)
|
||||
{
|
||||
InitPhase2();
|
||||
phase = 2;
|
||||
skiprequest = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (screentext[nStringTypeOn][nCharTypeOn] != ' ')
|
||||
PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI);
|
||||
|
||||
nCharTypeOn++;
|
||||
if (screentext[nStringTypeOn][nCharTypeOn] == 0)
|
||||
{
|
||||
nCharTypeOn = 0;
|
||||
nStringTypeOn++;
|
||||
if (nStringTypeOn >= screentext.Size())
|
||||
{
|
||||
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||
phase = 3;
|
||||
}
|
||||
|
||||
}
|
||||
if (skiprequest)
|
||||
{
|
||||
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
|
||||
phase = 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (ticks >= nextclock || skiprequest)
|
||||
{
|
||||
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
|
||||
phase = 4;
|
||||
nextclock = ticks + 60;
|
||||
skiprequest = false;
|
||||
}
|
||||
|
||||
case 4:
|
||||
if (ticks >= nextclock)
|
||||
{
|
||||
skiprequest |= !Phase3();
|
||||
}
|
||||
if (skiprequest)
|
||||
{
|
||||
// Go to the next text page.
|
||||
if (screencnt != 2)
|
||||
{
|
||||
screencnt++;
|
||||
nextclock = ticks + 60;
|
||||
skiprequest = 0;
|
||||
phase = 1;
|
||||
}
|
||||
else state = finished;
|
||||
}
|
||||
|
||||
if (skiprequest)
|
||||
{
|
||||
state = finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
|
||||
if (phase == 2 || phase == 3) DisplayPhase2();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Credits roll
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class DExCredits : public DScreenJob
|
||||
{
|
||||
TArray<FString> credits;
|
||||
TArray<FString> pagelines;
|
||||
uint64_t page;
|
||||
uint64_t pagetime;
|
||||
bool skiprequest = false;
|
||||
|
||||
public:
|
||||
DExCredits()
|
||||
{
|
||||
auto textdata = fileSystem.LoadFile("credits.txt", 1);
|
||||
FString text = (char*)textdata.Data();
|
||||
text.Substitute("\r", "");
|
||||
credits = text.Split("\n\n");
|
||||
}
|
||||
|
||||
bool OnEvent(event_t* ev)
|
||||
{
|
||||
if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Start() override
|
||||
{
|
||||
if (credits.Size() == 0)
|
||||
{
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
playCDtrack(19, false);
|
||||
pagetime = 0;
|
||||
page = -1;
|
||||
}
|
||||
|
||||
void OnTick() override
|
||||
{
|
||||
if (ticks >= pagetime || skiprequest)
|
||||
{
|
||||
page++;
|
||||
if (page < credits.Size())
|
||||
pagelines = credits[page].Split("\n");
|
||||
else
|
||||
{
|
||||
if (skiprequest || !CDplaying())
|
||||
{
|
||||
state = finished;
|
||||
return;
|
||||
}
|
||||
pagelines.Clear();
|
||||
}
|
||||
pagetime = ticks + 60; //
|
||||
}
|
||||
}
|
||||
|
||||
void Draw(double smoothratio) override
|
||||
{
|
||||
twod->ClearScreen();
|
||||
|
||||
int y = 100 - ((10 * (pagelines.Size() - 1)) / 2);
|
||||
|
||||
for (unsigned i = 0; i < pagelines.Size(); i++)
|
||||
{
|
||||
int ptime = clamp((pagetime - ticks - smoothratio) * 1000 / GameTicRate, 0, 2000); // in milliseconds
|
||||
int light;
|
||||
|
||||
if (ptime < 255) light = ptime;
|
||||
else if (ptime > 2000 - 255) light = 2000 - ptime;
|
||||
else light = 255;
|
||||
|
||||
auto color = PalEntry(255, light, light, light);
|
||||
|
||||
int nStringWidth = SmallFont->StringWidth(pagelines[i]);
|
||||
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, color, TAG_DONE);
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// player died
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void DoGameOverScene(bool finallevel)
|
||||
{
|
||||
JobDesc job;
|
||||
|
||||
if (finallevel)
|
||||
{
|
||||
job = { Create<DCinema>(CINEMA_LOSE_SCENE) };
|
||||
}
|
||||
else
|
||||
{
|
||||
StopCD();
|
||||
PlayGameOverSound();
|
||||
job = { Create<DImageScreen>(tileGetTexture(kTile3591), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff, TRANSLATION(Translation_BasePalettes, 16)) };
|
||||
}
|
||||
RunScreenJob(&job, 1, [](bool) { gameaction = ga_mainmenu; });
|
||||
return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
|
||||
}
|
||||
|
||||
|
||||
void DoAfterCinemaScene(int nLevel, TArray<JobDesc>& jobs)
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, DoStatic, DoStatic)
|
||||
{
|
||||
int scene = -1;
|
||||
if (nLevel == 10) scene = CINEMA_AFTER_LEVEL_10;
|
||||
if (nLevel == 15) scene = CINEMA_AFTER_LEVEL_15;
|
||||
if (nLevel == 20) scene = CINEMA_AFTER_LEVEL_20;
|
||||
if (scene > 0) jobs.Push({ Create<DCinema>(scene) });
|
||||
if (nLevel == 19) { jobs.Push({ Create<DLastLevelCinema>() }); selectedlevelnew = 20; }
|
||||
if (nLevel == 20) jobs.Push({ Create<DExCredits>() });
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(x);
|
||||
PARAM_INT(y);
|
||||
ACTION_RETURN_INT(DoStatic(x, y));
|
||||
}
|
||||
|
||||
void DoBeforeCinemaScene(int nLevel, TArray<JobDesc>& jobs)
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, UndoStatic, UndoStatic)
|
||||
{
|
||||
if (nLevel == 5) jobs.Push({ Create<DCinema>(CINEMA_BEFORE_LEVEL_5) });
|
||||
else if (nLevel == 11) jobs.Push({ Create<DCinema>(CINEMA_BEFORE_LEVEL_11, 11) });
|
||||
ACTION_RETURN_INT(UndoStatic());
|
||||
}
|
||||
|
||||
END_PS_NS
|
||||
|
|
|
@ -236,7 +236,7 @@ void DoBubbleMachines()
|
|||
void BuildBubbleMachine(int nSprite)
|
||||
{
|
||||
if (nMachineCount >= kMaxMachines) {
|
||||
I_Error("too many bubble machines in level %d\n", currentLevel->levelNumber);
|
||||
I_Error("too many bubble machines in level %s\n", currentLevel->labelName.GetChars());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,13 +72,6 @@ void GameInterface::QuitToTitle()
|
|||
gameaction = ga_mainmenu;
|
||||
}
|
||||
|
||||
bool GameInterface::StartGame(FNewGameStartup& gs)
|
||||
{
|
||||
auto map = FindMapByLevelNum(gs.Skill); // 0 is training, 1 is the regular game - the game does not have skill levels.
|
||||
DeferedStartGame(map, 1, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
FSavegameInfo GameInterface::GetSaveSig()
|
||||
{
|
||||
return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS };
|
||||
|
@ -90,12 +83,6 @@ END_PS_NS
|
|||
|
||||
using namespace Exhumed;
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw)
|
||||
{
|
||||
menu_DoPlasma();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw)
|
||||
{
|
||||
auto nLogoTile = GameLogo();
|
||||
|
|
|
@ -36,6 +36,8 @@ void resettiming()
|
|||
|
||||
void precache()
|
||||
{
|
||||
if (!r_precache) return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numsectors; i++)
|
||||
|
|
|
@ -238,6 +238,15 @@ double calc_smoothratio()
|
|||
return I_GetTimeFrac() * MaxSmoothRatio;
|
||||
}
|
||||
|
||||
void DoGameOverScene(bool finallevel)
|
||||
{
|
||||
// todo: make these customizable later.
|
||||
StartCutscene(finallevel ? "ExhumedCutscenes.BuildCinemaLose" : "ExhumedCutscenes.BuildGameoverScene", 0, [](bool)
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
});
|
||||
}
|
||||
|
||||
void GameMove(void)
|
||||
{
|
||||
FixPalette();
|
||||
|
@ -247,7 +256,7 @@ void GameMove(void)
|
|||
sprite[i].backuploc();
|
||||
}
|
||||
|
||||
if (currentLevel->levelNumber == kMap20)
|
||||
if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)
|
||||
{
|
||||
if (lCountDown <= 0)
|
||||
{
|
||||
|
@ -456,7 +465,7 @@ void GameInterface::Ticker()
|
|||
|
||||
void LevelFinished()
|
||||
{
|
||||
NextMap = currentLevel->levelNumber == 20 ? nullptr : FindMapByLevelNum(currentLevel->levelNumber + 1); // todo: Use the map record for progression
|
||||
NextMap = FindNextMap(currentLevel);
|
||||
EndLevel = 13;
|
||||
}
|
||||
|
||||
|
@ -480,19 +489,6 @@ void GameInterface::app_init()
|
|||
#if 0
|
||||
help_disabled = true;
|
||||
#endif
|
||||
// Create the global level table. Parts of the engine need it, even though the game itself does not.
|
||||
for (int i = 0; i <= 32; i++)
|
||||
{
|
||||
auto mi = AllocateMap();
|
||||
mi->fileName.Format("LEV%d.MAP", i);
|
||||
mi->labelName.Format("LEV%d", i);
|
||||
mi->name.Format("$TXT_EX_MAP%02d", i);
|
||||
mi->levelNumber = i;
|
||||
|
||||
int nTrack = i;
|
||||
if (nTrack != 0) nTrack--;
|
||||
mi->cdSongId = (nTrack % 8) + 11;
|
||||
}
|
||||
|
||||
InitCheats();
|
||||
registerosdcommands();
|
||||
|
@ -615,7 +611,7 @@ void SerializeState(FSerializer& arc)
|
|||
int loaded = 0;
|
||||
if (arc.BeginObject("state"))
|
||||
{
|
||||
if (arc.isReading() && currentLevel->levelNumber == 20)
|
||||
if (arc.isReading() && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
|
||||
{
|
||||
InitEnergyTile();
|
||||
}
|
||||
|
@ -639,7 +635,6 @@ void SerializeState(FSerializer& arc)
|
|||
("bsnakecam", bSnakeCam)
|
||||
("slipmode", bSlipMode)
|
||||
("PlayClock", PlayClock)
|
||||
("cinemaseen", nCinemaSeen)
|
||||
("spiritsprite", nSpiritSprite)
|
||||
.EndObject();
|
||||
}
|
||||
|
|
|
@ -54,8 +54,6 @@ void SetHiRes();
|
|||
void BlackOut();
|
||||
|
||||
void DoGameOverScene(bool finallevel);
|
||||
void DoAfterCinemaScene(int nLevel, TArray<JobDesc> &jobs);
|
||||
void DoBeforeCinemaScene(int nLevel, TArray<JobDesc>& jobs);
|
||||
|
||||
int Query(short n, short l, ...);
|
||||
|
||||
|
@ -83,12 +81,11 @@ void DoSpiritHead();
|
|||
|
||||
void CheckKeys2();
|
||||
void GameTicker();
|
||||
void InitLevel(int);
|
||||
void InitLevel(MapRecord*);
|
||||
void InitNewGame();
|
||||
|
||||
int showmap(short nLevel, short nLevelNew, short nLevelBest);
|
||||
void menu_DoPlasma();
|
||||
void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray<JobDesc>& jobs);
|
||||
void DoEnergyTile();
|
||||
void InitEnergyTile();
|
||||
|
||||
|
@ -127,7 +124,6 @@ extern short nCurBodyNum;
|
|||
extern short nBodyTotal;
|
||||
|
||||
extern short bSnakeCam;
|
||||
extern uint8_t nCinemaSeen;
|
||||
|
||||
extern short nButtonColor;
|
||||
|
||||
|
@ -155,11 +151,6 @@ extern short bDoFlashes;
|
|||
|
||||
extern int bVanilla;
|
||||
|
||||
inline int PublisherLogo()
|
||||
{
|
||||
return (g_gameType & GAMEFLAG_EXHUMED) ? kTileBMGLogo : kTilePIELogo;
|
||||
}
|
||||
|
||||
inline int GameLogo()
|
||||
{
|
||||
return (g_gameType & GAMEFLAG_EXHUMED) ? kExhumedLogo : kPowerslaveLogo;
|
||||
|
@ -197,10 +188,11 @@ public:
|
|||
|
||||
void Start(double starttime);
|
||||
void ComputeCinemaText();
|
||||
void ReadyCinemaText(uint16_t nVal);
|
||||
void ReadyCinemaText(const char* nVal);
|
||||
void DisplayText();
|
||||
bool AdvanceCinemaText(double clock);
|
||||
void SetPalette(int pal) { currentCinemaPalette = pal; }
|
||||
void Create(const FString& text, int pal);
|
||||
};
|
||||
|
||||
|
||||
|
@ -229,7 +221,6 @@ struct GameInterface : ::GameInterface
|
|||
bool GenerateSavePic() override;
|
||||
void MenuOpened() override;
|
||||
void MenuSound(EMenuSounds snd) override;
|
||||
bool StartGame(FNewGameStartup& gs) override;
|
||||
FSavegameInfo GetSaveSig() override;
|
||||
void SerializeGameState(FSerializer& arc);
|
||||
bool CanSave() override;
|
||||
|
|
|
@ -49,32 +49,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "automap.h"
|
||||
#include "raze_music.h"
|
||||
#include "v_draw.h"
|
||||
#include "vm.h"
|
||||
|
||||
BEGIN_PS_NS
|
||||
|
||||
short nBestLevel;
|
||||
|
||||
extern uint8_t nCinemaSeen;
|
||||
|
||||
void RunCinemaScene(int num);
|
||||
void GameMove(void);
|
||||
void DrawClock();
|
||||
double calc_smoothratio();
|
||||
void DoTitle(CompletionFunc completion);
|
||||
|
||||
static void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray<JobDesc> &jobs)
|
||||
{
|
||||
if (nLevelNew == 5 && !(nCinemaSeen & 1)) {
|
||||
nCinemaSeen |= 1;
|
||||
DoBeforeCinemaScene(5, jobs);
|
||||
}
|
||||
|
||||
menu_DrawTheMap(nLevel, nLevelNew, nLevelBest, jobs);
|
||||
|
||||
if (nLevelNew == 11 && !(nCinemaSeen & 2)) {
|
||||
DoBeforeCinemaScene(11, jobs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GameInterface::Render()
|
||||
|
@ -83,7 +69,7 @@ void GameInterface::Render()
|
|||
drawtime.Reset();
|
||||
drawtime.Clock();
|
||||
|
||||
if (currentLevel && currentLevel->levelNumber == kMap20)
|
||||
if (currentLevel && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
|
||||
{
|
||||
DoEnergyTile();
|
||||
DrawClock();
|
||||
|
@ -127,104 +113,89 @@ void GameInterface::DrawBackground()
|
|||
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 0);
|
||||
}
|
||||
|
||||
void GameInterface::NextLevel(MapRecord *map, int skill)
|
||||
{
|
||||
InitLevel(map);
|
||||
|
||||
if (map->levelNumber >= nBestLevel)
|
||||
{
|
||||
nBestLevel = map->levelNumber - 1;
|
||||
}
|
||||
|
||||
STAT_NewLevel(currentLevel->labelName);
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void Intermission(MapRecord *from_map, MapRecord *to_map)
|
||||
{
|
||||
TArray<JobDesc> jobs;
|
||||
|
||||
if (from_map) StopAllSounds();
|
||||
bCamera = false;
|
||||
automapMode = am_off;
|
||||
|
||||
if (to_map)
|
||||
{
|
||||
if (to_map->levelNumber != 0)
|
||||
nBestLevel = to_map->levelNumber - 1;
|
||||
|
||||
STAT_Update(false);
|
||||
if (to_map->levelNumber == kMap20)
|
||||
nPlayerLives[0] = 0;
|
||||
|
||||
if (to_map->levelNumber == 0) // skip all intermission stuff when going to the training map.
|
||||
{
|
||||
gameaction = ga_nextlevel;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
DoAfterCinemaScene(to_map->levelNumber - 1, jobs);
|
||||
}
|
||||
if (to_map->levelNumber > -1 && to_map->levelNumber < kMap20)
|
||||
{
|
||||
// start a new game at the given level
|
||||
if (!nNetPlayerCount && to_map->levelNumber > 0)
|
||||
{
|
||||
showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs);
|
||||
}
|
||||
else
|
||||
jobs.Push({ Create<DBlackScreen>(1) }); // we need something in here even in the multiplayer case.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DoAfterCinemaScene(20, jobs);
|
||||
STAT_Update(true);
|
||||
}
|
||||
|
||||
|
||||
if (jobs.Size() > 0)
|
||||
{
|
||||
RunScreenJob(jobs.Data(), jobs.Size(), [=](bool)
|
||||
{
|
||||
if (!to_map) gameaction = ga_startup; // this was the end of the game
|
||||
else
|
||||
{
|
||||
if (to_map->levelNumber != selectedlevelnew)
|
||||
{
|
||||
// User can switch destination on the scrolling map.
|
||||
g_nextmap = FindMapByLevelNum(selectedlevelnew);
|
||||
STAT_Cancel();
|
||||
}
|
||||
gameaction = ga_nextlevel;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GameInterface::NextLevel(MapRecord *map, int skill)
|
||||
{
|
||||
InitLevel(map->levelNumber);
|
||||
|
||||
if (map->levelNumber > nBestLevel)
|
||||
{
|
||||
nBestLevel = selectedlevelnew;
|
||||
}
|
||||
|
||||
if (map->levelNumber == 11) nCinemaSeen |= 2;
|
||||
STAT_NewLevel(currentLevel->labelName);
|
||||
|
||||
}
|
||||
|
||||
void GameInterface::NewGame(MapRecord *map, int skill, bool frommenu)
|
||||
{
|
||||
// start a new game on the given level
|
||||
InitNewGame();
|
||||
if (map->levelNumber == 1) STAT_StartNewGame("Exhumed", 1);
|
||||
Intermission(nullptr, map);
|
||||
InitLevel(map);
|
||||
gameaction = ga_level;
|
||||
}
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *map, int skill)
|
||||
int selectedlevelnew;
|
||||
|
||||
DEFINE_ACTION_FUNCTION(DMapScreen, SetNextLevel)
|
||||
{
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(v);
|
||||
selectedlevelnew = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GameInterface::LevelCompleted(MapRecord *to_map, int skill)
|
||||
{
|
||||
Mus_Stop();
|
||||
if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu;
|
||||
Intermission(currentLevel, map);
|
||||
if (currentLevel->gameflags & LEVEL_EX_TRAINING)
|
||||
{
|
||||
gameaction = ga_mainmenu;
|
||||
return;
|
||||
}
|
||||
|
||||
StopAllSounds();
|
||||
bCamera = false;
|
||||
automapMode = am_off;
|
||||
|
||||
STAT_Update(to_map == nullptr);
|
||||
if (to_map)
|
||||
{
|
||||
if (to_map->levelNumber > nBestLevel) nBestLevel = to_map->levelNumber - 1;
|
||||
|
||||
if (to_map->gameflags & LEVEL_EX_COUNTDOWN) nPlayerLives[0] = 0;
|
||||
if (to_map->gameflags & LEVEL_EX_TRAINING)
|
||||
{
|
||||
gameaction = ga_nextlevel;
|
||||
return;
|
||||
}
|
||||
}
|
||||
SummaryInfo info{};
|
||||
info.kills = nCreaturesKilled;
|
||||
info.maxkills = nCreaturesTotal;
|
||||
info.supersecrets = nBestLevel;
|
||||
info.time = PlayClock * GameTicRate / 120;
|
||||
selectedlevelnew = to_map->levelNumber;
|
||||
ShowIntermission(currentLevel, to_map, &info, [=](bool)
|
||||
{
|
||||
if (!to_map) gameaction = ga_startup; // this was the end of the game
|
||||
else
|
||||
{
|
||||
if (to_map->levelNumber != selectedlevelnew)
|
||||
{
|
||||
// User can switch destination on the scrolling map.
|
||||
g_nextmap = FindMapByLevelNum(selectedlevelnew);
|
||||
STAT_Cancel();
|
||||
}
|
||||
gameaction = ga_nextlevel;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -237,22 +208,7 @@ void GameInterface::Startup()
|
|||
{
|
||||
resettiming();
|
||||
EndLevel = 0;
|
||||
|
||||
if (userConfig.CommandMap.IsNotEmpty())
|
||||
{
|
||||
/*
|
||||
auto map = FindMapByName(userConfig.CommandMap);
|
||||
if (map) DeferedStartMap(map, 0);
|
||||
userConfig.CommandMap = "";
|
||||
goto again;
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; });
|
||||
else gameaction = ga_mainmenu;
|
||||
}
|
||||
|
||||
PlayLogos(ga_mainmenu, ga_mainmenu, false);
|
||||
}
|
||||
|
||||
void GameInterface::ErrorCleanup()
|
||||
|
|
|
@ -66,9 +66,9 @@ uint8_t bIsVersion6 = true;
|
|||
|
||||
|
||||
|
||||
uint8_t LoadLevel(int nMap)
|
||||
uint8_t LoadLevel(MapRecord* map)
|
||||
{
|
||||
if (nMap == kMap20)
|
||||
if (map->gameflags & LEVEL_EX_COUNTDOWN)
|
||||
{
|
||||
lCountDown = 81000;
|
||||
nAlarmTicks = 30;
|
||||
|
@ -116,12 +116,12 @@ uint8_t LoadLevel(int nMap)
|
|||
InitItems();
|
||||
InitInput();
|
||||
|
||||
if (nMap == kMap20) {
|
||||
if (map->gameflags & LEVEL_EX_COUNTDOWN) {
|
||||
InitEnergyTile();
|
||||
}
|
||||
}
|
||||
|
||||
if (nMap > 15)
|
||||
if (map->gameflags & LEVEL_EX_ALTSOUND)
|
||||
{
|
||||
nSwitchSound = 35;
|
||||
nStoneSound = 23;
|
||||
|
@ -136,10 +136,6 @@ uint8_t LoadLevel(int nMap)
|
|||
nStopSound = 66;
|
||||
}
|
||||
|
||||
if (nMap < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vec3_t startPos;
|
||||
engineLoadBoard(currentLevel->fileName, 0, &startPos, &inita, &initsect);
|
||||
initx = startPos.x;
|
||||
|
@ -172,12 +168,12 @@ uint8_t LoadLevel(int nMap)
|
|||
return true;
|
||||
}
|
||||
|
||||
void InitLevel(int level) // todo: use a map record
|
||||
void InitLevel(MapRecord* map)
|
||||
{
|
||||
StopCD();
|
||||
currentLevel = FindMapByLevelNum(level);
|
||||
if (!LoadLevel(level)) {
|
||||
I_Error("Can't load level %d...\n", level);
|
||||
currentLevel = map;
|
||||
if (!LoadLevel(map)) {
|
||||
I_Error("Cannot load %s...\n", map->fileName.GetChars());
|
||||
}
|
||||
|
||||
for (int i = 0; i < nTotalPlayers; i++)
|
||||
|
@ -200,17 +196,14 @@ void InitLevel(int level) // todo: use a map record
|
|||
|
||||
RefreshStatus();
|
||||
|
||||
int nTrack = level;
|
||||
if (nTrack != 0) nTrack--;
|
||||
|
||||
playCDtrack((nTrack % 8) + 11, true);
|
||||
if (!mus_redbook && map->music.IsNotEmpty()) Mus_Play(map->labelName, map->music, true); // Allow non-CD music if defined for the current level
|
||||
playCDtrack(map->cdSongId, true);
|
||||
setLevelStarted(currentLevel);
|
||||
}
|
||||
|
||||
void InitNewGame()
|
||||
{
|
||||
bCamera = false;
|
||||
nCinemaSeen = 0;
|
||||
PlayerCount = 0;
|
||||
|
||||
for (int i = 0; i < nTotalPlayers; i++)
|
||||
|
|
|
@ -432,7 +432,7 @@ void StartRegenerate(short nSprite)
|
|||
pSprite->extra = 1350;
|
||||
pSprite->ang = nFirstRegenerate;
|
||||
|
||||
if (currentLevel->levelNumber <= kMap20)
|
||||
if (!(currentLevel->gameflags & LEVEL_EX_MULTI))
|
||||
{
|
||||
pSprite->ang /= 5;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue