ZScript: Status Bar Questions

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
User avatar
AFADoomer
Posts: 1337
Joined: Tue Jul 15, 2003 4:18 pm

Re: ZScript: Status Bar Questions

Post by AFADoomer »

Have you tried looking at the default status bar definitions in ZScript (e.g., the Doom one here)? I had a lot of success using the Doom and Hexen status bars as a base, and going line-by-line through the SBARINFO code, converting one item at a time based on the closest thing I could find in the existing ZScript code.
User avatar
Kinsie
Posts: 7402
Joined: Fri Oct 22, 2004 9:22 am
Graphics Processor: nVidia with Vulkan support
Location: MAP33

Re: ZScript: Status Bar Questions

Post by Kinsie »

Why even have a wiki if the answer to every question is just gonna be "look at the PK3"? Are we commencing a cocoon-like metamorphosis into a Linux support forum where every support question is answered with "maybe you should RTFM" regardless of relevance or whether the topic of question is even in anything that could be considered a manual?
User avatar
phantombeta
Posts: 2119
Joined: Thu May 02, 2013 1:27 am
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia with Vulkan support
Location: Brazil

Re: ZScript: Status Bar Questions

Post by phantombeta »

@Kinsie
Uh... Maybe it's because the documentation for ZScript in the wiki is VERY incomplete?
If you actually take the time to look at the [wiki=ZScript_status_bars]page for ZScript status bars[/wiki], you'll notice that right now it isn't very useful.

In my experience with ZScript and the more advanced parts, the wiki is absurdly incomplete.
When converting a GLDefs point light to one spawned in ZScript, (So I could dynamically change the size of the light) it took me quite a while to find out that I had to take the size value in GLDefs, multiply it by 2, then divide it by 3 to be able to use it as the intensity argument for a PointLight actor in ZScript.
User avatar
wildweasel
Posts: 21706
Joined: Tue Jul 15, 2003 7:33 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): A lot of them
Graphics Processor: Not Listed

Re: ZScript: Status Bar Questions

Post by wildweasel »

AFADoomer wrote:Have you tried looking at the default status bar definitions in ZScript (e.g., the Doom one here)? I had a lot of success using the Doom and Hexen status bars as a base, and going line-by-line through the SBARINFO code, converting one item at a time based on the closest thing I could find in the existing ZScript code.
The problem I'm having is that I don't have much of a context to know what's relevant and what isn't, short of Ctrl+F-ing words that seem like they'd be used, and trying to guess what the code I'm seeing is actually doing. This is not the most noob-friendly way to learn how the language works, especially when said noob has zero C experience under his belt and couldn't tell the difference between a struct, an enum, and an array.
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia

Re: ZScript: Status Bar Questions

Post by Matt »

The most important functions to know off the top of your head are:


void Draw(int state,double TicFrac)
draws the actual status bar

void DrawImage(String texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1));
draws an image

void DrawString(HUDFont font, String string, Vector2 pos, int flags = 0, int translation = Font.CR_UNTRANSLATED, double Alpha = 1., int wrapwidth = -1, int linespacing = 4)
draws a string

string FormatNumber(int number, int minsize = 0, int maxsize = 0, int format = 0, String prefix = "")
because there's no separate "DrawNumber"

void DrawInventoryBar(InventoryBarState parms, Vector2 position, int numfields, int flags = 0, double bgalpha = 1.)
the inventory selector


An array is a list of items. A vector is a list of 2 or 3 numbers.

Use a "for loop" to draw the same thing a bunch of times (e.g., you want your hud to show every individual round loaded into the mag at the moment)

"cplayer.mo" is the name of the pointer for the affected playerpawn.


Once you've got that down, everything really just boils down to "If this is happening, I'd like this thing drawn here in this manner".


It will take some time to figure out what exactly can be done and how to match that up with what you want, so for your project I'd recommend doing a bugfix release that you can expect people to rely on for a few months while you work on the new status bar. Do not delete the old SBARINFO; if you need to update anything, you can just go into Mapinfo, comment out the reference to the ZScript status bar, and upload, and everyone will just see the old SBARINFO bar instead of the ZScript one.

