From 835c4ae0755eae1756a9906e776cd8d34762381c Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 1 Oct 2017 16:51:07 -0700 Subject: [PATCH 01/29] yet another comment typo --- src/potion.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/potion.c b/src/potion.c index aeed5891e..f167bf710 100644 --- a/src/potion.c +++ b/src/potion.c @@ -1243,7 +1243,7 @@ const char *objphrase; /* "Your widget glows" or "Steed's saddle glows" */ return res; } -/* potion obj hits monster mon, which might be youmonst; obj always use up */ +/* potion obj hits monster mon, which might be youmonst; obj always used up */ void potionhit(mon, obj, how) struct monst *mon; From 63953bc062a9754fbc4d01bf7159f4308e95cfd8 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 1 Oct 2017 17:20:30 -0700 Subject: [PATCH 02/29] !STATUS_HILITE warning fixes Mostly declarations of static functions which don't exist (due to being conditionally excluded). One unused local variable. --- src/botl.c | 6 +++++- win/tty/wintty.c | 23 ++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/botl.c b/src/botl.c index 03f2aea11..19ed8c973 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1469930895 2016/07/31 02:08:15 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.75 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1506903619 2017/10/02 00:20:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,7 +12,9 @@ const char *const enc_stat[] = { "", "Burdened", "Stressed", STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */ STATIC_DCL const char *NDECL(rank); +#ifdef STATUS_HILITES STATIC_DCL void NDECL(bot_via_windowport); +#endif static char * get_strength_str() @@ -1060,6 +1062,7 @@ int anytype; return buf; } +#ifdef STATUS_HILITES STATIC_OVL void s_to_anything(a, buf, anytype) anything *a; @@ -1107,6 +1110,7 @@ int anytype; } return; } +#endif /* STATUS_HILITES */ STATIC_OVL int percentage(bl, maxbl) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 2eb84b0cc..aa9ecfa5e 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1463614572 2016/05/18 23:36:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.131 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1506903624 2017/10/02 00:20:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.139 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -3431,25 +3431,25 @@ extern winid WIN_STATUS; static long tty_condition_bits; static int tty_status_colors[MAXBLSTATS]; int hpbar_percent, hpbar_color; -#endif /* STATUS_HILITES */ static int FDECL(condcolor, (long, unsigned long *)); static int FDECL(condattr, (long, unsigned long *)); - +#endif /* STATUS_HILITES */ void tty_status_init() { +#ifdef STATUS_HILITES int i; + for (i = 0; i < MAXBLSTATS; ++i) + tty_status_colors[i] = NO_COLOR; /* no color */ + tty_condition_bits = 0L; + hpbar_percent = 0, hpbar_color = NO_COLOR; +#endif /* STATUS_HILITES */ + /* let genl_status_init do most of the initialization */ genl_status_init(); - -#ifdef STATUS_HILITES - for (i = 0; i < MAXBLSTATS; ++i) { - tty_status_colors[i] = NO_COLOR; /* no color */ - } -#endif /* STATUS_HILITES */ } /* @@ -3581,7 +3581,7 @@ unsigned long *colormasks; int coloridx = NO_COLOR; #endif char *text = (char *) ptr; - static boolean oncearound = FALSE; /* prevent premature partial status display */ + static boolean oncearound = FALSE; /* prevent premature partial display */ enum statusfields fieldorder[2][15] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, @@ -3773,7 +3773,6 @@ int condcolor(bm, bmarray) long bm; unsigned long *bmarray; { -#ifdef STATUS_HILITES int i; if (bm && bmarray) @@ -3781,7 +3780,6 @@ unsigned long *bmarray; if (bmarray[i] && (bm & bmarray[i])) return i; } -#endif return NO_COLOR; } #endif /* TEXTCOLOR */ @@ -3820,7 +3818,6 @@ unsigned long *bmarray; } #endif /* STATUS_HILITES */ - #endif /* TTY_GRAPHICS */ /*wintty.c*/ From 67aaf4ef4c230d68a2ad08c1f87ce08e3ff22e08 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 1 Oct 2017 18:49:47 -0700 Subject: [PATCH 03/29] more !STATUS_HILITES - option parsing When built without STATUS_HILITES, don't treat highlighting options as if they were unknown. This may need some tweaking; the feedback feels a bit intrusive so perhaps 'statushilites' and 'hilite_status' should just be ignored when not available. 'hitpointbar' now relies on wc2 handling instead of being conditionally present. --- src/options.c | 33 ++++++++++++++++++++++----------- win/tty/wintty.c | 16 +++++++++------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/options.c b/src/options.c index d5842bb76..98afaa325 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1505214875 2017/09/12 11:14:35 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.302 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1506908974 2017/10/02 01:49:34 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.309 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -131,9 +131,7 @@ static struct Bool_Opt { { "help", &flags.help, TRUE, SET_IN_GAME }, { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/ { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME }, -#ifdef STATUS_HILITES { "hitpointbar", &iflags.wc2_hitpointbar, FALSE, SET_IN_GAME }, /*WC2*/ -#endif #ifndef MAC { "ignintr", &flags.ignintr, FALSE, SET_IN_GAME }, #else @@ -2172,8 +2170,9 @@ boolean tinitial, tfrom_file; symset[PRIMARY].name = dupstr(op); if (!read_sym_file(PRIMARY)) { clear_symsetentry(PRIMARY, TRUE); - config_error_add("Unable to load symbol set \"%s\" from \"%s\"", - op, SYMBOLS); + config_error_add( + "Unable to load symbol set \"%s\" from \"%s\"", + op, SYMBOLS); return FALSE; } else { switch_symbols(symset[PRIMARY].name != (char *) 0); @@ -2866,7 +2865,8 @@ boolean tinitial, tfrom_file; op++; } if (badopt) { - config_error_add("Unknown %s parameter '%s'", "pickup_types", op); + config_error_add("Unknown %s parameter '%s'", + "pickup_types", op); return FALSE; } } @@ -3351,6 +3351,7 @@ boolean tinitial, tfrom_file; return FALSE; return retval; } + #ifdef WINCHAIN fullname = "windowchain"; if (match_optname(opts, fullname, 3, TRUE)) { @@ -3465,9 +3466,9 @@ boolean tinitial, tfrom_file; return retval; } } -#ifdef STATUS_HILITES /* hilite fields in status prompt */ if (match_optname(opts, "hilite_status", 13, TRUE)) { +#ifdef STATUS_HILITES if (duplicate) complain_about_duplicate(opts, 1); op = string_for_opt(opts, TRUE); @@ -3481,11 +3482,16 @@ boolean tinitial, tfrom_file; if (!parse_status_hl1(op, tfrom_file)) return FALSE; return retval; +#else + config_error_add("'hilite_status' is not supported"); + return FALSE; +#endif } /* control over whether highlights should be displayed, and for how long */ fullname = "statushilites"; if (match_optname(opts, fullname, 9, TRUE)) { +#ifdef STATUS_HILITES if (negated) { iflags.hilite_delta = 0L; } else { @@ -3497,8 +3503,11 @@ boolean tinitial, tfrom_file; if (!tfrom_file) reset_status_hilites(); return retval; - } +#else + config_error_add("'statushilites' is not supported"); + return FALSE; #endif + } #if defined(BACKWARD_COMPAT) fullname = "DECgraphics"; @@ -3673,9 +3682,11 @@ boolean tinitial, tfrom_file; || boolopt[i].addr == &iflags.hilite_pile || boolopt[i].addr == &iflags.hilite_pet) { need_redraw = TRUE; - } else if ((boolopt[i].addr) == &iflags.wc2_hitpointbar) { +#ifdef STATUS_HILITES + } else if (boolopt[i].addr == &iflags.wc2_hitpointbar) { status_initialize(REASSESS_ONLY); need_redraw = TRUE; +#endif #ifdef TEXTCOLOR } else if (boolopt[i].addr == &iflags.use_color) { need_redraw = TRUE; @@ -6037,10 +6048,10 @@ struct wc_Opt wc2_options[] = { { "fullscreen", WC2_FULLSCREEN }, { "softkeyboard", WC2_SOFTKEYBOARD }, { "wraptext", WC2_WRAPTEXT }, { "use_darkgray", WC2_DARKGRAY }, -#ifdef STATUS_HILITES { "hitpointbar", WC2_HITPOINTBAR }, { "hilite_status", WC2_HILITE_STATUS }, -#endif + /* statushilites doesn't have its own bit */ + { "statushilites", WC2_HILITE_STATUS }, { (char *) 0, 0L } }; /* diff --git a/win/tty/wintty.c b/win/tty/wintty.c index aa9ecfa5e..821239c68 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1506903624 2017/10/02 00:20:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.139 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1506908980 2017/10/02 01:49:40 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.140 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -53,20 +53,22 @@ extern NEARDATA winid WIN_STATUS; /* Interface definition, for windows.c */ struct window_procs tty_procs = { "tty", + (0 #ifdef MSDOS - WC_TILED_MAP | WC_ASCII_MAP | + | WC_TILED_MAP | WC_ASCII_MAP #endif #if defined(WIN32CON) - WC_MOUSE_SUPPORT | + | WC_MOUSE_SUPPORT #endif - WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN, + | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), + (0 #if defined(SELECTSAVED) - WC2_SELECTSAVED | + | WC2_SELECTSAVED #endif #if defined(STATUS_HILITES) - WC2_HITPOINTBAR | WC2_FLUSH_STATUS | + | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS #endif - WC2_DARKGRAY, + | WC2_DARKGRAY), tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows, tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow, From 8a45da0e8d06651bc4400ca3cac3508446642134 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 2 Oct 2017 18:25:09 -0700 Subject: [PATCH 04/29] feature options (#version output) I've sometimes seen , and basic NetHack features. as the last line of the features section from '#version'. I thought it was due to the way feature phrases were split into individual words by makedefs, but it turned out to be due to inserting pattern matching method at run-time. That dynamic options update had a second problem: if the final phrase "and basic NetHack features" was split across two lines, the run-time substitution didn't find it and the pattern matching entry ended up being left out. This fixes both problems, but if future dynamic entries become more complex, the phrase-splitting/word-wrapping being done by makedefs may need to be moved into nethack. Also, add entries for XLOGFILE and PANICLOG to makedefs' options and re-order a couple of existing ones alphabetically (a failry hopeless endeavor). --- doc/fixes36.1 | 4 +++ src/version.c | 90 ++++++++++++++----------------------------------- util/makedefs.c | 32 +++++++++++------- 3 files changed, 49 insertions(+), 77 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index dcfa8eec0..2e29869c5 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -453,6 +453,10 @@ when lit candelabrum burned out, persistent inventory window showed that it improve hilite_status, allowing multiple stops per field, and temporarily or permanently hilited fields give feedback when released from a bear trap +#version output sometimes had ", and basic NetHack features." on its own line + depending upon how the dynamically inserted pattern-match phrase fit +#version output left out "pattern matching via " if the basic NetHack + features entry was split across two lines Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/version.c b/src/version.c index 745611793..4567e30d7 100644 --- a/src/version.c +++ b/src/version.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 version.c $NHDT-Date: 1449328116 2015/12/05 15:08:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.6 version.c $NHDT-Date: 1506993902 2017/10/03 01:25:02 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.44 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -17,7 +17,7 @@ #define BETA_INFO "" -STATIC_DCL void FDECL(insert_rtoptions, (winid,char *,const char *)); +STATIC_DCL void FDECL(insert_rtoption, (char *)); /* fill buffer with short version (so caller can avoid including date.h) */ char * @@ -57,9 +57,8 @@ int doextversion() { dlb *f; - char *pd, buf[BUFSZ]; + char buf[BUFSZ]; winid win = create_nhwindow(NHW_TEXT); - boolean rtadded = FALSE; /* instead of using ``display_file(OPTIONS_USED,TRUE)'' we handle the file manually so we can include dynamic version info */ @@ -74,8 +73,7 @@ doextversion() /* * already inserted above: * + outdented program name and version plus build date and time - * dat/options; display the contents with lines prefixed by '-' - * deleted: + * dat/options; display contents with lines prefixed by '-' deleted: * - blank-line * - indented program name and version * blank-line @@ -108,16 +106,9 @@ doextversion() if (prolog || !*buf) continue; - if (!rtadded) { - const char *catchphrase = ", and basic NetHack features."; + if (index(buf, ':')) + insert_rtoption(buf); - pd = strstri(buf, catchphrase); - if (pd) { - *pd = '\0'; - insert_rtoptions(win, buf, &catchphrase[2]); - rtadded = TRUE; /* only do it once */ - } - } if (*buf) putstr(win, 0, buf); } @@ -130,10 +121,19 @@ doextversion() extern const char regex_id[]; -static const char *rt_opts[] = { - "pattern matching via", regex_id, +/* + * makedefs should put the first token into dat/options; we'll substitute + * the second value for it. The token must contain at least one colon + * so that we can spot it, and should not contain spaces so that makedefs + * won't split it across lines. Ideally the length should be close to + * that of the substituted value since we don't do phrase-splitting/line- + * wrapping when displaying it. + */ +static struct rt_opt { + const char *token, *value; +} rt_opts[] = { + { ":PATMATCH:", regex_id }, }; -static const char indent[] = " "; /* * 3.6.0 @@ -142,57 +142,17 @@ static const char indent[] = " "; * game image, so we insert those options here. */ STATIC_OVL void -insert_rtoptions(win, buf, finalphrase) -winid win; +insert_rtoption(buf) char *buf; -const char *finalphrase; { - char rtbuf[BUFSZ]; - int l, i; - const char *s1 = 0, *s2 = 0, *s3 = 0, *s4 = 0; + int i; - if ((int) strlen(buf) >= (BUFSZ - 1)) - return; - - strcpy(rtbuf, buf); - for (i = 0; i < (SIZE(rt_opts) + 1); i += 2) { - if (i < SIZE(rt_opts)) { - s1 = ", "; - s2 = rt_opts[i]; - s3 = " "; - s4 = rt_opts[i+1]; - } else { - s1 = " "; - s2 = finalphrase; - s3 = ""; - s4 = ""; - } - l = (int) strlen(rtbuf) + (int) strlen(s1) + (int) strlen(s2) - + (int) strlen(s3) + (int) strlen(s4) + 1; - if (l <= (COLNO - 5) && l < (BUFSZ - 1)) { - Strcat(rtbuf, s1); - Strcat(rtbuf, s2); - Strcat(rtbuf, s3); - Strcat(rtbuf, s4); - } else { - putstr(win, 0, rtbuf); - if (i >= SIZE(rt_opts)) - s1 = ""; - l = (int) strlen(indent) + (int) strlen(s1) + (int) strlen(s2) - + (int) strlen(s3) + (int) strlen(s4) + 1; - if (l <= (COLNO - 5) && l < (BUFSZ - 1)) { - Strcpy(rtbuf, indent); - Strcat(rtbuf, s1); - Strcat(rtbuf, s2); - Strcat(rtbuf, s3); - Strcat(rtbuf, s4); - } - } + for (i = 0; i < SIZE(rt_opts); ++i) { + if (strstri(buf, rt_opts[i].token)) + (void) strsubst(buf, rt_opts[i].token, rt_opts[i].value); + /* we don't break out of the loop after a match; there might be + other matches on the same line */ } - - if (l) - putstr(win, 0, rtbuf); - *buf = '\0'; } #ifdef MICRO diff --git a/util/makedefs.c b/util/makedefs.c index cfcabb3ca..4d32cc4ad 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 makedefs.c $NHDT-Date: 1459208813 2016/03/28 23:46:53 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.110 $ */ +/* NetHack 3.6 makedefs.c $NHDT-Date: 1506993895 2017/10/03 01:24:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.117 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* Copyright (c) M. Stephenson, 1990, 1991. */ /* Copyright (c) Dean Luick, 1990. */ @@ -1437,21 +1437,27 @@ static const char *build_opts[] = { #ifdef DUMPLOG "end-of-game dumplogs", #endif -#ifdef MFLOPPY - "floppy drive support", -#endif -#ifdef INSURANCE - "insurance files for recovering from crashes", -#endif #ifdef HOLD_LOCKFILE_OPEN "exclusive lock on level 0 file", #endif #if defined(MSGHANDLER) && (defined(POSIX_TYPES) || defined(__GNUC__)) "external program as a message handler", #endif +#ifdef MFLOPPY + "floppy drive support", +#endif +#ifdef INSURANCE + "insurance files for recovering from crashes", +#endif #ifdef LOGFILE "log file", #endif +#ifdef XLOGFILE + "extended log file", +#endif +#ifdef PANICLOG + "errors and warnings log file", +#endif #ifdef MAIL "mail daemon", #endif @@ -1472,6 +1478,8 @@ static const char *build_opts[] = { #endif #endif #endif + /* pattern matching method will be substituted by nethack at run time */ + "pattern matching via :PATMATCH:", #ifdef SELECTSAVED "restore saved games via menu", #endif @@ -1539,7 +1547,8 @@ static const char *build_opts[] = { #ifdef SYSCF "system configuration at run-time", #endif - save_bones_compat_buf, "and basic NetHack features" + save_bones_compat_buf, + "and basic NetHack features" }; struct win_info { @@ -1604,8 +1613,7 @@ windowing_sanity() for (i = 0; window_opts[i].id; ++i) if (!strcmp(window_opts[i].id, DEFAULT_WINDOW_SYS)) break; - if (!window_opts[i] - .id) { /* went through whole list without a match */ + if (!window_opts[i].id) { /* went through whole list without a match */ Fprintf(stderr, "Configuration error: DEFAULT_WINDOW_SYS (%s)\n", DEFAULT_WINDOW_SYS); Fprintf(stderr, @@ -1649,7 +1657,8 @@ do_options() Fprintf(ofp, "\nOptions compiled into this edition:\n"); length = COLNO + 1; /* force 1st item onto new line */ for (i = 0; i < SIZE(build_opts); i++) { - str = strcpy(buf, build_opts[i]); + str = strcat(strcpy(buf, build_opts[i]), + (i < SIZE(build_opts) - 1) ? "," : "."); while (*str) { word = index(str, ' '); if (word) @@ -1661,7 +1670,6 @@ do_options() Fprintf(ofp, "%s", str), length += strlen(str); str += strlen(str) + (word ? 1 : 0); } - Fprintf(ofp, (i < SIZE(build_opts) - 1) ? "," : "."), length++; } winsyscnt = SIZE(window_opts) - 1; From 429288cac9a08cf53ab67b291bbcce842bd24929 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 4 Oct 2017 12:21:05 -0400 Subject: [PATCH 05/29] first build in a long while, clear up a warning ..\src\options.c(4005) : warning C4113: 'int (__cdecl *)()' differs in parameter lists from 'int (__cdecl *)(void)' --- src/botl.c | 2 +- src/options.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/botl.c b/src/botl.c index 19ed8c973..ca6c494cf 100644 --- a/src/botl.c +++ b/src/botl.c @@ -2334,7 +2334,7 @@ int fld; } int -count_status_hilites() +count_status_hilites(VOID_ARGS) { int count; status_hilite_linestr_gather(); diff --git a/src/options.c b/src/options.c index 98afaa325..59f6fc4e4 100644 --- a/src/options.c +++ b/src/options.c @@ -1593,7 +1593,7 @@ int hide_mask; } STATIC_OVL int -msgtype_count() +msgtype_count(VOID_ARGS) { int c = 0; struct plinemsg_type *tmp = plinemsg_types; @@ -1791,7 +1791,7 @@ int idx; /* 0 .. */ } STATIC_OVL int -count_menucolors() +count_menucolors(VOID_ARGS) { int count = 0; struct menucoloring *tmp = menu_colorings; @@ -3975,7 +3975,7 @@ int nset; } int -count_apes() +count_apes(VOID_ARGS) { return count_ape_maps((int *) 0, (int *) 0); } From 856034d58504da93cd82ee5bd4c8b06ccb641376 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 4 Oct 2017 22:01:03 +0300 Subject: [PATCH 06/29] Cleaver can hit three monsters with one swing When hitting a monster with the Cleaver, you swing it in an arc, possibly hitting the monsters to the left and right of the targeted monster. Based on code by Fredrik Ljungdahl --- doc/fixes36.1 | 1 + src/uhitm.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 2e29869c5..05e62c36d 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -354,6 +354,7 @@ wielding Trollsbane prevents trolls from reviving wielding Demonbane prevents demons summoning friends wielding Dragonbane confers reflection wielding Ogresmasher grants 25 constitution +Cleaver can hit three monsters with one swing Elbereth must now be on a square by itself to function Elbereth now erodes based on attacks by the player, not monsters scared novels are made of paper, not gold diff --git a/src/uhitm.c b/src/uhitm.c index 0fcf8445c..b4d272187 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -8,6 +8,7 @@ STATIC_DCL boolean FDECL(known_hitum, (struct monst *, struct obj *, int *, int, int, struct attack *, int)); STATIC_DCL boolean FDECL(theft_petrifies, (struct obj *)); STATIC_DCL void FDECL(steal_it, (struct monst *, struct attack *)); +STATIC_DCL boolean FDECL(hitum_cleave, (struct monst *, struct attack *)); STATIC_DCL boolean FDECL(hitum, (struct monst *, struct attack *)); STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *, struct obj *, int, int)); @@ -489,6 +490,63 @@ int dieroll; return malive; } +/* hit the monster next to you and the monsters to the left and right of it */ +STATIC_OVL boolean +hitum_cleave(mon, uattk) +struct monst *mon; +struct attack *uattk; +{ + int i = 0; + int x = u.ux; + int y = u.uy; + int count = 3; + boolean malive = TRUE; + struct monst *mtmp; + + /* find the direction we're swinging */ + while (i < 8) { + if (xdir[i] == u.dx && ydir[i] == u.dy) + break; + i++; + } + + if (i == 8) { + impossible("hitum_cleave: failed to find target monster?"); + return TRUE; + } + i = (i + 2) % 8; + + /* swing from right to left */ + while (count-- && uwep) { + boolean result; + int tmp, dieroll, mhit, attknum, armorpenalty; + + if (!i) + i = 7; + else + i--; + + mtmp = NULL; + if (isok(x + xdir[i], y + ydir[i])) + mtmp = m_at(x + xdir[i], y + ydir[i]); + if (!mtmp) + continue; + + + tmp = find_roll_to_hit(mtmp, uattk->aatyp, uwep, + &attknum, &armorpenalty); + dieroll = rnd(20); + mhit = (tmp > dieroll); + result = known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty, + uattk, dieroll); + (void) passive(mtmp, mhit, DEADMONSTER(mtmp), AT_WEAP, !uwep); + if (mon == mtmp) + malive = result; + } + + return malive; +} + /* hit target monster; returns TRUE if it still lives */ STATIC_OVL boolean hitum(mon, uattk) @@ -503,6 +561,10 @@ struct attack *uattk; int dieroll = rnd(20); int mhit = (tmp > dieroll || u.uswallow); + if (uwep && uwep->oartifact == ART_CLEAVER + && !u.uswallow && !u.ustuck && !NODIAG(u.umonnum)) + return hitum_cleave(mon, uattk); + if (tmp > dieroll) exercise(A_DEX, TRUE); malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); From c60db38725b56c20f1a35c6da769cf06c6e95e03 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 4 Oct 2017 19:55:40 -0400 Subject: [PATCH 07/29] missed file in vesa inclusion back in 2016 --- sys/msdos/vesa.h | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 sys/msdos/vesa.h diff --git a/sys/msdos/vesa.h b/sys/msdos/vesa.h new file mode 100644 index 000000000..1184a42ca --- /dev/null +++ b/sys/msdos/vesa.h @@ -0,0 +1,81 @@ +/* NetHack 3.6 vesa.h $NHDT-Date: 1507161296 2017/10/04 23:54:56 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.0 $ */ + +/* VESA structures from the VESA BIOS Specification, retrieved 15 Jan 2016 + * from http://flint.cs.yale.edu/cs422/readings/hardware/vbe3.pdf + * Part of rchason vesa port */ + +#ifndef VESA_H +#define VESA_H + +#include + +struct VbeInfoBlock { + uint8_t VbeSignature[4]; /* VBE Signature */ + uint16_t VbeVersion; /* VBE Version */ + uint32_t OemStringPtr; /* VbeFarPtr to OEM String */ + uint32_t Capabilities; /* Capabilities of graphics controller */ + uint32_t VideoModePtr; /* VbeFarPtr to VideoModeList */ + uint16_t TotalMemory; /* Number of 64kb memory blocks */ + /* Added for VBE 2.0+ */ + uint16_t OemSoftwareRev; /* VBE implementation Software revision */ + uint32_t OemVendorNamePtr; /* VbeFarPtr to Vendor Name String */ + uint32_t OemProductNamePtr; /* VbeFarPtr to Product Name String */ + uint32_t OemProductRevPtr; /* VbeFarPtr to Product Revision String */ + uint8_t Reserved[222]; /* Reserved for VBE implementation scratch area */ + uint8_t OemData[256]; /* Data Area for OEM Strings */ +} __attribute__((packed)); + +struct ModeInfoBlock { + /* Mandatory information for all VBE revisions */ + uint16_t ModeAttributes; /* mode attributes */ + uint8_t WinAAttributes; /* window A attributes */ + uint8_t WinBAttributes; /* window B attributes */ + uint16_t WinGranularity; /* window granularity */ + uint16_t WinSize; /* window size */ + uint16_t WinASegment; /* window A start segment */ + uint16_t WinBSegment; /* window B start segment */ + uint32_t WinFuncPtr; /* real mode pointer to window function */ + uint16_t BytesPerScanLine; /* bytes per scan line */ + /* Mandatory information for VBE 1.2 and above */ + uint16_t XResolution; /* horizontal resolution in pixels or characters */ + uint16_t YResolution; /* vertical resolution in pixels or characters */ + uint8_t XCharSize; /* character cell width in pixels */ + uint8_t YCharSize; /* character cell height in pixels */ + uint8_t NumberOfPlanes; /* number of memory planes */ + uint8_t BitsPerPixel; /* bits per pixel */ + uint8_t NumberOfBanks; /* number of banks */ + uint8_t MemoryModel; /* memory model type */ + uint8_t BankSize; /* bank size in KB */ + uint8_t NumberOfImagePages; /* number of images */ + uint8_t Reserved1; /* reserved for page function */ + /* Direct Color fields (required for direct/6 and YUV/7 memory models) */ + uint8_t RedMaskSize; /* size of direct color red mask in bits */ + uint8_t RedFieldPosition; /* bit position of lsb of red mask */ + uint8_t GreenMaskSize; /* size of direct color green mask in bits */ + uint8_t GreenFieldPosition; /* bit position of lsb of green mask */ + uint8_t BlueMaskSize; /* size of direct color blue mask in bits */ + uint8_t BlueFieldPosition; /* bit position of lsb of blue mask */ + uint8_t RsvdMaskSize; /* size of direct color reserved mask in bits */ + uint8_t RsvdFieldPosition; /* bit position of lsb of reserved mask */ + uint8_t DirectColorModeInfo; /* direct color mode attributes */ + /* Mandatory information for VBE 2.0 and above */ + uint32_t PhysBasePtr; /* physical address for flat memory frame buffer */ + uint32_t Reserved2; /* Reserved - always set to 0 */ + uint16_t Reserved3; /* Reserved - always set to 0 */ + /* Mandatory information for VBE 3.0 and above */ + uint16_t LinBytesPerScanLine; /* bytes per scan line for linear modes */ + uint8_t BnkNumberOfImagePages; /* number of images for banked modes */ + uint8_t LinNumberOfImagePages; /* number of images for linear modes */ + uint8_t LinRedMaskSize; /* size of direct color red mask (linear modes) */ + uint8_t LinRedFieldPosition; /* bit position of lsb of red mask (linear modes) */ + uint8_t LinGreenMaskSize; /* size of direct color green mask (linear modes) */ + uint8_t LinGreenFieldPosition; /* bit position of lsb of green mask (linear modes) */ + uint8_t LinBlueMaskSize; /* size of direct color blue mask (linear modes) */ + uint8_t LinBlueFieldPosition; /* bit position of lsb of blue mask (linear modes) */ + uint8_t LinRsvdMaskSize; /* size of direct color reserved mask (linear modes) */ + uint8_t LinRsvdFieldPosition; /* bit position of lsb of reserved mask (linear modes) */ + uint32_t MaxPixelClock; /* maximum pixel clock (in Hz) for graphics mode */ + uint8_t Reserved4[189]; /* remainder of ModeInfoBlock */ +} __attribute__((packed)); + +#endif From aac1d21255400dba4ea2a167ce3e3def04a51253 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 4 Oct 2017 19:57:31 -0400 Subject: [PATCH 08/29] reference the added file sys/msdos/vesa.h in Files --- Files | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Files b/Files index 8c3a948d0..6a851a810 100644 --- a/Files +++ b/Files @@ -121,7 +121,7 @@ Install.dos Makefile.BC Makefile.GCC Makefile.MSC moveinit.pat msdos.c msdoshlp.txt ovlinit.c pckeys.c pctiles.c pctiles.h pcvideo.h portio.h schema1.BC schema2.BC schema3.MSC SCHEMA35.MSC setup.bat sound.c tile2bin.c -video.c vidtxt.c vidvesa.c vidvga.c +vesa.h video.c vidtxt.c vidvesa.c vidvga.c (files for running MSDOS binary under Windows) nhico.uu nhpif.uu From d9a5190f4d9ec5e1a458d3986d864fccb0115eba Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 4 Oct 2017 17:51:48 -0700 Subject: [PATCH 09/29] options vs !BACKWARD_COMPAT Give sensible feedback for obsolete options if options.c is modified to #undef BACKWARD_COMPAT. Affects boulder, DECgraphics, IBMgraphics, and MACgraphics. --- src/options.c | 56 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/options.c b/src/options.c index 59f6fc4e4..90d58f6e7 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1506908974 2017/10/02 01:49:34 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.309 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1507164574 2017/10/05 00:49:34 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.311 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2557,11 +2557,12 @@ boolean tinitial, tfrom_file; return warning_opts(opts, fullname); } -#ifdef BACKWARD_COMPAT /* boulder:symbol */ fullname = "boulder"; if (match_optname(opts, fullname, 7, TRUE)) { +#ifdef BACKWARD_COMPAT int clash = 0; + if (duplicate) complain_about_duplicate(opts, 1); if (negated) { @@ -2582,7 +2583,7 @@ boolean tinitial, tfrom_file; symbol which is not good - reject it*/ config_error_add( "Badoption - boulder symbol '%c' conflicts with a %s symbol.", - opts[0], (clash == 1) ? "monster" : "warning"); + opts[0], (clash == 1) ? "monster" : "warning"); } else { /* * Override the default boulder symbol. @@ -2596,8 +2597,12 @@ boolean tinitial, tfrom_file; need_redraw = TRUE; } return retval; - } +#else + config_error_add("'%s' no longer supported; use S_boulder:c instead", + fullname); + return FALSE; #endif + } /* name:string */ fullname = "name"; @@ -3467,7 +3472,8 @@ boolean tinitial, tfrom_file; } } /* hilite fields in status prompt */ - if (match_optname(opts, "hilite_status", 13, TRUE)) { + fullname = "hilite_status"; + if (match_optname(opts, fullname, 13, TRUE)) { #ifdef STATUS_HILITES if (duplicate) complain_about_duplicate(opts, 1); @@ -3483,7 +3489,7 @@ boolean tinitial, tfrom_file; return FALSE; return retval; #else - config_error_add("'hilite_status' is not supported"); + config_error_add("'%s' is not supported", fullname); return FALSE; #endif } @@ -3504,14 +3510,14 @@ boolean tinitial, tfrom_file; reset_status_hilites(); return retval; #else - config_error_add("'statushilites' is not supported"); + config_error_add("'%s' is not supported", fullname); return FALSE; #endif } -#if defined(BACKWARD_COMPAT) fullname = "DECgraphics"; if (match_optname(opts, fullname, 3, TRUE)) { +#ifdef BACKWARD_COMPAT boolean badflag = FALSE; if (duplicate) @@ -3534,9 +3540,16 @@ boolean tinitial, tfrom_file; } } return retval; - } +#else + config_error_add("'%s' no longer supported; use 'symset:%s' instead", + fullname, fullname); + return FALSE; +#endif + } /* "DECgraphics" */ + fullname = "IBMgraphics"; if (match_optname(opts, fullname, 3, TRUE)) { +#ifdef BACKWARD_COMPAT const char *sym_name = fullname; boolean badflag = FALSE; @@ -3567,11 +3580,16 @@ boolean tinitial, tfrom_file; } } return retval; - } +#else + config_error_add("'%s' no longer supported; use 'symset:%s' instead", + fullname, fullname); + return FALSE; #endif -#ifdef MAC_GRAPHICS_ENV + } /* "IBMgraphics" */ + fullname = "MACgraphics"; if (match_optname(opts, fullname, 3, TRUE)) { +#if defined(MAC_GRAPHICS_ENV) && defined(BACKWARD_COMPAT) boolean badflag = FALSE; if (duplicate) @@ -3596,8 +3614,18 @@ boolean tinitial, tfrom_file; } } return retval; - } +#else /* !(MAC_GRAPHICS_ENV && BACKWARD_COMPAT) */ + config_error_add("'%s' %s; use 'symset:%s' instead", + fullname, +#ifdef MAC_GRAPHICS_ENV /* implies BACKWARD_COMPAT is not defined */ + "no longer supported", +#else + "is not supported", #endif + fullname); + return FALSE; +#endif /* ?(MAC_GRAPHICS_ENV && BACKWARD_COMPAT) */ + } /* "MACgraphics" */ /* OK, if we still haven't recognized the option, check the boolean * options list @@ -3621,7 +3649,9 @@ boolean tinitial, tfrom_file; if (op) { if (negated) { - config_error_add("Negated boolean '%s' should not have a parameter", boolopt[i].name); + config_error_add( + "Negated boolean '%s' should not have a parameter", + boolopt[i].name); return FALSE; } if (!strcmp(op, "true") || !strcmp(op, "yes")) { From 5aa1f3aa08ee7c0ed9419dbec0d2c3d9fdc3e91d Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 4 Oct 2017 18:18:21 -0700 Subject: [PATCH 10/29] set_uasmon Noticed while looking at something else, merging status highlighting changes introduced a redundant call to polysense() in set_uasmon(). --- src/polyself.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/polyself.c b/src/polyself.c index c1dc74ff6..a7febb3b1 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -110,8 +110,6 @@ set_uasmon() #ifdef STATUS_HILITES status_initialize(REASSESS_ONLY); #endif - - polysense(); } /* Levitation overrides Flying; set or clear BFlying|I_SPECIAL */ From e8b9805124c3ceb3ec7bd6e3e09a9003ea051c30 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 5 Oct 2017 02:01:18 -0700 Subject: [PATCH 11/29] STATUS_HILITES vs multiple interface binary My tty+X11 binary was letting me see and modify status highlights under X11 even though they don't do anything. Options parsing has to accept them since we don't know which interface will be chosen, but interactive option handling does know and shouldn't show inappropriate options. initoptions_finish() should probably we validating all the wc and wc2 options, but I only added a check for trying to enable statushilites when they're unavailable. --- src/options.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/options.c b/src/options.c index 90d58f6e7..c17e44869 100644 --- a/src/options.c +++ b/src/options.c @@ -652,7 +652,7 @@ initoptions() * lists of usernames into arrays with one name per element. */ #endif -#endif +#endif /* SYSCF */ initoptions_finish(); } @@ -809,12 +809,12 @@ initoptions_finish() config_error_done(); } } else -#endif - { - config_error_init(TRUE, (char *) 0, FALSE); - read_config_file((char *) 0, SET_IN_FILE); - config_error_done(); - } +#endif /* !MAC */ + /*else*/ { + config_error_init(TRUE, (char *) 0, FALSE); + read_config_file((char *) 0, SET_IN_FILE); + config_error_done(); + } (void) fruitadd(pl_fruit, (struct fruit *) 0); /* @@ -828,6 +828,19 @@ initoptions_finish() if (iflags.bouldersym) update_bouldersym(); reglyph_darkroom(); + +#ifdef STATUS_HILITES + /* + * A multi-interface binary might only support status highlighting + * for some of the interfaces; check whether we asked for it but are + * using one which doesn't. + */ + if (iflags.hilite_delta && !wc2_supported("statushilites")) { + raw_printf("Status highlighting not supported for %s interface.", + windowprocs.name); + iflags.hilite_delta = 0; + } +#endif return; } @@ -4158,9 +4171,13 @@ doset() /* changing options via menu by Per Liboriussen */ "Other settings:", MENU_UNSELECTED); - for (i = 0; othropt[i].name; i++) - opts_add_others(tmpwin, othropt[i].name, othropt[i].code, - NULL, othropt[i].othr_count_func()); + for (i = 0; (name = othropt[i].name) != 0; i++) { + if ((is_wc_option(name) && !wc_supported(name)) + || (is_wc2_option(name) && !wc2_supported(name))) + continue; + opts_add_others(tmpwin, name, othropt[i].code, + (char *) 0, othropt[i].othr_count_func()); + } #ifdef PREFIXES_IN_USE any = zeroany; @@ -6080,6 +6097,8 @@ struct wc_Opt wc2_options[] = { { "fullscreen", WC2_FULLSCREEN }, { "use_darkgray", WC2_DARKGRAY }, { "hitpointbar", WC2_HITPOINTBAR }, { "hilite_status", WC2_HILITE_STATUS }, + /* name shown in 'O' menu is different */ + { "status hilite rules", WC2_HILITE_STATUS }, /* statushilites doesn't have its own bit */ { "statushilites", WC2_HILITE_STATUS }, { (char *) 0, 0L } }; From 605227c0d693785209c7c32274835e1a02ef770e Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 5 Oct 2017 02:38:25 -0700 Subject: [PATCH 12/29] untrap() formatting bits --- src/trap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/trap.c b/src/trap.c index 91584da94..53493d426 100644 --- a/src/trap.c +++ b/src/trap.c @@ -4242,8 +4242,9 @@ boolean force; struct trap *ttmp; struct monst *mtmp; const char *trapdescr; - boolean here, useplural, confused = (Confusion || Hallucination), - trap_skipped = FALSE, deal_with_floor_trap; + boolean here, useplural, deal_with_floor_trap, + confused = (Confusion || Hallucination), + trap_skipped = FALSE; int boxcnt = 0; char the_trap[BUFSZ], qbuf[QBUFSZ]; @@ -4434,7 +4435,7 @@ boolean force; return 0; } - if ((levl[x][y].doormask & D_TRAPPED + if (((levl[x][y].doormask & D_TRAPPED) != 0 && (force || (!confused && rn2(MAXULEV - u.ulevel + 11) < 10))) || (!force && confused && !rn2(3))) { You("find a trap on the door!"); From 22ad1043180d1e2bba433944c6a76b218a533190 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 6 Oct 2017 01:14:00 +0300 Subject: [PATCH 13/29] Master Key of Thievery warns about undetected traps If the key is wielded and touching skin (that is, you're not wearing gloves), it will give heat-related messages like minesweeper, counting the undetected traps around player. --- doc/fixes36.1 | 1 + include/extern.h | 1 + src/allmain.c | 1 + src/artifact.c | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 05e62c36d..34502e489 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -355,6 +355,7 @@ wielding Demonbane prevents demons summoning friends wielding Dragonbane confers reflection wielding Ogresmasher grants 25 constitution Cleaver can hit three monsters with one swing +Master Key of Thievery warns about undetected traps if wielded Elbereth must now be on a square by itself to function Elbereth now erodes based on attacks by the player, not monsters scared novels are made of paper, not gold diff --git a/include/extern.h b/include/extern.h index 3a55fc4d0..de4003739 100644 --- a/include/extern.h +++ b/include/extern.h @@ -92,6 +92,7 @@ E const char *FDECL(glow_color, (int)); E void FDECL(Sting_effects, (int)); E int FDECL(retouch_object, (struct obj **, BOOLEAN_P)); E void FDECL(retouch_equipment, (int)); +E void NDECL(mkot_trap_warn); /* ### attrib.c ### */ diff --git a/src/allmain.c b/src/allmain.c index 170f8b1df..8834c6a60 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -267,6 +267,7 @@ boolean resuming; (void) dosearch0(1); if (Warning) warnreveal(); + mkot_trap_warn(); dosounds(); do_storms(); gethungry(); diff --git a/src/artifact.c b/src/artifact.c index 13e8be981..4fdfc828a 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -27,6 +27,7 @@ STATIC_DCL boolean FDECL(Mb_hit, (struct monst * magr, struct monst *mdef, STATIC_DCL unsigned long FDECL(abil_to_spfx, (long *)); STATIC_DCL uchar FDECL(abil_to_adtyp, (long *)); STATIC_DCL boolean FDECL(untouchable, (struct obj *, BOOLEAN_P)); +STATIC_DCL int FDECL(count_surround_traps, (int, int)); /* The amount added to the victim's total hit points to insure that the victim will be killed even after damage bonus/penalty adjustments. @@ -2051,4 +2052,39 @@ int dropflag; /* 0==don't drop, 1==drop all, 2==drop weapon */ clear_bypasses(); /* reset upon final exit */ } +static int mkot_trap_warn_count = 0; + +STATIC_OVL int +count_surround_traps(x,y) +int x,y; +{ + int dx, dy, ret = 0; + struct trap *ttmp; + + for (dx = x-1; dx < x+2; dx++) + for (dy = y-1; dy < y+2; dy++) + if (isok(dx,dy) && (ttmp = t_at(dx,dy)) + && !ttmp->tseen) + ret++; + return ret; +} + +void +mkot_trap_warn() +{ + if (!uarmg && uwep + && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) { + const char *const heat[7] = { "cold", "slightly warm", "warm", + "very warm", "hot", "very hot", + "like fire" }; + int ntraps = count_surround_traps(u.ux, u.uy); + + if (ntraps != mkot_trap_warn_count) + pline_The("key feels %s%c", heat[(ntraps > 6) ? 6 : ntraps], + ntraps > 3 ? '!' : '.'); + mkot_trap_warn_count = ntraps; + } else + mkot_trap_warn_count = 0; +} + /*artifact.c*/ From 9a6c3b4192ddecb0d7ac8c0ab3c03d5b16dad643 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 6 Oct 2017 01:13:17 -0700 Subject: [PATCH 14/29] master key of thievery data.base entry Make the description of the rogue's Key be more accurate: it disarms trapped door and check locks rather than "any trap". --- dat/data.base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dat/data.base b/dat/data.base index 9f2e6433a..ad7c61477 100644 --- a/dat/data.base +++ b/dat/data.base @@ -3213,7 +3213,7 @@ master key of thievery a powerful magic which allows it to open any lock. When carried, it grants its owner warning, teleport control, and reduces all physical damage by half. Finally, when invoked, - it has the ability to disarm any trap. + it has the ability to disarm any trapped lock. master of thieves There was a flutter of wings at the window. Ymor shifted his bulk out of the chair and crossed the room, coming back with From 468fc1a925585ede3ff40a9e22105909c2680032 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 7 Oct 2017 02:28:20 -0700 Subject: [PATCH 15/29] more Master Key of Thievery: lock/unlock vs traps Always find traps when using the rogue's quest Key to lock or unlock a trapped door or chest provided that the Key is not cursed (for rogues) or is blessed (for non-rogues). When a trap is found, the player is given the opportunity to disarm it, and doing so will always succeed. (It isn't disarmed automatically; the player may prefer to leave traps in place, presumably hoping to set up a dangerous bones file. Or he or she may be unaware of the guaranteed success and be too timid to risk trying to disarm the trap.) TODO: make #untrap of a door or chest while carrying that Key always find traps (with same bless/curse requirements as above). And maybe change its #invoke property from untrap to detect unseen/secret door detection since current invoke power would become redundant. Also, move a bunch of new artifact abilities from the fixes section to the new features section in fixes36.1. --- doc/fixes36.1 | 19 ++++++++------ src/lock.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 34502e489..8e2d0f486 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -350,14 +350,6 @@ pets start with apport equal to your charisma sometimes generate the random mazes with wide corridors, thick walls, or with dead ends changed to loops put throne room gold in the chest -wielding Trollsbane prevents trolls from reviving -wielding Demonbane prevents demons summoning friends -wielding Dragonbane confers reflection -wielding Ogresmasher grants 25 constitution -Cleaver can hit three monsters with one swing -Master Key of Thievery warns about undetected traps if wielded -Elbereth must now be on a square by itself to function -Elbereth now erodes based on attacks by the player, not monsters scared novels are made of paper, not gold movement speeds are made less predictable by using random rounding, rather than via adding a random offset @@ -681,6 +673,17 @@ new paranoid_confirm settings: wand-break to require "yes" rather than 'y' option force_invmenu to make commands asking for inventory items always use a menu instead of a text line query option hitpointbar to show a bar graph of hit points behind title field +wielding Trollsbane prevents troll corpses from reviving +wielding Demonbane prevents demons summoning friends +wielding Dragonbane confers reflection +wielding Ogresmasher grants 25 constitution +Cleaver can hit three adjacent monsters with one swing +Master Key of Thievery warns about undetected traps if wielded without gloves +Master Key of Thievery always finds door and chest traps if used to lock or + unlock a trapped door or chest while non-cursed (for rogues) or + blessed (for non-rogues); player is offered the opportunity to disarm +"Elbereth" must now be the only engraved text on a square to function +"Elbereth" now erodes based on attacks by the player, not monsters scared Platform- and/or Interface-Specific New Features diff --git a/src/lock.c b/src/lock.c index 94073f3ae..19dfabe46 100644 --- a/src/lock.c +++ b/src/lock.c @@ -4,18 +4,21 @@ #include "hack.h" -STATIC_PTR int NDECL(picklock); -STATIC_PTR int NDECL(forcelock); - /* at most one of `door' and `box' should be non-null at any given time */ STATIC_VAR NEARDATA struct xlock_s { struct rm *door; struct obj *box; int picktyp, /* key|pick|card for unlock, sharp vs blunt for #force */ chance, usedtime; + boolean magic_key; } xlock; +/* occupation callbacks */ +STATIC_PTR int NDECL(picklock); +STATIC_PTR int NDECL(forcelock); + STATIC_DCL const char *NDECL(lock_action); +STATIC_DCL boolean FDECL(is_magic_key, (struct monst *, struct obj *)); STATIC_DCL boolean FDECL(obstructed, (int, int, BOOLEAN_P)); STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *)); @@ -105,6 +108,40 @@ picklock(VOID_ARGS) if (rn2(100) >= xlock.chance) return 1; /* still busy */ + /* using the Master Key of Thievery finds traps if its bless/curse + state is adequate (non-cursed for rogues, blessed for others; + checked when setting up 'xlock') */ + if ((!xlock.door ? (int) xlock.box->otrapped + : (xlock.door->doormask & D_TRAPPED) != 0) + && xlock.magic_key) { + xlock.chance += 20; /* less effort needed next time */ + /* unfortunately we don't have a 'tknown' flag to record + "known to be trapped" so declining to disarm and then + retrying lock manipulation will find it all over again */ + if (yn("You find a trap! Do you want to try to disarm it?") == 'y') { + const char *what; + boolean alreadyunlocked; + + /* disarming while using magic key always succeeds */ + if (xlock.door) { + xlock.door->doormask &= ~D_TRAPPED; + what = "door"; + alreadyunlocked = !(xlock.door->doormask & D_LOCKED); + } else { + xlock.box->otrapped = 0; + what = (xlock.box->otyp == CHEST) ? "chest" : "box"; + alreadyunlocked = !xlock.box->olocked; + } + You("succeed in disarming the trap. The %s is still %slocked.", + what, alreadyunlocked ? "un" : ""); + exercise(A_WIS, TRUE); + } else { + You("stop %s.", lock_action()); + exercise(A_WIS, FALSE); + } + return ((xlock.usedtime = 0)); + } + You("succeed in %s.", lock_action()); if (xlock.door) { if (xlock.door->doormask & D_TRAPPED) { @@ -225,6 +262,7 @@ void reset_pick() { xlock.usedtime = xlock.chance = xlock.picktyp = 0; + xlock.magic_key = FALSE; xlock.door = 0; xlock.box = 0; } @@ -237,6 +275,21 @@ maybe_reset_pick() reset_pick(); } +/* Master Key is magic key if its bless/curse state meets our criteria: + not cursed for rogues or blessed for non-rogues */ +STATIC_OVL boolean +is_magic_key(mon, obj) +struct monst *mon; /* if null, non-rogue is assumed */ +struct obj *obj; +{ + if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY) + && ((mon == &youmonst) ? Role_if(PM_ROGUE) + : (mon && mon->data == &mons[PM_ROGUE]))) + ? !obj->cursed : obj->blessed) + return TRUE; + return FALSE; +} + /* for doapply(); if player gives a direction or resumes an interrupted previous attempt then it costs hero a move even if nothing ultimately happens; when told "can't do that" before being asked for direction @@ -264,6 +317,7 @@ struct obj *pick; if (nohands(youmonst.data)) { const char *what = (picktyp == LOCK_PICK) ? "pick" : "key"; + if (picktyp == CREDIT_CARD) what = "card"; pline(no_longer, "hold the", what); @@ -277,6 +331,7 @@ struct obj *pick; const char *action = lock_action(); You("resume your attempt at %s.", action); + xlock.magic_key = is_magic_key(&youmonst, pick); set_occupation(picklock, action, 0); return PICKLOCK_DID_SOMETHING; } @@ -291,8 +346,9 @@ struct obj *pick; return PICKLOCK_DID_NOTHING; } - if ((picktyp != LOCK_PICK && picktyp != CREDIT_CARD - && picktyp != SKELETON_KEY)) { + if (picktyp != LOCK_PICK + && picktyp != CREDIT_CARD + && picktyp != SKELETON_KEY) { impossible("picking lock with object %d?", picktyp); return PICKLOCK_DID_NOTHING; } @@ -375,7 +431,6 @@ struct obj *pick; if (otmp->cursed) ch /= 2; - xlock.picktyp = picktyp; xlock.box = otmp; xlock.door = 0; break; @@ -407,7 +462,7 @@ struct obj *pick; } else if (mtmp && is_door_mappear(mtmp)) { /* "The door actually was a !" */ stumble_onto_mimic(mtmp); - /* mimic might keep the key (50% chance, 10% for PYEC) */ + /* mimic might keep the key (50% chance, 10% for PYEC or MKoT) */ maybe_absorb_item(mtmp, pick, 50, 10); return PICKLOCK_LEARNED_SOMETHING; } @@ -462,6 +517,7 @@ struct obj *pick; context.move = 0; xlock.chance = ch; xlock.picktyp = picktyp; + xlock.magic_key = is_magic_key(&youmonst, pick); xlock.usedtime = 0; set_occupation(picklock, lock_action(), 0); return PICKLOCK_DID_SOMETHING; @@ -531,6 +587,7 @@ doforce() xlock.box = otmp; xlock.chance = objects[uwep->otyp].oc_wldam * 2; xlock.picktyp = picktyp; + xlock.magic_key = FALSE; xlock.usedtime = 0; break; } From f8760093cca7a74037a9a77123bd14b809eb6738 Mon Sep 17 00:00:00 2001 From: Bart House Date: Fri, 6 Oct 2017 18:57:15 -0700 Subject: [PATCH 16/29] Set the local working directory when debugging to $BinDir. This is needed to ensure the defaults.nh is found. --- win/win32/vs2015/default.props | 1 + win/win32/vs2017/default.props | 1 + 2 files changed, 2 insertions(+) diff --git a/win/win32/vs2015/default.props b/win/win32/vs2015/default.props index 688b5d311..82619060c 100644 --- a/win/win32/vs2015/default.props +++ b/win/win32/vs2015/default.props @@ -5,6 +5,7 @@ false MultiByte v140 + $(BinDir) true diff --git a/win/win32/vs2017/default.props b/win/win32/vs2017/default.props index 3ad8bdd10..e0bc58136 100644 --- a/win/win32/vs2017/default.props +++ b/win/win32/vs2017/default.props @@ -5,6 +5,7 @@ false MultiByte v141 + $(BinDir) true From 4b7aea0eac265f70654254c802b84c4e0e4d2d6d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 7 Oct 2017 16:24:49 +0300 Subject: [PATCH 17/29] Fold invisible glyph unmapping into single function --- include/extern.h | 1 + src/apply.c | 16 +++------------- src/detect.c | 11 +++-------- src/display.c | 13 +++++++++++++ src/dokick.c | 10 ++-------- src/explode.c | 7 ++----- src/hack.c | 5 +---- src/zap.c | 6 ++---- 8 files changed, 27 insertions(+), 42 deletions(-) diff --git a/include/extern.h b/include/extern.h index de4003739..54ec9440c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -321,6 +321,7 @@ E void FDECL(map_background, (XCHAR_P, XCHAR_P, int)); E void FDECL(map_trap, (struct trap *, int)); E void FDECL(map_object, (struct obj *, int)); E void FDECL(map_invisible, (XCHAR_P, XCHAR_P)); +E boolean FDECL(unmap_invisible, (int, int)); E void FDECL(unmap_object, (int, int)); E void FDECL(map_location, (int, int, int)); E void FDECL(feel_newsym, (XCHAR_P, XCHAR_P)); diff --git a/src/apply.c b/src/apply.c index e19e86b71..e5df1b904 100644 --- a/src/apply.c +++ b/src/apply.c @@ -408,11 +408,8 @@ register struct obj *obj; map_invisible(rx, ry); return res; } - if (glyph_is_invisible(levl[rx][ry].glyph)) { - unmap_object(rx, ry); - newsym(rx, ry); + if (unmap_invisible(rx,ry)) pline_The("invisible monster must have moved."); - } lev = &levl[rx][ry]; switch (lev->typ) { @@ -631,10 +628,7 @@ struct obj *obj; if (!(mtmp = m_at(cc.x, cc.y))) { There("is no creature there."); - if (glyph_is_invisible(levl[cc.x][cc.y].glyph)) { - unmap_object(cc.x, cc.y); - newsym(cc.x, cc.y); - } + (void) unmap_invisible(cc.x, cc.y); return 1; } @@ -3030,11 +3024,7 @@ struct obj *obj; } } else { /* no monster here and no statue seen or remembered here */ - if (glyph_is_invisible(glyph)) { - /* now you know that nothing is there... */ - unmap_object(bhitpos.x, bhitpos.y); - newsym(bhitpos.x, bhitpos.y); - } + (void) unmap_invisible(bhitpos.x, bhitpos.y); You("miss; there is no one there to hit."); } u_wipe_engr(2); /* same as for melee or throwing */ diff --git a/src/detect.c b/src/detect.c index 2ba4a968f..1c5f35203 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1394,9 +1394,7 @@ genericptr_t num; } if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph)) map_invisible(zx, zy); - } else if (glyph_is_invisible(levl[zx][zy].glyph)) { - unmap_object(zx, zy); - newsym(zx, zy); + } else if (unmap_invisible(zx, zy)) { (*(int *) num)++; } } @@ -1646,11 +1644,8 @@ register int aflag; /* intrinsic autosearch vs explicit searching */ /* see if an invisible monster has moved--if Blind, * feel_location() already did it */ - if (!aflag && !mtmp && !Blind - && glyph_is_invisible(levl[x][y].glyph)) { - unmap_object(x, y); - newsym(x, y); - } + if (!aflag && !mtmp && !Blind) + (void) unmap_invisible(x, y); if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) { nomul(0); diff --git a/src/display.c b/src/display.c index bede97c3c..a16b8909f 100644 --- a/src/display.c +++ b/src/display.c @@ -277,6 +277,19 @@ register xchar x, y; } } +boolean +unmap_invisible(x, y) +int x, y; +{ + if (isok(x,y) && glyph_is_invisible(levl[x][y].glyph)) { + unmap_object(x, y); + newsym(x, y); + return TRUE; + } + return FALSE; +} + + /* * unmap_object() * diff --git a/src/dokick.c b/src/dokick.c index a6a34898f..7aa8e2baa 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -262,10 +262,7 @@ doit: } else { maybe_mnexto(mon); if (mon->mx != x || mon->my != y) { - if (glyph_is_invisible(levl[x][y].glyph)) { - unmap_object(x, y); - newsym(x, y); - } + (void) unmap_invisible(x, y); pline("%s %s, %s evading your %skick.", Monnam(mon), (!level.flags.noteleport && can_teleport(mon->data)) ? "teleports" @@ -946,10 +943,7 @@ dokick() } return 1; } - if (glyph_is_invisible(levl[x][y].glyph)) { - unmap_object(x, y); - newsym(x, y); - } + (void) unmap_invisible(x, y); if (is_pool(x, y) ^ !!u.uinwater) { /* objects normally can't be removed from water by kicking */ You("splash some %s around.", hliquid("water")); diff --git a/src/explode.c b/src/explode.c index 643805dc4..cc6477c4a 100644 --- a/src/explode.c +++ b/src/explode.c @@ -219,11 +219,8 @@ int expltype; } if (mtmp && cansee(i + x - 1, j + y - 1) && !canspotmon(mtmp)) map_invisible(i + x - 1, j + y - 1); - else if (!mtmp && glyph_is_invisible( - levl[i + x - 1][j + y - 1].glyph)) { - unmap_object(i + x - 1, j + y - 1); - newsym(i + x - 1, j + y - 1); - } + else if (!mtmp) + (void) unmap_invisible(i + x - 1, j + y - 1); if (cansee(i + x - 1, j + y - 1)) visible = TRUE; if (explmask[i][j] == 1) diff --git a/src/hack.c b/src/hack.c index 14e7258b4..1aabddbdc 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1635,10 +1635,7 @@ domove() } return; } - if (glyph_is_invisible(levl[x][y].glyph)) { - unmap_object(x, y); - newsym(x, y); - } + (void) unmap_invisible(x, y); /* not attacking an animal, so we try to move */ if ((u.dx || u.dy) && u.usteed && stucksteed(FALSE)) { nomul(0); diff --git a/src/zap.c b/src/zap.c index 6d82ab48e..1a8570991 100644 --- a/src/zap.c +++ b/src/zap.c @@ -3958,10 +3958,8 @@ boolean say; /* Announce out of sight hit/miss events if true */ /* reveal/unreveal invisible monsters before tmp_at() */ if (mon && !canspotmon(mon)) map_invisible(sx, sy); - else if (!mon && glyph_is_invisible(levl[sx][sy].glyph)) { - unmap_object(sx, sy); - newsym(sx, sy); - } + else if (!mon) + (void) unmap_invisible(sx, sy); if (ZAP_POS(levl[sx][sy].typ) || (isok(lsx, lsy) && cansee(lsx, lsy))) tmp_at(sx, sy); From 9b82b72c6ef41d74005006c9d0c78b583f8e562c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 7 Oct 2017 16:30:17 +0300 Subject: [PATCH 18/29] Unmap invisible glyphs when cleaving and no monster present --- src/uhitm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/uhitm.c b/src/uhitm.c index b4d272187..97e49dbca 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -529,8 +529,10 @@ struct attack *uattk; mtmp = NULL; if (isok(x + xdir[i], y + ydir[i])) mtmp = m_at(x + xdir[i], y + ydir[i]); - if (!mtmp) + if (!mtmp) { + (void) unmap_invisible(x + xdir[i], y + ydir[i]); continue; + } tmp = find_roll_to_hit(mtmp, uattk->aatyp, uwep, From a4706b0928460a4ceb70c931b4f1ac6ea0ed9e31 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 7 Oct 2017 17:10:46 +0300 Subject: [PATCH 19/29] Use enums for skills --- include/skills.h | 112 +++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 52 deletions(-) diff --git a/include/skills.h b/include/skills.h index 1c174d5c3..e1807d9d5 100644 --- a/include/skills.h +++ b/include/skills.h @@ -9,8 +9,9 @@ * in a separate file so it can be included in objects.c. */ -/* Code to denote that no skill is applicable */ -#define P_NONE 0 +enum p_skills { + /* Code to denote that no skill is applicable */ + P_NONE = 0, /* Weapon Skills -- Stephen White * Order matters and are used in macros. @@ -19,58 +20,63 @@ * Update weapon.c if you amend any skills. * Also used for oc_subtyp. */ -#define P_DAGGER 1 -#define P_KNIFE 2 -#define P_AXE 3 -#define P_PICK_AXE 4 -#define P_SHORT_SWORD 5 -#define P_BROAD_SWORD 6 -#define P_LONG_SWORD 7 -#define P_TWO_HANDED_SWORD 8 -#define P_SCIMITAR 9 -#define P_SABER 10 -#define P_CLUB 11 /* Heavy-shafted bludgeon */ -#define P_MACE 12 -#define P_MORNING_STAR 13 /* Spiked bludgeon */ -#define P_FLAIL 14 /* Two pieces hinged or chained together */ -#define P_HAMMER 15 /* Heavy head on the end */ -#define P_QUARTERSTAFF 16 /* Long-shafted bludgeon */ -#define P_POLEARMS 17 -#define P_SPEAR 18 /* includes javelin */ -#define P_TRIDENT 19 -#define P_LANCE 20 -#define P_BOW 21 -#define P_SLING 22 -#define P_CROSSBOW 23 -#define P_DART 24 -#define P_SHURIKEN 25 -#define P_BOOMERANG 26 -#define P_WHIP 27 -#define P_UNICORN_HORN 28 /* last weapon */ + P_DAGGER, + P_KNIFE, + P_AXE, + P_PICK_AXE, + P_SHORT_SWORD, + P_BROAD_SWORD, + P_LONG_SWORD, + P_TWO_HANDED_SWORD, + P_SCIMITAR, + P_SABER, + P_CLUB, /* Heavy-shafted bludgeon */ + P_MACE, + P_MORNING_STAR, /* Spiked bludgeon */ + P_FLAIL, /* Two pieces hinged or chained together */ + P_HAMMER, /* Heavy head on the end */ + P_QUARTERSTAFF, /* Long-shafted bludgeon */ + P_POLEARMS, + P_SPEAR, /* includes javelin */ + P_TRIDENT, + P_LANCE, + P_BOW, + P_SLING, + P_CROSSBOW, + P_DART, + P_SHURIKEN, + P_BOOMERANG, + P_WHIP, + P_UNICORN_HORN, /* last weapon */ + + /* Spell Skills added by Larry Stewart-Zerba */ + P_ATTACK_SPELL, + P_HEALING_SPELL, + P_DIVINATION_SPELL, + P_ENCHANTMENT_SPELL, + P_CLERIC_SPELL, + P_ESCAPE_SPELL, + P_MATTER_SPELL, + + /* Other types of combat */ + P_BARE_HANDED_COMBAT, /* actually weaponless; gloves are ok */ + P_TWO_WEAPON_COMBAT, + P_RIDING, /* How well you control your steed */ + + P_NUM_SKILLS +}; + +#define P_MARTIAL_ARTS P_BARE_HANDED_COMBAT /* Role distinguishes */ + #define P_FIRST_WEAPON P_DAGGER #define P_LAST_WEAPON P_UNICORN_HORN -/* Spell Skills added by Larry Stewart-Zerba */ -#define P_ATTACK_SPELL 29 -#define P_HEALING_SPELL 30 -#define P_DIVINATION_SPELL 31 -#define P_ENCHANTMENT_SPELL 32 -#define P_CLERIC_SPELL 33 -#define P_ESCAPE_SPELL 34 -#define P_MATTER_SPELL 35 #define P_FIRST_SPELL P_ATTACK_SPELL #define P_LAST_SPELL P_MATTER_SPELL -/* Other types of combat */ -#define P_BARE_HANDED_COMBAT 36 /* actually weaponless; gloves are ok */ -#define P_MARTIAL_ARTS P_BARE_HANDED_COMBAT /* Role distinguishes */ -#define P_TWO_WEAPON_COMBAT 37 /* Finally implemented */ -#define P_RIDING 38 /* How well you control your steed */ #define P_LAST_H_TO_H P_RIDING #define P_FIRST_H_TO_H P_BARE_HANDED_COMBAT -#define P_NUM_SKILLS (P_LAST_H_TO_H + 1) - /* These roles qualify for a martial arts bonus */ #define martial_bonus() (Role_if(PM_SAMURAI) || Role_if(PM_MONK)) @@ -81,13 +87,15 @@ * with the current skill-1. To work out for the UNSKILLED case, * a value of 0 needed. */ -#define P_ISRESTRICTED 0 -#define P_UNSKILLED 1 -#define P_BASIC 2 -#define P_SKILLED 3 -#define P_EXPERT 4 -#define P_MASTER 5 /* Unarmed combat/martial arts only */ -#define P_GRAND_MASTER 6 /* Unarmed combat/martial arts only */ +enum skill_levels { + P_ISRESTRICTED = 0, + P_UNSKILLED, + P_BASIC, + P_SKILLED, + P_EXPERT, + P_MASTER, /* Unarmed combat/martial arts only */ + P_GRAND_MASTER /* Unarmed combat/martial arts only */ +}; #define practice_needed_to_advance(level) ((level) * (level) *20) From ed335dd0a7470713b91aa299e3667f249543012d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 7 Oct 2017 20:26:02 +0300 Subject: [PATCH 20/29] Add Qt4 windowport Originally by Ray Chason for 3.4.3, based on the Qt windowport by Warwick Allison. The look and feel is mostly the same. Some improvements over the Qt 3 interface are: * Panes are resizable * Full support for IBMgraphics, and walls and corridors are drawn with graphical primitives for a continuous appearance no matter what the font says * Lots of irritating glitches fixed * Menus support proportional fonts correctly Adding this because the old Qt windowport cannot be compiled on Qt4, even with Qt3 compatibility stuff. TODO: - background map glyphs - status hilites - menucolors --- doc/fixes36.1 | 1 + include/extern.h | 3 + src/cmd.c | 9 +- sys/unix/Makefile.src | 93 +++- sys/unix/hints/linux-qt4 | 47 ++ win/Qt4/qt4bind.cpp | 745 ++++++++++++++++++++++++++ win/Qt4/qt4bind.h | 92 ++++ win/Qt4/qt4click.cpp | 48 ++ win/Qt4/qt4click.h | 37 ++ win/Qt4/qt4clust.cpp | 167 ++++++ win/Qt4/qt4clust.h | 29 ++ win/Qt4/qt4delay.cpp | 42 ++ win/Qt4/qt4delay.h | 26 + win/Qt4/qt4glyph.cpp | 141 +++++ win/Qt4/qt4glyph.h | 34 ++ win/Qt4/qt4icon.cpp | 203 ++++++++ win/Qt4/qt4icon.h | 53 ++ win/Qt4/qt4inv.cpp | 104 ++++ win/Qt4/qt4inv.h | 25 + win/Qt4/qt4kde0.h | 14 + win/Qt4/qt4key.cpp | 91 ++++ win/Qt4/qt4key.h | 40 ++ win/Qt4/qt4line.cpp | 42 ++ win/Qt4/qt4line.h | 22 + win/Qt4/qt4main.cpp | 1063 ++++++++++++++++++++++++++++++++++++++ win/Qt4/qt4main.h | 94 ++++ win/Qt4/qt4map.cpp | 964 ++++++++++++++++++++++++++++++++++ win/Qt4/qt4map.h | 81 +++ win/Qt4/qt4menu.cpp | 840 ++++++++++++++++++++++++++++++ win/Qt4/qt4menu.h | 182 +++++++ win/Qt4/qt4msg.cpp | 134 +++++ win/Qt4/qt4msg.h | 42 ++ win/Qt4/qt4plsel.cpp | 502 ++++++++++++++++++ win/Qt4/qt4plsel.h | 45 ++ win/Qt4/qt4rip.cpp | 94 ++++ win/Qt4/qt4rip.h | 30 ++ win/Qt4/qt4set.cpp | 186 +++++++ win/Qt4/qt4set.h | 57 ++ win/Qt4/qt4stat.cpp | 540 +++++++++++++++++++ win/Qt4/qt4stat.h | 106 ++++ win/Qt4/qt4str.cpp | 83 +++ win/Qt4/qt4str.h | 24 + win/Qt4/qt4streq.cpp | 99 ++++ win/Qt4/qt4streq.h | 30 ++ win/Qt4/qt4svsel.cpp | 80 +++ win/Qt4/qt4svsel.h | 21 + win/Qt4/qt4win.cpp | 136 +++++ win/Qt4/qt4win.h | 49 ++ win/Qt4/qt4xcmd.cpp | 134 +++++ win/Qt4/qt4xcmd.h | 31 ++ win/Qt4/qt4yndlg.cpp | 244 +++++++++ win/Qt4/qt4yndlg.h | 34 ++ 52 files changed, 8024 insertions(+), 9 deletions(-) create mode 100644 sys/unix/hints/linux-qt4 create mode 100644 win/Qt4/qt4bind.cpp create mode 100644 win/Qt4/qt4bind.h create mode 100644 win/Qt4/qt4click.cpp create mode 100644 win/Qt4/qt4click.h create mode 100644 win/Qt4/qt4clust.cpp create mode 100644 win/Qt4/qt4clust.h create mode 100644 win/Qt4/qt4delay.cpp create mode 100644 win/Qt4/qt4delay.h create mode 100644 win/Qt4/qt4glyph.cpp create mode 100644 win/Qt4/qt4glyph.h create mode 100644 win/Qt4/qt4icon.cpp create mode 100644 win/Qt4/qt4icon.h create mode 100644 win/Qt4/qt4inv.cpp create mode 100644 win/Qt4/qt4inv.h create mode 100644 win/Qt4/qt4kde0.h create mode 100644 win/Qt4/qt4key.cpp create mode 100644 win/Qt4/qt4key.h create mode 100644 win/Qt4/qt4line.cpp create mode 100644 win/Qt4/qt4line.h create mode 100644 win/Qt4/qt4main.cpp create mode 100644 win/Qt4/qt4main.h create mode 100644 win/Qt4/qt4map.cpp create mode 100644 win/Qt4/qt4map.h create mode 100644 win/Qt4/qt4menu.cpp create mode 100644 win/Qt4/qt4menu.h create mode 100644 win/Qt4/qt4msg.cpp create mode 100644 win/Qt4/qt4msg.h create mode 100644 win/Qt4/qt4plsel.cpp create mode 100644 win/Qt4/qt4plsel.h create mode 100644 win/Qt4/qt4rip.cpp create mode 100644 win/Qt4/qt4rip.h create mode 100644 win/Qt4/qt4set.cpp create mode 100644 win/Qt4/qt4set.h create mode 100644 win/Qt4/qt4stat.cpp create mode 100644 win/Qt4/qt4stat.h create mode 100644 win/Qt4/qt4str.cpp create mode 100644 win/Qt4/qt4str.h create mode 100644 win/Qt4/qt4streq.cpp create mode 100644 win/Qt4/qt4streq.h create mode 100644 win/Qt4/qt4svsel.cpp create mode 100644 win/Qt4/qt4svsel.h create mode 100644 win/Qt4/qt4win.cpp create mode 100644 win/Qt4/qt4win.h create mode 100644 win/Qt4/qt4xcmd.cpp create mode 100644 win/Qt4/qt4xcmd.h create mode 100644 win/Qt4/qt4yndlg.cpp create mode 100644 win/Qt4/qt4yndlg.h diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 8e2d0f486..e6f3e7c78 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -718,6 +718,7 @@ Ray Chason's proper background tiles for lava and water Ray Chason's MS-DOS port restored to functionality with credit to Reddit user b_helyer for the fix to sys/share/pcmain.c Ray Chason's MSDOS port support for some VESA modes +Ray Chason's Qt4 windowport Darshan Shaligram's pet ranged attack Jason Dorje Short's key rebinding Maxime Bacoux's new DUMPLOG: compile-time option to enable logging of diff --git a/include/extern.h b/include/extern.h index 54ec9440c..018094dd4 100644 --- a/include/extern.h +++ b/include/extern.h @@ -168,6 +168,9 @@ E boolean NDECL(status_hilite_menu); /* ### cmd.c ### */ +E int NDECL(doconduct); +E int NDECL(domonability); +E char FDECL(cmd_from_func, (int NDECL((*)))); E boolean FDECL(redraw_cmd, (CHAR_P)); #ifdef USE_TRAMPOLI E int NDECL(doextcmd); diff --git a/src/cmd.c b/src/cmd.c index 6849f2ca4..add6ebd52 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -117,7 +117,6 @@ static int NDECL((*timed_occ_fn)); STATIC_PTR int NDECL(doprev_message); STATIC_PTR int NDECL(timed_occupation); STATIC_PTR int NDECL(doextcmd); -STATIC_PTR int NDECL(domonability); STATIC_PTR int NDECL(dotravel); STATIC_PTR int NDECL(doterrain); STATIC_PTR int NDECL(wiz_wish); @@ -164,9 +163,7 @@ STATIC_DCL boolean FDECL(accept_menu_prefix, (int NDECL((*)))); STATIC_DCL int NDECL(wiz_port_debug); #endif STATIC_PTR int NDECL(wiz_rumor_check); -STATIC_DCL char FDECL(cmd_from_func, (int NDECL((*)))); STATIC_PTR int NDECL(doattributes); -STATIC_PTR int NDECL(doconduct); /**/ STATIC_DCL void FDECL(enlght_line, (const char *, const char *, const char *, const char *)); @@ -523,7 +520,7 @@ extcmd_via_menu() #endif /* TTY_GRAPHICS */ /* #monster command - use special monster ability while polymorphed */ -STATIC_PTR int +int domonability(VOID_ARGS) { if (can_breathe(youmonst.data)) @@ -2734,7 +2731,7 @@ int msgflag; /* for variant message phrasing */ /* KMH, #conduct * (shares enlightenment's tense handling) */ -STATIC_PTR int +int doconduct(VOID_ARGS) { show_conduct(0); @@ -3283,7 +3280,7 @@ dokeylist(VOID_ARGS) destroy_nhwindow(datawin); } -STATIC_OVL char +char cmd_from_func(fn) int NDECL((*fn)); { diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 5488e6106..8c27b612c 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -201,6 +201,22 @@ WINX11OBJ = Window.o dialogs.o winX.o winmap.o winmenu.o winmesg.o \ WINQTSRC = ../win/Qt/qt_win.cpp ../win/Qt/qt_clust.cpp ../win/Qt/qttableview.cpp WINQTOBJ = qt_win.o qt_clust.o qttableview.o tile.o # +# Files for a Qt 4 or 5 port +# +WINQT4SRC = ../win/Qt4/qt4bind.cpp ../win/Qt4/qt4click.cpp \ + ../win/Qt4/qt4clust.cpp ../win/Qt4/qt4delay.cpp \ + ../win/Qt4/qt4glyph.cpp ../win/Qt4/qt4icon.cpp ../win/Qt4/qt4inv.cpp \ + ../win/Qt4/qt4key.cpp ../win/Qt4/qt4line.cpp ../win/Qt4/qt4main.cpp \ + ../win/Qt4/qt4map.cpp ../win/Qt4/qt4menu.cpp ../win/Qt4/qt4msg.cpp \ + ../win/Qt4/qt4plsel.cpp ../win/Qt4/qt4rip.cpp ../win/Qt4/qt4set.cpp \ + ../win/Qt4/qt4stat.cpp ../win/Qt4/qt4str.cpp ../win/Qt4/qt4streq.cpp \ + ../win/Qt4/qt4svsel.cpp ../win/Qt4/qt4win.cpp ../win/Qt4/qt4xcmd.cpp \ + ../win/Qt4/qt4yndlg.cpp +WINQT4OBJ = qt4bind.o qt4click.o qt4clust.o qt4delay.o qt4glyph.o qt4icon.o \ + qt4inv.o qt4key.o qt4line.o qt4main.o qt4map.o qt4menu.o qt4msg.o \ + qt4plsel.o qt4rip.o qt4set.o qt4stat.o qt4str.o qt4streq.o qt4svsel.o \ + qt4win.o qt4xcmd.o qt4yndlg.o tile.o +# # Files for a Gnome port # WINGNOMESRC = ../win/gnome/gnaskstr.c ../win/gnome/gnbind.c \ @@ -253,9 +269,12 @@ WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 # WINX11LIB = -lXaw -lXmu -lXext -lXt -lXpm -lX11 -lm # WINX11LIB = -lXaw -lXmu -lXpm -lXext -lXt -lX11 -lSM -lICE -lm # BSD/OS 2.0 # -# libraries for Qt +# libraries for Qt 3 WINQTLIB = -L$(QTDIR)/lib -lqt # +# libraries for Qt 4 +WINQT4LIB = `pkg-config QtGui --libs` +# # libraries for KDE (with Qt) WINKDELIB = -lkdecore -lkdeui -lXext # @@ -360,7 +379,7 @@ GENCSRC = monstr.c vis_tab.c #tile.c # all windowing-system-dependent .c (for dependencies and such) WINCSRC = $(WINTTYSRC) $(WINX11SRC) $(WINGNOMESRC) $(WINGEMSRC) # all windowing-system-dependent .cpp (for dependencies and such) -WINCXXSRC = $(WINQTSRC) $(WINBESRC) +WINCXXSRC = $(WINQTSRC) $(WINQT4SRC) $(WINBESRC) # Files for window system chaining. Requires SYSCF; include via HINTSRC/HINTOBJ CHAINSRC = ../win/chain/wc_chainin.c ../win/chain/wc_chainout.c \ @@ -486,7 +505,7 @@ objects.o: $(CC) $(CFLAGS) -c objects.c @rm -f $(MAKEDEFS) -# Qt windowport meta-object-compiler output +# Qt 3 windowport meta-object-compiler output qt_kde0.moc: ../include/qt_kde0.h $(QTDIR)/bin/moc -o qt_kde0.moc ../include/qt_kde0.h @@ -496,6 +515,28 @@ qt_win.moc: ../include/qt_win.h qttableview.moc: ../include/qttableview.h $(QTDIR)/bin/moc -o qttableview.moc ../include/qttableview.h +# Qt 4 windowport meta-object-compiler output +qt4kde0.moc : ../win/Qt4/qt4kde0.h + $(QTDIR)/bin/moc -o qt4kde0.moc ../win/Qt4/qt4kde0.h +qt4main.moc : ../win/Qt4/qt4main.h + $(QTDIR)/bin/moc -o qt4main.moc ../win/Qt4/qt4main.h +qt4map.moc : ../win/Qt4/qt4map.h + $(QTDIR)/bin/moc -o qt4map.moc ../win/Qt4/qt4map.h +qt4menu.moc : ../win/Qt4/qt4menu.h + $(QTDIR)/bin/moc -o qt4menu.moc ../win/Qt4/qt4menu.h +qt4msg.moc : ../win/Qt4/qt4msg.h + $(QTDIR)/bin/moc -o qt4msg.moc ../win/Qt4/qt4msg.h +qt4plsel.moc : ../win/Qt4/qt4plsel.h + $(QTDIR)/bin/moc -o qt4plsel.moc ../win/Qt4/qt4plsel.h +qt4set.moc : ../win/Qt4/qt4set.h + $(QTDIR)/bin/moc -o qt4set.moc ../win/Qt4/qt4set.h +qt4stat.moc : ../win/Qt4/qt4stat.h + $(QTDIR)/bin/moc -o qt4stat.moc ../win/Qt4/qt4stat.h +qt4xcmd.moc : ../win/Qt4/qt4xcmd.h + $(QTDIR)/bin/moc -o qt4xcmd.moc ../win/Qt4/qt4xcmd.h +qt4yndlg.moc : ../win/Qt4/qt4yndlg.h + $(QTDIR)/bin/moc -o qt4yndlg.moc ../win/Qt4/qt4yndlg.h + # build monst.o and objects.o before executing '$(MAKE) makedefs' $(MAKEDEFS): $(FIRSTOBJ) \ ../util/makedefs.c $(CONFIG_H) ../include/permonst.h \ @@ -737,6 +778,52 @@ qt_clust.o: ../win/Qt/qt_clust.cpp ../include/qt_clust.h $(CXX) $(CXXFLAGS) -c ../win/Qt/qt_clust.cpp qttableview.o: ../win/Qt/qttableview.cpp ../include/qttableview.h $(CXX) $(CXXFLAGS) -c ../win/Qt/qttableview.cpp +qt4bind.o : ../win/Qt4/qt4bind.cpp $(HACK_H) ../win/Qt4/qt4bind.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4bind.cpp +qt4click.o : ../win/Qt4/qt4click.cpp $(HACK_H) ../win/Qt4/qt4click.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4click.cpp +qt4clust.o : ../win/Qt4/qt4clust.cpp $(HACK_H) ../win/Qt4/qt4clust.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4clust.cpp +qt4delay.o : ../win/Qt4/qt4delay.cpp $(HACK_H) ../win/Qt4/qt4delay.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4delay.cpp +qt4glyph.o : ../win/Qt4/qt4glyph.cpp $(HACK_H) ../win/Qt4/qt4glyph.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4glyph.cpp +qt4icon.o : ../win/Qt4/qt4icon.cpp $(HACK_H) ../win/Qt4/qt4icon.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4icon.cpp +qt4inv.o : ../win/Qt4/qt4inv.cpp $(HACK_H) ../win/Qt4/qt4inv.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4inv.cpp +qt4key.o : ../win/Qt4/qt4key.cpp $(HACK_H) ../win/Qt4/qt4key.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4key.cpp +qt4line.o : ../win/Qt4/qt4line.cpp $(HACK_H) ../win/Qt4/qt4line.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4line.cpp +qt4main.o : ../win/Qt4/qt4main.cpp $(HACK_H) qt4main.moc ../win/Qt4/qt4main.h qt4kde0.moc + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4main.cpp +qt4map.o : ../win/Qt4/qt4map.cpp $(HACK_H) qt4map.moc ../win/Qt4/qt4map.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4map.cpp +qt4menu.o : ../win/Qt4/qt4menu.cpp $(HACK_H) qt4menu.moc ../win/Qt4/qt4menu.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4menu.cpp +qt4msg.o : ../win/Qt4/qt4msg.cpp $(HACK_H) qt4msg.moc ../win/Qt4/qt4msg.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4msg.cpp +qt4plsel.o : ../win/Qt4/qt4plsel.cpp $(HACK_H) qt4plsel.moc ../win/Qt4/qt4plsel.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4plsel.cpp +qt4rip.o : ../win/Qt4/qt4rip.cpp $(HACK_H) ../win/Qt4/qt4rip.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4rip.cpp +qt4set.o : ../win/Qt4/qt4set.cpp $(HACK_H) qt4set.moc ../win/Qt4/qt4set.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4set.cpp +qt4stat.o : ../win/Qt4/qt4stat.cpp $(HACK_H) qt4stat.moc ../win/Qt4/qt4stat.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4stat.cpp +qt4str.o : ../win/Qt4/qt4str.cpp ../win/Qt4/qt4str.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4str.cpp +qt4streq.o : ../win/Qt4/qt4streq.cpp $(HACK_H) ../win/Qt4/qt4streq.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4streq.cpp +qt4svsel.o : ../win/Qt4/qt4svsel.cpp $(HACK_H) ../win/Qt4/qt4svsel.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4svsel.cpp +qt4win.o : ../win/Qt4/qt4win.cpp $(HACK_H) ../win/Qt4/qt4win.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4win.cpp +qt4xcmd.o : ../win/Qt4/qt4xcmd.cpp $(HACK_H) qt4xcmd.moc ../win/Qt4/qt4xcmd.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4xcmd.cpp +qt4yndlg.o : ../win/Qt4/qt4yndlg.cpp $(HACK_H) qt4yndlg.moc ../win/Qt4/qt4yndlg.h + $(CXX) $(CXXFLAGS) -c ../win/Qt4/qt4yndlg.cpp wc_chainin.o: ../win/chain/wc_chainin.c $(HACK_H) $(CC) $(CFLAGS) -c ../win/chain/wc_chainin.c wc_chainout.o: ../win/chain/wc_chainout.c $(HACK_H) diff --git a/sys/unix/hints/linux-qt4 b/sys/unix/hints/linux-qt4 new file mode 100644 index 000000000..0feddb9b5 --- /dev/null +++ b/sys/unix/hints/linux-qt4 @@ -0,0 +1,47 @@ +# +# NetHack 3.6 linux-x11 $NHDT-Date: 1432512814 2015/05/25 00:13:34 $ $NHDT-Branch: master $:$NHDT-Revision: 1.12 $ +# Copyright (c) Kenneth Lorber, Kensington, Maryland, 2007. +# NetHack may be freely redistributed. See license for details. +# +#-PRE +# Linux hints file +# This hints file provides a single-user Qt4 build for Linux, specifically +# for Ubuntu dapper. + + +#PREFIX=/usr +PREFIX=$(wildcard ~)/nh/install +HACKDIR=$(PREFIX)/games/lib/$(GAME)dir +SHELLDIR = $(PREFIX)/games +INSTDIR=$(HACKDIR) +VARDIR = $(HACKDIR) + + +POSTINSTALL= cp -n sys/unix/sysconf $(INSTDIR)/sysconf; $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf; $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf; chmod $(VARFILEPERM) $(INSTDIR)/sysconf; +POSTINSTALL+= bdftopcf win/X11/nh10.bdf > $(INSTDIR)/nh10.pcf; (cd $(INSTDIR); mkfontdir); + +CFLAGS=-O -I../include -DNOTPARMDECL +CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" +CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" +CFLAGS+=-DQT_GRAPHICS -DDEFAULT_WINDOW_SYS=\"Qt\" -DNOTTYGRAPHICS +CFLAGS+=`pkg-config QtGui --cflags` + +LINK=g++ +CXX=g++ + +WINSRC = $(WINQT4SRC) +WINOBJ = $(WINQT4OBJ) +WINLIB = $(WINQT4LIB) + +VARDATND = nhtiles.bmp rip.xpm nhsplash.xpm pet_mark.xbm pilemark.xbm + +QTDIR=/usr + +CHOWN=true +CHGRP=true +VARDIRPERM = 0755 +VARFILEPERM = 0600 +GAMEPERM = 0755 + +# note: needs libxt-dev libxaw7-dev libx11-dev bdftopcf diff --git a/win/Qt4/qt4bind.cpp b/win/Qt4/qt4bind.cpp new file mode 100644 index 000000000..5dc17de99 --- /dev/null +++ b/win/Qt4/qt4bind.cpp @@ -0,0 +1,745 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4bind.cpp -- bindings between the Qt 4 interface and the main code + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#include +#else +#include +#endif +#include "qt4bind.h" +#include "qt4click.h" +#include "qt4delay.h" +#include "qt4xcmd.h" +#include "qt4key.h" +#include "qt4map.h" +#include "qt4menu.h" +#include "qt4msg.h" +#include "qt4plsel.h" +#include "qt4svsel.h" +#include "qt4set.h" +#include "qt4stat.h" +#include "qt4streq.h" +#include "qt4yndlg.h" +#include "qt4str.h" + +extern "C" { +#include "dlb.h" +} + +// temporary +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +// XXX Should be from Options +// +// XXX Hmm. Tricky part is that perhaps some macros should only be active +// XXX when a key is about to be gotten. For example, the user could +// XXX define "-" to do "E-yyyyyyyy\r", but would still need "-" for +// XXX other purposes. Maybe just too bad. +// +static struct key_macro_rec { + int key; + int state; + const char* macro; +} key_macro[]={ + { Qt::Key_F1, 0, "n100." }, // Rest (x100) + { Qt::Key_F2, 0, "n20s" }, // Search (x20) + { Qt::Key_Tab, 0, "\001" }, + { 0, 0, 0 } +}; + +NetHackQtBind::NetHackQtBind(int& argc, char** argv) : +#ifdef KDE + KApplication(argc,argv) +#elif defined(QWS) // not quite the right condition + QPEApplication(argc,argv) +#else + QApplication(argc,argv) +#endif +{ + QPixmap pm("nhsplash.xpm"); + if ( iflags.wc_splash_screen && !pm.isNull() ) { + splash = new QFrame(NULL, + Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint | Qt::WindowStaysOnTopHint ); + QVBoxLayout *vb = new QVBoxLayout(splash); + QLabel *lsplash = new QLabel(splash); + vb->addWidget(lsplash); + lsplash->setAlignment(Qt::AlignCenter); + lsplash->setPixmap(pm); + QLabel* capt = new QLabel("Loading...",splash); + vb->addWidget(capt); + capt->setAlignment(Qt::AlignCenter); + if ( !pm.isNull() ) { + lsplash->setFixedSize(pm.size()); + lsplash->setMask(pm); + } + splash->move((QApplication::desktop()->width()-pm.width())/2, + (QApplication::desktop()->height()-pm.height())/2); + //splash->setGeometry(0,0,100,100); + if ( qt_compact_mode ) { + splash->showMaximized(); + } else { + splash->setFrameStyle(QFrame::WinPanel|QFrame::Raised); + splash->setLineWidth(10); + splash->adjustSize(); + splash->show(); + } + + // force content refresh outside event loop + splash->repaint(); + lsplash->repaint(); + capt->repaint(); + qApp->flush(); + + } else { + splash = 0; + } + main = new NetHackQtMainWindow(keybuffer); + connect(qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit())); + qt_settings=new NetHackQtSettings(main->width(),main->height()); +} + +void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv) +{ + iflags.menu_tab_sep = true; + +#ifdef UNIX +// Userid control +// +// Michael Hohmuth ... +// +// As the game runs setuid games, it must seteuid(getuid()) before +// calling XOpenDisplay(), and reset the euid afterwards. +// Otherwise, it can't read the $HOME/.Xauthority file and whines about +// not being able to open the X display (if a magic-cookie +// authorization mechanism is being used). + + uid_t gamesuid=geteuid(); + seteuid(getuid()); +#endif + + QApplication::setColorSpec(ManyColor); + instance=new NetHackQtBind(*argc,argv); + +#ifdef UNIX + seteuid(gamesuid); +#endif + +#ifdef _WS_WIN_ + // This nethack engine feature should be moved into windowport API + nt_kbhit = NetHackQtBind::qt_kbhit; +#endif +} + +int NetHackQtBind::qt_kbhit() +{ + return !keybuffer.Empty(); +} + + +static bool have_asked = false; + +void NetHackQtBind::qt_player_selection() +{ + if ( !have_asked ) + qt_askname(); +} + +void NetHackQtBind::qt_askname() +{ + have_asked = true; + + // We do it all here, and nothing in askname + + char** saved = get_saved_games(); + int ch = -1; + if ( saved && *saved ) { + if ( splash ) splash->hide(); + NetHackQtSavedGameSelector sgsel((const char**)saved); + ch = sgsel.choose(); + if ( ch >= 0 ) + str_copy(plname, saved[ch], SIZE(plname)); + } + free_saved_games(saved); + + switch (ch) { + case -1: + if ( splash ) splash->hide(); + if (NetHackQtPlayerSelector(keybuffer).Choose()) + return; + case -2: + break; + default: + return; + } + + // Quit + clearlocks(); + qt_exit_nhwindows(0); + nh_terminate(0); +} + +void NetHackQtBind::qt_get_nh_event() +{ +} + +#if defined(QWS) +// Kludge to access lastWindowClosed() signal. +class TApp : public QApplication { +public: + TApp(int& c, char**v) : QApplication(c,v) {} + void lwc() { emit lastWindowClosed(); } +}; +#endif + +void NetHackQtBind::qt_exit_nhwindows(const char *) +{ +#if defined(QWS) + // Avoids bug in SHARP SL5500 + ((TApp*)qApp)->lwc(); + qApp->quit(); +#endif + + delete instance; // ie. qApp +} + +void NetHackQtBind::qt_suspend_nhwindows(const char *) +{ +} + +void NetHackQtBind::qt_resume_nhwindows() +{ +} + +static QVector id_to_window; + +winid NetHackQtBind::qt_create_nhwindow(int type) +{ + winid id; + for (id = 0; id < (winid) id_to_window.size(); id++) { + if ( !id_to_window[(int)id] ) + break; + } + if ( id == (winid) id_to_window.size() ) + id_to_window.resize(id+1); + + NetHackQtWindow* window=0; + + switch (type) { + case NHW_MAP: { + NetHackQtMapWindow2* w=new NetHackQtMapWindow2(clickbuffer); + main->AddMapWindow(w); + window=w; + } break; case NHW_MESSAGE: { + NetHackQtMessageWindow* w=new NetHackQtMessageWindow; + main->AddMessageWindow(w); + window=w; + } break; case NHW_STATUS: { + NetHackQtStatusWindow* w=new NetHackQtStatusWindow; + main->AddStatusWindow(w); + window=w; + } break; case NHW_MENU: + window=new NetHackQtMenuOrTextWindow(mainWidget()); + break; case NHW_TEXT: + window=new NetHackQtTextWindow(mainWidget()); + } + + window->nhid = id; + + // Note: use of isHidden does not work with Qt 2.1 + if ( splash +#if QT_VERSION >= 300 + && !main->isHidden() +#else + && main->isVisible() +#endif + ) + { + delete splash; + splash = 0; + } + + id_to_window[(int)id] = window; + return id; +} + +void NetHackQtBind::qt_clear_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->Clear(); +} + +void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->Display(block); +} + +void NetHackQtBind::qt_destroy_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + main->RemoveWindow(window); + if (window->Destroy()) + delete window; + id_to_window[(int)wid] = 0; +} + +void NetHackQtBind::qt_curs(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->CursorTo(x,y); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PutStr(attr,QString::fromLatin1(text)); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const std::string& text) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PutStr(attr,QString::fromLatin1(text.c_str(), text.size())); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const QString& text) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PutStr(attr,text); +} + +void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist) +{ + NetHackQtTextWindow* window=new NetHackQtTextWindow(mainWidget()); + bool complain = false; + + { + dlb *f; + char buf[BUFSZ]; + char *cr; + + window->Clear(); + f = dlb_fopen(filename, "r"); + if (!f) { + complain = must_exist; + } else { + while (dlb_fgets(buf, BUFSZ, f)) { + if ((cr = index(buf, '\n')) != 0) *cr = 0; +#ifdef MSDOS + if ((cr = index(buf, '\r')) != 0) *cr = 0; +#endif + window->PutStr(ATR_NONE, tabexpand(buf)); + } + window->Display(false); + (void) dlb_fclose(f); + } + } + + if (complain) { + QString message; + message.sprintf("File not found: %s\n",filename); + QMessageBox::warning(NULL, "File Error", message, QMessageBox::Ignore); + } +} + +void NetHackQtBind::qt_start_menu(winid wid) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->StartMenu(); +} + +void NetHackQtBind::qt_add_menu(winid wid, int glyph, + const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr, + const char *str, BOOLEAN_P presel) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->AddMenu(glyph, identifier, ch, gch, attr, + QString::fromLatin1(str), + presel); +} + +void NetHackQtBind::qt_end_menu(winid wid, const char *prompt) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->EndMenu(prompt); +} + +int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + return window->SelectMenu(how,menu_list); +} + +void NetHackQtBind::qt_update_inventory() +{ + if (main) + main->updateInventory(); + /* doesn't work yet + if (program_state.something_worth_saving && flags.perm_invent) + display_inventory(NULL, false); + */ +} + +void NetHackQtBind::qt_mark_synch() +{ +} + +void NetHackQtBind::qt_wait_synch() +{ +} + +void NetHackQtBind::qt_cliparound(int x, int y) +{ + // XXXNH - winid should be a parameter! + qt_cliparound_window(WIN_MAP,x,y); +} + +void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->ClipAround(x,y); +} +void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph,int bkglyph) +{ + /* TODO: bkglyph */ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->PrintGlyph(x,y,glyph); +} +//void NetHackQtBind::qt_print_glyph_compose(winid wid,xchar x,xchar y,int glyph1, int glyph2) +//{ + //NetHackQtWindow* window=id_to_window[(int)wid]; + //window->PrintGlyphCompose(x,y,glyph1,glyph2); +//} + +void NetHackQtBind::qt_raw_print(const char *str) +{ + puts(str); +} + +void NetHackQtBind::qt_raw_print_bold(const char *str) +{ + puts(str); +} + +int NetHackQtBind::qt_nhgetch() +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key arrives. + // + while (keybuffer.Empty()) { + qApp->exec(); + } + + return keybuffer.GetAscii(); +} + +int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod) +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key or map-click arrives. + // + while (keybuffer.Empty() && clickbuffer.Empty()) { + qApp->exec(); + } + if (!keybuffer.Empty()) { + return keybuffer.GetAscii(); + } else { + *x=clickbuffer.NextX(); + *y=clickbuffer.NextY(); + *mod=clickbuffer.NextMod(); + clickbuffer.Get(); + return 0; + } +} + +void NetHackQtBind::qt_nhbell() +{ + QApplication::beep(); +} + +int NetHackQtBind::qt_doprev_message() +{ + // Don't need it - uses scrollbar + // XXX but could make this a shortcut + return 0; +} + +char NetHackQtBind::qt_yn_function(const char *question_, const char *choices, CHAR_P def) +{ + QString question(QString::fromLatin1(question_)); + + if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) { + // Similar to X11 windowport `slow' feature. + + QString message; + char yn_esc_map='\033'; + + if (choices) { + // anything beyond is hidden> + QString choicebuf = choices; + size_t cb = choicebuf.indexOf('\033'); + choicebuf = choicebuf.mid(0U, cb); + message = QString("%1 [%2] ").arg(question, choicebuf); + if (def) message += QString("(%1) ").arg(QChar(def)); + // escape maps to 'q' or 'n' or default, in that order + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + message = question; + } + +#ifdef USE_POPUPS + // Improve some special-cases (DIRKS 08/02/23) + if (strcmp (choices,"ynq") == 0) { + switch (QMessageBox::information (NetHackQtBind::mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2)) + { + case 0: return 'y'; + case 1: return 'n'; + case 2: return 'q'; + } + } + + if (strcmp (choices,"yn") == 0) { + switch (QMessageBox::information(NetHackQtBind::mainWidget(),"NetHack",question,"&Yes", "&No",0,1)) + { + case 0: return 'y'; + case 1: return 'n'; + } + } +#endif + + NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message); + + int result=-1; + while (result<0) { + char ch=NetHackQtBind::qt_nhgetch(); + if (ch=='\033') { + result=yn_esc_map; + } else if (choices && !index(choices,ch)) { + if (def && (ch==' ' || ch=='\r' || ch=='\n')) { + result=def; + } else { + NetHackQtBind::qt_nhbell(); + // and try again... + } + } else { + result=ch; + } + } + + NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE); + + return result; + } else { + NetHackQtYnDialog dialog(mainWidget(),question,choices,def); + return dialog.Exec(); + } +} + +void NetHackQtBind::qt_getlin(const char *prompt, char *line) +{ + NetHackQtStringRequestor requestor(mainWidget(),prompt); + if (!requestor.Get(line)) { + line[0]=0; + } +} + +int NetHackQtBind::qt_get_ext_cmd() +{ + NetHackQtExtCmdRequestor requestor(mainWidget()); + return requestor.get(); +} + +void NetHackQtBind::qt_number_pad(int) +{ + // Ignore. +} + +void NetHackQtBind::qt_delay_output() +{ + NetHackQtDelay delay(15); + delay.wait(); +} + +void NetHackQtBind::qt_start_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_end_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_outrip(winid wid, int how, time_t when) +{ + NetHackQtWindow* window=id_to_window[(int)wid]; + window->UseRIP(how, when); +} + +bool NetHackQtBind::notify(QObject *receiver, QEvent *event) +{ + // Ignore Alt-key navigation to menubar, it's annoying when you + // use Alt-Direction to move around. + if ( main && event->type()==QEvent::KeyRelease && main==receiver + && ((QKeyEvent*)event)->key() == Qt::Key_Alt ) + return true; + + bool result=QApplication::notify(receiver,event); + if (event->type()==QEvent::KeyPress) { + QKeyEvent* key_event=(QKeyEvent*)event; + + if (!key_event->isAccepted()) { + const int k=key_event->key(); + bool macro=false; + for (int i=0; !macro && key_macro[i].key; i++) { + if (key_macro[i].key==k + && ((key_macro[i].state&key_event->modifiers())==key_macro[i].state)) + { + keybuffer.Put(key_macro[i].macro); + macro=true; + } + } + QString key=key_event->text(); + QChar ch = !key.isEmpty() ? key.at(0) : 0; + if (ch > 128) ch = 0; + if ( ch == 0 && (key_event->modifiers() & Qt::ControlModifier) ) { + // On Mac, ascii control codes are not sent, force them. + if ( k>=Qt::Key_A && k<=Qt::Key_Z ) + ch = k - Qt::Key_A + 1; + } + if (!macro && ch != 0) { + bool alt = (key_event->modifiers()&Qt::AltModifier) || + (k >= Qt::Key_0 && k <= Qt::Key_9 && (key_event->modifiers()&Qt::ControlModifier)); + keybuffer.Put(key_event->key(),ch.cell() + (alt ? 128 : 0), + key_event->modifiers()); + key_event->accept(); + result=true; + } + + if (ch != 0 || macro) { + qApp->exit(); + } + } + } + return result; +} + +NetHackQtBind* NetHackQtBind::instance=0; +NetHackQtKeyBuffer NetHackQtBind::keybuffer; +NetHackQtClickBuffer NetHackQtBind::clickbuffer; +NetHackQtMainWindow* NetHackQtBind::main=0; +QFrame* NetHackQtBind::splash=0; + +static void Qt_positionbar(char *) {} + +} // namespace nethack_qt4 + +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_PLAYER_SELECTION | WC_SPLASH_SCREEN, + 0L, + nethack_qt4::NetHackQtBind::qt_init_nhwindows, + nethack_qt4::NetHackQtBind::qt_player_selection, + nethack_qt4::NetHackQtBind::qt_askname, + nethack_qt4::NetHackQtBind::qt_get_nh_event, + nethack_qt4::NetHackQtBind::qt_exit_nhwindows, + nethack_qt4::NetHackQtBind::qt_suspend_nhwindows, + nethack_qt4::NetHackQtBind::qt_resume_nhwindows, + nethack_qt4::NetHackQtBind::qt_create_nhwindow, + nethack_qt4::NetHackQtBind::qt_clear_nhwindow, + nethack_qt4::NetHackQtBind::qt_display_nhwindow, + nethack_qt4::NetHackQtBind::qt_destroy_nhwindow, + nethack_qt4::NetHackQtBind::qt_curs, + nethack_qt4::NetHackQtBind::qt_putstr, + nethack_qt4::NetHackQtBind::qt_putstr, /* FIXME: should be qt_putmixed() */ + nethack_qt4::NetHackQtBind::qt_display_file, + nethack_qt4::NetHackQtBind::qt_start_menu, + nethack_qt4::NetHackQtBind::qt_add_menu, + nethack_qt4::NetHackQtBind::qt_end_menu, + nethack_qt4::NetHackQtBind::qt_select_menu, + genl_message_menu, /* no need for X-specific handling */ + nethack_qt4::NetHackQtBind::qt_update_inventory, + nethack_qt4::NetHackQtBind::qt_mark_synch, + nethack_qt4::NetHackQtBind::qt_wait_synch, +#ifdef CLIPPING + nethack_qt4::NetHackQtBind::qt_cliparound, +#endif +#ifdef POSITIONBAR + nethack_qt4::Qt_positionbar, +#endif + nethack_qt4::NetHackQtBind::qt_print_glyph, + //NetHackQtBind::qt_print_glyph_compose, + nethack_qt4::NetHackQtBind::qt_raw_print, + nethack_qt4::NetHackQtBind::qt_raw_print_bold, + nethack_qt4::NetHackQtBind::qt_nhgetch, + nethack_qt4::NetHackQtBind::qt_nh_poskey, + nethack_qt4::NetHackQtBind::qt_nhbell, + nethack_qt4::NetHackQtBind::qt_doprev_message, + nethack_qt4::NetHackQtBind::qt_yn_function, + nethack_qt4::NetHackQtBind::qt_getlin, + nethack_qt4::NetHackQtBind::qt_get_ext_cmd, + nethack_qt4::NetHackQtBind::qt_number_pad, + nethack_qt4::NetHackQtBind::qt_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + donull, + donull, + donull, + donull, +#endif + /* other defs that really should go away (they're tty specific) */ + nethack_qt4::NetHackQtBind::qt_start_screen, + nethack_qt4::NetHackQtBind::qt_end_screen, +#ifdef GRAPHIC_TOMBSTONE + nethack_qt4::NetHackQtBind::qt_outrip, +#else + genl_outrip, +#endif + genl_preference_update, + + genl_getmsghistory, genl_putmsghistory, + genl_status_init, + genl_status_finish, genl_status_enablefield, +#ifdef STATUS_HILITES + genl_status_update, +#else + genl_status_update, +#endif + genl_can_suspend_yes, +}; + +extern "C" void play_usersound(const char* filename, int volume) +{ +#ifdef USER_SOUNDS +#ifndef QT_NO_SOUND + QSound::play(filename); +#endif +#endif +} diff --git a/win/Qt4/qt4bind.h b/win/Qt4/qt4bind.h new file mode 100644 index 000000000..99a2c0d11 --- /dev/null +++ b/win/Qt4/qt4bind.h @@ -0,0 +1,92 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4bind.h -- bindings between the Qt 4 interface and the main code + +#ifndef QT4BIND_H +#define QT4BIND_H + +#include "qt4main.h" + +namespace nethack_qt4 { + +class NetHackQtClickBuffer; + +#ifdef KDE +#define NetHackQtBindBase KApplication +#elif defined(QWS) +#define NetHackQtBindBase QPEApplication +#else +#define NetHackQtBindBase QApplication +#endif + +class NetHackQtBind : NetHackQtBindBase { +private: + // Single-instance preservation... + NetHackQtBind(int& argc, char** argv); + + static NetHackQtBind* instance; + + static NetHackQtKeyBuffer keybuffer; + static NetHackQtClickBuffer clickbuffer; + + static QFrame* splash; + static NetHackQtMainWindow* main; + +public: + static void qt_init_nhwindows(int* argc, char** argv); + static void qt_player_selection(); + static void qt_askname(); + static void qt_get_nh_event(); + static void qt_exit_nhwindows(const char *); + static void qt_suspend_nhwindows(const char *); + static void qt_resume_nhwindows(); + static winid qt_create_nhwindow(int type); + static void qt_clear_nhwindow(winid wid); + static void qt_display_nhwindow(winid wid, BOOLEAN_P block); + static void qt_destroy_nhwindow(winid wid); + static void qt_curs(winid wid, int x, int y); + static void qt_putstr(winid wid, int attr, const char *text); + static void qt_putstr(winid wid, int attr, const std::string& text); + static void qt_putstr(winid wid, int attr, const QString& text); + static void qt_display_file(const char *filename, BOOLEAN_P must_exist); + static void qt_start_menu(winid wid); + static void qt_add_menu(winid wid, int glyph, + const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr, + const char *str, BOOLEAN_P presel); + static void qt_end_menu(winid wid, const char *prompt); + static int qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list); + static void qt_update_inventory(); + static void qt_mark_synch(); + static void qt_wait_synch(); + + static void qt_cliparound(int x, int y); + static void qt_cliparound_window(winid wid, int x, int y); + static void qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph,int bkglyph); + static void qt_raw_print(const char *str); + static void qt_raw_print_bold(const char *str); + static int qt_nhgetch(); + static int qt_nh_poskey(int *x, int *y, int *mod); + static void qt_nhbell(); + static int qt_doprev_message(); + static char qt_yn_function(const char *question, const char *choices, CHAR_P def); + static void qt_getlin(const char *prompt, char *line); + static int qt_get_ext_cmd(); + static void qt_number_pad(int); + static void qt_delay_output(); + static void qt_start_screen(); + static void qt_end_screen(); + + static void qt_outrip(winid wid, int how, time_t when); + static int qt_kbhit(); + + static QWidget *mainWidget() { return main; } + +private: + virtual bool notify(QObject *receiver, QEvent *event); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4click.cpp b/win/Qt4/qt4click.cpp new file mode 100644 index 000000000..78177a14f --- /dev/null +++ b/win/Qt4/qt4click.cpp @@ -0,0 +1,48 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4click.cpp -- a mouse click buffer + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#include "qt4click.h" + +namespace nethack_qt4 { + +NetHackQtClickBuffer::NetHackQtClickBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtClickBuffer::Empty() const { return in==out; } +bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; } + +void NetHackQtClickBuffer::Put(int x, int y, int mod) +{ + click[in].x=x; + click[in].y=y; + click[in].mod=mod; + in=(in+1)%maxclick; +} + +int NetHackQtClickBuffer::NextX() const { return click[out].x; } +int NetHackQtClickBuffer::NextY() const { return click[out].y; } +int NetHackQtClickBuffer::NextMod() const { return click[out].mod; } + +void NetHackQtClickBuffer::Get() +{ + out=(out+1)%maxclick; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4click.h b/win/Qt4/qt4click.h new file mode 100644 index 000000000..50fd8b1cc --- /dev/null +++ b/win/Qt4/qt4click.h @@ -0,0 +1,37 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4click.h -- a mouse click buffer + +#ifndef QT4CLICK_H +#define QT4CLICK_H + +namespace nethack_qt4 { + +class NetHackQtClickBuffer { +public: + NetHackQtClickBuffer(); + + bool Empty() const; + bool Full() const; + + void Put(int x, int y, int mod); + + int NextX() const; + int NextY() const; + int NextMod() const; + + void Get(); + +private: + enum { maxclick=64 }; + struct ClickRec { + int x,y,mod; + } click[maxclick]; + int in,out; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4clust.cpp b/win/Qt4/qt4clust.cpp new file mode 100644 index 000000000..1cd080c11 --- /dev/null +++ b/win/Qt4/qt4clust.cpp @@ -0,0 +1,167 @@ +/* SCCS Id: @(#)qt_clust.cpp 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ +#include "qt4clust.h" + +static void include(QRect& r, const QRect& rect) +{ + if (rect.left()r.right()) { + r.setRight(rect.right()); + } + if (rect.top()r.bottom()) { + r.setBottom(rect.bottom()); + } +} + +/* +A Clusterizer groups rectangles (QRects) into non-overlapping rectangles +by a merging heuristic. +*/ +Clusterizer::Clusterizer(int maxclusters) : + cluster(new QRect[maxclusters]), + count(0), + max(maxclusters) +{ } + +Clusterizer::~Clusterizer() +{ + delete [] cluster; +} + +void Clusterizer::clear() +{ + count=0; +} + +void Clusterizer::add(int x, int y) +{ + add(QRect(x,y,1,1)); +} + +void Clusterizer::add(int x, int y, int w, int h) +{ + add(QRect(x,y,w,h)); +} + +void Clusterizer::add(const QRect& rect) +{ + QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2); + + //assert(rect.width()>0 && rect.height()>0); + + int cursor; + + for (cursor=0; cursor=0) { + include(cluster[cheapest],rect); + return; + } + + if (count < max) { + cluster[count++]=rect; + return; + } + + // Do cheapest of: + // add to closest cluster + // do cheapest cluster merge, add to new cluster + + lowestcost=9999999; + cheapest=-1; + for (cursor=0; cursor=0) { + include(cluster[cheapestmerge1],cluster[cheapestmerge2]); + cluster[cheapestmerge2]=cluster[count--]; + } else { + // if (!cheapest) debugRectangles(rect); + include(cluster[cheapest],rect); + } + + // NB: clusters do not intersect (or intersection will + // overwrite). This is a result of the above algorithm, + // given the assumption that (x,y) are ordered topleft + // to bottomright. +} + +const QRect& Clusterizer::operator[](int i) +{ + return cluster[i]; +} diff --git a/win/Qt4/qt4clust.h b/win/Qt4/qt4clust.h new file mode 100644 index 000000000..c7112ea11 --- /dev/null +++ b/win/Qt4/qt4clust.h @@ -0,0 +1,29 @@ +/* SCCS Id: @(#)qt_clust.h 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef clusterizer_H +#define clusterizer_H + +#include + +class Clusterizer { +public: + Clusterizer(int maxclusters); + ~Clusterizer(); + + void add(int x, int y); // 1x1 rectangle (point) + void add(int x, int y, int w, int h); + void add(const QRect& rect); + + void clear(); + int clusters() { return count; } + const QRect& operator[](int i); + +private: + QRect* cluster; + int count; + const int max; +}; + +#endif diff --git a/win/Qt4/qt4delay.cpp b/win/Qt4/qt4delay.cpp new file mode 100644 index 000000000..eadf42a94 --- /dev/null +++ b/win/Qt4/qt4delay.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4delay.cpp -- implement a delay + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#include "qt4delay.h" + +namespace nethack_qt4 { + +// RLC Can we use QTimer::single_shot for this? +NetHackQtDelay::NetHackQtDelay(int ms) : + msec(ms), m_timer(0), m_loop(this) +{ +} + +void NetHackQtDelay::wait() +{ + m_timer = startTimer(msec); + m_loop.exec(); +} + +void NetHackQtDelay::timerEvent(QTimerEvent* timer) +{ + m_loop.exit(); + killTimer(m_timer); + m_timer = 0; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4delay.h b/win/Qt4/qt4delay.h new file mode 100644 index 000000000..2e0085edf --- /dev/null +++ b/win/Qt4/qt4delay.h @@ -0,0 +1,26 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4delay.h -- implement a delay + +#ifndef QT4DELAY_H +#define QT4DELAY_H + +namespace nethack_qt4 { + +class NetHackQtDelay : QObject { +private: + int msec; + int m_timer; + QEventLoop m_loop; + +public: + NetHackQtDelay(int ms); + void wait(); + virtual void timerEvent(QTimerEvent* timer); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4glyph.cpp b/win/Qt4/qt4glyph.cpp new file mode 100644 index 000000000..3ba56be6f --- /dev/null +++ b/win/Qt4/qt4glyph.cpp @@ -0,0 +1,141 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4glyph.cpp -- class to manage the glyphs in a tile set + +extern "C" { +#include "hack.h" +} +#include "tile2x11.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4glyph.h" +#include "qt4set.h" +#include "qt4str.h" + +extern short glyph2tile[]; // from tile.c + +namespace nethack_qt4 { + +static int tilefile_tile_W=16; +static int tilefile_tile_H=16; + +// Debian uses a separate PIXMAPDIR +#ifndef PIXMAPDIR +# ifdef HACKDIR +# define PIXMAPDIR HACKDIR +# else +# define PIXMAPDIR "." +# endif +#endif + +NetHackQtGlyphs::NetHackQtGlyphs() +{ + const char* tile_file = PIXMAPDIR "/nhtiles.bmp"; + if ( iflags.wc_tile_file ) + tile_file = iflags.wc_tile_file; + + if (!img.load(tile_file)) { + tile_file = PIXMAPDIR "/x11tiles"; + if (!img.load(tile_file)) { + QString msg; + msg.sprintf("Cannot load x11tiles or nhtiles.bmp"); + QMessageBox::warning(0, "IO Error", msg); + } else { + tiles_per_row = TILES_PER_ROW; + if (img.width()%tiles_per_row) { + impossible("Tile file \"%s\" has %d columns, not multiple of row count (%d)", + tile_file, img.width(), tiles_per_row); + } + } + } else { + tiles_per_row = 40; + } + + if ( iflags.wc_tile_width ) + tilefile_tile_W = iflags.wc_tile_width; + else + tilefile_tile_W = img.width() / tiles_per_row; + if ( iflags.wc_tile_height ) + tilefile_tile_H = iflags.wc_tile_height; + else + tilefile_tile_H = tilefile_tile_W; + + setSize(tilefile_tile_W, tilefile_tile_H); +} + +void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y) +{ + int tile = glyph2tile[glyph]; + int px = (tile%tiles_per_row)*width(); + int py = tile/tiles_per_row*height(); + + painter.drawPixmap( + x, + y, + pm, + px,py, + width(),height() + ); +} +void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly) +{ + drawGlyph(painter,glyph,cellx*width(),celly*height()); +} +QPixmap NetHackQtGlyphs::glyph(int glyph) +{ + int tile = glyph2tile[glyph]; + int px = (tile%tiles_per_row)*tilefile_tile_W; + int py = tile/tiles_per_row*tilefile_tile_H; + + return QPixmap::fromImage(img.copy(px, py, tilefile_tile_W, tilefile_tile_H)); +} +void NetHackQtGlyphs::setSize(int w, int h) +{ + if ( size == QSize(w,h) ) + return; + + bool was1 = size == pm1.size(); + size = QSize(w,h); + if (!w || !h) + return; // Still not decided + + if ( size == pm1.size() ) { + pm = pm1; + return; + } + if ( size == pm2.size() ) { + pm = pm2; + return; + } + + if (w==tilefile_tile_W && h==tilefile_tile_H) { + pm.convertFromImage(img); + } else { + QApplication::setOverrideCursor( Qt::WaitCursor ); + QImage scaled = img.scaled( + w*img.width()/tilefile_tile_W, + h*img.height()/tilefile_tile_H, + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation + ); + pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither); + QApplication::restoreOverrideCursor(); + } + (was1 ? pm2 : pm1) = pm; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4glyph.h b/win/Qt4/qt4glyph.h new file mode 100644 index 000000000..12fd915fd --- /dev/null +++ b/win/Qt4/qt4glyph.h @@ -0,0 +1,34 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4glyph.h -- class to manage the glyphs in a tile set + +#ifndef QT4GLYPH_H +#define QT4GLYPH_H + +namespace nethack_qt4 { + +class NetHackQtGlyphs { +public: + NetHackQtGlyphs(); + + int width() const { return size.width(); } + int height() const { return size.height(); } + void toggleSize(); + void setSize(int w, int h); + + void drawGlyph(QPainter&, int glyph, int pixelx, int pixely); + void drawCell(QPainter&, int glyph, int cellx, int celly); + QPixmap glyph(int glyph); + +private: + QImage img; + QPixmap pm,pm1, pm2; + QSize size; + int tiles_per_row; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4icon.cpp b/win/Qt4/qt4icon.cpp new file mode 100644 index 000000000..0876cb59d --- /dev/null +++ b/win/Qt4/qt4icon.cpp @@ -0,0 +1,203 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4icon.cpp -- a labelled icon + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4icon.h" + +namespace nethack_qt4 { + +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) : + QWidget(parent), + low_is_good(false), + prev_value(-123), + turn_count(-1), + label(new QLabel(l,this)), + icon(0) +{ + initHighlight(); +} + +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) : + QWidget(parent), + low_is_good(false), + prev_value(-123), + turn_count(-1), + label(new QLabel(l,this)), + icon(new QLabel(this)) +{ + setIcon(i); + initHighlight(); +} + +void NetHackQtLabelledIcon::initHighlight() +{ + hl_good = "QLabel { background-color : green; color : white }"; + hl_bad = "QLabel { background-color : red ; color : white }"; +} + +void NetHackQtLabelledIcon::setLabel(const QString& t, bool lower) +{ + if (!label) { + label=new QLabel(this); + label->setFont(font()); + resizeEvent(0); + } + if (label->text() != t) { + label->setText(t); + highlight(lower==low_is_good ? hl_good : hl_bad); + } +} + +void NetHackQtLabelledIcon::setLabel(const QString& t, long v, long cv, const QString& tail) +{ + QString buf; + if (v==NoNum) { + buf = ""; + } else { + buf.sprintf("%ld", v); + } + setLabel(t + buf + tail, cv < prev_value); + prev_value=cv; +} + +void NetHackQtLabelledIcon::setLabel(const QString& t, long v, const QString& tail) +{ + setLabel(t,v,v,tail); +} + +void NetHackQtLabelledIcon::setIcon(const QPixmap& i) +{ + if (icon) icon->setPixmap(i); + else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); } + icon->resize(i.width(),i.height()); +} + +void NetHackQtLabelledIcon::setFont(const QFont& f) +{ + QWidget::setFont(f); + if (label) label->setFont(f); +} + +void NetHackQtLabelledIcon::show() +{ +#if QT_VERSION >= 300 + if (isHidden()) +#else + if (!isVisible()) +#endif + highlight(hl_bad); + QWidget::show(); +} + +QSize NetHackQtLabelledIcon::sizeHint() const +{ + QSize iconsize, textsize; + + if (label && !icon) return label->sizeHint(); + if (icon && !label) return icon->sizeHint(); + if (!label && !icon) return QWidget::sizeHint(); + + iconsize = icon->sizeHint(); + textsize = label->sizeHint(); + return QSize( + std::max(iconsize.width(), textsize.width()), + iconsize.height() + textsize.height()); +} + +QSize NetHackQtLabelledIcon::minimumSizeHint() const +{ + QSize iconsize, textsize; + + if (label && !icon) return label->minimumSizeHint(); + if (icon && !label) return icon->minimumSizeHint(); + if (!label && !icon) return QWidget::minimumSizeHint(); + + iconsize = icon->minimumSizeHint(); + textsize = label->minimumSizeHint(); + return QSize( + std::max(iconsize.width(), textsize.width()), + iconsize.height() + textsize.height()); +} + +void NetHackQtLabelledIcon::highlightWhenChanging() +{ + turn_count=0; +} + +void NetHackQtLabelledIcon::lowIsGood() +{ + low_is_good=true; +} + +void NetHackQtLabelledIcon::dissipateHighlight() +{ + if (turn_count>0) { + turn_count--; + if (!turn_count) + unhighlight(); + } +} + +void NetHackQtLabelledIcon::highlight(const QString& hl) +{ + if (label) { // Surely it is?! + if (turn_count>=0) { + label->setStyleSheet(hl); + turn_count=4; + // `4' includes this turn, so dissipates after + // 3 more keypresses. + } else { + label->setStyleSheet(""); + } + } +} + +void NetHackQtLabelledIcon::unhighlight() +{ + if (label) { // Surely it is?! + label->setStyleSheet(""); + } +} + +void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*) +{ + setAlignments(); + + //int labw=label ? label->fontMetrics().width(label->text()) : 0; + int labh=label ? label->fontMetrics().height() : 0; + int icoh=icon ? icon->height() : 0; + int h=icoh+labh; + int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2); + int laby=icoy+icoh; + if (icon) { + icon->setGeometry(0,icoy,width(),icoh); + } + if (label) { + label->setGeometry(0,laby,width(),labh); + } +} + +void NetHackQtLabelledIcon::setAlignments() +{ + if (label) label->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter); + if (icon) icon->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4icon.h b/win/Qt4/qt4icon.h new file mode 100644 index 000000000..bdaf8183c --- /dev/null +++ b/win/Qt4/qt4icon.h @@ -0,0 +1,53 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4icon.cpp -- a labelled icon + +#ifndef QT4ICON_H +#define QT4ICON_H + +namespace nethack_qt4 { + +class NetHackQtLabelledIcon : public QWidget { +public: + NetHackQtLabelledIcon(QWidget* parent, const char* label); + NetHackQtLabelledIcon(QWidget* parent, const char* label, const QPixmap& icon); + + enum { NoNum=-99999 }; + void setLabel(const QString&, bool lower=true); // a string + void setLabel(const QString&, long, const QString& tail=""); // a number + void setLabel(const QString&, long show_value, long comparative_value, const QString& tail=""); + void setIcon(const QPixmap&); + virtual void setFont(const QFont&); + + void highlightWhenChanging(); + void lowIsGood(); + void dissipateHighlight(); + + virtual void show(); + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + +protected: + void resizeEvent(QResizeEvent*); + +private: + void initHighlight(); + void setAlignments(); + void highlight(const QString& highlight); + void unhighlight(); + + bool low_is_good; + int prev_value; + int turn_count; /* last time the value changed */ + QString hl_good; + QString hl_bad; + + QLabel* label; + QLabel* icon; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4inv.cpp b/win/Qt4/qt4inv.cpp new file mode 100644 index 000000000..9165d58b3 --- /dev/null +++ b/win/Qt4/qt4inv.cpp @@ -0,0 +1,104 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4inv.cpp -- inventory usage window +// This is at the top center of the main window + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4inv.h" +#include "qt4glyph.h" +#include "qt4set.h" + +namespace nethack_qt4 { + +NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) : + QWidget(parent) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe) +{ + short int glyph; + if (nhobj) + glyph=obj_to_glyph(nhobj); + else if (canbe) + glyph=cmap_to_glyph(S_room); + else + glyph=cmap_to_glyph(S_stone); + + qt_settings->glyphs().drawCell(painter,glyph,x,y); +} + +void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*) +{ + // 012 + // + //0 WhB + //1 s"w + //2 gCg + //3 =A= + //4 T + //5 S + + QPainter painter; + painter.begin(this); + + // Blanks + drawWorn(painter,0,0,4,false); + drawWorn(painter,0,0,5,false); + drawWorn(painter,0,2,4,false); + drawWorn(painter,0,2,5,false); + + drawWorn(painter,uarm,1,3); // Armour + drawWorn(painter,uarmc,1,2); // Cloak + drawWorn(painter,uarmh,1,0); // Helmet + drawWorn(painter,uarms,0,1); // Shield + drawWorn(painter,uarmg,0,2); // Gloves - repeated + drawWorn(painter,uarmg,2,2); // Gloves - repeated +#ifdef TOURIST + drawWorn(painter,uarmf,1,5); // Shoes (feet) + drawWorn(painter,uarmu,1,4); // Undershirt +#else + drawWorn(painter,0 ,1,5,false); + drawWorn(painter,uarmf,1,4); // Shoes (feet) +#endif + drawWorn(painter,uleft,0,3); // RingL + drawWorn(painter,uright,2,3); // RingR + + drawWorn(painter,uwep,2,1); // Weapon + drawWorn(painter,uswapwep,0,0); // Secondary weapon + drawWorn(painter,uamul,1,1); // Amulet + drawWorn(painter,ublindf,2,0); // Blindfold + + painter.end(); +} + +QSize NetHackQtInvUsageWindow::sizeHint(void) const +{ + if (qt_settings) { + return QSize(qt_settings->glyphs().width()*3, + qt_settings->glyphs().height()*6); + } else { + return QWidget::sizeHint(); + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4inv.h b/win/Qt4/qt4inv.h new file mode 100644 index 000000000..51cdd4d0b --- /dev/null +++ b/win/Qt4/qt4inv.h @@ -0,0 +1,25 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4inv.h -- inventory usage window +// This is at the top center of the main window + +#ifndef QT4INV_H +#define QT4INV_H + +namespace nethack_qt4 { + +class NetHackQtInvUsageWindow : public QWidget { +public: + NetHackQtInvUsageWindow(QWidget* parent); + virtual void paintEvent(QPaintEvent*); + virtual QSize sizeHint(void) const; + +private: + void drawWorn(QPainter& painter, obj*, int x, int y, bool canbe=true); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4kde0.h b/win/Qt4/qt4kde0.h new file mode 100644 index 000000000..27a678c01 --- /dev/null +++ b/win/Qt4/qt4kde0.h @@ -0,0 +1,14 @@ +/* SCCS Id: @(#)qt_kde0.h 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef QT_DUMMYKDE +#define QT_DUMMYKDE +namespace nethack_qt4 { + +class KTopLevelWidget : public QMainWindow { + Q_OBJECT +}; + +} +#endif diff --git a/win/Qt4/qt4key.cpp b/win/Qt4/qt4key.cpp new file mode 100644 index 000000000..ff16616fe --- /dev/null +++ b/win/Qt4/qt4key.cpp @@ -0,0 +1,91 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4key.cpp -- a key buffer + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#include "qt4key.h" + +namespace nethack_qt4 { + +NetHackQtKeyBuffer::NetHackQtKeyBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtKeyBuffer::Empty() const { return in==out; } +bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; } + +void NetHackQtKeyBuffer::Put(int k, int a, int state) +{ + if ( Full() ) return; // Safety + key[in]=k; + ascii[in]=a; + in=(in+1)%maxkey; +} + +void NetHackQtKeyBuffer::Put(char a) +{ + Put(0,a,0); +} + +void NetHackQtKeyBuffer::Put(const char* str) +{ + while (*str) Put(*str++); +} + +int NetHackQtKeyBuffer::GetKey() +{ + if ( Empty() ) return 0; + int r=TopKey(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::GetAscii() +{ + if ( Empty() ) return 0; // Safety + int r=TopAscii(); + out=(out+1)%maxkey; + return r; +} + +Qt::KeyboardModifiers NetHackQtKeyBuffer::GetState() +{ + if ( Empty() ) return 0; + Qt::KeyboardModifiers r=TopState(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::TopKey() const +{ + if ( Empty() ) return 0; + return key[out]; +} + +int NetHackQtKeyBuffer::TopAscii() const +{ + if ( Empty() ) return 0; + return ascii[out]; +} + +Qt::KeyboardModifiers NetHackQtKeyBuffer::TopState() const +{ + if ( Empty() ) return 0; + return state[out]; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4key.h b/win/Qt4/qt4key.h new file mode 100644 index 000000000..0333269cd --- /dev/null +++ b/win/Qt4/qt4key.h @@ -0,0 +1,40 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4key.h -- a key buffer + +#ifndef QT4KEY_H +#define QT4KEY_H + +namespace nethack_qt4 { + +class NetHackQtKeyBuffer { +public: + NetHackQtKeyBuffer(); + + bool Empty() const; + bool Full() const; + + void Put(int k, int ascii, int state); + void Put(char a); + void Put(const char* str); + int GetKey(); + int GetAscii(); + Qt::KeyboardModifiers GetState(); + + int TopKey() const; + int TopAscii() const; + Qt::KeyboardModifiers TopState() const; + +private: + enum { maxkey=64 }; + int key[maxkey]; + int ascii[maxkey]; + Qt::KeyboardModifiers state[maxkey]; + int in,out; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4line.cpp b/win/Qt4/qt4line.cpp new file mode 100644 index 000000000..dd9b18c5f --- /dev/null +++ b/win/Qt4/qt4line.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4line.cpp -- a one line input window + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4line.h" + +namespace nethack_qt4 { + +NetHackQtLineEdit::NetHackQtLineEdit() : + QLineEdit(0) +{ +} + +NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) : + QLineEdit(parent) +{ +} + +void NetHackQtLineEdit::fakeEvent(int key, int ascii, Qt::KeyboardModifiers state) +{ + QKeyEvent fake(QEvent::KeyPress,key,state,QChar(ascii)); + keyPressEvent(&fake); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4line.h b/win/Qt4/qt4line.h new file mode 100644 index 000000000..bb5067f79 --- /dev/null +++ b/win/Qt4/qt4line.h @@ -0,0 +1,22 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4line.h -- a one line input window + +#ifndef QT4LINE_H +#define QT4LINE_H + +namespace nethack_qt4 { + +class NetHackQtLineEdit : public QLineEdit { +public: + NetHackQtLineEdit(); + NetHackQtLineEdit(QWidget* parent, const char* name); + + void fakeEvent(int key, int ascii, Qt::KeyboardModifiers state); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4main.cpp b/win/Qt4/qt4main.cpp new file mode 100644 index 000000000..ec7e58324 --- /dev/null +++ b/win/Qt4/qt4main.cpp @@ -0,0 +1,1063 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4main.cpp -- the main window + +extern "C" { +#include "hack.h" +} +#include "patchlevel.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4main.h" +#include "qt4main.moc" +#include "qt4bind.h" +#include "qt4glyph.h" +#include "qt4inv.h" +#include "qt4key.h" +#include "qt4map.h" +#include "qt4msg.h" +#include "qt4set.h" +#include "qt4stat.h" +#include "qt4str.h" + +#ifndef KDE +#include "qt4kde0.moc" +#endif + +// temporary +extern char *qt_tilewidth; +extern char *qt_tileheight; +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +// temporary +void centerOnMain( QWidget* w ); +// end temporary + +/* XPM */ +static const char * nh_icon[] = { +"40 40 6 1", +" s None c none", +". c #ffffff", +"X c #dadab6", +"o c #6c91b6", +"O c #476c6c", +"+ c #000000", +" ", +" ", +" ", +" . .X..XX.XX X ", +" .. .....X.XXXXXX XX ", +" ... ....X..XX.XXXXX XXX ", +" .. ..........X.XXXXXXXXXXX XX ", +" .... ........X..XX.XXXXXXXXX XXXX ", +" .... ..........X.XXXXXXXXXXX XXXX ", +" ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ", +" ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ", +" ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ", +" ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ", +" ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ", +" ++++..ooooooooOoOOOOOOOOOXX+++ +++ ", +" +++..ooooooOooOOoOOOOOOOXX+++ + ", +" ++..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..oooooOooOOoOOOOOOXX+++ ", +" ..oooooooOoOOOOOOOOXX+++ ", +" ..ooooOooOOoOOOOOXX+++ ", +" ..ooooooOoOOOOOOOXX++++ ", +" ..o..oooOooOOoOOOOXX+XX+++ ", +" ...o..oooooOoOOOOOXX++XXX++ ", +" ....OO..ooOooOOoOOXX+++XXXX++ ", +" ...oo..+..oooOoOOOXX++XXooXXX++ ", +" ...ooo..++..OooOOoXX+++XXooOXXX+ ", +" ..oooOOXX+++....XXXX++++XXOOoOOXX+ ", +" ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ", +" ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ", +" .....XXX++++ XXXXXXX++ ", +" ....XX++++ XXXXXXX+ ", +" ...XX+++ XXXXX++ ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * nh_icon_small[] = { +/* width height ncolors chars_per_pixel */ +"16 16 16 1", +/* colors */ +" c #587070", +". c #D1D5C9", +"X c #8B8C84", +"o c #2A2A28", +"O c #9AABA9", +"+ c #6A8FB2", +"@ c #C4CAC4", +"# c #B6BEB6", +"$ c None", +"% c #54564E", +"& c #476C6C", +"* c #ADB2AB", +"= c #ABABA2", +"- c #5E8295", +"; c #8B988F", +": c #E8EAE7", +/* pixels */ +"$$$$$$$$$$$$$$$$", +"$$$.$#::.#==*$$$", +"$.*:::::....#*=$", +"$@#:..@#*==#;XX;", +"$@O:+++- &&; X%X", +"$#%.+++- &&;% oX", +"$$o.++-- &&;%%X$", +"$$$:++-- &&;%%$$", +"$$$.O++- &&=o $$", +"$$$=:++- & XoX$$", +"$$*:@O-- ;%Xo$$", +"$*:O#$+--;oOOX $", +"$:+ =o::=oo=-;%X", +"$::.%o$*;X;##@%$", +"$$@# ;$$$$$=*;X$", +"$$$$$$$$$$$$$$$$" +}; + +#if 0 // RLC +/* XPM */ +static const char * map_xpm[] = { +"12 13 4 1", +". c None", +" c #000000000000", +"X c #0000B6DAFFFF", +"o c #69A69248B6DA", +" .", +" XXXXX ooo ", +" XoooX o ", +" XoooX o o ", +" XoooX ooo ", +" XXoXX o ", +" oooooXXX ", +" oo o oooX ", +" o XooX ", +" oooo XooX ", +" o o XXXX ", +" ", +". "}; +/* XPM */ +static const char * msg_xpm[] = { +"12 13 4 1", +". c None", +" c #FFFFFFFFFFFF", +"X c #69A69248B6DA", +"o c #000000000000", +" .", +" XXX XXX X o", +" o", +" XXXXX XX o", +" o", +" XX XXXXX o", +" o", +" XXXXXX o", +" o", +" XX XXX XX o", +" o", +" o", +".ooooooooooo"}; +/* XPM */ +static const char * stat_xpm[] = { +"12 13 5 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +"o c #FFFFFFFF0000", +"O c #69A6FFFF0000", +" ", +" ", +"... ", +"...X ", +"...X ... ", +"oooX oooX", +"oooXooo oooX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +" XXXXXXXXXXX"}; +#endif +/* XPM */ +static const char * info_xpm[] = { +"12 13 4 1", +" c None", +". c #00000000FFFF", +"X c #FFFFFFFFFFFF", +"o c #000000000000", +" ... ", +" ....... ", +" ...XXX... ", +" .........o ", +"...XXXX.... ", +"....XXX....o", +"....XXX....o", +"....XXX....o", +" ...XXX...oo", +" ..XXXXX..o ", +" .......oo ", +" o...ooo ", +" ooo "}; + + +/* XPM */ +static const char * again_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" .. ", +" .. ", +" ..... ", +" ....... ", +"... .. .. ", +".. .. .. ", +".. ..", +".. ..", +".. ..", +" .. .. ", +" .......... ", +" ...... ", +" "}; +/* XPM */ +static const char * kick_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" ", +" . . . ", +" ... . . ", +" ... . ", +" ... . ", +" ... ", +"XXX ... ", +"XXX. ... ", +"XXX. ... ", +"XXX. .. ", +" ... ", +" "}; +/* XPM */ +static const char * throw_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ", +" ", +" ", +".... X ", +"....X X ", +"....X XXXXXX", +"....X X ", +" XXXX X ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * fire_xpm[] = { +"12 13 5 1", +" c None", +". c #B6DA45140000", +"X c #FFFFB6DA9658", +"o c #000000000000", +"O c #FFFF6DB60000", +" . ", +" X. ", +" X . ", +" X .o ", +" X . o ", +" X .o o ", +"OOOOOOOOoooo", +" X .o o ", +" X . o o ", +" X .o ", +" X. o ", +" . o ", +" o "}; +/* XPM */ +static const char * get_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" . ", +" ... ", +" . . . ", +" . ", +" . ", +" ", +" XXXXX ", +" XXXXX. ", +" XXXXX. ", +" XXXXX. ", +" ..... ", +" "}; +/* XPM */ +static const char * drop_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ..... ", +" .....X ", +" .....X ", +" .....X ", +" XXXXX ", +" ", +" X ", +" X ", +" X X X ", +" XXX ", +" X ", +" "}; +/* XPM */ +static const char * eat_xpm[] = { +"12 13 4 1", +" c None", +". c #000000000000", +"X c #FFFFB6DA9658", +"o c #FFFF6DB60000", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" ... .. ", +" .. .. ", +" .. .. ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo "}; +/* XPM */ +static const char * rest_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" ..... ", +" . ", +" . ", +" . ....", +" ..... . ", +" . ", +" ....", +" ", +" .... ", +" . ", +" . ", +" .... ", +" "}; +/* XPM */ +static const char * cast_a_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X X ", +" .. XXXX ", +" . X X ", +" . X X "}; +/* XPM */ +static const char * cast_b_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XXX ", +" .. X X ", +" .. XXX ", +" .. X X ", +" . X X ", +" . XXX "}; +/* XPM */ +static const char * cast_c_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X ", +" .. X ", +" . X X ", +" . XX "}; + +static QString +aboutMsg() +{ + QString msg; + msg.sprintf( + "Qt NetHack is a version of NetHack built\n" +#ifdef KDE + "using KDE and the Qt GUI toolkit.\n" +#else + "using the Qt GUI toolkit.\n" +#endif + "This is version %d.%d.%d\n\n" + "Homepage:\n http://trolls.troll.no/warwick/nethack/\n\n" +#ifdef KDE + "KDE:\n http://www.kde.org\n" +#endif + "Qt:\n http://www.troll.no", + VERSION_MAJOR, + VERSION_MINOR, + PATCHLEVEL); + return msg; +} + +class SmallToolButton : public QToolButton { +public: + SmallToolButton(const QPixmap & pm, const QString &textLabel, + const QString& grouptext, + QObject * receiver, const char* slot, + QWidget * parent) : + QToolButton(parent) + { + setIcon(QIcon(pm)); + setToolTip(textLabel); + setStatusTip(grouptext); + connect(this, SIGNAL(clicked(bool)), receiver, slot); + } + + QSize sizeHint() const + { + // get just a couple more pixels for the map + return QToolButton::sizeHint()-QSize(0,2); + } +}; + +NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : + message(0), map(0), status(0), invusage(0), + hsplitter(0), vsplitter(0), + keysink(ks), dirkey(0) +{ + QToolBar* toolbar = new QToolBar(this); + toolbar->setMovable(false); + toolbar->setFocusPolicy(Qt::NoFocus); + addToolBar(toolbar); + menubar = menuBar(); + + setWindowTitle("Qt NetHack"); + if ( qt_compact_mode ) + setWindowIcon(QIcon(QPixmap(nh_icon_small))); + else + setWindowIcon(QIcon(QPixmap(nh_icon))); + + QMenu* game=new QMenu; + QMenu* apparel=new QMenu; + QMenu* act1=new QMenu; + QMenu* act2 = qt_compact_mode ? new QMenu : act1; + QMenu* magic=new QMenu; + QMenu* info=new QMenu; + + QMenu *help; + +#ifdef KDE + help = kapp->getHelpMenu( true, "" ); + help->addSeparator(); +#else + help = qt_compact_mode ? info : new QMenu; +#endif + + enum { OnDesktop=1, OnHandhelds=2 }; + struct Macro { + QMenu* menu; + const char* name; + int flags; + int NDECL((*funct)); + } item[] = { + { game, 0, 3}, + { game, "Version", 3, doversion}, + { game, "Compilation", 3, doextversion}, + { game, "History", 3, dohistory}, + { game, "Redraw", 0, doredraw}, // useless + { game, "Options", 3, doset}, + { game, "Explore mode", 3, enter_explore_mode}, + { game, 0, 3}, + { game, "Save", 3, dosave}, + { game, "Quit", 3, done2}, + + { apparel, "Apparel off", 2, doddoremarm}, + { apparel, "Remove many", 1, doddoremarm}, + { apparel, 0, 3}, + { apparel, "Wield weapon", 3, dowield}, + { apparel, "Exchange weapons", 3, doswapweapon}, + { apparel, "Two weapon combat", 3, dotwoweapon}, + { apparel, "Load quiver", 3, dowieldquiver}, + { apparel, 0, 3}, + { apparel, "Wear armour", 3, dowear}, + { apparel, "Take off armour", 3, dotakeoff}, + { apparel, 0, 3}, + { apparel, "Put on non-armour", 3, doputon}, + { apparel, "Remove non-armour", 3, doremring}, + + /* { act1, "Again\tCtrl+A", "\001", 2}, + { act1, 0, 0, 3}, */ + { act1, "Apply", 3, doapply}, + { act1, "Chat", 3, dotalk}, + { act1, "Close door", 3, doclose}, + { act1, "Down", 3, dodown}, + { act1, "Drop many", 2, doddrop}, + { act1, "Drop", 2, dodrop}, + { act1, "Eat", 2, doeat}, + { act1, "Engrave", 3, doengrave}, + /* { act1, "Fight\tShift+F", "F", 3}, */ + { act1, "Fire from quiver", 2, dofire}, + { act1, "Force", 3, doforce}, + { act1, "Get", 2, dopickup}, + { act1, "Jump", 3, dojump}, + { act2, "Kick", 2, dokick}, + { act2, "Loot", 3, doloot}, + { act2, "Open door", 3, doopen}, + { act2, "Pay", 3, dopay}, + { act2, "Rest", 2, donull}, + { act2, "Ride", 3, doride}, + { act2, "Search", 3, dosearch}, + { act2, "Sit", 3, dosit}, + { act2, "Throw", 2, dothrow}, + { act2, "Untrap", 3, dountrap}, + { act2, "Up", 3, doup}, + { act2, "Wipe face", 3, dowipe}, + + { magic, "Quaff potion", 3, dodrink}, + { magic, "Read scroll/book", 3, doread}, + { magic, "Zap wand", 3, dozap}, + { magic, "Zap spell", 3, docast}, + { magic, "Dip", 3, dodip}, + { magic, "Rub", 3, dorub}, + { magic, "Invoke", 3, doinvoke}, + { magic, 0, 3}, + { magic, "Offer", 3, dosacrifice}, + { magic, "Pray", 3, dopray}, + { magic, 0, 3}, + { magic, "Teleport", 3, dotele}, + { magic, "Monster action", 3, domonability}, + { magic, "Turn undead", 3, doturn}, + + { help, "Help", 3, dohelp}, + { help, 0, 3}, + { help, "What is here", 3, dolook}, + { help, "What is there", 3, doquickwhatis}, + { help, "What is...", 2, dowhatis}, + { help, 0, 1}, + + { info, "Inventory", 3, ddoinv}, + { info, "Conduct", 3, doconduct}, + { info, "Discoveries", 3, dodiscovered}, + { info, "List/reorder spells", 3, dovspell}, + { info, "Adjust letters", 2, doorganize}, + { info, 0, 3}, + { info, "Name object or creature", 3, docallcmd}, + { info, 0, 3}, + { info, "Skills", 3, enhance_weapon_skill}, + + { 0, 0, 0 } + }; + + int i; + + game->addAction("Qt settings...",this,SLOT(doQtSettings(bool))); + help->addAction("About Qt NetHack...",this,SLOT(doAbout(bool))); + //help->addAction("NetHack Guidebook...",this,SLOT(doGuidebook(bool))); + help->addSeparator(); + + for (i=0; item[i].menu; i++) { + if ( item[i].flags & (qt_compact_mode ? 1 : 2) ) { + if (item[i].name) { + char actchar[32]; + char menuitem[BUFSZ]; + actchar[0] = '\0'; + if (item[i].funct) { + actchar[0] = cmd_from_func(item[i].funct); + actchar[1] = '\0'; + } + if (actchar[0] && !qt_compact_mode) + Sprintf(menuitem, "%s\t%s", item[i].name, + visctrl(actchar[0])); + else + Sprintf(menuitem, "%s", item[i].name); + if (actchar[0]) { + QString name = menuitem; + QAction *action = item[i].menu->addAction(name); + action->setData(actchar); + } + } else { + item[i].menu->addSeparator(); + } + } + } + + game->setTitle("Game"); + menubar->addMenu(game); + apparel->setTitle("Gear"); + menubar->addMenu(apparel); + + if ( qt_compact_mode ) { + act1->setTitle("A-J"); + menubar->addMenu(act1); + act2->setTitle("K-Z"); + menubar->addMenu(act2); + magic->setTitle("Magic"); + menubar->addMenu(magic); + info->setIcon(QIcon(QPixmap(info_xpm))); + info->setTitle("Info"); + menubar->addMenu(info); + //menubar->insertItem(QPixmap(map_xpm), this, SLOT(raiseMap())); + //menubar->insertItem(QPixmap(msg_xpm), this, SLOT(raiseMessages())); + //menubar->insertItem(QPixmap(stat_xpm), this, SLOT(raiseStatus())); + info->addSeparator(); + info->addAction("Map", this, SLOT(raiseMap())); + info->addAction("Messages", this, SLOT(raiseMessages())); + info->addAction("Status", this, SLOT(raiseStatus())); + } else { + act1->setTitle("Action"); + menubar->addMenu(act1); + magic->setTitle("Magic"); + menubar->addMenu(magic); + info->setTitle("Info"); + menubar->addMenu(info); + menubar->addSeparator(); + help->setTitle("Help"); + menubar->addMenu(help); + } + + QSignalMapper* sm = new QSignalMapper(this); + connect(sm, SIGNAL(mapped(const QString&)), this, SLOT(doKeys(const QString&))); + QToolButton* tb; + char actchar[32]; + tb = new SmallToolButton( QPixmap(again_xpm),"Again","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", Cmd.spkeys[NHKF_DOAGAIN]); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(get_xpm),"Get","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dopickup)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(kick_xpm),"Kick","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dokick)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(throw_xpm),"Throw","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dothrow)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(fire_xpm),"Fire","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(dofire)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(drop_xpm),"Drop","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(doddrop)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(eat_xpm),"Eat","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(doeat)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + tb = new SmallToolButton( QPixmap(rest_xpm),"Rest","Action", sm, SLOT(map()), toolbar ); + Sprintf(actchar, "%c", cmd_from_func(donull)); + sm->setMapping(tb, actchar ); + toolbar->addWidget(tb); + + connect(game,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(apparel,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(act1,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + if (act2 != act1) + connect(act2,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(magic,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(info,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + connect(help,SIGNAL(triggered(QAction *)),this,SLOT(doMenuItem(QAction *))); + +#ifdef KDE + setMenu (menubar); +#endif + + int x=0,y=0; + int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame + int h=QApplication::desktop()->height()-50; + + int maxwn; + int maxhn; + if (qt_tilewidth != NULL) { + maxwn = atoi(qt_tilewidth) * COLNO + 10; + } else { + maxwn = 1400; + } + if (qt_tileheight != NULL) { + maxhn = atoi(qt_tileheight) * ROWNO * 6/4; + } else { + maxhn = 1024; + } + + // Be exactly the size we want to be - full map... + if (w>maxwn) { + x+=(w-maxwn)/2; + w=maxwn; // Doesn't need to be any wider + } + if (h>maxhn) { + y+=(h-maxhn)/2; + h=maxhn; // Doesn't need to be any taller + } + + setGeometry(x,y,w,h); + + if ( qt_compact_mode ) { + stack = new QStackedWidget(this); + setCentralWidget(stack); + } else { + vsplitter = new QSplitter(Qt::Vertical); + setCentralWidget(vsplitter); + hsplitter = new QSplitter(Qt::Horizontal); + invusage = new NetHackQtInvUsageWindow(hsplitter); + vsplitter->insertWidget(0, hsplitter); + hsplitter->insertWidget(1, invusage); + } +} + +void NetHackQtMainWindow::zoomMap() +{ + qt_settings->toggleGlyphSize(); +} + +void NetHackQtMainWindow::raiseMap() +{ + if ( stack->currentWidget() == map->Widget() ) { + zoomMap(); + } else { + stack->setCurrentWidget(map->Widget()); + } +} + +void NetHackQtMainWindow::raiseMessages() +{ + stack->setCurrentWidget(message->Widget()); +} + +void NetHackQtMainWindow::raiseStatus() +{ + stack->setCurrentWidget(status->Widget()); +} + +#if 0 // RLC this isn't used +class NetHackMimeSourceFactory : public Q3MimeSourceFactory { +public: + const QMimeSource* data(const QString& abs_name) const + { + const QMimeSource* r = 0; + if ( (NetHackMimeSourceFactory*)this == Q3MimeSourceFactory::defaultFactory() ) + r = Q3MimeSourceFactory::data(abs_name); + else + r = Q3MimeSourceFactory::defaultFactory()->data(abs_name); + if ( !r ) { + int sl = abs_name.length(); + do { + sl = abs_name.lastIndexOf('/',sl-1); + QString name = sl>=0 ? abs_name.mid(sl+1) : abs_name; + int dot = name.lastIndexOf('.'); + if ( dot >= 0 ) + name = name.left(dot); + if ( name == "map" ) + r = new Q3ImageDrag(QImage(map_xpm)); + else if ( name == "msg" ) + r = new Q3ImageDrag(QImage(msg_xpm)); + else if ( name == "stat" ) + r = new Q3ImageDrag(QImage(stat_xpm)); + } while (!r && sl>0); + } + return r; + } +}; +#endif + +void NetHackQtMainWindow::doMenuItem(QAction *action) +{ + doKeys(action->data().toString()); +} + +void NetHackQtMainWindow::doQtSettings(bool) +{ + centerOnMain(qt_settings); + qt_settings->show(); +} + +void NetHackQtMainWindow::doAbout(bool) +{ + QMessageBox::about(this, "About Qt NetHack", aboutMsg()); +} + +#if 0 // RLC this isn't used +void NetHackQtMainWindow::doGuidebook(bool) +{ + QDialog dlg(this); + new QVBoxLayout(&dlg); + Q3TextBrowser browser(&dlg); + NetHackMimeSourceFactory ms; + browser.setMimeSourceFactory(&ms); + browser.setSource(QDir::currentPath()+"/Guidebook.html"); + if ( qt_compact_mode ) + dlg.showMaximized(); + dlg.exec(); +} +#endif + +void NetHackQtMainWindow::doKeys(const QString& k) +{ + keysink.Put(k.toLatin1().constData()); + qApp->exit(); +} + +void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window) +{ + message=window; + hsplitter->insertWidget(0, message->Widget()); + ShowIfReady(); +} + +void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow2* window) +{ + map=window; + vsplitter->insertWidget(1, map->Widget()); + ShowIfReady(); + connect(map,SIGNAL(resized()),this,SLOT(layout())); +} + +void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window) +{ + status=window; + hsplitter->insertWidget(2, status->Widget()); + ShowIfReady(); +} + +void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window) +{ + if (window==status) { + status=0; + ShowIfReady(); + } else if (window==map) { + map=0; + ShowIfReady(); + } else if (window==message) { + message=0; + ShowIfReady(); + } +} + +void NetHackQtMainWindow::updateInventory() +{ + if ( invusage ) + invusage->repaint(); +} + +void NetHackQtMainWindow::fadeHighlighting() +{ + if (status) { + status->fadeHighlighting(); + } +} + +void NetHackQtMainWindow::layout() +{ +#if 0 + if ( qt_compact_mode ) + return; + if (message && map && status) { + QSize maxs=map->Widget()->maximumSize(); + int maph=std::min(height()*2/3,maxs.height()); + + QWidget* c = centralWidget(); + int h=c->height(); + int toph=h-maph; + int iuw=3*qt_settings->glyphs().width(); + int topw=(c->width()-iuw)/2; + + message->Widget()->setGeometry(0,0,topw,toph); + invusage->setGeometry(topw,0,iuw,toph); + status->Widget()->setGeometry(topw+iuw,0,topw,toph); + map->Widget()->setGeometry(std::max(0,(c->width()-maxs.width())/2), + toph,c->width(),maph); + } +#endif +} + +void NetHackQtMainWindow::resizeEvent(QResizeEvent*) +{ + layout(); +#ifdef KDE + updateRects(); +#endif +} + +void NetHackQtMainWindow::keyReleaseEvent(QKeyEvent* event) +{ + if ( dirkey ) { + doKeys(QString(QChar(dirkey))); + if ( !event->isAutoRepeat() ) + dirkey = 0; + } +} + +void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event) +{ + // Global key controls + + // For desktop, arrow keys scroll map, since we don't want players + // to think that's the way to move. For handhelds, the normal way is to + // click-to-travel, so we allow the cursor keys for fine movements. + + // 321 + // 4 0 + // 567 + + if ( event->isAutoRepeat() && + event->key() >= Qt::Key_Left && event->key() <= Qt::Key_Down ) + return; + + const char* d = Cmd.dirchars; + switch (event->key()) { + case Qt::Key_Up: + if ( dirkey == d[0] ) + dirkey = d[1]; + else if ( dirkey == d[4] ) + dirkey = d[3]; + else + dirkey = d[2]; + break; case Qt::Key_Down: + if ( dirkey == d[0] ) + dirkey = d[7]; + else if ( dirkey == d[4] ) + dirkey = d[5]; + else + dirkey = d[6]; + break; case Qt::Key_Left: + if ( dirkey == d[2] ) + dirkey = d[1]; + else if ( dirkey == d[6] ) + dirkey = d[7]; + else + dirkey = d[0]; + break; case Qt::Key_Right: + if ( dirkey == d[2] ) + dirkey = d[3]; + else if ( dirkey == d[6] ) + dirkey = d[5]; + else + dirkey = d[4]; + break; case Qt::Key_PageUp: + dirkey = 0; + if (message) message->Scroll(0,-1); + break; case Qt::Key_PageDown: + dirkey = 0; + if (message) message->Scroll(0,+1); + break; case Qt::Key_Space: + if ( flags.rest_on_space ) { + event->ignore(); + return; + } + case Qt::Key_Enter: + if ( map ) + map->clickCursor(); + break; default: + dirkey = 0; + event->ignore(); + } +} + +void NetHackQtMainWindow::closeEvent(QCloseEvent* e) +{ + if ( program_state.something_worth_saving ) { + switch ( QMessageBox::information( this, "NetHack", + "This will end your NetHack session", + "&Save", "&Cancel", 0, 1 ) ) + { + case 0: + // See dosave() function + if (dosave0()) { + u.uhp = -1; + NetHackQtBind::qt_exit_nhwindows(0); + nh_terminate(EXIT_SUCCESS); + } + break; + case 1: + break; // ignore the event + } + } else { + e->accept(); + } +} + +void NetHackQtMainWindow::ShowIfReady() +{ + if (message && map && status) { + QWidget* hp = qt_compact_mode ? static_cast(stack) : static_cast(hsplitter); + QWidget* vp = qt_compact_mode ? static_cast(stack) : static_cast(vsplitter); + message->Widget()->setParent(hp); + map->Widget()->setParent(vp); + status->Widget()->setParent(hp); + if ( qt_compact_mode ) { + message->setMap(map); + stack->addWidget(map->Widget()); + stack->addWidget(message->Widget()); + stack->addWidget(status->Widget()); + raiseMap(); + } else { + layout(); + } + showMaximized(); + } else if (isVisible()) { + hide(); + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4main.h b/win/Qt4/qt4main.h new file mode 100644 index 000000000..33f5b2635 --- /dev/null +++ b/win/Qt4/qt4main.h @@ -0,0 +1,94 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4main.h -- the main window + +#ifndef QT4MAIN_H +#define QT4MAIN_H + +#ifdef KDE +#include +#include +#else +#include "qt4kde0.h" +#endif + +namespace nethack_qt4 { + +class NetHackQtInvUsageWindow; +class NetHackQtKeyBuffer; +class NetHackQtMapWindow2; +class NetHackQtMessageWindow; +class NetHackQtStatusWindow; +class NetHackQtWindow; + +// This class is the main widget for NetHack +// +// It is a collection of Message, Map, and Status windows. In the current +// version of nethack there is only one of each, and this class makes this +// assumption, not showing itself until all are inserted. +// +// This class simply knows how to layout such children sensibly. +// +// Since it is only responsible for layout, the class does not +// note the actual class of the windows. +// + +class NetHackQtMainWindow : public KTopLevelWidget { + Q_OBJECT +public: + NetHackQtMainWindow(NetHackQtKeyBuffer&); + + void AddMessageWindow(NetHackQtMessageWindow* window); + void AddMapWindow(NetHackQtMapWindow2* window); + void AddStatusWindow(NetHackQtStatusWindow* window); + void RemoveWindow(NetHackQtWindow* window); + void updateInventory(); + + void fadeHighlighting(); + +public slots: + void doMenuItem(QAction *); + void doQtSettings(bool); + void doAbout(bool); + //RLC void doGuidebook(bool); + void doKeys(const QString&); + +protected: + virtual void resizeEvent(QResizeEvent*); + virtual void keyPressEvent(QKeyEvent*); + virtual void keyReleaseEvent(QKeyEvent* event); + virtual void closeEvent(QCloseEvent*); + +private slots: + void layout(); + void raiseMap(); + void zoomMap(); + void raiseMessages(); + void raiseStatus(); + +private: + void ShowIfReady(); + +#ifdef KDE + KMenuBar* menubar; +#else + QMenuBar* menubar; +#endif + NetHackQtMessageWindow* message; + NetHackQtMapWindow2* map; + NetHackQtStatusWindow* status; + NetHackQtInvUsageWindow* invusage; + + QSplitter *hsplitter; + QSplitter *vsplitter; + + NetHackQtKeyBuffer& keysink; + QStackedWidget* stack; + int dirkey; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4map.cpp b/win/Qt4/qt4map.cpp new file mode 100644 index 000000000..39c915ee5 --- /dev/null +++ b/win/Qt4/qt4map.cpp @@ -0,0 +1,964 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4map.cpp -- the map window + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4map.h" +#include "qt4map.moc" +#include "qt4click.h" +#include "qt4glyph.h" +#include "qt_xpms.h" +#include "qt4set.h" +#include "qt4str.h" + +// temporary +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +#ifdef TEXTCOLOR +static const QPen& nhcolor_to_pen(int c) +{ + static QPen* pen=0; + if ( !pen ) { + pen = new QPen[17]; + pen[0] = QColor(64,64,64); + pen[1] = QColor(Qt::red); + pen[2] = QColor(0,191,0); + pen[3] = QColor(127,127,0); + pen[4] = QColor(Qt::blue); + pen[5] = QColor(Qt::magenta); + pen[6] = QColor(Qt::cyan); + pen[7] = QColor(Qt::gray); + pen[8] = QColor(Qt::white); // no color + pen[9] = QColor(255,127,0); + pen[10] = QColor(127,255,127); + pen[11] = QColor(Qt::yellow); + pen[12] = QColor(127,127,255); + pen[13] = QColor(255,127,255); + pen[14] = QColor(127,255,255); + pen[15] = QColor(Qt::white); + pen[16] = QColor(Qt::black); + } + + return pen[c]; +} +#endif + +NetHackQtMapViewport::NetHackQtMapViewport(NetHackQtClickBuffer& click_sink) : + QWidget(NULL), + rogue_font(NULL), + clicksink(click_sink), + change(10) +{ + pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + + Clear(); + cursor.setX(0); + cursor.setY(0); +} + +NetHackQtMapViewport::~NetHackQtMapViewport(void) +{ + delete rogue_font; +} + +void NetHackQtMapViewport::paintEvent(QPaintEvent* event) +{ + QRect area=event->rect(); + QRect garea; + garea.setCoords( + std::max(0,area.left()/qt_settings->glyphs().width()), + std::max(0,area.top()/qt_settings->glyphs().height()), + std::min(COLNO-1,area.right()/qt_settings->glyphs().width()), + std::min(ROWNO-1,area.bottom()/qt_settings->glyphs().height()) + ); + + QPainter painter; + + painter.begin(this); + + if (Is_rogue_level(&u.uz) || iflags.wc_ascii_map) { + // You enter a VERY primitive world! + + painter.setClipRect( event->rect() ); // (normally we don't clip) + painter.fillRect( event->rect(), Qt::black ); + + if ( !rogue_font ) { + // Find font... + int pts = 5; + QString fontfamily = iflags.wc_font_map + ? iflags.wc_font_map : "Monospace"; + bool bold = false; + if ( fontfamily.right(5).toLower() == "-bold" ) { + fontfamily.truncate(fontfamily.length()-5); + bold = true; + } + while ( pts < 32 ) { + QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal); + painter.setFont(QFont(fontfamily, pts)); + QFontMetrics fm = painter.fontMetrics(); + if ( fm.width("M") > qt_settings->glyphs().width() ) + break; + if ( fm.height() > qt_settings->glyphs().height() ) + break; + pts++; + } + rogue_font = new QFont(fontfamily,pts-1); + } + painter.setFont(*rogue_font); + + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + int color; + int ch; + unsigned special; + + painter.setPen( Qt::green ); + /* map glyph to character and color */ + mapglyph(g, &ch, &color, &special, i, j); + ch = cp437(ch); +#ifdef TEXTCOLOR + painter.setPen( nhcolor_to_pen(color) ); +#endif + if (!DrawWalls( + painter, + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + ch)) { + painter.drawText( + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + Qt::AlignCenter, + QString(QChar(ch)).left(1) + ); + } + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + + painter.setFont(font()); + } else { + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + qt_settings->glyphs().drawCell(painter, g, i, j); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + } + + if (garea.contains(cursor)) { + if (Is_rogue_level(&u.uz)) { +#ifdef TEXTCOLOR + painter.setPen( Qt::white ); +#else + painter.setPen( Qt::green ); // REALLY primitive +#endif + } else + { + int hp100; + if (u.mtimedone) { + hp100=u.mhmax ? u.mh*100/u.mhmax : 100; + } else { + hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + } + + if (hp100 > 75) painter.setPen(Qt::white); + else if (hp100 > 50) painter.setPen(Qt::yellow); + else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange + else if (hp100 > 10) painter.setPen(Qt::red); + else painter.setPen(Qt::magenta); + } + + painter.drawRect( + cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(), + qt_settings->glyphs().width()-1,qt_settings->glyphs().height()-1); + } + +#if 0 + if (area.intersects(messages_rect)) { + painter.setPen(Qt::black); + painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1, + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + painter.setPen(Qt::white); + painter.drawText(viewport.contentsX(),viewport.contentsY(), + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + } +#endif + + painter.end(); +} + +bool NetHackQtMapViewport::DrawWalls( + QPainter& painter, + int x, int y, int w, int h, + unsigned ch) +{ + enum + { + w_left = 0x01, + w_right = 0x02, + w_up = 0x04, + w_down = 0x08, + w_sq_top = 0x10, + w_sq_bottom = 0x20, + w_sq_left = 0x40, + w_sq_right = 0x80 + }; + unsigned linewidth; + unsigned walls; + int x1, y1, x2, y2, x3, y3; + + linewidth = ((w < h) ? w : h)/8; + if (linewidth == 0) linewidth = 1; + + // Single walls + walls = 0; + switch (ch) + { + case 0x2500: // BOX DRAWINGS LIGHT HORIZONTAL + walls = w_left | w_right; + break; + + case 0x2502: // BOX DRAWINGS LIGHT VERTICAL + walls = w_up | w_down; + break; + + case 0x250C: // BOX DRAWINGS LIGHT DOWN AND RIGHT + walls = w_down | w_right; + break; + + case 0x2510: // BOX DRAWINGS LIGHT DOWN AND LEFT + walls = w_down | w_left; + break; + + case 0x2514: // BOX DRAWINGS LIGHT UP AND RIGHT + walls = w_up | w_right; + break; + + case 0x2518: // BOX DRAWINGS LIGHT UP AND LEFT + walls = w_up | w_left; + break; + + case 0x251C: // BOX DRAWINGS LIGHT VERTICAL AND RIGHT + walls = w_up | w_down | w_right; + break; + + case 0x2524: // BOX DRAWINGS LIGHT VERTICAL AND LEFT + walls = w_up | w_down | w_left; + break; + + case 0x252C: // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + walls = w_down | w_left | w_right; + break; + + case 0x2534: // BOX DRAWINGS LIGHT UP AND HORIZONTAL + walls = w_up | w_left | w_right; + break; + + case 0x253C: // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + walls = w_up | w_down | w_left | w_right; + break; + } + + if (walls != 0) + { + x1 = x + w/2; + switch (walls & (w_up | w_down)) + { + case w_up: + painter.drawLine(x1, y, x1, y+h/2); + break; + + case w_down: + painter.drawLine(x1, y+h/2, x1, y+h-1); + break; + + case w_up | w_down: + painter.drawLine(x1, y, x1, y+h-1); + break; + } + + y1 = y + h/2; + switch (walls & (w_left | w_right)) + { + case w_left: + painter.drawLine(x, y1, x+w/2, y1); + break; + + case w_right: + painter.drawLine(x+w/2, y1, x+w-1, y1); + break; + + case w_left | w_right: + painter.drawLine(x, y1, x+w-1, y1); + break; + } + + return true; + } + + // Double walls + walls = 0; + switch (ch) + { + case 0x2550: // BOX DRAWINGS DOUBLE HORIZONTAL + walls = w_left | w_right | w_sq_top | w_sq_bottom; + break; + + case 0x2551: // BOX DRAWINGS DOUBLE VERTICAL + walls = w_up | w_down | w_sq_left | w_sq_right; + break; + + case 0x2554: // BOX DRAWINGS DOUBLE DOWN AND RIGHT + walls = w_down | w_right | w_sq_top | w_sq_left; + break; + + case 0x2557: // BOX DRAWINGS DOUBLE DOWN AND LEFT + walls = w_down | w_left | w_sq_top | w_sq_right; + break; + + case 0x255A: // BOX DRAWINGS DOUBLE UP AND RIGHT + walls = w_up | w_right | w_sq_bottom | w_sq_left; + break; + + case 0x255D: // BOX DRAWINGS DOUBLE UP AND LEFT + walls = w_up | w_left | w_sq_bottom | w_sq_right; + break; + + case 0x2560: // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + walls = w_up | w_down | w_right | w_sq_left; + break; + + case 0x2563: // BOX DRAWINGS DOUBLE VERTICAL AND LEFT + walls = w_up | w_down | w_left | w_sq_right; + break; + + case 0x2566: // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + walls = w_down | w_left | w_right | w_sq_top; + break; + + case 0x2569: // BOX DRAWINGS DOUBLE UP AND HORIZONTAL + walls = w_up | w_left | w_right | w_sq_bottom; + break; + + case 0x256C: // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + walls = w_up | w_down | w_left | w_right; + break; + } + if (walls != 0) + { + x1 = x + w/2 - linewidth; + x2 = x + w/2 + linewidth; + x3 = x + w - 1; + y1 = y + h/2 - linewidth; + y2 = y + h/2 + linewidth; + y3 = y + h - 1; + if (walls & w_up) + { + painter.drawLine(x1, y, x1, y1); + painter.drawLine(x2, y, x2, y1); + } + if (walls & w_down) + { + painter.drawLine(x1, y2, x1, y3); + painter.drawLine(x2, y2, x2, y3); + } + if (walls & w_left) + { + painter.drawLine(x, y1, x1, y1); + painter.drawLine(x, y2, x1, y2); + } + if (walls & w_right) + { + painter.drawLine(x2, y1, x3, y1); + painter.drawLine(x2, y2, x3, y2); + } + if (walls & w_sq_top) + { + painter.drawLine(x1, y1, x2, y1); + } + if (walls & w_sq_bottom) + { + painter.drawLine(x1, y2, x2, y2); + } + if (walls & w_sq_left) + { + painter.drawLine(x1, y1, x1, y2); + } + if (walls & w_sq_right) + { + painter.drawLine(x2, y1, x2, y2); + } + return true; + } + + // Solid blocks + if (0x2591 <= ch && ch <= 0x2593) + { + unsigned shade = ch - 0x2590; + QColor rgb(painter.pen().color()); + QColor rgb2( + rgb.red()*shade/4, + rgb.green()*shade/4, + rgb.blue()*shade/4); + painter.fillRect(x, y, w, h, rgb2); + return true; + } + + return false; +} + +void NetHackQtMapViewport::mousePressEvent(QMouseEvent* event) +{ + clicksink.Put( + event->pos().x()/qt_settings->glyphs().width(), + event->pos().y()/qt_settings->glyphs().height(), + event->button()==Qt::LeftButton ? CLICK_1 : CLICK_2 + ); + qApp->exit(); +} + +void NetHackQtMapViewport::updateTiles() +{ + change.clear(); + change.add(0,0,COLNO,ROWNO); + delete rogue_font; rogue_font = NULL; +} + +QSize NetHackQtMapViewport::sizeHint() const +{ + return QSize( + qt_settings->glyphs().width() * COLNO, + qt_settings->glyphs().height() * ROWNO); +} + +QSize NetHackQtMapViewport::minimumSizeHint() const +{ + return sizeHint(); +} + +void NetHackQtMapViewport::clickCursor() +{ + clicksink.Put(cursor.x(),cursor.y(),CLICK_1); + qApp->exit(); +} + +void NetHackQtMapViewport::Clear() +{ + unsigned short stone=cmap_to_glyph(S_stone); + + for (int j=0; jglyphs().width(), + ch.y()*qt_settings->glyphs().height(), + ch.width()*qt_settings->glyphs().width(), + ch.height()*qt_settings->glyphs().height() + ); + } + + change.clear(); + + if (block) { + yn_function("Press a key when done viewing",0,'\0'); + } +} + +void NetHackQtMapViewport::CursorTo(int x,int y) +{ + Changed(cursor.x(),cursor.y()); + cursor.setX(x); + cursor.setY(y); + Changed(cursor.x(),cursor.y()); +} + +void NetHackQtMapViewport::PrintGlyph(int x,int y,int glyph) +{ + Glyph(x,y)=glyph; + Changed(x,y); +} + +void NetHackQtMapViewport::Changed(int x, int y) +{ + change.add(x,y); +} + +NetHackQtMapWindow2::NetHackQtMapWindow2(NetHackQtClickBuffer& click_sink) : + QScrollArea(NULL), + m_viewport(new NetHackQtMapViewport(click_sink)) +{ + QPalette palette; + palette.setColor(backgroundRole(), Qt::black); + setPalette(palette); + + setWidget(m_viewport); + + connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles())); + updateTiles(); +} + +void NetHackQtMapWindow2::updateTiles() +{ + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + // Be exactly the size we want to be - full map... + m_viewport->resize(COLNO*gw,ROWNO*gh); + + verticalScrollBar()->setSingleStep(gh); + verticalScrollBar()->setPageStep(gh); + horizontalScrollBar()->setSingleStep(gw); + horizontalScrollBar()->setPageStep(gw); + + m_viewport->updateTiles(); + Display(false); + + emit resized(); +} + +void NetHackQtMapWindow2::clearMessages() +{ + messages = ""; + update(messages_rect); + messages_rect = QRect(); +} + +void NetHackQtMapWindow2::putMessage(int attr, const QString& text) +{ + if ( !messages.isEmpty() ) + messages += "\n"; + messages += QString(text).replace(QChar(0x200B), ""); + QFontMetrics fm = fontMetrics(); +#if 0 + messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + update(messages_rect); +#endif +} + +void NetHackQtMapWindow2::clickCursor() +{ + m_viewport->clickCursor(); +} + +QWidget *NetHackQtMapWindow2::Widget() +{ + return this; +} + +void NetHackQtMapWindow2::Clear() +{ + m_viewport->Clear(); +} + +void NetHackQtMapWindow2::Display(bool block) +{ + m_viewport->Display(block); +} + +void NetHackQtMapWindow2::CursorTo(int x,int y) +{ + m_viewport->CursorTo(x, y); +} + +void NetHackQtMapWindow2::PutStr(int attr, const QString& text) +{ + puts("unexpected PutStr in MapWindow"); +} + +void NetHackQtMapWindow2::ClipAround(int x,int y) +{ + // Convert to pixel of center of tile + x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2; + y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2; + + // Then ensure that pixel is visible + ensureVisible(x,y,width()*0.45,height()*0.45); +} + +void NetHackQtMapWindow2::PrintGlyph(int x,int y,int glyph) +{ + m_viewport->PrintGlyph(x, y, glyph); +} + +#if 0 //RLC +// XXX Hmmm... crash after saving bones file if Map window is +// XXX deleted. Strange bug somewhere. +bool NetHackQtMapWindow::Destroy() { return false; } + +NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) : + clicksink(click_sink), + change(10), + rogue_font(0) +{ + viewport.addChild(this); + + QPalette palette; + palette.setColor(backgroundRole(), Qt::black); + setPalette(palette); + palette.setColor(viewport.backgroundRole(), Qt::black); + viewport.setPalette(palette); + + pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + + cursor.setX(0); + cursor.setY(0); + Clear(); + + connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles())); + connect(&viewport, SIGNAL(contentsMoving(int,int)), this, + SLOT(moveMessages(int,int))); + + updateTiles(); + //setFocusPolicy(Qt::StrongFocus); +} + +void NetHackQtMapWindow::moveMessages(int x, int y) +{ + QRect u = messages_rect; + messages_rect.moveTopLeft(QPoint(x,y)); + u |= messages_rect; + update(u); +} + +void NetHackQtMapWindow::clearMessages() +{ + messages = ""; + update(messages_rect); + messages_rect = QRect(); +} + +void NetHackQtMapWindow::putMessage(int attr, const QString& text) +{ + if ( !messages.isEmpty() ) + messages += "\n"; + messages += QString(text).replace(QChar(0x200B), ""); + QFontMetrics fm = fontMetrics(); + messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + update(messages_rect); +} + +void NetHackQtMapWindow::updateTiles() +{ + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + // Be exactly the size we want to be - full map... + resize(COLNO*gw,ROWNO*gh); + + viewport.verticalScrollBar()->setSingleStep(gh); + viewport.verticalScrollBar()->setPageStep(gh); + viewport.horizontalScrollBar()->setSingleStep(gw); + viewport.horizontalScrollBar()->setPageStep(gw); + /* + viewport.setMaximumSize( + gw*COLNO + viewport.verticalScrollBar()->width(), + gh*ROWNO + viewport.horizontalScrollBar()->height() + ); + */ + viewport.updateScrollBars(); + + change.clear(); + change.add(0,0,COLNO,ROWNO); + delete rogue_font; rogue_font = 0; + Display(false); + + emit resized(); +} + +NetHackQtMapWindow::~NetHackQtMapWindow() +{ + // Remove from viewport porthole, since that is a destructible member. + viewport.removeChild(this); + setParent(0,0); +} + +QWidget* NetHackQtMapWindow::Widget() +{ + return &viewport; +} + +void NetHackQtMapWindow::Scroll(int dx, int dy) +{ + if (viewport.horizontalScrollBar()->isVisible()) { + while (dx<0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dx++; } + while (dx>0) { viewport.horizontalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dx--; } + } + if (viewport.verticalScrollBar()->isVisible()) { + while (dy<0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); dy++; } + while (dy>0) { viewport.verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); dy--; } + } +} + +void NetHackQtMapWindow::Clear() +{ + unsigned short stone=cmap_to_glyph(S_stone); + + for (int j=0; jexit(); +} + +void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event) +{ + clicksink.Put( + event->pos().x()/qt_settings->glyphs().width(), + event->pos().y()/qt_settings->glyphs().height(), + event->button()==Qt::LeftButton ? CLICK_1 : CLICK_2 + ); + qApp->exit(); +} + +void NetHackQtMapWindow::paintEvent(QPaintEvent* event) +{ + QRect area=event->rect(); + QRect garea; + garea.setCoords( + std::max(0,area.left()/qt_settings->glyphs().width()), + std::max(0,area.top()/qt_settings->glyphs().height()), + std::min(COLNO-1,area.right()/qt_settings->glyphs().width()), + std::min(ROWNO-1,area.bottom()/qt_settings->glyphs().height()) + ); + + QPainter painter; + + painter.begin(this); + + if (is_rogue_level(&u.uz) || iflags.wc_ascii_map) { + // You enter a VERY primitive world! + + painter.setClipRect( event->rect() ); // (normally we don't clip) + painter.fillRect( event->rect(), Qt::black ); + + if ( !rogue_font ) { + // Find font... + int pts = 5; + QString fontfamily = iflags.wc_font_map + ? iflags.wc_font_map : "Courier"; + bool bold = false; + if ( fontfamily.right(5).toLower() == "-bold" ) { + fontfamily.truncate(fontfamily.length()-5); + bold = true; + } + while ( pts < 32 ) { + QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal); + painter.setFont(QFont(fontfamily, pts)); + QFontMetrics fm = painter.fontMetrics(); + if ( fm.width("M") > qt_settings->glyphs().width() ) + break; + if ( fm.height() > qt_settings->glyphs().height() ) + break; + pts++; + } + rogue_font = new QFont(fontfamily,pts-1); + } + painter.setFont(*rogue_font); + + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + int color; + char32_t ch; + unsigned special; + + painter.setPen( Qt::green ); + /* map glyph to character and color */ + mapglyph(g, &ch, &color, &special, i, j); +#ifdef TEXTCOLOR + painter.setPen( nhcolor_to_pen(color) ); +#endif + painter.drawText( + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + Qt::AlignCenter, + QString(QChar(ch)).left(1) + ); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + + painter.setFont(font()); + } else { + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + qt_settings->glyphs().drawCell(painter, g, i, j); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + } + + if (garea.contains(cursor)) { + if (Is_rogue_level(&u.uz)) { +#ifdef TEXTCOLOR + painter.setPen( Qt::white ); +#else + painter.setPen( Qt::green ); // REALLY primitive +#endif + } else + { + int hp100; + if (u.mtimedone) { + hp100=u.mhmax ? u.mh*100/u.mhmax : 100; + } else { + hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + } + + if (hp100 > 75) painter.setPen(Qt::white); + else if (hp100 > 50) painter.setPen(Qt::yellow); + else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange + else if (hp100 > 10) painter.setPen(Qt::red); + else painter.setPen(Qt::magenta); + } + + painter.drawRect( + cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(), + qt_settings->glyphs().width()-1,qt_settings->glyphs().height()-1); + } + + if (area.intersects(messages_rect)) { + painter.setPen(Qt::black); + painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1, + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + painter.setPen(Qt::white); + painter.drawText(viewport.contentsX(),viewport.contentsY(), + viewport.width(),0, Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft|Qt::TextDontClip, messages); + } + + painter.end(); +} + +void NetHackQtMapWindow::Display(bool block) +{ + for (int i=0; iglyphs().width(), + ch.y()*qt_settings->glyphs().height(), + ch.width()*qt_settings->glyphs().width(), + ch.height()*qt_settings->glyphs().height() + ); + } + + change.clear(); + + if (block) { + yn_function("Press a key when done viewing",0,'\0'); + } +} + +void NetHackQtMapWindow::CursorTo(int x,int y) +{ + Changed(cursor.x(),cursor.y()); + cursor.setX(x); + cursor.setY(y); + Changed(cursor.x(),cursor.y()); +} + +void NetHackQtMapWindow::PutStr(int attr, const QString& text) +{ + puts("unexpected PutStr in MapWindow"); +} + +void NetHackQtMapWindow::ClipAround(int x,int y) +{ + // Convert to pixel of center of tile + x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2; + y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2; + + // Then ensure that pixel is visible + viewport.center(x,y,0.45,0.45); +} + +void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph) +{ + Glyph(x,y)=glyph; + Changed(x,y); +} + +//void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2) +//{ + // TODO: composed graphics +//} + +void NetHackQtMapWindow::Changed(int x, int y) +{ + change.add(x,y); +} +#endif + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4map.h b/win/Qt4/qt4map.h new file mode 100644 index 000000000..da2df88ad --- /dev/null +++ b/win/Qt4/qt4map.h @@ -0,0 +1,81 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4map.h -- the map window + +#ifndef QT4MAP_H +#define QT4MAP_H + +#include "qt4win.h" +#include "qt4clust.h" + +namespace nethack_qt4 { + +class NetHackQtClickBuffer; + +class NetHackQtMapViewport : public QWidget { + Q_OBJECT +public: + NetHackQtMapViewport(NetHackQtClickBuffer& click_sink); + ~NetHackQtMapViewport(void); + +protected: + virtual void paintEvent(QPaintEvent* event); + bool DrawWalls(QPainter& painter, int x, int y, int w, int h, unsigned ch); + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + virtual void mousePressEvent(QMouseEvent* event); + +private: + QFont *rogue_font; + unsigned short glyph[ROWNO][COLNO]; + unsigned short& Glyph(int x, int y) { return glyph[y][x]; } + QPoint cursor; + QPixmap pet_annotation; + NetHackQtClickBuffer& clicksink; + Clusterizer change; + + void clickCursor(); + void Clear(); + void Display(bool block); + void CursorTo(int x,int y); + void PrintGlyph(int x,int y,int glyph); + void Changed(int x, int y); + void updateTiles(); + + // NetHackQtMapWindow2 passes through many calls to the viewport + friend class NetHackQtMapWindow2; +}; + +class NetHackQtMapWindow2 : public QScrollArea, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMapWindow2(NetHackQtClickBuffer& click_sink); + void clearMessages(); + void putMessage(int attr, const QString& text); + void clickCursor(); + virtual QWidget *Widget(); + + virtual void Clear(); + virtual void Display(bool block); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const QString& text); + virtual void ClipAround(int x,int y); + virtual void PrintGlyph(int x,int y,int glyph); + +signals: + void resized(); + +private slots: + void updateTiles(); + +private: + NetHackQtMapViewport *m_viewport; + QRect messages_rect; + QString messages; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4menu.cpp b/win/Qt4/qt4menu.cpp new file mode 100644 index 000000000..b15aa0ee5 --- /dev/null +++ b/win/Qt4/qt4menu.cpp @@ -0,0 +1,840 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4menu.cpp -- a menu or text-list widget + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4menu.h" +#include "qt4menu.moc" +#include "qt4glyph.h" +#include "qt4set.h" +#include "qt4streq.h" +#include "qt4str.h" + +// temporary +extern "C" int qt_compact_mode; +// end temporary + +#ifdef MENU_COLOR +extern "C" struct menucoloring *menu_colorings; +#endif + +namespace nethack_qt4 { + +// temporary +void centerOnMain( QWidget* w ); +// end temporary + +static boolean get_menu_coloring(char const *str, int *color, int *attr); + +QSize NetHackQtTextListBox::sizeHint() const +{ + QScrollBar *hscroll = horizontalScrollBar(); + int hsize = hscroll ? hscroll->height() : 0; + return QSize(TotalWidth()+hsize, TotalHeight()+hsize); +} + +int NetHackQtMenuListBox::TotalWidth() const +{ + int width = 0; + + for (int col = 0; col < columnCount(); ++col) { + width += columnWidth(col); + } + return width; +} + +int NetHackQtMenuListBox::TotalHeight() const +{ + int height = 0; + + for (int row = 0; row < rowCount(); ++row) { + height += rowHeight(row); + } + return height; +} + +QSize NetHackQtMenuListBox::sizeHint() const +{ + QScrollBar *hscroll = horizontalScrollBar(); + int hsize = hscroll ? hscroll->height() : 0; + return QSize(TotalWidth()+hsize, TotalHeight()+hsize); +} + +// Table view columns: +// +// [pick-count] [accel] [glyph] [string] +// +// Maybe accel should be near string. We'll see. +// pick-count normally blank. +// double-clicking or click-on-count gives pop-up entry +// string is green when selected +// +NetHackQtMenuWindow::NetHackQtMenuWindow(QWidget *parent) : + QDialog(parent), + table(new NetHackQtMenuListBox()), + prompt(0), + counting(false) +{ + QGridLayout *grid = new QGridLayout(); + table->setColumnCount(5); + table->setFrameStyle(QFrame::Panel|QFrame::Sunken); + table->setLineWidth(2); + table->setShowGrid(false); + table->horizontalHeader()->hide(); + table->verticalHeader()->hide(); + + ok=new QPushButton("Ok"); + connect(ok,SIGNAL(clicked()),this,SLOT(accept())); + + cancel=new QPushButton("Cancel"); + connect(cancel,SIGNAL(clicked()),this,SLOT(reject())); + + all=new QPushButton("All"); + connect(all,SIGNAL(clicked()),this,SLOT(All())); + + none=new QPushButton("None"); + connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone())); + + invert=new QPushButton("Invert"); + connect(invert,SIGNAL(clicked()),this,SLOT(Invert())); + + search=new QPushButton("Search"); + connect(search,SIGNAL(clicked()),this,SLOT(Search())); + + QPoint pos(0,ok->height()); + move(pos); + prompt.setParent(this,0); + prompt.move(pos); + + grid->addWidget(ok, 0, 0); + grid->addWidget(cancel, 0, 1); + grid->addWidget(all, 0, 2); + grid->addWidget(none, 0, 3); + grid->addWidget(invert, 0, 4); + grid->addWidget(search, 0, 5); + grid->addWidget(&prompt, 1, 0, 1, 7); + grid->addWidget(table, 2, 0, 1, 7); + grid->setColumnStretch(6, 1); + grid->setRowStretch(2, 1); + setFocusPolicy(Qt::StrongFocus); + table->setFocusPolicy(Qt::NoFocus); + + setLayout(grid); +} + +NetHackQtMenuWindow::~NetHackQtMenuWindow() +{ +} + +QWidget* NetHackQtMenuWindow::Widget() { return this; } + +void NetHackQtMenuWindow::StartMenu() +{ + table->setRowCount((itemcount=0)); + next_accel=0; + has_glyphs=false; +} + +NetHackQtMenuWindow::MenuItem::MenuItem() : + str("") +{ +} + +NetHackQtMenuWindow::MenuItem::~MenuItem() +{ +} + +void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier, + char ch, char gch, int attr, const QString& str, bool presel) +{ + if (!ch && identifier->a_void!=0) { + // Supply a keyboard accelerator. Limited supply. + static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (accel[next_accel]) { + ch=accel[next_accel++]; + } + } + + if ((int)itemlist.size() < itemcount+1) { + itemlist.resize(itemcount*4+10); + } + itemlist[itemcount].glyph=glyph; + itemlist[itemcount].identifier=*identifier; + itemlist[itemcount].ch=ch; + itemlist[itemcount].gch=gch; + itemlist[itemcount].attr=attr; + itemlist[itemcount].str=str; + itemlist[itemcount].selected=presel; + itemlist[itemcount].count=-1; + itemlist[itemcount].color = -1; + // Display the boulder symbol correctly + if (str.left(8) == "boulder\t") { + int bracket = str.indexOf('['); + if (bracket != -1) { + itemlist[itemcount].str = str.left(bracket+1) + + QChar(cp437(str.at(bracket+1).unicode())) + + str.mid(bracket+2); + } + } +#ifdef MENU_COLOR + int mcolor, mattr; + if (attr == 0 + && get_menu_coloring(str.toLatin1().constData(), &mcolor, &mattr)) { + itemlist[itemcount].attr = mattr; + itemlist[itemcount].color = mcolor; + } +#endif + ++itemcount; + + if (glyph!=NO_GLYPH) has_glyphs=true; +} + +#ifdef MENU_COLOR +static boolean +get_menu_coloring(char const *str, int *color, int *attr) +{ + struct menucoloring *tmpmc; + if (iflags.use_menu_color) + for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next) +# ifdef MENU_COLOR_REGEX + if (re_search(&tmpmc->match, str, strlen(str), 0, 9999, 0) >= 0) { +# else + if (pmatch(tmpmc->match, str)) { +# endif + *color = tmpmc->color; + *attr = tmpmc->attr; + return TRUE; + } + return FALSE; +} +#endif /* MENU_COLOR */ + +void NetHackQtMenuWindow::EndMenu(const QString& p) +{ + prompt.setText(p); + promptstr = p; +} + +int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list) +{ + QFont tablefont(qt_settings->normalFont()); + table->setFont(tablefont); + + table->setRowCount(itemcount); + + how=h; + + ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE); + cancel->setEnabled(how!=PICK_NONE); + all->setEnabled(how==PICK_ANY); + none->setEnabled(how==PICK_ANY); + invert->setEnabled(how==PICK_ANY); + search->setEnabled(how!=PICK_NONE); + + setResult(-1); + + // Set contents of table + QFontMetrics fm(table->font()); + for (int i = 0; i < 5; i++) { + table->setColumnWidth(i, 0); + } + for (int i = 0; i < itemcount; i++) { + AddRow(i, itemlist[i]); + } + + // Determine column widths + std::vector col_widths; + for (std::size_t i = 0; i < itemlist.size(); ++i) { + QStringList columns = itemlist[i].str.split("\t"); + if (!itemlist[i].Selectable() && columns.size() == 1) + { + // Nonselectable line with no column dividers + // Assume this is a section header + continue; + } + for (std::size_t j = 0U; j < columns.size(); ++j) { + int w = fm.width(columns[j] + " \t"); + if (j >= col_widths.size()) { + col_widths.push_back(w); + } else if (col_widths[j] < w) { + col_widths[j] = w; + } + } + } + + // Pad each column to its column width + for (std::size_t i = 0U; i < itemlist.size(); ++i) { + QTableWidgetItem *twi = table->item(i, 4); + if (twi == NULL) { continue; } + QString text = twi->text(); + QStringList columns = text.split("\t"); + for (std::size_t j = 0U; j+1U < columns.size(); ++j) { + columns[j] += "\t"; + int width = col_widths[j]; + while (fm.width(columns[j]) < width) { + columns[j] += "\t"; + } + } + text = columns.join(""); + twi->setText(text); + WidenColumn(4, fm.width(text)); + } + + // FIXME: size for compact mode + //resize(this->width(), parent()->height()*7/8); + move(0, 0); + adjustSize(); + centerOnMain(this); + exec(); + int result=this->result(); + + *menu_list=0; + if (result>0 && how!=PICK_NONE) { + if (how==PICK_ONE) { + int i; + for (i=0; ifont()); + QTableWidgetItem *twi; + + if (mi.Selectable() && how != PICK_NONE) { + // Count + twi = new QTableWidgetItem(""); + table->setItem(row, 0, twi); + twi->setFlags(Qt::ItemIsEnabled); + WidenColumn(0, fm.width("999999")); + // Check box, set if selected + QCheckBox *cb = new QCheckBox(); + cb->setChecked(mi.selected); + cb->setFocusPolicy(Qt::NoFocus); + if (how == PICK_ONE) + connect(cb, SIGNAL(clicked(bool)), this, SLOT(DoSelection(bool))); + table->setCellWidget(row, 1, cb); + WidenColumn(1, cb->width()); + } + if (mi.glyph != NO_GLYPH) { + // Icon + QPixmap pm(qt_settings->glyphs().glyph(mi.glyph)); + twi = new QTableWidgetItem(QIcon(pm), ""); + table->setItem(row, 2, twi); + twi->setFlags(Qt::ItemIsEnabled); + WidenColumn(2, pm.width()); + } + QString letter, text(mi.str); + if (mi.ch != 0) { + // Letter specified + letter = QString(mi.ch) + " - "; + } + else { + // Letter is left blank, except for skills display when # and * are + // presented + if (text.startsWith(" ")) { + // If mi.str starts with " ", it's meant to line up with lines + // that have a letter; we don't want that here + text = text.mid(4); + } else if (text.startsWith(" #") || text.startsWith(" *")) { + // Put the * or # in the letter column + letter = text.left(4); + text = text.mid(4); + } + } + twi = new QTableWidgetItem(letter); + table->setItem(row, 3, twi); + table->item(row, 3)->setFlags(Qt::ItemIsEnabled); + WidenColumn(3, fm.width(letter)); + twi = new QTableWidgetItem(text); + table->setItem(row, 4, twi); + table->item(row, 4)->setFlags(Qt::ItemIsEnabled); + WidenColumn(4, fm.width(text)); + +#ifdef MENU_COLOR + if (mi.color != -1) { + twi->setForeground(colors[mi.color]); + } +#endif + + QFont itemfont(table->font()); + switch (mi.attr) { + case ATR_BOLD: + itemfont.setWeight(QFont::Bold); + twi->setFont(itemfont); + break; + + case ATR_DIM: + twi->setFlags(Qt::NoItemFlags); + break; + + case ATR_ULINE: + itemfont.setUnderline(true); + twi->setFont(itemfont); + break; + + case ATR_INVERSE: + { + QBrush fg = twi->foreground(); + QBrush bg = twi->background(); + if (fg == bg) { + // default foreground and background come up the same for + // some unknown reason + twi->setForeground(Qt::white); + twi->setBackground(Qt::black); + } else { + twi->setForeground(bg); + twi->setBackground(fg); + } + } + break; + } +} + +void NetHackQtMenuWindow::WidenColumn(int column, int width) +{ + // need to add a bit so the whole column displays + width += 7; + if (table->columnWidth(column) < width) { + table->setColumnWidth(column, width); + } +} + +void NetHackQtMenuWindow::InputCount(char key) +{ + if (key == '\b') + { + if (counting) + { + if (countstr.isEmpty()) + ClearCount(); + else + countstr = countstr.mid(0, countstr.size() - 1); + } + } + else + { + counting = true; + countstr += QChar(key); + } + if (counting) + prompt.setText("Count: " + countstr); +} + +void NetHackQtMenuWindow::ClearCount(void) +{ + counting = false; + prompt.setText(promptstr); + countstr = ""; +} + +void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event) +{ + QString text = event->text(); + + const QChar *uni = text.unicode(); + for (unsigned k = 0; uni[k] != 0; k++) { + unsigned key = uni[k].unicode(); + if (key=='\033') { + if (counting) + ClearCount(); + else + reject(); + } else if (key=='\r' || key=='\n' || key==' ') + accept(); + else if (key==MENU_SEARCH) + Search(); + else if (key==MENU_SELECT_ALL) + All(); + else if (key==MENU_INVERT_ALL) + Invert(); + else if (key==MENU_UNSELECT_ALL) + ChooseNone(); + else if (('0' <= key && key <= '9') || key == '\b') + InputCount(key); + else { + for (int i=0; iitem(i, 0); + if (count != NULL) count->setText(""); + + QCheckBox *cb = dynamic_cast(table->cellWidget(i, 1)); + if (cb != NULL) cb->setChecked(true); + } +} +void NetHackQtMenuWindow::ChooseNone() +{ + for (int i=0; iitem(i, 0); + if (count != NULL) count->setText(""); + + QCheckBox *cb = dynamic_cast(table->cellWidget(i, 1)); + if (cb != NULL) cb->setChecked(false); + } +} +void NetHackQtMenuWindow::Invert() +{ + for (int i=0; iitem(i, 0); + if (count != NULL) count->setText(""); + + QCheckBox *cb = dynamic_cast(table->cellWidget(i, 1)); + if (cb != NULL) cb->setChecked(cb->checkState() == Qt::Unchecked); + } +} +void NetHackQtMenuWindow::Search() +{ + NetHackQtStringRequestor requestor(this, "Search for:"); + char line[256]; + if (requestor.Get(line)) { + for (int i=0; i(table->cellWidget(i, 1)); + if (cb == NULL) return; + + cb->setChecked((counting && !countstr.isEmpty()) + || cb->checkState() == Qt::Unchecked); + + QTableWidgetItem *count = table->item(i, 0); + if (count != NULL) count->setText(countstr); + + ClearCount(); + + if (how==PICK_ONE) { + accept(); + } + } +} + +void NetHackQtMenuWindow::DoSelection(bool) +{ + if (how == PICK_ONE) { + accept(); + } +} + +bool NetHackQtMenuWindow::isSelected(int row) +{ + QCheckBox *cb = dynamic_cast(table->cellWidget(row, 1)); + return cb != NULL && cb->checkState() != Qt::Unchecked; +} + +int NetHackQtMenuWindow::count(int row) +{ + QTableWidgetItem *count = table->item(row, 0); + if (count == NULL) return -1; + QString cstr = count->text(); + return cstr.isEmpty() ? -1 : cstr.toInt(); +} + +NetHackQtTextWindow::NetHackQtTextWindow(QWidget *parent) : + QDialog(parent), + use_rip(false), + str_fixed(false), + ok("Dismiss",this), + search("Search",this), + lines(new NetHackQtTextListBox(this)), + rip(this) +{ + ok.setDefault(true); + connect(&ok,SIGNAL(clicked()),this,SLOT(accept())); + connect(&search,SIGNAL(clicked()),this,SLOT(Search())); + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate())); + + QVBoxLayout* vb = new QVBoxLayout(this); + vb->addWidget(&rip); + QHBoxLayout* hb = new QHBoxLayout(); + vb->addLayout(hb); + hb->addWidget(&ok); + hb->addWidget(&search); + vb->addWidget(lines); +} + +void NetHackQtTextWindow::doUpdate() +{ + update(); +} + + +NetHackQtTextWindow::~NetHackQtTextWindow() +{ + +} + +QWidget* NetHackQtTextWindow::Widget() +{ + return this; +} + +bool NetHackQtTextWindow::Destroy() +{ + return !isVisible(); +} + +void NetHackQtTextWindow::UseRIP(int how, time_t when) +{ +// Code from X11 windowport +#define STONE_LINE_LEN 16 /* # chars that fit on one line */ +#define NAME_LINE 0 /* line # for player name */ +#define GOLD_LINE 1 /* line # for amount of gold */ +#define DEATH_LINE 2 /* line # for death description */ +#define YEAR_LINE 6 /* line # for year */ + +static char** rip_line=0; + if (!rip_line) { + rip_line=new char*[YEAR_LINE+1]; + for (int i=0; i STONE_LINE_LEN) { + for(i = STONE_LINE_LEN; + ((i0 > STONE_LINE_LEN) && i); i--) + if(dpx[i] == ' ') i0 = i; + if(!i) i0 = STONE_LINE_LEN; + } + tmpchar = dpx[i0]; + dpx[i0] = 0; + str_copy(rip_line[line], dpx, STONE_LINE_LEN+1); + if (tmpchar != ' ') { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } else dpx= &dpx[i0+1]; + } + + /* Put year on stone */ + snprintf(rip_line[YEAR_LINE], STONE_LINE_LEN+1, "%4d", getyear()); + + rip.setLines(rip_line,YEAR_LINE+1); + + use_rip=true; +} + +void NetHackQtTextWindow::Clear() +{ + lines->clear(); + use_rip=false; + str_fixed=false; +} + +void NetHackQtTextWindow::Display(bool block) +{ + if (str_fixed) { + lines->setFont(qt_settings->normalFixedFont()); + } else { + lines->setFont(qt_settings->normalFont()); + } + + int h=0; + if (use_rip) { + h+=rip.height(); + ok.hide(); + search.hide(); + rip.show(); + } else { + h+=ok.height()*2 + 7; + ok.show(); + search.show(); + rip.hide(); + } + int mh = QApplication::desktop()->height()*3/5; + if ( (qt_compact_mode && lines->TotalHeight() > mh) || use_rip ) { + // big, so make it fill + showMaximized(); + } else { + move(0, 0); + adjustSize(); + centerOnMain(this); + show(); + } + exec(); +} + +void NetHackQtTextWindow::PutStr(int attr, const QString& text) +{ + str_fixed=str_fixed || text.contains(" "); + lines->addItem(text); +} + +void NetHackQtTextWindow::Search() +{ + NetHackQtStringRequestor requestor(this, "Search for:"); + static char line[256]=""; + requestor.SetDefault(line); + if (requestor.Get(line)) { + int current=lines->currentRow(); + for (int i=1; icount(); i++) { + int lnum=(i+current)%lines->count(); + QString str=lines->item(lnum)->text(); + if (str.contains(line)) { + lines->setCurrentRow(lnum); + return; + } + } + lines->setCurrentItem(NULL); + } +} + +NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(QWidget *parent_) : + actual(0), + parent(parent_) +{ +} + +QWidget* NetHackQtMenuOrTextWindow::Widget() +{ + if (!actual) impossible("Widget called before we know if Menu or Text"); + return actual->Widget(); +} + +// Text +void NetHackQtMenuOrTextWindow::Clear() +{ + if (!actual) impossible("Clear called before we know if Menu or Text"); + actual->Clear(); +} +void NetHackQtMenuOrTextWindow::Display(bool block) +{ + if (!actual) impossible("Display called before we know if Menu or Text"); + actual->Display(block); +} +bool NetHackQtMenuOrTextWindow::Destroy() +{ + if (!actual) impossible("Destroy called before we know if Menu or Text"); + return actual->Destroy(); +} + +void NetHackQtMenuOrTextWindow::PutStr(int attr, const QString& text) +{ + if (!actual) actual=new NetHackQtTextWindow(parent); + actual->PutStr(attr,text); +} + +// Menu +void NetHackQtMenuOrTextWindow::StartMenu() +{ + if (!actual) actual=new NetHackQtMenuWindow(parent); + actual->StartMenu(); +} +void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel) +{ + if (!actual) impossible("AddMenu called before we know if Menu or Text"); + actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel); +} +void NetHackQtMenuOrTextWindow::EndMenu(const QString& prompt) +{ + if (!actual) impossible("EndMenu called before we know if Menu or Text"); + actual->EndMenu(prompt); +} +int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) +{ + if (!actual) impossible("SelectMenu called before we know if Menu or Text"); + return actual->SelectMenu(how,menu_list); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4menu.h b/win/Qt4/qt4menu.h new file mode 100644 index 000000000..ad15841bc --- /dev/null +++ b/win/Qt4/qt4menu.h @@ -0,0 +1,182 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4menu.cpp -- a menu or text-list widget + +#ifndef QT4MENU_H +#define QT4MENU_H + +#include "qt4win.h" +#include "qt4rip.h" + +namespace nethack_qt4 { + +class NetHackQtTextListBox : public QListWidget { +public: + NetHackQtTextListBox(QWidget* parent = NULL) : QListWidget(parent) { } + + int TotalWidth() const + { + int width = 0; + QFontMetrics fm(font()); + for (int i = 0; i < count(); i++) { + int lwidth = fm.width(item(i)->text()); + width = std::max(width, lwidth); + } + return width; + } + int TotalHeight() const + { + QFontMetrics fm(font()); + return fm.height() * count(); + } + + virtual QSize sizeHint() const; +}; + +class NetHackQtMenuListBox : public QTableWidget { +public: + NetHackQtMenuListBox(QWidget* parent = NULL) : QTableWidget(parent) { } + + int TotalWidth() const; + int TotalHeight() const; + + virtual QSize sizeHint() const; +}; + +class NetHackQtMenuWindow : public QDialog, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMenuWindow(QWidget *parent = NULL); + ~NetHackQtMenuWindow(); + + virtual QWidget* Widget(); + + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel); + virtual void EndMenu(const QString& prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + +public slots: + void All(); + void ChooseNone(); + void Invert(); + void Search(); + + void ToggleSelect(int); + void DoSelection(bool); + +protected: + virtual void keyPressEvent(QKeyEvent*); + +private: + struct MenuItem { + MenuItem(); + ~MenuItem(); + + int glyph; + ANY_P identifier; + int attr; + QString str; + int count; + char ch; + char gch; + bool selected; + unsigned color; + + bool Selectable() const { return identifier.a_void!=0; } + }; + + QVector itemlist; + + int itemcount; + int next_accel; + + QTableWidget* table; + QPushButton* ok; + QPushButton* cancel; + QPushButton* all; + QPushButton* none; + QPushButton* invert; + QPushButton* search; + QLabel prompt; + + // Count replaces prompt while it is being input + QString promptstr; + QString countstr; + bool counting; + void InputCount(char key); + void ClearCount(void); + + int how; + + bool has_glyphs; + + bool isSelected(int row); + int count(int row); + + void AddRow(int row, const MenuItem& mi); + void WidenColumn(int column, int width); +}; + +class NetHackQtTextWindow : public QDialog, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtTextWindow(QWidget *parent = NULL); + ~NetHackQtTextWindow(); + + virtual QWidget* Widget(); + + virtual void Clear(); + virtual bool Destroy(); + virtual void Display(bool block); + virtual void PutStr(int attr, const QString& text); + virtual void UseRIP(int how, time_t when); + +public slots: + void Search(); + +private slots: + void doUpdate(); + +private: + bool use_rip; + bool str_fixed; + + QPushButton ok; + QPushButton search; + NetHackQtTextListBox* lines; + + NetHackQtRIP rip; +}; + +class NetHackQtMenuOrTextWindow : public NetHackQtWindow { +private: + NetHackQtWindow* actual; + QWidget *parent; + +public: + NetHackQtMenuOrTextWindow(QWidget *parent = NULL); + + virtual QWidget* Widget(); + + // Text + virtual void Clear(); + virtual bool Destroy(); + virtual void Display(bool block); + virtual void PutStr(int attr, const QString& text); + + // Menu + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel); + virtual void EndMenu(const QString& prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4msg.cpp b/win/Qt4/qt4msg.cpp new file mode 100644 index 000000000..e1194795d --- /dev/null +++ b/win/Qt4/qt4msg.cpp @@ -0,0 +1,134 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4msg.cpp -- a message window + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4msg.h" +#include "qt4msg.moc" +#include "qt4map.h" +#include "qt4set.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +NetHackQtMessageWindow::NetHackQtMessageWindow() : + list(new QListWidget()) +{ + list->setFocusPolicy(Qt::NoFocus); + ::iflags.window_inited = 1; + map = 0; + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont())); + updateFont(); +} + +NetHackQtMessageWindow::~NetHackQtMessageWindow() +{ + ::iflags.window_inited = 0; + delete list; +} + +QWidget* NetHackQtMessageWindow::Widget() { return list; } + +void NetHackQtMessageWindow::setMap(NetHackQtMapWindow2* m) +{ + map = m; + updateFont(); +} + +void NetHackQtMessageWindow::updateFont() +{ + list->setFont(qt_settings->normalFont()); + if ( map ) + map->setFont(qt_settings->normalFont()); +} + +void NetHackQtMessageWindow::Scroll(int dx, int dy) +{ + //RLC Is this necessary? + //RLC list->Scroll(dx,dy); +} + +void NetHackQtMessageWindow::Clear() +{ + if ( map ) + map->clearMessages(); +} + +void NetHackQtMessageWindow::Display(bool block) +{ + if (changed) { + list->repaint(); + changed=false; + } +} + +void NetHackQtMessageWindow::PutStr(int attr, const QString& text) +{ +#ifdef USER_SOUNDS + play_sound_for_message(text.toLatin1().constData()); +#endif + + changed=true; + + // If the line is output from the "/" command, map the first character + // as a symbol + QString text2; + if (text.mid(1, 3) == " ") { + text2 = QChar(cp437(text.at(0).unicode())) + text.mid(1); + } else { + text2 = text; + } +#if 0 + QListWidgetItem *item = new QListWidgetItem(text2); + + QFont font = item->font(); + font.setUnderline(attr == ATR_ULINE); + font.setWeight((attr == ATR_BOLD) ? QFont::Bold : QFont::Normal); + item->setFont(font); + + QColor fg = item->foreground().color(); + QColor bg = item->background().color(); + if (attr == ATR_DIM) + { + fg.setAlpha(fg.alpha() / 2); + } + if (attr == ATR_INVERSE) + { + QColor swap; + swap = fg; fg = bg; bg = swap; + } + item->setForeground(fg); + item->setBackground(bg); +#endif + + // ATR_BLINK not supported + if (list->count() >= ::iflags.msg_history) + delete list->item(0); + list->addItem(text2); + + // Force scrollbar to bottom + list->setCurrentRow(list->count()-1); + + if ( map ) + map->putMessage(attr, text2); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4msg.h b/win/Qt4/qt4msg.h new file mode 100644 index 000000000..5c391cc4f --- /dev/null +++ b/win/Qt4/qt4msg.h @@ -0,0 +1,42 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4msg.h -- a message window + +#ifndef QT4MSG_H +#define QT4MSG_H + +#include "qt4win.h" + +namespace nethack_qt4 { + +class NetHackQtMapWindow2; + +class NetHackQtMessageWindow : QObject, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMessageWindow(); + ~NetHackQtMessageWindow(); + + virtual QWidget* Widget(); + virtual void Clear(); + virtual void Display(bool block); + virtual void PutStr(int attr, const QString& text); + + void Scroll(int dx, int dy); + + void setMap(NetHackQtMapWindow2*); + +private: + QListWidget* list; + bool changed; + NetHackQtMapWindow2* map; + +private slots: + void updateFont(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4plsel.cpp b/win/Qt4/qt4plsel.cpp new file mode 100644 index 000000000..56b8849bd --- /dev/null +++ b/win/Qt4/qt4plsel.cpp @@ -0,0 +1,502 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4plsel.cpp -- player selector dialog + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4plsel.h" +#include "qt4plsel.moc" +#include "qt4bind.h" +#include "qt4glyph.h" +#include "qt4set.h" +#include "qt4str.h" + +// Warwick prefers it this way... +#define QT_CHOOSE_RACE_FIRST + +namespace nethack_qt4 { + +// temporary +void centerOnMain( QWidget* w ); +// end temporary + +static const char nh_attribution[] = "
NetHack" + "
by the NetHack DevTeam
"; + +class NhPSListViewItem : public QTableWidgetItem { +public: + NhPSListViewItem( QTableWidget* parent, const QString& name ) : + QTableWidgetItem(name) + { + } + + void setGlyph(int g) + { + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + QPixmap pm(gw,gh); + QPainter p(&pm); + glyphs.drawGlyph(p, g, 0, 0); + p.end(); + setIcon(QIcon(pm)); + //RLC setHeight(std::max(pm.height()+1,height())); + } + +#if 0 //RLC + void paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ) + { + if ( isSelectable() ) { + QTableWidgetItem::paintCell( p, cg, column, width, alignment ); + } else { + QColorGroup disabled( + cg.foreground().light(), + cg.button().light(), + cg.light(), cg.dark(), cg.mid(), + Qt::gray, cg.base() ); + QTableWidgetItem::paintCell( p, disabled, column, width, alignment ); + } + } +#endif +}; + +class NhPSListViewRole : public NhPSListViewItem { +public: + NhPSListViewRole( QTableWidget* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better + QString(roles[id].name.m).toLower() +#else + roles[id].name.m +#endif + ) + { + setGlyph(monnum_to_glyph(roles[id].malenum)); + } +}; + +class NhPSListViewRace : public NhPSListViewItem { +public: + NhPSListViewRace( QTableWidget* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better + str_titlecase(races[id].noun) +#else + races[id].noun +#endif + ) + { + setGlyph(monnum_to_glyph(races[id].malenum)); + } +}; + +class NhPSListView : public QTableWidget { +public: + NhPSListView( QWidget* parent ) : + QTableWidget(parent) + { + setColumnCount(1); + verticalHeader()->hide(); +#if QT_VERSION >= 0x050000 + horizontalHeader()->setSectionsClickable(false); +#else + horizontalHeader()->setClickable(false); +#endif + } + + QSizePolicy sizePolicy() const + { + return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + } + + QSize minimumSizeHint() const + { + return sizeHint(); + } + + QSize sizeHint() const + { + return QSize(columnWidth(0), QTableWidget::sizeHint().height()); + } +}; + +NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : + QDialog(NetHackQtBind::mainWidget()), + fully_specified_role(true) +{ + /* + 0 1 2 + + Name ------------------------------------+ + 0 | | + + ---- ------------------------------------+ + + Role ---+ + Race ---+ + Gender ------+ + | | | | | * Male | + 1 | | | | | * Female | + | | | | +--------------+ + | | | | + | | | | + Alignment ---+ + 2 | | | | | * Male | + | | | | | * Female | + | | | | +--------------+ + 3 | | | | ...stretch... + | | | | + 4 | | | | [ Play ] + 5 | | | | [ Quit ] + +---------+ +---------+ + */ + + QGridLayout *l = new QGridLayout(this); + l->setColumnStretch(2, 1); + sizePolicy().setHorizontalPolicy(QSizePolicy::Minimum); + + QGroupBox* namebox = new QGroupBox("Name", this); + QVBoxLayout *namelayout = new QVBoxLayout(namebox); + QLineEdit* name = new QLineEdit(namebox); + namelayout->addWidget(name); + name->setMaxLength(sizeof(plname)-1); + if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) ) + name->setText(plname); + connect(name, SIGNAL(textChanged(const QString&)), + this, SLOT(selectName(const QString&)) ); + name->setFocus(); + QGroupBox* genderbox = new QGroupBox("Sex",this); + QButtonGroup *gendergroup = new QButtonGroup(this); + QGroupBox* alignbox = new QGroupBox("Alignment",this); + QButtonGroup *aligngroup = new QButtonGroup(this); + QVBoxLayout* vbgb = new QVBoxLayout(genderbox); + vbgb->addSpacing(fontMetrics().height()*3/4); + QVBoxLayout* vbab = new QVBoxLayout(alignbox); + vbab->addSpacing(fontMetrics().height()); + QLabel* logo = new QLabel(nh_attribution, this); + + l->addWidget( namebox, 0,0,1,3 ); +#ifdef QT_CHOOSE_RACE_FIRST + race = new NhPSListView(this); + role = new NhPSListView(this); + l->addWidget( race, 1,0,5,1 ); + l->addWidget( role, 1,1,5,1 ); +#else + role = new NhPSListView(this); + race = new NhPSListView(this); + l->addWidget( role, 1,0,5,1 ); + l->addWidget( race, 1,1,5,1 ); +#endif + + l->addWidget( genderbox, 1, 2 ); + l->addWidget( alignbox, 2, 2 ); + l->addWidget( logo, 3, 2, Qt::AlignCenter ); + l->setRowStretch( 3, 5 ); + + int i; + int nrole; + + // XXX QListView unsorted goes in rev. + for (nrole=0; roles[nrole].name.m; nrole++) + ; + role->setRowCount(nrole); + for (i=0; roles[i].name.m; i++) { + QTableWidgetItem *item = new QTableWidgetItem( + QIcon(qt_settings->glyphs().glyph(roles[i].malenum)), + roles[i].name.m); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + role->setItem(i, 0, item); + } + connect( role, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(selectRole(int, int, int, int)) ); + role->setHorizontalHeaderLabels(QStringList("Role")); + role->resizeColumnToContents(0); + + int nrace; + for (nrace=0; races[nrace].noun; nrace++) + ; + race->setRowCount(nrace); + for (i=0; races[i].noun; i++) { + QTableWidgetItem *item = new QTableWidgetItem( + QIcon(qt_settings->glyphs().glyph(races[i].malenum)), + races[i].noun); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + race->setItem(i, 0, item); + } + connect( race, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(selectRace(int, int, int, int)) ); + race->setHorizontalHeaderLabels(QStringList("Race")); + race->resizeColumnToContents(0); + + gender = new QRadioButton*[ROLE_GENDERS]; + for (i=0; ilayout()->addWidget(gender[i]); + gendergroup->addButton(gender[i], i); + } + connect( gendergroup, SIGNAL(buttonPressed(int)), this, SLOT(selectGender(int)) ); + + alignment = new QRadioButton*[ROLE_ALIGNS]; + for (i=0; ilayout()->addWidget(alignment[i]); + aligngroup->addButton(alignment[i], i); + } + connect( aligngroup, SIGNAL(buttonPressed(int)), this, SLOT(selectAlignment(int)) ); + + QPushButton* ok = new QPushButton("Play",this); + l->addWidget( ok, 4, 2 ); + ok->setDefault(true); + connect( ok, SIGNAL(clicked()), this, SLOT(accept()) ); + + QPushButton* cancel = new QPushButton("Quit",this); + l->addWidget( cancel, 5, 2 ); + connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) ); + + // Randomize race and role, unless specified in config + int ro = flags.initrole; + if (ro == ROLE_NONE || ro == ROLE_RANDOM) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = false; + } + } + int ra = flags.initrace; + if (ra == ROLE_NONE || ra == ROLE_RANDOM) { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = false; + } + } + + // make sure we have a valid combination, honoring + // the users request if possible. + bool choose_race_first; +#ifdef QT_CHOOSE_RACE_FIRST + choose_race_first = true; + if (flags.initrole >= 0 && flags.initrace < 0) { + choose_race_first = false; + } +#else + choose_race_first = false; + if (flags.initrace >= 0 && flags.initrole < 0) { + choose_race_first = true; + } +#endif + while (!validrace(ro,ra)) { + if (choose_race_first) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = false; + } + } else { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = false; + } + } + } + + int g = flags.initgend; + if (g == -1) { + g = rn2(ROLE_GENDERS); + fully_specified_role = false; + } + while (!validgend(ro,ra,g)) { + g = rn2(ROLE_GENDERS); + } + gender[g]->setChecked(true); + selectGender(g); + + int a = flags.initalign; + if (a == -1) { + a = rn2(ROLE_ALIGNS); + fully_specified_role = false; + } + while (!validalign(ro,ra,a)) { + a = rn2(ROLE_ALIGNS); + } + alignment[a]->setChecked(true); + selectAlignment(a); + + role->setCurrentCell(ro, 0); + + race->setCurrentCell(ra, 0); + + flags.initrace = race->currentRow(); + flags.initrole = role->currentRow(); +} + + +void NetHackQtPlayerSelector::selectName(const QString& n) +{ + str_copy(plname,n.toLatin1().constData(),SIZE(plname)); +} + +void NetHackQtPlayerSelector::selectRole(int crow, int ccol, int prow, int pcol) +{ + int ra = race->currentRow(); + int ro = role->currentRow(); + if (ra == -1 || ro == -1) return; + QTableWidgetItem* item; + item = role->item(prow, 0); + if (item != NULL) + item->setSelected(false); + +#ifdef QT_CHOOSE_RACE_FIRST + selectRace(crow, ccol, prow, pcol); +#else + QTableWidgetItem* i=role->currentItem(); + QTableWidgetItem* valid=0; + int j; + for (j=0; roles[j].name.m; j++) { + bool v = validrace(j,ra); + item = role->item(j, 0); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + if ( !valid && v ) valid = item; + } + if ( !validrace(role->currentRow(),ra) ) + i = valid; + role->setCurrentItem(i, 0); + for (j=0; roles[j].name.m; j++) { + item = role->item(j, 0); + item->setSelected(item == i); + bool v = validrace(j,ra); + item->setFlags( + v ? Qt::ItemIsEnabled|Qt::ItemIsSelectable + : Qt::NoItemFlags); + } +#endif + + flags.initrole = role->currentRow(); + setupOthers(); +} + +void NetHackQtPlayerSelector::selectRace(int crow, int ccol, int prow, int pcol) +{ + int ra = race->currentRow(); + int ro = role->currentRow(); + if (ra == -1 || ro == -1) return; + QTableWidgetItem* item; + item = race->item(prow, 0); + if (item != NULL) + item->setSelected(false); + +#ifndef QT_CHOOSE_RACE_FIRST + selectRole(crow, ccol, prow, pcol); +#else + QTableWidgetItem* i=race->currentItem(); + QTableWidgetItem* valid=0; + int j; + for (j=0; races[j].noun; j++) { + bool v = validrace(ro,j); + item = race->item(j, 0); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + if ( !valid && v ) valid = item; + } + if ( !validrace(ro,race->currentRow()) ) + i = valid; + for (j=0; races[j].noun; j++) { + item = race->item(j, 0); + item->setSelected(item == i); + bool v = validrace(ro,j); + item->setFlags( + v ? Qt::ItemIsEnabled|Qt::ItemIsSelectable + : Qt::NoItemFlags); + } +#endif + + flags.initrace = race->currentRow(); + setupOthers(); +} + +void NetHackQtPlayerSelector::setupOthers() +{ + int ro = role->currentRow(); + int ra = race->currentRow(); + int valid=-1; + int c=0; + int j; + for (j=0; jisChecked() ) + c = j; + gender[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validgend(ro,ra,c) ) + c = valid; + int k; + for (k=0; ksetChecked(c==k); + } + selectGender(c); + + valid=-1; + for (j=0; jisChecked() ) + c = j; + alignment[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validalign(ro,ra,c) ) + c = valid; + for (k=0; ksetChecked(c==k); + } + selectAlignment(c); +} + +void NetHackQtPlayerSelector::selectGender(int i) +{ + flags.initgend = i; +} + +void NetHackQtPlayerSelector::selectAlignment(int i) +{ + flags.initalign = i; +} + +void NetHackQtPlayerSelector::Quit() +{ + done(R_Quit); +} + +void NetHackQtPlayerSelector::Random() +{ + done(R_Rand); +} + +bool NetHackQtPlayerSelector::Choose() +{ + if (fully_specified_role) return true; + +#if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog). + if ( qt_compact_mode ) { + showMaximized(); + } else +#endif + { + adjustSize(); + centerOnMain(this); + } + + if ( exec() ) { + return true; + } else { + return false; + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4plsel.h b/win/Qt4/qt4plsel.h new file mode 100644 index 000000000..b82341270 --- /dev/null +++ b/win/Qt4/qt4plsel.h @@ -0,0 +1,45 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4plsel.h -- player selector dialog + +#ifndef QT4PLSEL_H +#define QT4PLSEL_H + +namespace nethack_qt4 { + +class NetHackQtKeyBuffer; + +class NetHackQtPlayerSelector : private QDialog { + Q_OBJECT +public: + enum { R_None=-1, R_Quit=-2, R_Rand=-3 }; + + NetHackQtPlayerSelector(NetHackQtKeyBuffer&); + +public slots: + void Quit(); + void Random(); + + void selectName(const QString& n); + void selectRole(int current, int, int previous, int); + void selectRace(int current, int, int previous, int); + void setupOthers(); + void selectGender(int); + void selectAlignment(int); + +public: + bool Choose(); + +private: + QTableWidget* role; + QTableWidget* race; + QRadioButton **gender; + QRadioButton **alignment; + bool fully_specified_role; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4rip.cpp b/win/Qt4/qt4rip.cpp new file mode 100644 index 000000000..d509f8895 --- /dev/null +++ b/win/Qt4/qt4rip.cpp @@ -0,0 +1,94 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4rip.cpp -- tombstone window + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4rip.h" +#include "qt4bind.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +QPixmap* NetHackQtRIP::pixmap=0; + +// Debian uses a separate PIXMAPDIR +#ifndef PIXMAPDIR +# ifdef HACKDIR +# define PIXMAPDIR HACKDIR +# else +# define PIXMAPDIR "." +# endif +#endif + +static void +tryload(QPixmap& pm, const char* fn) +{ + if (!pm.load(fn)) { + QString msg; + msg.sprintf("Cannot load \"%s\"", fn); + QMessageBox::warning(NetHackQtBind::mainWidget(), "IO Error", msg); + } +} + +NetHackQtRIP::NetHackQtRIP(QWidget* parent) : + QWidget(parent) +{ + if (!pixmap) { + pixmap=new QPixmap; + tryload(*pixmap, PIXMAPDIR "/rip.xpm"); + } + riplines=0; + resize(pixmap->width(),pixmap->height()); + setFont(QFont("times",12)); // XXX may need to be configurable +} + +void NetHackQtRIP::setLines(char** l, int n) +{ + line=l; + riplines=n; +} + +QSize NetHackQtRIP::sizeHint() const +{ + return pixmap->size(); +} + +void NetHackQtRIP::paintEvent(QPaintEvent* event) +{ + if ( riplines ) { + int pix_x=(width()-pixmap->width())/2; + int pix_y=(height()-pixmap->height())/2; + + // XXX positions based on RIP image + int rip_text_x=pix_x+156; + int rip_text_y=pix_y+67; + int rip_text_h=94/riplines; + + QPainter painter; + painter.begin(this); + painter.drawPixmap(pix_x,pix_y,*pixmap); + for (int i=0; i +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4set.h" +#include "qt4set.moc" +#include "qt4glyph.h" +#include "qt4str.h" + +/* Used by tile/font-size patch below and in ../../src/files.c */ +char *qt_tilewidth=NULL; +char *qt_tileheight=NULL; +char *qt_fontsize=NULL; +#if defined(QWS) +int qt_compact_mode = 1; +#else +int qt_compact_mode = 0; +#endif + +namespace nethack_qt4 { + +#define TILEWMIN 1 +#define TILEHMIN 1 + +NetHackQtSettings::NetHackQtSettings(int w, int h) : + tilewidth(this), + tileheight(this), + widthlbl("&Width:",this), + heightlbl("&Height:",this), + whichsize("&Zoomed",this), + fontsize(this), + normal("times"), +#ifdef WS_WIN + normalfixed("courier new"), +#else + normalfixed("courier"), +#endif + large("times"), + theglyphs(0) + +{ + int default_fontsize; + + widthlbl.setBuddy(&tilewidth); + tilewidth.setRange(TILEWMIN, 128); + heightlbl.setBuddy(&tileheight); + tileheight.setRange(TILEHMIN, 128); + + default_fontsize=2; + tilewidth.setValue(16); + tileheight.setValue(16); + + // Tile/font sizes read from .nethackrc + if (qt_tilewidth != NULL) { + tilewidth.setValue(atoi(qt_tilewidth)); + delete[] qt_tilewidth; + } + if (qt_tileheight != NULL) { + tileheight.setValue(atoi(qt_tileheight)); + delete[] qt_tileheight; + } + if (qt_fontsize != NULL) { + switch (tolower(qt_fontsize[0])) { + case 'h': default_fontsize = 0; break; + case 'l': default_fontsize = 1; break; + case 'm': default_fontsize = 2; break; + case 's': default_fontsize = 3; break; + case 't': default_fontsize = 4; break; + } + delete[] qt_fontsize; + } + + theglyphs=new NetHackQtGlyphs(); + resizeTiles(); + + connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + connect(&whichsize,SIGNAL(toggled(bool)),this,SLOT(setGlyphSize(bool))); + + fontsize.addItem("Huge"); + fontsize.addItem("Large"); + fontsize.addItem("Medium"); + fontsize.addItem("Small"); + fontsize.addItem("Tiny"); + fontsize.setCurrentIndex(default_fontsize); + connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged())); + + QGridLayout* grid = new QGridLayout(this); + grid->addWidget(&whichsize, 0, 0, 1, 2); + grid->addWidget(&tilewidth, 1, 1); grid->addWidget(&widthlbl, 1, 0); + grid->addWidget(&tileheight, 2, 1); grid->addWidget(&heightlbl, 2, 0); + QLabel* flabel=new QLabel("&Font:",this); + flabel->setBuddy(&fontsize); + grid->addWidget(flabel, 3, 0); grid->addWidget(&fontsize, 3, 1); + QPushButton* dismiss=new QPushButton("Dismiss",this); + dismiss->setDefault(true); + grid->addWidget(dismiss, 4, 0, 1, 2); + grid->setRowStretch(4,0); + grid->setColumnStretch(1,1); + grid->setColumnStretch(2,2); + grid->activate(); + + connect(dismiss,SIGNAL(clicked()),this,SLOT(accept())); + resize(150,140); +} + +NetHackQtGlyphs& NetHackQtSettings::glyphs() +{ + return *theglyphs; +} + +void NetHackQtSettings::resizeTiles() +{ + int w = tilewidth.value(); + int h = tileheight.value(); + + theglyphs->setSize(w,h); + emit tilesChanged(); +} + +void NetHackQtSettings::toggleGlyphSize() +{ + whichsize.toggle(); +} + +void NetHackQtSettings::setGlyphSize(bool which) +{ + QSize n = QSize(tilewidth.value(),tileheight.value()); + if ( othersize.isValid() ) { + tilewidth.blockSignals(true); + tileheight.blockSignals(true); + tilewidth.setValue(othersize.width()); + tileheight.setValue(othersize.height()); + tileheight.blockSignals(false); + tilewidth.blockSignals(false); + resizeTiles(); + } + othersize = n; +} + +const QFont& NetHackQtSettings::normalFont() +{ + static int size[]={ 18, 14, 12, 10, 8 }; + normal.setPointSize(size[fontsize.currentIndex()]); + return normal; +} + +const QFont& NetHackQtSettings::normalFixedFont() +{ + static int size[]={ 18, 14, 13, 10, 8 }; + normalfixed.setPointSize(size[fontsize.currentIndex()]); + return normalfixed; +} + +const QFont& NetHackQtSettings::largeFont() +{ + static int size[]={ 24, 18, 14, 12, 10 }; + large.setPointSize(size[fontsize.currentIndex()]); + return large; +} + +bool NetHackQtSettings::ynInMessages() +{ + return !qt_compact_mode && !iflags.wc_popup_dialog; +} + +NetHackQtSettings* qt_settings; + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4set.h b/win/Qt4/qt4set.h new file mode 100644 index 000000000..e2253a8d6 --- /dev/null +++ b/win/Qt4/qt4set.h @@ -0,0 +1,57 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4set.h -- the Qt settings + +#ifndef QT4SET_H +#define QT4SET_H + +namespace nethack_qt4 { + +class NetHackQtGlyphs; + +class NetHackQtSettings : public QDialog { + Q_OBJECT +public: + // Size of window - used to decide default sizes + NetHackQtSettings(int width, int height); + + NetHackQtGlyphs& glyphs(); + const QFont& normalFont(); + const QFont& normalFixedFont(); + const QFont& largeFont(); + + bool ynInMessages(); + +signals: + void fontChanged(); + void tilesChanged(); + +public slots: + void toggleGlyphSize(); + void setGlyphSize(bool); + +private: + QSpinBox tilewidth; + QSpinBox tileheight; + QLabel widthlbl; + QLabel heightlbl; + QCheckBox whichsize; + QSize othersize; + + QComboBox fontsize; + + QFont normal, normalfixed, large; + + NetHackQtGlyphs* theglyphs; + +private slots: + void resizeTiles(); +}; + +extern NetHackQtSettings* qt_settings; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4stat.cpp b/win/Qt4/qt4stat.cpp new file mode 100644 index 000000000..3a15604e5 --- /dev/null +++ b/win/Qt4/qt4stat.cpp @@ -0,0 +1,540 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4stat.cpp -- bindings between the Qt 4 interface and the main code + +extern "C" { +#include "hack.h" +} +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4stat.h" +#include "qt4stat.moc" +#include "qt4set.h" +#include "qt4str.h" +#include "qt_xpms.h" + +extern const char *enc_stat[]; /* from botl.c */ +extern const char *hu_stat[]; /* from eat.c */ + +namespace nethack_qt4 { + +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"), + dex(this,"DEX"), + con(this,"CON"), + intel(this,"INT"), + wis(this,"WIS"), + cha(this,"CHA"), + gold(this,"Gold"), + hp(this,"Hit Points"), + power(this,"Power"), + ac(this,"Armour Class"), + level(this,"Level"), + exp(this,"Experience"), + align(this,"Alignment"), + time(this,"Time"), + score(this,"Score"), + hunger(this,""), + confused(this,"Confused"), + sick_fp(this,"Sick"), + sick_il(this,"Ill"), + blind(this,""), + stunned(this,"Stunned"), + hallu(this,"Hallu"), + encumber(this,""), + hline1(this), + hline2(this), + hline3(this), + first_set(true) +{ + p_str = QPixmap(str_xpm); + p_str = QPixmap(str_xpm); + p_dex = QPixmap(dex_xpm); + p_con = QPixmap(cns_xpm); + p_int = QPixmap(int_xpm); + p_wis = QPixmap(wis_xpm); + p_cha = QPixmap(cha_xpm); + + p_chaotic = QPixmap(chaotic_xpm); + p_neutral = QPixmap(neutral_xpm); + p_lawful = QPixmap(lawful_xpm); + + p_satiated = QPixmap(satiated_xpm); + p_hungry = QPixmap(hungry_xpm); + + p_confused = QPixmap(confused_xpm); + p_sick_fp = QPixmap(sick_fp_xpm); + p_sick_il = QPixmap(sick_il_xpm); + p_blind = QPixmap(blind_xpm); + p_stunned = QPixmap(stunned_xpm); + p_hallu = QPixmap(hallu_xpm); + + p_encumber[0] = QPixmap(slt_enc_xpm); + p_encumber[1] = QPixmap(mod_enc_xpm); + p_encumber[2] = QPixmap(hvy_enc_xpm); + p_encumber[3] = QPixmap(ext_enc_xpm); + p_encumber[4] = QPixmap(ovr_enc_xpm); + + str.setIcon(p_str); + dex.setIcon(p_dex); + con.setIcon(p_con); + intel.setIcon(p_int); + wis.setIcon(p_wis); + cha.setIcon(p_cha); + + align.setIcon(p_neutral); + hunger.setIcon(p_hungry); + + confused.setIcon(p_confused); + sick_fp.setIcon(p_sick_fp); + sick_il.setIcon(p_sick_il); + blind.setIcon(p_blind); + stunned.setIcon(p_stunned); + hallu.setIcon(p_hallu); + + encumber.setIcon(p_encumber[0]); + + hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline1.setLineWidth(1); + hline2.setLineWidth(1); + hline3.setLineWidth(1); + +#if 1 //RLC + name.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + dlevel.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + QVBoxLayout *vbox = new QVBoxLayout(); + vbox->setSpacing(0); + vbox->addWidget(&name); + vbox->addWidget(&dlevel); + vbox->addWidget(&hline1); + QHBoxLayout *atr1box = new QHBoxLayout(); + atr1box->addWidget(&str); + atr1box->addWidget(&dex); + atr1box->addWidget(&con); + atr1box->addWidget(&intel); + atr1box->addWidget(&wis); + atr1box->addWidget(&cha); + vbox->addLayout(atr1box); + vbox->addWidget(&hline2); + QHBoxLayout *atr2box = new QHBoxLayout(); + atr2box->addWidget(&gold); + atr2box->addWidget(&hp); + atr2box->addWidget(&power); + atr2box->addWidget(&ac); + atr2box->addWidget(&level); + atr2box->addWidget(&exp); + vbox->addLayout(atr2box); + vbox->addWidget(&hline3); + QHBoxLayout *timebox = new QHBoxLayout(); + timebox->addWidget(&time); + timebox->addWidget(&score); + vbox->addLayout(timebox); + QHBoxLayout *statbox = new QHBoxLayout(); + statbox->addWidget(&align); + statbox->addWidget(&hunger); + statbox->addWidget(&confused); + statbox->addWidget(&sick_fp); + statbox->addWidget(&sick_il); + statbox->addWidget(&blind); + statbox->addWidget(&stunned); + statbox->addWidget(&hallu); + statbox->addWidget(&encumber); + statbox->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + vbox->addLayout(statbox); + setLayout(vbox); +#endif + + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate())); + doUpdate(); +} + +void NetHackQtStatusWindow::doUpdate() +{ + const QFont& large=qt_settings->largeFont(); + name.setFont(large); + dlevel.setFont(large); + + const QFont& normal=qt_settings->normalFont(); + str.setFont(normal); + dex.setFont(normal); + con.setFont(normal); + intel.setFont(normal); + wis.setFont(normal); + cha.setFont(normal); + gold.setFont(normal); + hp.setFont(normal); + power.setFont(normal); + ac.setFont(normal); + level.setFont(normal); + exp.setFont(normal); + align.setFont(normal); + time.setFont(normal); + score.setFont(normal); + hunger.setFont(normal); + confused.setFont(normal); + sick_fp.setFont(normal); + sick_il.setFont(normal); + blind.setFont(normal); + stunned.setFont(normal); + hallu.setFont(normal); + encumber.setFont(normal); + + updateStats(); +} + +QWidget* NetHackQtStatusWindow::Widget() { return this; } + +void NetHackQtStatusWindow::Clear() +{ +} +void NetHackQtStatusWindow::Display(bool block) +{ +} +void NetHackQtStatusWindow::CursorTo(int,int y) +{ + cursy=y; +} +void NetHackQtStatusWindow::PutStr(int attr, const QString& text) +{ + // do a complete update when line 0 is done (as per X11 fancy status) + if (cursy==0) updateStats(); +} + +#if 0 // RLC +void NetHackQtStatusWindow::resizeEvent(QResizeEvent*) +{ +#if 0 + const float SP_name=0.13; // the (large) + const float SP_dlev=0.13; // Level 3 in The Dungeons of Doom (large) + const float SP_atr1=0.25; // STR DEX CON INT WIS CHA + const float SP_hln1=0.02; // --- + const float SP_atr2=0.09; // Au HP PW AC LVL EXP + const float SP_hln2=0.02; // --- + const float SP_time=0.09; // time score + const float SP_hln3=0.02; // --- + const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc. + + int h=height(); + int x=0,y=0; + + int iw; // Width of an item across line + int lh; // Height of a line of values + + lh=int(h*SP_name); + name.setGeometry(0,0,width(),lh); y+=lh; + lh=int(h*SP_dlev); + dlevel.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_hln1); + hline1.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr1); + iw=width()/6; + str.setGeometry(x,y,iw,lh); x+=iw; + dex.setGeometry(x,y,iw,lh); x+=iw; + con.setGeometry(x,y,iw,lh); x+=iw; + intel.setGeometry(x,y,iw,lh); x+=iw; + wis.setGeometry(x,y,iw,lh); x+=iw; + cha.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln2); + hline2.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr2); + iw=width()/6; + gold.setGeometry(x,y,iw,lh); x+=iw; + hp.setGeometry(x,y,iw,lh); x+=iw; + power.setGeometry(x,y,iw,lh); x+=iw; + ac.setGeometry(x,y,iw,lh); x+=iw; + level.setGeometry(x,y,iw,lh); x+=iw; + exp.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln3); + hline3.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_time); + iw=width()/3; x+=iw/2; + time.setGeometry(x,y,iw,lh); x+=iw; + score.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_stat); + iw=width()/9; + align.setGeometry(x,y,iw,lh); x+=iw; + hunger.setGeometry(x,y,iw,lh); x+=iw; + confused.setGeometry(x,y,iw,lh); x+=iw; + sick_fp.setGeometry(x,y,iw,lh); x+=iw; + sick_il.setGeometry(x,y,iw,lh); x+=iw; + blind.setGeometry(x,y,iw,lh); x+=iw; + stunned.setGeometry(x,y,iw,lh); x+=iw; + hallu.setGeometry(x,y,iw,lh); x+=iw; + encumber.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; +#else + // This is clumsy. But QLayout objects are proving balky. + + int row[10]; + + row[0] = name.sizeHint().height(); + row[1] = dlevel.sizeHint().height(); + row[2] = h.sizeHint().height(); +#endif +} +#endif + + +/* + * Set all widget values to a null string. This is used after all spacings + * have been calculated so that when the window is popped up we don't get all + * kinds of funny values being displayed. + */ +void NetHackQtStatusWindow::nullOut() +{ +} + +void NetHackQtStatusWindow::fadeHighlighting() +{ + name.dissipateHighlight(); + dlevel.dissipateHighlight(); + + str.dissipateHighlight(); + dex.dissipateHighlight(); + con.dissipateHighlight(); + intel.dissipateHighlight(); + wis.dissipateHighlight(); + cha.dissipateHighlight(); + + gold.dissipateHighlight(); + hp.dissipateHighlight(); + power.dissipateHighlight(); + ac.dissipateHighlight(); + level.dissipateHighlight(); + exp.dissipateHighlight(); + align.dissipateHighlight(); + + time.dissipateHighlight(); + score.dissipateHighlight(); + + hunger.dissipateHighlight(); + confused.dissipateHighlight(); + sick_fp.dissipateHighlight(); + sick_il.dissipateHighlight(); + blind.dissipateHighlight(); + stunned.dissipateHighlight(); + hallu.dissipateHighlight(); + encumber.dissipateHighlight(); +} + +/* + * Update the displayed status. The current code in botl.c updates + * two lines of information. Both lines are always updated one after + * the other. So only do our update when we update the second line. + * + * Information on the first line: + * name, attributes, alignment, score + * + * Information on the second line: + * dlvl, gold, hp, power, ac, {level & exp or HD **} + * status (hunger, conf, halu, stun, sick, blind), time, encumbrance + * + * [**] HD is shown instead of level and exp if mtimedone is non-zero. + */ +void NetHackQtStatusWindow::updateStats() +{ + if (!parentWidget()) return; + + QString buf; + const char *text; + + if (cursy != 0) return; /* do a complete update when line 0 is done */ + + if (ACURR(A_STR) > 118) { + buf.sprintf("STR:%d",ACURR(A_STR)-100); + } else if (ACURR(A_STR)==118) { + buf.sprintf("STR:18/**"); + } else if(ACURR(A_STR) > 18) { + buf.sprintf("STR:18/%02d",ACURR(A_STR)-18); + } else { + buf.sprintf("STR:%d",ACURR(A_STR)); + } + str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR)); + + dex.setLabel("DEX:",(long)ACURR(A_DEX)); + con.setLabel("CON:",(long)ACURR(A_CON)); + intel.setLabel("INT:",(long)ACURR(A_INT)); + wis.setLabel("WIS:",(long)ACURR(A_WIS)); + cha.setLabel("CHA:",(long)ACURR(A_CHA)); + const char* hung=hu_stat[u.uhs]; + if (hung[0]==' ') { + hunger.hide(); + } else { + hunger.setIcon(u.uhs ? p_hungry : p_satiated); + hunger.setLabel(hung); + hunger.show(); + } + if (Confusion) confused.show(); else confused.hide(); + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) { + sick_fp.show(); + } else { + sick_fp.hide(); + } + if (u.usick_type & SICK_NONVOMITABLE) { + sick_il.show(); + } else { + sick_il.hide(); + } + } else { + sick_fp.hide(); + sick_il.hide(); + } + if (Blind) { + blind.setLabel("Blind"); + blind.show(); + } else { + blind.hide(); + } + if (Stunned) stunned.show(); else stunned.hide(); + if (Hallucination) hallu.show(); else hallu.hide(); + const char* enc=enc_stat[near_capacity()]; + if (enc[0]==' ' || !enc[0]) { + encumber.hide(); + } else { + encumber.setIcon(p_encumber[near_capacity()-1]); + encumber.setLabel(enc); + encumber.show(); + } + if (u.mtimedone) { + buf = nh_capitalize_words(mons[u.umonnum].mname); + } else { + buf = rank_of(u.ulevel, pl_character[0], ::flags.female); + } + QString buf2; + buf2.sprintf("%s the %s", plname, buf.toLatin1().constData()); + name.setLabel(buf2, NetHackQtLabelledIcon::NoNum, u.ulevel); + + char buf3[BUFSZ]; + if (describe_level(buf3)) { + dlevel.setLabel(buf3,true); + } else { + buf.sprintf("%s, level ", dungeons[u.uz.dnum].dname); + dlevel.setLabel(buf,(long)::depth(&u.uz)); + } + + gold.setLabel("Au:", money_cnt(invent)); + + if (u.mtimedone) { + // You're a monster! + + buf.sprintf("/%d", u.mhmax); + hp.setLabel("HP:", u.mh > 0 ? u.mh : 0, buf); + level.setLabel("HD:",(long)mons[u.umonnum].mlevel); + } else { + // You're normal. + + buf.sprintf("/%d", u.uhpmax); + hp.setLabel("HP:", u.uhp > 0 ? u.uhp : 0, buf); + level.setLabel("Level:",(long)u.ulevel); + } + buf.sprintf("/%d", u.uenmax); + power.setLabel("Pow:", u.uen, buf); + ac.setLabel("AC:",(long)u.uac); +#ifdef EXP_ON_BOTL + if (::flags.showexp) { + exp.setLabel("Exp:",(long)u.uexp); + } else +#endif + { + exp.setLabel(""); + } + if (u.ualign.type==A_CHAOTIC) { + align.setIcon(p_chaotic); + text = "Chaotic"; + } else if (u.ualign.type==A_NEUTRAL) { + align.setIcon(p_neutral); + text = "Neutral"; + } else { + align.setIcon(p_lawful); + text = "Lawful"; + } + align.setLabel(text); + + if (::flags.time) time.setLabel("Time:",(long)moves); + else time.setLabel(""); +#ifdef SCORE_ON_BOTL + if (::flags.showscore) { + score.setLabel("Score:",(long)botl_score()); + } else +#endif + { + score.setLabel(""); + } + + if (first_set) + { + first_set=false; + + name.highlightWhenChanging(); + dlevel.highlightWhenChanging(); + + str.highlightWhenChanging(); + dex.highlightWhenChanging(); + con.highlightWhenChanging(); + intel.highlightWhenChanging(); + wis.highlightWhenChanging(); + cha.highlightWhenChanging(); + + gold.highlightWhenChanging(); + hp.highlightWhenChanging(); + power.highlightWhenChanging(); + ac.highlightWhenChanging(); ac.lowIsGood(); + level.highlightWhenChanging(); + exp.highlightWhenChanging(); + align.highlightWhenChanging(); + + //time.highlightWhenChanging(); + score.highlightWhenChanging(); + + hunger.highlightWhenChanging(); + confused.highlightWhenChanging(); + sick_fp.highlightWhenChanging(); + sick_il.highlightWhenChanging(); + blind.highlightWhenChanging(); + stunned.highlightWhenChanging(); + hallu.highlightWhenChanging(); + encumber.highlightWhenChanging(); + } +} + +/* + * Turn off hilighted status values after a certain amount of turns. + */ +void NetHackQtStatusWindow::checkTurnEvents() +{ +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4stat.h b/win/Qt4/qt4stat.h new file mode 100644 index 000000000..a0a00a46f --- /dev/null +++ b/win/Qt4/qt4stat.h @@ -0,0 +1,106 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4stat.h -- bindings between the Qt 4 interface and the main code + +#ifndef QT4STAT_H +#define QT4STAT_H + +#include "qt4win.h" +#include "qt4icon.h" + +namespace nethack_qt4 { + +class NetHackQtStatusWindow : QWidget, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtStatusWindow(); + + virtual QWidget* Widget(); + + virtual void Clear(); + virtual void Display(bool block); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const QString& text); + + void fadeHighlighting(); + +protected: + //RLC void resizeEvent(QResizeEvent*); + +private slots: + void doUpdate(); + +private: + enum { hilight_time=1 }; + + QPixmap p_str; + QPixmap p_dex; + QPixmap p_con; + QPixmap p_int; + QPixmap p_wis; + QPixmap p_cha; + + QPixmap p_chaotic; + QPixmap p_neutral; + QPixmap p_lawful; + + QPixmap p_satiated; + QPixmap p_hungry; + + QPixmap p_confused; + QPixmap p_sick_fp; + QPixmap p_sick_il; + QPixmap p_blind; + QPixmap p_stunned; + QPixmap p_hallu; + + QPixmap p_encumber[5]; + + NetHackQtLabelledIcon name; + NetHackQtLabelledIcon dlevel; + + NetHackQtLabelledIcon str; + NetHackQtLabelledIcon dex; + NetHackQtLabelledIcon con; + NetHackQtLabelledIcon intel; + NetHackQtLabelledIcon wis; + NetHackQtLabelledIcon cha; + + NetHackQtLabelledIcon gold; + NetHackQtLabelledIcon hp; + NetHackQtLabelledIcon power; + NetHackQtLabelledIcon ac; + NetHackQtLabelledIcon level; + NetHackQtLabelledIcon exp; + NetHackQtLabelledIcon align; + + NetHackQtLabelledIcon time; + NetHackQtLabelledIcon score; + + NetHackQtLabelledIcon hunger; + NetHackQtLabelledIcon confused; + NetHackQtLabelledIcon sick_fp; + NetHackQtLabelledIcon sick_il; + NetHackQtLabelledIcon blind; + NetHackQtLabelledIcon stunned; + NetHackQtLabelledIcon hallu; + NetHackQtLabelledIcon encumber; + + QFrame hline1; + QFrame hline2; + QFrame hline3; + + int cursy; + + bool first_set; + + void nullOut(); + void updateStats(); + void checkTurnEvents(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4str.cpp b/win/Qt4/qt4str.cpp new file mode 100644 index 000000000..b6b4440eb --- /dev/null +++ b/win/Qt4/qt4str.cpp @@ -0,0 +1,83 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4str.cpp -- some string functions + +#include +#include +#include "qt4str.h" + +namespace nethack_qt4 { + +// Bounded string copy +size_t str_copy(char *dest, const char *src, size_t max) +{ + size_t len = strlen(src); + if (max != 0) { + size_t csize = len; + if (len > max - 1) { + len = max - 1; + } + memcpy(dest, src, csize); + dest[csize] = '\0'; + } + return len; +} + +QString str_titlecase(const QString& str) +{ + if (str == "") { return str; } + + return str.left(1).toUpper() + str.mid(1).toLower(); +} + +QString nh_capitalize_words(const QString& str) +{ + QStringList words = str.split(" "); + for (size_t i = 0; i < words.size(); ++i) { + words[i] = str_titlecase(words[i]); + } + return words.join(" "); +} + +int cp437(int ch) +{ + static const unsigned short cp437table[] = { + 0x0000, 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, + }; + return cp437table[(unsigned char)ch]; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4str.h b/win/Qt4/qt4str.h new file mode 100644 index 000000000..05f25f4c4 --- /dev/null +++ b/win/Qt4/qt4str.h @@ -0,0 +1,24 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4str.h -- various string functions + +#ifndef QT4STR_H +#define QT4STR_H + +namespace nethack_qt4 { + +// Bounded string copy +extern size_t str_copy(char *dest, const char *src, size_t max); + +// Case mappings +extern QString str_titlecase(const QString& str); +extern QString nh_capitalize_words(const QString& str); + +// Map symbol conversion +extern int cp437(int ch); + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4streq.cpp b/win/Qt4/qt4streq.cpp new file mode 100644 index 000000000..5d03bb996 --- /dev/null +++ b/win/Qt4/qt4streq.cpp @@ -0,0 +1,99 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4streq.cpp -- string requestor + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4streq.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +// temporary +void centerOnMain(QWidget *); +// end temporary + +NetHackQtStringRequestor::NetHackQtStringRequestor(QWidget *parent, const char* p, const char* cancelstr) : + QDialog(parent), + prompt(QString::fromLatin1(p),this), + input(this,"input") +{ + cancel=new QPushButton(cancelstr,this); + connect(cancel,SIGNAL(clicked()),this,SLOT(reject())); + + okay=new QPushButton("Okay",this); + connect(okay,SIGNAL(clicked()),this,SLOT(accept())); + connect(&input,SIGNAL(returnPressed()),this,SLOT(accept())); + okay->setDefault(true); + + setFocusPolicy(Qt::StrongFocus); +} + +void NetHackQtStringRequestor::resizeEvent(QResizeEvent*) +{ + const int margin=5; + const int gutter=5; + + int h=(height()-margin*2-gutter); + + if (prompt.text().size() > 16) { + h/=3; + prompt.setGeometry(margin,margin,width()-margin*2,h); + input.setGeometry(width()*1/5,margin+h+gutter, + (width()-margin-2-gutter)*4/5,h); + } else { + h/=2; + prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h); + input.setGeometry(prompt.geometry().right()+gutter,margin, + (width()-margin-2-gutter)*3/5,h); + } + + cancel->setGeometry(margin,input.geometry().bottom()+gutter, + (width()-margin*2-gutter)/2,h); + okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(), + cancel->width(),h); +} + +void NetHackQtStringRequestor::SetDefault(const char* d) +{ + input.setText(d); +} + +bool NetHackQtStringRequestor::Get(char* buffer, int maxchar) +{ + input.setMaxLength(maxchar); + if (prompt.text().size() > 16) { + resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6); + } else { + resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4); + } + + centerOnMain(this); + show(); + input.setFocus(); + exec(); + + if (result()) { + str_copy(buffer,input.text().toLatin1().constData(),maxchar); + return true; + } else { + return false; + } +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4streq.h b/win/Qt4/qt4streq.h new file mode 100644 index 000000000..a5f05d769 --- /dev/null +++ b/win/Qt4/qt4streq.h @@ -0,0 +1,30 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4streq.h -- string requestor + +#ifndef QT4STREQ_H +#define QT4STREQ_H + +#include "qt4line.h" + +namespace nethack_qt4 { + +class NetHackQtStringRequestor : QDialog { +private: + QLabel prompt; + NetHackQtLineEdit input; + QPushButton* okay; + QPushButton* cancel; + +public: + NetHackQtStringRequestor(QWidget *parent, const char* p,const char* cancelstr="Cancel"); + void SetDefault(const char*); + bool Get(char* buffer, int maxchar=80); + virtual void resizeEvent(QResizeEvent*); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4svsel.cpp b/win/Qt4/qt4svsel.cpp new file mode 100644 index 000000000..0b5271588 --- /dev/null +++ b/win/Qt4/qt4svsel.cpp @@ -0,0 +1,80 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4svsel.cpp -- saved game selector + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4svsel.h" +#include "qt4bind.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +NetHackQtSavedGameSelector::NetHackQtSavedGameSelector(const char** saved) : + QDialog(NetHackQtBind::mainWidget()) +{ + QVBoxLayout *vbl = new QVBoxLayout(this); + QHBoxLayout* hb; + + QLabel* logo = new QLabel(this); vbl->addWidget(logo); + logo->setAlignment(Qt::AlignCenter); + logo->setPixmap(QPixmap("nhsplash.xpm")); + QLabel* attr = new QLabel("by the NetHack DevTeam",this); + attr->setAlignment(Qt::AlignCenter); + vbl->addWidget(attr); + vbl->addStretch(2); + /* + QLabel* logo = new QLabel(hb); + hb = new QHBox(this); + vbl->addWidget(hb, Qt::AlignCenter); + logo->setPixmap(QPixmap(nh_icon)); + logo->setAlignment(AlignRight|Qt::AlignVCenter); + new QLabel(nh_attribution,hb); + */ + + hb = new QHBoxLayout(this); + vbl->addLayout(hb, Qt::AlignCenter); + QPushButton* q = new QPushButton("Quit",this); + hb->addWidget(q); + connect(q, SIGNAL(clicked()), this, SLOT(reject())); + QPushButton* c = new QPushButton("New Game",this); + hb->addWidget(c); + connect(c, SIGNAL(clicked()), this, SLOT(accept())); + c->setDefault(true); + + QGroupBox* box = new QGroupBox("Saved Characters",this); + QButtonGroup *bg = new QButtonGroup(this); + vbl->addWidget(box); + QVBoxLayout *bgl = new QVBoxLayout(box); + connect(bg, SIGNAL(buttonPressed(int)), this, SLOT(done(int))); + for (int i=0; saved[i]; i++) { + QPushButton* b = new QPushButton(saved[i],box); + bg->addButton(b, i+2); + } +} + +int NetHackQtSavedGameSelector::choose() +{ +#if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog). + if ( qt_compact_mode ) + showMaximized(); +#endif + return exec()-2; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4svsel.h b/win/Qt4/qt4svsel.h new file mode 100644 index 000000000..918e6f7e3 --- /dev/null +++ b/win/Qt4/qt4svsel.h @@ -0,0 +1,21 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4svsel.h -- saved game selector + +#ifndef QT4SVSEL_H +#define QT4SVSEL_H + +namespace nethack_qt4 { + +class NetHackQtSavedGameSelector : public QDialog { +public: + NetHackQtSavedGameSelector(const char** saved); + + int choose(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4win.cpp b/win/Qt4/qt4win.cpp new file mode 100644 index 000000000..ca0abe57b --- /dev/null +++ b/win/Qt4/qt4win.cpp @@ -0,0 +1,136 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// Qt Binding for NetHack 3.4 +// +// Copyright (C) 1996-2001 by Warwick W. Allison (warwick@troll.no) +// +// Contributors: +// Michael Hohmuth +// - Userid control +// Svante Gerhard +// - .nethackrc tile and font size settings +// Dirk Schoenberger +// - KDE support +// - SlashEm support +// and many others for bug reports. +// +// Unfortunately, this doesn't use Qt as well as I would like, +// primarily because NetHack is fundamentally a getkey-type program +// rather than being event driven (hence the ugly key and click buffer) +// and also because this is my first major application of Qt. +// +// The problem of NetHack's getkey requirement is solved by intercepting +// key events by overiding QApplicion::notify(...), and putting them in +// a buffer. Mouse clicks on the map window are treated with a similar +// buffer. When the NetHack engine calls for a key, one is taken from +// the buffer, or if that is empty, QApplication::exec() is called. +// Whenever keys or clicks go into the buffer, QApplication::exit() +// is called. +// +// Another problem is that some NetHack players are decade-long players who +// demand complete keyboard control (while Qt and X11 conspire to make this +// difficult by having widget-based focus rather than application based - +// a good thing in general). This problem is solved by again using the key +// event buffer. +// +// Out of all this hackery comes a silver lining however, as macros for +// the super-expert and menus for the ultra-newbie are also made possible +// by the key event buffer. +// + +// This includes all the definitions we need from the NetHack main +// engine. We pretend MSC is a STDC compiler, because C++ is close +// enough, and we undefine NetHack macros which conflict with Qt +// identifiers. + +#define QT_DEPRECATED_WARNINGS +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4win.h" +#include "qt4bind.h" +#include "qt4click.h" +#include "qt4glyph.h" +#include "qt4inv.h" +#include "qt4key.h" +#include "qt4icon.h" +#include "qt4map.h" +#include "qt4menu.h" +#include "qt4msg.h" +#include "qt4set.h" + +#include + +#include "qt4clust.h" + +#include + +#ifdef _WS_X11_ +// For userid control +#include +#endif + +#ifdef USER_SOUNDS +#if QT_VERSION >= 0x050000 +# include +# else +# include +# endif +#endif + + +#ifdef USER_SOUNDS +extern void play_sound_for_message(const std::string& str); +#endif + +namespace nethack_qt4 { + +void +centerOnMain( QWidget* w ) +{ + QWidget* m = NetHackQtBind::mainWidget(); + if (!m) m = qApp->desktop(); + QPoint p = m->mapToGlobal(QPoint(0,0)); + w->move( p.x() + m->width()/2 - w->width()/2, + p.y() + m->height()/2 - w->height()/2 ); +} + +NetHackQtWindow::NetHackQtWindow() +{ +} +NetHackQtWindow::~NetHackQtWindow() +{ +} + +// XXX Use "expected ..." for now, abort or default later. +// +void NetHackQtWindow::Clear() { puts("unexpected Clear"); } +void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); } +bool NetHackQtWindow::Destroy() { return true; } +void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); } +void NetHackQtWindow::PutStr(int attr, const QString& text) { puts("unexpected PutStr"); } +void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); } +void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel) { puts("unexpected AddMenu"); } +void NetHackQtWindow::EndMenu(const QString& prompt) { puts("unexpected EndMenu"); } +int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; } +void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); } +void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); } +//void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); } +void NetHackQtWindow::UseRIP(int how, time_t when) { puts("unexpected UseRIP"); } + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4win.h b/win/Qt4/qt4win.h new file mode 100644 index 000000000..02e96cd31 --- /dev/null +++ b/win/Qt4/qt4win.h @@ -0,0 +1,49 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// Qt Binding for NetHack 3.4 +// +// Unfortunately, this doesn't use Qt as well as I would like, +// primarily because NetHack is fundamentally a getkey-type +// program rather than being event driven (hence the ugly key +// and click buffer rather), but also because this is my first +// major application of Qt. +// + +#ifndef qt4win_h +#define qt4win_h + +namespace nethack_qt4 { + +class NetHackQtWindow { +public: + NetHackQtWindow(); + virtual ~NetHackQtWindow(); + + virtual QWidget* Widget() =0; + + virtual void Clear(); + virtual void Display(bool block); + virtual bool Destroy(); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const QString& text); + void PutStr(int attr, const char *text) + { + PutStr(attr, QString::fromUtf8(text).replace(QChar(0x200B), "")); + } + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const QString& str, bool presel); + virtual void EndMenu(const QString& prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + virtual void ClipAround(int x,int y); + virtual void PrintGlyph(int x,int y,int glyph); + virtual void UseRIP(int how, time_t when); + + int nhid; +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4xcmd.cpp b/win/Qt4/qt4xcmd.cpp new file mode 100644 index 000000000..3e8703e49 --- /dev/null +++ b/win/Qt4/qt4xcmd.cpp @@ -0,0 +1,134 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4xcmd.cpp -- extended command widget + +#include "hack.h" +#include "func_tab.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4xcmd.h" +#include "qt4xcmd.moc" +#include "qt4bind.h" +#include "qt4set.h" +#include "qt4str.h" + +namespace nethack_qt4 { + +// temporary +void centerOnMain(QWidget *); +// end temporary + +NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(QWidget *parent) : + QDialog(parent) +{ + QVBoxLayout *l = new QVBoxLayout(this); + + QPushButton* can = new QPushButton("Cancel", this); + can->setDefault(true); + can->setMinimumSize(can->sizeHint()); + l->addWidget(can); + + prompt = new QLabel("#", this); + l->addWidget(prompt); + + QButtonGroup *group=new QButtonGroup(this); + QGroupBox *grid=new QGroupBox("Extended commands",this); + l->addWidget(grid); + + int i; + int butw=50; + QFontMetrics fm = fontMetrics(); + for (i=0; extcmdlist[i].ef_txt; i++) { + butw = std::max(butw,30+fm.width(extcmdlist[i].ef_txt)); + } + int ncols=4; + + QVBoxLayout* bl = new QVBoxLayout(grid); + bl->addSpacing(fm.height()); + QGridLayout* gl = new QGridLayout(); + bl->addLayout(gl); + for (i=0; extcmdlist[i].ef_txt; i++) { + QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid); + pb->setMinimumSize(butw,pb->sizeHint().height()); + group->addButton(pb, i+1); + gl->addWidget(pb,i/ncols,i%ncols); + } + group->addButton(can, 0); + connect(group,SIGNAL(buttonPressed(int)),this,SLOT(done(int))); + + bl->activate(); + l->activate(); + resize(1,1); +} + +void NetHackQtExtCmdRequestor::cancel() +{ + reject(); +} + +void NetHackQtExtCmdRequestor::keyPressEvent(QKeyEvent *event) +{ + QString text = event->text(); + if (text == "\r" || text == "\n" || text == " " || text == "\033") + { + reject(); + } + else if (text == "\b") + { + QString promptstr = prompt->text(); + if (promptstr != "#") + prompt->setText(promptstr.left(promptstr.size()-1)); + } + else + { + QString promptstr = prompt->text() + text; + QString typedstr = promptstr.mid(1); // skip the '#' + unsigned matches = 0; + unsigned match = 0; + for (unsigned i=0; extcmdlist[i].ef_txt; i++) { + if (QString(extcmdlist[i].ef_txt).startsWith(typedstr)) { + ++matches; + if (matches >= 2) + break; + match = i; + } + } + if (matches == 1) + done(match+1); + else if (matches >= 2) + prompt->setText(promptstr); + } +} + +int NetHackQtExtCmdRequestor::get() +{ + const int none = -10; + resize(1,1); // pack + centerOnMain(this); + // Add any keys presently buffered to the prompt + setResult(none); + while (NetHackQtBind::qt_kbhit() && result() == none) { + int ch = NetHackQtBind::qt_nhgetch(); + QKeyEvent event(QEvent::KeyPress, 0, Qt::NoModifier, QChar(ch)); + keyPressEvent(&event); + } + if (result() == none) + exec(); + return result()-1; +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4xcmd.h b/win/Qt4/qt4xcmd.h new file mode 100644 index 000000000..29ba23f0d --- /dev/null +++ b/win/Qt4/qt4xcmd.h @@ -0,0 +1,31 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4xcmd.h -- extended command widget + +#ifndef QT4XCMD_H +#define QT4XCMD_H + +namespace nethack_qt4 { + +class NetHackQtExtCmdRequestor : public QDialog { + Q_OBJECT + +protected: + virtual void keyPressEvent(QKeyEvent *event); + +public: + NetHackQtExtCmdRequestor(QWidget *parent); + int get(); + +private: + QLabel *prompt; + +private slots: + void cancel(); +}; + +} // namespace nethack_qt4 + +#endif diff --git a/win/Qt4/qt4yndlg.cpp b/win/Qt4/qt4yndlg.cpp new file mode 100644 index 000000000..e315189de --- /dev/null +++ b/win/Qt4/qt4yndlg.cpp @@ -0,0 +1,244 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4yndlg.cpp -- yes/no dialog + +#include "hack.h" +#undef Invisible +#undef Warning +#undef index +#undef msleep +#undef rindex +#undef wizard +#undef yn +#undef min +#undef max + +#include +#if QT_VERSION >= 0x050000 +#include +#endif +#include "qt4yndlg.h" +#include "qt4yndlg.moc" +#include "qt4str.h" + +// temporary +extern int qt_compact_mode; +// end temporary + +namespace nethack_qt4 { + +// temporary +void centerOnMain(QWidget *); +// end temporary + +NetHackQtYnDialog::NetHackQtYnDialog(QWidget *parent,const QString& q,const char* ch,char df) : + QDialog(parent), + question(q), choices(ch), def(df), + keypress('\033') +{ + setWindowTitle("NetHack: Question"); +} + +char NetHackQtYnDialog::Exec() +{ + QString ch(QString::fromLatin1(choices)); + int ch_per_line=6; + QString qlabel; + QString enable; + if ( qt_compact_mode && !choices ) { + ch = ""; + // expand choices from prompt + // ##### why isn't choices set properly??? + int c = question.indexOf(QChar('[')); + qlabel = QString(question).left(c); + if ( c >= 0 ) { + c++; + if ( question[c] == '-' ) + ch.append(question[c++]); + unsigned from=0; + while ( c < question.size() && question[c] != ']' && question[c] != ' ' ) { + if ( question[c] == '-' ) { + from = question[c-1].unicode(); + } else if ( from != 0 ) { + for (unsigned f=from+1; f<=question[c]; f++) + ch.append(QChar(f)); + from = 0; + } else { + ch.append(question[c]); + from = 0; + } + c++; + } + if ( question[c] == ' ' ) { + while ( c < question.size() && question[c] != ']' ) { + if ( question[c] == '*' || question[c] == '?' ) + ch.append(question[c]); + c++; + } + } + } + if ( question.indexOf("what direction") >= 0 ) { + // We replace this regardless, since sometimes you get choices. + const char* d = Cmd.dirchars; + enable=ch; + ch=""; + ch.append(d[1]); + ch.append(d[2]); + ch.append(d[3]); + ch.append(d[0]); + ch.append('.'); + ch.append(d[4]); + ch.append(d[7]); + ch.append(d[6]); + ch.append(d[5]); + ch.append(d[8]); + ch.append(d[9]); + ch_per_line = 3; + def = ' '; + } else { + // Hmm... they'll have to use a virtual keyboard + } + } else { + ch = QString::fromLatin1(choices); + qlabel = question.replace(QChar(0x200B), QString("")); + } + if (!ch.isNull()) { + QVBoxLayout *vb = new QVBoxLayout; + bool bigq = qlabel.length()>40; + if ( bigq ) { + QLabel* q = new QLabel(qlabel,this); + q->setAlignment(Qt::AlignLeft); + q->setWordWrap(true); + q->setMargin(4); + vb->addWidget(q); + } + QGroupBox *group = new QGroupBox(bigq ? QString::null : qlabel, this); + vb->addWidget(group); + QHBoxLayout *groupbox = new QHBoxLayout(); + group->setLayout(groupbox); + QButtonGroup *bgroup = new QButtonGroup(group); + + int nchoices=ch.length(); + + bool allow_count=ch.contains('#'); + QString yn = "yn", ynq = "ynq"; + bool is_ynq = ch == yn || ch == ynq; + + const int margin=8; + const int gutter=8; + const int extra=fontMetrics().height(); // Extra for group + int x=margin, y=extra+margin; + int butsize=fontMetrics().height()*2+5; + + QPushButton* button; + for (int i=0; isetEnabled(false); + } + button->setFixedSize(butsize,butsize); // Square + if (ch[i]==def) button->setDefault(true); + if (i%10==9) { + // last in row + x=margin; + y+=butsize+gutter; + } else { + x+=butsize+gutter; + } + groupbox->addWidget(button); + bgroup->addButton(button, i); + } + + connect(bgroup,SIGNAL(buttonClicked(int)),this,SLOT(doneItem(int))); + + QLabel* lb=0; + QLineEdit* le=0; + + if (allow_count) { + QHBoxLayout *hb = new QHBoxLayout(this); + lb=new QLabel("Count: "); + hb->addWidget(lb); + le=new QLineEdit(); + hb->addWidget(le); + vb->addLayout(hb); + } + + setLayout(vb); + adjustSize(); + centerOnMain(this); + show(); + char choice=0; + char ch_esc=0; + for (uint i=0; i= 1000 ) { + choice = ch[result() - 1000].unicode(); + } + if (allow_count && !le->text().isEmpty()) { + yn_number=le->text().toInt(); + choice='#'; + } + return choice; + } else { + QLabel label(qlabel,this); + QPushButton cancel("Dismiss",this); + label.setFrameStyle(QFrame::Box|QFrame::Sunken); + label.setAlignment(Qt::AlignCenter); + label.resize(fontMetrics().width(qlabel)+60,30+fontMetrics().height()); + cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8); + connect(&cancel,SIGNAL(clicked()),this,SLOT(reject())); + centerOnMain(this); + setResult(-1); + show(); + keypress = '\033'; + exec(); + return keypress; + } +} + +void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event) +{ + // Don't want QDialog's Return/Esc behaviour + //RLC ...or do we? + QString text(event->text()); + if (choices == NULL || choices[0] == 0) { + if (text != "") { + keypress = text.toUcs4()[0]; + done(1); + } + } else { + int where = QString::fromLatin1(choices).indexOf(text); + if (where != -1 && text != "#") { + done(where+1000); + } else { + QDialog::keyPressEvent(event); + } + } +} + +void NetHackQtYnDialog::doneItem(int i) +{ + done(i+1000); +} + +} // namespace nethack_qt4 diff --git a/win/Qt4/qt4yndlg.h b/win/Qt4/qt4yndlg.h new file mode 100644 index 000000000..d1474f541 --- /dev/null +++ b/win/Qt4/qt4yndlg.h @@ -0,0 +1,34 @@ +// Copyright (c) Warwick Allison, 1999. +// Qt4 conversion copyright (c) Ray Chason, 2012-2014. +// NetHack may be freely redistributed. See license for details. + +// qt4yndlg.h -- yes/no dialog + +#ifndef QT4YNDLG_H +#define QT4YNDLG_H + +namespace nethack_qt4 { + +class NetHackQtYnDialog : QDialog { + Q_OBJECT +private: + QString question; + const char* choices; + char def; + char keypress; + +protected: + virtual void keyPressEvent(QKeyEvent*); + +private slots: + void doneItem(int); + +public: + NetHackQtYnDialog(QWidget *,const QString&,const char*,char); + + char Exec(); +}; + +} // namespace nethack_qt4 + +#endif From b744e2ce1f3d736fe2114299b421426d06839f21 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sat, 7 Oct 2017 14:00:09 -0700 Subject: [PATCH 21/29] Win32GUI: Fix permanent inventory window going away Fix bug where permanent inventory window would go away after any operation that used the inventory window to pick items. Since we were hiding the permanent inventory window, this would also leave a space filled with white, creating jarring visual if using dark themed window backgrounds. --- win/win32/mhmenu.c | 11 +++++++++++ win/win32/mswproc.c | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/win/win32/mhmenu.c b/win/win32/mhmenu.c index bbf364cd4..59004e21f 100644 --- a/win/win32/mhmenu.c +++ b/win/win32/mhmenu.c @@ -246,7 +246,18 @@ mswin_menu_window_select_menu(HWND hWnd, int how, MENU_ITEM_P **_selected, data->is_active = FALSE; LayoutMenu(hWnd); // hide dialog buttons mswin_popup_destroy(hWnd); + + /* If we just used the permanent inventory window to pick something, + * set the menu back to its display inventory state. + */ + if (flags.perm_invent && mswin_winid_from_handle(hWnd) == WIN_INVEN + && how != PICK_NONE) { + data->menu.prompt[0] = '\0'; + SetMenuListType(hWnd, PICK_NONE); + LayoutMenu(hWnd); + } } + return ret_val; } /*-----------------------------------------------------------------------------*/ diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index db045a4cc..1b208053a 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -2202,7 +2202,10 @@ mswin_popup_destroy(HWND hWnd) } DrawMenuBar(GetNHApp()->hMainWnd); - ShowWindow(hWnd, SW_HIDE); + /* Don't hide the permanent inventory window ... leave it showing */ + if (!flags.perm_invent || mswin_winid_from_handle(hWnd) != WIN_INVEN) + ShowWindow(hWnd, SW_HIDE); + GetNHApp()->hPopupWnd = NULL; mswin_layout_main_window(hWnd); From 583edab99f2bf050fdc84312bb4235517c9674d1 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Oct 2017 01:45:46 +0300 Subject: [PATCH 22/29] Qt4: Add menucolors The Qt4 windowport already supported my 3.4.3 menucolors patch, so adding that was simple. --- include/extern.h | 2 +- src/options.c | 2 +- win/Qt4/qt4menu.cpp | 30 +----------------------------- 3 files changed, 3 insertions(+), 31 deletions(-) diff --git a/include/extern.h b/include/extern.h index 018094dd4..ca78d340f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1723,7 +1723,7 @@ E const char *FDECL(clr2colorname, (int)); E int FDECL(match_str2clr, (char *)); E int FDECL(match_str2attr, (const char *, BOOLEAN_P)); E boolean FDECL(add_menu_coloring, (char *)); -E boolean FDECL(get_menu_coloring, (char *, int *, int *)); +E boolean FDECL(get_menu_coloring, (const char *, int *, int *)); E void NDECL(free_menu_coloring); E boolean FDECL(msgtype_parse_add, (char *)); E int FDECL(msgtype_type, (const char *, BOOLEAN_P)); diff --git a/src/options.c b/src/options.c index c17e44869..7086a7b5e 100644 --- a/src/options.c +++ b/src/options.c @@ -1747,7 +1747,7 @@ char *tmpstr; boolean get_menu_coloring(str, color, attr) -char *str; +const char *str; int *color, *attr; { struct menucoloring *tmpmc; diff --git a/win/Qt4/qt4menu.cpp b/win/Qt4/qt4menu.cpp index b15aa0ee5..7d70a26e2 100644 --- a/win/Qt4/qt4menu.cpp +++ b/win/Qt4/qt4menu.cpp @@ -32,9 +32,7 @@ extern "C" { extern "C" int qt_compact_mode; // end temporary -#ifdef MENU_COLOR extern "C" struct menucoloring *menu_colorings; -#endif namespace nethack_qt4 { @@ -42,8 +40,6 @@ namespace nethack_qt4 { void centerOnMain( QWidget* w ); // end temporary -static boolean get_menu_coloring(char const *str, int *color, int *attr); - QSize NetHackQtTextListBox::sizeHint() const { QScrollBar *hscroll = horizontalScrollBar(); @@ -194,39 +190,17 @@ void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier, + str.mid(bracket+2); } } -#ifdef MENU_COLOR int mcolor, mattr; if (attr == 0 - && get_menu_coloring(str.toLatin1().constData(), &mcolor, &mattr)) { + && get_menu_coloring(str.toLatin1().constData(), &mcolor, &mattr)) { itemlist[itemcount].attr = mattr; itemlist[itemcount].color = mcolor; } -#endif ++itemcount; if (glyph!=NO_GLYPH) has_glyphs=true; } -#ifdef MENU_COLOR -static boolean -get_menu_coloring(char const *str, int *color, int *attr) -{ - struct menucoloring *tmpmc; - if (iflags.use_menu_color) - for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next) -# ifdef MENU_COLOR_REGEX - if (re_search(&tmpmc->match, str, strlen(str), 0, 9999, 0) >= 0) { -# else - if (pmatch(tmpmc->match, str)) { -# endif - *color = tmpmc->color; - *attr = tmpmc->attr; - return TRUE; - } - return FALSE; -} -#endif /* MENU_COLOR */ - void NetHackQtMenuWindow::EndMenu(const QString& p) { prompt.setText(p); @@ -417,11 +391,9 @@ void NetHackQtMenuWindow::AddRow(int row, const MenuItem& mi) table->item(row, 4)->setFlags(Qt::ItemIsEnabled); WidenColumn(4, fm.width(text)); -#ifdef MENU_COLOR if (mi.color != -1) { twi->setForeground(colors[mi.color]); } -#endif QFont itemfont(table->font()); switch (mi.attr) { From 8656807b4de6ad118dffc7adda90ab662c21e410 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sat, 7 Oct 2017 10:53:03 -0700 Subject: [PATCH 23/29] Windows GUI supports WC2_HILITE_STATUS Avoids raw_print on startup --- win/win32/mswproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 1b208053a..236396b83 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -86,7 +86,7 @@ struct window_procs mswin_procs = { | WC_VARY_MSGCOUNT | WC_WINDOWCOLORS | WC_PLAYER_SELECTION | WC_SPLASH_SCREEN | WC_POPUP_DIALOG | WC_MOUSE_SUPPORT, #ifdef STATUS_HILITES - WC2_HITPOINTBAR | WC2_FLUSH_STATUS | + WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_HILITE_STATUS | #endif 0L, mswin_init_nhwindows, mswin_player_selection, mswin_askname, mswin_get_nh_event, mswin_exit_nhwindows, mswin_suspend_nhwindows, From e8ef27d985579d40777fabcab776a2db5af3bd3a Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Oct 2017 03:04:52 +0300 Subject: [PATCH 24/29] Enable dumplogs for X11 and Qt4 --- src/rip.c | 2 +- sys/unix/hints/linux | 4 ++-- sys/unix/hints/linux-qt4 | 2 ++ sys/unix/hints/linux-x11 | 8 +++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/rip.c b/src/rip.c index 11abaea63..b8ef7f594 100644 --- a/src/rip.c +++ b/src/rip.c @@ -7,7 +7,7 @@ STATIC_DCL void FDECL(center, (int, char *)); #if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \ - || defined(MSWIN_GRAPHICS) + || defined(MSWIN_GRAPHICS) || defined(DUMPLOG) #define TEXT_TOMBSTONE #endif #if defined(mac) || defined(__BEOS__) || defined(WIN32_GRAPHICS) diff --git a/sys/unix/hints/linux b/sys/unix/hints/linux index 8edd35afc..4dc6b2f53 100644 --- a/sys/unix/hints/linux +++ b/sys/unix/hints/linux @@ -20,8 +20,8 @@ VARDIR = $(HACKDIR) POSTINSTALL=cp -n sys/unix/sysconf $(INSTDIR)/sysconf; $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf; $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf; chmod $(VARFILEPERM) $(INSTDIR)/sysconf; -CFLAGS=-g -O -I../include -DNOTPARMDECL $(CFLAGS1) -DDLB -CFLAGS1=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" +CFLAGS=-g -O -I../include -DNOTPARMDECL -DDLB +CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE CFLAGS+=-DTIMED_DELAY CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" diff --git a/sys/unix/hints/linux-qt4 b/sys/unix/hints/linux-qt4 index 0feddb9b5..4a3d4c1fd 100644 --- a/sys/unix/hints/linux-qt4 +++ b/sys/unix/hints/linux-qt4 @@ -24,6 +24,8 @@ CFLAGS=-O -I../include -DNOTPARMDECL CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" +CFLAGS+=-DDUMPLOG +CFLAGS+=-DCONFIG_ERROR_SECURE=FALSE CFLAGS+=-DQT_GRAPHICS -DDEFAULT_WINDOW_SYS=\"Qt\" -DNOTTYGRAPHICS CFLAGS+=`pkg-config QtGui --cflags` diff --git a/sys/unix/hints/linux-x11 b/sys/unix/hints/linux-x11 index 0728234aa..a0d5552e2 100644 --- a/sys/unix/hints/linux-x11 +++ b/sys/unix/hints/linux-x11 @@ -20,10 +20,12 @@ VARDIR = $(HACKDIR) POSTINSTALL= cp -n sys/unix/sysconf $(INSTDIR)/sysconf; $(CHOWN) $(GAMEUID) $(INSTDIR)/sysconf; $(CHGRP) $(GAMEGRP) $(INSTDIR)/sysconf; chmod $(VARFILEPERM) $(INSTDIR)/sysconf; POSTINSTALL+= bdftopcf win/X11/nh10.bdf > $(INSTDIR)/nh10.pcf; (cd $(INSTDIR); mkfontdir); -CFLAGS=-O -I../include -DNOTPARMDECL $(CFLAGS1) $(CFLAGS3) +CFLAGS=-O -I../include -DNOTPARMDECL CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -CFLAGS1=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" -CFLAGS3=-DX11_GRAPHICS -DDEFAULT_WINDOW_SYS=\"X11\" -DNOTTYGRAPHICS +CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" +CFLAGS+=-DX11_GRAPHICS -DDEFAULT_WINDOW_SYS=\"X11\" -DNOTTYGRAPHICS +CFLAGS+=-DDUMPLOG +CFLAGS+=-DCONFIG_ERROR_SECURE=FALSE LINK=$(CC) From a69c57b865d9cfea17213b47b98e6cba8b95d5a9 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Oct 2017 10:44:59 +0300 Subject: [PATCH 25/29] Automatically define LINUX, ifdef __linux__ --- include/unixconf.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/unixconf.h b/include/unixconf.h index 236994bce..0d4fbce17 100644 --- a/include/unixconf.h +++ b/include/unixconf.h @@ -36,7 +36,9 @@ #define NETWORK /* if running on a networked system */ /* e.g. Suns sharing a playground through NFS */ /* #define SUNOS4 */ /* SunOS 4.x */ -/* #define LINUX */ /* Another Unix clone */ +#ifdef __linux__ +#define LINUX /* Another Unix clone */ +#endif /* #define CYGWIN32 */ /* Unix on Win32 -- use with case sensitive defines */ /* #define GENIX */ /* Yet Another Unix Clone */ /* #define HISX */ /* Bull Unix for XPS Machines */ From 6b51f6ce094e88658f3f2054fdaf2ccdce2a8099 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Oct 2017 11:28:28 +0300 Subject: [PATCH 26/29] Qt4: Use TIMED_DELAY and same delay as tty --- sys/unix/hints/linux-qt4 | 1 + win/Qt4/qt4bind.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sys/unix/hints/linux-qt4 b/sys/unix/hints/linux-qt4 index 4a3d4c1fd..51f6ad05b 100644 --- a/sys/unix/hints/linux-qt4 +++ b/sys/unix/hints/linux-qt4 @@ -24,6 +24,7 @@ CFLAGS=-O -I../include -DNOTPARMDECL CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" +CFLAGS+=-DTIMED_DELAY CFLAGS+=-DDUMPLOG CFLAGS+=-DCONFIG_ERROR_SECURE=FALSE CFLAGS+=-DQT_GRAPHICS -DDEFAULT_WINDOW_SYS=\"Qt\" -DNOTTYGRAPHICS diff --git a/win/Qt4/qt4bind.cpp b/win/Qt4/qt4bind.cpp index 5dc17de99..feed0a656 100644 --- a/win/Qt4/qt4bind.cpp +++ b/win/Qt4/qt4bind.cpp @@ -26,7 +26,9 @@ extern "C" { #endif #include "qt4bind.h" #include "qt4click.h" +#ifdef TIMED_DELAY #include "qt4delay.h" +#endif #include "qt4xcmd.h" #include "qt4key.h" #include "qt4map.h" @@ -581,8 +583,10 @@ void NetHackQtBind::qt_number_pad(int) void NetHackQtBind::qt_delay_output() { - NetHackQtDelay delay(15); +#ifdef TIMED_DELAY + NetHackQtDelay delay(50); delay.wait(); +#endif } void NetHackQtBind::qt_start_screen() From 5c77360023f84a9a709f52fac10cf43fd3167d5f Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 8 Oct 2017 03:29:16 -0700 Subject: [PATCH 27/29] still more Master Key of Thievery Make #untrap while carrying the non-cursed (for rogues) or blessed (for non-rogues) Key work the same as #invoke has been doing (without regard to its bless/curse state): when used on trapped door or chest, that trap will always be found and disarming it will always succeed. It should work when carried by monsters too: if they try to open a trapped door while carrying the Key (must be blessed since they're not rogues) the trap will be automatically disarmed. (Caveat: that hasn't been adequately tested.) TODO (maybe...): change the #invoke property to detect unseen/secret door detection instead of #untrap. The latter isn't completely redundant; it works when the Key is cursed. But quest artifacts strongly resist becoming cursed so that isn't a particularly useful distinction. Also, trap hints when wielding the Key without gloves didn't notice adjacent door and chest traps. Now it does. And the behavior is slightly different: known traps covered by objects or monsters are treated like unknown traps as far as the hot/cold hints go. --- include/artilist.h | 3 ++ include/extern.h | 6 ++- src/artifact.c | 100 +++++++++++++++++++++++++++++++++++++-------- src/lock.c | 16 -------- src/monmove.c | 10 +++++ src/trap.c | 5 +++ 6 files changed, 105 insertions(+), 35 deletions(-) diff --git a/include/artilist.h b/include/artilist.h index e06e99a7f..d5992bcef 100644 --- a/include/artilist.h +++ b/include/artilist.h @@ -208,6 +208,9 @@ A("The Palantir of Westernesse", CRYSTAL_BALL, PHYS(5, 0), NO_DFNS, NO_CARY, CREATE_AMMO, A_CHAOTIC, PM_RANGER, NON_PM, 4000L, NO_COLOR), + /* MKoT has an additional carry property if the Key is not cursed (for + rogues) or blessed (for non-rogues): #untrap of doors and chests + will always find any traps and disarming those will always succeed */ A("The Master Key of Thievery", SKELETON_KEY, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_SPEAK), (SPFX_WARN | SPFX_TCTRL | SPFX_HPHDAM), 0, NO_ATTK, NO_DFNS, NO_CARY, diff --git a/include/extern.h b/include/extern.h index ca78d340f..cc2bf44dc 100644 --- a/include/extern.h +++ b/include/extern.h @@ -78,8 +78,8 @@ E int FDECL(spec_dbon, (struct obj *, struct monst *, int)); E void FDECL(discover_artifact, (XCHAR_P)); E boolean FDECL(undiscovered_artifact, (XCHAR_P)); E int FDECL(disp_artifact_discoveries, (winid)); -E boolean FDECL(artifact_hit, - (struct monst *, struct monst *, struct obj *, int *, int)); +E boolean FDECL(artifact_hit, (struct monst *, struct monst *, struct obj *, + int *, int)); E int NDECL(doinvoke); E boolean FDECL(finesse_ahriman, (struct obj *)); E void FDECL(arti_speak, (struct obj *)); @@ -93,6 +93,8 @@ E void FDECL(Sting_effects, (int)); E int FDECL(retouch_object, (struct obj **, BOOLEAN_P)); E void FDECL(retouch_equipment, (int)); E void NDECL(mkot_trap_warn); +E boolean FDECL(is_magic_key, (struct monst *, struct obj *)); +E struct obj *FDECL(has_magic_key, (struct monst *)); /* ### attrib.c ### */ diff --git a/src/artifact.c b/src/artifact.c index 4fdfc828a..25fc17118 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -2055,36 +2055,102 @@ int dropflag; /* 0==don't drop, 1==drop all, 2==drop weapon */ static int mkot_trap_warn_count = 0; STATIC_OVL int -count_surround_traps(x,y) -int x,y; +count_surround_traps(x, y) +int x, y; { - int dx, dy, ret = 0; + struct rm *levp; + struct obj *otmp; struct trap *ttmp; + int dx, dy, glyph, ret = 0; - for (dx = x-1; dx < x+2; dx++) - for (dy = y-1; dy < y+2; dy++) - if (isok(dx,dy) && (ttmp = t_at(dx,dy)) - && !ttmp->tseen) - ret++; + for (dx = x - 1; dx < x + 2; ++dx) + for (dy = y - 1; dy < y + 2; ++dy) { + if (!isok(dx, dy)) + continue; + /* If a trap is shown here, don't count it; the hero + * should be expecting it. But if there is a trap here + * that's not shown, either undiscovered or covered by + * something, do count it. + */ + glyph = glyph_at(dx, dy); + if (glyph_is_trap(glyph)) + continue; + if ((ttmp = t_at(dx, dy)) != 0) { + ++ret; + continue; + } + levp = &levl[dx][dy]; + if (IS_DOOR(levp->typ) && (levp->doormask & D_TRAPPED) != 0) { + ++ret; + continue; + } + for (otmp = level.objects[dx][dy]; otmp; otmp = otmp->nexthere) + if (Is_container(otmp) && otmp->otrapped) { + ++ret; /* we're counting locations, so just */ + break; /* count the first one in a pile */ + } + } + /* + * [Shouldn't we also check inventory for a trapped container? + * Even if its trap has already been found, there's no 'tknown' + * flag to help hero remember that so we have nothing comparable + * to a shown glyph to justify skipping it.] + */ return ret; } +/* sense adjacent traps if wielding MKoT without wearing gloves */ void mkot_trap_warn() { - if (!uarmg && uwep - && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) { - const char *const heat[7] = { "cold", "slightly warm", "warm", - "very warm", "hot", "very hot", - "like fire" }; - int ntraps = count_surround_traps(u.ux, u.uy); + static const char *const heat[7] = { + "cool", "slightly warm", "warm", "very warm", + "hot", "very hot", "like fire" + }; - if (ntraps != mkot_trap_warn_count) - pline_The("key feels %s%c", heat[(ntraps > 6) ? 6 : ntraps], - ntraps > 3 ? '!' : '.'); + if (!uarmg && uwep && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) { + int idx, ntraps = count_surround_traps(u.ux, u.uy); + + if (ntraps != mkot_trap_warn_count) { + idx = min(ntraps, SIZE(heat) - 1); + pline_The("Key feels %s%c", heat[idx], (ntraps > 3) ? '!' : '.'); + } mkot_trap_warn_count = ntraps; } else mkot_trap_warn_count = 0; } +/* Master Key is magic key if its bless/curse state meets our criteria: + not cursed for rogues or blessed for non-rogues */ +boolean +is_magic_key(mon, obj) +struct monst *mon; /* if null, non-rogue is assumed */ +struct obj *obj; +{ + if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY) + && ((mon == &youmonst) ? Role_if(PM_ROGUE) + : (mon && mon->data == &mons[PM_ROGUE]))) + ? !obj->cursed : obj->blessed) + return TRUE; + return FALSE; +} + +/* figure out whether 'mon' (usually youmonst) is carrying the magic key */ +struct obj * +has_magic_key(mon) +struct monst *mon; /* if null, hero assumed */ +{ + struct obj *o; + short key = artilist[ART_MASTER_KEY_OF_THIEVERY].otyp; + + if (!mon) + mon = &youmonst; + for (o = ((mon == &youmonst) ? invent : mon->minvent); o; + o = nxtobj(o, key, FALSE)) { + if (is_magic_key(mon, o)) + return o; + } + return (struct obj *) 0; +} + /*artifact.c*/ diff --git a/src/lock.c b/src/lock.c index 19dfabe46..426e8a35f 100644 --- a/src/lock.c +++ b/src/lock.c @@ -18,7 +18,6 @@ STATIC_PTR int NDECL(picklock); STATIC_PTR int NDECL(forcelock); STATIC_DCL const char *NDECL(lock_action); -STATIC_DCL boolean FDECL(is_magic_key, (struct monst *, struct obj *)); STATIC_DCL boolean FDECL(obstructed, (int, int, BOOLEAN_P)); STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *)); @@ -275,21 +274,6 @@ maybe_reset_pick() reset_pick(); } -/* Master Key is magic key if its bless/curse state meets our criteria: - not cursed for rogues or blessed for non-rogues */ -STATIC_OVL boolean -is_magic_key(mon, obj) -struct monst *mon; /* if null, non-rogue is assumed */ -struct obj *obj; -{ - if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY) - && ((mon == &youmonst) ? Role_if(PM_ROGUE) - : (mon && mon->data == &mons[PM_ROGUE]))) - ? !obj->cursed : obj->blessed) - return TRUE; - return FALSE; -} - /* for doapply(); if player gives a direction or resumes an interrupted previous attempt then it costs hero a move even if nothing ultimately happens; when told "can't do that" before being asked for direction diff --git a/src/monmove.c b/src/monmove.c index ab17709f4..26d7e2144 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1246,6 +1246,16 @@ postmov: boolean btrapped = (here->doormask & D_TRAPPED) != 0, observeit = canseeit && canspotmon(mtmp); + /* if mon has MKoT, disarm door trap; no message given */ + if (btrapped && has_magic_key(mtmp)) { + /* BUG: this lets a vampire or blob or a doorbuster + holding the Key disarm the trap even though it isn't + using that Key when squeezing under or smashing the + door. Not significant enough to worry about; perhaps + the Key's magic is more powerful for monsters? */ + here->doormask &= ~D_TRAPPED; + btrapped = FALSE; + } if ((here->doormask & (D_LOCKED | D_CLOSED)) != 0 && (amorphous(ptr) || (can_fog(mtmp) diff --git a/src/trap.c b/src/trap.c index 53493d426..401d5a7fc 100644 --- a/src/trap.c +++ b/src/trap.c @@ -4256,6 +4256,11 @@ boolean force; pline_The("perils lurking there are beyond your grasp."); return 0; } + /* 'force' is true for #invoke; make it be true for #untrap if + carrying MKoT */ + if (!force && has_magic_key(&youmonst)) + force = TRUE; + ttmp = t_at(x, y); if (ttmp && !ttmp->tseen) ttmp = 0; From 61e2960c0276f06c5561e7ac526dc88cacdd010c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Oct 2017 18:36:53 +0300 Subject: [PATCH 28/29] win32: Turn off autopickup by default Watching new players stream NetHack, the conclusion is that everyone turns off autopickup by default... once they learn how to do it. --- sys/winnt/defaults.nh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sys/winnt/defaults.nh b/sys/winnt/defaults.nh index ee1b479f9..8edd4ce6e 100644 --- a/sys/winnt/defaults.nh +++ b/sys/winnt/defaults.nh @@ -36,12 +36,17 @@ OPTIONS=symset:IBMGraphics_2,roguesymset:RogueEpyx #OPTIONS=name:Janet,role:Valkyrie,race:Human,gender:female,align:lawful #OPTIONS=dogname:Fido,catname:Morris,fruit:guava #OPTIONS=horsename:Silver -#OPTIONS=autopickup,pickup_types:$"=/!?+ + +# Disable autopickup +OPTIONS=!autopickup + +# When autopickup is on, automatically pick up these types of objects +#OPTIONS=pickup_types:$"=/!?+ + #OPTIONS=packorder:")[%?+/=!(*0_` #OPTIONS=scores:10 top/2 around/own #OPTIONS=nolegacy,noverbose #OPTIONS=menustyle:traditional -#OPTIONS=hilite_status:hitpoints/30%/bright-magenta/normal #OPTIONS=perm_invent From 4659c55b5c1c687bbcf8d0c84a77e445b2ca351c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sun, 8 Oct 2017 18:56:07 +0300 Subject: [PATCH 29/29] Win32GUI: increase scroll_margin Scrolling when 5 tiles from the edge is far too little, you could get zapped from farther away. --- sys/winnt/defaults.nh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/winnt/defaults.nh b/sys/winnt/defaults.nh index 8edd4ce6e..079ef0400 100644 --- a/sys/winnt/defaults.nh +++ b/sys/winnt/defaults.nh @@ -98,7 +98,7 @@ OPTIONS=suppress_alert:3.3.1 # possible map_mode options include: tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8| # ascii7x12|ascii8x12|ascii16x12|ascii12x16| # ascii10x18|fit_to_screen -OPTIONS=map_mode:tiles,scroll_margin:5 +OPTIONS=map_mode:tiles,scroll_margin:10 # Message window settings OPTIONS=font_message:Arial,font_size_message:9,align_message:top