class BaseStatusBar native ui { enum EPop { POP_NoChange = -1, POP_None, POP_Log, POP_Keys, POP_Status } // Status face stuff enum EMug { ST_NUMPAINFACES = 5, ST_NUMSTRAIGHTFACES = 3, ST_NUMTURNFACES = 2, ST_NUMSPECIALFACES = 3, ST_NUMEXTRAFACES = 2, ST_FACESTRIDE = ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES, ST_NUMFACES = ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES, ST_TURNOFFSET = ST_NUMSTRAIGHTFACES, ST_OUCHOFFSET = ST_TURNOFFSET + ST_NUMTURNFACES, ST_EVILGRINOFFSET = ST_OUCHOFFSET + 1, ST_RAMPAGEOFFSET = ST_EVILGRINOFFSET + 1, ST_GODFACE = ST_NUMPAINFACES*ST_FACESTRIDE, ST_DEADFACE = ST_GODFACE + 1 } enum EHudState { HUD_StatusBar, HUD_Fullscreen, HUD_None, HUD_AltHud // Used for passing through popups to the alt hud } enum DI_Flags { DI_SKIPICON = 0x1, DI_SKIPALTICON = 0x2, DI_SKIPSPAWN = 0x4, DI_SKIPREADY = 0x8, DI_ALTICONFIRST = 0x10, DI_TRANSLATABLE = 0x20, DI_FORCESCALE = 0x40, DI_DIM = 0x80, }; enum IconType { ITYPE_PLAYERICON = 1000, ITYPE_AMMO1, ITYPE_AMMO2, ITYPE_ARMOR, ITYPE_WEAPON, ITYPE_SIGIL, ITYPE_WEAPONSLOT, ITYPE_SELECTEDINVENTORY, } enum HexArmorType { HEXENARMOR_ARMOR, HEXENARMOR_SHIELD, HEXENARMOR_HELM, HEXENARMOR_AMULET, }; enum EAlign { ALIGN_TOP = 0, ALIGN_VCENTER = 1, ALIGN_BOTTOM = 2, ALIGN_VOFFSET = 3, ALIGN_VMASK = 3, ALIGN_LEFT = 0, ALIGN_HCENTER = 4, ALIGN_RIGHT = 8, ALIGN_HOFFSET = 12, ALIGN_HMASK = 12, ALIGN_CENTER = ALIGN_VCENTER|ALIGN_HCENTER, ALIGN_CENTER_BOTTOM = ALIGN_BOTTOM|ALIGN_HCENTER, ALIGN_OFFSETS = ALIGN_HOFFSET|ALIGN_VOFFSET }; enum ETextAlign { TEXT_LEFT = 0, TEXT_CENTER = 1, TEXT_RIGHT = 2 }; enum SBGameModes { GAMEMODE_SINGLEPLAYER = 0x1, GAMEMODE_COOPERATIVE = 0x2, GAMEMODE_DEATHMATCH = 0x4, GAMEMODE_TEAMGAME = 0x8 }; enum AmmoModes { AMMO_PRIMARY, AMMO_SECONDARY, AMMO_ANY, AMMO_BOTH }; enum EHudDraw { HUD_Normal, HUD_HorizCenter } enum ENumFlags { FNF_FILLZEROS, FNF_WHENNOTZERO, } const XHAIRSHRINKSIZE =(1./18); const XHAIRPICKUPSIZE = (2+XHAIRSHRINKSIZE); native int ST_X, ST_Y; native int RelTop; native int HorizontalResolution, VerticalResolution; native bool Scaled; native bool Centering; native bool FixedOrigin; native bool CompleteBorder; native double CrosshairSize; native double Displacement; native PlayerInfo CPlayer; native bool ShowLog; // These are block properties for the drawers. A child class can set them to have a block of items use the same settings. native double Alpha; native Vector2 drawOffset; // can be set by subclasses to offset drawing operations native double drawClip[4]; // defines a clipping rectangle (not used yet) native bool fullscreenOffsets; // current screen is displayed with fullscreen behavior. native Vector2 cleanScale; // factor for scaled fullscreen display. native void SetSize(int height, int vwidth, int vheight); virtual void Init() {} native virtual void SetScaled(bool scale, bool force = false); native virtual void Tick (); native virtual void Draw (int state, double TicFrac); native virtual void ScreenSizeChanged (); virtual void FlashItem (class itemtype) {} virtual void AttachToPlayer (PlayerInfo player) { CPlayer = player; } virtual void FlashCrosshair () { CrosshairSize = XHAIRPICKUPSIZE; } virtual void NewGame () {} virtual void ShowPop (int popnum) { ShowLog = (popnum == POP_Log && !ShowLog); } virtual clearscope void ReceivedWeapon (Weapon weapn) {} virtual bool MustDrawLog(int state) { return true; } virtual void SetMugShotState (String state_name, bool wait_till_done=false, bool reset=false) {} native void RefreshBackground () const; // These functions are kept native solely for performance reasons. They get called repeatedly and can drag down performance easily if they get too slow. native Inventory ValidateInvFirst (int numVisible) const; native static TextureID, bool GetInventoryIcon(Inventory item, int flags); native void DrawGraphic(TextureID texture, bool animate, Vector2 pos, double Alpha, bool translatable, bool dim, int imgAlign, int screenalign, bool alphamap, Vector2 box); native void DrawString(Font font, String string, Vector2 pos , double Alpha, int translation, int align, int screenalign, int spacing=0, bool monospaced = false, int shadowX=0, int shadowY=0, int wrapwidth = -1, int linespacing = 4); native static String FormatNumber(int number, int minsize, int maxsize, int format, String prefix = ""); //============================================================================ // // DBaseStatusBar :: GetCurrentAmmo // // Returns the types and amounts of ammo used by the current weapon. If the // weapon only uses one type of ammo, it is always returned as ammo1. // //============================================================================ Inventory, Inventory, int, int GetCurrentAmmo () const { Ammo ammo1, ammo2; if (CPlayer.ReadyWeapon != NULL) { ammo1 = CPlayer.ReadyWeapon.Ammo1; ammo2 = CPlayer.ReadyWeapon.Ammo2; if (ammo1 == NULL) { ammo1 = ammo2; ammo2 = NULL; } } else { ammo1 = ammo2 = NULL; } let ammocount1 = ammo1 != NULL ? ammo1.Amount : 0; let ammocount2 = ammo2 != NULL ? ammo2.Amount : 0; return ammo1, ammo2, ammocount1, ammocount2; } //============================================================================ // // Get an icon // //============================================================================ TextureID, Vector2 GetIcon(Inventory item, int flags, bool showdepleted = false) { TextureID icon; Vector2 scale = (1,1); icon.SetInvalid(); if (item != null) { bool applyscale; [icon, applyscale] = GetInventoryIcon(item, flags); if (item.Amount == 0 && !showdepleted) return icon; if (applyscale) scale = item.Scale; } return icon; } //============================================================================ // // Convenience functions to retrieve item tags // //============================================================================ String GetAmmoTag(bool secondary = false) { let w = CPlayer.ReadyWeapon; if (w == null) return ""; let ammo = secondary? w.ammo2 : w.ammo1; return ammo.GetTag(); } String GetWeaponTag() { let w = CPlayer.ReadyWeapon; if (w == null) return ""; return w.GetTag(); } String GetSelectedInventoryTag() { let w = CPlayer.mo.InvSel; if (w == null) return ""; return w.GetTag(); } // These cannot be done in ZScript. native String GetGlobalACSString(int index); native String GetGlobalACSArrayString(int arrayno, int index); native int GetGlobalACSValue(int index); native int GetGlobalACSArrayValue(int arrayno, int index); //============================================================================ // // Convenience functions to retrieve some numbers // //============================================================================ int GetArmorAmount() { let armor = CPlayer.mo.FindInventory("BasicArmor"); return armor? armor.Amount : 0; } int GetMaxAmount(class item) { let it = CPlayer.mo.FindInventory(item); return it? it.MaxAmount : GetDefaultByType(item).MaxAmount; } int GetArmorSavePercent() { double add = 0; let harmor = HexenArmor(CPlayer.mo.FindInventory("HexenArmor")); if(harmor != NULL) { add = harmor.Slots[0] + harmor.Slots[1] + harmor.Slots[2] + harmor.Slots[3] + harmor.Slots[4]; } //Hexen counts basic armor also so we should too. let armor = BasicArmor(CPlayer.mo.FindInventory("BasicArmor")); if(armor != NULL) { add += armor.SavePercent * 100; } return int(add); } // Note that this retrieves the value in tics, not seconds like the equivalent SBARINFO function. // The idea is to let the caller decide what to do with it instead of destroying accuracy here. int GetAirTime() { if(CPlayer.mo.waterlevel < 3) return level.airsupply; else return max(CPlayer.air_finished - level.time, 0); } int GetSelectedInventoryAmount() { if(CPlayer.mo.InvSel != NULL) return CPlayer.mo.InvSel.Amount; return 0; } int GetKeyCount() { int num = 0; for(Inventory item = CPlayer.mo.Inv;item != NULL;item = item.Inv) { if(item is "Key") num++; } return num; } //============================================================================ // // various checker functions, based on SBARINFOs condition nodes. // //============================================================================ //============================================================================ // // checks current game mode against a flag mask // //============================================================================ bool CheckGameMode(int ValidModes) { return (!multiplayer && (ValidModes & GAMEMODE_SINGLEPLAYER)) || (deathmatch && (ValidModes & GAMEMODE_DEATHMATCH)) || (multiplayer && !deathmatch && (ValidModes & GAMEMODE_COOPERATIVE)) || (teamplay && (ValidModes & GAMEMODE_TEAMGAME)); } //============================================================================ // // checks ammo use of current weapon // //============================================================================ bool WeaponUsesAmmo(int ValidModes) { if (CPlayer == null) return false; let w = CPlayer.ReadyWeapon; if (w == null) return false; bool usesammo1 = w.AmmoType1 != NULL; bool usesammo2 = w.AmmoType2 != NULL; if (ValidModes == AMMO_PRIMARY) return usesammo1; if (ValidModes == AMMO_SECONDARY) return usesammo2; if (ValidModes == AMMO_ANY) return (usesammo1 || usesammo2); if (ValidModes == AMMO_BOTH) return (usesammo1 && usesammo2); return false; } //============================================================================ // // checks if inventory bar is visible // //============================================================================ bool isInventoryBarVisible() { if (CPlayer == null) return false; return (CPlayer.inventorytics <= 0 || level.NoInventoryBar); } //============================================================================ // // checks if aspect ratio is in a given range // //============================================================================ bool CheckAspectRatio(double min, double max) { if (CPlayer == null) return false; double aspect = screen.GetAspectRatio(); return (aspect >= min && aspect < max); } //============================================================================ // // checks if weapon is selected. // //============================================================================ bool CheckWeaponSelected(class weap, bool checksister = true) { if (CPlayer == null) return false; let w = CPlayer.ReadyWeapon; if (w == null) return false; if (w.GetClass() == weap) return true; if (checksister && w.SisterWeapon != null && w.SisterWeapon.GetClass() == weap) return true; return false; } //============================================================================ // // checks if player has the given display name // //============================================================================ bool CheckDiplayName(String displayname) { if (CPlayer == null) return false; return displayname == PlayerPawn.GetPrintableDisplayName(CPlayer.mo.GetClass()); } //============================================================================ // // checks if player has the given weapon piece // //============================================================================ bool CheckWeaponPiece(class weap, int piecenum) { if (CPlayer == null) return false; for(let inv = CPlayer.mo.Inv; inv != NULL; inv = inv.Inv) { let wh = WeaponHolder(inv); if (wh != null && wh.PieceWeapon == weap) { return (!!(wh.PieceMask & (1 << (PieceNum-1)))); } } return false; } //============================================================================ // // checks if player has the given weapon piece // //============================================================================ bool WeaponUsesAmmoType(class ammotype) { if (CPlayer == null) return false; let w = CPlayer.ReadyWeapon; if (w == NULL) return false; return w.AmmoType1 == ammotype || w.AmmoType2 == ammotype; } //============================================================================ // // checks if player has the required health // //============================================================================ bool CheckHealth(int Amount, bool percentage = false) { if (CPlayer == null) return false; int phealth = percentage ? CPlayer.mo.health * 100 / CPlayer.mo.GetMaxHealth() : CPlayer.mo.health; return (phealth >= Amount); } //============================================================================ // // checks if player is invulnerable // //============================================================================ bool isInvulnerable() { if (CPlayer == null) return false; return ((CPlayer.mo.bInvulnerable) || (CPlayer.cheats & (CF_GODMODE | CF_GODMODE2))); } //============================================================================ // // checks if player owns enough of the item // //============================================================================ bool CheckInventory(class item, int amount = 1) { if (CPlayer == null) return false; let it = CPlayer.mo.FindInventory(item); return it != null && it.Amount >= amount; } //============================================================================ // // draw stuff // //============================================================================ int GetTranslation() const { if(gameinfo.gametype & GAME_Raven) return Translation.MakeID(TRANSLATION_PlayersExtra, CPlayer.mo.PlayerNumber()); else return Translation.MakeID(TRANSLATION_Players, CPlayer.mo.PlayerNumber()); } //============================================================================ // // draw stuff // //============================================================================ void DrawTexture(TextureID texture, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0, Vector2 scale = (1., 1.) ) { if (!texture.IsValid()) return; // nothing to draw alpha *= self.alpha; if (alpha <= 0) return; // invisible Vector2 texsize = TexMan.GetScaledSize(texture); texsize.X *= scale.X; texsize.Y *= scale.Y; if (boxsize.X > 0 || boxsize.Y > 0) { double scale1 = 1., scale2 = 1.; if (boxsize.X != -1 && (boxsize.X < texsize.X || (flags & DI_FORCESCALE))) { scale1 = boxsize.X / texsize.X; } if (boxsize.Y != -1 && (boxsize.Y < texsize.Y || (flags & DI_FORCESCALE))) { scale2 = boxsize.Y / texsize.Y; } if (flags & DI_FORCESCALE) { if (boxsize.X == -1 || (boxsize.Y != -1 && scale2 < scale1)) scale1 = scale2; } else scale1 = min(scale1, scale2); boxsize = texsize * scale1; } else { boxsize = texsize; } DrawGraphic(texture, animated, pos, Alpha, !!(flags & DI_TRANSLATABLE), !!(flags & DI_DIM), itemAlign, screenAlign, false, boxsize); } //============================================================================ // // // //============================================================================ void DrawImage(String imagename, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0, Vector2 scale = (1., 1.) ) { let tex = TexMan.CheckForTexture(imagename, TexMan.TYPE_MiscPatch); DrawTexture(tex, pos, animated, screenalign, alpha, boxsize, itemAlign, flags, scale); } //============================================================================ // // // //============================================================================ void DrawIcon(int icontype, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0) { TextureID texture; Vector2 applyscale = (1, 1); Inventory atype1, atype2; switch (icontype) { case ITYPE_PLAYERICON: texture = CPlayer.mo.ScoreIcon; break; case ITYPE_AMMO1: case ITYPE_AMMO2: [atype1, atype2] = GetCurrentAmmo(); [texture, applyscale] = GetIcon(icontype == ITYPE_AMMO1? atype1 : atype2, flags, true); break; case ITYPE_ARMOR: [texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("BasicArmor"), flags, false); break; case ITYPE_WEAPON: [texture, applyscale] = GetIcon(CPlayer.ReadyWeapon, flags, false); break; case ITYPE_SIGIL: [texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("Sigil"), flags, false); break; case ITYPE_SELECTEDINVENTORY: if (CPlayer.mo.InvSel != NULL) texture = CPlayer.mo.InvSel.Icon; break; } DrawTexture(texture, pos, animated, screenalign, alpha, boxsize, itemAlign, flags, applyscale); } //============================================================================ // // // //============================================================================ void DrawHexenArmor(int armortype, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0) { let harmor = HexenArmor(statusBar.CPlayer.mo.FindInventory("HexenArmor")); if (harmor != NULL) { let slotval = harmor.Slots[armorType]; let slotincr = harmor.SlotsIncrement[armorType]; if (slotval > 0 && slotincr > 0) { //combine the alpha values alpha *= MIN(1., slotval / slotincr); } else return; } DrawImage(image, pos, animated, screenalign, alpha, boxsize, itemAlign, flags); } //============================================================================ // // // //============================================================================ void DrawInventoryIcon(class item, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0) { let texture = GetDefaultByType(item).Icon; if (texture.IsValid()) { DrawTexture(texture, pos, animated, screenalign, alpha, boxsize, itemAlign, flags); } } } //============================================================================ // // a generic value interpolator for status bar elements that can change // gradually to their new value. // //============================================================================ class LinearValueInterpolator : Object { int mCurrentValue; int mMaxChange; static LinearValueInterpolator Create(int startval, int maxchange) { let v = new("LinearValueInterpolator"); v.mCurrentValue = startval; v.mMaxChange = maxchange; return v; } // This must be called peroiodically in the status bar's Tick function. // Do not call this in the Draw function because that may skip some frames! void Update(int destvalue) { if (mCurrentValue > destvalue) { mCurrentValue = max(destvalue, mCurrentValue - mMaxChange); } else { mCurrentValue = min(destvalue, mCurrentValue + mMaxChange); } } // This must be called in the draw function to retrieve the value for output. int GetValue() { return mCurrentValue; } } class DynamicValueInterpolator : Object { int mCurrentValue; int mMinChange; double mChangeFactor; static DynamicValueInterpolator Create(int startval, double changefactor, int minchange) { let v = new("DynamicValueInterpolator"); v.mCurrentValue = startval; v.mMinChange = minchange; v.mChangeFactor = changefactor; return v; } // This must be called peroiodically in the status bar's Tick function. // Do not call this in the Draw function because that may skip some frames! void Update(int destvalue) { int diff = int(max(abs(destvalue - mCurrentValue) * mChangeFactor, mMinChange)); if (mCurrentValue > destvalue) { mCurrentValue = max(destvalue, mCurrentValue - diff); } else { mCurrentValue = min(destvalue, mCurrentValue + diff); } } // This must be called in the draw function to retrieve the value for output. int GetValue() { return mCurrentValue; } }