From 4069a5096a87a3cde917264d7ecbd289724d21ec Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 30 Apr 2021 16:21:37 +0200 Subject: [PATCH] - scriptified Exhumed's 2D content (minus the programmatic textures.) --- .../scripting/interface/stringformat.cpp | 16 +- .../common/scripting/interface/vmnatives.cpp | 8 + source/core/binaryangle.h | 8 +- source/core/gamecontrol.cpp | 18 +- source/core/mainloop.cpp | 4 +- source/core/screenjob.cpp | 20 +- source/games/exhumed/src/2d.cpp | 1026 +---------------- source/games/exhumed/src/d_menu.cpp | 6 - source/games/exhumed/src/exhumed.cpp | 10 +- source/games/exhumed/src/exhumed.h | 9 +- source/games/exhumed/src/gameloop.cpp | 77 +- source/games/exhumed/src/init.cpp | 23 +- source/games/exhumed/src/menu.cpp | 2 - source/games/exhumed/src/movie.cpp | 163 ++- source/games/exhumed/src/namelist.h | 98 ++ source/games/exhumed/src/names.h | 128 +- source/games/exhumed/src/queen.cpp | 2 +- source/games/exhumed/src/sound.cpp | 31 + .../static/filter/exhumed/engine/engine.def | 71 ++ wadsrc/static/zscript.txt | 2 + wadsrc/static/zscript/engine/base.zs | 3 + wadsrc/static/zscript/engine/inputevents.zs | 15 + wadsrc/static/zscript/engine/ui/menu/menu.zs | 1 + .../zscript/games/exhumed/exhumedgame.zs | 104 ++ .../static/zscript/games/exhumed/ui/menu.zs | 5 +- .../zscript/games/exhumed/ui/screens.zs | 927 +++++++++++++++ wadsrc/static/zscript/razebase.zs | 2 + 27 files changed, 1522 insertions(+), 1257 deletions(-) create mode 100644 wadsrc/static/filter/exhumed/engine/engine.def create mode 100644 wadsrc/static/zscript/games/exhumed/exhumedgame.zs create mode 100644 wadsrc/static/zscript/games/exhumed/ui/screens.zs diff --git a/source/common/scripting/interface/stringformat.cpp b/source/common/scripting/interface/stringformat.cpp index 8e0bf0c75..da667cbbf 100644 --- a/source/common/scripting/interface/stringformat.cpp +++ b/source/common/scripting/interface/stringformat.cpp @@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl) ACTION_RETURN_FLOAT(self->ToDouble()); } -static void StringSplit(FString *self, TArray *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* tokens, const FString& delimiter, int keepEmpty) { self->Split(*tokens, delimiter, static_cast(keepEmpty)); } diff --git a/source/common/scripting/interface/vmnatives.cpp b/source/common/scripting/interface/vmnatives.cpp index 570a423f1..5eebabbab 100644 --- a/source/common/scripting/interface/vmnatives.cpp +++ b/source/common/scripting/interface/vmnatives.cpp @@ -881,6 +881,13 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, GetAllKeysForCommand) return 0; } +DEFINE_ACTION_FUNCTION(FKeyBindings, GetBinding) +{ + PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); + PARAM_INT(key); + ACTION_RETURN_STRING(self->GetBinding(key)); +} + DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand) { PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); @@ -928,6 +935,7 @@ DEFINE_GLOBAL_NAMED(mus_playing, musplaying); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, name); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder); DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop); +DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, handle); DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses) DEFINE_GLOBAL(Bindings) diff --git a/source/core/binaryangle.h b/source/core/binaryangle.h index 3cdc8dc49..de895ee2f 100644 --- a/source/core/binaryangle.h +++ b/source/core/binaryangle.h @@ -69,11 +69,11 @@ constexpr double BAngToDegree = 360. / 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))); } diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 19c09124d..b08f26383 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -555,7 +555,7 @@ int GameMain() I_ShowFatalError(err.what()); r = -1; } - DeleteScreenJob(); + //DeleteScreenJob(); DeinitMenus(); if (StatusBar) StatusBar->Destroy(); StatusBar = nullptr; @@ -1479,6 +1479,22 @@ DEFINE_ACTION_FUNCTION(_Raze, PlayerName) 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)); +} + extern bool demoplayback; DEFINE_GLOBAL(multiplayer) DEFINE_GLOBAL(netgame) diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 7db914ed9..1bd454228 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -136,7 +136,9 @@ bool newGameStarted; void NewGame(MapRecord* map, int skill, bool ns = false) { newGameStarted = true; - ShowIntermission(nullptr, map, nullptr, [=](bool) { gi->NewGame(map, skill, ns); }); + ShowIntermission(nullptr, map, nullptr, [=](bool) { + gi->NewGame(map, skill, ns); + }); } //========================================================================== diff --git a/source/core/screenjob.cpp b/source/core/screenjob.cpp index 1e0e3c3ce..8dfaee5a2 100644 --- a/source/core/screenjob.cpp +++ b/source/core/screenjob.cpp @@ -148,15 +148,18 @@ void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map) // //============================================================================= -void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* map, SummaryInfo* info) +void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* map, SummaryInfo* info, MapRecord* map2) { auto func = LookupFunction(qname); - if (func->Proto->ArgumentTypes.Size() != 3) I_Error("Bad map-cutscene function %s. Must receive precisely three arguments.", qname); - if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype && func->Proto->ArgumentTypes[2] != summaryinfotype) - I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference.", qname); - summaryinfo = *info; // must be copied to a persistent location. - VMValue val[3] = { runner, map, &summaryinfo }; - VMCall(func, val, 3, nullptr, 0); + 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); } //============================================================================= @@ -480,8 +483,9 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C globalCutscenes.DefaultMapOutro.Create(runner, fromMap, !!toMap); } - CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info); } + if (fromMap || (g_gameType & GAMEFLAG_PSEXHUMED)) + CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info, toMap); if (toMap) { diff --git a/source/games/exhumed/src/2d.cpp b/source/games/exhumed/src/2d.cpp index 66e880ca5..4131f1e1d 100644 --- a/source/games/exhumed/src/2d.cpp +++ b/source/games/exhumed/src/2d.cpp @@ -39,6 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "m_random.h" #include "gstrings.h" #include "c_bind.h" +#include "vm.h" #include @@ -46,8 +47,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_PS_NS -int selectedlevelnew; - //--------------------------------------------------------------------------- // // @@ -372,476 +371,26 @@ void menu_DoPlasma() DrawRel(kTile3512 + ((dword_9AB5F + 2) & 3), 270, 150); } -#if 0 -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- -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); - } - } -}; -#endif - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -//DScreenJob *PlayMovie(const char* fileName); - -void DoTitle(CompletionFunc completion) -{ -#if 0 - TArray jobs; - - jobs.Push(Create(tileGetTexture(PublisherLogo()), DScreenJob::fadein | DScreenJob::fadeout)); - jobs.Push(Create(tileGetTexture(seq_GetSeqPicnum(kSeqScreens, 0, 0)), DScreenJob::fadein | DScreenJob::fadeout)); - jobs.Push(PlayMovie("book.mov")); - jobs.Push(Create()); - - RunScreenJob(jobs, completion, SJ_BLOCKUI); -#endif + menu_DoPlasma(); + return 0; } -#if 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 &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(nLevel-1, nLevelNew-1, nLevelBest-1)); - } - -#endif - -//--------------------------------------------------------------------------- -// -// text overlay -// -//--------------------------------------------------------------------------- + lastclock = 0; + FString ttext = GStrings(text); + screentext = ttext.Split("\n"); + ComputeCinemaText(); +} void TextOverlay::Start(double starttime) { @@ -907,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", @@ -967,514 +489,64 @@ void uploadCinemaPalettes() } } -#if 0 //--------------------------------------------------------------------------- // -// 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; + auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop); -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 + int v2 = 160 - a / 2; + int v4 = 81 - b / 2; + + int var_18 = v2 + a; + int v5 = v4 + b; + + auto pTile = (pixels + (200 * v2)) + v4; + + TileFiles.InvalidateTile(kTileLoboLaptop); + + while (v2 < var_18) { - if (check > 0 && check != selectedlevelnew) + uint8_t* pStart = pTile; + pTile += 200; + + int v7 = v4; + + while (v7 < v5) { - 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); + *pStart = RandomBit() * 16; + + v7++; + pStart++; } + v2++; } - - 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 screentext; - -public: - DLastLevelCinema() : DScreenJob(fadein | fadeout) {} - -private: - void DoStatic(int a, int b) - { - auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop); - - int v2 = 160 - a / 2; - int v4 = 81 - b / 2; - - int var_18 = v2 + a; - int v5 = v4 + b; - - auto pTile = (pixels + (200 * v2)) + v4; - - TileFiles.InvalidateTile(kTileLoboLaptop); - - while (v2 < var_18) - { - uint8_t* pStart = pTile; - pTile += 200; - - int v7 = v4; - - while (v7 < v5) - { - *pStart = RandomBit() * 16; - - v7++; - pStart++; - } - 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. - - auto tex = dynamic_cast(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 credits; - TArray 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; - } - } -}; -#endif - -//--------------------------------------------------------------------------- -// -// player died -// -//--------------------------------------------------------------------------- - -void DoGameOverScene(bool finallevel) -{ -#if 0 - TArray jobs(1, true); - - if (finallevel) - { - jobs[0] = Create(CINEMA_LOSE_SCENE); - } - else - { - StopCD(); - PlayGameOverSound(); - jobs[0] = Create(tileGetTexture(kTile3591), DScreenJob::fadein | DScreenJob::fadeout, 0x7fffffff, TRANSLATION(Translation_BasePalettes, 16)); - } - RunScreenJob(jobs, [](bool) { gameaction = ga_mainmenu; }); -#endif + return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex(); } -#if 0 - -void DoAfterCinemaScene(int nLevel, TArray& jobs) +static int UndoStatic() { - 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(scene)); - if (nLevel == 19) { jobs.Push(Create()); selectedlevelnew = 20; } - if (nLevel == 20) jobs.Push(Create()); + auto tex = dynamic_cast(tileGetTexture(kTileLoboLaptop)->GetTexture()->GetImage()); + if (tex) tex->Reload(); + TileFiles.InvalidateTile(kTileLoboLaptop); + return tileGetTexture(kTileLoboLaptop)->GetID().GetIndex(); } -void DoBeforeCinemaScene(int nLevel, TArray& jobs) +DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, DoStatic, DoStatic) { - if (nLevel == 5) jobs.Push(Create(CINEMA_BEFORE_LEVEL_5)); - else if (nLevel == 11) jobs.Push(Create(CINEMA_BEFORE_LEVEL_11, 11)); + PARAM_PROLOGUE; + PARAM_INT(x); + PARAM_INT(y); + ACTION_RETURN_INT(DoStatic(x, y)); } -void showmap(short nLevel, short nLevelNew, short nLevelBest, TArray& jobs) +DEFINE_ACTION_FUNCTION_NATIVE(DLastLevelCinema, UndoStatic, UndoStatic) { - if (nLevelNew == 5 && !(nCinemaSeen & 1)) { - nCinemaSeen |= 1; - DoBeforeCinemaScene(5, jobs); - } - - menu_DrawTheMap(nLevel, nLevelNew, nLevelBest, jobs); - - if (nLevelNew == 11 && !(nCinemaSeen & 2)) { - DoBeforeCinemaScene(11, jobs); - } + ACTION_RETURN_INT(UndoStatic()); } -#endif - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void Intermission(MapRecord* from_map, MapRecord* to_map) -{ -#if 0 - TArray 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(1)); // we need something in here even in the multiplayer case. - } - } - else - { - DoAfterCinemaScene(20, jobs); - STAT_Update(true); - } - - - if (jobs.Size() > 0) - { - RunScreenJob(jobs, [=](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; - - } - }); - } -#endif -} - END_PS_NS diff --git a/source/games/exhumed/src/d_menu.cpp b/source/games/exhumed/src/d_menu.cpp index 79061cb69..4a7fdf88e 100644 --- a/source/games/exhumed/src/d_menu.cpp +++ b/source/games/exhumed/src/d_menu.cpp @@ -90,12 +90,6 @@ END_PS_NS using namespace Exhumed; -DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedPlasma, Draw) -{ - menu_DoPlasma(); - return 0; -} - DEFINE_ACTION_FUNCTION(_ListMenuItemExhumedLogo, Draw) { auto nLogoTile = GameLogo(); diff --git a/source/games/exhumed/src/exhumed.cpp b/source/games/exhumed/src/exhumed.cpp index 2426b2208..95ca2ff4b 100644 --- a/source/games/exhumed/src/exhumed.cpp +++ b/source/games/exhumed/src/exhumed.cpp @@ -244,6 +244,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(); @@ -646,7 +655,6 @@ void SerializeState(FSerializer& arc) ("bsnakecam", bSnakeCam) ("slipmode", bSlipMode) ("PlayClock", PlayClock) - ("cinemaseen", nCinemaSeen) ("spiritsprite", nSpiritSprite) .EndObject(); } diff --git a/source/games/exhumed/src/exhumed.h b/source/games/exhumed/src/exhumed.h index 6f424fbdc..ab0e531f5 100644 --- a/source/games/exhumed/src/exhumed.h +++ b/source/games/exhumed/src/exhumed.h @@ -81,7 +81,7 @@ void DoSpiritHead(); void CheckKeys2(); void GameTicker(); -void InitLevel(int); +void InitLevel(MapRecord*); void InitNewGame(); int showmap(short nLevel, short nLevelNew, short nLevelBest); @@ -124,7 +124,6 @@ extern short nCurBodyNum; extern short nBodyTotal; extern short bSnakeCam; -extern uint8_t nCinemaSeen; extern short nButtonColor; @@ -152,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; @@ -198,6 +192,7 @@ public: void DisplayText(); bool AdvanceCinemaText(double clock); void SetPalette(int pal) { currentCinemaPalette = pal; } + void Create(const FString& text, int pal); }; diff --git a/source/games/exhumed/src/gameloop.cpp b/source/games/exhumed/src/gameloop.cpp index a95e8299d..c8276456a 100644 --- a/source/games/exhumed/src/gameloop.cpp +++ b/source/games/exhumed/src/gameloop.cpp @@ -49,13 +49,12 @@ 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(); @@ -116,33 +115,87 @@ void GameInterface::DrawBackground() void GameInterface::NextLevel(MapRecord *map, int skill) { - InitLevel(map->levelNumber); + InitLevel(map); - if (map->levelNumber > nBestLevel) + if (map->levelNumber >= nBestLevel) { - nBestLevel = selectedlevelnew; + nBestLevel = map->levelNumber - 1; } - if (map->levelNumber == 11) nCinemaSeen |= 2; STAT_NewLevel(currentLevel->labelName); } -void Intermission(MapRecord* from_map, MapRecord* to_map); +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- 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->levelNumber == 0) + { + 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->levelNumber == 20) nPlayerLives[0] = 0; + if (to_map->levelNumber == 0) // skip all intermission stuff when going to the training map. + { + 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; + + } + }); } //--------------------------------------------------------------------------- diff --git a/source/games/exhumed/src/init.cpp b/source/games/exhumed/src/init.cpp index ad81fc807..8645a05ac 100644 --- a/source/games/exhumed/src/init.cpp +++ b/source/games/exhumed/src/init.cpp @@ -66,9 +66,9 @@ uint8_t bIsVersion6 = true; -uint8_t LoadLevel(int nMap) +uint8_t LoadLevel(MapRecord* map) { - if (nMap == kMap20) + if (map->levelNumber == kMap20) { lCountDown = 81000; nAlarmTicks = 30; @@ -116,12 +116,12 @@ uint8_t LoadLevel(int nMap) InitItems(); InitInput(); - if (nMap == kMap20) { + if (map->levelNumber == kMap20) { InitEnergyTile(); } } - if (nMap > 15) + if (map->levelNumber > 15) { 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,7 +196,7 @@ void InitLevel(int level) // todo: use a map record RefreshStatus(); - int nTrack = level; + int nTrack = map->levelNumber; if (nTrack != 0) nTrack--; playCDtrack((nTrack % 8) + 11, true); @@ -210,7 +206,6 @@ void InitLevel(int level) // todo: use a map record void InitNewGame() { bCamera = false; - nCinemaSeen = 0; PlayerCount = 0; for (int i = 0; i < nTotalPlayers; i++) diff --git a/source/games/exhumed/src/menu.cpp b/source/games/exhumed/src/menu.cpp index f47158246..52d05404e 100644 --- a/source/games/exhumed/src/menu.cpp +++ b/source/games/exhumed/src/menu.cpp @@ -43,8 +43,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_PS_NS -uint8_t nCinemaSeen; - uint8_t energytile[66 * 66] = {0}; uint8_t *cur; diff --git a/source/games/exhumed/src/movie.cpp b/source/games/exhumed/src/movie.cpp index 85399eb3e..b5c57a221 100644 --- a/source/games/exhumed/src/movie.cpp +++ b/source/games/exhumed/src/movie.cpp @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "s_music.h" #include "screenjob.h" #include "v_draw.h" +#include "vm.h" BEGIN_PS_NS @@ -63,9 +64,34 @@ class LMFPlayer AudioData audio{}; AnimTextures animtex; + FileReader fp; + int nFrame = 0; + uint64_t nextclock = 0; + public: + + LMFPlayer(const char *filename) + { + fp = fileSystem.OpenFileReader(filename); + Open(fp); + } + + bool Frame(uint64_t clock) + { + if (clock >= nextclock) + { + nextclock += 100'000'000; + if (ReadFrame(fp) == 0) + { + return true; + } + } + + return false; + } + int ReadFrame(FileReader& fp) { nFrame++; @@ -188,107 +214,56 @@ public: S_StopCustomStream(stream); } - AnimTextures& animTex() + FTextureID GetTexture() { - return animtex; + return animtex.GetFrameID(); } }; -#if 0 -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -class DLmfPlayer : public DSkippableScreenJob +int IdentifyLMF(const FString* fn) { - LMFPlayer decoder; - double angle = 1536; - double z = 0; - uint64_t nextclock = 0, lastclock = 0; - FileReader fp; - -public: - - DLmfPlayer(FileReader& fr) - { - decoder.Open(fr); - lastclock = 0; - nextclock = 0; - fp = std::move(fr); - pausable = false; - } - - //--------------------------------------------------------------------------- - // - // - // - //--------------------------------------------------------------------------- - - void Draw(double smoothratio) override - { - uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate; - if (clock >= nextclock) - { - nextclock += 100'000'000; - if (decoder.ReadFrame(fp) == 0) - { - state = finished; - return; - } - } - - double duration = (clock - lastclock) * double(120. / 8'000'000'000); - if (z < 65536) { // Zoom - normal zoom is 65536. - z += 2048 * duration; - } - if (z > 65536) z = 65536; - if (angle != 0) { - angle += 16. * duration; - if (angle >= 2048) { - angle = 0; - } - } - - { - twod->ClearScreen(); - DrawTexture(twod, decoder.animTex().GetFrame(), 160, 100, DTA_FullscreenScale, FSMode_Fit320x200, - DTA_CenterOffset, true, DTA_FlipY, true, DTA_ScaleX, z / 65536., DTA_ScaleY, z / 65536., DTA_Rotate, (-angle - 512) * BAngToDegree, TAG_DONE); - } - - lastclock = clock; - } - - void OnDestroy() override - { - decoder.Close(); - fp.Close(); - } -}; - - - -DScreenJob* PlayMovie(const char* fileName) -{ - // clear keys - - auto fp = fileSystem.OpenFileReader(fileName); - if (!fp.isOpen()) - { - return Create(1); - } + auto fp = fileSystem.OpenFileReader(*fn); + if (!fp.isOpen()) return false; char buffer[4]; fp.Read(buffer, 4); - if (memcmp(buffer, "LMF ", 4)) - { - fp.Close(); - // Allpw replacement with more modern formats. - return PlayVideo(fileName); - } - fp.Seek(0, FileReader::SeekSet); - return Create(fp); + return (0 == memcmp(buffer, "LMF ", 4)); } -#endif + +DEFINE_ACTION_FUNCTION(_LMFDecoder, Create) +{ + PARAM_PROLOGUE; + PARAM_STRING(fn); + ACTION_RETURN_POINTER(new LMFPlayer(fn)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_LMFDecoder, Identify, IdentifyLMF) +{ + PARAM_PROLOGUE; + PARAM_STRING(fn); + ACTION_RETURN_BOOL(IdentifyLMF(&fn)); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, Frame) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + PARAM_FLOAT(clock); + ACTION_RETURN_BOOL(self->Frame(uint64_t(clock))); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, GetTexture) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + ACTION_RETURN_INT(self->GetTexture().GetIndex()); +} + +DEFINE_ACTION_FUNCTION(_LMFDecoder, Close) +{ + PARAM_SELF_STRUCT_PROLOGUE(LMFPlayer); + self->Close(); + delete self; + return 0; +} + + END_PS_NS diff --git a/source/games/exhumed/src/namelist.h b/source/games/exhumed/src/namelist.h index 2a6a22d87..50787ef8d 100644 --- a/source/games/exhumed/src/namelist.h +++ b/source/games/exhumed/src/namelist.h @@ -1,3 +1,4 @@ +x(SkullJaw, 3437) x(PowerslaveLogo, 3442) x(MenuNewGameTile, 3460) x(MenuLoadGameTile, 3461) @@ -6,6 +7,7 @@ x(MenuSoundFxTile, 3466) x(MenuCursorTile, 3468) x(MenuBlank, 3469) x(SkullHead, 3582) +x(SkullJaw2, 3583) x(ExhumedLogo, 3592) x(Energy1, 3604) x(Energy2, 3605) @@ -27,4 +29,100 @@ x(ClockSymbol15, 3620) x(ClockSymbol16, 3621) x(TileLoboLaptop, 3623) x(TileBMGLogo, 3368) +x(LobotomyLogo, 3348) x(TilePIELogo, 3349) +x(Gameover, 3591) + +x(MapPlaque1_01, 3376) +x(MapPlaque1_02, 3378) +x(MapPlaque1_03, 3380) +x(MapPlaque1_04, 3382) +x(MapPlaque1_05, 3384) +x(MapPlaque1_06, 3371) +x(MapPlaque1_07, 3387) +x(MapPlaque1_08, 3389) +x(MapPlaque1_09, 3391) +x(MapPlaque1_10, 3409) +x(MapPlaque1_11, 3393) +x(MapPlaque1_12, 3395) +x(MapPlaque1_13, 3397) +x(MapPlaque1_14, 3399) +x(MapPlaque1_15, 3401) +x(MapPlaque1_16, 3403) +x(MapPlaque1_17, 3405) +x(MapPlaque1_18, 3407) +x(MapPlaque1_19, 3412) +x(MapPlaque1_20, 3415) + +x(MapPlaque2_01, 3377) +x(MapPlaque2_02, 3379) +x(MapPlaque2_03, 3381) +x(MapPlaque2_04, 3383) +x(MapPlaque2_05, 3385) +x(MapPlaque2_06, 3386) +x(MapPlaque2_07, 3388) +x(MapPlaque2_08, 3390) +x(MapPlaque2_09, 3392) +x(MapPlaque2_10, 3410) +x(MapPlaque2_11, 3394) +x(MapPlaque2_12, 3396) +x(MapPlaque2_13, 3398) +x(MapPlaque2_14, 3400) +x(MapPlaque2_15, 3402) +x(MapPlaque2_16, 3404) +x(MapPlaque2_17, 3406) +x(MapPlaque2_18, 3408) +x(MapPlaque2_19, 3413) +x(MapPlaque2_20, 3416) + +x(MapPlaqueText_01, 3411) +x(MapPlaqueText_02, 3414) +x(MapPlaqueText_03, 3417) +x(MapPlaqueText_04, 3420) +x(MapPlaqueText_05, 3423) +x(MapPlaqueText_06, 3426) +x(MapPlaqueText_07, 3429) +x(MapPlaqueText_08, 3432) +x(MapPlaqueText_09, 3435) +x(MapPlaqueText_10, 3418) +x(MapPlaqueText_11, 3438) +x(MapPlaqueText_12, 3441) +x(MapPlaqueText_13, 3444) +x(MapPlaqueText_14, 3447) +x(MapPlaqueText_15, 3450) +x(MapPlaqueText_16, 3453) +x(MapPlaqueText_17, 3456) +x(MapPlaqueText_18, 3459) +x(MapPlaqueText_19, 3419) +x(MapPlaqueText_20, 3421) + +x(MapFire_11, 3484) +x(MapFire_12, 3485) +x(MapFire_13, 3486) +x(MapFire_14, 3487) +x(MapFire_21, 3488) +x(MapFire_22, 3489) +x(MapFire_23, 3490) +x(MapFire_24, 3491) +x(MapFire_31, 3492) +x(MapFire_32, 3493) +x(MapFire_33, 3494) +x(MapFire_34, 3495) + +x(MapBG01, 3353) +x(MapBG02, 3354) +x(MapBG03, 3355) +x(MapBG04, 3356) +x(MapBG05, 3357) +x(MapBG06, 3358) +x(MapBG07, 3359) +x(MapBG08, 3360) +x(MapBG09, 3361) +x(MapBG10, 3362) + +x(TileCinema5, 3449) +x(TileCinema10, 3451) +x(TileCinema11, 3454) +x(TileCinema15, 3446) +x(TileCinemaLose, 3445) +x(TileCinema20, 3448) diff --git a/source/games/exhumed/src/names.h b/source/games/exhumed/src/names.h index 6f0292256..ed087cf72 100644 --- a/source/games/exhumed/src/names.h +++ b/source/games/exhumed/src/names.h @@ -29,136 +29,10 @@ kTileStatusBar = 657, kTile985 = 985, kTile986 = 986, kTile3000 = 3000, -kTile3117 = 3117, +kQueenChunk = 3117, kTile3126 = 3126, -kTile3353 = 3353, -kTile3370 = 3370, -kTile3371 = 3371, -kTile3372 = 3372, -kTile3373 = 3373, -kTile3374 = 3374, -kTile3375 = 3375, -kTile3376 = 3376, -kTile3377 = 3377, -kTile3378 = 3378, -kTile3379 = 3379, -kTile3380 = 3380, -kTile3381 = 3381, -kTile3382 = 3382, -kTile3383 = 3383, -kTile3384 = 3384, -kTile3385 = 3385, -kTile3386 = 3386, -kTile3387 = 3387, -kTile3388 = 3388, -kTile3389 = 3389, -kTile3390 = 3390, -kTile3391 = 3391, -kTile3392 = 3392, -kTile3393 = 3393, -kTile3394 = 3394, -kTile3395 = 3395, -kTile3396 = 3396, -kTile3397 = 3397, -kTile3398 = 3398, -kTile3399 = 3399, -kTile3400 = 3400, -kTile3401 = 3401, -kTile3402 = 3402, -kTile3403 = 3403, -kTile3404 = 3404, -kTile3405 = 3405, -kTile3406 = 3406, -kTile3407 = 3407, -kTile3408 = 3408, -kTile3409 = 3409, -kTile3410 = 3410, -kTile3411 = 3411, -kTile3412 = 3412, -kTile3413 = 3413, -kTile3414 = 3414, -kTile3415 = 3415, -kTile3416 = 3416, -kTile3417 = 3417, -kTile3418 = 3418, -kTile3419 = 3419, -kTile3420 = 3420, -kTile3421 = 3421, -kTile3422 = 3422, -kTile3423 = 3423, -kTile3424 = 3424, -kTile3425 = 3425, -kTile3426 = 3426, -kTile3427 = 3427, -kTile3428 = 3428, -kTile3429 = 3429, -kTile3430 = 3430, -kTile3431 = 3431, -kTile3432 = 3432, -kTile3433 = 3433, -kTile3434 = 3434, -kTile3435 = 3435, -kTile3436 = 3436, -kSkullJaw = 3437, -kTile3438 = 3438, -kTile3439 = 3439, -kTile3440 = 3440, -kTile3441 = 3441, -kTile3443 = 3443, -kTile3444 = 3444, -kTile3445 = 3445, -kTile3446 = 3446, -kTile3447 = 3447, -kTile3448 = 3448, -kTile3449 = 3449, -kTile3450 = 3450, -kTile3451 = 3451, -kTile3452 = 3452, -kTile3453 = 3453, -kTile3454 = 3454, -kTile3455 = 3455, -kTile3456 = 3456, -kTile3457 = 3457, -kTile3458 = 3458, -kTile3459 = 3459, -kTile3462 = 3462, -kTile3463 = 3463, -kTile3464 = 3464, -kTile3467 = 3467, -kTile3470 = 3470, -kTile3471 = 3471, -kTile3472 = 3472, -kTile3473 = 3473, -kTile3474 = 3474, -kTile3475 = 3475, -kTile3476 = 3476, -kTile3477 = 3477, -kTile3478 = 3478, -kTile3479 = 3479, -kTile3480 = 3480, -kTile3481 = 3481, -kTile3482 = 3482, -kTile3483 = 3483, -kTile3484 = 3484, -kTile3485 = 3485, -kTile3486 = 3486, -kTile3487 = 3487, -kTile3488 = 3488, -kTile3489 = 3489, -kTile3490 = 3490, -kTile3491 = 3491, -kTile3492 = 3492, -kTile3493 = 3493, -kTile3494 = 3494, -kTile3495 = 3495, -kTile3496 = 3496, -kTile3497 = 3497, -kTile3498 = 3498, -kTile3499 = 3499, kTile3512 = 3512, kTile3571 = 3571, -kTile3583 = 3583, -kTile3591 = 3591, kTile3593 = 3593, kTile3603 = 3603, kTile4092 = 4092, diff --git a/source/games/exhumed/src/queen.cpp b/source/games/exhumed/src/queen.cpp index 02839887d..0e4b73bda 100644 --- a/source/games/exhumed/src/queen.cpp +++ b/source/games/exhumed/src/queen.cpp @@ -1445,7 +1445,7 @@ void FuncQueen(int a, int nDamage, int nRun) { short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF; - sprite[nChunkSprite].picnum = kTile3117 + (i % 3); + sprite[nChunkSprite].picnum = kQueenChunk + (i % 3); sprite[nChunkSprite].xrepeat = 100; sprite[nChunkSprite].yrepeat = 100; } diff --git a/source/games/exhumed/src/sound.cpp b/source/games/exhumed/src/sound.cpp index d7343d0e9..d9d35005f 100644 --- a/source/games/exhumed/src/sound.cpp +++ b/source/games/exhumed/src/sound.cpp @@ -800,4 +800,35 @@ void PlayGameOverSound(void) PlayLocalSound(StaticSound[kSoundJonLaugh2], 0, false, CHANF_UI); } +DEFINE_ACTION_FUNCTION(_Exhumed, PlayLocalSound) +{ + PARAM_PROLOGUE; + PARAM_INT(snd); + PARAM_INT(pitch); + PARAM_BOOL(unatt); + PARAM_INT(flags); + PlayLocalSound(StaticSound[snd], pitch, unatt, EChanFlags::FromInt(flags)); + return 0; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Exhumed, StopLocalSound, StopLocalSound) +{ + StopLocalSound(); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Exhumed, LocalSoundPlaying) +{ + ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1)); +} + +DEFINE_ACTION_FUNCTION(_Exhumed, PlayCDTrack) +{ + PARAM_PROLOGUE; + PARAM_INT(track); + PARAM_BOOL(loop); + playCDtrack(track, loop); + return 0; +} + END_PS_NS diff --git a/wadsrc/static/filter/exhumed/engine/engine.def b/wadsrc/static/filter/exhumed/engine/engine.def new file mode 100644 index 000000000..42a6265ef --- /dev/null +++ b/wadsrc/static/filter/exhumed/engine/engine.def @@ -0,0 +1,71 @@ +// Cutscene definitions for Duke + +definecutscene intro +{ + function ExhumedCutscenes.BuildIntro +} + +definecutscene map lev5 +{ + intro + { + function ExhumedCutscenes.BuildCinemaBefore5 + } +} + +definecutscene map lev10 +{ + outro + { + function ExhumedCutscenes.BuildCinemaAfter10 + } +} + +definecutscene map lev11 +{ + intro + { + function ExhumedCutscenes.BuildCinemaBefore11 + } +} + +definecutscene map lev15 +{ + outro + { + function ExhumedCutscenes.BuildCinemaAfter15 + } +} + +definecutscene map lev20 +{ + intro + { + function ExhumedCutscenes.BuildCinemaBefore20 + } + outro + { + function ExhumedCutscenes.BuildCinemaAfter20 + } +} + +/* +definecutscene gameover +{ + function ExhumedCutscenes.BuildGameoverScene +} + +definecutscene lose +{ + function ExhumedCutscenes.BuildCinemaLose +} + +definecutscene loading +{ + function DukeCutscenes.BuildLoading +} +*/ + +definecutscene summary ExhumedCutscenes.BuildMap +//definecutscene mpsummary DukeCutscenes.BuildMPSummary + diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 6a79ab679..262e72b63 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -39,4 +39,6 @@ version "4.3" #include "zscript/games/sw/swgame.zs" #include "zscript/games/sw/ui/menu.zs" #include "zscript/games/sw/ui/screens.zs" +#include "zscript/games/exhumed/exhumedgame.zs" #include "zscript/games/exhumed/ui/menu.zs" +#include "zscript/games/exhumed/ui/screens.zs" diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index 5749ff38c..21115a193 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -187,6 +187,8 @@ struct MusPlayingInfo native native String name; native int baseorder; native bool loop; + native voidptr handle; + }; struct TexMan @@ -662,6 +664,7 @@ struct StringStruct native native void DeleteLastCharacter(); native int CodePointCount() const; native int, int GetNextCodePoint(int position) const; + native void Substitute(String str, String replace); } struct Translation version("2.4") diff --git a/wadsrc/static/zscript/engine/inputevents.zs b/wadsrc/static/zscript/engine/inputevents.zs index 55df156ed..ba63b59e9 100644 --- a/wadsrc/static/zscript/engine/inputevents.zs +++ b/wadsrc/static/zscript/engine/inputevents.zs @@ -120,6 +120,21 @@ struct InputEvent native play version("2.4") Key_F12 = 0x58, // DIK_F12 Key_Grave = 0x29, // DIK_GRAVE + KEY_kpad_1 = 0x4f, + KEY_kpad_2 = 0x50, + KEY_kpad_3 = 0x51, + KEY_kpad_4 = 0x4b, + KEY_kpad_5 = 0x4c, + KEY_kpad_6 = 0x4d, + KEY_kpad_7 = 0x47, + KEY_kpad_8 = 0x48, + KEY_kpad_9 = 0x49, + KEY_kpad_0 = 0x52, + KEY_kpad_Minus = 0x4a, + KEY_kpad_Plus = 0x4e, + KEY_kpad_Period = 0x53, + + Key_Backspace = 0x0e, // DIK_BACK Key_Equals = 0x0d, // DIK_EQUALS diff --git a/wadsrc/static/zscript/engine/ui/menu/menu.zs b/wadsrc/static/zscript/engine/ui/menu/menu.zs index b40c10f0c..8e08870aa 100644 --- a/wadsrc/static/zscript/engine/ui/menu/menu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/menu.zs @@ -40,6 +40,7 @@ struct KeyBindings native version("2.4") native int, int GetKeysForCommand(String cmd); native void GetAllKeysForCommand(out array list, String cmd); + native String GetBinding(int key); native void SetBind(int key, String cmd); native void UnbindACommand (String str); diff --git a/wadsrc/static/zscript/games/exhumed/exhumedgame.zs b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs new file mode 100644 index 000000000..49597d2f8 --- /dev/null +++ b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs @@ -0,0 +1,104 @@ + + +struct Exhumed native +{ + native static void PlayLocalSound(int snd, int pitch, bool b, int chanf); + native static void StopLocalSound(); + native static bool LocalSoundPlaying(); + native static void playCDTrack(int track, bool looped); + native static void DrawPlasma(); + + + static void DrawAbs(String img, int x, int y, int shade = 0) + { + Screen.DrawTexture(TexMan.CheckForTexture(img, TexMan.Type_Any), false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_Color, Raze.shadeToLight(shade)); + } + + static void DRawRel(String img, int x, int y, int shade = 0) + { + let tex = TexMan.CheckForTexture(img, TexMan.Type_Any); + if (!tex.IsValid()) return; + let size = TexMan.GetScaledSize(tex); + let offs = TexMan.GetScaledOffset(tex); + // The integer truncation here is important. Old Build versions were bugged here. + x -= (int(size.x) >> 1) + int(offs.x); + y -= (int(size.y) >> 1) + int(offs.y); + Screen.DrawTexture(tex, false, x, y, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TopLeft, true, DTA_Color, Raze.shadeToLight(shade)); + } +} + + +struct ExhumedSnd native +{ + enum ESounds + { + kSound0 = 0, + kSound1, + kSound2, + kSound3, + kSound4, + kSound5, + kSound6, + kSound7, + kSound8, + kSound9, + kSoundItemSpecial, + kSound11, + kSoundTorchOn, + kSound13, + kSound14, + kSound15, + kSound16, + kSound17, + kSound18, + kSound19, + kSound20, + kSound21, + kSound22, + kSound23, + kSound24, + kSound25, + kSound26, + kSound27, + kSoundJonLaugh2, + kSound29, + kSound30, + kSound31, + kSound32, + kSound33, + kSound34, + kSound35, + kSound36, + kSound38 = 38, + kSound39, + kSound40, + kSound41, + kSound42, + kSound43, + kSound47 = 47, + kSound48 = 48, + kSoundQTail = 50, + kSound52 = 52, + kSoundTauntStart = 53, + kSoundJonFDie = 60, + kSound61, + kSound62, + kSound63, + kSound64, + kSound65, + kSound66, + kSoundMana1, + kSoundMana2, + kSoundAmmoPickup, + kSound70, + kSound71, + kSound72, + kSoundAlarm, + kSound74, + kSound75, + kSound76, + kSound77, + kSound78, + kSound79, + } +} \ No newline at end of file diff --git a/wadsrc/static/zscript/games/exhumed/ui/menu.zs b/wadsrc/static/zscript/games/exhumed/ui/menu.zs index b8e139181..46b908fb1 100644 --- a/wadsrc/static/zscript/games/exhumed/ui/menu.zs +++ b/wadsrc/static/zscript/games/exhumed/ui/menu.zs @@ -50,7 +50,10 @@ class ListMenuItemExhumedPlasma : ListMenuItem Super.Init(0, 0); } - native override void Draw(bool selected, ListMenuDescriptor desc); + override void Draw(bool selected, ListMenuDescriptor desc) + { + Exhumed.DrawPlasma(); + } } class ListMenuItemExhumedLogo : ListMenuItem diff --git a/wadsrc/static/zscript/games/exhumed/ui/screens.zs b/wadsrc/static/zscript/games/exhumed/ui/screens.zs new file mode 100644 index 000000000..133e16418 --- /dev/null +++ b/wadsrc/static/zscript/games/exhumed/ui/screens.zs @@ -0,0 +1,927 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 sirlemonhead, Nuke.YKT +Copyright (C) 2020-2021 Christoph Oelckers +This file is part of Raze. +PCExhumed 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. +*/ +//------------------------------------------------------------------------- + +struct LMFDecoder native +{ + static native bool Identify(String fn); + static native LMFDecoder Create(String fn); + native bool Frame(double clock); + native TextureID GetTexture(); + native void Close(); +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LmfPlayer : SkippableScreenJob +{ + LMFDecoder decoder; + double nextclock; + String fn; + + ScreenJob Init(String filename) + { + fn = filename; + return self; + } + + override void Start() + { + decoder = LMFDecoder.Create(fn); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void Draw(double smoothratio) + { + double clock = (ticks + smoothratio) * 1000000000. / GameTicRate; + if (clock >= nextclock) + { + if (decoder.Frame(clock)) + { + jobstate = finished; + return; + } + } + + double duration = clock * (120. / 8000000000.); + double z = 2048 * duration; + if (z > 65536) z = 65536; + + double angle = 1536. + 16. * duration; + if (angle >= 2048.) angle = 0.; + + Screen.DrawTexture(decoder.getTexture(), false, 160, 100, DTA_FullscreenScale, FSMode_Fit320x200, + DTA_CenterOffset, true, DTA_FlipY, true, DTA_ScaleX, z / 65536., DTA_ScaleY, z / 65536., DTA_Rotate, (-angle - 512) * (360. / 2048.)); + } + + override void OnDestroy() + { + decoder.Close(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LobotomyScreen : ImageScreen +{ + ScreenJob Init(String texname, int fade) + { + Super.InitNamed(texname, fade); + return self; + } + + override void OnSkip() + { + Exhumed.StopLocalSound(); + } + + override void Start() + { + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 7000, false, CHANF_UI); + } + + override void OnTick() + { + Super.OnTick(); + if (jobstate == finished) Exhumed.StopLocalSound(); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + + +class MainTitle : SkippableScreenJob +{ + String a, b; + int mystate; + int duration; + int var_4; + int esi; + int nCount; + int starttime; + static const short skullDurations[] = { 6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 600 }; + + ScreenJob Init() + { + Super.Init(fadein); + a = StringTable.Localize("$TXT_EX_COPYRIGHT1"); + b = StringTable.Localize("$TXT_EX_COPYRIGHT2"); + duration = skullDurations[0]; + esi = 130; + return self; + } + + override void Start() + { + Exhumed.PlayLocalSound(59, 0, true, CHANF_UI); + Exhumed.playCDtrack(19, true); + } + + override void OnTick() + { + int ticker = ticks * 120 / GameTicRate; + if (ticks > 1 && mystate == 0 && !Exhumed.LocalSoundPlaying()) + { + if (random(0, 15)) + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 0, false, CHANF_UI); + else + Exhumed.PlayLocalSound(61, 0, false, CHANF_UI); + mystate = 1; + starttime = ticker; + } + if (mystate == 1) + { + if (ticker > duration) + { + nCount++; + if (nCount > 12) + { + jobstate = finished; + return; + } + duration = starttime + skullDurations[nCount]; + var_4 = var_4 == 0; + } + } + } + + override void Draw(double sr) + { + Exhumed.DrawPlasma(); + Exhumed.DrawRel("SkullHead", 160, 100); + if (mystate == 0) + { + Exhumed.DrawRel("SkullJaw", 161, 130); + } + else + { + int nStringWidth = SmallFont.StringWidth(a); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 24, a, DTA_FullscreenScale, FSMode_Fit320x200); + nStringWidth = SmallFont.StringWidth(b); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, 200 - 16, b, DTA_FullscreenScale, FSMode_Fit320x200); + + + String nTile = "SkullJaw"; + + if (var_4) + { + if (esi >= 135) nTile = "SkullJaw2"; + else esi += 5; + } + else if (esi <= 130) esi = 130; + else esi -= 2; + + int y; + + if (nTile == "SkullJaw2") + { + y = 131; + } + else + { + y = esi; + if (y > 135) y = 135; + } + + Exhumed.DrawRel(nTile, 161, y); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class MapScreen : ScreenJob +{ + static const int MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 }; + static const int MapPlaqueX[] = { 100, 230, 180, 10, 210, 10, 10, 140, 30, 200, 145, 80, 15, 220, 190, 20, 220, 20, 200, 20 }; + static const int MapPlaqueY[] = { 170, 10, 125, 95, 160, 110, 50, 0, 20, 150, 170, 80, 0, 35, 40, 130, 160, 10, 10, 10 }; + static const int MapPlaqueTextX[] = { 18, 18, 18, 18, 18, 18, 18, 18, 18, 20, 18, 18, 18, 18, 18, 19, 18, 18, 18, 19 }; + static const int MapPlaqueTextY[] = { 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 6, 5, 6, 6, 6, 6, 6, 5, 4 }; + + static const int FireTilesX[] = { 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1 }; + static const int FireTilesY[] = { 3, 0, 3, 0, 0, 0, 1, 1, 2, 0, 2, 0 }; + + static const int 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 + }; + + const FIRE_SIZE = 10; + const FIRE_TYPE = 1; + const FIRE_XOFS = 2; + const FIRE_YOFS = 3; + const FIRE_ELEMENT_SIZE = 3; + + int x; + int delta; + int nIdleSeconds; + + int curYPos, destYPos; + int nLevel, nLevelNew, nLevelBest; + + native static void SetNextLevel(int num); + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + ScreenJob Init(int oldlevel, int newlevel, int maxlevel) + { + Super.Init(fadein|fadeout); + nLevel = oldlevel - 1; + nLevelNew = newlevel - 1; + nLevelBest = min(maxlevel, 19) - 1; + curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2)); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + if (curYPos < destYPos) delta = 2; + else if (curYPos > destYPos) delta = -2; + // Trim smoke in widescreen + /* + 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; + } + */ + return self; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown) + { + int key = ev.KeyScan; + let binding = Bindings.GetBinding(key); + if (key == InputEvent.KEY_UPARROW || key == InputEvent.KEY_PAD_DPAD_UP || key == InputEvent.Key_kpad_8 || binding ~== "+move_forward") + { + if (curYPos == destYPos && nLevelNew <= nLevelBest) + { + nLevelNew++; + SetNextLevel(nLevelNew + 1); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) delta = 2; + else delta = -2; + nIdleSeconds = 0; + } + return true; + } + + if (key == InputEvent.KEY_DOWNARROW || key == InputEvent.KEY_PAD_DPAD_DOWN || key == InputEvent.Key_kpad_2 || binding ~== "+move_backward") + { + if (curYPos == destYPos && nLevelNew > 0) + { + nLevelNew--; + SetNextLevel(nLevelNew + 1); + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) delta = 2; + else delta = -2; + nIdleSeconds = 0; + } + return true; + } + if (!Raze.specialKeyEvent(ev)) jobstate = skipped; + return true; + } + return false; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void OnTick() + { + if (curYPos != destYPos) + { + // scroll the map every couple of ms + curYPos += delta; + + if ((curYPos > destYPos && delta > 0) || (curYPos < destYPos && delta < 0)) + curYPos = destYPos; + + nIdleSeconds = 0; + } + else nIdleSeconds++; + if (nIdleSeconds > 300) jobstate = finished; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void Draw(double smoothratio) + { + int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); + + int tileY = curYPos; + + // Draw the background screens + for (int i = 0; i < 10; i++) + { + let tex = String.Format("MapBG%02d", i+1); + Exhumed.DrawAbs(tex, x, tileY); + tileY -= 200; + } + + // for each level - drawing the 'level completed' on-fire smoke markers + for (int i = 0; i < 20; i++) + { + int screenY = (i >> 1) * -200; + + if (nLevelBest >= i) // check if the player has finished this level + { + for (int j = 0; j < MapLevelFires[i * FIRE_SIZE]; j++) + { + int nFireFrame = ((currentclock >> 4) & 3); + int elem = i * FIRE_SIZE + FIRE_ELEMENT_SIZE * j; + int nFireType = MapLevelFires[elem + FIRE_TYPE]; + int x = MapLevelFires[elem + FIRE_XOFS]; + int y = MapLevelFires[elem + FIRE_YOFS]; + + String nTile = String.Format("MAPFIRE_%d%d", nFireType+1, nFireFrame+1); + int smokeX = x + FireTilesX[nFireType*3 + nFireFrame]; + int smokeY = y + FireTilesY[nFireType*3 + nFireFrame] + curYPos + screenY; + + // Use rotatesprite to trim smoke in widescreen + Exhumed.DrawAbs(nTile, smokeX, smokeY); + // Todo: mask out the sides of the screen if the background is not widescreen. + } + } + + int t = (((currentclock & 16) >> 4)); + + String nTile = String.Format("MapPlaque%d_%02d", t+1, i+1); + + int nameX = mapPlaqueX[i]; + int nameY = mapPlaqueY[i] + curYPos + screenY; + + // Draw level name plaque + Exhumed.DrawAbs(nTile, nameX, nameY); + + int shade = 96; + + if (nLevelNew == i) + { + shade = (Raze.bsin(16 * currentclock) + 31) >> 8; + } + else if (nLevelBest >= i) + { + shade = 31; + } + + int textY = nameY + MapPlaqueTextY[i]; + int textX = nameX + MapPlaqueTextX[i]; + nTile = String.Format("MapPlaqueText_%02d", i+1); + + // draw the text, alternating between red and black + Exhumed.DrawAbs(nTile, textX, textY, shade); + } + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class TextOverlay +{ + int nHeight; + double nCrawlY; + int palette; + BrokenLines screentext; + + void Init(String text, int pal) + { + screentext = SmallFont.BreakLines(StringTable.Localize(text), 320); + nCrawlY = 199; + nHeight = screentext.Count() * 10; + palette = pal; + } + + void DisplayText() + { + if (nHeight + nCrawlY > 0) + { + double y = nCrawlY; + for (int i = 0; i < screentext.Count() && y <= 199; i++) + { + if (y >= -10) + { + int x = 160 - screenText.StringWidth(i)/2; + Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, palette); + } + y += 10; + } + } + } + + bool AdvanceCinemaText(double clock) + { + if (nHeight + nCrawlY > 0 || musplaying.handle) + { + nCrawlY = 199 - clock / 15.; + return false; + } + return true; + } +} + +//--------------------------------------------------------------------------- +// +// cinema (this has been stripped off all game logic that was still in here) +// +//--------------------------------------------------------------------------- + +class Cinema : SkippableScreenJob +{ + TextOverlay textov; + TextureID cinematile; + int currentCinemaPalette; + int cdtrack; + int palette; + bool done; + + ScreenJob Init(String bgTexture, String text, int pal, int cdtrk) + { + Super.Init(fadein|fadeout); + cinematile = TexMan.CheckForTexture(bgTexture, TexMan.Type_Any); + textov = new("TextOverlay"); + palette = Translation.MakeID(Translation_BasePalette, pal); + textov.Init(text, palette); + cdtrack = cdtrk; + return self; + } + + override void Start() + { + Raze.StopAllSounds(); + if (cdtrack != -1) + { + Exhumed.playCDtrack(cdtrack, false); + } + } + + override void OnTick() + { + if (done) jobstate = finished; + } + + override void Draw(double smoothratio) + { + Screen.DrawTexture(cinematile, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, palette); + textov.DisplayText(); + done = textov.AdvanceCinemaText((ticks + smoothratio) * (120. / GameTicRate)); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class LastLevelCinema : ScreenJob +{ + int var_24; + int var_28; + + int ebp; + int phase; + int nextclock; + uint nStringTypeOn, nCharTypeOn; + int screencnt; + bool skiprequest; + + BrokenLines screentext; + Font printFont; + TextureID tex; + + ScreenJob Init() + { + Super.Init(fadein | fadeout); + var_24 = 16; + var_28 = 12; + nextclock = 4; + let p = StringTable.Localize("REQUIRED_CHARACTERS", false); + if (p == "REQUIRED_CHARACTERS") printFont = SmallFont2; + else printFont = ConFont; + return self; + } + + native static TextureID DoStatic(int a, int b); + native static TextureID UndoStatic(); + + void Phase1() + { + if (var_24 >= 116) + { + if (var_28 < 192) + var_28 += 20; + } + else + { + var_24 += 20; + } + + tex = DoStatic(var_28, var_24); + } + + bool InitPhase2() + { + let label = StringTable.Localize(String.Format("$TXT_EX_LASTLEVEL%d", screencnt + 1)); + screentext = printFont.BreakLines(label, 320); + if (screentext.Count() == 0) return false; + + nStringTypeOn = 0; + nCharTypeOn = 0; + + ebp = screentext.Count() * 4; // half height of the entire text + ebp = 81 - ebp; // offset from the screen's center. + tex = UndoStatic(); + return true; + } + + bool Phase3() + { + tex = 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; + + // for international content, use the generic 8x8 font. The original one is too small for expansion. + if (printFont == ConFont) + { + yy *= 2; + for (int i = 0; i < nStringTypeOn; i++, yy += 10) Screen.DrawText(ConFont, Font.CR_GREEN, 140, yy, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit640x400); + Screen.DrawText(ConFont, Font.CR_GREEN, 140, yy, screentext.StringAt(nStringTypeOn), DTA_FullscreenScale, FSMode_Fit640x400, DTA_TextLen, nCharTypeOn); + } + else + { + for (int i = 0; i < nStringTypeOn; i++, yy += 8) Screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, 70, yy, screentext.StringAt(i), DTA_FullscreenScale, FSMode_Fit320x200); + Screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, 70, yy, screentext.StringAt(nStringTypeOn), DTA_FullscreenScale, FSMode_Fit320x200, DTA_TextLen, nCharTypeOn); + } + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) skiprequest = true; + return true; + } + + override void Start() + { + Exhumed.PlayLocalSound(ExhumedSnd.kSound75, 0, false, CHANF_UI); + phase = 1; + } + + override void OnTick() + { + switch (phase) + { + case 1: + Phase1(); + if (skiprequest || ticks >= nextclock) + { + InitPhase2(); + phase = 2; + skiprequest = false; + } + break; + + case 2: + { + let text = screenText.StringAt(nStringTypeOn); + int chr; + [chr,nCharTypeOn] = text.GetNextCodePoint(nCharTypeOn); + + if (chr == 0) + { + nCharTypeOn = 0; + nStringTypeOn++; + if (nStringTypeOn >= screentext.Count()) + { + nextclock = (GameTicRate * (screentext.Count() + 2)) + ticks; + phase = 3; + } + + } + else + { + nCharTypeOn++; + if (chr != 32) Exhumed.PlayLocalSound(ExhumedSnd.kSound71, 0, false, CHANF_UI); + } + + if (skiprequest) + { + nextclock = (GameTicRate * (screentext.Count() + 2)) + ticks; + phase = 4; + } + break; + } + case 3: + if (ticks >= nextclock || skiprequest) + { + Exhumed.PlayLocalSound(ExhumedSnd.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 jobstate = finished; + } + + if (skiprequest) + { + jobstate = finished; + } + } + } + + override void Draw(double sm) + { + Screen.DrawTexture(tex, false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + if (phase == 2 || phase == 3) DisplayPhase2(); + } + +} + +//--------------------------------------------------------------------------- +// +// Credits roll +// +//--------------------------------------------------------------------------- + +class ExCredits : ScreenJob +{ + Array credits; + Array pagelines; + int page; + int pagetime; + bool skiprequest; + + ScreenJob Init() + { + Super.Init(); + String text; + int lump = Wads.CheckNumForFullName("credits.txt"); + if (lump > -1) text = Wads.ReadLump(lump); + text.Substitute("\r", ""); + text.Split(credits, "\n\n"); + return self; + } + + override bool OnEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown && !Raze.specialKeyEvent(ev)) skiprequest = true; + return true; + } + + override void Start() + { + if (credits.Size() == 0) + { + jobstate = finished; + return; + } + Exhumed.playCDtrack(19, false); + pagetime = 0; + page = -1; + } + + override void OnTick() + { + if (ticks >= pagetime || skiprequest) + { + page++; + if (page < credits.Size()) + credits[page].Split(pagelines, "\n"); + else + { + if (skiprequest || !musplaying.handle) + { + jobstate = finished; + return; + } + pagelines.Clear(); + } + pagetime = ticks + 60; // + } + } + + override void Draw(double smoothratio) + { + int y = 100 - ((10 * (pagelines.Size() - 1)) / 2); + + for (int 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; + + let colr = Color(light, light, light); + + int nStringWidth = SmallFont.StringWidth(pagelines[i]); + Screen.DrawText(SmallFont, Font.CR_UNTRANSLATED, 160 - nStringWidth / 2, y, pagelines[i], DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, colr); + y += 10; + } + } +} + +class ExhumedCutscenes +{ + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildIntro(ScreenJobRunner runner) + { + let logo = (gameinfo.gameType & GAMEFLAG_EXHUMED) ? "TileBMGLogo" : "TilePIELogo"; + runner.Append(ImageScreen.CreateNamed(logo, ScreenJob.fadein | ScreenJob.fadeout)); + runner.Append(new("LobotomyScreen").Init("LobotomyLogo", ScreenJob.fadein | ScreenJob.fadeout)); + if (LMFDecoder.Identify("book.mov")) runner.Append(new("LMFPlayer").Init("book.mov")); + else runner.Append(MoviePlayerJob.Create("book.mov", 0)); + runner.Append(new("MainTitle").Init()); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMap(ScreenJobRunner runner, MapRecord frommap, SummaryInfo info, MapRecord tomap) + { + // This is only defined for the regular levels. + int frommapnum = frommap == null? 1 : frommap.levelNumber; + if (fromMapnum < 1 || fromMapNum > 20 || tomap == null || tomap.levelNumber < 1 || tomap.levelNumber > 20) return; + + // hijack the super secret info in the summary info to convey the max. map because we won't need that field for its real purpose. + runner.Append(new("MapScreen").Init(fromMapNum, toMap.levelNumber, info.supersecrets)); + } + + //--------------------------------------------------------------------------- + // + // This removes all the insanity the original setup had with these. + // Simplicity rules! + // + //--------------------------------------------------------------------------- + + static void BuildCinemaBefore5(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema5", "$TXT_EX_CINEMA2", 3, 2)); + } + + static void BuildCinemaAfter10(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema10", "$TXT_EX_CINEMA4", 5, 3)); + } + + static void BuildCinemaBefore11(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema11", "$TXT_EX_CINEMA3", 1, 4)); + } + + static void BuildCinemaAfter15(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema15", "$TXT_EX_CINEMA6", 7, 6)); + } + + static void BuildCinemaBefore20(ScreenJobRunner runner) + { + runner.Append(new("LastLevelCinema").Init()); + } + + static void BuildCinemaAfter20(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinema20", "$TXT_EX_CINEMA8", 6, 8)); + runner.Append(new("ExCredits").Init()); + } + + static void BuildCinemaLose(ScreenJobRunner runner) + { + runner.Append(new("Cinema").Init("TileCinemaLose", "$TXT_EX_CINEMA7", 4, 7)); + } + + + //--------------------------------------------------------------------------- + // + // player died + // + //--------------------------------------------------------------------------- + + void BuildGameOverScene(ScreenJobRunner runner, MapRecord map) + { + Raze.StopMusic(); + Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 0, false, CHANF_UI); + runner.Append(ImageScreen.CreateNamed("Gameover", ScreenJob.fadein | ScreenJob.fadeout, 0x7fffffff, Translation.MakeID(Translation_BasePalette, 16))); + } + +} diff --git a/wadsrc/static/zscript/razebase.zs b/wadsrc/static/zscript/razebase.zs index 12f4e5637..70a05d590 100644 --- a/wadsrc/static/zscript/razebase.zs +++ b/wadsrc/static/zscript/razebase.zs @@ -111,6 +111,8 @@ struct Raze native static bool MusicEnabled(); native static String PlayerName(int i); native static double GetTimeFrac(); + native static int bsin(int angle, int shift = 0); + native static int bcos(int angle, int shift = 0); static bool specialKeyEvent(InputEvent ev) {