With that in mind, you can then just try to get some really basic stuff working (say, show a picture of a blue key card at the bottom of the screen if you have that card, then maybe one CLIPA0 in the corner for every 10 Clip you have) and build up from there.


Do you know how to do functions?
User avatar
AFADoomer
Posts: 1337
Joined: Tue Jul 15, 2003 4:18 pm

Re: ZScript: Status Bar Questions

Post by AFADoomer »

Here's a (very ugly) status bar that is stripped down to bare minimals and heavily commented... Not sure if that will help or not.

I didn't include keys on here, since there's no key bar equivalent in ZScript - you have to manually check for the keys and draw them yourself (see ugliness here), but this at least mostly documents the core functions that Vaecrius mentioned in the previous post, plus some.

Code: Select all

version "2.5"

class NewStatusBar : BaseStatusBar
{
    // Unlike SBARINFO, fonts can't be directly referenced by name.
    //  You'll need to declare them here, then initialize them in Init() below
    HUDFont mHUDFont;
    HUDFont mIndexFont;
    HUDFont mAmountFont;

    // This is necessary if you want to use the Inventory Bar
    InventoryBarState diparms;

    // You need a DynamicValueInterpolator for any value that you want to have interpolated
    //  This includes both bar values and any numbers that you want to "count to" 
    //  instead of having the value immediately change over
    DynamicValueInterpolator mHealthInterpolator;

    // Declare any other variables you might end up needing here
    
    // What happens when this bar is initialized?
    override void Init()
    {
        // Run the parent status bar's Init() function
        //   It's generally a good idea to call the super class's function whenever you 
        //   are overriding a function, in case some critical code is executed by the 
        //   parent class - unless you know specifically that you want to completely
        //   replace the parent class's function instead
        Super.Init();

        SetSize(32, 320, 200); // This sets the pixel height of the status bar and the base resolution

        // Initialize the fonts - there's actually a wiki page on this part: 
        //    https://zdoom.org/wiki/Classes:HUDFont
        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");

        // Initialize the inventory bar
        diparms = InventoryBarState.Create();

        // Initialize the interpolation variables
        //  Parameters:
        //    Starting Value
        //    Change Factor (I'm not 100% sure what this does)
        //    Minimum change step size (don't increment by less than this amount each Update() call)
        //    Maximum change step size (don't increment by more than this amount each Update() call)
        //
        //  These are the values used by:
        //    Hexen's health bar: 0, 0.25, 1, 8
        //    Hexen's chain health bar: 0, 0.25, 1, 6
        mHealthInterpolator = DynamicValueInterpolator.Create(0, 0.25, 1, 8);
    }

    // What happens when a new game begins
    override void NewGame ()
    {
        // Run the parent status bar's NewGame() function
        Super.NewGame();

        // Reset any interpolators that you declared
        //  void Reset(int value)
        //  Parameter: Desired value that you want to set the value to immediately
        mHealthInterpolator.Reset(0); // Reset sets the current value directly, so this sets the value to 0
    }

    // What needs to run every tick
    override void Tick()
    {
        // Run the parent status bar's Tick() function
        Super.Tick();

        // Update the value of the interpolators
        //  void Update(int destvalue)
        //  Parameter: Desired end-state value of the interpolator
        //   This is what sets the interpolator's value.  It uses the min/max step size variables that we created 
        //   interpolator with in Init(), so must be called repeatedly to set the correct value (so, if we start
        //   at 0, and Update to 100, and max step size is 8, each Update call will increment the value up by 8
        //   until the value reaches 100...  8, 16, 24, 32, ... 96, 100)
        mHealthInterpolator.Update(CPlayer.health); // Set the interpolator's value to this player's health
    }

