From a868f7779583a00316cb076fc4d62ccfb1488434 Mon Sep 17 00:00:00 2001 From: Joe Wreschnig Date: Thu, 4 Apr 2013 12:00:26 +0200 Subject: [PATCH] Rogue 3.6 reconstruction from http://rogue.rogueforge.net/rogue36/. --- LICENSE.TXT | 92 +++ Makefile | 145 ++++ armor.c | 90 +++ chase.c | 323 ++++++++ command.c | 633 +++++++++++++++ daemon.c | 201 +++++ daemons.c | 169 ++++ fight.c | 742 +++++++++++++++++ init.c | 589 ++++++++++++++ io.c | 241 ++++++ list.c | 121 +++ machdep.h | 55 ++ main.c | 445 +++++++++++ mdport.c | 1250 +++++++++++++++++++++++++++++ misc.c | 432 ++++++++++ monsters.c | 240 ++++++ move.c | 385 +++++++++ newlevel.c | 173 ++++ options.c | 352 ++++++++ pack.c | 402 ++++++++++ passages.c | 287 +++++++ potions.c | 211 +++++ readme36.html | 78 ++ rings.c | 207 +++++ rip.c | 376 +++++++++ rogue.6 | 58 ++ rogue.h | 527 ++++++++++++ rogue.r | 411 ++++++++++ rogue36.cat | 54 ++ rogue36.doc | 660 +++++++++++++++ rogue36.html | 633 +++++++++++++++ rogue36.sln | 20 + rogue36.vcproj | 360 +++++++++ rooms.c | 185 +++++ save.c | 276 +++++++ scrolls.c | 316 ++++++++ state.c | 2079 ++++++++++++++++++++++++++++++++++++++++++++++++ sticks.c | 398 +++++++++ things.c | 382 +++++++++ vers.c | 16 + weapons.c | 316 ++++++++ wizard.c | 197 +++++ xcrypt.c | 684 ++++++++++++++++ 43 files changed, 15811 insertions(+) create mode 100644 LICENSE.TXT create mode 100644 Makefile create mode 100644 armor.c create mode 100644 chase.c create mode 100644 command.c create mode 100644 daemon.c create mode 100644 daemons.c create mode 100644 fight.c create mode 100644 init.c create mode 100644 io.c create mode 100644 list.c create mode 100644 machdep.h create mode 100644 main.c create mode 100644 mdport.c create mode 100644 misc.c create mode 100644 monsters.c create mode 100644 move.c create mode 100644 newlevel.c create mode 100644 options.c create mode 100644 pack.c create mode 100644 passages.c create mode 100644 potions.c create mode 100644 readme36.html create mode 100644 rings.c create mode 100644 rip.c create mode 100644 rogue.6 create mode 100644 rogue.h create mode 100644 rogue.r create mode 100644 rogue36.cat create mode 100644 rogue36.doc create mode 100644 rogue36.html create mode 100644 rogue36.sln create mode 100644 rogue36.vcproj create mode 100644 rooms.c create mode 100644 save.c create mode 100644 scrolls.c create mode 100644 state.c create mode 100644 sticks.c create mode 100644 things.c create mode 100644 vers.c create mode 100644 weapons.c create mode 100644 wizard.c create mode 100644 xcrypt.c diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 0000000..7993e9d --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,92 @@ +Rogue: Exploring the Dungeons of Doom +Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +=========================================================================== + +Portions of this software (save/restore game state) are based on the work +of Nicholas J. Kisseberth. Used under license: + +Copyright (C) 1999, 2000, 2006 Nicholas J. Kisseberth + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +=========================================================================== + +Portions of this software (encryption) are based on the work +of David Burren. Used under license: + +FreeSec: libcrypt + +Copyright (C) 1994 David Burren +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ef1d6b5 --- /dev/null +++ b/Makefile @@ -0,0 +1,145 @@ +# +# Makefile for rogue +# %W% (Berkeley) %G% +# +# Rogue: Exploring the Dungeons of Doom +# Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman +# All rights reserved. +# +# See the file LICENSE.TXT for full copyright and licensing information. +# + +DISTNAME=rogue3.6.3 +PROGRAM=rogue36 + +O=o + +HDRS= rogue.h machdep.h + +OBJS1 = vers.$(O) armor.$(O) chase.$(O) command.$(O) daemon.$(O) daemons.$(O) \ + fight.$(O) init.$(O) io.$(O) list.$(O) main.$(O) mdport.$(O) \ + misc.$(O) monsters.$(O) move.$(O) newlevel.$(O) options.$(O) +OBJS2 = pack.$(O) passages.$(O) potions.$(O) rings.$(O) rip.$(O) rooms.$(O) \ + save.$(O) scrolls.$(O) state.$(O) sticks.$(O) things.$(O) \ + weapons.$(O) wizard.$(O) xcrypt.$(O) +OBJS = $(OBJS1) $(OBJS2) + +CFILES= vers.c armor.c chase.c command.c daemon.c daemons.c fight.c \ + init.c io.c list.c main.c mdport.c misc.c monsters.c move.c newlevel.c \ + options.c pack.c passages.c potions.c rings.c rip.c rooms.c \ + save.c scrolls.c state.c sticks.c things.c weapons.c wizard.c xcrypt.c + + +MISC_C= +DOCSRC= rogue.6 rogue.r +DOCS = $(PROGRAM).doc $(PROGRAM).cat $(PROGRAM).html readme36.html +MISC = Makefile $(MISC_C) LICENSE.TXT $(PROGRAM).sln $(PROGRAM).vcproj $(DOCS)\ + $(DOCSRC) + +CC = gcc +ROPTS = +COPTS = -O3 +CFLAGS= $(COPTS) $(ROPTS) +LIBS = -lcurses +RM = rm -f +LD = $(CC) +LDOUT = -o + +.SUFFIXES: .obj + +.c.obj: + $(CC) $(CFLAGS) /c $*.c + +$(PROGRAM)$(EXE): $(HDRS) $(OBJS) + $(LD) $(LDFLAGS) $(OBJS) $(LIBS) $(LDOUT)$@ + +clean: + $(RM) $(OBJS1) + $(RM) $(OBJS2) + $(RM) core $(PROGRAM) $(PROGRAM).exe $(DISTNAME).tar $(DISTNAME).tar.gz + $(RM) $(DISTNAME).zip + +dist.src: + make clean + tar cf $(DISTNAME)-src.tar $(CFILES) $(HDRS) $(MISC) $(DOCS) + gzip -f $(DISTNAME)-src.tar + +dist.irix: + @$(MAKE) clean + @$(MAKE) CC=cc CFLAGS="-woff 1116 -O3" $(PROGRAM) +# tbl rogue.r | nroff -ms | colcrt - > $(PROGRAM).doc +# nroff -man rogue.6 | colcrt - > $(PROGRAM).cat + tar cf $(DISTNAME)-irix.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-irix.tar + +dist.aix: + @$(MAKE) clean + @$(MAKE) CC=xlc CFLAGS="-qmaxmem=16768 -O3 -qstrict" $(PROGRAM) +# tbl rogue.r | nroff -ms | colcrt - > $(ROGUE).doc +# nroff -man rogue.6 | colcrt - > $(ROGUE).cat + tar cf $(DISTNAME)-aix.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-aix.tar + +dist.linux: + @$(MAKE) clean + @$(MAKE) $(PROGRAM) +# groff -P-c -t -ms -Tascii rogue.r | sed -e 's/.\x08//g' > $(PROGRAM).doc +# groff -man rogue.6 | sed -e 's/.\x08//g' > $(PROGRAM).cat + tar cf $(DISTNAME)-linux.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-linux.tar + +dist.interix: + @$(MAKE) clean + @$(MAKE) COPTS="-ansi" $(PROGRAM) +# groff -P-b -P-u -t -ms -Tascii rogue.r > $(PROGRAM).doc +# groff -P-b -P-u -man -Tascii rogue.6 > $(PROGRAM).cat + tar cf $(DISTNAME)-interix.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-interix.tar + +dist.cygwin: + @$(MAKE) --no-print-directory clean + @$(MAKE) COPTS="-I/usr/include/ncurses" --no-print-directory $(PROGRAM) +# groff -P-c -t -ms -Tascii rogue.r | sed -e 's/.\x08//g' > $(PROGRAM).doc +# groff -P-c -man -Tascii rogue.6 | sed -e 's/.\x08//g' > $(PROGRAM).cat + tar cf $(DISTNAME)-cygwin.tar $(PROGRAM).exe LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-cygwin.tar + +# +# Use MINGW32-MAKE to build this target +# +dist.mingw32: + @$(MAKE) --no-print-directory RM="cmd /c del" clean + @$(MAKE) --no-print-directory COPTS="-I../pdcurses" LIBS="../pdcurses/pdcurses.a" $(PROGRAM) + cmd /c del $(DISTNAME)-mingw32.zip + zip $(DISTNAME)-mingw32.zip $(PROGRAM).exe LICENSE.TXT $(DOCS) + +# +# Seperate doc targets for DJGPP prevent strange SIGSEGV in groff +# in that environment. +# +doc.djgpp: + groff -t -ms -Tascii rogue.r | sed -e 's/.\x08//g' > $(PROGRAM).doc + +cat.djgpp: + groff -man -Tascii rogue.6 | sed -e 's/.\x08//g' > $(PROGRAM).cat + +dist.djgpp: + @$(MAKE) --no-print-directory clean + @$(MAKE) --no-print-directory LDFLAGS="-L$(DJDIR)/LIB" \ + LIBS="-lpdcur" $(PROGRAM) +# @$(MAKE) --no-print-directory doc.djgpp +# @$(MAKE) --no-print-directory cat.djgpp + rm -f $(DISTNAME)-djgpp.zip + zip $(DISTNAME)-djgpp.zip $(PROGRAM) LICENSE.TXT $(DOCS) + +# +# Use NMAKE to build this target +# +dist.win32: + @$(MAKE) /NOLOGO O="obj" RM="-del" clean + @$(MAKE) /NOLOGO O="obj" CC="@CL" LD="link" LDOUT="/OUT:" EXE=".exe"\ + LIBS="/NODEFAULTLIB:LIBC ..\pdcurses\pdcurses.lib shell32.lib user32.lib Advapi32.lib" \ + COPTS="-nologo -D_CRT_SECURE_NO_DEPRECATE -I..\pdcurses \ + -Ox -wd4033 -wd4716" $(PROGRAM).exe + -del $(DISTNAME)-win32.zip + zip $(DISTNAME)-win32.zip $(PROGRAM).exe LICENSE.TXT $(DOCS) diff --git a/armor.c b/armor.c new file mode 100644 index 0000000..f4817b1 --- /dev/null +++ b/armor.c @@ -0,0 +1,90 @@ +/* + * This file contains misc functions for dealing with armor + * @(#)armor.c 3.9 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" + +/* + * wear: + * The player wants to wear something, so let him/her put it on. + */ + +wear() +{ + register struct linked_list *item; + register struct object *obj; + + if (cur_armor != NULL) + { + addmsg("You are already wearing some"); + if (!terse) + addmsg(". You'll have to take it off first"); + endmsg(); + after = FALSE; + return; + } + if ((item = get_item("wear", ARMOR)) == NULL) + return; + obj = (struct object *) ldata(item); + if (obj->o_type != ARMOR) + { + msg("You can't wear that."); + return; + } + waste_time(); + if (!terse) + addmsg("You are now w"); + else + addmsg("W"); + msg("earing %s.", a_names[obj->o_which]); + cur_armor = obj; + obj->o_flags |= ISKNOW; +} + +/* + * take_off: + * Get the armor off of the players back + */ + +take_off() +{ + register struct object *obj; + + if ((obj = cur_armor) == NULL) + { + if (terse) + msg("Not wearing armor"); + else + msg("You aren't wearing any armor"); + return; + } + if (!dropcheck(cur_armor)) + return; + cur_armor = NULL; + if (terse) + addmsg("Was"); + else + addmsg("You used to be "); + msg(" wearing %c) %s", pack_char(obj), inv_name(obj, TRUE)); +} + +/* + * waste_time: + * Do nothing but let other things happen + */ + +waste_time() +{ + do_daemons(BEFORE); + do_fuses(BEFORE); + do_daemons(AFTER); + do_fuses(AFTER); +} diff --git a/chase.c b/chase.c new file mode 100644 index 0000000..1c5bb95 --- /dev/null +++ b/chase.c @@ -0,0 +1,323 @@ +/* + * Code for one object to chase another + * + * @(#)chase.c 3.17 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" + +coord ch_ret; /* Where chasing takes you */ + +/* + * runners: + * Make all the running monsters move. + */ + +runners() +{ + register struct linked_list *item; + register struct thing *tp; + + for (item = mlist; item != NULL;) + { + tp = (struct thing *) ldata(item); + item = next(item); + if (off(*tp, ISHELD) && on(*tp, ISRUN)) + { + if (off(*tp, ISSLOW) || tp->t_turn) + if (do_chase(tp) == -1) + continue; + if (on(*tp, ISHASTE)) + if (do_chase(tp) == -1) + continue; + tp->t_turn ^= TRUE; + } + } +} + +/* + * do_chase: + * Make one thing chase another. + */ + +do_chase(th) +register struct thing *th; +{ + register struct room *rer, *ree; /* room of chaser, room of chasee */ + register int mindist = 32767, i, dist; + register bool stoprun = FALSE; /* TRUE means we are there */ + register char sch; + coord this; /* Temporary destination for chaser */ + + rer = roomin(&th->t_pos); /* Find room of chaser */ + ree = roomin(th->t_dest); /* Find room of chasee */ + /* + * We don't count doors as inside rooms for this routine + */ + if (mvwinch(stdscr, th->t_pos.y, th->t_pos.x) == DOOR) + rer = NULL; + this = *th->t_dest; + /* + * If the object of our desire is in a different room, + * than we are and we ar not in a corridor, run to the + * door nearest to our goal. + */ + if (rer != NULL && rer != ree) + for (i = 0; i < rer->r_nexits; i++) /* loop through doors */ + { + dist = DISTANCE(th->t_dest->y, th->t_dest->x, + rer->r_exit[i].y, rer->r_exit[i].x); + if (dist < mindist) /* minimize distance */ + { + this = rer->r_exit[i]; + mindist = dist; + } + } + /* + * this now contains what we want to run to this time + * so we run to it. If we hit it we either want to fight it + * or stop running + */ + if (!chase(th, &this)) + { + if (ce(this, hero)) + { + return( attack(th) ); + } + else if (th->t_type != 'F') + stoprun = TRUE; + } + else if (th->t_type == 'F') + return(0); + mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); + sch = mvwinch(cw, ch_ret.y, ch_ret.x); + if (rer != NULL && (rer->r_flags & ISDARK) && sch == FLOOR + && DISTANCE(ch_ret.y, ch_ret.x, th->t_pos.y, th->t_pos.x) < 3 + && off(player, ISBLIND)) + th->t_oldch = ' '; + else + th->t_oldch = sch; + + if (cansee(unc(ch_ret)) && !on(*th, ISINVIS)) + mvwaddch(cw, ch_ret.y, ch_ret.x, th->t_type); + mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); + mvwaddch(mw, ch_ret.y, ch_ret.x, th->t_type); + th->t_pos = ch_ret; + /* + * And stop running if need be + */ + if (stoprun && ce(th->t_pos, *(th->t_dest))) + th->t_flags &= ~ISRUN; + + return(0); +} + +/* + * runto: + * Set a mosnter running after something + * or stop it from running (for when it dies) + */ + +runto(runner, spot) +register coord *runner; +coord *spot; +{ + register struct linked_list *item; + register struct thing *tp; + + /* + * If we couldn't find him, something is funny + */ + if ((item = find_mons(runner->y, runner->x)) == NULL) + { + msg("CHASER '%s'", unctrl(winat(runner->y, runner->x))); + return; + } + tp = (struct thing *) ldata(item); + /* + * Start the beastie running + */ + tp->t_dest = spot; + tp->t_flags |= ISRUN; + tp->t_flags &= ~ISHELD; +} + +/* + * chase: + * Find the spot for the chaser(er) to move closer to the + * chasee(ee). Returns TRUE if we want to keep on chasing later + * FALSE if we reach the goal. + */ + +chase(tp, ee) +struct thing *tp; +coord *ee; +{ + register int x, y; + register int dist, thisdist; + register struct linked_list *item; + register struct object *obj; + register coord *er = &tp->t_pos; + register char ch; + + /* + * If the thing is confused, let it move randomly. Invisible + * Stalkers are slightly confused all of the time, and bats are + * quite confused all the time + */ + if ((on(*tp, ISHUH) && rnd(10) < 8) || (tp->t_type == 'I' && rnd(100) < 20) + || (tp->t_type == 'B' && rnd(100) < 50)) + { + /* + * get a valid random move + */ + ch_ret = *rndmove(tp); + dist = DISTANCE(ch_ret.y, ch_ret.x, ee->y, ee->x); + /* + * Small chance that it will become un-confused + */ + if (rnd(1000) < 50) + tp->t_flags &= ~ISHUH; + } + /* + * Otherwise, find the empty spot next to the chaser that is + * closest to the chasee. + */ + else + { + register int ey, ex; + /* + * This will eventually hold where we move to get closer + * If we can't find an empty spot, we stay where we are. + */ + dist = DISTANCE(er->y, er->x, ee->y, ee->x); + ch_ret = *er; + + ey = er->y + 1; + ex = er->x + 1; + for (x = er->x - 1; x <= ex; x++) + for (y = er->y - 1; y <= ey; y++) + { + coord tryp; + + tryp.x = x; + tryp.y = y; + if (!diag_ok(er, &tryp)) + continue; + ch = winat(y, x); + if (step_ok(ch)) + { + /* + * If it is a scroll, it might be a scare monster scroll + * so we need to look it up to see what type it is. + */ + if (ch == SCROLL) + { + for (item = lvl_obj; item != NULL; item = next(item)) + { + obj = (struct object *) ldata(item); + if (y == obj->o_pos.y && x == obj->o_pos.x) + break; + } + if (item != NULL && obj->o_which == S_SCARE) + continue; + } + /* + * If we didn't find any scrolls at this place or it + * wasn't a scare scroll, then this place counts + */ + thisdist = DISTANCE(y, x, ee->y, ee->x); + if (thisdist < dist) + { + ch_ret = tryp; + dist = thisdist; + } + } + } + } + return (dist != 0); +} + +/* + * roomin: + * Find what room some coordinates are in. NULL means they aren't + * in any room. + */ + +struct room * +roomin(cp) +register coord *cp; +{ + register struct room *rp; + + for (rp = rooms; rp <= &rooms[MAXROOMS-1]; rp++) + if (inroom(rp, cp)) + return rp; + return NULL; +} + +/* + * find_mons: + * Find the monster from his corrdinates + */ + +struct linked_list * +find_mons(y, x) +register int y; +int x; +{ + register struct linked_list *item; + register struct thing *th; + + for (item = mlist; item != NULL; item = next(item)) + { + th = (struct thing *) ldata(item); + if (th->t_pos.y == y && th->t_pos.x == x) + return item; + } + return NULL; +} + +/* + * diag_ok: + * Check to see if the move is legal if it is diagonal + */ + +diag_ok(sp, ep) +register coord *sp, *ep; +{ + if (ep->x == sp->x || ep->y == sp->y) + return TRUE; + return (step_ok(mvinch(ep->y, sp->x)) && step_ok(mvinch(sp->y, ep->x))); +} + +/* + * cansee: + * returns true if the hero can see a certain coordinate. + */ + +cansee(y, x) +register int y, x; +{ + register struct room *rer; + coord tp; + + if (on(player, ISBLIND)) + return FALSE; + tp.y = y; + tp.x = x; + rer = roomin(&tp); + /* + * We can only see if the hero in the same room as + * the coordinate and the room is lit or if it is close. + */ + return (rer != NULL && rer == roomin(&hero) && !(rer->r_flags&ISDARK)) || + DISTANCE(y, x, hero.y, hero.x) < 3; +} diff --git a/command.c b/command.c new file mode 100644 index 0000000..6b8033b --- /dev/null +++ b/command.c @@ -0,0 +1,633 @@ +/* + * Read and execute the user commands + * + * @(#)command.c 3.45 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include +#include +#include "rogue.h" + +/* + * command: + * Process the user commands + */ + +command() +{ + register char ch; + register int ntimes = 1; /* Number of player moves */ + static char countch, direction, newcount = FALSE; + + + if (on(player, ISHASTE)) ntimes++; + /* + * Let the daemons start up + */ + do_daemons(BEFORE); + do_fuses(BEFORE); + while (ntimes--) + { + look(TRUE); + if (!running) + door_stop = FALSE; + status(); + lastscore = purse; + wmove(cw, hero.y, hero.x); + if (!((running || count) && jump)) + draw(cw); /* Draw screen */ + take = 0; + after = TRUE; + /* + * Read command or continue run + */ + if (wizard) + waswizard = TRUE; + if (!no_command) + { + if (running) ch = runch; + else if (count) ch = countch; + else + { + ch = readchar(cw); + if (mpos != 0 && !running) /* Erase message if its there */ + msg(""); + } + } + else ch = ' '; + if (no_command) + { + if (--no_command == 0) + msg("You can move again."); + } + else + { + /* + * check for prefixes + */ + if (isdigit(ch)) + { + count = 0; + newcount = TRUE; + while (isdigit(ch)) + { + count = count * 10 + (ch - '0'); + ch = readchar(cw); + } + countch = ch; + /* + * turn off count for commands which don't make sense + * to repeat + */ + switch (ch) { + case 'h': case 'j': case 'k': case 'l': + case 'y': case 'u': case 'b': case 'n': + case 'H': case 'J': case 'K': case 'L': + case 'Y': case 'U': case 'B': case 'N': + case 'q': case 'r': case 's': case 'f': + case 't': case 'C': case 'I': case ' ': + case 'z': case 'p': + break; + default: + count = 0; + } + } + switch (ch) + { + case 'f': + if (!on(player, ISBLIND)) + { + door_stop = TRUE; + firstmove = TRUE; + } + if (count && !newcount) + ch = direction; + else + ch = readchar(cw); + switch (ch) + { + case 'h': case 'j': case 'k': case 'l': + case 'y': case 'u': case 'b': case 'n': + ch = toupper(ch); + } + direction = ch; + } + newcount = FALSE; + /* + * execute a command + */ + if (count && !running) + count--; + switch (ch) + { + case '!' : shell(); + when 'h' : do_move(0, -1); + when 'j' : do_move(1, 0); + when 'k' : do_move(-1, 0); + when 'l' : do_move(0, 1); + when 'y' : do_move(-1, -1); + when 'u' : do_move(-1, 1); + when 'b' : do_move(1, -1); + when 'n' : do_move(1, 1); + when 'H' : do_run('h'); + when 'J' : do_run('j'); + when 'K' : do_run('k'); + when 'L' : do_run('l'); + when 'Y' : do_run('y'); + when 'U' : do_run('u'); + when 'B' : do_run('b'); + when 'N' : do_run('n'); + when 't': + if (!get_dir()) + after = FALSE; + else + missile(delta.y, delta.x); + when 'Q' : after = FALSE; quit(0); + when 'i' : after = FALSE; inventory(pack, 0); + when 'I' : after = FALSE; picky_inven(); + when 'd' : drop(); + when 'q' : quaff(); + when 'r' : read_scroll(); + when 'e' : eat(); + when 'w' : wield(); + when 'W' : wear(); + when 'T' : take_off(); + when 'P' : ring_on(); + when 'R' : ring_off(); + when 'o' : option(); + when 'c' : call(); + when '>' : after = FALSE; d_level(); + when '<' : after = FALSE; u_level(); + when '?' : after = FALSE; help(); + when '/' : after = FALSE; identify(); + when 's' : search(); + when 'z' : do_zap(FALSE); + when 'p': + if (get_dir()) + do_zap(TRUE); + else + after = FALSE; + when 'v' : msg("Rogue version %s. (mctesq was here)", release); + when CTRL('L') : after = FALSE; clearok(curscr,TRUE);draw(curscr); + when CTRL('R') : after = FALSE; msg(huh); + when 'S' : + after = FALSE; + if (save_game()) + { + wmove(cw, LINES-1, 0); + wclrtoeol(cw); + draw(cw); + endwin(); + exit(0); + } + when ' ' : ; /* Rest command */ + when CTRL('P') : + after = FALSE; + if (wizard) + { + wizard = FALSE; + msg("Not wizard any more"); + } + else + { + if (wizard = passwd()) + { + msg("You are suddenly as smart as Ken Arnold in dungeon #%d", dnum); + wizard = TRUE; + waswizard = TRUE; + } + else + msg("Sorry"); + } + when ESCAPE : /* Escape */ + door_stop = FALSE; + count = 0; + after = FALSE; + otherwise : + after = FALSE; + if (wizard) switch (ch) + { + case '@' : msg("@ %d,%d", hero.y, hero.x); + when 'C' : create_obj(); + when CTRL('I') : inventory(lvl_obj, 0); + when CTRL('W') : whatis(); + when CTRL('D') : level++; new_level(); + when CTRL('U') : level--; new_level(); + when CTRL('F') : show_win(stdscr, "--More (level map)--"); + when CTRL('X') : show_win(mw, "--More (monsters)--"); + when CTRL('T') : teleport(); + when CTRL('E') : msg("food left: %d", food_left); + when CTRL('A') : msg("%d things in your pack", inpack); + when CTRL('C') : add_pass(); + when CTRL('N') : + { + register struct linked_list *item; + + if ((item = get_item("charge", STICK)) != NULL) + ((struct object *) ldata(item))->o_charges = 10000; + } + when CTRL('H') : + { + register int i; + register struct linked_list *item; + register struct object *obj; + + for (i = 0; i < 9; i++) + raise_level(); + /* + * Give the rogue a sword (+1,+1) + */ + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + obj->o_type = WEAPON; + obj->o_which = TWOSWORD; + init_weapon(obj, SWORD); + obj->o_hplus = 1; + obj->o_dplus = 1; + add_pack(item, TRUE); + cur_weapon = obj; + /* + * And his suit of armor + */ + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + obj->o_type = ARMOR; + obj->o_which = PLATE_MAIL; + obj->o_ac = -5; + obj->o_flags |= ISKNOW; + cur_armor = obj; + add_pack(item, TRUE); + } + otherwise : + msg("Illegal command '%s'.", unctrl(ch)); + count = 0; + } + else + { + msg("Illegal command '%s'.", unctrl(ch)); + count = 0; + } + } + /* + * turn off flags if no longer needed + */ + if (!running) + door_stop = FALSE; + } + /* + * If he ran into something to take, let him pick it up. + */ + if (take != 0) + pick_up(take); + if (!running) + door_stop = FALSE; + } + /* + * Kick off the rest if the daemons and fuses + */ + if (after) + { + look(FALSE); + do_daemons(AFTER); + do_fuses(AFTER); + if (ISRING(LEFT, R_SEARCH)) + search(); + else if (ISRING(LEFT, R_TELEPORT) && rnd(100) < 2) + teleport(); + if (ISRING(RIGHT, R_SEARCH)) + search(); + else if (ISRING(RIGHT, R_TELEPORT) && rnd(100) < 2) + teleport(); + } +} + +/* + * quit: + * Have player make certain, then exit. + */ + +void +quit(int p) +{ + /* + * Reset the signal in case we got here via an interrupt + */ + if (signal(SIGINT, quit) != &quit) + mpos = 0; + msg("Really quit?"); + draw(cw); + if (readchar(cw) == 'y') + { + clear(); + move(LINES-1, 0); + draw(stdscr); + endwin(); + score(purse, 1, 0); + exit(0); + } + else + { + signal(SIGINT, quit); + wmove(cw, 0, 0); + wclrtoeol(cw); + status(); + draw(cw); + mpos = 0; + count = 0; + } +} + +/* + * search: + * Player gropes about him to find hidden things. + */ + +search() +{ + register int x, y; + register char ch; + + /* + * Look all around the hero, if there is something hidden there, + * give him a chance to find it. If its found, display it. + */ + if (on(player, ISBLIND)) + return; + for (x = hero.x - 1; x <= hero.x + 1; x++) + for (y = hero.y - 1; y <= hero.y + 1; y++) + { + ch = winat(y, x); + switch (ch) + { + case SECRETDOOR: + if (rnd(100) < 20) { + mvaddch(y, x, DOOR); + count = 0; + } + break; + case TRAP: + { + register struct trap *tp; + + if (mvwinch(cw, y, x) == TRAP) + break; + if (rnd(100) > 50) + break; + tp = trap_at(y, x); + tp->tr_flags |= ISFOUND; + mvwaddch(cw, y, x, TRAP); + count = 0; + running = FALSE; + msg(tr_name(tp->tr_type)); + } + } + } +} + +/* + * help: + * Give single character help, or the whole mess if he wants it + */ + +help() +{ + register struct h_list *strp = helpstr; + register char helpch; + register int cnt; + + msg("Character you want help for (* for all): "); + helpch = readchar(cw); + mpos = 0; + /* + * If its not a *, print the right help string + * or an error if he typed a funny character. + */ + if (helpch != '*') + { + wmove(cw, 0, 0); + while (strp->h_ch) + { + if (strp->h_ch == helpch) + { + msg("%s%s", unctrl(strp->h_ch), strp->h_desc); + break; + } + strp++; + } + if (strp->h_ch != helpch) + msg("Unknown character '%s'", unctrl(helpch)); + return; + } + /* + * Here we print help for everything. + * Then wait before we return to command mode + */ + wclear(hw); + cnt = 0; + while (strp->h_ch) + { + mvwaddstr(hw, cnt % 23, cnt > 22 ? 40 : 0, unctrl(strp->h_ch)); + waddstr(hw, strp->h_desc); + cnt++; + strp++; + } + wmove(hw, LINES-1, 0); + wprintw(hw, "--Press space to continue--"); + draw(hw); + wait_for(hw,' '); + wclear(hw); + draw(hw); + wmove(cw, 0, 0); + wclrtoeol(cw); + status(); + touchwin(cw); +} + +/* + * identify: + * Tell the player what a certain thing is. + */ + +identify() +{ + register char ch, *str; + + msg("What do you want identified? "); + ch = readchar(cw); + mpos = 0; + if (ch == ESCAPE) + { + msg(""); + return; + } + if (isalpha(ch) && isupper(ch)) + str = monsters[ch-'A'].m_name; + else switch(ch) + { + case '|': + case '-': + str = "wall of a room"; + when GOLD: str = "gold"; + when STAIRS : str = "passage leading down"; + when DOOR: str = "door"; + when FLOOR: str = "room floor"; + when PLAYER: str = "you"; + when PASSAGE: str = "passage"; + when TRAP: str = "trap"; + when POTION: str = "potion"; + when SCROLL: str = "scroll"; + when FOOD: str = "food"; + when WEAPON: str = "weapon"; + when ' ' : str = "solid rock"; + when ARMOR: str = "armor"; + when AMULET: str = "The Amulet of Yendor"; + when RING: str = "ring"; + when STICK: str = "wand or staff"; + otherwise: str = "unknown character"; + } + msg("'%s' : %s", unctrl(ch), str); +} + +/* + * d_level: + * He wants to go down a level + */ + +d_level() +{ + if (winat(hero.y, hero.x) != STAIRS) + msg("I see no way down."); + else + { + level++; + new_level(); + } +} + +/* + * u_level: + * He wants to go up a level + */ + +u_level() +{ + if (winat(hero.y, hero.x) == STAIRS) + { + if (amulet) + { + level--; + if (level == 0) + total_winner(); + new_level(); + msg("You feel a wrenching sensation in your gut."); + return; + } + } + msg("I see no way up."); +} + +/* + * Let him escape for a while + */ + +shell() +{ + /* + * Set the terminal back to original mode + */ + wclear(hw); + wmove(hw, LINES-1, 0); + draw(hw); + endwin(); + in_shell = TRUE; + fflush(stdout); + + md_shellescape(); + + printf("\n[Press return to continue]"); + fflush(stdout); + noecho(); + crmode(); + in_shell = FALSE; + wait_for(cw,'\n'); + clearok(cw, TRUE); + touchwin(cw); + draw(cw); +} + +/* + * allow a user to call a potion, scroll, or ring something + */ +call() +{ + register struct object *obj; + register struct linked_list *item; + register char **guess, *elsewise; + register bool *know; + + item = get_item("call", CALLABLE); + /* + * Make certain that it is somethings that we want to wear + */ + if (item == NULL) + return; + obj = (struct object *) ldata(item); + switch (obj->o_type) + { + case RING: + guess = r_guess; + know = r_know; + elsewise = (r_guess[obj->o_which] != NULL ? + r_guess[obj->o_which] : r_stones[obj->o_which]); + when POTION: + guess = p_guess; + know = p_know; + elsewise = (p_guess[obj->o_which] != NULL ? + p_guess[obj->o_which] : p_colors[obj->o_which]); + when SCROLL: + guess = s_guess; + know = s_know; + elsewise = (s_guess[obj->o_which] != NULL ? + s_guess[obj->o_which] : s_names[obj->o_which]); + when STICK: + guess = ws_guess; + know = ws_know; + elsewise = (ws_guess[obj->o_which] != NULL ? + ws_guess[obj->o_which] : ws_made[obj->o_which]); + otherwise: + msg("You can't call that anything"); + return; + } + if (know[obj->o_which]) + { + msg("That has already been identified"); + return; + } + if (terse) + addmsg("C"); + else + addmsg("Was c"); + msg("alled \"%s\"", elsewise); + if (terse) + msg("Call it: "); + else + msg("What do you want to call it? "); + strcpy(prbuf, elsewise); + if (get_str(prbuf, cw) == NORM) + { + if (guess[obj->o_which] != NULL) + free(guess[obj->o_which]); + guess[obj->o_which] = malloc((unsigned int) strlen(prbuf) + 1); + if (guess[obj->o_which] != NULL) + strcpy(guess[obj->o_which], prbuf); + } +} diff --git a/daemon.c b/daemon.c new file mode 100644 index 0000000..7a26866 --- /dev/null +++ b/daemon.c @@ -0,0 +1,201 @@ +/* + * Contains functions for dealing with things that happen in the + * future. + * + * @(#)daemon.c 3.3 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" + +#define EMPTY 0 +#define DAEMON -1 +#define MAXDAEMONS 20 + +#define _X_ { EMPTY } + +struct delayed_action d_list[MAXDAEMONS] = { + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, +}; + +/* + * d_slot: + * Find an empty slot in the daemon/fuse list + */ +struct delayed_action * +d_slot() +{ + register int i; + register struct delayed_action *dev; + + for (i = 0, dev = d_list; i < MAXDAEMONS; i++, dev++) + if (dev->d_type == EMPTY) + return dev; + debug("Ran out of fuse slots"); + return NULL; +} + +/* + * find_slot: + * Find a particular slot in the table + */ + +struct delayed_action * +find_slot(func) +register int (*func)(); +{ + register int i; + register struct delayed_action *dev; + + for (i = 0, dev = d_list; i < MAXDAEMONS; i++, dev++) + if (dev->d_type != EMPTY && func == dev->d_func) + return dev; + return NULL; +} + +/* + * daemon: + * Start a daemon, takes a function. + */ + +start_daemon(func, arg, type) +int (*func)(), arg, type; +{ + register struct delayed_action *dev; + + dev = d_slot(); + + if (dev != NULL) + { + dev->d_type = type; + dev->d_func = func; + dev->d_arg = arg; + dev->d_time = DAEMON; + } +} + +/* + * kill_daemon: + * Remove a daemon from the list + */ + +kill_daemon(func) +int (*func)(); +{ + register struct delayed_action *dev; + + if ((dev = find_slot(func)) == NULL) + return; + /* + * Take it out of the list + */ + dev->d_type = EMPTY; +} + +/* + * do_daemons: + * Run all the daemons that are active with the current flag, + * passing the argument to the function. + */ + +do_daemons(flag) +register int flag; +{ + register struct delayed_action *dev; + + /* + * Loop through the devil list + */ + for (dev = d_list; dev <= &d_list[MAXDAEMONS-1]; dev++) + /* + * Executing each one, giving it the proper arguments + */ + if (dev->d_type == flag && dev->d_time == DAEMON) + (*dev->d_func)(dev->d_arg); +} + +/* + * fuse: + * Start a fuse to go off in a certain number of turns + */ + +fuse(func, arg, time, type) +int (*func)(), arg, time, type; +{ + register struct delayed_action *wire; + + wire = d_slot(); + + if (wire != NULL) + { + wire->d_type = type; + wire->d_func = func; + wire->d_arg = arg; + wire->d_time = time; + } +} + +/* + * lengthen: + * Increase the time until a fuse goes off + */ + +lengthen(func, xtime) +int (*func)(); +int xtime; +{ + register struct delayed_action *wire; + + if ((wire = find_slot(func)) == NULL) + return; + wire->d_time += xtime; +} + +/* + * extinguish: + * Put out a fuse + */ + +extinguish(func) +int (*func)(); +{ + register struct delayed_action *wire; + + if ((wire = find_slot(func)) == NULL) + return; + wire->d_type = EMPTY; +} + +/* + * do_fuses: + * Decrement counters and start needed fuses + */ + +do_fuses(flag) +register int flag; +{ + register struct delayed_action *wire; + + /* + * Step though the list + */ + for (wire = d_list; wire <= &d_list[MAXDAEMONS-1]; wire++) + { + /* + * Decrementing counters and starting things we want. We also need + * to remove the fuse from the list once it has gone off. + */ + if (flag == wire->d_type && wire->d_time > 0 && --wire->d_time == 0) + { + wire->d_type = EMPTY; + (*wire->d_func)(wire->d_arg); + } + } +} diff --git a/daemons.c b/daemons.c new file mode 100644 index 0000000..380e5c4 --- /dev/null +++ b/daemons.c @@ -0,0 +1,169 @@ +/* + * All the daemon and fuse functions are in here + * + * @(#)daemons.c 3.7 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" + +/* + * doctor: + * A healing daemon that restors hit points after rest + */ + +doctor() +{ + register int lv, ohp; + + lv = pstats.s_lvl; + ohp = pstats.s_hpt; + quiet++; + if (lv < 8) + { + if (quiet > 20 - lv*2) + pstats.s_hpt++; + } + else + if (quiet >= 3) + pstats.s_hpt += rnd(lv - 7)+1; + if (ISRING(LEFT, R_REGEN)) + pstats.s_hpt++; + if (ISRING(RIGHT, R_REGEN)) + pstats.s_hpt++; + if (ohp != pstats.s_hpt) + { + if (pstats.s_hpt > max_hp) + pstats.s_hpt = max_hp; + quiet = 0; + } +} + +/* + * Swander: + * Called when it is time to start rolling for wandering monsters + */ + +swander() +{ + start_daemon(rollwand, 0, BEFORE); +} + +/* + * rollwand: + * Called to roll to see if a wandering monster starts up + */ + +int between = 0; + +rollwand() +{ + if (++between >= 4) + { + if (roll(1, 6) == 4) + { + wanderer(); + kill_daemon(rollwand); + fuse(swander, 0, WANDERTIME, BEFORE); + } + between = 0; + } +} + +/* + * unconfuse: + * Release the poor player from his confusion + */ + +unconfuse() +{ + player.t_flags &= ~ISHUH; + msg("You feel less confused now"); +} + + +/* + * unsee: + * He lost his see invisible power + */ + +unsee() +{ + player.t_flags &= ~CANSEE; +} + +/* + * sight: + * He gets his sight back + */ + +sight() +{ + if (on(player, ISBLIND)) + { + extinguish(sight); + player.t_flags &= ~ISBLIND; + light(&hero); + msg("The veil of darkness lifts"); + } +} + +/* + * nohaste: + * End the hasting + */ + +nohaste() +{ + player.t_flags &= ~ISHASTE; + msg("You feel yourself slowing down."); +} + +/* + * digest the hero's food + */ +stomach() +{ + register int oldfood; + + if (food_left <= 0) + { + /* + * the hero is fainting + */ + if (no_command || rnd(100) > 20) + return; + no_command = rnd(8)+4; + if (!terse) + addmsg("You feel too weak from lack of food. "); + msg("You faint"); + running = FALSE; + count = 0; + hungry_state = 3; + } + else + { + oldfood = food_left; + food_left -= ring_eat(LEFT) + ring_eat(RIGHT) + 1 - amulet; + + if (food_left < MORETIME && oldfood >= MORETIME) + { + msg("You are starting to feel weak"); + hungry_state = 2; + } + else if (food_left < 2 * MORETIME && oldfood >= 2 * MORETIME) + { + if (!terse) + msg("You are starting to get hungry"); + else + msg("Getting hungry"); + hungry_state = 1; + } + } +} diff --git a/fight.c b/fight.c new file mode 100644 index 0000000..516d8b7 --- /dev/null +++ b/fight.c @@ -0,0 +1,742 @@ +/* + * All the fighting gets done here + * + * @(#)fight.c 3.28 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +long e_levels[] = { + 10L,20L,40L,80L,160L,320L,640L,1280L,2560L,5120L,10240L,20480L, + 40920L, 81920L, 163840L, 327680L, 655360L, 1310720L, 2621440L, 0L }; + +/* + * fight: + * The player attacks the monster. + */ + +fight(mp, mn, weap, thrown) +register coord *mp; +char mn; +struct object *weap; +bool thrown; +{ + register struct thing *tp; + register struct linked_list *item; + register bool did_hit = TRUE; + + /* + * Find the monster we want to fight + */ + if ((item = find_mons(mp->y, mp->x)) == NULL) + { + debug("Fight what @ %d,%d", mp->y, mp->x); + return(0); + } + tp = (struct thing *) ldata(item); + /* + * Since we are fighting, things are not quiet so no healing takes + * place. + */ + quiet = 0; + runto(mp, &hero); + /* + * Let him know it was really a mimic (if it was one). + */ + if (tp->t_type == 'M' && tp->t_disguise != 'M' && off(player, ISBLIND)) + { + msg("Wait! That's a mimic!"); + tp->t_disguise = 'M'; + did_hit = thrown; + } + if (did_hit) + { + register char *mname; + + did_hit = FALSE; + if (on(player, ISBLIND)) + mname = "it"; + else + mname = monsters[mn-'A'].m_name; + if (roll_em(&pstats, &tp->t_stats, weap, thrown)) + { + did_hit = TRUE; + if (thrown) + thunk(weap, mname); + else + hit(NULL, mname); + if (on(player, CANHUH)) + { + msg("Your hands stop glowing red"); + msg("The %s appears confused.", mname); + tp->t_flags |= ISHUH; + player.t_flags &= ~CANHUH; + } + if (tp->t_stats.s_hpt <= 0) + killed(item, TRUE); + } + else + if (thrown) + bounce(weap, mname); + else + miss(NULL, mname); + } + count = 0; + return did_hit; +} + +/* + * attack: + * The monster attacks the player + */ + +attack(mp) +register struct thing *mp; +{ + register char *mname; + + /* + * Since this is an attack, stop running and any healing that was + * going on at the time. + */ + running = FALSE; + quiet = 0; + if (mp->t_type == 'M' && off(player, ISBLIND)) + mp->t_disguise = 'M'; + if (on(player, ISBLIND)) + mname = "it"; + else + mname = monsters[mp->t_type-'A'].m_name; + if (roll_em(&mp->t_stats, &pstats, NULL, FALSE)) + { + if (mp->t_type != 'E') + hit(mname, NULL); + if (pstats.s_hpt <= 0) + death(mp->t_type); /* Bye bye life ... */ + if (off(*mp, ISCANC)) + switch (mp->t_type) + { + case 'R': + /* + * If a rust monster hits, you lose armor + */ + if (cur_armor != NULL && cur_armor->o_ac < 9) + { + if (!terse) + msg("Your armor appears to be weaker now. Oh my!"); + else + msg("Your armor weakens"); + cur_armor->o_ac++; + } + when 'E': + /* + * The gaze of the floating eye hypnotizes you + */ + if (on(player, ISBLIND)) + break; + if (!no_command) + { + addmsg("You are transfixed"); + if (!terse) + addmsg(" by the gaze of the floating eye."); + endmsg(); + } + no_command += rnd(2)+2; + when 'A': + /* + * Ants have poisonous bites + */ + if (!save(VS_POISON)) + if (!ISWEARING(R_SUSTSTR)) + { + chg_str(-1); + if (!terse) + msg("You feel a sting in your arm and now feel weaker"); + else + msg("A sting has weakened you"); + } + else + if (!terse) + msg("A sting momentarily weakens you"); + else + msg("Sting has no effect"); + when 'W': + /* + * Wraiths might drain energy levels + */ + if (rnd(100) < 15) + { + int fewer; + + if (pstats.s_exp == 0) + death('W'); /* All levels gone */ + msg("You suddenly feel weaker."); + if (--pstats.s_lvl == 0) + { + pstats.s_exp = 0; + pstats.s_lvl = 1; + } + else + pstats.s_exp = e_levels[pstats.s_lvl-1]+1; + fewer = roll(1, 10); + pstats.s_hpt -= fewer; + max_hp -= fewer; + if (pstats.s_hpt < 1) + pstats.s_hpt = 1; + if (max_hp < 1) + death('W'); + } + when 'F': + /* + * Violet fungi stops the poor guy from moving + */ + player.t_flags |= ISHELD; + sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dd1",++fung_hit); + when 'L': + { + /* + * Leperachaun steals some gold + */ + register long lastpurse; + + lastpurse = purse; + purse -= GOLDCALC; + if (!save(VS_MAGIC)) + purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; + if (purse < 0) + purse = 0; + if (purse != lastpurse) + msg("Your purse feels lighter"); + remove_monster(&mp->t_pos, find_mons(mp->t_pos.y, mp->t_pos.x)); + mp = NULL; + } + when 'N': + { + register struct linked_list *list, *steal; + register struct object *obj; + register int nobj; + + /* + * Nymph's steal a magic item, look through the pack + * and pick out one we like. + */ + steal = NULL; + for (nobj = 0, list = pack; list != NULL; list = next(list)) + { + obj = (struct object *) ldata(list); + if (obj != cur_armor && + obj != cur_weapon && + obj != cur_ring[LEFT] && + obj != cur_ring[RIGHT] && /* Nymph bug fix */ + is_magic(obj) && + rnd(++nobj) == 0) + steal = list; + } + if (steal != NULL) + { + register struct object *obj; + + obj = (struct object *) ldata(steal); + remove_monster(&mp->t_pos, find_mons(mp->t_pos.y, mp->t_pos.x)); + mp = NULL; + if (obj->o_count > 1 && obj->o_group == 0) + { + register int oc; + + oc = obj->o_count; + obj->o_count = 1; + msg("She stole %s!", inv_name(obj, TRUE)); + obj->o_count = oc - 1; + } + else + { + msg("She stole %s!", inv_name(obj, TRUE)); + detach(pack, steal); + discard(steal); + } + inpack--; + } + } + otherwise: + break; + } + } + else if (mp->t_type != 'E') + { + if (mp->t_type == 'F') + { + pstats.s_hpt -= fung_hit; + if (pstats.s_hpt <= 0) + death(mp->t_type); /* Bye bye life ... */ + } + miss(mname, NULL); + } + /* + * Check to see if this is a regenerating monster and let it heal if + * it is. + */ + if ((mp != NULL) && (on(*mp, ISREGEN) && rnd(100) < 33)) + mp->t_stats.s_hpt++; + if (fight_flush) + { + flush_type(); /* flush typeahead */ + } + count = 0; + status(); + + if (mp == NULL) + return(-1); + else + return(0); +} + +/* + * swing: + * returns true if the swing hits + */ + +swing(at_lvl, op_arm, wplus) +int at_lvl, op_arm, wplus; +{ + register int res = rnd(20)+1; + register int need = (21-at_lvl)-op_arm; + + return (res+wplus >= need); +} + +/* + * check_level: + * Check to see if the guy has gone up a level. + */ + +check_level() +{ + register int i, add; + + for (i = 0; e_levels[i] != 0; i++) + if (e_levels[i] > pstats.s_exp) + break; + i++; + if (i > pstats.s_lvl) + { + add = roll(i-pstats.s_lvl,10); + max_hp += add; + if ((pstats.s_hpt += add) > max_hp) + pstats.s_hpt = max_hp; + msg("Welcome to level %d", i); + } + pstats.s_lvl = i; +} + +/* + * roll_em: + * Roll several attacks + */ + +roll_em(att, def, weap, hurl) +struct stats *att, *def; +struct object *weap; +bool hurl; +{ + register char *cp; + register int ndice, nsides, def_arm; + register bool did_hit = FALSE; + register int prop_hplus, prop_dplus; + + prop_hplus = prop_dplus = 0; + if (weap == NULL) + cp = att->s_dmg; + else if (hurl) + if ((weap->o_flags&ISMISL) && cur_weapon != NULL && + cur_weapon->o_which == weap->o_launch) + { + cp = weap->o_hurldmg; + prop_hplus = cur_weapon->o_hplus; + prop_dplus = cur_weapon->o_dplus; + } + else + cp = (weap->o_flags&ISMISL ? weap->o_damage : weap->o_hurldmg); + else + { + cp = weap->o_damage; + /* + * Drain a staff of striking + */ + if (weap->o_type == STICK && weap->o_which == WS_HIT + && weap->o_charges == 0) + { + strcpy(weap->o_damage,"0d0"); + weap->o_hplus = weap->o_dplus = 0; + } + } + for (;;) + { + int damage; + int hplus = prop_hplus + (weap == NULL ? 0 : weap->o_hplus); + int dplus = prop_dplus + (weap == NULL ? 0 : weap->o_dplus); + + if (weap == cur_weapon) + { + if (ISRING(LEFT, R_ADDDAM)) + dplus += cur_ring[LEFT]->o_ac; + else if (ISRING(LEFT, R_ADDHIT)) + hplus += cur_ring[LEFT]->o_ac; + if (ISRING(RIGHT, R_ADDDAM)) + dplus += cur_ring[RIGHT]->o_ac; + else if (ISRING(RIGHT, R_ADDHIT)) + hplus += cur_ring[RIGHT]->o_ac; + } + ndice = atoi(cp); + if ((cp = strchr(cp, 'd')) == NULL) + break; + nsides = atoi(++cp); + if (def == &pstats) + { + if (cur_armor != NULL) + def_arm = cur_armor->o_ac; + else + def_arm = def->s_arm; + if (ISRING(LEFT, R_PROTECT)) + def_arm -= cur_ring[LEFT]->o_ac; + else if (ISRING(RIGHT, R_PROTECT)) + def_arm -= cur_ring[RIGHT]->o_ac; + } + else + def_arm = def->s_arm; + if (swing(att->s_lvl, def_arm, hplus+str_plus(&att->s_str))) + { + register int proll; + + proll = roll(ndice, nsides); + if (ndice + nsides > 0 && proll < 1) + debug("Damage for %dd%d came out %d.", ndice, nsides, proll); + damage = dplus + proll + add_dam(&att->s_str); + def->s_hpt -= max(0, damage); + did_hit = TRUE; + } + if ((cp = strchr(cp, '/')) == NULL) + break; + cp++; + } + return did_hit; +} + +/* + * prname: + * The print name of a combatant + */ + +char * +prname(who, upper) +register char *who; +bool upper; +{ + static char tbuf[80]; + + *tbuf = '\0'; + if (who == 0) + strcpy(tbuf, "you"); + else if (on(player, ISBLIND)) + strcpy(tbuf, "it"); + else + { + strcpy(tbuf, "the "); + strcat(tbuf, who); + } + if (upper) + *tbuf = toupper(*tbuf); + return tbuf; +} + +/* + * hit: + * Print a message to indicate a succesful hit + */ + +hit(er, ee) +register char *er, *ee; +{ + register char *s; + + addmsg(prname(er, TRUE)); + if (terse) + s = " hit."; + else + switch (rnd(4)) + { + case 0: s = " scored an excellent hit on "; + when 1: s = " hit "; + when 2: s = (er == 0 ? " have injured " : " has injured "); + when 3: s = (er == 0 ? " swing and hit " : " swings and hits "); + } + addmsg(s); + if (!terse) + addmsg(prname(ee, FALSE)); + endmsg(); +} + +/* + * miss: + * Print a message to indicate a poor swing + */ + +miss(er, ee) +register char *er, *ee; +{ + register char *s; + + addmsg(prname(er, TRUE)); + switch (terse ? 0 : rnd(4)) + { + case 0: s = (er == 0 ? " miss" : " misses"); + when 1: s = (er == 0 ? " swing and miss" : " swings and misses"); + when 2: s = (er == 0 ? " barely miss" : " barely misses"); + when 3: s = (er == 0 ? " don't hit" : " doesn't hit"); + } + addmsg(s); + if (!terse) + addmsg(" %s", prname(ee, FALSE)); + endmsg(); +} + +/* + * save_throw: + * See if a creature save against something + */ +save_throw(which, tp) +int which; +struct thing *tp; +{ + register int need; + + need = 14 + which - tp->t_stats.s_lvl / 2; + return (roll(1, 20) >= need); +} + +/* + * save: + * See if he saves against various nasty things + */ + +save(which) +int which; +{ + return save_throw(which, &player); +} + +/* + * str_plus: + * compute bonus/penalties for strength on the "to hit" roll + */ + +str_plus(str) +register str_t *str; +{ + if (str->st_str == 18) + { + if (str->st_add == 100) + return 3; + if (str->st_add > 50) + return 2; + } + if (str->st_str >= 17) + return 1; + if (str->st_str > 6) + return 0; + return str->st_str - 7; +} + +/* + * add_dam: + * compute additional damage done for exceptionally high or low strength + */ + + add_dam(str) + register str_t *str; + { + if (str->st_str == 18) + { + if (str->st_add == 100) + return 6; + if (str->st_add > 90) + return 5; + if (str->st_add > 75) + return 4; + if (str->st_add != 0) + return 3; + return 2; + } + if (str->st_str > 15) + return 1; + if (str->st_str > 6) + return 0; + return str->st_str - 7; +} + +/* + * raise_level: + * The guy just magically went up a level. + */ + +raise_level() +{ + pstats.s_exp = e_levels[pstats.s_lvl-1] + 1L; + check_level(); +} + +/* + * thunk: + * A missile hits a monster + */ + +thunk(weap, mname) +register struct object *weap; +register char *mname; +{ + if (weap->o_type == WEAPON) + msg("The %s hits the %s", w_names[weap->o_which], mname); + else + msg("You hit the %s.", mname); +} + +/* + * bounce: + * A missile misses a monster + */ + +bounce(weap, mname) +register struct object *weap; +register char *mname; +{ + if (weap->o_type == WEAPON) + msg("The %s misses the %s", w_names[weap->o_which], mname); + else + msg("You missed the %s.", mname); +} + +/* + * remove a monster from the screen + */ +remove_monster(mp, item) +register coord *mp; +register struct linked_list *item; +{ + mvwaddch(mw, mp->y, mp->x, ' '); + mvwaddch(cw, mp->y, mp->x, ((struct thing *) ldata(item))->t_oldch); + detach(mlist, item); + discard(item); +} + +/* + * is_magic: + * Returns true if an object radiates magic + */ + +is_magic(obj) +register struct object *obj; +{ + switch (obj->o_type) + { + case ARMOR: + return obj->o_ac != a_class[obj->o_which]; + when WEAPON: + return obj->o_hplus != 0 || obj->o_dplus != 0; + when POTION: + case SCROLL: + case STICK: + case RING: + case AMULET: + return TRUE; + } + return FALSE; +} + +/* + * killed: + * Called to put a monster to death + */ + +killed(item, pr) +register struct linked_list *item; +bool pr; +{ + register struct thing *tp; + register struct linked_list *pitem, *nexti; + + tp = (struct thing *) ldata(item); + if (pr) + { + addmsg(terse ? "Defeated " : "You have defeated "); + if (on(player, ISBLIND)) + msg("it."); + else + { + if (!terse) + addmsg("the "); + msg("%s.", monsters[tp->t_type-'A'].m_name); + } + } + pstats.s_exp += tp->t_stats.s_exp; + /* + * Do adjustments if he went up a level + */ + check_level(); + /* + * If the monster was a violet fungi, un-hold him + */ + switch (tp->t_type) + { + case 'F': + player.t_flags &= ~ISHELD; + fung_hit = 0; + strcpy(monsters['F'-'A'].m_stats.s_dmg, "000d0"); + when 'L': + { + register struct room *rp; + + if ((rp = roomin(&tp->t_pos)) == NULL) + break; + if (rp->r_goldval != 0 || fallpos(&tp->t_pos,&rp->r_gold,FALSE)) + { + rp->r_goldval += GOLDCALC; + if (save(VS_MAGIC)) + rp->r_goldval += GOLDCALC + GOLDCALC + + GOLDCALC + GOLDCALC; + mvwaddch(stdscr, rp->r_gold.y, rp->r_gold.x, GOLD); + if (!(rp->r_flags & ISDARK)) + { + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + } + } + } + /* + * Empty the monsters pack + */ + pitem = tp->t_pack; + while (pitem != NULL) + { + register struct object *obj; + + nexti = next(tp->t_pack); + obj = (struct object *) ldata(pitem); + obj->o_pos = tp->t_pos; + detach(tp->t_pack, pitem); + fall(pitem, FALSE); + pitem = nexti; + } + /* + * Get rid of the monster. + */ + remove_monster(&tp->t_pos, item); +} diff --git a/init.c b/init.c new file mode 100644 index 0000000..031ff3e --- /dev/null +++ b/init.c @@ -0,0 +1,589 @@ +/* + * global variable initializaton + * + * @(#)init.c 3.33 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +bool playing = TRUE, running = FALSE, wizard = FALSE; +bool notify = TRUE, fight_flush = FALSE, terse = FALSE, door_stop = FALSE; +bool jump = FALSE, slow_invent = FALSE, firstmove = FALSE, askme = FALSE; +bool amulet = FALSE, in_shell = FALSE; +struct linked_list *lvl_obj = NULL, *mlist = NULL; +struct object *cur_weapon = NULL; +int mpos = 0, no_move = 0, no_command = 0, level = 1, purse = 0, inpack = 0; +int total = 0, no_food = 0, count = 0, fung_hit = 0, quiet = 0; +int food_left = HUNGERTIME, group = 1, hungry_state = 0; +int lastscore = -1; + +struct thing player; +struct room rooms[MAXROOMS]; +struct room *oldrp; +struct stats max_stats; +struct object *cur_armor; +struct object *cur_ring[2]; +bool after; +bool waswizard; +coord oldpos; /* Position before last look() call */ +coord delta; /* Change indicated to get_dir() */ + +bool s_know[MAXSCROLLS]; /* Does he know what a scroll does */ +bool p_know[MAXPOTIONS]; /* Does he know what a potion does */ +bool r_know[MAXRINGS]; /* Does he know what a ring does + */ +bool ws_know[MAXSTICKS]; /* Does he know what a stick does */ + +char take; /* Thing the rogue is taking */ +char runch; /* Direction player is running */ +char whoami[80]; /* Name of player */ +char fruit[80]; /* Favorite fruit */ +char huh[80]; /* The last message printed */ +int dnum; /* Dungeon number */ +char *s_names[MAXSCROLLS]; /* Names of the scrolls */ +char *p_colors[MAXPOTIONS]; /* Colors of the potions */ +char *r_stones[MAXRINGS]; /* Stone settings of the rings */ +char *a_names[MAXARMORS]; /* Names of armor types */ +char *ws_made[MAXSTICKS]; /* What sticks are made of */ +char *s_guess[MAXSCROLLS]; /* Players guess at what scroll is */ +char *p_guess[MAXPOTIONS]; /* Players guess at what potion is */ +char *r_guess[MAXRINGS]; /* Players guess at what ring is */ +char *ws_guess[MAXSTICKS]; /* Players guess at what wand is */ +char *ws_type[MAXSTICKS]; /* Is it a wand or a staff */ +char file_name[80]; /* Save file name */ +char home[80]; /* User's home directory */ +char prbuf[80]; /* Buffer for sprintfs */ +int max_hp; /* Player's max hit points */ +int ntraps; /* Number of traps on this level */ +int max_level; /* Deepest player has gone */ +int seed; /* Random number seed */ + +struct trap traps[MAXTRAPS]; + + +#define ___ 1 +#define _x {1,1} +struct monster monsters[26] = { + /* Name CARRY FLAG str, exp, lvl, amr, hpt, dmg */ + { "giant ant", 0, ISMEAN, { _x, 10, 2, 3, ___, "1d6" } }, + { "bat", 0, 0, { _x, 1, 1, 3, ___, "1d2" } }, + { "centaur", 15, 0, { _x, 15, 4, 4, ___, "1d6/1d6" } }, + { "dragon", 100, ISGREED,{ _x,9000, 10, -1, ___, "1d8/1d8/3d10" } }, + { "floating eye",0, 0, { _x, 5, 1, 9, ___, "0d0" } }, + { "violet fungi",0, ISMEAN, { _x, 85, 8, 3, ___, "000d0" } }, + { "gnome", 10, 0, { _x, 8, 1, 5, ___, "1d6" } }, + { "hobgoblin", 0, ISMEAN, { _x, 3, 1, 5, ___, "1d8" } }, + { "invisible stalker",0,ISINVIS,{ _x,120, 8, 3, ___, "4d4" } }, + { "jackal", 0, ISMEAN, { _x, 2, 1, 7, ___, "1d2" } }, + { "kobold", 0, ISMEAN, { _x, 1, 1, 7, ___, "1d4" } }, + { "leprechaun", 0, 0, { _x, 10, 3, 8, ___, "1d1" } }, + { "mimic", 30, 0, { _x,140, 7, 7, ___, "3d4" } }, + { "nymph", 100, 0, { _x, 40, 3, 9, ___, "0d0" } }, + { "orc", 15, ISBLOCK,{ _x, 5, 1, 6, ___, "1d8" } }, + { "purple worm", 70, 0, { _x,7000, 15, 6, ___, "2d12/2d4" } }, + { "quasit", 30, ISMEAN, { _x, 35, 3, 2, ___, "1d2/1d2/1d4" } }, + { "rust monster",0, ISMEAN, { _x, 25, 5, 2, ___, "0d0/0d0" } }, + { "snake", 0, ISMEAN, { _x, 3, 1, 5, ___, "1d3" } }, + { "troll", 50, ISREGEN|ISMEAN,{ _x, 55, 6, 4, ___, "1d8/1d8/2d6" } }, + { "umber hulk", 40, ISMEAN, { _x,130, 8, 2, ___, "3d4/3d4/2d5" } }, + { "vampire", 20, ISREGEN|ISMEAN,{ _x,380, 8, 1, ___, "1d10" } }, + { "wraith", 0, 0, { _x, 55, 5, 4, ___, "1d6" } }, + { "xorn", 0, ISMEAN, { _x,120, 7, -2, ___, "1d3/1d3/1d3/4d6" } }, + { "yeti", 30, 0, { _x, 50, 4, 6, ___, "1d6/1d6" } }, + { "zombie", 0, ISMEAN, { _x, 7, 2, 8, ___, "1d8" } } +}; +#undef ___ + +/* + * init_player: + * roll up the rogue + */ + +init_player() +{ + pstats.s_lvl = 1; + pstats.s_exp = 0L; + max_hp = pstats.s_hpt = 12; + if (rnd(100) == 7) + { + pstats.s_str.st_str = 18; + pstats.s_str.st_add = rnd(100) + 1; + } + else + { + pstats.s_str.st_str = 16; + pstats.s_str.st_add = 0; + } + strcpy(pstats.s_dmg,"1d4"); + pstats.s_arm = 10; + max_stats = pstats; + pack = NULL; +} + +/* + * Contains defintions and functions for dealing with things like + * potions and scrolls + */ + +char *rainbow[] = { + "red", + "blue", + "green", + "yellow", + "black", + "brown", + "orange", + "pink", + "purple", + "grey", + "white", + "silver", + "gold", + "violet", + "clear", + "vermilion", + "ecru", + "turquoise", + "magenta", + "amber", + "topaz", + "plaid", + "tan", + "tangerine" +}; + +#define NCOLORS (sizeof rainbow / sizeof (char *)) +const int cNCOLORS = NCOLORS; + +char *sylls[] = { + "a", "ab", "ag", "aks", "ala", "an", "ankh", "app", "arg", "arze", + "ash", "ban", "bar", "bat", "bek", "bie", "bin", "bit", "bjor", + "blu", "bot", "bu", "byt", "comp", "con", "cos", "cre", "dalf", + "dan", "den", "do", "e", "eep", "el", "eng", "er", "ere", "erk", + "esh", "evs", "fa", "fid", "for", "fri", "fu", "gan", "gar", + "glen", "gop", "gre", "ha", "he", "hyd", "i", "ing", "ion", "ip", + "ish", "it", "ite", "iv", "jo", "kho", "kli", "klis", "la", "lech", + "man", "mar", "me", "mi", "mic", "mik", "mon", "mung", "mur", + "nej", "nelg", "nep", "ner", "nes", "nes", "nih", "nin", "o", "od", + "ood", "org", "orn", "ox", "oxy", "pay", "pet", "ple", "plu", "po", + "pot", "prok", "re", "rea", "rhov", "ri", "ro", "rog", "rok", "rol", + "sa", "san", "sat", "see", "sef", "seh", "shu", "ski", "sna", + "sne", "snik", "sno", "so", "sol", "sri", "sta", "sun", "ta", + "tab", "tem", "ther", "ti", "tox", "trol", "tue", "turs", "u", + "ulk", "um", "un", "uni", "ur", "val", "viv", "vly", "vom", "wah", + "wed", "werg", "wex", "whon", "wun", "xo", "y", "yot", "yu", + "zant", "zap", "zeb", "zim", "zok", "zon", "zum", +}; + +char *stones[] = { + "agate", + "alexandrite", + "amethyst", + "carnelian", + "diamond", + "emerald", + "granite", + "jade", + "kryptonite", + "lapus lazuli", + "moonstone", + "obsidian", + "onyx", + "opal", + "pearl", + "ruby", + "saphire", + "tiger eye", + "topaz", + "turquoise", +}; + +#define NSTONES (sizeof stones / sizeof (char *)) +const int cNSTONES = NSTONES; + +char *wood[] = { + "avocado wood", + "balsa", + "banyan", + "birch", + "cedar", + "cherry", + "cinnibar", + "driftwood", + "ebony", + "eucalyptus", + "hemlock", + "ironwood", + "mahogany", + "manzanita", + "maple", + "oak", + "persimmon wood", + "redwood", + "rosewood", + "teak", + "walnut", + "zebra wood", +}; + +#define NWOOD (sizeof wood / sizeof (char *)) +const int cNWOOD = NWOOD; + +char *metal[] = { + "aluminium", + "bone", + "brass", + "bronze", + "copper", + "iron", + "lead", + "pewter", + "steel", + "tin", + "zinc", +}; + +#define NMETAL (sizeof metal / sizeof (char *)) +const int cNMETAL = NMETAL; + +struct magic_item things[NUMTHINGS] = { + { "", 27 }, /* potion */ + { "", 27 }, /* scroll */ + { "", 18 }, /* food */ + { "", 9 }, /* weapon */ + { "", 9 }, /* armor */ + { "", 5 }, /* ring */ + { "", 5 }, /* stick */ +}; + +struct magic_item s_magic[MAXSCROLLS] = { + { "monster confusion", 8, 170 }, + { "magic mapping", 5, 180 }, + { "light", 10, 100 }, + { "hold monster", 2, 200 }, + { "sleep", 5, 50 }, + { "enchant armor", 8, 130 }, + { "identify", 21, 100 }, + { "scare monster", 4, 180 }, + { "gold detection", 4, 110 }, + { "teleportation", 7, 175 }, + { "enchant weapon", 10, 150 }, + { "create monster", 5, 75 }, + { "remove curse", 8, 105 }, + { "aggravate monsters", 1, 60 }, + { "blank paper", 1, 50 }, + { "genocide", 1, 200 }, +}; + +struct magic_item p_magic[MAXPOTIONS] = { + { "confusion", 8, 50 }, + { "paralysis", 10, 50 }, + { "poison", 8, 50 }, + { "gain strength", 15, 150 }, + { "see invisible", 2, 170 }, + { "healing", 15, 130 }, + { "monster detection", 6, 120 }, + { "magic detection", 6, 105 }, + { "raise level", 2, 220 }, + { "extra healing", 5, 180 }, + { "haste self", 4, 200 }, + { "restore strength", 14, 120 }, + { "blindness", 4, 50 }, + { "thirst quenching", 1, 50 }, +}; + +struct magic_item r_magic[MAXRINGS] = { + { "protection", 9, 200 }, + { "add strength", 9, 200 }, + { "sustain strength", 5, 180 }, + { "searching", 10, 200 }, + { "see invisible", 10, 175 }, + { "adornment", 1, 100 }, + { "aggravate monster", 11, 100 }, + { "dexterity", 8, 220 }, + { "increase damage", 8, 220 }, + { "regeneration", 4, 260 }, + { "slow digestion", 9, 240 }, + { "teleportation", 9, 100 }, + { "stealth", 7, 100 }, +}; + +struct magic_item ws_magic[MAXSTICKS] = { + { "light", 12, 120 }, + { "striking", 9, 115 }, + { "lightning", 3, 200 }, + { "fire", 3, 200 }, + { "cold", 3, 200 }, + { "polymorph", 15, 210 }, + { "magic missile", 10, 170 }, + { "haste monster", 9, 50 }, + { "slow monster", 11, 220 }, + { "drain life", 9, 210 }, + { "nothing", 1, 70 }, + { "teleport away", 5, 140 }, + { "teleport to", 5, 60 }, + { "cancellation", 5, 130 }, +}; + +int a_class[MAXARMORS] = { + 8, + 7, + 7, + 6, + 5, + 4, + 4, + 3, +}; + +char *a_names[MAXARMORS] = { + "leather armor", + "ring mail", + "studded leather armor", + "scale mail", + "chain mail", + "splint mail", + "banded mail", + "plate mail", +}; + +int a_chances[MAXARMORS] = { + 20, + 35, + 50, + 63, + 75, + 85, + 95, + 100 +}; + +#define MAX3(a,b,c) (a > b ? (a > c ? a : c) : (b > c ? b : c)) +static bool used[MAX3(NCOLORS, NSTONES, NWOOD)]; + +/* + * init_things + * Initialize the probabilities for types of things + */ +init_things() +{ + register struct magic_item *mp; + + for (mp = &things[1]; mp <= &things[NUMTHINGS-1]; mp++) + mp->mi_prob += (mp-1)->mi_prob; + badcheck("things", things, NUMTHINGS); +} + +/* + * init_colors: + * Initialize the potion color scheme for this time + */ + +init_colors() +{ + register int i, j; + + for (i = 0; i < NCOLORS; i++) + used[i] = 0; + for (i = 0; i < MAXPOTIONS; i++) + { + do + j = rnd(NCOLORS); + until (!used[j]); + used[j] = TRUE; + p_colors[i] = rainbow[j]; + p_know[i] = FALSE; + p_guess[i] = NULL; + if (i > 0) + p_magic[i].mi_prob += p_magic[i-1].mi_prob; + } + badcheck("potions", p_magic, MAXPOTIONS); +} + +/* + * init_names: + * Generate the names of the various scrolls + */ + +init_names() +{ + register int nsyl; + register char *cp, *sp; + register int i, nwords; + + for (i = 0; i < MAXSCROLLS; i++) + { + cp = prbuf; + nwords = rnd(4)+2; + while(nwords--) + { + nsyl = rnd(3)+1; + while(nsyl--) + { + sp = sylls[rnd((sizeof sylls) / (sizeof (char *)))]; + while(*sp) + *cp++ = *sp++; + } + *cp++ = ' '; + } + *--cp = '\0'; + s_names[i] = (char *) new(strlen(prbuf)+1); + s_know[i] = FALSE; + s_guess[i] = NULL; + strcpy(s_names[i], prbuf); + if (i > 0) + s_magic[i].mi_prob += s_magic[i-1].mi_prob; + } + badcheck("scrolls", s_magic, MAXSCROLLS); +} + +/* + * init_stones: + * Initialize the ring stone setting scheme for this time + */ + +init_stones() +{ + register int i, j; + + for (i = 0; i < NSTONES; i++) + used[i] = FALSE; + for (i = 0; i < MAXRINGS; i++) + { + do + j = rnd(NSTONES); + until (!used[j]); + used[j] = TRUE; + r_stones[i] = stones[j]; + r_know[i] = FALSE; + r_guess[i] = NULL; + if (i > 0) + r_magic[i].mi_prob += r_magic[i-1].mi_prob; + } + badcheck("rings", r_magic, MAXRINGS); +} + +/* + * init_materials: + * Initialize the construction materials for wands and staffs + */ + +init_materials() +{ + register int i, j; + static bool metused[NMETAL]; + + for (i = 0; i < NWOOD; i++) + used[i] = FALSE; + for (i = 0; i < NMETAL; i++) + metused[i] = FALSE; + + for (i = 0; i < MAXSTICKS; i++) + { + for (;;) + if (rnd(100) > 50) + { + j = rnd(NMETAL); + if (!metused[j]) + { + metused[j] = TRUE; + ws_made[i] = metal[j]; + ws_type[i] = "wand"; + break; + } + } + else + { + j = rnd(NWOOD); + if (!used[j]) + { + used[j] = TRUE; + ws_made[i] = wood[j]; + ws_type[i] = "staff"; + break; + } + } + + ws_know[i] = FALSE; + ws_guess[i] = NULL; + if (i > 0) + ws_magic[i].mi_prob += ws_magic[i-1].mi_prob; + } + badcheck("sticks", ws_magic, MAXSTICKS); +} + +badcheck(name, magic, bound) +char *name; +register struct magic_item *magic; +register int bound; +{ + register struct magic_item *end; + + if (magic[bound - 1].mi_prob == 100) + return; + printf("\nBad percentages for %s:\n", name); + for (end = &magic[bound]; magic < end; magic++) + printf("%3d%% %s\n", magic->mi_prob, magic->mi_name); + printf("[hit RETURN to continue]"); + fflush(stdout); + while (getchar() != '\n') + continue; +} + +struct h_list helpstr[] = { + '?', " prints help", + '/', " identify object", + 'h', " left", + 'j', " down", + 'k', " up", + 'l', " right", + 'y', " up & left", + 'u', " up & right", + 'b', " down & left", + 'n', " down & right", + 'H', " run left", + 'J', " run down", + 'K', " run up", + 'L', " run right", + 'Y', " run up & left", + 'U', " run up & right", + 'B', " run down & left", + 'N', " run down & right", + 't', " throw something", + 'f', " forward until find something", + 'p', " zap a wand in a direction", + 'z', " zap a wand or staff", + '>', " go down a staircase", + 's', " search for trap/secret door", + ' ', " (space) rest for a while", + 'i', " inventory", + 'I', " inventory single item", + 'q', " quaff potion", + 'r', " read paper", + 'e', " eat food", + 'w', " wield a weapon", + 'W', " wear armor", + 'T', " take armor off", + 'P', " put on ring", + 'R', " remove ring", + 'd', " drop object", + 'c', " call object", + 'o', " examine/set options", + CTRL('L'), " redraw screen", + CTRL('R'), " repeat last message", + ESCAPE, " cancel command", + 'v', " print program version number", + '!', " shell escape", + 'S', " save game", + 'Q', " quit", + 0, 0 +}; diff --git a/io.c b/io.c new file mode 100644 index 0000000..dbc7481 --- /dev/null +++ b/io.c @@ -0,0 +1,241 @@ +/* + * Various input/output functions + * + * @(#)io.c 3.10 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include +#include "rogue.h" + +/* + * msg: + * Display a message at the top of the screen. + */ + +static char msgbuf[BUFSIZ]; +static int newpos = 0; + +/*VARARGS1*/ +msg(char *fmt, ...) +{ + va_list ap; + /* + * if the string is "", just clear the line + */ + if (*fmt == '\0') + { + wmove(cw, 0, 0); + wclrtoeol(cw); + mpos = 0; + return; + } + /* + * otherwise add to the message and flush it out + */ + va_start(ap, fmt); + doadd(fmt, ap); + va_end(ap); + endmsg(); +} + +/* + * add things to the current message + */ +addmsg(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doadd(fmt, ap); + va_end(ap); +} + +/* + * Display a new msg (giving him a chance to see the previous one if it + * is up there with the --More--) + */ +endmsg() +{ + strncpy(huh, msgbuf, 80); + huh[79] = 0; + + if (mpos) + { + wmove(cw, 0, mpos); + waddstr(cw, "--More--"); + draw(cw); + wait_for(cw,' '); + } + mvwaddstr(cw, 0, 0, msgbuf); + wclrtoeol(cw); + mpos = newpos; + newpos = 0; + draw(cw); +} + +doadd(char *fmt, va_list ap) +{ + vsprintf(&msgbuf[newpos], fmt, ap); + newpos = (int) strlen(msgbuf); +} + +/* + * step_ok: + * returns true if it is ok to step on ch + */ + +step_ok(ch) +{ + switch (ch) + { + case ' ': + case '|': + case '-': + case SECRETDOOR: + return FALSE; + default: + return (!isalpha(ch)); + } +} + +/* + * readchar: + * flushes stdout so that screen is up to date and then returns + * getchar. + */ + +readchar(win) +WINDOW *win; +{ + int ch; + + ch = md_readchar(win); + + if ((ch == 3) || (ch == 0)) + { + quit(0); + return(27); + } + + return(ch); +} + +/* + * status: + * Display the important stats line. Keep the cursor where it was. + */ + +status() +{ + register int oy, ox, temp; + register char *pb; + static char buf[80]; + static int hpwidth = 0, s_hungry = -1; + static int s_lvl = -1, s_pur, s_hp = -1, s_str, s_add, s_ac = 0; + static long s_exp = 0; + + /* + * If nothing has changed since the last status, don't + * bother. + */ + if (s_hp == pstats.s_hpt && s_exp == pstats.s_exp && s_pur == purse + && s_ac == (cur_armor != NULL ? cur_armor->o_ac : pstats.s_arm) + && s_str == pstats.s_str.st_str && s_add == pstats.s_str.st_add + && s_lvl == level && s_hungry == hungry_state) + return; + + getyx(cw, oy, ox); + if (s_hp != max_hp) + { + temp = s_hp = max_hp; + for (hpwidth = 0; temp; hpwidth++) + temp /= 10; + } + sprintf(buf, "Level: %d Gold: %-5d Hp: %*d(%*d) Str: %-2d", + level, purse, hpwidth, pstats.s_hpt, hpwidth, max_hp, + pstats.s_str.st_str); + if (pstats.s_str.st_add != 0) + { + pb = &buf[strlen(buf)]; + sprintf(pb, "/%d", pstats.s_str.st_add); + } + pb = &buf[strlen(buf)]; + sprintf(pb, " Ac: %-2d Exp: %d/%ld", + cur_armor != NULL ? cur_armor->o_ac : pstats.s_arm, pstats.s_lvl, + pstats.s_exp); + /* + * Save old status + */ + s_lvl = level; + s_pur = purse; + s_hp = pstats.s_hpt; + s_str = pstats.s_str.st_str; + s_add = pstats.s_str.st_add; + s_exp = pstats.s_exp; + s_ac = (cur_armor != NULL ? cur_armor->o_ac : pstats.s_arm); + mvwaddstr(cw, LINES - 1, 0, buf); + switch (hungry_state) + { + case 0: ; + when 1: + waddstr(cw, " Hungry"); + when 2: + waddstr(cw, " Weak"); + when 3: + waddstr(cw, " Fainting"); + } + wclrtoeol(cw); + s_hungry = hungry_state; + wmove(cw, oy, ox); +} + +/* + * wait_for + * Sit around until the guy types the right key + */ + +wait_for(win, ch) +WINDOW *win; +register char ch; +{ + register char c; + + if (ch == '\n') + while ((c = readchar(win)) != '\n' && c != '\r') + continue; + else + while (readchar(win) != ch) + continue; +} + +/* + * show_win: + * function used to display a window and wait before returning + */ + +show_win(scr, message) +register WINDOW *scr; +char *message; +{ + mvwaddstr(scr, 0, 0, message); + touchwin(scr); + wmove(scr, hero.y, hero.x); + draw(scr); + wait_for(scr,' '); + clearok(cw, TRUE); + touchwin(cw); +} + +flush_type() +{ + flushinp(); +} diff --git a/list.c b/list.c new file mode 100644 index 0000000..0b710d2 --- /dev/null +++ b/list.c @@ -0,0 +1,121 @@ +/* + * Functions for dealing with linked lists of goodies + * + * @(#)list.c 3.3 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +/* + * detach: + * Takes an item out of whatever linked list it might be in + */ + +_detach(list, item) +register struct linked_list **list, *item; +{ + if (*list == item) + *list = next(item); + if (prev(item) != NULL) item->l_prev->l_next = next(item); + if (next(item) != NULL) item->l_next->l_prev = prev(item); + item->l_next = NULL; + item->l_prev = NULL; +} + +/* + * _attach: + * add an item to the head of a list + */ + +_attach(list, item) +register struct linked_list **list, *item; +{ + if (*list != NULL) + { + item->l_next = *list; + (*list)->l_prev = item; + item->l_prev = NULL; + } + else + { + item->l_next = NULL; + item->l_prev = NULL; + } + + *list = item; +} + +/* + * _free_list: + * Throw the whole blamed thing away + */ + +_free_list(ptr) +register struct linked_list **ptr; +{ + register struct linked_list *item; + + while (*ptr != NULL) + { + item = *ptr; + *ptr = next(item); + discard(item); + } +} + +/* + * discard: + * free up an item + */ + +discard(item) +register struct linked_list *item; +{ + total -= 2; + FREE(item->l_data); + FREE(item); +} + +/* + * new_item + * get a new item with a specified size + */ + +struct linked_list * +new_item(size) +int size; +{ + register struct linked_list *item; + + if ((item = (struct linked_list *) new(sizeof *item)) == NULL) + msg("Ran out of memory for header after %d items", total); + if ((item->l_data = new(size)) == NULL) + msg("Ran out of memory for data after %d items", total); + item->l_next = item->l_prev = NULL; + memset(item->l_data,0,size); + return item; +} + +char * +new(size) +int size; +{ + register char *space = ALLOC(size); + + if (space == NULL) + { + sprintf(prbuf, "Rogue ran out of memory (%d). Fatal error!", md_memused()); + fatal(prbuf); + } + total++; + return space; +} diff --git a/machdep.h b/machdep.h new file mode 100644 index 0000000..45b072a --- /dev/null +++ b/machdep.h @@ -0,0 +1,55 @@ +/* + * machine dependicies + * + * %G% (Berkeley) %W% + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +/* + * where scorefile should live + */ + +#include +#include + +/* + * Variables for checking to make sure the system isn't too loaded + * for people to play + */ + +#define AUTHORUID 0 +#define MAXUSERS 25 /* max number of users for this game */ +#define MAXLOAD 40 /* 10 * max 15 minute load average */ +#define CHECKTIME 15 /* number of minutes between load checks */ + +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif + +#ifdef _WIN32 +#define fstat _fstat +#define stat _stat +#define open _open +#define getpid _getpid +#define fdopen _fdopen +#define unlink _unlink +#ifndef __MINGW32__ +#define fileno _fileno +#endif +#endif + +extern char *md_getusername(); +extern char *md_gethomedir(); +extern void md_flushinp(); +extern char *md_getshell(); +extern char *md_gethostname(); +extern void md_dobinaryio(); +extern char *md_getpass(); +extern void md_init(); +extern char *xcrypt(); +extern char *md_getroguedir(); diff --git a/main.c b/main.c new file mode 100644 index 0000000..32fdbd0 --- /dev/null +++ b/main.c @@ -0,0 +1,445 @@ +/* + * @(#)main.c 3.27 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include +#include +#include +#include +#include "machdep.h" +#include "rogue.h" + +int num_checks; /* times we've gone over in checkout() */ +WINDOW *cw; /* Window that the player sees */ +WINDOW *hw; /* Used for the help command */ +WINDOW *mw; /* Used to store mosnters */ + +main(argc, argv, envp) +char **argv; +char **envp; +{ + register char *env; + register struct linked_list *item; + register struct object *obj; + int lowtime; + time_t now; + + md_init(); + + /* + * check for print-score option + */ + if (argc == 2 && strcmp(argv[1], "-s") == 0) + { + waswizard = TRUE; + score(0, -1, 0); + exit(0); + } + /* + * Check to see if he is a wizard + */ + if (argc >= 2 && argv[1][0] == '\0') + if (strcmp(PASSWD, xcrypt(md_getpass("Wizard's password: "), "mT")) == 0) + { + wizard = TRUE; + argv++; + argc--; + } + + /* + * get home and options from environment + */ + strncpy(home, md_gethomedir(), PATH_MAX); + + strcpy(file_name, home); + strcat(file_name, "rogue36.sav"); + + if ((env = getenv("ROGUEOPTS")) != NULL) + parse_opts(env); + if (env == NULL || whoami[0] == '\0') + strucpy(whoami, md_getusername(md_getuid()), strlen(md_getusername(md_getuid()))); + if (env == NULL || fruit[0] == '\0') + strcpy(fruit, "slime-mold"); + + if (too_much() && !wizard && !author()) + { + printf("Sorry, %s, but the system is too loaded now.\n", whoami); + printf("Try again later. Meanwhile, why not enjoy a%s %s?\n", + vowelstr(fruit), fruit); + exit(1); + } + + if (argc == 2) + if (!restore(argv[1], envp)) /* Note: restore will never return */ + exit(1); + + time(&now); + lowtime = (int) now; + dnum = (wizard && getenv("SEED") != NULL ? + atoi(getenv("SEED")) : + lowtime + getpid()); + if (wizard) + printf("Hello %s, welcome to dungeon #%d", whoami, dnum); + else + printf("Hello %s, just a moment while I dig the dungeon...", whoami); + fflush(stdout); + seed = dnum; + init_player(); /* Roll up the rogue */ + init_things(); /* Set up probabilities of things */ + init_names(); /* Set up names of scrolls */ + init_colors(); /* Set up colors of potions */ + init_stones(); /* Set up stone settings of rings */ + init_materials(); /* Set up materials of wands */ + initscr(); /* Start up cursor package */ + + if (COLS < 70) + { + endwin(); + printf("\n\nSorry, %s, but your terminal window has too few columns.\n", whoami); + printf("Your terminal has %d columns, needs 70.\n",COLS); + exit(1); + } + if (LINES < 22) + { + endwin(); + printf("\n\nSorry, %s, but your terminal window has too few lines.\n", whoami); + printf("Your terminal has %d lines, needs 22.\n",LINES); + exit(1); + } + + + setup(); + /* + * Set up windows + */ + cw = newwin(LINES, COLS, 0, 0); + mw = newwin(LINES, COLS, 0, 0); + hw = newwin(LINES, COLS, 0, 0); + keypad(cw,1); + waswizard = wizard; + new_level(); /* Draw current level */ + /* + * Start up daemons and fuses + */ + start_daemon(doctor, 0, AFTER); + fuse(swander, 0, WANDERTIME, AFTER); + start_daemon(stomach, 0, AFTER); + start_daemon(runners, 0, AFTER); + /* + * Give the rogue his weaponry. First a mace. + */ + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + obj->o_type = WEAPON; + obj->o_which = MACE; + init_weapon(obj, MACE); + obj->o_hplus = 1; + obj->o_dplus = 1; + obj->o_flags |= ISKNOW; + add_pack(item, TRUE); + cur_weapon = obj; + /* + * Now a +1 bow + */ + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + obj->o_type = WEAPON; + obj->o_which = BOW; + init_weapon(obj, BOW); + obj->o_hplus = 1; + obj->o_dplus = 0; + obj->o_flags |= ISKNOW; + add_pack(item, TRUE); + /* + * Now some arrows + */ + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + obj->o_type = WEAPON; + obj->o_which = ARROW; + init_weapon(obj, ARROW); + obj->o_count = 25+rnd(15); + obj->o_hplus = obj->o_dplus = 0; + obj->o_flags |= ISKNOW; + add_pack(item, TRUE); + /* + * And his suit of armor + */ + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + obj->o_type = ARMOR; + obj->o_which = RING_MAIL; + obj->o_ac = a_class[RING_MAIL] - 1; + obj->o_flags |= ISKNOW; + cur_armor = obj; + add_pack(item, TRUE); + /* + * Give him some food too + */ + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + obj->o_type = FOOD; + obj->o_count = 1; + obj->o_which = 0; + add_pack(item, TRUE); + playit(); +} + +/* + * endit: + * Exit the program abnormally. + */ + +void +endit(int p) +{ + fatal("Ok, if you want to exit that badly, I'll have to allow it\n"); +} + +/* + * fatal: + * Exit the program, printing a message. + */ + +fatal(s) +char *s; +{ + clear(); + move(LINES-2, 0); + printw("%s", s); + draw(stdscr); + endwin(); + exit(0); +} + +/* + * rnd: + * Pick a very random number. + */ + +rnd(range) +register int range; +{ + return range == 0 ? 0 : abs(RN) % range; +} + +/* + * roll: + * roll a number of dice + */ + +roll(number, sides) +register int number, sides; +{ + register int dtotal = 0; + + while(number--) + dtotal += rnd(sides)+1; + return dtotal; +} +/* + * handle stop and start signals + */ + +void +tstp(int p) +{ +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + mvcur(0, COLS - 1, LINES - 1, 0); + endwin(); + fflush(stdout); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_DFL); + kill(0, SIGTSTP); + signal(SIGTSTP, tstp); +#endif + crmode(); + noecho(); + clearok(curscr, TRUE); + touchwin(cw); + draw(cw); + flush_type(); /* flush input */ +} + +setup() +{ +#ifdef SIGHUP + signal(SIGHUP, auto_save); +#endif + signal(SIGILL, auto_save); +#ifdef SIGTRAP + signal(SIGTRAP, auto_save); +#endif +#ifdef SIGIOT + signal(SIGIOT, auto_save); +#endif +#ifdef SIGEMT + signal(SIGEMT, auto_save); +#endif + signal(SIGFPE, auto_save); +#ifdef SIGBUS + signal(SIGBUS, auto_save); +#endif + signal(SIGSEGV, auto_save); +#ifdef SIGSYS + signal(SIGSYS, auto_save); +#endif +#ifdef SIGPIPE + signal(SIGPIPE, auto_save); +#endif + signal(SIGTERM, auto_save); + signal(SIGINT, quit); +#ifdef SIGQUIT + signal(SIGQUIT, endit); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, tstp); +#endif + + if (!author()) + { +#ifdef SIGALRM + signal(SIGALRM, checkout); + alarm(CHECKTIME * 60); +#endif + num_checks = 0; + } + + crmode(); /* Cbreak mode */ + noecho(); /* Echo off */ +} + +/* + * playit: + * The main loop of the program. Loop until the game is over, + * refreshing things and looking at the proper times. + */ + +playit() +{ + register char *opts; + + /* + * set up defaults for slow terminals + */ + + + if (baudrate() < 1200) + { + terse = TRUE; + jump = TRUE; + } + + /* + * parse environment declaration of options + */ + if ((opts = getenv("ROGUEOPTS")) != NULL) + parse_opts(opts); + + + oldpos = hero; + oldrp = roomin(&hero); + while (playing) + command(); /* Command execution */ + endit(-1); +} + +/* + * see if the system is being used too much for this game + */ +too_much() +{ + double avec[3]; + + if (md_getloadavg(avec) == 0) + return (avec[2] > (MAXLOAD / 10.0)); + else + return (md_ucount() > MAXUSERS); +} + +/* + * see if a user is an author of the program + */ +author() +{ + switch (md_getuid()) + { + case AUTHORUID: + return TRUE; + default: + return FALSE; + } +} + +int chmsg(char *fmt, ...); + +void +checkout(int p) +{ + static char *msgs[] = { + "The load is too high to be playing. Please leave in %d minutes", + "Please save your game. You have %d minutes", + "Last warning. You have %d minutes to leave", + }; + int checktime; +#ifdef SIGALRM + signal(SIGALRM, checkout); +#endif + if (too_much()) + { + if (num_checks == 3) + fatal("Sorry. You took to long. You are dead\n"); + checktime = CHECKTIME / (num_checks + 1); + chmsg(msgs[num_checks++], checktime); +#ifdef SIGALRM + alarm(checktime * 60); +#endif + } + else + { + if (num_checks) + { + chmsg("The load has dropped back down. You have a reprieve."); + num_checks = 0; + } +#ifdef SIGALRM + alarm(CHECKTIME * 60); +#endif + } +} + +/* + * checkout()'s version of msg. If we are in the middle of a shell, do a + * printf instead of a msg to avoid the refresh. + */ +chmsg(char *fmt, ...) +{ + va_list args; + + if (in_shell) + { + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putchar('\n'); + fflush(stdout); + } + else + { + va_start(args, fmt); + doadd(fmt, args); + va_end(args); + endmsg(); + } +} diff --git a/mdport.c b/mdport.c new file mode 100644 index 0000000..eb33c32 --- /dev/null +++ b/mdport.c @@ -0,0 +1,1250 @@ +/* + mdport.c - Machine Dependent Code for Porting Unix/Curses games + + Copyright (C) 2005 Nicholas J. Kisseberth + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#if defined(_WIN32) +#include +#include +#include +#include +#include +#include +#include +#undef MOUSE_MOVED +#elif defined(__DJGPP__) +#include +#else +#include +#include +#include +#include +#endif + +#ifdef __INTERIX +char *strdup(const char *s); +#endif + +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) +#define PATH_MAX MAX_PATH +#endif + +#include +#if !defined(DJGPP) +#include +#endif + +#include +#include +#include +#include +#include + +#define MOD_MOVE(c) (toupper(c) ) + +void +md_init() +{ +#ifdef __INTERIX + char *term; + + term = getenv("TERM"); + + if (term == NULL) + setenv("TERM","interix",1); +#endif +#if defined(__DJGPP__) || defined(_WIN32) + _fmode = _O_BINARY; +#endif +#if defined(__CYGWIN__) || defined(__MSYS__) + ESCDELAY=250; +#endif +} + +int +md_hasclreol() +{ +#ifdef CE + return((CE != NULL) && (*CE != 0)); +#elif defined (clr_eol) + return((clr_eol != NULL) && (*clr_eol != 0)); +#elif !defined(__PDCURSES__) + return(clr_eol != NULL); +#else + return(TRUE); +#endif +} + +void +md_putchar(int c) +{ + putchar(c); +} + +static int md_standout_mode = 0; + +void +md_raw_standout() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + HANDLE hStdout; + int fgattr,bgattr; + + if (md_standout_mode == 0) + { + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hStdout, &csbiInfo); + fgattr = (csbiInfo.wAttributes & 0xF); + bgattr = (csbiInfo.wAttributes & 0xF0); + SetConsoleTextAttribute(hStdout,(fgattr << 4) | (bgattr >> 4)); + md_standout_mode = 1; + } +#elif defined(SO) + tputs(SO,0,md_putchar); + fflush(stdout); +#endif +} + +void +md_raw_standend() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + HANDLE hStdout; + int fgattr,bgattr; + + if (md_standout_mode == 1) + { + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hStdout, &csbiInfo); + fgattr = (csbiInfo.wAttributes & 0xF); + bgattr = (csbiInfo.wAttributes & 0xF0); + SetConsoleTextAttribute(hStdout,(fgattr << 4) | (bgattr >> 4)); + md_standout_mode = 0; + } +#elif defined(SE) + tputs(SE,0,md_putchar); + fflush(stdout); +#endif +} + +int +md_unlink_open_file(char *file, int inf) +{ +#ifdef _WIN32 + _close(inf); + _chmod(file, 0600); + return( _unlink(file) ); +#else + return(unlink(file)); +#endif +} + +int +md_unlink(char *file) +{ +#ifdef _WIN32 + _chmod(file, 0600); + return( _unlink(file) ); +#else + return(unlink(file)); +#endif +} + +int +md_creat(char *file, int mode) +{ + int fd; +#ifdef _WIN32 + mode = _S_IREAD | _S_IWRITE; + fd = _open(file,O_CREAT | O_EXCL | O_WRONLY, mode); +#else + fd = open(file,O_CREAT | O_EXCL | O_WRONLY, mode); +#endif + + return(fd); +} + + +void +md_normaluser() +{ +#ifndef _WIN32 + setuid(getuid()); + setgid(getgid()); +#endif +} + +int +md_getuid() +{ +#ifndef _WIN32 + return( getuid() ); +#else + return(42); +#endif +} + +char * +md_getusername(int uid) +{ + static char login[80]; + char *l = NULL; +#ifdef _WIN32 + LPSTR mybuffer; + DWORD size = UNLEN + 1; + TCHAR buffer[UNLEN + 1]; + + mybuffer = buffer; + if (uid != md_getuid()) + strcpy(mybuffer, "someone"); + else + GetUserName(mybuffer,&size); + l = mybuffer; +#endif +#if !defined(_WIN32) && !defined(DJGPP) + struct passwd *pw; + + pw = getpwuid(getuid()); + + l = pw->pw_name; +#endif + + if ((l == NULL) || (*l == '\0')) + if ( (l = getenv("USERNAME")) == NULL ) + if ( (l = getenv("LOGNAME")) == NULL ) + if ( (l = getenv("USER")) == NULL ) + l = "nobody"; + + strncpy(login,l,80); + login[79] = 0; + + return(login); +} + +char * +md_gethomedir() +{ + static char homedir[PATH_MAX]; + char *h = NULL; + size_t len; +#if defined(_WIN32) + TCHAR szPath[PATH_MAX]; +#endif +#if defined(_WIN32) || defined(DJGPP) + char slash = '\\'; +#else + char slash = '/'; + struct passwd *pw; + pw = getpwuid(getuid()); + + h = pw->pw_dir; + + if (strcmp(h,"/") == 0) + h = NULL; +#endif + homedir[0] = 0; +#ifdef _WIN32 + if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) + h = szPath; +#endif + + if ( (h == NULL) || (*h == '\0') ) + if ( (h = getenv("HOME")) == NULL ) + h = ""; + + strncpy(homedir,h,PATH_MAX-1); + len = strlen(homedir); + + if ((len > 0) && (homedir[len-1] == slash)) + homedir[len-1] = 0; + + return(homedir); +} + +void +md_sleep(int s) +{ +#ifdef _WIN32 + Sleep(s); +#else + sleep(s); +#endif +} + +char * +md_getshell() +{ + static char shell[PATH_MAX]; + char *s = NULL; +#ifdef _WIN32 + char *def = "C:\\WINDOWS\\SYSTEM32\\CMD.EXE"; +#elif defined(__DJGPP__) + char *def = "C:\\COMMAND.COM"; +#else + char *def = "/bin/sh"; + struct passwd *pw; + pw = getpwuid(getuid()); + s = pw->pw_shell; +#endif + if ((s == NULL) || (*s == '\0')) + if ( (s = getenv("COMSPEC")) == NULL) + if ( (s = getenv("SHELL")) == NULL) + if ( (s = getenv("SystemRoot")) == NULL) + s = def; + + strncpy(shell,s,PATH_MAX); + shell[PATH_MAX-1] = 0; + + return(shell); +} + +int +md_shellescape() +{ +#if (!defined(_WIN32) && !defined(__DJGPP__)) + int ret_status; + int pid; + void (*myquit)(int); + void (*myend)(int); +#endif + char *sh; + + sh = md_getshell(); + +#if defined(_WIN32) + return((int)_spawnl(_P_WAIT,sh,"shell",NULL,0)); +#elif defined(__DJGPP__) + return ( spawnl(P_WAIT,sh,"shell",NULL,0) ); +#else + while((pid = fork()) < 0) + sleep(1); + + if (pid == 0) /* Shell Process */ + { + /* + * Set back to original user, just in case + */ + setuid(getuid()); + setgid(getgid()); + execl(sh == NULL ? "/bin/sh" : sh, "shell", "-i", 0); + perror("No shelly"); + _exit(-1); + } + else /* Application */ + { + myend = signal(SIGINT, SIG_IGN); +#ifdef SIGQUIT + myquit = signal(SIGQUIT, SIG_IGN); +#endif + while (wait(&ret_status) != pid) + continue; + + signal(SIGINT, myquit); +#ifdef SIGQUIT + signal(SIGQUIT, myend); +#endif + } + + return(ret_status); +#endif +} + +int +directory_exists(char *dirname) +{ + struct stat sb; + + if (stat(dirname, &sb) == 0) /* path exists */ + return (sb.st_mode & S_IFDIR); + + return(0); +} + +char * +md_getroguedir() +{ + static char path[1024]; + char *end,*home; + + if ( (home = getenv("ROGUEHOME")) != NULL) + { + if (*home) + { + strncpy(path, home, PATH_MAX - 20); + + end = &path[strlen(path)-1]; + + while( (end >= path) && ((*end == '/') || (*end == '\\'))) + *end-- = '\0'; + + if (directory_exists(path)) + return(path); + } + } + + if (directory_exists("/var/games/roguelike")) + return("/var/games/roguelike"); + if (directory_exists("/var/lib/roguelike")) + return("/var/lib/roguelike"); + if (directory_exists("/var/roguelike")) + return("/var/roguelike"); + if (directory_exists("/usr/games/lib")) + return("/usr/games/lib"); + if (directory_exists("/games/roguelik")) + return("/games/roguelik"); + if (directory_exists(md_gethomedir())) + return(md_gethomedir()); + return(""); +} + +char * +md_getrealname(int uid) +{ + static char uidstr[20]; +#if !defined(_WIN32) && !defined(DJGPP) + struct passwd *pp; + + if ((pp = getpwuid(uid)) == NULL) + { + sprintf(uidstr,"%d", uid); + return(uidstr); + } + else + return(pp->pw_name); +#else + sprintf(uidstr,"%d", uid); + return(uidstr); +#endif +} + +extern char *xcrypt(char *key, char *salt); + +char * +md_crypt(char *key, char *salt) +{ + return( xcrypt(key,salt) ); +} + +char * +md_getpass(char *prompt) +{ +#ifdef _WIN32 + static char password_buffer[9]; + char *p = password_buffer; + int c, count = 0; + int max_length = 9; + + fflush(stdout); + /* If we can't prompt, abort */ + if (fputs(prompt, stderr) < 0) + { + *p = '\0'; + return NULL; + } + + for(;;) + { + /* Get a character with no echo */ + c = _getch(); + + /* Exit on interrupt (^c or ^break) */ + if (c == '\003' || c == 0x100) + exit(1); + + /* Terminate on end of line or file (^j, ^m, ^d, ^z) */ + if (c == '\r' || c == '\n' || c == '\004' || c == '\032') + break; + + /* Back up on backspace */ + if (c == '\b') + { + if (count) + count--; + else if (p > password_buffer) + p--; + continue; + } + + /* Ignore DOS extended characters */ + if ((c & 0xff) != c) + continue; + + /* Add to password if it isn't full */ + if (p < password_buffer + max_length - 1) + *p++ = c; + else + count++; + } + *p = '\0'; + + fputc('\n', stderr); + + return password_buffer; +#else + return( (char *) getpass(prompt) ); +#endif +} + + +int md_endian = 0x01020304; + +unsigned long int +md_ntohl(unsigned long int x) +{ +#ifdef _WIN32 + if ( *((char *)&md_endian) == 0x01 ) + return(x); + else + return( ((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24) ); +#else + return( ntohl(x) ); +#endif +} + +unsigned long int +md_htonl(unsigned long int x) +{ +#ifdef _WIN32 + if ( *((char *)&md_endian) == 0x01 ) + return(x); + else + return( ((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24) ); +#else + return( htonl(x) ); +#endif +} + +int +md_ucount() +{ +#ifdef __DJGPP__ + return(1); +#elif defined(_WIN32) + return(1); +#else + struct utmpx *up=NULL; + int count=0; + + setutxent(); + do + { + up = getutxent(); + if (up && up->ut_type == USER_PROCESS) + count++; + } while(up != NULL); + + endutxent(); + + return(count); +#endif +} + +int +md_getloadavg(double *avg) +{ +#if defined(__GLIBC__) || defined(_BSD) + if (getloadavg(avg, 3) == -1) +#endif + { + avg[0] = avg[1] = avg[2] = 0.0; + return -1; + } +} + +long +md_random() +{ +#ifdef _WIN32 + return(rand()); +#else + return( random() ); +#endif +} + +void +md_srandom(unsigned x) +{ +#ifdef _WIN32 + srand(x); +#else + srandom(x); +#endif +} + +int +md_rand() +{ +#ifdef _WIN32 + return(rand()); +#else + return(lrand48() & 0x7fffffff); +#endif +} + +void +md_srand(int seed) +{ +#ifdef _WIN32 + srand(seed); +#else + srand48(seed); +#endif +} + +char * +md_strdup(const char *s) +{ +#ifdef _WIN32 + return( _strdup(s) ); +#else + return(strdup(s)); +#endif +} + +long +md_memused() +{ +#ifdef _WIN32 + MEMORYSTATUS stat; + + GlobalMemoryStatus(&stat); + + return((long)stat.dwTotalPageFile); +#else + return( (long)sbrk(0) ); +#endif +} + +char * +md_gethostname() +{ + static char nodename[80]; + char *n = NULL; +#if !defined(_WIN32) && !defined(__DJGPP__) + struct utsname ourname; + + if (uname(&ourname) == 0) + n = ourname.nodename; +#endif + if ((n == NULL) || (*n == '\0')) + if ( (n = getenv("COMPUTERNAME")) == NULL) + if ( (n = getenv("HOSTNAME")) == NULL) + n = "localhost"; + + strncpy(nodename, n, 80); + nodename[79] = 0; + + return(nodename); +} + +int +md_erasechar() +{ +#ifdef BSD + return(_tty.sg_erase); /* process erase character */ +#elif defined(USG5_0) + return(_tty.c_cc[VERASE]); /* process erase character */ +#else /* USG5_2 .... curses */ + return( erasechar() ); /* process erase character */ +#endif +} + +int +md_killchar() +{ +#ifdef BSD + return(_tty.sg_kill); +#elif defined(USG5_0) + return(_tty.c_cc[VKILL]); +#else /* USG5_2 ..... curses */ + return( killchar() ); +#endif +} + +/* + * unctrl: + * Print a readable version of a certain character + */ + +char * +md_unctrl(char ch) +{ +#if USG5_0 + extern char *_unctrl[]; /* Defined in curses library */ + + return _unctrl[ch&0177]; +#else + return( unctrl(ch) ); +#endif +} + +void +md_flushinp() +{ +#ifdef BSD + ioctl(0, TIOCFLUSH); +#elif defined(USG5_0) + ioctl(_tty_ch,TCFLSH,0) +#else /* USG5_2.... curses */ + flushinp(); +#endif +} + +/* + Cursor/Keypad Support + + Sadly Cursor/Keypad support is less straightforward than it should be. + + The various terminal emulators/consoles choose to differentiate the + cursor and keypad keys (with modifiers) in different ways (if at all!). + Furthermore they use different code set sequences for each key only + a subset of which the various curses libraries recognize. Partly due + to incomplete termcap/terminfo entries and partly due to inherent + limitations of those terminal capability databases. + + I give curses first crack at decoding the sequences. If it fails to decode + it we check for common ESC-prefixed sequences. + + All cursor/keypad results are translated into standard rogue movement + commands. + + Unmodified keys are translated to walk commands: hjklyubn + Modified (shift,control,alt) are translated to run commands: HJKLYUBN + + Console and supported (differentiated) keys + Interix: Cursor Keys, Keypad, Ctl-Keypad + Cygwin: Cursor Keys, Keypad, Alt-Cursor Keys + MSYS: Cursor Keys, Keypad, Ctl-Cursor Keys, Ctl-Keypad + Win32: Cursor Keys, Keypad, Ctl/Shift/Alt-Cursor Keys, Ctl/Alt-Keypad + DJGPP: Cursor Keys, Keypad, Ctl/Shift/Alt-Cursor Keys, Ctl/Alt-Keypad + + Interix Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC [D, ESC F^, ESC [D, ESC [D /# Left #/ + ESC [C, ESC F$, ESC [C, ESC [C /# Right #/ + ESC [A, ESC F-, local win, ESC [A /# Up #/ + ESC [B, ESC F+, local win, ESC [B /# Down #/ + ESC [H, ESC [H, ESC [H, ESC [H /# Home #/ + ESC [S, local win, ESC [S, ESC [S /# Page Up #/ + ESC [T, local win, ESC [T, ESC [T /# Page Down #/ + ESC [U, ESC [U, ESC [U, ESC [U /# End #/ + ESC [D, ESC F^, ESC [D, O /# Keypad Left #/ + ESC [C, ESC F$, ESC [C, O /# Keypad Right #/ + ESC [A, ESC [A, ESC [-1, O /# Keypad Up #/ + ESC [B, ESC [B, ESC [-2, O /# Keypad Down #/ + ESC [H, ESC [H, ESC [-263, O /# Keypad Home #/ + ESC [S, ESC [S, ESC [-19, O /# Keypad PgUp #/ + ESC [T, ESC [T, ESC [-20, O /# Keypad PgDn #/ + ESC [U, ESC [U, ESC [-21, O /# Keypad End #/ + nothing, nothing, nothing, O /# Kaypad 5 #/ + + Interix Console (term=interix, ncurses) + ============================== + KEY_LEFT, ESC F^, KEY_LEFT, KEY_LEFT /# Left #/ + KEY_RIGHT, ESC F$, KEY_RIGHT, KEY_RIGHT /# Right #/ + KEY_UP, 0x146, local win, KEY_UP /# Up #/ + KEY_DOWN, 0x145, local win, KEY_DOWN /# Down #/ + ESC [H, ESC [H, ESC [H, ESC [H /# Home #/ + KEY_PPAGE, local win, KEY_PPAGE, KEY_PPAGE /# Page Up #/ + KEY_NPAGE, local win, KEY_NPAGE, KEY_NPAGE /# Page Down #/ + KEY_LL, KEY_LL, KEY_LL, KEY_LL /# End #/ + KEY_LEFT, ESC F^, ESC [-4, O /# Keypad Left #/ + KEY_RIGHT, ESC F$, ESC [-3, O /# Keypad Right #/ + KEY_UP, KEY_UP, ESC [-1, O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, ESC [-2, O /# Keypad Down #/ + ESC [H, ESC [H, ESC [-263, O /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, ESC [-19, O /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, ESC [-20, O /# Keypad PgDn #/ + KEY_LL, KEY_LL, ESC [-21, O /# Keypad End #/ + nothing, nothing, nothing, O /# Keypad 5 #/ + + Cygwin Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC [D, ESC ESC [D /# Left #/ + ESC [C, ESC [C, ESC [C, ESC ESC [C /# Rght #/ + ESC [A, ESC [A, ESC [A, ESC ESC [A /# Up #/ + ESC [B, ESC [B, ESC [B, ESC ESC [B /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC ESC [1~ /# Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC ESC [5~ /# Page Up #/ + ESC [6~, ESC [6~, ESC [6~, ESC ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, ESC ESC [D,O /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, ESC ESC [C,O /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, ESC ESC [A,O /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, ESC ESC [B,O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC ESC [1~,O /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC ESC [5~,O /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, ESC ESC [6~,O /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, ESC ESC [4~,O /# Keypad End #/ + ESC [-71, nothing, nothing, O /# Keypad 5 #/ + + Cygwin Console (term=cygwin, ncurses) + ============================== + KEY_LEFT, KEY_LEFT, KEY_LEFT, ESC-260 /# Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, ESC-261 /# Rght #/ + KEY_UP, KEY_UP, KEY_UP, ESC-259 /# Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, ESC-258 /# Down #/ + KEY_HOME, KEY_HOME, KEY_HOME, ESC-262 /# Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, ESC-339 /# Page Up #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, ESC-338 /# Page Down #/ + KEY_END, KEY_END, KEY_END, ESC-360 /# End #/ + KEY_LEFT, KEY_LEFT, KEY_LEFT, ESC-260,O /# Keypad Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, ESC-261,O /# Keypad Right #/ + KEY_UP, KEY_UP, KEY_UP, ESC-259,O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, ESC-258,O /# Keypad Down #/ + KEY_HOME, KEY_HOME, KEY_HOME, ESC-262,O /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, ESC-339,O /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, ESC-338,O /# Keypad PgDn #/ + KEY_END, KEY_END, KEY_END, ESC-360,O /# Keypad End #/ + ESC [G, nothing, nothing, O /# Keypad 5 #/ + + MSYS Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC OD, ESC [d, ESC Od nothing /# Left #/ + ESC OE, ESC [e, ESC Oe, nothing /# Right #/ + ESC OA, ESC [a, ESC Oa, nothing /# Up #/ + ESC OB, ESC [b, ESC Ob, nothing /# Down #/ + ESC [7~, ESC [7$, ESC [7^, nothing /# Home #/ + ESC [5~, local window, ESC [5^, nothing /# Page Up #/ + ESC [6~, local window, ESC [6^, nothing /# Page Down #/ + ESC [8~, ESC [8$, ESC [8^, nothing /# End #/ + ESC OD, ESC [d, ESC Od O /# Keypad Left #/ + ESC OE, ESC [c, ESC Oc, O /# Keypad Right #/ + ESC OA, ESC [a, ESC Oa, O /# Keypad Up #/ + ESC OB, ESC [b, ESC Ob, O /# Keypad Down #/ + ESC [7~, ESC [7$, ESC [7^, O /# Keypad Home #/ + ESC [5~, local window, ESC [5^, O /# Keypad PgUp #/ + ESC [6~, local window, ESC [6^, O /# Keypad PgDn #/ + ESC [8~, ESC [8$, ESC [8^, O /# Keypad End #/ + 11, 11, 11, O /# Keypad 5 #/ + + MSYS Console (term=rxvt, ncurses) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_SLEFT, 514 nothing /# Left #/ + KEY_RIGHT, KEY_SRIGHT, 516, nothing /# Right #/ + KEY_UP, 518, 519, nothing /# Up #/ + KEY_DOWN, 511, 512, nothing /# Down #/ + KEY_HOME, KEY_SHOME, ESC [7^, nothing /# Home #/ + KEY_PPAGE, local window, ESC [5^, nothing /# Page Up #/ + KEY_NPAGE, local window, ESC [6^, nothing /# Page Down #/ + KEY_END, KEY_SEND, KEY_EOL, nothing /# End #/ + KEY_LEFT, KEY_SLEFT, 514 O /# Keypad Left #/ + KEY_RIGHT, KEY_SRIGHT, 516, O /# Keypad Right #/ + KEY_UP, 518, 519, O /# Keypad Up #/ + KEY_DOWN, 511, 512, O /# Keypad Down #/ + KEY_HOME, KEY_SHOME, ESC [7^, O /# Keypad Home #/ + KEY_PPAGE, local window, ESC [5^, O /# Keypad PgUp #/ + KEY_NPAGE, local window, ESC [6^, O /# Keypad PgDn #/ + KEY_END, KEY_SEND, KEY_EOL, O /# Keypad End #/ + 11, 11, 11, O /# Keypad 5 #/ + + Win32 Console (raw, pdcurses) + DJGPP Console (raw, pdcurses) + ============================== + normal shift ctrl alt + 260, 391, 443, 493 /# Left #/ + 261, 400, 444, 492 /# Right #/ + 259, 547, 480, 490 /# Up #/ + 258, 548, 481, 491 /# Down #/ + 262, 388, 447, 524 /# Home #/ + 339, 396, 445, 526 /# Page Up #/ + 338, 394, 446, 520 /# Page Down #/ + 358, 384, 448, 518 /# End #/ + 452, 52('4'), 511, 521 /# Keypad Left #/ + 454, 54('6'), 513, 523 /# Keypad Right #/ + 450, 56('8'), 515, 525 /# Keypad Up #/ + 456, 50('2'), 509, 519 /# Keypad Down #/ + 449, 55('7'), 514, 524 /# Keypad Home #/ + 451, 57('9'), 516, 526 /# Keypad PgUp #/ + 457, 51('3'), 510, 520 /# Keypad PgDn #/ + 455, 49('1'), 508, 518 /# Keypad End #/ + 453, 53('5'), 512, 522 /# Keypad 5 #/ + + Win32 Console (pdcurses, MSVC/MingW32) + DJGPP Console (pdcurses) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_SLEFT, CTL_LEFT, ALT_LEFT /# Left #/ + KEY_RIGHT, KEY_SRIGHT, CTL_RIGHT, ALT_RIGHT /# Right #/ + KEY_UP, KEY_SUP, CTL_UP, ALT_UP /# Up #/ + KEY_DOWN, KEY_SDOWN, CTL_DOWN, ALT_DOWN /# Down #/ + KEY_HOME, KEY_SHOME, CTL_HOME, ALT_HOME /# Home #/ + KEY_PPAGE, KEY_SPREVIOUS, CTL_PGUP, ALT_PGUP /# Page Up #/ + KEY_NPAGE, KEY_SNEXTE, CTL_PGDN, ALT_PGDN /# Page Down #/ + KEY_END, KEY_SEND, CTL_END, ALT_END /# End #/ + KEY_B1, 52('4'), CTL_PAD4, ALT_PAD4 /# Keypad Left #/ + KEY_B3, 54('6'), CTL_PAD6, ALT_PAD6 /# Keypad Right #/ + KEY_A2, 56('8'), CTL_PAD8, ALT_PAD8 /# Keypad Up #/ + KEY_C2, 50('2'), CTL_PAD2, ALT_PAD2 /# Keypad Down #/ + KEY_A1, 55('7'), CTL_PAD7, ALT_PAD7 /# Keypad Home #/ + KEY_A3, 57('9'), CTL_PAD9, ALT_PAD9 /# Keypad PgUp #/ + KEY_C3, 51('3'), CTL_PAD3, ALT_PAD3 /# Keypad PgDn #/ + KEY_C1, 49('1'), CTL_PAD1, ALT_PAD1 /# Keypad End #/ + KEY_B2, 53('5'), CTL_PAD5, ALT_PAD5 /# Keypad 5 #/ + + Windows Telnet (raw) + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC [D, ESC [D /# Left #/ + ESC [C, ESC [C, ESC [C, ESC [C /# Right #/ + ESC [A, ESC [A, ESC [A, ESC [A /# Up #/ + ESC [B, ESC [B, ESC [B, ESC [B /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC [5~ /# Page Up #/ + ESC [6~, ESC [6~, ESC [6~, ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, ESC [D /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, ESC [C /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, ESC [A /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, ESC [B /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC [5~ /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, ESC [6~ /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# Keypad End #/ + nothing, nothing, nothing, nothing /# Keypad 5 #/ + + Windows Telnet (term=xterm) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_LEFT, KEY_LEFT, KEY_LEFT /# Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, KEY_RIGHT /# Right #/ + KEY_UP, KEY_UP, KEY_UP, KEY_UP /# Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, KEY_DOWN /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, KEY_PPAGE /# Page Up #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, KEY_NPAGE /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# End #/ + KEY_LEFT, KEY_LEFT, KEY_LEFT, O /# Keypad Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, O /# Keypad Right #/ + KEY_UP, KEY_UP, KEY_UP, O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, KEY_PPAGE /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, KEY_NPAGE /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, O /# Keypad End #/ + ESC [-71, nothing, nothing, O /# Keypad 5 #/ + + PuTTY + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC OD, ESC [D /# Left #/ + ESC [C, ESC [C, ESC OC, ESC [C /# Right #/ + ESC [A, ESC [A, ESC OA, ESC [A /# Up #/ + ESC [B, ESC [B, ESC OB, ESC [B /# Down #/ + ESC [1~, ESC [1~, local win, ESC [1~ /# Home #/ + ESC [5~, local win, local win, ESC [5~ /# Page Up #/ + ESC [6~, local win, local win, ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, local win, ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, O /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, O /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, O /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, O /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, O /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, O /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, O /# Keypad End #/ + nothing, nothing, nothing, O /# Keypad 5 #/ + + PuTTY + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_LEFT, ESC OD, ESC KEY_LEFT /# Left #/ + KEY_RIGHT KEY_RIGHT, ESC OC, ESC KEY_RIGHT /# Right #/ + KEY_UP, KEY_UP, ESC OA, ESC KEY_UP /# Up #/ + KEY_DOWN, KEY_DOWN, ESC OB, ESC KEY_DOWN /# Down #/ + ESC [1~, ESC [1~, local win, ESC ESC [1~ /# Home #/ + KEY_PPAGE local win, local win, ESC KEY_PPAGE /# Page Up #/ + KEY_NPAGE local win, local win, ESC KEY_NPAGE /# Page Down #/ + ESC [4~, ESC [4~, local win, ESC ESC [4~ /# End #/ + ESC Ot, ESC Ot, ESC Ot, O /# Keypad Left #/ + ESC Ov, ESC Ov, ESC Ov, O /# Keypad Right #/ + ESC Ox, ESC Ox, ESC Ox, O /# Keypad Up #/ + ESC Or, ESC Or, ESC Or, O /# Keypad Down #/ + ESC Ow, ESC Ow, ESC Ow, O /# Keypad Home #/ + ESC Oy, ESC Oy, ESC Oy, O /# Keypad PgUp #/ + ESC Os, ESC Os, ESC Os, O /# Keypad PgDn #/ + ESC Oq, ESC Oq, ESC Oq, O /# Keypad End #/ + ESC Ou, ESC Ou, ESC Ou, O /# Keypad 5 #/ +*/ + +#define M_NORMAL 0 +#define M_ESC 1 +#define M_KEYPAD 2 +#define M_TRAIL 3 + +int +md_readchar(WINDOW *win) +{ + int ch = 0; + int lastch = 0; + int mode = M_NORMAL; + int mode2 = M_NORMAL; + + for(;;) + { + ch = wgetch(win); + + if (ch == ERR) /* timed out waiting for valid sequence */ + { /* flush input so far and start over */ + mode = M_NORMAL; + nocbreak(); + raw(); + ch = 27; + break; + } + + if (mode == M_TRAIL) + { + if (ch == '^') /* msys console : 7,5,6,8: modified*/ + ch = MOD_MOVE( toupper(lastch) ); + + if (ch == '~') /* cygwin console: 1,5,6,4: normal */ + ch = tolower(lastch); /* windows telnet: 1,5,6,4: normal */ + /* msys console : 7,5,6,8: normal */ + + if (mode2 == M_ESC) /* cygwin console: 1,5,6,4: modified*/ + ch = MOD_MOVE( toupper(ch) ); + + break; + } + + if (mode == M_ESC) + { + if (ch == 27) + { + mode2 = M_ESC; + continue; + } + + if ((ch == 'F') || (ch == 'O') || (ch == '[')) + { + mode = M_KEYPAD; + continue; + } + + + switch(ch) + { + /* Cygwin Console */ + /* PuTTY */ + case KEY_LEFT : ch = MOD_MOVE('H'); break; + case KEY_RIGHT: ch = MOD_MOVE('L'); break; + case KEY_UP : ch = MOD_MOVE('K'); break; + case KEY_DOWN : ch = MOD_MOVE('J'); break; + case KEY_HOME : ch = MOD_MOVE('Y'); break; + case KEY_PPAGE: ch = MOD_MOVE('U'); break; + case KEY_NPAGE: ch = MOD_MOVE('N'); break; + case KEY_END : ch = MOD_MOVE('B'); break; + + default: break; + } + + break; + } + + if (mode == M_KEYPAD) + { + switch(ch) + { + /* ESC F - Interix Console codes */ + case '^': ch = MOD_MOVE('H'); break; /* Shift-Left */ + case '$': ch = MOD_MOVE('L'); break; /* Shift-Right */ + + /* ESC [ - Interix Console codes */ + case 'H': ch = 'y'; break; /* Home */ + case 1: ch = MOD_MOVE('K'); break; /* Ctl-Keypad Up */ + case 2: ch = MOD_MOVE('J'); break; /* Ctl-Keypad Down */ + case 3: ch = MOD_MOVE('L'); break; /* Ctl-Keypad Right */ + case 4: ch = MOD_MOVE('H'); break; /* Ctl-Keypad Left */ + case 263: ch = MOD_MOVE('Y'); break; /* Ctl-Keypad Home */ + case 19: ch = MOD_MOVE('U'); break; /* Ctl-Keypad PgUp */ + case 20: ch = MOD_MOVE('N'); break; /* Ctl-Keypad PgDn */ + case 21: ch = MOD_MOVE('B'); break; /* Ctl-Keypad End */ + + /* ESC [ - Cygwin Console codes */ + case 'G': ch = '.'; break; /* Keypad 5 */ + case '7': lastch = 'Y'; mode=M_TRAIL; break; /* Ctl-Home */ + case '5': lastch = 'U'; mode=M_TRAIL; break; /* Ctl-PgUp */ + case '6': lastch = 'N'; mode=M_TRAIL; break; /* Ctl-PgDn */ + + /* ESC [ - Win32 Telnet, PuTTY */ + case '1': lastch = 'y'; mode=M_TRAIL; break; /* Home */ + case '4': lastch = 'b'; mode=M_TRAIL; break; /* End */ + + /* ESC O - PuTTY */ + case 'D': ch = MOD_MOVE('H'); break; + case 'C': ch = MOD_MOVE('L'); break; + case 'A': ch = MOD_MOVE('K'); break; + case 'B': ch = MOD_MOVE('J'); break; + case 't': ch = 'h'; break; + case 'v': ch = 'l'; break; + case 'x': ch = 'k'; break; + case 'r': ch = 'j'; break; + case 'w': ch = 'y'; break; + case 'y': ch = 'u'; break; + case 's': ch = 'n'; break; + case 'q': ch = 'b'; break; + case 'u': ch = '.'; break; + } + + if (mode != M_KEYPAD) + continue; + } + + if (ch == 27) + { + halfdelay(1); + mode = M_ESC; + continue; + } + + switch(ch) + { + case KEY_LEFT : ch = 'h'; break; + case KEY_DOWN : ch = 'j'; break; + case KEY_UP : ch = 'k'; break; + case KEY_RIGHT : ch = 'l'; break; + case KEY_HOME : ch = 'y'; break; + case KEY_PPAGE : ch = 'u'; break; + case KEY_END : ch = 'b'; break; +#ifdef KEY_LL + case KEY_LL : ch = 'b'; break; +#endif + case KEY_NPAGE : ch = 'n'; break; + +#ifdef KEY_B1 + case KEY_B1 : ch = 'h'; break; + case KEY_C2 : ch = 'j'; break; + case KEY_A2 : ch = 'k'; break; + case KEY_B3 : ch = 'l'; break; +#endif + case KEY_A1 : ch = 'y'; break; + case KEY_A3 : ch = 'u'; break; + case KEY_C1 : ch = 'b'; break; + case KEY_C3 : ch = 'n'; break; + /* next should be '.', but for problem with putty/linux */ + case KEY_B2 : ch = 'u'; break; + +#ifdef KEY_SLEFT + case KEY_SRIGHT : ch = MOD_MOVE('L'); break; + case KEY_SLEFT : ch = MOD_MOVE('H'); break; +#ifdef KEY_SUP + case KEY_SUP : ch = MOD_MOVE('K'); break; + case KEY_SDOWN : ch = MOD_MOVE('J'); break; +#endif + case KEY_SHOME : ch = MOD_MOVE('Y'); break; + case KEY_SPREVIOUS:ch = MOD_MOVE('U'); break; + case KEY_SEND : ch = MOD_MOVE('B'); break; + case KEY_SNEXT : ch = MOD_MOVE('N'); break; +#endif + case 0x146 : ch = MOD_MOVE('K'); break; /* Shift-Up */ + case 0x145 : ch = MOD_MOVE('J'); break; /* Shift-Down */ + +#ifdef CTL_RIGHT + case CTL_RIGHT : ch = MOD_MOVE('L'); break; + case CTL_LEFT : ch = MOD_MOVE('H'); break; + case CTL_UP : ch = MOD_MOVE('K'); break; + case CTL_DOWN : ch = MOD_MOVE('J'); break; + case CTL_HOME : ch = MOD_MOVE('Y'); break; + case CTL_PGUP : ch = MOD_MOVE('U'); break; + case CTL_END : ch = MOD_MOVE('B'); break; + case CTL_PGDN : ch = MOD_MOVE('N'); break; +#endif +#ifdef KEY_EOL + case KEY_EOL : ch = MOD_MOVE('B'); break; +#endif + +#ifndef CTL_PAD1 + /* MSYS rxvt console */ + case 511 : ch = MOD_MOVE('J'); break; /* Shift Dn */ + case 512 : ch = MOD_MOVE('J'); break; /* Ctl Down */ + case 514 : ch = MOD_MOVE('H'); break; /* Ctl Left */ + case 516 : ch = MOD_MOVE('L'); break; /* Ctl Right*/ + case 518 : ch = MOD_MOVE('K'); break; /* Shift Up */ + case 519 : ch = MOD_MOVE('K'); break; /* Ctl Up */ +#endif + +#ifdef CTL_PAD1 + case CTL_PAD1 : ch = MOD_MOVE('B'); break; + case CTL_PAD2 : ch = MOD_MOVE('J'); break; + case CTL_PAD3 : ch = MOD_MOVE('N'); break; + case CTL_PAD4 : ch = MOD_MOVE('H'); break; + case CTL_PAD5 : ch = '.'; break; + case CTL_PAD6 : ch = MOD_MOVE('L'); break; + case CTL_PAD7 : ch = MOD_MOVE('Y'); break; + case CTL_PAD8 : ch = MOD_MOVE('K'); break; + case CTL_PAD9 : ch = MOD_MOVE('U'); break; +#endif + +#ifdef ALT_RIGHT + case ALT_RIGHT : ch = MOD_MOVE('L'); break; + case ALT_LEFT : ch = MOD_MOVE('H'); break; + case ALT_DOWN : ch = MOD_MOVE('J'); break; + case ALT_HOME : ch = MOD_MOVE('Y'); break; + case ALT_PGUP : ch = MOD_MOVE('U'); break; + case ALT_END : ch = MOD_MOVE('B'); break; + case ALT_PGDN : ch = MOD_MOVE('N'); break; +#endif + +#ifdef ALT_PAD1 + case ALT_PAD1 : ch = MOD_MOVE('B'); break; + case ALT_PAD2 : ch = MOD_MOVE('J'); break; + case ALT_PAD3 : ch = MOD_MOVE('N'); break; + case ALT_PAD4 : ch = MOD_MOVE('H'); break; + case ALT_PAD5 : ch = '.'; break; + case ALT_PAD6 : ch = MOD_MOVE('L'); break; + case ALT_PAD7 : ch = MOD_MOVE('Y'); break; + case ALT_PAD8 : ch = MOD_MOVE('K'); break; + case ALT_PAD9 : ch = MOD_MOVE('U'); break; +#endif + } + + break; + } + + nocbreak(); /* disable halfdelay mode if on */ + raw(); + + return(ch & 0x7F); +} diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..0b2871f --- /dev/null +++ b/misc.c @@ -0,0 +1,432 @@ +/* + * all sorts of miscellaneous routines + * + * @(#)misc.c 3.13 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" +#include + +/* + * tr_name: + * print the name of a trap + */ + +char * +tr_name(ch) +char ch; +{ + register char *s; + + switch (ch) + { + case TRAPDOOR: + s = terse ? "A trapdoor." : "You found a trapdoor."; + when BEARTRAP: + s = terse ? "A beartrap." : "You found a beartrap."; + when SLEEPTRAP: + s = terse ? "A sleeping gas trap.":"You found a sleeping gas trap."; + when ARROWTRAP: + s = terse ? "An arrow trap." : "You found an arrow trap."; + when TELTRAP: + s = terse ? "A teleport trap." : "You found a teleport trap."; + when DARTTRAP: + s = terse ? "A dart trap." : "You found a poison dart trap."; + } + return s; +} + +/* + * Look: + * A quick glance all around the player + */ + +look(wakeup) +bool wakeup; +{ + register int x, y; + register char ch; + register int oldx, oldy; + register bool inpass; + register int passcount = 0; + register struct room *rp; + register int ey, ex; + + getyx(cw, oldy, oldx); + if (oldrp != NULL && (oldrp->r_flags & ISDARK) && off(player, ISBLIND)) + { + for (x = oldpos.x - 1; x <= oldpos.x + 1; x++) + for (y = oldpos.y - 1; y <= oldpos.y + 1; y++) + if ((y != hero.y || x != hero.x) && show(y, x) == FLOOR) + mvwaddch(cw, y, x, ' '); + } + inpass = ((rp = roomin(&hero)) == NULL); + ey = hero.y + 1; + ex = hero.x + 1; + for (x = hero.x - 1; x <= ex; x++) + if (x >= 0 && x < COLS) for (y = hero.y - 1; y <= ey; y++) + { + if (y <= 0 || y >= LINES - 1) + continue; + if (isupper(mvwinch(mw, y, x))) + { + register struct linked_list *it; + register struct thing *tp; + + if (wakeup) + it = wake_monster(y, x); + else + it = find_mons(y, x); + tp = (struct thing *) ldata(it); + if ((tp->t_oldch = mvinch(y, x)) == TRAP) + tp->t_oldch = + (trap_at(y,x)->tr_flags&ISFOUND) ? TRAP : FLOOR; + if (tp->t_oldch == FLOOR && (rp->r_flags & ISDARK) + && off(player, ISBLIND)) + tp->t_oldch = ' '; + } + /* + * Secret doors show as walls + */ + if ((ch = show(y, x)) == SECRETDOOR) + ch = secretdoor(y, x); + /* + * Don't show room walls if he is in a passage + */ + if (off(player, ISBLIND)) + { + if (y == hero.y && x == hero.x + || (inpass && (ch == '-' || ch == '|'))) + continue; + } + else if (y != hero.y || x != hero.x) + continue; + wmove(cw, y, x); + waddch(cw, ch); + if (door_stop && !firstmove && running) + { + switch (runch) + { + case 'h': + if (x == ex) + continue; + when 'j': + if (y == hero.y - 1) + continue; + when 'k': + if (y == ey) + continue; + when 'l': + if (x == hero.x - 1) + continue; + when 'y': + if ((x + y) - (hero.x + hero.y) >= 1) + continue; + when 'u': + if ((y - x) - (hero.y - hero.x) >= 1) + continue; + when 'n': + if ((x + y) - (hero.x + hero.y) <= -1) + continue; + when 'b': + if ((y - x) - (hero.y - hero.x) <= -1) + continue; + } + switch (ch) + { + case DOOR: + if (x == hero.x || y == hero.y) + running = FALSE; + break; + case PASSAGE: + if (x == hero.x || y == hero.y) + passcount++; + break; + case FLOOR: + case '|': + case '-': + case ' ': + break; + default: + running = FALSE; + break; + } + } + } + if (door_stop && !firstmove && passcount > 1) + running = FALSE; + mvwaddch(cw, hero.y, hero.x, PLAYER); + wmove(cw, oldy, oldx); + oldpos = hero; + oldrp = rp; +} + +/* + * secret_door: + * Figure out what a secret door looks like. + */ + +secretdoor(y, x) +register int y, x; +{ + register int i; + register struct room *rp; + register coord *cpp; + static coord cp; + + cp.y = y; + cp.x = x; + cpp = &cp; + for (rp = rooms, i = 0; i < MAXROOMS; rp++, i++) + if (inroom(rp, cpp)) + if (y == rp->r_pos.y || y == rp->r_pos.y + rp->r_max.y - 1) + return('-'); + else + return('|'); + + return('p'); +} + +/* + * find_obj: + * find the unclaimed object at y, x + */ + +struct linked_list * +find_obj(y, x) +register int y; +int x; +{ + register struct linked_list *obj; + register struct object *op; + + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + { + op = (struct object *) ldata(obj); + if (op->o_pos.y == y && op->o_pos.x == x) + return obj; + } + sprintf(prbuf, "Non-object %d,%d", y, x); + debug(prbuf); + return NULL; +} + +/* + * eat: + * She wants to eat something, so let her try + */ + +eat() +{ + register struct linked_list *item; + register struct object *obj; + + if ((item = get_item("eat", FOOD)) == NULL) + return; + obj = (struct object *) ldata(item); + if (obj->o_type != FOOD) + { + if (!terse) + msg("Ugh, you would get ill if you ate that."); + else + msg("That's Inedible!"); + return; + } + inpack--; + if (obj->o_which == 1) + msg("My, that was a yummy %s", fruit); + else + if (rnd(100) > 70) + { + msg("Yuk, this food tastes awful"); + pstats.s_exp++; + check_level(); + } + else + msg("Yum, that tasted good"); + if ((food_left += HUNGERTIME + rnd(400) - 200) > STOMACHSIZE) + food_left = STOMACHSIZE; + hungry_state = 0; + if (obj == cur_weapon) + cur_weapon = NULL; + if (--obj->o_count < 1) + { + detach(pack, item); + discard(item); + } +} + +/* + * Used to modify the playes strength + * it keeps track of the highest it has been, just in case + */ + +chg_str(amt) +register int amt; +{ + if (amt == 0) + return; + if (amt > 0) + { + while (amt--) + { + if (pstats.s_str.st_str < 18) + pstats.s_str.st_str++; + else if (pstats.s_str.st_add == 0) + pstats.s_str.st_add = rnd(50) + 1; + else if (pstats.s_str.st_add <= 50) + pstats.s_str.st_add = 51 + rnd(24); + else if (pstats.s_str.st_add <= 75) + pstats.s_str.st_add = 76 + rnd(14); + else if (pstats.s_str.st_add <= 90) + pstats.s_str.st_add = 91; + else if (pstats.s_str.st_add < 100) + pstats.s_str.st_add++; + } + if (pstats.s_str.st_str > max_stats.s_str.st_str || + (pstats.s_str.st_str == 18 && + pstats.s_str.st_add > max_stats.s_str.st_add)) + max_stats.s_str = pstats.s_str; + } + else + { + while (amt++) + { + if (pstats.s_str.st_str < 18 || pstats.s_str.st_add == 0) + pstats.s_str.st_str--; + else if (pstats.s_str.st_add < 51) + pstats.s_str.st_add = 0; + else if (pstats.s_str.st_add < 76) + pstats.s_str.st_add = 1 + rnd(50); + else if (pstats.s_str.st_add < 91) + pstats.s_str.st_add = 51 + rnd(25); + else if (pstats.s_str.st_add < 100) + pstats.s_str.st_add = 76 + rnd(14); + else + pstats.s_str.st_add = 91 + rnd(8); + } + if (pstats.s_str.st_str < 3) + pstats.s_str.st_str = 3; + } +} + +/* + * add_haste: + * add a haste to the player + */ + +add_haste(potion) +bool potion; +{ + if (on(player, ISHASTE)) + { + msg("You faint from exhaustion."); + no_command += rnd(8); + extinguish(nohaste); + } + else + { + player.t_flags |= ISHASTE; + if (potion) + fuse(nohaste, 0, rnd(4)+4, AFTER); + } +} + +/* + * aggravate: + * aggravate all the monsters on this level + */ + +aggravate() +{ + register struct linked_list *mi; + + for (mi = mlist; mi != NULL; mi = next(mi)) + runto(&((struct thing *) ldata(mi))->t_pos, &hero); +} + +/* + * for printfs: if string starts with a vowel, return "n" for an "an" + */ +char * +vowelstr(str) +register char *str; +{ + switch (*str) + { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + return "n"; + default: + return ""; + } +} + +/* + * see if the object is one of the currently used items + */ +is_current(obj) +register struct object *obj; +{ + if (obj == NULL) + return FALSE; + if (obj == cur_armor || obj == cur_weapon || obj == cur_ring[LEFT] + || obj == cur_ring[RIGHT]) + { + msg(terse ? "In use." : "That's already in use."); + return TRUE; + } + return FALSE; +} + +/* + * set up the direction co_ordinate for use in varios "prefix" commands + */ +get_dir() +{ + register char *prompt; + register bool gotit; + + if (!terse) + msg(prompt = "Which direction? "); + else + prompt = "Direction: "; + do + { + gotit = TRUE; + switch (readchar(cw)) + { + case 'h': case'H': delta.y = 0; delta.x = -1; + when 'j': case'J': delta.y = 1; delta.x = 0; + when 'k': case'K': delta.y = -1; delta.x = 0; + when 'l': case'L': delta.y = 0; delta.x = 1; + when 'y': case'Y': delta.y = -1; delta.x = -1; + when 'u': case'U': delta.y = -1; delta.x = 1; + when 'b': case'B': delta.y = 1; delta.x = -1; + when 'n': case'N': delta.y = 1; delta.x = 1; + when ESCAPE: return FALSE; + otherwise: + mpos = 0; + msg(prompt); + gotit = FALSE; + } + } until (gotit); + if (on(player, ISHUH) && rnd(100) > 80) + do + { + delta.y = rnd(3) - 1; + delta.x = rnd(3) - 1; + } while (delta.y == 0 && delta.x == 0); + mpos = 0; + return TRUE; +} diff --git a/monsters.c b/monsters.c new file mode 100644 index 0000000..8121959 --- /dev/null +++ b/monsters.c @@ -0,0 +1,240 @@ +/* + * File with various monster functions in it + * + * @(#)monsters.c 3.18 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" +#include +#include + +/* + * List of monsters in rough order of vorpalness + */ +char lvl_mons[27] = "KJBSHEAOZGLCRQNYTWFIXUMVDP"; +char wand_mons[27] = "KJBSH AOZG CRQ Y W IXU V "; + +/* + * randmonster: + * Pick a monster to show up. The lower the level, + * the meaner the monster. + */ + +randmonster(wander) +bool wander; +{ + register int d; + register char *mons; + + mons = wander ? wand_mons : lvl_mons; + do + { + d = level + (rnd(10) - 5); + if (d < 1) + d = rnd(5) + 1; + if (d > 26) + d = rnd(5) + 22; + } while (mons[--d] == ' '); + return mons[d]; +} + +/* + * new_monster: + * Pick a new monster and add it to the list + */ + +new_monster(item, type, cp) +struct linked_list *item; +char type; +register coord *cp; +{ + register struct thing *tp; + register struct monster *mp; + + attach(mlist, item); + tp = (struct thing *) ldata(item); + tp->t_type = type; + tp->t_pos = *cp; + tp->t_oldch = mvwinch(cw, cp->y, cp->x); + mvwaddch(mw, cp->y, cp->x, tp->t_type); + mp = &monsters[tp->t_type-'A']; + tp->t_stats.s_hpt = roll(mp->m_stats.s_lvl, 8); + tp->t_stats.s_lvl = mp->m_stats.s_lvl; + tp->t_stats.s_arm = mp->m_stats.s_arm; + strcpy(tp->t_stats.s_dmg,mp->m_stats.s_dmg); + tp->t_stats.s_exp = mp->m_stats.s_exp; + tp->t_stats.s_str.st_str = 10; + tp->t_flags = mp->m_flags; + tp->t_turn = TRUE; + tp->t_pack = NULL; + if (ISWEARING(R_AGGR)) + runto(cp, &hero); + if (type == 'M') + { + char mch; + + if (tp->t_pack != NULL) + mch = ((struct object *) ldata(tp->t_pack))->o_type; + else + switch (rnd(level > 25 ? 9 : 8)) + { + case 0: mch = GOLD; + when 1: mch = POTION; + when 2: mch = SCROLL; + when 3: mch = STAIRS; + when 4: mch = WEAPON; + when 5: mch = ARMOR; + when 6: mch = RING; + when 7: mch = STICK; + when 8: mch = AMULET; + } + tp->t_disguise = mch; + } +} + +/* + * wanderer: + * A wandering monster has awakened and is headed for the player + */ + +wanderer() +{ + register int i, ch; + register struct room *rp, *hr = roomin(&hero); + register struct linked_list *item; + register struct thing *tp; + coord cp; + + item = new_item(sizeof *tp); + do + { + i = rnd_room(); + if ((rp = &rooms[i]) == hr) + continue; + rnd_pos(rp, &cp); + if ((ch = mvwinch(stdscr, cp.y, cp.x)) == ERR) + { + debug("Routine wanderer: mvwinch failed to %d,%d", cp.y, cp.x); + if (wizard) + wait_for(cw,'\n'); + return; + } + } until(hr != rp && step_ok(ch)); + new_monster(item, randmonster(TRUE), &cp); + tp = (struct thing *) ldata(item); + tp->t_flags |= ISRUN; + tp->t_pos = cp; + tp->t_dest = &hero; + if (wizard) + msg("Started a wandering %s", monsters[tp->t_type-'A'].m_name); +} + +/* + * what to do when the hero steps next to a monster + */ +struct linked_list * +wake_monster(y, x) +int y, x; +{ + register struct thing *tp; + register struct linked_list *it; + register struct room *rp; + register char ch; + + if ((it = find_mons(y, x)) == NULL) + fatal("Can't find monster in wake"); + tp = (struct thing *) ldata(it); + ch = tp->t_type; + /* + * Every time he sees mean monster, it might start chasing him + */ + if (rnd(100) > 33 && on(*tp, ISMEAN) && off(*tp, ISHELD) + && !ISWEARING(R_STEALTH)) + { + tp->t_dest = &hero; + tp->t_flags |= ISRUN; + } + if (ch == 'U' && off(player, ISBLIND)) + { + rp = roomin(&hero); + if ((rp != NULL && !(rp->r_flags&ISDARK)) + || DISTANCE(y, x, hero.y, hero.x) < 3) + { + if (off(*tp, ISFOUND) && !save(VS_MAGIC)) + { + msg("The umber hulk's gaze has confused you."); + if (on(player, ISHUH)) + lengthen(unconfuse, rnd(20)+HUHDURATION); + else + fuse(unconfuse, 0, rnd(20)+HUHDURATION, AFTER); + player.t_flags |= ISHUH; + } + tp->t_flags |= ISFOUND; + } + } + /* + * Hide invisible monsters + */ + if (on(*tp, ISINVIS) && off(player, CANSEE)) + ch = mvwinch(stdscr, y, x); + /* + * Let greedy ones guard gold + */ + if (on(*tp, ISGREED) && off(*tp, ISRUN)) + { + rp = roomin(&hero); + + if (rp != NULL && rp->r_goldval) + { + tp->t_dest = &rp->r_gold; + tp->t_flags |= ISRUN; + } + } + + return it; +} + +genocide() +{ + register struct linked_list *ip; + register struct thing *mp; + register char c; + register int i; + register struct linked_list *nip; + + addmsg("Which monster"); + if (!terse) + addmsg(" do you wish to wipe out"); + msg("? "); + while (!isalpha(c = readchar(cw))) + if (c == ESCAPE) + return; + else + { + mpos = 0; + msg("Please specify a letter between 'A' and 'Z'"); + } + if (islower(c)) + c = toupper(c); + for (ip = mlist; ip; ip = nip) + { + mp = (struct thing *) ldata(ip); + nip = next(ip); + if (mp->t_type == c) + remove_monster(&mp->t_pos, ip); + } + for (i = 0; i < 26; i++) + if (lvl_mons[i] == c) + { + lvl_mons[i] = ' '; + wand_mons[i] = ' '; + break; + } +} diff --git a/move.c b/move.c new file mode 100644 index 0000000..e49107b --- /dev/null +++ b/move.c @@ -0,0 +1,385 @@ +/* + * Hero movement commands + * + * @(#)move.c 3.26 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include "rogue.h" + +/* + * Used to hold the new hero position + */ + +coord nh; + +/* + * do_run: + * Start the hero running + */ + +do_run(ch) +char ch; +{ + running = TRUE; + after = FALSE; + runch = ch; +} + +/* + * do_move: + * Check to see that a move is legal. If it is handle the + * consequences (fighting, picking up, etc.) + */ + +do_move(dy, dx) +int dy, dx; +{ + register char ch; + + firstmove = FALSE; + if (no_move) + { + no_move--; + msg("You are still stuck in the bear trap"); + return; + } + /* + * Do a confused move (maybe) + */ + if (rnd(100) < 80 && on(player, ISHUH)) + nh = *rndmove(&player); + else + { + nh.y = hero.y + dy; + nh.x = hero.x + dx; + } + + /* + * Check if he tried to move off the screen or make an illegal + * diagonal move, and stop him if he did. + */ + if (nh.x < 0 || nh.x > COLS-1 || nh.y < 0 || nh.y > LINES - 1 + || !diag_ok(&hero, &nh)) + { + after = FALSE; + running = FALSE; + return; + } + if (running && ce(hero, nh)) + after = running = FALSE; + ch = winat(nh.y, nh.x); + if (on(player, ISHELD) && ch != 'F') + { + msg("You are being held"); + return; + } + switch(ch) + { + case ' ': + case '|': + case '-': + case SECRETDOOR: + after = running = FALSE; + return; + case TRAP: + ch = be_trapped(&nh); + if (ch == TRAPDOOR || ch == TELTRAP) + return; + goto move_stuff; + case GOLD: + case POTION: + case SCROLL: + case FOOD: + case WEAPON: + case ARMOR: + case RING: + case AMULET: + case STICK: + running = FALSE; + take = ch; + default: +move_stuff: + if (ch == PASSAGE && winat(hero.y, hero.x) == DOOR) + light(&hero); + else if (ch == DOOR) + { + running = FALSE; + if (winat(hero.y, hero.x) == PASSAGE) + light(&nh); + } + else if (ch == STAIRS) + running = FALSE; + else if (isupper(ch)) + { + running = FALSE; + fight(&nh, ch, cur_weapon, FALSE); + return; + } + ch = winat(hero.y, hero.x); + wmove(cw, unc(hero)); + waddch(cw, ch); + hero = nh; + wmove(cw, unc(hero)); + waddch(cw, PLAYER); + } +} + +/* + * Called to illuminate a room. + * If it is dark, remove anything that might move. + */ + +light(cp) +coord *cp; +{ + register struct room *rp; + register int j, k; + register char ch, rch; + register struct linked_list *item; + + if ((rp = roomin(cp)) != NULL && !on(player, ISBLIND)) + { + for (j = 0; j < rp->r_max.y; j++) + { + for (k = 0; k < rp->r_max.x; k++) + { + ch = show(rp->r_pos.y + j, rp->r_pos.x + k); + wmove(cw, rp->r_pos.y + j, rp->r_pos.x + k); + /* + * Figure out how to display a secret door + */ + if (ch == SECRETDOOR) + { + if (j == 0 || j == rp->r_max.y - 1) + ch = '-'; + else + ch = '|'; + } + /* + * If the room is a dark room, we might want to remove + * monsters and the like from it (since they might + * move) + */ + if (isupper(ch)) + { + item = wake_monster(rp->r_pos.y+j, rp->r_pos.x+k); + if (((struct thing *) ldata(item))->t_oldch == ' ') + if (!(rp->r_flags & ISDARK)) + ((struct thing *) ldata(item))->t_oldch = + mvwinch(stdscr, rp->r_pos.y+j, rp->r_pos.x+k); + } + if (rp->r_flags & ISDARK) + { + rch = mvwinch(cw, rp->r_pos.y+j, rp->r_pos.x+k); + switch (rch) + { + case DOOR: + case STAIRS: + case TRAP: + case '|': + case '-': + case ' ': + ch = rch; + when FLOOR: + ch = (on(player, ISBLIND) ? FLOOR : ' '); + otherwise: + ch = ' '; + } + } + mvwaddch(cw, rp->r_pos.y+j, rp->r_pos.x+k, ch); + } + } + } +} + +/* + * show: + * returns what a certain thing will display as to the un-initiated + */ + +show(y, x) +register int y, x; +{ + register char ch = winat(y, x); + register struct linked_list *it; + register struct thing *tp; + + if (ch == TRAP) + return (trap_at(y, x)->tr_flags & ISFOUND) ? TRAP : FLOOR; + else if (ch == 'M' || ch == 'I') + { + if ((it = find_mons(y, x)) == NULL) + fatal("Can't find monster in show"); + tp = (struct thing *) ldata(it); + if (ch == 'M') + ch = tp->t_disguise; + /* + * Hide invisible monsters + */ + else if (off(player, CANSEE)) + ch = mvwinch(stdscr, y, x); + } + return ch; +} + +/* + * be_trapped: + * The guy stepped on a trap.... Make him pay. + */ + +be_trapped(tc) +register coord *tc; +{ + register struct trap *tp; + register char ch; + + tp = trap_at(tc->y, tc->x); + count = running = FALSE; + mvwaddch(cw, tp->tr_pos.y, tp->tr_pos.x, TRAP); + tp->tr_flags |= ISFOUND; + switch (ch = tp->tr_type) + { + case TRAPDOOR: + level++; + new_level(); + msg("You fell into a trap!"); + when BEARTRAP: + no_move += BEARTIME; + msg("You are caught in a bear trap"); + when SLEEPTRAP: + no_command += SLEEPTIME; + msg("A strange white mist envelops you and you fall asleep"); + when ARROWTRAP: + if (swing(pstats.s_lvl-1, pstats.s_arm, 1)) + { + msg("Oh no! An arrow shot you"); + if ((pstats.s_hpt -= roll(1, 6)) <= 0) + { + msg("The arrow killed you."); + death('a'); + } + } + else + { + register struct linked_list *item; + register struct object *arrow; + + msg("An arrow shoots past you."); + item = new_item(sizeof *arrow); + arrow = (struct object *) ldata(item); + arrow->o_type = WEAPON; + arrow->o_which = ARROW; + init_weapon(arrow, ARROW); + arrow->o_count = 1; + arrow->o_pos = hero; + arrow->o_hplus = arrow->o_dplus = 0; /* "arrow bug" FIX */ + fall(item, FALSE); + } + when TELTRAP: + teleport(); + when DARTTRAP: + if (swing(pstats.s_lvl+1, pstats.s_arm, 1)) + { + msg("A small dart just hit you in the shoulder"); + if ((pstats.s_hpt -= roll(1, 4)) <= 0) + { + msg("The dart killed you."); + death('d'); + } + if (!ISWEARING(R_SUSTSTR)) + chg_str(-1); + } + else + msg("A small dart whizzes by your ear and vanishes."); + } + flush_type(); /* flush typeahead */ + return(ch); +} + +/* + * trap_at: + * find the trap at (y,x) on screen. + */ + +struct trap * +trap_at(y, x) +register int y, x; +{ + register struct trap *tp, *ep; + + ep = &traps[ntraps]; + for (tp = traps; tp < ep; tp++) + if (tp->tr_pos.y == y && tp->tr_pos.x == x) + break; + if (tp == ep) + { + sprintf(prbuf, "Trap at %d,%d not in array", y, x); + fatal(prbuf); + } + return tp; +} + +/* + * rndmove: + * move in a random direction if the monster/person is confused + */ + +coord * +rndmove(who) +struct thing *who; +{ + register int x, y; + register char ch; + register int ex, ey, nopen = 0; + register struct linked_list *item; + register struct object *obj; + static coord ret; /* what we will be returning */ + static coord dest; + + ret = who->t_pos; + /* + * Now go through the spaces surrounding the player and + * set that place in the array to true if the space can be + * moved into + */ + ey = ret.y + 1; + ex = ret.x + 1; + for (y = who->t_pos.y - 1; y <= ey; y++) + if (y >= 0 && y < LINES) + for (x = who->t_pos.x - 1; x <= ex; x++) + { + if (x < 0 || x >= COLS) + continue; + ch = winat(y, x); + if (step_ok(ch)) + { + dest.y = y; + dest.x = x; + if (!diag_ok(&who->t_pos, &dest)) + continue; + if (ch == SCROLL) + { + item = NULL; + for (item = lvl_obj; item != NULL; item = next(item)) + { + obj = (struct object *) ldata(item); + if (y == obj->o_pos.y && x == obj->o_pos.x) + break; + } + if (item != NULL && obj->o_which == S_SCARE) + continue; + } + if (rnd(++nopen) == 0) + ret = dest; + } + } + return &ret; +} diff --git a/newlevel.c b/newlevel.c new file mode 100644 index 0000000..3f20c21 --- /dev/null +++ b/newlevel.c @@ -0,0 +1,173 @@ +/* + * new_level: + * Dig and draw a new level + * + * @(#)new_level.c 3.7 (Berkeley) 6/2/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" + +#include + +new_level() +{ + register int rm, i; + register char ch; + coord stairs; + + if (level > max_level) + max_level = level; + wclear(cw); + wclear(mw); + clear(); + status(); + /* + * Free up the monsters on the last level + */ + free_list(mlist); + do_rooms(); /* Draw rooms */ + do_passages(); /* Draw passages */ + no_food++; + put_things(); /* Place objects (if any) */ + /* + * Place the staircase down. + */ + do { + rm = rnd_room(); + rnd_pos(&rooms[rm], &stairs); + } until (winat(stairs.y, stairs.x) == FLOOR); + addch(STAIRS); + /* + * Place the traps + */ + if (rnd(10) < level) + { + ntraps = rnd(level/4)+1; + if (ntraps > MAXTRAPS) + ntraps = MAXTRAPS; + i = ntraps; + while (i--) + { + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &stairs); + } until (winat(stairs.y, stairs.x) == FLOOR); + switch(rnd(6)) + { + case 0: ch = TRAPDOOR; + when 1: ch = BEARTRAP; + when 2: ch = SLEEPTRAP; + when 3: ch = ARROWTRAP; + when 4: ch = TELTRAP; + when 5: ch = DARTTRAP; + } + addch(TRAP); + traps[i].tr_type = ch; + traps[i].tr_flags = 0; + traps[i].tr_pos = stairs; + } + } + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &hero); + } + until(winat(hero.y, hero.x) == FLOOR); + light(&hero); + wmove(cw, hero.y, hero.x); + waddch(cw, PLAYER); +} + +/* + * Pick a room that is really there + */ + +rnd_room() +{ + register int rm; + + do + { + rm = rnd(MAXROOMS); + } while (rooms[rm].r_flags & ISGONE); + return rm; +} + +/* + * put_things: + * put potions and scrolls on this level + */ + +put_things() +{ + register int i; + register struct linked_list *item; + register struct object *cur; + register int rm; + coord tp; + + /* + * Throw away stuff left on the previous level (if anything) + */ + free_list(lvl_obj); + /* + * Once you have found the amulet, the only way to get new stuff is + * go down into the dungeon. + */ + if (amulet && level < max_level) + return; + /* + * Do MAXOBJ attempts to put things on a level + */ + for (i = 0; i < MAXOBJ; i++) + if (rnd(100) < 35) + { + /* + * Pick a new object and link it in the list + */ + item = new_thing(); + attach(lvl_obj, item); + cur = (struct object *) ldata(item); + /* + * Put it somewhere + */ + rm = rnd_room(); + do { + rnd_pos(&rooms[rm], &tp); + } until (winat(tp.y, tp.x) == FLOOR); + mvaddch(tp.y, tp.x, cur->o_type); + cur->o_pos = tp; + } + /* + * If he is really deep in the dungeon and he hasn't found the + * amulet yet, put it somewhere on the ground + */ + if (level > 25 && !amulet) + { + item = new_item(sizeof *cur); + attach(lvl_obj, item); + cur = (struct object *) ldata(item); + cur->o_hplus = cur->o_dplus = 0; + strcpy(cur->o_damage, "0d0"); + strcpy(cur->o_hurldmg, "0d0"); + cur->o_ac = 11; + cur->o_type = AMULET; + /* + * Put it somewhere + */ + do { + rm = rnd_room(); + rnd_pos(&rooms[rm], &tp); + } until (winat(tp.y, tp.x) == FLOOR); + mvaddch(tp.y, tp.x, cur->o_type); + cur->o_pos = tp; + } +} diff --git a/options.c b/options.c new file mode 100644 index 0000000..537e130 --- /dev/null +++ b/options.c @@ -0,0 +1,352 @@ +/* + * This file has all the code for the option command. + * I would rather this command were not necessary, but + * it is the only way to keep the wolves off of my back. + * + * @(#)options.c 3.3 (Berkeley) 5/25/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +#define NUM_OPTS (sizeof optlist / sizeof (OPTION)) + +/* + * description of an option and what to do with it + */ +struct optstruct { + char *o_name; /* option name */ + char *o_prompt; /* prompt for interactive entry */ + int *o_opt; /* pointer to thing to set */ + int (*o_putfunc)(); /* function to print value */ + int (*o_getfunc)(); /* function to get value interactively */ +}; + +typedef struct optstruct OPTION; + +int put_bool(), get_bool(), put_str(), get_str(); + +OPTION optlist[] = { + {"terse", "Terse output: ", + (int *) &terse, put_bool, get_bool }, + {"flush", "Flush typeahead during battle: ", + (int *) &fight_flush, put_bool, get_bool }, + {"jump", "Show position only at end of run: ", + (int *) &jump, put_bool, get_bool }, + {"step", "Do inventories one line at a time: ", + (int *) &slow_invent, put_bool, get_bool }, + {"askme", "Ask me about unidentified things: ", + (int *) &askme, put_bool, get_bool }, + {"name", "Name: ", + (int *) whoami, put_str, get_str }, + {"fruit", "Fruit: ", + (int *) fruit, put_str, get_str }, + {"file", "Save file: ", + (int *) file_name, put_str, get_str } +}; + +/* + * print and then set options from the terminal + */ +option() +{ + register OPTION *op; + register int retval; + + wclear(hw); + touchwin(hw); + /* + * Display current values of options + */ + for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++) + { + waddstr(hw, op->o_prompt); + (*op->o_putfunc)(op->o_opt); + waddch(hw, '\n'); + } + /* + * Set values + */ + wmove(hw, 0, 0); + for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++) + { + waddstr(hw, op->o_prompt); + if ((retval = (*op->o_getfunc)(op->o_opt, hw))) + if (retval == QUIT) + break; + else if (op > optlist) { /* MINUS */ + wmove(hw, (op - optlist) - 1, 0); + op -= 2; + } + else /* trying to back up beyond the top */ + { + beep(); + wmove(hw, 0, 0); + op--; + } + } + /* + * Switch back to original screen + */ + mvwaddstr(hw, LINES-1, 0, "--Press space to continue--"); + draw(hw); + wait_for(hw,' '); + clearok(cw, TRUE); + touchwin(cw); + after = FALSE; +} + +/* + * put out a boolean + */ +put_bool(b) +bool *b; +{ + waddstr(hw, *b ? "True" : "False"); +} + +/* + * put out a string + */ +put_str(str) +char *str; +{ + waddstr(hw, str); +} + +/* + * allow changing a boolean option and print it out + */ + +get_bool(bp, win) +bool *bp; +WINDOW *win; +{ + register int oy, ox; + register bool op_bad; + + op_bad = TRUE; + getyx(win, oy, ox); + waddstr(win, *bp ? "True" : "False"); + while(op_bad) + { + wmove(win, oy, ox); + draw(win); + switch (readchar(win)) + { + case 't': + case 'T': + *bp = TRUE; + op_bad = FALSE; + break; + case 'f': + case 'F': + *bp = FALSE; + op_bad = FALSE; + break; + case '\n': + case '\r': + op_bad = FALSE; + break; + case '\033': + case '\007': + return QUIT; + case '-': + return MINUS; + default: + mvwaddstr(win, oy, ox + 10, "(T or F)"); + } + } + wmove(win, oy, ox); + waddstr(win, *bp ? "True" : "False"); + waddch(win, '\n'); + return NORM; +} + +/* + * set a string option + */ +get_str(opt, win) +register char *opt; +WINDOW *win; +{ + register char *sp; + register int c, oy, ox; + char buf[80]; + + draw(win); + getyx(win, oy, ox); + /* + * loop reading in the string, and put it in a temporary buffer + */ + for (sp = buf; + (c = readchar(win)) != '\n' && c != '\r' && c != '\033' && c != '\007'; + wclrtoeol(win), draw(win)) + { + if (c == -1) + continue; + else if (c == md_erasechar()) /* process erase character */ + { + if (sp > buf) + { + register int i; + int myx, myy; + + sp--; + + for (i = (int) strlen(unctrl(*sp)); i; i--) + { + getyx(win,myy,myx); + if ((myx == 0)&& (myy > 0)) + { + wmove(win,myy-1,getmaxx(win)-1); + waddch(win,' '); + wmove(win,myy-1,getmaxx(win)-1); + } + else + waddch(win, '\b'); + } + } + continue; + } + else if (c == md_killchar()) /* process kill character */ + { + sp = buf; + wmove(win, oy, ox); + continue; + } + else if (sp == buf) + if (c == '-') + break; + else if (c == '~') + { + strcpy(buf, home); + waddstr(win, home); + sp += strlen(home); + continue; + } + + if ((sp - buf) < 78) /* Avoid overflow */ + { + *sp++ = c; + waddstr(win, unctrl(c)); + } + } + *sp = '\0'; + if (sp > buf) /* only change option if something has been typed */ + strucpy(opt, buf, strlen(buf)); + wmove(win, oy, ox); + waddstr(win, opt); + waddch(win, '\n'); + draw(win); + if (win == cw) + mpos += sp - buf; + if (c == '-') + return MINUS; + else if (c == '\033' || c == '\007') + return QUIT; + else + return NORM; +} + +/* + * parse options from string, usually taken from the environment. + * the string is a series of comma seperated values, with booleans + * being stated as "name" (true) or "noname" (false), and strings + * being "name=....", with the string being defined up to a comma + * or the end of the entire option string. + */ + +parse_opts(str) +register char *str; +{ + register char *sp; + register OPTION *op; + register int len; + + while (*str) + { + /* + * Get option name + */ + for (sp = str; isalpha(*sp); sp++) + continue; + len = sp - str; + /* + * Look it up and deal with it + */ + for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++) + if (EQSTR(str, op->o_name, len)) + { + if (op->o_putfunc == put_bool) /* if option is a boolean */ + *(bool *)op->o_opt = TRUE; + else /* string option */ + { + register char *start; + /* + * Skip to start of string value + */ + for (str = sp + 1; *str == '='; str++) + continue; + if (*str == '~') + { + strcpy((char *) op->o_opt, home); + start = (char *) op->o_opt + strlen(home); + while (*++str == '/') + continue; + } + else + start = (char *) op->o_opt; + /* + * Skip to end of string value + */ + for (sp = str + 1; *sp && *sp != ','; sp++) + continue; + strucpy(start, str, sp - str); + } + break; + } + /* + * check for "noname" for booleans + */ + else if (op->o_putfunc == put_bool + && EQSTR(str, "no", 2) && EQSTR(str + 2, op->o_name, len - 2)) + { + *(bool *)op->o_opt = FALSE; + break; + } + + /* + * skip to start of next option name + */ + while (*sp && !isalpha(*sp)) + sp++; + str = sp; + } +} + +/* + * copy string using unctrl for things + */ +strucpy(s1, s2, len) +register char *s1, *s2; +register int len; +{ + register char *sp; + + while (len--) + { + strcpy(s1, (sp = unctrl(*s2))); + s1 += strlen(sp); + s2++; + } + *s1 = '\0'; +} diff --git a/pack.c b/pack.c new file mode 100644 index 0000000..6dcb7f0 --- /dev/null +++ b/pack.c @@ -0,0 +1,402 @@ +/* + * Routines to deal with the pack + * + * @(#)pack.c 3.6 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include "rogue.h" + +/* + * add_pack: + * Pick up an object and add it to the pack. If the argument is non-null + * use it as the linked_list pointer instead of gettting it off the ground. + */ +add_pack(item, silent) +register struct linked_list *item; +bool silent; +{ + register struct linked_list *ip, *lp; + register struct object *obj, *op; + register bool exact, from_floor; + + if (item == NULL) + { + from_floor = TRUE; + if ((item = find_obj(hero.y, hero.x)) == NULL) + return; + } + else + from_floor = FALSE; + obj = (struct object *) ldata(item); + /* + * Link it into the pack. Search the pack for a object of similar type + * if there isn't one, stuff it at the beginning, if there is, look for one + * that is exactly the same and just increment the count if there is. + * it that. Food is always put at the beginning for ease of access, but + * is not ordered so that you can't tell good food from bad. First check + * to see if there is something in thr same group and if there is then + * increment the count. + */ + if (obj->o_group) + { + for (ip = pack; ip != NULL; ip = next(ip)) + { + op = (struct object *) ldata(ip); + if (op->o_group == obj->o_group) + { + /* + * Put it in the pack and notify the user + */ + op->o_count++; + if (from_floor) + { + detach(lvl_obj, item); + mvaddch(hero.y, hero.x, + (roomin(&hero) == NULL ? PASSAGE : FLOOR)); + } + discard(item); + item = ip; + goto picked_up; + } + } + } + /* + * Check if there is room + */ + if (inpack == MAXPACK-1) + { + msg("You can't carry anything else."); + return; + } + /* + * Check for and deal with scare monster scrolls + */ + if (obj->o_type == SCROLL && obj->o_which == S_SCARE) + if (obj->o_flags & ISFOUND) + { + msg("The scroll turns to dust as you pick it up."); + detach(lvl_obj, item); + mvaddch(hero.y, hero.x, FLOOR); + return; + } + else + obj->o_flags |= ISFOUND; + + inpack++; + if (from_floor) + { + detach(lvl_obj, item); + mvaddch(hero.y, hero.x, (roomin(&hero) == NULL ? PASSAGE : FLOOR)); + } + /* + * Search for an object of the same type + */ + exact = FALSE; + for (ip = pack; ip != NULL; ip = next(ip)) + { + op = (struct object *) ldata(ip); + if (obj->o_type == op->o_type) + break; + } + if (ip == NULL) + { + /* + * Put it at the end of the pack since it is a new type + */ + for (ip = pack; ip != NULL; ip = next(ip)) + { + op = (struct object *) ldata(ip); + if (op->o_type != FOOD) + break; + lp = ip; + } + } + else + { + /* + * Search for an object which is exactly the same + */ + while (ip != NULL && op->o_type == obj->o_type) + { + if (op->o_which == obj->o_which) + { + exact = TRUE; + break; + } + lp = ip; + if ((ip = next(ip)) == NULL) + break; + op = (struct object *) ldata(ip); + } + } + if (ip == NULL) + { + /* + * Didn't find an exact match, just stick it here + */ + if (pack == NULL) + pack = item; + else + { + lp->l_next = item; + item->l_prev = lp; + item->l_next = NULL; + } + } + else + { + /* + * If we found an exact match. If it is a potion, food, or a + * scroll, increase the count, otherwise put it with its clones. + */ + if (exact && ISMULT(obj->o_type)) + { + op->o_count++; + discard(item); + item = ip; + goto picked_up; + } + if ((item->l_prev = prev(ip)) != NULL) + item->l_prev->l_next = item; + else + pack = item; + item->l_next = ip; + ip->l_prev = item; + } +picked_up: + /* + * Notify the user + */ + obj = (struct object *) ldata(item); + if (notify && !silent) + { + if (!terse) + addmsg("You now have "); + msg("%s (%c)", inv_name(obj, !terse), pack_char(obj)); + } + if (obj->o_type == AMULET) + amulet = TRUE; +} + +/* + * inventory: + * list what is in the pack + */ +inventory(list, type) +struct linked_list *list; +int type; +{ + register struct object *obj; + register char ch; + register int n_objs; + char inv_temp[80]; + + n_objs = 0; + for (ch = 'a'; list != NULL; ch++, list = next(list)) + { + obj = (struct object *) ldata(list); + if (type && type != obj->o_type && !(type == CALLABLE && + (obj->o_type == SCROLL || obj->o_type == POTION || + obj->o_type == RING || obj->o_type == STICK))) + continue; + switch (n_objs++) + { + /* + * For the first thing in the inventory, just save the string + * in case there is only one. + */ + case 0: + sprintf(inv_temp, "%c) %s", ch, inv_name(obj, FALSE)); + break; + /* + * If there is more than one, clear the screen, print the + * saved message and fall through to ... + */ + case 1: + if (slow_invent) + msg(inv_temp); + else + { + wclear(hw); + waddstr(hw, inv_temp); + waddch(hw, '\n'); + } + /* + * Print the line for this object + */ + default: + if (slow_invent) + msg("%c) %s", ch, inv_name(obj, FALSE)); + else + wprintw(hw, "%c) %s\n", ch, inv_name(obj, FALSE)); + } + } + if (n_objs == 0) + { + if (terse) + msg(type == 0 ? "Empty handed." : + "Nothing appropriate"); + else + msg(type == 0 ? "You are empty handed." : + "You don't have anything appropriate"); + return FALSE; + } + if (n_objs == 1) + { + msg(inv_temp); + return TRUE; + } + if (!slow_invent) + { + mvwaddstr(hw, LINES-1, 0, "--Press space to continue--"); + draw(hw); + wait_for(hw,' '); + clearok(cw, TRUE); + touchwin(cw); + } + return TRUE; +} + +/* + * pick_up: + * Add something to characters pack. + */ +pick_up(ch) +char ch; +{ + switch(ch) + { + case GOLD: + money(); + break; + default: + debug("Where did you pick that up???"); + case ARMOR: + case POTION: + case FOOD: + case WEAPON: + case SCROLL: + case AMULET: + case RING: + case STICK: + add_pack(NULL, FALSE); + break; + } +} + +/* + * picky_inven: + * Allow player to inventory a single item + */ +picky_inven() +{ + register struct linked_list *item; + register char ch, mch; + + if (pack == NULL) + msg("You aren't carrying anything"); + else if (next(pack) == NULL) + msg("a) %s", inv_name((struct object *) ldata(pack), FALSE)); + else + { + msg(terse ? "Item: " : "Which item do you wish to inventory: "); + mpos = 0; + if ((mch = readchar(cw)) == ESCAPE) + { + msg(""); + return; + } + for (ch = 'a', item = pack; item != NULL; item = next(item), ch++) + if (ch == mch) + { + msg("%c) %s",ch,inv_name((struct object *) ldata(item), FALSE)); + return; + } + if (!terse) + msg("'%s' not in pack", unctrl(mch)); + msg("Range is 'a' to '%c'", --ch); + } +} + +/* + * get_item: + * pick something out of a pack for a purpose + */ +struct linked_list * +get_item(purpose, type) +char *purpose; +int type; +{ + register struct linked_list *obj; + register char ch, och; + + if (pack == NULL) + msg("You aren't carrying anything."); + else + { + for (;;) + { + if (!terse) + addmsg("Which object do you want to "); + addmsg(purpose); + if (terse) + addmsg(" what"); + msg("? (* for list): "); + ch = readchar(cw); + mpos = 0; + /* + * Give the poor player a chance to abort the command + */ + if (ch == ESCAPE || ch == CTRL('G')) + { + after = FALSE; + msg(""); + return NULL; + } + if (ch == '*') + { + mpos = 0; + if (inventory(pack, type) == 0) + { + after = FALSE; + return NULL; + } + continue; + } + for (obj = pack, och = 'a'; obj != NULL; obj = next(obj), och++) + if (ch == och) + break; + if (obj == NULL) + { + msg("Please specify a letter between 'a' and '%c'", och-1); + continue; + } + else + return obj; + } + } + return NULL; +} + +pack_char(obj) +register struct object *obj; +{ + register struct linked_list *item; + register char c; + + c = 'a'; + for (item = pack; item != NULL; item = next(item)) + if ((struct object *) ldata(item) == obj) + return c; + else + c++; + return 'z'; +} diff --git a/passages.c b/passages.c new file mode 100644 index 0000000..87314a7 --- /dev/null +++ b/passages.c @@ -0,0 +1,287 @@ +/* + * Draw the connecting passages + * + * @(#)passages.c 3.4 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" + +/* + * do_passages: + * Draw all the passages on a level. + */ + +do_passages() +{ + register struct rdes *r1, *r2; + register int i, j; + register int roomcount; + static struct rdes + { + bool conn[MAXROOMS]; /* possible to connect to room i? */ + bool isconn[MAXROOMS]; /* connection been made to room i? */ + bool ingraph; /* this room in graph already? */ + } rdes[MAXROOMS] = { + { { 0, 1, 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 1, 0, 1, 0, 1, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 1, 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 1, 0, 0, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 1, 0, 1, 0, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 0, 1, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 0, 0, 1, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 0, 0, 0, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + }; + + /* + * reinitialize room graph description + */ + for (r1 = rdes; r1 <= &rdes[MAXROOMS-1]; r1++) + { + for (j = 0; j < MAXROOMS; j++) + r1->isconn[j] = FALSE; + r1->ingraph = FALSE; + } + + /* + * starting with one room, connect it to a random adjacent room and + * then pick a new room to start with. + */ + roomcount = 1; + r1 = &rdes[rnd(MAXROOMS)]; + r1->ingraph = TRUE; + do + { + /* + * find a room to connect with + */ + j = 0; + for (i = 0; i < MAXROOMS; i++) + if (r1->conn[i] && !rdes[i].ingraph && rnd(++j) == 0) + r2 = &rdes[i]; + /* + * if no adjacent rooms are outside the graph, pick a new room + * to look from + */ + if (j == 0) + { + do + r1 = &rdes[rnd(MAXROOMS)]; + until (r1->ingraph); + } + /* + * otherwise, connect new room to the graph, and draw a tunnel + * to it + */ + else + { + r2->ingraph = TRUE; + i = r1 - rdes; + j = r2 - rdes; + conn(i, j); + r1->isconn[j] = TRUE; + r2->isconn[i] = TRUE; + roomcount++; + } + } while (roomcount < MAXROOMS); + + /* + * attempt to add passages to the graph a random number of times so + * that there isn't just one unique passage through it. + */ + for (roomcount = rnd(5); roomcount > 0; roomcount--) + { + r1 = &rdes[rnd(MAXROOMS)]; /* a random room to look from */ + /* + * find an adjacent room not already connected + */ + j = 0; + for (i = 0; i < MAXROOMS; i++) + if (r1->conn[i] && !r1->isconn[i] && rnd(++j) == 0) + r2 = &rdes[i]; + /* + * if there is one, connect it and look for the next added + * passage + */ + if (j != 0) + { + i = r1 - rdes; + j = r2 - rdes; + conn(i, j); + r1->isconn[j] = TRUE; + r2->isconn[i] = TRUE; + } + } +} + +/* + * conn: + * Draw a corridor from a room in a certain direction. + */ + +conn(r1, r2) +int r1, r2; +{ + register struct room *rpf, *rpt; + register char rmt; + register int distance, turn_spot, turn_distance; + register int rm; + register char direc; + coord delta, curr, turn_delta, spos, epos; + + if (r1 < r2) + { + rm = r1; + if (r1 + 1 == r2) + direc = 'r'; + else + direc = 'd'; + } + else + { + rm = r2; + if (r2 + 1 == r1) + direc = 'r'; + else + direc = 'd'; + } + rpf = &rooms[rm]; + /* + * Set up the movement variables, in two cases: + * first drawing one down. + */ + if (direc == 'd') + { + rmt = rm + 3; /* room # of dest */ + rpt = &rooms[rmt]; /* room pointer of dest */ + delta.x = 0; /* direction of move */ + delta.y = 1; + spos.x = rpf->r_pos.x; /* start of move */ + spos.y = rpf->r_pos.y; + epos.x = rpt->r_pos.x; /* end of move */ + epos.y = rpt->r_pos.y; + if (!(rpf->r_flags & ISGONE)) /* if not gone pick door pos */ + { + spos.x += rnd(rpf->r_max.x-2)+1; + spos.y += rpf->r_max.y-1; + } + if (!(rpt->r_flags & ISGONE)) + epos.x += rnd(rpt->r_max.x-2)+1; + distance = abs(spos.y - epos.y) - 1; /* distance to move */ + turn_delta.y = 0; /* direction to turn */ + turn_delta.x = (spos.x < epos.x ? 1 : -1); + turn_distance = abs(spos.x - epos.x); /* how far to turn */ + turn_spot = rnd(distance-1) + 1; /* where turn starts */ + } + else if (direc == 'r') /* setup for moving right */ + { + rmt = rm + 1; + rpt = &rooms[rmt]; + delta.x = 1; + delta.y = 0; + spos.x = rpf->r_pos.x; + spos.y = rpf->r_pos.y; + epos.x = rpt->r_pos.x; + epos.y = rpt->r_pos.y; + if (!(rpf->r_flags & ISGONE)) + { + spos.x += rpf->r_max.x-1; + spos.y += rnd(rpf->r_max.y-2)+1; + } + if (!(rpt->r_flags & ISGONE)) + epos.y += rnd(rpt->r_max.y-2)+1; + distance = abs(spos.x - epos.x) - 1; + turn_delta.y = (spos.y < epos.y ? 1 : -1); + turn_delta.x = 0; + turn_distance = abs(spos.y - epos.y); + turn_spot = rnd(distance-1) + 1; + } + else + fatal("error in connection tables"); + /* + * Draw in the doors on either side of the passage or just put #'s + * if the rooms are gone. + */ + if (!(rpf->r_flags & ISGONE)) door(rpf, &spos); + else + { + cmov(spos); + addch('#'); + } + if (!(rpt->r_flags & ISGONE)) door(rpt, &epos); + else + { + cmov(epos); + addch('#'); + } + /* + * Get ready to move... + */ + curr.x = spos.x; + curr.y = spos.y; + while(distance) + { + /* + * Move to new position + */ + curr.x += delta.x; + curr.y += delta.y; + /* + * Check if we are at the turn place, if so do the turn + */ + if (distance == turn_spot && turn_distance > 0) + while(turn_distance--) + { + cmov(curr); + addch(PASSAGE); + curr.x += turn_delta.x; + curr.y += turn_delta.y; + } + /* + * Continue digging along + */ + cmov(curr); + addch(PASSAGE); + distance--; + } + curr.x += delta.x; + curr.y += delta.y; + if (!ce(curr, epos)) + msg("Warning, connectivity problem on this level."); +} + +/* + * Add a door or possibly a secret door + * also enters the door in the exits array of the room. + */ + +door(rm, cp) +register struct room *rm; +register coord *cp; +{ + cmov(*cp); + addch( (rnd(10) < level - 1 && rnd(100) < 20 ? SECRETDOOR : DOOR) ); + rm->r_exit[rm->r_nexits++] = *cp; +} + +/* + * add_pass: + * add the passages to the current window (wizard command) + */ + +add_pass() +{ + register int y, x, ch; + + for (y = 1; y < LINES - 2; y++) + for (x = 0; x < COLS; x++) + if ((ch=mvinch(y, x)) == PASSAGE || ch == DOOR || ch == SECRETDOOR) + mvwaddch(cw, y, x, ch); +} diff --git a/potions.c b/potions.c new file mode 100644 index 0000000..8a551f3 --- /dev/null +++ b/potions.c @@ -0,0 +1,211 @@ +/* + * @(#)potions.c 3.1 3.1 5/7/81 + * Function(s) for dealing with potions + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +quaff() +{ + register struct object *obj; + register struct linked_list *item, *titem; + register struct thing *th; + char buf[80]; + + item = get_item("quaff", POTION); + /* + * Make certain that it is somethings that we want to drink + */ + if (item == NULL) + return; + obj = (struct object *) ldata(item); + if (obj->o_type != POTION) + { + if (!terse) + msg("Yuk! Why would you want to drink that?"); + else + msg("That's undrinkable"); + return; + } + if (obj == cur_weapon) + cur_weapon = NULL; + + /* + * Calculate the effect it has on the poor guy. + */ + switch(obj->o_which) + { + case P_CONFUSE: + if (off(player, ISHUH)) + msg("Wait, what's going on here. Huh? What? Who?"); + + if (on(player, ISHUH)) + lengthen(unconfuse, rnd(8)+HUHDURATION); + else + fuse(unconfuse, 0, rnd(8)+HUHDURATION, AFTER); + + player.t_flags |= ISHUH; + p_know[P_CONFUSE] = TRUE; + when P_POISON: + if (!ISWEARING(R_SUSTSTR)) + { + chg_str(-(rnd(3)+1)); + msg("You feel very sick now."); + } + else + msg("You feel momentarily sick"); + p_know[P_POISON] = TRUE; + when P_HEALING: + if ((pstats.s_hpt += roll(pstats.s_lvl, 4)) > max_hp) + pstats.s_hpt = ++max_hp; + msg("You begin to feel better."); + sight(); + p_know[P_HEALING] = TRUE; + when P_STRENGTH: + msg("You feel stronger, now. What bulging muscles!"); + chg_str(1); + p_know[P_STRENGTH] = TRUE; + when P_MFIND: + /* + * Potion of monster detection, if there are monters, detect them + */ + if (mlist != NULL) + { + wclear(hw); + overwrite(mw, hw); + show_win(hw, + "You begin to sense the presence of monsters.--More--"); + p_know[P_MFIND] = TRUE; + } + else + msg("You have a strange feeling for a moment, then it passes."); + when P_TFIND: + /* + * Potion of magic detection. Show the potions and scrolls + */ + if (lvl_obj != NULL) + { + struct linked_list *mobj; + struct object *tp; + bool show; + + show = FALSE; + wclear(hw); + for (mobj = lvl_obj; mobj != NULL; mobj = next(mobj)) + { + tp = (struct object *) ldata(mobj); + if (is_magic(tp)) + { + show = TRUE; + mvwaddch(hw, tp->o_pos.y, tp->o_pos.x, MAGIC); + } + p_know[P_TFIND] = TRUE; + } + for (titem = mlist; titem != NULL; titem = next(titem)) + { + register struct linked_list *pitem; + + th = (struct thing *) ldata(titem); + for (pitem = th->t_pack; pitem != NULL; pitem = next(pitem)) + { + if (is_magic(ldata(pitem))) + { + show = TRUE; + mvwaddch(hw, th->t_pos.y, th->t_pos.x, MAGIC); + } + p_know[P_TFIND] = TRUE; + } + } + if (show) + { + show_win(hw, + "You sense the presence of magic on this level.--More--"); + break; + } + } + msg("You have a strange feeling for a moment, then it passes."); + when P_PARALYZE: + msg("You can't move."); + no_command = HOLDTIME; + p_know[P_PARALYZE] = TRUE; + when P_SEEINVIS: + msg("This potion tastes like %s juice.", fruit); + if (off(player, CANSEE)) + { + player.t_flags |= CANSEE; + fuse(unsee, 0, SEEDURATION, AFTER); + light(&hero); + } + sight(); + when P_RAISE: + msg("You suddenly feel much more skillful"); + p_know[P_RAISE] = TRUE; + raise_level(); + when P_XHEAL: + if ((pstats.s_hpt += roll(pstats.s_lvl, 8)) > max_hp) + pstats.s_hpt = ++max_hp; + msg("You begin to feel much better."); + p_know[P_XHEAL] = TRUE; + sight(); + when P_HASTE: + add_haste(TRUE); + msg("You feel yourself moving much faster."); + p_know[P_HASTE] = TRUE; + when P_RESTORE: + msg("Hey, this tastes great. It make you feel warm all over."); + if (pstats.s_str.st_str < max_stats.s_str.st_str || + (pstats.s_str.st_str == 18 && + pstats.s_str.st_add < max_stats.s_str.st_add)) + pstats.s_str = max_stats.s_str; + when P_BLIND: + msg("A cloak of darkness falls around you."); + if (off(player, ISBLIND)) + { + player.t_flags |= ISBLIND; + fuse(sight, 0, SEEDURATION, AFTER); + look(FALSE); + } + p_know[P_BLIND] = TRUE; + when P_NOP: + msg("This potion tastes extremely dull."); + otherwise: + msg("What an odd tasting potion!"); + return; + } + status(); + if (p_know[obj->o_which] && p_guess[obj->o_which]) + { + free(p_guess[obj->o_which]); + p_guess[obj->o_which] = NULL; + } + else if (!p_know[obj->o_which] && askme && p_guess[obj->o_which] == NULL) + { + msg(terse ? "Call it: " : "What do you want to call it? "); + if (get_str(buf, cw) == NORM) + { + p_guess[obj->o_which] = malloc((unsigned int) strlen(buf) + 1); + if (p_guess[obj->o_which] != NULL) + strcpy(p_guess[obj->o_which], buf); + } + } + /* + * Throw the item away + */ + inpack--; + if (obj->o_count > 1) + obj->o_count--; + else + { + detach(pack, item); + discard(item); + } +} diff --git a/readme36.html b/readme36.html new file mode 100644 index 0000000..134e826 --- /dev/null +++ b/readme36.html @@ -0,0 +1,78 @@ + + +ROGUE + + + +

ROGUE 3.6

+ +
+ +

NAME

+
+

rogue36 − Exploring The Dungeons of Doom

+
+ +

SYNOPSIS

+
+

rogue36 [ save_file ]

+
+ +

DESCRIPTION

+
+ +

Rogue is a computer fantasy game with a new twist. It is crt oriented and the object of the game is to survive the attacks of various + monsters and get a lot of gold, rather than the puzzle solving orientation + of most computer fantasy games.

+ +

To get started you really only need to know two commands. + The command ? will give you a list of the available commands and the command / + will identify the things you see on the screen.

+ +

To win the game (as opposed to merely playing to beat + other people high scores) you must locate the Amulet of Yendor which is + somewhere below the 20th level of the dungeon and get it out. Nobody has + achieved this yet and if somebody does, they will probably go down in history + as a hero among heros.

+ +

When the game ends, either by your death, when you quit, + or if you (by some miracle) manage to win, rogue will give you a list of the + top-ten scorers. The scoring is based entirely upon how much gold you get. + There is a 10% penalty for getting yourself killed.

+ +

For more detailed directions, read the document A Guide + to the Dungeons of Doom.

+ +
+ +

FILES

+
+ + + + + + + + + + + +
rogue36.scr Score file
rogue36.sav Default save file
+
+ +

SEE ALSO

+

Michael C. Toy, A Guide to the Dungeons of Doom

+ +

BUGS

+

Probably infinite. Currently known bugs are: Sometimes you are still +hungry even after you eat food and sometimes you get a monster on the +screen in reverse video which may or may not cause a core dump.

+ +

COPYRIGHT

+Rogue: Exploring the Dungeons of Doom
+Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
+All rights reserved. + + + \ No newline at end of file diff --git a/rings.c b/rings.c new file mode 100644 index 0000000..1344ca1 --- /dev/null +++ b/rings.c @@ -0,0 +1,207 @@ +/* + * routines dealing specifically with rings + * + * @(#)rings.c 3.17 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +ring_on() +{ + register struct object *obj; + register struct linked_list *item; + register int ring; + str_t save_max; + char buf[80]; + + item = get_item("put on", RING); + /* + * Make certain that it is somethings that we want to wear + */ + if (item == NULL) + return; + obj = (struct object *) ldata(item); + if (obj->o_type != RING) + { + if (!terse) + msg("It would be difficult to wrap that around a finger"); + else + msg("Not a ring"); + return; + } + + /* + * find out which hand to put it on + */ + if (is_current(obj)) + return; + + if (cur_ring[LEFT] == NULL && cur_ring[RIGHT] == NULL) + { + if ((ring = gethand()) < 0) + return; + } + else if (cur_ring[LEFT] == NULL) + ring = LEFT; + else if (cur_ring[RIGHT] == NULL) + ring = RIGHT; + else + { + if (!terse) + msg("You already have a ring on each hand"); + else + msg("Wearing two"); + return; + } + cur_ring[ring] = obj; + + /* + * Calculate the effect it has on the poor guy. + */ + switch (obj->o_which) + { + case R_ADDSTR: + save_max = max_stats.s_str; + chg_str(obj->o_ac); + max_stats.s_str = save_max; + break; + case R_SEEINVIS: + player.t_flags |= CANSEE; + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + break; + case R_AGGR: + aggravate(); + break; + } + status(); + if (r_know[obj->o_which] && r_guess[obj->o_which]) + { + free(r_guess[obj->o_which]); + r_guess[obj->o_which] = NULL; + } + else if (!r_know[obj->o_which] && askme && r_guess[obj->o_which] == NULL) + { + mpos = 0; + msg(terse ? "Call it: " : "What do you want to call it? "); + if (get_str(buf, cw) == NORM) + { + r_guess[obj->o_which] = malloc((unsigned int) strlen(buf) + 1); + if (r_guess[obj->o_which] != NULL) + strcpy(r_guess[obj->o_which], buf); + } + msg(""); + } +} + +ring_off() +{ + register int ring; + register struct object *obj; + + if (cur_ring[LEFT] == NULL && cur_ring[RIGHT] == NULL) + { + if (terse) + msg("No rings"); + else + msg("You aren't wearing any rings"); + return; + } + else if (cur_ring[LEFT] == NULL) + ring = RIGHT; + else if (cur_ring[RIGHT] == NULL) + ring = LEFT; + else + if ((ring = gethand()) < 0) + return; + mpos = 0; + obj = cur_ring[ring]; + if (obj == NULL) + { + msg("Not wearing such a ring"); + return; + } + if (dropcheck(obj)) + msg("Was wearing %s", inv_name(obj, TRUE)); +} + +gethand() +{ + register int c; + + for (;;) + { + if (terse) + msg("Left or Right ring? "); + else + msg("Left hand or right hand? "); + if ((c = readchar(cw)) == 'l' || c == 'L') + return LEFT; + else if (c == 'r' || c == 'R') + return RIGHT; + else if (c == ESCAPE) + return -1; + mpos = 0; + if (terse) + msg("L or R"); + else + msg("Please type L or R"); + } +} + +/* + * how much food does this ring use up? + */ +ring_eat(hand) +register int hand; +{ + if (cur_ring[hand] == NULL) + return 0; + switch (cur_ring[hand]->o_which) + { + case R_REGEN: + return 2; + case R_SUSTSTR: + return 1; + case R_SEARCH: + return (rnd(100) < 33); + case R_DIGEST: + return -(rnd(100) < 50); + default: + return 0; + } +} + +/* + * print ring bonuses + */ +char * +ring_num(obj) +register struct object *obj; +{ + static char buf[5]; + + if (!(obj->o_flags & ISKNOW)) + return ""; + switch (obj->o_which) + { + case R_PROTECT: + case R_ADDSTR: + case R_ADDDAM: + case R_ADDHIT: + buf[0] = ' '; + strcpy(&buf[1], num(obj->o_ac, 0)); + otherwise: + return ""; + } + return buf; +} diff --git a/rip.c b/rip.c new file mode 100644 index 0000000..809ba6a --- /dev/null +++ b/rip.c @@ -0,0 +1,376 @@ +/* + * File for the fun ends + * Death or a total win + * + * @(#)rip.c 3.13 (Berkeley) 6/16/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include +#include +#include +#include +#include "machdep.h" +#include "rogue.h" + +static char *rip[] = { +" __________", +" / \\", +" / REST \\", +" / IN \\", +" / PEACE \\", +" / \\", +" | |", +" | |", +" | killed by a |", +" | |", +" | 1980 |", +" *| * * * | *", +" ________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______", + 0 +}; + +char *killname(); + +/* + * death: + * Do something really fun when he dies + */ + +death(monst) +register char monst; +{ + register char **dp = rip, *killer; + register struct tm *lt; + time_t date; + char buf[80]; + + time(&date); + lt = localtime(&date); + clear(); + move(8, 0); + while (*dp) + printw("%s\n", *dp++); + mvaddstr(14, 28-(((int)strlen(whoami)+1)/2), whoami); + purse -= purse/10; + sprintf(buf, "%d Au", purse); + mvaddstr(15, 28-(((int)strlen(buf)+1)/2), buf); + killer = killname(monst); + mvaddstr(17, 28-(((int)strlen(killer)+1)/2), killer); + mvaddstr(16, 33, vowelstr(killer)); + sprintf(prbuf, "%4d", 1900+lt->tm_year); + mvaddstr(18, 26, prbuf); + move(LINES-1, 0); + draw(stdscr); + score(purse, 0, monst); + exit(0); +} + +/* + * score -- figure score and post it. + */ + +/* VARARGS2 */ +score(amount, flags, monst) +char monst; +{ + static struct sc_ent { + int sc_score; + char sc_name[80]; + int sc_flags; + int sc_level; + char sc_login[8]; + char sc_monster; + } top_ten[10]; + register struct sc_ent *scp; + register int i; + register struct sc_ent *sc2; + register FILE *outf; + register char *killer; + register int prflags = 0; + register int fd; + static char *reason[] = { + "killed", + "quit", + "A total winner", + }; + char scoreline[100]; + char score_file[PATH_MAX]; + int rogue_ver = 0, scorefile_ver = 0; + + /* + * Open file and read list + */ + + /* Get default score file */ + strcpy(score_file, md_getroguedir()); + + if (*score_file) + strcat(score_file,"\\"); + + strcat(score_file, "rogue36.scr"); + + if ((fd = open(score_file, O_RDWR | O_CREAT, 0666 )) < 0) + return; + + outf = (FILE *) fdopen(fd, "w"); + + for (scp = top_ten; scp <= &top_ten[9]; scp++) + { + scp->sc_score = 0; + for (i = 0; i < 80; i++) + scp->sc_name[i] = rnd(255); + scp->sc_flags = RN; + scp->sc_level = RN; + scp->sc_monster = RN; + scp->sc_login[0] = '\0'; + } + + signal(SIGINT, SIG_DFL); + if ((flags != -1) && (flags != 1)) + { + mvaddstr(LINES-1, 0, "[Press return to continue]"); + draw(stdscr); + prbuf[0] = 0; + get_str(prbuf, stdscr); + endwin(); + } + if (wizard) + if (strcmp(prbuf, "names") == 0) + prflags = 1; + else if (strcmp(prbuf, "edit") == 0) + prflags = 2; + + encread((char *) scoreline, 100, fd); + sscanf(scoreline, "R%d %d\n", &rogue_ver, &scorefile_ver); + + if ((rogue_ver == 36) && (scorefile_ver == 2)) + for(i = 0; i < 10; i++) + { + encread((char *) &top_ten[i].sc_name, 80, fd); + encread((char *) &top_ten[i].sc_login, 8, fd); + encread((char *) scoreline, 100, fd); + sscanf(scoreline, " %d %d %d %d \n", + &top_ten[i].sc_score, &top_ten[i].sc_flags, + &top_ten[i].sc_level, &top_ten[i].sc_monster); + } + + /* + * Insert her in list if need be + */ + if (!waswizard) + { + for (scp = top_ten; scp <= &top_ten[9]; scp++) + if (amount > scp->sc_score) + break; + if (scp <= &top_ten[9]) + { + for (sc2 = &top_ten[9]; sc2 > scp; sc2--) + *sc2 = *(sc2-1); + scp->sc_score = amount; + strcpy(scp->sc_name, whoami); + scp->sc_flags = flags; + if (flags == 2) + scp->sc_level = max_level; + else + scp->sc_level = level; + scp->sc_monster = monst; + strncpy(scp->sc_login, md_getusername(), 8); + } + } + /* + * Print the list + */ + if (flags != -1) + printf("\n\n\n"); + printf("Top Ten Adventurers:\nRank\tScore\tName\n"); + for (scp = top_ten; scp <= &top_ten[9]; scp++) { + if (scp->sc_score) { + printf("%d\t%d\t%s: %s on level %d", scp - top_ten + 1, + scp->sc_score, scp->sc_name, reason[scp->sc_flags], + scp->sc_level); + if (scp->sc_flags == 0) { + printf(" by a"); + killer = killname(scp->sc_monster); + if (*killer == 'a' || *killer == 'e' || *killer == 'i' || + *killer == 'o' || *killer == 'u') + putchar('n'); + printf(" %s", killer); + } + if (prflags == 1) + { + printf(" (%s)", scp->sc_login); + putchar('\n'); + } + else if (prflags == 2) + { + fflush(stdout); + fgets(prbuf,80,stdin); + if (prbuf[0] == 'd') + { + for (sc2 = scp; sc2 < &top_ten[9]; sc2++) + *sc2 = *(sc2 + 1); + top_ten[9].sc_score = 0; + for (i = 0; i < 80; i++) + top_ten[9].sc_name[i] = rnd(255); + top_ten[9].sc_flags = RN; + top_ten[9].sc_level = RN; + top_ten[9].sc_monster = RN; + scp--; + } + } + else + printf(".\n"); + } + } + fseek(outf, 0L, 0); + /* + * Update the list file + */ + strcpy(scoreline, "R36 2\n"); + encwrite(scoreline, 100, outf); + for(i = 0; i < 10; i++) + { + encwrite((char *) &top_ten[i].sc_name, 80, outf); + encwrite((char *) &top_ten[i].sc_login, 8, outf); + sprintf(scoreline, " %d %d %d %d \n", + top_ten[i].sc_score, top_ten[i].sc_flags, + top_ten[i].sc_level, top_ten[i].sc_monster); + encwrite((char *) scoreline, 100, outf); + } + fclose(outf); +} + +total_winner() +{ + register struct linked_list *item; + register struct object *obj; + register int worth; + register char c; + register int oldpurse; + + clear(); + standout(); + addstr(" \n"); + addstr(" @ @ @ @ @ @@@ @ @ \n"); + addstr(" @ @ @@ @@ @ @ @ @ \n"); + addstr(" @ @ @@@ @ @ @ @ @ @@@ @@@@ @@@ @ @@@ @ \n"); + addstr(" @@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ \n"); + addstr(" @ @ @ @ @ @ @ @@@@ @ @ @@@@@ @ @ @ \n"); + addstr(" @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ \n"); + addstr(" @@@ @@@ @@ @ @ @ @@@@ @@@@ @@@ @@@ @@ @ \n"); + addstr(" \n"); + addstr(" Congratulations, you have made it to the light of day! \n"); + standend(); + addstr("\nYou have joined the elite ranks of those who have escaped the\n"); + addstr("Dungeons of Doom alive. You journey home and sell all your loot at\n"); + addstr("a great profit and are admitted to the fighters guild.\n"); + mvaddstr(LINES - 1, 0, "--Press space to continue--"); + refresh(); + wait_for(' '); + clear(); + mvaddstr(0, 0, " Worth Item"); + oldpurse = purse; + for (c = 'a', item = pack; item != NULL; c++, item = next(item)) + { + obj = (struct object *) ldata(item); + switch (obj->o_type) + { + case FOOD: + worth = 2 * obj->o_count; + when WEAPON: + switch (obj->o_which) + { + case MACE: worth = 8; + when SWORD: worth = 15; + when BOW: worth = 75; + when ARROW: worth = 1; + when DAGGER: worth = 2; + when ROCK: worth = 1; + when TWOSWORD: worth = 30; + when SLING: worth = 1; + when DART: worth = 1; + when CROSSBOW: worth = 15; + when BOLT: worth = 1; + when SPEAR: worth = 2; + otherwise: worth = 0; + } + worth *= (1 + (10 * obj->o_hplus + 10 * obj->o_dplus)); + worth *= obj->o_count; + obj->o_flags |= ISKNOW; + when ARMOR: + switch (obj->o_which) + { + case LEATHER: worth = 5; + when RING_MAIL: worth = 30; + when STUDDED_LEATHER: worth = 15; + when SCALE_MAIL: worth = 3; + when CHAIN_MAIL: worth = 75; + when SPLINT_MAIL: worth = 80; + when BANDED_MAIL: worth = 90; + when PLATE_MAIL: worth = 400; + otherwise: worth = 0; + } + worth *= (1 + (10 * (a_class[obj->o_which] - obj->o_ac))); + obj->o_flags |= ISKNOW; + when SCROLL: + s_know[obj->o_which] = TRUE; + worth = s_magic[obj->o_which].mi_worth; + worth *= obj->o_count; + when POTION: + p_know[obj->o_which] = TRUE; + worth = p_magic[obj->o_which].mi_worth; + worth *= obj->o_count; + when RING: + obj->o_flags |= ISKNOW; + r_know[obj->o_which] = TRUE; + worth = r_magic[obj->o_which].mi_worth; + if (obj->o_which == R_ADDSTR || obj->o_which == R_ADDDAM || + obj->o_which == R_PROTECT || obj->o_which == R_ADDHIT) + if (obj->o_ac > 0) + worth += obj->o_ac * 20; + else + worth = 50; + when STICK: + obj->o_flags |= ISKNOW; + ws_know[obj->o_which] = TRUE; + worth = ws_magic[obj->o_which].mi_worth; + worth += 20 * obj->o_charges; + when AMULET: + worth = 1000; + } + mvprintw(c - 'a' + 1, 0, "%c) %5d %s", c, worth, inv_name(obj, FALSE)); + purse += worth; + } + mvprintw(c - 'a' + 1, 0," %5d Gold Peices ", oldpurse); + refresh(); + score(purse, 2, 0); + exit(0); +} + +char * +killname(monst) +register char monst; +{ + if (isupper(monst)) + return monsters[monst-'A'].m_name; + else + switch (monst) + { + case 'a': + return "arrow"; + case 'd': + return "dart"; + case 'b': + return "bolt"; + } + return(""); +} diff --git a/rogue.6 b/rogue.6 new file mode 100644 index 0000000..2058b9f --- /dev/null +++ b/rogue.6 @@ -0,0 +1,58 @@ +.TH ROGUE 6 +.UC +.SH NAME +rogue \- Exploring The Dungeons of Doom +.SH SYNOPSIS +.B rogue +[ +.I save_file +] +.SH DESCRIPTION +.PP +.I Rogue +is a computer fantasy game with a new twist. It is crt oriented and the +object of the game is to survive the attacks of various monsters and get +a lot of gold, rather than the puzzle solving orientation of most computer +fantasy games. +.PP +To get started you really only need to know two commands. The command +.B ? +will give you a list of the available commands and the command +.B / +will identify the things you see on the screen. +.PP +To win the game (as opposed to merely playing to beat other people high +scores) you must locate the Amulet of Yendor which is somewhere below +the 20th level of the dungeon and get it out. Nobody has achieved this +yet and if somebody does, they will probably go down in history as a hero +among heros. +.PP +When the game ends, either by your death, when you quit, or if you (by +some miracle) manage to win, +.I rogue +will give you alist of the top-ten scorers. The scoring is based entirely +upon how much gold you get. There is a 10% penalty for getting yourself +killed. +.PP +For more detailed directions, read the document +.I "A Guide to the Dungeons of Doom." +.SH FILES +.ta 2i +rogue36.scr Score file +.br +~/rogue36.sav Default save file +.DT +.SH SEE ALSO +Michael C. Toy, +.I "A Guide to the Dungeons of Doom" +.SH BUGS +.PP +Probably infinite. Currently known bugs are: Sometimes you are still hungry +even after you eat food and sometimes you get a monster on the screen in +reverse video which may or may not cause a core dump. +.SH COPYRIGHT +Rogue: Exploring the Dungeons of Doom +.br +Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman +.br +All rights reserved. diff --git a/rogue.h b/rogue.h new file mode 100644 index 0000000..6ca801a --- /dev/null +++ b/rogue.h @@ -0,0 +1,527 @@ +/* + * Rogue definitions and variable declarations + * + * @(#)rogue.h 3.38 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +/* + * Maximum number of different things + */ +#define MAXROOMS 9 +#define MAXTHINGS 9 +#define MAXOBJ 9 +#define MAXPACK 23 +#define MAXTRAPS 10 +#define NUMTHINGS 7 /* number of types of things (scrolls, rings, etc.) */ + +/* + * return values for get functions + */ +#define NORM 0 /* normal exit */ +#define QUIT 1 /* quit option setting */ +#define MINUS 2 /* back up one option */ + +/* + * All the fun defines + */ +#define next(ptr) (*ptr).l_next +#define prev(ptr) (*ptr).l_prev +#define ldata(ptr) (*ptr).l_data +#define inroom(rp, cp) (\ + (cp)->x <= (rp)->r_pos.x + ((rp)->r_max.x - 1) && (rp)->r_pos.x <= (cp)->x \ + && (cp)->y <= (rp)->r_pos.y + ((rp)->r_max.y - 1) && (rp)->r_pos.y <= (cp)->y) +#define winat(y, x) (mvwinch(mw,y,x)==' '?mvwinch(stdscr,y,x):winch(mw)) +#define debug if (wizard) msg +#define RN (((seed = seed*11109+13849) & 0x7fff) >> 1) +#define unc(cp) (cp).y, (cp).x +#define cmov(xy) move((xy).y, (xy).x) +#define DISTANCE(y1, x1, y2, x2) ((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)) +#define OBJPTR(what) (struct object *)((*what).l_data) +#define THINGPTR(what) (struct thing *)((*what).l_data) +#define when break;case +#define otherwise break;default +#define until(expr) while(!(expr)) +#define ce(a, b) ((a).x == (b).x && (a).y == (b).y) +#define draw(window) wrefresh(window) +#define hero player.t_pos +#define pstats player.t_stats +#define pack player.t_pack +#define attach(a,b) _attach(&a,b) +#define detach(a,b) _detach(&a,b) +#define free_list(a) _free_list(&a) +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif +#define on(thing, flag) (((thing).t_flags & flag) != 0) +#define off(thing, flag) (((thing).t_flags & flag) == 0) +#undef CTRL +#define CTRL(ch) (ch & 037) +#define ALLOC(x) malloc((unsigned int) x) +#define FREE(x) free((char *) x) +#define EQSTR(a, b, c) (strncmp(a, b, c) == 0) +#define GOLDCALC (rnd(50 + 10 * level) + 2) +#define ISRING(h,r) (cur_ring[h] != NULL && cur_ring[h]->o_which == r) +#define ISWEARING(r) (ISRING(LEFT, r) || ISRING(RIGHT, r)) +#define newgrp() ++group +#define o_charges o_ac +#define ISMULT(type) (type == POTION || type == SCROLL || type == FOOD) + +/* + * Things that appear on the screens + */ +#define PASSAGE '#' +#define DOOR '+' +#define FLOOR '.' +#define PLAYER '@' +#define TRAP '^' +#define TRAPDOOR '>' +#define ARROWTRAP '{' +#define SLEEPTRAP '$' +#define BEARTRAP '}' +#define TELTRAP '~' +#define DARTTRAP '`' +#define SECRETDOOR '&' +#define STAIRS '%' +#define GOLD '*' +#define POTION '!' +#define SCROLL '?' +#define MAGIC '$' +#define FOOD ':' +#define WEAPON ')' +#define ARMOR ']' +#define AMULET ',' +#define RING '=' +#define STICK '/' +#define CALLABLE -1 + +/* + * Various constants + */ +#define PASSWD "mTBellIQOsLNA" +#define BEARTIME 3 +#define SLEEPTIME 5 +#define HEALTIME 30 +#define HOLDTIME 2 +#define STPOS 0 +#define WANDERTIME 70 +#define BEFORE 1 +#define AFTER 2 +#define HUHDURATION 20 +#define SEEDURATION 850 +#define HUNGERTIME 1300 +#define MORETIME 150 +#define STOMACHSIZE 2000 +#define ESCAPE 27 +#define LEFT 0 +#define RIGHT 1 +#define BOLT_LENGTH 6 + +/* + * Save against things + */ +#define VS_POISON 00 +#define VS_PARALYZATION 00 +#define VS_DEATH 00 +#define VS_PETRIFICATION 01 +#define VS_BREATH 02 +#define VS_MAGIC 03 + +/* + * Various flag bits + */ +#define ISDARK 0000001 +#define ISCURSED 000001 +#define ISBLIND 0000001 +#define ISGONE 0000002 +#define ISKNOW 0000002 +#define ISRUN 0000004 +#define ISFOUND 0000010 +#define ISINVIS 0000020 +#define ISMEAN 0000040 +#define ISGREED 0000100 +#define ISBLOCK 0000200 +#define ISHELD 0000400 +#define ISHUH 0001000 +#define ISREGEN 0002000 +#define CANHUH 0004000 +#define CANSEE 0010000 +#define ISMISL 0020000 +#define ISCANC 0020000 +#define ISMANY 0040000 +#define ISSLOW 0040000 +#define ISHASTE 0100000 + +/* + * Potion types + */ +#define P_CONFUSE 0 +#define P_PARALYZE 1 +#define P_POISON 2 +#define P_STRENGTH 3 +#define P_SEEINVIS 4 +#define P_HEALING 5 +#define P_MFIND 6 +#define P_TFIND 7 +#define P_RAISE 8 +#define P_XHEAL 9 +#define P_HASTE 10 +#define P_RESTORE 11 +#define P_BLIND 12 +#define P_NOP 13 +#define MAXPOTIONS 14 + +/* + * Scroll types + */ +#define S_CONFUSE 0 +#define S_MAP 1 +#define S_LIGHT 2 +#define S_HOLD 3 +#define S_SLEEP 4 +#define S_ARMOR 5 +#define S_IDENT 6 +#define S_SCARE 7 +#define S_GFIND 8 +#define S_TELEP 9 +#define S_ENCH 10 +#define S_CREATE 11 +#define S_REMOVE 12 +#define S_AGGR 13 +#define S_NOP 14 +#define S_GENOCIDE 15 +#define MAXSCROLLS 16 + +/* + * Weapon types + */ +#define MACE 0 +#define SWORD 1 +#define BOW 2 +#define ARROW 3 +#define DAGGER 4 +#define ROCK 5 +#define TWOSWORD 6 +#define SLING 7 +#define DART 8 +#define CROSSBOW 9 +#define BOLT 10 +#define SPEAR 11 +#define MAXWEAPONS 12 + +/* + * Armor types + */ +#define LEATHER 0 +#define RING_MAIL 1 +#define STUDDED_LEATHER 2 +#define SCALE_MAIL 3 +#define CHAIN_MAIL 4 +#define SPLINT_MAIL 5 +#define BANDED_MAIL 6 +#define PLATE_MAIL 7 +#define MAXARMORS 8 + +/* + * Ring types + */ +#define R_PROTECT 0 +#define R_ADDSTR 1 +#define R_SUSTSTR 2 +#define R_SEARCH 3 +#define R_SEEINVIS 4 +#define R_NOP 5 +#define R_AGGR 6 +#define R_ADDHIT 7 +#define R_ADDDAM 8 +#define R_REGEN 9 +#define R_DIGEST 10 +#define R_TELEPORT 11 +#define R_STEALTH 12 +#define MAXRINGS 13 + +/* + * Rod/Wand/Staff types + */ + +#define WS_LIGHT 0 +#define WS_HIT 1 +#define WS_ELECT 2 +#define WS_FIRE 3 +#define WS_COLD 4 +#define WS_POLYMORPH 5 +#define WS_MISSILE 6 +#define WS_HASTE_M 7 +#define WS_SLOW_M 8 +#define WS_DRAIN 9 +#define WS_NOP 10 +#define WS_TELAWAY 11 +#define WS_TELTO 12 +#define WS_CANCEL 13 +#define MAXSTICKS 14 + +/* + * Now we define the structures and types + */ + +/* + * Help list + */ + +struct h_list { + char h_ch; + char *h_desc; +}; + +extern struct h_list helpstr[]; + +/* + * Coordinate data type + */ +typedef struct { + int x; + int y; +} coord; + +typedef struct { + short st_str; + short st_add; +} str_t; + +/* + * Linked list data type + */ +struct linked_list { + struct linked_list *l_next; + struct linked_list *l_prev; + char *l_data; /* Various structure pointers */ +}; + +/* + * Stuff about magic items + */ + +struct magic_item { + char mi_name[30]; + int mi_prob; + int mi_worth; +}; + +/* + * Room structure + */ +struct room { + coord r_pos; /* Upper left corner */ + coord r_max; /* Size of room */ + coord r_gold; /* Where the gold is */ + int r_goldval; /* How much the gold is worth */ + int r_flags; /* Info about the room */ + int r_nexits; /* Number of exits */ + coord r_exit[4]; /* Where the exits are */ +}; + +/* + * Array of all traps on this level + */ +struct trap { + coord tr_pos; /* Where trap is */ + char tr_type; /* What kind of trap */ + int tr_flags; /* Info about trap (i.e. ISFOUND) */ +}; + +extern struct trap traps[MAXTRAPS]; + +/* + * Structure describing a fighting being + */ +struct stats { + str_t s_str; /* Strength */ + long s_exp; /* Experience */ + int s_lvl; /* Level of mastery */ + int s_arm; /* Armor class */ + int s_hpt; /* Hit points */ + char s_dmg[30]; /* String describing damage done */ +}; + +/* + * Structure for monsters and player + */ +struct thing { + coord t_pos; /* Position */ + bool t_turn; /* If slowed, is it a turn to move */ + char t_type; /* What it is */ + char t_disguise; /* What mimic looks like */ + char t_oldch; /* Character that was where it was */ + coord *t_dest; /* Where it is running to */ + short t_flags; /* State word */ + struct stats t_stats; /* Physical description */ + struct linked_list *t_pack; /* What the thing is carrying */ + int t_reserved; /* reserved for save/restore code */ +}; + +/* + * Array containing information on all the various types of mosnters + */ +struct monster { + char m_name[20]; /* What to call the monster */ + short m_carry; /* Probability of carrying something */ + short m_flags; /* Things about the monster */ + struct stats m_stats; /* Initial stats */ +}; + +/* + * Structure for a thing that the rogue can carry + */ + +struct object { + int o_type; /* What kind of object it is */ + coord o_pos; /* Where it lives on the screen */ + char o_launch; /* What you need to launch it */ + char o_damage[8]; /* Damage if used like sword */ + char o_hurldmg[8]; /* Damage if thrown */ + int o_count; /* Count for plural objects */ + int o_which; /* Which object of a type it is */ + int o_hplus; /* Plusses to hit */ + int o_dplus; /* Plusses to damage */ + int o_ac; /* Armor class */ + int o_flags; /* Information about objects */ + int o_group; /* Group number for this object */ +}; + +struct delayed_action { + int d_type; + int (*d_func)(); + int d_arg; + int d_time; +}; + +/* + * Now all the global variables + */ + +extern struct room rooms[MAXROOMS]; /* One for each room -- A level */ +extern struct room *oldrp; /* Roomin(&oldpos) */ +extern struct linked_list *mlist; /* List of monsters on the level */ +extern struct thing player; /* The rogue */ +extern struct stats max_stats; /* The maximum for the player */ +extern struct monster monsters[26]; /* The initial monster states */ +extern struct linked_list *lvl_obj; /* List of objects on this level */ +extern struct object *cur_weapon; /* Which weapon he is weilding */ +extern struct object *cur_armor; /* What a well dresssed rogue wears */ +extern struct object *cur_ring[2]; /* Which rings are being worn */ +extern struct magic_item things[NUMTHINGS]; /* Chances for each type of item */ +extern struct magic_item s_magic[MAXSCROLLS]; /* Names and chances for scrolls */ +extern struct magic_item p_magic[MAXPOTIONS]; /* Names and chances for potions */ +extern struct magic_item r_magic[MAXRINGS]; /* Names and chances for rings */ +extern struct magic_item ws_magic[MAXSTICKS]; /* Names and chances for sticks */ + +extern int level; /* What level rogue is on */ +extern int purse; /* How much gold the rogue has */ +extern int mpos; /* Where cursor is on top line */ +extern int ntraps; /* Number of traps on this level */ +extern int no_move; /* Number of turns held in place */ +extern int no_command; /* Number of turns asleep */ +extern int inpack; /* Number of things in pack */ +extern int max_hp; /* Player's max hit points */ +extern int total; /* Total dynamic memory bytes */ +extern int a_chances[MAXARMORS]; /* Probabilities for armor */ +extern int a_class[MAXARMORS]; /* Armor class for various armors */ +extern int lastscore; /* Score before this turn */ +extern int no_food; /* Number of levels without food */ +extern int seed; /* Random number seed */ +extern int count; /* Number of times to repeat command */ +extern int dnum; /* Dungeon number */ +extern int fung_hit; /* Number of time fungi has hit */ +extern int quiet; /* Number of quiet turns */ +extern int max_level; /* Deepest player has gone */ +extern int food_left; /* Amount of food in hero's stomach */ +extern int group; /* Current group number */ +extern int hungry_state; /* How hungry is he */ + +extern char take; /* Thing the rogue is taking */ +extern char prbuf[80]; /* Buffer for sprintfs */ +extern char runch; /* Direction player is running */ +extern char *s_names[MAXSCROLLS]; /* Names of the scrolls */ +extern char *p_colors[MAXPOTIONS]; /* Colors of the potions */ +extern char *r_stones[MAXRINGS]; /* Stone settings of the rings */ +extern char *w_names[MAXWEAPONS]; /* Names of the various weapons */ +extern char *a_names[MAXARMORS]; /* Names of armor types */ +extern char *ws_made[MAXSTICKS]; /* What sticks are made of */ +extern char *release; /* Release number of rogue */ +extern char whoami[80]; /* Name of player */ +extern char fruit[80]; /* Favorite fruit */ +extern char huh[80]; /* The last message printed */ +extern char *s_guess[MAXSCROLLS]; /* Players guess at what scroll is */ +extern char *p_guess[MAXPOTIONS]; /* Players guess at what potion is */ +extern char *r_guess[MAXRINGS]; /* Players guess at what ring is */ +extern char *ws_guess[MAXSTICKS]; /* Players guess at what wand is */ +extern char *ws_type[MAXSTICKS]; /* Is it a wand or a staff */ +extern char file_name[80]; /* Save file name */ +extern char home[80]; /* User's home directory */ + +extern WINDOW *cw; /* Window that the player sees */ +extern WINDOW *hw; /* Used for the help command */ +extern WINDOW *mw; /* Used to store mosnters */ + +extern bool running; /* True if player is running */ +extern bool playing; /* True until he quits */ +extern bool wizard; /* True if allows wizard commands */ +extern bool after; /* True if we want after daemons */ +extern bool notify; /* True if player wants to know */ +extern bool fight_flush; /* True if toilet input */ +extern bool terse; /* True if we should be short */ +extern bool door_stop; /* Stop running when we pass a door */ +extern bool jump; /* Show running as series of jumps */ +extern bool slow_invent; /* Inventory one line at a time */ +extern bool firstmove; /* First move after setting door_stop */ +extern bool waswizard; /* Was a wizard sometime */ +extern bool askme; /* Ask about unidentified things */ +extern bool s_know[MAXSCROLLS]; /* Does he know what a scroll does */ +extern bool p_know[MAXPOTIONS]; /* Does he know what a potion does */ +extern bool r_know[MAXRINGS]; /* Does he know what a ring does */ +extern bool ws_know[MAXSTICKS]; /* Does he know what a stick does */ +extern bool amulet; /* He found the amulet */ +extern bool in_shell; /* True if executing a shell */ + +extern coord oldpos; /* Position before last look() call */ +extern coord delta; /* Change indicated to get_dir() */ + +extern coord ch_ret; +extern char countch,direction,newcount; +extern struct delayed_action d_list[20]; +extern int between; +extern int num_checks; +extern char lvl_mons[27],wand_mons[27]; +extern coord nh; + +struct linked_list *find_mons(), *find_obj(), *get_item(), *new_item(); +struct linked_list *new_thing(), *wake_monster(); + +char *tr_name(), *new(); +char *charge_str(),*vowelstr(), *inv_name(); +char *ctime(), *num(), *ring_num(); + +struct room *roomin(); + +coord *rndmove(); + +void auto_save(int p), endit(int p), quit(int p), tstp(), checkout(); +int nohaste(), doctor(), runners(), swander(); +int unconfuse(), unsee(), rollwand(), stomach(), sight(); + +struct trap *trap_at(); + +extern char *rainbow[]; +extern char *stones[]; +extern char *wood[]; +extern char *metal[]; + +extern const int cNCOLORS; +extern const int cNSTONES; +extern const int cNWOOD; +extern const int cNMETAL; diff --git a/rogue.r b/rogue.r new file mode 100644 index 0000000..4642a25 --- /dev/null +++ b/rogue.r @@ -0,0 +1,411 @@ +.RP +.ds RH A Guide to the Dungeons of Doom +.ds CH +.ds CF - % - +.TL +A Guide to the Dungeons of Doom +.AU +Michael C. Toy +.AI +Computer Systems Research Group +Department of Electrical Engineering and Computer Science +University of California +Berkeley, California 94720 +.AB +Rogue is a visual CRT based fantasy game which runs +under the UNIX timesharing system. This paper describes +how to play rogue and gives a few hints for those who might +otherwise get lost in the Dungeons of Doom. +.AE +.NH +Introduction +.PP +You have just finished your years as a student at the local fighter's guild. +After much practice and sweat you have finally completed your training and +are ready to embark upon a perilous adventure. As a test of your skills, +the local guildmasters have sent you into the Dungeons of Doom. Your +task is to return with the Amulet of Yendor. Your reward for the completion +of this task will be a full membership in the local guild. In addition, you are +allowed to keep all the loot you bring back from the dungeons. +.PP +In preparation for your journey, you are given an enchanted sword, taken +from a dragon's hoard in the far off Dark Mountains. You are also outfitted +with elf-crafted armor and given enough food to reach the dungeons. You +say goodbye to family and friends for what may be the last time and head +up the road. +.PP +You set out on your way to the dungeons and after several days of uneventful +travel, you see the ancient ruins that mark the entrance to the Dungeons +of Doom. It is late at night so you make camp at the entrance and spend +the night sleeping under the open skies. In the morning you gather +your sword, put on your armor, eat what is almost your last food and enter +the dungeons. +.NH +What is going on here? +.PP +You have just begun a game of rogue. Your goal is to grab as much treasure +as you can, find the Amulet of Yendor, and get out of the Dungeons of Doom +alive. On the screen, a map of where you have been and what you have seen on +the current dungeon level is kept. As you explore more of the level, +it appears on the screen in front of you. +.PP +Rogue differs from most computer fantasy games in that it is screen +oriented. Commands are all one or two keystrokes\(dg +.FS +\(dgAs opposed to pseudo English sentences. +.FE +and the results of your commands are displayed +graphically on the screen rather than being explained in words. +.PP +Another major difference between rogue and other computer fantasy games +is that once you have solved all the puzzles in a standard fantasy game, +it has lost most of its excitement and it ceases to be fun. Rogue on the +other hand generates a new dungeon every time you play it and +even the author finds it an entertaining and exciting game. +.NH +What do all those things on the screen mean? +.PP +In order to understand what is going on in rogue you have to first get +some grasp of what rogue is doing with the screen. +The rogue screen is intended to replace the "You can see ..." descriptions +of standard fantasy games. Here is a sample of what a rogue screen might +look like. +.in +1i +.nf +.cs R 15 + --------------------- + |...................+ + |...@...........[...| + |........B..........| + |...................| + --------+------------ + + +.cs R +Level: 1 Gold: 0 Hp: 12(12) Str: 16 Ac: 6 Exp: 1/0 +.fi +.in 0 +.NH 2 +The bottom line +.PP +At the bottom line of the screen is a few pieces of cryptic information, +describing your current status. Here is an explanation of what these +things mean: +.IP Level 8 +This number indicates how deep you have gone in the dungeon. It starts +at one and goes up forever\(dg. +.FS +\(dgOr until you get killed or decide to quit. +.FE +.IP Gold +The number of gold pieces you have managed to find and keep with +you so far. +.IP Hp +Your current and maximum hit points. Hit points indicate how much +damage you can take before you die. The more you get hit in a +fight, the lower they +get. You can regain hit points by resting. The number in parentheses is +the maximum number your hit points can reach. +.IP Str +Your current strength. This can be any integer less than or +equal to eighteen. The higher the number, the stronger you are. +.IP Ac +Your current armor class. This number indicates how effective +your armor is in stopping blows from unfriendly creatures. The lower +this number is, the more effective the armor. +.IP Exp +These two numbers give your current experience level and experience points. +As you do things, you gain experience points. At certain experience point +totals, you gain an experience level. The more experienced you are, the +better you are able to fight and to withstand magical attacks. +.NH 2 +The top line +.PP +The top line of the screen is reserved for printing messages that describe +things that are impossible to represent visually. If you see a +"--More--" on the top line, this means that rogue wants to print another +message on the screen, but it wants to make certain that you have read +the one that is there first. To read the next message, just press a +space. +.NH 2 +The rest of the screen +.PP +The rest of the screen is the map of the level as you have explored it so far. +Each symbol on the screen represents something. Here is a list of what +the various symbols mean: +.IP @ +This symbol represents you, the adventurer. +.IP "-|" 6 +These symbols represent the walls of rooms. +.IP + +A door to/from a room. +.IP . +The floor of a room. +.IP # +The floor of a passage between rooms. +.IP * +A pile or pot of gold. +.IP ) +A weapon of some sort. +.IP ] +A piece of armor. +.IP ! +A flask containing a magic potion. +.IP ? +A piece of paper, usually a magic scroll. +.IP ^ +A trap, watch out for these. +.IP % +The passage leading down to the next level. +.IP : +A piece of food. +.IP A-Z +The uppercase letters represent the various inhabitants of the +Dungeons of Doom. Watch out, they can be mean. +.NH +Commands +.PP +Commands are given to rogue by pressing single letters. Most commands can +be preceded by a count to repeat them (e.g. typing "10s" will do ten searches) +The list of commands is rather long, but it can be read at any time during +the game with the ? command. Here it is for reference, with a short +explanation of each command. +.IP ? 6 +The help command. Asks for a character to give help on. If you type +a "*", it will list all the commands, otherwise it will explain what the +character you typed does. +.IP / +This is the "What is that on the screen?" command. A "/" followed by any +character that you see on the level, will tell you what that character +is. For instance, typing "/@" will tell you that the @ symbol represents +you, the player. +.IP "h , H" +Move left. You move one space to the left. If you use upper case +h, you will continue to move left until you run into something. This +works for all movement commands (e.g. "L" means run in direction "l") +.IP j +Move down. +.IP k +Move up. +.IP l +Move right. +.IP y +Move diagonally up and left. +.IP u +Move diagonally up and right. +.IP b +Move diagonally down and left. +.IP n +Move diagonally down and right. +.IP f +Find prefix. When followed by a direction it means to continue moving +in the specified direction until you pass something interesting or +run into a wall. +.IP t +Throw an object. This is a prefix command. Follow it with a direction and +you throw an object in the specified direction. (e.g. type "th" to throw +something left.) +.IP > +If you are standing over the passage down to the next level, this command +means to climb down. +.IP s +Search for traps and secret doors. Examine each space immediately adjacent +to you for the existence of a trap or secret door. There is a large chance +that even if there is something there, you won't find it so you might +have to search a while before you find something. +.IP " " +(space) Rest. This is the "do nothing" command. +This is good for waiting and healing. +.IP i +Inventory. List what you are carrying in your pack. +.IP I +Selective inventory. Tells you what a single item in your pack is. +.IP q +Quaff. Drink one of the potions you are carrying. +.IP r +Read. Read one of the scrolls in your pack. +.IP e +Eat food. Take some food out of your pack and eat it. +.IP w +Wield a weapon. Take a weapon out of your pack and carry it. You must be +wielding weapon to use it (except to throw things). To fire an arrow, +you must wield the bow. You can only wield one weapon at a time. +.IP W +Wear armor. Take a piece of armor out of your pack and put it on. You can +only wear one suit of armor at a time. +.IP T +Take armor off. You can't remove armor that is cursed. +This takes extra time. +.IP d +Drop an object. Take something out of your pack and leave it lying +on the floor. Only one object can occupy each space. +.IP o +Examine and set options. This command is further explained in the section +on options. +.IP ^L +REdraws the screen. Useful if spurious messages or transmission errors +have messed up the display. +.IP v +Prints the program version number. +.IP Q +Quit. Leave the game. +.IP R +Repeat last message. Useful when a message disappears before you can +read it. +.IP S +Save the current game in a file. Caveat: Rogue won't let you start +up a copy of a saved game, and it removes the save file as soon as +you start up a restored game. This is to prevent people from saving +a game just before a dangerous position and then restarting it +if they die. To restore a saved game, give the file name as an argument +to rogue. As in +.ti +1i +.nf +% rogue save_file +.NH +Dealing with objects +.PP +When you find something in the dungeon, it is common to want to pick the +object up. This is accomplished in rogue by walking over the object. If +you are carrying too many things, the program will tell you and it won't pick +up the object, otherwise it will add it to your pack and if the notify +option is set, tell you what you just picked up. +.PP +Many of the commands that operate on objects must prompt you to find +out which object you want to use. If you change your mind and don't want to +do that command after all, just press an escape and the command will be +aborted. +.NH +Light +.PP +Rooms in the dungeons are either lit or dark. If you walk into a lit room, +the entire room will be drawn on the screen as soon as you enter. If you +walk into a dark room, it will only be displayed as you explore it. Upon +leaving a dark room, all objects inside the room which might move are +removed from the screen. In the darkness you can only see one space +in all directions around you. +.NH +Fighting +.PP +If you see a monster and you wish to fight it, just attempt to run into it. +Many times a monster you find will mind its own business unless you attack +it. It is often the case that discretion is the better part of valor. +.NH +Armor +.PP +There are various sorts of armor lying around in the dungeon. Some of it +is enchanted, some is cursed and some is just normal. Different armor +types have different armor classes. The lower the armor class, the +more protection the armor affords against the blows of monsters. +If a piece of armor is enchanted or +cursed, its armor class will be higher or lower than normal. Here is +a list of the various armor types and their normal armor class. +.TS +center box; +c c +l | c. +Type Class += +Leather armor 8 +Studded leather / Ring mail 7 +Scale mail 6 +Chain mail 5 +Banded mail / Splint mail 4 +Plate mail 3 +.TE +.NH +Options +.PP +Due to variations in personal tastes and conceptions of the way rogue +should do things, there are a set of options you can set that cause +rogue to behave in various different ways. +.NH 2 +Setting the options +.PP +There are basically two ways to set the options. The first is with the +"o" command of rogue, the second is with the ROGUEOPTS environment +variable. On Version 6 systems, there is no equivalent of +the ROGUEOPTS feature. +.NH 3 +Using the "o" command +.PP +When you press "o" in rogue, it clears the screen and displays the current +settings for all the options. It then places the cursor by the value of the +first option and waits for you to type. You can type a RETURN which means to +go to the next option, a "\-" which means to go to the previous option, an +escape which means to return to the game, or you can give the option a +value. For boolean options this merely involves pressing "t" for true or +"f" for false. For string options, type the new value followed by a +return. +.NH 3 +Using the ROGUEOPTS variable +.PP +The ROGUEOPTS variable is a string containing a comma separated list of +initial values for the various options. Boolean variables can be turned +on by listing their name and turned off by putting a "no" in front of the +name. Thus to set up an environment variable so that jump is on, terse is +off, the name is set to "Conan the Barbarian" and the fruit is "mango", +use the command +.nf +.nf +.ti +3 +% setenv ROGUEOPTS "jump,noterse,name=Conan the Barbarian,fruit=mango" \(dg +.fi +.ti +3 +% setenv ROGUEOPTS "jump,noterse,name=Conan the Barbarian,fruit=mango" \(dg +.fi +.FS +\(dgFor those of you who use the bourne shell, the commands would be +.in +3 +.nf +$ ROGUEOPTS="jump,noterse,name=Conan the Barbarian,fruit=mango" +$ export ROGUEOPTS +.fi +.in +0 +.FE +.NH 2 +Option list +.PP +Here is a list of the options and an explanation of what each one is for. +The default value for each is enclosed in square brackets. +.IP "terse [noterse]" 25 +Useful for those who are tired of the sometimes lengthy messages of rogue. +This is a useful option for those on slow terminals. This option defaults to +on if your are on a slow (under 1200 baud) terminal. +.IP "jump [nojump]" +If this option is set, running moves will not be displayed until you +reach the end of the move. This saves considerable cpu time and +display time. This option defaults to on if you are using a slow terminal. +.IP "step [nostep] +When step is set, lists of things, like inventories or "*" responses to +"Which item do you wish to xxxx? " questions, are displayed one item +at a time on the top of the screen, rather than clearing the screen, +displaying the list, then re-displaying the dungeon level. +.IP "flush [noflush]" +If flush is set, all typeahead is thrown away after each round of battle. +This is useful for those who type way ahead and watch to their dismay as +a Kobold kills them. +.IP "askme [noaskme]" +Upon reading a scroll or quaffing a potion which does not automatically +identify it upon use, rogue will ask you what to name it so you can +recognize it in the future. +.IP "name [account name]" +This is the name of your character. It is used if you get on the top ten +scorer's list. It should be less than eighty characters long. +.IP "fruit [slime-mold]" +This should hold the name of a fruit that you enjoy eating. It is basically +a whimsy that the program uses in a couple of places. +.IP "file [rogue.save]" +The default file name for saving the game. If your phone is hung up by +accident, rogue will automatically save the game in this file. The +file name may contain the special character "~" which expands to be +your home directory. +.NH +Acknowledgements +.PP +Rogue was originally conceived of by Glenn Wichman and Michael Toy. The +help of Ken Arnold in making the program easier to use and putting the +finishing touches on is greatly appreciated. I would also like to thank +Marty McNary, Scott Nelson, Daniel Jensen, Kipp Hickman, Joe Kalash, +Steve Maurer, Bill Joy, Mark Horton and Jan Miller for their ideas +and assistance. diff --git a/rogue36.cat b/rogue36.cat new file mode 100644 index 0000000..3ecefd9 --- /dev/null +++ b/rogue36.cat @@ -0,0 +1,54 @@ +ROGUE(6) ROGUE(6) + + + +NAME + rogue - Exploring The Dungeons of Doom + +SYNOPSIS + rogue [ save_file ] + +DESCRIPTION + Rogue is a computer fantasy game with a new twist. It is crt oriented + and the object of the game is to survive the attacks of various mon- + sters and get a lot of gold, rather than the puzzle solving orientation + of most computer fantasy games. + + To get started you really only need to know two commands. The command + ? will give you a list of the available commands and the command / + will identify the things you see on the screen. + + To win the game (as opposed to merely playing to beat other people high + scores) you must locate the Amulet of Yendor which is somewhere below + the 20th level of the dungeon and get it out. Nobody has achieved this + yet and if somebody does, they will probably go down in history as a + hero among heros. + + When the game ends, either by your death, when you quit, or if you (by + some miracle) manage to win, rogue will give you alist of the top-ten + scorers. The scoring is based entirely upon how much gold you get. + There is a 10% penalty for getting yourself killed. + + For more detailed directions, read the document A Guide to the Dungeons + of Doom. + +FILES + rogue36.scr Score file + ~/rogue36.sav Default save file + +SEE ALSO + Michael C. Toy, A Guide to the Dungeons of Doom + +BUGS + Probably infinite. Currently known bugs are: Sometimes you are still + hungry even after you eat food and sometimes you get a monster on the + screen in reverse video which may or may not cause a core dump. + +COPYRIGHT + Rogue: Exploring the Dungeons of Doom + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + + +3rd Berkeley Distribution ROGUE(6) diff --git a/rogue36.doc b/rogue36.doc new file mode 100644 index 0000000..9eef5a7 --- /dev/null +++ b/rogue36.doc @@ -0,0 +1,660 @@ + + + + + + + + + + A Guide to the Dungeons of Doom + + Michael C. Toy + Computer Systems Research Group + Department of Electrical Engineering and Computer Science + University of California + Berkeley, California 94720 + + + + ABSTRACT + + Rogue is a visual CRT based fantasy game + which runs under the UNIX timesharing system. + This paper describes how to play rogue and gives a + few hints for those who might otherwise get lost + in the Dungeons of Doom. + + + +11 July 2006 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A Guide to the Dungeons of Doom + + Michael C. Toy + Computer Systems Research Group + Department of Electrical Engineering and Computer Science + University of California + Berkeley, California 94720 + + +1. Introduction + + You have just finished your years as a student at the +local fighter's guild. After much practice and sweat you +have finally completed your training and are ready to embark +upon a perilous adventure. As a test of your skills, the +local guildmasters have sent you into the Dungeons of Doom. +Your task is to return with the Amulet of Yendor. Your +reward for the completion of this task will be a full mem- +bership in the local guild. In addition, you are allowed to +keep all the loot you bring back from the dungeons. + + In preparation for your journey, you are given an +enchanted sword, taken from a dragon's hoard in the far off +Dark Mountains. You are also outfitted with elf-crafted +armor and given enough food to reach the dungeons. You say +goodbye to family and friends for what may be the last time +and head up the road. + + You set out on your way to the dungeons and after sev- +eral days of uneventful travel, you see the ancient ruins +that mark the entrance to the Dungeons of Doom. It is late +at night so you make camp at the entrance and spend the +night sleeping under the open skies. In the morning you +gather your sword, put on your armor, eat what is almost +your last food and enter the dungeons. + +2. What is going on here? + + You have just begun a game of rogue. Your goal is to +grab as much treasure as you can, find the Amulet of Yendor, +and get out of the Dungeons of Doom alive. On the screen, a +map of where you have been and what you have seen on the +current dungeon level is kept. As you explore more of the +level, it appears on the screen in front of you. + + Rogue differs from most computer fantasy games in that +it is screen oriented. Commands are all one or two +keystrokes and the results of your commands are displayed +graphically on the screen rather than being explained in +----------- +As opposed to pseudo English sentences. + + + + - 1 - + + + + + + A Guide to the Dungeons of Doom + + +words. + + Another major difference between rogue and other com- +puter fantasy games is that once you have solved all the +puzzles in a standard fantasy game, it has lost most of its +excitement and it ceases to be fun. Rogue on the other hand +generates a new dungeon every time you play it and even the +author finds it an entertaining and exciting game. + +3. What do all those things on the screen mean? + + In order to understand what is going on in rogue you +have to first get some grasp of what rogue is doing with the +screen. The rogue screen is intended to replace the "You +can see ..." descriptions of standard fantasy games. Here +is a sample of what a rogue screen might look like. + --------------------- + |...................+ + |...@...........[...| + |........B..........| + |...................| + --------+------------ + + + Level: 1 Gold: 0 Hp: 12(12) Str: 16 Ac: 6 Exp: 1/0 + +3.1. The bottom line + + At the bottom line of the screen is a few pieces of +cryptic information, describing your current status. Here +is an explanation of what these things mean: + +Level This number indicates how deep you have gone in the + dungeon. It starts at one and goes up forever. + +Gold The number of gold pieces you have managed to find + and keep with you so far. + +Hp Your current and maximum hit points. Hit points + indicate how much damage you can take before you + die. The more you get hit in a fight, the lower + they get. You can regain hit points by resting. The + number in parentheses is the maximum number your hit + points can reach. + +Str Your current strength. This can be any integer less + than or equal to eighteen. The higher the number, + the stronger you are. + +Ac Your current armor class. This number indicates how + effective your armor is in stopping blows from + unfriendly creatures. The lower this number is, the +----------- +Or until you get killed or decide to quit. + + + + - 2 - + + + + + + A Guide to the Dungeons of Doom + + + more effective the armor. + +Exp These two numbers give your current experience level + and experience points. As you do things, you gain + experience points. At certain experience point + totals, you gain an experience level. The more + experienced you are, the better you are able to + fight and to withstand magical attacks. + +3.2. The top line + + The top line of the screen is reserved for printing +messages that describe things that are impossible to repre- +sent visually. If you see a "--More--" on the top line, +this means that rogue wants to print another message on the +screen, but it wants to make certain that you have read the +one that is there first. To read the next message, just +press a space. + +3.3. The rest of the screen + + The rest of the screen is the map of the level as you +have explored it so far. Each symbol on the screen repre- +sents something. Here is a list of what the various symbols +mean: + +@ This symbol represents you, the adventurer. + +-| These symbols represent the walls of rooms. + ++ A door to/from a room. + +. The floor of a room. + +# The floor of a passage between rooms. + +* A pile or pot of gold. + +) A weapon of some sort. + +] A piece of armor. + +! A flask containing a magic potion. + +? A piece of paper, usually a magic scroll. + +^ A trap, watch out for these. + +% The passage leading down to the next level. + +: A piece of food. + +A-Z The uppercase letters represent the various inhabi- + tants of the Dungeons of Doom. Watch out, they can be + + + + - 3 - + + + + + + A Guide to the Dungeons of Doom + + + mean. + +4. Commands + + Commands are given to rogue by pressing single letters. +Most commands can be preceded by a count to repeat them +(e.g. typing "10s" will do ten searches) The list of com- +mands is rather long, but it can be read at any time during +the game with the ? command. Here it is for reference, with +a short explanation of each command. + +? The help command. Asks for a character to give help + on. If you type a "*", it will list all the commands, + otherwise it will explain what the character you typed + does. + +/ This is the "What is that on the screen?" command. A + "/" followed by any character that you see on the + level, will tell you what that character is. For + instance, typing "/@" will tell you that the @ symbol + represents you, the player. + +h , H Move left. You move one space to the left. If you + use upper case h, you will continue to move left until + you run into something. This works for all movement + commands (e.g. "L" means run in direction "l") + +j Move down. + +k Move up. + +l Move right. + +y Move diagonally up and left. + +u Move diagonally up and right. + +b Move diagonally down and left. + +n Move diagonally down and right. + +f Find prefix. When followed by a direction it means to + continue moving in the specified direction until you + pass something interesting or run into a wall. + +t Throw an object. This is a prefix command. Follow it + with a direction and you throw an object in the speci- + fied direction. (e.g. type "th" to throw something + left.) + +> If you are standing over the passage down to the next + level, this command means to climb down. + + + + + + - 4 - + + + + + + A Guide to the Dungeons of Doom + + +s Search for traps and secret doors. Examine each space + immediately adjacent to you for the existence of a + trap or secret door. There is a large chance that + even if there is something there, you won't find it so + you might have to search a while before you find some- + thing. + + (space) Rest. This is the "do nothing" command. This + is good for waiting and healing. + +i Inventory. List what you are carrying in your pack. + +I Selective inventory. Tells you what a single item in + your pack is. + +q Quaff. Drink one of the potions you are carrying. + +r Read. Read one of the scrolls in your pack. + +e Eat food. Take some food out of your pack and eat it. + +w Wield a weapon. Take a weapon out of your pack and + carry it. You must be wielding weapon to use it + (except to throw things). To fire an arrow, you must + wield the bow. You can only wield one weapon at a + time. + +W Wear armor. Take a piece of armor out of your pack + and put it on. You can only wear one suit of armor at + a time. + +T Take armor off. You can't remove armor that is + cursed. This takes extra time. + +d Drop an object. Take something out of your pack and + leave it lying on the floor. Only one object can + occupy each space. + +o Examine and set options. This command is further + explained in the section on options. + +^L REdraws the screen. Useful if spurious messages or + transmission errors have messed up the display. + +v Prints the program version number. + +Q Quit. Leave the game. + +R Repeat last message. Useful when a message disappears + before you can read it. + +S Save the current game in a file. Caveat: Rogue won't + let you start up a copy of a saved game, and it + removes the save file as soon as you start up a + + + + - 5 - + + + + + + A Guide to the Dungeons of Doom + + + restored game. This is to prevent people from saving + a game just before a dangerous position and then + restarting it if they die. To restore a saved game, + give the file name as an argument to rogue. As in + % rogue save_file + +5. Dealing with objects + + When you find something in the dungeon, it is common to +want to pick the object up. This is accomplished in rogue +by walking over the object. If you are carrying too many +things, the program will tell you and it won't pick up the +object, otherwise it will add it to your pack and if the +notify option is set, tell you what you just picked up. + + Many of the commands that operate on objects must +prompt you to find out which object you want to use. If you +change your mind and don't want to do that command after +all, just press an escape and the command will be aborted. + +6. Light + + Rooms in the dungeons are either lit or dark. If you +walk into a lit room, the entire room will be drawn on the +screen as soon as you enter. If you walk into a dark room, +it will only be displayed as you explore it. Upon leaving a +dark room, all objects inside the room which might move are +removed from the screen. In the darkness you can only see +one space in all directions around you. + +7. Fighting + + If you see a monster and you wish to fight it, just +attempt to run into it. Many times a monster you find will +mind its own business unless you attack it. It is often the +case that discretion is the better part of valor. + +8. Armor + + There are various sorts of armor lying around in the +dungeon. Some of it is enchanted, some is cursed and some +is just normal. Different armor types have different armor +classes. The lower the armor class, the more protection the +armor affords against the blows of monsters. If a piece of +armor is enchanted or cursed, its armor class will be higher +or lower than normal. Here is a list of the various armor +types and their normal armor class. + + + + + + + + + + + - 6 - + + + + + + A Guide to the Dungeons of Doom + + + +------------------------------------+ + | Type Class | + +----------------------------+-------+ + |Leather armor | 8 | + |Studded leather / Ring mail | 7 | + |Scale mail | 6 | + |Chain mail | 5 | + |Banded mail / Splint mail | 4 | + |Plate mail | 3 | + +----------------------------+-------+ + +9. Options + + Due to variations in personal tastes and conceptions of +the way rogue should do things, there are a set of options +you can set that cause rogue to behave in various different +ways. + +9.1. Setting the options + + There are basically two ways to set the options. The +first is with the "o" command of rogue, the second is with +the ROGUEOPTS environment variable. On Version 6 systems, +there is no equivalent of the ROGUEOPTS feature. + +9.1.1. Using the "o" command + + When you press "o" in rogue, it clears the screen and +displays the current settings for all the options. It then +places the cursor by the value of the first option and waits +for you to type. You can type a RETURN which means to go to +the next option, a "-" which means to go to the previous +option, an escape which means to return to the game, or you +can give the option a value. For boolean options this +merely involves pressing "t" for true or "f" for false. For +string options, type the new value followed by a return. + +9.1.2. Using the ROGUEOPTS variable + + The ROGUEOPTS variable is a string containing a comma +separated list of initial values for the various options. +Boolean variables can be turned on by listing their name and +turned off by putting a "no" in front of the name. Thus to +set up an environment variable so that jump is on, terse is +off, the name is set to "Conan the Barbarian" and the fruit +is "mango", use the command + % setenv ROGUEOPTS "jump,noterse,name=Conan the Barbarian,fruit=mango" + % setenv ROGUEOPTS "jump,noterse,name=Conan the Barbar- +ian,fruit=mango" + +----------- +For those of you who use the bourne shell, the +commands would be + $ ROGUEOPTS="jump,noterse,name=Conan the Barbarian,fruit=mango" + + + + - 7 - + + + + + + A Guide to the Dungeons of Doom + + +9.2. Option list + + Here is a list of the options and an explanation of +what each one is for. The default value for each is +enclosed in square brackets. + +terse [noterse] Useful for those who are tired of + the sometimes lengthy messages of + rogue. This is a useful option for + those on slow terminals. This + option defaults to on if your are + on a slow (under 1200 baud) termi- + nal. + +jump [nojump] If this option is set, running + moves will not be displayed until + you reach the end of the move. + This saves considerable cpu time + and display time. This option + defaults to on if you are using a + slow terminal. + +step [nostep] When step is set, lists of things, + like inventories or "*" responses + to "Which item do you wish to xxxx? + " questions, are displayed one item + at a time on the top of the screen, + rather than clearing the screen, + displaying the list, then re-dis- + playing the dungeon level. + +flush [noflush] If flush is set, all typeahead is + thrown away after each round of + battle. This is useful for those + who type way ahead and watch to + their dismay as a Kobold kills + them. + +askme [noaskme] Upon reading a scroll or quaffing a + potion which does not automatically + identify it upon use, rogue will + ask you what to name it so you can + recognize it in the future. + +name [account name] This is the name of your character. + It is used if you get on the top + ten scorer's list. It should be + less than eighty characters long. + +fruit [slime-mold] This should hold the name of a + fruit that you enjoy eating. It is + basically a whimsy that the program +----------- + $ export ROGUEOPTS + + + + - 8 - + + + + + + A Guide to the Dungeons of Doom + + + uses in a couple of places. + +file [rogue.save] The default file name for saving + the game. If your phone is hung up + by accident, rogue will automati- + cally save the game in this file. + The file name may contain the spe- + cial character "~" which expands to + be your home directory. + +10. Acknowledgements + + Rogue was originally conceived of by Glenn Wichman and +Michael Toy. The help of Ken Arnold in making the program +easier to use and putting the finishing touches on is +greatly appreciated. I would also like to thank Marty +McNary, Scott Nelson, Daniel Jensen, Kipp Hickman, Joe +Kalash, Steve Maurer, Bill Joy, Mark Horton and Jan Miller +for their ideas and assistance. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 9 - + + diff --git a/rogue36.html b/rogue36.html new file mode 100644 index 0000000..d5a70a2 --- /dev/null +++ b/rogue36.html @@ -0,0 +1,633 @@ + + + + + + +

A Guide to the Dungeons of Doom

+ +

Michael C. Toy

+ +

+Computer Systems Research Group
+Department of Electrical Engineering and Computer Science
+University of California
+Berkeley, California 94720

+ +

ABSTRACT

+ +
+
+

Rogue is a visual CRT based fantasy game which runs under the + UNIX timesharing system. This paper describes how to play rogue, and gives a few hints for + those who might otherwise get + lost in the Dungeons of Doom.

+
+
+ +

1. Introduction

+ +

You have just finished your years as a +student at the local fighter’s guild. After much +practice and sweat you have finally completed your training +and are ready to embark upon a perilous adventure. As a test +of your skills, the local guildmasters have sent you into +the Dungeons of Doom. Your task is to return with the Amulet +of Yendor. Your reward for the completion of this task will +be a full membership in the local guild. In addition, you +are allowed to keep all the loot you bring back from the +dungeons.

+ +

In preparation for your journey, you are +given an enchanted sword, taken +from a dragon’s hoard in the far off Dark Mountains. +You are also outfitted with elf-crafted armor and given +enough food to reach the dungeons. You say goodbye to family +and friends for what may be the last time and head up the +road.

+ +

You set out on your way to the dungeons and +after several days of uneventful travel, you see the +ancient ruins that mark the entrance to the Dungeons of +Doom. It is late at night so you make camp at the entrance +and spend the night sleeping under the open skies. In the +morning you gather your sword, put on your armor, eat what +is almost your last food and enter the +dungeons.

+ +

2. What is going on here?

+ +

You have just begun a game of rogue. Your +goal is to grab as much treasure as you can, find the Amulet +of Yendor, and get out of the Dungeons of Doom alive. On the +screen, a map of where you have been and what you have seen +on the current dungeon level is kept. As you explore more of +the level, it appears on the screen in front of +you.

+ +

Rogue differs from most computer fantasy +games in that it is screen oriented. Commands are all one or +two keystrokes1 and the +results of your commands are displayed graphically on the +screen rather than being explained in words2.

+ +

Another major difference between rogue and +other computer fantasy games is that once you have solved +all the puzzles in a standard fantasy game, it has lost most +of its excitement and it ceases to be fun. Rogue on the +other hand generates a new dungeon every time you play it +and even the author finds it an entertaining and exciting +game.

+ +

3. What do all those things on the screen mean?

+ +

In order to understand what is going on in +rogue you have to first get some grasp of what rogue is +doing with the screen. The rogue screen is intended to +replace the “You can see ...” descriptions of +standard fantasy games. Here is a sample of what a +rogue screen might look like.

+ +
+                  ---------------------
+                  |...................+
+                  |...@...........[...|
+                  |........B..........|
+                  |...................|
+                  --------+------------
+
+Level: 1  Gold: 0      Hp: 12(12)  Str: 16  Ac: 6  Exp: 1/0
+
+ +

3.1. The bottom line

+ +

At the bottom line of the screen is a few +pieces of cryptic information describing your current +status. Here is an explanation of what these things +mean:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +

Level  

This number indicates how deep you have gone in the + dungeon. It starts at one and goes up forever2.

Gold

The number of gold pieces you have managed to find + and keep with you so far.

Hp

Your current and maximum hit points. + Health points indicate how much damage you can take before + you die. The more you get hit in a fight, the lower they + get. You can regain health points by resting. The number in + parentheses is the maximum number your hit points can + reach.

Str

Your current strength. This can be any + integer less than or equal to eighteen. The higher the number, + the stronger you are.

Ac

Your current armor class. This number + indicates how effective your armor is in stopping blows from + unfriendly creatures. The lower this number is, the more + effective the armor.

Exp

These two numbers give your current experience + level and experience points. As you do things, you gain experience + points. At certain experience point totals, you gain an + experience level. The more experienced you are, the better + you are able to fight and to withstand magical attacks.

+ +

3.2. The top line

+ +

The top line of the screen is reserved for +printing messages that describe things that are impossible +to represent visually. If you see a “--More--” +on the top line, this means that rogue wants to print +another message on the screen, but it wants to make certain +that you have read the one that is there first. To read the +next message, just type a space.

+ +

3.3. The rest of the screen

+ +

The rest of the screen is the map of the +level as you have explored it so far. Each symbol on the +screen represents something. Here is a list of what the +various symbols mean:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@

This symbol represents you, the adventurer.

- |

These symbols represent the walls of rooms.

+ +

A door to/from a room.

.

The floor of a room.

#

The floor of a passage between rooms.

*

A pile or pot of gold.

)

A weapon of some sort.

]

A piece of armor.

!

A flask containing a magic potion.

?

A piece of paper, usually a magic scroll.

^

A trap, watch out for these.

%

The passage leading down to the next level.

:

A piece of food.

A-Z  

The uppercase letters represent the various + inhabitants of the Dungeons of Doom. Watch out, they can be mean.

+ +

4. Commands

+ +

Commands are given to rogue by pressing single letters. +Most commands can be preceded by a count to repeat them +(e.g. typing "10s" will do ten searches) The list +of commands is rather long, but it can be read at any time +during the game with the ? command. Here it is for +reference, with a short explanation of each command.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
?

The help command. Asks for a character to give help + on. If you type a "*", it will list all the commands, + otherwise it will explain what the character you typed + does.

/

This is the "What is that on the screen?" + command. A "/" followed by any character that you + see on the level, will tell you what that character is. For + instance, typing "/@" will tell you that the @ + symbol represents you, the player.

h, H
 

Move left. You move one space to the left. If you use + upper case h, you will continue to move left until you run into something. + This works for all movement commands (e.g. "L" means run in direction + "l")

j

Move down.

k

Move up.

l

Move right.

y

Move diagonally up and left.

u

Move diagonally up and right.

b

Move diagonally down and left.

n

Move diagonally down and right.

t

Throw an object. This is a prefix command. When + followed with a direction it throws an object in the specified direction. + (e.g. type “th” to throw something to the left.)

f

Find prefix. When followed by a direction it means to + continue moving in the specified direction until you pass + something interesting or run into a wall.

t

Throw an object. This is a prefix command. Follow it + with a direction and you throw an object in the specified + direction. (e.g. type "th" to throw something + left.)

>

If you are standing over the passage down to the next + level, this command means to climb down.

s

Search for traps and secret doors. Examine each space + immediately adjacent to you for the existence of a trap or + secret door. There is a large chance that even if there is + something there, you won’t find it so you might have + to search a while before you find something.

 

(space) Rest. This is the "do nothing" + command. This is good for waiting and healing.

+

i

+

Inventory. List what you are carrying in your pack.

I

Selective inventory. Tells you what a single item in + your pack is.

q

Quaff. Drink one of the potions you are + carrying.

r

Read. Read one of the scrolls in your pack.

e

Eat food. Take some food out of your pack and eat + it.

w

Wield a weapon. Take a weapon out of your pack and + carry it. You must be wielding weapon to use it (except to throw + things). To fire an arrow, you must wield the bow. You can + only wield one weapon at a time.

W

Wear armor. Take a piece of armor out of your pack + and put it on. You can only wear one suit of armor at a time.

T

Take armor off. You can’t remove armor that is + cursed. This takes extra time.

d

Drop an object. Take something out of your pack and + leave it lying on the floor. Only one object can occupy each + space.

o

Examine and set options. This command is further + explained in the section on options.

^L

Redraws the screen. Useful if spurious messages or + transmission errors have messed up the display.

v

Prints the program version number.

Q

Quit. Leave the game.

R

Repeat last message. Useful when a message disappears + before you can read it.

S

Save the current game in a file. Caveat: Rogue + won’t let you start up a copy of a saved game, and it + removes the save file as soon as you start up a restored + game. This is to prevent people from saving a game just + before a dangerous position and then restarting it if they + die. To restore a saved game, give the file name as an + argument to rogue. As in % rogue36 save_file

+
+ +

5. Dealing with objects

+ +

When you find something in the dungeon, it is common to +want to pick the object up. This is accomplished in rogue by +walking over the object. If you are carrying too many +things, the program will tell you and it won’t pick up +the object, otherwise it will add it to your pack and if the +notify option is set, tell you what you just picked up.

+ +

Many of the commands that operate on objects must prompt +you to find out which object you want to use. If you change +your mind and don’t want to do that command after all, +just press an escape and the command will be aborted.

+ +

6. Light

+ +

Rooms in the dungeons are either lit or dark. If you +walk into a lit room, the entire room will be drawn on the +screen as soon as you enter. If you walk into a dark room, +it will only be displayed as you explore it. Upon leaving a +dark room, all objects inside the room which might move are +removed from the screen. In the darkness you can only see +one space in all directions around you.

+ +

7. Fighting

+

If you see a monster and you wish to fight it, just +attempt to run into it. Many times a monster you find will +mind its own business unless you attack it. It is often the +case that discretion is the better part of valor.

+ +

8. Armor

+ +

There are various sorts of armor lying around in the +dungeon. Some of it is enchanted, some is cursed and some is +just normal. Different armor types have different armor +classes. The lower the armor class, the more protection the +armor affords against the blows of monsters. If a piece of +armor is enchanted or cursed, its armor class will be higher +or lower than normal. Here is a list of the various armor +types and their normal armor class.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeClass
Leather armor8
Studded leather / Ring mail7
Scale mail 6
Chain mail  5
Banded mail / Splint mail4
Plate mail3
+
+ +

9. Options

+ +

Due to variations in personal tastes and conceptions of +the way rogue should do things, there are a set of options +you can set that cause rogue to behave in various different +ways.

+ +

9.1 Setting the options

+ +

There are basically two ways to set the options. The +first is with the "o" command of rogue, the second +is with the ROGUEOPTS environment variable. On Version 6 +systems, there is no equivalent of the ROGUEOPTS +feature.

+ +

9.1.1. Using the "o" command

+ +

When you press "o" in rogue, it clears the +screen and displays the current settings for all the +options. It then places the cursor by the value of the first +option and waits for you to type. You can type a RETURN +which means to go to the next option, a "−" +which means to go to the previous option, an escape which +means to return to the game, or you can give the option a +value. For boolean options this merely involves pressing +"t" for true or "f" for false. For +string options, type the new value followed by a return.

+ +

9.1.2. Using the ROGUEOPTS variable

+ +

The ROGUEOPTS variable is a string containing a comma +separated list of initial values for the various options. +Boolean variables can be turned on by listing their name and +turned off by putting a "no" in front of the name. +Thus to set up an environment variable so that jump is on, +terse is off, the name is set to "Conan the +Barbarian" and the fruit is "mango", use the +command

+ +
+   % setenv ROGUEOPTS "jump,noterse,name=Conan the Barbarian,fruit=mango"3
+
+ +

9.2. Option list

+ +

Here is a list of the options and an explanation of what +each one is for. The default value for each is enclosed in +square brackets.

+ +

terse[noterse]

+
+

+ Useful for those who are tired of the sometimes lengthy + messages of rogue. This is a useful option for those on slow + terminals. This option defaults to on if your are on a slow + (under 1200 baud) terminal.

+
+ +

jump[nojump]

+
+

If this option is set, running moves will not be + displayed until you reach the end of the move. This saves + considerable cpu time and display time. This option defaults + to on if you are using a slow terminal.

+
+ +

step[nostep]

+
+

When step is set, lists of things, like inventories or + "*" responses to "Which item do you wish to + xxxx? " questions, are displayed one item at a time on + the top of the screen, rather than clearing the screen, + displaying the list, then re-displaying the dungeon + level.

+
+ +

flush[noflush]

+
+

If flush is set, all typeahead is thrown away after each + round of battle. This is useful for those who type way ahead + and watch to their dismay as a Kobold kills them.

+
+ +

askme[noaskme]

+
+

Upon reading a scroll or quaffing a potion which does + not automatically identify it upon use, rogue will ask you + what to name it so you can recognize it in the future.

+
+ +

name [account name]

+
+

This is the name of your character. It is used if you + get on the top ten scorer’s list. It should be less + than eighty characters long.

+
+ +

fruit[slime-mold]

+
+

This should hold the name of a fruit that you enjoy + eating. It is basically a whimsy that the program uses in a + couple of places.

+
+ +

file[rogue.save]

+
+

The default file name for saving the game. If your phone + is hung up by accident, rogue will automatically save the + game in this file. The file name may contain the special + character "~" which expands to be your home + directory.

+
+ +


+ +10. Acknowledgements

+ +

Rogue was originally conceived of by Glenn Wichman and +Michael Toy. The help of Ken Arnold in making the program +easier to use and putting the finishing touches on is +greatly appreciated. I would also like to thank Marty +McNary, Scott Nelson, Daniel Jensen, Kipp Hickman, Joe +Kalash, Steve Maurer, Bill Joy, Mark Horton and Jan Miller +for their ideas and assistance.

+ + + + + + + + + + + + + + +
1As opposed to pseudo English sentences.
2Or until you get killed or decide to quit.
3  

For those of you who use the Bourne shell, the commands would be

+
    $ ROGUEOPTS="jump,noterse,name=Conan the Barbarian,fruit=mango"
+    $ export ROGUEOPTS
+
+ + + \ No newline at end of file diff --git a/rogue36.sln b/rogue36.sln new file mode 100644 index 0000000..2b4f12e --- /dev/null +++ b/rogue36.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rogue36", "rogue36.vcproj", "{B986AB16-E9C2-4299-A772-F02B28620521}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B986AB16-E9C2-4299-A772-F02B28620521}.Debug|Win32.ActiveCfg = Debug|Win32 + {B986AB16-E9C2-4299-A772-F02B28620521}.Debug|Win32.Build.0 = Debug|Win32 + {B986AB16-E9C2-4299-A772-F02B28620521}.Release|Win32.ActiveCfg = Release|Win32 + {B986AB16-E9C2-4299-A772-F02B28620521}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rogue36.vcproj b/rogue36.vcproj new file mode 100644 index 0000000..4878426 --- /dev/null +++ b/rogue36.vcproj @@ -0,0 +1,360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rooms.c b/rooms.c new file mode 100644 index 0000000..0e4530e --- /dev/null +++ b/rooms.c @@ -0,0 +1,185 @@ +/* + * Draw the nine rooms on the screen + * + * @(#)rooms.c 3.8 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include "rogue.h" + +do_rooms() +{ + register int i; + register struct room *rp; + register struct linked_list *item; + register struct thing *tp; + register int left_out; + coord top; + coord bsze; + coord mp; + + /* + * bsze is the maximum room size + */ + bsze.x = COLS/3; + bsze.y = LINES/3; + /* + * Clear things for a new level + */ + for (rp = rooms; rp <= &rooms[MAXROOMS-1]; rp++) + rp->r_goldval = rp->r_nexits = rp->r_flags = 0; + /* + * Put the gone rooms, if any, on the level + */ + left_out = rnd(4); + for (i = 0; i < left_out; i++) + rooms[rnd_room()].r_flags |= ISGONE; + /* + * dig and populate all the rooms on the level + */ + for (i = 0, rp = rooms; i < MAXROOMS; rp++, i++) + { + /* + * Find upper left corner of box that this room goes in + */ + top.x = (i%3)*bsze.x + 1; + top.y = i/3*bsze.y; + if (rp->r_flags & ISGONE) + { + /* + * Place a gone room. Make certain that there is a blank line + * for passage drawing. + */ + do + { + rp->r_pos.x = top.x + rnd(bsze.x-2) + 1; + rp->r_pos.y = top.y + rnd(bsze.y-2) + 1; + rp->r_max.x = -COLS; + rp->r_max.y = -LINES; + } until(rp->r_pos.y > 0 && rp->r_pos.y < LINES-1); + continue; + } + if (rnd(10) < level-1) + rp->r_flags |= ISDARK; + /* + * Find a place and size for a random room + */ + do + { + rp->r_max.x = rnd(bsze.x - 4) + 4; + rp->r_max.y = rnd(bsze.y - 4) + 4; + rp->r_pos.x = top.x + rnd(bsze.x - rp->r_max.x); + rp->r_pos.y = top.y + rnd(bsze.y - rp->r_max.y); + } until (rp->r_pos.y != 0); + /* + * Put the gold in + */ + if (rnd(100) < 50 && (!amulet || level >= max_level)) + { + rp->r_goldval = GOLDCALC; + rnd_pos(rp, &rp->r_gold); + if (roomin(&rp->r_gold) != rp) + endwin(), abort(); + } + draw_room(rp); + /* + * Put the monster in + */ + if (rnd(100) < (rp->r_goldval > 0 ? 80 : 25)) + { + item = new_item(sizeof *tp); + tp = (struct thing *) ldata(item); + do + { + rnd_pos(rp, &mp); + } until(mvwinch(stdscr, mp.y, mp.x) == FLOOR); + new_monster(item, randmonster(FALSE), &mp); + /* + * See if we want to give it a treasure to carry around. + */ + if (rnd(100) < monsters[tp->t_type-'A'].m_carry) + attach(tp->t_pack, new_thing()); + } + } +} + +/* + * Draw a box around a room + */ + +draw_room(rp) +register struct room *rp; +{ + register int j, k; + + move(rp->r_pos.y, rp->r_pos.x+1); + vert(rp->r_max.y-2); /* Draw left side */ + move(rp->r_pos.y+rp->r_max.y-1, rp->r_pos.x); + horiz(rp->r_max.x); /* Draw bottom */ + move(rp->r_pos.y, rp->r_pos.x); + horiz(rp->r_max.x); /* Draw top */ + vert(rp->r_max.y-2); /* Draw right side */ + /* + * Put the floor down + */ + for (j = 1; j < rp->r_max.y-1; j++) + { + move(rp->r_pos.y + j, rp->r_pos.x+1); + for (k = 1; k < rp->r_max.x-1; k++) + addch(FLOOR); + } + /* + * Put the gold there + */ + if (rp->r_goldval) + mvaddch(rp->r_gold.y, rp->r_gold.x, GOLD); +} + +/* + * horiz: + * draw a horizontal line + */ + +horiz(cnt) +register int cnt; +{ + while (cnt--) + addch('-'); +} + +/* + * vert: + * draw a vertical line + */ + +vert(cnt) +register int cnt; +{ + register int x, y; + + getyx(stdscr, y, x); + x--; + while (cnt--) { + move(++y, x); + addch('|'); + } +} + +/* + * rnd_pos: + * pick a random spot in a room + */ + +rnd_pos(rp, cp) +register struct room *rp; +register coord *cp; +{ + cp->x = rp->r_pos.x + rnd(rp->r_max.x-2) + 1; + cp->y = rp->r_pos.y + rnd(rp->r_max.y-2) + 1; +} diff --git a/save.c b/save.c new file mode 100644 index 0000000..03f4511 --- /dev/null +++ b/save.c @@ -0,0 +1,276 @@ +/* + * save and restore routines + * + * @(#)save.c 3.9 (Berkeley) 6/16/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include +#include +#include +#include +#include +#include "rogue.h" +#include "machdep.h" + +typedef struct stat STAT; + +extern char version[], encstr[]; + +STAT sbuf; + +save_game() +{ + register FILE *savef; + register int c; + char buf[80]; + + /* + * get file name + */ + mpos = 0; + if (file_name[0] != '\0') + { + msg("Save file (%s)? ", file_name); + do + { + c = readchar(cw); + } while (c != 'n' && c != 'N' && c != 'y' && c != 'Y'); + mpos = 0; + if (c == 'y' || c == 'Y') + { + msg("File name: %s", file_name); + goto gotfile; + } + } + + do + { + msg("File name: "); + mpos = 0; + buf[0] = '\0'; + if (get_str(buf, cw) == QUIT) + { + msg(""); + return FALSE; + } + strcpy(file_name, buf); +gotfile: + if ((savef = fopen(file_name, "w")) == NULL) + msg(strerror(errno)); /* fake perror() */ + } while (savef == NULL); + + /* + * write out encrpyted file (after a stat) + * The fwrite is to force allocation of the buffer before the write + */ + if (save_file(savef) != 0) + { + msg("Save game failed!"); + return FALSE; + } + return TRUE; +} + +/* + * automatically save a file. This is used if a HUP signal is + * recieved + */ +void +auto_save(int p) +{ + register FILE *savef; + register int i; + + for (i = 0; i < NSIG; i++) + signal(i, SIG_IGN); + if (file_name[0] != '\0' && (savef = fopen(file_name, "w")) != NULL) + save_file(savef); + endwin(); + exit(1); +} + +/* + * write the saved game on the file + */ +save_file(savef) +register FILE *savef; +{ + char buf[80]; + int ret; + + wmove(cw, LINES-1, 0); + draw(cw); + fseek(savef, 0L, 0); + + memset(buf,0,80); + strcpy(buf,version); + encwrite(buf,80,savef); + memset(buf,0,80); + strcpy(buf,"R36 2\n"); + encwrite(buf,80,savef); + memset(buf,0,80); + sprintf(buf,"%d x %d\n", LINES, COLS); + encwrite(buf,80,savef); + + ret = rs_save_file(savef); + + fclose(savef); + + return(ret); +} + +restore(file, envp) +register char *file; +char **envp; +{ + register int inf; + extern char **environ; + char buf[80]; + int slines, scols; + int rogue_version = 0, savefile_version = 0; + + if (strcmp(file, "-r") == 0) + file = file_name; + if ((inf = open(file, 0)) < 0) + { + perror(file); + return FALSE; + } + + fflush(stdout); + encread(buf, 80, inf); + + if (strcmp(buf, version) != 0) + { + printf("Sorry, saved game is out of date.\n"); + return FALSE; + } + + encread(buf, 80, inf); + sscanf(buf, "R%d %d\n", &rogue_version, &savefile_version); + + if ((rogue_version != 36) && (savefile_version != 2)) + { + printf("Sorry, saved game format is out of date.\n"); + return FALSE; + } + + encread(buf,80,inf); + sscanf(buf,"%d x %d\n",&slines, &scols); + + /* + * we do not close the file so that we will have a hold of the + * inode for as long as possible + */ + + initscr(); + + if (slines > LINES) + { + endwin(); + printf("Sorry, original game was played on a screen with %d lines.\n",slines); + printf("Current screen only has %d lines. Unable to restore game\n",LINES); + return(FALSE); + } + + if (scols > COLS) + { + endwin(); + printf("Sorry, original game was played on a screen with %d columns.\n",scols); + printf("Current screen only has %d columns. Unable to restore game\n",COLS); + return(FALSE); + } + + cw = newwin(LINES, COLS, 0, 0); + mw = newwin(LINES, COLS, 0, 0); + hw = newwin(LINES, COLS, 0, 0); + nocrmode(); + keypad(cw,1); + mpos = 0; + mvwprintw(cw, 0, 0, "%s", file); + + if (rs_restore_file(inf) != 0) + { + endwin(); + printf("Cannot restore file\n"); + return(FALSE); + } + + if (!wizard && (md_unlink_open_file(file, inf) < 0)) + { + endwin(); + printf("Cannot unlink file\n"); + return FALSE; + } + + environ = envp; + strcpy(file_name, file); + setup(); + clearok(curscr, TRUE); + touchwin(cw); + srand(getpid()); + status(); + playit(); + /*NOTREACHED*/ + return(0); +} + +/* + * perform an encrypted write + */ +encwrite(starta, size, outf) +register void *starta; +unsigned int size; +register FILE *outf; +{ + register char *ep; + register char *start = starta; + unsigned int o_size = size; + ep = encstr; + + while (size) + { + if (putc(*start++ ^ *ep++, outf) == EOF) + return(o_size - size); + if (*ep == '\0') + ep = encstr; + size--; + } + + return(o_size - size); +} + +/* + * perform an encrypted read + */ +encread(starta, size, inf) +register void *starta; +unsigned int size; +register int inf; +{ + register char *ep; + register int read_size; + register char *start = starta; + + if ((read_size = read(inf, start, size)) == -1 || read_size == 0) + return read_size; + + ep = encstr; + + while (size--) + { + *start++ ^= *ep++; + if (*ep == '\0') + ep = encstr; + } + return read_size; +} diff --git a/scrolls.c b/scrolls.c new file mode 100644 index 0000000..24a5578 --- /dev/null +++ b/scrolls.c @@ -0,0 +1,316 @@ + +/* + * Read a scroll and let it happen + * + * @(#)scrolls.c 3.5 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include +#include "rogue.h" + +read_scroll() +{ + register struct object *obj; + register struct linked_list *item; + register struct room *rp; + register int i,j; + register char ch, nch; + register struct linked_list *titem; + char buf[80]; + + item = get_item("read", SCROLL); + if (item == NULL) + return; + obj = (struct object *) ldata(item); + if (obj->o_type != SCROLL) + { + if (!terse) + msg("There is nothing on it to read"); + else + msg("Nothing to read"); + return; + } + msg("As you read the scroll, it vanishes."); + /* + * Calculate the effect it has on the poor guy. + */ + if (obj == cur_weapon) + cur_weapon = NULL; + switch(obj->o_which) + { + case S_CONFUSE: + /* + * Scroll of monster confusion. Give him that power. + */ + msg("Your hands begin to glow red"); + player.t_flags |= CANHUH; + when S_LIGHT: + s_know[S_LIGHT] = TRUE; + if ((rp = roomin(&hero)) == NULL) + msg("The corridor glows and then fades"); + else + { + addmsg("The room is lit"); + if (!terse) + addmsg(" by a shimmering blue light."); + endmsg(); + rp->r_flags &= ~ISDARK; + /* + * Light the room and put the player back up + */ + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + when S_ARMOR: + if (cur_armor != NULL) + { + msg("Your armor glows faintly for a moment"); + cur_armor->o_ac--; + cur_armor->o_flags &= ~ISCURSED; + } + when S_HOLD: + /* + * Hold monster scroll. Stop all monsters within two spaces + * from chasing after the hero. + */ + { + register int x,y; + register struct linked_list *mon; + + for (x = hero.x-2; x <= hero.x+2; x++) + for (y = hero.y-2; y <= hero.y+2; y++) + if (y > 0 && x > 0 && isupper(mvwinch(mw, y, x))) + if ((mon = find_mons(y, x)) != NULL) + { + register struct thing *th; + + th = (struct thing *) ldata(mon); + th->t_flags &= ~ISRUN; + th->t_flags |= ISHELD; + } + } + when S_SLEEP: + /* + * Scroll which makes you fall asleep + */ + s_know[S_SLEEP] = TRUE; + msg("You fall asleep."); + no_command += 4 + rnd(SLEEPTIME); + when S_CREATE: + /* + * Create a monster + * First look in a circle around him, next try his room + * otherwise give up + */ + { + register int x, y; + register bool appear = 0; + coord mp; + + /* + * Search for an open place + */ + for (y = hero.y-1; y <= hero.y+1; y++) + for (x = hero.x-1; x <= hero.x+1; x++) + { + /* + * Don't put a monster in top of the player. + */ + if (y == hero.y && x == hero.x) + continue; + /* + * Or anything else nasty + */ + if (step_ok(winat(y, x))) + { + if (rnd(++appear) == 0) + { + mp.y = y; + mp.x = x; + } + } + } + if (appear) + { + titem = new_item(sizeof (struct thing)); + new_monster(titem, randmonster(FALSE), &mp); + } + else + msg("You hear a faint cry of anguish in the distance."); + } + when S_IDENT: + /* + * Identify, let the rogue figure something out + */ + msg("This scroll is an identify scroll"); + s_know[S_IDENT] = TRUE; + whatis(); + when S_MAP: + /* + * Scroll of magic mapping. + */ + s_know[S_MAP] = TRUE; + msg("Oh, now this scroll has a map on it."); + overwrite(stdscr, hw); + /* + * Take all the things we want to keep hidden out of the window + */ + for (i = 0; i < LINES; i++) + for (j = 0; j < COLS; j++) + { + switch (nch = ch = mvwinch(hw, i, j)) + { + case SECRETDOOR: + nch = DOOR; + mvaddch(i, j, nch); + case '-': + case '|': + case DOOR: + case PASSAGE: + case ' ': + case STAIRS: + if (mvwinch(mw, i, j) != ' ') + { + register struct thing *it; + + it = (struct thing *) ldata(find_mons(i, j)); + if ((it != NULL) && (it->t_oldch == ' ')) + it->t_oldch = nch; + } + break; + default: + nch = ' '; + } + if (nch != ch) + waddch(hw, nch); + } + /* + * Copy in what he has discovered + */ + overlay(cw, hw); + /* + * And set up for display + */ + overwrite(hw, cw); + when S_GFIND: + /* + * Potion of gold detection + */ + { + int gtotal = 0; + + wclear(hw); + for (i = 0; i < MAXROOMS; i++) + { + gtotal += rooms[i].r_goldval; + if (rooms[i].r_goldval != 0 && + mvwinch(stdscr, rooms[i].r_gold.y, rooms[i].r_gold.x) + == GOLD) + mvwaddch(hw,rooms[i].r_gold.y,rooms[i].r_gold.x,GOLD); + } + if (gtotal) + { + s_know[S_GFIND] = TRUE; + show_win(hw, + "You begin to feel greedy and you sense gold.--More--"); + } + else msg("You begin to feel a pull downward"); + } + when S_TELEP: + /* + * Scroll of teleportation: + * Make him dissapear and reappear + */ + { + int rm; + struct room *cur_room; + + cur_room = roomin(&hero); + rm = teleport(); + if (cur_room != &rooms[rm]) + s_know[S_TELEP] = TRUE; + } + when S_ENCH: + if (cur_weapon == NULL) + msg("You feel a strange sense of loss."); + else + { + cur_weapon->o_flags &= ~ISCURSED; + if (rnd(100) > 50) + cur_weapon->o_hplus++; + else + cur_weapon->o_dplus++; + msg("Your %s glows blue for a moment.", w_names[cur_weapon->o_which]); + } + when S_SCARE: + /* + * A monster will refuse to step on a scare monster scroll + * if it is dropped. Thus reading it is a mistake and produces + * laughter at the poor rogue's boo boo. + */ + msg("You hear maniacal laughter in the distance."); + when S_REMOVE: + if (cur_armor != NULL) + cur_armor->o_flags &= ~ISCURSED; + if (cur_weapon != NULL) + cur_weapon->o_flags &= ~ISCURSED; + if (cur_ring[LEFT] != NULL) + cur_ring[LEFT]->o_flags &= ~ISCURSED; + if (cur_ring[RIGHT] != NULL) + cur_ring[RIGHT]->o_flags &= ~ISCURSED; + msg("You feel as if somebody is watching over you."); + when S_AGGR: + /* + * This scroll aggravates all the monsters on the current + * level and sets them running towards the hero + */ + aggravate(); + msg("You hear a high pitched humming noise."); + when S_NOP: + msg("This scroll seems to be blank."); + when S_GENOCIDE: + msg("You have been granted the boon of genocide"); + genocide(); + s_know[S_GENOCIDE] = TRUE; + otherwise: + msg("What a puzzling scroll!"); + return; + } + look(TRUE); /* put the result of the scroll on the screen */ + status(); + if (s_know[obj->o_which] && s_guess[obj->o_which]) + { + free(s_guess[obj->o_which]); + s_guess[obj->o_which] = NULL; + } + else if (!s_know[obj->o_which] && askme && s_guess[obj->o_which] == NULL) + { + msg(terse ? "Call it: " : "What do you want to call it? "); + if (get_str(buf, cw) == NORM) + { + s_guess[obj->o_which] = malloc((unsigned int) strlen(buf) + 1); + if (s_guess[obj->o_which] != NULL) + strcpy(s_guess[obj->o_which], buf); + } + } + /* + * Get rid of the thing + */ + inpack--; + if (obj->o_count > 1) + obj->o_count--; + else + { + detach(pack, item); + discard(item); + } +} diff --git a/state.c b/state.c new file mode 100644 index 0000000..3006337 --- /dev/null +++ b/state.c @@ -0,0 +1,2079 @@ +/* + state.c - Portable Rogue Save State Code + + Copyright (C) 1999, 2000, 2005, 2006 Nicholas J. Kisseberth + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +/************************************************************************/ +/* Save State Code */ +/************************************************************************/ + +#define RSID_STATS 0xABCD0001 +#define RSID_THING 0xABCD0002 +#define RSID_THING_NULL 0xDEAD0002 +#define RSID_OBJECT 0xABCD0003 +#define RSID_MAGICITEMS 0xABCD0004 +#define RSID_KNOWS 0xABCD0005 +#define RSID_GUESSES 0xABCD0006 +#define RSID_OBJECTLIST 0xABCD0007 +#define RSID_BAGOBJECT 0xABCD0008 +#define RSID_MONSTERLIST 0xABCD0009 +#define RSID_MONSTERSTATS 0xABCD000A +#define RSID_MONSTERS 0xABCD000B +#define RSID_TRAP 0xABCD000C +#define RSID_WINDOW 0xABCD000D +#define RSID_DAEMONS 0xABCD000E +#define RSID_IWEAPS 0xABCD000F +#define RSID_IARMOR 0xABCD0010 +#define RSID_SPELLS 0xABCD0011 +#define RSID_ILIST 0xABCD0012 +#define RSID_HLIST 0xABCD0013 +#define RSID_DEATHTYPE 0xABCD0014 +#define RSID_CTYPES 0XABCD0015 +#define RSID_COORDLIST 0XABCD0016 +#define RSID_ROOMS 0XABCD0017 + +#include +#include +#include +#include +#include "rogue.h" + +#define READSTAT (format_error || read_error ) +#define WRITESTAT (write_error) + +static int read_error = FALSE; +static int write_error = FALSE; +static int format_error = FALSE; +static int endian = 0x01020304; +#define big_endian ( *((char *)&endian) == 0x01 ) + +int +rs_write(FILE *savef, void *ptr, size_t size) +{ + if (write_error) + return(WRITESTAT); + + if (encwrite(ptr, size, savef) != size) + write_error = 1; + + return(WRITESTAT); +} + +int +rs_read(int inf, void *ptr, size_t size) +{ + if (read_error || format_error) + return(READSTAT); + + if (encread(ptr, size, inf) != size) + read_error = 1; + + return(READSTAT); +} + +int +rs_write_char(FILE *savef, char c) +{ + if (write_error) + return(WRITESTAT); + + rs_write(savef, &c, 1); + + return(WRITESTAT); +} + +int +rs_read_char(int inf, char *c) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, c, 1); + + return(READSTAT); +} + +int +rs_write_chars(FILE *savef, char *c, int count) +{ + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + rs_write(savef, c, count); + + return(WRITESTAT); +} + +int +rs_read_chars(int inf, char *i, int count) +{ + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &value); + + if (value != count) + format_error = TRUE; + + rs_read(inf, i, count); + + return(READSTAT); +} + +int +rs_write_int(FILE *savef, int c) +{ + unsigned char bytes[4]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + rs_write(savef, buf, 4); + + return(WRITESTAT); +} + +int +rs_read_int(int inf, int *i) +{ + unsigned char bytes[4]; + int input = 0; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 4); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + *i = *((int *) buf); + + return(READSTAT); +} + +int +rs_write_ints(FILE *savef, int *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if( rs_write_int(savef,c[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_ints(int inf, int *i, int count) +{ + int n, value; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_int(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_boolean(FILE *savef, bool c) +{ + unsigned char buf = (c == 0) ? 0 : 1; + + if (write_error) + return(WRITESTAT); + + rs_write(savef, &buf, 1); + + return(WRITESTAT); +} + +int +rs_read_boolean(int inf, bool *i) +{ + unsigned char buf = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &buf, 1); + + *i = (buf != 0); + + return(READSTAT); +} + +int +rs_write_booleans(FILE *savef, bool *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if (rs_write_boolean(savef, c[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_booleans(int inf, bool *i, int count) +{ + int n = 0, value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_boolean(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_short(FILE *savef, short c) +{ + unsigned char bytes[2]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + rs_write(savef, buf, 2); + + return(WRITESTAT); +} + +int +rs_read_short(int inf, short *i) +{ + unsigned char bytes[2]; + short input; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 2); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + *i = *((short *) buf); + + return(READSTAT); +} + +int +rs_write_shorts(FILE *savef, short *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if (rs_write_short(savef, c[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_shorts(int inf, short *i, int count) +{ + int n = 0, value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < value; n++) + if (rs_read_short(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_ushort(FILE *savef, unsigned short c) +{ + unsigned char bytes[2]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + rs_write(savef, buf, 2); + + return(WRITESTAT); +} + +int +rs_read_ushort(int inf, unsigned short *i) +{ + unsigned char bytes[2]; + unsigned short input; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 2); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + *i = *((unsigned short *) buf); + + return(READSTAT); +} + +int +rs_write_uint(FILE *savef, unsigned int c) +{ + unsigned char bytes[4]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + rs_write(savef, buf, 4); + + return(WRITESTAT); +} + +int +rs_read_uint(int inf, unsigned int *i) +{ + unsigned char bytes[4]; + int input; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 4); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + *i = *((unsigned int *) buf); + + return(READSTAT); +} + +int +rs_write_long(FILE *savef, long c) +{ + int c2; + unsigned char bytes[4]; + unsigned char *buf = (unsigned char *)&c; + + if (write_error) + return(WRITESTAT); + + if (sizeof(long) == 8) + { + c2 = c; + buf = (unsigned char *) &c2; + } + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + rs_write(savef, buf, 4); + + return(WRITESTAT); +} + +int +rs_read_long(int inf, long *i) +{ + unsigned char bytes[4]; + long input; + unsigned char *buf = (unsigned char *) &input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 4); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + *i = *((long *) buf); + + return(READSTAT); +} + +int +rs_write_longs(FILE *savef, long *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef,count); + + for(n = 0; n < count; n++) + rs_write_long(savef, c[n]); + + return(WRITESTAT); +} + +int +rs_read_longs(int inf, long *i, int count) +{ + int n = 0, value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < value; n++) + if (rs_read_long(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_ulong(FILE *savef, unsigned long c) +{ + unsigned int c2; + unsigned char bytes[4]; + unsigned char *buf = (unsigned char *)&c; + + if (write_error) + return(WRITESTAT); + + if ( (sizeof(long) == 8) && (sizeof(int) == 4) ) + { + c2 = c; + buf = (unsigned char *) &c2; + } + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + rs_write(savef, buf, 4); + + return(WRITESTAT); +} + +int +rs_read_ulong(int inf, unsigned long *i) +{ + unsigned char bytes[4]; + unsigned long input; + unsigned char *buf = (unsigned char *) &input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 4); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + *i = *((unsigned long *) buf); + + return(READSTAT); +} + +int +rs_write_ulongs(FILE *savef, unsigned long *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef,count); + + for(n = 0; n < count; n++) + if (rs_write_ulong(savef,c[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_ulongs(int inf, unsigned long *i, int count) +{ + int n = 0, value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_ulong(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_marker(FILE *savef, int id) +{ + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, id); + + return(WRITESTAT); +} + +int +rs_read_marker(int inf, int id) +{ + int nid; + + if (read_error || format_error) + return(READSTAT); + + if (rs_read_int(inf, &nid) == 0) + if (id != nid) + format_error = 1; + + return(READSTAT); +} + + + +/******************************************************************************/ + +int +rs_write_string(FILE *savef, char *s) +{ + int len = 0; + + if (write_error) + return(WRITESTAT); + + len = (s == NULL) ? 0 : (int) strlen(s) + 1; + + rs_write_int(savef, len); + rs_write_chars(savef, s, len); + + return(WRITESTAT); +} + +int +rs_read_string(int inf, char *s, int max) +{ + int len = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &len); + + if (len > max) + format_error = TRUE; + + rs_read_chars(inf, s, len); + + return(READSTAT); +} + +int +rs_read_new_string(int inf, char **s) +{ + int len=0; + char *buf=0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &len); + + if (len == 0) + buf = NULL; + else + { + buf = malloc(len); + + if (buf == NULL) + read_error = TRUE; + } + + rs_read_chars(inf, buf, len); + + *s = buf; + + return(READSTAT); +} + +int +rs_write_strings(FILE *savef, char *s[], int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if (rs_write_string(savef, s[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_strings(int inf, char **s, int count, int max) +{ + int n = 0; + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_string(inf, s[n], max) != 0) + break; + + return(READSTAT); +} + +int +rs_read_new_strings(int inf, char **s, int count) +{ + int n = 0; + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_new_string(inf, &s[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_string_index(FILE *savef, char *master[], int max, const char *str) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < max; i++) + if (str == master[i]) + return( rs_write_int(savef, i) ); + + return( rs_write_int(savef,-1) ); +} + +int +rs_read_string_index(int inf, char *master[], int maxindex, char **str) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &i); + + if (i > maxindex) + format_error = TRUE; + else if (i >= 0) + *str = master[i]; + else + *str = NULL; + + return(READSTAT); +} + +int +rs_write_str_t(FILE *savef, str_t st) +{ + if (write_error) + return(WRITESTAT); + + rs_write_short(savef,st.st_str); + rs_write_short(savef,st.st_add); + + return(WRITESTAT); +} + +int +rs_read_str_t(int inf, str_t *st) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_short(inf,&st->st_str); + rs_read_short(inf,&st->st_add); + + return(READSTAT); +} + +int +rs_write_coord(FILE *savef, coord c) +{ + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, c.x); + rs_write_int(savef, c.y); + + return(WRITESTAT); +} + +int +rs_read_coord(int inf, coord *c) +{ + coord in; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&in.x); + rs_read_int(inf,&in.y); + + if (READSTAT == 0) + { + c->x = in.x; + c->y = in.y; + } + + return(READSTAT); +} + +int +rs_write_window(FILE *savef, WINDOW *win) +{ + int row,col,height,width; + + if (write_error) + return(WRITESTAT); + + width = getmaxx(win); + height = getmaxy(win); + + rs_write_marker(savef,RSID_WINDOW); + rs_write_int(savef,height); + rs_write_int(savef,width); + + for(row=0;rowl_next) + if (count == i) + return(l->l_data); + + return(NULL); +} + +int +find_list_ptr(struct linked_list *l, void *ptr) +{ + int count; + + for(count = 0; l != NULL; count++, l = l->l_next) + if (l->l_data == ptr) + return(count); + + return(-1); +} + +int +list_size(struct linked_list *l) +{ + int count; + + for(count = 0; l != NULL; count++, l = l->l_next) + if (l->l_data == NULL) + return(count); + + return(count); +} + +/******************************************************************************/ + +int +rs_write_stats(FILE *savef, struct stats *s) +{ + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_STATS); + rs_write_str_t(savef, s->s_str); + rs_write_long(savef, s->s_exp); + rs_write_int(savef, s->s_lvl); + rs_write_int(savef, s->s_arm); + rs_write_int(savef, s->s_hpt); + rs_write_chars(savef, s->s_dmg, sizeof(s->s_dmg)); + + return(WRITESTAT); +} + +int +rs_read_stats(int inf, struct stats *s) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_STATS); + rs_read_str_t(inf,&s->s_str); + rs_read_long(inf,&s->s_exp); + rs_read_int(inf,&s->s_lvl); + rs_read_int(inf,&s->s_arm); + rs_read_int(inf,&s->s_hpt); + rs_read_chars(inf,s->s_dmg,sizeof(s->s_dmg)); + + return(READSTAT); +} + +int +rs_write_scrolls(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < MAXSCROLLS; i++) + { + rs_write_string(savef,s_names[i]); + rs_write_boolean(savef,s_know[i]); + rs_write_string(savef,s_guess[i]); + } + return(READSTAT); +} + +int +rs_read_scrolls(int inf) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXSCROLLS; i++) + { + rs_read_new_string(inf,&s_names[i]); + rs_read_boolean(inf,&s_know[i]); + rs_read_new_string(inf,&s_guess[i]); + } + + return(READSTAT); +} + +int +rs_write_potions(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < MAXPOTIONS; i++) + { + rs_write_string_index(savef, rainbow, cNCOLORS, p_colors[i]); + rs_write_boolean(savef,p_know[i]); + rs_write_string(savef,p_guess[i]); + } + + return(WRITESTAT); +} + +int +rs_read_potions(int inf) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXPOTIONS; i++) + { + rs_read_string_index(inf, rainbow, cNCOLORS, &p_colors[i]); + rs_read_boolean(inf,&p_know[i]); + rs_read_new_string(inf,&p_guess[i]); + } + + return(READSTAT); +} + +int +rs_write_rings(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < MAXRINGS; i++) + { + rs_write_string_index(savef, stones, cNSTONES, r_stones[i]); + rs_write_boolean(savef,r_know[i]); + rs_write_string(savef,r_guess[i]); + } + + return(WRITESTAT); +} + +int +rs_read_rings(int inf) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXRINGS; i++) + { + rs_read_string_index(inf, stones, cNSTONES, &r_stones[i]); + rs_read_boolean(inf,&r_know[i]); + rs_read_new_string(inf,&r_guess[i]); + } + + return(READSTAT); +} + +int +rs_write_sticks(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for (i = 0; i < MAXSTICKS; i++) + { + if (strcmp(ws_type[i],"staff") == 0) + { + rs_write_int(savef,0); + rs_write_string_index(savef, wood, cNWOOD, ws_made[i]); + } + else + { + rs_write_int(savef,1); + rs_write_string_index(savef, metal, cNMETAL, ws_made[i]); + } + rs_write_boolean(savef, ws_know[i]); + rs_write_string(savef, ws_guess[i]); + } + + return(WRITESTAT); +} + +int +rs_read_sticks(int inf) +{ + int i = 0, list = 0; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXSTICKS; i++) + { + rs_read_int(inf,&list); + + if (list == 0) + { + rs_read_string_index(inf, wood, cNWOOD, &ws_made[i]); + ws_type[i] = "staff"; + } + else + { + rs_read_string_index(inf, metal, cNMETAL, &ws_made[i]); + ws_type[i] = "wand"; + } + rs_read_boolean(inf, &ws_know[i]); + rs_read_new_string(inf, &ws_guess[i]); + } + + return(READSTAT); +} + +int +rs_write_daemons(FILE *savef, struct delayed_action *d_list, int count) +{ + int i = 0; + int func = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_DAEMONS); + rs_write_int(savef, count); + + for(i = 0; i < count; i++) + { + if (d_list[i].d_func == rollwand) + func = 1; + else if (d_list[i].d_func == doctor) + func = 2; + else if (d_list[i].d_func == stomach) + func = 3; + else if (d_list[i].d_func == runners) + func = 4; + else if (d_list[i].d_func == swander) + func = 5; + else if (d_list[i].d_func == nohaste) + func = 6; + else if (d_list[i].d_func == unconfuse) + func = 7; + else if (d_list[i].d_func == unsee) + func = 8; + else if (d_list[i].d_func == sight) + func = 9; + else if (d_list[i].d_func == NULL) + func = 0; + else + func = -1; + + rs_write_int(savef, d_list[i].d_type); + rs_write_int(savef, func); + rs_write_int(savef, d_list[i].d_arg); + rs_write_int(savef, d_list[i].d_time); + } + + return(WRITESTAT); +} + +int +rs_read_daemons(int inf, struct delayed_action *d_list, int count) +{ + int i = 0; + int func = 0; + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_DAEMONS); + rs_read_int(inf, &value); + + if (value != count) + format_error = TRUE; + + for(i=0; i < count; i++) + { + func = 0; + rs_read_int(inf, &d_list[i].d_type); + rs_read_int(inf, &func); + rs_read_int(inf, &d_list[i].d_arg); + rs_read_int(inf, &d_list[i].d_time); + + switch(func) + { + case 1: d_list[i].d_func = rollwand; + break; + case 2: d_list[i].d_func = doctor; + break; + case 3: d_list[i].d_func = stomach; + break; + case 4: d_list[i].d_func = runners; + break; + case 5: d_list[i].d_func = swander; + break; + case 6: d_list[i].d_func = nohaste; + break; + case 7: d_list[i].d_func = unconfuse; + break; + case 8: d_list[i].d_func = unsee; + break; + case 9: d_list[i].d_func = sight; + break; + default:d_list[i].d_func = NULL; + break; + } + } + + if (d_list[i].d_func == NULL) + { + d_list[i].d_type = 0; + d_list[i].d_arg = 0; + d_list[i].d_time = 0; + } + + return(READSTAT); +} + +int +rs_write_trap(FILE *savef, struct trap *trap) +{ + if (write_error) + return(WRITESTAT); + + rs_write_coord(savef, trap->tr_pos); + rs_write_char(savef, trap->tr_type); + rs_write_int(savef, trap->tr_flags); + + return(WRITESTAT); +} + +int +rs_read_trap(int inf, struct trap *trap) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_coord(inf,&trap->tr_pos); + rs_read_char(inf,&trap->tr_type); + rs_read_int(inf,&trap->tr_flags); + + return(READSTAT); +} + +int +rs_write_traps(FILE *savef, struct trap t[], int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_MONSTERS); + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + rs_write_trap(savef, &t[n]); + + return(WRITESTAT); +} + +int +rs_read_traps(int inf, struct trap *t, int count) +{ + int value = 0, n = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_MONSTERS); + + rs_read_int(inf,&value); + + if (value > count) + format_error = TRUE; + + for(n = 0; n < value; n++) + rs_read_trap(inf,&t[n]); + + return(READSTAT); +} + +int +rs_write_room(FILE *savef, struct room *r) +{ + if (write_error) + return(WRITESTAT); + + rs_write_coord(savef, r->r_pos); + rs_write_coord(savef, r->r_max); + rs_write_coord(savef, r->r_gold); + rs_write_int(savef, r->r_goldval); + rs_write_int(savef, r->r_flags); + rs_write_int(savef, r->r_nexits); + rs_write_coord(savef, r->r_exit[0]); + rs_write_coord(savef, r->r_exit[1]); + rs_write_coord(savef, r->r_exit[2]); + rs_write_coord(savef, r->r_exit[3]); + + return(WRITESTAT); +} + +int +rs_read_room(int inf, struct room *r) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_coord(inf,&r->r_pos); + rs_read_coord(inf,&r->r_max); + rs_read_coord(inf,&r->r_gold); + rs_read_int(inf,&r->r_goldval); + rs_read_int(inf,&r->r_flags); + rs_read_int(inf,&r->r_nexits); + rs_read_coord(inf,&r->r_exit[0]); + rs_read_coord(inf,&r->r_exit[1]); + rs_read_coord(inf,&r->r_exit[2]); + rs_read_coord(inf,&r->r_exit[3]); + + return(READSTAT); +} + +int +rs_write_rooms(FILE *savef, struct room r[], int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + rs_write_room(savef, &r[n]); + + return(WRITESTAT); +} + +int +rs_read_rooms(int inf, struct room *r, int count) +{ + int value = 0, n = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value > count) + format_error = TRUE; + + for(n = 0; n < value; n++) + rs_read_room(inf,&r[n]); + + return(READSTAT); +} + +int +rs_write_room_reference(FILE *savef, struct room *rp) +{ + int i, room = -1; + + if (write_error) + return(WRITESTAT); + + for (i = 0; i < MAXROOMS; i++) + if (&rooms[i] == rp) + room = i; + + rs_write_int(savef, room); + + return(WRITESTAT); +} + +int +rs_read_room_reference(int inf, struct room **rp) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &i); + + *rp = &rooms[i]; + + return(READSTAT); +} + +int +rs_write_object(FILE *savef, struct object *o) +{ + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_OBJECT); + rs_write_int(savef, o->o_type); + rs_write_coord(savef, o->o_pos); + rs_write_char(savef, o->o_launch); + rs_write_chars(savef, o->o_damage, sizeof(o->o_damage)); + rs_write_chars(savef, o->o_hurldmg, sizeof(o->o_damage)); + rs_write_int(savef, o->o_count); + rs_write_int(savef, o->o_which); + rs_write_int(savef, o->o_hplus); + rs_write_int(savef, o->o_dplus); + rs_write_int(savef, o->o_ac); + rs_write_int(savef, o->o_flags); + rs_write_int(savef, o->o_group); + return(WRITESTAT); +} + +int +rs_read_object(int inf, struct object *o) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_OBJECT); + rs_read_int(inf, &o->o_type); + rs_read_coord(inf, &o->o_pos); + rs_read_char(inf, &o->o_launch); + rs_read_chars(inf, o->o_damage, sizeof(o->o_damage)); + rs_read_chars(inf, o->o_hurldmg, sizeof(o->o_hurldmg)); + rs_read_int(inf, &o->o_count); + rs_read_int(inf, &o->o_which); + rs_read_int(inf, &o->o_hplus); + rs_read_int(inf, &o->o_hplus); + rs_read_int(inf,&o->o_ac); + rs_read_int(inf,&o->o_flags); + rs_read_int(inf,&o->o_group); + + return(READSTAT); +} + +int +rs_write_object_list(FILE *savef, struct linked_list *l) +{ + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_OBJECTLIST); + rs_write_int(savef, list_size(l)); + + for( ;l != NULL; l = l->l_next) + rs_write_object(savef, (struct object *) l->l_data); + + return(WRITESTAT); +} + +int +rs_read_object_list(int inf, struct linked_list **list) +{ + int i, cnt; + struct linked_list *l = NULL, *previous = NULL, *head = NULL; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_OBJECTLIST); + rs_read_int(inf, &cnt); + + for (i = 0; i < cnt; i++) + { + l = new_item(sizeof(struct object)); + + memset(l->l_data,0,sizeof(struct object)); + + l->l_prev = previous; + + if (previous != NULL) + previous->l_next = l; + + rs_read_object(inf,(struct object *) l->l_data); + + if (previous == NULL) + head = l; + + previous = l; + } + + if (l != NULL) + l->l_next = NULL; + + *list = head; + + return(READSTAT); +} + +int +rs_write_object_reference(FILE *savef, struct linked_list *list, + struct object *item) +{ + int i; + + if (write_error) + return(WRITESTAT); + + i = find_list_ptr(list, item); + + rs_write_int(savef, i); + + return(WRITESTAT); +} + +int +rs_read_object_reference(int inf, struct linked_list *list, + struct object **item) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &i); + + *item = get_list_item(list,i); + + return(READSTAT); +} + +int +find_room_coord(struct room *rmlist, coord *c, int n) +{ + int i = 0; + + for(i = 0; i < n; i++) + if(&rmlist[i].r_gold == c) + return(i); + + return(-1); +} + +int +find_thing_coord(struct linked_list *monlist, coord *c) +{ + struct linked_list *mitem; + struct thing *tp; + int i = 0; + + for(mitem = monlist; mitem != NULL; mitem = mitem->l_next) + { + tp = THINGPTR(mitem); + + if (c == &tp->t_pos) + return(i); + + i++; + } + + return(-1); +} + +int +find_object_coord(struct linked_list *objlist, coord *c) +{ + struct linked_list *oitem; + struct object *obj; + int i = 0; + + for(oitem = objlist; oitem != NULL; oitem = oitem->l_next) + { + obj = OBJPTR(oitem); + + if (c == &obj->o_pos) + return(i); + + i++; + } + + return(-1); +} + +int +rs_write_thing(FILE *savef, struct thing *t) +{ + int i = -1; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_THING); + + if (t == NULL) + { + rs_write_int(savef, 0); + return(WRITESTAT); + } + + rs_write_int(savef, 1); + rs_write_coord(savef, t->t_pos); + rs_write_boolean(savef, t->t_turn); + rs_write_char(savef, t->t_type); + rs_write_char(savef, t->t_disguise); + rs_write_char(savef, t->t_oldch); + + /* + t_dest can be: + 0,0: NULL + 0,1: location of hero + 1,i: location of a thing (monster) + 2,i: location of an object + 3,i: location of gold in a room + + We need to remember what we are chasing rather than + the current location of what we are chasing. + */ + + if (t->t_dest == &hero) + { + rs_write_int(savef,0); + rs_write_int(savef,1); + } + else if (t->t_dest != NULL) + { + i = find_thing_coord(mlist, t->t_dest); + + if (i >=0 ) + { + rs_write_int(savef,1); + rs_write_int(savef,i); + } + else + { + i = find_object_coord(lvl_obj, t->t_dest); + + if (i >= 0) + { + rs_write_int(savef,2); + rs_write_int(savef,i); + } + else + { + i = find_room_coord(rooms, t->t_dest, MAXROOMS); + + if (i >= 0) + { + rs_write_int(savef,3); + rs_write_int(savef,i); + } + else + { + rs_write_int(savef, 0); + rs_write_int(savef,1); /* chase the hero anyway */ + } + } + } + } + else + { + rs_write_int(savef,0); + rs_write_int(savef,0); + } + + rs_write_short(savef, t->t_flags); + rs_write_stats(savef, &t->t_stats); + rs_write_object_list(savef, t->t_pack); + + return(WRITESTAT); +} + +int +rs_read_thing(int inf, struct thing *t) +{ + int listid = 0, index = -1; + struct linked_list *item; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_THING); + + rs_read_int(inf, &index); + + if (index == 0) + return(READSTAT); + + rs_read_coord(inf,&t->t_pos); + rs_read_boolean(inf,&t->t_turn); + rs_read_char(inf,&t->t_type); + rs_read_char(inf,&t->t_disguise); + rs_read_char(inf,&t->t_oldch); + + /* + t_dest can be (listid,index): + 0,0: NULL + 0,1: location of hero + 1,i: location of a thing (monster) + 2,i: location of an object + 3,i: location of gold in a room + + We need to remember what we are chasing rather than + the current location of what we are chasing. + */ + + rs_read_int(inf, &listid); + rs_read_int(inf, &index); + t->t_reserved = -1; + + if (listid == 0) /* hero or NULL */ + { + if (index == 1) + t->t_dest = &hero; + else + t->t_dest = NULL; + } + else if (listid == 1) /* monster/thing */ + { + t->t_dest = NULL; + t->t_reserved = index; + } + else if (listid == 2) /* object */ + { + struct object *obj; + + item = get_list_item(lvl_obj,index); + + if (item != NULL) + { + obj = OBJPTR(item); + t->t_dest = &obj->o_pos; + } + } + else if (listid == 3) /* gold */ + { + t->t_dest = &rooms[index].r_gold; + } + else + t->t_dest = NULL; + + rs_read_short(inf,&t->t_flags); + rs_read_stats(inf,&t->t_stats); + rs_read_object_list(inf,&t->t_pack); + + return(READSTAT); +} + +int +rs_fix_thing(struct thing *t) +{ + struct linked_list *item; + struct thing *tp; + + if (t->t_reserved < 0) + return; + + item = get_list_item(mlist,t->t_reserved); + + if (item != NULL) + { + tp = THINGPTR(item); + t->t_dest = &tp->t_pos; + } +} + +int +rs_write_thing_list(FILE *savef, struct linked_list *l) +{ + int cnt = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_MONSTERLIST); + + cnt = list_size(l); + + rs_write_int(savef, cnt); + + if (cnt < 1) + return(WRITESTAT); + + while (l != NULL) { + rs_write_thing(savef, (struct thing *)l->l_data); + l = l->l_next; + } + + return(WRITESTAT); +} + +int +rs_read_thing_list(int inf, struct linked_list **list) +{ + int i, cnt; + struct linked_list *l = NULL, *previous = NULL, *head = NULL; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_MONSTERLIST); + + rs_read_int(inf, &cnt); + + for (i = 0; i < cnt; i++) + { + l = new_item(sizeof(struct thing)); + + l->l_prev = previous; + + if (previous != NULL) + previous->l_next = l; + + rs_read_thing(inf,(struct thing *)l->l_data); + + if (previous == NULL) + head = l; + + previous = l; + } + + if (l != NULL) + l->l_next = NULL; + + *list = head; + + return(READSTAT); +} + +int +rs_fix_thing_list(struct linked_list *list) +{ + struct linked_list *item; + + for(item = list; item != NULL; item = item->l_next) + rs_fix_thing(THINGPTR(item)); +} + +int +rs_fix_magic_items(struct magic_item *mi, int count) +{ + int i; + + for (i = 0; i < count; i++) + if (i > 0) + mi[i].mi_prob += mi[i-1].mi_prob; +} + +int +rs_fix_monsters(struct monster monsters[26]) +{ + sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dd1",fung_hit); +} + +int +rs_save_file(FILE *savef) +{ + if (write_error) + return(WRITESTAT); + + rs_write_thing(savef, &player); + rs_write_object_list(savef, lvl_obj); + rs_write_thing_list(savef, mlist); + rs_write_traps(savef, traps, MAXTRAPS); + rs_write_rooms(savef, rooms, MAXROOMS); + rs_write_room_reference(savef, oldrp); + rs_write_stats(savef,&max_stats); + rs_write_object_reference(savef, player.t_pack, cur_weapon); + rs_write_object_reference(savef, player.t_pack, cur_armor); + rs_write_object_reference(savef, player.t_pack, cur_ring[0]); + rs_write_object_reference(savef, player.t_pack, cur_ring[1]); + rs_write_int(savef, level); + rs_write_int(savef, purse); + rs_write_int(savef, mpos); + rs_write_int(savef, ntraps); + rs_write_int(savef, no_move); + rs_write_int(savef, no_command); + rs_write_int(savef, inpack); + rs_write_int(savef, max_hp); + rs_write_int(savef, total); + rs_write_int(savef, lastscore); + rs_write_int(savef, no_food); + rs_write_int(savef, seed); + rs_write_int(savef, count); + rs_write_int(savef, dnum); + rs_write_int(savef, fung_hit); + rs_write_int(savef, quiet); + rs_write_int(savef, max_level); + rs_write_int(savef, food_left); + rs_write_int(savef, group); + rs_write_int(savef, hungry_state); + rs_write_char(savef, take); + rs_write_char(savef, runch); + rs_write_scrolls(savef); + rs_write_potions(savef); + rs_write_rings(savef); + rs_write_sticks(savef); + rs_write_chars(savef,whoami,80); + rs_write_chars(savef,fruit,80); + rs_write_window(savef, cw); + rs_write_window(savef, mw); + rs_write_window(savef, stdscr); + rs_write_boolean(savef, running); + rs_write_boolean(savef, playing); + rs_write_boolean(savef, wizard); + rs_write_boolean(savef, after); + rs_write_boolean(savef, notify); + rs_write_boolean(savef, fight_flush); + rs_write_boolean(savef, terse); + rs_write_boolean(savef, door_stop); + rs_write_boolean(savef, jump); + rs_write_boolean(savef, slow_invent); + rs_write_boolean(savef, firstmove); + rs_write_boolean(savef, waswizard); + rs_write_boolean(savef, askme); + rs_write_boolean(savef, amulet); + rs_write_boolean(savef, in_shell); + rs_write_coord(savef, oldpos); + rs_write_coord(savef, delta); + rs_write_coord(savef, ch_ret); /* chase.c */ + rs_write_daemons(savef, &d_list[0], 20); /* daemon.c */ + rs_write_int(savef,between); /* daemons.c */ + rs_write_int(savef,num_checks); /* main.c */ + rs_write_chars(savef,lvl_mons,sizeof(lvl_mons)); /* monsters.c */ + rs_write_chars(savef,wand_mons,sizeof(wand_mons)); /* monsters.c */ + + return(WRITESTAT); +} + +int +rs_restore_file(int inf) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_thing(inf, &player); + rs_read_object_list(inf, &lvl_obj); + rs_read_thing_list(inf, &mlist); + rs_fix_thing(&player); + rs_fix_thing_list(mlist); + rs_read_traps(inf, traps, MAXTRAPS); + rs_read_rooms(inf, rooms, MAXROOMS); + rs_read_room_reference(inf, &oldrp); + rs_read_stats(inf,&max_stats); + rs_read_object_reference(inf, player.t_pack, &cur_weapon); + rs_read_object_reference(inf, player.t_pack, &cur_armor); + rs_read_object_reference(inf, player.t_pack, &cur_ring[0]); + rs_read_object_reference(inf, player.t_pack, &cur_ring[1]); + rs_fix_magic_items(things,NUMTHINGS); + rs_fix_magic_items(s_magic,MAXSCROLLS); + rs_fix_magic_items(p_magic,MAXPOTIONS); + rs_fix_magic_items(r_magic,MAXRINGS); + rs_fix_magic_items(ws_magic,MAXSTICKS); + rs_read_int(inf, &level); + rs_read_int(inf, &purse); + rs_read_int(inf, &mpos); + rs_read_int(inf, &ntraps); + rs_read_int(inf, &no_move); + rs_read_int(inf, &no_command); + rs_read_int(inf, &inpack); + rs_read_int(inf, &max_hp); + rs_read_int(inf, &total); + rs_read_int(inf, &lastscore); + rs_read_int(inf, &no_food); + rs_read_int(inf, &seed); + rs_read_int(inf, &count); + rs_read_int(inf, &dnum); + rs_read_int(inf, &fung_hit); + rs_read_int(inf, &quiet); + rs_read_int(inf, &max_level); + rs_read_int(inf, &food_left); + rs_read_int(inf, &group); + rs_read_int(inf, &hungry_state); + rs_read_char(inf, &take); + rs_read_char(inf, &runch); + rs_read_scrolls(inf); + rs_read_potions(inf); + rs_read_rings(inf); + rs_read_sticks(inf); + rs_read_chars(inf,whoami,80); + rs_read_chars(inf,fruit,80); + rs_read_window(inf, cw); + rs_read_window(inf, mw); + rs_read_window(inf, stdscr); + rs_read_boolean(inf, &running); + rs_read_boolean(inf, &playing); + rs_read_boolean(inf, &wizard); + rs_read_boolean(inf, &after); + rs_read_boolean(inf, ¬ify); + rs_read_boolean(inf, &fight_flush); + rs_read_boolean(inf, &terse); + rs_read_boolean(inf, &door_stop); + rs_read_boolean(inf, &jump); + rs_read_boolean(inf, &slow_invent); + rs_read_boolean(inf, &firstmove); + rs_read_boolean(inf, &waswizard); + rs_read_boolean(inf, &askme); + rs_read_boolean(inf, &amulet); + rs_read_boolean(inf, &in_shell); + rs_read_coord(inf,&oldpos); + rs_read_coord(inf,&delta); + rs_read_coord(inf, &ch_ret); /* chase.c */ + rs_read_daemons(inf, d_list, 20); /* daemon.c */ + rs_read_int(inf,&between); /* daemons.c */ + rs_read_int(inf,&num_checks); /* main.c */ + rs_read_chars(inf, lvl_mons, sizeof(lvl_mons)); /* monsters.c */ + rs_read_chars(inf, wand_mons, sizeof(wand_mons)); /* monsters.c */ + rs_fix_monsters(monsters); + return(READSTAT); +} diff --git a/sticks.c b/sticks.c new file mode 100644 index 0000000..262dce1 --- /dev/null +++ b/sticks.c @@ -0,0 +1,398 @@ +/* + * Functions to implement the various sticks one might find + * while wandering around the dungeon. + * + * @(#)sticks.c 3.14 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +fix_stick(cur) +register struct object *cur; +{ + if (strcmp(ws_type[cur->o_which], "staff") == 0) + strcpy(cur->o_damage,"2d3"); + else + strcpy(cur->o_damage,"1d1"); + strcpy(cur->o_hurldmg,"1d1"); + + cur->o_charges = 3 + rnd(5); + switch (cur->o_which) + { + case WS_HIT: + cur->o_hplus = 3; + cur->o_dplus = 3; + strcpy(cur->o_damage,"1d8"); + when WS_LIGHT: + cur->o_charges = 10 + rnd(10); + } +} + +do_zap(gotdir) +bool gotdir; +{ + register struct linked_list *item; + register struct object *obj; + register struct room *rp; + register struct thing *tp; + register int y, x; + + if ((item = get_item("zap with", STICK)) == NULL) + return; + obj = (struct object *) ldata(item); + if (obj->o_type != STICK) + { + msg("You can't zap with that!"); + after = FALSE; + return; + } + if (obj->o_charges == 0) + { + msg("Nothing happens."); + return; + } + if (!gotdir) + do { + delta.y = rnd(3) - 1; + delta.x = rnd(3) - 1; + } while (delta.y == 0 && delta.x == 0); + switch (obj->o_which) + { + case WS_LIGHT: + /* + * Reddy Kilowat wand. Light up the room + */ + ws_know[WS_LIGHT] = TRUE; + if ((rp = roomin(&hero)) == NULL) + msg("The corridor glows and then fades"); + else + { + addmsg("The room is lit"); + if (!terse) + addmsg(" by a shimmering blue light."); + endmsg(); + rp->r_flags &= ~ISDARK; + /* + * Light the room and put the player back up + */ + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + when WS_DRAIN: + /* + * Take away 1/2 of hero's hit points, then take it away + * evenly from the monsters in the room (or next to hero + * if he is in a passage) + */ + if (pstats.s_hpt < 2) + { + msg("You are too weak to use it."); + return; + } + else if ((rp = roomin(&hero)) == NULL) + drain(hero.y-1, hero.y+1, hero.x-1, hero.x+1); + else + drain(rp->r_pos.y, rp->r_pos.y+rp->r_max.y, + rp->r_pos.x, rp->r_pos.x+rp->r_max.x); + when WS_POLYMORPH: + case WS_TELAWAY: + case WS_TELTO: + case WS_CANCEL: + { + register char monster, oldch; + register int rm; + + y = hero.y; + x = hero.x; + while (step_ok(winat(y, x))) + { + y += delta.y; + x += delta.x; + } + if (isupper(monster = mvwinch(mw, y, x))) + { + register char omonst = monster; + + if (monster == 'F') + player.t_flags &= ~ISHELD; + item = find_mons(y, x); + tp = (struct thing *) ldata(item); + if (obj->o_which == WS_POLYMORPH) + { + detach(mlist, item); + oldch = tp->t_oldch; + delta.y = y; + delta.x = x; + new_monster(item, monster = rnd(26) + 'A', &delta); + if (!(tp->t_flags & ISRUN)) + runto(&delta, &hero); + if (isupper(mvwinch(cw, y, x))) + mvwaddch(cw, y, x, monster); + tp->t_oldch = oldch; + ws_know[WS_POLYMORPH] |= (monster != omonst); + } + else if (obj->o_which == WS_CANCEL) + { + tp->t_flags |= ISCANC; + tp->t_flags &= ~ISINVIS; + } + else + { + if (obj->o_which == WS_TELAWAY) + { + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &tp->t_pos); + } until(winat(tp->t_pos.y, tp->t_pos.x) == FLOOR); + } + else + { + tp->t_pos.y = hero.y + delta.y; + tp->t_pos.x = hero.x + delta.x; + } + if (isupper(mvwinch(cw, y, x))) + mvwaddch(cw, y, x, tp->t_oldch); + tp->t_dest = &hero; + tp->t_flags |= ISRUN; + mvwaddch(mw, y, x, ' '); + mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, monster); + if (tp->t_pos.y != y || tp->t_pos.x != x) + tp->t_oldch = mvwinch(cw, tp->t_pos.y, tp->t_pos.x); + } + } + } + when WS_MISSILE: + { + static struct object bolt = + { + '*' , {0, 0}, 0, "", "1d4" , 0, 0, 100, 1, 0, 0, 0 + }; + + do_motion(&bolt, delta.y, delta.x); + if (isupper(mvwinch(mw, bolt.o_pos.y, bolt.o_pos.x)) + && !save_throw(VS_MAGIC, ldata(find_mons(unc(bolt.o_pos))))) + hit_monster(unc(bolt.o_pos), &bolt); + else if (terse) + msg("Missile vanishes"); + else + msg("The missile vanishes with a puff of smoke"); + ws_know[WS_MISSILE] = TRUE; + } + when WS_HIT: + { + register char ch; + + delta.y += hero.y; + delta.x += hero.x; + ch = winat(delta.y, delta.x); + if (isupper(ch)) + { + if (rnd(20) == 0) + { + strcpy(obj->o_damage,"3d8"); + obj->o_dplus = 9; + } + else + { + strcpy(obj->o_damage,"1d8"); + obj->o_dplus = 3; + } + fight(&delta, ch, obj, FALSE); + } + } + when WS_HASTE_M: + case WS_SLOW_M: + y = hero.y; + x = hero.x; + while (step_ok(winat(y, x))) + { + y += delta.y; + x += delta.x; + } + if (isupper(mvwinch(mw, y, x))) + { + item = find_mons(y, x); + tp = (struct thing *) ldata(item); + if (obj->o_which == WS_HASTE_M) + { + if (on(*tp, ISSLOW)) + tp->t_flags &= ~ISSLOW; + else + tp->t_flags |= ISHASTE; + } + else + { + if (on(*tp, ISHASTE)) + tp->t_flags &= ~ISHASTE; + else + tp->t_flags |= ISSLOW; + tp->t_turn = TRUE; + } + delta.y = y; + delta.x = x; + runto(&delta, &hero); + } + when WS_ELECT: + case WS_FIRE: + case WS_COLD: + { + register char dirch, ch, *name; + register bool bounced, used; + coord pos; + coord spotpos[BOLT_LENGTH]; + static struct object bolt = + { + '*' , {0, 0}, 0, "", "6d6" , 0, 0, 100, 0, 0, 0 ,0 + }; + + + switch (delta.y + delta.x) + { + case 0: dirch = '/'; + when 1: case -1: dirch = (delta.y == 0 ? '-' : '|'); + when 2: case -2: dirch = '\\'; + } + pos = hero; + bounced = FALSE; + used = FALSE; + if (obj->o_which == WS_ELECT) + name = "bolt"; + else if (obj->o_which == WS_FIRE) + name = "flame"; + else + name = "ice"; + for (y = 0; y < BOLT_LENGTH && !used; y++) + { + ch = winat(pos.y, pos.x); + spotpos[y] = pos; + switch (ch) + { + case DOOR: + case SECRETDOOR: + case '|': + case '-': + case ' ': + bounced = TRUE; + delta.y = -delta.y; + delta.x = -delta.x; + y--; + msg("The bolt bounces"); + break; + default: + if (!bounced && isupper(ch)) + { + if (!save_throw(VS_MAGIC, ldata(find_mons(unc(pos))))) + { + bolt.o_pos = pos; + hit_monster(unc(pos), &bolt); + used = TRUE; + } + else if (ch != 'M' || show(pos.y, pos.x) == 'M') + { + if (terse) + msg("%s misses", name); + else + msg("The %s whizzes past the %s", name, monsters[ch-'A'].m_name); + runto(&pos, &hero); + } + } + else if (bounced && pos.y == hero.y && pos.x == hero.x) + { + bounced = FALSE; + if (!save(VS_MAGIC)) + { + if (terse) + msg("The %s hits", name); + else + msg("You are hit by the %s", name); + if ((pstats.s_hpt -= roll(6, 6)) <= 0) + death('b'); + used = TRUE; + } + else + msg("The %s whizzes by you", name); + } + mvwaddch(cw, pos.y, pos.x, dirch); + draw(cw); + } + pos.y += delta.y; + pos.x += delta.x; + } + for (x = 0; x < y; x++) + mvwaddch(cw, spotpos[x].y, spotpos[x].x, show(spotpos[x].y, spotpos[x].x)); + ws_know[obj->o_which] = TRUE; + } + otherwise: + msg("What a bizarre schtick!"); + } + obj->o_charges--; +} + +/* + * drain: + * Do drain hit points from player shtick + */ + +drain(ymin, ymax, xmin, xmax) +int ymin, ymax, xmin, xmax; +{ + register int i, j, count; + register struct thing *ick; + register struct linked_list *item; + + /* + * First count how many things we need to spread the hit points among + */ + count = 0; + for (i = ymin; i <= ymax; i++) + for (j = xmin; j <= xmax; j++) + if (isupper(mvwinch(mw, i, j))) + count++; + if (count == 0) + { + msg("You have a tingling feeling"); + return; + } + count = pstats.s_hpt / count; + pstats.s_hpt /= 2; + /* + * Now zot all of the monsters + */ + for (i = ymin; i <= ymax; i++) + for (j = xmin; j <= xmax; j++) + if (isupper(mvwinch(mw, i, j)) && + ((item = find_mons(i, j)) != NULL)) + { + ick = (struct thing *) ldata(item); + if ((ick->t_stats.s_hpt -= count) < 1) + killed(item, cansee(i, j) && !on(*ick, ISINVIS)); + } +} + +/* + * charge a wand for wizards. + */ +char * +charge_str(obj) +register struct object *obj; +{ + static char buf[20]; + + if (!(obj->o_flags & ISKNOW)) + buf[0] = '\0'; + else if (terse) + sprintf(buf, " [%d]", obj->o_charges); + else + sprintf(buf, " [%d charges]", obj->o_charges); + return buf; +} diff --git a/things.c b/things.c new file mode 100644 index 0000000..ee08f26 --- /dev/null +++ b/things.c @@ -0,0 +1,382 @@ +/* + * Contains functions for dealing with things like + * potions and scrolls + * + * @(#)things.c 3.37 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +/* + * inv_name: + * return the name of something as it would appear in an + * inventory. + */ +char * +inv_name(obj, drop) +register struct object *obj; +register bool drop; +{ + register char *pb; + + switch(obj->o_type) + { + case SCROLL: + if (obj->o_count == 1) + strcpy(prbuf, "A scroll "); + else + sprintf(prbuf, "%d scrolls ", obj->o_count); + pb = &prbuf[strlen(prbuf)]; + if (s_know[obj->o_which]) + sprintf(pb, "of %s", s_magic[obj->o_which].mi_name); + else if (s_guess[obj->o_which]) + sprintf(pb, "called %s", s_guess[obj->o_which]); + else + sprintf(pb, "titled '%s'", s_names[obj->o_which]); + when POTION: + if (obj->o_count == 1) + strcpy(prbuf, "A potion "); + else + sprintf(prbuf, "%d potions ", obj->o_count); + pb = &prbuf[strlen(prbuf)]; + if (p_know[obj->o_which]) + sprintf(pb, "of %s(%s)", p_magic[obj->o_which].mi_name, + p_colors[obj->o_which]); + else if (p_guess[obj->o_which]) + sprintf(pb, "called %s(%s)", p_guess[obj->o_which], + p_colors[obj->o_which]); + else if (obj->o_count == 1) + sprintf(prbuf, "A%s %s potion", + vowelstr(p_colors[obj->o_which]), + p_colors[obj->o_which]); + else + sprintf(prbuf, "%d %s potions", obj->o_count, + p_colors[obj->o_which]); + when FOOD: + if (obj->o_which == 1) + if (obj->o_count == 1) + sprintf(prbuf, "A%s %s", vowelstr(fruit), fruit); + else + sprintf(prbuf, "%d %ss", obj->o_count, fruit); + else + if (obj->o_count == 1) + strcpy(prbuf, "Some food"); + else + sprintf(prbuf, "%d rations of food", obj->o_count); + when WEAPON: + if (obj->o_count > 1) + sprintf(prbuf, "%d ", obj->o_count); + else + strcpy(prbuf, "A "); + pb = &prbuf[strlen(prbuf)]; + if (obj->o_flags & ISKNOW) + sprintf(pb, "%s %s", num(obj->o_hplus, obj->o_dplus), + w_names[obj->o_which]); + else + sprintf(pb, "%s", w_names[obj->o_which]); + if (obj->o_count > 1) + strcat(prbuf, "s"); + when ARMOR: + if (obj->o_flags & ISKNOW) + sprintf(prbuf, "%s %s", + num(a_class[obj->o_which] - obj->o_ac, 0), + a_names[obj->o_which]); + else + sprintf(prbuf, "%s", a_names[obj->o_which]); + when AMULET: + strcpy(prbuf, "The Amulet of Yendor"); + when STICK: + sprintf(prbuf, "A %s ", ws_type[obj->o_which]); + pb = &prbuf[strlen(prbuf)]; + if (ws_know[obj->o_which]) + sprintf(pb, "of %s%s(%s)", ws_magic[obj->o_which].mi_name, + charge_str(obj), ws_made[obj->o_which]); + else if (ws_guess[obj->o_which]) + sprintf(pb, "called %s(%s)", ws_guess[obj->o_which], + ws_made[obj->o_which]); + else + sprintf(&prbuf[2], "%s %s", ws_made[obj->o_which], + ws_type[obj->o_which]); + when RING: + if (r_know[obj->o_which]) + sprintf(prbuf, "A%s ring of %s(%s)", ring_num(obj), + r_magic[obj->o_which].mi_name, r_stones[obj->o_which]); + else if (r_guess[obj->o_which]) + sprintf(prbuf, "A ring called %s(%s)", + r_guess[obj->o_which], r_stones[obj->o_which]); + else + sprintf(prbuf, "A%s %s ring", vowelstr(r_stones[obj->o_which]), + r_stones[obj->o_which]); + otherwise: + debug("Picked up something funny"); + sprintf(prbuf, "Something bizarre %s", unctrl(obj->o_type)); + } + if (obj == cur_armor) + strcat(prbuf, " (being worn)"); + if (obj == cur_weapon) + strcat(prbuf, " (weapon in hand)"); + if (obj == cur_ring[LEFT]) + strcat(prbuf, " (on left hand)"); + else if (obj == cur_ring[RIGHT]) + strcat(prbuf, " (on right hand)"); + if (drop && isupper(prbuf[0])) + prbuf[0] = tolower(prbuf[0]); + else if (!drop && islower(*prbuf)) + *prbuf = toupper(*prbuf); + if (!drop) + strcat(prbuf, "."); + return prbuf; +} + +/* + * money: + * Add to characters purse + */ +money() +{ + register struct room *rp; + + for (rp = rooms; rp <= &rooms[MAXROOMS-1]; rp++) + if (ce(hero, rp->r_gold)) + { + if (notify) + { + if (!terse) + addmsg("You found "); + msg("%d gold pieces.", rp->r_goldval); + } + purse += rp->r_goldval; + rp->r_goldval = 0; + cmov(rp->r_gold); + addch(FLOOR); + return; + } + msg("That gold must have been counterfeit"); +} + +/* + * drop: + * put something down + */ +drop() +{ + register char ch; + register struct linked_list *obj, *nobj; + register struct object *op; + + ch = mvwinch(stdscr, hero.y, hero.x); + if (ch != FLOOR && ch != PASSAGE) + { + msg("There is something there already"); + return; + } + if ((obj = get_item("drop", 0)) == NULL) + return; + op = (struct object *) ldata(obj); + if (!dropcheck(op)) + return; + /* + * Take it out of the pack + */ + if (op->o_count >= 2 && op->o_type != WEAPON) + { + nobj = new_item(sizeof *op); + op->o_count--; + op = (struct object *) ldata(nobj); + *op = *((struct object *) ldata(obj)); + op->o_count = 1; + obj = nobj; + if (op->o_group != 0) + inpack++; + } + else + detach(pack, obj); + inpack--; + /* + * Link it into the level object list + */ + attach(lvl_obj, obj); + mvaddch(hero.y, hero.x, op->o_type); + op->o_pos = hero; + msg("Dropped %s", inv_name(op, TRUE)); +} + +/* + * do special checks for dropping or unweilding|unwearing|unringing + */ +dropcheck(op) +register struct object *op; +{ + str_t save_max; + + if (op == NULL) + return TRUE; + if (op != cur_armor && op != cur_weapon + && op != cur_ring[LEFT] && op != cur_ring[RIGHT]) + return TRUE; + if (op->o_flags & ISCURSED) + { + msg("You can't. It appears to be cursed."); + return FALSE; + } + if (op == cur_weapon) + cur_weapon = NULL; + else if (op == cur_armor) + { + waste_time(); + cur_armor = NULL; + } + else if (op == cur_ring[LEFT] || op == cur_ring[RIGHT]) + { + switch (op->o_which) + { + case R_ADDSTR: + save_max = max_stats.s_str; + chg_str(-op->o_ac); + max_stats.s_str = save_max; + break; + case R_SEEINVIS: + player.t_flags &= ~CANSEE; + extinguish(unsee); + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + break; + } + cur_ring[op == cur_ring[LEFT] ? LEFT : RIGHT] = NULL; + } + return TRUE; +} + +/* + * return a new thing + */ +struct linked_list * +new_thing() +{ + register struct linked_list *item; + register struct object *cur; + register int j, k; + + item = new_item(sizeof *cur); + cur = (struct object *) ldata(item); + cur->o_hplus = cur->o_dplus = 0; + strcpy(cur->o_damage,"0d0"); + strcpy(cur->o_hurldmg,"0d0"); + cur->o_ac = 11; + cur->o_count = 1; + cur->o_group = 0; + cur->o_flags = 0; + /* + * Decide what kind of object it will be + * If we haven't had food for a while, let it be food. + */ + switch (no_food > 3 ? 2 : pick_one(things, NUMTHINGS)) + { + case 0: + cur->o_type = POTION; + cur->o_which = pick_one(p_magic, MAXPOTIONS); + when 1: + cur->o_type = SCROLL; + cur->o_which = pick_one(s_magic, MAXSCROLLS); + when 2: + no_food = 0; + cur->o_type = FOOD; + if (rnd(100) > 10) + cur->o_which = 0; + else + cur->o_which = 1; + when 3: + cur->o_type = WEAPON; + cur->o_which = rnd(MAXWEAPONS); + init_weapon(cur, cur->o_which); + if ((k = rnd(100)) < 10) + { + cur->o_flags |= ISCURSED; + cur->o_hplus -= rnd(3)+1; + } + else if (k < 15) + cur->o_hplus += rnd(3)+1; + when 4: + cur->o_type = ARMOR; + for (j = 0, k = rnd(100); j < MAXARMORS; j++) + if (k < a_chances[j]) + break; + if (j == MAXARMORS) + { + debug("Picked a bad armor %d", k); + j = 0; + } + cur->o_which = j; + cur->o_ac = a_class[j]; + if ((k = rnd(100)) < 20) + { + cur->o_flags |= ISCURSED; + cur->o_ac += rnd(3)+1; + } + else if (k < 28) + cur->o_ac -= rnd(3)+1; + when 5: + cur->o_type = RING; + cur->o_which = pick_one(r_magic, MAXRINGS); + switch (cur->o_which) + { + case R_ADDSTR: + case R_PROTECT: + case R_ADDHIT: + case R_ADDDAM: + if ((cur->o_ac = rnd(3)) == 0) + { + cur->o_ac = -1; + cur->o_flags |= ISCURSED; + } + when R_AGGR: + case R_TELEPORT: + cur->o_flags |= ISCURSED; + } + when 6: + cur->o_type = STICK; + cur->o_which = pick_one(ws_magic, MAXSTICKS); + fix_stick(cur); + otherwise: + debug("Picked a bad kind of object"); + wait_for(' '); + } + return item; +} + +/* + * pick an item out of a list of nitems possible magic items + */ +pick_one(magic, nitems) +register struct magic_item *magic; +int nitems; +{ + register struct magic_item *end; + register int i; + register struct magic_item *start; + + start = magic; + for (end = &magic[nitems], i = rnd(100); magic < end; magic++) + if (i < magic->mi_prob) + break; + if (magic == end) + { + if (wizard) + { + msg("bad pick_one: %d from %d items", i, nitems); + for (magic = start; magic < end; magic++) + msg("%s: %d%%", magic->mi_name, magic->mi_prob); + } + magic = start; + } + return magic - start; +} diff --git a/vers.c b/vers.c new file mode 100644 index 0000000..b30cd2a --- /dev/null +++ b/vers.c @@ -0,0 +1,16 @@ +/* + * version number. Whenever a new version number is desired, use + * sccs to get vers.c. Environ and encstr are declared here to + * force them to be loaded before the version number, and therefore + * not to be written in saved games. + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +char encstr[] = "\354\251\243\332A\201|\301\321p\210\251\327\"\257\365t\341%3\271^`~\203z{\341};\f\341\231\222e\234\351]\321"; +char version[] = "@(#)vers.c 3.6 (Berkeley) 4/21/81"; +char *release = "3.6.3"; diff --git a/weapons.c b/weapons.c new file mode 100644 index 0000000..24e811f --- /dev/null +++ b/weapons.c @@ -0,0 +1,316 @@ +/* + * Functions for dealing with problems brought about by weapons + * + * @(#)weapons.c 3.17 (Berkeley) 6/15/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +#define NONE 100 + +char *w_names[MAXWEAPONS] = { + "mace", + "long sword", + "short bow", + "arrow", + "dagger", + "rock", + "two handed sword", + "sling", + "dart", + "crossbow", + "crossbow bolt", + "spear", +}; + +static struct init_weps { + char *iw_dam; + char *iw_hrl; + char iw_launch; + int iw_flags; +} init_dam[MAXWEAPONS] = { + "2d4", "1d3", NONE, 0, /* Mace */ + "1d10", "1d2", NONE,0, /* Long sword */ + "1d1", "1d1", NONE, 0, /* Bow */ + "1d1", "1d6", BOW, ISMANY|ISMISL, /* Arrow */ + "1d6", "1d4", NONE, ISMISL, /* Dagger */ + "1d2", "1d4", SLING,ISMANY|ISMISL, /* Rock */ + "3d6", "1d2", NONE, 0, /* 2h sword */ + "0d0", "0d0", NONE, 0, /* Sling */ + "1d1", "1d3", NONE, ISMANY|ISMISL, /* Dart */ + "1d1", "1d1", NONE, 0, /* Crossbow */ + "1d2", "1d10", CROSSBOW, ISMANY|ISMISL,/* Crossbow bolt */ + "1d8", "1d6", NONE, ISMISL, /* Spear */ +}; + +/* + * missile: + * Fire a missile in a given direction + */ + +missile(ydelta, xdelta) +int ydelta, xdelta; +{ + register struct object *obj; + register struct linked_list *item, *nitem; + + /* + * Get which thing we are hurling + */ + if ((item = get_item("throw", WEAPON)) == NULL) + return; + obj = (struct object *) ldata(item); + if (!dropcheck(obj) || is_current(obj)) + return; + /* + * Get rid of the thing. If it is a non-multiple item object, or + * if it is the last thing, just drop it. Otherwise, create a new + * item with a count of one. + */ + if (obj->o_count < 2) + { + detach(pack, item); + inpack--; + } + else + { + obj->o_count--; + if (obj->o_group == 0) + inpack--; + nitem = (struct linked_list *) new_item(sizeof *obj); + obj = (struct object *) ldata(nitem); + *obj = *((struct object *) ldata(item)); + obj->o_count = 1; + item = nitem; + } + do_motion(obj, ydelta, xdelta); + /* + * AHA! Here it has hit something. If it is a wall or a door, + * or if it misses (combat) the mosnter, put it on the floor + */ + if (!isupper(mvwinch(mw, obj->o_pos.y, obj->o_pos.x)) + || !hit_monster(unc(obj->o_pos), obj)) + fall(item, TRUE); + mvwaddch(cw, hero.y, hero.x, PLAYER); +} + +/* + * do the actual motion on the screen done by an object traveling + * across the room + */ +do_motion(obj, ydelta, xdelta) +register struct object *obj; +register int ydelta, xdelta; +{ + /* + * Come fly with us ... + */ + obj->o_pos = hero; + for (;;) + { + register int ch; + + /* + * Erase the old one + */ + if (!ce(obj->o_pos, hero) && cansee(unc(obj->o_pos)) && + mvwinch(cw, obj->o_pos.y, obj->o_pos.x) != ' ') + mvwaddch(cw, obj->o_pos.y, obj->o_pos.x, + show(obj->o_pos.y, obj->o_pos.x)); + /* + * Get the new position + */ + obj->o_pos.y += ydelta; + obj->o_pos.x += xdelta; + if (step_ok(ch = winat(obj->o_pos.y, obj->o_pos.x)) && ch != DOOR) + { + /* + * It hasn't hit anything yet, so display it + * If it alright. + */ + if (cansee(unc(obj->o_pos)) && + mvwinch(cw, obj->o_pos.y, obj->o_pos.x) != ' ') + { + mvwaddch(cw, obj->o_pos.y, obj->o_pos.x, obj->o_type); + draw(cw); + } + continue; + } + break; + } +} + +/* + * fall: + * Drop an item someplace around here. + */ + +fall(item, pr) +register struct linked_list *item; +bool pr; +{ + register struct object *obj; + register struct room *rp; + static coord fpos; + + obj = (struct object *) ldata(item); + if (fallpos(&obj->o_pos, &fpos, TRUE)) + { + mvaddch(fpos.y, fpos.x, obj->o_type); + obj->o_pos = fpos; + if ((rp = roomin(&hero)) != NULL && !(rp->r_flags & ISDARK)) + { + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + attach(lvl_obj, item); + return; + } + if (pr) + if (obj->o_type == WEAPON) /* BUGFUX: Identification trick */ + msg("Your %s vanishes as it hits the ground.", w_names[obj->o_which]); + else + msg("%s vanishes as it hits the ground.", inv_name(obj,TRUE)); + discard(item); +} + +/* + * init_weapon: + * Set up the initial goodies for a weapon + */ + +init_weapon(weap, type) +register struct object *weap; +char type; +{ + register struct init_weps *iwp; + + iwp = &init_dam[type]; + strcpy(weap->o_damage,iwp->iw_dam); + strcpy(weap->o_hurldmg,iwp->iw_hrl); + weap->o_launch = iwp->iw_launch; + weap->o_flags = iwp->iw_flags; + if (weap->o_flags & ISMANY) + { + weap->o_count = rnd(8) + 8; + weap->o_group = newgrp(); + } + else + weap->o_count = 1; +} + +/* + * Does the missile hit the monster + */ + +hit_monster(y, x, obj) +register int y, x; +struct object *obj; +{ + static coord mp; + + mp.y = y; + mp.x = x; + return fight(&mp, winat(y, x), obj, TRUE); +} + +/* + * num: + * Figure out the plus number for armor/weapons + */ + +char * +num(n1, n2) +register int n1, n2; +{ + static char numbuf[80]; + + if (n1 == 0 && n2 == 0) + return "+0"; + if (n2 == 0) + sprintf(numbuf, "%s%d", n1 < 0 ? "" : "+", n1); + else + sprintf(numbuf, "%s%d,%s%d", + n1 < 0 ? "" : "+", n1, n2 < 0 ? "" : "+", n2); + return numbuf; +} + +/* + * wield: + * Pull out a certain weapon + */ + +wield() +{ + register struct linked_list *item; + register struct object *obj, *oweapon; + + oweapon = cur_weapon; + if (!dropcheck(cur_weapon)) + { + cur_weapon = oweapon; + return; + } + cur_weapon = oweapon; + if ((item = get_item("wield", WEAPON)) == NULL) + { +bad: + after = FALSE; + return; + } + + obj = (struct object *) ldata(item); + if (obj->o_type == ARMOR) + { + msg("You can't wield armor"); + goto bad; + } + if (is_current(obj)) + goto bad; + + if (terse) + addmsg("W"); + else + addmsg("You are now w"); + msg("ielding %s", inv_name(obj, TRUE)); + cur_weapon = obj; +} + +/* + * pick a random position around the give (y, x) coordinates + */ +fallpos(pos, newpos, passages) +register coord *pos, *newpos; +register bool passages; +{ + register int y, x, cnt, ch; + + cnt = 0; + for (y = pos->y - 1; y <= pos->y + 1; y++) + for (x = pos->x - 1; x <= pos->x + 1; x++) + { + /* + * check to make certain the spot is empty, if it is, + * put the object there, set it in the level list + * and re-draw the room if he can see it + */ + if (y == hero.y && x == hero.x) + continue; + if (((ch = winat(y, x)) == FLOOR || (passages && ch == PASSAGE)) + && rnd(++cnt) == 0) + { + newpos->y = y; + newpos->x = x; + } + } + return (cnt != 0); +} diff --git a/wizard.c b/wizard.c new file mode 100644 index 0000000..d5d13c7 --- /dev/null +++ b/wizard.c @@ -0,0 +1,197 @@ + +/* + * Special wizard commands (some of which are also non-wizard commands + * under strange circumstances) + * + * @(#)wizard.c 3.8 (Berkeley) 6/3/81 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#include "curses.h" +#include +#include +#include "rogue.h" + +/* + * whatis: + * What a certin object is + */ + +whatis() +{ + register struct object *obj; + register struct linked_list *item; + + if ((item = get_item("identify", 0)) == NULL) + return; + obj = (struct object *) ldata(item); + switch (obj->o_type) + { + case SCROLL: + s_know[obj->o_which] = TRUE; + if (s_guess[obj->o_which]) + { + free(s_guess[obj->o_which]); + s_guess[obj->o_which] = NULL; + } + when POTION: + p_know[obj->o_which] = TRUE; + if (p_guess[obj->o_which]) + { + free(p_guess[obj->o_which]); + p_guess[obj->o_which] = NULL; + } + when STICK: + ws_know[obj->o_which] = TRUE; + obj->o_flags |= ISKNOW; + if (ws_guess[obj->o_which]) + { + free(ws_guess[obj->o_which]); + ws_guess[obj->o_which] = NULL; + } + when WEAPON: + case ARMOR: + obj->o_flags |= ISKNOW; + when RING: + r_know[obj->o_which] = TRUE; + obj->o_flags |= ISKNOW; + if (r_guess[obj->o_which]) + { + free(r_guess[obj->o_which]); + r_guess[obj->o_which] = NULL; + } + } + msg(inv_name(obj, FALSE)); +} + +/* + * create_obj: + * Wizard command for getting anything he wants + */ + +create_obj() +{ + register struct linked_list *item; + register struct object *obj; + register char ch, bless; + + item = new_item(sizeof *obj); + obj = (struct object *) ldata(item); + msg("Type of item: "); + obj->o_type = readchar(cw); + mpos = 0; + msg("Which %c do you want? (0-f)", obj->o_type); + obj->o_which = (isdigit((ch = readchar(cw))) ? ch - '0' : ch - 'a' + 10); + obj->o_group = 0; + obj->o_count = 1; + mpos = 0; + if (obj->o_type == WEAPON || obj->o_type == ARMOR) + { + msg("Blessing? (+,-,n)"); + bless = readchar(cw); + mpos = 0; + if (obj->o_type == WEAPON) + { + init_weapon(obj, obj->o_which); + if (bless == '-') { + obj->o_hplus -= rnd(3)+1; + obj->o_flags |= ISCURSED; + } + if (bless == '+') + obj->o_hplus += rnd(3)+1; + } + else + { + obj->o_ac = a_class[obj->o_which]; + if (bless == '-') { + obj->o_ac += rnd(3)+1; + obj->o_flags |= ISCURSED; + } + if (bless == '+') + obj->o_ac -= rnd(3)+1; + } + } + else if (obj->o_type == RING) + switch (obj->o_which) + { + case R_PROTECT: + case R_ADDSTR: + case R_ADDHIT: + case R_ADDDAM: + msg("Blessing? (+,-,n)"); + bless = readchar(cw); + mpos = 0; + if (bless == '-') + obj->o_flags |= ISCURSED; + obj->o_ac = (bless == '-' ? -1 : rnd(2) + 1); + } + else if (obj->o_type == STICK) + fix_stick(obj); + add_pack(item, FALSE); +} + +/* + * telport: + * Bamf the hero someplace else + */ + +teleport() +{ + register int rm; + coord c; + + c = hero; + mvwaddch(cw, hero.y, hero.x, mvwinch(stdscr, hero.y, hero.x)); + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &hero); + } until(winat(hero.y, hero.x) == FLOOR); + light(&c); + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + /* + * turn off ISHELD in case teleportation was done while fighting + * a Fungi + */ + if (on(player, ISHELD)) { + player.t_flags &= ~ISHELD; + fung_hit = 0; + strcpy(monsters['F'-'A'].m_stats.s_dmg, "000d0"); + } + count = 0; + running = FALSE; + flush_type(); /* flush typeahead */ + return rm; +} + +/* + * passwd: + * see if user knows password + */ + +passwd() +{ + register char *sp, c; + char buf[80], *xcrypt(); + + msg("Wizard's Password:"); + mpos = 0; + sp = buf; + while ((c = readchar(cw)) != '\n' && c != '\r' && c != '\033') + if (c == md_killchar()) + sp = buf; + else if (c == md_erasechar() && sp > buf) + sp--; + else + *sp++ = c; + if (sp == buf) + return FALSE; + *sp = '\0'; + return (strcmp(PASSWD, xcrypt(buf, "mT")) == 0); +} diff --git a/xcrypt.c b/xcrypt.c new file mode 100644 index 0000000..26a55cd --- /dev/null +++ b/xcrypt.c @@ -0,0 +1,684 @@ +/* + * FreeSec: libcrypt + * + * Copyright (C) 1994 David Burren + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name(s) of the author(s) nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * This is an original implementation of the DES and the crypt(3) interfaces + * by David Burren . + * + * An excellent reference on the underlying algorithm (and related + * algorithms) is: + * + * B. Schneier, Applied Cryptography: protocols, algorithms, + * and source code in C, John Wiley & Sons, 1994. + * + * Note that in that book's description of DES the lookups for the initial, + * pbox, and final permutations are inverted (this has been brought to the + * attention of the author). A list of errata for this book has been + * posted to the sci.crypt newsgroup by the author and is available for FTP. + * + * NOTE: + * This file has a static version of des_setkey() so that crypt.o exports + * only the crypt() interface. This is required to make binaries linked + * against crypt.o exportable or re-exportable from the USA. + */ + +#include +#include + +extern unsigned long int md_ntohl(unsigned long int x); +extern unsigned long int md_htonl(unsigned long int x); + +#define _PASSWORD_EFMT1 '_' + +static unsigned char IP[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; + +static unsigned char inv_key_perm[64]; +static unsigned char key_perm[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +static unsigned char key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static unsigned char inv_comp_perm[56]; +static unsigned char comp_perm[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * No E box is used, as it's replaced by some ANDs, shifts, and ORs. + */ + +static unsigned char u_sbox[8][64]; +static unsigned char sbox[8][64] = { + { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + }, + { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + }, + { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + }, + { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + }, + { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + }, + { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + }, + { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + }, + { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + } +}; + +static unsigned char un_pbox[32]; +static unsigned char pbox[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static unsigned int bits32[32] = +{ + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static unsigned char bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + +static unsigned int saltbits; +static int old_salt; +static unsigned int *bits28, *bits24; +static unsigned char init_perm[64], final_perm[64]; +static unsigned int en_keysl[16], en_keysr[16]; +static unsigned int de_keysl[16], de_keysr[16]; +static int des_initialised = 0; +static unsigned char m_sbox[4][4096]; +static unsigned int psbox[4][256]; +static unsigned int ip_maskl[8][256], ip_maskr[8][256]; +static unsigned int fp_maskl[8][256], fp_maskr[8][256]; +static unsigned int key_perm_maskl[8][128], key_perm_maskr[8][128]; +static unsigned int comp_maskl[8][128], comp_maskr[8][128]; +static unsigned int old_rawkey0, old_rawkey1; + +static unsigned char ascii64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +/* 0000000000111111111122222222223333333333444444444455555555556666 */ +/* 0123456789012345678901234567890123456789012345678901234567890123 */ + +static __inline int +ascii_to_bin(char ch) +{ + if (ch > 'z') + return(0); + if (ch >= 'a') + return(ch - 'a' + 38); + if (ch > 'Z') + return(0); + if (ch >= 'A') + return(ch - 'A' + 12); + if (ch > '9') + return(0); + if (ch >= '.') + return(ch - '.'); + return(0); +} + +static void +des_init() +{ + int i, j, b, k, inbit, obit; + unsigned int *p, *il, *ir, *fl, *fr; + + old_rawkey0 = old_rawkey1 = 0; + saltbits = 0; + old_salt = 0; + bits24 = (bits28 = bits32 + 4) + 4; + + /* + * Invert the S-boxes, reordering the input bits. + */ + for (i = 0; i < 8; i++) + for (j = 0; j < 64; j++) { + b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf); + u_sbox[i][j] = sbox[i][b]; + } + + /* + * Convert the inverted S-boxes into 4 arrays of 8 bits. + * Each will handle 12 bits of the S-box input. + */ + for (b = 0; b < 4; b++) + for (i = 0; i < 64; i++) + for (j = 0; j < 64; j++) + m_sbox[b][(i << 6) | j] = + (u_sbox[(b << 1)][i] << 4) | + u_sbox[(b << 1) + 1][j]; + + /* + * Set up the initial & final permutations into a useful form, and + * initialise the inverted key permutation. + */ + for (i = 0; i < 64; i++) { + init_perm[final_perm[i] = IP[i] - 1] = i; + inv_key_perm[i] = 255; + } + + /* + * Invert the key permutation and initialise the inverted key + * compression permutation. + */ + for (i = 0; i < 56; i++) { + inv_key_perm[key_perm[i] - 1] = i; + inv_comp_perm[i] = 255; + } + + /* + * Invert the key compression permutation. + */ + for (i = 0; i < 48; i++) { + inv_comp_perm[comp_perm[i] - 1] = i; + } + + /* + * Set up the OR-mask arrays for the initial and final permutations, + * and for the key initial and compression permutations. + */ + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(il = &ip_maskl[k][i]) = 0; + *(ir = &ip_maskr[k][i]) = 0; + *(fl = &fp_maskl[k][i]) = 0; + *(fr = &fp_maskr[k][i]) = 0; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = init_perm[inbit]) < 32) + *il |= bits32[obit]; + else + *ir |= bits32[obit-32]; + if ((obit = final_perm[inbit]) < 32) + *fl |= bits32[obit]; + else + *fr |= bits32[obit - 32]; + } + } + } + for (i = 0; i < 128; i++) { + *(il = &key_perm_maskl[k][i]) = 0; + *(ir = &key_perm_maskr[k][i]) = 0; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + if ((obit = inv_key_perm[inbit]) == 255) + continue; + if (obit < 28) + *il |= bits28[obit]; + else + *ir |= bits28[obit - 28]; + } + } + *(il = &comp_maskl[k][i]) = 0; + *(ir = &comp_maskr[k][i]) = 0; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + if ((obit=inv_comp_perm[inbit]) == 255) + continue; + if (obit < 24) + *il |= bits24[obit]; + else + *ir |= bits24[obit - 24]; + } + } + } + } + + /* + * Invert the P-box permutation, and convert into OR-masks for + * handling the output of the S-box arrays setup above. + */ + for (i = 0; i < 32; i++) + un_pbox[pbox[i] - 1] = i; + + for (b = 0; b < 4; b++) + for (i = 0; i < 256; i++) { + *(p = &psbox[b][i]) = 0; + for (j = 0; j < 8; j++) { + if (i & bits8[j]) + *p |= bits32[un_pbox[8 * b + j]]; + } + } + + des_initialised = 1; +} + +static void +setup_salt(int salt) +{ + unsigned int obit, saltbit; + int i; + + if (salt == old_salt) + return; + old_salt = salt; + + saltbits = 0; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } +} + +static int +des_setkey(const unsigned char *key) +{ + unsigned int k0, k1, rawkey0, rawkey1; + int shifts, round; + + if (!des_initialised) + des_init(); + + rawkey0 = md_ntohl(*(unsigned int *) key); + rawkey1 = md_ntohl(*(unsigned int *) (key + 4)); + + if ((rawkey0 | rawkey1) + && rawkey0 == old_rawkey0 + && rawkey1 == old_rawkey1) { + /* + * Already setup for this key. + * This optimisation fails on a zero key (which is weak and + * has bad parity anyway) in order to simplify the starting + * conditions. + */ + return(0); + } + old_rawkey0 = rawkey0; + old_rawkey1 = rawkey1; + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + k0 = key_perm_maskl[0][rawkey0 >> 25] + | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskl[4][rawkey1 >> 25] + | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; + k1 = key_perm_maskr[0][rawkey0 >> 25] + | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskr[4][rawkey1 >> 25] + | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + unsigned int t0, t1; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + + de_keysl[15 - round] = + en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] + | comp_maskl[1][(t0 >> 14) & 0x7f] + | comp_maskl[2][(t0 >> 7) & 0x7f] + | comp_maskl[3][t0 & 0x7f] + | comp_maskl[4][(t1 >> 21) & 0x7f] + | comp_maskl[5][(t1 >> 14) & 0x7f] + | comp_maskl[6][(t1 >> 7) & 0x7f] + | comp_maskl[7][t1 & 0x7f]; + + de_keysr[15 - round] = + en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] + | comp_maskr[1][(t0 >> 14) & 0x7f] + | comp_maskr[2][(t0 >> 7) & 0x7f] + | comp_maskr[3][t0 & 0x7f] + | comp_maskr[4][(t1 >> 21) & 0x7f] + | comp_maskr[5][(t1 >> 14) & 0x7f] + | comp_maskr[6][(t1 >> 7) & 0x7f] + | comp_maskr[7][t1 & 0x7f]; + } + return(0); +} + +static int +do_des(unsigned int l_in, unsigned int r_in, unsigned int *l_out, + unsigned int *r_out, int count) +{ + /* + * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. + */ + unsigned int l, r, *kl, *kr, *kl1, *kr1; + unsigned int f = 0, r48l, r48r; + int round; + + if (count == 0) { + return(1); + } else if (count > 0) { + /* + * Encrypting + */ + kl1 = en_keysl; + kr1 = en_keysr; + } else { + /* + * Decrypting + */ + count = -count; + kl1 = de_keysl; + kr1 = de_keysr; + } + + /* + * Do initial permutation (IP). + */ + l = ip_maskl[0][l_in >> 24] + | ip_maskl[1][(l_in >> 16) & 0xff] + | ip_maskl[2][(l_in >> 8) & 0xff] + | ip_maskl[3][l_in & 0xff] + | ip_maskl[4][r_in >> 24] + | ip_maskl[5][(r_in >> 16) & 0xff] + | ip_maskl[6][(r_in >> 8) & 0xff] + | ip_maskl[7][r_in & 0xff]; + r = ip_maskr[0][l_in >> 24] + | ip_maskr[1][(l_in >> 16) & 0xff] + | ip_maskr[2][(l_in >> 8) & 0xff] + | ip_maskr[3][l_in & 0xff] + | ip_maskr[4][r_in >> 24] + | ip_maskr[5][(r_in >> 16) & 0xff] + | ip_maskr[6][(r_in >> 8) & 0xff] + | ip_maskr[7][r_in & 0xff]; + + while (count--) { + /* + * Do each round. + */ + kl = kl1; + kr = kr1; + round = 16; + while (round--) { + /* + * Expand R to 48 bits (simulate the E-box). + */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do sbox lookups (which shrink it back to 32 bits) + * and do the pbox permutation at the same time. + */ + f = psbox[0][m_sbox[0][r48l >> 12]] + | psbox[1][m_sbox[1][r48l & 0xfff]] + | psbox[2][m_sbox[2][r48r >> 12]] + | psbox[3][m_sbox[3][r48r & 0xfff]]; + /* + * Now that we've permuted things, complete f(). + */ + f ^= l; + l = r; + r = f; + } + r = l; + l = f; + } + /* + * Do final permutation (inverse of IP). + */ + *l_out = fp_maskl[0][l >> 24] + | fp_maskl[1][(l >> 16) & 0xff] + | fp_maskl[2][(l >> 8) & 0xff] + | fp_maskl[3][l & 0xff] + | fp_maskl[4][r >> 24] + | fp_maskl[5][(r >> 16) & 0xff] + | fp_maskl[6][(r >> 8) & 0xff] + | fp_maskl[7][r & 0xff]; + *r_out = fp_maskr[0][l >> 24] + | fp_maskr[1][(l >> 16) & 0xff] + | fp_maskr[2][(l >> 8) & 0xff] + | fp_maskr[3][l & 0xff] + | fp_maskr[4][r >> 24] + | fp_maskr[5][(r >> 16) & 0xff] + | fp_maskr[6][(r >> 8) & 0xff] + | fp_maskr[7][r & 0xff]; + return(0); +} + +static int +des_cipher(const unsigned char *in, unsigned char *out, int salt, int count) +{ + unsigned int l_out, r_out, rawl, rawr; + unsigned int x[2]; + int retval; + + if (!des_initialised) + des_init(); + + setup_salt(salt); + + memcpy(x, in, sizeof x); + rawl = md_ntohl(x[0]); + rawr = md_ntohl(x[1]); + retval = do_des(rawl, rawr, &l_out, &r_out, count); + + x[0] = md_htonl(l_out); + x[1] = md_htonl(r_out); + memcpy(out, x, sizeof x); + return(retval); +} + +char * +xcrypt(const char *key, const char *setting) +{ + int i; + unsigned int count, salt, l, r0, r1, keybuf[2]; + unsigned char *p, *q; + static unsigned char output[21]; + + if (!des_initialised) + des_init(); + + /* + * Copy the key, shifting each character up by one bit + * and padding with zeros. + */ + q = (unsigned char *) keybuf; + while ((q - (unsigned char *) keybuf) < sizeof(keybuf)) { + if ((*q++ = *key << 1)) + key++; + } + if (des_setkey((unsigned char *) keybuf)) + return(NULL); + + if (*setting == _PASSWORD_EFMT1) { + /* + * "new"-style: + * setting - underscore, 4 bytes of count, 4 bytes of salt + * key - unlimited characters + */ + for (i = 1, count = 0; i < 5; i++) + count |= ascii_to_bin(setting[i]) << (i - 1) * 6; + + for (i = 5, salt = 0; i < 9; i++) + salt |= ascii_to_bin(setting[i]) << (i - 5) * 6; + + while (*key) { + /* + * Encrypt the key with itself. + */ + if (des_cipher((unsigned char*)keybuf, (unsigned char*)keybuf, 0, 1)) + return(NULL); + /* + * And XOR with the next 8 characters of the key. + */ + q = (unsigned char *) keybuf; + while (((q - (unsigned char *) keybuf) < sizeof(keybuf)) && + *key) + *q++ ^= *key++ << 1; + + if (des_setkey((unsigned char *) keybuf)) + return(NULL); + } + strncpy((char *)output, setting, 9); + + /* + * Double check that we weren't given a short setting. + * If we were, the above code will probably have created + * wierd values for count and salt, but we don't really care. + * Just make sure the output string doesn't have an extra + * NUL in it. + */ + output[9] = '\0'; + p = output + strlen((const char *)output); + } else { + /* + * "old"-style: + * setting - 2 bytes of salt + * key - up to 8 characters + */ + count = 25; + + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + /* + * If the encrypted password that the salt was extracted from + * is only 1 character long, the salt will be corrupted. We + * need to ensure that the output string doesn't have an extra + * NUL in it! + */ + output[1] = setting[1] ? setting[1] : output[0]; + + p = output + 2; + } + setup_salt(salt); + /* + * Do it. + */ + if (do_des(0, 0, &r0, &r1, count)) + return(NULL); + /* + * Now encode the result... + */ + l = (r0 >> 8); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = (r0 << 16) | ((r1 >> 16) & 0xffff); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = r1 << 2; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + *p = 0; + + return((char *)output); +} -- 2.20.1