├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc ├── CHANGES ├── a-guide-to-the-dungeons-of-doom.pdf ├── a-guide-to-the-dungeons-of-doom.txt ├── rogue-clone.6 └── screenshot.png ├── hit.c ├── inventory.c ├── level.c ├── machdep.c ├── main.c ├── message.c ├── monster.c ├── move.c ├── object.c ├── pack.c ├── play.c ├── ring.c ├── rogue.h ├── room.c ├── save.c ├── score.c ├── throw.c ├── trap.c ├── use.c └── zap.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | /rogue-clone 3 | /rogue-clone.exe 4 | /wasm 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Many files contains two licenses, a standard 3-clause BSD license and a custom 2 | "no commercial and don't be evil" license, which means it doesn't satisfy the 3 | "four freedoms" or the OSD. 4 | 5 | "No commercial and don't be evil" license: 6 | 7 | This source herein may be modified and/or distributed by anybody who so 8 | desires, with the following restrictions: 9 | 10 | 1.) No portion of this notice shall be removed. 11 | 2.) Credit shall not be taken for the creation of this source. 12 | 3.) This code is not to be traded, sold, or used for personal gain or 13 | profit. 14 | 15 | BSD license: 16 | 17 | Copyright (c) 1988, 1993 18 | The Regents of the University of California. All rights reserved. 19 | 20 | This code is derived from software contributed to Berkeley by 21 | Timothy C. Stoehr. 22 | 23 | Redistribution and use in source and binary forms, with or without 24 | modification, are permitted provided that the following conditions are met: 25 | 26 | 1. Redistributions of source code must retain the above copyright notice, 27 | this list of conditions and the following disclaimer. 28 | 2. Redistributions in binary form must reproduce the above copyright notice, 29 | this list of conditions and the following disclaimer in the documentation 30 | and/or other materials provided with the distribution. 31 | 3. Neither the name of the University nor the names of its contributors may 32 | be used to endorse or promote products derived from this software without 33 | specific prior written permission. 34 | 35 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 36 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 37 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 38 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 39 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 40 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 41 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 42 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 43 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 44 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 45 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= cc 2 | PREFIX ?= /usr/local 3 | MANPREFIX ?= ${PREFIX}/share/man 4 | NAME ?= rogue-clone 5 | STATIC ?= -static 6 | CFLAGS += -std=c99 -g -Wall -pedantic -D_DEFAULT_SOURCE 7 | LDFLAGS += -lcurses ${STATIC} 8 | 9 | .PHONY: all clean install uninstall 10 | 11 | SRC = hit.c inventory.c level.c machdep.c main.c message.c monster.c move.c \ 12 | object.c pack.c play.c ring.c room.c save.c score.c throw.c trap.c use.c zap.c 13 | OBJ = ${SRC:.c=.o} 14 | 15 | all: rogue-clone 16 | 17 | ${OBJ}: rogue.h Makefile 18 | .c.o: 19 | ${CC} -c ${CFLAGS} $< 20 | rogue-clone: ${OBJ} 21 | ${CC} -o $@ *.o ${LDFLAGS} 22 | 23 | clean: 24 | rm -f rogue-clone *.o 25 | install: 26 | install -Dm755 rogue-clone ${DESTDIR}${PREFIX}/bin/${NAME} 27 | install -Dm644 doc/rogue-clone.6 ${DESTDIR}${MANPREFIX}/man6/${NAME}.6 28 | uninstall: 29 | rm -f ${DESTDIR}${PREFIX}/bin/${NAME} ${DESTDIR}${MANPREFIX}/man6/${NAME}.6 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The "rogue" game from 4.3BSD-Tahoe, for modern systems. 2 | 3 | ![screenshot.png](https://raw.githubusercontent.com/arp242/rogue-clone/master/doc/screenshot.png) 4 | 5 | There are many versions and variants of the original rogue game; the original 6 | was shipped with 4.2BSD in 1980, and throughout the 80s the original authors 7 | released different versions, and others made their own variants based on 8 | "leaked" source code. 9 | 10 | This one is a "clone" added to 4.3BSD-Tahoe in 1987 and was written by Timothy 11 | C. Stoehr; it doesn't re-use any code from the original but was intended as a 12 | "freely distributable open source" re-implementation (for a while it was named 13 | "pdrogue" for "public domain rogue", but got renamed to "rogue" a bit later on). 14 | 15 | This is also the "rogue" game that is shipped with NetBSD, DragonFly BSD, 16 | FreeBSD <5, and OpenBSD <3.3, and that you can find in "bsdgames" packages on 17 | some Linux systems. 18 | 19 | Since 1987 the meaning of what many people understand as "freely distributable" 20 | and "open source" has changed, and the "don't be evil and don't use for 21 | commercial purposes"-type [license] makes it "non-free", which is why e.g. 22 | Debian ships it as "bsdgames-nonfree". I have no idea how to contact "Timothy C. 23 | Stoehr" to ask for an updated license (a basic internet search doesn't show up 24 | anything), so it is what it is. If you know how to contact him: please let me 25 | know. 26 | 27 | There are some differences with the original rogue; the [CHANGES] file lists 28 | some of them, but there are some more (e.g. different monsters). [The roguelike 29 | archive] has links for other variants, and [Wikipedia] has an overview of the 30 | history. 31 | 32 | This game is much simpler than more modern roguelikes such as Angband, Dungeon 33 | Crawl, and many others; there are no races, classes, too many attributes, or 34 | many of the other things. This is a feature! Sometimes you just want to play a 35 | simple game. That said, I've never actually finished the game, so while fairly 36 | simple it's not an easy game. 37 | 38 | [license]: LICENSE 39 | [CHANGES]: doc/CHANGES 40 | [The roguelike archive]: https://britzl.github.io/roguearchive/ 41 | [Wikipedia]: https://en.wikipedia.org/wiki/Rogue_(video_game) 42 | 43 | Building 44 | -------- 45 | To compile, simply using `make` should work on most systems; you will need some 46 | curses (e.g. ncurses or PDCurses). The default is to build a statically linked 47 | binary; use `make STATIC=` to build a dynamically linked one. 48 | 49 | Run `./rogue-clone` to play the game; you only need this binary – there are no 50 | data files. You can use `make install` or `make install PREFIX=/usr` to install 51 | it system-wide. 52 | 53 | It should work on all Unix-y systems (including macOS) and Windows (with 54 | PDCurses). 55 | 56 | Usage 57 | ----- 58 | Use `?` to get a list of keybinds and `/` to get the meaning of a character on 59 | the screen. 60 | 61 | The original *A Guide to the Dungeons of Doom* manual is also available as [PDF] 62 | or [text]; most of it still applies, aside from a few details. 63 | 64 | [PDF]: doc/a-guide-to-the-dungeons-of-doom.pdf 65 | [text]: doc/a-guide-to-the-dungeons-of-doom.txt 66 | 67 | Probably the most confusing for most will be the movement keys; it uses Vim-like 68 | hjkl keys, and with some extra ones for diagonal movement: 69 | 70 | y k u 71 | ↖ ↑ ↗ 72 | h ← → l 73 | ↙ ↓ ↘ 74 | b j n 75 | 76 | So if you had mastered hjkl in Vim then here's a new challenge. You do get used 77 | to it; I haven't played this in over 10 years, but I can still do these 78 | movements without difficulty from muscle memory (which probably says something 79 | sad about my youth...) 80 | 81 | However, to make things a bit easier for the p0sers it also supports arrow keys 82 | and the numpad: 83 | 84 | 7 8 9 85 | ↖ ↑ ↗ 86 | 4 ← → 6 87 | ↙ ↓ ↘ 88 | 1 2 3 89 | 90 | You can set options with `o`; these are *not* saved, but you can use the 91 | `ROGUE_CLONE_OPTS` environment variable, which takes a comma separated list of 92 | options: 93 | 94 | | Name | Default | Description | 95 | | ---- | ------- | ----------- | 96 | | file | ~/.local/share/rogue-clone/save | Save file | 97 | | name= | $USER | Character name; detaults to user name; use `*` for a random name | 98 | | fruit= | slime-mold | Name of the "fruit" food | 99 | | lowhealth= | 0 | Print health in standout if HP is lower than this number | 100 | | noaskquit | (not set) | Don't ask confirmation on SIGQUIT (`^\`, no effect for Q) | 101 | | noskull, notomb | (not set) | Don't display headstone after death | 102 | | passgo | (not set) | Run around corners with Shift | 103 | | lock | (not set) | Don't allow changing name or save path in `o` menu | 104 | | noautosave | (not set) | Don't autosave every 120 moves and on level change | 105 | | revshift | (not set) | Reverse shift+dir/ctrl+dir | 106 | 107 | e.g. `export ROGUE_CLONE_OPTS='name=*,lowhealth=5,passgo'`. 108 | 109 | Changes from the original "rogue clone" 110 | --------------------------------------- 111 | The core game is unchanged, with most changes being related to the UI and 112 | contemporary usage patterns: 113 | 114 | - Scores are stored in `~/.local/share/rogue-clone/score` instead of 115 | `/var/games/rogue/scores`. It also stores all runs for all users, instead of 116 | just the top run for every user, and it uses the "name" set in the game 117 | instead of the user's login name. 118 | 119 | The original assumed there were many people playing rogue on a single system 120 | (and used setgid to make the score file writeable), but this is a rare use 121 | case now and this avoids having to create system directories. 122 | 123 | - The default save file is now `~/.local/share/rogue-clone/save` instead of 124 | `./rogue.dump` in the current directory, and this will be loaded automatically 125 | on startup if it exists so you don't need to use load it manually with `rogue 126 | ~/.local/share/rogue-clone/save`. 127 | 128 | - `!` prints game seed, and you can set the game seed on startup with -S. 129 | 130 | In the original `!` would drop back to shell; this is removed as it doesn't 131 | work on Windows or WASM and it's pretty useless in modern context anyway where 132 | you can just open a new xterm or use tmux or whatever. 133 | 134 | - Support arrow keys and numpad for movement. Also add `m` and `^M` to run (as 135 | an alternative to `J`/`^j` etc.) 136 | 137 | - To repeat `.` and `s` you now need to use `Cs`, instead of `s` – 138 | this was needed to add numpad support. 139 | 140 | - Rename `ROGUEOPTS` to `ROGUE_CLONE_OPTS`; this way it won't conflict with 141 | other rogue variants. 142 | 143 | - Store name in savegame. 144 | 145 | - name=* selects a random name from a built-in list. 146 | 147 | - Add `lowhealth` option to make health stand out if it's low. 148 | 149 | - Add `lock` option to disallow changing the username and savegame location (the 150 | main use case for this is offering a ssh service to play the game, but I 151 | haven't gotten around to that yet). 152 | 153 | - Automatically open inventory instead of having to press `*`. 154 | 155 | - Some more feedback in prompts and when repeating things. 156 | 157 | - Also allow Esc and Enter to clear "--press space to continue--" prompts. 158 | 159 | - Add `noautosave` option; the default is to autosave every 120 moves and before 160 | changing levels; you can set this to disable it. 161 | 162 | - Add `revshift` option to reverse Shift+dir and Ctrl+dir movements; many more 163 | modern roguelike games use Shift+dir to mean what's Ctrl+dir. 164 | 165 | Sources 166 | ------- 167 | Source files for rogue; most are pretty much the same as far as I could see: 168 | 169 | - [4.3BSD-Tahoe](https://github.com/weiss/original-bsd/commit/e376515dbfe31966ba0d288786a8073ff710ac2f) (first version) 170 | - [DragonFly BSD](https://gitweb.dragonflybsd.org/dragonfly.git/tree/HEAD:/games/rogue) 171 | - [NetBSD](https://github.com/NetBSD/src/tree/trunk/games/rogue) 172 | - [OpenBSD](https://github.com/openbsd/src/tree/b8d5a5fb3cd18b5becb179d749e65fc04a659093/games/rogue) (Removed in 3.3) 173 | - [FreeBSD](https://github.com/freebsd/freebsd-src/tree/e05f78b8316cc4c48131cbc9093827a26f204680/games/rogue) 174 | (Removed in 5). 175 | - https://www.freshports.org/games/bsdgames/ (the "BSD games" were moved to a 176 | port in FreeBSD 5; I used this as a starting point) 177 | -------------------------------------------------------------------------------- /doc/CHANGES: -------------------------------------------------------------------------------- 1 | From: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU 2 | Date: 30 Nov 87 15:08:15 PST (Mon) 3 | To: okeeffe.Berkeley.EDU!mckusick@ucbvax.Berkeley.EDU (Kirk McKusick) 4 | Subject: Re: Public domain rogue 5 | Return-Path: tektronix!zeus.TEK.COM!tims@ucbvax.Berkeley.EDU 6 | 7 | Here is a list of discrepencies from the documentation you sent me: 8 | 9 | The -d option not implemented. 10 | The -r option not implemented, use "rogue save_file" instead. 11 | Strength is between 1 and 99, not 3 and 32. 12 | The D command is not implemented. 13 | Only scrolls,potions,wands,and rings may be "call"ed something. 14 | The ^P command may be used to go 4 messages back, instead of just 1. 15 | The @ comand is not implemented. 16 | There are no dark rooms. 17 | ROGUEOPTS of flush,terse,seefloor,askme,inventory are ignored. 18 | 'askquit' is added to prevent ^\ from terminating the game accidentally. 19 | If 'noaskquit' is 20 | found in the ROGUEOPTS string, the ^\ kills the game, otherwise, 21 | the player is asked if he really wants to quit. In either case, no 22 | score file processing is attempted. 23 | The score is keyed to winning scores, and no player may appear twice. 24 | 25 | 26 | 27 | 28 | 29 | 30 | Other differences from "standard" rogue 5.3. This list covers externally 31 | visible differences only. 32 | 33 | There should be NO bugs with any severe consequences. Absolutely NO 34 | game-stopping, or game-winning bugs should be present. 35 | Traps fail occasionally, that is, they sometimes are sprung but miss. 36 | The ^A command prints out some stuff you're probably not interested in. 37 | The '&' command silently saves your screen into the file 'rogue.screen' 38 | Any inventory selection command that takes '*' as a request to list all 39 | appropriate items, can take one of "=?:)]!/" to list only rings, 40 | scrolls, or whatever. 41 | Scrolls and potions, once used, become identified. All other objects become 42 | identified only by scroll of identification. 43 | There is only one scroll of identification, and it works on any item. 44 | ROGUEOPTS 45 | Only the following are implemented: 46 | file,jump,name,askquit,tombstone,passgo 47 | "askquit" is used to prevent accidental termination of the game via ^\ 48 | You may drop objects in doorways. 49 | Prints a picture of a skull, not a tombstone, upon death. 50 | The save/restore game function is faster and machine-independent, but sometimes 51 | requires modification when new variables are added to the source. 52 | The potion of detect monster lasts for the whole level. 53 | Their is no wand of light. 54 | -------------------------------------------------------------------------------- /doc/a-guide-to-the-dungeons-of-doom.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arp242/rogue-clone/72ccfe9a3689df8c9639285dccaa329d934baccf/doc/a-guide-to-the-dungeons-of-doom.pdf -------------------------------------------------------------------------------- /doc/rogue-clone.6: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1988, 1993 2 | .\" The Regents of the University of California. All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 3. Neither the name of the University nor the names of its contributors 13 | .\" may be used to endorse or promote products derived from this software 14 | .\" without specific prior written permission. 15 | .\" 16 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | .\" SUCH DAMAGE. 27 | .\" 28 | .\" @(#)rogue.6 8.1 (Berkeley) 5/31/93 29 | .\" $FreeBSD: src/games/rogue/rogue.6,v 1.4.2.3 2001/12/14 14:26:32 ru Exp $ 30 | .\" $DragonFly: src/games/rogue/rogue.6,v 1.3 2006/02/17 19:33:31 swildner Exp $ 31 | .\" 32 | .Dd June 19, 2023 33 | .Dt ROGUE-CLONE 6 34 | .Os 35 | .Sh NAME 36 | .Nm rogue-clone 37 | .Nd exploring The Dungeons of Doom 38 | .Sh SYNOPSIS 39 | .Nm 40 | .Op Ar save_file 41 | .Op Ar -S seed 42 | .Op Fl s 43 | .Sh DESCRIPTION 44 | The 45 | .Nm 46 | is a computer fantasy with a new twist. 47 | It is CRT oriented and the object of the game is to survive 48 | the attacks of various monsters and gather a lot of gold, rather 49 | than the puzzle solving orientation of most computer fantasy games. 50 | .Pp 51 | To get started you really only need to know two commands. 52 | The command 53 | .Ic ?\& 54 | will give you a list of the available commands and the command 55 | .Ic / 56 | will identify the things you see on the screen. 57 | .Pp 58 | To win the game (as opposed to merely playing to beat other people's high 59 | scores) you must locate the 60 | .Em Amulet of Yendor 61 | which is somewhere below the 20th level of the dungeon and get it out. 62 | Nobody has achieved this 63 | yet and if somebody does, they will probably go down in history as a hero 64 | among heroes. 65 | .Pp 66 | When the game ends, either by your death, when you quit, or if you (by 67 | some miracle) manage to win, 68 | .Nm 69 | will give you a list of the top-ten scorers. 70 | The scoring is based entirely upon how much gold you get. 71 | There is a 10% penalty for getting yourself killed. 72 | .Pp 73 | If the savefile at the default path exists it will be loaded automatically, or if 74 | .Ar save_file 75 | is specified, 76 | .Nm 77 | will be restored from the specified saved game file. 78 | .Pp 79 | The 80 | .Fl s 81 | option will print out the list of scores. 82 | .Pp 83 | The 84 | .Fl S 85 | option can be used to set a fixed randomness seed. 86 | For more detailed directions, read the document 87 | .Rs 88 | .%B "A Guide to the Dungeons of Doom" 89 | .Re 90 | .Sh FILES 91 | .Bl -tag -width ~/.local/share/rogue-clone/scoreXX -compact 92 | .It Pa ~/.local/share/rogue-clone/score 93 | score file 94 | .It Pa ~/.local/share/rogue-clone/save 95 | default save file 96 | .El 97 | .Sh SEE ALSO 98 | .Rs 99 | .%A "Michael C. Toy" 100 | .%A "Kenneth C. R. C. Arnold" 101 | .%B "4.4BSD Users's Supplementary Documents (USD)" 102 | .%T "A Guide to the Dungeons of Doom" 103 | .\" .%O (see /usr/share/doc/usd/30.rogue/) 104 | .Re 105 | .Sh AUTHORS 106 | .An Timothy Stoehr , 107 | .An Michael C. Toy , 108 | .An Kenneth C. R. C. Arnold , 109 | .An Glenn Wichman 110 | .Sh BUGS 111 | Probably infinite, although none are known. 112 | However, that Ice Monsters sometimes transfix you permanently is 113 | .Em not 114 | a bug. 115 | It's a feature. 116 | -------------------------------------------------------------------------------- /doc/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arp242/rogue-clone/72ccfe9a3689df8c9639285dccaa329d934baccf/doc/screenshot.png -------------------------------------------------------------------------------- /hit.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)hit.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/hit.c,v 1.6 1999/11/30 03:49:22 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | static short damage_for_strength(void); 45 | static int get_w_damage(const object *); 46 | static int to_hit(const object *); 47 | 48 | static object *fight_monster = NULL; 49 | char hit_message[HIT_MESSAGE_SIZE] = ""; 50 | 51 | static void disappear(object *); 52 | static void drain_life(void); 53 | static void drop_level(void); 54 | static void freeze(object *); 55 | static short get_dir(short, short, short, short); 56 | static bool gold_at(short, short); 57 | static void steal_gold(object *); 58 | static void steal_item(object *); 59 | static void sting(object *); 60 | static bool try_to_cough(short, short, object *); 61 | 62 | short less_hp = 0; 63 | bool being_held; 64 | 65 | void mon_hit(object *monster) { 66 | if (fight_monster && monster != fight_monster) 67 | fight_monster = NULL; 68 | 69 | short hit_chance; 70 | 71 | monster->trow = NO_ROOM; 72 | if (cur_level >= (AMULET_LEVEL * 2)) 73 | hit_chance = 100; 74 | else { 75 | hit_chance = monster->m_hit_chance - (((2 * rogue.exp) + (2 * ring_exp)) - r_rings); 76 | } 77 | if (wizard) 78 | hit_chance /= 2; 79 | 80 | if (!fight_monster) 81 | interrupted = 1; 82 | 83 | const char *mn = mon_name(monster); 84 | if (!rand_percent(hit_chance)) { 85 | if (!fight_monster) { 86 | sprintf(hit_message + strlen(hit_message), "the %s misses", mn); 87 | message(hit_message, 1); 88 | hit_message[0] = 0; 89 | } 90 | return; 91 | } 92 | if (!fight_monster) { 93 | sprintf(hit_message + strlen(hit_message), "the %s hit", mn); 94 | message(hit_message, 1); 95 | hit_message[0] = 0; 96 | } 97 | 98 | short damage; 99 | if (!(monster->m_flags & STATIONARY)) { 100 | damage = get_damage(monster->m_damage, 1); 101 | float minus; 102 | if (cur_level >= (AMULET_LEVEL * 2)) 103 | minus = (float)((AMULET_LEVEL * 2) - cur_level); 104 | else { 105 | minus = (float)get_armor_class(rogue.armor) * 3.00; 106 | minus = minus / 100.00 * (float)damage; 107 | } 108 | damage -= (short)minus; 109 | } else 110 | damage = monster->stationary_damage++; 111 | 112 | if (wizard) 113 | damage /= 3; 114 | if (damage > 0) 115 | rogue_damage(damage, monster, 0); 116 | if (monster->m_flags & SPECIAL_HIT) 117 | special_hit(monster); 118 | } 119 | 120 | void rogue_hit(object *monster, bool force_hit) { 121 | if (!monster) 122 | return; 123 | if (check_imitator(monster)) 124 | return; 125 | 126 | short hit_chance = force_hit ? 100 : get_hit_chance(rogue.weapon); 127 | if (wizard) 128 | hit_chance *= 2; 129 | if (!rand_percent(hit_chance)) { 130 | if (!fight_monster) 131 | strcpy(hit_message, "you miss "); 132 | goto RET; 133 | } 134 | short damage = get_weapon_damage(rogue.weapon); 135 | if (wizard) 136 | damage *= 3; 137 | if (con_mon) 138 | s_con_mon(monster); 139 | if (mon_damage(monster, damage)) { // still alive? 140 | if (!fight_monster) 141 | strcpy(hit_message, "you hit "); 142 | } 143 | 144 | RET: 145 | check_gold_seeker(monster); 146 | wake_up(monster); 147 | } 148 | 149 | void rogue_damage(short d, object *monster, short other) { 150 | if (d >= rogue.hp_current) { 151 | rogue.hp_current = 0; 152 | print_stats(STAT_HP); 153 | killed_by(monster, other); 154 | } 155 | if (d > 0) { 156 | rogue.hp_current -= d; 157 | print_stats(STAT_HP); 158 | } 159 | } 160 | 161 | int get_damage(const char *ds, bool r) { 162 | int i = 0, total = 0; 163 | while (ds[i]) { 164 | int n = get_number(ds+i); 165 | while (ds[i++] != 'd') 166 | ; 167 | int d = get_number(ds+i); 168 | while ((ds[i] != '/') && ds[i]) 169 | i++; 170 | 171 | for (int j = 0; j < n; j++) { 172 | if (r) 173 | total += get_rand(1, d); 174 | else 175 | total += d; 176 | } 177 | if (ds[i] == '/') 178 | i++; 179 | } 180 | return total; 181 | } 182 | 183 | static int get_w_damage(const object *obj) { 184 | if ((!obj) || (obj->what_is != WEAPON)) 185 | return -1; 186 | 187 | int t_hit = get_number(obj->damage) + obj->hit_enchant; 188 | int i = 0; 189 | while (obj->damage[i++] != 'd') 190 | ; 191 | int damage = get_number(obj->damage + i) + obj->d_enchant; 192 | 193 | char new_damage[12]; 194 | sprintf(new_damage, "%dd%d", t_hit, damage); 195 | return get_damage(new_damage, 1); 196 | } 197 | 198 | int get_number(const char *s) { 199 | int i = 0, total = 0; 200 | while (s[i] >= '0' && s[i] <= '9') { 201 | total = (10 * total) + (s[i] - '0'); 202 | i++; 203 | } 204 | return total; 205 | } 206 | 207 | long lget_number(const char *s) { 208 | short i = 0; 209 | long total = 0; 210 | while (s[i] >= '0' && s[i] <= '9') { 211 | total = (10 * total) + (s[i] - '0'); 212 | i++; 213 | } 214 | return total; 215 | } 216 | 217 | unsigned long ulget_number(const char *s) { 218 | short i = 0; 219 | unsigned long total = 0; 220 | while (s[i] >= '0' && s[i] <= '9') { 221 | total = (10 * total) + (s[i] - '0'); 222 | i++; 223 | } 224 | return total; 225 | } 226 | 227 | static int to_hit(const object *obj) { 228 | if (!obj) 229 | return 1; 230 | return get_number(obj->damage) + obj->hit_enchant; 231 | } 232 | 233 | static short damage_for_strength(void) { 234 | short strength = rogue.str_current + add_strength; 235 | if (strength <= 6) return strength - 5; 236 | if (strength <= 14) return 1; 237 | if (strength <= 17) return 3; 238 | if (strength <= 18) return 4; 239 | if (strength <= 20) return 5; 240 | if (strength <= 21) return 6; 241 | if (strength <= 30) return 7; 242 | return 8; 243 | } 244 | 245 | bool mon_damage(object *monster, short damage) { 246 | monster->hp_to_kill -= damage; 247 | if (monster->hp_to_kill <= 0) { 248 | short row = monster->row; 249 | short col = monster->col; 250 | dungeon[row][col] &= ~MONSTER; 251 | mvaddch(row, col, (int)get_dungeon_char(row, col)); 252 | 253 | fight_monster = NULL; 254 | cough_up(monster); 255 | const char *mn = mon_name(monster); 256 | sprintf(hit_message + strlen(hit_message), "defeated the %s", mn); 257 | message(hit_message, 1); 258 | hit_message[0] = 0; 259 | add_exp(monster->kill_exp, 1); 260 | take_from_pack(monster, &level_monsters); 261 | 262 | if (monster->m_flags & HOLDS) 263 | being_held = 0; 264 | free_object(monster); 265 | return 0; 266 | } 267 | return 1; 268 | } 269 | 270 | void fight(bool to_the_death) { 271 | message("fight direction? ", 0); 272 | 273 | int ch, d; 274 | bool first_miss = 1; 275 | while (!is_direction(ch = rgetchar(), &d)) { 276 | sound_bell(); 277 | if (first_miss) { 278 | message("direction? ", 0); 279 | first_miss = 0; 280 | } 281 | } 282 | check_message(); 283 | if (ch == CANCEL) 284 | return; 285 | short row = rogue.row; 286 | short col = rogue.col; 287 | get_dir_rc(d, &row, &col, 0); 288 | 289 | short c = (short)mvinch(row, col); 290 | if (c < 'A' || c > 'Z' || !can_move(rogue.row, rogue.col, row, col)) { 291 | message("I see no monster there", 0); 292 | return; 293 | } 294 | if (!(fight_monster = object_at(&level_monsters, row, col))) 295 | return; 296 | 297 | short possible_damage; 298 | if (!(fight_monster->m_flags & STATIONARY)) 299 | possible_damage = ((get_damage(fight_monster->m_damage, 0) * 2) / 3); 300 | else 301 | possible_damage = fight_monster->stationary_damage - 1; 302 | 303 | while (fight_monster) { 304 | one_move_rogue(ch, 0); 305 | if ((!to_the_death && rogue.hp_current <= possible_damage) || interrupted || (!(dungeon[row][col] & MONSTER))) 306 | fight_monster = NULL; 307 | else { 308 | object *monster = object_at(&level_monsters, row, col); 309 | if (monster != fight_monster) 310 | fight_monster = NULL; 311 | } 312 | } 313 | } 314 | 315 | void get_dir_rc(short dir, short *row, short *col, short allow_off_screen) { 316 | switch (dir) { 317 | case LEFT: 318 | if (allow_off_screen || (*col > 0)) 319 | (*col)--; 320 | break; 321 | case DOWN: 322 | if (allow_off_screen || (*row < (DROWS - 2))) 323 | (*row)++; 324 | break; 325 | case UPWARD: 326 | if (allow_off_screen || (*row > MIN_ROW)) 327 | (*row)--; 328 | break; 329 | case RIGHT: 330 | if (allow_off_screen || (*col < (DCOLS - 1))) 331 | (*col)++; 332 | break; 333 | case UPLEFT: 334 | if (allow_off_screen || ((*row > MIN_ROW) && (*col > 0))) { 335 | (*row)--; 336 | (*col)--; 337 | } 338 | break; 339 | case UPRIGHT: 340 | if (allow_off_screen || ((*row > MIN_ROW) && (*col < (DCOLS - 1)))) { 341 | (*row)--; 342 | (*col)++; 343 | } 344 | break; 345 | case DOWNRIGHT: 346 | if (allow_off_screen || ((*row < (DROWS - 2)) && (*col < (DCOLS - 1)))) { 347 | (*row)++; 348 | (*col)++; 349 | } 350 | break; 351 | case DOWNLEFT: 352 | if (allow_off_screen || ((*row < (DROWS - 2)) && (*col > 0))) { 353 | (*row)++; 354 | (*col)--; 355 | } 356 | break; 357 | } 358 | } 359 | 360 | short get_hit_chance(const object *weapon) { 361 | return 40 + (3 * to_hit(weapon)) + ((2*rogue.exp + 2*ring_exp) - r_rings); 362 | } 363 | 364 | short get_weapon_damage(const object *weapon) { 365 | return get_w_damage(weapon) + damage_for_strength() + 366 | ((((rogue.exp + ring_exp) - r_rings) + 1) / 2); 367 | } 368 | 369 | void s_con_mon(object *monster) { 370 | if (con_mon) { 371 | monster->m_flags |= CONFUSED; 372 | monster->moves_confused += get_rand(12, 22); 373 | message("the monster appears confused", 0); 374 | con_mon = 0; 375 | } 376 | } 377 | 378 | void special_hit(object *monster) { 379 | if ((monster->m_flags & CONFUSED) && rand_percent(66)) 380 | return; 381 | 382 | if (monster->m_flags & RUSTS) 383 | rust(monster); 384 | if ((monster->m_flags & HOLDS) && !levitate) 385 | being_held = 1; 386 | if (monster->m_flags & FREEZES) 387 | freeze(monster); 388 | if (monster->m_flags & STINGS) 389 | sting(monster); 390 | if (monster->m_flags & DRAINS_LIFE) 391 | drain_life(); 392 | if (monster->m_flags & DROPS_LEVEL) 393 | drop_level(); 394 | 395 | if (monster->m_flags & STEALS_GOLD) 396 | steal_gold(monster); 397 | else if (monster->m_flags & STEALS_ITEM) 398 | steal_item(monster); 399 | } 400 | 401 | void rust(object *monster) { 402 | if ((!rogue.armor) || (get_armor_class(rogue.armor) <= 1) || 403 | (rogue.armor->which_kind == LEATHER)) { 404 | return; 405 | } 406 | if ((rogue.armor->is_protected) || maintain_armor) { 407 | if (monster && (!(monster->m_flags & RUST_VANISHED))) { 408 | message("the rust vanishes instantly", 0); 409 | monster->m_flags |= RUST_VANISHED; 410 | } 411 | } else { 412 | rogue.armor->d_enchant--; 413 | message("your armor weakens", 0); 414 | print_stats(STAT_ARMOR); 415 | } 416 | } 417 | 418 | static void freeze(object *monster) { 419 | if (rand_percent(12)) 420 | return; 421 | 422 | short freeze_percent = 99 - 423 | (rogue.str_current + rogue.str_current/2) - 424 | ((rogue.exp + ring_exp) * 4) - 425 | (get_armor_class(rogue.armor) * 5) - 426 | (rogue.hp_max / 3); 427 | if (freeze_percent > 10) { 428 | monster->m_flags |= FREEZING_ROGUE; 429 | message("you are frozen", 1); 430 | 431 | short n = get_rand(4, 8); 432 | for (short i = 0; i < n; i++) 433 | mv_mons(); 434 | if (rand_percent(freeze_percent)) { 435 | for (short i = 0; i < 50; i++) 436 | mv_mons(); 437 | killed_by(NULL, HYPOTHERMIA); 438 | } 439 | message(you_can_move_again, 1); 440 | monster->m_flags &= (~FREEZING_ROGUE); 441 | } 442 | } 443 | 444 | static void steal_gold(object *monster) { 445 | if ((rogue.gold <= 0) || rand_percent(10)) 446 | return; 447 | 448 | int amount = get_rand((cur_level * 10), (cur_level * 30)); 449 | if (amount > rogue.gold) 450 | amount = rogue.gold; 451 | rogue.gold -= amount; 452 | message("your purse feels lighter", 0); 453 | print_stats(STAT_GOLD); 454 | disappear(monster); 455 | } 456 | 457 | static void steal_item(object *monster) { 458 | if (rand_percent(15)) 459 | return; 460 | 461 | object *obj = rogue.pack.next_object; 462 | if (!obj) 463 | goto DSPR; 464 | 465 | bool has_something = 0; 466 | while (obj) { 467 | if (!(obj->in_use_flags & BEING_USED)) { 468 | has_something = 1; 469 | break; 470 | } 471 | obj = obj->next_object; 472 | } 473 | if (!has_something) 474 | goto DSPR; 475 | 476 | short n = get_rand(0, MAX_PACK_COUNT); 477 | obj = rogue.pack.next_object; 478 | for (short i = 0; i <= n; i++) { 479 | obj = obj->next_object; 480 | while (!obj || (obj->in_use_flags & BEING_USED)) { 481 | if (!obj) 482 | obj = rogue.pack.next_object; 483 | else 484 | obj = obj->next_object; 485 | } 486 | } 487 | 488 | short t = 0; 489 | if (obj->what_is != WEAPON) { 490 | t = obj->quantity; 491 | obj->quantity = 1; 492 | } 493 | messagef(0, "she stole %s", get_desc_str(obj)); 494 | 495 | obj->quantity = ((obj->what_is != WEAPON) ? t : 1); 496 | 497 | vanish(obj, 0, &rogue.pack); 498 | DSPR: 499 | disappear(monster); 500 | } 501 | 502 | static void disappear(object *monster) { 503 | short row = monster->row; 504 | short col = monster->col; 505 | 506 | dungeon[row][col] &= ~MONSTER; 507 | if (rogue_can_see(row, col)) 508 | mvaddch(row, col, get_dungeon_char(row, col)); 509 | take_from_pack(monster, &level_monsters); 510 | free_object(monster); 511 | mon_disappeared = 1; 512 | } 513 | 514 | void cough_up(object *monster) { 515 | if (cur_level < max_level) 516 | return; 517 | 518 | object *obj; 519 | if (monster->m_flags & STEALS_GOLD) { 520 | obj = alloc_object(); 521 | obj->what_is = GOLD; 522 | obj->quantity = get_rand((cur_level * 15), (cur_level * 30)); 523 | } else { 524 | if (!rand_percent((int)monster->drop_percent)) 525 | return; 526 | obj = gr_object(); 527 | } 528 | short row = monster->row; 529 | short col = monster->col; 530 | 531 | for (short n = 0; n <= 5; n++) { 532 | for (short i = -n; i <= n; i++) { 533 | if (try_to_cough(row + n, col + i, obj)) 534 | return; 535 | if (try_to_cough(row - n, col + i, obj)) 536 | return; 537 | } 538 | for (short i = -n; i <= n; i++) { 539 | if (try_to_cough(row + i, col - n, obj)) 540 | return; 541 | if (try_to_cough(row + i, col + n, obj)) 542 | return; 543 | } 544 | } 545 | free_object(obj); 546 | } 547 | 548 | static bool try_to_cough(short row, short col, object *obj) { 549 | if (row < MIN_ROW || row > (DROWS - 2) || col < 0 || col > (DCOLS - 1)) 550 | return 0; 551 | 552 | if ((!(dungeon[row][col] & (OBJECT | STAIRS | TRAP))) && (dungeon[row][col] & (TUNNEL | FLOOR | DOOR))) { 553 | place_at(obj, row, col); 554 | if (((row != rogue.row) || (col != rogue.col)) && (!(dungeon[row][col] & MONSTER))) 555 | mvaddch(row, col, get_dungeon_char(row, col)); 556 | return 1; 557 | } 558 | return 0; 559 | } 560 | 561 | bool seek_gold(object *monster) { 562 | short rn = get_room_number(monster->row, monster->col); 563 | if (rn < 0) 564 | return 0; 565 | 566 | for (short i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) { 567 | for (short j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) { 568 | if (gold_at(i, j) && !(dungeon[i][j] & MONSTER)) { 569 | monster->m_flags |= CAN_FLIT; 570 | short s = mon_can_go(monster, i, j); 571 | monster->m_flags &= (~CAN_FLIT); 572 | if (s) { 573 | move_mon_to(monster, i, j); 574 | monster->m_flags |= ASLEEP; 575 | monster->m_flags &= (~(WAKENS | SEEKS_GOLD)); 576 | return 1; 577 | } 578 | monster->m_flags &= (~SEEKS_GOLD); 579 | monster->m_flags |= CAN_FLIT; 580 | mv_1_monster(monster, i, j); 581 | monster->m_flags &= (~CAN_FLIT); 582 | monster->m_flags |= SEEKS_GOLD; 583 | return 1; 584 | } 585 | } 586 | } 587 | return 0; 588 | } 589 | 590 | static bool gold_at(short row, short col) { 591 | if (dungeon[row][col] & OBJECT) { 592 | object *obj; 593 | if ((obj = object_at(&level_objects, row, col)) && (obj->what_is == GOLD)) 594 | return 1; 595 | } 596 | return 0; 597 | } 598 | 599 | void check_gold_seeker(object *monster) { 600 | monster->m_flags &= (~SEEKS_GOLD); 601 | } 602 | 603 | bool check_imitator(object *monster) { 604 | if (monster->m_flags & IMITATES) { 605 | wake_up(monster); 606 | if (!blind) { 607 | mvaddch(monster->row, monster->col, get_dungeon_char(monster->row, monster->col)); 608 | check_message(); 609 | messagef(0, "wait, that's a %s!", mon_name(monster)); 610 | } 611 | return 1; 612 | } 613 | return 0; 614 | } 615 | 616 | bool imitating(short row, short col) { 617 | if (dungeon[row][col] & MONSTER) { 618 | object *monster; 619 | if ((monster = object_at(&level_monsters, row, col)) != NULL) { 620 | if (monster->m_flags & IMITATES) 621 | return 1; 622 | } 623 | } 624 | return 0; 625 | } 626 | 627 | static void sting(object *monster) { 628 | if ((rogue.str_current <= 3) || sustain_strength) 629 | return; 630 | 631 | short sting_chance = 35 + (6 * (6 - get_armor_class(rogue.armor))); 632 | if ((rogue.exp + ring_exp) > 8) 633 | sting_chance -= (6 * ((rogue.exp + ring_exp) - 8)); 634 | 635 | if (rand_percent(sting_chance)) { 636 | messagef(0, "the %s's bite has weakened you", mon_name(monster)); 637 | rogue.str_current--; 638 | print_stats(STAT_STRENGTH); 639 | } 640 | } 641 | 642 | static void drop_level(void) { 643 | if (rand_percent(80) || (rogue.exp <= 5)) 644 | return; 645 | 646 | rogue.exp_points = level_points[rogue.exp-2] - get_rand(9, 29); 647 | rogue.exp -= 2; 648 | int hp = hp_raise(); 649 | if (rogue.hp_current -= hp <= 0) 650 | rogue.hp_current = 1; 651 | if (rogue.hp_max -= hp <= 0) 652 | rogue.hp_max = 1; 653 | add_exp(1, 0); 654 | } 655 | 656 | static void drain_life(void) { 657 | if (rand_percent(60) || rogue.hp_max <= 30 || rogue.hp_current < 10) 658 | return; 659 | 660 | short n = get_rand(1, 3); // 1 Hp, 2 Str, 3 both 661 | if (n != 2 || !sustain_strength) 662 | message("you feel weaker", 0); 663 | 664 | if (n != 2) { 665 | rogue.hp_max--; 666 | rogue.hp_current--; 667 | less_hp++; 668 | } 669 | if (n != 1) { 670 | if ((rogue.str_current > 3) && (!sustain_strength)) { 671 | rogue.str_current--; 672 | if (coin_toss()) 673 | rogue.str_max--; 674 | } 675 | } 676 | print_stats((STAT_STRENGTH | STAT_HP)); 677 | } 678 | 679 | bool m_confuse(object *monster) { 680 | if (!rogue_can_see(monster->row, monster->col)) 681 | return 0; 682 | 683 | if (rand_percent(45)) { 684 | monster->m_flags &= (~CONFUSES); // will not confuse the rogue 685 | return 0; 686 | } 687 | if (rand_percent(55)) { 688 | monster->m_flags &= (~CONFUSES); 689 | messagef(1, "the gaze of the %s has confused you", mon_name(monster)); 690 | cnfs(); 691 | return 1; 692 | } 693 | return 0; 694 | } 695 | 696 | bool flame_broil(object *monster) { 697 | if ((!mon_sees(monster, rogue.row, rogue.col)) || coin_toss()) 698 | return 0; 699 | 700 | short row = rogue.row - monster->row; 701 | short col = rogue.col - monster->col; 702 | if (row < 0) 703 | row = -row; 704 | if (col < 0) 705 | col = -col; 706 | if (((row != 0) && (col != 0) && (row != col)) || ((row > 7) || (col > 7))) 707 | return 0; 708 | 709 | short dir = get_dir(monster->row, monster->col, row, col); 710 | bounce(FIRE, dir, monster->row, monster->col, 0); 711 | 712 | return 1; 713 | } 714 | 715 | static short get_dir(short srow, short scol, short drow, short dcol) { 716 | if (srow == drow) { 717 | if (scol < dcol) 718 | return RIGHT; 719 | else 720 | return LEFT; 721 | } 722 | if (scol == dcol) { 723 | if (srow < drow) 724 | return DOWN; 725 | else 726 | return UPWARD; 727 | } 728 | if ((srow > drow) && (scol > dcol)) 729 | return UPLEFT; 730 | if ((srow < drow) && (scol < dcol)) 731 | return DOWNRIGHT; 732 | if ((srow < drow) && (scol > dcol)) 733 | return DOWNLEFT; 734 | // if ((srow > drow) && (scol < dcol)) 735 | return UPRIGHT; 736 | } 737 | -------------------------------------------------------------------------------- /inventory.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)inventory.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/inventory.c,v 1.4 1999/11/30 03:49:23 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | bool is_wood[WANDS]; 45 | const char *press_space = " --press space to continue--"; 46 | 47 | static const char *const wand_materials[WAND_MATERIALS] = { 48 | "steel ", "bronze ", "gold ", "silver ", "copper ", "nickel ", "cobalt ", 49 | "tin ", "iron ", "magnesium ", "chrome ", "carbon ", "platinum ", "silicon ", 50 | "titanium ", "teak ", "oak ", "cherry ", "birch ", "pine ", "cedar ", 51 | "balsa ", "ivory ", "walnut ", "maple ", "mahogany ", "elm ", "palm ", 52 | "redwood ", "wooden " 53 | }; 54 | 55 | static const char *const gems[GEMS] = { 56 | "diamond ", "stibotantalite ", "lapi-lazuli ", "ruby ", "emerald ", "sapphire ", 57 | "amethyst ", "quartz ", "tiger-eye ", "opal ", "agate ", "turquoise ", 58 | "pearl ", "garnet " 59 | }; 60 | 61 | static const char *const syllables[MAXSYLLABLES] = { 62 | "blech ", "foo ", "barf ", "rech ", "bar ", "blech ", "quo ", "bloto ", "oh ", 63 | "caca ", "blorp ", "erp ", "festr ", "rot ", "slie ", "snorf ", "iky ", "yuky ", 64 | "ooze ", "ah ", "bahl ", "zep ", "druhl ", "flem ", "behil ", "arek ", "mep ", 65 | "zihr ", "grit ", "kona ", "kini ", "ichi ", "tims ", "ogr ", "oo ", "ighr ", 66 | "coph ", "swerr ", "mihln ", "poxi " 67 | }; 68 | 69 | #define COMS 47 70 | 71 | struct show_help_s { 72 | short com_char; 73 | const char *com_desc; 74 | }; 75 | 76 | static const struct show_help_s com_id_tab[COMS] = { 77 | { '?', "? prints help" }, 78 | { 'r', "r read scroll" }, 79 | { '/', "/ identify object" }, 80 | { 'e', "e eat food" }, 81 | { 'h', "h left " }, 82 | { 'w', "w wield a weapon" }, 83 | { 'j', "j down" }, 84 | { 'W', "W wear armor" }, 85 | { 'k', "k up" }, 86 | { 'T', "T take armor off" }, 87 | { 'l', "l right" }, 88 | { 'P', "P put on ring" }, 89 | { 'y', "y up & left" }, 90 | { 'R', "R remove ring" }, 91 | { 'u', "u up & right" }, 92 | { 'd', "d drop object" }, 93 | { 'b', "b down & left" }, 94 | { 'c', "c call object" }, 95 | { 'n', "n down & right" }, 96 | { '\0', "M or : run that way" }, 97 | { ')', ") print current weapon" }, 98 | { '\0', "^m or : run till adjacent" }, 99 | { ']', "] print current armor" }, 100 | { 'f', "f fight till death or near death" }, 101 | { '=', "= print current rings" }, 102 | { 't', "t throw something" }, 103 | { '\001', "^A print Hp-raise average" }, 104 | { 'm', "m move onto without picking up" }, 105 | { 'z', "z zap a wand in a direction" }, 106 | { 'o', "o examine/set options" }, 107 | { '^', "^ identify trap type" }, 108 | { '\022', "^R redraw screen" }, 109 | { '&', "& save screen into 'rogue.screen'" }, 110 | { 's', "s search for trap/secret door" }, 111 | { '\020', "^P repeat last message" }, 112 | { '>', "> go down a staircase" }, 113 | { '\033', "^[ cancel command" }, 114 | { '<', "< go up a staircase" }, 115 | { 'S', "S save game" }, 116 | { '.', ". rest for a turn" }, 117 | { 'Q', "Q quit" }, 118 | { ',', ", pick something up" }, 119 | { 'i', "i inventory" }, 120 | { 'F', "F fight till either of you dies" }, 121 | { 'I', "I inventory single item" }, 122 | { 'v', "v print version number" }, 123 | { 'q', "q quaff potion" } 124 | }; 125 | 126 | // Store this as globals so we can set it in inventory() and clear the 127 | // screen again in clear_inventory() – this is a fast but a bit of a hacky way 128 | // to add the openinv option. 129 | short last_inv_i, last_inv_j, last_inv_col; 130 | char inv_descs[MAX_PACK_COUNT+1][DCOLS]; 131 | 132 | void inventory(const object *pack, unsigned short mask, short dont_wait) { 133 | object *obj = pack->next_object; 134 | if (!obj) { 135 | message("your pack is empty", 0); 136 | return; 137 | } 138 | 139 | short i = 0, j = 0, maxlen = 0, n; 140 | while (obj) { 141 | if (obj->what_is & mask) { 142 | inv_descs[i][0] = ' '; 143 | inv_descs[i][1] = obj->ichar; 144 | inv_descs[i][2] = ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')'; 145 | inv_descs[i][3] = ' '; 146 | get_desc(obj, inv_descs[i]+4); 147 | if ((n = strlen(inv_descs[i])) > maxlen) 148 | maxlen = n; 149 | i++; 150 | } 151 | obj = obj->next_object; 152 | } 153 | if (dont_wait) 154 | strcpy(inv_descs[i++], "--select item--"); 155 | else 156 | strcpy(inv_descs[i++], press_space); 157 | if (maxlen < 27) 158 | maxlen = 27; 159 | 160 | short col = DCOLS - (maxlen + 2); 161 | for (short row = 0; ((row < i) && (row < DROWS)); row++) { 162 | if (row > 0) { 163 | for (j = col; j < DCOLS; j++) { 164 | inv_descs[row-1][j-col] = (short)mvinch(row, j); 165 | } 166 | inv_descs[row-1][j-col] = 0; 167 | } 168 | mvaddstr(row, col, inv_descs[row]); 169 | clrtoeol(); 170 | } 171 | refresh(); 172 | last_inv_i = i; 173 | last_inv_j = j; 174 | last_inv_col = col; 175 | if (!dont_wait) { 176 | wait_for_ack(); 177 | clear_inventory(); 178 | } 179 | } 180 | 181 | void clear_inventory(void) { 182 | move(0, 0); 183 | clrtoeol(); 184 | for (last_inv_j = 1; ((last_inv_j < last_inv_i) && (last_inv_j < DROWS)); last_inv_j++) 185 | mvaddstr(last_inv_j, last_inv_col, inv_descs[last_inv_j-1]); 186 | } 187 | 188 | void show_help(void) { 189 | check_message(); 190 | 191 | short rows = (((COMS / 2) + (COMS % 2)) + 1); 192 | bool need_two_screens = FALSE; 193 | if (rows > LINES) { 194 | need_two_screens = 1; 195 | rows = LINES; 196 | } 197 | 198 | char save[(((COMS / 2) + (COMS % 2)) + 1)][DCOLS]; 199 | for (short i = 0; i < rows; i++) { 200 | for (short j = 0; j < DCOLS; j++) 201 | save[i][j] = (short)mvinch(i, j); 202 | } 203 | 204 | short k = 0; 205 | next_page: 206 | for (short i = 0; i < rows; i++) { 207 | move(i, 0); 208 | clrtoeol(); 209 | } 210 | for (short i = 0; i < (rows-1); i++) { 211 | if (i < (LINES-1)) { 212 | if (((i + i) < COMS) && ((i + i + k) < COMS)) 213 | mvaddstr(i, 0, com_id_tab[i + i + k].com_desc); 214 | if (((i + i + 1) < COMS) && ((i + i + k + 1) < COMS)) 215 | mvaddstr(i, (DCOLS/2), com_id_tab[i + i + k + 1].com_desc); 216 | } 217 | } 218 | mvaddstr(rows - 1, 0, need_two_screens ? more : press_space); 219 | refresh(); 220 | wait_for_ack(); 221 | 222 | if (need_two_screens) { 223 | k += ((rows - 1) * 2); 224 | need_two_screens = 0; 225 | goto next_page; 226 | } 227 | 228 | for (short i = 0; i < rows; i++) { 229 | move(i, 0); 230 | for (short j = 0; j < DCOLS; j++) 231 | addch(save[i][j]); 232 | } 233 | } 234 | 235 | void mix_colors(void) { 236 | char *t[MAX_ID_TITLE_LEN]; 237 | for (short i = 0; i <= 32; i++) { 238 | short j = get_rand(0, (POTIONS - 1)); 239 | short k = get_rand(0, (POTIONS - 1)); 240 | memcpy(t, id_potions[j].title, MAX_ID_TITLE_LEN); 241 | memcpy(id_potions[j].title, id_potions[k].title, MAX_ID_TITLE_LEN); 242 | memcpy(id_potions[k].title, t, MAX_ID_TITLE_LEN); 243 | } 244 | } 245 | 246 | void make_scroll_titles(void) { 247 | for (short i = 0; i < SCROLS; i++) { 248 | short sylls = get_rand(2, 5); 249 | strcpy(id_scrolls[i].title, "'"); 250 | 251 | for (short j = 0; j < sylls; j++) { 252 | short s = get_rand(1, (MAXSYLLABLES-1)); 253 | strcat(id_scrolls[i].title, syllables[s]); 254 | } 255 | short n = strlen(id_scrolls[i].title); 256 | strcpy(id_scrolls[i].title+(n-1), "' "); 257 | } 258 | } 259 | 260 | const char * get_desc_str(const object *obj) { 261 | char *buf = calloc(128, sizeof(char)); 262 | get_desc(obj, buf); 263 | return buf; 264 | } 265 | 266 | void get_desc(const object *obj, char *desc) { 267 | if (!obj) 268 | return; 269 | if (obj->what_is == AMULET) { 270 | strcpy(desc, "the amulet of Yendor "); 271 | return; 272 | } 273 | if (obj->what_is == GOLD) { 274 | sprintf(desc, "%d pieces of gold", obj->quantity); 275 | return; 276 | } 277 | 278 | if (obj->what_is != ARMOR) { 279 | if (obj->quantity == 1) 280 | strcpy(desc, "a "); 281 | else 282 | sprintf(desc, "%d ", obj->quantity); 283 | } 284 | 285 | char more_info[32]; 286 | const char *item_name = name_of(obj); 287 | if (obj->what_is == FOOD) { 288 | if (obj->which_kind == RATION) { 289 | if (obj->quantity > 1) 290 | sprintf(desc, "%d rations of ", obj->quantity); 291 | else 292 | strcpy(desc, "some "); 293 | } else 294 | strcpy(desc, "a "); 295 | strcat(desc, item_name); 296 | goto ANA; 297 | } 298 | 299 | struct id *id_table = get_id_table(obj); 300 | 301 | if (wizard) 302 | goto ID; 303 | if (obj->what_is & (WEAPON | ARMOR | WAND | RING)) 304 | goto CHECK; 305 | 306 | switch (id_table[obj->which_kind].id_status) { 307 | case UNIDENTIFIED: 308 | CHECK: 309 | switch (obj->what_is) { 310 | case SCROL: 311 | strcat(desc, item_name); 312 | strcat(desc, "entitled: "); 313 | strcat(desc, id_table[obj->which_kind].title); 314 | break; 315 | case POTION: 316 | strcat(desc, id_table[obj->which_kind].title); 317 | strcat(desc, item_name); 318 | break; 319 | case WAND: 320 | case RING: 321 | if (obj->identified || (id_table[obj->which_kind].id_status == IDENTIFIED)) 322 | goto ID; 323 | if (id_table[obj->which_kind].id_status == CALLED) 324 | goto CALL; 325 | strcat(desc, id_table[obj->which_kind].title); 326 | strcat(desc, item_name); 327 | break; 328 | case ARMOR: 329 | if (obj->identified) 330 | goto ID; 331 | strcpy(desc, id_table[obj->which_kind].title); 332 | break; 333 | case WEAPON: 334 | if (obj->identified) 335 | goto ID; 336 | strcat(desc, name_of(obj)); 337 | break; 338 | } 339 | break; 340 | case CALLED: 341 | CALL: 342 | switch (obj->what_is) { 343 | case SCROL: 344 | case POTION: 345 | case WAND: 346 | case RING: 347 | strcat(desc, item_name); 348 | strcat(desc, "called "); 349 | strcat(desc, id_table[obj->which_kind].title); 350 | break; 351 | } 352 | break; 353 | case IDENTIFIED: 354 | ID: 355 | switch (obj->what_is) { 356 | case SCROL: 357 | case POTION: 358 | strcat(desc, item_name); 359 | strcat(desc, id_table[obj->which_kind].real); 360 | break; 361 | case RING: 362 | if (wizard || obj->identified) { 363 | if ((obj->which_kind == DEXTERITY) || 364 | (obj->which_kind == ADD_STRENGTH)) { 365 | sprintf(more_info, "%s%d ", ((obj->class > 0) ? "+" : ""), 366 | obj->class); 367 | strcat(desc, more_info); 368 | } 369 | } 370 | strcat(desc, item_name); 371 | strcat(desc, id_table[obj->which_kind].real); 372 | break; 373 | case WAND: 374 | strcat(desc, item_name); 375 | strcat(desc, id_table[obj->which_kind].real); 376 | if (wizard || obj->identified) { 377 | sprintf(more_info, "[%d]", obj->class); 378 | strcat(desc, more_info); 379 | } 380 | break; 381 | case ARMOR: 382 | sprintf(desc, "%s%d ", ((obj->d_enchant >= 0) ? "+" : ""), 383 | obj->d_enchant); 384 | strcat(desc, id_table[obj->which_kind].title); 385 | sprintf(more_info, "[%d] ", get_armor_class(obj)); 386 | strcat(desc, more_info); 387 | break; 388 | case WEAPON: 389 | sprintf(desc+strlen(desc), "%s%d,%s%d ", 390 | ((obj->hit_enchant >= 0) ? "+" : ""), obj->hit_enchant, 391 | ((obj->d_enchant >= 0) ? "+" : ""), obj->d_enchant); 392 | strcat(desc, name_of(obj)); 393 | break; 394 | } 395 | break; 396 | } 397 | ANA: 398 | if (!strncmp(desc, "a ", 2)) { 399 | if (is_vowel(desc[2])) { 400 | for (short i = strlen(desc) + 1; i > 1; i--) 401 | desc[i] = desc[i-1]; 402 | desc[1] = 'n'; 403 | } 404 | } 405 | if (obj->in_use_flags & BEING_WIELDED) 406 | strcat(desc, "in hand"); 407 | else if (obj->in_use_flags & BEING_WORN) 408 | strcat(desc, "being worn"); 409 | else if (obj->in_use_flags & ON_LEFT_HAND) 410 | strcat(desc, "on left hand"); 411 | else if (obj->in_use_flags & ON_RIGHT_HAND) 412 | strcat(desc, "on right hand"); 413 | } 414 | 415 | void get_wand_and_ring_materials(void) { 416 | short j; 417 | bool used[WAND_MATERIALS]; 418 | 419 | for (short i = 0; i < WAND_MATERIALS; i++) 420 | used[i] = 0; 421 | for (short i = 0; i < WANDS; i++) { 422 | do { 423 | j = get_rand(0, WAND_MATERIALS - 1); 424 | } while (used[j]); 425 | used[j] = 1; 426 | strcpy(id_wands[i].title, wand_materials[j]); 427 | is_wood[i] = (j > MAX_METAL); 428 | } 429 | 430 | for (short i = 0; i < GEMS; i++) 431 | used[i] = 0; 432 | for (short i = 0; i < RINGS; i++) { 433 | do { 434 | j = get_rand(0, GEMS - 1); 435 | } while (used[j]); 436 | used[j] = 1; 437 | strcpy(id_rings[i].title, gems[j]); 438 | } 439 | } 440 | 441 | void single_inv(short ichar) { 442 | short ch = ichar ? ichar : pack_letter("inventory what?", ALL_OBJECTS); 443 | if (ch == CANCEL) 444 | return; 445 | 446 | object *obj; 447 | if (!(obj = get_letter_object(ch))) { 448 | message("no such item.", 0); 449 | return; 450 | } 451 | messagef(0, "%c%c %s", ch, ((obj->what_is & ARMOR) && obj->is_protected) ? '}' : ')', get_desc_str(obj)); 452 | } 453 | 454 | struct id * get_id_table(const object *obj) { 455 | switch (obj->what_is) { 456 | case SCROL: return id_scrolls; 457 | case POTION: return id_potions; 458 | case WAND: return id_wands; 459 | case RING: return id_rings; 460 | case WEAPON: return id_weapons; 461 | case ARMOR: return id_armors; 462 | } 463 | return NULL; 464 | } 465 | 466 | void inv_armor_weapon(bool is_weapon) { 467 | if (is_weapon) { 468 | if (rogue.weapon) 469 | single_inv(rogue.weapon->ichar); 470 | else 471 | message("not wielding anything", 0); 472 | } else { 473 | if (rogue.armor) 474 | single_inv(rogue.armor->ichar); 475 | else 476 | message("not wearing anything", 0); 477 | } 478 | } 479 | 480 | void id_type(void) { 481 | message("what do you want identified?", 0); 482 | 483 | const char *id; 484 | int ch = rgetchar(); 485 | if (ch >= 'A' && ch <= 'Z') { 486 | id = m_names[ch-'A']; 487 | } else if (ch < 32) { 488 | check_message(); 489 | return; 490 | } else { 491 | switch (ch) { 492 | case '@': id = "you"; break; 493 | case '%': id = "staircase"; break; 494 | case '^': id = "trap"; break; 495 | case '+': id = "door"; break; 496 | case '-': 497 | case '|': id = "wall of a room"; break; 498 | case '.': id = "floor"; break; 499 | case '#': id = "passage"; break; 500 | case ' ': id = "solid rock"; break; 501 | case '=': id = "ring"; break; 502 | case '?': id = "scroll"; break; 503 | case '!': id = "potion"; break; 504 | case '/': id = "wand or staff"; break; 505 | case ')': id = "weapon"; break; 506 | case ']': id = "armor"; break; 507 | case '*': id = "gold"; break; 508 | case ':': id = "food"; break; 509 | case ',': id = "the Amulet of Yendor"; break; 510 | default: id = "unknown character"; break; 511 | } 512 | } 513 | check_message(); 514 | messagef(0, "'%c': %s", ch, id); 515 | } 516 | -------------------------------------------------------------------------------- /machdep.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)machdep.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/machdep.c,v 1.6.2.1 2001/12/17 12:43:23 phantom Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | #include "rogue.h" 50 | 51 | void md_heed_signals(void) { 52 | signal(SIGINT, onintr); 53 | #ifndef WINDOWS 54 | signal(SIGQUIT, byebye); 55 | signal(SIGHUP, error_save); 56 | #endif 57 | } 58 | 59 | void md_ignore_signals(void) { 60 | signal(SIGINT, SIG_IGN); 61 | #ifndef WINDOWS 62 | signal(SIGQUIT, SIG_IGN); 63 | signal(SIGHUP, SIG_IGN); 64 | #endif 65 | } 66 | 67 | char * md_user(void) { 68 | #ifdef WINDOWS 69 | return getenv("USERNAME"); 70 | #else 71 | return getenv("USER"); 72 | #endif 73 | } 74 | 75 | char * md_homedir(void) { 76 | #ifdef WINDOWS 77 | return getenv("USERPROFILE"); 78 | #else 79 | return getenv("HOME"); 80 | #endif 81 | } 82 | 83 | char * md_savedir(void) { 84 | char *base = getenv("XDG_DATA_HOME"); 85 | if (!base || base[0] != '/') { 86 | char *home = md_homedir(); 87 | #ifdef WINDOWS 88 | if (!home) 89 | clean_up("md_scorefile: invalid $HOME\n"); 90 | base = calloc(strlen(home) + 14, sizeof(char)); 91 | snprintf(base, strlen(home) + 14, "%s/_rogue-clone", home); 92 | #else 93 | if (!home || home[0] != '/') 94 | clean_up("md_scorefile: invalid $HOME\n"); 95 | base = calloc(strlen(home) + 14, sizeof(char)); 96 | snprintf(base, strlen(home) + 14, "%s/.local/share", home); 97 | #endif 98 | } 99 | 100 | char *dir = calloc(strlen(base) + 13, sizeof(char)); 101 | snprintf(dir, strlen(base) + 13, "%s/rogue-clone", base); 102 | #ifdef WINDOWS 103 | mkdir(dir); 104 | #else 105 | mkdir(dir, 0755); 106 | #endif 107 | 108 | free(base); 109 | return dir; 110 | } 111 | 112 | char * md_scorefile(void) { 113 | char *dir = md_savedir(); 114 | char *file = calloc(strlen(dir) + 13, sizeof(char)); 115 | snprintf(file, strlen(dir) + 13, "%s/score", dir); 116 | 117 | free(dir); 118 | return file; 119 | } 120 | 121 | char * md_savefile(void) { 122 | char *dir = md_savedir(); 123 | char *file = calloc(strlen(dir) + 12, sizeof(char)); 124 | snprintf(file, strlen(dir) + 12, "%s/save", dir); 125 | 126 | free(dir); 127 | return file; 128 | } 129 | 130 | // When the parameter 'l' is non-zero a lock is requested. Otherwise the lock is 131 | // released. 132 | void md_lock(bool l) { 133 | #if defined(WINDOWS) || defined(WASM) 134 | // Nothing or now. 135 | #else 136 | static int fd; 137 | short tries; 138 | 139 | if (l) { 140 | char *f = md_scorefile(); 141 | if ((fd = open(f, O_RDONLY)) < 1) { 142 | free(f); 143 | message("cannot lock score file", 0); 144 | return; 145 | } 146 | free(f); 147 | for (tries = 0; tries < 5; tries++) 148 | if (!flock(fd, LOCK_EX|LOCK_NB)) 149 | return; 150 | } else { 151 | flock(fd, LOCK_NB); 152 | close(fd); 153 | } 154 | #endif 155 | } 156 | 157 | int get_rand(int x, int y) { 158 | int t; 159 | if (x > y) { 160 | t = y; 161 | y = x; 162 | x = t; 163 | } 164 | 165 | long lr; 166 | #ifdef WINDOWS 167 | lr = rand(); 168 | #else 169 | lr = random(); 170 | #endif 171 | lr &= 0x00003fffL; 172 | 173 | int r = (int)lr; 174 | r = (r % ((y - x) + 1)) + x; 175 | return r; 176 | } 177 | 178 | bool rand_percent(int percentage) { 179 | return get_rand(1, 100) <= percentage; 180 | } 181 | 182 | bool coin_toss(void) { 183 | #ifdef WINDOWS 184 | return ((rand() & 01) ? 1 : 0); 185 | #else 186 | return ((random() & 01) ? 1 : 0); 187 | #endif 188 | } 189 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)main.c 8.1 (Berkeley) 5/31/93 32 | * $NetBSD: main.c,v 1.9 2008/07/20 01:03:22 lukem Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include "rogue.h" 47 | 48 | int main(int argc, char *argv[]) { 49 | if (init(argc, argv)) // restored game 50 | goto play_level; 51 | 52 | for (;;) { 53 | if (!noautosave && rogue.hp_current > 0 && cur_level > 0) 54 | save_into_file(save_file); 55 | clear_level(); 56 | make_level(); 57 | put_objects(); 58 | put_stairs(); 59 | add_traps(); 60 | put_mons(); 61 | put_player(party_room); 62 | print_stats(STAT_ALL); 63 | play_level: 64 | play_level(); 65 | free_stuff(&level_objects); 66 | free_stuff(&level_monsters); 67 | } 68 | } 69 | 70 | static const char *const rnd_names[] = { 71 | "Arthur Dent", "Benjy Mouse", "Deep Thought", "Ford Prefect", "Frankie Mouse", 72 | "Marvin the Paranoid Android", "Slartibartfast", "Trillian", "Vogon Jeltz", 73 | "Zaphod Beeblebrox", "Zarniwoop", 74 | 75 | "Baldrick", "Bishop of Bath and Wells", "Bob", "Captain Darling", "Dougal MacAngus", 76 | "Edmund Blackadder", "Lord Flashheart", "Lord Percy Percy", "MacAdder", "Melchett", 77 | "Mrs Miggins", "Nursie", "Prince George", "Queenie", "William Pitt the Even Younger", 78 | 79 | "Adam Kenyon", "Angela Heaney", "Ben Swain", "Cal Richards", "Claire Ballentine", "Cliff Lawton", 80 | "Dan Miller", "Douglas Tickel", "Emma Messinger", "Fergus Williams", "Geoff Holhurst", "Glenn Cullen", 81 | "Helen Hatley", "Hugh Abbot", "Jamie McDonald", "John Duggan", "Julius Nicholson", "Malcolm Tucker", 82 | "Mary Drake", "Nick Hanway", "Nicola Murray", "Ollie Reeder", "Pat Morrissey", "Peter Mannion", 83 | "Phil Smith", "Robyn Murdoch", "Steve Fleming", "Stewart Pearson", "Terri Coverley", 84 | }; 85 | 86 | static void do_args(int, char **); 87 | static void do_opts(void); 88 | static void env_get_value(char **, char *, bool); 89 | static void init_str(char **, const char *); 90 | static void player_init(void); 91 | 92 | static bool init_curses = 0; 93 | 94 | char *nick_name = NULL; 95 | int low_health_warn = 0; 96 | bool cant_int = 0; 97 | bool did_int = 0; 98 | bool score_only; 99 | unsigned long game_seed = 0; 100 | bool save_is_interactive = 1; 101 | bool ask_quit = 1; 102 | bool no_skull = 0; 103 | bool passgo = 0; 104 | bool locked_down = 0; 105 | const char *error_file = "rogue.esave"; 106 | const char *byebye_string = "Okay, bye bye!"; 107 | 108 | bool init(int argc, char *argv[]) { 109 | do_args(argc, argv); 110 | do_opts(); 111 | struct stat st; 112 | int exist = stat(save_file, &st); 113 | bool do_restore = false; 114 | if (exist == 0) { 115 | do_restore = true; 116 | printf("Restoring from %s", save_file); 117 | fflush(stdout); 118 | } 119 | 120 | if (!score_only && !do_restore) { 121 | printf("Hello %s, just a moment while I dig the dungeon...", nick_name); 122 | fflush(stdout); 123 | } 124 | 125 | initscr(); 126 | if (LINES < DROWS || COLS < DCOLS) 127 | clean_up("must be played on 24 x 80 screen"); 128 | start_window(); 129 | init_curses = 1; 130 | 131 | md_heed_signals(); 132 | 133 | if (score_only) 134 | put_scores(NULL, 0); 135 | 136 | if (game_seed == 0) 137 | game_seed = seed(); 138 | srand(game_seed); 139 | 140 | if (do_restore) { 141 | restore(save_file); 142 | return 1; 143 | } 144 | mix_colors(); 145 | get_wand_and_ring_materials(); 146 | make_scroll_titles(); 147 | 148 | level_objects.next_object = NULL; 149 | level_monsters.next_monster = NULL; 150 | player_init(); 151 | ring_stats(0); 152 | return 0; 153 | } 154 | 155 | // https://stackoverflow.com/a/323302 156 | unsigned long seed(void) { 157 | unsigned long a = clock(); 158 | unsigned long b = time(NULL); 159 | unsigned long c = getpid(); 160 | a=a-b; a=a-c; a=a^(c >> 13); 161 | b=b-c; b=b-a; b=b^(a << 8); 162 | c=c-a; c=c-b; c=c^(b >> 13); 163 | a=a-b; a=a-c; a=a^(c >> 12); 164 | b=b-c; b=b-a; b=b^(a << 16); 165 | c=c-a; c=c-b; c=c^(b >> 5); 166 | a=a-b; a=a-c; a=a^(c >> 3); 167 | b=b-c; b=b-a; b=b^(a << 10); 168 | c=c-a; c=c-b; c=c^(b >> 15); 169 | return c; 170 | } 171 | 172 | static void player_init(void) { 173 | rogue.pack.next_object = NULL; 174 | 175 | object *obj = alloc_object(); 176 | get_food(obj, 1); 177 | add_to_pack(obj, &rogue.pack, 1); 178 | 179 | obj = alloc_object(); // initial armor 180 | obj->what_is = ARMOR; 181 | obj->which_kind = RINGMAIL; 182 | obj->class = RINGMAIL+2; 183 | obj->is_protected = 0; 184 | obj->d_enchant = 1; 185 | add_to_pack(obj, &rogue.pack, 1); 186 | do_wear(obj); 187 | 188 | obj = alloc_object(); // initial weapons 189 | obj->what_is = WEAPON; 190 | obj->which_kind = MACE; 191 | strcpy(&obj->damage[0], "2d3"); 192 | obj->hit_enchant = obj->d_enchant = 1; 193 | obj->identified = 1; 194 | add_to_pack(obj, &rogue.pack, 1); 195 | do_wield(obj); 196 | 197 | obj = alloc_object(); 198 | obj->what_is = WEAPON; 199 | obj->which_kind = BOW; 200 | strcpy(&obj->damage[0],"1d2"); 201 | obj->hit_enchant = 1; 202 | obj->d_enchant = 0; 203 | obj->identified = 1; 204 | add_to_pack(obj, &rogue.pack, 1); 205 | 206 | obj = alloc_object(); 207 | obj->what_is = WEAPON; 208 | obj->which_kind = ARROW; 209 | obj->quantity = get_rand(25, 35); 210 | strcpy(&obj->damage[0], "1d2"); 211 | obj->hit_enchant = 0; 212 | obj->d_enchant = 0; 213 | obj->identified = 1; 214 | add_to_pack(obj, &rogue.pack, 1); 215 | } 216 | 217 | void clean_up(const char *estr) { 218 | if (save_is_interactive) { 219 | if (init_curses) { 220 | move(DROWS - 1, 0); 221 | refresh(); 222 | stop_window(); 223 | } 224 | printf("\n%s\n", estr); 225 | } 226 | exit(0); 227 | } 228 | 229 | void start_window(void) { 230 | cbreak(); 231 | noecho(); 232 | nonl(); 233 | keypad(stdscr, true); 234 | } 235 | 236 | void stop_window(void) { 237 | endwin(); 238 | } 239 | 240 | void byebye(int sig) { 241 | md_ignore_signals(); 242 | if (ask_quit) 243 | quit(1); 244 | else 245 | clean_up(byebye_string); 246 | md_heed_signals(); 247 | } 248 | 249 | void onintr(int sig) { 250 | md_ignore_signals(); 251 | if (cant_int) 252 | did_int = 1; 253 | else { 254 | check_message(); 255 | message("interrupt", 1); 256 | } 257 | md_heed_signals(); 258 | } 259 | 260 | void error_save(int sig) { 261 | save_is_interactive = 0; 262 | save_into_file(error_file); 263 | clean_up(""); 264 | } 265 | 266 | static void do_args(int argc, char *argv[]) { 267 | int opt; 268 | while ((opt = getopt(argc, argv, "sS:")) != -1) { 269 | switch (opt) { 270 | case 's': 271 | score_only = 1; 272 | break; 273 | case 'S': 274 | game_seed = ulget_number(optarg); 275 | break; 276 | default: 277 | exit(1); 278 | } 279 | } 280 | if (argc >= optind) 281 | save_file = argv[optind]; 282 | } 283 | 284 | static void do_opts(void) { 285 | char *eptr; 286 | if ((eptr = getenv("ROGUE_CLONE_OPTS")) != NULL) { 287 | for (;;) { 288 | while ((*eptr) == ' ') 289 | eptr++; 290 | if (!(*eptr)) 291 | break; 292 | if (!strncmp(eptr, "fruit=", 6)) { 293 | eptr += 6; 294 | env_get_value(&fruit, eptr, 1); 295 | } else if (!strncmp(eptr, "file=", 5)) { 296 | eptr += 5; 297 | env_get_value(&save_file, eptr, 0); 298 | } else if (!strncmp(eptr, "name=", 5)) { 299 | eptr += 5; 300 | env_get_value(&nick_name, eptr, 0); 301 | if (nick_name != NULL && strcmp(nick_name, "*") == 0) { 302 | srand(seed()); // Make sure it's seeded; will apply -S/seed() later for the actual game. 303 | free(nick_name); 304 | nick_name = (char *)rnd_names[get_rand(0, sizeof(rnd_names)/sizeof(rnd_names[0])) - 1]; 305 | } 306 | } else if (!strncmp(eptr, "lowhealth=", 5)) { 307 | eptr += 10; 308 | char *buf = NULL; 309 | env_get_value(&buf, eptr, 0); 310 | low_health_warn = get_number(buf); 311 | free(buf); 312 | } else if (!strncmp(eptr, "noaskquit", 9)) 313 | ask_quit = 0; 314 | else if (!strncmp(eptr, "noskull", 7) || !strncmp(eptr, "notomb", 6)) 315 | no_skull = 1; 316 | else if (!strncmp(eptr, "passgo", 6)) 317 | passgo = 1; 318 | else if (!strncmp(eptr, "lock", 4)) 319 | locked_down = 1; 320 | else if (!strncmp(eptr, "noautosave", 10)) 321 | noautosave = true; 322 | else if (!strncmp(eptr, "revshift", 8)) 323 | revshift = true; 324 | 325 | while ((*eptr) && (*eptr != ',')) 326 | eptr++; 327 | if (!(*(eptr++))) 328 | break; 329 | } 330 | } 331 | 332 | // If some strings have not been set through ROGUE_CLONE_OPTS, assign defaults 333 | // to them so that the options editor has data to work with. 334 | init_str(&nick_name, md_user()); 335 | init_str(&fruit, "slime-mold"); 336 | char *f = md_savefile(); 337 | init_str(&save_file, f); 338 | free(f); 339 | } 340 | 341 | static void env_get_value(char **s, char *e, bool add_blank) { 342 | short i = 0; 343 | const char *t = e; 344 | while ((*e) && (*e != ',')) { 345 | if (*e == ':') 346 | *e = ';'; // ':' reserved for score file purposes 347 | e++; 348 | if (++i >= MAX_OPT_LEN) 349 | break; 350 | } 351 | // note: edit_opts() in room.c depends on this being the right size 352 | *s = malloc(MAX_OPT_LEN + 2); 353 | if (*s == NULL) 354 | clean_up("out of memory"); 355 | strncpy(*s, t, i); 356 | if (add_blank) 357 | (*s)[i++] = ' '; 358 | (*s)[i] = '\0'; 359 | } 360 | 361 | static void init_str(char **str, const char *dflt) { 362 | if (!(*str)) { 363 | // note: edit_opts() in room.c depends on this size 364 | *str = calloc(MAX_OPT_LEN + 2, sizeof(char)); 365 | if (*str == NULL) 366 | clean_up("out of memory"); 367 | strcpy(*str, dflt); 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /message.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)message.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/message.c,v 1.7.2.1 2000/07/20 10:35:07 kris Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include 43 | #include 44 | #include "rogue.h" 45 | 46 | static char msgs[NMESSAGES][DCOLS] = {"", "", "", "", ""}; 47 | static short msg_col = 0, imsg = -1; 48 | static bool rmsg = 0; 49 | 50 | bool msg_cleared = 1; 51 | char hunger_str[HUNGER_STR_LEN] = ""; 52 | const char *more = "-more-"; 53 | 54 | static void pad(const char *, short); 55 | static void save_screen(void); 56 | 57 | void message(const char *msg, bool interrupt_player) { 58 | cant_int = 1; 59 | 60 | if (!save_is_interactive) 61 | return; 62 | if (interrupt_player) { 63 | interrupted = 1; 64 | fflush(stdin); 65 | } 66 | 67 | if (!msg_cleared) { 68 | mvaddstr(MIN_ROW-1, msg_col, more); 69 | refresh(); 70 | wait_for_ack(); 71 | check_message(); 72 | } 73 | if (!rmsg) { 74 | imsg = (imsg + 1) % NMESSAGES; 75 | strcpy(msgs[imsg], msg); 76 | } 77 | mvaddstr(MIN_ROW-1, 0, msg); 78 | addch(' '); 79 | refresh(); 80 | msg_cleared = 0; 81 | msg_col = strlen(msg); 82 | 83 | cant_int = 0; 84 | 85 | if (did_int) { 86 | did_int = 0; 87 | onintr(0); 88 | } 89 | } 90 | 91 | void messagef(bool interrupt_player, const char *fmt, ...) { 92 | char buf[1024]; 93 | va_list ap; 94 | va_start(ap, fmt); 95 | vsnprintf(buf, sizeof(buf), fmt, ap); 96 | va_end(ap); 97 | message(buf, interrupt_player); 98 | } 99 | 100 | void remessage(short c) { 101 | if (imsg != -1) { 102 | check_message(); 103 | rmsg = 1; 104 | while (c > imsg) 105 | c -= NMESSAGES; 106 | message(msgs[(imsg - c) % NMESSAGES], 0); 107 | rmsg = 0; 108 | move(rogue.row, rogue.col); 109 | refresh(); 110 | } 111 | } 112 | 113 | void check_message(void) { 114 | if (msg_cleared) 115 | return; 116 | move(MIN_ROW - 1, 0); 117 | clrtoeol(); 118 | refresh(); 119 | msg_cleared = 1; 120 | } 121 | 122 | bool get_input_line(const char *prompt, const char *insert, char *buf, bool add_blank) { 123 | message(prompt, 0); 124 | short n = strlen(prompt); 125 | short i = 0; 126 | if (insert[0]) { 127 | mvaddstr(0, n + 1, insert); 128 | strcpy(buf, insert); 129 | i = strlen(insert); 130 | move(0, (n + i + 1)); 131 | refresh(); 132 | } 133 | 134 | int ch; 135 | while (true) { 136 | ch = rgetchar(); 137 | if (ch == '\r' || ch == '\n' || ch == CANCEL) 138 | break; 139 | 140 | if (ch >= ' ' && ch <= '~' && i < PATH_MAX - 2) { 141 | buf[i++] = ch; 142 | addch(ch); 143 | } 144 | if (ch == KEY_BACKSPACE && i > 0) { 145 | mvaddch(0, i + n, ' '); 146 | move(MIN_ROW-1, i+n); 147 | i--; 148 | } 149 | refresh(); 150 | } 151 | check_message(); 152 | if (add_blank) 153 | buf[i++] = ' '; 154 | else { 155 | while ((i > 0) && (buf[i - 1] == ' ')) 156 | i--; 157 | } 158 | buf[i] = 0; 159 | 160 | return !(ch == CANCEL || i == 0 || (i == 1 && add_blank)); 161 | } 162 | 163 | int rgetchar(void) { 164 | for(;;) { 165 | int ch = getch(); 166 | switch (ch) { 167 | case 'R' - 0x40: wrefresh(curscr); break; 168 | case '&': save_screen(); break; 169 | default: return ch; 170 | } 171 | } 172 | } 173 | 174 | // Level: 99 Gold: 999999 Hp: 999(999) Str: 99(99) Arm: 99 Exp: 21/10000000 Hungry 175 | // 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 176 | void print_stats(int stat_mask) { 177 | char buf[16]; 178 | int row = DROWS - 1; 179 | bool label = (stat_mask & STAT_LABEL) ? 1 : 0; 180 | 181 | if (stat_mask & STAT_LEVEL) { 182 | if (label) 183 | mvaddstr(row, 0, "Level: "); 184 | // max level taken care of in make_level() 185 | sprintf(buf, "%d", cur_level); 186 | mvaddstr(row, 7, buf); 187 | pad(buf, 2); 188 | } 189 | if (stat_mask & STAT_GOLD) { 190 | if (label) 191 | mvaddstr(row, 10, "Gold: "); 192 | if (rogue.gold > MAX_GOLD) 193 | rogue.gold = MAX_GOLD; 194 | sprintf(buf, "%ld", rogue.gold); 195 | mvaddstr(row, 16, buf); 196 | pad(buf, 6); 197 | } 198 | if (stat_mask & STAT_HP) { 199 | if (rogue.hp_current < low_health_warn) 200 | attron(A_BOLD | A_REVERSE); 201 | // Always print this label, to apply or clear standout (could probably 202 | // just always print this entire line without this stat_mask business; 203 | // terminals are so fast compared to 1980 terminals it probably doesn't 204 | // matter much). 205 | mvaddstr(row, 23, "Hp: "); 206 | if (rogue.hp_max > MAX_HP) { 207 | rogue.hp_current -= (rogue.hp_max - MAX_HP); 208 | rogue.hp_max = MAX_HP; 209 | } 210 | sprintf(buf, "%d(%d)", rogue.hp_current, rogue.hp_max); 211 | mvaddstr(row, 27, buf); 212 | if (rogue.hp_current < low_health_warn) 213 | attroff(A_BOLD | A_REVERSE); 214 | pad(buf, 8); 215 | } 216 | if (stat_mask & STAT_STRENGTH) { 217 | if (label) 218 | mvaddstr(row, 36, "Str: "); 219 | if (rogue.str_max > MAX_STRENGTH) { 220 | rogue.str_current -= (rogue.str_max - MAX_STRENGTH); 221 | rogue.str_max = MAX_STRENGTH; 222 | } 223 | sprintf(buf, "%d(%d)", (rogue.str_current + add_strength), rogue.str_max); 224 | mvaddstr(row, 41, buf); 225 | pad(buf, 6); 226 | } 227 | if (stat_mask & STAT_ARMOR) { 228 | if (label) 229 | mvaddstr(row, 48, "Arm: "); 230 | if (rogue.armor && (rogue.armor->d_enchant > MAX_ARMOR)) 231 | rogue.armor->d_enchant = MAX_ARMOR; 232 | sprintf(buf, "%d", get_armor_class(rogue.armor)); 233 | mvaddstr(row, 53, buf); 234 | pad(buf, 2); 235 | } 236 | if (stat_mask & STAT_EXP) { 237 | if (label) 238 | mvaddstr(row, 56, "Exp: "); 239 | if (rogue.exp_points > MAX_EXP) 240 | rogue.exp_points = MAX_EXP; 241 | if (rogue.exp > MAX_EXP_LEVEL) 242 | rogue.exp = MAX_EXP_LEVEL; 243 | sprintf(buf, "%d/%ld", rogue.exp, rogue.exp_points); 244 | mvaddstr(row, 61, buf); 245 | pad(buf, 11); 246 | } 247 | if (stat_mask & STAT_HUNGER) { 248 | mvaddstr(row, 73, hunger_str); 249 | clrtoeol(); 250 | } 251 | refresh(); 252 | } 253 | 254 | static void pad(const char *s, short n) { 255 | for (short i = strlen(s); i < n; i++) 256 | addch(' '); 257 | } 258 | 259 | static void save_screen(void) { 260 | FILE *fp = fopen("rogue.screen", "w"); 261 | if (!fp) { 262 | sound_bell(); 263 | return; 264 | } 265 | 266 | for (short i = 0; i < DROWS; i++) { 267 | bool found_non_blank = 0; 268 | char buf[DCOLS + 2]; 269 | for (short j = (DCOLS - 1); j >= 0; j--) { 270 | buf[j] = (short)mvinch(i, j); 271 | if (!found_non_blank) { 272 | if (buf[j] != ' ' || j == 0) { 273 | buf[j + ((j == 0) ? 0 : 1)] = 0; 274 | found_non_blank = 1; 275 | } 276 | } 277 | } 278 | fputs(buf, fp); 279 | putc('\n', fp); 280 | } 281 | fclose(fp); 282 | } 283 | 284 | void sound_bell(void) { 285 | putchar(7); 286 | fflush(stdout); 287 | } 288 | 289 | bool is_digit(short ch) { 290 | return (ch >= '0') && (ch <= '9'); 291 | } 292 | 293 | int r_index(const char *str, int ch, bool last) { 294 | int i = 0; 295 | if (last) { 296 | for (i = strlen(str) - 1; i >= 0; i--) { 297 | if (str[i] == ch) 298 | return i; 299 | } 300 | } else { 301 | for (i = 0; str[i]; i++) { 302 | if (str[i] == ch) 303 | return i; 304 | } 305 | } 306 | return -1; 307 | } 308 | -------------------------------------------------------------------------------- /move.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)move.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/move.c,v 1.7 1999/11/30 03:49:24 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | short m_moves = 0; 45 | bool revshift = false; 46 | const char you_can_move_again[] = "you can move again"; 47 | 48 | static bool can_turn(short, short); 49 | static bool check_hunger(bool); 50 | static short gr_dir(void); 51 | static void heal(void); 52 | static bool next_to_something(int, int); 53 | static void turn_passage(short, bool); 54 | 55 | short one_move_rogue(int dirch, short pickup) { 56 | if (confused) 57 | dirch = gr_dir(); 58 | 59 | int d = -1; 60 | short row = rogue.row, col = rogue.col; 61 | is_direction(dirch, &d); 62 | get_dir_rc(d, &row, &col, 1); 63 | if (!can_move(rogue.row, rogue.col, row, col)) 64 | return MOVE_FAILED; 65 | 66 | if (being_held || bear_trap) { 67 | if (!(dungeon[row][col] & MONSTER)) { 68 | if (being_held) { 69 | message("you are being held", 1); 70 | } else { 71 | message("you are still stuck in the bear trap", 0); 72 | reg_move(); 73 | } 74 | return MOVE_FAILED; 75 | } 76 | } 77 | if (r_teleport) { 78 | if (rand_percent(R_TELE_PERCENT)) { 79 | tele(); 80 | return STOPPED_ON_SOMETHING; 81 | } 82 | } 83 | if (dungeon[row][col] & MONSTER) { 84 | rogue_hit(object_at(&level_monsters, row, col), 0); 85 | reg_move(); 86 | return MOVE_FAILED; 87 | } 88 | if (dungeon[row][col] & DOOR) { 89 | if (cur_room == PASSAGE) { 90 | cur_room = get_room_number(row, col); 91 | if (cur_room == NO_ROOM) 92 | clean_up("one_move_rogue: door to nowhere"); 93 | light_up_room(cur_room); 94 | wake_room(cur_room, 1, row, col); 95 | } else { 96 | light_passage(row, col); 97 | } 98 | } else if ((dungeon[rogue.row][rogue.col] & DOOR) && (dungeon[row][col] & TUNNEL)) { 99 | light_passage(row, col); 100 | wake_room(cur_room, 0, rogue.row, rogue.col); 101 | darken_room(cur_room); 102 | cur_room = PASSAGE; 103 | } else if (dungeon[row][col] & TUNNEL) 104 | light_passage(row, col); 105 | 106 | mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); 107 | mvaddch(row, col, rogue.fchar); 108 | refresh(); 109 | rogue.row = row; 110 | rogue.col = col; 111 | object *obj; 112 | char desc[DCOLS]; 113 | if (dungeon[row][col] & OBJECT) { 114 | if (levitate && pickup) 115 | return STOPPED_ON_SOMETHING; 116 | 117 | if (pickup && !levitate) { 118 | short status; 119 | if ((obj = pick_up(row, col, &status)) != NULL) { 120 | get_desc(obj, desc); 121 | if (obj->what_is == GOLD) { 122 | free_object(obj); 123 | goto not_in_pack; 124 | } 125 | } else if (!status) 126 | goto moved; 127 | else 128 | goto move_on; 129 | } else { 130 | move_on: 131 | obj = object_at(&level_objects, row, col); 132 | strcpy(desc, "moved onto "); 133 | get_desc(obj, desc + 11); 134 | goto not_in_pack; 135 | } 136 | short n = strlen(desc); 137 | desc[n] = '('; 138 | desc[n+1] = obj->ichar; 139 | desc[n+2] = ')'; 140 | desc[n+3] = 0; 141 | not_in_pack: 142 | message(desc, 1); 143 | reg_move(); 144 | return STOPPED_ON_SOMETHING; 145 | } 146 | if (dungeon[row][col] & (DOOR | STAIRS | TRAP)) { 147 | if ((!levitate) && (dungeon[row][col] & TRAP)) 148 | trap_player(row, col); 149 | reg_move(); 150 | return STOPPED_ON_SOMETHING; 151 | } 152 | moved: 153 | if (reg_move()) // fainted from hunger 154 | return STOPPED_ON_SOMETHING; 155 | return confused ? STOPPED_ON_SOMETHING : MOVED; 156 | } 157 | 158 | void multiple_move_rogue(short dirch) { 159 | if (revshift) 160 | dirch ^= 0x40; 161 | 162 | switch (dirch) { 163 | case 'H': case 'J': case 'K': case 'L': case 'B': case 'Y': case 'U': case 'N': 164 | while (!interrupted && one_move_rogue((dirch + 32), 1) == MOVED) 165 | ; 166 | 167 | if (!interrupted && passgo && (dungeon[rogue.row][rogue.col] & TUNNEL)) 168 | turn_passage(dirch + 32, 1); 169 | return; 170 | } 171 | 172 | short m, row, col; 173 | do { 174 | row = rogue.row; 175 | col = rogue.col; 176 | if ((m = one_move_rogue((dirch + 96), 1)) == MOVE_FAILED || m == STOPPED_ON_SOMETHING || interrupted) 177 | break; 178 | } while (!next_to_something(row, col)); 179 | if (!interrupted && passgo && m == MOVE_FAILED && (dungeon[rogue.row][rogue.col] & TUNNEL)) 180 | turn_passage(dirch + 96, 0); 181 | } 182 | 183 | bool is_passable(int row, int col) { 184 | if ((row < MIN_ROW) || (row > (DROWS - 2)) || (col < 0) || (col > (DCOLS-1))) 185 | return 0; 186 | if (dungeon[row][col] & HIDDEN) 187 | return (dungeon[row][col] & TRAP) ? 1 : 0; 188 | return dungeon[row][col] & (FLOOR | TUNNEL | DOOR | STAIRS | TRAP); 189 | } 190 | 191 | static bool next_to_something(int drow, int dcol) { 192 | if (confused) 193 | return 1; 194 | if (blind) 195 | return 0; 196 | 197 | short pass_count = 0; 198 | short i_end = (rogue.row < (DROWS-2)) ? 1 : 0; 199 | short j_end = (rogue.col < (DCOLS-1)) ? 1 : 0; 200 | 201 | for (short i = ((rogue.row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { 202 | for (short j = ((rogue.col > 0) ? -1 : 0); j <= j_end; j++) { 203 | if (i == 0 && j == 0) 204 | continue; 205 | if (rogue.row + i == drow && rogue.col + j == dcol) 206 | continue; 207 | 208 | short row = rogue.row + i; 209 | short col = rogue.col + j; 210 | unsigned short s = dungeon[row][col]; 211 | if (s & HIDDEN) 212 | continue; 213 | 214 | // If the rogue used to be right, up, left, down, or right of 215 | // row,col, and now isn't, then don't stop. 216 | if (s & (MONSTER | OBJECT | STAIRS)) { 217 | if ((row == drow || col == dcol) && (!(row == rogue.row || col == rogue.col))) 218 | continue; 219 | return 1; 220 | } 221 | if (s & TRAP) { 222 | if (!(s & HIDDEN)) { 223 | if (((row == drow) || (col == dcol)) && (!((row == rogue.row) || (col == rogue.col)))) 224 | continue; 225 | return 1; 226 | } 227 | } 228 | if ((i - j == 1 || i - j == -1) && (s & TUNNEL)) { 229 | if (++pass_count > 1) 230 | return 1; 231 | } 232 | if ((s & DOOR) && ((i == 0) || (j == 0))) 233 | return 1; 234 | } 235 | } 236 | return 0; 237 | } 238 | 239 | bool can_move(short row1, short col1, short row2, short col2) { 240 | if (!is_passable(row2, col2)) 241 | return 0; 242 | 243 | if (row1 != row2 && col1 != col2) { 244 | if ((dungeon[row1][col1] & DOOR) || (dungeon[row2][col2] & DOOR)) 245 | return 0; 246 | if ((!dungeon[row1][col2]) || (!dungeon[row2][col1])) 247 | return 0; 248 | } 249 | return 1; 250 | } 251 | 252 | void move_onto(void) { 253 | message("move direction? ", 0); 254 | 255 | int ch, d; 256 | bool first_miss = 1; 257 | while (!is_direction(ch = rgetchar(), &d)) { 258 | sound_bell(); 259 | if (first_miss) { 260 | message("direction? ", 0); 261 | first_miss = 0; 262 | } 263 | } 264 | check_message(); 265 | if (ch != CANCEL) 266 | one_move_rogue(ch, 0); 267 | } 268 | 269 | bool is_direction(int c, int *d) { 270 | if (c >= '1' && c <= '9' && c != '5') 271 | c = "bjnh lyku"[c - '1']; 272 | switch (c) { 273 | case 'h': case KEY_LEFT: *d = LEFT; return 1; 274 | case 'j': case KEY_DOWN: *d = DOWN; return 1; 275 | case 'k': case KEY_UP: *d = UPWARD; return 1; 276 | case 'l': case KEY_RIGHT: *d = RIGHT; return 1; 277 | case 'b': *d = DOWNLEFT; return 1; 278 | case 'y': *d = UPLEFT; return 1; 279 | case 'u': *d = UPRIGHT; return 1; 280 | case 'n': *d = DOWNRIGHT; return 1; 281 | case CANCEL: return 1; 282 | default: return 0; 283 | } 284 | } 285 | 286 | static bool check_hunger(bool msg_only) { 287 | if (rogue.moves_left == HUNGRY) { 288 | strcpy(hunger_str, "hungry"); 289 | message(hunger_str, 0); 290 | print_stats(STAT_HUNGER); 291 | } 292 | if (rogue.moves_left == WEAK) { 293 | strcpy(hunger_str, "weak"); 294 | message(hunger_str, 1); 295 | print_stats(STAT_HUNGER); 296 | } 297 | 298 | bool fainted = 0; 299 | if (rogue.moves_left <= FAINT) { 300 | if (rogue.moves_left == FAINT) { 301 | strcpy(hunger_str, "faint"); 302 | message(hunger_str, 1); 303 | print_stats(STAT_HUNGER); 304 | } 305 | short n = get_rand(0, (FAINT - rogue.moves_left)); 306 | if (n > 0) { 307 | fainted = 1; 308 | if (rand_percent(40)) 309 | rogue.moves_left++; 310 | message("you faint", 1); 311 | for (short i = 0; i < n; i++) { 312 | if (coin_toss()) 313 | mv_mons(); 314 | } 315 | message(you_can_move_again, 1); 316 | } 317 | } 318 | if (msg_only) 319 | return fainted; 320 | if (rogue.moves_left <= STARVE) 321 | killed_by(NULL, STARVATION); 322 | 323 | switch (e_rings) { 324 | case -1: 325 | rogue.moves_left -= (rogue.moves_left % 2); 326 | break; 327 | case 0: 328 | rogue.moves_left--; 329 | break; 330 | case 1: 331 | rogue.moves_left--; 332 | check_hunger(1); 333 | rogue.moves_left -= (rogue.moves_left % 2); 334 | break; 335 | case 2: 336 | rogue.moves_left--; 337 | check_hunger(1); 338 | rogue.moves_left--; 339 | break; 340 | } 341 | return fainted; 342 | } 343 | 344 | bool reg_move(void) { 345 | bool fainted = 0; 346 | if (rogue.moves_left <= HUNGRY || cur_level >= max_level) 347 | fainted = check_hunger(0); 348 | 349 | mv_mons(); 350 | 351 | if (++m_moves >= 120) { 352 | m_moves = 0; 353 | wanderer(); 354 | if (!noautosave) 355 | save_into_file(save_file); 356 | } 357 | if (halluc) { 358 | if (!(--halluc)) 359 | unhallucinate(); 360 | else 361 | hallucinate(); 362 | } 363 | if (blind) { 364 | if (!(--blind)) 365 | unblind(); 366 | } 367 | if (confused) { 368 | if (!(--confused)) 369 | unconfuse(); 370 | } 371 | if (bear_trap) 372 | bear_trap--; 373 | if (levitate) { 374 | if (!(--levitate)) { 375 | message("you float gently to the ground", 1); 376 | if (dungeon[rogue.row][rogue.col] & TRAP) { 377 | trap_player(rogue.row, rogue.col); 378 | } 379 | } 380 | } 381 | if (haste_self) { 382 | if (!(--haste_self)) 383 | message("you feel yourself slowing down", 0); 384 | } 385 | heal(); 386 | if (auto_search > 0) 387 | search(auto_search, auto_search); 388 | return fainted; 389 | } 390 | 391 | void rest(int count) { 392 | interrupted = 0; 393 | for (int i = 0; i < count; i++) { 394 | if (interrupted) 395 | break; 396 | reg_move(); 397 | } 398 | } 399 | 400 | static short gr_dir(void) { 401 | switch (get_rand(1, 8)) { 402 | case 1: return 'j'; 403 | case 2: return 'k'; 404 | case 3: return 'l'; 405 | case 4: return 'h'; 406 | case 5: return 'y'; 407 | case 6: return 'u'; 408 | case 7: return 'b'; 409 | case 8: return 'n'; 410 | default: return 0; // Unreachable 411 | } 412 | } 413 | 414 | static void heal(void) { 415 | static short heal_exp = -1, n, c = 0; 416 | static bool alt; 417 | 418 | if (rogue.hp_current == rogue.hp_max) { 419 | c = 0; 420 | return; 421 | } 422 | if (rogue.exp != heal_exp) { 423 | heal_exp = rogue.exp; 424 | switch (heal_exp) { 425 | case 1: n = 20; break; 426 | case 2: n = 18; break; 427 | case 3: n = 17; break; 428 | case 4: n = 14; break; 429 | case 5: n = 13; break; 430 | case 6: n = 10; break; 431 | case 7: n = 9; break; 432 | case 8: n = 8; break; 433 | case 9: n = 7; break; 434 | case 10: n = 4; break; 435 | case 11: n = 3; break; 436 | case 12: 437 | default: n = 2; 438 | } 439 | } 440 | if (++c >= n) { 441 | c = 0; 442 | rogue.hp_current++; 443 | if ((alt = !alt) != 0) 444 | rogue.hp_current++; 445 | if ((rogue.hp_current += regeneration) > rogue.hp_max) 446 | rogue.hp_current = rogue.hp_max; 447 | print_stats(STAT_HP); 448 | } 449 | } 450 | 451 | static bool can_turn(short nrow, short ncol) { 452 | if ((dungeon[nrow][ncol] & TUNNEL) && is_passable(nrow, ncol)) 453 | return 1; 454 | return 0; 455 | } 456 | 457 | static void turn_passage(short dir, bool fast) { 458 | short crow = rogue.row, ccol = rogue.col, turns = 0, ndir = 0; 459 | if (dir != 'h' && can_turn(crow, ccol + 1)) { 460 | turns++; 461 | ndir = 'l'; 462 | } 463 | if (dir != 'l' && can_turn(crow, ccol - 1)) { 464 | turns++; 465 | ndir = 'h'; 466 | } 467 | if (dir != 'k' && can_turn(crow + 1, ccol)) { 468 | turns++; 469 | ndir = 'j'; 470 | } 471 | if (dir != 'j' && can_turn(crow - 1, ccol)) { 472 | turns++; 473 | ndir = 'k'; 474 | } 475 | if (turns == 1) 476 | multiple_move_rogue(ndir - (fast ? 32 : 96)); 477 | } 478 | -------------------------------------------------------------------------------- /object.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)object.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/object.c,v 1.5 1999/11/30 03:49:25 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | object level_objects; 45 | unsigned short dungeon[DROWS][DCOLS]; 46 | short foods = 0; 47 | char *fruit = NULL; 48 | 49 | static object *free_list = NULL; 50 | 51 | fighter rogue = { 52 | INIT_AW, // armor, weapon 53 | INIT_RINGS, // rings 54 | INIT_HP,INIT_HP, // HP current,max 55 | INIT_STR, // Str current,max 56 | INIT_PACK, // pack 57 | INIT_GOLD, // gold 58 | INIT_EXP, // exp level,points 59 | 0, 0, // row, col 60 | INIT_CHAR, // char 61 | INIT_MOVES // moves 62 | }; 63 | 64 | struct id id_potions[POTIONS] = { 65 | {100, "blue \0 ", "of increase strength ", 0}, 66 | {250, "red \0 ", "of restore strength ", 0}, 67 | {100, "green \0 ", "of healing ", 0}, 68 | {200, "grey \0 ", "of extra healing ", 0}, 69 | { 10, "brown \0 ", "of poison ", 0}, 70 | {300, "clear \0 ", "of raise level ", 0}, 71 | { 10, "pink \0 ", "of blindness ", 0}, 72 | { 25, "white \0 ", "of hallucination ", 0}, 73 | {100, "purple \0 ", "of detect monster ", 0}, 74 | {100, "black \0 ", "of detect things ", 0}, 75 | { 10, "yellow \0 ", "of confusion ", 0}, 76 | { 80, "plaid \0 ", "of levitation ", 0}, 77 | {150, "burgundy \0 ", "of haste self ", 0}, 78 | {145, "beige \0 ", "of see invisible ", 0} 79 | }; 80 | 81 | struct id id_scrolls[SCROLS] = { 82 | {505, " ", "of protect armor ", 0}, 83 | {200, " ", "of hold monster ", 0}, 84 | {235, " ", "of enchant weapon ", 0}, 85 | {235, " ", "of enchant armor ", 0}, 86 | {175, " ", "of identify ", 0}, 87 | {190, " ", "of teleportation ", 0}, 88 | { 25, " ", "of sleep ", 0}, 89 | {610, " ", "of scare monster ", 0}, 90 | {210, " ", "of remove curse ", 0}, 91 | { 80, " ", "of create monster ",0}, 92 | { 25, " ", "of aggravate monster ",0}, 93 | {180, " ", "of magic mapping ", 0}, 94 | { 90, " ", "of confuse monster ", 0} 95 | }; 96 | 97 | struct id id_weapons[WEAPONS] = { 98 | {150, "short bow ", "", 0}, 99 | { 8, "darts ", "", 0}, 100 | { 15, "arrows ", "", 0}, 101 | { 27, "daggers ", "", 0}, 102 | { 35, "shurikens ", "", 0}, 103 | {360, "mace ", "", 0}, 104 | {470, "long sword ", "", 0}, 105 | {580, "two-handed sword ", "", 0} 106 | }; 107 | 108 | struct id id_armors[ARMORS] = { 109 | {300, "leather armor ", "", (UNIDENTIFIED)}, 110 | {300, "ring mail ", "", (UNIDENTIFIED)}, 111 | {400, "scale mail ", "", (UNIDENTIFIED)}, 112 | {500, "chain mail ", "", (UNIDENTIFIED)}, 113 | {600, "banded mail ", "", (UNIDENTIFIED)}, 114 | {600, "splint mail ", "", (UNIDENTIFIED)}, 115 | {700, "plate mail ", "", (UNIDENTIFIED)} 116 | }; 117 | 118 | struct id id_wands[WANDS] = { 119 | {25, " ", "of teleport away ", 0}, 120 | {50, " ", "of slow monster ", 0}, 121 | { 8, " ", "of invisibility ", 0}, 122 | {55, " ", "of polymorph ", 0}, 123 | { 2, " ", "of haste monster ", 0}, 124 | {20, " ", "of magic missile ", 0}, 125 | {20, " ", "of cancellation ", 0}, 126 | { 0, " ", "of do nothing ", 0}, 127 | {35, " ", "of drain life ", 0}, 128 | {20, " ", "of cold ", 0}, 129 | {20, " ", "of fire ", 0} 130 | }; 131 | 132 | struct id id_rings[RINGS] = { 133 | {250, " ", "of stealth ", 0}, 134 | {100, " ", "of teleportation ", 0}, 135 | {255, " ", "of regeneration ", 0}, 136 | {295, " ", "of slow digestion ", 0}, 137 | {200, " ", "of add strength ", 0}, 138 | {250, " ", "of sustain strength ", 0}, 139 | {250, " ", "of dexterity ", 0}, 140 | { 25, " ", "of adornment ", 0}, 141 | {300, " ", "of see invisible ", 0}, 142 | {290, " ", "of maintain armor ", 0}, 143 | {270, " ", "of searching ", 0}, 144 | }; 145 | 146 | static void gr_armor(object *); 147 | static void gr_potion(object *); 148 | static void gr_scroll(object *); 149 | static void gr_wand(object *); 150 | static void gr_weapon(object *, int); 151 | static unsigned short gr_what_is(void); 152 | static void make_party(void); 153 | static void plant_gold(short, short, bool); 154 | static void put_gold(void); 155 | static void rand_place(object *); 156 | 157 | void put_objects(void) { 158 | if (cur_level < max_level) 159 | return; 160 | 161 | short n = coin_toss() ? get_rand(2, 4) : get_rand(3, 5); 162 | while (rand_percent(33)) 163 | n++; 164 | 165 | if (party_room != NO_ROOM) 166 | make_party(); 167 | for (short i = 0; i < n; i++) 168 | rand_place(gr_object()); 169 | put_gold(); 170 | } 171 | 172 | static void put_gold(void) { 173 | for (int i = 0; i < MAXROOMS; i++) { 174 | bool is_maze = (rooms[i].is_room & R_MAZE) ? 1 : 0; 175 | bool is_room = (rooms[i].is_room & R_ROOM) ? 1 : 0; 176 | if (!(is_room || is_maze)) 177 | continue; 178 | 179 | if (is_maze || rand_percent(GOLD_PERCENT)) { 180 | for (int j = 0; j < 50; j++) { 181 | short row = get_rand(rooms[i].top_row + 1, 182 | rooms[i].bottom_row-1); 183 | short col = get_rand(rooms[i].left_col + 1, 184 | rooms[i].right_col-1); 185 | if (dungeon[row][col] == FLOOR || dungeon[row][col] == TUNNEL) { 186 | plant_gold(row, col, is_maze); 187 | break; 188 | } 189 | } 190 | } 191 | } 192 | } 193 | 194 | static void plant_gold(short row, short col, bool is_maze) { 195 | object *obj = alloc_object(); 196 | obj->row = row; obj->col = col; 197 | obj->what_is = GOLD; 198 | obj->quantity = get_rand((2 * cur_level), (16 * cur_level)); 199 | if (is_maze) 200 | obj->quantity += obj->quantity / 2; 201 | dungeon[row][col] |= OBJECT; 202 | add_to_pack(obj, &level_objects, 0); 203 | } 204 | 205 | void place_at(object *obj, int row, int col) { 206 | obj->row = row; 207 | obj->col = col; 208 | dungeon[row][col] |= OBJECT; 209 | add_to_pack(obj, &level_objects, 0); 210 | } 211 | 212 | object * object_at(object *pack, short row, short col) { 213 | object *obj = NULL; 214 | if (dungeon[row][col] & (MONSTER | OBJECT)) { 215 | obj = pack->next_object; 216 | 217 | while (obj && ((obj->row != row) || (obj->col != col))) 218 | obj = obj->next_object; 219 | if (!obj) 220 | message("object_at(): inconsistent", 1); 221 | } 222 | return obj; 223 | } 224 | 225 | object * get_letter_object(int ch) { 226 | object *obj = rogue.pack.next_object; 227 | while (obj && (obj->ichar != ch)) 228 | obj = obj->next_object; 229 | return obj; 230 | } 231 | 232 | void free_stuff(object *objlist) { 233 | while (objlist->next_object) { 234 | object *obj = objlist->next_object; 235 | objlist->next_object = objlist->next_object->next_object; 236 | free_object(obj); 237 | } 238 | } 239 | 240 | const char * name_of(const object *obj) { 241 | switch (obj->what_is) { 242 | case ARMOR: return "armor "; 243 | case RING: return "ring "; 244 | case AMULET: return "amulet "; 245 | case SCROL: return obj->quantity > 1 ? "scrolls " : "scroll "; 246 | case POTION: return obj->quantity > 1 ? "potions " : "potion "; 247 | case WAND: return is_wood[obj->which_kind] ? "staff " : "wand "; 248 | case FOOD: return obj->which_kind == RATION ? "food " : fruit; 249 | case WEAPON: 250 | switch (obj->which_kind) { 251 | case DART: return obj->quantity > 1 ? "darts " : "dart "; 252 | case ARROW: return obj->quantity > 1 ? "arrows " : "arrow "; 253 | case DAGGER: return obj->quantity > 1 ? "daggers " : "dagger "; 254 | case SHURIKEN: return obj->quantity > 1 ? "shurikens " : "shuriken "; 255 | default: return id_weapons[obj->which_kind].title; 256 | } 257 | break; 258 | default: return "unknown "; 259 | } 260 | } 261 | 262 | object * gr_object(void) { 263 | object *obj = alloc_object(); 264 | if (foods < (cur_level / 3)) { 265 | obj->what_is = FOOD; 266 | foods++; 267 | } else 268 | obj->what_is = gr_what_is(); 269 | switch (obj->what_is) { 270 | case SCROL: gr_scroll(obj); break; 271 | case POTION: gr_potion(obj); break; 272 | case WEAPON: gr_weapon(obj, 1); break; 273 | case ARMOR: gr_armor(obj); break; 274 | case WAND: gr_wand(obj); break; 275 | case FOOD: get_food(obj, 0); break; 276 | case RING: gr_ring(obj, 1); break; 277 | } 278 | return obj; 279 | } 280 | 281 | static unsigned short gr_what_is(void) { 282 | short percent = get_rand(1, 91); 283 | if (percent <= 30) return SCROL; 284 | else if (percent <= 60) return POTION; 285 | else if (percent <= 64) return WAND; 286 | else if (percent <= 74) return WEAPON; 287 | else if (percent <= 83) return ARMOR; 288 | else if (percent <= 88) return FOOD; 289 | else return RING; 290 | } 291 | 292 | static void gr_scroll(object *obj) { 293 | obj->what_is = SCROL; 294 | 295 | short percent = get_rand(0, 91); 296 | if (percent <= 5) obj->which_kind = PROTECT_ARMOR; 297 | else if (percent <= 10) obj->which_kind = HOLD_MONSTER; 298 | else if (percent <= 20) obj->which_kind = CREATE_MONSTER; 299 | else if (percent <= 35) obj->which_kind = IDENTIFY; 300 | else if (percent <= 43) obj->which_kind = TELEPORT; 301 | else if (percent <= 50) obj->which_kind = SLEEP; 302 | else if (percent <= 55) obj->which_kind = SCARE_MONSTER; 303 | else if (percent <= 64) obj->which_kind = REMOVE_CURSE; 304 | else if (percent <= 69) obj->which_kind = ENCH_ARMOR; 305 | else if (percent <= 74) obj->which_kind = ENCH_WEAPON; 306 | else if (percent <= 80) obj->which_kind = AGGRAVATE_MONSTER; 307 | else if (percent <= 86) obj->which_kind = CON_MON; 308 | else obj->which_kind = MAGIC_MAPPING; 309 | } 310 | 311 | static void gr_potion(object *obj) { 312 | obj->what_is = POTION; 313 | 314 | short percent = get_rand(1, 118); 315 | if (percent <= 5) obj->which_kind = RAISE_LEVEL; 316 | else if (percent <= 15) obj->which_kind = DETECT_OBJECTS; 317 | else if (percent <= 25) obj->which_kind = DETECT_MONSTER; 318 | else if (percent <= 35) obj->which_kind = INCREASE_STRENGTH; 319 | else if (percent <= 45) obj->which_kind = RESTORE_STRENGTH; 320 | else if (percent <= 55) obj->which_kind = HEALING; 321 | else if (percent <= 65) obj->which_kind = EXTRA_HEALING; 322 | else if (percent <= 75) obj->which_kind = BLINDNESS; 323 | else if (percent <= 85) obj->which_kind = HALLUCINATION; 324 | else if (percent <= 95) obj->which_kind = CONFUSION; 325 | else if (percent <= 105) obj->which_kind = POISON; 326 | else if (percent <= 110) obj->which_kind = LEVITATION; 327 | else if (percent <= 114) obj->which_kind = HASTE_SELF; 328 | else obj->which_kind = SEE_INVISIBLE; 329 | } 330 | 331 | static void gr_weapon(object *obj, int assign_wk) { 332 | obj->what_is = WEAPON; 333 | obj->hit_enchant = obj->d_enchant = 0; 334 | 335 | if (assign_wk) 336 | obj->which_kind = get_rand(0, WEAPONS - 1); 337 | 338 | if (obj->which_kind == ARROW || obj->which_kind == DAGGER || obj->which_kind == SHURIKEN || obj->which_kind == DART) { 339 | obj->quantity = get_rand(3, 15); 340 | obj->quiver = get_rand(0, 126); 341 | } else 342 | obj->quantity = 1; 343 | 344 | short percent = get_rand(1, 96); 345 | short increment; 346 | if (percent <= 16) 347 | increment = 1; 348 | else if (percent <= 32) { 349 | increment = -1; 350 | obj->is_cursed = 1; 351 | } 352 | 353 | short blessing = get_rand(1, 3); 354 | if (percent <= 32) { 355 | for (short i = 0; i < blessing; i++) { 356 | if (coin_toss()) 357 | obj->hit_enchant += increment; 358 | else 359 | obj->d_enchant += increment; 360 | } 361 | } 362 | switch (obj->which_kind) { 363 | case BOW: 364 | case DART: strcpy(&obj->damage[0], "1d1"); break; 365 | case ARROW: strcpy(&obj->damage[0], "1d2"); break; 366 | case DAGGER: strcpy(&obj->damage[0], "1d3"); break; 367 | case SHURIKEN: strcpy(&obj->damage[0], "1d4"); break; 368 | case MACE: strcpy(&obj->damage[0], "2d3"); break; 369 | case LONG_SWORD: strcpy(&obj->damage[0], "3d4"); break; 370 | case TWO_HANDED_SWORD: strcpy(&obj->damage[0], "4d5"); break; 371 | } 372 | } 373 | 374 | static void gr_armor(object *obj) { 375 | obj->what_is = ARMOR; 376 | obj->which_kind = get_rand(0, (ARMORS - 1)); 377 | obj->is_protected = 0; 378 | 379 | obj->class = obj->which_kind + 2; 380 | if (obj->which_kind == PLATE || obj->which_kind == SPLINT) 381 | obj->class--; 382 | 383 | obj->d_enchant = 0; 384 | short percent = get_rand(1, 100); 385 | short blessing = get_rand(1, 3); 386 | if (percent <= 16) { 387 | obj->is_cursed = 1; 388 | obj->d_enchant -= blessing; 389 | } else if (percent <= 33) 390 | obj->d_enchant += blessing; 391 | } 392 | 393 | static void gr_wand(object *obj) { 394 | obj->what_is = WAND; 395 | obj->which_kind = get_rand(0, (WANDS - 1)); 396 | obj->class = get_rand(3, 7); 397 | } 398 | 399 | void get_food(object *obj, bool force_ration) { 400 | obj->what_is = FOOD; 401 | 402 | if (force_ration || rand_percent(80)) 403 | obj->which_kind = RATION; 404 | else 405 | obj->which_kind = FRUIT; 406 | } 407 | 408 | void put_stairs(void) { 409 | short row, col; 410 | gr_row_col(&row, &col, (FLOOR | TUNNEL)); 411 | dungeon[row][col] |= STAIRS; 412 | } 413 | 414 | short get_armor_class(const object *obj) { 415 | if (obj) 416 | return obj->class + obj->d_enchant; 417 | return 0; 418 | } 419 | 420 | object * alloc_object(void) { 421 | object *obj; 422 | 423 | if (free_list) { 424 | obj = free_list; 425 | free_list = free_list->next_object; 426 | } else if (!(obj = (object *)malloc(sizeof(object)))) { 427 | message("cannot allocate object, saving game", 0); 428 | save_into_file(error_file); 429 | clean_up("alloc_object: save failed"); 430 | } 431 | obj->quantity = 1; 432 | obj->ichar = 'L'; 433 | obj->picked_up = obj->is_cursed = 0; 434 | obj->in_use_flags = NOT_USED; 435 | obj->identified = UNIDENTIFIED; 436 | strcpy(&obj->damage[0], "1d1"); 437 | return obj; 438 | } 439 | 440 | void free_object(object *obj) { 441 | obj->next_object = free_list; 442 | free_list = obj; 443 | } 444 | 445 | static void make_party(void) { 446 | party_room = gr_room(); 447 | 448 | short n = rand_percent(99) ? party_objects(party_room) : 11; 449 | if (rand_percent(99)) { 450 | party_monsters(party_room, n); 451 | } 452 | } 453 | 454 | void show_objects(void) { 455 | object *monster; 456 | 457 | object *obj = level_objects.next_object; 458 | 459 | while (obj) { 460 | short row = obj->row; 461 | short col = obj->col; 462 | 463 | short rc = get_mask_char(obj->what_is); 464 | 465 | if (dungeon[row][col] & MONSTER) { 466 | if ((monster = object_at(&level_monsters, row, col))) { 467 | monster->trail_char = rc; 468 | } 469 | } 470 | short mc = (short)mvinch(row, col); 471 | if (((mc < 'A') || (mc > 'Z')) && 472 | ((row != rogue.row) || (col != rogue.col))) { 473 | mvaddch(row, col, rc); 474 | } 475 | obj = obj->next_object; 476 | } 477 | 478 | monster = level_monsters.next_object; 479 | 480 | while (monster) { 481 | if (monster->m_flags & IMITATES) { 482 | mvaddch(monster->row, monster->col, (int)monster->disguise); 483 | } 484 | monster = monster->next_monster; 485 | } 486 | } 487 | 488 | void put_amulet(void) { 489 | object *obj; 490 | 491 | obj = alloc_object(); 492 | obj->what_is = AMULET; 493 | rand_place(obj); 494 | } 495 | 496 | static void rand_place(object *obj) { 497 | short row, col; 498 | gr_row_col(&row, &col, (FLOOR | TUNNEL)); 499 | place_at(obj, row, col); 500 | } 501 | 502 | void c_object_for_wizard(void) { 503 | if (pack_count(NULL) >= MAX_PACK_COUNT) { 504 | message("pack full", 0); 505 | return; 506 | } 507 | message("type of object?", 0); 508 | 509 | int ch; 510 | while (r_index("!?:)]=/,\033", (ch = rgetchar()), 0) == -1) 511 | sound_bell(); 512 | check_message(); 513 | 514 | if (ch == CANCEL) 515 | return; 516 | 517 | object *obj = alloc_object(); 518 | short max = 0; 519 | short num = 1; 520 | switch (ch) { 521 | case ':': get_food(obj, 0); num = 5; break; 522 | case ',': obj->what_is = AMULET; break; 523 | case '!': obj->what_is = POTION; max = POTIONS - 1; num = 5; break; 524 | case '?': obj->what_is = SCROL; max = SCROLS - 1; num = 5; break; 525 | case ')': gr_weapon(obj, 0); max = WEAPONS - 1; break; 526 | case ']': gr_armor(obj); max = ARMORS - 1; break; 527 | case '/': gr_wand(obj); max = WANDS - 1; break; 528 | case '=': obj->what_is = RING; max = RINGS - 1; break; 529 | } 530 | 531 | char buf[80]; 532 | if (ch != ',' && ch != ':') { 533 | GIL: 534 | if (get_input_line("which kind?", "", buf, 0)) { 535 | short wk = get_number(buf); 536 | if (wk >= 0 && wk <= max) { 537 | obj->which_kind = (unsigned short)wk; 538 | obj->quantity = num; 539 | if (obj->what_is == RING) 540 | gr_ring(obj, 0); 541 | } else { 542 | sound_bell(); 543 | goto GIL; 544 | } 545 | } else { 546 | free_object(obj); 547 | return; 548 | } 549 | } 550 | get_desc(obj, buf); 551 | message(buf, 0); 552 | add_to_pack(obj, &rogue.pack, 1); 553 | } 554 | -------------------------------------------------------------------------------- /pack.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)pack.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/pack.c,v 1.8 1999/11/30 03:49:25 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include 43 | #include "rogue.h" 44 | 45 | const char curse_message[] = "you can't, it appears to be cursed"; 46 | 47 | static object *check_duplicate(object *, object *); 48 | static bool is_pack_letter(int *, unsigned short *); 49 | static bool mask_pack(const object *, unsigned short); 50 | static int next_avail_ichar(void); 51 | 52 | object * add_to_pack(object *obj, object *pack, int condense) { 53 | object *op; 54 | if (condense) { 55 | if ((op = check_duplicate(obj, pack)) != NULL) { 56 | free_object(obj); 57 | return op; 58 | } else 59 | obj->ichar = next_avail_ichar(); 60 | } 61 | if (pack->next_object == 0) 62 | pack->next_object = obj; 63 | else { 64 | op = pack->next_object; 65 | while (op->next_object) 66 | op = op->next_object; 67 | op->next_object = obj; 68 | } 69 | obj->next_object = 0; 70 | return obj; 71 | } 72 | 73 | void take_from_pack(object *obj, object *pack) { 74 | while (pack->next_object != obj) 75 | pack = pack->next_object; 76 | pack->next_object = pack->next_object->next_object; 77 | } 78 | 79 | // Note: *status is set to 0 if the rogue attempts to pick up a scroll of 80 | // scare-monster and it turns to dust. *status is otherwise set to 1. 81 | object * pick_up(int row, int col, short *status) { 82 | if (levitate) { 83 | message("you're floating in the air!", 0); 84 | return NULL; 85 | } 86 | 87 | object *obj = object_at(&level_objects, row, col); 88 | if (!obj) { 89 | message("pick_up(): inconsistent", 1); 90 | return obj; 91 | } 92 | 93 | *status = 1; 94 | if (obj->what_is == SCROL && obj->which_kind == SCARE_MONSTER && obj->picked_up) { 95 | message("the scroll turns to dust as you pick it up", 0); 96 | dungeon[row][col] &= (~OBJECT); 97 | vanish(obj, 0, &level_objects); 98 | *status = 0; 99 | if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) { 100 | id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED; 101 | } 102 | return NULL; 103 | } 104 | if (obj->what_is == GOLD) { 105 | rogue.gold += obj->quantity; 106 | dungeon[row][col] &= ~(OBJECT); 107 | take_from_pack(obj, &level_objects); 108 | print_stats(STAT_GOLD); 109 | return obj; // obj will be free_object()ed in caller 110 | } 111 | if (pack_count(obj) >= MAX_PACK_COUNT) { 112 | message("pack too full", 1); 113 | return NULL; 114 | } 115 | dungeon[row][col] &= ~(OBJECT); 116 | take_from_pack(obj, &level_objects); 117 | obj = add_to_pack(obj, &rogue.pack, 1); 118 | obj->picked_up = 1; 119 | return obj; 120 | } 121 | 122 | void drop(void) { 123 | if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) { 124 | message("there's already something there", 0); 125 | return; 126 | } 127 | if (!rogue.pack.next_object) { 128 | message("you have nothing to drop", 0); 129 | return; 130 | } 131 | short ch; 132 | if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) 133 | return; 134 | 135 | object *obj = get_letter_object(ch); 136 | if (!obj) { 137 | message("no such item.", 0); 138 | return; 139 | } 140 | if (obj->in_use_flags & BEING_WIELDED) { 141 | if (obj->is_cursed) { 142 | message(curse_message, 0); 143 | return; 144 | } 145 | unwield(rogue.weapon); 146 | } else if (obj->in_use_flags & BEING_WORN) { 147 | if (obj->is_cursed) { 148 | message(curse_message, 0); 149 | return; 150 | } 151 | mv_aquatars(); 152 | unwear(rogue.armor); 153 | print_stats(STAT_ARMOR); 154 | } else if (obj->in_use_flags & ON_EITHER_HAND) { 155 | if (obj->is_cursed) { 156 | message(curse_message, 0); 157 | return; 158 | } 159 | un_put_on(obj); 160 | } 161 | obj->row = rogue.row; 162 | obj->col = rogue.col; 163 | 164 | if ((obj->quantity > 1) && (obj->what_is != WEAPON)) { 165 | obj->quantity--; 166 | object *new = alloc_object(); 167 | *new = *obj; 168 | new->quantity = 1; 169 | obj = new; 170 | } else { 171 | obj->ichar = 'L'; 172 | take_from_pack(obj, &rogue.pack); 173 | } 174 | place_at(obj, rogue.row, rogue.col); 175 | messagef(0, "dropped %s", get_desc_str(obj)); 176 | reg_move(); 177 | } 178 | 179 | static object * check_duplicate(object *obj, object *pack) { 180 | if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) 181 | return 0; 182 | if (obj->what_is == FOOD && obj->which_kind == FRUIT) 183 | return 0; 184 | 185 | object *op = pack->next_object; 186 | while (op) { 187 | if (op->what_is == obj->what_is && op->which_kind == obj->which_kind) { 188 | if (obj->what_is != WEAPON || ( 189 | obj->what_is == WEAPON && ( 190 | obj->which_kind == ARROW || obj->which_kind == DAGGER || 191 | obj->which_kind == DART || obj->which_kind == SHURIKEN 192 | ) && 193 | obj->quiver == op->quiver 194 | ) 195 | ) { 196 | op->quantity += obj->quantity; 197 | return op; 198 | } 199 | } 200 | op = op->next_object; 201 | } 202 | return 0; 203 | } 204 | 205 | static int next_avail_ichar(void) { 206 | bool ichars[26]; 207 | for (int i = 0; i < 26; i++) 208 | ichars[i] = 0; 209 | 210 | object *obj = rogue.pack.next_object; 211 | while (obj) { 212 | if (obj->ichar >= 'a' && obj->ichar <= 'z') 213 | ichars[(obj->ichar - 'a')] = 1; 214 | obj = obj->next_object; 215 | } 216 | for (int i = 0; i < 26; i++) { 217 | if (!ichars[i]) 218 | return i + 'a'; 219 | } 220 | return '?'; 221 | } 222 | 223 | void wait_for_ack(void) { 224 | if (!isatty(0) || !isatty(1)) 225 | return; 226 | while (true) { 227 | int c = rgetchar(); 228 | if (c == ' ' || c == 0x1b || c == 0x0d) 229 | break; 230 | } 231 | } 232 | 233 | short pack_letter(const char *prompt, unsigned short mask) { 234 | if (!mask_pack(&rogue.pack, mask)) { 235 | message("nothing appropriate", 0); 236 | return CANCEL; 237 | } 238 | 239 | message(prompt, 0); 240 | inventory(&rogue.pack, mask, 1); 241 | for (;;) { 242 | int ch = rgetchar(); 243 | if (is_pack_letter(&ch, &mask)) { 244 | check_message(); 245 | clear_inventory(); 246 | return ch; 247 | } 248 | sound_bell(); 249 | } 250 | } 251 | 252 | void take_off(void) { 253 | if (rogue.armor) { 254 | if (rogue.armor->is_cursed) 255 | message(curse_message, 0); 256 | else { 257 | mv_aquatars(); 258 | object *obj = rogue.armor; // Save so get_desc() gets right message. 259 | unwear(rogue.armor); 260 | messagef(0, "was wearing %s", get_desc_str(obj)); 261 | print_stats(STAT_ARMOR); 262 | reg_move(); 263 | } 264 | } else { 265 | message("not wearing any", 0); 266 | } 267 | } 268 | 269 | void wear(void) { 270 | if (rogue.armor) { 271 | message("you're already wearing some", 0); 272 | return; 273 | } 274 | 275 | short ch = pack_letter("wear what?", ARMOR); 276 | if (ch == CANCEL) 277 | return; 278 | 279 | object *obj = get_letter_object(ch); 280 | if (!obj) { 281 | message("no such item.", 0); 282 | return; 283 | } 284 | if (obj->what_is != ARMOR) { 285 | message("you can't wear that", 0); 286 | return; 287 | } 288 | obj->identified = 1; 289 | messagef(0, "wearing %s", get_desc_str(obj)); 290 | do_wear(obj); 291 | print_stats(STAT_ARMOR); 292 | reg_move(); 293 | } 294 | 295 | void unwear(object *obj) { 296 | if (obj) 297 | obj->in_use_flags &= (~BEING_WORN); 298 | rogue.armor = NULL; 299 | } 300 | 301 | void do_wear(object *obj) { 302 | rogue.armor = obj; 303 | obj->in_use_flags |= BEING_WORN; 304 | obj->identified = 1; 305 | } 306 | 307 | void wield(void) { 308 | if (rogue.weapon && rogue.weapon->is_cursed) { 309 | message(curse_message, 0); 310 | return; 311 | } 312 | 313 | short ch = pack_letter("wield what?", WEAPON); 314 | if (ch == CANCEL) 315 | return; 316 | 317 | object *obj = get_letter_object(ch); 318 | if (!obj) { 319 | message("No such item.", 0); 320 | return; 321 | } 322 | if (obj->what_is & (ARMOR | RING)) { 323 | messagef(0, "you can't wield %s", ((obj->what_is == ARMOR) ? "armor" : "rings")); 324 | return; 325 | } 326 | if (obj->in_use_flags & BEING_WIELDED) 327 | message("in use", 0); 328 | else { 329 | unwield(rogue.weapon); 330 | messagef(0, "wielding %s", get_desc_str(obj)); 331 | do_wield(obj); 332 | reg_move(); 333 | } 334 | } 335 | 336 | void do_wield(object *obj) { 337 | rogue.weapon = obj; 338 | obj->in_use_flags |= BEING_WIELDED; 339 | } 340 | 341 | void unwield(object *obj) { 342 | if (obj) 343 | obj->in_use_flags &= (~BEING_WIELDED); 344 | rogue.weapon = NULL; 345 | } 346 | 347 | void call_it(void) { 348 | short ch = pack_letter("call what?", (SCROL | POTION | WAND | RING)); 349 | if (ch == CANCEL) 350 | return; 351 | 352 | object *obj = get_letter_object(ch); 353 | if (!obj) { 354 | message("no such item.", 0); 355 | return; 356 | } 357 | if (!(obj->what_is & (SCROL | POTION | WAND | RING))) { 358 | message("surely you already know what that's called", 0); 359 | return; 360 | } 361 | 362 | char buf[MAX_TITLE_LENGTH+2]; 363 | struct id *id_table = get_id_table(obj); 364 | if (get_input_line("call it:", "", buf, 1)) { 365 | id_table[obj->which_kind].id_status = CALLED; 366 | strcpy(id_table[obj->which_kind].title, buf); 367 | } 368 | message(id_table[obj->which_kind].title, 0); 369 | } 370 | 371 | short pack_count(const object *new_obj) { 372 | short count = 0; 373 | object *obj = rogue.pack.next_object; 374 | while (obj) { 375 | if (obj->what_is != WEAPON) 376 | count += obj->quantity; 377 | else if (!new_obj) 378 | count++; 379 | else if (new_obj->what_is != WEAPON || 380 | (obj->which_kind != ARROW && obj->which_kind != DAGGER && obj->which_kind != DART && obj->which_kind != SHURIKEN) || 381 | new_obj->which_kind != obj->which_kind || obj->quiver != new_obj->quiver 382 | ) { 383 | count++; 384 | } 385 | obj = obj->next_object; 386 | } 387 | return count; 388 | } 389 | 390 | static bool mask_pack(const object *pack, unsigned short mask) { 391 | while (pack->next_object) { 392 | pack = pack->next_object; 393 | if (pack->what_is & mask) 394 | return 1; 395 | } 396 | return 0; 397 | } 398 | 399 | static bool is_pack_letter(int *c, unsigned short *mask) { 400 | switch (*c) { 401 | case '?': *mask = SCROL; return 1; 402 | case '!': *mask = POTION; return 1; 403 | case ':': *mask = FOOD; return 1; 404 | case '=': *mask = RING; return 1; 405 | case ')': *mask = WEAPON; return 1; 406 | case ']': *mask = ARMOR; return 1; 407 | case '/': *mask = WAND; return 1; 408 | case ',': *mask = AMULET; return 1; 409 | } 410 | return (*c >= 'a' && *c <= 'z') || *c == CANCEL; 411 | } 412 | 413 | bool has_amulet(void) { 414 | return mask_pack(&rogue.pack, AMULET); 415 | } 416 | 417 | void kick_into_pack(void) { 418 | if (!(dungeon[rogue.row][rogue.col] & OBJECT)) { 419 | message("nothing here", 0); 420 | return; 421 | } 422 | 423 | short stat = 0; 424 | object *obj = pick_up(rogue.row, rogue.col, &stat); 425 | if (obj != NULL) { 426 | if (obj->what_is == GOLD) { 427 | message(get_desc_str(obj), 0); 428 | free_object(obj); 429 | } else 430 | messagef(0, "%s (%c)", get_desc_str(obj), obj->ichar); 431 | } 432 | if (obj || !stat) 433 | reg_move(); 434 | } 435 | -------------------------------------------------------------------------------- /play.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)play.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/play.c,v 1.3 1999/11/30 03:49:26 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | bool interrupted = 0; 45 | 46 | static const char unknown_command[] = "unknown command"; 47 | 48 | void play_level(void) { 49 | int count; 50 | for (;;) { 51 | interrupted = 0; 52 | if (hit_message[0]) { 53 | message(hit_message, 1); 54 | hit_message[0] = 0; 55 | } 56 | if (trap_door) { 57 | trap_door = 0; 58 | return; 59 | } 60 | move(rogue.row, rogue.col); 61 | refresh(); 62 | 63 | int ch = rgetchar(); 64 | clear_messages: 65 | check_message(); 66 | count = 0; 67 | switch_start: 68 | switch (ch) { 69 | // ^R and & handled in rgetchar(), they work in any location. 70 | case ' ': break; 71 | case 'f': fight(0); break; 72 | case 'F': fight(1); break; 73 | case 'e': eat(); break; 74 | case 'q': quaff(); break; 75 | case 'r': read_scroll(); break; 76 | case 'm': move_onto(); break; 77 | case ',': kick_into_pack(); break; 78 | case 'd': drop(); break; 79 | case 'P': put_on_ring(); break; 80 | case 'R': remove_ring(); break; 81 | case '=': inv_rings(); break; 82 | case '^': id_trap(); break; 83 | case '/': id_type(); break; 84 | case '?': show_help(); break; 85 | case 'o': edit_opts(); break; 86 | case 'I': single_inv(0); break; 87 | case 'T': take_off(); break; 88 | case 'W': wear(); break; 89 | case 'w': wield(); break; 90 | case 'c': call_it(); break; 91 | case 'z': zapp(); break; 92 | case 't': throw(); break; 93 | case 'Q': quit(0); break; 94 | case 'S': save_game(); break; 95 | case '.': rest(count > 0 ? count : 1); break; 96 | case 's': search(count > 0 ? count : 1, 0); break; 97 | case 'i': inventory(&rogue.pack, ALL_OBJECTS, 0); break; 98 | case '!': messagef(0, "seed: %ld", game_seed); break; 99 | case '>': if (drop_check()) { return; } break; 100 | case '<': if (check_up()) { return; } break; 101 | case 'v': message("rogue-clone: Version III. (Tim Stoehr was here), tektronix!zeus!tims", 0); break; 102 | case ')': 103 | case ']': inv_armor_weapon(ch == ')'); break; 104 | case 'A' - 0x40: show_average_hp(); break; 105 | 106 | case KEY_UP: one_move_rogue('k', 1); break; 107 | case KEY_RIGHT: one_move_rogue('l', 1); break; 108 | case KEY_DOWN: one_move_rogue('j', 1); break; 109 | case KEY_LEFT: one_move_rogue('h', 1); break; 110 | case '1': case '2': case '3': case '4': case '6': case '7': case '8': case '9': 111 | ch = "bjnh lyku"[ch - '1']; // fallthrough 112 | case 'h': case 'j': case 'k': case 'l': case 'y': case 'u': case 'n': case 'b': 113 | one_move_rogue(ch, 1); break; 114 | 115 | case 'M': 116 | case 'M' - 0x40: { 117 | bool ctrl = (ch & 0x40) == 0x40; 118 | message("direction? ", 0); 119 | ch = rgetchar(); 120 | switch (ch) { 121 | case KEY_UP: ch = 'k'; break; 122 | case KEY_RIGHT: ch = 'l'; break; 123 | case KEY_DOWN: ch = 'j'; break; 124 | case KEY_LEFT: ch = 'h'; break; 125 | } 126 | if (ch >= '1' && ch <= '9' && ch != '5') 127 | ch = "bjnh lyku"[ch - '1']; 128 | ch ^= (ctrl ? 0x20 : 0x60); 129 | // fallthrough 130 | } 131 | case 'H': case 'H' - 0x40: 132 | case 'J': case 'J' - 0x40: 133 | case 'K': case 'K' - 0x40: 134 | case 'L': case 'L' - 0x40: 135 | case 'B': case 'B' - 0x40: 136 | case 'Y': case 'Y' - 0x40: 137 | case 'U': case 'U' - 0x40: 138 | case 'N': case 'N' - 0x40: 139 | multiple_move_rogue(ch); break; 140 | 141 | case 'P' - 0x40: 142 | do { 143 | remessage(count++); 144 | ch = rgetchar(); 145 | } while (ch == '\020'); 146 | goto clear_messages; 147 | break; 148 | 149 | case 'C': 150 | move(rogue.row, rogue.col); 151 | refresh(); 152 | check_message(); 153 | message("repeat:", 1); 154 | for (ch = rgetchar(); is_digit(ch); ch = rgetchar()) { 155 | if (count < 100) 156 | count = (10 * count) + (ch - '0'); 157 | check_message(); 158 | messagef(1, "repeat: %d", count); 159 | } 160 | check_message(); 161 | if (ch != CANCEL) 162 | goto switch_start; 163 | break; 164 | 165 | case 'W' - 0x40: wizardize(); break; 166 | case 'I' - 0x40: if (wizard) inventory(&level_objects, ALL_OBJECTS, 0); else message(unknown_command, 0); break; 167 | case 'S' - 0x40: if (wizard) draw_magic_map(); else message(unknown_command, 0); break; 168 | case 'T' - 0x40: if (wizard) show_traps(); else message(unknown_command, 0); break; 169 | case 'O' - 0x40: if (wizard) show_objects(); else message(unknown_command, 0); break; 170 | case 'E' - 0x40: if (wizard) c_object_for_wizard(); else message(unknown_command, 0); break; 171 | case 'V' - 0x40: if (wizard) show_monsters(); else message(unknown_command, 0); break; 172 | default: 173 | message(unknown_command, 0); 174 | break; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /ring.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)ring.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/ring.c,v 1.3 1999/11/30 03:49:26 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | static const char left_or_right[] = "left or right hand?"; 45 | 46 | short stealthy; 47 | short r_rings; 48 | short add_strength; 49 | short e_rings; 50 | short regeneration; 51 | short ring_exp; 52 | short auto_search; 53 | bool r_teleport; 54 | bool r_see_invisible; 55 | bool sustain_strength; 56 | bool maintain_armor; 57 | 58 | void put_on_ring(void) { 59 | if (r_rings == 2) { 60 | message("wearing two rings already", 0); 61 | return; 62 | } 63 | 64 | int ch = pack_letter("put on what?", RING); 65 | if (ch == CANCEL) 66 | return; 67 | 68 | object *ring = get_letter_object(ch); 69 | if (!ring) { 70 | message("no such item.", 0); 71 | return; 72 | } 73 | 74 | if (!(ring->what_is & RING)) { 75 | message("that's not a ring", 0); 76 | return; 77 | } 78 | if (ring->in_use_flags & (ON_LEFT_HAND | ON_RIGHT_HAND)) { 79 | message("that ring is already being worn", 0); 80 | return; 81 | } 82 | if (r_rings == 1) 83 | ch = (rogue.left_ring ? 'r' : 'l'); 84 | else { 85 | message(left_or_right, 0); 86 | do { 87 | ch = rgetchar(); 88 | } while (ch != CANCEL && ch != 'l' && ch != 'r' && ch != '\n' && ch != '\r'); 89 | } 90 | if (ch != 'l' && ch != 'r') { 91 | check_message(); 92 | return; 93 | } 94 | if ((ch == 'l' && rogue.left_ring) || (ch == 'r' && rogue.right_ring)) { 95 | check_message(); 96 | message("there's already a ring on that hand", 0); 97 | return; 98 | } 99 | 100 | do_put_on(ring, ch == 'l'); 101 | ring_stats(1); 102 | check_message(); 103 | message(get_desc_str(ring), 0); 104 | reg_move(); 105 | } 106 | 107 | // Do not call ring_stats() from within do_put_on(). It will cause serious 108 | // problems when do_put_on() is called from read_pack() in restore(). 109 | void do_put_on(object *ring, bool on_left) { 110 | if (on_left) { 111 | ring->in_use_flags |= ON_LEFT_HAND; 112 | rogue.left_ring = ring; 113 | } else { 114 | ring->in_use_flags |= ON_RIGHT_HAND; 115 | rogue.right_ring = ring; 116 | } 117 | } 118 | 119 | void remove_ring(void) { 120 | if (r_rings == 0) { 121 | message("not wearing any rings", 0); 122 | return; 123 | } 124 | 125 | bool left = 0; 126 | if (rogue.left_ring && !rogue.right_ring) 127 | left = 1; 128 | else if (!rogue.left_ring && rogue.right_ring) 129 | left = 0; 130 | else { 131 | message(left_or_right, 0); 132 | int ch; 133 | do { 134 | ch = rgetchar(); 135 | } while (ch != CANCEL && ch != 'l' && ch != 'r' && ch != '\n' && ch != '\r'); 136 | left = (ch == 'l'); 137 | check_message(); 138 | } 139 | 140 | object *ring = left ? rogue.left_ring : rogue.right_ring; 141 | if (ring->is_cursed) 142 | message(curse_message, 0); 143 | else { 144 | un_put_on(ring); 145 | messagef(0, "removed %s", get_desc_str(ring)); 146 | reg_move(); 147 | } 148 | } 149 | 150 | void un_put_on(object *ring) { 151 | if (ring && (ring->in_use_flags & ON_LEFT_HAND)) { 152 | ring->in_use_flags &= (~ON_LEFT_HAND); 153 | rogue.left_ring = NULL; 154 | } else if (ring && (ring->in_use_flags & ON_RIGHT_HAND)) { 155 | ring->in_use_flags &= (~ON_RIGHT_HAND); 156 | rogue.right_ring = NULL; 157 | } 158 | ring_stats(1); 159 | } 160 | 161 | void gr_ring(object *ring, bool assign_wk) { 162 | ring->what_is = RING; 163 | if (assign_wk) 164 | ring->which_kind = get_rand(0, (RINGS - 1)); 165 | ring->class = 0; 166 | 167 | switch (ring->which_kind) { 168 | case R_TELEPORT: 169 | ring->is_cursed = 1; 170 | break; 171 | case ADD_STRENGTH: 172 | case DEXTERITY: 173 | while ((ring->class = (get_rand(0, 4) - 2)) == 0) 174 | ; 175 | ring->is_cursed = (ring->class < 0); 176 | break; 177 | case ADORNMENT: 178 | ring->is_cursed = coin_toss(); 179 | break; 180 | } 181 | } 182 | 183 | void inv_rings(void) { 184 | if (r_rings == 0) 185 | message("not wearing any rings", 0); 186 | else { 187 | if (rogue.left_ring) 188 | message(get_desc_str(rogue.left_ring), 0); 189 | if (rogue.right_ring) 190 | message(get_desc_str(rogue.right_ring), 0); 191 | } 192 | if (wizard) 193 | messagef(0, "ste %hi, r_r %hi, e_r %hi, r_t %d, s_s %d" 194 | ", a_s %hi, reg %hi, r_e %hi, s_i %d, m_a %d, aus %hi", 195 | stealthy, r_rings, e_rings, r_teleport, sustain_strength, 196 | add_strength, regeneration, ring_exp, r_see_invisible, 197 | maintain_armor, auto_search); 198 | } 199 | 200 | void ring_stats(bool pr) { 201 | stealthy = 0; 202 | r_rings = 0; 203 | e_rings = 0; 204 | r_teleport = 0; 205 | sustain_strength = 0; 206 | add_strength = 0; 207 | regeneration = 0; 208 | ring_exp = 0; 209 | r_see_invisible = 0; 210 | maintain_armor = 0; 211 | auto_search = 0; 212 | 213 | object *ring; 214 | for (short i = 0; i < 2; i++) { 215 | if (!(ring = ((i == 0) ? rogue.left_ring : rogue.right_ring))) { 216 | continue; 217 | } 218 | r_rings++; 219 | e_rings++; 220 | switch (ring->which_kind) { 221 | case STEALTH: stealthy++; break; 222 | case R_TELEPORT: r_teleport = 1; break; 223 | case REGENERATION: regeneration++; break; 224 | case SLOW_DIGEST: e_rings -= 2; break; 225 | case ADD_STRENGTH: add_strength += ring->class; break; 226 | case SUSTAIN_STRENGTH: sustain_strength = 1; break; 227 | case DEXTERITY: ring_exp += ring->class; break; 228 | case R_SEE_INVISIBLE: r_see_invisible = 1; break; 229 | case MAINTAIN_ARMOR: maintain_armor = 1; break; 230 | case SEARCHING: auto_search += 2; break; 231 | case ADORNMENT: /* Does nothing */ break; 232 | } 233 | } 234 | if (pr) { 235 | print_stats(STAT_STRENGTH); 236 | relight(); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /room.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)room.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/room.c,v 1.7 1999/11/30 03:49:26 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | room rooms[MAXROOMS]; 45 | 46 | static bool rooms_visited[MAXROOMS]; 47 | 48 | #define NOPTS 9 49 | static const struct option { 50 | const char *name; 51 | const char *prompt; 52 | bool is_bool; 53 | bool is_int; 54 | char **strval; 55 | bool *bval; 56 | int *ival; 57 | } options[NOPTS] = { 58 | {"passgo", "Follow turnings in passageways", 1, 0, NULL, &passgo, NULL}, 59 | {"noskull", "Don't print skull when killed", 1, 0, NULL, &no_skull, NULL}, 60 | {"askquit", "Ask before exiting on SIGQUIT", 1, 0, NULL, &ask_quit, NULL}, 61 | {"lowhealth", "Low health warning below", 0, 1, NULL, NULL, &low_health_warn}, 62 | {"fruit", "Fruit name", 0, 0, &fruit, NULL, NULL}, 63 | {"noautosave", "Don't autosave", 1, 0, NULL, &noautosave, NULL}, 64 | {"revshift", "Reverse shift+dir and ctrl+dir", 1, 0, NULL, &revshift, NULL}, 65 | // Keep these two last so there won't be any gaps when lock is set. 66 | {"name", "Name", 0, 0, &nick_name, NULL, NULL}, 67 | {"file", "Save file", 0, 0, &save_file, NULL, NULL}, 68 | }; 69 | 70 | static bool get_oth_room(short, short *, short *); 71 | static void opt_erase(int); 72 | static void opt_go(int); 73 | static void opt_show(int); 74 | static void visit_rooms(int); 75 | 76 | void light_up_room(int rn) { 77 | if (blind) 78 | return; 79 | 80 | for (short i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { 81 | for (short j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { 82 | if (dungeon[i][j] & MONSTER) { 83 | object *monster; 84 | if ((monster = object_at(&level_monsters, i, j))) { 85 | dungeon[monster->row][monster->col] &= (~MONSTER); 86 | monster->trail_char = get_dungeon_char(monster->row, monster->col); 87 | dungeon[monster->row][monster->col] |= MONSTER; 88 | } 89 | } 90 | mvaddch(i, j, get_dungeon_char(i, j)); 91 | } 92 | } 93 | mvaddch(rogue.row, rogue.col, rogue.fchar); 94 | } 95 | 96 | void light_passage(int row, int col) { 97 | if (blind) 98 | return; 99 | 100 | short i_end = (row < (DROWS - 2)) ? 1 : 0; 101 | short j_end = (col < (DCOLS - 1)) ? 1 : 0; 102 | for (short i = ((row > MIN_ROW) ? -1 : 0); i <= i_end; i++) { 103 | for (short j = ((col > 0) ? -1 : 0); j <= j_end; j++) { 104 | if (can_move(row, col, row + i, col + j)) 105 | mvaddch(row + i, col + j, get_dungeon_char(row + i, col + j)); 106 | } 107 | } 108 | } 109 | 110 | void darken_room(short rn) { 111 | for (short i = rooms[rn].top_row + 1; i < rooms[rn].bottom_row; i++) { 112 | for (short j = rooms[rn].left_col + 1; j < rooms[rn].right_col; j++) { 113 | if (blind) { 114 | mvaddch(i, j, ' '); 115 | continue; 116 | } 117 | 118 | if (!(dungeon[i][j] & (OBJECT | STAIRS)) && !(detect_monster && (dungeon[i][j] & MONSTER))) { 119 | if (!imitating(i, j)) 120 | mvaddch(i, j, ' '); 121 | if ((dungeon[i][j] & TRAP) && (!(dungeon[i][j] & HIDDEN))) 122 | mvaddch(i, j, '^'); 123 | } 124 | } 125 | } 126 | } 127 | 128 | char get_dungeon_char(int row, int col) { 129 | unsigned short mask = dungeon[row][col]; 130 | 131 | if (mask & MONSTER) 132 | return gmc_row_col(row, col); 133 | 134 | if (mask & OBJECT) { 135 | object *obj = object_at(&level_objects, row, col); 136 | return get_mask_char(obj->what_is); 137 | } 138 | if (mask & (TUNNEL | STAIRS | HORWALL | VERTWALL | FLOOR | DOOR)) { 139 | if ((mask & (TUNNEL| STAIRS)) && (!(mask & HIDDEN))) 140 | return ((mask & STAIRS) ? '%' : '#'); 141 | if (mask & HORWALL) 142 | return '-'; 143 | if (mask & VERTWALL) 144 | return '|'; 145 | if (mask & FLOOR) { 146 | if (mask & TRAP) { 147 | if (!(dungeon[row][col] & HIDDEN)) 148 | return '^'; 149 | } 150 | return '.'; 151 | } 152 | if (mask & DOOR) { 153 | if (mask & HIDDEN) { 154 | if ((col > 0 && (dungeon[row][col-1] & HORWALL)) || (col < (DCOLS-1) && (dungeon[row][col+1] & HORWALL))) 155 | return '-'; 156 | else 157 | return '|'; 158 | } else 159 | return '+'; 160 | } 161 | } 162 | return ' '; 163 | } 164 | 165 | char get_mask_char(unsigned short mask) { 166 | switch (mask) { 167 | case SCROL: return '?'; 168 | case POTION: return '!'; 169 | case GOLD: return '*'; 170 | case FOOD: return ':'; 171 | case WAND: return '/'; 172 | case ARMOR: return ']'; 173 | case WEAPON: return ')'; 174 | case RING: return '='; 175 | case AMULET: return ','; 176 | default: return '~'; // unknown, something is wrong 177 | } 178 | } 179 | 180 | void gr_row_col(short *row, short *col, unsigned short mask) { 181 | short rn, r, c; 182 | do { 183 | r = get_rand(MIN_ROW, DROWS-2); 184 | c = get_rand(0, DCOLS-1); 185 | rn = get_room_number(r, c); 186 | } while (rn == NO_ROOM || 187 | (!(dungeon[r][c] & mask)) || 188 | (dungeon[r][c] & (~mask)) || 189 | (!(rooms[rn].is_room & (R_ROOM | R_MAZE))) || 190 | ((r == rogue.row) && (c == rogue.col))); 191 | 192 | *row = r; 193 | *col = c; 194 | } 195 | 196 | short gr_room(void) { 197 | short i; 198 | do { 199 | i = get_rand(0, MAXROOMS-1); 200 | } while (!(rooms[i].is_room & (R_ROOM | R_MAZE))); 201 | return i; 202 | } 203 | 204 | short party_objects(short rn) { 205 | short N = ((rooms[rn].bottom_row - rooms[rn].top_row) - 1) * ((rooms[rn].right_col - rooms[rn].left_col) - 1); 206 | short n = get_rand(5, 10); 207 | if (n > N) 208 | n = N - 2; 209 | 210 | short row = 0, col = 0; 211 | short nf = 0; 212 | bool found; 213 | for (short i = 0; i < n; i++) { 214 | for (short j = found = 0; ((!found) && (j < 250)); j++) { 215 | row = get_rand(rooms[rn].top_row + 1, rooms[rn].bottom_row - 1); 216 | col = get_rand(rooms[rn].left_col + 1, rooms[rn].right_col - 1); 217 | if (dungeon[row][col] == FLOOR || dungeon[row][col] == TUNNEL) 218 | found = 1; 219 | } 220 | if (found) { 221 | object *obj = gr_object(); 222 | place_at(obj, row, col); 223 | nf++; 224 | } 225 | } 226 | return nf; 227 | } 228 | 229 | short get_room_number(int row, int col) { 230 | for (short i = 0; i < MAXROOMS; i++) { 231 | if (row >= rooms[i].top_row && row <= rooms[i].bottom_row && col >= rooms[i].left_col && col <= rooms[i].right_col) 232 | return i; 233 | } 234 | return NO_ROOM; 235 | } 236 | 237 | bool is_all_connected(void) { 238 | short starting_room = 0; 239 | for (short i = 0; i < MAXROOMS; i++) { 240 | rooms_visited[i] = 0; 241 | if (rooms[i].is_room & (R_ROOM | R_MAZE)) 242 | starting_room = i; 243 | } 244 | visit_rooms(starting_room); 245 | 246 | for (short i = 0; i < MAXROOMS; i++) { 247 | if ((rooms[i].is_room & (R_ROOM | R_MAZE)) && !rooms_visited[i]) 248 | return 0; 249 | } 250 | return 1; 251 | } 252 | 253 | static void visit_rooms(int rn) { 254 | rooms_visited[rn] = 1; 255 | for (short i = 0; i < 4; i++) { 256 | short oth_rn = rooms[rn].doors[i].oth_room; 257 | if (oth_rn >= 0 && !rooms_visited[oth_rn]) 258 | visit_rooms(oth_rn); 259 | } 260 | } 261 | 262 | void draw_magic_map(void) { 263 | for (short i = 0; i < DROWS; i++) { 264 | for (short j = 0; j < DCOLS; j++) { 265 | unsigned short s = dungeon[i][j]; 266 | if (s & (HORWALL | VERTWALL | DOOR | TUNNEL | TRAP | STAIRS | MONSTER)) { 267 | short ch = (short)mvinch(i, j); 268 | if ((ch == ' ') || ((ch >= 'A') && (ch <= 'Z')) || (s & (TRAP | HIDDEN))) { 269 | short och = ch; 270 | dungeon[i][j] &= (~HIDDEN); 271 | if (s & HORWALL) ch = '-'; 272 | else if (s & VERTWALL) ch = '|'; 273 | else if (s & DOOR) ch = '+'; 274 | else if (s & TRAP) ch = '^'; 275 | else if (s & STAIRS) ch = '%'; 276 | else if (s & TUNNEL) ch = '#'; 277 | else continue; 278 | 279 | if ((!(s & MONSTER)) || och == ' ') 280 | addch(ch); 281 | 282 | if (s & MONSTER) { 283 | object *monster; 284 | if ((monster = object_at(&level_monsters, i, j))) 285 | monster->trail_char = ch; 286 | } 287 | } 288 | } 289 | } 290 | } 291 | } 292 | 293 | void dr_course(object *monster, bool entering, short row, short col) { 294 | monster->row = row; 295 | monster->col = col; 296 | if (mon_sees(monster, rogue.row, rogue.col)) { 297 | monster->trow = NO_ROOM; 298 | return; 299 | } 300 | 301 | short rn = get_room_number(row, col); 302 | if (entering) { // entering room 303 | // look for door to some other room 304 | short r = get_rand(0, MAXROOMS-1); 305 | for (short i = 0; i < MAXROOMS; i++) { 306 | short rr = (r + i) % MAXROOMS; 307 | if ((!(rooms[rr].is_room & (R_ROOM | R_MAZE))) || rr == rn) { 308 | continue; 309 | } 310 | for (short k = 0; k < 4; k++) { 311 | if (rooms[rr].doors[k].oth_room == rn) { 312 | monster->trow = rooms[rr].doors[k].oth_row; 313 | monster->tcol = rooms[rr].doors[k].oth_col; 314 | if (monster->trow == row && monster->tcol == col) 315 | continue; 316 | return; 317 | } 318 | } 319 | } 320 | // look for door to dead end 321 | if (rn == NO_ROOM) 322 | clean_up("dr_course: monster not in room"); 323 | for (short i = rooms[rn].top_row; i <= rooms[rn].bottom_row; i++) { 324 | for (short j = rooms[rn].left_col; j <= rooms[rn].right_col; j++) { 325 | if (i != monster->row && j != monster->col && (dungeon[i][j] & DOOR)) { 326 | monster->trow = i; 327 | monster->tcol = j; 328 | return; 329 | } 330 | } 331 | } 332 | // return monster to room that he came from 333 | for (short i = 0; i < MAXROOMS; i++) { 334 | for (short j = 0; j < 4; j++) { 335 | if (rooms[i].doors[j].oth_room == rn) { 336 | for (short k = 0; k < 4; k++) { 337 | if (rooms[rn].doors[k].oth_room == i) { 338 | monster->trow = rooms[rn].doors[k].oth_row; 339 | monster->tcol = rooms[rn].doors[k].oth_col; 340 | return; 341 | } 342 | } 343 | } 344 | } 345 | } 346 | monster->trow = NO_ROOM; // no place to send monster 347 | } else { // exiting room 348 | if (rn == NO_ROOM || !get_oth_room(rn, &row, &col)) 349 | monster->trow = NO_ROOM; 350 | else { 351 | monster->trow = row; 352 | monster->tcol = col; 353 | } 354 | } 355 | } 356 | 357 | static bool get_oth_room(short rn, short *row, short *col) { 358 | short d = -1; 359 | if (*row == rooms[rn].top_row) 360 | d = UPWARD / 2; 361 | else if (*row == rooms[rn].bottom_row) 362 | d = DOWN / 2; 363 | else if (*col == rooms[rn].left_col) 364 | d = LEFT / 2; 365 | else if (*col == rooms[rn].right_col) 366 | d = RIGHT / 2; 367 | 368 | if (d != -1 && rooms[rn].doors[d].oth_room >= 0) { 369 | *row = rooms[rn].doors[d].oth_row; 370 | *col = rooms[rn].doors[d].oth_col; 371 | return 1; 372 | } 373 | return 0; 374 | } 375 | 376 | void edit_opts(void) { 377 | char save[NOPTS + 1][DCOLS]; 378 | for (short i = 0; i < NOPTS + (locked_down ? -1 : 1); i++) { 379 | for (short j = 0; j < DCOLS; j++) 380 | save[i][j] = (short)mvinch(i, j); 381 | if (i < NOPTS && (locked_down && i < NOPTS - 2)) 382 | opt_show(i); 383 | } 384 | opt_go(0); 385 | 386 | short i = 0; 387 | short j; 388 | char buf[MAX_OPT_LEN + 2]; 389 | bool done = 0; 390 | while (!done) { 391 | refresh(); 392 | int ch = rgetchar(); 393 | CH: 394 | switch (ch) { 395 | case 0x1b: 396 | done = 1; 397 | break; 398 | case 0x0a: case 0x0d: 399 | if (i == NOPTS - 1 || (locked_down && i == NOPTS - 3)) { 400 | mvaddstr(locked_down ? NOPTS - 2 : NOPTS, 0, press_space); 401 | refresh(); 402 | wait_for_ack(); 403 | done = 1; 404 | } else { 405 | i++; 406 | opt_go(i); 407 | } 408 | break; 409 | case '-': 410 | if (i > 0) 411 | opt_go(--i); 412 | else 413 | sound_bell(); 414 | break; 415 | case 't': 416 | case 'T': 417 | case 'f': 418 | case 'F': 419 | if (options[i].is_bool) { 420 | *(options[i].bval) = ((ch == 't' || ch == 'T') ? 1 : 0); 421 | opt_show(i); 422 | opt_go(++i); 423 | break; 424 | } 425 | // FALLTHROUGH 426 | default: 427 | if (options[i].is_bool) { 428 | sound_bell(); 429 | break; 430 | } 431 | 432 | j = 0; 433 | if (ch == '\010' || (ch >= ' ' && ch <= '~')) { 434 | opt_erase(i); 435 | do { 436 | if (ch >= ' ' && ch <= '~' && j < MAX_OPT_LEN) { 437 | buf[j++] = ch; 438 | buf[j] = '\0'; 439 | addch(ch); 440 | } else if (ch == '\010' && j > 0) { 441 | buf[--j] = '\0'; 442 | move(i, j + strlen(options[i].prompt) + strlen(options[i].name) + 7); 443 | addch(' '); 444 | move(i, j + strlen(options[i].prompt) + strlen(options[i].name) + 7); 445 | } 446 | refresh(); 447 | ch = rgetchar(); 448 | } while (ch != '\012' && ch != '\015' && ch != '\033'); 449 | if (j != 0) { 450 | // We rely on the option string being allocated to hold 451 | // MAX_OPT_LEN+2 bytes. This is arranged in init.c. 452 | if (options[i].is_int) 453 | *options[i].ival = get_number(buf); 454 | else 455 | strcpy(*(options[i].strval), buf); 456 | } 457 | opt_show(i); 458 | goto CH; 459 | } else { 460 | sound_bell(); 461 | } 462 | break; 463 | } 464 | } 465 | 466 | for (i = 0; i < NOPTS + (locked_down ? -1 : 1); i++) { 467 | move(i, 0); 468 | for (j = 0; j < DCOLS; j++) 469 | addch(save[i][j]); 470 | } 471 | } 472 | 473 | static void opt_show(int i) { 474 | opt_erase(i); 475 | 476 | const struct option *opt = &options[i]; 477 | const char *s; 478 | if (opt->is_bool) 479 | s = *(opt->bval) ? "True" : "False"; 480 | else if (opt->is_int) { 481 | char buf[10]; 482 | sprintf(buf, "%d", *opt->ival); 483 | s = &buf[0]; 484 | } 485 | else 486 | s = *(opt->strval); 487 | addstr(s); 488 | } 489 | 490 | static void opt_erase(int i) { 491 | const struct option *opt = &options[i]; 492 | mvaddstr(i, 0, opt->prompt); 493 | mvaddstr(i, strlen(opt->prompt), " (\""); 494 | mvaddstr(i, strlen(opt->prompt)+3, opt->name); 495 | mvaddstr(i, strlen(opt->prompt)+strlen(opt->name)+3, "\"): "); 496 | clrtoeol(); 497 | } 498 | 499 | static void opt_go(int i) { 500 | move(i, strlen(options[i].prompt) + strlen(options[i].name) + 7); 501 | } 502 | -------------------------------------------------------------------------------- /save.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)save.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/save.c,v 1.6 1999/11/30 03:49:27 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include "rogue.h" 47 | 48 | static void del_save_file(void); 49 | static void r_read(FILE *, char *, int); 50 | static void r_write(FILE *, const char *, int); 51 | static void read_pack(object *, FILE *, bool); 52 | static void read_string(char *, FILE *, size_t); 53 | static void rw_dungeon(FILE *, bool); 54 | static void rw_id(struct id *, FILE *, int, bool); 55 | static void rw_rooms(FILE *, bool); 56 | static void write_pack(const object *, FILE *); 57 | static void write_string(char *, FILE *); 58 | 59 | static bool write_failed = 0; 60 | static char save_name[80]; 61 | 62 | char *save_file = NULL; 63 | bool noautosave = false; 64 | 65 | void save_game(void) { 66 | char fname[PATH_MAX]; 67 | if (locked_down) 68 | strncpy(fname, save_file, PATH_MAX); 69 | else { 70 | if (!get_input_line("file name?", save_file, fname, 0)) { 71 | message("game not saved", 0); 72 | return; 73 | } 74 | } 75 | check_message(); 76 | messagef(0, "savegame loaded from \"%s\"", fname); 77 | 78 | int ok = save_into_file(fname); 79 | if (ok) { 80 | save_name[0] = 0; // Clear so it won't get removed on exit. 81 | clean_up(""); 82 | } 83 | } 84 | 85 | bool save_into_file(const char *sfile) { 86 | if (sfile[0] == '~') { 87 | char *home = md_homedir(); 88 | if (home != NULL) { 89 | size_t len = strlen(home) + strlen(sfile); 90 | char *name_buffer = calloc(len, sizeof(char)); 91 | if (name_buffer == NULL) { 92 | message("out of memory for save file name", 0); 93 | sfile = error_file; 94 | } else { 95 | strcpy(name_buffer, home); 96 | strcat(name_buffer, sfile+1); 97 | sfile = name_buffer; 98 | } 99 | free(home); 100 | } 101 | } 102 | 103 | FILE *fp = fopen(sfile, "w"); 104 | if (fp == NULL) { 105 | messagef(0, "problem accessing the save file %s", sfile); 106 | return false; 107 | } 108 | md_ignore_signals(); 109 | write_failed = false; 110 | 111 | r_write(fp, (char *)&game_seed, sizeof(game_seed)); 112 | r_write(fp, (char *)&detect_monster, sizeof(detect_monster)); 113 | r_write(fp, (char *)&cur_level, sizeof(cur_level)); 114 | r_write(fp, (char *)&max_level, sizeof(max_level)); 115 | write_string(hunger_str, fp); 116 | write_string(nick_name, fp); 117 | r_write(fp, (char *) &party_room, sizeof(party_room)); 118 | write_pack(&level_monsters, fp); 119 | write_pack(&level_objects, fp); 120 | rw_dungeon(fp, 1); 121 | r_write(fp, (char *) &foods, sizeof(foods)); 122 | r_write(fp, (char *) &rogue, sizeof(fighter)); 123 | write_pack(&rogue.pack, fp); 124 | rw_id(id_potions, fp, POTIONS, 1); 125 | rw_id(id_scrolls, fp, SCROLS, 1); 126 | rw_id(id_wands, fp, WANDS, 1); 127 | rw_id(id_rings, fp, RINGS, 1); 128 | r_write(fp, (char *) traps, (MAX_TRAPS * sizeof(trap))); 129 | r_write(fp, (char *) is_wood, (WANDS * sizeof(bool))); 130 | r_write(fp, (char *) &cur_room, sizeof(cur_room)); 131 | rw_rooms(fp, 1); 132 | r_write(fp, (char *) &being_held, sizeof(being_held)); 133 | r_write(fp, (char *) &bear_trap, sizeof(bear_trap)); 134 | r_write(fp, (char *) &halluc, sizeof(halluc)); 135 | r_write(fp, (char *) &blind, sizeof(blind)); 136 | r_write(fp, (char *) &confused, sizeof(confused)); 137 | r_write(fp, (char *) &levitate, sizeof(levitate)); 138 | r_write(fp, (char *) &haste_self, sizeof(haste_self)); 139 | r_write(fp, (char *) &see_invisible, sizeof(see_invisible)); 140 | r_write(fp, (char *) &detect_monster, sizeof(detect_monster)); 141 | r_write(fp, (char *) &wizard, sizeof(wizard)); 142 | r_write(fp, (char *) &score_only, sizeof(score_only)); 143 | r_write(fp, (char *) &m_moves, sizeof(m_moves)); 144 | fclose(fp); 145 | 146 | if (write_failed) 147 | unlink(sfile); 148 | return !write_failed; 149 | } 150 | 151 | static void del_save_file(void) { 152 | if (!save_name[0]) 153 | return; 154 | unlink(save_name); 155 | } 156 | 157 | void restore(const char *fname) { 158 | FILE *fp = fopen(fname, "r"); 159 | if (fp == NULL) 160 | clean_up("cannot open file"); 161 | 162 | r_read(fp, (char *)&game_seed, sizeof(game_seed)); 163 | r_read(fp, (char *)&detect_monster, sizeof(detect_monster)); 164 | r_read(fp, (char *)&cur_level, sizeof(cur_level)); 165 | r_read(fp, (char *)&max_level, sizeof(max_level)); 166 | read_string(hunger_str, fp, sizeof(hunger_str)); 167 | nick_name = calloc(MAX_OPT_LEN, sizeof(char)); 168 | read_string(nick_name, fp, MAX_OPT_LEN + 1); 169 | r_read(fp, (char *) &party_room, sizeof(party_room)); 170 | read_pack(&level_monsters, fp, 0); 171 | read_pack(&level_objects, fp, 0); 172 | rw_dungeon(fp, 0); 173 | r_read(fp, (char *) &foods, sizeof(foods)); 174 | r_read(fp, (char *) &rogue, sizeof(fighter)); 175 | read_pack(&rogue.pack, fp, 1); 176 | rw_id(id_potions, fp, POTIONS, 0); 177 | rw_id(id_scrolls, fp, SCROLS, 0); 178 | rw_id(id_wands, fp, WANDS, 0); 179 | rw_id(id_rings, fp, RINGS, 0); 180 | r_read(fp, (char *) traps, (MAX_TRAPS * sizeof(trap))); 181 | r_read(fp, (char *) is_wood, (WANDS * sizeof(bool))); 182 | r_read(fp, (char *) &cur_room, sizeof(cur_room)); 183 | rw_rooms(fp, 0); 184 | r_read(fp, (char *) &being_held, sizeof(being_held)); 185 | r_read(fp, (char *) &bear_trap, sizeof(bear_trap)); 186 | r_read(fp, (char *) &halluc, sizeof(halluc)); 187 | r_read(fp, (char *) &blind, sizeof(blind)); 188 | r_read(fp, (char *) &confused, sizeof(confused)); 189 | r_read(fp, (char *) &levitate, sizeof(levitate)); 190 | r_read(fp, (char *) &haste_self, sizeof(haste_self)); 191 | r_read(fp, (char *) &see_invisible, sizeof(see_invisible)); 192 | r_read(fp, (char *) &detect_monster, sizeof(detect_monster)); 193 | r_read(fp, (char *) &wizard, sizeof(wizard)); 194 | r_read(fp, (char *) &score_only, sizeof(score_only)); 195 | r_read(fp, (char *) &m_moves, sizeof(m_moves)); 196 | 197 | char buf[4]; 198 | if (fread(buf, sizeof(char), 1, fp) > 0) { 199 | clear(); 200 | clean_up("extra characters in file"); 201 | } 202 | 203 | if (!wizard) { 204 | strcpy(save_name, fname); 205 | atexit(del_save_file); 206 | } 207 | msg_cleared = 0; 208 | ring_stats(0); 209 | fclose(fp); 210 | } 211 | 212 | static void write_pack(const object *pack, FILE *fp) { 213 | while ((pack = pack->next_object) != NULL) 214 | r_write(fp, (const char *)pack, sizeof(object)); 215 | 216 | object t; 217 | t.ichar = t.what_is = 0; 218 | r_write(fp, (const char *) &t, sizeof(object)); 219 | } 220 | 221 | static void read_pack(object *pack, FILE *fp, bool is_rogue) { 222 | for (;;) { 223 | object read_obj; 224 | r_read(fp, (char *) &read_obj, sizeof(object)); 225 | if (read_obj.ichar == 0) { 226 | pack->next_object = NULL; 227 | break; 228 | } 229 | 230 | object *new_obj = alloc_object(); 231 | *new_obj = read_obj; 232 | if (is_rogue) { 233 | if (new_obj->in_use_flags & BEING_WORN) 234 | do_wear(new_obj); 235 | else if (new_obj->in_use_flags & BEING_WIELDED) 236 | do_wield(new_obj); 237 | else if (new_obj->in_use_flags & (ON_EITHER_HAND)) 238 | do_put_on(new_obj, (new_obj->in_use_flags & ON_LEFT_HAND) ? 1 : 0); 239 | } 240 | pack->next_object = new_obj; 241 | pack = new_obj; 242 | } 243 | } 244 | 245 | static void rw_dungeon(FILE *fp, bool rw) { 246 | for (short i = 0; i < DROWS; i++) { 247 | char buf[DCOLS]; 248 | if (rw) { 249 | r_write(fp, (char *) dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); 250 | for (short j = 0; j < DCOLS; j++) 251 | buf[j] = (short)mvinch(i, j); 252 | r_write(fp, buf, DCOLS); 253 | } else { 254 | r_read(fp, (char *) dungeon[i], (DCOLS * sizeof(dungeon[0][0]))); 255 | r_read(fp, buf, DCOLS); 256 | for (short j = 0; j < DCOLS; j++) 257 | mvaddch(i, j, buf[j]); 258 | } 259 | } 260 | } 261 | 262 | static void rw_id(struct id id_table[], FILE *fp, int n, bool wr) { 263 | for (short i = 0; i < n; i++) { 264 | if (wr) { 265 | r_write(fp, (const char *) &(id_table[i].value), sizeof(short)); 266 | r_write(fp, (const char *) &(id_table[i].id_status), sizeof(unsigned short)); 267 | write_string(id_table[i].title, fp); 268 | } else { 269 | r_read(fp, (char *) &(id_table[i].value), sizeof(short)); 270 | r_read(fp, (char *) &(id_table[i].id_status), sizeof(unsigned short)); 271 | read_string(id_table[i].title, fp, MAX_ID_TITLE_LEN); 272 | } 273 | } 274 | } 275 | 276 | static void write_string(char *s, FILE *fp) { 277 | short n = strlen(s) + 1; 278 | r_write(fp, (char *) &n, sizeof(short)); 279 | r_write(fp, s, n); 280 | } 281 | 282 | static void read_string(char *s, FILE *fp, size_t len) { 283 | short n; 284 | r_read(fp, (char *) &n, sizeof(short)); 285 | if (n < 0 || n > (short)len) 286 | clean_up("read_string: corrupt game file"); 287 | r_read(fp, s, n); 288 | s[n - 1] = 0; // ensure null termination 289 | } 290 | 291 | static void rw_rooms(FILE *fp, bool rw) { 292 | for (short i = 0; i < MAXROOMS; i++) { 293 | rw ? r_write(fp, (char *) (rooms + i), sizeof(room)) : 294 | r_read(fp, (char *) (rooms + i), sizeof(room)); 295 | } 296 | } 297 | 298 | static void r_read(FILE *fp, char *buf, int n) { 299 | if (fread(buf, sizeof(char), n, fp) != (unsigned)n) 300 | clean_up("read() failed, don't know why"); 301 | } 302 | 303 | static void r_write(FILE *fp, const char *buf, int n) { 304 | if (!write_failed) { 305 | if (fwrite(buf, sizeof(char), n, fp) != (unsigned)n) { 306 | message("write() failed, don't know why", 0); 307 | sound_bell(); 308 | write_failed = true; 309 | } 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /score.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)score.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/score.c,v 1.4 1999/11/30 03:49:27 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include 43 | #include 44 | #include "rogue.h" 45 | 46 | static void center(short, const char *); 47 | static int get_value(const object *); 48 | static void id_all(void); 49 | static void score_reason(char[128], const object *, short); 50 | static void sell_pack(void); 51 | static void sf_error(const char *); 52 | 53 | void killed_by(const object *monster, short other) { 54 | md_ignore_signals(); 55 | 56 | if (other != QUIT) 57 | rogue.gold = ((rogue.gold * 9) / 10); 58 | 59 | char buf[128]; 60 | if (other) { 61 | switch (other) { 62 | case HYPOTHERMIA: strcpy(buf, "died of hypothermia"); break; 63 | case STARVATION: strcpy(buf, "died of starvation"); break; 64 | case POISON_DART: strcpy(buf, "killed by a dart"); break; 65 | case QUIT: strcpy(buf, "quit"); break; 66 | case KFIRE: strcpy(buf, "killed by fire"); break; 67 | } 68 | } else { 69 | strcpy(buf, "Killed by "); 70 | if (is_vowel(m_names[monster->m_char - 'A'][0])) 71 | strcat(buf, "an "); 72 | else 73 | strcat(buf, "a "); 74 | strcat(buf, m_names[monster->m_char - 'A']); 75 | } 76 | strcat(buf, " with "); 77 | sprintf(buf + strlen(buf), "%ld gold", rogue.gold); 78 | if (!other && !no_skull) { 79 | clear(); 80 | mvaddstr(4, 32, "__---------__"); 81 | mvaddstr(5, 30, "_~ ~_"); 82 | mvaddstr(6, 29, "/ \\"); 83 | mvaddstr(7, 28, "~ ~"); 84 | mvaddstr(8, 27, "/ \\"); 85 | mvaddstr(9, 27, "| XXXX XXXX |"); 86 | mvaddstr(10, 27, "| XXXX XXXX |"); 87 | mvaddstr(11, 27, "| XXX XXX |"); 88 | mvaddstr(12, 28, "\\ @ /"); 89 | mvaddstr(13, 29, "--\\ @@@ /--"); 90 | mvaddstr(14, 30, "| | @@@ | |"); 91 | mvaddstr(15, 30, "| | | |"); 92 | mvaddstr(16, 30, "| vvVvvvvvvvVvv |"); 93 | mvaddstr(17, 30, "| ^^^^^^^^^^^ |"); 94 | mvaddstr(18, 31, "\\_ _/"); 95 | mvaddstr(19, 33, "~---------~"); 96 | center(21, nick_name); 97 | center(22, buf); 98 | } else 99 | message(buf, 0); 100 | message("", 0); 101 | put_scores(monster, other); 102 | } 103 | 104 | void win(void) { 105 | unwield(rogue.weapon); // disarm and relax 106 | unwear(rogue.armor); 107 | un_put_on(rogue.left_ring); 108 | un_put_on(rogue.right_ring); 109 | 110 | clear(); 111 | mvaddstr(10, 11, "@ @ @@@ @ @ @ @ @ @@@ @ @ @"); 112 | mvaddstr(11, 11, " @ @ @ @ @ @ @ @ @ @ @ @@ @ @"); 113 | mvaddstr(12, 11, " @ @ @ @ @ @ @ @ @ @ @ @ @ @"); 114 | mvaddstr(13, 11, " @ @ @ @ @ @ @ @ @ @ @ @@"); 115 | mvaddstr(14, 11, " @ @@@ @@@ @@ @@ @@@ @ @ @"); 116 | mvaddstr(17, 11, "Congratulations, you have been admitted to the"); 117 | mvaddstr(18, 11, "Fighters' Guild. You return home, sell all your"); 118 | mvaddstr(19, 11, "treasures at great profit and retire into comfort."); 119 | message("", 0); 120 | message("", 0); 121 | id_all(); 122 | sell_pack(); 123 | put_scores(NULL, WIN); 124 | } 125 | 126 | void quit(bool from_intrpt) { 127 | md_ignore_signals(); 128 | 129 | bool mc = FALSE; 130 | short orow = 0, ocol = 0; 131 | char buf[128]; 132 | if (from_intrpt) { 133 | orow = rogue.row; 134 | ocol = rogue.col; 135 | 136 | mc = msg_cleared; 137 | 138 | for (short i = 0; i < DCOLS; i++) 139 | buf[i] = (short)mvinch(0, i); 140 | } 141 | check_message(); 142 | message("really quit?", 1); 143 | if (rgetchar() != 'y') { 144 | md_heed_signals(); 145 | check_message(); 146 | if (from_intrpt) { 147 | for (short i = 0; i < DCOLS; i++) 148 | mvaddch(0, i, buf[i]); 149 | msg_cleared = mc; 150 | move(orow, ocol); 151 | refresh(); 152 | } 153 | return; 154 | } 155 | if (from_intrpt) 156 | clean_up(byebye_string); 157 | check_message(); 158 | killed_by(NULL, QUIT); 159 | } 160 | 161 | struct scoreline { 162 | short rank; 163 | int gold; 164 | char nick[31]; 165 | char reason[128]; 166 | }; 167 | 168 | void put_scores(const object *monster, short other) { 169 | bool pause = score_only; // score_only is the -s flag from the CLI. 170 | 171 | md_lock(1); 172 | const char *file = md_scorefile(); 173 | FILE *fp; 174 | if ((fp = fopen(file, "r+")) == NULL && (fp = fopen(file, "w+")) == NULL) { 175 | char errmsg[1024]; 176 | snprintf(errmsg, 1024, "cannot read/write score file %s: %s", file, strerror(errno)); 177 | message(errmsg, 0); 178 | sf_error(errmsg); 179 | } 180 | rewind(fp); 181 | 182 | struct scoreline scores[NUM_SCORES] = {{0}}; 183 | int n = fread(&scores, sizeof(char), sizeof(scores), fp); 184 | if (n < sizeof(scores) && n != 0) 185 | sf_error(NULL); 186 | 187 | short rank = 0; 188 | if (!score_only) { 189 | for (short i = 0; i < NUM_SCORES; i++) { 190 | if (rogue.gold > scores[i].gold) { 191 | rank = i + 1; 192 | break; 193 | } 194 | } 195 | 196 | if (rank >= 1) { 197 | for (short i = NUM_SCORES; i >= rank - 1; i--) { 198 | if (scores[i].rank == 0) 199 | continue; 200 | scores[i].rank = i + 2; 201 | scores[i + 1] = scores[i]; 202 | } 203 | struct scoreline s = {rank, rogue.gold, "Jan 12 '23", ""}; 204 | snprintf(s.nick, sizeof(s.nick) - 1, "%s", nick_name); 205 | score_reason(s.reason, monster, other); 206 | scores[rank - 1] = s; 207 | } 208 | } 209 | 210 | clear(); 211 | mvaddstr(1, 30, "Top Rogueists"); 212 | mvaddstr(2, 0, "Rank Score Name"); 213 | 214 | short offset = 0; 215 | for (short i = 0; i < NUM_SCORES; i++) { 216 | if (scores[i].rank == 0) 217 | break; 218 | 219 | char buf[128]; 220 | snprintf(buf, 128, "%2d %6d %s: %s\n", 221 | scores[i].rank, scores[i].gold, scores[i].nick, scores[i].reason); 222 | 223 | if (i == rank - 1) 224 | standout(); 225 | /// Wrap to next line with indent if >80 chars. 64 characters available 226 | /// – needs to indent 16. 227 | int l = 80; 228 | if (strlen(buf) > l + 1) { 229 | char buf2[80] = {0}; 230 | while (buf[l] != ' ') 231 | l--; 232 | strncpy(buf2, buf, l); 233 | mvaddstr(i + offset + 3, 0, buf2); 234 | 235 | buf2[0] = 0; 236 | strncpy(buf2, &buf[l+1], 80); 237 | mvaddstr(i + offset + 4, 0, " "); 238 | mvaddstr(i + offset + 4, 16, buf2); 239 | offset++; 240 | } else 241 | mvaddstr(i + offset + 3, 0, buf); 242 | if (i == rank - 1) 243 | standend(); 244 | } 245 | 246 | md_ignore_signals(); 247 | if (!score_only && rank >= 1) { 248 | rewind(fp); 249 | fwrite((char *)&scores, sizeof(char), sizeof(scores), fp); 250 | } 251 | 252 | md_lock(0); 253 | refresh(); 254 | fclose(fp); 255 | message("", 0); 256 | if (pause) 257 | message("", 0); 258 | clean_up(""); 259 | } 260 | 261 | static void score_reason(char buf[128], const object *monster, short other) { 262 | if (other) { 263 | switch (other) { 264 | case HYPOTHERMIA: strncat(buf, "died of hypothermia", 128); break; 265 | case STARVATION: strncat(buf, "died of starvation", 128); break; 266 | case POISON_DART: strncat(buf, "killed by a dart", 128); break; 267 | case QUIT: strncat(buf, "quit", 128); break; 268 | case WIN: strncat(buf, "a total winner", 128); break; 269 | case KFIRE: strncpy(buf, "killed by fire", 128); break; 270 | } 271 | } 272 | else { 273 | snprintf(buf, 128, "killed by a%s %s", 274 | is_vowel(m_names[monster->m_char - 'A'][0]) ? "n" : "", 275 | m_names[monster->m_char - 'A']); 276 | //strcat(buf, "killed by "); 277 | //strcat(buf, is_vowel(m_names[monster->m_char - 'A'][0]) ? "an " : "a "); 278 | //strcat(buf, m_names[monster->m_char - 'A']); 279 | } 280 | sprintf(buf + strlen(buf), " on %s at level %d ", "Jun 19 '23", max_level); 281 | if (other != WIN && has_amulet()) 282 | strncat(buf, "with amulet", 128); 283 | } 284 | 285 | bool is_vowel(short ch) { 286 | return ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u'; 287 | } 288 | 289 | static void sell_pack(void) { 290 | clear(); 291 | mvaddstr(1, 0, "Value Item"); 292 | 293 | char buf[DCOLS]; 294 | short row = 2; 295 | object *obj = rogue.pack.next_object; 296 | while (obj) { 297 | if (obj->what_is != FOOD) { 298 | obj->identified = 1; 299 | short val = get_value(obj); 300 | rogue.gold += val; 301 | 302 | if (row < DROWS) { 303 | sprintf(buf, "%5d ", val); 304 | get_desc(obj, buf+11); 305 | mvaddstr(row++, 0, buf); 306 | } 307 | } 308 | obj = obj->next_object; 309 | } 310 | refresh(); 311 | if (rogue.gold > MAX_GOLD) 312 | rogue.gold = MAX_GOLD; 313 | message("", 0); 314 | } 315 | 316 | static int get_value(const object *obj) { 317 | int val = 0; 318 | short wc = obj->which_kind; 319 | switch (obj->what_is) { 320 | case WEAPON: 321 | val = id_weapons[wc].value; 322 | if (wc == ARROW || wc == DAGGER || wc == SHURIKEN || wc == DART) 323 | val *= obj->quantity; 324 | val += (obj->d_enchant * 85); 325 | val += (obj->hit_enchant * 85); 326 | break; 327 | case ARMOR: 328 | val = id_armors[wc].value; 329 | val += (obj->d_enchant * 75); 330 | if (obj->is_protected) 331 | val += 200; 332 | break; 333 | case WAND: 334 | val = id_wands[wc].value * (obj->class + 1); 335 | break; 336 | case SCROL: 337 | val = id_scrolls[wc].value * obj->quantity; 338 | break; 339 | case POTION: 340 | val = id_potions[wc].value * obj->quantity; 341 | break; 342 | case AMULET: 343 | val = 5000; 344 | break; 345 | case RING: 346 | val = id_rings[wc].value * (obj->class + 1); 347 | break; 348 | } 349 | if (val <= 0) 350 | val = 10; 351 | return val; 352 | } 353 | 354 | static void id_all(void) { 355 | for (short i = 0; i < SCROLS; i++) 356 | id_scrolls[i].id_status = IDENTIFIED; 357 | for (short i = 0; i < WEAPONS; i++) 358 | id_weapons[i].id_status = IDENTIFIED; 359 | for (short i = 0; i < ARMORS; i++) 360 | id_armors[i].id_status = IDENTIFIED; 361 | for (short i = 0; i < WANDS; i++) 362 | id_wands[i].id_status = IDENTIFIED; 363 | for (short i = 0; i < POTIONS; i++) 364 | id_potions[i].id_status = IDENTIFIED; 365 | } 366 | 367 | static void center(short row, const char *buf) { 368 | short margin = ((DCOLS - strlen(buf)) / 2); 369 | mvaddstr(row, margin, buf); 370 | } 371 | 372 | static void sf_error(const char *msg) { 373 | md_lock(0); 374 | message("", 1); 375 | if (msg != NULL) 376 | clean_up(msg); 377 | else 378 | clean_up("sorry, score file is out of order"); 379 | } 380 | -------------------------------------------------------------------------------- /throw.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)throw.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/throw.c,v 1.3 1999/11/30 03:49:28 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | static void flop_weapon(object *, short, short); 45 | static object *get_thrown_at_monster(object *, short, short *, short *); 46 | static bool throw_at_monster(object *, object *); 47 | 48 | void throw(void) { 49 | message("throw direction? ", 0); 50 | 51 | bool first_miss = 1; 52 | int d, dir; 53 | while (!is_direction(dir = rgetchar(), &d)) { 54 | sound_bell(); 55 | if (first_miss) { 56 | message("direction? ", 0); 57 | first_miss = 0; 58 | } 59 | } 60 | check_message(); 61 | if (dir == CANCEL) 62 | return; 63 | 64 | short wch = pack_letter("throw what?", WEAPON); 65 | if (wch == CANCEL) 66 | return; 67 | check_message(); 68 | 69 | object *weapon = get_letter_object(wch); 70 | if (!weapon) { 71 | message("no such item.", 0); 72 | return; 73 | } 74 | if ((weapon->in_use_flags & BEING_USED) && weapon->is_cursed) { 75 | message(curse_message, 0); 76 | return; 77 | } 78 | 79 | short row = rogue.row, col = rogue.col; 80 | 81 | if ((weapon->in_use_flags & BEING_WIELDED) && (weapon->quantity <= 1)) 82 | unwield(rogue.weapon); 83 | else if (weapon->in_use_flags & BEING_WORN) { 84 | mv_aquatars(); 85 | unwear(rogue.armor); 86 | print_stats(STAT_ARMOR); 87 | } else if (weapon->in_use_flags & ON_EITHER_HAND) 88 | un_put_on(weapon); 89 | 90 | object *monster = get_thrown_at_monster(weapon, d, &row, &col); 91 | mvaddch(rogue.row, rogue.col, rogue.fchar); 92 | refresh(); 93 | 94 | if (rogue_can_see(row, col) && ((row != rogue.row) || (col != rogue.col))) 95 | mvaddch(row, col, get_dungeon_char(row, col)); 96 | 97 | if (monster) { 98 | wake_up(monster); 99 | check_gold_seeker(monster); 100 | 101 | if (!throw_at_monster(monster, weapon)) 102 | flop_weapon(weapon, row, col); 103 | } else 104 | flop_weapon(weapon, row, col); 105 | vanish(weapon, 1, &rogue.pack); 106 | } 107 | 108 | static bool throw_at_monster(object *monster, object *weapon) { 109 | short hit_chance = get_hit_chance(weapon); 110 | short damage = get_weapon_damage(weapon); 111 | if (weapon->which_kind == ARROW && (rogue.weapon && rogue.weapon->which_kind == BOW)) { 112 | damage += get_weapon_damage(rogue.weapon); 113 | damage = ((damage * 2) / 3); 114 | hit_chance += (hit_chance / 3); 115 | } else if ((weapon->in_use_flags & BEING_WIELDED) && ( 116 | (weapon->which_kind == DAGGER) || 117 | (weapon->which_kind == SHURIKEN) || 118 | (weapon->which_kind == DART) 119 | )) { 120 | damage = ((damage * 3) / 2); 121 | hit_chance += (hit_chance / 3); 122 | } 123 | short t = weapon->quantity; 124 | weapon->quantity = 1; 125 | sprintf(hit_message, "the %s", name_of(weapon)); 126 | weapon->quantity = t; 127 | 128 | if (!rand_percent(hit_chance)) { 129 | strcat(hit_message, "misses "); 130 | return 0; 131 | } 132 | s_con_mon(monster); 133 | strcat(hit_message, "hit "); 134 | mon_damage(monster, damage); 135 | return 1; 136 | } 137 | 138 | static object * get_thrown_at_monster(object *obj, short dir, short *row, short *col) { 139 | short orow = *row, ocol = *col; 140 | short ch = get_mask_char(obj->what_is); 141 | 142 | for (short i = 0; i < 24; i++) { 143 | get_dir_rc(dir, row, col, 0); 144 | if (((*col <= 0 || *col >= DCOLS - 1) || dungeon[*row][*col] == NOTHING) || 145 | ((dungeon[*row][*col] & (HORWALL | VERTWALL | HIDDEN)) && (!(dungeon[*row][*col] & TRAP))) 146 | ) { 147 | *row = orow; 148 | *col = ocol; 149 | return 0; 150 | } 151 | if ((i != 0) && rogue_can_see(orow, ocol)) 152 | mvaddch(orow, ocol, get_dungeon_char(orow, ocol)); 153 | 154 | if (rogue_can_see(*row, *col)) { 155 | if (!(dungeon[*row][*col] & MONSTER)) 156 | mvaddch(*row, *col, ch); 157 | refresh(); 158 | } 159 | orow = *row; ocol = *col; 160 | if (dungeon[*row][*col] & MONSTER) { 161 | if (!imitating(*row, *col)) 162 | return object_at(&level_monsters, *row, *col); 163 | } 164 | if (dungeon[*row][*col] & TUNNEL) 165 | i += 2; 166 | } 167 | return 0; 168 | } 169 | 170 | static void flop_weapon(object *weapon, short row, short col) { 171 | bool found = 0; 172 | short i = 0; 173 | while ((i < 9) && dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER)) { 174 | rand_around(i++, &row, &col); 175 | if ((row > (DROWS - 2)) || (row < MIN_ROW) || 176 | (col > (DCOLS - 1)) || (col < 0) || (!dungeon[row][col]) || 177 | (dungeon[row][col] & ~(FLOOR | TUNNEL | DOOR | MONSTER)) 178 | ) { 179 | continue; 180 | } 181 | found = 1; 182 | break; 183 | } 184 | 185 | if (found || (i == 0)) { 186 | object *new_weapon = alloc_object(); 187 | *new_weapon = *weapon; 188 | new_weapon->in_use_flags = NOT_USED; 189 | new_weapon->quantity = 1; 190 | new_weapon->ichar = 'L'; 191 | place_at(new_weapon, row, col); 192 | if (rogue_can_see(row, col) && ((row != rogue.row) || (col != rogue.col))) { 193 | unsigned short mon = dungeon[row][col] & MONSTER; 194 | dungeon[row][col] &= (~MONSTER); 195 | short dch = get_dungeon_char(row, col); 196 | if (mon) { 197 | short mch = (short)mvinch(row, col); 198 | object *monster; 199 | if ((monster = object_at(&level_monsters, 200 | row, col)) != NULL) { 201 | monster->trail_char = dch; 202 | } 203 | if ((mch < 'A') || (mch > 'Z')) 204 | mvaddch(row, col, dch); 205 | } else 206 | mvaddch(row, col, dch); 207 | dungeon[row][col] |= mon; 208 | } 209 | } else { 210 | short t = weapon->quantity; 211 | weapon->quantity = 1; 212 | messagef(0, "the %svanishes as it hits the ground", name_of(weapon)); 213 | weapon->quantity = t; 214 | } 215 | } 216 | 217 | void rand_around(short i, short *r, short *c) { 218 | static char pos[] = "\010\007\001\003\004\005\002\006\0"; 219 | static short row, col; 220 | if (i == 0) { 221 | row = *r; 222 | col = *c; 223 | 224 | short o = get_rand(1, 8); 225 | for (short j = 0; j < 5; j++) { 226 | short x = get_rand(0, 8); 227 | short y = (x + o) % 9; 228 | short t = pos[x]; 229 | pos[x] = pos[y]; 230 | pos[y] = t; 231 | } 232 | } 233 | switch ((short)pos[i]) { 234 | case 0: *r = row + 1; *c = col + 1; break; 235 | case 1: *r = row + 1; *c = col - 1; break; 236 | case 2: *r = row - 1; *c = col + 1; break; 237 | case 3: *r = row - 1; *c = col - 1; break; 238 | case 4: *r = row; *c = col + 1; break; 239 | case 5: *r = row + 1; *c = col; break; 240 | case 6: *r = row; *c = col; break; 241 | case 7: *r = row - 1; *c = col; break; 242 | case 8: *r = row; *c = col - 1; break; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /trap.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)trap.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/trap.c,v 1.6 1999/11/30 03:49:28 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | trap traps[MAX_TRAPS]; 45 | bool trap_door = 0; 46 | short bear_trap = 0; 47 | 48 | static const char *const trap_strings[TRAPS * 2] = { 49 | "trap door", "you fell down a trap", 50 | "bear trap", "you are caught in a bear trap", 51 | "teleport trap", "teleport", 52 | "poison dart trap", "a small dart just hit you in the shoulder", 53 | "sleeping gas trap", "a strange white mist envelops you and you fall asleep", 54 | "rust trap", "a gush of water hits you on the head" 55 | }; 56 | 57 | static short trap_at(int row, int col) { 58 | for (short i = 0; ((i < MAX_TRAPS) && (traps[i].trap_type != NO_TRAP)); i++) { 59 | if ((traps[i].trap_row == row) && (traps[i].trap_col == col)) 60 | return traps[i].trap_type; 61 | } 62 | return NO_TRAP; 63 | } 64 | 65 | void trap_player(short row, short col) { 66 | short t = trap_at(row, col); 67 | if (t == NO_TRAP) 68 | return; 69 | 70 | dungeon[row][col] &= (~HIDDEN); 71 | if (rand_percent(rogue.exp + ring_exp)) { 72 | message("the trap failed", 1); 73 | return; 74 | } 75 | switch (t) { 76 | case TRAP_DOOR: 77 | trap_door = 1; 78 | new_level_message = trap_strings[(t * 2) + 1]; 79 | break; 80 | case BEAR_TRAP: 81 | message(trap_strings[(t * 2) + 1], 1); 82 | bear_trap = get_rand(4, 7); 83 | break; 84 | case TELE_TRAP: 85 | mvaddch(rogue.row, rogue.col, '^'); 86 | tele(); 87 | break; 88 | case DART_TRAP: 89 | message(trap_strings[(t * 2) + 1], 1); 90 | rogue.hp_current -= get_damage("1d6", 1); 91 | if (rogue.hp_current <= 0) 92 | rogue.hp_current = 0; 93 | if ((!sustain_strength) && rand_percent(40) && (rogue.str_current >= 3)) 94 | rogue.str_current--; 95 | print_stats(STAT_HP | STAT_STRENGTH); 96 | if (rogue.hp_current <= 0) 97 | killed_by(NULL, POISON_DART); 98 | break; 99 | case SLEEPING_GAS_TRAP: 100 | message(trap_strings[(t * 2) + 1], 1); 101 | take_a_nap(); 102 | break; 103 | case RUST_TRAP: 104 | message(trap_strings[(t * 2) + 1], 1); 105 | rust(NULL); 106 | break; 107 | } 108 | } 109 | 110 | void add_traps(void) { 111 | short n; 112 | if (cur_level <= 2) n = 0; 113 | else if (cur_level <= 7) n = get_rand(0, 2); 114 | else if (cur_level <= 11) n = get_rand(1, 2); 115 | else if (cur_level <= 16) n = get_rand(2, 3); 116 | else if (cur_level <= 21) n = get_rand(2, 4); 117 | else if (cur_level <= AMULET_LEVEL + 2) n = get_rand(3, 5); 118 | else n = get_rand(5, MAX_TRAPS); 119 | 120 | short row, col; 121 | for (short i = 0; i < n; i++) { 122 | traps[i].trap_type = get_rand(0, (TRAPS - 1)); 123 | 124 | short tries = 0; 125 | if ((i == 0) && (party_room != NO_ROOM)) { 126 | do { 127 | row = get_rand((rooms[party_room].top_row+1), (rooms[party_room].bottom_row-1)); 128 | col = get_rand((rooms[party_room].left_col+1), (rooms[party_room].right_col-1)); 129 | tries++; 130 | } while (((dungeon[row][col] & (OBJECT|STAIRS|TRAP|TUNNEL)) || (dungeon[row][col] == NOTHING)) && (tries < 15)); 131 | if (tries >= 15) 132 | gr_row_col(&row, &col, (FLOOR | MONSTER)); 133 | } else 134 | gr_row_col(&row, &col, (FLOOR | MONSTER)); 135 | 136 | traps[i].trap_row = row; 137 | traps[i].trap_col = col; 138 | dungeon[row][col] |= (TRAP | HIDDEN); 139 | } 140 | } 141 | 142 | void id_trap(void) { 143 | message("direction? ", 0); 144 | int dir, d; 145 | while (!is_direction(dir = rgetchar(), &d)) 146 | sound_bell(); 147 | check_message(); 148 | if (dir == CANCEL) 149 | return; 150 | 151 | short row = rogue.row; 152 | short col = rogue.col; 153 | get_dir_rc(d, &row, &col, 0); 154 | 155 | if ((dungeon[row][col] & TRAP) && (!(dungeon[row][col] & HIDDEN))) { 156 | short t = trap_at(row, col); 157 | message(trap_strings[t * 2], 0); 158 | } else 159 | message("no trap there", 0); 160 | } 161 | 162 | void show_traps(void) { 163 | for (short i = 0; i < DROWS; i++) { 164 | for (short j = 0; j < DCOLS; j++) { 165 | if (dungeon[i][j] & TRAP) 166 | mvaddch(i, j, '^'); 167 | } 168 | } 169 | } 170 | 171 | void search(short n, bool is_auto) { 172 | static bool reg_search; 173 | 174 | short found = 0; 175 | for (short i = -1; i <= 1; i++) { 176 | for (short j = -1; j <= 1; j++) { 177 | short row = rogue.row + i; 178 | short col = rogue.col + j; 179 | if ((row < MIN_ROW) || (row >= (DROWS-1)) || (col < 0) || (col >= DCOLS)) 180 | continue; 181 | if (dungeon[row][col] & HIDDEN) 182 | found++; 183 | } 184 | } 185 | 186 | short shown = 0; 187 | for (short s = 0; s < n; s++) { 188 | for (short i = -1; i <= 1; i++) { 189 | for (short j = -1; j <= 1; j++) { 190 | short row = rogue.row + i; 191 | short col = rogue.col + j ; 192 | if ((row < MIN_ROW) || (row >= (DROWS-1)) || (col < 0) || (col >= DCOLS)) 193 | continue; 194 | 195 | if (dungeon[row][col] & HIDDEN) { 196 | if (rand_percent(17 + (rogue.exp + ring_exp))) { 197 | dungeon[row][col] &= (~HIDDEN); 198 | if (!blind && (row != rogue.row || col != rogue.col)) 199 | mvaddch(row, col, get_dungeon_char(row, col)); 200 | shown++; 201 | if (dungeon[row][col] & TRAP) { 202 | short t = trap_at(row, col); 203 | message(trap_strings[t*2], 1); 204 | } 205 | } 206 | } 207 | if ((shown == found && found > 0) || interrupted) 208 | return; 209 | } 210 | } 211 | if (!is_auto && (reg_search = !reg_search)) 212 | reg_move(); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /use.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)use.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/use.c,v 1.4 1999/11/30 03:49:29 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include 43 | #include "rogue.h" 44 | 45 | short halluc = 0; 46 | short blind = 0; 47 | short confused = 0; 48 | short levitate = 0; 49 | short haste_self = 0; 50 | bool see_invisible = 0; 51 | short extra_hp = 0; 52 | bool detect_monster = 0; 53 | bool con_mon = 0; 54 | 55 | static const char strange_feeling[] = 56 | "you have a strange feeling for a moment, then it passes"; 57 | 58 | static const char *get_ench_color(void); 59 | static void go_blind(void); 60 | static void hold_monster(void); 61 | static void idntfy(void); 62 | static void potion_heal(bool); 63 | static void uncurse_all(void); 64 | 65 | void quaff(void) { 66 | short ch = pack_letter("quaff what?", POTION); 67 | if (ch == CANCEL) 68 | return; 69 | 70 | object *obj = get_letter_object(ch); 71 | if (!obj) { 72 | message("no such item.", 0); 73 | return; 74 | } 75 | if (obj->what_is != POTION) { 76 | message("you can't drink that", 0); 77 | return; 78 | } 79 | 80 | switch (obj->which_kind) { 81 | case INCREASE_STRENGTH: 82 | message("you feel stronger now, what bulging muscles!", 0); 83 | rogue.str_current++; 84 | if (rogue.str_current > rogue.str_max) 85 | rogue.str_max = rogue.str_current; 86 | break; 87 | case RESTORE_STRENGTH: 88 | rogue.str_current = rogue.str_max; 89 | message("this tastes great, you feel warm all over", 0); 90 | break; 91 | case HEALING: 92 | message("you begin to feel better", 0); 93 | potion_heal(0); 94 | break; 95 | case EXTRA_HEALING: 96 | message("you begin to feel much better", 0); 97 | potion_heal(1); 98 | break; 99 | case POISON: 100 | if (!sustain_strength) { 101 | rogue.str_current -= get_rand(1, 3); 102 | if (rogue.str_current < 1) 103 | rogue.str_current = 1; 104 | } 105 | message("you feel very sick now", 0); 106 | if (halluc) 107 | unhallucinate(); 108 | break; 109 | case RAISE_LEVEL: 110 | rogue.exp_points = level_points[rogue.exp - 1]; 111 | message("you suddenly feel much more skillful", 0); 112 | add_exp(1, 1); 113 | break; 114 | case BLINDNESS: 115 | go_blind(); 116 | break; 117 | case HALLUCINATION: 118 | message("oh wow, everything seems so cosmic", 0); 119 | halluc += get_rand(500, 800); 120 | break; 121 | case DETECT_MONSTER: 122 | show_monsters(); 123 | if (!level_monsters.next_monster) 124 | message(strange_feeling, 0); 125 | break; 126 | case DETECT_OBJECTS: 127 | if (level_objects.next_object) { 128 | if (!blind) 129 | show_objects(); 130 | } else 131 | message(strange_feeling, 0); 132 | break; 133 | case CONFUSION: 134 | message((halluc ? "what a trippy feeling" : "you feel confused"), 0); 135 | cnfs(); 136 | break; 137 | case LEVITATION: 138 | message("you start to float in the air", 0); 139 | levitate += get_rand(15, 30); 140 | bear_trap = being_held = 0; 141 | break; 142 | case HASTE_SELF: 143 | message("you feel yourself moving much faster", 0); 144 | haste_self += get_rand(11, 21); 145 | if (!(haste_self % 2)) 146 | haste_self++; 147 | break; 148 | case SEE_INVISIBLE: 149 | messagef(0, "hmm, this potion tastes like %sjuice", fruit); 150 | if (blind) 151 | unblind(); 152 | see_invisible = 1; 153 | relight(); 154 | break; 155 | } 156 | print_stats((STAT_STRENGTH | STAT_HP)); 157 | if (id_potions[obj->which_kind].id_status != CALLED) 158 | id_potions[obj->which_kind].id_status = IDENTIFIED; 159 | vanish(obj, 1, &rogue.pack); 160 | } 161 | 162 | void read_scroll(void) { 163 | short ch = pack_letter("read what?", SCROL); 164 | if (ch == CANCEL) 165 | return; 166 | 167 | object *obj = get_letter_object(ch); 168 | if (!obj) { 169 | message("no such item.", 0); 170 | return; 171 | } 172 | if (obj->what_is != SCROL) { 173 | message("you can't read that", 0); 174 | return; 175 | } 176 | 177 | switch (obj->which_kind) { 178 | case SCARE_MONSTER: 179 | message("you hear a maniacal laughter in the distance", 0); 180 | break; 181 | case HOLD_MONSTER: 182 | hold_monster(); 183 | break; 184 | case ENCH_WEAPON: 185 | if (rogue.weapon) { 186 | if (rogue.weapon->what_is == WEAPON) { 187 | messagef(0, "your %sglow%s %sfor a moment", 188 | name_of(rogue.weapon), ((rogue.weapon->quantity <= 1) ? "s" : ""), get_ench_color()); 189 | if (coin_toss()) 190 | rogue.weapon->hit_enchant++; 191 | else 192 | rogue.weapon->d_enchant++; 193 | } 194 | rogue.weapon->is_cursed = 0; 195 | } else 196 | message("your hands tingle", 0); 197 | break; 198 | case ENCH_ARMOR: 199 | if (rogue.armor) { 200 | messagef(0, "your armor glows %sfor a moment", get_ench_color()); 201 | rogue.armor->d_enchant++; 202 | rogue.armor->is_cursed = 0; 203 | print_stats(STAT_ARMOR); 204 | } else 205 | message("your skin crawls", 0); 206 | break; 207 | case IDENTIFY: 208 | message("this is a scroll of identify", 0); 209 | obj->identified = 1; 210 | id_scrolls[obj->which_kind].id_status = IDENTIFIED; 211 | idntfy(); 212 | break; 213 | case TELEPORT: 214 | tele(); 215 | break; 216 | case SLEEP: 217 | message("you fall asleep", 0); 218 | take_a_nap(); 219 | break; 220 | case PROTECT_ARMOR: 221 | if (rogue.armor) { 222 | message("your armor is covered by a shimmering gold shield", 0); 223 | rogue.armor->is_protected = 1; 224 | rogue.armor->is_cursed = 0; 225 | } else 226 | message("your acne seems to have disappeared", 0); 227 | break; 228 | case REMOVE_CURSE: 229 | message((!halluc) ? 230 | "you feel as though someone is watching over you" : 231 | "you feel in touch with the universal oneness", 0); 232 | uncurse_all(); 233 | break; 234 | case CREATE_MONSTER: 235 | create_monster(); 236 | break; 237 | case AGGRAVATE_MONSTER: 238 | aggravate(); 239 | break; 240 | case MAGIC_MAPPING: 241 | message("this scroll seems to have a map on it", 0); 242 | draw_magic_map(); 243 | break; 244 | case CON_MON: 245 | con_mon = 1; 246 | messagef(0, "your hands glow %sfor a moment", get_ench_color()); 247 | break; 248 | } 249 | if (id_scrolls[obj->which_kind].id_status != CALLED) 250 | id_scrolls[obj->which_kind].id_status = IDENTIFIED; 251 | vanish(obj, (obj->which_kind != SLEEP), &rogue.pack); 252 | } 253 | 254 | // vanish() does NOT handle a quiver of weapons with more than one arrow (or 255 | // whatever) in the quiver. It will only decrement the count. 256 | void vanish(object *obj, short rm, object *pack) { 257 | if (obj->quantity > 1) 258 | obj->quantity--; 259 | else { 260 | if (obj->in_use_flags & BEING_WIELDED) 261 | unwield(obj); 262 | else if (obj->in_use_flags & BEING_WORN) 263 | unwear(obj); 264 | else if (obj->in_use_flags & ON_EITHER_HAND) 265 | un_put_on(obj); 266 | take_from_pack(obj, pack); 267 | free_object(obj); 268 | } 269 | if (rm) 270 | reg_move(); 271 | } 272 | 273 | static void potion_heal(bool extra) { 274 | rogue.hp_current += rogue.exp; 275 | 276 | float ratio = ((float)rogue.hp_current) / rogue.hp_max; 277 | if (ratio >= 1.00) { 278 | rogue.hp_max += (extra ? 2 : 1); 279 | extra_hp += (extra ? 2 : 1); 280 | rogue.hp_current = rogue.hp_max; 281 | } else if (ratio >= 0.90) { 282 | rogue.hp_max += (extra ? 1 : 0); 283 | extra_hp += (extra ? 1 : 0); 284 | rogue.hp_current = rogue.hp_max; 285 | } else { 286 | if (ratio < 0.33) 287 | ratio = 0.33; 288 | if (extra) 289 | ratio += ratio; 290 | short add = (short)(ratio * ((float)rogue.hp_max - rogue.hp_current)); 291 | rogue.hp_current += add; 292 | if (rogue.hp_current > rogue.hp_max) 293 | rogue.hp_current = rogue.hp_max; 294 | } 295 | 296 | if (blind) 297 | unblind(); 298 | 299 | if (confused && extra) 300 | unconfuse(); 301 | else if (confused) 302 | confused = (confused / 2) + 1; 303 | 304 | if (halluc && extra) 305 | unhallucinate(); 306 | else if (halluc) 307 | halluc = (halluc / 2) + 1; 308 | } 309 | 310 | static void idntfy(void) { 311 | short ch; 312 | object *obj; 313 | struct id *id_table; 314 | char desc[DCOLS]; 315 | 316 | again: 317 | ch = pack_letter("what would you like to identify?", ALL_OBJECTS); 318 | if (ch == CANCEL) 319 | return; 320 | 321 | if (!(obj = get_letter_object(ch))) { 322 | message("no such item, try again", 0); 323 | message("", 0); 324 | check_message(); 325 | goto again; 326 | } 327 | obj->identified = 1; 328 | if (obj->what_is & (SCROL | POTION | WEAPON | ARMOR | WAND | RING)) { 329 | id_table = get_id_table(obj); 330 | id_table[obj->which_kind].id_status = IDENTIFIED; 331 | } 332 | get_desc(obj, desc); 333 | message(desc, 0); 334 | } 335 | 336 | void eat(void) { 337 | short ch = pack_letter("eat what?", FOOD); 338 | if (ch == CANCEL) 339 | return; 340 | 341 | object *obj = get_letter_object(ch); 342 | if (!obj) { 343 | message("no such item.", 0); 344 | return; 345 | } 346 | if (obj->what_is != FOOD) { 347 | message("you can't eat that", 0); 348 | return; 349 | } 350 | 351 | short moves; 352 | if ((obj->which_kind == FRUIT) || rand_percent(60)) { 353 | moves = get_rand(950, 1150); 354 | if (obj->which_kind == RATION) 355 | message("yum, that tasted good", 0); 356 | else 357 | messagef(0, "my, that was a yummy %s", fruit); 358 | } else { 359 | moves = get_rand(750, 950); 360 | message("yuk, that food tasted awful", 0); 361 | add_exp(2, 1); 362 | } 363 | rogue.moves_left /= 3; 364 | rogue.moves_left += moves; 365 | hunger_str[0] = 0; 366 | print_stats(STAT_HUNGER); 367 | 368 | vanish(obj, 1, &rogue.pack); 369 | } 370 | 371 | static void hold_monster(void) { 372 | short mcount = 0; 373 | for (short i = -2; i <= 2; i++) { 374 | for (short j = -2; j <= 2; j++) { 375 | short row = rogue.row + i; 376 | short col = rogue.col + j; 377 | if ((row < MIN_ROW) || (row > (DROWS-2)) || (col < 0) || 378 | (col > (DCOLS-1))) { 379 | continue; 380 | } 381 | if (dungeon[row][col] & MONSTER) { 382 | object *monster = object_at(&level_monsters, row, col); 383 | monster->m_flags |= ASLEEP; 384 | monster->m_flags &= (~WAKENS); 385 | mcount++; 386 | } 387 | } 388 | } 389 | if (mcount == 0) 390 | message("you feel a strange sense of loss", 0); 391 | else if (mcount == 1) 392 | message("the monster freezes", 0); 393 | else 394 | message("the monsters around you freeze", 0); 395 | } 396 | 397 | void tele(void) { 398 | mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col)); 399 | 400 | if (cur_room >= 0) 401 | darken_room(cur_room); 402 | put_player(get_room_number(rogue.row, rogue.col)); 403 | being_held = 0; 404 | bear_trap = 0; 405 | } 406 | 407 | void hallucinate(void) { 408 | if (blind) 409 | return; 410 | 411 | object *obj = level_objects.next_object; 412 | while (obj) { 413 | short ch = (short)mvinch(obj->row, obj->col); 414 | if ((ch < 'A' || ch > 'Z') && (obj->row != rogue.row || obj->col != rogue.col)) { 415 | if (ch != ' ' && ch != '.' && ch != '#' && ch != '+') 416 | addch(gr_obj_char()); 417 | } 418 | obj = obj->next_object; 419 | } 420 | 421 | object *monster = level_monsters.next_monster; 422 | while (monster) { 423 | short ch = (short)mvinch(monster->row, monster->col); 424 | if (ch >= 'A' && ch <= 'Z') 425 | addch(get_rand('A', 'Z')); 426 | monster = monster->next_monster; 427 | } 428 | } 429 | 430 | void unhallucinate(void) { 431 | halluc = 0; 432 | relight(); 433 | message("everything looks SO boring now", 1); 434 | } 435 | 436 | void unblind(void) { 437 | blind = 0; 438 | message("the veil of darkness lifts", 1); 439 | relight(); 440 | if (halluc) 441 | hallucinate(); 442 | if (detect_monster) 443 | show_monsters(); 444 | } 445 | 446 | void relight(void) { 447 | if (cur_room == PASSAGE) 448 | light_passage(rogue.row, rogue.col); 449 | else 450 | light_up_room(cur_room); 451 | mvaddch(rogue.row, rogue.col, rogue.fchar); 452 | } 453 | 454 | void take_a_nap(void) { 455 | short i = get_rand(2, 5); 456 | sleep(1); 457 | while (i--) 458 | mv_mons(); 459 | sleep(1); 460 | message(you_can_move_again, 0); 461 | } 462 | 463 | static void go_blind(void) { 464 | if (!blind) 465 | message("a cloak of darkness falls around you", 0); 466 | 467 | blind += get_rand(500, 800); 468 | 469 | if (detect_monster) { 470 | object *monster = level_monsters.next_monster; 471 | while (monster) { 472 | mvaddch(monster->row, monster->col, monster->trail_char); 473 | monster = monster->next_monster; 474 | } 475 | } 476 | if (cur_room >= 0) { 477 | for (short i = rooms[cur_room].top_row + 1; i < rooms[cur_room].bottom_row; i++) { 478 | for (short j = rooms[cur_room].left_col + 1; j < rooms[cur_room].right_col; j++) 479 | mvaddch(i, j, ' '); 480 | } 481 | } 482 | mvaddch(rogue.row, rogue.col, rogue.fchar); 483 | } 484 | 485 | static const char * get_ench_color(void) { 486 | if (halluc) 487 | return id_potions[get_rand(0, POTIONS-1)].title; 488 | else if (con_mon) 489 | return "red "; 490 | return "blue "; 491 | } 492 | 493 | void cnfs(void) { 494 | confused += get_rand(12, 22); 495 | } 496 | 497 | void unconfuse(void) { 498 | confused = 0; 499 | messagef(1, "you feel less %s now", (halluc ? "trippy" : "confused")); 500 | } 501 | 502 | static void uncurse_all(void) { 503 | object *obj = rogue.pack.next_object; 504 | while (obj) { 505 | obj->is_cursed = 0; 506 | obj = obj->next_object; 507 | } 508 | } 509 | -------------------------------------------------------------------------------- /zap.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 1988, 1993 2 | * The Regents of the University of California. All rights reserved. 3 | * 4 | * This code is derived from software contributed to Berkeley by 5 | * Timothy C. Stoehr. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | * @(#)zap.c 8.1 (Berkeley) 5/31/93 32 | * $FreeBSD: src/games/rogue/zap.c,v 1.3 1999/11/30 03:49:29 billf Exp $ 33 | * 34 | * This source herein may be modified and/or distributed by anybody who 35 | * so desires, with the following restrictions: 36 | * 1.) No portion of this notice shall be removed. 37 | * 2.) Credit shall not be taken for the creation of this source. 38 | * 3.) This code is not to be traded, sold, or used for personal 39 | * gain or profit. 40 | */ 41 | 42 | #include "rogue.h" 43 | 44 | static object *get_zapped_monster(short, short *, short *); 45 | static void tele_away(object *); 46 | static void wdrain_life(object *); 47 | static void zap_monster(object *, unsigned short); 48 | 49 | bool wizard = 0; 50 | 51 | void zapp(void) { 52 | message("zap direction? ", 0); 53 | 54 | int dir, d; 55 | bool first_miss = 1; 56 | while (!is_direction(dir = rgetchar(), &d)) { 57 | sound_bell(); 58 | if (first_miss) { 59 | message("direction? ", 0); 60 | first_miss = 0; 61 | } 62 | } 63 | check_message(); 64 | if (dir == CANCEL) 65 | return; 66 | short wch = pack_letter("zap with what?", WAND); 67 | if (wch == CANCEL) 68 | return; 69 | 70 | check_message(); 71 | object *wand = get_letter_object(wch); 72 | if (!wand) { 73 | message("no such item.", 0); 74 | return; 75 | } 76 | if (wand->what_is != WAND) { 77 | message("you can't zap with that", 0); 78 | return; 79 | } 80 | if (wand->class <= 0) 81 | message("nothing happens", 0); 82 | else { 83 | wand->class--; 84 | short row = rogue.row, col = rogue.col; 85 | if ((wand->which_kind == COLD) || (wand->which_kind == FIRE)) 86 | bounce((short)wand->which_kind, d, row, col, 0); 87 | else { 88 | object *monster = get_zapped_monster(d, &row, &col); 89 | if (wand->which_kind == DRAIN_LIFE) 90 | wdrain_life(monster); 91 | else if (monster) { 92 | wake_up(monster); 93 | s_con_mon(monster); 94 | zap_monster(monster, wand->which_kind); 95 | relight(); 96 | } 97 | } 98 | } 99 | reg_move(); 100 | } 101 | 102 | static object * get_zapped_monster(short dir, short *row, short *col) { 103 | for (;;) { 104 | short orow = *row, ocol = *col; 105 | get_dir_rc(dir, row, col, 0); 106 | if ((*row == orow && *col == ocol) || (dungeon[*row][*col] & (HORWALL | VERTWALL)) || dungeon[*row][*col] == NOTHING) 107 | return 0; 108 | if (dungeon[*row][*col] & MONSTER) { 109 | if (!imitating(*row, *col)) 110 | return object_at(&level_monsters, *row, *col); 111 | } 112 | } 113 | } 114 | 115 | static void zap_monster(object *monster, unsigned short kind) { 116 | short row = monster->row, col = monster->col; 117 | switch (kind) { 118 | case SLOW_MONSTER: 119 | if (monster->m_flags & HASTED) { 120 | monster->m_flags &= (~HASTED); 121 | } else { 122 | monster->slowed_toggle = 0; 123 | monster->m_flags |= SLOWED; 124 | } 125 | break; 126 | case HASTE_MONSTER: 127 | if (monster->m_flags & SLOWED) 128 | monster->m_flags &= (~SLOWED); 129 | else 130 | monster->m_flags |= HASTED; 131 | break; 132 | case TELE_AWAY: 133 | tele_away(monster); 134 | break; 135 | case INVISIBILITY: 136 | monster->m_flags |= INVISIBLE; 137 | break; 138 | case POLYMORPH: 139 | if (monster->m_flags & HOLDS) 140 | being_held = 0; 141 | 142 | object *nm = monster->next_monster; 143 | short tc = monster->trail_char; 144 | gr_monster(monster, get_rand(0, MONSTERS-1)); 145 | monster->row = row; 146 | monster->col = col; 147 | monster->next_monster = nm; 148 | monster->trail_char = tc; 149 | if (!(monster->m_flags & IMITATES)) 150 | wake_up(monster); 151 | break; 152 | case MAGIC_MISSILE: 153 | rogue_hit(monster, 1); 154 | break; 155 | case CANCELLATION: 156 | if (monster->m_flags & HOLDS) 157 | being_held = 0; 158 | if (monster->m_flags & STEALS_ITEM) 159 | monster->drop_percent = 0; 160 | monster->m_flags &= (~(FLIES | FLITS | SPECIAL_HIT | INVISIBLE | 161 | FLAMES | IMITATES | CONFUSES | SEEKS_GOLD | HOLDS)); 162 | break; 163 | case DO_NOTHING: 164 | message("nothing happens", 0); 165 | break; 166 | } 167 | } 168 | 169 | static void tele_away(object *monster) { 170 | if (monster->m_flags & HOLDS) 171 | being_held = 0; 172 | 173 | short row, col; 174 | gr_row_col(&row, &col, (FLOOR | TUNNEL | STAIRS | OBJECT)); 175 | 176 | mvaddch(monster->row, monster->col, monster->trail_char); 177 | dungeon[monster->row][monster->col] &= ~MONSTER; 178 | monster->row = row; monster->col = col; 179 | dungeon[row][col] |= MONSTER; 180 | monster->trail_char = (short)mvinch(row, col); 181 | if (detect_monster || rogue_can_see(row, col)) 182 | mvaddch(row, col, gmc(monster)); 183 | } 184 | 185 | void wizardize(void) { 186 | if (wizard) { 187 | wizard = 0; 188 | message("not wizard anymore", 0); 189 | return; 190 | } 191 | 192 | char buf[100]; 193 | if (get_input_line("wizard's password:", "", buf, 0)) { 194 | if (!strncmp(buf, "bathtub", 7)) { 195 | wizard = 1; 196 | score_only = 1; 197 | message("Welcome, mighty wizard!", 0); 198 | } else 199 | message("sorry", 0); 200 | } 201 | } 202 | 203 | static void wdrain_life(object *monster) { 204 | short hp = rogue.hp_current / 3; 205 | rogue.hp_current = (rogue.hp_current + 1) / 2; 206 | 207 | if (cur_room >= 0) { 208 | object *lmon = level_monsters.next_monster; 209 | while (lmon) { 210 | object *nm = lmon->next_monster; 211 | if (get_room_number(lmon->row, lmon->col) == cur_room) { 212 | wake_up(lmon); 213 | mon_damage(lmon, hp); 214 | } 215 | lmon = nm; 216 | } 217 | } else { 218 | if (monster) { 219 | wake_up(monster); 220 | mon_damage(monster, hp); 221 | } 222 | } 223 | print_stats(STAT_HP); 224 | relight(); 225 | } 226 | 227 | void bounce(short ball, short dir, short row, short col, short r) { 228 | static short btime; 229 | if (++r == 1) 230 | btime = get_rand(3, 6); 231 | else if (r > btime) 232 | return; 233 | 234 | const char *s = (ball == FIRE ? "fire" : "ice"); 235 | short i, ch, new_dir = -1, damage; 236 | 237 | if (r > 1) 238 | messagef(0, "the %s bounces", s); 239 | 240 | short orow = row, ocol = col; 241 | do { 242 | ch = (short)mvinch(orow, ocol); 243 | standout(); 244 | mvaddch(orow, ocol, ch); 245 | get_dir_rc(dir, &orow, &ocol, 1); 246 | } while (!( (ocol <= 0) || 247 | (ocol >= DCOLS-1) || 248 | (dungeon[orow][ocol] == NOTHING) || 249 | (dungeon[orow][ocol] & MONSTER) || 250 | (dungeon[orow][ocol] & (HORWALL | VERTWALL)) || 251 | ((orow == rogue.row) && (ocol == rogue.col)))); 252 | standend(); 253 | refresh(); 254 | do { 255 | orow = row; 256 | ocol = col; 257 | ch = (short)mvinch(row, col); 258 | mvaddch(row, col, ch); 259 | get_dir_rc(dir, &row, &col, 1); 260 | } while (!( (col <= 0) || 261 | (col >= DCOLS-1) || 262 | (dungeon[row][col] == NOTHING) || 263 | (dungeon[row][col] & MONSTER) || 264 | (dungeon[row][col] & (HORWALL | VERTWALL)) || 265 | ((row == rogue.row) && (col == rogue.col)))); 266 | 267 | if (dungeon[row][col] & MONSTER) { 268 | object *monster = object_at(&level_monsters, row, col); 269 | wake_up(monster); 270 | if (rand_percent(33)) { 271 | messagef(0, "the %s misses the %s", s, mon_name(monster)); 272 | goto ND; 273 | } 274 | if (ball == FIRE) { 275 | if (!(monster->m_flags & RUSTS)) { 276 | if (monster->m_flags & FREEZES) 277 | damage = monster->hp_to_kill; 278 | else if (monster->m_flags & FLAMES) 279 | damage = (monster->hp_to_kill / 10) + 1; 280 | else 281 | damage = get_rand((rogue.hp_current / 3), rogue.hp_max); 282 | } else 283 | damage = (monster->hp_to_kill / 2) + 1; 284 | messagef(0, "the %s hits the %s", s, mon_name(monster)); 285 | mon_damage(monster, damage); 286 | } else { 287 | damage = -1; 288 | if (!(monster->m_flags & FREEZES)) { 289 | if (rand_percent(33)) { 290 | message("the monster is frozen", 0); 291 | monster->m_flags |= (ASLEEP | NAPPING); 292 | monster->nap_length = get_rand(3, 6); 293 | } else 294 | damage = rogue.hp_current / 4; 295 | } else 296 | damage = -2; 297 | if (damage != -1) { 298 | messagef(0, "the %s hits the %s", s, mon_name(monster)); 299 | mon_damage(monster, damage); 300 | } 301 | } 302 | } else if ((row == rogue.row) && (col == rogue.col)) { 303 | if (rand_percent(10 + (3 * get_armor_class(rogue.armor)))) { 304 | messagef(0, "the %s misses", s); 305 | goto ND; 306 | } else { 307 | damage = get_rand(3, (3 * rogue.exp)); 308 | if (ball == FIRE) { 309 | damage = (damage * 3) / 2; 310 | damage -= get_armor_class(rogue.armor); 311 | } 312 | rogue_damage(damage, NULL, ((ball == FIRE) ? KFIRE : HYPOTHERMIA)); 313 | messagef(0, "the %s hits", s); 314 | } 315 | } else { 316 | short nrow, ncol; 317 | 318 | ND: for (i = 0; i < 10; i++) { 319 | dir = get_rand(0, DIRS-1); 320 | nrow = orow; 321 | ncol = ocol; 322 | get_dir_rc(dir, &nrow, &ncol, 1); 323 | if ((ncol >= 0 && ncol <= DCOLS-1) && dungeon[nrow][ncol] != NOTHING && 324 | (!(dungeon[nrow][ncol] & (VERTWALL | HORWALL))) 325 | ) { 326 | new_dir = dir; 327 | break; 328 | } 329 | } 330 | if (new_dir != -1) 331 | bounce(ball, new_dir, orow, ocol, r); 332 | } 333 | } 334 | --------------------------------------------------------------------------------