    // Draw the status bar
    //   The default status bars use some logic here to call different functions based on whether the bar is normal
    //   or full-screen.  Since you don't actually want to use anything but a fullscreen HUD, you can get rid of the 
    //   checks here and jsut do the drawing here instead of calling another function
    override void Draw (int state, double TicFrac)
    {
        // Run the parent status bar's Draw() function
        Super.Draw (state, TicFrac); 

        BeginHUD(); // You'd use BeginStatusBar(); if you wanted a traditional bar instead of fullscreen

        // Draw the hud elements
        //   The x, y coordinates are all in Vector2 format, which for editing purposes means that they are enclosed
        //   in their own set of parenthesis...  Screen coordinates of x=5, y=10 is written as "(5, 10)"
        //
        //   Positioning flags are mostly self-explanatory; a list can be seen here: https://github.com/coelckers/gzdoom/blob/master/wadsrc/static/zscript/statusbar/statusbar.txt#L159
        //    Flags that start with DI_SCREEN set the relative position of the coordinates you pass - so (0, -15) and 
        //    DI_SCREEN_CENTER_BOTTOM will position your element at screen center, 15 pixels from the bottom
        //
        //    Flags that start with DI_ITEM set the positioning point of the element, essentially by overriding the 
        //    sprite/graphic's image offsets.  DI_ITEM_LEFT_TOP will position the item with it's top left corner at 
        //    the coordinates you provide, and DI_ITEM_OFFSETS will use the item's offset values

        // Only draw if there's no automap active
        if (!automapactive)
        {
            // Draw an image
            //  native void DrawImage(String texture, Vector2 pos, int flags = 0, double Alpha = 1., Vector2 box = (-1, -1), Vector2 scale = (1, 1));
            //  Parameters:
            //   Texture name as a string
            //   The desired coordinates of the image
            //   Positioning flags (DI_*)
            //   Alpha of the image (0.0 - 1.0)
            //   Vector2 box = (-1, -1) (Don't know what this does...)
            //   Scale as a Vector2 (so, half sized would be: (0.5, 0.5) )
            //
            //  NOTES: 
            //  There is also a DrawTexture function that uses the exact same parameters, except it takes an texture ID 
            //  instead of the string name of the texture - it's used when drawing the MugShot below
            //
            //  There is also a DrawInventoryIcon function with the same parameters except that it takes an inventory item
            //  instead of the texture name - used for ammo and inventory icon drawing below 
            DrawImage("STBAR", (0, 168), DI_ITEM_OFFSETS, 1.0, (-1, -1), (1, 1));

            // Draw a text string
            //  native void DrawString(HUDFont font, String string, Vector2 pos, int flags = 0, int translation = Font.CR_UNTRANSLATED, double Alpha = 1., int wrapwidth = -1, int linespacing = 4);
            //  Parameters:
            //   Font variable name, as declared in Init()
            //   The string you want to print.  To print a variable that is a number value (like DrawNumber in SBARINFO), use FormatNumber(yourvariable)
            //   Position where you want the string printed
            //   Positioning flags
            //   The font color translation that you want to use
            //   Alpha of the string
            //   Width that the string can be before it begins to wrap
            //   Spacing between the lines in the string when it wraps or has a newline character in it
            DrawString(mHUDFont, FormatNumber(CPlayer.health, 3), (90, 171), DI_TEXT_ALIGN_RIGHT|DI_NOSHADOW);
            DrawString(mHUDFont, "This is a normal string...", (221, 171), DI_TEXT_ALIGN_RIGHT|DI_NOSHADOW);

            // Draw a bar
            //  void DrawBar(String ongfx, String offgfx, double curval, double maxval, Vector2 position, int border, int vertical, int flags = 0)
            //  Parameters:
            //   The top layer graphic of the bar that is incrementally drawn based on the variable value
            //   The bottom/background graphic of the bar
            //   The current value of the bar - usually your interpolated variable
            //   The maximum value of the bar
            //   Bar position
            //   "Border" - I think only really used by Hexen?
            //   Bar direction flags - SHADER_HORZ, SHADER_VERT, or SHADER_REVERSE - https://github.com/coelckers/gzdoom/blob/master/wadsrc/static/zscript/statusbar/statusbar.txt#L261
            //   Positioning Flags
            //
            //  Note the use of GetValue() on the health interpolator here.
            DrawBar("SOULA0", "PINVA0", mHealthInterpolator.GetValue(), CPlayer.mo.GetMaxHealth(true), (36, 160), 0, SHADER_HORZ, DI_ITEM_OFFSETS);

            // Draw current ammo amounts
            Ammo ammo1, ammo2;
            int ammocount1, ammocount2;
            [ammo1, ammo2, ammocount1, ammocount2] = GetCurrentAmmo();
            if (ammo1) { DrawString(mAmountFont, FormatNumber(ammocount1, 3), (225, 171), DI_TEXT_ALIGN_RIGHT); }
            if (ammo2) { DrawString(mAmountFont, FormatNumber(ammocount2, 3), (225, 185), DI_TEXT_ALIGN_RIGHT); }

            //Ammo Icons
            DrawInventoryIcon(ammo1, (231, 170), DI_ITEM_OFFSETS);
            DrawInventoryIcon(ammo2, (231, 184), DI_ITEM_OFFSETS);

            // Draw armor amount
            let armor = CPlayer.mo.FindInventory("BasicArmor");
            if (armor != null && armor.Amount > 0)
            {
                DrawInventoryIcon(armor, (4, 184), DI_ITEM_OFFSETS);
                DrawString(mHUDFont, FormatNumber(GetArmorAmount(), 3), (52, 185), DI_TEXT_ALIGN_RIGHT);
                DrawString(mHUDFont, "%", (65, 185), DI_TEXT_ALIGN_RIGHT);
            }

            // Draw the mugshot
            //  Uses DrawTexture to draw the mugshot image, as retrieved by GetMugShot
            //  native TextureID GetMugshot(int accuracy, int stateflags=MugShot.STANDARD, String default_face = "STF");
            //   (I haven't looked into these parameters at all - I assume the "STF" one changes the mugshot image base, though)
            DrawTexture(GetMugShot(5), (143, 168), DI_ITEM_OFFSETS);

            // Draw the currently selected inventory item
            //  void DrawInventoryIcon(Inventory item, Vector2 pos, int flags = 0, double alpha = 1.0, Vector2 boxsize = (-1, -1), Vector2 scale = (1.,1.))
            //  Parameters:
            //   Inventory item name - CPlayer.mo.InvSel is the current inventroy selected by the player
            //   All other parameters are the same as DrawImage
            //
            //  A major difference from SBARINFO is that you have to manually draw the inventory amount
            if (CPlayer.mo.InvSel != null) // If there's an inventory item selected...
            {
                double x = 160;
                double y = 198;
                DrawInventoryIcon(CPlayer.mo.InvSel, (x, y));
                if (CPlayer.mo.InvSel.Amount > 1) // If you have more than one, draw the amount number as a string
                {
                    DrawString(mAmountFont, FormatNumber(CPlayer.mo.InvSel.Amount), (x + 15, y-mIndexFont.mFont.GetHeight()), DI_TEXT_ALIGN_RIGHT, Font.CR_GOLD);
                }
            }
    
            // Draw the inventory bar
            if (isInventoryBarVisible()) // If it should be visible...
            {
                // void DrawInventoryBar(InventoryBarState parms, Vector2 position, int numfields, int flags = 0, double bgalpha = 1.)
                // Parameters:
                //  The InventoryBarState variable that you declared and initialized above
                //  The desired coordinates of the bar
                //  The number of inventory items to show in the bar at one time
                //  Positioning flags
                //  Alpha of the background for the inventory bar (0.0 to 1.0)
                DrawInventoryBar(diparms, (0, 0), 7, DI_SCREEN_CENTER_BOTTOM, 0.5);
            }

            // Other useful functions:
            //  bool CheckInventory(class<Inventory> item, int amount = 1)
            //  bool CheckHealth(int Amount, bool percentage = false)
            //  bool isInvulnerable()
            //  bool isInventoryBarVisible()
            //  bool CheckWeaponSelected(class<Weapon> weap, bool checksister = true)
            //  bool CheckDisplayName(String displayname)
            //  String FormatNumber(int number, int minsize = 0, int maxsize = 0, int format = 0, String prefix = "")
        }
    }
} 
User avatar
Major Cooke
Posts: 8196
Joined: Sun Jan 28, 2007 3:55 pm
Preferred Pronouns: He/Him
Location: QZDoom Maintenance Team

