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:
Christoph Oelckers 2021-05-03 18:53:54 +02:00
commit 6f19c415d6
166 changed files with 10829 additions and 5566 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -37,6 +37,7 @@
#include "dobject.h"
#include "vm.h"
#include "types.h"
#include "v_draw.h"
// We need one specific type for each of the 8 integral VM types and instantiate the needed functions for each of them.
// Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible.
@ -412,6 +413,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Push, ArrayPush<FDynArray_I32 COMMA
ACTION_RETURN_INT(self->Push(val));
}
DEFINE_ACTION_FUNCTION(FDynArray_I32, PushV)
{
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
PARAM_VA_POINTER(va_reginfo); // Get the hidden type information array
VMVa_List args = { param + 1, 0, numparam - 2, va_reginfo + 1 };
while (args.curindex < args.numargs)
{
if (args.reginfo[args.curindex] == REGT_INT)
{
self->Push(args.args[args.curindex++].i);
}
else if (args.reginfo[args.curindex] == REGT_FLOAT)
{
self->Push(int(args.args[args.curindex++].f));
}
else ThrowAbortException(X_OTHER, "Invalid parameter in pushv, int expected");
}
ACTION_RETURN_INT(self->Size()-1);
}
DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Pop, ArrayPop<FDynArray_I32>)
{
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);

View file

@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl)
ACTION_RETURN_FLOAT(self->ToDouble());
}
static void StringSplit(FString *self, TArray<FString> *tokens, const FString &delimiter, int keepEmpty)
static void StringSubst(FString *self, const FString &substr, const FString& replc)
{
self->Substitute(substr, replc);
}
DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Substitute, StringSubst)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(substr);
PARAM_STRING(replc);
StringSubst(self, substr, replc);
return 0;
}
static void StringSplit(FString* self, TArray<FString>* tokens, const FString& delimiter, int keepEmpty)
{
self->Split(*tokens, delimiter, static_cast<FString::EmptyTokenType>(keepEmpty));
}

View file

@ -49,6 +49,7 @@
#include "s_music.h"
#include "i_interface.h"
#include "base_sbar.h"
#include "image.h"
//==========================================================================
//
@ -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)

View file

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

View file

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

View file

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

View file

@ -69,11 +69,11 @@ extern int16_t sintable[2048];
//
//---------------------------------------------------------------------------
inline int32_t bsin(const int ang, const int8_t shift = 0)
inline int bsin(const int ang, const int shift = 0)
{
return shift < 0 ? sintable[ang & 2047] >> abs(shift) : sintable[ang & 2047] << shift;
}
inline double bsinf(const double ang, const int8_t shift = 0)
inline double bsinf(const double ang, const int shift = 0)
{
return g_sin(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
}
@ -85,11 +85,11 @@ inline double bsinf(const double ang, const int8_t shift = 0)
//
//---------------------------------------------------------------------------
inline int32_t bcos(const int ang, const int8_t shift = 0)
inline int bcos(const int ang, const int shift = 0)
{
return shift < 0 ? sintable[(ang + 512) & 2047] >> abs(shift) : sintable[(ang + 512) & 2047] << shift;
}
inline double bcosf(const double ang, const int8_t shift = 0)
inline double bcosf(const double ang, const int shift = 0)
{
return g_cos(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
}

View file

@ -242,7 +242,7 @@ void changeMap(int player, uint8_t** stream, bool skip)
void endScreenJob(int player, uint8_t** stream, bool skip)
{
if (!skip) EndScreenJob();
if (!skip) gameaction = ga_endscreenjob;
}
//---------------------------------------------------------------------------
@ -280,6 +280,7 @@ void DeferedStartGame(MapRecord* map, int skill, bool nostopsound)
static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, const char *t2)
{
int numparm = g_gameType & (GAMEFLAG_SW | GAMEFLAG_PSEXHUMED) ? 1 : 2; // Handle games with episodic and non-episodic level order.
if (numparm == 2 && argv.argc() == 2) numparm = 1;
if (argv.argc() <= numparm)
{
if (numparm == 2) Printf(PRINT_BOLD, "%s <e> <m>: %s episode 'e' and map 'm'\n", cmdname, t2);
@ -294,7 +295,7 @@ static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, cons
Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n");
return nullptr;
}
auto map = FindMapByLevelNum(numparm == 1 ? m : levelnum(e - 1, m - 1));
auto map = FindMapByIndex(e, m);
if (!map)
{
if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]);

1232
source/core/g_mapinfo.cpp Normal file

File diff suppressed because it is too large Load diff

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

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

View file

@ -73,6 +73,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "automap.h"
#include "v_draw.h"
#include "gi.h"
#include "vm.h"
#include "g_mapinfo.h"
#include "gamefuncs.h"
#include "hw_voxels.h"
#include "hw_palmanager.h"
@ -286,6 +288,11 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr)
UserConfig userConfig;
DEFINE_GLOBAL(userConfig)
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nomonsters)
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nosound)
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nologo)
void UserConfig::ProcessOptions()
{
// -help etc are omitted
@ -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()

View file

@ -70,12 +70,12 @@ extern UserConfig userConfig;
extern int nomusic;
extern bool nosound;
inline bool MusicEnabled()
inline int MusicEnabled() // int return is for scripting
{
return mus_enabled && !nomusic;
}
inline bool SoundEnabled()
inline int SoundEnabled()
{
return snd_enabled && !nosound;
}
@ -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,

View file

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

View file

@ -79,7 +79,7 @@ struct GameInterface
virtual void MenuSound(EMenuSounds snd) {}
virtual bool CanSave() { return true; }
virtual void CustomMenuSelection(int menu, int item) {}
virtual bool StartGame(FNewGameStartup& gs) { return false; }
virtual bool StartGame(FNewGameStartup& gs) { return true; }
virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; }
virtual double SmallFontScale() { return 1; }
virtual void SerializeGameState(FSerializer& arc) {}

View file

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

View file

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

View file

