mirror of
https://codeberg.org/noisytoot/notnotdnethack.git
synced 2024-11-14 13:26:10 +00:00
949 lines
30 KiB
C
949 lines
30 KiB
C
/* vim:set cin ft=c sw=4 sts=4 ts=8 et ai cino=Ls\:0t0(0 : -*- mode:c;fill-column:80;tab-width:8;c-basic-offset:4;indent-tabs-mode:nil;c-file-style:"k&r" -*-*/
|
|
|
|
#include "curses.h"
|
|
#include "hack.h"
|
|
#include "wincurs.h"
|
|
#include "cursstat.h"
|
|
#include "botl.h"
|
|
|
|
/* Status window functions for curses interface */
|
|
|
|
/* Private declarations */
|
|
|
|
/* Used to track previous value of things, to highlight changes. */
|
|
typedef struct nhs {
|
|
long value;
|
|
int highlight_turns;
|
|
int highlight_color;
|
|
} nhstat;
|
|
|
|
static attr_t get_trouble_color(const char *);
|
|
static void draw_trouble_str(const char *, const char *);
|
|
static void print_statdiff(const char *append, nhstat *, int, int);
|
|
static void get_playerrank(char *);
|
|
static int hpen_color(boolean, int, int);
|
|
static void draw_bar(boolean, int, int, const char *);
|
|
static void draw_horizontal(int, int, int, int);
|
|
static void draw_horizontal_new(int, int, int, int);
|
|
static void draw_vertical(int, int, int, int);
|
|
static void curses_add_statuses(WINDOW *, boolean, boolean, int *, int *, boolean);
|
|
static void curses_add_status(WINDOW *, boolean, boolean, int *, int *,
|
|
const char *, const char *, long, boolean);
|
|
static int decrement_highlight(nhstat *, boolean);
|
|
|
|
static attr_t hpen_color_attr(boolean, int, int);
|
|
extern struct color_option text_color_of(const char *text,
|
|
const struct text_color_option *color_options);
|
|
struct color_option percentage_color_of(int value, int max,
|
|
const struct percent_color_option *color_options);
|
|
|
|
extern const struct text_color_option *text_colors;
|
|
extern const struct percent_color_option *hp_colors;
|
|
extern const struct percent_color_option *pw_colors;
|
|
|
|
/* Whether or not we have printed status window content at least once.
|
|
Used to ensure that prev* doesn't end up highlighted on game start. */
|
|
static boolean first = TRUE;
|
|
static nhstat prevdepth;
|
|
static nhstat prevstr;
|
|
static nhstat prevint;
|
|
static nhstat prevwis;
|
|
static nhstat prevdex;
|
|
static nhstat prevcon;
|
|
static nhstat prevcha;
|
|
static nhstat prevau;
|
|
static nhstat prevlevel;
|
|
static nhstat prevdive;
|
|
static nhstat prevac;
|
|
static nhstat prevdr;
|
|
static nhstat prevexp;
|
|
static nhstat prevtime;
|
|
|
|
static nhstat prevscore;
|
|
|
|
extern const char *hu_stat[]; /* from eat.c */
|
|
extern const char *ca_hu_stat[]; /* from eat.c */
|
|
extern const char *enc_stat[]; /* from botl.c */
|
|
extern const char *enc_stat_abbrev1[]; /* from botl.c */
|
|
extern const char *enc_stat_abbrev2[]; /* from botl.c */
|
|
|
|
static attr_t
|
|
get_trouble_color(const char *stat)
|
|
{
|
|
attr_t res = curses_color_attr(CLR_GRAY, 0);
|
|
/* Check if we have a color enabled with statuscolors */
|
|
if (!iflags.use_status_colors)
|
|
return curses_color_attr(CLR_GRAY, 0); /* no color configured */
|
|
|
|
struct color_option stat_color;
|
|
|
|
stat_color = text_color_of(stat, text_colors);
|
|
if (stat_color.color == NO_COLOR && !stat_color.attr_bits)
|
|
return curses_color_attr(CLR_GRAY, 0);
|
|
|
|
if (stat_color.color != NO_COLOR)
|
|
res = curses_color_attr(stat_color.color, 0);
|
|
|
|
res = curses_color_attr(stat_color.color, 0);
|
|
int count;
|
|
for (count = 0; (1 << count) <= stat_color.attr_bits; count++) {
|
|
if (count != ATR_NONE &&
|
|
(stat_color.attr_bits & (1 << count)))
|
|
res |= curses_convert_attr(count);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* TODO: This is in the wrong place. */
|
|
void
|
|
get_playerrank(char *rank)
|
|
{
|
|
char buf[BUFSZ];
|
|
if (Upolyd) {
|
|
int k = 0;
|
|
|
|
Strncpy(buf, mons[u.umonnum].mname, BUFSZ);
|
|
while(buf[k] != 0) {
|
|
if ((k == 0 || (k > 0 && buf[k-1] == ' ')) &&
|
|
'a' <= buf[k] && buf[k] <= 'z')
|
|
buf[k] += 'A' - 'a';
|
|
k++;
|
|
}
|
|
Strncpy(rank, buf, BUFSZ);
|
|
} else
|
|
Strncpy(rank, rank_of(u.ulevel, Role_switch, flags.female), BUFSZ);
|
|
}
|
|
|
|
/* Handles numerical stat changes of various kinds.
|
|
type is generally STAT_OTHER (generic "do nothing special"),
|
|
but is used if the stat needs to be handled in a special way. */
|
|
static void
|
|
print_statdiff(const char *append, nhstat *stat, int new, int type)
|
|
{
|
|
char buf[BUFSZ];
|
|
WINDOW *win = curses_get_nhwin(STATUS_WIN);
|
|
|
|
int color = CLR_GRAY;
|
|
|
|
/* Turncount isn't highlighted, or it would be highlighted constantly. */
|
|
if (type != STAT_TIME && new != stat->value) {
|
|
/* Less AC is better */
|
|
if ((type == STAT_AC && new < stat->value) ||
|
|
(type != STAT_AC && new > stat->value)) {
|
|
color = STAT_UP_COLOR;
|
|
if (type == STAT_GOLD)
|
|
color = HI_GOLD;
|
|
} else
|
|
color = STAT_DOWN_COLOR;
|
|
|
|
stat->value = new;
|
|
stat->highlight_color = color;
|
|
stat->highlight_turns = 5;
|
|
} else if (stat->highlight_turns)
|
|
color = stat->highlight_color;
|
|
|
|
attr_t attr = curses_color_attr(color, 0);
|
|
wattron(win, attr);
|
|
wprintw(win, "%s", append);
|
|
if (type == STAT_STR && new > 18) {
|
|
if (new > STR18(100))
|
|
wprintw(win, "%d", new - 20);
|
|
else if (new == STR18(100))
|
|
wprintw(win, "18/**");
|
|
else
|
|
wprintw(win, "18/%02d", (new - 18)*5);
|
|
} else
|
|
wprintw(win, "%d", new);
|
|
|
|
wattroff(win, attr);
|
|
}
|
|
|
|
static void
|
|
draw_trouble_str(const char *hilite, const char *str)
|
|
{
|
|
WINDOW *win = curses_get_nhwin(STATUS_WIN);
|
|
|
|
attr_t attr = get_trouble_color(hilite);
|
|
wattron(win, attr);
|
|
wprintw(win, "%s", str);
|
|
wattroff(win, attr);
|
|
}
|
|
|
|
/* Returns a ncurses attribute for foreground and background.
|
|
This should probably be in cursinit.c or something. */
|
|
attr_t
|
|
curses_color_attr(int nh_color, int bg_color)
|
|
{
|
|
int color = nh_color + 1;
|
|
int cnum = COLORS >= 16 ? 16 : 8;
|
|
attr_t cattr = A_NORMAL;
|
|
|
|
if (!nh_color) {
|
|
#ifdef USE_DARKGRAY
|
|
if (iflags.wc2_darkgray) {
|
|
if (!can_change_color() || COLORS <= 16)
|
|
cattr |= A_BOLD;
|
|
} else
|
|
#endif
|
|
color = COLOR_BLUE;
|
|
}
|
|
|
|
if (COLORS < 16 && color > 8) {
|
|
color -= 8;
|
|
cattr = A_BOLD;
|
|
}
|
|
|
|
/* Can we do background colors? We can if we have more than
|
|
16*7 colors (more than 8*7 for terminals with bold) */
|
|
if (COLOR_PAIRS > cnum * 7) {
|
|
if (bg_color != NO_COLOR && bg_color != CLR_BLACK)
|
|
color += cnum * (1+bg_color);
|
|
}
|
|
cattr |= COLOR_PAIR(color);
|
|
|
|
return cattr;
|
|
}
|
|
|
|
/* Returns a complete curses attribute. Used to possibly bold/underline/etc HP/Pw. */
|
|
static attr_t
|
|
hpen_color_attr(boolean is_hp, int cur, int max)
|
|
{
|
|
struct color_option stat_color;
|
|
int count;
|
|
attr_t attr = 0;
|
|
if (!iflags.use_status_colors)
|
|
return curses_color_attr(CLR_GRAY, 0);
|
|
|
|
stat_color = percentage_color_of(cur, max, is_hp ? hp_colors : pw_colors);
|
|
|
|
if (stat_color.color != NO_COLOR)
|
|
attr |= curses_color_attr(stat_color.color, 0);
|
|
|
|
for (count = 0; (1 << count) <= stat_color.attr_bits; count++) {
|
|
if (count != ATR_NONE && (stat_color.attr_bits & (1 << count)))
|
|
attr |= curses_convert_attr(count);
|
|
}
|
|
|
|
return attr;
|
|
}
|
|
|
|
/* Return color for the HP bar.
|
|
With status colors ON, this respect its configuration (defaulting to gray), but
|
|
only obeys the color (no weird attributes for the HP bar).
|
|
With status colors OFF, this returns reasonable defaults which are also used
|
|
for the HP/Pw text itself. */
|
|
static int
|
|
hpen_color(boolean is_hp, int cur, int max)
|
|
{
|
|
if (iflags.use_status_colors) {
|
|
struct color_option stat_color;
|
|
stat_color = percentage_color_of(cur, max, is_hp ? hp_colors : pw_colors);
|
|
|
|
if (stat_color.color == NO_COLOR)
|
|
return CLR_GRAY;
|
|
else
|
|
return stat_color.color;
|
|
} else
|
|
return CLR_GRAY;
|
|
|
|
int color = CLR_GRAY;
|
|
if (cur == max)
|
|
color = CLR_GRAY;
|
|
else if (cur * 3 > max * 2) /* >2/3 */
|
|
color = is_hp ? CLR_GREEN : CLR_CYAN;
|
|
else if (cur * 3 > max) /* >1/3 */
|
|
color = is_hp ? CLR_YELLOW : CLR_BLUE;
|
|
else if (cur * 7 > max) /* >1/7 */
|
|
color = is_hp ? CLR_RED : CLR_MAGENTA;
|
|
else
|
|
color = is_hp ? CLR_ORANGE : CLR_BRIGHT_MAGENTA;
|
|
|
|
return color;
|
|
}
|
|
|
|
/* Draws a bar
|
|
is_hp: TRUE if we're drawing HP, Pw otherwise (determines colors)
|
|
cur/max: Current/max HP/Pw
|
|
title: Not NULL if we are drawing as part of an existing title.
|
|
Otherwise, the format is as follows: [ 11 / 11 ] */
|
|
static void
|
|
draw_bar(boolean is_hp, int cur, int max, const char *title)
|
|
{
|
|
WINDOW *win = curses_get_nhwin(STATUS_WIN);
|
|
|
|
char buf[BUFSZ];
|
|
if (title)
|
|
Strncpy(buf, title, BUFSZ);
|
|
else {
|
|
int len = 5;
|
|
snprintf(buf, BUFSZ, "%*d / %-*d", len, cur, len, max);
|
|
if (!iflags.hitpointbar) {
|
|
attr_t attr = hpen_color_attr(is_hp, cur, max);
|
|
wattron(win, attr);
|
|
wprintw(win, "%s", buf);
|
|
wattroff(win, attr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!iflags.hitpointbar) {
|
|
wprintw(win, "%s", buf);
|
|
return;
|
|
}
|
|
|
|
/* Colors */
|
|
attr_t fillattr, attr;
|
|
int color = hpen_color(is_hp, cur, max);
|
|
int invcolor = color & 7;
|
|
|
|
fillattr = curses_color_attr(color, invcolor);
|
|
attr = curses_color_attr(color, 0);
|
|
|
|
/* Figure out how much of the bar to fill */
|
|
int fill = 0;
|
|
int len = strlen(buf);
|
|
if (cur > 0 && max > 0)
|
|
fill = len * cur / max;
|
|
if (fill > len)
|
|
fill = len;
|
|
|
|
waddch(win, '[');
|
|
wattron(win, fillattr);
|
|
wprintw(win, "%.*s", fill, buf);
|
|
wattroff(win, fillattr);
|
|
wattron(win, attr);
|
|
wprintw(win, "%.*s", len - fill, &buf[fill]);
|
|
wattroff(win, attr);
|
|
waddch(win, ']');
|
|
}
|
|
|
|
/* Update the status win - this is called when NetHack would normally
|
|
write to the status window, so we know somwthing has changed. We
|
|
override the write and update what needs to be updated ourselves. */
|
|
void
|
|
curses_update_stats(void)
|
|
{
|
|
WINDOW *win = curses_get_nhwin(STATUS_WIN);
|
|
|
|
/* Clear the window */
|
|
werase(win);
|
|
|
|
int orient = curses_get_window_orientation(STATUS_WIN);
|
|
|
|
boolean horiz = FALSE;
|
|
if ((orient != ALIGN_RIGHT) && (orient != ALIGN_LEFT))
|
|
horiz = TRUE;
|
|
|
|
boolean border = curses_window_has_border(STATUS_WIN);
|
|
|
|
/* Figure out if we have proper window dimensions for horizontal statusbar. */
|
|
if (horiz) {
|
|
/* correct y */
|
|
int cy = 3;
|
|
if (!iflags.classic_status && iflags.statuslines >= 4)
|
|
cy = 4;
|
|
if (iflags.classic_status && iflags.statuslines <= 2)
|
|
cy = 2;
|
|
|
|
/* actual y (and x) */
|
|
int ax = 0;
|
|
int ay = 0;
|
|
getmaxyx(win, ay, ax);
|
|
if (border)
|
|
ay -= 2;
|
|
|
|
if (cy != ay) {
|
|
curses_create_main_windows();
|
|
curses_last_messages();
|
|
doredraw();
|
|
|
|
/* Reset XP highlight (since classic_status and new show different numbers) */
|
|
prevexp.highlight_turns = 0;
|
|
curses_update_stats();
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Starting x/y. Passed to draw_horizontal/draw_vertical to keep track of
|
|
window positioning. */
|
|
int x = 0;
|
|
int y = 0;
|
|
|
|
/* Don't start at border position if applicable */
|
|
if (border) {
|
|
x++;
|
|
y++;
|
|
}
|
|
|
|
/* Get HP values. */
|
|
int hp = u.uhp;
|
|
int hpmax = u.uhpmax;
|
|
if (Upolyd) {
|
|
hp = u.mh;
|
|
hpmax = u.mhmax;
|
|
}
|
|
|
|
if (orient != ALIGN_RIGHT && orient != ALIGN_LEFT)
|
|
draw_horizontal(x, y, hp, hpmax);
|
|
else
|
|
draw_vertical(x, y, hp, hpmax);
|
|
|
|
if (border)
|
|
box(win, 0, 0);
|
|
|
|
wnoutrefresh(win);
|
|
|
|
if (first) {
|
|
first = FALSE;
|
|
|
|
/* Zero highlight timers. This will call curses_update_status again if needed */
|
|
curses_decrement_highlights(TRUE);
|
|
}
|
|
}
|
|
|
|
static void
|
|
draw_horizontal(int x, int y, int hp, int hpmax)
|
|
{
|
|
if (!iflags.classic_status) {
|
|
/* Draw new-style statusbar */
|
|
draw_horizontal_new(x, y, hp, hpmax);
|
|
return;
|
|
}
|
|
char buf[BUFSZ];
|
|
char rank[BUFSZ];
|
|
WINDOW *win = curses_get_nhwin(STATUS_WIN);
|
|
|
|
/* Line 1 */
|
|
wmove(win, y, x);
|
|
|
|
get_playerrank(rank);
|
|
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
|
snprintf(buf, BUFSZ, "%s the %s", plname, rank);
|
|
#pragma GCC diagnostic pop
|
|
|
|
/* Use the title as HP bar (similar to hitpointbar) */
|
|
draw_bar(TRUE, hp, hpmax, buf);
|
|
|
|
/* Attributes */
|
|
print_statdiff(" St:", &prevstr, ACURR(A_STR), STAT_STR);
|
|
print_statdiff(" Dx:", &prevdex, ACURR(A_DEX), STAT_OTHER);
|
|
print_statdiff(" Co:", &prevcon, ACURR(A_CON), STAT_OTHER);
|
|
print_statdiff(" In:", &prevint, ACURR(A_INT), STAT_OTHER);
|
|
print_statdiff(" Wi:", &prevwis, ACURR(A_WIS), STAT_OTHER);
|
|
print_statdiff(" Ch:", &prevcha, ACURR(A_CHA), STAT_OTHER);
|
|
|
|
wprintw(win, (u.ualign.type == A_CHAOTIC ? " Chaotic" :
|
|
u.ualign.type == A_NEUTRAL ? " Neutral" :
|
|
u.ualign.type == A_VOID ? " Gnostic" :
|
|
u.ualign.type == A_LAWFUL ? " Lawful" :
|
|
u.ualign.type == A_NONE ? " Unaligned" : " Other"));
|
|
|
|
if (flags.showscore)
|
|
print_statdiff(" S:", &prevscore, botl_score(), STAT_OTHER);
|
|
|
|
|
|
/* Line 2 */
|
|
y++;
|
|
wmove(win, y, x);
|
|
|
|
describe_level(buf);
|
|
|
|
wprintw(win, "%s", buf);
|
|
|
|
#ifndef GOLDOBJ
|
|
print_statdiff("$", &prevau, u.ugold, STAT_GOLD);
|
|
#else
|
|
print_statdiff("$", &prevau, money_cnt(invent), STAT_GOLD);
|
|
#endif
|
|
|
|
/* HP/Pw use special coloring rules */
|
|
attr_t hpattr, pwattr;
|
|
hpattr = hpen_color_attr(TRUE, hp, hpmax);
|
|
pwattr = hpen_color_attr(FALSE, u.uen, u.uenmax);
|
|
wprintw(win, " HP:");
|
|
wattron(win, hpattr);
|
|
wprintw(win, "%d(%d)", hp, hpmax);
|
|
wattroff(win, hpattr);
|
|
|
|
wprintw(win, " Pw:");
|
|
wattron(win, pwattr);
|
|
wprintw(win, "%d(%d)", u.uen, u.uenmax);
|
|
wattroff(win, pwattr);
|
|
|
|
print_statdiff(" Br:", &prevdive, u.divetimer, STAT_OTHER);
|
|
print_statdiff(" AC:", &prevac, (u.uac + u.ustdy), STAT_AC);
|
|
print_statdiff(" DR:", &prevdr, u.udr, STAT_OTHER);
|
|
|
|
if (Upolyd)
|
|
print_statdiff(" HD:", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER);
|
|
else if (flags.showexp) {
|
|
print_statdiff(" Xp:", &prevlevel, u.ulevel, STAT_OTHER);
|
|
/* use waddch, we don't want to highlight the '/' */
|
|
waddch(win, '/');
|
|
print_statdiff("", &prevexp, u.uexp, STAT_OTHER);
|
|
}
|
|
else
|
|
print_statdiff(" Exp:", &prevlevel, u.ulevel, STAT_OTHER);
|
|
|
|
if (flags.time)
|
|
print_statdiff(" T:", &prevtime, moves, STAT_TIME);
|
|
if (wizard && flags.movetoprint) {
|
|
int i, tmp;
|
|
*buf='\0';
|
|
for (i=0, tmp = flags.movetoprint; tmp>=1<<i; i++)
|
|
switch(tmp & (1<<i)) {
|
|
case MOVE_STANDARD: Strcat(buf, "standard/"); break;
|
|
case MOVE_INSTANT: Strcat(buf, "instant/"); break;
|
|
case MOVE_PARTIAL: Strcat(buf, "bonus/"); break;
|
|
case MOVE_ATTACKED: Strcat(buf, "attacked/"); break;
|
|
case MOVE_MOVED: Strcat(buf, "moved/"); break;
|
|
case MOVE_QUAFFED: Strcat(buf, "quaffed/"); break;
|
|
case MOVE_ZAPPED: Strcat(buf, "zapped/"); break;
|
|
case MOVE_READ: Strcat(buf, "read/"); break;
|
|
case MOVE_CASTSPELL: Strcat(buf, "cast/"); break;
|
|
case MOVE_ATE: Strcat(buf, "ate/"); break;
|
|
case MOVE_FIRED: Strcat(buf, "fired/"); break;
|
|
}
|
|
if (strlen(buf) > 0) Strcpy(eos(buf)-1, "");
|
|
wprintw(win, " [%d:%s]", flags.movetoprintcost, buf);
|
|
}
|
|
if (iflags.showrealtime) {
|
|
time_t currenttime = get_realtime();
|
|
wprintw(win, " %ld:%2.2ld", currenttime / 3600,
|
|
(currenttime % 3600) / 60);
|
|
}
|
|
if (iflags.statuslines >= 3) {
|
|
y++;
|
|
wmove(win, y, x);
|
|
}
|
|
curses_add_statuses(win, FALSE, FALSE, NULL, NULL, iflags.statuslines >= 3);
|
|
}
|
|
|
|
static void
|
|
draw_horizontal_new(int x, int y, int hp, int hpmax)
|
|
{
|
|
char buf[BUFSZ];
|
|
char rank[BUFSZ];
|
|
WINDOW *win = curses_get_nhwin(STATUS_WIN);
|
|
|
|
/* Line 1 */
|
|
wmove(win, y, x);
|
|
|
|
get_playerrank(rank);
|
|
char race[BUFSZ];
|
|
Strncpy(race, urace.adj, BUFSZ);
|
|
race[0] = highc(race[0]);
|
|
for (int i = 0; race[i]; i++)
|
|
if (race[i] == ' ' && race[i+1])
|
|
race[i+1] = highc(race[i+1]);
|
|
wprintw(win, "%s the %s %s%s%s", plname,
|
|
(u.ualign.type == A_CHAOTIC ? "Chaotic" :
|
|
u.ualign.type == A_NEUTRAL ? "Neutral" :
|
|
u.ualign.type == A_VOID ? "Gnostic" :
|
|
u.ualign.type == A_LAWFUL ? "Lawful" :
|
|
u.ualign.type == A_NONE ? "Unaligned" : "Other"),
|
|
Upolyd ? "" : race, Upolyd ? "" : " ",
|
|
rank);
|
|
|
|
/* Line 2 */
|
|
y++;
|
|
wmove(win, y, x);
|
|
wprintw(win, "HP:");
|
|
draw_bar(TRUE, hp, hpmax, NULL);
|
|
print_statdiff(" AC:", &prevac, (u.uac + u.ustdy), STAT_AC);
|
|
print_statdiff(" DR:", &prevdr, u.udr, STAT_OTHER);
|
|
if (Upolyd)
|
|
print_statdiff(" HD:", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER);
|
|
else if (flags.showexp) {
|
|
/* Ensure that Xp have proper highlight on level change. */
|
|
int levelchange = 0;
|
|
if (prevlevel.value != u.ulevel) {
|
|
if (prevlevel.value < u.ulevel)
|
|
levelchange = 1;
|
|
else
|
|
levelchange = 2;
|
|
}
|
|
print_statdiff(" Xp:", &prevlevel, u.ulevel, STAT_OTHER);
|
|
/* use waddch, we don't want to highlight the '/' */
|
|
waddch(win, '(');
|
|
|
|
/* Figure out amount of Xp needed to next level */
|
|
int xp_left = 0;
|
|
if (u.ulevel < 30)
|
|
xp_left = (newuexp(u.ulevel) - u.uexp);
|
|
|
|
if (levelchange) {
|
|
prevexp.value = (xp_left + 1);
|
|
if (levelchange == 2)
|
|
prevexp.value = (xp_left - 1);
|
|
}
|
|
print_statdiff("", &prevexp, xp_left, STAT_AC);
|
|
waddch(win, ')');
|
|
}
|
|
else
|
|
print_statdiff(" Exp:", &prevlevel, u.ulevel, STAT_OTHER);
|
|
|
|
waddch(win, ' ');
|
|
describe_level(buf);
|
|
|
|
wprintw(win, "%s", buf);
|
|
|
|
/* Line 3 */
|
|
y++;
|
|
wmove(win, y, x);
|
|
wprintw(win, "Pw:");
|
|
draw_bar(FALSE, u.uen, u.uenmax, NULL);
|
|
|
|
print_statdiff(" Br:", &prevdive, u.divetimer, STAT_OTHER);
|
|
|
|
#ifndef GOLDOBJ
|
|
print_statdiff(" $", &prevau, u.ugold, STAT_GOLD);
|
|
#else
|
|
print_statdiff(" $", &prevau, money_cnt(invent), STAT_GOLD);
|
|
#endif
|
|
|
|
if (flags.showscore)
|
|
print_statdiff(" S:", &prevscore, botl_score(), STAT_OTHER);
|
|
|
|
if (flags.time)
|
|
print_statdiff(" T:", &prevtime, moves, STAT_TIME);
|
|
if (wizard && flags.movetoprint) {
|
|
int i, tmp;
|
|
*buf='\0';
|
|
for (i=0, tmp = flags.movetoprint; tmp>=1<<i; i++)
|
|
switch(tmp & (1<<i)) {
|
|
case MOVE_STANDARD: Strcat(buf, "standard/"); break;
|
|
case MOVE_INSTANT: Strcat(buf, "instant/"); break;
|
|
case MOVE_PARTIAL: Strcat(buf, "bonus/"); break;
|
|
case MOVE_ATTACKED: Strcat(buf, "attacked/"); break;
|
|
case MOVE_MOVED: Strcat(buf, "moved/"); break;
|
|
case MOVE_QUAFFED: Strcat(buf, "quaffed/"); break;
|
|
case MOVE_ZAPPED: Strcat(buf, "zapped/"); break;
|
|
case MOVE_READ: Strcat(buf, "read/"); break;
|
|
case MOVE_CASTSPELL: Strcat(buf, "cast/"); break;
|
|
case MOVE_ATE: Strcat(buf, "ate/"); break;
|
|
case MOVE_FIRED: Strcat(buf, "fired/"); break;
|
|
}
|
|
if (strlen(buf) > 0) Strcpy(eos(buf)-1, "");
|
|
wprintw(win, " [%d:%s]", flags.movetoprintcost, buf);
|
|
}
|
|
if (iflags.showrealtime) {
|
|
time_t currenttime = get_realtime();
|
|
wprintw(win, " %ld:%2.2ld", currenttime / 3600,
|
|
(currenttime % 3600) / 60);
|
|
}
|
|
if (iflags.statuslines >= 4) {
|
|
y++;
|
|
wmove(win, y, x);
|
|
}
|
|
curses_add_statuses(win, TRUE, FALSE, &x, &y, iflags.statuslines >= 4);
|
|
|
|
/* Right-aligned attributes */
|
|
int stat_length = 6; /* " Dx:xx" */
|
|
int str_length = 6;
|
|
if (ACURR(A_STR) > 18 && ACURR(A_STR) < 119)
|
|
str_length = 9;
|
|
|
|
getmaxyx(win, y, x);
|
|
|
|
/* We want to deal with top line of y. getmaxx would do what we want, but it only
|
|
exist for compatibility reasons and might not exist at all in some versions. */
|
|
y = 0;
|
|
if (curses_window_has_border(STATUS_WIN)) {
|
|
x--;
|
|
y++;
|
|
}
|
|
|
|
x -= stat_length;
|
|
int orig_x = x;
|
|
wmove(win, y, x);
|
|
print_statdiff(" Co:", &prevcon, ACURR(A_CON), STAT_OTHER);
|
|
x -= stat_length;
|
|
wmove(win, y, x);
|
|
print_statdiff(" Dx:", &prevdex, ACURR(A_DEX), STAT_OTHER);
|
|
x -= str_length;
|
|
wmove(win, y, x);
|
|
print_statdiff(" St:", &prevstr, ACURR(A_STR), STAT_STR);
|
|
|
|
x = orig_x;
|
|
y++;
|
|
wmove(win, y, x);
|
|
print_statdiff(" Ch:", &prevcha, ACURR(A_CHA), STAT_OTHER);
|
|
x -= stat_length;
|
|
wmove(win, y, x);
|
|
print_statdiff(" Wi:", &prevwis, ACURR(A_WIS), STAT_OTHER);
|
|
x -= str_length;
|
|
wmove(win, y, x);
|
|
print_statdiff(" In:", &prevint, ACURR(A_INT), STAT_OTHER);
|
|
}
|
|
|
|
/* Personally I never understood the point of a vertical status bar. But removing the
|
|
option would be silly, so keep the functionality. */
|
|
static void
|
|
draw_vertical(int x, int y, int hp, int hpmax)
|
|
{
|
|
char buf[BUFSZ];
|
|
char rank[BUFSZ];
|
|
WINDOW *win = curses_get_nhwin(STATUS_WIN);
|
|
|
|
/* Print title and dungeon branch */
|
|
wmove(win, y++, x);
|
|
|
|
get_playerrank(rank);
|
|
int ranklen = strlen(rank);
|
|
int namelen = strlen(plname);
|
|
int maxlen = 19;
|
|
if (!iflags.hitpointbar)
|
|
maxlen += 2; /* With no hitpointbar, we can fit more since there's no "[]" */
|
|
|
|
if ((ranklen + namelen) > maxlen) {
|
|
/* The result doesn't fit. Strip name if >10 characters, then strip title */
|
|
if (namelen > 10) {
|
|
while (namelen > 10 && (ranklen + namelen) > maxlen)
|
|
namelen--;
|
|
}
|
|
|
|
while ((ranklen + namelen) > maxlen)
|
|
ranklen--; /* Still doesn't fit, strip rank */
|
|
}
|
|
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
|
snprintf(buf, BUFSZ, "%-*s the %-*s", namelen, plname, ranklen, rank);
|
|
#pragma GCC diagnostic pop
|
|
draw_bar(TRUE, hp, hpmax, buf);
|
|
wmove(win, y++, x);
|
|
wprintw(win, "%s", dungeons[u.uz.dnum].dname);
|
|
|
|
y++; /* Blank line inbetween */
|
|
wmove(win, y++, x);
|
|
|
|
/* Attributes. Old vertical order is preserved */
|
|
print_statdiff("Strength: ", &prevstr, ACURR(A_STR), STAT_STR);
|
|
wmove(win, y++, x);
|
|
print_statdiff("Intelligence: ", &prevint, ACURR(A_INT), STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
print_statdiff("Wisdom: ", &prevwis, ACURR(A_WIS), STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
print_statdiff("Dexterity: ", &prevdex, ACURR(A_DEX), STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
print_statdiff("Constitution: ", &prevcon, ACURR(A_CON), STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
print_statdiff("Charisma: ", &prevcha, ACURR(A_CHA), STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
wprintw(win, "Alignment: ");
|
|
wprintw(win, (u.ualign.type == A_CHAOTIC ? "Chaotic" :
|
|
u.ualign.type == A_NEUTRAL ? "Neutral" :
|
|
u.ualign.type == A_VOID ? "Gnostic" :
|
|
u.ualign.type == A_LAWFUL ? "Lawful" :
|
|
u.ualign.type == A_NONE ? "Unaligned" : "Other"));
|
|
wmove(win, y++, x);
|
|
wprintw(win, "Dungeon Level: ");
|
|
|
|
/* Astral Plane doesn't fit */
|
|
if (In_endgame(&u.uz))
|
|
wprintw(win, "%s", Is_astralevel(&u.uz) ? "Astral" : "End Game");
|
|
else
|
|
wprintw(win, "%d", depth(&u.uz));
|
|
wmove(win, y++, x);
|
|
|
|
#ifndef GOLDOBJ
|
|
print_statdiff("Gold: ", &prevau, u.ugold, STAT_GOLD);
|
|
#else
|
|
print_statdiff("Gold: ", &prevau, money_cnt(invent), STAT_GOLD);
|
|
#endif
|
|
wmove(win, y++, x);
|
|
|
|
/* HP/Pw use special coloring rules */
|
|
attr_t hpattr, pwattr;
|
|
hpattr = hpen_color_attr(TRUE, hp, hpmax);
|
|
pwattr = hpen_color_attr(FALSE, u.uen, u.uenmax);
|
|
|
|
wprintw(win, "Hit Points: ");
|
|
wattron(win, hpattr);
|
|
wprintw(win, "%d/%d", hp, hpmax);
|
|
wattroff(win, hpattr);
|
|
wmove(win, y++, x);
|
|
|
|
wprintw(win, "Magic Power: ");
|
|
wattron(win, pwattr);
|
|
wprintw(win, "%d/%d", u.uen, u.uenmax);
|
|
wattroff(win, pwattr);
|
|
wmove(win, y++, x);
|
|
|
|
print_statdiff("Breath: ", &prevdive, u.divetimer, STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
print_statdiff("Armor Class: ", &prevac, (u.uac + u.ustdy), STAT_AC);
|
|
wmove(win, y++, x);
|
|
print_statdiff("Damage Resist: ", &prevdr, u.udr, STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
|
|
if (Upolyd)
|
|
print_statdiff("Hit Dice: ", &prevlevel, mons[u.umonnum].mlevel, STAT_OTHER);
|
|
else if (flags.showexp) {
|
|
print_statdiff("Experience: ", &prevlevel, u.ulevel, STAT_OTHER);
|
|
/* use waddch, we don't want to highlight the '/' */
|
|
waddch(win, '/');
|
|
print_statdiff("", &prevexp, u.uexp, STAT_OTHER);
|
|
}
|
|
else
|
|
print_statdiff("Level: ", &prevlevel, u.ulevel, STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
|
|
if (flags.time) {
|
|
print_statdiff("Time: ", &prevtime, moves, STAT_TIME);
|
|
wmove(win, y++, x);
|
|
}
|
|
|
|
if (iflags.showrealtime) {
|
|
time_t currenttime = get_realtime();
|
|
wprintw(win, "Realtime: %ld:%2.2ld", currenttime / 3600,
|
|
(currenttime % 3600) / 60);
|
|
wmove(win, y++, x);
|
|
}
|
|
|
|
if (flags.showscore) {
|
|
print_statdiff("Score: ", &prevscore, botl_score(), STAT_OTHER);
|
|
wmove(win, y++, x);
|
|
}
|
|
|
|
curses_add_statuses(win, FALSE, TRUE, &x, &y, FALSE);
|
|
}
|
|
|
|
static void
|
|
curses_add_statuses(WINDOW *win, boolean align_right,
|
|
boolean vertical, int *x, int *y, boolean first)
|
|
{
|
|
if (align_right) {
|
|
/* Right-aligned statuses. Since add_status decrease one x more
|
|
(to separate them with spaces), add 1 to x unless we have borders
|
|
(which would offset what add_status does) */
|
|
int mx = *x;
|
|
int my = *y;
|
|
getmaxyx(win, my, mx);
|
|
if (!curses_window_has_border(STATUS_WIN))
|
|
mx++;
|
|
|
|
*x = mx;
|
|
}
|
|
|
|
/* Find out how much abbreviation is required to make it fit, if not using
|
|
vertical status. do_statuseffects (used by tty and dumplogs) can print to
|
|
a buffer without affecting the terminal unlike curses statusline code, so
|
|
call that to find the length. */
|
|
int abbrev = 0;
|
|
if (!vertical) {
|
|
int cx, cy, mx, my;
|
|
getyx(win, cy, cx);
|
|
getmaxyx(win, my, mx);
|
|
char buf[MAXCO];
|
|
for (;;) {
|
|
buf[0] = '\0'; /* so eos() works */
|
|
do_statuseffects(buf, FALSE, abbrev, first ? 3 : 2);
|
|
if (abbrev >= 2 || strlen(buf) < mx - cx)
|
|
break;
|
|
abbrev++;
|
|
}
|
|
}
|
|
|
|
#define status_effect(str1, str2, str3, duration) \
|
|
(curses_add_status(win, align_right, vertical, x, y, str1, \
|
|
abbrev == 2 ? (str3) : abbrev == 1 ? (str2) : (str1), \
|
|
duration, first), first = FALSE)
|
|
long long mask = get_status_mask();
|
|
for (int i = 0; i < SIZE(status_effects); i++) {
|
|
struct status_effect status = status_effects[i];
|
|
if (mask & status.mask & iflags.statuseffects)
|
|
status_effect(status.name, status.abbrev1, status.abbrev2, get_status_duration(status.mask));
|
|
/* Add hunger and encumbrance after foodpois */
|
|
if (status.mask == BL_MASK_FOODPOIS) {
|
|
if (u.uhs != NOT_HUNGRY) {
|
|
if (uclockwork)
|
|
status_effect(ca_hu_stat[u.uhs], ca_hu_stat[u.uhs], ca_hu_stat[u.uhs], 0);
|
|
else
|
|
status_effect(hu_stat[u.uhs], hu_stat[u.uhs], hu_stat[u.uhs], 0);
|
|
}
|
|
int enc = near_capacity();
|
|
if (enc > UNENCUMBERED)
|
|
status_effect(enc_stat[enc], enc_stat_abbrev1[enc], enc_stat_abbrev2[enc], 0);
|
|
}
|
|
}
|
|
#undef status_effect
|
|
|
|
}
|
|
|
|
static void
|
|
curses_add_status(WINDOW *win, boolean align_right, boolean vertical,
|
|
int *x, int *y, const char *hilite, const char *str,
|
|
long duration, boolean first)
|
|
{
|
|
/* If vertical is TRUE here with no x/y, that's an error. But handle
|
|
it gracefully since NH3 doesn't recover well in crashes. */
|
|
if (!x || !y)
|
|
vertical = FALSE;
|
|
|
|
if (!vertical && !align_right && !first)
|
|
waddch(win, ' ');
|
|
|
|
char buf[BUFSZ];
|
|
if (duration & TIMEOUT)
|
|
Snprintf(buf, BUFSZ, "%s:%ld", str, duration);
|
|
else
|
|
Strncpy(buf, str, BUFSZ);
|
|
|
|
if (align_right) {
|
|
*x -= (strlen(buf) + 1); /* add spacing */
|
|
wmove(win, *y, *x);
|
|
}
|
|
|
|
draw_trouble_str(hilite, buf);
|
|
|
|
if (vertical) {
|
|
wmove(win, *y, *x);
|
|
*y += 1; /* ++ advances the pointer addr */
|
|
}
|
|
}
|
|
|
|
/* Decrement a single highlight, return 1 if decremented to zero. zero is TRUE if we're
|
|
zeroing the highlight. */
|
|
static int
|
|
decrement_highlight(nhstat *stat, boolean zero)
|
|
{
|
|
if (stat->highlight_turns > 0) {
|
|
if (zero) {
|
|
stat->highlight_turns = 0;
|
|
return 1;
|
|
}
|
|
|
|
stat->highlight_turns--;
|
|
if (stat->highlight_turns == 0)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Decrement the highlight_turns for all stats. Call curses_update_stats
|
|
if needed to unhighlight a stat */
|
|
void
|
|
curses_decrement_highlights(boolean zero)
|
|
{
|
|
int unhighlight = 0;
|
|
|
|
unhighlight |= decrement_highlight(&prevdepth, zero);
|
|
unhighlight |= decrement_highlight(&prevstr, zero);
|
|
unhighlight |= decrement_highlight(&prevdex, zero);
|
|
unhighlight |= decrement_highlight(&prevcon, zero);
|
|
unhighlight |= decrement_highlight(&prevint, zero);
|
|
unhighlight |= decrement_highlight(&prevwis, zero);
|
|
unhighlight |= decrement_highlight(&prevcha, zero);
|
|
unhighlight |= decrement_highlight(&prevau, zero);
|
|
unhighlight |= decrement_highlight(&prevlevel, zero);
|
|
unhighlight |= decrement_highlight(&prevdive, zero);
|
|
unhighlight |= decrement_highlight(&prevac, zero);
|
|
unhighlight |= decrement_highlight(&prevdr, zero);
|
|
unhighlight |= decrement_highlight(&prevexp, zero);
|
|
unhighlight |= decrement_highlight(&prevtime, zero);
|
|
unhighlight |= decrement_highlight(&prevscore, zero);
|
|
|
|
if (unhighlight)
|
|
curses_update_stats();
|
|
}
|