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*/