├── hackdir ├── record_lock ├── news └── hh ├── dev ├── .python-version ├── lib │ ├── __init__.py │ └── colors.py ├── pyproject.toml ├── valgrind.supp ├── uv.lock ├── run-asan.py ├── run-asan-ubsan.py └── run-ubsan.py ├── OWNER ├── src ├── date.h ├── def.wseg.h ├── hack.mfndpos.h ├── def.gold.h ├── def.func_tab.h ├── def.gen.h ├── def.edog.h ├── hack.version.c ├── rnd.c ├── def.mkroom.h ├── def.trap.h ├── def.eshk.h ├── compat.h ├── def.permonst.h ├── def.rm.h ├── hack.track.c ├── def.obj.h ├── hack.worn.c ├── def.flag.h ├── hack.Decl.c ├── def.objclass.h ├── alloc.c ├── def.monst.h ├── pathnames.h ├── hack.wield.c ├── hack.rumors.c ├── hack.monst.c ├── hack.bones.c ├── hack.timeout.c ├── hack.ioctl.c ├── hack.rip.c ├── hack.mkmaze.c └── hack.search.c ├── docs ├── historical │ ├── original-source │ │ ├── OWNER │ │ ├── date.h │ │ ├── hack.sh │ │ ├── def.gold.h │ │ ├── def.wseg.h │ │ ├── hack.version.c │ │ ├── COPYRIGHT │ │ ├── def.func_tab.h │ │ ├── hack.mfndpos.h │ │ ├── rnd.c │ │ ├── def.gen.h │ │ ├── def.edog.h │ │ ├── def.mkroom.h │ │ ├── def.trap.h │ │ ├── def.permonst.h │ │ ├── hack.track.c │ │ ├── def.eshk.h │ │ ├── alloc.c │ │ ├── hack.Decl.c │ │ ├── def.rm.h │ │ ├── hack.ioctl.c │ │ ├── hack.worn.c │ │ ├── hack.timeout.c │ │ ├── def.obj.h │ │ ├── hack.rumors.c │ │ ├── def.flag.h │ │ ├── Makefile │ │ ├── def.objclass.h │ │ ├── pathnames.h │ │ ├── hh │ │ ├── hack.rip.c │ │ ├── def.monst.h │ │ ├── Original_READ_ME │ │ ├── hack.bones.c │ │ ├── hack.wield.c │ │ ├── hack.monst.c │ │ ├── hack.mkmaze.c │ │ ├── hack.mkobj.c │ │ ├── hack.search.c │ │ ├── hack.fix │ │ ├── READ_ME │ │ ├── hack.o_init.c │ │ ├── hack.6 │ │ ├── hack.topl.c │ │ ├── hack.shknam.c │ │ ├── hack.worm.c │ │ ├── config.h │ │ ├── hack.h │ │ └── hack.makemon.c │ ├── Original_READ_ME │ ├── hack.fix │ └── READ_ME ├── media │ ├── FullMoon.png │ └── Tombstone.png ├── BUILD.md └── CODING_STANDARDS.md ├── COPYRIGHT ├── config.h.in ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── CMakePresets.json ├── hh ├── .gitignore └── man └── hack.6 /hackdir/record_lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/.python-version: -------------------------------------------------------------------------------- 1 | 3.11 2 | -------------------------------------------------------------------------------- /OWNER: -------------------------------------------------------------------------------- 1 | Andries Brouwer 2 | mcvax!aeb 3 | -------------------------------------------------------------------------------- /dev/lib/__init__.py: -------------------------------------------------------------------------------- 1 | # restoHack testing library -------------------------------------------------------------------------------- /src/date.h: -------------------------------------------------------------------------------- 1 | char datestring[] = "Tue Jul 23 1985"; 2 | -------------------------------------------------------------------------------- /hackdir/news: -------------------------------------------------------------------------------- 1 | Welcome to Hack 1.0.3 - Restored for modern systems 2 | -------------------------------------------------------------------------------- /docs/historical/original-source/OWNER: -------------------------------------------------------------------------------- 1 | Andries Brouwer 2 | mcvax!aeb 3 | -------------------------------------------------------------------------------- /docs/historical/original-source/date.h: -------------------------------------------------------------------------------- 1 | 2 | char datestring[] = "Tue Jul 23 1985"; 3 | -------------------------------------------------------------------------------- /docs/media/FullMoon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critlist/restoHack/HEAD/docs/media/FullMoon.png -------------------------------------------------------------------------------- /docs/media/Tombstone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Critlist/restoHack/HEAD/docs/media/Tombstone.png -------------------------------------------------------------------------------- /dev/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "dev" 3 | version = "0.1.0" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | requires-python = ">=3.11" 7 | dependencies = [ 8 | "pexpect>=4.9.0", 9 | ] 10 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | HACKDIR=/usr/games/lib/hackdir 3 | HACK=$HACKDIR/hack 4 | MAXNROFPLAYERS=4 5 | 6 | cd $HACKDIR 7 | case $1 in 8 | -s*) 9 | exec $HACK $@ 10 | ;; 11 | *) 12 | exec $HACK $@ $MAXNROFPLAYERS 13 | ;; 14 | esac 15 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | This entire subtree is copyright the Stichting Mathematisch Centrum. 2 | The following copyright notice applies to all files found here. None of 3 | these files contain AT&T proprietary source code. 4 | _____________________________________________________________________________ 5 | 6 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 7 | -------------------------------------------------------------------------------- /src/def.wseg.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.wseg.h - version 1.0.2 */ 3 | 4 | #ifndef NOWORM 5 | /* worm structure */ 6 | struct wseg { 7 | struct wseg *nseg; 8 | xchar wx, wy; 9 | unsigned wdispl : 1; 10 | }; 11 | 12 | #define newseg() (struct wseg *)alloc(sizeof(struct wseg)) 13 | #endif /* NOWORM */ 14 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.gold.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.gold.h - version 1.0.2 */ 3 | 4 | struct gold { 5 | struct gold *ngold; 6 | xchar gx,gy; 7 | long amount; 8 | }; 9 | 10 | extern struct gold *fgold; 11 | struct gold *g_at(); 12 | #define newgold() (struct gold *) alloc(sizeof(struct gold)) 13 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.wseg.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.wseg.h - version 1.0.2 */ 3 | 4 | #ifndef NOWORM 5 | /* worm structure */ 6 | struct wseg { 7 | struct wseg *nseg; 8 | xchar wx,wy; 9 | unsigned wdispl:1; 10 | }; 11 | 12 | #define newseg() (struct wseg *) alloc(sizeof(struct wseg)) 13 | #endif NOWORM 14 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.version.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.version.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "date.h" 6 | 7 | doversion(){ 8 | pline("%s 1.0.3 - last edit %s.", ( 9 | #ifdef QUEST 10 | "Quest" 11 | #else 12 | "Hack" 13 | #endif QUEST 14 | ), datestring); 15 | return(0); 16 | } 17 | -------------------------------------------------------------------------------- /docs/historical/original-source/COPYRIGHT: -------------------------------------------------------------------------------- 1 | This entire subtree is copyright the Stichting Mathematisch Centrum. 2 | The following copyright notice applies to all files found here. None of 3 | these files contain AT&T proprietary source code. 4 | _____________________________________________________________________________ 5 | 6 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 7 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.func_tab.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.func_tab.h - version 1.0.2 */ 3 | 4 | struct func_tab { 5 | char f_char; 6 | int (*f_funct)(); 7 | }; 8 | 9 | extern struct func_tab cmdlist[]; 10 | 11 | struct ext_func_tab { 12 | char *ef_txt; 13 | int (*ef_funct)(); 14 | }; 15 | 16 | extern struct ext_func_tab extcmdlist[]; 17 | -------------------------------------------------------------------------------- /src/hack.mfndpos.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.mfndpos.h - version 1.0.2 */ 3 | 4 | #define ALLOW_TRAPS 0777 5 | #define ALLOW_U 01000 6 | #define ALLOW_M 02000 7 | #define ALLOW_TM 04000 8 | #define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS) 9 | #define ALLOW_SSM 010000 10 | #define ALLOW_ROCK 020000 11 | #define NOTONL 040000 12 | #define NOGARLIC 0100000 13 | -------------------------------------------------------------------------------- /src/def.gold.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.gold.h - version 1.0.2 */ 3 | 4 | struct gold { 5 | struct gold *ngold; 6 | xchar gx, gy; 7 | long amount; 8 | }; 9 | 10 | extern struct gold *fgold; 11 | struct gold *g_at(int x, 12 | int y); /* K&R function with proper parameter declaration */ 13 | #define newgold() (struct gold *)alloc(sizeof(struct gold)) 14 | -------------------------------------------------------------------------------- /src/def.func_tab.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.func_tab.h - version 1.0.2 */ 3 | 4 | struct func_tab { 5 | char f_char; 6 | int (*f_funct)(); 7 | }; 8 | 9 | extern struct func_tab cmdlist[]; 10 | 11 | struct ext_func_tab { 12 | const char *ef_txt; /* MODERN: const because points to string literals */ 13 | int (*ef_funct)(); 14 | }; 15 | 16 | extern struct ext_func_tab extcmdlist[]; 17 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.mfndpos.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.mfndpos.h - version 1.0.2 */ 3 | 4 | #define ALLOW_TRAPS 0777 5 | #define ALLOW_U 01000 6 | #define ALLOW_M 02000 7 | #define ALLOW_TM 04000 8 | #define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS) 9 | #define ALLOW_SSM 010000 10 | #define ALLOW_ROCK 020000 11 | #define NOTONL 040000 12 | #define NOGARLIC 0100000 13 | -------------------------------------------------------------------------------- /docs/historical/original-source/rnd.c: -------------------------------------------------------------------------------- 1 | /* rnd.c - version 1.0.2 */ 2 | /* $FreeBSD$ */ 3 | 4 | #include 5 | 6 | #define RND(x) (random() % x) 7 | 8 | rn1(x,y) 9 | int x,y; 10 | { 11 | return(RND(x)+y); 12 | } 13 | 14 | rn2(x) 15 | int x; 16 | { 17 | return(RND(x)); 18 | } 19 | 20 | rnd(x) 21 | int x; 22 | { 23 | return(RND(x)+1); 24 | } 25 | 26 | d(n,x) 27 | int n,x; 28 | { 29 | int tmp = n; 30 | 31 | while(n--) tmp += RND(x); 32 | return(tmp); 33 | } 34 | -------------------------------------------------------------------------------- /src/def.gen.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.gen.h version 1.0.1: added ONCE flag */ 3 | 4 | struct gen { 5 | struct gen *ngen; 6 | xchar gx, gy; 7 | unsigned gflag; /* 037: trap type; 040: SEEN flag */ 8 | /* 0100: ONCE only */ 9 | #define TRAPTYPE 037 10 | #define SEEN 040 11 | #define ONCE 0100 12 | }; 13 | extern struct gen *fgold, *ftrap; 14 | struct gen *g_at(); 15 | #define newgen() (struct gen *)alloc(sizeof(struct gen)) 16 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.gen.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.gen.h version 1.0.1: added ONCE flag */ 3 | 4 | struct gen { 5 | struct gen *ngen; 6 | xchar gx,gy; 7 | unsigned gflag; /* 037: trap type; 040: SEEN flag */ 8 | /* 0100: ONCE only */ 9 | #define TRAPTYPE 037 10 | #define SEEN 040 11 | #define ONCE 0100 12 | }; 13 | extern struct gen *fgold, *ftrap; 14 | struct gen *g_at(); 15 | #define newgen() (struct gen *) alloc(sizeof(struct gen)) 16 | -------------------------------------------------------------------------------- /src/def.edog.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.edog.h - version 1.0.2 */ 3 | 4 | struct edog { 5 | long hungrytime; /* at this time dog gets hungry */ 6 | long eattime; /* dog is eating */ 7 | long droptime; /* moment dog dropped object */ 8 | unsigned dropdist; /* dist of drpped obj from @ */ 9 | unsigned apport; /* amount of training */ 10 | long whistletime; /* last time he whistled */ 11 | }; 12 | #define EDOG(mp) ((struct edog *)(&(mp->mextra[0]))) 13 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in - Feature detection for restoHack NetBSD/pkgsrc compatibility */ 2 | 3 | /* Header availability */ 4 | #cmakedefine HAVE_BSD_STDLIB_H 1 5 | 6 | /* RNG seeding functions - prefer arc4random_buf, then arc4random, then getentropy */ 7 | #cmakedefine HAVE_ARC4RANDOM_BUF 1 8 | #cmakedefine HAVE_ARC4RANDOM 1 9 | #cmakedefine HAVE_SRANDOMDEV 1 10 | #cmakedefine HAVE_GETENTROPY 1 11 | 12 | /* Library linkage */ 13 | #cmakedefine HAVE_LIBBSD 1 14 | 15 | /* Terminal/curses functions */ 16 | #cmakedefine HAVE_DELAY_OUTPUT 1 -------------------------------------------------------------------------------- /docs/historical/original-source/def.edog.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.edog.h - version 1.0.2 */ 3 | 4 | struct edog { 5 | long hungrytime; /* at this time dog gets hungry */ 6 | long eattime; /* dog is eating */ 7 | long droptime; /* moment dog dropped object */ 8 | unsigned dropdist; /* dist of drpped obj from @ */ 9 | unsigned apport; /* amount of training */ 10 | long whistletime; /* last time he whistled */ 11 | }; 12 | #define EDOG(mp) ((struct edog *)(&(mp->mextra[0]))) 13 | -------------------------------------------------------------------------------- /src/hack.version.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.version.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | /* 6 | * Version information for 1984 Hack - game version and build date display 7 | * Original 1984 source: docs/historical/original-source/hack.version.c 8 | * 9 | * Key modernizations: ANSI C function signatures 10 | */ 11 | 12 | #include "hack.h" 13 | #include "date.h" 14 | int doversion(void) { 15 | pline("%s 1.0.3 - last edit %s.", 16 | ( 17 | #ifdef QUEST 18 | "Quest" 19 | #else 20 | "Hack" 21 | #endif /* QUEST */ 22 | ), 23 | datestring); 24 | return (0); 25 | } 26 | -------------------------------------------------------------------------------- /src/rnd.c: -------------------------------------------------------------------------------- 1 | /* rnd.c - version 1.0.2 */ 2 | /* $FreeBSD$ */ 3 | 4 | /* 5 | * Random number utilities for 1984 Hack - dice rolling and random generation 6 | * Original 1984 source: docs/historical/original-source/rnd.c 7 | * 8 | * Key modernizations: ANSI C function signatures 9 | */ 10 | 11 | #include 12 | 13 | #define RND(x) (random() % x) 14 | 15 | /* MODERN: K&R to ANSI C conversion - function signatures only */ 16 | int rn1(int x, int y) { return (RND(x) + y); } 17 | 18 | int rn2(int x) { return (RND(x)); } 19 | 20 | int rnd(int x) { return (RND(x) + 1); } 21 | 22 | int d(int n, int x) { 23 | int tmp = n; 24 | 25 | while (n--) 26 | tmp += RND(x); 27 | return (tmp); 28 | } 29 | -------------------------------------------------------------------------------- /src/def.mkroom.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.mkroom.h - version 1.0.3 */ 3 | 4 | struct mkroom { 5 | schar lx, hx, ly, hy; /* usually xchar, but hx may be -1 */ 6 | schar rtype, rlit, doorct, fdoor; 7 | }; 8 | 9 | #define MAXNROFROOMS 15 10 | extern struct mkroom rooms[MAXNROFROOMS + 1]; 11 | 12 | #define DOORMAX 100 13 | extern coord doors[DOORMAX]; 14 | 15 | /* various values of rtype */ 16 | /* 0: ordinary room; 8-15: various shops */ 17 | /* Note: some code assumes that >= 8 means shop, so be careful when adding 18 | new roomtypes */ 19 | #define SWAMP 3 20 | #define VAULT 4 21 | #define BEEHIVE 5 22 | #define MORGUE 6 23 | #define ZOO 7 24 | #define SHOPBASE 8 25 | #define WANDSHOP 9 26 | #define GENERAL 15 27 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.mkroom.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.mkroom.h - version 1.0.3 */ 3 | 4 | struct mkroom { 5 | schar lx,hx,ly,hy; /* usually xchar, but hx may be -1 */ 6 | schar rtype,rlit,doorct,fdoor; 7 | }; 8 | 9 | #define MAXNROFROOMS 15 10 | extern struct mkroom rooms[MAXNROFROOMS+1]; 11 | 12 | #define DOORMAX 100 13 | extern coord doors[DOORMAX]; 14 | 15 | /* various values of rtype */ 16 | /* 0: ordinary room; 8-15: various shops */ 17 | /* Note: some code assumes that >= 8 means shop, so be careful when adding 18 | new roomtypes */ 19 | #define SWAMP 3 20 | #define VAULT 4 21 | #define BEEHIVE 5 22 | #define MORGUE 6 23 | #define ZOO 7 24 | #define SHOPBASE 8 25 | #define WANDSHOP 9 26 | #define GENERAL 15 27 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.trap.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.trap.h - version 1.0.2 */ 3 | 4 | struct trap { 5 | struct trap *ntrap; 6 | xchar tx,ty; 7 | unsigned ttyp:5; 8 | unsigned tseen:1; 9 | unsigned once:1; 10 | }; 11 | 12 | extern struct trap *ftrap; 13 | struct trap *t_at(); 14 | #define newtrap() (struct trap *) alloc(sizeof(struct trap)) 15 | 16 | /* various kinds of traps */ 17 | #define BEAR_TRAP 0 18 | #define ARROW_TRAP 1 19 | #define DART_TRAP 2 20 | #define TRAPDOOR 3 21 | #define TELEP_TRAP 4 22 | #define PIT 5 23 | #define SLP_GAS_TRAP 6 24 | #define PIERC 7 25 | #define MIMIC 8 /* used only in mklev.c */ 26 | #define TRAPNUM 9 /* if not less than 32, change sizeof(ttyp) */ 27 | /* see also mtrapseen (bit map) */ 28 | -------------------------------------------------------------------------------- /src/def.trap.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.trap.h - version 1.0.2 */ 3 | 4 | struct trap { 5 | struct trap *ntrap; 6 | xchar tx, ty; 7 | unsigned ttyp : 5; 8 | unsigned tseen : 1; 9 | unsigned once : 1; 10 | }; 11 | 12 | extern struct trap *ftrap; 13 | struct trap *t_at(int x, int y); 14 | #define newtrap() (struct trap *)alloc(sizeof(struct trap)) 15 | 16 | /* various kinds of traps */ 17 | #define BEAR_TRAP 0 18 | #define ARROW_TRAP 1 19 | #define DART_TRAP 2 20 | #define TRAPDOOR 3 21 | #define TELEP_TRAP 4 22 | #define PIT 5 23 | #define SLP_GAS_TRAP 6 24 | #define PIERC 7 25 | #define MIMIC 8 /* used only in mklev.c */ 26 | #define TRAPNUM 9 /* if not less than 32, change sizeof(ttyp) */ 27 | /* see also mtrapseen (bit map) */ 28 | -------------------------------------------------------------------------------- /dev/lib/colors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Shared color utilities for restoHack test tools 4 | """ 5 | 6 | class Colors: 7 | """ANSI color codes for output""" 8 | BLUE = '\033[1;36m' 9 | RED = '\033[1;31m' 10 | GREEN = '\033[1;32m' 11 | YELLOW = '\033[1;33m' 12 | RESET = '\033[0m' 13 | 14 | @classmethod 15 | def say(cls, tag: str, msg: str = "") -> None: 16 | print(f"{cls.BLUE}[{tag}]{cls.RESET} {msg}") 17 | 18 | @classmethod 19 | def error(cls, msg: str) -> None: 20 | print(f"{cls.RED}[ERROR]{cls.RESET} {msg}") 21 | 22 | @classmethod 23 | def success(cls, msg: str) -> None: 24 | print(f"{cls.GREEN}[SUCCESS]{cls.RESET} {msg}") 25 | 26 | @classmethod 27 | def warn(cls, msg: str) -> None: 28 | print(f"{cls.YELLOW}[WARN]{cls.RESET} {msg}") -------------------------------------------------------------------------------- /docs/historical/original-source/def.permonst.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.permonst.h - version 1.0.2 */ 3 | 4 | struct permonst { 5 | char *mname,mlet; 6 | schar mlevel,mmove,ac,damn,damd; 7 | unsigned pxlth; 8 | }; 9 | 10 | extern struct permonst mons[]; 11 | #define PM_ACID_BLOB &mons[7] 12 | #define PM_ZOMBIE &mons[13] 13 | #define PM_PIERCER &mons[17] 14 | #define PM_KILLER_BEE &mons[26] 15 | #define PM_WRAITH &mons[33] 16 | #define PM_MIMIC &mons[37] 17 | #define PM_VAMPIRE &mons[43] 18 | #define PM_CHAMELEON &mons[47] 19 | #define PM_DEMON &mons[54] 20 | #define PM_MINOTAUR &mons[55] /* last in mons array */ 21 | #define PM_SHK &mons[56] /* very last */ 22 | #define PM_GHOST &pm_ghost 23 | #define PM_EEL &pm_eel 24 | #define PM_WIZARD &pm_wizard 25 | #define CMNUM 55 /* number of common monsters */ 26 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.track.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.track.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | #define UTSZ 50 8 | 9 | coord utrack[UTSZ]; 10 | int utcnt = 0; 11 | int utpnt = 0; 12 | 13 | initrack(){ 14 | utcnt = utpnt = 0; 15 | } 16 | 17 | /* add to track */ 18 | settrack(){ 19 | if(utcnt < UTSZ) utcnt++; 20 | if(utpnt == UTSZ) utpnt = 0; 21 | utrack[utpnt].x = u.ux; 22 | utrack[utpnt].y = u.uy; 23 | utpnt++; 24 | } 25 | 26 | coord * 27 | gettrack(x,y) int x,y; { 28 | int i,cnt,dist; 29 | coord tc; 30 | cnt = utcnt; 31 | for(i = utpnt-1; cnt--; i--){ 32 | if(i == -1) i = UTSZ-1; 33 | tc = utrack[i]; 34 | dist = (x-tc.x)*(x-tc.x) + (y-tc.y)*(y-tc.y); 35 | if(dist < 3) 36 | return(dist ? &(utrack[i]) : 0); 37 | } 38 | return(0); 39 | } 40 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.eshk.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.eshk.h - version 1.0.2 : added 'following' */ 3 | 4 | #define BILLSZ 200 5 | struct bill_x { 6 | unsigned bo_id; 7 | unsigned useup:1; 8 | unsigned bquan:7; 9 | unsigned price; /* price per unit */ 10 | }; 11 | 12 | struct eshk { 13 | long int robbed; /* amount stolen by most recent customer */ 14 | boolean following; /* following customer since he owes us sth */ 15 | schar shoproom; /* index in rooms; set by inshop() */ 16 | coord shk; /* usual position shopkeeper */ 17 | coord shd; /* position shop door */ 18 | int shoplevel; /* level of his shop */ 19 | int billct; 20 | struct bill_x bill[BILLSZ]; 21 | int visitct; /* nr of visits by most recent customer */ 22 | char customer[PL_NSIZ]; /* most recent customer */ 23 | char shknam[PL_NSIZ]; 24 | }; 25 | -------------------------------------------------------------------------------- /src/def.eshk.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.eshk.h - version 1.0.2 : added 'following' */ 3 | 4 | #define BILLSZ 200 5 | struct bill_x { 6 | unsigned bo_id; 7 | unsigned useup : 1; 8 | unsigned bquan : 7; 9 | unsigned price; /* price per unit */ 10 | }; 11 | 12 | struct eshk { 13 | long int robbed; /* amount stolen by most recent customer */ 14 | boolean following; /* following customer since he owes us sth */ 15 | schar shoproom; /* index in rooms; set by inshop() */ 16 | coord shk; /* usual position shopkeeper */ 17 | coord shd; /* position shop door */ 18 | int shoplevel; /* level of his shop */ 19 | int billct; 20 | struct bill_x bill[BILLSZ]; 21 | int visitct; /* nr of visits by most recent customer */ 22 | char customer[PL_NSIZ]; /* most recent customer */ 23 | char shknam[PL_NSIZ]; 24 | }; 25 | -------------------------------------------------------------------------------- /src/compat.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* compat.h - MODERN compatibility layer for portability */ 3 | 4 | /** 5 | * MODERN ADDITION (2025): Compatibility header for portability 6 | * WHY: Centralize BSD function aliases and platform-specific code 7 | * HOW: Use macros to map BSD names to standard C library functions 8 | * PRESERVES: Original 1984 BSD function calls in code 9 | * ADDS: Windows/MSVC compatibility and cleaner organization 10 | */ 11 | 12 | #ifndef RESTOHACK_COMPAT_H 13 | #define RESTOHACK_COMPAT_H 14 | 15 | #include 16 | 17 | /* Historic BSD names used in 1984 code */ 18 | #ifndef index 19 | #define index strchr 20 | #endif 21 | #ifndef rindex 22 | #define rindex strrchr 23 | #endif 24 | 25 | /* Windows/MSVC shims (still 1980s semantics) */ 26 | #if defined(_MSC_VER) 27 | #include 28 | #define srandom srand 29 | #define random rand 30 | #endif 31 | 32 | #endif /* RESTOHACK_COMPAT_H */ -------------------------------------------------------------------------------- /docs/historical/original-source/alloc.c: -------------------------------------------------------------------------------- 1 | /* alloc.c - version 1.0.2 */ 2 | /* $FreeBSD$ */ 3 | 4 | #include 5 | 6 | #ifdef LINT 7 | 8 | /* 9 | a ridiculous definition, suppressing 10 | "possible pointer alignment problem" for (long *) malloc() 11 | "enlarg defined but never used" 12 | "ftell defined (in ) but never used" 13 | from lint 14 | */ 15 | #include 16 | long * 17 | alloc(n) unsigned n; { 18 | long dummy = ftell(stderr); 19 | if(n) dummy = 0; /* make sure arg is used */ 20 | return(&dummy); 21 | } 22 | 23 | #else 24 | 25 | long * 26 | alloc(lth) 27 | unsigned lth; 28 | { 29 | char *ptr; 30 | 31 | if(!(ptr = malloc(lth))) 32 | panic("Cannot get %d bytes", lth); 33 | return((long *) ptr); 34 | } 35 | 36 | long * 37 | enlarge(ptr,lth) 38 | char *ptr; 39 | unsigned lth; 40 | { 41 | char *nptr; 42 | 43 | if(!(nptr = realloc(ptr,lth))) 44 | panic("Cannot reallocate %d bytes", lth); 45 | return((long *) nptr); 46 | } 47 | 48 | #endif LINT 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what went wrong in restoHack. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the bug (if known): 15 | 1. Start a new game / load a save 16 | 2. Perform action(s) [e.g., "wield a cursed dagger", "descend to level 5"] 17 | 3. Observe crash / unexpected behavior 18 | 19 | **Expected behavior** 20 | What you thought should have happened instead. 21 | 22 | **Screenshots / Logs** 23 | If possible, include: 24 | - Screenshot of the game screen or terminal output 25 | - Any error messages, sanitizer logs, or crash output 26 | 27 | **System information** 28 | - OS/Distribution: [e.g. Arch Linux, Ubuntu 24.04, FreeBSD] 29 | - Build type: [prebuilt release binary / built from source] 30 | - Compiler & version (if built from source): [e.g. gcc 13.2, clang 18] 31 | - restoHack version: [e.g. v1.1.0, commit hash] 32 | 33 | **Additional context** 34 | Anything else that might help us reproduce or understand the bug. 35 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "configurePresets": [ 4 | { 5 | "name": "release", 6 | "generator": "Ninja", 7 | "binaryDir": "build", 8 | "cacheVariables": { 9 | "CMAKE_BUILD_TYPE": "Release", 10 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" 11 | } 12 | }, 13 | { 14 | "name": "debug", 15 | "generator": "Ninja", 16 | "binaryDir": "build-debug", 17 | "cacheVariables": { 18 | "CMAKE_BUILD_TYPE": "Debug", 19 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" 20 | } 21 | }, 22 | { 23 | "name": "relwithdebinfo", 24 | "generator": "Ninja", 25 | "binaryDir": "build-relwithdebinfo", 26 | "cacheVariables": { 27 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 28 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" 29 | } 30 | } 31 | ], 32 | "buildPresets": [ 33 | { 34 | "name": "release", 35 | "configurePreset": "release" 36 | }, 37 | { 38 | "name": "debug", 39 | "configurePreset": "debug" 40 | }, 41 | { 42 | "name": "relwithdebinfo", 43 | "configurePreset": "relwithdebinfo" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/def.permonst.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.permonst.h - version 1.0.2 */ 3 | 4 | /** 5 | * MODERN ADDITION (2025): const-qualified monster name string 6 | * 7 | * WHY: String literals are const char*, causing 251 discarded-qualifier 8 | * warnings HOW: Changed mname from char* to const char* for type safety 9 | * 10 | * PRESERVES: All original monster data and functionality 11 | * ADDS: Type safety and eliminates const qualifier warnings 12 | */ 13 | struct permonst { 14 | const char *mname; /* MODERN: const-qualified for string literal safety */ 15 | char mlet; 16 | schar mlevel, mmove, ac, damn, damd; 17 | unsigned pxlth; 18 | }; 19 | 20 | extern struct permonst mons[]; 21 | #define PM_ACID_BLOB &mons[7] 22 | #define PM_ZOMBIE &mons[13] 23 | #define PM_PIERCER &mons[17] 24 | #define PM_KILLER_BEE &mons[26] 25 | #define PM_WRAITH &mons[33] 26 | #define PM_MIMIC &mons[37] 27 | #define PM_VAMPIRE &mons[43] 28 | #define PM_CHAMELEON &mons[47] 29 | #define PM_DEMON &mons[54] 30 | #define PM_MINOTAUR &mons[55] /* last in mons array */ 31 | #define PM_SHK &mons[56] /* very last */ 32 | #define PM_GHOST &pm_ghost 33 | #define PM_EEL &pm_eel 34 | #define PM_WIZARD &pm_wizard 35 | #define CMNUM 55 /* number of common monsters */ 36 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.Decl.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.Decl.c - version 1.0.3 */ 3 | 4 | #include "hack.h" 5 | char nul[40]; /* contains zeros */ 6 | char plname[PL_NSIZ]; /* player name */ 7 | char lock[PL_NSIZ+4] = "1lock"; /* long enough for login name .99 */ 8 | 9 | boolean in_mklev, restoring; 10 | 11 | struct rm levl[COLNO][ROWNO]; /* level map */ 12 | #ifndef QUEST 13 | #include "def.mkroom.h" 14 | struct mkroom rooms[MAXNROFROOMS+1]; 15 | coord doors[DOORMAX]; 16 | #endif QUEST 17 | struct monst *fmon = 0; 18 | struct trap *ftrap = 0; 19 | struct gold *fgold = 0; 20 | struct obj *fobj = 0, *fcobj = 0, *invent = 0, *uwep = 0, *uarm = 0, 21 | *uarm2 = 0, *uarmh = 0, *uarms = 0, *uarmg = 0, *uright = 0, 22 | *uleft = 0, *uchain = 0, *uball = 0; 23 | struct flag flags; 24 | struct you u; 25 | struct monst youmonst; /* dummy; used as return value for boomhit */ 26 | 27 | xchar dlevel = 1; 28 | xchar xupstair, yupstair, xdnstair, ydnstair; 29 | char *save_cm = 0, *killer, *nomovemsg; 30 | 31 | long moves = 1; 32 | long wailmsg = 0; 33 | 34 | int multi = 0; 35 | char genocided[60]; 36 | char fut_geno[60]; 37 | 38 | xchar curx,cury; 39 | xchar seelx, seehx, seely, seehy; /* corners of lit room */ 40 | 41 | coord bhitpos; 42 | 43 | char quitchars[] = " \r\n\033"; 44 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.rm.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.rm.h - version 1.0.2 */ 3 | 4 | /* Level location types */ 5 | #define HWALL 1 6 | #define VWALL 2 7 | #define SDOOR 3 8 | #define SCORR 4 9 | #define LDOOR 5 10 | #define POOL 6 /* not yet fully implemented */ 11 | /* this should in fact be a bit like lit */ 12 | #define DOOR 7 13 | #define CORR 8 14 | #define ROOM 9 15 | #define STAIRS 10 16 | 17 | /* 18 | * Avoid using the level types in inequalities: 19 | * these types are subject to change. 20 | * Instead, use one of the macros below. 21 | */ 22 | #define IS_WALL(typ) ((typ) <= VWALL) 23 | #define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */ 24 | #define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ 25 | #define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM or STAIRS */ 26 | #define ZAP_POS(typ) ((typ) > DOOR) 27 | 28 | /* 29 | * A few of the associated symbols are not hardwired. 30 | */ 31 | #ifdef QUEST 32 | #define CORR_SYM ':' 33 | #else 34 | #define CORR_SYM '#' 35 | #endif QUEST 36 | #define POOL_SYM '}' 37 | 38 | #define ERRCHAR '{' 39 | 40 | /* 41 | * The structure describing a coordinate position. 42 | * Before adding fields, remember that this will significantly affect 43 | * the size of temporary files and save files. 44 | */ 45 | struct rm { 46 | char scrsym; 47 | unsigned typ:5; 48 | unsigned new:1; 49 | unsigned seen:1; 50 | unsigned lit:1; 51 | }; 52 | extern struct rm levl[COLNO][ROWNO]; 53 | -------------------------------------------------------------------------------- /src/def.rm.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.rm.h - version 1.0.2 */ 3 | 4 | /* Level location types */ 5 | #define HWALL 1 6 | #define VWALL 2 7 | #define SDOOR 3 8 | #define SCORR 4 9 | #define LDOOR 5 10 | #define POOL 6 /* not yet fully implemented */ 11 | /* this should in fact be a bit like lit */ 12 | #define DOOR 7 13 | #define CORR 8 14 | #define ROOM 9 15 | #define STAIRS 10 16 | 17 | /* 18 | * Avoid using the level types in inequalities: 19 | * these types are subject to change. 20 | * Instead, use one of the macros below. 21 | */ 22 | #define IS_WALL(typ) ((typ) <= VWALL) 23 | #define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */ 24 | #define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ 25 | #define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM or STAIRS */ 26 | #define ZAP_POS(typ) ((typ) > DOOR) 27 | 28 | /* 29 | * A few of the associated symbols are not hardwired. 30 | */ 31 | #ifdef QUEST 32 | #define CORR_SYM ':' 33 | #else 34 | #define CORR_SYM '#' 35 | #endif /* QUEST */ 36 | #define POOL_SYM '}' 37 | 38 | #define ERRCHAR '{' 39 | 40 | /* 41 | * The structure describing a coordinate position. 42 | * Before adding fields, remember that this will significantly affect 43 | * the size of temporary files and save files. 44 | */ 45 | struct rm { 46 | char scrsym; 47 | unsigned typ : 5; 48 | unsigned new : 1; 49 | unsigned seen : 1; 50 | unsigned lit : 1; 51 | }; 52 | extern struct rm levl[COLNO][ROWNO]; 53 | -------------------------------------------------------------------------------- /src/hack.track.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.track.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | /* 6 | * Player tracking system for 1984 Hack - movement history for monsters 7 | * Original 1984 source: docs/historical/original-source/hack.track.c 8 | * 9 | * Key modernizations: ANSI C function signatures 10 | */ 11 | 12 | #include "hack.h" 13 | 14 | #define UTSZ 50 15 | 16 | coord utrack[UTSZ]; 17 | int utcnt = 0; 18 | int utpnt = 0; 19 | 20 | void initrack(void) { utcnt = utpnt = 0; } 21 | 22 | /* add to track */ 23 | void settrack(void) { 24 | if (utcnt < UTSZ) 25 | utcnt++; 26 | if (utpnt >= UTSZ) /* MODERN: bounds check prevents array overflow */ 27 | utpnt = 0; 28 | if (utpnt < 0 || utpnt >= UTSZ) return; /* MODERN: defensive bounds check */ 29 | utrack[utpnt].x = u.ux; 30 | utrack[utpnt].y = u.uy; 31 | utpnt++; 32 | if (utpnt >= UTSZ) /* MODERN: wrap after increment to prevent overflow */ 33 | utpnt = 0; 34 | } 35 | 36 | coord *gettrack(int x, int y) { 37 | int i, cnt, dist; 38 | coord tc; 39 | cnt = utcnt; 40 | for (i = utpnt - 1; cnt--; i--) { 41 | if (i < 0) /* MODERN: simplified bounds check */ 42 | i = UTSZ - 1; 43 | if (i < 0 || i >= UTSZ) break; /* MODERN: defensive bounds check prevents array overflow */ 44 | tc = utrack[i]; 45 | dist = (x - tc.x) * (x - tc.x) + (y - tc.y) * (y - tc.y); 46 | if (dist < 3) 47 | return (dist ? &(utrack[i]) : 0); 48 | } 49 | return (0); 50 | } 51 | -------------------------------------------------------------------------------- /dev/valgrind.supp: -------------------------------------------------------------------------------- 1 | # Valgrind suppressions for restoHack 2 | # Ignore system library leaks that are not our responsibility 3 | 4 | # ncurses library leaks - terminal handling 5 | { 6 | ncurses_malloc 7 | Memcheck:Leak 8 | ... 9 | obj:*/libncurses* 10 | } 11 | 12 | { 13 | ncurses_tinfo 14 | Memcheck:Leak 15 | ... 16 | obj:*/libtinfo* 17 | } 18 | 19 | { 20 | ncurses_setupterm 21 | Memcheck:Leak 22 | fun:malloc 23 | ... 24 | fun:setupterm 25 | } 26 | 27 | { 28 | ncurses_newterm 29 | Memcheck:Leak 30 | fun:malloc 31 | ... 32 | fun:newterm 33 | } 34 | 35 | # glibc dynamic loading - not our leaks 36 | { 37 | glibc_dlopen 38 | Memcheck:Leak 39 | fun:malloc 40 | ... 41 | fun:_dl_* 42 | } 43 | 44 | { 45 | glibc_locale 46 | Memcheck:Leak 47 | fun:malloc 48 | ... 49 | fun:*locale* 50 | } 51 | 52 | # Termcap/terminfo database loading 53 | { 54 | termcap_buffer 55 | Memcheck:Leak 56 | fun:malloc 57 | ... 58 | fun:tgetent 59 | } 60 | 61 | { 62 | termcap_tgetstr 63 | Memcheck:Leak 64 | fun:malloc 65 | ... 66 | fun:tgetstr 67 | } 68 | 69 | # System getenv and environment handling 70 | { 71 | getenv_malloc 72 | Memcheck:Leak 73 | fun:malloc 74 | ... 75 | fun:getenv 76 | } 77 | 78 | # Still reachable blocks in glibc - not actual leaks 79 | { 80 | glibc_still_reachable 81 | Memcheck:Leak 82 | match-leak-kinds: reachable 83 | ... 84 | obj:*/libc-* 85 | } 86 | 87 | { 88 | glibc_still_reachable_ld 89 | Memcheck:Leak 90 | match-leak-kinds: reachable 91 | ... 92 | obj:*/ld-* 93 | } -------------------------------------------------------------------------------- /docs/historical/original-source/hack.ioctl.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.ioctl.c - version 1.0.2 */ 3 | /* $FreeBSD$ 4 | 5 | /* This cannot be part of hack.tty.c (as it was earlier) since on some 6 | systems (e.g. MUNIX) the include files and 7 | define the same constants, and the C preprocessor complains. */ 8 | #include 9 | #include "config.h" 10 | #ifdef BSD 11 | #include 12 | struct ltchars ltchars, ltchars0; 13 | #else 14 | #include /* also includes part of */ 15 | struct termio termio; 16 | #endif BSD 17 | 18 | getioctls() { 19 | #ifdef BSD 20 | (void) ioctl(fileno(stdin), (int) TIOCGLTC, (char *) <chars); 21 | (void) ioctl(fileno(stdin), (int) TIOCSLTC, (char *) <chars0); 22 | #else 23 | (void) ioctl(fileno(stdin), (int) TCGETA, &termio); 24 | #endif BSD 25 | } 26 | 27 | setioctls() { 28 | #ifdef BSD 29 | (void) ioctl(fileno(stdin), (int) TIOCSLTC, (char *) <chars); 30 | #else 31 | (void) ioctl(fileno(stdin), (int) TCSETA, &termio); 32 | #endif BSD 33 | } 34 | 35 | #ifdef SUSPEND /* implies BSD */ 36 | #include 37 | dosuspend() { 38 | #ifdef SIGTSTP 39 | if(signal(SIGTSTP, SIG_IGN) == SIG_DFL) { 40 | settty((char *) 0); 41 | (void) signal(SIGTSTP, SIG_DFL); 42 | (void) kill(0, SIGTSTP); 43 | gettty(); 44 | setftty(); 45 | docrt(); 46 | } else { 47 | pline("I don't think your shell has job control."); 48 | } 49 | #else SIGTSTP 50 | pline("Sorry, it seems we have no SIGTSTP here. Try ! or S."); 51 | #endif SIGTSTP 52 | return(0); 53 | } 54 | #endif SUSPEND 55 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.worn.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.worn.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | struct worn { 8 | long w_mask; 9 | struct obj **w_obj; 10 | } worn[] = { 11 | { W_ARM, &uarm }, 12 | { W_ARM2, &uarm2 }, 13 | { W_ARMH, &uarmh }, 14 | { W_ARMS, &uarms }, 15 | { W_ARMG, &uarmg }, 16 | { W_RINGL, &uleft }, 17 | { W_RINGR, &uright }, 18 | { W_WEP, &uwep }, 19 | { W_BALL, &uball }, 20 | { W_CHAIN, &uchain }, 21 | { 0, 0 } 22 | }; 23 | 24 | setworn(obj, mask) 25 | struct obj *obj; 26 | long mask; 27 | { 28 | struct worn *wp; 29 | struct obj *oobj; 30 | 31 | for(wp = worn; wp->w_mask; wp++) if(wp->w_mask & mask) { 32 | oobj = *(wp->w_obj); 33 | if(oobj && !(oobj->owornmask & wp->w_mask)) 34 | impossible("Setworn: mask = %ld.", wp->w_mask); 35 | if(oobj) oobj->owornmask &= ~wp->w_mask; 36 | if(obj && oobj && wp->w_mask == W_ARM){ 37 | if(uarm2) { 38 | impossible("Setworn: uarm2 set?"); 39 | } else 40 | setworn(uarm, W_ARM2); 41 | } 42 | *(wp->w_obj) = obj; 43 | if(obj) obj->owornmask |= wp->w_mask; 44 | } 45 | if(uarm2 && !uarm) { 46 | uarm = uarm2; 47 | uarm2 = 0; 48 | uarm->owornmask ^= (W_ARM | W_ARM2); 49 | } 50 | } 51 | 52 | /* called e.g. when obj is destroyed */ 53 | setnotworn(obj) struct obj *obj; { 54 | struct worn *wp; 55 | 56 | for(wp = worn; wp->w_mask; wp++) 57 | if(obj == *(wp->w_obj)) { 58 | *(wp->w_obj) = 0; 59 | obj->owornmask &= ~wp->w_mask; 60 | } 61 | if(uarm2 && !uarm) { 62 | uarm = uarm2; 63 | uarm2 = 0; 64 | uarm->owornmask ^= (W_ARM | W_ARM2); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.timeout.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.timeout.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | timeout(){ 8 | struct prop *upp; 9 | if(Stoned) stoned_dialogue(); 10 | for(upp = u.uprops; upp < u.uprops+SIZE(u.uprops); upp++) 11 | if((upp->p_flgs & TIMEOUT) && !--upp->p_flgs) { 12 | if(upp->p_tofn) (*upp->p_tofn)(); 13 | else switch(upp - u.uprops){ 14 | case STONED: 15 | killer = "cockatrice"; 16 | done("died"); 17 | break; 18 | case SICK: 19 | pline("You die because of food poisoning."); 20 | killer = u.usick_cause; 21 | done("died"); 22 | break; 23 | case FAST: 24 | pline("You feel yourself slowing down."); 25 | break; 26 | case CONFUSION: 27 | pline("You feel less confused now."); 28 | break; 29 | case BLIND: 30 | pline("You can see again."); 31 | setsee(); 32 | break; 33 | case INVIS: 34 | on_scr(u.ux,u.uy); 35 | pline("You are no longer invisible."); 36 | break; 37 | case WOUNDED_LEGS: 38 | heal_legs(); 39 | break; 40 | } 41 | } 42 | } 43 | 44 | /* He is being petrified - dialogue by inmet!tower */ 45 | char *stoned_texts[] = { 46 | "You are slowing down.", /* 5 */ 47 | "Your limbs are stiffening.", /* 4 */ 48 | "Your limbs have turned to stone.", /* 3 */ 49 | "You have turned to stone.", /* 2 */ 50 | "You are a statue." /* 1 */ 51 | }; 52 | 53 | stoned_dialogue() 54 | { 55 | long i = (Stoned & TIMEOUT); 56 | 57 | if(i > 0 && i <= SIZE(stoned_texts)) 58 | pline(stoned_texts[SIZE(stoned_texts) - i]); 59 | if(i == 5) 60 | Fast = 0; 61 | if(i == 3) 62 | nomul(-3); 63 | } 64 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.obj.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.obj.h - version 1.0.3 */ 3 | 4 | struct obj { 5 | struct obj *nobj; 6 | unsigned o_id; 7 | unsigned o_cnt_id; /* id of container object is in */ 8 | xchar ox,oy; 9 | xchar odx,ody; 10 | uchar otyp; 11 | uchar owt; 12 | uchar quan; /* use oextra for tmp gold objects */ 13 | schar spe; /* quality of weapon, armor or ring (+ or -) 14 | number of charges for wand ( >= -1 ) 15 | special for uball and amulet %% BAH */ 16 | char olet; 17 | char invlet; 18 | Bitfield(oinvis,1); /* not yet implemented */ 19 | Bitfield(odispl,1); 20 | Bitfield(known,1); /* exact nature known */ 21 | Bitfield(dknown,1); /* color or text known */ 22 | Bitfield(cursed,1); 23 | Bitfield(unpaid,1); /* on some bill */ 24 | Bitfield(rustfree,1); 25 | Bitfield(onamelth,6); 26 | long age; /* creation date */ 27 | long owornmask; 28 | #define W_ARM 01L 29 | #define W_ARM2 02L 30 | #define W_ARMH 04L 31 | #define W_ARMS 010L 32 | #define W_ARMG 020L 33 | #define W_ARMOR (W_ARM | W_ARM2 | W_ARMH | W_ARMS | W_ARMG) 34 | #define W_RINGL 010000L /* make W_RINGL = RING_LEFT (see uprop) */ 35 | #define W_RINGR 020000L 36 | #define W_RING (W_RINGL | W_RINGR) 37 | #define W_WEP 01000L 38 | #define W_BALL 02000L 39 | #define W_CHAIN 04000L 40 | long oextra[1]; /* used for name of ordinary objects - length 41 | is flexible; amount for tmp gold objects */ 42 | }; 43 | 44 | extern struct obj *fobj; 45 | 46 | #define newobj(xl) (struct obj *) alloc((unsigned)(xl) + sizeof(struct obj)) 47 | #define ONAME(otmp) ((char *) otmp->oextra) 48 | #define OGOLD(otmp) (otmp->oextra[0]) 49 | -------------------------------------------------------------------------------- /src/def.obj.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.obj.h - version 1.0.3 */ 3 | 4 | struct obj { 5 | struct obj *nobj; 6 | unsigned o_id; 7 | unsigned o_cnt_id; /* id of container object is in */ 8 | xchar ox, oy; 9 | xchar odx, ody; 10 | uchar otyp; 11 | uchar owt; 12 | uchar quan; /* use oextra for tmp gold objects */ 13 | schar spe; /* quality of weapon, armor or ring (+ or -) 14 | number of charges for wand ( >= -1 ) 15 | special for uball and amulet %% BAH */ 16 | char olet; 17 | char invlet; 18 | Bitfield(oinvis, 1); /* not yet implemented */ 19 | Bitfield(odispl, 1); 20 | Bitfield(known, 1); /* exact nature known */ 21 | Bitfield(dknown, 1); /* color or text known */ 22 | Bitfield(cursed, 1); 23 | Bitfield(unpaid, 1); /* on some bill */ 24 | Bitfield(rustfree, 1); 25 | Bitfield(onamelth, 6); 26 | long age; /* creation date */ 27 | long owornmask; 28 | #define W_ARM 01L 29 | #define W_ARM2 02L 30 | #define W_ARMH 04L 31 | #define W_ARMS 010L 32 | #define W_ARMG 020L 33 | #define W_ARMOR (W_ARM | W_ARM2 | W_ARMH | W_ARMS | W_ARMG) 34 | #define W_RINGL 010000L /* make W_RINGL = RING_LEFT (see uprop) */ 35 | #define W_RINGR 020000L 36 | #define W_RING (W_RINGL | W_RINGR) 37 | #define W_WEP 01000L 38 | #define W_BALL 02000L 39 | #define W_CHAIN 04000L 40 | long oextra[1]; /* used for name of ordinary objects - length 41 | is flexible; amount for tmp gold objects */ 42 | }; 43 | 44 | extern struct obj *fobj; 45 | 46 | #define newobj(xl) (struct obj *)alloc((unsigned)(xl) + sizeof(struct obj)) 47 | #define ONAME(otmp) ((char *)otmp->oextra) 48 | #define OGOLD(otmp) (otmp->oextra[0]) 49 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.rumors.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.rumors.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include 6 | #include "hack.h" /* for RUMORFILE and BSD (index) */ 7 | #define CHARSZ 8 /* number of bits in a char */ 8 | extern long *alloc(); 9 | extern char *index(); 10 | int n_rumors = 0; 11 | int n_used_rumors = -1; 12 | char *usedbits; 13 | 14 | init_rumors(rumf) FILE *rumf; { 15 | int i; 16 | n_used_rumors = 0; 17 | while(skipline(rumf)) n_rumors++; 18 | rewind(rumf); 19 | i = n_rumors/CHARSZ; 20 | usedbits = (char *) alloc((unsigned)(i+1)); 21 | for( ; i>=0; i--) usedbits[i] = 0; 22 | } 23 | 24 | skipline(rumf) FILE *rumf; { 25 | char line[COLNO]; 26 | while(1) { 27 | if(!fgets(line, sizeof(line), rumf)) return(0); 28 | if(index(line, '\n')) return(1); 29 | } 30 | } 31 | 32 | outline(rumf) FILE *rumf; { 33 | char line[COLNO]; 34 | char *ep; 35 | if(!fgets(line, sizeof(line), rumf)) return; 36 | if((ep = index(line, '\n')) != 0) *ep = 0; 37 | pline("This cookie has a scrap of paper inside! It reads: "); 38 | pline(line); 39 | } 40 | 41 | outrumor(){ 42 | int rn,i; 43 | FILE *rumf; 44 | if(n_rumors <= n_used_rumors || 45 | (rumf = fopen(RUMORFILE, "r")) == (FILE *) 0) return; 46 | if(n_used_rumors < 0) init_rumors(rumf); 47 | if(!n_rumors) goto none; 48 | rn = rn2(n_rumors - n_used_rumors); 49 | i = 0; 50 | while(rn || used(i)) { 51 | (void) skipline(rumf); 52 | if(!used(i)) rn--; 53 | i++; 54 | } 55 | usedbits[i/CHARSZ] |= (1 << (i % CHARSZ)); 56 | n_used_rumors++; 57 | outline(rumf); 58 | none: 59 | (void) fclose(rumf); 60 | } 61 | 62 | used(i) int i; { 63 | return(usedbits[i/CHARSZ] & (1 << (i % CHARSZ))); 64 | } 65 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.flag.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.flag.h - version 1.0.3 */ 3 | 4 | struct flag { 5 | unsigned ident; /* social security number for each monster */ 6 | unsigned debug:1; /* in debugging mode */ 7 | #define wizard flags.debug 8 | unsigned toplin:2; /* a top line (message) has been printed */ 9 | /* 0: top line empty; 2: no --More-- reqd. */ 10 | unsigned cbreak:1; /* in cbreak mode, rogue format */ 11 | unsigned standout:1; /* use standout for --More-- */ 12 | unsigned nonull:1; /* avoid sending nulls to the terminal */ 13 | unsigned time:1; /* display elapsed 'time' */ 14 | unsigned nonews:1; /* suppress news printing */ 15 | unsigned notombstone:1; 16 | unsigned end_top, end_around; /* describe desired score list */ 17 | unsigned end_own:1; /* idem (list all own scores) */ 18 | unsigned no_rest_on_space:1; /* spaces are ignored */ 19 | unsigned beginner:1; 20 | unsigned female:1; 21 | unsigned invlet_constant:1; /* let objects keep their 22 | inventory symbol */ 23 | unsigned move:1; 24 | unsigned mv:1; 25 | unsigned run:3; /* 0: h (etc), 1: H (etc), 2: fh (etc) */ 26 | /* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */ 27 | unsigned nopick:1; /* do not pickup objects */ 28 | unsigned echo:1; /* 1 to echo characters */ 29 | unsigned botl:1; /* partially redo status line */ 30 | unsigned botlx:1; /* print an entirely new bottom line */ 31 | unsigned nscrinh:1; /* inhibit nscr() in pline(); */ 32 | unsigned made_amulet:1; 33 | unsigned no_of_wizards:2;/* 0, 1 or 2 (wizard and his shadow) */ 34 | /* reset from 2 to 1, but never to 0 */ 35 | unsigned moonphase:3; 36 | #define NEW_MOON 0 37 | #define FULL_MOON 4 38 | 39 | }; 40 | 41 | extern struct flag flags; 42 | 43 | -------------------------------------------------------------------------------- /dev/uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 2 3 | requires-python = ">=3.11" 4 | 5 | [[package]] 6 | name = "dev" 7 | version = "0.1.0" 8 | source = { virtual = "." } 9 | dependencies = [ 10 | { name = "pexpect" }, 11 | ] 12 | 13 | [package.metadata] 14 | requires-dist = [{ name = "pexpect", specifier = ">=4.9.0" }] 15 | 16 | [[package]] 17 | name = "pexpect" 18 | version = "4.9.0" 19 | source = { registry = "https://pypi.org/simple" } 20 | dependencies = [ 21 | { name = "ptyprocess" }, 22 | ] 23 | sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } 24 | wheels = [ 25 | { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, 26 | ] 27 | 28 | [[package]] 29 | name = "ptyprocess" 30 | version = "0.7.0" 31 | source = { registry = "https://pypi.org/simple" } 32 | sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } 33 | wheels = [ 34 | { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, 35 | ] 36 | -------------------------------------------------------------------------------- /src/hack.worn.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.worn.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | struct worn { 8 | long w_mask; 9 | struct obj **w_obj; 10 | } worn[] = {{W_ARM, &uarm}, 11 | {W_ARM2, &uarm2}, 12 | {W_ARMH, &uarmh}, 13 | {W_ARMS, &uarms}, 14 | {W_ARMG, &uarmg}, 15 | {W_RINGL, &uleft}, 16 | {W_RINGR, &uright}, 17 | {W_WEP, &uwep}, 18 | {W_BALL, &uball}, 19 | {W_CHAIN, &uchain}, 20 | {0, 0}}; 21 | 22 | void setworn(struct obj *obj, long mask) { 23 | struct worn *wp; 24 | struct obj *oobj; 25 | 26 | for (wp = worn; wp->w_mask; wp++) 27 | if (wp->w_mask & mask) { 28 | oobj = *(wp->w_obj); 29 | if (oobj && !(oobj->owornmask & wp->w_mask)) 30 | impossible("Setworn: mask = %ld.", (int)wp->w_mask, 0); 31 | if (oobj) 32 | oobj->owornmask &= ~wp->w_mask; 33 | if (obj && oobj && wp->w_mask == W_ARM) { 34 | if (uarm2) { 35 | impossible("Setworn: uarm2 set?", 0, 0); 36 | } else 37 | setworn(uarm, W_ARM2); 38 | } 39 | *(wp->w_obj) = obj; 40 | if (obj) 41 | obj->owornmask |= wp->w_mask; 42 | } 43 | if (uarm2 && !uarm) { 44 | uarm = uarm2; 45 | uarm2 = 0; 46 | uarm->owornmask ^= (W_ARM | W_ARM2); 47 | } 48 | } 49 | 50 | /* called e.g. when obj is destroyed */ 51 | void setnotworn(struct obj *obj) { 52 | struct worn *wp; 53 | 54 | for (wp = worn; wp->w_mask; wp++) 55 | if (obj == *(wp->w_obj)) { 56 | *(wp->w_obj) = 0; 57 | obj->owornmask &= ~wp->w_mask; 58 | } 59 | if (uarm2 && !uarm) { 60 | uarm = uarm2; 61 | uarm2 = 0; 62 | uarm->owornmask ^= (W_ARM | W_ARM2); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /docs/historical/original-source/Makefile: -------------------------------------------------------------------------------- 1 | # @(#)Makefile 8.1 (Berkeley) 5/31/93 2 | # $FreeBSD$ 3 | 4 | PROG= hack 5 | SRCS= alloc.c hack.Decl.c hack.apply.c hack.bones.c hack.c hack.cmd.c \ 6 | hack.do.c hack.do_name.c hack.do_wear.c hack.dog.c hack.eat.c \ 7 | hack.end.c hack.engrave.c hack.fight.c hack.invent.c hack.ioctl.c \ 8 | hack.lev.c hack.main.c hack.makemon.c hack.mhitu.c hack.mklev.c \ 9 | hack.mkmaze.c hack.mkobj.c hack.mkshop.c hack.mon.c hack.monst.c \ 10 | hack.o_init.c hack.objnam.c hack.options.c hack.pager.c hack.potion.c \ 11 | hack.pri.c hack.read.c hack.rip.c hack.rumors.c hack.save.c \ 12 | hack.search.c hack.shk.c hack.shknam.c hack.steal.c hack.termcap.c \ 13 | hack.timeout.c hack.topl.c hack.track.c hack.trap.c hack.tty.c \ 14 | hack.u_init.c hack.unix.c hack.vault.c hack.version.c hack.wield.c \ 15 | hack.wizard.c hack.worm.c hack.worn.c hack.zap.c rnd.c \ 16 | hack.onames.h 17 | MAN= hack.6 18 | DPADD= ${LIBTERMCAP} ${LIBCOMPAT} 19 | LDADD= -ltermcap -lcompat 20 | CFLAGS+= -fwritable-strings -I${.CURDIR} -I. 21 | FILES= rumors help hh data 22 | FILESMODE_rumors= 440 23 | FILESGRP= ${BINGRP} 24 | FILESDIR= /var/games/hackdir 25 | HIDEGAME=hidegame 26 | CLEANFILES=hack.onames.h makedefs makedefs.o 27 | 28 | build-tools: makedefs 29 | 30 | hack.onames.h: makedefs def.objects.h 31 | ./makedefs ${.CURDIR}/def.objects.h > hack.onames.h 32 | 33 | makedefs: makedefs.o 34 | ${CC} -static ${CFLAGS} ${LDFLAGS} -o ${.TARGET} ${.ALLSRC} 35 | 36 | beforeinstall: 37 | ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} /dev/null \ 38 | ${DESTDIR}/var/games/hackdir/perm 39 | .if !exists(${DESTDIR}/var/games/hackdir/record) 40 | ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 460 /dev/null \ 41 | ${DESTDIR}/var/games/hackdir/record 42 | .endif 43 | # rm -f ${DESTDIR}/var/games/hackdir/bones* \ 44 | # ${DESTDIR}/var/games/hackdir/save/* 45 | 46 | .include 47 | -------------------------------------------------------------------------------- /src/def.flag.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.flag.h - version 1.0.3 */ 3 | 4 | struct flag { 5 | unsigned ident; /* social security number for each monster */ 6 | unsigned debug : 1; /* in debugging mode */ 7 | #define wizard flags.debug 8 | unsigned toplin : 2; /* a top line (message) has been printed */ 9 | /* 0: top line empty; 2: no --More-- reqd. */ 10 | unsigned cbreak : 1; /* in cbreak mode, rogue format */ 11 | unsigned standout : 1; /* use standout for --More-- */ 12 | unsigned nonull : 1; /* avoid sending nulls to the terminal */ 13 | unsigned time : 1; /* display elapsed 'time' */ 14 | unsigned nonews : 1; /* suppress news printing */ 15 | unsigned notombstone : 1; 16 | unsigned end_top, end_around; /* describe desired score list */ 17 | unsigned end_own : 1; /* idem (list all own scores) */ 18 | unsigned no_rest_on_space : 1; /* spaces are ignored */ 19 | unsigned beginner : 1; 20 | unsigned female : 1; 21 | unsigned invlet_constant : 1; /* let objects keep their 22 | inventory symbol */ 23 | unsigned move : 1; 24 | unsigned mv : 1; 25 | unsigned run : 3; /* 0: h (etc), 1: H (etc), 2: fh (etc) */ 26 | /* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */ 27 | unsigned nopick : 1; /* do not pickup objects */ 28 | unsigned echo : 1; /* 1 to echo characters */ 29 | unsigned botl : 1; /* partially redo status line */ 30 | unsigned botlx : 1; /* print an entirely new bottom line */ 31 | unsigned nscrinh : 1; /* inhibit nscr() in pline(); */ 32 | unsigned made_amulet : 1; 33 | unsigned no_of_wizards : 2; /* 0, 1 or 2 (wizard and his shadow) */ 34 | /* reset from 2 to 1, but never to 0 */ 35 | unsigned moonphase : 3; 36 | #define NEW_MOON 0 37 | #define FULL_MOON 4 38 | }; 39 | 40 | extern struct flag flags; 41 | -------------------------------------------------------------------------------- /src/hack.Decl.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.Decl.c - version 1.0.3 */ 3 | 4 | /* 5 | * Global variable declarations for 1984 Hack - game state and data structures 6 | * Original 1984 source: docs/historical/original-source/hack.Decl.c 7 | * 8 | * Key modernizations: ANSI C compatibility 9 | */ 10 | 11 | #include "hack.h" 12 | char nul[40]; /* contains zeros */ 13 | char plname[PL_NSIZ]; /* player name */ 14 | char lock[PL_NSIZ + 4] = "1lock"; /* long enough for login name .99 */ 15 | 16 | boolean in_mklev, restoring; 17 | 18 | struct rm levl[COLNO][ROWNO]; /* level map */ 19 | #ifndef QUEST 20 | struct mkroom rooms[MAXNROFROOMS + 1]; 21 | coord doors[DOORMAX]; 22 | #endif /* QUEST */ 23 | struct monst *fmon = 0; 24 | struct trap *ftrap = 0; 25 | struct gold *fgold = 0; 26 | struct obj *fobj = 0, *fcobj = 0, *invent = 0, *uwep = 0, *uarm = 0, *uarm2 = 0, 27 | *uarmh = 0, *uarms = 0, *uarmg = 0, *uright = 0, *uleft = 0, 28 | *uchain = 0, *uball = 0; 29 | struct flag flags; 30 | struct you u; 31 | struct monst youmonst; /* dummy; used as return value for boomhit */ 32 | 33 | xchar dlevel = 1; 34 | /* Original 1984: xchar xupstair, yupstair, xdnstair, ydnstair; */ 35 | unsigned char xupstair, yupstair, xdnstair, 36 | ydnstair; /* MODERN: unsigned to prevent buffer underflow */ 37 | char *save_cm = 0; 38 | const char *nomovemsg; /* MODERN: const because assigned string literals */ 39 | const char *killer; /* MODERN: CONST-CORRECTNESS: killer points to read-only 40 | death reasons */ 41 | 42 | long moves = 1; 43 | long wailmsg = 0; 44 | 45 | int multi = 0; 46 | char genocided[60]; 47 | char fut_geno[60]; 48 | 49 | xchar curx, cury; 50 | /* Original 1984: xchar seelx, seehx, seely, seehy; */ 51 | unsigned char seelx, seehx, seely, 52 | seehy; /* corners of lit room - MODERN: unsigned to prevent buffer underflow 53 | */ 54 | 55 | coord bhitpos; 56 | 57 | char quitchars[] = " \r\n\033"; 58 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.objclass.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.objclass.h - version 1.0.3 */ 3 | 4 | /* definition of a class of objects */ 5 | 6 | struct objclass { 7 | char *oc_name; /* actual name */ 8 | char *oc_descr; /* description when name unknown */ 9 | char *oc_uname; /* called by user */ 10 | Bitfield(oc_name_known,1); 11 | Bitfield(oc_merge,1); /* merge otherwise equal objects */ 12 | char oc_olet; 13 | schar oc_prob; /* probability for mkobj() */ 14 | schar oc_delay; /* delay when using such an object */ 15 | uchar oc_weight; 16 | schar oc_oc1, oc_oc2; 17 | int oc_oi; 18 | #define nutrition oc_oi /* for foods */ 19 | #define a_ac oc_oc1 /* for armors - only used in ARM_BONUS */ 20 | #define ARM_BONUS(obj) ((10 - objects[obj->otyp].a_ac) + obj->spe) 21 | #define a_can oc_oc2 /* for armors */ 22 | #define bits oc_oc1 /* for wands and rings */ 23 | /* wands */ 24 | #define NODIR 1 25 | #define IMMEDIATE 2 26 | #define RAY 4 27 | /* rings */ 28 | #define SPEC 1 /* +n is meaningful */ 29 | #define wldam oc_oc1 /* for weapons and PICK_AXE */ 30 | #define wsdam oc_oc2 /* for weapons and PICK_AXE */ 31 | #define g_val oc_oi /* for gems: value on exit */ 32 | }; 33 | 34 | extern struct objclass objects[]; 35 | 36 | /* definitions of all object-symbols */ 37 | 38 | #define ILLOBJ_SYM '\\' 39 | #define AMULET_SYM '"' 40 | #define FOOD_SYM '%' 41 | #define WEAPON_SYM ')' 42 | #define TOOL_SYM '(' 43 | #define BALL_SYM '0' 44 | #define CHAIN_SYM '_' 45 | #define ROCK_SYM '`' 46 | #define ARMOR_SYM '[' 47 | #define POTION_SYM '!' 48 | #define SCROLL_SYM '?' 49 | #define WAND_SYM '/' 50 | #define RING_SYM '=' 51 | #define GEM_SYM '*' 52 | /* Other places with explicit knowledge of object symbols: 53 | * ....shk.c: char shtypes[] = "=/)%?!["; 54 | * mklev.c: "=/)%?![<>" 55 | * hack.mkobj.c: char mkobjstr[] = "))[[!!!!????%%%%/=**"; 56 | * hack.apply.c: otmp = getobj("0#%", "put in"); 57 | * hack.eat.c: otmp = getobj("%", "eat"); 58 | * hack.invent.c: if(index("!%?[)=*(0/\"", sym)){ 59 | * hack.invent.c: || index("%?!*",otmp->olet))){ 60 | */ 61 | -------------------------------------------------------------------------------- /docs/historical/original-source/pathnames.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1990, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. All advertising materials mentioning features or use of this software 14 | * must display the following acknowledgement: 15 | * This product includes software developed by the University of 16 | * California, Berkeley and its contributors. 17 | * 4. Neither the name of the University nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | * 33 | * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 34 | */ 35 | 36 | #define _PATH_MAIL "/usr/bin/mail" 37 | #define _PATH_QUEST "/var/games/questdir" 38 | #define _PATH_HACK "/var/games/hackdir" 39 | 40 | -------------------------------------------------------------------------------- /hh: -------------------------------------------------------------------------------- 1 | y k u Move commands: 2 | \|/ hykulnjb: single move in specified direction 3 | h-+-l HYKULNJB: repeated move in specified direction 4 | /|\ (until stopped by e.g. a wall) 5 | b j n f: fast movement in direction 6 | (until something interesting is seen) 7 | m: move without picking up objects 8 | 9 | Meta commands: 10 | Q quit leave the game 11 | S save save the game (to be continued later) 12 | ! sh escape to some SHELL 13 | ^Z suspend suspend the game (independent of your current suspend char) 14 | O set set options 15 | ? help print information 16 | / whatis give name (and sometimes more info) of specified monster 17 | \ known print list of what's been discovered 18 | v version print version number 19 | ^R redraw redraw the screen (^R denotes the symbol CTRL/R) 20 | ^P print repeat last message (subsequent ^P's repeat earlier messages) 21 | # introduces a long command; not really implemented 22 | 23 | Game commands: 24 | ^T teleport teleport 25 | a apply, use use something (a key, camera, etc.) 26 | c call give a name to a class of objects 27 | d drop drop an object. d7a: drop seven items of object a. 28 | e eat eat something 29 | i invent list the inventory (all objects you are carrying) 30 | I invent list selected parts of the inventory 31 | IU: list unpaid objects 32 | IX: list unpaid but used up items 33 | I$: count your money 34 | p pay pay your bill 35 | q drink quaff a potion 36 | r read read a scroll 37 | s search search for secret doors, hidden traps and monsters 38 | t throw throw or shoot a weapon 39 | w wield wield a weapon (w- wield nothing) 40 | z zap zap a wand 41 | C name name an individual monster (e.g., baptize your dog) 42 | D Drop drop several things 43 | E Engrave write a message in the dust on the floor (E- use fingers) 44 | P wear put on a ring 45 | R remove remove a ring 46 | T remove take off some armor 47 | W wear put on some armor 48 | < up go up the stairs 49 | > down go down the stairs 50 | ^ trap_id identify a previously found trap 51 | ),[,= ask for current weapon, armor, rings, respectively 52 | $ gold count your gold 53 | . rest wait a moment 54 | , pickup pick up all you can carry 55 | : look look at what is here 56 | -------------------------------------------------------------------------------- /hackdir/hh: -------------------------------------------------------------------------------- 1 | y k u Move commands: 2 | \|/ hykulnjb: single move in specified direction 3 | h-+-l HYKULNJB: repeated move in specified direction 4 | /|\ (until stopped by e.g. a wall) 5 | b j n f: fast movement in direction 6 | (until something interesting is seen) 7 | m: move without picking up objects 8 | 9 | Meta commands: 10 | Q quit leave the game 11 | S save save the game (to be continued later) 12 | ! sh escape to some SHELL 13 | ^Z suspend suspend the game (independent of your current suspend char) 14 | O set set options 15 | ? help print information 16 | / whatis give name (and sometimes more info) of specified monster 17 | \ known print list of what's been discovered 18 | v version print version number 19 | ^R redraw redraw the screen (^R denotes the symbol CTRL/R) 20 | ^P print repeat last message (subsequent ^P's repeat earlier messages) 21 | # introduces a long command; not really implemented 22 | 23 | Game commands: 24 | ^T teleport teleport 25 | a apply, use use something (a key, camera, etc.) 26 | c call give a name to a class of objects 27 | d drop drop an object. d7a: drop seven items of object a. 28 | e eat eat something 29 | i invent list the inventory (all objects you are carrying) 30 | I invent list selected parts of the inventory 31 | IU: list unpaid objects 32 | IX: list unpaid but used up items 33 | I$: count your money 34 | p pay pay your bill 35 | q drink quaff a potion 36 | r read read a scroll 37 | s search search for secret doors, hidden traps and monsters 38 | t throw throw or shoot a weapon 39 | w wield wield a weapon (w- wield nothing) 40 | z zap zap a wand 41 | C name name an individual monster (e.g., baptize your dog) 42 | D Drop drop several things 43 | E Engrave write a message in the dust on the floor (E- use fingers) 44 | P wear put on a ring 45 | R remove remove a ring 46 | T remove take off some armor 47 | W wear put on some armor 48 | < up go up the stairs 49 | > down go down the stairs 50 | ^ trap_id identify a previously found trap 51 | ),[,= ask for current weapon, armor, rings, respectively 52 | $ gold count your gold 53 | . rest wait a moment 54 | , pickup pick up all you can carry 55 | : look look at what is here 56 | -------------------------------------------------------------------------------- /src/def.objclass.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.objclass.h - version 1.0.3 */ 3 | 4 | /* definition of a class of objects */ 5 | 6 | struct objclass { 7 | const char *oc_name; /* actual name */ 8 | const char *oc_descr; /* description when name unknown */ 9 | char *oc_uname; /* called by user (mutable, may be allocated/freed) */ 10 | Bitfield(oc_name_known, 1); 11 | Bitfield(oc_merge, 1); /* merge otherwise equal objects */ 12 | char oc_olet; 13 | schar oc_prob; /* probability for mkobj() */ 14 | schar oc_delay; /* delay when using such an object */ 15 | uchar oc_weight; 16 | schar oc_oc1, oc_oc2; 17 | int oc_oi; 18 | #define nutrition oc_oi /* for foods */ 19 | #define a_ac oc_oc1 /* for armors - only used in ARM_BONUS */ 20 | #define ARM_BONUS(obj) ((10 - objects[obj->otyp].a_ac) + obj->spe) 21 | #define a_can oc_oc2 /* for armors */ 22 | #define bits oc_oc1 /* for wands and rings */ 23 | /* wands */ 24 | #define NODIR 1 25 | #define IMMEDIATE 2 26 | #define RAY 4 27 | /* rings */ 28 | #define SPEC 1 /* +n is meaningful */ 29 | #define wldam oc_oc1 /* for weapons and PICK_AXE */ 30 | #define wsdam oc_oc2 /* for weapons and PICK_AXE */ 31 | #define g_val oc_oi /* for gems: value on exit */ 32 | }; 33 | 34 | extern struct objclass objects[]; 35 | 36 | /* definitions of all object-symbols */ 37 | 38 | #define ILLOBJ_SYM '\\' 39 | #define AMULET_SYM '"' 40 | #define FOOD_SYM '%' 41 | #define WEAPON_SYM ')' 42 | #define TOOL_SYM '(' 43 | #define BALL_SYM '0' 44 | #define CHAIN_SYM '_' 45 | #define ROCK_SYM '`' 46 | #define ARMOR_SYM '[' 47 | #define POTION_SYM '!' 48 | #define SCROLL_SYM '?' 49 | #define WAND_SYM '/' 50 | #define RING_SYM '=' 51 | #define GEM_SYM '*' 52 | /* Other places with explicit knowledge of object symbols: 53 | * ....shk.c: char shtypes[] = "=/)%?!["; 54 | * mklev.c: "=/)%?![<>" 55 | * hack.mkobj.c: char mkobjstr[] = "))[[!!!!????%%%%/=**"; 56 | * hack.apply.c: otmp = getobj("0#%", "put in"); 57 | * hack.eat.c: otmp = getobj("%", "eat"); 58 | * hack.invent.c: if(index("!%?[)=*(0/\"", sym)){ 59 | * hack.invent.c: || index("%?!*",otmp->olet))){ 60 | */ 61 | -------------------------------------------------------------------------------- /docs/historical/original-source/hh: -------------------------------------------------------------------------------- 1 | y k u Move commands: 2 | \|/ hykulnjb: single move in specified direction 3 | h-+-l HYKULNJB: repeated move in specified direction 4 | /|\ (until stopped by e.g. a wall) 5 | b j n f: fast movement in direction 6 | (until something interesting is seen) 7 | m: move without picking up objects 8 | 9 | Meta commands: 10 | Q quit leave the game 11 | S save save the game (to be continued later) 12 | ! sh escape to some SHELL 13 | ^Z suspend suspend the game (independent of your current suspend char) 14 | O set set options 15 | ? help print information 16 | / whatis give name (and sometimes more info) of specified monster 17 | \ known print list of what's been discovered 18 | v version print version number 19 | ^R redraw redraw the screen (^R denotes the symbol CTRL/R) 20 | ^P print repeat last message (subsequent ^P's repeat earlier messages) 21 | # introduces a long command; not really implemented 22 | 23 | Game commands: 24 | ^T teleport teleport 25 | a apply, use use something (a key, camera, etc.) 26 | c call give a name to a class of objects 27 | d drop drop an object. d7a: drop seven items of object a. 28 | e eat eat something 29 | i invent list the inventory (all objects you are carrying) 30 | I invent list selected parts of the inventory 31 | IU: list unpaid objects 32 | IX: list unpaid but used up items 33 | I$: count your money 34 | p pay pay your bill 35 | q drink quaff a potion 36 | r read read a scroll 37 | s search search for secret doors, hidden traps and monsters 38 | t throw throw or shoot a weapon 39 | w wield wield a weapon (w- wield nothing) 40 | z zap zap a wand 41 | C name name an individual monster (e.g., baptize your dog) 42 | D Drop drop several things 43 | E Engrave write a message in the dust on the floor (E- use fingers) 44 | P wear put on a ring 45 | R remove remove a ring 46 | T remove take off some armor 47 | W wear put on some armor 48 | < up go up the stairs 49 | > down go down the stairs 50 | ^ trap_id identify a previously found trap 51 | ),[,= ask for current weapon, armor, rings, respectively 52 | $ gold count your gold 53 | . rest wait a moment 54 | , pickup pick up all you can carry 55 | : look look at what is here 56 | -------------------------------------------------------------------------------- /docs/BUILD.md: -------------------------------------------------------------------------------- 1 | # restoHack Build Instructions 2 | 3 | ## Quick Start 4 | 5 | **Just want to play?** Download a pre-built binary from [Releases](https://github.com/Critlist/restoHack/releases). 6 | 7 | **Building from source:** 8 | 9 | ```bash 10 | git clone https://github.com/Critlist/restoHack.git 11 | cd restoHack 12 | cmake -B build -DCMAKE_BUILD_TYPE=Release 13 | cmake --build build 14 | ./build/hack 15 | ``` 16 | 17 | ## Requirements 18 | 19 | - CMake (≥3.16) 20 | - C compiler (gcc or clang) 21 | - ncurses library 22 | 23 | **Install dependencies:** 24 | 25 | - **Ubuntu/Debian:** `sudo apt install cmake build-essential libncurses-dev` 26 | - **Fedora/RHEL:** `sudo dnf install cmake gcc ncurses-devel` 27 | - **Arch:** `sudo pacman -S cmake gcc ncurses` 28 | - **macOS:** `brew install cmake ncurses` 29 | - **FreeBSD:** `pkg install cmake gcc ncurses` 30 | 31 | ## Build Types 32 | 33 | ```bash 34 | # Release (optimized for playing) 35 | cmake -B build -DCMAKE_BUILD_TYPE=Release 36 | cmake --build build 37 | 38 | # Debug (with sanitizers for development) 39 | cmake -B build-debug -DCMAKE_BUILD_TYPE=Debug 40 | cmake --build build-debug 41 | 42 | # RelWithDebInfo (production servers with debug symbols) 43 | cmake -B build-server -DCMAKE_BUILD_TYPE=RelWithDebInfo 44 | cmake --build build-server 45 | ``` 46 | 47 | ## Platform Notes 48 | 49 | ### macOS 50 | 51 | Xcode Command Line Tools required. Install with: `xcode-select --install` 52 | 53 | ### OpenBSD 54 | 55 | ```bash 56 | pkg_add cmake gcc ncurses 57 | cmake -B build -DCMAKE_C_COMPILER=/usr/local/bin/gcc 58 | cmake --build build 59 | ``` 60 | 61 | ### Static Binary 62 | 63 | Download from releases or build on Alpine/musl with `-DCMAKE_EXE_LINKER_FLAGS="-static"` 64 | 65 | ## Troubleshooting 66 | 67 | **ncurses not found:** Install development package (see Requirements) 68 | 69 | **Old CMake:** Update or use traditional syntax: 70 | 71 | ```bash 72 | mkdir build && cd build 73 | cmake .. -DCMAKE_BUILD_TYPE=Release 74 | make 75 | ``` 76 | 77 | **Clean rebuild:** `rm -rf build && cmake -B build && cmake --build build` 78 | 79 | ## Development 80 | 81 | The project uses standard CMake with presets for modern IDEs (VSCode, CLion, etc). 82 | 83 | For contributing, see [CODING_STANDARDS.md](CODING_STANDARDS.md). 84 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.rip.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.rip.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include 6 | #include "hack.h" 7 | 8 | extern char plname[]; 9 | 10 | static char *rip[] = { 11 | " ----------", 12 | " / \\", 13 | " / REST \\", 14 | " / IN \\", 15 | " / PEACE \\", 16 | " / \\", 17 | " | |", 18 | " | |", 19 | " | |", 20 | " | |", 21 | " | |", 22 | " | 1001 |", 23 | " *| * * * | *", 24 | " _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n", 25 | 0 26 | }; 27 | 28 | outrip(){ 29 | char **dp = rip; 30 | char *dpx; 31 | char buf[BUFSZ]; 32 | int x,y; 33 | 34 | cls(); 35 | (void) strcpy(buf, plname); 36 | buf[16] = 0; 37 | center(6, buf); 38 | (void) sprintf(buf, "%ld AU", u.ugold); 39 | center(7, buf); 40 | (void) sprintf(buf, "killed by%s", 41 | !strncmp(killer, "the ", 4) ? "" : 42 | !strcmp(killer, "starvation") ? "" : 43 | index(vowels, *killer) ? " an" : " a"); 44 | center(8, buf); 45 | (void) strcpy(buf, killer); 46 | if(strlen(buf) > 16) { 47 | int i,i0,i1; 48 | i0 = i1 = 0; 49 | for(i = 0; i <= 16; i++) 50 | if(buf[i] == ' ') i0 = i, i1 = i+1; 51 | if(!i0) i0 = i1 = 16; 52 | buf[i1 + 16] = 0; 53 | center(10, buf+i1); 54 | buf[i0] = 0; 55 | } 56 | center(9, buf); 57 | (void) sprintf(buf, "%4d", getyear()); 58 | center(11, buf); 59 | for(y=8; *dp; y++,dp++){ 60 | x = 0; 61 | dpx = *dp; 62 | while(dpx[x]) { 63 | while(dpx[x] == ' ') x++; 64 | curs(x,y); 65 | while(dpx[x] && dpx[x] != ' '){ 66 | extern int done_stopprint; 67 | if(done_stopprint) 68 | return; 69 | curx++; 70 | (void) putchar(dpx[x++]); 71 | } 72 | } 73 | } 74 | getret(); 75 | } 76 | 77 | center(line, text) int line; char *text; { 78 | char *ip,*op; 79 | ip = text; 80 | op = &rip[line][28 - ((strlen(text)+1)/2)]; 81 | while(*ip) *op++ = *ip++; 82 | } 83 | -------------------------------------------------------------------------------- /docs/historical/original-source/def.monst.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.monst.h - version 1.0.2 */ 3 | 4 | struct monst { 5 | struct monst *nmon; 6 | struct permonst *data; 7 | unsigned m_id; 8 | xchar mx,my; 9 | xchar mdx,mdy; /* if mdispl then pos where last displayed */ 10 | #define MTSZ 4 11 | coord mtrack[MTSZ]; /* monster track */ 12 | schar mhp,mhpmax; 13 | char mappearance; /* nonzero for undetected 'M's and for '1's */ 14 | Bitfield(mimic,1); /* undetected mimic */ 15 | Bitfield(mdispl,1); /* mdx,mdy valid */ 16 | Bitfield(minvis,1); /* invisible */ 17 | Bitfield(cham,1); /* shape-changer */ 18 | Bitfield(mhide,1); /* hides beneath objects */ 19 | Bitfield(mundetected,1); /* not seen in present hiding place */ 20 | Bitfield(mspeed,2); 21 | Bitfield(msleep,1); 22 | Bitfield(mfroz,1); 23 | Bitfield(mconf,1); 24 | Bitfield(mflee,1); /* fleeing */ 25 | Bitfield(mfleetim,7); /* timeout for mflee */ 26 | Bitfield(mcan,1); /* has been cancelled */ 27 | Bitfield(mtame,1); /* implies peaceful */ 28 | Bitfield(mpeaceful,1); /* does not attack unprovoked */ 29 | Bitfield(isshk,1); /* is shopkeeper */ 30 | Bitfield(isgd,1); /* is guard */ 31 | Bitfield(mcansee,1); /* cansee 1, temp.blinded 0, blind 0 */ 32 | Bitfield(mblinded,7); /* cansee 0, temp.blinded n, blind 0 */ 33 | Bitfield(mtrapped,1); /* trapped in a pit or bear trap */ 34 | Bitfield(mnamelth,6); /* length of name (following mxlth) */ 35 | #ifndef NOWORM 36 | Bitfield(wormno,5); /* at most 31 worms on any level */ 37 | #endif NOWORM 38 | unsigned mtrapseen; /* bitmap of traps we've been trapped in */ 39 | long mlstmv; /* prevent two moves at once */ 40 | struct obj *minvent; 41 | long mgold; 42 | unsigned mxlth; /* length of following data */ 43 | /* in order to prevent alignment problems mextra should 44 | be (or follow) a long int */ 45 | long mextra[1]; /* monster dependent info */ 46 | }; 47 | 48 | #define newmonst(xl) (struct monst *) alloc((unsigned)(xl) + sizeof(struct monst)) 49 | 50 | extern struct monst *fmon; 51 | extern struct monst *fallen_down; 52 | struct monst *m_at(); 53 | 54 | /* these are in mspeed */ 55 | #define MSLOW 1 /* slow monster */ 56 | #define MFAST 2 /* speeded monster */ 57 | 58 | #define NAME(mtmp) (((char *) mtmp->mextra) + mtmp->mxlth) 59 | #define MREGEN "TVi1" 60 | #define UNDEAD "ZVW " 61 | -------------------------------------------------------------------------------- /src/alloc.c: -------------------------------------------------------------------------------- 1 | /* alloc.c - version 1.0.2 */ 2 | /* $FreeBSD$ */ 3 | 4 | /* 5 | * Memory allocation utilities for 1984 Hack - safe malloc/free wrappers 6 | * Original 1984 source: docs/historical/original-source/alloc.c 7 | * 8 | * Key modernizations: ANSI C function signatures, enhanced type safety 9 | */ 10 | 11 | #include "hack.h" 12 | #include 13 | 14 | #ifdef LINT 15 | 16 | /* 17 | a ridiculous definition, suppressing 18 | "possible pointer alignment problem" for (long *) malloc() 19 | "enlarg defined but never used" 20 | "ftell defined (in ) but never used" 21 | from lint 22 | */ 23 | #include 24 | void *alloc(unsigned n) { 25 | /* Original 1984: return(&dummy); */ 26 | static long dummy_storage = 27 | 0; /* MODERN: static storage prevents dangling pointer */ 28 | long dummy = ftell(stderr); 29 | if (n) 30 | dummy = 0; /* make sure arg is used */ 31 | dummy_storage = dummy; 32 | return (&dummy_storage); 33 | } 34 | 35 | #else 36 | 37 | #if 0 38 | /* ORIGINAL 1984 CODE - preserved for reference */ 39 | long * 40 | alloc(lth) 41 | unsigned lth; 42 | { 43 | char *ptr; 44 | 45 | if(!(ptr = malloc(lth))) 46 | panic("Cannot get %d bytes", lth); 47 | return((long *) ptr); 48 | } 49 | 50 | long * 51 | enlarge(ptr,lth) 52 | char *ptr; 53 | unsigned lth; 54 | { 55 | char *nptr; 56 | 57 | if(!(nptr = realloc(ptr,lth))) 58 | panic("Cannot reallocate %d bytes", lth); 59 | return((long *) nptr); 60 | } 61 | #endif 62 | 63 | /** 64 | * MODERN ADDITION (2025): ANSI C memory allocation functions 65 | * 66 | * WHY: Original returned long* which required casting at every call site. 67 | * Modern void* return eliminates casting and follows ANSI C conventions. 68 | * 69 | * HOW: Changed return type from long* to void*, updated function signatures 70 | * to ANSI C style with proper parameter declarations. 71 | * 72 | * PRESERVES: Identical allocation behavior and error handling via panic() 73 | * ADDS: Type safety and ANSI C compliance without casting requirements 74 | */ 75 | void *alloc(unsigned lth) { 76 | char *ptr; 77 | 78 | if (!(ptr = malloc(lth))) 79 | panic("Cannot get %d bytes", lth); 80 | return (ptr); 81 | } 82 | 83 | void *enlarge(char *ptr, unsigned lth) { 84 | char *nptr; 85 | 86 | if (!(nptr = realloc(ptr, lth))) 87 | panic("Cannot reallocate %d bytes", lth); 88 | return (nptr); 89 | } 90 | 91 | #endif /* LINT */ 92 | -------------------------------------------------------------------------------- /src/def.monst.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* def.monst.h - version 1.0.2 */ 3 | 4 | struct monst { 5 | struct monst *nmon; 6 | struct permonst *data; 7 | unsigned m_id; 8 | xchar mx, my; 9 | xchar mdx, mdy; /* if mdispl then pos where last displayed */ 10 | #define MTSZ 4 11 | coord mtrack[MTSZ]; /* monster track */ 12 | schar mhp, mhpmax; 13 | char mappearance; /* nonzero for undetected 'M's and for '1's */ 14 | Bitfield(mimic, 1); /* undetected mimic */ 15 | Bitfield(mdispl, 1); /* mdx,mdy valid */ 16 | Bitfield(minvis, 1); /* invisible */ 17 | Bitfield(cham, 1); /* shape-changer */ 18 | Bitfield(mhide, 1); /* hides beneath objects */ 19 | Bitfield(mundetected, 1); /* not seen in present hiding place */ 20 | Bitfield(mspeed, 2); 21 | Bitfield(msleep, 1); 22 | Bitfield(mfroz, 1); 23 | Bitfield(mconf, 1); 24 | Bitfield(mflee, 1); /* fleeing */ 25 | Bitfield(mfleetim, 7); /* timeout for mflee */ 26 | Bitfield(mcan, 1); /* has been cancelled */ 27 | Bitfield(mtame, 1); /* implies peaceful */ 28 | Bitfield(mpeaceful, 1); /* does not attack unprovoked */ 29 | Bitfield(isshk, 1); /* is shopkeeper */ 30 | Bitfield(isgd, 1); /* is guard */ 31 | Bitfield(mcansee, 1); /* cansee 1, temp.blinded 0, blind 0 */ 32 | Bitfield(mblinded, 7); /* cansee 0, temp.blinded n, blind 0 */ 33 | Bitfield(mtrapped, 1); /* trapped in a pit or bear trap */ 34 | Bitfield(mnamelth, 6); /* length of name (following mxlth) */ 35 | #ifndef NOWORM 36 | Bitfield(wormno, 5); /* at most 31 worms on any level */ 37 | #endif /* NOWORM */ 38 | unsigned mtrapseen; /* bitmap of traps we've been trapped in */ 39 | long mlstmv; /* prevent two moves at once */ 40 | struct obj *minvent; 41 | long mgold; 42 | unsigned mxlth; /* length of following data */ 43 | /* in order to prevent alignment problems mextra should 44 | be (or follow) a long int */ 45 | long mextra[1]; /* monster dependent info */ 46 | }; 47 | 48 | #define newmonst(xl) \ 49 | (struct monst *)alloc((unsigned)(xl) + sizeof(struct monst)) 50 | 51 | extern struct monst *fmon; 52 | extern struct monst *fallen_down; 53 | struct monst *m_at(int x, int y); 54 | 55 | /* these are in mspeed */ 56 | #define MSLOW 1 /* slow monster */ 57 | #define MFAST 2 /* speeded monster */ 58 | 59 | #define NAME(mtmp) (((char *)mtmp->mextra) + mtmp->mxlth) 60 | #define MREGEN "TVi1" 61 | #define UNDEAD "ZVW " 62 | -------------------------------------------------------------------------------- /src/pathnames.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1990, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. All advertising materials mentioning features or use of this software 14 | * must display the following acknowledgement: 15 | * This product includes software developed by the University of 16 | * California, Berkeley and its contributors. 17 | * 4. Neither the name of the University nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | * 33 | * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 34 | */ 35 | 36 | #define _PATH_MAIL "/usr/bin/mail" 37 | #define _PATH_QUEST "/var/games/questdir" 38 | /** 39 | * MODERN ADDITION (2025): Local development path for hackdir 40 | * 41 | * WHY: Original 1984 path was "/usr/games/lib/hackdir" requiring system 42 | * installation. For development and educational use, we need a local directory 43 | * that can be created and managed without root privileges or system-wide 44 | * installation. 45 | * 46 | * HOW: Changed from system path to current directory relative path "./hackdir" 47 | * allowing the game to run from the build directory with local data files. 48 | * 49 | * PRESERVES: All original file structure and game data organization 50 | * ADDS: Development-friendly local execution without system installation 51 | */ 52 | #define _PATH_HACK "./hackdir" 53 | -------------------------------------------------------------------------------- /docs/historical/Original_READ_ME: -------------------------------------------------------------------------------- 1 | This is export hack, my first semester programming project. 2 | 3 | To set it up for your system, you will have to do the following: 4 | 1: create a hack uid, to own the top ten list, etc. 5 | 2: create a hack directory "/usr/lib/game/hack" is the default. 6 | 2.5: make the directory 700 mode. /* sav files go in there...*/ 7 | 3: modify hack.main.c to use the new directory. 8 | 4: modify hack.main.c so it uses the new hack gid. Gid accounts can 9 | go into magic mode without the password, can get cores with ^G, etc. 10 | (make sure gid isn't checked anywhere else...) 11 | 5: recompile hack. 12 | 6: put it in games after making it set-uid hack. 13 | 8: fix the bugs I undobtedly left in it. 14 | 9: tell me what you think of it. 15 | 16 | Hack uses the UCB file /etc/termcap to get your terminal escape codes. 17 | If you don't use it, you will have to make extensive changes to hack.pri.c 18 | 19 | If you find any bugs (That you think I don't know about), or have any 20 | awesome new changes (Like a better save (One that works!)), or have ANY 21 | questions, write me 22 | Jay Fenlason 23 | 29 East St. 24 | Sudbury Mass. 25 | 01776 26 | 27 | or call me at (617) 443-5036. Since I have both a modem and a teen-age 28 | sister, Good Luck. 29 | 30 | 31 | Hack is split (roughly) into several source files that do different things. 32 | I have tried to fit all the procedures having to do with a certain segment 33 | of the game into a single file, but the job is not the best in the world. 34 | The rough splits are: 35 | 36 | hack.c General random stuff and things I never got around to moving. 37 | hack.main.c main() and other random procedures, also the lock file stuff. 38 | hack.mon.c Monsters, moving, attacking, etc. 39 | hack.do.c drink, eat, read, wield, save, etc. 40 | hack.do1.c zap, wear, remove, etc... 41 | hack.pri.c stuff having to do with the screen, most of the terminal 42 | independant stuff is in here. 43 | hack.lev.c temp files and calling of mklev. 44 | 45 | Because of the peculiar restraints on our system, I make mklev (create 46 | a level) a separate procedure execd by hack when needed. The source for 47 | mklev is (Naturaly) mklev.c. You may want to put mklev back into hack. 48 | Good luck. 49 | 50 | Most of hack was written by me, with help from 51 | Kenny Woodland (KW) (general random things including 52 | the original BUZZ()) 53 | Mike Thome (MT) (The original chamelian) 54 | and Jon Payne (JP) (The original lock file kludge and 55 | the massive CURS()) 56 | 57 | This entire program would not have been possible without the SFSU Logo 58 | Workshop. I am eternally grateful to all of our students (Especially K.L.), 59 | without whom I would never have seen Rogue. I am especially grateful to 60 | Mike Clancy, without whose generous help I would never have gotten to play 61 | ROGUE. 62 | -------------------------------------------------------------------------------- /docs/historical/original-source/Original_READ_ME: -------------------------------------------------------------------------------- 1 | This is export hack, my first semester programming project. 2 | 3 | To set it up for your system, you will have to do the following: 4 | 1: create a hack uid, to own the top ten list, etc. 5 | 2: create a hack directory "/usr/lib/game/hack" is the default. 6 | 2.5: make the directory 700 mode. /* sav files go in there...*/ 7 | 3: modify hack.main.c to use the new directory. 8 | 4: modify hack.main.c so it uses the new hack gid. Gid accounts can 9 | go into magic mode without the password, can get cores with ^G, etc. 10 | (make sure gid isn't checked anywhere else...) 11 | 5: recompile hack. 12 | 6: put it in games after making it set-uid hack. 13 | 8: fix the bugs I undobtedly left in it. 14 | 9: tell me what you think of it. 15 | 16 | Hack uses the UCB file /etc/termcap to get your terminal escape codes. 17 | If you don't use it, you will have to make extensive changes to hack.pri.c 18 | 19 | If you find any bugs (That you think I don't know about), or have any 20 | awesome new changes (Like a better save (One that works!)), or have ANY 21 | questions, write me 22 | Jay Fenlason 23 | 29 East St. 24 | Sudbury Mass. 25 | 01776 26 | 27 | or call me at (617) 443-5036. Since I have both a modem and a teen-age 28 | sister, Good Luck. 29 | 30 | 31 | Hack is split (roughly) into several source files that do different things. 32 | I have tried to fit all the procedures having to do with a certain segment 33 | of the game into a single file, but the job is not the best in the world. 34 | The rough splits are: 35 | 36 | hack.c General random stuff and things I never got around to moving. 37 | hack.main.c main() and other random procedures, also the lock file stuff. 38 | hack.mon.c Monsters, moving, attacking, etc. 39 | hack.do.c drink, eat, read, wield, save, etc. 40 | hack.do1.c zap, wear, remove, etc... 41 | hack.pri.c stuff having to do with the screen, most of the terminal 42 | independant stuff is in here. 43 | hack.lev.c temp files and calling of mklev. 44 | 45 | Because of the peculiar restraints on our system, I make mklev (create 46 | a level) a separate procedure execd by hack when needed. The source for 47 | mklev is (Naturaly) mklev.c. You may want to put mklev back into hack. 48 | Good luck. 49 | 50 | Most of hack was written by me, with help from 51 | Kenny Woodland (KW) (general random things including 52 | the original BUZZ()) 53 | Mike Thome (MT) (The original chamelian) 54 | and Jon Payne (JP) (The original lock file kludge and 55 | the massive CURS()) 56 | 57 | This entire program would not have been possible without the SFSU Logo 58 | Workshop. I am eternally grateful to all of our students (Especially K.L.), 59 | without whom I would never have seen Rogue. I am especially grateful to 60 | Mike Clancy, without whose generous help I would never have gotten to play 61 | ROGUE. 62 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.bones.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.bones.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | extern char plname[PL_NSIZ]; 7 | extern long somegold(); 8 | extern struct monst *makemon(); 9 | extern struct permonst pm_ghost; 10 | 11 | char bones[] = "bones_xx"; 12 | 13 | /* save bones and possessions of a deceased adventurer */ 14 | savebones(){ 15 | int fd; 16 | struct obj *otmp; 17 | struct trap *ttmp; 18 | struct monst *mtmp; 19 | if(dlevel <= 0 || dlevel > MAXLEVEL) return; 20 | if(!rn2(1 + dlevel/2)) return; /* not so many ghosts on low levels */ 21 | bones[6] = '0' + (dlevel/10); 22 | bones[7] = '0' + (dlevel%10); 23 | if((fd = open(bones,0)) >= 0){ 24 | (void) close(fd); 25 | return; 26 | } 27 | /* drop everything; the corpse's possessions are usually cursed */ 28 | otmp = invent; 29 | while(otmp){ 30 | otmp->ox = u.ux; 31 | otmp->oy = u.uy; 32 | otmp->age = 0; /* very long ago */ 33 | otmp->owornmask = 0; 34 | if(rn2(5)) otmp->cursed = 1; 35 | if(!otmp->nobj){ 36 | otmp->nobj = fobj; 37 | fobj = invent; 38 | invent = 0; /* superfluous */ 39 | break; 40 | } 41 | otmp = otmp->nobj; 42 | } 43 | if(!(mtmp = makemon(PM_GHOST, u.ux, u.uy))) return; 44 | mtmp->mx = u.ux; 45 | mtmp->my = u.uy; 46 | mtmp->msleep = 1; 47 | (void) strcpy((char *) mtmp->mextra, plname); 48 | mkgold(somegold() + d(dlevel,30), u.ux, u.uy); 49 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon){ 50 | mtmp->m_id = 0; 51 | if(mtmp->mtame) { 52 | mtmp->mtame = 0; 53 | mtmp->mpeaceful = 0; 54 | } 55 | mtmp->mlstmv = 0; 56 | if(mtmp->mdispl) unpmon(mtmp); 57 | } 58 | for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) 59 | ttmp->tseen = 0; 60 | for(otmp = fobj; otmp; otmp = otmp->nobj) { 61 | otmp->o_id = 0; 62 | /* otmp->o_cnt_id = 0; - superfluous */ 63 | otmp->onamelth = 0; 64 | otmp->known = 0; 65 | otmp->invlet = 0; 66 | if(otmp->olet == AMULET_SYM && !otmp->spe) { 67 | otmp->spe = -1; /* no longer the actual amulet */ 68 | otmp->cursed = 1; /* flag as gotten from a ghost */ 69 | } 70 | } 71 | if((fd = creat(bones, FMASK)) < 0) return; 72 | savelev(fd,dlevel); 73 | (void) close(fd); 74 | } 75 | 76 | getbones(){ 77 | int fd,x,y,ok; 78 | if(rn2(3)) return(0); /* only once in three times do we find bones */ 79 | bones[6] = '0' + dlevel/10; 80 | bones[7] = '0' + dlevel%10; 81 | if((fd = open(bones, 0)) < 0) return(0); 82 | if((ok = uptodate(fd)) != 0){ 83 | getlev(fd, 0, dlevel); 84 | for(x = 0; x < COLNO; x++) for(y = 0; y < ROWNO; y++) 85 | levl[x][y].seen = levl[x][y].new = 0; 86 | } 87 | (void) close(fd); 88 | #ifdef WIZARD 89 | if(!wizard) /* duvel!frans: don't remove bones while debugging */ 90 | #endif WiZARD 91 | if(unlink(bones) < 0){ 92 | pline("Cannot unlink %s .", bones); 93 | return(0); 94 | } 95 | return(ok); 96 | } 97 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.wield.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.wield.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | extern struct obj zeroobj; 7 | 8 | setuwep(obj) struct obj *obj; { 9 | setworn(obj, W_WEP); 10 | } 11 | 12 | dowield() 13 | { 14 | struct obj *wep; 15 | int res = 0; 16 | 17 | multi = 0; 18 | if(!(wep = getobj("#-)", "wield"))) /* nothing */; 19 | else if(uwep == wep) 20 | pline("You are already wielding that!"); 21 | else if(uwep && uwep->cursed) 22 | pline("The %s welded to your hand!", 23 | aobjnam(uwep, "are")); 24 | else if(wep == &zeroobj) { 25 | if(uwep == 0){ 26 | pline("You are already empty handed."); 27 | } else { 28 | setuwep((struct obj *) 0); 29 | res++; 30 | pline("You are empty handed."); 31 | } 32 | } else if(uarms && wep->otyp == TWO_HANDED_SWORD) 33 | pline("You cannot wield a two-handed sword and wear a shield."); 34 | else if(wep->owornmask & (W_ARMOR | W_RING)) 35 | pline("You cannot wield that!"); 36 | else { 37 | setuwep(wep); 38 | res++; 39 | if(uwep->cursed) 40 | pline("The %s %s to your hand!", 41 | aobjnam(uwep, "weld"), 42 | (uwep->quan == 1) ? "itself" : "themselves"); /* a3 */ 43 | else prinv(uwep); 44 | } 45 | return(res); 46 | } 47 | 48 | corrode_weapon(){ 49 | if(!uwep || uwep->olet != WEAPON_SYM) return; /* %% */ 50 | if(uwep->rustfree) 51 | pline("Your %s not affected.", aobjnam(uwep, "are")); 52 | else { 53 | pline("Your %s!", aobjnam(uwep, "corrode")); 54 | uwep->spe--; 55 | } 56 | } 57 | 58 | chwepon(otmp,amount) 59 | struct obj *otmp; 60 | int amount; 61 | { 62 | char *color = (amount < 0) ? "black" : "green"; 63 | char *time; 64 | if(!uwep || uwep->olet != WEAPON_SYM) { 65 | strange_feeling(otmp, 66 | (amount > 0) ? "Your hands twitch." 67 | : "Your hands itch."); 68 | return(0); 69 | } 70 | 71 | if(uwep->otyp == WORM_TOOTH && amount > 0) { 72 | uwep->otyp = CRYSKNIFE; 73 | pline("Your weapon seems sharper now."); 74 | uwep->cursed = 0; 75 | return(1); 76 | } 77 | 78 | if(uwep->otyp == CRYSKNIFE && amount < 0) { 79 | uwep->otyp = WORM_TOOTH; 80 | pline("Your weapon looks duller now."); 81 | return(1); 82 | } 83 | 84 | /* there is a (soft) upper limit to uwep->spe */ 85 | if(amount > 0 && uwep->spe > 5 && rn2(3)) { 86 | pline("Your %s violently green for a while and then evaporate%s.", 87 | aobjnam(uwep, "glow"), plur(uwep->quan)); 88 | while(uwep) /* let all of them disappear */ 89 | /* note: uwep->quan = 1 is nogood if unpaid */ 90 | useup(uwep); 91 | return(1); 92 | } 93 | if(!rn2(6)) amount *= 2; 94 | time = (amount*amount == 1) ? "moment" : "while"; 95 | pline("Your %s %s for a %s.", 96 | aobjnam(uwep, "glow"), color, time); 97 | uwep->spe += amount; 98 | if(amount > 0) uwep->cursed = 0; 99 | return(1); 100 | } 101 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.monst.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.monst.c - version 1.0.2 */ 3 | 4 | #include "hack.h" 5 | #include "def.eshk.h" 6 | extern char plname[PL_NSIZ]; 7 | 8 | struct permonst mons[CMNUM+2] = { 9 | { "bat", 'B',1,22,8,1,4,0 }, 10 | { "gnome", 'G',1,6,5,1,6,0 }, 11 | { "hobgoblin", 'H',1,9,5,1,8,0 }, 12 | { "jackal", 'J',0,12,7,1,2,0 }, 13 | { "kobold", 'K',1,6,7,1,4,0 }, 14 | { "leprechaun", 'L',5,15,8,1,2,0 }, 15 | { "giant rat", 'r',0,12,7,1,3,0 }, 16 | { "acid blob", 'a',2,3,8,0,0,0 }, 17 | { "floating eye", 'E',2,1,9,0,0,0 }, 18 | { "homunculus", 'h',2,6,6,1,3,0 }, 19 | { "imp", 'i',2,6,2,1,4,0 }, 20 | { "orc", 'O',2,9,6,1,8,0 }, 21 | { "yellow light", 'y',3,15,0,0,0,0 }, 22 | { "zombie", 'Z',2,6,8,1,8,0 }, 23 | { "giant ant", 'A',3,18,3,1,6,0 }, 24 | { "fog cloud", 'f',3,1,0,1,6,0 }, 25 | { "nymph", 'N',6,12,9,1,2,0 }, 26 | { "piercer", 'p',3,1,3,2,6,0 }, 27 | { "quasit", 'Q',3,15,3,1,4,0 }, 28 | { "quivering blob", 'q',3,1,8,1,8,0 }, 29 | { "violet fungi", 'v',3,1,7,1,4,0 }, 30 | { "giant beetle", 'b',4,6,4,3,4,0 }, 31 | { "centaur", 'C',4,18,4,1,6,0 }, 32 | { "cockatrice", 'c',4,6,6,1,3,0 }, 33 | { "gelatinous cube", 'g',4,6,8,2,4,0 }, 34 | { "jaguar", 'j',4,15,6,1,8,0 }, 35 | { "killer bee", 'k',4,14,4,2,4,0 }, 36 | { "snake", 'S',4,15,3,1,6,0 }, 37 | { "freezing sphere", 'F',2,13,4,0,0,0 }, 38 | { "owlbear", 'o',5,12,5,2,6,0 }, 39 | { "rust monster", 'R',10,18,3,0,0,0 }, 40 | { "scorpion", 's',5,15,3,1,4,0 }, 41 | { "tengu", 't',5,13,5,1,7,0 }, 42 | { "wraith", 'W',5,12,5,1,6,0 }, 43 | #ifdef NOWORM 44 | { "wumpus", 'w',8,3,2,3,6,0 }, 45 | #else 46 | { "long worm", 'w',8,3,5,1,4,0 }, 47 | #endif NOWORM 48 | { "large dog", 'd',6,15,4,2,4,0 }, 49 | { "leocrotta", 'l',6,18,4,3,6,0 }, 50 | { "mimic", 'M',7,3,7,3,4,0 }, 51 | { "troll", 'T',7,12,4,2,7,0 }, 52 | { "unicorn", 'u',8,24,5,1,10,0 }, 53 | { "yeti", 'Y',5,15,6,1,6,0 }, 54 | { "stalker", 'I',8,12,3,4,4,0 }, 55 | { "umber hulk", 'U',9,6,2,2,10,0 }, 56 | { "vampire", 'V',8,12,1,1,6,0 }, 57 | { "xorn", 'X',8,9,-2,4,6,0 }, 58 | { "xan", 'x',7,18,-2,2,4,0 }, 59 | { "zruty", 'z',9,8,3,3,6,0 }, 60 | { "chameleon", ':',6,5,6,4,2,0 }, 61 | { "dragon", 'D',10,9,-1,3,8,0 }, 62 | { "ettin", 'e',10,12,3,2,8,0 }, 63 | { "lurker above", '\'',10,3,3,0,0,0 }, 64 | { "nurse", 'n',11,6,0,1,3,0 }, 65 | { "trapper", ',',12,3,3,0,0,0 }, 66 | { "purple worm", 'P',15,9,6,2,8,0 }, 67 | { "demon", '&',10,12,-4,1,4,0 }, 68 | { "minotaur", 'm',15,15,6,4,10,0 }, 69 | { "shopkeeper", '@', 12, 18, 0, 4, 8, sizeof(struct eshk) } 70 | }; 71 | 72 | struct permonst pm_ghost = { "ghost", ' ', 10, 3, -5, 1, 1, sizeof(plname) }; 73 | struct permonst pm_wizard = { 74 | "wizard of Yendor", '1', 15, 12, -2, 1, 12, 0 75 | }; 76 | #ifdef MAIL 77 | struct permonst pm_mail_daemon = { "mail daemon", '2', 100, 1, 10, 0, 0, 0 }; 78 | #endif MAIL 79 | struct permonst pm_eel = { "giant eel", ';', 15, 6, -3, 3, 6, 0 }; 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | *.dylib 25 | 26 | # Shared objects (inc. Windows DLLs) 27 | *.dll 28 | *.so 29 | *.so.* 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Debug information files 55 | *.dwo 56 | 57 | # Static analysis 58 | *.sarif 59 | 60 | # Editor backups 61 | *~ 62 | *.swp 63 | *.swo 64 | .DS_Store 65 | Thumbs.db 66 | 67 | # IDE files 68 | .vscode/ 69 | .idea/ 70 | *.sublime-* 71 | *.code-workspace 72 | .claude/ 73 | # CMake 74 | build/ 75 | build-*/ 76 | cmake-build-*/ 77 | alp-build/ 78 | CMakeCache.txt 79 | CMakeFiles/ 80 | CMakeScripts/ 81 | cmake_install.cmake 82 | install_manifest.txt 83 | compile_commands.json 84 | CTestTestfile.cmake 85 | _deps/ 86 | 87 | # Generated Makefiles (not our BSD Makefile) 88 | [Mm]akefile 89 | !Makefile 90 | 91 | # Autotools 92 | .deps/ 93 | .libs/ 94 | *.la 95 | *.lo 96 | aclocal.m4 97 | autom4te.cache/ 98 | config.guess 99 | config.log 100 | config.status 101 | config.sub 102 | configure 103 | depcomp 104 | install-sh 105 | libtool 106 | ltmain.sh 107 | missing 108 | stamp-h1 109 | 110 | # pkg-config 111 | *.pc 112 | 113 | # Coverage files 114 | *.gcov 115 | *.gcno 116 | *.gcda 117 | lcov.info 118 | coverage/ 119 | 120 | # Valgrind 121 | *.valgrind 122 | vgcore.* 123 | *logs/ 124 | # Core dumps 125 | core 126 | core.* 127 | 128 | # Game-specific generated files 129 | hack.onames.h 130 | makedefs 131 | 132 | # Game executables and utilities 133 | hack 134 | hack.exe 135 | 136 | # Game runtime files 137 | save/ 138 | bones* 139 | record 140 | record_lock 141 | safelock 142 | perm 143 | logfile 144 | paniclog 145 | hackdir/record_lock 146 | hackdir/safelock 147 | 148 | # Test outputs 149 | test_results/ 150 | test_tarball/ 151 | *.log 152 | 153 | # Package files 154 | *.tar.gz 155 | *.tar.bz2 156 | *.tar.xz 157 | *.zip 158 | *.deb 159 | *.rpm 160 | 161 | # Temporary files 162 | *.tmp 163 | *.temp 164 | .cache/ 165 | 166 | # Miscellaneous 167 | hackdir/game.lock 168 | hackdir/record.lock 169 | static-build.sh 170 | test.sh 171 | tools/build-matrix.sh 172 | restoHack-static-*-linux-x86_64.tar.gz.sha256 173 | CLAUDE.md 174 | verify-release.sh 175 | gh-release-notes.txt 176 | linux-debug 177 | sha256sums 178 | SHA256SUMS 179 | *.sha256 180 | release-notes-v1.1.0.md 181 | CLAUDE.md 182 | dev/.venv/ 183 | dev/gauntlet-logs/ 184 | uv.lock 185 | fuzz*/ 186 | dev/README-modular.md 187 | *.pyc 188 | ctest/ 189 | docs/messagebug.txt 190 | sumlogs.sh 191 | release-v*/ 192 | prepare-release.sh 193 | __pycache__/ 194 | review_diffs/ 195 | test/ 196 | release.sh 197 | .gdbinit 198 | -------------------------------------------------------------------------------- /src/hack.wield.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.wield.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | extern struct obj zeroobj; 7 | 8 | void setuwep(struct obj *obj) { setworn(obj, W_WEP); } 9 | 10 | int dowield(void) { 11 | struct obj *wep; 12 | int res = 0; 13 | 14 | multi = 0; 15 | if (!(wep = getobj("#-)", "wield"))) /* nothing */ 16 | ; 17 | else if (uwep == wep) 18 | pline("You are already wielding that!"); 19 | else if (uwep && uwep->cursed) 20 | pline("The %s welded to your hand!", aobjnam(uwep, "are")); 21 | else if (wep == &zeroobj) { 22 | if (uwep == 0) { 23 | pline("You are already empty handed."); 24 | } else { 25 | setuwep((struct obj *)0); 26 | res++; 27 | pline("You are empty handed."); 28 | } 29 | } else if (uarms && wep->otyp == TWO_HANDED_SWORD) 30 | pline("You cannot wield a two-handed sword and wear a shield."); 31 | else if (wep->owornmask & (W_ARMOR | W_RING)) 32 | pline("You cannot wield that!"); 33 | else { 34 | setuwep(wep); 35 | res++; 36 | if (uwep->cursed) 37 | pline("The %s %s to your hand!", aobjnam(uwep, "weld"), 38 | (uwep->quan == 1) ? "itself" : "themselves"); /* a3 */ 39 | else 40 | prinv(uwep); 41 | } 42 | return (res); 43 | } 44 | 45 | void corrode_weapon(void) { 46 | if (!uwep || uwep->olet != WEAPON_SYM) 47 | return; /* %% */ 48 | if (uwep->rustfree) 49 | pline("Your %s not affected.", aobjnam(uwep, "are")); 50 | else { 51 | pline("Your %s!", aobjnam(uwep, "corrode")); 52 | uwep->spe--; 53 | } 54 | } 55 | 56 | int chwepon(struct obj *otmp, int amount) { 57 | const char *color = 58 | (amount < 0) ? "black" : "green"; /* MODERN: const for string literals */ 59 | const char *time; /* MODERN: const for string literals */ 60 | if (!uwep || uwep->olet != WEAPON_SYM) { 61 | strange_feeling(otmp, 62 | (amount > 0) ? "Your hands twitch." : "Your hands itch."); 63 | return (0); 64 | } 65 | 66 | if (uwep->otyp == WORM_TOOTH && amount > 0) { 67 | uwep->otyp = CRYSKNIFE; 68 | pline("Your weapon seems sharper now."); 69 | uwep->cursed = 0; 70 | return (1); 71 | } 72 | 73 | if (uwep->otyp == CRYSKNIFE && amount < 0) { 74 | uwep->otyp = WORM_TOOTH; 75 | pline("Your weapon looks duller now."); 76 | return (1); 77 | } 78 | 79 | /* there is a (soft) upper limit to uwep->spe */ 80 | if (amount > 0 && uwep->spe > 5 && rn2(3)) { 81 | pline("Your %s violently green for a while and then evaporate%s.", 82 | aobjnam(uwep, "glow"), plur(uwep->quan)); 83 | while (uwep) /* let all of them disappear */ 84 | /* note: uwep->quan = 1 is nogood if unpaid */ 85 | useup(uwep); 86 | return (1); 87 | } 88 | if (!rn2(6)) 89 | amount *= 2; 90 | time = (amount * amount == 1) ? "moment" : "while"; 91 | pline("Your %s %s for a %s.", aobjnam(uwep, "glow"), color, time); 92 | uwep->spe += (schar)amount; /* MODERN: Safe cast - enchantment limited by game mechanics */ 93 | if (amount > 0) 94 | uwep->cursed = 0; 95 | return (1); 96 | } 97 | -------------------------------------------------------------------------------- /src/hack.rumors.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.rumors.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" /* for RUMORFILE and BSD (index) */ 6 | #include 7 | #define CHARSZ 8 /* number of bits in a char */ 8 | /* MODERN: removed bogus extern for index() - provided by string.h/compat.h */ 9 | 10 | /* Forward declarations */ 11 | int skipline(FILE *rumf); 12 | int used(int i); 13 | 14 | int n_rumors = 0; 15 | int n_used_rumors = -1; 16 | char *usedbits; 17 | 18 | void init_rumors(FILE *rumf) { 19 | int i; 20 | n_used_rumors = 0; 21 | while (skipline(rumf)) 22 | n_rumors++; 23 | rewind(rumf); 24 | /* MODERN: Prevent division by zero and integer overflow */ 25 | if (CHARSZ == 0 || n_rumors < 0) { 26 | impossible("Invalid rumor system parameters: rumors=%d charsz=%d", n_rumors, 27 | CHARSZ); 28 | return; 29 | } 30 | i = n_rumors / CHARSZ; 31 | usedbits = (char *)alloc((unsigned)(i + 1)); 32 | for (; i >= 0; i--) 33 | usedbits[i] = 0; 34 | } 35 | 36 | int skipline(FILE *rumf) { 37 | char line[COLNO]; 38 | while (1) { 39 | if (!fgets(line, sizeof(line), rumf)) 40 | return (0); 41 | /* MODERN: Ensure null termination */ 42 | line[COLNO - 1] = '\0'; 43 | if (index(line, '\n')) 44 | return (1); 45 | } 46 | } 47 | 48 | void outline(FILE *rumf) { 49 | char line[COLNO]; 50 | char *ep; 51 | if (!fgets(line, sizeof(line), rumf)) 52 | return; 53 | /* MODERN: Ensure null termination and validate line length */ 54 | line[COLNO - 1] = '\0'; 55 | if ((ep = index(line, '\n')) != 0) 56 | *ep = 0; 57 | pline("This cookie has a scrap of paper inside! It reads: "); 58 | pline("%s", 59 | line); /* MODERN: Safe format string - prevent format string attacks */ 60 | } 61 | 62 | void outrumor(void) { 63 | int rn, i; 64 | FILE *rumf; 65 | if (n_rumors <= n_used_rumors || (rumf = fopen(RUMORFILE, "r")) == (FILE *)0) 66 | return; 67 | if (n_used_rumors < 0) 68 | init_rumors(rumf); 69 | if (!n_rumors) 70 | goto none; 71 | rn = rn2(n_rumors - n_used_rumors); 72 | i = 0; 73 | while (rn || used(i)) { 74 | (void)skipline(rumf); 75 | if (!used(i)) 76 | rn--; 77 | i++; 78 | } 79 | /* MODERN: Bounds check before array access */ 80 | int byte_idx = i / CHARSZ; 81 | int bit_idx = i % CHARSZ; 82 | if (byte_idx >= 0 && byte_idx < (n_rumors / CHARSZ) + 1 && bit_idx >= 0 && 83 | bit_idx < CHARSZ) { 84 | usedbits[byte_idx] |= (1 << bit_idx); 85 | n_used_rumors++; 86 | } else { 87 | impossible("Rumor index out of bounds: i=%d byte_idx=%d", i, byte_idx); 88 | } 89 | outline(rumf); 90 | none: 91 | (void)fclose(rumf); 92 | } 93 | 94 | int used(int i) { 95 | /* MODERN: Bounds check before array access */ 96 | int byte_idx = i / CHARSZ; 97 | int bit_idx = i % CHARSZ; 98 | if (byte_idx >= 0 && byte_idx < (n_rumors / CHARSZ) + 1 && bit_idx >= 0 && 99 | bit_idx < CHARSZ) { 100 | return (usedbits[byte_idx] & (1 << bit_idx)); 101 | } 102 | return 0; /* Safe default for out-of-bounds access */ 103 | } 104 | -------------------------------------------------------------------------------- /src/hack.monst.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.monst.c - version 1.0.2 */ 3 | 4 | /* 5 | * Monster definitions for 1984 Hack - creature stats and abilities 6 | * Original 1984 source: docs/historical/original-source/hack.monst.c 7 | * 8 | * Key modernizations: ANSI C compatibility 9 | */ 10 | 11 | #include "hack.h" 12 | #include "def.eshk.h" 13 | extern char plname[PL_NSIZ]; 14 | 15 | struct permonst mons[CMNUM + 2] = { 16 | {"bat", 'B', 1, 22, 8, 1, 4, 0}, 17 | {"gnome", 'G', 1, 6, 5, 1, 6, 0}, 18 | {"hobgoblin", 'H', 1, 9, 5, 1, 8, 0}, 19 | {"jackal", 'J', 0, 12, 7, 1, 2, 0}, 20 | {"kobold", 'K', 1, 6, 7, 1, 4, 0}, 21 | {"leprechaun", 'L', 5, 15, 8, 1, 2, 0}, 22 | {"giant rat", 'r', 0, 12, 7, 1, 3, 0}, 23 | {"acid blob", 'a', 2, 3, 8, 0, 0, 0}, 24 | {"floating eye", 'E', 2, 1, 9, 0, 0, 0}, 25 | {"homunculus", 'h', 2, 6, 6, 1, 3, 0}, 26 | {"imp", 'i', 2, 6, 2, 1, 4, 0}, 27 | {"orc", 'O', 2, 9, 6, 1, 8, 0}, 28 | {"yellow light", 'y', 3, 15, 0, 0, 0, 0}, 29 | {"zombie", 'Z', 2, 6, 8, 1, 8, 0}, 30 | {"giant ant", 'A', 3, 18, 3, 1, 6, 0}, 31 | {"fog cloud", 'f', 3, 1, 0, 1, 6, 0}, 32 | {"nymph", 'N', 6, 12, 9, 1, 2, 0}, 33 | {"piercer", 'p', 3, 1, 3, 2, 6, 0}, 34 | {"quasit", 'Q', 3, 15, 3, 1, 4, 0}, 35 | {"quivering blob", 'q', 3, 1, 8, 1, 8, 0}, 36 | {"violet fungi", 'v', 3, 1, 7, 1, 4, 0}, 37 | {"giant beetle", 'b', 4, 6, 4, 3, 4, 0}, 38 | {"centaur", 'C', 4, 18, 4, 1, 6, 0}, 39 | {"cockatrice", 'c', 4, 6, 6, 1, 3, 0}, 40 | {"gelatinous cube", 'g', 4, 6, 8, 2, 4, 0}, 41 | {"jaguar", 'j', 4, 15, 6, 1, 8, 0}, 42 | {"killer bee", 'k', 4, 14, 4, 2, 4, 0}, 43 | {"snake", 'S', 4, 15, 3, 1, 6, 0}, 44 | {"freezing sphere", 'F', 2, 13, 4, 0, 0, 0}, 45 | {"owlbear", 'o', 5, 12, 5, 2, 6, 0}, 46 | {"rust monster", 'R', 10, 18, 3, 0, 0, 0}, 47 | {"scorpion", 's', 5, 15, 3, 1, 4, 0}, 48 | {"tengu", 't', 5, 13, 5, 1, 7, 0}, 49 | {"wraith", 'W', 5, 12, 5, 1, 6, 0}, 50 | #ifdef NOWORM 51 | {"wumpus", 'w', 8, 3, 2, 3, 6, 0}, 52 | #else 53 | {"long worm", 'w', 8, 3, 5, 1, 4, 0}, 54 | #endif /* NOWORM */ 55 | {"large dog", 'd', 6, 15, 4, 2, 4, 0}, 56 | {"leocrotta", 'l', 6, 18, 4, 3, 6, 0}, 57 | {"mimic", 'M', 7, 3, 7, 3, 4, 0}, 58 | {"troll", 'T', 7, 12, 4, 2, 7, 0}, 59 | {"unicorn", 'u', 8, 24, 5, 1, 10, 0}, 60 | {"yeti", 'Y', 5, 15, 6, 1, 6, 0}, 61 | {"stalker", 'I', 8, 12, 3, 4, 4, 0}, 62 | {"umber hulk", 'U', 9, 6, 2, 2, 10, 0}, 63 | {"vampire", 'V', 8, 12, 1, 1, 6, 0}, 64 | {"xorn", 'X', 8, 9, -2, 4, 6, 0}, 65 | {"xan", 'x', 7, 18, -2, 2, 4, 0}, 66 | {"zruty", 'z', 9, 8, 3, 3, 6, 0}, 67 | {"chameleon", ':', 6, 5, 6, 4, 2, 0}, 68 | {"dragon", 'D', 10, 9, -1, 3, 8, 0}, 69 | {"ettin", 'e', 10, 12, 3, 2, 8, 0}, 70 | {"lurker above", '\'', 10, 3, 3, 0, 0, 0}, 71 | {"nurse", 'n', 11, 6, 0, 1, 3, 0}, 72 | {"trapper", ',', 12, 3, 3, 0, 0, 0}, 73 | {"purple worm", 'P', 15, 9, 6, 2, 8, 0}, 74 | {"demon", '&', 10, 12, -4, 1, 4, 0}, 75 | {"minotaur", 'm', 15, 15, 6, 4, 10, 0}, 76 | {"shopkeeper", '@', 12, 18, 0, 4, 8, sizeof(struct eshk)}}; 77 | 78 | struct permonst pm_ghost = {"ghost", ' ', 10, 3, -5, 1, 1, sizeof(plname)}; 79 | struct permonst pm_wizard = {"wizard of Yendor", '1', 15, 12, -2, 1, 12, 0}; 80 | #ifdef MAIL 81 | struct permonst pm_mail_daemon = {"mail daemon", '2', 100, 1, 10, 0, 0, 0}; 82 | #endif /* MAIL */ 83 | struct permonst pm_eel = {"giant eel", ';', 15, 6, -3, 3, 6, 0}; 84 | -------------------------------------------------------------------------------- /dev/run-asan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Individual AddressSanitizer (ASan) test runner for restoHack 4 | """ 5 | 6 | import os 7 | import sys 8 | import shutil 9 | import argparse 10 | from pathlib import Path 11 | from datetime import datetime, timezone 12 | 13 | # Add lib directory to path for imports 14 | sys.path.insert(0, str(Path(__file__).parent / 'lib')) 15 | 16 | from colors import Colors 17 | from build_lanes import BuildLane, BuildManager 18 | from test_runner import TestRunner 19 | 20 | 21 | def main(): 22 | parser = argparse.ArgumentParser(description='restoHack ASan Test Runner') 23 | parser.add_argument('--debug', action='store_true', help='Enable debug output') 24 | parser.add_argument('--runs', type=int, default=50, help='Number of test runs') 25 | parser.add_argument('--steps', type=int, default=40, help='Steps per test run') 26 | parser.add_argument('--enhanced', action='store_true', help='Enable enhanced testing') 27 | parser.add_argument('--build-only', action='store_true', help='Only build, don\'t test') 28 | parser.add_argument('--test-only', action='store_true', help='Only test (assume built)') 29 | parser.add_argument('--no-clean', action='store_true', help='Skip clean rebuild (faster but may have stale objects)') 30 | 31 | args = parser.parse_args() 32 | 33 | # Check requirements 34 | if not shutil.which('clang'): 35 | Colors.error("clang not found") 36 | return 1 37 | 38 | if not shutil.which('cmake'): 39 | Colors.error("cmake not found") 40 | return 1 41 | 42 | try: 43 | import pexpect 44 | except ImportError: 45 | Colors.error("pexpect module not found. Install with: uv add pexpect") 46 | return 1 47 | 48 | # Setup paths 49 | project_root = Path(__file__).parent.parent 50 | 51 | # Create timestamped log directory 52 | timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") 53 | log_dir = Path(__file__).parent / "test-logs" / f"asan-{timestamp}" 54 | log_dir.mkdir(parents=True, exist_ok=True) 55 | 56 | # Create ASan lane 57 | lane = BuildLane( 58 | "asan", 59 | str(project_root / "build-asan"), 60 | "-fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all" 61 | ) 62 | 63 | Colors.say("ASAN", f"🔥 AddressSanitizer Lane - Logs: {log_dir}") 64 | 65 | # Build phase 66 | if not args.test_only: 67 | build_manager = BuildManager(str(project_root), log_dir) 68 | clean_rebuild = not args.no_clean 69 | if not build_manager.build_lane(lane, clean_rebuild): 70 | Colors.error("ASan build failed") 71 | return 1 72 | Colors.success("ASan build completed") 73 | 74 | if args.build_only: 75 | Colors.success("Build-only mode complete") 76 | return 0 77 | 78 | # Test phase 79 | test_runner = TestRunner(log_dir, args.debug, args.enhanced) 80 | passed, failed = test_runner.test_lane(lane, args.runs, args.steps) 81 | 82 | # Results 83 | total = passed + failed 84 | failure_rate = (failed * 100) // total if total > 0 else 0 85 | 86 | Colors.say("RESULTS", f"Passed: {passed}, Failed: {failed}, Rate: {failure_rate}%") 87 | 88 | if failure_rate > 10: # Allow up to 10% failure for stress testing 89 | Colors.error(f"High failure rate: {failure_rate}%") 90 | return 1 91 | else: 92 | Colors.success("ASan testing completed successfully") 93 | return 0 94 | 95 | 96 | if __name__ == '__main__': 97 | sys.exit(main()) -------------------------------------------------------------------------------- /src/hack.bones.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.bones.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | /* 6 | * Bones file system for 1984 Hack - death and ghost management 7 | * Original 1984 source: docs/historical/original-source/hack.bones.c 8 | * 9 | * Key modernizations: ANSI C function signatures 10 | */ 11 | 12 | #include "hack.h" 13 | #include 14 | #include 15 | extern char plname[PL_NSIZ]; 16 | extern long somegold(); 17 | extern struct monst *makemon(); 18 | extern struct permonst pm_ghost; 19 | 20 | char bones[] = "bones_xx"; 21 | 22 | /* save bones and possessions of a deceased adventurer */ 23 | void savebones(void) { 24 | int fd; 25 | struct obj *otmp; 26 | struct trap *ttmp; 27 | struct monst *mtmp; 28 | if (dlevel <= 0 || dlevel > MAXLEVEL) 29 | return; 30 | if (!rn2(1 + dlevel / 2)) 31 | return; /* not so many ghosts on low levels */ 32 | bones[6] = '0' + (dlevel / 10); 33 | bones[7] = '0' + (dlevel % 10); 34 | if ((fd = open(bones, 0)) >= 0) { 35 | (void)close(fd); 36 | return; 37 | } 38 | /* drop everything; the corpse's possessions are usually cursed */ 39 | otmp = invent; 40 | while (otmp) { 41 | otmp->ox = u.ux; 42 | otmp->oy = u.uy; 43 | otmp->age = 0; /* very long ago */ 44 | otmp->owornmask = 0; 45 | if (rn2(5)) 46 | otmp->cursed = 1; 47 | if (!otmp->nobj) { 48 | otmp->nobj = fobj; 49 | fobj = invent; 50 | invent = 0; /* superfluous */ 51 | break; 52 | } 53 | otmp = otmp->nobj; 54 | } 55 | if (!(mtmp = makemon(PM_GHOST, u.ux, u.uy))) 56 | return; 57 | mtmp->mx = u.ux; 58 | mtmp->my = u.uy; 59 | mtmp->msleep = 1; 60 | (void)strncpy((char *)mtmp->mextra, plname, PL_NSIZ - 1); 61 | ((char *)mtmp->mextra)[PL_NSIZ - 1] = 62 | '\0'; /* MODERN: Ensure null termination */ 63 | mkgold(somegold() + d(dlevel, 30), u.ux, u.uy); 64 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { 65 | mtmp->m_id = 0; 66 | if (mtmp->mtame) { 67 | mtmp->mtame = 0; 68 | mtmp->mpeaceful = 0; 69 | } 70 | mtmp->mlstmv = 0; 71 | if (mtmp->mdispl) 72 | unpmon(mtmp); 73 | } 74 | for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) 75 | ttmp->tseen = 0; 76 | for (otmp = fobj; otmp; otmp = otmp->nobj) { 77 | otmp->o_id = 0; 78 | /* otmp->o_cnt_id = 0; - superfluous */ 79 | otmp->onamelth = 0; 80 | otmp->known = 0; 81 | otmp->invlet = 0; 82 | if (otmp->olet == AMULET_SYM && !otmp->spe) { 83 | otmp->spe = -1; /* no longer the actual amulet */ 84 | otmp->cursed = 1; /* flag as gotten from a ghost */ 85 | } 86 | } 87 | if ((fd = creat(bones, FMASK)) < 0) 88 | return; 89 | savelev(fd, dlevel); 90 | (void)close(fd); 91 | } 92 | 93 | int getbones(void) { 94 | int fd, x, y, ok; 95 | if (rn2(3)) 96 | return (0); /* only once in three times do we find bones */ 97 | bones[6] = '0' + dlevel / 10; 98 | bones[7] = '0' + dlevel % 10; 99 | if ((fd = open(bones, 0)) < 0) 100 | return (0); 101 | if ((ok = uptodate(fd)) != 0) { 102 | getlev(fd, 0, dlevel); 103 | for (x = 0; x < COLNO; x++) 104 | for (y = 0; y < ROWNO; y++) 105 | levl[x][y].seen = levl[x][y].new = 0; 106 | } 107 | (void)close(fd); 108 | #ifdef WIZARD 109 | if (!wizard) /* duvel!frans: don't remove bones while debugging */ 110 | #endif /* WiZARD */ 111 | if (unlink(bones) < 0) { 112 | pline("Cannot unlink %s .", bones); 113 | return (0); 114 | } 115 | return (ok); 116 | } 117 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.mkmaze.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.mkmaze.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | #include "def.mkroom.h" /* not really used */ 7 | extern struct monst *makemon(); 8 | extern struct permonst pm_wizard; 9 | extern struct obj *mkobj_at(); 10 | extern coord mazexy(); 11 | struct permonst hell_hound = 12 | { "hell hound", 'd', 12, 14, 2, 3, 6, 0 }; 13 | 14 | makemaz() 15 | { 16 | int x,y; 17 | int zx,zy; 18 | coord mm; 19 | boolean al = (dlevel >= 30 && !flags.made_amulet); 20 | 21 | for(x = 2; x < COLNO-1; x++) 22 | for(y = 2; y < ROWNO-1; y++) 23 | levl[x][y].typ = (x%2 && y%2) ? 0 : HWALL; 24 | if(al) { 25 | struct monst *mtmp; 26 | 27 | zx = 2*(COLNO/4) - 1; 28 | zy = 2*(ROWNO/4) - 1; 29 | for(x = zx-2; x < zx+4; x++) for(y = zy-2; y <= zy+2; y++) { 30 | levl[x][y].typ = 31 | (y == zy-2 || y == zy+2 || x == zx-2 || x == zx+3) ? POOL : 32 | (y == zy-1 || y == zy+1 || x == zx-1 || x == zx+2) ? HWALL: 33 | ROOM; 34 | } 35 | (void) mkobj_at(AMULET_SYM, zx, zy); 36 | flags.made_amulet = 1; 37 | walkfrom(zx+4, zy); 38 | if(mtmp = makemon(&hell_hound, zx, zy)) 39 | mtmp->msleep = 1; 40 | if(mtmp = makemon(PM_WIZARD, zx+1, zy)) { 41 | mtmp->msleep = 1; 42 | flags.no_of_wizards = 1; 43 | } 44 | } else { 45 | mm = mazexy(); 46 | zx = mm.x; 47 | zy = mm.y; 48 | walkfrom(zx,zy); 49 | (void) mksobj_at(WAN_WISHING, zx, zy); 50 | (void) mkobj_at(ROCK_SYM, zx, zy); /* put a rock on top of it */ 51 | } 52 | 53 | for(x = 2; x < COLNO-1; x++) 54 | for(y = 2; y < ROWNO-1; y++) { 55 | switch(levl[x][y].typ) { 56 | case HWALL: 57 | levl[x][y].scrsym = '-'; 58 | break; 59 | case ROOM: 60 | levl[x][y].scrsym = '.'; 61 | break; 62 | } 63 | } 64 | for(x = rn1(8,11); x; x--) { 65 | mm = mazexy(); 66 | (void) mkobj_at(rn2(2) ? GEM_SYM : 0, mm.x, mm.y); 67 | } 68 | for(x = rn1(10,2); x; x--) { 69 | mm = mazexy(); 70 | (void) mkobj_at(ROCK_SYM, mm.x, mm.y); 71 | } 72 | mm = mazexy(); 73 | (void) makemon(PM_MINOTAUR, mm.x, mm.y); 74 | for(x = rn1(5,7); x; x--) { 75 | mm = mazexy(); 76 | (void) makemon((struct permonst *) 0, mm.x, mm.y); 77 | } 78 | for(x = rn1(6,7); x; x--) { 79 | mm = mazexy(); 80 | mkgold(0L,mm.x,mm.y); 81 | } 82 | for(x = rn1(6,7); x; x--) 83 | mktrap(0,1,(struct mkroom *) 0); 84 | mm = mazexy(); 85 | levl[(xupstair = mm.x)][(yupstair = mm.y)].scrsym = '<'; 86 | levl[xupstair][yupstair].typ = STAIRS; 87 | xdnstair = ydnstair = 0; 88 | } 89 | 90 | walkfrom(x,y) int x,y; { 91 | int q,a,dir; 92 | int dirs[4]; 93 | levl[x][y].typ = ROOM; 94 | while(1) { 95 | q = 0; 96 | for(a = 0; a < 4; a++) 97 | if(okay(x,y,a)) dirs[q++]= a; 98 | if(!q) return; 99 | dir = dirs[rn2(q)]; 100 | move(&x,&y,dir); 101 | levl[x][y].typ = ROOM; 102 | move(&x,&y,dir); 103 | walkfrom(x,y); 104 | } 105 | } 106 | 107 | move(x,y,dir) 108 | int *x, *y; 109 | int dir; 110 | { 111 | switch(dir){ 112 | case 0: --(*y); break; 113 | case 1: (*x)++; break; 114 | case 2: (*y)++; break; 115 | case 3: --(*x); break; 116 | } 117 | } 118 | 119 | okay(x,y,dir) 120 | int x,y; 121 | int dir; 122 | { 123 | move(&x,&y,dir); 124 | move(&x,&y,dir); 125 | if(x<3 || y<3 || x>COLNO-3 || y>ROWNO-3 || levl[x][y].typ != 0) 126 | return(0); 127 | else 128 | return(1); 129 | } 130 | 131 | coord 132 | mazexy(){ 133 | coord mm; 134 | mm.x = 3 + 2*rn2(COLNO/2 - 2); 135 | mm.y = 3 + 2*rn2(ROWNO/2 - 2); 136 | return mm; 137 | } 138 | -------------------------------------------------------------------------------- /dev/run-asan-ubsan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Individual ASan+UBSan combined test runner for restoHack 4 | """ 5 | 6 | import os 7 | import sys 8 | import shutil 9 | import argparse 10 | from pathlib import Path 11 | from datetime import datetime, timezone 12 | 13 | # Add lib directory to path for imports 14 | sys.path.insert(0, str(Path(__file__).parent / 'lib')) 15 | 16 | from colors import Colors 17 | from build_lanes import BuildLane, BuildManager 18 | from test_runner import TestRunner 19 | 20 | 21 | def main(): 22 | parser = argparse.ArgumentParser(description='restoHack ASan+UBSan Test Runner') 23 | parser.add_argument('--debug', action='store_true', help='Enable debug output') 24 | parser.add_argument('--runs', type=int, default=50, help='Number of test runs') 25 | parser.add_argument('--steps', type=int, default=40, help='Steps per test run') 26 | parser.add_argument('--enhanced', action='store_true', help='Enable enhanced testing') 27 | parser.add_argument('--build-only', action='store_true', help='Only build, don\'t test') 28 | parser.add_argument('--test-only', action='store_true', help='Only test (assume built)') 29 | parser.add_argument('--no-clean', action='store_true', help='Skip clean rebuild (faster but may have stale objects)') 30 | 31 | args = parser.parse_args() 32 | 33 | # Check requirements 34 | if not shutil.which('clang'): 35 | Colors.error("clang not found") 36 | return 1 37 | 38 | if not shutil.which('cmake'): 39 | Colors.error("cmake not found") 40 | return 1 41 | 42 | try: 43 | import pexpect 44 | except ImportError: 45 | Colors.error("pexpect module not found. Install with: uv add pexpect") 46 | return 1 47 | 48 | # Setup paths 49 | project_root = Path(__file__).parent.parent 50 | 51 | # Create timestamped log directory 52 | timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") 53 | log_dir = Path(__file__).parent / "test-logs" / f"asan-ubsan-{timestamp}" 54 | log_dir.mkdir(parents=True, exist_ok=True) 55 | 56 | # Create ASan+UBSan combined lane 57 | lane = BuildLane( 58 | "asan-ubsan", 59 | str(project_root / "build-asan-ubsan"), 60 | "-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all" 61 | ) 62 | 63 | Colors.say("ASAN+UBSAN", f"🔥⚡ ASan+UBSan Combined Lane - Logs: {log_dir}") 64 | 65 | # Build phase 66 | if not args.test_only: 67 | build_manager = BuildManager(str(project_root), log_dir) 68 | clean_rebuild = not args.no_clean 69 | if not build_manager.build_lane(lane, clean_rebuild): 70 | Colors.error("ASan+UBSan build failed") 71 | return 1 72 | Colors.success("ASan+UBSan build completed") 73 | 74 | if args.build_only: 75 | Colors.success("Build-only mode complete") 76 | return 0 77 | 78 | # Test phase 79 | test_runner = TestRunner(log_dir, args.debug, args.enhanced) 80 | passed, failed = test_runner.test_lane(lane, args.runs, args.steps) 81 | 82 | # Results 83 | total = passed + failed 84 | failure_rate = (failed * 100) // total if total > 0 else 0 85 | 86 | Colors.say("RESULTS", f"Passed: {passed}, Failed: {failed}, Rate: {failure_rate}%") 87 | 88 | if failure_rate > 10: # Allow up to 10% failure for stress testing 89 | Colors.error(f"High failure rate: {failure_rate}%") 90 | return 1 91 | else: 92 | Colors.success("ASan+UBSan testing completed successfully") 93 | return 0 94 | 95 | 96 | if __name__ == '__main__': 97 | sys.exit(main()) -------------------------------------------------------------------------------- /docs/historical/original-source/hack.mkobj.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.mkobj.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | char mkobjstr[] = "))[[!!!!????%%%%/=**))[[!!!!????%%%%/=**(%"; 8 | struct obj *mkobj(), *mksobj(); 9 | 10 | struct obj * 11 | mkobj_at(let,x,y) 12 | int let,x,y; 13 | { 14 | struct obj *otmp = mkobj(let); 15 | otmp->ox = x; 16 | otmp->oy = y; 17 | otmp->nobj = fobj; 18 | fobj = otmp; 19 | return(otmp); 20 | } 21 | 22 | mksobj_at(otyp,x,y) 23 | int otyp,x,y; 24 | { 25 | struct obj *otmp = mksobj(otyp); 26 | otmp->ox = x; 27 | otmp->oy = y; 28 | otmp->nobj = fobj; 29 | fobj = otmp; 30 | } 31 | 32 | struct obj * 33 | mkobj(let) { 34 | if(!let) 35 | let = mkobjstr[rn2(sizeof(mkobjstr) - 1)]; 36 | return( 37 | mksobj( 38 | letter(let) ? 39 | CORPSE + ((let > 'Z') ? (let-'a'+'Z'-'@'+1) : (let-'@')) 40 | : probtype(let) 41 | ) 42 | ); 43 | } 44 | 45 | 46 | struct obj zeroobj; 47 | 48 | struct obj * 49 | mksobj(otyp) 50 | int otyp; 51 | { 52 | struct obj *otmp; 53 | char let = objects[otyp].oc_olet; 54 | 55 | otmp = newobj(0); 56 | *otmp = zeroobj; 57 | otmp->age = moves; 58 | otmp->o_id = flags.ident++; 59 | otmp->quan = 1; 60 | otmp->olet = let; 61 | otmp->otyp = otyp; 62 | otmp->dknown = index("/=!?*", let) ? 0 : 1; 63 | switch(let) { 64 | case WEAPON_SYM: 65 | otmp->quan = (otmp->otyp <= ROCK) ? rn1(6,6) : 1; 66 | if(!rn2(11)) otmp->spe = rnd(3); 67 | else if(!rn2(10)) { 68 | otmp->cursed = 1; 69 | otmp->spe = -rnd(3); 70 | } 71 | break; 72 | case FOOD_SYM: 73 | if(otmp->otyp >= CORPSE) break; 74 | #ifdef NOT_YET_IMPLEMENTED 75 | /* if tins are to be identified, need to adapt doname() etc */ 76 | if(otmp->otyp == TIN) 77 | otmp->spe = rnd(...); 78 | #endif NOT_YET_IMPLEMENTED 79 | /* fall into next case */ 80 | case GEM_SYM: 81 | otmp->quan = rn2(6) ? 1 : 2; 82 | case TOOL_SYM: 83 | case CHAIN_SYM: 84 | case BALL_SYM: 85 | case ROCK_SYM: 86 | case POTION_SYM: 87 | case SCROLL_SYM: 88 | case AMULET_SYM: 89 | break; 90 | case ARMOR_SYM: 91 | if(!rn2(8)) otmp->cursed = 1; 92 | if(!rn2(10)) otmp->spe = rnd(3); 93 | else if(!rn2(9)) { 94 | otmp->spe = -rnd(3); 95 | otmp->cursed = 1; 96 | } 97 | break; 98 | case WAND_SYM: 99 | if(otmp->otyp == WAN_WISHING) otmp->spe = 3; else 100 | otmp->spe = rn1(5, 101 | (objects[otmp->otyp].bits & NODIR) ? 11 : 4); 102 | break; 103 | case RING_SYM: 104 | if(objects[otmp->otyp].bits & SPEC) { 105 | if(!rn2(3)) { 106 | otmp->cursed = 1; 107 | otmp->spe = -rnd(2); 108 | } else otmp->spe = rnd(2); 109 | } else if(otmp->otyp == RIN_TELEPORTATION || 110 | otmp->otyp == RIN_AGGRAVATE_MONSTER || 111 | otmp->otyp == RIN_HUNGER || !rn2(9)) 112 | otmp->cursed = 1; 113 | break; 114 | default: 115 | panic("impossible mkobj"); 116 | } 117 | otmp->owt = weight(otmp); 118 | return(otmp); 119 | } 120 | 121 | letter(c) { 122 | return(('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z')); 123 | } 124 | 125 | weight(obj) 126 | struct obj *obj; 127 | { 128 | int wt = objects[obj->otyp].oc_weight; 129 | return(wt ? wt*obj->quan : (obj->quan + 1)/2); 130 | } 131 | 132 | mkgold(num,x,y) 133 | long num; 134 | { 135 | struct gold *gold; 136 | long amount = (num ? num : 1 + (rnd(dlevel+2) * rnd(30))); 137 | 138 | if(gold = g_at(x,y)) 139 | gold->amount += amount; 140 | else { 141 | gold = newgold(); 142 | gold->ngold = fgold; 143 | gold->gx = x; 144 | gold->gy = y; 145 | gold->amount = amount; 146 | fgold = gold; 147 | /* do sth with display? */ 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /dev/run-ubsan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Individual UndefinedBehaviorSanitizer (UBSan) test runner for restoHack 4 | """ 5 | 6 | import os 7 | import sys 8 | import shutil 9 | import argparse 10 | from pathlib import Path 11 | from datetime import datetime, timezone 12 | 13 | # Add lib directory to path for imports 14 | sys.path.insert(0, str(Path(__file__).parent / 'lib')) 15 | 16 | from colors import Colors 17 | from build_lanes import BuildLane, BuildManager 18 | from test_runner import TestRunner 19 | 20 | 21 | def main(): 22 | parser = argparse.ArgumentParser(description='restoHack UBSan Test Runner') 23 | parser.add_argument('--debug', action='store_true', help='Enable debug output') 24 | parser.add_argument('--runs', type=int, default=50, help='Number of test runs') 25 | parser.add_argument('--steps', type=int, default=40, help='Steps per test run') 26 | parser.add_argument('--enhanced', action='store_true', help='Enable enhanced testing') 27 | parser.add_argument('--build-only', action='store_true', help='Only build, don\'t test') 28 | parser.add_argument('--test-only', action='store_true', help='Only test (assume built)') 29 | parser.add_argument('--no-clean', action='store_true', help='Skip clean rebuild (faster but may have stale objects)') 30 | 31 | args = parser.parse_args() 32 | 33 | # Check requirements 34 | if not shutil.which('clang'): 35 | Colors.error("clang not found") 36 | return 1 37 | 38 | if not shutil.which('cmake'): 39 | Colors.error("cmake not found") 40 | return 1 41 | 42 | try: 43 | import pexpect 44 | except ImportError: 45 | Colors.error("pexpect module not found. Install with: uv add pexpect") 46 | return 1 47 | 48 | # Setup paths 49 | project_root = Path(__file__).parent.parent 50 | 51 | # Create timestamped log directory 52 | timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S") 53 | log_dir = Path(__file__).parent / "test-logs" / f"ubsan-{timestamp}" 54 | log_dir.mkdir(parents=True, exist_ok=True) 55 | 56 | # Create UBSan lane 57 | lane = BuildLane( 58 | "ubsan", 59 | str(project_root / "build-ubsan"), 60 | "-fsanitize=undefined,bounds,shift,integer-divide-by-zero,signed-integer-overflow,null,unreachable,vla-bound,object-size -fno-omit-frame-pointer -fno-sanitize-recover=all" 61 | ) 62 | 63 | Colors.say("UBSAN", f"⚡ UndefinedBehaviorSanitizer Lane - Logs: {log_dir}") 64 | 65 | # Build phase 66 | if not args.test_only: 67 | build_manager = BuildManager(str(project_root), log_dir) 68 | clean_rebuild = not args.no_clean 69 | if not build_manager.build_lane(lane, clean_rebuild): 70 | Colors.error("UBSan build failed") 71 | return 1 72 | Colors.success("UBSan build completed") 73 | 74 | if args.build_only: 75 | Colors.success("Build-only mode complete") 76 | return 0 77 | 78 | # Test phase 79 | test_runner = TestRunner(log_dir, args.debug, args.enhanced) 80 | passed, failed = test_runner.test_lane(lane, args.runs, args.steps) 81 | 82 | # Results 83 | total = passed + failed 84 | failure_rate = (failed * 100) // total if total > 0 else 0 85 | 86 | Colors.say("RESULTS", f"Passed: {passed}, Failed: {failed}, Rate: {failure_rate}%") 87 | 88 | if failure_rate > 10: # Allow up to 10% failure for stress testing 89 | Colors.error(f"High failure rate: {failure_rate}%") 90 | return 1 91 | else: 92 | Colors.success("UBSan testing completed successfully") 93 | return 0 94 | 95 | 96 | if __name__ == '__main__': 97 | sys.exit(main()) -------------------------------------------------------------------------------- /docs/historical/original-source/hack.search.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.search.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | extern struct monst *makemon(); 8 | 9 | findit() /* returns number of things found */ 10 | { 11 | int num; 12 | xchar zx,zy; 13 | struct trap *ttmp; 14 | struct monst *mtmp; 15 | xchar lx,hx,ly,hy; 16 | 17 | if(u.uswallow) return(0); 18 | for(lx = u.ux; (num = levl[lx-1][u.uy].typ) && num != CORR; lx--) ; 19 | for(hx = u.ux; (num = levl[hx+1][u.uy].typ) && num != CORR; hx++) ; 20 | for(ly = u.uy; (num = levl[u.ux][ly-1].typ) && num != CORR; ly--) ; 21 | for(hy = u.uy; (num = levl[u.ux][hy+1].typ) && num != CORR; hy++) ; 22 | num = 0; 23 | for(zy = ly; zy <= hy; zy++) 24 | for(zx = lx; zx <= hx; zx++) { 25 | if(levl[zx][zy].typ == SDOOR) { 26 | levl[zx][zy].typ = DOOR; 27 | atl(zx, zy, '+'); 28 | num++; 29 | } else if(levl[zx][zy].typ == SCORR) { 30 | levl[zx][zy].typ = CORR; 31 | atl(zx, zy, CORR_SYM); 32 | num++; 33 | } else if(ttmp = t_at(zx, zy)) { 34 | if(ttmp->ttyp == PIERC){ 35 | (void) makemon(PM_PIERCER, zx, zy); 36 | num++; 37 | deltrap(ttmp); 38 | } else if(!ttmp->tseen) { 39 | ttmp->tseen = 1; 40 | if(!vism_at(zx, zy)) 41 | atl(zx,zy,'^'); 42 | num++; 43 | } 44 | } else if(mtmp = m_at(zx,zy)) if(mtmp->mimic){ 45 | seemimic(mtmp); 46 | num++; 47 | } 48 | } 49 | return(num); 50 | } 51 | 52 | dosearch() 53 | { 54 | xchar x,y; 55 | struct trap *trap; 56 | struct monst *mtmp; 57 | 58 | if(u.uswallow) 59 | pline("What are you looking for? The exit?"); 60 | else 61 | for(x = u.ux-1; x < u.ux+2; x++) 62 | for(y = u.uy-1; y < u.uy+2; y++) if(x != u.ux || y != u.uy) { 63 | if(levl[x][y].typ == SDOOR) { 64 | if(rn2(7)) continue; 65 | levl[x][y].typ = DOOR; 66 | levl[x][y].seen = 0; /* force prl */ 67 | prl(x,y); 68 | nomul(0); 69 | } else if(levl[x][y].typ == SCORR) { 70 | if(rn2(7)) continue; 71 | levl[x][y].typ = CORR; 72 | levl[x][y].seen = 0; /* force prl */ 73 | prl(x,y); 74 | nomul(0); 75 | } else { 76 | /* Be careful not to find anything in an SCORR or SDOOR */ 77 | if(mtmp = m_at(x,y)) if(mtmp->mimic){ 78 | seemimic(mtmp); 79 | pline("You find a mimic."); 80 | return(1); 81 | } 82 | for(trap = ftrap; trap; trap = trap->ntrap) 83 | if(trap->tx == x && trap->ty == y && 84 | !trap->tseen && !rn2(8)) { 85 | nomul(0); 86 | pline("You find a%s.", traps[trap->ttyp]); 87 | if(trap->ttyp == PIERC) { 88 | deltrap(trap); 89 | (void) makemon(PM_PIERCER,x,y); 90 | return(1); 91 | } 92 | trap->tseen = 1; 93 | if(!vism_at(x,y)) atl(x,y,'^'); 94 | } 95 | } 96 | } 97 | return(1); 98 | } 99 | 100 | doidtrap() { 101 | struct trap *trap; 102 | int x,y; 103 | if(!getdir(1)) return(0); 104 | x = u.ux + u.dx; 105 | y = u.uy + u.dy; 106 | for(trap = ftrap; trap; trap = trap->ntrap) 107 | if(trap->tx == x && trap->ty == y && trap->tseen) { 108 | if(u.dz) 109 | if((u.dz < 0) != (!xdnstair && trap->ttyp == TRAPDOOR)) 110 | continue; 111 | pline("That is a%s.", traps[trap->ttyp]); 112 | return(0); 113 | } 114 | pline("I can't see a trap there."); 115 | return(0); 116 | } 117 | 118 | wakeup(mtmp) 119 | struct monst *mtmp; 120 | { 121 | mtmp->msleep = 0; 122 | setmangry(mtmp); 123 | if(mtmp->mimic) seemimic(mtmp); 124 | } 125 | 126 | /* NOTE: we must check if(mtmp->mimic) before calling this routine */ 127 | seemimic(mtmp) 128 | struct monst *mtmp; 129 | { 130 | mtmp->mimic = 0; 131 | mtmp->mappearance = 0; 132 | unpmon(mtmp); 133 | pmon(mtmp); 134 | } 135 | -------------------------------------------------------------------------------- /docs/historical/hack.fix: -------------------------------------------------------------------------------- 1 | /***** unido:net.games.hack / ab / 7:23 pm Sep 13, 1985*/ 2 | 3 | Recently hack (1.0.3) crashed with core dumps during some good games. 4 | The crashes occured in the onbill-routine. After investigating the core 5 | dump I found that the shopkeeper's bill was still to be paid. Normaly 6 | if you leave a shop the bill will be cleared and onbill() would not 7 | check it. But under certain conditions you can leave a shop without 8 | clearing the bill. The conditions are: 9 | 10 | 1. You have to rob a shop in order to make the shopkeeper 11 | follow you. 12 | 13 | 2. After leaving the shop being followed by the shopkeeper 14 | you must return to the shop... 15 | 16 | 3. ...and then leave the unguarded shop again. 17 | - The shopkeeper mustn't be present! 18 | 19 | If you climb the stairs to the previous level, chances are that your 20 | bill now contains much more items than allowed. If so the next call to 21 | onbill() will dump the core. 22 | 23 | Following is a context diff to fix the bug. Actually just the last hunk 24 | does the fix [it deletes two lines which have been inserted in 1.0.3], 25 | but I think the other fix was intended by the now deleted lines. 26 | 27 | Andreas 28 | 29 | -- 30 | Andreas Bormann ab@unido.UUCP 31 | University of Dortmund N 51 29' 05" E 07 24' 42" 32 | West Germany 33 | 34 | ------ the diff follows: 35 | 36 | *** hack.shk.c.orig Sun Aug 4 12:07:51 1985 37 | --- hack.shk.c Fri Sep 13 14:29:52 1985 38 | *************** 39 | *** 133,139 40 | /* Did we just leave a shop? */ 41 | if(u.uinshop && 42 | (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { 43 | - u.uinshop = 0; 44 | if(shopkeeper) { 45 | if(ESHK(shopkeeper)->billct) { 46 | pline("Somehow you escaped the shop without paying!"); 47 | 48 | --- 133,138 ----- 49 | /* Did we just leave a shop? */ 50 | if(u.uinshop && 51 | (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { 52 | if(shopkeeper) { 53 | if(ESHK(shopkeeper)->billct) { 54 | if(inroom(shopkeeper->mx, shopkeeper->my) 55 | *************** 56 | *** 136,142 57 | u.uinshop = 0; 58 | if(shopkeeper) { 59 | if(ESHK(shopkeeper)->billct) { 60 | ! pline("Somehow you escaped the shop without paying!"); 61 | addupbill(); 62 | pline("You stole for a total worth of %ld zorkmids.", 63 | total); 64 | 65 | --- 135,143 ----- 66 | (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { 67 | if(shopkeeper) { 68 | if(ESHK(shopkeeper)->billct) { 69 | ! if(inroom(shopkeeper->mx, shopkeeper->my) 70 | ! == u.uinshop - 1) /* ab@unido */ 71 | ! pline("Somehow you escaped the shop without paying!"); 72 | addupbill(); 73 | pline("You stole for a total worth of %ld zorkmids.", 74 | total); 75 | *************** 76 | *** 149,154 77 | shopkeeper = 0; 78 | shlevel = 0; 79 | } 80 | } 81 | 82 | /* Did we just enter a zoo of some kind? */ 83 | 84 | --- 150,156 ----- 85 | shopkeeper = 0; 86 | shlevel = 0; 87 | } 88 | + u.uinshop = 0; 89 | } 90 | 91 | /* Did we just enter a zoo of some kind? */ 92 | *************** 93 | *** 183,190 94 | findshk(roomno); 95 | if(!shopkeeper) { 96 | rooms[roomno].rtype = 0; 97 | - u.uinshop = 0; 98 | - } else if(inroom(shopkeeper->mx, shopkeeper->my) != roomno) { 99 | u.uinshop = 0; 100 | } else if(!u.uinshop){ 101 | if(!ESHK(shopkeeper)->visitct || 102 | 103 | --- 185,190 ----- 104 | findshk(roomno); 105 | if(!shopkeeper) { 106 | rooms[roomno].rtype = 0; 107 | u.uinshop = 0; 108 | } else if(!u.uinshop){ 109 | if(!ESHK(shopkeeper)->visitct || 110 | /* ---------- */ 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.fix: -------------------------------------------------------------------------------- 1 | /***** unido:net.games.hack / ab / 7:23 pm Sep 13, 1985*/ 2 | 3 | Recently hack (1.0.3) crashed with core dumps during some good games. 4 | The crashes occured in the onbill-routine. After investigating the core 5 | dump I found that the shopkeeper's bill was still to be paid. Normaly 6 | if you leave a shop the bill will be cleared and onbill() would not 7 | check it. But under certain conditions you can leave a shop without 8 | clearing the bill. The conditions are: 9 | 10 | 1. You have to rob a shop in order to make the shopkeeper 11 | follow you. 12 | 13 | 2. After leaving the shop being followed by the shopkeeper 14 | you must return to the shop... 15 | 16 | 3. ...and then leave the unguarded shop again. 17 | - The shopkeeper mustn't be present! 18 | 19 | If you climb the stairs to the previous level, chances are that your 20 | bill now contains much more items than allowed. If so the next call to 21 | onbill() will dump the core. 22 | 23 | Following is a context diff to fix the bug. Actually just the last hunk 24 | does the fix [it deletes two lines which have been inserted in 1.0.3], 25 | but I think the other fix was intended by the now deleted lines. 26 | 27 | Andreas 28 | 29 | -- 30 | Andreas Bormann ab@unido.UUCP 31 | University of Dortmund N 51 29' 05" E 07 24' 42" 32 | West Germany 33 | 34 | ------ the diff follows: 35 | 36 | *** hack.shk.c.orig Sun Aug 4 12:07:51 1985 37 | --- hack.shk.c Fri Sep 13 14:29:52 1985 38 | *************** 39 | *** 133,139 40 | /* Did we just leave a shop? */ 41 | if(u.uinshop && 42 | (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { 43 | - u.uinshop = 0; 44 | if(shopkeeper) { 45 | if(ESHK(shopkeeper)->billct) { 46 | pline("Somehow you escaped the shop without paying!"); 47 | 48 | --- 133,138 ----- 49 | /* Did we just leave a shop? */ 50 | if(u.uinshop && 51 | (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { 52 | if(shopkeeper) { 53 | if(ESHK(shopkeeper)->billct) { 54 | if(inroom(shopkeeper->mx, shopkeeper->my) 55 | *************** 56 | *** 136,142 57 | u.uinshop = 0; 58 | if(shopkeeper) { 59 | if(ESHK(shopkeeper)->billct) { 60 | ! pline("Somehow you escaped the shop without paying!"); 61 | addupbill(); 62 | pline("You stole for a total worth of %ld zorkmids.", 63 | total); 64 | 65 | --- 135,143 ----- 66 | (u.uinshop != roomno + 1 || shlevel != dlevel || !shopkeeper)) { 67 | if(shopkeeper) { 68 | if(ESHK(shopkeeper)->billct) { 69 | ! if(inroom(shopkeeper->mx, shopkeeper->my) 70 | ! == u.uinshop - 1) /* ab@unido */ 71 | ! pline("Somehow you escaped the shop without paying!"); 72 | addupbill(); 73 | pline("You stole for a total worth of %ld zorkmids.", 74 | total); 75 | *************** 76 | *** 149,154 77 | shopkeeper = 0; 78 | shlevel = 0; 79 | } 80 | } 81 | 82 | /* Did we just enter a zoo of some kind? */ 83 | 84 | --- 150,156 ----- 85 | shopkeeper = 0; 86 | shlevel = 0; 87 | } 88 | + u.uinshop = 0; 89 | } 90 | 91 | /* Did we just enter a zoo of some kind? */ 92 | *************** 93 | *** 183,190 94 | findshk(roomno); 95 | if(!shopkeeper) { 96 | rooms[roomno].rtype = 0; 97 | - u.uinshop = 0; 98 | - } else if(inroom(shopkeeper->mx, shopkeeper->my) != roomno) { 99 | u.uinshop = 0; 100 | } else if(!u.uinshop){ 101 | if(!ESHK(shopkeeper)->visitct || 102 | 103 | --- 185,190 ----- 104 | findshk(roomno); 105 | if(!shopkeeper) { 106 | rooms[roomno].rtype = 0; 107 | u.uinshop = 0; 108 | } else if(!u.uinshop){ 109 | if(!ESHK(shopkeeper)->visitct || 110 | /* ---------- */ 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/hack.timeout.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.timeout.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | /* 6 | * Timeout system for 1984 Hack - timed effects and events 7 | * Original 1984 source: docs/historical/original-source/hack.timeout.c 8 | * 9 | * Key modernizations: ANSI C function signatures, hack_timeout() renamed for 10 | * curses compatibility 11 | */ 12 | 13 | #include "hack.h" 14 | 15 | #if 0 16 | /* ORIGINAL 1984 CODE - commented out due to curses library conflict */ 17 | void timeout(void){ 18 | struct prop *upp; 19 | if(Stoned) stoned_dialogue(); 20 | for(upp = u.uprops; upp < u.uprops+SIZE(u.uprops); upp++) 21 | if((upp->p_flgs & TIMEOUT) && !--upp->p_flgs) { 22 | if(upp->p_tofn) (*upp->p_tofn)(); 23 | else switch(upp - u.uprops){ 24 | case STONED: 25 | killer = "cockatrice"; 26 | done("died"); 27 | break; 28 | case SICK: 29 | pline("You die because of food poisoning."); 30 | killer = u.usick_cause; 31 | done("died"); 32 | break; 33 | case FAST: 34 | pline("You feel yourself slowing down."); 35 | break; 36 | case CONFUSION: 37 | pline("You feel less confused now."); 38 | break; 39 | case BLIND: 40 | pline("You can see again."); 41 | setsee(); 42 | break; 43 | case INVIS: 44 | on_scr(u.ux,u.uy); 45 | pline("You are no longer invisible."); 46 | break; 47 | case WOUNDED_LEGS: 48 | heal_legs(); 49 | break; 50 | } 51 | } 52 | } 53 | #endif 54 | 55 | /** 56 | * MODERN ADDITION (2025): Renamed timeout to avoid curses library conflict 57 | * 58 | * WHY: curses.h may declare timeout function causing compile error. 59 | * Original function name conflicts with potential system function. 60 | * 61 | * HOW: Renamed function from timeout to hack_timeout to avoid namespace 62 | * collision. Updated all call sites to use new name. 63 | * 64 | * PRESERVES: Original 1984 game timeout logic for player effects unchanged. 65 | * Same algorithm for handling timed effects like poison, blindness, etc. 66 | * 67 | * ADDS: Namespace safety to avoid conflicts with system libraries. 68 | * Allows building with curses headers without symbol conflicts. 69 | */ 70 | void hack_timeout(void) { 71 | struct prop *upp; 72 | if (Stoned) 73 | stoned_dialogue(); 74 | for (upp = u.uprops; upp < u.uprops + SIZE(u.uprops); upp++) 75 | if ((upp->p_flgs & TIMEOUT) && !--upp->p_flgs) { 76 | if (upp->p_tofn) 77 | (*upp->p_tofn)(); 78 | else 79 | switch (upp - u.uprops) { 80 | case STONED: 81 | killer = "cockatrice"; 82 | done("died"); 83 | break; 84 | case SICK: 85 | pline("You die because of food poisoning."); 86 | killer = u.usick_cause; 87 | done("died"); 88 | break; 89 | case FAST: 90 | pline("You feel yourself slowing down."); 91 | break; 92 | case CONFUSION: 93 | pline("You feel less confused now."); 94 | break; 95 | case BLIND: 96 | pline("You can see again."); 97 | setsee(); 98 | break; 99 | case INVIS: 100 | on_scr(u.ux, u.uy); 101 | pline("You are no longer invisible."); 102 | break; 103 | case WOUNDED_LEGS: 104 | heal_legs(); 105 | break; 106 | } 107 | } 108 | } 109 | 110 | /* He is being petrified - dialogue by inmet!tower */ 111 | /* MODERN: CONST-CORRECTNESS: petrification message strings are read-only */ 112 | const char *const stoned_texts[] = { 113 | "You are slowing down.", /* 5 */ 114 | "Your limbs are stiffening.", /* 4 */ 115 | "Your limbs have turned to stone.", /* 3 */ 116 | "You have turned to stone.", /* 2 */ 117 | "You are a statue." /* 1 */ 118 | }; 119 | 120 | void stoned_dialogue(void) { 121 | long i = (Stoned & TIMEOUT); 122 | 123 | if (i > 0 && i <= SIZE(stoned_texts)) 124 | pline(stoned_texts[SIZE(stoned_texts) - i]); 125 | if (i == 5) 126 | Fast = 0; 127 | if (i == 3) 128 | nomul(-3); 129 | } 130 | -------------------------------------------------------------------------------- /src/hack.ioctl.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.ioctl.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | /* 6 | * Terminal I/O control for 1984 Hack - raw mode and signal handling 7 | * Original 1984 source: docs/historical/original-source/hack.ioctl.c 8 | * 9 | * Key modernizations: POSIX termios instead of sgtty 10 | */ 11 | 12 | /* This cannot be part of hack.tty.c (as it was earlier) since on some 13 | systems (e.g. MUNIX) the include files and 14 | define the same constants, and the C preprocessor complains. */ 15 | 16 | #include "hack.h" 17 | #include "config.h" 18 | #include 19 | #include 20 | #include 21 | 22 | /* Forward declarations for functions not properly visible */ 23 | /* MODERN: CONST-CORRECTNESS: settty message is read-only */ 24 | extern void settty(const char *s); 25 | 26 | #ifdef BSD 27 | /** 28 | * MODERN ADDITION (2025): Platform-specific terminal interface selection 29 | * 30 | * WHY: Original 1984 code used only BSD sgtty interfaces. Modern FreeBSD 31 | * has deprecated sgtty in favor of POSIX termios for standards compliance. 32 | * 33 | * HOW: Uses feature detection to include appropriate terminal headers: 34 | * - Linux: Always POSIX termios 35 | * - FreeBSD: POSIX termios (modern standard) 36 | * - Other BSD: Original sgtty interface (compatibility) 37 | * 38 | * PRESERVES: Original terminal control behavior and functionality 39 | * ADDS: POSIX compliance and cross-platform compatibility 40 | */ 41 | #ifdef __linux__ 42 | #include 43 | #else 44 | /* ORIGINAL 1984: #include */ 45 | /* MODERN: FreeBSD uses POSIX termios like Linux */ 46 | #ifdef __FreeBSD__ 47 | #include 48 | #else 49 | #include /* Original BSD sgtty interface */ 50 | #endif 51 | #endif 52 | /** 53 | * MODERN ADDITION (2025): BSD terminal compatibility fallbacks 54 | * 55 | * WHY: Original 1984 code assumed BSD sgtty interfaces available on all 56 | * systems. Modern Linux and other systems may lack BSD-specific terminal 57 | * constants. 58 | * 59 | * HOW: Provides safe fallback definitions for missing BSD terminal control 60 | * constants and structures, preventing compilation failures on non-BSD systems. 61 | * 62 | * PRESERVES: Original 1984 terminal control logic and variable usage 63 | * ADDS: Cross-platform compatibility without changing game behavior 64 | */ 65 | #ifndef TIOCGLTC 66 | #define TIOCGLTC 0 67 | #endif 68 | #ifndef TIOCSLTC 69 | #define TIOCSLTC 0 70 | #endif 71 | /* Fallback definition for ltchars if not available */ 72 | #ifndef _SGTTY_H_ 73 | struct ltchars { 74 | char t_suspc; /* stop process signal */ 75 | char t_dsuspc; /* delayed stop process signal */ 76 | char t_rprntc; /* reprint line */ 77 | char t_flushc; /* flush output (toggles) */ 78 | char t_werasc; /* word erase */ 79 | char t_lnextc; /* literal next character */ 80 | }; 81 | #endif 82 | struct ltchars ltchars, ltchars0; 83 | #else 84 | #include /* also includes part of */ 85 | struct termio termio; 86 | #endif /* BSD */ 87 | 88 | void getioctls(void) { 89 | #ifdef BSD 90 | (void)ioctl(fileno(stdin), (int)TIOCGLTC, (char *)<chars); 91 | (void)ioctl(fileno(stdin), (int)TIOCSLTC, (char *)<chars0); 92 | #else 93 | (void)ioctl(fileno(stdin), (int)TCGETA, &termio); 94 | #endif /* BSD */ 95 | } 96 | 97 | void setioctls(void) { 98 | #ifdef BSD 99 | (void)ioctl(fileno(stdin), (int)TIOCSLTC, (char *)<chars); 100 | #else 101 | (void)ioctl(fileno(stdin), (int)TCSETA, &termio); 102 | #endif /* BSD */ 103 | } 104 | 105 | #ifdef SUSPEND /* implies BSD */ 106 | int dosuspend(void) { 107 | #ifdef SIGTSTP 108 | if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) { 109 | settty((char *)0); 110 | (void)signal(SIGTSTP, SIG_DFL); 111 | (void)kill(0, SIGTSTP); 112 | gettty(); 113 | setftty(); 114 | docrt(); 115 | } else { 116 | pline("I don't think your shell has job control."); 117 | } 118 | #else /* SIGTSTP */ 119 | pline("Sorry, it seems we have no SIGTSTP here. Try ! or S."); 120 | #endif /* SIGTSTP */ 121 | return (0); 122 | } 123 | #endif /* SUSPEND */ 124 | -------------------------------------------------------------------------------- /src/hack.rip.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.rip.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | #include 7 | 8 | extern char plname[]; 9 | 10 | /* MODERN ADDITION (2025): Made tombstone strings writable to prevent segfaults 11 | */ 12 | /* Original 1984 code used read-only string literals which crash on modern 13 | * systems */ 14 | static char rip[][60] = { 15 | " ----------", 16 | " / \\", 17 | " / REST \\", 18 | " / IN \\", 19 | " / PEACE \\", 20 | " / \\", 21 | " | |", 22 | " | |", 23 | " | |", 24 | " | |", 25 | " | |", 26 | " | 1001 |", 27 | " *| * * * | *", 28 | " _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n", 29 | ""}; 30 | 31 | void outrip(void) { 32 | int dp = 0; /* Index into rip array */ 33 | char *dpx; 34 | char buf[BUFSZ]; 35 | int x, y; 36 | 37 | cls(); 38 | (void)strncpy(buf, plname, BUFSZ - 1); 39 | buf[BUFSZ - 1] = '\0'; /* MODERN: Ensure null termination */ 40 | buf[16] = 0; 41 | center(6, buf); 42 | (void)snprintf(buf, BUFSZ, "%ld AU", 43 | u.ugold); /* MODERN: Safe sprintf replacement - identical 44 | output, prevents overflow */ 45 | center(7, buf); 46 | (void)snprintf(buf, BUFSZ, 47 | "killed by%s", /* MODERN: Safe sprintf replacement - identical 48 | output, prevents overflow */ 49 | !strncmp(killer, "the ", 4) ? "" 50 | : !strcmp(killer, "starvation") ? "" 51 | : index(vowels, *killer) ? " an" 52 | : " a"); 53 | center(8, buf); 54 | (void)strncpy(buf, killer, BUFSZ - 1); 55 | buf[BUFSZ - 1] = '\0'; /* MODERN: Ensure null termination */ 56 | if (strlen(buf) > 16) { 57 | int i, i0, i1; 58 | i0 = i1 = 0; 59 | for (i = 0; i <= 16; i++) 60 | if (buf[i] == ' ') 61 | i0 = i, i1 = i + 1; 62 | if (!i0) 63 | i0 = i1 = 16; 64 | if (i1 + 16 < BUFSZ) /* MODERN: bounds check prevents buffer overflow */ 65 | buf[i1 + 16] = 0; 66 | center(10, buf + i1); 67 | buf[i0] = 0; 68 | } 69 | center(9, buf); 70 | (void)snprintf(buf, BUFSZ, "%4d", 71 | getyear()); /* MODERN: Safe sprintf replacement - identical 72 | output, prevents overflow */ 73 | center(11, buf); 74 | for (y = 8; dp >= 0 && dp < (int)(sizeof(rip)/sizeof(rip[0])) && rip[dp][0]; y++, dp++) { /* MODERN: bounds check prevents array overflow */ 75 | x = 0; 76 | dpx = rip[dp]; 77 | while (dpx[x]) { 78 | while (dpx[x] == ' ') 79 | x++; 80 | curs(x, y); 81 | while (dpx[x] && dpx[x] != ' ') { 82 | extern int done_stopprint; 83 | if (done_stopprint) 84 | return; 85 | curx++; 86 | (void)putchar(dpx[x++]); 87 | } 88 | } 89 | } 90 | getret(); 91 | } 92 | 93 | void center(int line, char *text) { 94 | char *ip, *op; 95 | int offset, max_len, text_len; 96 | ip = text; 97 | text_len = strlen(text); 98 | 99 | /* MODERN ADDITION (2025): Bounds checking to prevent buffer overflow */ 100 | /* Now safe with writable 2D array */ 101 | offset = 28 - ((text_len + 1) / 2); 102 | if (offset < 0) 103 | offset = 0; 104 | if (offset + text_len >= 60) { /* Array width is 60 */ 105 | max_len = 60 - offset - 1; 106 | if (max_len <= 0) 107 | return; 108 | } else { 109 | max_len = text_len; 110 | } 111 | 112 | if (line < 0 || line >= (int)(sizeof(rip)/sizeof(rip[0]))) return; /* MODERN: bounds check prevents line overflow */ 113 | op = &rip[line][offset]; 114 | while (*ip && max_len-- > 0) 115 | *op++ = *ip++; 116 | } 117 | -------------------------------------------------------------------------------- /docs/CODING_STANDARDS.md: -------------------------------------------------------------------------------- 1 | # restoHack Coding Standards 2 | 3 | ## Documentation Policy for Non-Original Code 4 | 5 | This project preserves authentic 1984 Hack source code while making minimal modern additions for buildability and stability. To maintain clear distinction between original and modern code: 6 | 7 | ### Documentation Requirement 8 | 9 | **ALL** code that is not part of the original 1984 codebase or simple K&R to ANSI C conversion MUST be documented with structured comments explaining: 10 | 11 | 1. **WHY** the addition was necessary 12 | 2. **HOW** it was implemented 13 | 3. **WHAT** it preserves from the original 14 | 4. **WHAT** it adds that's modern 15 | 16 | ### Documentation Format 17 | 18 | ```c 19 | /** 20 | * MODERN ADDITION (YYYY): Brief description 21 | * 22 | * WHY: Explanation of the problem that required this addition 23 | * 24 | * HOW: Technical explanation of the implementation approach 25 | * 26 | * PRESERVES: What original 1984 behavior/logic is maintained 27 | * ADDS: What modern functionality or compatibility is provided 28 | */ 29 | ``` 30 | 31 | ### Examples of Code Requiring Documentation 32 | 33 | - **Bug fixes** that add new logic (not just syntax fixes) 34 | - **Defensive programming** additions (error checking, resource cleanup) 35 | - **Compatibility shims** for modern systems 36 | - **New functions** not present in original codebase 37 | - **Modified algorithms** or data structures 38 | - **Added includes** for modern headers not in original 39 | 40 | ### Examples of Code NOT Requiring Documentation 41 | 42 | - **K&R to ANSI C conversion**: Function signature modernization 43 | - **Simple syntax fixes**: Adding void, const keywords 44 | - **Header organization**: Moving declarations to proper files 45 | - **Build system**: CMake, compiler flags, etc. 46 | - **Whitespace/formatting**: Code style consistency 47 | 48 | ### Original Source Preservation Rule 49 | 50 | ### NEVER DELETE ORIGINAL 1984 CODE 51 | 52 | All original source code must be preserved using one of these methods: 53 | 54 | 1. **Comment Preservation** (Preferred): 55 | 56 | ```c 57 | #if 0 58 | /* ORIGINAL 1984 CODE - preserved for reference */ 59 | original_function() { 60 | /* original implementation */ 61 | } 62 | #endif 63 | ``` 64 | 65 | 2. **Inline Comments** (For small changes): 66 | 67 | ```c 68 | /* Original 1984: old_approach(); */ 69 | modern_approach(); /* MODERN: explanation */ 70 | ``` 71 | 72 | 3. **Documentation References**: 73 | 74 | ```c 75 | /* See docs/historical/original-source/filename.c for original implementation */ 76 | ``` 77 | 78 | **Rationale**: Enables future researchers to understand evolution, verify authenticity claims, and potentially revert changes if needed. 79 | 80 | ### Preservation Principle 81 | 82 | The goal is to make it easy for future developers to: 83 | 84 | 1. **Identify authentic 1984 code** vs modern additions 85 | 2. **Understand why** modern additions were necessary 86 | 3. **Evaluate whether** additions could be removed or improved 87 | 4. **Learn from** the historical code while understanding modern adaptations 88 | 89 | ### Existing Documented Additions 90 | 91 | As of August 2025, the following modern additions have been documented: 92 | 93 | 1. **hack.u_init.c**: Mutable role strings for ANSI C compatibility 94 | 2. **hack.end.c**: Stale lock detection and cleanup for robustness 95 | 3. **hack.termcap.c**: delay_output() fallback implementation 96 | 4. **hack.timeout.c**: timeout() renamed to hack_timeout() for curses compatibility 97 | 5. **hack.cmd.c**: unctrl() renamed to hack_unctrl() for curses compatibility 98 | 6. **hack.rip.c**: Mutable tombstone strings for memory safety 99 | 7. **hack.lock.c**: Modern flock()-based locking with 1984 fallback 100 | 101 | ### Original Code Preservation Audit 102 | 103 | **✅ COMPLIANT FILES** (Original code preserved): 104 | 105 | - hack.termcap.c: delay_output() preserved under #if 0 106 | - hack.unix.c: setrandom() and link() locking preserved 107 | - hack.timeout.c: timeout() preserved under #if 0 108 | - hack.cmd.c: unctrl() preserved under #if 0 109 | - hack.end.c: Original locking mechanisms preserved 110 | - hack.ioctl.c: Original sgtty includes preserved in comments 111 | 112 | All future additions should follow this documentation standard to maintain the educational and preservation value of the project. 113 | -------------------------------------------------------------------------------- /docs/historical/READ_ME: -------------------------------------------------------------------------------- 1 | Hack is a display oriented dungeons & dragons - like game. 2 | Both display and command structure resemble rogue. 3 | (For a game with the same structure but entirely different display - 4 | a real cave instead of dull rectangles - try Quest) 5 | 6 | Hack was originally written by Jay Fenlason (at lincolnsudbury: 7 | 29 East St., Sudbury Mass., 01776) with help from 8 | Kenny Woodland, Mike Thome and Jon Payne. 9 | Basically it was an implementation of Rogue, however, with 52+ instead of 26 10 | monster types. 11 | The current version is more than thrice as large (with such new features as 12 | the dog, the long worms, the shops, etc.) and almost entirely rewritten 13 | (only the display routines are the original ones - I must rewrite these 14 | too one day; especially when you are blind strange things still happen). 15 | 16 | Files for hack: 17 | hack The actual game 18 | record Top 100 list (just start with an empty file) 19 | news Tells about recent changes in hack, or bugs found ... 20 | (Just start with no news file.) 21 | data Auxiliary file used by hack to give you the names 22 | and sometimes some more information on the 23 | objects and monsters. 24 | help Introductory information (no doubt outdated). 25 | hh Compactified version of help. 26 | perm An empty file used for locking purposes. 27 | rumors Texts for fortune cookies. 28 | (Some of these contain information on the game, 29 | others are just plain stupid. Additional rumors 30 | are appreciated.) 31 | hack.sh A shell script. 32 | (We have hack.sh in /usr/games/hack and 33 | hack in /usr/games/lib/hackdir/hack and all the other 34 | hack stuff in /usr/games/lib/hackdir - perhaps this 35 | will make the script clear. 36 | There is no need for you to use it.) 37 | READ_ME This file. 38 | Original_READ_ME Jay Fenlason's READ_ME 39 | 40 | System files used: 41 | /etc/termcap Used in conjunction with the environment variable 42 | $TERM. 43 | /bin/cat 44 | /usr/ucb/more 45 | /bin/sh Used when $SHELL is undefined. 46 | 47 | How to install hack: 48 | 0. Compile the sources. Perhaps you should first look at the file config.h 49 | and define BSD if you are on a BSDtype system, 50 | define STUPID if your C-compiler chokes on complicated expressions. 51 | Make sure schar and uchar represent signed and unsigned types. 52 | If your C compiler doesnt allow initialization of bit fields 53 | change Bitfield. When config.h looks reasonable, say 'make'. 54 | (Perhaps you have to change TERMLIB in the makefile.) 55 | 1. If it didnt exist already, introduce a loginname `play' . 56 | 2. The program hack resides in a directory so that it is executable 57 | for everybody and is suid play: 58 | ---s--s--x 1 play 206848 Apr 3 00:17 hack 59 | Perhaps you wish to restrict playing to certain hours, or have games 60 | running under nice; in that case you might write a program play.c 61 | such that the program play is suid play and executable for everybody 62 | while all the games in /usr/games are readable or executable for 63 | play only; all the program play does is asking for the name of a game, 64 | checking that time-of-day and system load do not forbid playing, 65 | and then executing the game. Thus: 66 | -r-sr-sr-x 1 play 13312 May 24 12:52 play 67 | ---x------ 1 play 206848 Apr 3 00:17 hack 68 | If you are worried about security you might let play do 69 | chroot("/usr/games") so that no player can get access to the rest 70 | of the system via shell escapes and the likes. 71 | If you #define SECURE in config.h then hack will not setuid(getuid()) 72 | before executing a chdir(). Hack will always do setuid(getuid()) with 73 | a fork. If you do not define UNIX then hack will not fork. 74 | 3. The rest of the stuff belonging to hack sits in a subdirectory hackdir 75 | (on our system /usr/games/lib/hackdir) with modes 76 | drwx------ 3 play 1024 Aug 9 09:03 hackdir 77 | Here all the temporary files will be created (with names like xlock.17 78 | or user.5). 79 | 4. If you are not really short on file space, creating a subdirectory 80 | hackdir/save (modes again drwx------) will enable users to save their 81 | unfinished games. 82 | 83 | The program hack is called 84 | $ hack [-d hackdir] [maxnrofplayers] 85 | (for playing) or 86 | $ hack [-d hackdir] -s [listofusers | limit | all] 87 | (for seeing part of the scorelist). 88 | The shell file hack (in this kit called hack.sh) takes care of 89 | calling hack with the right arguments. 90 | 91 | Send complaints, bug reports, suggestions for improvements to 92 | mcvax!aeb - in real life Andries Brouwer. 93 | -------------------------------------------------------------------------------- /docs/historical/original-source/READ_ME: -------------------------------------------------------------------------------- 1 | Hack is a display oriented dungeons & dragons - like game. 2 | Both display and command structure resemble rogue. 3 | (For a game with the same structure but entirely different display - 4 | a real cave instead of dull rectangles - try Quest) 5 | 6 | Hack was originally written by Jay Fenlason (at lincolnsudbury: 7 | 29 East St., Sudbury Mass., 01776) with help from 8 | Kenny Woodland, Mike Thome and Jon Payne. 9 | Basically it was an implementation of Rogue, however, with 52+ instead of 26 10 | monster types. 11 | The current version is more than thrice as large (with such new features as 12 | the dog, the long worms, the shops, etc.) and almost entirely rewritten 13 | (only the display routines are the original ones - I must rewrite these 14 | too one day; especially when you are blind strange things still happen). 15 | 16 | Files for hack: 17 | hack The actual game 18 | record Top 100 list (just start with an empty file) 19 | news Tells about recent changes in hack, or bugs found ... 20 | (Just start with no news file.) 21 | data Auxiliary file used by hack to give you the names 22 | and sometimes some more information on the 23 | objects and monsters. 24 | help Introductory information (no doubt outdated). 25 | hh Compactified version of help. 26 | perm An empty file used for locking purposes. 27 | rumors Texts for fortune cookies. 28 | (Some of these contain information on the game, 29 | others are just plain stupid. Additional rumors 30 | are appreciated.) 31 | hack.sh A shell script. 32 | (We have hack.sh in /usr/games/hack and 33 | hack in /usr/games/lib/hackdir/hack and all the other 34 | hack stuff in /usr/games/lib/hackdir - perhaps this 35 | will make the script clear. 36 | There is no need for you to use it.) 37 | READ_ME This file. 38 | Original_READ_ME Jay Fenlason's READ_ME 39 | 40 | System files used: 41 | /etc/termcap Used in conjunction with the environment variable 42 | $TERM. 43 | /bin/cat 44 | /usr/ucb/more 45 | /bin/sh Used when $SHELL is undefined. 46 | 47 | How to install hack: 48 | 0. Compile the sources. Perhaps you should first look at the file config.h 49 | and define BSD if you are on a BSDtype system, 50 | define STUPID if your C-compiler chokes on complicated expressions. 51 | Make sure schar and uchar represent signed and unsigned types. 52 | If your C compiler doesnt allow initialization of bit fields 53 | change Bitfield. When config.h looks reasonable, say 'make'. 54 | (Perhaps you have to change TERMLIB in the makefile.) 55 | 1. If it didnt exist already, introduce a loginname `play' . 56 | 2. The program hack resides in a directory so that it is executable 57 | for everybody and is suid play: 58 | ---s--s--x 1 play 206848 Apr 3 00:17 hack 59 | Perhaps you wish to restrict playing to certain hours, or have games 60 | running under nice; in that case you might write a program play.c 61 | such that the program play is suid play and executable for everybody 62 | while all the games in /usr/games are readable or executable for 63 | play only; all the program play does is asking for the name of a game, 64 | checking that time-of-day and system load do not forbid playing, 65 | and then executing the game. Thus: 66 | -r-sr-sr-x 1 play 13312 May 24 12:52 play 67 | ---x------ 1 play 206848 Apr 3 00:17 hack 68 | If you are worried about security you might let play do 69 | chroot("/usr/games") so that no player can get access to the rest 70 | of the system via shell escapes and the likes. 71 | If you #define SECURE in config.h then hack will not setuid(getuid()) 72 | before executing a chdir(). Hack will always do setuid(getuid()) with 73 | a fork. If you do not define UNIX then hack will not fork. 74 | 3. The rest of the stuff belonging to hack sits in a subdirectory hackdir 75 | (on our system /usr/games/lib/hackdir) with modes 76 | drwx------ 3 play 1024 Aug 9 09:03 hackdir 77 | Here all the temporary files will be created (with names like xlock.17 78 | or user.5). 79 | 4. If you are not really short on file space, creating a subdirectory 80 | hackdir/save (modes again drwx------) will enable users to save their 81 | unfinished games. 82 | 83 | The program hack is called 84 | $ hack [-d hackdir] [maxnrofplayers] 85 | (for playing) or 86 | $ hack [-d hackdir] -s [listofusers | limit | all] 87 | (for seeing part of the scorelist). 88 | The shell file hack (in this kit called hack.sh) takes care of 89 | calling hack with the right arguments. 90 | 91 | Send complaints, bug reports, suggestions for improvements to 92 | mcvax!aeb - in real life Andries Brouwer. 93 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.o_init.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.o_init.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include 6 | #include "config.h" /* for typedefs */ 7 | #include "def.objects.h" 8 | #include "hack.onames.h" /* for LAST_GEM */ 9 | extern char *index(); 10 | 11 | int 12 | letindex(let) char let; { 13 | int i = 0; 14 | char ch; 15 | while((ch = obj_symbols[i++]) != 0) 16 | if(ch == let) return(i); 17 | return(0); 18 | } 19 | 20 | init_objects(){ 21 | int i, j, first, last, sum, end; 22 | char let, *tmp; 23 | /* init base; if probs given check that they add up to 100, 24 | otherwise compute probs; shuffle descriptions */ 25 | end = SIZE(objects); 26 | first = 0; 27 | while( first < end ) { 28 | let = objects[first].oc_olet; 29 | last = first+1; 30 | while(last < end && objects[last].oc_olet == let 31 | && objects[last].oc_name != NULL) 32 | last++; 33 | i = letindex(let); 34 | if((!i && let != ILLOBJ_SYM) || bases[i] != 0) 35 | error("initialization error"); 36 | bases[i] = first; 37 | 38 | if(let == GEM_SYM) 39 | setgemprobs(); 40 | check: 41 | sum = 0; 42 | for(j = first; j < last; j++) sum += objects[j].oc_prob; 43 | if(sum == 0) { 44 | for(j = first; j < last; j++) 45 | objects[j].oc_prob = (100+j-first)/(last-first); 46 | goto check; 47 | } 48 | if(sum != 100) 49 | error("init-prob error for %c", let); 50 | 51 | if(objects[first].oc_descr != NULL && let != TOOL_SYM){ 52 | /* shuffle, also some additional descriptions */ 53 | while(last < end && objects[last].oc_olet == let) 54 | last++; 55 | j = last; 56 | while(--j > first) { 57 | i = first + rn2(j+1-first); 58 | tmp = objects[j].oc_descr; 59 | objects[j].oc_descr = objects[i].oc_descr; 60 | objects[i].oc_descr = tmp; 61 | } 62 | } 63 | first = last; 64 | } 65 | } 66 | 67 | probtype(let) char let; { 68 | int i = bases[letindex(let)]; 69 | int prob = rn2(100); 70 | while((prob -= objects[i].oc_prob) >= 0) i++; 71 | if(objects[i].oc_olet != let || !objects[i].oc_name) 72 | panic("probtype(%c) error, i=%d", let, i); 73 | return(i); 74 | } 75 | 76 | setgemprobs() 77 | { 78 | int j,first; 79 | extern xchar dlevel; 80 | 81 | first = bases[letindex(GEM_SYM)]; 82 | 83 | for(j = 0; j < 9-dlevel/3; j++) 84 | objects[first+j].oc_prob = 0; 85 | first += j; 86 | if(first >= LAST_GEM || first >= SIZE(objects) || 87 | objects[first].oc_olet != GEM_SYM || 88 | objects[first].oc_name == NULL) 89 | printf("Not enough gems? - first=%d j=%d LAST_GEM=%d\n", 90 | first, j, LAST_GEM); 91 | for(j = first; j < LAST_GEM; j++) 92 | objects[j].oc_prob = (20+j-first)/(LAST_GEM-first); 93 | } 94 | 95 | oinit() /* level dependent initialization */ 96 | { 97 | setgemprobs(); 98 | } 99 | 100 | extern long *alloc(); 101 | 102 | savenames(fd) int fd; { 103 | int i; 104 | unsigned len; 105 | bwrite(fd, (char *) bases, sizeof bases); 106 | bwrite(fd, (char *) objects, sizeof objects); 107 | /* as long as we use only one version of Hack/Quest we 108 | need not save oc_name and oc_descr, but we must save 109 | oc_uname for all objects */ 110 | for(i=0; i < SIZE(objects); i++) { 111 | if(objects[i].oc_uname) { 112 | len = strlen(objects[i].oc_uname)+1; 113 | bwrite(fd, (char *) &len, sizeof len); 114 | bwrite(fd, objects[i].oc_uname, len); 115 | } 116 | } 117 | } 118 | 119 | restnames(fd) int fd; { 120 | int i; 121 | unsigned len; 122 | mread(fd, (char *) bases, sizeof bases); 123 | mread(fd, (char *) objects, sizeof objects); 124 | for(i=0; i < SIZE(objects); i++) if(objects[i].oc_uname) { 125 | mread(fd, (char *) &len, sizeof len); 126 | objects[i].oc_uname = (char *) alloc(len); 127 | mread(fd, objects[i].oc_uname, len); 128 | } 129 | } 130 | 131 | dodiscovered() /* free after Robert Viduya */ 132 | { 133 | extern char *typename(); 134 | int i, end; 135 | int ct = 0; 136 | 137 | cornline(0, "Discoveries"); 138 | 139 | end = SIZE(objects); 140 | for (i = 0; i < end; i++) { 141 | if (interesting_to_discover (i)) { 142 | ct++; 143 | cornline(1, typename(i)); 144 | } 145 | } 146 | if (ct == 0) { 147 | pline ("You haven't discovered anything yet..."); 148 | cornline(3, (char *) 0); 149 | } else 150 | cornline(2, (char *) 0); 151 | 152 | return(0); 153 | } 154 | 155 | interesting_to_discover(i) 156 | int i; 157 | { 158 | return( 159 | objects[i].oc_uname != NULL || 160 | (objects[i].oc_name_known && objects[i].oc_descr != NULL) 161 | ); 162 | } 163 | -------------------------------------------------------------------------------- /man/hack.6: -------------------------------------------------------------------------------- 1 | .\" $FreeBSD$ 2 | .TH HACK 6 "31 March 1985" 3 | .UC 4 4 | .SH NAME 5 | hack \- exploring The Dungeons of Doom 6 | .SH SYNOPSIS 7 | .B /usr/games/hack 8 | [ 9 | .B \-d 10 | .I directory 11 | ] 12 | [ 13 | .B \-n 14 | ] 15 | [ 16 | .B \-u 17 | .I playername 18 | ] 19 | .br 20 | .B /usr/games/hack 21 | [ 22 | .B \-d 23 | .I directory 24 | ] 25 | .B \-s 26 | [ 27 | .B \-X 28 | ] 29 | [ 30 | .I playernames 31 | ] 32 | .SH DESCRIPTION 33 | .I Hack 34 | is a display oriented dungeons & dragons - like game. 35 | Both display and command structure resemble rogue. 36 | (For a game with the same structure but entirely different display - 37 | a real cave instead of dull rectangles - try Quest.) 38 | .PP 39 | To get started you really only need to know two commands. The command 40 | .B ? 41 | will give you a list of the available commands and the command 42 | .B / 43 | will identify the things you see on the screen. 44 | .PP 45 | To win the game (as opposed to merely playing to beat other people high 46 | scores) you must locate the Amulet of Yendor which is somewhere below 47 | the 20th level of the dungeon and get it out. Nobody has achieved this 48 | yet and if somebody does, he will probably go down in history as a hero 49 | among heroes. 50 | .PP 51 | When the game ends, either by your death, when you quit, or if you escape 52 | from the caves, 53 | .I hack 54 | will give you (a fragment of) the list of top scorers. The scoring 55 | is based on many aspects of your behavior but a rough estimate is 56 | obtained by taking the amount of gold you've found in the cave plus four 57 | times your (real) experience. Precious stones may be worth a lot of gold 58 | when brought to the exit. 59 | There is a 10% penalty for getting yourself killed. 60 | .PP 61 | The administration of the game is kept in the directory specified with the 62 | .B \-d 63 | option, or, if no such option is given, in the directory specified by 64 | the environment variable HACKDIR, or, if no such variable exists, in 65 | the current directory. This same directory contains several auxiliary 66 | files such as lockfiles and the list of top scorers and a subdirectory 67 | .I save 68 | where games are saved. 69 | The game administrator may however choose to install hack with a fixed 70 | playing ground, usually /usr/games/lib/hackdir. 71 | .PP 72 | The 73 | .B \-n 74 | option suppresses printing of the news. 75 | .PP 76 | The 77 | .B \-u 78 | .I playername 79 | option supplies the answer to the question "Who are you?". 80 | When 81 | .I playername 82 | has as suffix one of 83 | .B \-T \-S \-K \-F \-C \-W 84 | then this supplies the answer to the question "What kind of character ... ?". 85 | .PP 86 | The 87 | .B \-s 88 | option will print out the list of your scores. It may be followed by arguments 89 | .B \-X 90 | where X is one of the letters C, F, K, S, T, W to print the scores of 91 | Cavemen, Fighters, Knights, Speleologists, Tourists or Wizards. 92 | It may also be followed by one or more player names to print the scores of the 93 | players mentioned. 94 | .SH AUTHORS 95 | Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the 96 | original hack, very much like rogue (but full of bugs). 97 | .br 98 | Andries Brouwer continuously deformed their sources into the current 99 | version - in fact an entirely different game. 100 | .SH FILES 101 | .DT 102 | .ta \w'data, rumors\ \ \ 'u 103 | hack The hack program. 104 | .br 105 | data, rumors Data files used by hack. 106 | .br 107 | help, hh Help data files. 108 | .br 109 | record The list of top scorers. 110 | .br 111 | save A subdirectory containing the saved 112 | .br 113 | games. 114 | .br 115 | bones_dd Descriptions of the ghost and 116 | .br 117 | belongings of a deceased adventurer. 118 | .br 119 | xlock.dd Description of a dungeon level. 120 | .br 121 | safelock Lock file for xlock. 122 | .br 123 | record_lock Lock file for record. 124 | .SH ENVIRONMENT 125 | .DT 126 | .ta \w'HACKPAGER, PAGER\ \ \ 'u 127 | USER or LOGNAME Your login name. 128 | .br 129 | HOME Your home directory. 130 | .br 131 | SHELL Your shell. 132 | .br 133 | TERM The type of your terminal. 134 | .br 135 | HACKPAGER, PAGER Pager used instead of default pager. 136 | .br 137 | MAIL Mailbox file. 138 | .br 139 | MAILREADER Reader used instead of default. 140 | .br 141 | HACKDIR Playground. 142 | .br 143 | HACKOPTIONS String predefining several hack options 144 | .br 145 | (see help file). 146 | .br 147 | 148 | Several other environment variables are used in debugging (wizard) mode, 149 | like GENOCIDED, INVENT, MAGIC and SHOPTYPE. 150 | .SH BUGS 151 | Probably infinite. 152 | Mail complaints to mcvax!aeb . 153 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.6: -------------------------------------------------------------------------------- 1 | .\" $FreeBSD$ 2 | .TH HACK 6 "31 March 1985" 3 | .UC 4 4 | .SH NAME 5 | hack \- exploring The Dungeons of Doom 6 | .SH SYNOPSIS 7 | .B /usr/games/hack 8 | [ 9 | .B \-d 10 | .I directory 11 | ] 12 | [ 13 | .B \-n 14 | ] 15 | [ 16 | .B \-u 17 | .I playername 18 | ] 19 | .br 20 | .B /usr/games/hack 21 | [ 22 | .B \-d 23 | .I directory 24 | ] 25 | .B \-s 26 | [ 27 | .B \-X 28 | ] 29 | [ 30 | .I playernames 31 | ] 32 | .SH DESCRIPTION 33 | .I Hack 34 | is a display oriented dungeons & dragons - like game. 35 | Both display and command structure resemble rogue. 36 | (For a game with the same structure but entirely different display - 37 | a real cave instead of dull rectangles - try Quest.) 38 | .PP 39 | To get started you really only need to know two commands. The command 40 | .B ? 41 | will give you a list of the available commands and the command 42 | .B / 43 | will identify the things you see on the screen. 44 | .PP 45 | To win the game (as opposed to merely playing to beat other people high 46 | scores) you must locate the Amulet of Yendor which is somewhere below 47 | the 20th level of the dungeon and get it out. Nobody has achieved this 48 | yet and if somebody does, he will probably go down in history as a hero 49 | among heroes. 50 | .PP 51 | When the game ends, either by your death, when you quit, or if you escape 52 | from the caves, 53 | .I hack 54 | will give you (a fragment of) the list of top scorers. The scoring 55 | is based on many aspects of your behavior but a rough estimate is 56 | obtained by taking the amount of gold you've found in the cave plus four 57 | times your (real) experience. Precious stones may be worth a lot of gold 58 | when brought to the exit. 59 | There is a 10% penalty for getting yourself killed. 60 | .PP 61 | The administration of the game is kept in the directory specified with the 62 | .B \-d 63 | option, or, if no such option is given, in the directory specified by 64 | the environment variable HACKDIR, or, if no such variable exists, in 65 | the current directory. This same directory contains several auxiliary 66 | files such as lockfiles and the list of top scorers and a subdirectory 67 | .I save 68 | where games are saved. 69 | The game administrator may however choose to install hack with a fixed 70 | playing ground, usually /usr/games/lib/hackdir. 71 | .PP 72 | The 73 | .B \-n 74 | option suppresses printing of the news. 75 | .PP 76 | The 77 | .B \-u 78 | .I playername 79 | option supplies the answer to the question "Who are you?". 80 | When 81 | .I playername 82 | has as suffix one of 83 | .B \-T \-S \-K \-F \-C \-W 84 | then this supplies the answer to the question "What kind of character ... ?". 85 | .PP 86 | The 87 | .B \-s 88 | option will print out the list of your scores. It may be followed by arguments 89 | .B \-X 90 | where X is one of the letters C, F, K, S, T, W to print the scores of 91 | Cavemen, Fighters, Knights, Speleologists, Tourists or Wizards. 92 | It may also be followed by one or more player names to print the scores of the 93 | players mentioned. 94 | .SH AUTHORS 95 | Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the 96 | original hack, very much like rogue (but full of bugs). 97 | .br 98 | Andries Brouwer continuously deformed their sources into the current 99 | version - in fact an entirely different game. 100 | .SH FILES 101 | .DT 102 | .ta \w'data, rumors\ \ \ 'u 103 | hack The hack program. 104 | .br 105 | data, rumors Data files used by hack. 106 | .br 107 | help, hh Help data files. 108 | .br 109 | record The list of top scorers. 110 | .br 111 | save A subdirectory containing the saved 112 | .br 113 | games. 114 | .br 115 | bones_dd Descriptions of the ghost and 116 | .br 117 | belongings of a deceased adventurer. 118 | .br 119 | xlock.dd Description of a dungeon level. 120 | .br 121 | safelock Lock file for xlock. 122 | .br 123 | record_lock Lock file for record. 124 | .SH ENVIRONMENT 125 | .DT 126 | .ta \w'HACKPAGER, PAGER\ \ \ 'u 127 | USER or LOGNAME Your login name. 128 | .br 129 | HOME Your home directory. 130 | .br 131 | SHELL Your shell. 132 | .br 133 | TERM The type of your terminal. 134 | .br 135 | HACKPAGER, PAGER Pager used instead of default pager. 136 | .br 137 | MAIL Mailbox file. 138 | .br 139 | MAILREADER Reader used instead of default. 140 | .br 141 | HACKDIR Playground. 142 | .br 143 | HACKOPTIONS String predefining several hack options 144 | .br 145 | (see help file). 146 | .br 147 | 148 | Several other environment variables are used in debugging (wizard) mode, 149 | like GENOCIDED, INVENT, MAGIC and SHOPTYPE. 150 | .SH BUGS 151 | Probably infinite. 152 | Mail complaints to mcvax!aeb . 153 | -------------------------------------------------------------------------------- /src/hack.mkmaze.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.mkmaze.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | /* 6 | * Maze generation system for 1984 Hack - labyrinth creation algorithms 7 | * Original 1984 source: docs/historical/original-source/hack.mkmaze.c 8 | * 9 | * Key modernizations: ANSI C function signatures 10 | */ 11 | 12 | #include "hack.h" 13 | extern struct permonst pm_wizard; 14 | extern coord mazexy(); 15 | 16 | /* Forward declarations for local functions */ 17 | void walkfrom(int x, int y); 18 | void move(int *x, int *y, int dir); 19 | int okay(int x, int y, int dir); 20 | struct permonst hell_hound = {"hell hound", 'd', 12, 14, 2, 3, 6, 0}; 21 | 22 | void makemaz(void) { 23 | int x, y; 24 | int zx, zy; 25 | coord mm; 26 | boolean al = (dlevel >= 30 && !flags.made_amulet); 27 | 28 | for (x = 2; x < COLNO - 1; x++) 29 | for (y = 2; y < ROWNO - 1; y++) 30 | levl[x][y].typ = (x % 2 && y % 2) ? 0 : HWALL; 31 | if (al) { 32 | struct monst *mtmp; 33 | 34 | zx = 2 * (COLNO / 4) - 1; 35 | zy = 2 * (ROWNO / 4) - 1; 36 | for (x = zx - 2; x < zx + 4; x++) 37 | for (y = zy - 2; y <= zy + 2; y++) { 38 | if (!isok(x, y)) continue; /* MODERN: bounds check prevents OOB access to levl[][] array */ 39 | levl[x][y].typ = 40 | (y == zy - 2 || y == zy + 2 || x == zx - 2 || x == zx + 3) ? POOL 41 | : (y == zy - 1 || y == zy + 1 || x == zx - 1 || x == zx + 2) ? HWALL 42 | : ROOM; 43 | } 44 | (void)mkobj_at(AMULET_SYM, zx, zy); 45 | flags.made_amulet = 1; 46 | walkfrom(zx + 4, zy); 47 | if ((mtmp = makemon(&hell_hound, zx, zy))) 48 | mtmp->msleep = 1; 49 | if ((mtmp = makemon(PM_WIZARD, zx + 1, zy))) { 50 | mtmp->msleep = 1; 51 | flags.no_of_wizards = 1; 52 | } 53 | } else { 54 | mm = mazexy(); 55 | zx = mm.x; 56 | zy = mm.y; 57 | walkfrom(zx, zy); 58 | (void)mksobj_at(WAN_WISHING, zx, zy); 59 | (void)mkobj_at(ROCK_SYM, zx, zy); /* put a rock on top of it */ 60 | } 61 | 62 | for (x = 2; x < COLNO - 1; x++) 63 | for (y = 2; y < ROWNO - 1; y++) { 64 | switch (levl[x][y].typ) { 65 | case HWALL: 66 | levl[x][y].scrsym = '-'; 67 | break; 68 | case ROOM: 69 | levl[x][y].scrsym = '.'; 70 | break; 71 | } 72 | } 73 | for (x = rn1(8, 11); x; x--) { 74 | mm = mazexy(); 75 | (void)mkobj_at(rn2(2) ? GEM_SYM : 0, mm.x, mm.y); 76 | } 77 | for (x = rn1(10, 2); x; x--) { 78 | mm = mazexy(); 79 | (void)mkobj_at(ROCK_SYM, mm.x, mm.y); 80 | } 81 | mm = mazexy(); 82 | (void)makemon(PM_MINOTAUR, mm.x, mm.y); 83 | for (x = rn1(5, 7); x; x--) { 84 | mm = mazexy(); 85 | (void)makemon((struct permonst *)0, mm.x, mm.y); 86 | } 87 | for (x = rn1(6, 7); x; x--) { 88 | mm = mazexy(); 89 | mkgold(0L, mm.x, mm.y); 90 | } 91 | for (x = rn1(6, 7); x; x--) 92 | mktrap(0, 1, (struct mkroom *)0); 93 | mm = mazexy(); 94 | if (isok(mm.x, mm.y)) { /* MODERN: bounds check prevents OOB access to levl[][] array */ 95 | xupstair = mm.x; 96 | yupstair = mm.y; 97 | levl[xupstair][yupstair].scrsym = '<'; 98 | levl[xupstair][yupstair].typ = STAIRS; 99 | } 100 | xdnstair = ydnstair = 0; 101 | } 102 | 103 | void walkfrom(int x, int y) { 104 | int q, a, dir; 105 | int dirs[4]; 106 | if (!isok(x, y)) return; /* MODERN: bounds check prevents OOB access to levl[][] array */ 107 | levl[x][y].typ = ROOM; 108 | while (1) { 109 | q = 0; 110 | for (a = 0; a < 4; a++) 111 | if (okay(x, y, a)) 112 | dirs[q++] = a; 113 | if (!q) 114 | return; 115 | dir = dirs[rn2(q)]; 116 | move(&x, &y, dir); 117 | if (!isok(x, y)) return; /* MODERN: bounds check prevents OOB access to levl[][] array */ 118 | levl[x][y].typ = ROOM; 119 | move(&x, &y, dir); 120 | walkfrom(x, y); 121 | } 122 | } 123 | 124 | void move(int *x, int *y, int dir) { 125 | switch (dir) { 126 | case 0: 127 | --(*y); 128 | break; 129 | case 1: 130 | (*x)++; 131 | break; 132 | case 2: 133 | (*y)++; 134 | break; 135 | case 3: 136 | --(*x); 137 | break; 138 | } 139 | } 140 | 141 | int okay(int x, int y, int dir) { 142 | move(&x, &y, dir); 143 | move(&x, &y, dir); 144 | if (x < 3 || y < 3 || x > COLNO - 3 || y > ROWNO - 3 || levl[x][y].typ != 0) 145 | return (0); 146 | else 147 | return (1); 148 | } 149 | 150 | coord mazexy() { 151 | coord mm; 152 | mm.x = 3 + 2 * rn2(COLNO / 2 - 2); 153 | mm.y = 3 + 2 * rn2(ROWNO / 2 - 2); 154 | return mm; 155 | } 156 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.topl.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.topl.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | #include 7 | extern char *eos(); 8 | extern int CO; 9 | 10 | char toplines[BUFSZ]; 11 | xchar tlx, tly; /* set by pline; used by addtopl */ 12 | 13 | struct topl { 14 | struct topl *next_topl; 15 | char *topl_text; 16 | } *old_toplines, *last_redone_topl; 17 | #define OTLMAX 20 /* max nr of old toplines remembered */ 18 | 19 | doredotopl(){ 20 | if(last_redone_topl) 21 | last_redone_topl = last_redone_topl->next_topl; 22 | if(!last_redone_topl) 23 | last_redone_topl = old_toplines; 24 | if(last_redone_topl){ 25 | (void) strcpy(toplines, last_redone_topl->topl_text); 26 | } 27 | redotoplin(); 28 | return(0); 29 | } 30 | 31 | redotoplin() { 32 | home(); 33 | if(index(toplines, '\n')) cl_end(); 34 | putstr(toplines); 35 | cl_end(); 36 | tlx = curx; 37 | tly = cury; 38 | flags.toplin = 1; 39 | if(tly > 1) 40 | more(); 41 | } 42 | 43 | remember_topl() { 44 | struct topl *tl; 45 | int cnt = OTLMAX; 46 | if(last_redone_topl && 47 | !strcmp(toplines, last_redone_topl->topl_text)) return; 48 | if(old_toplines && 49 | !strcmp(toplines, old_toplines->topl_text)) return; 50 | last_redone_topl = 0; 51 | tl = (struct topl *) 52 | alloc((unsigned)(strlen(toplines) + sizeof(struct topl) + 1)); 53 | tl->next_topl = old_toplines; 54 | tl->topl_text = (char *)(tl + 1); 55 | (void) strcpy(tl->topl_text, toplines); 56 | old_toplines = tl; 57 | while(cnt && tl){ 58 | cnt--; 59 | tl = tl->next_topl; 60 | } 61 | if(tl && tl->next_topl){ 62 | free((char *) tl->next_topl); 63 | tl->next_topl = 0; 64 | } 65 | } 66 | 67 | addtopl(s) char *s; { 68 | curs(tlx,tly); 69 | if(tlx + strlen(s) > CO) putsym('\n'); 70 | putstr(s); 71 | tlx = curx; 72 | tly = cury; 73 | flags.toplin = 1; 74 | } 75 | 76 | xmore(s) 77 | char *s; /* allowed chars besides space/return */ 78 | { 79 | if(flags.toplin) { 80 | curs(tlx, tly); 81 | if(tlx + 8 > CO) putsym('\n'), tly++; 82 | } 83 | 84 | if(flags.standout) 85 | standoutbeg(); 86 | putstr("--More--"); 87 | if(flags.standout) 88 | standoutend(); 89 | 90 | xwaitforspace(s); 91 | if(flags.toplin && tly > 1) { 92 | home(); 93 | cl_end(); 94 | docorner(1, tly-1); 95 | } 96 | flags.toplin = 0; 97 | } 98 | 99 | more(){ 100 | xmore(""); 101 | } 102 | 103 | cmore(s) 104 | char *s; 105 | { 106 | xmore(s); 107 | } 108 | 109 | clrlin(){ 110 | if(flags.toplin) { 111 | home(); 112 | cl_end(); 113 | if(tly > 1) docorner(1, tly-1); 114 | remember_topl(); 115 | } 116 | flags.toplin = 0; 117 | } 118 | 119 | /*VARARGS1*/ 120 | pline(line,arg1,arg2,arg3,arg4,arg5,arg6) 121 | char *line,*arg1,*arg2,*arg3,*arg4,*arg5,*arg6; 122 | { 123 | char pbuf[BUFSZ]; 124 | char *bp = pbuf, *tl; 125 | int n,n0; 126 | 127 | if(!line || !*line) return; 128 | if(!index(line, '%')) (void) strcpy(pbuf,line); else 129 | (void) sprintf(pbuf,line,arg1,arg2,arg3,arg4,arg5,arg6); 130 | if(flags.toplin == 1 && !strcmp(pbuf, toplines)) return; 131 | nscr(); /* %% */ 132 | 133 | /* If there is room on the line, print message on same line */ 134 | /* But messages like "You die..." deserve their own line */ 135 | n0 = strlen(bp); 136 | if(flags.toplin == 1 && tly == 1 && 137 | n0 + strlen(toplines) + 3 < CO-8 && /* leave room for --More-- */ 138 | strncmp(bp, "You ", 4)) { 139 | (void) strcat(toplines, " "); 140 | (void) strcat(toplines, bp); 141 | tlx += 2; 142 | addtopl(bp); 143 | return; 144 | } 145 | if(flags.toplin == 1) more(); 146 | remember_topl(); 147 | toplines[0] = 0; 148 | while(n0){ 149 | if(n0 >= CO){ 150 | /* look for appropriate cut point */ 151 | n0 = 0; 152 | for(n = 0; n < CO; n++) if(bp[n] == ' ') 153 | n0 = n; 154 | if(!n0) for(n = 0; n < CO-1; n++) 155 | if(!letter(bp[n])) n0 = n; 156 | if(!n0) n0 = CO-2; 157 | } 158 | (void) strncpy((tl = eos(toplines)), bp, n0); 159 | tl[n0] = 0; 160 | bp += n0; 161 | 162 | /* remove trailing spaces, but leave one */ 163 | while(n0 > 1 && tl[n0-1] == ' ' && tl[n0-2] == ' ') 164 | tl[--n0] = 0; 165 | 166 | n0 = strlen(bp); 167 | if(n0 && tl[0]) (void) strcat(tl, "\n"); 168 | } 169 | redotoplin(); 170 | } 171 | 172 | putsym(c) char c; { 173 | switch(c) { 174 | case '\b': 175 | backsp(); 176 | return; 177 | case '\n': 178 | curx = 1; 179 | cury++; 180 | if(cury > tly) tly = cury; 181 | break; 182 | default: 183 | if(curx == CO) 184 | putsym('\n'); /* 1 <= curx <= CO; avoid CO */ 185 | else 186 | curx++; 187 | } 188 | (void) putchar(c); 189 | } 190 | 191 | putstr(s) char *s; { 192 | while(*s) putsym(*s++); 193 | } 194 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.shknam.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.shknam.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | char *shkliquors[] = { 8 | /* Ukraine */ 9 | "Njezjin", "Tsjernigof", "Gomel", "Ossipewsk", "Gorlowka", 10 | /* N. Russia */ 11 | "Konosja", "Weliki Oestjoeg", "Syktywkar", "Sablja", 12 | "Narodnaja", "Kyzyl", 13 | /* Silezie */ 14 | "Walbrzych", "Swidnica", "Klodzko", "Raciborz", "Gliwice", 15 | "Brzeg", "Krnov", "Hradec Kralove", 16 | /* Schweiz */ 17 | "Leuk", "Brig", "Brienz", "Thun", "Sarnen", "Burglen", "Elm", 18 | "Flims", "Vals", "Schuls", "Zum Loch", 19 | 0 20 | }; 21 | 22 | char *shkbooks[] = { 23 | /* Eire */ 24 | "Skibbereen", "Kanturk", "Rath Luirc", "Ennistymon", "Lahinch", 25 | "Loughrea", "Croagh", "Maumakeogh", "Ballyjamesduff", 26 | "Kinnegad", "Lugnaquillia", "Enniscorthy", "Gweebarra", 27 | "Kittamagh", "Nenagh", "Sneem", "Ballingeary", "Kilgarvan", 28 | "Cahersiveen", "Glenbeigh", "Kilmihil", "Kiltamagh", 29 | "Droichead Atha", "Inniscrone", "Clonegal", "Lisnaskea", 30 | "Culdaff", "Dunfanaghy", "Inishbofin", "Kesh", 31 | 0 32 | }; 33 | 34 | char *shkarmors[] = { 35 | /* Turquie */ 36 | "Demirci", "Kalecik", "Boyabai", "Yildizeli", "Gaziantep", 37 | "Siirt", "Akhalataki", "Tirebolu", "Aksaray", "Ermenak", 38 | "Iskenderun", "Kadirli", "Siverek", "Pervari", "Malasgirt", 39 | "Bayburt", "Ayancik", "Zonguldak", "Balya", "Tefenni", 40 | "Artvin", "Kars", "Makharadze", "Malazgirt", "Midyat", 41 | "Birecik", "Kirikkale", "Alaca", "Polatli", "Nallihan", 42 | 0 43 | }; 44 | 45 | char *shkwands[] = { 46 | /* Wales */ 47 | "Yr Wyddgrug", "Trallwng", "Mallwyd", "Pontarfynach", 48 | "Rhaeader", "Llandrindod", "Llanfair-ym-muallt", 49 | "Y-Fenni", "Measteg", "Rhydaman", "Beddgelert", 50 | "Curig", "Llanrwst", "Llanerchymedd", "Caergybi", 51 | /* Scotland */ 52 | "Nairn", "Turriff", "Inverurie", "Braemar", "Lochnagar", 53 | "Kerloch", "Beinn a Ghlo", "Drumnadrochit", "Morven", 54 | "Uist", "Storr", "Sgurr na Ciche", "Cannich", "Gairloch", 55 | "Kyleakin", "Dunvegan", 56 | 0 57 | }; 58 | 59 | char *shkrings[] = { 60 | /* Hollandse familienamen */ 61 | "Feyfer", "Flugi", "Gheel", "Havic", "Haynin", "Hoboken", 62 | "Imbyze", "Juyn", "Kinsky", "Massis", "Matray", "Moy", 63 | "Olycan", "Sadelin", "Svaving", "Tapper", "Terwen", "Wirix", 64 | "Ypey", 65 | /* Skandinaviske navne */ 66 | "Rastegaisa", "Varjag Njarga", "Kautekeino", "Abisko", 67 | "Enontekis", "Rovaniemi", "Avasaksa", "Haparanda", 68 | "Lulea", "Gellivare", "Oeloe", "Kajaani", "Fauske", 69 | 0 70 | }; 71 | 72 | char *shkfoods[] = { 73 | /* Indonesia */ 74 | "Djasinga", "Tjibarusa", "Tjiwidej", "Pengalengan", 75 | "Bandjar", "Parbalingga", "Bojolali", "Sarangan", 76 | "Ngebel", "Djombang", "Ardjawinangun", "Berbek", 77 | "Papar", "Baliga", "Tjisolok", "Siboga", "Banjoewangi", 78 | "Trenggalek", "Karangkobar", "Njalindoeng", "Pasawahan", 79 | "Pameunpeuk", "Patjitan", "Kediri", "Pemboeang", "Tringanoe", 80 | "Makin", "Tipor", "Semai", "Berhala", "Tegal", "Samoe", 81 | 0 82 | }; 83 | 84 | char *shkweapons[] = { 85 | /* Perigord */ 86 | "Voulgezac", "Rouffiac", "Lerignac", "Touverac", "Guizengeard", 87 | "Melac", "Neuvicq", "Vanzac", "Picq", "Urignac", "Corignac", 88 | "Fleac", "Lonzac", "Vergt", "Queyssac", "Liorac", "Echourgnac", 89 | "Cazelon", "Eypau", "Carignan", "Monbazillac", "Jonzac", 90 | "Pons", "Jumilhac", "Fenouilledes", "Laguiolet", "Saujon", 91 | "Eymoutiers", "Eygurande", "Eauze", "Labouheyre", 92 | 0 93 | }; 94 | 95 | char *shkgeneral[] = { 96 | /* Suriname */ 97 | "Hebiwerie", "Possogroenoe", "Asidonhopo", "Manlobbi", 98 | "Adjama", "Pakka Pakka", "Kabalebo", "Wonotobo", 99 | "Akalapi", "Sipaliwini", 100 | /* Greenland */ 101 | "Annootok", "Upernavik", "Angmagssalik", 102 | /* N. Canada */ 103 | "Aklavik", "Inuvik", "Tuktoyaktuk", 104 | "Chicoutimi", "Ouiatchouane", "Chibougamau", 105 | "Matagami", "Kipawa", "Kinojevis", 106 | "Abitibi", "Maganasipi", 107 | /* Iceland */ 108 | "Akureyri", "Kopasker", "Budereyri", "Akranes", "Bordeyri", 109 | "Holmavik", 110 | 0 111 | }; 112 | 113 | struct shk_nx { 114 | char x; 115 | char **xn; 116 | } shk_nx[] = { 117 | { POTION_SYM, shkliquors }, 118 | { SCROLL_SYM, shkbooks }, 119 | { ARMOR_SYM, shkarmors }, 120 | { WAND_SYM, shkwands }, 121 | { RING_SYM, shkrings }, 122 | { FOOD_SYM, shkfoods }, 123 | { WEAPON_SYM, shkweapons }, 124 | { 0, shkgeneral } 125 | }; 126 | 127 | findname(nampt, let) char *nampt; char let; { 128 | struct shk_nx *p = shk_nx; 129 | char **q; 130 | int i; 131 | while(p->x && p->x != let) p++; 132 | q = p->xn; 133 | for(i=0; iwormno = tmp; 17 | return(1); 18 | } 19 | return(0); /* level infested with worms */ 20 | } 21 | 22 | /* called to initialize a worm unless cut in half */ 23 | initworm(mtmp) struct monst *mtmp; { 24 | struct wseg *wtmp; 25 | int tmp = mtmp->wormno; 26 | if(!tmp) return; 27 | wheads[tmp] = wsegs[tmp] = wtmp = newseg(); 28 | wgrowtime[tmp] = 0; 29 | wtmp->wx = mtmp->mx; 30 | wtmp->wy = mtmp->my; 31 | /* wtmp->wdispl = 0; */ 32 | wtmp->nseg = 0; 33 | } 34 | 35 | worm_move(mtmp) struct monst *mtmp; { 36 | struct wseg *wtmp, *whd; 37 | int tmp = mtmp->wormno; 38 | wtmp = newseg(); 39 | wtmp->wx = mtmp->mx; 40 | wtmp->wy = mtmp->my; 41 | wtmp->nseg = 0; 42 | /* wtmp->wdispl = 0; */ 43 | (whd = wheads[tmp])->nseg = wtmp; 44 | wheads[tmp] = wtmp; 45 | if(cansee(whd->wx,whd->wy)){ 46 | unpmon(mtmp); 47 | atl(whd->wx, whd->wy, '~'); 48 | whd->wdispl = 1; 49 | } else whd->wdispl = 0; 50 | if(wgrowtime[tmp] <= moves) { 51 | if(!wgrowtime[tmp]) wgrowtime[tmp] = moves + rnd(5); 52 | else wgrowtime[tmp] += 2+rnd(15); 53 | mtmp->mhpmax += 3; 54 | mtmp->mhp += 3; 55 | return; 56 | } 57 | whd = wsegs[tmp]; 58 | wsegs[tmp] = whd->nseg; 59 | remseg(whd); 60 | } 61 | 62 | worm_nomove(mtmp) struct monst *mtmp; { 63 | int tmp; 64 | struct wseg *wtmp; 65 | tmp = mtmp->wormno; 66 | wtmp = wsegs[tmp]; 67 | if(wtmp == wheads[tmp]) return; 68 | if(wtmp == 0 || wtmp->nseg == 0) panic("worm_nomove?"); 69 | wsegs[tmp] = wtmp->nseg; 70 | remseg(wtmp); 71 | mtmp->mhp -= 3; /* mhpmax not changed ! */ 72 | } 73 | 74 | wormdead(mtmp) struct monst *mtmp; { 75 | int tmp = mtmp->wormno; 76 | struct wseg *wtmp, *wtmp2; 77 | if(!tmp) return; 78 | mtmp->wormno = 0; 79 | for(wtmp = wsegs[tmp]; wtmp; wtmp = wtmp2){ 80 | wtmp2 = wtmp->nseg; 81 | remseg(wtmp); 82 | } 83 | wsegs[tmp] = 0; 84 | } 85 | 86 | wormhit(mtmp) struct monst *mtmp; { 87 | int tmp = mtmp->wormno; 88 | struct wseg *wtmp; 89 | if(!tmp) return; /* worm without tail */ 90 | for(wtmp = wsegs[tmp]; wtmp; wtmp = wtmp->nseg) 91 | (void) hitu(mtmp,1); 92 | } 93 | 94 | wormsee(tmp) unsigned tmp; { 95 | struct wseg *wtmp = wsegs[tmp]; 96 | if(!wtmp) panic("wormsee: wtmp==0"); 97 | for(; wtmp->nseg; wtmp = wtmp->nseg) 98 | if(!cansee(wtmp->wx,wtmp->wy) && wtmp->wdispl){ 99 | newsym(wtmp->wx, wtmp->wy); 100 | wtmp->wdispl = 0; 101 | } 102 | } 103 | 104 | pwseg(wtmp) struct wseg *wtmp; { 105 | if(!wtmp->wdispl){ 106 | atl(wtmp->wx, wtmp->wy, '~'); 107 | wtmp->wdispl = 1; 108 | } 109 | } 110 | 111 | cutworm(mtmp,x,y,weptyp) 112 | struct monst *mtmp; 113 | xchar x,y; 114 | uchar weptyp; /* uwep->otyp or 0 */ 115 | { 116 | struct wseg *wtmp, *wtmp2; 117 | struct monst *mtmp2; 118 | int tmp,tmp2; 119 | if(mtmp->mx == x && mtmp->my == y) return; /* hit headon */ 120 | 121 | /* cutting goes best with axe or sword */ 122 | tmp = rnd(20); 123 | if(weptyp == LONG_SWORD || weptyp == TWO_HANDED_SWORD || 124 | weptyp == AXE) tmp += 5; 125 | if(tmp < 12) return; 126 | 127 | /* if tail then worm just loses a tail segment */ 128 | tmp = mtmp->wormno; 129 | wtmp = wsegs[tmp]; 130 | if(wtmp->wx == x && wtmp->wy == y){ 131 | wsegs[tmp] = wtmp->nseg; 132 | remseg(wtmp); 133 | return; 134 | } 135 | 136 | /* cut the worm in two halves */ 137 | mtmp2 = newmonst(0); 138 | *mtmp2 = *mtmp; 139 | mtmp2->mxlth = mtmp2->mnamelth = 0; 140 | 141 | /* sometimes the tail end dies */ 142 | if(rn2(3) || !getwn(mtmp2)){ 143 | monfree(mtmp2); 144 | tmp2 = 0; 145 | } else { 146 | tmp2 = mtmp2->wormno; 147 | wsegs[tmp2] = wsegs[tmp]; 148 | wgrowtime[tmp2] = 0; 149 | } 150 | do { 151 | if(wtmp->nseg->wx == x && wtmp->nseg->wy == y){ 152 | if(tmp2) wheads[tmp2] = wtmp; 153 | wsegs[tmp] = wtmp->nseg->nseg; 154 | remseg(wtmp->nseg); 155 | wtmp->nseg = 0; 156 | if(tmp2){ 157 | pline("You cut the worm in half."); 158 | mtmp2->mhpmax = mtmp2->mhp = 159 | d(mtmp2->data->mlevel, 8); 160 | mtmp2->mx = wtmp->wx; 161 | mtmp2->my = wtmp->wy; 162 | mtmp2->nmon = fmon; 163 | fmon = mtmp2; 164 | pmon(mtmp2); 165 | } else { 166 | pline("You cut off part of the worm's tail."); 167 | remseg(wtmp); 168 | } 169 | mtmp->mhp /= 2; 170 | return; 171 | } 172 | wtmp2 = wtmp->nseg; 173 | if(!tmp2) remseg(wtmp); 174 | wtmp = wtmp2; 175 | } while(wtmp->nseg); 176 | panic("Cannot find worm segment"); 177 | } 178 | 179 | remseg(wtmp) struct wseg *wtmp; { 180 | if(wtmp->wdispl) 181 | newsym(wtmp->wx, wtmp->wy); 182 | free((char *) wtmp); 183 | } 184 | #endif NOWORM 185 | -------------------------------------------------------------------------------- /src/hack.search.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.search.c - version 1.0.3 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | 7 | extern struct monst *makemon(); 8 | 9 | int findit(void) /* returns number of things found */ 10 | { 11 | int num; 12 | unsigned char zx, zy; 13 | struct trap *ttmp; 14 | struct monst *mtmp; 15 | unsigned char lx, hx, ly, hy; 16 | 17 | if (u.uswallow) 18 | return (0); 19 | for (lx = (unsigned char)u.ux; /* MODERN: cast xchar to unsigned char */ 20 | (num = levl[(int)(lx - 1)][(int)u.uy].typ) && 21 | num != CORR; 22 | lx--) 23 | ; /* MODERN: Cast to int for safe array indexing */ 24 | for (hx = (unsigned char)u.ux; /* MODERN: cast xchar to unsigned char */ 25 | (num = levl[(int)(hx + 1)][(int)u.uy].typ) && 26 | num != CORR; 27 | hx++) 28 | ; /* MODERN: Cast to int for safe array indexing */ 29 | for (ly = (unsigned char)u.uy; /* MODERN: cast xchar to unsigned char */ 30 | (num = levl[(int)u.ux][(int)(ly - 1)].typ) && 31 | num != CORR; 32 | ly--) 33 | ; /* MODERN: Cast to int for safe array indexing */ 34 | for (hy = (unsigned char)u.uy; /* MODERN: cast xchar to unsigned char */ 35 | (num = levl[(int)u.ux][(int)(hy + 1)].typ) && 36 | num != CORR; 37 | hy++) 38 | ; /* MODERN: Cast to int for safe array indexing */ 39 | num = 0; 40 | for (zy = ly; zy <= hy; zy++) 41 | for (zx = lx; zx <= hx; zx++) { 42 | if (levl[zx][zy].typ == SDOOR) { 43 | levl[zx][zy].typ = DOOR; 44 | atl(zx, zy, '+'); 45 | num++; 46 | } else if (levl[zx][zy].typ == SCORR) { 47 | levl[zx][zy].typ = CORR; 48 | atl(zx, zy, CORR_SYM); 49 | num++; 50 | } else if ((ttmp = t_at(zx, zy))) { 51 | if (ttmp->ttyp == PIERC) { 52 | (void)makemon(PM_PIERCER, zx, zy); 53 | num++; 54 | deltrap(ttmp); 55 | } else if (!ttmp->tseen) { 56 | ttmp->tseen = 1; 57 | if (!vism_at(zx, zy)) 58 | atl(zx, zy, '^'); 59 | num++; 60 | } 61 | } else if ((mtmp = m_at(zx, zy))) 62 | if (mtmp->mimic) { 63 | seemimic(mtmp); 64 | num++; 65 | } 66 | } 67 | return (num); 68 | } 69 | 70 | int dosearch(void) { 71 | unsigned char x, y; 72 | struct trap *trap; 73 | struct monst *mtmp; 74 | 75 | if (u.uswallow) { 76 | pline("What are you looking for? The exit?"); 77 | return (1); 78 | } else 79 | /* MODERN: Safe coordinate iteration with bounds checking */ 80 | for (x = (unsigned char)(u.ux - 1); x <= u.ux + 1; x++) /* MODERN: cast to unsigned char */ 81 | for (y = (unsigned char)(u.uy - 1); y <= u.uy + 1; y++) /* MODERN: cast to unsigned char */ 82 | if ((x != u.ux || y != u.uy) && isok(x, y)) { 83 | if (levl[x][y].typ == SDOOR) { 84 | if (rn2(7)) 85 | continue; 86 | levl[x][y].typ = DOOR; 87 | levl[x][y].seen = 0; /* force prl */ 88 | prl(x, y); 89 | nomul(0); 90 | } else if (levl[x][y].typ == SCORR) { 91 | if (rn2(7)) 92 | continue; 93 | levl[x][y].typ = CORR; 94 | levl[x][y].seen = 0; /* force prl */ 95 | prl(x, y); 96 | nomul(0); 97 | } else { 98 | /* Be careful not to find anything in an SCORR or SDOOR */ 99 | if ((mtmp = m_at(x, y))) 100 | if (mtmp->mimic) { 101 | seemimic(mtmp); 102 | pline("You find a mimic."); 103 | return (1); 104 | } 105 | for (trap = ftrap; trap; trap = trap->ntrap) 106 | if (trap->tx == x && trap->ty == y && !trap->tseen && !rn2(8)) { 107 | nomul(0); 108 | pline("You find a%s.", traps[trap->ttyp]); 109 | if (trap->ttyp == PIERC) { 110 | deltrap(trap); 111 | (void)makemon(PM_PIERCER, x, y); 112 | return (1); 113 | } 114 | trap->tseen = 1; 115 | if (!vism_at(x, y)) 116 | atl(x, y, '^'); 117 | } 118 | } 119 | } 120 | return (1); 121 | } 122 | 123 | int doidtrap(void) { 124 | struct trap *trap; 125 | int x, y; 126 | if (!getdir(1)) 127 | return (0); 128 | x = u.ux + u.dx; 129 | y = u.uy + u.dy; 130 | for (trap = ftrap; trap; trap = trap->ntrap) 131 | if (trap->tx == x && trap->ty == y && trap->tseen) { 132 | if (u.dz) 133 | if ((u.dz < 0) != (!xdnstair && trap->ttyp == TRAPDOOR)) 134 | continue; 135 | pline("That is a%s.", traps[trap->ttyp]); 136 | return (0); 137 | } 138 | pline("I can't see a trap there."); 139 | return (0); 140 | } 141 | 142 | void wakeup(struct monst *mtmp) { 143 | mtmp->msleep = 0; 144 | setmangry(mtmp); 145 | if (mtmp->mimic) 146 | seemimic(mtmp); 147 | } 148 | 149 | /* NOTE: we must check if(mtmp->mimic) before calling this routine */ 150 | void seemimic(struct monst *mtmp) { 151 | mtmp->mimic = 0; 152 | mtmp->mappearance = 0; 153 | unpmon(mtmp); 154 | pmon(mtmp); 155 | } 156 | -------------------------------------------------------------------------------- /docs/historical/original-source/config.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* config.h - version 1.0.3 */ 3 | 4 | #include "pathnames.h" 5 | 6 | #ifndef CONFIG /* make sure the compiler doesnt see the typedefs twice */ 7 | 8 | #define CONFIG 9 | #define UNIX /* delete if no fork(), exec() available */ 10 | #define CHDIR /* delete if no chdir() available */ 11 | 12 | /* 13 | * Some include files are in a different place under SYSV 14 | * BSD SYSV 15 | * 16 | * 17 | * 18 | * Some routines are called differently 19 | * index strchr 20 | * rindex strrchr 21 | * Also, the code for suspend and various ioctls is only given for BSD4.2 22 | * (I do not have access to a SYSV system.) 23 | */ 24 | #define BSD /* delete this line on System V */ 25 | 26 | /* #define STUPID */ /* avoid some complicated expressions if 27 | your C compiler chokes on them */ 28 | /* #define PYRAMID_BUG */ /* avoid a bug on the Pyramid */ 29 | /* #define NOWAITINCLUDE */ /* neither nor exists */ 30 | 31 | #define WIZARD "bruno" /* the person allowed to use the -D option */ 32 | #define RECORD "record"/* the file containing the list of topscorers */ 33 | #define NEWS "news" /* the file containing the latest hack news */ 34 | #define HELP "help" /* the file containing a description of the commands */ 35 | #define SHELP "hh" /* abbreviated form of the same */ 36 | #define RUMORFILE "rumors" /* a file with fortune cookies */ 37 | #define DATAFILE "data" /* a file giving the meaning of symbols used */ 38 | #define FMASK 0660 /* file creation mask */ 39 | #define HLOCK "perm" /* an empty file used for locking purposes */ 40 | #define LLOCK "safelock" /* link to previous */ 41 | 42 | #ifdef UNIX 43 | /* 44 | * Define DEF_PAGER as your default pager, e.g. "/bin/cat" or "/usr/ucb/more" 45 | * If defined, it can be overridden by the environment variable PAGER. 46 | * Hack will use its internal pager if DEF_PAGER is not defined. 47 | * (This might be preferable for security reasons.) 48 | * #define DEF_PAGER ".../mydir/mypager" 49 | */ 50 | 51 | /* 52 | * If you define MAIL, then the player will be notified of new mail 53 | * when it arrives. If you also define DEF_MAILREADER then this will 54 | * be the default mail reader, and can be overridden by the environment 55 | * variable MAILREADER; otherwise an internal pager will be used. 56 | * A stat system call is done on the mailbox every MAILCKFREQ moves. 57 | */ 58 | /* #define MAIL */ 59 | #define DEF_MAILREADER _PATH_MAIL /* or e.g. /bin/mail */ 60 | #define MAILCKFREQ 100 61 | 62 | 63 | #define SHELL /* do not delete the '!' command */ 64 | 65 | #ifdef BSD 66 | #define SUSPEND /* let ^Z suspend the game */ 67 | #endif BSD 68 | #endif UNIX 69 | 70 | #ifdef CHDIR 71 | /* 72 | * If you define HACKDIR, then this will be the default playground; 73 | * otherwise it will be the current directory. 74 | */ 75 | #ifdef QUEST 76 | #define HACKDIR _PATH_QUEST 77 | #else QUEST 78 | #define HACKDIR _PATH_HACK 79 | #endif QUEST 80 | 81 | /* 82 | * Some system administrators are stupid enough to make Hack suid root 83 | * or suid daemon, where daemon has other powers besides that of reading or 84 | * writing Hack files. In such cases one should be careful with chdir's 85 | * since the user might create files in a directory of his choice. 86 | * Of course SECURE is meaningful only if HACKDIR is defined. 87 | */ 88 | #define SECURE /* do setuid(getuid()) after chdir() */ 89 | 90 | /* 91 | * If it is desirable to limit the number of people that can play Hack 92 | * simultaneously, define HACKDIR, SECURE and MAX_NR_OF_PLAYERS. 93 | * #define MAX_NR_OF_PLAYERS 100 94 | */ 95 | #endif CHDIR 96 | 97 | /* size of terminal screen is (at least) (ROWNO+2) by COLNO */ 98 | #define COLNO 80 99 | #define ROWNO 22 100 | 101 | /* 102 | * small signed integers (8 bits suffice) 103 | * typedef char schar; 104 | * will do when you have signed characters; otherwise use 105 | * typedef short int schar; 106 | */ 107 | typedef char schar; 108 | 109 | /* 110 | * small unsigned integers (8 bits suffice - but 7 bits do not) 111 | * - these are usually object types; be careful with inequalities! - 112 | * typedef unsigned char uchar; 113 | * will be satisfactory if you have an "unsigned char" type; otherwise use 114 | * typedef unsigned short int uchar; 115 | */ 116 | typedef unsigned char uchar; 117 | 118 | /* 119 | * small integers in the range 0 - 127, usually coordinates 120 | * although they are nonnegative they must not be declared unsigned 121 | * since otherwise comparisons with signed quantities are done incorrectly 122 | */ 123 | typedef schar xchar; 124 | typedef xchar boolean; /* 0 or 1 */ 125 | #define TRUE 1 126 | #define FALSE 0 127 | 128 | /* 129 | * Declaration of bitfields in various structs; if your C compiler 130 | * doesnt handle bitfields well, e.g., if it is unable to initialize 131 | * structs containing bitfields, then you might use 132 | * #define Bitfield(x,n) uchar x 133 | * since the bitfields used never have more than 7 bits. (Most have 1 bit.) 134 | */ 135 | #define Bitfield(x,n) unsigned x:n 136 | 137 | #define SIZE(x) (int)(sizeof(x) / sizeof(x[0])) 138 | 139 | #endif CONFIG 140 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.h - version 1.0.3 */ 3 | 4 | #include "config.h" 5 | #include 6 | 7 | #ifndef BSD 8 | #define index strchr 9 | #define rindex strrchr 10 | #endif BSD 11 | 12 | #define Null(type) ((struct type *) 0) 13 | 14 | #include "def.objclass.h" 15 | 16 | typedef struct { 17 | xchar x,y; 18 | } coord; 19 | 20 | #include "def.monst.h" /* uses coord */ 21 | #include "def.gold.h" 22 | #include "def.trap.h" 23 | #include "def.obj.h" 24 | #include "def.flag.h" 25 | 26 | #define plur(x) (((x) == 1) ? "" : "s") 27 | 28 | #define BUFSZ 256 /* for getlin buffers */ 29 | #define PL_NSIZ 32 /* name of player, ghost, shopkeeper */ 30 | 31 | #include "def.rm.h" 32 | #include "def.permonst.h" 33 | 34 | extern long *alloc(); 35 | 36 | extern xchar xdnstair, ydnstair, xupstair, yupstair; /* stairs up and down. */ 37 | 38 | extern xchar dlevel; 39 | #define newstring(x) (char *) alloc((unsigned)(x)) 40 | #include "hack.onames.h" 41 | 42 | #define ON 1 43 | #define OFF 0 44 | 45 | extern struct obj *invent, *uwep, *uarm, *uarm2, *uarmh, *uarms, *uarmg, 46 | *uleft, *uright, *fcobj; 47 | extern struct obj *uchain; /* defined iff PUNISHED */ 48 | extern struct obj *uball; /* defined if PUNISHED */ 49 | struct obj *o_at(), *getobj(), *sobj_at(); 50 | 51 | struct prop { 52 | #define TIMEOUT 007777 /* mask */ 53 | #define LEFT_RING W_RINGL /* 010000L */ 54 | #define RIGHT_RING W_RINGR /* 020000L */ 55 | #define INTRINSIC 040000L 56 | #define LEFT_SIDE LEFT_RING 57 | #define RIGHT_SIDE RIGHT_RING 58 | #define BOTH_SIDES (LEFT_SIDE | RIGHT_SIDE) 59 | long p_flgs; 60 | int (*p_tofn)(); /* called after timeout */ 61 | }; 62 | 63 | struct you { 64 | xchar ux, uy; 65 | schar dx, dy, dz; /* direction of move (or zap or ... ) */ 66 | #ifdef QUEST 67 | schar di; /* direction of FF */ 68 | xchar ux0, uy0; /* initial position FF */ 69 | #endif QUEST 70 | xchar udisx, udisy; /* last display pos */ 71 | char usym; /* usually '@' */ 72 | schar uluck; 73 | #define LUCKMAX 10 /* on moonlit nights 11 */ 74 | #define LUCKMIN (-10) 75 | int last_str_turn:3; /* 0: none, 1: half turn, 2: full turn */ 76 | /* +: turn right, -: turn left */ 77 | unsigned udispl:1; /* @ on display */ 78 | unsigned ulevel:4; /* 1 - 14 */ 79 | #ifdef QUEST 80 | unsigned uhorizon:7; 81 | #endif QUEST 82 | unsigned utrap:3; /* trap timeout */ 83 | unsigned utraptype:1; /* defined if utrap nonzero */ 84 | #define TT_BEARTRAP 0 85 | #define TT_PIT 1 86 | unsigned uinshop:6; /* used only in shk.c - (roomno+1) of shop */ 87 | 88 | 89 | /* perhaps these #define's should also be generated by makedefs */ 90 | #define TELEPAT LAST_RING /* not a ring */ 91 | #define Telepat u.uprops[TELEPAT].p_flgs 92 | #define FAST (LAST_RING+1) /* not a ring */ 93 | #define Fast u.uprops[FAST].p_flgs 94 | #define CONFUSION (LAST_RING+2) /* not a ring */ 95 | #define Confusion u.uprops[CONFUSION].p_flgs 96 | #define INVIS (LAST_RING+3) /* not a ring */ 97 | #define Invis u.uprops[INVIS].p_flgs 98 | #define Invisible (Invis && !See_invisible) 99 | #define GLIB (LAST_RING+4) /* not a ring */ 100 | #define Glib u.uprops[GLIB].p_flgs 101 | #define PUNISHED (LAST_RING+5) /* not a ring */ 102 | #define Punished u.uprops[PUNISHED].p_flgs 103 | #define SICK (LAST_RING+6) /* not a ring */ 104 | #define Sick u.uprops[SICK].p_flgs 105 | #define BLIND (LAST_RING+7) /* not a ring */ 106 | #define Blind u.uprops[BLIND].p_flgs 107 | #define WOUNDED_LEGS (LAST_RING+8) /* not a ring */ 108 | #define Wounded_legs u.uprops[WOUNDED_LEGS].p_flgs 109 | #define STONED (LAST_RING+9) /* not a ring */ 110 | #define Stoned u.uprops[STONED].p_flgs 111 | #define PROP(x) (x-RIN_ADORNMENT) /* convert ring to index in uprops */ 112 | unsigned umconf:1; 113 | char *usick_cause; 114 | struct prop uprops[LAST_RING+10]; 115 | 116 | unsigned uswallow:1; /* set if swallowed by a monster */ 117 | unsigned uswldtim:4; /* time you have been swallowed */ 118 | unsigned uhs:3; /* hunger state - see hack.eat.c */ 119 | schar ustr,ustrmax; 120 | schar udaminc; 121 | schar uac; 122 | int uhp,uhpmax; 123 | long int ugold,ugold0,uexp,urexp; 124 | int uhunger; /* refd only in eat.c and shk.c */ 125 | int uinvault; 126 | struct monst *ustuck; 127 | int nr_killed[CMNUM+2]; /* used for experience bookkeeping */ 128 | }; 129 | 130 | extern struct you u; 131 | 132 | extern char *traps[]; 133 | extern char *monnam(), *Monnam(), *amonnam(), *Amonnam(), 134 | *doname(), *aobjnam(); 135 | extern char readchar(); 136 | extern char vowels[]; 137 | 138 | extern xchar curx,cury; /* cursor location on screen */ 139 | 140 | extern coord bhitpos; /* place where thrown weapon falls to the ground */ 141 | 142 | extern xchar seehx,seelx,seehy,seely; /* where to see*/ 143 | extern char *save_cm,*killer; 144 | 145 | extern xchar dlevel, maxdlevel; /* dungeon level */ 146 | 147 | extern long moves; 148 | 149 | extern int multi; 150 | 151 | 152 | extern char lock[]; 153 | 154 | 155 | #define DIST(x1,y1,x2,y2) (((x1)-(x2))*((x1)-(x2)) + ((y1)-(y2))*((y1)-(y2))) 156 | 157 | #define PL_CSIZ 20 /* sizeof pl_character */ 158 | #define MAX_CARR_CAP 120 /* so that boulders can be heavier */ 159 | #define MAXLEVEL 40 160 | #define FAR (COLNO+2) /* position outside screen */ 161 | -------------------------------------------------------------------------------- /docs/historical/original-source/hack.makemon.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 | /* hack.makemon.c - version 1.0.2 */ 3 | /* $FreeBSD$ */ 4 | 5 | #include "hack.h" 6 | extern char fut_geno[]; 7 | extern char *index(); 8 | extern struct obj *mkobj_at(); 9 | struct monst zeromonst; 10 | 11 | /* 12 | * called with [x,y] = coordinates; 13 | * [0,0] means anyplace 14 | * [u.ux,u.uy] means: call mnexto (if !in_mklev) 15 | * 16 | * In case we make an Orc or killer bee, we make an entire horde (swarm); 17 | * note that in this case we return only one of them (the one at [x,y]). 18 | */ 19 | struct monst * 20 | makemon(ptr,x,y) 21 | struct permonst *ptr; 22 | { 23 | struct monst *mtmp; 24 | int tmp, ct; 25 | boolean anything = (!ptr); 26 | extern boolean in_mklev; 27 | 28 | if(x != 0 || y != 0) if(m_at(x,y)) return((struct monst *) 0); 29 | if(ptr){ 30 | if(index(fut_geno, ptr->mlet)) return((struct monst *) 0); 31 | } else { 32 | ct = CMNUM - strlen(fut_geno); 33 | if(index(fut_geno, 'm')) ct++; /* make only 1 minotaur */ 34 | if(index(fut_geno, '@')) ct++; 35 | if(ct <= 0) return(0); /* no more monsters! */ 36 | tmp = rn2(ct*dlevel/24 + 7); 37 | if(tmp < dlevel - 4) tmp = rn2(ct*dlevel/24 + 12); 38 | if(tmp >= ct) tmp = rn1(ct - ct/2, ct/2); 39 | for(ct = 0; ct < CMNUM; ct++){ 40 | ptr = &mons[ct]; 41 | if(index(fut_geno, ptr->mlet)) 42 | continue; 43 | if(!tmp--) goto gotmon; 44 | } 45 | panic("makemon?"); 46 | } 47 | gotmon: 48 | mtmp = newmonst(ptr->pxlth); 49 | *mtmp = zeromonst; /* clear all entries in structure */ 50 | for(ct = 0; ct < ptr->pxlth; ct++) 51 | ((char *) &(mtmp->mextra[0]))[ct] = 0; 52 | mtmp->nmon = fmon; 53 | fmon = mtmp; 54 | mtmp->m_id = flags.ident++; 55 | mtmp->data = ptr; 56 | mtmp->mxlth = ptr->pxlth; 57 | if(ptr->mlet == 'D') mtmp->mhpmax = mtmp->mhp = 80; 58 | else if(!ptr->mlevel) mtmp->mhpmax = mtmp->mhp = rnd(4); 59 | else mtmp->mhpmax = mtmp->mhp = d(ptr->mlevel, 8); 60 | mtmp->mx = x; 61 | mtmp->my = y; 62 | mtmp->mcansee = 1; 63 | if(ptr->mlet == 'M'){ 64 | mtmp->mimic = 1; 65 | mtmp->mappearance = ']'; 66 | } 67 | if(!in_mklev) { 68 | if(x == u.ux && y == u.uy && ptr->mlet != ' ') 69 | mnexto(mtmp); 70 | if(x == 0 && y == 0) 71 | rloc(mtmp); 72 | } 73 | if(ptr->mlet == 's' || ptr->mlet == 'S') { 74 | mtmp->mhide = mtmp->mundetected = 1; 75 | if(in_mklev) 76 | if(mtmp->mx && mtmp->my) 77 | (void) mkobj_at(0, mtmp->mx, mtmp->my); 78 | } 79 | if(ptr->mlet == ':') { 80 | mtmp->cham = 1; 81 | (void) newcham(mtmp, &mons[dlevel+14+rn2(CMNUM-14-dlevel)]); 82 | } 83 | if(ptr->mlet == 'I' || ptr->mlet == ';') 84 | mtmp->minvis = 1; 85 | if(ptr->mlet == 'L' || ptr->mlet == 'N' 86 | || (in_mklev && index("&w;", ptr->mlet) && rn2(5)) 87 | ) mtmp->msleep = 1; 88 | 89 | #ifndef NOWORM 90 | if(ptr->mlet == 'w' && getwn(mtmp)) 91 | initworm(mtmp); 92 | #endif NOWORM 93 | 94 | if(anything) if(ptr->mlet == 'O' || ptr->mlet == 'k') { 95 | coord enexto(); 96 | coord mm; 97 | int cnt = rnd(10); 98 | mm.x = x; 99 | mm.y = y; 100 | while(cnt--) { 101 | mm = enexto(mm.x, mm.y); 102 | (void) makemon(ptr, mm.x, mm.y); 103 | } 104 | } 105 | 106 | return(mtmp); 107 | } 108 | 109 | coord 110 | enexto(xx,yy) 111 | xchar xx,yy; 112 | { 113 | xchar x,y; 114 | coord foo[15], *tfoo; 115 | int range; 116 | 117 | tfoo = foo; 118 | range = 1; 119 | do { /* full kludge action. */ 120 | for(x = xx-range; x <= xx+range; x++) 121 | if(goodpos(x, yy-range)) { 122 | tfoo->x = x; 123 | tfoo++->y = yy-range; 124 | if(tfoo == &foo[15]) goto foofull; 125 | } 126 | for(x = xx-range; x <= xx+range; x++) 127 | if(goodpos(x,yy+range)) { 128 | tfoo->x = x; 129 | tfoo++->y = yy+range; 130 | if(tfoo == &foo[15]) goto foofull; 131 | } 132 | for(y = yy+1-range; y < yy+range; y++) 133 | if(goodpos(xx-range,y)) { 134 | tfoo->x = xx-range; 135 | tfoo++->y = y; 136 | if(tfoo == &foo[15]) goto foofull; 137 | } 138 | for(y = yy+1-range; y < yy+range; y++) 139 | if(goodpos(xx+range,y)) { 140 | tfoo->x = xx+range; 141 | tfoo++->y = y; 142 | if(tfoo == &foo[15]) goto foofull; 143 | } 144 | range++; 145 | } while(tfoo == foo); 146 | foofull: 147 | return( foo[rn2(tfoo-foo)] ); 148 | } 149 | 150 | goodpos(x,y) /* used only in mnexto and rloc */ 151 | { 152 | return( 153 | ! (x < 1 || x > COLNO-2 || y < 1 || y > ROWNO-2 || 154 | m_at(x,y) || !ACCESSIBLE(levl[x][y].typ) 155 | || (x == u.ux && y == u.uy) 156 | || sobj_at(ENORMOUS_ROCK, x, y) 157 | )); 158 | } 159 | 160 | rloc(mtmp) 161 | struct monst *mtmp; 162 | { 163 | int tx,ty; 164 | char ch = mtmp->data->mlet; 165 | 166 | #ifndef NOWORM 167 | if(ch == 'w' && mtmp->mx) return; /* do not relocate worms */ 168 | #endif NOWORM 169 | do { 170 | tx = rn1(COLNO-3,2); 171 | ty = rn2(ROWNO); 172 | } while(!goodpos(tx,ty)); 173 | mtmp->mx = tx; 174 | mtmp->my = ty; 175 | if(u.ustuck == mtmp){ 176 | if(u.uswallow) { 177 | u.ux = tx; 178 | u.uy = ty; 179 | docrt(); 180 | } else u.ustuck = 0; 181 | } 182 | pmon(mtmp); 183 | } 184 | 185 | struct monst * 186 | mkmon_at(let,x,y) 187 | char let; 188 | int x,y; 189 | { 190 | int ct; 191 | struct permonst *ptr; 192 | 193 | for(ct = 0; ct < CMNUM; ct++) { 194 | ptr = &mons[ct]; 195 | if(ptr->mlet == let) 196 | return(makemon(ptr,x,y)); 197 | } 198 | return(0); 199 | } 200 | --------------------------------------------------------------------------------