Re: ZScript: Status Bar Questions

Post by Major Cooke »

How does CheckAspectRatio work?
User avatar
Kinsie
Posts: 7402
Joined: Fri Oct 22, 2004 9:22 am
Graphics Processor: nVidia with Vulkan support
Location: MAP33

Re: ZScript: Status Bar Questions

Post by Kinsie »

AFADoomer wrote:Here's a (very ugly) status bar that is stripped down to bare minimals and heavily commented... Not sure if that will help or not.

I didn't include keys on here, since there's no key bar equivalent in ZScript - you have to manually check for the keys and draw them yourself (see ugliness here), but this at least mostly documents the core functions that Vaecrius mentioned in the previous post, plus some.
This is a great kicking off point and managed to get me porting JPF's hud to ZScript. (I mean, granted, it also took a lot of swearing and one and a half jack-n-cokes, but...) Thanks a ton!
User avatar
Gutawer
Posts: 469
Joined: Sat Apr 16, 2016 6:01 am
Preferred Pronouns: She/Her

Re: ZScript: Status Bar Questions

Post by Gutawer »

Major Cooke wrote:How does CheckAspectRatio work?
You give it two aspect ratios (say 4/3 and 16/9) and it returns true if the actual aspect ratio is between those two. For the example 4/3 and 16/9, 4/3 would return true, 16/10 would return true, but 16/9 would return false as 16/9 is the max value (ideally, anyway - I think floating point precision issues could screw this up for values equal to the min and max, but any value in between those two values, lower than min, or higher than max should be reliable).
User avatar
Major Cooke
Posts: 8196
Joined: Sun Jan 28, 2007 3:55 pm
Preferred Pronouns: He/Him
Location: QZDoom Maintenance Team

