From 58477b33f4e797fab8c8e5202e146a678de448d5 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 24 Oct 2017 00:37:21 -0700 Subject: [PATCH 01/25] more fix for #H5056 - achievement tracking The followup message about the fix for #5056 was trapped by the spam filter so didn't reach us for a while. xlogfile has an extra field to track various achievements made during the game it logs, two of which are fully exploring the gnomish mines and fully exploring sokoban. Those are accomplished by finding the special 'prize' item on the final level of their branch: luckstone for mines and bag of holding or amulet of reflecition for sokoban. 3.6.0 had a bug where any item of the target type found anywhere in the dungeon resulted in achieving the relevant goal. A post-3.6.1 fix for that required that the item be found on the end level of the branch and attempted to require that it an item explicitly placed there by the special level loader, but the latter aspect had a bug which meant that random items of the appropriate type placed on final level would count as the prize. Chance of extra luckstones on mines' end is fairly high, so potential for false completion of the achievement was also high. The second complaint was that since the achievement was only recorded if the special prize item was found on final level, then if a monster took it to another level then the achievement became impossible. (Not true, the player could take it back, drop it, and pick it up again, but that is admittedly a pretty silly hoop to jump through.) On the other hand, if a monster removed the item before the hero found it, then a case could be made that the hero hadn't really fully explored the level. However, this fix records the achievement no matter where the hero picks up the item. The final level must be entered--otherwise no monster could possibly acquire and transport the item--but it isn't guaranteed to have been fully explored. Big deal.... The prize could also be acquired in bones data. Before the second portion of this fix, that wouldn't have mattered. But now it does, so clear the prize indicator when saving bones unless it happens to be the same level where that item is created (impossible for sokoban, where no bones are left; not sure offhand about mines' end). The former prize stone or bag or amulet becomes an ordinary one of its type. This can all be done in a much cleaner fashion once we give up on the current save file compatability. Putting obj->o_id values into new context.mines_prize and context.soko_prize, plus a hack to mkobj() to not reuse those two values if the o_id counter ever wraps back to 0, would cover most of the details. Adding an achievement tracking flag to lev_comp's object handling for use by the special level loader would cover most of the rest. --- doc/fixes36.1 | 1 + include/flag.h | 24 ++++++++++++++---------- include/obj.h | 20 +++++++++++++------- src/bones.c | 36 +++++++++++++++++++++--------------- src/invent.c | 19 ++++++++++++++----- src/options.c | 8 +++++++- src/sp_lev.c | 35 ++++++++++++++++++++++++++++------- 7 files changed, 98 insertions(+), 45 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index ff470d323..7be6ef2e3 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -521,6 +521,7 @@ hero poly'd into vampire could drain monster down to 0 HP without killing it, fix mention_walls reporting secret doors as solid stone jumping over water unintentionally moved hero through that water, causing drowning if not able to water walk or fly +try again to fix achievement recording bug with mines and sokoban prizes Platform- and/or Interface-Specific Fixes diff --git a/include/flag.h b/include/flag.h index 5b205e5b7..3bccbaa18 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 flag.h $NHDT-Date: 1505214875 2017/09/12 11:14:35 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.123 $ */ +/* NetHack 3.6 flag.h $NHDT-Date: 1508827590 2017/10/24 06:46:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -349,25 +349,24 @@ struct instance_flags { int wc_fontsiz_menu; /* font size for the menu window */ int wc_fontsiz_text; /* font size for text windows */ int wc_scroll_amount; /* scroll this amount at scroll_margin */ - int wc_scroll_margin; /* scroll map when this far from - the edge */ + int wc_scroll_margin; /* scroll map when this far from the edge */ int wc_map_mode; /* specify map viewing options, mostly - for backward compatibility */ + * for backward compatibility */ int wc_player_selection; /* method of choosing character */ boolean wc_splash_screen; /* display an opening splash screen or not */ boolean wc_popup_dialog; /* put queries in pop up dialogs instead of - in the message window */ + * in the message window */ boolean wc_eight_bit_input; /* allow eight bit input */ boolean wc_mouse_support; /* allow mouse support */ boolean wc2_fullscreen; /* run fullscreen */ boolean wc2_softkeyboard; /* use software keyboard */ boolean wc2_wraptext; /* wrap text */ boolean wc2_selectsaved; /* display a menu of user's saved games */ - boolean wc2_darkgray; /* try to use dark-gray color for black glyphs */ - boolean wc2_hitpointbar; /* show graphical bar representing hit points */ - boolean cmdassist; /* provide detailed assistance for some commands */ - boolean clicklook; /* allow right-clicking for look */ - boolean obsolete; /* obsolete options can point at this, it isn't used */ + boolean wc2_darkgray; /* try to use dark-gray color for black glyphs */ + boolean wc2_hitpointbar; /* show graphical bar representing hit points */ + boolean cmdassist; /* provide detailed assistance for some commands */ + boolean clicklook; /* allow right-clicking for look */ + boolean obsolete; /* obsolete options can point at this, it isn't used */ struct autopickup_exception *autopickup_exceptions[2]; #define AP_LEAVE 0 #define AP_GRAB 1 @@ -381,6 +380,11 @@ struct instance_flags { Bitfield(save_uswallow, 1); Bitfield(save_uinwater, 1); Bitfield(save_uburied, 1); + /* item types used to acomplish "special achievements"; find the target + object and you'll be flagged as having achieved something... */ + short mines_prize_type; /* luckstone */ + short soko_prize_type1; /* bag of holding or */ + short soko_prize_type2; /* amulet of reflection */ }; /* diff --git a/include/obj.h b/include/obj.h index 75b35779d..f87caaa02 100644 --- a/include/obj.h +++ b/include/obj.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 obj.h $NHDT-Date: 1456618994 2016/02/28 00:23:14 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.53 $ */ +/* NetHack 3.6 obj.h $NHDT-Date: 1508827590 2017/10/24 06:46:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -343,13 +343,19 @@ struct obj { && !undiscovered_artifact(ART_EYES_OF_THE_OVERWORLD))) #define pair_of(o) ((o)->otyp == LENSES || is_gloves(o) || is_boots(o)) +/* 'PRIZE' values override obj->corpsenm so prizes mustn't be object types + which use that field for monster type (or other overloaded purpose) */ +#define MINES_PRIZE 1 +#define SOKO_PRIZE1 2 +#define SOKO_PRIZE2 3 #define is_mines_prize(o) \ - ((o)->otyp == LUCKSTONE && Is_mineend_level(&u.uz)) -#define is_soko_prize(o) \ - (((o)->otyp == AMULET_OF_REFLECTION \ - || (o)->otyp == BAG_OF_HOLDING) \ - && Is_sokoend_level(&u.uz)) - + ((o)->otyp == iflags.mines_prize_type \ + && (o)->record_achieve_special == MINES_PRIZE) +#define is_soko_prize(o) \ + (((o)->otyp == iflags.soko_prize_type1 \ + && (o)->record_achieve_special == SOKO_PRIZE1) \ + || ((o)->otyp == iflags.soko_prize_type2 \ + && (o)->record_achieve_special == SOKO_PRIZE2)) /* Flags for get_obj_location(). */ #define CONTAINED_TOO 0x1 diff --git a/src/bones.c b/src/bones.c index 4d3f67138..66efd480b 100644 --- a/src/bones.c +++ b/src/bones.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 bones.c $NHDT-Date: 1503309019 2017/08/21 09:50:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.70 $ */ +/* NetHack 3.6 bones.c $NHDT-Date: 1508827591 2017/10/24 06:46:31 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.71 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985,1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -124,7 +124,7 @@ boolean restore; otmp->spe = 1; #endif } else if (otmp->otyp == EGG) { - otmp->spe = 0; + otmp->spe = 0; /* not "laid by you" in next game */ } else if (otmp->otyp == TIN) { /* make tins of unique monster's meat be empty */ if (otmp->corpsenm >= LOW_PM @@ -133,24 +133,30 @@ boolean restore; } else if (otmp->otyp == CORPSE || otmp->otyp == STATUE) { int mnum = otmp->corpsenm; - /* Discard incarnation details of unique - monsters (by passing null instead of otmp - for object), shopkeepers (by passing false - for revival flag), temple priests, and - vault guards in order to prevent corpse - revival or statue reanimation. */ + /* Discard incarnation details of unique monsters + (by passing null instead of otmp for object), + shopkeepers (by passing false for revival flag), + temple priests, and vault guards in order to + prevent corpse revival or statue reanimation. */ if (has_omonst(otmp) && cant_revive(&mnum, FALSE, (struct obj *) 0)) { free_omonst(otmp); - /* mnum is now either human_zombie or - doppelganger; for corpses of uniques, - we need to force the transformation - now rather than wait until a revival - attempt, otherwise eating this corpse + /* mnum is now either human_zombie or doppelganger; + for corpses of uniques, we need to force the + transformation now rather than wait until a + revival attempt, otherwise eating this corpse would behave as if it remains unique */ if (mnum == PM_DOPPELGANGER && otmp->otyp == CORPSE) set_corpsenm(otmp, mnum); } + } else if ((otmp->otyp == iflags.mines_prize_type + && !Is_mineend_level(&u.uz)) + || ((otmp->otyp == iflags.soko_prize_type1 + || otmp->otyp == iflags.soko_prize_type2) + && !Is_sokoend_level(&u.uz))) { + /* "special prize" in this game becomes ordinary object + if loaded into another game */ + otmp->record_achieve_special = NON_PM; } else if (otmp->otyp == AMULET_OF_YENDOR) { /* no longer the real Amulet */ otmp->otyp = FAKE_AMULET_OF_YENDOR; @@ -183,8 +189,8 @@ sanitize_name(namebuf) char *namebuf; { int c; - boolean strip_8th_bit = - !strcmp(windowprocs.name, "tty") && !iflags.wc_eight_bit_input; + boolean strip_8th_bit = (!strcmp(windowprocs.name, "tty") + && !iflags.wc_eight_bit_input); /* it's tempting to skip this for single-user platforms, since only the current player could have left these bones--except diff --git a/src/invent.c b/src/invent.c index 0c61b3dee..429b5a9d5 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 invent.c $NHDT-Date: 1498078873 2017/06/21 21:01:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.214 $ */ +/* NetHack 3.6 invent.c $NHDT-Date: 1508827592 2017/10/24 06:46:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.220 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -504,12 +504,21 @@ struct obj *obj; } set_artifact_intrinsic(obj, 1, W_ART); } - if (is_mines_prize(obj) && obj->record_achieve_special) { + + /* "special achievements" aren't discoverable during play, they + end up being recorded in XLOGFILE at end of game, nowhere else; + record_achieve_special overloads corpsenm which is ordinarily + initialized to NON_PM (-1) rather than to 0; any special prize + must never be a corpse, egg, tin, figurine, or statue because + their use of obj->corpsenm for monster type would conflict, + nor be a leash (corpsenm overloaded for m_id of leashed + monster) or a novel (corpsenm overloaded for novel index) */ + if (is_mines_prize(obj)) { u.uachieve.mines_luckstone = 1; - obj->record_achieve_special = 0; - } else if (is_soko_prize(obj) && obj->record_achieve_special) { + obj->record_achieve_special = NON_PM; + } else if (is_soko_prize(obj)) { u.uachieve.finish_sokoban = 1; - obj->record_achieve_special = 0; + obj->record_achieve_special = NON_PM; } } diff --git a/src/options.c b/src/options.c index b281ba695..d378d2200 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1507846854 2017/10/12 22:20:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.315 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1508827592 2017/10/24 06:46:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.316 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -724,6 +724,12 @@ initoptions_init() iflags.travelcc.x = iflags.travelcc.y = -1; + /* for "special achievement" tracking (see obj.h, + create_object(sp_lev.c), addinv_core1(invent.c) */ + iflags.mines_prize_type = LUCKSTONE; + iflags.soko_prize_type1 = BAG_OF_HOLDING; + iflags.soko_prize_type2 = AMULET_OF_REFLECTION; + /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */ (void) memcpy((genericptr_t) flags.inv_order, (genericptr_t) def_inv_order, sizeof flags.inv_order); diff --git a/src/sp_lev.c b/src/sp_lev.c index 448907f63..78dec8ebe 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1449269920 2015/12/04 22:58:40 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.77 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1508827593 2017/10/24 06:46:33 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.89 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1881,6 +1881,7 @@ struct mkroom *croom; } } else { struct obj *cobj = container_obj[container_idx - 1]; + remove_object(otmp); if (cobj) { (void) add_to_container(cobj, otmp); @@ -1941,12 +1942,32 @@ struct mkroom *croom; } } - /* Nasty hack here: try to determine if this is the Mines or Sokoban - * "prize" and then set record_achieve_special (maps to corpsenm) - * for the object. That field will later be checked to find out if - * the player obtained the prize. */ - if (is_mines_prize(otmp) || is_soko_prize(otmp)) { - otmp->record_achieve_special = 1; + if (o->id != -1) { + static const char prize_warning[] = "multiple prizes on %s level"; + static int mcount = 0, scount = 0; + + /* if this is a specific item of the right type and it is being + created on the right level, flag it as the designated item + used to detect a special achievement (to whit, reaching and + exploring the target level, although the exploration part + might be short-circuited if a monster brings object to hero) */ + if (Is_mineend_level(&u.uz)) { + if (otmp->otyp == iflags.mines_prize_type) { + otmp->record_achieve_special = MINES_PRIZE; + if (++mcount > 1) + impossible(prize_warning, "mines end"); + } + } else if (Is_sokoend_level(&u.uz)) { + if (otmp->otyp == iflags.soko_prize_type1) { + otmp->record_achieve_special = SOKO_PRIZE1; + if (++scount > 1) + impossible(prize_warning, "sokoban end"); + } else if (otmp->otyp == iflags.soko_prize_type2) { + otmp->record_achieve_special = SOKO_PRIZE2; + if (++scount > 1) + impossible(prize_warning, "sokoban end"); + } + } } stackobj(otmp); From 5a50bb64451196433380cd70a884f7791ce2a288 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 24 Oct 2017 14:17:25 -0700 Subject: [PATCH 02/25] more achievement tracking The code that checked for and complained about having more than one 'prize' object on the mines' end or sokoban end level uses static counters and would complain if you used #wizmakemap to recreate the level. (Or if a game got far enough that either of those levels was created and then started over--I'm not sure what state support for that has reached.) So re-init those counters each time any special level gets created; that's sufficient for what they track. I also changed several variables in sp_lev.c from global to file scope since they aren't used anywhere else. --- src/sp_lev.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/sp_lev.c b/src/sp_lev.c index 78dec8ebe..badbab319 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1508827593 2017/10/24 06:46:33 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.89 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1508879840 2017/10/24 21:17:20 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.90 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -198,13 +198,14 @@ static NEARDATA char xsize, ysize; char *lev_message = 0; lev_region *lregions = 0; int num_lregions = 0; -boolean splev_init_present = FALSE; -boolean icedpools = FALSE; -struct obj *container_obj[MAX_CONTAINMENT]; -int container_idx = 0; +static boolean splev_init_present = FALSE; +static boolean icedpools = FALSE; +static int mines_prize_count = 0, soko_prize_count = 0; /* achievements */ -struct monst *invent_carrying_monster = NULL; +static struct obj *container_obj[MAX_CONTAINMENT]; +static int container_idx = 0; +static struct monst *invent_carrying_monster = NULL; #define SPLEV_STACK_RESERVE 128 @@ -1944,7 +1945,6 @@ struct mkroom *croom; if (o->id != -1) { static const char prize_warning[] = "multiple prizes on %s level"; - static int mcount = 0, scount = 0; /* if this is a specific item of the right type and it is being created on the right level, flag it as the designated item @@ -1954,17 +1954,17 @@ struct mkroom *croom; if (Is_mineend_level(&u.uz)) { if (otmp->otyp == iflags.mines_prize_type) { otmp->record_achieve_special = MINES_PRIZE; - if (++mcount > 1) + if (++mines_prize_count > 1) impossible(prize_warning, "mines end"); } } else if (Is_sokoend_level(&u.uz)) { if (otmp->otyp == iflags.soko_prize_type1) { otmp->record_achieve_special = SOKO_PRIZE1; - if (++scount > 1) + if (++soko_prize_count > 1) impossible(prize_warning, "sokoban end"); } else if (otmp->otyp == iflags.soko_prize_type2) { otmp->record_achieve_special = SOKO_PRIZE2; - if (++scount > 1) + if (++soko_prize_count > 1) impossible(prize_warning, "sokoban end"); } } @@ -5201,7 +5201,7 @@ sp_lev *lvl; long room_stack = 0; unsigned long max_execution = SPCODER_MAX_RUNTIME; struct sp_coder *coder = - (struct sp_coder *) alloc(sizeof(struct sp_coder)); + (struct sp_coder *) alloc(sizeof (struct sp_coder)); coder->frame = frame_new(0); coder->stack = NULL; @@ -5215,9 +5215,14 @@ sp_lev *lvl; splev_init_present = FALSE; icedpools = FALSE; + /* achievement tracking; static init would suffice except we need to + reset if #wizmakemap is used to recreate mines' end or sokoban end; + once either level is created, these values can be forgotten */ + mines_prize_count = soko_prize_count = 0; if (wizard) { char *met = nh_getenv("SPCODER_MAX_RUNTIME"); + if (met && met[0] == '1') max_execution = (1 << 30) - 1; } From defde3d33202a2c935628b43f6abb7a10dae7014 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 24 Oct 2017 14:29:38 -0700 Subject: [PATCH 03/25] description of #therecmdmenu The extended command added to test handling for adjacent mouse clicks had a description which was too long. In the list from '#?', white space for column alignment got squeezed out to make it fit (at least for tty, where it ended up looking awful). The new description isn't a complete sentence any more, but I don't think anyone will care. --- src/cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index 27e05f4d8..0272ec0b9 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 cmd.c $NHDT-Date: 1494985492 2017/05/17 01:44:52 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.258 $ */ +/* NetHack 3.6 cmd.c $NHDT-Date: 1508880573 2017/10/24 21:29:33 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.275 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -3005,7 +3005,7 @@ struct ext_func_tab extcmdlist[] = { { '\0', "terrain", "show map without obstructions", doterrain, IFBURIED | AUTOCOMPLETE }, { '\0', "therecmdmenu", - "show menu of commands you can do from here to adjacent spot", + "menu of commands you can do from here to adjacent spot", dotherecmdmenu }, { 't', "throw", "throw something", dothrow }, { '\0', "timeout", "look at timeout queue and hero's timed intrinsics", From b02dae91a14f33697a2aabbf1f86bd4f766b901b Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 25 Oct 2017 09:59:17 +0300 Subject: [PATCH 04/25] Fix status hilites parsing from config file --- src/botl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botl.c b/src/botl.c index ca6c494cf..df45de839 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1794,7 +1794,7 @@ boolean from_configfile; numeric = TRUE; tmp = tmpbuf; if (strlen(tmp) > 0) { - dt = blstats[0][fld].anytype; + dt = initblstats[fld].anytype; if (percent) dt = ANY_INT; (void) s_to_anything(&hilite.value, tmp, dt); From a588541a27d417f5e3cf57fa8ad2dba49a8a1cd1 Mon Sep 17 00:00:00 2001 From: Bart House Date: Mon, 2 Oct 2017 21:11:30 -0700 Subject: [PATCH 05/25] Win32GUI: Gather raw_print text and display it all in single dialog Defined strbuf_t and related routines to support dynamically sized strings. Modified strip_newline() to strip the last newline in a string instead of the first. Simplified splash window code using new strbuf_t. Prior to exiting game, re-enable getreturn and call wait_synch() in case there is buffered raw prints that must be displayed to user. --- include/extern.h | 5 +++ include/hack.h | 6 ++++ src/hacklib.c | 72 +++++++++++++++++++++++++++++++++++++++++- sys/share/pcmain.c | 9 ++++++ sys/share/pcsys.c | 2 ++ win/win32/mhmsgwnd.c | 53 +++++++++++++++++++++++++------ win/win32/mhsplash.c | 75 +++++++++----------------------------------- win/win32/mswproc.c | 57 +++++++++++++++++++++++++++------ win/win32/winMS.h | 1 + 9 files changed, 200 insertions(+), 80 deletions(-) diff --git a/include/extern.h b/include/extern.h index bc1c183ab..de7c35999 100644 --- a/include/extern.h +++ b/include/extern.h @@ -924,6 +924,11 @@ E int NDECL(phase_of_the_moon); E boolean NDECL(friday_13th); E int NDECL(night); E int NDECL(midnight); +E void FDECL(strbuf_init, (strbuf_t *)); +E void FDECL(strbuf_append, (strbuf_t *, const char *)); +E void FDECL(strbuf_reserve, (strbuf_t *, int)); +E void FDECL(strbuf_empty, (strbuf_t *)); +E void FDECL(strbuf_nl_to_crlf, (strbuf_t *)); /* ### invent.c ### */ diff --git a/include/hack.h b/include/hack.h index b3a05ca67..9bc2fc403 100644 --- a/include/hack.h +++ b/include/hack.h @@ -151,6 +151,12 @@ enum game_end_types { ASCENDED }; +typedef struct strbuf { + int len; + char * str; + char buf[256]; +} strbuf_t; + #include "align.h" #include "dungeon.h" #include "monsym.h" diff --git a/src/hacklib.c b/src/hacklib.c index 5d18fcbf7..69cfc3f5b 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -61,6 +61,11 @@ boolean friday_13th (void) int night (void) int midnight (void) + void strbuf_init (strbuf *, const char *) + void strbuf_append (strbuf *, const char *) + void strbuf_reserve (strbuf *, int) + void strbuf_empty (strbuf *) + void strbuf_nl_to_crlf (strbuf_t *) =*/ #ifdef LINT #define Static /* pacify lint */ @@ -183,7 +188,7 @@ char * strip_newline(str) char *str; { - char *p = index(str, '\n'); + char *p = rindex(str, '\n'); if (p) { if (p > str && *(p - 1) == '\r') @@ -1101,4 +1106,69 @@ midnight() return (getlt()->tm_hour == 0); } +/* strbuf_init() initializes strbuf state for use */ +void strbuf_init(strbuf_t * strbuf) +{ + strbuf->str = NULL; + strbuf->len = 0; +} + +/* strbuf_append() appends given str to strbuf->str */ +void strbuf_append(strbuf_t * strbuf, const char * str) +{ + if (strbuf->str == NULL) + strbuf_reserve(strbuf, strlen(str) + 1); + else + strbuf_reserve(strbuf, strlen(strbuf->str) + strlen(str) + 1); + + strcat(strbuf->str, str); +} + +/* strbuf_reserve() ensure strbuf->str has storage for len characters */ +void strbuf_reserve(strbuf_t * strbuf, int len) +{ + if (strbuf->str == NULL) { + strbuf->str = strbuf->buf; + strbuf->str[0] = '\0'; + strbuf->len = sizeof(strbuf->buf); + } + + if (len > strbuf->len) { + char * oldbuf = strbuf->str; + strbuf->len = len + sizeof(strbuf->buf); + strbuf->str = (char *) alloc(strbuf->len); + strcpy(strbuf->str, oldbuf); + if (oldbuf != strbuf->buf) free(oldbuf); + } +} + +/* strbuf_empty() frees allocated memory and set strbuf to initial state */ +void strbuf_empty(strbuf_t * strbuf) +{ + if (strbuf->str != strbuf->buf) + free(strbuf->str); + strbuf_init(strbuf); +} + +/* strbuf_nl_to_crlf() converts all occurences of \n to \r\n */ +void strbuf_nl_to_crlf(strbuf_t * strbuf) +{ + if (strbuf->str) { + int len = strlen(strbuf->str); + int count = 0; + char * cp = strbuf->str; + while (*cp) if (*cp++ == '\n') count++; + if (count) { + strbuf_reserve(strbuf, len + count + 1); + cp = strbuf->str + len + count; + while (count) { + if ((*cp-- = cp[-count]) == '\n') { + *cp-- = '\r'; + count--; + } + } + } + } +} + /*hacklib.c*/ diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index d62d234cc..a18157639 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -139,6 +139,15 @@ char *argv[]; /* use STDERR by default _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ +/* Heap Debugging + _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) + | _CRTDBG_ALLOC_MEM_DF + | _CRTDBG_CHECK_ALWAYS_DF + | _CRTDBG_CHECK_CRT_DF + | _CRTDBG_DELAY_FREE_MEM_DF + | _CRTDBG_LEAK_CHECK_DF); + _CrtSetBreakAlloc(1423); +*/ # endif #endif diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c index 085956c76..5746b778c 100644 --- a/sys/share/pcsys.c +++ b/sys/share/pcsys.c @@ -546,6 +546,8 @@ msexit() getreturn("to end"); synch_cursor(); #endif + getreturn_enabled = TRUE; + wait_synch(); return; } #endif /* MICRO || WIN32 || OS2 */ diff --git a/win/win32/mhmsgwnd.c b/win/win32/mhmsgwnd.c index 40b4791ef..22b0fdd9f 100644 --- a/win/win32/mhmsgwnd.c +++ b/win/win32/mhmsgwnd.c @@ -245,6 +245,25 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) data = (PNHMessageWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA); switch (wParam) { case MSNH_MSG_PUTSTR: { + /* Add the passed in message to the existing text. Support the + * adding of text that ends in newline. A newline in text + * will force any subsequent text that is added to be added on + * a new output line. + * + * TODO: Text can be added with newlines occurring within the text not + * just at the end. As currently implemented, this can cause + * the text to be rendered such that the text following the + * newline is rendered on a new line. This can cause a poor + * user experience when the user has set only a single text line + * for the message window. In this case, the user will not see + * any line other then the last line of text and the --MORE-- + * message thus missing any text that appears before the last + * embedded newline. This does not meet the requirements of the + * message window. + * This code should be changed to do the right thing and split + * the text so that only lines that end in newlines are added to + * the stored window text. + */ PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam; SCROLLINFO si; char *p; @@ -271,10 +290,17 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) /* check for "--more--" */ if (!data->nevermore && more_prompt_check(hWnd)) { int okkey = 0; - int chop; + char tmptext[MAXWINDOWTEXT + 1]; + // @@@ Ok respnses - /* append more prompt and inticate the update */ + /* save original text */ + strcpy(tmptext, data->window_text[MSG_LINES - 1].text); + + /* text could end in newline so strip it */ + strip_newline(data->window_text[MSG_LINES - 1].text); + + /* append more prompt and indicate the update */ strncat( data->window_text[MSG_LINES - 1].text, MORE, MAXWINDOWTEXT @@ -301,10 +327,9 @@ onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) } } - /* erase the "--more--" prompt */ - chop = strlen(data->window_text[MSG_LINES - 1].text) - - strlen(MORE); - data->window_text[MSG_LINES - 1].text[chop] = '\0'; + /* restore original text */ + strcpy(data->window_text[MSG_LINES - 1].text, tmptext); + data->lines_not_seen = 0; } @@ -601,6 +626,7 @@ onPaint(HWND hWnd) - (client_rt.bottom - ps.rcPaint.bottom) / data->yChar); y = min(ps.rcPaint.bottom, client_rt.bottom); for (i = LastLine; i >= FirstLine; i--) { + char tmptext[MAXWINDOWTEXT + 1]; x = data->xChar * (2 - data->xPos); draw_rt.left = x; @@ -612,8 +638,10 @@ onPaint(HWND hWnd) hdc, mswin_get_font(NHW_MESSAGE, data->window_text[i].attr, hdc, FALSE)); - /* convert to UNICODE */ - NH_A2W(data->window_text[i].text, wbuf, sizeof(wbuf)); + /* convert to UNICODE stripping newline */ + strcpy(tmptext, data->window_text[i].text); + strip_newline(tmptext); + NH_A2W(tmptext, wbuf, sizeof(wbuf)); wlen = _tcslen(wbuf); setMsgTextColor(hdc, i < (MSG_LINES - data->lines_last_turn)); #ifdef MSG_WRAP_TEXT @@ -765,6 +793,10 @@ can_append_text(HWND hWnd, int attr, const char *text) if (data->window_text[MSG_LINES - 1].attr != attr) return FALSE; + /* cannot append if current line ends in newline */ + if (str_end_is(data->window_text[MSG_LINES - 1].text, "\n")) + return FALSE; + /* check if the maximum string langth will be exceeded */ if (strlen(data->window_text[MSG_LINES - 1].text) + 2 + /* space characters */ @@ -772,10 +804,11 @@ can_append_text(HWND hWnd, int attr, const char *text) >= MAXWINDOWTEXT) return FALSE; - /* check if the text is goinf to fin into a single line */ + /* check if the text is going to fit into a single line */ strcpy(tmptext, data->window_text[MSG_LINES - 1].text); strcat(tmptext, " "); strcat(tmptext, text); + strip_newline(tmptext); strcat(tmptext, MORE); hdc = GetDC(hWnd); @@ -836,6 +869,8 @@ more_prompt_check(HWND hWnd) hdc, FALSE)); strcpy(tmptext, data->window_text[MSG_LINES - i - 1].text); + strip_newline(tmptext); + if (i == 0) strcat(tmptext, MORE); diff --git a/win/win32/mhsplash.c b/win/win32/mhsplash.c index 680da598e..d1bceaa07 100644 --- a/win/win32/mhsplash.c +++ b/win/win32/mhsplash.c @@ -36,13 +36,9 @@ mswin_display_splash_window(BOOL show_ver) RECT controlrt; HWND hWnd; int buttop; - int strsize = 0; - int bufsize = BUFSZ; - char *buf = malloc(bufsize); + strbuf_t strbuf; - if (buf == NULL) - panic("out of memory"); - buf[0] = '\0'; + strbuf_init(&strbuf); hWnd = CreateDialog(GetNHApp()->hApp, MAKEINTRESOURCE(IDD_SPLASH), GetNHApp()->hMainWnd, NHSplashWndProc); @@ -87,9 +83,9 @@ mswin_display_splash_window(BOOL show_ver) clientrt.right - 2 * SPLASH_OFFSET_X, controlrt.bottom, TRUE); /* Fill the text control */ - Sprintf(buf, "%s\r\n%s\r\n%s\r\n%s\r\n\r\n", COPYRIGHT_BANNER_A, + strbuf_reserve(&strbuf, BUFSIZ); + Sprintf(strbuf.str, "%s\n%s\n%s\n%s\n\n", COPYRIGHT_BANNER_A, COPYRIGHT_BANNER_B, COPYRIGHT_BANNER_C, COPYRIGHT_BANNER_D); - strsize = strlen(buf); if (show_ver) { /* Show complete version information */ @@ -98,43 +94,16 @@ mswin_display_splash_window(BOOL show_ver) int verstrsize = 0; getversionstring(verbuf); - verstrsize = strlen(verbuf); - if (verstrsize + strlen("\r\n\r\n") + 1 < BUFSZ - 1) - strcat(verbuf, "\r\n\r\n"); - verstrsize = strlen(verbuf); - - if (strsize + verstrsize + 1 > bufsize) { - bufsize += BUFSZ; - buf = realloc(buf, bufsize); - if (buf == NULL) - panic("out of memory"); - } - strcat(buf, verbuf); - strsize = strlen(buf); + strbuf_append(&strbuf, verbuf); + strbuf_append(&strbuf, "\n\n"); /* Add compile options */ f = dlb_fopen(OPTIONS_USED, RDTMODE); if (f) { char line[LLEN + 1]; - while (dlb_fgets(line, LLEN, f)) { - size_t len; - len = strlen(line); - if (len > 0 && line[len - 1] == '\n') { - line[len - 1] = '\r'; - line[len] = '\n'; - line[len + 1] = '\0'; - len++; - } - if (strsize + (int) len + 1 > bufsize) { - bufsize += BUFSZ; - buf = realloc(buf, bufsize); - if (buf == NULL) - panic("out of memory"); - } - strcat(buf, line); - strsize += len; - } + while (dlb_fgets(line, LLEN, f)) + strbuf_append(&strbuf, line); (void) dlb_fclose(f); } } else { @@ -147,32 +116,18 @@ mswin_display_splash_window(BOOL show_ver) if (nf != NULL) { char line[LLEN + 1]; - while (fgets(line, LLEN, nf)) { - size_t len; - len = strlen(line); - if (len > 0 && line[len - 1] == '\n') { - line[len - 1] = '\r'; - line[len] = '\n'; - line[len + 1] = '\0'; - len++; - } - if (strsize + (int) len + 1 > bufsize) { - bufsize += BUFSZ; - buf = realloc(buf, bufsize); - if (buf == NULL) - panic("out of memory"); - } - strcat(buf, line); - strsize += len; - } + while (fgets(line, LLEN, nf)) + strbuf_append(&strbuf, line); (void) fclose(nf); } else { - strcat(buf, "No news."); + strbuf_append(&strbuf, "No news."); } } } - SetWindowText(GetDlgItem(hWnd, IDC_EXTRAINFO), buf); - free(buf); + + strbuf_nl_to_crlf(&strbuf); + SetWindowText(GetDlgItem(hWnd, IDC_EXTRAINFO), strbuf.str); + strbuf_empty(&strbuf); ShowWindow(hWnd, SW_SHOW); while (IsWindow(hWnd) && GetMessage(&msg, NULL, 0, 0) != 0) { diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 236396b83..c20fffbff 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -75,6 +75,8 @@ COLORREF status_fg_color = RGB(0xFF, 0xFF, 0xFF); COLORREF message_bg_color = RGB(0, 0, 0); COLORREF message_fg_color = RGB(0xFF, 0xFF, 0xFF); +strbuf_t raw_print_strbuf = { 0 }; + /* Interface definition, for windows.c */ struct window_procs mswin_procs = { "MSWIN", @@ -717,8 +719,7 @@ mswin_exit_nhwindows(const char *str) /* Write Window settings to the registry */ mswin_write_reg(); - while (max_brush) - DeleteObject(brush_table[--max_brush]); + } /* Prepare the window to be suspended. */ @@ -1238,6 +1239,7 @@ void mswin_wait_synch() { logDebug("mswin_wait_synch()\n"); + mswin_raw_print_flush(); } /* @@ -1293,6 +1295,40 @@ mswin_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph) } } +/* + * mswin_raw_print_accumulate() accumulate the given text into + * raw_print_strbuf. + */ +void +mswin_raw_print_accumulate(const char * str, boolean bold) +{ + bold; // ignored for now + + if (raw_print_strbuf.str != NULL) strbuf_append(&raw_print_strbuf, "\n"); + strbuf_append(&raw_print_strbuf, str); +} + +/* + * mswin_raw_print_flush() - display any text found in raw_print_strbuf in a + * dialog box and clear raw_print_strbuf. + */ +void +mswin_raw_print_flush() +{ + if (raw_print_strbuf.str != NULL) { + int wlen = strlen(raw_print_strbuf.str) + 1; + TCHAR * wbuf = (TCHAR *) alloc(wlen * sizeof(TCHAR)); + if (wbuf != NULL) { + NHMessageBox(GetNHApp()->hMainWnd, + NH_A2W(raw_print_strbuf.str, wbuf, wlen), + MB_ICONINFORMATION | MB_OK); + free(wbuf); + } + strbuf_empty(&raw_print_strbuf); + } +} + + /* raw_print(str) -- Print directly to a screen, or otherwise guarantee that the user sees str. raw_print() appends a newline to str. @@ -1305,14 +1341,12 @@ raw_print(str) -- Print directly to a screen, or otherwise guarantee that void mswin_raw_print(const char *str) { - TCHAR wbuf[255]; logDebug("mswin_raw_print(%s)\n", str); + if (str && *str) { extern int redirect_stdout; if (!redirect_stdout) - NHMessageBox(GetNHApp()->hMainWnd, - NH_A2W(str, wbuf, sizeof(wbuf)), - MB_ICONINFORMATION | MB_OK); + mswin_raw_print_accumulate(str, FALSE); else fprintf(stdout, "%s", str); } @@ -1326,11 +1360,14 @@ possible). void mswin_raw_print_bold(const char *str) { - TCHAR wbuf[255]; logDebug("mswin_raw_print_bold(%s)\n", str); - if (str && *str) - NHMessageBox(GetNHApp()->hMainWnd, NH_A2W(str, wbuf, sizeof(wbuf)), - MB_ICONINFORMATION | MB_OK); + if (str && *str) { + extern int redirect_stdout; + if (!redirect_stdout) + mswin_raw_print_accumulate(str, TRUE); + else + fprintf(stdout, "%s", str); + } } /* diff --git a/win/win32/winMS.h b/win/win32/winMS.h index b799da1e7..0347674ff 100644 --- a/win/win32/winMS.h +++ b/win/win32/winMS.h @@ -164,6 +164,7 @@ void mswin_cliparound(int x, int y); void mswin_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph); void mswin_raw_print(const char *str); void mswin_raw_print_bold(const char *str); +void mswin_raw_print_flush(); int mswin_nhgetch(void); int mswin_nh_poskey(int *x, int *y, int *mod); void mswin_nhbell(void); From 3d2e59ecec49db8036296667ca29437898304308 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sat, 14 Oct 2017 18:19:24 -0700 Subject: [PATCH 06/25] Win32GUI: Changes to player selection dialog. Significant changes to player selection dialog that attempt to match recently made changes to the Qt and X11 window ports. --- win/win32/mhdlg.c | 874 +++++++++++++++++++++---------------------- win/win32/mhdlg.h | 2 +- win/win32/mswproc.c | 4 +- win/win32/resource.h | 290 +++++++------- win/win32/winMS.h | 1 + win/win32/winhack.rc | 168 +++++---- 6 files changed, 662 insertions(+), 677 deletions(-) diff --git a/win/win32/mhdlg.c b/win/win32/mhdlg.c index efd35dff8..a91eecf20 100644 --- a/win/win32/mhdlg.c +++ b/win/win32/mhdlg.c @@ -10,6 +10,8 @@ #include "resource.h" #include "mhdlg.h" +#include + /*---------------------------------------------------------------*/ /* data for getlin dialog */ struct getlin_data { @@ -256,34 +258,51 @@ ExtCmdDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) } /*---------------------------------------------------------------*/ -/* player selector dialog data */ -struct plsel_data { - int *selection; -}; +/* player selector dialog */ +typedef struct plsel_data { + int config_race; + int config_role; + int config_gender; + int config_alignment; + HWND control_role; + HWND control_race; + HWND control_genders[ROLE_GENDERS]; + HWND control_aligns[ROLE_ALIGNS]; + int role_count; + int race_count; + HWND focus; +} plsel_data_t; INT_PTR CALLBACK PlayerSelectorDlgProc(HWND, UINT, WPARAM, LPARAM); static void plselInitDialog(HWND hWnd); -static void plselAdjustLists(HWND hWnd, int changed_opt); -static int plselFinalSelection(HWND hWnd, int *selection); +static void plselAdjustSelections(HWND hWnd); +static boolean plselRandomize(plsel_data_t * data); +static BOOL plselDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam); -int -mswin_player_selection_window(int *selection) +boolean +mswin_player_selection_window() { INT_PTR ret; - struct plsel_data data; + plsel_data_t data; + boolean ok = TRUE; - /* init dialog data */ - ZeroMemory(&data, sizeof(data)); - data.selection = selection; + /* save away configuration settings */ + data.config_role = flags.initrole; + data.config_race = flags.initrace; + data.config_gender = flags.initgend; + data.config_alignment = flags.initalign; - /* create modal dialog */ - ret = DialogBoxParam( - GetNHApp()->hApp, MAKEINTRESOURCE(IDD_PLAYER_SELECTOR), - GetNHApp()->hMainWnd, PlayerSelectorDlgProc, (LPARAM) &data); - if (ret == -1) - panic("Cannot create getlin window"); + if (!plselRandomize(&data)) { + /* create modal dialog */ + ret = DialogBoxParam( + GetNHApp()->hApp, MAKEINTRESOURCE(IDD_PLAYER_SELECTOR), + GetNHApp()->hMainWnd, PlayerSelectorDlgProc, (LPARAM) &data); + if (ret == -1) + panic("Cannot create getlin window"); + ok = (ret == IDOK); + } - return (int) ret; + return ok; } INT_PTR CALLBACK @@ -315,11 +334,94 @@ PlayerSelectorDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) /* init dialog */ plselInitDialog(hWnd); - /* set focus on the role checkbox (random) field */ - SetFocus(GetDlgItem(hWnd, IDC_PLSEL_ROLE_RANDOM)); + /* tell windows to set the focus */ + return TRUE; + break; - /* tell windows we set the focus */ - return FALSE; + case WM_DRAWITEM: + if (wParam == IDC_PLSEL_ROLE_LIST || wParam == IDC_PLSEL_RACE_LIST) + return plselDrawItem(hWnd, wParam, lParam); + break; + + case WM_NOTIFY: + { + LPNMHDR nmhdr = (LPNMHDR)lParam; + HWND control = nmhdr->hwndFrom; + + data = (struct plsel_data *) GetWindowLongPtr(hWnd, GWLP_USERDATA); + + switch (nmhdr->code) { + case LVN_KEYDOWN: + { + LPNMLVKEYDOWN lpnmkeydown = (LPNMLVKEYDOWN) lParam; + + if (lpnmkeydown->wVKey == ' ') { + if (control == data->control_role) { + int i = ListView_GetNextItem(data->control_role, -1, LVNI_FOCUSED); + assert(i == -1 || ListView_GetNextItem(data->control_role, i, LVNI_FOCUSED) == -1); + flags.initrole = i; + plselAdjustSelections(hWnd); + } else if (control == data->control_race) { + int i = ListView_GetNextItem(data->control_race, -1, LVNI_FOCUSED); + assert(i == -1 || ListView_GetNextItem(data->control_race, i, LVNI_FOCUSED) == -1); + if (ok_race(flags.initrole, i, ROLE_RANDOM, ROLE_RANDOM)) { + flags.initrace = i; + plselAdjustSelections(hWnd); + } + } + } + } + break; + case NM_CLICK: + { + LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW)lParam; + int i = lpnmitem->iItem; + if (i == -1) + return FALSE; + if (control == data->control_role) { + flags.initrole = i; + plselAdjustSelections(hWnd); + } else if(control == data->control_race) { + if (ok_race(flags.initrole, i, ROLE_RANDOM, ROLE_RANDOM)) { + flags.initrace = i; + plselAdjustSelections(hWnd); + } + } + } + break; + case NM_KILLFOCUS: + { + char buf[64]; + sprintf(buf, "KILLFOCUS %lx\n", (unsigned long) control); + OutputDebugStringA(buf); + + if (data->focus == data->control_race) { + data->focus = NULL; + ListView_RedrawItems(data->control_race, 0, data->race_count - 1); + } else if (data->focus == data->control_role) { + data->focus = NULL; + ListView_RedrawItems(data->control_role, 0, data->role_count - 1); + } + } + break; + case NM_SETFOCUS: + { + char buf[64]; + sprintf(buf, "SETFOCUS %lx\n", (unsigned long) control); + OutputDebugStringA(buf); + data->focus = control; + + if (control == data->control_race) { + data->focus = data->control_race; + plselAdjustSelections(hWnd); + } else if (control == data->control_role) { + data->focus = data->control_role; + plselAdjustSelections(hWnd); + } + } + break; + } + } break; case WM_COMMAND: @@ -327,483 +429,361 @@ PlayerSelectorDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) switch (LOWORD(wParam)) { /* OK button was clicked */ case IDOK: - if (plselFinalSelection(hWnd, data->selection)) { - EndDialog(hWnd, wParam); - } else { - NHMessageBox( - hWnd, TEXT("Cannot match this role. Try something else."), - MB_ICONSTOP | MB_OK); - } + EndDialog(hWnd, wParam); return TRUE; /* CANCEL button was clicked */ case IDCANCEL: - *data->selection = -1; EndDialog(hWnd, wParam); return TRUE; - /* following are events from dialog controls: - "random" checkboxes send BN_CLICKED messages; - role/race/... combo-boxes send CBN_SELENDOK - if something was selected; - */ - case IDC_PLSEL_ROLE_RANDOM: + case IDC_PLSEL_RANDOM: + plselRandomize(data); + plselAdjustSelections(hWnd); + return TRUE; + + case IDC_PLSEL_GENDER_MALE: + case IDC_PLSEL_GENDER_FEMALE: if (HIWORD(wParam) == BN_CLICKED) { - /* enable corresponding list window if "random" - checkbox was "unchecked" */ - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), - SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) - == BST_UNCHECKED); + int i = LOWORD(wParam) - IDC_PLSEL_GENDER_MALE; + if (ok_gend(flags.initrole, flags.initrace, i, ROLE_RANDOM)) { + flags.initgend = i; + plselAdjustSelections(hWnd); + } } break; - case IDC_PLSEL_RACE_RANDOM: + + case IDC_PLSEL_ALIGN_LAWFUL: + case IDC_PLSEL_ALIGN_NEUTRAL: + case IDC_PLSEL_ALIGN_CHAOTIC: if (HIWORD(wParam) == BN_CLICKED) { - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), - SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) - == BST_UNCHECKED); + int i = LOWORD(wParam) - IDC_PLSEL_ALIGN_LAWFUL; + if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) { + flags.initalign = i; + plselAdjustSelections(hWnd); + } } break; - case IDC_PLSEL_GENDER_RANDOM: - if (HIWORD(wParam) == BN_CLICKED) { - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), - SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) - == BST_UNCHECKED); - } - break; - - case IDC_PLSEL_ALIGN_RANDOM: - if (HIWORD(wParam) == BN_CLICKED) { - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), - SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) - == BST_UNCHECKED); - } - break; - - case IDC_PLSEL_ROLE_LIST: - if (HIWORD(wParam) == CBN_SELENDOK) { - /* filter out invalid options if - the selection was made */ - plselAdjustLists(hWnd, LOWORD(wParam)); - } - break; - - case IDC_PLSEL_RACE_LIST: - if (HIWORD(wParam) == CBN_SELENDOK) { - plselAdjustLists(hWnd, LOWORD(wParam)); - } - break; - - case IDC_PLSEL_GENDER_LIST: - if (HIWORD(wParam) == CBN_SELENDOK) { - plselAdjustLists(hWnd, LOWORD(wParam)); - } - break; - - case IDC_PLSEL_ALIGN_LIST: - if (HIWORD(wParam) == CBN_SELENDOK) { - plselAdjustLists(hWnd, LOWORD(wParam)); - } - break; } break; } return FALSE; } -void -setComboBoxValue(HWND hWnd, int combo_box, int value) -{ - int index_max = - (int) SendDlgItemMessage(hWnd, combo_box, CB_GETCOUNT, 0, 0); - int index; - int value_to_set = LB_ERR; - for (index = 0; index < index_max; index++) { - if (SendDlgItemMessage(hWnd, combo_box, CB_GETITEMDATA, - (WPARAM) index, 0) == value) { - value_to_set = index; - break; - } - } - SendDlgItemMessage(hWnd, combo_box, CB_SETCURSEL, (WPARAM) value_to_set, - 0); -} - /* initialize player selector dialog */ void plselInitDialog(HWND hWnd) { + struct plsel_data * data = (plsel_data_t *) GetWindowLongPtr(hWnd, GWLP_USERDATA); TCHAR wbuf[BUFSZ]; + LVCOLUMN lvcol; + data->control_role = GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST); + data->control_race = GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST); + + ZeroMemory(&lvcol, sizeof(lvcol)); + lvcol.mask = LVCF_WIDTH; + lvcol.cx = GetSystemMetrics(SM_CXFULLSCREEN); + + /* build role list */ + ListView_InsertColumn(data->control_role, 0, &lvcol); + data->role_count = 0; + for (int i = 0; roles[i].name.m; i++) { + LVITEM lvitem; + ZeroMemory(&lvitem, sizeof(lvitem)); + + lvitem.mask = LVIF_STATE | LVIF_TEXT; + lvitem.iItem = i; + lvitem.iSubItem = 0; + lvitem.state = 0; + lvitem.stateMask = LVIS_FOCUSED; + if (flags.female && roles[i].name.f) + lvitem.pszText = NH_A2W(roles[i].name.f, wbuf, BUFSZ); + else + lvitem.pszText = NH_A2W(roles[i].name.m, wbuf, BUFSZ); + if (ListView_InsertItem(data->control_role, &lvitem) == -1) { + panic("cannot insert menu item"); + } + data->role_count++; + } + + /* build race list */ + ListView_InsertColumn(data->control_race, 0, &lvcol); + data->race_count = 0; + for (int i = 0; races[i].noun; i++) { + LVITEM lvitem; + ZeroMemory(&lvitem, sizeof(lvitem)); + + lvitem.mask = LVIF_STATE | LVIF_TEXT; + lvitem.iItem = i; + lvitem.iSubItem = 0; + lvitem.state = 0; + lvitem.stateMask = LVIS_FOCUSED; + lvitem.pszText = NH_A2W(races[i].noun, wbuf, BUFSZ); + if (ListView_InsertItem(data->control_race, &lvitem) == -1) { + panic("cannot insert menu item"); + } + data->race_count++; + } + + for(int i = 0; i < ROLE_GENDERS; i++) + data->control_genders[i] = GetDlgItem(hWnd, IDC_PLSEL_GENDER_MALE + i); + + for(int i = 0; i < ROLE_ALIGNS; i++) + data->control_aligns[i] = GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LAWFUL + i); + + /* set gender radio button state */ + for (int i = 0; i < ROLE_GENDERS; i++) + Button_Enable(data->control_genders[i], TRUE); + + Button_SetCheck(data->control_genders[0], BST_CHECKED); + + /* set alignment radio button state */ + for (int i = 0; i < ROLE_ALIGNS; i++) + Button_Enable(data->control_aligns[i], TRUE); + + Button_SetCheck(data->control_aligns[0], BST_CHECKED); /* set player name */ SetDlgItemText(hWnd, IDC_PLSEL_NAME, NH_A2W(plname, wbuf, sizeof(wbuf))); - /* check flags for consistency */ - if (flags.initrole >= 0) { - if (flags.initrace >= 0 - && !validrace(flags.initrole, flags.initrace)) { - flags.initrace = ROLE_NONE; - } - - if (flags.initgend >= 0 - && !validgend(flags.initrole, flags.initrace, flags.initgend)) { - flags.initgend = ROLE_NONE; - } - - if (flags.initalign >= 0 - && !validalign(flags.initrole, flags.initrace, flags.initalign)) { - flags.initalign = ROLE_NONE; - } - } + plselRandomize(data); /* populate select boxes */ - plselAdjustLists(hWnd, -1); + plselAdjustSelections(hWnd); - /* intialize roles list */ - if (flags.initrole < 0 - || !ok_role(flags.initrole, ROLE_NONE, ROLE_NONE, ROLE_NONE)) { - CheckDlgButton(hWnd, IDC_PLSEL_ROLE_RANDOM, BST_CHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), FALSE); - } else { - CheckDlgButton(hWnd, IDC_PLSEL_ROLE_RANDOM, BST_UNCHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), TRUE); - setComboBoxValue(hWnd, IDC_PLSEL_ROLE_LIST, flags.initrole); - } + /* set tab order */ + SetWindowPos(GetDlgItem(hWnd, IDCANCEL), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(GetDlgItem(hWnd, IDC_PLSEL_RANDOM), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(GetDlgItem(hWnd, IDOK), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + for(int i = ROLE_GENDERS - 1; i >= 0; i--) + SetWindowPos(data->control_genders[i], NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + for(int i = ROLE_ALIGNS - 1; i >= 0; i--) + SetWindowPos(data->control_aligns[i], NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(data->control_race, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + SetWindowPos(data->control_role, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - /* intialize races list */ - if (flags.initrace < 0 - || !ok_race(flags.initrole, flags.initrace, ROLE_NONE, ROLE_NONE)) { - CheckDlgButton(hWnd, IDC_PLSEL_RACE_RANDOM, BST_CHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), FALSE); - } else { - CheckDlgButton(hWnd, IDC_PLSEL_RACE_RANDOM, BST_UNCHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), TRUE); - setComboBoxValue(hWnd, IDC_PLSEL_RACE_LIST, flags.initrace); - } - - /* intialize genders list */ - if (flags.initgend < 0 - || !ok_gend(flags.initrole, flags.initrace, flags.initgend, - ROLE_NONE)) { - CheckDlgButton(hWnd, IDC_PLSEL_GENDER_RANDOM, BST_CHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), FALSE); - } else { - CheckDlgButton(hWnd, IDC_PLSEL_GENDER_RANDOM, BST_UNCHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), TRUE); - setComboBoxValue(hWnd, IDC_PLSEL_GENDER_LIST, flags.initgend); - } - - /* intialize alignments list */ - if (flags.initalign < 0 - || !ok_align(flags.initrole, flags.initrace, flags.initgend, - flags.initalign)) { - CheckDlgButton(hWnd, IDC_PLSEL_ALIGN_RANDOM, BST_CHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), FALSE); - } else { - CheckDlgButton(hWnd, IDC_PLSEL_ALIGN_RANDOM, BST_UNCHECKED); - EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), TRUE); - setComboBoxValue(hWnd, IDC_PLSEL_ALIGN_LIST, flags.initalign); - } } -/* adjust role/race/alignment/gender list - filter out - invalid combinations - changed_sel points to the list where selection occurred - (-1 if unknown) -*/ void -plselAdjustLists(HWND hWnd, int changed_sel) +plselAdjustSelections(HWND hWnd) { - HWND control_role, control_race, control_gender, control_align; - int initrole, initrace, initgend, initalign; - int i; - LRESULT ind; - int valid_opt; - TCHAR wbuf[255]; + struct plsel_data * data = (plsel_data_t *) GetWindowLongPtr(hWnd, GWLP_USERDATA); - /* get control handles */ - control_role = GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST); - control_race = GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST); - control_gender = GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST); - control_align = GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST); + if (!ok_race(flags.initrole, flags.initrace, ROLE_RANDOM, ROLE_RANDOM)) + flags.initrace = pick_race(flags.initrole, ROLE_RANDOM, ROLE_RANDOM, ROLE_RANDOM); - /* get current selections */ - ind = SendMessage(control_role, CB_GETCURSEL, 0, 0); - initrole = (ind == LB_ERR) - ? flags.initrole - : (int) SendMessage(control_role, CB_GETITEMDATA, ind, 0); + if (!ok_gend(flags.initrole, flags.initrace, flags.initgend, ROLE_RANDOM)) + flags.initgend = pick_gend(flags.initrole, flags.initrace, ROLE_RANDOM , ROLE_RANDOM); - ind = SendMessage(control_race, CB_GETCURSEL, 0, 0); - initrace = (ind == LB_ERR) - ? flags.initrace - : (int) SendMessage(control_race, CB_GETITEMDATA, ind, 0); + if (!ok_align(flags.initrole, flags.initrace, flags.initgend, flags.initalign)) + flags.initalign = pick_align(flags.initrole, flags.initrace, flags.initgend , ROLE_RANDOM); - ind = SendMessage(control_gender, CB_GETCURSEL, 0, 0); - initgend = (ind == LB_ERR) ? flags.initgend - : (int) SendMessage(control_gender, - CB_GETITEMDATA, ind, 0); + ListView_RedrawItems(data->control_role, 0, data->role_count - 1); + ListView_RedrawItems(data->control_race, 0, data->race_count - 1); - ind = SendMessage(control_align, CB_GETCURSEL, 0, 0); - initalign = (ind == LB_ERR) ? flags.initalign - : (int) SendMessage(control_align, - CB_GETITEMDATA, ind, 0); - - /* intialize roles list */ - if (changed_sel == -1) { - valid_opt = 0; - - /* reset content and populate the list */ - SendMessage(control_role, CB_RESETCONTENT, 0, 0); - for (i = 0; roles[i].name.m; i++) { - if (initgend >= 0 && flags.female && roles[i].name.f) - ind = SendMessage( - control_role, CB_ADDSTRING, (WPARAM) 0, - (LPARAM) NH_A2W(roles[i].name.f, wbuf, sizeof(wbuf))); - else - ind = SendMessage( - control_role, CB_ADDSTRING, (WPARAM) 0, - (LPARAM) NH_A2W(roles[i].name.m, wbuf, sizeof(wbuf))); - - SendMessage(control_role, CB_SETITEMDATA, (WPARAM) ind, - (LPARAM) i); - if (i == initrole) { - SendMessage(control_role, CB_SETCURSEL, (WPARAM) ind, - (LPARAM) 0); - valid_opt = 1; - } - } - - /* set selection to the previously selected role - if it is still valid */ - if (!valid_opt) { - initrole = ROLE_NONE; - initrace = ROLE_NONE; - initgend = ROLE_NONE; - initalign = ROLE_NONE; - SendMessage(control_role, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0); - } - - /* trigger change of the races list */ - changed_sel = IDC_PLSEL_ROLE_LIST; + /* set gender radio button state */ + for (int i = 0; i < ROLE_GENDERS; i++) { + BOOL enable = ok_gend(flags.initrole, flags.initrace, i, flags.initalign); + Button_Enable(data->control_genders[i], enable); + LRESULT state = Button_GetCheck(data->control_genders[i]); + if (state == BST_CHECKED && flags.initgend != i) + Button_SetCheck(data->control_genders[i], BST_UNCHECKED); + if (state == BST_UNCHECKED && flags.initgend == i) + Button_SetCheck(data->control_genders[i], BST_CHECKED); } - /* intialize races list */ - if (changed_sel == IDC_PLSEL_ROLE_LIST) { - valid_opt = 0; - - /* reset content and populate the list */ - SendMessage(control_race, CB_RESETCONTENT, 0, 0); - for (i = 0; races[i].noun; i++) - if (ok_race(initrole, i, ROLE_NONE, ROLE_NONE)) { - ind = SendMessage( - control_race, CB_ADDSTRING, (WPARAM) 0, - (LPARAM) NH_A2W(races[i].noun, wbuf, sizeof(wbuf))); - SendMessage(control_race, CB_SETITEMDATA, (WPARAM) ind, - (LPARAM) i); - if (i == initrace) { - SendMessage(control_race, CB_SETCURSEL, (WPARAM) ind, - (LPARAM) 0); - valid_opt = 1; - } - } - - /* set selection to the previously selected race - if it is still valid */ - if (!valid_opt) { - initrace = ROLE_NONE; - initgend = ROLE_NONE; - initalign = ROLE_NONE; - SendMessage(control_race, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0); - } - - /* trigger change of the genders list */ - changed_sel = IDC_PLSEL_RACE_LIST; + /* set alignment radio button state */ + for (int i = 0; i < ROLE_ALIGNS; i++) { + BOOL enable = ok_align(flags.initrole, flags.initrace, flags.initgend, i); + Button_Enable(data->control_aligns[i], enable); + LRESULT state = Button_GetCheck(data->control_aligns[i]); + if (state == BST_CHECKED && flags.initalign != i) + Button_SetCheck(data->control_aligns[i], BST_UNCHECKED); + if (state == BST_UNCHECKED && flags.initalign == i) + Button_SetCheck(data->control_aligns[i], BST_CHECKED); } - /* intialize genders list */ - if (changed_sel == IDC_PLSEL_RACE_LIST) { - valid_opt = 0; - - /* reset content and populate the list */ - SendMessage(control_gender, CB_RESETCONTENT, 0, 0); - for (i = 0; i < ROLE_GENDERS; i++) - if (ok_gend(initrole, initrace, i, ROLE_NONE)) { - ind = SendMessage( - control_gender, CB_ADDSTRING, (WPARAM) 0, - (LPARAM) NH_A2W(genders[i].adj, wbuf, sizeof(wbuf))); - SendMessage(control_gender, CB_SETITEMDATA, (WPARAM) ind, - (LPARAM) i); - if (i == initgend) { - SendMessage(control_gender, CB_SETCURSEL, (WPARAM) ind, - (LPARAM) 0); - valid_opt = 1; - } - } - - /* set selection to the previously selected gender - if it is still valid */ - if (!valid_opt) { - initgend = ROLE_NONE; - initalign = ROLE_NONE; - SendMessage(control_gender, CB_SETCURSEL, (WPARAM) -1, - (LPARAM) 0); - } - - /* trigger change of the alignments list */ - changed_sel = IDC_PLSEL_GENDER_LIST; - } - - /* intialize alignments list */ - if (changed_sel == IDC_PLSEL_GENDER_LIST) { - valid_opt = 0; - - /* reset content and populate the list */ - SendMessage(control_align, CB_RESETCONTENT, 0, 0); - for (i = 0; i < ROLE_ALIGNS; i++) - if (ok_align(initrole, initrace, initgend, i)) { - ind = SendMessage( - control_align, CB_ADDSTRING, (WPARAM) 0, - (LPARAM) NH_A2W(aligns[i].adj, wbuf, sizeof(wbuf))); - SendMessage(control_align, CB_SETITEMDATA, (WPARAM) ind, - (LPARAM) i); - if (i == initalign) { - SendMessage(control_align, CB_SETCURSEL, (WPARAM) ind, - (LPARAM) 0); - valid_opt = 1; - } - } - - /* set selection to the previously selected alignment - if it is still valid */ - if (!valid_opt) { - initalign = ROLE_NONE; - SendMessage(control_align, CB_SETCURSEL, (WPARAM) -1, (LPARAM) 0); - } - } } /* player made up his mind - get final selection here */ int -plselFinalSelection(HWND hWnd, int *selection) +plselFinalSelection(HWND hWnd) { - LRESULT ind; + int role = flags.initrole; + int race = flags.initrace; + int gender = flags.initgend; + int alignment = flags.initalign; - UNREFERENCED_PARAMETER(selection); - - /* get current selections */ - if (SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_RANDOM, BM_GETCHECK, 0, 0) - == BST_CHECKED) { - flags.initrole = ROLE_RANDOM; - } else { - ind = - SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_LIST, CB_GETCURSEL, 0, 0); - flags.initrole = - (ind == LB_ERR) - ? ROLE_RANDOM - : (int) SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_LIST, - CB_GETITEMDATA, ind, 0); - } - - if (SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_RANDOM, BM_GETCHECK, 0, 0) - == BST_CHECKED) { - flags.initrace = ROLE_RANDOM; - } else { - ind = - SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_LIST, CB_GETCURSEL, 0, 0); - flags.initrace = - (ind == LB_ERR) - ? ROLE_RANDOM - : (int) SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_LIST, - CB_GETITEMDATA, ind, 0); - } - - if (SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_RANDOM, BM_GETCHECK, 0, 0) - == BST_CHECKED) { - flags.initgend = ROLE_RANDOM; - } else { - ind = SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_LIST, CB_GETCURSEL, 0, - 0); - flags.initgend = - (ind == LB_ERR) - ? ROLE_RANDOM - : (int) SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_LIST, - CB_GETITEMDATA, ind, 0); - } - - if (SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_RANDOM, BM_GETCHECK, 0, 0) - == BST_CHECKED) { - flags.initalign = ROLE_RANDOM; - } else { - ind = SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_LIST, CB_GETCURSEL, 0, - 0); - flags.initalign = - (ind == LB_ERR) - ? ROLE_RANDOM - : (int) SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_LIST, - CB_GETITEMDATA, ind, 0); - } - - /* check the role */ - if (flags.initrole == ROLE_RANDOM) { - flags.initrole = pick_role(flags.initrace, flags.initgend, - flags.initalign, PICK_RANDOM); - if (flags.initrole < 0) { - NHMessageBox(hWnd, TEXT("Incompatible role!"), - MB_ICONSTOP | MB_OK); - return FALSE; - } - } - - /* Select a race, if necessary */ - /* force compatibility with role */ - if (flags.initrace == ROLE_RANDOM - || !validrace(flags.initrole, flags.initrace)) { - /* pre-selected race not valid */ - if (flags.initrace == ROLE_RANDOM) { - flags.initrace = pick_race(flags.initrole, flags.initgend, - flags.initalign, PICK_RANDOM); - } - - if (flags.initrace < 0) { - NHMessageBox(hWnd, TEXT("Incompatible race!"), - MB_ICONSTOP | MB_OK); - return FALSE; - } - } - - /* Select a gender, if necessary */ - /* force compatibility with role/race, try for compatibility with - * pre-selected alignment */ - if (flags.initgend < 0 - || !validgend(flags.initrole, flags.initrace, flags.initgend)) { - /* pre-selected gender not valid */ - if (flags.initgend == ROLE_RANDOM) { - flags.initgend = pick_gend(flags.initrole, flags.initrace, - flags.initalign, PICK_RANDOM); - } - - if (flags.initgend < 0) { - NHMessageBox(hWnd, TEXT("Incompatible gender!"), - MB_ICONSTOP | MB_OK); - return FALSE; - } - } - - /* Select an alignment, if necessary */ - /* force compatibility with role/race/gender */ - if (flags.initalign < 0 - || !validalign(flags.initrole, flags.initrace, flags.initalign)) { - /* pre-selected alignment not valid */ - if (flags.initalign == ROLE_RANDOM) { - flags.initalign = pick_align(flags.initrole, flags.initrace, - flags.initgend, PICK_RANDOM); - } else { - NHMessageBox(hWnd, TEXT("Incompatible alignment!"), - MB_ICONSTOP | MB_OK); - return FALSE; - } - } + assert(role != ROLE_RANDOM && role != ROLE_NONE); + assert(race != ROLE_RANDOM && race != ROLE_NONE); + assert(gender != ROLE_RANDOM && gender != ROLE_NONE); + assert(alignment != ROLE_RANDOM && alignment != ROLE_NONE); + assert(ok_role(role, race, gender, alignment)); + assert(ok_race(role, race, gender, alignment)); + assert(ok_gend(role, race, gender, alignment)); + assert(ok_align(role, race, gender, alignment)); + + return TRUE; +} + +static boolean plselRandomize(plsel_data_t * data) +{ + boolean fully_specified = TRUE; + + // restore back to configuration settings + flags.initrole = data->config_role; + flags.initrace = data->config_race; + flags.initgend = data->config_gender; + flags.initalign = data->config_alignment; + + if (!flags.randomall) { + if(flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE + || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE) + fully_specified = FALSE; + } + + if (flags.initrole == ROLE_NONE) + flags.initrole = ROLE_RANDOM; + if (flags.initrace == ROLE_NONE) + flags.initrace = ROLE_RANDOM; + if (flags.initgend == ROLE_NONE) + flags.initgend = ROLE_RANDOM; + if (flags.initalign == ROLE_NONE) + flags.initalign = ROLE_RANDOM; + + rigid_role_checks(); + + int role = flags.initrole; + int race = flags.initrace; + int gender = flags.initgend; + int alignment = flags.initalign; + + assert(role != ROLE_RANDOM && role != ROLE_NONE); + assert(race != ROLE_RANDOM && race != ROLE_NONE); + assert(gender != ROLE_RANDOM && gender != ROLE_NONE); + assert(alignment != ROLE_RANDOM && alignment != ROLE_NONE); + + if (!ok_role(role, race, gender, alignment)) { + fully_specified = FALSE; + flags.initrole = ROLE_RANDOM; + } + + if (!ok_race(role, race, gender, alignment)) { + fully_specified = FALSE; + flags.initrace = ROLE_RANDOM; + } + + if (!ok_gend(role, race, gender, alignment)) { + fully_specified = FALSE; + flags.initgend = ROLE_RANDOM; + } + + if(!ok_align(role, race, gender, alignment)) + { + fully_specified = FALSE; + flags.initalign = ROLE_RANDOM; + } + + rigid_role_checks(); + + role = flags.initrole; + race = flags.initrace; + gender = flags.initgend; + alignment = flags.initalign; + + assert(role != ROLE_RANDOM && role != ROLE_NONE); + assert(race != ROLE_RANDOM && race != ROLE_NONE); + assert(gender != ROLE_RANDOM && gender != ROLE_NONE); + assert(alignment != ROLE_RANDOM && alignment != ROLE_NONE); + assert(ok_role(role, race, gender, alignment)); + assert(ok_race(role, race, gender, alignment)); + assert(ok_gend(role, race, gender, alignment)); + assert(ok_align(role, race, gender, alignment)); + + return fully_specified; +} + +/*-----------------------------------------------------------------------------*/ +BOOL +plselDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam; + struct plsel_data * data = (plsel_data_t *) GetWindowLongPtr(hWnd, GWLP_USERDATA); + + /* If there are no list box items, skip this message. */ + if (lpdis->itemID < 0) + return FALSE; + + HWND control = GetDlgItem(hWnd, wParam); + int i = lpdis->itemID; + + { + char buf[64]; + sprintf(buf, "DRAW %lx %d\n", (unsigned long)control, i); + OutputDebugStringA(buf); + } + + const char * string; + + boolean ok = TRUE; + boolean selected; + + if (wParam == IDC_PLSEL_ROLE_LIST) { + if (flags.female && roles[i].name.f) + string = roles[i].name.f; + else + string = roles[i].name.m; + selected = (flags.initrole == i); + } else { + assert(wParam == IDC_PLSEL_RACE_LIST); + ok = ok_race(flags.initrole, i, ROLE_RANDOM, ROLE_RANDOM); + string = races[i].noun; + selected = (flags.initrace == i); + } + + SetBkMode(lpdis->hDC, OPAQUE); + HBRUSH brush; + if (selected) { + brush = CreateSolidBrush(RGB(0, 0, 0)); + SetTextColor(lpdis->hDC, RGB(255, 255, 255)); + SetBkColor(lpdis->hDC, RGB(0, 0, 0)); + } else { + brush = CreateSolidBrush(RGB(255, 255, 255)); + if (!ok) + SetTextColor(lpdis->hDC, RGB(220, 0, 0)); + else + SetTextColor(lpdis->hDC, RGB(0, 0, 0)); + SetBkColor(lpdis->hDC, RGB(255, 255, 255)); + } + + FillRect(lpdis->hDC, &lpdis->rcItem, brush); + RECT rect = lpdis->rcItem; + rect.left += 5; + DrawTextA(lpdis->hDC, string, strlen(string), &rect, + DT_LEFT | DT_SINGLELINE | DT_VCENTER); + + if (data->focus == control) { + if (lpdis->itemState & LVIS_FOCUSED) { + /* draw focus rect */ + RECT client_rt; + + GetClientRect(lpdis->hwndItem, &client_rt); + SetRect(&rect, client_rt.left, lpdis->rcItem.top, + client_rt.left + ListView_GetColumnWidth(lpdis->hwndItem, 0), + lpdis->rcItem.bottom); + DrawFocusRect(lpdis->hDC, &rect); + + { + char buf[64]; + sprintf(buf, "FOCUS %lx %d\n", (unsigned long)control, i); + OutputDebugStringA(buf); + } + } + } + + DeleteObject(brush); return TRUE; } diff --git a/win/win32/mhdlg.h b/win/win32/mhdlg.h index 60f392901..c028fc82b 100644 --- a/win/win32/mhdlg.h +++ b/win/win32/mhdlg.h @@ -12,6 +12,6 @@ int mswin_getlin_window(const char *question, char *result, size_t result_size); int mswin_ext_cmd_window(int *selection); -int mswin_player_selection_window(int *selection); +boolean mswin_player_selection_window(); #endif /* MSWINDlgWindow_h */ diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index c20fffbff..c0cd44f5d 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -260,8 +260,6 @@ mswin_init_nhwindows(int *argc, char **argv) void mswin_player_selection(void) { - int nRole; - logDebug("mswin_player_selection()\n"); if (iflags.wc_player_selection == VIA_DIALOG) { @@ -311,7 +309,7 @@ mswin_player_selection(void) } } else { /* select a role */ - if (mswin_player_selection_window(&nRole) == IDCANCEL) { + if (!mswin_player_selection_window()) { bail(0); } } diff --git a/win/win32/resource.h b/win/win32/resource.h index 98b6ba1b8..899007b9f 100644 --- a/win/win32/resource.h +++ b/win/win32/resource.h @@ -2,155 +2,155 @@ // Microsoft Visual C++ generated include file. // Used by winhack.rc // -#define IDC_MYICON 2 -#define IDD_WINHACK_DIALOG 102 -#define IDD_ABOUTBOX 103 -#define IDS_APP_TITLE 103 -#define IDM_ABOUT 104 -#define IDM_EXIT 105 -#define IDS_HELLO 106 -#define IDI_NETHACKW 107 -#define IDC_NETHACKW 109 -#define IDS_APP_TITLE_SHORT 110 -#define IDR_MAINFRAME 128 -#define IDB_TILES 129 -#define IDD_TEXT 130 -#define IDD_NHTEXT 130 -#define IDD_MENU 132 -#define IDB_MENU_SEL 133 -#define IDB_MENU_UNSEL 134 -#define IDD_COMMANDS 136 -#define IDD_GETLIN 138 -#define IDD_EXTCMD 139 -#define IDD_PLAYER_SELECTOR 141 -#define IDB_PETMARK 143 -#define IDB_MENU_SEL_COUNT 144 -#define IDD_NHRIP 145 -#define IDB_SPLASH 146 -#define IDB_RIP 147 -#define IDD_SPLASH 148 -#define IDB_PILEMARK 149 -#define IDC_TEXT_VIEW 1000 -#define IDC_TEXT_CONTROL 1000 -#define IDC_CMD_MOVE_NW 1001 -#define IDC_CMD_MOVE_N 1002 -#define IDC_MENU_LIST 1003 -#define IDC_CMD_MOVE_NE 1003 -#define IDC_MENU_TEXT 1004 -#define IDC_CMD_MOVE_W 1004 -#define IDC_CMD_MOVE_SELF 1005 -#define IDC_CMD_MOVE_E 1006 -#define IDC_CMD_MOVE_SW 1007 -#define IDC_CMD_MOVE_S 1008 -#define IDC_CMD_MOVE_SE 1009 -#define IDC_CMD_MOVE_UP 1010 -#define IDC_CMD_MOVE_DOWN 1011 -#define IDC_CMD_5 1012 -#define IDC_CMD_A 1013 -#define IDC_CMD_B 1014 -#define IDC_CMD_C 1015 -#define IDC_CMD_D 1016 -#define IDC_CMD_E 1017 -#define IDC_CMD_F 1018 -#define IDC_CMD_G 1019 -#define IDC_CMD_H 1020 -#define IDC_CMD_I 1021 -#define IDC_CMD_J 1022 -#define IDC_CMD_K 1023 -#define IDC_CMD_L 1024 -#define IDC_CMD_M 1025 -#define IDC_CMD_N 1026 -#define IDC_CMD_O 1027 -#define IDC_CMD_P 1028 -#define IDC_CMD_Q 1029 -#define IDC_CMD_R 1030 -#define IDC_CMD_S 1031 -#define IDC_CMD_T 1032 -#define IDC_CMD_U 1033 -#define IDC_CMD_V 1034 -#define IDC_CMD_W 1035 -#define IDC_CMD_X 1036 -#define IDC_CMD_Y 1037 -#define IDC_CMD_Z 1038 -#define IDC_CMD_AA 1039 -#define IDC_CMD_BB 1040 -#define IDC_CMD_CC 1041 -#define IDC_CMD_DD 1042 -#define IDC_CMD_EE 1043 -#define IDC_CMD_FF 1044 -#define IDC_CMD_GG 1045 -#define IDC_CMD_HH 1046 -#define IDC_CMD_II 1047 -#define IDC_CMD_JJ 1048 -#define IDC_CMD_KK 1049 -#define IDC_CMD_LL 1050 -#define IDC_CMD_MM 1051 -#define IDC_CMD_NN 1052 -#define IDC_CMD_OO 1053 -#define IDC_CMD_PP 1054 -#define IDC_CMD_QQ 1055 -#define IDC_CMD_RR 1056 -#define IDC_CMD_SS 1057 -#define IDC_CMD_TT 1058 -#define IDC_CMD_UU 1059 -#define IDC_CMD_VV 1060 -#define IDC_CMD_WW 1061 -#define IDC_CMD_XX 1062 -#define IDC_CMD_YY 1063 -#define IDC_CMD_ZZ 1064 -#define IDC_CMD_FIRST 1100 -#define IDC_CMD_LAST 1300 -#define IDC_GETLIN_EDIT 1309 -#define IDC_EXTCMD_LIST 1310 -#define IDC_PLSEL_NAME 1314 -#define IDC_PLSEL_ROLE_RANDOM 1315 -#define IDC_PLSEL_RACE_RANDOM 1318 -#define IDC_PLSEL_GENDER_RANDOM 1319 -#define IDC_PLSEL_ALIGN_RANDOM 1320 -#define IDC_PLSEL_ROLE_LIST 1323 -#define IDC_PLSEL_RACE_LIST 1324 -#define IDC_PLSEL_ALIGN_LIST 1325 -#define IDC_PLSEL_GENDER_LIST 1326 -#define IDC_ABOUT_VERSION 1327 -#define IDC_ABOUT_COPYRIGHT 1328 -#define IDC_EXTRAINFO 1331 -#define IDM_SAVE 32771 -#define IDM_HELP_LONG 32772 -#define IDM_HELP_COMMANDS 32773 -#define IDM_HELP_HISTORY 32774 -#define IDM_HELP_INFO_CHAR 32775 -#define IDM_HELP_INFO_KEY 32776 -#define IDM_HELP_OPTIONS 32777 -#define IDM_HELP_OPTIONS_LONG 32778 -#define IDM_HELP_EXTCMD 32779 -#define IDM_HELP_LICENSE 32780 -#define IDM_HELP_PORTHELP 32781 -#define IDM_MAP_TILES 32782 -#define IDM_MAP_ASCII4X6 32783 -#define IDM_MAP_ASCII6X8 32784 -#define IDM_MAP_ASCII8X8 32785 -#define IDM_MAP_ASCII16X8 32786 -#define IDM_MAP_ASCII7X12 32787 -#define IDM_MAP_ASCII8X12 32788 -#define IDM_MAP_ASCII16X12 32789 -#define IDM_MAP_ASCII12X16 32790 -#define IDM_MAP_ASCII10X18 32791 -#define IDM_MAP_FIT_TO_SCREEN 32792 -#define IDM_NHMODE 32794 -#define IDM_CLEARSETTINGS 32795 -#define IDM_SETTING_AUTOLAYOUT 32796 -#define IDM_SETTING_LOCKWINDOWS 32797 +#define IDC_MYICON 2 +#define IDD_WINHACK_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDS_HELLO 106 +#define IDI_NETHACKW 107 +#define IDC_NETHACKW 109 +#define IDS_APP_TITLE_SHORT 110 +#define IDR_MAINFRAME 128 +#define IDB_TILES 129 +#define IDD_TEXT 130 +#define IDD_NHTEXT 130 +#define IDD_MENU 132 +#define IDB_MENU_SEL 133 +#define IDB_MENU_UNSEL 134 +#define IDD_COMMANDS 136 +#define IDD_GETLIN 138 +#define IDD_EXTCMD 139 +#define IDD_PLAYER_SELECTOR 141 +#define IDB_PETMARK 143 +#define IDB_MENU_SEL_COUNT 144 +#define IDD_NHRIP 145 +#define IDB_SPLASH 146 +#define IDB_RIP 147 +#define IDD_SPLASH 148 +#define IDB_PILEMARK 149 +#define IDC_TEXT_VIEW 1000 +#define IDC_TEXT_CONTROL 1000 +#define IDC_CMD_MOVE_NW 1001 +#define IDC_CMD_MOVE_N 1002 +#define IDC_MENU_LIST 1003 +#define IDC_CMD_MOVE_NE 1003 +#define IDC_MENU_TEXT 1004 +#define IDC_CMD_MOVE_W 1004 +#define IDC_CMD_MOVE_SELF 1005 +#define IDC_CMD_MOVE_E 1006 +#define IDC_CMD_MOVE_SW 1007 +#define IDC_CMD_MOVE_S 1008 +#define IDC_CMD_MOVE_SE 1009 +#define IDC_CMD_MOVE_UP 1010 +#define IDC_CMD_MOVE_DOWN 1011 +#define IDC_CMD_5 1012 +#define IDC_CMD_A 1013 +#define IDC_CMD_B 1014 +#define IDC_CMD_C 1015 +#define IDC_CMD_D 1016 +#define IDC_CMD_E 1017 +#define IDC_CMD_F 1018 +#define IDC_CMD_G 1019 +#define IDC_CMD_H 1020 +#define IDC_CMD_I 1021 +#define IDC_CMD_J 1022 +#define IDC_CMD_K 1023 +#define IDC_CMD_L 1024 +#define IDC_CMD_M 1025 +#define IDC_CMD_N 1026 +#define IDC_CMD_O 1027 +#define IDC_CMD_P 1028 +#define IDC_CMD_Q 1029 +#define IDC_CMD_R 1030 +#define IDC_CMD_S 1031 +#define IDC_CMD_T 1032 +#define IDC_CMD_U 1033 +#define IDC_CMD_V 1034 +#define IDC_CMD_W 1035 +#define IDC_CMD_X 1036 +#define IDC_CMD_Y 1037 +#define IDC_CMD_Z 1038 +#define IDC_CMD_AA 1039 +#define IDC_CMD_BB 1040 +#define IDC_CMD_CC 1041 +#define IDC_CMD_DD 1042 +#define IDC_CMD_EE 1043 +#define IDC_CMD_FF 1044 +#define IDC_CMD_GG 1045 +#define IDC_CMD_HH 1046 +#define IDC_CMD_II 1047 +#define IDC_CMD_JJ 1048 +#define IDC_CMD_KK 1049 +#define IDC_CMD_LL 1050 +#define IDC_CMD_MM 1051 +#define IDC_CMD_NN 1052 +#define IDC_CMD_OO 1053 +#define IDC_CMD_PP 1054 +#define IDC_CMD_QQ 1055 +#define IDC_CMD_RR 1056 +#define IDC_CMD_SS 1057 +#define IDC_CMD_TT 1058 +#define IDC_CMD_UU 1059 +#define IDC_CMD_VV 1060 +#define IDC_CMD_WW 1061 +#define IDC_CMD_XX 1062 +#define IDC_CMD_YY 1063 +#define IDC_CMD_ZZ 1064 +#define IDC_CMD_FIRST 1100 +#define IDC_CMD_LAST 1300 +#define IDC_GETLIN_EDIT 1309 +#define IDC_EXTCMD_LIST 1310 +#define IDC_PLSEL_NAME 1314 +#define IDC_PLSEL_RANDOM 1315 +#define IDC_ABOUT_VERSION 1327 +#define IDC_ABOUT_COPYRIGHT 1328 +#define IDC_EXTRAINFO 1331 +#define IDC_PLSEL_ROLE_LIST 1332 +#define IDC_PLSEL_RACE_LIST 1333 +#define IDC_PLSEL_ALIGN_LAWFUL 1334 +#define IDC_PLSEL_ALIGN_NEUTRAL 1335 +#define IDC_PLSEL_ALIGN_CHAOTIC 1336 +#define IDC_PLSEL_GENDER_MALE 1337 +#define IDC_PLSEL_GENDER_FEMALE 1338 +#define IDM_SAVE 32771 +#define IDM_HELP_LONG 32772 +#define IDM_HELP_COMMANDS 32773 +#define IDM_HELP_HISTORY 32774 +#define IDM_HELP_INFO_CHAR 32775 +#define IDM_HELP_INFO_KEY 32776 +#define IDM_HELP_OPTIONS 32777 +#define IDM_HELP_OPTIONS_LONG 32778 +#define IDM_HELP_EXTCMD 32779 +#define IDM_HELP_LICENSE 32780 +#define IDM_HELP_PORTHELP 32781 +#define IDM_MAP_TILES 32782 +#define IDM_MAP_ASCII4X6 32783 +#define IDM_MAP_ASCII6X8 32784 +#define IDM_MAP_ASCII8X8 32785 +#define IDM_MAP_ASCII16X8 32786 +#define IDM_MAP_ASCII7X12 32787 +#define IDM_MAP_ASCII8X12 32788 +#define IDM_MAP_ASCII16X12 32789 +#define IDM_MAP_ASCII12X16 32790 +#define IDM_MAP_ASCII10X18 32791 +#define IDM_MAP_FIT_TO_SCREEN 32792 +#define IDM_NHMODE 32794 +#define IDM_CLEARSETTINGS 32795 +#define IDM_SETTING_AUTOLAYOUT 32796 +#define IDM_SETTING_LOCKWINDOWS 32797 #define IDM_SETTING_SCREEN_TO_CLIPBOARD 32798 -#define IDM_SETTING_SCREEN_TO_FILE 32799 -#define IDC_STATIC -1 +#define IDM_SETTING_SCREEN_TO_FILE 32799 +#define IDC_STATIC -1 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 145 -#define _APS_NEXT_COMMAND_VALUE 32800 -#define _APS_NEXT_CONTROL_VALUE 1332 -#define _APS_NEXT_SYMED_VALUE 110 +#define _APS_NEXT_RESOURCE_VALUE 146 +#define _APS_NEXT_COMMAND_VALUE 32800 +#define _APS_NEXT_CONTROL_VALUE 1341 +#define _APS_NEXT_SYMED_VALUE 110 #endif #endif diff --git a/win/win32/winMS.h b/win/win32/winMS.h index 0347674ff..c5ee77368 100644 --- a/win/win32/winMS.h +++ b/win/win32/winMS.h @@ -29,6 +29,7 @@ #define WIN32_LEAN_AND_MEAN #include +#include #include #include #include "hack.h" diff --git a/win/win32/winhack.rc b/win/win32/winhack.rc index 4c69358c9..06876aecf 100644 --- a/win/win32/winhack.rc +++ b/win/win32/winhack.rc @@ -1,4 +1,4 @@ -//Microsoft Developer Studio generated resource script. +// Microsoft Visual C++ generated resource script. // #include "resource.h" @@ -19,13 +19,11 @@ LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// English (U.S.) resources +// English (United States) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -#ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) -#endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // @@ -34,14 +32,15 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_NETHACKW ICON DISCARDABLE "NETHACK.ICO" +IDI_NETHACKW ICON "NETHACK.ICO" + ///////////////////////////////////////////////////////////////////////////// // // Menu // -IDC_NETHACKW MENU DISCARDABLE +IDC_NETHACKW MENU BEGIN POPUP "&File" BEGIN @@ -98,7 +97,7 @@ END // Accelerator // -IDC_NETHACKW ACCELERATORS MOVEABLE PURE +IDC_NETHACKW ACCELERATORS BEGIN "?", IDM_ABOUT, ASCII, ALT "/", IDM_ABOUT, ASCII, ALT @@ -110,8 +109,8 @@ END // Dialog // -IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 230, 75 -STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +IDD_ABOUTBOX DIALOG 22, 17, 230, 75 +STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU CAPTION "About" FONT 8, "System" BEGIN @@ -121,29 +120,27 @@ BEGIN END IDD_NHTEXT DIALOGEX 0, 0, 172, 178 -STYLE WS_CHILD | WS_CLIPSIBLINGS | WS_THICKFRAME +STYLE DS_SETFONT | WS_CHILD | WS_CLIPSIBLINGS | WS_THICKFRAME EXSTYLE WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT -FONT 8, "MS Sans Serif", 0, 0 +FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN - EDITTEXT IDC_TEXT_CONTROL,0,0,172,160,ES_MULTILINE | - ES_OEMCONVERT | ES_READONLY | WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP + EDITTEXT IDC_TEXT_CONTROL,0,0,172,160,ES_MULTILINE | ES_OEMCONVERT | ES_READONLY | WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP DEFPUSHBUTTON "OK",IDOK,54,163,50,14,BS_FLAT | NOT WS_TABSTOP END IDD_MENU DIALOGEX 0, 0, 187, 153 -STYLE WS_CHILD | WS_CLIPSIBLINGS | WS_THICKFRAME +STYLE DS_SETFONT | WS_CHILD | WS_CLIPSIBLINGS | WS_THICKFRAME EXSTYLE WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT -FONT 8, "MS Sans Serif", 0, 0 +FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN LISTBOX IDC_MENU_LIST,10,10,170,55,LBS_SORT | NOT WS_BORDER - EDITTEXT IDC_MENU_TEXT,10,70,170,60,ES_MULTILINE | ES_OEMCONVERT | - ES_READONLY | WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP | NOT WS_BORDER + EDITTEXT IDC_MENU_TEXT,10,70,170,60,ES_MULTILINE | ES_OEMCONVERT | ES_READONLY | NOT WS_BORDER | WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP DEFPUSHBUTTON "OK",IDOK,7,132,50,14,BS_FLAT | NOT WS_TABSTOP PUSHBUTTON "Cancel",IDCANCEL,130,132,50,14,BS_FLAT | NOT WS_TABSTOP END -IDD_GETLIN DIALOG DISCARDABLE 0, 0, 131, 29 -STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_GETLIN DIALOG 0, 0, 131, 29 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Question?" FONT 8, "MS Sans Serif" BEGIN @@ -152,65 +149,54 @@ BEGIN EDITTEXT IDC_GETLIN_EDIT,0,0,130,13,ES_AUTOHSCROLL END -IDD_EXTCMD DIALOG DISCARDABLE 0, 0, 137, 117 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_EXTCMD DIALOG 0, 0, 137, 117 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Extended Commands" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,80,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,80,24,50,14 - LISTBOX IDC_EXTCMD_LIST,7,7,65,103,LBS_NOINTEGRALHEIGHT | - WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_EXTCMD_LIST,7,7,65,103,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP END -IDD_PLAYER_SELECTOR DIALOG DISCARDABLE 0, 0, 152, 169 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_PLAYER_SELECTOR DIALOGEX 0, 0, 225, 212 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "What are you?" -FONT 8, "MS Sans Serif" +FONT 8, "MS Sans Serif", 0, 0, 0x0 BEGIN - DEFPUSHBUTTON "Play",IDOK,7,148,66,14 - PUSHBUTTON "Quit",IDCANCEL,79,148,66,14 - LTEXT "Name:",IDC_STATIC,7,8,25,10 - EDITTEXT IDC_PLSEL_NAME,40,7,105,12,ES_AUTOHSCROLL | ES_READONLY | - NOT WS_TABSTOP - GROUPBOX "Role",IDC_STATIC,7,21,138,30 - CONTROL "Random",IDC_PLSEL_ROLE_RANDOM,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,14,34,40,10 - COMBOBOX IDC_PLSEL_ROLE_LIST,63,33,75,50,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP - GROUPBOX "Race",IDC_STATIC,7,51,138,30 - CONTROL "Random",IDC_PLSEL_RACE_RANDOM,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,14,63,40,10 - COMBOBOX IDC_PLSEL_RACE_LIST,63,62,75,45,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP - GROUPBOX "Gender",IDC_STATIC,7,81,138,30 - CONTROL "Random",IDC_PLSEL_GENDER_RANDOM,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,14,93,40,10 - COMBOBOX IDC_PLSEL_GENDER_LIST,63,92,75,40,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP - GROUPBOX "Alignment",IDC_STATIC,7,111,138,30 - CONTROL "Random",IDC_PLSEL_ALIGN_RANDOM,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,13,123,40,10 - COMBOBOX IDC_PLSEL_ALIGN_LIST,63,122,75,45,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "Play",IDOK,12,192,54,14,WS_GROUP + PUSHBUTTON "Quit",IDCANCEL,162,192,54,14,WS_GROUP + EDITTEXT IDC_PLSEL_NAME,12,12,105,12,ES_AUTOHSCROLL | ES_READONLY | WS_GROUP | NOT WS_TABSTOP + CONTROL "Neutral",IDC_PLSEL_ALIGN_NEUTRAL,"Button",BS_AUTORADIOBUTTON,168,60,37,10 + CONTROL "Lawful",IDC_PLSEL_ALIGN_LAWFUL,"Button",BS_AUTORADIOBUTTON | WS_GROUP,168,48,35,10 + CONTROL "Chaotic",IDC_PLSEL_ALIGN_CHAOTIC,"Button",BS_AUTORADIOBUTTON,168,72,38,10 + CONTROL "Male",IDC_PLSEL_GENDER_MALE,"Button",BS_AUTORADIOBUTTON | WS_GROUP,168,108,30,10 + CONTROL "Female",IDC_PLSEL_GENDER_FEMALE,"Button",BS_AUTORADIOBUTTON,168,120,38,10 + GROUPBOX "Alignment",IDC_STATIC,162,36,48,54 + GROUPBOX "Gender",IDC_STATIC,162,96,48,42 + GROUPBOX "Role",IDC_STATIC,6,36,72,150 + GROUPBOX "Race",IDC_STATIC,84,36,72,72 + PUSHBUTTON "Random",IDC_PLSEL_RANDOM,90,192,54,14,WS_GROUP + GROUPBOX "Name",IDC_STATIC,6,0,120,30 + CONTROL "",IDC_PLSEL_ROLE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_OWNERDRAWFIXED | LVS_ALIGNLEFT | LVS_NOSCROLL | LVS_NOCOLUMNHEADER | WS_BORDER | WS_GROUP | WS_TABSTOP,12,48,60,130 + CONTROL "",IDC_PLSEL_RACE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_OWNERDRAWFIXED | LVS_ALIGNLEFT | LVS_NOSCROLL | LVS_NOCOLUMNHEADER | WS_BORDER | WS_GROUP | WS_TABSTOP,90,48,60,51 END IDD_NHRIP DIALOGEX 0, 0, 281, 209 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Here lies..." -FONT 8, "MS Sans Serif", 0, 0 +FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,82,188,50,14 END -IDD_SPLASH DIALOG DISCARDABLE 0, 0, 281, 257 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +IDD_SPLASH DIALOG 0, 0, 281, 257 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Welcome to NetHack" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,224,236,50,14 - EDITTEXT IDC_EXTRAINFO,7,176,267,52,ES_MULTILINE | ES_READONLY | - WS_VSCROLL + EDITTEXT IDC_EXTRAINFO,7,176,267,52,ES_MULTILINE | ES_READONLY | WS_VSCROLL END @@ -220,7 +206,7 @@ END // TEXTINCLUDE // -2 TEXTINCLUDE DISCARDABLE +2 TEXTINCLUDE BEGIN "#if defined(__BORLANDC__)\r\n" "LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US\r\n" @@ -232,13 +218,13 @@ BEGIN "\0" END -3 TEXTINCLUDE DISCARDABLE +3 TEXTINCLUDE BEGIN "\r\n" "\0" END -1 TEXTINCLUDE DISCARDABLE +1 TEXTINCLUDE BEGIN "resource.h\0" END @@ -251,14 +237,22 @@ END // Bitmap // -IDB_TILES BITMAP DISCARDABLE "tiles.bmp" -IDB_MENU_SEL BITMAP DISCARDABLE "mnsel.bmp" -IDB_MENU_UNSEL BITMAP DISCARDABLE "mnunsel.bmp" -IDB_PETMARK BITMAP DISCARDABLE "petmark.bmp" -IDB_PILEMARK BITMAP DISCARDABLE "pilemark.bmp" -IDB_MENU_SEL_COUNT BITMAP DISCARDABLE "mnselcnt.bmp" -IDB_RIP BITMAP DISCARDABLE "rip.bmp" -IDB_SPLASH BITMAP DISCARDABLE "splash.bmp" +IDB_TILES BITMAP "tiles.bmp" + +IDB_MENU_SEL BITMAP "mnsel.bmp" + +IDB_MENU_UNSEL BITMAP "mnunsel.bmp" + +IDB_PETMARK BITMAP "petmark.bmp" + +IDB_PILEMARK BITMAP "pilemark.bmp" + +IDB_MENU_SEL_COUNT BITMAP "mnselcnt.bmp" + +IDB_RIP BITMAP "rip.bmp" + +IDB_SPLASH BITMAP "splash.bmp" + ///////////////////////////////////////////////////////////////////////////// // @@ -266,7 +260,7 @@ IDB_SPLASH BITMAP DISCARDABLE "splash.bmp" // #ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE +GUIDELINES DESIGNINFO BEGIN IDD_NHTEXT, DIALOG BEGIN @@ -297,9 +291,9 @@ BEGIN IDD_PLAYER_SELECTOR, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 145 + RIGHTMARGIN, 218 TOPMARGIN, 7 - BOTTOMMARGIN, 162 + BOTTOMMARGIN, 205 END IDD_NHRIP, DIALOG @@ -342,14 +336,14 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "FileDescription", "NetHack for Windows - Graphical Interface\0" - VALUE "FileVersion", "3.6.1\0" - VALUE "InternalName", "NetHackW\0" - VALUE "LegalCopyright", "Copyright (C) 1985 - 2017. By Stichting Mathematisch Centrum and M. Stephenson. See license for details.\0" - VALUE "OriginalFilename", "NetHackW.exe\0" - VALUE "PrivateBuild", "140606\0" - VALUE "ProductName", "NetHack\0" - VALUE "ProductVersion", "3.6.1\0" + VALUE "FileDescription", "NetHack for Windows - Graphical Interface" + VALUE "FileVersion", "3.6.1" + VALUE "InternalName", "NetHackW" + VALUE "LegalCopyright", "Copyright (C) 1985 - 2017. By Stichting Mathematisch Centrum and M. Stephenson. See license for details." + VALUE "OriginalFilename", "NetHackW.exe" + VALUE "PrivateBuild", "140606" + VALUE "ProductName", "NetHack" + VALUE "ProductVersion", "3.6.1" END END BLOCK "VarFileInfo" @@ -358,19 +352,31 @@ BEGIN END END + +///////////////////////////////////////////////////////////////////////////// +// +// AFX_DIALOG_LAYOUT +// + +IDD_PLAYER_SELECTOR AFX_DIALOG_LAYOUT +BEGIN + 0 +END + + ///////////////////////////////////////////////////////////////////////////// // // String Table // -STRINGTABLE DISCARDABLE +STRINGTABLE BEGIN IDS_APP_TITLE "NetHack for Windows - Graphical Interface" IDC_NETHACKW "NETHACKW" IDS_APP_TITLE_SHORT "NetHack for Windows" END -#endif // English (U.S.) resources +#endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// From 6322aec82968089bb90759dd05ed6da7e0a79b18 Mon Sep 17 00:00:00 2001 From: Bart House Date: Sat, 21 Oct 2017 13:41:29 -0700 Subject: [PATCH 07/25] Win32TTY: Fix using a console font with wide glyphs Added support to detect when the current console font has glyphs that are too wide and will cause rendering errors in the console. If detected, we warn the user and change the code page to 437 and the font to Consolas. At exit, if we had changed the font and code page then we will restore to the original font and code page. --- include/rm.h | 2 + src/options.c | 1 + sys/winnt/nttty.c | 264 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 232 insertions(+), 35 deletions(-) diff --git a/include/rm.h b/include/rm.h index 2c68e6515..18b33050b 100644 --- a/include/rm.h +++ b/include/rm.h @@ -300,6 +300,8 @@ extern const struct symdef defsyms[MAXPCHARS]; /* defaults */ extern const struct symdef def_warnsyms[WARNCOUNT]; extern int currentgraphics; /* from drawing.c */ extern nhsym showsyms[]; +extern nhsym l_syms[]; +extern nhsym r_syms[]; extern struct symsetentry symset[NUM_GRAPHICS]; /* from drawing.c */ #define SYMHANDLING(ht) (symset[currentgraphics].handling == (ht)) diff --git a/src/options.c b/src/options.c index d378d2200..bfd42b189 100644 --- a/src/options.c +++ b/src/options.c @@ -5114,6 +5114,7 @@ boolean setinitial, setfromfile; assign_graphics(ROGUESET); } else if (!rogueflag) assign_graphics(PRIMARY); + preference_update("symset"); need_redraw = TRUE; return TRUE; diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index 64bea743c..3ac469ea8 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -44,6 +44,10 @@ int FDECL(process_keystroke, (INPUT_RECORD *, boolean *, BOOLEAN_P numberpad, int portdebug)); static void NDECL(init_ttycolor); static void NDECL(really_move_cursor); +static void NDECL(check_and_set_font); +static boolean NDECL(check_font_widths); +static void NDECL(set_known_good_console_font); +static void NDECL(restore_original_console_font); /* Win32 Console handles for input and output */ HANDLE hConIn; @@ -54,6 +58,11 @@ CONSOLE_SCREEN_BUFFER_INFO csbi, origcsbi; COORD ntcoord; INPUT_RECORD ir; +/* Support for changing console font if existing glyph widths are too wide */ +boolean console_font_changed; +CONSOLE_FONT_INFOEX original_console_font_info; +UINT original_console_code_page; + extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ extern int redirect_stdout; @@ -148,6 +157,10 @@ KEYHANDLERNAME pKeyHandlerName; void gettty() { + console_font_changed = FALSE; + + check_and_set_font(); + #ifndef TEXTCOLOR int k; #endif @@ -171,6 +184,8 @@ const char *s; end_screen(); if (s) raw_print(s); + + restore_original_console_font(); } /* called by init_nhwindows() and resume_nhwindows() */ @@ -514,45 +529,47 @@ char ch; * Overrides wintty.c function of the same name * for win32. It is used for glyphs only, not text. */ + +/* CP437 to Unicode mapping according to the Unicode Consortium */ +static const WCHAR cp437[] = { + 0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, + 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, + 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, + 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, + 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, + 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, + 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, + 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, + 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, + 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, + 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, + 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, + 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, + 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, + 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, + 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, + 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 +}; + void g_putch(in_ch) int in_ch; { - /* CP437 to Unicode mapping according to the Unicode Consortium */ - static const WCHAR cp437[] = { - 0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, - 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, - 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, - 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302, - 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, - 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, - 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, - 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, - 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, - 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, - 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, - 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, - 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, - 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, - 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, - 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, - 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, - 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, - 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, - 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 - }; boolean inverse = FALSE; unsigned char ch = (unsigned char) in_ch; @@ -853,6 +870,8 @@ const char *pref; toggle_mouse_support(); #endif } + if (stricmp(pref, "symset") == 0) + check_and_set_font(); return; } @@ -1466,4 +1485,179 @@ GetConsoleHwnd(void) return hwndFound; } #endif /*CHANGE_COLOR*/ + +static int CALLBACK EnumFontCallback( + const LOGFONTW * lf, const TEXTMETRICW * tm, DWORD fontType, LPARAM lParam) +{ + LOGFONTW * lf_ptr = (LOGFONTW *) lParam; + *lf_ptr = *lf; + return 0; +} + +/* check_and_set_font ensures that the current font will render the symbols + * that are currently being used correctly. If they will not be rendered + * correctly, then it will change the font to a known good font. + */ +void +check_and_set_font() +{ + if (!check_font_widths()) { + raw_print("WARNING: glyphs too wide in console font." + " Changing code page to 437 and font to Consolas\n"); + set_known_good_console_font(); + } +} + +/* check_font_widths returns TRUE if all glyphs in current console font + * fit within the width of a single console cell. + */ +boolean +check_font_widths() +{ + CONSOLE_FONT_INFOEX console_font_info; + console_font_info.cbSize = sizeof(console_font_info); + BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE, + &console_font_info); + + /* get console window and DC + * NOTE: the DC from the console window does not have the correct + * font selected at this point. + */ + HWND hWnd = GetConsoleWindow(); + HDC hDC = GetDC(hWnd); + + LOGFONTW logical_font; + logical_font.lfCharSet = DEFAULT_CHARSET; + wcscpy(logical_font.lfFaceName, console_font_info.FaceName); + logical_font.lfPitchAndFamily = 0; + + /* getting matching console font */ + LOGFONTW matching_log_font = { 0 }; + EnumFontFamiliesExW(hDC, &logical_font, EnumFontCallback, + (LPARAM) &matching_log_font, 0); + + if (matching_log_font.lfHeight == 0) { + raw_print("Unable to enumerate system fonts\n"); + return FALSE; + } + + /* create font matching console font */ + LOGFONTW console_font_log_font = matching_log_font; + console_font_log_font.lfWeight = console_font_info.FontWeight; + console_font_log_font.lfHeight = console_font_info.dwFontSize.Y; + console_font_log_font.lfWidth = 0; + HFONT console_font = CreateFontIndirectW(&console_font_log_font); + + if (console_font == NULL) { + raw_print("Unable to create console font\n"); + return FALSE; + } + + /* select font */ + HGDIOBJ saved_font = SelectObject(hDC, console_font); + + /* determine whether it is a true type font */ + TEXTMETRICA tm; + success = GetTextMetricsA(hDC, &tm); + + if (!success) { + raw_print("Unable to get console font text metrics\n"); + goto clean_up; + } + + boolean isTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) != 0; + + /* determine which glyphs are used */ + boolean used[256]; + memset(used, 0, sizeof(used)); + for (int i = 0; i < SYM_MAX; i++) { + used[l_syms[i]] = TRUE; + used[r_syms[i]] = TRUE; + } + + int wcUsedCount = 0; + wchar_t wcUsed[256]; + for (int i = 0; i < sizeof(used); i++) + if (used[i]) + wcUsed[wcUsedCount++] = cp437[i]; + + /* measure the set of used glyphs to ensure they fit */ + boolean all_glyphs_fit = TRUE; + + for (int i = 0; i < wcUsedCount; i++) { + int width; + if (isTrueType) { + ABC abc; + success = GetCharABCWidthsW(hDC, wcUsed[i], wcUsed[i], &abc); + width = abc.abcA + abc.abcB + abc.abcC; + } else { + success = GetCharWidthW(hDC, wcUsed[i], wcUsed[i], &width); + } + + if (success && width > console_font_info.dwFontSize.X) { + all_glyphs_fit = FALSE; + break; + } + } + +clean_up: + + SelectObject(hDC, saved_font); + DeleteObject(console_font); + + return all_glyphs_fit; +} + +/* set_known_good_console_font sets the code page and font used by the console + * to settings know to work well with NetHack. It also saves the original + * settings so that they can be restored prior to NetHack exit. + */ +void +set_known_good_console_font() +{ + CONSOLE_FONT_INFOEX console_font_info; + console_font_info.cbSize = sizeof(console_font_info); + BOOL success = GetCurrentConsoleFontEx(hConOut, FALSE, + &console_font_info); + + console_font_changed = TRUE; + original_console_font_info = console_font_info; + original_console_code_page = GetConsoleOutputCP(); + + wcscpy_s(console_font_info.FaceName, + sizeof(console_font_info.FaceName) + / sizeof(console_font_info.FaceName[0]), + L"Consolas"); + + success = SetConsoleOutputCP(437); + if (!success) + raw_print("Unable to set console code page to 437\n"); + + success = SetCurrentConsoleFontEx(hConOut, FALSE, &console_font_info); + if (!success) + raw_print("Unable to set console font to Consolas\n"); +} + +/* restore_original_console_font will restore the console font and code page + * settings to what they were when NetHack was launched. + */ +void +restore_original_console_font() +{ + if (console_font_changed) { + BOOL success; + raw_print("Restoring original font and code page\n"); + success = SetConsoleOutputCP(original_console_code_page); + if (!success) + raw_print("Unable to restore original code page\n"); + + success = SetCurrentConsoleFontEx(hConOut, FALSE, + &original_console_font_info); + if (!success) + raw_print("Unable to restore original font\n"); + + console_font_changed = FALSE; + } +} + #endif /* WIN32 */ From 4dca7db840f2b5e8d4908370930dea8bcbff1670 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 25 Oct 2017 11:05:03 +0300 Subject: [PATCH 08/25] Fixes entries for recent Windows commits --- doc/fixes36.1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 7be6ef2e3..ccde572fd 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -584,6 +584,9 @@ X11/USE_XPM: [post-3.6.0 issue] the 'tiles' bug of incorrect total_tiles_used for STATUES_LOOK_LIKE_MONSTERS broke XPM manipulation of tiles data PANICTRACE: PANICTRACE_GDB used wrong value for ARGV0 when launching gdb if 'nethack -dpath' was used to specify non-default playground directory +win32gui: gather raw_print error messages into a single dialog window +win32tty: fix display errors when using a font with double wide or ambiguous + width characters General New Features @@ -721,6 +724,7 @@ X11: more terminal-like default resources win32gui: save and load map colors from registry X11: add new character selection dialog, and obey player_selection:dialog unix: reduce makefile verbosity by default +win32gui: new player selection dialog NetHack Community Patches (or Variation) Included From 4af8a93cff6bf0e81d2c1ccb52fb754d3ed5abdd Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 25 Oct 2017 12:27:02 +0300 Subject: [PATCH 09/25] Status hilites: Fix defining hunger via menu --- src/botl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/botl.c b/src/botl.c index df45de839..7b06dec87 100644 --- a/src/botl.c +++ b/src/botl.c @@ -2593,7 +2593,7 @@ int fld; nopts++; } - if (fld != BL_CAP && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) { + if (fld != BL_CAP && fld != BL_HUNGER && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) { any = zeroany; any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE; add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, @@ -2609,7 +2609,7 @@ int fld; nopts++; } - if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP) { + if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP || fld == BL_HUNGER) { any = zeroany; any.a_int = onlybeh = BL_TH_TEXTMATCH; Sprintf(buf, "%s text match", initblstats[fld].fldname); @@ -2878,7 +2878,7 @@ choose_value: hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, aligntxt[rv]); } else if (fld == BL_HUNGER) { - const char *hutxt[] = {"Satiated", "", "Hungry", "Weak", + const char *hutxt[] = {"Satiated", (char *)0, "Hungry", "Weak", "Fainting", "Fainted", "Starved"}; int rv = query_arrayvalue(qry_buf, hutxt, From 279050ba84b46ce3df70cb371191a9b5eec2c86a Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 25 Oct 2017 14:21:07 +0300 Subject: [PATCH 10/25] Sometimes put rings dropped into sinks in the pipes aka bury the ring under the sink. Idea from Fredrik Ljungdahl. --- doc/fixes36.1 | 1 + src/do.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index ccde572fd..863bd0798 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -462,6 +462,7 @@ when poly'd into a giant and moving onto a boulder's spot, you could get "you it up", "you are carrying too much stuff to pick up another boulder" improve #adjust command's handling of the '$' and '#' inventory slots prevent #adjust from allowing anything to be moved into the special '-' slot +sometimes rings dropped into sinks can be found in the pipes Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/do.c b/src/do.c index 28d745082..352d26e99 100644 --- a/src/do.c +++ b/src/do.c @@ -501,6 +501,12 @@ register struct obj *obj; pline_The("sink backs up, leaving %s.", doname(obj)); obj->in_use = FALSE; dropx(obj); + } else if (!rn2(5)) { + freeinv(obj); + obj->in_use = FALSE; + obj->ox = u.ux; + obj->oy = u.uy; + add_to_buried(obj); } else useup(obj); } From 0bdbfaa580e7ab9a48bfee11960c75ee0bdfb273 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 25 Oct 2017 19:13:21 -0700 Subject: [PATCH 11/25] strbuf cleanup The expression '*cp-- = cp[-count]' is not valid C. There's no sequence point between the two references to 'cp', and the decrement side-effect could occur before or after cp[-count] is resolved. The functions were also using ANSI-style argument definitions. The rest is just reformatting. It seems to me that the strbuf structure ought to have an allocation size field in addition to the current length field. Otherwise a string which gets shortened will forget about the extra length available for later expansion, potentially resulting in unnecessary reallocation. --- src/hacklib.c | 66 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/hacklib.c b/src/hacklib.c index 69cfc3f5b..a5d9d0c15 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -1107,66 +1107,80 @@ midnight() } /* strbuf_init() initializes strbuf state for use */ -void strbuf_init(strbuf_t * strbuf) +void +strbuf_init(strbuf) +strbuf_t *strbuf; { strbuf->str = NULL; strbuf->len = 0; } /* strbuf_append() appends given str to strbuf->str */ -void strbuf_append(strbuf_t * strbuf, const char * str) +void +strbuf_append(strbuf, str) +strbuf_t *strbuf; +const char *str; { - if (strbuf->str == NULL) - strbuf_reserve(strbuf, strlen(str) + 1); - else - strbuf_reserve(strbuf, strlen(strbuf->str) + strlen(str) + 1); + int len = (int) strlen(str) + 1; - strcat(strbuf->str, str); + strbuf_reserve(strbuf, + len + (strbuf->str ? (int) strlen(strbuf->str) : 0)); + Strcat(strbuf->str, str); } /* strbuf_reserve() ensure strbuf->str has storage for len characters */ -void strbuf_reserve(strbuf_t * strbuf, int len) +void +strbuf_reserve(strbuf, len) +strbuf_t *strbuf; +int len; { if (strbuf->str == NULL) { strbuf->str = strbuf->buf; strbuf->str[0] = '\0'; - strbuf->len = sizeof(strbuf->buf); + strbuf->len = (int) sizeof strbuf->buf; } if (len > strbuf->len) { - char * oldbuf = strbuf->str; - strbuf->len = len + sizeof(strbuf->buf); + char *oldbuf = strbuf->str; + + strbuf->len = len + (int) sizeof strbuf->buf; strbuf->str = (char *) alloc(strbuf->len); - strcpy(strbuf->str, oldbuf); - if (oldbuf != strbuf->buf) free(oldbuf); + Strcpy(strbuf->str, oldbuf); + if (oldbuf != strbuf->buf) + free((genericptr_t) oldbuf); } } /* strbuf_empty() frees allocated memory and set strbuf to initial state */ -void strbuf_empty(strbuf_t * strbuf) +void +strbuf_empty(strbuf) +strbuf_t *strbuf; { - if (strbuf->str != strbuf->buf) - free(strbuf->str); + if (strbuf->str != NULL && strbuf->str != strbuf->buf) + free((genericptr_t) strbuf->str); strbuf_init(strbuf); } /* strbuf_nl_to_crlf() converts all occurences of \n to \r\n */ -void strbuf_nl_to_crlf(strbuf_t * strbuf) +void +strbuf_nl_to_crlf(strbuf) +strbuf_t *strbuf; { if (strbuf->str) { - int len = strlen(strbuf->str); + int len = (int) strlen(strbuf->str); int count = 0; - char * cp = strbuf->str; - while (*cp) if (*cp++ == '\n') count++; + char *cp = strbuf->str; + + while (*cp) + if (*cp++ == '\n') + count++; if (count) { strbuf_reserve(strbuf, len + count + 1); - cp = strbuf->str + len + count; - while (count) { - if ((*cp-- = cp[-count]) == '\n') { - *cp-- = '\r'; - count--; + for (cp = strbuf->str + len + count; count; --cp) + if ((*cp = cp[-count]) == '\n') { + *--cp = '\r'; + --count; } - } } } } From bf0223fd9f9cd6fffab4b24b1034d574eb444d83 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 26 Oct 2017 21:56:43 +0300 Subject: [PATCH 12/25] Fix door orientation in des-files Doors in des-files were always generated vertically. This wasn't visible unless you had separate symbols for closed vertical and horizontal doors, or used tiles. --- doc/fixes36.1 | 1 + include/rm.h | 3 ++- src/sp_lev.c | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 863bd0798..8f01b89a3 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -463,6 +463,7 @@ when poly'd into a giant and moving onto a boulder's spot, you could get "you improve #adjust command's handling of the '$' and '#' inventory slots prevent #adjust from allowing anything to be moved into the special '-' slot sometimes rings dropped into sinks can be found in the pipes +doors in special levels were always generated in vertical orientation Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/include/rm.h b/include/rm.h index 18b33050b..9a0ce5343 100644 --- a/include/rm.h +++ b/include/rm.h @@ -85,7 +85,8 @@ enum levl_typ_types { #define IS_STWALL(typ) ((typ) <= DBWALL) /* STONE <= (typ) <= DBWALL */ #define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */ #define IS_DOOR(typ) ((typ) == DOOR) -#define IS_TREE(typ) \ +#define IS_DOORJOIN(typ) (IS_ROCK(typ) || (typ) == IRONBARS) +#define IS_TREE(typ) \ ((typ) == TREE || (level.flags.arboreal && (typ) == STONE)) #define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ #define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM, STAIRS, furniture.. */ diff --git a/src/sp_lev.c b/src/sp_lev.c index badbab319..fc1dcf9e1 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -4191,6 +4191,13 @@ genericptr_t arg; if (typ < D_CLOSED) typ = D_CLOSED; } + + if (((isok(x-1,y) && IS_DOORJOIN(levl[x-1][y].typ)) || !isok(x-1,y)) + || (isok(x+1,y) && IS_DOORJOIN(levl[x+1][y].typ)) || !isok(x+1,y)) + levl[x][y].horizontal = 1; + else + levl[x][y].horizontal = 0; + levl[x][y].doormask = typ; SpLev_Map[x][y] = 1; } From 25c27cd4cf1670b78cdab86410bbf7f28ed7ea54 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 28 Oct 2017 01:18:25 -0700 Subject: [PATCH 13/25] fix #H6338 - naming mimicked potion Player tried to #name a potion on the floor and got prompted to call a stream of fluid (sink feedback) instead of a potion. A mimic posing as an object is represented by a partially initialized object when examining its map location. #name for floor object uses the same data as look_at. obj->fromsink overloads obj->corpsenm which is set to NON_PM (-1) even when creating a non-init'd object. 'fromsink' was only being forced to 0 when creating an init'd object (unlike leash which has its overload of corpsenm set properly regardless of caller's request to init). So docall() treated a mimicked potion as a sink stream. The fix is straightforward but has pointed out another bug which is harder to fix. Examining a floor object next to you sets that obj's dknown flag as if you had seen it up close (a new feature in 3.6.0). But a mimicked item is discarded as soon as it's been looked at, so looking again from a non-adjacent spot will give different feedback since the previously set dknown will be unset when replaced by a new fake object. So you can use '/' and ';' to recognize mimics without provoking them into motion. Best fix: mimicking an object should use a fully initialized one which is tracked via monst->mextra, but that will break save file compatibility. Possible hack: change monst-> mappearance into a mask which uses N bits for object type (instead of full 'int') and one of the other bits to track obj->dknown. Examining an adjacent object probably ought to set bknown for priests, so bknown and blessed/uncursed/cursed would need to be tracked too. --- doc/fixes36.1 | 2 ++ src/mkobj.c | 44 +++++++++++++++++++++++++------------------- src/pager.c | 2 +- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 8f01b89a3..3002f5512 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -464,6 +464,8 @@ improve #adjust command's handling of the '$' and '#' inventory slots prevent #adjust from allowing anything to be moved into the special '-' slot sometimes rings dropped into sinks can be found in the pipes doors in special levels were always generated in vertical orientation +assigning a type name to a potion on the floor which is actually a mimic could + prompt "Call a stream of fluid:" (bogus 'fromsink') Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mkobj.c b/src/mkobj.c index ab59719b7..43d62b6c2 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -223,6 +223,7 @@ boolean init, artif; return otmp; } +/* mkobj(): select a type of item from a class, use mksobj() to create it */ struct obj * mkobj(oclass, artif) char oclass; @@ -277,7 +278,7 @@ struct obj *box; n = 0; break; } - /*else FALLTHRU*/ + /*else FALLTHRU*/ case BAG_OF_HOLDING: n = 1; break; @@ -712,6 +713,7 @@ static const char dknowns[] = { WAND_CLASS, RING_CLASS, POTION_CLASS, SCROLL_CLASS, GEM_CLASS, SPBOOK_CLASS, WEAPON_CLASS, TOOL_CLASS, 0 }; +/* mksobj(): create a specific type of object */ struct obj * mksobj(otyp, init, artif) int otyp; @@ -743,7 +745,7 @@ boolean artif; otmp->cknown = 0; otmp->corpsenm = NON_PM; - if (init) + if (init) { switch (let) { case WEAPON_CLASS: otmp->quan = is_multigen(otmp) ? (long) rn1(6, 6) : 1L; @@ -773,9 +775,8 @@ boolean artif; && (--tryct > 0)); if (tryct == 0) { /* perhaps rndmonnum() only wants to make G_NOCORPSE - monsters on - this level; let's create an adventurer's corpse - instead, then */ + monsters on this level; create an adventurer's + corpse instead, then */ otmp->corpsenm = PM_HUMAN; } /* timer set below */ @@ -889,14 +890,13 @@ boolean artif; case BAG_OF_TRICKS: otmp->spe = rnd(20); break; - case FIGURINE: { - int tryct2 = 0; + case FIGURINE: + tryct = 0; do otmp->corpsenm = rndmonnum(); - while (is_human(&mons[otmp->corpsenm]) && tryct2++ < 30); + while (is_human(&mons[otmp->corpsenm]) && tryct++ < 30); blessorcurse(otmp, 4); break; - } case BELL_OF_OPENING: otmp->spe = 3; break; @@ -918,15 +918,12 @@ boolean artif; curse(otmp); } else blessorcurse(otmp, 10); + break; case VENOM_CLASS: case CHAIN_CLASS: case BALL_CLASS: break; - case POTION_CLASS: - otmp->fromsink = 0; - if (otmp->otyp == POT_OIL) - otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */ - /* fall through */ + case POTION_CLASS: /* note: potions get some additional init below */ case SCROLL_CLASS: #ifdef MAIL if (otmp->otyp != SCR_MAIL) @@ -1012,27 +1009,36 @@ boolean artif; objects[otmp->otyp].oc_class); return (struct obj *) 0; } + } /* some things must get done (corpsenm, timers) even if init = 0 */ - switch (otmp->otyp) { + switch ((otmp->oclass == POTION_CLASS && otmp->otyp != POT_OIL) + ? POT_WATER + : otmp->otyp) { case CORPSE: if (otmp->corpsenm == NON_PM) { otmp->corpsenm = undead_to_corpse(rndmonnum()); if (mvitals[otmp->corpsenm].mvflags & (G_NOCORPSE | G_GONE)) otmp->corpsenm = urole.malenum; } - /*FALLTHRU*/ + /*FALLTHRU*/ case STATUE: case FIGURINE: if (otmp->corpsenm == NON_PM) otmp->corpsenm = rndmonnum(); - /*FALLTHRU*/ + /*FALLTHRU*/ case EGG: - /* case TIN: */ + /* case TIN: */ set_corpsenm(otmp, otmp->corpsenm); break; + case POT_OIL: + otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */ + /*FALLTHRU*/ + case POT_WATER: /* POTION_CLASS */ + otmp->fromsink = 0; /* overloads corpsenm, which was set to NON_PM */ + break; case LEASH: - otmp->leashmon = 0; + otmp->leashmon = 0; /* overloads corpsenm, which was set to NON_PM */ break; case SPE_NOVEL: otmp->novelidx = -1; /* "none of the above"; will be changed */ diff --git a/src/pager.c b/src/pager.c index d7fc456b8..5d9f4ddc1 100644 --- a/src/pager.c +++ b/src/pager.c @@ -195,7 +195,7 @@ struct obj **obj_p; glyph among floor and buried objects; when !Blind, any buried object's glyph will have been replaced by whatever is present on the surface as soon as we moved next to its spot */ - && otmp->where == OBJ_FLOOR /* not buried */ + && (fakeobj || otmp->where == OBJ_FLOOR) /* not buried */ /* terrain mode views what's already known, doesn't learn new stuff */ && !iflags.terrainmode) /* so don't set dknown when in terrain mode */ otmp->dknown = 1; /* if a pile, clearly see the top item only */ From bba7a6b44c709a0cb7301db34f0f8541cc6cbe38 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 28 Oct 2017 09:53:27 -0400 Subject: [PATCH 14/25] fix Makefile build for Windows after recent changes New code in nttty.c had a dependency on gdi32.lib. Previously that was only being linked in for the gui build when using the Makefile. Move the reference into the base libraries if both tty and gui depend on it now. --- sys/winnt/Makefile.msc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc index 380019228..93324245e 100644 --- a/sys/winnt/Makefile.msc +++ b/sys/winnt/Makefile.msc @@ -247,8 +247,8 @@ guilflags = $(lflags) -subsystem:windows,$(EXEVER) dlllflags = $(lflags) -entry:_DllMainCRTStartup$(DLLENTRY) -dll # basic subsystem specific libraries, less the C Run-Time -baselibs = kernel32.lib $(optlibs) $(winsocklibs) advapi32.lib -winlibs = $(baselibs) user32.lib gdi32.lib comdlg32.lib winspool.lib +baselibs = kernel32.lib $(optlibs) $(winsocklibs) advapi32.lib gdi32.lib +winlibs = $(baselibs) user32.lib comdlg32.lib winspool.lib # for Windows applications that use the C Run-Time libraries conlibs = $(baselibs) From 72978d69faa880b1808d549f717445b6a1e3fb96 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 28 Oct 2017 14:12:50 -0700 Subject: [PATCH 15/25] fix #6284 - empty perm_invent Report was for tty, but X11 exhibited the same behavior. With the perm_invent option enabled, when the permanent inventory window is displayed, it would be empty if not carrying anything. For tty, that meant a naked "(end) " selection prompt. Put a separator line of "Not carrying anything" into the menu so that it won't be completely empty. The selection prompt is still present but it is attached to something. (The behavior is different from !perm_invent, where you get that same text via pline without any menu at all.) --- doc/fixes36.1 | 1 + src/end.c | 8 ++++++-- src/invent.c | 21 ++++++++++++++++----- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 3002f5512..c36858045 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -466,6 +466,7 @@ sometimes rings dropped into sinks can be found in the pipes doors in special levels were always generated in vertical orientation assigning a type name to a potion on the floor which is actually a mimic could prompt "Call a stream of fluid:" (bogus 'fromsink') +with perm_invent option enabled and no inventory, 'i' put up an empty menu Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/end.c b/src/end.c index d0fb09bc5..e1d8a6423 100644 --- a/src/end.c +++ b/src/end.c @@ -1274,8 +1274,11 @@ int how; if (have_windows) { wait_synch(); free_pickinv_cache(); /* extra persistent window if perm_invent */ - if (WIN_INVEN != WIN_ERR) + if (WIN_INVEN != WIN_ERR) { destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR; + /* precaution in case any late update_inventory() calls occur */ + flags.perm_invent = 0; + } display_nhwindow(WIN_MESSAGE, TRUE); destroy_nhwindow(WIN_MAP), WIN_MAP = WIN_ERR; #ifndef STATUS_HILITES @@ -1315,7 +1318,7 @@ int how; ? (const char *) ((flags.female && urole.name.f) ? urole.name.f : urole.name.m) - : (const char *) (flags.female ? "Demigoddess" : "Demigod")); + : (const char *) (flags.female ? "Demigoddess" : "Demigod")); dump_forward_putstr(endwin, 0, pbuf, done_stopprint); dump_forward_putstr(endwin, 0, "", done_stopprint); @@ -1350,6 +1353,7 @@ int how; Schroedingers_cat = odds_and_ends(invent, CAT_CHECK); if (Schroedingers_cat) { int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]); + mhp = d(m_lev, 8); nowrap_add(u.urexp, mhp); Strcat(eos(pbuf), " and Schroedinger's cat"); diff --git a/src/invent.c b/src/invent.c index 429b5a9d5..edacfc547 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2180,6 +2180,7 @@ const char *query; boolean want_reply; long *out_cnt; { + static const char not_carrying_anything[] = "Not carrying anything"; struct obj *otmp; char ilet, ret; char *invlet = flags.inv_order; @@ -2226,7 +2227,7 @@ long *out_cnt; ++n; if (n == 0) { - pline("Not carrying anything."); + pline("%s.", not_carrying_anything); return 0; } @@ -2314,14 +2315,24 @@ nextclass: goto nextclass; } } - if (iflags.force_invmenu && lets && want_reply) { any = zeroany; - add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "Special", MENU_UNSELECTED); + add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + "Special", MENU_UNSELECTED); any.a_char = '*'; - add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, "(list everything)", MENU_UNSELECTED); + add_menu(win, NO_GLYPH, &any, '*', 0, ATR_NONE, + "(list everything)", MENU_UNSELECTED); + } + /* for permanent inventory where we intend to show everything but + nothing has been listed (because there isn't anyhing to list; + recognized via any.a_char still being zero; the n==0 case above + gets skipped for perm_invent), put something into the menu */ + if (flags.perm_invent && !lets && !any.a_char) { + any = zeroany; + add_menu(win, NO_GLYPH, &any, 0, 0, 0, + not_carrying_anything, MENU_UNSELECTED); + want_reply = FALSE; } - end_menu(win, query && *query ? query : (char *) 0); n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected); From fe9762d1cc82b30d7ff001128c49460a92f16d2c Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 31 Oct 2017 21:18:21 +0200 Subject: [PATCH 16/25] X11: Fix renaming at player selection Due to the new player selection dialog I did, it was possible to rename your character - but this didn't rename the lock files and tried to load a save from the wrong name. This is a bit of a hack, but seems to work and didn't seem to cause problems for the tty. --- include/flag.h | 1 + src/role.c | 4 ++-- sys/unix/hints/linux-x11 | 2 +- sys/unix/unixmain.c | 42 +++++++++++++++++++++++++--------------- win/X11/winX.c | 9 +-------- win/X11/winmisc.c | 16 +++++++++++++++ 6 files changed, 47 insertions(+), 27 deletions(-) diff --git a/include/flag.h b/include/flag.h index 3bccbaa18..81630bd27 100644 --- a/include/flag.h +++ b/include/flag.h @@ -191,6 +191,7 @@ struct instance_flags { * behaviour of various NetHack functions and probably warrant * a structure of their own elsewhere some day. */ + boolean defer_plname; /* X11 hack: askname() might not set plname */ boolean herecmd_menu; /* use menu when mouseclick on yourself */ boolean invis_goldsym; /* gold symbol is ' '? */ int parse_config_file_src; /* hack for parse_config_line() */ diff --git a/src/role.c b/src/role.c index 5a1a4ab56..fd23f3adb 100644 --- a/src/role.c +++ b/src/role.c @@ -1714,7 +1714,7 @@ plnamesuffix() do { if (!*plname) - askname(); /* fill plname[] if necessary */ + askname(); /* fill plname[] if necessary, or set defer_plname */ /* Look for tokens delimited by '-' */ if ((eptr = index(plname, '-')) != (char *) 0) @@ -1735,7 +1735,7 @@ plnamesuffix() else if ((i = str2align(sptr)) != ROLE_NONE) flags.initalign = i; } - } while (!*plname); + } while (!*plname && !iflags.defer_plname); /* commas in the plname confuse the record file, convert to spaces */ for (sptr = plname; *sptr; sptr++) { diff --git a/sys/unix/hints/linux-x11 b/sys/unix/hints/linux-x11 index a0d5552e2..05d7e7304 100644 --- a/sys/unix/hints/linux-x11 +++ b/sys/unix/hints/linux-x11 @@ -20,7 +20,7 @@ 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=-g -O -I../include -DNOTPARMDECL CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" CFLAGS+=-DX11_GRAPHICS -DDEFAULT_WINDOW_SYS=\"X11\" -DNOTTYGRAPHICS diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 2e8f41330..b9ab44526 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -55,6 +55,7 @@ char *argv[]; #endif boolean exact_username; boolean resuming = FALSE; /* assume new game */ + boolean plsel_once = FALSE; sys_early_init(); @@ -237,19 +238,6 @@ char *argv[]; (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGINT, SIG_IGN); } - /* - * getlock() complains and quits if there is already a game - * in progress for current character name (when locknum == 0) - * or if there are too many active games (when locknum > 0). - * When proceeding, it creates an empty .0 file to - * designate the current game. - * getlock() constructs based on the character - * name (for !locknum) or on first available of alock, block, - * clock, &c not currently in use in the playground directory - * (for locknum > 0). - */ - getlock(); - program_state.preserve_locks = 0; /* after getlock() */ dlb_init(); /* must be before newgame() */ @@ -266,7 +254,24 @@ char *argv[]; * We'll return here if new game player_selection() renames the hero. */ attempt_restore: - if ((fd = restore_saved_game()) >= 0) { + + /* + * getlock() complains and quits if there is already a game + * in progress for current character name (when locknum == 0) + * or if there are too many active games (when locknum > 0). + * When proceeding, it creates an empty .0 file to + * designate the current game. + * getlock() constructs based on the character + * name (for !locknum) or on first available of alock, block, + * clock, &c not currently in use in the playground directory + * (for locknum > 0). + */ + if (*plname) { + getlock(); + program_state.preserve_locks = 0; /* after getlock() */ + } + + if (*plname && (fd = restore_saved_game()) >= 0) { const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1); (void) chmod(fq_save, 0); /* disallow parallel restores */ @@ -297,12 +302,17 @@ attempt_restore: } if (!resuming) { + boolean neednewlock = (!*plname); /* new game: start by choosing role, race, etc; player might change the hero's name while doing that, in which case we try to restore under the new name and skip selection this time if that didn't succeed */ - if (!iflags.renameinprogress) { - player_selection(); + if (!iflags.renameinprogress || iflags.defer_plname || neednewlock) { + if (!plsel_once) + player_selection(); + plsel_once = TRUE; + if (neednewlock && *plname) + goto attempt_restore; if (iflags.renameinprogress) { /* player has renamed the hero while selecting role; if locking alphabetically, the existing lock file diff --git a/win/X11/winX.c b/win/X11/winX.c index ff9b0ff51..6b285a2e5 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1474,18 +1474,11 @@ X11_askname() { Widget popup, dialog; Arg args[1]; - char *defplname = (char *)0; - -#ifdef UNIX - defplname = get_login_name(); -#endif - (void) strncpy(plname, defplname ? defplname : "Mumbles", - sizeof plname - 1); - plname[sizeof plname - 1] = '\0'; if (iflags.wc_player_selection == VIA_DIALOG) { /* X11_player_selection_dialog() handles name query */ plsel_ask_name = TRUE; + iflags.defer_plname = TRUE; return; } /* else iflags.wc_player_selection == VIA_PROMPTS */ diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index 8ddb62a79..945e35fc4 100644 --- a/win/X11/winmisc.c +++ b/win/X11/winmisc.c @@ -352,8 +352,13 @@ plsel_dialog_acceptvalues() XtSetArg(args[0], nhStr(XtNstring), &s); XtGetValues(plsel_name_input, args, ONE); + (void) strncpy(plname, (char *) s, sizeof plname - 1); plname[sizeof plname - 1] = '\0'; + (void) mungspaces(plname); + if (strlen(plname) < 1) + (void) strcpy(plname, "Mumbles"); + iflags.renameinprogress = FALSE; } /* ARGSUSED */ @@ -1553,6 +1558,17 @@ void X11_player_selection() { if (iflags.wc_player_selection == VIA_DIALOG) { + if (!*plname) { +#ifdef UNIX + char *defplname = get_login_name(); +#else + char *defplname = (char *)0; +#endif + (void) strncpy(plname, defplname ? defplname : "Mumbles", + sizeof plname - 1); + plname[sizeof plname - 1] = '\0'; + iflags.renameinprogress = TRUE; + } X11_player_selection_dialog(); } else { /* iflags.wc_player_selection == VIA_PROMPTS */ X11_player_selection_prompts(); From c50a93b5e046b88172420fa9298a25fbe769e7d8 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Tue, 31 Oct 2017 22:30:07 +0200 Subject: [PATCH 17/25] Charisma affects the leeway in demon lord bribes --- doc/fixes36.1 | 1 + src/minion.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index c36858045..bb535b631 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -467,6 +467,7 @@ doors in special levels were always generated in vertical orientation assigning a type name to a potion on the floor which is actually a mimic could prompt "Call a stream of fluid:" (bogus 'fromsink') with perm_invent option enabled and no inventory, 'i' put up an empty menu +charisma affects the leeway in demon lord bribes Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/minion.c b/src/minion.c index d0394477b..e690d22d1 100644 --- a/src/minion.c +++ b/src/minion.c @@ -279,7 +279,8 @@ register struct monst *mtmp; if ((offer = bribe(mtmp)) >= demand) { pline("%s vanishes, laughing about cowardly mortals.", Amonnam(mtmp)); - } else if (offer > 0L && (long) rnd(40) > (demand - offer)) { + } else if (offer > 0L + && (long) rnd(5 * ACURR(A_CHA)) > (demand - offer)) { pline("%s scowls at you menacingly, then vanishes.", Amonnam(mtmp)); } else { From 001d9681c23107b570e943ba168e42e524d55737 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 1 Nov 2017 00:07:24 +0200 Subject: [PATCH 18/25] Make Vlad slightly tougher Increase speed from 18 to 26, HD from 14 to 28, AC from -3 to -6, weapon attack from 1d10 to 2d10, bite from 1d10 to 1d12 --- doc/fixes36.1 | 1 + src/monst.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index bb535b631..c56c2ffcc 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -468,6 +468,7 @@ assigning a type name to a potion on the floor which is actually a mimic could prompt "Call a stream of fluid:" (bogus 'fromsink') with perm_invent option enabled and no inventory, 'i' put up an empty menu charisma affects the leeway in demon lord bribes +make Vlad slightly tougher Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/monst.c b/src/monst.c index 2bd2b99ed..18178bf0a 100644 --- a/src/monst.c +++ b/src/monst.c @@ -1870,9 +1870,9 @@ struct permonst _mons2[] = { | M2_MALE | M2_MAGIC | M2_SHAPESHIFTER, M3_INFRAVISIBLE, HI_ZAP), #endif - MON("Vlad the Impaler", S_VAMPIRE, LVL(14, 18, -3, 80, -10), + MON("Vlad the Impaler", S_VAMPIRE, LVL(28, 26, -6, 80, -10), (G_NOGEN | G_NOCORPSE | G_UNIQ), - A(ATTK(AT_WEAP, AD_PHYS, 1, 10), ATTK(AT_BITE, AD_DRLI, 1, 10), + A(ATTK(AT_WEAP, AD_PHYS, 2, 10), ATTK(AT_BITE, AD_DRLI, 1, 12), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(WT_HUMAN, 400, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP | MR_POISON, 0, M1_FLY | M1_BREATHLESS | M1_HUMANOID | M1_POIS | M1_REGEN, From f19752fc32d9b343f35f6b0abc33ef3c158b9abb Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 1 Nov 2017 12:12:17 +0200 Subject: [PATCH 19/25] Reduce the amount of gold laying on the floor There's far too much gold just laying around on the floor. Didn't previous adventurers grab most of it? This should incentivize gold detection and digging out vaults, selling stuff to the shopkeeps, and making it harder to donate for protection. Most radical reduction on the first few levels, for dlevel 1, average amount of gold was 80, is now 10, for dlevel 2, 95->15 Does not change the amount of gold deposited by mineralize, in the vaults, or contained in chests and large boxes. --- doc/fixes36.1 | 1 + src/mkobj.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index c56c2ffcc..b5fc58221 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -469,6 +469,7 @@ assigning a type name to a potion on the floor which is actually a mimic could with perm_invent option enabled and no inventory, 'i' put up an empty menu charisma affects the leeway in demon lord bribes make Vlad slightly tougher +reduce the amount of gold laying on the floor Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mkobj.c b/src/mkobj.c index 43d62b6c2..592633cac 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1418,8 +1418,10 @@ int x, y; { register struct obj *gold = g_at(x, y); - if (amount <= 0L) - amount = (long) (1 + rnd(level_difficulty() + 2) * rnd(30)); + if (amount <= 0L) { + long mul = rnd(30 / max(12-depth(&u.uz), 2)); + amount = (long) (1 + rnd(level_difficulty() + 2) * mul); + } if (gold) { gold->quan += amount; } else { From 089863088e27d519fc16db601678d501700a7a69 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 1 Nov 2017 15:38:44 +0200 Subject: [PATCH 20/25] Locked chests and large boxes contain more items --- doc/fixes36.1 | 1 + src/mkobj.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index b5fc58221..04d4f53a1 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -470,6 +470,7 @@ with perm_invent option enabled and no inventory, 'i' put up an empty menu charisma affects the leeway in demon lord bribes make Vlad slightly tougher reduce the amount of gold laying on the floor +locked chests and large boxes contain more stuff Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mkobj.c b/src/mkobj.c index 592633cac..ed59caa9a 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -266,10 +266,10 @@ struct obj *box; n = 20; break; case CHEST: - n = 5; + n = box->olocked ? 7 : 5; break; case LARGE_BOX: - n = 3; + n = box->olocked ? 5 : 3; break; case SACK: case OILSKIN_SACK: From f2765c573fd1d965736310c663b1bbbf933154c8 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 1 Nov 2017 16:13:51 +0200 Subject: [PATCH 21/25] Document therecmdmenu --- doc/Guidebook.mn | 7 +++++-- doc/Guidebook.tex | 11 +++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index b7b2258cf..70b439138 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1126,6 +1126,8 @@ Teleport around the level. Default key is '^T'. .lp #terrain Show bare map without displaying monsters, objects, or traps. Autocompletes. +.lp #therecmdmenu +Show a menu of possible actions in a location next to you. .lp #throw Throw something. Default key is 't'. .lp #timeout @@ -2559,8 +2561,9 @@ off makes just looking at things faster, since you aren't interrupted with the ``More info?'' prompt, but it also means that you might miss some interesting and/or important information. Persistent. .lp herecmd_menu -When using a windowport that supports mouse and clicking on yourself, show -a menu of possible actions for this location. Same as herecmdmenu command. +When using a windowport that supports mouse and clicking on yourself or +next to you, show a menu of possible actions for the location. +Same as herecmdmenu and therecmdmenu commands. .lp hilite_pet Visually distinguish pets from similar animals (default off). The behavior of this option depends on the type of windowing you use. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 66610799e..6612ae19c 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1186,6 +1186,9 @@ Show what type of thing a map symbol corresponds to. Default key is '{\tt ;}'. \item[\tb{\#help}] Show the help menu. Default key is '{\tt ?}', and '{\tt h}' if {\it number\verb+_+pad\/} is on. %.lp +\item[\tb{\#herecmdmenu}] +Show a menu of possible actions in your current location. +%.lp \item[\tb{\#history}] Show long version and game history. Default key is '{\tt V}'. %.lp @@ -1386,6 +1389,9 @@ Teleport around the level. Default key is '{\tt \^{}T}'. Show bare map without displaying monsters, objects, or traps. Autocompletes. %.lp +\item[\tb{\#therecmdmenu}] +Show a menu of possible actions in a location next to you. +%.lp \item[\tb{\#throw}] Throw something. Default key is '{\tt t}'. %.lp @@ -3125,8 +3131,9 @@ interrupted with the ``{\tt More info?}'' prompt, but it also means that you might miss some interesting and/or important information. Persistent. %.lp \item[\ib{herecmd\verb+_+menu}] -When using a windowport that supports mouse and clicking on yourself, show -a menu of possible actions for this location. Same as herecmdmenu command. +When using a windowport that supports mouse and clicking on yourself or +next to you, show a menu of possible actions for the location. +Same as herecmdmenu and therecmdmenu commands. %.lp \item[\ib{hilite\verb+_+pet}] Visually distinguish pets from similar animals (default off). From e4db58bdf3376eddf9ad6416c0664975e7787035 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 1 Nov 2017 14:55:40 +0000 Subject: [PATCH 22/25] It's OK for pets to oscillate near the player This change was coded by FIQ, who suggested it by email. The main change here is related to monster anti-oscillation code. When a monster believes it's stuck in an AI loop, it looks for an alternative strategy. That applies to pets too. However, if the pet is currently near the player, we can typically assume that it's there because it wants to be there, and an oscillation is not because it's stuck but because it's already in the best possible place. This commit causes pets to be "allowed" to stay near the player, rather than running the wrong way down a corridor because it's the only way to do something different than what they're currently doing. If the pet is far from the player, we use the old behaviour unless the pet is leashed or the player tried to call it with a whistle or the like, in order to avoid the risk of a genuine AI loop trying to get back to the player. (Whistling happens rarely enough that it won't cause AI loops of its own - the player isn't going to whistle every turn - and it makes flavour sense that a pet might interpret it as "you're going in the wrong direction!".) --- src/dogmove.c | 17 ++++++++++++----- src/mon.c | 9 +++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/dogmove.c b/src/dogmove.c index 5a385b6aa..200779ea3 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -1086,11 +1086,18 @@ int after; /* this is extra fast monster movement */ continue; /* lessen the chance of backtracking to previous position(s) */ - k = has_edog ? uncursedcnt : cnt; - for (j = 0; j < MTSZ && j < k - 1; j++) - if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) - if (rn2(MTSZ * (k - j))) - goto nxti; + /* This causes unintended issues for pets trying to follow + the hero. Thus, only run it if not leashed and >5 tiles + away. */ + if (!mtmp->mleashed && + distmin(mtmp->mx, mtmp->my, u.ux, u.uy) > 5) { + k = has_edog ? uncursedcnt : cnt; + for (j = 0; j < MTSZ && j < k - 1; j++) + if (nx == mtmp->mtrack[j].x && + ny == mtmp->mtrack[j].y) + if (rn2(MTSZ * (k - j))) + goto nxti; + } j = ((ndist = GDIST(nx, ny)) - nidist) * appr; if ((j == 0 && !rn2(++chcnt)) || j < 0 diff --git a/src/mon.c b/src/mon.c index d01a79c95..a872b93a9 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2787,8 +2787,13 @@ wake_nearby() mtmp->msleeping = 0; if (!unique_corpstat(mtmp->data)) mtmp->mstrategy &= ~STRAT_WAITMASK; - if (mtmp->mtame && !mtmp->isminion) - EDOG(mtmp)->whistletime = moves; + if (mtmp->mtame) { + if (!mtmp->isminion) + EDOG(mtmp)->whistletime = moves; + /* Clear mtrack. This is to fix up a pet who is + stuck "fleeing" its master. */ + memset(mtmp->mtrack, 0, sizeof(mtmp->mtrack)); + } } } } From 27e5f025f12c4e0cdde6f023cd63ca10b75252d8 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 1 Nov 2017 15:22:28 +0000 Subject: [PATCH 23/25] Don't use a plain %s when writing to a buffer My compiler was understandably concerned about a potential buffer overflow here. I don't think the string could get long enough to cause that to happen, but it's hard to be certain. It's much safer to limit the length of the string so that it fits in the buffer, as done here, and if there really wasn't a problem the change will cause no harm at all. (If there was, the string will be truncated rather than corrupting memory. This code is in showing the config-file version of a status highlight, something where truncated text will probably be obvious to the user.) --- src/botl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/botl.c b/src/botl.c index 7b06dec87..f0ee33fd0 100644 --- a/src/botl.c +++ b/src/botl.c @@ -3163,7 +3163,9 @@ status_hilites_viewall() datawin = create_nhwindow(NHW_TEXT); while (hlstr) { - Sprintf(buf, "OPTIONS=hilite_status: %s", hlstr->str); + Sprintf(buf, "OPTIONS=hilite_status: %.*s", + (int)(BUFSZ - sizeof "OPTIONS=hilite_status: " - 1), + hlstr->str); putstr(datawin, 0, buf); hlstr = hlstr->next; } From f3814417e9f5f5bc74c0461552717e81080370ac Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 2 Nov 2017 13:37:53 +0200 Subject: [PATCH 24/25] Use enums for properties --- include/artifact.h | 20 +++--- include/prop.h | 148 +++++++++++++++++++++++---------------------- 2 files changed, 86 insertions(+), 82 deletions(-) diff --git a/include/artifact.h b/include/artifact.h index 4b7f55d6d..32261fdfd 100644 --- a/include/artifact.h +++ b/include/artifact.h @@ -56,14 +56,16 @@ struct artifact { }; /* invoked properties with special powers */ -#define TAMING (LAST_PROP + 1) -#define HEALING (LAST_PROP + 2) -#define ENERGY_BOOST (LAST_PROP + 3) -#define UNTRAP (LAST_PROP + 4) -#define CHARGE_OBJ (LAST_PROP + 5) -#define LEV_TELE (LAST_PROP + 6) -#define CREATE_PORTAL (LAST_PROP + 7) -#define ENLIGHTENING (LAST_PROP + 8) -#define CREATE_AMMO (LAST_PROP + 9) +enum invoke_prop_types { + TAMING = (LAST_PROP + 1), + HEALING, + ENERGY_BOOST, + UNTRAP, + CHARGE_OBJ, + LEV_TELE, + CREATE_PORTAL, + ENLIGHTENING, + CREATE_AMMO +}; #endif /* ARTIFACT_H */ diff --git a/include/prop.h b/include/prop.h index 3bbc7182f..e9f1fc0c0 100644 --- a/include/prop.h +++ b/include/prop.h @@ -11,79 +11,81 @@ * Property #0 is not used. */ /* Resistances to troubles */ -#define FIRE_RES 1 -#define COLD_RES 2 -#define SLEEP_RES 3 -#define DISINT_RES 4 -#define SHOCK_RES 5 -#define POISON_RES 6 -#define ACID_RES 7 -#define STONE_RES 8 -/* note: for the first eight properties, MR_xxx == (1 << (xxx_RES - 1)) */ -#define DRAIN_RES 9 -#define SICK_RES 10 -#define INVULNERABLE 11 -#define ANTIMAGIC 12 -/* Troubles */ -#define STUNNED 13 -#define CONFUSION 14 -#define BLINDED 15 -#define DEAF 16 -#define SICK 17 -#define STONED 18 -#define STRANGLED 19 -#define VOMITING 20 -#define GLIB 21 -#define SLIMED 22 -#define HALLUC 23 -#define HALLUC_RES 24 -#define FUMBLING 25 -#define WOUNDED_LEGS 26 -#define SLEEPY 27 -#define HUNGER 28 -/* Vision and senses */ -#define SEE_INVIS 29 -#define TELEPAT 30 -#define WARNING 31 -#define WARN_OF_MON 32 -#define WARN_UNDEAD 33 -#define SEARCHING 34 -#define CLAIRVOYANT 35 -#define INFRAVISION 36 -#define DETECT_MONSTERS 37 -/* Appearance and behavior */ -#define ADORNED 38 -#define INVIS 39 -#define DISPLACED 40 -#define STEALTH 41 -#define AGGRAVATE_MONSTER 42 -#define CONFLICT 43 -/* Transportation */ -#define JUMPING 44 -#define TELEPORT 45 -#define TELEPORT_CONTROL 46 -#define LEVITATION 47 -#define FLYING 48 -#define WWALKING 49 -#define SWIMMING 50 -#define MAGICAL_BREATHING 51 -#define PASSES_WALLS 52 -/* Physical attributes */ -#define SLOW_DIGESTION 53 -#define HALF_SPDAM 54 -#define HALF_PHDAM 55 -#define REGENERATION 56 -#define ENERGY_REGENERATION 57 -#define PROTECTION 58 -#define PROT_FROM_SHAPE_CHANGERS 59 -#define POLYMORPH 60 -#define POLYMORPH_CONTROL 61 -#define UNCHANGING 62 -#define FAST 63 -#define REFLECTING 64 -#define FREE_ACTION 65 -#define FIXED_ABIL 66 -#define LIFESAVED 67 +enum prop_types { + FIRE_RES = 1, + COLD_RES, + SLEEP_RES, + DISINT_RES, + SHOCK_RES, + POISON_RES, + ACID_RES, + STONE_RES, + /* note: for the first eight properties, MR_xxx == (1 << (xxx_RES - 1)) */ + DRAIN_RES, + SICK_RES, + INVULNERABLE, + ANTIMAGIC, + /* Troubles */ + STUNNED, + CONFUSION, + BLINDED, + DEAF, + SICK, + STONED, + STRANGLED, + VOMITING, + GLIB, + SLIMED, + HALLUC, + HALLUC_RES, + FUMBLING, + WOUNDED_LEGS, + SLEEPY, + HUNGER, + /* Vision and senses */ + SEE_INVIS, + TELEPAT, + WARNING, + WARN_OF_MON, + WARN_UNDEAD, + SEARCHING, + CLAIRVOYANT, + INFRAVISION, + DETECT_MONSTERS, + /* Appearance and behavior */ + ADORNED, + INVIS, + DISPLACED, + STEALTH, + AGGRAVATE_MONSTER, + CONFLICT, + /* Transportation */ + JUMPING, + TELEPORT, + TELEPORT_CONTROL, + LEVITATION, + FLYING, + WWALKING, + SWIMMING, + MAGICAL_BREATHING, + PASSES_WALLS, + /* Physical attributes */ + SLOW_DIGESTION, + HALF_SPDAM, + HALF_PHDAM, + REGENERATION, + ENERGY_REGENERATION, + PROTECTION, + PROT_FROM_SHAPE_CHANGERS, + POLYMORPH, + POLYMORPH_CONTROL, + UNCHANGING, + FAST, + REFLECTING, + FREE_ACTION, + FIXED_ABIL, + LIFESAVED +}; #define LAST_PROP (LIFESAVED) /*** Where the properties come from ***/ From 647ea55b1ac02f10ab88e4956fd8630d2503261d Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 2 Nov 2017 23:41:51 +0200 Subject: [PATCH 25/25] Fix compile of tile2x11 I was too zealous changing fprintf to the Fprintf macro, which ignores the return value. --- win/X11/tile2x11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/X11/tile2x11.c b/win/X11/tile2x11.c index a51cb75cd..cf2d1ffe3 100644 --- a/win/X11/tile2x11.c +++ b/win/X11/tile2x11.c @@ -157,7 +157,7 @@ FILE *fp; Fprintf(fp, "\",\n"); } - return Fprintf(fp, "};\n") >= 0; + return fprintf(fp, "};\n") >= 0; } #endif /* USE_XPM */