- scriptified Exhumed's 2D content (minus the programmatic textures.)

This commit is contained in:
Christoph Oelckers 2021-04-30 16:21:37 +02:00
parent 5b54e9c1ad
commit 4069a5096a
27 changed files with 1522 additions and 1257 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -43,8 +43,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
BEGIN_PS_NS
uint8_t nCinemaSeen;
uint8_t energytile[66 * 66] = {0};
uint8_t *cur;

View file

@ -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<DBlackScreen>(1);
}
auto fp = fileSystem.OpenFileReader(*fn);
if (!fp.isOpen()) return false;
char buffer[4];
fp.Read(buffer, 4);
if (memcmp(buffer, "LMF ", 4))
{
fp.Close();
// Allpw replacement with more modern formats.
return PlayVideo(fileName);
}
fp.Seek(0, FileReader::SeekSet);
return Create<DLmfPlayer>(fp);
return (0 == memcmp(buffer, "LMF ", 4));
}
#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

View file

@ -1,3 +1,4 @@
x(SkullJaw, 3437)
x(PowerslaveLogo, 3442)
x(MenuNewGameTile, 3460)
x(MenuLoadGameTile, 3461)
@ -6,6 +7,7 @@ x(MenuSoundFxTile, 3466)
x(MenuCursorTile, 3468)
x(MenuBlank, 3469)
x(SkullHead, 3582)
x(SkullJaw2, 3583)
x(ExhumedLogo, 3592)
x(Energy1, 3604)
x(Energy2, 3605)
@ -27,4 +29,100 @@ x(ClockSymbol15, 3620)
x(ClockSymbol16, 3621)
x(TileLoboLaptop, 3623)
x(TileBMGLogo, 3368)
x(LobotomyLogo, 3348)
x(TilePIELogo, 3349)
x(Gameover, 3591)
x(MapPlaque1_01, 3376)
x(MapPlaque1_02, 3378)
x(MapPlaque1_03, 3380)
x(MapPlaque1_04, 3382)
x(MapPlaque1_05, 3384)
x(MapPlaque1_06, 3371)
x(MapPlaque1_07, 3387)
x(MapPlaque1_08, 3389)
x(MapPlaque1_09, 3391)
x(MapPlaque1_10, 3409)
x(MapPlaque1_11, 3393)
x(MapPlaque1_12, 3395)
x(MapPlaque1_13, 3397)
x(MapPlaque1_14, 3399)
x(MapPlaque1_15, 3401)
x(MapPlaque1_16, 3403)
x(MapPlaque1_17, 3405)
x(MapPlaque1_18, 3407)
x(MapPlaque1_19, 3412)
x(MapPlaque1_20, 3415)
x(MapPlaque2_01, 3377)
x(MapPlaque2_02, 3379)
x(MapPlaque2_03, 3381)
x(MapPlaque2_04, 3383)
x(MapPlaque2_05, 3385)
x(MapPlaque2_06, 3386)
x(MapPlaque2_07, 3388)
x(MapPlaque2_08, 3390)
x(MapPlaque2_09, 3392)
x(MapPlaque2_10, 3410)
x(MapPlaque2_11, 3394)
x(MapPlaque2_12, 3396)
x(MapPlaque2_13, 3398)
x(MapPlaque2_14, 3400)
x(MapPlaque2_15, 3402)
x(MapPlaque2_16, 3404)
x(MapPlaque2_17, 3406)
x(MapPlaque2_18, 3408)
x(MapPlaque2_19, 3413)
x(MapPlaque2_20, 3416)
x(MapPlaqueText_01, 3411)
x(MapPlaqueText_02, 3414)
x(MapPlaqueText_03, 3417)
x(MapPlaqueText_04, 3420)
x(MapPlaqueText_05, 3423)
x(MapPlaqueText_06, 3426)
x(MapPlaqueText_07, 3429)
x(MapPlaqueText_08, 3432)
x(MapPlaqueText_09, 3435)
x(MapPlaqueText_10, 3418)
x(MapPlaqueText_11, 3438)
x(MapPlaqueText_12, 3441)
x(MapPlaqueText_13, 3444)
x(MapPlaqueText_14, 3447)
x(MapPlaqueText_15, 3450)
x(MapPlaqueText_16, 3453)
x(MapPlaqueText_17, 3456)
x(MapPlaqueText_18, 3459)
x(MapPlaqueText_19, 3419)
x(MapPlaqueText_20, 3421)
x(MapFire_11, 3484)
x(MapFire_12, 3485)
x(MapFire_13, 3486)
x(MapFire_14, 3487)
x(MapFire_21, 3488)
x(MapFire_22, 3489)
x(MapFire_23, 3490)
x(MapFire_24, 3491)
x(MapFire_31, 3492)
x(MapFire_32, 3493)
x(MapFire_33, 3494)
x(MapFire_34, 3495)
x(MapBG01, 3353)
x(MapBG02, 3354)
x(MapBG03, 3355)
x(MapBG04, 3356)
x(MapBG05, 3357)
x(MapBG06, 3358)
x(MapBG07, 3359)
x(MapBG08, 3360)
x(MapBG09, 3361)
x(MapBG10, 3362)
x(TileCinema5, 3449)
x(TileCinema10, 3451)
x(TileCinema11, 3454)
x(TileCinema15, 3446)
x(TileCinemaLose, 3445)
x(TileCinema20, 3448)

