From e3b78ef14fd58bd4477a5899732f50e4798b9dff Mon Sep 17 00:00:00 2001 From: Robin Karlsson Date: Sat, 14 Mar 2015 04:29:23 +0100 Subject: [PATCH] Import NetBSD games/rogue Change-Id: Id4aef4950f250edef2d507910877aabc6b9580ea --- distrib/sets/lists/minix/mi | 5 + etc/mtree/NetBSD.dist.base | 1 + games/Makefile | 2 +- games/rogue/CHANGES | 55 +++ games/rogue/Makefile | 20 + games/rogue/USD.doc/Makefile | 11 + games/rogue/USD.doc/rogue.me | 834 ++++++++++++++++++++++++++++++++ games/rogue/hit.c | 466 ++++++++++++++++++ games/rogue/init.c | 366 ++++++++++++++ games/rogue/inventory.c | 841 ++++++++++++++++++++++++++++++++ games/rogue/level.c | 900 +++++++++++++++++++++++++++++++++++ games/rogue/machdep.c | 492 +++++++++++++++++++ games/rogue/main.c | 84 ++++ games/rogue/message.c | 378 +++++++++++++++ games/rogue/monster.c | 886 ++++++++++++++++++++++++++++++++++ games/rogue/move.c | 649 +++++++++++++++++++++++++ games/rogue/object.c | 802 +++++++++++++++++++++++++++++++ games/rogue/pack.c | 574 ++++++++++++++++++++++ games/rogue/pathnames.h | 35 ++ games/rogue/play.c | 299 ++++++++++++ games/rogue/random.c | 146 ++++++ games/rogue/ring.c | 338 +++++++++++++ games/rogue/rogue.6 | 107 +++++ games/rogue/rogue.h | 702 +++++++++++++++++++++++++++ games/rogue/room.c | 671 ++++++++++++++++++++++++++ games/rogue/save.c | 427 +++++++++++++++++ games/rogue/score.c | 674 ++++++++++++++++++++++++++ games/rogue/spec_hit.c | 536 +++++++++++++++++++++ games/rogue/throw.c | 324 +++++++++++++ games/rogue/trap.c | 282 +++++++++++ games/rogue/use.c | 625 ++++++++++++++++++++++++ games/rogue/zap.c | 407 ++++++++++++++++ 32 files changed, 12938 insertions(+), 1 deletion(-) create mode 100644 games/rogue/CHANGES create mode 100644 games/rogue/Makefile create mode 100644 games/rogue/USD.doc/Makefile create mode 100644 games/rogue/USD.doc/rogue.me create mode 100644 games/rogue/hit.c create mode 100644 games/rogue/init.c create mode 100644 games/rogue/inventory.c create mode 100644 games/rogue/level.c create mode 100644 games/rogue/machdep.c create mode 100644 games/rogue/main.c create mode 100644 games/rogue/message.c create mode 100644 games/rogue/monster.c create mode 100644 games/rogue/move.c create mode 100644 games/rogue/object.c create mode 100644 games/rogue/pack.c create mode 100644 games/rogue/pathnames.h create mode 100644 games/rogue/play.c create mode 100644 games/rogue/random.c create mode 100644 games/rogue/ring.c create mode 100644 games/rogue/rogue.6 create mode 100644 games/rogue/rogue.h create mode 100644 games/rogue/room.c create mode 100644 games/rogue/save.c create mode 100644 games/rogue/score.c create mode 100644 games/rogue/spec_hit.c create mode 100644 games/rogue/throw.c create mode 100644 games/rogue/trap.c create mode 100644 games/rogue/use.c create mode 100644 games/rogue/zap.c diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 320c424f5..0fc13a37c 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -636,6 +636,7 @@ ./usr/games/ppt minix-sys ./usr/games/primes minix-sys ./usr/games/random minix-sys +./usr/games/rogue minix-sys ./usr/games/snake minix-sys ./usr/games/snscore minix-sys ./usr/games/strfile minix-sys @@ -5174,6 +5175,7 @@ ./usr/man/man6/ppt.6 minix-sys ./usr/man/man6/primes.6 minix-sys ./usr/man/man6/random.6 minix-sys +./usr/man/man6/rogue.6 minix-sys ./usr/man/man6/snake.6 minix-sys ./usr/man/man6/tetris.6 minix-sys ./usr/man/man6/wargames.6 minix-sys @@ -5414,6 +5416,9 @@ ./usr/share/doc/usd/03.shell/t3 minix-sys ./usr/share/doc/usd/03.shell/t4 minix-sys ./usr/share/doc/usd/03.shell/t.mac minix-sys +./usr/share/doc/usd/30.rogue minix-sys +./usr/share/doc/usd/30.rogue/Makefile minix-sys +./usr/share/doc/usd/30.rogue/rogue.me minix-sys ./usr/share/examples minix-sys ./usr/share/examples/atf minix-sys atf ./usr/share/examples/atf/atf-run.hooks minix-sys atf,!kyua diff --git a/etc/mtree/NetBSD.dist.base b/etc/mtree/NetBSD.dist.base index ece90ce0c..f99527388 100644 --- a/etc/mtree/NetBSD.dist.base +++ b/etc/mtree/NetBSD.dist.base @@ -135,6 +135,7 @@ ./usr/share/doc/psd/19.curses ./usr/share/doc/usd ./usr/share/doc/usd/03.shell +./usr/share/doc/usd/30.rogue ./usr/share/examples ./usr/share/examples/tmux ./usr/share/info diff --git a/games/Makefile b/games/Makefile index 085caaf40..1fcc709e1 100644 --- a/games/Makefile +++ b/games/Makefile @@ -13,7 +13,7 @@ SUBDIR= adventure arithmetic \ factor fish fortune \ monop morse number \ pig ppt primes \ - random snake tetris \ + random rogue snake tetris \ wargames .if !defined(__MINIX) diff --git a/games/rogue/CHANGES b/games/rogue/CHANGES new file mode 100644 index 000000000..066ac128c --- /dev/null +++ b/games/rogue/CHANGES @@ -0,0 +1,55 @@ +$NetBSD: CHANGES,v 1.3 2000/03/13 22:53:22 soren Exp $ + +From: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU +Date: 30 Nov 87 15:08:15 PST (Mon) +To: okeeffe.Berkeley.EDU!mckusick@ucbvax.Berkeley.EDU (Kirk McKusick) +Subject: Re: Public domain rogue +Return-Path: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU + +Here is a list of discrepencies from the documentation you sent me: + +The -d option not implemented. +The -r option not implemented, use "rogue save_file" instead. +Strength is between 1 and 99, not 3 and 32. +The D command is not implemented. +Only scrolls,potions,wands,and rings may be "call"ed something. +The ^P command may be used to go 4 messages back, instead of just 1. +The @ comand is not implemented. +There are no dark rooms. +ROGUEOPTS of flush,terse,seefloor,askme,inventory are ignored. + 'askquit' is added to prevent ^\ from terminating the game accidentally. + If 'noaskquit' is + found in the ROGUEOPTS string, the ^\ kills the game, otherwise, + the player is asked if he really wants to quit. In either case, no + score file processing is attempted. +The score is keyed to winning scores, and no player may appear twice. + + + + + + +Other differences from "standard" rogue 5.3. This list covers externally +visible differences only. + +There should be NO bugs with any severe consequences. Absolutely NO + game-stopping, or game-winning bugs should be present. +Traps fail occasionally, that is, they sometimes are sprung but miss. +The ^A command prints out some stuff you're probably not interested in. +The '&' command silently saves your screen into the file 'rogue.screen' +Any inventory selection command that takes '*' as a request to list all + appropriate items, can take one of "=?:)]!/" to list only rings, + scrolls, or whatever. +Scrolls and potions, once used, become identified. All other objects become + identified only by scroll of identification. +There is only one scroll of identification, and it works on any item. +ROGUEOPTS + Only the following are implemented: + file,jump,name,askquit,tombstone,passgo + "askquit" is used to prevent accidental termination of the game via ^\ +You may drop objects in doorways. +Prints a picture of a skull, not a tombstone, upon death. +The save/restore game function is faster and machine-independent, but sometimes + requires modification when new variables are added to the source. +The potion of detect monster lasts for the whole level. +Their is no wand of light. diff --git a/games/rogue/Makefile b/games/rogue/Makefile new file mode 100644 index 000000000..2d4a5658c --- /dev/null +++ b/games/rogue/Makefile @@ -0,0 +1,20 @@ +# $NetBSD: Makefile,v 1.18 2013/08/11 03:44:27 dholland Exp $ +# @(#)Makefile 8.1 (Berkeley) 5/31/93 + +PROG= rogue +CPPFLAGS+=-DUNIX +SRCS= hit.c init.c inventory.c level.c machdep.c main.c \ + message.c monster.c move.c object.c pack.c play.c random.c ring.c \ + room.c save.c score.c spec_hit.c throw.c trap.c use.c zap.c +DPADD= ${LIBCURSES} ${LIBTERMINFO} +LDADD= -lcurses -lterminfo +HIDEGAME=hidegame +SETGIDGAME=yes +MAN= rogue.6 + +.if make(install) +SUBDIR+=USD.doc +.endif + +.include +.include diff --git a/games/rogue/USD.doc/Makefile b/games/rogue/USD.doc/Makefile new file mode 100644 index 000000000..38881ba91 --- /dev/null +++ b/games/rogue/USD.doc/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.5 2013/02/17 12:17:40 jmcneill Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +DIR= usd/30.rogue +SRCS= rogue.me +MACROS= -me + +paper.ps: ${SRCS} + ${TOOL_TBL} ${SRCS} | ${TOOL_ROFF_PS} ${MACROS} > ${.TARGET} + +.include diff --git a/games/rogue/USD.doc/rogue.me b/games/rogue/USD.doc/rogue.me new file mode 100644 index 000000000..b1592dd21 --- /dev/null +++ b/games/rogue/USD.doc/rogue.me @@ -0,0 +1,834 @@ +.\" $NetBSD: rogue.me,v 1.6 2004/02/13 11:36:08 wiz Exp $ +.\" +.\" Copyright (c) 1986, 1993 +.\" The Regents of the University of California. 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 of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. +.\" +.\" @(#)rogue.me 8.2 (Berkeley) 6/1/94 +.\" +.ds E \s-2\s0 +.ds R \s-2\s0 +.ds U \s-2UNIX\s0 +.ie t .ds _ \d\(mi\u +.el .ds _ _ +.de Cs +\&\\$3\*(lq\\$1\*(rq\\$2 +.. +.sp 5 +.ce 1000 +.ps +4 +.vs +4p +.b +A Guide to the Dungeons of Doom +.r +.vs +.ps +.sp 2 +.i +Michael C. Toy +Kenneth C. R. C. Arnold +.r +.sp 2 +Computer Systems Research Group +Department of Electrical Engineering and Computer Science +University of California +Berkeley, California 94720 +.sp 4 +.i ABSTRACT +.ce 0 +.(b I F +.bi Rogue +is a visual CRT based fantasy game +which runs under the \*U\(dg timesharing system. +.(f +\fR\(dg\*U is a trademark of Bell Laboratories\fP +.)f +This paper describes how to play rogue, +and gives a few hints +for those who might otherwise get lost in the Dungeons of Doom. +.)b +\".he '''\fBA Guide to the Dungeons of Doom\fP' +\" .fo ''- % -'' +.eh 'USD:30-%''A Guide to the Dungeons of Doom' +.oh 'A Guide to the Dungeons of Doom''USD:30-%' +.sh 1 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 mace, +a bow, and a quiver of arrows +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 weapons, +put on your armor, +eat what is almost your last food, +and enter the dungeons. +.sh 1 "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\** +.(f +\** As opposed to pseudo English sentences. +.)f +and the results of your commands +are displayed graphically on the screen rather +than being explained in words.\** +.(f +\** A minimum screen size of 24 lines by 80 columns is required. +If the screen is larger, only the 24x80 section will be used +for the map. +.)f +.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. +.sh 1 "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 \*(lqYou can see ...\*(rq descriptions +of standard fantasy games. +Figure 1 is a sample of what a rogue screen might look like. +.(z +.hl +.nf +.TS +center; +ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce. +- - - - - - - - - - - - +| . . . . . . . . . . + +| . . @ . . . . ] . . | +| . . . . B . . . . . | +| . . . . . . . . . . | +- - - - - + - - - - - - +.TE + + +.ce 1000 +Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0 + +Figure 1 +.ce +.hl +.)z +.sh 2 "The bottom line" +.pp +At the bottom line of the screen +are a few pieces of cryptic information +describing your current status. +Here is an explanation of what these things mean: +.ip Level \w'Level\ \ 'u +This number indicates how deep you have gone in the dungeon. +It starts at one and goes up as you go deeper into the dungeon. +.ip Gold \w'Level\ \ 'u +The number of gold pieces you have managed to find +and keep with you so far. +.ip Hp \w'Level\ \ 'u +Your current and maximum health 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 health points can reach. +.ip Str \w'Level\ \ 'u +Your current strength and maximum ever strength. +This can be any integer less than or equal to 99, +or greater than or equal to 1. +The higher the number, +the stronger you are. +The number in the parentheses +is the maximum strength you have attained so far this game. +.ip Arm \w'Level\ \ 'u +Your current armor protection. +This number indicates how effective your armor is +in stopping blows from unfriendly creatures. +The higher this number is, +the more effective the armor. +.ip Exp \w'Level\ \ 'u +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. +.sh 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 \*(lq--More--\*(rq 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. +.sh 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 "-\^|" +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 ring with magic properties +.ip / +A magical staff or wand +.ip ^ +A trap, watch out for these. +.ip % +A staircase to other levels +.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 nasty and vicious. +.sh 1 Commands +.pp +Commands are given to rogue by typing one or two characters. +Most commands can be preceded by a count to repeat them +(e.g. typing +.Cs 10s +will do ten searches). +Commands for which counts make no sense +have the count ignored. +To cancel a count or a prefix, +type \*E. +The list of commands is rather long, +but it can be read at any time during the game with the +.Cs ? +command. +Here it is for reference, +with a short explanation of each command. +.ip ? +The help command. +Asks for a character to give help on. +If you type a +.Cs * , +it will list all the commands, +otherwise it will explain what the character you typed does. +.ip / +This is the \*(lqWhat is that on the screen?\*(rq command. +A +.Cs / +followed by any character that you see on the level, +will tell you what that character is. +For instance, +typing +.Cs /@ +will tell you that the +.Cs @ +symbol represents you, the player. +.ip "h, H, ^H" +Move left. +You move one space to the left. +If you use upper case +.Cs h , +you will continue to move left until you run into something. +This works for all movement commands +(e.g. +.Cs L +means run in direction +.Cs l ) +If you use the \*(lqcontrol\*(rq +.Cs h , +you will continue moving in the specified direction +until you pass something interesting or run into a wall. +You should experiment with this, +since it is a very useful command, +but very difficult to describe. +This also works for all movement commands. +.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 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 +.Cs th +to throw +something to the left.) +.ip f +Fight until someone dies. +When followed with a direction +this will force you to fight the creature in that direction +until either you or it bites the big one. +.ip m +Move onto something without picking it up. +This will move you one space in the direction you specify and, +if there is an object there you can pick up, +it won't do it. +.ip z +Zap prefix. +Point a staff or wand in a given direction +and fire it. +Even non-directional staves must be pointed in some direction +to be used. +.ip ^ +Identify trap command. +If a trap is on your map +and you can't remember what type it is, +you can get rogue to remind you +by getting next to it and typing +.Cs ^ +followed by the direction that would move you on top of it. +.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 > +Climb down a staircase to the next level. +Not surprisingly, this can only be done if you are standing on staircase. +.ip < +Climb up a staircase to the level above. +This can't be done without the Amulet of Yendor in your possession. +.ip "." +Rest. +This is the \*(lqdo nothing\*(rq command. +This is good for waiting and healing. +.ip , +Pick up something. +This picks up whatever you are currently standing on, +if you are standing on anything at all. +.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 one of the potions you are carrying. +.ip r +Read one of the scrolls in your pack. +.ip e +Eat food from your pack. +.ip w +Wield a weapon. +Take a weapon out of your pack and carry it for use in combat, +replacing the one you are currently using (if any). +.ip W +Wear armor. +You can only wear one suit of armor at a time. +This takes extra time. +.ip T +Take armor off. +You can't remove armor that is cursed. +This takes extra time. +.ip P +Put on a ring. +You can wear only two rings at a time +(one on each hand). +If you aren't wearing any rings, +this command will ask you which hand you want to wear it on, +otherwise, it will place it on the unused hand. +The program assumes that you wield your sword in your right hand. +.ip R +Remove a ring. +If you are only wearing one ring, +this command takes it off. +If you are wearing two, +it will ask you which one you wish to remove, +.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. +You cannot drop a cursed object at all +if you are wielding or wearing it. +.ip c +Call an object something. +If you have a type of object in your pack +which you wish to remember something about, +you can use the call command to give a name to that type of object. +This is usually used when you figure out what a +potion, scroll, ring, or staff is +after you pick it up but before it is truly identified. Each type of +scroll and potion will become identified after its first use. +.ip o +Examine and set options. +This command is further explained in the section on options. +.ip ^R +Redraws the screen. +Useful if spurious messages or transmission errors +have messed up the display. +.ip ^P +Print last message. +Useful when a message disappears before you can read it. +Consecutive repetitions of this command will reveal the last +five messages. +.ip \*E +Cancel a command, prefix, or count. +.ip ! +Escape to a shell for some commands. +.ip Q +Quit. +Leave the game. +.ip S +Save the current game in a file. +It will ask you whether you wish to use the default save file. +.i 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 \fIsave\*_file\fP +.ip v +Prints the program version number. +.ip ) +Print the weapon you are currently wielding +.ip ] +Print the armor you are currently wearing +.ip = +Print the rings you are currently wearing +.sh 1 Rooms +.pp +Rooms in the dungeons are lit as you enter them. +Upon leaving a room, +all monsters inside the room are erased from the screen. +In the darkness of a corridor, you can only see one space +in all directions around you. +.sh 1 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. +.sh 1 "Objects you can find" +.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 +(unless you use the +.Cs m +prefix, see above). +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 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 type an \*E and the command will be aborted. +.pp +Some objects, like armor and weapons, +are easily differentiated. +Others, like scrolls and potions, +are given labels which vary according to type. +During a game, +any two of the same kind of object +with the same label +are the same type. +However, +the labels will vary from game to game. +.pp +When you use one of these labeled objects, +if its effect may be obvious. Potions or scrolls will +become identified at this point, but not other items. +You may want to call these other items something +so you will recognize it later, +you can use the +.Cs call +command +(see above). +.sh 2 Weapons +.pp +Some weapons, +like arrows, +come in bunches, +but most come one at a time. +In order to use a weapon, +you must wield it. +To fire an arrow out of a bow, +you must first wield the bow, +then throw the arrow. +You can only wield one weapon at a time, +but you can't change weapons if the one +you are currently wielding is cursed. +The commands to use weapons are +.Cs w +(wield) +and +.Cs t +(throw). +.sh 2 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 protection. +The higher the armor protection, +the more protection the armor affords against the blows of monsters. +Here is a list of the various armor types and their normal armor protection: +.(b +.TS +box center; +l r. +\ \ \fIType Protection\fP +None 0 +Leather armor 2 +Studded leather / Ring mail 3 +Scale mail 4 +Chain mail 5 +Banded mail / Splint mail 6 +Plate mail 7 +.TE +.)b +.lp +If a piece of armor is enchanted, +its armor protection will be higher than normal. +If a suit of armor is cursed, +its armor protection will be lower, +and you will not be able to remove it. +However, not all armor with a protection that is lower than normal is cursed. +.pp +The commands to use weapons are +.Cs W +(wear) +and +.Cs T +(take off). +.sh 2 Scrolls +.pp +Scrolls come with titles in an unknown tongue\**. +.(f +\** Actually, it's a dialect spoken only by the twenty-seven members +of a tribe in Outer Mongolia, +but you're not supposed to +.i know +that. +.)f +After you read a scroll, +it disappears from your pack. +The command to use a scroll is +.Cs r +(read). +.sh 2 Potions +.pp +Potions are labeled by the color of the liquid inside the flask. +They disappear after being quaffed. +The command to quaff a potion is +.Cs q +(quaff). +.sh 2 "Staves and Wands" +.pp +Staves and wands do the same kinds of things. +Staves are identified by a type of wood; +wands by a type of metal or bone. +They are generally things you want to do to something +over a long distance, +so you must point them at what you wish to affect +to use them. +Some staves are not affected by the direction they are pointed, though. +Staves come with multiple magic charges, +the number being random, +and when they are used up, +the staff is just a piece of wood or metal. +.pp +The command to use a wand or staff is +.Cs z +(zap) +.sh 2 Rings +.pp +Rings are very useful items, +since they are relatively permanent magic, +unlike the usually fleeting effects of potions, scrolls, and staves. +Of course, +the bad rings are also more powerful. +Most rings also cause you to use up food more rapidly, +the rate varying with the type of ring. +Rings are differentiated by their stone settings. +The commands to use rings are +.Cs P +(put on) +and +.Cs R +(remove). +.sh 2 Food +.pp +Food is necessary to keep you going. +If you go too long without eating you will faint, +and eventually die of starvation. +The command to use food is +.Cs e +(eat). +.sh 1 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. +.ne 1i +.sh 2 "Setting the options" +.pp +There are two ways to set the options. +The first is with the +.Cs o +command of rogue; +the second is with the +.Cs ROGUEOPTS +environment variable\**. +.(f +\** On Version 6 systems, +there is no equivalent of the ROGUEOPTS feature. +.br +.)f +.br +.sh 3 "Using the `o' command" +.pp +When you type +.Cs 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 \*R +which means to go to the next option, +a +.Cs \- +which means to go to the previous option, +an \*E +which means to return to the game, +or you can give the option a value. +For boolean options this merely involves typing +.Cs t +for true or +.Cs f +for false. +For string options, +type the new value followed by a \*R. +.sh 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 +or turned off by putting a +.Cs no +in front of the name. +Thus to set up an environment variable so that +.b jump +is on, +.b passgo +is off, +and the +.b name +is set to \*(lqBlue Meanie\*(rq, +use the command +.nf +.ti +3n +% setenv ROGUEOPTS "jump,nopassgo,name=Blue Meanie"\** +.fi +.(f +\** +For those of you who use the Bourne shell sh (1), the commands would be +.in +3 +.nf +$ ROGUEOPTS="jump,nopassgo,name=Blue Meanie" +$ export ROGUEOPTS +.fi +.in +0 +.)f +.sh 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. +For character string options, +input over forty characters will be ignored. +.ip "\fBjump\fP [\fI\^nojump\^\fP]" +If this option is set, +running moves will not be displayed +until you reach the end of the move. +This saves considerable CPU and display time. +This option defaults to +.i jump +if you are using a slow terminal. +.ip "\fBpassgo\fP [\fI\^nopassgo\^\fP]" +Follow turnings in passageways. +If you run in a passage +and you run into stone or a wall, +rogue will see if it can turn to the right or left. +If it can only turn one way, +it will turn that way. +If it can turn either or neither, +it will stop. +This algorithm can sometimes lead to slightly confusing occurrences +which is why it defaults to \fInopassgo\fP. +.ip "\fBskull\fP [\fI\^skull\^\fP]" +Print out the skull at the end if you get killed. +This is nice but slow, so you can turn it off if you like. +.ip "\fBname\fP [account name]" +This is the name of your character. +It is used if you get on the top ten scorer's list. +.ip "\fBfruit\fP [\fI\^slime-mold\^\fP]" +This should hold the name of a fruit that you enjoy eating. +It is basically a whimsey that rogue uses in a couple of places. +.ip "\fBfile\fP [\fI\^~/rogue.save\^\fP]" +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 start with the special character +.Cs ~ +which expands to be your home directory. +.sh 1 Scoring +.pp +Rogue maintains a list +of the top scoring people or scores on your machine. +If you score higher than someone else on this list, +or better your previous score on the list, +you will be inserted in the proper place +under your current name. +.pp +If you quit the game, you get out with all of your gold intact. +If, however, you get killed in the Dungeons of Doom, +your body is forwarded to your next-of-kin, +along with 90% of your gold; +ten percent of your gold is kept by the Dungeons' wizard as a fee\**. +.(f +\** The Dungeon's wizard is named Wally the Wonder Badger. +Invocations should be accompanied by a sizable donation. +.)f +This should make you consider whether you want to take one last hit +at that monster and possibly live, +or quit and thus stop with whatever you have. +If you quit, you do get all your gold, +but if you swing and live, you might find more. +.pp +If you just want to see what the current top players/games list is, +you can type +.ti +1i +.nf +% rogue \-s +.br +.sh 1 Acknowledgements +.pp +Rogue was originally conceived of by Glenn Wichman and Michael Toy. +Ken Arnold and Michael Toy then smoothed out the user interface, +and added jillions of new features. +We would like to thank +Bob Arnold, +Michelle Busch, +Andy Hatcher, +Kipp Hickman, +Mark Horton, +Daniel Jensen, +Bill Joy, +Joe Kalash, +Steve Maurer, +Marty McNary, +Jan Miller, +and +Scott Nelson +for their ideas and assistance; +and also the teeming multitudes +who graciously ignored work, school, and social life to play rogue +and send us bugs, complaints, suggestions, and just plain flames. +And also Mom. +.pp +The public domain version of rogue now distributed with Berkeley UNIX +was written by Timothy Stoehr. diff --git a/games/rogue/hit.c b/games/rogue/hit.c new file mode 100644 index 000000000..9d27c1fec --- /dev/null +++ b/games/rogue/hit.c @@ -0,0 +1,466 @@ +/* $NetBSD: hit.c,v 1.10 2008/01/14 03:50:01 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hit.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: hit.c,v 1.10 2008/01/14 03:50:01 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * hit.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static int damage_for_strength(void); +static int get_w_damage(const object *); +static int to_hit(const object *); + +static object *fight_monster = NULL; +char hit_message[HIT_MESSAGE_SIZE] = ""; + +void +mon_hit(object *monster) +{ + short damage, hit_chance; + const char *mn; + float minus; + + if (fight_monster && (monster != fight_monster)) { + fight_monster = 0; + } + monster->trow = NO_ROOM; + if (cur_level >= (AMULET_LEVEL * 2)) { + hit_chance = 100; + } else { + hit_chance = monster->m_hit_chance; + hit_chance -= (((2 * rogue.exp) + (2 * ring_exp)) - r_rings); + } + if (wizard) { + hit_chance /= 2; + } + if (!fight_monster) { + interrupted = 1; + } + mn = mon_name(monster); + + if (!rand_percent(hit_chance)) { + if (!fight_monster) { + messagef(1, "%sthe %s misses", hit_message, mn); + hit_message[0] = 0; + } + return; + } + if (!fight_monster) { + messagef(1, "%sthe %s hit", hit_message, mn); + hit_message[0] = 0; + } + if (!(monster->m_flags & STATIONARY)) { + damage = get_damage(monster->m_damage, 1); + if (cur_level >= (AMULET_LEVEL * 2)) { + minus = (float)((AMULET_LEVEL * 2) - cur_level); + } else { + minus = (float)get_armor_class(rogue.armor) * 3.00; + minus = minus/100.00 * (float)damage; + } + damage -= (short)minus; + } else { + damage = monster->stationary_damage++; + } + if (wizard) { + damage /= 3; + } + if (damage > 0) { + rogue_damage(damage, monster, 0); + } + if (monster->m_flags & SPECIAL_HIT) { + special_hit(monster); + } +} + +void +rogue_hit(object *monster, boolean force_hit) +{ + short damage, hit_chance; + + if (monster) { + if (check_imitator(monster)) { + return; + } + hit_chance = force_hit ? 100 : get_hit_chance(rogue.weapon); + + if (wizard) { + hit_chance *= 2; + } + if (!rand_percent(hit_chance)) { + if (!fight_monster) { + (void)strlcpy(hit_message, "you miss ", + sizeof(hit_message)); + } + goto RET; + } + damage = get_weapon_damage(rogue.weapon); + if (wizard) { + damage *= 3; + } + if (con_mon) { + s_con_mon(monster); + } + if (mon_damage(monster, damage)) { /* still alive? */ + if (!fight_monster) { + (void)strlcpy(hit_message, "you hit ", + sizeof(hit_message)); + } + } +RET: check_gold_seeker(monster); + wake_up(monster); + } +} + +void +rogue_damage(short d, object *monster, short other) +{ + if (d >= rogue.hp_current) { + rogue.hp_current = 0; + print_stats(STAT_HP); + killed_by(monster, other); + } + if (d > 0) { + rogue.hp_current -= d; + print_stats(STAT_HP); + } +} + +int +get_damage(const char *ds, boolean r) +{ + int i = 0, j, n, d, total = 0; + + while (ds[i]) { + n = get_number(ds+i); + while ((ds[i] != 'd') && ds[i]) { + i++; + } + if (ds[i] == 'd') { + i++; + } + + d = get_number(ds+i); + while ((ds[i] != '/') && ds[i]) { + i++; + } + if (ds[i] == '/') { + i++; + } + + for (j = 0; j < n; j++) { + if (r) { + total += get_rand(1, d); + } else { + total += d; + } + } + } + return(total); +} + +static int +get_w_damage(const object *obj) +{ + char new_damage[32]; + int tmp_to_hit, tmp_damage; + int i = 0; + + if ((!obj) || (obj->what_is != WEAPON)) { + return(-1); + } + tmp_to_hit = get_number(obj->damage) + obj->hit_enchant; + while ((obj->damage[i] != 'd') && obj->damage[i]) { + i++; + } + if (obj->damage[i] == 'd') { + i++; + } + tmp_damage = get_number(obj->damage + i) + obj->d_enchant; + + snprintf(new_damage, sizeof(new_damage), "%dd%d", + tmp_to_hit, tmp_damage); + + return(get_damage(new_damage, 1)); +} + +int +get_number(const char *s) +{ + int i = 0; + int total = 0; + + while ((s[i] >= '0') && (s[i] <= '9')) { + total = (10 * total) + (s[i] - '0'); + i++; + } + return(total); +} + +long +lget_number(const char *s) +{ + short i = 0; + long total = 0; + + while ((s[i] >= '0') && (s[i] <= '9')) { + total = (10 * total) + (s[i] - '0'); + i++; + } + return(total); +} + +static int +to_hit(const object *obj) +{ + if (!obj) { + return(1); + } + return(get_number(obj->damage) + obj->hit_enchant); +} + +static int +damage_for_strength(void) +{ + short strength; + + strength = rogue.str_current + add_strength; + + if (strength <= 6) { + return(strength-5); + } + if (strength <= 14) { + return(1); + } + if (strength <= 17) { + return(3); + } + if (strength <= 18) { + return(4); + } + if (strength <= 20) { + return(5); + } + if (strength <= 21) { + return(6); + } + if (strength <= 30) { + return(7); + } + return(8); +} + +int +mon_damage(object *monster, short damage) +{ + const char *mn; + short row, col; + + monster->hp_to_kill -= damage; + + if (monster->hp_to_kill <= 0) { + row = monster->row; + col = monster->col; + dungeon[row][col] &= ~MONSTER; + mvaddch(row, col, get_dungeon_char(row, col)); + + fight_monster = 0; + cough_up(monster); + mn = mon_name(monster); + messagef(1, "%sdefeated the %s", hit_message, mn); + hit_message[0] = 0; + add_exp(monster->kill_exp, 1); + take_from_pack(monster, &level_monsters); + + if (monster->m_flags & HOLDS) { + being_held = 0; + } + free_object(monster); + return(0); + } + return(1); +} + +void +fight(boolean to_the_death) +{ + short ch, c, d; + short row, col; + boolean first_miss = 1; + short possible_damage; + object *monster; + + ch = 0; + while (!is_direction(ch = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction?"); + first_miss = 0; + } + } + check_message(); + if (ch == CANCEL) { + return; + } + row = rogue.row; col = rogue.col; + get_dir_rc(d, &row, &col, 0); + + c = mvinch(row, col); + if (((c < 'A') || (c > 'Z')) || + (!can_move(rogue.row, rogue.col, row, col))) { + messagef(0, "I see no monster there"); + return; + } + if (!(fight_monster = object_at(&level_monsters, row, col))) { + return; + } + if (!(fight_monster->m_flags & STATIONARY)) { + possible_damage = ((get_damage(fight_monster->m_damage, 0) * 2) / 3); + } else { + possible_damage = fight_monster->stationary_damage - 1; + } + while (fight_monster) { + (void)one_move_rogue(ch, 0); + if (((!to_the_death) && (rogue.hp_current <= possible_damage)) || + interrupted || (!(dungeon[row][col] & MONSTER))) { + fight_monster = 0; + } else { + monster = object_at(&level_monsters, row, col); + if (monster != fight_monster) { + fight_monster = 0; + } + } + } +} + +void +get_dir_rc(short dir, short *row, short *col, short allow_off_screen) +{ + switch(dir) { + case LEFT: + if (allow_off_screen || (*col > 0)) { + (*col)--; + } + break; + case DOWN: + if (allow_off_screen || (*row < (DROWS-2))) { + (*row)++; + } + break; + case UPWARD: + if (allow_off_screen || (*row > MIN_ROW)) { + (*row)--; + } + break; + case RIGHT: + if (allow_off_screen || (*col < (DCOLS-1))) { + (*col)++; + } + break; + case UPLEFT: + if (allow_off_screen || ((*row > MIN_ROW) && (*col > 0))) { + (*row)--; + (*col)--; + } + break; + case UPRIGHT: + if (allow_off_screen || ((*row > MIN_ROW) && (*col < (DCOLS-1)))) { + (*row)--; + (*col)++; + } + break; + case DOWNRIGHT: + if (allow_off_screen || ((*row < (DROWS-2)) && (*col < (DCOLS-1)))) { + (*row)++; + (*col)++; + } + break; + case DOWNLEFT: + if (allow_off_screen || ((*row < (DROWS-2)) && (*col > 0))) { + (*row)++; + (*col)--; + } + break; + } +} + +int +get_hit_chance(const object *weapon) +{ + short hit_chance; + + hit_chance = 40; + hit_chance += 3 * to_hit(weapon); + hit_chance += (((2 * rogue.exp) + (2 * ring_exp)) - r_rings); + return(hit_chance); +} + +int +get_weapon_damage(const object *weapon) +{ + short damage; + + damage = get_w_damage(weapon); + damage += damage_for_strength(); + damage += ((((rogue.exp + ring_exp) - r_rings) + 1) / 2); + return(damage); +} + +void +s_con_mon(object *monster) +{ + if (con_mon) { + monster->m_flags |= CONFUSED; + monster->moves_confused += get_rand(12, 22); + messagef(0, "the monster appears confused"); + con_mon = 0; + } +} diff --git a/games/rogue/init.c b/games/rogue/init.c new file mode 100644 index 000000000..982a95cb8 --- /dev/null +++ b/games/rogue/init.c @@ -0,0 +1,366 @@ +/* $NetBSD: init.c,v 1.18 2008/08/08 16:10:47 drochner Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)init.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: init.c,v 1.18 2008/08/08 16:10:47 drochner Exp $"); +#endif +#endif /* not lint */ + +/* + * init.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include +#include + +#include "rogue.h" + +static void do_args(int, char **); +static void do_opts(void); +static void env_get_value(char **, char *, boolean); +static void init_str(char **, const char *); +static void player_init(void); + +static char *rest_file = NULL; +static boolean init_curses = 0; + +char login_name[MAX_OPT_LEN]; +char *nick_name = NULL; +boolean cant_int = 0; +boolean did_int = 0; +boolean score_only; +boolean save_is_interactive = 1; +boolean ask_quit = 1; +boolean no_skull = 0; +boolean passgo = 0; +const char *error_file = "rogue.esave"; +const char *byebye_string = "Okay, bye bye!"; +gid_t gid, egid; + +int +init(int argc, char *argv[]) +{ + const char *pn; + int seed; + int fd; + + gid = getgid(); + egid = getegid(); + setegid(gid); + /* Check for dirty tricks with closed fds 0, 1, 2 */ + fd = open("/dev/null", O_RDONLY); + if (fd < 3) + exit(1); + close(fd); + + seed = 0; + pn = md_gln(); + if ((!pn) || (strlen(pn) >= MAX_OPT_LEN)) { + clean_up("Hey! Who are you?"); + } + /* LOGIN_NAME_SIZE == MAX_OPT_LEN now, but just in case... */ + (void)strlcpy(login_name, pn, sizeof(login_name)); + + do_args(argc, argv); + do_opts(); + + if (!score_only && !rest_file) { + printf("Hello %s, just a moment while I dig the dungeon...", + nick_name); + fflush(stdout); + } + + if (!initscr()) { + fprintf(stderr, "couldn't initialize screen\n"); + exit (0); + } + if ((LINES < DROWS) || (COLS < DCOLS)) { + clean_up("must be played on at least 80 x 24 screen"); + } + start_window(); + init_curses = 1; + + md_heed_signals(); + + if (score_only) { + put_scores(NULL, 0); + } + seed = md_gseed(); + (void)srrandom(seed); + if (rest_file) { + restore(rest_file); + return(1); + } + mix_colors(); + get_wand_and_ring_materials(); + make_scroll_titles(); + + level_objects.next_object = NULL; + level_monsters.next_monster = NULL; + player_init(); + ring_stats(0); + return(0); +} + +static void +player_init(void) +{ + object *obj; + + rogue.pack.next_object = NULL; + + obj = alloc_object(); + get_food(obj, 1); + (void)add_to_pack(obj, &rogue.pack, 1); + + obj = alloc_object(); /* initial armor */ + obj->what_is = ARMOR; + obj->which_kind = RINGMAIL; + obj->class = RINGMAIL+2; + obj->is_protected = 0; + obj->d_enchant = 1; + (void)add_to_pack(obj, &rogue.pack, 1); + do_wear(obj); + + obj = alloc_object(); /* initial weapons */ + obj->what_is = WEAPON; + obj->which_kind = MACE; + obj->damage = "2d3"; + obj->hit_enchant = obj->d_enchant = 1; + obj->identified = 1; + (void)add_to_pack(obj, &rogue.pack, 1); + do_wield(obj); + + obj = alloc_object(); + obj->what_is = WEAPON; + obj->which_kind = BOW; + obj->damage = "1d2"; + obj->hit_enchant = 1; + obj->d_enchant = 0; + obj->identified = 1; + (void)add_to_pack(obj, &rogue.pack, 1); + + obj = alloc_object(); + obj->what_is = WEAPON; + obj->which_kind = ARROW; + obj->quantity = get_rand(25, 35); + obj->damage = "1d2"; + obj->hit_enchant = 0; + obj->d_enchant = 0; + obj->identified = 1; + (void)add_to_pack(obj, &rogue.pack, 1); +} + +void +clean_up(const char *estr) +{ + if (save_is_interactive) { + if (init_curses) { + move(DROWS-1, 0); + refresh(); + stop_window(); + } + printf("\n%s\n", estr); + } + md_exit(0); +} + +void +start_window(void) +{ + cbreak(); + noecho(); +#ifndef BAD_NONL + nonl(); +#endif +} + +void +stop_window(void) +{ + endwin(); +} + +void +byebye(int dummy __unused) +{ + md_ignore_signals(); + if (ask_quit) { + quit(1); + } else { + clean_up(byebye_string); + } + md_heed_signals(); +} + +void +onintr(int dummy __unused) +{ + md_ignore_signals(); + if (cant_int) { + did_int = 1; + } else { + check_message(); + messagef(1, "interrupt"); + } + md_heed_signals(); +} + +void +error_save(int dummy __unused) +{ + save_is_interactive = 0; + save_into_file(error_file); + clean_up(""); +} + +static void +do_args(int argc, char *argv[]) +{ + int i, j; + + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + for (j = 1; argv[i][j]; j++) { + switch(argv[i][j]) { + case 's': + score_only = 1; + break; + } + } + } else { + rest_file = argv[i]; + } + } +} + +static void +do_opts(void) +{ + char *eptr; + + if ((eptr = md_getenv("ROGUEOPTS")) != NULL) { + for (;;) { + while ((*eptr) == ' ') { + eptr++; + } + if (!(*eptr)) { + break; + } + if (!strncmp(eptr, "fruit=", 6)) { + eptr += 6; + env_get_value(&fruit, eptr, 1); + } else if (!strncmp(eptr, "file=", 5)) { + eptr += 5; + env_get_value(&save_file, eptr, 0); + } else if (!strncmp(eptr, "jump", 4)) { + jump = 1; + } else if (!strncmp(eptr, "name=", 5)) { + eptr += 5; + env_get_value(&nick_name, eptr, 0); + } else if (!strncmp(eptr, "noaskquit", 9)) { + ask_quit = 0; + } else if (!strncmp(eptr, "noskull", 5) || + !strncmp(eptr,"notomb", 6)) { + no_skull = 1; + } else if (!strncmp(eptr, "passgo", 5)) { + passgo = 1; + } + while ((*eptr) && (*eptr != ',')) { + eptr++; + } + if (!(*(eptr++))) { + break; + } + } + } + /* If some strings have not been set through ROGUEOPTS, assign defaults + * to them so that the options editor has data to work with. + */ + init_str(&nick_name, login_name); + init_str(&save_file, "rogue.save"); + init_str(&fruit, "slime-mold"); +} + +static void +env_get_value(char **s, char *e, boolean add_blank) +{ + short i = 0; + const char *t; + + t = e; + + while ((*e) && (*e != ',')) { + if (*e == ':') { + *e = ';'; /* ':' reserved for score file purposes */ + } + e++; + if (++i >= MAX_OPT_LEN) { + break; + } + } + /* note: edit_opts() in room.c depends on this being the right size */ + *s = md_malloc(MAX_OPT_LEN + 2); + if (*s == NULL) + clean_up("out of memory"); + (void)strncpy(*s, t, i); + if (add_blank) { + (*s)[i++] = ' '; + } + (*s)[i] = '\0'; +} + +static void +init_str(char **str, const char *dflt) +{ + if (!(*str)) { + /* note: edit_opts() in room.c depends on this size */ + *str = md_malloc(MAX_OPT_LEN + 2); + if (*str == NULL) + clean_up("out of memory"); + (void)strlcpy(*str, dflt, MAX_OPT_LEN + 2); + } +} diff --git a/games/rogue/inventory.c b/games/rogue/inventory.c new file mode 100644 index 000000000..934e8aa3f --- /dev/null +++ b/games/rogue/inventory.c @@ -0,0 +1,841 @@ +/* $NetBSD: inventory.c,v 1.15 2011/08/26 06:18:17 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)inventory.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: inventory.c,v 1.15 2011/08/26 06:18:17 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * inventory.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include +#include "rogue.h" + +boolean is_wood[WANDS]; +const char *press_space = " --press space to continue--"; + +static const char *const wand_materials[WAND_MATERIALS] = { + "steel ", + "bronze ", + "gold ", + "silver ", + "copper ", + "nickel ", + "cobalt ", + "tin ", + "iron ", + "magnesium ", + "chrome ", + "carbon ", + "platinum ", + "silicon ", + "titanium ", + + "teak ", + "oak ", + "cherry ", + "birch ", + "pine ", + "cedar ", + "redwood ", + "balsa ", + "ivory ", + "walnut ", + "maple ", + "mahogany ", + "elm ", + "palm ", + "wooden " +}; + +static const char *const gems[GEMS] = { + "diamond ", + "stibotantalite ", + "lapi-lazuli ", + "ruby ", + "emerald ", + "sapphire ", + "amethyst ", + "quartz ", + "tiger-eye ", + "opal ", + "agate ", + "turquoise ", + "pearl ", + "garnet " +}; + +static const char *const syllables[MAXSYLLABLES] = { + "blech ", + "foo ", + "barf ", + "rech ", + "bar ", + "blech ", + "quo ", + "bloto ", + "oh ", + "caca ", + "blorp ", + "erp ", + "festr ", + "rot ", + "slie ", + "snorf ", + "iky ", + "yuky ", + "ooze ", + "ah ", + "bahl ", + "zep ", + "druhl ", + "flem ", + "behil ", + "arek ", + "mep ", + "zihr ", + "grit ", + "kona ", + "kini ", + "ichi ", + "tims ", + "ogr ", + "oo ", + "ighr ", + "coph ", + "swerr ", + "mihln ", + "poxi " +}; + +#define COMS 48 + +struct id_com_s { + short com_char; + const char *com_desc; +}; + +static const struct id_com_s com_id_tab[COMS] = { + {'?', "? prints help"}, + {'r', "r read scroll"}, + {'/', "/ identify object"}, + {'e', "e eat food"}, + {'h', "h left "}, + {'w', "w wield a weapon"}, + {'j', "j down"}, + {'W', "W wear armor"}, + {'k', "k up"}, + {'T', "T take armor off"}, + {'l', "l right"}, + {'P', "P put on ring"}, + {'y', "y up & left"}, + {'R', "R remove ring"}, + {'u', "u up & right"}, + {'d', "d drop object"}, + {'b', "b down & left"}, + {'c', "c call object"}, + {'n', "n down & right"}, + {'\0', ": run that way"}, + {')', ") print current weapon"}, + {'\0', ": run till adjacent"}, + {']', "] print current armor"}, + {'f', "f fight till death or near death"}, + {'=', "= print current rings"}, + {'t', "t throw something"}, + {'\001', "^A print Hp-raise average"}, + {'m', "m move onto without picking up"}, + {'z', "z zap a wand in a direction"}, + {'o', "o examine/set options"}, + {'^', "^ identify trap type"}, + {'\022', "^R redraw screen"}, + {'&', "& save screen into 'rogue.screen'"}, + {'s', "s search for trap/secret door"}, + {'\020', "^P repeat last message"}, + {'>', "> go down a staircase"}, + {'\033', "^[ cancel command"}, + {'<', "< go up a staircase"}, + {'S', "S save game"}, + {'.', ". rest for a turn"}, + {'Q', "Q quit"}, + {',', ", pick something up"}, + {'!', "! shell escape"}, + {'i', "i inventory"}, + {'F', "F fight till either of you dies"}, + {'I', "I inventory single item"}, + {'v', "v print version number"}, + {'q', "q quaff potion" } +}; + +static int get_com_id(int *, short); +static int pr_com_id(int); +static int pr_motion_char(int); + +void +inventory(const object *pack, unsigned short mask) +{ + object *obj; + short i = 0, j; + size_t maxlen = 0, n; + short row, col; + + struct { + short letter; + short sepchar; + char desc[DCOLS]; + char savebuf[DCOLS+8]; + } descs[MAX_PACK_COUNT+1]; + + + obj = pack->next_object; + + if (!obj) { + messagef(0, "your pack is empty"); + return; + } + while (obj) { + if (obj->what_is & mask) { + descs[i].letter = obj->ichar; + descs[i].sepchar = ((obj->what_is & ARMOR) && obj->is_protected) + ? '}' : ')'; + get_desc(obj, descs[i].desc, sizeof(descs[i].desc)); + n = strlen(descs[i].desc) + 4; + if (n > maxlen) { + maxlen = n; + } + i++; + /*assert(i<=MAX_PACK_COUNT);*/ + } + obj = obj->next_object; + } + if (maxlen < 27) maxlen = 27; + if (maxlen > DCOLS-2) maxlen = DCOLS-2; + col = DCOLS - (maxlen + 2); + + for (row = 0; ((row <= i) && (row < DROWS)); row++) { + for (j = col; j < DCOLS; j++) { + descs[row].savebuf[j-col] = mvinch(row, j); + } + descs[row].savebuf[j-col] = 0; + if (row < i) { + mvprintw(row, col, " %c%c %s", + descs[row].letter, descs[row].sepchar, + descs[row].desc); + } + else { + mvaddstr(row, col, press_space); + } + clrtoeol(); + } + refresh(); + wait_for_ack(); + + move(0, 0); + clrtoeol(); + + for (j = 1; ((j <= i) && (j < DROWS)); j++) { + mvaddstr(j, col, descs[j].savebuf); + } +} + +void +id_com(void) +{ + int ch = 0; + short i, j, k; + + while (ch != CANCEL) { + check_message(); + messagef(0, "Character you want help for (* for all):"); + + refresh(); + ch = getchar(); + + switch(ch) { + case LIST: + { + char save[(((COMS / 2) + (COMS % 2)) + 1)][DCOLS]; + short rows = (((COMS / 2) + (COMS % 2)) + 1); + boolean need_two_screens = FALSE; + + if (rows > LINES) { + need_two_screens = 1; + rows = LINES; + } + k = 0; + + for (i = 0; i < rows; i++) { + for (j = 0; j < DCOLS; j++) { + save[i][j] = mvinch(i, j); + } + } +MORE: + for (i = 0; i < rows; i++) { + move(i, 0); + clrtoeol(); + } + for (i = 0; i < (rows-1); i++) { + if (i < (LINES-1)) { + if (((i + i) < COMS) && ((i+i+k) < COMS)) { + mvaddstr(i, 0, com_id_tab[i+i+k].com_desc); + } + if (((i + i + 1) < COMS) && ((i+i+k+1) < COMS)) { + mvaddstr(i, (DCOLS/2), + com_id_tab[i+i+k+1].com_desc); + } + } + } + mvaddstr(rows - 1, 0, need_two_screens ? more : press_space); + refresh(); + wait_for_ack(); + + if (need_two_screens) { + k += ((rows-1) * 2); + need_two_screens = 0; + goto MORE; + } + for (i = 0; i < rows; i++) { + move(i, 0); + for (j = 0; j < DCOLS; j++) { + addch(save[i][j]); + } + } + } + break; + default: + if (!pr_com_id(ch)) { + if (!pr_motion_char(ch)) { + check_message(); + messagef(0, "unknown character"); + } + } + ch = CANCEL; + break; + } + } +} + +static int +pr_com_id(int ch) +{ + int i; + + if (!get_com_id(&i, ch)) { + return(0); + } + check_message(); + messagef(0, "%s", com_id_tab[i].com_desc); + return(1); +} + +static int +get_com_id(int *indexp, short ch) +{ + short i; + + for (i = 0; i < COMS; i++) { + if (com_id_tab[i].com_char == ch) { + *indexp = i; + return(1); + } + } + return(0); +} + +static int +pr_motion_char(int ch) +{ + if ( (ch == 'J') || + (ch == 'K') || + (ch == 'L') || + (ch == 'H') || + (ch == 'Y') || + (ch == 'U') || + (ch == 'N') || + (ch == 'B') || + (ch == '\012') || + (ch == '\013') || + (ch == '\010') || + (ch == '\014') || + (ch == '\025') || + (ch == '\031') || + (ch == '\016') || + (ch == '\002')) { + const char *until; + int n = 0; /* XXX: GCC */ + if (ch <= '\031') { + ch += 96; + until = " until adjacent"; + } else { + ch += 32; + until = ""; + } + (void)get_com_id(&n, ch); + check_message(); + messagef(0, "run %s%s", com_id_tab[n].com_desc + 8, until); + return(1); + } else { + return(0); + } +} + +void +mix_colors(void) +{ + short i, j, k; + char t[MAX_ID_TITLE_LEN]; + + for (i = 0; i <= 32; i++) { + j = get_rand(0, (POTIONS - 1)); + k = get_rand(0, (POTIONS - 1)); + strlcpy(t, id_potions[j].title, sizeof(t)); + strlcpy(id_potions[j].title, id_potions[k].title, + sizeof(id_potions[j].title)); + strlcpy(id_potions[k].title, t, sizeof(id_potions[k].title)); + } +} + +void +make_scroll_titles(void) +{ + short i, j, n; + short sylls, s; + size_t maxlen = sizeof(id_scrolls[0].title); + + for (i = 0; i < SCROLS; i++) { + sylls = get_rand(2, 5); + (void)strlcpy(id_scrolls[i].title, "'", maxlen); + + for (j = 0; j < sylls; j++) { + s = get_rand(1, (MAXSYLLABLES-1)); + (void)strlcat(id_scrolls[i].title, syllables[s], + maxlen); + } + /* trim trailing space */ + n = strlen(id_scrolls[i].title); + id_scrolls[i].title[n-1] = 0; + + (void)strlcat(id_scrolls[i].title, "' ", maxlen); + } +} + +struct sbuf { + char *buf; + size_t maxlen; +}; + +static void sbuf_init(struct sbuf *s, char *buf, size_t maxlen); +static void sbuf_addstr(struct sbuf *s, const char *str); +static void sbuf_addf(struct sbuf *s, const char *fmt, ...) __printflike(2,3); +static void desc_count(struct sbuf *s, int n); +static void desc_called(struct sbuf *s, const object *); + +static +void +sbuf_init(struct sbuf *s, char *buf, size_t maxlen) +{ + s->buf = buf; + s->maxlen = maxlen; + /*assert(maxlen>0);*/ + s->buf[0] = 0; +} + +static +void +sbuf_addstr(struct sbuf *s, const char *str) +{ + strlcat(s->buf, str, s->maxlen); +} + +static +void +sbuf_addf(struct sbuf *s, const char *fmt, ...) +{ + va_list ap; + size_t initlen; + + initlen = strlen(s->buf); + va_start(ap, fmt); + vsnprintf(s->buf+initlen, s->maxlen-initlen, fmt, ap); + va_end(ap); +} + +static +void +desc_count(struct sbuf *s, int n) +{ + if (n == 1) { + sbuf_addstr(s, "an "); + } else { + sbuf_addf(s, "%d ", n); + } +} + +static +void +desc_called(struct sbuf *s, const object *obj) +{ + struct id *id_table; + + id_table = get_id_table(obj); + sbuf_addstr(s, name_of(obj)); + sbuf_addstr(s, "called "); + sbuf_addstr(s, id_table[obj->which_kind].title); +} + +void +get_desc(const object *obj, char *desc, size_t desclen) +{ + const char *item_name; + struct id *id_table; + struct sbuf db; + unsigned short objtype_id_status; + + if (obj->what_is == AMULET) { + (void)strlcpy(desc, "the amulet of Yendor ", desclen); + return; + } + + if (obj->what_is == GOLD) { + snprintf(desc, desclen, "%d pieces of gold", obj->quantity); + return; + } + + item_name = name_of(obj); + id_table = get_id_table(obj); + if (wizard || id_table == NULL) { + objtype_id_status = IDENTIFIED; + } + else { + objtype_id_status = id_table[obj->which_kind].id_status; + } + if (obj->what_is & (WEAPON | ARMOR | WAND | RING)) { + if (obj->identified) { + objtype_id_status = IDENTIFIED; + } + } + + sbuf_init(&db, desc, desclen); + + switch (obj->what_is) { + case FOOD: + if (obj->which_kind == RATION) { + if (obj->quantity > 1) { + sbuf_addf(&db, + "%d rations of %s", obj->quantity, + item_name); + } else { + sbuf_addf(&db, "some %s", item_name); + } + } else { + sbuf_addf(&db, "an %s", item_name); + } + break; + case SCROL: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, "entitled: "); + sbuf_addstr(&db, id_table[obj->which_kind].title); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + } + break; + case POTION: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + sbuf_addstr(&db, item_name); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + } + break; + case WAND: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + sbuf_addstr(&db, item_name); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + if (wizard || obj->identified) { + sbuf_addf(&db, "[%d]", obj->class); + } + } + break; + case RING: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + sbuf_addstr(&db, item_name); + } else if (objtype_id_status==CALLED) { + desc_called(&db, obj); + } else { + if ((wizard || obj->identified) && + (obj->which_kind == DEXTERITY || + obj->which_kind == ADD_STRENGTH)) { + sbuf_addf(&db, "%+d ", obj->class); + } + sbuf_addstr(&db, item_name); + sbuf_addstr(&db, id_table[obj->which_kind].real); + } + break; + case ARMOR: + /* no desc_count() */ + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, id_table[obj->which_kind].title); + } else { + sbuf_addf(&db, "%+d %s[%d] ", obj->d_enchant, + id_table[obj->which_kind].title, + get_armor_class(obj)); + } + break; + case WEAPON: + desc_count(&db, obj->quantity); + if (objtype_id_status==UNIDENTIFIED) { + sbuf_addstr(&db, name_of(obj)); + } else { + sbuf_addf(&db, "%+d,%+d %s", + obj->hit_enchant, obj->d_enchant, + name_of(obj)); + } + break; + } + + if (obj->in_use_flags & BEING_WIELDED) { + sbuf_addstr(&db, "in hand"); + } else if (obj->in_use_flags & BEING_WORN) { + sbuf_addstr(&db, "being worn"); + } else if (obj->in_use_flags & ON_LEFT_HAND) { + sbuf_addstr(&db, "on left hand"); + } else if (obj->in_use_flags & ON_RIGHT_HAND) { + sbuf_addstr(&db, "on right hand"); + } + + if (!strncmp(db.buf, "an ", 3)) { + if (!is_vowel(db.buf[3])) { + memmove(db.buf+2, db.buf+3, strlen(db.buf+3)+1); + db.buf[1] = ' '; + } + } +} + +void +get_wand_and_ring_materials(void) +{ + short i, j; + boolean used[WAND_MATERIALS]; + + for (i = 0; i < WAND_MATERIALS; i++) { + used[i] = 0; + } + for (i = 0; i < WANDS; i++) { + do { + j = get_rand(0, WAND_MATERIALS-1); + } while (used[j]); + used[j] = 1; + (void)strlcpy(id_wands[i].title, wand_materials[j], + sizeof(id_wands[i].title)); + is_wood[i] = (j > MAX_METAL); + } + for (i = 0; i < GEMS; i++) { + used[i] = 0; + } + for (i = 0; i < RINGS; i++) { + do { + j = get_rand(0, GEMS-1); + } while (used[j]); + used[j] = 1; + (void)strlcpy(id_rings[i].title, gems[j], + sizeof(id_rings[i].title)); + } +} + +void +single_inv(short ichar) +{ + short ch, ch2; + char desc[DCOLS]; + object *obj; + + ch = ichar ? ichar : pack_letter("inventory what?", ALL_OBJECTS); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + ch2 = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')'; + get_desc(obj, desc, sizeof(desc)); + messagef(0, "%c%c %s", ch, ch2, desc); +} + +struct id * +get_id_table(const object *obj) +{ + switch(obj->what_is) { + case SCROL: + return(id_scrolls); + case POTION: + return(id_potions); + case WAND: + return(id_wands); + case RING: + return(id_rings); + case WEAPON: + return(id_weapons); + case ARMOR: + return(id_armors); + } + return((struct id *)0); +} + +void +inv_armor_weapon(boolean is_weapon) +{ + if (is_weapon) { + if (rogue.weapon) { + single_inv(rogue.weapon->ichar); + } else { + messagef(0, "not wielding anything"); + } + } else { + if (rogue.armor) { + single_inv(rogue.armor->ichar); + } else { + messagef(0, "not wearing anything"); + } + } +} + +void +id_type(void) +{ + const char *id; + int ch; + + messagef(0, "what do you want identified?"); + + ch = rgetchar(); + + if ((ch >= 'A') && (ch <= 'Z')) { + id = m_names[ch-'A']; + } else if (ch < 32) { + check_message(); + return; + } else { + switch(ch) { + case '@': + id = "you"; + break; + case '%': + id = "staircase"; + break; + case '^': + id = "trap"; + break; + case '+': + id = "door"; + break; + case '-': + case '|': + id = "wall of a room"; + break; + case '.': + id = "floor"; + break; + case '#': + id = "passage"; + break; + case ' ': + id = "solid rock"; + break; + case '=': + id = "ring"; + break; + case '?': + id = "scroll"; + break; + case '!': + id = "potion"; + break; + case '/': + id = "wand or staff"; + break; + case ')': + id = "weapon"; + break; + case ']': + id = "armor"; + break; + case '*': + id = "gold"; + break; + case ':': + id = "food"; + break; + case ',': + id = "the Amulet of Yendor"; + break; + default: + id = "unknown character"; + break; + } + } + check_message(); + messagef(0, "'%c': %s", ch, id); +} diff --git a/games/rogue/level.c b/games/rogue/level.c new file mode 100644 index 000000000..4b95277bc --- /dev/null +++ b/games/rogue/level.c @@ -0,0 +1,900 @@ +/* $NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)level.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: level.c,v 1.10 2008/01/14 03:50:01 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * level.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +#define SWAP(x,y) (t = (x), (x) = (y), (y) = t) + +static void add_mazes(void); +static int connect_rooms(short, short); +static void draw_simple_passage(short, short, short, short, short); +static void fill_it(int, boolean); +static void fill_out_level(void); +static int get_exp_level(long); +static void hide_boxed_passage(short, short, short, short, short); +static void make_maze(short, short, short, short, short, short); +static void make_room(short, short, short, short); +static boolean mask_room(short, short *, short *, unsigned short); +static void mix_random_rooms(void); +static void put_door(room *, short, short *, short *); +static void recursive_deadend(short, const short *, short, short); +static int same_col(int, int); +static int same_row(int, int); + +short cur_level = 0; +short max_level = 1; +short cur_room; +const char *new_level_message = NULL; +short party_room = NO_ROOM; + +static short r_de; + +const long level_points[MAX_EXP_LEVEL] = { + 10L, + 20L, + 40L, + 80L, + 160L, + 320L, + 640L, + 1300L, + 2600L, + 5200L, + 10000L, + 20000L, + 40000L, + 80000L, + 160000L, + 320000L, + 1000000L, + 3333333L, + 6666666L, + MAX_EXP, + 99900000L +}; + +static short random_rooms[MAXROOMS] = {3, 7, 5, 2, 0, 6, 1, 4, 8}; + +void +make_level(void) +{ + short i, j; + short must_1, must_2, must_3; + boolean big_room; + + must_2 = must_3 = 0; + if (cur_level < LAST_DUNGEON) { + cur_level++; + } + if (cur_level > max_level) { + max_level = cur_level; + } + must_1 = get_rand(0, 5); + + switch(must_1) { + case 0: + must_1 = 0; + must_2 = 1; + must_3 = 2; + break; + case 1: + must_1 = 3; + must_2 = 4; + must_3 = 5; + break; + case 2: + must_1 = 6; + must_2 = 7; + must_3 = 8; + break; + case 3: + must_1 = 0; + must_2 = 3; + must_3 = 6; + break; + case 4: + must_1 = 1; + must_2 = 4; + must_3 = 7; + break; + case 5: + must_1 = 2; + must_2 = 5; + must_3 = 8; + break; + } + if (rand_percent(8)) { + party_room = 0; + } + big_room = ((party_room != NO_ROOM) && rand_percent(1)); + if (big_room) { + make_room(BIG_ROOM, 0, 0, 0); + } else { + for (i = 0; i < MAXROOMS; i++) { + make_room(i, must_1, must_2, must_3); + } + } + if (!big_room) { + add_mazes(); + + mix_random_rooms(); + + for (j = 0; j < MAXROOMS; j++) { + + i = random_rooms[j]; + + if (i < (MAXROOMS-1)) { + (void)connect_rooms(i, i+1); + } + if (i < (MAXROOMS-3)) { + (void)connect_rooms(i, i+3); + } + if (i < (MAXROOMS-2)) { + if (rooms[i+1].is_room & R_NOTHING) { + if (connect_rooms(i, i+2)) { + rooms[i+1].is_room = R_CROSS; + } + } + } + if (i < (MAXROOMS-6)) { + if (rooms[i+3].is_room & R_NOTHING) { + if (connect_rooms(i, i+6)) { + rooms[i+3].is_room = R_CROSS; + } + } + } + if (is_all_connected()) { + break; + } + } + fill_out_level(); + } + if (!has_amulet() && (cur_level >= AMULET_LEVEL)) { + put_amulet(); + } +} + +static void +make_room(short rn, short r1, short r2, short r3) +{ + short left_col, right_col, top_row, bottom_row; + short width, height; + short row_offset, col_offset; + short i, j, ch; + + left_col = right_col = top_row = bottom_row = 0; + switch(rn) { + case 0: + left_col = 0; + right_col = COL1-1; + top_row = MIN_ROW; + bottom_row = ROW1-1; + break; + case 1: + left_col = COL1+1; + right_col = COL2-1; + top_row = MIN_ROW; + bottom_row = ROW1-1; + break; + case 2: + left_col = COL2+1; + right_col = DCOLS-1; + top_row = MIN_ROW; + bottom_row = ROW1-1; + break; + case 3: + left_col = 0; + right_col = COL1-1; + top_row = ROW1+1; + bottom_row = ROW2-1; + break; + case 4: + left_col = COL1+1; + right_col = COL2-1; + top_row = ROW1+1; + bottom_row = ROW2-1; + break; + case 5: + left_col = COL2+1; + right_col = DCOLS-1; + top_row = ROW1+1; + bottom_row = ROW2-1; + break; + case 6: + left_col = 0; + right_col = COL1-1; + top_row = ROW2+1; + bottom_row = DROWS - 2; + break; + case 7: + left_col = COL1+1; + right_col = COL2-1; + top_row = ROW2+1; + bottom_row = DROWS - 2; + break; + case 8: + left_col = COL2+1; + right_col = DCOLS-1; + top_row = ROW2+1; + bottom_row = DROWS - 2; + break; + case BIG_ROOM: + top_row = get_rand(MIN_ROW, MIN_ROW+5); + bottom_row = get_rand(DROWS-7, DROWS-2); + left_col = get_rand(0, 10); + right_col = get_rand(DCOLS-11, DCOLS-1); + rn = 0; + goto B; + } + height = get_rand(4, (bottom_row - top_row + 1)); + width = get_rand(7, (right_col - left_col - 2)); + + row_offset = get_rand(0, ((bottom_row - top_row) - height + 1)); + col_offset = get_rand(0, ((right_col - left_col) - width + 1)); + + top_row += row_offset; + bottom_row = top_row + height - 1; + + left_col += col_offset; + right_col = left_col + width - 1; + + if ((rn != r1) && (rn != r2) && (rn != r3) && rand_percent(40)) { + goto END; + } +B: + rooms[rn].is_room = R_ROOM; + + for (i = top_row; i <= bottom_row; i++) { + for (j = left_col; j <= right_col; j++) { + if ((i == top_row) || (i == bottom_row)) { + ch = HORWALL; + } else if ( ((i != top_row) && (i != bottom_row)) && + ((j == left_col) || (j == right_col))) { + ch = VERTWALL; + } else { + ch = FLOOR; + } + dungeon[i][j] = ch; + } + } +END: + rooms[rn].top_row = top_row; + rooms[rn].bottom_row = bottom_row; + rooms[rn].left_col = left_col; + rooms[rn].right_col = right_col; +} + +static int +connect_rooms(short room1, short room2) +{ + short row1, col1, row2, col2, dir; + + if ((!(rooms[room1].is_room & (R_ROOM | R_MAZE))) || + (!(rooms[room2].is_room & (R_ROOM | R_MAZE)))) { + return(0); + } + if (same_row(room1, room2) && + (rooms[room1].left_col > rooms[room2].right_col)) { + put_door(&rooms[room1], LEFT, &row1, &col1); + put_door(&rooms[room2], RIGHT, &row2, &col2); + dir = LEFT; + } else if (same_row(room1, room2) && + (rooms[room2].left_col > rooms[room1].right_col)) { + put_door(&rooms[room1], RIGHT, &row1, &col1); + put_door(&rooms[room2], LEFT, &row2, &col2); + dir = RIGHT; + } else if (same_col(room1, room2) && + (rooms[room1].top_row > rooms[room2].bottom_row)) { + put_door(&rooms[room1], UPWARD, &row1, &col1); + put_door(&rooms[room2], DOWN, &row2, &col2); + dir = UPWARD; + } else if (same_col(room1, room2) && + (rooms[room2].top_row > rooms[room1].bottom_row)) { + put_door(&rooms[room1], DOWN, &row1, &col1); + put_door(&rooms[room2], UPWARD, &row2, &col2); + dir = DOWN; + } else { + return(0); + } + + do { + draw_simple_passage(row1, col1, row2, col2, dir); + } while (rand_percent(4)); + + rooms[room1].doors[dir/2].oth_room = room2; + rooms[room1].doors[dir/2].oth_row = row2; + rooms[room1].doors[dir/2].oth_col = col2; + + rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_room = room1; + rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_row = row1; + rooms[room2].doors[(((dir+4)%DIRS)/2)].oth_col = col1; + return(1); +} + +void +clear_level(void) +{ + short i, j; + + for (i = 0; i < MAXROOMS; i++) { + rooms[i].is_room = R_NOTHING; + for (j = 0; j < 4; j++) { + rooms[i].doors[j].oth_room = NO_ROOM; + } + } + + for (i = 0; i < MAX_TRAPS; i++) { + traps[i].trap_type = NO_TRAP; + } + for (i = 0; i < DROWS; i++) { + for (j = 0; j < DCOLS; j++) { + dungeon[i][j] = NOTHING; + } + } + detect_monster = see_invisible = 0; + being_held = bear_trap = 0; + party_room = NO_ROOM; + rogue.row = rogue.col = -1; + clear(); +} + +static void +put_door(room *rm, short dir, short *row, short *col) +{ + short wall_width; + + wall_width = (rm->is_room & R_MAZE) ? 0 : 1; + + switch(dir) { + case UPWARD: + case DOWN: + *row = ((dir == UPWARD) ? rm->top_row : rm->bottom_row); + do { + *col = get_rand(rm->left_col+wall_width, + rm->right_col-wall_width); + } while (!(dungeon[*row][*col] & (HORWALL | TUNNEL))); + break; + case RIGHT: + case LEFT: + *col = (dir == LEFT) ? rm->left_col : rm->right_col; + do { + *row = get_rand(rm->top_row+wall_width, + rm->bottom_row-wall_width); + } while (!(dungeon[*row][*col] & (VERTWALL | TUNNEL))); + break; + } + if (rm->is_room & R_ROOM) { + dungeon[*row][*col] = DOOR; + } + if ((cur_level > 2) && rand_percent(HIDE_PERCENT)) { + dungeon[*row][*col] |= HIDDEN; + } + rm->doors[dir/2].door_row = *row; + rm->doors[dir/2].door_col = *col; +} + +static void +draw_simple_passage(short row1, short col1, short row2, short col2, short dir) +{ + short i, middle, t; + + if ((dir == LEFT) || (dir == RIGHT)) { + if (col1 > col2) { + SWAP(row1, row2); + SWAP(col1, col2); + } + middle = get_rand(col1+1, col2-1); + for (i = col1+1; i != middle; i++) { + dungeon[row1][i] = TUNNEL; + } + for (i = row1; i != row2; i += (row1 > row2) ? -1 : 1) { + dungeon[i][middle] = TUNNEL; + } + for (i = middle; i != col2; i++) { + dungeon[row2][i] = TUNNEL; + } + } else { + if (row1 > row2) { + SWAP(row1, row2); + SWAP(col1, col2); + } + middle = get_rand(row1+1, row2-1); + for (i = row1+1; i != middle; i++) { + dungeon[i][col1] = TUNNEL; + } + for (i = col1; i != col2; i += (col1 > col2) ? -1 : 1) { + dungeon[middle][i] = TUNNEL; + } + for (i = middle; i != row2; i++) { + dungeon[i][col2] = TUNNEL; + } + } + if (rand_percent(HIDE_PERCENT)) { + hide_boxed_passage(row1, col1, row2, col2, 1); + } +} + +static int +same_row(int room1, int room2) +{ + return((room1 / 3) == (room2 / 3)); +} + +static int +same_col(int room1, int room2) +{ + return((room1 % 3) == (room2 % 3)); +} + +static void +add_mazes(void) +{ + short i, j; + short start; + short maze_percent; + + if (cur_level > 1) { + start = get_rand(0, (MAXROOMS-1)); + maze_percent = (cur_level * 5) / 4; + + if (cur_level > 15) { + maze_percent += cur_level; + } + for (i = 0; i < MAXROOMS; i++) { + j = ((start + i) % MAXROOMS); + if (rooms[j].is_room & R_NOTHING) { + if (rand_percent(maze_percent)) { + rooms[j].is_room = R_MAZE; + make_maze(get_rand(rooms[j].top_row+1, rooms[j].bottom_row-1), + get_rand(rooms[j].left_col+1, rooms[j].right_col-1), + rooms[j].top_row, rooms[j].bottom_row, + rooms[j].left_col, rooms[j].right_col); + hide_boxed_passage(rooms[j].top_row, rooms[j].left_col, + rooms[j].bottom_row, rooms[j].right_col, + get_rand(0, 2)); + } + } + } + } +} + +static void +fill_out_level(void) +{ + short i, rn; + + mix_random_rooms(); + + r_de = NO_ROOM; + + for (i = 0; i < MAXROOMS; i++) { + rn = random_rooms[i]; + if ((rooms[rn].is_room & R_NOTHING) || + ((rooms[rn].is_room & R_CROSS) && coin_toss())) { + fill_it(rn, 1); + } + } + if (r_de != NO_ROOM) { + fill_it(r_de, 0); + } +} + +static void +fill_it(int rn, boolean do_rec_de) +{ + short i, tunnel_dir, door_dir, drow, dcol; + short target_room, rooms_found = 0; + short srow, scol, t; + static short offsets[4] = {-1, 1, 3, -3}; + boolean did_this = 0; + + for (i = 0; i < 10; i++) { + srow = get_rand(0, 3); + scol = get_rand(0, 3); + t = offsets[srow]; + offsets[srow] = offsets[scol]; + offsets[scol] = t; + } + for (i = 0; i < 4; i++) { + + target_room = rn + offsets[i]; + + if (((target_room < 0) || (target_room >= MAXROOMS)) || + (!(same_row(rn,target_room) || same_col(rn,target_room))) || + (!(rooms[target_room].is_room & (R_ROOM | R_MAZE)))) { + continue; + } + if (same_row(rn, target_room)) { + tunnel_dir = (rooms[rn].left_col < rooms[target_room].left_col) ? + RIGHT : LEFT; + } else { + tunnel_dir = (rooms[rn].top_row < rooms[target_room].top_row) ? + DOWN : UPWARD; + } + door_dir = ((tunnel_dir + 4) % DIRS); + if (rooms[target_room].doors[door_dir/2].oth_room != NO_ROOM) { + continue; + } + if (((!do_rec_de) || did_this) || + (!mask_room(rn, &srow, &scol, TUNNEL))) { + srow = (rooms[rn].top_row + rooms[rn].bottom_row) / 2; + scol = (rooms[rn].left_col + rooms[rn].right_col) / 2; + } + put_door(&rooms[target_room], door_dir, &drow, &dcol); + rooms_found++; + draw_simple_passage(srow, scol, drow, dcol, tunnel_dir); + rooms[rn].is_room = R_DEADEND; + dungeon[srow][scol] = TUNNEL; + + if ((i < 3) && (!did_this)) { + did_this = 1; + if (coin_toss()) { + continue; + } + } + if ((rooms_found < 2) && do_rec_de) { + recursive_deadend(rn, offsets, srow, scol); + } + break; + } +} + +static void +recursive_deadend(short rn, const short *offsets, short srow, short scol) +{ + short i, de; + short drow, dcol, tunnel_dir; + + rooms[rn].is_room = R_DEADEND; + dungeon[srow][scol] = TUNNEL; + + for (i = 0; i < 4; i++) { + de = rn + offsets[i]; + if (((de < 0) || (de >= MAXROOMS)) || + (!(same_row(rn, de) || same_col(rn, de)))) { + continue; + } + if (!(rooms[de].is_room & R_NOTHING)) { + continue; + } + drow = (rooms[de].top_row + rooms[de].bottom_row) / 2; + dcol = (rooms[de].left_col + rooms[de].right_col) / 2; + if (same_row(rn, de)) { + tunnel_dir = (rooms[rn].left_col < rooms[de].left_col) ? + RIGHT : LEFT; + } else { + tunnel_dir = (rooms[rn].top_row < rooms[de].top_row) ? + DOWN : UPWARD; + } + draw_simple_passage(srow, scol, drow, dcol, tunnel_dir); + r_de = de; + recursive_deadend(de, offsets, drow, dcol); + } +} + +static boolean +mask_room(short rn, short *row, short *col, unsigned short mask) +{ + short i, j; + + for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { + if (dungeon[i][j] & mask) { + *row = i; + *col = j; + return(1); + } + } + } + return(0); +} + +static void +make_maze(short r, short c, short tr, short br, short lc, short rc) +{ + char dirs[4]; + short i, t; + + dirs[0] = UPWARD; + dirs[1] = DOWN; + dirs[2] = LEFT; + dirs[3] = RIGHT; + + dungeon[r][c] = TUNNEL; + + if (rand_percent(20)) { + for (i = 0; i < 10; i++) { + short t1, t2; + + t1 = get_rand(0, 3); + t2 = get_rand(0, 3); + + SWAP(dirs[t1], dirs[t2]); + } + } + for (i = 0; i < 4; i++) { + switch(dirs[i]) { + case UPWARD: + if (((r-1) >= tr) && + (dungeon[r-1][c] != TUNNEL) && + (dungeon[r-1][c-1] != TUNNEL) && + (dungeon[r-1][c+1] != TUNNEL) && + (dungeon[r-2][c] != TUNNEL)) { + make_maze((r-1), c, tr, br, lc, rc); + } + break; + case DOWN: + if (((r+1) <= br) && + (dungeon[r+1][c] != TUNNEL) && + (dungeon[r+1][c-1] != TUNNEL) && + (dungeon[r+1][c+1] != TUNNEL) && + (dungeon[r+2][c] != TUNNEL)) { + make_maze((r+1), c, tr, br, lc, rc); + } + break; + case LEFT: + if (((c-1) >= lc) && + (dungeon[r][c-1] != TUNNEL) && + (dungeon[r-1][c-1] != TUNNEL) && + (dungeon[r+1][c-1] != TUNNEL) && + (dungeon[r][c-2] != TUNNEL)) { + make_maze(r, (c-1), tr, br, lc, rc); + } + break; + case RIGHT: + if (((c+1) <= rc) && + (dungeon[r][c+1] != TUNNEL) && + (dungeon[r-1][c+1] != TUNNEL) && + (dungeon[r+1][c+1] != TUNNEL) && + (dungeon[r][c+2] != TUNNEL)) { + make_maze(r, (c+1), tr, br, lc, rc); + } + break; + } + } +} + +static void +hide_boxed_passage(short row1, short col1, short row2, short col2, short n) +{ + short i, j, t; + short row, col, row_cut, col_cut; + short h, w; + + if (cur_level > 2) { + if (row1 > row2) { + SWAP(row1, row2); + } + if (col1 > col2) { + SWAP(col1, col2); + } + h = row2 - row1; + w = col2 - col1; + + if ((w >= 5) || (h >= 5)) { + row_cut = ((h >= 2) ? 1 : 0); + col_cut = ((w >= 2) ? 1 : 0); + + for (i = 0; i < n; i++) { + for (j = 0; j < 10; j++) { + row = get_rand(row1 + row_cut, row2 - row_cut); + col = get_rand(col1 + col_cut, col2 - col_cut); + if (dungeon[row][col] == TUNNEL) { + dungeon[row][col] |= HIDDEN; + break; + } + } + } + } + } +} + +/* + * try not to put in room NR + */ +void +put_player(short nr) +{ + short rn = nr, misses; + short row, col; + + for (misses = 0; ((misses < 2) && (rn == nr)); misses++) { + gr_row_col(&row, &col, (FLOOR | TUNNEL | OBJECT | STAIRS)); + rn = get_room_number(row, col); + } + rogue.row = row; + rogue.col = col; + + if (dungeon[rogue.row][rogue.col] & TUNNEL) { + cur_room = PASSAGE; + } else { + cur_room = rn; + } + if (cur_room != PASSAGE) { + light_up_room(cur_room); + } else { + light_passage(rogue.row, rogue.col); + } + rn = get_room_number(rogue.row, rogue.col); + wake_room(rn, 1, rogue.row, rogue.col); + if (new_level_message) { + messagef(0, "%s", new_level_message); + new_level_message = NULL; + } + mvaddch(rogue.row, rogue.col, rogue.fchar); +} + +int +drop_check(void) +{ + if (wizard) { + return(1); + } + if (dungeon[rogue.row][rogue.col] & STAIRS) { + if (levitate) { + messagef(0, "you're floating in the air!"); + return(0); + } + return(1); + } + messagef(0, "I see no way down"); + return(0); +} + +int +check_up(void) +{ + if (!wizard) { + if (!(dungeon[rogue.row][rogue.col] & STAIRS)) { + messagef(0, "I see no way up"); + return(0); + } + if (!has_amulet()) { + messagef(0, "your way is magically blocked"); + return(0); + } + } + new_level_message = "you feel a wrenching sensation in your gut"; + if (cur_level == 1) { + win(); + } else { + cur_level -= 2; + return(1); + } + return(0); +} + +void +add_exp(int e, boolean promotion) +{ + short new_exp; + short i, hp; + + rogue.exp_points += e; + + if (rogue.exp_points >= level_points[rogue.exp-1]) { + new_exp = get_exp_level(rogue.exp_points); + if (rogue.exp_points > MAX_EXP) { + rogue.exp_points = MAX_EXP + 1; + } + for (i = rogue.exp+1; i <= new_exp; i++) { + messagef(0, "welcome to level %d", i); + if (promotion) { + hp = hp_raise(); + rogue.hp_current += hp; + rogue.hp_max += hp; + } + rogue.exp = i; + print_stats(STAT_HP | STAT_EXP); + } + } else { + print_stats(STAT_EXP); + } +} + +static int +get_exp_level(long e) +{ + short i; + + for (i = 0; i < (MAX_EXP_LEVEL - 1); i++) { + if (level_points[i] > e) { + break; + } + } + return(i+1); +} + +int +hp_raise(void) +{ + int hp; + + hp = (wizard ? 10 : get_rand(3, 10)); + return(hp); +} + +void +show_average_hp(void) +{ + float real_average; + float effective_average; + + if (rogue.exp == 1) { + real_average = effective_average = 0.00; + } else { + real_average = (float) + ((rogue.hp_max - extra_hp - INIT_HP) + less_hp) / (rogue.exp - 1); + effective_average = (float)(rogue.hp_max - INIT_HP) / (rogue.exp - 1); + + } + messagef(0, "R-Hp: %.2f, E-Hp: %.2f (!: %d, V: %d)", real_average, + effective_average, extra_hp, less_hp); +} + +static void +mix_random_rooms(void) +{ + short i, t; + short x, y; + + for (i = 0; i < (3 * MAXROOMS); i++) { + do { + x = get_rand(0, (MAXROOMS-1)); + y = get_rand(0, (MAXROOMS-1)); + } while (x == y); + SWAP(random_rooms[x], random_rooms[y]); + } +} diff --git a/games/rogue/machdep.c b/games/rogue/machdep.c new file mode 100644 index 000000000..e6d6292de --- /dev/null +++ b/games/rogue/machdep.c @@ -0,0 +1,492 @@ +/* $NetBSD: machdep.c,v 1.20 2012/12/01 11:37:27 mbalmer Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)machdep.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: machdep.c,v 1.20 2012/12/01 11:37:27 mbalmer Exp $"); +#endif +#endif /* not lint */ + +/* + * machdep.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +/* Included in this file are all system dependent routines. Extensive use + * of #ifdef's will be used to compile the appropriate code on each system: + * + * UNIX: all UNIX systems. + * UNIX_BSD4_2: UNIX BSD 4.2 and later, UTEK, (4.1 BSD too?) + * UNIX_SYSV: UNIX system V + * UNIX_V7: UNIX version 7 + * + * All UNIX code should be included between the single "#ifdef UNIX" at the + * top of this file, and the "#endif" at the bottom. + * + * To change a routine to include a new UNIX system, simply #ifdef the + * existing routine, as in the following example: + * + * To make a routine compatible with UNIX system 5, change the first + * function to the second: + * + * md_function() + * { + * code; + * } + * + * md_function() + * { + * #ifdef UNIX_SYSV + * sys5code; + * #else + * code; + * #endif + * } + * + * Appropriate variations of this are of course acceptable. + * The use of "#elseif" is discouraged because of non-portability. + * If the correct #define doesn't exist, "UNIX_SYSV" in this case, make it up + * and insert it in the list at the top of the file. Alter the CFLAGS + * in you Makefile appropriately. + * + */ + +#ifdef UNIX + +#include +#include +#include +#include +#include + +#ifdef UNIX_BSD4_2 +#include +#endif + +#ifdef UNIX_SYSV +#include +#endif + +#include +#include +#include +#include +#include "rogue.h" +#include "pathnames.h" + +/* md_slurp: + * + * This routine throws away all keyboard input that has not + * yet been read. It is used to get rid of input that the user may have + * typed-ahead. + * + * This function is not necessary, so it may be stubbed. The might cause + * message-line output to flash by because the game has continued to read + * input without waiting for the user to read the message. Not such a + * big deal. + */ + +void +md_slurp(void) +{ + (void)fpurge(stdin); +} + +/* md_heed_signals(): + * + * This routine tells the program to call particular routines when + * certain interrupts/events occur: + * + * SIGINT: call onintr() to interrupt fight with monster or long rest. + * SIGQUIT: call byebye() to check for game termination. + * SIGHUP: call error_save() to save game when terminal hangs up. + * + * On VMS, SIGINT and SIGQUIT correspond to ^C and ^Y. + * + * This routine is not strictly necessary and can be stubbed. This will + * mean that the game cannot be interrupted properly with keyboard + * input, this is not usually critical. + */ + +void +md_heed_signals(void) +{ + signal(SIGINT, onintr); + signal(SIGQUIT, byebye); + signal(SIGHUP, error_save); +} + +/* md_ignore_signals(): + * + * This routine tells the program to completely ignore the events mentioned + * in md_heed_signals() above. The event handlers will later be turned on + * by a future call to md_heed_signals(), so md_heed_signals() and + * md_ignore_signals() need to work together. + * + * This function should be implemented or the user risks interrupting + * critical sections of code, which could cause score file, or saved-game + * file, corruption. + */ + +void +md_ignore_signals(void) +{ + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +} + +/* md_get_file_id(): + * + * This function returns an integer that uniquely identifies the specified + * file. It need not check for the file's existence. In UNIX, the inode + * number is used. + * + * This function is used to identify saved-game files. + */ + +int +md_get_file_id(const char *fname) +{ + struct stat sbuf; + + if (stat(fname, &sbuf)) { + return(-1); + } + return((int)sbuf.st_ino); +} + +/* md_link_count(): + * + * This routine returns the number of hard links to the specified file. + * + * This function is not strictly necessary. On systems without hard links + * this routine can be stubbed by just returning 1. + */ + +int +md_link_count(const char *fname) +{ + struct stat sbuf; + + stat(fname, &sbuf); + return((int)sbuf.st_nlink); +} + +/* md_gct(): (Get Current Time) + * + * This function returns the current year, month(1-12), day(1-31), hour(0-23), + * minute(0-59), and second(0-59). This is used for identifying the time + * at which a game is saved. + * + * This function is not strictly necessary. It can be stubbed by returning + * zeros instead of the correct year, month, etc. If your operating + * system doesn't provide all of the time units requested here, then you + * can provide only those that it does, and return zeros for the others. + * If you cannot provide good time values, then users may be able to copy + * saved-game files and play them. + */ + +void +md_gct(struct rogue_time *rt_buf) +{ + struct tm *t; + time_t seconds; + + time(&seconds); + t = localtime(&seconds); + + rt_buf->year = t->tm_year; + rt_buf->month = t->tm_mon + 1; + rt_buf->day = t->tm_mday; + rt_buf->hour = t->tm_hour; + rt_buf->minute = t->tm_min; + rt_buf->second = t->tm_sec; +} + +/* md_gfmt: (Get File Modification Time) + * + * This routine returns a file's date of last modification in the same format + * as md_gct() above. + * + * This function is not strictly necessary. It is used to see if saved-game + * files have been modified since they were saved. If you have stubbed the + * routine md_gct() above by returning constant values, then you may do + * exactly the same here. + * Or if md_gct() is implemented correctly, but your system does not provide + * file modification dates, you may return some date far in the past so + * that the program will never know that a saved-game file being modified. + * You may also do this if you wish to be able to restore games from + * saved-games that have been modified. + */ + +void +md_gfmt(const char *fname, struct rogue_time *rt_buf) +{ + struct stat sbuf; + time_t seconds; + struct tm *t; + + stat(fname, &sbuf); + seconds = sbuf.st_mtime; + t = localtime(&seconds); + + rt_buf->year = t->tm_year; + rt_buf->month = t->tm_mon + 1; + rt_buf->day = t->tm_mday; + rt_buf->hour = t->tm_hour; + rt_buf->minute = t->tm_min; + rt_buf->second = t->tm_sec; +} + +/* md_df: (Delete File) + * + * This function deletes the specified file, and returns true (1) if the + * operation was successful. This is used to delete saved-game files + * after restoring games from them. + * + * Again, this function is not strictly necessary, and can be stubbed + * by simply returning 1. In this case, saved-game files will not be + * deleted and can be replayed. + */ + +boolean +md_df(const char *fname) +{ + if (unlink(fname)) { + return(0); + } + return(1); +} + +/* md_gln: (Get login name) + * + * This routine returns the login name of the user. This string is + * used mainly for identifying users in score files. + * + * A dummy string may be returned if you are unable to implement this + * function, but then the score file would only have one name in it. + */ + +const char * +md_gln(void) +{ + struct passwd *p; + + if (!(p = getpwuid(getuid()))) + return NULL; + return p->pw_name; +} + +/* md_sleep: + * + * This routine causes the game to pause for the specified number of + * seconds. + * + * This routine is not particularly necessary at all. It is used for + * delaying execution, which is useful to this program at some times. + */ + +void +md_sleep(int nsecs) +{ + (void)sleep(nsecs); +} + +/* md_getenv() + * + * This routine gets certain values from the user's environment. These + * values are strings, and each string is identified by a name. The names + * of the values needed, and their use, is as follows: + * + * ROGUEOPTS + * A string containing the various game options. This need not be + * defined. + * HOME + * The user's home directory. This is only used when the user specifies + * '~' as the first character of a saved-game file. This string need + * not be defined. + * SHELL + * The user's favorite shell. If not found, "/bin/sh" is assumed. + * + * If your system does not provide a means of searching for these values, + * you will have to do it yourself. None of the values above really need + * to be defined; you can get by with simply always returning zero. + * Returning zero indicates that their is no defined value for the + * given string. + */ + +char * +md_getenv(const char *name) +{ + char *value; + + value = getenv(name); + + return(value); +} + +/* md_malloc() + * + * This routine allocates, and returns a pointer to, the specified number + * of bytes. This routines absolutely MUST be implemented for your + * particular system or the program will not run at all. Return zero + * when no more memory can be allocated. + */ + +void * +md_malloc(size_t n) +{ + char *t; + + t = malloc(n); + return(t); +} + +/* md_gseed() (Get Seed) + * + * This function returns a seed for the random number generator (RNG). This + * seed causes the RNG to begin generating numbers at some point in its + * sequence. Without a random seed, the RNG will generate the same set + * of numbers, and every game will start out exactly the same way. A good + * number to use is the process id, given by getpid() on most UNIX systems. + * + * You need to find some single random integer, such as: + * process id. + * current time (minutes + seconds) returned from md_gct(), if implemented. + * + * It will not help to return "get_rand()" or "rand()" or the return value of + * any pseudo-RNG. If you don't have a random number, you can just return 1, + * but this means your games will ALWAYS start the same way, and will play + * exactly the same way given the same input. + */ + +int +md_gseed(void) +{ + time_t seconds; + + time(&seconds); + return((int)seconds); +} + +/* md_exit(): + * + * This function causes the program to discontinue execution and exit. + * This function must be implemented or the program will continue to + * hang when it should quit. + */ + +void +md_exit(int status) +{ + exit(status); +} + +/* md_lock(): + * + * This function is intended to give the user exclusive access to the score + * file. It does so by flock'ing the score file. The full path name of the + * score file should be defined for any particular site in rogue.h. The + * constants _PATH_SCOREFILE defines this file name. + * + * When the parameter 'l' is non-zero (true), a lock is requested. Otherwise + * the lock is released. + */ + +void +md_lock(boolean l) +{ + static int fd = -1; + short tries; + + if (l) { + setegid(egid); + if ((fd = open(_PATH_SCOREFILE, O_RDONLY)) < 1) { + setegid(gid); + messagef(0, "cannot lock score file"); + return; + } + setegid(gid); + for (tries = 0; tries < 5; tries++) + if (!flock(fd, LOCK_EX|LOCK_NB)) + return; + } else { + (void)flock(fd, LOCK_UN|LOCK_NB); + (void)close(fd); + } +} + +/* md_shell(): + * + * This function spawns a shell for the user to use. When this shell is + * terminated, the game continues. + * + * It is important that the game not give the shell the privileges the + * game uses to access the scores file. This version of the game runs + * with privileges low by default; only the saved gid (if setgid) or uid + * (if setuid) will be privileged, but that privilege is discarded by + * exec(). + */ + +void +md_shell(const char *shell) +{ + int w; + pid_t pid; + + pid = fork(); + switch (pid) { + case -1: + break; + case 0: + execl(shell, shell, (char *)NULL); + _exit(255); + default: + waitpid(pid, &w, 0); + break; + } +} + +#endif /* UNIX */ diff --git a/games/rogue/main.c b/games/rogue/main.c new file mode 100644 index 000000000..fb8843643 --- /dev/null +++ b/games/rogue/main.c @@ -0,0 +1,84 @@ +/* $NetBSD: main.c,v 1.9 2008/07/20 01:03:22 lukem Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: main.c,v 1.9 2008/07/20 01:03:22 lukem Exp $"); +#endif +#endif /* not lint */ + +/* + * main.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +int +main(int argc, char *argv[]) +{ + if (init(argc, argv)) { /* restored game */ + goto PL; + } + + for (;;) { + clear_level(); + make_level(); + put_objects(); + put_stairs(); + add_traps(); + put_mons(); + put_player(party_room); + print_stats(STAT_ALL); +PL: + play_level(); + free_stuff(&level_objects); + free_stuff(&level_monsters); + } +} diff --git a/games/rogue/message.c b/games/rogue/message.c new file mode 100644 index 000000000..e38377deb --- /dev/null +++ b/games/rogue/message.c @@ -0,0 +1,378 @@ +/* $NetBSD: message.c,v 1.14 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)message.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: message.c,v 1.14 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * message.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include +#include +#include +#include "rogue.h" +#include "pathnames.h" + +static char msgs[NMESSAGES][DCOLS] = {"", "", "", "", ""}; +static short msg_col = 0, imsg = -1; +static boolean rmsg = 0; + +boolean msg_cleared = 1; +char hunger_str[HUNGER_STR_LEN] = ""; +const char *more = "-more-"; + +static void save_screen(void); + +static +void +message(const char *msg, boolean intrpt) +{ + cant_int = 1; + + if (!save_is_interactive) { + return; + } + if (intrpt) { + interrupted = 1; + md_slurp(); + } + + if (!msg_cleared) { + mvaddstr(MIN_ROW-1, msg_col, more); + refresh(); + wait_for_ack(); + check_message(); + } + if (!rmsg) { + imsg = (imsg + 1) % NMESSAGES; + (void)strlcpy(msgs[imsg], msg, sizeof(msgs[imsg])); + } + mvaddstr(MIN_ROW-1, 0, msg); + addch(' '); + refresh(); + msg_cleared = 0; + msg_col = strlen(msg); + + cant_int = 0; + + if (did_int) { + did_int = 0; + onintr(0); + } +} + +void +messagef(boolean intrpt, const char *fmt, ...) +{ + va_list ap; + char buf[DCOLS]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + message(buf, intrpt); +} + +void +remessage(short c) +{ + if (imsg != -1) { + check_message(); + rmsg = 1; + while (c > imsg) { + c -= NMESSAGES; + } + message(msgs[((imsg - c) % NMESSAGES)], 0); + rmsg = 0; + move(rogue.row, rogue.col); + refresh(); + } +} + +void +check_message(void) +{ + if (msg_cleared) { + return; + } + move(MIN_ROW-1, 0); + clrtoeol(); + refresh(); + msg_cleared = 1; +} + +int +get_input_line(const char *prompt, const char *insert, + char *buf, size_t buflen, + const char *if_cancelled, + boolean add_blank, boolean do_echo) +{ + short ch; + size_t i = 0, n; + + message(prompt, 0); + n = strlen(prompt); + + if (insert[0]) { + mvaddstr(0, n + 1, insert); + (void)strlcpy(buf, insert, buflen); + i = strlen(buf); + move(0, (n + i + 1)); + refresh(); + } + + while (((ch = rgetchar()) != '\r') && (ch != '\n') && (ch != CANCEL)) { + if ((ch >= ' ') && (ch <= '~') && (i < buflen-2)) { + if ((ch != ' ') || (i > 0)) { + buf[i++] = ch; + if (do_echo) { + addch(ch); + } + } + } + if ((ch == '\b') && (i > 0)) { + if (do_echo) { + mvaddch(0, i + n, ' '); + move(MIN_ROW-1, i+n); + } + i--; + } + refresh(); + } + check_message(); + if (add_blank) { + buf[i++] = ' '; + } else { + while ((i > 0) && (buf[i-1] == ' ')) { + i--; + } + } + + buf[i] = 0; + + if ((ch == CANCEL) || (i == 0) || ((i == 1) && add_blank)) { + if (if_cancelled) { + message(if_cancelled, 0); + } + return(0); + } + return(i); +} + +int +rgetchar(void) +{ + int ch; + + for(;;) { + ch = getchar(); + + switch(ch) { + case '\022': /* ^R */ + wrefresh(curscr); + break; +#ifdef UNIX_BSD4_2 + case '\032': /* ^Z */ + printf("%s", CL); + fflush(stdout); + tstp(); + break; +#endif + case '&': + save_screen(); + break; + default: + return(ch); + } + } +} + +/* +Level: 99 Gold: 999999 Hp: 999(999) Str: 99(99) Arm: 99 Exp: 21/10000000 Hungry +0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 +*/ + +void +print_stats(int stat_mask) +{ + char buf[16]; + boolean label; + int row = DROWS - 1; + + label = (stat_mask & STAT_LABEL) ? 1 : 0; + + if (stat_mask & STAT_LEVEL) { + if (label) { + mvaddstr(row, 0, "Level: "); + } + /* max level taken care of in make_level() */ + mvprintw(row, 7, "%-2d", cur_level); + } + if (stat_mask & STAT_GOLD) { + if (label) { + mvaddstr(row, 10, "Gold: "); + } + if (rogue.gold > MAX_GOLD) { + rogue.gold = MAX_GOLD; + } + mvprintw(row, 16, "%-6ld", rogue.gold); + } + if (stat_mask & STAT_HP) { + if (label) { + mvaddstr(row, 23, "Hp: "); + } + if (rogue.hp_max > MAX_HP) { + rogue.hp_current -= (rogue.hp_max - MAX_HP); + rogue.hp_max = MAX_HP; + } + snprintf(buf, sizeof(buf), "%d(%d)", + rogue.hp_current, rogue.hp_max); + mvprintw(row, 27, "%-8s", buf); + } + if (stat_mask & STAT_STRENGTH) { + if (label) { + mvaddstr(row, 36, "Str: "); + } + if (rogue.str_max > MAX_STRENGTH) { + rogue.str_current -= (rogue.str_max - MAX_STRENGTH); + rogue.str_max = MAX_STRENGTH; + } + snprintf(buf, sizeof(buf), "%d(%d)", + (rogue.str_current + add_strength), rogue.str_max); + mvprintw(row, 41, "%-6s", buf); + } + if (stat_mask & STAT_ARMOR) { + if (label) { + mvaddstr(row, 48, "Arm: "); + } + if (rogue.armor && (rogue.armor->d_enchant > MAX_ARMOR)) { + rogue.armor->d_enchant = MAX_ARMOR; + } + mvprintw(row, 53, "%-2d", get_armor_class(rogue.armor)); + } + if (stat_mask & STAT_EXP) { + if (label) { + mvaddstr(row, 56, "Exp: "); + } + if (rogue.exp_points > MAX_EXP) { + rogue.exp_points = MAX_EXP; + } + if (rogue.exp > MAX_EXP_LEVEL) { + rogue.exp = MAX_EXP_LEVEL; + } + snprintf(buf, sizeof(buf), "%d/%ld", + rogue.exp, rogue.exp_points); + mvprintw(row, 61, "%-11s", buf); + } + if (stat_mask & STAT_HUNGER) { + mvaddstr(row, 73, hunger_str); + clrtoeol(); + } + refresh(); +} + +static void +save_screen(void) +{ + FILE *fp; + short i, j; + char buf[DCOLS+2]; + + if ((fp = fopen(_PATH_SCREENDUMP, "w")) != NULL) { + for (i = 0; i < DROWS; i++) { + for (j=0; j0 && buf[j-1]==' '; j--); + buf[j] = 0; + + fputs(buf, fp); + putc('\n', fp); + } + fclose(fp); + } else { + sound_bell(); + } +} + +void +sound_bell(void) +{ + putchar(7); + fflush(stdout); +} + +boolean +is_digit(int ch) +{ + return((ch >= '0') && (ch <= '9')); +} + +int +r_index(const char *str, int ch, boolean last) +{ + int i = 0; + + if (last) { + for (i = strlen(str) - 1; i >= 0; i--) { + if (str[i] == ch) { + return(i); + } + } + } else { + for (i = 0; str[i]; i++) { + if (str[i] == ch) { + return(i); + } + } + } + return(-1); +} diff --git a/games/rogue/monster.c b/games/rogue/monster.c new file mode 100644 index 000000000..1d644f65b --- /dev/null +++ b/games/rogue/monster.c @@ -0,0 +1,886 @@ +/* $NetBSD: monster.c,v 1.17 2013/08/11 03:44:27 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)monster.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: monster.c,v 1.17 2013/08/11 03:44:27 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * monster.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +object level_monsters; +boolean mon_disappeared; + +const char *const m_names[] = { + "aquator", + "bat", + "centaur", + "dragon", + "emu", + "venus fly-trap", + "griffin", + "hobgoblin", + "ice monster", + "jabberwock", + "kestrel", + "leprechaun", + "medusa", + "nymph", + "orc", + "phantom", + "quagga", + "rattlesnake", + "snake", + "troll", + "black unicorn", + "vampire", + "wraith", + "xeroc", + "yeti", + "zombie" +}; + +#define FILL 0,0,0,0,0,0,0,0,0,0,0,0,0,NULL + +static object mon_tab[MONSTERS] = { + {(ASLEEP|WAKENS|WANDERS|RUSTS),"0d0",25,'A',20,9,18,100,0,0, FILL}, + {(ASLEEP|WANDERS|FLITS|FLIES),"1d3",10,'B',2,1,8,60,0,0, FILL}, + {(ASLEEP|WANDERS),"3d3/2d5",32,'C',15,7,16,85,0,10, FILL}, + {(ASLEEP|WAKENS|FLAMES),"4d6/4d9",145,'D',5000,21,126,100,0,90, FILL}, + {(ASLEEP|WAKENS),"1d3",11,'E',2,1,7,65,0,0, FILL}, + {(HOLDS|STATIONARY),"5d5",73,'F',91,12,126,80,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS|FLIES),"5d5/5d5",115,'G', + 2000,20,126,85,0,10, FILL}, + {(ASLEEP|WAKENS|WANDERS),"1d3/1d2",15,'H',3,1,10,67,0,0, FILL}, + {(ASLEEP|FREEZES),"0d0",15,'I',5,2,11,68,0,0, FILL}, + {(ASLEEP|WANDERS),"3d10/4d5",132,'J',3000,21,126,100,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS|FLIES),"1d4",10,'K',2,1,6,60,0,0, FILL}, + {(ASLEEP|STEALS_GOLD),"0d0",25,'L',21,6,16,75,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS|CONFUSES),"4d4/3d7",97,'M', + 250,18,126,85,0,25, FILL}, + {(ASLEEP|STEALS_ITEM),"0d0",25,'N',39,10,19,75,0,100, FILL}, + {(ASLEEP|WANDERS|WAKENS|SEEKS_GOLD),"1d6",25,'O',5,4,13,70,0,10, FILL}, + {(ASLEEP|INVISIBLE|WANDERS|FLITS),"5d4",76,'P',120,15,24,80,0,50, FILL}, + {(ASLEEP|WAKENS|WANDERS),"3d5",30,'Q',20,8,17,78,0,20, FILL}, + {(ASLEEP|WAKENS|WANDERS|STINGS),"2d5",19,'R',10,3,12,70,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS),"1d3",8,'S',2,1,9,50,0,0, FILL}, + {(ASLEEP|WAKENS|WANDERS),"4d6/1d4",75,'T',125,13,22,75,0,33, FILL}, + {(ASLEEP|WAKENS|WANDERS),"4d10",90,'U', + 200,17,26,85,0,33, FILL}, + {(ASLEEP|WAKENS|WANDERS|DRAINS_LIFE),"1d14/1d4",55,'V', + 350,19,126,85,0,18, FILL}, + {(ASLEEP|WANDERS|DROPS_LEVEL),"2d8",45,'W',55,14,23,75,0,0, FILL}, + {(ASLEEP|IMITATES),"4d6",42,'X',110,16,25,75,0,0, FILL}, + {(ASLEEP|WANDERS),"3d6",35,'Y',50,11,20,80,0,20, FILL}, + {(ASLEEP|WAKENS|WANDERS),"1d7",21,'Z',8,5,14,69,0,0, FILL} +}; + +static void aim_monster(object *); +static int flit(object *); +static int move_confused(object *); +static int mtry(object *, short, short); +static int no_room_for_monster(int); +static void put_m_at(short, short, object *); +static int rogue_is_around(int, int); + +void +put_mons(void) +{ + short i; + short n; + object *monster; + short row, col; + + n = get_rand(4, 6); + + for (i = 0; i < n; i++) { + monster = gr_monster(NULL, 0); + if ((monster->m_flags & WANDERS) && coin_toss()) { + wake_up(monster); + } + gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); + put_m_at(row, col, monster); + } +} + +object * +gr_monster(object *monster, int mn) +{ + if (!monster) { + monster = alloc_object(); + + for (;;) { + mn = get_rand(0, MONSTERS-1); + if ((cur_level >= mon_tab[mn].first_level) && + (cur_level <= mon_tab[mn].last_level)) { + break; + } + } + } + *monster = mon_tab[mn]; + if (monster->m_flags & IMITATES) { + monster->disguise = gr_obj_char(); + } + if (cur_level > (AMULET_LEVEL + 2)) { + monster->m_flags |= HASTED; + } + monster->trow = NO_ROOM; + return(monster); +} + +void +mv_mons(void) +{ + object *monster, *next_monster, *test_mons; + boolean flew; + + if (haste_self % 2) { + return; + } + + monster = level_monsters.next_monster; + + while (monster) { + next_monster = monster->next_monster; + mon_disappeared = 0; + if (monster->m_flags & HASTED) { + mv_1_monster(monster, rogue.row, rogue.col); + if (mon_disappeared) { + goto NM; + } + } else if (monster->m_flags & SLOWED) { + monster->slowed_toggle = !monster->slowed_toggle; + if (monster->slowed_toggle) { + goto NM; + } + } + if ((monster->m_flags & CONFUSED) && move_confused(monster)) { + goto NM; + } + flew = 0; + if ( (monster->m_flags & FLIES) && + !(monster->m_flags & NAPPING) && + !mon_can_go(monster, rogue.row, rogue.col)) { + flew = 1; + mv_1_monster(monster, rogue.row, rogue.col); + if (mon_disappeared) { + goto NM; + } + } + if (!(flew && mon_can_go(monster, rogue.row, rogue.col))) { + mv_1_monster(monster, rogue.row, rogue.col); + } +NM: test_mons = level_monsters.next_monster; + monster = NULL; + while (test_mons) + { + if (next_monster == test_mons) + { + monster = next_monster; + break; + } + test_mons = test_mons -> next_monster; + } + } +} + +void +party_monsters(int rn, int n) +{ + short i, j; + short row, col; + object *monster; + boolean found; + + row = col = 0; + n += n; + + for (i = 0; i < MONSTERS; i++) { + mon_tab[i].first_level -= (cur_level % 3); + } + for (i = 0; i < n; i++) { + if (no_room_for_monster(rn)) { + break; + } + for (j = found = 0; ((!found) && (j < 250)); j++) { + row = get_rand(rooms[rn].top_row+1, + rooms[rn].bottom_row-1); + col = get_rand(rooms[rn].left_col+1, + rooms[rn].right_col-1); + if ((!(dungeon[row][col] & MONSTER)) && + (dungeon[row][col] & (FLOOR | TUNNEL))) { + found = 1; + } + } + if (found) { + monster = gr_monster((object *)0, 0); + if (!(monster->m_flags & IMITATES)) { + monster->m_flags |= WAKENS; + } + put_m_at(row, col, monster); + } + } + for (i = 0; i < MONSTERS; i++) { + mon_tab[i].first_level += (cur_level % 3); + } +} + +char +gmc_row_col(int row, int col) +{ + object *monster; + + if ((monster = object_at(&level_monsters, row, col)) != NULL) { + if ((!(detect_monster || see_invisible || r_see_invisible) && + (monster->m_flags & INVISIBLE)) || blind) { + return(monster->trail_char); + } + if (monster->m_flags & IMITATES) { + return(monster->disguise); + } + return(monster->m_char); + } else { + return('&'); /* BUG if this ever happens */ + } +} + +char +gmc(object *monster) +{ + if ((!(detect_monster || see_invisible || r_see_invisible) && + (monster->m_flags & INVISIBLE)) + || blind) { + return(monster->trail_char); + } + if (monster->m_flags & IMITATES) { + return(monster->disguise); + } + return(monster->m_char); +} + +void +mv_1_monster(object *monster, short row, short col) +{ + short i, n; + boolean tried[6]; + + if (monster->m_flags & ASLEEP) { + if (monster->m_flags & NAPPING) { + if (--monster->nap_length <= 0) { + monster->m_flags &= (~(NAPPING | ASLEEP)); + } + return; + } + if ((monster->m_flags & WAKENS) && + rogue_is_around(monster->row, monster->col) && + rand_percent(((stealthy > 0) ? + (WAKE_PERCENT / (STEALTH_FACTOR + stealthy)) : + WAKE_PERCENT))) { + wake_up(monster); + } + return; + } else if (monster->m_flags & ALREADY_MOVED) { + monster->m_flags &= (~ALREADY_MOVED); + return; + } + if ((monster->m_flags & FLITS) && flit(monster)) { + return; + } + if ((monster->m_flags & STATIONARY) && + (!mon_can_go(monster, rogue.row, rogue.col))) { + return; + } + if (monster->m_flags & FREEZING_ROGUE) { + return; + } + if ((monster->m_flags & CONFUSES) && m_confuse(monster)) { + return; + } + if (mon_can_go(monster, rogue.row, rogue.col)) { + mon_hit(monster); + return; + } + if ((monster->m_flags & FLAMES) && flame_broil(monster)) { + return; + } + if ((monster->m_flags & SEEKS_GOLD) && seek_gold(monster)) { + return; + } + if ((monster->trow == monster->row) && + (monster->tcol == monster->col)) { + monster->trow = NO_ROOM; + } else if (monster->trow != NO_ROOM) { + row = monster->trow; + col = monster->tcol; + } + if (monster->row > row) { + row = monster->row - 1; + } else if (monster->row < row) { + row = monster->row + 1; + } + if ((dungeon[row][monster->col] & DOOR) && + mtry(monster, row, monster->col)) { + return; + } + if (monster->col > col) { + col = monster->col - 1; + } else if (monster->col < col) { + col = monster->col + 1; + } + if ((dungeon[monster->row][col] & DOOR) && + mtry(monster, monster->row, col)) { + return; + } + if (mtry(monster, row, col)) { + return; + } + + for (i = 0; i <= 5; i++) tried[i] = 0; + + for (i = 0; i < 6; i++) { +NEXT_TRY: n = get_rand(0, 5); + switch(n) { + case 0: + if (!tried[n] && mtry(monster, row, monster->col-1)) { + goto O; + } + break; + case 1: + if (!tried[n] && mtry(monster, row, monster->col)) { + goto O; + } + break; + case 2: + if (!tried[n] && mtry(monster, row, monster->col+1)) { + goto O; + } + break; + case 3: + if (!tried[n] && mtry(monster, monster->row-1, col)) { + goto O; + } + break; + case 4: + if (!tried[n] && mtry(monster, monster->row, col)) { + goto O; + } + break; + case 5: + if (!tried[n] && mtry(monster, monster->row+1, col)) { + goto O; + } + break; + } + if (!tried[n]) { + tried[n] = 1; + } else { + goto NEXT_TRY; + } + } +O: + if ((monster->row == monster->o_row) && (monster->col == monster->o_col)) { + if (++(monster->o) > 4) { + if ((monster->trow == NO_ROOM) && + (!mon_sees(monster, rogue.row, rogue.col))) { + monster->trow = get_rand(1, (DROWS - 2)); + monster->tcol = get_rand(0, (DCOLS - 1)); + } else { + monster->trow = NO_ROOM; + monster->o = 0; + } + } + } else { + monster->o_row = monster->row; + monster->o_col = monster->col; + monster->o = 0; + } +} + +static int +mtry(object *monster, short row, short col) +{ + if (mon_can_go(monster, row, col)) { + move_mon_to(monster, row, col); + return(1); + } + return(0); +} + +void +move_mon_to(object *monster, short row, short col) +{ + short c; + int mrow, mcol; + + mrow = monster->row; + mcol = monster->col; + + dungeon[mrow][mcol] &= ~MONSTER; + dungeon[row][col] |= MONSTER; + + c = mvinch(mrow, mcol); + + if ((c >= 'A') && (c <= 'Z')) { + if (!detect_monster) { + mvaddch(mrow, mcol, monster->trail_char); + } else { + if (rogue_can_see(mrow, mcol)) { + mvaddch(mrow, mcol, monster->trail_char); + } else { + if (monster->trail_char == '.') { + monster->trail_char = ' '; + } + mvaddch(mrow, mcol, monster->trail_char); + } + } + } + monster->trail_char = mvinch(row, col); + if (!blind && (detect_monster || rogue_can_see(row, col))) { + if ((!(monster->m_flags & INVISIBLE) || + (detect_monster || see_invisible || r_see_invisible))) { + mvaddch(row, col, gmc(monster)); + } + } + if ((dungeon[row][col] & DOOR) && + (get_room_number(row, col) != cur_room) && + (dungeon[mrow][mcol] == FLOOR) && !blind) { + mvaddch(mrow, mcol, ' '); + } + if (dungeon[row][col] & DOOR) { + dr_course(monster, ((dungeon[mrow][mcol] & TUNNEL) ? 1 : 0), + row, col); + } else { + monster->row = row; + monster->col = col; + } +} + +int +mon_can_go(const object *monster, short row, short col) +{ + object *obj; + short dr, dc; + + dr = monster->row - row; /* check if move distance > 1 */ + if ((dr >= 2) || (dr <= -2)) { + return(0); + } + dc = monster->col - col; + if ((dc >= 2) || (dc <= -2)) { + return(0); + } + if ((!dungeon[monster->row][col]) || (!dungeon[row][monster->col])) { + return(0); + } + if ((!is_passable(row, col)) || (dungeon[row][col] & MONSTER)) { + return(0); + } + if ((monster->row!=row)&&(monster->col!=col)&&((dungeon[row][col]&DOOR) || + (dungeon[monster->row][monster->col]&DOOR))) { + return(0); + } + if (!(monster->m_flags & (FLITS | CONFUSED | CAN_FLIT)) && + (monster->trow == NO_ROOM)) { + if ((monster->row < rogue.row) && (row < monster->row)) return(0); + if ((monster->row > rogue.row) && (row > monster->row)) return(0); + if ((monster->col < rogue.col) && (col < monster->col)) return(0); + if ((monster->col > rogue.col) && (col > monster->col)) return(0); + } + if (dungeon[row][col] & OBJECT) { + obj = object_at(&level_objects, row, col); + if ((obj->what_is == SCROL) && (obj->which_kind == SCARE_MONSTER)) { + return(0); + } + } + return(1); +} + +void +wake_up(object *monster) +{ + if (!(monster->m_flags & NAPPING)) { + monster->m_flags &= (~(ASLEEP | IMITATES | WAKENS)); + } +} + +void +wake_room(short rn, boolean entering, short row, short col) +{ + object *monster; + short wake_percent; + boolean in_room; + + wake_percent = (rn == party_room) ? PARTY_WAKE_PERCENT : WAKE_PERCENT; + if (stealthy > 0) { + wake_percent /= (STEALTH_FACTOR + stealthy); + } + + monster = level_monsters.next_monster; + + while (monster) { + in_room = (rn == get_room_number(monster->row, monster->col)); + if (in_room) { + if (entering) { + monster->trow = NO_ROOM; + } else { + monster->trow = row; + monster->tcol = col; + } + } + if ((monster->m_flags & WAKENS) && + (rn == get_room_number(monster->row, monster->col))) { + if (rand_percent(wake_percent)) { + wake_up(monster); + } + } + monster = monster->next_monster; + } +} + +const char * +mon_name(const object *monster) +{ + short ch; + + if (blind || ((monster->m_flags & INVISIBLE) && + !(detect_monster || see_invisible || r_see_invisible))) { + return("something"); + } + if (halluc) { + ch = get_rand('A', 'Z') - 'A'; + return(m_names[ch]); + } + ch = monster->m_char - 'A'; + return(m_names[ch]); +} + +static int +rogue_is_around(int row, int col) +{ + short rdif, cdif, retval; + + rdif = row - rogue.row; + cdif = col - rogue.col; + + retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1); + return(retval); +} + +void +wanderer(void) +{ + object *monster; + short row, col, i; + boolean found = 0; + + monster = NULL; /* XXXGCC -Wuninitialized [powerpc] */ + + for (i = 0; ((i < 15) && (!found)); i++) { + monster = gr_monster(NULL, 0); + if (!(monster->m_flags & (WAKENS | WANDERS))) { + free_object(monster); + } else { + found = 1; + } + } + if (found) { + found = 0; + wake_up(monster); + for (i = 0; ((i < 25) && (!found)); i++) { + gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); + if (!rogue_can_see(row, col)) { + put_m_at(row, col, monster); + found = 1; + } + } + if (!found) { + free_object(monster); + } + } +} + +void +show_monsters(void) +{ + object *monster; + + detect_monster = 1; + + if (blind) { + return; + } + monster = level_monsters.next_monster; + + while (monster) { + mvaddch(monster->row, monster->col, monster->m_char); + if (monster->m_flags & IMITATES) { + monster->m_flags &= (~IMITATES); + monster->m_flags |= WAKENS; + } + monster = monster->next_monster; + } +} + +void +create_monster(void) +{ + short row, col; + short i; + boolean found = 0; + object *monster; + + row = rogue.row; + col = rogue.col; + + for (i = 0; i < 9; i++) { + rand_around(i, &row, &col); + if (((row == rogue.row) && (col == rogue.col)) || + (row < MIN_ROW) || (row > (DROWS-2)) || + (col < 0) || (col > (DCOLS-1))) { + continue; + } + if ((!(dungeon[row][col] & MONSTER)) && + (dungeon[row][col] & (FLOOR|TUNNEL|STAIRS|DOOR))) { + found = 1; + break; + } + } + if (found) { + monster = gr_monster((object *)0, 0); + put_m_at(row, col, monster); + mvaddch(row, col, gmc(monster)); + if (monster->m_flags & (WANDERS | WAKENS)) { + wake_up(monster); + } + } else { + messagef(0, "you hear a faint cry of anguish in the distance"); + } +} + +static void +put_m_at(short row, short col, object *monster) +{ + monster->row = row; + monster->col = col; + dungeon[row][col] |= MONSTER; + monster->trail_char = mvinch(row, col); + (void)add_to_pack(monster, &level_monsters, 0); + aim_monster(monster); +} + +static void +aim_monster(object *monster) +{ + short i, rn, d, r; + + rn = get_room_number(monster->row, monster->col); + if (rn == NO_ROOM) + clean_up("aim_monster: monster not in room"); + r = get_rand(0, 12); + + for (i = 0; i < 4; i++) { + d = (r + i) % 4; + if (rooms[rn].doors[d].oth_room != NO_ROOM) { + monster->trow = rooms[rn].doors[d].door_row; + monster->tcol = rooms[rn].doors[d].door_col; + break; + } + } +} + +int +rogue_can_see(int row, int col) +{ + int retval; + + retval = !blind && + (((get_room_number(row, col) == cur_room) && + !(rooms[cur_room].is_room & R_MAZE)) || + rogue_is_around(row, col)); + + return(retval); +} + +static int +move_confused(object *monster) +{ + short i, row, col; + + if (!(monster->m_flags & ASLEEP)) { + if (--monster->moves_confused <= 0) { + monster->m_flags &= (~CONFUSED); + } + if (monster->m_flags & STATIONARY) { + return(coin_toss() ? 1 : 0); + } else if (rand_percent(15)) { + return(1); + } + row = monster->row; + col = monster->col; + + for (i = 0; i < 9; i++) { + rand_around(i, &row, &col); + if ((row == rogue.row) && (col == rogue.col)) { + return(0); + } + if (mtry(monster, row, col)) { + return(1); + } + } + } + return(0); +} + +static int +flit(object *monster) +{ + short i, row, col; + + if (!rand_percent(FLIT_PERCENT + ((monster->m_flags & FLIES) ? 20 : 0))) { + return(0); + } + if (rand_percent(10)) { + return(1); + } + row = monster->row; + col = monster->col; + + for (i = 0; i < 9; i++) { + rand_around(i, &row, &col); + if ((row == rogue.row) && (col == rogue.col)) { + continue; + } + if (mtry(monster, row, col)) { + return(1); + } + } + return(1); +} + +char +gr_obj_char(void) +{ + short r; + const char *rs = "%!?]=/):*"; + + r = get_rand(0, 8); + + return(rs[r]); +} + +static int +no_room_for_monster(int rn) +{ + short i, j; + + for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) { + if (!(dungeon[i][j] & MONSTER)) { + return(0); + } + } + } + return(1); +} + +void +aggravate(void) +{ + object *monster; + + messagef(0, "you hear a high pitched humming noise"); + + monster = level_monsters.next_monster; + + while (monster) { + wake_up(monster); + monster->m_flags &= (~IMITATES); + if (rogue_can_see(monster->row, monster->col)) { + mvaddch(monster->row, monster->col, monster->m_char); + } + monster = monster->next_monster; + } +} + +boolean +mon_sees(const object *monster, int row, int col) +{ + short rn, rdif, cdif, retval; + + rn = get_room_number(row, col); + + if ( (rn != NO_ROOM) && + (rn == get_room_number(monster->row, monster->col)) && + !(rooms[rn].is_room & R_MAZE)) { + return(1); + } + rdif = row - monster->row; + cdif = col - monster->col; + + retval = (rdif >= -1) && (rdif <= 1) && (cdif >= -1) && (cdif <= 1); + return(retval); +} + +void +mv_aquatars(void) +{ + object *monster; + + monster = level_monsters.next_monster; + + while (monster) { + if ((monster->m_char == 'A') && + mon_can_go(monster, rogue.row, rogue.col)) { + mv_1_monster(monster, rogue.row, rogue.col); + monster->m_flags |= ALREADY_MOVED; + } + monster = monster->next_monster; + } +} diff --git a/games/rogue/move.c b/games/rogue/move.c new file mode 100644 index 000000000..8378654f2 --- /dev/null +++ b/games/rogue/move.c @@ -0,0 +1,649 @@ +/* $NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)move.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: move.c,v 1.13 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * move.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +short m_moves = 0; +boolean jump = 0; +const char you_can_move_again[] = "you can move again"; + +static boolean can_turn(short, short); +static boolean check_hunger(boolean); +static char gr_dir(void); +static void heal(void); +static boolean next_to_something(int, int); +static void turn_passage(short, boolean); + +int +one_move_rogue(short dirch, short pickup) +{ + short row, col; + object *obj; + char desc[DCOLS]; + short status, d = 0; /* XXX: GCC */ + + row = rogue.row; + col = rogue.col; + + if (confused) { + dirch = gr_dir(); + } + (void)is_direction(dirch, &d); + get_dir_rc(d, &row, &col, 1); + + if (!can_move(rogue.row, rogue.col, row, col)) { + return(MOVE_FAILED); + } + if (being_held || bear_trap) { + if (!(dungeon[row][col] & MONSTER)) { + if (being_held) { + messagef(1, "you are being held"); + } else { + messagef(0, "you are still stuck in the bear trap"); + (void)reg_move(); + } + return(MOVE_FAILED); + } + } + if (r_teleport) { + if (rand_percent(R_TELE_PERCENT)) { + tele(); + return(STOPPED_ON_SOMETHING); + } + } + if (dungeon[row][col] & MONSTER) { + rogue_hit(object_at(&level_monsters, row, col), 0); + (void)reg_move(); + return(MOVE_FAILED); + } + if (dungeon[row][col] & DOOR) { + if (cur_room == PASSAGE) { + cur_room = get_room_number(row, col); + if (cur_room == NO_ROOM) + clean_up("one_move_rogue: door to nowhere"); + light_up_room(cur_room); + wake_room(cur_room, 1, row, col); + } else { + light_passage(row, col); + } + } else if ((dungeon[rogue.row][rogue.col] & DOOR) && + (dungeon[row][col] & TUNNEL)) { + light_passage(row, col); + wake_room(cur_room, 0, rogue.row, rogue.col); + darken_room(cur_room); + cur_room = PASSAGE; + } else if (dungeon[row][col] & TUNNEL) { + light_passage(row, col); + } + mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); + mvaddch(row, col, rogue.fchar); + + if (!jump) { + refresh(); + } + rogue.row = row; + rogue.col = col; + if (dungeon[row][col] & OBJECT) { + if (levitate && pickup) { + return(STOPPED_ON_SOMETHING); + } + if (pickup && !levitate) { + if ((obj = pick_up(row, col, &status)) != NULL) { + get_desc(obj, desc, sizeof(desc)); + if (obj->what_is == GOLD) { + free_object(obj); + messagef(1, "%s", desc); + goto NOT_IN_PACK; + } + } else if (!status) { + goto MVED; + } else { + goto MOVE_ON; + } + } else { +MOVE_ON: + obj = object_at(&level_objects, row, col); + get_desc(obj, desc, sizeof(desc)); + messagef(1, "moved onto %s", desc); + goto NOT_IN_PACK; + } + messagef(1, "%s(%c)", desc, obj->ichar); +NOT_IN_PACK: + (void)reg_move(); + return(STOPPED_ON_SOMETHING); + } + if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) { + if ((!levitate) && (dungeon[row][col] & TRAP)) { + trap_player(row, col); + } + (void)reg_move(); + return(STOPPED_ON_SOMETHING); + } +MVED: if (reg_move()) { /* fainted from hunger */ + return(STOPPED_ON_SOMETHING); + } + return((confused ? STOPPED_ON_SOMETHING : MOVED)); +} + +void +multiple_move_rogue(short dirch) +{ + short row, col; + short m; + + switch(dirch) { + case '\010': + case '\012': + case '\013': + case '\014': + case '\031': + case '\025': + case '\016': + case '\002': + do { + row = rogue.row; + col = rogue.col; + if (((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED) || + (m == STOPPED_ON_SOMETHING) || + interrupted) { + break; + } + } while (!next_to_something(row, col)); + if ( (!interrupted) && passgo && (m == MOVE_FAILED) && + (dungeon[rogue.row][rogue.col] & TUNNEL)) { + turn_passage(dirch + 96, 0); + } + break; + case 'H': + case 'J': + case 'K': + case 'L': + case 'B': + case 'Y': + case 'U': + case 'N': + while ((!interrupted) && (one_move_rogue((dirch + 32), 1) == MOVED)) + ; + + if ( (!interrupted) && passgo && + (dungeon[rogue.row][rogue.col] & TUNNEL)) { + turn_passage(dirch + 32, 1); + } + break; + } +} + +boolean +is_passable(int row, int col) +{ + if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) || + (col > (DCOLS-1))) { + return(0); + } + if (dungeon[row][col] & HIDDEN) { + return((dungeon[row][col] & TRAP) ? 1 : 0); + } + return(dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP)); +} + +static boolean +next_to_something(int drow, int dcol) +{ + short i, j, i_end, j_end, row, col; + short pass_count = 0; + unsigned short s; + + if (confused) { + return(1); + } + if (blind) { + return(0); + } + i_end = (rogue.row < (DROWS-2)) ? 1 : 0; + j_end = (rogue.col < (DCOLS-1)) ? 1 : 0; + + for (i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { + for (j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) { + if ((i == 0) && (j == 0)) { + continue; + } + if (((rogue.row+i) == drow) && ((rogue.col+j) == dcol)) { + continue; + } + row = rogue.row + i; + col = rogue.col + j; + s = dungeon[row][col]; + if (s & HIDDEN) { + continue; + } + /* If the rogue used to be right, up, left, down, or right of + * row,col, and now isn't, then don't stop */ + if (s & (MONSTER | OBJECT | STAIRS)) { + if (((row == drow) || (col == dcol)) && + (!((row == rogue.row) || (col == rogue.col)))) { + continue; + } + return(1); + } + if (s & TRAP) { + if (!(s & HIDDEN)) { + if (((row == drow) || (col == dcol)) && + (!((row == rogue.row) || (col == rogue.col)))) { + continue; + } + return(1); + } + } + if ((((i - j) == 1) || ((i - j) == -1)) && (s & TUNNEL)) { + if (++pass_count > 1) { + return(1); + } + } + if ((s & DOOR) && ((i == 0) || (j == 0))) { + return(1); + } + } + } + return(0); +} + +boolean +can_move(int row1, int col1, int row2, int col2) +{ + if (!is_passable(row2, col2)) { + return(0); + } + if ((row1 != row2) && (col1 != col2)) { + if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) { + return(0); + } + if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) { + return(0); + } + } + return(1); +} + +void +move_onto(void) +{ + short ch, d; + boolean first_miss = 1; + + while (!is_direction(ch = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction? "); + first_miss = 0; + } + } + check_message(); + if (ch != CANCEL) { + (void)one_move_rogue(ch, 0); + } +} + +boolean +is_direction(short c, short *d) +{ + switch(c) { + case 'h': + *d = LEFT; + break; + case 'j': + *d = DOWN; + break; + case 'k': + *d = UPWARD; + break; + case 'l': + *d = RIGHT; + break; + case 'b': + *d = DOWNLEFT; + break; + case 'y': + *d = UPLEFT; + break; + case 'u': + *d = UPRIGHT; + break; + case 'n': + *d = DOWNRIGHT; + break; + case CANCEL: + break; + default: + return(0); + } + return(1); +} + +static boolean +check_hunger(boolean msg_only) +{ + short i, n; + boolean fainted = 0; + + if (rogue.moves_left == HUNGRY) { + (void)strlcpy(hunger_str, "hungry", sizeof(hunger_str)); + messagef(0, "%s", hunger_str); + print_stats(STAT_HUNGER); + } + if (rogue.moves_left == WEAK) { + (void)strlcpy(hunger_str, "weak", sizeof(hunger_str)); + messagef(1, "%s", hunger_str); + print_stats(STAT_HUNGER); + } + if (rogue.moves_left <= FAINT) { + if (rogue.moves_left == FAINT) { + (void)strlcpy(hunger_str, "faint", sizeof(hunger_str)); + messagef(1, "%s", hunger_str); + print_stats(STAT_HUNGER); + } + n = get_rand(0, (FAINT - rogue.moves_left)); + if (n > 0) { + fainted = 1; + if (rand_percent(40)) { + rogue.moves_left++; + } + messagef(1, "you faint"); + for (i = 0; i < n; i++) { + if (coin_toss()) { + mv_mons(); + } + } + messagef(1, "%s", you_can_move_again); + } + } + if (msg_only) { + return(fainted); + } + if (rogue.moves_left <= STARVE) { + killed_by(NULL, STARVATION); + } + + switch(e_rings) { + /*case -2: + Subtract 0, i.e. do nothing. + break;*/ + case -1: + rogue.moves_left -= (rogue.moves_left % 2); + break; + case 0: + rogue.moves_left--; + break; + case 1: + rogue.moves_left--; + (void)check_hunger(1); + rogue.moves_left -= (rogue.moves_left % 2); + break; + case 2: + rogue.moves_left--; + (void)check_hunger(1); + rogue.moves_left--; + break; + } + return(fainted); +} + +boolean +reg_move(void) +{ + boolean fainted; + + if ((rogue.moves_left <= HUNGRY) || (cur_level >= max_level)) { + fainted = check_hunger(0); + } else { + fainted = 0; + } + + mv_mons(); + + if (++m_moves >= 120) { + m_moves = 0; + wanderer(); + } + if (halluc) { + if (!(--halluc)) { + unhallucinate(); + } else { + hallucinate(); + } + } + if (blind) { + if (!(--blind)) { + unblind(); + } + } + if (confused) { + if (!(--confused)) { + unconfuse(); + } + } + if (bear_trap) { + bear_trap--; + } + if (levitate) { + if (!(--levitate)) { + messagef(1, "you float gently to the ground"); + if (dungeon[rogue.row][rogue.col] & TRAP) { + trap_player(rogue.row, rogue.col); + } + } + } + if (haste_self) { + if (!(--haste_self)) { + messagef(0, "you feel yourself slowing down"); + } + } + heal(); + if (auto_search > 0) { + search(auto_search, auto_search); + } + return(fainted); +} + +void +rest(int count) +{ + int i; + + interrupted = 0; + + for (i = 0; i < count; i++) { + if (interrupted) { + break; + } + (void)reg_move(); + } +} + +static char +gr_dir(void) +{ + short d; + + d = get_rand(1, 8); + + switch(d) { + case 1: + d = 'j'; + break; + case 2: + d = 'k'; + break; + case 3: + d = 'l'; + break; + case 4: + d = 'h'; + break; + case 5: + d = 'y'; + break; + case 6: + d = 'u'; + break; + case 7: + d = 'b'; + break; + case 8: + d = 'n'; + break; + } + return(d); +} + +static void +heal(void) +{ + static short heal_exp = -1, n, c = 0; + static boolean alt; + + if (rogue.hp_current == rogue.hp_max) { + c = 0; + return; + } + if (rogue.exp != heal_exp) { + heal_exp = rogue.exp; + + switch(heal_exp) { + case 1: + n = 20; + break; + case 2: + n = 18; + break; + case 3: + n = 17; + break; + case 4: + n = 14; + break; + case 5: + n = 13; + break; + case 6: + n = 10; + break; + case 7: + n = 9; + break; + case 8: + n = 8; + break; + case 9: + n = 7; + break; + case 10: + n = 4; + break; + case 11: + n = 3; + break; + case 12: + default: + n = 2; + } + } + if (++c >= n) { + c = 0; + rogue.hp_current++; + if ((alt = !alt) != 0) { + rogue.hp_current++; + } + if ((rogue.hp_current += regeneration) > rogue.hp_max) { + rogue.hp_current = rogue.hp_max; + } + print_stats(STAT_HP); + } +} + +static boolean +can_turn(short nrow, short ncol) +{ + if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) { + return(1); + } + return(0); +} + +static void +turn_passage(short dir, boolean fast) +{ + short crow = rogue.row, ccol = rogue.col, turns = 0; + short ndir = 0; + + if ((dir != 'h') && can_turn(crow, ccol + 1)) { + turns++; + ndir = 'l'; + } + if ((dir != 'l') && can_turn(crow, ccol - 1)) { + turns++; + ndir = 'h'; + } + if ((dir != 'k') && can_turn(crow + 1, ccol)) { + turns++; + ndir = 'j'; + } + if ((dir != 'j') && can_turn(crow - 1, ccol)) { + turns++; + ndir = 'k'; + } + if (turns == 1) { + multiple_move_rogue(ndir - (fast ? 32 : 96)); + } +} diff --git a/games/rogue/object.c b/games/rogue/object.c new file mode 100644 index 000000000..9897ec6ad --- /dev/null +++ b/games/rogue/object.c @@ -0,0 +1,802 @@ +/* $NetBSD: object.c,v 1.14 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)object.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: object.c,v 1.14 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * object.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +object level_objects; +unsigned short dungeon[DROWS][DCOLS]; +short foods = 0; +char *fruit = NULL; + +static object *free_list = NULL; + +fighter rogue = { + INIT_AW, /* armor */ + INIT_AW, /* weapon */ + INIT_RINGS, /* left ring */ + INIT_RINGS, /* right ring */ + INIT_HP, /* Hp current */ + INIT_HP, /* Hp max */ + INIT_STR, /* Str current */ + INIT_STR, /* Str max */ + INIT_PACK, /* pack */ + INIT_GOLD, /* gold */ + INIT_EXPLEVEL, /* exp level */ + INIT_EXP, /* exp points */ + 0, 0, /* row, col */ + INIT_CHAR, /* char */ + INIT_MOVES /* moves */ +}; + +struct id id_potions[POTIONS] = { +{100, "blue ", "of increase strength ", 0}, +{250, "red ", "of restore strength ", 0}, +{100, "green ", "of healing ", 0}, +{200, "grey ", "of extra healing ", 0}, + {10, "brown ", "of poison ", 0}, +{300, "clear ", "of raise level ", 0}, + {10, "pink ", "of blindness ", 0}, + {25, "white ", "of hallucination ", 0}, +{100, "purple ", "of detect monster ", 0}, +{100, "black ", "of detect things ", 0}, + {10, "yellow ", "of confusion ", 0}, + {80, "plaid ", "of levitation ", 0}, +{150, "burgundy ", "of haste self ", 0}, +{145, "beige ", "of see invisible ", 0} +}; + +struct id id_scrolls[SCROLS] = { +{505, "", "of protect armor ", 0}, +{200, "", "of hold monster ", 0}, +{235, "", "of enchant weapon ", 0}, +{235, "", "of enchant armor ", 0}, +{175, "", "of identify ", 0}, +{190, "", "of teleportation ", 0}, + {25, "", "of sleep ", 0}, +{610, "", "of scare monster ", 0}, +{210, "", "of remove curse ", 0}, + {80, "", "of create monster ",0}, + {25, "", "of aggravate monster ",0}, +{180, "", "of magic mapping ", 0}, + {90, "", "of confuse monster ", 0} +}; + +struct id id_weapons[WEAPONS] = { + {150, "short bow ", "", 0}, + {8, "darts ", "", 0}, + {15, "arrows ", "", 0}, + {27, "daggers ", "", 0}, + {35, "shurikens ", "", 0}, + {360, "mace ", "", 0}, + {470, "long sword ", "", 0}, + {580, "two-handed sword ", "", 0} +}; + +struct id id_armors[ARMORS] = { + {300, "leather armor ", "", (UNIDENTIFIED)}, + {300, "ring mail ", "", (UNIDENTIFIED)}, + {400, "scale mail ", "", (UNIDENTIFIED)}, + {500, "chain mail ", "", (UNIDENTIFIED)}, + {600, "banded mail ", "", (UNIDENTIFIED)}, + {600, "splint mail ", "", (UNIDENTIFIED)}, + {700, "plate mail ", "", (UNIDENTIFIED)} +}; + +struct id id_wands[WANDS] = { + {25, "", "of teleport away ",0}, + {50, "", "of slow monster ", 0}, + {8, "", "of invisibility ",0}, + {55, "", "of polymorph ",0}, + {2, "", "of haste monster ",0}, + {20, "", "of magic missile ",0}, + {20, "", "of cancellation ",0}, + {0, "", "of do nothing ",0}, + {35, "", "of drain life ",0}, + {20, "", "of cold ",0}, + {20, "", "of fire ",0} +}; + +struct id id_rings[RINGS] = { + {250, "", "of stealth ",0}, + {100, "", "of teleportation ", 0}, + {255, "", "of regeneration ",0}, + {295, "", "of slow digestion ",0}, + {200, "", "of add strength ",0}, + {250, "", "of sustain strength ",0}, + {250, "", "of dexterity ",0}, + {25, "", "of adornment ",0}, + {300, "", "of see invisible ",0}, + {290, "", "of maintain armor ",0}, + {270, "", "of searching ",0}, +}; + +static void gr_armor(object *); +static void gr_potion(object *); +static void gr_scroll(object *); +static void gr_wand(object *); +static void gr_weapon(object *, int); +static unsigned short gr_what_is(void); +static void make_party(void); +static void plant_gold(short, short, boolean); +static void put_gold(void); +static void rand_place(object *); + +void +put_objects(void) +{ + short i, n; + object *obj; + + if (cur_level < max_level) { + return; + } + n = coin_toss() ? get_rand(2, 4) : get_rand(3, 5); + while (rand_percent(33)) { + n++; + } + if (party_room != NO_ROOM) { + make_party(); + } + for (i = 0; i < n; i++) { + obj = gr_object(); + rand_place(obj); + } + put_gold(); +} + +static void +put_gold(void) +{ + short i, j; + short row,col; + boolean is_maze, is_room; + + for (i = 0; i < MAXROOMS; i++) { + is_maze = (rooms[i].is_room & R_MAZE) ? 1 : 0; + is_room = (rooms[i].is_room & R_ROOM) ? 1 : 0; + + if (!(is_room || is_maze)) { + continue; + } + if (is_maze || rand_percent(GOLD_PERCENT)) { + for (j = 0; j < 50; j++) { + row = get_rand(rooms[i].top_row+1, + rooms[i].bottom_row-1); + col = get_rand(rooms[i].left_col+1, + rooms[i].right_col-1); + if ((dungeon[row][col] == FLOOR) || + (dungeon[row][col] == TUNNEL)) { + plant_gold(row, col, is_maze); + break; + } + } + } + } +} + +static void +plant_gold(short row, short col, boolean is_maze) +{ + object *obj; + + obj = alloc_object(); + obj->row = row; obj->col = col; + obj->what_is = GOLD; + obj->quantity = get_rand((2 * cur_level), (16 * cur_level)); + if (is_maze) { + obj->quantity += obj->quantity / 2; + } + dungeon[row][col] |= OBJECT; + (void)add_to_pack(obj, &level_objects, 0); +} + +void +place_at(object *obj, int row, int col) +{ + obj->row = row; + obj->col = col; + dungeon[row][col] |= OBJECT; + (void)add_to_pack(obj, &level_objects, 0); +} + +object * +object_at(object *pack, short row, short col) +{ + object *obj = NULL; + + if (dungeon[row][col] & (MONSTER | OBJECT)) { + obj = pack->next_object; + + while (obj && ((obj->row != row) || (obj->col != col))) { + obj = obj->next_object; + } + if (!obj) { + messagef(1, "object_at(): inconsistent"); + } + } + return(obj); +} + +object * +get_letter_object(int ch) +{ + object *obj; + + obj = rogue.pack.next_object; + + while (obj && (obj->ichar != ch)) { + obj = obj->next_object; + } + return(obj); +} + +void +free_stuff(object *objlist) +{ + object *obj; + + while (objlist->next_object) { + obj = objlist->next_object; + objlist->next_object = + objlist->next_object->next_object; + free_object(obj); + } +} + +const char * +name_of(const object *obj) +{ + const char *retstring; + + switch(obj->what_is) { + case SCROL: + retstring = obj->quantity > 1 ? "scrolls " : "scroll "; + break; + case POTION: + retstring = obj->quantity > 1 ? "potions " : "potion "; + break; + case FOOD: + if (obj->which_kind == RATION) { + retstring = "food "; + } else { + retstring = fruit; + } + break; + case WAND: + retstring = is_wood[obj->which_kind] ? "staff " : "wand "; + break; + case WEAPON: + switch(obj->which_kind) { + case DART: + retstring=obj->quantity > 1 ? "darts " : "dart "; + break; + case ARROW: + retstring=obj->quantity > 1 ? "arrows " : "arrow "; + break; + case DAGGER: + retstring=obj->quantity > 1 ? "daggers " : "dagger "; + break; + case SHURIKEN: + retstring=obj->quantity > 1?"shurikens ":"shuriken "; + break; + default: + retstring = id_weapons[obj->which_kind].title; + } + break; + case ARMOR: + retstring = "armor "; + break; + case RING: + retstring = "ring "; + break; + case AMULET: + retstring = "amulet "; + break; + default: + retstring = "unknown "; + break; + } + return(retstring); +} + +object * +gr_object(void) +{ + object *obj; + + obj = alloc_object(); + + if (foods < (cur_level / 3)) { + obj->what_is = FOOD; + foods++; + } else { + obj->what_is = gr_what_is(); + } + switch(obj->what_is) { + case SCROL: + gr_scroll(obj); + break; + case POTION: + gr_potion(obj); + break; + case WEAPON: + gr_weapon(obj, 1); + break; + case ARMOR: + gr_armor(obj); + break; + case WAND: + gr_wand(obj); + break; + case FOOD: + get_food(obj, 0); + break; + case RING: + gr_ring(obj, 1); + break; + } + return(obj); +} + +static unsigned short +gr_what_is(void) +{ + short percent; + unsigned short what_is; + + percent = get_rand(1, 91); + + if (percent <= 30) { + what_is = SCROL; + } else if (percent <= 60) { + what_is = POTION; + } else if (percent <= 64) { + what_is = WAND; + } else if (percent <= 74) { + what_is = WEAPON; + } else if (percent <= 83) { + what_is = ARMOR; + } else if (percent <= 88) { + what_is = FOOD; + } else { + what_is = RING; + } + return(what_is); +} + +static void +gr_scroll(object *obj) +{ + short percent; + + percent = get_rand(0, 91); + + obj->what_is = SCROL; + + if (percent <= 5) { + obj->which_kind = PROTECT_ARMOR; + } else if (percent <= 10) { + obj->which_kind = HOLD_MONSTER; + } else if (percent <= 20) { + obj->which_kind = CREATE_MONSTER; + } else if (percent <= 35) { + obj->which_kind = IDENTIFY; + } else if (percent <= 43) { + obj->which_kind = TELEPORT; + } else if (percent <= 50) { + obj->which_kind = SLEEP; + } else if (percent <= 55) { + obj->which_kind = SCARE_MONSTER; + } else if (percent <= 64) { + obj->which_kind = REMOVE_CURSE; + } else if (percent <= 69) { + obj->which_kind = ENCH_ARMOR; + } else if (percent <= 74) { + obj->which_kind = ENCH_WEAPON; + } else if (percent <= 80) { + obj->which_kind = AGGRAVATE_MONSTER; + } else if (percent <= 86) { + obj->which_kind = CON_MON; + } else { + obj->which_kind = MAGIC_MAPPING; + } +} + +static void +gr_potion(object *obj) +{ + short percent; + + percent = get_rand(1, 118); + + obj->what_is = POTION; + + if (percent <= 5) { + obj->which_kind = RAISE_LEVEL; + } else if (percent <= 15) { + obj->which_kind = DETECT_OBJECTS; + } else if (percent <= 25) { + obj->which_kind = DETECT_MONSTER; + } else if (percent <= 35) { + obj->which_kind = INCREASE_STRENGTH; + } else if (percent <= 45) { + obj->which_kind = RESTORE_STRENGTH; + } else if (percent <= 55) { + obj->which_kind = HEALING; + } else if (percent <= 65) { + obj->which_kind = EXTRA_HEALING; + } else if (percent <= 75) { + obj->which_kind = BLINDNESS; + } else if (percent <= 85) { + obj->which_kind = HALLUCINATION; + } else if (percent <= 95) { + obj->which_kind = CONFUSION; + } else if (percent <= 105) { + obj->which_kind = POISON; + } else if (percent <= 110) { + obj->which_kind = LEVITATION; + } else if (percent <= 114) { + obj->which_kind = HASTE_SELF; + } else { + obj->which_kind = SEE_INVISIBLE; + } +} + +static void +gr_weapon(object *obj, int assign_wk) +{ + short percent; + short i; + short blessing, increment; + + obj->what_is = WEAPON; + if (assign_wk) { + obj->which_kind = get_rand(0, (WEAPONS - 1)); + } + if ((obj->which_kind == ARROW) || (obj->which_kind == DAGGER) || + (obj->which_kind == SHURIKEN) || (obj->which_kind == DART)) { + obj->quantity = get_rand(3, 15); + obj->quiver = get_rand(0, 126); + } else { + obj->quantity = 1; + } + obj->hit_enchant = obj->d_enchant = 0; + + percent = get_rand(1, 96); + blessing = get_rand(1, 3); + + if (percent <= 32) { + if (percent <= 16) { + increment = 1; + } else { + increment = -1; + obj->is_cursed = 1; + } + for (i = 0; i < blessing; i++) { + if (coin_toss()) { + obj->hit_enchant += increment; + } else { + obj->d_enchant += increment; + } + } + } + switch(obj->which_kind) { + case BOW: + case DART: + obj->damage = "1d1"; + break; + case ARROW: + obj->damage = "1d2"; + break; + case DAGGER: + obj->damage = "1d3"; + break; + case SHURIKEN: + obj->damage = "1d4"; + break; + case MACE: + obj->damage = "2d3"; + break; + case LONG_SWORD: + obj->damage = "3d4"; + break; + case TWO_HANDED_SWORD: + obj->damage = "4d5"; + break; + } +} + +static void +gr_armor(object *obj) +{ + short percent; + short blessing; + + obj->what_is = ARMOR; + obj->which_kind = get_rand(0, (ARMORS - 1)); + obj->class = obj->which_kind + 2; + if ((obj->which_kind == PLATE) || (obj->which_kind == SPLINT)) { + obj->class--; + } + obj->is_protected = 0; + obj->d_enchant = 0; + + percent = get_rand(1, 100); + blessing = get_rand(1, 3); + + if (percent <= 16) { + obj->is_cursed = 1; + obj->d_enchant -= blessing; + } else if (percent <= 33) { + obj->d_enchant += blessing; + } +} + +static void +gr_wand(object *obj) +{ + obj->what_is = WAND; + obj->which_kind = get_rand(0, (WANDS - 1)); + obj->class = get_rand(3, 7); +} + +void +get_food(object *obj, boolean force_ration) +{ + obj->what_is = FOOD; + + if (force_ration || rand_percent(80)) { + obj->which_kind = RATION; + } else { + obj->which_kind = FRUIT; + } +} + +void +put_stairs(void) +{ + short row, col; + + gr_row_col(&row, &col, (FLOOR | TUNNEL)); + dungeon[row][col] |= STAIRS; +} + +int +get_armor_class(const object *obj) +{ + if (obj) { + return(obj->class + obj->d_enchant); + } + return(0); +} + +object * +alloc_object(void) +{ + object *obj; + + if (free_list) { + obj = free_list; + free_list = free_list->next_object; + } else if (!(obj = md_malloc(sizeof(object)))) { + messagef(0, "cannot allocate object, saving game"); + save_into_file(error_file); + clean_up("alloc_object: save failed"); + } + obj->quantity = 1; + obj->ichar = 'L'; + obj->picked_up = obj->is_cursed = 0; + obj->in_use_flags = NOT_USED; + obj->identified = UNIDENTIFIED; + obj->damage = "1d1"; + return(obj); +} + +void +free_object(object *obj) +{ + obj->next_object = free_list; + free_list = obj; +} + +static void +make_party(void) +{ + short n; + + party_room = gr_room(); + + n = rand_percent(99) ? party_objects(party_room) : 11; + if (rand_percent(99)) { + party_monsters(party_room, n); + } +} + +void +show_objects(void) +{ + object *obj; + short mc, rc, row, col; + object *monster; + + obj = level_objects.next_object; + + while (obj) { + row = obj->row; + col = obj->col; + + rc = get_mask_char(obj->what_is); + + if (dungeon[row][col] & MONSTER) { + if ((monster = + object_at(&level_monsters, row, col)) != NULL) { + monster->trail_char = rc; + } + } + mc = mvinch(row, col); + if (((mc < 'A') || (mc > 'Z')) && + ((row != rogue.row) || (col != rogue.col))) { + mvaddch(row, col, rc); + } + obj = obj->next_object; + } + + monster = level_monsters.next_object; + + while (monster) { + if (monster->m_flags & IMITATES) { + mvaddch(monster->row, monster->col, (int)monster->disguise); + } + monster = monster->next_monster; + } +} + +void +put_amulet(void) +{ + object *obj; + + obj = alloc_object(); + obj->what_is = AMULET; + rand_place(obj); +} + +static void +rand_place(object *obj) +{ + short row, col; + + gr_row_col(&row, &col, (FLOOR | TUNNEL)); + place_at(obj, row, col); +} + +void +c_object_for_wizard(void) +{ + short ch, max, wk; + object *obj; + char buf[80]; + + max = 0; + if (pack_count(NULL) >= MAX_PACK_COUNT) { + messagef(0, "pack full"); + return; + } + messagef(0, "type of object?"); + + while (r_index("!?:)]=/,\033", (ch = rgetchar()), 0) == -1) { + sound_bell(); + } + check_message(); + + if (ch == '\033') { + return; + } + obj = alloc_object(); + + switch(ch) { + case '!': + obj->what_is = POTION; + max = POTIONS - 1; + break; + case '?': + obj->what_is = SCROL; + max = SCROLS - 1; + break; + case ',': + obj->what_is = AMULET; + break; + case ':': + get_food(obj, 0); + break; + case ')': + gr_weapon(obj, 0); + max = WEAPONS - 1; + break; + case ']': + gr_armor(obj); + max = ARMORS - 1; + break; + case '/': + gr_wand(obj); + max = WANDS - 1; + break; + case '=': + max = RINGS - 1; + obj->what_is = RING; + break; + } + if ((ch != ',') && (ch != ':')) { +GIL: + if (get_input_line("which kind?", "", buf, sizeof(buf), "", 0, 1)) { + wk = get_number(buf); + if ((wk >= 0) && (wk <= max)) { + obj->which_kind = wk; + if (obj->what_is == RING) { + gr_ring(obj, 0); + } + } else { + sound_bell(); + goto GIL; + } + } else { + free_object(obj); + return; + } + } + get_desc(obj, buf, sizeof(buf)); + messagef(0, "%s", buf); + (void)add_to_pack(obj, &rogue.pack, 1); +} diff --git a/games/rogue/pack.c b/games/rogue/pack.c new file mode 100644 index 000000000..f8aed6ea6 --- /dev/null +++ b/games/rogue/pack.c @@ -0,0 +1,574 @@ +/* $NetBSD: pack.c,v 1.12 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)pack.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: pack.c,v 1.12 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * pack.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +const char curse_message[] = "you can't, it appears to be cursed"; + +static object *check_duplicate(object *, object *); +static boolean is_pack_letter(short *, unsigned short *); +static boolean mask_pack(const object *, unsigned short); +static short next_avail_ichar(void); + +object * +add_to_pack(object *obj, object *pack, int condense) +{ + object *op; + + if (condense) { + if ((op = check_duplicate(obj, pack)) != NULL) { + free_object(obj); + return(op); + } else { + obj->ichar = next_avail_ichar(); + } + } + if (pack->next_object == 0) { + pack->next_object = obj; + } else { + op = pack->next_object; + + while (op->next_object) { + op = op->next_object; + } + op->next_object = obj; + } + obj->next_object = 0; + return(obj); +} + +void +take_from_pack(object *obj, object *pack) +{ + while (pack->next_object != obj) { + pack = pack->next_object; + } + pack->next_object = pack->next_object->next_object; +} + +/* Note: *status is set to 0 if the rogue attempts to pick up a scroll + * of scare-monster and it turns to dust. *status is otherwise set to 1. + */ + +object * +pick_up(int row, int col, short *status) +{ + object *obj; + + *status = 1; + + if (levitate) { + messagef(0, "you're floating in the air!"); + return NULL; + } + obj = object_at(&level_objects, row, col); + if (!obj) { + messagef(1, "pick_up(): inconsistent"); + return(obj); + } + if ( (obj->what_is == SCROL) && + (obj->which_kind == SCARE_MONSTER) && + obj->picked_up) { + messagef(0, "the scroll turns to dust as you pick it up"); + dungeon[row][col] &= (~OBJECT); + vanish(obj, 0, &level_objects); + *status = 0; + if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) { + id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED; + } + return NULL; + } + if (obj->what_is == GOLD) { + rogue.gold += obj->quantity; + dungeon[row][col] &= ~(OBJECT); + take_from_pack(obj, &level_objects); + print_stats(STAT_GOLD); + return(obj); /* obj will be free_object()ed in caller */ + } + if (pack_count(obj) >= MAX_PACK_COUNT) { + messagef(1, "pack too full"); + return NULL; + } + dungeon[row][col] &= ~(OBJECT); + take_from_pack(obj, &level_objects); + obj = add_to_pack(obj, &rogue.pack, 1); + obj->picked_up = 1; + return(obj); +} + +void +drop(void) +{ + object *obj, *new; + short ch; + char desc[DCOLS]; + + if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) { + messagef(0, "there's already something there"); + return; + } + if (!rogue.pack.next_object) { + messagef(0, "you have nothing to drop"); + return; + } + if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->in_use_flags & BEING_WIELDED) { + if (obj->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + unwield(rogue.weapon); + } else if (obj->in_use_flags & BEING_WORN) { + if (obj->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + mv_aquatars(); + unwear(rogue.armor); + print_stats(STAT_ARMOR); + } else if (obj->in_use_flags & ON_EITHER_HAND) { + if (obj->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + un_put_on(obj); + } + obj->row = rogue.row; + obj->col = rogue.col; + + if ((obj->quantity > 1) && (obj->what_is != WEAPON)) { + obj->quantity--; + new = alloc_object(); + *new = *obj; + new->quantity = 1; + obj = new; + } else { + obj->ichar = 'L'; + take_from_pack(obj, &rogue.pack); + } + place_at(obj, rogue.row, rogue.col); + get_desc(obj, desc, sizeof(desc)); + messagef(0, "dropped %s", desc); + (void)reg_move(); +} + +static object * +check_duplicate(object *obj, object *pack) +{ + object *op; + + if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) { + return(0); + } + if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) { + return(0); + } + op = pack->next_object; + + while (op) { + if ((op->what_is == obj->what_is) && + (op->which_kind == obj->which_kind)) { + + if ((obj->what_is != WEAPON) || + ((obj->what_is == WEAPON) && + ((obj->which_kind == ARROW) || + (obj->which_kind == DAGGER) || + (obj->which_kind == DART) || + (obj->which_kind == SHURIKEN)) && + (obj->quiver == op->quiver))) { + op->quantity += obj->quantity; + return(op); + } + } + op = op->next_object; + } + return(0); +} + +static short +next_avail_ichar(void) +{ + object *obj; + int i; + boolean ichars[26]; + + for (i = 0; i < 26; i++) { + ichars[i] = 0; + } + obj = rogue.pack.next_object; + while (obj) { + if (obj->ichar >= 'a' && obj->ichar <= 'z') { + ichars[(obj->ichar - 'a')] = 1; + } + obj = obj->next_object; + } + for (i = 0; i < 26; i++) { + if (!ichars[i]) { + return(i + 'a'); + } + } + return('?'); +} + +void +wait_for_ack(void) +{ + while (rgetchar() != ' ') + ; +} + +short +pack_letter(const char *prompt, unsigned short mask) +{ + short ch; + unsigned short tmask = mask; + + if (!mask_pack(&rogue.pack, mask)) { + messagef(0, "nothing appropriate"); + return(CANCEL); + } + for (;;) { + + messagef(0, "%s", prompt); + + for (;;) { + ch = rgetchar(); + if (!is_pack_letter(&ch, &mask)) { + sound_bell(); + } else { + break; + } + } + + if (ch == LIST) { + check_message(); + mask = tmask; + inventory(&rogue.pack, mask); + } else { + break; + } + mask = tmask; + } + check_message(); + return(ch); +} + +void +take_off(void) +{ + char desc[DCOLS]; + object *obj; + + if (rogue.armor) { + if (rogue.armor->is_cursed) { + messagef(0, "%s", curse_message); + } else { + mv_aquatars(); + obj = rogue.armor; + unwear(rogue.armor); + get_desc(obj, desc, sizeof(desc)); + messagef(0, "was wearing %s", desc); + print_stats(STAT_ARMOR); + (void)reg_move(); + } + } else { + messagef(0, "not wearing any"); + } +} + +void +wear(void) +{ + short ch; + object *obj; + char desc[DCOLS]; + + if (rogue.armor) { + messagef(0, "you're already wearing some"); + return; + } + ch = pack_letter("wear what?", ARMOR); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != ARMOR) { + messagef(0, "you can't wear that"); + return; + } + obj->identified = 1; + get_desc(obj, desc, sizeof(desc)); + messagef(0, "wearing %s", desc); + do_wear(obj); + print_stats(STAT_ARMOR); + (void)reg_move(); +} + +void +unwear(object *obj) +{ + if (obj) { + obj->in_use_flags &= (~BEING_WORN); + } + rogue.armor = NULL; +} + +void +do_wear(object *obj) +{ + rogue.armor = obj; + obj->in_use_flags |= BEING_WORN; + obj->identified = 1; +} + +void +wield(void) +{ + short ch; + object *obj; + char desc[DCOLS]; + + if (rogue.weapon && rogue.weapon->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + ch = pack_letter("wield what?", WEAPON); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "No such item."); + return; + } + if (obj->what_is & (ARMOR | RING)) { + messagef(0, "you can't wield %s", + ((obj->what_is == ARMOR) ? "armor" : "rings")); + return; + } + if (obj->in_use_flags & BEING_WIELDED) { + messagef(0, "in use"); + } else { + unwield(rogue.weapon); + get_desc(obj, desc, sizeof(desc)); + messagef(0, "wielding %s", desc); + do_wield(obj); + (void)reg_move(); + } +} + +void +do_wield(object *obj) +{ + rogue.weapon = obj; + obj->in_use_flags |= BEING_WIELDED; +} + +void +unwield(object *obj) +{ + if (obj) { + obj->in_use_flags &= (~BEING_WIELDED); + } + rogue.weapon = NULL; +} + +void +call_it(void) +{ + short ch; + object *obj; + struct id *id_table; + char buf[MAX_TITLE_LENGTH+2]; + + ch = pack_letter("call what?", (SCROL | POTION | WAND | RING)); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (!(obj->what_is & (SCROL | POTION | WAND | RING))) { + messagef(0, "surely you already know what that's called"); + return; + } + id_table = get_id_table(obj); + + if (get_input_line("call it:", "", buf, sizeof(buf), + id_table[obj->which_kind].title, 1, 1)) { + id_table[obj->which_kind].id_status = CALLED; + (void)strlcpy(id_table[obj->which_kind].title, buf, + sizeof(id_table[obj->which_kind].title)); + } +} + +short +pack_count(const object *new_obj) +{ + object *obj; + short count = 0; + + obj = rogue.pack.next_object; + + while (obj) { + if (obj->what_is != WEAPON) { + count += obj->quantity; + } else if (!new_obj) { + count++; + } else if ((new_obj->what_is != WEAPON) || + ((obj->which_kind != ARROW) && + (obj->which_kind != DAGGER) && + (obj->which_kind != DART) && + (obj->which_kind != SHURIKEN)) || + (new_obj->which_kind != obj->which_kind) || + (obj->quiver != new_obj->quiver)) { + count++; + } + obj = obj->next_object; + } + return(count); +} + +static boolean +mask_pack(const object *pack, unsigned short mask) +{ + while (pack->next_object) { + pack = pack->next_object; + if (pack->what_is & mask) { + return(1); + } + } + return(0); +} + +static boolean +is_pack_letter(short *c, unsigned short *mask) +{ + if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') || + (*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) { + switch(*c) { + case '?': + *mask = SCROL; + break; + case '!': + *mask = POTION; + break; + case ':': + *mask = FOOD; + break; + case ')': + *mask = WEAPON; + break; + case ']': + *mask = ARMOR; + break; + case '/': + *mask = WAND; + break; + case '=': + *mask = RING; + break; + case ',': + *mask = AMULET; + break; + } + *c = LIST; + return(1); + } + return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST)); +} + +boolean +has_amulet(void) +{ + return(mask_pack(&rogue.pack, AMULET)); +} + +void +kick_into_pack(void) +{ + object *obj; + char desc[DCOLS]; + short stat; + + if (!(dungeon[rogue.row][rogue.col] & OBJECT)) { + messagef(0, "nothing here"); + } else { + if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) { + get_desc(obj, desc, sizeof(desc)); + if (obj->what_is == GOLD) { + messagef(0, "%s", desc); + free_object(obj); + } else { + messagef(0, "%s(%c)", desc, obj->ichar); + } + } + if (obj || (!stat)) { + (void)reg_move(); + } + } +} diff --git a/games/rogue/pathnames.h b/games/rogue/pathnames.h new file mode 100644 index 000000000..d6e91a570 --- /dev/null +++ b/games/rogue/pathnames.h @@ -0,0 +1,35 @@ +/* $NetBSD: pathnames.h,v 1.5 2008/01/14 03:50:02 dholland Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_SCOREFILE "/var/games/rogue.scores" +#define _PATH_SCREENDUMP "rogue.screen" diff --git a/games/rogue/play.c b/games/rogue/play.c new file mode 100644 index 000000000..88245384f --- /dev/null +++ b/games/rogue/play.c @@ -0,0 +1,299 @@ +/* $NetBSD: play.c,v 1.9 2008/01/14 03:50:02 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)play.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: play.c,v 1.9 2008/01/14 03:50:02 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * play.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +boolean interrupted = 0; + +static const char unknown_command[] = "unknown command"; + +void +play_level(void) +{ + short ch; + int count; + + for (;;) { + interrupted = 0; + if (hit_message[0]) { + messagef(1, "%s", hit_message); + hit_message[0] = 0; + } + if (trap_door) { + trap_door = 0; + return; + } + move(rogue.row, rogue.col); + refresh(); + + ch = rgetchar(); +CMCH: + check_message(); + count = 0; +CH: + switch(ch) { + case '.': + rest((count > 0) ? count : 1); + break; + case 's': + search(((count > 0) ? count : 1), 0); + break; + case 'i': + inventory(&rogue.pack, ALL_OBJECTS); + break; + case 'f': + fight(0); + break; + case 'F': + fight(1); + break; + case 'h': + case 'j': + case 'k': + case 'l': + case 'y': + case 'u': + case 'n': + case 'b': + (void)one_move_rogue(ch, 1); + break; + case 'H': + case 'J': + case 'K': + case 'L': + case 'B': + case 'Y': + case 'U': + case 'N': + case '\010': + case '\012': + case '\013': + case '\014': + case '\031': + case '\025': + case '\016': + case '\002': + multiple_move_rogue(ch); + break; + case 'e': + eat(); + break; + case 'q': + quaff(); + break; + case 'r': + read_scroll(); + break; + case 'm': + move_onto(); + break; + case ',': + kick_into_pack(); + break; + case 'd': + drop(); + break; + case 'P': + put_on_ring(); + break; + case 'R': + remove_ring(); + break; + case '\020': + do { + remessage(count++); + ch = rgetchar(); + } while (ch == '\020'); + goto CMCH; + break; + case '\027': + wizardize(); + break; + case '>': + if (drop_check()) { + return; + } + break; + case '<': + if (check_up()) { + return; + } + break; + case ')': + case ']': + inv_armor_weapon(ch == ')'); + break; + case '=': + inv_rings(); + break; + case '^': + id_trap(); + break; + case '/': + id_type(); + break; + case '?': + id_com(); + break; + case '!': + do_shell(); + break; + case 'o': + edit_opts(); + break; + case 'I': + single_inv(0); + break; + case 'T': + take_off(); + break; + case 'W': + wear(); + break; + case 'w': + wield(); + break; + case 'c': + call_it(); + break; + case 'z': + zapp(); + break; + case 't': + throw(); + break; + case 'v': + messagef(0, "rogue-clone: Version III. (Tim Stoehr was here), tektronix!zeus!tims"); + break; + case 'Q': + quit(0); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + move(rogue.row, rogue.col); + refresh(); + do { + if (count < 100) { + count = (10 * count) + (ch - '0'); + } + ch = rgetchar(); + } while (is_digit(ch)); + if (ch != CANCEL) { + goto CH; + } + break; + case ' ': + break; + case '\011': + if (wizard) { + inventory(&level_objects, ALL_OBJECTS); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\023': + if (wizard) { + draw_magic_map(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\024': + if (wizard) { + show_traps(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\017': + if (wizard) { + show_objects(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\001': + show_average_hp(); + break; + case '\003': + if (wizard) { + c_object_for_wizard(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case '\015': + if (wizard) { + show_monsters(); + } else { + messagef(0, "%s", unknown_command); + } + break; + case 'S': + save_game(); + break; + default: + messagef(0, "%s", unknown_command); + break; + } + } +} diff --git a/games/rogue/random.c b/games/rogue/random.c new file mode 100644 index 000000000..f119c0f9c --- /dev/null +++ b/games/rogue/random.c @@ -0,0 +1,146 @@ +/* $NetBSD: random.c,v 1.8 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)random.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: random.c,v 1.8 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +#include "rogue.h" + +/* + * random.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +static long rntb[32] = { + 3, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, + 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb, 0x7449e56b, + 0xbeb1dbb0, 0xab5c5918, 0x946554fd, 0x8c2e680f, 0xeb3d799f, + 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, 0xe369735d, + 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, + 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, + 0x8999220b, 0x27fb47b9 +}; + +static long *fptr = &rntb[4]; +static long *rptr = &rntb[1]; +static long *state = &rntb[1]; +static int rand_type = 3; +static int rand_deg = 31; +static int rand_sep = 3; +static long *end_ptr = &rntb[32]; + +static long rrandom(void); + +void +srrandom(int x) +{ + int i; + + state[0] = x; + if (rand_type != 0) { + for (i = 1; i < rand_deg; i++) { + state[i] = 1103515245 * state[i - 1] + 12345; + } + fptr = &state[rand_sep]; + rptr = &state[0]; + for (i = 0; i < 10 * rand_deg; i++) { + (void)rrandom(); + } + } +} + +static long +rrandom(void) +{ + long i; + + if (rand_type == 0) { + i = state[0] = (state[0]*1103515245 + 12345) & 0x7fffffff; + } else { + *fptr += *rptr; + i = (*fptr >> 1) & 0x7fffffff; + if (++fptr >= end_ptr) { + fptr = state; + ++rptr; + } else { + if (++rptr >= end_ptr) { + rptr = state; + } + } + } + return(i); +} + +int +get_rand(int x, int y) +{ + int r, t; + long lr; + + if (x > y) { + t = y; + y = x; + x = t; + } + lr = rrandom(); + lr &= 0x00003fffL; + r = (int)lr; + r = (r % ((y - x) + 1)) + x; + return(r); +} + +int +rand_percent(int percentage) +{ + return(get_rand(1, 100) <= percentage); +} + +int +coin_toss(void) +{ + return(((rrandom() & 01) ? 1 : 0)); +} diff --git a/games/rogue/ring.c b/games/rogue/ring.c new file mode 100644 index 000000000..5cdcf5fbf --- /dev/null +++ b/games/rogue/ring.c @@ -0,0 +1,338 @@ +/* $NetBSD: ring.c,v 1.9 2008/01/14 03:50:02 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ring.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: ring.c,v 1.9 2008/01/14 03:50:02 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * ring.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static const char left_or_right[] = "left or right hand?"; +static const char no_ring[] = "there's no ring on that hand"; + +short stealthy; +short r_rings; +short add_strength; +short e_rings; +short regeneration; +short ring_exp; +short auto_search; +boolean r_teleport; +boolean r_see_invisible; +boolean sustain_strength; +boolean maintain_armor; + +void +put_on_ring(void) +{ + short ch; + char desc[DCOLS]; + object *ring; + + if (r_rings == 2) { + messagef(0, "wearing two rings already"); + return; + } + if ((ch = pack_letter("put on what?", RING)) == CANCEL) { + return; + } + if (!(ring = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (!(ring->what_is & RING)) { + messagef(0, "that's not a ring"); + return; + } + if (ring->in_use_flags & (ON_LEFT_HAND | ON_RIGHT_HAND)) { + messagef(0, "that ring is already being worn"); + return; + } + if (r_rings == 1) { + ch = (rogue.left_ring ? 'r' : 'l'); + } else { + messagef(0, "%s", left_or_right); + do { + ch = rgetchar(); + } while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') && (ch != '\n') && + (ch != '\r')); + } + if ((ch != 'l') && (ch != 'r')) { + check_message(); + return; + } + if (((ch == 'l') && rogue.left_ring)||((ch == 'r') && rogue.right_ring)) { + check_message(); + messagef(0, "there's already a ring on that hand"); + return; + } + if (ch == 'l') { + do_put_on(ring, 1); + } else { + do_put_on(ring, 0); + } + ring_stats(1); + check_message(); + get_desc(ring, desc, sizeof(desc)); + messagef(0, "%s", desc); + (void)reg_move(); +} + +/* + * Do not call ring_stats() from within do_put_on(). It will cause + * serious problems when do_put_on() is called from read_pack() in restore(). + */ + +void +do_put_on(object *ring, boolean on_left) +{ + if (on_left) { + ring->in_use_flags |= ON_LEFT_HAND; + rogue.left_ring = ring; + } else { + ring->in_use_flags |= ON_RIGHT_HAND; + rogue.right_ring = ring; + } +} + +void +remove_ring(void) +{ + boolean left = 0, right = 0; + short ch; + char buf[DCOLS]; + object *ring; + + ring = NULL; + if (r_rings == 0) { + inv_rings(); + } else if (rogue.left_ring && !rogue.right_ring) { + left = 1; + } else if (!rogue.left_ring && rogue.right_ring) { + right = 1; + } else { + messagef(0, "%s", left_or_right); + do { + ch = rgetchar(); + } while ((ch != CANCEL) && (ch != 'l') && (ch != 'r') && + (ch != '\n') && (ch != '\r')); + left = (ch == 'l'); + right = (ch == 'r'); + check_message(); + } + if (left || right) { + if (left) { + if (rogue.left_ring) { + ring = rogue.left_ring; + } else { + messagef(0, "%s", no_ring); + } + } else { + if (rogue.right_ring) { + ring = rogue.right_ring; + } else { + messagef(0, "%s", no_ring); + } + } + if (ring->is_cursed) { + messagef(0, "%s", curse_message); + } else { + un_put_on(ring); + get_desc(ring, buf, sizeof(buf)); + messagef(0, "removed %s", buf); + (void)reg_move(); + } + } +} + +void +un_put_on(object *ring) +{ + if (ring && (ring->in_use_flags & ON_LEFT_HAND)) { + ring->in_use_flags &= (~ON_LEFT_HAND); + rogue.left_ring = NULL; + } else if (ring && (ring->in_use_flags & ON_RIGHT_HAND)) { + ring->in_use_flags &= (~ON_RIGHT_HAND); + rogue.right_ring = NULL; + } + ring_stats(1); +} + +void +gr_ring(object *ring, boolean assign_wk) +{ + ring->what_is = RING; + if (assign_wk) { + ring->which_kind = get_rand(0, (RINGS - 1)); + } + ring->class = 0; + + switch(ring->which_kind) { + /* + case STEALTH: + break; + case SLOW_DIGEST: + break; + case REGENERATION: + break; + case R_SEE_INVISIBLE: + break; + case SUSTAIN_STRENGTH: + break; + case R_MAINTAIN_ARMOR: + break; + case SEARCHING: + break; + */ + case R_TELEPORT: + ring->is_cursed = 1; + break; + case ADD_STRENGTH: + case DEXTERITY: + while ((ring->class = (get_rand(0, 4) - 2)) == 0) + ; + ring->is_cursed = (ring->class < 0); + break; + case ADORNMENT: + ring->is_cursed = coin_toss(); + break; + } +} + +void +inv_rings(void) +{ + char buf[DCOLS]; + + if (r_rings == 0) { + messagef(0, "not wearing any rings"); + } else { + if (rogue.left_ring) { + get_desc(rogue.left_ring, buf, sizeof(buf)); + messagef(0, "%s", buf); + } + if (rogue.right_ring) { + get_desc(rogue.right_ring, buf, sizeof(buf)); + messagef(0, "%s", buf); + } + } + if (wizard) { + messagef(0, "ste %d, r_r %d, e_r %d, r_t %d, s_s %d, a_s %d, reg %d, r_e %d, s_i %d, m_a %d, aus %d", + stealthy, r_rings, e_rings, r_teleport, sustain_strength, + add_strength, regeneration, ring_exp, r_see_invisible, + maintain_armor, auto_search); + } +} + +void +ring_stats(boolean pr) +{ + short i; + object *ring; + + stealthy = 0; + r_rings = 0; + e_rings = 0; + r_teleport = 0; + sustain_strength = 0; + add_strength = 0; + regeneration = 0; + ring_exp = 0; + r_see_invisible = 0; + maintain_armor = 0; + auto_search = 0; + + for (i = 0; i < 2; i++) { + if (!(ring = ((i == 0) ? rogue.left_ring : rogue.right_ring))) { + continue; + } + r_rings++; + e_rings++; + switch(ring->which_kind) { + case STEALTH: + stealthy++; + break; + case R_TELEPORT: + r_teleport = 1; + break; + case REGENERATION: + regeneration++; + break; + case SLOW_DIGEST: + e_rings -= 2; + break; + case ADD_STRENGTH: + add_strength += ring->class; + break; + case SUSTAIN_STRENGTH: + sustain_strength = 1; + break; + case DEXTERITY: + ring_exp += ring->class; + break; + case ADORNMENT: + break; + case R_SEE_INVISIBLE: + r_see_invisible = 1; + break; + case MAINTAIN_ARMOR: + maintain_armor = 1; + break; + case SEARCHING: + auto_search += 2; + break; + } + } + if (pr) { + print_stats(STAT_STRENGTH); + relight(); + } +} diff --git a/games/rogue/rogue.6 b/games/rogue/rogue.6 new file mode 100644 index 000000000..4598c7b44 --- /dev/null +++ b/games/rogue/rogue.6 @@ -0,0 +1,107 @@ +.\" $NetBSD: rogue.6,v 1.11 2005/09/15 02:09:41 wiz Exp $ +.\" +.\" Copyright (c) 1988, 1993 +.\" The Regents of the University of California. 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 of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. +.\" +.\" @(#)rogue.6 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt ROGUE 6 +.Os +.Sh NAME +.Nm rogue +.Nd exploring The Dungeons of Doom +.Sh SYNOPSIS +.Nm +.Op Fl s +.Op Ar save_file +.\" .Op Fl r +.\" .Op Fl d +.Sh DESCRIPTION +.Nm +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 +.Ic \&? +will give you a list of the available commands and the command +.Ic \&/ +will identify the things you see on the screen. +.Pp +To win the game (as opposed to merely playing to beat other people's 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 heroes. +.Pp +When the game ends, either by your death, when you quit, or if you (by +some miracle) manage to win, +.Nm +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. +.Pp +If +.Ar save_file +is specified, +rogue will be restored from the specified saved game file. +.Pp +The +.Fl s +option will print out the list of scores. +.Pp +For more detailed directions, read the document +.Rs +.%T A Guide to the Dungeons of Doom +.Re +.Sh FILES +.Bl -tag -width /var/games/rogue.scores -compact +.It Pa /var/games/rogue.scores +Score file +.It Pa ~/rogue.save +Default save file +.El +.Sh SEE ALSO +.Rs +.%A Michael C. Toy +.%A Kenneth C. R. C. Arnold +.%T A guide to the Dungeons of Doom +.Re +.Sh AUTHORS +.An Timothy Stoehr +.An Michael C. Toy +.An Kenneth C. R. C. Arnold +.An Glenn Wichman +.Sh BUGS +Probably infinite, although none are known. +However, that Ice Monsters sometimes transfix you permanently is +.Em not +a bug. +It's a feature. diff --git a/games/rogue/rogue.h b/games/rogue/rogue.h new file mode 100644 index 000000000..8ce9411ea --- /dev/null +++ b/games/rogue/rogue.h @@ -0,0 +1,702 @@ +/* $NetBSD: rogue.h,v 1.24 2013/08/11 03:44:27 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)rogue.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * rogue.h + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) This notice shall not be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + */ + +#define boolean char + +#define NOTHING ((unsigned short) 0) +#define OBJECT ((unsigned short) 01) +#define MONSTER ((unsigned short) 02) +#define STAIRS ((unsigned short) 04) +#define HORWALL ((unsigned short) 010) +#define VERTWALL ((unsigned short) 020) +#define DOOR ((unsigned short) 040) +#define FLOOR ((unsigned short) 0100) +#define TUNNEL ((unsigned short) 0200) +#define TRAP ((unsigned short) 0400) +#define HIDDEN ((unsigned short) 01000) + +#define ARMOR ((unsigned short) 01) +#define WEAPON ((unsigned short) 02) +#define SCROL ((unsigned short) 04) +#define POTION ((unsigned short) 010) +#define GOLD ((unsigned short) 020) +#define FOOD ((unsigned short) 040) +#define WAND ((unsigned short) 0100) +#define RING ((unsigned short) 0200) +#define AMULET ((unsigned short) 0400) +#define ALL_OBJECTS ((unsigned short) 0777) + +#define LEATHER 0 +#define RINGMAIL 1 +#define SCALE 2 +#define CHAIN 3 +#define BANDED 4 +#define SPLINT 5 +#define PLATE 6 +#define ARMORS 7 + +#define BOW 0 +#define DART 1 +#define ARROW 2 +#define DAGGER 3 +#define SHURIKEN 4 +#define MACE 5 +#define LONG_SWORD 6 +#define TWO_HANDED_SWORD 7 +#define WEAPONS 8 + +#define MAX_PACK_COUNT 24 + +#define PROTECT_ARMOR 0 +#define HOLD_MONSTER 1 +#define ENCH_WEAPON 2 +#define ENCH_ARMOR 3 +#define IDENTIFY 4 +#define TELEPORT 5 +#define SLEEP 6 +#define SCARE_MONSTER 7 +#define REMOVE_CURSE 8 +#define CREATE_MONSTER 9 +#define AGGRAVATE_MONSTER 10 +#define MAGIC_MAPPING 11 +#define CON_MON 12 +#define SCROLS 13 + +#define INCREASE_STRENGTH 0 +#define RESTORE_STRENGTH 1 +#define HEALING 2 +#define EXTRA_HEALING 3 +#define POISON 4 +#define RAISE_LEVEL 5 +#define BLINDNESS 6 +#define HALLUCINATION 7 +#define DETECT_MONSTER 8 +#define DETECT_OBJECTS 9 +#define CONFUSION 10 +#define LEVITATION 11 +#define HASTE_SELF 12 +#define SEE_INVISIBLE 13 +#define POTIONS 14 + +#define TELE_AWAY 0 +#define SLOW_MONSTER 1 +#define INVISIBILITY 2 +#define POLYMORPH 3 +#define HASTE_MONSTER 4 +#define MAGIC_MISSILE 5 +#define CANCELLATION 6 +#define DO_NOTHING 7 +#define DRAIN_LIFE 8 +#define COLD 9 +#define FIRE 10 +#define WANDS 11 + +#define STEALTH 0 +#define R_TELEPORT 1 +#define REGENERATION 2 +#define SLOW_DIGEST 3 +#define ADD_STRENGTH 4 +#define SUSTAIN_STRENGTH 5 +#define DEXTERITY 6 +#define ADORNMENT 7 +#define R_SEE_INVISIBLE 8 +#define MAINTAIN_ARMOR 9 +#define SEARCHING 10 +#define RINGS 11 + +#define RATION 0 +#define FRUIT 1 + +#define NOT_USED ((unsigned short) 0) +#define BEING_WIELDED ((unsigned short) 01) +#define BEING_WORN ((unsigned short) 02) +#define ON_LEFT_HAND ((unsigned short) 04) +#define ON_RIGHT_HAND ((unsigned short) 010) +#define ON_EITHER_HAND ((unsigned short) 014) +#define BEING_USED ((unsigned short) 017) + +#define NO_TRAP -1 +#define TRAP_DOOR 0 +#define BEAR_TRAP 1 +#define TELE_TRAP 2 +#define DART_TRAP 3 +#define SLEEPING_GAS_TRAP 4 +#define RUST_TRAP 5 +#define TRAPS 6 + +#define STEALTH_FACTOR 3 +#define R_TELE_PERCENT 8 + +#define UNIDENTIFIED ((unsigned short) 00) /* MUST BE ZERO! */ +#define IDENTIFIED ((unsigned short) 01) +#define CALLED ((unsigned short) 02) + +#define DROWS 24 +#define DCOLS 80 +#define NMESSAGES 5 +#define MAX_TITLE_LENGTH 30 +#define MAXSYLLABLES 40 +#define MAX_METAL 14 +#define WAND_MATERIALS 30 +#define GEMS 14 + +#define GOLD_PERCENT 46 + +#define MAX_OPT_LEN 40 + +#define MAX_ID_TITLE_LEN 64 +struct id { + short value; + char title[MAX_ID_TITLE_LEN]; + const char *real; + unsigned short id_status; +}; + +/* The following #defines provide more meaningful names for some of the + * struct object fields that are used for monsters. This, since each monster + * and object (scrolls, potions, etc) are represented by a struct object. + * Ideally, this should be handled by some kind of union structure. + */ + +#define m_damage damage +#define hp_to_kill quantity +#define m_char ichar +#define first_level is_protected +#define last_level is_cursed +#define m_hit_chance class +#define stationary_damage identified +#define drop_percent which_kind +#define trail_char d_enchant +#define slowed_toggle quiver +#define moves_confused hit_enchant +#define nap_length picked_up +#define disguise what_is +#define next_monster next_object + +struct obj { /* comment is monster meaning */ + unsigned long m_flags; /* monster flags */ + const char *damage; /* damage it does */ + short quantity; /* hit points to kill */ + short ichar; /* 'A' is for aquator */ + short kill_exp; /* exp for killing it */ + short is_protected; /* level starts */ + short is_cursed; /* level ends */ + short class; /* chance of hitting you */ + short identified; /* 'F' damage, 1,2,3... */ + unsigned short which_kind; /* item carry/drop % */ + short o_row, o_col, o; /* o is how many times stuck at o_row, o_col */ + short row, col; /* current row, col */ + short d_enchant; /* room char when detect_monster */ + short quiver; /* monster slowed toggle */ + short trow, tcol; /* target row, col */ + short hit_enchant; /* how many moves is confused */ + unsigned short what_is; /* imitator's charactor (?!%: */ + short picked_up; /* sleep from wand of sleep */ + unsigned short in_use_flags; + struct obj *next_object; /* next monster */ +}; + +typedef struct obj object; + +#define INIT_AW NULL +#define INIT_RINGS NULL +#define INIT_HP 12 +#define INIT_STR 16 +#define INIT_EXPLEVEL 1 +#define INIT_EXP 0 +#define INIT_PACK {0,NULL,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,NULL} +#define INIT_GOLD 0 +#define INIT_CHAR '@' +#define INIT_MOVES 1250 + +struct fightr { + object *armor; + object *weapon; + object *left_ring, *right_ring; + short hp_current; + short hp_max; + short str_current; + short str_max; + object pack; + long gold; + short exp; + long exp_points; + short row, col; + short fchar; + short moves_left; +}; + +typedef struct fightr fighter; + +struct dr { + short oth_room; + short oth_row, + oth_col; + short door_row, + door_col; +}; + +typedef struct dr door; + +struct rm { + short bottom_row, right_col, left_col, top_row; + door doors[4]; + unsigned short is_room; +}; + +typedef struct rm room; + +#define MAXROOMS 9 +#define BIG_ROOM 10 + +#define NO_ROOM (-1) + +#define PASSAGE (-3) /* cur_room value */ + +#define AMULET_LEVEL 26 + +#define R_NOTHING ((unsigned short) 01) +#define R_ROOM ((unsigned short) 02) +#define R_MAZE ((unsigned short) 04) +#define R_DEADEND ((unsigned short) 010) +#define R_CROSS ((unsigned short) 020) + +#define MAX_EXP_LEVEL 21 +#define MAX_EXP 10000001L +#define MAX_GOLD 999999 +#define MAX_ARMOR 99 +#define MAX_HP 999 +#define MAX_STRENGTH 99 +#define LAST_DUNGEON 99 + +#define STAT_LEVEL 01 +#define STAT_GOLD 02 +#define STAT_HP 04 +#define STAT_STRENGTH 010 +#define STAT_ARMOR 020 +#define STAT_EXP 040 +#define STAT_HUNGER 0100 +#define STAT_LABEL 0200 +#define STAT_ALL 0377 + +#define PARTY_TIME 10 /* one party somewhere in each 10 level span */ + +#define MAX_TRAPS 10 /* maximum traps per level */ + +#define HIDE_PERCENT 12 + +struct tr { + short trap_type; + short trap_row, trap_col; +}; + +typedef struct tr trap; + +extern fighter rogue; +extern room rooms[]; +extern trap traps[]; +extern unsigned short dungeon[DROWS][DCOLS]; +extern object level_objects; + +extern struct id id_scrolls[]; +extern struct id id_potions[]; +extern struct id id_wands[]; +extern struct id id_rings[]; +extern struct id id_weapons[]; +extern struct id id_armors[]; + +extern object level_monsters; + +#define MONSTERS 26 + +#define HASTED 01L +#define SLOWED 02L +#define INVISIBLE 04L +#define ASLEEP 010L +#define WAKENS 020L +#define WANDERS 040L +#define FLIES 0100L +#define FLITS 0200L +#define CAN_FLIT 0400L /* can, but usually doesn't, flit */ +#define CONFUSED 01000L +#define RUSTS 02000L +#define HOLDS 04000L +#define FREEZES 010000L +#define STEALS_GOLD 020000L +#define STEALS_ITEM 040000L +#define STINGS 0100000L +#define DRAINS_LIFE 0200000L +#define DROPS_LEVEL 0400000L +#define SEEKS_GOLD 01000000L +#define FREEZING_ROGUE 02000000L +#define RUST_VANISHED 04000000L +#define CONFUSES 010000000L +#define IMITATES 020000000L +#define FLAMES 040000000L +#define STATIONARY 0100000000L /* damage will be 1,2,3,... */ +#define NAPPING 0200000000L /* can't wake up for a while */ +#define ALREADY_MOVED 0400000000L + +#define SPECIAL_HIT (RUSTS|HOLDS|FREEZES|STEALS_GOLD|STEALS_ITEM|STINGS|DRAINS_LIFE|DROPS_LEVEL) + +#define WAKE_PERCENT 45 +#define FLIT_PERCENT 40 +#define PARTY_WAKE_PERCENT 75 + +#define HYPOTHERMIA 1 +#define STARVATION 2 +#define POISON_DART 3 +#define QUIT 4 +#define WIN 5 +#define KFIRE 6 + +#define UPWARD 0 +#define UPRIGHT 1 +#define RIGHT 2 +#define DOWNRIGHT 3 +#define DOWN 4 +#define DOWNLEFT 5 +#define LEFT 6 +#define UPLEFT 7 +#define DIRS 8 + +#define ROW1 7 +#define ROW2 15 + +#define COL1 26 +#define COL2 52 + +#define MOVED 0 +#define MOVE_FAILED -1 +#define STOPPED_ON_SOMETHING -2 +#define CANCEL '\033' +#define LIST '*' + +#define HUNGRY 300 +#define WEAK 150 +#define FAINT 20 +#define STARVE 0 + +#define MIN_ROW 1 + +struct rogue_time { + short year; /* >= 1987 */ + short month; /* 1 - 12 */ + short day; /* 1 - 31 */ + short hour; /* 0 - 23 */ + short minute; /* 0 - 59 */ + short second; /* 0 - 59 */ +}; + +#include + +/* + * external routine declarations. + */ +#include +#include +#include +#include + +object *alloc_object(void); +object *get_letter_object(int); +object *gr_monster(object *, int); +object *gr_object(void); +char *md_getenv(const char *); +const char * + md_gln(void); +void *md_malloc(size_t); +const char *mon_name(const object *); +const char *name_of(const object *); +object *object_at(object *, short, short); +object *pick_up(int, int, short *); +void add_exp(int, boolean); +void add_traps(void); +void aggravate(void); +void bounce(short, short, short, short, short); +void byebye(int); +void c_object_for_wizard(void); +void call_it(void); +boolean can_move(int, int, int, int); +void check_gold_seeker(object *); +boolean check_imitator(object *); +void check_message(void); +int check_up(void); +void clean_up(const char *) __dead; +void clear_level(void); +void cnfs(void); +int coin_toss(void); +void cough_up(object *); +void create_monster(void); +void darken_room(short); +void do_put_on(object *, boolean); +void do_shell(void); +void do_wear(object *); +void do_wield(object *); +void dr_course(object *, boolean, short, short); +void draw_magic_map(void); +void drop(void); +int drop_check(void); +void eat(void); +void edit_opts(void); +void error_save(int) __dead; +void fight(boolean); +boolean flame_broil(object *); +void free_object(object *); +void free_stuff(object *); +int get_armor_class(const object *); +int get_damage(const char *, boolean); +void get_desc(const object *, char *, size_t); +void get_dir_rc(short, short *, short *, short); +char get_dungeon_char(short, short); +void get_food(object *, boolean); +int get_hit_chance(const object *); +int get_input_line(const char *, const char *, char *, size_t, const char *, boolean, boolean); +char get_mask_char(unsigned short); +int get_number(const char *); +int get_rand(int, int); +short get_room_number(int, int); +void get_wand_and_ring_materials(void); +int get_weapon_damage(const object *); +char gmc(object *); +char gmc_row_col(int, int); +char gr_obj_char(void); +void gr_ring(object *, boolean); +short gr_room(void); +void gr_row_col(short *, short *, unsigned short); +void hallucinate(void); +boolean has_amulet(void); +int hp_raise(void); +void id_com(void); +void id_trap(void); +void id_type(void); +boolean imitating(short, short); +int init(int, char **); +void insert_score(char [][82], char [][30], const char *, short, short, const object *, int); +void inv_armor_weapon(boolean); +void inv_rings(void); +void inventory(const object *, unsigned short); +boolean is_all_connected(void); +boolean is_digit(int); +boolean is_direction(short, short *); +boolean is_passable(int, int); +boolean is_vowel(short); +void kick_into_pack(void); +void killed_by(const object *, short) __dead; +long lget_number(const char *); +void light_passage(int, int); +void light_up_room(int); +boolean m_confuse(object *); +void make_level(void); +void make_scroll_titles(void); +boolean md_df(const char *); +void md_exit(int) __dead; +void md_gct(struct rogue_time *); +int md_get_file_id(const char *); +void md_gfmt(const char *, struct rogue_time *); +int md_gseed(void); +void md_heed_signals(void); +void md_ignore_signals(void); +int md_link_count(const char *); +void md_lock(boolean); +void md_shell(const char *); +void md_sleep(int); +void md_slurp(void); +/*void message(const char *, boolean);*/ +void messagef(boolean, const char *, ...) __printflike(2, 3); +void mix_colors(void); +int mon_can_go(const object *, short, short); +int mon_damage(object *, short); +void mon_hit(object *); +boolean mon_sees(const object *, int, int); +void move_mon_to(object *, short, short); +void move_onto(void); +void multiple_move_rogue(short); +void mv_1_monster(object *, short, short); +void mv_aquatars(void); +void mv_mons(void); +int name_cmp(char *, const char *); +void nickize(char *, const char *, const char *); +int one_move_rogue(short, short); +void onintr(int); +short pack_count(const object *); +short pack_letter(const char *, unsigned short); +void pad(const char *, short); +void party_monsters(int, int); +short party_objects(int); +void place_at(object *, int, int); +void play_level(void); +void print_stats(int); +void put_amulet(void); +void put_mons(void); +void put_objects(void); +void put_on_ring(void); +void put_player(short); +void put_scores(const object *, short) __dead; +void put_stairs(void); +void quaff(void); +void quit(boolean); +int r_index(const char *, int, boolean); +void rand_around(short, short *, short *); +int rand_percent(int); +void read_scroll(void); +boolean reg_move(void); +void relight(void); +void remessage(short); +void remove_ring(void); +void rest(int); +void restore(const char *); +int rgetchar(void); +void ring_stats(boolean); +int rogue_can_see(int, int); +void rogue_damage(short, object *, short); +void rogue_hit(object *, boolean); +void rust(object *); +void s_con_mon(object *); +void save_game(void); +void save_into_file(const char *); +void search(short, boolean); +boolean seek_gold(object *); +void show_average_hp(void); +void show_monsters(void); +void show_objects(void); +void show_traps(void); +void single_inv(short); +void sound_bell(void); +void special_hit(object *); +void srrandom(int); +void start_window(void); +void stop_window(void); +void take_a_nap(void); +void take_from_pack(object *, object *); +void take_off(void); +void tele(void); +void throw(void); +void trap_player(short, short); +void un_put_on(object *); +void unblind(void); +void unconfuse(void); +void unhallucinate(void); +void unwear(object *); +void unwield(object *); +void vanish(object *, short, object *); +void wait_for_ack(void); +void wake_room(short, boolean, short, short); +void wake_up(object *); +void wanderer(void); +void wear(void); +void wield(void); +void win(void) __dead; +void wizardize(void); +long xxx(boolean); +void xxxx(char *, short); +void zapp(void); +object *add_to_pack(object *, object *, int); +struct id *get_id_table(const object *); + +extern boolean ask_quit; +extern boolean being_held; +extern boolean cant_int; +extern boolean con_mon; +extern boolean detect_monster; +extern boolean did_int; +extern boolean interrupted; +extern boolean is_wood[]; +extern boolean jump; +extern boolean maintain_armor; +extern boolean mon_disappeared; +extern boolean msg_cleared; +extern boolean no_skull; +extern boolean passgo; +extern boolean r_see_invisible; +extern boolean r_teleport; +extern boolean save_is_interactive; +extern boolean score_only; +extern boolean see_invisible; +extern boolean sustain_strength; +extern boolean trap_door; +extern boolean wizard; +#define HIT_MESSAGE_SIZE 80 +extern char hit_message[HIT_MESSAGE_SIZE]; +#define HUNGER_STR_LEN 8 +extern char hunger_str[HUNGER_STR_LEN]; +extern char login_name[MAX_OPT_LEN]; +extern const char *byebye_string; +extern const char curse_message[]; +extern const char *error_file; +extern char *fruit; +extern const char *const m_names[]; +extern const char *more; +extern const char *new_level_message; +extern char *nick_name; +extern const char *press_space; +extern char *save_file; +extern const char you_can_move_again[]; +extern const long level_points[]; +extern short add_strength; +extern short auto_search; +extern short bear_trap; +extern short blind; +extern short confused; +extern short cur_level; +extern short cur_room; +extern short e_rings; +extern short extra_hp; +extern short foods; +extern short halluc; +extern short haste_self; +extern short less_hp; +extern short levitate; +extern short m_moves; +extern short max_level; +extern short party_room; +extern short r_rings; +extern short regeneration; +extern short ring_exp; +extern short stealthy; +extern gid_t gid; +extern gid_t egid; diff --git a/games/rogue/room.c b/games/rogue/room.c new file mode 100644 index 000000000..0071489f1 --- /dev/null +++ b/games/rogue/room.c @@ -0,0 +1,671 @@ +/* $NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)room.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: room.c,v 1.13 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * room.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +room rooms[MAXROOMS]; + +static boolean rooms_visited[MAXROOMS]; + +#define NOPTS 7 +static const struct option { + const char *prompt; + boolean is_bool; + char **strval; + boolean *bval; +} options[NOPTS] = { + { + "Show position only at end of run (\"jump\"): ", + 1, NULL, &jump + }, + { + "Follow turnings in passageways (\"passgo\"): ", + 1, NULL, &passgo + }, + { + "Don't print skull when killed (\"noskull\" or \"notombstone\"): ", + 1, NULL, &no_skull + }, + { + "Ask player before saying 'Okay, bye-bye!' (\"askquit\"): ", + 1, NULL, &ask_quit + }, + { + "Name (\"name\"): ", + 0, &nick_name, NULL + }, + { + "Fruit (\"fruit\"): ", + 0, &fruit, NULL + }, + { + "Save file (\"file\"): ", + 0, &save_file, NULL + } +}; + +static boolean get_oth_room(short, short *, short *); +static void opt_erase(int); +static void opt_go(int); +static void opt_show(int); +static void visit_rooms(int); + +void +light_up_room(int rn) +{ + short i, j; + + if (!blind) { + for (i = rooms[rn].top_row; + i <= rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col; + j <= rooms[rn].right_col; j++) { + if (dungeon[i][j] & MONSTER) { + object *monster; + + if ((monster = object_at( + &level_monsters, i, j)) != NULL) { + dungeon[monster->row][monster->col] &= (~MONSTER); + monster->trail_char = + get_dungeon_char(monster->row, monster->col); + dungeon[monster->row][monster->col] |= MONSTER; + } + } + mvaddch(i, j, get_dungeon_char(i, j)); + } + } + mvaddch(rogue.row, rogue.col, rogue.fchar); + } +} + +void +light_passage(int row, int col) +{ + short i, j, i_end, j_end; + + if (blind) { + return; + } + i_end = (row < (DROWS-2)) ? 1 : 0; + j_end = (col < (DCOLS-1)) ? 1 : 0; + + for (i = ((row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { + for (j = ((col > 0) ? -1 : 0); j <= j_end; j++) { + if (can_move(row, col, row+i, col+j)) { + mvaddch(row+i, col+j, get_dungeon_char(row+i, col+j)); + } + } + } +} + +void +darken_room(short rn) +{ + short i, j; + + for (i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) { + if (blind) { + mvaddch(i, j, ' '); + } else { + if (!(dungeon[i][j] & (OBJECT | STAIRS)) && + !(detect_monster && (dungeon[i][j] & MONSTER))) { + if (!imitating(i, j)) { + mvaddch(i, j, ' '); + } + if ((dungeon[i][j] & TRAP) && (!(dungeon[i][j] & HIDDEN))) { + mvaddch(i, j, '^'); + } + } + } + } + } +} + +char +get_dungeon_char(short row, short col) +{ + unsigned short mask = dungeon[row][col]; + + if (mask & MONSTER) { + return(gmc_row_col(row, col)); + } + if (mask & OBJECT) { + object *obj; + + obj = object_at(&level_objects, row, col); + return(get_mask_char(obj->what_is)); + } + if (mask & (TUNNEL | STAIRS | HORWALL | VERTWALL | FLOOR | DOOR)) { + if ((mask & (TUNNEL| STAIRS)) && (!(mask & HIDDEN))) { + return(((mask & STAIRS) ? '%' : '#')); + } + if (mask & HORWALL) { + return('-'); + } + if (mask & VERTWALL) { + return('|'); + } + if (mask & FLOOR) { + if (mask & TRAP) { + if (!(dungeon[row][col] & HIDDEN)) { + return('^'); + } + } + return('.'); + } + if (mask & DOOR) { + if (mask & HIDDEN) { + if (((col > 0) && (dungeon[row][col-1] & HORWALL)) || + ((col < (DCOLS-1)) && (dungeon[row][col+1] & HORWALL))) { + return('-'); + } else { + return('|'); + } + } else { + return('+'); + } + } + } + return(' '); +} + +char +get_mask_char(unsigned short mask) +{ + switch(mask) { + case SCROL: + return('?'); + case POTION: + return('!'); + case GOLD: + return('*'); + case FOOD: + return(':'); + case WAND: + return('/'); + case ARMOR: + return(']'); + case WEAPON: + return(')'); + case RING: + return('='); + case AMULET: + return(','); + default: + return('~'); /* unknown, something is wrong */ + } +} + +void +gr_row_col(short *row, short *col, unsigned short mask) +{ + short rn; + short r, c; + + do { + r = get_rand(MIN_ROW, DROWS-2); + c = get_rand(0, DCOLS-1); + rn = get_room_number(r, c); + } while ((rn == NO_ROOM) || + (!(dungeon[r][c] & mask)) || + (dungeon[r][c] & (~mask)) || + (!(rooms[rn].is_room & (R_ROOM | R_MAZE))) || + ((r == rogue.row) && (c == rogue.col))); + + *row = r; + *col = c; +} + +short +gr_room(void) +{ + short i; + + do { + i = get_rand(0, MAXROOMS-1); + } while (!(rooms[i].is_room & (R_ROOM | R_MAZE))); + + return(i); +} + +short +party_objects(int rn) +{ + short i, j, nf = 0; + object *obj; + short n, N, row, col; + boolean found; + + row = col = 0; + N = ((rooms[rn].bottom_row - rooms[rn].top_row) - 1) * + ((rooms[rn].right_col - rooms[rn].left_col) - 1); + n = get_rand(5, 10); + if (n > N) { + n = N - 2; + } + for (i = 0; i < n; i++) { + for (j = found = 0; ((!found) && (j < 250)); j++) { + row = get_rand(rooms[rn].top_row+1, + rooms[rn].bottom_row-1); + col = get_rand(rooms[rn].left_col+1, + rooms[rn].right_col-1); + if ((dungeon[row][col] == FLOOR) || (dungeon[row][col] == TUNNEL)) { + found = 1; + } + } + if (found) { + obj = gr_object(); + place_at(obj, row, col); + nf++; + } + } + return(nf); +} + +short +get_room_number(int row, int col) +{ + short i; + + for (i = 0; i < MAXROOMS; i++) { + if ((row >= rooms[i].top_row) && (row <= rooms[i].bottom_row) && + (col >= rooms[i].left_col) && (col <= rooms[i].right_col)) { + return(i); + } + } + return(NO_ROOM); +} + +boolean +is_all_connected(void) +{ + short i, starting_room; + + starting_room = 0; + for (i = 0; i < MAXROOMS; i++) { + rooms_visited[i] = 0; + if (rooms[i].is_room & (R_ROOM | R_MAZE)) { + starting_room = i; + } + } + + visit_rooms(starting_room); + + for (i = 0; i < MAXROOMS; i++) { + if ((rooms[i].is_room & (R_ROOM | R_MAZE)) && (!rooms_visited[i])) { + return(0); + } + } + return(1); +} + +static void +visit_rooms(int rn) +{ + short i; + short oth_rn; + + rooms_visited[rn] = 1; + + for (i = 0; i < 4; i++) { + oth_rn = rooms[rn].doors[i].oth_room; + if ((oth_rn >= 0) && (!rooms_visited[oth_rn])) { + visit_rooms(oth_rn); + } + } +} + +void +draw_magic_map(void) +{ + short i, j, ch, och; + unsigned short mask = (HORWALL | VERTWALL | DOOR | TUNNEL | TRAP | STAIRS | + MONSTER); + unsigned short s; + + for (i = 0; i < DROWS; i++) { + for (j = 0; j < DCOLS; j++) { + s = dungeon[i][j]; + if (s & mask) { + if (((ch = mvinch(i, j)) == ' ') || + ((ch >= 'A') && (ch <= 'Z')) || (s & (TRAP | HIDDEN))) { + och = ch; + dungeon[i][j] &= (~HIDDEN); + if (s & HORWALL) { + ch = '-'; + } else if (s & VERTWALL) { + ch = '|'; + } else if (s & DOOR) { + ch = '+'; + } else if (s & TRAP) { + ch = '^'; + } else if (s & STAIRS) { + ch = '%'; + } else if (s & TUNNEL) { + ch = '#'; + } else { + continue; + } + if ((!(s & MONSTER)) || (och == ' ')) { + addch(ch); + } + if (s & MONSTER) { + object *monster; + + if ((monster = object_at( + &level_monsters, i, j)) + != NULL) { + monster->trail_char = + ch; + } + } + } + } + } + } +} + +void +dr_course(object *monster, boolean entering, short row, short col) +{ + short i, j, k, rn; + short r, rr; + + monster->row = row; + monster->col = col; + + if (mon_sees(monster, rogue.row, rogue.col)) { + monster->trow = NO_ROOM; + return; + } + rn = get_room_number(row, col); + + if (entering) { /* entering room */ + /* look for door to some other room */ + r = get_rand(0, MAXROOMS-1); + for (i = 0; i < MAXROOMS; i++) { + rr = (r + i) % MAXROOMS; + if ((!(rooms[rr].is_room & (R_ROOM | R_MAZE))) || (rr == rn)) { + continue; + } + for (k = 0; k < 4; k++) { + if (rooms[rr].doors[k].oth_room == rn) { + monster->trow = rooms[rr].doors[k].oth_row; + monster->tcol = rooms[rr].doors[k].oth_col; + if ((monster->trow == row) && + (monster->tcol == col)) { + continue; + } + return; + } + } + } + /* look for door to dead end */ + if (rn == NO_ROOM) + clean_up("dr_course: monster not in room"); + for (i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { + if ((i != monster->row) && (j != monster->col) && + (dungeon[i][j] & DOOR)) { + monster->trow = i; + monster->tcol = j; + return; + } + } + } + /* return monster to room that he came from */ + for (i = 0; i < MAXROOMS; i++) { + for (j = 0; j < 4; j++) { + if (rooms[i].doors[j].oth_room == rn) { + for (k = 0; k < 4; k++) { + if (rooms[rn].doors[k].oth_room == i) { + monster->trow = rooms[rn].doors[k].oth_row; + monster->tcol = rooms[rn].doors[k].oth_col; + return; + } + } + } + } + } + /* no place to send monster */ + monster->trow = NO_ROOM; + } else { /* exiting room */ + if (rn == NO_ROOM || !get_oth_room(rn, &row, &col)) { + monster->trow = NO_ROOM; + } else { + monster->trow = row; + monster->tcol = col; + } + } +} + +static boolean +get_oth_room(short rn, short *row, short *col) +{ + short d = -1; + + if (*row == rooms[rn].top_row) { + d = UPWARD/2; + } else if (*row == rooms[rn].bottom_row) { + d = DOWN/2; + } else if (*col == rooms[rn].left_col) { + d = LEFT/2; + } else if (*col == rooms[rn].right_col) { + d = RIGHT/2; + } + if ((d != -1) && (rooms[rn].doors[d].oth_room >= 0)) { + *row = rooms[rn].doors[d].oth_row; + *col = rooms[rn].doors[d].oth_col; + return(1); + } + return(0); +} + +void +edit_opts(void) +{ + char save[NOPTS+1][DCOLS]; + short i, j; + short ch; + boolean done = 0; + char buf[MAX_OPT_LEN + 2]; + + for (i = 0; i < NOPTS+1; i++) { + for (j = 0; j < DCOLS; j++) { + save[i][j] = mvinch(i, j); + } + if (i < NOPTS) { + opt_show(i); + } + } + opt_go(0); + i = 0; + + while (!done) { + refresh(); + ch = rgetchar(); +CH: + switch(ch) { + case '\033': + done = 1; + break; + case '\012': + case '\015': + if (i == (NOPTS - 1)) { + mvaddstr(NOPTS, 0, press_space); + refresh(); + wait_for_ack(); + done = 1; + } else { + i++; + opt_go(i); + } + break; + case '-': + if (i > 0) { + opt_go(--i); + } else { + sound_bell(); + } + break; + case 't': + case 'T': + case 'f': + case 'F': + if (options[i].is_bool) { + *(options[i].bval) = (((ch == 't') || (ch == 'T')) ? 1 : 0); + opt_show(i); + opt_go(++i); + break; + } + default: + if (options[i].is_bool) { + sound_bell(); + break; + } + j = 0; + if ((ch == '\010') || ((ch >= ' ') && (ch <= '~'))) { + opt_erase(i); + do { + if ((ch >= ' ') && (ch <= '~') && (j < MAX_OPT_LEN)) { + buf[j++] = ch; + buf[j] = '\0'; + addch(ch); + } else if ((ch == '\010') && (j > 0)) { + buf[--j] = '\0'; + move(i, j + strlen(options[i].prompt)); + addch(' '); + move(i, j + strlen(options[i].prompt)); + } + refresh(); + ch = rgetchar(); + } while ((ch != '\012') && (ch != '\015') && (ch != '\033')); + if (j != 0) { + /* + * We rely on the option string being + * allocated to hold MAX_OPT_LEN+2 + * bytes. This is arranged in init.c. + */ + (void)strcpy(*(options[i].strval), buf); + } + opt_show(i); + goto CH; + } else { + sound_bell(); + } + break; + } + } + + for (i = 0; i < NOPTS+1; i++) { + move(i, 0); + for (j = 0; j < DCOLS; j++) { + addch(save[i][j]); + } + } +} + +static void +opt_show(int i) +{ + const char *s; + const struct option *opt = &options[i]; + + opt_erase(i); + + if (opt->is_bool) { + s = *(opt->bval) ? "True" : "False"; + } else { + s = *(opt->strval); + } + addstr(s); +} + +static void +opt_erase(int i) +{ + const struct option *opt = &options[i]; + + mvaddstr(i, 0, opt->prompt); + clrtoeol(); +} + +static void +opt_go(int i) +{ + move(i, strlen(options[i].prompt)); +} + +void +do_shell(void) +{ +#ifdef UNIX + const char *sh; + + md_ignore_signals(); + if (!(sh = md_getenv("SHELL"))) { + sh = "/bin/sh"; + } + move(LINES-1, 0); + refresh(); + stop_window(); + printf("\nCreating new shell...\n"); + md_shell(sh); + start_window(); + wrefresh(curscr); + md_heed_signals(); +#endif +} diff --git a/games/rogue/save.c b/games/rogue/save.c new file mode 100644 index 000000000..4a7190afb --- /dev/null +++ b/games/rogue/save.c @@ -0,0 +1,427 @@ +/* $NetBSD: save.c,v 1.13 2008/01/14 03:50:02 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)save.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: save.c,v 1.13 2008/01/14 03:50:02 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * save.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include +#include "rogue.h" + +static boolean has_been_touched(const struct rogue_time *, + const struct rogue_time *); +static void r_read(FILE *, void *, size_t); +static void r_write(FILE *, const void *, size_t); +static void read_pack(object *, FILE *, boolean); +static void read_string(char *, FILE *, size_t); +static void rw_dungeon(FILE *, boolean); +static void rw_id(struct id *, FILE *, int, boolean); +static void rw_rooms(FILE *, boolean); +static void write_pack(const object *, FILE *); +static void write_string(char *, FILE *); + +static short write_failed = 0; + +char *save_file = NULL; + +void +save_game(void) +{ + char fname[64]; + + if (!get_input_line("file name?", save_file, fname, sizeof(fname), + "game not saved", 0, 1)) { + return; + } + check_message(); + messagef(0, "%s", fname); + save_into_file(fname); +} + +void +save_into_file(const char *sfile) +{ + FILE *fp; + int file_id; + char *name_buffer; + size_t len; + char *hptr; + struct rogue_time rt_buf; + + if (sfile[0] == '~') { + if ((hptr = md_getenv("HOME")) != NULL) { + len = strlen(hptr) + strlen(sfile); + name_buffer = md_malloc(len); + if (name_buffer == NULL) { + messagef(0, + "out of memory for save file name"); + sfile = error_file; + } else { + (void)strcpy(name_buffer, hptr); + (void)strcat(name_buffer, sfile+1); + sfile = name_buffer; + } + /* + * Note: name_buffer gets leaked. But it's small, + * and in the common case we're about to exit. + */ + } + } + if (((fp = fopen(sfile, "w")) == NULL) || + ((file_id = md_get_file_id(sfile)) == -1)) { + if (fp) + fclose(fp); + messagef(0, "problem accessing the save file"); + return; + } + md_ignore_signals(); + write_failed = 0; + (void)xxx(1); + r_write(fp, &detect_monster, sizeof(detect_monster)); + r_write(fp, &cur_level, sizeof(cur_level)); + r_write(fp, &max_level, sizeof(max_level)); + write_string(hunger_str, fp); + write_string(login_name, fp); + r_write(fp, &party_room, sizeof(party_room)); + write_pack(&level_monsters, fp); + write_pack(&level_objects, fp); + r_write(fp, &file_id, sizeof(file_id)); + rw_dungeon(fp, 1); + r_write(fp, &foods, sizeof(foods)); + r_write(fp, &rogue, sizeof(fighter)); + write_pack(&rogue.pack, fp); + rw_id(id_potions, fp, POTIONS, 1); + rw_id(id_scrolls, fp, SCROLS, 1); + rw_id(id_wands, fp, WANDS, 1); + rw_id(id_rings, fp, RINGS, 1); + r_write(fp, traps, (MAX_TRAPS * sizeof(trap))); + r_write(fp, is_wood, (WANDS * sizeof(boolean))); + r_write(fp, &cur_room, sizeof(cur_room)); + rw_rooms(fp, 1); + r_write(fp, &being_held, sizeof(being_held)); + r_write(fp, &bear_trap, sizeof(bear_trap)); + r_write(fp, &halluc, sizeof(halluc)); + r_write(fp, &blind, sizeof(blind)); + r_write(fp, &confused, sizeof(confused)); + r_write(fp, &levitate, sizeof(levitate)); + r_write(fp, &haste_self, sizeof(haste_self)); + r_write(fp, &see_invisible, sizeof(see_invisible)); + r_write(fp, &detect_monster, sizeof(detect_monster)); + r_write(fp, &wizard, sizeof(wizard)); + r_write(fp, &score_only, sizeof(score_only)); + r_write(fp, &m_moves, sizeof(m_moves)); + md_gct(&rt_buf); + rt_buf.second += 10; /* allow for some processing time */ + r_write(fp, &rt_buf, sizeof(rt_buf)); + fclose(fp); + + if (write_failed) { + (void)md_df(sfile); /* delete file */ + } else { + clean_up(""); + } +} + +void +restore(const char *fname) +{ + FILE *fp; + struct rogue_time saved_time, mod_time; + char buf[4]; + char tbuf[MAX_OPT_LEN]; + int new_file_id, saved_file_id; + + fp = NULL; + if (((new_file_id = md_get_file_id(fname)) == -1) || + ((fp = fopen(fname, "r")) == NULL)) { + clean_up("cannot open file"); + } + if (md_link_count(fname) > 1) { + clean_up("file has link"); + } + (void)xxx(1); + r_read(fp, &detect_monster, sizeof(detect_monster)); + r_read(fp, &cur_level, sizeof(cur_level)); + r_read(fp, &max_level, sizeof(max_level)); + read_string(hunger_str, fp, sizeof hunger_str); + + (void)strlcpy(tbuf, login_name, sizeof tbuf); + read_string(login_name, fp, sizeof login_name); + if (strcmp(tbuf, login_name)) { + clean_up("you're not the original player"); + } + + r_read(fp, &party_room, sizeof(party_room)); + read_pack(&level_monsters, fp, 0); + read_pack(&level_objects, fp, 0); + r_read(fp, &saved_file_id, sizeof(saved_file_id)); + if (new_file_id != saved_file_id) { + clean_up("sorry, saved game is not in the same file"); + } + rw_dungeon(fp, 0); + r_read(fp, &foods, sizeof(foods)); + r_read(fp, &rogue, sizeof(fighter)); + read_pack(&rogue.pack, fp, 1); + rw_id(id_potions, fp, POTIONS, 0); + rw_id(id_scrolls, fp, SCROLS, 0); + rw_id(id_wands, fp, WANDS, 0); + rw_id(id_rings, fp, RINGS, 0); + r_read(fp, traps, (MAX_TRAPS * sizeof(trap))); + r_read(fp, is_wood, (WANDS * sizeof(boolean))); + r_read(fp, &cur_room, sizeof(cur_room)); + rw_rooms(fp, 0); + r_read(fp, &being_held, sizeof(being_held)); + r_read(fp, &bear_trap, sizeof(bear_trap)); + r_read(fp, &halluc, sizeof(halluc)); + r_read(fp, &blind, sizeof(blind)); + r_read(fp, &confused, sizeof(confused)); + r_read(fp, &levitate, sizeof(levitate)); + r_read(fp, &haste_self, sizeof(haste_self)); + r_read(fp, &see_invisible, sizeof(see_invisible)); + r_read(fp, &detect_monster, sizeof(detect_monster)); + r_read(fp, &wizard, sizeof(wizard)); + r_read(fp, &score_only, sizeof(score_only)); + r_read(fp, &m_moves, sizeof(m_moves)); + r_read(fp, &saved_time, sizeof(saved_time)); + + if (fread(buf, 1, 1, fp) > 0) { + clear(); + clean_up("extra characters in file"); + } + + md_gfmt(fname, &mod_time); /* get file modification time */ + + if (has_been_touched(&saved_time, &mod_time)) { + clear(); + clean_up("sorry, file has been touched"); + } + if ((!wizard) && !md_df(fname)) { + clean_up("cannot delete file"); + } + msg_cleared = 0; + ring_stats(0); + fclose(fp); +} + +static void +write_pack(const object *pack, FILE *fp) +{ + object t; + + while ((pack = pack->next_object) != NULL) { + r_write(fp, pack, sizeof(object)); + } + t.ichar = t.what_is = 0; + r_write(fp, &t, sizeof(object)); +} + +static void +read_pack(object *pack, FILE *fp, boolean is_rogue) +{ + object read_obj, *new_obj; + + for (;;) { + r_read(fp, &read_obj, sizeof(object)); + if (read_obj.ichar == 0) { + pack->next_object = NULL; + break; + } + new_obj = alloc_object(); + *new_obj = read_obj; + if (is_rogue) { + if (new_obj->in_use_flags & BEING_WORN) { + do_wear(new_obj); + } else if (new_obj->in_use_flags & BEING_WIELDED) { + do_wield(new_obj); + } else if (new_obj->in_use_flags & (ON_EITHER_HAND)) { + do_put_on(new_obj, + ((new_obj->in_use_flags & ON_LEFT_HAND) ? 1 : 0)); + } + } + pack->next_object = new_obj; + pack = new_obj; + } +} + +static void +rw_dungeon(FILE *fp, boolean rw) +{ + short i, j; + char buf[DCOLS]; + + for (i = 0; i < DROWS; i++) { + if (rw) { + r_write(fp, dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); + for (j = 0; j < DCOLS; j++) { + buf[j] = mvinch(i, j); + } + r_write(fp, buf, DCOLS); + } else { + r_read(fp, dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); + r_read(fp, buf, DCOLS); + for (j = 0; j < DCOLS; j++) { + mvaddch(i, j, buf[j]); + } + } + } +} + +static void +rw_id(struct id id_table[], FILE *fp, int n, boolean wr) +{ + int i; + + for (i = 0; i < n; i++) { + if (wr) { + r_write(fp, &id_table[i].value, sizeof(short)); + r_write(fp, &id_table[i].id_status, + sizeof(unsigned short)); + write_string(id_table[i].title, fp); + } else { + r_read(fp, &id_table[i].value, sizeof(short)); + r_read(fp, &id_table[i].id_status, + sizeof(unsigned short)); + read_string(id_table[i].title, fp, MAX_ID_TITLE_LEN); + } + } +} + +static void +write_string(char *s, FILE *fp) +{ + short n; + + n = strlen(s) + 1; + xxxx(s, n); + r_write(fp, &n, sizeof(short)); + r_write(fp, s, n); +} + +static void +read_string(char *s, FILE *fp, size_t len) +{ + short n; + + r_read(fp, &n, sizeof(short)); + if (n<=0 || (size_t)(unsigned short)n > len) { + clean_up("read_string: corrupt game file"); + } + r_read(fp, s, n); + xxxx(s, n); + /* ensure null termination */ + s[n-1] = 0; +} + +static void +rw_rooms(FILE *fp, boolean rw) +{ + short i; + + for (i = 0; i < MAXROOMS; i++) { + rw ? r_write(fp, (rooms + i), sizeof(room)) : + r_read(fp, (rooms + i), sizeof(room)); + } +} + +static void +r_read(FILE *fp, void *buf, size_t n) +{ + if (fread(buf, 1, n, fp) != n) { + clean_up("fread() failed, don't know why"); + } +} + +static void +r_write(FILE *fp, const void *buf, size_t n) +{ + if (!write_failed) { + if (fwrite(buf, 1, n, fp) != n) { + messagef(0, "write() failed, don't know why"); + sound_bell(); + write_failed = 1; + } + } +} + +static boolean +has_been_touched(const struct rogue_time *saved_time, + const struct rogue_time *mod_time) +{ + if (saved_time->year < mod_time->year) { + return(1); + } else if (saved_time->year > mod_time->year) { + return(0); + } + if (saved_time->month < mod_time->month) { + return(1); + } else if (saved_time->month > mod_time->month) { + return(0); + } + if (saved_time->day < mod_time->day) { + return(1); + } else if (saved_time->day > mod_time->day) { + return(0); + } + if (saved_time->hour < mod_time->hour) { + return(1); + } else if (saved_time->hour > mod_time->hour) { + return(0); + } + if (saved_time->minute < mod_time->minute) { + return(1); + } else if (saved_time->minute > mod_time->minute) { + return(0); + } + if (saved_time->second < mod_time->second) { + return(1); + } + return(0); +} diff --git a/games/rogue/score.c b/games/rogue/score.c new file mode 100644 index 000000000..4bcf4e369 --- /dev/null +++ b/games/rogue/score.c @@ -0,0 +1,674 @@ +/* $NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)score.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * score.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include +#include "rogue.h" +#include "pathnames.h" + +static void center(short, const char *); +static int get_value(const object *); +static void id_all(void); +static void sell_pack(void); +static void sf_error(void) __dead; + +void +killed_by(const object *monster, short other) +{ + const char *mechanism = "killed by something unknown (?)"; + char mechanism_buf[128]; + const char *article; + char message_buf[128]; + + md_ignore_signals(); + + if (other != QUIT) { + rogue.gold = ((rogue.gold * 9) / 10); + } + + if (other) { + switch(other) { + case HYPOTHERMIA: + mechanism = "died of hypothermia"; + break; + case STARVATION: + mechanism = "died of starvation"; + break; + case POISON_DART: + mechanism = "killed by a dart"; + break; + case QUIT: + mechanism = "quit"; + break; + case KFIRE: + mechanism = "killed by fire"; + break; + } + } else { + if (is_vowel(m_names[monster->m_char - 'A'][0])) { + article = "an"; + } else { + article = "a"; + } + snprintf(mechanism_buf, sizeof(mechanism_buf), + "Killed by %s %s", + article, m_names[monster->m_char - 'A']); + mechanism = mechanism_buf; + } + snprintf(message_buf, sizeof(message_buf), + "%s with %ld gold", mechanism, rogue.gold); + + if ((!other) && (!no_skull)) { + clear(); + mvaddstr(4, 32, "__---------__"); + mvaddstr(5, 30, "_~ ~_"); + mvaddstr(6, 29, "/ \\"); + mvaddstr(7, 28, "~ ~"); + mvaddstr(8, 27, "/ \\"); + mvaddstr(9, 27, "| XXXX XXXX |"); + mvaddstr(10, 27, "| XXXX XXXX |"); + mvaddstr(11, 27, "| XXX XXX |"); + mvaddstr(12, 28, "\\ @ /"); + mvaddstr(13, 29, "--\\ @@@ /--"); + mvaddstr(14, 30, "| | @@@ | |"); + mvaddstr(15, 30, "| | | |"); + mvaddstr(16, 30, "| vvVvvvvvvvVvv |"); + mvaddstr(17, 30, "| ^^^^^^^^^^^ |"); + mvaddstr(18, 31, "\\_ _/"); + mvaddstr(19, 33, "~---------~"); + center(21, nick_name); + center(22, message_buf); + } else { + messagef(0, "%s", message_buf); + } + messagef(0, "%s", ""); /* gcc objects to just "" */ + put_scores(monster, other); +} + +void +win(void) +{ + unwield(rogue.weapon); /* disarm and relax */ + unwear(rogue.armor); + un_put_on(rogue.left_ring); + un_put_on(rogue.right_ring); + + clear(); + mvaddstr(10, 11, "@ @ @@@ @ @ @ @ @ @@@ @ @ @"); + mvaddstr(11, 11, " @ @ @ @ @ @ @ @ @ @ @ @@ @ @"); + mvaddstr(12, 11, " @ @ @ @ @ @ @ @ @ @ @ @ @ @"); + mvaddstr(13, 11, " @ @ @ @ @ @ @ @ @ @ @ @@"); + mvaddstr(14, 11, " @ @@@ @@@ @@ @@ @@@ @ @ @"); + mvaddstr(17, 11, "Congratulations, you have been admitted to the"); + mvaddstr(18, 11, "Fighters' Guild. You return home, sell all your"); + mvaddstr(19, 11, "treasures at great profit and retire into comfort."); + messagef(0, "%s", ""); /* gcc objects to just "" */ + messagef(0, "%s", ""); /* gcc objects to just "" */ + id_all(); + sell_pack(); + put_scores(NULL, WIN); +} + +void +quit(boolean from_intrpt) +{ + char buf[DCOLS]; + short i, orow, ocol; + boolean mc; + + orow = ocol = 0; + mc = FALSE; + md_ignore_signals(); + + if (from_intrpt) { + orow = rogue.row; + ocol = rogue.col; + + mc = msg_cleared; + + for (i = 0; i < DCOLS; i++) { + buf[i] = mvinch(0, i); + } + } + check_message(); + messagef(1, "really quit?"); + if (rgetchar() != 'y') { + md_heed_signals(); + check_message(); + if (from_intrpt) { + for (i = 0; i < DCOLS; i++) { + mvaddch(0, i, buf[i]); + } + msg_cleared = mc; + move(orow, ocol); + refresh(); + } + return; + } + if (from_intrpt) { + clean_up(byebye_string); + } + check_message(); + killed_by(NULL, QUIT); +} + +/* + * The score file on disk is up to ten entries of the form + * score block [80 bytes] + * nickname block [30 bytes] + * + * The score block is to be parsed as follows: + * bytes 0-1 Rank (" 1" to "10") + * bytes 2-4 space padding + * bytes 5-15 Score/gold + * byte 15 up to a ':' Login name + * past the ':' Death mechanism + * + * The nickname block is an alternate name to be printed in place of the + * login name. Both blocks are supposed to contain a null-terminator. + */ + +struct score_entry { + long gold; + char username[80]; + char death[80]; + char nickname[30]; +}; + +#define NUM_SCORE_ENTRIES 10 + +static void make_score(struct score_entry *, const object *, int); + +static +void +pad_spaces(char *str, size_t len) +{ + size_t x; + for (x=strlen(str); x0 && str[x-1]==' '; x--); + str[x] = 0; +} + +static +int +read_score_entry(struct score_entry *se, FILE *fp) +{ + char score_block[80]; + char nickname_block[30]; + size_t n, x; + + n = fread(score_block, 1, sizeof(score_block), fp); + if (n==0) { + /* EOF */ + return 0; + } + if (n != sizeof(score_block)) { + sf_error(); + } + + n = fread(nickname_block, 1, sizeof(nickname_block), fp); + if (n != sizeof(nickname_block)) { + sf_error(); + } + + xxxx(score_block, sizeof(score_block)); + xxxx(nickname_block, sizeof(nickname_block)); + + /* Ensure null termination */ + score_block[sizeof(score_block)-1] = 0; + nickname_block[sizeof(nickname_block)-1] = 0; + + /* If there are other nulls in the score block, file is corrupt */ + if (strlen(score_block)!=sizeof(score_block)-1) { + sf_error(); + } + /* but this is NOT true of the nickname block */ + + /* quash trailing spaces */ + unpad_spaces(score_block); + unpad_spaces(nickname_block); + + for (x=5; score_block[x] == ' '; x++); + se->gold = lget_number(score_block+x); + + for (x=15; score_block[x] != 0 && score_block[x] != ':'; x++); + if (score_block[x] == 0) { + sf_error(); + } + score_block[x++] = 0; + strlcpy(se->username, score_block+15, sizeof(se->username)); + + strlcpy(se->death, score_block+x, sizeof(se->death)); + strlcpy(se->nickname, nickname_block, sizeof(se->nickname)); + + return 1; +} + +static +void +write_score_entry(const struct score_entry *se, int rank, FILE *fp) +{ + char score_block[80]; + char nickname_block[30]; + + /* avoid writing crap to score file */ + memset(score_block, 0, sizeof(score_block)); + memset(nickname_block, 0, sizeof(nickname_block)); + + snprintf(score_block, sizeof(score_block), + "%2d %6ld %s: %s", + rank+1, se->gold, se->username, se->death); + strlcpy(nickname_block, se->nickname, sizeof(nickname_block)); + + /* pad blocks out with spaces */ + pad_spaces(score_block, sizeof(score_block)); + /*pad_spaces(nickname_block, sizeof(nickname_block)); -- wrong! */ + + xxxx(score_block, sizeof(score_block)); + xxxx(nickname_block, sizeof(nickname_block)); + + fwrite(score_block, 1, sizeof(score_block), fp); + fwrite(nickname_block, 1, sizeof(nickname_block), fp); +} + +void +put_scores(const object *monster, short other) +{ + short i, rank=-1, found_player = -1, numscores = 0; + struct score_entry scores[NUM_SCORE_ENTRIES]; + const char *name; + FILE *fp; + boolean dopause = score_only; + + md_lock(1); + + setegid(egid); + if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL && + (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) { + setegid(gid); + messagef(0, "cannot read/write/create score file"); + sf_error(); + } + setegid(gid); + rewind(fp); + (void)xxx(1); + + for (numscores = 0; numscores < NUM_SCORE_ENTRIES; numscores++) { + if (read_score_entry(&scores[numscores], fp) == 0) { + break; + } + } + + /* Search the score list. */ + for (i=0; i= scores[i].gold) { + rank = i; + break; + } + } + + if (rank < NUM_SCORE_ENTRIES) { + /* Open up a slot */ + for (i = numscores; i > rank; i--) { + scores[i] = scores[i-1]; + } + numscores++; + + /* Put our info in the slot */ + make_score(&scores[rank], monster, other); + } + + /* Now rewrite the score file */ + + md_ignore_signals(); + rewind(fp); + (void)xxx(1); + + for (i = 0; i < numscores; i++) { + write_score_entry(&scores[i], i, fp); + } + } + md_lock(0); + fclose(fp); + + /* Display the scores */ + + clear(); + mvaddstr(3, 30, "Top Ten Rogueists"); + mvaddstr(8, 0, "Rank Score Name"); + + for (i = 0; i < numscores; i++) { + if (i == rank) { + standout(); + } + + if (scores[i].nickname[0]) { + name = scores[i].nickname; + } else { + name = scores[i].username; + } + + mvprintw(i+10, 0, "%2d %6ld %s: %s", + i+1, scores[i].gold, name, scores[i].death); + + if (i == rank) { + standend(); + } + } + refresh(); + messagef(0, "%s", ""); /* gcc objects to just "" */ + if (dopause) { + messagef(0, "%s", ""); + } + clean_up(""); +} + +static +void +make_score(struct score_entry *se, const object *monster, int other) +{ + const char *death = "bolts from the blue (?)"; + const char *hasamulet; + char deathbuf[80]; + + se->gold = rogue.gold; + strlcpy(se->username, login_name, sizeof(se->username)); + + if (other) { + switch(other) { + case HYPOTHERMIA: + death = "died of hypothermia"; + break; + case STARVATION: + death = "died of starvation"; + break; + case POISON_DART: + death = "killed by a dart"; + break; + case QUIT: + death = "quit"; + break; + case WIN: + death = "a total winner"; + break; + case KFIRE: + death = "killed by fire"; + break; + } + } else { + const char *mn, *article; + + mn = m_names[monster->m_char - 'A']; + if (is_vowel(mn[0])) { + article = "an"; + } else { + article = "a"; + } + + snprintf(deathbuf, sizeof(deathbuf), + "killed by %s %s", article, mn); + death = deathbuf; + } + + if (other != WIN && has_amulet()) { + hasamulet = " with amulet"; + } else { + hasamulet = ""; + } + + snprintf(se->death, sizeof(se->death), "%s on level %d%s", + death, max_level, hasamulet); + + strlcpy(se->nickname, nick_name, sizeof(se->nickname)); +} + +boolean +is_vowel(short ch) +{ + return( (ch == 'a') || + (ch == 'e') || + (ch == 'i') || + (ch == 'o') || + (ch == 'u') ); +} + +static void +sell_pack(void) +{ + object *obj; + short row = 2, val; + char buf[DCOLS]; + + obj = rogue.pack.next_object; + + clear(); + mvaddstr(1, 0, "Value Item"); + + while (obj) { + if (obj->what_is != FOOD) { + obj->identified = 1; + val = get_value(obj); + rogue.gold += val; + + if (row < DROWS) { + get_desc(obj, buf, sizeof(buf)); + mvprintw(row++, 0, "%5d %s", val, buf); + } + } + obj = obj->next_object; + } + refresh(); + if (rogue.gold > MAX_GOLD) { + rogue.gold = MAX_GOLD; + } + messagef(0, "%s", ""); /* gcc objects to just "" */ +} + +static int +get_value(const object *obj) +{ + short wc; + int val; + + val = 0; + wc = obj->which_kind; + + switch(obj->what_is) { + case WEAPON: + val = id_weapons[wc].value; + if ((wc == ARROW) || (wc == DAGGER) || (wc == SHURIKEN) || + (wc == DART)) { + val *= obj->quantity; + } + val += (obj->d_enchant * 85); + val += (obj->hit_enchant * 85); + break; + case ARMOR: + val = id_armors[wc].value; + val += (obj->d_enchant * 75); + if (obj->is_protected) { + val += 200; + } + break; + case WAND: + val = id_wands[wc].value * (obj->class + 1); + break; + case SCROL: + val = id_scrolls[wc].value * obj->quantity; + break; + case POTION: + val = id_potions[wc].value * obj->quantity; + break; + case AMULET: + val = 5000; + break; + case RING: + val = id_rings[wc].value * (obj->class + 1); + break; + } + if (val <= 0) { + val = 10; + } + return(val); +} + +static void +id_all(void) +{ + short i; + + for (i = 0; i < SCROLS; i++) { + id_scrolls[i].id_status = IDENTIFIED; + } + for (i = 0; i < WEAPONS; i++) { + id_weapons[i].id_status = IDENTIFIED; + } + for (i = 0; i < ARMORS; i++) { + id_armors[i].id_status = IDENTIFIED; + } + for (i = 0; i < WANDS; i++) { + id_wands[i].id_status = IDENTIFIED; + } + for (i = 0; i < POTIONS; i++) { + id_potions[i].id_status = IDENTIFIED; + } +} + +void +xxxx(char *buf, short n) +{ + short i; + unsigned char c; + + for (i = 0; i < n; i++) { + + /* It does not matter if accuracy is lost during this assignment */ + c = (unsigned char)xxx(0); + + buf[i] ^= c; + } +} + +long +xxx(boolean st) +{ + static long f, s; + long r; + + if (st) { + f = 37; + s = 7; + return(0L); + } + r = ((f * s) + 9337) % 8887; + f = s; + s = r; + return(r); +} + +static void +center(short row, const char *buf) +{ + short margin; + + margin = ((DCOLS - strlen(buf)) / 2); + mvaddstr(row, margin, buf); +} + +static void +sf_error(void) +{ + md_lock(0); + messagef(1, "%s", ""); /* gcc objects to just "" */ + clean_up("sorry, score file is out of order"); +} diff --git a/games/rogue/spec_hit.c b/games/rogue/spec_hit.c new file mode 100644 index 000000000..69e314c30 --- /dev/null +++ b/games/rogue/spec_hit.c @@ -0,0 +1,536 @@ +/* $NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)spec_hit.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: spec_hit.c,v 1.9 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * special_hit.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static void disappear(object *); +static void drain_life(void); +static void drop_level(void); +static void freeze(object *); +static int get_dir(short, short, short, short); +static boolean gold_at(short, short); +static void steal_gold(object *); +static void steal_item(object *); +static void sting(object *); +static boolean try_to_cough(short, short, object *); + +short less_hp = 0; +boolean being_held; + +void +special_hit(object *monster) +{ + if ((monster->m_flags & CONFUSED) && rand_percent(66)) { + return; + } + if (monster->m_flags & RUSTS) { + rust(monster); + } + if ((monster->m_flags & HOLDS) && !levitate) { + being_held = 1; + } + if (monster->m_flags & FREEZES) { + freeze(monster); + } + if (monster->m_flags & STINGS) { + sting(monster); + } + if (monster->m_flags & DRAINS_LIFE) { + drain_life(); + } + if (monster->m_flags & DROPS_LEVEL) { + drop_level(); + } + if (monster->m_flags & STEALS_GOLD) { + steal_gold(monster); + } else if (monster->m_flags & STEALS_ITEM) { + steal_item(monster); + } +} + +void +rust(object *monster) +{ + if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) || + (rogue.armor->which_kind == LEATHER)) { + return; + } + if ((rogue.armor->is_protected) || maintain_armor) { + if (monster && (!(monster->m_flags & RUST_VANISHED))) { + messagef(0, "the rust vanishes instantly"); + monster->m_flags |= RUST_VANISHED; + } + } else { + rogue.armor->d_enchant--; + messagef(0, "your armor weakens"); + print_stats(STAT_ARMOR); + } +} + +void +freeze(object *monster) +{ + short freeze_percent = 99; + short i, n; + + if (rand_percent(12)) { + return; + } + freeze_percent -= (rogue.str_current+(rogue.str_current / 2)); + freeze_percent -= ((rogue.exp + ring_exp) * 4); + freeze_percent -= (get_armor_class(rogue.armor) * 5); + freeze_percent -= (rogue.hp_max / 3); + + if (freeze_percent > 10) { + monster->m_flags |= FREEZING_ROGUE; + messagef(1, "you are frozen"); + + n = get_rand(4, 8); + for (i = 0; i < n; i++) { + mv_mons(); + } + if (rand_percent(freeze_percent)) { + for (i = 0; i < 50; i++) { + mv_mons(); + } + killed_by(NULL, HYPOTHERMIA); + } + messagef(1, "%s", you_can_move_again); + monster->m_flags &= (~FREEZING_ROGUE); + } +} + +void +steal_gold(object *monster) +{ + int amount; + + if ((rogue.gold <= 0) || rand_percent(10)) { + return; + } + + amount = get_rand((cur_level * 10), (cur_level * 30)); + + if (amount > rogue.gold) { + amount = rogue.gold; + } + rogue.gold -= amount; + messagef(0, "your purse feels lighter"); + print_stats(STAT_GOLD); + disappear(monster); +} + +void +steal_item(object *monster) +{ + object *obj; + short i, n, t = 0; + char desc[80]; + boolean has_something = 0; + + if (rand_percent(15)) { + return; + } + obj = rogue.pack.next_object; + + if (!obj) { + goto DSPR; + } + while (obj) { + if (!(obj->in_use_flags & BEING_USED)) { + has_something = 1; + break; + } + obj = obj->next_object; + } + if (!has_something) { + goto DSPR; + } + n = get_rand(0, MAX_PACK_COUNT); + obj = rogue.pack.next_object; + + for (i = 0; i <= n; i++) { + obj = obj->next_object; + while ((!obj) || (obj->in_use_flags & BEING_USED)) { + if (!obj) { + obj = rogue.pack.next_object; + } else { + obj = obj->next_object; + } + } + } + if (obj->what_is != WEAPON) { + t = obj->quantity; + obj->quantity = 1; + } + get_desc(obj, desc, sizeof(desc)); + messagef(0, "she stole %s", desc); + + obj->quantity = ((obj->what_is != WEAPON) ? t : 1); + + vanish(obj, 0, &rogue.pack); +DSPR: + disappear(monster); +} + +static void +disappear(object *monster) +{ + short row, col; + + row = monster->row; + col = monster->col; + + dungeon[row][col] &= ~MONSTER; + if (rogue_can_see(row, col)) { + mvaddch(row, col, get_dungeon_char(row, col)); + } + take_from_pack(monster, &level_monsters); + free_object(monster); + mon_disappeared = 1; +} + +void +cough_up(object *monster) +{ + object *obj; + short row, col, i, n; + + if (cur_level < max_level) { + return; + } + + if (monster->m_flags & STEALS_GOLD) { + obj = alloc_object(); + obj->what_is = GOLD; + obj->quantity = get_rand((cur_level * 15), (cur_level * 30)); + } else { + if (!rand_percent((int)monster->drop_percent)) { + return; + } + obj = gr_object(); + } + row = monster->row; + col = monster->col; + + for (n = 0; n <= 5; n++) { + for (i = -n; i <= n; i++) { + if (try_to_cough(row+n, col+i, obj)) { + return; + } + if (try_to_cough(row-n, col+i, obj)) { + return; + } + } + for (i = -n; i <= n; i++) { + if (try_to_cough(row+i, col-n, obj)) { + return; + } + if (try_to_cough(row+i, col+n, obj)) { + return; + } + } + } + free_object(obj); +} + +static boolean +try_to_cough(short row, short col, object *obj) +{ + if ((row < MIN_ROW) || + (row > (DROWS-2)) || (col < 0) || (col>(DCOLS-1))) { + return(0); + } + if ((!(dungeon[row][col] & (OBJECT | STAIRS | TRAP))) && + (dungeon[row][col] & (TUNNEL | FLOOR | DOOR))) { + place_at(obj, row, col); + if (((row != rogue.row) || (col != rogue.col)) && + (!(dungeon[row][col] & MONSTER))) { + mvaddch(row, col, get_dungeon_char(row, col)); + } + return(1); + } + return(0); +} + +boolean +seek_gold(object *monster) +{ + short i, j, rn, s; + + if ((rn = get_room_number(monster->row, monster->col)) < 0) { + return(0); + } + for (i = rooms[rn].top_row+1; i < rooms[rn].bottom_row; i++) { + for (j = rooms[rn].left_col+1; j < rooms[rn].right_col; j++) { + if ((gold_at(i, j)) && !(dungeon[i][j] & MONSTER)) { + monster->m_flags |= CAN_FLIT; + s = mon_can_go(monster, i, j); + monster->m_flags &= (~CAN_FLIT); + if (s) { + move_mon_to(monster, i, j); + monster->m_flags |= ASLEEP; + monster->m_flags &= (~(WAKENS | SEEKS_GOLD)); + return(1); + } + monster->m_flags &= (~SEEKS_GOLD); + monster->m_flags |= CAN_FLIT; + mv_1_monster(monster, i, j); + monster->m_flags &= (~CAN_FLIT); + monster->m_flags |= SEEKS_GOLD; + return(1); + } + } + } + return(0); +} + +static boolean +gold_at(short row, short col) +{ + if (dungeon[row][col] & OBJECT) { + object *obj; + + if ((obj = object_at(&level_objects, row, col)) && + (obj->what_is == GOLD)) { + return(1); + } + } + return(0); +} + +void +check_gold_seeker(object *monster) +{ + monster->m_flags &= (~SEEKS_GOLD); +} + +boolean +check_imitator(object *monster) +{ + if (monster->m_flags & IMITATES) { + wake_up(monster); + if (!blind) { + mvaddch(monster->row, monster->col, + get_dungeon_char(monster->row, monster->col)); + check_message(); + messagef(1, "wait, that's a %s!", mon_name(monster)); + } + return(1); + } + return(0); +} + +boolean +imitating(short row, short col) +{ + if (dungeon[row][col] & MONSTER) { + object *monster; + + if ((monster = object_at(&level_monsters, row, col)) != NULL) { + if (monster->m_flags & IMITATES) { + return(1); + } + } + } + return(0); +} + +static void +sting(object *monster) +{ + short sting_chance = 35; + + if ((rogue.str_current <= 3) || sustain_strength) { + return; + } + sting_chance += (6 * (6 - get_armor_class(rogue.armor))); + + if ((rogue.exp + ring_exp) > 8) { + sting_chance -= (6 * ((rogue.exp + ring_exp) - 8)); + } + if (rand_percent(sting_chance)) { + messagef(0, "the %s's bite has weakened you", + mon_name(monster)); + rogue.str_current--; + print_stats(STAT_STRENGTH); + } +} + +static void +drop_level(void) +{ + int hp; + + if (rand_percent(80) || (rogue.exp <= 5)) { + return; + } + rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29); + rogue.exp -= 2; + hp = hp_raise(); + if ((rogue.hp_current -= hp) <= 0) { + rogue.hp_current = 1; + } + if ((rogue.hp_max -= hp) <= 0) { + rogue.hp_max = 1; + } + add_exp(1, 0); +} + +void +drain_life(void) +{ + short n; + + if (rand_percent(60) || (rogue.hp_max <= 30) || (rogue.hp_current < 10)) { + return; + } + n = get_rand(1, 3); /* 1 Hp, 2 Str, 3 both */ + + if ((n != 2) || (!sustain_strength)) { + messagef(0, "you feel weaker"); + } + if (n != 2) { + rogue.hp_max--; + rogue.hp_current--; + less_hp++; + } + if (n != 1) { + if ((rogue.str_current > 3) && (!sustain_strength)) { + rogue.str_current--; + if (coin_toss()) { + rogue.str_max--; + } + } + } + print_stats((STAT_STRENGTH | STAT_HP)); +} + +boolean +m_confuse(object *monster) +{ + if (!rogue_can_see(monster->row, monster->col)) { + return(0); + } + if (rand_percent(45)) { + monster->m_flags &= (~CONFUSES); /* will not confuse the rogue */ + return(0); + } + if (rand_percent(55)) { + monster->m_flags &= (~CONFUSES); + messagef(1, "the gaze of the %s has confused you", + mon_name(monster)); + cnfs(); + return(1); + } + return(0); +} + +boolean +flame_broil(object *monster) +{ + short row, col, dir; + + if ((!mon_sees(monster, rogue.row, rogue.col)) || coin_toss()) { + return(0); + } + row = rogue.row - monster->row; + col = rogue.col - monster->col; + if (row < 0) { + row = -row; + } + if (col < 0) { + col = -col; + } + if (((row != 0) && (col != 0) && (row != col)) || + ((row > 7) || (col > 7))) { + return(0); + } + dir = get_dir(monster->row, monster->col, row, col); + bounce(FIRE, dir, monster->row, monster->col, 0); + + return(1); +} + +static int +get_dir(short srow, short scol, short drow, short dcol) +{ + if (srow == drow) { + if (scol < dcol) { + return(RIGHT); + } else { + return(LEFT); + } + } + if (scol == dcol) { + if (srow < drow) { + return(DOWN); + } else { + return(UPWARD); + } + } + if ((srow > drow) && (scol > dcol)) { + return(UPLEFT); + } + if ((srow < drow) && (scol < dcol)) { + return(DOWNRIGHT); + } + if ((srow < drow) && (scol > dcol)) { + return(DOWNLEFT); + } + /*if ((srow > drow) && (scol < dcol)) {*/ + return(UPRIGHT); + /*}*/ +} diff --git a/games/rogue/throw.c b/games/rogue/throw.c new file mode 100644 index 000000000..93c88c5a2 --- /dev/null +++ b/games/rogue/throw.c @@ -0,0 +1,324 @@ +/* $NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)throw.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: throw.c,v 1.12 2011/05/23 23:01:17 joerg Exp $"); +#endif +#endif /* not lint */ + +/* + * throw.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static void flop_weapon(object *, short, short); +static object *get_thrown_at_monster(object *, short, short *, short *); +static boolean throw_at_monster(object *, object *); + +void +throw(void) +{ + short wch, d; + boolean first_miss = 1; + object *weapon; + short dir, row, col; + object *monster; + + while (!is_direction(dir = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction? "); + first_miss = 0; + } + } + check_message(); + if (dir == CANCEL) { + return; + } + if ((wch = pack_letter("throw what?", WEAPON)) == CANCEL) { + return; + } + check_message(); + + if (!(weapon = get_letter_object(wch))) { + messagef(0, "no such item."); + return; + } + if ((weapon->in_use_flags & BEING_USED) && weapon->is_cursed) { + messagef(0, "%s", curse_message); + return; + } + row = rogue.row; col = rogue.col; + + if ((weapon->in_use_flags & BEING_WIELDED) && (weapon->quantity <= 1)) { + unwield(rogue.weapon); + } else if (weapon->in_use_flags & BEING_WORN) { + mv_aquatars(); + unwear(rogue.armor); + print_stats(STAT_ARMOR); + } else if (weapon->in_use_flags & ON_EITHER_HAND) { + un_put_on(weapon); + } + monster = get_thrown_at_monster(weapon, d, &row, &col); + mvaddch(rogue.row, rogue.col, rogue.fchar); + refresh(); + + if (rogue_can_see(row, col) && ((row != rogue.row) || (col != rogue.col))){ + mvaddch(row, col, get_dungeon_char(row, col)); + } + if (monster) { + wake_up(monster); + check_gold_seeker(monster); + + if (!throw_at_monster(monster, weapon)) { + flop_weapon(weapon, row, col); + } + } else { + flop_weapon(weapon, row, col); + } + vanish(weapon, 1, &rogue.pack); +} + +boolean +throw_at_monster(object *monster, object *weapon) +{ + short damage, hit_chance; + short t; + + hit_chance = get_hit_chance(weapon); + damage = get_weapon_damage(weapon); + if ((weapon->which_kind == ARROW) && + (rogue.weapon && (rogue.weapon->which_kind == BOW))) { + damage += get_weapon_damage(rogue.weapon); + damage = ((damage * 2) / 3); + hit_chance += (hit_chance / 3); + } else if ((weapon->in_use_flags & BEING_WIELDED) && + ((weapon->which_kind == DAGGER) || + (weapon->which_kind == SHURIKEN) || + (weapon->which_kind == DART))) { + damage = ((damage * 3) / 2); + hit_chance += (hit_chance / 3); + } + t = weapon->quantity; + weapon->quantity = 1; + snprintf(hit_message, HIT_MESSAGE_SIZE, "the %s", name_of(weapon)); + weapon->quantity = t; + + if (!rand_percent(hit_chance)) { + (void)strlcat(hit_message, "misses ", HIT_MESSAGE_SIZE); + return(0); + } + s_con_mon(monster); + (void)strlcat(hit_message, "hit ", HIT_MESSAGE_SIZE); + (void)mon_damage(monster, damage); + return(1); +} + +object * +get_thrown_at_monster(object *obj, short dir, short *row, short *col) +{ + short orow, ocol; + short i, ch; + + orow = *row; ocol = *col; + + ch = get_mask_char(obj->what_is); + + for (i = 0; i < 24; i++) { + get_dir_rc(dir, row, col, 0); + if ( (((*col <= 0) || (*col >= DCOLS-1)) || + (dungeon[*row][*col] == NOTHING)) || + ((dungeon[*row][*col] & (HORWALL | VERTWALL | HIDDEN)) && + (!(dungeon[*row][*col] & TRAP)))) { + *row = orow; + *col = ocol; + return(0); + } + if ((i != 0) && rogue_can_see(orow, ocol)) { + mvaddch(orow, ocol, get_dungeon_char(orow, ocol)); + } + if (rogue_can_see(*row, *col)) { + if (!(dungeon[*row][*col] & MONSTER)) { + mvaddch(*row, *col, ch); + } + refresh(); + } + orow = *row; ocol = *col; + if (dungeon[*row][*col] & MONSTER) { + if (!imitating(*row, *col)) { + return(object_at(&level_monsters, *row, *col)); + } + } + if (dungeon[*row][*col] & TUNNEL) { + i += 2; + } + } + return(0); +} + +void +flop_weapon(object *weapon, short row, short col) +{ + object *new_weapon, *monster; + short i = 0; + boolean found = 0; + short mch, dch; + unsigned short mon; + + if ((row < 0) || (row >= DROWS) || (col < 0) || (col >= DCOLS)) + clean_up("flop_weapon: weapon landed outside of dungeon"); + + while ((i < 9) && dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER)) { + rand_around(i++, &row, &col); + if ((row > (DROWS-2)) || (row < MIN_ROW) || + (col > (DCOLS-1)) || (col < 0) || (!dungeon[row][col]) || + (dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER))) { + continue; + } + found = 1; + break; + } + + if (found || (i == 0)) { + new_weapon = alloc_object(); + *new_weapon = *weapon; + new_weapon->in_use_flags = NOT_USED; + new_weapon->quantity = 1; + new_weapon->ichar = 'L'; + place_at(new_weapon, row, col); + if (rogue_can_see(row, col) && + ((row != rogue.row) || (col != rogue.col))) { + mon = dungeon[row][col] & MONSTER; + dungeon[row][col] &= (~MONSTER); + dch = get_dungeon_char(row, col); + if (mon) { + mch = mvinch(row, col); + if ((monster = object_at(&level_monsters, + row, col)) != NULL) { + monster->trail_char = dch; + } + if ((mch < 'A') || (mch > 'Z')) { + mvaddch(row, col, dch); + } + } else { + mvaddch(row, col, dch); + } + dungeon[row][col] |= mon; + } + } else { + short t; + + t = weapon->quantity; + weapon->quantity = 1; + messagef(0, "the %svanishes as it hits the ground", + name_of(weapon)); + weapon->quantity = t; + } +} + +void +rand_around(short i, short *r, short *c) +{ + static char pos[] = "\010\007\001\003\004\005\002\006\0"; + static short row, col; + short j; + + if (i == 0) { + short x, y, o, t; + + row = *r; + col = *c; + + o = get_rand(1, 8); + + for (j = 0; j < 5; j++) { + x = get_rand(0, 8); + y = (x + o) % 9; + t = pos[x]; + pos[x] = pos[y]; + pos[y] = t; + } + } + switch((short)pos[i]) { + case 0: + *r = row + 1; + *c = col + 1; + break; + case 1: + *r = row + 1; + *c = col - 1; + break; + case 2: + *r = row - 1; + *c = col + 1; + break; + case 3: + *r = row - 1; + *c = col - 1; + break; + case 4: + *r = row; + *c = col + 1; + break; + case 5: + *r = row + 1; + *c = col; + break; + case 6: + *r = row; + *c = col; + break; + case 7: + *r = row - 1; + *c = col; + break; + case 8: + *r = row; + *c = col - 1; + break; + } +} diff --git a/games/rogue/trap.c b/games/rogue/trap.c new file mode 100644 index 000000000..ae3c91860 --- /dev/null +++ b/games/rogue/trap.c @@ -0,0 +1,282 @@ +/* $NetBSD: trap.c,v 1.10 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)trap.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: trap.c,v 1.10 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * trap.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +trap traps[MAX_TRAPS]; +boolean trap_door = 0; +short bear_trap = 0; + +static const char *const trap_strings[TRAPS * 2] = { + "trap door", + "you fell down a trap", + "bear trap", + "you are caught in a bear trap", + "teleport trap", + "teleport", + "poison dart trap", + "a small dart just hit you in the shoulder", + "sleeping gas trap", + "a strange white mist envelops you and you fall asleep", + "rust trap", + "a gush of water hits you on the head" +}; + +static short +trap_at(int row, int col) +{ + short i; + + for (i = 0; ((i < MAX_TRAPS) && (traps[i].trap_type != NO_TRAP)); i++) { + if ((traps[i].trap_row == row) && (traps[i].trap_col == col)) { + return(traps[i].trap_type); + } + } + return(NO_TRAP); +} + +void +trap_player(short row, short col) +{ + short t; + + if ((t = trap_at(row, col)) == NO_TRAP) { + return; + } + dungeon[row][col] &= (~HIDDEN); + if (rand_percent(rogue.exp + ring_exp)) { + messagef(1, "the trap failed"); + return; + } + switch(t) { + case TRAP_DOOR: + trap_door = 1; + new_level_message = trap_strings[(t*2)+1]; + break; + case BEAR_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + bear_trap = get_rand(4, 7); + break; + case TELE_TRAP: + mvaddch(rogue.row, rogue.col, '^'); + tele(); + break; + case DART_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + rogue.hp_current -= get_damage("1d6", 1); + if (rogue.hp_current <= 0) { + rogue.hp_current = 0; + } + if ((!sustain_strength) && rand_percent(40) && + (rogue.str_current >= 3)) { + rogue.str_current--; + } + print_stats(STAT_HP | STAT_STRENGTH); + if (rogue.hp_current <= 0) { + killed_by((object *)0, POISON_DART); + } + break; + case SLEEPING_GAS_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + take_a_nap(); + break; + case RUST_TRAP: + messagef(1, "%s", trap_strings[(t*2)+1]); + rust(NULL); + break; + } +} + +void +add_traps(void) +{ + short i, n, tries = 0; + short row, col; + + if (cur_level <= 2) { + n = 0; + } else if (cur_level <= 7) { + n = get_rand(0, 2); + } else if (cur_level <= 11) { + n = get_rand(1, 2); + } else if (cur_level <= 16) { + n = get_rand(2, 3); + } else if (cur_level <= 21) { + n = get_rand(2, 4); + } else if (cur_level <= (AMULET_LEVEL + 2)) { + n = get_rand(3, 5); + } else { + n = get_rand(5, MAX_TRAPS); + } + for (i = 0; i < n; i++) { + traps[i].trap_type = get_rand(0, (TRAPS - 1)); + + if ((i == 0) && (party_room != NO_ROOM)) { + do { + row = get_rand((rooms[party_room].top_row+1), + (rooms[party_room].bottom_row-1)); + col = get_rand((rooms[party_room].left_col+1), + (rooms[party_room].right_col-1)); + tries++; + } while (((dungeon[row][col] & (OBJECT|STAIRS|TRAP|TUNNEL)) || + (dungeon[row][col] == NOTHING)) && (tries < 15)); + if (tries >= 15) { + gr_row_col(&row, &col, (FLOOR | MONSTER)); + } + } else { + gr_row_col(&row, &col, (FLOOR | MONSTER)); + } + traps[i].trap_row = row; + traps[i].trap_col = col; + dungeon[row][col] |= (TRAP | HIDDEN); + } +} + +void +id_trap(void) +{ + short dir, row, col, d, t; + + messagef(0, "direction? "); + + while (!is_direction(dir = rgetchar(), &d)) { + sound_bell(); + } + check_message(); + + if (dir == CANCEL) { + return; + } + row = rogue.row; + col = rogue.col; + + get_dir_rc(d, &row, &col, 0); + + if ((dungeon[row][col] & TRAP) && (!(dungeon[row][col] & HIDDEN))) { + t = trap_at(row, col); + messagef(0, "%s", trap_strings[t*2]); + } else { + messagef(0, "no trap there"); + } +} + +void +show_traps(void) +{ + short i, j; + + for (i = 0; i < DROWS; i++) { + for (j = 0; j < DCOLS; j++) { + if (dungeon[i][j] & TRAP) { + mvaddch(i, j, '^'); + } + } + } +} + +void +search(short n, boolean is_auto) +{ + short s, i, j, row, col, t; + short shown = 0, found = 0; + static boolean reg_search; + + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + row = rogue.row + i; + col = rogue.col + j; + if ((row < MIN_ROW) || (row >= (DROWS-1)) || + (col < 0) || (col >= DCOLS)) { + continue; + } + if (dungeon[row][col] & HIDDEN) { + found++; + } + } + } + for (s = 0; s < n; s++) { + for (i = -1; i <= 1; i++) { + for (j = -1; j <= 1; j++) { + row = rogue.row + i; + col = rogue.col + j ; + if ((row < MIN_ROW) || (row >= (DROWS-1)) || + (col < 0) || (col >= DCOLS)) { + continue; + } + if (dungeon[row][col] & HIDDEN) { + if (rand_percent(17 + (rogue.exp + ring_exp))) { + dungeon[row][col] &= (~HIDDEN); + if ((!blind) && ((row != rogue.row) || + (col != rogue.col))) { + mvaddch(row, col, get_dungeon_char(row, col)); + } + shown++; + if (dungeon[row][col] & TRAP) { + t = trap_at(row, col); + messagef(1, "%s", + trap_strings[t*2]); + } + } + } + if (((shown == found) && (found > 0)) || interrupted) { + return; + } + } + } + if ((!is_auto) && (reg_search = !reg_search)) { + (void)reg_move(); + } + } +} diff --git a/games/rogue/use.c b/games/rogue/use.c new file mode 100644 index 000000000..8371a6f0f --- /dev/null +++ b/games/rogue/use.c @@ -0,0 +1,625 @@ +/* $NetBSD: use.c,v 1.10 2009/08/12 08:44:45 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)use.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: use.c,v 1.10 2009/08/12 08:44:45 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * use.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +short halluc = 0; +short blind = 0; +short confused = 0; +short levitate = 0; +short haste_self = 0; +boolean see_invisible = 0; +short extra_hp = 0; +boolean detect_monster = 0; +boolean con_mon = 0; + +static const char strange_feeling[] = + "you have a strange feeling for a moment, then it passes"; + +static const char *get_ench_color(void); +static void go_blind(void); +static void hold_monster(void); +static void idntfy(void); +static void potion_heal(int); +static void uncurse_all(void); + +void +quaff(void) +{ + short ch; + object *obj; + + ch = pack_letter("quaff what?", POTION); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != POTION) { + messagef(0, "you can't drink that"); + return; + } + switch(obj->which_kind) { + case INCREASE_STRENGTH: + messagef(0, "you feel stronger now, what bulging muscles!"); + rogue.str_current++; + if (rogue.str_current > rogue.str_max) { + rogue.str_max = rogue.str_current; + } + break; + case RESTORE_STRENGTH: + rogue.str_current = rogue.str_max; + messagef(0, "this tastes great, you feel warm all over"); + break; + case HEALING: + messagef(0, "you begin to feel better"); + potion_heal(0); + break; + case EXTRA_HEALING: + messagef(0, "you begin to feel much better"); + potion_heal(1); + break; + case POISON: + if (!sustain_strength) { + rogue.str_current -= get_rand(1, 3); + if (rogue.str_current < 1) { + rogue.str_current = 1; + } + } + messagef(0, "you feel very sick now"); + if (halluc) { + unhallucinate(); + } + break; + case RAISE_LEVEL: + rogue.exp_points = level_points[rogue.exp - 1]; + messagef(0, "you suddenly feel much more skillful"); + add_exp(1, 1); + break; + case BLINDNESS: + go_blind(); + break; + case HALLUCINATION: + messagef(0, "oh wow, everything seems so cosmic"); + halluc += get_rand(500, 800); + break; + case DETECT_MONSTER: + show_monsters(); + if (!(level_monsters.next_monster)) { + messagef(0, "%s", strange_feeling); + } + break; + case DETECT_OBJECTS: + if (level_objects.next_object) { + if (!blind) { + show_objects(); + } + } else { + messagef(0, "%s", strange_feeling); + } + break; + case CONFUSION: + messagef(0, (halluc ? "what a trippy feeling" : + "you feel confused")); + cnfs(); + break; + case LEVITATION: + messagef(0, "you start to float in the air"); + levitate += get_rand(15, 30); + being_held = bear_trap = 0; + break; + case HASTE_SELF: + messagef(0, "you feel yourself moving much faster"); + haste_self += get_rand(11, 21); + if (!(haste_self % 2)) { + haste_self++; + } + break; + case SEE_INVISIBLE: + messagef(0, "hmm, this potion tastes like %sjuice", + fruit); + if (blind) { + unblind(); + } + see_invisible = 1; + relight(); + break; + } + print_stats((STAT_STRENGTH | STAT_HP)); + if (id_potions[obj->which_kind].id_status != CALLED) { + id_potions[obj->which_kind].id_status = IDENTIFIED; + } + vanish(obj, 1, &rogue.pack); +} + +void +read_scroll(void) +{ + short ch; + object *obj; + + ch = pack_letter("read what?", SCROL); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != SCROL) { + messagef(0, "you can't read that"); + return; + } + switch(obj->which_kind) { + case SCARE_MONSTER: + messagef(0, "you hear a maniacal laughter in the distance"); + break; + case HOLD_MONSTER: + hold_monster(); + break; + case ENCH_WEAPON: + if (rogue.weapon) { + if (rogue.weapon->what_is == WEAPON) { + messagef(0, "your %sglow%s %sfor a moment", + name_of(rogue.weapon), + ((rogue.weapon->quantity <= 1) ? "s" : ""), + get_ench_color()); + if (coin_toss()) { + rogue.weapon->hit_enchant++; + } else { + rogue.weapon->d_enchant++; + } + } + rogue.weapon->is_cursed = 0; + } else { + messagef(0, "your hands tingle"); + } + break; + case ENCH_ARMOR: + if (rogue.armor) { + messagef(0, "your armor glows %sfor a moment", + get_ench_color()); + rogue.armor->d_enchant++; + rogue.armor->is_cursed = 0; + print_stats(STAT_ARMOR); + } else { + messagef(0, "your skin crawls"); + } + break; + case IDENTIFY: + messagef(0, "this is a scroll of identify"); + obj->identified = 1; + id_scrolls[obj->which_kind].id_status = IDENTIFIED; + idntfy(); + break; + case TELEPORT: + tele(); + break; + case SLEEP: + messagef(0, "you fall asleep"); + take_a_nap(); + break; + case PROTECT_ARMOR: + if (rogue.armor) { + messagef(0, "your armor is covered by a shimmering gold shield"); + rogue.armor->is_protected = 1; + rogue.armor->is_cursed = 0; + } else { + messagef(0, "your acne seems to have disappeared"); + } + break; + case REMOVE_CURSE: + messagef(0, (!halluc) ? + "you feel as though someone is watching over you" : + "you feel in touch with the universal oneness"); + uncurse_all(); + break; + case CREATE_MONSTER: + create_monster(); + break; + case AGGRAVATE_MONSTER: + aggravate(); + break; + case MAGIC_MAPPING: + messagef(0, "this scroll seems to have a map on it"); + draw_magic_map(); + break; + case CON_MON: + con_mon = 1; + messagef(0, "your hands glow %sfor a moment", + get_ench_color()); + break; + } + if (id_scrolls[obj->which_kind].id_status != CALLED) { + id_scrolls[obj->which_kind].id_status = IDENTIFIED; + } + vanish(obj, (obj->which_kind != SLEEP), &rogue.pack); +} + +/* vanish() does NOT handle a quiver of weapons with more than one + * arrow (or whatever) in the quiver. It will only decrement the count. + */ + +void +vanish(object *obj, short rm, object *pack) +{ + if (obj->quantity > 1) { + obj->quantity--; + } else { + if (obj->in_use_flags & BEING_WIELDED) { + unwield(obj); + } else if (obj->in_use_flags & BEING_WORN) { + unwear(obj); + } else if (obj->in_use_flags & ON_EITHER_HAND) { + un_put_on(obj); + } + take_from_pack(obj, pack); + free_object(obj); + } + if (rm) { + (void)reg_move(); + } +} + +static void +potion_heal(int extra) +{ + float ratio; + short add; + + rogue.hp_current += rogue.exp; + + ratio = ((float)rogue.hp_current) / rogue.hp_max; + + if (ratio >= 1.00) { + rogue.hp_max += (extra ? 2 : 1); + extra_hp += (extra ? 2 : 1); + rogue.hp_current = rogue.hp_max; + } else if (ratio >= 0.90) { + rogue.hp_max += (extra ? 1 : 0); + extra_hp += (extra ? 1 : 0); + rogue.hp_current = rogue.hp_max; + } else { + if (ratio < 0.33) { + ratio = 0.33; + } + if (extra) { + ratio += ratio; + } + add = (short)(ratio * (rogue.hp_max - rogue.hp_current)); + rogue.hp_current += add; + if (rogue.hp_current > rogue.hp_max) { + rogue.hp_current = rogue.hp_max; + } + } + if (blind) { + unblind(); + } + if (confused && extra) { + unconfuse(); + } else if (confused) { + confused = (confused / 2) + 1; + } + if (halluc && extra) { + unhallucinate(); + } else if (halluc) { + halluc = (halluc / 2) + 1; + } +} + +static void +idntfy(void) +{ + short ch; + object *obj; + struct id *id_table; + char desc[DCOLS]; +AGAIN: + ch = pack_letter("what would you like to identify?", ALL_OBJECTS); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item, try again"); + messagef(0, "%s", ""); /* gcc objects to just "" */ + check_message(); + goto AGAIN; + } + obj->identified = 1; + if (obj->what_is & (SCROL | POTION | WEAPON | ARMOR | WAND | RING)) { + id_table = get_id_table(obj); + id_table[obj->which_kind].id_status = IDENTIFIED; + } + get_desc(obj, desc, sizeof(desc)); + messagef(0, "%s", desc); +} + +void +eat(void) +{ + short ch; + short moves; + object *obj; + + ch = pack_letter("eat what?", FOOD); + + if (ch == CANCEL) { + return; + } + if (!(obj = get_letter_object(ch))) { + messagef(0, "no such item."); + return; + } + if (obj->what_is != FOOD) { + messagef(0, "you can't eat that"); + return; + } + if ((obj->which_kind == FRUIT) || rand_percent(60)) { + moves = get_rand(950, 1150); + if (obj->which_kind == RATION) { + messagef(0, "yum, that tasted good"); + } else { + messagef(0, "my, that was a yummy %s", fruit); + } + } else { + moves = get_rand(750, 950); + messagef(0, "yuk, that food tasted awful"); + add_exp(2, 1); + } + rogue.moves_left /= 3; + rogue.moves_left += moves; + hunger_str[0] = 0; + print_stats(STAT_HUNGER); + + vanish(obj, 1, &rogue.pack); +} + +static void +hold_monster(void) +{ + short i, j; + short mcount = 0; + object *monster; + short row, col; + + for (i = -2; i <= 2; i++) { + for (j = -2; j <= 2; j++) { + row = rogue.row + i; + col = rogue.col + j; + if ((row < MIN_ROW) || (row > (DROWS-2)) || (col < 0) || + (col > (DCOLS-1))) { + continue; + } + if (dungeon[row][col] & MONSTER) { + monster = object_at(&level_monsters, row, col); + monster->m_flags |= ASLEEP; + monster->m_flags &= (~WAKENS); + mcount++; + } + } + } + if (mcount == 0) { + messagef(0, "you feel a strange sense of loss"); + } else if (mcount == 1) { + messagef(0, "the monster freezes"); + } else { + messagef(0, "the monsters around you freeze"); + } +} + +void +tele(void) +{ + mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); + + if (cur_room >= 0) { + darken_room(cur_room); + } + put_player(get_room_number(rogue.row, rogue.col)); + being_held = 0; + bear_trap = 0; +} + +void +hallucinate(void) +{ + object *obj, *monster; + short ch; + + if (blind) return; + + obj = level_objects.next_object; + + while (obj) { + ch = mvinch(obj->row, obj->col); + if (((ch < 'A') || (ch > 'Z')) && + ((obj->row != rogue.row) || (obj->col != rogue.col))) + if ((ch != ' ') && (ch != '.') && (ch != '#') && (ch != '+')) { + addch(gr_obj_char()); + } + obj = obj->next_object; + } + monster = level_monsters.next_monster; + + while (monster) { + ch = mvinch(monster->row, monster->col); + if ((ch >= 'A') && (ch <= 'Z')) { + addch(get_rand('A', 'Z')); + } + monster = monster->next_monster; + } +} + +void +unhallucinate(void) +{ + halluc = 0; + relight(); + messagef(1, "everything looks SO boring now"); +} + +void +unblind(void) +{ + blind = 0; + messagef(1, "the veil of darkness lifts"); + relight(); + if (halluc) { + hallucinate(); + } + if (detect_monster) { + show_monsters(); + } +} + +void +relight(void) +{ + if (cur_room == PASSAGE) { + light_passage(rogue.row, rogue.col); + } else { + light_up_room(cur_room); + } + mvaddch(rogue.row, rogue.col, rogue.fchar); +} + +void +take_a_nap(void) +{ + short i; + + i = get_rand(2, 5); + md_sleep(1); + + while (i--) { + mv_mons(); + } + md_sleep(1); + messagef(0, "%s", you_can_move_again); +} + +static void +go_blind(void) +{ + short i, j; + + if (!blind) { + messagef(0, "a cloak of darkness falls around you"); + } + blind += get_rand(500, 800); + + if (detect_monster) { + object *monster; + + monster = level_monsters.next_monster; + + while (monster) { + mvaddch(monster->row, monster->col, monster->trail_char); + monster = monster->next_monster; + } + } + if (cur_room >= 0) { + for (i = rooms[cur_room].top_row + 1; + i < rooms[cur_room].bottom_row; i++) { + for (j = rooms[cur_room].left_col + 1; + j < rooms[cur_room].right_col; j++) { + mvaddch(i, j, ' '); + } + } + } + mvaddch(rogue.row, rogue.col, rogue.fchar); +} + +static const char * +get_ench_color(void) +{ + if (halluc) { + return(id_potions[get_rand(0, POTIONS-1)].title); + } else if (con_mon) { + return("red "); + } + return("blue "); +} + +void +cnfs(void) +{ + confused += get_rand(12, 22); +} + +void +unconfuse(void) +{ + confused = 0; + messagef(1, "you feel less %s now", (halluc ? "trippy" : "confused")); +} + +static void +uncurse_all(void) +{ + object *obj; + + obj = rogue.pack.next_object; + + while (obj) { + obj->is_cursed = 0; + obj = obj->next_object; + } +} diff --git a/games/rogue/zap.c b/games/rogue/zap.c new file mode 100644 index 000000000..3e9134f0a --- /dev/null +++ b/games/rogue/zap.c @@ -0,0 +1,407 @@ +/* $NetBSD: zap.c,v 1.9 2008/01/14 03:50:03 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Timothy C. Stoehr. + * + * 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 of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)zap.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: zap.c,v 1.9 2008/01/14 03:50:03 dholland Exp $"); +#endif +#endif /* not lint */ + +/* + * zap.c + * + * This source herein may be modified and/or distributed by anybody who + * so desires, with the following restrictions: + * 1.) No portion of this notice shall be removed. + * 2.) Credit shall not be taken for the creation of this source. + * 3.) This code is not to be traded, sold, or used for personal + * gain or profit. + * + */ + +#include "rogue.h" + +static object *get_zapped_monster(short, short *, short *); +static void tele_away(object *); +static void wdrain_life(object *); +static void zap_monster(object *, unsigned short); + +boolean wizard = 0; + +void +zapp(void) +{ + short wch; + boolean first_miss = 1; + object *wand; + short dir, d, row, col; + object *monster; + + while (!is_direction(dir = rgetchar(), &d)) { + sound_bell(); + if (first_miss) { + messagef(0, "direction? "); + first_miss = 0; + } + } + check_message(); + if (dir == CANCEL) { + return; + } + if ((wch = pack_letter("zap with what?", WAND)) == CANCEL) { + return; + } + check_message(); + + if (!(wand = get_letter_object(wch))) { + messagef(0, "no such item."); + return; + } + if (wand->what_is != WAND) { + messagef(0, "you can't zap with that"); + return; + } + if (wand->class <= 0) { + messagef(0, "nothing happens"); + } else { + wand->class--; + row = rogue.row; col = rogue.col; + if ((wand->which_kind == COLD) || (wand->which_kind == FIRE)) { + bounce((short)wand->which_kind, d, row, col, 0); + } else { + monster = get_zapped_monster(d, &row, &col); + if (wand->which_kind == DRAIN_LIFE) { + wdrain_life(monster); + } else if (monster) { + wake_up(monster); + s_con_mon(monster); + zap_monster(monster, wand->which_kind); + relight(); + } + } + } + (void)reg_move(); +} + +static object * +get_zapped_monster(short dir, short *row, short *col) +{ + short orow, ocol; + + for (;;) { + orow = *row; ocol = *col; + get_dir_rc(dir, row, col, 0); + if (((*row == orow) && (*col == ocol)) || + (dungeon[*row][*col] & (HORWALL | VERTWALL)) || + (dungeon[*row][*col] == NOTHING)) { + return(0); + } + if (dungeon[*row][*col] & MONSTER) { + if (!imitating(*row, *col)) { + return(object_at(&level_monsters, *row, *col)); + } + } + } +} + +static void +zap_monster(object *monster, unsigned short kind) +{ + short row, col; + object *nm; + short tc; + + row = monster->row; + col = monster->col; + + switch(kind) { + case SLOW_MONSTER: + if (monster->m_flags & HASTED) { + monster->m_flags &= (~HASTED); + } else { + monster->slowed_toggle = 0; + monster->m_flags |= SLOWED; + } + break; + case HASTE_MONSTER: + if (monster->m_flags & SLOWED) { + monster->m_flags &= (~SLOWED); + } else { + monster->m_flags |= HASTED; + } + break; + case TELE_AWAY: + tele_away(monster); + break; + case INVISIBILITY: + monster->m_flags |= INVISIBLE; + break; + case POLYMORPH: + if (monster->m_flags & HOLDS) { + being_held = 0; + } + nm = monster->next_monster; + tc = monster->trail_char; + (void)gr_monster(monster, get_rand(0, MONSTERS-1)); + monster->row = row; + monster->col = col; + monster->next_monster = nm; + monster->trail_char = tc; + if (!(monster->m_flags & IMITATES)) { + wake_up(monster); + } + break; + case MAGIC_MISSILE: + rogue_hit(monster, 1); + break; + case CANCELLATION: + if (monster->m_flags & HOLDS) { + being_held = 0; + } + if (monster->m_flags & STEALS_ITEM) { + monster->drop_percent = 0; + } + monster->m_flags &= (~(FLIES | FLITS | SPECIAL_HIT | INVISIBLE | + FLAMES | IMITATES | CONFUSES | SEEKS_GOLD | HOLDS)); + break; + case DO_NOTHING: + messagef(0, "nothing happens"); + break; + } +} + +static void +tele_away(object *monster) +{ + short row, col; + + if (monster->m_flags & HOLDS) { + being_held = 0; + } + gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); + mvaddch(monster->row, monster->col, monster->trail_char); + dungeon[monster->row][monster->col] &= ~MONSTER; + monster->row = row; monster->col = col; + dungeon[row][col] |= MONSTER; + monster->trail_char = mvinch(row, col); + if (detect_monster || rogue_can_see(row, col)) { + mvaddch(row, col, gmc(monster)); + } +} + +void +wizardize(void) +{ + char buf[100]; + + if (wizard) { + wizard = 0; + messagef(0, "not wizard anymore"); + } else { + if (get_input_line("wizard's password:", "", buf, sizeof(buf), + "", 0, 0)) { + (void)xxx(1); + xxxx(buf, strlen(buf)); + if (!strncmp(buf, "\247\104\126\272\115\243\027", 7)) { + wizard = 1; + score_only = 1; + messagef(0, "Welcome, mighty wizard!"); + } else { + messagef(0, "sorry"); + } + } + } +} + +static void +wdrain_life(object *monster) +{ + short hp; + object *lmon, *nm; + + hp = rogue.hp_current / 3; + rogue.hp_current = (rogue.hp_current + 1) / 2; + + if (cur_room >= 0) { + lmon = level_monsters.next_monster; + while (lmon) { + nm = lmon->next_monster; + if (get_room_number(lmon->row, lmon->col) == cur_room) { + wake_up(lmon); + (void)mon_damage(lmon, hp); + } + lmon = nm; + } + } else { + if (monster) { + wake_up(monster); + (void)mon_damage(monster, hp); + } + } + print_stats(STAT_HP); + relight(); +} + +void +bounce(short ball, short dir, short row, short col, short r) +{ + short orow, ocol; + const char *s; + short i, ch, new_dir = -1, damage; + static short btime; + + if (++r == 1) { + btime = get_rand(3, 6); + } else if (r > btime) { + return; + } + + if (ball == FIRE) { + s = "fire"; + } else { + s = "ice"; + } + if (r > 1) { + messagef(0, "the %s bounces", s); + } + orow = row; + ocol = col; + do { + ch = mvinch(orow, ocol); + standout(); + mvaddch(orow, ocol, ch); + get_dir_rc(dir, &orow, &ocol, 1); + } while (!( (ocol <= 0) || + (ocol >= DCOLS-1) || + (dungeon[orow][ocol] == NOTHING) || + (dungeon[orow][ocol] & MONSTER) || + (dungeon[orow][ocol] & (HORWALL | VERTWALL)) || + ((orow == rogue.row) && (ocol == rogue.col)))); + standend(); + refresh(); + do { + orow = row; + ocol = col; + ch = mvinch(row, col); + mvaddch(row, col, ch); + get_dir_rc(dir, &row, &col, 1); + } while (!( (col <= 0) || + (col >= DCOLS-1) || + (dungeon[row][col] == NOTHING) || + (dungeon[row][col] & MONSTER) || + (dungeon[row][col] & (HORWALL | VERTWALL)) || + ((row == rogue.row) && (col == rogue.col)))); + + if (dungeon[row][col] & MONSTER) { + object *monster; + + monster = object_at(&level_monsters, row, col); + + wake_up(monster); + if (rand_percent(33)) { + messagef(0, "the %s misses the %s", s, + mon_name(monster)); + goto ND; + } + if (ball == FIRE) { + if (!(monster->m_flags & RUSTS)) { + if (monster->m_flags & FREEZES) { + damage = monster->hp_to_kill; + } else if (monster->m_flags & FLAMES) { + damage = (monster->hp_to_kill / 10) + 1; + } else { + damage = get_rand((rogue.hp_current / 3), rogue.hp_max); + } + } else { + damage = (monster->hp_to_kill / 2) + 1; + } + messagef(0, "the %s hits the %s", s, + mon_name(monster)); + (void)mon_damage(monster, damage); + } else { + damage = -1; + if (!(monster->m_flags & FREEZES)) { + if (rand_percent(33)) { + messagef(0, "the monster is frozen"); + monster->m_flags |= (ASLEEP | NAPPING); + monster->nap_length = get_rand(3, 6); + } else { + damage = rogue.hp_current / 4; + } + } else { + damage = -2; + } + if (damage != -1) { + messagef(0, "the %s hits the %s", s, + mon_name(monster)); + (void)mon_damage(monster, damage); + } + } + } else if ((row == rogue.row) && (col == rogue.col)) { + if (rand_percent(10 + (3 * get_armor_class(rogue.armor)))) { + messagef(0, "the %s misses", s); + goto ND; + } else { + damage = get_rand(3, (3 * rogue.exp)); + if (ball == FIRE) { + damage = (damage * 3) / 2; + damage -= get_armor_class(rogue.armor); + } + rogue_damage(damage, NULL, + ((ball == FIRE) ? KFIRE : HYPOTHERMIA)); + messagef(0, "the %s hits", s); + } + } else { + short nrow, ncol; + +ND: for (i = 0; i < 10; i++) { + dir = get_rand(0, DIRS-1); + nrow = orow; + ncol = ocol; + get_dir_rc(dir, &nrow, &ncol, 1); + if (((ncol >= 0) && (ncol <= DCOLS-1)) && + (dungeon[nrow][ncol] != NOTHING) && + (!(dungeon[nrow][ncol] & (VERTWALL | HORWALL)))) { + new_dir = dir; + break; + } + } + if (new_dir != -1) { + bounce(ball, new_dir, orow, ocol, r); + } + } +}