@ -41,43 +41,102 @@
#include "raze_sound.h"
FString gSkillNames[MAXSKILLS];
FString gVolumeNames[MAXVOLUMES];
FString gVolumeSubtitles[MAXVOLUMES];
int32_t gVolumeFlags[MAXVOLUMES];
int gDefaultVolume = 0, gDefaultSkill = 1;
MapRecord mapList[512];
MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
GlobalCutscenes globalCutscenes;
TArray<ClusterDef> clusters;
TArray<VolumeRecord> volumes;
TArray<TPointer<MapRecord>> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array.
MapRecord *currentLevel; // level that is currently played.
MapRecord* lastLevel; // Same here, for the last level.
unsigned int numUsedSlots;
CCMD(listmaps)
{
for (unsigned int i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
int lump = fileSystem.FindFile(mapList[i].fileName);
int lump = fileSystem.FindFile(map->fileName);
if (lump >= 0)
{
int rfnum = fileSystem.GetFileContainer(lump);
Printf("%s - %s (%s)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName(), fileSystem.GetResourceFileName(rfnum));
Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
}
else
{
Printf("%s - %s (defined but does not exist)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName());
Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName());
}
}
}
CCMD(mapinfo)
{
const char* mapname = nullptr;
if (argv.argc() > 1) mapname = argv[1];
if (!mapname)
{
for (auto& vol : volumes)
{
Printf("Volume %d\n\tName = '%s'\n\tstartmap = '%s'\n}\n", vol.index, vol.name.GetChars(), vol.startmap.GetChars());
}
for (auto& clus : clusters)
{
if (clus.intro.isdefined() || clus.outro.isdefined())
{
Printf("Cluster %d\n\tName = '%s'\n", clus.index, clus.name.GetChars());
if (clus.intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", clus.intro.function.GetChars());
if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", clus.intro.video.GetChars());
if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", clus.outro.function.GetChars());
if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", clus.outro.video.GetChars());
Printf("}\n");
}
}
}
for (auto& map : mapList)
{
if (mapname && map->labelName.CompareNoCase(mapname)) continue;
int lump = fileSystem.FindFile(map->fileName);
if (lump >= 0)
{
int rfnum = fileSystem.GetFileContainer(lump);
Printf("%s - %s (%s)\n{\n", map->fileName.GetChars(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
Printf("\tlevel number = %d\n\tCluster = %d\n\tIndex = %d\n", map->levelNumber, map->cluster, map->mapindex);
if (map->Author.IsNotEmpty()) Printf("\tAuthor = '%s'\n", map->Author.GetChars());
if (map->NextMap.IsNotEmpty()) Printf("\tNext map = '%s'\n", map->NextMap.GetChars());
if (map->NextSecret.IsNotEmpty()) Printf("\tNext secret map = '%s'\n", map->NextSecret.GetChars());
if (map->music.IsNotEmpty()) Printf("\tMusic = '%s:%d'", map->music.GetChars(), map->musicorder);
if (map->cdSongId > 0) Printf("\tCD track = %d\n", map->cdSongId);
if (map->parTime) Printf("\tPar Time = %d\n", map->parTime);
if (map->designerTime) Printf("\tPar Time = %d\n", map->designerTime);
if (map->intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", map->intro.function.GetChars());
if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", map->intro.video.GetChars());
if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", map->outro.function.GetChars());
if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", map->outro.video.GetChars());
Printf("}\n");
}
else
{
Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName());
}
}
}
int CutsceneDef::GetSound()
{
int id;
if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName);
if (id <= 0) id = soundEngine->FindSoundByResID(soundID);
return id;
}
MapRecord *FindMapByName(const char *nm)
{
for (unsigned i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
auto &map = mapList[i];
if (map.labelName.CompareNoCase(nm) == 0)
if (map->labelName.CompareNoCase(nm) == 0)
{
return &map;
return map.Data();
}
}
return nullptr;
@ -86,23 +145,78 @@ MapRecord *FindMapByName(const char *nm)
MapRecord *FindMapByLevelNum(int num)
{
for (unsigned i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
auto &map = mapList[i];
if (map.levelNumber == num)
if (map->levelNumber == num)
{
return &map;
return map.Data();
}
}
return nullptr;
}
MapRecord *FindNextMap(MapRecord *thismap)
VolumeRecord* FindVolume(int index)
{
if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel);
return FindMapByLevelNum(thismap->levelNumber+1);
for (auto& vol : volumes)
{
if (vol.index == index) return &vol;
}
return nullptr;
}
ClusterDef* FindCluster(int index)
{
for (auto& vol : clusters)
{
if (vol.index == index) return &vol;
}
return nullptr;
}
ClusterDef* AllocateCluster()
{
return &clusters[clusters.Reserve(1)];
}
VolumeRecord* AllocateVolume()
{
return &volumes[volumes.Reserve(1)];
}
MapRecord* FindMapByIndexOnly(int cluster, int num)
{
for (auto& map : mapList)
{
if (map->mapindex == num && map->cluster == cluster) return map.Data();
}
return nullptr;
}
MapRecord* FindMapByIndex(int cluster, int num)
{
auto map = FindMapByLevelNum(num);
if (!map) map = FindMapByIndexOnly(cluster, num); // modern definitions take precedence.
return map;
}
MapRecord* FindNextMap(MapRecord* thismap)
{
MapRecord* next = nullptr;
if (!thismap->NextMap.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
if (thismap->NextMap.IsNotEmpty()) next = FindMapByName(thismap->NextMap);
if (!next) next = FindMapByLevelNum(thismap->levelNumber + 1);
return next;
}
MapRecord* FindNextSecretMap(MapRecord* thismap)
{
MapRecord* next = nullptr;
if (!thismap->NextSecret.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
if (thismap->NextSecret.IsNotEmpty()) next = FindMapByName(thismap->NextSecret);
return next? next : FindNextMap(thismap);
}
bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
{
static const char* specials[] = { "intro", "briefing", "loading" };
@ -129,7 +243,7 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
if (numMatches != 4 || toupper(b1) != 'E' || toupper(b2) != 'L')
return false;
index = FindMapByLevelNum(levelnum(ep - 1, lev - 1));
index = FindMapByIndexOnly(ep, lev);
}
if (index != nullptr)
@ -142,18 +256,19 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
MapRecord *AllocateMap()
{
return &mapList[numUsedSlots++];
auto&p = mapList[mapList.Reserve(1)];
p.Alloc();
return p.Data();
}
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic)
{
for (unsigned i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
auto &map = mapList[i];
if (map.fileName.CompareNoCase(boardfilename) == 0)
if (map->fileName.CompareNoCase(boardfilename) == 0)
{
return &map;
return map.Data();
}
}

View file

@ -3,28 +3,58 @@
#include "gstrings.h"
#include "cmdlib.h"
#include "quotemgr.h"
#include "palentry.h"
#include "vectors.h"
#ifdef GetMessage
#undef GetMessage // Windows strikes...
#endif
enum EMax
{
MAXSKILLS = 7,
MAXVOLUMES = 7,
MAXMENUGAMEPLAYENTRIES = 7,
};
enum EVolFlags
{
EF_HIDEFROMSP = 1,
VF_HIDEFROMSP = 1,
VF_OPTIONAL = 2,
VF_SHAREWARELOCK = 4, // show in shareware but lock access.
VF_NOSKILL = 8,
};
enum EMapFlags
{
LEVEL_NOINTERMISSION = 1,
LEVEL_SECRETEXITOVERRIDE = 2, // when given an explicit level number, override with secret exit in the map, mainly for compiling episodes out of single levels.
LEVEL_CLEARINVENTORY = 4,
LEVEL_CLEARWEAPONS = 8,
LEVEL_FORCENOEOG = 16, // RR E1L7 needs this to override its boss's death ending the game.
};
enum EMapGameFlags
{
LEVEL_RR_HULKSPAWN = 1,
LEVEL_RR_CLEARMOONSHINE = 2,
LEVEL_EX_COUNTDOWN = 4,
LEVEL_EX_TRAINING = 8,
LEVEL_EX_ALTSOUND = 16,
LEVEL_EX_MULTI = 32,
LEVEL_SW_SPAWNMINES = 64,
LEVEL_SW_BOSSMETER_SERPENT = 128,
LEVEL_SW_BOSSMETER_SUMO = 256,
LEVEL_SW_BOSSMETER_ZILLA = 512,
LEVEL_SW_DEATHEXIT_SERPENT = 1024,
LEVEL_SW_DEATHEXIT_SUMO = 2048,
LEVEL_SW_DEATHEXIT_ZILLA = 4096,
};
// These get filled in by the map definition parsers of the front ends.
extern FString gSkillNames[MAXSKILLS];
extern FString gVolumeNames[MAXVOLUMES];
extern FString gVolumeSubtitles[MAXVOLUMES];
extern int32_t gVolumeFlags[MAXVOLUMES];
extern int gDefaultVolume, gDefaultSkill;
@ -46,6 +76,58 @@ enum {
MAX_MESSAGES = 32
};
class DObject;
struct MapRecord;
struct CutsceneDef
{
FString video;
FString function;
FString soundName;
int soundID = -1; // ResID not SoundID!
int framespersec = 0; // only relevant for ANM.
bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game.
void Create(DObject* runner);
bool Create(DObject* runner, MapRecord* map, bool transition);
bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); }
int GetSound();
};
struct GlobalCutscenes
{
CutsceneDef Intro;
CutsceneDef DefaultMapIntro;
CutsceneDef DefaultMapOutro;
CutsceneDef DefaultGameover;
CutsceneDef SharewareEnd;
CutsceneDef LoadingScreen;
FString MPSummaryScreen;
FString SummaryScreen;
};
struct ClusterDef
{
FString name; // What gets displayed for this cluster. In Duke this is normally the corresponding volume name but does not have to be.
CutsceneDef intro; // plays when entering this cluster
CutsceneDef outro; // plays when leaving this cluster
CutsceneDef gameover; // when defined, plays when the player dies in this cluster
FString InterBackground;
int index = -1;
int flags = 0; // engine and common flags
int gameflags = 0; // game specific flags.
};
struct VolumeRecord // episodes
{
FString startmap;
FString name;
FString subtitle;
int index = -1;
int flags = 0;
int shortcut = 0;
};
struct MapRecord
{
int parTime = 0;
@ -54,16 +136,39 @@ struct MapRecord
FString labelName;
FString name;
FString music;
FString Author;
FString NextMap;
FString NextSecret;
int cdSongId = -1;
int musicorder = -1;
CutsceneDef intro;
CutsceneDef outro;
int flags = 0;
int gameflags = 0;
int levelNumber = -1;
int mapindex = -1; // index in the episode. This only for finding the next map in the progression when nothing explicit is defined.
int cluster = -1;
PalEntry fadeto = 0;
int fogdensity = 0;
int skyfog = 0;
FString BorderTexture;
FString InterBackground;
TArray<FString> PrecacheTextures;
FVector4 skyrotatevector;
// The rest is only used by Blood
int nextLevel = -1;
int nextSecret = -1;
FString messages[MAX_MESSAGES];
FString author;
int8_t fog = -1, weather = -1; // Blood defines these but they aren't used.
// game specific stuff
int rr_startsound = 0;
int rr_mamaspawn = 15;
int ex_ramses_horiz = 11;
int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue!
FString ex_ramses_pup;
FString ex_ramses_text;
const char* LabelName() const
{
@ -97,39 +202,65 @@ struct MapRecord
{
messages[num] = msg;
}
};
struct SummaryInfo
{
int kills;
int maxkills;
int secrets;
int maxsecrets;
int supersecrets;
int time;
int playercount;
bool cheated;
bool endofgame;
};
extern MapRecord mapList[512];
extern GlobalCutscenes globalCutscenes;
extern MapRecord *currentLevel;
bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false);
MapRecord *FindMapByName(const char *nm);
MapRecord *FindMapByLevelNum(int num);
MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable.
MapRecord* FindMapByIndex(int clst, int num);
MapRecord *FindNextMap(MapRecord *thismap);
MapRecord* FindNextSecretMap(MapRecord* thismap);
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
MapRecord* AllocateMap();
VolumeRecord* FindVolume(int index);
ClusterDef* FindCluster(int index);
ClusterDef* AllocateCluster();
VolumeRecord* AllocateVolume();
void SetLevelNum(MapRecord* info, int num);
inline VolumeRecord* MustFindVolume(int index)
{
auto r = FindVolume(index);
if (r) return r;
r = AllocateVolume();
r->index = index;
return r;
}
inline ClusterDef* MustFindCluster(int index)
{
auto r = FindCluster(index);
if (r) return r;
r = AllocateCluster();
r->index = index;
return r;
}
// These should be the only places converting between level numbers and volume/map pairs
constexpr inline int levelnum(int vol, int map)
constexpr inline int makelevelnum(int vol, int map)
{
return vol * 1000 + map;
}
constexpr inline int volfromlevelnum(int num)
{
return num >= 0 ? num / 1000 : 0;
}
constexpr inline int mapfromlevelnum(int num)
{
return num >= 0 ? num % 1000 : -1;
}
enum
{
RRENDSLOT = 127

View file

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

View file

@ -83,8 +83,31 @@ bool help_disabled;
FNewGameStartup NewGameStartupInfo;
//FNewGameStartup NewGameStartupInfo;
static bool DoStartGame(FNewGameStartup& gs)
{
auto vol = FindVolume(gs.Episode);
if (!vol) return false;
if (isShareware() && (vol->flags & VF_SHAREWARELOCK))
{
M_StartMessage(GStrings("SHAREWARELOCK"), 1, NAME_None);
return false;
}
auto map = FindMapByName(vol->startmap);
if (!map) return false;
soundEngine->StopAllChannels();
gi->StartGame(gs); // play game specific effects (like Duke/RR/SW's voice lines when starting a game.)
DeferedStartGame(map, gs.Skill);
return true;
}
bool M_SetSpecialMenu(FName& menu, int param)
{
@ -115,13 +138,19 @@ bool M_SetSpecialMenu(FName& menu, int param)
case NAME_Startgame:
case NAME_StartgameNoSkill:
menu = NAME_Startgame;
NewGameStartupInfo.Skill = param;
if (menu == NAME_StartgameNoSkill) NewGameStartupInfo.Episode = param;
if (gi->StartGame(NewGameStartupInfo))
if (menu == NAME_StartgameNoSkill)
{
menu = NAME_Startgame;
NewGameStartupInfo.Episode = param;
NewGameStartupInfo.Skill = 1;
}
if (DoStartGame(NewGameStartupInfo))
{
M_ClearMenus();
STAT_StartNewGame(gVolumeNames[NewGameStartupInfo.Episode], NewGameStartupInfo.Skill);
int ep = NewGameStartupInfo.Episode;
auto vol = FindVolume(ep);
if (vol) STAT_StartNewGame(vol->name, NewGameStartupInfo.Skill);
inputState.ClearAllInput();
}
return false;
@ -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;

View file

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

View file

@ -39,8 +39,10 @@
#include "hw_material.h"
#include "gamestruct.h"
#include "gamecontrol.h"
#include "texturemanager.h"
#include "hw_models.h"
#include "hw_voxels.h"
#include "mapinfo.h"
BEGIN_BLD_NS
extern short voxelIndex[MAXTILES];
@ -129,6 +131,19 @@ void precacheMarkedTiles()
int dapalnum = pair->Key >> 32;
doprecache(dapicnum, dapalnum);
}
// Cache everything the map explicitly declares.
TMap<FString, bool> cachetexmap;
for (auto& tex : currentLevel->PrecacheTextures) cachetexmap.Insert(tex, true);
decltype(cachetexmap)::Iterator it2(cachetexmap);
decltype(cachetexmap)::Pair* pair2;
while (it2.NextPair(pair2))
{
auto tex = TexMan.FindGameTexture(pair2->Key, ETextureType::Any);
if (tex) PrecacheTex(tex, 0);
}
cachemap.Clear();
}

View file

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

View file

@ -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 = &sections[firstsection];
Section* sect2 = &sections[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);
}

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

View file

@ -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 = &sectionLines[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], &sector[bunch->sectnum], wall[i].nextsector < 0 ? nullptr : &sector[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], &sector[bunch->sectnum], wall[ww].nextsector < 0 ? nullptr : &sector[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 = &sector[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, &sector[sectnum]);
flat.ProcessSector(di, &sector[sectnum], sectionnum);
SetupFlat.Unclock();
//Todo: process subsectors
inbunch = false;
for (int i = 0; i < sect->wallnum; i++)
auto section = &sections[sectionnum];
for (unsigned i = 0; i < section->lines.Size(); i++)
{
auto thiswall = &wall[sect->wallptr + i];
auto thisline = &sectionLines[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();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -51,59 +51,371 @@
#include <vpx/vpx_decoder.h>
#include <vpx/vp8dx.h>
#include "raze_music.h"
#include "vm.h"
#include "mapinfo.h"
static DObject* runner;
static SummaryInfo sinfo;
static PClass* runnerclass;
static PType* runnerclasstype;
static PType* maprecordtype;
static PType* summaryinfotype;
static CompletionFunc completion;
static int ticks;
static SummaryInfo summaryinfo;
IMPLEMENT_CLASS(DScreenJob, true, false)
IMPLEMENT_CLASS(DImageScreen, true, false)
bool DSkippableScreenJob::OnEvent(event_t* evt)
{
if (evt->type == EV_KeyDown && !specialKeyEvent(evt))
{
state = skipped;
Skipped();
}
return true;
}
void DBlackScreen::OnTick()
{
if (cleared)
{
int span = ticks * 1000 / GameTicRate;
if (span > wait) state = finished;
}
}
void DBlackScreen::Draw(double)
{
cleared = true;
twod->ClearScreen();
}
//---------------------------------------------------------------------------
//=============================================================================
//
//
//
//---------------------------------------------------------------------------
//=============================================================================
void DImageScreen::OnTick()
void Job_Init()
{
if (cleared)
static bool done = false;
if (!done)
{
int span = ticks * 1000 / GameTicRate;
if (span > waittime) state = finished;
done = true;
GC::AddMarkerFunc([] { GC::Mark(runner); });
}
runnerclass = PClass::FindClass("ScreenJobRunner");
if (!runnerclass) I_FatalError("ScreenJobRunner not defined");
runnerclasstype = NewPointer(runnerclass);
maprecordtype = NewPointer(NewStruct("MapRecord", nullptr, true));
summaryinfotype = NewPointer(NewStruct("SummaryInfo", nullptr, true));
}
//=============================================================================
//
//
//
//=============================================================================
static VMFunction* LookupFunction(const char* qname, bool validate = true)
{
int p = strcspn(qname, ".");
if (p == 0) I_Error("Call to undefined function %s", qname);
FString clsname(qname, p);
FString funcname = qname + p + 1;
auto func = PClass::FindFunction(clsname, funcname);
if (func == nullptr) I_Error("Call to undefined function %s", qname);
if (validate)
{
// these conditions must be met by all functions for this interface.
if (func->Proto->ReturnTypes.Size() != 0) I_Error("Bad cutscene function %s. Return value not allowed", qname);
if (func->ImplicitArgs != 0) I_Error("Bad cutscene function %s. Must be static", qname);
}
return func;
}
//=============================================================================
//
//
//
//=============================================================================
void CallCreateFunction(const char* qname, DObject* runner)
{
auto func = LookupFunction(qname);
if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname);
if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname);
VMValue val = runner;
VMCall(func, &val, 1, nullptr, 0);
}
//=============================================================================
//
//
//
//=============================================================================
void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map)
{
auto func = LookupFunction(qname);
if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here.
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype)
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname);
VMValue val[2] = { runner, map };
VMCall(func, val, 2, nullptr, 0);
}
//=============================================================================
//
//
//
//=============================================================================
void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* map, SummaryInfo* info, MapRecord* map2)
{
auto func = LookupFunction(qname);
auto s = func->Proto->ArgumentTypes.Size();
auto at = func->Proto->ArgumentTypes.Data();
if (s != 3 && s != 4) I_Error("Bad map-cutscene function %s. Must receive precisely three or four arguments.", qname);
if (at[0] != runnerclasstype && at[1] != maprecordtype && at[2] != summaryinfotype && (s == 3 || at[3] == maprecordtype))
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference,", qname);
if (info) summaryinfo = *info; // must be copied to a persistent location.
else summaryinfo = {};
VMValue val[] = { runner, map, &summaryinfo, map2 };
VMCall(func, val, s, nullptr, 0);
}
//=============================================================================
//
//
//
//=============================================================================
DObject* CreateRunner(bool clearbefore = true)
{
auto obj = runnerclass->CreateNew();
auto func = LookupFunction("ScreenJobRunner.Init", false);
VMValue val[3] = { obj, clearbefore, false };
VMCall(func, val, 3, nullptr, 0);
return obj;
}
//=============================================================================
//
//
//
//=============================================================================
void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps)
{
auto obj = runnerclass->CreateNew();
auto func = LookupFunction("ScreenJobRunner.AddGenericVideo", false);
VMValue val[] = { runner, &fn, soundid, fps };
VMCall(func, val, 4, nullptr, 0);
}
//=============================================================================
//
//
//
//=============================================================================
void CutsceneDef::Create(DObject* runner)
{
if (function.IsNotEmpty())
{
CallCreateFunction(function, runner);
}
else if (video.IsNotEmpty())
{
AddGenericVideo(runner, video, GetSound(), framespersec);
}
}
//=============================================================================
//
//
//
//=============================================================================
void DImageScreen::Draw(double smoothratio)
bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition)
{
if (tilenum > 0) tex = tileGetTexture(tilenum, true);
twod->ClearScreen();
if (tex) DrawTexture(twod, tex, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans, TAG_DONE);
cleared = true;
if (!transition && transitiononly) return false;
if (function.CompareNoCase("none") == 0)
return true; // play nothing but return as being validated
if (function.IsNotEmpty())
{
CallCreateMapFunction(function, runner, map);
return true;
}
else if (video.IsNotEmpty())
{
AddGenericVideo(runner, video, GetSound(), framespersec);
return true;
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DeleteScreenJob()
{
if (runner) runner->Destroy();
runner = nullptr;
}
void EndScreenJob()
{
DeleteScreenJob();
if (completion) completion(false);
completion = nullptr;
}
//=============================================================================
//
//
//
//=============================================================================
bool ScreenJobResponder(event_t* ev)
{
if (ev->type == EV_KeyDown)
{
// We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
auto binding = Bindings.GetBinding(ev->data1);
if (binding.CompareNoCase("toggleconsole") == 0)
{
C_ToggleConsole();
return true;
}
}
FInputEvent evt = ev;
if (runner)
{
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent)
{
int result = 0;
VMValue parm[] = { runner, &evt };
VMReturn ret(&result);
VMCall(func, parm, 2, &ret, 1);
return result;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool ScreenJobTick()
{
ticks++;
if (runner)
{
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick)
{
int result = 0;
VMValue parm[] = { runner };
VMReturn ret(&result);
VMCall(func, parm, 1, &ret, 1);
return result;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void ScreenJobDraw()
{
double smoothratio = I_GetTimeFrac();
if (runner)
{
twod->ClearScreen();
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame)
{
VMValue parm[] = { runner, smoothratio };
VMCall(func, parm, 2, nullptr, 0);
}
}
}
//=============================================================================
//
//
//
//=============================================================================
bool ScreenJobValidate()
{
if (runner)
{
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate)
{
int res;
VMValue parm[] = { runner };
VMReturn ret(&res);
VMCall(func, parm, 2, &ret, 1);
return res;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_)
{
if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0)
{
completion = completion_;
runner = CreateRunner();
GC::WriteBarrier(runner);
try
{
cs.Create(runner);
if (!ScreenJobValidate())
{
runner->Destroy();
runner = nullptr;
return false;
}
if (flags & SJ_DELAY) intermissiondelay = 10; // need to wait a bit at the start to let the timer catch up.
else intermissiondelay = 0;
gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission;
}
catch (...)
{
if (runner) runner->Destroy();
runner = nullptr;
throw;
}
return true;
}
return false;
}
bool StartCutscene(const char* s, int flags, const CompletionFunc& completion)
{
CutsceneDef def;
def.function = s;
return StartCutscene(def, 0, completion);
}
//=============================================================================
//
//
//
//=============================================================================
void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic)
{
Mus_Stop();
FX_StopAllSounds(); // JBF 20031228
if (userConfig.nologo)
{
gameaction = def_ga;
}
else
{
if (!StartCutscene(globalCutscenes.Intro, SJ_BLOCKUI|SJ_DELAY, [=](bool) {
gameaction = complete_ga;
})) gameaction = def_ga;
}
}
//---------------------------------------------------------------------------
@ -112,269 +424,137 @@ void DImageScreen::Draw(double smoothratio)
//
//---------------------------------------------------------------------------
class ScreenJobRunner
void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
{
enum
{
State_Clear,
State_Run,
State_Fadeout
};
TArray<JobDesc> jobs;
CompletionFunc completion;
int index = -1;
float screenfade;
bool clearbefore;
int actionState;
int terminateState;
int fadeticks = 0;
int last_paused_tic = -1;
public:
ScreenJobRunner(JobDesc* jobs_, int count, CompletionFunc completion_, bool clearbefore_)
: completion(std::move(completion_)), clearbefore(clearbefore_)
{
jobs.Resize(count);
memcpy(jobs.Data(), jobs_, count * sizeof(JobDesc));
// Release all jobs from the garbage collector - the code as it is cannot deal with them getting collected. This should be removed later once the GC is working.
for (int i = 0; i < count; i++)
{
jobs[i].job->Release();
}
AdvanceJob(false);
}
~ScreenJobRunner()
{
DeleteJobs();
}
void DeleteJobs()
{
for (auto& job : jobs)
{
job.job->ObjectFlags |= OF_YesReallyDelete;
delete job.job;
}
jobs.Clear();
}
void AdvanceJob(bool skip)
{
if (index >= 0)
{
if (jobs[index].postAction) jobs[index].postAction();
jobs[index].job->Destroy();
}
index++;
while (index < jobs.Size() && (jobs[index].job == nullptr || (skip && jobs[index].ignoreifskipped)))
{
if (jobs[index].job != nullptr) jobs[index].job->Destroy();
index++;
}
actionState = clearbefore ? State_Clear : State_Run;
if (index < jobs.Size())
{
jobs[index].job->fadestate = !paused && jobs[index].job->fadestyle & DScreenJob::fadein? DScreenJob::fadein : DScreenJob::visible;
jobs[index].job->Start();
}
inputState.ClearAllInput();
}
int DisplayFrame(double smoothratio)
{
auto& job = jobs[index];
auto now = I_GetTimeNS();
bool processed = job.job->ProcessInput();
if (job.job->fadestate == DScreenJob::fadein)
{
double ms = (job.job->ticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime;
float screenfade = (float)clamp(ms, 0., 1.);
twod->SetScreenFade(screenfade);
if (screenfade == 1.f) job.job->fadestate = DScreenJob::visible;
}
int state = job.job->DrawFrame(smoothratio);
twod->SetScreenFade(1.f);
return state;
}
int FadeoutFrame(double smoothratio)
{
auto& job = jobs[index];
double ms = (fadeticks + smoothratio) * 1000 / GameTicRate / job.job->fadetime;
float screenfade = 1.f - (float)clamp(ms, 0., 1.);
twod->SetScreenFade(screenfade);
job.job->DrawFrame(1.);
return (screenfade > 0.f);
}
bool OnEvent(event_t* ev)
{
if (paused || index >= jobs.Size()) return false;
if (ev->type == EV_KeyDown)
{
// We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here.
auto binding = Bindings.GetBinding(ev->data1);
if (binding.CompareNoCase("toggleconsole") == 0)
{
C_ToggleConsole();
return true;
}
}
if (jobs[index].job->state != DScreenJob::running) return false;
return jobs[index].job->OnEvent(ev);
}
void OnFinished()
completion = completion_;
runner = CreateRunner();
GC::WriteBarrier(runner);
const char* qname = globalCutscenes.MPSummaryScreen;
auto func = LookupFunction(qname);
if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname);
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != TypeSInt32)
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname);
VMValue val[2] = { runner, numplayers };
VMCall(func, val, 2, nullptr, 0);
if (!ScreenJobValidate())
{
runner->Destroy();
runner = nullptr;
if (completion) completion(false);
completion = nullptr; // only finish once.
completion = nullptr;
return;
}
gameaction = ga_intermission;
}
void OnTick()
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_)
{
completion = completion_;
runner = CreateRunner();
GC::WriteBarrier(runner);
// retrieve cluster relations for cluster-based cutscenes.
ClusterDef* fromcluster = nullptr, *tocluster = nullptr;
if (fromMap) fromcluster = FindCluster(fromMap->cluster);
if (toMap) tocluster = FindCluster(toMap->cluster);
if (fromcluster == tocluster) fromcluster = tocluster = nullptr;
try
{
if (paused) return;
if (index >= jobs.Size())
if (fromMap)
{
//DeleteJobs();
//twod->SetScreenFade(1);
//twod->ClearScreen(); // This must not leave the 2d buffer empty.
//if (gamestate == GS_INTRO) OnFinished();
//else Net_WriteByte(DEM_ENDSCREENJOB); // intermissions must be terminated synchronously.
if (!fromMap->outro.Create(runner, fromMap, !!toMap))
{
if (fromcluster == nullptr || !fromcluster->outro.Create(runner, fromMap, !!toMap))
globalCutscenes.DefaultMapOutro.Create(runner, fromMap, !!toMap);
}
}
if (fromMap || (g_gameType & GAMEFLAG_PSEXHUMED))
CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info, toMap);
if (toMap)
{
if (!toMap->intro.Create(runner, toMap, !!fromMap))
{
if (tocluster == nullptr || !tocluster->intro.Create(runner, toMap, !!fromMap))
globalCutscenes.DefaultMapIntro.Create(runner, toMap, !!fromMap);
}
globalCutscenes.LoadingScreen.Create(runner, toMap, true);
}
else if (isShareware())
{
globalCutscenes.SharewareEnd.Create(runner);
}
if (!ScreenJobValidate())
{
runner->Destroy();
runner = nullptr;
if (completion) completion(false);
completion = nullptr;
return;
}
gameaction = ga_intermission;
}
catch (...)
{
if (runner) runner->Destroy();
runner = nullptr;
throw;
}
}
CCMD(testcutscene)
{
if (argv.argc() < 2)
{
Printf("Usage: testcutscene <buildfunction>\n");
return;
}
try
{
if (StartCutscene(argv[1], 0, [](bool) {}))
{
C_HideConsole();
}
}
catch (const CRecoverableError& err)
{
Printf(TEXTCOLOR_RED "Unable to play cutscene: %s\n", err.what());
}
}
/*
Blood:
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
else
{
if (jobs[index].job->state == DScreenJob::running)
gameaction = ga_mainmenu;
}
RunScreenJob(jobs, [](bool) {
Mus_Stop();
gameaction = ga_mainmenu;
}, SJ_BLOCKUI);
Exhumed:
if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; });
else gameaction = ga_mainmenu;
SW:
if (!userConfig.nologo) Logo([](bool)
{
jobs[index].job->ticks++;
jobs[index].job->OnTick();
}
else if (jobs[index].job->state == DScreenJob::stopping)
{
fadeticks++;
}
}
}
bool RunFrame()
{
if (index >= jobs.Size())
{
DeleteJobs();
twod->SetScreenFade(1);
twod->ClearScreen(); // This must not leave the 2d buffer empty.
if (completion) completion(false);
return false;
}
// ensure that we won't go back in time if the menu is dismissed without advancing our ticker
bool menuon = paused;
if (menuon) last_paused_tic = jobs[index].job->ticks;
else if (last_paused_tic == jobs[index].job->ticks) menuon = true;
double smoothratio = menuon ? 1. : I_GetTimeFrac();
if (actionState == State_Clear)
{
actionState = State_Run;
twod->ClearScreen();
}
else if (actionState == State_Run)
{
terminateState = DisplayFrame(smoothratio);
if (terminateState < 1)
{
// Must lock before displaying.
if (jobs[index].job->fadestyle & DScreenJob::fadeout)
{
jobs[index].job->fadestate = DScreenJob::fadeout;
jobs[index].job->state = DScreenJob::stopping;
actionState = State_Fadeout;
fadeticks = 0;
}
else
{
AdvanceJob(terminateState < 0);
}
}
}
else if (actionState == State_Fadeout)
{
int ended = FadeoutFrame(smoothratio);
if (ended < 1)
{
jobs[index].job->state = DScreenJob::stopped;
AdvanceJob(terminateState < 0);
}
}
return true;
}
};
ScreenJobRunner *runner;
void RunScreenJob(JobDesc* jobs, int count, CompletionFunc completion, bool clearbefore, bool blockingui)
{
assert(completion != nullptr);
videoclearFade();
if (count)
{
runner = new ScreenJobRunner(jobs, count, completion, clearbefore);
gameaction = blockingui? ga_intro : ga_intermission;
}
else
{
completion(false);
}
}
void DeleteScreenJob()
{
if (runner)
{
delete runner;
runner = nullptr;
}
twod->SetScreenFade(1);
}
void EndScreenJob()
{
if (runner) runner->OnFinished();
DeleteScreenJob();
}
bool ScreenJobResponder(event_t* ev)
{
if (runner) return runner->OnEvent(ev);
return false;
}
void ScreenJobTick()
{
if (runner) runner->OnTick();
}
bool ScreenJobDraw()
{
// we cannot recover from this because we have no completion callback to call.
if (!runner)
{
// We can get here before a gameaction has been processed. In that case just draw a black screen and wait.
if (gameaction == ga_nothing) I_Error("Trying to run a non-existent screen job");
twod->ClearScreen();
return false;
}
auto res = runner->RunFrame();
if (!res)
{
assert((gamestate != GS_INTERMISSION && gamestate != GS_INTRO) || gameaction != ga_nothing);
DeleteScreenJob();
}
return res;
}
gameaction = ga_mainmenunostopsound;
});
else gameaction = ga_mainmenu;
*/