View file

@ -29,136 +29,10 @@ kTileStatusBar = 657,
kTile985 = 985,
kTile986 = 986,
kTile3000 = 3000,
kTile3117 = 3117,
kQueenChunk = 3117,
kTile3126 = 3126,
kTile3353 = 3353,
kTile3370 = 3370,
kTile3371 = 3371,
kTile3372 = 3372,
kTile3373 = 3373,
kTile3374 = 3374,
kTile3375 = 3375,
kTile3376 = 3376,
kTile3377 = 3377,
kTile3378 = 3378,
kTile3379 = 3379,
kTile3380 = 3380,
kTile3381 = 3381,
kTile3382 = 3382,
kTile3383 = 3383,
kTile3384 = 3384,
kTile3385 = 3385,
kTile3386 = 3386,
kTile3387 = 3387,
kTile3388 = 3388,
kTile3389 = 3389,
kTile3390 = 3390,
kTile3391 = 3391,
kTile3392 = 3392,
kTile3393 = 3393,
kTile3394 = 3394,
kTile3395 = 3395,
kTile3396 = 3396,
kTile3397 = 3397,
kTile3398 = 3398,
kTile3399 = 3399,
kTile3400 = 3400,
kTile3401 = 3401,
kTile3402 = 3402,
kTile3403 = 3403,
kTile3404 = 3404,
kTile3405 = 3405,
kTile3406 = 3406,
kTile3407 = 3407,
kTile3408 = 3408,
kTile3409 = 3409,
kTile3410 = 3410,
kTile3411 = 3411,
kTile3412 = 3412,
kTile3413 = 3413,
kTile3414 = 3414,
kTile3415 = 3415,
kTile3416 = 3416,
kTile3417 = 3417,
kTile3418 = 3418,
kTile3419 = 3419,
kTile3420 = 3420,
kTile3421 = 3421,
kTile3422 = 3422,
kTile3423 = 3423,
kTile3424 = 3424,
kTile3425 = 3425,
kTile3426 = 3426,
kTile3427 = 3427,
kTile3428 = 3428,
kTile3429 = 3429,
kTile3430 = 3430,
kTile3431 = 3431,
kTile3432 = 3432,
kTile3433 = 3433,
kTile3434 = 3434,
kTile3435 = 3435,
kTile3436 = 3436,
kSkullJaw = 3437,
kTile3438 = 3438,
kTile3439 = 3439,
kTile3440 = 3440,
kTile3441 = 3441,
kTile3443 = 3443,
kTile3444 = 3444,
kTile3445 = 3445,
kTile3446 = 3446,
kTile3447 = 3447,
kTile3448 = 3448,
kTile3449 = 3449,
kTile3450 = 3450,
kTile3451 = 3451,
kTile3452 = 3452,
kTile3453 = 3453,
kTile3454 = 3454,
kTile3455 = 3455,
kTile3456 = 3456,
kTile3457 = 3457,
kTile3458 = 3458,
kTile3459 = 3459,
kTile3462 = 3462,
kTile3463 = 3463,
kTile3464 = 3464,
kTile3467 = 3467,
kTile3470 = 3470,
kTile3471 = 3471,
kTile3472 = 3472,
kTile3473 = 3473,
kTile3474 = 3474,
kTile3475 = 3475,
kTile3476 = 3476,
kTile3477 = 3477,
kTile3478 = 3478,
kTile3479 = 3479,
kTile3480 = 3480,
kTile3481 = 3481,
kTile3482 = 3482,
kTile3483 = 3483,
kTile3484 = 3484,
kTile3485 = 3485,
kTile3486 = 3486,
kTile3487 = 3487,
kTile3488 = 3488,
kTile3489 = 3489,
kTile3490 = 3490,
kTile3491 = 3491,
kTile3492 = 3492,
kTile3493 = 3493,
kTile3494 = 3494,
kTile3495 = 3495,
kTile3496 = 3496,
kTile3497 = 3497,
kTile3498 = 3498,
kTile3499 = 3499,
kTile3512 = 3512,
kTile3571 = 3571,
kTile3583 = 3583,
kTile3591 = 3591,
kTile3593 = 3593,
kTile3603 = 3603,
kTile4092 = 4092,

