]> granicus.if.org Git - nethack/commitdiff
Qt hitpointbar
authorPatR <rankin@nethack.org>
Sun, 25 Oct 2020 22:47:49 +0000 (15:47 -0700)
committerPatR <rankin@nethack.org>
Sun, 25 Oct 2020 22:47:49 +0000 (15:47 -0700)
Add support for the 'hitpointbar' to the Qt interface.  Rather
than rendering the status title (name+rank or name+monster_species)
using inverse video for leading substring to produce distinct left
and right sides, draw a horizontal bar above that field.

The left portion (current health) is thicker and uses red for <10%
or <5hp, orange for <25% or <10hp, yellow for <50%, green for <75%,
blue for <100%, and black for 100%.

The right portion (missing maximum health) is thinner and runs
from white (paired with red), light gray (paired with orange),
dark gray (with yellow), plain gray (which turns out to be darker
than dark gray, with green), dark blue (with blue), and black (but
black is never shown for injury portion because that's suppressed
when at full health).

Qt already supports a square frame around the hero's map tile that
changes color according to health.  Turning the hitpointbar option
Off or On has no effect on that.

doc/fixes37.0
src/options.c
win/Qt/qt_bind.cpp
win/Qt/qt_stat.cpp
win/Qt/qt_stat.h

index c068816bec1c462488315e25df5de80bc04ad46e..d56d5c7b69911444510b1e4f383822a22d5ce60b 100644 (file)
@@ -1,4 +1,4 @@
-NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.338 $ $NHDT-Date: 1603509297 2020/10/24 03:14:57 $
+NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.341 $ $NHDT-Date: 1603666043 2020/10/25 22:47:23 $
 
 General Fixes and Modified Features
 -----------------------------------
@@ -600,6 +600,7 @@ Qt: clicking on the paper doll runs the #seeall command (inventory of wielded
        words, same set of things whose tiles are used to populate the doll)
 Qt: clicking on the status window runs the #attributes command (^X)
 Qt: add a Search button to the toolbar
+Qt: support the 'hitpointbar' option
 
 
 NetHack Community Patches (or Variation) Included
index 39e69c9f1696941ee405ee801d68cc546a96bf75..f009079127b39049291c157196a42664bed6711d 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.7 options.c       $NHDT-Date: 1599893947 2020/09/12 06:59:07 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.473 $ */
+/* NetHack 3.7 options.c       $NHDT-Date: 1603666043 2020/10/25 22:47:23 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.478 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Michael Allison, 2008. */
 /* NetHack may be freely redistributed.  See license for details. */
@@ -4607,6 +4607,12 @@ char *op;
                 /* [is reassessment really needed here?] */
                 status_initialize(REASSESS_ONLY);
                 g.opt_need_redraw = TRUE;
+#ifdef QT_GRAPHICS
+            } else if (WINDOWPORT("Qt")) {
+                /* Qt doesn't support HILITE_STATUS or FLUSH_STATUS so fails
+                   VIA_WINDOWPORT(), but it does support WC2_HITPOINTBAR */
+                g.context.botlx = TRUE;
+#endif
             }
             break;
         case opt_color:
@@ -7619,7 +7625,8 @@ doset() /* changing options via menu by Per Liboriussen */
         check_gold_symbol();
         reglyph_darkroom();
         (void) doredraw();
-    } else if (g.context.botl || g.context.botlx) {
+    }
+    if (g.context.botl || g.context.botlx) {
         bot();
     }
     return 0;
