diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index ff84ecb97..d4649d006 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -829,10 +829,10 @@ void CreateStatusBar() StatusBar->SetSize(0, 320, 200); InitStatusBar(); // this is for comparing the scriptification with the C++ versions - //stbarclass = PClass::FindClass("NativeBloodStatusBar"); - //StatusBar2 = static_cast(stbarclass->CreateNew()); - //StatusBar2->SetSize(0, 320, 200); - //StatusBar2->Release(); + stbarclass = PClass::FindClass("NativeExhumedStatusBar"); + StatusBar2 = static_cast(stbarclass->CreateNew()); + StatusBar2->SetSize(0, 320, 200); + StatusBar2->Release(); } diff --git a/source/games/exhumed/src/cheats.cpp b/source/games/exhumed/src/cheats.cpp index 17055f960..d0de7c83b 100644 --- a/source/games/exhumed/src/cheats.cpp +++ b/source/games/exhumed/src/cheats.cpp @@ -203,17 +203,17 @@ static void cmd_Give(int player, uint8_t** stream, bool skip) if (buttons & kButtonCheatGuns) // LOBOCOP cheat { FillWeapons(player); - if (player == myconnectindex) StatusMessage(150, GStrings("TXT_EX_WEAPONS")); + if (player == myconnectindex) Printf(PRINT_NOTIFY, "%s\n", GStrings("TXT_EX_WEAPONS")); } if (buttons & kButtonCheatKeys) // LOBOPICK cheat { PlayerList[player].keys = 0xFFFF; - if (player == myconnectindex) StatusMessage(150, GStrings("TXT_EX_KEYS")); + if (player == myconnectindex) Printf(PRINT_NOTIFY, "%s\n", GStrings("TXT_EX_KEYS")); } if (buttons & kButtonCheatItems) // LOBOSWAG cheat { FillItems(player); - if (player == myconnectindex) StatusMessage(150, GStrings("TXT_EX_ITEMS")); + if (player == myconnectindex) Printf(PRINT_NOTIFY, "%s\n", GStrings("TXT_EX_ITEMS")); } } diff --git a/source/games/exhumed/src/namelist.h b/source/games/exhumed/src/namelist.h index 8d7076723..cad2b2f24 100644 --- a/source/games/exhumed/src/namelist.h +++ b/source/games/exhumed/src/namelist.h @@ -1,3 +1,13 @@ +x(Torch1, 338) +x(Torch2, 350) +x(TileRamsesGold, 590) +x(TileRamsesWorkTile, 591) +x(TileRamsesNormal, 592) +x(TileStatusBar, 657) +x(KeyIcon1, 675) +x(KeyIcon2, 679) +x(KeyIcon3, 683) +x(KeyIcon4, 687) x(CrosshairTile, 1579) x(SkullJaw, 3437) x(PowerslaveLogo, 3442) diff --git a/source/games/exhumed/src/names.h b/source/games/exhumed/src/names.h index ed087cf72..743ede805 100644 --- a/source/games/exhumed/src/names.h +++ b/source/games/exhumed/src/names.h @@ -20,12 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. enum { -kTorch1 = 338, -kTorch2 = 350, -kTileRamsesGold = 590, -kTileRamsesWorkTile = 591, -kTileRamsesNormal = 592, -kTileStatusBar = 657, kTile985 = 985, kTile986 = 986, kTile3000 = 3000, diff --git a/source/games/exhumed/src/player.cpp b/source/games/exhumed/src/player.cpp index 845e36461..3c25d47aa 100644 --- a/source/games/exhumed/src/player.cpp +++ b/source/games/exhumed/src/player.cpp @@ -2760,4 +2760,54 @@ void SerializePlayer(FSerializer& arc) } } + +DEFINE_FIELD_X(ExhumedPlayer, Player, nHealth); +DEFINE_FIELD_X(ExhumedPlayer, Player, nLives); +DEFINE_FIELD_X(ExhumedPlayer, Player, nDouble); +DEFINE_FIELD_X(ExhumedPlayer, Player, nInvisible); +DEFINE_FIELD_X(ExhumedPlayer, Player, nTorch); +DEFINE_FIELD_X(ExhumedPlayer, Player, field_2); +DEFINE_FIELD_X(ExhumedPlayer, Player, nAction); +DEFINE_FIELD_X(ExhumedPlayer, Player, nSprite); +DEFINE_FIELD_X(ExhumedPlayer, Player, bIsMummified); +DEFINE_FIELD_X(ExhumedPlayer, Player, invincibility); +DEFINE_FIELD_X(ExhumedPlayer, Player, nAir); +DEFINE_FIELD_X(ExhumedPlayer, Player, nSeq); +DEFINE_FIELD_X(ExhumedPlayer, Player, nMaskAmount); +DEFINE_FIELD_X(ExhumedPlayer, Player, keys); +DEFINE_FIELD_X(ExhumedPlayer, Player, nMagic); +DEFINE_FIELD_X(ExhumedPlayer, Player, nItem); +DEFINE_FIELD_X(ExhumedPlayer, Player, items); +DEFINE_FIELD_X(ExhumedPlayer, Player, nAmmo); // TODO - kMaxWeapons? +DEFINE_FIELD_X(ExhumedPlayer, Player, pad); + +DEFINE_FIELD_X(ExhumedPlayer, Player, nCurrentWeapon); +DEFINE_FIELD_X(ExhumedPlayer, Player, field_3FOUR); +DEFINE_FIELD_X(ExhumedPlayer, Player, bIsFiring); +DEFINE_FIELD_X(ExhumedPlayer, Player, field_38); +DEFINE_FIELD_X(ExhumedPlayer, Player, field_3A); +DEFINE_FIELD_X(ExhumedPlayer, Player, field_3C); +DEFINE_FIELD_X(ExhumedPlayer, Player, nRun); +DEFINE_FIELD_X(ExhumedPlayer, Player, bPlayerPan); +DEFINE_FIELD_X(ExhumedPlayer, Player, bLockPan); + +DEFINE_ACTION_FUNCTION(_Exhumed, GetViewPlayer) +{ + ACTION_RETURN_POINTER(&PlayerList[nLocalPlayer]); +} + +DEFINE_ACTION_FUNCTION(_ExhumedPlayer, IsUnderwater) +{ + PARAM_SELF_STRUCT_PROLOGUE(Player); + auto nLocalPlayer = self - PlayerList; + ACTION_RETURN_BOOL(SectFlag[nPlayerViewSect[nLocalPlayer]] & kSectUnderwater); +} + +DEFINE_ACTION_FUNCTION(_ExhumedPlayer, GetAngle) +{ + PARAM_SELF_STRUCT_PROLOGUE(Player); + ACTION_RETURN_INT(sprite[self->nSprite].ang); +} + + END_PS_NS diff --git a/source/games/exhumed/src/player.h b/source/games/exhumed/src/player.h index e1638787f..cf63e6d26 100644 --- a/source/games/exhumed/src/player.h +++ b/source/games/exhumed/src/player.h @@ -65,7 +65,7 @@ struct Player uint16_t keys; short nMagic; short nItem; - char items[8]; + uint8_t items[8]; short nAmmo[7]; // TODO - kMaxWeapons? short pad[2]; diff --git a/source/games/exhumed/src/status.cpp b/source/games/exhumed/src/status.cpp index 520d0eea4..4190fa8d4 100644 --- a/source/games/exhumed/src/status.cpp +++ b/source/games/exhumed/src/status.cpp @@ -47,9 +47,77 @@ void InitStatus() nStatusSeqOffset = SeqOffsets[kSeqStatus]; } -class DExhumedStatusBar : public DBaseStatusBar + +//--------------------------------------------------------------------------- +// +// This is to hide the dirt from the script code. +// These sequence arrays later need to be refactored +// if this is ever supposed to become a useful feature, +// so hide the dirty internals behind a handful of functions. +// +//--------------------------------------------------------------------------- + +struct ChunkFrame { - DECLARE_CLASS(DExhumedStatusBar, DBaseStatusBar) + FTextureID tex; + int x, y; + int flags; + + void GetChunkFrame(int nFrameBase) + { + x = ChunkXpos[nFrameBase]; + y = ChunkYpos[nFrameBase]; + auto ttex = tileGetTexture(nFrameBase); + if (ttex) tex = ttex->GetID(); + else tex.SetInvalid(); + flags = ChunkFlag[nFrameBase]; + } +}; + +DEFINE_ACTION_FUNCTION(_ChunkFrame, GetChunkFrame) +{ + PARAM_SELF_STRUCT_PROLOGUE(ChunkFrame); + PARAM_INT(index); + self->GetChunkFrame(index); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Exhumed, GetStatusSequence) +{ + PARAM_PROLOGUE; + PARAM_INT(nSequence); + PARAM_INT(frameindex); + + frameindex += SeqBase[nStatusSeqOffset + nSequence]; + if (numret > 0) ret[0].SetInt(FrameBase[frameindex]); + if (numret > 1) ret[1].SetInt(FrameSize[frameindex]); + return min(numret, 2); +} + +DEFINE_ACTION_FUNCTION(_Exhumed, MoveStatusSequence) +{ + PARAM_PROLOGUE; + PARAM_INT(s1); + PARAM_INT(s2); + seq_MoveSequence(-1, nStatusSeqOffset + s1, s2); + ACTION_RETURN_INT(SeqSize[nStatusSeqOffset + s1]); +} + +int SizeOfStatusSequence(int s1) +{ + return SeqSize[nStatusSeqOffset + s1]; +} + +DEFINE_ACTION_FUNCTION_NATIVE(_Exhumed, SizeOfStatusSequence, SizeOfStatusSequence) +{ + PARAM_PROLOGUE; + PARAM_INT(s1); + ACTION_RETURN_INT(SeqSize[nStatusSeqOffset + s1]); +} + +class DNativeExhumedStatusBar : public DBaseStatusBar +{ + DECLARE_CLASS(DNativeExhumedStatusBar, DBaseStatusBar) HAS_OBJECT_POINTERS TObjPtr textfont, numberFont; @@ -81,7 +149,7 @@ class DExhumedStatusBar : public DBaseStatusBar }; public: - DExhumedStatusBar() + DNativeExhumedStatusBar() { textfont = Create(SmallFont, 1, Off, 1, 1); numberFont = Create(BigFont, 0, Off, 1, 1); @@ -505,12 +573,6 @@ private: } DrawMulti(); - - if (nSnakeCam >= 0) - { - BeginHUD(320, 200, 1); - SBar_DrawString(this, textfont, "S E R P E N T C A M", 0, 0, DI_TEXT_ALIGN_CENTER | DI_SCREEN_CENTER_TOP, CR_UNTRANSLATED, 1, -1, 0, 1, 1); - } } //--------------------------------------------------------------------------- @@ -898,8 +960,8 @@ public: } }; -IMPLEMENT_CLASS(DExhumedStatusBar, false, true) -IMPLEMENT_POINTERS_START(DExhumedStatusBar) +IMPLEMENT_CLASS(DNativeExhumedStatusBar, false, true) +IMPLEMENT_POINTERS_START(DNativeExhumedStatusBar) IMPLEMENT_POINTER(textfont) IMPLEMENT_POINTER(numberFont) IMPLEMENT_POINTERS_END @@ -928,7 +990,7 @@ void StatusMessage(int messageTime, const char* fmt, ...) va_end(ap); } - +CVAR(Bool, sb_native, false, 0) void DrawStatusBar() { if (nFreeze == 2) return; // Hide when Ramses is talking. @@ -936,7 +998,21 @@ void DrawStatusBar() { UpdateFrame(); } - StatusBar->UpdateStatusBar(); + SummaryInfo info{}; + info.kills = nCreaturesKilled; + info.maxkills = nCreaturesTotal; + // got no secrets in the game + info.time = Scale(PlayClock, 1000, 120); + if (!sb_native) UpdateStatusBar(&info); + else StatusBar2->UpdateStatusBar(); + + if (nSnakeCam >= 0) + { + const char* text = "S E R P E N T C A M"; + int width = SmallFont->StringWidth(text); + DrawText(twod, SmallFont, CR_UNTRANSLATED, 160 - width / 2, 1, text, DTA_FullscreenScale, FSMode_Fit320x200, TAG_DONE); + } + } END_PS_NS diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 2bbe02e6b..861421d55 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -43,4 +43,5 @@ version "4.3" #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/sbar.zs" #include "zscript/games/exhumed/ui/screens.zs" diff --git a/wadsrc/static/zscript/games/exhumed/exhumedgame.zs b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs index 49597d2f8..33382b585 100644 --- a/wadsrc/static/zscript/games/exhumed/exhumedgame.zs +++ b/wadsrc/static/zscript/games/exhumed/exhumedgame.zs @@ -7,14 +7,17 @@ struct Exhumed native native static bool LocalSoundPlaying(); native static void playCDTrack(int track, bool looped); native static void DrawPlasma(); - + native static int, int GetStatusSequence(int seq, int index); + native static int MoveStatusSequence(int s1, int s2); + native static int SizeOfStatusSequence(int s1); + native static ExhumedPlayer GetViewPlayer(); 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) + static void DrawRel(String img, int x, int y, int shade = 0) { let tex = TexMan.CheckForTexture(img, TexMan.Type_Any); if (!tex.IsValid()) return; @@ -27,6 +30,56 @@ struct Exhumed native } } +struct ExhumedPlayer native +{ + native int16 nHealth; + native int16 nLives; + native int16 nDouble; + native int16 nInvisible; + native int16 nTorch; + native int16 field_2; + native int16 nAction; + native int16 nSprite; + native int16 bIsMummified; + native int16 invincibility; + native int16 nAir; + native int16 nSeq; + native int16 nMaskAmount; + native uint16 keys; + native int16 nMagic; + native int16 nItem; + native uint8 items[8]; + native int16 nAmmo[7]; // TODO - kMaxWeapons? + native int16 pad[2]; + + native int16 nCurrentWeapon; + native int16 field_3FOUR; + native int16 bIsFiring; + native int16 field_38; + native int16 field_3A; + native int16 field_3C; + native int16 nRun; + native bool bPlayerPan, bLockPan; + //fixedhoriz nDestVertPan; + + //PlayerHorizon horizon; + //PlayerAngle angle; + + native bool IsUnderwater(); + native int GetAngle(); +} + +enum EEWeap +{ + kWeaponSword = 0, + kWeaponPistol, + kWeaponM60, + kWeaponFlamer, + kWeaponGrenade, + kWeaponStaff, + kWeaponRing, + kWeaponMummified +} struct ExhumedSnd native { diff --git a/wadsrc/static/zscript/games/exhumed/ui/sbar.zs b/wadsrc/static/zscript/games/exhumed/ui/sbar.zs new file mode 100644 index 000000000..87a86e40c --- /dev/null +++ b/wadsrc/static/zscript/games/exhumed/ui/sbar.zs @@ -0,0 +1,781 @@ +//------------------------------------------------------------------------- +/* +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. + +This 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 ChunkFrame // this wraps the internal (mis-)representation of the chunk data. +{ + TextureID tex; + int x, y; + int flags; + + native void GetChunkFrame(int nFrameBase); +} + +class ExhumedStatusBar : RazeStatusBar +{ + HUDFont textfont, numberFont; + + int keyanims[4]; + int airframe, lungframe; + + int nSelectedItem; + int nHealthLevel; + int nMagicLevel; + int nMeterRange; + int nHurt; + int nHealthFrame; + int nMagicFrame; + int nItemAltSeq; + int nItemSeq; + int nItemFrames; + int nItemFrame; + int totalmoves; + + + int nCounter; + int nCounterDest; + int nDigit[3]; + int ammodelay; + int nLastWeapon; + + enum EConst + { + KeySeq = 36, + } + + override void Init() + { + textfont = HUDFont.Create(SmallFont, 1, Mono_Off, 1, 1); + numberFont = HUDFont.Create(BigFont, 0, Mono_Off, 1, 1); + + let nPicNum = GetStatusSequencePic(0, 0); + let siz = TexMan.GetScaledSize(nPicNum); + nMeterRange = siz.Y; + Reset(); + } + + override void Reset() + { + airframe = lungframe = nHurt = nHealthFrame = nMagicFrame = nItemAltSeq = nItemFrames = nItemFrame = nCounter = 0; + + nDigit[0] = nDigit[1] = nDigit[2] = 0; + nHealthLevel = -1; + nMagicLevel = -1; + nSelectedItem = -1; + nItemSeq = -1; + ammodelay = 3; + nLastWeapon = -1; + totalmoves = 0; + nCounterDest = 0; + } + + //--------------------------------------------------------------------------- + // + // draws a sequence animation to the status bar + // + //--------------------------------------------------------------------------- + + void DrawStatusSequence(int nSequence, int frameindex, double yoffset, double xoffset = 0) + { + int nFrameBase, nFrameSize; + [nFrameBase, nFrameSize] = Exhumed.GetStatusSequence(nSequence, frameindex); + + for(; nFrameSize > 0; nFrameSize--, nFrameBase++) + { + int flags = 0; + ChunkFrame chunk; + chunk.GetChunkFrame(nFrameBase); + + double x = chunk.x + xoffset; + double y = chunk.y + yoffset; + + if (hud_size <= Hud_StbarOverlay) + { + x += 161; + y += 100; + } + else + { + if (x < 0) + { + x += 160; + flags |= DI_SCREEN_LEFT_BOTTOM; + } + else if (x > 0) + { + x -= 159; // graphics do not match up precisely. + flags |= DI_SCREEN_RIGHT_BOTTOM; + } + y -= 100; + } + + if (chunk.flags & 3) + { + // This is hard to align with bad offsets, so skip that treatment for mirrored elements. + flags |= DI_ITEM_RELCENTER; + } + else + { + let tsiz = TexMan.GetScaledSize(chunk.tex); + x -= tsiz.x * .5; + y -= tsiz.y * .5; + flags |= DI_ITEM_OFFSETS; + } + + if (chunk.flags & 1) + flags |= DI_MIRROR; + if (chunk.flags & 2) + flags |= DI_MIRRORY; + + DrawTexture(chunk.tex, (x, y), flags); + } + } + + //--------------------------------------------------------------------------- + // + // draws a sequence animation to the status bar + // + //--------------------------------------------------------------------------- + + TextureID GetStatusSequencePic(int nSequence, int frameindex) + { + int nFrameBase = Exhumed.GetStatusSequence(nSequence, frameindex); + ChunkFrame chunk; + chunk.GetChunkFrame(nFrameBase); + return chunk.tex; + } + + //--------------------------------------------------------------------------- + // + // Frag display - very ugly and may have to be redone if multiplayer support gets added. + // + //--------------------------------------------------------------------------- + + void DrawMulti() + { + /* + char stringBuf[30]; + if (netgame) + { + BeginHUD(1, false, 320, 200); + + int shade; + + if ((PlayClock / 120) & 1) + shade = -100; + else + shade = 127; + + int nTile = 3593; + + int xx = 320 / (nTotalPlayers + 1); + int x = xx - 160; + + for (int i = 0; i < nTotalPlayers; i++) + { + DrawImage(String.Format("#%05d", nTile)), (x, 7), DI_ITEM_CENTER); + + if (i != nLocalPlayer) { + shade = -100; + } + + sprintf(stringBuf, "%d", nPlayerScore[i]); + DrawString(this, textfont, stringBuf, x, 0, DI_ITEM_TOP | DI_TEXT_ALIGN_CENTER, i != nLocalPlayer ? CR_UNTRANSLATED : CR_GOLD, 1, -1, 0, 1, 1); + x += xx; + nTile++; + } + } + */ + } + + //========================================================================== + // + // Fullscreen HUD variant #1 + // + //========================================================================== + + int ItemTimer(ExhumedPlayer pPlayer, int num) + { + switch (num) { + case 1: //Scarab item + return (pPlayer.invincibility * 100) / 900; + case 3: //Hand item + return (pPlayer.nDouble * 100) / 1350; + case 5: //Mask + return (pPlayer.nMaskAmount * 100) / 1350; + case 4: //Invisible + return (pPlayer.nInvisible * 100) / 900; + case 2: //Torch + return (pPlayer.nTorch * 100) / 900; + } + + return -1; + } + + void DrawHUD2(ExhumedPlayer pp) + { + BeginHUD(1, false, 320, 200); + + String format; + TextureID img; + double imgScale; + double baseScale = numberFont.mFont.GetHeight() * 0.75; + + + // + // Health + // + img = GetStatusSequencePic(125, 0); + let size = TexMan.GetScaledSize(img); + imgScale = baseScale / size.Y; + DrawTexture(img, (1.5, -1), DI_ITEM_LEFT_BOTTOM, scale:(imgScale, imgScale)); + + if (!hud_flashing || pp.nHealth > 150 || (PlayClock & 32)) + { + int s = -8; + if (hud_flashing && pp.nHealth > 800) + s += Raze.bsin(PlayClock << 5, -10); + int intens = clamp(255 - 4 * s, 0, 255); + format = String.Format("%d", pp.nHealth >> 3); + DrawString(numberFont, format, (13, -numberFont.mFont.GetHeight() + 3), DI_TEXT_ALIGN_LEFT, intens / 255.); + } + + // + // Air + // + if (pp.isUnderwater()) + { + img = GetStatusSequencePic(133, airframe); + let size = TexMan.GetScaledSize(img); + imgScale = baseScale / size.Y; + DrawTexture(img, (-4, -22), DI_ITEM_RIGHT_BOTTOM, scale:(imgScale, imgScale)); + } + + // + // Magic + // + if (nItemSeq >= 0) + { + img = GetStatusSequencePic(nItemSeq, nItemFrame); + //int tile = GetStatusSequenceTile(nItemSeq, nItemFrame); + //int tile2 = tile; + //if (tile2 > 744 && tile2 < 751) tile2 = 744; + + let size = TexMan.GetScaledSize(img); + imgScale = baseScale / size.Y; + DrawTexture(img, (70, -1), DI_ITEM_CENTER_BOTTOM, scale:(imgScale, imgScale)); + + format = String.Format("%d", pp.nMagic / 10); + + int nItem = pp.nItem; + int timer = ItemTimer(pp, nItem); + if (timer > 0) + { + format.AppendFormat("/%d", timer); + } + DrawString(numberFont, format, (79.5, -numberFont.mFont.GetHeight() + 3), DI_TEXT_ALIGN_LEFT); + } + // + // Weapon + // + int weapon = pp.nCurrentWeapon; + int ammo = nCounterDest; + if (ammo > 0) // wicon > 0 + { + format = String.Format("%d", ammo); + /* non-implemented weapon icon. + int wicon = 0;// ammo_sprites[weapon]; + img = tileGetTexture(wicon); + imgScale = baseScale / img.GetDisplayHeight(); + let imgX = 21.125; + let strlen = format.Len(); + + if (strlen > 1) + { + imgX += (imgX * 0.855) * (strlen - 1); + } + */ + + if ((!hud_flashing || PlayClock & 32 || ammo > 10))// (DamageData[weapon].max_ammo / 10))) + { + DrawString(numberFont, format, (-3, -numberFont.mFont.GetHeight() + 3), DI_TEXT_ALIGN_RIGHT); + } + + //DrawGraphic(img, (-imgX, -1), DI_ITEM_RIGHT_BOTTOM, scale:(imgScale, imgScale)); + } + + // + // keys + // + + int nKeys = pp.keys; + int x = -134; + + for (int i = 0; i < 4; i++) + { + if (nKeys & (0x1000 << i)) + { + DrawImage(String.Format("KeyIcon%d", i+1), (x, -2), DI_ITEM_LEFT_BOTTOM); + } + x += 20; + } + } + + + + //--------------------------------------------------------------------------- + // + // draw the full status bar + // + //--------------------------------------------------------------------------- + + void DrawStatus(ExhumedPlayer pp) + { + if (hud_size <= Hud_StbarOverlay) + { + // draw the main bar itself + BeginStatusBar(false, 320, 200, 40); + if (hud_size == Hud_StbarOverlay) Set43ClipRect(); + DrawImage("TileStatusBar", (160, 200), DI_ITEM_CENTER_BOTTOM); + screen.ClearClipRect(); + } + else if (hud_size == Hud_Mini) + { + BeginHUD(1, false, 320, 200); + DrawImage("hud_l", (0, 0), DI_ITEM_LEFT_BOTTOM | DI_SCREEN_LEFT_BOTTOM); + DrawImage("hud_r", (0, 0), DI_ITEM_RIGHT_BOTTOM | DI_SCREEN_RIGHT_BOTTOM); + } + else if (hud_size == Hud_full) + { + DrawHUD2(pp); + return; + } + + for (int i = 0; i < 4; i++) + { + if (pp.keys & (4096 << i)) + { + DrawStatusSequence(KeySeq + 2 * i, keyanims[i], 0.5, 0.5); + } + } + + //if (/*!bFullScreen &&*/ nNetTime) + { + DrawStatusSequence(127, 0, 0); + DrawStatusSequence(129, nMagicFrame, nMagicLevel); + DrawStatusSequence(131, 0, 0); // magic pool frame (bottom) + + DrawStatusSequence(128, 0, 0); + DrawStatusSequence(1, nHealthFrame, nHealthLevel); + DrawStatusSequence(125, 0, 0); // draw ankh on health pool + DrawStatusSequence(130, 0, 0); // draw health pool frame (top) + + // Item on the magic pool + if (nItemSeq >= 0) DrawStatusSequence(nItemSeq, nItemFrame, 1); + + // draw the blue air level meter when underwater + if (pp.isUnderwater()) DrawStatusSequence(133, airframe, 0, 0.5); + else DrawStatusSequence(132, lungframe, 0); + + // item count dots. + if (nSelectedItem >= 0) + { + int count = pp.items[nSelectedItem]; + DrawStatusSequence(156 + 2 * count, 0, 0); + } + + // life dots. + DrawStatusSequence(145 + (2 * pp.nLives), 0, 0); + + if (nHurt > 0) DrawStatusSequence(4, nHurt - 1, 0); + + // draw compass + if (hud_size <= Hud_StbarOverlay) DrawStatusSequence(35, ((pp.GetAngle() + 128) & Raze.kAngleMask) >> 8, 0, 0.5); + + //if (hud_size < Hud_full) + { + // draw ammo count + DrawStatusSequence(44, nDigit[2], 0, 0.5); + DrawStatusSequence(45, nDigit[1], 0, 0.5); + DrawStatusSequence(46, nDigit[0], 0, 0.5); + } + } + + DrawMulti(); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void DoLevelStats(int bottomy, Summaryinfo summary) + { + StatsPrintInfo stats; + + stats.fontscale = 1.; + stats.spacing = SmallFont.GetHeight(); + stats.screenbottomspace = bottomy; + stats.statfont = SmallFont; + stats.letterColor = TEXTCOLOR_RED; + stats.standardColor = TEXTCOLOR_UNTRANSLATED; + + if (automapMode == am_full) + { + PrintAutomapInfo(stats, true); + } + else if (automapMode == am_off && hud_stats) + { + stats.completeColor = TEXTCOLOR_DARKGREEN; + PrintLevelStats(stats, summary); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void SetItemSeq(ExhumedPlayer pp) + { + static const int nItemSeqOffset[] = { 91, 72, 76, 79, 68, 87, 83 }; + static const int nItemMagic[] = { 500, 1000, 100, 500, 400, 200, 700, 0 }; + + int nItem = pp.nItem; + if (nItem < 0) + { + nItemSeq = -1; + return; + } + + if (nItemMagic[nItem] <= pp.nMagic) nItemAltSeq = 0; + else nItemAltSeq = 2; + + nItemFrame = 0; + nItemSeq = nItemSeqOffset[nItem] + nItemAltSeq; + nItemFrames = Exhumed.SizeofStatusSequence(nItemSeq); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void SetMagicFrame(ExhumedPlayer pp) + { + int magicperline = 1000 / nMeterRange; + + int newMagicLevel = (1000 - pp.nMagic) / magicperline; + newMagicLevel = clamp(newMagicLevel, 0, nMeterRange - 1); + if (newMagicLevel != nMagicLevel) SetItemSeq(pp); + nMagicLevel = newMagicLevel; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void SetHealthFrame(ExhumedPlayer pp) + { + if (nHurt) + { + nHurt++; + if (nHurt > Exhumed.SizeofStatusSequence(4)) nHurt = 0; + } + + int healthperline = 800 / nMeterRange; + + int newHealthLevel = (800 - pp.nHealth) / healthperline; + newHealthLevel = clamp(newHealthLevel, 0, nMeterRange - 1); + if (newHealthLevel > nHealthLevel) nHurt = 1; + nHealthLevel = newHealthLevel; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void SetCounter(int nVal) + { + nCounterDest = clamp(nVal, 0, 999); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void SetCounterImmediate(int nVal) + { + SetCounter(nVal); + nCounter = nCounterDest; + SetCounterDigits(); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void SetCounterDigits() + { + nDigit[2] = 3 * (nCounter / 100 % 10); + nDigit[1] = 3 * (nCounter / 10 % 10); + nDigit[0] = 3 * (nCounter % 10); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void UpdateCounter(ExhumedPlayer pp) + { + int nWeapon = pp.nCurrentWeapon; + + if (nWeapon < 0) + { + SetCounterImmediate(0); + } + else + { + int thiscount; + + if (nWeapon >= kWeaponSword && nWeapon <= kWeaponRing) + thiscount = pp.nAmmo[nWeapon]; + else + thiscount = 0; + + if (nWeapon != nLastWeapon) SetCounterImmediate(thiscount); + else SetCounter(thiscount); + } + nLastWeapon = nWeapon; + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void MoveStatus(ExhumedPlayer pp) + { + if (nItemSeq >= 0) + { + nItemFrame++; + + if (nItemFrame >= nItemFrames) + { + if (nItemSeq == 67) { + SetItemSeq(pp); + } + else + { + nItemSeq -= nItemAltSeq; + + if (nItemAltSeq || (totalmoves & 0x1F)) + { + if (nItemSeq < 2) { + nItemAltSeq = 0; + } + } + else + { + nItemAltSeq = 1; + } + + nItemFrame = 0; + nItemSeq += nItemAltSeq; + nItemFrames = Exhumed.SizeofStatusSequence(nItemSeq); + } + } + } + + nHealthFrame++; + if (nHealthFrame >= Exhumed.SizeofStatusSequence(1)) nHealthFrame = 0; + + nMagicFrame++; + if (nMagicFrame >= Exhumed.SizeofStatusSequence(129)) nMagicFrame = 0; + + if (nCounter == nCounterDest) + { + nCounter = nCounterDest; + ammodelay = 3; + return; + } + else + { + ammodelay--; + if (ammodelay > 0) + return; + } + + int eax = nCounterDest - nCounter; + + if (eax <= 0) + { + if (eax >= -30) + { + for (int i = 0; i < 3; i++) + { + nDigit[i]--; + + if (nDigit[i] < 0) + { + nDigit[i] += 30; + } + + if (nDigit[i] < 27) { + break; + } + } + } + else + { + nCounter += (nCounterDest - nCounter) >> 1; + SetCounterDigits(); + return; + } + } + else + { + if (eax <= 30) + { + for (int i = 0; i < 3; i++) + { + nDigit[i]++; + + if (nDigit[i] <= 27) + break; + + if (nDigit[i] >= 30) + nDigit[i] -= 30; + } + } + else + { + nCounter += (nCounterDest - nCounter) >> 1; + SetCounterDigits(); + return; + } + } + + if (!(nDigit[0] % 3)) + nCounter = nDigit[0] / 3 + 100 * (nDigit[2] / 3) + 10 * (nDigit[1] / 3); + + eax = nCounterDest - nCounter; + if (eax < 0) + eax = -eax; + + ammodelay = max(1, 4 - (eax >> 1)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + void SetPlayerItem(ExhumedPlayer pp) + { + if (nSelectedItem != pp.nItem) + { + nSelectedItem = pp.nItem; + SetItemSeq(pp); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void Tick() + { + totalmoves++; + let pp = Exhumed.GetViewPlayer(); + SetMagicFrame(pp); + SetHealthFrame(pp); + SetPlayerItem(pp); + UpdateCounter(pp); + MoveStatus(pp); + for (int i = 0; i < 4; i++) + { + int seq = KeySeq + 2 * i; + if (pp.keys & (4096 << i)) + { + if (keyanims[i] < Exhumed.SizeofStatusSequence(seq) - 1) + { + Exhumed.MoveStatusSequence(seq, 0); // this plays the pickup sound. + keyanims[i]++; + } + } + else + { + keyanims[i] = 0; + } + } + + if (pp.isUnderwater()) + { + + int nAirFrames = Exhumed.SizeofStatusSequence(133); + int airperline = 100 / nAirFrames; + + airframe = pp.nAir / airperline; + + if (airframe >= nAirFrames) + { + airframe = nAirFrames - 1; + } + else if (airframe < 0) + { + airframe = 0; + } + lungframe = 0; + } + else + { + int size = Exhumed.SizeofStatusSequence(132); + if (++lungframe == size) lungframe = 0; + } + } + + + override void UpdateStatusBar(SummaryInfo info) + { + if (hud_size <= Hud_full) + { + DrawStatus(Exhumed.GetViewPlayer()); + } + DoLevelStats(hud_size == Hud_Nothing ? 0 : hud_size == Hud_full ? 22 : 40, info); + } +} diff --git a/wadsrc/static/zscript/razebase.zs b/wadsrc/static/zscript/razebase.zs index b38aa0ccc..df1f770ae 100644 --- a/wadsrc/static/zscript/razebase.zs +++ b/wadsrc/static/zscript/razebase.zs @@ -134,6 +134,8 @@ struct SummaryInfo native struct Raze { + const kAngleMask = 0x7FF; + static int calcSinTableValue(int ang) { return int(16384 * sin((360./2048) * ang)); @@ -254,7 +256,7 @@ class DukeStatusBar : DukeCommonStatusBar native class RedneckStatusBar : DukeCommonStatusBar native {} -class ExhumedStatusBar : BaseStatusBar native +class NativeExhumedStatusBar : BaseStatusBar native {} class SWStatusBar : BaseStatusBar native