class StrifeStatusBar : BaseStatusBar { // Number of tics to move the popscreen up and down. const POP_TIME = (Thinker.TICRATE/8); // Popscreen height when fully extended const POP_HEIGHT = 104; // Number of tics to scroll keys left const KEY_TIME = (Thinker.TICRATE/3); enum eImg { imgINVCURS, imgCURSOR01, imgINVBACK, imgINVTOP, imgINVPOP, imgINVPOP2, imgINVPBAK, imgINVPBAK2, imgFONG0, imgFONG1, imgFONG2, imgFONG3, imgFONG4, imgFONG5, imgFONG6, imgFONG7, imgFONG8, imgFONG9, imgFONG_PERCENT, imgFONY0, imgFONY1, imgFONY2, imgFONY3, imgFONY4, imgFONY5, imgFONY6, imgFONY7, imgFONY8, imgFONY9, imgFONY_PERCENT, imgCOMM, imgMEDI, imgARM1, imgARM2, imgNEGATIVE, imgINumbers = imgFONG0, }; TextureID Images[imgNEGATIVE + 1]; int CursorImage; int CurrentPop, PendingPop, PopHeight, PopHeightChange; int KeyPopPos, KeyPopScroll; double ItemFlash; override void Init() { Super.Init(); SetSize(32, 320, 200); DoCommonInit(); } override void NewGame () { DoCommonInit (); if (CPlayer != NULL) { AttachToPlayer (CPlayer); } } override void Draw (int state, double TicFrac) { Super.Draw (state, TicFrac); if (state == HUD_StatusBar) { DrawMainBar (TicFrac); } else { if (state == HUD_Fullscreen) { DrawFullScreenStuff (); } // Draw pop screen (log, keys, and status) if (CurrentPop != POP_None && PopHeight < 0) { DrawPopScreen (screen.GetHeight(), TicFrac); } } } override void ShowPop (int popnum) { Super.ShowPop(popnum); if (popnum == CurrentPop) { if (popnum == POP_Keys) { Inventory item; KeyPopPos += 10; KeyPopScroll = 280; int i = 0; for (item = CPlayer.mo.Inv; item != NULL; item = item.Inv) { if (item is "Key") { if (i == KeyPopPos) { return; } i++; } } } PendingPop = POP_None; // Do not scroll keys horizontally when dropping the popscreen KeyPopScroll = 0; KeyPopPos -= 10; } else { KeyPopPos = 0; PendingPop = popnum; } } override bool MustDrawLog(int state) { // Tell the base class to draw the log if the pop screen won't be displayed. return false; } void DoCommonInit () { static const String strifeLumpNames[] = { "INVCURS", "CURSOR01", "INVBACK", "INVTOP", "INVPOP", "INVPOP2", "INVPBAK", "INVPBAK2", "INVFONG0", "INVFONG1", "INVFONG2", "INVFONG3", "INVFONG4", "INVFONG5", "INVFONG6", "INVFONG7", "INVFONG8", "INVFONG9", "INVFONG%", "INVFONY0", "INVFONY1", "INVFONY2", "INVFONY3", "INVFONY4", "INVFONY5", "INVFONY6", "INVFONY7", "INVFONY8", "INVFONY9", "INVFONY%", "I_COMM", "I_MDKT", "I_ARM1", "I_ARM2", "" }; for(int i = 0; i <= imgNEGATIVE; i++) { Images[i] = TexMan.CheckForTexture(strifeLumpNames[i], TexMan.TYPE_MiscPatch); } CursorImage = Images[imgINVCURS].IsValid() ? imgINVCURS : imgCURSOR01; CurrentPop = POP_None; PendingPop = POP_NoChange; PopHeight = 0; KeyPopPos = 0; KeyPopScroll = 0; ItemFlash = 0; } override void Tick () { Super.Tick (); if (ItemFlash > 0) { ItemFlash -= 1/14.; if (ItemFlash < 0) { ItemFlash = 0; } } PopHeightChange = 0; if (PendingPop != POP_NoChange) { if (PopHeight < 0) { PopHeightChange = POP_HEIGHT / POP_TIME; PopHeight += POP_HEIGHT / POP_TIME; } else { CurrentPop = PendingPop; PendingPop = POP_NoChange; } } else { if (CurrentPop == POP_None) { PopHeight = 0; } else if (PopHeight > -POP_HEIGHT) { PopHeight -= POP_HEIGHT / POP_TIME; if (PopHeight < -POP_HEIGHT) { PopHeight = -POP_HEIGHT; } else { PopHeightChange = -POP_HEIGHT / POP_TIME; } } if (KeyPopScroll > 0) { KeyPopScroll -= 280 / KEY_TIME; if (KeyPopScroll < 0) { KeyPopScroll = 0; } } } } override void FlashItem (Class itemtype) { ItemFlash = 0.75; } private void FillBar(double x, double y, double start, double stopp, Color color1, Color color2) { Vector2 virt = Scaled? (320., 200.) : (screen.GetWidth(), screen.GetHeight()); Vector2 pos, sizev; start *=2; stopp *=2; [pos, sizev] = screen.VirtualToRealCoords((ST_X + x + start, ST_Y + y), (stopp - start, 1), virt, true, Scaled); screen.Dim(color1, 1.0, pos.X + 0.5, pos.Y + 0.5, sizev.X + 0.5, sizev.Y + 0.5); [pos, sizev] = screen.VirtualToRealCoords((ST_X + x + start, ST_Y + y + 1), (stopp - start, 1), virt, true, Scaled); screen.Dim(color2, 1.0, pos.X + 0.5, pos.Y + 0.5, sizev.X + 0.5, sizev.Y + 0.5); } protected void DrawHealthBar(int health, int x, int y) { Color green1 = Color(180, 228, 128); // light green Color green2 = Color(128, 180, 80); // dark green Color blue1 = Color(196, 204, 252); // light blue Color blue2 = Color(148, 152, 200); // dark blue Color gold1 = Color(224, 188, 0); // light gold Color gold2 = Color(208, 128, 0); // dark gold Color red1 = Color(216, 44, 44); // light red Color red2 = Color(172, 28, 28); // dark red if (health == 999) { FillBar (x, y, 0, 100, gold1, gold2); } else { if (health <= 100) { if (health <= 10) { FillBar (x, y, 0, health, red1, red2); } else if (health <= 20) { FillBar (x, y, 0, health, gold1, gold2); } else { FillBar (x, y, 0, health, green1, green2); } //FillBar (x, y, health, 100, 0, 0); } else { int stopp = 200 - health; FillBar (x, y, 0, stopp, green1, green2); FillBar (x, y, stopp, 100, blue1, blue2); } } } protected void DrawMainBar (double TicFrac) { Inventory item; int i; // Pop screen (log, keys, and status) if (CurrentPop != POP_None && PopHeight < 0) { DrawPopScreen (Scaled ? (ST_Y - 8) * screen.GetHeight() / 200 : ST_Y - 8, TicFrac); } DrawImage (Images[imgINVBACK], 0, 0); DrawImage (Images[imgINVTOP], 0, -8); // Health DrINumber (CPlayer.health, 79, -6, imgFONG0); int points; if (CPlayer.cheats & CF_GODMODE) { points = 999; } else { points = min(CPlayer.health, 200); } DrawHealthBar (points, 49, 4); DrawHealthBar (points, 49, 7); // Armor item = CPlayer.mo.FindInventory('BasicArmor'); if (item != NULL && item.Amount > 0) { DrawImage (item.Icon, 2, 9); DrINumber (item.Amount, 27, 23, imgFONY0); } // Ammo Inventory ammo1, ammo2; int ammocount1, ammocount2; [ammo1, ammo2, ammocount1, ammocount2] = GetCurrentAmmo (); if (ammo1 != NULL) { DrINumber (ammo1.Amount, 311, -6, imgFONG0); DrawImage (ammo1.Icon, 290, 13); } // Sigil item = CPlayer.mo.FindInventory('Sigil'); if (item != NULL) { DrawImage (item.Icon, 253, 7); } // Inventory CPlayer.inventorytics = 0; CPlayer.mo.InvFirst = ValidateInvFirst (6); i = 0; for (item = CPlayer.mo.InvFirst; item != NULL && i < 6; item = item.NextInv()) { if (item == CPlayer.mo.InvSel) { screen.DrawTexture (Images[CursorImage], true, 42 + 35*i + ST_X, 12 + ST_Y, DTA_Bottom320x200, Scaled, DTA_Alpha, 1. - ItemFlash); } if (item.Icon.isValid()) { DrawDimImage (item.Icon, 48 + 35*i, 14, item.Amount <= 0); } DrINumber (item.Amount, 74 + 35*i, 23, imgFONY0); i++; } } protected void DrawFullScreenStuff () { fullscreenoffsets = true; // Draw health DrINumberOuter (CPlayer.health, 4, -10, false, 7); DrawTexture(Images[imgMEDI], (14, -17), false, 1.0, BOTTOM|LEFT, itemalign: BOTTOM|HCENTER); // Draw armor let armor = CPlayer.mo.FindInventory('BasicArmor'); if (armor != NULL && armor.Amount != 0) { DrINumberOuter (armor.Amount, 35, -10, false, 7); DrawTexture(armor.Icon, (45, -17), false, 1.0, BOTTOM|LEFT, itemalign: BOTTOM|HCENTER); } // Draw ammo Inventory ammo1, ammo2; int ammocount1, ammocount2; [ammo1, ammo2, ammocount1, ammocount2] = GetCurrentAmmo (); if (ammo1 != NULL) { // Draw primary ammo in the bottom-right corner DrINumberOuter (ammo1.Amount, -23, -10, false, 7); DrawTexture(ammo1.Icon, (-14, -17), false, 1.0, BOTTOM|RIGHT, itemalign: BOTTOM|HCENTER); if (ammo2 != NULL && ammo1!=ammo2) { // Draw secondary ammo just above the primary ammo DrINumberOuter (ammo2.Amount, -23, -48, false, 7); DrawTexture(ammo1.Icon, (-14, -55), false, 1.0, BOTTOM|RIGHT, itemalign: BOTTOM|HCENTER); } } if (deathmatch) { // Draw frags (in DM) DrBNumberOuterFont (CPlayer.fragcount, -44, 1); } // Draw inventory if (CPlayer.inventorytics == 0) { if (CPlayer.mo.InvSel != null) { if (ItemFlash > 0) { vector2 size = TexMan.GetScaledSize(Images[CursorImage]); screen.DrawTexture (Images[CursorImage], true, -28, -15, DTA_HUDRules, HUD_Normal, DTA_LeftOffsetF, size.X, DTA_TopOffsetF, size.Y, DTA_Alpha, ItemFlash); } DrINumberOuter (CPlayer.mo.InvSel.Amount, -51, -10, false, 7); screen.DrawTexture (CPlayer.mo.InvSel.Icon, true, -42, -17, DTA_HUDRules, HUD_Normal, DTA_CenterBottomOffset, true, DTA_ColorOverlay, CPlayer.mo.InvSel.Amount > 0 ? 0 : Color(170, 0, 0, 0)); } } else { CPlayer.mo.InvFirst = ValidateInvFirst (6); int i = 0; Inventory item; if (CPlayer.mo.InvFirst != NULL) { for (item = CPlayer.mo.InvFirst; item != NULL && i < 6; item = item.NextInv()) { if (item == CPlayer.mo.InvSel) { screen.DrawTexture (Images[CursorImage], true, -100+i*35, -21, DTA_HUDRules, HUD_HorizCenter, DTA_Alpha, 0.75); } if (item.Icon.isValid()) { screen.DrawTexture (item.Icon, true, -94 + i*35, -19, DTA_HUDRules, HUD_HorizCenter, DTA_ColorOverlay, CPlayer.mo.InvSel.Amount > 0 ? 0 : Color(170, 0, 0, 0)); } DrINumberOuter (item.Amount, -89 + i*35, -10, true, 7); ++i; } } } fullscreenoffsets = false; } protected void DrawPopScreen (int bottom, double TicFrac) { String buff; String label; int i; Inventory item; int xscale, yscale, left, top; int bars = (CurrentPop == POP_Status) ? imgINVPOP : imgINVPOP2; int back = (CurrentPop == POP_Status) ? imgINVPBAK : imgINVPBAK2; // Extrapolate the height of the popscreen for smoother movement int height = clamp (PopHeight + int(TicFrac * PopHeightChange), -POP_HEIGHT, 0); xscale = CleanXfac; yscale = CleanYfac; left = screen.GetWidth()/2 - 160*CleanXfac; top = bottom + height * yscale; screen.DrawTexture (Images[back], true, left, top, DTA_CleanNoMove, true, DTA_Alpha, 0.75); screen.DrawTexture (Images[bars], true, left, top, DTA_CleanNoMove, true); switch (CurrentPop) { case POP_Log: { int seconds = Thinker.Tics2Seconds(level.time); // Draw the latest log message. buff = String.Format("%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, (seconds) % 60); screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, buff, DTA_CleanNoMove, true); if (CPlayer.LogText.Length() > 0) { BrokenLines lines = SmallFont2.BreakLines(CPlayer.LogText, 272); for (i = 0; i < lines.Count(); ++i) { screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 24 * xscale, top + (18 + i * 12)*yscale, lines.StringAt(i), DTA_CleanNoMove, true); } } break; } case POP_Keys: // List the keys the player has. int pos, endpos, leftcol; int clipleft, clipright; pos = KeyPopPos; endpos = pos + 10; leftcol = 20; clipleft = left + 17*xscale; clipright = left + (320-17)*xscale; if (KeyPopScroll > 0) { // Extrapolate the scroll position for smoother scrolling int scroll = MAX (0, KeyPopScroll - int(TicFrac * (280./KEY_TIME))); pos -= 10; leftcol = leftcol - 280 + scroll; } i = 0; for (item = CPlayer.mo.Inv; i < endpos && item != NULL; item = item.Inv) { if (!(item is "Key")) continue; if (i < pos) { i++; continue; } label = item.GetTag(); int colnum = ((i-pos) / 5) & (KeyPopScroll > 0 ? 3 : 1); int rownum = (i % 5) * 18; screen.DrawTexture (item.Icon, true, left + (colnum * 140 + leftcol)*xscale, top + (6 + rownum)*yscale, DTA_CleanNoMove, true, DTA_ClipLeft, clipleft, DTA_ClipRight, clipright); screen.DrawText (SmallFont2, Font.CR_UNTRANSLATED, left + (colnum * 140 + leftcol + 17)*xscale, top + (11 + rownum)*yscale, label, DTA_CleanNoMove, true, DTA_ClipLeft, clipleft, DTA_ClipRight, clipright); i++; } break; case POP_Status: // Show miscellaneous status items. // Print stats DrINumber2 (CPlayer.mo.accuracy, left+268*xscale, top+28*yscale, 7*xscale, imgFONY0); DrINumber2 (CPlayer.mo.stamina, left+268*xscale, top+52*yscale, 7*xscale, imgFONY0); // How many keys does the player have? i = 0; for (item = CPlayer.mo.Inv; item != NULL; item = item.Inv) { if (item is "Key") { i++; } } DrINumber2 (i, left+268*xscale, top+76*yscale, 7*xscale, imgFONY0); // Does the player have a communicator? item = CPlayer.mo.FindInventory ("Communicator"); if (item != NULL) { screen.DrawTexture (item.Icon, true, left + 280*xscale, top + 74*yscale, DTA_CleanNoMove, true); } // How much ammo does the player have? static const class AmmoList[] = { "ClipOfBullets", "PoisonBolts", "ElectricBolts", "HEGrenadeRounds", "PhosphorusGrenadeRounds", "MiniMissiles", "EnergyPod"}; static const int AmmoY[] = {19, 35, 43, 59, 67, 75, 83}; for (i = 0; i < 7; ++i) { item = CPlayer.mo.FindInventory (AmmoList[i]); if (item == NULL) { DrINumber2 (0, left+206*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0); DrINumber2 (GetDefaultByType(AmmoList[i]).MaxAmount, left+239*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0); } else { DrINumber2 (item.Amount, left+206*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0); DrINumber2 (item.MaxAmount, left+239*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0); } } // What weapons does the player have? static const class WeaponList[] = { "StrifeCrossbow", "AssaultGun", "FlameThrower", "MiniMissileLauncher", "StrifeGrenadeLauncher", "Mauler" }; static const int WeaponX[] = {23, 21, 57, 20, 55, 52}; static const int WeaponY[] = {19, 41, 50, 64, 20, 75}; for (i = 0; i < 6; ++i) { item = CPlayer.mo.FindInventory (WeaponList[i]); if (item != NULL) { screen.DrawTexture (item.Icon, true, left + WeaponX[i] * xscale, top + WeaponY[i] * yscale, DTA_CleanNoMove, true, DTA_LeftOffset, 0, DTA_TopOffset, 0); } } break; } } void DrINumber (int val, int x, int y, int imgBase) const { x -= 7; if (val == 0) { DrawImage (Images[imgBase], x, y); } else { while (val != 0) { DrawImage (Images[imgBase+val%10], x, y); val /= 10; x -= 7; } } } void DrINumber2 (int val, int x, int y, int width, int imgBase) const { x -= width; if (val == 0) { screen.DrawTexture (Images[imgBase], true, x, y, DTA_CleanNoMove, true); } else { while (val != 0) { screen.DrawTexture (Images[imgBase+val%10], true, x, y, DTA_CleanNoMove, true); val /= 10; x -= width; } } } //--------------------------------------------------------------------------- // // PROC DrINumberOuter // // Draws a number outside the status bar, possibly scaled. // //--------------------------------------------------------------------------- void DrINumberOuter(int val, int x, int y, bool center, int w) const { bool negative = false; x += w * 2; if (val < 0) { negative = true; val = -val; } else if (val == 0) { screen.DrawTexture(Images[imgINumbers], true, x + 1, y + 1, DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); screen.DrawTexture(Images[imgINumbers], true, x, y, DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); return; } int oval = val; int ox = x; // First the shadow while (val != 0) { screen.DrawTexture(Images[imgINumbers + val % 10], true, x + 1, y + 1, DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); x -= w; val /= 10; } if (negative) { screen.DrawTexture(Images[imgNEGATIVE], true, x + 1, y + 1, DTA_FillColor, 0, DTA_Alpha, HR_SHADOW, DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); } // Then the real deal val = oval; x = ox; while (val != 0) { screen.DrawTexture(Images[imgINumbers + val % 10], true, x, y, DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); x -= w; val /= 10; } if (negative) { screen.DrawTexture(Images[imgNEGATIVE], true, x, y, DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal); } } //--------------------------------------------------------------------------- // // PROC DrBNumberOuter // // Draws a three digit number using the real big font outside the status bar. // //--------------------------------------------------------------------------- void DrBNumberOuterFont(int val, int x, int y, int size = 3) const { int v; bool negative = false; TextureID pic; int w = BigFont.GetCharWidth("0"); int ww = w; if (w > 1) { w--; } int xpos = x + w / 2 + (size - 1)*w; if (val == 0) { screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, "0", DTA_HUDRules, HUD_Normal, DTA_Alpha, HR_SHADOW, DTA_FillColor, 0); screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "0", DTA_HUDRules, HUD_Normal); return; } else if (val < 0) { negative = true; val = -val; } int oval = val; int oxpos = xpos; // First the shadow while (val != 0) { v = BigFont.GetCharWidth(int("0") + val % 10); screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, DTA_HUDRules, HUD_Normal, DTA_Alpha, HR_SHADOW, DTA_FillColor, 0); val /= 10; xpos -= w; } if (negative) { v = BigFont.GetCharWidth("-"); screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, "-", DTA_HUDRules, HUD_Normal, DTA_Alpha, HR_SHADOW, DTA_FillColor, 0); } // Then the foreground number val = oval; xpos = oxpos; while (val != 0) { v = BigFont.GetCharWidth(int("0") + val % 10); screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "0", DTA_HUDRules, HUD_Normal); val /= 10; xpos -= w; } if (negative) { v = BigFont.GetCharWidth("-"); screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "-", DTA_HUDRules, HUD_Normal); } } //--------------------------------------------------------------------------- // // PROC DrawImage // // Draws an image with the status bar's upper-left corner as the origin. // //--------------------------------------------------------------------------- void DrawImage(TextureID img, int x, int y) const { if (img.IsValid()) { screen.DrawTexture(img, true, x + ST_X, y + ST_Y, DTA_Bottom320x200, Scaled); } } //--------------------------------------------------------------------------- // // PROC DrawImage // // Draws an optionally dimmed image with the status bar's upper-left corner // as the origin. // //--------------------------------------------------------------------------- void DrawDimImage(TextureID img, int x, int y, bool dimmed) const { if (img.IsValid()) { screen.DrawTexture(img, true, x + ST_X, y + ST_Y, DTA_ColorOverlay, dimmed ? Color(170, 0, 0, 0) : 0, DTA_Bottom320x200, Scaled); } } }