index 5fb65f980a1e35b4259a47c9c36aabc55a166a3a..b31465ad55bb6a6103af854f74a702527bf2e4cd 100644 (file)
@@ -931,12 +931,11 @@ static void Qt_positionbar(char *) {}
 
 struct window_procs Qt_procs = {
     "Qt",
-    WC_COLOR | WC_HILITE_PET
-    | WC_ASCII_MAP | WC_TILED_MAP
-    | WC_FONT_MAP | WC_TILE_FILE | WC_TILE_WIDTH | WC_TILE_HEIGHT
-    | WC_POPUP_DIALOG
-    | WC_PLAYER_SELECTION | WC_SPLASH_SCREEN,
-    0L,
+    (WC_COLOR | WC_HILITE_PET
+     | WC_ASCII_MAP | WC_TILED_MAP
+     | WC_FONT_MAP | WC_TILE_FILE | WC_TILE_WIDTH | WC_TILE_HEIGHT
+     | WC_POPUP_DIALOG | WC_PLAYER_SELECTION | WC_SPLASH_SCREEN),
+    (WC2_HITPOINTBAR),
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */
     nethack_qt_::NetHackQtBind::qt_init_nhwindows,
     nethack_qt_::NetHackQtBind::qt_player_selection,
index 02fb15772e6e42996eb92856225a65f6a87f7cbd..cfccb439d1ab6b960432d723c00b6a5829e86c22 100644 (file)
@@ -2,7 +2,7 @@
 // Qt4 conversion copyright (c) Ray Chason, 2012-2014.
 // NetHack may be freely redistributed.  See license for details.
 
-// qt_stat.cpp -- bindings between the Qt 4 interface and the main code
+// qt_stat.cpp -- status window, upper right portion of the overall window
 
 extern "C" {
 #include "hack.h"
@@ -26,10 +26,6 @@ extern const char *hu_stat[]; /* from eat.c */
 namespace nethack_qt_ {
 
 NetHackQtStatusWindow::NetHackQtStatusWindow() :
-    // Notes:
-    //  Alignment needs -2 init value, because -1 is an alignment.
-    //  Armor Class is an schar, so 256 is out of range.
-    //  Blank value is 0 and should never change.
     name(this,"(name)"),
     dlevel(this,"(dlevel)"),
     str(this, "Str"),
@@ -62,10 +58,14 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() :
     lev(this,"Lev"),
     fly(this,"Fly"),
     ride(this,"Ride"),
+    hpbar_health(this),
+    hpbar_injury(this),
     hline1(this),
     hline2(this),
     hline3(this),
-    first_set(true)
+    cursy(0),
+    first_set(true),
+    alreadyfullhp(false)
 {
     p_str = QPixmap(str_xpm);
     p_str = QPixmap(str_xpm);
@@ -127,6 +127,7 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() :
     fly.setIcon(p_fly);
     ride.setIcon(p_ride);
 
+    // separator lines
     hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken);
     hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken);
     hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken);
@@ -134,11 +135,14 @@ NetHackQtStatusWindow::NetHackQtStatusWindow() :
     hline2.setLineWidth(1);
     hline3.setLineWidth(1);
 
+    QHBoxLayout *hpbar = InitHitpointBar();
+
 #if 1 //RLC
     name.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
     dlevel.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
     QVBoxLayout *vbox = new QVBoxLayout();
     vbox->setSpacing(0);
+    vbox->addLayout(hpbar);
     vbox->addWidget(&name);
     vbox->addWidget(&dlevel);
     vbox->addWidget(&hline1);
@@ -390,6 +394,130 @@ void NetHackQtStatusWindow::fadeHighlighting()
     ride.dissipateHighlight();
 }
 
