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 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 diff --git a/doc/fixes36.1 b/doc/fixes36.1 index dcfa8eec0..e6f3e7c78 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -350,12 +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 -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 @@ -453,6 +447,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 @@ -675,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 @@ -709,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/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 3c1f79ed3..203e51076 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 *)); @@ -92,6 +92,9 @@ 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); +E boolean FDECL(is_magic_key, (struct monst *, struct obj *)); +E struct obj *FDECL(has_magic_key, (struct monst *)); /* ### attrib.c ### */ @@ -167,6 +170,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); @@ -320,6 +326,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)); @@ -1721,7 +1728,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/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) 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 */ diff --git a/src/allmain.c b/src/allmain.c index 0519cfab7..684f08e66 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/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/artifact.c b/src/artifact.c index 13e8be981..25fc17118 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,105 @@ 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; +{ + 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)) + 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() +{ + static const char *const heat[7] = { + "cool", "slightly warm", "warm", "very warm", + "hot", "very hot", "like fire" + }; + + 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/botl.c b/src/botl.c index 03f2aea11..ca6c494cf 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) @@ -2330,7 +2334,7 @@ int fld; } int -count_status_hilites() +count_status_hilites(VOID_ARGS) { int count; status_hilite_linestr_gather(); 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/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/lock.c b/src/lock.c index 94073f3ae..426e8a35f 100644 --- a/src/lock.c +++ b/src/lock.c @@ -4,17 +4,19 @@ #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(obstructed, (int, int, BOOLEAN_P)); STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *)); @@ -105,6 +107,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 +261,7 @@ void reset_pick() { xlock.usedtime = xlock.chance = xlock.picktyp = 0; + xlock.magic_key = FALSE; xlock.door = 0; xlock.box = 0; } @@ -264,6 +301,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 +315,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 +330,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 +415,6 @@ struct obj *pick; if (otmp->cursed) ch /= 2; - xlock.picktyp = picktyp; xlock.box = otmp; xlock.door = 0; break; @@ -407,7 +446,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 +501,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 +571,7 @@ doforce() xlock.box = otmp; xlock.chance = objects[uwep->otyp].oc_wldam * 2; xlock.picktyp = picktyp; + xlock.magic_key = FALSE; xlock.usedtime = 0; break; } 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/options.c b/src/options.c index d5842bb76..7086a7b5e 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: 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. */ @@ -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 @@ -654,7 +652,7 @@ initoptions() * lists of usernames into arrays with one name per element. */ #endif -#endif +#endif /* SYSCF */ initoptions_finish(); } @@ -811,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); /* @@ -830,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; } @@ -1595,7 +1606,7 @@ int hide_mask; } STATIC_OVL int -msgtype_count() +msgtype_count(VOID_ARGS) { int c = 0; struct plinemsg_type *tmp = plinemsg_types; @@ -1736,7 +1747,7 @@ char *tmpstr; boolean get_menu_coloring(str, color, attr) -char *str; +const char *str; int *color, *attr; { struct menucoloring *tmpmc; @@ -1793,7 +1804,7 @@ int idx; /* 0 .. */ } STATIC_OVL int -count_menucolors() +count_menucolors(VOID_ARGS) { int count = 0; struct menucoloring *tmp = menu_colorings; @@ -2172,8 +2183,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); @@ -2558,11 +2570,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) { @@ -2583,7 +2596,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. @@ -2597,8 +2610,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"; @@ -2866,7 +2883,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 +3369,7 @@ boolean tinitial, tfrom_file; return FALSE; return retval; } + #ifdef WINCHAIN fullname = "windowchain"; if (match_optname(opts, fullname, 3, TRUE)) { @@ -3465,9 +3484,10 @@ boolean tinitial, tfrom_file; return retval; } } -#ifdef STATUS_HILITES /* 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); op = string_for_opt(opts, TRUE); @@ -3481,11 +3501,16 @@ boolean tinitial, tfrom_file; if (!parse_status_hl1(op, tfrom_file)) return FALSE; return retval; +#else + config_error_add("'%s' is not supported", fullname); + 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,12 +3522,15 @@ boolean tinitial, tfrom_file; if (!tfrom_file) reset_status_hilites(); return retval; - } +#else + 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) @@ -3525,9 +3553,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; @@ -3558,11 +3593,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) @@ -3587,8 +3627,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 @@ -3612,7 +3662,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")) { @@ -3673,9 +3725,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; @@ -3964,7 +4018,7 @@ int nset; } int -count_apes() +count_apes(VOID_ARGS) { return count_ape_maps((int *) 0, (int *) 0); } @@ -4117,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; @@ -6037,10 +6095,12 @@ 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 + /* 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 } }; /* 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 */ 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; 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/src/trap.c b/src/trap.c index 91584da94..401d5a7fc 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]; @@ -4255,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; @@ -4434,7 +4440,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!"); diff --git a/src/uhitm.c b/src/uhitm.c index 0fcf8445c..97e49dbca 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,65 @@ 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) { + (void) unmap_invisible(x + xdir[i], y + ydir[i]); + 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 +563,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); 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/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); 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 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 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 new file mode 100644 index 000000000..51f6ad05b --- /dev/null +++ b/sys/unix/hints/linux-qt4 @@ -0,0 +1,50 @@ +# +# 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+=-DTIMED_DELAY +CFLAGS+=-DDUMPLOG +CFLAGS+=-DCONFIG_ERROR_SECURE=FALSE +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/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) diff --git a/sys/winnt/defaults.nh b/sys/winnt/defaults.nh index ee1b479f9..079ef0400 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 @@ -93,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 diff --git a/util/makedefs.c b/util/makedefs.c index 97d7f10a9..a4a3f558d 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. */ @@ -1451,21 +1451,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 @@ -1486,6 +1492,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 @@ -1553,7 +1561,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 { @@ -1618,8 +1627,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, @@ -1663,7 +1671,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) @@ -1675,7 +1684,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; diff --git a/win/Qt4/qt4bind.cpp b/win/Qt4/qt4bind.cpp new file mode 100644 index 000000000..feed0a656 --- /dev/null +++ b/win/Qt4/qt4bind.cpp @@ -0,0 +1,749 @@ +// 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" +#ifdef TIMED_DELAY +#include "qt4delay.h" +#endif +#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() +{ +#ifdef TIMED_DELAY + NetHackQtDelay delay(50); + delay.wait(); +#endif +} + +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..7d70a26e2 --- /dev/null +++ b/win/Qt4/qt4menu.cpp @@ -0,0 +1,812 @@ +// 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 + +extern "C" struct menucoloring *menu_colorings; + +namespace nethack_qt4 { + +// temporary +void centerOnMain( QWidget* w ); +// end temporary + +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); + } + } + int mcolor, mattr; + if (attr == 0 + && get_menu_coloring(str.toLatin1().constData(), &mcolor, &mattr)) { + itemlist[itemcount].attr = mattr; + itemlist[itemcount].color = mcolor; + } + ++itemcount; + + if (glyph!=NO_GLYPH) has_glyphs=true; +} + +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)); + + if (mi.color != -1) { + twi->setForeground(colors[mi.color]); + } + + 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 diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 2eb84b0cc..821239c68 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: 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, @@ -3431,25 +3433,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 +3583,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 +3775,6 @@ int condcolor(bm, bmarray) long bm; unsigned long *bmarray; { -#ifdef STATUS_HILITES int i; if (bm && bmarray) @@ -3781,7 +3782,6 @@ unsigned long *bmarray; if (bmarray[i] && (bm & bmarray[i])) return i; } -#endif return NO_COLOR; } #endif /* TEXTCOLOR */ @@ -3820,7 +3820,6 @@ unsigned long *bmarray; } #endif /* STATUS_HILITES */ - #endif /* TTY_GRAPHICS */ /*wintty.c*/ 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..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, @@ -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); 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