Re: ZScript: Status Bar Questions

Post by Major Cooke »

Okay, so to put in easier terms...

Code: Select all

CheckAspectRatio(3, 4);
If I want to check for 4:3, yes?
User avatar
Gutawer
Posts: 469
Joined: Sat Apr 16, 2016 6:01 am
Preferred Pronouns: She/Her

Re: ZScript: Status Bar Questions

Post by Gutawer »

No. You'd use the Screen.GetAspectRatio() function for that - CheckAspectRatio() checks for a range of ratios, for example CheckAspectRatio(4/3.0, 16/9.0) would return true if the screen's aspect ratio is greater than or equal to 4/3 and lower than 16/9, for example 16/10 would return true, but 5/4 would return false.
User avatar
Matt
Posts: 9696
Joined: Sun Jan 04, 2004 5:37 pm
Preferred Pronouns: They/Them
Operating System Version (Optional): Debian Bullseye
Location: Gotham City SAR, Wyld-Lands of the Lotus People, Dominionist PetroConfederacy of Saudi Canadia

Re: ZScript: Status Bar Questions

Post by Matt »

Is there a way to draw the consoleplayer's current time (of day) on the HUD?
Or, possibly, the administrator's time of day for everyone?
User avatar
Gifty
Posts: 615
Joined: Sat Jun 15, 2013 8:21 pm

Re: ZScript: Status Bar Questions

Post by Gifty »

Hi, complete Zscript neophyte here -- I'm trying to code a basic script that will change the player's armor icon depending on their armor level, with the ultimate goal of being able to display different icons at different damage levels, similar to the player mugshot. I'm totally new to Zscript, though, and even with the wiki I'm having trouble figuring out how to do this. Could someone break down for me what I'm doing wrong?

This code should simply display an armor icon when the player is at max.

Code: Select all

{
	int GerArmorAmount()
	{
		let armor = CPlayer.mo.FindInventory("BasicArmor");
		return armor? armor.Amount : 0;
		
		if (armor.Amount) 100;
		void drawimage(ITYPE_ARMOR, 23, 191);
	}
}
User avatar
Gutawer
Posts: 469
Joined: Sat Apr 16, 2016 6:01 am
Preferred Pronouns: She/Her

Re: ZScript: Status Bar Questions

Post by Gutawer »

It's returning before the image is drawn, and if (armor.Amount) 100; will do nothing - you need something like this:

Code: Select all

if (armor.Amount >= 100) {
      drawimage(ITYPE_ARMOR, 23, 191);
}
^ Assuming you want the image to be drawn if the player has 100 or more armor, change the operator to suit your needs.
User avatar
cotton_disciple
Posts: 55
Joined: Fri Jul 03, 2015 4:22 am
Location: Russian Federation

Re: ZScript: Status Bar Questions

Post by cotton_disciple »

Does anyone know how to get powermorph duration?

Return to “Scripting”