View file

@ -3,153 +3,29 @@
#include "dobject.h"
#include "v_2ddrawer.h"
#include "d_eventbase.h"
#include "s_soundinternal.h"
#include "gamestate.h"
using CompletionFunc = std::function<void(bool)>;
struct JobDesc;
class ScreenJobRunner;
class DScreenJob : public DObject
void Job_Init();
enum
{
DECLARE_CLASS(DScreenJob, DObject)
const int fadestyle;
const float fadetime; // in milliseconds
int fadestate = fadein;
friend class ScreenJobRunner;
protected:
int ticks = 0;
int state = running;
bool pausable = true;
public:
enum
{
running = 1, // normal operation
skipped = 2, // finished by user skipping
finished = 3, // finished by completing its sequence
stopping = 4, // running ending animations / fadeout, etc. Will not accept more input.
stopped = 5, // we're done here.
};
enum
{
visible = 0,
fadein = 1,
fadeout = 2,
};
DScreenJob(int fade = 0, float fadet = 250.f) : fadestyle(fade), fadetime(fadet) {}
virtual bool ProcessInput()
{
return false;
}
virtual void Start() {}
virtual bool OnEvent(event_t* evt) { return false; }
virtual void OnTick() { /*state = finished;*/ }
virtual void Draw(double smoothratio) {}
int DrawFrame(double smoothratio)
{
if (state != running) smoothratio = 1; // this is necessary because the ticker won't be incremented anymore to avoid having a negative time span.
Draw(smoothratio);
if (state == skipped) return -1;
if (state == finished) return 0;
return 1;
}
int GetFadeState() const { return fadestate; }
SJ_BLOCKUI = 1,
SJ_DELAY = 2,
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DSkippableScreenJob : public DScreenJob
{
protected:
DSkippableScreenJob(int fade = 0, float fadet = 250.f) : DScreenJob(fade, fadet)
{}
bool OnEvent(event_t* evt) override;
virtual void Skipped() {}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DBlackScreen : public DScreenJob
{
int wait;
bool cleared = false;
public:
DBlackScreen(int w) : wait(w) {}
void OnTick() override;
void Draw(double smooth) override;
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DImageScreen : public DSkippableScreenJob
{
DECLARE_CLASS(DImageScreen, DScreenJob)
int tilenum = -1;
int trans;
int waittime; // in ms.
bool cleared = false;
FGameTexture* tex = nullptr;
public:
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
{
tex = tile;
trans = translation;
}
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
{
tilenum = tile;
trans = translation;
}
void OnTick() override;
void Draw(double smooth) override;
};
struct JobDesc
{
DScreenJob* job;
void (*postAction)();
bool ignoreifskipped;
};
void RunScreenJob(JobDesc *jobs, int count, CompletionFunc completion, bool clearbefore = true, bool blockingui = false);
void EndScreenJob();
void DeleteScreenJob();
bool ScreenJobResponder(event_t* ev);
void ScreenJobTick();
bool ScreenJobDraw();
bool ScreenJobTick();
void ScreenJobDraw();
struct AnimSound
{
int framenum;
int soundnum;
};
DScreenJob *PlayVideo(const char *filename, const AnimSound *ans = nullptr, const int *frameticks = nullptr, bool nosoundstop = false);
struct CutsceneDef;
struct MapRecord;
struct SummaryInfo;
bool StartCutscene(const char* s, int flags, const CompletionFunc& completion);
void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic);
void ShowScoreboard(int numplayers, const CompletionFunc& completion_);
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_);

View file

@ -398,6 +398,12 @@ static TArray<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);

View file

@ -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 = &sector[secnum];
int numvertices = sec->wallnum;
auto sec = &sections[secnum];
auto sectorp = &sector[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 = &sectionLines[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 = &sections[secnum];
auto sectorp = &sector[sec->sector];
int numvertices = sec->lines.Size();
// Convert our sector into something the node builder understands
auto sect = &sector[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 = &sectionLines[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 = &sector[secnum];
auto sec = &sector[sections[secnum].sector];
auto compare = &data[secnum].compare[plane];
if (plane == 0)
{

View file

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

View file

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

View file

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

View file

@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "v_draw.h"
#include "texturemanager.h"
#include "statusbar.h"
#include "vm.h"
BEGIN_BLD_NS
@ -79,7 +80,7 @@ void EndLevel(void)
seqKillAll();
}
void StartLevel(MapRecord* level)
void StartLevel(MapRecord* level, bool newgame)
{
if (!level) return;
gFrameCount = 0;
@ -96,14 +97,14 @@ void StartLevel(MapRecord* level)
///////
}
#if 0
else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel))
else if (gGameOptions.nGameType > 0 && newgame)
{
// todo
gBlueFlagDropped = false;
gRedFlagDropped = false;
}
#endif
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
if (!newgame)
{
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
@ -180,13 +181,13 @@ void StartLevel(MapRecord* level)
evInit();
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
if (!(gGameOptions.uGameFlags & GF_AdvanceLevel))
if (newgame)
{
playerInit(i, 0);
}
playerStart(i, 1);
}
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
if (!newgame)
{
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
@ -203,7 +204,6 @@ void StartLevel(MapRecord* level)
pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon;
}
}
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
PreloadCache();
InitMirrors();
trInit();
@ -222,43 +222,24 @@ void StartLevel(MapRecord* level)
}
void NewLevel(MapRecord *sng, int skill)
void NewLevel(MapRecord *sng, int skill, bool newgame)
{
auto completion = [=](bool = false)
{
if (skill != -1) gGameOptions.nDifficulty = skill;
gSkill = gGameOptions.nDifficulty;
StartLevel(sng);
gameaction = ga_level;
};
bool startedCutscene = false;
if (!(sng->flags & MI_USERMAP))
{
int episode = volfromlevelnum(sng->levelNumber);
int level = mapfromlevelnum(sng->levelNumber);
if (gEpisodeInfo[episode].cutALevel == level && gEpisodeInfo[episode].cutsceneAName[0])
{
levelPlayIntroScene(episode, completion);
startedCutscene = true;
}
}
if (!startedCutscene) completion(false);
if (skill != -1) gGameOptions.nDifficulty = skill;
gSkill = gGameOptions.nDifficulty;
StartLevel(sng, newgame);
gameaction = ga_level;
}
void GameInterface::NewGame(MapRecord *sng, int skill, bool)
{
gGameOptions.uGameFlags = 0;
cheatReset();
NewLevel(sng, skill);
NewLevel(sng, skill, true);
}
void GameInterface::NextLevel(MapRecord *map, int skill)
{
gGameOptions.uGameFlags = GF_AdvanceLevel;
NewLevel(map, skill);
NewLevel(map, skill, false);
}
void GameInterface::Ticker()
@ -328,40 +309,12 @@ void GameInterface::Ticker()
team_ticker[i] = 0;
}
if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0)
if (gGameOptions.uGameFlags & GF_AdvanceLevel)
{
gGameOptions.uGameFlags &= ~GF_AdvanceLevel;
seqKillAll();
if (gGameOptions.uGameFlags & GF_EndGame)
{
STAT_Update(true);
if (gGameOptions.nGameType == 0)
{
auto completion = [](bool) {
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
gameaction = ga_creditsmenu;
};
if (gGameOptions.uGameFlags & GF_PlayCutscene)
{
levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion);
}
else completion(false);
}
else
{
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
}
}
else
{
STAT_Update(false);
EndLevel();
Mus_Stop();
// Fixme: Link maps, not episode/level pairs.
int ep = volfromlevelnum(currentLevel->levelNumber);
auto map = FindMapByLevelNum(levelnum(ep, gNextLevel));
CompleteLevel(map);
}
STAT_Update(gNextLevel == nullptr);
CompleteLevel(gNextLevel);
}
r_NoInterpolate = false;
}
@ -471,11 +424,12 @@ void GameInterface::app_init()
levelLoadDefaults();
LoadDefinitions();
//---------
SetTileNames();
C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25);
TileFiles.SetBackup();
powerupInit();
Printf(PRINT_NONOTIFY, "Loading cosine table\n");
trigInit();
Printf(PRINT_NONOTIFY, "Initializing view subsystem\n");
@ -485,15 +439,15 @@ void GameInterface::app_init()
Printf(PRINT_NONOTIFY, "Initializing weapon animations\n");
WeaponInit();
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
sndInit();
myconnectindex = connecthead = 0;
gNetPlayers = numplayers = 1;
connectpoint2[0] = -1;
gGameOptions.nGameType = 0;
UpdateNetworkMenus();
Printf(PRINT_NONOTIFY, "Initializing sound system\n");
sndInit();
gChoke.init(518, chokeCallback);
UpdateDacs(0, true);
@ -518,17 +472,7 @@ static void gameInit()
void GameInterface::Startup()
{
gameInit();
if (userConfig.CommandMap.IsNotEmpty())
{
}
else
{
if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos();
else
{
gameaction = ga_mainmenu;
}
}
PlayLogos(ga_mainmenu, ga_mainmenu, true);
}
@ -578,4 +522,48 @@ ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize)
return new GameInterface;
}
enum
{
kLoadScreenCRC = -2051908571,
kLoadScreenWideBackWidth = 256,
kLoadScreenWideSideWidth = 128,
};
DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen)
{
static int bLoadScreenCrcMatch = -1;
if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
ACTION_RETURN_INT(bLoadScreenCrcMatch);
}
DEFINE_ACTION_FUNCTION(_Blood, PlayIntroMusic)
{
Mus_Play(nullptr, "PESTIS.MID", false);
return 0;
}
DEFINE_ACTION_FUNCTION(_Blood, sndStartSample)
{
PARAM_PROLOGUE;
PARAM_INT(id);
PARAM_INT(vol);
PARAM_INT(chan);
PARAM_BOOL(looped);
PARAM_INT(chanflags);
sndStartSample(id, vol, chan, looped, EChanFlags::FromInt(chanflags));
return 0;
}
DEFINE_ACTION_FUNCTION(_Blood, sndStartSampleNamed)
{
PARAM_PROLOGUE;
PARAM_STRING(id);
PARAM_INT(vol);
PARAM_INT(chan);
sndStartSample(id, vol, chan);
return 0;
}
END_BLD_NS

View file

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

View file

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

View file