View file

@ -1445,7 +1445,7 @@ void FuncQueen(int a, int nDamage, int nRun)
{
short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF;
sprite[nChunkSprite].picnum = kTile3117 + (i % 3);
sprite[nChunkSprite].picnum = kQueenChunk + (i % 3);
sprite[nChunkSprite].xrepeat = 100;
sprite[nChunkSprite].yrepeat = 100;
}

View file

@ -800,4 +800,35 @@ void PlayGameOverSound(void)
PlayLocalSound(StaticSound[kSoundJonLaugh2], 0, false, CHANF_UI);
}
DEFINE_ACTION_FUNCTION(_Exhumed, PlayLocalSound)
{
PARAM_PROLOGUE;
PARAM_INT(snd);
PARAM_INT(pitch);
PARAM_BOOL(unatt);
PARAM_INT(flags);
PlayLocalSound(StaticSound[snd], pitch, unatt, EChanFlags::FromInt(flags));
return 0;
}
DEFINE_ACTION_FUNCTION_NATIVE(_Exhumed, StopLocalSound, StopLocalSound)
{
StopLocalSound();
return 0;
}
DEFINE_ACTION_FUNCTION(_Exhumed, LocalSoundPlaying)
{
ACTION_RETURN_BOOL(soundEngine->IsSourcePlayingSomething(SOURCE_None, nullptr, CHAN_AUTO, -1));
}
DEFINE_ACTION_FUNCTION(_Exhumed, PlayCDTrack)
{
PARAM_PROLOGUE;
PARAM_INT(track);
PARAM_BOOL(loop);
playCDtrack(track, loop);
return 0;
}
END_PS_NS

View file

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

View file

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

View file

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

View file

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

View file

@ -40,6 +40,7 @@ struct KeyBindings native version("2.4")
native int, int GetKeysForCommand(String cmd);
native void GetAllKeysForCommand(out array<int> list, String cmd);
native String GetBinding(int key);
native void SetBind(int key, String cmd);
native void UnbindACommand (String str);

View file

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

View file

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

View file

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

View file

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