mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-27 22:42:22 +00:00
878 lines
22 KiB
Text
878 lines
22 KiB
Text
//-------------------------------------------------------------------------
|
|
/*
|
|
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 (!System.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 myCurYPos = curYPos == destYPos? curYPos : curYPos - int((1. - smoothratio) * delta);
|
|
int tileY = myCurYPos;
|
|
|
|
// 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] + myCurYPos + 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] + myCurYPos + 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);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// 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, Font.CR_NATIVEPAL, palette);
|
|
cdtrack = cdtrk;
|
|
return self;
|
|
}
|
|
|
|
override void Start()
|
|
{
|
|
System.StopAllSounds();
|
|
if (cdtrack != -1)
|
|
{
|
|
Exhumed.playCDtrack(cdtrack+2, 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.ScrollText((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 (uint 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 (uint 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 && !System.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 >= uint(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<String> credits;
|
|
Array<String> 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 && !System.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++;
|
|
pagelines.Clear();
|
|
if (page < credits.Size())
|
|
credits[page].Split(pagelines, "\n");
|
|
else
|
|
{
|
|
if (skiprequest || !musplaying.handle)
|
|
{
|
|
jobstate = finished;
|
|
return;
|
|
}
|
|
}
|
|
pagetime = ticks + 90; //
|
|
}
|
|
}
|
|
|
|
override void Draw(double smoothratio)
|
|
{
|
|
int y = 100 - ((10 * (pagelines.Size() - 1)) / 2);
|
|
|
|
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(255, light, light, light);
|
|
|
|
for (int i = 0; i < pagelines.Size(); i++)
|
|
{
|
|
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 ui
|
|
{
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
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
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
static void BuildGameOverScene(ScreenJobRunner runner)
|
|
{
|
|
System.StopMusic();
|
|
Exhumed.PlayLocalSound(ExhumedSnd.kSoundJonLaugh2, 0, false, CHANF_UI);
|
|
runner.Append(ImageScreen.CreateNamed("Gameover", ScreenJob.fadein | ScreenJob.fadeout, 0x7fffffff, Translation.MakeID(Translation_BasePalette, 16)));
|
|
}
|
|
|
|
}
|