@ -159,23 +159,6 @@ bool GameInterface::CanSave()
return (gamestate == GS_LEVEL && gPlayer[myconnectindex].pXSprite->health != 0);
}
bool GameInterface::StartGame(FNewGameStartup& gs)
{
if (gs.Episode >= 1)
{
if (g_gameType & GAMEFLAG_SHAREWARE)
{
M_StartMessage(GStrings("BUYBLOOD"), 1, NAME_None); // unreachable because we do not support Blood SW versions yet.
return false;
}
}
sfxKillAllSounds();
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
DeferedStartGame(map, gs.Skill);
return true;
}
FSavegameInfo GameInterface::GetSaveSig()
{
return { SAVESIG_BLD, MINSAVEVER_BLD, SAVEVER_BLD };

View file

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

View file

@ -36,152 +36,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_BLD_NS
enum
{
kLoadScreenCRC = -2051908571,
kLoadScreenWideBackWidth = 256,
kLoadScreenWideSideWidth = 128,
};
static int bLoadScreenCrcMatch = -1;
static void drawTextScreenBackground(void)
{
if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
if (bLoadScreenCrcMatch)
{
if (ActiveRatio(twod->GetWidth(), twod->GetHeight()) < 1.34f)
{
DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
}
else
{
int width = scale(twod->GetWidth(), 240, twod->GetHeight());
int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth;
for (int i = 0; i < nCount; i++)
{
DrawTexture(twod, tileGetTexture(kLoadScreenWideBack), (i * kLoadScreenWideBackWidth), 0,
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
}
DrawTexture(twod, tileGetTexture(kLoadScreenWideLeft), 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true, TAG_DONE);
DrawTexture(twod, tileGetTexture(kLoadScreenWideRight), width - tileWidth(kLoadScreenWideRight), 0, DTA_TopLeft, true,
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
DrawTexture(twod, tileGetTexture(kLoadScreenWideMiddle), (width - tileWidth(kLoadScreenWideMiddle)) / 2, 0, DTA_TopLeft, true,
DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE);
}
}
else
{
DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
}
}
// One these screens get scriptified this should use the version in BloodMenuDelegate.
static void DrawCaption(const char* text)
{
double scalex = 1.; // Expand the box if the text is longer
int width = BigFont->StringWidth(text);
int boxwidth = tileWidth(2038);
if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10);
DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE);
DrawText(twod, BigFont, CR_UNDEFINED, 160 - width / 2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE);
}
class DBloodSummaryScreen : public DSkippableScreenJob
{
void DrawKills(void)
{
char pBuffer[40];
if (gGameOptions.nGameType == 0)
{
viewDrawText(1, FStringf("%s:", GStrings("KILLS")), 75, 50, -128, 0, 0, 1);
mysnprintf(pBuffer, 40,"%2d", gKillMgr.Kills);
viewDrawText(1, pBuffer, 160, 50, -128, 0, 0, 1);
viewDrawText(1, GStrings("OF"), 190, 50, -128, 0, 0, 1);
mysnprintf(pBuffer, 40, "%2d", gKillMgr.TotalKills);
viewDrawText(1, pBuffer, 220, 50, -128, 0, 0, 1);
}
else
{
viewDrawText(3, "#", 85, 35, -128, 0, 0, 1);
viewDrawText(3, GStrings("NAME"), 100, 35, -128, 0, 0, 1);
viewDrawText(3, GStrings("FRAGS"), 210, 35, -128, 0, 0, 1);
int nStart = 0;
int nEnd = kMaxPlayers;
for (int i = nStart; i < nEnd; i++) if (playeringame[i])
{
mysnprintf(pBuffer, 40, "%-2d", i);
viewDrawText(3, pBuffer, 85, 50 + 8 * i, -128, 0, 0, 1);
mysnprintf(pBuffer, 40, "%s", PlayerName(i));
viewDrawText(3, pBuffer, 100, 50 + 8 * i, -128, 0, 0, 1);
mysnprintf(pBuffer, 40, "%d", gPlayer[i].fragCount);
viewDrawText(3, pBuffer, 210, 50 + 8 * i, -128, 0, 0, 1);
}
}
}
void DrawSecrets(void)
{
char pBuffer[40];
viewDrawText(1, FStringf("%s:", GStrings("TXT_SECRETS")), 75, 70, -128, 0, 0, 1);
mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Founds);
viewDrawText(1, pBuffer, 160, 70, -128, 0, 0, 1);
viewDrawText(1, GStrings("OF"), 190, 70, -128, 0, 0, 1);
mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Total);
viewDrawText(1, pBuffer, 220, 70, -128, 0, 0, 1);
if (gSecretMgr.Super > 0)
viewDrawText(1, GStrings("TXT_SUPERSECRET"), 160, 100, -128, 2, 1, 1);
}
void Draw(double) override
{
drawTextScreenBackground();
if (gGameOptions.nGameType == 0)
{
DrawCaption(GStrings("TXTB_LEVELSTATS"));
if (bPlayerCheated)
{
auto text = GStrings("TXTB_CHEATED");
int font = 3;
if (!SmallFont2->CanPrint(text)) font = 0;
viewDrawText(font, text, 160, 32, -128, 0, 1, font == 3);
}
DrawKills();
DrawSecrets();
}
else
{
DrawCaption(GStrings("TXTB_FRAGSTATS"));
DrawKills();
}
int myclock = ticks * 120 / GameTicRate;
if ((myclock & 32))
{
auto text = GStrings("PRESSKEY");
int font = 3;
if (!SmallFont2->CanPrint(text)) font = 0;
viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3);
}
}
};
void GameInterface::LevelCompleted(MapRecord *map, int skill)
{
JobDesc job = { Create<DBloodSummaryScreen>() };
sndStartSample(268, 128, -1, false, CHANF_UI);
EndLevel();
Mus_Stop();
RunScreenJob(&job, 1, [=](bool)
SummaryInfo info{};
info.kills = gKillMgr.Kills;
info.maxkills = gKillMgr.TotalKills;
info.secrets = gSecretMgr.Founds;
info.maxsecrets = gSecretMgr.Total;
info.time = gSecretMgr.Super;
info.endofgame = map == nullptr;
ShowIntermission(currentLevel, map, &info, [=](bool)
{
soundEngine->StopAllChannels();
gameaction = ga_nextlevel;
gameaction = map? ga_nextlevel : ga_creditsmenu;
});
}
@ -272,38 +146,4 @@ void SerializeGameStats(FSerializer& arc)
CSecretMgr gSecretMgr;
CKillMgr gKillMgr;
class DBloodLoadScreen : public DScreenJob
{
const char* pzLoadingScreenText1;
MapRecord* rec;
public:
DBloodLoadScreen(const char* caption, MapRecord* maprec) : DScreenJob(), rec(maprec)
{
if (gGameOptions.nGameType == 0) pzLoadingScreenText1 = GStrings("TXTB_LLEVEL");
else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType));
}
void Draw(double) override
{
twod->ClearScreen();
drawTextScreenBackground();
DrawCaption(pzLoadingScreenText1);
viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1);
auto text = GStrings("TXTB_PLSWAIT");
int font = 3;
if (!SmallFont2->CanPrint(text)) font = 0;
viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3);
}
};
void loadscreen(const char *caption, MapRecord* rec, CompletionFunc func)
{
JobDesc job = { Create<DBloodLoadScreen>(caption, rec) };
RunScreenJob(&job, 1, func);
}
END_BLD_NS

View file

@ -38,11 +38,8 @@ GAMEOPTIONS gSingleGameOptions = {
0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200
};
EPISODEINFO gEpisodeInfo[kMaxEpisodes+1];
int gSkill = 2;
int gEpisodeCount;
int gNextLevel; // fixme: let this contain a full level number.
MapRecord* gNextLevel;
char BloodIniFile[BMAX_PATH] = "BLOOD.INI";
bool bINIOverride = false;
@ -94,22 +91,23 @@ void CheckKeyAbend(const char *pzSection, const char *pzKey)
}
void levelLoadMapInfo(IniFile *pIni, MapRecord *pLevelInfo, const char *pzSection, int epinum, int mapnum)
void levelLoadMapInfo(IniFile* pIni, MapRecord* pLevelInfo, const char* pzSection, int epinum, int mapnum, int* nextmap, int* nextsecret)
{
char buffer[16];
pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName));
pLevelInfo->author = pIni->GetKeyString(pzSection, "Author", "");
pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", "");
pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid");
pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1);
pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1);
pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1);
*nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0);
*nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0);
pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0);
pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0);
for (int i = 0; i < kMaxMessages; i++)
{
sprintf(buffer, "Message%d", i+1);
auto msg = pIni->GetKeyString(pzSection, buffer, "");
pLevelInfo->AddMessage(i, msg);
sprintf(buffer, "Message%d", i + 1);
auto msg = pIni->GetKeyString(pzSection, buffer, "");
pLevelInfo->AddMessage(i, msg);
}
}
@ -142,108 +140,101 @@ static const char* DefFile(void)
return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini";
}
static FString cleanPath(const char* pth)
{
FString path = pth;
FixPathSeperator(path);
if (FileExists(path)) return path;
if (path.Len() > 3 && path[1] == ':' && isalpha(path[0]) && path[2] == '/')
{
return path.Mid(3);
}
return path;
}
void levelLoadDefaults(void)
{
char buffer[64];
char buffer2[16];
int cutALevel = 0;
levelInitINI(DefFile());
memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo));
quoteMgr.InitializeQuote(MUS_INTRO, "PESTIS.MID");
int i;
for (i = 0; i < kMaxEpisodes; i++)
for (i = 1; i <= kMaxEpisodes; i++)
{
sprintf(buffer, "Episode%d", i+1);
sprintf(buffer, "Episode%d", i);
if (!BloodINI->SectionExists(buffer))
break;
EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i];
auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer);
gVolumeNames[i] = ep_str; // only keep one table for the names. Todo: Consolidate this across games.
strncpy(pEpisodeInfo->cutsceneAName, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH);
pEpisodeInfo->at9028 = BloodINI->GetKeyInt(buffer, "CutWavA", -1);
if (pEpisodeInfo->at9028 == 0)
strncpy(pEpisodeInfo->cutsceneASound, BloodINI->GetKeyString(buffer, "CutWavA", ""), BMAX_PATH);
else
pEpisodeInfo->cutsceneASound[0] = 0;
strncpy(pEpisodeInfo->cutsceneBName, BloodINI->GetKeyString(buffer, "CutSceneB", ""), BMAX_PATH);
pEpisodeInfo->at902c = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
if (pEpisodeInfo->at902c == 0)
strncpy(pEpisodeInfo->cutsceneBSound, BloodINI->GetKeyString(buffer, "CutWavB", ""), BMAX_PATH);
else
pEpisodeInfo->cutsceneBSound[0] = 0;
auto cluster = MustFindCluster(i);
auto volume = MustFindVolume(i);
CutsceneDef &csB = cluster->outro;
auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer);
cluster->name = volume->name = ep_str;
if (i > 1) volume->flags |= VF_SHAREWARELOCK;
pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
if (pEpisodeInfo->cutALevel > 0)
pEpisodeInfo->cutALevel--;
int j;
for (j = 0; j < kMaxLevels; j++)
csB.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneB", ""));
int soundint = BloodINI->GetKeyInt(buffer, "CutWavB", -1);
if (soundint > 0) csB.soundID = soundint + 0x40000000;
else csB.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", ""));
//pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
if (cutALevel < 1) cutALevel = 1;
int nextmaps[kMaxLevels]{}, nextsecrets[kMaxLevels]{};
for (int j = 1; j <= kMaxLevels; j++)
{
sprintf(buffer2, "Map%d", j+1);
sprintf(buffer2, "Map%d", j);
if (!BloodINI->KeyExists(buffer, buffer2))
break;
auto pLevelInfo = AllocateMap();
const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL);
CheckSectionAbend(pMap);
pLevelInfo->levelNumber = levelnum(i, j);
SetLevelNum(pLevelInfo, makelevelnum(i, j));
pLevelInfo->cluster = i;
pLevelInfo->mapindex = j;
pLevelInfo->labelName = pMap;
if (j == 1) volume->startmap = pLevelInfo->labelName;
pLevelInfo->fileName.Format("%s.map", pMap);
levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j);
levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j, &nextmaps[j - 1], &nextsecrets[j - 1]);
if (j == cutALevel)
{
CutsceneDef& csA = pLevelInfo->intro;
csA.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneA", ""));
int soundint = BloodINI->GetKeyInt(buffer, "CutWavA", -1);
if (soundint > 0) csA.soundID = soundint + 0x40000000;
else csA.soundName = cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", ""));
}
}
// Now resolve the level links
for (int j = 1; j <= kMaxLevels; j++)
{
auto map = FindMapByIndexOnly(i, j);
if (map)
{
if (nextmaps[j - 1] > 0)
{
auto nmap = FindMapByIndexOnly(i, nextmaps[j - 1]);
if (nmap) map->NextMap = nmap->labelName;
else map->NextMap = "-";
}
if (nextsecrets[j - 1] > 0)
{
auto nmap = FindMapByIndexOnly(i, nextsecrets[j - 1]);
if (nmap) map->NextSecret = nmap->labelName;
else map->NextSecret = "-";
}
}
}
pEpisodeInfo->nLevels = j;
}
gEpisodeCount = i;
}
void levelGetNextLevels(int *pnEndingA, int *pnEndingB)
void levelEndLevel(int secret)
{
assert(pnEndingA != NULL && pnEndingB != NULL);
int nEndingA = currentLevel->nextLevel;
if (nEndingA >= 0)
nEndingA--;
int nEndingB = currentLevel->nextSecret;
if (nEndingB >= 0)
nEndingB--;
*pnEndingA = nEndingA;
*pnEndingB = nEndingB;
}
void levelEndLevel(int arg)
{
int nEndingA, nEndingB;
auto episode = volfromlevelnum(currentLevel->levelNumber);
EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[episode];
gGameOptions.uGameFlags |= GF_AdvanceLevel;
levelGetNextLevels(&nEndingA, &nEndingB);
switch (arg)
{
case 0:
if (nEndingA == -1)
{
if (pEpisodeInfo->cutsceneBName[0])
gGameOptions.uGameFlags |= GF_PlayCutscene;
gGameOptions.uGameFlags |= GF_EndGame;
}
else
gNextLevel = nEndingA;
break;
case 1:
if (nEndingB == -1)
{
if (episode + 1 < gEpisodeCount)
{
if (pEpisodeInfo->cutsceneBName[0])
gGameOptions.uGameFlags |= GF_PlayCutscene;
gGameOptions.uGameFlags |= GF_EndGame;
}
else
{
gGameOptions.uGameFlags |= GF_AdvanceLevel;
}
}
else
gNextLevel = nEndingB;
break;
}
if (!secret) gNextLevel = FindNextMap(currentLevel);
else gNextLevel = FindNextSecretMap(currentLevel);
}
void levelTryPlayMusic()

View file

