From a112b29c43de1f54e48df0e158cb0a49e1661cbc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 27 Mar 2017 01:02:10 +0200 Subject: [PATCH] - implemented the inventory bar. This object is a bit special because it requires a lot of parameters, most of which are easily set to defaults. To make handling easier, most are passed through a container object which does some processing up front. - finished work on the Doom status bar. I also took the opportunity to fix the layout of the inventory bar which is a bit broken in SBARINFO. - tuned the selection rules for deciding what creates the status bar, so that the most recent definition that can be found is chosen. --- src/g_shared/shared_hud.cpp | 10 +- src/g_statusbar/sbarinfo.cpp | 4 +- src/g_statusbar/shared_sbar.cpp | 30 ++-- src/p_mobj.cpp | 6 + wadsrc/static/zscript/actor.txt | 1 + wadsrc/static/zscript/statusbar/doom_sbar.txt | 34 +++-- wadsrc/static/zscript/statusbar/statusbar.txt | 130 +++++++++++++++++- 7 files changed, 181 insertions(+), 34 deletions(-) diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index fa5b8a0306..ce8a4965e0 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -115,7 +115,7 @@ static FFont * IndexFont; // The font for the inventory indices static FTexture * healthpic; // Health icon static FTexture * berserkpic; // Berserk icon (Doom only) static FTexture * fragpic; // Frags icon -static FTexture * invgems[4]; // Inventory arrows +static FTexture * invgems[2]; // Inventory arrows static int hudwidth, hudheight; // current width/height for HUD display static int statspace; @@ -816,7 +816,7 @@ static void DrawInventory(player_t * CPlayer, int x,int y) { if(rover->PrevInv()) { - screen->DrawTexture(invgems[!!(level.time&4)], x-10, y, + screen->DrawTexture(invgems[0], x-10, y, DTA_KeepRatio, true, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.4, TAG_DONE); } @@ -852,7 +852,7 @@ static void DrawInventory(player_t * CPlayer, int x,int y) } if(rover) { - screen->DrawTexture(invgems[2 + !!(level.time&4)], x-10, y, + screen->DrawTexture(invgems[1], x-10, y, DTA_KeepRatio, true, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.4, TAG_DONE); } @@ -1279,9 +1279,7 @@ void HUD_InitHud() if (IndexFont == NULL) IndexFont = ConFont; // Emergency fallback invgems[0] = TexMan.FindTexture("INVGEML1"); - invgems[1] = TexMan.FindTexture("INVGEML2"); - invgems[2] = TexMan.FindTexture("INVGEMR1"); - invgems[3] = TexMan.FindTexture("INVGEMR2"); + invgems[1] = TexMan.FindTexture("INVGEMR1"); fragpic = TexMan.FindTexture("HU_FRAGS"); // Sadly, I don't have anything usable for this. :( diff --git a/src/g_statusbar/sbarinfo.cpp b/src/g_statusbar/sbarinfo.cpp index ae39fdca81..b7f8594429 100644 --- a/src/g_statusbar/sbarinfo.cpp +++ b/src/g_statusbar/sbarinfo.cpp @@ -984,13 +984,13 @@ public: "USEARTIA", "USEARTIB", "USEARTIC", "USEARTID", }; TArray patchnames; - patchnames.Resize(script->Images.Size()+10); + patchnames.Resize(script->Images.Size()+9); unsigned int i = 0; for(i = 0;i < script->Images.Size();i++) { patchnames[i] = script->Images[i]; } - for(i = 0;i < 10;i++) + for(i = 0;i < 9;i++) { patchnames[i+script->Images.Size()] = InventoryBarLumps[i]; } diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index fd1b914b17..acb9c42952 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -243,16 +243,24 @@ void ST_CreateStatusBar(bool bTitleLevel) StatusBar = new DBaseStatusBar(); StatusBar->SetSize(0); } - else if (gameinfo.statusbarclassfile >= gameinfo.statusbarfile) + else { - auto cls = PClass::FindClass(gameinfo.statusbarclass); - if (cls != nullptr) + // The old rule of 'what came last wins' goes here, as well. + // If the most recent SBARINFO definition comes before a status bar class definition it will be picked, + // if the class is defined later, this will be picked. If both come from the same file, the class definition will win. + int sbarinfolump = Wads.CheckNumForName("SBARINFO"); + int sbarinfofile = Wads.GetLumpFile(sbarinfolump); + if (gameinfo.statusbarclassfile >= gameinfo.statusbarfile && gameinfo.statusbarclassfile >= sbarinfofile) { - StatusBar = (DBaseStatusBar *)cls->CreateNew(); - IFVIRTUALPTR(StatusBar, DBaseStatusBar, Init) + auto cls = PClass::FindClass(gameinfo.statusbarclass); + if (cls != nullptr) { - VMValue params[] = { StatusBar }; - GlobalVMStack.Call(func, params, 1, nullptr, 0); + StatusBar = (DBaseStatusBar *)cls->CreateNew(); + IFVIRTUALPTR(StatusBar, DBaseStatusBar, Init) + { + VMValue params[] = { StatusBar }; + GlobalVMStack.Call(func, params, 1, nullptr, 0); + } } } } @@ -283,7 +291,6 @@ void ST_CreateStatusBar(bool bTitleLevel) auto cls = PClass::FindClass(defname); if (cls != nullptr) { - StatusBar = (DBaseStatusBar *)cls->CreateNew(); IFVIRTUALPTR(StatusBar, DBaseStatusBar, Init) { @@ -1605,8 +1612,8 @@ void DBaseStatusBar::DrawGraphic(FTextureID texture, double x, double y, int fla switch (flags & DI_ITEM_VMASK) { - case DI_ITEM_VCENTER: y -= texheight / 2; break; - case DI_ITEM_BOTTOM: y -= texheight; break; + case DI_ITEM_VCENTER: y -= boxheight / 2; break; + case DI_ITEM_BOTTOM: y -= boxheight; break; case DI_ITEM_VOFFSET: y -= tex->GetScaledTopOffsetDouble() * boxheight / texheight; break; } @@ -1732,6 +1739,8 @@ DEFINE_ACTION_FUNCTION(DHUDFont, Create) ACTION_RETURN_POINTER(new DHUDFont(fnt, spac, mono, sy, sy)); } +DEFINE_FIELD(DHUDFont, mFont); + //============================================================================ // // draw a string @@ -2039,3 +2048,4 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetMugshot) auto tex = self->mugshot.GetFace(self->CPlayer, def_face, accuracy, (FMugShot::StateFlags)stateflags); ACTION_RETURN_INT(tex ? tex->id.GetIndex() : -1); } + diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index c175858de3..08a2f922fe 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1045,6 +1045,12 @@ AInventory *AActor::FirstInv () return Inventory->NextInv (); } +DEFINE_ACTION_FUNCTION(AActor, FirstInv) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_OBJECT(self->FirstInv()); +} + //============================================================================ // // AActor :: UseInventory diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index af9ff38d6b..aae7d6aebc 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -502,6 +502,7 @@ class Actor : Thinker native native void SetShade(color col); native clearscope int GetRenderStyle() const; native clearscope bool CheckKeys(int locknum, bool remote, bool quiet = false); + native clearscope Inventory FirstInv() const; native clearscope string GetTag(string defstr = "") const; native void SetTag(string defstr = ""); diff --git a/wadsrc/static/zscript/statusbar/doom_sbar.txt b/wadsrc/static/zscript/statusbar/doom_sbar.txt index cd77b7752a..e9e96307cb 100644 --- a/wadsrc/static/zscript/statusbar/doom_sbar.txt +++ b/wadsrc/static/zscript/statusbar/doom_sbar.txt @@ -2,7 +2,8 @@ class DoomStatusBar : BaseStatusBar { HUDFont mHUDFont; HUDFont mIndexFont; - //DrawInventoryBarParms diparms; + HUDFont mAmountFont; + InventoryBarState diparms; override void Init() @@ -10,19 +11,13 @@ class DoomStatusBar : BaseStatusBar Super.Init(); SetSize(32, 320, 200); - // set up the inventory bar drawer. - /* - diparms.SetDefaults(HX_SHADOW); - diparms.SetTextures("SELECTBO", "ARTIBOX", "INVGEML1", "INVGEMR1"); - diparms.SetLeftArrow("INVGEML1"); - diparms.SetRightArrow("INVGEMR1"); - */ - // Create the font used for the fullscreen HUD Font fnt = "HUDFONT_DOOM"; mHUDFont = HUDFont.Create(fnt, fnt.GetCharWidth("0"), true, 1, 1); fnt = "INDEXFONT_DOOM"; mIndexFont = HUDFont.Create(fnt, fnt.GetCharWidth("0"), true); + mAmountFont = HUDFont.Create("INDEXFONT"); + diparms = InventoryBarState.Create(); } override void NewGame () @@ -120,14 +115,23 @@ class DoomStatusBar : BaseStatusBar DrawImage("STFBANY", (143, 0), DI_ITEM_OFFSETS|DI_TRANSLATABLE); } - if (CPlayer.mo.InvSel != null) + if (CPlayer.mo.InvSel != null && !level.NoInventoryBar) { - //drawinventorybar Doom, 7, INDEXFONT, 50, 170; + DrawInventoryIcon(CPlayer.mo.InvSel, (160, 30)); + if (CPlayer.mo.InvSel.Amount > 0) + { + DrawString(mAmountFont, FormatNumber(CPlayer.mo.InvSel.Amount), (175, 30-mIndexFont.mFont.GetHeight()), DI_TEXT_ALIGN_RIGHT, Font.CR_GOLD); + } } else { DrawTexture(GetMugShot(5), (143, 0), DI_ITEM_OFFSETS); } + if (CPlayer.inventorytics != 0 && !level.NoInventoryBar) + { + DrawInventoryBar(diparms, (48, 1), 7, DI_ITEM_LEFT_TOP); + } + } protected void DrawFullScreenStuff () @@ -159,14 +163,14 @@ class DoomStatusBar : BaseStatusBar DrawString(mHUDFont, FormatNumber(ammotype2.Amount, 3), (-30, invY), DI_TEXT_ALIGN_RIGHT); invY -= 20; } - if (CPlayer.inventorytics == 0 && CPlayer.mo.InvSel != null) + if (CPlayer.inventorytics == 0 && CPlayer.mo.InvSel != null && !level.NoInventoryBar) { DrawInventoryIcon(CPlayer.mo.InvSel, (-14, invY + 17)); DrawString(mHUDFont, FormatNumber(CPlayer.mo.InvSel.Amount, 3), (-30, invY), DI_TEXT_ALIGN_RIGHT); } if (deathmatch) { - DrawString(mHUDFont, FormatNumber(CPlayer.FragCount, 3), (-3, 1), DI_TEXT_ALIGN_RIGHT); + DrawString(mHUDFont, FormatNumber(CPlayer.FragCount, 3), (-3, 1), DI_TEXT_ALIGN_RIGHT, Font.CR_GOLD); } // Draw the keys. This does not use a special draw function like SBARINFO because the specifics will be different for each mod @@ -191,9 +195,9 @@ class DoomStatusBar : BaseStatusBar } } } - if (CPlayer.inventorytics != 0) + if (CPlayer.inventorytics != 0 && !level.NoInventoryBar) { - //DrawInventoryBar(diparms, (0, 0), 7, ALIGN_CENTER_BOTTOM, ALIGN_CENTER_BOTTOM); + DrawInventoryBar(diparms, (0, 0), 7, DI_SCREEN_CENTER_BOTTOM, HX_SHADOW); } } } diff --git a/wadsrc/static/zscript/statusbar/statusbar.txt b/wadsrc/static/zscript/statusbar/statusbar.txt index 45d15c0d42..48bc73dd4e 100644 --- a/wadsrc/static/zscript/statusbar/statusbar.txt +++ b/wadsrc/static/zscript/statusbar/statusbar.txt @@ -17,10 +17,57 @@ struct MugShot class HUDFont native ui { - // This is intentionally opaque to the script side. + native Font mFont; native static HUDFont Create(Font fnt, int spacing = 0, bool monospaced = false, int shadowx = 0, int shadowy = 0); } +class InventoryBarState ui +{ + TextureID box; + TextureID selector; + Vector2 boxsize; + Vector2 innersize; + int boxframesize; + + TextureID left; + TextureID right; + Vector2 arrowoffset; + + double itemalpha; + + HUDFont amountfont; + int cr; + int flags; + + // The default settings here are what SBARINFO is using. + static InventoryBarState Create(HUDFont indexfont = null, int cr = Font.CR_UNTRANSLATED, double itemalpha = 1., + String boxgfx = "ARTIBOX", String selgfx = "SELECTBO", int fsize = 2, + String leftgfx = "INVGEML1", String rightgfx = "INVGEMR1", Vector2 arrowoffs = (0, 0), int flags = 0) + { + let me = new ("InventoryBarState"); + me.itemalpha = itemalpha; + me.box = TexMan.CheckForTexture(boxgfx, TexMan.TYPE_MiscPatch); + me.selector = TexMan.CheckForTexture(selgfx, TexMan.TYPE_MiscPatch); + me.boxframesize = fsize; + if (me.box.IsValid() || me.selector.IsValid()) me.boxsize = TexMan.GetScaledSize(me.box.IsValid()? me.box : me.selector); + else me.boxsize = (32., 32.); + me.innersize = me.boxsize - (2.*fsize, 2.*fsize); + me.left = TexMan.CheckForTexture(leftgfx, TexMan.TYPE_MiscPatch); + me.right = TexMan.CheckForTexture(rightgfx, TexMan.TYPE_MiscPatch); + me.arrowoffset = arrowoffs; + me.arrowoffset.Y += me.boxsize.Y/2; // default is centered to the side of the box. + if (indexfont == null) + { + me.amountfont = HUDFont.Create("INDEXFONT"); + if (cr == Font.CR_UNTRANSLATED) cr = Font.CR_GOLD; + } + else me.amountfont = indexfont; + me.cr = cr; + me.flags = flags; + return me; + } +} + class BaseStatusBar native ui { @@ -130,6 +177,7 @@ class BaseStatusBar native ui DI_ALPHAMAPPED = 0x2000000, DI_NOSHADOW = 0x4000000, + DI_ALWAYSSHOWCOUNTERS = 0x8000000, }; enum IconType @@ -630,6 +678,86 @@ class BaseStatusBar native ui } } + //============================================================================ + // + // DrawInventoryBar + // + // This function needs too many parameters, so most have been offloaded to + // a struct to keep code readable and allow initialization somewhere outside + // the actual drawing code. + // + //============================================================================ + + // Except for the placement information this gets all info from the struct that gets passed in. + void DrawInventoryBar(InventoryBarState parms, Vector2 position, int numfields, int flags = 0, double bgalpha = 1.) + { + double width = parms.boxsize.X * numfields; + + // This must be done here, before altered coordinates get sent to the draw functions. + if (!(flags & DI_SCREEN_MANUAL_ALIGN)) + { + if (position.x < 0) flags |= DI_SCREEN_RIGHT; + else flags |= DI_SCREEN_LEFT; + if (position.y < 0) flags |= DI_SCREEN_BOTTOM; + else flags |= DI_SCREEN_TOP; + } + + // placement by offset is not supported because the inventory bar is a composite. + switch (flags & DI_ITEM_HMASK) + { + case DI_ITEM_HCENTER: position.x -= width / 2; break; + case DI_ITEM_RIGHT: position.x -= width; break; + } + + switch (flags & DI_ITEM_VMASK) + { + case DI_ITEM_VCENTER: position.y -= parms.boxsize.Y / 2; break; + case DI_ITEM_BOTTOM: position.y -= parms.boxsize.Y; break; + } + + // clear all alignment flags so that the following code only passed on the rest + flags &= ~(DI_ITEM_VMASK|DI_ITEM_HMASK); + + CPlayer.mo.InvFirst = ValidateInvFirst(numfields); + if (CPlayer.mo.InvFirst == null) return; // Player has no listed inventory items. + + Vector2 boxsize = parms.boxsize; + // First draw all the boxes + for(int i = 0; i < numfields; i++) + { + DrawTexture(parms.box, position + (boxsize.X * i, 0), flags | DI_ITEM_LEFT_TOP, bgalpha); + } + + // now the items and the rest + + Vector2 itempos = position + (boxsize.X / 2, boxsize.Y - parms.boxframesize); + Vector2 textpos = position + boxsize - (parms.boxframesize*2, parms.boxframesize + parms.amountfont.mFont.GetHeight()); + + int i = 0; + Inventory item; + for(item = CPlayer.mo.InvFirst; item != NULL && i < numfields; item = item.NextInv()) + { + if ((parms.flags & DI_DRAWCURSORFIRST) && item == CPlayer.mo.InvSel) DrawTexture(parms.selector, position + (boxsize.X * i, 0), flags | DI_ITEM_LEFT_TOP); + DrawInventoryIcon(item, itempos + (boxsize.X * i, 0), flags | DI_ITEM_CENTER_BOTTOM ); + if (!(parms.flags & DI_DRAWCURSORFIRST) && item == CPlayer.mo.InvSel) DrawTexture(parms.selector, position + (boxsize.X * i, 0), flags | DI_ITEM_LEFT_TOP); + + if (parms.amountfont != null && (item.Amount > 1 || (parms.flags & DI_ALWAYSSHOWCOUNTERS))) + { + DrawString(parms.amountfont, FormatNumber(item.Amount, 0, 5), textpos + (boxsize.X * i, 0), flags | DI_TEXT_ALIGN_RIGHT, parms.cr, parms.itemalpha); + } + i++; + } + // Is there something to the left? + if (CPlayer.mo.FirstInv() != CPlayer.mo.InvFirst) + { + DrawTexture(parms.left, position + (-parms.arrowoffset.X, parms.arrowoffset.Y), flags | DI_ITEM_RIGHT|DI_ITEM_VCENTER); + } + // Is there something to the right? + if (item != NULL) + { + DrawTexture(parms.right, position + parms.arrowoffset + (width, 0), flags | DI_ITEM_LEFT|DI_ITEM_VCENTER); + } + } } //============================================================================