author Ray Chason <ray.chason@protonmail.com> 1684372172 -0400 committer nhmall <nhmall@nethack.org> 1685414340 -0400 Add configuration to support Curses on WinGUI and enable support for Unicode on Curses. Allow NetHackW to select the Curses interface Reorder drawing of extended command prompt Curses on WinGUI needs this change. This may be a bug in PDCursesMod, but it seems to be harmless to the other ports. Avoid calling Curses after the windows are closed Provide erase_char and kill_char for WinGUI Curses Set Lua version to 5.4.6
915 lines
25 KiB
C
915 lines
25 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" -*-*/
|
|
/* NetHack 3.7 curswins.c */
|
|
/* Copyright (c) Karl Garrison, 2010. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#if defined(CURSES_UNICODE) && !defined(_XOPEN_SOURCE_EXTENDED)
|
|
#define _XOPEN_SOURCE_EXTENDED 1
|
|
#endif
|
|
#include "curses.h"
|
|
#include "hack.h"
|
|
#include "wincurs.h"
|
|
#include "curswins.h"
|
|
|
|
/* Window handling for curses interface */
|
|
|
|
/* Private declarations */
|
|
|
|
typedef struct nhw {
|
|
winid nhwin; /* NetHack window id */
|
|
WINDOW *curwin; /* Curses window pointer */
|
|
int width; /* Usable width not counting border */
|
|
int height; /* Usable height not counting border */
|
|
int x; /* start of window on terminal (left) */
|
|
int y; /* start of window on termial (top) */
|
|
int orientation; /* Placement of window relative to map */
|
|
boolean border; /* Whether window has a visible border */
|
|
} nethack_window;
|
|
|
|
typedef struct nhwd {
|
|
winid nhwid; /* NetHack window id */
|
|
struct nhwd *prev_wid; /* Pointer to previous entry */
|
|
struct nhwd *next_wid; /* Pointer to next entry */
|
|
} nethack_wid;
|
|
|
|
typedef struct nhchar {
|
|
int ch; /* character */
|
|
int color; /* color info for character */
|
|
int framecolor; /* background color info for character */
|
|
int attr; /* attributes of character */
|
|
struct unicode_representation *unicode_representation;
|
|
} nethack_char;
|
|
|
|
static boolean map_clipped; /* Map window smaller than 80x21 */
|
|
static nethack_window nhwins[NHWIN_MAX]; /* NetHack window array */
|
|
static nethack_char map[ROWNO][COLNO]; /* Map window contents */
|
|
static nethack_wid *nhwids = NULL; /* NetHack wid array */
|
|
|
|
static boolean is_main_window(winid wid);
|
|
static void write_char(WINDOW * win, int x, int y, nethack_char ch);
|
|
static void clear_map(void);
|
|
|
|
/* Create a window with the specified size and orientation */
|
|
|
|
WINDOW *
|
|
curses_create_window(int width, int height, orient orientation)
|
|
{
|
|
int mapx = 0, mapy = 0, maph = 0, mapw = 0;
|
|
int startx = 0;
|
|
int starty = 0;
|
|
WINDOW *win;
|
|
boolean map_border = FALSE;
|
|
int mapb_offset = 0;
|
|
|
|
if ((orientation == UP) || (orientation == DOWN) ||
|
|
(orientation == LEFT) || (orientation == RIGHT)) {
|
|
if (gi.invent || (gm.moves > 1)) {
|
|
map_border = curses_window_has_border(MAP_WIN);
|
|
curses_get_window_xy(MAP_WIN, &mapx, &mapy);
|
|
curses_get_window_size(MAP_WIN, &maph, &mapw);
|
|
} else {
|
|
map_border = TRUE;
|
|
mapx = 0;
|
|
mapy = 0;
|
|
maph = term_rows;
|
|
mapw = term_cols;
|
|
}
|
|
}
|
|
|
|
if (map_border) {
|
|
mapb_offset = 1;
|
|
}
|
|
|
|
width += 2; /* leave room for bounding box */
|
|
height += 2;
|
|
|
|
if ((width > term_cols) || (height > term_rows)) {
|
|
impossible(
|
|
"curses_create_window: Terminal too small for dialog window");
|
|
width = term_cols;
|
|
height = term_rows;
|
|
}
|
|
switch (orientation) {
|
|
default:
|
|
impossible("curses_create_window: Bad orientation");
|
|
/*FALLTHRU*/
|
|
case CENTER:
|
|
startx = (term_cols / 2) - (width / 2);
|
|
starty = (term_rows / 2) - (height / 2);
|
|
break;
|
|
case UP:
|
|
if (gi.invent || (gm.moves > 1)) {
|
|
startx = (mapw / 2) - (width / 2) + mapx + mapb_offset;
|
|
} else {
|
|
startx = 0;
|
|
}
|
|
|
|
starty = mapy + mapb_offset;
|
|
break;
|
|
case DOWN:
|
|
if (gi.invent || (gm.moves > 1)) {
|
|
startx = (mapw / 2) - (width / 2) + mapx + mapb_offset;
|
|
} else {
|
|
startx = 0;
|
|
}
|
|
|
|
starty = height - mapy - 1 - mapb_offset;
|
|
break;
|
|
case LEFT:
|
|
if (map_border && (width < term_cols))
|
|
startx = 1;
|
|
else
|
|
startx = 0;
|
|
starty = term_rows - height;
|
|
break;
|
|
case RIGHT:
|
|
if (gi.invent || (gm.moves > 1)) {
|
|
startx = (mapw + mapx + (mapb_offset * 2)) - width;
|
|
} else {
|
|
startx = term_cols - width;
|
|
}
|
|
|
|
starty = 0;
|
|
break;
|
|
}
|
|
|
|
if (startx < 0) {
|
|
startx = 0;
|
|
}
|
|
|
|
if (starty < 0) {
|
|
starty = 0;
|
|
}
|
|
|
|
win = newwin(height, width, starty, startx);
|
|
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, ON);
|
|
box(win, 0, 0);
|
|
curses_toggle_color_attr(win, DIALOG_BORDER_COLOR, NONE, OFF);
|
|
return win;
|
|
}
|
|
|
|
|
|
/* Erase and delete curses window, and refresh standard windows */
|
|
|
|
void
|
|
curses_destroy_win(WINDOW *win)
|
|
{
|
|
werase(win);
|
|
wrefresh(win);
|
|
delwin(win);
|
|
if (win == activemenu)
|
|
activemenu = NULL;
|
|
curses_refresh_nethack_windows();
|
|
}
|
|
|
|
|
|
/* Refresh nethack windows if they exist, or base window if not */
|
|
|
|
void
|
|
curses_refresh_nethack_windows(void)
|
|
{
|
|
WINDOW *status_window, *message_window, *map_window, *inv_window;
|
|
|
|
status_window = curses_get_nhwin(STATUS_WIN);
|
|
message_window = curses_get_nhwin(MESSAGE_WIN);
|
|
map_window = curses_get_nhwin(MAP_WIN);
|
|
inv_window = curses_get_nhwin(INV_WIN);
|
|
|
|
if (!iflags.window_inited) {
|
|
return;
|
|
}
|
|
|
|
if ((gm.moves <= 1) && !gi.invent) {
|
|
/* Main windows not yet displayed; refresh base window instead */
|
|
touchwin(stdscr);
|
|
refresh();
|
|
} else {
|
|
if (status_window != NULL) {
|
|
touchwin(status_window);
|
|
wnoutrefresh(status_window);
|
|
}
|
|
if (map_window != NULL) {
|
|
touchwin(map_window);
|
|
wnoutrefresh(map_window);
|
|
}
|
|
if (message_window != NULL) {
|
|
touchwin(message_window);
|
|
wnoutrefresh(message_window);
|
|
}
|
|
if (inv_window) {
|
|
touchwin(inv_window);
|
|
wnoutrefresh(inv_window);
|
|
}
|
|
doupdate();
|
|
}
|
|
}
|
|
|
|
|
|
/* Return curses window pointer for given NetHack winid */
|
|
|
|
WINDOW *
|
|
curses_get_nhwin(winid wid)
|
|
{
|
|
if (!is_main_window(wid)) {
|
|
impossible("curses_get_nhwin: wid %d out of range. Not a main window.",
|
|
wid);
|
|
return NULL;
|
|
}
|
|
|
|
return nhwins[wid].curwin;
|
|
}
|
|
|
|
|
|
/* Add curses window pointer and window info to list for given NetHack winid */
|
|
|
|
void
|
|
curses_add_nhwin(winid wid, int height, int width, int y, int x,
|
|
orient orientation, boolean border)
|
|
{
|
|
WINDOW *win;
|
|
int real_width = width;
|
|
int real_height = height;
|
|
|
|
if (!is_main_window(wid)) {
|
|
impossible("curses_add_nhwin: wid %d out of range. Not a main window.",
|
|
wid);
|
|
return;
|
|
}
|
|
|
|
nhwins[wid].nhwin = wid;
|
|
nhwins[wid].border = border;
|
|
nhwins[wid].width = width;
|
|
nhwins[wid].height = height;
|
|
nhwins[wid].x = x;
|
|
nhwins[wid].y = y;
|
|
nhwins[wid].orientation = orientation;
|
|
|
|
if (border) {
|
|
real_width += 2; /* leave room for bounding box */
|
|
real_height += 2;
|
|
}
|
|
|
|
win = newwin(real_height, real_width, y, x);
|
|
|
|
switch (wid) {
|
|
case MESSAGE_WIN:
|
|
messagewin = win;
|
|
break;
|
|
case STATUS_WIN:
|
|
statuswin = win;
|
|
break;
|
|
case MAP_WIN:
|
|
mapwin = win;
|
|
map_clipped = (width < COLNO || height < ROWNO);
|
|
break;
|
|
}
|
|
|
|
if (border) {
|
|
box(win, 0, 0);
|
|
}
|
|
|
|
nhwins[wid].curwin = win;
|
|
}
|
|
|
|
|
|
/* Add wid to list of known window IDs */
|
|
|
|
void
|
|
curses_add_wid(winid wid)
|
|
{
|
|
nethack_wid *new_wid;
|
|
nethack_wid *widptr = nhwids;
|
|
|
|
new_wid = (nethack_wid *) alloc((unsigned) sizeof (nethack_wid));
|
|
new_wid->nhwid = wid;
|
|
|
|
new_wid->next_wid = NULL;
|
|
|
|
if (widptr == NULL) {
|
|
new_wid->prev_wid = NULL;
|
|
nhwids = new_wid;
|
|
} else {
|
|
while (widptr->next_wid != NULL) {
|
|
widptr = widptr->next_wid;
|
|
}
|
|
new_wid->prev_wid = widptr;
|
|
widptr->next_wid = new_wid;
|
|
}
|
|
}
|
|
|
|
|
|
/* refresh a curses window via given nethack winid */
|
|
|
|
void
|
|
curses_refresh_nhwin(winid wid)
|
|
{
|
|
wnoutrefresh(curses_get_nhwin(wid));
|
|
doupdate();
|
|
}
|
|
|
|
|
|
/* Delete curses window via given NetHack winid and remove entry from list */
|
|
|
|
void
|
|
curses_del_nhwin(winid wid)
|
|
{
|
|
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
|
curses_del_menu(wid, TRUE);
|
|
return;
|
|
} else if (wid == INV_WIN) {
|
|
curses_del_menu(wid, TRUE);
|
|
/* don't return yet */
|
|
}
|
|
|
|
if (!is_main_window(wid)) {
|
|
impossible("curses_del_nhwin: wid %d out of range. Not a main window.",
|
|
wid);
|
|
return;
|
|
}
|
|
if (nhwins[wid].curwin != NULL) {
|
|
delwin(nhwins[wid].curwin);
|
|
nhwins[wid].curwin = NULL;
|
|
}
|
|
nhwins[wid].nhwin = -1;
|
|
}
|
|
|
|
|
|
/* Delete wid from list of known window IDs */
|
|
|
|
void
|
|
curses_del_wid(winid wid)
|
|
{
|
|
nethack_wid *tmpwid;
|
|
nethack_wid *widptr;
|
|
|
|
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
|
curses_del_menu(wid, FALSE);
|
|
}
|
|
|
|
for (widptr = nhwids; widptr; widptr = widptr->next_wid) {
|
|
if (widptr->nhwid == wid) {
|
|
if ((tmpwid = widptr->prev_wid) != NULL) {
|
|
tmpwid->next_wid = widptr->next_wid;
|
|
} else {
|
|
nhwids = widptr->next_wid; /* New head mode, or NULL */
|
|
}
|
|
if ((tmpwid = widptr->next_wid) != NULL) {
|
|
tmpwid->prev_wid = widptr->prev_wid;
|
|
}
|
|
free(widptr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* called by destroy_nhwindows() prior to exit */
|
|
void
|
|
curs_destroy_all_wins(void)
|
|
{
|
|
curses_count_window((char *) 0); /* clean up orphan */
|
|
|
|
while (nhwids)
|
|
curses_del_wid(nhwids->nhwid);
|
|
}
|
|
|
|
/* Print a single character in the given window at the given coordinates */
|
|
|
|
void
|
|
#ifdef ENHANCED_SYMBOLS
|
|
curses_putch(winid wid, int x, int y, int ch,
|
|
struct unicode_representation *unicode_representation,
|
|
int color, int framecolor, int attr)
|
|
#else
|
|
curses_putch(winid wid, int x, int y, int ch, int color, int framecolor, int attr)
|
|
#endif
|
|
{
|
|
static boolean map_initted = FALSE;
|
|
int sx, sy, ex, ey;
|
|
boolean border = curses_window_has_border(wid);
|
|
nethack_char nch;
|
|
/*
|
|
if (wid == STATUS_WIN) {
|
|
curses_update_stats();
|
|
}
|
|
*/
|
|
if (wid != MAP_WIN) {
|
|
return;
|
|
}
|
|
|
|
if (!map_initted) {
|
|
clear_map();
|
|
map_initted = TRUE;
|
|
}
|
|
|
|
--x; /* map column [0] is not used; draw column [1] in first screen col */
|
|
map[y][x].ch = ch;
|
|
map[y][x].color = color;
|
|
map[y][x].framecolor = framecolor;
|
|
map[y][x].attr = attr;
|
|
#ifdef ENHANCED_SYMBOLS
|
|
map[y][x].unicode_representation = unicode_representation;
|
|
#endif
|
|
nch = map[y][x];
|
|
|
|
(void) curses_map_borders(&sx, &sy, &ex, &ey, -1, -1);
|
|
|
|
if ((x >= sx) && (x <= ex) && (y >= sy) && (y <= ey)) {
|
|
if (border) {
|
|
x++;
|
|
y++;
|
|
}
|
|
|
|
write_char(mapwin, x - sx, y - sy, nch);
|
|
}
|
|
/* refresh after every character?
|
|
* Fair go, mate! Some of us are playing from Australia! */
|
|
/* wrefresh(mapwin); */
|
|
}
|
|
|
|
|
|
/* Get x, y coordinates of curses window on the physical terminal window */
|
|
|
|
void
|
|
curses_get_window_xy(winid wid, int *x, int *y)
|
|
{
|
|
if (!is_main_window(wid)) {
|
|
impossible(
|
|
"curses_get_window_xy: wid %d out of range. Not a main window.",
|
|
wid);
|
|
*x = 0;
|
|
*y = 0;
|
|
return;
|
|
}
|
|
|
|
*x = nhwins[wid].x;
|
|
*y = nhwins[wid].y;
|
|
}
|
|
|
|
|
|
/* Get usable width and height curses window on the physical terminal window */
|
|
|
|
void
|
|
curses_get_window_size(winid wid, int *height, int *width)
|
|
{
|
|
*height = nhwins[wid].height;
|
|
*width = nhwins[wid].width;
|
|
}
|
|
|
|
|
|
/* Determine if given window has a visible border */
|
|
|
|
boolean
|
|
curses_window_has_border(winid wid)
|
|
{
|
|
return nhwins[wid].border;
|
|
}
|
|
|
|
|
|
/* Determine if window for given winid exists */
|
|
|
|
boolean
|
|
curses_window_exists(winid wid)
|
|
{
|
|
nethack_wid *widptr;
|
|
|
|
for (widptr = nhwids; widptr; widptr = widptr->next_wid)
|
|
if (widptr->nhwid == wid)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* Return the orientation of the specified window */
|
|
|
|
int
|
|
curses_get_window_orientation(winid wid)
|
|
{
|
|
if (!is_main_window(wid)) {
|
|
impossible(
|
|
"curses_get_window_orientation: wid %d out of range. Not a main window.",
|
|
wid);
|
|
return CENTER;
|
|
}
|
|
|
|
return nhwins[wid].orientation;
|
|
}
|
|
|
|
|
|
/* Output a line of text to specified NetHack window with given coordinates
|
|
and text attributes */
|
|
|
|
void
|
|
curses_puts(winid wid, int attr, const char *text)
|
|
{
|
|
anything Id;
|
|
WINDOW *win = NULL;
|
|
|
|
if (is_main_window(wid)) {
|
|
win = curses_get_nhwin(wid);
|
|
}
|
|
|
|
if (wid == MESSAGE_WIN) {
|
|
/* if a no-history message is being shown, remove it */
|
|
if (counting)
|
|
curses_count_window((char *) 0);
|
|
|
|
curses_message_win_puts(text, FALSE);
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
if (wid == STATUS_WIN) {
|
|
curses_update_stats(); /* We will do the write ourselves */
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (curses_is_menu(wid) || curses_is_text(wid)) {
|
|
if (!curses_menu_exists(wid)) {
|
|
impossible(
|
|
"curses_puts: Attempted write to nonexistent window %d!",
|
|
wid);
|
|
return;
|
|
}
|
|
Id = cg.zeroany;
|
|
curses_add_nhmenu_item(wid, &nul_glyphinfo, &Id, 0, 0,
|
|
attr, text, MENU_ITEMFLAGS_NONE);
|
|
} else {
|
|
waddstr(win, text);
|
|
wnoutrefresh(win);
|
|
}
|
|
}
|
|
|
|
|
|
/* Clear the contents of a window via the given NetHack winid */
|
|
|
|
void
|
|
curses_clear_nhwin(winid wid)
|
|
{
|
|
WINDOW *win = curses_get_nhwin(wid);
|
|
boolean border = curses_window_has_border(wid);
|
|
|
|
if (wid == MAP_WIN) {
|
|
clearok(win, TRUE); /* Redraw entire screen when refreshed */
|
|
clear_map();
|
|
}
|
|
|
|
werase(win);
|
|
|
|
if (border) {
|
|
box(win, 0, 0);
|
|
}
|
|
}
|
|
|
|
/* Change colour of window border to alert player to something */
|
|
void
|
|
curses_alert_win_border(winid wid, boolean onoff)
|
|
{
|
|
WINDOW *win = curses_get_nhwin(wid);
|
|
|
|
if (!win || !curses_window_has_border(wid))
|
|
return;
|
|
if (onoff)
|
|
curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, ON);
|
|
box(win, 0, 0);
|
|
if (onoff)
|
|
curses_toggle_color_attr(win, ALERT_BORDER_COLOR, NONE, OFF);
|
|
wnoutrefresh(win);
|
|
}
|
|
|
|
|
|
void
|
|
curses_alert_main_borders(boolean onoff)
|
|
{
|
|
curses_alert_win_border(MAP_WIN, onoff);
|
|
curses_alert_win_border(MESSAGE_WIN, onoff);
|
|
curses_alert_win_border(STATUS_WIN, onoff);
|
|
curses_alert_win_border(INV_WIN, onoff);
|
|
}
|
|
|
|
/* Return true if given wid is a main NetHack window */
|
|
|
|
static boolean
|
|
is_main_window(winid wid)
|
|
{
|
|
if (wid == MESSAGE_WIN || wid == MAP_WIN
|
|
|| wid == STATUS_WIN || wid == INV_WIN)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* Unconditionally write a single character to a window at the given
|
|
coordinates without a refresh. Currently only used for the map. */
|
|
|
|
static int
|
|
get_framecolor(int nhcolor, int framecolor)
|
|
{
|
|
boolean hicolor = (COLORS >= 16), adj_framecolor = framecolor;
|
|
static int framecolors[16][8] = {
|
|
{ 0, 16, 8, 32, 17, 40, 48, 0 }, { 1, 18, 9, 33, 19, 41, 49, 1 },
|
|
{ 2, 20, 10, 34, 21, 42, 50, 2 }, { 3, 22, 11, 35, 23, 43, 51, 3 },
|
|
{ 4, 24, 12, 36, 25, 44, 52, 4 }, { 5, 26, 13, 37, 27, 45, 53, 5 },
|
|
{ 6, 28, 14, 38, 29, 46, 54, 6 }, { 0, 30, 15, 72, 31, 88, 56, 0 },
|
|
{ 0, 30, 15, 39, 31, 47, 55, 0 }, { 1, 18, 9, 73, 19, 89, 57, 121 },
|
|
{ 2, 20, 10, 74, 21, 90, 58, 122 }, { 2, 22, 11, 75, 23, 91, 59, 123 },
|
|
{ 4, 24, 12, 76, 25, 44, 60, 124 }, { 5, 26, 13, 77, 27, 93, 61, 125 },
|
|
{ 6, 28, 14, 78, 29, 94, 62, 126 }, { 0, 30, 15, 79, 31, 95, 63, 127 },
|
|
};
|
|
|
|
if (framecolor < 16 && framecolor >= 8)
|
|
adj_framecolor = framecolor - 8;
|
|
return ((nhcolor < (hicolor ? 16 : 8) && adj_framecolor < 8)
|
|
? framecolors[nhcolor][adj_framecolor]
|
|
: nhcolor);
|
|
}
|
|
|
|
static void
|
|
write_char(WINDOW * win, int x, int y, nethack_char nch)
|
|
{
|
|
int curscolor = nch.color, cursattr = nch.attr;
|
|
|
|
if (nch.framecolor != NO_COLOR) {
|
|
curscolor = get_framecolor(nch.color, nch.framecolor);
|
|
if (nch.attr == A_REVERSE)
|
|
cursattr = A_NORMAL; /* hilited pet looks odd otherwise */
|
|
}
|
|
curses_toggle_color_attr(win, curscolor, cursattr, ON);
|
|
#if defined(CURSES_UNICODE) && defined(ENHANCED_SYMBOLS)
|
|
if ((nch.unicode_representation && nch.unicode_representation->utf8str)
|
|
|| SYMHANDLING(H_IBM)) {
|
|
/* CP437 to Unicode mapping according to the Unicode Consortium */
|
|
static const uint16 cp437[256] = {
|
|
0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
|
0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
|
|
0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
|
|
0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
|
|
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
|
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
|
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
|
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
|
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
|
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
|
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
|
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
|
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
|
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
|
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
|
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
|
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
|
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
|
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
|
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
|
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
|
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
|
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
|
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
|
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
|
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
|
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
|
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
|
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
|
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
|
};
|
|
attr_t attr;
|
|
short pair;
|
|
wchar_t wch[3];
|
|
uint32 utf32ch;
|
|
cchar_t cch;
|
|
|
|
if (SYMHANDLING(H_UTF8)) {
|
|
utf32ch = nch.unicode_representation->utf32ch;
|
|
} else if (SYMHANDLING(H_IBM)) {
|
|
utf32ch = cp437[(uint8)nch.ch];
|
|
} else {
|
|
utf32ch = (uint8)nch.ch;
|
|
}
|
|
if (sizeof(wchar_t) == 2 && utf32ch >= 0x10000) {
|
|
/* UTF-16 surrogate pair */
|
|
wch[0] = (wchar_t)((utf32ch >> 10) + 0xD7C0);
|
|
wch[1] = (wchar_t)((utf32ch & 0x3FF) + 0xDC00);
|
|
wch[2] = L'\0';
|
|
} else {
|
|
wch[0] = (wchar_t)utf32ch;
|
|
wch[1] = L'\0';
|
|
}
|
|
wmove(win, y, x);
|
|
wattr_get(win, &attr, &pair, NULL);
|
|
setcchar(&cch, wch, attr, pair, NULL);
|
|
mvwadd_wch(win, y, x, &cch);
|
|
} else
|
|
#endif
|
|
#ifdef PDCURSES
|
|
mvwaddrawch(win, y, x, nch.ch);
|
|
#else
|
|
mvwaddch(win, y, x, nch.ch);
|
|
#endif
|
|
curses_toggle_color_attr(win, curscolor, cursattr, OFF);
|
|
}
|
|
|
|
|
|
/* Draw the entire visible map onto the screen given the visible map
|
|
boundaries */
|
|
|
|
void
|
|
curses_draw_map(int sx, int sy, int ex, int ey)
|
|
{
|
|
int curx, cury;
|
|
int bspace = 0;
|
|
|
|
#ifdef MAP_SCROLLBARS
|
|
int sbsx, sbsy, sbex, sbey, count;
|
|
nethack_char hsb_back, hsb_bar, vsb_back, vsb_bar;
|
|
#endif
|
|
|
|
if (curses_window_has_border(MAP_WIN)) {
|
|
bspace++;
|
|
}
|
|
#ifdef MAP_SCROLLBARS
|
|
hsb_back.ch = '-';
|
|
hsb_back.color = SCROLLBAR_BACK_COLOR;
|
|
hsb_back.framecolor = NO_COLOR;
|
|
hsb_back.attr = A_NORMAL;
|
|
hsb_back.unicode_representation = NULL;
|
|
hsb_bar.ch = '*';
|
|
hsb_bar.color = SCROLLBAR_COLOR;
|
|
hsb_bar.framecolor = NO_COLOR;
|
|
hsb_bar.attr = A_NORMAL;
|
|
hsb_bar.unicode_representation = NULL;
|
|
vsb_back.ch = '|';
|
|
vsb_back.color = SCROLLBAR_BACK_COLOR;
|
|
vsb_back.framecolor = NO_COLOR;
|
|
vsb_back.attr = A_NORMAL;
|
|
vsb_back.unicode_representation = NULL;
|
|
vsb_bar.ch = '*';
|
|
vsb_bar.color = SCROLLBAR_COLOR;
|
|
vsb_bar.framecolor = NO_COLOR;
|
|
vsb_bar.attr = A_NORMAL;
|
|
vsb_bar.unicode_representation = NULL;
|
|
|
|
/* Horizontal scrollbar */
|
|
if (sx > 0 || ex < (COLNO - 1)) {
|
|
sbsx = (int) (((long) sx * (long) (ex - sx + 1)) / (long) COLNO);
|
|
sbex = (int) (((long) ex * (long) (ex - sx + 1)) / (long) COLNO);
|
|
|
|
if (sx > 0 && sbsx == 0)
|
|
++sbsx;
|
|
if (ex < COLNO - 1 && sbex == COLNO - 1)
|
|
--sbex;
|
|
|
|
for (count = 0; count < sbsx; count++) {
|
|
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_back);
|
|
}
|
|
for (count = sbsx; count <= sbex; count++) {
|
|
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_bar);
|
|
}
|
|
for (count = sbex + 1; count <= (ex - sx); count++) {
|
|
write_char(mapwin, count + bspace, ey - sy + 1 + bspace, hsb_back);
|
|
}
|
|
}
|
|
|
|
/* Vertical scrollbar */
|
|
if (sy > 0 || ey < (ROWNO - 1)) {
|
|
sbsy = (int) (((long) sy * (long) (ey - sy + 1)) / (long) ROWNO);
|
|
sbey = (int) (((long) ey * (long) (ey - sy + 1)) / (long) ROWNO);
|
|
|
|
if (sy > 0 && sbsy == 0)
|
|
++sbsy;
|
|
if (ey < ROWNO - 1 && sbey == ROWNO - 1)
|
|
--sbey;
|
|
|
|
for (count = 0; count < sbsy; count++) {
|
|
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_back);
|
|
}
|
|
for (count = sbsy; count <= sbey; count++) {
|
|
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_bar);
|
|
}
|
|
for (count = sbey + 1; count <= (ey - sy); count++) {
|
|
write_char(mapwin, ex - sx + 1 + bspace, count + bspace, vsb_back);
|
|
}
|
|
}
|
|
#endif /* MAP_SCROLLBARS */
|
|
|
|
for (curx = sx; curx <= ex; curx++) {
|
|
for (cury = sy; cury <= ey; cury++) {
|
|
write_char(mapwin, curx - sx + bspace, cury - sy + bspace,
|
|
map[cury][curx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Init map array to blanks */
|
|
|
|
static void
|
|
clear_map(void)
|
|
{
|
|
int x, y;
|
|
|
|
for (x = 0; x < COLNO; x++) {
|
|
for (y = 0; y < ROWNO; y++) {
|
|
map[y][x].ch = ' ';
|
|
map[y][x].color = NO_COLOR;
|
|
map[y][x].attr = A_NORMAL;
|
|
map[y][x].unicode_representation = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Determine visible boundaries of map, and determine if it needs to be
|
|
based on the location of the player. */
|
|
|
|
boolean
|
|
curses_map_borders(int *sx, int *sy, int *ex, int *ey, int ux, int uy)
|
|
{
|
|
static int width = 0;
|
|
static int height = 0;
|
|
static int osx = 0;
|
|
static int osy = 0;
|
|
static int oex = 0;
|
|
static int oey = 0;
|
|
static int oux = -1;
|
|
static int ouy = -1;
|
|
|
|
if ((oux == -1) || (ouy == -1)) {
|
|
oux = u.ux;
|
|
ouy = u.uy;
|
|
}
|
|
|
|
if (ux == -1) {
|
|
ux = oux;
|
|
} else {
|
|
oux = ux;
|
|
}
|
|
|
|
if (uy == -1) {
|
|
uy = ouy;
|
|
} else {
|
|
ouy = uy;
|
|
}
|
|
|
|
curses_get_window_size(MAP_WIN, &height, &width);
|
|
|
|
#ifdef MAP_SCROLLBARS
|
|
if (width < COLNO) {
|
|
height--; /* room for horizontal scrollbar */
|
|
}
|
|
|
|
if (height < ROWNO) {
|
|
width--; /* room for vertical scrollbar */
|
|
|
|
if (width == COLNO) {
|
|
height--;
|
|
}
|
|
}
|
|
#endif /* MAP_SCROLLBARS */
|
|
|
|
if (width >= COLNO) {
|
|
*sx = 0;
|
|
*ex = COLNO - 1;
|
|
} else {
|
|
*ex = (width / 2) + ux;
|
|
*sx = *ex - (width - 1);
|
|
|
|
if (*ex >= COLNO) {
|
|
*sx = COLNO - width;
|
|
*ex = COLNO - 1;
|
|
} else if (*sx < 0) {
|
|
*sx = 0;
|
|
*ex = width - 1;
|
|
}
|
|
}
|
|
|
|
if (height >= ROWNO) {
|
|
*sy = 0;
|
|
*ey = ROWNO - 1;
|
|
} else {
|
|
*ey = (height / 2) + uy;
|
|
*sy = *ey - (height - 1);
|
|
|
|
if (*ey >= ROWNO) {
|
|
*sy = ROWNO - height;
|
|
*ey = ROWNO - 1;
|
|
} else if (*sy < 0) {
|
|
*sy = 0;
|
|
*ey = height - 1;
|
|
}
|
|
}
|
|
|
|
if ((*sx != osx) || (*sy != osy) || (*ex != oex) || (*ey != oey) ||
|
|
map_clipped) {
|
|
osx = *sx;
|
|
osy = *sy;
|
|
oex = *ex;
|
|
oey = *ey;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|