@ -41,7 +41,6 @@ enum
enum EGameFlag
{
GF_AdvanceLevel = 1,
GF_EndGame = 2,
// 4 was for playing intro cutscenes but is no longer used.
GF_PlayCutscene = 8,
};
@ -67,38 +66,16 @@ struct GAMEOPTIONS {
#pragma pack(pop)
enum {
MUS_INTRO = 0,
MUS_LOADING = 1,
};
struct EPISODEINFO
{
int nLevels;
unsigned int bloodbath : 1;
unsigned int cutALevel : 4;
char cutsceneAName[BMAX_PATH];
char cutsceneBName[BMAX_PATH];
int at9028;
int at902c;
char cutsceneASound[BMAX_PATH];
char cutsceneBSound[BMAX_PATH];
};
extern EPISODEINFO gEpisodeInfo[];
extern GAMEOPTIONS gSingleGameOptions;
extern GAMEOPTIONS gGameOptions;
extern int gSkill;
extern char BloodIniFile[];
extern bool bINIOverride;
extern int gEpisodeCount;
extern int gNextLevel;
extern MapRecord* gNextLevel;
extern bool gGameStarted;
void levelInitINI(const char *pzIni);
void levelOverrideINI(const char *pzIni);
void levelPlayIntroScene(int nEpisode, CompletionFunc completion);
void levelPlayEndScene(int nEpisode, CompletionFunc completion);
void levelSetupSecret(int nCount);
void levelTriggerSecret(int nSecret);
void CheckSectionAbend(const char *pzSection);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3697,7 +3697,7 @@ void moveeffectors_d(void) //STATNUM 3
{
static int16_t list1[] = { BLOODPOOL, PUKE, FOOTPRINTS, FOOTPRINTS2, FOOTPRINTS3, FOOTPRINTS4, BULLETHOLE, BLOODSPLAT1, BLOODSPLAT2, BLOODSPLAT3, BLOODSPLAT4, -1 };
static int16_t list2[] = { BOLT1, BOLT1 + 1,BOLT1 + 2, BOLT1 + 3, SIDEBOLT1, SIDEBOLT1 + 1, SIDEBOLT1 + 2, SIDEBOLT1 + 3, -1 };
handle_se24(act, list1, list2, false, TRIPBOMB, LASERLINE, CRANE, 2);
handle_se24(act, list1, list2, true, TRIPBOMB, LASERLINE, CRANE, 2);
break;
}
case SE_35:

View file

@ -292,11 +292,11 @@ static bool cheatItems(int player)
static bool cheatLevel(cheatseq_t *s)
{
int volnume,levnume;
volnume = s->Args[0] - '0' - 1;
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1;
volnume = s->Args[0] - '0';
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0');
// Instead of hard coded range checks on volume and level, let's just check if the level is defined.
auto map = FindMapByLevelNum(levelnum(volnume, levnume));
auto map = FindMapByIndex(volnume, levnume);
if (map)
{
ChangeLevel(map, -1);

View file

@ -97,19 +97,8 @@ bool GameInterface::CanSave()
bool GameInterface::StartGame(FNewGameStartup& gs)
{
if (gs.Episode >= 1)
{
if (g_gameType & GAMEFLAG_SHAREWARE)
{
M_StartMessage(GStrings("BUYDUKE"), 1, NAME_None);
return false;
}
}
int32_t skillsound = PISTOL_BODYHIT;
soundEngine->StopAllChannels();
static const short sounds_d[] = { JIBBED_ACTOR6, BONUS_SPEECH1, DUKE_GETWEAPON2, JIBBED_ACTOR5, JIBBED_ACTOR5 };
static const short sounds_r[] = { 427, 428, 196, 195, 197 };
if (gs.Skill >=0 && gs.Skill <= 5) skillsound = isRR()? sounds_r[gs.Skill] : sounds_d[gs.Skill];
@ -126,15 +115,7 @@ bool GameInterface::StartGame(FNewGameStartup& gs)
}
Net_ClearFifo();
}
auto map = FindMapByLevelNum(levelnum(gs.Episode, gs.Level));
if (map)
{
DeferedStartGame(map, gs.Skill);
return true;
}
return false;
return true;
}
FSavegameInfo GameInterface::GetSaveSig()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -92,13 +92,12 @@ static void endthegame(bool)
void GameInterface::ExitFromMenu()
{
#if 0
// do we really need this scoreboard stuff here?
auto runbonus = [=](auto completion)
{
// MP scoreboard
if (playerswhenstarted > 1 && !ud.coop)
{
dobonus(1, completion);
}
if (playerswhenstarted > 1 && !ud.coop) ShowScoreboard(playerswhenstarted);
else completion(false);
};
@ -106,11 +105,16 @@ void GameInterface::ExitFromMenu()
{
// shareware and TEN screens
if (isShareware() && !isRR())
showtwoscreens(completion);
StartCutscene("DukeCutscenes.BuildSharewareExit", 0, completion);
else completion(false);
};
runbonus([=](bool aborted) { runtwoscreens(endthegame); });
#else
if (isShareware() && !isRR())
StartCutscene("DukeCutscenes.BuildSharewareExit", 0, endthegame);
else endthegame(false);
#endif
}
//---------------------------------------------------------------------------
@ -297,7 +301,13 @@ void drawoverlays(double smoothratio)
}
if (paused == 2)
fi.PrintPaused();
{
double x = 160, y = 100;
double scale = isRR() ? 0.4 : 1.;
const char* text = GStrings("Game Paused");
x -= BigFont->StringWidth(text) * 0.5 * scale;
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
}
}
@ -342,18 +352,6 @@ void cameratext(DDukeActor *cam)
//
//---------------------------------------------------------------------------
void dobonus(int bonusonly, const CompletionFunc& completion)
{
if (isRR()) dobonus_r(bonusonly, completion);
else dobonus_d(bonusonly, completion);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int startrts(int lumpNum, int localPlayer)
{
if (SoundEnabled() &&

View file

@ -48,11 +48,12 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
#include "conlabeldef.h"
#include "gi.h"
extern TArray<TPointer<MapRecord>> mapList;
BEGIN_DUKE_NS
enum { VERSIONCHECK = 41 };
//---------------------------------------------------------------------------
//
// definitions needed by the parser.
@ -88,7 +89,8 @@ public:
struct TempMusic
{
int levnum;
int volnum;
int levlnum;
FString music;
};
@ -1016,7 +1018,8 @@ int ConCompiler::parsecommand()
if (k >= 0)
{
tempMusic.Reserve(1);
tempMusic.Last().levnum = levelnum(k, i);
tempMusic.Last().volnum = k + 1;
tempMusic.Last().levlnum = i + 1;
tempMusic.Last().music = parsebuffer.Data();
}
else
@ -1644,6 +1647,7 @@ int ConCompiler::parsecommand()
return 0;
case concmd_definevolumename:
{
popscriptvalue();
transnum(LABEL_DEFINE);
j = popscriptvalue();
@ -1658,8 +1662,13 @@ int ConCompiler::parsecommand()
textptr++, i++;
}
parsebuffer.Push(0);
gVolumeNames[j] = FStringTable::MakeMacro(parsebuffer.Data(), i);
// We need both a volume and a cluster for this new episode.
auto vol = MustFindVolume(j);
auto clust = MustFindCluster(j + 1);
vol->name = clust->name = FStringTable::MakeMacro(parsebuffer.Data(), i);
if (j > 0) vol->flags |= VF_SHAREWARELOCK;
return 0;
}
case concmd_defineskillname:
popscriptvalue();
transnum(LABEL_DEFINE);
@ -1695,25 +1704,32 @@ int ConCompiler::parsecommand()
textptr++, i++;
}
parsebuffer.Push(0);
auto levnum = levelnum(j, k);
auto map = FindMapByLevelNum(levnum);
auto map = FindMapByIndexOnly(j + 1, k + 1);
if (!map) map = AllocateMap();
map->SetFileName(parsebuffer.Data());
if (k == 0)
{
auto vol = MustFindVolume(j);
vol->startmap = map->labelName;
}
while (*textptr == ' ' || *textptr == '\t') textptr++;
map->parTime =
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
textptr += 5;
while (*textptr == ' ' || *textptr == '\t') textptr++;
map->designerTime =
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 26 * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')) * 26);
(((*(textptr + 0) - '0') * 10 + (*(textptr + 1) - '0')) * 60) +
(((*(textptr + 3) - '0') * 10 + (*(textptr + 4) - '0')));
map->levelNumber = levnum;
SetLevelNum(map, makelevelnum(j + 1, k + 1));
map->mapindex = k + 1;
map->cluster = j + 1;
textptr += 5;
while (*textptr == ' ' || *textptr == '\t') textptr++;
@ -3132,7 +3148,7 @@ void ConCompiler::setmusic()
{
for (auto& tm : tempMusic)
{
auto map = FindMapByLevelNum(tm.levnum);
auto map = FindMapByIndexOnly(tm.volnum, tm.levlnum);
if (map) map->music = tm.music;
}
tempMusic.Clear();
@ -3176,6 +3192,12 @@ void loadcons()
ScriptCode.Push(0);
ConCompiler comp;
if (fileSystem.FileExists("engine/engine.con"))
{
comp.compilecon("engine/engine.con");
}
comp.compilecon(ConFile()); //Tokenize
if (userConfig.AddCons) for (FString& m : *userConfig.AddCons.get())
@ -3206,45 +3228,38 @@ void loadcons()
InitGameVarPointers();
ResetSystemDefaults();
S_WorldTourMappingsForOldSounds(); // create a sound mapping for World Tour.
S_CacheAllSounds();
comp.setmusic();
// RR must link the last map of E1 to the first map of E2.
if (isRR()) for (auto& map : mapList)
{
if (map->cluster == 1)
{
if (!FindMapByIndexOnly(map->cluster, map->mapindex + 1))
{
auto nextmap = FindMapByIndexOnly(map->cluster + 1, 1);
if (nextmap)
{
map->NextMap = nextmap->labelName;
map->flags |= LEVEL_FORCENOEOG;
}
}
}
}
if (isWorldTour())
{
// fix broken secret exit in WT's super secret map.
// This cannot be done from an RMAPINFO definition because the conditions are too specific and must not override custom maps.
int num = fileSystem.CheckNumForName("e1l7.map");
int file = fileSystem.GetFileContainer(num);
if (file <= fileSystem.GetMaxIwadNum())
{
auto maprec = FindMapByName("e1l7");
if (maprec) maprec->nextLevel = levelnum(0, 4);
if (maprec) maprec->NextMap = "e1l5";
}
}
else if (isRRRA())
{
// RRRA goes directly to the second episode after E1L7 to continue the game.
int num = fileSystem.CheckNumForName("e1l7.map");
int file = fileSystem.GetFileContainer(num);
if (file <= fileSystem.GetMaxIwadNum())
{
auto maprec = FindMapByName("e1l7");
if (maprec) maprec->nextLevel = levelnum(1, 0);
}
}
else if (isRR())
{
// RR does not define its final level and crudely hacked it into the progression. This puts it into the E2L8 slot so that the game can naturally progress there.
auto maprec1 = FindMapByLevelNum(levelnum(1, 6));
auto maprec2 = FindMapByLevelNum(levelnum(1, 7));
auto maprec3 = FindMapByName("endgame");
int num3 = fileSystem.FindFile("endgame.map");
if (maprec1 && !maprec2 && !maprec3 && num3 >= 0)
{
auto maprec = AllocateMap();
maprec->designerTime = 0;
maprec->parTime = 0;
maprec->SetFileName("endgame.map");
maprec->SetName("$TXT_CLOSEENCOUNTERS");
maprec->levelNumber = levelnum(1, 7);
}
}
comp.setmusic();
}
END_DUKE_NS

View file

@ -130,11 +130,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
if (!bSet) SetGameVarID(lVar2, cl_showweapon, sActor, sPlayer);
break;
case USERDEFS_FROM_BONUS:
if (bSet) ud.from_bonus = lValue;
else SetGameVarID(lVar2, ud.from_bonus, sActor, sPlayer);
break;
case USERDEFS_CAMERASPRITE:
if (bSet) ud.cameraactor = ScriptIndexToActor(lValue);
else SetGameVarID(lVar2, ActorToScriptIndex(ud.cameraactor), sActor, sPlayer);
@ -239,14 +234,6 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
else SetGameVarID(lVar2, ud.player_skill, sActor, sPlayer);
break;
case USERDEFS_LEVEL_NUMBER:
if (!bSet) SetGameVarID(lVar2, mapfromlevelnum(currentLevel->levelNumber), sActor, sPlayer);
break;
case USERDEFS_VOLUME_NUMBER:
if (!bSet) SetGameVarID(lVar2, volfromlevelnum(currentLevel->levelNumber), sActor, sPlayer);
break;
case USERDEFS_MARKER:
if (bSet) ud.marker = lValue;
else SetGameVarID(lVar2, ud.marker, sActor, sPlayer);
@ -3455,7 +3442,7 @@ int ParseState::parse(void)
insptr++; // skip command
volnume = GetGameVarID(*insptr++, g_ac, g_p);
levnume = GetGameVarID(*insptr++, g_ac, g_p);
auto level = FindMapByLevelNum(levelnum(volnume - 1, levnume - 1));
auto level = FindMapByIndex(volnume, levnume);
if (level != nullptr)
ChangeLevel(level, -1);
break;
@ -3572,7 +3559,7 @@ int ParseState::parse(void)
{
insptr++;
int music_select = *insptr++;
auto level = FindMapByLevelNum(levelnum(currentLevel->levelNumber, music_select));
auto level = FindMapByIndex(currentLevel->cluster, music_select+1); // this was 0-based in EDuke 2.0...
if (level) S_PlayLevelMusic(level);
break;
}

View file

@ -113,29 +113,8 @@ void GameInterface::Ticker()
void GameInterface::Startup()
{
ps[myconnectindex].ftq = 0;
if (userConfig.CommandMap.IsNotEmpty())
{
auto maprecord = FindMapByName(userConfig.CommandMap);
userConfig.CommandMap = "";
if (maprecord)
{
ud.m_respawn_monsters = ud.player_skill == 4;
for (int i = 0; i != -1; i = connectpoint2[i])
{
resetweapons(i);
resetinventory(i);
}
startnewgame(maprecord, /*userConfig.skill*/2);
}
}
else
{
if (!userConfig.nologo) fi.ShowLogo([](bool) { gameaction = ga_mainmenunostopsound; });
else gameaction = ga_mainmenunostopsound;
}
ps[myconnectindex].ftq = 0;
PlayLogos(ga_mainmenunostopsound, ga_mainmenunostopsound, false);
}
//---------------------------------------------------------------------------
@ -160,20 +139,9 @@ void GameInterface::Render()
//
//
//---------------------------------------------------------------------------
void loadscreen_d(MapRecord* rec, CompletionFunc func);
void loadscreen_r(MapRecord* rec, CompletionFunc func);
void GameInterface::NextLevel(MapRecord* map, int skill)
{
#if 0
// Loading is so fast on modern system so that this only serves as an irritant, not an asset.
auto loadscreen = isRR() ? loadscreen_r : loadscreen_d;
loadscreen_d(map, [=](bool)
{
enterlevel(map, 0);
gameaction = ga_level;
});
#endif
enterlevel(map, 0);
}
@ -183,23 +151,6 @@ void GameInterface::NextLevel(MapRecord* map, int skill)
//
//---------------------------------------------------------------------------
void GameInterface::NewGame(MapRecord* map, int skill, bool)
{
// Hmm... What about the other players?
ps[0].last_extra = gs.max_player_health;
resetweapons(0);
resetinventory(0);
if (skill != -1) skill = skill + 1;
startnewgame(map, skill);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void GameInterface::LevelCompleted(MapRecord* map, int skill)
{
exitlevel(map);

View file

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

View file

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

View file

@ -517,6 +517,7 @@ x(THREEDEE, 2498)
x(INGAMEDUKETHREEDEE, 2499)
x(TENSCREEN, 2500)
x(PLUTOPAKSPRITE, 2501)
x(TITLEPLUTOPAKSPRITE, 2502)
x(MENUPLUTOPAKSPRITE, 2503)
x(CREDITPAGE1, 2504)
x(CREDITPAGE2, 2505)
@ -587,11 +588,33 @@ x(RESPAWNMARKERRED, 3190)
x(RESPAWNMARKERYELLOW, 3200)
x(RESPAWNMARKERGREEN, 3210)
x(BONUSSCREEN, 3240)
x(BONUSSCREEN_O1, 3241)
x(BONUSSCREEN_O2, 3242)
x(BONUSSCREEN_O3, 3243)
x(BONUSSCREEN_O4, 3244)
x(BONUSSCREEN2, 3245)
x(BONUSSCREEN2_O1, 3246)
x(BONUSSCREEN2_O2, 3247)
x(BONUSSCREEN2_O3, 3248)
x(BONUSSCREEN2_O4, 3249)
x(VIEWBORDER, 3250)
x(VICTORY1, 3260)
x(VICTORY2, 3261)
x(VICTORY3, 3262)
x(VICTORY4, 3263)
x(VICTORY5, 3264)
x(VICTORY6, 3265)
x(VICTORY7, 3266)
x(VICTORY8, 3267)
x(VICTORY9, 3268)
x(ORDERING, 3270)
x(ORDERING1, 3271)
x(ORDERING2, 3272)
x(ORDERING3, 3273)
x(TEXTSTORY, 3280)
x(LOADSCREEN, 3281)
x(SWEXIT2, 3290)
x(SWEXIT1, 3291)
x(E1ENDSCREEN, 3292)
x(E2ENDSCREEN, 3293)
x(BORNTOBEWILDSCREEN, 3370)

View file

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

View file

@ -22,10 +22,6 @@ extern int TILE_CAMCORNER;
extern int TILE_CAMLIGHT;
extern int TILE_STATIC;
extern int TILE_BOTTOMSTATUSBAR;
extern int TILE_THREEDEE;
extern int TILE_INGAMEDUKETHREEDEE;
extern int TILE_PLUTOPAKSPRITE;
extern int TILE_MENUBAR;
extern int TILE_ATOMICHEALTH;
extern int TILE_FLOORSLIME;
extern int TILE_JIBS6;

View file

@ -2630,27 +2630,6 @@ static void processweapon(int snum, ESyncBits actions, int psect)
auto s = pact->s;
int shrunk = (s->yrepeat < 32);
// Set maximum for pistol slightly higher if playing with `cl_showmagamount 1`.
if (!cl_showmagamt)
{
if (p->ammo_amount[PISTOL_WEAPON] > PISTOL_MAXDEFAULT)
p->ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT;
if (gs.max_ammo_amount[PISTOL_WEAPON] != PISTOL_MAXDEFAULT)
gs.max_ammo_amount[PISTOL_WEAPON] = PISTOL_MAXDEFAULT;
}
else
{
short pistolAddition = 4;
short pistolNewMaximum = PISTOL_MAXDEFAULT + pistolAddition;
if (p->ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT && gs.max_ammo_amount[PISTOL_WEAPON] == PISTOL_MAXDEFAULT)
p->ammo_amount[PISTOL_WEAPON] += pistolAddition;
if (gs.max_ammo_amount[PISTOL_WEAPON] != pistolNewMaximum)
gs.max_ammo_amount[PISTOL_WEAPON] = pistolNewMaximum;
}
if (isNamWW2GI() && (actions & SB_HOLSTER)) // 'Holster Weapon
{
if (isWW2GI())
@ -3138,7 +3117,7 @@ HORIZONLY:
if (SyncInput())
{
p->horizon.applyinput(PlayerHorizon(snum), &actions);
p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
}
p->checkhardlanding();

View file

@ -1464,26 +1464,8 @@ int doincrements_r(struct player_struct* p)
{
if (!wupass)
{
short snd;
int snd = currentLevel->rr_startsound ? currentLevel->rr_startsound : 391;
wupass = 1;
switch (currentLevel->levelNumber)
{
default: snd = 391; break;
case levelnum(0, 0): snd = isRRRA() ? 63 : 391; break;
case levelnum(0, 1): snd = 64; break;
case levelnum(0, 2): snd = 77; break;
case levelnum(0, 3): snd = 80; break;
case levelnum(0, 4): snd = 102; break;
case levelnum(0, 5): snd = 103; break;
case levelnum(0, 6): snd = 104; break;
case levelnum(1, 0): snd = 105; break;
case levelnum(1, 1): snd = 176; break;
case levelnum(1, 2): snd = 177; break;
case levelnum(1, 3): snd = 198; break;
case levelnum(1, 4): snd = 230; break;
case levelnum(1, 5): snd = 255; break;
case levelnum(1, 6): snd = 283; break;
}
S_PlayActorSound(snd, pact);
}
else if (PlayClock > 1024)
@ -3399,8 +3381,7 @@ void processinput_r(int snum)
psectlotag = 2;
}
}
else if (psectlotag == 7777)
if (currentLevel->levelNumber == levelnum(1, 6))
else if (psectlotag == 7777 && (currentLevel->gameflags & LEVEL_RR_HULKSPAWN))
lastlevel = 1;
if (psectlotag == 848 && sector[psect].floorpicnum == WATERTILE2)
@ -4000,7 +3981,7 @@ HORIZONLY:
if (SyncInput())
{
p->horizon.applyinput(PlayerHorizon(snum), &actions);
p->horizon.applyinput(GetPlayerHorizon(snum), &actions);
}
p->checkhardlanding();

View file

@ -36,6 +36,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
#include "automap.h"
#include "dukeactor.h"
#include "interpolate.h"
#include "precache.h"
#include "render.h"
BEGIN_DUKE_NS
@ -141,7 +142,7 @@ void resetplayerstats(int snum)
p->jetpack_on = 0;
p->holoduke_on = nullptr;
p->angle.olook_ang = p->angle.look_ang = buildang(512 - ((currentLevel->levelNumber & 1) << 10));
p->angle.olook_ang = p->angle.look_ang = buildang(512 - (((~currentLevel->levelNumber) & 1) << 10));
p->angle.orotscrnang = p->angle.rotscrnang = buildang(0);
p->newOwner =nullptr;
@ -659,7 +660,6 @@ void prelevel_common(int g)
p->SlotWin = 0;
enemysizecheat = 0;
p->MamaEnd = 0;
mamaspawn_count = 15;
banjosound = 0;
RRRA_ExitedLevel = 0;
@ -672,7 +672,7 @@ void prelevel_common(int g)
WindDir = 0;
fakebubba_spawn = 0;
RRRA_ExitedLevel = 0;
mamaspawn_count = 15;
mamaspawn_count = currentLevel->rr_mamaspawn;
BellTime = 0;
BellSprite = nullptr;
@ -754,7 +754,6 @@ void donewgame(MapRecord* map, int sk)
auto p = &ps[0];
show_shareware = 26 * 34;
//ud.nextLevel = map;
ud.player_skill = sk;
ud.secretlevel = 0;
ud.from_bonus = 0;
@ -813,33 +812,6 @@ void donewgame(MapRecord* map, int sk)
}
}
template<class func>
void newgame(MapRecord* map, int sk, func completion)
{
auto completion1 = [=](bool res)
{
if (!isRR() && map->levelNumber == levelnum(3, 0) && (ud.multimode < 2))
{
e4intro([=](bool) { donewgame(map, sk); completion(res); });
}
else
{
donewgame(map, sk);
completion(res);
}
};
if (ud.m_recstat != 2 && ud.last_level >= 0 && ud.multimode > 1 && ud.coop != 1)
dobonus(1, completion1);
#if 0 // this is one lousy hack job that's hopefully not needed anymore.
else if (isRR() && !isRRRA() && map->levelNumber == levelnum(0, 6))
dobonus(0, completion1);
#endif
else completion1(false);
}
//---------------------------------------------------------------------------
//
// the setup here is very, very sloppy, because mappings are not 1:1.
@ -978,12 +950,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
SECRET_SetMapName(mi->DisplayName(), mi->name);
STAT_NewLevel(mi->fileName);
if (isRR() && !isRRRA() && mi->levelNumber == levelnum(1, 1))
{
for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++)
ps[0].ammo_amount[i] = 0;
ps[0].gotweapon.Clear(KNEE_WEAPON);
}
p->angle.ang = buildang(lbang);
memset(gotpic, 0, sizeof(gotpic));
@ -993,16 +959,6 @@ static int LoadTheMap(MapRecord *mi, struct player_struct *p, int gamemode)
SpawnPortals();
if (isRRRA() && mi->levelNumber == levelnum(2, 0))
{
for (int i = PISTOL_WEAPON; i < MAX_WEAPONS; i++)
ps[0].ammo_amount[i] = 0;
ps[0].gotweapon.Clear(KNEE_WEAPON);
ps[0].gotweapon.Set(SLINGBLADE_WEAPON);
ps[0].ammo_amount[SLINGBLADE_WEAPON] = 1;
ps[0].curr_weapon = SLINGBLADE_WEAPON;
}
allignwarpelevators();
resetpspritevars(gamemode);
@ -1047,7 +1003,6 @@ void enterlevel(MapRecord *mi, int gamemode)
OnEvent(EVENT_ENTERLEVEL);
// Stop all sounds
S_ResumeSound(false);
FX_StopAllSounds();
FX_SetReverb(0);
@ -1064,27 +1019,32 @@ void enterlevel(MapRecord *mi, int gamemode)
S_PlayLevelMusic(mi);
}
if (isShareware() && mi->levelNumber == 0 && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
for (int i = connecthead; i >= 0; i = connectpoint2[i])
{
bool clearweapon = !!(currentLevel->flags & LEVEL_CLEARWEAPONS);
int pn = sector[ps[i].GetActor()->s->sectnum].floorpicnum;
if (pn == TILE_HURTRAIL || pn == TILE_FLOORSLIME || pn == TILE_FLOORPLASMA)
{
resetweapons(i);
resetinventory(i);
clearweapon = true;
}
if (clearweapon)
{
resetweapons(i);
ps[i].gotweapon.Clear(PISTOL_WEAPON);
ps[i].ammo_amount[PISTOL_WEAPON] = 0;
ps[i].curr_weapon = KNEE_WEAPON;
ps[i].kickback_pic = 0;
ps[i].okickback_pic = ps[i].kickback_pic = 0;
}
if (currentLevel->flags & LEVEL_CLEARINVENTORY) resetinventory(i);
}
resetmys();
everyothertime = 0;
global_random = 0;
ud.last_level = currentLevel->levelNumber;
ud.last_level = 1;
ps[myconnectindex].over_shoulder_on = 0;
clearfrags();
resettimevars(); // Here we go
@ -1105,9 +1065,19 @@ void enterlevel(MapRecord *mi, int gamemode)
//
//---------------------------------------------------------------------------
void startnewgame(MapRecord* map, int skill)
void GameInterface::NewGame(MapRecord* map, int skill, bool)
{
for (int i = 0; i != -1; i = connectpoint2[i])
{
resetweapons(i);
resetinventory(i);
}
ps[0].last_extra = gs.max_player_health;
if (skill == -1) skill = ud.player_skill;
else skill++;
ud.player_skill = skill;
ud.m_respawn_monsters = (skill == 4);
ud.m_monsters_off = ud.monsters_off = 0;
@ -1115,13 +1085,13 @@ void startnewgame(MapRecord* map, int skill)
ud.m_respawn_inventory = 0;
ud.multimode = 1;
newgame(map, skill, [=](bool)
{
donewgame(map, skill);
enterlevel(map, 0);
if (isShareware() && ud.recstat != 2) FTA(QUOTE_F1HELP, &ps[myconnectindex]);
PlayerColorChanged();
inputState.ClearAllInput();
gameaction = ga_level;
});
}
//---------------------------------------------------------------------------
@ -1132,25 +1102,27 @@ void startnewgame(MapRecord* map, int skill)
bool setnextmap(bool checksecretexit)
{
MapRecord* map = nullptr;;
int from_bonus = 0;
MapRecord* map = nullptr;
MapRecord* from_bonus = nullptr;
if (ud.eog)
if (ud.eog && !(currentLevel->flags & LEVEL_FORCENOEOG))
{
}
else if (checksecretexit && ud.from_bonus == 0)
{
if (ud.secretlevel > 0)
{
int newlevnum = levelnum(volfromlevelnum(currentLevel->levelNumber), ud.secretlevel-1);
map = FindMapByLevelNum(newlevnum);
// allow overriding the secret exit destination to make episode compilation easier with maps containing secret exits.
if (currentLevel->flags & LEVEL_SECRETEXITOVERRIDE) map = FindNextSecretMap(currentLevel);
if (!map) map = FindMapByIndex(currentLevel->cluster, ud.secretlevel);
if (map)
{
from_bonus = currentLevel->levelNumber + 1;
from_bonus = FindNextMap(currentLevel);
}
}
}
else if (ud.from_bonus && currentLevel->nextLevel == -1) // if the current level has an explicit link, use that instead of ud.from_bonus.
else if (ud.from_bonus && currentLevel->NextMap.IsEmpty()) // if the current level has an explicit link, use that instead of ud.from_bonus.
{
map = FindMapByLevelNum(ud.from_bonus);
}
@ -1169,7 +1141,7 @@ bool setnextmap(bool checksecretexit)
{
I_Error("Trying to open non-existent %s", map->fileName.GetChars());
}
ud.from_bonus = from_bonus;
ud.from_bonus = from_bonus? from_bonus->levelNumber : 0;
}
CompleteLevel(map);
return false;
@ -1181,30 +1153,33 @@ bool setnextmap(bool checksecretexit)
//
//---------------------------------------------------------------------------
void exitlevel(MapRecord *nextlevel)
void exitlevel(MapRecord* nextlevel)
{
bool endofgame = nextlevel == nullptr;
STAT_Update(endofgame);
StopCommentary();
dobonus(endofgame? -1 : 0, [=](bool)
{
SummaryInfo info{};
info.kills = ps[0].actors_killed;
info.maxkills = ps[0].max_actors_killed;
info.secrets = ps[0].secret_rooms;
info.maxsecrets = ps[0].max_secret_rooms;
info.time = ps[0].player_par / GameTicRate;
info.endofgame = endofgame;
Mus_Stop();
if (playerswhenstarted > 1 && ud.coop != 1)
{
// MP scoreboard
ShowScoreboard(playerswhenstarted, [=](bool)
{
// Clear potentially loaded per-map ART only after the bonus screens.
artClearMapArt();
gameaction = ga_level;
ud.eog = false;
if (endofgame)
{
if (ud.multimode < 2)
{
if (isShareware())
doorders([](bool) { gameaction = ga_startup; });
else gameaction = ga_startup;
return;
}
else
{
auto nextlevel = FindMapByLevelNum(0);
if (!nextlevel)
{
@ -1213,11 +1188,22 @@ void exitlevel(MapRecord *nextlevel)
}
else gameaction = ga_nextlevel;
}
}
else
else
gameaction = ga_nextlevel;
});
}
else if (ud.multimode <= 1)
{
// SP cutscene + summary
ShowIntermission(currentLevel, nextlevel, &info, [=](bool)
{
// Clear potentially loaded per-map ART only after the bonus screens.
artClearMapArt();
ud.eog = false;
gameaction = endofgame? ga_startup : ga_nextlevel;
});
}
}

View file

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

View file

@ -366,37 +366,6 @@ static void cachegoodsprites(void)
for (i = SMALLSMOKE; i < (SMALLSMOKE + 4); i++)
tloadtile(i);
if (isRRRA() && currentLevel->levelNumber == levelnum(0, 4))
{
tloadtile(RRTILE2577);
}
if (!isRRRA() && currentLevel->levelNumber == levelnum(1, 2))
{
tloadtile(RRTILE3190);
tloadtile(RRTILE3191);
tloadtile(RRTILE3192);
tloadtile(RRTILE3144);
tloadtile(RRTILE3139);
tloadtile(RRTILE3132);
tloadtile(RRTILE3120);
tloadtile(RRTILE3121);
tloadtile(RRTILE3122);
tloadtile(RRTILE3123);
tloadtile(RRTILE3124);
}
if (lastlevel)
{
i = isRRRA() ? UFO1_RRRA : UFO1_RR;
tloadtile(i);
i = UFO2;
tloadtile(i);
i = UFO3;
tloadtile(i);
i = UFO4;
tloadtile(i);
i = UFO5;
tloadtile(i);
}
}
//---------------------------------------------------------------------------
@ -407,6 +376,7 @@ static void cachegoodsprites(void)
void cacheit_r(void)
{
if (!r_precache) return;
int i;
cachegoodsprites();
@ -461,11 +431,11 @@ void prelevel_r(int g)
prelevel_common(g);
p = &ps[screenpeek];
if (currentLevel->gameflags & LEVEL_RR_CLEARMOONSHINE)
ps[myconnectindex].steroids_amount = 0;
if (isRRRA())
{
if (currentLevel->levelNumber == levelnum(1, 4))
ps[myconnectindex].steroids_amount = 0;
for (j = 0; j < MAXSPRITES; j++)
{

View file

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

View file

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

View file

@ -46,6 +46,7 @@ source as it is released.
#include "gamestate.h"
#include "names_d.h"
#include "i_music.h"
#include "vm.h"
CVAR(Bool, wt_forcemidi, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // quick hack to disable the oggs, which are of lower quality than playing the MIDIs with a good synth and sound font.
CVAR(Bool, wt_forcevoc, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // The same for sound effects. The re-recordings are rather poor and disliked
@ -872,4 +873,48 @@ bool StartCommentary(int tag, DDukeActor* actor)
return false;
}
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySpecialMusic, S_PlaySpecialMusic)
{
PARAM_PROLOGUE;
PARAM_INT(song);
S_PlaySpecialMusic(song);
return 0;
}
static int PlaySound(int num, int chan, int flags, double vol)
{
return S_PlaySound(num, chan, EChanFlags::FromInt(flags), float(vol));
}
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, PlaySound, PlaySound)
{
PARAM_PROLOGUE;
PARAM_INT(snd);
PARAM_INT(chan);
PARAM_INT(flags);
PARAM_FLOAT(vol);
ACTION_RETURN_INT(PlaySound(snd, chan, flags, vol));
}
static void StopSound(int num)
{
S_StopSound(num);
}
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, StopSound, StopSound)
{
PARAM_PROLOGUE;
PARAM_INT(snd);
StopSound(snd);
return 0;
}
DEFINE_ACTION_FUNCTION_NATIVE(_Duke, CheckSoundPlaying, S_CheckSoundPlaying)
{
PARAM_PROLOGUE;
PARAM_INT(snd);
ACTION_RETURN_INT(S_CheckSoundPlaying(snd));
}
END_DUKE_NS

View file

@ -48,7 +48,6 @@ void S_MenuSound(void);
void S_StopSound(int sndNum, DDukeActor* spr = nullptr, int flags = -1);
int S_CheckSoundPlaying(int soundNum);
inline int S_CheckSoundPlaying(int sprnum, int soundNum) { return S_CheckSoundPlaying(soundNum); }
int S_CheckActorSoundPlaying(DDukeActor* spriteNum, int soundNum, int channel = 0);
int S_CheckAnyActorSoundPlaying(DDukeActor* spriteNum);
@ -64,7 +63,6 @@ inline bool S_IsSoundValid(int num)
void S_PlayRRMusic(int newTrack = -1);
void S_PlayBonusMusic();
void S_PlayLevelMusic(MapRecord* mi);
void S_PlaySpecialMusic(unsigned int);
void S_ContinueLevelMusic(void);
// Placeholders.

View file

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

View file

@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gstrings.h"
#include "gamefuncs.h"
#include "c_bind.h"
#include "vm.h"
#include <string>
@ -47,8 +48,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_PS_NS
int selectedlevelnew;
//---------------------------------------------------------------------------
//
//
@ -373,471 +372,26 @@ void menu_DoPlasma()
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DLobotomyScreen : public DImageScreen
DEFINE_ACTION_FUNCTION(_Exhumed, DrawPlasma)
{
public:
DLobotomyScreen(FGameTexture *tex, int fade) : DImageScreen(tex, fade)
{}
void Skipped() override
{
StopLocalSound();
}
void Start() override
{
PlayLocalSound(StaticSound[kSoundJonLaugh2], 7000, false, CHANF_UI);
}
void OnTick() override
{
DImageScreen::OnTick();
if (state == finished) StopLocalSound();
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 };
class DMainTitle : public DSkippableScreenJob
{
const char* a;
const char* b;
int state = 0;
int duration;
int var_4 = 0;
int esi = 130;
int nCount = 0;
int start;
public:
DMainTitle() : DSkippableScreenJob(fadein)
{
a = GStrings("TXT_EX_COPYRIGHT1");
b = GStrings("TXT_EX_COPYRIGHT2");
duration = skullDurations[0];
}
void Start() override
{
PlayLocalSound(StaticSound[59], 0, true, CHANF_UI);
playCDtrack(19, true);
}
void OnTick() override
{
int ticker = ticks * 120 / GameTicRate;
if (ticks > 1 && state == 0 && !soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1))
{
if (time(0) & 0xF) // cheap-ass random...
PlayGameOverSound();
else
PlayLocalSound(StaticSound[61], 0, false, CHANF_UI);
state = 1;
start = ticker;
}
if (state == 1)
{
if (ticker > duration)
{
nCount++;
if (nCount > 12)
{
state = finished;
return;
}
duration = start + skullDurations[nCount];
var_4 = var_4 == 0;
}
}
}
void Draw(double) override
{
twod->ClearScreen();
menu_DoPlasma();
DrawRel(kSkullHead, 160, 100);
if (state == 0)
{
DrawRel(kSkullJaw, 161, 130);
}
else
{
int nStringWidth = SmallFont->StringWidth(a);
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
nStringWidth = SmallFont->StringWidth(b);
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
short nTile = kSkullJaw;
if (var_4)
{
if (esi >= 135) nTile = kTile3583;
else esi += 5;
}
else if (esi <= 130) esi = 130;
else esi -= 2;
int y;
if (nTile == kTile3583)
{
y = 131;
}
else
{
y = esi;
if (y > 135) y = 135;
}
DrawRel(nTile, 161, y);
}
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
DScreenJob *PlayMovie(const char* fileName);
void DoTitle(CompletionFunc completion)
{
JobDesc jobs[5];
int job = 0;
jobs[job++] = { Create<DImageScreen>(tileGetTexture(PublisherLogo()), DScreenJob::fadein | DScreenJob::fadeout) };
jobs[job++] = { Create<DLobotomyScreen>(tileGetTexture(seq_GetSeqPicnum(kSeqScreens, 0, 0)), DScreenJob::fadein | DScreenJob::fadeout) };
jobs[job++] = { PlayMovie("book.mov") };
jobs[job++] = { Create<DMainTitle>() };
RunScreenJob(jobs, job, completion, true, true);
return 0;
}
//---------------------------------------------------------------------------
//
// pre-level map display
// text overlay (native version still needed for Ramses texts.
//
//---------------------------------------------------------------------------
static const int8_t MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 };
struct TILEFRAMEDEF
{
short nTile;
short xOffs;
short yOffs;
};
// 22 bytes
struct MapNamePlaque
{
short xPos;
short yPos;
TILEFRAMEDEF tiles[2];
TILEFRAMEDEF text;
};
static const MapNamePlaque mapNamePlaques[] = {
{ 100, 170, kTile3376, 0, 0, kTile3377, 0, 0, kTile3411, 18, 6 },
{ 230, 10, kTile3378, 0, 0, kTile3379, 0, 0, kTile3414, 18, 6 }, // DENDUR (level 2)
{ 180, 125, kTile3380, 0, 0, kTile3381, 0, 0, kTile3417, 18, 6 }, // Kalabash
{ 10, 95, kTile3382, 0, 0, kTile3383, 0, 0, kTile3420, 18, 6 },
{ 210, 160, kTile3384, 0, 0, kTile3385, 0, 0, kTile3423, 18, 6 },
{ 10, 110, kTile3371, 0, 0, kTile3386, 0, 0, kTile3426, 18, 6 },
{ 10, 50, kTile3387, 0, 0, kTile3388, 0, 0, kTile3429, 18, 6 },
{ 140, 0, kTile3389, 0, 0, kTile3390, 0, 0, kTile3432, 18, 6 },
{ 30, 20, kTile3391, 0, 0, kTile3392, 0, 0, kTile3435, 18, 6 },
{ 200, 150, kTile3409, 0, 0, kTile3410, 0, 0, kTile3418, 20, 4 },
{ 145, 170, kTile3393, 0, 0, kTile3394, 0, 0, kTile3438, 18, 6 },
{ 80, 80, kTile3395, 0, 0, kTile3396, 0, 0, kTile3441, 18, 6 },
{ 15, 0, kTile3397, 0, 0, kTile3398, 0, 0, kTile3444, 18, 5 },
{ 220, 35, kTile3399, 0, 0, kTile3400, 0, 0, kTile3447, 18, 6 },
{ 190, 40, kTile3401, 0, 0, kTile3402, 0, 0, kTile3450, 18, 6 },
{ 20, 130, kTile3403, 0, 0, kTile3404, 0, 0, kTile3453, 19, 6 },
{ 220, 160, kTile3405, 0, 0, kTile3406, 0, 0, kTile3456, 18, 6 },
{ 20, 10, kTile3407, 0, 0, kTile3408, 0, 0, kTile3459, 18, 6 },
{ 200, 10, kTile3412, 0, 0, kTile3413, 0, 0, kTile3419, 18, 5 },
{ 20, 10, kTile3415, 0, 0, kTile3416, 0, 0, kTile3421, 19, 4 }
};
// 3 different types of fire, each with 4 frames
static const TILEFRAMEDEF FireTiles[3][4] = {
{{ kTile3484,0,3 },{ kTile3485,0,0 },{ kTile3486,0,3 },{ kTile3487,0,0 }},
{{ kTile3488,1,0 },{ kTile3489,1,0 },{ kTile3490,0,1 },{ kTile3491,1,1 }},
{{ kTile3492,1,2 },{ kTile3493,1,0 },{ kTile3494,1,2 },{ kTile3495,1,0 }}
};
struct Fire
{
short nFireType;
short xPos;
short yPos;
};
// 20 bytes
struct MapFire
{
short nFires;
Fire fires[3];
};
/*
level 1 - 3 fires
level 2 - 3 fires
level 3 - 1 fire
*/
static const MapFire MapLevelFires[] = {
3, {{0, 107, 95}, {1, 58, 140}, {2, 28, 38}},
3, {{2, 240, 0}, {0, 237, 32}, {1, 200, 30}},
2, {{2, 250, 57}, {0, 250, 43}, {2, 200, 70}},
2, {{1, 82, 59}, {2, 84, 16}, {0, 10, 95}},
2, {{2, 237, 50}, {1, 215, 42}, {1, 210, 50}},
3, {{0, 40, 7}, {1, 75, 6}, {2, 100, 10}},
3, {{0, 58, 61}, {1, 85, 80}, {2, 111, 63}},
3, {{0, 260, 65}, {1, 228, 0}, {2, 259, 15}},
2, {{0, 81, 38}, {2, 58, 38}, {2, 30, 20}},
3, {{0, 259, 49}, {1, 248, 76}, {2, 290, 65}},
3, {{2, 227, 66}, {0, 224, 98}, {1, 277, 30}},
2, {{0, 100, 10}, {2, 48, 76}, {2, 80, 80}},
3, {{0, 17, 2}, {1, 29, 49}, {2, 53, 28}},
3, {{0, 266, 42}, {1, 283, 99}, {2, 243, 108}},
2, {{0, 238, 19}, {2, 240, 92}, {2, 190, 40}},
2, {{0, 27, 0}, {1, 70, 40}, {0, 20, 130}},
3, {{0, 275, 65}, {1, 235, 8}, {2, 274, 6}},
3, {{0, 75, 45}, {1, 152, 105}, {2, 24, 68}},
3, {{0, 290, 25}, {1, 225, 63}, {2, 260, 110}},
0, {{1, 20, 10}, {1, 20, 10}, {1, 20, 10}}
};
class DMapScreen : public DScreenJob
void TextOverlay::Create(const FString& text, int pal)
{
int i;
int x = 0;
int delta = 0;
int nIdleSeconds = 0;
int curYPos, destYPos;
int nLevel, nLevelNew, nLevelBest;
public:
DMapScreen(int nLevel_, int nLevelNew_, int nLevelBest_) : DScreenJob(fadein|fadeout), nLevel(nLevel_), nLevelNew(nLevelNew_), nLevelBest(nLevelBest_)
{
curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2));
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
if (curYPos < destYPos) {
delta = 2;
}
if (curYPos > destYPos) {
delta = -2;
}
// Trim smoke in widescreen
#if 0
vec2_t mapwinxy1 = windowxy1, mapwinxy2 = windowxy2;
int32_t width = mapwinxy2.x - mapwinxy1.x + 1, height = mapwinxy2.y - mapwinxy1.y + 1;
if (3 * width > 4 * height)
{
mapwinxy1.x += (width - 4 * height / 3) / 2;
mapwinxy2.x -= (width - 4 * height / 3) / 2;
}
#endif
}
void Draw(double smoothratio)
{
int currentclock = int((ticks + smoothratio) * 120 / GameTicRate);
twod->ClearScreen();
int tileY = curYPos;
// Draw the background screens
for (i = 0; i < 10; i++)
{
DrawAbs(kTile3353 + i, x, tileY);
tileY -= 200;
}
// for each level - drawing the 'level completed' on-fire smoke markers
for (i = 0; i < kMap20; i++)
{
int screenY = (i >> 1) * -200;
if (nLevelBest >= i) // check if the player has finished this level
{
for (int j = 0; j < MapLevelFires[i].nFires; j++)
{
int nFireFrame = ((currentclock >> 4) & 3);
assert(nFireFrame >= 0 && nFireFrame < 4);
int nFireType = MapLevelFires[i].fires[j].nFireType;
assert(nFireType >= 0 && nFireType < 3);
int nTile = FireTiles[nFireType][nFireFrame].nTile;
int smokeX = MapLevelFires[i].fires[j].xPos + FireTiles[nFireType][nFireFrame].xOffs;
int smokeY = MapLevelFires[i].fires[j].yPos + FireTiles[nFireType][nFireFrame].yOffs + curYPos + screenY;
// Use rotatesprite to trim smoke in widescreen
DrawAbs(nTile, smokeX, smokeY);
// Todo: mask out the sides of the screen if the background is not widescreen.
}
}
int t = (((currentclock & 16) >> 4));
int nTile = mapNamePlaques[i].tiles[t].nTile;
int nameX = mapNamePlaques[i].xPos + mapNamePlaques[i].tiles[t].xOffs;
int nameY = mapNamePlaques[i].yPos + mapNamePlaques[i].tiles[t].yOffs + curYPos + screenY;
// Draw level name plaque
DrawAbs(nTile, nameX, nameY);
int8_t shade = 96;
if (nLevelNew == i)
{
shade = (bsin(16 * currentclock) + 31) >> 8;
}
else if (nLevelBest >= i)
{
shade = 31;
}
int textY = mapNamePlaques[i].yPos + mapNamePlaques[i].text.yOffs + curYPos + screenY;
int textX = mapNamePlaques[i].xPos + mapNamePlaques[i].text.xOffs;
nTile = mapNamePlaques[i].text.nTile;
// draw the text, alternating between red and black
DrawAbs(nTile, textX, textY, shade);
}
selectedlevelnew = nLevelNew + 1;
}
void OnTick() override
{
if (curYPos != destYPos)
{
// scroll the map every couple of ms
curYPos += delta;
if (curYPos > destYPos && delta > 0) {
curYPos = destYPos;
}
if (curYPos < destYPos && delta < 0) {
curYPos = destYPos;
}
nIdleSeconds = 0;
}
else nIdleSeconds++;
if (nIdleSeconds > 300) state = finished;
}
bool OnEvent(event_t* ev) override
{
int key = ev->data1;
if (ev->type == EV_KeyDown)
{
auto binding = Bindings.GetBinding(ev->data1);
if (!binding.CompareNoCase("+move_forward")) key = KEY_UPARROW;
if (!binding.CompareNoCase("+move_backward")) key = KEY_DOWNARROW;
if (key == KEY_UPARROW || key == KEY_PAD_DPAD_UP || key == sc_kpad_8)
{
if (curYPos == destYPos && nLevelNew <= nLevelBest)
{
nLevelNew++;
assert(nLevelNew < 20);
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
if (curYPos <= destYPos) {
delta = 2;
}
else {
delta = -2;
}
nIdleSeconds = 0;
}
return true;
}
if (key == KEY_DOWNARROW || key == KEY_PAD_DPAD_DOWN || key == sc_kpad_2)
{
if (curYPos == destYPos && nLevelNew > 0)
{
nLevelNew--;
assert(nLevelNew >= 0);
destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2));
if (curYPos <= destYPos) {
delta = 2;
}
else {
delta = -2;
}
nIdleSeconds = 0;
}
return true;
}
if (!specialKeyEvent(ev)) state = skipped;
return true;
}
return false;
}
};
void menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest, TArray<JobDesc> &jobs)
{
if (nLevel > kMap20 || nLevelNew > kMap20) // max single player levels
{
return;
}
#ifdef _DEBUG
nLevelBest = kMap20;
#endif
if (nLevel < 1) nLevel = 1;
if (nLevelNew < 1) nLevelNew = nLevel;
// 0-offset the level numbers
jobs.Push( { Create<DMapScreen>(nLevel-1, nLevelNew-1, nLevelBest-1) });
}
//---------------------------------------------------------------------------
//
// text overlay
//
//---------------------------------------------------------------------------
lastclock = 0;
FString ttext = GStrings(text);
screentext = ttext.Split("\n");
ComputeCinemaText();
}
void TextOverlay::Start(double starttime)
{
@ -857,10 +411,9 @@ void TextOverlay::ComputeCinemaText()
nHeight = screentext.Size() * 10;
}
void TextOverlay::ReadyCinemaText(uint16_t nVal)
void TextOverlay::ReadyCinemaText(const char* nVal)
{
FStringf label("TXT_EX_CINEMA%d", nVal);
label = GStrings(label);
FString label = nVal[0] == '$'? GStrings(nVal +1) : nVal;
screentext = label.Split("\n");
ComputeCinemaText();
}
@ -903,33 +456,6 @@ bool TextOverlay::AdvanceCinemaText(double clock)
//
//---------------------------------------------------------------------------
enum EScenes
{
CINEMA_BEFORE_LEVEL_5,
CINEMA_AFTER_LEVEL_10,
CINEMA_BEFORE_LEVEL_11,
CINEMA_AFTER_LEVEL_15,
CINEMA_LOSE_SCENE,
CINEMA_AFTER_LEVEL_20,
};
struct CinemaDef
{
short tile;
short palette;
short text;
short track;
};
static CinemaDef cinemas[] = {
{ 3449, 3, 2, 2},
{ 3451, 5, 4, 3},
{ 3454, 1, 3, 4},
{ 3446, 7, 6, 6},
{ 3445, 4, 7, 7},
{ 3448, 6, 8, 8}
};
static const char * const cinpalfname[] = {
"3454.pal",
"3452.pal",
@ -965,419 +491,59 @@ void uploadCinemaPalettes()
//---------------------------------------------------------------------------
//
// cinema
// this accesses the tile data and needs to remain native.
//
//---------------------------------------------------------------------------
class DCinema : public DSkippableScreenJob
static int DoStatic(int a, int b)
{
TextOverlay text;
short cinematile;
int currentCinemaPalette;
int edx;
int check;
int cont = 1;
public:
DCinema(int nVal, int checklevel = -1) : DSkippableScreenJob(fadein|fadeout)
{
if (nVal < 0 || nVal >5) return;
cinematile = cinemas[nVal].tile;
currentCinemaPalette = cinemas[nVal].palette;
text.Start(0);
text.ReadyCinemaText(cinemas[nVal].text);
text.SetPalette(currentCinemaPalette);
edx = cinemas[nVal].track;
check = checklevel;
}
void Start() override
{
if (check > 0 && check != selectedlevelnew)
{
state = finished;
return; // immediately abort if the player selected a different level on the map
}
check = -1;
StopAllSounds();
if (edx != -1)
{
playCDtrack(edx + 2, false);
}
}
void OnTick() override
{
if (!cont)
{
state = finished;
// quit the game if we've finished level 4 and displayed the advert text
if (isShareware() && currentCinemaPalette == 3)
{
gameaction = ga_mainmenu;
}
return;
}
}
void Draw(double smoothratio) override
{
twod->ClearScreen();
if (check == 0) return;
DrawTexture(twod, tileGetTexture(cinematile), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, TRANSLATION(Translation_BasePalettes, currentCinemaPalette), TAG_DONE);
text.DisplayText();
cont = text.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate));
}
};
//---------------------------------------------------------------------------
//
// last level cinema
//
//---------------------------------------------------------------------------
class DLastLevelCinema : public DScreenJob
{
int var_24 = 16;
int var_28 = 12;
int ebp;
int phase = 0;
int nextclock = 4;
unsigned int nStringTypeOn, nCharTypeOn;
int screencnt = 0;
bool skiprequest = false;
TArray<FString> screentext;
public:
DLastLevelCinema() : DScreenJob(fadein | fadeout) {}
private:
void DoStatic(int a, int b)
{
TileFiles.tileMakeWritable(kTileLoboLaptop);
auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
if (tex) tex->Reload();
auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop);
int v2 = 160 - a / 2;
int v4 = 81 - b / 2;
int y = 160 - a / 2;
int left = 81 - b / 2;
int var_18 = v2 + a;
int v5 = v4 + b;
int bottom = y + a;
int right = left + b;
auto pTile = (pixels + (200 * v2)) + v4;
auto pTile = (pixels + (200 * y)) + left;
TileFiles.InvalidateTile(kTileLoboLaptop);
while (v2 < var_18)
for(;y < bottom; y++)
{
uint8_t* pStart = pTile;
uint8_t* pixel = pTile;
pTile += 200;
int v7 = v4;
while (v7 < v5)
for (int x = left; x < right; x++)
{
*pStart = RandomBit() * 16;
v7++;
pStart++;
*pixel++ = RandomBit() * 16;
}
v2++;
}
}
void Phase1()
{
if (var_24 >= 116)
{
if (var_28 < 192)
var_28 += 20;
}
else
{
var_24 += 20;
}
DoStatic(var_28, var_24);
}
bool InitPhase2()
{
FStringf label("TXT_EX_LASTLEVEL%d", screencnt + 1);
label = GStrings(label);
screentext = label.Split("\n");
if (screentext.Size() == 0) return false;
nStringTypeOn = 0;
nCharTypeOn = 0;
ebp = screentext.Size() * 4; // half height of the entire text
ebp = 81 - ebp; // offset from the screen's center.
return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
}
static int UndoStatic()
{
auto tex = dynamic_cast<FRestorableTile*>(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage());
if (tex) tex->Reload();
TileFiles.InvalidateTile(kTileLoboLaptop);
return true;
}
bool Phase3()
{
DoStatic(var_28, var_24);
if (var_28 > 20) {
var_28 -= 20;
return true;
}
if (var_24 > 20) {
var_24 -= 20;
return true;
}
return false;
}
void DisplayPhase2()
{
int yy = ebp;
auto p = GStrings["REQUIRED_CHARACTERS"];
if (p && *p)
{
yy *= 2;
for (int i = 0; i < nStringTypeOn; i++, yy += 10)
{
DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit640x400, TAG_DONE);
}
DrawText(twod, ConFont, CR_GREEN, 140, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn, TAG_DONE);
}
else
{
for (int i = 0; i < nStringTypeOn; i++, yy += 8)
{
DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[i], DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE);
}
DrawText(twod, SmallFont2, CR_UNTRANSLATED, 70, yy, screentext[nStringTypeOn], DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn, TAG_DONE);
}
}
bool OnEvent(event_t* ev)
{
if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true;
return true;
}
void Start() override
{
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
phase = 1;
}
void OnTick() override
{
switch (phase)
{
case 1:
Phase1();
if (skiprequest || ticks >= nextclock)
{
InitPhase2();
phase = 2;
skiprequest = false;
}
break;
case 2:
if (screentext[nStringTypeOn][nCharTypeOn] != ' ')
PlayLocalSound(StaticSound[kSound71], 0, false, CHANF_UI);
nCharTypeOn++;
if (screentext[nStringTypeOn][nCharTypeOn] == 0)
{
nCharTypeOn = 0;
nStringTypeOn++;
if (nStringTypeOn >= screentext.Size())
{
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
phase = 3;
}
}
if (skiprequest)
{
nextclock = (GameTicRate * (screentext.Size() + 2)) + ticks;
phase = 4;
}
break;
case 3:
if (ticks >= nextclock || skiprequest)
{
PlayLocalSound(StaticSound[kSound75], 0, false, CHANF_UI);
phase = 4;
nextclock = ticks + 60;
skiprequest = false;
}
case 4:
if (ticks >= nextclock)
{
skiprequest |= !Phase3();
}
if (skiprequest)
{
// Go to the next text page.
if (screencnt != 2)
{
screencnt++;
nextclock = ticks + 60;
skiprequest = 0;
phase = 1;
}
else state = finished;
}
if (skiprequest)
{
state = finished;
}
}
}
void Draw(double) override
{
twod->ClearScreen();
DrawTexture(twod, tileGetTexture(kTileLoboLaptop), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
if (phase == 2 || phase == 3) DisplayPhase2();
}
};
//---------------------------------------------------------------------------
//
// Credits roll
//
//---------------------------------------------------------------------------
class DExCredits : public DScreenJob
{
TArray<FString> credits;
TArray<FString> pagelines;
uint64_t page;
uint64_t pagetime;
bool skiprequest = false;
public:
DExCredits()
{
auto textdata = fileSystem.LoadFile("credits.txt", 1);
FString text = (char*)textdata.Data();
text.Substitute("\r", "");
credits = text.Split("\n\n");
}
bool OnEvent(event_t* ev)
{
if (ev->type == EV_KeyDown && !specialKeyEvent(ev)) skiprequest = true;
return true;
}
void Start() override
{
if (credits.Size() == 0)
{
state = finished;
return;
}
playCDtrack(19, false);
pagetime = 0;
page = -1;
}
void OnTick() override
{
if (ticks >= pagetime || skiprequest)
{
page++;
if (page < credits.Size())
pagelines = credits[page].Split("\n");
else
{
if (skiprequest || !CDplaying())
{
state = finished;
return;
}
pagelines.Clear();
}
pagetime = ticks + 60; //
}
}
void Draw(double smoothratio) override
{
twod->ClearScreen();
int y = 100 - ((10 * (pagelines.Size() - 1)) / 2);
for (unsigned i = 0; i < pagelines.Size(); i++)
{
int ptime = clamp((pagetime - ticks - smoothratio) * 1000 / GameTicRate, 0, 2000); // in milliseconds
int light;
if (ptime < 255) light = ptime;
else if (ptime > 2000 - 255) light = 2000 - ptime;
else light = 255;
auto color = PalEntry(255, light, light, light);
int nStringWidth = SmallFont->StringWidth(pagelines[i]);
DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, color, TAG_DONE);
y += 10;
}
}
};
//---------------------------------------------------------------------------
//
// player died
//
//---------------------------------------------------------------------------
void DoGameOverScene(bool finallevel)
{
JobDesc job;
if (finallevel)
{
job = { Create<DCinema>(CINEMA_LOSE_SCENE) };
}
else
{
StopCD();
PlayGameOverSound();
job = { Create<DImageScreen>(tileGetTexture(kTile3591), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff, TRANSLATION(Translation_BasePalettes, 16)) };
}
RunScreenJob(&job, 1, [](bool) { gameaction = ga_mainmenu; });
return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex();
}
void DoAfterCinemaScene(int nLevel, TArray<JobDesc>& jobs)
DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, DoStatic, DoStatic)
{
int scene = -1;
if (nLevel == 10) scene = CINEMA_AFTER_LEVEL_10;
if (nLevel == 15) scene = CINEMA_AFTER_LEVEL_15;
if (nLevel == 20) scene = CINEMA_AFTER_LEVEL_20;
if (scene > 0) jobs.Push({ Create<DCinema>(scene) });
if (nLevel == 19) { jobs.Push({ Create<DLastLevelCinema>() }); selectedlevelnew = 20; }
if (nLevel == 20) jobs.Push({ Create<DExCredits>() });
PARAM_PROLOGUE;
PARAM_INT(x);
PARAM_INT(y);
ACTION_RETURN_INT(DoStatic(x, y));
}
void DoBeforeCinemaScene(int nLevel, TArray<JobDesc>& jobs)
DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, UndoStatic, UndoStatic)
{
if (nLevel == 5) jobs.Push({ Create<DCinema>(CINEMA_BEFORE_LEVEL_5) });
else if (nLevel == 11) jobs.Push({ Create<DCinema>(CINEMA_BEFORE_LEVEL_11, 11) });
ACTION_RETURN_INT(UndoStatic());
}
END_PS_NS

