From 50811037f3a8c5456e9178d98112210a99e00311 Mon Sep 17 00:00:00 2001 From: nhmall Date: Thu, 7 Mar 2024 11:01:04 -0500 Subject: [PATCH] split some code into separate files new .h files: hacklib.h selvar.h stairs.h new .c files: calendar.c, getpos.c, report.c, selvar.c, stairs.c, strutil.c, wizcmds.c cleanup of hacklib.c and mdlib.c hacklib contains functions that do not have to link with the core relocate wiz commands from cmd.c to wizcmds.c relocate CRASHREPORT stuff to report.c relocate getpos stuff from do_name.c to getpos.c remove temporary struct definition from extern.h cross-compile PRE-section split into cross-pre1.370 and cross-pre2.370 Windows sys/windows/Makefile.nmake and sys/windows/Makefile.mingw32 and visual studio project file updates Unix sys/unix/Makefile.src, sys/unix/Makefile.utl populate selvar.c and selvar.h build on MS-DOS (not cross-compile) Makefile updates for sys/msdos/Makefile.GCC (untested) vms updates for above (untested) --- include/cstd.h | 1 + include/dungeon.h | 9 - include/extern.h | 309 +-- include/hack.h | 3 +- include/hacklib.h | 83 + include/selvar.h | 11 + include/stairs.h | 18 + src/alloc.c | 30 +- src/calendar.c | 229 ++ src/cmd.c | 1871 +---------------- src/date.c | 11 +- src/do_name.c | 1144 ---------- src/dungeon.c | 230 -- src/end.c | 777 +------ src/getpos.c | 1140 ++++++++++ src/hack.c | 28 + src/hacklib.c | 473 +---- src/mdlib.c | 29 +- src/report.c | 622 ++++++ src/rnd.c | 81 + src/selvar.c | 802 +++++++ src/sp_lev.c | 755 +------ src/stairs.c | 237 +++ src/strutil.c | 158 ++ src/wizcmds.c | 1821 ++++++++++++++++ sys/msdos/Makefile.GCC | 99 +- sys/share/cppregex.cpp | 2 +- sys/unix/Makefile.src | 102 +- sys/unix/Makefile.utl | 43 +- sys/unix/hints/include/cross-post.370 | 6 +- sys/unix/hints/include/cross-pre1.370 | 49 + .../include/{cross-pre.370 => cross-pre2.370} | 45 +- sys/unix/hints/linux.370 | 8 +- sys/unix/hints/macOS.370 | 6 +- sys/vms/Makefile.src | 46 +- sys/vms/vmsbuild.com | 17 +- sys/windows/Makefile.mingw32 | 54 +- sys/windows/Makefile.mingw32.depend | 5 +- sys/windows/Makefile.nmake | 157 +- util/dlb_main.c | 11 +- util/makedefs.c | 23 +- win/share/tile2bmp.c | 2 + win/share/tilemap.c | 43 +- 43 files changed, 5913 insertions(+), 5677 deletions(-) create mode 100644 include/hacklib.h create mode 100644 include/selvar.h create mode 100644 include/stairs.h create mode 100644 src/calendar.c create mode 100644 src/getpos.c create mode 100644 src/report.c create mode 100644 src/selvar.c create mode 100644 src/stairs.c create mode 100644 src/strutil.c create mode 100644 src/wizcmds.c create mode 100644 sys/unix/hints/include/cross-pre1.370 rename sys/unix/hints/include/{cross-pre.370 => cross-pre2.370} (94%) diff --git a/include/cstd.h b/include/cstd.h index 9a22651f6..a0bc025c0 100644 --- a/include/cstd.h +++ b/include/cstd.h @@ -55,6 +55,7 @@ #include #include #include +#include #endif /* !__cplusplus */ #endif /* CSTD_H */ diff --git a/include/dungeon.h b/include/dungeon.h index 82fa2b2db..11efc6053 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -31,15 +31,6 @@ typedef struct s_level { /* special dungeon level element */ d_flags flags; /* type flags */ } s_level; -typedef struct stairway { /* basic stairway identifier */ - coordxy sx, sy; /* x / y location of the stair */ - d_level tolev; /* where does it go */ - boolean up; /* up or down? */ - boolean isladder; /* ladder or stairway? */ - boolean u_traversed; /* hero has traversed this stair */ - struct stairway *next; -} stairway; - /* level region types */ enum level_region_types { LR_DOWNSTAIR = 0, diff --git a/include/extern.h b/include/extern.h index a3347b132..e0a53ffc5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.7 extern.h $NHDT-Date: 1708126520 2024/02/16 23:35:20 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1384 $ */ +/* NetHack 3.7 extern.h $NHDT-Date: 1709675219 2024/03/05 21:46:59 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.1400 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -81,7 +81,8 @@ extern int FITSint_(long long, const char *, int) NONNULLARG2; extern unsigned FITSuint_(unsigned long long, const char *, int) NONNULLARG2; /* for Strlen() which returns unsigned instead of size_t and panics for strings of length INT_MAX (32K - 1) or longer */ -extern unsigned Strlen_(const char *, const char *, int) NONNULLPTRS; + +#include "hacklib.h" /* This next pre-processor directive covers almost the entire file, * interrupted only occasionally to pick up specific functions as needed. */ @@ -281,6 +282,22 @@ extern void all_options_statushilites(strbuf_t *); extern boolean status_hilite_menu(void); #endif /* STATUS_HILITES */ +/* ### calendar.c ### */ + +extern time_t getnow(void); +extern int getyear(void); +#if 0 +extern char *yymmdd(time_t) NONNULL; +#endif +extern long yyyymmdd(time_t); +extern long hhmmss(time_t); +extern char *yyyymmddhhmmss(time_t) NONNULL; +extern time_t time_from_yyyymmddhhmmss(char *); +extern int phase_of_the_moon(void); +extern boolean friday_13th(void); +extern int night(void); +extern int midnight(void); + /* ### coloratt.c ### */ extern char *color_attr_to_str(color_attr *); @@ -403,6 +420,7 @@ extern char yn_function(const char *, const char *, char, boolean); extern char paranoid_ynq(boolean, const char *, boolean); extern boolean paranoid_query(boolean, const char *); extern void makemap_prepost(boolean, boolean); +extern const char *ecname_from_fn(int (*)(void)); /* ### date.c ### */ @@ -594,12 +612,6 @@ extern void heal_legs(int); /* ### do_name.c ### */ -extern char *dxdy_to_dist_descr(coordxy, coordxy, boolean); -extern char *coord_desc(coordxy, coordxy, char *, char) NONNULLARG3; -extern void auto_describe(coordxy, coordxy); -extern boolean getpos_menu(coord *, int) NONNULLARG1; -extern int getpos(coord *, boolean, const char *) NONNULLARG1; -extern void getpos_sethilite(void(*f)(boolean), boolean(*d)(coordxy,coordxy)); extern void new_mgivenname(struct monst *, int) NONNULLARG1; extern void free_mgivenname(struct monst *) NONNULLARG1; extern void new_oname(struct obj *, int) NONNULLARG1; @@ -654,8 +666,6 @@ extern const char *pmname(struct permonst *, int) NONNULLARG1; #endif extern const char *mon_pmname(struct monst *) NONNULLARG1; extern const char *obj_pmname(struct obj *) NONNULLARG1; -extern boolean mapxy_valid(coordxy, coordxy); -extern boolean gather_locs_interesting(coordxy, coordxy, int); /* ### do_wear.c ### */ @@ -802,23 +812,6 @@ extern void next_level(boolean); extern void prev_level(boolean); extern void u_on_newpos(coordxy, coordxy); extern void u_on_rndspot(int); -extern void stairway_add(coordxy, coordxy, - boolean, boolean, d_level *) NONNULLPTRS; -extern void stairway_print(void); -extern void stairway_free_all(void); -extern stairway *stairway_at(coordxy, coordxy); -extern stairway *stairway_find(d_level *) NONNULLARG1; -extern stairway *stairway_find_from(d_level *, boolean) NONNULLARG1; -extern stairway *stairway_find_dir(boolean); -extern stairway *stairway_find_type_dir(boolean, boolean); -extern stairway *stairway_find_special_dir(boolean); -extern void u_on_sstairs(int); -extern void u_on_upstairs(void); -extern void u_on_dnstairs(void); -extern boolean On_stairs(coordxy, coordxy); -extern boolean On_ladder(coordxy, coordxy); -extern boolean On_stairs_up(coordxy, coordxy); -extern boolean On_stairs_dn(coordxy, coordxy); extern void get_level(d_level *, int) NONNULLARG1; extern boolean Is_botlevel(d_level *) NONNULLARG1; extern boolean Can_fall_thru(d_level *) NONNULLARG1; @@ -845,8 +838,6 @@ extern unsigned int induced_align(int); extern boolean Invocation_lev(d_level *) NONNULLARG1; extern xint16 level_difficulty(void); extern schar lev_by_name(const char *); -extern boolean known_branch_stairs(stairway *); -extern char *stairs_description(stairway *, char *, boolean) NONNULLARG1; extern schar print_dungeon(boolean, schar *, xint16 *); extern void print_level_annotation(void); extern int donamelevel(void); @@ -914,6 +905,7 @@ extern void done1(int); extern int done2(void); extern void done_in_by(struct monst *, int) NONNULLARG1; extern void done_object_cleanup(void); +extern void NH_abort(char *); #endif /* !MAKEDEFS_C && MDLIB_C && !CPPREGEX_C */ #if !defined(CPPREGEX_C) ATTRNORETURN extern void panic(const char *, ...) PRINTF_F(1, 2) NORETURN; @@ -927,17 +919,7 @@ extern struct kinfo *find_delayed_killer(int); extern void dealloc_killer(struct kinfo *); extern void save_killers(NHFILE *) NONNULLARG1; extern void restore_killers(NHFILE *) NONNULLARG1; -#ifdef CRASHREPORT -extern boolean submit_web_report(int, const char *, const char *); -extern void crashreport_init(int, char *[]); -extern void crashreport_bidshow(void); -extern boolean swr_add_uricoded(const char *, char **, int *, char *); -extern int dobugreport(void); -#endif extern char *build_english_list(char *) NONNULLARG1; -#if defined(PANICTRACE) && !defined(NO_SIGNAL) -extern void panictrace_setsignals(boolean); -#endif /* ### engrave.c ### */ @@ -1084,6 +1066,17 @@ extern void drinksink(void); extern void dipsink(struct obj *) NONNULLARG1; extern void sink_backs_up(coordxy, coordxy); +/* ### getpos.c ### */ + +extern char *dxdy_to_dist_descr(coordxy, coordxy, boolean); +extern char *coord_desc(coordxy, coordxy, char *, char) NONNULLARG3; +extern void auto_describe(coordxy, coordxy); +extern boolean getpos_menu(coord *, int) NONNULLARG1; +extern int getpos(coord *, boolean, const char *) NONNULLARG1; +extern void getpos_sethilite(void(*f)(boolean), boolean(*d)(coordxy,coordxy)); +extern boolean mapxy_valid(coordxy, coordxy); +extern boolean gather_locs_interesting(coordxy, coordxy, int); + /* ### hack.c ### */ extern boolean is_valid_travelpt(coordxy, coordxy); @@ -1143,96 +1136,21 @@ extern int inv_cnt(boolean); /* sometimes money_cnt(gi.invent) which can be null */ extern long money_cnt(struct obj *) NO_NNARGS; extern void spot_checks(coordxy, coordxy, schar); - -/* ### hacklib.c ### */ - -extern boolean digit(char); -extern boolean letter(char); -extern char highc(char); -extern char lowc(char); -extern char *lcase(char *) NONNULL NONNULLARG1; -extern char *ucase(char *) NONNULL NONNULLARG1; -extern char *upstart(char *); /* ought to be changed to NONNULL NONNULLARG1 - * and the code changed to not allow NULL arg */ -extern char *upwords(char *) NONNULL NONNULLARG1; -extern char *mungspaces(char *) NONNULL NONNULLARG1; -extern char *trimspaces(char *) NONNULL NONNULLARG1; -extern char *strip_newline(char *) NONNULL NONNULLARG1; -extern char *eos(char *) NONNULL NONNULLARG1; -extern const char *c_eos(const char *) NONNULL NONNULLARG1; -extern unsigned Strlen_(const char *, const char *, int) NONNULLPTRS; -extern boolean str_start_is(const char *, const char *, boolean) NONNULLPTRS; -extern boolean str_end_is(const char *, const char *) NONNULLPTRS; -extern int str_lines_maxlen(const char *); -extern char *strkitten(char *, char) NONNULL NONNULLARG1; -extern void copynchars(char *, const char *, int) NONNULLARG12; -extern char chrcasecpy(int, int); -extern char *strcasecpy(char *, const char *) NONNULL NONNULLPTRS; -extern char *s_suffix(const char *) NONNULL NONNULLARG1; -extern char *ing_suffix(const char *) NONNULL NONNULLARG1; -extern char *xcrypt(const char *, char *) NONNULL NONNULLPTRS; -extern boolean onlyspace(const char *) NONNULLARG1; -extern char *tabexpand(char *) NONNULL NONNULLARG1; -extern char *visctrl(char) NONNULL; -extern char *stripchars(char *, const char *, - const char *) NONNULL NONNULLPTRS; -extern char *stripdigits(char *) NONNULL NONNULLARG1; -extern char *strsubst(char *, const char *, const char *) NONNULL NONNULLPTRS; -extern int strNsubst(char *, const char *, const char *, int) NONNULLPTRS; -extern const char *findword(const char *, const char *, int, - boolean) NONNULLARG2; -extern const char *ordin(int) NONNULL; -extern char *sitoa(int) NONNULL; -extern int sgn(int); extern int rounddiv(long, int); -extern int dist2(coordxy, coordxy, coordxy, coordxy); -extern int isqrt(int); -extern int distmin(coordxy, coordxy, coordxy, coordxy); -extern boolean online2(coordxy, coordxy, coordxy, coordxy); -extern boolean pmatch(const char *, const char *) NONNULLPTRS; -extern boolean pmatchi(const char *, const char *) NONNULLPTRS; -/* -extern boolean pmatchz(const char *, const char *) NONNULLPTRS; -*/ -#ifndef STRNCMPI -extern int strncmpi(const char *, const char *, int) NONNULLPTRS; -#endif -#ifndef STRSTRI -extern char *strstri(const char *, const char *) NONNULLPTRS; -#endif -extern boolean fuzzymatch(const char *, const char *, - const char *, boolean) NONNULLPTRS; -extern void init_random(int(*fn)(int)); -extern void reseed_random(int(*fn)(int)); -extern time_t getnow(void); -extern int getyear(void); -#if 0 -extern char *yymmdd(time_t) NONNULL; -#endif -extern long yyyymmdd(time_t); -extern long hhmmss(time_t); -extern char *yyyymmddhhmmss(time_t) NONNULL; -extern time_t time_from_yyyymmddhhmmss(char *); -extern int phase_of_the_moon(void); -extern boolean friday_13th(void); -extern int night(void); -extern int midnight(void); + +/* ### strutil.c ### */ + extern void strbuf_init(strbuf_t *) NONNULLARG1; extern void strbuf_append(strbuf_t *, const char *) NONNULLPTRS; extern void strbuf_reserve(strbuf_t *, int) NONNULLARG1; extern void strbuf_empty(strbuf_t *) NONNULLARG1; extern void strbuf_nl_to_crlf(strbuf_t *) NONNULLARG1; -extern int swapbits(int, int, int); -extern void shuffle_int_array(int *, int) NONNULLARG1; -/* note: the snprintf CPP wrapper includes the "fmt" argument in "..." - (__VA_ARGS__) to allow for zero arguments after fmt */ -#define Snprintf(str, size, ...) \ - nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) -extern void nh_snprintf(const char *func, int line, char *str, size_t size, - const char *fmt, ...) PRINTF_F(5, 6); -#ifdef ENHANCED_SYMBOLS -extern int unicodeval_to_utf8str(int, uint8 *, size_t) NONNULLARG2; -#endif +extern unsigned Strlen_(const char *, const char *, int) NONNULLPTRS; +extern boolean pmatch(const char *, const char *) NONNULLPTRS; +extern boolean pmatchi(const char *, const char *) NONNULLPTRS; +/* +extern boolean pmatchz(const char *, const char *) NONNULLPTRS; +*/ /* ### insight.c ### */ @@ -2601,6 +2519,26 @@ extern NhRegion *create_gas_cloud_selection(struct selectionvar *, int); extern boolean region_danger(void); extern void region_safety(void); +/* ### report.c ### */ + +#ifdef CRASHREPORT +extern const char *get_saved_pline(int); +extern boolean submit_web_report(int, const char *, const char *); +extern boolean submit_web_report(int, const char *, const char *); +extern void crashreport_init(int, char *[]); +extern void crashreport_bidshow(void); +extern boolean swr_add_uricoded(const char *, char **, int *, char *); +extern int dobugreport(void); +# ifndef NO_SIGNAL +extern void panictrace_handler(int); +# endif +extern boolean NH_panictrace_libc(void); +extern boolean NH_panictrace_gdb(void); +#endif +#if defined(PANICTRACE) && !defined(NO_SIGNAL) +extern void panictrace_setsignals(boolean); +#endif + /* ### restore.c ### */ extern void inven_inuse(boolean); @@ -2637,6 +2575,9 @@ extern int rnd(int); extern int d(int, int); extern int rne(int); extern int rnz(int); +extern void init_random(int(*fn)(int)); +extern void reseed_random(int(*fn)(int)); +extern void shuffle_int_array(int *, int) NONNULLARG1; /* ### role.c ### */ @@ -2715,6 +2656,42 @@ extern FILE *getlog(NHFILE *); extern void closelog(NHFILE *); #endif +/* ### selvar.c ### */ + +extern struct selectionvar *selection_new(void); +extern void selection_free(struct selectionvar *, boolean) NO_NNARGS; +extern void selection_clear(struct selectionvar *, int) NONNULLARG1; +extern struct selectionvar *selection_clone(struct selectionvar *) NONNULLARG1; +extern void selection_getbounds(struct selectionvar *, NhRect *) NO_NNARGS; +extern void selection_recalc_bounds(struct selectionvar *) NONNULLARG1; +extern coordxy selection_getpoint(coordxy, coordxy, struct selectionvar *) NO_NNARGS; +extern void selection_setpoint(coordxy, coordxy, struct selectionvar *, int); +extern struct selectionvar * selection_not(struct selectionvar *); +extern struct selectionvar *selection_filter_percent(struct selectionvar *, + int); +extern struct selectionvar *selection_filter_mapchar(struct selectionvar *, + xint16, int); +extern int selection_rndcoord(struct selectionvar *, coordxy *, coordxy *, + boolean); +extern void selection_do_grow(struct selectionvar *, int); +extern void set_selection_floodfillchk(int(*)(coordxy, coordxy)); +extern void selection_floodfill(struct selectionvar *, coordxy, coordxy, + boolean); +extern void selection_do_ellipse(struct selectionvar *, int, int, int, int, + int); +extern void selection_do_gradient(struct selectionvar *, long, long, long, + long, long, long, long); +extern void selection_do_line(coordxy, coordxy, coordxy, coordxy, + struct selectionvar *); +extern void selection_do_randline(coordxy, coordxy, coordxy, coordxy, + schar, schar, struct selectionvar *); +extern void selection_iterate(struct selectionvar *, select_iter_func, + genericptr_t); +extern boolean selection_is_irregular(struct selectionvar *); +extern char *selection_size_description(struct selectionvar *, char *); +extern struct selectionvar *selection_from_mkroom(struct mkroom *) NO_NNARGS; +extern void selection_force_newsyms(struct selectionvar *) NONNULLARG1; + /* ### sfstruct.c ### */ extern boolean close_check(int); @@ -2869,6 +2846,7 @@ extern void sound_speak(const char *) NO_NNARGS; /* ### sp_lev.c ### */ #if !defined(CROSSCOMPILE) || defined(CROSSCOMPILE_TARGET) +extern boolean match_maptyps(xint16, xint16); extern void create_des_coder(void); extern void reset_xystart_size(void); extern struct mapfragment *mapfrag_fromstr(char *) NONNULLARG1; @@ -2886,37 +2864,11 @@ extern boolean dig_corridor(coord *, coord *, boolean, schar, schar) NONNULLARG1 extern void fill_special_room(struct mkroom *) NO_NNARGS; extern void wallify_map(coordxy, coordxy, coordxy, coordxy); extern boolean load_special(const char *) NONNULLARG1; -extern coordxy selection_getpoint(coordxy, coordxy, struct selectionvar *) NO_NNARGS; -extern struct selectionvar *selection_new(void); -extern void selection_free(struct selectionvar *, boolean) NO_NNARGS; -extern void selection_clear(struct selectionvar *, int) NONNULLARG1; -extern struct selectionvar *selection_clone(struct selectionvar *) NONNULLARG1; -extern void selection_getbounds(struct selectionvar *, NhRect *) NO_NNARGS; -extern void selection_recalc_bounds(struct selectionvar *) NONNULLARG1; -extern void set_selection_floodfillchk(int(*)(coordxy, coordxy)); -extern void selection_floodfill(struct selectionvar *, coordxy, coordxy, - boolean); +extern coordxy random_wdir(void); extern boolean pm_good_location(coordxy, coordxy, struct permonst *) NONNULLARG3; extern void get_location_coord(coordxy *, coordxy *, int, struct mkroom *, long) NONNULLARG12; -extern void selection_setpoint(coordxy, coordxy, struct selectionvar *, int); -extern struct selectionvar * selection_not(struct selectionvar *); -extern struct selectionvar *selection_filter_percent(struct selectionvar *, - int); -extern int selection_rndcoord(struct selectionvar *, coordxy *, coordxy *, - boolean); -extern void selection_do_grow(struct selectionvar *, int); -extern void selection_do_line(coordxy, coordxy, coordxy, coordxy, - struct selectionvar *); -extern void selection_do_randline(coordxy, coordxy, coordxy, coordxy, - schar, schar, struct selectionvar *); -extern struct selectionvar *selection_filter_mapchar(struct selectionvar *, - xint16, int); extern void set_floodfillchk_match_under(coordxy); -extern void selection_do_ellipse(struct selectionvar *, int, int, int, int, - int); -extern void selection_do_gradient(struct selectionvar *, long, long, long, - long, long, long, long); extern int lspo_reset_level(lua_State *) NO_NNARGS; /* wiz_load_splua NULL */ /* lspo_finalize_level() has tests for whether arg1 L is null, and chooses code paths to follow based on that. Also preventing NONNULLARG1 is it @@ -2927,7 +2879,6 @@ extern boolean get_coord(lua_State *, int, lua_Integer *, lua_Integer *) NONNULL extern void cvt_to_abscoord(coordxy *, coordxy *) NONNULLPTRS; extern void cvt_to_relcoord(coordxy *, coordxy *) NONNULLPTRS; extern int nhl_abs_coord(lua_State *) NONNULLARG1; -extern struct selectionvar *selection_from_mkroom(struct mkroom *) NO_NNARGS; extern void update_croom(void); extern const char *get_trapname_bytype(int); extern void l_register_des(lua_State *) NONNULLARG1; @@ -2954,6 +2905,27 @@ extern char force_learn_spell(short); extern int num_spells(void); extern void skill_based_spellbook_id(void); +/* ### stairs.c ### */ + +extern void stairway_add(coordxy, coordxy, + boolean, boolean, d_level *) NONNULLPTRS; +extern void stairway_free_all(void); +extern stairway *stairway_at(coordxy, coordxy); +extern stairway *stairway_find(d_level *) NONNULLARG1; +extern stairway *stairway_find_from(d_level *, boolean) NONNULLARG1; +extern stairway *stairway_find_dir(boolean); +extern stairway *stairway_find_type_dir(boolean, boolean); +extern stairway *stairway_find_special_dir(boolean); +extern void u_on_sstairs(int); +extern void u_on_upstairs(void); +extern void u_on_dnstairs(void); +extern boolean On_stairs(coordxy, coordxy); +extern boolean On_ladder(coordxy, coordxy); +extern boolean On_stairs_up(coordxy, coordxy); +extern boolean On_stairs_dn(coordxy, coordxy); +extern boolean known_branch_stairs(stairway *); +extern char *stairs_description(stairway *, char *, boolean) NONNULLARG1; + /* ### steal.c ### */ extern long somegold(long); @@ -3684,6 +3656,41 @@ extern void intervene(void); extern void wizdead(void); extern void cuss(struct monst *) NONNULLARG1; +/* ### wizcmds.c ### */ + +extern int wiz_detect(void); +extern int wiz_flip_level(void); +extern int wiz_fuzzer(void); +extern int wiz_genesis(void); +extern int wiz_identify(void); +extern int wiz_intrinsic(void); +extern int wiz_kill(void); +extern int wiz_level_change(void); +extern int wiz_level_tele(void); +extern int wiz_load_lua(void); +extern int wiz_load_splua(void); +extern int wiz_makemap(void); +extern int wiz_map(void); +extern int wiz_migrate_mons(void); +extern int wiz_panic(void); +extern int wiz_polyself(void); +extern int wiz_rumor_check(void); +extern int wiz_show_seenv(void); +extern int wiz_show_stats(void); +extern int wiz_show_vision(void); +extern int wiz_show_wmodes(void); +extern int wiz_smell(void); +extern int wiz_telekinesis(void); +extern int wiz_where(void); +extern int wiz_wish(void); +extern void makemap_remove_mons(void); +extern void wiz_levltyp_legend(void); +extern void wiz_map_levltyp(void); +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) +extern int wiz_display_macros(void); +extern int wiz_mon_diff(void); +#endif + /* ### worm.c ### */ extern int get_wormno(void); diff --git a/include/hack.h b/include/hack.h index 47ccf5080..9d8522014 100644 --- a/include/hack.h +++ b/include/hack.h @@ -13,6 +13,7 @@ #include "align.h" #include "dungeon.h" +#include "stairs.h" #include "objclass.h" #include "wintype.h" #include "flag.h" @@ -24,7 +25,6 @@ #include "botl.h" #include "context.h" -#include "dungeon.h" #include "engrave.h" #include "mkroom.h" #include "obj.h" @@ -32,6 +32,7 @@ #include "rect.h" #include "region.h" #include "rm.h" +#include "selvar.h" #include "sndprocs.h" #include "spell.h" #include "sym.h" diff --git a/include/hacklib.h b/include/hacklib.h new file mode 100644 index 000000000..79e86585a --- /dev/null +++ b/include/hacklib.h @@ -0,0 +1,83 @@ +/* NetHack 3.7 hacklib.h $NHDT-Date: 1657918089 2022/07/15 20:48:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.1132 $ */ +/* Copyright (c) Steve Creps, 1988. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef HACKLIB_H +#define HACKLIB_H + +/* + * hacklib true library functions + */ +extern boolean digit(char); +extern boolean letter(char); +extern char highc(char); +extern char lowc(char); +extern char *lcase(char *) NONNULL NONNULLARG1; +extern char *ucase(char *) NONNULL NONNULLARG1; +extern char *upstart(char *); /* ought to be changed to NONNULL NONNULLARG1 + * and the code changed to not allow NULL arg */ +extern char *upwords(char *) NONNULL NONNULLARG1; +extern char *mungspaces(char *) NONNULL NONNULLARG1; +extern char *trimspaces(char *) NONNULL NONNULLARG1; +extern char *strip_newline(char *) NONNULL NONNULLARG1; +extern char *eos(char *) NONNULL NONNULLARG1; +extern const char *c_eos(const char *) NONNULL NONNULLARG1; +extern boolean str_start_is(const char *, const char *, boolean) NONNULLPTRS; +extern boolean str_end_is(const char *, const char *) NONNULLPTRS; +extern int str_lines_maxlen(const char *); +extern char *strkitten(char *, char) NONNULL NONNULLARG1; +extern void copynchars(char *, const char *, int) NONNULLARG12; +extern char chrcasecpy(int, int); +extern char *strcasecpy(char *, const char *) NONNULL NONNULLPTRS; +extern char *s_suffix(const char *) NONNULL NONNULLARG1; +extern char *ing_suffix(const char *) NONNULL NONNULLARG1; +extern char *xcrypt(const char *, char *) NONNULL NONNULLPTRS; +extern boolean onlyspace(const char *) NONNULLARG1; +extern char *tabexpand(char *) NONNULL NONNULLARG1; +extern char *visctrl(char) NONNULL; +extern char *stripchars(char *, const char *, const char *) NONNULL NONNULLPTRS; +extern char *stripdigits(char *) NONNULL NONNULLARG1; +extern char *strsubst(char *, const char *, const char *) NONNULL NONNULLPTRS; +extern int strNsubst(char *, const char *, const char *, int) NONNULLPTRS; +extern const char *findword(const char *, const char *, int, + boolean) NONNULLARG2; +extern const char *ordin(int) NONNULL; +extern char *sitoa(int) NONNULL; +extern int sgn(int); +extern int distmin(coordxy, coordxy, coordxy, coordxy); +extern int dist2(coordxy, coordxy, coordxy, coordxy); +extern int isqrt(int); +extern boolean online2(coordxy, coordxy, coordxy, coordxy); +#ifndef STRNCMPI +extern int strncmpi(const char *, const char *, int) NONNULLPTRS; +#endif +#ifndef STRSTRI +extern char *strstri(const char *, const char *) NONNULLPTRS; +#endif +#define FITSint(x) FITSint_(x, __func__, __LINE__) +extern int FITSint_(long long, const char *, int); +#define FITSuint(x) FITSuint_(x, __func__, __LINE__) +extern unsigned FITSuint_(unsigned long long, const char *, int); +extern int case_insensitive_comp(const char *, const char *); +extern boolean fuzzymatch(const char *, const char *, + const char *, boolean) NONNULLPTRS; +extern int swapbits(int, int, int); +/* note: the snprintf CPP wrapper includes the "fmt" argument in "..." + (__VA_ARGS__) to allow for zero arguments after fmt */ +extern void nh_snprintf(const char *func, int line, char *str, + size_t size, const char *fmt, ...) PRINTF_F(5, 6); +extern void nh_snprintf_w_impossible(const char *func, int line, char *str, + size_t size, const char *fmt, ...) PRINTF_F(5, 6); + +#define Snprintf(str, size, ...) \ + nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) + +#if 0 +/*#define Strlen(s) Strlen_(s, __func__, __LINE__)*/ +extern unsigned Strlen_(const char *, const char *, int) NONNULLPTRS; +#endif +extern int unicodeval_to_utf8str(int, uint8 *, size_t); + + +#endif /* HACKLIB_H */ + diff --git a/include/selvar.h b/include/selvar.h new file mode 100644 index 000000000..2551efa34 --- /dev/null +++ b/include/selvar.h @@ -0,0 +1,11 @@ +/* NetHack 3.7 selvar.h $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.0 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SELVAR_H +#define SELVAR_H + +typedef void (*select_iter_func)(coordxy, coordxy, genericptr); + +#endif /* SELVAR_H */ + diff --git a/include/stairs.h b/include/stairs.h new file mode 100644 index 000000000..bb37a4dee --- /dev/null +++ b/include/stairs.h @@ -0,0 +1,18 @@ +/* NetHack 3.7 stairs.h $NHDT-Date: 1685863327 2023/06/04 07:22:07 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.47 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef STAIRS_H +#define STAIRS_H + +typedef struct stairway { /* basic stairway identifier */ + coordxy sx, sy; /* x / y location of the stair */ + d_level tolev; /* where does it go */ + boolean up; /* up or down? */ + boolean isladder; /* ladder or stairway? */ + boolean u_traversed; /* hero has traversed this stair */ + struct stairway *next; +} stairway; + +#endif /* STAIRS_H */ + diff --git a/src/alloc.c b/src/alloc.c index 84566719a..796ab7ade 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -13,17 +13,11 @@ #include "nhlua.h" #endif -/* - * Some stuff that isn't allocation related but included is this file - * so that utility programs can access it more easily since they link - * with alloc.{o,obj}. - */ + /*#define FITSint(x) FITSint_(x, __func__, __LINE__)*/ extern int FITSint_(LUA_INTEGER, const char *, int) NONNULLARG2; /*#define FITSuint(x) FITSuint_(x, __func__, __LINE__)*/ extern unsigned FITSuint_(unsigned long long, const char *, int) NONNULLARG2; -/*#define Strlen(s) Strlen_(s, __func__, __LINE__)*/ -extern unsigned Strlen_(const char *, const char *, int) NONNULLPTRS; char *fmt_ptr(const genericptr) NONNULL; @@ -256,6 +250,7 @@ dupstr_n(const char *string, unsigned int *lenout) return strcpy((char *) alloc(len + 1), string); } + /* cast to int or panic on overflow; use via macro */ int FITSint_(LUA_INTEGER i, const char *file, int line) @@ -277,25 +272,4 @@ FITSuint_(unsigned long long ull, const char *file, int line) return uret; } -/* strlen() but returns unsigned and panics if string is unreasonably long; - used by dlb as well as by nethack */ -unsigned -Strlen_( - const char *str, - const char *file, - int line) -{ - const char *p; - size_t len; - - /* strnlen(str, LARGEST_INT) w/o requiring posix.1 headers or libraries */ - for (p = str, len = 0; len < LARGEST_INT; ++len) - if (*p++ == '\0') - break; - - if (len == LARGEST_INT) - panic("%s:%d string too long", file, line); - return (unsigned) len; -} - /*alloc.c*/ diff --git a/src/calendar.c b/src/calendar.c new file mode 100644 index 000000000..9021881df --- /dev/null +++ b/src/calendar.c @@ -0,0 +1,229 @@ +/* NetHack 3.7 calendar.c $NHDT-Date: 1706213796 2024/01/25 20:16:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.116 $ */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/*-Copyright (c) Michael Allison, 2007. */ +/* Copyright (c) Robert Patrick Rankin, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* + * Time routines + * + * The time is used for: + * - seed for rand() + * - year on tombstone and yyyymmdd in record file + * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON) + * - night and midnight (the undead are dangerous at midnight) + * - determination of what files are "very old" + */ + +/* TIME_type: type of the argument to time(); we actually use &(time_t); + you might need to define either or both of these to 'long *' in *conf.h */ +#ifndef TIME_type +#define TIME_type time_t * +#endif +#ifndef LOCALTIME_type +#define LOCALTIME_type time_t * +#endif + +static struct tm *getlt(void); + +time_t +getnow(void) +{ + time_t datetime = 0; + + (void) time((TIME_type) &datetime); + return datetime; +} + +static struct tm * +getlt(void) +{ + time_t date = getnow(); + + return localtime((LOCALTIME_type) &date); +} + +int +getyear(void) +{ + return (1900 + getlt()->tm_year); +} + + +long +yyyymmdd(time_t date) +{ + long datenum; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else + lt = localtime((LOCALTIME_type) &date); + + /* just in case somebody's localtime supplies (year % 100) + rather than the expected (year - 1900) */ + if (lt->tm_year < 70) + datenum = (long) lt->tm_year + 2000L; + else + datenum = (long) lt->tm_year + 1900L; + /* yyyy --> yyyymm */ + datenum = datenum * 100L + (long) (lt->tm_mon + 1); + /* yyyymm --> yyyymmdd */ + datenum = datenum * 100L + (long) lt->tm_mday; + return datenum; +} + +long +hhmmss(time_t date) +{ + long timenum; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else + lt = localtime((LOCALTIME_type) &date); + + timenum = lt->tm_hour * 10000L + lt->tm_min * 100L + lt->tm_sec; + return timenum; +} + +char * +yyyymmddhhmmss(time_t date) +{ + long datenum; + static char datestr[15]; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else + lt = localtime((LOCALTIME_type) &date); + + /* just in case somebody's localtime supplies (year % 100) + rather than the expected (year - 1900) */ + if (lt->tm_year < 70) + datenum = (long) lt->tm_year + 2000L; + else + datenum = (long) lt->tm_year + 1900L; + Snprintf(datestr, sizeof datestr, "%04ld%02d%02d%02d%02d%02d", + datenum, lt->tm_mon + 1, + lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); + //debugpline1("yyyymmddhhmmss() produced date string %s", datestr); + return datestr; +} + +time_t +time_from_yyyymmddhhmmss(char *buf) +{ + int k; + time_t timeresult = (time_t) 0; + struct tm t, *lt; + char *d, *p, y[5], mo[3], md[3], h[3], mi[3], s[3]; + + if (buf && strlen(buf) == 14) { + d = buf; + p = y; /* year */ + for (k = 0; k < 4; ++k) + *p++ = *d++; + *p = '\0'; + p = mo; /* month */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = md; /* day */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = h; /* hour */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = mi; /* minutes */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + p = s; /* seconds */ + for (k = 0; k < 2; ++k) + *p++ = *d++; + *p = '\0'; + lt = getlt(); + if (lt) { + t = *lt; + t.tm_year = atoi(y) - 1900; + t.tm_mon = atoi(mo) - 1; + t.tm_mday = atoi(md); + t.tm_hour = atoi(h); + t.tm_min = atoi(mi); + t.tm_sec = atoi(s); + timeresult = mktime(&t); + } + if (timeresult == (time_t) -1) + ; +#if 0 +TODO: set_debugpline1, debugpline1 -> function pointer + debugpline1("time_from_yyyymmddhhmmss(%s) would have returned -1", + buf ? buf : ""); +#endif + else + return timeresult; + } + return (time_t) 0; +} + +/* + * moon period = 29.53058 days ~= 30, year = 365.2422 days + * days moon phase advances on first day of year compared to preceding year + * = 365.2422 - 12*29.53058 ~= 11 + * years in Metonic cycle (time until same phases fall on the same days of + * the month) = 18.6 ~= 19 + * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 + * (29 as initial condition) + * current phase in days = first day phase + days elapsed in year + * 6 moons ~= 177 days + * 177 ~= 8 reported phases * 22 + * + 11/22 for rounding + */ +int +phase_of_the_moon(void) /* 0-7, with 0: new, 4: full */ +{ + struct tm *lt = getlt(); + int epact, diy, goldn; + + diy = lt->tm_yday; + goldn = (lt->tm_year % 19) + 1; + epact = (11 * goldn + 18) % 30; + if ((epact == 25 && goldn > 11) || epact == 24) + epact++; + + return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7); +} + +boolean +friday_13th(void) +{ + struct tm *lt = getlt(); + + /* tm_wday (day of week; 0==Sunday) == 5 => Friday */ + return (boolean) (lt->tm_wday == 5 && lt->tm_mday == 13); +} + +int +night(void) +{ + int hour = getlt()->tm_hour; + + return (hour < 6 || hour > 21); +} + +int +midnight(void) +{ + return (getlt()->tm_hour == 0); +} + +/* calendar.c */ + diff --git a/src/cmd.c b/src/cmd.c index e21f06540..6b066ea6b 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 cmd.c $NHDT-Date: 1707547723 2024/02/10 06:48:43 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.704 $ */ +/* NetHack 3.7 cmd.c $NHDT-Date: 1709675219 2024/03/05 21:46:59 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.711 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -15,12 +15,6 @@ #define NR_OF_EOFS 20 #endif #endif -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) -static int wiz_display_macros(void); -static int wiz_mon_diff(void); -#endif -static int wiz_load_splua(void); -static int wiz_telekinesis(void); #ifdef DUMB /* stuff commented out in extern.h, but needed here */ extern int doapply(void); /**/ @@ -100,7 +94,6 @@ extern int dozap(void); /**/ extern int doorganize(void); /**/ #endif /* DUMB */ -static const char *ecname_from_fn(int (*)(void)); static int dosuspend_core(void); static int dosh_core(void); static int doherecmdmenu(void); @@ -116,53 +109,12 @@ static int doterrain(void); static boolean u_have_seen_whole_selection(struct selectionvar *); static boolean u_have_seen_bounds_selection(struct selectionvar *); static boolean u_can_see_whole_selection(struct selectionvar *); -static boolean selection_is_irregular(struct selectionvar *); -static char *selection_size_description(struct selectionvar *, char *); static int dolookaround_floodfill_findroom(coordxy, coordxy); static void lookaround_known_room(coordxy, coordxy); -static int wiz_wish(void); -static int wiz_identify(void); -static int wiz_map(void); -static int wiz_makemap(void); -static int wiz_genesis(void); -static int wiz_where(void); -static int wiz_detect(void); -static int wiz_panic(void); -static int wiz_fuzzer(void); -static int wiz_polyself(void); -static int wiz_kill(void); -static int wiz_load_lua(void); -static int wiz_level_tele(void); -static int wiz_level_change(void); -static int wiz_flip_level(void); -static int wiz_show_seenv(void); -static int wiz_show_vision(void); -static int wiz_smell(void); -static int wiz_intrinsic(void); -static int wiz_show_wmodes(void); -static int wiz_show_stats(void); -static int wiz_rumor_check(void); -static int wiz_migrate_mons(void); -static void makemap_unmakemon(struct monst *, boolean); -static void makemap_remove_mons(void); -static void wiz_map_levltyp(void); -static void wiz_levltyp_legend(void); #if defined(__BORLANDC__) && !defined(_WIN32) extern void show_borlandc_stats(winid); #endif -static int size_monst(struct monst *, boolean); -static int size_obj(struct obj *); -static void count_obj(struct obj *, long *, long *, boolean, boolean); -static void obj_chain(winid, const char *, struct obj *, boolean, long *, - long *); -static void mon_invent_chain(winid, const char *, struct monst *, long *, - long *); -static void mon_chain(winid, const char *, struct monst *, boolean, long *, - long *); -static void contained_stats(winid, const char *, long *, long *); -static void misc_stats(winid, long *, long *); -static void you_sanity_check(void); static boolean accept_menu_prefix(const struct ext_func_tab *); static void reset_cmd_vars(boolean); @@ -179,8 +131,6 @@ static char readchar_core(coordxy *, coordxy *, int *); static int parse(void); static void show_direction_keys(winid, char, boolean); static boolean help_dir(char, uchar, const char *); -static int QSORTCALLBACK migrsort_cmp(const genericptr, const genericptr); -static void list_migrating_mons(d_level *); static void handler_rebind_keys_add(boolean); static boolean bind_key_fn(uchar, int (*)(void)); @@ -193,8 +143,9 @@ static int (*timed_occ_fn)(void); static char *doc_extcmd_flagstr(winid, const struct ext_func_tab *); static const char *readchar_queue = ""; + /* for rejecting attempts to use wizard mode commands */ -static const char unavailcmd[] = "Unavailable command '%s'."; +const char unavailcmd[] = "Unavailable command '%s'."; /* for rejecting #if !SHELL, !SUSPEND */ static const char cmdnotavail[] = "'%s' command not available."; @@ -1003,124 +954,6 @@ enter_explore_mode(void) return ECMD_OK; } -/* #wizwish command - wish for something */ -static int -wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */ -{ - if (wizard) { - boolean save_verbose = flags.verbose; - - flags.verbose = FALSE; - makewish(); - flags.verbose = save_verbose; - (void) encumber_msg(); - } else - pline(unavailcmd, ecname_from_fn(wiz_wish)); - return ECMD_OK; -} - -/* #wizidentify command - reveal and optionally identify hero's inventory */ -static int -wiz_identify(void) -{ - if (wizard) { - iflags.override_ID = (int) cmd_from_func(wiz_identify); - /* command remapping might leave #wizidentify as the only way - to invoke us, in which case cmd_from_func() will yield NUL; - it won't matter to display_inventory()/display_pickinv() - if ^I invokes some other command--what matters is that - display_pickinv() and xname() see override_ID as nonzero */ - if (!iflags.override_ID) - iflags.override_ID = C('I'); - (void) display_inventory((char *) 0, FALSE); - iflags.override_ID = 0; - } else - pline(unavailcmd, ecname_from_fn(wiz_identify)); - return ECMD_OK; -} - -/* used when wiz_makemap() gets rid of monsters for the old incarnation of - a level before creating a new incarnation of it */ -static void -makemap_unmakemon(struct monst *mtmp, boolean migratory) -{ - int ndx = monsndx(mtmp->data); - - /* uncreate any unique monster so that it is eligible to be remade - on the new incarnation of the level; ignores DEADMONSTER() [why?] */ - if (mtmp->data->geno & G_UNIQ) - gm.mvitals[ndx].mvflags &= ~G_EXTINCT; - if (gm.mvitals[ndx].born) - gm.mvitals[ndx].born--; - - /* vault is going away; get rid of guard who might be in play or - be parked at <0,0>; for the latter, might already be flagged as - dead but is being kept around because of the 'isgd' flag */ - if (mtmp->isgd) { - mtmp->isgd = 0; /* after this, fall through to mongone() */ - } else if (DEADMONSTER(mtmp)) { - return; /* already set to be discarded */ - } else if (mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) { - setpaid(mtmp); - } - if (migratory) { - /* caller has removed 'mtmp' from migrating_mons; put it onto fmon - so that dmonsfree() bookkeeping for number of dead or removed - monsters won't get out of sync; it is not on the map but - mongone() -> m_detach() -> mon_leaving_level() copes with that */ - mtmp->mstate |= MON_OFFMAP; - mtmp->mstate &= ~(MON_MIGRATING | MON_LIMBO | MON_ENDGAME_MIGR); - mtmp->nmon = fmon; - fmon = mtmp; - } - mongone(mtmp); -} - -/* get rid of the all the monsters on--or intimately involved with--current - level; used when #wizmakemap destroys the level before replacing it */ -static void -makemap_remove_mons(void) -{ - struct monst *mtmp, **mprev; - - /* keep steed and other adjacent pets after releasing them - from traps, stopping eating, &c as if hero were ascending */ - keepdogs(TRUE); /* (pets-only; normally we'd be using 'FALSE') */ - /* get rid of all the monsters that didn't make it to 'mydogs' */ - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { - /* if already dead, dmonsfree(below) will get rid of it */ - if (DEADMONSTER(mtmp)) - continue; - makemap_unmakemon(mtmp, FALSE); - } - /* some monsters retain details of this level in mon->mextra; that - data becomes invalid when the level is replaced by a new one; - get rid of them now if migrating or already arrived elsewhere; - [when on their 'home' level, the previous loop got rid of them; - if they aren't actually migrating but have been placed on some - 'away' level, such monsters are treated like the Wizard: kept - on migrating monsters list, scheduled to migrate back to their - present location instead of being saved with whatever level they - happen to be on; see keepdogs() and keep_mon_accessible(dog.c)] */ - for (mprev = &gm.migrating_mons; (mtmp = *mprev) != 0; ) { - if (mtmp->mextra - && ((mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) - || (mtmp->ispriest && on_level(&u.uz, &EPRI(mtmp)->shrlevel)) - || (mtmp->isgd && on_level(&u.uz, &EGD(mtmp)->gdlevel)))) { - *mprev = mtmp->nmon; - makemap_unmakemon(mtmp, TRUE); - } else { - mprev = &mtmp->nmon; - } - } - /* release dead and 'unmade' monsters */ - dmonsfree(); - if (fmon) { - impossible("makemap_remove_mons: 'fmon' did not get emptied?"); - } - return; -} - void makemap_prepost(boolean pre, boolean wiztower) { @@ -1204,675 +1037,6 @@ makemap_prepost(boolean pre, boolean wiztower) } } -/* #wizmakemap - discard current dungeon level and replace with a new one */ -static int -wiz_makemap(void) -{ - if (wizard) { - boolean was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz); - - makemap_prepost(TRUE, was_in_W_tower); - /* create a new level; various things like bestowing a guardian - angel on Astral or setting off alarm on Ft.Ludios are handled - by goto_level(do.c) so won't occur for replacement levels */ - mklev(); - makemap_prepost(FALSE, was_in_W_tower); - } else { - pline(unavailcmd, ecname_from_fn(wiz_makemap)); - } - return ECMD_OK; -} - -/* the #wizmap command - reveal the level map - and any traps or engravings on it */ -static int -wiz_map(void) -{ - if (wizard) { - struct trap *t; - struct engr *ep; - long save_Hconf = HConfusion, save_Hhallu = HHallucination; - - notice_mon_off(); - HConfusion = HHallucination = 0L; - for (t = gf.ftrap; t != 0; t = t->ntrap) { - t->tseen = 1; - map_trap(t, TRUE); - } - for (ep = head_engr; ep != 0; ep = ep->nxt_engr) { - map_engraving(ep, TRUE); - } - do_mapping(); - notice_mon_on(); - HConfusion = save_Hconf; - HHallucination = save_Hhallu; - } else - pline(unavailcmd, ecname_from_fn(wiz_map)); - return ECMD_OK; -} - -/* #wizgenesis - generate monster(s); a count prefix will be honored */ -static int -wiz_genesis(void) -{ - if (wizard) { - boolean mongen_saved = iflags.debug_mongen; - - iflags.debug_mongen = FALSE; - (void) create_particular(); - iflags.debug_mongen = mongen_saved; - } else - pline(unavailcmd, ecname_from_fn(wiz_genesis)); - return ECMD_OK; -} - -/* #wizwhere command - display dungeon layout */ -static int -wiz_where(void) -{ - if (wizard) - (void) print_dungeon(FALSE, (schar *) 0, (xint16 *) 0); - else - pline(unavailcmd, ecname_from_fn(wiz_where)); - return ECMD_OK; -} - -/* the #wizdetect command - detect secret doors, traps, hidden monsters */ -static int -wiz_detect(void) -{ - if (wizard) - (void) findit(); - else - pline(unavailcmd, ecname_from_fn(wiz_detect)); - return ECMD_OK; -} - -/* the #wizkill command - pick targets and reduce them to 0HP; - by default, the hero is credited/blamed; use 'm' prefix to avoid that */ -static int -wiz_kill(void) -{ - struct monst *mtmp; - coord cc; - int ans; - char c, qbuf[QBUFSZ]; - const char *prompt = "Pick first monster to slay"; - boolean save_verbose = flags.verbose, - save_autodescribe = iflags.autodescribe; - d_level uarehere = u.uz; - - cc.x = u.ux, cc.y = u.uy; - for (;;) { - pline("%s:", prompt); - prompt = "Next monster"; - - flags.verbose = FALSE; - iflags.autodescribe = TRUE; - ans = getpos(&cc, TRUE, "a monster"); - flags.verbose = save_verbose; - iflags.autodescribe = save_autodescribe; - if (ans < 0 || cc.x < 1) - break; - - mtmp = 0; - if (u_at(cc.x, cc.y)) { - if (u.usteed) { - Sprintf(qbuf, "Kill %.110s?", mon_nam(u.usteed)); - if ((c = ynq(qbuf)) == 'q') - break; - if (c == 'y') - mtmp = u.usteed; - } - if (!mtmp) { - Sprintf(qbuf, "%s?", Role_if(PM_SAMURAI) ? "Perform seppuku" - : "Commit suicide"); - if (paranoid_query(TRUE, qbuf)) { - Sprintf(gk.killer.name, "%s own player", uhis()); - gk.killer.format = KILLED_BY; - done(DIED); - } - break; - } - } else if (u.uswallow) { - mtmp = next2u(cc.x, cc.y) ? u.ustuck : 0; - } else { - mtmp = m_at(cc.x, cc.y); - } - - /* whether there's an unseen monster here or not, player will know - that there's no monster here after the kill or failed attempt; - let hero know too */ - (void) unmap_invisible(cc.x, cc.y); - - if (mtmp) { - /* we don't require that the monster be seen or sensed so - we issue our own message in order to name it in case it - isn't; note that if it triggers other kills, those might - be referred to as "it" */ - int tame = !!mtmp->mtame, - seen = (canspotmon(mtmp) || (u.uswallow && mtmp == u.ustuck)), - flgs = (SUPPRESS_IT | SUPPRESS_HALLUCINATION - | ((tame && has_mgivenname(mtmp)) ? SUPPRESS_SADDLE - : 0)), - articl = tame ? ARTICLE_YOUR : seen ? ARTICLE_THE : ARTICLE_A; - const char *adjs = tame ? (!seen ? "poor, unseen" : "poor") - : (!seen ? "unseen" : (const char *) 0); - char *Mn = x_monnam(mtmp, articl, adjs, flgs, FALSE); - - if (!iflags.menu_requested) { - /* normal case: hero is credited/blamed */ - You("%s %s!", nonliving(mtmp->data) ? "destroy" : "kill", Mn); - xkilled(mtmp, XKILL_NOMSG); - } else { /* 'm'-prefix */ - /* we know that monsters aren't moving because player has - just issued this #wizkill command, but if 'mtmp' is a - gas spore whose explosion kills any other monsters we - need to have the mon_moving flag be True in order to - avoid blaming or crediting hero for their deaths */ - gc.context.mon_moving = TRUE; - pline("%s is %s.", upstart(Mn), - nonliving(mtmp->data) ? "destroyed" : "killed"); - /* Null second arg suppresses the usual message */ - monkilled(mtmp, (char *) 0, AD_PHYS); - gc.context.mon_moving = FALSE; - } - /* end targetting loop if an engulfer dropped hero onto a level- - changing trap */ - if (u.utotype || !on_level(&u.uz, &uarehere)) - break; - } else { - There("is no monster there."); - break; - } - } - /* since #wizkill takes no game time, it is possible to kill something - in the main dungeon and immediately level teleport into the endgame - which will delete the main dungeon's level files; avoid triggering - impossible "dmonsfree: 0 removed doesn't match N pending" by forcing - dead monster cleanup; we don't track whether anything was actually - killed above--if nothing was, this will be benign */ - dmonsfree(); - /* distinction between ECMD_CANCEL and ECMD_OK is unimportant here */ - return ECMD_OK; /* no time elapses */ -} - -/* the #wizloadlua command - load an arbitrary lua file */ -static int -wiz_load_lua(void) -{ - if (wizard) { - char buf[BUFSZ]; - /* Large but not unlimited memory and CPU so random bits of - * code can be tested by wizards. */ - nhl_sandbox_info sbi = {NHL_SB_SAFE | NHL_SB_DEBUGGING, - 16*1024*1024, 0, 16*1024*1024}; - - buf[0] = '\0'; - getlin("Load which lua file?", buf); - if (buf[0] == '\033' || buf[0] == '\0') - return ECMD_CANCEL; - if (!strchr(buf, '.')) - strcat(buf, ".lua"); - (void) load_lua(buf, &sbi); - } else - pline(unavailcmd, ecname_from_fn(wiz_load_lua)); - return ECMD_OK; -} - -/* the #wizloaddes command - load a special level lua file */ -static int -wiz_load_splua(void) -{ - if (wizard) { - char buf[BUFSZ]; - - buf[0] = '\0'; - getlin("Load which des lua file?", buf); - if (buf[0] == '\033' || buf[0] == '\0') - return ECMD_CANCEL; - if (!strchr(buf, '.')) - strcat(buf, ".lua"); - - lspo_reset_level(NULL); - (void) load_special(buf); - lspo_finalize_level(NULL); - - } else - pline(unavailcmd, ecname_from_fn(wiz_load_splua)); - return ECMD_OK; -} - -/* the #wizlevelport command - level teleport */ -static int -wiz_level_tele(void) -{ - if (wizard) - level_tele(); - else - pline(unavailcmd, ecname_from_fn(wiz_level_tele)); - return ECMD_OK; -} - -/* #wizfliplevel - transpose the current level */ -static int -wiz_flip_level(void) -{ - static const char choices[] = "0123", - prmpt[] = "Flip 0=randomly, 1=vertically, 2=horizontally, 3=both:"; - - /* - * Does not handle - * levregions, - * monster mtrack, - * migrating monsters aimed at returning to specific coordinates - * on this level - * as flipping is normally done only during level creation. - */ - if (wizard) { - char c = yn_function(prmpt, choices, '\0', TRUE); - - if (c && strchr(choices, c)) { - c -= '0'; - - if (!c) - flip_level_rnd(3, TRUE); - else - flip_level((int) c, TRUE); - - docrt(); - } else { - pline("%s", Never_mind); - } - } - return ECMD_OK; -} - -/* #levelchange command - adjust hero's experience level */ -static int -wiz_level_change(void) -{ - char buf[BUFSZ] = DUMMY; - int newlevel = 0; - int ret; - - getlin("To what experience level do you want to be set?", buf); - (void) mungspaces(buf); - if (buf[0] == '\033' || buf[0] == '\0') - ret = 0; - else - ret = sscanf(buf, "%d", &newlevel); - - if (ret != 1) { - pline1(Never_mind); - return ECMD_OK; - } - if (newlevel == u.ulevel) { - You("are already that experienced."); - } else if (newlevel < u.ulevel) { - if (u.ulevel == 1) { - You("are already as inexperienced as you can get."); - return ECMD_OK; - } - if (newlevel < 1) - newlevel = 1; - while (u.ulevel > newlevel) - losexp("#levelchange"); - } else { - if (u.ulevel >= MAXULEV) { - You("are already as experienced as you can get."); - return ECMD_OK; - } - if (newlevel > MAXULEV) - newlevel = MAXULEV; - while (u.ulevel < newlevel) - pluslvl(FALSE); - } - u.ulevelmax = u.ulevel; - return ECMD_OK; -} - -DISABLE_WARNING_CONDEXPR_IS_CONSTANT - -/* #wiztelekinesis */ -static int -wiz_telekinesis(void) -{ - int ans = 0; - coord cc; - struct monst *mtmp = (struct monst *) 0; - - cc.x = u.ux; - cc.y = u.uy; - - pline("Pick a monster to hurtle."); - do { - ans = getpos(&cc, TRUE, "a monster"); - if (ans < 0 || cc.x < 1) - return ECMD_CANCEL; - - if ((((mtmp = m_at(cc.x, cc.y)) != 0) && canspotmon(mtmp)) - || u_at(cc.x, cc.y)) { - if (!getdir("which direction?")) - return ECMD_CANCEL; - - if (mtmp) { - mhurtle(mtmp, u.dx, u.dy, 6); - if (!DEADMONSTER(mtmp) && canspotmon(mtmp)) { - cc.x = mtmp->mx; - cc.y = mtmp->my; - } - } else { - hurtle(u.dx, u.dy, 6, FALSE); - cc.x = u.ux, cc.y = u.uy; - } - } - - } while (u.utotype == UTOTYPE_NONE); - return ECMD_OK; -} - -RESTORE_WARNING_CONDEXPR_IS_CONSTANT - -/* #panic command - test program's panic handling */ -static int -wiz_panic(void) -{ - if (iflags.debug_fuzzer) { - u.uhp = u.uhpmax = 1000; - u.uen = u.uenmax = 1000; - return ECMD_OK; - } - if (paranoid_query(TRUE, - "Do you want to call panic() and end your game?")) - panic("Crash test."); - return ECMD_OK; -} - -/* #debugfuzzer command - fuzztest the program */ -static int -wiz_fuzzer(void) -{ - if (flags.suppress_alert < FEATURE_NOTICE_VER(3,7,0)) { - pline("The fuzz tester will make NetHack execute random keypresses."); - There("is no conventional way out of this mode."); - } - if (paranoid_query(TRUE, "Do you want to start fuzz testing?")) - iflags.debug_fuzzer = TRUE; /* Thoth, take the reins */ - return ECMD_OK; -} - -/* #polyself command - change hero's form */ -static int -wiz_polyself(void) -{ - polyself(POLY_CONTROLLED); - return ECMD_OK; -} - -/* #seenv command */ -static int -wiz_show_seenv(void) -{ - winid win; - coordxy x, y, startx, stopx, curx; - int v; - char row[COLNO + 1]; - - win = create_nhwindow(NHW_TEXT); - /* - * Each seenv description takes up 2 characters, so center - * the seenv display around the hero. - */ - startx = max(1, u.ux - (COLNO / 4)); - stopx = min(startx + (COLNO / 2), COLNO); - /* can't have a line exactly 80 chars long */ - if (stopx - startx == COLNO / 2) - startx++; - - for (y = 0; y < ROWNO; y++) { - for (x = startx, curx = 0; x < stopx; x++, curx += 2) { - if (u_at(x, y)) { - row[curx] = row[curx + 1] = '@'; - } else { - v = levl[x][y].seenv & 0xff; - if (v == 0) - row[curx] = row[curx + 1] = ' '; - else - Sprintf(&row[curx], "%02x", v); - } - } - /* remove trailing spaces */ - for (x = curx - 1; x >= 0; x--) - if (row[x] != ' ') - break; - row[x + 1] = '\0'; - - putstr(win, 0, row); - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return ECMD_OK; -} - -/* #vision command */ -static int -wiz_show_vision(void) -{ - winid win; - coordxy x, y; - int v; - char row[COLNO + 1]; - - win = create_nhwindow(NHW_TEXT); - Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit", - COULD_SEE, IN_SIGHT, TEMP_LIT); - putstr(win, 0, row); - putstr(win, 0, ""); - for (y = 0; y < ROWNO; y++) { - for (x = 1; x < COLNO; x++) { - if (u_at(x, y)) { - row[x] = '@'; - } else { - v = gv.viz_array[y][x]; /* data access should be hidden */ - row[x] = (v == 0) ? ' ' : ('0' + v); - } - } - /* remove trailing spaces */ - for (x = COLNO - 1; x >= 1; x--) - if (row[x] != ' ') - break; - row[x + 1] = '\0'; - - putstr(win, 0, &row[1]); - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return ECMD_OK; -} - -/* #wmode command */ -static int -wiz_show_wmodes(void) -{ - winid win; - coordxy x, y; - char row[COLNO + 1]; - struct rm *lev; - boolean istty = WINDOWPORT(tty); - - win = create_nhwindow(NHW_TEXT); - if (istty) - putstr(win, 0, ""); /* tty only: blank top line */ - for (y = 0; y < ROWNO; y++) { - for (x = 0; x < COLNO; x++) { - lev = &levl[x][y]; - if (u_at(x, y)) - row[x] = '@'; - else if (IS_WALL(lev->typ) || lev->typ == SDOOR) - row[x] = '0' + (lev->wall_info & WM_MASK); - else if (lev->typ == CORR) - row[x] = '#'; - else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ)) - row[x] = '.'; - else - row[x] = 'x'; - } - row[COLNO] = '\0'; - /* map column 0, levl[0][], is off the left edge of the screen */ - putstr(win, 0, &row[1]); - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return ECMD_OK; -} - -/* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */ -static void -wiz_map_levltyp(void) -{ - winid win; - coordxy x, y; - int terrain; - char row[COLNO + 1]; - boolean istty = !strcmp(windowprocs.name, "tty"); - - win = create_nhwindow(NHW_TEXT); - /* map row 0, levl[][0], is drawn on the second line of tty screen */ - if (istty) - putstr(win, 0, ""); /* tty only: blank top line */ - for (y = 0; y < ROWNO; y++) { - /* map column 0, levl[0][], is off the left edge of the screen; - it should always have terrain type "undiggable stone" */ - for (x = 1; x < COLNO; x++) { - terrain = levl[x][y].typ; - /* assumes there aren't more than 10+26+26 terrain types */ - row[x - 1] = (char) ((terrain == STONE && !may_dig(x, y)) - ? '*' - : (terrain < 10) - ? '0' + terrain - : (terrain < 36) - ? 'a' + terrain - 10 - : 'A' + terrain - 36); - } - x--; - if (levl[0][y].typ != STONE || may_dig(0, y)) - row[x++] = '!'; - row[x] = '\0'; - putstr(win, 0, row); - } - - { - char dsc[COLBUFSZ]; - s_level *slev = Is_special(&u.uz); - - Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel); - /* [dungeon branch features currently omitted] */ - /* special level features */ - if (slev) { - Sprintf(eos(dsc), " \"%s\"", slev->proto); - /* special level flags (note: dungeon.def doesn't set `maze' - or `hell' for any specific levels so those never show up) */ - if (slev->flags.maze_like) - Strcat(dsc, " mazelike"); - if (slev->flags.hellish) - Strcat(dsc, " hellish"); - if (slev->flags.town) - Strcat(dsc, " town"); - if (slev->flags.rogue_like) - Strcat(dsc, " roguelike"); - /* alignment currently omitted to save space */ - } - /* level features */ - if (gl.level.flags.nfountains) - Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym, - (int) gl.level.flags.nfountains); - if (gl.level.flags.nsinks) - Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym, - (int) gl.level.flags.nsinks); - if (gl.level.flags.has_vault) - Strcat(dsc, " vault"); - if (gl.level.flags.has_shop) - Strcat(dsc, " shop"); - if (gl.level.flags.has_temple) - Strcat(dsc, " temple"); - if (gl.level.flags.has_court) - Strcat(dsc, " throne"); - if (gl.level.flags.has_zoo) - Strcat(dsc, " zoo"); - if (gl.level.flags.has_morgue) - Strcat(dsc, " morgue"); - if (gl.level.flags.has_barracks) - Strcat(dsc, " barracks"); - if (gl.level.flags.has_beehive) - Strcat(dsc, " hive"); - if (gl.level.flags.has_swamp) - Strcat(dsc, " swamp"); - /* level flags */ - if (gl.level.flags.noteleport) - Strcat(dsc, " noTport"); - if (gl.level.flags.hardfloor) - Strcat(dsc, " noDig"); - if (gl.level.flags.nommap) - Strcat(dsc, " noMMap"); - if (!gl.level.flags.hero_memory) - Strcat(dsc, " noMem"); - if (gl.level.flags.shortsighted) - Strcat(dsc, " shortsight"); - if (gl.level.flags.graveyard) - Strcat(dsc, " graveyard"); - if (gl.level.flags.is_maze_lev) - Strcat(dsc, " maze"); - if (gl.level.flags.is_cavernous_lev) - Strcat(dsc, " cave"); - if (gl.level.flags.arboreal) - Strcat(dsc, " tree"); - if (Sokoban) - Strcat(dsc, " sokoban-rules"); - /* non-flag info; probably should include dungeon branching - checks (extra stairs and magic portals) here */ - if (Invocation_lev(&u.uz)) - Strcat(dsc, " invoke"); - if (On_W_tower_level(&u.uz)) - Strcat(dsc, " tower"); - /* append a branch identifier for completeness' sake */ - if (u.uz.dnum == 0) - Strcat(dsc, " dungeon"); - else if (u.uz.dnum == mines_dnum) - Strcat(dsc, " mines"); - else if (In_sokoban(&u.uz)) - Strcat(dsc, " sokoban"); - else if (u.uz.dnum == quest_dnum) - Strcat(dsc, " quest"); - else if (Is_knox(&u.uz)) - Strcat(dsc, " ludios"); - else if (u.uz.dnum == 1) - Strcat(dsc, " gehennom"); - else if (u.uz.dnum == tower_dnum) - Strcat(dsc, " vlad"); - else if (In_endgame(&u.uz)) - Strcat(dsc, " endgame"); - else { - /* somebody's added a dungeon branch we're not expecting */ - const char *brname = gd.dungeons[u.uz.dnum].dname; - - if (!brname || !*brname) - brname = "unknown"; - if (!strncmpi(brname, "the ", 4)) - brname += 4; - Sprintf(eos(dsc), " %s", brname); - } - /* limit the line length to map width */ - if (strlen(dsc) >= COLNO) - dsc[COLNO - 1] = '\0'; /* truncate */ - putstr(win, 0, dsc); - } - - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return; -} - /* temporary? hack, since level type codes aren't the same as screen symbols and only the latter have easily accessible descriptions */ const char *levltyp[MAX_TYPE + 2] = { @@ -1899,272 +1063,6 @@ levltyp_to_name(int typ) return NULL; } -DISABLE_WARNING_FORMAT_NONLITERAL - -/* explanation of base-36 output from wiz_map_levltyp() */ -static void -wiz_levltyp_legend(void) -{ - winid win; - int i, j, last, c; - const char *dsc, *fmt; - char buf[BUFSZ]; - - win = create_nhwindow(NHW_TEXT); - putstr(win, 0, "#terrain encodings:"); - putstr(win, 0, ""); - fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */ - *buf = '\0'; - /* output in pairs, left hand column holds [0],[1],...,[N/2-1] - and right hand column holds [N/2],[N/2+1],...,[N-1]; - N ('last') will always be even, and may or may not include - the empty string entry to pad out the final pair, depending - upon how many other entries are present in levltyp[] */ - last = SIZE(levltyp) & ~1; - for (i = 0; i < last / 2; ++i) - for (j = i; j < last; j += last / 2) { - dsc = levltyp[j]; - c = !*dsc ? ' ' - : !strncmp(dsc, "unreachable", 11) ? '*' - /* same int-to-char conversion as wiz_map_levltyp() */ - : (j < 10) ? '0' + j - : (j < 36) ? 'a' + j - 10 - : 'A' + j - 36; - Sprintf(eos(buf), fmt, c, dsc); - if (j > i) { - putstr(win, 0, buf); - *buf = '\0'; - } - } - display_nhwindow(win, TRUE); - destroy_nhwindow(win); - return; -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -DISABLE_WARNING_CONDEXPR_IS_CONSTANT - -/* #wizsmell command - test usmellmon(). */ -static int -wiz_smell(void) -{ - struct monst *mtmp; /* monster being smelled */ - struct permonst *mptr; - int ans, glyph; - coord cc; /* screen pos to sniff */ - boolean is_you; - - cc.x = u.ux; - cc.y = u.uy; - if (!olfaction(gy.youmonst.data)) { - You("are incapable of detecting odors in your present form."); - return ECMD_OK; - } - - You("can move the cursor to a monster that you want to smell."); - do { - pline("Pick a monster to smell."); - ans = getpos(&cc, TRUE, "a monster"); - if (ans < 0 || cc.x < 0) { - return ECMD_CANCEL; /* done */ - } - is_you = FALSE; - if (u_at(cc.x, cc.y)) { - if (u.usteed) { - mptr = u.usteed->data; - } else { - mptr = gy.youmonst.data; - is_you = TRUE; - } - } else if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *) 0) { - mptr = mtmp->data; - } else { - mptr = (struct permonst *) 0; - } - /* Buglet: mapping or unmapping "remembered, unseen monster" should - cause time to elapse; since we're in wizmode, don't bother */ - glyph = glyph_at(cc.x, cc.y); - /* Is it a monster? */ - if (mptr) { - if (is_you) - You("surreptitiously sniff under your %s.", body_part(ARM)); - if (!usmellmon(mptr)) - pline("%s to not give off any smell.", - is_you ? "You seem" : "That monster seems"); - if (!glyph_is_monster(glyph)) - map_invisible(cc.x, cc.y); - } else { - You("don't smell any monster there."); - if (glyph_is_invisible(glyph)) - unmap_invisible(cc.x, cc.y); - } - } while (TRUE); - return ECMD_OK; -} - -RESTORE_WARNING_CONDEXPR_IS_CONSTANT - -DISABLE_WARNING_FORMAT_NONLITERAL - -#define DEFAULT_TIMEOUT_INCR 30 - -/* #wizinstrinsic command to set some intrinsics for testing */ -static int -wiz_intrinsic(void) -{ - if (wizard) { - static const char wizintrinsic[] = "#wizintrinsic"; - static const char fmt[] = "You are%s %s."; - winid win; - anything any; - char buf[BUFSZ]; - int i, j, n, amt, typ, p = 0; - long oldtimeout, newtimeout; - const char *propname; - menu_item *pick_list = (menu_item *) 0; - int clr = NO_COLOR; - - any = cg.zeroany; - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - if (iflags.cmdassist) { - /* start menu with a subtitle */ - Sprintf(buf, - "[Precede any selection with a count to increment by other than %d.]", - DEFAULT_TIMEOUT_INCR); - add_menu_str(win, buf); - } - for (i = 0; (propname = property_by_index(i, &p)) != 0; ++i) { - if (p == HALLUC_RES) { - /* Grayswandir vs hallucination; ought to be redone to - use u.uprops[HALLUC].blocked instead of being treated - as a separate property; letting in be manually toggled - even only in wizard mode would be asking for trouble... */ - continue; - } - if (p == FIRE_RES) { - /* FIRE_RES and properties beyond it (in the propertynames[] - ordering, not their numerical PROP values), can only be - set to timed values here so show a separator */ - add_menu_str(win, "--"); - } - any.a_int = i + 1; /* +1: avoid 0 */ - oldtimeout = u.uprops[p].intrinsic & TIMEOUT; - if (oldtimeout) - Sprintf(buf, "%-27s [%li]", propname, oldtimeout); - else - Sprintf(buf, "%s", propname); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, - MENU_ITEMFLAGS_NONE); - } - end_menu(win, "Which intrinsics?"); - n = select_menu(win, PICK_ANY, &pick_list); - destroy_nhwindow(win); - - for (j = 0; j < n; ++j) { - i = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */ - propname = property_by_index(i, &p); - oldtimeout = u.uprops[p].intrinsic & TIMEOUT; - amt = (pick_list[j].count == -1L) ? DEFAULT_TIMEOUT_INCR - : (int) pick_list[j].count; - if (amt <= 0) /* paranoia */ - continue; - newtimeout = oldtimeout + (long) amt; - - switch (p) { - case SICK: - case SLIMED: - case STONED: - if (oldtimeout > 0L && newtimeout > oldtimeout) - newtimeout = oldtimeout; - break; - } - - switch (p) { - case BLINDED: - make_blinded(newtimeout, TRUE); - break; -#if 0 /* make_confused() only gives feedback when confusion is - * ending so use the 'default' case for it instead */ - case CONFUSION: - make_confused(newtimeout, TRUE); - break; -#endif /*0*/ - case DEAF: - make_deaf(newtimeout, TRUE); - break; - case HALLUC: - make_hallucinated(newtimeout, TRUE, 0L); - break; - case SICK: - typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE; - make_sick(newtimeout, wizintrinsic, TRUE, typ); - break; - case SLIMED: - Sprintf(buf, fmt, - !Slimed ? "" : " still", "turning into slime"); - make_slimed(newtimeout, buf); - break; - case STONED: - Sprintf(buf, fmt, - !Stoned ? "" : " still", "turning into stone"); - make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic); - break; - case STUNNED: - make_stunned(newtimeout, TRUE); - break; - case VOMITING: - Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting"); - make_vomiting(newtimeout, FALSE); - pline1(buf); - break; - case WARN_OF_MON: - if (!Warn_of_mon) { - gc.context.warntype.speciesidx = PM_GRID_BUG; - gc.context.warntype.species - = &mons[gc.context.warntype.speciesidx]; - } - goto def_feedback; - case GLIB: - /* slippery fingers might need a persistent inventory update - so needs more than simple incr_itimeout() but we want - the pline() issued with that */ - make_glib((int) newtimeout); - /*FALLTHRU*/ - default: - def_feedback: - if (p != GLIB) - incr_itimeout(&u.uprops[p].intrinsic, amt); - disp.botl = TRUE; /* have pline() do a status update */ - pline("Timeout for %s %s %d.", propname, - oldtimeout ? "increased by" : "set to", amt); - break; - } - /* this has to be after incr_itimeout() */ - if (p == LEVITATION || p == FLYING) - float_vs_flight(); - else if (p == PROT_FROM_SHAPE_CHANGERS) - rescham(); - } - if (n >= 1) - free((genericptr_t) pick_list); - docrt(); - } else - pline(unavailcmd, ecname_from_fn(wiz_intrinsic)); - return ECMD_OK; -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -/* #wizrumorcheck command - verify each rumor access */ -static int -wiz_rumor_check(void) -{ - rumor_check(); - return ECMD_OK; -} - /* #terrain command -- show known map, inspired by crawl's '|' command */ static int doterrain(void) @@ -2330,40 +1228,6 @@ u_can_see_whole_selection(struct selectionvar *sel) return TRUE; } -/* selection is not rectangular, or has holes in it */ -static boolean -selection_is_irregular(struct selectionvar *sel) -{ - coordxy x, y; - NhRect rect = cg.zeroNhRect; - - selection_getbounds(sel, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (isok(x,y) && !selection_getpoint(x, y, sel)) - return TRUE; - - return FALSE; -} - -/* return a description of the selection size */ -static char * -selection_size_description(struct selectionvar *sel, char *buf) -{ - NhRect rect = cg.zeroNhRect; - coordxy dx, dy; - - selection_getbounds(sel, &rect); - dx = rect.hx - rect.lx + 1; - dy = rect.hy - rect.ly + 1; - Sprintf(buf, "%s %i by %i", selection_is_irregular(sel) ? "irregularly shaped" - : (dx == dy) ? "square" - : "rectangular", - dx, dy); - return buf; -} - /* selection_floofill callback to get all locations in a room */ static int dolookaround_floodfill_findroom(coordxy x, coordxy y) @@ -3886,7 +2750,7 @@ cmd_from_ecname(const char *ecname) return cmdnamebuf; } -static const char * +const char * ecname_from_fn(int (*fn)(void)) { const struct ext_func_tab *extcmd, *cmdptr = 0; @@ -3952,733 +2816,6 @@ cmdname_from_func( return res; } -/* - * wizard mode sanity_check code - */ - -static const char template[] = "%-27s %4ld %6ld"; -static const char stats_hdr[] = " count bytes"; -static const char stats_sep[] = "--------------------------- ----- -------"; - -static int -size_obj(struct obj *otmp) -{ - int sz = (int) sizeof (struct obj); - - if (otmp->oextra) { - sz += (int) sizeof (struct oextra); - if (ONAME(otmp)) - sz += (int) strlen(ONAME(otmp)) + 1; - if (OMONST(otmp)) - sz += size_monst(OMONST(otmp), FALSE); - if (OMAILCMD(otmp)) - sz += (int) strlen(OMAILCMD(otmp)) + 1; - /* sz += (int) sizeof (unsigned); -- now part of oextra itself */ - } - return sz; -} - -static void -count_obj(struct obj *chain, long *total_count, long *total_size, - boolean top, boolean recurse) -{ - long count, size; - struct obj *obj; - - for (count = size = 0, obj = chain; obj; obj = obj->nobj) { - if (top) { - count++; - size += size_obj(obj); - } - if (recurse && obj->cobj) - count_obj(obj->cobj, total_count, total_size, TRUE, TRUE); - } - *total_count += count; - *total_size += size; -} - -DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE_WARNING follows show_wiz_stats */ - -static void -obj_chain( - winid win, - const char *src, - struct obj *chain, - boolean force, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count = 0L, size = 0L; - - count_obj(chain, &count, &size, TRUE, FALSE); - - if (count || size || force) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static void -mon_invent_chain( - winid win, - const char *src, - struct monst *chain, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count = 0, size = 0; - struct monst *mon; - - for (mon = chain; mon; mon = mon->nmon) - count_obj(mon->minvent, &count, &size, TRUE, FALSE); - - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static void -contained_stats( - winid win, - const char *src, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count = 0, size = 0; - struct monst *mon; - - count_obj(gi.invent, &count, &size, FALSE, TRUE); - count_obj(fobj, &count, &size, FALSE, TRUE); - count_obj(gl.level.buriedobjlist, &count, &size, FALSE, TRUE); - count_obj(gm.migrating_objs, &count, &size, FALSE, TRUE); - /* DEADMONSTER check not required in this loop since they have no - * inventory */ - for (mon = fmon; mon; mon = mon->nmon) - count_obj(mon->minvent, &count, &size, FALSE, TRUE); - for (mon = gm.migrating_mons; mon; mon = mon->nmon) - count_obj(mon->minvent, &count, &size, FALSE, TRUE); - - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static int -size_monst(struct monst *mtmp, boolean incl_wsegs) -{ - int sz = (int) sizeof (struct monst); - - if (mtmp->wormno && incl_wsegs) - sz += size_wseg(mtmp); - - if (mtmp->mextra) { - sz += (int) sizeof (struct mextra); - if (MGIVENNAME(mtmp)) - sz += (int) strlen(MGIVENNAME(mtmp)) + 1; - if (EGD(mtmp)) - sz += (int) sizeof (struct egd); - if (EPRI(mtmp)) - sz += (int) sizeof (struct epri); - if (ESHK(mtmp)) - sz += (int) sizeof (struct eshk); - if (EMIN(mtmp)) - sz += (int) sizeof (struct emin); - if (EDOG(mtmp)) - sz += (int) sizeof (struct edog); - /* mextra->mcorpsenm doesn't point to more memory */ - } - return sz; -} - -static void -mon_chain( - winid win, - const char *src, - struct monst *chain, - boolean force, - long *total_count, long *total_size) -{ - char buf[BUFSZ]; - long count, size; - struct monst *mon; - /* mon->wormno means something different for migrating_mons and mydogs */ - boolean incl_wsegs = !strcmpi(src, "fmon"); - - count = size = 0L; - for (mon = chain; mon; mon = mon->nmon) { - count++; - size += size_monst(mon, incl_wsegs); - } - if (count || size || force) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, src, count, size); - putstr(win, 0, buf); - } -} - -static void -misc_stats( - winid win, - long *total_count, long *total_size) -{ - char buf[BUFSZ], hdrbuf[QBUFSZ]; - long count, size; - int idx; - struct trap *tt; - struct damage *sd; /* shop damage */ - struct kinfo *k; /* delayed killer */ - struct cemetery *bi; /* bones info */ - - /* traps and engravings are output unconditionally; - * others only if nonzero - */ - count = size = 0L; - for (tt = gf.ftrap; tt; tt = tt->ntrap) { - ++count; - size += (long) sizeof *tt; - } - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - - count = size = 0L; - engr_stats("engravings, size %ld+text", hdrbuf, &count, &size); - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - - count = size = 0L; - light_stats("light sources, size %ld", hdrbuf, &count, &size); - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - timer_stats("timers, size %ld", hdrbuf, &count, &size); - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (sd = gl.level.damagelist; sd; sd = sd->next) { - ++count; - size += (long) sizeof *sd; - } - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "shop damage, size %ld", - (long) sizeof (struct damage)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size); - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (k = gk.killer.next; k; k = k->next) { - ++count; - size += (long) sizeof *k; - } - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "delayed killer%s, size %ld", - plur(count), (long) sizeof (struct kinfo)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (bi = gl.level.bonesinfo; bi; bi = bi->next) { - ++count; - size += (long) sizeof *bi; - } - if (count || size) { - *total_count += count; - *total_size += size; - Sprintf(hdrbuf, "bones history, size %ld", - (long) sizeof (struct cemetery)); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } - - count = size = 0L; - for (idx = 0; idx < NUM_OBJECTS; ++idx) - if (objects[idx].oc_uname) { - ++count; - size += (long) (strlen(objects[idx].oc_uname) + 1); - } - if (count || size) { - *total_count += count; - *total_size += size; - Strcpy(hdrbuf, "object type names, text"); - Sprintf(buf, template, hdrbuf, count, size); - putstr(win, 0, buf); - } -} - -/* the #stats command - * Display memory usage of all monsters and objects on the level. - */ -static int -wiz_show_stats(void) -{ - char buf[BUFSZ]; - winid win; - long total_obj_size, total_obj_count, - total_mon_size, total_mon_count, - total_ovr_size, total_ovr_count, - total_misc_size, total_misc_count; - - win = create_nhwindow(NHW_TEXT); - putstr(win, 0, "Current memory statistics:"); - - total_obj_count = total_obj_size = 0L; - putstr(win, 0, stats_hdr); - Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj)); - putstr(win, 0, buf); - obj_chain(win, "invent", gi.invent, TRUE, - &total_obj_count, &total_obj_size); - obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size); - obj_chain(win, "buried", gl.level.buriedobjlist, FALSE, - &total_obj_count, &total_obj_size); - obj_chain(win, "migrating obj", gm.migrating_objs, FALSE, - &total_obj_count, &total_obj_size); - obj_chain(win, "billobjs", gb.billobjs, FALSE, - &total_obj_count, &total_obj_size); - mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size); - mon_invent_chain(win, "migrating minvent", gm.migrating_mons, - &total_obj_count, &total_obj_size); - contained_stats(win, "contained", &total_obj_count, &total_obj_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size); - putstr(win, 0, buf); - - total_mon_count = total_mon_size = 0L; - putstr(win, 0, ""); - Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst)); - putstr(win, 0, buf); - mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size); - mon_chain(win, "migrating", gm.migrating_mons, FALSE, - &total_mon_count, &total_mon_size); - /* 'gm.mydogs' is only valid during level change or end of game disclosure, - but conceivably we've been called from within debugger at such time */ - if (gm.mydogs) /* monsters accompanying hero */ - mon_chain(win, "mydogs", gm.mydogs, FALSE, - &total_mon_count, &total_mon_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size); - putstr(win, 0, buf); - - total_ovr_count = total_ovr_size = 0L; - putstr(win, 0, ""); - putstr(win, 0, " Overview"); - overview_stats(win, template, &total_ovr_count, &total_ovr_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size); - putstr(win, 0, buf); - - total_misc_count = total_misc_size = 0L; - putstr(win, 0, ""); - putstr(win, 0, " Miscellaneous"); - misc_stats(win, &total_misc_count, &total_misc_size); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size); - putstr(win, 0, buf); - - putstr(win, 0, ""); - putstr(win, 0, stats_sep); - Sprintf(buf, template, " Grand total", - (total_obj_count + total_mon_count - + total_ovr_count + total_misc_count), - (total_obj_size + total_mon_size - + total_ovr_size + total_misc_size)); - putstr(win, 0, buf); - -#if defined(__BORLANDC__) && !defined(_WIN32) - show_borlandc_stats(win); -#endif - - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - return ECMD_OK; -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) -/* the #wizdispmacros command - * Verify that some display macros are returning sane values */ -static int -wiz_display_macros(void) -{ - static const char display_issues[] = "Display macro issues:"; - char buf[BUFSZ]; - winid win; - int glyph, test, trouble = 0, no_glyph = NO_GLYPH, max_glyph = MAX_GLYPH; - - win = create_nhwindow(NHW_TEXT); - - for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { - /* glyph_is_cmap / glyph_to_cmap() */ - if (glyph_is_cmap(glyph)) { - test = glyph_to_cmap(glyph); - /* check for MAX_GLYPH return */ - if (test == no_glyph) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_is_cmap() / glyph_to_cmap(glyph=%d)" - " sync failure, returned NO_GLYPH (%d)", - glyph, test); - putstr(win, 0, buf); - } - if (glyph_is_cmap_zap(glyph) - && !(test >= S_vbeam && test <= S_rslant)) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, - "glyph_is_cmap_zap(glyph=%d) returned non-zap cmap %d", - glyph, test); - putstr(win, 0, buf); - } - /* check against defsyms array subscripts */ - if (!IndexOk(test, defsyms)) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_to_cmap(glyph=%d) returns %d" - " exceeds defsyms[%d] bounds (MAX_GLYPH = %d)", - glyph, test, SIZE(defsyms), max_glyph); - putstr(win, 0, buf); - } - } - /* glyph_is_monster / glyph_to_mon */ - if (glyph_is_monster(glyph)) { - test = glyph_to_mon(glyph); - /* check against mons array subscripts */ - if (test < 0 || test >= NUMMONS) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_to_mon(glyph=%d) returns %d" - " exceeds mons[%d] bounds", - glyph, test, NUMMONS); - putstr(win, 0, buf); - } - } - /* glyph_is_object / glyph_to_obj */ - if (glyph_is_object(glyph)) { - test = glyph_to_obj(glyph); - /* check against objects array subscripts */ - if (test < 0 || test > NUM_OBJECTS) { - if (!trouble++) - putstr(win, 0, display_issues); - Sprintf(buf, "glyph_to_obj(glyph=%d) returns %d" - " exceeds objects[%d] bounds", - glyph, test, NUM_OBJECTS); - putstr(win, 0, buf); - } - } - } - if (!trouble) - putstr(win, 0, "No display macro issues detected."); - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - return ECMD_OK; -} -#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ - -#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) -/* the #wizmondiff command */ -static int -wiz_mon_diff(void) -{ - static const char window_title[] = "Review of monster difficulty ratings" - " [index:level]:"; - char buf[BUFSZ]; - winid win; - int mhardcoded = 0, mcalculated = 0, trouble = 0, cnt = 0, mdiff = 0; - int mlev; - struct permonst *ptr; - - /* - * Possible extension: choose between showing discrepancies, - * showing all monsters, or monsters within a particular class. - */ - - win = create_nhwindow(NHW_TEXT); - for (ptr = &mons[0]; ptr->mlet; ptr++, cnt++) { - mcalculated = mstrength(ptr); - mhardcoded = (int) ptr->difficulty; - mdiff = mhardcoded - mcalculated; - if (mdiff) { - if (!trouble++) - putstr(win, 0, window_title); - mlev = (int) ptr->mlevel; - if (mlev > 50) /* hack for named demons */ - mlev = 50; - Snprintf(buf, sizeof buf, - "%-18s [%3d:%2d]: calculated: %2d, hardcoded: %2d (%+d)", - ptr->pmnames[NEUTRAL], cnt, mlev, - mcalculated, mhardcoded, mdiff); - putstr(win, 0, buf); - } - } - if (!trouble) - putstr(win, 0, "No monster difficulty discrepancies were detected."); - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - return ECMD_OK; -} -#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ - -static void -you_sanity_check(void) -{ - struct monst *mtmp; - - if (u.uswallow && !u.ustuck) { - /* this probably ought to be panic() */ - impossible("sanity_check: swallowed by nothing?"); - display_nhwindow(WIN_MESSAGE, TRUE); - /* try to recover from whatever the problem is */ - u.uswallow = 0; - u.uswldtim = 0; - docrt(); - } - if ((mtmp = m_at(u.ux, u.uy)) != 0) { - /* u.usteed isn't on the map */ - if (u.ustuck != mtmp) - impossible("sanity_check: you over monster"); - } - - check_wornmask_slots(); - (void) check_invent_gold("invent"); -} - -void -sanity_check(void) -{ - if (iflags.sanity_no_check) { - /* in case a recurring sanity_check warning occurs, we mustn't - re-trigger it when ^P is used, otherwise msg_window:Single - and msg_window:Combination will always repeat the most recent - instance, never able to go back to any earlier messages */ - iflags.sanity_no_check = FALSE; - return; - } - you_sanity_check(); - obj_sanity_check(); - timer_sanity_check(); - mon_sanity_check(); - light_sources_sanity_check(); - bc_sanity_check(); - trap_sanity_check(); - engraving_sanity_check(); -} - -/* qsort() comparison routine for use in list_migrating_mons() */ -static int QSORTCALLBACK -migrsort_cmp(const genericptr vptr1, const genericptr vptr2) -{ - const struct monst *m1 = *(const struct monst **) vptr1, - *m2 = *(const struct monst **) vptr2; - int d1 = (int) m1->mux, l1 = (int) m1->muy, - d2 = (int) m2->mux, l2 = (int) m2->muy; - - /* if different branches, sort by dungeon number */ - if (d1 != d2) - return d1 - d2; - /* within same branch, sort by level number */ - if (l1 != l2) - return l1 - l2; - /* same destination level: use a tie-breaker to force stable sort; - monst->m_id is unsigned so we need more than just simple subtraction */ - return (m1->m_id < m2->m_id) ? -1 : (m1->m_id > m2->m_id); -} - -/* called by #migratemons; displays count of migrating monsters, optionally - displays them as well */ -static void -list_migrating_mons( - d_level *nextlevl) /* default destination for wiz_migrate_mons() */ -{ - winid win = WIN_ERR; - boolean showit = FALSE; - unsigned n; - int xyloc; - coordxy x, y; - char c, prmpt[10], xtra[10], buf[BUFSZ]; - struct monst *mtmp, **marray; - int here = 0, nxtlv = 0, other = 0; - - for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { - if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) - ++here; - else if (mtmp->mux == nextlevl->dnum && mtmp->muy == nextlevl->dlevel) - ++nxtlv; - else - ++other; - } - if (here + nxtlv + other == 0) { - pline("No monsters currently migrating."); - } else { - pline( - "%d mon%s pending for current level, %d for next level, %d for others.", - here, plur(here), nxtlv, other); - prmpt[0] = xtra[0] = '\0'; - (void) strkitten(here ? prmpt : xtra, 'c'); - (void) strkitten(nxtlv ? prmpt : xtra, 'n'); - (void) strkitten(other ? prmpt : xtra, 'o'); - Strcat(prmpt, "a q"); - if (*xtra) - Sprintf(eos(prmpt), "%c%s", '\033', xtra); - c = yn_function("List which?", prmpt, 'q', TRUE); - n = (c == 'c') ? here - : (c == 'n') ? nxtlv - : (c == 'o') ? other - : (c == 'a') ? here + nxtlv + other - : 0; - if (n > 0) { - win = create_nhwindow(NHW_TEXT); - switch (c) { - case 'c': - case 'n': - case 'o': - Sprintf(buf, "Monster%s migrating to %s:", plur(n), - (c == 'c') ? "current level" - : (c == 'n') ? "next level" - : "'other' levels"); - break; - default: - Strcpy(buf, "All migrating monsters:"); - break; - } - putstr(win, 0, buf); - putstr(win, 0, ""); - /* collect the migrating monsters into an array; for 'o' and 'a' - where multiple destination levels might be present, sort by - the destination; 'c' and 'n' don't need to be sorted but we - do that anyway to get the same tie-breaker as 'o' and 'a' */ - marray = (struct monst **) alloc((n + 1) * sizeof *marray); - n = 0; - for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { - if (c == 'a') - showit = TRUE; - else if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) - showit = (c == 'c'); - else if (mtmp->mux == nextlevl->dnum - && mtmp->muy == nextlevl->dlevel) - showit = (c == 'n'); - else - showit = (c == 'o'); - - if (showit) - marray[n++] = mtmp; - } - marray[n] = (struct monst *) 0; /* mark end for traversal loop */ - if (n > 1) - qsort((genericptr_t) marray, (size_t) n, sizeof *marray, - migrsort_cmp); /* sort elements [0] through [n-1] */ - for (n = 0; (mtmp = marray[n]) != 0; ++n) { - Sprintf(buf, " %s", minimal_monnam(mtmp, FALSE)); - /* minimal_monnam() appends map coordinates; strip that */ - (void) strsubst(buf, " <0,0>", ""); - if (has_mgivenname(mtmp)) /* if mtmp is named, include that */ - Sprintf(eos(buf), " named %s", MGIVENNAME(mtmp)); - if (c == 'o' || c == 'a') - Sprintf(eos(buf), " to %d:%d", mtmp->mux, mtmp->muy); - xyloc = mtmp->mtrack[0].x; /* (for legibility) */ - if (xyloc == MIGR_EXACT_XY) { - x = mtmp->mtrack[1].x; - y = mtmp->mtrack[1].y; - Sprintf(eos(buf), " at <%d,%d>", (int) x, (int) y); - } - putstr(win, 0, buf); - } - free((genericptr_t) marray); - display_nhwindow(win, FALSE); - destroy_nhwindow(win); - } else if (c != 'q') { - pline("None."); - } - - } -} - -/* #migratemons command */ -static int -wiz_migrate_mons(void) -{ -#ifdef DEBUG_MIGRATING_MONS - int mcount; - char inbuf[BUFSZ]; - struct permonst *ptr; - struct monst *mtmp; -#endif - d_level tolevel; - - if (Is_stronghold(&u.uz)) - assign_level(&tolevel, &valley_level); - else if (!Is_botlevel(&u.uz)) - get_level(&tolevel, depth(&u.uz) + 1); - else - tolevel.dnum = 0, tolevel.dlevel = 0; - - list_migrating_mons(&tolevel); - -#ifdef DEBUG_MIGRATING_MONS - inbuf[0] = '\033', inbuf[1] = '\0'; - if (tolevel.dnum || tolevel.dlevel) - getlin("How many random monsters to migrate to next level? [0]", - inbuf); - else - pline("Can't get there from here."); - if (*inbuf == '\033') - return ECMD_OK; - - mcount = atoi(inbuf); - if (mcount < 1) - mcount = 0; - else if (mcount > ((COLNO - 1) * ROWNO)) - mcount = (COLNO - 1) * ROWNO; - - while (mcount > 0) { - ptr = rndmonst(); - mtmp = makemon(ptr, 0, 0, MM_NOMSG); - if (mtmp) - migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM, - (coord *) 0); - mcount--; - } -#endif /* DEBUG_MIGRATING_MONS */ - return ECMD_OK; -} - static struct { int nhkf; uchar key; diff --git a/src/date.c b/src/date.c index 678120d98..8764a89be 100644 --- a/src/date.c +++ b/src/date.c @@ -3,20 +3,21 @@ /* NetHack may be freely redistributed. See license for details. */ #include "config.h" +#include "hacklib.h" +#ifdef Snprintf +#undef Snprintf +#endif +#define Snprintf(str, size, ...) \ + nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) /* these are in extern.h but we don't include hack.h */ void populate_nomakedefs(struct version_info *) NONNULLARG1; void free_nomakedefs(void); -#define Snprintf(str, size, ...) \ - nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) -extern void nh_snprintf(const char *func, int line, char *str, size_t size, - const char *fmt, ...); extern unsigned long md_ignored_features(void); extern char *mdlib_version_string(char *, const char *); extern char *version_id_string(char *, size_t, const char *); extern char *bannerc_string(char *, size_t, const char *); -extern int case_insensitive_comp(const char *, const char *); /* nomakedefs_populated: flag for whether 'nomakedefs' should be freed */ static int nomakedefs_populated = 0; diff --git a/src/do_name.c b/src/do_name.c index 32b96fc38..6a3eed42d 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -6,21 +6,6 @@ #include "hack.h" static char *nextmbuf(void); -static void getpos_toggle_hilite_state(void); -static void getpos_getvalids_selection(struct selectionvar *, - boolean (*)(coordxy, coordxy)); -static void selection_force_newsyms(struct selectionvar *); -static void getpos_help_keyxhelp(winid, const char *, const char *, int); -static void getpos_help(boolean, const char *); -static int QSORTCALLBACK cmp_coord_distu(const void *, const void *); -static int gloc_filter_classify_glyph(int); -static int gloc_filter_floodfill_matcharea(coordxy, coordxy); -static void gloc_filter_floodfill(coordxy, coordxy); -static void gloc_filter_init(void); -static void gloc_filter_done(void); -static void gather_locs(coord **, int *, int); -static void truncate_to_map(coordxy *, coordxy *, schar, schar); -static void getpos_refresh(void); static char *name_from_player(char *, const char *, const char *); static void do_mgivenname(void); static boolean alreadynamed(struct monst *, char *, char *) NONNULLPTRS; @@ -28,8 +13,6 @@ static void do_oname(struct obj *) NONNULLARG1; static char *docall_xname(struct obj *) NONNULLARG1; static void namefloorobj(void); -extern const char what_is_an_unknown_object[]; /* from pager.c */ - #define NUMMBUF 5 /* manage a pool of BUFSZ buffers, so callers don't have to */ @@ -43,1133 +26,6 @@ nextmbuf(void) return bufs[bufidx]; } -/* Callback function for getpos() to highlight desired map locations. - * Parameter TRUE: initialize and highlight, FALSE: done (remove highlights). - */ -static void (*getpos_hilitefunc)(boolean) = (void (*)(boolean)) 0; -static boolean (*getpos_getvalid)(coordxy, coordxy) - = (boolean (*)(coordxy, coordxy)) 0; -enum getposHiliteState { - HiliteNormalMap = 0, - HiliteGoodposSymbol = 1, - HiliteBackground = 2, -}; -static enum getposHiliteState - getpos_hilite_state = HiliteNormalMap, - defaultHiliteState = HiliteNormalMap; - -void -getpos_sethilite( - void (*gp_hilitef)(boolean), - boolean (*gp_getvalidf)(coordxy, coordxy)) -{ - boolean (*old_getvalid)(coordxy, coordxy) = getpos_getvalid; - uint32 old_map_frame_color = gw.wsettings.map_frame_color; - struct selectionvar *sel = selection_new(); - - defaultHiliteState = iflags.bgcolors ? HiliteBackground : HiliteNormalMap; - if (gp_getvalidf != old_getvalid) - getpos_hilite_state = defaultHiliteState; - - getpos_getvalids_selection(sel, getpos_getvalid); - getpos_hilitefunc = gp_hilitef; - getpos_getvalid = gp_getvalidf; - getpos_getvalids_selection(sel, getpos_getvalid); - gw.wsettings.map_frame_color = (getpos_hilite_state == HiliteBackground) - ? HI_ZAP : NO_COLOR; - - if (getpos_getvalid != old_getvalid - || gw.wsettings.map_frame_color != old_map_frame_color) - selection_force_newsyms(sel); - selection_free(sel, TRUE); -} - -/* cycle 'getpos_hilite_state' to its next state; - when 'bgcolors' is Off, it will alternate between not showing valid - positions and showing them via temporary S_goodpos symbol; - when 'bgcolors' is On, there are three states and showing them via - setting background color becomes the default */ -static void -getpos_toggle_hilite_state(void) -{ - /* getpos_hilitefunc isn't Null */ - if (getpos_hilite_state == HiliteGoodposSymbol) { - /* currently on, finish */ - (*getpos_hilitefunc)(FALSE); /* tmp_at(DISP_END) */ - } - - getpos_hilite_state = (getpos_hilite_state + 1) - % (iflags.bgcolors ? 3 : 2); - /* resetting the callback functions to their current values will draw - valid-spots with background color if that is the new state and turn - off that color if it was the previous state */ - getpos_sethilite(getpos_hilitefunc, getpos_getvalid); - - if (getpos_hilite_state == HiliteGoodposSymbol) { - /* now on, begin */ - (*getpos_hilitefunc)(TRUE); - } -} - -boolean -mapxy_valid(coordxy x, coordxy y) -{ - if (getpos_getvalid) - return (*getpos_getvalid)(x, y); - return FALSE; -} - -static void -getpos_getvalids_selection( - struct selectionvar *sel, - boolean (*validf)(coordxy, coordxy)) -{ - coordxy x, y; - - if (!sel || !validf) - return; - - for (x = 1; x < sel->wid; x++) - for (y = 0; y < sel->hei; y++) - if ((*validf)(x, y)) - selection_setpoint(x, y, sel, 1); -} - -static void -selection_force_newsyms(struct selectionvar *sel) -{ - coordxy x, y; - - for (x = 1; x < sel->wid; x++) - for (y = 0; y < sel->hei; y++) - if (selection_getpoint(x, y, sel)) - newsym_force(x, y); -} - -static const char *const gloc_descr[NUM_GLOCS][4] = { - { "any monsters", "monster", "next/previous monster", "monsters" }, - { "any items", "item", "next/previous object", "objects" }, - { "any doors", "door", "next/previous door or doorway", - "doors or doorways" }, - { "any unexplored areas", "unexplored area", "unexplored location", - "locations next to unexplored locations" }, - { "anything interesting", "interesting thing", "anything interesting", - "anything interesting" }, - { "any valid locations", "valid location", "valid location", - "valid locations" } -}; - -static const char *const gloc_filtertxt[NUM_GFILTER] = { - "", - " in view", - " in this area" -}; - -static void -getpos_help_keyxhelp( - winid tmpwin, - const char *k1, const char *k2, - int gloc) -{ - char sbuf[BUFSZ], fbuf[QBUFSZ]; - const char *move_cursor_to = "move the cursor to ", - *filtertxt = gloc_filtertxt[iflags.getloc_filter]; - - if (gloc == GLOC_EXPLORE) { - /* default of "move to unexplored location" is inaccurate - because the position will be one spot short of that */ - move_cursor_to = "move the cursor next to an "; - if (iflags.getloc_usemenu) - /* default is too wide for basic 80-column tty so shorten it - to avoid wrapping */ - filtertxt = strsubst(strcpy(fbuf, filtertxt), - "this area", "area"); - } - Sprintf(sbuf, "Use '%s'/'%s' to %s%s%s.", - k1, k2, - iflags.getloc_usemenu ? "get a menu of " : move_cursor_to, - gloc_descr[gloc][2 + iflags.getloc_usemenu], filtertxt); - putstr(tmpwin, 0, sbuf); -} - -DISABLE_WARNING_FORMAT_NONLITERAL - -/* the response for '?' help request in getpos() */ -static void -getpos_help(boolean force, const char *goal) -{ - static const char *const fastmovemode[2] = { "8 units at a time", - "skipping same glyphs" }; - char sbuf[BUFSZ]; - boolean doing_what_is; - winid tmpwin = create_nhwindow(NHW_MENU); - - Sprintf(sbuf, - "Use '%s', '%s', '%s', '%s' to move the cursor to %s.", /* hjkl */ - visctrl(cmd_from_func(do_move_west)), - visctrl(cmd_from_func(do_move_south)), - visctrl(cmd_from_func(do_move_north)), - visctrl(cmd_from_func(do_move_east)), goal); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - "Use '%s', '%s', '%s', '%s' to fast-move the cursor, %s.", - visctrl(cmd_from_func(do_run_west)), - visctrl(cmd_from_func(do_run_south)), - visctrl(cmd_from_func(do_run_north)), - visctrl(cmd_from_func(do_run_east)), - fastmovemode[iflags.getloc_moveskip]); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, "(or prefix normal move with '%s' or '%s' to fast-move)", - visctrl(cmd_from_func(do_run)), - visctrl(cmd_from_func(do_rush))); - putstr(tmpwin, 0, sbuf); - putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); - Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); - putstr(tmpwin, 0, sbuf); - if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_PREV]), - GLOC_MONS); - } - if (goal && !strcmp(goal, "a monster")) - goto skip_non_mons; - if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) { - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]), - GLOC_OBJS); - } - if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) { - /* these are primarily useful when choosing a travel - destination for the '_' command */ - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]), - GLOC_DOOR); - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]), - GLOC_EXPLORE); - getpos_help_keyxhelp(tmpwin, - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]), - GLOC_INTERESTING); - } - Sprintf(sbuf, "Use '%s' to change fast-move mode to %s.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]), - fastmovemode[!iflags.getloc_moveskip]); - putstr(tmpwin, 0, sbuf); - if (!iflags.terrainmode || (iflags.terrainmode & TER_DETECT) == 0) { - Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MENU])); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - "Use '%s' to change the mode of limiting possible targets.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW])); - putstr(tmpwin, 0, sbuf); - } - if (!iflags.terrainmode) { - char kbuf[BUFSZ]; - - if (getpos_getvalid) { - Sprintf(sbuf, "Use '%s' or '%s' to move to valid locations.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_NEXT]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_PREV])); - putstr(tmpwin, 0, sbuf); - } - if (getpos_hilitefunc) { - Sprintf(sbuf, "Use '%s' to toggle marking of valid locations.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID])); - putstr(tmpwin, 0, sbuf); - } - Sprintf(sbuf, "Use '%s' to toggle automatic description.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); - putstr(tmpwin, 0, sbuf); - if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */ - Sprintf(sbuf, - (iflags.getpos_coords == GPCOORDS_NONE) - ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)" - : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); - } - skip_non_mons: - /* disgusting hack; the alternate selection characters work for any - getpos call, but only matter for dowhatis (and doquickwhatis, - also for dotherecmdmenu's simulated mouse) */ - doing_what_is = (goal == what_is_an_unknown_object); - if (doing_what_is) { - Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O]), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); - } else { - Sprintf(kbuf, "'%s'", visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); - } - Snprintf(sbuf, sizeof(sbuf), - "Type a %s when you are at the right place.", kbuf); - putstr(tmpwin, 0, sbuf); - if (doing_what_is) { - Sprintf(sbuf, - " '%s' describe current spot, show 'more info', move to another spot.", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - " '%s' describe current spot,%s move to another spot;", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), - flags.help && !force ? " prompt if 'more info'," : ""); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - " '%s' describe current spot, move to another spot;", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q])); - putstr(tmpwin, 0, sbuf); - Sprintf(sbuf, - " '%s' describe current spot, stop looking at things;", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O])); - putstr(tmpwin, 0, sbuf); - } - } - if (!force) - putstr(tmpwin, 0, "Type Space or Escape when you're done."); - putstr(tmpwin, 0, ""); - display_nhwindow(tmpwin, TRUE); - destroy_nhwindow(tmpwin); -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -static int QSORTCALLBACK -cmp_coord_distu(const void *a, const void *b) -{ - const coord *c1 = a; - const coord *c2 = b; - int dx, dy, dist_1, dist_2; - - dx = u.ux - c1->x; - dy = u.uy - c1->y; - dist_1 = max(abs(dx), abs(dy)); - dx = u.ux - c2->x; - dy = u.uy - c2->y; - dist_2 = max(abs(dx), abs(dy)); - - if (dist_1 == dist_2) - return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x); - - return dist_1 - dist_2; -} - -#define IS_UNEXPLORED_LOC(x,y) \ - (isok((x), (y)) \ - && glyph_is_unexplored(levl[(x)][(y)].glyph) \ - && !levl[(x)][(y)].seenv) - -#define GLOC_SAME_AREA(x,y) \ - (isok((x), (y)) \ - && (selection_getpoint((x),(y), gg.gloc_filter_map))) - -static int -gloc_filter_classify_glyph(int glyph) -{ - int c; - - if (!glyph_is_cmap(glyph)) - return 0; - - c = glyph_to_cmap(glyph); - - if (is_cmap_room(c) || is_cmap_furniture(c)) - return 1; - else if (is_cmap_wall(c) || c == S_tree) - return 2; - else if (is_cmap_corr(c)) - return 3; - else if (is_cmap_water(c)) - return 4; - else if (is_cmap_lava(c)) - return 5; - return 0; -} - -static int -gloc_filter_floodfill_matcharea(coordxy x, coordxy y) -{ - int glyph = back_to_glyph(x, y); - - if (!levl[x][y].seenv) - return FALSE; - - if (glyph == gg.gloc_filter_floodfill_match_glyph) - return TRUE; - - if (gloc_filter_classify_glyph(glyph) - == gloc_filter_classify_glyph(gg.gloc_filter_floodfill_match_glyph)) - return TRUE; - - return FALSE; -} - -static void -gloc_filter_floodfill(coordxy x, coordxy y) -{ - gg.gloc_filter_floodfill_match_glyph = back_to_glyph(x, y); - - set_selection_floodfillchk(gloc_filter_floodfill_matcharea); - selection_floodfill(gg.gloc_filter_map, x, y, FALSE); -} - -static void -gloc_filter_init(void) -{ - if (iflags.getloc_filter == GFILTER_AREA) { - if (!gg.gloc_filter_map) { - gg.gloc_filter_map = selection_new(); - } - /* special case: if we're in a doorway, try to figure out which - direction we're moving, and use that side of the doorway */ - if (IS_DOOR(levl[u.ux][u.uy].typ)) { - if (u.dx || u.dy) { - gloc_filter_floodfill(u.ux + u.dx, u.uy + u.dy); - } else { - /* TODO: maybe add both sides of the doorway? */ - } - } else { - gloc_filter_floodfill(u.ux, u.uy); - } - } -} - -static void -gloc_filter_done(void) -{ - if (gg.gloc_filter_map) { - selection_free(gg.gloc_filter_map, TRUE); - gg.gloc_filter_map = (struct selectionvar *) 0; - - } -} - -DISABLE_WARNING_UNREACHABLE_CODE - -boolean -gather_locs_interesting(coordxy x, coordxy y, int gloc) -{ - int glyph, sym; - - if (iflags.getloc_filter == GFILTER_VIEW && !cansee(x, y)) - return FALSE; - if (iflags.getloc_filter == GFILTER_AREA && !GLOC_SAME_AREA(x, y) - && !GLOC_SAME_AREA(x - 1, y) && !GLOC_SAME_AREA(x, y - 1) - && !GLOC_SAME_AREA(x + 1, y) && !GLOC_SAME_AREA(x, y + 1)) - return FALSE; - - glyph = glyph_at(x, y); - sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) : -1; - switch (gloc) { - default: - case GLOC_MONS: - /* unlike '/M', this skips monsters revealed by - warning glyphs and remembered unseen ones */ - return (glyph_is_monster(glyph) - && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL,MALE) - && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, FEMALE)); - case GLOC_OBJS: - return (glyph_is_object(glyph) - && glyph != objnum_to_glyph(BOULDER) - && glyph != objnum_to_glyph(ROCK)); - case GLOC_DOOR: - return (glyph_is_cmap(glyph) - && (is_cmap_door(sym) - || is_cmap_drawbridge(sym) - || sym == S_ndoor)); - case GLOC_EXPLORE: - return (glyph_is_cmap(glyph) - && !glyph_is_nothing(glyph_to_cmap(glyph)) - && (is_cmap_door(sym) - || is_cmap_drawbridge(sym) - || sym == S_ndoor - || is_cmap_room(sym) - || is_cmap_corr(sym)) - && (IS_UNEXPLORED_LOC(x + 1, y) - || IS_UNEXPLORED_LOC(x - 1, y) - || IS_UNEXPLORED_LOC(x, y + 1) - || IS_UNEXPLORED_LOC(x, y - 1))); - case GLOC_VALID: - if (getpos_getvalid) - return (*getpos_getvalid)(x, y); - /*FALLTHRU*/ - case GLOC_INTERESTING: - return (gather_locs_interesting(x, y, GLOC_DOOR) - || !((glyph_is_cmap(glyph) - && (is_cmap_wall(sym) - || sym == S_tree - || sym == S_bars - || sym == S_ice - || sym == S_air - || sym == S_cloud - || is_cmap_lava(sym) - || is_cmap_water(sym) - || sym == S_ndoor - || is_cmap_room(sym) - || is_cmap_corr(sym))) - || glyph_is_nothing(glyph) - || glyph_is_unexplored(glyph))); - } - /*NOTREACHED*/ - return FALSE; -} - -RESTORE_WARNINGS - -/* gather locations for monsters or objects shown on the map */ -static void -gather_locs(coord **arr_p, int *cnt_p, int gloc) -{ - int pass, idx; - coordxy x, y; - - /* - * We always include the hero's location even if there is no monster - * (invisible hero without see invisible) or object (usual case) - * displayed there. That way, the count will always be at least 1, - * and player has a visual indicator (cursor returns to hero's spot) - * highlighting when successive 'm's or 'o's have cycled all the way - * through all monsters or objects. - * - * Hero's spot will always sort to array[0] because it will always - * be the shortest distance (namely, 0 units) away from . - */ - - gloc_filter_init(); - - *cnt_p = idx = 0; - for (pass = 0; pass < 2; pass++) { - for (x = 1; x < COLNO; x++) - for (y = 0; y < ROWNO; y++) { - if (u_at(x, y) || gather_locs_interesting(x, y, gloc)) { - if (!pass) { - ++*cnt_p; - } else { - (*arr_p)[idx].x = x; - (*arr_p)[idx].y = y; - ++idx; - } - } - } - - if (!pass) /* end of first pass */ - *arr_p = (coord *) alloc(*cnt_p * sizeof (coord)); - else /* end of second pass */ - qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu); - } /* pass */ - - gloc_filter_done(); -} - -char * -dxdy_to_dist_descr(coordxy dx, coordxy dy, boolean fulldir) -{ - static char buf[30]; - int dst; - - if (!dx && !dy) { - Sprintf(buf, "here"); - } else if ((dst = xytod(dx, dy)) != -1) { - /* explicit direction; 'one step' is implicit */ - Sprintf(buf, "%s", directionname(dst)); - } else { - static const char *const dirnames[4][2] = { - { "n", "north" }, - { "s", "south" }, - { "w", "west" }, - { "e", "east" } }; - buf[0] = '\0'; - /* 9999: protect buf[] against overflow caused by invalid values */ - if (dy) { - if (abs(dy) > 9999) - dy = sgn(dy) * 9999; - Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir], - dx ? "," : ""); - } - if (dx) { - if (abs(dx) > 9999) - dx = sgn(dx) * 9999; - Sprintf(eos(buf), "%d%s", abs(dx), - dirnames[2 + (dx > 0)][fulldir]); - } - } - return buf; -} - -DISABLE_WARNING_FORMAT_NONLITERAL - -/* coordinate formatting for 'whatis_coord' option */ -char * -coord_desc(coordxy x, coordxy y, char *outbuf, char cmode) -{ - static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */ - int dx, dy; - - outbuf[0] = '\0'; - switch (cmode) { - default: - break; - case GPCOORDS_COMFULL: - case GPCOORDS_COMPASS: - /* "east", "3s", "2n,4w" */ - dx = x - u.ux; - dy = y - u.uy; - Sprintf(outbuf, "(%s)", - dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL)); - break; - case GPCOORDS_MAP: /* x,y */ - /* upper left corner of map is <1,0>; - with default COLNO,ROWNO lower right corner is <79,20> */ - Sprintf(outbuf, "<%d,%d>", x, y); - break; - case GPCOORDS_SCREEN: /* y+2,x */ - /* for normal map sizes, force a fixed-width formatting so that - /m, /M, /o, and /O output lines up cleanly; map sizes bigger - than Nx999 or 999xM will still work, but not line up like normal - when displayed in a column setting. - - The (100) is placed in brackets below to mark the [: "03"] as - explicit compile-time dead code for clang */ - if (!*screen_fmt) - Sprintf(screen_fmt, "[%%%sd,%%%sd]", - (ROWNO - 1 + 2 < (100)) ? "02" : "03", - (COLNO - 1 < (100)) ? "02" : "03"); - /* map line 0 is screen row 2; - map column 0 isn't used, map column 1 is screen column 1 */ - Sprintf(outbuf, screen_fmt, y + 2, x); - break; - } - return outbuf; -} - -RESTORE_WARNING_FORMAT_NONLITERAL - -void -auto_describe(coordxy cx, coordxy cy) -{ - coord cc; - int sym = 0; - char tmpbuf[BUFSZ]; - const char *firstmatch = "unknown"; - - cc.x = cx; - cc.y = cy; - if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch, - (struct permonst **) 0)) { - (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords); - custompline((SUPPRESS_HISTORY | OVERRIDE_MSGTYPE | NO_CURS_ON_U), - "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf, - (iflags.autodescribe - && getpos_getvalid && !(*getpos_getvalid)(cx, cy)) - ? " (invalid target)" : "", - (iflags.getloc_travelmode && !is_valid_travelpt(cx, cy)) - ? " (no travel path)" : ""); - curs(WIN_MAP, cx, cy); - flush_screen(0); - } -} - -boolean -getpos_menu(coord *ccp, int gloc) -{ - coord *garr = DUMMY; - int gcount = 0; - winid tmpwin; - anything any; - int i, pick_cnt; - menu_item *picks = (menu_item *) 0; - char tmpbuf[BUFSZ]; - int clr = NO_COLOR; - - gather_locs(&garr, &gcount, gloc); - - if (gcount < 2) { /* gcount always includes the hero */ - free((genericptr_t) garr); - You("cannot %s %s.", - (iflags.getloc_filter == GFILTER_VIEW) ? "see" : "detect", - gloc_descr[gloc][0]); - return FALSE; - } - - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - - /* gather_locs returns array[0] == you. skip it. */ - for (i = 1; i < gcount; i++) { - char fullbuf[BUFSZ]; - coord tmpcc; - const char *firstmatch = "unknown"; - int sym = 0; - - any.a_int = i + 1; - tmpcc.x = garr[i].x; - tmpcc.y = garr[i].y; - if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, - &firstmatch, (struct permonst **)0)) { - (void) coord_desc(garr[i].x, garr[i].y, tmpbuf, - iflags.getpos_coords); - Snprintf(fullbuf, sizeof fullbuf, "%s%s%s", firstmatch, - (*tmpbuf ? " " : ""), tmpbuf); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, fullbuf, MENU_ITEMFLAGS_NONE); - } - } - - Sprintf(tmpbuf, "Pick %s%s%s", - an(gloc_descr[gloc][1]), - gloc_filtertxt[iflags.getloc_filter], - iflags.getloc_travelmode ? " for travel destination" : ""); - end_menu(tmpwin, tmpbuf); - pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); - destroy_nhwindow(tmpwin); - if (pick_cnt > 0) { - ccp->x = garr[picks->item.a_int - 1].x; - ccp->y = garr[picks->item.a_int - 1].y; - free((genericptr_t) picks); - } - free((genericptr_t) garr); - return (pick_cnt > 0); -} - -/* add dx,dy to cx,cy, truncating at map edges */ -static void -truncate_to_map(coordxy *cx, coordxy *cy, schar dx, schar dy) -{ - /* diagonal moves complicate this... */ - if (*cx + dx < 1) { - dy -= sgn(dy) * (1 - (*cx + dx)); - dx = 1 - *cx; /* so that (cx+dx == 1) */ - } else if (*cx + dx > COLNO - 1) { - dy += sgn(dy) * ((COLNO - 1) - (*cx + dx)); - dx = (COLNO - 1) - *cx; - } - if (*cy + dy < 0) { - dx -= sgn(dx) * (0 - (*cy + dy)); - dy = 0 - *cy; /* so that (cy+dy == 0) */ - } else if (*cy + dy > ROWNO - 1) { - dx += sgn(dx) * ((ROWNO - 1) - (*cy + dy)); - dy = (ROWNO - 1) - *cy; - } - *cx += dx; - *cy += dy; -} - -/* called when ^R typed; if '$' is being shown for valid spots, remove that; - if alternate background color is being shown for that, redraw it */ -static void -getpos_refresh(void) -{ - if (getpos_hilitefunc && getpos_hilite_state == HiliteGoodposSymbol) { - (*getpos_hilitefunc)(FALSE); /* tmp_at(DISP_END) */ - getpos_hilite_state = defaultHiliteState; - } - - docrt_flags(docrtRefresh); - - if (getpos_hilitefunc && getpos_hilite_state == HiliteBackground) { - /* resetting to current values will draw valid-spots highlighting */ - getpos_sethilite(getpos_hilitefunc, getpos_getvalid); - } -} - -/* have the player use movement keystrokes to position the cursor at a - particular map location, then use one of [.,:;] to pick the spot */ -int -getpos(coord *ccp, boolean force, const char *goal) -{ - static struct { - int nhkf, ret; - } const pick_chars_def[] = { - { NHKF_GETPOS_PICK, LOOK_TRADITIONAL }, - { NHKF_GETPOS_PICK_Q, LOOK_QUICK }, - { NHKF_GETPOS_PICK_O, LOOK_ONCE }, - { NHKF_GETPOS_PICK_V, LOOK_VERBOSE } - }; - static const int mMoOdDxX_def[] = { - NHKF_GETPOS_MON_NEXT, - NHKF_GETPOS_MON_PREV, - NHKF_GETPOS_OBJ_NEXT, - NHKF_GETPOS_OBJ_PREV, - NHKF_GETPOS_DOOR_NEXT, - NHKF_GETPOS_DOOR_PREV, - NHKF_GETPOS_UNEX_NEXT, - NHKF_GETPOS_UNEX_PREV, - NHKF_GETPOS_INTERESTING_NEXT, - NHKF_GETPOS_INTERESTING_PREV, - NHKF_GETPOS_VALID_NEXT, - NHKF_GETPOS_VALID_PREV - }; - struct _cmd_queue cq, *cmdq; - const char *cp; - char pick_chars[6]; - char mMoOdDxX[13]; - int result = 0; - int i, c; - int sidx; - coordxy cx, cy; - coordxy tx = u.ux, ty = u.uy; - boolean msg_given = TRUE; /* clear message window by default */ - boolean show_goal_msg = FALSE; - coord *garr[NUM_GLOCS] = DUMMY; - int gcount[NUM_GLOCS] = DUMMY; - int gidx[NUM_GLOCS] = DUMMY; - schar udx = u.dx, udy = u.dy, udz = u.dz; - int dx, dy; - boolean rushrun = FALSE; - - /* temporary? if we have a queued direction, return the adjacent spot - in that direction */ - if (!gi.in_doagain) { - if ((cmdq = cmdq_pop()) != 0) { - cq = *cmdq; - free((genericptr_t) cmdq); - if (cq.typ == CMDQ_DIR && !cq.dirz) { - ccp->x = u.ux + cq.dirx; - ccp->y = u.uy + cq.diry; - } else { - cmdq_clear(CQ_CANNED); - result = -1; - } - return result; - } - } - - for (i = 0; i < SIZE(pick_chars_def); i++) - pick_chars[i] = gc.Cmd.spkeys[pick_chars_def[i].nhkf]; - pick_chars[SIZE(pick_chars_def)] = '\0'; - - for (i = 0; i < SIZE(mMoOdDxX_def); i++) - mMoOdDxX[i] = gc.Cmd.spkeys[mMoOdDxX_def[i]]; - mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0'; - - handle_tip(TIP_GETPOS); - - if (!goal) - goal = "desired location"; - if (flags.verbose) { - pline("(For instructions type a '%s')", - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_HELP])); - msg_given = TRUE; - } - cx = gg.getposx = ccp->x; - cy = gg.getposy = ccp->y; -#ifdef CLIPPING - cliparound(cx, cy); -#endif - curs(WIN_MAP, cx, cy); - flush_screen(0); -#ifdef MAC - lock_mouse_cursor(TRUE); -#endif - lock_mouse_buttons(TRUE); - for (;;) { - if (show_goal_msg) { - pline("Move cursor to %s:", goal); - curs(WIN_MAP, cx, cy); - flush_screen(0); - show_goal_msg = FALSE; - } else if (iflags.autodescribe && !msg_given) { - auto_describe(cx, cy); - } - - rushrun = FALSE; - - if ((cmdq = cmdq_pop()) != 0) { - if (cmdq->typ == CMDQ_KEY) { - c = cmdq->key; - } else { - cmdq_clear(CQ_CANNED); - result = -1; - goto exitgetpos; - } - free(cmdq); - } else { - c = readchar_poskey(&tx, &ty, &sidx); - /* remember_getpos is normally False because reusing the - cursor positioning during ^A is almost never the right - thing to do, but caller could set it if that was needed */ - if (iflags.remember_getpos && !gi.in_doagain) - cmdq_add_key(CQ_REPEAT, c); - } - - if (iflags.autodescribe) - msg_given = FALSE; - - if (c == gc.Cmd.spkeys[NHKF_ESC]) { - cx = cy = -10; - msg_given = TRUE; /* force clear */ - result = -1; - break; - } - if (c == cmd_from_func(do_run) || c == cmd_from_func(do_rush)) { - c = readchar_poskey(&tx, &ty, &sidx); - rushrun = TRUE; - } - if (c == 0) { - if (!isok(tx, ty)) - continue; - /* a mouse click event, just assign and return */ - cx = tx; - cy = ty; - break; - } - if ((cp = strchr(pick_chars, c)) != 0) { - /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */ - result = pick_chars_def[(int) (cp - pick_chars)].ret; - break; - } else if (movecmd(c, MV_WALK)) { - if (rushrun) - goto do_rushrun; - dx = u.dx; - dy = u.dy; - truncate_to_map(&cx, &cy, dx, dy); - goto nxtc; - } else if (movecmd(c, MV_RUSH) || movecmd(c, MV_RUN)) { - do_rushrun: - if (iflags.getloc_moveskip) { - /* skip same glyphs */ - int glyph = glyph_at(cx, cy); - - dx = u.dx; - dy = u.dy; - while (isok(cx + dx, cy + dy) - && glyph == glyph_at(cx + dx, cy + dy) - && isok(cx + dx + u.dx, cy + dy + u.dy) - && glyph == glyph_at(cx + dx + u.dx, cy + dy + u.dy)) { - dx += u.dx; - dy += u.dy; - - } - } else { - dx = 8 * u.dx; - dy = 8 * u.dy; - } - truncate_to_map(&cx, &cy, dx, dy); - goto nxtc; - } - - if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) { - /* '?' will redraw twice, first when removing popup text window - after showing the help text, then to reset highlighting */ - if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP]) - getpos_help(force, goal); - /* ^R: docrt(), hilite_state = default */ - getpos_refresh(); - curs(WIN_MAP, cx, cy); - /* update message window to reflect that we're still targeting */ - show_goal_msg = TRUE; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]) { - if (getpos_hilitefunc) { - getpos_toggle_hilite_state(); - curs(WIN_MAP, cx, cy); - } - show_goal_msg = TRUE; /* we're still targeting */ - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC]) { - iflags.autodescribe = !iflags.autodescribe; - pline("Automatic description %sis %s.", - flags.verbose ? "of features under cursor " : "", - iflags.autodescribe ? "on" : "off"); - if (!iflags.autodescribe) - show_goal_msg = TRUE; - msg_given = TRUE; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) { - static const char *const view_filters[NUM_GFILTER] = { - "Not limiting targets", - "Limiting targets to those in sight", - "Limiting targets to those in same area" - }; - - iflags.getloc_filter = (iflags.getloc_filter + 1) % NUM_GFILTER; - for (i = 0; i < NUM_GLOCS; i++) { - if (garr[i]) { - free((genericptr_t) garr[i]); - garr[i] = NULL; - } - gidx[i] = gcount[i] = 0; - } - pline("%s.", view_filters[iflags.getloc_filter]); - msg_given = TRUE; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MENU]) { - iflags.getloc_usemenu = !iflags.getloc_usemenu; - pline("%s a menu to show possible targets%s.", - iflags.getloc_usemenu ? "Using" : "Not using", - iflags.getloc_usemenu - ? " for 'm|M', 'o|O', 'd|D', and 'x|X'" : ""); - msg_given = TRUE; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SELF]) { - /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player - to achieve that except by manually cycling through all spots */ - for (i = 0; i < NUM_GLOCS; i++) - gidx[i] = 0; - cx = u.ux; - cy = u.uy; - goto nxtc; - } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]) { - iflags.getloc_moveskip = !iflags.getloc_moveskip; - pline("%skipping over similar terrain when fastmoving the cursor.", - iflags.getloc_moveskip ? "S" : "Not s"); - msg_given = TRUE; - goto nxtc; - } else if ((cp = strchr(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */ - /* nearest or farthest monster or object or door or unexplored */ - int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */ - gloc = gtmp >> 1; /* 0..3 */ - - if (iflags.getloc_usemenu) { - coord tmpcrd; - - if (getpos_menu(&tmpcrd, gloc)) { - cx = tmpcrd.x; - cy = tmpcrd.y; - } - goto nxtc; - } - - if (!garr[gloc]) { - gather_locs(&garr[gloc], &gcount[gloc], gloc); - gidx[gloc] = 0; /* garr[][0] is hero's spot */ - } - if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */ - gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc]; - } else { /* c=='M' || c=='O' || c=='D' || c=='X') */ - if (--gidx[gloc] < 0) - gidx[gloc] = gcount[gloc] - 1; - } - cx = garr[gloc][gidx[gloc]].x; - cy = garr[gloc][gidx[gloc]].y; - goto nxtc; - } else { - if (!strchr(quitchars, c)) { - char matching[MAXPCHARS]; - int pass, k = 0; - coordxy lo_x, lo_y, hi_x, hi_y; - - (void) memset((genericptr_t) matching, 0, sizeof matching); - for (sidx = 0; sidx < MAXPCHARS; sidx++) { - /* don't even try to match some terrain: walls, room... */ - if (is_cmap_wall(sidx) || is_cmap_room(sidx) - || is_cmap_corr(sidx) || is_cmap_door(sidx) - || sidx == S_ndoor) - continue; - if (c == defsyms[sidx].sym - || c == (int) gs.showsyms[sidx] - /* have '^' match webs and vibrating square or any - other trap that uses something other than '^' */ - || (c == '^' && is_cmap_trap(sidx))) - matching[sidx] = (char) ++k; - } - if (k) { - for (pass = 0; pass <= 1; pass++) { - /* pass 0: just past current pos to lower right; - pass 1: upper left corner to current pos */ - lo_y = (pass == 0) ? cy : 0; - hi_y = (pass == 0) ? ROWNO - 1 : cy; - for (ty = lo_y; ty <= hi_y; ty++) { - lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1; - hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1; - for (tx = lo_x; tx <= hi_x; tx++) { - /* first, look at what is currently visible - (might be monster) */ - k = glyph_at(tx, ty); - if (glyph_is_cmap(k) - && matching[glyph_to_cmap(k)]) - goto foundc; - /* next, try glyph that's remembered here - (might be trap or object) */ - if (gl.level.flags.hero_memory - /* !terrainmode: don't move to remembered - trap or object if not currently shown */ - && !iflags.terrainmode) { - k = levl[tx][ty].glyph; - if (glyph_is_cmap(k) - && matching[glyph_to_cmap(k)]) - goto foundc; - } - /* last, try actual terrain here (shouldn't - we be using gl.lastseentyp[][] instead?) */ - if (levl[tx][ty].seenv) { - k = back_to_glyph(tx, ty); - if (glyph_is_cmap(k) - && matching[glyph_to_cmap(k)]) - goto foundc; - } - continue; - foundc: - cx = tx, cy = ty; - if (msg_given) { - clear_nhwindow(WIN_MESSAGE); - msg_given = FALSE; - } - goto nxtc; - } /* column */ - } /* row */ - } /* pass */ - pline("Can't find dungeon feature '%c'.", c); - msg_given = TRUE; - goto nxtc; - } else { - char note[QBUFSZ]; - - if (!force) - Strcpy(note, "aborted"); - else /* hjkl */ - Sprintf(note, "use '%s', '%s', '%s', '%s' or '%s'", - visctrl(cmd_from_func(do_move_west)), - visctrl(cmd_from_func(do_move_south)), - visctrl(cmd_from_func(do_move_north)), - visctrl(cmd_from_func(do_move_east)), - visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); - pline("Unknown direction: '%s' (%s).", visctrl((char) c), - note); - msg_given = TRUE; - } /* k => matching */ - } /* !quitchars */ - if (force) - goto nxtc; - pline("Done."); - msg_given = FALSE; /* suppress clear */ - cx = -1; - cy = 0; - result = 0; /* not -1 */ - break; - } - nxtc: - gg.getposx = cx, gg.getposy = cy; -#ifdef CLIPPING - cliparound(cx, cy); -#endif - curs(WIN_MAP, cx, cy); - flush_screen(0); - } - exitgetpos: -#ifdef MAC - lock_mouse_cursor(FALSE); -#endif - lock_mouse_buttons(FALSE); - if (msg_given) - clear_nhwindow(WIN_MESSAGE); - ccp->x = cx; - ccp->y = cy; - gg.getposx = gg.getposy = 0; - for (i = 0; i < NUM_GLOCS; i++) - if (garr[i]) - free((genericptr_t) garr[i]); - getpos_sethilite(NULL, NULL); - u.dx = udx, u.dy = udy, u.dz = udz; - return result; -} - /* allocate space for a monster's name; removes old name if there is one */ void new_mgivenname( diff --git a/src/dungeon.c b/src/dungeon.c index a78622a81..abf6fa149 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -1602,176 +1602,6 @@ u_on_rndspot(int upflag) switch_terrain(); } -void -stairway_add( - coordxy x, coordxy y, - boolean up, boolean isladder, - d_level *dest) -{ - stairway *tmp = (stairway *) alloc(sizeof (stairway)); - - (void) memset((genericptr_t) tmp, 0, sizeof (stairway)); - tmp->sx = x; - tmp->sy = y; - tmp->up = up; - tmp->isladder = isladder; - tmp->u_traversed = FALSE; - assign_level(&(tmp->tolev), dest); - tmp->next = gs.stairs; - gs.stairs = tmp; -} - -void -stairway_free_all(void) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - stairway *tmp2 = tmp->next; - free(tmp); - tmp = tmp2; - } - gs.stairs = NULL; -} - -stairway * -stairway_at(coordxy x, coordxy y) -{ - stairway *tmp = gs.stairs; - - while (tmp && !(tmp->sx == x && tmp->sy == y)) - tmp = tmp->next; - return tmp; -} - -stairway * -stairway_find(d_level *fromdlev) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - if (tmp->tolev.dnum == fromdlev->dnum - && tmp->tolev.dlevel == fromdlev->dlevel) - break; /* return */ - tmp = tmp->next; - } - return tmp; -} - -stairway * -stairway_find_from(d_level *fromdlev, boolean isladder) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - if (tmp->tolev.dnum == fromdlev->dnum - && tmp->tolev.dlevel == fromdlev->dlevel - && tmp->isladder == isladder) - break; /* return */ - tmp = tmp->next; - } - return tmp; -} - -stairway * -stairway_find_dir(boolean up) -{ - stairway *tmp = gs.stairs; - - while (tmp && !(tmp->up == up)) - tmp = tmp->next; - return tmp; -} - -stairway * -stairway_find_type_dir(boolean isladder, boolean up) -{ - stairway *tmp = gs.stairs; - - while (tmp && !(tmp->isladder == isladder && tmp->up == up)) - tmp = tmp->next; - return tmp; -} - -stairway * -stairway_find_special_dir(boolean up) -{ - stairway *tmp = gs.stairs; - - while (tmp) { - if (tmp->tolev.dnum != u.uz.dnum && tmp->up != up) - return tmp; - tmp = tmp->next; - } - return tmp; -} - -/* place you on the special staircase */ -void -u_on_sstairs(int upflag) -{ - stairway *stway = stairway_find_special_dir(upflag); - - if (stway) - u_on_newpos(stway->sx, stway->sy); - else - u_on_rndspot(upflag); -} - -/* place you on upstairs (or special equivalent) */ -void -u_on_upstairs(void) -{ - stairway *stway = stairway_find_dir(TRUE); - - if (stway) - u_on_newpos(stway->sx, stway->sy); - else - u_on_sstairs(0); /* destination upstairs implies moving down */ -} - -/* place you on dnstairs (or special equivalent) */ -void -u_on_dnstairs(void) -{ - stairway *stway = stairway_find_dir(FALSE); - - if (stway) - u_on_newpos(stway->sx, stway->sy); - else - u_on_sstairs(1); /* destination dnstairs implies moving up */ -} - -boolean -On_stairs(coordxy x, coordxy y) -{ - return (stairway_at(x, y) != NULL); -} - -boolean -On_ladder(coordxy x, coordxy y) -{ - stairway *stway = stairway_at(x, y); - - return (boolean) (stway && stway->isladder); -} - -boolean -On_stairs_up(coordxy x, coordxy y) -{ - stairway *stway = stairway_at(x, y); - - return (boolean) (stway && stway->up); -} - -boolean -On_stairs_dn(coordxy x, coordxy y) -{ - stairway *stway = stairway_at(x, y); - - return (boolean) (stway && !stway->up); -} - boolean Is_botlevel(d_level *lev) { @@ -2364,66 +2194,6 @@ tport_menu( return; } -/* return True if 'sway' is a branch staircase and hero has used these stairs - to visit the branch */ -boolean -known_branch_stairs(stairway *sway) -{ - return (sway && sway->tolev.dnum != u.uz.dnum && sway->u_traversed); -} - -/* describe staircase 'sway' based on whether hero knows the destination */ -char * -stairs_description( - stairway *sway, /* stairs/ladder to describe */ - char *outbuf, /* result buffer */ - boolean stcase) /* True: "staircase" or "ladder", always singular; - * False: "stairs" or "ladder"; caller needs to deal - * with singular vs plural when forming a sentence */ -{ - d_level tolev; - const char *stairs, *updown; - - tolev = sway->tolev; - stairs = sway->isladder ? "ladder" : stcase ? "staircase" : "stairs"; - updown = sway->up ? "up" : "down"; - - if (!known_branch_stairs(sway)) { - /* ordinary stairs or branch stairs to not-yet-visited branch */ - Sprintf(outbuf, "%s %s", stairs, updown); - if (sway->u_traversed) { - boolean specialdepth = (tolev.dnum == quest_dnum - || single_level_branch(&tolev)); /* knox */ - int to_dlev = specialdepth ? dunlev(&tolev) : depth(&tolev); - - Sprintf(eos(outbuf), " to level %d", to_dlev); - } - } else if (u.uz.dnum == 0 && u.uz.dlevel == 1 && sway->up) { - /* stairs up from level one are a special case; they are marked - as having been traversed because the hero obviously started - the game by coming down them, but the remote side varies - depending on whether the Amulet is being carried */ - Sprintf(outbuf, "%s%s %s %s", - !u.uhave.amulet ? "" : "branch ", - stairs, updown, - !u.uhave.amulet ? "out of the dungeon" - /* minimize our expectations about what comes next */ - : (on_level(&tolev, &earth_level) - || on_level(&tolev, &air_level) - || on_level(&tolev, &fire_level) - || on_level(&tolev, &water_level)) - ? "to the Elemental Planes" - : "to the end game"); - } else { - /* known branch stairs; tacking on destination level is too verbose */ - Sprintf(outbuf, "branch %s %s to %s", - stairs, updown, gd.dungeons[tolev.dnum].dname); - /* dungeons[].dname is capitalized; undo that for "The " */ - (void) strsubst(outbuf, "The ", "the "); - } - return outbuf; -} - /* Convert a branch type to a string usable by print_dungeon(). */ static const char * br_string(int type) diff --git a/src/end.c b/src/end.c index 78b34f43a..bc6aa5588 100644 --- a/src/end.c +++ b/src/end.c @@ -37,9 +37,6 @@ static boolean should_query_disclose_option(int, char *); static void dump_plines(void); #endif static void dump_everything(int, time_t); -#ifdef CRASHREPORT -static const char *get_saved_pline(int); -#endif static void fixup_death(int); static int wordcount(char *); static void bel_copy1(char **, char *); @@ -70,673 +67,6 @@ ATTRNORETURN extern void nethack_exit(int) NORETURN; #endif /* !SYSV */ #endif /* !AMIGA */ -/* NB: CRASHREPORT implies PANICTRACE */ -#if defined(PANICTRACE) -#include -#ifdef PANICTRACE_LIBC -#include -#endif - -/* What do we try to in what order? Tradeoffs: - * libc: +no external programs required - * -requires newish libc/glibc - * -requires -rdynamic - * gdb: +gives more detailed information - * +works on more OS versions - * -requires -g, which may preclude -O on some compilers - * - * And the UI: if sysopt.crashreporturl, and defined(CRASHREPORT) - * we gather the stacktrace (etc) and launch a browser to submit a bug report - * otherwise we just use stdout. Requires libc for now. - */ -#ifdef SYSCF -# define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb -# ifdef PANICTRACE_LIBC -# define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc -# else -# define SYSOPT_PANICTRACE_LIBC 0 -# endif -#else -# define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2) -# ifdef PANICTRACE_LIBC -# define SYSOPT_PANICTRACE_LIBC 1 -# else -# define SYSOPT_PANICTRACE_LIBC 0 -# endif -#endif - -static void NH_abort(char *); -#ifndef NO_SIGNAL -static void panictrace_handler(int); -#endif -static boolean NH_panictrace_libc(void); -static boolean NH_panictrace_gdb(void); - -#ifndef NO_SIGNAL -/* called as signal() handler, so sent at least one arg */ -/*ARGUSED*/ -void -panictrace_handler(int sig_unused UNUSED) -{ -#define SIG_MSG "\nSignal received.\n" - int f2; - -#ifdef CURSES_GRAPHICS - if (iflags.window_inited && WINDOWPORT(curses)) { - extern void curses_uncurse_terminal(void); /* wincurs.h */ - - /* it is risky calling this during a program-terminating signal, - but without it the subsequent backtrace is useless because - that ends up being scrawled all over the screen; call is - here rather than in NH_abort() because panic() calls both - exit_nhwindows(), which makes this same call under curses, - then NH_abort() and we don't want to call this twice */ - curses_uncurse_terminal(); - } -#endif - - f2 = (int) write(2, SIG_MSG, sizeof SIG_MSG - 1); - nhUse(f2); /* what could we do if write to fd#2 (stderr) fails */ - NH_abort(NULL); /* ... and we're already in the process of quitting? */ -} - -void -panictrace_setsignals(boolean set) -{ -#define SETSIGNAL(sig) \ - (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL); -#ifdef SIGILL - SETSIGNAL(SIGILL); -#endif -#ifdef SIGTRAP - SETSIGNAL(SIGTRAP); -#endif -#ifdef SIGIOT - SETSIGNAL(SIGIOT); -#endif -#ifdef SIGBUS - SETSIGNAL(SIGBUS); -#endif -#ifdef SIGFPE - SETSIGNAL(SIGFPE); -#endif -#ifdef SIGSEGV - SETSIGNAL(SIGSEGV); -#endif -#ifdef SIGSTKFLT - SETSIGNAL(SIGSTKFLT); -#endif -#ifdef SIGSYS - SETSIGNAL(SIGSYS); -#endif -#ifdef SIGEMT - SETSIGNAL(SIGEMT); -#endif -#undef SETSIGNAL -} -#endif /* NO_SIGNAL */ - -#ifdef CRASHREPORT -#define USED_FOR_CRASHREPORT -#else -#define USED_FOR_CRASHREPORT UNUSED -#endif - -static void -NH_abort(char *why USED_FOR_CRASHREPORT) -{ - int gdb_prio = SYSOPT_PANICTRACE_GDB; - int libc_prio = SYSOPT_PANICTRACE_LIBC; - static volatile boolean aborting = FALSE; - - /* don't execute this code recursively if a second abort is requested - while this routine or the code it calls is executing */ - if (aborting) - return; - aborting = TRUE; - -#ifdef CRASHREPORT - if(!submit_web_report(1, "Panic", why)) -#endif - { -#ifndef VMS - if (gdb_prio == libc_prio && gdb_prio > 0) - gdb_prio++; - - if (gdb_prio > libc_prio) { - (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc())); - } else { - (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb())); - } - -#else /* VMS */ - /* overload otherwise unused priority for debug mode: 1 = show - traceback and exit; 2 = show traceback and stay in debugger */ - /* if (wizard && gdb_prio == 1) gdb_prio = 2; */ - vms_traceback(gdb_prio); - nhUse(libc_prio); - -#endif /* ?VMS */ - } - -#ifndef NO_SIGNAL - panictrace_setsignals(FALSE); -#endif - NH_abort_; -} -#undef USED_FOR_CRASHREPORT - -#ifdef CRASHREPORT -# include -# ifdef WIN32 -# define HASH_PRAGMA_START -# define HASH_PRAGMA_END -# else -# define HASH_PRAGMA_START \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop"); -# endif -# ifdef MACOS -# include -# define HASH_CONTEXTPTR(CTXP) \ - unsigned char tmp[CC_MD4_DIGEST_LENGTH]; \ - CC_MD4_CTX CTXP ## _; \ - CC_MD4_CTX *CTXP = &CTXP ## _ -# define HASH_INIT(ctxp) !CC_MD4_Init(ctxp) -# define HASH_UPDATE(ctx, ptr, len) !CC_MD4_Update(ctx, ptr, len) -# define HASH_FINISH(ctxp) !CC_MD4_Final(tmp, ctxp) -# define HASH_RESULT_SIZE(ctxp) CC_MD4_DIGEST_LENGTH -# define HASH_RESULT(ctx, inp) *inp = (unsigned char *) ctx -# define HASH_CLEANUP(ctxp) -# define HASH_OFLAGS O_RDONLY -# define HASH_BINFILE_DECL char *binfile = argv[0]; -# ifdef BETA -# define HASH_BINFILE \ - if (!binfile || !*binfile) { \ - /* If this triggers, investigate CFBundleGetMainBundle */ \ - /* or CFBundleCopyExecutableURL. */ \ - raw_print( \ - "BETA warning: crashreport_init called without useful info"); \ - goto skip; \ - } -# else -# define HASH_BINFILE() \ - if (!binfile || !*binfile) { \ - goto skip; \ - } -# endif -# endif -# ifdef __linux__ -# include "nhmd4.h" -# define HASH_CONTEXTPTR(CTXP) \ - unsigned char tmp[NHMD4_DIGEST_LENGTH]; \ - NHMD4_CTX CTXP ## _; \ - NHMD4_CTX *CTXP = &CTXP ## _ -# define HASH_INIT(ctxp) (nhmd4_init(ctxp), 0) -# define HASH_UPDATE(ctx, ptr, len) (nhmd4_update(ctx, ptr, len), 0) -# define HASH_FINISH(ctxp) (nhmd4_final(ctxp, tmp), 0) -# define HASH_RESULT_SIZE(ctxp) NHMD4_RESULTLEN -# define HASH_RESULT(ctx, inp) *inp = tmp -# define HASH_CLEANUP(ctxp) -# define HASH_OFLAGS O_RDONLY -# define HASH_BINFILE_DECL char binfile[PATH_MAX+1]; -# define HASH_BINFILE() \ - int len = readlink("/proc/self/exe", binfile, sizeof binfile - 1); \ - if (len > 0) { \ - binfile[len] = '\0'; \ - } else { \ - goto skip; \ - } -# endif // __linux__ -# ifdef WIN32 -/* WIN32 takes too much code and is dependent on OS includes we can't - * pull in here, so we call out to code in sys/windows/windsys.c */ -# define HASH_CONTEXTPTR(CTXP) -# define HASH_INIT(ctxp) win32_cr_helper('i', ctxp, NULL, 0) -# define HASH_UPDATE(ctxp, ptr, len) win32_cr_helper('u', ctxp, ptr, len) -# define HASH_FINISH(ctxp) win32_cr_helper('f', ctxp, NULL, 0) -# define HASH_CLEANUP(ctxp) win32_cr_helper('c', ctxp, NULL, 0) -# define HASH_RESULT_SIZE(ctxp) win32_cr_helper('s', ctxp, NULL, 0) -# define HASH_RESULT(ctxp, inp) win32_cr_helper('r', ctxp, inp, 0) -# define HASH_OFLAGS _O_RDONLY | _O_BINARY -# define HASH_BINFILE_DECL char *binfile; -# define HASH_BINFILE() \ - if (win32_cr_helper('b', NULL, &binfile, 0)) { \ - goto skip; \ - } -# endif // WIN32 - -/* Binary ID - Use only as a hint to contact.html for recognizing our own - binaries. This is easily spoofed! */ -static char bid[40]; - -/* ARGSUSED */ -void -crashreport_init(int argc UNUSED, char *argv[] UNUSED) -{ - static int once = 0; - if (once++) /* NetHackW.exe calls us twice */ - return; - HASH_BINFILE_DECL; - HASH_PRAGMA_START - HASH_CONTEXTPTR(ctxp); - if (HASH_INIT(ctxp)) - goto skip; - HASH_BINFILE(); /* Does "goto skip" on error. */ - - int fd = open(binfile, HASH_OFLAGS, 0); - if (fd == -1) { -# ifdef BETA - raw_printf("open e=%s", strerror(errno)); -# endif - goto skip; - } - - int segsize; - unsigned char segment[4096]; - - while (0 < (segsize = read(fd, segment, sizeof segment))) { - if (HASH_UPDATE(ctxp, segment, segsize)) - goto skip; - } - if (segsize < 0) { - close(fd); - goto skip; - } - if (HASH_FINISH(ctxp)) - goto skip; - close(fd); - - static const char hex[] = "0123456789abcdef"; - char *p = bid; - unsigned char *in; - HASH_RESULT(ctxp, &in); - uint8 cnt = (uint8) HASH_RESULT_SIZE(ctxp); - /* Just in case, make sure not to overflow the bid buffer. - Divide size by 2 because each octet in the hash uses two slots - in bid[] when formatted as a pair of hexadecimal digits. */ - if (cnt >= (uint8) sizeof bid / 2) - cnt = (uint8) sizeof bid / 2 - 1; - while (cnt) { - /* sprintf(p, "%02x", *in++), p += 2; */ - *p++ = hex[(*in >> 4) & 0x0f]; - *p++ = hex[*in++ & 0x0f]; - --cnt; - } - *p = '\0'; - return; - - skip: - Strcpy(bid, "unknown"); - HASH_CLEANUP(ctxp); - HASH_PRAGMA_END -} -#undef HASH_CONTEXTPTR -#undef HASH_INIT -#undef HASH_UPDATE -#undef HASH_FINISH -#undef HASH_CLEANUP -#undef HASH_RESULT -#undef HASH_RESULT_SIZE -#undef HASH_PRAGMA_START -#undef HASH_PRAGMA_END -#undef HASH_BINFILE_DECL -#undef HASH_BINFILE - -void -crashreport_bidshow(void) -{ -#if defined(WIN32) && !defined(WIN32CON) - if (0 == win32_cr_helper('D', ctxp, bid, 0)) -#endif - { - raw_print(bid); -#ifdef WIN32notyet - wait_synch(); -#endif - } -} - -/* Build a URL with a query string and try to launch a new browser window - * to report from panic() or impossible(). Requires libc support for - * the stacktrace. Uses memory on the stack to avoid memory allocation - * (on most platforms) (but libc can still do anything it wants). */ - -// No theoretial limit for URL length but reality is messy. -// This should work on all modern platforms. -#ifndef MAX_URL -# define MAX_URL 8192 -#endif -#ifndef SWR_FRAMES -# define SWR_FRAMES 20 -#endif - -// mark holds the initial eos; if we can't get the value in -// then we can remove the whole item if desired. For other -// semantics, caller can handle mark. -#define SWR_ADD(str) \ - utmp = strlen(str); \ - mark = uend; \ - if (utmp >= urem) \ - goto full; \ - strncpy(uend, str, utmp); \ - uend += utmp; urem -= utmp; \ - *uend = '\0'; - -// NB: on overflow this rolls us back to mark, so if we don't -// want to roll back to the last SWR_ADD, update mark before -// calling this macro. -#define SWR_ADD_URIcoded(str) \ - if (swr_add_uricoded(str, &uend, &urem, mark)) \ - goto full; - -/* On overflow, truncate to markp (but only if markp != NULL). */ -boolean -swr_add_uricoded( - const char *in, - char **out, - int *remaining, - char *markp) -{ - while (*in) { - if (isalnum(*in) || strchr("_-.~", *in)) { - **out = *in; - (*out)++; - (*remaining)--; - } else if (*in == ' ') { - **out = '+'; - (*out)++; - (*remaining)--; - } else { - if (*remaining <= 3) { - if (markp) - *out = markp, *remaining = 0; - **out = '\0'; - return TRUE; - } - int x = snprintf(*out, *remaining, "%%%02X", *in); - *out += x; - *remaining -= x; - } - in++; - if (!*remaining) { - if (markp) - *out = markp, *remaining = 0; - **out = '\0'; - return TRUE; - } - **out = '\0'; - } - return FALSE; /* normal return */ -} - -static char url[MAX_URL]; // XXX too bad this isn't allocated as needed -static int urem = MAX_URL; // adjusted for gc.crash_urlmax below -static char *uend = url; -static int utmp; // used inside macros -static char *mark; // holds previous terminator (generally) - -boolean -submit_web_report(int cos, const char *msg, const char *why) -{ - urem = (gc.crash_urlmax < 0 || gc.crash_urlmax > MAX_URL) - ? MAX_URL : min(MAX_URL,gc.crash_urlmax); - char temp[200]; - char temp2[200]; - int countpp = 0; /* pre and post traceback lines */ -// URL loaded for creating reports to the NetHack DevTeam -// CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html - if (!sysopt.crashreporturl) - return FALSE; - SWR_ADD(sysopt.crashreporturl); - /* cos - operation, v - version */ - Snprintf(temp, sizeof temp, "?cos=%d&v=1", cos); - SWR_ADD(temp); - - /* msg==NULL for #bugreport */ - if (msg) { - SWR_ADD("&subject="); - Snprintf(temp, sizeof temp, "%s report for NetHack %s", - msg, version_string(temp2, sizeof temp2 )); - SWR_ADD_URIcoded(temp); - } - - SWR_ADD("&gitver="); - SWR_ADD_URIcoded(getversionstring(temp2, sizeof temp2)); - - if (gc.crash_name) { - SWR_ADD("&name="); - SWR_ADD_URIcoded(gc.crash_name); - } - - if(gc.crash_email) { - SWR_ADD("&email="); - SWR_ADD_URIcoded(gc.crash_email); - } - // hardware: leave for user - // software: leave for user - // comments: leave for user - - SWR_ADD("&details="); - if (why) { - SWR_ADD_URIcoded(why); - SWR_ADD_URIcoded("\n"); - mark = uend; - countpp++; - } - - SWR_ADD_URIcoded("bid: "); - SWR_ADD_URIcoded(bid); - SWR_ADD_URIcoded("\n"); - mark = uend; - countpp++; - - int count = 0; - if (cos == 1) { -#ifdef WIN32 - count = win32_cr_gettrace(SWR_FRAMES, uend, MAX_URL - (uend - url)); - uend = eos(url); -#else - void *bt[SWR_FRAMES]; - int x; - char **info; - - count = backtrace(bt, SIZE(bt)); - info = backtrace_symbols(bt, count); - for (x = 0; x < count; x++) { - copynchars(temp, info[x], (int) sizeof temp - 1 - 1); /* \n\0 */ - /* try to remove up to 16 blank spaces by removing 8 twice */ - (void) strsubst(temp, " ", ""); - (void) strsubst(temp, " ", ""); - (void) strncat(temp, "\n", sizeof temp - 1); -# if 0 // __linux__ -// not needed for MacOS -// XXX is it actually needed for linux? TBD - Snprintf(temp2, sizeof temp2, "[%02lu]\n", (unsigned long) x); - uend--; // remove the \n we added above - SWR_ADD_URIcoded(temp2); -# endif // linux - SWR_ADD_URIcoded(temp); - mark = uend; - } -#endif // !WIN32 - } - -#ifdef DUMPLOG_CORE - // config.h turns this on, but make it easy to turn off if needed - if (cos == 1) { - int k; - SWR_ADD_URIcoded("Latest messages:\n"); - mark=uend; - countpp++; - for (k = 0; k < 5; k++) { - const char *line = get_saved_pline(k); - if (!line) - break; - SWR_ADD_URIcoded(line); - SWR_ADD_URIcoded("\n"); - countpp++; - mark = uend; - } - } -#endif - - // detailrows: Guess since we can't know the - // width of the window. - SWR_ADD("&detailrows="); - Snprintf(temp, sizeof temp, "%d", min(count + countpp, 30)); - SWR_ADD_URIcoded(temp); - - full: - ; -//printf("URL=%ld '%s'\n",strlen(url),url); -#ifdef WIN32 - int *rv = win32_cr_shellexecute(url); -// XXX TESTING -printf("ShellExecute returned: %p\n",rv); // >32 is ok -#else - const char *xargv[] = { - CRASHREPORT, - url, - NULL - }; - int pid = fork(); - extern char **environ; - if (pid == 0) { - char err[100]; -#ifdef CRASHREPORT_EXEC_NOSTDERR - /* Keep the output clean - firefox spews useless errors on - * my system. */ - (void) close(2); - (void) open("/dev/null", O_WRONLY); -#endif - - (void) execve(CRASHREPORT, (char * const *) xargv, environ); - Sprintf(err, "Can't start " CRASHREPORT ": %s", strerror(errno)); - raw_print(err); - } else { - int status; - errno = 0; - (void) waitpid(pid, &status, 0); - if (status) { /* XXX check could be more precise */ -#ifdef BETA - /* Not useful at the moment. XXX */ - char err[100]; - - Sprintf(err, "pid=%d e=%d status=%0x", wpid, errno, status); - raw_print(err); -#endif - return FALSE; - } - } - /* free(info); -- Don't risk it. */ -#endif - return TRUE; -} - -int -dobugreport(void) -{ - if (!submit_web_report(2, NULL, "#bugreport command")) { - pline("Unable to send bug report. Please visit %s instead.", - (sysopt.crashreporturl && *sysopt.crashreporturl) - ? sysopt.crashreporturl - : "https://www.nethack.org" - ); - } - return ECMD_OK; -} - -#endif /* CRASHREPORT */ -#undef SWR_ADD -#undef SWR_ADD_URIcoded -#undef SWR_FRAMES -#undef SWR_HDR -#undef SWR_LINES - -/*ARGSUSED*/ -static boolean -NH_panictrace_libc(void) -{ -#if 0 /* XXX how did this get left here? */ -#ifdef CRASHREPORT - if (submit_web_report("Panic", why)) - return TRUE; -#endif -#endif - -#ifdef PANICTRACE_LIBC - void *bt[20]; - int count, x; - char **info, buf[BUFSZ]; - - raw_print(" Generating more information you may report:\n"); - count = backtrace(bt, SIZE(bt)); - info = backtrace_symbols(bt, count); - for (x = 0; x < count; x++) { - copynchars(buf, info[x], (int) sizeof buf - 1); - /* try to remove up to 16 blank spaces by removing 8 twice */ - (void) strsubst(buf, " ", ""); - (void) strsubst(buf, " ", ""); - raw_printf("[%02lu] %s", (unsigned long) x, buf); - } - /* free(info); -- Don't risk it. */ - return TRUE; -#else - return FALSE; -#endif /* !PANICTRACE_LIBC */ -} - -/* - * fooPATH file system path for foo - * fooVAR (possibly const) variable containing fooPATH - */ -#ifdef PANICTRACE_GDB -# ifdef SYSCF -# define GDBVAR sysopt.gdbpath -# define GREPVAR sysopt.greppath -# else /* SYSCF */ -# define GDBVAR GDBPATH -# define GREPVAR GREPPATH -# endif /* SYSCF */ -#endif /* PANICTRACE_GDB */ - -static boolean -NH_panictrace_gdb(void) -{ -#ifdef PANICTRACE_GDB - /* A (more) generic method to get a stack trace - invoke - * gdb on ourself. */ - const char *gdbpath = GDBVAR; - const char *greppath = GREPVAR; - char buf[BUFSZ]; - FILE *gdb; - - if (gdbpath == NULL || gdbpath[0] == 0) - return FALSE; - if (greppath == NULL || greppath[0] == 0) - return FALSE; - - sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", - gdbpath, ARGV0, getpid(), greppath); - gdb = popen(buf, "w"); - if (gdb) { - raw_print(" Generating more information you may report:\n"); - fprintf(gdb, "bt\nquit\ny"); - fflush(gdb); - sleep(4); /* ugly */ - pclose(gdb); - return TRUE; - } else { - return FALSE; - } -#else - return FALSE; -#endif /* !PANICTRACE_GDB */ -} -#endif /* PANICTRACE */ - /* * The order of these needs to match the macros in hack.h. */ @@ -1222,31 +552,6 @@ dump_plines(void) } #endif /* DUMPLOG */ -#ifdef CRASHREPORT -/* lineno==0 gives the most recent message (e.g. - "Do you want to call panic..." if called from #panic) */ -static const char * -get_saved_pline(int lineno) -{ - int p; - int limit = DUMPLOG_MSG_COUNT; - if (lineno >= DUMPLOG_MSG_COUNT) - return NULL; - p = (gs.saved_pline_index - 1) % DUMPLOG_MSG_COUNT; - - while (limit--) { - if (gs.saved_plines[p]) { /* valid line */ - if (lineno--) { - p = (p - 1 + DUMPLOG_MSG_COUNT) % DUMPLOG_MSG_COUNT; - } else { - return gs.saved_plines[p]; - } - } - } - return NULL; -} -#endif /* CRASHREPORT */ - /*ARGSUSED*/ static void dump_everything( @@ -2556,4 +1861,86 @@ build_english_list(char *in) return out; } +/* What do we try to in what order? Tradeoffs: + * libc: +no external programs required + * -requires newish libc/glibc + * -requires -rdynamic + * gdb: +gives more detailed information + * +works on more OS versions + * -requires -g, which may preclude -O on some compilers + * + * And the UI: if sysopt.crashreporturl, and defined(CRASHREPORT) + * we gather the stacktrace (etc) and launch a browser to submit a bug report + * otherwise we just use stdout. Requires libc for now. + */ +# ifdef SYSCF +# define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb +# ifdef PANICTRACE_LIBC +# define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc +# else +# define SYSOPT_PANICTRACE_LIBC 0 +# endif +# else +# define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2) +# ifdef PANICTRACE_LIBC +# define SYSOPT_PANICTRACE_LIBC 1 +# else +# define SYSOPT_PANICTRACE_LIBC 0 +# endif +# endif + +# ifdef CRASHREPORT +#define USED_FOR_CRASHREPORT +# else +#define USED_FOR_CRASHREPORT UNUSED +# endif + +void +NH_abort(char *why USED_FOR_CRASHREPORT) +{ +#ifdef PANICTRACE + int gdb_prio = SYSOPT_PANICTRACE_GDB; + int libc_prio = SYSOPT_PANICTRACE_LIBC; +#endif + static volatile boolean aborting = FALSE; + + /* don't execute this code recursively if a second abort is requested + while this routine or the code it calls is executing */ + if (aborting) + return; + aborting = TRUE; + +#ifdef PANICTRACE +#ifdef CRASHREPORT + if(!submit_web_report(1, "Panic", why)) +#endif + { +#ifndef VMS + if (gdb_prio == libc_prio && gdb_prio > 0) + gdb_prio++; + + if (gdb_prio > libc_prio) { + (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc())); + } else { + (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb())); + } + +#else /* VMS */ + /* overload otherwise unused priority for debug mode: 1 = show + traceback and exit; 2 = show traceback and stay in debugger */ + /* if (wizard && gdb_prio == 1) gdb_prio = 2; */ + vms_traceback(gdb_prio); + nhUse(libc_prio); + +#endif /* ?VMS */ + } + +#ifndef NO_SIGNAL + panictrace_setsignals(FALSE); +#endif +#endif /* PANICTRACE */ + NH_abort_; +} +#undef USED_FOR_CRASHREPORT + /*end.c*/ diff --git a/src/getpos.c b/src/getpos.c new file mode 100644 index 000000000..527e7afea --- /dev/null +++ b/src/getpos.c @@ -0,0 +1,1140 @@ +/* NetHack 3.7 getpos.c $NHDT-Date: 1708126536 2024/02/16 23:35:36 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.307 $ */ +/*-Copyright (c) Pasi Kallinen, 2023. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +extern const char what_is_an_unknown_object[]; /* from pager.c */ + +static void getpos_toggle_hilite_state(void); +static void getpos_getvalids_selection(struct selectionvar *, + boolean (*)(coordxy, coordxy)); +static void getpos_help_keyxhelp(winid, const char *, const char *, int); +static void getpos_help(boolean, const char *); +static int QSORTCALLBACK cmp_coord_distu(const void *, const void *); +static int gloc_filter_classify_glyph(int); +static int gloc_filter_floodfill_matcharea(coordxy, coordxy); +static void gloc_filter_floodfill(coordxy, coordxy); +static void gloc_filter_init(void); +static void gloc_filter_done(void); +static void gather_locs(coord **, int *, int); +static void truncate_to_map(coordxy *, coordxy *, schar, schar); +static void getpos_refresh(void); +/* Callback function for getpos() to highlight desired map locations. + * Parameter TRUE: initialize and highlight, FALSE: done (remove highlights). + */ +static void (*getpos_hilitefunc)(boolean) = (void (*)(boolean)) 0; +static boolean (*getpos_getvalid)(coordxy, coordxy) + = (boolean (*)(coordxy, coordxy)) 0; +enum getposHiliteState { + HiliteNormalMap = 0, + HiliteGoodposSymbol = 1, + HiliteBackground = 2, +}; + +static enum getposHiliteState + getpos_hilite_state = HiliteNormalMap, + defaultHiliteState = HiliteNormalMap; + +void +getpos_sethilite( + void (*gp_hilitef)(boolean), + boolean (*gp_getvalidf)(coordxy, coordxy)) +{ + boolean (*old_getvalid)(coordxy, coordxy) = getpos_getvalid; + uint32 old_map_frame_color = gw.wsettings.map_frame_color; + struct selectionvar *sel = selection_new(); + + defaultHiliteState = iflags.bgcolors ? HiliteBackground : HiliteNormalMap; + if (gp_getvalidf != old_getvalid) + getpos_hilite_state = defaultHiliteState; + + getpos_getvalids_selection(sel, getpos_getvalid); + getpos_hilitefunc = gp_hilitef; + getpos_getvalid = gp_getvalidf; + getpos_getvalids_selection(sel, getpos_getvalid); + gw.wsettings.map_frame_color = (getpos_hilite_state == HiliteBackground) + ? HI_ZAP : NO_COLOR; + + if (getpos_getvalid != old_getvalid + || gw.wsettings.map_frame_color != old_map_frame_color) + selection_force_newsyms(sel); + selection_free(sel, TRUE); +} + +/* cycle 'getpos_hilite_state' to its next state; + when 'bgcolors' is Off, it will alternate between not showing valid + positions and showing them via temporary S_goodpos symbol; + when 'bgcolors' is On, there are three states and showing them via + setting background color becomes the default */ +static void +getpos_toggle_hilite_state(void) +{ + /* getpos_hilitefunc isn't Null */ + if (getpos_hilite_state == HiliteGoodposSymbol) { + /* currently on, finish */ + (*getpos_hilitefunc)(FALSE); /* tmp_at(DISP_END) */ + } + + getpos_hilite_state = (getpos_hilite_state + 1) + % (iflags.bgcolors ? 3 : 2); + /* resetting the callback functions to their current values will draw + valid-spots with background color if that is the new state and turn + off that color if it was the previous state */ + getpos_sethilite(getpos_hilitefunc, getpos_getvalid); + + if (getpos_hilite_state == HiliteGoodposSymbol) { + /* now on, begin */ + (*getpos_hilitefunc)(TRUE); + } +} + +boolean +mapxy_valid(coordxy x, coordxy y) +{ + if (getpos_getvalid) + return (*getpos_getvalid)(x, y); + return FALSE; +} + +static void +getpos_getvalids_selection( + struct selectionvar *sel, + boolean (*validf)(coordxy, coordxy)) +{ + coordxy x, y; + + if (!sel || !validf) + return; + + for (x = 1; x < sel->wid; x++) + for (y = 0; y < sel->hei; y++) + if ((*validf)(x, y)) + selection_setpoint(x, y, sel, 1); +} + +static const char *const gloc_descr[NUM_GLOCS][4] = { + { "any monsters", "monster", "next/previous monster", "monsters" }, + { "any items", "item", "next/previous object", "objects" }, + { "any doors", "door", "next/previous door or doorway", + "doors or doorways" }, + { "any unexplored areas", "unexplored area", "unexplored location", + "locations next to unexplored locations" }, + { "anything interesting", "interesting thing", "anything interesting", + "anything interesting" }, + { "any valid locations", "valid location", "valid location", + "valid locations" } +}; + +static const char *const gloc_filtertxt[NUM_GFILTER] = { + "", + " in view", + " in this area" +}; + +static void +getpos_help_keyxhelp( + winid tmpwin, + const char *k1, const char *k2, + int gloc) +{ + char sbuf[BUFSZ], fbuf[QBUFSZ]; + const char *move_cursor_to = "move the cursor to ", + *filtertxt = gloc_filtertxt[iflags.getloc_filter]; + + if (gloc == GLOC_EXPLORE) { + /* default of "move to unexplored location" is inaccurate + because the position will be one spot short of that */ + move_cursor_to = "move the cursor next to an "; + if (iflags.getloc_usemenu) + /* default is too wide for basic 80-column tty so shorten it + to avoid wrapping */ + filtertxt = strsubst(strcpy(fbuf, filtertxt), + "this area", "area"); + } + Sprintf(sbuf, "Use '%s'/'%s' to %s%s%s.", + k1, k2, + iflags.getloc_usemenu ? "get a menu of " : move_cursor_to, + gloc_descr[gloc][2 + iflags.getloc_usemenu], filtertxt); + putstr(tmpwin, 0, sbuf); +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* the response for '?' help request in getpos() */ +static void +getpos_help(boolean force, const char *goal) +{ + static const char *const fastmovemode[2] = { "8 units at a time", + "skipping same glyphs" }; + char sbuf[BUFSZ]; + boolean doing_what_is; + winid tmpwin = create_nhwindow(NHW_MENU); + + Sprintf(sbuf, + "Use '%s', '%s', '%s', '%s' to move the cursor to %s.", /* hjkl */ + visctrl(cmd_from_func(do_move_west)), + visctrl(cmd_from_func(do_move_south)), + visctrl(cmd_from_func(do_move_north)), + visctrl(cmd_from_func(do_move_east)), goal); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + "Use '%s', '%s', '%s', '%s' to fast-move the cursor, %s.", + visctrl(cmd_from_func(do_run_west)), + visctrl(cmd_from_func(do_run_south)), + visctrl(cmd_from_func(do_run_north)), + visctrl(cmd_from_func(do_run_east)), + fastmovemode[iflags.getloc_moveskip]); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, "(or prefix normal move with '%s' or '%s' to fast-move)", + visctrl(cmd_from_func(do_run)), + visctrl(cmd_from_func(do_rush))); + putstr(tmpwin, 0, sbuf); + putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); + Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SELF])); + putstr(tmpwin, 0, sbuf); + if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MON_PREV]), + GLOC_MONS); + } + if (goal && !strcmp(goal, "a monster")) + goto skip_non_mons; + if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) { + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_OBJ_PREV]), + GLOC_OBJS); + } + if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) { + /* these are primarily useful when choosing a travel + destination for the '_' command */ + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_DOOR_PREV]), + GLOC_DOOR); + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_UNEX_PREV]), + GLOC_EXPLORE); + getpos_help_keyxhelp(tmpwin, + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_INTERESTING_PREV]), + GLOC_INTERESTING); + } + Sprintf(sbuf, "Use '%s' to change fast-move mode to %s.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]), + fastmovemode[!iflags.getloc_moveskip]); + putstr(tmpwin, 0, sbuf); + if (!iflags.terrainmode || (iflags.terrainmode & TER_DETECT) == 0) { + Sprintf(sbuf, "Use '%s' to toggle menu listing for possible targets.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_MENU])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + "Use '%s' to change the mode of limiting possible targets.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW])); + putstr(tmpwin, 0, sbuf); + } + if (!iflags.terrainmode) { + char kbuf[BUFSZ]; + + if (getpos_getvalid) { + Sprintf(sbuf, "Use '%s' or '%s' to move to valid locations.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_NEXT]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_VALID_PREV])); + putstr(tmpwin, 0, sbuf); + } + if (getpos_hilitefunc) { + Sprintf(sbuf, "Use '%s' to toggle marking of valid locations.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID])); + putstr(tmpwin, 0, sbuf); + } + Sprintf(sbuf, "Use '%s' to toggle automatic description.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); + putstr(tmpwin, 0, sbuf); + if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */ + Sprintf(sbuf, + (iflags.getpos_coords == GPCOORDS_NONE) + ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)" + : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC])); + } + skip_non_mons: + /* disgusting hack; the alternate selection characters work for any + getpos call, but only matter for dowhatis (and doquickwhatis, + also for dotherecmdmenu's simulated mouse) */ + doing_what_is = (goal == what_is_an_unknown_object); + if (doing_what_is) { + Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O]), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); + } else { + Sprintf(kbuf, "'%s'", visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); + } + Snprintf(sbuf, sizeof(sbuf), + "Type a %s when you are at the right place.", kbuf); + putstr(tmpwin, 0, sbuf); + if (doing_what_is) { + Sprintf(sbuf, + " '%s' describe current spot, show 'more info', move to another spot.", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_V])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot,%s move to another spot;", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK]), + flags.help && !force ? " prompt if 'more info'," : ""); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot, move to another spot;", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_Q])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot, stop looking at things;", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK_O])); + putstr(tmpwin, 0, sbuf); + } + } + if (!force) + putstr(tmpwin, 0, "Type Space or Escape when you're done."); + putstr(tmpwin, 0, ""); + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +static int QSORTCALLBACK +cmp_coord_distu(const void *a, const void *b) +{ + const coord *c1 = a; + const coord *c2 = b; + int dx, dy, dist_1, dist_2; + + dx = u.ux - c1->x; + dy = u.uy - c1->y; + dist_1 = max(abs(dx), abs(dy)); + dx = u.ux - c2->x; + dy = u.uy - c2->y; + dist_2 = max(abs(dx), abs(dy)); + + if (dist_1 == dist_2) + return (c1->y != c2->y) ? (c1->y - c2->y) : (c1->x - c2->x); + + return dist_1 - dist_2; +} + +#define IS_UNEXPLORED_LOC(x,y) \ + (isok((x), (y)) \ + && glyph_is_unexplored(levl[(x)][(y)].glyph) \ + && !levl[(x)][(y)].seenv) + +#define GLOC_SAME_AREA(x,y) \ + (isok((x), (y)) \ + && (selection_getpoint((x),(y), gg.gloc_filter_map))) + +static int +gloc_filter_classify_glyph(int glyph) +{ + int c; + + if (!glyph_is_cmap(glyph)) + return 0; + + c = glyph_to_cmap(glyph); + + if (is_cmap_room(c) || is_cmap_furniture(c)) + return 1; + else if (is_cmap_wall(c) || c == S_tree) + return 2; + else if (is_cmap_corr(c)) + return 3; + else if (is_cmap_water(c)) + return 4; + else if (is_cmap_lava(c)) + return 5; + return 0; +} + +static int +gloc_filter_floodfill_matcharea(coordxy x, coordxy y) +{ + int glyph = back_to_glyph(x, y); + + if (!levl[x][y].seenv) + return FALSE; + + if (glyph == gg.gloc_filter_floodfill_match_glyph) + return TRUE; + + if (gloc_filter_classify_glyph(glyph) + == gloc_filter_classify_glyph(gg.gloc_filter_floodfill_match_glyph)) + return TRUE; + + return FALSE; +} + +static void +gloc_filter_floodfill(coordxy x, coordxy y) +{ + gg.gloc_filter_floodfill_match_glyph = back_to_glyph(x, y); + + set_selection_floodfillchk(gloc_filter_floodfill_matcharea); + selection_floodfill(gg.gloc_filter_map, x, y, FALSE); +} + +static void +gloc_filter_init(void) +{ + if (iflags.getloc_filter == GFILTER_AREA) { + if (!gg.gloc_filter_map) { + gg.gloc_filter_map = selection_new(); + } + /* special case: if we're in a doorway, try to figure out which + direction we're moving, and use that side of the doorway */ + if (IS_DOOR(levl[u.ux][u.uy].typ)) { + if (u.dx || u.dy) { + gloc_filter_floodfill(u.ux + u.dx, u.uy + u.dy); + } else { + /* TODO: maybe add both sides of the doorway? */ + } + } else { + gloc_filter_floodfill(u.ux, u.uy); + } + } +} + +static void +gloc_filter_done(void) +{ + if (gg.gloc_filter_map) { + selection_free(gg.gloc_filter_map, TRUE); + gg.gloc_filter_map = (struct selectionvar *) 0; + + } +} + +DISABLE_WARNING_UNREACHABLE_CODE + +boolean +gather_locs_interesting(coordxy x, coordxy y, int gloc) +{ + int glyph, sym; + + if (iflags.getloc_filter == GFILTER_VIEW && !cansee(x, y)) + return FALSE; + if (iflags.getloc_filter == GFILTER_AREA && !GLOC_SAME_AREA(x, y) + && !GLOC_SAME_AREA(x - 1, y) && !GLOC_SAME_AREA(x, y - 1) + && !GLOC_SAME_AREA(x + 1, y) && !GLOC_SAME_AREA(x, y + 1)) + return FALSE; + + glyph = glyph_at(x, y); + sym = glyph_is_cmap(glyph) ? glyph_to_cmap(glyph) : -1; + switch (gloc) { + default: + case GLOC_MONS: + /* unlike '/M', this skips monsters revealed by + warning glyphs and remembered unseen ones */ + return (glyph_is_monster(glyph) + && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL,MALE) + && glyph != monnum_to_glyph(PM_LONG_WORM_TAIL, FEMALE)); + case GLOC_OBJS: + return (glyph_is_object(glyph) + && glyph != objnum_to_glyph(BOULDER) + && glyph != objnum_to_glyph(ROCK)); + case GLOC_DOOR: + return (glyph_is_cmap(glyph) + && (is_cmap_door(sym) + || is_cmap_drawbridge(sym) + || sym == S_ndoor)); + case GLOC_EXPLORE: + return (glyph_is_cmap(glyph) + && !glyph_is_nothing(glyph_to_cmap(glyph)) + && (is_cmap_door(sym) + || is_cmap_drawbridge(sym) + || sym == S_ndoor + || is_cmap_room(sym) + || is_cmap_corr(sym)) + && (IS_UNEXPLORED_LOC(x + 1, y) + || IS_UNEXPLORED_LOC(x - 1, y) + || IS_UNEXPLORED_LOC(x, y + 1) + || IS_UNEXPLORED_LOC(x, y - 1))); + case GLOC_VALID: + if (getpos_getvalid) + return (*getpos_getvalid)(x, y); + /*FALLTHRU*/ + case GLOC_INTERESTING: + return (gather_locs_interesting(x, y, GLOC_DOOR) + || !((glyph_is_cmap(glyph) + && (is_cmap_wall(sym) + || sym == S_tree + || sym == S_bars + || sym == S_ice + || sym == S_air + || sym == S_cloud + || is_cmap_lava(sym) + || is_cmap_water(sym) + || sym == S_ndoor + || is_cmap_room(sym) + || is_cmap_corr(sym))) + || glyph_is_nothing(glyph) + || glyph_is_unexplored(glyph))); + } + /*NOTREACHED*/ + return FALSE; +} + +RESTORE_WARNINGS + +/* gather locations for monsters or objects shown on the map */ +static void +gather_locs(coord **arr_p, int *cnt_p, int gloc) +{ + int pass, idx; + coordxy x, y; + + /* + * We always include the hero's location even if there is no monster + * (invisible hero without see invisible) or object (usual case) + * displayed there. That way, the count will always be at least 1, + * and player has a visual indicator (cursor returns to hero's spot) + * highlighting when successive 'm's or 'o's have cycled all the way + * through all monsters or objects. + * + * Hero's spot will always sort to array[0] because it will always + * be the shortest distance (namely, 0 units) away from . + */ + + gloc_filter_init(); + + *cnt_p = idx = 0; + for (pass = 0; pass < 2; pass++) { + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) { + if (u_at(x, y) || gather_locs_interesting(x, y, gloc)) { + if (!pass) { + ++*cnt_p; + } else { + (*arr_p)[idx].x = x; + (*arr_p)[idx].y = y; + ++idx; + } + } + } + + if (!pass) /* end of first pass */ + *arr_p = (coord *) alloc(*cnt_p * sizeof (coord)); + else /* end of second pass */ + qsort(*arr_p, *cnt_p, sizeof (coord), cmp_coord_distu); + } /* pass */ + + gloc_filter_done(); +} + +char * +dxdy_to_dist_descr(coordxy dx, coordxy dy, boolean fulldir) +{ + static char buf[30]; + int dst; + + if (!dx && !dy) { + Sprintf(buf, "here"); + } else if ((dst = xytod(dx, dy)) != -1) { + /* explicit direction; 'one step' is implicit */ + Sprintf(buf, "%s", directionname(dst)); + } else { + static const char *const dirnames[4][2] = { + { "n", "north" }, + { "s", "south" }, + { "w", "west" }, + { "e", "east" } }; + buf[0] = '\0'; + /* 9999: protect buf[] against overflow caused by invalid values */ + if (dy) { + if (abs(dy) > 9999) + dy = sgn(dy) * 9999; + Sprintf(eos(buf), "%d%s%s", abs(dy), dirnames[(dy > 0)][fulldir], + dx ? "," : ""); + } + if (dx) { + if (abs(dx) > 9999) + dx = sgn(dx) * 9999; + Sprintf(eos(buf), "%d%s", abs(dx), + dirnames[2 + (dx > 0)][fulldir]); + } + } + return buf; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* coordinate formatting for 'whatis_coord' option */ +char * +coord_desc(coordxy x, coordxy y, char *outbuf, char cmode) +{ + static char screen_fmt[16]; /* [12] suffices: "[%02d,%02d]" */ + int dx, dy; + + outbuf[0] = '\0'; + switch (cmode) { + default: + break; + case GPCOORDS_COMFULL: + case GPCOORDS_COMPASS: + /* "east", "3s", "2n,4w" */ + dx = x - u.ux; + dy = y - u.uy; + Sprintf(outbuf, "(%s)", + dxdy_to_dist_descr(dx, dy, cmode == GPCOORDS_COMFULL)); + break; + case GPCOORDS_MAP: /* x,y */ + /* upper left corner of map is <1,0>; + with default COLNO,ROWNO lower right corner is <79,20> */ + Sprintf(outbuf, "<%d,%d>", x, y); + break; + case GPCOORDS_SCREEN: /* y+2,x */ + /* for normal map sizes, force a fixed-width formatting so that + /m, /M, /o, and /O output lines up cleanly; map sizes bigger + than Nx999 or 999xM will still work, but not line up like normal + when displayed in a column setting. + + The (100) is placed in brackets below to mark the [: "03"] as + explicit compile-time dead code for clang */ + if (!*screen_fmt) + Sprintf(screen_fmt, "[%%%sd,%%%sd]", + (ROWNO - 1 + 2 < (100)) ? "02" : "03", + (COLNO - 1 < (100)) ? "02" : "03"); + /* map line 0 is screen row 2; + map column 0 isn't used, map column 1 is screen column 1 */ + Sprintf(outbuf, screen_fmt, y + 2, x); + break; + } + return outbuf; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +void +auto_describe(coordxy cx, coordxy cy) +{ + coord cc; + int sym = 0; + char tmpbuf[BUFSZ]; + const char *firstmatch = "unknown"; + + cc.x = cx; + cc.y = cy; + if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch, + (struct permonst **) 0)) { + (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords); + custompline((SUPPRESS_HISTORY | OVERRIDE_MSGTYPE | NO_CURS_ON_U), + "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf, + (iflags.autodescribe + && getpos_getvalid && !(*getpos_getvalid)(cx, cy)) + ? " (invalid target)" : "", + (iflags.getloc_travelmode && !is_valid_travelpt(cx, cy)) + ? " (no travel path)" : ""); + curs(WIN_MAP, cx, cy); + flush_screen(0); + } +} + +boolean +getpos_menu(coord *ccp, int gloc) +{ + coord *garr = DUMMY; + int gcount = 0; + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *) 0; + char tmpbuf[BUFSZ]; + int clr = NO_COLOR; + + gather_locs(&garr, &gcount, gloc); + + if (gcount < 2) { /* gcount always includes the hero */ + free((genericptr_t) garr); + You("cannot %s %s.", + (iflags.getloc_filter == GFILTER_VIEW) ? "see" : "detect", + gloc_descr[gloc][0]); + return FALSE; + } + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + /* gather_locs returns array[0] == you. skip it. */ + for (i = 1; i < gcount; i++) { + char fullbuf[BUFSZ]; + coord tmpcc; + const char *firstmatch = "unknown"; + int sym = 0; + + any.a_int = i + 1; + tmpcc.x = garr[i].x; + tmpcc.y = garr[i].y; + if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, + &firstmatch, (struct permonst **)0)) { + (void) coord_desc(garr[i].x, garr[i].y, tmpbuf, + iflags.getpos_coords); + Snprintf(fullbuf, sizeof fullbuf, "%s%s%s", firstmatch, + (*tmpbuf ? " " : ""), tmpbuf); + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, fullbuf, MENU_ITEMFLAGS_NONE); + } + } + + Sprintf(tmpbuf, "Pick %s%s%s", + an(gloc_descr[gloc][1]), + gloc_filtertxt[iflags.getloc_filter], + iflags.getloc_travelmode ? " for travel destination" : ""); + end_menu(tmpwin, tmpbuf); + pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (pick_cnt > 0) { + ccp->x = garr[picks->item.a_int - 1].x; + ccp->y = garr[picks->item.a_int - 1].y; + free((genericptr_t) picks); + } + free((genericptr_t) garr); + return (pick_cnt > 0); +} + +/* add dx,dy to cx,cy, truncating at map edges */ +static void +truncate_to_map(coordxy *cx, coordxy *cy, schar dx, schar dy) +{ + /* diagonal moves complicate this... */ + if (*cx + dx < 1) { + dy -= sgn(dy) * (1 - (*cx + dx)); + dx = 1 - *cx; /* so that (cx+dx == 1) */ + } else if (*cx + dx > COLNO - 1) { + dy += sgn(dy) * ((COLNO - 1) - (*cx + dx)); + dx = (COLNO - 1) - *cx; + } + if (*cy + dy < 0) { + dx -= sgn(dx) * (0 - (*cy + dy)); + dy = 0 - *cy; /* so that (cy+dy == 0) */ + } else if (*cy + dy > ROWNO - 1) { + dx += sgn(dx) * ((ROWNO - 1) - (*cy + dy)); + dy = (ROWNO - 1) - *cy; + } + *cx += dx; + *cy += dy; +} + +/* called when ^R typed; if '$' is being shown for valid spots, remove that; + if alternate background color is being shown for that, redraw it */ +static void +getpos_refresh(void) +{ + if (getpos_hilitefunc && getpos_hilite_state == HiliteGoodposSymbol) { + (*getpos_hilitefunc)(FALSE); /* tmp_at(DISP_END) */ + getpos_hilite_state = defaultHiliteState; + } + + docrt_flags(docrtRefresh); + + if (getpos_hilitefunc && getpos_hilite_state == HiliteBackground) { + /* resetting to current values will draw valid-spots highlighting */ + getpos_sethilite(getpos_hilitefunc, getpos_getvalid); + } +} + +/* have the player use movement keystrokes to position the cursor at a + particular map location, then use one of [.,:;] to pick the spot */ +int +getpos(coord *ccp, boolean force, const char *goal) +{ + static struct { + int nhkf, ret; + } const pick_chars_def[] = { + { NHKF_GETPOS_PICK, LOOK_TRADITIONAL }, + { NHKF_GETPOS_PICK_Q, LOOK_QUICK }, + { NHKF_GETPOS_PICK_O, LOOK_ONCE }, + { NHKF_GETPOS_PICK_V, LOOK_VERBOSE } + }; + static const int mMoOdDxX_def[] = { + NHKF_GETPOS_MON_NEXT, + NHKF_GETPOS_MON_PREV, + NHKF_GETPOS_OBJ_NEXT, + NHKF_GETPOS_OBJ_PREV, + NHKF_GETPOS_DOOR_NEXT, + NHKF_GETPOS_DOOR_PREV, + NHKF_GETPOS_UNEX_NEXT, + NHKF_GETPOS_UNEX_PREV, + NHKF_GETPOS_INTERESTING_NEXT, + NHKF_GETPOS_INTERESTING_PREV, + NHKF_GETPOS_VALID_NEXT, + NHKF_GETPOS_VALID_PREV + }; + struct _cmd_queue cq, *cmdq; + const char *cp; + char pick_chars[6]; + char mMoOdDxX[13]; + int result = 0; + int i, c; + int sidx; + coordxy cx, cy; + coordxy tx = u.ux, ty = u.uy; + boolean msg_given = TRUE; /* clear message window by default */ + boolean show_goal_msg = FALSE; + coord *garr[NUM_GLOCS] = DUMMY; + int gcount[NUM_GLOCS] = DUMMY; + int gidx[NUM_GLOCS] = DUMMY; + schar udx = u.dx, udy = u.dy, udz = u.dz; + int dx, dy; + boolean rushrun = FALSE; + + /* temporary? if we have a queued direction, return the adjacent spot + in that direction */ + if (!gi.in_doagain) { + if ((cmdq = cmdq_pop()) != 0) { + cq = *cmdq; + free((genericptr_t) cmdq); + if (cq.typ == CMDQ_DIR && !cq.dirz) { + ccp->x = u.ux + cq.dirx; + ccp->y = u.uy + cq.diry; + } else { + cmdq_clear(CQ_CANNED); + result = -1; + } + return result; + } + } + + for (i = 0; i < SIZE(pick_chars_def); i++) + pick_chars[i] = gc.Cmd.spkeys[pick_chars_def[i].nhkf]; + pick_chars[SIZE(pick_chars_def)] = '\0'; + + for (i = 0; i < SIZE(mMoOdDxX_def); i++) + mMoOdDxX[i] = gc.Cmd.spkeys[mMoOdDxX_def[i]]; + mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0'; + + handle_tip(TIP_GETPOS); + + if (!goal) + goal = "desired location"; + if (flags.verbose) { + pline("(For instructions type a '%s')", + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_HELP])); + msg_given = TRUE; + } + cx = gg.getposx = ccp->x; + cy = gg.getposy = ccp->y; +#ifdef CLIPPING + cliparound(cx, cy); +#endif + curs(WIN_MAP, cx, cy); + flush_screen(0); +#ifdef MAC + lock_mouse_cursor(TRUE); +#endif + lock_mouse_buttons(TRUE); + for (;;) { + if (show_goal_msg) { + pline("Move cursor to %s:", goal); + curs(WIN_MAP, cx, cy); + flush_screen(0); + show_goal_msg = FALSE; + } else if (iflags.autodescribe && !msg_given) { + auto_describe(cx, cy); + } + + rushrun = FALSE; + + if ((cmdq = cmdq_pop()) != 0) { + if (cmdq->typ == CMDQ_KEY) { + c = cmdq->key; + } else { + cmdq_clear(CQ_CANNED); + result = -1; + goto exitgetpos; + } + free(cmdq); + } else { + c = readchar_poskey(&tx, &ty, &sidx); + /* remember_getpos is normally False because reusing the + cursor positioning during ^A is almost never the right + thing to do, but caller could set it if that was needed */ + if (iflags.remember_getpos && !gi.in_doagain) + cmdq_add_key(CQ_REPEAT, c); + } + + if (iflags.autodescribe) + msg_given = FALSE; + + if (c == gc.Cmd.spkeys[NHKF_ESC]) { + cx = cy = -10; + msg_given = TRUE; /* force clear */ + result = -1; + break; + } + if (c == cmd_from_func(do_run) || c == cmd_from_func(do_rush)) { + c = readchar_poskey(&tx, &ty, &sidx); + rushrun = TRUE; + } + if (c == 0) { + if (!isok(tx, ty)) + continue; + /* a mouse click event, just assign and return */ + cx = tx; + cy = ty; + break; + } + if ((cp = strchr(pick_chars, c)) != 0) { + /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */ + result = pick_chars_def[(int) (cp - pick_chars)].ret; + break; + } else if (movecmd(c, MV_WALK)) { + if (rushrun) + goto do_rushrun; + dx = u.dx; + dy = u.dy; + truncate_to_map(&cx, &cy, dx, dy); + goto nxtc; + } else if (movecmd(c, MV_RUSH) || movecmd(c, MV_RUN)) { + do_rushrun: + if (iflags.getloc_moveskip) { + /* skip same glyphs */ + int glyph = glyph_at(cx, cy); + + dx = u.dx; + dy = u.dy; + while (isok(cx + dx, cy + dy) + && glyph == glyph_at(cx + dx, cy + dy) + && isok(cx + dx + u.dx, cy + dy + u.dy) + && glyph == glyph_at(cx + dx + u.dx, cy + dy + u.dy)) { + dx += u.dx; + dy += u.dy; + + } + } else { + dx = 8 * u.dx; + dy = 8 * u.dy; + } + truncate_to_map(&cx, &cy, dx, dy); + goto nxtc; + } + + if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) { + /* '?' will redraw twice, first when removing popup text window + after showing the help text, then to reset highlighting */ + if (c == gc.Cmd.spkeys[NHKF_GETPOS_HELP]) + getpos_help(force, goal); + /* ^R: docrt(), hilite_state = default */ + getpos_refresh(); + curs(WIN_MAP, cx, cy); + /* update message window to reflect that we're still targeting */ + show_goal_msg = TRUE; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SHOWVALID]) { + if (getpos_hilitefunc) { + getpos_toggle_hilite_state(); + curs(WIN_MAP, cx, cy); + } + show_goal_msg = TRUE; /* we're still targeting */ + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_AUTODESC]) { + iflags.autodescribe = !iflags.autodescribe; + pline("Automatic description %sis %s.", + flags.verbose ? "of features under cursor " : "", + iflags.autodescribe ? "on" : "off"); + if (!iflags.autodescribe) + show_goal_msg = TRUE; + msg_given = TRUE; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_LIMITVIEW]) { + static const char *const view_filters[NUM_GFILTER] = { + "Not limiting targets", + "Limiting targets to those in sight", + "Limiting targets to those in same area" + }; + + iflags.getloc_filter = (iflags.getloc_filter + 1) % NUM_GFILTER; + for (i = 0; i < NUM_GLOCS; i++) { + if (garr[i]) { + free((genericptr_t) garr[i]); + garr[i] = NULL; + } + gidx[i] = gcount[i] = 0; + } + pline("%s.", view_filters[iflags.getloc_filter]); + msg_given = TRUE; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MENU]) { + iflags.getloc_usemenu = !iflags.getloc_usemenu; + pline("%s a menu to show possible targets%s.", + iflags.getloc_usemenu ? "Using" : "Not using", + iflags.getloc_usemenu + ? " for 'm|M', 'o|O', 'd|D', and 'x|X'" : ""); + msg_given = TRUE; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_SELF]) { + /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player + to achieve that except by manually cycling through all spots */ + for (i = 0; i < NUM_GLOCS; i++) + gidx[i] = 0; + cx = u.ux; + cy = u.uy; + goto nxtc; + } else if (c == gc.Cmd.spkeys[NHKF_GETPOS_MOVESKIP]) { + iflags.getloc_moveskip = !iflags.getloc_moveskip; + pline("%skipping over similar terrain when fastmoving the cursor.", + iflags.getloc_moveskip ? "S" : "Not s"); + msg_given = TRUE; + goto nxtc; + } else if ((cp = strchr(mMoOdDxX, c)) != 0) { /* 'm|M', 'o|O', &c */ + /* nearest or farthest monster or object or door or unexplored */ + int gtmp = (int) (cp - mMoOdDxX), /* 0..7 */ + gloc = gtmp >> 1; /* 0..3 */ + + if (iflags.getloc_usemenu) { + coord tmpcrd; + + if (getpos_menu(&tmpcrd, gloc)) { + cx = tmpcrd.x; + cy = tmpcrd.y; + } + goto nxtc; + } + + if (!garr[gloc]) { + gather_locs(&garr[gloc], &gcount[gloc], gloc); + gidx[gloc] = 0; /* garr[][0] is hero's spot */ + } + if (!(gtmp & 1)) { /* c=='m' || c=='o' || c=='d' || c=='x') */ + gidx[gloc] = (gidx[gloc] + 1) % gcount[gloc]; + } else { /* c=='M' || c=='O' || c=='D' || c=='X') */ + if (--gidx[gloc] < 0) + gidx[gloc] = gcount[gloc] - 1; + } + cx = garr[gloc][gidx[gloc]].x; + cy = garr[gloc][gidx[gloc]].y; + goto nxtc; + } else { + if (!strchr(quitchars, c)) { + char matching[MAXPCHARS]; + int pass, k = 0; + coordxy lo_x, lo_y, hi_x, hi_y; + + (void) memset((genericptr_t) matching, 0, sizeof matching); + for (sidx = 0; sidx < MAXPCHARS; sidx++) { + /* don't even try to match some terrain: walls, room... */ + if (is_cmap_wall(sidx) || is_cmap_room(sidx) + || is_cmap_corr(sidx) || is_cmap_door(sidx) + || sidx == S_ndoor) + continue; + if (c == defsyms[sidx].sym + || c == (int) gs.showsyms[sidx] + /* have '^' match webs and vibrating square or any + other trap that uses something other than '^' */ + || (c == '^' && is_cmap_trap(sidx))) + matching[sidx] = (char) ++k; + } + if (k) { + for (pass = 0; pass <= 1; pass++) { + /* pass 0: just past current pos to lower right; + pass 1: upper left corner to current pos */ + lo_y = (pass == 0) ? cy : 0; + hi_y = (pass == 0) ? ROWNO - 1 : cy; + for (ty = lo_y; ty <= hi_y; ty++) { + lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1; + hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1; + for (tx = lo_x; tx <= hi_x; tx++) { + /* first, look at what is currently visible + (might be monster) */ + k = glyph_at(tx, ty); + if (glyph_is_cmap(k) + && matching[glyph_to_cmap(k)]) + goto foundc; + /* next, try glyph that's remembered here + (might be trap or object) */ + if (gl.level.flags.hero_memory + /* !terrainmode: don't move to remembered + trap or object if not currently shown */ + && !iflags.terrainmode) { + k = levl[tx][ty].glyph; + if (glyph_is_cmap(k) + && matching[glyph_to_cmap(k)]) + goto foundc; + } + /* last, try actual terrain here (shouldn't + we be using gl.lastseentyp[][] instead?) */ + if (levl[tx][ty].seenv) { + k = back_to_glyph(tx, ty); + if (glyph_is_cmap(k) + && matching[glyph_to_cmap(k)]) + goto foundc; + } + continue; + foundc: + cx = tx, cy = ty; + if (msg_given) { + clear_nhwindow(WIN_MESSAGE); + msg_given = FALSE; + } + goto nxtc; + } /* column */ + } /* row */ + } /* pass */ + pline("Can't find dungeon feature '%c'.", c); + msg_given = TRUE; + goto nxtc; + } else { + char note[QBUFSZ]; + + if (!force) + Strcpy(note, "aborted"); + else /* hjkl */ + Sprintf(note, "use '%s', '%s', '%s', '%s' or '%s'", + visctrl(cmd_from_func(do_move_west)), + visctrl(cmd_from_func(do_move_south)), + visctrl(cmd_from_func(do_move_north)), + visctrl(cmd_from_func(do_move_east)), + visctrl(gc.Cmd.spkeys[NHKF_GETPOS_PICK])); + pline("Unknown direction: '%s' (%s).", visctrl((char) c), + note); + msg_given = TRUE; + } /* k => matching */ + } /* !quitchars */ + if (force) + goto nxtc; + pline("Done."); + msg_given = FALSE; /* suppress clear */ + cx = -1; + cy = 0; + result = 0; /* not -1 */ + break; + } + nxtc: + gg.getposx = cx, gg.getposy = cy; +#ifdef CLIPPING + cliparound(cx, cy); +#endif + curs(WIN_MAP, cx, cy); + flush_screen(0); + } + exitgetpos: +#ifdef MAC + lock_mouse_cursor(FALSE); +#endif + lock_mouse_buttons(FALSE); + if (msg_given) + clear_nhwindow(WIN_MESSAGE); + ccp->x = cx; + ccp->y = cy; + gg.getposx = gg.getposy = 0; + for (i = 0; i < NUM_GLOCS; i++) + if (garr[i]) + free((genericptr_t) garr[i]); + getpos_sethilite(NULL, NULL); + u.dx = udx, u.dy = udy, u.dz = udz; + return result; +} + +/*getpos.c*/ diff --git a/src/hack.c b/src/hack.c index 2624db0e9..88fab01be 100644 --- a/src/hack.c +++ b/src/hack.c @@ -4,6 +4,7 @@ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" +#include "extern.h" /* #define DEBUG */ /* uncomment for debugging */ @@ -4184,4 +4185,31 @@ spot_checks(coordxy x, coordxy y, schar old_typ) break; } } + +/* calculate x/y, rounding as appropriate */ +int +rounddiv(long x, int y) +{ + int r, m; + int divsgn = 1; + + if (y == 0) + panic("division by zero in rounddiv"); + else if (y < 0) { + divsgn = -divsgn; + y = -y; + } + if (x < 0) { + divsgn = -divsgn; + x = -x; + } + r = (int) (x / y); + m = x % y; + if (2 * m >= y) + r++; + + return divsgn * r; +} + + /*hack.c*/ diff --git a/src/hacklib.c b/src/hacklib.c index 1d09a1ae9..9513ca42b 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -49,39 +49,15 @@ int distmin (int, int, int, int) int dist2 (int, int, int, int) boolean online2 (int, int) - boolean pmatch (const char *, const char *) - boolean pmatchi (const char *, const char *) - boolean pmatchz (const char *, const char *) int strncmpi (const char *, const char *, int) char * strstri (const char *, const char *) boolean fuzzymatch (const char *, const char *, const char *, boolean) void setrandom (void) - void init_random (fn) - void reseed_random (fn) - time_t getnow (void) - int getyear (void) - char * yymmdd (time_t) - long yyyymmdd (time_t) - long hhmmss (time_t) - char * yyyymmddhhmmss (time_t) - time_t time_from_yyyymmddhhmmss (char *) - int phase_of_the_moon (void) - boolean friday_13th (void) - int night (void) - int midnight (void) - void strbuf_init (strbuf *, const char *) - void strbuf_append (strbuf *, const char *) - void strbuf_reserve (strbuf *, int) - void strbuf_empty (strbuf *) - void strbuf_nl_to_crlf (strbuf_t *) int swapbits (int, int, int) - void shuffle_int_array (int *, int) void nh_snprintf (const char *, int, char *, size_t, const char *, ...) =*/ -static boolean pmatch_internal(const char *, const char *, boolean, - const char *); /* is 'c' a digit? */ boolean @@ -255,8 +231,10 @@ str_start_is( if (t1 != t2) return FALSE; } +#if 0 if (n == 0) panic("string too long"); +#endif return TRUE; } @@ -676,6 +654,7 @@ sgn(int n) return (n < 0) ? -1 : (n != 0); } +#if 0 /* calculate x/y, rounding as appropriate */ int rounddiv(long x, int y) @@ -700,6 +679,7 @@ rounddiv(long x, int y) return divsgn * r; } +#endif /* distance between two points, in moves */ int @@ -759,74 +739,6 @@ online2(coordxy x0, coordxy y0, coordxy x1, coordxy y1) return (boolean) (!dy || !dx || dy == dx || dy == -dx); } -/* guts of pmatch(), pmatchi(), and pmatchz(); - match a string against a pattern */ -static boolean -pmatch_internal(const char *patrn, const char *strng, - boolean ci, /* True => case-insensitive, - False => case-sensitive */ - const char *sk) /* set of characters to skip */ -{ - char s, p; - /* - * Simple pattern matcher: '*' matches 0 or more characters, '?' matches - * any single character. Returns TRUE if 'strng' matches 'patrn'. - */ - pmatch_top: - if (!sk) { - s = *strng++; - p = *patrn++; /* get next chars and pre-advance */ - } else { - /* fuzzy match variant of pmatch; particular characters are ignored */ - do { - s = *strng++; - } while (strchr(sk, s)); - do { - p = *patrn++; - } while (strchr(sk, p)); - } - if (!p) /* end of pattern */ - return (boolean) (s == '\0'); /* matches iff end of string too */ - else if (p == '*') /* wildcard reached */ - return (boolean) ((!*patrn - || pmatch_internal(patrn, strng - 1, ci, sk)) - ? TRUE - : s ? pmatch_internal(patrn - 1, strng, ci, sk) - : FALSE); - else if ((ci ? lowc(p) != lowc(s) : p != s) /* check single character */ - && (p != '?' || !s)) /* & single-char wildcard */ - return FALSE; /* doesn't match */ - else /* return pmatch_internal(patrn, strng, ci, sk); */ - goto pmatch_top; /* optimize tail recursion */ -} - -/* case-sensitive wildcard match */ -boolean -pmatch(const char *patrn, const char *strng) -{ - return pmatch_internal(patrn, strng, FALSE, (const char *) 0); -} - -/* case-insensitive wildcard match */ -boolean -pmatchi(const char *patrn, const char *strng) -{ - return pmatch_internal(patrn, strng, TRUE, (const char *) 0); -} - -#if 0 -/* case-insensitive wildcard fuzzymatch; - NEVER WORKED AS INTENDED but fortunately isn't needed */ -boolean -pmatchz(const char *patrn, const char *strng) -{ - /* ignore spaces, tabs (just in case), dashes, and underscores */ - static const char fuzzychars[] = " \t-_"; - - return pmatch_internal(patrn, strng, TRUE, fuzzychars); -} -#endif - #ifndef STRNCMPI /* case insensitive counted string comparison */ /*{ aka strncasecmp }*/ @@ -944,359 +856,6 @@ fuzzymatch( #define LOCALTIME_type time_t * #endif -static struct tm *getlt(void); - -/* Sets the seed for the random number generator */ -#ifdef USE_ISAAC64 -static void set_random(unsigned long seed, int (*)(int)); - -static void -set_random(unsigned long seed, - int (*fn)(int)) -{ - init_isaac64(seed, fn); -} - -#else /* USE_ISAAC64 */ - -/*ARGSUSED*/ -static void -set_random(unsigned long seed, - int (*fn)(int) UNUSED) -{ - /* - * The types are different enough here that sweeping the different - * routine names into one via #defines is even more confusing. - */ -# ifdef RANDOM /* srandom() from sys/share/random.c */ - srandom((unsigned int) seed); -# else -# if defined(__APPLE__) || defined(BSD) || defined(LINUX) \ - || defined(ULTRIX) || defined(CYGWIN32) /* system srandom() */ -# if defined(BSD) && !defined(POSIX_TYPES) && defined(SUNOS4) - (void) -# endif - srandom((int) seed); -# else -# ifdef UNIX /* system srand48() */ - srand48((long) seed); -# else /* poor quality system routine */ - srand((int) seed); -# endif -# endif -# endif -} - -#endif /* USE_ISAAC64 */ - -/* An appropriate version of this must always be provided in - port-specific code somewhere. It returns a number suitable - as seed for the random number generator */ -extern unsigned long sys_random_seed(void); - -/* - * Initializes the random number generator. - * Only call once. - */ -void -init_random(int (*fn)(int)) -{ - set_random(sys_random_seed(), fn); -} - -/* Reshuffles the random number generator. */ -void -reseed_random(int (*fn)(int)) -{ - /* only reseed if we are certain that the seed generation is unguessable - * by the players. */ - if (has_strong_rngseed) - init_random(fn); -} - -time_t -getnow(void) -{ - time_t datetime = 0; - - (void) time((TIME_type) &datetime); - return datetime; -} - -static struct tm * -getlt(void) -{ - time_t date = getnow(); - - return localtime((LOCALTIME_type) &date); -} - -int -getyear(void) -{ - return (1900 + getlt()->tm_year); -} - -#if 0 -/* This routine is no longer used since in 20YY it yields "1YYmmdd". */ -char * -yymmdd(time_t date) -{ - static char datestr[10]; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - Sprintf(datestr, "%02d%02d%02d", - lt->tm_year, lt->tm_mon + 1, lt->tm_mday); - return datestr; -} -#endif - -long -yyyymmdd(time_t date) -{ - long datenum; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - /* just in case somebody's localtime supplies (year % 100) - rather than the expected (year - 1900) */ - if (lt->tm_year < 70) - datenum = (long) lt->tm_year + 2000L; - else - datenum = (long) lt->tm_year + 1900L; - /* yyyy --> yyyymm */ - datenum = datenum * 100L + (long) (lt->tm_mon + 1); - /* yyyymm --> yyyymmdd */ - datenum = datenum * 100L + (long) lt->tm_mday; - return datenum; -} - -long -hhmmss(time_t date) -{ - long timenum; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - timenum = lt->tm_hour * 10000L + lt->tm_min * 100L + lt->tm_sec; - return timenum; -} - -char * -yyyymmddhhmmss(time_t date) -{ - long datenum; - static char datestr[15]; - struct tm *lt; - - if (date == 0) - lt = getlt(); - else - lt = localtime((LOCALTIME_type) &date); - - /* just in case somebody's localtime supplies (year % 100) - rather than the expected (year - 1900) */ - if (lt->tm_year < 70) - datenum = (long) lt->tm_year + 2000L; - else - datenum = (long) lt->tm_year + 1900L; - Snprintf(datestr, sizeof datestr, "%04ld%02d%02d%02d%02d%02d", - datenum, lt->tm_mon + 1, - lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); - debugpline1("yyyymmddhhmmss() produced date string %s", datestr); - return datestr; -} - -time_t -time_from_yyyymmddhhmmss(char *buf) -{ - int k; - time_t timeresult = (time_t) 0; - struct tm t, *lt; - char *d, *p, y[5], mo[3], md[3], h[3], mi[3], s[3]; - - if (buf && strlen(buf) == 14) { - d = buf; - p = y; /* year */ - for (k = 0; k < 4; ++k) - *p++ = *d++; - *p = '\0'; - p = mo; /* month */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = md; /* day */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = h; /* hour */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = mi; /* minutes */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - p = s; /* seconds */ - for (k = 0; k < 2; ++k) - *p++ = *d++; - *p = '\0'; - lt = getlt(); - if (lt) { - t = *lt; - t.tm_year = atoi(y) - 1900; - t.tm_mon = atoi(mo) - 1; - t.tm_mday = atoi(md); - t.tm_hour = atoi(h); - t.tm_min = atoi(mi); - t.tm_sec = atoi(s); - timeresult = mktime(&t); - } - if (timeresult == (time_t) -1) { - debugpline1("time_from_yyyymmddhhmmss(%s) would have returned -1", - buf ? buf : ""); - timeresult = (time_t) 0; - } - } - return timeresult; -} - -/* - * moon period = 29.53058 days ~= 30, year = 365.2422 days - * days moon phase advances on first day of year compared to preceding year - * = 365.2422 - 12*29.53058 ~= 11 - * years in Metonic cycle (time until same phases fall on the same days of - * the month) = 18.6 ~= 19 - * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 - * (29 as initial condition) - * current phase in days = first day phase + days elapsed in year - * 6 moons ~= 177 days - * 177 ~= 8 reported phases * 22 - * + 11/22 for rounding - */ -int -phase_of_the_moon(void) /* 0-7, with 0: new, 4: full */ -{ - struct tm *lt = getlt(); - int epact, diy, goldn; - - diy = lt->tm_yday; - goldn = (lt->tm_year % 19) + 1; - epact = (11 * goldn + 18) % 30; - if ((epact == 25 && goldn > 11) || epact == 24) - epact++; - - return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7); -} - -boolean -friday_13th(void) -{ - struct tm *lt = getlt(); - - /* tm_wday (day of week; 0==Sunday) == 5 => Friday */ - return (boolean) (lt->tm_wday == 5 && lt->tm_mday == 13); -} - -int -night(void) -{ - int hour = getlt()->tm_hour; - - return (hour < 6 || hour > 21); -} - -int -midnight(void) -{ - return (getlt()->tm_hour == 0); -} - -/* strbuf_init() initializes strbuf state for use */ -void -strbuf_init(strbuf_t *strbuf) -{ - strbuf->str = NULL; - strbuf->len = 0; -} - -/* strbuf_append() appends given str to strbuf->str */ -void -strbuf_append(strbuf_t *strbuf, const char *str) -{ - int len = (int) strlen(str) + 1; - - strbuf_reserve(strbuf, - len + (strbuf->str ? (int) strlen(strbuf->str) : 0)); - Strcat(strbuf->str, str); -} - -/* strbuf_reserve() ensure strbuf->str has storage for len characters */ -void -strbuf_reserve(strbuf_t *strbuf, int len) -{ - if (strbuf->str == NULL) { - strbuf->str = strbuf->buf; - strbuf->str[0] = '\0'; - strbuf->len = (int) sizeof strbuf->buf; - } - - if (len > strbuf->len) { - char *oldbuf = strbuf->str; - - strbuf->len = len + (int) sizeof strbuf->buf; - strbuf->str = (char *) alloc(strbuf->len); - Strcpy(strbuf->str, oldbuf); - if (oldbuf != strbuf->buf) - free((genericptr_t) oldbuf); - } -} - -/* strbuf_empty() frees allocated memory and set strbuf to initial state */ -void -strbuf_empty(strbuf_t *strbuf) -{ - if (strbuf->str != NULL && strbuf->str != strbuf->buf) - free((genericptr_t) strbuf->str); - strbuf_init(strbuf); -} - -/* strbuf_nl_to_crlf() converts all occurrences of \n to \r\n */ -void -strbuf_nl_to_crlf(strbuf_t *strbuf) -{ - if (strbuf->str) { - int len = (int) strlen(strbuf->str); - int count = 0; - char *cp = strbuf->str; - - while (*cp) - if (*cp++ == '\n') - count++; - if (count) { - strbuf_reserve(strbuf, len + count + 1); - for (cp = strbuf->str + len + count; count; --cp) - if ((*cp = cp[-count]) == '\n') { - *--cp = '\r'; - --count; - } - } - } -} - /* swapbits(val, bita, bitb) swaps bit a with bit b in val */ int swapbits(int val, int bita, int bitb) @@ -1306,21 +865,6 @@ swapbits(int val, int bita, int bitb) return (val ^ ((tmp << bita) | (tmp << bitb))); } -/* randomize the given list of numbers 0 <= i < count */ -void -shuffle_int_array(int *indices, int count) -{ - int i, iswap, temp; - - for (i = count - 1; i > 0; i--) { - if ((iswap = rn2(i + 1)) == i) - continue; - temp = indices[i]; - indices[i] = indices[iswap]; - indices[iswap] = temp; - } -} - DISABLE_WARNING_FORMAT_NONLITERAL /* @@ -1337,7 +881,7 @@ DISABLE_WARNING_FORMAT_NONLITERAL */ void nh_snprintf( - const char *func, int line, + const char *func UNUSED, int line UNUSED, char *str, size_t size, const char *fmt, ...) { @@ -1348,17 +892,19 @@ nh_snprintf( n = vsnprintf(str, size, fmt, ap); va_end(ap); if (n < 0 || (size_t) n >= size) { /* is there a problem? */ +#if 0 +TODO: add set_impossible(), impossible -> func pointer, + test funcpointer before call impossible("snprintf %s: func %s, file line %d", (n < 0) ? "format error" : "overflow", func, line); +#endif str[size - 1] = '\0'; /* make sure it is nul terminated */ } } RESTORE_WARNING_FORMAT_NONLITERAL -#ifdef ENHANCED_SYMBOLS - /* Unicode routines */ int @@ -1400,6 +946,5 @@ unicodeval_to_utf8str(int uval, uint8 *buffer, size_t bufsz) *b = '\0'; /* NUL terminate */ return 1; } -#endif /* ENHANCED_SYMBOLS */ /*hacklib.c*/ diff --git a/src/mdlib.c b/src/mdlib.c index 740c3ecb1..23827a812 100644 --- a/src/mdlib.c +++ b/src/mdlib.c @@ -27,6 +27,7 @@ #include "context.h" #include "flag.h" #include "dlb.h" +#include "hacklib.h" #include /* version information */ #ifdef SHORT_FILENAMES @@ -60,7 +61,9 @@ char *bannerc_string(char *, size_t, const char *) NONNULL NONNULLPTRS; int case_insensitive_comp(const char *, const char *) NONNULLPTRS; static void make_version(void); +#if 0 static char *eos(char *) NONNULL NONNULLARG1; +#endif #if 0 static char *mdlib_strsubst(char *, const char *, const char *); @@ -79,6 +82,7 @@ extern int GUILaunched; #endif /* these are in extern.h but we don't include hack.h */ +/* XXX move to new file mdlib.h? */ extern void populate_nomakedefs(struct version_info *) NONNULLARG1; /* date.c */ extern void free_nomakedefs(void); /* date.c */ void runtime_info_init(void); @@ -341,29 +345,6 @@ mdlib_version_string(char *outbuf, const char *delim) extern void nh_snprintf(const char *func, int line, char *str, size_t size, const char *fmt, ...); -#ifdef MAKEDEFS_C -DISABLE_WARNING_FORMAT_NONLITERAL - -void -nh_snprintf(const char *func UNUSED, int line UNUSED, char *str, size_t size, - const char *fmt, ...) -{ - va_list ap; - int n; - - va_start(ap, fmt); - n = vsnprintf(str, size, fmt, ap); - va_end(ap); - - if (n < 0 || (size_t) n >= size) { /* is there a problem? */ - str[size-1] = 0; /* make sure it is nul terminated */ - } - -} - -RESTORE_WARNING_FORMAT_NONLITERAL -#endif /* MAKEDEFS_C */ - char * version_id_string(char *outbuf, size_t bufsz, const char *build_date) { @@ -439,6 +420,7 @@ mkstemp(char *template) #endif /* HAS_NO_MKSTEMP */ #endif /* MAKEDEFS_C || FOR_RUNTIME */ +#if 0 static char * eos(char *str) { @@ -446,6 +428,7 @@ eos(char *str) str++; return str; } +#endif #if 0 static char * diff --git a/src/report.c b/src/report.c new file mode 100644 index 000000000..a65e8aa20 --- /dev/null +++ b/src/report.c @@ -0,0 +1,622 @@ +/* NetHack 3.7 report.c $NHDT-Date: 1709571806 2024/03/04 17:03:26 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.306 $ */ +/* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2024 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* NB: CRASHREPORT implies PANICTRACE */ +#ifdef CRASHREPORT + +# ifndef NO_SIGNAL +#include +# endif +#include +# ifndef LONG_MAX +#include +# endif +#include "dlb.h" +#include + +#include +# ifdef PANICTRACE_LIBC +#include +# endif + +# ifndef NO_SIGNAL +/* called as signal() handler, so sent at least one arg */ +/*ARGUSED*/ +void +panictrace_handler(int sig_unused UNUSED) +{ +#define SIG_MSG "\nSignal received.\n" + int f2; + +# ifdef CURSES_GRAPHICS + if (iflags.window_inited && WINDOWPORT(curses)) { + extern void curses_uncurse_terminal(void); /* wincurs.h */ + + /* it is risky calling this during a program-terminating signal, + but without it the subsequent backtrace is useless because + that ends up being scrawled all over the screen; call is + here rather than in NH_abort() because panic() calls both + exit_nhwindows(), which makes this same call under curses, + then NH_abort() and we don't want to call this twice */ + curses_uncurse_terminal(); + } +# endif + + f2 = (int) write(2, SIG_MSG, sizeof SIG_MSG - 1); + nhUse(f2); /* what could we do if write to fd#2 (stderr) fails */ + NH_abort(NULL); /* ... and we're already in the process of quitting? */ +} + +void +panictrace_setsignals(boolean set) +{ +#define SETSIGNAL(sig) \ + (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL); +# ifdef SIGILL + SETSIGNAL(SIGILL); +# endif +# ifdef SIGTRAP + SETSIGNAL(SIGTRAP); +# endif +# ifdef SIGIOT + SETSIGNAL(SIGIOT); +# endif +# ifdef SIGBUS + SETSIGNAL(SIGBUS); +# endif +# ifdef SIGFPE + SETSIGNAL(SIGFPE); +# endif +# ifdef SIGSEGV + SETSIGNAL(SIGSEGV); +# endif +# ifdef SIGSTKFLT + SETSIGNAL(SIGSTKFLT); +# endif +# ifdef SIGSYS + SETSIGNAL(SIGSYS); +# endif +# ifdef SIGEMT + SETSIGNAL(SIGEMT); +# endif +#undef SETSIGNAL +} +# endif /* NO_SIGNAL */ + +# ifdef WIN32 +# define HASH_PRAGMA_START +# define HASH_PRAGMA_END +# else +# define HASH_PRAGMA_START \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +# define HASH_PRAGMA_END _Pragma("GCC diagnostic pop"); +# endif +# ifdef MACOS +#include +# define HASH_CONTEXTPTR(CTXP) \ + unsigned char tmp[CC_MD4_DIGEST_LENGTH]; \ + CC_MD4_CTX CTXP ## _; \ + CC_MD4_CTX *CTXP = &CTXP ## _ +# define HASH_INIT(ctxp) !CC_MD4_Init(ctxp) +# define HASH_UPDATE(ctx, ptr, len) !CC_MD4_Update(ctx, ptr, len) +# define HASH_FINISH(ctxp) !CC_MD4_Final(tmp, ctxp) +# define HASH_RESULT_SIZE(ctxp) CC_MD4_DIGEST_LENGTH +# define HASH_RESULT(ctx, inp) *inp = (unsigned char *) ctx +# define HASH_CLEANUP(ctxp) +# define HASH_OFLAGS O_RDONLY +# define HASH_BINFILE_DECL char *binfile = argv[0]; +# ifdef BETA +# define HASH_BINFILE \ + if (!binfile || !*binfile) { \ + /* If this triggers, investigate CFBundleGetMainBundle */ \ + /* or CFBundleCopyExecutableURL. */ \ + raw_print( \ + "BETA warning: crashreport_init called without useful info"); \ + goto skip; \ + } +# else /* BETA */ +# define HASH_BINFILE() \ + if (!binfile || !*binfile) { \ + goto skip; \ + } +# endif /* BETA */ +# endif /* MACOS */ + +# ifdef __linux__ +#include "nhmd4.h" +# define HASH_CONTEXTPTR(CTXP) \ + unsigned char tmp[NHMD4_DIGEST_LENGTH]; \ + NHMD4_CTX CTXP ## _; \ + NHMD4_CTX *CTXP = &CTXP ## _ +# define HASH_INIT(ctxp) (nhmd4_init(ctxp), 0) +# define HASH_UPDATE(ctx, ptr, len) (nhmd4_update(ctx, ptr, len), 0) +# define HASH_FINISH(ctxp) (nhmd4_final(ctxp, tmp), 0) +# define HASH_RESULT_SIZE(ctxp) NHMD4_RESULTLEN +# define HASH_RESULT(ctx, inp) *inp = tmp +# define HASH_CLEANUP(ctxp) +# define HASH_OFLAGS O_RDONLY +# define HASH_BINFILE_DECL char binfile[PATH_MAX+1]; +# define HASH_BINFILE() \ + int len = readlink("/proc/self/exe", binfile, sizeof binfile - 1); \ + if (len > 0) { \ + binfile[len] = '\0'; \ + } else { \ + goto skip; \ + } +# endif // __linux__ + +# ifdef WIN32 +/* WIN32 takes too much code and is dependent on OS includes we can't + * pull in here, so we call out to code in sys/windows/windsys.c */ +# define HASH_CONTEXTPTR(CTXP) +# define HASH_INIT(ctxp) win32_cr_helper('i', ctxp, NULL, 0) +# define HASH_UPDATE(ctxp, ptr, len) win32_cr_helper('u', ctxp, ptr, len) +# define HASH_FINISH(ctxp) win32_cr_helper('f', ctxp, NULL, 0) +# define HASH_CLEANUP(ctxp) win32_cr_helper('c', ctxp, NULL, 0) +# define HASH_RESULT_SIZE(ctxp) win32_cr_helper('s', ctxp, NULL, 0) +# define HASH_RESULT(ctxp, inp) win32_cr_helper('r', ctxp, inp, 0) +# define HASH_OFLAGS _O_RDONLY | _O_BINARY +# define HASH_BINFILE_DECL char *binfile; +# define HASH_BINFILE() \ + if (win32_cr_helper('b', NULL, &binfile, 0)) { \ + goto skip; \ + } +# endif // WIN32 + +/* Binary ID - Use only as a hint to contact.html for recognizing our own + binaries. This is easily spoofed! */ +static char bid[40]; + +/* ARGSUSED */ +void +crashreport_init(int argc UNUSED, char *argv[] UNUSED) +{ + static int once = 0; + if (once++) /* NetHackW.exe calls us twice */ + return; + HASH_BINFILE_DECL; + HASH_PRAGMA_START + HASH_CONTEXTPTR(ctxp); + if (HASH_INIT(ctxp)) + goto skip; + HASH_BINFILE(); /* Does "goto skip" on error. */ + + int fd = open(binfile, HASH_OFLAGS, 0); + if (fd == -1) { +# ifdef BETA + raw_printf("open e=%s", strerror(errno)); +# endif + goto skip; + } + + int segsize; + unsigned char segment[4096]; + + while (0 < (segsize = read(fd, segment, sizeof segment))) { + if (HASH_UPDATE(ctxp, segment, segsize)) + goto skip; + } + if (segsize < 0) { + close(fd); + goto skip; + } + if (HASH_FINISH(ctxp)) + goto skip; + close(fd); + + static const char hex[] = "0123456789abcdef"; + char *p = bid; + unsigned char *in; + HASH_RESULT(ctxp, &in); + uint8 cnt = (uint8) HASH_RESULT_SIZE(ctxp); + /* Just in case, make sure not to overflow the bid buffer. + Divide size by 2 because each octet in the hash uses two slots + in bid[] when formatted as a pair of hexadecimal digits. */ + if (cnt >= (uint8) sizeof bid / 2) + cnt = (uint8) sizeof bid / 2 - 1; + while (cnt) { + /* sprintf(p, "%02x", *in++), p += 2; */ + *p++ = hex[(*in >> 4) & 0x0f]; + *p++ = hex[*in++ & 0x0f]; + --cnt; + } + *p = '\0'; + return; + + skip: + Strcpy(bid, "unknown"); + HASH_CLEANUP(ctxp); + HASH_PRAGMA_END +} + +#undef HASH_CONTEXTPTR +#undef HASH_INIT +#undef HASH_UPDATE +#undef HASH_FINISH +#undef HASH_CLEANUP +#undef HASH_RESULT +#undef HASH_RESULT_SIZE +#undef HASH_PRAGMA_START +#undef HASH_PRAGMA_END +#undef HASH_BINFILE_DECL +#undef HASH_BINFILE + +void +crashreport_bidshow(void) +{ +# if defined(WIN32) && !defined(WIN32CON) + if (0 == win32_cr_helper('D', ctxp, bid, 0)) +# endif + { + raw_print(bid); +# ifdef WIN32notyet + wait_synch(); +# endif + } +} + +/* Build a URL with a query string and try to launch a new browser window + * to report from panic() or impossible(). Requires libc support for + * the stacktrace. Uses memory on the stack to avoid memory allocation + * (on most platforms) (but libc can still do anything it wants). */ + +// No theoretial limit for URL length but reality is messy. +// This should work on all modern platforms. +# ifndef MAX_URL +# define MAX_URL 8192 +# endif +# ifndef SWR_FRAMES +# define SWR_FRAMES 20 +# endif + +// mark holds the initial eos; if we can't get the value in +// then we can remove the whole item if desired. For other +// semantics, caller can handle mark. +#define SWR_ADD(str) \ + utmp = strlen(str); \ + mark = uend; \ + if (utmp >= urem) \ + goto full; \ + strncpy(uend, str, utmp); \ + uend += utmp; urem -= utmp; \ + *uend = '\0'; + +// NB: on overflow this rolls us back to mark, so if we don't +// want to roll back to the last SWR_ADD, update mark before +// calling this macro. +#define SWR_ADD_URIcoded(str) \ + if (swr_add_uricoded(str, &uend, &urem, mark)) \ + goto full; + +/* On overflow, truncate to markp (but only if markp != NULL). */ +boolean +swr_add_uricoded( + const char *in, + char **out, + int *remaining, + char *markp) +{ + while (*in) { + if (isalnum(*in) || strchr("_-.~", *in)) { + **out = *in; + (*out)++; + (*remaining)--; + } else if (*in == ' ') { + **out = '+'; + (*out)++; + (*remaining)--; + } else { + if (*remaining <= 3) { + if (markp) + *out = markp, *remaining = 0; + **out = '\0'; + return TRUE; + } + int x = snprintf(*out, *remaining, "%%%02X", *in); + *out += x; + *remaining -= x; + } + in++; + if (!*remaining) { + if (markp) + *out = markp, *remaining = 0; + **out = '\0'; + return TRUE; + } + **out = '\0'; + } + return FALSE; /* normal return */ +} + +static char url[MAX_URL]; // XXX too bad this isn't allocated as needed +static int urem = MAX_URL; // adjusted for gc.crash_urlmax below +static char *uend = url; +static int utmp; // used inside macros +static char *mark; // holds previous terminator (generally) + +boolean +submit_web_report(int cos, const char *msg, const char *why) +{ + urem = (gc.crash_urlmax < 0 || gc.crash_urlmax > MAX_URL) + ? MAX_URL : min(MAX_URL,gc.crash_urlmax); + char temp[200]; + char temp2[200]; + int countpp = 0; /* pre and post traceback lines */ +// URL loaded for creating reports to the NetHack DevTeam +// CRASHREPORTURL=https://nethack.org/links/cr-37BETA.html + if (!sysopt.crashreporturl) + return FALSE; + SWR_ADD(sysopt.crashreporturl); + /* cos - operation, v - version */ + Snprintf(temp, sizeof temp, "?cos=%d&v=1", cos); + SWR_ADD(temp); + + /* msg==NULL for #bugreport */ + if (msg) { + SWR_ADD("&subject="); + Snprintf(temp, sizeof temp, "%s report for NetHack %s", + msg, version_string(temp2, sizeof temp2 )); + SWR_ADD_URIcoded(temp); + } + + SWR_ADD("&gitver="); + SWR_ADD_URIcoded(getversionstring(temp2, sizeof temp2)); + + if (gc.crash_name) { + SWR_ADD("&name="); + SWR_ADD_URIcoded(gc.crash_name); + } + + if(gc.crash_email) { + SWR_ADD("&email="); + SWR_ADD_URIcoded(gc.crash_email); + } + // hardware: leave for user + // software: leave for user + // comments: leave for user + + SWR_ADD("&details="); + if (why) { + SWR_ADD_URIcoded(why); + SWR_ADD_URIcoded("\n"); + mark = uend; + countpp++; + } + + SWR_ADD_URIcoded("bid: "); + SWR_ADD_URIcoded(bid); + SWR_ADD_URIcoded("\n"); + mark = uend; + countpp++; + + int count = 0; + if (cos == 1) { +# ifdef WIN32 + count = win32_cr_gettrace(SWR_FRAMES, uend, MAX_URL - (uend - url)); + uend = eos(url); +# else + void *bt[SWR_FRAMES]; + int x; + char **info; + + count = backtrace(bt, SIZE(bt)); + info = backtrace_symbols(bt, count); + for (x = 0; x < count; x++) { + copynchars(temp, info[x], (int) sizeof temp - 1 - 1); /* \n\0 */ + /* try to remove up to 16 blank spaces by removing 8 twice */ + (void) strsubst(temp, " ", ""); + (void) strsubst(temp, " ", ""); + (void) strncat(temp, "\n", sizeof temp - 1); +# if 0 // __linux__ +// not needed for MacOS +// XXX is it actually needed for linux? TBD + Snprintf(temp2, sizeof temp2, "[%02lu]\n", (unsigned long) x); + uend--; // remove the \n we added above + SWR_ADD_URIcoded(temp2); +# endif // linux + SWR_ADD_URIcoded(temp); + mark = uend; + } +# endif // !WIN32 + } + +# ifdef DUMPLOG_CORE + // config.h turns this on, but make it easy to turn off if needed + if (cos == 1) { + int k; + SWR_ADD_URIcoded("Latest messages:\n"); + mark=uend; + countpp++; + for (k = 0; k < 5; k++) { + const char *line = get_saved_pline(k); + if (!line) + break; + SWR_ADD_URIcoded(line); + SWR_ADD_URIcoded("\n"); + countpp++; + mark = uend; + } + } +# endif /* DUMPLOG_CORE */ + + // detailrows: Guess since we can't know the + // width of the window. + SWR_ADD("&detailrows="); + Snprintf(temp, sizeof temp, "%d", min(count + countpp, 30)); + SWR_ADD_URIcoded(temp); + + full: + ; +//printf("URL=%ld '%s'\n",strlen(url),url); +# ifdef WIN32 + int *rv = win32_cr_shellexecute(url); +// XXX TESTING +printf("ShellExecute returned: %p\n",rv); // >32 is ok +# else /* !WIN32 */ + const char *xargv[] = { + CRASHREPORT, + url, + NULL + }; + int pid = fork(); + extern char **environ; + if (pid == 0) { + char err[100]; +# ifdef CRASHREPORT_EXEC_NOSTDERR + /* Keep the output clean - firefox spews useless errors on + * my system. */ + (void) close(2); + (void) open("/dev/null", O_WRONLY); +# endif + + (void) execve(CRASHREPORT, (char * const *) xargv, environ); + Sprintf(err, "Can't start " CRASHREPORT ": %s", strerror(errno)); + raw_print(err); + } else { + int status; + errno = 0; + (void) waitpid(pid, &status, 0); + if (status) { /* XXX check could be more precise */ +# ifdef BETA + /* Not useful at the moment. XXX */ + char err[100]; + + Sprintf(err, "pid=%d e=%d status=%0x", wpid, errno, status); + raw_print(err); +# endif + return FALSE; + } + } + /* free(info); -- Don't risk it. */ +# endif /* !WIN32 */ + return TRUE; +} + +int +dobugreport(void) +{ + if (!submit_web_report(2, NULL, "#bugreport command")) { + pline("Unable to send bug report. Please visit %s instead.", + (sysopt.crashreporturl && *sysopt.crashreporturl) + ? sysopt.crashreporturl + : "https://www.nethack.org" + ); + } + return ECMD_OK; +} + +#undef SWR_ADD +#undef SWR_ADD_URIcoded +#undef SWR_FRAMES +#undef SWR_HDR +#undef SWR_LINES + +/*ARGSUSED*/ +boolean +NH_panictrace_libc(void) +{ +# if 0 /* XXX how did this get left here? */ + if (submit_web_report("Panic", why)) + return TRUE; +# endif + +# ifdef PANICTRACE_LIBC + void *bt[20]; + int count, x; + char **info, buf[BUFSZ]; + + raw_print(" Generating more information you may report:\n"); + count = backtrace(bt, SIZE(bt)); + info = backtrace_symbols(bt, count); + for (x = 0; x < count; x++) { + copynchars(buf, info[x], (int) sizeof buf - 1); + /* try to remove up to 16 blank spaces by removing 8 twice */ + (void) strsubst(buf, " ", ""); + (void) strsubst(buf, " ", ""); + raw_printf("[%02lu] %s", (unsigned long) x, buf); + } + /* free(info); -- Don't risk it. */ + return TRUE; +# else + return FALSE; +# endif /* !PANICTRACE_LIBC */ +} + +/* + * fooPATH file system path for foo + * fooVAR (possibly const) variable containing fooPATH + */ +# ifdef PANICTRACE_GDB +# ifdef SYSCF +# define GDBVAR sysopt.gdbpath +# define GREPVAR sysopt.greppath +# else /* SYSCF */ +# define GDBVAR GDBPATH +# define GREPVAR GREPPATH +# endif /* SYSCF */ +# endif /* PANICTRACE_GDB */ + +boolean +NH_panictrace_gdb(void) +{ +# ifdef PANICTRACE_GDB + /* A (more) generic method to get a stack trace - invoke + * gdb on ourself. */ + const char *gdbpath = GDBVAR; + const char *greppath = GREPVAR; + char buf[BUFSZ]; + FILE *gdb; + + if (gdbpath == NULL || gdbpath[0] == 0) + return FALSE; + if (greppath == NULL || greppath[0] == 0) + return FALSE; + + sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", + gdbpath, ARGV0, getpid(), greppath); + gdb = popen(buf, "w"); + if (gdb) { + raw_print(" Generating more information you may report:\n"); + fprintf(gdb, "bt\nquit\ny"); + fflush(gdb); + sleep(4); /* ugly */ + pclose(gdb); + return TRUE; + } else { + return FALSE; + } +# else + return FALSE; +# endif /* !PANICTRACE_GDB */ +} + +/* lineno==0 gives the most recent message (e.g. + "Do you want to call panic..." if called from #panic) */ +const char * +get_saved_pline(int lineno) +{ + int p; + int limit = DUMPLOG_MSG_COUNT; + if (lineno >= DUMPLOG_MSG_COUNT) + return NULL; + p = (gs.saved_pline_index - 1) % DUMPLOG_MSG_COUNT; + + while (limit--) { + if (gs.saved_plines[p]) { /* valid line */ + if (lineno--) { + p = (p - 1 + DUMPLOG_MSG_COUNT) % DUMPLOG_MSG_COUNT; + } else { + return gs.saved_plines[p]; + } + } + } + return NULL; +} + +#endif /* CRASHREPORT */ + + /*report.c*/ diff --git a/src/rnd.c b/src/rnd.c index 410f85687..6c9854c0b 100644 --- a/src/rnd.c +++ b/src/rnd.c @@ -227,4 +227,85 @@ rnz(int i) return (int) x; } +/* Sets the seed for the random number generator */ +#ifdef USE_ISAAC64 + +static void +set_random(unsigned long seed, + int (*fn)(int)) +{ + init_isaac64(seed, fn); +} + +#else /* USE_ISAAC64 */ + +/*ARGSUSED*/ +static void +set_random(unsigned long seed, + int (*fn)(int) UNUSED) +{ + /* + * The types are different enough here that sweeping the different + * routine names into one via #defines is even more confusing. + */ +# ifdef RANDOM /* srandom() from sys/share/random.c */ + srandom((unsigned int) seed); +# else +# if defined(__APPLE__) || defined(BSD) || defined(LINUX) \ + || defined(ULTRIX) || defined(CYGWIN32) /* system srandom() */ +# if defined(BSD) && !defined(POSIX_TYPES) && defined(SUNOS4) + (void) +# endif + srandom((int) seed); +# else +# ifdef UNIX /* system srand48() */ + srand48((long) seed); +# else /* poor quality system routine */ + srand((int) seed); +# endif +# endif +# endif +} +#endif /* USE_ISAAC64 */ + +/* An appropriate version of this must always be provided in + port-specific code somewhere. It returns a number suitable + as seed for the random number generator */ +extern unsigned long sys_random_seed(void); + +/* + * Initializes the random number generator. + * Only call once. + */ +void +init_random(int (*fn)(int)) +{ + set_random(sys_random_seed(), fn); +} + +/* Reshuffles the random number generator. */ +void +reseed_random(int (*fn)(int)) +{ + /* only reseed if we are certain that the seed generation is unguessable + * by the players. */ + if (has_strong_rngseed) + init_random(fn); +} + +/* randomize the given list of numbers 0 <= i < count */ +void +shuffle_int_array(int *indices, int count) +{ + int i, iswap, temp; + + for (i = count - 1; i > 0; i--) { + if ((iswap = rn2(i + 1)) == i) + continue; + temp = indices[i]; + indices[i] = indices[iswap]; + indices[iswap] = temp; + } +} + /*rnd.c*/ diff --git a/src/selvar.c b/src/selvar.c new file mode 100644 index 000000000..eeacabf42 --- /dev/null +++ b/src/selvar.c @@ -0,0 +1,802 @@ +/* NetHack 3.7 selvar.c $NHDT-Date: 1709677544 2024/03/05 22:25:44 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.360 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "selvar.h" +#include "sp_lev.h" + +static boolean sel_flood_havepoint(coordxy, coordxy, coordxy *, coordxy *, int); +static long line_dist_coord(long, long, long, long, long, long); + +/* selection */ +struct selectionvar * +selection_new(void) +{ + struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); + + tmps->wid = COLNO; + tmps->hei = ROWNO; + tmps->bounds_dirty = FALSE; + tmps->bounds.lx = COLNO; + tmps->bounds.ly = ROWNO; + tmps->bounds.hx = tmps->bounds.hy = 0; + tmps->map = (char *) alloc((COLNO * ROWNO) + 1); + (void) memset(tmps->map, 1, (COLNO * ROWNO)); + tmps->map[(COLNO * ROWNO)] = '\0'; + + return tmps; +} + +void +selection_free(struct selectionvar *sel, boolean freesel) +{ + if (sel) { + if (sel->map) + free(sel->map); + sel->map = NULL; + if (freesel) + free((genericptr_t) sel); + else + (void) memset((genericptr_t) sel, 0, sizeof *sel); + } +} + +/* clear selection, setting all locations to value val */ +void +selection_clear(struct selectionvar *sel, int val) +{ + (void) memset(sel->map, 1 + val, (COLNO * ROWNO)); + if (val) { + sel->bounds.lx = 0; + sel->bounds.ly = 0; + sel->bounds.hx = COLNO - 1; + sel->bounds.hy = ROWNO - 1; + } else { + sel->bounds.lx = COLNO; + sel->bounds.ly = ROWNO; + sel->bounds.hx = sel->bounds.hy = 0; + } + sel->bounds_dirty = FALSE; +} + +struct selectionvar * +selection_clone(struct selectionvar *sel) +{ + struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); + + *tmps = *sel; + tmps->map = dupstr(sel->map); + + return tmps; +} + +/* get boundary rect of selection sel into b */ +void +selection_getbounds(struct selectionvar *sel, NhRect *b) +{ + if (!sel || !b) + return; + + selection_recalc_bounds(sel); + + if (sel->bounds.lx >= sel->wid) { + b->lx = 0; + b->ly = 0; + b->hx = COLNO - 1; + b->hy = ROWNO - 1; + } else { + b->lx = sel->bounds.lx; + b->ly = sel->bounds.ly; + b->hx = sel->bounds.hx; + b->hy = sel->bounds.hy; + } +} + +/* recalc the boundary of selection, if necessary */ +void +selection_recalc_bounds(struct selectionvar *sel) +{ + coordxy x, y; + NhRect r; + + if (!sel->bounds_dirty) + return; + + sel->bounds.lx = COLNO; + sel->bounds.ly = ROWNO; + sel->bounds.hx = sel->bounds.hy = 0; + + r.lx = r.ly = r.hx = r.hy = -1; + + /* left */ + for (x = 0; x < sel->wid; x++) { + for (y = 0; y < sel->hei; y++) { + if (selection_getpoint(x, y, sel)) { + r.lx = x; + break; + } + } + if (r.lx > -1) + break; + } + + if (r.lx > -1) { + /* right */ + for (x = sel->wid-1; x >= r.lx; x--) { + for (y = 0; y < sel->hei; y++) { + if (selection_getpoint(x, y, sel)) { + r.hx = x; + break; + } + } + if (r.hx > -1) + break; + } + + /* top */ + for (y = 0; y < sel->hei; y++) { + for (x = r.lx; x <= r.hx; x++) { + if (selection_getpoint(x, y, sel)) { + r.ly = y; + break; + } + } + if (r.ly > -1) + break; + } + + /* bottom */ + for (y = sel->hei-1; y >= r.ly; y--) { + for (x = r.lx; x <= r.hx; x++) { + if (selection_getpoint(x, y, sel)) { + r.hy = y; + break; + } + } + if (r.hy > -1) + break; + } + sel->bounds = r; + } + + sel->bounds_dirty = FALSE; +} + +coordxy +selection_getpoint( + coordxy x, coordxy y, + struct selectionvar *sel) +{ + if (!sel || !sel->map) + return 0; + if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) + return 0; + + return (sel->map[sel->wid * y + x] - 1); +} + +void +selection_setpoint( + coordxy x, coordxy y, + struct selectionvar *sel, + int c) +{ + if (!sel || !sel->map) + return; + if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) + return; + + if (c && !sel->bounds_dirty) { + if (sel->bounds.lx > x) sel->bounds.lx = x; + if (sel->bounds.ly > y) sel->bounds.ly = y; + if (sel->bounds.hx < x) sel->bounds.hx = x; + if (sel->bounds.hy < y) sel->bounds.hy = y; + } else { + sel->bounds_dirty = TRUE; + } + + sel->map[sel->wid * y + x] = (char) (c + 1); +} + +struct selectionvar * +selection_not(struct selectionvar *s) +{ + int x, y; + NhRect tmprect = cg.zeroNhRect; + + for (x = 0; x < s->wid; x++) + for (y = 0; y < s->hei; y++) + selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1); + selection_getbounds(s, &tmprect); + return s; +} + +struct selectionvar * +selection_filter_percent( + struct selectionvar *ov, + int percent) +{ + int x, y; + struct selectionvar *ret; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return NULL; + + ret = selection_new(); + + selection_getbounds(ov, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (selection_getpoint(x, y, ov) && (rn2(100) < percent)) + selection_setpoint(x, y, ret, 1); + + return ret; +} + +struct selectionvar * +selection_filter_mapchar(struct selectionvar *ov, xint16 typ, int lit) +{ + int x, y; + struct selectionvar *ret; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return NULL; + + ret = selection_new(); + + selection_getbounds(ov, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (selection_getpoint(x, y, ov) + && match_maptyps(typ, levl[x][y].typ)) { + switch (lit) { + default: + case -2: + selection_setpoint(x, y, ret, 1); + break; + case -1: + selection_setpoint(x, y, ret, rn2(2)); + break; + case 0: + case 1: + if (levl[x][y].lit == (unsigned int) lit) + selection_setpoint(x, y, ret, 1); + break; + } + } + return ret; +} + +int +selection_rndcoord( + struct selectionvar *ov, + coordxy *x, coordxy *y, + boolean removeit) +{ + int idx = 0; + int c; + int dx, dy; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(ov, &rect); + + for (dx = rect.lx; dx <= rect.hx; dx++) + for (dy = rect.ly; dy <= rect.hy; dy++) + if (selection_getpoint(dx, dy, ov)) + idx++; + + if (idx) { + c = rn2(idx); + for (dx = rect.lx; dx <= rect.hx; dx++) + for (dy = rect.ly; dy <= rect.hy; dy++) + if (selection_getpoint(dx, dy, ov)) { + if (!c) { + *x = dx; + *y = dy; + if (removeit) + selection_setpoint(dx, dy, ov, 0); + return 1; + } + c--; + } + } + *x = *y = -1; + return 0; +} + +void +selection_do_grow(struct selectionvar *ov, int dir) +{ + coordxy x, y; + struct selectionvar *tmp; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return; + + tmp = selection_new(); + + if (dir == W_RANDOM) + dir = random_wdir(); + + selection_getbounds(ov, &rect); + + for (x = max(0, rect.lx-1); x <= min(COLNO-1, rect.hx+1); x++) + for (y = max(0, rect.ly-1); y <= min(ROWNO-1, rect.hy+1); y++) { + /* note: dir is a mask of multiple directions, but the only + way to specify diagonals is by including the two adjacent + orthogonal directions, which effectively specifies three- + way growth [WEST|NORTH => WEST plus WEST|NORTH plus NORTH] */ + if (((dir & W_WEST) && selection_getpoint(x + 1, y, ov)) + || (((dir & (W_WEST | W_NORTH)) == (W_WEST | W_NORTH)) + && selection_getpoint(x + 1, y + 1, ov)) + || ((dir & W_NORTH) && selection_getpoint(x, y + 1, ov)) + || (((dir & (W_NORTH | W_EAST)) == (W_NORTH | W_EAST)) + && selection_getpoint(x - 1, y + 1, ov)) + || ((dir & W_EAST) && selection_getpoint(x - 1, y, ov)) + || (((dir & (W_EAST | W_SOUTH)) == (W_EAST | W_SOUTH)) + && selection_getpoint(x - 1, y - 1, ov)) + || ((dir & W_SOUTH) && selection_getpoint(x, y - 1, ov)) + || (((dir & (W_SOUTH | W_WEST)) == (W_SOUTH | W_WEST)) + && selection_getpoint(x + 1, y - 1, ov))) { + selection_setpoint(x, y, tmp, 1); + } + } + + selection_getbounds(tmp, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (selection_getpoint(x, y, tmp)) + selection_setpoint(x, y, ov, 1); + + selection_free(tmp, TRUE); +} + +static int (*selection_flood_check_func)(coordxy, coordxy); + +void +set_selection_floodfillchk(int (*f)(coordxy, coordxy)) +{ + selection_flood_check_func = f; +} + +/* check whethere is already in xs[],ys[] */ +static boolean +sel_flood_havepoint( + coordxy x, coordxy y, + coordxy xs[], coordxy ys[], + int n) +{ + coordxy xx = x, yy = y; + + while (n > 0) { + --n; + if (xs[n] == xx && ys[n] == yy) + return TRUE; + } + return FALSE; +} + +void +selection_floodfill( + struct selectionvar *ov, + coordxy x, coordxy y, + boolean diagonals) +{ + struct selectionvar *tmp = selection_new(); +#define SEL_FLOOD_STACK (COLNO * ROWNO) +#define SEL_FLOOD(nx, ny) \ + do { \ + if (idx < SEL_FLOOD_STACK) { \ + dx[idx] = (nx); \ + dy[idx] = (ny); \ + idx++; \ + } else \ + panic(floodfill_stack_overrun); \ + } while (0) +#define SEL_FLOOD_CHKDIR(mx, my, sel) \ + do { \ + if (isok((mx), (my)) \ + && (*selection_flood_check_func)((mx), (my)) \ + && !selection_getpoint((mx), (my), (sel)) \ + && !sel_flood_havepoint((mx), (my), dx, dy, idx)) \ + SEL_FLOOD((mx), (my)); \ + } while (0) + static const char floodfill_stack_overrun[] = "floodfill stack overrun"; + int idx = 0; + coordxy dx[SEL_FLOOD_STACK]; + coordxy dy[SEL_FLOOD_STACK]; + + if (selection_flood_check_func == (int (*)(coordxy, coordxy)) 0) { + selection_free(tmp, TRUE); + return; + } + SEL_FLOOD(x, y); + do { + idx--; + x = dx[idx]; + y = dy[idx]; + if (isok(x, y)) { + selection_setpoint(x, y, ov, 1); + selection_setpoint(x, y, tmp, 1); + } + SEL_FLOOD_CHKDIR((x + 1), y, tmp); + SEL_FLOOD_CHKDIR((x - 1), y, tmp); + SEL_FLOOD_CHKDIR(x, (y + 1), tmp); + SEL_FLOOD_CHKDIR(x, (y - 1), tmp); + if (diagonals) { + SEL_FLOOD_CHKDIR((x + 1), (y + 1), tmp); + SEL_FLOOD_CHKDIR((x - 1), (y - 1), tmp); + SEL_FLOOD_CHKDIR((x - 1), (y + 1), tmp); + SEL_FLOOD_CHKDIR((x + 1), (y - 1), tmp); + } + } while (idx > 0); +#undef SEL_FLOOD +#undef SEL_FLOOD_STACK +#undef SEL_FLOOD_CHKDIR + selection_free(tmp, TRUE); +} + +/* McIlroy's Ellipse Algorithm */ +void +selection_do_ellipse( + struct selectionvar *ov, + int xc, int yc, + int a, int b, + int filled) +{ /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */ + int x = 0, y = b; + long a2 = (long) a * a, b2 = (long) b * b; + long crit1 = -(a2 / 4 + a % 2 + b2); + long crit2 = -(b2 / 4 + b % 2 + a2); + long crit3 = -(b2 / 4 + b % 2); + long t = -a2 * y; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */ + long dxt = 2 * b2 * x, dyt = -2 * a2 * y; + long d2xt = 2 * b2, d2yt = 2 * a2; + long width = 1; + long i; + + if (!ov) + return; + + filled = !filled; + + if (!filled) { + while (y >= 0 && x <= a) { + selection_setpoint(xc + x, yc + y, ov, 1); + if (x != 0 || y != 0) + selection_setpoint(xc - x, yc - y, ov, 1); + if (x != 0 && y != 0) { + selection_setpoint(xc + x, yc - y, ov, 1); + selection_setpoint(xc - x, yc + y, ov, 1); + } + if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ + || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ + x++; + dxt += d2xt; + t += dxt; + } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ + y--; + dyt += d2yt; + t += dyt; + } else { + x++; + dxt += d2xt; + t += dxt; + y--; + dyt += d2yt; + t += dyt; + } + } + } else { + while (y >= 0 && x <= a) { + if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ + || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ + x++; + dxt += d2xt; + t += dxt; + width += 2; + } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc - y, ov, 1); + if (y != 0) + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc + y, ov, 1); + y--; + dyt += d2yt; + t += dyt; + } else { + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc - y, ov, 1); + if (y != 0) + for (i = 0; i < width; i++) + selection_setpoint(xc - x + i, yc + y, ov, 1); + x++; + dxt += d2xt; + t += dxt; + y--; + dyt += d2yt; + t += dyt; + width += 2; + } + } + } +} + +/* square of distance from line segment (x1,y1, x2,y2) to point (x3,y3) */ +static long +line_dist_coord(long x1, long y1, long x2, long y2, long x3, long y3) +{ + long px = x2 - x1; + long py = y2 - y1; + long s = px * px + py * py; + long x, y, dx, dy, distsq = 0; + float lu = 0; + + if (x1 == x2 && y1 == y2) + return dist2(x1, y1, x3, y3); + + lu = ((x3 - x1) * px + (y3 - y1) * py) / (float) s; + if (lu > 1) + lu = 1; + else if (lu < 0) + lu = 0; + + x = x1 + lu * px; + y = y1 + lu * py; + dx = x - x3; + dy = y - y3; + distsq = dx * dx + dy * dy; + + return distsq; +} + +/* guts of l_selection_gradient */ +void +selection_do_gradient( + struct selectionvar *ov, + long x, long y, + long x2,long y2, + long gtyp, + long mind, long maxd) +{ + long dx, dy, dofs; + + if (mind > maxd) { + long tmp = mind; + mind = maxd; + maxd = tmp; + } + + dofs = maxd * maxd - mind * mind; + if (dofs < 1) + dofs = 1; + + switch (gtyp) { + default: + impossible("Unrecognized gradient type! Defaulting to radial..."); + /* FALLTHRU */ + case SEL_GRADIENT_RADIAL: { + for (dx = 0; dx < COLNO; dx++) + for (dy = 0; dy < ROWNO; dy++) { + long d0 = line_dist_coord(x, y, x2, y2, dx, dy); + + if (d0 <= mind * mind + || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) + selection_setpoint(dx, dy, ov, 1); + } + break; + } + case SEL_GRADIENT_SQUARE: { + for (dx = 0; dx < COLNO; dx++) + for (dy = 0; dy < ROWNO; dy++) { + long d1 = line_dist_coord(x, y, x2, y2, x, dy); + long d2 = line_dist_coord(x, y, x2, y2, dx, y); + long d3 = line_dist_coord(x, y, x2, y2, x2, dy); + long d4 = line_dist_coord(x, y, x2, y2, dx, y2); + long d5 = line_dist_coord(x, y, x2, y2, dx, dy); + long d0 = min(d5, min(max(d1, d2), max(d3, d4))); + + if (d0 <= mind * mind + || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) + selection_setpoint(dx, dy, ov, 1); + } + break; + } /*case*/ + } /*switch*/ +} + +/* bresenham line algo */ +void +selection_do_line( + coordxy x1, coordxy y1, + coordxy x2, coordxy y2, + struct selectionvar *ov) +{ + int d0, dx, dy, ai, bi, xi, yi; + + if (x1 < x2) { + xi = 1; + dx = x2 - x1; + } else { + xi = -1; + dx = x1 - x2; + } + if (y1 < y2) { + yi = 1; + dy = y2 - y1; + } else { + yi = -1; + dy = y1 - y2; + } + + selection_setpoint(x1, y1, ov, 1); + + if (!dx && !dy) { + /* single point - already all done */ + ; + } else if (dx > dy) { + ai = (dy - dx) * 2; + bi = dy * 2; + d0 = bi - dx; + do { + if (d0 >= 0) { + y1 += yi; + d0 += ai; + } else + d0 += bi; + x1 += xi; + selection_setpoint(x1, y1, ov, 1); + } while (x1 != x2); + } else { + ai = (dx - dy) * 2; + bi = dx * 2; + d0 = bi - dy; + do { + if (d0 >= 0) { + x1 += xi; + d0 += ai; + } else + d0 += bi; + y1 += yi; + selection_setpoint(x1, y1, ov, 1); + } while (y1 != y2); + } +} + +void +selection_do_randline( + coordxy x1, coordxy y1, + coordxy x2, coordxy y2, + schar rough, + schar rec, + struct selectionvar *ov) +{ + int mx, my; + int dx, dy; + + if (rec < 1 || (x2 == x1 && y2 == y1)) + return; + + if (rough > max(abs(x2 - x1), abs(y2 - y1))) + rough = max(abs(x2 - x1), abs(y2 - y1)); + + if (rough < 2) { + mx = ((x1 + x2) / 2); + my = ((y1 + y2) / 2); + } else { + do { + dx = rn2(rough) - (rough / 2); + dy = rn2(rough) - (rough / 2); + mx = ((x1 + x2) / 2) + dx; + my = ((y1 + y2) / 2) + dy; + } while ((mx > COLNO - 1 || mx < 0 || my < 0 || my > ROWNO - 1)); + } + + if (!selection_getpoint(mx, my, ov)) { + selection_setpoint(mx, my, ov, 1); + } + + rough = (rough * 2) / 3; + + rec--; + + selection_do_randline(x1, y1, mx, my, rough, rec, ov); + selection_do_randline(mx, my, x2, y2, rough, rec, ov); + + selection_setpoint(x2, y2, ov, 1); +} + +void +selection_iterate( + struct selectionvar *ov, + select_iter_func func, + genericptr_t arg) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + if (!ov) + return; + + selection_getbounds(ov, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && selection_getpoint(x, y, ov)) + (*func)(x, y, arg); +} + +/* selection is not rectangular, or has holes in it */ +boolean +selection_is_irregular(struct selectionvar *sel) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(sel, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && !selection_getpoint(x, y, sel)) + return TRUE; + + return FALSE; +} + +/* return a description of the selection size */ +char * +selection_size_description(struct selectionvar *sel, char *buf) +{ + NhRect rect = cg.zeroNhRect; + coordxy dx, dy; + + selection_getbounds(sel, &rect); + dx = rect.hx - rect.lx + 1; + dy = rect.hy - rect.ly + 1; + Sprintf(buf, "%s %i by %i", selection_is_irregular(sel) ? "irregularly shaped" + : (dx == dy) ? "square" + : "rectangular", + dx, dy); + return buf; +} + +struct selectionvar * +selection_from_mkroom(struct mkroom *croom) +{ + struct selectionvar *sel = selection_new(); + coordxy x, y; + unsigned rmno; + + if (!croom && gc.coder && gc.coder->croom) + croom = gc.coder->croom; + if (!croom) + return sel; + + rmno = (unsigned)((croom - gr.rooms) + ROOMOFFSET); + for (y = croom->ly; y <= croom->hy; y++) + for (x = croom->lx; x <= croom->hx; x++) + if (isok(x, y) && !levl[x][y].edge + && levl[x][y].roomno == rmno) + selection_setpoint(x, y, sel, 1); + return sel; +} + +void +selection_force_newsyms(struct selectionvar *sel) +{ + coordxy x, y; + + for (x = 1; x < sel->wid; x++) + for (y = 0; y < sel->hei; y++) + if (selection_getpoint(x, y, sel)) + newsym_force(x, y); +} + +/*selvar.c*/ diff --git a/src/sp_lev.c b/src/sp_lev.c index 973598791..3c21d06c6 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -14,11 +14,8 @@ #include "hack.h" #include "sp_lev.h" -typedef void (*select_iter_func)(coordxy, coordxy, genericptr); - extern void mkmap(lev_init *); -static boolean match_maptyps(xint16, xint16); static void solidify_map(void); static void map_cleanup(void); static void lvlfill_maze_grid(int, int, int, int, schar); @@ -101,8 +98,6 @@ static void sel_set_lit(coordxy, coordxy, genericptr_t); static void add_doors_to_room(struct mkroom *); static void get_table_coords_or_region(lua_State *, coordxy *, coordxy *, coordxy *, coordxy *); -static void selection_iterate(struct selectionvar *, select_iter_func, - genericptr_t); static void sel_set_ter(coordxy, coordxy, genericptr_t); static void sel_set_door(coordxy, coordxy, genericptr_t); static void sel_set_feature(coordxy, coordxy, genericptr_t); @@ -111,11 +106,8 @@ static void get_table_xy_or_coord(lua_State *, lua_Integer *, lua_Integer *); static int get_table_region(lua_State *, const char *, lua_Integer *, lua_Integer *, lua_Integer *, lua_Integer *, boolean); static void set_wallprop_in_selection(lua_State *, int); -static coordxy random_wdir(void); static int floodfillchk_match_under(coordxy, coordxy); static int floodfillchk_match_accessible(coordxy, coordxy); -static boolean sel_flood_havepoint(coordxy, coordxy, coordxy *, coordxy *, int); -static long line_dist_coord(long, long, long, long, long, long); static void l_push_mkroom_table(lua_State *, struct mkroom *); static int get_table_align(lua_State *); static int get_table_monclass(lua_State *); @@ -221,7 +213,7 @@ reset_xystart_size(void) /* Does typ match with levl[][].typ, considering special types MATCH_WALL and MAX_TYPE (aka transparency)? */ -static boolean +boolean match_maptyps(xint16 typ, xint16 levltyp) { if ((typ == MATCH_WALL) && !IS_STWALL(levltyp)) @@ -4484,371 +4476,16 @@ lspo_random_corridors(lua_State *L UNUSED) return 0; } -/* selection */ -struct selectionvar * -selection_new(void) -{ - struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); - - tmps->wid = COLNO; - tmps->hei = ROWNO; - tmps->bounds_dirty = FALSE; - tmps->bounds.lx = COLNO; - tmps->bounds.ly = ROWNO; - tmps->bounds.hx = tmps->bounds.hy = 0; - tmps->map = (char *) alloc((COLNO * ROWNO) + 1); - (void) memset(tmps->map, 1, (COLNO * ROWNO)); - tmps->map[(COLNO * ROWNO)] = '\0'; - - return tmps; -} - -void -selection_free(struct selectionvar *sel, boolean freesel) -{ - if (sel) { - Free(sel->map); - sel->map = NULL; - if (freesel) - free((genericptr_t) sel); - else - (void) memset((genericptr_t) sel, 0, sizeof *sel); - } -} - -/* clear selection, setting all locations to value val */ -void -selection_clear(struct selectionvar *sel, int val) -{ - (void) memset(sel->map, 1 + val, (COLNO * ROWNO)); - if (val) { - sel->bounds.lx = 0; - sel->bounds.ly = 0; - sel->bounds.hx = COLNO - 1; - sel->bounds.hy = ROWNO - 1; - } else { - sel->bounds.lx = COLNO; - sel->bounds.ly = ROWNO; - sel->bounds.hx = sel->bounds.hy = 0; - } - sel->bounds_dirty = FALSE; -} - -struct selectionvar * -selection_clone(struct selectionvar *sel) -{ - struct selectionvar *tmps = (struct selectionvar *) alloc(sizeof *tmps); - - *tmps = *sel; - tmps->map = dupstr(sel->map); - - return tmps; -} - -/* get boundary rect of selection sel into b */ -void -selection_getbounds(struct selectionvar *sel, NhRect *b) -{ - if (!sel || !b) - return; - - selection_recalc_bounds(sel); - - if (sel->bounds.lx >= sel->wid) { - b->lx = 0; - b->ly = 0; - b->hx = COLNO - 1; - b->hy = ROWNO - 1; - } else { - b->lx = sel->bounds.lx; - b->ly = sel->bounds.ly; - b->hx = sel->bounds.hx; - b->hy = sel->bounds.hy; - } -} - -/* recalc the boundary of selection, if necessary */ -void -selection_recalc_bounds(struct selectionvar *sel) -{ - coordxy x, y; - NhRect r; - - if (!sel->bounds_dirty) - return; - - sel->bounds.lx = COLNO; - sel->bounds.ly = ROWNO; - sel->bounds.hx = sel->bounds.hy = 0; - - r.lx = r.ly = r.hx = r.hy = -1; - - /* left */ - for (x = 0; x < sel->wid; x++) { - for (y = 0; y < sel->hei; y++) { - if (selection_getpoint(x, y, sel)) { - r.lx = x; - break; - } - } - if (r.lx > -1) - break; - } - - if (r.lx > -1) { - /* right */ - for (x = sel->wid-1; x >= r.lx; x--) { - for (y = 0; y < sel->hei; y++) { - if (selection_getpoint(x, y, sel)) { - r.hx = x; - break; - } - } - if (r.hx > -1) - break; - } - - /* top */ - for (y = 0; y < sel->hei; y++) { - for (x = r.lx; x <= r.hx; x++) { - if (selection_getpoint(x, y, sel)) { - r.ly = y; - break; - } - } - if (r.ly > -1) - break; - } - - /* bottom */ - for (y = sel->hei-1; y >= r.ly; y--) { - for (x = r.lx; x <= r.hx; x++) { - if (selection_getpoint(x, y, sel)) { - r.hy = y; - break; - } - } - if (r.hy > -1) - break; - } - sel->bounds = r; - } - - sel->bounds_dirty = FALSE; -} - -coordxy -selection_getpoint( - coordxy x, coordxy y, - struct selectionvar *sel) -{ - if (!sel || !sel->map) - return 0; - if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) - return 0; - - return (sel->map[sel->wid * y + x] - 1); -} - -void -selection_setpoint( - coordxy x, coordxy y, - struct selectionvar *sel, - int c) -{ - if (!sel || !sel->map) - return; - if (x < 0 || y < 0 || x >= sel->wid || y >= sel->hei) - return; - - if (c && !sel->bounds_dirty) { - if (sel->bounds.lx > x) sel->bounds.lx = x; - if (sel->bounds.ly > y) sel->bounds.ly = y; - if (sel->bounds.hx < x) sel->bounds.hx = x; - if (sel->bounds.hy < y) sel->bounds.hy = y; - } else { - sel->bounds_dirty = TRUE; - } - - sel->map[sel->wid * y + x] = (char) (c + 1); -} - -struct selectionvar * -selection_not(struct selectionvar *s) -{ - int x, y; - NhRect tmprect = cg.zeroNhRect; - - for (x = 0; x < s->wid; x++) - for (y = 0; y < s->hei; y++) - selection_setpoint(x, y, s, selection_getpoint(x, y, s) ? 0 : 1); - selection_getbounds(s, &tmprect); - return s; -} - -struct selectionvar * -selection_filter_mapchar(struct selectionvar *ov, xint16 typ, int lit) -{ - int x, y; - struct selectionvar *ret; - NhRect rect = cg.zeroNhRect; - - if (!ov) - return NULL; - - ret = selection_new(); - - selection_getbounds(ov, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (selection_getpoint(x, y, ov) - && match_maptyps(typ, levl[x][y].typ)) { - switch (lit) { - default: - case -2: - selection_setpoint(x, y, ret, 1); - break; - case -1: - selection_setpoint(x, y, ret, rn2(2)); - break; - case 0: - case 1: - if (levl[x][y].lit == (unsigned int) lit) - selection_setpoint(x, y, ret, 1); - break; - } - } - return ret; -} - -struct selectionvar * -selection_filter_percent( - struct selectionvar *ov, - int percent) -{ - int x, y; - struct selectionvar *ret; - NhRect rect = cg.zeroNhRect; - - if (!ov) - return NULL; - - ret = selection_new(); - - selection_getbounds(ov, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (selection_getpoint(x, y, ov) && (rn2(100) < percent)) - selection_setpoint(x, y, ret, 1); - - return ret; -} - -int -selection_rndcoord( - struct selectionvar *ov, - coordxy *x, coordxy *y, - boolean removeit) -{ - int idx = 0; - int c; - int dx, dy; - NhRect rect = cg.zeroNhRect; - - selection_getbounds(ov, &rect); - - for (dx = rect.lx; dx <= rect.hx; dx++) - for (dy = rect.ly; dy <= rect.hy; dy++) - if (selection_getpoint(dx, dy, ov)) - idx++; - - if (idx) { - c = rn2(idx); - for (dx = rect.lx; dx <= rect.hx; dx++) - for (dy = rect.ly; dy <= rect.hy; dy++) - if (selection_getpoint(dx, dy, ov)) { - if (!c) { - *x = dx; - *y = dy; - if (removeit) - selection_setpoint(dx, dy, ov, 0); - return 1; - } - c--; - } - } - *x = *y = -1; - return 0; -} - /* Choose a single random W_* direction. */ -static coordxy +coordxy random_wdir(void) { static const coordxy wdirs[4] = { W_NORTH, W_SOUTH, W_EAST, W_WEST }; return wdirs[rn2(4)]; } -void -selection_do_grow(struct selectionvar *ov, int dir) -{ - coordxy x, y; - struct selectionvar *tmp; - NhRect rect = cg.zeroNhRect; - - if (!ov) - return; - - tmp = selection_new(); - - if (dir == W_RANDOM) - dir = random_wdir(); - - selection_getbounds(ov, &rect); - - for (x = max(0, rect.lx-1); x <= min(COLNO-1, rect.hx+1); x++) - for (y = max(0, rect.ly-1); y <= min(ROWNO-1, rect.hy+1); y++) { - /* note: dir is a mask of multiple directions, but the only - way to specify diagonals is by including the two adjacent - orthogonal directions, which effectively specifies three- - way growth [WEST|NORTH => WEST plus WEST|NORTH plus NORTH] */ - if (((dir & W_WEST) && selection_getpoint(x + 1, y, ov)) - || (((dir & (W_WEST | W_NORTH)) == (W_WEST | W_NORTH)) - && selection_getpoint(x + 1, y + 1, ov)) - || ((dir & W_NORTH) && selection_getpoint(x, y + 1, ov)) - || (((dir & (W_NORTH | W_EAST)) == (W_NORTH | W_EAST)) - && selection_getpoint(x - 1, y + 1, ov)) - || ((dir & W_EAST) && selection_getpoint(x - 1, y, ov)) - || (((dir & (W_EAST | W_SOUTH)) == (W_EAST | W_SOUTH)) - && selection_getpoint(x - 1, y - 1, ov)) - || ((dir & W_SOUTH) && selection_getpoint(x, y - 1, ov)) - || (((dir & (W_SOUTH | W_WEST)) == (W_SOUTH | W_WEST)) - && selection_getpoint(x + 1, y - 1, ov))) { - selection_setpoint(x, y, tmp, 1); - } - } - - selection_getbounds(tmp, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (selection_getpoint(x, y, tmp)) - selection_setpoint(x, y, ov, 1); - - selection_free(tmp, TRUE); -} - -static int (*selection_flood_check_func)(coordxy, coordxy); static schar floodfillchk_match_under_typ; -void -set_selection_floodfillchk(int (*f)(coordxy, coordxy)) -{ - selection_flood_check_func = f; -} - static int floodfillchk_match_under(coordxy x, coordxy y) { @@ -4870,373 +4507,6 @@ floodfillchk_match_accessible(coordxy x, coordxy y) || levl[x][y].typ == SCORR); } -/* check whethere is already in xs[],ys[] */ -static boolean -sel_flood_havepoint( - coordxy x, coordxy y, - coordxy xs[], coordxy ys[], - int n) -{ - coordxy xx = x, yy = y; - - while (n > 0) { - --n; - if (xs[n] == xx && ys[n] == yy) - return TRUE; - } - return FALSE; -} - -void -selection_floodfill( - struct selectionvar *ov, - coordxy x, coordxy y, - boolean diagonals) -{ - struct selectionvar *tmp = selection_new(); -#define SEL_FLOOD_STACK (COLNO * ROWNO) -#define SEL_FLOOD(nx, ny) \ - do { \ - if (idx < SEL_FLOOD_STACK) { \ - dx[idx] = (nx); \ - dy[idx] = (ny); \ - idx++; \ - } else \ - panic(floodfill_stack_overrun); \ - } while (0) -#define SEL_FLOOD_CHKDIR(mx, my, sel) \ - do { \ - if (isok((mx), (my)) \ - && (*selection_flood_check_func)((mx), (my)) \ - && !selection_getpoint((mx), (my), (sel)) \ - && !sel_flood_havepoint((mx), (my), dx, dy, idx)) \ - SEL_FLOOD((mx), (my)); \ - } while (0) - static const char floodfill_stack_overrun[] = "floodfill stack overrun"; - int idx = 0; - coordxy dx[SEL_FLOOD_STACK]; - coordxy dy[SEL_FLOOD_STACK]; - - if (selection_flood_check_func == (int (*)(coordxy, coordxy)) 0) { - selection_free(tmp, TRUE); - return; - } - SEL_FLOOD(x, y); - do { - idx--; - x = dx[idx]; - y = dy[idx]; - if (isok(x, y)) { - selection_setpoint(x, y, ov, 1); - selection_setpoint(x, y, tmp, 1); - } - SEL_FLOOD_CHKDIR((x + 1), y, tmp); - SEL_FLOOD_CHKDIR((x - 1), y, tmp); - SEL_FLOOD_CHKDIR(x, (y + 1), tmp); - SEL_FLOOD_CHKDIR(x, (y - 1), tmp); - if (diagonals) { - SEL_FLOOD_CHKDIR((x + 1), (y + 1), tmp); - SEL_FLOOD_CHKDIR((x - 1), (y - 1), tmp); - SEL_FLOOD_CHKDIR((x - 1), (y + 1), tmp); - SEL_FLOOD_CHKDIR((x + 1), (y - 1), tmp); - } - } while (idx > 0); -#undef SEL_FLOOD -#undef SEL_FLOOD_STACK -#undef SEL_FLOOD_CHKDIR - selection_free(tmp, TRUE); -} - -/* McIlroy's Ellipse Algorithm */ -void -selection_do_ellipse( - struct selectionvar *ov, - int xc, int yc, - int a, int b, - int filled) -{ /* e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2 */ - int x = 0, y = b; - long a2 = (long) a * a, b2 = (long) b * b; - long crit1 = -(a2 / 4 + a % 2 + b2); - long crit2 = -(b2 / 4 + b % 2 + a2); - long crit3 = -(b2 / 4 + b % 2); - long t = -a2 * y; /* e(x+1/2,y-1/2) - (a^2+b^2)/4 */ - long dxt = 2 * b2 * x, dyt = -2 * a2 * y; - long d2xt = 2 * b2, d2yt = 2 * a2; - long width = 1; - long i; - - if (!ov) - return; - - filled = !filled; - - if (!filled) { - while (y >= 0 && x <= a) { - selection_setpoint(xc + x, yc + y, ov, 1); - if (x != 0 || y != 0) - selection_setpoint(xc - x, yc - y, ov, 1); - if (x != 0 && y != 0) { - selection_setpoint(xc + x, yc - y, ov, 1); - selection_setpoint(xc - x, yc + y, ov, 1); - } - if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ - || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ - x++; - dxt += d2xt; - t += dxt; - } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ - y--; - dyt += d2yt; - t += dyt; - } else { - x++; - dxt += d2xt; - t += dxt; - y--; - dyt += d2yt; - t += dyt; - } - } - } else { - while (y >= 0 && x <= a) { - if (t + b2 * x <= crit1 /* e(x+1,y-1/2) <= 0 */ - || t + a2 * y <= crit3) { /* e(x+1/2,y) <= 0 */ - x++; - dxt += d2xt; - t += dxt; - width += 2; - } else if (t - a2 * y > crit2) { /* e(x+1/2,y-1) > 0 */ - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc - y, ov, 1); - if (y != 0) - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc + y, ov, 1); - y--; - dyt += d2yt; - t += dyt; - } else { - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc - y, ov, 1); - if (y != 0) - for (i = 0; i < width; i++) - selection_setpoint(xc - x + i, yc + y, ov, 1); - x++; - dxt += d2xt; - t += dxt; - y--; - dyt += d2yt; - t += dyt; - width += 2; - } - } - } -} - -/* square of distance from line segment (x1,y1, x2,y2) to point (x3,y3) */ -static long -line_dist_coord(long x1, long y1, long x2, long y2, long x3, long y3) -{ - long px = x2 - x1; - long py = y2 - y1; - long s = px * px + py * py; - long x, y, dx, dy, distsq = 0; - float lu = 0; - - if (x1 == x2 && y1 == y2) - return dist2(x1, y1, x3, y3); - - lu = ((x3 - x1) * px + (y3 - y1) * py) / (float) s; - if (lu > 1) - lu = 1; - else if (lu < 0) - lu = 0; - - x = x1 + lu * px; - y = y1 + lu * py; - dx = x - x3; - dy = y - y3; - distsq = dx * dx + dy * dy; - - return distsq; -} - -/* guts of l_selection_gradient */ -void -selection_do_gradient( - struct selectionvar *ov, - long x, long y, - long x2,long y2, - long gtyp, - long mind, long maxd) -{ - long dx, dy, dofs; - - if (mind > maxd) { - long tmp = mind; - mind = maxd; - maxd = tmp; - } - - dofs = maxd * maxd - mind * mind; - if (dofs < 1) - dofs = 1; - - switch (gtyp) { - default: - impossible("Unrecognized gradient type! Defaulting to radial..."); - /* FALLTHRU */ - case SEL_GRADIENT_RADIAL: { - for (dx = 0; dx < COLNO; dx++) - for (dy = 0; dy < ROWNO; dy++) { - long d0 = line_dist_coord(x, y, x2, y2, dx, dy); - - if (d0 <= mind * mind - || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) - selection_setpoint(dx, dy, ov, 1); - } - break; - } - case SEL_GRADIENT_SQUARE: { - for (dx = 0; dx < COLNO; dx++) - for (dy = 0; dy < ROWNO; dy++) { - long d1 = line_dist_coord(x, y, x2, y2, x, dy); - long d2 = line_dist_coord(x, y, x2, y2, dx, y); - long d3 = line_dist_coord(x, y, x2, y2, x2, dy); - long d4 = line_dist_coord(x, y, x2, y2, dx, y2); - long d5 = line_dist_coord(x, y, x2, y2, dx, dy); - long d0 = min(d5, min(max(d1, d2), max(d3, d4))); - - if (d0 <= mind * mind - || (d0 <= maxd * maxd && d0 - mind * mind < rn2(dofs))) - selection_setpoint(dx, dy, ov, 1); - } - break; - } /*case*/ - } /*switch*/ -} - -/* bresenham line algo */ -void -selection_do_line( - coordxy x1, coordxy y1, - coordxy x2, coordxy y2, - struct selectionvar *ov) -{ - int d0, dx, dy, ai, bi, xi, yi; - - if (x1 < x2) { - xi = 1; - dx = x2 - x1; - } else { - xi = -1; - dx = x1 - x2; - } - if (y1 < y2) { - yi = 1; - dy = y2 - y1; - } else { - yi = -1; - dy = y1 - y2; - } - - selection_setpoint(x1, y1, ov, 1); - - if (!dx && !dy) { - /* single point - already all done */ - ; - } else if (dx > dy) { - ai = (dy - dx) * 2; - bi = dy * 2; - d0 = bi - dx; - do { - if (d0 >= 0) { - y1 += yi; - d0 += ai; - } else - d0 += bi; - x1 += xi; - selection_setpoint(x1, y1, ov, 1); - } while (x1 != x2); - } else { - ai = (dx - dy) * 2; - bi = dx * 2; - d0 = bi - dy; - do { - if (d0 >= 0) { - x1 += xi; - d0 += ai; - } else - d0 += bi; - y1 += yi; - selection_setpoint(x1, y1, ov, 1); - } while (y1 != y2); - } -} - -void -selection_do_randline( - coordxy x1, coordxy y1, - coordxy x2, coordxy y2, - schar rough, - schar rec, - struct selectionvar *ov) -{ - int mx, my; - int dx, dy; - - if (rec < 1 || (x2 == x1 && y2 == y1)) - return; - - if (rough > max(abs(x2 - x1), abs(y2 - y1))) - rough = max(abs(x2 - x1), abs(y2 - y1)); - - if (rough < 2) { - mx = ((x1 + x2) / 2); - my = ((y1 + y2) / 2); - } else { - do { - dx = rn2(rough) - (rough / 2); - dy = rn2(rough) - (rough / 2); - mx = ((x1 + x2) / 2) + dx; - my = ((y1 + y2) / 2) + dy; - } while ((mx > COLNO - 1 || mx < 0 || my < 0 || my > ROWNO - 1)); - } - - if (!selection_getpoint(mx, my, ov)) { - selection_setpoint(mx, my, ov, 1); - } - - rough = (rough * 2) / 3; - - rec--; - - selection_do_randline(x1, y1, mx, my, rough, rec, ov); - selection_do_randline(mx, my, x2, y2, rough, rec, ov); - - selection_setpoint(x2, y2, ov, 1); -} - -static void -selection_iterate( - struct selectionvar *ov, - select_iter_func func, - genericptr_t arg) -{ - coordxy x, y; - NhRect rect = cg.zeroNhRect; - - if (!ov) - return; - - selection_getbounds(ov, &rect); - - for (x = rect.lx; x <= rect.hx; x++) - for (y = rect.ly; y <= rect.hy; y++) - if (isok(x,y) && selection_getpoint(x, y, ov)) - (*func)(x, y, arg); -} - /* change map location terrain type during level creation */ static void sel_set_ter(coordxy x, coordxy y, genericptr_t arg) @@ -6938,27 +6208,6 @@ TODO: gc.coder->croom needs to be updated RESTORE_WARNING_UNREACHABLE_CODE -struct selectionvar * -selection_from_mkroom(struct mkroom *croom) -{ - struct selectionvar *sel = selection_new(); - coordxy x, y; - unsigned rmno; - - if (!croom && gc.coder && gc.coder->croom) - croom = gc.coder->croom; - if (!croom) - return sel; - - rmno = (unsigned)((croom - gr.rooms) + ROOMOFFSET); - for (y = croom->ly; y <= croom->hy; y++) - for (x = croom->lx; x <= croom->hx; x++) - if (isok(x, y) && !levl[x][y].edge - && levl[x][y].roomno == rmno) - selection_setpoint(x, y, sel, 1); - return sel; -} - void update_croom(void) { diff --git a/src/stairs.c b/src/stairs.c new file mode 100644 index 000000000..27007c53b --- /dev/null +++ b/src/stairs.c @@ -0,0 +1,237 @@ +/* NetHack 3.7 stairs.c $NHDT-Date: 1704043695 2023/12/31 17:28:15 $ $NHDT-Branch: keni-luabits2 $:$NHDT-Revision: 1.207 $ */ +/* Copyright (c) 2024 by Pasi Kallinen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +void +stairway_add( + coordxy x, coordxy y, + boolean up, boolean isladder, + d_level *dest) +{ + stairway *tmp = (stairway *) alloc(sizeof (stairway)); + + (void) memset((genericptr_t) tmp, 0, sizeof (stairway)); + tmp->sx = x; + tmp->sy = y; + tmp->up = up; + tmp->isladder = isladder; + tmp->u_traversed = FALSE; + assign_level(&(tmp->tolev), dest); + tmp->next = gs.stairs; + gs.stairs = tmp; +} + +void +stairway_free_all(void) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + stairway *tmp2 = tmp->next; + free(tmp); + tmp = tmp2; + } + gs.stairs = NULL; +} + +stairway * +stairway_at(coordxy x, coordxy y) +{ + stairway *tmp = gs.stairs; + + while (tmp && !(tmp->sx == x && tmp->sy == y)) + tmp = tmp->next; + return tmp; +} + +stairway * +stairway_find(d_level *fromdlev) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + if (tmp->tolev.dnum == fromdlev->dnum + && tmp->tolev.dlevel == fromdlev->dlevel) + break; /* return */ + tmp = tmp->next; + } + return tmp; +} + +stairway * +stairway_find_from(d_level *fromdlev, boolean isladder) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + if (tmp->tolev.dnum == fromdlev->dnum + && tmp->tolev.dlevel == fromdlev->dlevel + && tmp->isladder == isladder) + break; /* return */ + tmp = tmp->next; + } + return tmp; +} + +stairway * +stairway_find_dir(boolean up) +{ + stairway *tmp = gs.stairs; + + while (tmp && !(tmp->up == up)) + tmp = tmp->next; + return tmp; +} + +stairway * +stairway_find_type_dir(boolean isladder, boolean up) +{ + stairway *tmp = gs.stairs; + + while (tmp && !(tmp->isladder == isladder && tmp->up == up)) + tmp = tmp->next; + return tmp; +} + +stairway * +stairway_find_special_dir(boolean up) +{ + stairway *tmp = gs.stairs; + + while (tmp) { + if (tmp->tolev.dnum != u.uz.dnum && tmp->up != up) + return tmp; + tmp = tmp->next; + } + return tmp; +} + +/* place you on the special staircase */ +void +u_on_sstairs(int upflag) +{ + stairway *stway = stairway_find_special_dir(upflag); + + if (stway) + u_on_newpos(stway->sx, stway->sy); + else + u_on_rndspot(upflag); +} + +/* place you on upstairs (or special equivalent) */ +void +u_on_upstairs(void) +{ + stairway *stway = stairway_find_dir(TRUE); + + if (stway) + u_on_newpos(stway->sx, stway->sy); + else + u_on_sstairs(0); /* destination upstairs implies moving down */ +} + +/* place you on dnstairs (or special equivalent) */ +void +u_on_dnstairs(void) +{ + stairway *stway = stairway_find_dir(FALSE); + + if (stway) + u_on_newpos(stway->sx, stway->sy); + else + u_on_sstairs(1); /* destination dnstairs implies moving up */ +} + +boolean +On_stairs(coordxy x, coordxy y) +{ + return (stairway_at(x, y) != NULL); +} + +boolean +On_ladder(coordxy x, coordxy y) +{ + stairway *stway = stairway_at(x, y); + + return (boolean) (stway && stway->isladder); +} + +boolean +On_stairs_up(coordxy x, coordxy y) +{ + stairway *stway = stairway_at(x, y); + + return (boolean) (stway && stway->up); +} + +boolean +On_stairs_dn(coordxy x, coordxy y) +{ + stairway *stway = stairway_at(x, y); + + return (boolean) (stway && !stway->up); +} + +/* return True if 'sway' is a branch staircase and hero has used these stairs + to visit the branch */ +boolean +known_branch_stairs(stairway *sway) +{ + return (sway && sway->tolev.dnum != u.uz.dnum && sway->u_traversed); +} + +/* describe staircase 'sway' based on whether hero knows the destination */ +char * +stairs_description( + stairway *sway, /* stairs/ladder to describe */ + char *outbuf, /* result buffer */ + boolean stcase) /* True: "staircase" or "ladder", always singular; + * False: "stairs" or "ladder"; caller needs to deal + * with singular vs plural when forming a sentence */ +{ + d_level tolev; + const char *stairs, *updown; + + tolev = sway->tolev; + stairs = sway->isladder ? "ladder" : stcase ? "staircase" : "stairs"; + updown = sway->up ? "up" : "down"; + + if (!known_branch_stairs(sway)) { + /* ordinary stairs or branch stairs to not-yet-visited branch */ + Sprintf(outbuf, "%s %s", stairs, updown); + if (sway->u_traversed) { + boolean specialdepth = (tolev.dnum == quest_dnum + || single_level_branch(&tolev)); /* knox */ + int to_dlev = specialdepth ? dunlev(&tolev) : depth(&tolev); + + Sprintf(eos(outbuf), " to level %d", to_dlev); + } + } else if (u.uz.dnum == 0 && u.uz.dlevel == 1 && sway->up) { + /* stairs up from level one are a special case; they are marked + as having been traversed because the hero obviously started + the game by coming down them, but the remote side varies + depending on whether the Amulet is being carried */ + Sprintf(outbuf, "%s%s %s %s", + !u.uhave.amulet ? "" : "branch ", + stairs, updown, + !u.uhave.amulet ? "out of the dungeon" + /* minimize our expectations about what comes next */ + : (on_level(&tolev, &earth_level) + || on_level(&tolev, &air_level) + || on_level(&tolev, &fire_level) + || on_level(&tolev, &water_level)) + ? "to the Elemental Planes" + : "to the end game"); + } else { + /* known branch stairs; tacking on destination level is too verbose */ + Sprintf(outbuf, "branch %s %s to %s", + stairs, updown, gd.dungeons[tolev.dnum].dname); + /* dungeons[].dname is capitalized; undo that for "The " */ + (void) strsubst(outbuf, "The ", "the "); + } + return outbuf; +} + +/*stairs.c*/ diff --git a/src/strutil.c b/src/strutil.c new file mode 100644 index 000000000..82b10d11b --- /dev/null +++ b/src/strutil.c @@ -0,0 +1,158 @@ +/* NetHack 3.7 strutil.c $NHDT-Date: 1709571807 2024/03/04 17:03:27 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.0 $ */ +/* Copyright (c) Robert Patrick Rankin, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" /* for config.h+extern.h */ + +/* strbuf_init() initializes strbuf state for use */ +void +strbuf_init(strbuf_t *strbuf) +{ + strbuf->str = NULL; + strbuf->len = 0; +} + +/* strbuf_append() appends given str to strbuf->str */ +void +strbuf_append(strbuf_t *strbuf, const char *str) +{ + int len = (int) strlen(str) + 1; + + strbuf_reserve(strbuf, + len + (strbuf->str ? (int) strlen(strbuf->str) : 0)); + Strcat(strbuf->str, str); +} + +/* strbuf_reserve() ensure strbuf->str has storage for len characters */ +void +strbuf_reserve(strbuf_t *strbuf, int len) +{ + if (strbuf->str == NULL) { + strbuf->str = strbuf->buf; + strbuf->str[0] = '\0'; + strbuf->len = (int) sizeof strbuf->buf; + } + + if (len > strbuf->len) { + char *oldbuf = strbuf->str; + + strbuf->len = len + (int) sizeof strbuf->buf; + strbuf->str = (char *) alloc(strbuf->len); + Strcpy(strbuf->str, oldbuf); + if (oldbuf != strbuf->buf) + free((genericptr_t) oldbuf); + } +} + +/* strbuf_empty() frees allocated memory and set strbuf to initial state */ +void +strbuf_empty(strbuf_t *strbuf) +{ + if (strbuf->str != NULL && strbuf->str != strbuf->buf) + free((genericptr_t) strbuf->str); + strbuf_init(strbuf); +} + +/* strbuf_nl_to_crlf() converts all occurences of \n to \r\n */ +void +strbuf_nl_to_crlf(strbuf_t *strbuf) +{ + if (strbuf->str) { + int len = (int) strlen(strbuf->str); + int count = 0; + char *cp = strbuf->str; + + while (*cp) + if (*cp++ == '\n') + count++; + if (count) { + strbuf_reserve(strbuf, len + count + 1); + for (cp = strbuf->str + len + count; count; --cp) + if ((*cp = cp[-count]) == '\n') { + *--cp = '\r'; + --count; + } + } + } +} + +/* strlen() but returns unsigned and panics if string is unreasonably long; + used by dlb as well as by nethack */ +unsigned +Strlen_( + const char *str, + const char *file, + int line) +{ + const char *p; + size_t len; + + /* strnlen(str, LARGEST_INT) w/o requiring posix.1 headers or libraries */ + for (p = str, len = 0; len < LARGEST_INT; ++len) + if (*p++ == '\0') + break; + + if (len == LARGEST_INT) + panic("%s:%d string too long", file, line); + return (unsigned) len; +} + +static boolean pmatch_internal(const char *, const char *, boolean, + const char *); +/* guts of pmatch(), pmatchi(), and pmatchz(); + match a string against a pattern */ +static boolean +pmatch_internal(const char *patrn, const char *strng, + boolean ci, /* True => case-insensitive, + False => case-sensitive */ + const char *sk) /* set of characters to skip */ +{ + char s, p; + /* + * Simple pattern matcher: '*' matches 0 or more characters, '?' matches + * any single character. Returns TRUE if 'strng' matches 'patrn'. + */ + pmatch_top: + if (!sk) { + s = *strng++; + p = *patrn++; /* get next chars and pre-advance */ + } else { + /* fuzzy match variant of pmatch; particular characters are ignored */ + do { + s = *strng++; + } while (strchr(sk, s)); + do { + p = *patrn++; + } while (strchr(sk, p)); + } + if (!p) /* end of pattern */ + return (boolean) (s == '\0'); /* matches iff end of string too */ + else if (p == '*') /* wildcard reached */ + return (boolean) ((!*patrn + || pmatch_internal(patrn, strng - 1, ci, sk)) + ? TRUE + : s ? pmatch_internal(patrn - 1, strng, ci, sk) + : FALSE); + else if ((ci ? lowc(p) != lowc(s) : p != s) /* check single character */ + && (p != '?' || !s)) /* & single-char wildcard */ + return FALSE; /* doesn't match */ + else /* return pmatch_internal(patrn, strng, ci, sk); */ + goto pmatch_top; /* optimize tail recursion */ +} + +/* case-sensitive wildcard match */ +boolean +pmatch(const char *patrn, const char *strng) +{ + return pmatch_internal(patrn, strng, FALSE, (const char *) 0); +} + +/* case-insensitive wildcard match */ +boolean +pmatchi(const char *patrn, const char *strng) +{ + return pmatch_internal(patrn, strng, TRUE, (const char *) 0); +} + + +/*strutil.c*/ diff --git a/src/wizcmds.c b/src/wizcmds.c new file mode 100644 index 000000000..33a039b66 --- /dev/null +++ b/src/wizcmds.c @@ -0,0 +1,1821 @@ +/* NetHack 3.7 wizcmds.c $NHDT-Date: 1709675219 2024/03/05 21:46:59 $ $NHDT-Branch: keni-mdlib-followup $:$NHDT-Revision: 1.713 $ */ +/*-Copyright (c) Robert Patrick Rankin, 2024. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "func_tab.h" + +extern const char unavailcmd[]; /* cmd.c [27] */ +extern const char *levltyp[MAX_TYPE + 2]; /* cmd.c */ + +static int size_monst(struct monst *, boolean); +static int size_obj(struct obj *); +static void count_obj(struct obj *, long *, long *, boolean, boolean); +static void obj_chain(winid, const char *, struct obj *, boolean, long *, + long *); +static void mon_invent_chain(winid, const char *, struct monst *, long *, + long *); +static void mon_chain(winid, const char *, struct monst *, boolean, long *, + long *); +static void contained_stats(winid, const char *, long *, long *); +static void misc_stats(winid, long *, long *); +static void you_sanity_check(void); +static void makemap_unmakemon(struct monst *, boolean); +static int QSORTCALLBACK migrsort_cmp(const genericptr, const genericptr); +static void list_migrating_mons(d_level *); + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* #wizwish command - wish for something */ +int +wiz_wish(void) /* Unlimited wishes for debug mode by Paul Polderman */ +{ + if (wizard) { + boolean save_verbose = flags.verbose; + + flags.verbose = FALSE; + makewish(); + flags.verbose = save_verbose; + (void) encumber_msg(); + } else + pline(unavailcmd, ecname_from_fn(wiz_wish)); + return ECMD_OK; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* #wizidentify command - reveal and optionally identify hero's inventory */ +int +wiz_identify(void) +{ + if (wizard) { + iflags.override_ID = (int) cmd_from_func(wiz_identify); + /* command remapping might leave #wizidentify as the only way + to invoke us, in which case cmd_from_func() will yield NUL; + it won't matter to display_inventory()/display_pickinv() + if ^I invokes some other command--what matters is that + display_pickinv() and xname() see override_ID as nonzero */ + if (!iflags.override_ID) + iflags.override_ID = C('I'); + (void) display_inventory((char *) 0, FALSE); + iflags.override_ID = 0; + } else + pline(unavailcmd, ecname_from_fn(wiz_identify)); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* used when wiz_makemap() gets rid of monsters for the old incarnation of + a level before creating a new incarnation of it */ +void +makemap_unmakemon(struct monst *mtmp, boolean migratory) +{ + int ndx = monsndx(mtmp->data); + + /* uncreate any unique monster so that it is eligible to be remade + on the new incarnation of the level; ignores DEADMONSTER() [why?] */ + if (mtmp->data->geno & G_UNIQ) + gm.mvitals[ndx].mvflags &= ~G_EXTINCT; + if (gm.mvitals[ndx].born) + gm.mvitals[ndx].born--; + + /* vault is going away; get rid of guard who might be in play or + be parked at <0,0>; for the latter, might already be flagged as + dead but is being kept around because of the 'isgd' flag */ + if (mtmp->isgd) { + mtmp->isgd = 0; /* after this, fall through to mongone() */ + } else if (DEADMONSTER(mtmp)) { + return; /* already set to be discarded */ + } else if (mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) { + setpaid(mtmp); + } + if (migratory) { + /* caller has removed 'mtmp' from migrating_mons; put it onto fmon + so that dmonsfree() bookkeeping for number of dead or removed + monsters won't get out of sync; it is not on the map but + mongone() -> m_detach() -> mon_leaving_level() copes with that */ + mtmp->mstate |= MON_OFFMAP; + mtmp->mstate &= ~(MON_MIGRATING | MON_LIMBO | MON_ENDGAME_MIGR); + mtmp->nmon = fmon; + fmon = mtmp; + } + mongone(mtmp); +} + +/* get rid of the all the monsters on--or intimately involved with--current + level; used when #wizmakemap destroys the level before replacing it */ +void +makemap_remove_mons(void) +{ + struct monst *mtmp, **mprev; + + /* keep steed and other adjacent pets after releasing them + from traps, stopping eating, &c as if hero were ascending */ + keepdogs(TRUE); /* (pets-only; normally we'd be using 'FALSE') */ + /* get rid of all the monsters that didn't make it to 'mydogs' */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + /* if already dead, dmonsfree(below) will get rid of it */ + if (DEADMONSTER(mtmp)) + continue; + makemap_unmakemon(mtmp, FALSE); + } + /* some monsters retain details of this level in mon->mextra; that + data becomes invalid when the level is replaced by a new one; + get rid of them now if migrating or already arrived elsewhere; + [when on their 'home' level, the previous loop got rid of them; + if they aren't actually migrating but have been placed on some + 'away' level, such monsters are treated like the Wizard: kept + on migrating monsters list, scheduled to migrate back to their + present location instead of being saved with whatever level they + happen to be on; see keepdogs() and keep_mon_accessible(dog.c)] */ + for (mprev = &gm.migrating_mons; (mtmp = *mprev) != 0; ) { + if (mtmp->mextra + && ((mtmp->isshk && on_level(&u.uz, &ESHK(mtmp)->shoplevel)) + || (mtmp->ispriest && on_level(&u.uz, &EPRI(mtmp)->shrlevel)) + || (mtmp->isgd && on_level(&u.uz, &EGD(mtmp)->gdlevel)))) { + *mprev = mtmp->nmon; + makemap_unmakemon(mtmp, TRUE); + } else { + mprev = &mtmp->nmon; + } + } + /* release dead and 'unmade' monsters */ + dmonsfree(); + if (fmon) { + impossible("makemap_remove_mons: 'fmon' did not get emptied?"); + } + return; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* #wizmakemap - discard current dungeon level and replace with a new one */ +int +wiz_makemap(void) +{ + if (wizard) { + boolean was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz); + + makemap_prepost(TRUE, was_in_W_tower); + /* create a new level; various things like bestowing a guardian + angel on Astral or setting off alarm on Ft.Ludios are handled + by goto_level(do.c) so won't occur for replacement levels */ + mklev(); + makemap_prepost(FALSE, was_in_W_tower); + } else { + pline(unavailcmd, ecname_from_fn(wiz_makemap)); + } + return ECMD_OK; +} + +/* the #wizmap command - reveal the level map + and any traps or engravings on it */ +int +wiz_map(void) +{ + if (wizard) { + struct trap *t; + struct engr *ep; + long save_Hconf = HConfusion, save_Hhallu = HHallucination; + + notice_mon_off(); + HConfusion = HHallucination = 0L; + for (t = gf.ftrap; t != 0; t = t->ntrap) { + t->tseen = 1; + map_trap(t, TRUE); + } + for (ep = head_engr; ep != 0; ep = ep->nxt_engr) { + map_engraving(ep, TRUE); + } + do_mapping(); + notice_mon_on(); + HConfusion = save_Hconf; + HHallucination = save_Hhallu; + } else + pline(unavailcmd, ecname_from_fn(wiz_map)); + return ECMD_OK; +} + +/* #wizgenesis - generate monster(s); a count prefix will be honored */ +int +wiz_genesis(void) +{ + if (wizard) { + boolean mongen_saved = iflags.debug_mongen; + + iflags.debug_mongen = FALSE; + (void) create_particular(); + iflags.debug_mongen = mongen_saved; + } else + pline(unavailcmd, ecname_from_fn(wiz_genesis)); + return ECMD_OK; +} + +/* #wizwhere command - display dungeon layout */ +int +wiz_where(void) +{ + if (wizard) + (void) print_dungeon(FALSE, (schar *) 0, (xint16 *) 0); + else + pline(unavailcmd, ecname_from_fn(wiz_where)); + return ECMD_OK; +} + +/* the #wizdetect command - detect secret doors, traps, hidden monsters */ +int +wiz_detect(void) +{ + if (wizard) + (void) findit(); + else + pline(unavailcmd, ecname_from_fn(wiz_detect)); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* the #wizkill command - pick targets and reduce them to 0HP; + by default, the hero is credited/blamed; use 'm' prefix to avoid that */ +int +wiz_kill(void) +{ + struct monst *mtmp; + coord cc; + int ans; + char c, qbuf[QBUFSZ]; + const char *prompt = "Pick first monster to slay"; + boolean save_verbose = flags.verbose, + save_autodescribe = iflags.autodescribe; + d_level uarehere = u.uz; + + cc.x = u.ux, cc.y = u.uy; + for (;;) { + pline("%s:", prompt); + prompt = "Next monster"; + + flags.verbose = FALSE; + iflags.autodescribe = TRUE; + ans = getpos(&cc, TRUE, "a monster"); + flags.verbose = save_verbose; + iflags.autodescribe = save_autodescribe; + if (ans < 0 || cc.x < 1) + break; + + mtmp = 0; + if (u_at(cc.x, cc.y)) { + if (u.usteed) { + Sprintf(qbuf, "Kill %.110s?", mon_nam(u.usteed)); + if ((c = ynq(qbuf)) == 'q') + break; + if (c == 'y') + mtmp = u.usteed; + } + if (!mtmp) { + Sprintf(qbuf, "%s?", Role_if(PM_SAMURAI) ? "Perform seppuku" + : "Commit suicide"); + if (paranoid_query(TRUE, qbuf)) { + Sprintf(gk.killer.name, "%s own player", uhis()); + gk.killer.format = KILLED_BY; + done(DIED); + } + break; + } + } else if (u.uswallow) { + mtmp = next2u(cc.x, cc.y) ? u.ustuck : 0; + } else { + mtmp = m_at(cc.x, cc.y); + } + + /* whether there's an unseen monster here or not, player will know + that there's no monster here after the kill or failed attempt; + let hero know too */ + (void) unmap_invisible(cc.x, cc.y); + + if (mtmp) { + /* we don't require that the monster be seen or sensed so + we issue our own message in order to name it in case it + isn't; note that if it triggers other kills, those might + be referred to as "it" */ + int tame = !!mtmp->mtame, + seen = (canspotmon(mtmp) || (u.uswallow && mtmp == u.ustuck)), + flgs = (SUPPRESS_IT | SUPPRESS_HALLUCINATION + | ((tame && has_mgivenname(mtmp)) ? SUPPRESS_SADDLE + : 0)), + articl = tame ? ARTICLE_YOUR : seen ? ARTICLE_THE : ARTICLE_A; + const char *adjs = tame ? (!seen ? "poor, unseen" : "poor") + : (!seen ? "unseen" : (const char *) 0); + char *Mn = x_monnam(mtmp, articl, adjs, flgs, FALSE); + + if (!iflags.menu_requested) { + /* normal case: hero is credited/blamed */ + You("%s %s!", nonliving(mtmp->data) ? "destroy" : "kill", Mn); + xkilled(mtmp, XKILL_NOMSG); + } else { /* 'm'-prefix */ + /* we know that monsters aren't moving because player has + just issued this #wizkill command, but if 'mtmp' is a + gas spore whose explosion kills any other monsters we + need to have the mon_moving flag be True in order to + avoid blaming or crediting hero for their deaths */ + gc.context.mon_moving = TRUE; + pline("%s is %s.", upstart(Mn), + nonliving(mtmp->data) ? "destroyed" : "killed"); + /* Null second arg suppresses the usual message */ + monkilled(mtmp, (char *) 0, AD_PHYS); + gc.context.mon_moving = FALSE; + } + /* end targetting loop if an engulfer dropped hero onto a level- + changing trap */ + if (u.utotype || !on_level(&u.uz, &uarehere)) + break; + } else { + There("is no monster there."); + break; + } + } + /* since #wizkill takes no game time, it is possible to kill something + in the main dungeon and immediately level teleport into the endgame + which will delete the main dungeon's level files; avoid triggering + impossible "dmonsfree: 0 removed doesn't match N pending" by forcing + dead monster cleanup; we don't track whether anything was actually + killed above--if nothing was, this will be benign */ + dmonsfree(); + /* distinction between ECMD_CANCEL and ECMD_OK is unimportant here */ + return ECMD_OK; /* no time elapses */ +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* the #wizloadlua command - load an arbitrary lua file */ +int +wiz_load_lua(void) +{ + if (wizard) { + char buf[BUFSZ]; + /* Large but not unlimited memory and CPU so random bits of + * code can be tested by wizards. */ + nhl_sandbox_info sbi = {NHL_SB_SAFE | NHL_SB_DEBUGGING, + 16*1024*1024, 0, 16*1024*1024}; + + buf[0] = '\0'; + getlin("Load which lua file?", buf); + if (buf[0] == '\033' || buf[0] == '\0') + return ECMD_CANCEL; + if (!strchr(buf, '.')) + strcat(buf, ".lua"); + (void) load_lua(buf, &sbi); + } else + pline(unavailcmd, ecname_from_fn(wiz_load_lua)); + return ECMD_OK; +} + +/* the #wizloaddes command - load a special level lua file */ +int +wiz_load_splua(void) +{ + if (wizard) { + char buf[BUFSZ]; + + buf[0] = '\0'; + getlin("Load which des lua file?", buf); + if (buf[0] == '\033' || buf[0] == '\0') + return ECMD_CANCEL; + if (!strchr(buf, '.')) + strcat(buf, ".lua"); + + lspo_reset_level(NULL); + (void) load_special(buf); + lspo_finalize_level(NULL); + + } else + pline(unavailcmd, ecname_from_fn(wiz_load_splua)); + return ECMD_OK; +} + +/* the #wizlevelport command - level teleport */ +int +wiz_level_tele(void) +{ + if (wizard) + level_tele(); + else + pline(unavailcmd, ecname_from_fn(wiz_level_tele)); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* #wizfliplevel - transpose the current level */ +int +wiz_flip_level(void) +{ + static const char choices[] = "0123", + prmpt[] = "Flip 0=randomly, 1=vertically, 2=horizontally, 3=both:"; + + /* + * Does not handle + * levregions, + * monster mtrack, + * migrating monsters aimed at returning to specific coordinates + * on this level + * as flipping is normally done only during level creation. + */ + if (wizard) { + char c = yn_function(prmpt, choices, '\0', TRUE); + + if (c && strchr(choices, c)) { + c -= '0'; + + if (!c) + flip_level_rnd(3, TRUE); + else + flip_level((int) c, TRUE); + + docrt(); + } else { + pline("%s", Never_mind); + } + } + return ECMD_OK; +} + +/* #levelchange command - adjust hero's experience level */ +int +wiz_level_change(void) +{ + char buf[BUFSZ] = DUMMY; + int newlevel = 0; + int ret; + + getlin("To what experience level do you want to be set?", buf); + (void) mungspaces(buf); + if (buf[0] == '\033' || buf[0] == '\0') + ret = 0; + else + ret = sscanf(buf, "%d", &newlevel); + + if (ret != 1) { + pline1(Never_mind); + return ECMD_OK; + } + if (newlevel == u.ulevel) { + You("are already that experienced."); + } else if (newlevel < u.ulevel) { + if (u.ulevel == 1) { + You("are already as inexperienced as you can get."); + return ECMD_OK; + } + if (newlevel < 1) + newlevel = 1; + while (u.ulevel > newlevel) + losexp("#levelchange"); + } else { + if (u.ulevel >= MAXULEV) { + You("are already as experienced as you can get."); + return ECMD_OK; + } + if (newlevel > MAXULEV) + newlevel = MAXULEV; + while (u.ulevel < newlevel) + pluslvl(FALSE); + } + u.ulevelmax = u.ulevel; + return ECMD_OK; +} + +DISABLE_WARNING_CONDEXPR_IS_CONSTANT + +/* #wiztelekinesis */ +int +wiz_telekinesis(void) +{ + int ans = 0; + coord cc; + struct monst *mtmp = (struct monst *) 0; + + cc.x = u.ux; + cc.y = u.uy; + + pline("Pick a monster to hurtle."); + do { + ans = getpos(&cc, TRUE, "a monster"); + if (ans < 0 || cc.x < 1) + return ECMD_CANCEL; + + if ((((mtmp = m_at(cc.x, cc.y)) != 0) && canspotmon(mtmp)) + || u_at(cc.x, cc.y)) { + if (!getdir("which direction?")) + return ECMD_CANCEL; + + if (mtmp) { + mhurtle(mtmp, u.dx, u.dy, 6); + if (!DEADMONSTER(mtmp) && canspotmon(mtmp)) { + cc.x = mtmp->mx; + cc.y = mtmp->my; + } + } else { + hurtle(u.dx, u.dy, 6, FALSE); + cc.x = u.ux, cc.y = u.uy; + } + } + + } while (u.utotype == UTOTYPE_NONE); + return ECMD_OK; +} + +RESTORE_WARNING_CONDEXPR_IS_CONSTANT + +/* #panic command - test program's panic handling */ +int +wiz_panic(void) +{ + if (iflags.debug_fuzzer) { + u.uhp = u.uhpmax = 1000; + u.uen = u.uenmax = 1000; + return ECMD_OK; + } + if (paranoid_query(TRUE, + "Do you want to call panic() and end your game?")) + panic("Crash test."); + return ECMD_OK; +} + +/* #debugfuzzer command - fuzztest the program */ +int +wiz_fuzzer(void) +{ + if (flags.suppress_alert < FEATURE_NOTICE_VER(3,7,0)) { + pline("The fuzz tester will make NetHack execute random keypresses."); + There("is no conventional way out of this mode."); + } + if (paranoid_query(TRUE, "Do you want to start fuzz testing?")) + iflags.debug_fuzzer = TRUE; /* Thoth, take the reins */ + return ECMD_OK; +} + +/* #polyself command - change hero's form */ +int +wiz_polyself(void) +{ + polyself(POLY_CONTROLLED); + return ECMD_OK; +} + +/* #seenv command */ +int +wiz_show_seenv(void) +{ + winid win; + coordxy x, y, startx, stopx, curx; + int v; + char row[COLNO + 1]; + + win = create_nhwindow(NHW_TEXT); + /* + * Each seenv description takes up 2 characters, so center + * the seenv display around the hero. + */ + startx = max(1, u.ux - (COLNO / 4)); + stopx = min(startx + (COLNO / 2), COLNO); + /* can't have a line exactly 80 chars long */ + if (stopx - startx == COLNO / 2) + startx++; + + for (y = 0; y < ROWNO; y++) { + for (x = startx, curx = 0; x < stopx; x++, curx += 2) { + if (u_at(x, y)) { + row[curx] = row[curx + 1] = '@'; + } else { + v = levl[x][y].seenv & 0xff; + if (v == 0) + row[curx] = row[curx + 1] = ' '; + else + Sprintf(&row[curx], "%02x", v); + } + } + /* remove trailing spaces */ + for (x = curx - 1; x >= 0; x--) + if (row[x] != ' ') + break; + row[x + 1] = '\0'; + + putstr(win, 0, row); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return ECMD_OK; +} + +/* #vision command */ +int +wiz_show_vision(void) +{ + winid win; + coordxy x, y; + int v; + char row[COLNO + 1]; + + win = create_nhwindow(NHW_TEXT); + Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit", + COULD_SEE, IN_SIGHT, TEMP_LIT); + putstr(win, 0, row); + putstr(win, 0, ""); + for (y = 0; y < ROWNO; y++) { + for (x = 1; x < COLNO; x++) { + if (u_at(x, y)) { + row[x] = '@'; + } else { + v = gv.viz_array[y][x]; /* data access should be hidden */ + row[x] = (v == 0) ? ' ' : ('0' + v); + } + } + /* remove trailing spaces */ + for (x = COLNO - 1; x >= 1; x--) + if (row[x] != ' ') + break; + row[x + 1] = '\0'; + + putstr(win, 0, &row[1]); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return ECMD_OK; +} + +/* #wmode command */ +int +wiz_show_wmodes(void) +{ + winid win; + coordxy x, y; + char row[COLNO + 1]; + struct rm *lev; + boolean istty = WINDOWPORT(tty); + + win = create_nhwindow(NHW_TEXT); + if (istty) + putstr(win, 0, ""); /* tty only: blank top line */ + for (y = 0; y < ROWNO; y++) { + for (x = 0; x < COLNO; x++) { + lev = &levl[x][y]; + if (u_at(x, y)) + row[x] = '@'; + else if (IS_WALL(lev->typ) || lev->typ == SDOOR) + row[x] = '0' + (lev->wall_info & WM_MASK); + else if (lev->typ == CORR) + row[x] = '#'; + else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ)) + row[x] = '.'; + else + row[x] = 'x'; + } + row[COLNO] = '\0'; + /* map column 0, levl[0][], is off the left edge of the screen */ + putstr(win, 0, &row[1]); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return ECMD_OK; +} + +/* wizard mode variant of #terrain; internal levl[][].typ values in base-36 */ +void +wiz_map_levltyp(void) +{ + winid win; + coordxy x, y; + int terrain; + char row[COLNO + 1]; + boolean istty = !strcmp(windowprocs.name, "tty"); + + win = create_nhwindow(NHW_TEXT); + /* map row 0, levl[][0], is drawn on the second line of tty screen */ + if (istty) + putstr(win, 0, ""); /* tty only: blank top line */ + for (y = 0; y < ROWNO; y++) { + /* map column 0, levl[0][], is off the left edge of the screen; + it should always have terrain type "undiggable stone" */ + for (x = 1; x < COLNO; x++) { + terrain = levl[x][y].typ; + /* assumes there aren't more than 10+26+26 terrain types */ + row[x - 1] = (char) ((terrain == STONE && !may_dig(x, y)) + ? '*' + : (terrain < 10) + ? '0' + terrain + : (terrain < 36) + ? 'a' + terrain - 10 + : 'A' + terrain - 36); + } + x--; + if (levl[0][y].typ != STONE || may_dig(0, y)) + row[x++] = '!'; + row[x] = '\0'; + putstr(win, 0, row); + } + + { + char dsc[COLBUFSZ]; + s_level *slev = Is_special(&u.uz); + + Sprintf(dsc, "D:%d,L:%d", u.uz.dnum, u.uz.dlevel); + /* [dungeon branch features currently omitted] */ + /* special level features */ + if (slev) { + Sprintf(eos(dsc), " \"%s\"", slev->proto); + /* special level flags (note: dungeon.def doesn't set `maze' + or `hell' for any specific levels so those never show up) */ + if (slev->flags.maze_like) + Strcat(dsc, " mazelike"); + if (slev->flags.hellish) + Strcat(dsc, " hellish"); + if (slev->flags.town) + Strcat(dsc, " town"); + if (slev->flags.rogue_like) + Strcat(dsc, " roguelike"); + /* alignment currently omitted to save space */ + } + /* level features */ + if (gl.level.flags.nfountains) + Sprintf(eos(dsc), " %c:%d", defsyms[S_fountain].sym, + (int) gl.level.flags.nfountains); + if (gl.level.flags.nsinks) + Sprintf(eos(dsc), " %c:%d", defsyms[S_sink].sym, + (int) gl.level.flags.nsinks); + if (gl.level.flags.has_vault) + Strcat(dsc, " vault"); + if (gl.level.flags.has_shop) + Strcat(dsc, " shop"); + if (gl.level.flags.has_temple) + Strcat(dsc, " temple"); + if (gl.level.flags.has_court) + Strcat(dsc, " throne"); + if (gl.level.flags.has_zoo) + Strcat(dsc, " zoo"); + if (gl.level.flags.has_morgue) + Strcat(dsc, " morgue"); + if (gl.level.flags.has_barracks) + Strcat(dsc, " barracks"); + if (gl.level.flags.has_beehive) + Strcat(dsc, " hive"); + if (gl.level.flags.has_swamp) + Strcat(dsc, " swamp"); + /* level flags */ + if (gl.level.flags.noteleport) + Strcat(dsc, " noTport"); + if (gl.level.flags.hardfloor) + Strcat(dsc, " noDig"); + if (gl.level.flags.nommap) + Strcat(dsc, " noMMap"); + if (!gl.level.flags.hero_memory) + Strcat(dsc, " noMem"); + if (gl.level.flags.shortsighted) + Strcat(dsc, " shortsight"); + if (gl.level.flags.graveyard) + Strcat(dsc, " graveyard"); + if (gl.level.flags.is_maze_lev) + Strcat(dsc, " maze"); + if (gl.level.flags.is_cavernous_lev) + Strcat(dsc, " cave"); + if (gl.level.flags.arboreal) + Strcat(dsc, " tree"); + if (Sokoban) + Strcat(dsc, " sokoban-rules"); + /* non-flag info; probably should include dungeon branching + checks (extra stairs and magic portals) here */ + if (Invocation_lev(&u.uz)) + Strcat(dsc, " invoke"); + if (On_W_tower_level(&u.uz)) + Strcat(dsc, " tower"); + /* append a branch identifier for completeness' sake */ + if (u.uz.dnum == 0) + Strcat(dsc, " dungeon"); + else if (u.uz.dnum == mines_dnum) + Strcat(dsc, " mines"); + else if (In_sokoban(&u.uz)) + Strcat(dsc, " sokoban"); + else if (u.uz.dnum == quest_dnum) + Strcat(dsc, " quest"); + else if (Is_knox(&u.uz)) + Strcat(dsc, " ludios"); + else if (u.uz.dnum == 1) + Strcat(dsc, " gehennom"); + else if (u.uz.dnum == tower_dnum) + Strcat(dsc, " vlad"); + else if (In_endgame(&u.uz)) + Strcat(dsc, " endgame"); + else { + /* somebody's added a dungeon branch we're not expecting */ + const char *brname = gd.dungeons[u.uz.dnum].dname; + + if (!brname || !*brname) + brname = "unknown"; + if (!strncmpi(brname, "the ", 4)) + brname += 4; + Sprintf(eos(dsc), " %s", brname); + } + /* limit the line length to map width */ + if (strlen(dsc) >= COLNO) + dsc[COLNO - 1] = '\0'; /* truncate */ + putstr(win, 0, dsc); + } + + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* explanation of base-36 output from wiz_map_levltyp() */ +void +wiz_levltyp_legend(void) +{ + winid win; + int i, j, last, c; + const char *dsc, *fmt; + char buf[BUFSZ]; + + win = create_nhwindow(NHW_TEXT); + putstr(win, 0, "#terrain encodings:"); + putstr(win, 0, ""); + fmt = " %c - %-28s"; /* TODO: include tab-separated variant for win32 */ + *buf = '\0'; + /* output in pairs, left hand column holds [0],[1],...,[N/2-1] + and right hand column holds [N/2],[N/2+1],...,[N-1]; + N ('last') will always be even, and may or may not include + the empty string entry to pad out the final pair, depending + upon how many other entries are present in levltyp[] */ + last = SIZE(levltyp) & ~1; + for (i = 0; i < last / 2; ++i) + for (j = i; j < last; j += last / 2) { + dsc = levltyp[j]; + c = !*dsc ? ' ' + : !strncmp(dsc, "unreachable", 11) ? '*' + /* same int-to-char conversion as wiz_map_levltyp() */ + : (j < 10) ? '0' + j + : (j < 36) ? 'a' + j - 10 + : 'A' + j - 36; + Sprintf(eos(buf), fmt, c, dsc); + if (j > i) { + putstr(win, 0, buf); + *buf = '\0'; + } + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +DISABLE_WARNING_CONDEXPR_IS_CONSTANT + +/* #wizsmell command - test usmellmon(). */ +int +wiz_smell(void) +{ + struct monst *mtmp; /* monster being smelled */ + struct permonst *mptr; + int ans, glyph; + coord cc; /* screen pos to sniff */ + boolean is_you; + + cc.x = u.ux; + cc.y = u.uy; + if (!olfaction(gy.youmonst.data)) { + You("are incapable of detecting odors in your present form."); + return ECMD_OK; + } + + You("can move the cursor to a monster that you want to smell."); + do { + pline("Pick a monster to smell."); + ans = getpos(&cc, TRUE, "a monster"); + if (ans < 0 || cc.x < 0) { + return ECMD_CANCEL; /* done */ + } + is_you = FALSE; + if (u_at(cc.x, cc.y)) { + if (u.usteed) { + mptr = u.usteed->data; + } else { + mptr = gy.youmonst.data; + is_you = TRUE; + } + } else if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *) 0) { + mptr = mtmp->data; + } else { + mptr = (struct permonst *) 0; + } + /* Buglet: mapping or unmapping "remembered, unseen monster" should + cause time to elapse; since we're in wizmode, don't bother */ + glyph = glyph_at(cc.x, cc.y); + /* Is it a monster? */ + if (mptr) { + if (is_you) + You("surreptitiously sniff under your %s.", body_part(ARM)); + if (!usmellmon(mptr)) + pline("%s to not give off any smell.", + is_you ? "You seem" : "That monster seems"); + if (!glyph_is_monster(glyph)) + map_invisible(cc.x, cc.y); + } else { + You("don't smell any monster there."); + if (glyph_is_invisible(glyph)) + unmap_invisible(cc.x, cc.y); + } + } while (TRUE); + return ECMD_OK; +} + +RESTORE_WARNING_CONDEXPR_IS_CONSTANT + +DISABLE_WARNING_FORMAT_NONLITERAL + +#define DEFAULT_TIMEOUT_INCR 30 + +/* #wizinstrinsic command to set some intrinsics for testing */ +int +wiz_intrinsic(void) +{ + if (wizard) { + static const char wizintrinsic[] = "#wizintrinsic"; + static const char fmt[] = "You are%s %s."; + winid win; + anything any; + char buf[BUFSZ]; + int i, j, n, amt, typ, p = 0; + long oldtimeout, newtimeout; + const char *propname; + menu_item *pick_list = (menu_item *) 0; + int clr = NO_COLOR; + + any = cg.zeroany; + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + if (iflags.cmdassist) { + /* start menu with a subtitle */ + Sprintf(buf, + "[Precede any selection with a count to increment by other than %d.]", + DEFAULT_TIMEOUT_INCR); + add_menu_str(win, buf); + } + for (i = 0; (propname = property_by_index(i, &p)) != 0; ++i) { + if (p == HALLUC_RES) { + /* Grayswandir vs hallucination; ought to be redone to + use u.uprops[HALLUC].blocked instead of being treated + as a separate property; letting in be manually toggled + even only in wizard mode would be asking for trouble... */ + continue; + } + if (p == FIRE_RES) { + /* FIRE_RES and properties beyond it (in the propertynames[] + ordering, not their numerical PROP values), can only be + set to timed values here so show a separator */ + add_menu_str(win, "--"); + } + any.a_int = i + 1; /* +1: avoid 0 */ + oldtimeout = u.uprops[p].intrinsic & TIMEOUT; + if (oldtimeout) + Sprintf(buf, "%-27s [%li]", propname, oldtimeout); + else + Sprintf(buf, "%s", propname); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, buf, + MENU_ITEMFLAGS_NONE); + } + end_menu(win, "Which intrinsics?"); + n = select_menu(win, PICK_ANY, &pick_list); + destroy_nhwindow(win); + + for (j = 0; j < n; ++j) { + i = pick_list[j].item.a_int - 1; /* -1: reverse +1 above */ + propname = property_by_index(i, &p); + oldtimeout = u.uprops[p].intrinsic & TIMEOUT; + amt = (pick_list[j].count == -1L) ? DEFAULT_TIMEOUT_INCR + : (int) pick_list[j].count; + if (amt <= 0) /* paranoia */ + continue; + newtimeout = oldtimeout + (long) amt; + + switch (p) { + case SICK: + case SLIMED: + case STONED: + if (oldtimeout > 0L && newtimeout > oldtimeout) + newtimeout = oldtimeout; + break; + } + + switch (p) { + case BLINDED: + make_blinded(newtimeout, TRUE); + break; +#if 0 /* make_confused() only gives feedback when confusion is + * ending so use the 'default' case for it instead */ + case CONFUSION: + make_confused(newtimeout, TRUE); + break; +#endif /*0*/ + case DEAF: + make_deaf(newtimeout, TRUE); + break; + case HALLUC: + make_hallucinated(newtimeout, TRUE, 0L); + break; + case SICK: + typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE; + make_sick(newtimeout, wizintrinsic, TRUE, typ); + break; + case SLIMED: + Sprintf(buf, fmt, + !Slimed ? "" : " still", "turning into slime"); + make_slimed(newtimeout, buf); + break; + case STONED: + Sprintf(buf, fmt, + !Stoned ? "" : " still", "turning into stone"); + make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic); + break; + case STUNNED: + make_stunned(newtimeout, TRUE); + break; + case VOMITING: + Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting"); + make_vomiting(newtimeout, FALSE); + pline1(buf); + break; + case WARN_OF_MON: + if (!Warn_of_mon) { + gc.context.warntype.speciesidx = PM_GRID_BUG; + gc.context.warntype.species + = &mons[gc.context.warntype.speciesidx]; + } + goto def_feedback; + case GLIB: + /* slippery fingers might need a persistent inventory update + so needs more than simple incr_itimeout() but we want + the pline() issued with that */ + make_glib((int) newtimeout); + /*FALLTHRU*/ + default: + def_feedback: + if (p != GLIB) + incr_itimeout(&u.uprops[p].intrinsic, amt); + disp.botl = TRUE; /* have pline() do a status update */ + pline("Timeout for %s %s %d.", propname, + oldtimeout ? "increased by" : "set to", amt); + break; + } + /* this has to be after incr_itimeout() */ + if (p == LEVITATION || p == FLYING) + float_vs_flight(); + else if (p == PROT_FROM_SHAPE_CHANGERS) + rescham(); + } + if (n >= 1) + free((genericptr_t) pick_list); + docrt(); + } else + pline(unavailcmd, ecname_from_fn(wiz_intrinsic)); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +/* #wizrumorcheck command - verify each rumor access */ +int +wiz_rumor_check(void) +{ + rumor_check(); + return ECMD_OK; +} + +/* + * wizard mode sanity_check code + */ + +static const char template[] = "%-27s %4ld %6ld"; +static const char stats_hdr[] = " count bytes"; +static const char stats_sep[] = "--------------------------- ----- -------"; + +static int +size_obj(struct obj *otmp) +{ + int sz = (int) sizeof (struct obj); + + if (otmp->oextra) { + sz += (int) sizeof (struct oextra); + if (ONAME(otmp)) + sz += (int) strlen(ONAME(otmp)) + 1; + if (OMONST(otmp)) + sz += size_monst(OMONST(otmp), FALSE); + if (OMAILCMD(otmp)) + sz += (int) strlen(OMAILCMD(otmp)) + 1; + /* sz += (int) sizeof (unsigned); -- now part of oextra itself */ + } + return sz; +} + +static void +count_obj(struct obj *chain, long *total_count, long *total_size, + boolean top, boolean recurse) +{ + long count, size; + struct obj *obj; + + for (count = size = 0, obj = chain; obj; obj = obj->nobj) { + if (top) { + count++; + size += size_obj(obj); + } + if (recurse && obj->cobj) + count_obj(obj->cobj, total_count, total_size, TRUE, TRUE); + } + *total_count += count; + *total_size += size; +} + +DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE_WARNING follows show_wiz_stats */ + +static void +obj_chain( + winid win, + const char *src, + struct obj *chain, + boolean force, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count = 0L, size = 0L; + + count_obj(chain, &count, &size, TRUE, FALSE); + + if (count || size || force) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +static void +mon_invent_chain( + winid win, + const char *src, + struct monst *chain, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + for (mon = chain; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, TRUE, FALSE); + + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +static void +contained_stats( + winid win, + const char *src, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + count_obj(gi.invent, &count, &size, FALSE, TRUE); + count_obj(fobj, &count, &size, FALSE, TRUE); + count_obj(gl.level.buriedobjlist, &count, &size, FALSE, TRUE); + count_obj(gm.migrating_objs, &count, &size, FALSE, TRUE); + /* DEADMONSTER check not required in this loop since they have no + * inventory */ + for (mon = fmon; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + for (mon = gm.migrating_mons; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +static int +size_monst(struct monst *mtmp, boolean incl_wsegs) +{ + int sz = (int) sizeof (struct monst); + + if (mtmp->wormno && incl_wsegs) + sz += size_wseg(mtmp); + + if (mtmp->mextra) { + sz += (int) sizeof (struct mextra); + if (MGIVENNAME(mtmp)) + sz += (int) strlen(MGIVENNAME(mtmp)) + 1; + if (EGD(mtmp)) + sz += (int) sizeof (struct egd); + if (EPRI(mtmp)) + sz += (int) sizeof (struct epri); + if (ESHK(mtmp)) + sz += (int) sizeof (struct eshk); + if (EMIN(mtmp)) + sz += (int) sizeof (struct emin); + if (EDOG(mtmp)) + sz += (int) sizeof (struct edog); + /* mextra->mcorpsenm doesn't point to more memory */ + } + return sz; +} + +static void +mon_chain( + winid win, + const char *src, + struct monst *chain, + boolean force, + long *total_count, long *total_size) +{ + char buf[BUFSZ]; + long count, size; + struct monst *mon; + /* mon->wormno means something different for migrating_mons and mydogs */ + boolean incl_wsegs = !strcmpi(src, "fmon"); + + count = size = 0L; + for (mon = chain; mon; mon = mon->nmon) { + count++; + size += size_monst(mon, incl_wsegs); + } + if (count || size || force) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); + } +} + +static void +misc_stats( + winid win, + long *total_count, long *total_size) +{ + char buf[BUFSZ], hdrbuf[QBUFSZ]; + long count, size; + int idx; + struct trap *tt; + struct damage *sd; /* shop damage */ + struct kinfo *k; /* delayed killer */ + struct cemetery *bi; /* bones info */ + + /* traps and engravings are output unconditionally; + * others only if nonzero + */ + count = size = 0L; + for (tt = gf.ftrap; tt; tt = tt->ntrap) { + ++count; + size += (long) sizeof *tt; + } + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "traps, size %ld", (long) sizeof (struct trap)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + + count = size = 0L; + engr_stats("engravings, size %ld+text", hdrbuf, &count, &size); + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + + count = size = 0L; + light_stats("light sources, size %ld", hdrbuf, &count, &size); + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + timer_stats("timers, size %ld", hdrbuf, &count, &size); + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (sd = gl.level.damagelist; sd; sd = sd->next) { + ++count; + size += (long) sizeof *sd; + } + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "shop damage, size %ld", + (long) sizeof (struct damage)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + region_stats("regions, size %ld+%ld*rect+N", hdrbuf, &count, &size); + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (k = gk.killer.next; k; k = k->next) { + ++count; + size += (long) sizeof *k; + } + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "delayed killer%s, size %ld", + plur(count), (long) sizeof (struct kinfo)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (bi = gl.level.bonesinfo; bi; bi = bi->next) { + ++count; + size += (long) sizeof *bi; + } + if (count || size) { + *total_count += count; + *total_size += size; + Sprintf(hdrbuf, "bones history, size %ld", + (long) sizeof (struct cemetery)); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } + + count = size = 0L; + for (idx = 0; idx < NUM_OBJECTS; ++idx) + if (objects[idx].oc_uname) { + ++count; + size += (long) (strlen(objects[idx].oc_uname) + 1); + } + if (count || size) { + *total_count += count; + *total_size += size; + Strcpy(hdrbuf, "object type names, text"); + Sprintf(buf, template, hdrbuf, count, size); + putstr(win, 0, buf); + } +} + +static void +you_sanity_check(void) +{ + struct monst *mtmp; + + if (u.uswallow && !u.ustuck) { + /* this probably ought to be panic() */ + impossible("sanity_check: swallowed by nothing?"); + display_nhwindow(WIN_MESSAGE, TRUE); + /* try to recover from whatever the problem is */ + u.uswallow = 0; + u.uswldtim = 0; + docrt(); + } + if ((mtmp = m_at(u.ux, u.uy)) != 0) { + /* u.usteed isn't on the map */ + if (u.ustuck != mtmp) + impossible("sanity_check: you over monster"); + } + + check_wornmask_slots(); + (void) check_invent_gold("invent"); +} + +void +sanity_check(void) +{ + if (iflags.sanity_no_check) { + /* in case a recurring sanity_check warning occurs, we mustn't + re-trigger it when ^P is used, otherwise msg_window:Single + and msg_window:Combination will always repeat the most recent + instance, never able to go back to any earlier messages */ + iflags.sanity_no_check = FALSE; + return; + } + you_sanity_check(); + obj_sanity_check(); + timer_sanity_check(); + mon_sanity_check(); + light_sources_sanity_check(); + bc_sanity_check(); + trap_sanity_check(); + engraving_sanity_check(); +} + +/* qsort() comparison routine for use in list_migrating_mons() */ +static int QSORTCALLBACK +migrsort_cmp(const genericptr vptr1, const genericptr vptr2) +{ + const struct monst *m1 = *(const struct monst **) vptr1, + *m2 = *(const struct monst **) vptr2; + int d1 = (int) m1->mux, l1 = (int) m1->muy, + d2 = (int) m2->mux, l2 = (int) m2->muy; + + /* if different branches, sort by dungeon number */ + if (d1 != d2) + return d1 - d2; + /* within same branch, sort by level number */ + if (l1 != l2) + return l1 - l2; + /* same destination level: use a tie-breaker to force stable sort; + monst->m_id is unsigned so we need more than just simple subtraction */ + return (m1->m_id < m2->m_id) ? -1 : (m1->m_id > m2->m_id); +} + +/* called by #migratemons; displays count of migrating monsters, optionally + displays them as well */ +static void +list_migrating_mons( + d_level *nextlevl) /* default destination for wiz_migrate_mons() */ +{ + winid win = WIN_ERR; + boolean showit = FALSE; + unsigned n; + int xyloc; + coordxy x, y; + char c, prmpt[10], xtra[10], buf[BUFSZ]; + struct monst *mtmp, **marray; + int here = 0, nxtlv = 0, other = 0; + + for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { + if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) + ++here; + else if (mtmp->mux == nextlevl->dnum && mtmp->muy == nextlevl->dlevel) + ++nxtlv; + else + ++other; + } + if (here + nxtlv + other == 0) { + pline("No monsters currently migrating."); + } else { + pline( + "%d mon%s pending for current level, %d for next level, %d for others.", + here, plur(here), nxtlv, other); + prmpt[0] = xtra[0] = '\0'; + (void) strkitten(here ? prmpt : xtra, 'c'); + (void) strkitten(nxtlv ? prmpt : xtra, 'n'); + (void) strkitten(other ? prmpt : xtra, 'o'); + Strcat(prmpt, "a q"); + if (*xtra) + Sprintf(eos(prmpt), "%c%s", '\033', xtra); + c = yn_function("List which?", prmpt, 'q', TRUE); + n = (c == 'c') ? here + : (c == 'n') ? nxtlv + : (c == 'o') ? other + : (c == 'a') ? here + nxtlv + other + : 0; + if (n > 0) { + win = create_nhwindow(NHW_TEXT); + switch (c) { + case 'c': + case 'n': + case 'o': + Sprintf(buf, "Monster%s migrating to %s:", plur(n), + (c == 'c') ? "current level" + : (c == 'n') ? "next level" + : "'other' levels"); + break; + default: + Strcpy(buf, "All migrating monsters:"); + break; + } + putstr(win, 0, buf); + putstr(win, 0, ""); + /* collect the migrating monsters into an array; for 'o' and 'a' + where multiple destination levels might be present, sort by + the destination; 'c' and 'n' don't need to be sorted but we + do that anyway to get the same tie-breaker as 'o' and 'a' */ + marray = (struct monst **) alloc((n + 1) * sizeof *marray); + n = 0; + for (mtmp = gm.migrating_mons; mtmp; mtmp = mtmp->nmon) { + if (c == 'a') + showit = TRUE; + else if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) + showit = (c == 'c'); + else if (mtmp->mux == nextlevl->dnum + && mtmp->muy == nextlevl->dlevel) + showit = (c == 'n'); + else + showit = (c == 'o'); + + if (showit) + marray[n++] = mtmp; + } + marray[n] = (struct monst *) 0; /* mark end for traversal loop */ + if (n > 1) + qsort((genericptr_t) marray, (size_t) n, sizeof *marray, + migrsort_cmp); /* sort elements [0] through [n-1] */ + for (n = 0; (mtmp = marray[n]) != 0; ++n) { + Sprintf(buf, " %s", minimal_monnam(mtmp, FALSE)); + /* minimal_monnam() appends map coordinates; strip that */ + (void) strsubst(buf, " <0,0>", ""); + if (has_mgivenname(mtmp)) /* if mtmp is named, include that */ + Sprintf(eos(buf), " named %s", MGIVENNAME(mtmp)); + if (c == 'o' || c == 'a') + Sprintf(eos(buf), " to %d:%d", mtmp->mux, mtmp->muy); + xyloc = mtmp->mtrack[0].x; /* (for legibility) */ + if (xyloc == MIGR_EXACT_XY) { + x = mtmp->mtrack[1].x; + y = mtmp->mtrack[1].y; + Sprintf(eos(buf), " at <%d,%d>", (int) x, (int) y); + } + putstr(win, 0, buf); + } + free((genericptr_t) marray); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + } else if (c != 'q') { + pline("None."); + } + + } +} + +/* the #stats command + * Display memory usage of all monsters and objects on the level. + */ +int +wiz_show_stats(void) +{ + char buf[BUFSZ]; + winid win; + long total_obj_size, total_obj_count, + total_mon_size, total_mon_count, + total_ovr_size, total_ovr_count, + total_misc_size, total_misc_count; + + win = create_nhwindow(NHW_TEXT); + putstr(win, 0, "Current memory statistics:"); + + total_obj_count = total_obj_size = 0L; + putstr(win, 0, stats_hdr); + Sprintf(buf, " Objects, base size %ld", (long) sizeof (struct obj)); + putstr(win, 0, buf); + obj_chain(win, "invent", gi.invent, TRUE, + &total_obj_count, &total_obj_size); + obj_chain(win, "fobj", fobj, TRUE, &total_obj_count, &total_obj_size); + obj_chain(win, "buried", gl.level.buriedobjlist, FALSE, + &total_obj_count, &total_obj_size); + obj_chain(win, "migrating obj", gm.migrating_objs, FALSE, + &total_obj_count, &total_obj_size); + obj_chain(win, "billobjs", gb.billobjs, FALSE, + &total_obj_count, &total_obj_size); + mon_invent_chain(win, "minvent", fmon, &total_obj_count, &total_obj_size); + mon_invent_chain(win, "migrating minvent", gm.migrating_mons, + &total_obj_count, &total_obj_size); + contained_stats(win, "contained", &total_obj_count, &total_obj_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Obj total", total_obj_count, total_obj_size); + putstr(win, 0, buf); + + total_mon_count = total_mon_size = 0L; + putstr(win, 0, ""); + Sprintf(buf, " Monsters, base size %ld", (long) sizeof (struct monst)); + putstr(win, 0, buf); + mon_chain(win, "fmon", fmon, TRUE, &total_mon_count, &total_mon_size); + mon_chain(win, "migrating", gm.migrating_mons, FALSE, + &total_mon_count, &total_mon_size); + /* 'gm.mydogs' is only valid during level change or end of game disclosure, + but conceivably we've been called from within debugger at such time */ + if (gm.mydogs) /* monsters accompanying hero */ + mon_chain(win, "mydogs", gm.mydogs, FALSE, + &total_mon_count, &total_mon_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Mon total", total_mon_count, total_mon_size); + putstr(win, 0, buf); + + total_ovr_count = total_ovr_size = 0L; + putstr(win, 0, ""); + putstr(win, 0, " Overview"); + overview_stats(win, template, &total_ovr_count, &total_ovr_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Over total", total_ovr_count, total_ovr_size); + putstr(win, 0, buf); + + total_misc_count = total_misc_size = 0L; + putstr(win, 0, ""); + putstr(win, 0, " Miscellaneous"); + misc_stats(win, &total_misc_count, &total_misc_size); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Misc total", total_misc_count, total_misc_size); + putstr(win, 0, buf); + + putstr(win, 0, ""); + putstr(win, 0, stats_sep); + Sprintf(buf, template, " Grand total", + (total_obj_count + total_mon_count + + total_ovr_count + total_misc_count), + (total_obj_size + total_mon_size + + total_ovr_size + total_misc_size)); + putstr(win, 0, buf); + +#if defined(__BORLANDC__) && !defined(_WIN32) + show_borlandc_stats(win); +#endif + + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return ECMD_OK; +} + +RESTORE_WARNING_FORMAT_NONLITERAL + +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) +/* the #wizdispmacros command + * Verify that some display macros are returning sane values */ +int +wiz_display_macros(void) +{ + static const char display_issues[] = "Display macro issues:"; + char buf[BUFSZ]; + winid win; + int glyph, test, trouble = 0, no_glyph = NO_GLYPH, max_glyph = MAX_GLYPH; + + win = create_nhwindow(NHW_TEXT); + + for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { + /* glyph_is_cmap / glyph_to_cmap() */ + if (glyph_is_cmap(glyph)) { + test = glyph_to_cmap(glyph); + /* check for MAX_GLYPH return */ + if (test == no_glyph) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_is_cmap() / glyph_to_cmap(glyph=%d)" + " sync failure, returned NO_GLYPH (%d)", + glyph, test); + putstr(win, 0, buf); + } + if (glyph_is_cmap_zap(glyph) + && !(test >= S_vbeam && test <= S_rslant)) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, + "glyph_is_cmap_zap(glyph=%d) returned non-zap cmap %d", + glyph, test); + putstr(win, 0, buf); + } + /* check against defsyms array subscripts */ + if (!IndexOk(test, defsyms)) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_to_cmap(glyph=%d) returns %d" + " exceeds defsyms[%d] bounds (MAX_GLYPH = %d)", + glyph, test, SIZE(defsyms), max_glyph); + putstr(win, 0, buf); + } + } + /* glyph_is_monster / glyph_to_mon */ + if (glyph_is_monster(glyph)) { + test = glyph_to_mon(glyph); + /* check against mons array subscripts */ + if (test < 0 || test >= NUMMONS) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_to_mon(glyph=%d) returns %d" + " exceeds mons[%d] bounds", + glyph, test, NUMMONS); + putstr(win, 0, buf); + } + } + /* glyph_is_object / glyph_to_obj */ + if (glyph_is_object(glyph)) { + test = glyph_to_obj(glyph); + /* check against objects array subscripts */ + if (test < 0 || test > NUM_OBJECTS) { + if (!trouble++) + putstr(win, 0, display_issues); + Sprintf(buf, "glyph_to_obj(glyph=%d) returns %d" + " exceeds objects[%d] bounds", + glyph, test, NUM_OBJECTS); + putstr(win, 0, buf); + } + } + } + if (!trouble) + putstr(win, 0, "No display macro issues detected."); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return ECMD_OK; +} +#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ + +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) +/* the #wizmondiff command */ +int +wiz_mon_diff(void) +{ + static const char window_title[] = "Review of monster difficulty ratings" + " [index:level]:"; + char buf[BUFSZ]; + winid win; + int mhardcoded = 0, mcalculated = 0, trouble = 0, cnt = 0, mdiff = 0; + int mlev; + struct permonst *ptr; + + /* + * Possible extension: choose between showing discrepancies, + * showing all monsters, or monsters within a particular class. + */ + + win = create_nhwindow(NHW_TEXT); + for (ptr = &mons[0]; ptr->mlet; ptr++, cnt++) { + mcalculated = mstrength(ptr); + mhardcoded = (int) ptr->difficulty; + mdiff = mhardcoded - mcalculated; + if (mdiff) { + if (!trouble++) + putstr(win, 0, window_title); + mlev = (int) ptr->mlevel; + if (mlev > 50) /* hack for named demons */ + mlev = 50; + Snprintf(buf, sizeof buf, + "%-18s [%3d:%2d]: calculated: %2d, hardcoded: %2d (%+d)", + ptr->pmnames[NEUTRAL], cnt, mlev, + mcalculated, mhardcoded, mdiff); + putstr(win, 0, buf); + } + } + if (!trouble) + putstr(win, 0, "No monster difficulty discrepancies were detected."); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return ECMD_OK; +} +#endif /* (NH_DEVEL_STATUS != NH_STATUS_RELEASED) || defined(DEBUG) */ + +/* #migratemons command */ +int +wiz_migrate_mons(void) +{ +#ifdef DEBUG_MIGRATING_MONS + int mcount; + char inbuf[BUFSZ]; + struct permonst *ptr; + struct monst *mtmp; +#endif + d_level tolevel; + + if (Is_stronghold(&u.uz)) + assign_level(&tolevel, &valley_level); + else if (!Is_botlevel(&u.uz)) + get_level(&tolevel, depth(&u.uz) + 1); + else + tolevel.dnum = 0, tolevel.dlevel = 0; + + list_migrating_mons(&tolevel); + +#ifdef DEBUG_MIGRATING_MONS + inbuf[0] = '\033', inbuf[1] = '\0'; + if (tolevel.dnum || tolevel.dlevel) + getlin("How many random monsters to migrate to next level? [0]", + inbuf); + else + pline("Can't get there from here."); + if (*inbuf == '\033') + return ECMD_OK; + + mcount = atoi(inbuf); + if (mcount < 1) + mcount = 0; + else if (mcount > ((COLNO - 1) * ROWNO)) + mcount = (COLNO - 1) * ROWNO; + + while (mcount > 0) { + ptr = rndmonst(); + mtmp = makemon(ptr, 0, 0, MM_NOMSG); + if (mtmp) + migrate_to_level(mtmp, ledger_no(&tolevel), MIGR_RANDOM, + (coord *) 0); + mcount--; + } +#endif /* DEBUG_MIGRATING_MONS */ + return ECMD_OK; +} + +/*wizcmds.c*/ diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC index b7fee8c46..eec43acdd 100644 --- a/sys/msdos/Makefile.GCC +++ b/sys/msdos/Makefile.GCC @@ -1,5 +1,5 @@ # NetHack 3.7 Makefile.GCC $NHDT-Date: 1596498268 2020/08/03 23:44:28 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.53 $ -# Copyright (c) NetHack PC Development Team 1996-2022. +# Copyright (c) NetHack PC Development Team 1996-2024. # PC NetHack 3.7 Makefile for djgpp V2 # # Gnu gcc compiler for msdos (djgpp) @@ -163,6 +163,9 @@ else DLBFLG = endif +# +HACKLIB = $(O)hacklib.a + TERMLIB = # Build NetHack suitable for blind players @@ -269,29 +272,31 @@ DLBOBJ = $(O)dlb.o VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o $(O)attrib.o -VOBJ02 = $(O)ball.o $(O)bones.o $(O)botl.o $(O)cmd.o $(O)date.o $(O)dbridge.o -VOBJ03 = $(O)decl.o $(O)detect.o $(O)dig.o $(O)display.o $(O)do.o -VOBJ04 = $(O)do_name.o $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o -VOBJ05 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o $(O)end.o -VOBJ06 = $(O)engrave.o $(O)exper.o $(O)explode.o $(O)extralev.o $(O)files.o -VOBJ07 = $(O)fountain.o $(O)getline.o $(O)hack.o $(O)hacklib.o $(O)insight.o $(O)invent.o -VOBJ08 = $(O)isaac64.o $(O)lock.o $(O)mail.o $(O)main.o $(O)makemon.o -VOBJ09 = $(O)mcastu.o $(O)mdlib.o $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mkmap.o -VOBJ10 = $(O)mklev.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o $(O)mon.o -VOBJ11 = $(O)mondata.o $(O)monmove.o $(O)monst.o $(O)mplayer.o $(O)mthrowu.o -VOBJ12 = $(O)muse.o $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o -VOBJ13 = $(O)options.o $(O)pickup.o $(O)pline.o $(O)polyself.o $(O)potion.o -VOBJ14 = $(O)quest.o $(O)questpgr.o $(O)pager.o $(O)pray.o $(O)priest.o -VOBJ15 = $(O)read.o $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o -VOBJ16 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o $(O)sfstruct.o -VOBJ17 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o $(O)sp_lev.o -VOBJ18 = $(O)spell.o $(O)steal.o $(O)steed.o $(O)symbols.o $(O)sys.o -VOBJ19 = $(O)teleport.o $(O)termcap.o $(O)timeout.o $(O)topl.o $(O)topten.o -VOBJ20 = $(O)track.o $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)utf8map.o $(O)vault.o -VOBJ21 = $(O)vision.o $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o -VOBJ22 = $(O)wintty.o $(O)wizard.o $(O)worm.o $(O)worn.o $(O)write.o -VOBJ23 = $(O)zap.o $(O)light.o $(O)dlb.o -VOBJ24 = $(REGEX) +VOBJ02 = $(O)ball.o $(O)bones.o $(O)botl.o $(O)calendar.o $(O)cmd.o +VOBJ03 = $(O)coloratt.o $(O)date.o $(O)dbridge.o $(O)decl.o $(O)detect.o +VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o $(O)do_wear.o +VOBJ05 = $(O)dog.o $(O)dogmove.o $(O)dokick.o $(O)dothrow.o $(O)drawing.o +VOBJ06 = $(O)dungeon.o $(O)eat.o $(O)end.o $(O)engrave.o $(O)exper.o +VOBJ07 = $(O)explode.o $(O)extralev.o $(O)files.o $(O)fountain.o $(O)getpos.o +VOBJ08 = $(O)getline.o $(O)hack.o $(O)hacklib.o $(O)insight.o $(O)invent.o +VOBJ09 = $(O)isaac64.o $(O)lock.o $(O)mail.o $(O)main.o $(O)makemon.o +VOBJ10 = $(O)mcastu.o $(O)mdlib.o $(O)mhitm.o $(O)mhitu.o $(O)minion.o +VOBJ11 = $(O)mkmap.o $(O)mklev.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o +VOBJ12 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o $(O)mplayer.o +VOBJ13 = $(O)mthrowu.o $(O)muse.o $(O)music.o $(O)o_init.o $(O)objects.o +VOBJ14 = $(O)objnam.o $(O)options.o $(O)pickup.o $(O)pline.o $(O)polyself.o +VOBJ15 = $(O)potion.o $(O)quest.o $(O)questpgr.o $(O)pager.o $(O)pray.o +VOBJ16 = $(O)priest.o $(O)read.o $(O)rect.o $(O)region.o $(O)report.o +VOBJ17 = $(O)restore.o $(O)rip.o $(O)rnd.o $(O)role.o $(O)rumors.o +VOBJ18 = $(O)save.o $(O)selvar.o $(O)sfstruct.o $(O)shk.o $(O)shknam.o +VOBJ19 = $(O)sit.o $(O)sounds.o $(O)sp_lev.o $(O)spell.o $(O)stairs.o +VOBJ20 = $(O)steal.o $(O)steed.o $(O)symbols.o $(O)sys.o $(O)teleport.o +VOBJ21 = $(O)strutil.o $(O)termcap.o $(O)timeout.o $(O)topl.o $(O)topten.o +VOBJ22 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)utf8map.o $(O)vault.o +VOBJ23 = $(O)track.o $(O)vision.o $(O)weapon.o $(O)were.o $(O)wield.o +VOBJ24 = $(O)windows.o $(O)wintty.o $(O)wizard.o $(O)wizcmds.o $(O)worm.o +VOBJ25 = $(O)worn.o $(O)write.o $(O)zap.o $(O)light.o $(O)dlb.o +VOBJ26 = $(REGEX) SOBJ = $(O)msdos.o $(O)pcsys.o $(O)tty.o $(O)unix.o \ $(O)video.o $(O)vidtxt.o $(O)pckeys.o @@ -313,7 +318,7 @@ VOBJ = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ - $(CURSESOBJ) $(LUAOBJ) + $(VOBJ26) $(CURSESOBJ) $(LUAOBJ) ALLOBJ = $(VOBJ) $(SOBJ) $(TILOBJ) $(TILOBJ2) $(VVOBJ) @@ -609,7 +614,7 @@ endif #========================================== $(GAMEFILE): $(O)obj.tag $(PATCHLEV_H) $(PDCLIB) $(LUATARGETS) \ - $(O)utility.tag $(ALLOBJ) + $(O)utility.tag $(ALLOBJ) $(O)hacklib.a @if exist temp.a del temp.a @ar ruS temp.a $(VOBJ01) @ar ruS temp.a $(VOBJ02) @@ -636,6 +641,7 @@ $(GAMEFILE): $(O)obj.tag $(PATCHLEV_H) $(PDCLIB) $(LUATARGETS) \ @ar ruS temp.a $(VOBJ23) @ar ruS temp.a $(VOBJ24) @ar ruS temp.a $(VOBJ25) + @ar ruS temp.a $(VOBJ26) @ar ruS temp.a $(SOBJ) @ar ruS temp.a $(TILOBJ) @ar ruS temp.a $(TILOBJ2) @@ -674,12 +680,24 @@ $(INCL)/pm.h: $(U)makedefs.exe # Makedefs Stuff #========================================== -$(U)makedefs.exe: $(MAKEDEFSOBJS) - $(LINK) $(LFLAGS) -o$@ $(MAKEDEFSOBJS) +$(U)makedefs.exe: $(MAKEDEFSOBJS) $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(MAKEDEFSOBJS) $(HACKLIB) $(O)makedefs.o: $(CONFIG_H) $(PERMONST_H) $(INCL)/objclass.h \ $(INCL)/sym.h $(INCL)/defsym.h $(U)makedefs.c +#========================================== +# hacklib.a static library +#========================================== + +$(O)hacklib.a: $(O)hacklibu.o + @ar rcs $@ $(O)hacklibu.o +# @ranlib $@ + +$(O)hacklibu.o: $(CONFIG_H) $(SRC)/hacklib.c + $(CC) $(cflags) -I$(SRC) -I$(MSYS) \ + -o$@ $(SRC)/hacklib.c + #========================================== # Recover Utility #========================================== @@ -707,8 +725,8 @@ $(SRC)/tile.c: $(U)tilemap.exe TILEMAPOBJS = $(O)tilemap.o $(O)monst.o $(O)objects.o $(O)drawing.o -$(U)tilemap.exe: $(TILEMAPOBJS) - $(LINK) $(LFLAGS) -o$@ $(TILEMAPOBJS) +$(U)tilemap.exe: $(TILEMAPOBJS) $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(TILEMAPOBJS) $(HACKLIB) $(O)tilemap.o: $(WSHR)/tilemap.c $(HACK_H) $(TILE_H) $(CC) $(cflags) -I$(WSHR) -I$(MSYS) -o$@ $(WSHR)/tilemap.c @@ -726,23 +744,23 @@ $(DAT)/nhtiles.bmp: $(TILEFILES) $(U)tile2bmp.exe @$(subst /,\,$(U)tile2bmp.exe $@) @$(subst /,\,chdir $(SRC)) -$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXTIO) +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXTIO) $(HACKLIB) -rm -f temp.a @ar ru temp.a $(TEXTIO) - $(LINK) $(LFLAGS) -o$@ $(O)tile2bmp.o temp.a + $(LINK) $(LFLAGS) -o$@ $(O)tile2bmp.o temp.a $(HACKLIB) -$(U)tile2bin.exe: $(O)tile2bin.o $(TEXTIO) +$(U)tile2bin.exe: $(O)tile2bin.o $(TEXTIO) $(HACKLIB) -rm -f temp.a @ar ru temp.a $(TEXTIO) - $(LINK) $(LFLAGS) -o$@ $(O)tile2bin.o temp.a + $(LINK) $(LFLAGS) -o$@ $(O)tile2bin.o temp.a $(HACKLIB) -$(U)til2bin2.exe: $(O)til2bin2.o $(TEXTIO2) +$(U)til2bin2.exe: $(O)til2bin2.o $(TEXTIO2) $(HACKLIB) -rm -f temp.a @ar ru temp.a $(TEXTIO2) - $(LINK) $(LFLAGS) -o$@ $(O)til2bin2.o temp.a + $(LINK) $(LFLAGS) -o$@ $(O)til2bin2.o temp.a $(HACKLIB) -$(U)thintile.exe: $(O)thintile.o - $(LINK) $(LFLAGS) -o$@ $(O)thintile.o +$(U)thintile.exe: $(O)thintile.o $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(O)thintile.o $(HACKLIB) $(O)thintile.o: $(HACK_H) $(WSHR)/tile.h $(WSHR)/thintile.c $(CC) $(cflags) -o$@ $(WSHR)/thintile.c @@ -895,8 +913,8 @@ $(DAT)/nhdat: $(U)dlb_main.exe $(DAT)/data $(DAT)/rumors \ @$(subst /,\,$(U)dlb_main cvIf dlb.lst nhdat) @$(subst /,\,cd $(SRC)) -$(U)dlb_main.exe: $(DLBOBJS) - $(LINK) $(LFLAGS) -o$@ $(DLBOBJS) +$(U)dlb_main.exe: $(DLBOBJS) $(HACKLIB) + $(LINK) $(LFLAGS) -o$@ $(DLBOBJS) $(HACKLIB) $(O)dlb_main.o: $(U)dlb_main.c $(INCL)/config.h $(DLB_H) $(CC) $(cflags) -o$@ $(U)dlb_main.c @@ -981,6 +999,7 @@ endif $(subst /,\,if exist $(WSHR)/monthin.txt del $(WSHR)/monthin.txt) $(subst /,\,if exist $(WSHR)/objthin.txt del $(WSHR)/objthin.txt) $(subst /,\,if exist $(WSHR)/oththin.txt del $(WSHR)/oththin.txt) + $(subst /,\,if exist $(O)hacklib.a del $(O)hacklib.a) #========================================== # Create directory for holding object files diff --git a/sys/share/cppregex.cpp b/sys/share/cppregex.cpp index 75cfa6123..4d45928c7 100644 --- a/sys/share/cppregex.cpp +++ b/sys/share/cppregex.cpp @@ -10,7 +10,7 @@ extern "C" { #include "config.h" #define CPPREGEX_C -#include "extern.h" +//#include "extern.h" } // extern "C" diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index f682444f8..b1948d126 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -188,6 +188,9 @@ SYSOBJ = $(TARGETPFX)ioctl.o $(TARGETPFX)unixmain.o $(TARGETPFX)unixtty.o \ #CFLAGS = -O -I../include #LFLAGS = +AR = ar +ARFLAGS = rcs + # The Qt and Be window systems are written in C++, while the rest of # NetHack is standard C. If using Qt, uncomment the LINK line here to get # the C++ libraries linked in. @@ -210,6 +213,8 @@ TARGET_LFLAGS = $(LFLAGS) TARGET_CXX = $(CXX) TARGET_CXXFLAGS = $(CXXFLAGS) TARGET_LIBS = $(LIBS) +TARGET_AR = $(AR) +TARGET_ARFLAGS = $(ARFLAGS) # we specify C preprocessor flags via CFLAGS; files built with default rules # might include $(CPPFLAGS) which could get a value from user's environment; @@ -478,6 +483,10 @@ AT = $(AT_V$(QUIETCC)) PREGAME=@true PACKAGE=@true +HACKLIBSRC = hacklib.c +HACKLIBOBJS = hacklib.o +HACKLIB = hacklib.a + MAKEDEFS = ../util/makedefs # -lm required by lua @@ -493,24 +502,23 @@ HACK_H = ../src/hack.h-t # all .c that are part of the main NetHack program and are not operating- or # windowing-system specific. Do not include date.c in this list. HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ - botl.c coloratt.c cmd.c dbridge.c decl.c detect.c dig.c display.c \ - dlb.c do.c \ - do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c drawing.c \ - dungeon.c eat.c end.c engrave.c exper.c explode.c extralev.c \ - files.c fountain.c hack.c hacklib.c \ - insight.c invent.c isaac64.c light.c \ + botl.c calendar.c cmd.c coloratt.c dbridge.c decl.c detect.c dig.c display.c \ + dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c \ + drawing.c dungeon.c eat.c end.c engrave.c exper.c explode.c \ + extralev.c files.c fountain.c hack.c hacklib.c \ + getpos.c insight.c invent.c isaac64.c light.c \ lock.c mail.c makemon.c mcastu.c mdlib.c mhitm.c \ mhitu.c minion.c mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c \ mondata.c monmove.c monst.c mplayer.c mthrowu.c muse.c music.c \ nhlua.c nhlsel.c nhlobj.c nhmd4.c o_init.c objects.c objnam.c \ options.c pager.c pickup.c pline.c polyself.c potion.c pray.c \ - priest.c quest.c questpgr.c read.c rect.c region.c restore.c \ - rip.c rnd.c role.c rumors.c save.c sfstruct.c \ - shk.c shknam.c sit.c sounds.c \ - sp_lev.c spell.c steal.c steed.c symbols.c sys.c teleport.c \ + priest.c quest.c questpgr.c read.c rect.c region.c report.c restore.c \ + rip.c rnd.c role.c rumors.c save.c selvar.c sfstruct.c \ + shk.c shknam.c sit.c sounds.c sp_lev.c spell.c stairs.c steal.c steed.c \ + strutil.c symbols.c sys.c teleport.c \ timeout.c topten.c track.c trap.c u_init.c utf8map.c \ uhitm.c vault.c version.c vision.c weapon.c were.c wield.c \ - windows.c wizard.c worm.c worn.c write.c zap.c + windows.c wizard.c wizcmds.c worm.c worn.c write.c zap.c # all operating-system-dependent .c (for dependencies and such) SYSCSRC = ../sys/share/pcmain.c ../sys/share/pcsys.c \ @@ -536,10 +544,10 @@ CHAINOBJ = $(TARGETPFX)wc_chainin.o $(TARGETPFX)wc_chainout.o \ $(TARGETPFX)wc_trace.o # .c files for this version (for date.h) -VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(CHAINSRC) $(GENCSRC) +VERSOURCES = $(HACKCSRC) $(HACKLIBSRC) $(SYSSRC) $(WINSRC) $(CHAINSRC) $(GENCSRC) # .c files for all versions using this Makefile (for lint and tags) -CSOURCES = $(HACKCSRC) $(SYSCSRC) $(WINCSRC) $(CHAINSRC) $(GENCSRC) +CSOURCES = $(HACKCSRC) $(HACKLIBSRC) $(SYSCSRC) $(WINCSRC) $(CHAINSRC) $(GENCSRC) # all .h files except date.h, which would @@ -552,8 +560,8 @@ HACKINCL = align.h artifact.h artilist.h attrib.h botl.h \ func_tab.h global.h warnings.h hack.h lint.h mextra.h mfndpos.h \ micro.h mkroom.h monattk.h mondata.h monflag.h monst.h monsters.h \ nhmd4.h obj.h objects.h objclass.h optlist.h patchlevel.h pcconf.h \ - permonst.h prop.h rect.h region.h sym.h defsym.h rm.h sp_lev.h \ - spell.h sndprocs.h seffects.h sys.h tcap.h timeout.h \ + permonst.h prop.h rect.h region.h selvar.h sym.h defsym.h rm.h sp_lev.h \ + spell.h sndprocs.h seffects.h stairs.h sys.h tcap.h timeout.h \ tradstdc.h trap.h unixconf.h vision.h vmsconf.h wintty.h wincurs.h \ winX.h winprocs.h wintype.h you.h youprop.h cstd.h @@ -569,8 +577,8 @@ HOSTOBJ = $(FIRSTOBJ) alloc.o drawing.o HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)apply.o $(TARGETPFX)artifact.o $(TARGETPFX)attrib.o \ $(TARGETPFX)ball.o $(TARGETPFX)bones.o $(TARGETPFX)botl.o \ - $(TARGETPFX)coloratt.o \ - $(TARGETPFX)cmd.o $(TARGETPFX)dbridge.o $(TARGETPFX)decl.o \ + $(TARGETPFX)calendar.o \ + $(TARGETPFX)cmd.o $(TARGETPFX)coloratt.o $(TARGETPFX)dbridge.o $(TARGETPFX)decl.o \ $(TARGETPFX)detect.o $(TARGETPFX)dig.o $(TARGETPFX)display.o \ $(TARGETPFX)dlb.o $(TARGETPFX)do.o $(TARGETPFX)do_name.o \ $(TARGETPFX)do_wear.o $(TARGETPFX)dog.o $(TARGETPFX)dogmove.o \ @@ -578,7 +586,7 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)dungeon.o $(TARGETPFX)eat.o $(TARGETPFX)end.o \ $(TARGETPFX)engrave.o $(TARGETPFX)exper.o $(TARGETPFX)explode.o \ $(TARGETPFX)extralev.o $(TARGETPFX)files.o $(TARGETPFX)fountain.o \ - $(TARGETPFX)hack.o $(TARGETPFX)hacklib.o $(TARGETPFX)insight.o \ + $(TARGETPFX)getpos.o $(TARGETPFX)hack.o $(TARGETPFX)insight.o \ $(TARGETPFX)invent.o $(TARGETPFX)isaac64.o $(TARGETPFX)light.o \ $(TARGETPFX)lock.o $(TARGETPFX)mail.o $(TARGETPFX)makemon.o \ $(TARGETPFX)mcastu.o $(TARGETPFX)mdlib.o \ @@ -594,22 +602,23 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)pickup.o $(TARGETPFX)pline.o $(TARGETPFX)polyself.o \ $(TARGETPFX)potion.o $(TARGETPFX)pray.o $(TARGETPFX)priest.o \ $(TARGETPFX)quest.o $(TARGETPFX)questpgr.o $(TARGETPFX)read.o \ - $(TARGETPFX)rect.o $(TARGETPFX)region.o $(TARGETPFX)restore.o \ - $(TARGETPFX)rip.o $(TARGETPFX)rnd.o $(TARGETPFX)role.o \ - $(TARGETPFX)rumors.o $(TARGETPFX)save.o $(TARGETPFX)sfstruct.o \ + $(TARGETPFX)rect.o $(TARGETPFX)region.o $(TARGETPFX)report.o \ + $(TARGETPFX)restore.o $(TARGETPFX)rip.o $(TARGETPFX)rnd.o \ + $(TARGETPFX)role.o $(TARGETPFX)rumors.o $(TARGETPFX)save.o \ + $(TARGETPFX)selvar.o $(TARGETPFX)sfstruct.o \ $(TARGETPFX)shk.o $(TARGETPFX)shknam.o $(TARGETPFX)sit.o \ - $(TARGETPFX)sounds.o $(TARGETPFX)sp_lev.o $(TARGETPFX)spell.o \ + $(TARGETPFX)sounds.o $(TARGETPFX)sp_lev.o $(TARGETPFX)spell.o $(TARGETPFX)stairs.o \ $(TARGETPFX)symbols.o $(TARGETPFX)sys.o $(TARGETPFX)steal.o \ - $(TARGETPFX)steed.o $(TARGETPFX)teleport.o $(TARGETPFX)timeout.o \ - $(TARGETPFX)topten.o $(TARGETPFX)track.o $(TARGETPFX)trap.o \ + $(TARGETPFX)steed.o $(TARGETPFX)strutil.o $(TARGETPFX)teleport.o \ + $(TARGETPFX)timeout.o $(TARGETPFX)topten.o $(TARGETPFX)track.o $(TARGETPFX)trap.o \ $(TARGETPFX)u_init.o $(TARGETPFX)uhitm.o $(TARGETPFX)utf8map.o \ $(TARGETPFX)vault.o $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ $(TARGETPFX)were.o $(TARGETPFX)wield.o $(TARGETPFX)windows.o \ - $(TARGETPFX)wizard.o $(TARGETPFX)worm.o $(TARGETPFX)worn.o \ + $(TARGETPFX)wizard.o $(TARGETPFX)wizcmds.o $(TARGETPFX)worm.o $(TARGETPFX)worn.o \ $(TARGETPFX)write.o $(TARGETPFX)zap.o \ $(REGEXOBJ) $(RANDOBJ) $(SYSOBJ) $(WINOBJ) $(HINTOBJ) $(SNDLIBOBJ) \ $(TARGETPFX)version.o -# + DATE_O = $(TARGETPFX)date.o # the .o files from the HACKCSRC, SYSSRC, and WINSRC lists @@ -624,41 +633,46 @@ pregame: $(GAME): pregame $(MAKEDEFS) $(LUALIB) $(WAVS) $(SYSTEM) @echo "$(GAME) is up to date." -Sysunix: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Sysunix: $(HOSTOBJ) $(HOBJ) $(TARGETPFX)$(HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(TARGET_LIBS) $(LUALIBS) \ - $(AUTOLIBS) + $(HOBJ) $(DATE_O) $(TARGETPFX)$(HACKLIB) $(WINLIB) \ + $(TARGET_LIBS) $(LUALIBS) $(AUTOLIBS) @touch Sysunix -Sys3B2: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Sys3B2: $(HOSTOBJ) $(HOBJ) $(TARGETPFX)$(HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(LUALIBS) -lmalloc + $(HOBJ) $(DATE_O) $(TARGETPFX)$(HACKLIB) $(WINLIB) \ + $(LUALIBS) -lmalloc @touch Sys3B2 -Sysatt: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Sysatt: $(HOSTOBJ) $(HOBJ) $(TARGETPFX)$(HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Loading $(GAME)." $(AT)$(LD) $(TARGET_LFLAGS) /lib/crt0s.o /lib/shlib.ifile \ - -o $(GAMEBIN) $(HOSTOBJ) $(HOBJ) $(DATE_O) $(LUALIBS) + -o $(GAMEBIN) $(HOSTOBJ) $(HOBJ) $(DATE_O) $(TARGETPFX)$(HACKLIB) \ + $(LUALIBS) @touch Sysatt -Systos: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +Systos: $(HOSTOBJ) $(HOBJ) $(TARGETPFX)$(HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(LUALIBS) + $(HOBJ) $(DATE_O) $(TARGETPFX)$(HACKLIB) \ + $(WINLIB) $(LUALIBS) @touch Systos -SysV-AT: DUMB.Setup $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +SysV-AT: DUMB.Setup $(HOSTOBJ) $(HOBJ) $(TARGETPFX)$(HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(LUALIBS) + $(HOBJ) $(DATE_O) $(TARGETPFX)$(HACKLIB) \ + $(WINLIB) $(LUALIBS) @touch SysV-AT -SysBe: $(HOSTOBJ) $(HOBJ) $(DATE_O) $(BUILDMORE) Makefile +SysBe: $(HOSTOBJ) $(HOBJ) $(TARGETPFX)$(HACKLIB) $(DATE_O) $(BUILDMORE) Makefile @echo "Linking $(GAME)." $(AT)$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAME) \ - $(HOBJ) $(DATE_O) $(WINLIB) $(TARGET_LIBS) $(LUALIBS) + $(HOBJ) $(DATE_O) $(TARGETPFX)$(HACKLIB) \ + $(WINLIB) $(TARGET_LIBS) $(LUALIBS) @xres -o $(GAME) ../win/BeOS/nethack.rsrc @mimeset -f $(GAME) @touch SysBe @@ -730,6 +744,12 @@ $(MAKEDEFS): $(FIRSTOBJ) \ tile.c: ../win/share/tilemap.c $(HACK_H) @( cd ../util ; $(MAKE) ../src/tile.c ) +# +# hacklib (a library of utility routines) +# + +hacklib.a: hacklib.o + $(AR) $(ARFLAGS) $@ hacklib.o ../win/gnome/gn_rip.h: ../win/X11/rip.xpm cp ../win/X11/rip.xpm ../win/gnome/gn_rip.h @@ -738,7 +758,7 @@ $(TARGETPFX)sfstruct.o: sfstruct.c $(HACK_H) # date.c should be recompiled any time any of the source or include code # is modified. -$(TARGETPFX)date.o: date.c $(HACK_H) $(HACKCSRC) $(HOBJ) +$(TARGETPFX)date.o: date.c $(HACK_H) $(HACKCSRC) $(HOBJ) $(TARGETPFX)$(HACKLIB) $(TARGET_CC) $(TARGET_CFLAGS) $(GITHASH) $(GITBRANCH) -c -o $@ date.c # date.h should be remade any time any of the source or include code @@ -774,7 +794,7 @@ clean: true; $(CLEANMORE) spotless: clean - -rm -f a.out core $(GAMEBIN) Sys* + -rm -f a.out core $(HACKLIB) $(GAMEBIN) Sys* -rm -f ../include/nhlua.h -rm -f ../include/date.h #created but no longer used, at least by core -rm -f ../include/onames.h ../include/pm.h #obsolete generated files diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index c51e1991a..25b1f65e8 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -189,7 +189,12 @@ CALLOC = ../src/alloc.c panic.c OALLOC = $(OBJDIR)/alloc.o panic.o # build time info CDATE = ../src/date.c -ODATE = $(OBJDIR)/date.o +ODATE = $(OBJDIR)/date.o + +# hacklib utility routines +HACKLIB = $(OBJDIR)/hacklib.a +HACKLIBSRC = ../src/hacklib.c +HACKLIBOBJ = $(OBJDIR)/hacklib.o panic.o # object files for makedefs MAKEOBJS = makedefs.o $(OMONOBJ) $(ODATE) $(OALLOC) @@ -213,11 +218,12 @@ TARGET_CLINK = $(CLINK) TARGET_LFLAGS = $(LFLAGS) TARGET_CXX = $(CXX) TARGET_CXXFLAGS = $(CXXFLAGS) +TARGET_AR = $(AR) # dependencies for makedefs # -makedefs: $(MAKEOBJS) mdgrep.h - $(CLINK) $(LFLAGS) -o makedefs $(MAKEOBJS) +makedefs: $(HACKLIB) $(MAKEOBJS) mdgrep.h + $(CLINK) $(LFLAGS) -o makedefs $(MAKEOBJS) $(HACKLIB) # note: the headers listed here are maintained manually rather than via @@ -233,6 +239,8 @@ makedefs.o: makedefs.c ../src/mdlib.c $(CONFIG_H) \ ../include/dlb.h ../include/patchlevel.h mdgrep.h $(CC) $(CFLAGS) $(CSTD) -c makedefs.c -o $@ +$(OBJDIR)/hacklib.o: $(HACKLIBSRC) + # Don't require perl to build; that is why mdgrep.h is spelled wrong below. mdgreph: mdgrep.pl perl mdgrep.pl @@ -252,6 +260,9 @@ lintdefs: ../include/date.h:: @( cd ../src ; $(MAKE) ../include/date.h ) +$(HACKLIB): $(CONFIG_H) $(HACKLIBSRC) panic.o + @( cd ../src ; $(MAKE) hacklib.a ) + # support code used by several of the utility programs (but not makedefs) panic.o: panic.c $(CONFIG_H) $(CC) $(CFLAGS) $(CSTD) -c panic.c -o $@ @@ -275,8 +286,8 @@ $(TARGETPFX)recover.o: recover.c $(CONFIG_H) # dependencies for dlb # -dlb: $(DLBOBJS) - $(CLINK) $(LFLAGS) -o dlb $(DLBOBJS) $(LIBS) +dlb: $(DLBOBJS) $(HACKLIB) + $(CLINK) $(LFLAGS) -o dlb $(DLBOBJS) $(HACKLIB) $(LIBS) dlb_main.o: dlb_main.c $(CONFIG_H) ../include/dlb.h $(CC) $(CFLAGS) $(CSTD) -c dlb_main.c -o $@ @@ -295,30 +306,34 @@ gif2txt: $(GIFREADERS) $(TEXT_IO) txt2ppm: $(PPMWRITERS) $(TEXT_IO) $(CLINK) $(LFLAGS) -o txt2ppm $(PPMWRITERS) $(TEXT_IO) $(LIBS) -tile2x11: tile2x11.o $(TEXT_IO) - $(CLINK) $(LFLAGS) -o tile2x11 tile2x11.o $(TEXT_IO) $(LIBS) +tile2x11: tile2x11.o $(TEXT_IO) $(HACKLIB) + $(CLINK) $(LFLAGS) -o tile2x11 tile2x11.o $(TEXT_IO) \ + $(HACKLIB) $(LIBS) tile2img.ttp: tile2img.o bitmfile.o $(TEXT_IO) $(CLINK) $(LFLAGS) -o tile2img.ttp tile2img.o bitmfile.o \ $(TEXT_IO) $(LIBS) -tile2bmp: tile2bmp.o $(TEXT_IO) - $(CLINK) $(LFLAGS) -o tile2bmp tile2bmp.o $(TEXT_IO) +tile2bmp: tile2bmp.o $(TEXT_IO) $(HACKLIB) + $(CLINK) $(LFLAGS) -o tile2bmp tile2bmp.o $(TEXT_IO) $(HACKLIB) xpm2img.ttp: xpm2img.o bitmfile.o $(CLINK) $(LFLAGS) -o xpm2img.ttp xpm2img.o bitmfile.o $(LIBS) -tile2beos: tile2beos.o $(TEXT_IO) - $(CXXLINK) $(LFLAGS) -o tile2beos tile2beos.o $(TEXT_IO) -lbe +tile2beos: tile2beos.o $(TEXT_IO) $(HACKLIB) + $(CXXLINK) $(LFLAGS) -o tile2beos tile2beos.o \ + $(TEXT_IO) $(HACKLIB) -lbe #--compiling and linking in one step leaves extra debugging files (in their # own subdirectories!) on OSX; compile and link separately to suppress # that without mucking about with extra OS-specific CFLAGS and/or LFLAGS #tilemap: ../win/share/tilemap.c $(HACK_H) # $(CC) $(CFLAGS) $(LFLAGS) -o tilemap ../win/share/tilemap.c $(LIBS) -tilemap: tilemap.o $(OBJDIR)/objects.o $(OBJDIR)/monst.o $(OBJDIR)/drawing.o +tilemap: tilemap.o $(OBJDIR)/objects.o $(OBJDIR)/monst.o $(OBJDIR)/drawing.o \ + $(HACKLIB) $(CLINK) $(LFLAGS) -o tilemap tilemap.o $(OBJDIR)/objects.o \ - $(OBJDIR)/monst.o $(OBJDIR)/drawing.o $(LIBS) + $(OBJDIR)/monst.o $(OBJDIR)/drawing.o $(HACKLIB) \ + $(LIBS) ../src/tile.c: tilemap ./tilemap @@ -426,6 +441,6 @@ clean: clean-fixup -rm -f *.o spotless: clean - -rm -f makedefs recover dlb + -rm -f makedefs recover dlb $(HACKLIB) -rm -f gif2txt txt2ppm tile2x11 tile2img.ttp xpm2img.ttp \ tilemap tileedit tile2bmp uudecode diff --git a/sys/unix/hints/include/cross-post.370 b/sys/unix/hints/include/cross-post.370 index 17f44fdb7..fced108ad 100644 --- a/sys/unix/hints/include/cross-post.370 +++ b/sys/unix/hints/include/cross-post.370 @@ -22,9 +22,9 @@ $(TARGETPFX)tile.o : tile.c $(TARGETPFX)exceptn.o : ../lib/djgpp/djgpp-patch/src/libc/go32/exceptn.S $(TARGET_CC) -c -o $@ ../lib/djgpp/djgpp-patch/src/libc/go32/exceptn.S $(TARGET_AR) ru ../lib/djgpp/i586-pc-msdosdjgpp/lib/libc.a $(TARGETPFX)exceptn.o -$(GAMEBIN) : $(HOBJ) $(LUACROSSLIB) +$(GAMEBIN) : $(HOBJ) $(HACKLIB) $(LUACROSSLIB) $(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \ - $(HOBJ) $(WINLIB) $(TARGET_LIBS) + $(HOBJ) $(HACKLIB) $(WINLIB) $(TARGET_LIBS) $(DOSFONT)/ter-u16b.psf: $(FONTTOP)/ter-u16b.bdf $(DOSFONT)/nh-u16b.bdf $(DOSFONT)/makefont.lua $(LUABIN) $(LUABIN) $(DOSFONT)/makefont.lua $(FONTTOP)/ter-u16b.bdf $(DOSFONT)/nh-u16b.bdf $@ $(DOSFONT)/ter-u16v.psf: $(FONTTOP)/ter-u16v.bdf $(DOSFONT)/nh-u16v.bdf $(DOSFONT)/makefont.lua $(LUABIN) @@ -41,6 +41,8 @@ $(DOSFONT)/ter-u28b.psf: $(FONTTOP)/ter-u28b.bdf $(DOSFONT)/nh-u28b.bdf $(DOSFON $(LUABIN) $(DOSFONT)/makefont.lua $(FONTTOP)/ter-u28b.bdf $(DOSFONT)/nh-u28b.bdf $@ $(DOSFONT)/ter-u32b.psf: $(FONTTOP)/ter-u32b.bdf $(DOSFONT)/nh-u32b.bdf $(DOSFONT)/makefont.lua $(LUABIN) $(LUABIN) $(DOSFONT)/makefont.lua $(FONTTOP)/ter-u32b.bdf $(DOSFONT)/nh-u32b.bdf $@ +$(TARGETPFX)hacklib.a: $(TARGETPFX)hacklib.o + $(TARGET_AR) $(TARGET_ARFLAGS) $@ $(TARGETPFX)hacklib.o # .PHONY: dodata dospkg dosfonts dosfonts: $(FONTTARGETS) diff --git a/sys/unix/hints/include/cross-pre1.370 b/sys/unix/hints/include/cross-pre1.370 new file mode 100644 index 000000000..203c06048 --- /dev/null +++ b/sys/unix/hints/include/cross-pre1.370 @@ -0,0 +1,49 @@ +#===============-================================================= +# NetHack 3.7 include/cross-pre1. $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ +# +# Cross-compiling -PRE section 1 +# + +ifdef CROSS_TO_MSDOS +CROSS=1 +BUILD_TARGET_LUA=1 +BUILD_PDCURSES=1 +CROSS_SHARED=1 +override TARGET = msdos +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + +ifdef CROSS_TO_WASM +CROSS=1 +BUILD_TARGET_LUA=1 +HACKDIR=/ +PREFIX= +override TARGET = wasm +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + +ifdef CROSS_TO_MIPS +CROSS=1 +BUILD_TARGET_LUA=1 +BUILD_TARGET_NCURSES=1 +HACKDIR=/ +PREFIX= +override TARGET = mips +override TARGETDIR=../targets/$(TARGET) +override TARGETPFX = $(TARGETDIR)/ +override TARGET_LIBS= +endif + +ifdef CROSS +override PREGAME= +override BUILDMORE= +override CLEANMORE= +override PACKAGE= +endif +# End of cross-compiling -PRE section 1 +#===============-================================================= + diff --git a/sys/unix/hints/include/cross-pre.370 b/sys/unix/hints/include/cross-pre2.370 similarity index 94% rename from sys/unix/hints/include/cross-pre.370 rename to sys/unix/hints/include/cross-pre2.370 index 04b0104e8..2b1c9ef91 100644 --- a/sys/unix/hints/include/cross-pre.370 +++ b/sys/unix/hints/include/cross-pre2.370 @@ -1,50 +1,9 @@ #===============-================================================= -# NetHack 3.7 include/cross-pre $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ +# NetHack 3.7 include/cross-pre2 $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ # -# Cross-compiling -PRE section +# Cross-compiling -PRE section 2 # -ifdef CROSS_TO_MSDOS -CROSS=1 -BUILD_TARGET_LUA=1 -BUILD_PDCURSES=1 -CROSS_SHARED=1 -override TARGET = msdos -override TARGETDIR=../targets/$(TARGET) -override TARGETPFX = $(TARGETDIR)/ -override TARGET_LIBS= -endif - -ifdef CROSS_TO_WASM -CROSS=1 -BUILD_TARGET_LUA=1 -HACKDIR=/ -PREFIX= -override TARGET = wasm -override TARGETDIR=../targets/$(TARGET) -override TARGETPFX = $(TARGETDIR)/ -override TARGET_LIBS= -endif - -ifdef CROSS_TO_MIPS -CROSS=1 -BUILD_TARGET_LUA=1 -BUILD_TARGET_NCURSES=1 -HACKDIR=/ -PREFIX= -override TARGET = mips -override TARGETDIR=../targets/$(TARGET) -override TARGETPFX = $(TARGETDIR)/ -override TARGET_LIBS= -endif - -ifdef CROSS -override PREGAME= -override BUILDMORE= -override CLEANMORE= -override PACKAGE= -endif - ifdef BUILD_TARGET_LUA #===============-================================================= # LUA library diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index b32d26fc0..19db7e4ad 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -27,7 +27,7 @@ ifeq ($(MAKELEVEL),0) PRECHECK+=checkmakefiles # all files included from this hints file get listed # in HINTSINCLNAMES (without suffix and without a path) -HINTSINCLNAMES := compiler cross-pre cross-post \ +HINTSINCLNAMES := compiler cross-pre1 cross-pre2 cross-post \ gbdates-pre gbdates-post \ multiw-1 multiw-2 misc \ multisnd1-pre multisnd2-pre multisnd-post @@ -43,6 +43,10 @@ endif GAMEUID = $(USER) GAMEGRP = games +#----------------------------------------------------------------------------- +#-INCLUDE cross-pre1.370 + + #----------------------------------------------------------------------------- # You shouldn't need to change anything below here (in the hints file; if # you're reading this in Makefile augmented by hints, that may not be true). @@ -366,7 +370,7 @@ CHGRP=true # manpages directory MANDIR=/usr/share/man/man6 # -#-INCLUDE cross-pre.370 +#-INCLUDE cross-pre2.370 # # diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index c031cdc69..4a5d59816 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -27,7 +27,7 @@ ifeq ($(MAKELEVEL),0) PRECHECK+=checkmakefiles # all files included from this hints file get listed # in HINTSINCLNAMES (without suffix and without a path) -HINTSINCLNAMES := compiler cross-pre cross-post \ +HINTSINCLNAMES := compiler cross-pre1 cross-pre2 cross-post \ gbdates-pre gbdates-post \ multiw-1 multiw-2 misc \ multisnd1-pre multisnd2-pre multisnd-post @@ -55,6 +55,8 @@ endif # you're reading this in Makefile augmented by hints, that may not be true). # +#-INCLUDE cross-pre1.370 + #-INCLUDE multiw-2.370 # compiler.370 contains compiler detection and adjustments common @@ -427,7 +429,7 @@ SYSCONFENSURE = (if ! test -f $(INSTDIR)/sysconf ; then \ # Install.Qt mentions a patch for macos - it's not there (it seems to be in the Qt binary # package under the docs directory). # -#-INCLUDE cross-pre.370 +#-INCLUDE cross-pre2.370 # # #-INCLUDE gbdates-pre.370 diff --git a/sys/vms/Makefile.src b/sys/vms/Makefile.src index a0b95262b..8ccd977a3 100644 --- a/sys/vms/Makefile.src +++ b/sys/vms/Makefile.src @@ -147,23 +147,22 @@ HACK_H = $(SRC)hack.h-t # all .c that are part of the main NetHack program and are not operating- or # windowing-system specific HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ - botl.c cmd.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c \ - do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c drawing.c \ - dungeon.c eat.c end.c engrave.c exper.c explode.c extralev.c \ - files.c fountain.c hack.c hacklib.c \ + botl.c calendar.c cmd.c coloratt.c dbridge.c decl.c detect.c dig.c \ + display.c dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c \ + dothrow.c drawing.c dungeon.c eat.c end.c engrave.c exper.c \ + explode.c extralev.c files.c fountain.c getpos.c hack.c hacklib.c \ insight.c invent.c light.c lock.c \ mail.c makemon.c mcastu.c mhitm.c mhitu.c minion.c \ - mklev.c mkmap.c \ - mkmaze.c mkobj.c mkroom.c mon.c mondata.c monmove.c monst.c \ - mplayer.c mthrowu.c muse.c music.c o_init.c objects.c objnam.c \ - options.c pager.c pickup.c pline.c polyself.c potion.c pray.c \ - priest.c quest.c questpgr.c read.c rect.c region.c restore.c \ - rip.c rnd.c role.c \ - rumors.c save.c shk.c shknam.c sit.c sounds.c sp_lev.c spell.c \ - steal.c steed.c sys.c teleport.c timeout.c topten.c track.c \ - trap.c u_init.c \ - uhitm.c vault.c version.c vision.c weapon.c were.c wield.c \ - windows.c wizard.c worm.c worn.c write.c zap.c + mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c mondata.c \ + monmove.c monst.c mplayer.c mthrowu.c muse.c music.c o_init.c \ + objects.c objnam.c options.c pager.c pickup.c pline.c polyself.c \ + potion.c pray.c priest.c quest.c questpgr.c read.c rect.c \ + region.c report.c restore.c rip.c rnd.c role.c rumors.c save.c \ + selvar.c sfstruct.c shk.c shknam.c sit.c sounds.c sp_lev.c \ + spell.c stairs.c steal.c steed.c strutil.c sys.c teleport.c \ + timeout.c topten.c track.c trap.c u_init.c uhitm.c vault.c \ + version.c vision.c weapon.c were.c wield.c windows.c wizard.c \ + wizcmds.c worm.c worn.c write.c zap.c #-# generated source files (vis_tab.c is gone; tile.c is handled separately #-# via WINxxxSRC) @@ -178,7 +177,7 @@ VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(RANDSRC) $(GENCSRC) HACKINCL = align.h artifact.h artilist.h attrib.h color.h \ config.h config1.h context.h coord.h decl.h defsym.h display.h \ dlb.h dungeon.h engrave.h extern.h flag.h func_tab.h global.h \ - hack.h mextra.h mfndpos.h micro.h mkroom.h \ + hack.h hacklib.h mextra.h mfndpos.h micro.h mkroom.h \ monattk.h mondata.h monflag.h monst.h sym.h obj.h objclass.h \ patchlevel.h pcconf.h permonst.h prop.h rect.h \ region.h sym.h defsym.h rm.h sp_lev.h spell.h sys.h system.h \ @@ -192,11 +191,12 @@ FIRSTOBJ = vmsmisc.obj,vmsfiles.obj,monst.obj,objects.obj # split up long list so that we can write pieces of it into nethack.opt HOBJ1 = allmain.obj,alloc.obj,apply.obj,artifact.obj,attrib.obj, \ - ball.obj,bones.obj,botl.obj,cmd.obj,dbridge.obj,decl.obj, \ - detect.obj,dig.obj,display.obj,dlb.obj,do.obj,do_name.obj,do_wear.obj + ball.obj,bones.obj,botl.obj,calendar.obj,cmd.obj,coloratt.obj, \ + dbridge.obj,decl.obj,detect.obj,dig.obj,display.obj,dlb.obj, \ + do.obj,do_name.obj,do_wear.obj HOBJ2 = dog.obj,dogmove.obj,dokick.obj,dothrow.obj,drawing.obj, \ dungeon.obj,eat.obj,end.obj,engrave.obj,exper.obj,explode.obj, \ - extralev.obj,files.obj,fountain.obj,hack.obj,hacklib.obj, \ + extralev.obj,files.obj,fountain.obj,getpos.obj,hack.obj,hacklib.obj, \ insight.obj,invent.obj HOBJ3 = light.obj,lock.obj,mail.obj,makemon.obj,mcastu.obj, \ mhitm.obj,mhitu.obj,minion.obj,mklev.obj,mkmap.obj,mkmaze.obj, \ @@ -205,11 +205,11 @@ HOBJ4 = mplayer.obj,mthrowu.obj,muse.obj,music.obj,o_init.obj,objnam.obj, \ options.obj,pager.obj,pickup.obj,pline.obj,polyself.obj, \ potion.obj,pray.obj,priest.obj,quest.obj,questpgr.obj,read.obj HOBJ5 = rect.obj,region.obj,restore.obj,rip.obj,rnd.obj,role.obj, \ - rumors.obj,save.obj,sfstruct.obj,shk.obj,shknam.obj,sit.obj, \ - sounds.obj,sp_lev.obj,spell.obj,steal.obj,steed.obj,sys.obj, \ + rumors.obj,save.obj,selvar.obj,sfstruct.obj,shk.obj,shknam.obj,sit.obj, \ + sounds.obj,sp_lev.obj,spell.obj,stairs.obj,steal.obj,steed.obj,sys.obj, \ teleport.obj,timeout.obj,topten.obj, track.obj,trap.obj -HOBJ6 = u_init.obj,uhitm.obj,vault.obj,vision.obj,weapon.obj, \ - were.obj,wield.obj,windows.obj,wizard.obj,worm.obj,worn.obj, \ +HOBJ6 = u_init.obj,uhitm.obj,utf8map.obj,vault.obj,vision.obj,weapon.obj, \ + were.obj,wield.obj,windows.obj,wizard.obj,wizcmds.obj,worm.obj,worn.obj, \ write.obj,zap.obj,version.obj HOBJ = $(FIRSTOBJ) $(SYSOBJ) $(WINOBJ) $(RANDOBJ) \ $(HOBJ1) $(HOBJ2) $(HOBJ3) $(HOBJ4) $(HOBJ5) $(HOBJ6) diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com index 3f2ca521e..e7b11df52 100755 --- a/sys/vms/vmsbuild.com +++ b/sys/vms/vmsbuild.com @@ -420,10 +420,10 @@ $ c_list = "decl,version,[-.sys.vms]vmsunix" - $ gosub compile_list $ c_list = interface !ttysrc or cursessrc or both $ gosub compile_list -$ c_list = "allmain,apply,artifact,attrib,ball,bones,botl,cmd,dbridge" - - + ",dothrow,drawing,detect,dig,display,do,do_name,do_wear,dog" - - + ",dogmove,dokick,dungeon,eat,end,engrave,exper,explode" - - + ",extralev,files,fountain" +$ c_list = "allmain,apply,artifact,attrib,ball,bones,botl,calendar,cmd" - + + ",coloratt,dbridge,dothrow,drawing,detect,dig,display,do,do_name" - + + ",do_wear,dog,dogmove,dokick,dungeon,eat,end,engrave,exper,explode" - + + ",extralev,files,fountain,getpos" $ gosub compile_list $ c_list = "hack,hacklib,insight,invent,light,lock,mail,makemon" - + ",mcastu,mdlib,mhitm,mhitu,minion,mklev,mkmap,mkmaze" - @@ -437,12 +437,13 @@ $ c_list = "nhlua,nhlobj,nhlsel" $ gosub compile_list $ c_list = "o_init,objnam,options,pager,pickup" - + ",pline,polyself,potion,pray,priest,quest,questpgr,read" - - + ",rect,region,restore,rip,rnd,role,rumors,save,sfstruct,shk" - - + ",shknam,sit,sounds,sp_lev,spell,steal,steed,symbols" - - + ",sys,teleport,timeout,topten,track,trap,utf8map,u_init" + + ",rect,region,report,restore,rip,rnd,role,rumors,save,selvar" - + + ",sfstruct,shk,shknam,sit,sounds,sp_lev,spell,stairs,steal" - + + ",steed,strutil,symbols,sys,teleport,timeout,topten,track" - + + ",trap,u_init,utf8map" $ gosub compile_list $ c_list = "uhitm,vault,vision,weapon,were,wield,windows" - - + ",wizard,worm,worn,write,zap" + + ",wizard,wizcmds,worm,worn,write,zap" $ gosub compile_list $! $link: diff --git a/sys/windows/Makefile.mingw32 b/sys/windows/Makefile.mingw32 index 88e5ef135..427ef88b3 100644 --- a/sys/windows/Makefile.mingw32 +++ b/sys/windows/Makefile.mingw32 @@ -458,6 +458,29 @@ LUALIST = air Arc-fila Arc-filb Arc-goal Arc-loca Arc-strt \ LUAFILES = $(addprefix $(DAT)/, $(addsuffix .lua, $(LUALIST))) +#========================================== +# Hacklib +#========================================== +HACKLIBLIST = hacklib +HACKLIBSRC = $(addprefix ../src/,$(addsuffix .c, $(HACKLIBLIST))) +HL = $(O)hacklib +HLHACKLIB = $(HL)/hacklib.a +HLHACKLIBOBJS = $(addprefix $(HL)/, $(addsuffix .o, $(HACKLIBLIST))) +HLTARGETS = $(HLHACKLIB) $(HLHACKLIBOBJS) + +$(HL)/hacklib.a: $(HLHACKLIBOBJS) + ar rcs $@ $^ + +$(HL)/hacklib.o: $(HACKLIBSRC) | $(HL) + +$(HL)/%.o: $(SRC)/%.c | $(HL) + $(cc) $(CFLAGSU) $< -o$@ +$(HL): + @mkdir -p $@ + +CLEAN_DIR += $(HL) +CLEAN_FILE += $(HLTARGETS) + #========================================== # Makedefs #========================================== @@ -467,7 +490,7 @@ MTARGETS = $(U)makedefs.exe makedefs: $(MTARGETS) -$(U)makedefs.exe: $(MOBJS) +$(U)makedefs.exe: $(MOBJS) $(HLHACKLIB) $(ld) $(LDFLAGS) $^ -o$@ $(OM)/%.o: $(SRC)/%.c $(NHLUAH) | $(OM) @@ -579,8 +602,8 @@ TTARGETS = $(U)tile2bmp.exe tile2bmp: $(TTARGETS) -$(U)tile2bmp.exe: $(TOBJS) - $(ld) $(LDFLAGS) $^ -o$@ +$(U)tile2bmp.exe: $(TOBJS) $(HLHACKLIB) + $(ld) $(LDFLAGS) $(HLHACKLIB) $^ -o$@ $(OT)/tiletxt.o: $(WSHR)/tilemap.c $(NHLUAH) | $(OT) $(cc) $(CFLAGSU) -DTILETEXT $< -o$@ @@ -662,8 +685,8 @@ tilemap: $(SRC)/tile.c $(SRC)/tile.c: $(U)tilemap.exe $< -$(U)tilemap.exe: $(TMOBJS) - $(ld) $(LDFLAGS) $^ -o$@ +$(U)tilemap.exe: $(TMOBJS) $(HLHACKLIB) + $(ld) $(LDFLAGS) $(HLHACKLIB) $^ -o$@ $(OTM)/tilemap.o: $(WSHR)/tilemap.c $(NHLUAH) | $(OTM) $(cc) $(CFLAGSU) $< -o$@ @@ -716,8 +739,8 @@ DTARGETS = $(U)dlb.exe $(DAT_CLEAN) $(DLBLST) $(DLB) dlb: $(DTARGETS) -$(U)dlb.exe: $(DLBOBJS) - $(ld) $(LDFLAGS) $^ -o$@ +$(U)dlb.exe: $(DLBOBJS) $(HLHACKLIB) + $(ld) $(LDFLAGS) $(HLHACKLIB) $^ -o$@ $(ODLB)/%.o: $(SRC)/%.c $(NHLUAH) | $(ODLB) $(cc) $(CFLAGSU) $< -o$@ @@ -961,17 +984,18 @@ endif # HAVE_SOUNDLIB # nethackw #========================================== COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl \ - coloratt cmd cppregex \ + calendar coloratt cmd cppregex \ dbridge decl detect dig display dlb do do_name do_wear \ dog dogmove dokick dothrow drawing dungeon \ eat end engrave exper explode extralev files fountain \ - hack hacklib insight invent isaac64 light lock \ + getpos hack insight invent isaac64 light lock \ mail makemon mcastu mdlib mhitm mhitu minion mklev mkmap mkmaze mkobj mkroom \ mon mondata monmove monst mplayer mthrowu muse music \ nhlobj nhlsel nhlua windsound o_init objects objnam options \ pager pickup pline polyself potion pray priest quest questpgr \ - random read rect region restore rip rnd role rumors \ - safeproc save sfstruct shk shknam sit sounds sp_lev spell steal steed \ + random read rect region report restore rip rnd role rumors \ + safeproc save sfstruct shk shknam sit selvar sounds sp_lev \ + spell stairs steal steed strutil \ symbols sys teleport timeout topten track trap u_init uhitm utf8map \ vault version vision weapon were wield windmain windows windsys wizard \ worm worn write zap $(SOUNDLIBOBJS)) @@ -1006,7 +1030,8 @@ endif nethackw: $(NHWTARGETS) $(GAMEDIR)/NetHackW.exe: $(NHWOBJS) $(NHWRES) $(DATEW_O) $(LUALIB) $(PDCWLIB) | $(GAMEDIR) - $(ld) $(LDFLAGS) -mwindows $^ $(LIBS) -static -lstdc++ $(SOUNDLIBLIBS) -o$@ + $(ld) $(LDFLAGS) -mwindows $^ $(HLHACKLIB) $(LIBS) -static -lstdc++ \ + $(SOUNDLIBLIBS) -o$@ $(ONHW)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONHW) $(cc) $(CFLAGSW) $< -o$@ @@ -1096,8 +1121,9 @@ NHTARGET = $(GAMEDIR)/NetHack.exe nethack: $(NHTARGET) -$(GAMEDIR)/NetHack.exe: $(NHOBJS) $(NHRES) $(DATE_O) $(LUALIB) $(PDCLIB) $(SOUNDLIBLIBS) | $(GAMEDIR) - $(ld) $(LDFLAGS) -mconsole $^ $(LIBS) -static -lstdc++ -o$@ +$(GAMEDIR)/NetHack.exe: $(NHOBJS) $(NHRES) $(DATE_O) $(LUALIB) $(PDCLIB) \ + $(SOUNDLIBLIBS) | $(GAMEDIR) + $(ld) $(LDFLAGS) -mconsole $^ $(HLHACKLIB) $(LIBS) -static -lstdc++ -o$@ $(ONH)/%.o: $(SRC)/%.c $(NHLUAH) | $(ONH) $(cc) $(CFLAGSNH) $< -o$@ diff --git a/sys/windows/Makefile.mingw32.depend b/sys/windows/Makefile.mingw32.depend index ae358190c..71c925c67 100644 --- a/sys/windows/Makefile.mingw32.depend +++ b/sys/windows/Makefile.mingw32.depend @@ -9,6 +9,9 @@ cce = gcc -E -MM -MT "$(@:d=o) $@" # Run # mingw32-make depend +$(HL)/%.d: $(SRC)/%.c | $(HL) + $(cce) $(CFLAGSU) $< -o$@ + $(OM)/%.d: $(SRC)/%.c $(NHLUAH) | $(OM) $(cce) $(CFLAGSU) -DENUM_PM $< -o$@ @@ -108,7 +111,7 @@ $(ONH)/%.d: $(TTY)/%.c $(NHLUAH) | $(ONH) $(ONH)/%.d: $(WCURSES)/%.c $(NHLUAH) | $(ONH) $(cce) $(CFLAGSNH) $(PDCINCL) $< -o$@ -OBJS4DEP = $(MOBJS) $(ROBJS) \ +OBJS4DEP = $(MOBJS) $(ROBJS) $(HLHACKLIBOBJS) \ $(TOBJS) $(GIFOBJ) $(GIF32OBJ) $(PPMOBJ) $(BMP32OBJ) $(T32OBJS) $(TUCOMMON) \ $(TMOBJS) $(UOBJS) $(DLBOBJS) $(NHWOBJS) $(NHOBJS) DEP_TARGETS = $(OBJS4DEP:o=d) diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 873ad918b..5df4d5d54 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -495,8 +495,8 @@ HACKINCL = $(INCL)\align.h $(INCL)\artifact.h $(INCL)\artilist.h \ $(INCL)\monsters.h $(INCL)\obj.h $(INCL)\objects.h $(INCL)\objclass.h \ $(INCL)\optlist.h $(INCL)\patchlevel.h $(INCL)\pcconf.h \ $(INCL)\permonst.h $(INCL)\prop.h $(INCL)\rect.h $(INCL)\region.h \ - $(INCL)\sym.h $(INCL)\defsym.h $(INCL)\rm.h $(INCL)\sp_lev.h \ - $(INCL)\spell.h $(INCL)\sys.h $(INCL)\cstd.h $(INCL)\tcap.h \ + $(INCL)\sym.h $(INCL)\defsym.h $(INCL)\rm.h $(INCL)\selvar.h $(INCL)\sp_lev.h \ + $(INCL)\spell.h $(INCL)\stairs.h $(INCL)\sys.h $(INCL)\cstd.h $(INCL)\tcap.h \ $(INCL)\timeout.h $(INCL)\tradstdc.h $(INCL)\trap.h \ $(INCL)\unixconf.h $(INCL)\vision.h $(INCL)\vmsconf.h \ $(INCL)\wintty.h $(INCL)\wincurs.h $(INCL)\winX.h \ @@ -506,10 +506,10 @@ HACKINCL = $(INCL)\align.h $(INCL)\artifact.h $(INCL)\artilist.h \ # all .c that are part of the main NetHack program and are not operating- or # windowing-system specific HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ - botl.c coloratt.c cmd.c dbridge.c decl.c detect.c dig.c display.c \ + botl.c calendar.c cmd.c coloratt.c dbridge.c decl.c detect.c dig.c display.c \ dlb.c do.c do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c \ drawing.c dungeon.c eat.c end.c engrave.c exper.c explode.c \ - extralev.c files.c fountain.c hack.c hacklib.c \ + files.c fountain.c hack.c \ insight.c invent.c isaac64.c light.c \ lock.c mail.c makemon.c mcastu.c mdlib.c mhitm.c \ mhitu.c minion.c mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c \ @@ -630,6 +630,14 @@ CURSESDEF2=-DCURSES_UNICODE $(PDCURSESFLAGS) !UNDEF PDCWINGUILIB !ENDIF +OUTLHACKLIBOBJS = $(OUTL)hacklib.o +OTTYHACKLIBOBJS = $(OTTY)hacklib.o +OGUIHACKLIBOBJS = $(OGUI)hacklib.o +OUTLHACKLIB = $(OUTL)hacklib-$(TARGET_CPU)-static.lib +OTTYHACKLIB = $(OTTY)hacklib-$(TARGET_CPU)-static.lib +OGUIHACKLIB = $(OGUI)hacklib-$(TARGET_CPU)-static.lib + + # # - TTY # @@ -650,32 +658,33 @@ TTYOBJ = $(OTTY)topl.o $(OTTY)getline.o $(OTTY)wintty.o VTTYOBJ01 = $(OTTY)allmain.o $(OTTY)alloc.o $(OTTY)apply.o $(OTTY)artifact.o VTTYOBJ02 = $(OTTY)attrib.o $(OTTY)ball.o $(OTTY)bones.o $(OTTY)botl.o -VTTYOBJ03 = $(OTTY)coloratt.o $(OTTY)cmd.o $(OTTY)dbridge.o $(OTTY)decl.o $(OTTY)detect.o -VTTYOBJ04 = $(OTTY)dig.o $(OTTY)display.o $(OTTY)do.o $(OTTY)do_name.o -VTTYOBJ05 = $(OTTY)do_wear.o $(OTTY)dog.o $(OTTY)dogmove.o $(OTTY)dokick.o +VTTYOBJ03 = $(OTTY)calendar.o $(OTTY)cmd.o $(OTTY)coloratt.o $(OTTY)dbridge.o +VTTYOBJ04 = $(OTTY)decl.o $(OTTY)detect.o $(OTTY)dig.o $(OTTY)display.o $(OTTY)do.o +VTTYOBJ05 = $(OTTY)do_name.o $(OTTY)do_wear.o $(OTTY)dog.o $(OTTY)dogmove.o $(OTTY)dokick.o VTTYOBJ06 = $(OTTY)dothrow.o $(OTTY)drawing.o $(OTTY)dungeon.o $(OTTY)eat.o VTTYOBJ07 = $(OTTY)end.o $(OTTY)engrave.o $(OTTY)exper.o $(OTTY)explode.o -VTTYOBJ08 = $(OTTY)extralev.o $(OTTY)files.o $(OTTY)fountain.o $(OTTY)hack.o -VTTYOBJ09 = $(OTTY)hacklib.o $(OTTY)insight.o $(OTTY)invent.o $(OTTY)isaac64.o -VTTYOBJ10 = $(OTTY)light.o $(OTTY)lock.o $(OTTY)mail.o $(OTTY)makemon.o -VTTYOBJ11 = $(OTTY)mcastu.o $(OTTY)mhitm.o $(OTTY)mhitu.o $(OTTY)minion.o -VTTYOBJ12 = $(OTTY)mklev.o $(OTTY)mkmap.o $(OTTY)mkmaze.o $(OTTY)mkobj.o -VTTYOBJ13 = $(OTTY)mkroom.o $(OTTY)mon.o $(OTTY)mondata.o $(OTTY)monmove.o -VTTYOBJ14 = $(OTTY)monst.o $(OTTY)mplayer.o $(OTTY)mthrowu.o $(OTTY)muse.o -VTTYOBJ15 = $(OTTY)music.o $(OTTY)o_init.o $(OTTY)objects.o $(OTTY)objnam.o -VTTYOBJ16 = $(OTTY)options.o $(OTTY)pager.o $(OTTY)pickup.o $(OTTY)pline.o -VTTYOBJ17 = $(OTTY)polyself.o $(OTTY)potion.o $(OTTY)pray.o $(OTTY)priest.o -VTTYOBJ18 = $(OTTY)quest.o $(OTTY)questpgr.o $(RANDOM) $(OTTY)read.o -VTTYOBJ19 = $(OTTY)rect.o $(OTTY)region.o $(OTTY)restore.o $(OTTY)rip.o -VTTYOBJ20 = $(OTTY)rnd.o $(OTTY)role.o $(OTTY)rumors.o $(OTTY)save.o -VTTYOBJ21 = $(OTTY)sfstruct.o $(OTTY)shk.o $(OTTY)shknam.o $(OTTY)sit.o -VTTYOBJ22 = $(OTTY)sounds.o $(OTTY)sp_lev.o $(OTTY)spell.o $(OTTY)steal.o -VTTYOBJ23 = $(OTTY)steed.o $(OTTY)symbols.o $(OTTY)sys.o $(OTTY)teleport.o -VTTYOBJ24 = $(OTTY)timeout.o $(OTTY)topten.o $(OTTY)track.o $(OTTY)trap.o -VTTYOBJ25 = $(OTTY)u_init.o $(OTTY)uhitm.o $(OTTY)utf8map.o $(OTTY)vault.o -VTTYOBJ26 = $(OTTY)vision.o $(OTTY)weapon.o $(OTTY)were.o $(OTTY)wield.o -VTTYOBJ27 = $(OTTY)windows.o $(OTTY)wizard.o $(OTTY)worm.o $(OTTY)worn.o -VTTYOBJ28 = $(OTTY)write.o $(OTTY)zap.o +VTTYOBJ08 = $(OTTY)extralev.o $(OTTY)files.o $(OTTY)fountain.o $(OTTY)getpos.o +VTTYOBJ09 = $(OTTY)hack.o $(OTTY)insight.o $(OTTY)invent.o +VTTYOBJ10 = $(OTTY)isaac64.o $(OTTY)light.o $(OTTY)lock.o $(OTTY)mail.o +VTTYOBJ11 = $(OTTY)makemon.o $(OTTY)mcastu.o $(OTTY)mhitm.o $(OTTY)mhitu.o +VTTYOBJ12 = $(OTTY)minion.o $(OTTY)mklev.o $(OTTY)mkmap.o $(OTTY)mkmaze.o +VTTYOBJ13 = $(OTTY)mkobj.o $(OTTY)mkroom.o $(OTTY)mon.o $(OTTY)mondata.o +VTTYOBJ14 = $(OTTY)monmove.o $(OTTY)monst.o $(OTTY)mplayer.o $(OTTY)mthrowu.o +VTTYOBJ15 = $(OTTY)muse.o $(OTTY)music.o $(OTTY)o_init.o $(OTTY)objects.o +VTTYOBJ16 = $(OTTY)objnam.o $(OTTY)options.o $(OTTY)pager.o $(OTTY)pickup.o +VTTYOBJ17 = $(OTTY)pline.o $(OTTY)polyself.o $(OTTY)potion.o $(OTTY)pray.o +VTTYOBJ18 = $(OTTY)priest.o $(OTTY)quest.o $(OTTY)questpgr.o $(RANDOM) +VTTYOBJ19 = $(OTTY)read.o $(OTTY)rect.o $(OTTY)region.o $(OTTY)report.o $(OTTY)restore.o +VTTYOBJ20 = $(OTTY)rip.o $(OTTY)rnd.o $(OTTY)role.o $(OTTY)rumors.o +VTTYOBJ21 = $(OTTY)save.o $(OTTY)selvar.o $(OTTY)sfstruct.o $(OTTY)shk.o +VTTYOBJ22 = $(OTTY)shknam.o $(OTTY)sit.o $(OTTY)sounds.o $(OTTY)sp_lev.o +VTTYOBJ23 = $(OTTY)spell.o $(OTTY)stairs.o $(OTTY)steal.o $(OTTY)steed.o +VTTYOBJ24 = $(OTTY)strutil.o $(OTTY)symbols.o $(OTTY)sys.o $(OTTY)teleport.o $(OTTY)timeout.o +VTTYOBJ25 = $(OTTY)topten.o $(OTTY)track.o $(OTTY)trap.o $(OTTY)u_init.o +VTTYOBJ26 = $(OTTY)uhitm.o $(OTTY)utf8map.o $(OTTY)vault.o $(OTTY)vision.o +VTTYOBJ27 = $(OTTY)weapon.o $(OTTY)were.o $(OTTY)wield.o $(OTTY)windows.o +VTTYOBJ28 = $(OTTY)wizard.o $(OTTY)wizcmds.o $(OTTY)worm.o $(OTTY)worn.o +VTTYOBJ29 = $(OTTY)write.o $(OTTY)zap.o OBJSTTY = $(MDLIBTTY) \ $(VTTYOBJ01) $(VTTYOBJ02) $(VTTYOBJ03) $(VTTYOBJ04) $(VTTYOBJ05) \ @@ -717,32 +726,33 @@ GUIOBJ = $(OGUI)mhaskyn.o $(OGUI)mhdlg.o \ VGUIOBJ01 = $(OGUI)allmain.o $(OGUI)alloc.o $(OGUI)apply.o $(OGUI)artifact.o VGUIOBJ02 = $(OGUI)attrib.o $(OGUI)ball.o $(OGUI)bones.o $(OGUI)botl.o -VGUIOBJ03 = $(OGUI)coloratt.o $(OGUI)cmd.o $(OGUI)dbridge.o $(OGUI)decl.o $(OGUI)detect.o -VGUIOBJ04 = $(OGUI)dig.o $(OGUI)display.o $(OGUI)do.o $(OGUI)do_name.o +VGUIOBJ03 = $(OGUI)calendar.o $(OGUI)cmd.o $(OGUI)coloratt.o $(OGUI)dbridge.o $(OGUI)decl.o +VGUIOBJ04 = $(OGUI)detect.o $(OGUI)dig.o $(OGUI)display.o $(OGUI)do.o $(OGUI)do_name.o VGUIOBJ05 = $(OGUI)do_wear.o $(OGUI)dog.o $(OGUI)dogmove.o $(OGUI)dokick.o VGUIOBJ06 = $(OGUI)dothrow.o $(OGUI)drawing.o $(OGUI)dungeon.o $(OGUI)eat.o VGUIOBJ07 = $(OGUI)end.o $(OGUI)engrave.o $(OGUI)exper.o $(OGUI)explode.o -VGUIOBJ08 = $(OGUI)extralev.o $(OGUI)files.o $(OGUI)fountain.o $(OGUI)hack.o -VGUIOBJ09 = $(OGUI)hacklib.o $(OGUI)insight.o $(OGUI)invent.o $(OGUI)isaac64.o -VGUIOBJ10 = $(OGUI)light.o $(OGUI)lock.o $(OGUI)mail.o $(OGUI)makemon.o -VGUIOBJ11 = $(OGUI)mcastu.o $(OGUI)mhitm.o $(OGUI)mhitu.o $(OGUI)minion.o -VGUIOBJ12 = $(OGUI)mklev.o $(OGUI)mkmap.o $(OGUI)mkmaze.o $(OGUI)mkobj.o -VGUIOBJ13 = $(OGUI)mkroom.o $(OGUI)mon.o $(OGUI)mondata.o $(OGUI)monmove.o -VGUIOBJ14 = $(OGUI)monst.o $(OGUI)mplayer.o $(OGUI)mthrowu.o $(OGUI)muse.o -VGUIOBJ15 = $(OGUI)music.o $(OGUI)o_init.o $(OGUI)objects.o $(OGUI)objnam.o -VGUIOBJ16 = $(OGUI)options.o $(OGUI)pager.o $(OGUI)pickup.o $(OGUI)pline.o -VGUIOBJ17 = $(OGUI)polyself.o $(OGUI)potion.o $(OGUI)pray.o $(OGUI)priest.o -VGUIOBJ18 = $(OGUI)quest.o $(OGUI)questpgr.o $(RANDOMGUI) $(OGUI)read.o -VGUIOBJ19 = $(OGUI)rect.o $(OGUI)region.o $(OGUI)restore.o $(OGUI)rip.o -VGUIOBJ20 = $(OGUI)rnd.o $(OGUI)role.o $(OGUI)rumors.o $(OGUI)save.o -VGUIOBJ21 = $(OGUI)sfstruct.o $(OGUI)shk.o $(OGUI)shknam.o $(OGUI)sit.o -VGUIOBJ22 = $(OGUI)sounds.o $(OGUI)sp_lev.o $(OGUI)spell.o $(OGUI)steal.o -VGUIOBJ23 = $(OGUI)steed.o $(OGUI)symbols.o $(OGUI)sys.o $(OGUI)teleport.o -VGUIOBJ24 = $(OGUI)timeout.o $(OGUI)topten.o $(OGUI)track.o $(OGUI)trap.o -VGUIOBJ25 = $(OGUI)u_init.o $(OGUI)uhitm.o $(OGUI)utf8map.o $(OGUI)vault.o -VGUIOBJ26 = $(OGUI)vision.o $(OGUI)weapon.o $(OGUI)were.o $(OGUI)wield.o -VGUIOBJ27 = $(OGUI)windows.o $(OGUI)wizard.o $(OGUI)worm.o $(OGUI)worn.o -VGUIOBJ28 = $(OGUI)write.o $(OGUI)zap.o +VGUIOBJ08 = $(OGUI)extralev.o $(OGUI)files.o $(OGUI)fountain.o $(OGUI)getpos.o +VGUIOBJ09 = $(OGUI)hack.o $(OGUI)insight.o $(OGUI)invent.o +VGUIOBJ10 = $(OGUI)isaac64.o $(OGUI)light.o $(OGUI)lock.o $(OGUI)mail.o +VGUIOBJ11 = $(OGUI)makemon.o $(OGUI)mcastu.o $(OGUI)mhitm.o $(OGUI)mhitu.o +VGUIOBJ12 = $(OGUI)minion.o $(OGUI)mklev.o $(OGUI)mkmap.o $(OGUI)mkmaze.o +VGUIOBJ13 = $(OGUI)mkobj.o $(OGUI)mkroom.o $(OGUI)mon.o $(OGUI)mondata.o +VGUIOBJ14 = $(OGUI)monmove.o $(OGUI)monst.o $(OGUI)mplayer.o $(OGUI)mthrowu.o +VGUIOBJ15 = $(OGUI)muse.o $(OGUI)music.o $(OGUI)o_init.o $(OGUI)objects.o +VGUIOBJ16 = $(OGUI)objnam.o $(OGUI)options.o $(OGUI)pager.o $(OGUI)pickup.o +VGUIOBJ17 = $(OGUI)pline.o $(OGUI)polyself.o $(OGUI)potion.o $(OGUI)pray.o +VGUIOBJ18 = $(OGUI)priest.o $(OGUI)quest.o $(OGUI)questpgr.o $(RANDOMGUI) +VGUIOBJ19 = $(OGUI)read.o $(OGUI)rect.o $(OGUI)region.o $(OGUI)report.o $(OGUI)restore.o +VGUIOBJ20 = $(OGUI)rip.o $(OGUI)rnd.o $(OGUI)role.o $(OGUI)rumors.o +VGUIOBJ21 = $(OGUI)save.o $(OGUI)selvar.o $(OGUI)sfstruct.o $(OGUI)shk.o +VGUIOBJ22 = $(OGUI)shknam.o $(OGUI)sit.o $(OGUI)sounds.o $(OGUI)sp_lev.o +VGUIOBJ23 = $(OGUI)spell.o $(OGUI)stairs.o $(OGUI)steal.o $(OGUI)steed.o +VGUIOBJ24 = $(OGUI)strutil.o $(OGUI)symbols.o $(OGUI)sys.o $(OGUI)teleport.o $(OGUI)timeout.o +VGUIOBJ25 = $(OGUI)topten.o $(OGUI)track.o $(OGUI)trap.o $(OGUI)u_init.o +VGUIOBJ26 = $(OGUI)uhitm.o $(OGUI)utf8map.o $(OGUI)vault.o $(OGUI)vision.o +VGUIOBJ27 = $(OGUI)weapon.o $(OGUI)were.o $(OGUI)wield.o $(OGUI)windows.o +VGUIOBJ28 = $(OGUI)wizard.o $(OGUI)wizcmds.o $(OGUI)worm.o $(OGUI)worn.o +VGUIOBJ29 = $(OGUI)write.o $(OGUI)zap.o OBJSGUI = $(MDLIBGUI) \ $(VGUIOBJ01) $(VGUIOBJ02) $(VGUIOBJ03) $(VGUIOBJ04) $(VGUIOBJ05) \ @@ -1456,11 +1466,11 @@ GAMEOBJ=$(GAMEOBJ:^ =^ $(GAMEDIR)\NetHack.exe : gamedir.tag $(OTTY)consoletty.o \ $(ALLOBJTTY) $(CURSESWINCONOBJS) \ $(TTYSOUNDOBJS) $(OTTY)date.o $(OTTY)console.res \ - $(LUALIB) $(TTYOBJ) $(PDCWINCONLIB) $(PDCWINCONOBJS) + $(LUALIB) $(TTYOBJ) $(PDCWINCONLIB) $(PDCWINCONOBJS) $(OTTYHACKLIB) @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) @echo Linking $(@:\=/) $(link) $(LFLAGS) $(conlflags) /STACK:2048 /PDB:$(GAMEDIR)\$(@B).PDB /MAP:$(OTTY)$(@B).MAP \ - $(LIBS) $(PDCWINCONLIB) $(LUALIB) $(TTYSOUNDLIBS) \ + $(LIBS) $(PDCWINCONLIB) $(LUALIB) $(TTYSOUNDLIBS) $(OTTYHACKLIB) \ $(conlibs) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJTTY) $(ALLOBJTTY) @@ -1480,12 +1490,12 @@ $(GAMEDIR)\NetHackW.exe : gamedir.tag $(OGUI)tile.o \ $(ALLOBJGUI) $(GAMEOBJGUI) $(GUIOBJ) $(GUISOUNDOBJS) \ $(CURSESWINGUIOBJS) $(OGUI)date.o \ $(OGUI)NetHackW.res \ - $(LUALIB) $(PDCWINGUILIB) $(PDCWINGUIOBJS) + $(LUALIB) $(PDCWINGUILIB) $(PDCWINGUIOBJS) $(OGUIHACKLIB) @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) @echo Linking $(@:\=/) $(link) $(LFLAGS) $(guilflags) /STACK:2048 /PDB:$(GAMEDIR)\$(@B).PDB \ /MAP:$(OGUI)$(@B).MAP $(LIBS) $(PDCWINGUILIB) $(LUALIB) \ - $(GUISOUNDLIBS) \ + $(GUISOUNDLIBS) $(OGUIHACKLIB) \ $(guilibs) $(COMCTRL) $(BCRYPT) -out:$@ @<<$(@B).lnk $(GAMEOBJGUI) $(ALLOBJGUI) @@ -1547,7 +1557,7 @@ recover: $(U)recover.exe if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR) if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR)\recover.txt -$(OUTL)utility.tag: $(INCL)\nhlua.h outldir$(TARGET_CPU).tag $(U)tile2bmp.exe $(U)makedefs.exe +$(OUTL)utility.tag: $(INCL)\nhlua.h outldir$(TARGET_CPU).tag $(OUTLHACKLIB) $(U)tile2bmp.exe $(U)makedefs.exe @echo utilities made >$@ @echo utilities made. @@ -1587,9 +1597,9 @@ $(U)nhsizes3.exe: $(OUTL)nhsizes3.o $(OUTL)nhsizes3.o: $(CONFIG_H) nhsizes3.c $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -Fo$@ nhsizes3.c -$(U)makedefs.exe: $(MAKEDEFSOBJS) +$(U)makedefs.exe: $(OUTLHACKLIB) $(MAKEDEFSOBJS) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ $(MAKEDEFSOBJS) + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ $(MAKEDEFSOBJS) $(OUTLHACKLIB) $(OUTL)makedefs.o: $(U)makedefs.c $(SRC)\mdlib.c $(CONFIG_H) $(INCL)\permonst.h \ $(INCL)\objclass.h $(INCL)\sym.h $(INCL)\defsym.h \ @@ -1663,6 +1673,23 @@ $(INCL)\onames.h : $(U)makedefs.exe $(INCL)\pm.h : $(U)makedefs.exe $(U)makedefs -p + +#========================================== +# HACKLIB +#========================================== + +$(OUTLHACKLIB): $(OUTLHACKLIBOBJS) + @echo Building library $@ from $** + @$(librarian) /OUT:$@ $(OUTLHACKLIBOBJS) + +$(OTTYHACKLIB): $(OTTYHACKLIBOBJS) + @echo Building library $@ from $** + @$(librarian) /OUT:$@ $(OTTYHACKLIBOBJS) + +$(OGUIHACKLIB): $(OGUIHACKLIBOBJS) + @echo Building library $@ from $** + @$(librarian) /OUT:$@ $(OGUIHACKLIBOBJS) + #========================================== # uudecode utility and uuencoded targets #========================================== @@ -1802,9 +1829,9 @@ fetch-pdcurses: # DLB utility and nhdatNNN file creation #========================================== -$(U)dlb.exe: $(DLBOBJ_HOST) $(OUTL)dlb$(HOST).o +$(U)dlb.exe: $(DLBOBJ_HOST) $(OUTL)dlb$(HOST).o $(OUTLHACKLIB) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ @<<$(@B).lnk + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(OUTLHACKLIB) -out:$@ @<<$(@B).lnk $(OUTL)dlb_main$(HOST).o $(OUTL)dlb$(HOST).o $(OUTL)alloc$(HOST).o @@ -1886,10 +1913,10 @@ tile.c: $(U)tilemap.exe @$(U)tilemap @echo A new $(@:\=/) has been created -$(U)tilemap.exe: $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o +$(U)tilemap.exe: $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o $(OUTLHACKLIB) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ \ - $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(HACKLIB) -out:$@ \ + $(OUTL)tilemap.o $(OUTL)monst.o $(OUTL)objects.o $(OUTL)drawing.o $(OUTLHACKLIB) $(OUTL)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) $(Q)$(CC) $(CFLAGS) $(CROSSCOMPILE) -Fo$@ $(WSHR)\tilemap.c @@ -1952,7 +1979,7 @@ $(SRC)\tiles.bmp: $(U)tile2bmp.exe $(TILEFILES) $(U)tile2bmp.exe: $(OUTL)tile2bmp.o $(TEXT_IO) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ @<<$(@B).lnk + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(OUTLHACKLIB) -out:$@ @<<$(@B).lnk $(OUTL)tile2bmp.o $(TEXT_IO:^ =^ ) @@ -1960,7 +1987,7 @@ $(U)tile2bmp.exe: $(OUTL)tile2bmp.o $(TEXT_IO) $(U)til2bm32.exe: $(OUTL)til2bm32.o $(TEXT_IO32) @echo Linking $(@:\=/) - @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" -out:$@ @<<$(@B).lnk + @$(link) $(LFLAGS) /PDB:"$(OUTL)$(@B).PDB" /MAP:"$(OUTL)$(@B).MAP" $(OUTLHACKLIB) -out:$@ @<<$(@B).lnk $(OUTL)til2bm32.o $(TEXT_IO32:^ =^ ) diff --git a/util/dlb_main.c b/util/dlb_main.c index 09f1432ce..57ea1c78a 100644 --- a/util/dlb_main.c +++ b/util/dlb_main.c @@ -7,6 +7,8 @@ #include "config.h" #include "dlb.h" +#include "hacklib.h" + #if !defined(O_WRONLY) && !defined(MAC) && !defined(AZTEC_C) #include #endif @@ -16,7 +18,6 @@ ATTRNORETURN static void xexit(int) NORETURN; ATTRNORETURN extern void panic(const char *, ...) NORETURN; -char *eos(char *); /* also used by dlb.c */ FILE *fopen_datafile(const char *, const char *); #ifdef DLB @@ -138,14 +139,6 @@ fopen_datafile(const char *filename, const char *mode) return fopen(filename, mode); } -char * -eos(char *s) -{ - while (*s) - s++; - return s; -} - #ifdef DLB #define UNUSED_if_no_DLB /*empty*/ #else diff --git a/util/makedefs.c b/util/makedefs.c index b2f8b0cf0..df3afd4ee 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -21,6 +21,7 @@ #include "context.h" #include "flag.h" #include "dlb.h" +#include "hacklib.h" #include #ifdef MAC @@ -166,7 +167,9 @@ extern void objects_globals_init(void); /* objects.c */ static char *name_file(const char *, const char *); static FILE *getfp(const char *, const char *, const char *, int); static void do_ext_makedefs(int, char **); +#if 0 static char *xcrypt(const char *); +#endif static char *padline(char *, unsigned); static unsigned long read_rumors_file(const char *, int *, long *, unsigned long, unsigned); @@ -974,10 +977,11 @@ grep0(FILE *inputfp0, FILE* outputfp0, int flg) return; } +#if 0 /* trivial text encryption routine which can't be broken with `tr' */ static char * xcrypt(const char *str) -{ /* duplicated in src/hacklib.c */ +{ /* slightly different version in src/hacklib.c */ static char buf[BUFSZ]; const char *p; char *q; @@ -993,6 +997,7 @@ xcrypt(const char *str) *q = '\0'; return buf; } +#endif static char * padline(char *line, unsigned padlength) @@ -1038,7 +1043,7 @@ read_rumors_file( unsigned long old_rumor_offset, unsigned padlength) { - char infile[MAXFNAMELEN]; + char infile[MAXFNAMELEN], xbuf[BUFSZ]; char *line; unsigned long rumor_offset; @@ -1059,7 +1064,7 @@ read_rumors_file( /*[if we forced binary output, this would be sufficient]*/ *rumor_size += strlen(line); /* includes newline */ #endif - (void) fputs(xcrypt(line), tfp); + (void) fputs(xcrypt(line, xbuf), tfp); free((genericptr_t) line); } /* record the current position; next rumors section will start here */ @@ -1081,7 +1086,7 @@ do_rnd_access_file( const char *deflt_content, unsigned padlength) { - char *line, buf[BUFSZ]; + char *line, buf[BUFSZ], xbuf[BUFSZ]; Sprintf(filename, DATA_IN_TEMPLATE, fname); Strcat(filename, ".txt"); @@ -1108,7 +1113,7 @@ do_rnd_access_file( Strcpy(buf, deflt_content); if (!strchr(buf, '\n')) /* lines from the file include trailing newline +*/ Strcat(buf, "\n"); /* so make sure that the default one does too */ - (void) fputs(xcrypt(padline(buf, padlength)), ofp); + (void) fputs(xcrypt(padline(buf, padlength), xbuf), ofp); tfp = getfp(DATA_TEMPLATE, "grep.tmp", WRTMODE, FLG_TEMPFILE); grep0(ifp, tfp, FLG_TEMPFILE); @@ -1122,7 +1127,7 @@ do_rnd_access_file( while ((line = fgetline(ifp)) != 0) { if (line[0] != '#' && line[0] != '\n') { (void) padline(line, padlength); - (void) fputs(xcrypt(line), ofp); + (void) fputs(xcrypt(line, xbuf), ofp); } free((genericptr_t) line); } @@ -1413,7 +1418,7 @@ static const char *special_oracle[] = { void do_oracles(void) { - char infile[60], tempfile[60]; + char infile[60], tempfile[60], xbuf[BUFSZ]; boolean in_oracle, ok; long fpos; unsigned long txt_offset, offset; @@ -1457,7 +1462,7 @@ do_oracles(void) offset = (unsigned long) ftell(tfp); Fprintf(ofp, "%05lx\n", offset); /* start pos of special oracle */ for (i = 0; i < SIZE(special_oracle); i++) { - (void) fputs(xcrypt(special_oracle[i]), tfp); + (void) fputs(xcrypt(special_oracle[i], xbuf), tfp); (void) fputc('\n', tfp); } SpinCursor(3); @@ -1488,7 +1493,7 @@ do_oracles(void) Fprintf(ofp, "%05lx\n", offset); /* start pos of this oracle */ } else { in_oracle = TRUE; - (void) fputs(xcrypt(line), tfp); + (void) fputs(xcrypt(line, xbuf), tfp); } free((genericptr_t) line); } diff --git a/win/share/tile2bmp.c b/win/share/tile2bmp.c index 37dc1e2ef..10561a2e7 100644 --- a/win/share/tile2bmp.c +++ b/win/share/tile2bmp.c @@ -15,6 +15,8 @@ #endif #include "config.h" +#include "hacklib.h" + #include "tile.h" extern void monst_globals_init(void); extern void objects_globals_init(void); diff --git a/win/share/tilemap.c b/win/share/tilemap.c index 67eef6c99..36b673868 100644 --- a/win/share/tilemap.c +++ b/win/share/tilemap.c @@ -18,10 +18,14 @@ #include "rm.h" #else #include "hack.h" -#include "display.h" -#include #endif +#ifdef Snprintf +#undef Snprintf +#endif +#define Snprintf(str, size, ...) \ + nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) + #ifdef MONITOR_HEAP /* with heap monitoring enabled, free(ptr) is a macro which expands to nhfree(ptr,__FILE__,__LINE__); since tilemap doesn't link with @@ -30,10 +34,6 @@ #endif #define Fprintf (void) fprintf -#define Snprintf(str, size, ...) \ - nh_snprintf(__func__, __LINE__, str, size, __VA_ARGS__) -void nh_snprintf(const char *func, int line, char *str, size_t size, - const char *fmt, ...); /* * Defining OBTAIN_TILEMAP to get a listing of the tile-mappings @@ -118,14 +118,14 @@ struct tilemap_t { #endif } tilemap[MAX_GLYPH]; -#define MAX_TILENAM 30 +#define MAX_TILENAM 256 /* List of tiles encountered and their usage */ struct tiles_used { int tilenum; enum tilesrc src; int file_entry; char tilenam[MAX_TILENAM]; - char references[120]; + char references[1024]; }; struct tiles_used *tilelist[2500] = { 0 }; @@ -1484,7 +1484,8 @@ precheck(int offset, const char *glyphtype) glyphtype); } -void add_tileref( +void +add_tileref( int n, int glyphref, enum tilesrc src, @@ -1548,28 +1549,4 @@ free_tilerefs(void) #endif -DISABLE_WARNING_FORMAT_NONLITERAL - -void -nh_snprintf( - const char *func UNUSED, - int line UNUSED, - char *str, size_t size, - const char *fmt, ...) -{ - va_list ap; - int n; - - va_start(ap, fmt); - n = vsnprintf(str, size, fmt, ap); - va_end(ap); - - if (n < 0 || (size_t) n >= size) { /* is there a problem? */ - str[size - 1] = 0; /* make sure it is nul terminated */ - } -} - -RESTORE_WARNING_FORMAT_NONLITERAL - - /*tilemap.c*/