-/* NetHack 3.6 botl.c $NHDT-Date: 1557094795 2019/05/05 22:19:55 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.145 $ */
+/* NetHack 3.6 botl.c $NHDT-Date: 1562114350 2019/07/03 00:39:10 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.146 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2006. */
/* NetHack may be freely redistributed. See license for details. */
const char *fldfmt;
long time; /* moves when this field hilite times out */
boolean chg; /* need to recalc time? */
+ boolean percent_matters;
+ short percent_value;
unsigned anytype;
anything a;
char *val;
STATIC_DCL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *));
STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int));
STATIC_DCL int FDECL(percentage, (struct istat_s *, struct istat_s *));
+STATIC_DCL int NDECL(exp_percentage);
#ifdef STATUS_HILITES
STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int));
#define INIT_THRESH /*empty*/
#endif
-#define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld) \
- { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \
+#define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld) \
+ { name, fmtstr, 0L, FALSE, FALSE, 0, anytyp, \
+ { (genericptr_t) 0 }, (char *) 0, \
wid, -1, fld INIT_THRESH }
-#define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld) \
- { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \
+#define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld) \
+ { name, fmtstr, 0L, FALSE, TRUE, 0, anytyp, \
+ { (genericptr_t) 0 }, (char *) 0, \
wid, maxfld, fld INIT_THRESH }
-/* If entries are added to this, botl.h will require updating too */
+/* If entries are added to this, botl.h will require updating too.
+ 'max' value of BL_EXP gets special handling since the percentage
+ involved isn't a direct 100*current/maximum calculation. */
STATIC_VAR struct istat_s initblstats[MAXBLSTATS] = {
INIT_BLSTAT("title", "%s", ANY_STR, MAXVALWIDTH, BL_TITLE),
INIT_BLSTAT("strength", " St:%s", ANY_INT, 10, BL_STR),
INIT_BLSTAT("gold", " %s", ANY_LONG, 30, BL_GOLD),
INIT_BLSTATP("power", " Pw:%s", ANY_INT, 10, BL_ENEMAX, BL_ENE),
INIT_BLSTAT("power-max", "(%s)", ANY_INT, 10, BL_ENEMAX),
- INIT_BLSTAT("experience-level", " Xp:%s", ANY_INT, 10, BL_XP),
+ INIT_BLSTATP("experience-level", " Xp:%s", ANY_INT, 10, BL_EXP, BL_XP),
INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC),
INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD),
INIT_BLSTAT("time", " T:%s", ANY_LONG, 20, BL_TIME),
INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP),
INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX),
INIT_BLSTAT("dungeon-level", "%s", ANY_STR, MAXVALWIDTH, BL_LEVELDESC),
- INIT_BLSTAT("experience", "/%s", ANY_LONG, 20, BL_EXP),
+ INIT_BLSTATP("experience", "/%s", ANY_LONG, 20, BL_EXP, BL_EXP),
INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION)
};
int pc, chg, color = NO_COLOR;
unsigned anytype;
boolean updated = FALSE, reset;
- struct istat_s *curr = NULL, *prev = NULL;
- enum statusfields idxmax;
+ struct istat_s *curr, *prev;
+ enum statusfields fldmax;
/*
* Now pass the changed values to window port.
color = NO_COLOR;
chg = update_all ? 0 : compare_blstats(prev, curr);
+ /*
+ * TODO:
+ * Dynamically update 'percent_matters' as rules are added or
+ * removed to track whether any of them are precentage rules.
+ * Then there'll be no need to assume that non-Null 'thresholds'
+ * means that percentages need to be kept up to date.
+ * [Affects exp_percent_changing() too.]
+ */
+ if (((chg || update_all || fld == BL_XP)
+ && curr->percent_matters && curr->thresholds)
+ /* when 'hitpointbar' is On, percent matters even if HP
+ hasn't changed and has no percentage rules (in case HPmax
+ has changed when HP hasn't, where we ordinarily wouldn't
+ update HP so would miss an update of the hitpoint bar) */
+ || (fld == BL_HP && iflags.wc2_hitpointbar)) {
+ fldmax = curr->idxmax;
+ pc = (fldmax == BL_EXP) ? exp_percentage()
+ : (fldmax >= 0) ? percentage(curr, &blstats[idx][fldmax])
+ : 0; /* bullet proofing; can't get here */
+ if (pc != prev->percent_value)
+ chg = 1;
+ curr->percent_value = pc;
+ } else {
+ pc = 0;
+ }
/* Temporary? hack: moveloop()'s prolog for a new game sets
* context.rndencode after the status window has been init'd,
}
#endif
- /*
- * TODO?
- * It's possible for HPmax (or ENEmax) to change while current
- * HP (or energy) stays the same. [Perhaps current and maximum
- * both go up, then before the next status update takes place
- * current goes down again.] If that happens with HPmax, we
- * ought to force the windowport to treat current HP as changed
- * if hitpointbar is On, in order for that to be re-rendered.
- */
if (update_all || chg || reset) {
- idxmax = curr->idxmax;
- pc = (idxmax >= 0) ? percentage(curr, &blstats[idx][idxmax]) : 0;
-
if (!valsetlist[fld])
(void) anything_to_s(curr->val, &curr->a, anytype);
status_enablefield(fld, fieldname, fieldfmt, fldenabl);
}
update_all = TRUE;
+ context.botlx = TRUE;
}
void
return result;
}
+/* percentage for both xp (level) and exp (points) is the percentage for
+ (curr_exp - this_level_start) in (next_level_start - this_level_start) */
+STATIC_OVL int
+exp_percentage()
+{
+ int res = 0;
+
+ if (u.ulevel < 30) {
+ long exp_val, nxt_exp_val, curlvlstart;
+
+ curlvlstart = newuexp(u.ulevel - 1);
+ exp_val = u.uexp - curlvlstart;
+ nxt_exp_val = newuexp(u.ulevel) - curlvlstart;
+ if (exp_val == nxt_exp_val - 1L) {
+ /*
+ * Full 100% is unattainable since hero gains a level
+ * and the threshold for next level increases, but treat
+ * (next_level_start - 1 point) as a special case. It's a
+ * key value after being level drained so is something that
+ * some players would like to be able to highlight distinctly.
+ */
+ res = 100;
+ } else {
+ struct istat_s curval, maxval;
+
+ curval.anytype = maxval.anytype = ANY_LONG;
+ curval.a = maxval.a = zeroany;
+ curval.a.a_long = exp_val;
+ maxval.a.a_long = nxt_exp_val;
+ /* maximum delta between levels is 10000000; calculation of
+ 100 * (10000000 - N) / 10000000 fits within 32-bit long */
+ res = percentage(&curval, &maxval);
+ }
+ }
+ return res;
+}
+
+/* experience points have changed but experience level hasn't; decide whether
+ botl update is needed for a different percentage highlight rule for Xp */
+boolean
+exp_percent_changing()
+{
+ int pc, color_dummy;
+ anything a;
+ struct hilite_s *rule;
+ struct istat_s *curr;
+
+ /* if status update is already requested, skip this processing */
+ if (!context.botl) {
+ /*
+ * Status update is warranted iff percent integer changes and the new
+ * percentage results in a different highlighting rule being selected.
+ */
+ curr = &blstats[now_or_before_idx][BL_XP];
+ /* TODO: [see eval_notify_windowport_field() about percent_matters
+ and the check against 'thresholds'] */
+ if (curr->percent_matters && curr->thresholds
+ && (pc = exp_percentage()) != curr->percent_value) {
+ a = zeroany;
+ a.a_int = (int) u.ulevel;
+ rule = get_hilite(now_or_before_idx, BL_XP,
+ (genericptr_t) &a, 0, pc, &color_dummy);
+ if (rule != curr->hilite_rule)
+ return TRUE; /* caller should set 'context.botl' to True */
+ }
+ }
+ return FALSE;
+}
+
/* callback so that interface can get capacity index rather than trying
to reconstruct that from the encumbrance string or asking the general
core what the value is */