View file

@ -236,7 +236,7 @@ void DoBubbleMachines()
void BuildBubbleMachine(int nSprite)
{
if (nMachineCount >= kMaxMachines) {
I_Error("too many bubble machines in level %d\n", currentLevel->levelNumber);
I_Error("too many bubble machines in level %s\n", currentLevel->labelName.GetChars());
exit(-1);
}

View file

@ -72,13 +72,6 @@ void GameInterface::QuitToTitle()
gameaction = ga_mainmenu;
}
bool GameInterface::StartGame(FNewGameStartup& gs)
{
auto map = FindMapByLevelNum(gs.Skill); // 0 is training, 1 is the regular game - the game does not have skill levels.
DeferedStartGame(map, 1, true);
return true;
}
FSavegameInfo GameInterface::GetSaveSig()
{
return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS };
@ -90,12 +83,6 @@ END_PS_NS
using namespace Exhumed;
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw)
{
menu_DoPlasma();
return 0;
}
DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw)
{
auto nLogoTile = GameLogo();

View file

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

View file

@ -238,6 +238,15 @@ double calc_smoothratio()
return I_GetTimeFrac() * MaxSmoothRatio;
}
void DoGameOverScene(bool finallevel)
{
// todo: make these customizable later.
StartCutscene(finallevel ? "ExhumedCutscenes.BuildCinemaLose" : "ExhumedCutscenes.BuildGameoverScene", 0, [](bool)
{
gameaction = ga_mainmenu;
});
}
void GameMove(void)
{
FixPalette();
@ -247,7 +256,7 @@ void GameMove(void)
sprite[i].backuploc();
}
if (currentLevel->levelNumber == kMap20)
if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)
{
if (lCountDown <= 0)
{
@ -456,7 +465,7 @@ void GameInterface::Ticker()
void LevelFinished()
{
NextMap = currentLevel->levelNumber == 20 ? nullptr : FindMapByLevelNum(currentLevel->levelNumber + 1); // todo: Use the map record for progression
NextMap = FindNextMap(currentLevel);
EndLevel = 13;
}
@ -480,19 +489,6 @@ void GameInterface::app_init()
#if 0
help_disabled = true;
#endif
// Create the global level table. Parts of the engine need it, even though the game itself does not.
for (int i = 0; i <= 32; i++)
{
auto mi = AllocateMap();
mi->fileName.Format("LEV%d.MAP", i);
mi->labelName.Format("LEV%d", i);
mi->name.Format("$TXT_EX_MAP%02d", i);
mi->levelNumber = i;
int nTrack = i;
if (nTrack != 0) nTrack--;
mi->cdSongId = (nTrack % 8) + 11;
}
InitCheats();
registerosdcommands();
@ -615,7 +611,7 @@ void SerializeState(FSerializer& arc)
int loaded = 0;
if (arc.BeginObject("state"))
{
if (arc.isReading() && currentLevel->levelNumber == 20)
if (arc.isReading() && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
{
InitEnergyTile();
}
@ -639,7 +635,6 @@ void SerializeState(FSerializer& arc)
("bsnakecam", bSnakeCam)
("slipmode", bSlipMode)
("PlayClock", PlayClock)
("cinemaseen", nCinemaSeen)
("spiritsprite", nSpiritSprite)
.EndObject();
}

View file

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

View file

@ -49,32 +49,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "automap.h"
#include "raze_music.h"
#include "v_draw.h"
#include "vm.h"
BEGIN_PS_NS
short nBestLevel;
extern uint8_t nCinemaSeen;
void RunCinemaScene(int num);
void GameMove(void);
void DrawClock();
double calc_smoothratio();
void DoTitle(CompletionFunc completion);
static void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray<JobDesc> &jobs)
{
if (nLevelNew == 5 && !(nCinemaSeen & 1)) {
nCinemaSeen |= 1;
DoBeforeCinemaScene(5, jobs);
}
menu_DrawTheMap(nLevel, nLevelNew, nLevelBest, jobs);
if (nLevelNew == 11 && !(nCinemaSeen & 2)) {
DoBeforeCinemaScene(11, jobs);
}
}
void GameInterface::Render()
@ -83,7 +69,7 @@ void GameInterface::Render()
drawtime.Reset();
drawtime.Clock();
if (currentLevel && currentLevel->levelNumber == kMap20)
if (currentLevel && (currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
{
DoEnergyTile();
DrawClock();
@ -127,104 +113,89 @@ void GameInterface::DrawBackground()
DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150, 0);
}
void GameInterface::NextLevel(MapRecord *map, int skill)
{
InitLevel(map);
if (map->levelNumber >= nBestLevel)
{
nBestLevel = map->levelNumber - 1;
}
STAT_NewLevel(currentLevel->labelName);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void Intermission(MapRecord *from_map, MapRecord *to_map)
{
TArray<JobDesc> jobs;
if (from_map) StopAllSounds();
bCamera = false;
automapMode = am_off;
if (to_map)
{
if (to_map->levelNumber != 0)
nBestLevel = to_map->levelNumber - 1;
STAT_Update(false);
if (to_map->levelNumber == kMap20)
nPlayerLives[0] = 0;
if (to_map->levelNumber == 0) // skip all intermission stuff when going to the training map.
{
gameaction = ga_nextlevel;
return;
}
else
{
DoAfterCinemaScene(to_map->levelNumber - 1, jobs);
}
if (to_map->levelNumber > -1 && to_map->levelNumber < kMap20)
{
// start a new game at the given level
if (!nNetPlayerCount && to_map->levelNumber > 0)
{
showmap(from_map ? from_map->levelNumber : -1, to_map->levelNumber, nBestLevel, jobs);
}
else
jobs.Push({ Create<DBlackScreen>(1) }); // we need something in here even in the multiplayer case.
}
}
else
{
DoAfterCinemaScene(20, jobs);
STAT_Update(true);
}
if (jobs.Size() > 0)
{
RunScreenJob(jobs.Data(), jobs.Size(), [=](bool)
{
if (!to_map) gameaction = ga_startup; // this was the end of the game
else
{
if (to_map->levelNumber != selectedlevelnew)
{
// User can switch destination on the scrolling map.
g_nextmap = FindMapByLevelNum(selectedlevelnew);
STAT_Cancel();
}
gameaction = ga_nextlevel;
}
});
}
}
void GameInterface::NextLevel(MapRecord *map, int skill)
{
InitLevel(map->levelNumber);
if (map->levelNumber > nBestLevel)
{
nBestLevel = selectedlevelnew;
}
if (map->levelNumber == 11) nCinemaSeen |= 2;
STAT_NewLevel(currentLevel->labelName);
}
void GameInterface::NewGame(MapRecord *map, int skill, bool frommenu)
{
// start a new game on the given level
InitNewGame();
if (map->levelNumber == 1) STAT_StartNewGame("Exhumed", 1);
Intermission(nullptr, map);
InitLevel(map);
gameaction = ga_level;
}
void GameInterface::LevelCompleted(MapRecord *map, int skill)
int selectedlevelnew;
DEFINE_ACTION_FUNCTION(DMapScreen, SetNextLevel)
{
PARAM_PROLOGUE;
PARAM_INT(v);
selectedlevelnew = v;
return 0;
}
void GameInterface::LevelCompleted(MapRecord *to_map, int skill)
{
Mus_Stop();
if (currentLevel->levelNumber == 0) gameaction = ga_mainmenu;
Intermission(currentLevel, map);
if (currentLevel->gameflags & LEVEL_EX_TRAINING)
{
gameaction = ga_mainmenu;
return;
}
StopAllSounds();
bCamera = false;
automapMode = am_off;
STAT_Update(to_map == nullptr);
if (to_map)
{
if (to_map->levelNumber > nBestLevel) nBestLevel = to_map->levelNumber - 1;
if (to_map->gameflags & LEVEL_EX_COUNTDOWN) nPlayerLives[0] = 0;
if (to_map->gameflags & LEVEL_EX_TRAINING)
{
gameaction = ga_nextlevel;
return;
}
}
SummaryInfo info{};
info.kills = nCreaturesKilled;
info.maxkills = nCreaturesTotal;
info.supersecrets = nBestLevel;
info.time = PlayClock * GameTicRate / 120;
selectedlevelnew = to_map->levelNumber;
ShowIntermission(currentLevel, to_map, &info, [=](bool)
{
if (!to_map) gameaction = ga_startup; // this was the end of the game
else
{
if (to_map->levelNumber != selectedlevelnew)
{
// User can switch destination on the scrolling map.
g_nextmap = FindMapByLevelNum(selectedlevelnew);
STAT_Cancel();
}
gameaction = ga_nextlevel;
}
});
}
//---------------------------------------------------------------------------
@ -237,22 +208,7 @@ void GameInterface::Startup()
{
resettiming();
EndLevel = 0;
if (userConfig.CommandMap.IsNotEmpty())
{
/*
auto map = FindMapByName(userConfig.CommandMap);
if (map) DeferedStartMap(map, 0);
userConfig.CommandMap = "";
goto again;
*/
}
else
{
if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; });
else gameaction = ga_mainmenu;
}
PlayLogos(ga_mainmenu, ga_mainmenu, false);
}
void GameInterface::ErrorCleanup()

View file

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

View file

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

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