+// hitpointbar: two panels: left==current health, right==missing max health
+QHBoxLayout *NetHackQtStatusWindow::InitHitpointBar()
+{
+    hpbar_health.setFrameStyle(QFrame::NoFrame);
+    hpbar_health.setMaximumHeight(9);
+    hpbar_health.setAutoFillBackground(true);
+    if (!iflags.wc2_hitpointbar)
+        hpbar_health.hide();
+
+    hpbar_injury.setFrameStyle(QFrame::NoFrame);
+    /* health portion has thickness 9, injury portion just 3 */
+    hpbar_injury.setMaximumHeight(3);
+    hpbar_injury.setContentsMargins(0, 3, 0, 3); // left,top,right,bottom
+    hpbar_injury.setAutoFillBackground(true);
+    hpbar_injury.hide(); // only shown when hitpointbar is On and uhp < uhpmax
+
+    QHBoxLayout *hpbar = new QHBoxLayout;
+    hpbar->setSpacing(0);
+    hpbar->setMargin(0);
+    hpbar->addWidget(&hpbar_health);
+    hpbar->setAlignment(&hpbar_health, Qt::AlignLeft);
+    hpbar->addWidget(&hpbar_injury);
+    hpbar->setAlignment(&hpbar_injury, Qt::AlignRight);
+    return hpbar; // caller will add our result to vbox layout
+}
+
+// when hitpoint bar is enabled, calculate and draw it, otherwise remove it
+void NetHackQtStatusWindow::HitpointBar()
+{
+    // a style sheet is used to specify color for otherwise blank labels;
+    // barcolors[][*]: column [0=left] is current health, [1=right] is injury
+    static const char
+    *styleformat = "QLabel { background-color : %s ; color : transparent ;"
+                           " min-width : %d ; max-width %d }",
+    *barcolors[6][2] = {
+        { "black",   "black"     },  // 100%   /* second black never shown */
+        { "blue",    "darkBlue"  },  //75..99
+        // gray is darker than darkGray for some reason (at least on OSX)...
+        // green and orange would look better if they were lighter/brighter
+        { "green",   "gray"      },  //50..74
+        { "yellow",  "darkGray"  },  //25..49
+        { "#ff7f00", "lightGray" },  //10..24  /* #ff7f00=="orange" */
+        { "red",     "white"     },  // 0..9
+    };
+
+    /*
+     * tty and curses use inverse video characters in the left portion
+     * of the name+rank string to reflect hero's health.  We draw a
+     * separate line above the name+rank field instead.  The left side
+     * of the line indicates current health.  The right side is only
+     * shown when injured and indicates missing amount of maximum health.
+     */
+    if (iflags.wc2_hitpointbar) {
+        int colorindx, w,
+            ihp = Upolyd ? u.mh : u.uhp,
+            ihpmax = Upolyd ? u.mhmax : u.uhpmax;
+        ihp = std::max(std::min(ihp, ihpmax), 0);
+        int pct = 100 * ihp / ihpmax,
+            lox = hline1.x(),
+            hix = lox + hline1.width() - 1;
+        QRect geoH = hpbar_health.geometry(),
+              geoI = hpbar_injury.geometry();
+        QString styleH, styleI;
+
+        if (ihp < ihpmax) {
+            // health is less than full;
+            // use red for extreme low health even if the percentage is
+            // above the usual threshold (which will happen when maximum
+            // health is very low); do a similar threshold override for
+            // orange even though it can be distracting for low level hero
+            colorindx = (pct < 10 || ihp < 5) ? 5       // red    | white
+                        : (pct < 25 || ihp < 10 ) ? 4   // orange | lightGray
+                          : (pct < 50) ? 3              // yellow | darkGray*
+                            : (pct < 75) ? 2            // green  | gray*
+                              : 1;                      // blue   | darkBlue
+
+            int pxl_health = (hix - lox + 1) * ihp / ihpmax;
+            geoH.setRight(std::min(lox + pxl_health - 1, hix));
+            hpbar_health.setGeometry(geoH);
+            w = geoH.right() - geoH.left() + 1; // might yield 0 (ie, if dead)
+            styleH.sprintf(styleformat, barcolors[colorindx][0], w, w);
+            hpbar_health.setStyleSheet(styleH);
+            // style sheet should be doing this but width was sticking at full
+            hpbar_health.setMaximumWidth(w);
+            hpbar_health.show(); // don't need to hide() if/when width is 0
+
+            int oldleft = geoI.left();
+            geoI.setLeft(geoH.right() + 1);
+            geoI.setRight(hix);
+            hpbar_injury.setGeometry(geoI);
+            w = geoI.right() - geoI.left() + 1;
+            styleI.sprintf(styleformat, barcolors[colorindx][1], w, w);
+            hpbar_injury.setStyleSheet(styleI);
+            if (geoI.left() != oldleft)
+                hpbar_injury.move(geoI.left(), geoI.top());
+            hpbar_injury.show();
+
+            alreadyfullhp = false;
+        } else if (!alreadyfullhp) { // skip if unchanged
+            // health is full
+            colorindx = 0; // black | (not used)
+
+            hpbar_injury.hide();
+            geoI.setLeft(hix); // hix + 1
+            hpbar_injury.setGeometry(geoI);
+
+            geoH.setRight(hix);
+            hpbar_health.setGeometry(geoH);
+            w = geoH.right() - geoH.left() + 1;
+            styleH.sprintf(styleformat, barcolors[colorindx][0], w, w);
+            hpbar_health.setStyleSheet(styleH);
+            hpbar_health.setMaximumWidth(w); // (see above)
+            hpbar_health.show();
+
+            alreadyfullhp = true;
+        }
+    } else {
+        // hitpoint bar is disabled
+        hpbar_health.hide();
+        hpbar_injury.hide();
+        alreadyfullhp = false;
+    }
+}
+
 /*
  * Update the displayed status.  The current code in botl.c updates
  * two lines of information.  Both lines are always updated one after
@@ -413,6 +541,8 @@ void NetHackQtStatusWindow::updateStats()
 
     if (cursy != 0) return;    /* do a complete update when line 0 is done */
 
+    HitpointBar();
+
     int st = ACURR(A_STR);
     if (st > STR18(100)) {
         buf.sprintf("Str:%d", st - 100);        // 19..25
index a8d27cb952ca310bef182e75e644f07ab766359d..7a3715b3de91002ada581ec8a440f925ff6a15c4 100644 (file)
@@ -104,6 +104,9 @@ private:
        NetHackQtLabelledIcon fly;
        NetHackQtLabelledIcon ride;
 
+        QLabel hpbar_health; // hit point bar, left half
+        QLabel hpbar_injury; // hit point bar, right half
+
        QFrame hline1;
        QFrame hline2;
        QFrame hline3;
@@ -111,7 +114,10 @@ private:
        int cursy;
 
        bool first_set;
+        bool alreadyfullhp;
 
+        QHBoxLayout *InitHitpointBar();
+        void HitpointBar();
        void nullOut();
        void updateStats();
        void checkTurnEvents();