diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 0d934b465..b7b2258cf 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -982,6 +982,8 @@ Force a lock. Autocompletes. Default key is 'M-f'. Show what type of thing a map symbol corresponds to. Default key is ';'. .lp #help Show the help menu. Default key is '?', and 'h' if number_pad is on. +.lp #herecmdmenu +Show a menu of possible actions in your current location. .lp #history Show long version and game history. Default key is 'V'. .lp #inventory @@ -2556,6 +2558,9 @@ with the `/' command, ask if you want to see it (default on). Turning help 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. .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 9119ac17d..66610799e 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3124,6 +3124,10 @@ Turning help off makes just looking at things faster, since you aren't 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. +%.lp \item[\ib{hilite\verb+_+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/fixes36.1 b/doc/fixes36.1 index e6f3e7c78..9c74120fd 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -451,6 +451,8 @@ give feedback when released from a bear trap depending upon how the dynamically inserted pattern-match phrase fit #version output left out "pattern matching via " if the basic NetHack features entry was split across two lines +recovery of strength lost due to weakness from hunger was vulnerable to abuse +monsters hit by scroll of earth boulder did not get angry Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository @@ -508,6 +510,8 @@ hero poly'd into vampire could drain monster down to 0 HP without killing it, "you observe a fog cloud where a vampire/bat was" if an unseen vampire on the far side of a closed door shifted shape to pass under that door 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 Platform- and/or Interface-Specific Fixes @@ -684,6 +688,8 @@ Master Key of Thievery always finds door and chest traps if used to lock or blessed (for non-rogues); player is offered the opportunity to disarm "Elbereth" must now be the only engraved text on a square to function "Elbereth" now erodes based on attacks by the player, not monsters scared +option herecmd_menu to make a mouse click on your character pop up + a context menu, and extended command #herecmdmenu to do the same Platform- and/or Interface-Specific New Features @@ -703,6 +709,8 @@ X11: status display split into three columns to accomodate Stone/Deaf/Lev/&c; resources replaced by status_condition[1-3].* 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 NetHack Community Patches (or Variation) Included diff --git a/include/extern.h b/include/extern.h index 203e51076..599a69d95 100644 --- a/include/extern.h +++ b/include/extern.h @@ -536,11 +536,11 @@ E int FDECL(thitmonst, (struct monst *, struct obj *)); E int FDECL(hero_breaks, (struct obj *, XCHAR_P, XCHAR_P, BOOLEAN_P)); E int FDECL(breaks, (struct obj *, XCHAR_P, XCHAR_P)); E void FDECL(release_camera_demon, (struct obj *, XCHAR_P, XCHAR_P)); -E void FDECL(breakobj, - (struct obj *, XCHAR_P, XCHAR_P, BOOLEAN_P, BOOLEAN_P)); +E void FDECL(breakobj, (struct obj *, XCHAR_P, XCHAR_P, BOOLEAN_P, BOOLEAN_P)); E boolean FDECL(breaktest, (struct obj *)); E boolean FDECL(walk_path, (coord *, coord *, boolean (*)(genericptr, int, int), genericptr_t)); +E boolean FDECL(hurtle_jump, (genericptr_t, int, int)); E boolean FDECL(hurtle_step, (genericptr_t, int, int)); /* ### drawing.c ### */ @@ -2487,6 +2487,7 @@ E void NDECL(port_help); E void FDECL(sethanguphandler, (void (*)(int))); E boolean NDECL(authorize_wizard_mode); E boolean FDECL(check_user_string, (char *)); +E char *NDECL(get_login_name); #endif /* UNIX */ /* ### unixtty.c ### */ diff --git a/include/flag.h b/include/flag.h index 415688a92..5b205e5b7 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 herecmd_menu; /* use menu when mouseclick on yourself */ boolean invis_goldsym; /* gold symbol is ' '? */ int parse_config_file_src; /* hack for parse_config_line() */ int in_lava_effects; /* hack for Boots_off() */ diff --git a/include/qt_xpms.h b/include/qt_xpms.h index a91ac5f11..5667c13c0 100644 --- a/include/qt_xpms.h +++ b/include/qt_xpms.h @@ -946,6 +946,20 @@ static const char *pet_mark_small_xpm[] = { "..X.." }; /* XPM */ +static const char *pile_mark_xpm[] = { +/* width height ncolors chars_per_pixel */ +"5 5 2 1", +/* colors */ +". c None", +"X c #00FF00", +/* pixels */ +"..X..", +"..X..", +"XXXXX", +"..X..", +"..X.." +}; +/* XPM */ static const char *satiated_xpm[] = { /* width height ncolors chars_per_pixel */ "40 40 23 1", diff --git a/include/winX.h b/include/winX.h index 796121405..6e87aa62b 100644 --- a/include/winX.h +++ b/include/winX.h @@ -242,6 +242,7 @@ E boolean exit_x_event; /* exit condition for event loop */ #define EXIT_ON_EXIT 2 #define EXIT_ON_SENT_EVENT 3 E int click_x, click_y, click_button, updated_inventory; +E boolean plsel_ask_name; typedef struct { Boolean slow; /* issue prompts between map and message wins */ @@ -334,6 +335,12 @@ E void FDECL(algn_key, E void FDECL(ec_delete, (Widget, XEvent *, String *, Cardinal *)); E void FDECL(ec_key, (Widget, XEvent *, String *, Cardinal *)); /* extended command action */ +E void FDECL(plsel_quit, (Widget, XEvent *, String *, + Cardinal *)); /* player selection dialog */ +E void FDECL(plsel_play, (Widget, XEvent *, String *, + Cardinal *)); /* player selection dialog */ +E void FDECL(plsel_randomize, (Widget, XEvent *, String *, + Cardinal *)); /* player selection dialog */ E void NDECL(release_extended_cmds); /* ### winstatus.c ### */ diff --git a/include/wintype.h b/include/wintype.h index 3dbcc0500..3bb82347d 100644 --- a/include/wintype.h +++ b/include/wintype.h @@ -15,6 +15,7 @@ typedef union any { int a_int; char a_char; schar a_schar; + uchar a_uchar; unsigned int a_uint; long a_long; unsigned long a_ulong; @@ -23,6 +24,8 @@ typedef union any { unsigned long *a_ulptr; unsigned *a_uptr; const char *a_string; + int NDECL((*a_nfunc)); + unsigned long a_mask32; /* used by status highlighting */ /* add types as needed */ } anything; #define ANY_P union any /* avoid typedef in prototypes */ @@ -45,6 +48,7 @@ enum any_types { ANY_LPTR, /* pointer to long */ ANY_ULPTR, /* pointer to unsigned long */ ANY_STR, /* pointer to null-terminated char string */ + ANY_NFUNC, /* pointer to function taking no args, returning int */ ANY_MASK32 /* 32-bit mask (stored as unsigned long) */ }; diff --git a/include/youprop.h b/include/youprop.h index 79fa70a78..d71ec8b5c 100644 --- a/include/youprop.h +++ b/include/youprop.h @@ -227,7 +227,8 @@ && !BFlying) /* May touch surface; does not override any others */ -#define Wwalking (u.uprops[WWALKING].extrinsic && !Is_waterlevel(&u.uz)) +#define EWwalking u.uprops[WWALKING].extrinsic +#define Wwalking (EWwalking && !Is_waterlevel(&u.uz)) /* Don't get wet, can't go under water; overrides others except levitation */ /* Wwalking is meaningless on water level */ diff --git a/src/apply.c b/src/apply.c index e5df1b904..ca72cc466 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1769,10 +1769,10 @@ int magic; /* 0=Physical, otherwise skill level */ temp = -temp; if (range < temp) range = temp; - (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t) &range); - /* hurtle_step results in (u.ux, u.uy) == (cc.x, cc.y) and usually - * moves the ball if punished, but does not handle all the effects - * of landing on the final position. + (void) walk_path(&uc, &cc, hurtle_jump, (genericptr_t) &range); + /* hurtle_jump -> hurtle_step results in == + * and usually moves the ball if punished, but does not handle all + * the effects of landing on the final position. */ teleds(cc.x, cc.y, FALSE); sokoban_guilt(); @@ -1951,9 +1951,8 @@ struct obj *obj; if (ABASE(idx) >= AMAX(idx)) continue; val_limit = AMAX(idx); - /* don't recover strength lost from hunger */ - if (idx == A_STR && u.uhs >= WEAK) - val_limit--; + /* this used to adjust 'val_limit' for A_STR when u.uhs was + WEAK or worse, but that's handled via ATEMP(A_STR) now */ if (Fixed_abil) { /* potion/spell of restore ability override sustain ability intrinsic but unicorn horn usage doesn't */ diff --git a/src/attrib.c b/src/attrib.c index e0021632f..a3812d54b 100644 --- a/src/attrib.c +++ b/src/attrib.c @@ -366,20 +366,27 @@ set_moreluck() void restore_attrib() { - int i; + int i, equilibrium;; + + /* + * Note: this gets called on every turn but ATIME() is never set + * to non-zero anywhere, and ATEMP() is only used for strength loss + * from hunger, so it doesn't actually do anything. + */ for (i = 0; i < A_MAX; i++) { /* all temporary losses/gains */ - - if (ATEMP(i) && ATIME(i)) { + equilibrium = (i == A_STR && u.uhs >= WEAK) ? -1 : 0; + if (ATEMP(i) != equilibrium && ATIME(i) != 0) { if (!(--(ATIME(i)))) { /* countdown for change */ - ATEMP(i) += ATEMP(i) > 0 ? -1 : 1; - + ATEMP(i) += (ATEMP(i) > 0) ? -1 : 1; + context.botl = 1; if (ATEMP(i)) /* reset timer */ ATIME(i) = 100 / ACURR(A_CON); } } } - (void) encumber_msg(); + if (context.botl) + (void) encumber_msg(); } #define AVAL 50 /* tune value for exercise gains */ diff --git a/src/cmd.c b/src/cmd.c index add6ebd52..27e05f4d8 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -114,6 +114,8 @@ static int NDECL(dosuspend_core); /**/ static int NDECL((*timed_occ_fn)); +STATIC_PTR int NDECL(doherecmdmenu); +STATIC_PTR int NDECL(dotherecmdmenu); STATIC_PTR int NDECL(doprev_message); STATIC_PTR int NDECL(timed_occupation); STATIC_PTR int NDECL(doextcmd); @@ -181,6 +183,10 @@ STATIC_DCL void FDECL(attributes_enlightenment, (int, int)); static const char *readchar_queue = ""; static coord clicklook_cc; +STATIC_DCL void FDECL(add_herecmd_menuitem, (winid, int NDECL((*)), + const char *)); +STATIC_DCL char FDECL(here_cmd_menu, (BOOLEAN_P)); +STATIC_DCL char FDECL(there_cmd_menu, (BOOLEAN_P, int, int)); STATIC_DCL char *NDECL(parse); STATIC_DCL void FDECL(show_direction_keys, (winid, CHAR_P, BOOLEAN_P)); STATIC_DCL boolean FDECL(help_dir, (CHAR_P, int, const char *)); @@ -2899,6 +2905,8 @@ struct ext_func_tab extcmdlist[] = { { ';', "glance", "show what type of thing a map symbol corresponds to", doquickwhatis, IFBURIED | GENERALCMD }, { '?', "help", "give a help message", dohelp, IFBURIED | GENERALCMD }, + { '\0', "herecmdmenu", "show menu of commands you can do here", + doherecmdmenu, IFBURIED }, { 'V', "history", "show long version and game history", dohistory, IFBURIED | GENERALCMD }, { 'i', "inventory", "show your inventory", ddoinv, IFBURIED }, @@ -2996,6 +3004,9 @@ struct ext_func_tab extcmdlist[] = { { C('t'), "teleport", "teleport around the level", dotele, IFBURIED }, { '\0', "terrain", "show map without obstructions", doterrain, IFBURIED | AUTOCOMPLETE }, + { '\0', "therecmdmenu", + "show 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", wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, @@ -4658,6 +4669,251 @@ register int x, y; return x >= 1 && x <= COLNO - 1 && y >= 0 && y <= ROWNO - 1; } +/* #herecmdmenu command */ +STATIC_PTR int +doherecmdmenu(VOID_ARGS) +{ + char ch = here_cmd_menu(TRUE); + + return ch ? 1 : 0; +} + +/* #therecmdmenu command, a way to test there_cmd_menu without mouse */ +STATIC_PTR int +dotherecmdmenu(VOID_ARGS) +{ + char ch; + + if (!getdir((const char *) 0) || !isok(u.ux + u.dx, u.uy + u.dy)) + return 0; + + if (u.dx || u.dy) + ch = there_cmd_menu(TRUE, u.ux + u.dx, u.uy + u.dy); + else + ch = here_cmd_menu(TRUE); + + return ch ? 1 : 0; +} + +STATIC_OVL void +add_herecmd_menuitem(win, func, text) +winid win; +int NDECL((*func)); +const char *text; +{ + char ch; + anything any; + + if ((ch = cmd_from_func(func)) != '\0') { + any = zeroany; + any.a_nfunc = func; + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, text, MENU_UNSELECTED); + } +} + +STATIC_OVL char +there_cmd_menu(doit, x, y) +boolean doit; +int x, y; +{ + winid win; + char ch; + char buf[BUFSZ]; + schar typ = levl[x][y].typ; + int npick, K = 0; + menu_item *picks = (menu_item *) 0; + struct trap *ttmp; + struct monst *mtmp; + + win = create_nhwindow(NHW_MENU); + start_menu(win); + + if (IS_DOOR(typ)) { + boolean key_or_pick, card; + int dm = levl[x][y].doormask; + + if ((dm & (D_CLOSED | D_LOCKED))) { + add_herecmd_menuitem(win, doopen, "Open the door"), ++K; + /* unfortunately there's no lknown flag for doors to + remember the locked/unlocked state */ + key_or_pick = (carrying(SKELETON_KEY) || carrying(LOCK_PICK)); + card = (carrying(CREDIT_CARD) != 0); + if (key_or_pick || card) { + Sprintf(buf, "%sunlock the door", + key_or_pick ? "lock or " : ""); + add_herecmd_menuitem(win, doapply, upstart(buf)), ++K; + } + /* unfortunately there's no tknown flag for doors (or chests) + to remember whether a trap had been found */ + add_herecmd_menuitem(win, dountrap, + "Search the door for a trap"), ++K; + /* [what about #force?] */ + add_herecmd_menuitem(win, dokick, "Kick the door"), ++K; + } else if ((dm & D_ISOPEN)) { + add_herecmd_menuitem(win, doclose, "Close the door"), ++K; + } + } + + if (typ <= SCORR) + add_herecmd_menuitem(win, dosearch, "Search for secret doors"), ++K; + + if ((ttmp = t_at(x, y)) != 0 && ttmp->tseen) { + add_herecmd_menuitem(win, doidtrap, "Examine trap"), ++K; + if (ttmp->ttyp != VIBRATING_SQUARE) + add_herecmd_menuitem(win, dountrap, "Attempt to disarm trap"), ++K; + } + + mtmp = m_at(x, y); + if (mtmp && !canspotmon(mtmp)) + mtmp = 0; + if (mtmp && which_armor(mtmp, W_SADDLE)) { + char *mnam = x_monnam(mtmp, ARTICLE_THE, (char *) 0, + SUPPRESS_SADDLE, FALSE); + + if (!u.usteed) { + Sprintf(buf, "Ride %s", mnam); + add_herecmd_menuitem(win, doride, buf), ++K; + } + Sprintf(buf, "Remove saddle from %s", mnam); + add_herecmd_menuitem(win, doloot, buf), ++K; + } + if (mtmp && can_saddle(mtmp) && !which_armor(mtmp, W_SADDLE) + && carrying(SADDLE)) { + Sprintf(buf, "Put saddle on %s", mon_nam(mtmp)), ++K; + add_herecmd_menuitem(win, doapply, buf); + } +#if 0 + if (mtmp || glyph_is_invisible(glyph_at(x, y))) { + /* "Attack %s", mtmp ? mon_nam(mtmp) : "unseen creature" */ + } else { + /* "Move %s", direction */ + } +#endif + + if (K) { + end_menu(win, "What do you want to do?"); + npick = select_menu(win, PICK_ONE, &picks); + } else { + pline("No applicable actions."); + npick = 0; + } + destroy_nhwindow(win); + ch = '\0'; + if (npick > 0) { + int NDECL((*func)) = picks->item.a_nfunc; + free((genericptr_t) picks); + + if (doit) { + int ret = (*func)(); + + ch = (char) ret; + } else { + ch = cmd_from_func(func); + } + } + return ch; +} + +STATIC_OVL char +here_cmd_menu(doit) +boolean doit; +{ + winid win; + char ch; + char buf[BUFSZ]; + schar typ = levl[u.ux][u.uy].typ; + int npick; + menu_item *picks = (menu_item *) 0; + + win = create_nhwindow(NHW_MENU); + start_menu(win); + + if (IS_FOUNTAIN(typ) || IS_SINK(typ)) { + Sprintf(buf, "Drink from the %s", + defsyms[IS_FOUNTAIN(typ) ? S_fountain : S_sink].explanation); + add_herecmd_menuitem(win, dodrink, buf); + } + if (IS_FOUNTAIN(typ)) + add_herecmd_menuitem(win, dodip, + "Dip something into the fountain"); + if (IS_THRONE(typ)) + add_herecmd_menuitem(win, dosit, + "Sit on the throne"); + + if ((u.ux == xupstair && u.uy == yupstair) + || (u.ux == sstairs.sx && u.uy == sstairs.sy && sstairs.up) + || (u.ux == xupladder && u.uy == yupladder)) { + Sprintf(buf, "Go up the %s", + (u.ux == xupladder && u.uy == yupladder) + ? "ladder" : "stairs"); + add_herecmd_menuitem(win, doup, buf); + } + if ((u.ux == xdnstair && u.uy == ydnstair) + || (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up) + || (u.ux == xdnladder && u.uy == ydnladder)) { + Sprintf(buf, "Go down the %s", + (u.ux == xupladder && u.uy == yupladder) + ? "ladder" : "stairs"); + add_herecmd_menuitem(win, dodown, buf); + } + if (u.usteed) { /* another movement choice */ + Sprintf(buf, "Dismount %s", + x_monnam(u.usteed, ARTICLE_THE, (char *) 0, + SUPPRESS_SADDLE, FALSE)); + add_herecmd_menuitem(win, doride, buf); + } + +#if 0 + if (Upolyd) { /* before objects */ + Sprintf(buf, "Use %s special ability", + s_suffix(mons[u.umonnum].mname)); + add_herecmd_menuitem(win, domonability, buf); + } +#endif + + if (OBJ_AT(u.ux, u.uy)) { + struct obj *otmp = level.objects[u.ux][u.uy]; + + Sprintf(buf, "Pick up %s", otmp->nexthere ? "items" : doname(otmp)); + add_herecmd_menuitem(win, dopickup, buf); + + if (Is_container(otmp)) { + Sprintf(buf, "Loot %s", doname(otmp)); + add_herecmd_menuitem(win, doloot, buf); + } + if (otmp->oclass == FOOD_CLASS) { + Sprintf(buf, "Eat %s", doname(otmp)); + add_herecmd_menuitem(win, doeat, buf); + } + } + + if (invent) + add_herecmd_menuitem(win, dodrop, "Drop items"); + + add_herecmd_menuitem(win, donull, "Rest one turn"); + add_herecmd_menuitem(win, dosearch, "Search around you"); + add_herecmd_menuitem(win, dolook, "Look at what is here"); + + end_menu(win, "What do you want to do?"); + npick = select_menu(win, PICK_ONE, &picks); + destroy_nhwindow(win); + ch = '\0'; + if (npick > 0) { + int NDECL((*func)) = picks->item.a_nfunc; + free((genericptr_t) picks); + + if (doit) { + int ret = (*func)(); + + ch = (char) ret; + } else { + ch = cmd_from_func(func); + } + } + return ch; +} + + static NEARDATA int last_multi; /* @@ -4692,6 +4948,11 @@ int x, y, mod; } if (x == 0 && y == 0) { + if (iflags.herecmd_menu) { + cmd[0] = here_cmd_menu(FALSE); + return cmd; + } + /* here */ if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) || IS_SINK(levl[u.ux][u.uy].typ)) { @@ -4730,6 +4991,13 @@ int x, y, mod; && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) { cmd[1] = Cmd.dirchars[dir]; cmd[2] = '\0'; + if (iflags.herecmd_menu) { + cmd[0] = there_cmd_menu(FALSE, u.ux + x, u.uy + y); + if (cmd[0] == '\0') + cmd[1] = '\0'; + return cmd; + } + if (IS_DOOR(levl[u.ux + x][u.uy + y].typ)) { /* slight assistance to the player: choose kick/open for them */ @@ -5096,6 +5364,7 @@ wiz_port_debug() num_menu_selections = SIZE(menu_selections) - 1; if (num_menu_selections > 0) { menu_item *pick_list; + win = create_nhwindow(NHW_MENU); start_menu(win); for (k = 0; k < num_menu_selections; ++k) { diff --git a/src/dothrow.c b/src/dothrow.c index 6c9aeb315..0b1539454 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -519,6 +519,25 @@ genericptr_t arg; return FALSE; } +/* hack for hurtle_step() -- it ought to be changed to take an argument + indicating lev/fly-to-dest vs lev/fly-to-dest-minus-one-land-on-dest + vs drag-to-dest; original callers use first mode, jumping wants second, + grappling hook backfire and thrown chained ball need third */ +boolean +hurtle_jump(arg, x, y) +genericptr_t arg; +int x, y; +{ + boolean res; + long save_EWwalking = EWwalking; + + /* prevent jumping over water from being placed in that water */ + EWwalking |= I_SPECIAL; + res = hurtle_step(arg, x, y); + EWwalking = save_EWwalking; + return res; +} + /* * Single step for the hero flying through the air from jumping, flying, * etc. Called from hurtle() and jump() via walk_path(). We expect the @@ -606,8 +625,8 @@ int x, y; } if ((u.ux - x) && (u.uy - y) && bad_rock(youmonst.data, u.ux, y) && bad_rock(youmonst.data, x, u.uy)) { - boolean too_much = - (invent && (inv_weight() + weight_cap() > 600)); + boolean too_much = (invent && (inv_weight() + weight_cap() > 600)); + /* Move at a diagonal. */ if (bigmonst(youmonst.data) || too_much) { You("%sget forcefully wedged into a crevice.", @@ -621,14 +640,43 @@ int x, y; } } - if ((mon = m_at(x, y)) != 0) { - You("bump into %s.", a_monnam(mon)); + if ((mon = m_at(x, y)) != 0 +#if 0 /* we can't include these two exceptions unless we know we're + * going to end up past the current spot rather than on it; + * for that, we need to know that the range is not exhausted + * and also that the next spot doesn't contain an obstacle */ + && !(mon->mundetected && hides_under(mon) && (Flying || Levitation)) + && !(mon->mundetected && mon->data->mlet == S_EEL + && (Flying || Levitation || Wwalking)) +#endif + ) { + const char *mnam, *pronoun; + int glyph = glyph_at(x, y); + + mon->mundetected = 0; /* wakeup() will handle mimic */ + mnam = a_monnam(mon); /* after unhiding */ + pronoun = mhim(mon); + if (!strcmp(mnam, "it")) { + /* mhim() uses pronoun_gender() which forces neuter if monster + can't be seen; we want him/her for humanoid sensed by touch */ + if (!strcmp(pronoun, "it") && humanoid(mon->data)) + pronoun = genders[mon->female].him; + mnam = !strcmp(pronoun, "it") ? "something" : "someone"; + } + if (!glyph_is_monster(glyph) && !glyph_is_invisible(glyph)) + You("find %s by bumping into %s.", mnam, pronoun); + else + You("bump into %s.", mnam); wakeup(mon, FALSE); + if (!canspotmon(mon)) + map_invisible(mon->mx, mon->my); setmangry(mon, FALSE); - wake_nearto(x,y, 10); + wake_nearto(x, y, 10); return FALSE; } - if ((u.ux - x) && (u.uy - y) && bad_rock(youmonst.data, u.ux, y) + + if ((u.ux - x) && (u.uy - y) + && bad_rock(youmonst.data, u.ux, y) && bad_rock(youmonst.data, x, u.uy)) { /* Move at a diagonal. */ if (Sokoban) { diff --git a/src/eat.c b/src/eat.c index 9bd6346b0..a1b4bc33d 100644 --- a/src/eat.c +++ b/src/eat.c @@ -112,8 +112,11 @@ register struct obj *obj; void init_uhunger() { + context.botl = (u.uhs != NOT_HUNGRY || ATEMP(A_STR) < 0); u.uhunger = 900; u.uhs = NOT_HUNGRY; + if (ATEMP(A_STR) < 0) + ATEMP(A_STR) = 0; } /* tin types [SPINACH_TIN = -1, overrides corpsenm, nut==600] */ @@ -2440,10 +2443,7 @@ doeat() * mails, players who polymorph back to human in the middle of their * metallic meal, etc.... */ - if (!(carried(otmp) ? retouch_object(&otmp, FALSE) - : touch_artifact(otmp, &youmonst))) { - return 1; - } else if (!is_edible(otmp)) { + if (!is_edible(otmp)) { You("cannot eat that!"); return 0; } else if ((otmp->owornmask & (W_ARMOR | W_TOOL | W_AMUL | W_SADDLE)) @@ -2451,6 +2451,9 @@ doeat() /* let them eat rings */ You_cant("eat %s you're wearing.", something); return 0; + } else if (!(carried(otmp) ? retouch_object(&otmp, FALSE) + : touch_artifact(otmp, &youmonst))) { + return 1; /* got blasted so use a turn */ } if (is_metallic(otmp) && u.umonnum == PM_RUST_MONSTER && otmp->oerodeproof) { @@ -2465,13 +2468,26 @@ doeat() /* The regurgitated object's rustproofing is gone now */ otmp->oerodeproof = 0; make_stunned((HStun & TIMEOUT) + (long) rn2(10), TRUE); - You("spit %s out onto the %s.", the(xname(otmp)), - surface(u.ux, u.uy)); - if (carried(otmp)) { - freeinv(otmp); - dropy(otmp); + /* + * We don't expect rust monsters to be wielding welded weapons + * or wearing cursed rings which were rustproofed, but guard + * against the possibility just in case. + */ + if (welded(otmp) || (otmp->cursed && (otmp->owornmask & W_RING))) { + otmp->bknown = 1; /* for ring; welded() does this for weapon */ + You("spit out %s.", the(xname(otmp))); + } else { + You("spit %s out onto the %s.", the(xname(otmp)), + surface(u.ux, u.uy)); + if (carried(otmp)) { + /* no need to check for leash in use; it's not metallic */ + if (otmp->owornmask) + remove_worn_item(otmp, FALSE); + freeinv(otmp); + dropy(otmp); + } + stackobj(otmp); } - stackobj(otmp); return 1; } /* KMH -- Slow digestion is... indigestible */ @@ -2961,10 +2977,23 @@ boolean incr; } if (newhs != u.uhs) { - if (newhs >= WEAK && u.uhs < WEAK) - losestr(1); /* this may kill you -- see below */ - else if (newhs < WEAK && u.uhs >= WEAK) - losestr(-1); + if (newhs >= WEAK && u.uhs < WEAK) { + /* this used to be losestr(1) which had the potential to + be fatal (still handled below) by reducing HP if it + tried to take base strength below minimum of 3 */ + ATEMP(A_STR) = -1; /* temporary loss overrides Fixed_abil */ + /* defer context.botl status update until after hunger message */ + } else if (newhs < WEAK && u.uhs >= WEAK) { + /* this used to be losestr(-1) which could be abused by + becoming weak while wearing ring of sustain ability, + removing ring, eating to 'restore' strength which boosted + strength by a point each time the cycle was performed; + substituting "while polymorphed" for sustain ability and + "rehumanize" for ring removal might have done that too */ + ATEMP(A_STR) = 0; /* repair of loss also overrides Fixed_abil */ + /* defer context.botl status update until after hunger message */ + } + switch (newhs) { case HUNGRY: if (Hallucination) { diff --git a/src/end.c b/src/end.c index 31914dbb5..d0fb09bc5 100644 --- a/src/end.c +++ b/src/end.c @@ -763,6 +763,7 @@ time_t when; /* date+time at end of game */ dump_redirect(FALSE); #else nhUse(how); + nhUse(when); #endif } diff --git a/src/options.c b/src/options.c index 7086a7b5e..b281ba695 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1507164574 2017/10/05 00:49:34 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.311 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1507846854 2017/10/12 22:20:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.315 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -129,6 +129,7 @@ static struct Bool_Opt { { "fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE }, { "goldX", &iflags.goldX, FALSE, SET_IN_GAME }, { "help", &flags.help, TRUE, SET_IN_GAME }, + { "herecmd_menu", &iflags.herecmd_menu, FALSE, SET_IN_GAME }, { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/ { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME }, { "hitpointbar", &iflags.wc2_hitpointbar, FALSE, SET_IN_GAME }, /*WC2*/ @@ -3719,8 +3720,19 @@ boolean tinitial, tfrom_file; vision_full_recalc = 1; /* delayed recalc */ if (iflags.use_color) need_redraw = TRUE; /* darkroom refresh */ - } else if (boolopt[i].addr == &iflags.wc_tiled_map - || boolopt[i].addr == &flags.showrace + } else if (boolopt[i].addr == &iflags.wc_ascii_map) { + /* toggling ascii_map; set tiled_map to its opposite; + what does it mean to turn off ascii map if tiled map + isn't supported? -- right now, we do nothing */ + iflags.wc_tiled_map = negated; + need_redraw = TRUE; + } else if (boolopt[i].addr == &iflags.wc_tiled_map) { + /* toggling tiled_map; set ascii_map to its opposite; + as with ascii_map, what does it mean to turn off tiled + map if ascii map isn't supported? */ + iflags.wc_ascii_map = negated; + need_redraw = TRUE; + } else if (boolopt[i].addr == &flags.showrace || boolopt[i].addr == &iflags.use_inverse || boolopt[i].addr == &iflags.hilite_pile || boolopt[i].addr == &iflags.hilite_pet) { @@ -5523,7 +5535,8 @@ const char *mapping; ape = (struct autopickup_exception *) alloc(sizeof *ape); ape->regex = regex_init(); if (!regex_compile(text, ape->regex)) { - config_error_add("%s: %s", APE_regex_error, regex_error_desc(ape->regex)); + config_error_add("%s: %s", APE_regex_error, + regex_error_desc(ape->regex)); regex_free(ape->regex); free((genericptr_t) ape); return 0; @@ -6182,13 +6195,11 @@ STATIC_OVL boolean wc_supported(optnam) const char *optnam; { - int k = 0; + int k; - while (wc_options[k].wc_name) { - if (!strcmp(wc_options[k].wc_name, optnam) - && (windowprocs.wincap & wc_options[k].wc_bit)) - return TRUE; - k++; + for (k = 0; wc_options[k].wc_name; ++k) { + if (!strcmp(wc_options[k].wc_name, optnam)) + return (windowprocs.wincap & wc_options[k].wc_bit) ? TRUE : FALSE; } return FALSE; } @@ -6242,13 +6253,12 @@ STATIC_OVL boolean wc2_supported(optnam) const char *optnam; { - int k = 0; + int k; - while (wc2_options[k].wc_name) { - if (!strcmp(wc2_options[k].wc_name, optnam) - && (windowprocs.wincap2 & wc2_options[k].wc_bit)) - return TRUE; - k++; + for (k = 0; wc2_options[k].wc_name; ++k) { + if (!strcmp(wc2_options[k].wc_name, optnam)) + return (windowprocs.wincap2 & wc2_options[k].wc_bit) ? TRUE + : FALSE; } return FALSE; } diff --git a/src/potion.c b/src/potion.c index f167bf710..4925b27b0 100644 --- a/src/potion.c +++ b/src/potion.c @@ -565,8 +565,8 @@ register struct obj *otmp; i = rn2(A_MAX); /* start at a random point */ for (ii = 0; ii < A_MAX; ii++) { lim = AMAX(i); - if (i == A_STR && u.uhs >= 3) - --lim; /* WEAK */ + /* this used to adjust 'lim' for A_STR when u.uhs was + WEAK or worse, but that's handled via ATEMP(A_STR) now */ if (ABASE(i) < lim) { ABASE(i) = lim; context.botl = 1; diff --git a/src/pray.c b/src/pray.c index f9fe72083..51dfbb5bb 100644 --- a/src/pray.c +++ b/src/pray.c @@ -343,7 +343,7 @@ int trouble; u.utrap = 0; break; case TROUBLE_STARVING: - losestr(-1); + /* temporarily lost strength recovery now handled by init_uhunger() */ /*FALLTHRU*/ case TROUBLE_HUNGRY: Your("%s feels content.", body_part(STOMACH)); diff --git a/src/read.c b/src/read.c index 9732fea96..29513f7ef 100644 --- a/src/read.c +++ b/src/read.c @@ -1714,6 +1714,7 @@ boolean confused, helmet_protects, byu, skip_uswallow; } } else dmg = 0; + wake_nearto(u.ux, u.uy, 4 * 4); /* Must be before the losehp(), for bones files */ if (!flooreffects(otmp2, u.ux, u.uy, "fall")) { place_object(otmp2, u.ux, u.uy); @@ -1779,7 +1780,10 @@ boolean confused, byu; pline("%s is killed.", Monnam(mtmp)); mondied(mtmp); } + } else { + wakeup(mtmp, byu); } + wake_nearto(x, y, 4 * 4); } else if (u.uswallow && mtmp == u.ustuck) { obfree(otmp2, (struct obj *) 0); /* fall through to player */ diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 8c27b612c..3696ce5c0 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -332,6 +332,9 @@ RANDOBJ = # used by `make depend' to reconstruct this Makefile; you shouldn't need this AWK = nawk + +#VERBOSEMAKE = 1 + # ---------------------------------------- # # Nothing below this line should have to be changed. @@ -339,6 +342,20 @@ AWK = nawk # Other things that have to be reconfigured are in config.h, # {unixconf.h, pcconf.h, tosconf.h}, and possibly system.h +# Verbosity +ACTUAL_CC := $(CC) +CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V = $(CC_V0) +CC_V1 = $(ACTUAL_CC) +CC = $(CC_V$(VERBOSEMAKE)) + +ACTUAL_CXX := $(CXX) +CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V = $(CXX_V0) +CXX_V1 = $(ACTUAL_CXX) +CXX = $(CXX_V$(VERBOSEMAKE)) + + MAKEDEFS = ../util/makedefs # timestamp files to reduce `make' overhead and shorten .o dependency lists @@ -438,7 +455,7 @@ $(GAME): $(SYSTEM) Sysunix: $(HOBJ) Makefile @echo "Loading ..." - $(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LIBS) + @$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LIBS) @touch Sysunix Sys3B2: $(HOBJ) Makefile diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 3cd34906e..dc56d4fdf 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -130,6 +130,20 @@ LEXYYC = lex.yy.c # # Nothing below this line should have to be changed. +# Verbosity +ACTUAL_CC := $(CC) +CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V = $(CC_V0) +CC_V1 = $(ACTUAL_CC) +CC = $(CC_V$(VERBOSEMAKE)) + +ACTUAL_CXX := $(CXX) +CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V = $(CXX_V0) +CXX_V1 = $(ACTUAL_CXX) +CXX = $(CXX_V$(VERBOSEMAKE)) + + # timestamps for primary header files, matching src/Makefile CONFIG_H = ../src/config.h-t HACK_H = ../src/hack.h-t @@ -232,8 +246,7 @@ lev_main.o: lev_main.c $(HACK_H) ../include/sp_lev.h ../include/tcap.h \ # egrep will return failure if it doesn't find anything, but we know there # is one "_cplusplus" inside a comment lev_lex.o: lev_lex.c $(HACK_H) ../include/lev_comp.h ../include/sp_lev.h - @echo $(CC) -c $(CFLAGS) lev_lex.c - @$(CC) -c $(CFLAGS) -DWEIRD_LEX=`egrep -c _cplusplus lev_lex.c` lev_lex.c + $(CC) -c $(CFLAGS) -DWEIRD_LEX=`egrep -c _cplusplus lev_lex.c` lev_lex.c # '$(YACC) -d' generates both $(YTABC) and $(YTABH) in one run ../include/lev_comp.h: lev_yacc.c @@ -270,8 +283,7 @@ dgn_main.o: dgn_main.c $(CONFIG_H) ../include/dlb.h # see dgn_comp.l for WEIRD_LEX discussion dgn_lex.o: dgn_lex.c $(CONFIG_H) ../include/dgn_comp.h ../include/dgn_file.h - @echo $(CC) -c $(CFLAGS) dgn_lex.c - @$(CC) -c $(CFLAGS) -DWEIRD_LEX=`egrep -c _cplusplus dgn_lex.c` dgn_lex.c + $(CC) -c $(CFLAGS) -DWEIRD_LEX=`egrep -c _cplusplus dgn_lex.c` dgn_lex.c # '$(YACC) -d' generates both $(YTABC) and $(YTABH) in one run ../include/dgn_comp.h: dgn_yacc.c diff --git a/sys/unix/hints/linux-qt4 b/sys/unix/hints/linux-qt4 index 51f6ad05b..ae3911939 100644 --- a/sys/unix/hints/linux-qt4 +++ b/sys/unix/hints/linux-qt4 @@ -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+=-DHACKDIR=\"$(HACKDIR)\" CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" diff --git a/sys/unix/setup.sh b/sys/unix/setup.sh index f3319adb7..5c210264c 100755 --- a/sys/unix/setup.sh +++ b/sys/unix/setup.sh @@ -26,6 +26,11 @@ x) hints=/dev/null ;; esac +if [ ! -f "$hints" ]; then + echo "Cannot find hints file $hfile" + exit 1 +fi + /bin/sh ./mkmkfile.sh Makefile.top TOP ../../Makefile $hints $hfile /bin/sh ./mkmkfile.sh Makefile.dat DAT ../../dat/Makefile $hints $hfile /bin/sh ./mkmkfile.sh Makefile.doc DOC ../../doc/Makefile $hints $hfile diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 2270ab262..2e8f41330 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -696,4 +696,18 @@ get_unix_pw() return pw; } +char * +get_login_name() +{ + static char buf[BUFSZ]; + struct passwd *pw = get_unix_pw(); + + buf[0] = '\0'; + + if (pw) + (void)strcpy(buf, pw->pw_name); + + return buf; +} + /*unixmain.c*/ diff --git a/win/Qt4/qt4bind.cpp b/win/Qt4/qt4bind.cpp index feed0a656..2fef131ce 100644 --- a/win/Qt4/qt4bind.cpp +++ b/win/Qt4/qt4bind.cpp @@ -18,6 +18,7 @@ extern "C" { #undef max #include +#include #if QT_VERSION >= 0x050000 #include #include @@ -119,6 +120,9 @@ NetHackQtBind::NetHackQtBind(int& argc, char** argv) : main = new NetHackQtMainWindow(keybuffer); connect(qApp, SIGNAL(lastWindowClosed()), qApp, SLOT(quit())); qt_settings=new NetHackQtSettings(main->width(),main->height()); + msgs_strings = new QStringList(); + msgs_initd = false; + msgs_saved = false; } void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv) @@ -493,51 +497,48 @@ int NetHackQtBind::qt_doprev_message() char NetHackQtBind::qt_yn_function(const char *question_, const char *choices, CHAR_P def) { QString question(QString::fromLatin1(question_)); + QString message; + char yn_esc_map='\033'; + + if (choices) { + // anything beyond is hidden> + QString choicebuf = choices; + size_t cb = choicebuf.indexOf('\033'); + choicebuf = choicebuf.mid(0U, cb); + message = QString("%1 [%2] ").arg(question, choicebuf); + if (def) message += QString("(%1) ").arg(QChar(def)); + // escape maps to 'q' or 'n' or default, in that order + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + message = question; + } if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) { // Similar to X11 windowport `slow' feature. - QString message; - char yn_esc_map='\033'; - - if (choices) { - // anything beyond is hidden> - QString choicebuf = choices; - size_t cb = choicebuf.indexOf('\033'); - choicebuf = choicebuf.mid(0U, cb); - message = QString("%1 [%2] ").arg(question, choicebuf); - if (def) message += QString("(%1) ").arg(QChar(def)); - // escape maps to 'q' or 'n' or default, in that order - yn_esc_map = (index(choices, 'q') ? 'q' : - (index(choices, 'n') ? 'n' : def)); - } else { - message = question; - } + int result = -1; #ifdef USE_POPUPS - // Improve some special-cases (DIRKS 08/02/23) - if (strcmp (choices,"ynq") == 0) { - switch (QMessageBox::information (NetHackQtBind::mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2)) - { - case 0: return 'y'; - case 1: return 'n'; - case 2: return 'q'; - } - } + if (choices) { + if (!strcmp(choices,"ynq")) + result = QMessageBox::information (NetHackQtBind::mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2); + else if (!strcmp(choices,"yn")) + result = QMessageBox::information(NetHackQtBind::mainWidget(),"NetHack",question,"&Yes", "&No",0,1); + else if (!strcmp(choices, "rl")) + result = QMessageBox::information(NetHackQtBind::mainWidget(),"NetHack",question,"&Right", "&Left",0,1); - if (strcmp (choices,"yn") == 0) { - switch (QMessageBox::information(NetHackQtBind::mainWidget(),"NetHack",question,"&Yes", "&No",0,1)) - { - case 0: return 'y'; - case 1: return 'n'; - } - } + if (result >= 0 && result < strlen(choices)) { + char yn_resp = choices[result]; + message += QString(" %1").arg(yn_resp); + result = yn_resp; + } + } #endif NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message); - int result=-1; - while (result<0) { + while (result < 0) { char ch=NetHackQtBind::qt_nhgetch(); if (ch=='\033') { result=yn_esc_map; @@ -558,7 +559,12 @@ char NetHackQtBind::qt_yn_function(const char *question_, const char *choices, C return result; } else { NetHackQtYnDialog dialog(mainWidget(),question,choices,def); - return dialog.Exec(); + char ret = dialog.Exec(); + if (!(ret == '\0' || ret == '\033') && choices) + message += QString(" %1").arg(ret); + else if (def) + message += QString(" %1").arg(def); + NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message); } } @@ -605,6 +611,55 @@ void NetHackQtBind::qt_outrip(winid wid, int how, time_t when) window->UseRIP(how, when); } +char * NetHackQtBind::qt_getmsghistory(BOOLEAN_P init) +{ + NetHackQtMessageWindow* window = main->GetMessageWindow(); + if (window) + return (char *)window->GetStr(init); + return NULL; +} + +void NetHackQtBind::qt_putmsghistory(const char *msg, BOOLEAN_P is_restoring) +{ + NetHackQtMessageWindow* window = main->GetMessageWindow(); + if (!window) + return; + + if (is_restoring && !msgs_initd) { + /* we're restoring history from the previous session, but new + messages have already been issued this session */ + int i = 0; + const char *str; + + while ((str = window->GetStr((i == 0)))) { + msgs_strings->append(str); + i++; + } + msgs_initd = true; + msgs_saved = (i > 0); + window->ClearMessages(); + } + + if (msg) { + //raw_printf("msg='%s'", msg); + window->PutStr(ATR_NONE, QString::fromLatin1(msg)); +#ifdef DUMPLOG + dumplogmsg(msg); +#endif + } else if (msgs_saved) { + /* restore strings */ + int i; + for (i = 0; i < msgs_strings->size(); i++) { + window->PutStr(ATR_NONE, msgs_strings->at((i))); +#ifdef DUMPLOG + dumplogmsg(msgs_strings->at(i).toLatin1().constData()); +#endif + } + delete msgs_strings; + msgs_initd = false; + } +} + bool NetHackQtBind::notify(QObject *receiver, QEvent *event) { // Ignore Alt-key navigation to menubar, it's annoying when you @@ -658,6 +713,9 @@ NetHackQtKeyBuffer NetHackQtBind::keybuffer; NetHackQtClickBuffer NetHackQtBind::clickbuffer; NetHackQtMainWindow* NetHackQtBind::main=0; QFrame* NetHackQtBind::splash=0; +QStringList *NetHackQtBind::msgs_strings; +boolean NetHackQtBind::msgs_saved = false; +boolean NetHackQtBind::msgs_initd = false; static void Qt_positionbar(char *) {} @@ -683,7 +741,7 @@ struct window_procs Qt_procs = { nethack_qt4::NetHackQtBind::qt_destroy_nhwindow, nethack_qt4::NetHackQtBind::qt_curs, nethack_qt4::NetHackQtBind::qt_putstr, - nethack_qt4::NetHackQtBind::qt_putstr, /* FIXME: should be qt_putmixed() */ + genl_putmixed, nethack_qt4::NetHackQtBind::qt_display_file, nethack_qt4::NetHackQtBind::qt_start_menu, nethack_qt4::NetHackQtBind::qt_add_menu, @@ -728,7 +786,8 @@ struct window_procs Qt_procs = { #endif genl_preference_update, - genl_getmsghistory, genl_putmsghistory, + nethack_qt4::NetHackQtBind::qt_getmsghistory, + nethack_qt4::NetHackQtBind::qt_putmsghistory, genl_status_init, genl_status_finish, genl_status_enablefield, #ifdef STATUS_HILITES diff --git a/win/Qt4/qt4bind.h b/win/Qt4/qt4bind.h index 99a2c0d11..820341cae 100644 --- a/win/Qt4/qt4bind.h +++ b/win/Qt4/qt4bind.h @@ -78,6 +78,9 @@ public: static void qt_start_screen(); static void qt_end_screen(); + static char *qt_getmsghistory(BOOLEAN_P init); + static void qt_putmsghistory(const char *msg, BOOLEAN_P is_restoring); + static void qt_outrip(winid wid, int how, time_t when); static int qt_kbhit(); @@ -85,6 +88,10 @@ public: private: virtual bool notify(QObject *receiver, QEvent *event); + + static QStringList *msgs_strings; + static boolean msgs_saved; + static boolean msgs_initd; }; } // namespace nethack_qt4 diff --git a/win/Qt4/qt4inv.cpp b/win/Qt4/qt4inv.cpp index 9165d58b3..f753ad097 100644 --- a/win/Qt4/qt4inv.cpp +++ b/win/Qt4/qt4inv.cpp @@ -73,13 +73,8 @@ void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*) drawWorn(painter,uarms,0,1); // Shield drawWorn(painter,uarmg,0,2); // Gloves - repeated drawWorn(painter,uarmg,2,2); // Gloves - repeated -#ifdef TOURIST drawWorn(painter,uarmf,1,5); // Shoes (feet) drawWorn(painter,uarmu,1,4); // Undershirt -#else - drawWorn(painter,0 ,1,5,false); - drawWorn(painter,uarmf,1,4); // Shoes (feet) -#endif drawWorn(painter,uleft,0,3); // RingL drawWorn(painter,uright,2,3); // RingR diff --git a/win/Qt4/qt4main.cpp b/win/Qt4/qt4main.cpp index ec7e58324..4fd8d14da 100644 --- a/win/Qt4/qt4main.cpp +++ b/win/Qt4/qt4main.cpp @@ -861,14 +861,22 @@ void NetHackQtMainWindow::doKeys(const QString& k) void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window) { message=window; - hsplitter->insertWidget(0, message->Widget()); + if (!qt_compact_mode) + hsplitter->insertWidget(0, message->Widget()); ShowIfReady(); } +NetHackQtMessageWindow * NetHackQtMainWindow::GetMessageWindow() +{ + return message; +} + void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow2* window) { + map=window; - vsplitter->insertWidget(1, map->Widget()); + if (!qt_compact_mode) + vsplitter->insertWidget(1, map->Widget()); ShowIfReady(); connect(map,SIGNAL(resized()),this,SLOT(layout())); } @@ -876,7 +884,8 @@ void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow2* window) void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window) { status=window; - hsplitter->insertWidget(2, status->Widget()); + if (!qt_compact_mode) + hsplitter->insertWidget(2, status->Widget()); ShowIfReady(); } diff --git a/win/Qt4/qt4main.h b/win/Qt4/qt4main.h index 33f5b2635..a3ec15ac2 100644 --- a/win/Qt4/qt4main.h +++ b/win/Qt4/qt4main.h @@ -41,6 +41,7 @@ public: NetHackQtMainWindow(NetHackQtKeyBuffer&); void AddMessageWindow(NetHackQtMessageWindow* window); + NetHackQtMessageWindow * GetMessageWindow(); void AddMapWindow(NetHackQtMapWindow2* window); void AddStatusWindow(NetHackQtStatusWindow* window); void RemoveWindow(NetHackQtWindow* window); diff --git a/win/Qt4/qt4map.cpp b/win/Qt4/qt4map.cpp index 39c915ee5..f3425bab2 100644 --- a/win/Qt4/qt4map.cpp +++ b/win/Qt4/qt4map.cpp @@ -71,6 +71,7 @@ NetHackQtMapViewport::NetHackQtMapViewport(NetHackQtClickBuffer& click_sink) : change(10) { pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + pile_annotation = QPixmap(pile_mark_xpm); Clear(); cursor.setX(0); @@ -157,29 +158,33 @@ void NetHackQtMapViewport::paintEvent(QPaintEvent* event) QString(QChar(ch)).left(1) ); } - if (glyph_is_pet(g) #ifdef TEXTCOLOR - && ::iflags.hilite_pet + if (((special & MG_PET) != 0) && ::iflags.hilite_pet) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation); + } #endif - ) { - painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); - } - } - } + } + } painter.setFont(font()); } else { for (int j=garea.top(); j<=garea.bottom(); j++) { for (int i=garea.left(); i<=garea.right(); i++) { unsigned short g=Glyph(i,j); + int color; + int ch; + unsigned special; + mapglyph(g, &ch, &color, &special, i, j); qt_settings->glyphs().drawCell(painter, g, i, j); - if (glyph_is_pet(g) #ifdef TEXTCOLOR - && ::iflags.hilite_pet + if (((special & MG_PET) != 0) && ::iflags.hilite_pet) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation); + } #endif - ) { - painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); - } } } } @@ -649,6 +654,7 @@ NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) : viewport.setPalette(palette); pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + pile_annotation = QPixmap(pile_mark_xpm); cursor.setX(0); cursor.setY(0); @@ -835,13 +841,13 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) Qt::AlignCenter, QString(QChar(ch)).left(1) ); - if (glyph_is_pet(g) #ifdef TEXTCOLOR - && ::iflags.hilite_pet + if (((special & MG_PET) != 0) && ::iflags.hilite_pet) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation); + } #endif - ) { - painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); - } } } @@ -850,14 +856,18 @@ void NetHackQtMapWindow::paintEvent(QPaintEvent* event) for (int j=garea.top(); j<=garea.bottom(); j++) { for (int i=garea.left(); i<=garea.right(); i++) { unsigned short g=Glyph(i,j); + int color; + int ch; + unsigned special; + mapglyph(g, &ch, &color, &special, i, j); qt_settings->glyphs().drawCell(painter, g, i, j); - if (glyph_is_pet(g) #ifdef TEXTCOLOR - && ::iflags.hilite_pet + if (((special & MG_PET) != 0) && ::iflags.hilite_pet) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } else if (((special & MG_OBJPILE) != 0) && ::iflags.hilite_pile) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pile_annotation); + } #endif - ) { - painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); - } } } } diff --git a/win/Qt4/qt4map.h b/win/Qt4/qt4map.h index da2df88ad..337a726aa 100644 --- a/win/Qt4/qt4map.h +++ b/win/Qt4/qt4map.h @@ -33,6 +33,7 @@ private: unsigned short& Glyph(int x, int y) { return glyph[y][x]; } QPoint cursor; QPixmap pet_annotation; + QPixmap pile_annotation; NetHackQtClickBuffer& clicksink; Clusterizer change; diff --git a/win/Qt4/qt4menu.cpp b/win/Qt4/qt4menu.cpp index 7d70a26e2..5ab053891 100644 --- a/win/Qt4/qt4menu.cpp +++ b/win/Qt4/qt4menu.cpp @@ -132,6 +132,7 @@ NetHackQtMenuWindow::NetHackQtMenuWindow(QWidget *parent) : grid->setRowStretch(2, 1); setFocusPolicy(Qt::StrongFocus); table->setFocusPolicy(Qt::NoFocus); + connect(table, SIGNAL(cellClicked(int,int)), this, SLOT(cellToggleSelect(int,int))); setLayout(grid); } @@ -482,11 +483,11 @@ void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event) accept(); else if (key==MENU_SEARCH) Search(); - else if (key==MENU_SELECT_ALL) + else if (key==MENU_SELECT_ALL || key==MENU_SELECT_PAGE) All(); - else if (key==MENU_INVERT_ALL) + else if (key==MENU_INVERT_ALL || key==MENU_INVERT_PAGE) Invert(); - else if (key==MENU_UNSELECT_ALL) + else if (key==MENU_UNSELECT_ALL || key==MENU_UNSELECT_PAGE) ChooseNone(); else if (('0' <= key && key <= '9') || key == '\b') InputCount(key); @@ -501,6 +502,9 @@ void NetHackQtMenuWindow::keyPressEvent(QKeyEvent* event) void NetHackQtMenuWindow::All() { + if (how != PICK_ANY) + return; + for (int i=0; iitem(i, 0); if (count != NULL) count->setText(""); @@ -511,6 +515,9 @@ void NetHackQtMenuWindow::All() } void NetHackQtMenuWindow::ChooseNone() { + if (how != PICK_ANY) + return; + for (int i=0; iitem(i, 0); if (count != NULL) count->setText(""); @@ -521,6 +528,9 @@ void NetHackQtMenuWindow::ChooseNone() } void NetHackQtMenuWindow::Invert() { + if (how != PICK_ANY) + return; + for (int i=0; iitem(i, 0); if (count != NULL) count->setText(""); @@ -531,6 +541,9 @@ void NetHackQtMenuWindow::Invert() } void NetHackQtMenuWindow::Search() { + if (how == PICK_NONE) + return; + NetHackQtStringRequestor requestor(this, "Search for:"); char line[256]; if (requestor.Get(line)) { @@ -560,6 +573,11 @@ void NetHackQtMenuWindow::ToggleSelect(int i) } } +void NetHackQtMenuWindow::cellToggleSelect(int i, int j) +{ + ToggleSelect(i); +} + void NetHackQtMenuWindow::DoSelection(bool) { if (how == PICK_ONE) { diff --git a/win/Qt4/qt4menu.h b/win/Qt4/qt4menu.h index ad15841bc..5bc265920 100644 --- a/win/Qt4/qt4menu.h +++ b/win/Qt4/qt4menu.h @@ -66,6 +66,7 @@ public slots: void Search(); void ToggleSelect(int); + void cellToggleSelect(int, int); void DoSelection(bool); protected: diff --git a/win/Qt4/qt4msg.cpp b/win/Qt4/qt4msg.cpp index e1194795d..d82e102fe 100644 --- a/win/Qt4/qt4msg.cpp +++ b/win/Qt4/qt4msg.cpp @@ -35,6 +35,7 @@ NetHackQtMessageWindow::NetHackQtMessageWindow() : list->setFocusPolicy(Qt::NoFocus); ::iflags.window_inited = 1; map = 0; + currgetmsg = 0; connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont())); updateFont(); } @@ -72,6 +73,12 @@ void NetHackQtMessageWindow::Clear() map->clearMessages(); } +void NetHackQtMessageWindow::ClearMessages() +{ + if (list) + list->clear(); +} + void NetHackQtMessageWindow::Display(bool block) { if (changed) { @@ -80,6 +87,20 @@ void NetHackQtMessageWindow::Display(bool block) } } +const char * NetHackQtMessageWindow::GetStr(bool init) +{ + if (init) + currgetmsg = 0; + + QListWidgetItem *item = list->item(currgetmsg++); + if (item) { + QString str = item->text(); + //raw_printf("getstr[%i]='%s'", currgetmsg, str.toLatin1().constData()); + return str.toLatin1().constData(); + } + return NULL; +} + void NetHackQtMessageWindow::PutStr(int attr, const QString& text) { #ifdef USER_SOUNDS diff --git a/win/Qt4/qt4msg.h b/win/Qt4/qt4msg.h index 5c391cc4f..f4091ad18 100644 --- a/win/Qt4/qt4msg.h +++ b/win/Qt4/qt4msg.h @@ -22,15 +22,18 @@ public: virtual QWidget* Widget(); virtual void Clear(); virtual void Display(bool block); + virtual const char *GetStr(bool init); virtual void PutStr(int attr, const QString& text); void Scroll(int dx, int dy); + void ClearMessages(); void setMap(NetHackQtMapWindow2*); private: QListWidget* list; bool changed; + int currgetmsg; NetHackQtMapWindow2* map; private slots: diff --git a/win/Qt4/qt4plsel.cpp b/win/Qt4/qt4plsel.cpp index 56b8849bd..81bf5f023 100644 --- a/win/Qt4/qt4plsel.cpp +++ b/win/Qt4/qt4plsel.cpp @@ -37,7 +37,7 @@ namespace nethack_qt4 { void centerOnMain( QWidget* w ); // end temporary -static const char nh_attribution[] = "
NetHack" +static const char nh_attribution[] = "
NetHack %1" "
by the NetHack DevTeam
"; class NhPSListViewItem : public QTableWidgetItem { @@ -158,8 +158,9 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : | | | | +--------------+ 3 | | | | ...stretch... | | | | - 4 | | | | [ Play ] - 5 | | | | [ Quit ] + 4 | | | | [ Random ] + 5 | | | | [ Play ] + 6 | | | | [ Quit ] +---------+ +---------+ */ @@ -177,37 +178,39 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : connect(name, SIGNAL(textChanged(const QString&)), this, SLOT(selectName(const QString&)) ); name->setFocus(); - QGroupBox* genderbox = new QGroupBox("Sex",this); + QGroupBox* genderbox = new QGroupBox("Gender",this); QButtonGroup *gendergroup = new QButtonGroup(this); QGroupBox* alignbox = new QGroupBox("Alignment",this); QButtonGroup *aligngroup = new QButtonGroup(this); QVBoxLayout* vbgb = new QVBoxLayout(genderbox); - vbgb->addSpacing(fontMetrics().height()*3/4); QVBoxLayout* vbab = new QVBoxLayout(alignbox); - vbab->addSpacing(fontMetrics().height()); - QLabel* logo = new QLabel(nh_attribution, this); + char versionbuf[QBUFSZ]; + QLabel* logo = new QLabel(QString(nh_attribution).arg(version_string(versionbuf)), this); l->addWidget( namebox, 0,0,1,3 ); #ifdef QT_CHOOSE_RACE_FIRST race = new NhPSListView(this); role = new NhPSListView(this); - l->addWidget( race, 1,0,5,1 ); - l->addWidget( role, 1,1,5,1 ); + l->addWidget( race, 1,0,6,1 ); + l->addWidget( role, 1,1,6,1 ); #else role = new NhPSListView(this); race = new NhPSListView(this); - l->addWidget( role, 1,0,5,1 ); - l->addWidget( race, 1,1,5,1 ); + l->addWidget( role, 1,0,6,1 ); + l->addWidget( race, 1,1,6,1 ); #endif l->addWidget( genderbox, 1, 2 ); l->addWidget( alignbox, 2, 2 ); l->addWidget( logo, 3, 2, Qt::AlignCenter ); - l->setRowStretch( 3, 5 ); + l->setRowStretch( 3, 6 ); int i; int nrole; + chosen_gend = flags.initgend; + chosen_align = flags.initalign; + // XXX QListView unsorted goes in rev. for (nrole=0; roles[nrole].name.m; nrole++) ; @@ -254,15 +257,46 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : } connect( aligngroup, SIGNAL(buttonPressed(int)), this, SLOT(selectAlignment(int)) ); + QPushButton* rnd = new QPushButton("Random",this); + l->addWidget( rnd, 4, 2 ); + rnd->setDefault(false); + connect( rnd, SIGNAL(clicked()), this, SLOT(Randomize()) ); + QPushButton* ok = new QPushButton("Play",this); - l->addWidget( ok, 4, 2 ); + l->addWidget( ok, 5, 2 ); ok->setDefault(true); connect( ok, SIGNAL(clicked()), this, SLOT(accept()) ); QPushButton* cancel = new QPushButton("Quit",this); - l->addWidget( cancel, 5, 2 ); + l->addWidget( cancel, 6, 2 ); connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) ); + Randomize(); +} + +void NetHackQtPlayerSelector::Randomize() +{ + int nrole = role->rowCount(); + int nrace = race->rowCount(); + + boolean picksomething = (flags.initrole == ROLE_NONE + || flags.initrace == ROLE_NONE + || flags.initgend == ROLE_NONE + || flags.initalign == ROLE_NONE); + + if (flags.randomall && picksomething) { + 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(); + // Randomize race and role, unless specified in config int ro = flags.initrole; if (ro == ROLE_NONE || ro == ROLE_RANDOM) { @@ -279,7 +313,7 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : } } - // make sure we have a valid combination, honoring + // make sure we have a valid combination, honoring // the users request if possible. bool choose_race_first; #ifdef QT_CHOOSE_RACE_FIRST @@ -332,12 +366,8 @@ NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : role->setCurrentCell(ro, 0); race->setCurrentCell(ra, 0); - - flags.initrace = race->currentRow(); - flags.initrole = role->currentRow(); } - void NetHackQtPlayerSelector::selectName(const QString& n) { str_copy(plname,n.toLatin1().constData(),SIZE(plname)); @@ -378,7 +408,7 @@ void NetHackQtPlayerSelector::selectRole(int crow, int ccol, int prow, int pcol) } #endif - flags.initrole = role->currentRow(); + //flags.initrole = role->currentRow(); setupOthers(); } @@ -416,7 +446,7 @@ void NetHackQtPlayerSelector::selectRace(int crow, int ccol, int prow, int pcol) } #endif - flags.initrace = race->currentRow(); + //flags.initrace = race->currentRow(); setupOthers(); } @@ -460,12 +490,12 @@ void NetHackQtPlayerSelector::setupOthers() void NetHackQtPlayerSelector::selectGender(int i) { - flags.initgend = i; + chosen_gend = i; } void NetHackQtPlayerSelector::selectAlignment(int i) { - flags.initalign = i; + chosen_align = i; } void NetHackQtPlayerSelector::Quit() @@ -493,6 +523,10 @@ bool NetHackQtPlayerSelector::Choose() } if ( exec() ) { + flags.initrace = race->currentRow(); + flags.initrole = role->currentRow(); + flags.initgend = chosen_gend; + flags.initalign = chosen_align; return true; } else { return false; diff --git a/win/Qt4/qt4plsel.h b/win/Qt4/qt4plsel.h index b82341270..de070d147 100644 --- a/win/Qt4/qt4plsel.h +++ b/win/Qt4/qt4plsel.h @@ -21,6 +21,7 @@ public: public slots: void Quit(); void Random(); + void Randomize(); void selectName(const QString& n); void selectRole(int current, int, int previous, int); @@ -38,6 +39,8 @@ private: QRadioButton **gender; QRadioButton **alignment; bool fully_specified_role; + int chosen_gend; + int chosen_align; }; } // namespace nethack_qt4 diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad index 3e1eed6e7..25568da7b 100644 --- a/win/X11/NetHack.ad +++ b/win/X11/NetHack.ad @@ -4,7 +4,8 @@ #define NETHACK_CLR_FG grey #define NETHACK_CLR_BG black #define NETHACK_CLR_QUIT red -#define NETHACK_CLR_RANDOM green +#define NETHACK_CLR_RANDOM yellow +#define NETHACK_CLR_PLAY green #define NETHACK_CLR_HELP green #define NETHACK_BTN_SHAPE roundedRectangle #define NETHACK_BTN_ROUND 20 @@ -119,12 +120,54 @@ NetHack*showGrip: False ! text input prompt window NetHack*response*textSink*background: red +NetHack*textSink*background: red + NetHack*response*foreground: NETHACK_CLR_FG NetHack*response*background: NETHACK_CLR_BG NetHack*response*borderColor: NETHACK_CLR_FG NetHack*response*borderWidth: 1 -! role selection window +! player selection dialog (OPTIONS=player_selection:dialog) +!NetHack*player_selection_dialog.height: 420 +!NetHack*player_selection_dialog.width: 403 + +!NetHack*player_selection_dialog*foreground: black +!NetHack*player_selection_dialog*background: white +NetHack*player_selection_dialog*name_label.borderWidth: 0 +NetHack*player_selection_dialog*race_label.borderWidth: 0 +NetHack*player_selection_dialog*role_label.borderWidth: 0 +NetHack*player_selection_dialog*gender_label.borderWidth: 0 +NetHack*player_selection_dialog*align_label.borderWidth: 0 +NetHack*player_selection_dialog*button_vp.borderWidth: 0 + +NetHack*player_selection_dialog*name_input.borderWidth: 1 +NetHack*player_selection_dialog*name_input.borderColor: NETHACK_CLR_FG + +NetHack*player_selection_dialog*role_vp.borderColor: NETHACK_CLR_FG +NetHack*player_selection_dialog*race_vp.borderColor: NETHACK_CLR_FG +NetHack*player_selection_dialog*gender_vp.borderColor: NETHACK_CLR_FG +NetHack*player_selection_dialog*align_vp.borderColor: NETHACK_CLR_FG + +NetHack*player_selection_dialog*random.foreground: NETHACK_CLR_RANDOM +NetHack*player_selection_dialog*random.borderColor: NETHACK_CLR_RANDOM +NetHack*player_selection_dialog*random.borderWidth: 1 +NetHack*player_selection_dialog*random.shapeStyle: NETHACK_BTN_SHAPE +NetHack*player_selection_dialog*random.cornerRoundPercent: NETHACK_BTN_ROUND + +NetHack*player_selection_dialog*quit.foreground: NETHACK_CLR_QUIT +NetHack*player_selection_dialog*quit.borderColor: NETHACK_CLR_QUIT +NetHack*player_selection_dialog*quit.borderWidth: 1 +NetHack*player_selection_dialog*quit.shapeStyle: NETHACK_BTN_SHAPE +NetHack*player_selection_dialog*quit.cornerRoundPercent: NETHACK_BTN_ROUND + +NetHack*player_selection_dialog*play.foreground: NETHACK_CLR_PLAY +NetHack*player_selection_dialog*play.borderColor: NETHACK_CLR_PLAY +NetHack*player_selection_dialog*play.borderWidth: 1 +NetHack*player_selection_dialog*play.shapeStyle: NETHACK_BTN_SHAPE +NetHack*player_selection_dialog*play.cornerRoundPercent: NETHACK_BTN_ROUND + + +! role selection window (OPTIONS=player_selection:prompts) NetHack*player_selection*random.foreground: NETHACK_CLR_RANDOM NetHack*player_selection*random.background: black NetHack*player_selection*random.borderColor: NETHACK_CLR_RANDOM diff --git a/win/X11/Window.c b/win/X11/Window.c index e6db07b58..09c753f33 100644 --- a/win/X11/Window.c +++ b/win/X11/Window.c @@ -121,7 +121,7 @@ Region region; /* unused */ nhUse(region); /* This isn't correct - we need to call the callback with region. */ - XtCallCallbacks(w, XtNexposeCallback, (caddr_t) event); + XtCallCallbacks(w, XtNexposeCallback, (XtPointer)event); } /* ARGSUSED */ @@ -129,7 +129,7 @@ static void Resize(w) Widget w; { - XtCallCallbacks(w, XtNresizeCallback, (caddr_t) 0); + XtCallCallbacks(w, XtNresizeCallback, (XtPointer) 0); } WindowClassRec windowClassRec = { diff --git a/win/X11/tile2x11.c b/win/X11/tile2x11.c index 8b7b455e1..a51cb75cd 100644 --- a/win/X11/tile2x11.c +++ b/win/X11/tile2x11.c @@ -91,7 +91,7 @@ merge_text_colormap() if (j == header.ncolors) { /* couldn't find it */ #ifdef PRINT_COLORMAP - printf("color %2d: %3d %3d %3d\n", header.ncolors, + Fprintf(stdout, "color %2d: %3d %3d %3d\n", header.ncolors, ColorMap[CM_RED][i], ColorMap[CM_GREEN][i], ColorMap[CM_BLUE][i]); #endif @@ -117,7 +117,7 @@ char *fname; } merge_text_colormap(); count = convert_tiles(&curr_tb, header.ntiles); - Fprintf(stderr, "%s: %lu tiles\n", fname, count); + Fprintf(stdout, "%s: %lu tiles\n", fname, count); header.ntiles += count; fclose_text_file(); } @@ -157,7 +157,7 @@ FILE *fp; Fprintf(fp, "\",\n"); } - return fprintf(fp, "};\n") >= 0; + return Fprintf(fp, "};\n") >= 0; } #endif /* USE_XPM */ @@ -199,7 +199,7 @@ char **argv; } process_file(argv[i]); } - Fprintf(stderr, "Total tiles: %ld\n", header.ntiles); + Fprintf(stdout, "Total tiles: %ld\n", header.ntiles); /* round size up to the end of the row */ if ((header.ntiles % header.per_row) != 0) { diff --git a/win/X11/winX.c b/win/X11/winX.c index 78e304b1f..8d097a791 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 winX.c $NHDT-Date: 1457079197 2016/03/04 08:13:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.6 winX.c $NHDT-Date: 1507846693 2017/10/12 22:18:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.44 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -97,7 +97,11 @@ extern NEARDATA winid WIN_STATUS; /* Interface definition, for windows.c */ struct window_procs X11_procs = { - "X11", WC_COLOR | WC_HILITE_PET | WC_TILED_MAP, 0L, X11_init_nhwindows, + "X11", + (WC_COLOR | WC_HILITE_PET | WC_ASCII_MAP | WC_TILED_MAP + | WC_PLAYER_SELECTION | WC_PERM_INVENT | WC_MOUSE_SUPPORT), + 0L, /* WC2 flag mask */ + X11_init_nhwindows, X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows, X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow, X11_clear_nhwindow, X11_display_nhwindow, X11_destroy_nhwindow, X11_curs, @@ -136,6 +140,7 @@ struct window_procs X11_procs = { static winid NDECL(find_free_window); static void FDECL(nhFreePixel, (XtAppContext, XrmValuePtr, XtPointer, XrmValuePtr, Cardinal *)); +static boolean FDECL(new_resource_macro, (String, unsigned)); static void NDECL(load_default_resources); static void NDECL(release_default_resources); #ifdef X11_HANGUP_SIGNAL @@ -480,7 +485,43 @@ Widget w; #endif } -static String *default_resource_data = 0; /* NULL-terminated array */ +static String *default_resource_data = 0, /* NULL-terminated arrays */ + *def_rsrc_macr = 0, /* macro names */ + *def_rsrc_valu = 0; /* macro values */ + +/* caller found "#define"; parse into macro name and its expansion value */ +static boolean +new_resource_macro(inbuf, numdefs) +String inbuf; /* points past '#define' rather than to start of buffer */ +unsigned numdefs; /* array slot to fill */ +{ + String p, q; + + /* we expect inbuf to be terminated by newline; get rid of it */ + q = eos(inbuf); + if (q > inbuf && q[-1] == '\n') + q[-1] = '\0'; + + /* figure out macro's name */ + for (p = inbuf; *p == ' ' || *p == '\t'; ++p) + continue; /* skip whitespace */ + for (q = p; *q && *q != ' ' && *q != '\t'; ++q) + continue; /* token consists of non-whitespace */ + Strcat(q, " "); /* guarantee something beyond '#define FOO' */ + *q++ = '\0'; /* p..(q-1) contains macro name */ + if (!*p) /* invalid definition: '#define' followed by nothing */ + return FALSE; + def_rsrc_macr[numdefs] = dupstr(p); + + /* figure out macro's value; empty value is supported but not expected */ + while (*q == ' ' || *q == '\t') + ++q; /* skip whitespace between name and value */ + for (p = eos(q); --p > q && (*p == ' ' || *p == '\t'); ) + continue; /* discard trailing whitespace */ + *++p = '\0'; /* q..p containes macro value */ + def_rsrc_valu[numdefs] = dupstr(q); + return TRUE; +} /* read the template NetHack.ad into default_resource_data[] to supply fallback resources to XtAppInitialize() */ @@ -489,8 +530,8 @@ load_default_resources() { FILE *fp; String inbuf; - unsigned insiz, linelen, longlen, numlines; - boolean comment = FALSE; /* lint suppression */ + unsigned insiz, linelen, longlen, numlines, numdefs, midx; + boolean comment, isdef; /* * Running nethack via the shell script adds $HACKDIR to the path used @@ -499,7 +540,11 @@ load_default_resources() * load its contents into memory so that the application startup call * in X11_init_nhwindows() can use them as fallback resources. * - * No attempt to support the 'include' directive has been made. + * No attempt to support the 'include' directive has been made, nor + * backslash+newline continuation lines. Macro expansion (at most + * one substitution per line) is supported. '#define' to introduce + * a macro must be at start of line (no whitespace before or after + * the '#' character). */ fp = fopen("./NetHack.ad", "r"); if (!fp) @@ -509,35 +554,84 @@ load_default_resources() insiz = BUFSIZ; /* stdio BUFSIZ, not nethack BUFSZ */ inbuf = (String) alloc(insiz); linelen = longlen = 0; - numlines = 0; + numlines = numdefs = 0; + comment = isdef = FALSE; /* lint suppression */ while (fgets(inbuf, insiz, fp)) { - if (!linelen) /* inbuf has start of record; treat empty as comment */ + if (!linelen) { + /* !linelen: inbuf has start of record; treat empty as comment */ comment = (*inbuf == '!' || *inbuf == '\n'); + isdef = !strncmp(inbuf, "#define", 7); + ++numdefs; + } linelen += strlen(inbuf); if (!index(inbuf, '\n')) continue; if (linelen > longlen) longlen = linelen; linelen = 0; - if (!comment) + if (!comment && !isdef) ++numlines; } - free((genericptr_t) inbuf); insiz = longlen + 1; - inbuf = (String) alloc(insiz); + if (numdefs) { /* don't alloc if 0; no need for any terminator */ + def_rsrc_macr = (String *) alloc(numdefs * sizeof (String)); + def_rsrc_valu = (String *) alloc(numdefs * sizeof (String)); + insiz += BUFSIZ; /* include room for macro expansion within buffer */ + } + if (insiz > BUFSIZ) { + free((genericptr_t) inbuf); + inbuf = (String) alloc(insiz); + } ++numlines; /* room for terminator */ default_resource_data = (String *) alloc(numlines * sizeof (String)); - /* now read the file into the array */ + /* now re-read the file, storing its contents into the allocated array + after performing macro substitutions */ (void) rewind(fp); - numlines = 0; + numlines = numdefs = 0; while (fgets(inbuf, insiz, fp)) { - if (*inbuf != '!' && *inbuf != '\n') + if (!strncmp(inbuf, "#define", 7)) { + if (new_resource_macro(&inbuf[7], numdefs)) + ++numdefs; + } else if (*inbuf != '!' && *inbuf != '\n') { + if (numdefs) { + /* + * Macro expansion: we assume at most one substitution + * per line. That's all that our sample NetHack.ad uses. + * + * If we ever need more, this will have to become a lot + * more sophisticated. It will need to find the first + * instance within inbuf[] rather than first macro which + * appears, and to avoid finding names within substituted + * expansion values. + * + * Any substitution which would exceed the buffer size is + * skipped. A sophisticated implementation would need to + * be prepared to allocate a bigger buffer when needed. + */ + linelen = strlen(inbuf); + for (midx = 0; midx < numdefs; ++midx) { + if ((linelen + strlen(def_rsrc_valu[midx]) + < insiz - strlen(def_rsrc_macr[midx])) + && strNsubst(inbuf, def_rsrc_macr[midx], + def_rsrc_valu[midx], 1)) + break; + } + } default_resource_data[numlines++] = dupstr(inbuf); + } } default_resource_data[numlines] = (String) 0; (void) fclose(fp); free((genericptr_t) inbuf); + if (def_rsrc_macr) { /* implies def_rsrc_valu is non-Null too */ + for (midx = 0; midx < numdefs; ++midx) { + free((genericptr_t) def_rsrc_macr[midx]); + free((genericptr_t) def_rsrc_valu[midx]); + } + free((genericptr_t) def_rsrc_macr), def_rsrc_macr = 0; + free((genericptr_t) def_rsrc_valu), def_rsrc_valu = 0; + } } static void @@ -550,6 +644,7 @@ release_default_resources() free((genericptr_t) default_resource_data[idx]); free((genericptr_t) default_resource_data), default_resource_data = 0; } + /* def_rsrc_macr[] and def_rsrc_valu[] have already been released */ } /* Global Functions ======================================================= */ @@ -1031,6 +1126,9 @@ static XtActionsRec actions[] = { { nhStr("ec_key"), ec_key }, /* extended commands */ { nhStr("ec_delete"), ec_delete }, /* ext-com menu delete */ { nhStr("ps_key"), ps_key }, /* player selection */ + { nhStr("plsel_quit"), plsel_quit }, /* player selection dialog */ + { nhStr("plsel_play"), plsel_play }, /* player selection dialog */ + { nhStr("plsel_rnd"), plsel_randomize }, /* player selection dialog */ { nhStr("race_key"), race_key }, /* race selection */ { nhStr("gend_key"), gend_key }, /* gender selection */ { nhStr("algn_key"), algn_key }, /* alignment selection */ @@ -1176,6 +1274,7 @@ char **argv; (void) seteuid(savuid); x_inited = TRUE; /* X is now initialized */ + plsel_ask_name = FALSE; release_default_resources(); @@ -1374,6 +1473,20 @@ 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; + return; + } /* else iflags.wc_player_selection == VIA_PROMPTS */ XtSetArg(args[0], XtNallowShellResize, True); @@ -1386,7 +1499,7 @@ X11_askname() (XtCallbackProc) 0); SetDialogPrompt(dialog, nhStr("What is your name?")); /* set prompt */ - SetDialogResponse(dialog, nhStr(""), PL_NSIZ); /* set default answer */ + SetDialogResponse(dialog, plname, PL_NSIZ); /* set default answer */ XtRealizeWidget(popup); positionpopup(popup, TRUE); /* center,bottom */ diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index 7f71b0ca3..a181770b1 100644 --- a/win/X11/winmisc.c +++ b/win/X11/winmisc.c @@ -18,9 +18,12 @@ #include #include #include +#include #include +#include #include #include +#include #include /* for index() */ #include @@ -52,6 +55,17 @@ static int ec_nchars = 0; static char ec_chars[EC_NCHARS]; static Time ec_time; +boolean plsel_ask_name; + +static const char plsel_dialog_translations[] = "#override\n\ + Escape: plsel_quit()\n\ + Return: plsel_play()"; + +static const char plsel_input_accelerators[] = "#override\n\ + Escape: plsel_quit()\n\ + Return: plsel_play()\n\ + Tab: \n"; + static const char extended_command_translations[] = "#override\n\ Left: scroll(4)\n\ Right: scroll(6)\n\ @@ -83,6 +97,23 @@ static Widget FDECL(make_menu, (const char *, const char *, const char *, XtCallbackProc, int, const char **, Widget **, XtCallbackProc, Widget *)); +void NDECL(X11_player_selection_setupOthers); +void NDECL(X11_player_selection_randomize); + +/* Bad Hack alert. Using integers instead of XtPointers */ +XtPointer +i2xtp(i) +int i; +{ + return (XtPointer)(long)i; +} +int +xtp2i(x) +XtPointer x; +{ + return (long)x; +} + /* Player Selection -------------------------------------------------------- */ /* ARGSUSED */ @@ -298,10 +329,911 @@ Cardinal *num_params; exit_x_event = TRUE; } +int plsel_n_races, plsel_n_roles; +Widget *plsel_race_radios = (Widget *) 0; +Widget *plsel_role_radios = (Widget *) 0; +Widget *plsel_gend_radios = (Widget *) 0; +Widget *plsel_align_radios = (Widget *) 0; + +Widget plsel_name_input; + +Widget plsel_btn_play; + +void +plsel_dialog_acceptvalues() +{ + Arg args[2]; + String s; + + flags.initrace = xtp2i(XawToggleGetCurrent(plsel_race_radios[0]))-1; + flags.initrole = xtp2i(XawToggleGetCurrent(plsel_role_radios[0]))-1; + flags.initgend = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0]))-1; + flags.initalign = xtp2i(XawToggleGetCurrent(plsel_align_radios[0]))-1; + + 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'; +} + +/* ARGSUSED */ +void +plsel_quit(w, event, params, num_params) +Widget w; +XEvent *event; +String *params; +Cardinal *num_params; +{ + nhUse(w); + nhUse(event); + nhUse(params); + nhUse(num_params); + + ps_selected = PS_QUIT; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +void +plsel_play(w, event, params, num_params) +Widget w; +XEvent *event; +String *params; +Cardinal *num_params; +{ + Arg args[2]; + Boolean state; + + nhUse(w); + nhUse(event); + nhUse(params); + nhUse(num_params); + + XtSetArg(args[0], nhStr(XtNsensitive), &state); + XtGetValues(plsel_btn_play, args, ONE); + + if (state) { + plsel_dialog_acceptvalues(); + exit_x_event = TRUE; /* leave event loop */ + } else { + X11_nhbell(); + } +} + +/* ARGSUSED */ +void +plsel_randomize(w, event, params, num_params) +Widget w; +XEvent *event; +String *params; +Cardinal *num_params; +{ + nhUse(w); + nhUse(event); + nhUse(params); + nhUse(num_params); + + X11_player_selection_randomize(); +} + +/* enable or disable the Play button */ +void +plsel_set_play_button(state) +boolean state; +{ + Arg args[2]; + + XtSetArg(args[0], nhStr(XtNsensitive), !state); + XtSetValues(plsel_btn_play, args, ONE); +} + +void +plsel_set_sensitivities(setcurr) +boolean setcurr; +{ + Arg args[2]; + int j, valid; + int c = 0; + + int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0]))-1; + int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0]))-1; + + plsel_set_play_button(ra < 0 || ro < 0); + + if (ra < 0 || ro < 0) + return; + + valid = -1; + + for (j = 0; roles[j].name.m; j++) { + boolean v = validrace(j, ra); + + if (j == ro) + c = j; + XtSetArg(args[0], nhStr(XtNsensitive), v); + XtSetValues(plsel_role_radios[j], args, ONE); + if (valid < 0 && v) + valid = j; + } + if (!validrace(ro, c)) + c = valid; + + if (setcurr) { + int tmpc = c+1; + XawToggleSetCurrent(plsel_role_radios[0], i2xtp(tmpc)); + } + + valid = -1; + + for (j = 0; races[j].noun; j++) { + boolean v = validrace(ro, j); + + if (j == ra) + c = j; + XtSetArg(args[0], nhStr(XtNsensitive), v); + XtSetValues(plsel_race_radios[j], args, ONE); + if (valid < 0 && v) + valid = j; + } + if (!validrace(ro, c)) + c = valid; + + if (setcurr) { + int tmpc = c+1; + XawToggleSetCurrent(plsel_race_radios[0], i2xtp(tmpc)); + } + + X11_player_selection_setupOthers(); +} + +void +X11_player_selection_randomize() +{ + int nrole = plsel_n_roles; + int nrace = plsel_n_races; + int ro, ra; + boolean fully_specified_role; + boolean choose_race_first; + int a, g; + int tmpx; + + boolean picksomething = (flags.initrole == ROLE_NONE + || flags.initrace == ROLE_NONE + || flags.initgend == ROLE_NONE + || flags.initalign == ROLE_NONE); + + if (flags.randomall && picksomething) { + 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(); + + /* Randomize race and role, unless specified in config */ + ro = flags.initrole; + if (ro == ROLE_NONE || ro == ROLE_RANDOM) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } + ra = flags.initrace; + if (ra == ROLE_NONE || ra == ROLE_RANDOM) { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } + + /* make sure we have a valid combination, honoring + the users request if possible. */ + choose_race_first = FALSE; + if (flags.initrace >= 0 && flags.initrole < 0) { + choose_race_first = TRUE; + } + + while (!validrace(ro,ra)) { + if (choose_race_first) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } else { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } + } + + g = flags.initgend; + if (g == ROLE_NONE) { + g = rn2(ROLE_GENDERS); + fully_specified_role = FALSE; + } + while (!validgend(ro,ra,g)) { + g = rn2(ROLE_GENDERS); + } + + a = flags.initalign; + if (a == ROLE_NONE) { + a = rn2(ROLE_ALIGNS); + fully_specified_role = FALSE; + } + while (!validalign(ro,ra,a)) { + a = rn2(ROLE_ALIGNS); + } + + tmpx = g+1; XawToggleSetCurrent(plsel_gend_radios[0], i2xtp(tmpx)); + tmpx = a+1; XawToggleSetCurrent(plsel_align_radios[0], i2xtp(tmpx)); + tmpx = ra+1; XawToggleSetCurrent(plsel_race_radios[0], i2xtp(tmpx)); + tmpx = ro+1; XawToggleSetCurrent(plsel_role_radios[0], i2xtp(tmpx)); + plsel_set_sensitivities(FALSE); +} + +void +X11_player_selection_setupOthers() +{ + Arg args[2]; + int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0]))-1; + int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0]))-1; + + int valid = -1; + int c = 0; + int j; + int k; + + int gchecked = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0]))-1; + int achecked = xtp2i(XawToggleGetCurrent(plsel_align_radios[0]))-1; + + if (ro < 0 || ra < 0) + return; + + for (j = 0; j < ROLE_GENDERS; j++) { + boolean v = validgend(ro, ra, j); + if (j == gchecked) + c = j; + XtSetArg(args[0], nhStr(XtNsensitive), v); + XtSetValues(plsel_gend_radios[j], args, ONE); + if (valid < 0 && v) + valid = j; + } + if (!validgend(ro, ra, c)) + c = valid; + + k = c+1; + XawToggleSetCurrent(plsel_gend_radios[0], i2xtp(k)); + + valid = -1; + + for (j = 0; j < ROLE_ALIGNS; j++) { + boolean v = validalign(ro, ra, j); + if (j == achecked) + c = j; + XtSetArg(args[0], nhStr(XtNsensitive), v); + XtSetValues(plsel_align_radios[j], args, ONE); + if (valid < 0 && v) + valid = j; + } + if (!validalign(ro, ra, c)) + c = valid; + + k = c+1; + XawToggleSetCurrent(plsel_align_radios[0], i2xtp(k)); +} + +static void +racetoggleCallback(w, client, call) +Widget w; +XtPointer client, call; +{ + Arg args[2]; + int j, valid; + int c = 0; + + int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0]))-1; + int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0]))-1; + + nhUse(w); + nhUse(client); + nhUse(call); + + plsel_set_play_button(ra < 0 || ro < 0); + + if (ra < 0 || ro < 0) + return; + + valid = -1; + + for (j = 0; roles[j].name.m; j++) { + boolean v = validrace(j, ra); + if (j == ro) + c = j; + XtSetArg(args[0], nhStr(XtNsensitive), v); + XtSetValues(plsel_role_radios[j], args, ONE); + if (valid < 0 && v) + valid = j; + } + if (!validrace(c, ra)) + c = valid; + + j = c+1; + XawToggleSetCurrent(plsel_role_radios[0], i2xtp(j)); + + X11_player_selection_setupOthers(); +} + +static void +roletoggleCallback(w, client, call) +Widget w; +XtPointer client, call; +{ + Arg args[2]; + + int j, valid; + int c = 0; + + int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0]))-1; + int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0]))-1; + + nhUse(w); + nhUse(client); + nhUse(call); + + plsel_set_play_button(ra < 0 || ro < 0); + + if (ra < 0 || ro < 0) + return; + + valid = -1; + + for (j = 0; races[j].noun; j++) { + boolean v = validrace(ro, j); + if (j == ra) + c = j; + XtSetArg(args[0], nhStr(XtNsensitive), v); + XtSetValues(plsel_race_radios[j], args, ONE); + if (valid < 0 && v) + valid = j; + } + if (!validrace(ro, c)) + c = valid; + + j = c + 1; + XawToggleSetCurrent(plsel_race_radios[0], i2xtp(j)); + + X11_player_selection_setupOthers(); +} + +static void +gendertoggleCallback(w, client, call) +Widget w; +XtPointer client, call; +{ + int r = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0])) - 1; + + nhUse(w); + nhUse(client); + nhUse(call); + + plsel_set_play_button(r < 0); +} + +static void +aligntoggleCallback(w, client, call) +Widget w; +XtPointer client, call; +{ + int r = xtp2i(XawToggleGetCurrent(plsel_align_radios[0])) - 1; + + nhUse(w); + nhUse(client); + nhUse(call); + + plsel_set_play_button(r < 0); +} + +static void +plsel_random_btn_callback(w, client, call) +Widget w; +XtPointer client; +XtPointer call; +{ + nhUse(w); + nhUse(client); + nhUse(call); + + X11_player_selection_randomize(); +} + +static void +plsel_play_btn_callback(w, client, call) +Widget w; +XtPointer client; +XtPointer call; +{ + nhUse(w); + nhUse(client); + nhUse(call); + + plsel_dialog_acceptvalues(); + exit_x_event = TRUE; /* leave event loop */ +} + +static void +plsel_quit_btn_callback(w, client, call) +Widget w; +XtPointer client; +XtPointer call; +{ + nhUse(w); + nhUse(client); + nhUse(call); + + ps_selected = PS_QUIT; + exit_x_event = TRUE; /* leave event loop */ +} + +Widget +X11_create_player_selection_name(form) +Widget form; +{ + Widget namelabel, name_vp, name_form; + Arg args[10]; + Cardinal num_args; + + /* name viewport */ + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; + XtSetArg(args[num_args], XtNforceBars, False); num_args++; + XtSetArg(args[num_args], XtNallowVert, False); num_args++; + XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; + name_vp = XtCreateManagedWidget("name_vp", viewportWidgetClass, form, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; + name_form = XtCreateManagedWidget("name_form", formWidgetClass, name_vp, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Name"); num_args++; + XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; + + namelabel = XtCreateManagedWidget("name_label", + labelWidgetClass, name_form, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), namelabel); num_args++; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; + XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++; + XtSetArg(args[num_args], nhStr(XtNeditType), + !plsel_ask_name ? XawtextRead : XawtextEdit); num_args++; + XtSetArg(args[num_args], nhStr(XtNresize), XawtextResizeWidth); num_args++; + XtSetArg(args[num_args], nhStr(XtNstring), plname); num_args++; + XtSetArg(args[num_args], XtNinsertPosition, strlen(plname)); num_args++; + XtSetArg(args[num_args], nhStr(XtNaccelerators), + XtParseAcceleratorTable(plsel_input_accelerators)); num_args++; + plsel_name_input = XtCreateManagedWidget("name_input", + asciiTextWidgetClass, name_form, + args, num_args); + + XtInstallAccelerators(plsel_name_input, plsel_name_input); + if (plsel_ask_name) { + XtSetKeyboardFocus(form, plsel_name_input); + } else { + XtSetArg(args[0], nhStr(XtNdisplayCaret), False); + XtSetValues(plsel_name_input, args, ONE); + } + + return name_vp; +} + +void +X11_player_selection_dialog() +{ + Widget popup, popup_vp; + Widget form; + Widget name_vp; + Widget racelabel, race_form, race_vp, race_form2; + Widget rolelabel, role_form, role_vp, role_form2; + Widget gendlabel, gend_form, + gend_radio_m, gend_radio_f, gend_vp, gend_form2; + Widget alignlabel, align_form, + align_radio_l, align_radio_n, align_radio_c, align_vp, align_form2; + Widget btn_vp, btn_form, random_btn, play_btn, quit_btn; + Widget tmpwidget; + Arg args[10]; + Cardinal num_args; + int i; + int winwid = 400; + int cwid = (winwid / 3) - 14; + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + XtSetArg(args[num_args], XtNtitle, "Player Selection"); num_args++; + popup = XtCreatePopupShell("player_selection_dialog", + transientShellWidgetClass, + toplevel, args, num_args); + + num_args = 0; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(plsel_dialog_translations)); num_args++; + popup_vp = XtCreateManagedWidget("plsel_vp", viewportWidgetClass, + popup, args, num_args); + + num_args = 0; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(plsel_dialog_translations)); num_args++; + form = XtCreateManagedWidget("plsel_form", formWidgetClass, popup_vp, + args, num_args); + + name_vp = X11_create_player_selection_name(form); + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, winwid); num_args++; + XtSetValues(name_vp, args, num_args); + + + /********************************************/ + + /* Race */ + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++; + race_form = XtCreateManagedWidget("race_form", formWidgetClass, form, + args, num_args); + + /* race label */ + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Race"); num_args++; + racelabel = XtCreateManagedWidget("race_label", + labelWidgetClass, race_form, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromVert), racelabel); num_args++; + XtSetArg(args[num_args], XtNforceBars, False); num_args++; + XtSetArg(args[num_args], XtNallowVert, True); num_args++; + XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; + race_vp = XtCreateManagedWidget("race_vp", viewportWidgetClass, race_form, + args, num_args); + + num_args = 0; + race_form2 = XtCreateManagedWidget("race_form2", formWidgetClass, race_vp, + args, num_args); + + for (i = 0; races[i].noun; i++) + continue; + plsel_n_races = i; + + plsel_race_radios = (Widget *) alloc(sizeof (Widget) * plsel_n_races); + + /* race radio buttons */ + for (i = 0; races[i].noun; i++) { + Widget racewidget; + + num_args = 0; + if (i > 0) + XtSetArg(args[num_args], nhStr(XtNfromVert), + tmpwidget); num_args++; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + if (i > 0) + XtSetArg(args[num_args], nhStr(XtNradioGroup), + plsel_race_radios[0]); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++; + + racewidget = XtCreateManagedWidget(races[i].noun, + toggleWidgetClass, + race_form2, + args, num_args); + XtAddCallback(racewidget, XtNcallback, racetoggleCallback, i2xtp(i)); + tmpwidget = racewidget; + plsel_race_radios[i] = racewidget; + } + + XawToggleUnsetCurrent(plsel_race_radios[0]); + + /********************************************/ + + /* Role */ + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromHoriz), race_form); num_args++; + role_form = XtCreateManagedWidget("role_form", formWidgetClass, form, + args, num_args); + + /* role label */ + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Role"); num_args++; + rolelabel = XtCreateManagedWidget("role_label", + labelWidgetClass, role_form, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromVert), rolelabel); num_args++; + XtSetArg(args[num_args], XtNforceBars, False); num_args++; + XtSetArg(args[num_args], XtNallowVert, True); num_args++; + XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; + role_vp = XtCreateManagedWidget("role_vp", viewportWidgetClass, role_form, + args, num_args); + + num_args = 0; + role_form2 = XtCreateManagedWidget("role_form2", formWidgetClass, role_vp, + args, num_args); + + for (i = 0; roles[i].name.m; i++) + continue; + plsel_n_roles = i; + + plsel_role_radios = (Widget *)alloc(sizeof(Widget) * plsel_n_roles); + + /* role radio buttons */ + for (i = 0; roles[i].name.m; i++) { + Widget rolewidget; + + num_args = 0; + if (i > 0) + XtSetArg(args[num_args], nhStr(XtNfromVert), + tmpwidget); num_args++; + XtSetArg(args[num_args], nhStr(XtNwidth), cwid); num_args++; + if (i > 0) + XtSetArg(args[num_args], nhStr(XtNradioGroup), + plsel_role_radios[0]); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++; + + rolewidget = XtCreateManagedWidget(roles[i].name.m, + toggleWidgetClass, + role_form2, + args, num_args); + XtAddCallback(rolewidget, XtNcallback, roletoggleCallback, i2xtp(i)); + tmpwidget = rolewidget; + plsel_role_radios[i] = rolewidget; + } + XawToggleUnsetCurrent(plsel_role_radios[0]); + + /********************************************/ + + /* Gender*/ + + plsel_gend_radios = (Widget *)alloc(sizeof(Widget) * ROLE_GENDERS); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++; + gend_form = XtCreateManagedWidget("gender_form", formWidgetClass, form, + args, num_args); + + /* gender label */ + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Gender"); num_args++; + gendlabel = XtCreateManagedWidget("gender_label", + labelWidgetClass, gend_form, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromVert), gendlabel); num_args++; + XtSetArg(args[num_args], XtNforceBars, False); num_args++; + XtSetArg(args[num_args], XtNallowVert, True); num_args++; + XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; + gend_vp = XtCreateManagedWidget("gender_vp", viewportWidgetClass, + gend_form, args, num_args); + + num_args = 0; + gend_form2 = XtCreateManagedWidget("gender_form2", formWidgetClass, + gend_vp, args, num_args); + + /* gender radio buttons */ + num_args = 0; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++; + plsel_gend_radios[0] = gend_radio_m + = XtCreateManagedWidget("Male", toggleWidgetClass, + gend_form2, args, num_args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), gend_radio_m); num_args++; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_gend_radios[0]); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++; + plsel_gend_radios[1] = gend_radio_f + = XtCreateManagedWidget("Female", toggleWidgetClass, + gend_form2, args, num_args); + + XawToggleUnsetCurrent(plsel_gend_radios[0]); + + XtAddCallback(gend_radio_m, XtNcallback, + gendertoggleCallback, (XtPointer) (1)); + XtAddCallback(gend_radio_f, XtNcallback, + gendertoggleCallback, (XtPointer) (2)); + + /********************************************/ + + /* Alignment */ + + plsel_align_radios = (Widget *)alloc(sizeof(Widget) * ROLE_ALIGNS); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), gend_form); num_args++; + XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++; + align_form = XtCreateManagedWidget("align_form", formWidgetClass, form, + args, num_args); + + /* align label */ + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Alignment"); num_args++; + alignlabel = XtCreateManagedWidget("align_label", + labelWidgetClass, align_form, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromVert), alignlabel); num_args++; + XtSetArg(args[num_args], XtNforceBars, False); num_args++; + XtSetArg(args[num_args], XtNallowVert, True); num_args++; + XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; + align_vp = XtCreateManagedWidget("align_vp", viewportWidgetClass, + align_form, args, num_args); + + num_args = 0; + align_form2 = XtCreateManagedWidget("align_form2", formWidgetClass, + align_vp, args, num_args); + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++; + plsel_align_radios[0] = align_radio_l + = XtCreateManagedWidget("Lawful", toggleWidgetClass, + align_form2, args, num_args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_l); num_args++; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_align_radios[0]); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++; + plsel_align_radios[1] = align_radio_n + = XtCreateManagedWidget("Neutral", toggleWidgetClass, + align_form2, args, num_args); + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_n); num_args++; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_align_radios[0]); num_args++; + XtSetArg(args[num_args], nhStr(XtNradioData), 3); num_args++; + plsel_align_radios[2] = align_radio_c = XtCreateManagedWidget("Chaotic", + toggleWidgetClass, + align_form2, + args, num_args); + + XawToggleUnsetCurrent(plsel_align_radios[0]); + + XtAddCallback(align_radio_l, XtNcallback, + aligntoggleCallback, (XtPointer) (1)); + XtAddCallback(align_radio_n, XtNcallback, + aligntoggleCallback, (XtPointer) (2)); + XtAddCallback(align_radio_c, XtNcallback, + aligntoggleCallback, (XtPointer) (3)); + + /********************************************/ + + /* Buttons! */ + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), align_form); num_args++; + XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++; + XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++; + XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++; + XtSetArg(args[num_args], XtNforceBars, False); num_args++; + XtSetArg(args[num_args], XtNallowVert, False); num_args++; + XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; + btn_vp = XtCreateManagedWidget("btn_vp", viewportWidgetClass, form, + args, num_args); + + num_args = 0; + btn_form = XtCreateManagedWidget("btn_form", formWidgetClass, btn_vp, + args, num_args); + + /* "Random" button */ + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Random"); num_args++; + random_btn = XtCreateManagedWidget("random", commandWidgetClass, btn_form, + args, num_args); + XtAddCallback(random_btn, XtNcallback, plsel_random_btn_callback, form); + + /* "Play" button */ + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), random_btn); num_args++; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Play"); num_args++; + plsel_btn_play = play_btn + = XtCreateManagedWidget("play", commandWidgetClass, btn_form, + args, num_args); + XtAddCallback(play_btn, XtNcallback, plsel_play_btn_callback, form); + + /* "Quit" button */ + + num_args = 0; + XtSetArg(args[num_args], nhStr(XtNfromVert), play_btn); num_args++; + XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++; + XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; + XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; + XtSetArg(args[num_args], XtNwidth, cwid); num_args++; + XtSetArg(args[num_args], nhStr(XtNlabel), "Quit"); num_args++; + quit_btn = XtCreateManagedWidget("quit", commandWidgetClass, btn_form, + args, num_args); + XtAddCallback(quit_btn, XtNcallback, plsel_quit_btn_callback, form); + + /********************************************/ + + XtRealizeWidget(popup); + X11_player_selection_randomize(); + + ps_selected = -1; + + nh_XtPopup(popup, (int) XtGrabExclusive, form); + + /* The callback will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); + + nh_XtPopdown(popup); + XtDestroyWidget(popup); + + if (plsel_race_radios) + free(plsel_race_radios); + + if (plsel_role_radios) + free(plsel_role_radios); + + if (plsel_gend_radios) + free(plsel_gend_radios); + + if (plsel_align_radios) + free(plsel_align_radios); + + if (ps_selected == PS_QUIT || program_state.done_hup) { + clearlocks(); + X11_exit_nhwindows((char *) 0); + nh_terminate(0); + } +} + /* Global functions ========================================================= */ void -X11_player_selection() +X11_player_selection_prompts() { int num_roles, num_races, num_gends, num_algns, i, availcount, availindex; Widget popup, player_form; @@ -586,6 +1518,16 @@ X11_player_selection() } } +void +X11_player_selection() +{ + if (iflags.wc_player_selection == VIA_DIALOG) { + X11_player_selection_dialog(); + } else { /* iflags.wc_player_selection == VIA_PROMPTS */ + X11_player_selection_prompts(); + } +} + int X11_get_ext_cmd() { diff --git a/win/win32/mhmain.c b/win/win32/mhmain.c index 48c2ea3a6..f64ab7665 100644 --- a/win/win32/mhmain.c +++ b/win/win32/mhmain.c @@ -104,7 +104,7 @@ register_main_window_class() wcex.hInstance = GetNHApp()->hApp; wcex.hIcon = LoadIcon(GetNHApp()->hApp, (LPCTSTR) IDI_NETHACKW); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); wcex.lpszMenuName = (TCHAR *) IDC_NETHACKW; wcex.lpszClassName = szMainWindowClass; diff --git a/win/win32/mhmenu.c b/win/win32/mhmenu.c index 59004e21f..6bdea0a2f 100644 --- a/win/win32/mhmenu.c +++ b/win/win32/mhmenu.c @@ -254,6 +254,8 @@ mswin_menu_window_select_menu(HWND hWnd, int how, MENU_ITEM_P **_selected, && how != PICK_NONE) { data->menu.prompt[0] = '\0'; SetMenuListType(hWnd, PICK_NONE); + for (i = 0; i < data->menu.size; i++) + data->menu.items[i].count = 0; LayoutMenu(hWnd); } } diff --git a/win/win32/mhstatus.c b/win/win32/mhstatus.c index 5e522b7b1..a362c60bd 100644 --- a/win/win32/mhstatus.c +++ b/win/win32/mhstatus.c @@ -12,6 +12,52 @@ extern COLORREF nhcolor_to_RGB(int c); /* from mhmap */ +typedef struct back_buffer { + HWND hWnd; + HDC hdc; + HBITMAP bm; + HBITMAP orig_bm; + int width; + int height; +} back_buffer_t; + +void back_buffer_free(back_buffer_t * back_buffer) +{ + if (back_buffer->bm != NULL) { + SelectObject(back_buffer->hdc, back_buffer->orig_bm); + DeleteObject(back_buffer->bm); + back_buffer->bm = NULL; + } +} + +void back_buffer_allocate(back_buffer_t * back_buffer, int width, int height) +{ + HDC hdc = GetDC(back_buffer->hWnd); + back_buffer->bm = CreateCompatibleBitmap(hdc, width, height); + back_buffer->orig_bm = SelectObject(back_buffer->hdc, back_buffer->bm); + back_buffer->width = width; + back_buffer->height = height; + ReleaseDC(back_buffer->hWnd, hdc); +} + +void back_buffer_size(back_buffer_t * back_buffer, int width, int height) +{ + if (back_buffer->bm == NULL || back_buffer->width != width + || back_buffer->height != height) { + back_buffer_free(back_buffer); + back_buffer_allocate(back_buffer, width, height); + } +} + +void back_buffer_init(back_buffer_t * back_buffer, HWND hWnd, int width, int height) +{ + back_buffer->hWnd = hWnd; + back_buffer->hdc = CreateCompatibleDC(NULL); + back_buffer->bm = NULL; + + back_buffer_size(back_buffer, width, height); +} + typedef struct mswin_nethack_status_window { int index; char window_text[NHSW_LINES][MAXWINDOWTEXT + 1]; @@ -20,6 +66,7 @@ typedef struct mswin_nethack_status_window { boolean *activefields; int *percents; int *colors; + back_buffer_t back_buffer; } NHStatusWindow, *PNHStatusWindow; static int fieldorder1[] = { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, @@ -45,6 +92,7 @@ mswin_init_status_window() HWND ret; NHStatusWindow *data; RECT rt; + int width, height; if (!run_once) { register_status_window_class(); @@ -59,12 +107,14 @@ mswin_init_status_window() } /* create status window object */ + width = rt.right - rt.left; + height = rt.bottom - rt.top; ret = CreateWindow(szStatusWindowClass, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_SIZEBOX, - rt.left, /* horizontal position of window */ - rt.top, /* vertical position of window */ - rt.right - rt.left, /* window width */ - rt.bottom - rt.top, /* window height */ + rt.left, /* horizontal position of window */ + rt.top, /* vertical position of window */ + width, /* window width */ + height, /* window height */ GetNHApp()->hMainWnd, NULL, GetNHApp()->hApp, NULL); if (!ret) panic("Cannot create status window"); @@ -80,8 +130,20 @@ mswin_init_status_window() ZeroMemory(data, sizeof(NHStatusWindow)); SetWindowLongPtr(ret, GWLP_USERDATA, (LONG_PTR) data); + back_buffer_init(&data->back_buffer, ret, width, height); + mswin_apply_window_style(ret); + if (status_bg_brush == NULL) { + status_bg_color = (COLORREF)GetSysColor(DEFAULT_COLOR_BG_STATUS); + status_bg_brush = CreateSolidBrush(status_bg_color); + } + + if (status_fg_brush == NULL) { + status_fg_color = (COLORREF)GetSysColor(DEFAULT_COLOR_FG_STATUS); + status_fg_brush = CreateSolidBrush(status_fg_color); + } + return ret; } @@ -98,9 +160,7 @@ register_status_window_class() wcex.hInstance = GetNHApp()->hApp; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = status_bg_brush - ? status_bg_brush - : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_STATUS); + wcex.hbrBackground = NULL; wcex.lpszMenuName = NULL; wcex.lpszClassName = szStatusWindowClass; @@ -216,30 +276,35 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) int *f; int **fop; SIZE sz; - HGDIOBJ oldFont, normalFont, boldFont; + HGDIOBJ normalFont, boldFont; TCHAR wbuf[BUFSZ]; - COLORREF OldBg, OldFg, Bg, Fg; RECT rt; PAINTSTRUCT ps; HDC hdc; PNHStatusWindow data; + int width, height; + RECT clear_rect; + HDC front_buffer_hdc; data = (PNHStatusWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA); - hdc = BeginPaint(hWnd, &ps); + front_buffer_hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rt); + width = rt.right - rt.left; + height = rt.bottom - rt.top; + + back_buffer_size(&data->back_buffer, width, height); + + hdc = data->back_buffer.hdc; + normalFont = mswin_get_font(NHW_STATUS, ATR_NONE, hdc, FALSE); boldFont = mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, FALSE); - oldFont = SelectObject(hdc, normalFont); - Bg = status_bg_brush ? status_bg_color - : (COLORREF) GetSysColor(DEFAULT_COLOR_BG_STATUS); - OldBg = SetBkColor(hdc, Bg); + SelectObject(hdc, normalFont); - Fg = status_fg_brush ? status_fg_color - : (COLORREF) GetSysColor(DEFAULT_COLOR_FG_STATUS); - OldFg = SetTextColor(hdc, Fg); + SetBkColor(hdc, status_bg_color); + SetTextColor(hdc, status_fg_color); if (iflags.wc2_hitpointbar && BL_HP < data->n_fields && data->activefields[BL_HP]) { @@ -247,6 +312,13 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) hpbar_color = data->colors[BL_HP] & 0x00ff; } + clear_rect.left = 0; + clear_rect.top = 0; + clear_rect.right = width; + clear_rect.bottom = height; + + FillRect(hdc, &clear_rect, status_bg_brush); + for (fop = fieldorders; *fop; fop++) { LONG left = rt.left; LONG cy = 0; @@ -275,8 +347,10 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) else if (atr & HL_DIM) fntatr = ATR_DIM; fnt = mswin_get_font(NHW_STATUS, fntatr, hdc, FALSE); - nFg = (clr >= 0 && clr < CLR_MAX) ? nhcolor_to_RGB(clr) : Fg; - nBg = Bg; + nFg = (clr == NO_COLOR ? status_fg_color + : ((clr >= 0 && clr < CLR_MAX) ? nhcolor_to_RGB(clr) + : status_fg_color)); + nBg = status_bg_color; GetTextExtentPoint32(hdc, wbuf, vlen, &sz); if (*f == BL_TITLE && iflags.wc2_hitpointbar) { @@ -286,7 +360,7 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) /* first draw title normally */ SelectObject(hdc, fnt); SetBkMode(hdc, OPAQUE); - SetBkColor(hdc, Bg); + SetBkColor(hdc, status_bg_color); SetTextColor(hdc, nhcolor_to_RGB(hpbar_color)); DrawText(hdc, wbuf, vlen, &rt, DT_LEFT); @@ -322,13 +396,13 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) rt.left += sz.cx; cy = max(cy, sz.cy); } + rt.left = left; rt.top += cy; } - SelectObject(hdc, oldFont); - SetTextColor(hdc, OldFg); - SetBkColor(hdc, OldBg); + BitBlt(front_buffer_hdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY); + EndPaint(hWnd, &ps); return 0;