# HG changeset patch # User John "Elwin" Edwards # Date 1485910564 18000 # Node ID c495a4f288c6dfe91d92fe9b7cfb1160bd8a8d6e # Parent d9badb9c0179807735aee51381bcdeb6f70c2d8d Import UltraRogue from the Roguelike Restoration Project (r1490) diff -r d9badb9c0179 -r c495a4f288c6 urogue/LICENSE.TXT --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/LICENSE.TXT Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,138 @@ +Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong +Portions Copyright (C) 1985 Michael Morgan, Ken Dalka +Portions Copyright (C) 1981 Michael Toy, Ken Arnold and Glenn Wichman +Portions Copyright (C) 1993, 1995 Nicholas J. Kisseberth +All rights reserved. + +=========================================================================== + +UltaRogue: The Ultimate Adventure in the Dungeons of Doom +Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. +4. The name "UltraRogue" and "urogue" must not be used to endorse or + promote products derived from this software without prior written + permission. +5. Products derived from this software may not be called "UltraRogue" or + "urogue", nor may "UltraRogue" or "urogue" appear in their name, + without prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +=========================================================================== + +Portions of this software are based on the work of Michael Morgan and +Ken Dalka. Used under license: + +Advanced Rogue +Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +=========================================================================== + +Portions of this software are based on the work of Michael Toy, Ken Arnold +and Glenn Wichman. Used under license: + +Rogue: Exploring the Dungeons of Doom +Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +=========================================================================== + +Portions of this software (save/restore game state) are based on the work +of Nicholas J. Kisseberth. Used under license: + +Copyright (C) 1993, 1995 Nicholas J. Kisseberth + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff -r d9badb9c0179 -r c495a4f288c6 urogue/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/Makefile Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,167 @@ +# UltraRogue: The Ultimate Adventure in the Dungeons of Doom +# Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong +# All rights reserved. +# +# See the file LICENSE.TXT for full copyright and licensing information. + +# +# Makefile for urogue +# + +DISTNAME=urogue1.0.7 + +HDRS = dict.h dictutil.h rogue.h +OBJS = armor.o \ + artifact.o \ + bag.o \ + chase.o \ + command.o \ + daemon.o \ + daemons.o \ + dict.o \ + dictutil.o \ + encumb.o \ + fight.o \ + getplay.o \ + ident.o \ + init.o \ + io.o \ + list.o \ + magic.o \ + main.o \ + maze.o \ + memory.o \ + misc.o \ + monsdata.o \ + monsters.o \ + move.o \ + newlvl.o \ + options.o \ + pack.o \ + passages.o \ + player.o \ + potions.o \ + random.o \ + rings.o \ + rip.o \ + rooms.o \ + save.o \ + scrolls.o \ + state.o \ + status.o \ + sticks.o \ + things.o \ + trader.o \ + verify.o \ + vers.o \ + weapons.o \ + wizard.o + +PROGRAM = ur + +CFILES = armor.c \ + artifact.c \ + bag.c \ + chase.c \ + command.c \ + daemon.c \ + daemons.c \ + dict.c \ + dictutil.c \ + encumb.c \ + fight.c \ + getplay.c \ + ident.c \ + init.c \ + io.c \ + list.c \ + magic.c \ + main.c \ + maze.c \ + memory.c \ + misc.c \ + monsdata.c \ + monsters.c \ + move.c \ + newlvl.c \ + options.c \ + pack.c \ + passages.c \ + player.c \ + potions.c \ + random.c \ + rings.c \ + rip.c \ + rooms.c \ + save.c \ + scrolls.c \ + state.c \ + status.c \ + sticks.c \ + things.c \ + trader.c \ + verify.c \ + vers.c \ + weapons.c \ + wizard.c + +MISC= Makefile README LICENSE.TXT history.txt TODO + +CC = gcc +CFLAGS= -O3 +CRLIB = -lcurses +RM = rm -f +TAR = tar + +urogue: $(OBJS) $(MAKEFILE) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(CRLIB) -o $@ + +clean: + rm -f $(OBJS) urogue a.out core *.map urogue.exe urogue.cat + +dist.src: + make clean + tar cf $(DISTNAME)-src.tar $(CFILES) $(HDRS) $(MISC) + gzip -f $(DISTNAME)-src.tar + +dist.irix: + make clean + make CC=cc CFLAGS="-woff 1116 -O3" urogue + nroff -man urogue.6 | colcrt - > urogue.cat + tar cf $(DISTNAME)-irix.tar urogue urogue.cat README LICENSE.TXT + gzip -f $(DISTNAME)-irix.tar + +dist.aix: + make clean + make CC=xlc CFLAGS="-qmaxmem=16768 -O3 -qstrict" urogue + nroff -man urogue.6 | colcrt - > urogue.cat + tar cf $(DISTNAME)-aix.tar urogue urogue.cat README LICENSE.TXT + gzip -f $(DISTNAME)-aix.tar + +dist.linux: + make clean + make urogue + groff -man urogue.6 | sed -e 's/.\x08//g' > urogue.cat + tar cf $(DISTNAME)-linux.tar urogue urogue.cat README LICENSE.TXT + gzip -f $(DISTNAME)-linux.tar + +dist.interix: + make clean + make urogue + groff -P-b -P-u -man -Tascii urogue.6 > urogue.cat + tar cf $(DISTNAME)-interix.tar urogue urogue.cat README LICENSE.TXT + gzip -f $(DISTNAME)-interix.tar + +dist.cygwin: + make clean + make urogue + groff -P-c -man -Tascii urogue.6 | sed -e 's/.\x08//g' > urogue.cat + tar cf $(DISTNAME)-cygwin.tar urogue.exe urogue.cat README LICENSE.TXT + gzip -f $(DISTNAME)-cygwin.tar + +dist.djgpp: + make clean + make LDFLAGS="-L$(DJDIR)/LIB" CRLIB="-lpdcurses" urogue + groff -man -Tascii urogue.6 | sed -e 's/.\x08//g' > urogue.cat + rm -f $(DISTNAME)-djgpp.zip + zip $(DISTNAME)-djgpp.zip urogue.exe urogue.cat README LICENSE.TXT diff -r d9badb9c0179 -r c495a4f288c6 urogue/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/README Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,74 @@ +UltraRogue: The Ultimate Adventure in the Dungeons of Doom +Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong +All rights reserved. + +There are 8 different treasures to be picked up in this version of rogue. +The first is on level 25 and is relatively easy to pick up. The last is +on level 100 and is very difficult to get. Carrying any one of the +unique treasures allows the rogue to go up the stairs, but one has to +be very careful. To be a total winner, yu must pick up all 8 and +return to the surface again. + +(note: the above may be incorrect, i think the number of levels is +reduced to 50 and you have to get all 8 artifacts to go back up) + +The environment variable UROGUE is used to set up the default things +for a player. The options that can be set are: + +terse: short output messages +flush: flush typeahead when fighting +jump: show position only at the end of running +inven: inventory style +askme: ask about unidentified things +stopdoor: stop running when next to something interesting +name: the rogue's name (string) +fruit: the funny fruit used by the rogue (string) +file: default savefile name (string) +score: default scorefile name (string) +class: default player class (string) + +Something like + +setenv UROGUE "name=George of the Jungle,fruit=peach,noterse,jump" + +does the obvious. The "score" option is ignored unless you start +urogue in wizard mode. The class option string can have the following +values: "fighter", "illus", "paladin", "ranger", "cleric", "magic", +"assasin", "druid", "ninja", and "thief" and is initialized only at +startup time. The "inven" option can take the values "slow", "clear", +and "overwrite". + + +About the Author +================ + +Herb Chong is currently freelance writing and doing some teaching. +If you pick up a copy of Windows Sources Magazine, you can usually +find something he has written. Starting in October (1993) he will +have a regular column. Herb has also just started teaching Adult Ed +and undergraduate classes at Mercy College, a small and not very well +known college in Westchester, NY. + +Acknowledgements +================ + +The source code for Rogue 3.6 (by Michael Toy, Ken Arnold and Glenn +Wichman) was used as the original basis for this game. A thousand +thanks go out to them and their classic adventure game of the early 1980s. + +Modifications of a long forgotten and bastardized nature were taken +from Advanced Rogue 1.0 and/or SuperRogue. We believe that the flea market, +pools, and maze levels had their origin somewhere around here. + +Chief Architect : Herb Chong + +Major Contributers: Carlton Hommel Mike Cooper Mike Laman + Jason Venner Nick Kisseberth + +Other Contributers: Nick Flor Henry Chai Pat Place + Michael Maudlin dan@ciprico edjames@ucbshadow + Web Dove Tim Haapanen tecot@cmu-cs-k.arpa + +Any omissions or errors are purely unintentional. The above list was +compiled by Nick Kisseberth from the change log in UltraRogue through +1987 and from correspondense with Herb Chong. diff -r d9badb9c0179 -r c495a4f288c6 urogue/TODO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/TODO Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,23 @@ +UltraRogue: The Ultimate Adventure in the Dungeons of Doom +Copyright (C) 1993, 1995 Herb Chong +All rights reserved. + +%W% %G% + +add_pack() calls ur_free() on the linked list pointer passed +to it. Need to go through code and look for cases where we +use that pointer after calling add_pack(). Electric +fence in guard-free() mode should help. + +Similary throw_away(), discard(), discard_pack() free items... +there will be more like this... + +Bug in genocide scroll: don't request list, type letter, etc... + +Implement potion of true sight. + +Bug: apparently when creating a new familiar after a player level +change, something isn't begin reset because ur SEGVs when running +through the monster list in do_chase() right after the new +familiar is created. It appears that the familiar isn't being removed +from the monster list. diff -r d9badb9c0179 -r c495a4f288c6 urogue/armor.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/armor.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,207 @@ +/* + armor.c - functions for dealing with armor + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include "rogue.h" + +/* + wear() + The player wants to wear something, so let him/her put it on. +*/ + +void +wear(void) +{ + struct object *obj; + + if (cur_armor != NULL) + { + msg("You are already wearing some."); + + after = FALSE; + + return; + } + + /* What does player want to wear? */ + + if ((obj = get_object(pack, "wear", ARMOR, NULL)) == NULL) + return; + + wear_ok(&player, obj, MESSAGE); + waste_time(); + + cur_armor = obj; + obj->o_flags |= ISKNOW; + + msg("You are now wearing %s.", inv_name(obj, TRUE)); + + return; +} + +/* + take_off() + Get the armor off of the players back +*/ + +void +take_off(void) +{ + struct object *obj; + + if ((obj = cur_armor) == NULL) + { + msg("You aren't wearing armor!"); + return; + } + + if (!dropcheck(cur_armor)) + return; + + msg("You were wearing %c%c) %s.", ARMOR, print_letters[get_ident(obj)], + inv_name(obj, LOWERCASE)); + + cur_armor = NULL; + + if (on(player, STUMBLER)) + { + msg("Your foot feels a lot better now."); + turn_off(player, STUMBLER); + } +} + +/* + wear_ok() + enforce player class armor restrictions +*/ + +int +wear_ok(struct thing *wearee, struct object *obj, int print_message) +{ + int which = obj->o_which; + int ret_val = TRUE; + int class_type = wearee->t_ctype; + + if (obj->o_type != ARMOR) + return(FALSE); + else + switch (class_type) + { + case C_MAGICIAN: /* cannot wear metal */ + case C_ILLUSION: + switch (which) + { + case RING_MAIL: + case SCALE_MAIL: + case PADDED_ARMOR: + case CHAIN_MAIL: + case BRIGANDINE: + case SPLINT_MAIL: + case GOOD_CHAIN: + case PLATE_MAIL: + case PLATE_ARMOR: + ret_val = FALSE; + break; + default: + break; + } + + case C_THIEF: /* cannot clank around */ + case C_ASSASIN: + case C_NINJA: + switch (which) + { + case CHAIN_MAIL: + case BRIGANDINE: + case SPLINT_MAIL: + case GOOD_CHAIN: + case PLATE_MAIL: + case PLATE_ARMOR: + ret_val = FALSE; + break; + default: + break; + } + + case C_CLERIC: /* cannot wear plate */ + case C_DRUID: + switch (which) + { + case PLATE_MAIL: + case PLATE_ARMOR: + case MITHRIL: + ret_val = FALSE; + break; + default: + break; + } + + case C_FIGHTER: /* wear anything */ + case C_RANGER: + break; + + case C_PALADIN: /* cannot wear common stuff */ + switch (which) + { + case SOFT_LEATHER: + case CUIRBOLILLI: + case HEAVY_LEATHER: + case STUDDED_LEATHER: + case PADDED_ARMOR: + case BRIGANDINE: + ret_val = FALSE; + break; + default: + break; + } + + case C_MONSTER: + break; + + default: /* Unknown class */ + debug("Unknown class %d.", class_type); + break; + } + + if (ret_val == FALSE && print_message == MESSAGE) + switch (class_type) + { + case C_MAGICIAN: + case C_ILLUSION: + msg("You cannot regenerate spell points while wearing that!"); + break; + + case C_THIEF: + case C_ASSASIN: + case C_NINJA: + msg("Don't expect to be stealthy while wearing that!"); + break; + + case C_CLERIC: + case C_DRUID: + case C_PALADIN: + msg("Your god strongly disapproves of your wearing that!"); + break; + + case C_FIGHTER: + case C_RANGER: + case C_MONSTER: + break; + } + + return(ret_val); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/artifact.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/artifact.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1922 @@ +/* + artifact.c - functions for dealing with artifacts + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +/* + apply() + apply an artifact +*/ + +void +apply(void) +{ + struct linked_list *item; + struct object *obj; + int which; + int chance; + + if ((item = get_item("activate", ARTIFACT)) == NULL) + return; + + obj = OBJPTR(item); + which = obj->o_which; + + if (!(obj->ar_flags & ISACTIVE)) + { + chance = rnd(100) - 10 * rnd(luck); + debug("Rolled %d.", chance); + if (chance < 5) + do_major(); + else if (chance < 50) + do_minor(obj); + else + obj->ar_flags |= ISACTIVE; + } + + if (obj->ar_flags & ISACTIVE) + { + switch (which) + { + case TR_PURSE: do_bag(obj); + break; + case TR_PHIAL: do_phial(); + break; + case TR_AMULET: do_amulet(); + break; + case TR_PALANTIR: do_palantir(); + break; + case TR_CROWN: do_crown(); + break; + case TR_SCEPTRE: do_sceptre(); + break; + case TR_SILMARIL: do_silmaril(); + break; + case TR_WAND: do_wand(); + break; + default: nothing_message(ISCURSED); + return; + } + } + + if (rnd(pstats.s_lvl) < 6) + do_minor(obj); + + turn_on(player, POWEREAT); +} + +/* + possessed(int artifact) + was the hero carrying a particular artifact +*/ + +int +possessed(int artifact) +{ + return (picked_artifact >> artifact) & 1; +} + +/* + is_carrying(int artifact) + is the hero carrying a particular artifact +*/ + +int +is_carrying(int artifact) +{ + return (has_artifact >> artifact) & 1; +} + +/* + make_artifact() + is it time to make a new artifact? +*/ + +int +make_artifact(void) +{ + int i; + + mpos = 0; + + debug("Artifact possession and picked flags : %x %x.", + has_artifact, picked_artifact); + + for(i = 0; i < maxartifact; i++) + { + if (!is_carrying(i) && arts[i].ar_level <= level) + return TRUE; + } + + return FALSE; +} + +/* + new_artifact(int which, struct object *cur) + make a specified artifact +*/ + +struct object * +new_artifact(int which, struct object *cur) +{ + if (which >= maxartifact) + { + debug("Bad artifact %d. Random one created.", which); + which = rnd(maxartifact); + } + + if (which < 0) + { + for (which = 0; which < maxartifact; which++) + if (!is_carrying(which) && arts[which].ar_level <= level) + break; + } + + debug("Artifact number: %d.", which); + + cur->o_hplus = cur->o_dplus = 0; + cur->o_damage = cur->o_hurldmg = "0d0"; + cur->o_ac = 11; + cur->o_mark[0] = '\0'; + cur->o_type = ARTIFACT; + cur->o_which = which; + cur->o_weight = arts[which].ar_weight; + cur->o_flags = 0; + cur->o_group = 0; + cur->o_count = 1; + cur->o_bag = NULL; + cur->ar_flags = 0; + + return(cur); +} + +/* + do_minor(struct object *tr) + side effects and minor malevolent effects of artifacts +*/ + +void +do_minor(struct object *tr) +{ + int which; + long loss; + + which = rnd(110); + + debug("Rolled %d.", which); + + switch (which) + { + case 0: + seemsg("You develop some acne on your face."); + break; + + case 1: + if (on(player, CANSCENT)) + { + msg("A sudden whiff of BO causes you to faint."); + no_command = STONETIME; + } + else if (off(player, ISUNSMELL)) + msg("You begin to smell funny."); + break; + + case 2: + seemsg("A wart grows on the end of your nose."); + break; + + case 3: + hearmsg("Your hear strange noises in the distance."); + break; + + case 4: + hearmsg("You hear shuffling in the distance."); + break; + + case 5: + hearmsg("You hear clanking in the distance."); + break; + + case 6: + hearmsg("You hear water dripping onto the floor."); + break; + + case 7: + hearmsg("The dungeon goes strangely silent."); + break; + + case 8: + msg("You suddenly feel very warm."); + break; + + case 9: + msg("You feel very hot."); + break; + + case 10: + msg("A blast of heat hits you."); + break; + + case 11: + { + struct room *rp; + + if (off(player, ISBLIND)) + msg("A pillar of flame leaps up beside you."); + else + msg("You feel something very hot nearby."); + + if (ntraps + 1 < 2 * MAXTRAPS && + fallpos(hero, &traps[ntraps].tr_pos)) + { + mvaddch(traps[ntraps].tr_pos.y, traps[ntraps].tr_pos.x, + FIRETRAP); + traps[ntraps].tr_type = FIRETRAP; + traps[ntraps].tr_flags = ISFOUND; + traps[ntraps].tr_show = FIRETRAP; + ntraps++; + + if ((rp = roomin(hero)) != NULL) + { + rp->r_flags &= ~ISDARK; + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + } + } + break; + + case 12: + msg("You feel a blast of hot air."); + break; + + case 13: + msg("You feel very cold."); + break; + + case 14: + msg("You break out in a cold sweat."); + break; + + case 15: + if (off(player, ISBLIND) && cur_armor == NULL) + msg("You are covered with frost."); + else if (off(player, ISBLIND)) + msg("Your armor is covered with frost."); + else if (cur_armor == NULL) + msg("Your body feels very cold and you begin to shiver."); + else + msg("Your armor feels very cold. You hear cracking ice."); + break; + + case 16: + msg("A cold wind whistles through the dungeon."); + break; + + case 17: + { + int change; + + change = 18 - pstats.s_str; + chg_str(change, TRUE, FALSE); + chg_dext(-change, TRUE, FALSE); + + if (change > 0) + msg("You feel stronger and clumsier now."); + else if (change < 0) + msg("You feel weaker and more dextrous now."); + else + nothing_message(ISCURSED); + } + break; + + case 18: + msg("You begin to itch all over."); + break; + + case 19: + msg("You begin to feel hot and itchy."); + break; + + case 20: + msg("You feel a burning itch."); + chg_dext(-1, FALSE, TRUE); + + if (off(player, HASITCH)) + { + turn_on(player, HASITCH); + light_fuse(FUSE_UNITCH, 0, roll(4,6), AFTER); + } + else + lengthen_fuse(FUSE_UNITCH, roll(4,6)); + break; + + case 21: + if (off(player, ISBLIND)) + msg("Your skin begins to flake and peel."); + else + msg("You feel an urge to scratch an itch."); + break; + + + case 22: + seemsg("Your hair begins to turn grey."); + break; + + case 23: + seemsg("Your hair begins to turn white."); + break; + + case 24: + seemsg("Some of your hair instantly turns white."); + break; + + case 25: + seemsg("You are covered with long white hair."); + break; + + case 26: + seemsg("You are covered with long red hair."); + break; + + case 27: + msg("You grow a beard."); + break; + + case 28: + msg("Your hair falls out."); + break; + + case 29: + msg("You feel a burning down below."); + break; + + case 30: + msg("Your toes fall off."); + break; + + case 31: + msg("You grow some extra toes."); + break; + + case 32: + msg("You grow some extra fingers."); + break; + + case 33: + msg("You grow an extra thumb."); + break; + + case 34: + msg("Your nose falls off."); + break; + + case 35: + msg("Your nose gets bigger."); + break; + + case 36: + msg("Your nose shrinks."); + break; + + case 37: + msg("An eye grows on your forehead."); + break; + + case 38: + seemsg("You see beady eyes watching from a distance."); + break; + + case 39: + msg("The dungeon rumbles for a moment."); + break; + + case 40: + seemsg("A flower grows on the floor next to you."); + break; + + case 41: + msg("You are stunned by a psionic blast."); + + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(40) + (HUHDURATION * 3)); + else + { + light_fuse(FUSE_UNCONFUSE,0,rnd(40)+(HUHDURATION * 3), AFTER); + turn_on(player, ISHUH); + } + break; + + case 42: + msg("You are confused by thousands of voices in your head."); + + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(10) + (HUHDURATION * 2)); + else + { + light_fuse(FUSE_UNCONFUSE,0,rnd(10)+(HUHDURATION * 2), AFTER); + turn_on(player, ISHUH); + } + break; + + case 43: + hearmsg("You hear voices in the distance."); + break; + + case 44: + msg("You feel a strange pull."); + teleport(); + + if (off(player, ISCLEAR)) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + { + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + turn_on(player, ISHUH); + } + } + break; + + case 45: + msg("You feel less healthy now."); + pstats.s_const = max(pstats.s_const - 1, 3); + max_stats.s_const = max(max_stats.s_const - 1, 3); + break; + + case 46: + msg("You feel weaker now."); + chg_str(-1, TRUE, FALSE); + break; + + case 47: + msg("You feel less wise now."); + pstats.s_wisdom = max(pstats.s_wisdom - 1, 3); + max_stats.s_wisdom = max(max_stats.s_wisdom - 1, 3); + break; + + case 48: + msg("You feel less dextrous now."); + chg_dext(-1, TRUE, FALSE); + break; + + case 49: + msg("You feel less intelligent now."); + pstats.s_intel = max(pstats.s_intel - 1, 3); + max_stats.s_intel = max(max_stats.s_intel - 1, 3); + break; + + case 50: + msg("A trap door opens underneath your feet."); + mpos = 0; + level++; + new_level(NORMLEV,0); + + if (rnd(4) < 2) + { + addmsg("You are damaged by the fall"); + + if ((pstats.s_hpt -= roll(1, 6)) <= 0) + { + addmsg("! The fall killed you."); + endmsg(); + death(D_FALL); + return; + } + } + + addmsg("!"); + endmsg(); + + if (off(player, ISCLEAR) && rnd(4) < 3) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + + break; + + case 51: + msg("A maze entrance opens underneath your feet."); + mpos = 0; + level++; + new_level(MAZELEV,0); + + if (rnd(4) < 2) + { + addmsg("You are damaged by the fall"); + + if ((pstats.s_hpt -= roll(1, 6)) <= 0) + { + addmsg("! The fall killed you."); + endmsg(); + death(D_FALL); + return; + } + } + addmsg("!"); + endmsg(); + + if (off(player, ISCLEAR) && rnd(4) < 3) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE,0, rnd(8) + HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + + break; + + case 52: + hearmsg("You hear a wailing sound in the distance."); + aggravate(); + break; + + case 53: + read_scroll(&player, S_HOLD, ISCURSED); + break; + + case 54: + msg("You can't move."); + no_command = 3 * HOLDTIME; + break; + + case 55: + hearmsg("You hear a buzzing sound."); + aggravate(); + break; + + case 56: + msg("Your limbs stiffen."); + no_command = 3 * STONETIME; + break; + + case 57: + msg("You feel a rock in your shoe hurting your foot."); + turn_on(player, STUMBLER); + break; + + case 58: + msg("You get a hollow feeling in your stomach."); + food_left -= 500; + break; + + case 59: + msg("Your purse feels lighter."); + + loss = 50L + ulrnd(purse / 2L); + purse = (purse > loss) ? purse - loss : 0L; + break; + + case 60: + msg("A pixie appears and grabs gold from your purse."); + + loss = 50L + rnd(50); + purse = (purse > loss) ? purse - loss : 0L; + break; + + case 61: + msg("You feel a tingling sensation all over."); + pstats.s_hpt -= ulrnd(pstats.s_hpt / 3L); + break; + + case 62: + msg("You feel a pull downwards."); + break; + + case 63: + msg("You feel a strange pull downwards."); + break; + + case 64: + msg("You feel a peculiar pull downwards."); + break; + + case 65: + msg("You have a strange urge to go down."); + break; + + case 66: + msg("You feel a pull upwards."); + break; + + case 67: + msg("You feel a strange pull upwards."); + break; + + case 68: + msg("You have a strange feeling for a moment."); + break; + + case 69: + msg("You float in the air for a moment."); + break; + + case 70: + msg("You feel very heavy for a moment."); + break; + + case 71: + msg("You feel a strange sense of loss."); + break; + + case 72: + msg("You feel the earth spinning underneath your feet."); + break; + + case 73: + msg("You feel in touch with a Universal Oneness."); + break; + + case 74: + hearmsg("You hear voices in the distance."); + break; + + case 75: + msg("A strange feeling of power comes over you."); + break; + + case 76: + msg("You feel a strange sense of unease."); + break; + + case 77: + msg("You feel Lady Luck is looking the other way."); + luck++; + break; + + case 78: + msg("You feel your pack vibrate for a moment."); + break; + + case 79: + msg("You feel someone is watching you."); + break; + + case 80: + msg("You feel your hair standing on end."); + break; + + case 81: + msg("Wait! The walls are moving!"); + new_level(NORMLEV,0); + break; + + case 82: + msg("Wait! Walls are appearing out of nowhere!"); + new_level(MAZELEV,0); + break; + + case 83: + blue_light(ISCURSED); + break; + + case 84: + msg("Your mind goes blank for a moment."); + wclear(cw); + light(&hero); + status(TRUE); + break; + + case 85: + if (on(player, ISDEAF)) + { + msg("You feel your ears burn for a moment."); + lengthen_fuse(FUSE_HEAR, 2 * PHASEDURATION); + } + else + { + msg("You are suddenly surrounded by silence."); + turn_on(player, ISDEAF); + light_fuse(FUSE_HEAR, 0, 2 * PHASEDURATION, AFTER); + } + break; + + case 86: + { + apply_to_bag(pack, 0, NULL, baf_curse, NULL); + + if (off(player, ISUNSMELL)) + msg("You smell a faint trace of burning sulfur."); + } + break; + + case 87: + msg("You have contracted a parasitic infestation."); + infest_dam++; + turn_on(player, HASINFEST); + break; + + case 88: + msg("You suddenly feel a chill run up and down your spine."); + turn_on(player, ISFLEE); + player.t_ischasing = FALSE; + player.t_chasee = &player; + break; + + case 89: + if (cur_weapon != NULL) + msg("You feel your %s get very hot.", + inv_name(cur_weapon, LOWERCASE)); + break; + + case 90: + if (cur_weapon != NULL) + msg("Your %s glows white for an instant.", + inv_name(cur_weapon, LOWERCASE)); + break; + + case 91: + if (cur_armor != NULL) + msg("Your %s gets very hot.", inv_name(cur_armor, LOWERCASE)); + break; + + case 92: + if (cur_weapon != NULL) + msg("Your %s suddenly feels very cold.", + inv_name(cur_weapon, LOWERCASE)); + break; + + case 93: + if (cur_armor != NULL) + msg("Your armor is covered by an oily film."); + break; + + case 94: + read_scroll(&player, S_CREATE, ISNORMAL); + break; + + case 95: + lower_level(D_POTION); + break; + + case 96: + { + int x, y; + + for (x = -1; x <= 1; x++) + { + for (y = -1; y <= 1; y++) + { + if (x == 0 && y == 0) + continue; + + delta.x = x; + delta.y = y; + + do_zap(&player, WS_POLYMORPH, rnd(2) + ? ISCURSED : ISNORMAL); + } + } + } + break; + + case 97: + { + int x, y; + + for (x = -1; x <= 1; x++) + { + for (y = -1; y <= 1; y++) + { + if (x == 0 && y == 0) + continue; + + delta.x = x; + delta.y = y; + + do_zap(&player, WS_INVIS, ISNORMAL); + } + } + } + break; + + default: + tr->ar_flags &= ~ISACTIVE; + hearmsg("You hear a click coming from %s.",inv_name(tr,LOWERCASE)); + break; + + } +} + +/* + do_major() + + major malevolent effects + + 0. read_scroll(S_SELFTELEPORT, ISCURSED) + 1. PERMBLIND for twice normal duration + 2. new_level(THRONE); + 3. turn_on(player, SUPEREAT); + 4. lengthen(noslow, 20 + rnd(20)); + 5. lower_level(D_POTION) * roll(1,4) + 6. change stats + 7. FIRETRAP + 8. armor crumbles + 9. weapon crumbles + 10. weapon crumbles + 11. curse weapon +*/ + +void +do_major(void) +{ + int which; + + which = rnd(12); + + debug("Rolled %d.", which); + + switch (which) + { + case 0: + read_scroll(&player, S_SELFTELEP, ISCURSED); + break; + + case 1: + quaff(&player, P_TRUESEE, ISCURSED); + quaff(&player, P_TRUESEE, ISCURSED); + break; + + case 2: + new_level(THRONE,0); + break; + + case 3: /* Turn off other body-affecting spells */ + + if (on(player, ISREGEN)) + { + extinguish_fuse(FUSE_UNREGEN); + turn_off(player, ISREGEN); + unregen(NULL); + } + + if (on(player, NOCOLD)) + { + extinguish_fuse(FUSE_UNCOLD); + turn_off(player, NOCOLD); + uncold(NULL); + } + + if (on(player, NOFIRE)) + { + extinguish_fuse(FUSE_UNHOT); + turn_off(player, NOFIRE); + unhot(NULL); + } + + if (on(player, SUPEREAT)) + { + lengthen_fuse(FUSE_UNSUPEREAT, 2 * PHASEDURATION); + msg("Your body temperature rises still further."); + } + else + { + msg("You feel very warm all over."); + light_fuse(FUSE_UNSUPEREAT, 0, 2 * PHASEDURATION, AFTER); + turn_on(player, SUPEREAT); + } + break; + + case 4: + msg("You feel yourself moving %sslower.", + on(player, ISSLOW) ? "even " : ""); + + if (on(player, ISSLOW)) + lengthen_fuse(FUSE_NOSLOW, PHASEDURATION); + else + { + turn_on(player, ISSLOW); + player.t_turn = TRUE; + light_fuse(FUSE_NOSLOW, 0, PHASEDURATION, AFTER); + } + break; + + case 5: + { + int i, n = roll(1, 4); + + for (i = 1; i < n; i++) + lower_level(D_POTION); + } + break; + + case 6: + if (rnd(2)) + add_intelligence(TRUE); + + if (rnd(2)) + chg_dext(-1, TRUE, FALSE); + + if (rnd(2)) + chg_str(-1, TRUE, FALSE); + + if (rnd(2)) + add_wisdom(TRUE); + + if (rnd(2)) + add_const(TRUE); + + break; + + case 7: + { + struct room *rp; + + if (ntraps + 1 >= MAXTRAPS) + { + msg("You feel a puff of hot air."); + return; + } + + for (; ntraps < 2 * MAXTRAPS; ntraps++) + { + if (!fallpos(hero, &traps[ntraps].tr_pos)) + break; + + mvaddch(traps[ntraps].tr_pos.y, traps[ntraps].tr_pos.x, + FIRETRAP); + traps[ntraps].tr_type = FIRETRAP; + traps[ntraps].tr_flags |= ISFOUND; + traps[ntraps].tr_show = FIRETRAP; + + if ((rp = roomin(hero)) != NULL) + rp->r_flags &= ~ISDARK; + } + } + break; + + case 8: + { + object *obj; + + if (cur_weapon == NULL) + { + msg("You feel your hands tingle a moment."); + pstats.s_dmg = "1d2"; + return; + } + + obj = apply_to_bag(pack, 0, NULL, bafcweapon, NULL); + + if (obj->o_flags & ISMETAL) + msg("Your %s melts and disappears.", + inv_name(obj,LOWERCASE)); + else + msg("Your %s crumbles in your hands.", + inv_name(obj, LOWERCASE)); + + obj->o_flags &= ~ISCURSED; + dropcheck(obj); + del_bag(pack, obj); + + } + break; + + case 9: + { + object *obj; + + if (cur_armor == NULL) + { + msg("Your body tingles a moment."); + return; + } + + obj = apply_to_bag(pack, 0, NULL, bafcarmor, NULL); + + msg("Your %s crumbles into small black powdery dust.", + inv_name(obj, LOWERCASE)); + + obj->o_flags &= ~ISCURSED; + dropcheck(obj); + del_bag(pack, obj); + } + break; + + default: + + if (cur_weapon == NULL) + { + seemsg("Your hand glows yellow for an instant."); + pstats.s_dmg = "1d3"; + return; + } + + seemsg("Your %s glows bright red for a moment.", + weaps[cur_weapon->o_which].w_name); + + if (cur_weapon->o_hplus > 0) + cur_weapon->o_hplus = -rnd(3); + else + cur_weapon->o_hplus -= rnd(3); + + if (cur_weapon->o_dplus > 0) + cur_weapon->o_dplus = -rnd(3); + else + cur_weapon->o_dplus -= rnd(3); + + cur_weapon->o_flags = ISCURSED | ISLOST; + cur_weapon->o_ac = 0; + + break; + } +} + +/* + do_phial() + handle powers of the Phial of Galadriel +*/ + +void +do_phial(void) +{ + int which; + + /* Prompt for action */ + + msg("How do you wish to apply the Phial of Galadriel (* for list)? "); + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + if (which < 0 || which > 1) + { + add_line("[a] total healing"); + add_line("[b] total monster confusion"); + end_line(); + msg(""); + msg("Which power do you wish to use? "); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which > 1) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + msg(""); + msg("Please enter one of the listed powers: "); + + which = (short) ((readchar() & 0177) - 'a'); + } + msg("Your attempt is successful."); + } + else + msg("Your attempt is successsful."); + + switch (which) + { + case 0: + pstats.s_hpt = max_stats.s_hpt += rnd(pstats.s_lvl) + 1; + pstats.s_power = max_stats.s_power += rnd(pstats.s_lvl) + 1; + break; + + case 1: + { + struct linked_list *mi; + struct thing *tp; + + for (mi = mlist; mi != NULL; mi = next(mi)) + { + tp = THINGPTR(mi); + + if (off(*tp, ISUNIQUE) || !save_throw(VS_MAGIC, tp)) + turn_on(*tp, ISHUH); + } + } + break; + + default: + msg("What a strange thing to do!!"); + break; + + } +} + +/* + do_palantir() + handle powers of the Palantir of Might +*/ + +void +do_palantir(void) +{ + int which, limit; + + /* Prompt for action */ + + msg("How do you wish to apply the Palantir of Might? (* for list): "); + + limit = 3; + + if (is_carrying(TR_SCEPTRE)) + limit += 1; + + if (is_carrying(TR_CROWN)) + limit += 1; + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + if (which < 0 || which > limit) + { + msg(""); + add_line("[a] monster detection"); + add_line("[b] gold detection"); + add_line("[c] magic detection"); + add_line("[d] food detection"); + + if (limit >= 4) + add_line("[e] teleportation"); + + if (limit >= 5) + add_line("[f] clear thought"); + + end_line(); + + msg("Which power do you wish to use?"); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which > limit) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + msg("Please enter one of the listed powers: "); + which = (short) ((readchar() & 0177) - 'a'); + } + + msg("Your attempt is successful."); + } + else + msg("Your attempt is successful."); + + switch (which) + { + case 0: quaff(&player, P_MONSTDET, ISNORMAL); + break; + case 1: read_scroll(&player, S_GFIND, ISNORMAL); + break; + case 2: quaff(&player, P_TREASDET, ISNORMAL); + break; + case 3: read_scroll(&player, S_FOODDET, ISNORMAL); + break; + case 4: read_scroll(&player, S_SELFTELEP, ISNORMAL); + break; + case 5: quaff(&player, P_CLEAR, ISNORMAL); + break; + default: + msg("What a strange thing to do!!"); + break; + } +} + +/* + do_silmaril() + handle powers of the Silamril of Ea +*/ + +void +do_silmaril(void) +{ + int which; + + /* Prompt for action */ + msg("How do you wish to apply the Silamril of Ea (* for list)? "); + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + if (which < 0 || which > 2) + { + msg(""); + add_line("[a] magic mapping"); + add_line("[b] petrification"); + add_line("[c] stairwell downwards"); + end_line(); + + msg("Which power do you wish to use?"); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which > 2) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + msg(""); + msg("Please enter one of the listed powers: "); + which = (short) ((readchar() & 0177) - 'a'); + } + msg("Your attempt is successful."); + } + else + msg("Your attempt is successful."); + + switch (which) + { + case 0: read_scroll(&player, S_MAP, ISNORMAL); + break; + case 1: read_scroll(&player, S_PETRIFY, ISNORMAL); + break; + case 2: msg("A stairwell opens beneath your feet and you go down."); + level++; + new_level(NORMLEV,0); + break; + default:msg("What a strange thing to do!!"); + break; + } +} + +/* + do_amulet() + handle powers of the Amulet of Yendor +*/ + +void +do_amulet(void) +{ + int which, limit; + + /* Prompt for action */ + msg("How do you wish to apply the Amulet of Yendor (* for list)? "); + + limit = 0; + + if (is_carrying(TR_PURSE)) + limit += 1; + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + if (which < 0 || which > limit) + { + msg(""); + add_line("[a] level evaluation"); + + if (limit >= 1) + add_line("[b] invisibility"); + + end_line(); + msg("Which power do you wish to use?"); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which > limit) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + msg(""); + msg("Please enter one of the listed powers: "); + which = (short) ((readchar() & 0177) - 'a'); + } + + msg("Your attempt is successful."); + } + else + msg("Your attempt is successful."); + + switch (which) + { + case 0: level_eval(); + break; + case 1: quaff(&player, P_INVIS, ISNORMAL); + break; + default:msg("What a strange thing to do!!"); + break; + } +} + +/* + do_bag() + handle powers of the Magic Purse of Yendor as a bag of holding +*/ + +void +do_bag(struct object *obj) +{ + int which, limit; + + /* Prompt for action */ + msg("How do you wish to apply the Magic Purse of Yendor (* for list)? "); + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + limit = 2; + + if (is_carrying(TR_AMULET)) + limit += 1; + + if (which < 0 || which > limit) + { + msg(""); + add_line("[a] inventory"); + add_line("[b] add to bag"); + add_line("[c] remove from bag"); + + if (limit >= 3) + add_line("[d] see invisible"); + + end_line(); + + msg("Which power do you wish to use?"); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which > limit) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + msg(""); + msg("Please enter one of the listed powers: "); + which = (short) ((readchar() & 0177) - 'a'); + } + + msg("Your attempt is successful."); + } + else + msg("Your attempt is successful."); + + switch (which) + { + case 0: + inventory(obj->o_bag, 0); + break; + + case 1: + { + object *new_obj_p; /* what the user selected */ + + if ((new_obj_p = get_object(pack, "add", 0, NULL)) != NULL) + { + rem_pack(new_obj_p); /* free up pack slot */ + push_bag(&obj->o_bag, new_obj_p); + pack_report(new_obj_p, MESSAGE, "You just added "); + } + } + break; + + case 2: + { + object *obj_p; + linked_list *item_p; + + if ((obj_p=get_object(obj->o_bag,"remove",0,NULL)) != NULL) + { + item_p = make_item(obj_p); /* attach upper structure */ + + if (add_pack(item_p, MESSAGE) != FALSE) + pop_bag(&obj->o_bag, obj_p); + } + } + break; + + case 3: + quaff(&player, P_TRUESEE, ISBLESSED); + break; + + default: + msg("What a strange thing to do!!"); + } +} + +/* + do_sceptre() + handle powers of the Sceptre of Might +*/ + +void +do_sceptre(void) +{ + int which, limit; + + /* Prompt for action */ + msg("How do you wish to apply the Sceptre of Might (* for list)? "); + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + limit = 5; + + if (is_carrying(TR_CROWN)) + limit += 1; + + if (is_carrying(TR_PALANTIR)) + limit += 1; + + if (which < 0 || which > limit) + { + msg(""); + add_line("[a] cancellation"); + add_line("[b] polymorph monster"); + add_line("[c] slow monster"); + add_line("[d] teleport monster"); + add_line("[e] monster confusion"); + add_line("[f] paralyze monster"); + + if (limit >= 6) + add_line("[g] drain life"); + + if (limit >= 7) + add_line("[h] smell monster"); + + end_line(); + + msg("Which power do you wish to use?"); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which > limit) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + msg(""); + msg("Please enter one of the listed powers: "); + which = (short) ((readchar() & 0177) - 'a'); + } + + msg("Your attempt is successful."); + } + else + msg("Your attempt is successful."); + + if (rnd(pstats.s_lvl) < 7) + { + msg("Your finger slips."); + which = rnd(6); + if (wizard) + { + msg("What wand? (%d)", which); + + if (get_string(prbuf, cw) == NORM) + { + which = atoi(prbuf); + if (which < 0 || which > 5) + { + msg("Invalid selection."); + which = rnd(6); + msg("Rolled %d.", which); + } + } + } + } + + switch (which) + { + case 0: + if (get_dir()) + do_zap(&player, WS_CANCEL, ISBLESSED); + break; + + case 1: + if (get_dir()) + do_zap(&player, WS_POLYMORPH, ISBLESSED); + break; + + case 2: + if (get_dir()) + do_zap(&player, WS_SLOW_M, ISBLESSED); + break; + + case 3: + if (get_dir()) + do_zap(&player, WS_MONSTELEP, ISBLESSED); + break; + + case 4: + if (get_dir()) + do_zap(&player, WS_CONFMON, ISBLESSED); + break; + + case 5: + if (get_dir()) + do_zap(&player, WS_PARALYZE, ISBLESSED); + break; + + case 6: + if (get_dir()) + do_zap(&player, WS_DRAIN, ISBLESSED); + break; + + case 7: + quaff(&player, P_SMELL, ISBLESSED); + break; + + default: + msg("What a strange thing to do!!"); + break; + } +} + +/* + do_wand() + handle powers of the Wand of Yendor +*/ + +void +do_wand(void) +{ + int which, i; + + /* Prompt for action */ + msg("How do you wish to apply the Wand of Yendor (* for list)? "); + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + if (which < 0 || which >= maxsticks) + { + msg(""); + + for (i = 0; i < maxsticks; i++) + { + sprintf(prbuf, "[%c] %s", i + 'a', ws_magic[i].mi_name); + add_line(prbuf); + } + + end_line(); + + msg("Which power do you wish to use?"); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which >= maxsticks) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + msg(""); + msg("Please enter one of the listed powers: "); + which = (short) ((readchar() & 0177) - 'a'); + } + msg("Your attempt is successful."); + } + else + msg("Your attempt is successful."); + + if (rnd(pstats.s_lvl) < 12) + { + msg("Your finger slips."); + which = rnd(maxsticks); + + if (wizard) + { + msg("What wand? (%d)", which); + + if (get_string(prbuf, cw) == NORM) + { + which = atoi(prbuf); + + if (which < 0 || which >= maxsticks) + { + msg("Invalid selection."); + which = rnd(maxsticks); + msg("Rolled %d.", which); + } + } + } + } + + if (get_dir()) + do_zap(&player, which, ISBLESSED); +} + +/* + do_crown() + handle powers of the Crown of Might +*/ + +void +do_crown(void) +{ + int which, limit; + + /* Prompt for action */ + msg("How do you wish to apply the Crown of Might (* for list)? "); + + which = (short) ((readchar() & 0177) - 'a'); + + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + + limit = 9; + + if (is_carrying(TR_PALANTIR)) + limit += 1; + + if (is_carrying(TR_SCEPTRE)) + limit += 1; + + if (which < 0 || which > limit) + { + msg(""); + add_line("[a] add strength"); + add_line("[b] add intelligence"); + add_line("[c] add wisdom"); + add_line("[d] add dexterity"); + add_line("[e] add constitution"); + add_line("[f] normal strength"); + add_line("[g] normal intelligence"); + add_line("[h] normal wisdom"); + add_line("[i] normal dexterity"); + add_line("[j] normal constitution"); + + if (limit >= 10) + add_line("[k] disguise"); + + if (limit >= 11) + add_line("[l] super heroism"); + + end_line(); + + msg("Which power do you wish to use?"); + + which = (short) ((readchar() & 0177) - 'a'); + + while (which < 0 || which > limit) + { + if (which == (short) ESCAPE - (short) 'a') + { + after = FALSE; + return; + } + msg(""); + msg("Please enter one of the listed powers: "); + which = (short) ((readchar() & 0177) - 'a'); + } + + msg("Your attempt is successful."); + } + else + msg("Your attempt is successful."); + + switch (which) + { + case 0: + if (off(player, POWERSTR)) + { + turn_on(player, POWERSTR); + chg_str(10, FALSE, FALSE); + msg("You feel much stronger now."); + } + else + nothing_message(ISCURSED); + break; + + case 1: + if (off(player, POWERINTEL)) + { + pstats.s_intel += 10; + turn_on(player, POWERINTEL); + msg("You feel much more intelligent now."); + } + else + nothing_message(ISCURSED); + break; + + case 2: + if (off(player, POWERWISDOM)) + { + pstats.s_wisdom += 10; + turn_on(player, POWERWISDOM); + msg("Your feel much wiser know."); + } + else + nothing_message(ISCURSED); + break; + + case 3: + if (off(player, POWERDEXT)) + { + turn_on(player, POWERDEXT); + chg_dext(10, FALSE, FALSE); + msg("You feel much more dextrous now."); + } + else + nothing_message(ISCURSED); + break; + + case 4: + if (off(player, POWERCONST)) + { + pstats.s_const += 10; + turn_on(player, POWERCONST); + msg("You feel much healthier now."); + } + else + nothing_message(ISCURSED); + break; + + case 5: + if (on(player, POWERSTR)) + { + turn_off(player, POWERSTR); + chg_str(-10, FALSE, FALSE); + msg("Your muscles bulge less now."); + } + else + nothing_message(ISCURSED); + break; + + case 6: + if (on(player, POWERINTEL)) + { + pstats.s_intel = max(pstats.s_intel - 10, + 3 + ring_value(R_ADDINTEL)); + turn_off(player, POWERINTEL); + msg("You feel less intelligent now."); + } + else + nothing_message(ISCURSED); + break; + + case 7: + if (on(player, POWERWISDOM)) + { + pstats.s_wisdom = max(pstats.s_wisdom - 10, + 3 + ring_value(R_ADDWISDOM)); + turn_off(player, POWERWISDOM); + msg("You feel less wise now."); + } + else + nothing_message(ISCURSED); + break; + + case 8: + if (on(player, POWERDEXT)) + { + turn_off(player, POWERDEXT); + chg_dext(-10, FALSE, FALSE); + msg("You feel less dextrous now."); + } + else + nothing_message(ISCURSED); + break; + + case 9: + if (on(player, POWERCONST)) + { + pstats.s_const -= 10; + turn_off(player, POWERCONST); + msg("You feel less healthy now."); + } + else + nothing_message(ISCURSED); + break; + + case 10: quaff(&player, P_DISGUISE, ISNORMAL); + break; + + case 11: quaff(&player, P_SHERO, ISNORMAL); + break; + + default: + msg("What a strange thing to do!!"); + break; + + } +} + +/* + level_eval() + have amulet evaluate danger on this level +*/ + +void +level_eval(void) +{ + int cnt = 0; + long max_nasty = 0; + struct linked_list *item; + struct thing *tp; + char *colour, *temp; + + for (item = mlist; item != NULL; item = next(item)) + { + tp = THINGPTR(item); + cnt++; + max_nasty = max(max_nasty,(10L-tp->t_stats.s_arm) * tp->t_stats.s_hpt); + } + + if (cnt < 3) + colour = "black"; + else if (cnt < 6) + colour = "red"; + else if (cnt < 9) + colour = "orange"; + else if (cnt < 12) + colour = "yellow"; + else if (cnt < 15) + colour = "green"; + else if (cnt < 18) + colour = "blue"; + else if (cnt < 25) + colour = "violet"; + else + colour = "pink with purple polka dots"; + + if (max_nasty < 10) + temp = "feels cold and lifeless"; + else if (max_nasty < 30) + temp = "feels cool"; + else if (max_nasty < 200) + temp = "feels warm and soft"; + else if (max_nasty < 1000) + temp = "feels warm and slippery"; + else if (max_nasty < 5000) + temp = "feels hot and dry"; + else if (max_nasty < 10000) + temp = "feels too hot to hold"; + else if (max_nasty < 20000) + temp = "burns your hand"; + else + temp = "jumps up and down shrieking 'DANGER! DANGER'"; + + msg("The amulet glows %s and %s.", colour, temp); + + return; +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/bag.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/bag.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,448 @@ +/* + bag.c - functions for dealing with bags + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/* + * new bag functions + * + * This is a simple version of bag.c that uses linked lists to perform the bag + * functions. The bag is just a linked list of objects (struct object) to be + * specific, but most of that is supposed to be hidden from the user, who + * should access the bag only through the functions presented here. + */ + +#include +#include "rogue.h" + +/* + * apply_to_bag + * + * This is the general bag manipulation routine. The bag is subjected to + * selection criteria and those objects which pass are processed by an action + * routine. The two criteria are type and filter function. The filter + * function returns TRUE if the object passes and FALSE otherwise. The filter + * function is passed the object and the user-supplied argument. This gives + * the user plenty of flexibility in determining which items will be + * processed. The action routine is passed the object, the id, and the + * user-supplied argument given to apply_to_bag. Specifying NULL for either + * the type or filter function means that criterion always selects. A NULL + * action routine means no processing is done and the first object which + * passes the filter is returned to the user. The action routine returns TRUE + * if processing should continue or FALSE if the current item should be + * returned to the caller. + * + * Returns NULL if the bag is empty or if nothing qualified. + * + * linked_list *bag_p; // linked list of objects + * int type; // what is its type (ARMOR, ...) + * int (*bff_p)(); // bag filter function + * int (*baf_p)(); // bag action routine + * long user_arg; // user argument for filter, action + * + */ + +struct object * +apply_to_bag(struct linked_list *bag_p, + int type, + int (*bff_p)(struct object *obj, bag_arg *user_arg), + int (*baf_p)(struct object *obj, bag_arg *user_arg, int id), + void *user_arg) +{ + struct object *bag_obj_p = NULL; /* qualifying object */ + struct object *cur_obj_p; /* current object */ + bag_arg arg; + + arg.varg = user_arg; + + if (bag_p == NULL) + return (NULL); + + for (; bag_p != NULL; bag_p = next(bag_p)) + { + cur_obj_p = OBJPTR(bag_p); + + if (type != 0 && type != cur_obj_p->o_type) + continue; + + if (bff_p != NULL && !(*bff_p)(cur_obj_p, &arg)) + continue; + + /* + * At this point, we have an object which qualifies for + * processing + */ + + bag_obj_p = cur_obj_p; /* in case the user wants it */ + + if (baf_p != NULL && (*baf_p)(cur_obj_p, &arg, identifier(bag_obj_p))) + continue; + + /* + * We have an object which qualifies, quit now! + */ + + break; + } + + if (bag_p == NULL) + return (NULL); + + return (bag_obj_p); +} + +/* + count_bag() + + Counts up all bag items which meet the selection criteria +*/ + +int +count_bag(linked_list *bag_p, + int type, + int (*bff_p)(struct object *obj, bag_arg *junk)) +{ + int cnt = 0; + apply_to_bag(bag_p, type, bff_p, baf_increment, &cnt); + + return(cnt); +} + +/* + del_bag() + + Removes an object from a bag and throws it away. +*/ + +void +del_bag(linked_list *bag_p, object *obj_p) +{ + pop_bag(&bag_p, obj_p); /* get the thing from the bag */ + ur_free(obj_p); /* release the memory */ +} + +/* + pop_bag() + + Removes an item from a bag and returns it to the user. If the item is + not in the bag, return NULL. +*/ + +struct object * +pop_bag(linked_list **bag_pp, object *obj_p) +{ + linked_list *item_p; + + for (item_p = *bag_pp; item_p != NULL && OBJPTR(item_p) != obj_p; + item_p = next(item_p)); + + if (item_p == NULL) + return (NULL); + + _detach(bag_pp, item_p); + + return (obj_p); +} + +/* + push_bag() + + stuff another item into the bag +*/ + +void +push_bag(linked_list **bag_pp, object *obj_p) +{ + struct linked_list *item_p = NULL; + struct linked_list *new_p = NULL; + struct linked_list *best_p = NULL; + + new_p = new_list(); + new_p->data.obj = obj_p; /* attach our object */ + identifier(obj_p) = get_ident(obj_p); /* tag this object for */ + /* inventory */ + /* + * Find a place in the bag - try to match the type, then sort by + * identifier + */ + + for (item_p = *bag_pp; item_p != NULL; item_p = next(item_p)) + { + if ((OBJPTR(item_p))->o_type == obj_p->o_type) + { + if (best_p == NULL) + best_p = item_p; + else if (identifier((OBJPTR(item_p))) > + identifier((OBJPTR(best_p))) && + identifier((OBJPTR(item_p))) < + identifier(obj_p)) + best_p = item_p; + } + } + + _attach_after(bag_pp, best_p, new_p); /* stuff it in the list */ + + return; +} + +/* + scan_bag() + + Gets the object from the bag that matches the type and id. The object + is not removed from the bag. +*/ + +struct object * +scan_bag(linked_list *bag_p, int type, int id) +{ + object *obj_p = NULL; + + for (; bag_p != NULL; bag_p = next(bag_p)) + { + obj_p = OBJPTR(bag_p); + + if (obj_p->o_type == type && identifier(obj_p) == id) + break; + } + + if (bag_p == NULL) + return(NULL); + + return(obj_p); +} + +/* + baf_decrement_test() + + Assumes the argument is a pointer to int and it just decrements it. + Returns TRUE, except when the count goes to zero. +*/ + +int +baf_decrement_test(struct object *obj_p, bag_arg *count_p, int id) +{ + NOOP(obj_p); + NOOP(id); + + if (*count_p->iarg > 0) + return(TRUE); + + return(FALSE); +} + +/* + baf_identify() + + Bag action function to identify an object. This is needed to conform + to bag action routine calling conventions and to put the linked list + structure on top of the object before calling whatis() +*/ + +int +baf_identify(struct object *obj_p, bag_arg *junk, int id) +{ + linked_list l; + linked_list *lp = &l; + + NOOP(junk); + NOOP(id); + + lp->data.obj = obj_p; /* stuff object in the right place */ + whatis(lp); + + return(TRUE); +} + +/* + baf_increment() + + Assumes the argument is a pointer to int and it just increments it and + returns TRUE +*/ + +int +baf_increment(object *obj_p, bag_arg *count_p, int id) +{ + NOOP(obj_p); + NOOP(id); + + (*count_p->iarg)++; + + return(TRUE); +} + +/* + baf_print_item() + Bag action function to print a single item, inventory style. +*/ + +int +baf_print_item(struct object *obj_p, bag_arg *type, int id) +{ + char inv_temp[3 * LINELEN]; /* plenty of space for paranoid programmers */ + + if (*type->iarg == 0) + sprintf(inv_temp, "%c%c) %s", obj_p->o_type, + print_letters[id], inv_name(obj_p, LOWERCASE), FALSE); + else + sprintf(inv_temp, "%c) %s", print_letters[id], + inv_name(obj_p, LOWERCASE), FALSE); + + add_line(inv_temp); + return(TRUE); +} + +/* + bff_group() + This bag filter function checks to see if two items can be combined by + adjusting the count. Grouped items can be combined if the group numbers + match. The only other item that is allowed to have a count is food, and + there an exact match is required. +*/ + +int +bff_group(struct object *obj_p, bag_arg *arg) +{ + struct object *new_obj_p = arg->obj; + + if (new_obj_p->o_group > 0 && new_obj_p->o_group == obj_p->o_group) + return(TRUE); + + if (new_obj_p->o_type == FOOD && + obj_p->o_type == new_obj_p->o_type && + obj_p->o_which == new_obj_p->o_which) + return(TRUE); + + return(FALSE); +} + +/* + bff_callable + Figures out which items can be callable: current rules are: + potions, scrolls, staffs, and rings. +*/ + +int +bff_callable(struct object *obj_p, bag_arg *junk) +{ + NOOP(junk); + + if (obj_p->o_type == POTION || obj_p->o_type == RING || + obj_p->o_type == STICK || obj_p->o_type == SCROLL) + return(TRUE); + + return(FALSE); +} + +/* + bff_markable() + Selects which items can be marked. Current rules exclude only gold. +*/ + +int +bff_markable(struct object *obj_p, bag_arg *junk) +{ + NOOP(junk); + + if (obj_p->o_type == GOLD) + return(FALSE); + + return(TRUE); +} + +/* + bffron() + returns TRUE if hero is wearing this ring +*/ + +int +bffron(object *obj_p, bag_arg *junk) +{ + NOOP(junk); + + return(cur_ring[LEFT_1] == obj_p || cur_ring[LEFT_2] == obj_p || + cur_ring[LEFT_3] == obj_p || cur_ring[LEFT_4] == obj_p || + cur_ring[LEFT_5] || + cur_ring[RIGHT_1] == obj_p || cur_ring[RIGHT_2] == obj_p || + cur_ring[RIGHT_3] == obj_p || cur_ring[RIGHT_4] == obj_p || + cur_ring[RIGHT_5]); +} + +/* + bff_zappable() + Selects which items can be zapped. This includes both sticks and + magically enhanced weapons with lightning ability. +*/ + +int +bff_zappable(struct object *obj_p, bag_arg *junk) +{ + NOOP(junk); + + if (obj_p->o_type == STICK) + return(TRUE); + + if (obj_p->o_type == WEAPON && obj_p->o_flags & ISZAPPED) + return(TRUE); + + return (FALSE); +} + +/* + baf_curse() + Curse all non-artifact items in the player's pack +*/ + +int +baf_curse(struct object *obj_p, bag_arg *junk, int id) +{ + NOOP(junk); + NOOP(id); + + if (obj_p->o_type != ARTIFACT && rnd(8) == 0) + { + obj_p->o_flags |= ISCURSED; + obj_p->o_flags &= ~ISBLESSED; + } + + return(TRUE); +} + +/* + bafcweapon() + bag action routine to fetch the current weapon +*/ + +int +bafcweapon(struct object *obj_p, bag_arg *junk, int id) +{ + NOOP(junk); + NOOP(id); + + if (obj_p == cur_weapon) + return(FALSE); /* found what we wanted - stop and return it */ + + return(TRUE); +} + +/* + bafcarmor() + bag action routine to fetch the current armor +*/ + +int +bafcarmor(struct object *obj_p, bag_arg *junk, int id) +{ + NOOP(junk); + NOOP(id); + + if (obj_p == cur_armor) + return(FALSE); /* found what we wanted - stop and return it */ + + return(TRUE); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/chase.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/chase.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1326 @@ +/* + chase.c - Code for one creature to chase another + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +/* + do_chase() + Make one thing chase another. +*/ + +void +do_chase(struct thing *th, int flee) +{ + struct room *rer; /* room of chaser */ + struct room *ree; /* room of chasee */ + struct room *old_room; /* old room of monster */ + struct room *new_room; /* new room of monster */ + + int i, mindist = INT_MAX, maxdist = INT_MIN, dist = INT_MIN; + + int last_door = -1; /* Door we just came from */ + int stoprun = FALSE; /* TRUE means we are there */ + int rundoor; /* TRUE means run to a door */ + int hit_bad = FALSE; /* TRUE means hit bad monster */ + int mon_attack; /* TRUE means find a monster to hit */ + + char sch; + struct linked_list *item; + coord this; /* Temporary destination for chaser */ + + if (!th->t_ischasing) + return; + + /* Make sure the monster can move */ + + if (th->t_no_move != 0) + { + th->t_no_move--; + return; + } + + /* + * Bad monsters check for a good monster to hit, friendly monsters + * check for a bad monster to hit. + */ + + mon_attack = FALSE; + + if (good_monster(*th)) + { + hit_bad = TRUE; + mon_attack = TRUE; + } + else if (on(*th, ISMEAN)) + { + hit_bad = FALSE; + mon_attack = TRUE; + } + + if (mon_attack) + { + struct linked_list *mon_to_hit; + + mon_to_hit = f_mons_a(th->t_pos.y, th->t_pos.x, hit_bad); + + if (mon_to_hit) + { + mon_mon_attack(th, mon_to_hit, pick_weap(th), NOTHROWN); + return; + } + } + + /* no nearby monster to hit */ + + rer = roomin(th->t_pos); /* Find room of chaser */ + ree = roomin(th->t_chasee->t_pos); /* Find room of chasee */ + + /* + * We don't count doors as inside rooms for this routine + */ + + if (mvwinch(stdscr, th->t_pos.y, th->t_pos.x) == DOOR) + rer = NULL; + + this = th->t_chasee->t_pos; + + /* + * If we are not in a corridor and not a phasing monster, then if we + * are running after the player, we run to a door if he is not in the + * same room. If we are fleeing, we run to a door if he IS in the + * same room. Note: We don't bother with doors in mazes. Phasing + * monsters don't need to look for doors. There are no doors in mazes + * and throne rooms. + */ + + if (levtype != MAZELEV && levtype != THRONE && rer != NULL && off(*th, CANINWALL)) + { + if (flee) + rundoor = (rer == ree); + else + rundoor = (rer != ree); + } + else + rundoor = FALSE; + + if (rundoor) + { + coord d_exit; /* A particular door */ + int exity, exitx; /* Door's coordinates */ + + if (th->t_doorgoal != -1) + { /* Do we already have the goal? */ + this = rer->r_exit[th->t_doorgoal]; + dist = 0; /* Indicate that we have our door */ + } + else + for (i = 0; i < rer->r_nexits; i++) + { /* Loop through doors */ + d_exit = rer->r_exit[i]; + exity = d_exit.y; + exitx = d_exit.x; + + /* Avoid secret doors */ + if (mvwinch(stdscr, exity, exitx) == DOOR) + { + /* Were we just on this door? */ + if (ce(d_exit, th->t_oldpos)) + last_door = i; + else + { + dist = DISTANCE(th->t_chasee->t_pos, d_exit); + + /* + * If fleeing, we want to + * maximize distance from + * door to what we flee, and + * minimize distance from + * door to us. + */ + + if (flee) + dist-=DISTANCE(th->t_pos,d_exit); + + /* + * Maximize distance if + * fleeing, otherwise + * minimize it + */ + + if ((flee && (dist > maxdist)) || + (!flee && (dist < mindist))) + { + th->t_doorgoal = i; /* Use this door */ + this = d_exit; + mindist = maxdist = dist; + } + } + } + } + + /* Could we not find a door? */ + if (dist == INT_MIN) + { + /* If we were on a door, go ahead and use it */ + if (last_door != -1) + { + th->t_doorgoal = last_door; + this = th->t_oldpos; + dist = 0; /* Indicate that we found a door */ + } + } + + /* Indicate that we do not want to flee from the door */ + if (dist != INT_MIN) + flee = FALSE; + } + else + th->t_doorgoal = -1; /* Not going to any door */ + + /* + * this now contains what we want to run to this time so we run to + * it. If we hit it we either want to fight it or stop running + */ + + if (!chase(th, &this, flee)) + { + if (ce(th->t_nxtpos, hero)) + { + /* merchants try to sell something */ + + if (on(*th, CANSELL)) + { + sell(th); + return; + } + else if (off(*th, ISFRIENDLY) && off(*th, ISCHARMED) + && (off(*th, CANFLY) || (on(*th, CANFLY) && rnd(2)))) + attack(th, pick_weap(th), FALSE); + return; + } + else if (on(*th, NOMOVE)) + stoprun = TRUE; + } + + if (!curr_mons) + return; /* Did monster get itself killed? */ + + if (on(*th, NOMOVE)) + return; + + /* If we have a scavenger, it can pick something up */ + + if ((item = find_obj(th->t_nxtpos.y, th->t_nxtpos.x)) != NULL) + { + struct linked_list *node, *top = item; + struct object *obt; + + while(top) + { + /* grab all objects that qualify */ + + struct object *obj = OBJPTR(item); + + obt = OBJPTR(top); + node = obt->next_obj; + + if (on(*th, ISSCAVENGE) || + ((on(*th, CANWIELD) || on(*th, CANSHOOT)) && + (obj->o_type == WEAPON || obj->o_type == ARMOR)) || + (on(*th, CANCAST) && is_magic(obj))) + { + rem_obj(top, FALSE); + attach(th->t_pack, top); + } + + top = node; + } + + light(&hero); + } + + mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); + sch = CCHAR( mvwinch(cw, th->t_nxtpos.y, th->t_nxtpos.x) ); + + /* Get old and new room of monster */ + old_room = roomin(th->t_pos); + new_room = roomin(th->t_nxtpos); + + /* If the monster can illuminate rooms, check for a change */ + if (on(*th, HASFIRE)) + { + /* Is monster entering a room? */ + if (old_room != new_room && new_room != NULL) + { + new_room->r_flags |= HASFIRE; + new_room->r_fires++; + if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && new_room->r_fires==1) + light(&hero); + } + + /* Is monster leaving a room? */ + if (old_room != new_room && old_room != NULL) + { + if (--(old_room->r_fires) <= 0) + { + old_room->r_flags &= ~HASFIRE; + if (cansee(th->t_pos.y, th->t_pos.x)) + light(&th->t_pos); + } + } + } + + /* + * If monster is entering player's room and player can see it, stop + * the player's running. + */ + + if (new_room != old_room && new_room != NULL && + new_room == ree && cansee(th->t_nxtpos.y, th->t_nxtpos.x) && + (off(*th, ISINVIS) || (off(*th, ISSHADOW) || rnd(10) == 0) || + on(player, CANSEE)) && off(*th, CANSURPRISE)) + running = FALSE; + + if (rer != NULL && (rer->r_flags & ISDARK) && + !(rer->r_flags & HASFIRE) && sch == FLOOR && + DISTANCE(th->t_nxtpos, th->t_pos) < see_dist && + off(player, ISBLIND)) + th->t_oldch = ' '; + else + th->t_oldch = sch; + + if (cansee(th->t_nxtpos.y, th->t_nxtpos.x) && + off(*th, ISINWALL) && + ((off(*th, ISINVIS) && (off(*th, ISSHADOW) || rnd(100) < 10)) || + on(player, CANSEE)) && + off(*th, CANSURPRISE)) + mvwaddch(cw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type); + + mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); + mvwaddch(mw, th->t_nxtpos.y, th->t_nxtpos.x, th->t_type); + + /* Record monster's last position (if new one is different) */ + + if (!ce(th->t_nxtpos, th->t_pos)) + th->t_oldpos = th->t_pos; + + th->t_pos = th->t_nxtpos; /* Mark the monster's new position */ + + /* If the monster is on a trap, trap it */ + + sch = CCHAR(mvinch(th->t_nxtpos.y, th->t_nxtpos.x)); + + if (isatrap(sch)) + { + debug("Monster trapped by %c.", sch); + + if (cansee(th->t_nxtpos.y, th->t_nxtpos.x)) + th->t_oldch = sch; + + be_trapped(th, th->t_nxtpos); + } + + /* And stop running if need be */ + + if (stoprun && ce(th->t_pos, th->t_chasee->t_pos)) + { + th->t_ischasing = FALSE; + turn_off(*th, ISRUN); + } +} + +/* + chase_it() + Set a monster running after something or stop it from running (for + when it dies) +*/ + +void +chase_it(coord *runner, struct thing *th) +{ + struct linked_list *item; + struct thing *tp; + + /* If we couldn't find him, something is funny */ + + if ((item = find_mons(runner->y, runner->x)) == NULL) + { + debug("CHASER '%s'", unctrl(winat(runner->y, runner->x))); + return; + } + + tp = THINGPTR(item); + + /* Start the beastie running */ + + tp->t_ischasing = TRUE; + tp->t_chasee = th; + + turn_on(*tp, ISRUN); + turn_off(*tp, ISDISGUISE); + + return; +} + +/* + chase() + Find the spot for the chaser(er) to move closer to the chasee(ee). + Returns TRUE if we want to keep on chasing later, FALSE if we reach the + goal. +*/ + +int +chase(struct thing *tp, coord *ee, int flee) +{ + int x, y; + int dist, thisdist, monst_dist = INT_MAX; + struct linked_list *weapon; + coord *er = &tp->t_pos; + coord shoot; + coord *shootit_dir = NULL; + int ch; + char mch; + int next_player = FALSE; + + /* Take care of shooting directions */ + + if (on(*tp, CANBREATHE) || on(*tp, CANSHOOT) || on(*tp, CANCAST)) + { + if (good_monster(*tp)) + { + shootit_dir = find_shoot(tp, &shoot); /* find a mean monster */ + + if (wizard && shootit_dir) + msg("Found monster to attack towards (%d,%d).", + shootit_dir->x, shootit_dir->y); + } + else + shootit_dir = can_shoot(er, ee, &shoot); /* shoot hero */ + } + + /* + * If the thing is confused, let it move randomly. Some monsters are + * slightly confused all of the time. + */ + + if ((on(*tp, ISHUH) && rnd(10) < 8) || + ((on(*tp, ISINVIS) || on(*tp, ISSHADOW)) && rnd(100) < 20) || + (on(player, ISINVIS) && off(*tp, CANSEE))) + { /* Player is invisible */ + + /* get a valid random move */ + + tp->t_nxtpos = rndmove(tp); + + dist = DISTANCE(tp->t_nxtpos, *ee); + + if (on(*tp, ISHUH) && rnd(20) == 0) /* monster might lose confusion */ + turn_off(*tp, ISHUH); + + /* + * check to see if random move takes creature away from + * player if it does then turn off ISHELD + */ + + if (dist > 1 && on(*tp, DIDHOLD)) + { + turn_off(*tp, DIDHOLD); + turn_on(*tp, CANHOLD); + + if (--hold_count == 0) + turn_off(player, ISHELD); + } + } /* If we can breathe, we may do so */ + else if (on(*tp, CANBREATHE) && (shootit_dir) && (rnd(100) < 67) && + (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6)) && + (DISTANCE(*er, *ee) < BOLT_LENGTH * BOLT_LENGTH)) + { + int chance; + char *breath; + + /* Will it breathe at random */ + + if (on(*tp, CANBRANDOM)) + { + if (rnd(level / 20) == 0 && tp->t_index != nummonst + 1 + && !(good_monster(*tp))) + turn_off(*tp, CANBRANDOM); + + /* Select type of breath */ + + chance = rnd(100); + + if (chance < 11) + breath = "acid"; + else if (chance < 22) + breath = "flame"; + else if (chance < 33) + breath = "lightning bolt"; + else if (chance < 44) + breath = "chlorine gas"; + else if (chance < 55) + breath = "ice"; + else if (chance < 66) + breath = "nerve gas"; + else if (chance < 77) + breath = "sleeping gas"; + else if (chance < 88) + breath = "slow gas"; + else + breath = "fear gas"; + } /* Or can it breathe acid? */ + else if (on(*tp, CANBACID)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBACID); + + breath = "acid"; + } /* Or can it breathe fire */ + else if (on(*tp, CANBFIRE)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBFIRE); + + breath = "flame"; + } /* Or can it breathe electricity? */ + else if (on(*tp, CANBBOLT)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBBOLT); + + breath = "lightning bolt"; + } /* Or can it breathe gas? */ + else if (on(*tp, CANBGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBGAS); + + breath = "chlorine gas"; + } /* Or can it breathe ice? */ + else if (on(*tp, CANBICE)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBICE); + + breath = "ice"; + } + else if (on(*tp, CANBPGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBPGAS); + + breath = "nerve gas"; + } + else if (on(*tp, CANBSGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBSGAS); + + breath = "sleeping gas"; + } + else if (on(*tp, CANBSLGAS)) + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBSLGAS); + + breath = "slow gas"; + } + else + { + if (!good_monster(*tp) && rnd(level / 15) == 0) + turn_off(*tp, CANBFGAS); + + breath = "fear gas"; + } + + shoot_bolt(tp, *er, *shootit_dir, (tp == THINGPTR(fam_ptr)), + tp->t_index, breath, roll(tp->t_stats.s_lvl, 6)); + + tp->t_nxtpos = *er; + + dist = DISTANCE(tp->t_nxtpos, *ee); + + if (!curr_mons) + return (TRUE); + } + else if (shootit_dir && on(*tp, CANCAST) && + (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6))) + { + /* + If we can cast spells we might do so - even if adjacent fleeing + monsters are restricted to certain spells + */ + + incant(tp, *shootit_dir); + tp->t_nxtpos = *er; + dist = DISTANCE(tp->t_nxtpos, *ee); + } + else if (shootit_dir && on(*tp, CANSHOOT)) + { + weapon = get_hurl(tp); + + if (weapon && + (off(*tp, ISFLEE) || rnd(DISTANCE(*er, *ee)) > 2) && + (off(player, ISDISGUISE) || (rnd(tp->t_stats.s_lvl) > 6))) + { + /* + Should we shoot or throw something? fleeing monsters + may to shoot anyway if far enough away + */ + + missile(shootit_dir->y, shootit_dir->x, weapon, tp); + tp->t_nxtpos = *er; + dist = DISTANCE(tp->t_nxtpos, *ee); + } + } + else + { + /* + Otherwise, find the empty spot next to the chaser that is closest + to the chasee. + */ + int ey, ex; + struct room *rer, *ree; + int dist_to_old = INT_MIN; /* Dist from goal to old position */ + + /* Get rooms */ + rer = roomin(*er); + ree = roomin(*ee); + + /* + * This will eventually hold where we move to get closer. If + * we can't find an empty spot, we stay where we are. + */ + + dist = flee ? 0 : INT_MAX; + tp->t_nxtpos = *er; + + /* Are we at our goal already? */ + + if (!flee && ce(tp->t_nxtpos, *ee)) + return (FALSE); + + ey = er->y + 1; + ex = er->x + 1; + + for (x = er->x - 1; x <= ex; x++) + for (y = er->y - 1; y <= ey; y++) + { + coord tryp; /* test position */ + + /* Don't try off the screen */ + + if ((x < 0) || (x >= COLS) || (y < 1) || (y >= LINES - 2)) + continue; + + /* + * Don't try the player if not going after + * the player or he's disguised and monster is dumb + */ + + if (((off(*tp, ISFLEE) && !ce(hero, *ee)) || + (on(player, ISDISGUISE) && (rnd(tp->t_stats.s_lvl) < 6)) + || good_monster(*tp)) + && x == hero.x && y == hero.y) + continue; + + tryp.x = x; + tryp.y = y; + + /* + * Is there a monster on this spot closer to + * our goal? Don't look in our spot or where + * we were. + */ + + if (!ce(tryp, *er) && !ce(tryp, tp->t_oldpos) && + isalpha( (mch = CCHAR(mvwinch(mw, y, x))) ) ) + { + int test_dist; + + test_dist = DISTANCE(tryp,*ee); + if (test_dist <= 25 && /* Let's be fairly close */ + test_dist < monst_dist) + { + + /* Could we really move there? */ + + mvwaddch(mw, y, x, ' '); /* Temp blank monst */ + + if (diag_ok(er, &tryp, tp)) + monst_dist = test_dist; + + mvwaddch(mw, y, x, mch); /* Restore monster */ + } + } + + if (!diag_ok(er, &tryp, tp)) + continue; + + ch = mvwinch(cw, y, x); /* Screen character */ + + /* + * Stepping on player is NOT okay if we are + * fleeing + */ + + if (on(*tp, ISFLEE) && (ch == PLAYER)) + next_player = TRUE; + + if (step_ok(y, x, NOMONST, tp) && + (off(*tp, ISFLEE) || ch != PLAYER)) + { + + /* + * If it is a trap, an intelligent + * monster may not step on it (unless + * our hero is on top!) + */ + + if (isatrap(ch)) + { + if (!(ch == RUSTTRAP) && + !(ch == FIRETRAP && on(*tp, NOFIRE)) && + rnd(10) < tp->t_stats.s_intel && + (y != hero.y || x != hero.x)) + continue; + } + + /* + * OK -- this place counts + */ + + thisdist = DISTANCE(tryp, *ee); + + /* + * Adjust distance if we are being + * shot at to moving out of line of sight. + */ + + if (tp->t_wasshot && tp->t_stats.s_intel > 5 && + ce(hero, *ee)) + { + /* Move out of line of sight */ + if (straight_shot(tryp.y, tryp.x, ee->y, ee->x, NULL)) + { + if (flee) + thisdist -= SHOTPENALTY; + else + thisdist += SHOTPENALTY; + } + + /* + * But do we want to leave + * the room? + */ + else if (rer && rer == ree && ch == DOOR) + thisdist += DOORPENALTY; + } + + /* + * Don't move to the last position if + * we can help it + */ + + if (ce(tryp, tp->t_oldpos)) + dist_to_old = thisdist; + else if ((flee && (thisdist > dist)) || + (!flee && (thisdist < dist))) + { + tp->t_nxtpos = tryp; + dist = thisdist; + } + } + } + + /* + * If we are running from the player and he is in our way, go + * ahead and slug him. + */ + + if (next_player && DISTANCE(*er,*ee) < dist && + step_ok(tp->t_chasee->t_pos.y, tp->t_chasee->t_pos.x, NOMONST, tp)) + { + tp->t_nxtpos = tp->t_chasee->t_pos; /* Okay to hit player */ + return(FALSE); + } + + + /* + * If we can't get closer to the player (if that's our goal) + * because other monsters are in the way, just stay put + */ + + if (!flee && ce(hero, *ee) && monst_dist < INT_MAX && + DISTANCE(*er, hero) < dist) + tp->t_nxtpos = *er; + + /* Do we want to go back to the last position? */ + else if (dist_to_old != INT_MIN && /* It is possible to move back */ + ((flee && dist == 0) || /* No other possible moves */ + (!flee && dist == INT_MAX))) + { + /* Do we move back or just stay put (default)? */ + + dist = DISTANCE(*er,*ee); /* Current distance */ + + if (!flee || (flee && (dist_to_old > dist))) + tp->t_nxtpos = tp->t_oldpos; + } + } + + /* Make sure we have the real distance now */ + dist = DISTANCE(tp->t_nxtpos, *ee); + + /* Mark monsters in a wall */ + + switch(mvinch(tp->t_nxtpos.y, tp->t_nxtpos.x)) + { + case WALL: + case '-': + case '|': + turn_on(*tp, ISINWALL); + break; + default: + turn_off(*tp, ISINWALL); + } + + if (off(*tp, ISFLEE) && + !(!SAME_POS((tp->t_chasee->t_pos),hero) || off(player, ISINWALL) || on(*tp, CANINWALL))) + return(dist != 0); + else /* May actually hit here from a confused move */ + return(!ce(tp->t_nxtpos, hero)); +} + +/* + roomin(coord *cp) + + Find what room some coordinates are in. + NULL means they aren't in any room. +*/ + +struct room * +roomin(coord cp) +{ + struct room *rp; + int i; + + for (i = 0; i < MAXROOMS; i++) + { + rp = &rooms[i]; + + if ((cp.x <= (rp->r_pos.x + (rp->r_max.x - 1))) && + (cp.y <= (rp->r_pos.y + (rp->r_max.y - 1))) && + (cp.x >= rp->r_pos.x) && + (cp.y >= rp->r_pos.y)) + { + return(rp); + } + } + + return(NULL); +} + +/* + * find_mons: Find the monster from his corrdinates + */ + +struct linked_list * +find_mons(int y, int x) +{ + struct linked_list *item; + + for (item = mlist; item != NULL; item = next(item)) + { + struct thing *th = THINGPTR(item); + + if (th->t_pos.y == y && th->t_pos.x == x) + return item; + } + return NULL; +} + +/* + * Find an unfriendly monster around us to hit + */ + +struct linked_list * +f_mons_a(int y, int x, int hit_bad) +{ + int row, col; + struct linked_list *item; + struct thing *tp; + + for (row = x - 1; row <= x + 1; row++) + for (col = y - 1; col <= y + 1; col++) + if (row == x && col == y) + continue; + else if (col > 0 && row > 0 && + isalpha(mvwinch(mw, col, row)) && + ((item = find_mons(col, row)) != NULL)) + { + tp = THINGPTR(item); + if ((good_monster(*tp) && !hit_bad) || + (!good_monster(*tp) && hit_bad)) + return (item); + } + + return (NULL); +} + + +/* + diag_ok() + Check to see if the move is legal if it is diagonal +*/ + +int +diag_ok(coord *sp, coord *ep, struct thing *flgptr) +{ + if (ep->x == sp->x || ep->y == sp->y) + return TRUE; + + return (step_ok(ep->y, sp->x, MONSTOK, flgptr) && + step_ok(sp->y, ep->x, MONSTOK, flgptr)); +} + +/* + cansee() + returns true if the hero can see a certain coordinate. +*/ + +int +cansee(int y, int x) +{ + struct room *rer; + coord tp; + + if (on(player, ISBLIND)) + return FALSE; + + tp.y = y; + tp.x = x; + rer = roomin(tp); + + /* + * We can only see if the hero in the same room as the coordinate and + * the room is lit or if it is close. + */ + + return ((rer != NULL && + rer == roomin(hero) && + (!(rer->r_flags & ISDARK) || (rer->r_flags & HASFIRE)) && + (levtype != MAZELEV || /* Maze level needs direct line */ + maze_view(tp.y, tp.x))) || + DISTANCE(tp,hero) < see_dist); +} + +coord * +find_shoot(struct thing *tp, coord *dir) +{ + struct room *rtp; + int ulx, uly, xmx, ymx, xmon, ymon, tpx, tpy, row, col; + struct linked_list *mon; + struct thing *ick; + + rtp = roomin(tp->t_pos); /* Find room of chaser */ + + if (rtp == NULL) + return NULL; + + ulx = rtp->r_pos.x; + uly = rtp->r_pos.y; + xmx = rtp->r_max.x; + ymx = rtp->r_max.y; + + tpx = tp->t_pos.x; + tpy = tp->t_pos.y; + + for (col = ulx; col < (ulx + xmx); col++) + for (row = uly; row < (uly + ymx); row++) + { + if (row > 0 && col > 0 && isalpha(mvwinch(mw, row, col))) + { + mon = find_mons(row, col); + + if (mon) + { + ick = THINGPTR(mon); + xmon = ick->t_pos.x; + ymon = ick->t_pos.y; + + if (!(good_monster(*ick))) + { + if (straight_shot(tpy, tpx, ymon, xmon, dir)) + return(dir); + } + } + } + } + + return(NULL); +} + +/* + can_shoot() + determines if the monster (er) has a direct line of shot at the + player (ee). If so, it returns the direction in which to shoot. +*/ + +coord * +can_shoot(coord *er, coord *ee, coord *dir) +{ + int ery, erx, eey, eex; + + /* Make sure we are chasing the player */ + + if (!ce((*ee), hero)) + return(NULL); + + /* They must be in the same room */ + + if (roomin(*er) != roomin(hero)) + return(NULL); + + ery = er->y; + erx = er->x; + eey = ee->y; + eex = ee->x; + + /* Will shoot unless next to player, then 80% prob will fight */ + + if ((DISTANCE(*er,*ee) < 4) && (rnd(100) < 80)) + return(NULL); + + /* Do we have a straight shot? */ + + if (!straight_shot(ery, erx, eey, eex, dir)) + return(NULL); + else + return(dir); +} + +/* + straight_shot() + See if there is a straight line of sight between the two + given coordinates. If shooting is not NULL, it is a pointer to a + structure which should be filled with the direction to shoot (if + there is a line of sight). If shooting, monsters get in the way. + Otherwise, they do not. +*/ + +int +straight_shot(int ery, int erx, int eey, int eex, coord *dir) +{ + int dy, dx; /* Deltas */ + int ch; + + /* Does the monster have a straight shot at player */ + + if ((ery != eey) && (erx != eex) && + (abs(ery - eey) != abs(erx - eex))) + return (FALSE); + + /* Get the direction to shoot */ + + if (eey > ery) + dy = 1; + else if (eey == ery) + dy = 0; + else + dy = -1; + + if (eex > erx) + dx = 1; + else if (eex == erx) + dx = 0; + else + dx = -1; + + /* Make sure we have free area all the way to the player */ + + ery += dy; + erx += dx; + + while ((ery != eey) || (erx != eex)) + { + switch(ch = winat(ery, erx)) + { + case '|': + case '-': + case WALL: + case DOOR: + case SECRETDOOR: + return(FALSE); + default: + if (dir && isalpha(ch)) + return(FALSE); + } + + ery += dy; + erx += dx; + } + + if (dir) + { /* If we are shooting -- put in the directions */ + dir->y = dy; + dir->x = dx; + } + + return(TRUE); +} + +/* + get_hurl + returns the weapon that the monster will "throw" if it has one +*/ + +struct linked_list * +get_hurl(struct thing *tp) +{ + struct linked_list *arrow, *bolt, *rock, *silverarrow, *fbbolt; + struct linked_list *bullet, *firearrow, *dart, *dagger, *shuriken; + struct linked_list *oil, *grenade; + + struct linked_list *pitem; + int bow = FALSE, crossbow = FALSE, sling = FALSE, footbow = FALSE; + + /* Don't point to anything to begin with */ + + arrow = bolt = rock = silverarrow = fbbolt = NULL; + bullet = firearrow = dart = dagger = shuriken = NULL; + oil = grenade = NULL; + + for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem)) + if ((OBJPTR(pitem))->o_type == WEAPON) + switch ((OBJPTR(pitem))->o_which) + { + case BOW:bow = TRUE; break; + case CROSSBOW:crossbow = TRUE; break; + case SLING:sling = TRUE; break; + case FOOTBOW:footbow = TRUE; break; + case ROCK:rock = pitem; break; + case ARROW:arrow = pitem; break; + case SILVERARROW:silverarrow = pitem; break; + case BOLT:bolt = pitem; break; + case FBBOLT:fbbolt = pitem; break; + case BULLET:bullet = pitem; break; + case FLAMEARROW:firearrow = pitem; break; + case DART:dart = pitem; break; + case DAGGER:dagger = pitem; break; + case SHURIKEN:shuriken = pitem; break; + case MOLOTOV:oil = pitem; break; + case GRENADE:shuriken = pitem; break; + } + + if (bow && silverarrow) + return(silverarrow); + + if (crossbow && bolt) + return(bolt); + + if (bow && firearrow) + return(firearrow); + + if (off(*tp, ISCHARMED) && oil) + return(oil); + + if (off(*tp, ISCHARMED) && grenade) + return(grenade); + + if (footbow && fbbolt) + return(fbbolt); + + if (bow && arrow) + return(arrow); + + if (sling && bullet) + return(bullet); + + if (sling && rock) + return(rock); + + if (shuriken) + return(shuriken); + + if (dagger) + return(dagger); + + if (silverarrow) + return(silverarrow); + + if (firearrow) + return(firearrow); + + if (fbbolt) + return(fbbolt); + + if (bolt) + return(bolt); + + if (bullet) + return(bullet); + + if (dart) + return(dart); + + if (rock) + return(rock); + + return(NULL); +} + +/* + pick_weap() + returns the biggest weapon that the monster will wield if it + has a non-launching or non-missile weapon returns NULL if no weapon, or + bare hands is better +*/ + +struct object * +pick_weap(struct thing *tp) +{ + int weap_dam = maxdamage(tp->t_stats.s_dmg); + struct object *ret_obj = NULL; + struct linked_list *pitem; + + if (on(*tp, CANWIELD)) + { + for (pitem = tp->t_pack; pitem != NULL; pitem = next(pitem)) + { + struct object *obj = OBJPTR(pitem); + + if (obj->o_type != WEAPON && !(obj->o_flags&(ISLAUNCHER|ISMISL)) && + maxdamage(obj->o_damage) > weap_dam) + { + weap_dam = maxdamage(obj->o_damage); + ret_obj = obj; + } + } + } + + return (ret_obj); +} + +/* + canblink() + checks if the monster can teleport (blink). If so, it will try + to blink the monster next to the player. +*/ + +int +can_blink(struct thing *tp) +{ + int y, x, index = 9; + coord tryp; /* To hold the coordinates for use in diag_ok */ + int spots[9], found_one = FALSE; + + /* + * First, can the monster even blink? And if so, there is only a 30% + * chance that it will do so. And it won't blink if it is running. + */ + + if (off(*tp, CANBLINK) || (on(*tp, ISHELD)) || + on(*tp, ISFLEE) || + (on(*tp, ISSLOW) && off(*tp, ISHASTE) && !(tp->t_turn)) || + (rnd(10) < 9)) + return (FALSE); + + /* Initialize the spots as illegal */ + + do + { + spots[--index] = FALSE; + } + while (index > 0); + + /* Find a suitable spot next to the player */ + + for (y = hero.y - 1; y < hero.y + 2; y++) + for (x = hero.x - 1; x < hero.x + 2; x++, index++) + { + /* + * Make sure x coordinate is in range and that we are + * not at the player's position + */ + + if (x < 0 || x >= COLS || index == 4) + continue; + + /* Is it OK to move there? */ + + if (!step_ok(y, x, NOMONST, tp)) + spots[index] = FALSE; + else + { + + /* + * OK, we can go here. But don't go there if + * monster can't get at player from there + */ + + tryp.y = y; + tryp.x = x; + if (diag_ok(&tryp, &hero, tp)) + { + spots[index] = TRUE; + found_one = TRUE; + } + } + } + + /* If we found one, go to it */ + + if (found_one) + { + /* Find a legal spot */ + + while (spots[index = rnd(9)] == FALSE) + continue; + + /* Get the coordinates */ + + y = hero.y + (index / 3) - 1; + x = hero.x + (index % 3) - 1; + + /* Move the monster from the old space */ + + mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch); + + /* Move it to the new space */ + + tp->t_oldch = CCHAR( mvwinch(cw, y, x) ); + + if (cansee(y, x) && + off(*tp, ISINWALL) && + ((off(*tp, ISINVIS) && + (off(*tp, ISSHADOW) || rnd(100) < 10)) || on(player, CANSEE)) && + off(*tp, CANSURPRISE)) + mvwaddch(cw, y, x, tp->t_type); + + mvwaddch(mw, tp->t_pos.y,tp->t_pos.x,' '); /*Clear old position */ + mvwaddch(mw, y, x, tp->t_type); + tp->t_pos.y = y; + tp->t_pos.x = x; + } + + return (found_one); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/command.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/command.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1383 @@ +/* + command.c - Read and execute the user commands + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +/* + command() + Process the user commands +*/ + +void +command(void) +{ + static char repcommand; /* Command to repeat if we are repeating */ + static int fight_to_death; /* Flags if we are fighting to death */ + static coord dir; /* Last direction specified */ + + object *obj; + char ch; + int ntimes = 1; /* Number of player moves */ + coord nullcoord; + + nullcoord.x = nullcoord.y = 0; + + if (on(player, CANFLY) && rnd(2)) + ntimes++; + + if (on(player, ISHASTE)) + ntimes++; + + if (fighting && att_bonus()) + ntimes *= 2; + + if (on(player, ISSLOW)) + { + if (player.t_turn != TRUE) + ntimes--; + + player.t_turn ^= TRUE; + } + + if (ntimes == 0) + return; + + while (ntimes--) + { + moving = FALSE; + + /* If player is infested, take off a hit point */ + + if (on(player, HASINFEST) && !is_wearing(R_HEALTH)) + { + if ((pstats.s_hpt -= infest_dam) <= 0) + { + death(D_INFESTATION); + return; + } + } + + look(after); + + if (!running) + door_stop = FALSE; + + status(FALSE); + wmove(cw, hero.y, hero.x); + + if (!((running || count) && jump)) + wrefresh(cw); /* Draw screen */ + + take = 0; + after = TRUE; + + /* + * Read command or continue run + */ + + if (!no_command) + { + if (fighting) + { + ch = (fight_to_death) ? 'F' : 'f'; + } + else if (running) + { + /* + * If in a corridor, if we are at a turn with + * only one way to go, turn that way. + */ + + if ((winat(hero.y, hero.x) == PASSAGE) && off(player, ISHUH) && + (off(player, ISBLIND))) + switch (runch) + { + case 'h': corr_move(0, -1); break; + case 'j': corr_move(1, 0); break; + case 'k': corr_move(-1, 0); break; + case 'l': corr_move(0, 1); break; + } + + ch = runch; + } + else if (count) + ch = repcommand; + else + { + ch = readchar(); + + if (mpos != 0 && !running) + msg(""); /* Erase message if its there */ + } + } + else + { + ch = '.'; + fighting = moving = FALSE; + } + + if (no_command) + { + if (--no_command == 0) + msg("You can move again."); + } + else + { + + /* + * check for prefixes + */ + + if (isdigit(ch)) + { + count = 0; + while (isdigit(ch)) + { + count = count * 10 + (ch - '0'); + ch = readcharw(cw); + } + repcommand = ch; + + /* + * Preserve count for commands which can be + * repeated. + */ + + switch(ch) + { + case 'h': + case 'j': + case 'k': + case 'l': + case 'y': + case 'u': + case 'b': + case 'n': + case 'H': + case 'J': + case 'K': + case 'L': + case 'Y': + case 'U': + case 'B': + case 'N': + case 'q': + case 'r': + case 's': + case 'm': + case 't': + case 'C': + case 'I': + case '.': + case 'z': + case 'p': + break; + default: + count = 0; + } + } + + /* Save current direction */ + + if (!running) /* If running, it is already saved */ + switch (ch) + { + case 'h': + case 'j': + case 'k': + case 'l': + case 'y': + case 'u': + case 'b': + case 'n': + runch = ch; + break; + case 'H': + case 'J': + case 'K': + case 'L': + case 'Y': + case 'U': + case 'B': + case 'N': + runch = (char) tolower(ch); + break; + } + + /* + * execute a command + */ + + if (count && !running) + count--; + + switch(ch) + { + /* + * Movement and combat commands + */ + + case 'h': do_move(0,-1); break; + case 'j': do_move(1, 0); break; + case 'k': do_move(-1, 0); break; + case 'l': do_move(0, 1); break; + case 'y': do_move(-1, -1); break; + case 'u': do_move(-1, 1); break; + case 'b': do_move(1, -1); break; + case 'n': do_move(1, 1); break; + case 'H': do_run('h'); break; + case 'J': do_run('j'); break; + case 'K': do_run('k'); break; + case 'L': do_run('l'); break; + case 'Y': do_run('y'); break; + case 'U': do_run('u'); break; + case 'B': do_run('b'); break; + case 'N': do_run('n'); break; + case 'm': + moving = TRUE; + if (!get_dir()) + { + after = FALSE; + break; + } + do_move(delta.y, delta.x); + break; + case 'F': + case 'f': + fight_to_death = (ch == 'F'); + if (!fighting) + { + if (get_dir()) + { + dir = delta; + beast = NULL; + } + else + { + after = FALSE; + break; + } + } + do_fight(dir, (ch == 'F') ? TRUE : FALSE); + break; + case 't': + if (get_dir()) + missile(delta.y, delta.x, get_item("throw", 0), + &player); + else + after = FALSE; + + /* + * Informational commands - Do not do + * after daemons + */ + break; + + case 0x7f: /* sometime generated by */ + /* suspend/foreground */ + case ESCAPE: + case ' ': + after = FALSE; /* do nothing */ + break; + case 'Q': + after = FALSE; + quit(); + break; + case 'i': + after = FALSE; + inventory(pack, '*'); + break; + case 'I': + after = FALSE; + inventory(pack, 0); + break; + case '~': + after = FALSE; + next_exp_level(MESSAGE); + break; + case '>': + after = FALSE; + d_level(); + break; + case '<': + after = FALSE; + u_level(); + break; + case '?': + after = FALSE; + help(); + break; + case '/': + after = FALSE; + identify(); + break; + case 'v': + after = FALSE; + msg("UltraRogue Version %s.", release); + break; + case 'o': + after = FALSE; + option(); + strcpy(fd_data[1].mi_name, fruit); + break; + case 12: /* ctrl-l */ + case 18: /* ctrl-r */ + after = FALSE; + clearok(cw, TRUE); + wrefresh(cw); + break; + case 16: /* ctrl-p */ + { + int decrement = FALSE; + after = FALSE; + + if (mpos == 0) + decrement = TRUE; + + msg_index = (msg_index + 9) % 10; + msg(msgbuf[msg_index]); + if (decrement) + msg_index = (msg_index + 9) % 10; + } + break; + + case 'S': + after = FALSE; + if (save_game()) + { + wclear(cw); + wrefresh(cw); + endwin(); + exit(0); + } + break; + + /* + * Other misc commands + */ + + case '.': break; /* rest */ + case ',': add_pack(NULL, NOMESSAGE); break; + case 'q': quaff(&player, -1, ISNORMAL); break; + case 'r': read_scroll(&player, -1, ISNORMAL); break; + case 'd': drop(NULL); break; + case '^': set_trap(&player, hero.y, hero.x); break; + case 'c': incant(&player, nullcoord); break; + case 'D': dip_it(); break; + case 'e': eat(); break; + case '=': listen(); break; + case 'A': apply(); break; + case 'w': wield(); break; + case 'W': wear(); break; + case 'T': take_off(); break; + case 'P': ring_on(); break; + case 'R': ring_off(); break; + case 'p': prayer(); break; + case 'C': call(FALSE); break; + case 'M': call(TRUE); break; + case 's': search(FALSE); break; + + /* + * Directional commands - get_dir sets delta + */ + case 20: /* ctrl-t */ + if (get_dir()) + steal(); + else + after = FALSE; + break; + + case 'z': + if (get_dir()) + do_zap(&player, -1, ISNORMAL); + else + after = FALSE; + break; + + case 'a': + if (get_dir()) + affect(); + else + after = FALSE; + touchwin(cw); + break; + + /* + * wizard commands + */ + + case 0x17: /* ctrl-w */ + after = FALSE; + + if (!wizard) + { + if (!canwizard) + { + msg("Illegal command '^W'."); + break; + } + + if (passwd()) + { + msg("Welcome, oh mighty wizard."); + wizard = waswizard = TRUE; + } + else + { + msg("Incorrect password."); + break; + } + } + + msg("Wizard command: "); + mpos = 0; + ch = readchar(); + + switch (ch) + { + case 'v': + wiz_verbose = !wiz_verbose; + break; + + case 'e': + wizard = FALSE; + msg("Not wizard any more."); + break; + + case 's': activity(); break; + case 't': teleport(); break; + case 'm': overlay(mw, cw); break; + case 'f': overlay(stdscr, cw); break; + case 'i': inventory(lvl_obj, 0); break; + case 'c': buy_it('\0', ISNORMAL); break; + case 'I': whatis(NULL); break; + case 'F': + msg("food left: %d\tfood level: %d", + food_left,foodlev); + break; + case 'M': + creat_mons(&player, get_monster_number("create"), + MESSAGE); + break; + + case 'r': + msg("rnd(4)%d, rnd(40)%d, rnd(100)%d", + rnd(4), rnd(40), rnd(100)); + break; + + case 'C': + obj = get_object(pack, "charge", STICK, NULL); + + if (obj != NULL) + obj->o_charges = 10000; + + break; + + case 'w': + obj = get_object(pack, "price", 0, NULL); + + if (obj != NULL) + msg("Worth %d.", get_worth(obj)); + + break; + + case 'g': + { + int tlev; + + prbuf[0] = '\0'; + msg("Which level? "); + + if (get_string(prbuf, cw) == NORM) + { + msg(""); + + if ((tlev = atoi(prbuf)) < 1) + msg("Illegal level."); + else if (tlev > 3000) + { + levtype = THRONE; + level = tlev - 3000; + } + else if (tlev > 2000) + { + levtype = MAZELEV; + level = tlev - 2000; + } + else if (tlev > 1000) + { + levtype = POSTLEV; + level = tlev - 1000; + } + else + { + levtype = NORMLEV; + level = tlev; + } + + new_level(levtype,0); + } + } + break; + + case 'o': make_omnipotent(); break; + + case ESCAPE: /* Escape */ + door_stop = FALSE; + + count = 0; + after = FALSE; + break; + + default: + msg("Illegal wizard command '%s', %d.", + unctrl(ch), ch); + count = 0; + break; + + } + + break; + + default: + msg("Illegal command '%s', %d.", + unctrl(ch), ch); + count = 0; + after = FALSE; + break; + } + + /* + * turn off flags if no longer needed + */ + if (!running) + door_stop = FALSE; + } + + /* + * If he ran into something to take, let him pick it up. + */ + if (take != 0) + if (!moving) + pick_up(take); + else + show_floor(); + if (!running) + door_stop = FALSE; + } /* end while */ +} + + +void +do_after_effects(void) +{ + int i; + + /* Kick off the rest of the daemons and fuses */ + + look(FALSE); + do_daemons(AFTER); + do_fuses(AFTER); + + /* Special abilities */ + + if ((player.t_ctype == C_THIEF || player.t_ctype == C_ASSASIN || + player.t_ctype == C_NINJA || player.t_ctype == C_RANGER) && + (rnd(100) < (2 * pstats.s_dext + 5 * pstats.s_lvl))) + search(TRUE); + + for (i = 0; i < ring_value(R_SEARCH); i++) + search(FALSE); + + if (is_wearing(R_TELEPORT) && rnd(50) < 2) + { + teleport(); + + if (off(player, ISCLEAR)) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + } + + /* accidents and general clumsiness */ + + if (fighting && rnd(50) == 0) + { + msg("You become tired of this nonsense."); + fighting = after = FALSE; + } + + if (on(player, ISELECTRIC)) + electrificate(); + + if (!fighting && (no_command == 0) && cur_weapon != NULL + && rnd(on(player, STUMBLER) ? 399 : 9999) == 0 + && rnd(pstats.s_dext) < + 2 - hitweight() + (on(player, STUMBLER) ? 4 : 0)) + { + msg("You trip and stumble over your weapon."); + running = after = FALSE; + + if (rnd(8) == 0 && (pstats.s_hpt -= roll(1, 10)) <= 0) + { + msg("You break your neck and die."); + death(D_FALL); + return; + } + else if (cur_weapon->o_flags & ISPOISON && rnd(4) == 0) + { + msg("You are cut by your %s!", + inv_name(cur_weapon, LOWERCASE)); + + if (player.t_ctype != C_PALADIN + && !(player.t_ctype == C_NINJA && + pstats.s_lvl > 12) + && !save(VS_POISON)) + { + if (pstats.s_hpt == 1) + { + msg("You die from the poison in the cut."); + death(D_POISON); + return; + } + else + { + msg("You feel very sick now."); + pstats.s_hpt /= 2; + chg_str(-2, FALSE, FALSE); + } + } + } + } + + /* Time to enforce weapon and armor restrictions */ + if (rnd(9999) == 0) + if (((cur_weapon == NULL) || + (wield_ok(&player, cur_weapon, NOMESSAGE))) + && ((cur_armor == NULL) || + (wear_ok(&player, cur_armor, NOMESSAGE)))) + { + switch (player.t_ctype) + { + case C_CLERIC: + case C_DRUID: + case C_RANGER: + case C_PALADIN: + if (rnd(luck) != 0) + /* You better have done + * little wrong */ + goto bad_cleric; + + msg("You are enraptured by the renewed " + "power of your god."); + break; + + case C_MAGICIAN: + case C_ILLUSION: + msg("You become in tune with the universe."); + break; + + case C_THIEF: + case C_NINJA: + case C_ASSASIN: + msg("You become supernaly sensitive to your " + "surroundings."); + break; + + case C_FIGHTER: + msg("You catch your second wind."); + break; + + default: + msg("What a strange type you are!"); + break; + } + pstats.s_hpt = max_stats.s_hpt += rnd(pstats.s_lvl) + 1; + pstats.s_power = max_stats.s_power += rnd(pstats.s_lvl) + 1; + } + else + { /* he blew it - make him pay */ + + int death_cause = 0; + + switch (player.t_ctype) + { + case C_CLERIC: + case C_DRUID: + case C_RANGER: + case C_PALADIN: + bad_cleric: + msg("Your god scourges you for your misdeeds."); + death_cause = D_GODWRATH; + break; + + case C_MAGICIAN: + case C_ILLUSION: + msg("You short out your manna on the unfamiliar %s.", + (cur_armor != NULL ? "armor" : "weapon")); + + death_cause = D_SPELLFUMBLE; + break; + + case C_THIEF: + case C_NINJA: + case C_ASSASIN: + msg("You trip and fall because of the unfamiliar %s.", + (cur_armor != NULL ? "armor" : "weapon")); + death_cause = D_CLUMSY; + break; + + case C_FIGHTER: + debug("Fighter getting raw deal?"); + break; + + default: + msg("What a strange type you are!"); + break; + } + + aggravate(); + pstats.s_power /= 2; + pstats.s_hpt /= 2; + player.t_no_move++; + + if ((pstats.s_hpt -= rnd(pstats.s_lvl)) <= 0) + { + death(death_cause); + } + } + + if (rnd(500000) == 0) + { + new_level(THRONE,0); + fighting = running = after = FALSE; + command(); + } +} + +void +make_omnipotent(void) +{ + int i; + struct linked_list *item; + struct object *obj; + + for (i = 0; i < 20; i++) + raise_level(); + + max_stats.s_hpt += 1000; + max_stats.s_power += 1000; + pstats.s_hpt = max_stats.s_hpt; + pstats.s_power = max_stats.s_power; + max_stats.s_str = pstats.s_str = 25; + max_stats.s_intel = pstats.s_intel = 25; + max_stats.s_wisdom = pstats.s_wisdom = 25; + max_stats.s_dext = pstats.s_dext = 25; + max_stats.s_const = pstats.s_const = 25; + + if (cur_weapon == NULL || cur_weapon->o_which != CLAYMORE) + { + item = spec_item(WEAPON, CLAYMORE, 10, 10); + cur_weapon = OBJPTR(item); + cur_weapon->o_flags |= ISKNOW; + add_pack(item, NOMESSAGE); + } + + /* and a kill-o-zap stick */ + + item = spec_item(STICK, WS_DISINTEGRATE, 10000, 0); + obj = OBJPTR(item); + obj->o_flags |= ISKNOW; + know_items[TYP_STICK][WS_DISINTEGRATE] = TRUE; + + if (guess_items[TYP_STICK][WS_DISINTEGRATE]) + { + ur_free(guess_items[TYP_STICK][WS_DISINTEGRATE]); + guess_items[TYP_STICK][WS_DISINTEGRATE] = NULL; + } + + add_pack(item, NOMESSAGE); + + /* and his suit of armor */ + + if (cur_armor == NULL || + !(cur_armor->o_which == CRYSTAL_ARMOR || + cur_armor->o_which == MITHRIL)) + { + item = spec_item(ARMOR, CRYSTAL_ARMOR, 15, 0); + obj = OBJPTR(item); + obj->o_flags |= ISKNOW; + obj->o_weight = + (int) (armors[CRYSTAL_ARMOR].a_wght * 0.2); + cur_armor = obj; + add_pack(item, NOMESSAGE); + } + + /* and some rings (have to put them on, for now) */ + + + if (!is_wearing(R_SEARCH)) + { + item = spec_item(RING, R_SEARCH, 0, 0); + obj = OBJPTR(item); + obj->o_flags |= ISKNOW; + know_items[TYP_RING][R_SEARCH] = TRUE; + + if (guess_items[TYP_RING][R_SEARCH]) + { + ur_free(guess_items[TYP_RING][R_SEARCH]); + guess_items[TYP_RING][R_SEARCH] = NULL; + } + + add_pack(item, NOMESSAGE); + } + + if (!is_wearing(R_PIETY)) + { + item = spec_item(RING, R_PIETY, 0, 0); + obj = OBJPTR(item); + obj->o_flags |= ISKNOW; + know_items[TYP_RING][R_PIETY] = TRUE; + + if (guess_items[TYP_RING][R_PIETY]) + { + ur_free(guess_items[TYP_RING][R_PIETY]); + guess_items[TYP_RING][R_PIETY] = NULL; + } + + add_pack(item, NOMESSAGE); + } + + item = spec_item(SCROLL, S_ELECTRIFY, 0, 0); + obj = OBJPTR(item); + obj->o_flags |= ISKNOW; + know_items[TYP_SCROLL][S_ELECTRIFY] = TRUE; + + if (guess_items[TYP_SCROLL][S_ELECTRIFY]) + { + ur_free(guess_items[TYP_SCROLL][S_ELECTRIFY]); + guess_items[TYP_SCROLL][S_ELECTRIFY] = NULL; + } + + add_pack(item, NOMESSAGE); + + /* Spiff him up a bit */ + quaff(&player, P_SHERO, ISBLESSED); + quaff(&player, P_CLEAR, ISBLESSED); + quaff(&player, P_FIRERESIST, ISBLESSED); + quaff(&player, P_TRUESEE, ISBLESSED); + quaff(&player, P_PHASE, ISBLESSED); + purse += 50000L; + updpack(); +} + + +/* + quit() + Have player make certain, then exit. +*/ + +void +quit_handler(int sig) +{ + if (signal(SIGINT, quit_handler) != quit_handler) + mpos = 0; + + sig = 0; + + quit(); +} + +void +quit(void) +{ + msg("Really quit?"); + + wrefresh(cw); + + if (readchar() == 'y') + { + clear(); + wclear(cw); + wrefresh(cw); + move(LINES - 1, 0); + wrefresh(stdscr); + score(pstats.s_exp, pstats.s_lvl, CHICKEN, 0); + byebye(); + } + else + { + signal(SIGINT, quit_handler); + wmove(cw, 0, 0); + wclrtoeol(cw); + status(FALSE); + wrefresh(cw); + mpos = 0; + count = 0; + fighting = running = 0; + } +} + +/* + search() + Player gropes about him to find hidden things. +*/ + +void +search(int is_thief) +{ + int x, y; + char ch; + + /* + * Look all around the hero, if there is something hidden there, give + * him a chance to find it. If its found, display it. + */ + + if (on(player, ISBLIND)) + return; + + for (x = hero.x - 1; x <= hero.x + 1; x++) + for (y = hero.y - 1; y <= hero.y + 1; y++) + { + ch = winat(y, x); + + if (isatrap(ch)) + { + static char trname[1024]; /* temp scratch space */ + struct trap *tp; + struct room *rp; + + if (isatrap( mvwinch(cw, y, x))) + continue; + + tp = trap_at(y, x); + + if ((tp->tr_flags & ISTHIEFSET) || + (rnd(100) > 50 && !is_thief)) + break; + + rp = roomin(hero); + + if (tp->tr_type == FIRETRAP && rp != NULL) + { + rp->r_flags &= ~ISDARK; + light(&hero); + } + + tp->tr_flags |= ISFOUND; + mvwaddch(cw, y, x, ch); + count = 0; + running = FALSE; + msg(tr_name(tp->tr_type,trname)); + } + else if (ch == SECRETDOOR) + { + if (rnd(100) < 20 && !is_thief) + { + mvaddch(y, x, DOOR); + count = 0; + } + } + } +} + +/* + help() + Give single character help, or the whole mess if he wants it +*/ + +void +help(void) +{ + const struct h_list *strp = helpstr; + char helpch; + int cnt; + + msg("Character you want help for (* for all): "); + helpch = readchar(); + mpos = 0; + + /* + * If its not a *, print the right help string or an error if he + * typed a funny character. + */ + + if (helpch != '*') + { + wmove(cw, 0, 0); + + while (strp->h_ch) + { + if (strp->h_desc == 0) + if (!wizard) + break; + else + { + strp++; + continue; + } + + if (strp->h_ch == helpch) + { + msg("%s%s", unctrl(strp->h_ch), strp->h_desc); + break; + } + strp++; + } + + if (strp->h_ch != helpch) + msg("Unknown character '%s'.", unctrl(helpch)); + + return; + } + + /* + * Here we print help for everything. Then wait before we return to + * command mode + */ + + wclear(hw); + cnt = 0; + + while (strp->h_ch) + { + if (strp->h_desc == 0) + if (!wizard) + break; + else + { + strp++; + continue; + } + + mvwaddstr(hw, cnt % 23, cnt > 22 ? 40 : 0, unctrl(strp->h_ch)); + waddstr(hw, strp->h_desc); + strp++; + + if (++cnt >= 46 && strp->h_ch && (strp->h_desc != NULL || wizard)) + { + wmove(hw, LINES - 1, 0); + wprintw(hw, (char *) morestr); + wrefresh(hw); + wait_for(' '); + wclear(hw); + cnt = 0; + } + } + + wmove(hw, LINES - 1, 0); + wprintw(hw, (char *) morestr); + wrefresh(hw); + wait_for(' '); + wclear(hw); + wrefresh(hw); + + wmove(cw, 0, 0); + wclrtoeol(cw); + status(FALSE); + touchwin(cw); + + return; +} + +/* + identify() + Tell the player what a certain thing is. +*/ + +void +identify(void) +{ + int ch; + char *str; + + msg("What do you want identified? "); + mpos = 0; + + if ((ch = readchar()) == ESCAPE) + { + msg(""); + return; + } + + if (isalpha(ch)) + { + id_monst(ch); + return; + } + + switch (ch) + { + case '|': + case '-': str = "wall of a room"; break; + case GOLD: str = "gold"; break; + case STAIRS: str = "passage leading down"; break; + case DOOR: str = "door"; break; + case FLOOR: str = "room floor"; break; + case VPLAYER: str = "The hero of the game ---> you"; break; + case IPLAYER: str = "you (but invisible)"; break; + case PASSAGE: str = "passage"; break; + case POST: str = "trading post"; break; + case POOL: str = "a shimmering pool"; break; + case TRAPDOOR: str = "trapdoor"; break; + case ARROWTRAP: str = "arrow trap"; break; + case SLEEPTRAP: str = "sleeping gas trap"; break; + case BEARTRAP: str = "bear trap"; break; + case TELTRAP: str = "teleport trap"; break; + case DARTTRAP: str = "dart trap"; break; + case MAZETRAP: str = "entrance to a maze"; break; + case FIRETRAP: str = "fire trap"; break; + case POISONTRAP: str = "poison pool trap"; break; + case LAIR: str = "monster lair entrance"; break; + case RUSTTRAP: str = "rust trap"; break; + case POTION: str = "potion"; break; + case SCROLL: str = "scroll"; break; + case FOOD: str = "food"; break; + case WEAPON: str = "weapon"; break; + case ' ': str = "solid rock"; break; + case ARMOR: str = "armor"; break; + case ARTIFACT: str = "an artifact from bygone ages"; break; + case RING: str = "ring"; break; + case STICK: str = "wand or staff"; break; + default: str = "unknown character"; break; + } + msg("'%s'; %s", unctrl(ch), str); +} + +/* + d_level() + He wants to go down a level +*/ + +void +d_level(void) +{ + int no_phase = FALSE; + + if (mvinch(hero.y, hero.x) != STAIRS) + { + if (off(player, CANINWALL)) /* Must use stairs if can't phase */ + { + msg("I see no way down."); + return; + } + + extinguish_fuse(FUSE_UNPHASE);/*Using phase to go down gets rid of it*/ + no_phase = TRUE; + } + + if (is_wearing(R_LEVITATION) || on(player, CANFLY)) + { + msg("You can't! You're floating in the air."); + return; + } + + if (rnd(pstats.s_dext) < 3 * (2 - hitweight() + + (on(player, STUMBLER) ? 4 : 0))) + { + msg("You trip and fall down the stairs."); + + if ((pstats.s_hpt -= roll(1, 10)) <= 0) + { + msg("You break your neck and die."); + death(D_FALL); + return; + } + } + + level++; + new_level(NORMLEV,0); + + if (no_phase) + unphase(NULL); + + return; +} + +/* + u_level() + He wants to go up a level +*/ + +void +u_level(void) +{ + char ch = 0; + + if (has_artifact && ((ch = CCHAR(mvinch(hero.y, hero.x))) == STAIRS || + (on(player, CANINWALL) + && (is_wearing(R_LEVITATION) || on(player, CANFLY))))) + { + if (--level == 0) + total_winner(); + else if (rnd(wizard ? 3 : 15) == 0) + new_level(THRONE,0); + else + { + new_level(NORMLEV,0); + msg("You feel a wrenching sensation in your gut."); + } + + if (on(player, CANINWALL) && ch != STAIRS) + { + extinguish_fuse(FUSE_UNPHASE); + unphase(NULL); + } + + return; + } + else if (ch != STAIRS && + !(on(player, CANINWALL) && (is_wearing(R_LEVITATION) + || on(player, CANFLY)))) + msg("I see no way up."); + else + msg("Your way is magically blocked."); + + return; +} + +/* + call() + allow a user to call a potion, scroll, or ring something +*/ + +void +call(int mark) +{ + struct object *obj; + char *elsewise; + int item_type = numthings; + char **item_color = NULL; + + if (mark) + obj = get_object(pack, "mark", 0, bff_markable); + else + obj = get_object(pack, "call", 0, bff_callable); + + if (obj == NULL) + return; + + switch (obj->o_type) + { + case RING: + item_type = TYP_RING; + item_color = r_stones; + break; + case POTION: + item_type = TYP_POTION; + item_color = p_colors; + break; + case SCROLL: + item_type = TYP_SCROLL; + item_color = s_names; + break; + case STICK: + item_type = TYP_STICK; + item_color = ws_made; + default: + if (!mark) + { + msg("You can't call that anything."); + return; + } + break; + } + + elsewise = (guess_items[item_type][obj->o_which] != NULL ? + guess_items[item_type][obj->o_which] : item_color[obj->o_which]); + + if (know_items[item_type][obj->o_which] && !mark) + { + msg("That has already been identified."); + return; + } + + if (mark) + { + if (obj->o_mark[0]) + msg("Was marked \"%s\".", obj->o_mark); + + msg("What do you want to mark it? "); + prbuf[0] = '\0'; + } + else + { + msg("Was called \"%s\".", elsewise); + msg("What do you want to call it? "); + + if (guess_items[item_type][obj->o_which] != NULL) + ur_free(guess_items[item_type][obj->o_which]); + + strcpy(prbuf, elsewise); + } + + if (get_string(prbuf, cw) == NORM) + { + if (mark) + { + strncpy(obj->o_mark, prbuf, MARKLEN - 1); + obj->o_mark[MARKLEN - 1] = '\0'; + } + else + { + guess_items[item_type][obj->o_which] = new_alloc(strlen(prbuf) + 1); + strcpy(guess_items[item_type][obj->o_which], prbuf); + } + } + + return; +} + +/* + att_bonus() + bonus attacks for certain player classes +*/ + +int +att_bonus(void) +{ + int bonus = FALSE; + + if ((player.t_ctype == C_FIGHTER || player.t_ctype == C_PALADIN) + && (pstats.s_lvl > 12 || + (pstats.s_lvl > 6 && pstats.s_lvl < 13 && rnd(2)))) + bonus = TRUE; + + else if ((player.t_ctype == C_RANGER) + && (pstats.s_lvl > 14 || + (pstats.s_lvl > 7 && pstats.s_lvl < 15 && rnd(2)))) + bonus = TRUE; + + else if ((player.t_ctype == C_NINJA) + && (pstats.s_lvl > 8 || + (pstats.s_lvl > 4 && pstats.s_lvl < 9 && rnd(2)))) + bonus = TRUE; + + return(bonus); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/daemon.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/daemon.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,327 @@ +/* + daemon.c - functions for dealing with things that happen in the future + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/* + Needs to be rewritten again to work on a per creature basis. + Either each monster will have a list of effect, or each + fuse will take a creature pointer and multiple entries + for each fuse will be allowed. I tend to want to attach + the effects to the creature. +*/ + +#include "rogue.h" + +int demoncnt; + +struct daemon daemons[DAEMON_MAX] = +{ + { DAEMON_NULL, NULL }, + { DAEMON_DOCTOR, doctor }, + { DAEMON_ROLLWAND, rollwand }, + { DAEMON_STOMACH, stomach }, + { DAEMON_RUNNERS, runners } +}; + +struct fuse fuses[FUSE_MAX] = +{ + { FUSE_NULL, NULL }, + { FUSE_SWANDER, swander }, + { FUSE_UNCONFUSE, unconfuse }, + { FUSE_UNSCENT, unscent }, + { FUSE_SCENT, scent }, + { FUSE_UNHEAR, unhear }, + { FUSE_HEAR, hear }, + { FUSE_UNSEE, unsee }, + { FUSE_UNSTINK, unstink }, + { FUSE_UNCLRHEAD, unclrhead }, + { FUSE_UNPHASE, unphase }, + { FUSE_SIGHT, sight }, + { FUSE_RES_STRENGTH, res_strength }, + { FUSE_NOHASTE, nohaste }, + { FUSE_NOSLOW, noslow }, + { FUSE_SUFFOCATE, suffocate }, + { FUSE_CURE_DISEASE, cure_disease }, + { FUSE_UNITCH, un_itch }, + { FUSE_APPEAR, appear }, + { FUSE_UNELECTRIFY, unelectrify }, + { FUSE_UNBHERO, unbhero }, + { FUSE_UNSHERO, unshero }, + { FUSE_UNXRAY, NULL }, + { FUSE_UNDISGUISE, undisguise }, + { FUSE_SHERO, shero }, + { FUSE_WGHTCHK, wghtchk }, + { FUSE_UNSUMMON, unsummon }, + { FUSE_UNGAZE, ungaze }, + { FUSE_UNCOLD, uncold }, + { FUSE_UNHOT, unhot }, + { FUSE_UNFLY, unfly }, + { FUSE_UNBREATHE, unbreathe }, + { FUSE_UNREGEN, unregen }, + { FUSE_UNSUPEREAT, unsupereat }, + { FUSE_UNSHIELD, unshield }, + { FUSE_UNMSHIELD, unmshield }, + { FUSE_UNTRUESEE, untruesee } +}; + +/* + d_slot() + Find an empty slot in the daemon/fuse list +*/ + +struct delayed_action * +d_slot(void) +{ + int i; + struct delayed_action *dev; + + for (i = 0, dev = d_list; i < MAXDAEMONS; i++, dev++) + if (dev->d_type == EMPTY) + return(dev); + + return(NULL); +} + + +/* + find_slot() + Find a particular slot in the table +*/ + +struct delayed_action * +find_slot(int type, int id) +{ + int i; + struct delayed_action *dev; + + for (i = 0, dev = d_list; i < MAXDAEMONS; i++, dev++) + if ( (dev->d_type == type) && (id == dev->d_id) ) + return(dev); + + return(NULL); +} + + +/* + daemon() + Start a daemon, takes a function. +*/ + +void +start_daemon(int id, void *arg, int whendo) +{ + struct delayed_action *dev; + + dev = d_slot(); + + if (dev != NULL) + { + dev->d_type = DAEMON; + dev->d_when = whendo; + dev->d_id = id; + dev->d_arg = arg; + dev->d_time = 1; + demoncnt += 1; /* update count */ + } +} + + +/* + kill_daemon() + Remove a daemon from the list +*/ + +void +kill_daemon(int id) +{ + struct delayed_action *dev; + + if ((dev = find_slot(DAEMON, id)) == NULL) + return; + + /* Take it out of the list */ + + dev->d_type = EMPTY; + demoncnt -= 1; /* update count */ + + return; +} + + +/* + do_daemons() + Run all the daemons that are active with the current flag, + passing the argument to the function. +*/ + +void +do_daemons(int now) +{ + struct delayed_action *dev; + + /* Loop through the devil list */ + + for (dev = d_list; dev < &d_list[MAXDAEMONS]; dev++) + /* Executing each one, giving it the proper arguments */ + if ( (dev->d_when == now) && (dev->d_type == DAEMON)) + { + if ((dev->d_id < 1) || (dev->d_id >= DAEMON_MAX)) + printf("Bad daemon id %d\n", dev->d_id); + else if (daemons[dev->d_id].func == NULL) + printf("No action for daemon %d!!!\n", dev->d_id); + else + { + daemon_arg arg; + + arg.varg = dev->d_arg; + daemons[dev->d_id].func(&arg); + } + } + +} + + +/* + fuse() + Start a fuse to go off in a certain number of turns +*/ + +void +light_fuse(int id, void *arg, int time, int whendo) +{ + struct delayed_action *wire; + + wire = d_slot(); + + if (wire != NULL) + { + wire->d_type = FUSE; + wire->d_when = whendo; + wire->d_id = id; + wire->d_arg = arg; + wire->d_time = time; + demoncnt += 1; /* update count */ + } +} + + +/* + lengthen() + Increase the time until a fuse goes off +*/ + +void +lengthen_fuse(int id, int xtime) +{ + struct delayed_action *wire; + + if ((wire = find_slot(FUSE,id)) == NULL) + return; + + wire->d_time += xtime; + + return; +} + + +/* + extinguish() + Put out a fuse +*/ + +void +extinguish_fuse(int id) +{ + struct delayed_action *wire; + + if ((wire = find_slot(FUSE,id)) == NULL) + return; + + wire->d_type = EMPTY; + demoncnt -= 1; + + return; +} + + +/* + do_fuses() + Decrement counters and start needed fuses +*/ + +void +do_fuses(int now) +{ + struct delayed_action *wire; + + /* Step though the list */ + + for (wire = d_list; wire < &d_list[MAXDAEMONS]; wire++) + { + /* + * Decrementing counters and starting things we want. We + * also need to remove the fuse from the list once it has + * gone off. + */ + + if( (wire->d_type == FUSE) && (wire->d_when == now) ) + { + if (--wire->d_time <= 0) + { + fuse_arg arg; + + arg.varg = wire->d_arg; + wire->d_type = EMPTY; + fuses[wire->d_id].func(&arg); + demoncnt -= 1; + } + } + + } + + return; +} + + +/* + activity() + Show wizard number of demaons and memory blocks used +*/ + +void +activity(void) +{ + msg("Daemons = %d : Memory Items = %d ", demoncnt, total); + return; +} + +/* + waste_time() + Do nothing but let other things happen +*/ + +void +waste_time(void) +{ + if (inwhgt) /* if from wghtchk then done */ + return; + + do_daemons(BEFORE); + do_fuses(BEFORE); + do_daemons(AFTER); + do_fuses(AFTER); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/daemons.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/daemons.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,999 @@ +/* + daemons.c - All the daemon and fuse functions are in here + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include "rogue.h" + +/* + doctor() + A healing daemon that restors spell and hit points after rest +*/ + +void +doctor(daemon_arg *who) +{ + struct thing *tp = who->thingptr; + long ohp; /* turn off ISFLEE? */ + struct stats *curp; /* current stats pointer */ + struct stats *maxp; /* max stats pointer */ + int turns_quiet, new_points; + + curp = &(tp->t_stats); + maxp = &(tp->maxstats); + + if (on(*tp, ISINWALL)) + { + tp->t_rest_hpt = 0; + return; + } + + /* Check for regenerating spell points first */ + + doctor_spell_points(tp); + + if (curp->s_hpt == maxp->s_hpt) + { + tp->t_rest_hpt = 0; + return; + } + + tp->t_rest_hpt++; + + switch (tp->t_ctype) + { + case C_MAGICIAN: + case C_ILLUSION: + turns_quiet = 24 - curp->s_lvl; + new_points = curp->s_lvl / 2 - 4; + break; + + case C_THIEF: + case C_ASSASIN: + case C_NINJA: + turns_quiet = 16 - curp->s_lvl; + new_points = curp->s_lvl / 2 - 1; + break; + + case C_CLERIC: + case C_DRUID: + turns_quiet = 16 - curp->s_lvl; + new_points = curp->s_lvl / 2 - 2; + break; + + case C_FIGHTER: + case C_RANGER: + case C_PALADIN: + turns_quiet = 8 - curp->s_lvl / 2; + new_points = curp->s_lvl / 2 - 1; + break; + + case C_MONSTER: + turns_quiet = 16 - curp->s_lvl; + new_points = curp->s_lvl / 2 - 6; + break; + + default: + debug("What a strange character you are!"); + return; + } + + ohp = curp->s_hpt; + + if (off(*tp, HASDISEASE)) + { + if (curp->s_lvl < 8) + { + if (tp->t_rest_hpt > turns_quiet) + curp->s_hpt++; + } + else if (tp->t_rest_hpt >= 15) + curp->s_hpt += rnd(new_points) + 1; + } + + if (tp == &player) + { + if (curp->s_lvl > 10) + turns_quiet = 2; + else + turns_quiet = rnd(turns_quiet / 6) + 1; + + if (is_wearing(R_REGEN)) + curp->s_hpt += ring_value(R_REGEN); + } + + if (on(*tp, ISREGEN)) + curp->s_hpt += curp->s_lvl / 5 + 1; + + if (ohp != curp->s_hpt) + { + if (curp->s_hpt >= maxp->s_hpt) + { + curp->s_hpt = maxp->s_hpt; + + if (off(*tp, WASTURNED) && on(*tp, ISFLEE) + && tp != &player) + { + turn_off(*tp, ISFLEE); + tp->t_oldpos = tp->t_pos; + /* Start our trek over */ + } + } + tp->t_rest_hpt = 0; + } + + return; +} + + +/* + doctor_spell_points() + A healing daemon that restors spell points +*/ + +void +doctor_spell_points(struct thing *tp) +{ + int turns_quiet, new_points; + struct stats *curp; /* current stats pointer */ + struct stats *maxp; /* max stats pointer */ + int opower; /* current power */ + + curp = &(tp->t_stats); + maxp = &(tp->maxstats); + opower = curp->s_power; + + /* The right ring will let you regenerate while wearing bad armor */ + + if (off(*tp, CANCAST) || + ((tp == &player) && + (cur_armor && wear_ok(tp, cur_armor, NOMESSAGE) == FALSE) && + !(is_wearing(R_WIZARD) || is_wearing(R_PIETY)))) + { + tp->t_rest_pow = 0; + return; + } + + tp->t_rest_pow++; + + switch (tp->t_ctype) + { + case C_MAGICIAN: + case C_ILLUSION: + turns_quiet = 18 - curp->s_lvl / 2; + new_points = curp->s_lvl / 2; + break; + case C_CLERIC: + case C_DRUID: + turns_quiet = 24 - curp->s_lvl; + new_points = curp->s_lvl / 2 - 2; + break; + case C_THIEF: + case C_ASSASIN: + case C_NINJA: + turns_quiet = 32 - curp->s_lvl; + new_points = curp->s_lvl / 3 - 3; + break; + case C_FIGHTER: + case C_RANGER: + case C_PALADIN: + turns_quiet = 32 - curp->s_lvl; + new_points = curp->s_lvl / 3 - 4; + break; + case C_MONSTER: + turns_quiet = 24 - curp->s_lvl; + new_points = curp->s_lvl - 6; + break; + default: + return; + } + + if (curp->s_lvl < 8) + { + if (tp->t_rest_pow > turns_quiet) + curp->s_power++; + } + else if (tp->t_rest_pow >= 15) + curp->s_power += rnd(new_points) + 1; + + if (tp == &player && (is_wearing(R_WIZARD) || is_wearing(R_PIETY))) + curp->s_power += ring_value(R_WIZARD) + ring_value(R_PIETY); + + curp->s_power = min(max(0, curp->s_power), maxp->s_power); + + if (curp->s_power != opower) + tp->t_rest_pow = 0; + + return; +} + +/* + rollwand() + called to roll to see if a wandering monster starts up +*/ + +daemon +rollwand(daemon_arg *arg) +{ + NOOP(arg); + + if ((rnd(6) == 0) && (player.t_ctype != C_THIEF || + (rnd(30) >= pstats.s_dext))) + { + wanderer(); + kill_daemon(DAEMON_ROLLWAND); + light_fuse(FUSE_SWANDER, 0, WANDERTIME, BEFORE); + } + + return; +} + +/* + stomach() + digest the hero's food +*/ + +daemon +stomach(daemon_arg *arg) +{ + int oldfood, old_hunger; + int amount; + int power_scale; + + NOOP(arg); + + old_hunger = hungry_state; + + if (food_left <= 0) + { + /* the hero is fainting */ + + if (no_command || rnd(100) > 20) + return; + + no_command = rnd(8) + 4; + running = FALSE; + count = 0; + hungry_state = F_FAINT; + feed_me(hungry_state); + } + else + { + oldfood = food_left; + + amount = ring_eat(LEFT_1) + ring_eat(LEFT_2) + + ring_eat(LEFT_3) + ring_eat(LEFT_4) + + ring_eat(RIGHT_1) + ring_eat(RIGHT_2) + + ring_eat(RIGHT_3) + ring_eat(RIGHT_4) + + foodlev; + + if (on(player, SUPEREAT)) /* artifact or regeneration munchies */ + amount *= 2; + + if (on(player, POWEREAT)) /* Used an artifact power */ + { + amount += 40; + turn_off(player, POWEREAT); + } + + power_scale = (on(player, POWERDEXT) + on(player, POWERSTR) + + on(player, POWERWISDOM) + on(player, POWERINTEL) + + on(player, POWERCONST) + 1); + + food_left -= amount * power_scale; + + if (food_left < MORETIME && oldfood >= MORETIME) + { + hungry_state = F_WEAK; + running = FALSE; + feed_me(hungry_state); + } + else if (food_left < 2 * MORETIME && oldfood >= 2 * MORETIME) + { + hungry_state = F_HUNGRY; + running = FALSE; + feed_me(hungry_state); + } + } + + if (old_hunger != hungry_state) + updpack(); + + wghtchk(NULL); +} + + +/* + runners() + Make all the running monsters move. with monsters now fighting + each other, this routine have been enhanced and may need more work yet +*/ + +daemon +runners(daemon_arg *arg) +{ + struct linked_list *item; + struct thing *tp; + + NOOP(arg); + + for (item = mlist; item != NULL; item = next_mons) + { + curr_mons = item; + next_mons = next(curr_mons); + tp = THINGPTR(item); + + if (on(*tp, ISHELD) && rnd(tp->t_stats.s_str + + tp->t_stats.s_lvl) > 10 + rnd(50)) + { + turn_off(*tp, ISHELD); + turn_off(*tp, ISDISGUISE); + turn_on(*tp, ISRUN); + tp->t_ischasing = TRUE; + tp->t_chasee = &player; + tp->t_horde = NULL; + + if (tp->t_stats.s_hpt < rnd(tp->maxstats.s_hpt)) + turn_on(*tp, ISFLEE); + + if (cansee(tp->t_pos.y, tp->t_pos.x)) + msg("The %s breaks free!", monsters[tp->t_index].m_name); + } + + if (off(*tp, ISHELD) && on(*tp, ISRUN)) + { + int flee = FALSE; + + flee = on(*tp, ISFLEE) || + ( (tp->t_chasee == &player) && + on(player, ISINWALL) && + off(*tp, CANINWALL) && off(*tp, ISFAMILIAR) ); + + if (off(*tp, ISSLOW) || tp->t_turn) + { + daemon_arg targ; + + targ.thingptr = tp; + doctor(&targ); + do_chase(tp, flee); + } + + if (curr_mons && (on(*tp, ISHASTE) || + ((on(*tp, CANFLY) || on(*tp, ISFAST)) && + DISTANCE(hero, tp->t_pos) >= 4))) + { + daemon_arg targ; + + targ.thingptr = tp; + doctor(&targ); + do_chase(tp, flee); + } + + if (curr_mons) + { + tp->t_turn ^= TRUE; + tp->t_wasshot ^= FALSE; /* Not shot anymore */ + } + + } + } + + curr_mons = next_mons = NULL; + + return; +} + +/* + swander() + called when it is time to start rolling for wandering monsters +*/ + +fuse +swander(fuse_arg *arg) +{ + NOOP(arg); + + start_daemon(DAEMON_ROLLWAND, 0, BEFORE); + return; +} + +/* + unconfuse + release the poor player from his confusion +*/ + +fuse +unconfuse(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISHUH); + msg("You feel less confused now."); + return; +} + +/* + unscent() + turn of extra smelling ability +*/ + +fuse +unscent(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, CANSCENT); + msg("The smell of monsters goes away."); +} + +/* + scent + give back the players sense of smell +*/ + +fuse +scent(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISUNSMELL); + msg("You begin to smell the damp dungeon air again."); +} + + +/* + unhear + player doesn't have extra hearing any more +*/ + +fuse +unhear(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, CANHEAR); + msg("The sounds of monsters fades away."); +} + +/* + hear() + return the players sense of hearing +*/ + +fuse +hear(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISDEAF); + msg("You can hear again."); +} + + +/* + unsee + He lost his see invisible power + + Need to make monsters invisible again? This + was done in Rogue 5.2 + + for (th = mlist; th != NULL; th = next(th)) + if (on(*th, ISINVIS) && see_monst(th)) + { + move(th->t_pos.y, th->t_pos.x); + addch(th->t_oldch); + } +*/ + +fuse +unsee(fuse_arg *arg) +{ + NOOP(arg); + + if (!is_wearing(R_SEEINVIS)) + { + turn_off(player, CANSEE); + msg("The tingling feeling leaves your eyes."); + } +} + +/* + unstink() + Remove to-hit handicap from player +*/ + +fuse +unstink(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, HASSTINK); +} + +/* + unclrhead + Player is no longer immune to confusion +*/ + +fuse +unclrhead(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISCLEAR); + msg("The blue aura about your head fades away."); +} + +/* + unphase() + Player can no longer walk through walls +*/ + +fuse +unphase(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, CANINWALL); + + msg("Your dizzy feeling leaves you."); + + if (!step_ok(hero.y, hero.x, NOMONST, &player)) + death(D_PETRIFY); +} + +/* + sight() + He gets his sight back +*/ + +fuse +sight(fuse_arg *arg) +{ + NOOP(arg); + + if (on(player, ISBLIND)) + { + extinguish_fuse(FUSE_SIGHT); + turn_off(player, ISBLIND); + light(&hero); + msg("The veil of darkness lifts."); + } +} + +/* + res_strength() + Restore player's strength +*/ + +fuse +res_strength(fuse_arg *arg) +{ + NOOP(arg); + + if (lost_str) + { + chg_str(lost_str, FALSE, FALSE); + lost_str = 0; + } + else + pstats.s_str = max_stats.s_str + ring_value(R_ADDSTR) + + (on(player, POWERSTR) ? 10 : 0) + + (on(player, SUPERHERO) ? 10 : 0); + + updpack(); +} + +/* + nohaste() + End the hasting +*/ + +fuse +nohaste(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISHASTE); + msg("You feel yourself slowing down."); +} + +/* + noslow() + End the slowing +*/ + +fuse +noslow(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISSLOW); + msg("You feel yourself speeding up."); +} + +/* + suffocate() + If this gets called, the player has suffocated +*/ + +fuse +suffocate(fuse_arg *arg) +{ + NOOP(arg); + + death(D_SUFFOCATION); +} + +/* + cure_disease() + daemon for curing the diseased +*/ + +fuse +cure_disease(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, HASDISEASE); + + if (off(player, HASINFEST)) + msg("You begin to feel yourself improving again."); +} + +/* + un_itch() + daemon for adding back dexterity +*/ + +fuse +un_itch(fuse_arg *arg) +{ + NOOP(arg); + + if (lost_dext) + { + chg_dext(lost_dext, FALSE, FALSE); + lost_dext = 0; + turn_off(player, HASITCH); + } +} + +/* + appear() + Become visible again +*/ + +fuse +appear(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISINVIS); + PLAYER = VPLAYER; + msg("The tingling feeling leaves your body."); + light(&hero); +} + +/* + unelectrify() + stop shooting off sparks +*/ + +fuse +unelectrify(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISELECTRIC); + msg("The sparks and violet glow from your body fade away."); + light(&hero); +} + +/* + unshero() + super heroism wears off, now do nasty effects +*/ + +fuse +unshero(fuse_arg *arg) +{ + NOOP(arg); + + msg("Your feeling of invulnerability goes away."); + turn_off(player, SUPERHERO); + chg_str(-11, FALSE, FALSE); + chg_dext(-6, FALSE, FALSE); + food_left -= HEROTIME + rnd(HEROTIME); + no_command += 5 + rnd(5); + msg("You fall asleep."); +} + +/* + unbhero() + blessed super heroism wears off, no bad effects +*/ + +fuse +unbhero(fuse_arg *arg) +{ + NOOP(arg); + + msg("Your feeling of invincibility goes away."); + turn_off(player, SUPERHERO); + chg_str(-10, FALSE, FALSE); + chg_dext(-5, FALSE, FALSE); +} + +/* + undisguise() + player stops looking like a monster +*/ + +fuse +undisguise(fuse_arg *arg) +{ + NOOP(arg); + + msg("Your skin feels itchy for a moment."); + turn_off(player, ISDISGUISE); + PLAYER = VPLAYER; + light(&hero); +} + +/* + unsummon() + Unsummon a monster +*/ + +void +unsummon(fuse_arg *monny) +{ + struct linked_list *monst = monny->ll; + struct linked_list *sum_monst = (struct linked_list *) monst; + struct thing *tp = THINGPTR(sum_monst); + char *mname = monsters[tp->t_index].m_name; + + turn_off(*tp, WASSUMMONED); + turn_off(player, HASSUMMONED); + msg("Goodbye, master."); + msg("The summoned %s phases out of existence", mname); + killed(NULL, sum_monst, NOMESSAGE, NOPOINTS); + mons_summoned--; +} + +/* + ungaze() + Turn off gaze reflection +*/ + +fuse +ungaze(fuse_arg *arg) +{ + NOOP(arg); + + msg("The shiny particles swirl to the floor."); + turn_off(player, CANREFLECT); +} + +/* + shero() + restore lost abilities from cursed potion of shero +*/ + +fuse +shero(fuse_arg *arg) +{ + NOOP(arg); + + msg("You feel normal again."); + chg_str(2, FALSE, TRUE); + chg_dext(2, FALSE, TRUE); + turn_off(player, ISUNHERO); +} + +/* + wghtchk() + check that the pack weight is OK +*/ + +fuse +wghtchk(fuse_arg *arg) +{ + int dropchk, err = TRUE; + char ch; + + NOOP(arg); + + inwhgt = TRUE; + + if (pstats.s_pack > pstats.s_carry) + { + ch = CCHAR( mvwinch(stdscr, hero.y, hero.x) ); + + if ((ch != FLOOR && ch != PASSAGE)) + { + extinguish_fuse(FUSE_WGHTCHK); + light_fuse(FUSE_WGHTCHK, (void *)TRUE, 1, AFTER); + inwhgt = FALSE; + return; + } + + extinguish_fuse(FUSE_WGHTCHK); + msg("Your pack is too heavy for you."); + + do + { + dropchk = drop(NULL); + + if (dropchk == FALSE) + { + mpos = 0; + msg("You must drop something."); + } + + if (dropchk == TRUE) + err = FALSE; + + } + while (err); + + } + + inwhgt = FALSE; +} + + +/* + uncold() + He lost his cold resistance power +*/ + +fuse +uncold(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, NOCOLD); + + if (!is_wearing(R_COLDRESIST)) + msg("You feel a slight chill in the air."); +} + +/* + unhot() + He lost his fire resistance power +*/ + +fuse +unhot(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, NOFIRE); + + if (!is_wearing(R_FIRERESIST)) + msg("You feel a flush of warmth."); +} + +/* + unfly() + He stopped flying +*/ + +fuse +unfly(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, CANFLY); + + if (!is_wearing(R_LEVITATION)) + msg("You float gently to the ground."); +} + +/* + unbreathe() + He started needing oxygen +*/ + +fuse +unbreathe(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, HASOXYGEN); + + if (!is_wearing(R_BREATHE)) + msg("You start huffing and puffing."); +} + +/* + unregen() + He stops being regenerative +*/ + +fuse +unregen(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, ISREGEN); + + if (!is_wearing(R_REGEN)) + msg("Your metabolism slows down."); +} + +/* + unsupereat() + He stops being excessively hungry +*/ + +fuse +unsupereat(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, SUPEREAT); + msg("You stop feeling so hungry."); +} + +/* + unshield() + He stops having his AC helped by magic +*/ + +fuse +unshield(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, HASSHIELD); + pstats.s_arm -= pstats.s_acmod; + pstats.s_acmod = 0; + msg("Your skin feels normal."); +} + +/* + unmshield() + He stops ignoring thrown weapons +*/ + +fuse +unmshield(fuse_arg *arg) +{ + NOOP(arg); + + turn_off(player, HASMSHIELD); + msg("The fog dissapates."); +} + +/* + untrue() + He lost his true sight power +*/ + +void +untruesee(fuse_arg *arg) +{ + NOOP(arg); + + if (!is_wearing(R_TRUESEE)) + { + turn_off(player, CANTRUESEE); + msg("Your sensory perceptions return to normal."); + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/dict.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/dict.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1160 @@ +/* + dict.c + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/****************** +** Change history: +** +** (AK:04/03/95) - In dict_create, initialize hook for extensions to dictionary structure. +** (AK:04/04/95) - In dict_insert, only set any_ptr when a new string entry is created. +** (AK:04/17/95) - Added dict_union and dict_merge. +** (AK:04/18/95) - In dict_create, added code to create signature, +** table of contents, and parameter array. +** (AK:04/18/95) - Revised dict_load, dict_save +** (AK:04/18/95) - Added dict_import +** +******************/ + +static char sccsid[] = "%W% %G%"; + +#include +#include +#if !defined(OS2) && !defined(_WIN32) + #include +#endif + +#include "dict.h" +#include "dictutil.h" + +#define BUFLEN 300 + +/************************************************************************* +* Generic dictionary and hash table functions for word and +* string storage. +* +* Implements generic dictionary management by using a +* hash table with direct chaining. All terms are stored in a +* separate, unsorted, string table. When inserting into the +* hash table exceeds the current size, allocate a new hash table +* and rehash the old contents into the new table. When the string +* table fills, append more empty entries onto the old table and +* continue adding. Inserting a string that already exists increments +* the count for the string. Deleting a string only decrements the +* count to no less than 0. This will allow a 0 occurence string to be +* retrieved. To remove the 0 occurence string table entries, you must +* rebuild the dictionary. Merging from one dictionary to a new one will +* do this because it only copies nonzero occurences. +* +* Assumptions: +* unsigned long >= 32 bits +* int >= 16 bits +* char == 8 bits +* number of entries < 2^28 +*************************************************************************/ + +/************************************************************************* +* hash_string: calculate hash value for string, modified +* version of hashpjw() from the new Dragon book. +* +* s - string to calculate hash for +* +* Returns: hash value +*************************************************************************/ +static unsigned long hash_string(const char *s) +{ + unsigned long h = 0; + unsigned long g = 0; + + while (*s) { + h <<= 4; + h += *s++; + if ((g = h & 0xf0000000) != 0) + h ^= g >> 24; + } + return h ^ g; +} + +/************************************************************************* +* dict_create: create a dictionary and initialize its structures +* +* toc_size - number of entries in table of contents ( >= 4 ) +* initial_string_count - number of strings to hold as initial allocation +* initial_hash_chains - size of hash table, must be power of 4 +* max_chain_length - max number of elements in hash chain before signalling growth +* +* Returns: pointer to dictionary +* NULL - error creating dictionary +* non-NULL - sucessful creation +*************************************************************************/ + +DICTIONARY *dict_create( + const long toc_size, + const long initial_string_count, + const long initial_hash_chains, + const long max_chain_length ) +{ + DICTIONARY *dtemp = NULL; + STRING_ENTRY *stemp = NULL; + long *ltemp = NULL; + char *sltemp; + long i, j, tocsize; + + /* check for a power of 4 in number of initial hash entries */ + switch(initial_hash_chains) { + case 0x00000004: + case 0x00000010: + case 0x00000040: + case 0x00000100: + case 0x00000400: + case 0x00001000: + case 0x00004000: + case 0x00010000: + case 0x00040000: + case 0x00100000: + case 0x00400000: + case 0x01000000: + case 0x04000000: + break; + default: + return NULL; + } + + /* Allocate the dictionary structure */ + if ((dtemp = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) + goto err_exit; + + /* string count has to be within range */ + j = initial_string_count; + if (j > 0x04000000) + return NULL; + + /* force a reasonable value */ + if (j < 100) + j = 100; + + /* Allocate the string table and string array */ + if ((stemp = (STRING_ENTRY *)malloc(sizeof(STRING_ENTRY) * j)) == NULL) + goto err_exit; + if ((sltemp = (char *)malloc(8 * j)) == NULL) + goto err_exit; + + /* Allocate the hash table */ + if ((ltemp = (long *)malloc(sizeof(long) * initial_hash_chains)) == NULL) + goto err_exit; + + /* Allocate the parameter array */ + if ( (dtemp->parm = (DICT_PARM_ENTRY *)malloc(14*sizeof(DICT_PARM_ENTRY))) == NULL) + goto err_exit; + + /* Allocate the signature structure */ + if ( (dtemp->sig=(DICT_SIG*)malloc(sizeof(DICT_SIG))) == NULL ) + goto err_exit; + + /* Allocate the Table of Contents */ + tocsize = toc_size; + if ( tocsize < 4 ) + tocsize = 4; + dtemp->sig->toc_size = tocsize; + if ( (dtemp->toc=(DICT_TOC_ENTRY*)malloc(dtemp->sig->toc_size*sizeof(DICT_TOC_ENTRY))) == NULL ) + goto err_exit; + + dtemp->check_value = DICT_VALIDATE; /* validation value */ + dtemp->flags = DICT_NONE; /* no flags set */ + dtemp->entry_count = 0; /* nothing in either table */ + + dtemp->string_table = stemp; /* connect string table */ + dtemp->string_max = j; /* size of string table */ + dtemp->scan_string_index = DICT_ENTRY_NONE; /* not pointing to anything in scan */ + dtemp->string_growth_count = 0; /* haven't grown any */ + + dtemp->string_array = sltemp; /* character array for strings */ + dtemp->array_size = j * 8; /* how many bytes available */ + dtemp->array_used = 0; /* nothing used yet */ + dtemp->array_growth_count = 0; /* haven't grown any */ + + /* force maximum hash chain length to a reasonable value */ + i = max_chain_length; + if (i < 1 || i > 200) + i = 200; + dtemp->chains = ltemp; /* connect hash table */ + dtemp->longest_chain_length = 0; /* nothing in table yet */ + dtemp->allowable_chain_length = i; /* chain length limit */ + dtemp->table_size = initial_hash_chains; /* size of hash table */ + dtemp->hash_mask = initial_hash_chains - 1; /* mask for mod() function */ + dtemp->hash_growth_count = 0; /* haven't grown any */ + + /* string table is empty */ + for (i = 0 ; i < j ; i++) { + dtemp->string_table[i].string_offset = DICT_ENTRY_NONE; + dtemp->string_table[i].count = 0; + dtemp->string_table[i].next = DICT_ENTRY_NONE; + dtemp->string_table[i].flags = 0; + dtemp->string_table[i].hash_value = 0; + } + + /* no entries chained */ + for (i = 0; i < initial_hash_chains; i++) + dtemp->chains[i] = DICT_ENTRY_NONE; + + /* Initialize hook to extended dictionary structure */ + dtemp->ext = NULL; + + /* Set up the parameter array */ + if ( dict_set_parm_ids(dtemp) == FALSE ) + goto err_exit; + if ( dict_set_parm_values(dtemp) == FALSE ) + goto err_exit; + + /* Set up the Table of Contents */ + strcpy( dtemp->toc[0].id , "PARM" ); + dtemp->toc[0].size = dtemp->sig->nparms * sizeof(DICT_PARM_ENTRY); + dtemp->toc[0].ptr = dtemp->parm; + dtemp->toc[0].type = 0; + + strcpy( dtemp->toc[1].id , "HASH" ); + dtemp->toc[1].size = dtemp->table_size * sizeof(long); + dtemp->toc[1].ptr = dtemp->chains; + dtemp->toc[1].type = 0; + + strcpy( dtemp->toc[2].id , "STTB" ); + dtemp->toc[2].size = dtemp->string_max * sizeof(char); + dtemp->toc[2].ptr = dtemp->string_table; + dtemp->toc[2].type = 0; + + strcpy( dtemp->toc[3].id , "STAR" ); + dtemp->toc[3].size = dtemp->array_size * sizeof(STRING_ENTRY); + dtemp->toc[3].ptr = dtemp->string_array; + dtemp->toc[3].type = 0; + + /* Set up the signature */ + dtemp->sig->check_value = DICT_VALIDATE; + dtemp->sig->toc_size = tocsize; + dtemp->sig->nparms = 14; + + return dtemp; + + /* error exit - saves duplicate code */ +err_exit: + + dict_destroy( dtemp ); + return NULL; +} + +/************************************************************************* +* dict_destroy: discard a dictionary and its contents +* multiple calls to destroy the same dictionary is safe +* +* dict - pointer to a dictionary to discard +* +* Returns: status code +* TRUE - dictionary destroyed +* FALSE - error when destroying dictionary +* Note: Does free the dictionary structure itself. +*************************************************************************/ +BOOLEANC dict_destroy(DICTIONARY *dict) +{ + int i; + + /* have to point to something */ + if (dict == NULL) + return FALSE; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return FALSE; + + if ( (dict->sig==NULL) || (dict->toc==NULL) || (dict->parm==NULL) ) + return FALSE; + + /* Free the type=0 tables */ + for ( i = 0 ; i < dict->sig->toc_size ; i++ ) { + if ( dict->toc[i].ptr != NULL && dict->toc[i].type == 0 ) + free( dict->toc[i].ptr ); + } /* endfor */ + + /* Free the Table of Contents and signature */ + free( dict->toc ); + free( dict->sig ); + + /* Free the dictionary structure itself */ + free( dict ); + + return TRUE; +} + +/************************************************************************* +* dict_insert: add entries into the dictionary, growing as necessary +* +* dict - pointer to dictionary to insert into +* s - string to insert +* count - count of occurences of string +* flags - flag bits associated with the string +* number - pointer to long to return word number +* +* Returns: pointer to new entry in dictionary +* NULL - error during insert +* non-NULL - pointer to inserted or updated entry +* +* Notes: if the entry already exists, increment the count. +* If NULL is returned, the dictionary state can no longer +* be trusted. Terminating with an error code would be +* safest. +*************************************************************************/ +STRING_ENTRY *dict_insert(DICTIONARY *dict, char *s, + const long occurences, const unsigned long flags, + void *any_ptr, long *number) +{ + unsigned long hash_value; + long hash_index; + long string_index = -1; + long he2; + int chain_len; + + /* have to point to something */ + if (dict == NULL) + return FALSE; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return FALSE; + + /* must have a string */ + if (s == NULL) { + *number = -1; + return FALSE; + } + + /* must be non-NULL */ + if (s[0] == '\0') { + *number = -1; + return FALSE; + } + + /* figure out which chain it should go into */ + hash_value = hash_string(s); + hash_index = hash_value & dict->hash_mask; + + /* look for the entry in the chain */ + for (he2 = dict->chains[hash_index], chain_len = 0; he2 != DICT_ENTRY_NONE; + he2 = dict->string_table[he2].next, chain_len++) { + char *sa = &dict->string_array[dict->string_table[he2].string_offset]; + + /* compare hash_value and then string, in that order, for speed */ + if (dict->string_table[he2].hash_value == hash_value && !strcmp(s, sa)) { + string_index = he2; + break; + } + } + + /* string wasn't found */ + if (string_index < 0) { + /* make sure there is room in string entry table */ + if (dict->entry_count + 1 > dict->string_max) { + /* make new table 20% bigger */ + dict->string_max = (dict->string_max * 6) / 5; + dict->string_table = (STRING_ENTRY *)realloc(dict->string_table, + sizeof(STRING_ENTRY) * dict->string_max); + if (dict->string_table == NULL) + return NULL; + dict->string_growth_count++; + } + string_index = dict->entry_count; + dict->entry_count++; + + /* make sure there is room in the string array */ + if (dict->array_used + (long)strlen(s) + 1 > dict->array_size) { + dict->array_size = (dict->array_size * 6) / 5; + dict->string_array = (char *) realloc(dict->string_array, + dict->array_size); + if (dict->string_array == NULL) + return NULL; + } + + /* fill in starting values */ + strcpy(&dict->string_array[dict->array_used], s); + dict->string_table[string_index].string_offset = dict->array_used; + dict->array_used += (long) strlen(s) + 1; + dict->string_table[string_index].count = 0 ; + dict->string_table[string_index].flags = DICT_NONE; + dict->string_table[string_index].any_ptr = any_ptr; /* (AK:04/04/95) */ + dict->string_table[string_index].hash_value = hash_value; + + /* hook entry at beginning of chain */ + dict->string_table[string_index].next = dict->chains[hash_index]; + dict->chains[hash_index] = string_index; + + /* record chain lengths */ + chain_len++; + if (chain_len > dict->longest_chain_length) + dict->longest_chain_length = chain_len; + + /* if a chain is too long */ + if (chain_len > dict->allowable_chain_length) { + long new_size = dict->table_size * 4; + long new_mask = new_size - 1; + long *hetemp; + long i; + int longest_chain; + + if ((hetemp = (long *)malloc(sizeof(long) * new_size)) == NULL) + return NULL; + + /* hash table chains are empty */ + for (i = 0; i < new_size; i++) + hetemp[i] = DICT_ENTRY_NONE; + + /* reset all chains */ + for (i = 0; i < dict->entry_count; i++) + dict->string_table[i].next = DICT_ENTRY_NONE; + + /* recreate hash table entries by reinserting all strings */ + for (i = 0; i < dict->entry_count; i++) { + long he = dict->string_table[i].hash_value & new_mask; + + dict->string_table[i].next = hetemp[he]; + hetemp[he] = i; + } + + /* find longest chain length */ + for (i = 0, longest_chain = 0; i < new_size; i++) { + int len; + long cur; + + for (cur = hetemp[i], len = 0; cur != DICT_ENTRY_NONE; + cur = dict->string_table[cur].next) + len++; + ; + if (len > longest_chain) + longest_chain = len; + } + + /* delete old table and attach new one */ + free(dict->chains); + dict->chains = hetemp; + dict->longest_chain_length = longest_chain; + dict->table_size = new_size; + dict->hash_mask = new_mask; + + /* keep track of growth */ + dict->hash_growth_count++; + } + } + + dict->string_table[string_index].count += occurences; + dict->string_table[string_index].flags |= flags; + *number = string_index; + + return &dict->string_table[string_index]; +} + +/************************************************************************* +* dict_delete: deletes an entry from the dictionary +* (Actually, only decrements the entry's count) +* +* dict - pointer to dictionary to delete +* s - string to find and delete +* count - count to decrement entry by +* +* Returns: status code +* TRUE - entry has been deleted +* FALSE - entry wasn't found or error occured +*************************************************************************/ +BOOLEANC dict_delete(const DICTIONARY *dict, const char *s, const long count) +{ + STRING_ENTRY *se; + long n; + + /* find the string */ + if ((se = dict_search(dict, s, &n)) == NULL) + return FALSE; + + /* decrement count and make sure it stays valid */ + se->count -= count; + if (se->count < 0) + se->count = 0; + return TRUE; +} + +/************************************************************************* +* dict_search: look for entries in the dictionary +* +* dict - pointer to dictionary to search +* s - string to search for +* number - pointer to long to return string number +* +* Returns: pointer to string entry +* NULL - entry not found +* non-NULL - pointer to string entry +*************************************************************************/ +STRING_ENTRY *dict_search(const DICTIONARY *dict, const char *s, + long *number) +{ + unsigned long hash_value; + long hash_index; + long string_index = -1; + long he; + + /* have to point to something */ + if (dict == NULL) + return NULL; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return NULL; + + /* must have a string */ + if (s == NULL) { + *number = -1; + return NULL; + } + + /* must be non-NULL */ + if (s[0] == '\0') { + *number = -1; + return FALSE; + } + + /* figure out which chain it should be in */ + hash_value = hash_string(s); + hash_index = hash_value & dict->hash_mask; + + /* look for the entry in the chain */ + for (he = dict->chains[hash_index]; he != DICT_ENTRY_NONE; + he = dict->string_table[he].next) { + char *sa = (char*)(&dict->string_array[dict->string_table[he].string_offset]); + + /* compare hash_value and then string, in that order, for speed */ + if (dict->string_table[he].hash_value == hash_value && !strcmp(s, sa)) { + string_index = he; + break; + } + } + if (string_index < 0) { + *number = -1; + return NULL; + } + + *number = string_index; + return dict->string_table+string_index; +} + +/************************************************************************* +* dict_union: merges contents of 2 dictionaries into +* a third +* +* dict1 - pointer to dictionary 1 +* dict2 - pointer to dictionary 2 +* +* Returns: dictionary pointer +* NULL - failed to merge +* non-NULL - pointer to new dictionary +* +* Notes: entries of the same string have their counts +* added and their flags ORed together. +*************************************************************************/ +DICTIONARY *dict_union( const DICTIONARY *dict1 , const DICTIONARY *dict2 ) +{ + DICTIONARY *dict = NULL; + STRING_ENTRY *se, *se2; + long initial_string_count, initial_hash_chains, max_chain_length; + long i, string_index; + + /*********** + ** Initialize the new dictionary. + ***********/ + + if ( dict1==NULL || dict2==NULL ) + goto err_exit; + if ((dict=(DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) + goto err_exit; + + initial_string_count = dict1->string_max; + initial_hash_chains = dict1->table_size; + max_chain_length = dict1->allowable_chain_length; + dict = dict_create( 4, + initial_string_count, + initial_hash_chains, + max_chain_length ); + + /*********** + ** Copy the entries from dict1 into the new dictionary. + ***********/ + + for ( i = 0 ; i < dict1->entry_count ; i++ ) { + se = (STRING_ENTRY*)&dict1->string_table[i]; + if ( se->count > 0 ) { + se2 = dict_insert( + dict, + dict1->string_array+se->string_offset, + se->count, + se->flags, + NULL, + &string_index ); + if ( se2 == NULL ) + goto err_exit; + } /* endif */ + } /* endfor */ + + /* Merge the entries from dict2 into the new dictionary. */ + if ( dict_merge(dict,dict2,FALSE) == FALSE ) + goto err_exit; + + /* Success. Return a pointer to the new dictionary. */ + return( dict ); + + /* Failure. Ignominiously erase our tracks and return NULL. */ +err_exit: + dict_destroy( dict ); + return NULL; +} + +/************************************************************************* +* dict_merge: merges the contents of a dictionary into +* another one, updating the contents of the destination +* +* dst - dictionary to update +* src - dictionary to add +* move - boolean flag +* TRUE - move to dest +* FALSE - copy to dest +* +* Returns: status code +* TRUE - merge completed +* FALSE - error on merge +* +* Notes: entries of the same string have their counts +* added. At the end of a move, src is empty. If there +* is an error during a move, the contents of both +* dictionaries cannot be trusted. +*************************************************************************/ +BOOLEANC dict_merge(const DICTIONARY *dst, const DICTIONARY *src, const BOOLEANC move) +{ + STRING_ENTRY *se, *se2; + DICTIONARY *dict1, *dict2; + long i, string_index, index; + + dict1 = (DICTIONARY*)src; + dict2 = (DICTIONARY*)dst; + + /*********** + ** Copy the dictionary entries into the new dictionary. + ***********/ + + for ( i = 0 ; i < dict1->entry_count ; i++ ) { + se = (STRING_ENTRY*)&dict1->string_table[i]; + if ( se->count > 0 ) { + se2 = dict_insert( + dict2, + dict1->string_array+se->string_offset, + se->count, + se->flags, + NULL, + &string_index ); + if ( se2 == NULL ) + goto err_exit; + } /* endif */ + } /* endfor */ + + /*********** + ** Set up the dictionary parameter vector. + ***********/ + + if ( dict_set_parm_values(dict2) == FALSE ) + return( FALSE ); + + /*********** + ** Update the table of contents. + ** PARM HASH STTB STAR + ***********/ + + if ( (index=dict_toc_index(dict2,"HASH")) == -1) + goto err_exit; + dict2->toc[index].size = dict2->table_size * sizeof(long); + dict2->toc[index].ptr = dict2->chains; + + if ( (index=dict_toc_index(dict2,"STTB")) == -1) + goto err_exit; + dict2->toc[index].size = dict2->string_max * sizeof(char); + dict2->toc[index].ptr = dict2->string_table; + + if ( (index=dict_toc_index(dict2,"STAR")) == -1) + goto err_exit; + dict2->toc[index].size = dict2->array_size * sizeof(STRING_ENTRY); + dict2->toc[index].ptr = dict2->string_array; + + /*********** + ** Update the signature + ***********/ + + dict2->sig->checksum = + compute_checksum( 4*sizeof(DICT_TOC_ENTRY) , (char*)(dict2->toc) ); + + /*********** + ** If this is a move, destroy the source dictionary + ***********/ + + if ( move == TRUE ) + if ( dict_destroy(dict1) == FALSE ) + goto err_exit; + + /*********** + ** Success + ***********/ + + return( TRUE ); + + /*********** + ** Failure + ***********/ + +err_exit: + dict_destroy( dict2 ); + return FALSE; +} + +/************************************************************************* +* dict_string_by_number: return string pointer for +* a given string number +* +* number - string number +* +* Returns: pointer to string entry +* NULL - entry not found +* non-NULL - pointer to string entry +*************************************************************************/ +STRING_ENTRY *dict_string_by_number(const DICTIONARY *dict, const long number) +{ + if (dict == NULL) + return NULL; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return NULL; + + /* string number has to be within range */ + if (number < 0 || number >= dict->entry_count) + return NULL; + + return dict->string_table+number; +} + +/************************************************************************* +* dict_scan_begin: begin a scan of the dictionary +* +* dict - pointer to dictionary to scan +* +* Returns: status code +* TRUE - scan initialized +* FALSE - error +* +* Notes: if a scan is already in progress, it is +* abandoned and the scan is reset to the +* beginning. +*************************************************************************/ +BOOLEANC dict_scan_begin(DICTIONARY *dict) +{ + /* have to point to something */ + if (dict == NULL) + return FALSE; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return FALSE; + + /* point to first entry in string table */ + dict->scan_string_index = 0; + return TRUE; +} + +/************************************************************************* +* dict_scan_next: get the next entry in a scan +* +* dict - pointer to dictionary to continue scanning +* +* Returns: pointer to string entry +* NULL - no more entries +* non-NULL - next string entry +*************************************************************************/ +STRING_ENTRY *dict_scan_next(DICTIONARY *dict) +{ + /* have to point to something */ + if (dict == NULL) + return NULL; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + return NULL; + + /* scan index has to be within range */ + if (dict->scan_string_index < 0 + || dict->scan_string_index > dict->entry_count) + return NULL; + + /* for first non-empty table entry */ + while (dict->scan_string_index < dict->entry_count + && dict->string_table[dict->scan_string_index].count == 0) + dict->scan_string_index++; + + /* past end of table? */ + if (dict->scan_string_index >= dict->entry_count) + return NULL; + + return &dict->string_table[dict->scan_string_index++]; +} + + +/************************************************************************* +* dict_load - load a compiled dictionary into memory +* creates a new dictionary +* +* fname - fully qualified file name +* +* Returns: pointer to created dictionary structure +* (NULL on failure) +*************************************************************************/ +DICTIONARY *dict_load(const char *fname) +{ + DICTIONARY *dict = NULL; + int code, index; + FILE *fi; + int ntoc; + + if ( (fi=fopen((char*)fname,"rb")) == NULL ) { + signal_error( "dict_load: could not open file" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + if ((dict = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL) { + /* signal_error( "dict_load: alloc failed" , "" , 0 ); */ + goto err_exit; + } /* endif */ + + /* Read the dictionary signature record */ + if ((dict->sig = (DICT_SIG *)malloc(sizeof(DICT_SIG))) == NULL) { + goto err_exit; + } /* endif */ + code = block_read( fi , (char*)(dict->sig) , sizeof(DICT_SIG) , 0 ); + if ( code == -1 ) { + signal_error( "dict_load: could not read signature" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + if ( dict->sig->check_value != DICT_VALIDATE ) { + signal_error( "dict_load: could not validate file" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + dict->check_value = dict->sig->check_value; + + /* Read the dictionary Table of Contents */ + ntoc = dict->sig->toc_size; + dict->toc = (DICT_TOC_ENTRY *) malloc( ntoc * sizeof(DICT_TOC_ENTRY) ); + if ( dict->toc == NULL ) { + signal_error( "dict_load: alloc of TOC failed" , "" , 0 ); + goto err_exit; + } /* endif */ + code = block_read( fi , + (char*)(dict->toc) , + ntoc * sizeof(DICT_TOC_ENTRY) , + sizeof(DICT_SIG) ); + if ( code == -1 ) { + signal_error( "dict_load: could not read Table of Contents" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Read the dictionary parameters */ + dict->parm = (DICT_PARM_ENTRY *) + dict_load_block( dict , "PARM" , fi , NULL ); + if ( dict->parm == NULL ) { + signal_error( "dict_load: could not load parameter table" , "" , 0 ); + goto err_exit; + } /* endif */ + index = dict_toc_index( dict , "PARM" ); + dict->sig->nparms = dict->toc[index].size / sizeof(DICT_PARM_ENTRY); + + /* Set the parameter values in the dictionary structure */ + if ( dict_set_parm_variables(dict) == FALSE ) + goto err_exit; + + /* Load the string array */ + dict->string_array = (char *) + dict_load_block( dict , "STAR" , fi , NULL ); + if ( dict->string_array == NULL ) { + signal_error( "dict_load: could not load string array" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Load the string table */ + dict->string_table = (STRING_ENTRY *) + dict_load_block( dict , "STTB" , fi , NULL ); + if ( dict->string_table == NULL ) { + signal_error( "dict_load: could not load string table" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Load the hash table */ + dict->chains = (long *) + dict_load_block( dict , "HASH" , fi , NULL ); + if ( dict->chains == NULL ) { + signal_error( "dict_load: could not load hash table" , (char*)fname , 0 ); + goto err_exit; + } /* endif */ + + /* Initialize the hook for dictionary extensions */ + dict->ext = NULL; + + /* Success */ + fclose( fi ); + return( dict ); + + /* Failure */ +err_exit: + if ( fi != NULL ) + fclose( fi ); + dict_destroy( dict ); + return NULL; +} + + +/************************************************************************* +* dict_save - save a dictionary from memory into a file +* +* dict - pointer to dictionary to save +* fname - full qualified file name prefix of dictionary +* +* Returns: status code +* TRUE - dictionary was saved sucessfully +* FALSE - error during save +*************************************************************************/ +BOOLEANC dict_save( DICTIONARY *dict, const char *fname ) +{ + int index, ret_code; + FILE *fo = NULL; + + /* Have to be pointing at a valid dictionary */ + if ( dict == NULL || dict->sig->check_value != DICT_VALIDATE ) + goto err_exit; + + /* Open the file for output */ + if ( (fo=fopen((char*)fname,"wb")) == NULL ) + goto err_exit; + + /* Make the table of contents entries current */ + /* Note: This will not be necessary once the data is stored in EVECTORs */ + + if ( (index=dict_toc_index(dict,"PARM")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->sig->nparms * sizeof(DICT_PARM_ENTRY); + dict->toc[index].ptr = dict->parm; + + if ( (index=dict_toc_index(dict,"STAR")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->array_size * sizeof(char); + dict->toc[index].ptr = dict->string_array; + + if ( (index=dict_toc_index(dict,"STTB")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->string_max * sizeof(STRING_ENTRY); + dict->toc[index].ptr = dict->string_table; + + if ( (index=dict_toc_index(dict,"HASH")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->table_size * sizeof(long); + dict->toc[index].ptr = dict->chains; + + /* Reset the TOC offsets and checksums for ALL tables */ + /* (not just type=0 tables) */ + dict_reset_toc_offsets( dict ); + + /* Set the dictionary parm structure from the parameter values */ + if ( dict_set_parm_values(dict) == FALSE ) + goto err_exit; + + /* Save the signature */ + dict->sig->checksum = compute_checksum( sizeof(DICT_SIG) , (char*)(dict->sig) ); + ret_code = block_write( fo , + (char*)dict->sig , + sizeof(DICT_SIG) ); + if ( ret_code == -1 ) + goto err_exit; + + /* Save the table of contents */ + ret_code = block_write( fo, + (char*)dict->toc, + dict->sig->toc_size * sizeof(DICT_TOC_ENTRY) ); + if ( ret_code == -1 ) + goto err_exit; + + /* Save the tables */ + /* For now, only save type=0 tables */ + for ( index = 0 ; index < dict->sig->toc_size ; index++ ) { + if ( dict->toc[index].type == 0 ) { /* Ordinary table */ + ret_code = dict_save_block( dict , dict->toc[index].id , fo ); + if ( ret_code == FALSE ) + goto err_exit; + } /* endif */ + } /* endfor */ + + /* Success */ + fclose( fo ); + return TRUE; + + /* Failure */ +err_exit: + if ( fo != NULL ) + fclose( fo ); + return FALSE; +} + + +/************************************************************************* +* dict_import: read in an ASCII dictionary. +* +* dict_fname - name of dictionary file +* parameters to create a DICTIONARY structure (see dict_create) +* +* Returns: pointer to created DICTIONARY structure +* (NULL on failure) +* +*************************************************************************/ +DICTIONARY *dict_import( const char *dict_fname , + const long initial_string_count , + const long initial_hash_entries , + const long max_chain_length ) +{ + DICTIONARY *dict; + char buffer[BUFLEN], ch; + int index, c, c0; + long number; + FILE *fi = NULL; + + /*********** + ** Dictionary setup. + ***********/ + + dict = dict_create( 4, + initial_string_count , + initial_hash_entries , + max_chain_length ); + if ( dict == NULL ) + goto err_exit; + + /*********** + ** Read the dictionary file + ** Each line should have one word or a string delimited by '|' + ***********/ + + if ( (fi=fopen(dict_fname,"r")) == NULL ) + goto err_exit; + while( fgets(buffer,BUFLEN,fi) != NULL ) { + c0 = 0; + /* Skip to non-blank */ + while ( (c0toc[index].size = dict->table_size * sizeof(long); + + if ( (index=dict_toc_index(dict,"STTB")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->string_max * sizeof(char); + + if ( (index=dict_toc_index(dict,"STAR")) == -1 ) + goto err_exit; + dict->toc[index].size = dict->array_size * sizeof(STRING_ENTRY); + + /* Success. Return a pointer to the new dictionary. */ + fclose(fi); + return( dict ); + + /* Failure. Ignominiously erase our tracks and return NULL. */ +err_exit: + if ( fi != NULL ) + fclose(fi); + dict_destroy( dict ); + return NULL; +} + +/************************************************************************* +* dict_export - save an extended dictionary from memory into +* an ASCII file +* +* dict - pointer to dictionary to save +* fname - full qualified file name prefix of dictionary +* +* Returns: status code +* TRUE - dictionary was saved sucessfully +* FALSE - error during save +* +*************************************************************************/ +BOOLEANC dict_export( DICTIONARY *dict , const char *fname ) +{ + FILE *fp = NULL; + STRING_ENTRY *se; + int i; + + /* have to point to something */ + if (dict == NULL) + goto err_exit; + + /* check value has to be OK */ + if (dict->check_value != DICT_VALIDATE) + goto err_exit; + + /* must have a filename */ + if (fname == NULL) + goto err_exit; + + fp = fopen( (char*)fname , "w" ); + if ( fp == NULL ) + goto err_exit; + + for ( i = 0 ; i < dict->entry_count ; i++ ) { + se = &dict->string_table[i]; + fprintf( fp , "|%s|\n" , dict->string_array + se->string_offset ); + } /* endfor */ + fclose( fp ); + + /* Success. */ + fclose(fp); + return TRUE; + + /* Failure. */ +err_exit: + if ( fp != NULL ) + fclose(fp); + dict_destroy( dict ); + return FALSE; +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/dict.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/dict.h Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,216 @@ +/* + dict.h + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/*----------------- + Change history: + (AK:04/03/95) - Added hook for extensions to dictionary structure. + (AK:04/04/95) - Added dictionary signature, table of contents and parameter + structure defintions and fields in DICTIONARY structure +-------------------*/ + +#ifndef dict_h_included +#define dict_h_included + +static char dict_sccsid[] = "%W% %G%"; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define DICT_NONE 0x00000000 /* no flags set */ +#define DICT_VALIDATE 0xdeadbeef /* look for this in every dictionary structure */ +#define DICT_ENTRY_NONE -1 /* index of invalid entry */ + +typedef unsigned char BOOLEANC; + +/*------------------ + string table entry + ------------------*/ +typedef struct string_entry { + long string_offset; /* offset in string array */ + long count; /* number of occurrences of string */ + long next; /* offset of next in hash chain */ + void *any_ptr; /* free pointer */ + unsigned long flags; /* user definable flag value for string */ + unsigned long hash_value; /* hash value of string using hash function */ +} STRING_ENTRY; + +/*-------------------- + Dictionary signature (AK:04/04/95) + --------------------*/ +typedef struct dict_sig_ { /* Dictionary signature */ + unsigned long check_value; /* 0xdeadbeef */ + int toc_size; /* # of entries in Table of Contents */ + long nparms; /* # of parameters */ + unsigned long checksum; /* Checksum for TOC */ +} DICT_SIG; + +/*---------------------------------- + Dictionary table of contents entry (AK:04/04/95) + ----------------------------------*/ +typedef struct TOC_entry_ { /* Dictionary table of contents entry */ + char id[5]; /* Field identifier: */ + long offset; /* Offset (bytes) of entry in file */ + long size; /* Size (bytes) of entry */ + void *ptr; /* Where entry is stored in memory */ + unsigned long checksum; /* Checksum of entry */ + int type; /* 0=ordinary ; 1=EVECTOR ; 2=NULL */ +} DICT_TOC_ENTRY; + +/*-------------------------------- + Dictionary parameter table entry (AK:04/04/95) + --------------------------------*/ +typedef struct dict_parm_entry_ { /* Dictionary parameter table entry */ + char id[13]; /* Parameter identifier */ + unsigned long value; /* Parameter value */ +} DICT_PARM_ENTRY; + + +/*--------------------------- + Hash dictionary information + ---------------------------*/ +typedef struct dictionary { + unsigned long check_value; /* check validation value */ + unsigned long flags; /* flag values */ + long entry_count; /* number of used entries in each table */ + + char *string_array; /* storage for strings */ + long array_size; /* number of bytes allocated */ + long array_used; /* number of bytes occupied */ + int array_growth_count; /* number of times grown */ + + STRING_ENTRY *string_table; /* string table */ + long string_max; /* max number of entries in string table */ + long scan_string_index; /* current index into string table for scan */ + int string_growth_count; /* number of times had to grow string table */ + + long *chains; /* vector of array indices to hash entries */ + int longest_chain_length; /* longest chain length in hash table */ + int allowable_chain_length; /* chain lengths always < this */ + long table_size; /* number of elements in hash entries vector */ + unsigned long hash_mask; /* mask for doing mod() function */ + int hash_growth_count; /* number of times had to grow hash table */ + + void *ext; /* Hook for extensions to the dictionary (AK:04/03/95) */ + + DICT_SIG *sig; /* Signature (AK:04/04/95) */ + DICT_TOC_ENTRY *toc; /* Table of contents (AK:04/04/95) */ + DICT_PARM_ENTRY *parm; /* Parameters (AK:04/04/95) */ + +} DICTIONARY; + +/*-------------------------------------------------------------- + dict_create: create a dictionary and initialize its structures + --------------------------------------------------------------*/ +extern DICTIONARY *dict_create( + const long toc_size, + const long initial_string_count, + const long initial_hash_entries, + const long max_chain_length ); + +/*------------------------------------------------- + dict_delete: deletes an entry from the dictionary + -------------------------------------------------*/ +extern BOOLEANC dict_delete( + const DICTIONARY *dict, + const char *s, + const long count ); + +/*--------------------------------------------------- + dict_destroy: discard a dictionary and its contents + ---------------------------------------------------*/ +extern BOOLEANC dict_destroy( + DICTIONARY *dict ); + +/*------------------------------------------------ + dict_export: write a dictionary to an ASCII file + ------------------------------------------------*/ +extern BOOLEANC dict_export( + DICTIONARY *dict, + const char *fname ); + +/*------------------------------------------------- + dict_import: read a dictionary from an ASCII file + -------------------------------------------------*/ +extern DICTIONARY *dict_import( + const char *dict_fname, + const long initial_string_count, + const long initial_hash_entries, + const long max_chain_length ); + +/*------------------------------------------------------------------ + dict_insert: add entries into the dictionary, growing as necessary + ------------------------------------------------------------------*/ +extern STRING_ENTRY *dict_insert( + DICTIONARY *dict, + char *s, + const long occurences, + const unsigned long flags, + void *any_ptr, + long *number ); + +/*---------------------------------------------------- + dict_load: read a dictionary from a file into memory + ----------------------------------------------------*/ +extern DICTIONARY *dict_load( + const char *fname ); + +/*----------------------------------------------------------------- + dict_merge: merges the contents of a dictionary into another one, + updating the contents of the destination + -----------------------------------------------------------------*/ +extern BOOLEANC dict_merge( + const DICTIONARY *dst, + const DICTIONARY *src, + const BOOLEANC move ); + +/*---------------------------------------------------- + dict_save: save a dictionary from memory into a file + ----------------------------------------------------*/ +extern BOOLEANC dict_save( + DICTIONARY *dict, + const char *fname ); + +/*----------------------------------------------- + dict_scan_begin: begin a scan of the dictionary + -----------------------------------------------*/ +extern BOOLEANC dict_scan_begin( + DICTIONARY *dict ); + +/*-------------------------------------------- + dict_scan_next: get the next entry in a scan + --------------------------------------------*/ +extern STRING_ENTRY *dict_scan_next( + DICTIONARY *dict ); + +/*----------------------------------------------- + dict_search: look for entries in the dictionary + -----------------------------------------------*/ +extern STRING_ENTRY *dict_search( + const DICTIONARY *dict, + const char *s, + long *number ); + +/*---------------------------------------------------------------------- + dict_string_by_number: return string pointer for a given string number + ----------------------------------------------------------------------*/ +extern STRING_ENTRY *dict_string_by_number( + const DICTIONARY *dict, + const long number ); + +/*---------------------------------------------------------- + dict_union: merges contents of 2 dictionaries into a third + ----------------------------------------------------------*/ +extern DICTIONARY *dict_union( + const DICTIONARY *dict1, + const DICTIONARY *dict2 ); + +#endif /* dict_h_included */ diff -r d9badb9c0179 -r c495a4f288c6 urogue/dictutil.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/dictutil.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,438 @@ +/* + dictutil.c + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/************************************************************************* +** Utilities for Dictionary Maintenence Functions +*************************************************************************/ + +static char sccsid[] = "%W% %G%"; + +#include +#include +#include +#if !defined(OS2) && !defined(_WIN32) + #include +#else + #include + #include +#endif + +#include "dict.h" +#include "dictutil.h" +#include "rogue.h" + +int trace; +FILE *ft; + + +/*********** +** Read 'count' characters into 'buffer' at 'offset' in a binary file +** Return 0 on success; -1 on failure; +***********/ + +int block_read( FILE *fi , char *buffer , size_t count , long offset ) +{ + if ( fseek(fi,offset,SEEK_SET) == -1 ) + return( -1 ); + + if ( fread(buffer,1,count,fi) != count ) + return( -1 ); + return( 0 ); +} + +/*********** +** Write 'count' characters from 'buffer' to a binary file. +** Return -1 on failure; 0 on success. +***********/ + +int block_write( FILE *fo , char *buffer , size_t count ) +{ + if ( fwrite(buffer,1,count,fo) != count ) + return( -1 ); + return( 0 ); +} + +/*********** +** Load a dictionary table entry with id TOC_id into memory pointed to by block. +** Update the dictionary TOC. +** If *block=NULL, allocate the block of memory. +** Return 0 on success; -1 on failure. +** Set dt_entry->ptr to where the block is stored. +***********/ + +void *dict_load_block( DICTIONARY *dict , char *toc_id , + FILE *fi , void *block ) +{ DICT_TOC_ENTRY *dt_entry; + static void *ptr; + int index, ret_code; + + index = dict_toc_index( dict , toc_id ); + if ( index != -1 ) { /* Found the id */ + dt_entry = &(dict->toc[index]); + } else { + signal_error( "dict_load_block: could not find TOC_id" , toc_id , 1 ); + return( NULL ); + } /* endif */ + + if ( block == NULL ) { + ptr = malloc( dt_entry->size ); + if ( trace > 3 ) { + fprintf( ft , "\ndict_load_block allocates %lx bytes at location %p\n" , + dt_entry->size , ptr ); + } /* endif */ + } else { + ptr = block; + if ( trace > 3 ) { + fprintf( ft , "\ndict_load_block uses memory at location %p\n" , ptr ); + } /* endif */ + } /* endif */ + if ( ptr == NULL ) { + signal_error( "dict_load_block: alloc failed " , toc_id , 1 ); + return( NULL ); + } /* endif */ + + ret_code = block_read( fi , + (char*)ptr , + dt_entry->size , + dt_entry->offset ); + if ( ret_code == -1 ) + return( NULL ); + + if ( dt_entry->checksum != + compute_checksum( dt_entry->size , (char*)ptr ) ) { + signal_error( "dict_load_block: invalid checksum ", toc_id, 1); + return( NULL ); + } /* endif */ + + dt_entry->ptr = ptr; + + if ( trace > 3 ) { + fprintf( ft , "\nLoaded block\nTOC entry: id:%s offset:%lx size:%lx ptr:%p checksum:%lx type:%d\n" , + dict->toc[index].id , dict->toc[index].offset , + dict->toc[index].size , dict->toc[index].ptr , + dict->toc[index].checksum , dict->toc[index].type ); + } /* endif */ + + return( ptr ); +} + +/*********** +** Save a dictionary table entry. +** Update the dictionary TOC entry offset and checksum fields. +** Return 0 on success, -1 on failure. +** Note: It is assumed that the size and pointer fields in TOC entry are +** already up to date; i.e., that they are consistent with the current +** location and size of the block being written. This is essential +** because the table of contents must have already been written +** into the file. +***********/ + +BOOLEANC dict_save_block( DICTIONARY *dict , char *toc_id , FILE *fo ) +{ DICT_TOC_ENTRY *dt_entry; + int index, ret_code; + char *block; + + index = dict_toc_index( dict , toc_id ); + if ( index == -1 ) { + signal_error( "dict_save_block: id not found " , toc_id , 1 ); + return( FALSE ); + } /* endif */ + dt_entry = &(dict->toc[index]); + block = (char*)(dt_entry->ptr); + + if ( block == NULL ) { + signal_error( "dict_save_block: NULL block " , toc_id , 1 ); + return( FALSE ); + } /* endif */ + + /* dt_entry->offset = fseek( fo , 0 , SEEK_END ); */ + dt_entry->checksum = compute_checksum( dt_entry->size , block ); + ret_code = block_write( fo , dt_entry->ptr , dt_entry->size ); + if ( ret_code == -1 ) { + signal_error( "dict_save_block: block_write failed " , toc_id , 1 ); + return( FALSE ); + } /* endif */ + + if ( trace > 3 ) { + fprintf( ft , "\nStored block\nTOC entry: id:%s offset:%lx size:%lx ptr:%p checksum:%lx type:%d\n" , + dict->toc[index].id , dict->toc[index].offset , + dict->toc[index].size , dict->toc[index].ptr , + dict->toc[index].checksum , dict->toc[index].type ); + } /* endif */ + + return( TRUE ); +} + +/*********** +** Look up and id in the table of contents. +** Return its index (-1 on failure). +***********/ + +int dict_toc_index( DICTIONARY *dict , char *toc_id ) +{ int index; + + for ( index = 0 ; index < dict->sig->toc_size ; index++ ) { + if ( strcmp(dict->toc[index].id,toc_id) == 0 ) + return( index ); + } /* endfor */ + + return( -1 ); +} + +/*********** +** Compute a block checksum. +** (Currently just returns 0.) +***********/ + +unsigned long compute_checksum( size_t size , char *block ) +{ + NOOP(size); + NOOP(block); + return( 0 ); +} + +/*********** +** Create a dictionary paramter entry. +***********/ + +DICT_PARM_ENTRY *dict_make_parm_entry( char *id , unsigned long value ) +{ static DICT_PARM_ENTRY *entry; + + entry = (DICT_PARM_ENTRY *) malloc( sizeof(DICT_PARM_ENTRY) ); + if ( entry == NULL ) + return(NULL); + + strncpy( entry->id , id , 13 ); + entry->value = value; + + return( entry ); +} + +/*********** +** Look up and id in the parameter array. +** Return its index (-1 on failure). +***********/ + +int dict_parm_index( DICTIONARY *dict , char *parm_id ) +{ long index; + + for ( index = 0 ; index < dict->sig->nparms ; index++ ) { + if ( strcmp( dict->parm[index].id , parm_id ) == 0 ) + return( (int) index ); + } /* endfor */ + + return( -1 ); +} + +/*********** +** Reset table of contents offsets and checksums +** in preparation for dict_save(). +***********/ + +BOOLEANC dict_reset_toc_offsets( DICTIONARY *dict ) +{ int i; + long offset; + + offset = sizeof(DICT_SIG) + + dict->sig->toc_size * sizeof(DICT_TOC_ENTRY); + for ( i = 0 ; i < dict->sig->toc_size ; i++ ) { + dict->toc[i].offset = offset; + offset += dict->toc[i].size; + dict->toc[i].checksum = + compute_checksum( dict->toc[i].size , dict->toc[i].ptr ); + } /* endfor */ + + return( TRUE ); +} + +/*********** +** Load the names of the dictionary parameters. +** 14 parms +***********/ + +BOOLEANC dict_set_parm_ids( DICTIONARY *dict ) +{ + if ( dict==NULL || dict->sig == NULL ) { + signal_error( "dict_set_parm_ids: Allocate dict and signature first." , "" , 0 ); + return( FALSE ); + } + dict->sig->nparms = 14; + strcpy( dict->parm[0].id , "FLAGS_______" ); + strcpy( dict->parm[1].id , "ENTRY_COUNT_" ); + strcpy( dict->parm[2].id , "ARRAY_SIZE__" ); + strcpy( dict->parm[3].id , "ARRAY_USED__" ); + strcpy( dict->parm[4].id , "ARR_GROW_CT_" ); + strcpy( dict->parm[5].id , "STRING_MAX__" ); + strcpy( dict->parm[6].id , "STR_GROW_CT_" ); + strcpy( dict->parm[7].id , "LONG_CHAIN__" ); + strcpy( dict->parm[8].id , "ALLOW_CHAIN_" ); + strcpy( dict->parm[9].id , "HASH_TAB_SIZ" ); + strcpy( dict->parm[10].id , "HASH_MASK___" ); + strcpy( dict->parm[11].id , "HASH_GROW_CT" ); + strcpy( dict->parm[12].id , "CHECK_VALUE_" ); + strcpy( dict->parm[13].id , "SCAN_STR_IX_" ); + + return( TRUE ); +} + +/*********** +** Set the dictionary parm structure from the values in the dict structure. +** 14 parms +***********/ + +BOOLEANC dict_set_parm_values( DICTIONARY *dict ) +{ int index; + + if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->flags; + + if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->entry_count; + + if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->array_size; + + if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->array_used; + + if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->array_growth_count; + + if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->string_max; + + if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->string_growth_count; + + if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->longest_chain_length; + + if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->allowable_chain_length; + + if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->table_size; + + if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->hash_mask; + + if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->hash_growth_count; + + if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->check_value; + + if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 ) + return( FALSE ); + dict->parm[index].value = (unsigned long)dict->scan_string_index; + + return( TRUE ); +} + + +/*********** +** Set the values in the dict structure from the dictionary parm structure. +** 14 parms +***********/ + +BOOLEANC dict_set_parm_variables( DICTIONARY *dict ) +{ int index; + + if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 ) + return( FALSE ); + dict->flags = (unsigned long)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 ) + return( FALSE ); + dict->entry_count = (long)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 ) + return( FALSE ); + dict->array_size = (long)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 ) + return( FALSE ); + dict->array_used = (long)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 ) + return( FALSE ); + dict->array_growth_count = (int)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 ) + return( FALSE ); + dict->string_max = (long)dict->parm[index].value ; + + if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 ) + return( FALSE ); + dict->string_growth_count = (int)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 ) + return( FALSE ); + dict->longest_chain_length = (int)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 ) + return( FALSE ); + dict->allowable_chain_length = (int)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 ) + return( FALSE ); + dict->table_size = (long)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 ) + return( FALSE ); + dict->hash_mask = (unsigned long)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 ) + return( FALSE ); + dict->hash_growth_count = (int)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 ) + return( FALSE ); + dict->check_value = (unsigned long)dict->parm[index].value; + + if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 ) + return( FALSE ); + dict->scan_string_index = (long)dict->parm[index].value; + + return( TRUE ); +} + +/*********** +** If trace (global) > 0 , signal an error +** If severity > 0 , abort +***********/ + +void signal_error( char *header , char *message , int severity ) +{ + FILE *fpe; + + if ( trace > 0 ) { + printf( "%s: %s\n" , header , message ); + fpe = fopen( "ERROR.FIL" , "a" ); + fprintf( fpe , "\n%s: %s\n" , header , message ); + fclose( fpe ); + } /* endif */ + + if ( severity > 0 ) + abort(); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/dictutil.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/dictutil.h Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,116 @@ +/* + dictutil.h + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/****************************************************************/ +/* HEADERS FOR DICTIONARY MAINTENENCE ROUTINE UTILITIES */ +/****************************************************************/ + +#ifndef dictutil_h_included +#define dictutil_h_included + +static char dictutil_sccsid[] = "%W% %G%"; + +#include +#include "dict.h" + +/*------------------------------- + Binary read of a block of bytes + -------------------------------*/ +extern int block_read( + FILE *fi, + char *buffer, + size_t count, + long offset ); + +/*-------------------------------- + Binary write of a block of bytes + --------------------------------*/ +extern int block_write( + FILE *fo, + char *buffer, + size_t count ); + +/*-------------------------------------- + Compute a checksum of a block of bytes + --------------------------------------*/ +extern unsigned long compute_checksum( + size_t size, + char *block ); + +/*----------------------------------------------------------------- + Load a block of bytes from a compiled dictionary file into memory + -----------------------------------------------------------------*/ +extern void *dict_load_block( + DICTIONARY *dict, + char *toc_id, + FILE *fi, + void *block ); + +/*----------------------------------- + Create a dictionary parameter entry + -----------------------------------*/ +extern DICT_PARM_ENTRY *dict_make_parm_entry( + char *id, + unsigned long value ); + +/*------------------------------------ + Look up an id in the parameter array + ------------------------------------*/ +extern int dict_parm_index( + DICTIONARY *dict, + char *parm_id ); + +/*------------------------------- + Reset table of contents offsets + -------------------------------*/ +extern BOOLEANC dict_reset_toc_offsets( + DICTIONARY *dict ); + +/*----------------------------------------------------------------- + Save a block of bytes from memory into a compiled dictionary file + -----------------------------------------------------------------*/ +extern BOOLEANC dict_save_block( + DICTIONARY *dict, + char *toc_id, + FILE *fo ); + +/*-------------------------------------------------------------------- + Set the dictionary parm values from the values in the dict structure + --------------------------------------------------------------------*/ +extern BOOLEANC dict_set_parm_values( + DICTIONARY *dict ); + +/*-------------------------------------------------------------------- + Set the values in the dict structure from the dictionary parm values + --------------------------------------------------------------------*/ +extern BOOLEANC dict_set_parm_variables( + DICTIONARY *dict ); + +/*--------------------------- + Set the dictionary parm ids + ---------------------------*/ +extern BOOLEANC dict_set_parm_ids( + DICTIONARY *dict ); + +/*-------------------------------------- + Look up an id in the table of contents + --------------------------------------*/ +extern int dict_toc_index( + DICTIONARY *dict, + char *toc_id ); + +/*------------------------------------ + Record and error and abort if needed + ------------------------------------*/ +extern void signal_error( + char *header, + char *message, + int severity ); +#endif diff -r d9badb9c0179 -r c495a4f288c6 urogue/encumb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/encumb.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,185 @@ +/* + encumb.c - Stuff to do with encumberance + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include "rogue.h" + +/* + updpack() + Update his pack weight and adjust fooduse accordingly +*/ + +void +updpack(void) +{ + int curcarry = packweight(); + + pstats.s_carry = totalenc(); /* update max encumb */ + + if (is_carrying(TR_PURSE)) + pstats.s_carry += 1000; + + foodlev = 0; + + switch ((curcarry * 5) / pstats.s_carry) /* % of total capacity */ + { + case 5: /* 100 % */ + foodlev++; + + case 4: /* 80 % */ + if (rnd(100) < 80) + foodlev++; + + case 3: /* 60 % */ + if (rnd(100) < 60) + foodlev++; + + case 2: /* 40 % */ + if (rnd(100) < 40) + foodlev++; + + case 1: /* 20 % */ + if (rnd(100) < 20) + foodlev++; + + case 0: /* 0 % */ + foodlev++; + } + + pstats.s_pack = curcarry; /* update pack weight */ + + if (is_carrying(TR_PURSE)) /* makes pack lighter */ + foodlev--; +} + + +/* + packweight() + Get the total weight of the hero's pack +*/ + +int +packweight(void) +{ + struct linked_list *pc; + int weight = 0; + + for (pc = pack; pc != NULL; pc = next(pc)) + { + struct object *obj = OBJPTR(pc); + + weight += itemweight(obj) * obj->o_count; + } + + if (weight < 0) /* caused by artifacts or blessed items */ + weight = 0; + + return (weight); +} + + +/* + itemweight() + Get the weight of an object +*/ + +int +itemweight(struct object *wh) +{ + int weight = wh->o_weight; /* get base weight */ + int ac; + + switch (wh->o_type) + { + case ARMOR: /* 10% for each plus or minus*/ + ac = armors[wh->o_which].a_class - wh->o_ac; + weight *= (10 - ac) / 10; + break; + + case WEAPON: + if ((wh->o_hplus + wh->o_dplus) > 0) + weight /= 2; + } + + if (wh->o_flags & ISCURSED) + weight += weight / 2; /* +50% for cursed */ + else if (wh->o_flags & ISBLESSED) + weight -= weight / 5; /* -20% for blessed */ + + if (weight < 0) + weight = 0; + + return (weight); +} + + +/* + playenc() + Get hero's carrying ability above norm 50 units per point of STR + over 10, 300 units per plus on R_CARRYING 1000 units for TR_PURSE +*/ + +int +playenc(void) +{ + int ret_val = (pstats.s_str - 10) * 50; + + if (is_wearing(R_CARRYING)) + ret_val += ring_value(R_CARRYING) * 300; + + return (ret_val); +} + + +/* + totalenc() + Get total weight that the hero can carry +*/ + +int +totalenc(void) +{ + int wtotal = 1400 + playenc(); + + switch (hungry_state) + { + case F_OK: + case F_HUNGRY: /* no change */ + break; + + case F_WEAK: + wtotal -= wtotal / 4; /* 25% off weak */ + break; + + case F_FAINT: + wtotal /= 2; /* 50% off faint */ + break; + } + + return (wtotal); +} + + +/* + hitweight() + Gets the fighting ability according to current weight This + returns a +2 hit for very light pack weight, +1 hit + for light pack weight, 0 hit for medium pack weight, -1 hit for heavy + pack weight, -2 hit for very heavy pack weight +*/ + +int +hitweight(void) +{ + return(3 - foodlev); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/fight.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/fight.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,2175 @@ +/* + fight.c - All the fighting gets done here + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +/* + * This are the beginning experience levels for all players all further + * experience levels are computed by multiplying by 2 + */ + +static long e_levels[10] = +{ + 143L, /* Fighter */ + 182L, /* Paladin */ + 169L, /* Ranger */ + 127L, /* Cleric */ + 154L, /* Druid */ + 185L, /* Magician */ + 169L, /* Illusionist */ + 112L, /* Thief */ + 126L, /* Assasin */ + 319L /* Ninja */ +}; + +static struct matrix att_mat[11] = +{ + /* Base, Max_lvl, Factor, Offset, Range */ + + { 10, 17, 2, 1, 2 }, /* fi */ + { 10, 17, 2, 1, 2 }, /* pa */ + { 10, 17, 2, 1, 2 }, /* ra */ + { 10, 19, 2, 1, 3 }, /* cl */ + { 10, 19, 2, 1, 3 }, /* dr */ + { 9, 21, 2, 1, 5 }, /* mu */ + { 9, 21, 2, 1, 5 }, /* il */ + { 10, 21, 2, 1, 4 }, /* th */ + { 10, 21, 2, 1, 4 }, /* as */ + { 10, 21, 2, 1, 4 }, /* nj */ + { 7, 25, 1, 0, 2 } /* mn */ +}; + +void +do_fight(coord dir, int tothedeath) +{ + int x,y; + + x = dir.x; + y = dir.y; + + if (!tothedeath && pstats.s_hpt < max_stats.s_hpt / 3) + { + msg("That's not wise."); + + after = fighting = FALSE; + return; + } + + if (isalpha(CCHAR(winat(hero.y + y, hero.x + x)))) + { + after = fighting = TRUE; + do_move(y, x); + } + else + { + if (fighting == FALSE) + msg("Nothing there."); + + after = fighting = FALSE; + } + + return; +} + +/* + fight() + The player attacks the monster. +*/ + +int +fight(coord *mp, struct object *weap, int thrown) +{ + struct thing *tp; + struct linked_list *item; + int did_hit = TRUE; + char *mname; + + /* Find the monster we want to fight */ + + if ((item = find_mons(mp->y, mp->x)) == NULL) + { + debug("Fight what @ %d,%d", mp->y, mp->x); + return 0; + } + + tp = THINGPTR(item); + + mname = (on(player, ISBLIND)) ? "it" : monsters[tp->t_index].m_name; + + /* Since we are fighting, things are not quiet so no healing takes place */ + + player.t_rest_hpt = player.t_rest_pow = 0; + tp->t_rest_hpt = tp->t_rest_pow = 0; + + /* Let him know it was really a mimic (if it was one). */ + + if (off(player, ISBLIND)) + { + if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise)) + { + msg("Wait! That's a %s!", mname); + turn_off(*tp, ISDISGUISE); + did_hit = thrown; + } + + if (on(*tp, CANSURPRISE)) + { + turn_off(*tp, CANSURPRISE); + if ((player.t_ctype == C_RANGER && rnd(6) != 0) || + (player.t_ctype == C_NINJA && rnd(pstats.s_lvl / 2) + != 0)) + msg("You notice a %s trying to hide!", mname); + else + { + msg("Wait! There's a %s!", mname); + did_hit = thrown; + } + } + } + + /* Protection from Normal Missiles */ + + if (thrown && on(*tp, HASMSHIELD)) + { + msg("The %s slows as it approaches %s.", + weaps[weap->o_which].w_name, mname); + + did_hit = FALSE; + } + + if (did_hit) + { + did_hit = FALSE; + + if (!can_blink(tp) && + (off(*tp, MAGICHIT) || (weap != NULL && + (weap->o_hplus > 0 || weap->o_dplus > 0))) && + (off(*tp, BMAGICHIT) || (weap != NULL && + (weap->o_hplus > 2 || weap->o_dplus > 2))) && + roll_em(&player, tp, weap, thrown, cur_weapon)) + { + did_hit = TRUE; + tp->t_wasshot = TRUE; + + if (thrown) + { + if (weap != NULL && weap->o_type == WEAPON + && weap->o_which == GRENADE) + { + hearmsg("BOOOM!"); + aggravate(); + } + + thunk(weap, mname); + } + else + hit(mname); + + /* hitting a friendly monster is curtains */ + + if (on(*tp, ISFRIENDLY)) + { + turn_off(*tp, ISFRIENDLY); + turn_on(*tp, ISMEAN); + } + + /* Charmed monsters become uncharmed */ + + if (on(*tp, ISCHARMED)) + { + turn_off(*tp, ISCHARMED); + turn_on(*tp, ISMEAN); + } + + /* + * If the player hit a rust monster, he better have a + * + weapon + */ + + if (on(*tp, CANRUST)) + { + if (!thrown && (weap != NULL) && + (weap->o_flags & ISMETAL) && + !(weap->o_flags & ISPROT) && + !(weap->o_flags & ISSILVER) && + (weap->o_hplus < 1) && (weap->o_dplus < 1)) + { + if (rnd(100) < 50) + weap->o_hplus--; + else + weap->o_dplus--; + + msg("Your %s weakens!", weaps[weap->o_which].w_name); + } + else if (!thrown && weap != NULL && (weap->o_flags & ISMETAL)) + msg("The rust vanishes from your %s!", + weaps[weap->o_which].w_name); + } + + /* flammable monsters die from burning weapons */ + + if (thrown && on(*tp, CANBBURN) && + (weap->o_flags & CANBURN) && + !save_throw(VS_WAND, tp)) + { + msg("The %s vanishes in a ball of flame.", + monsters[tp->t_index].m_name); + + tp->t_stats.s_hpt = 0; + } + + /* spores explode and infest hero */ + + if (on(*tp, CANSPORE)) + { + msg("The %s explodes in a cloud of dust.", + monsters[tp->t_index].m_name); + + if (is_wearing(R_HEALTH) || + player.t_ctype == C_PALADIN || + (player.t_ctype == C_NINJA && pstats.s_lvl + > 6) || + thrown && rnd(50) > 0 || + rnd(20) > 0) + { + msg("The dust makes it hard to breath."); + } + else + { + msg("You have contracted a parasitic infestation!"); + + infest_dam++; + turn_on(player, HASINFEST); + } + + tp->t_stats.s_hpt = 0; + } + + /* fireproof monsters laugh at you when burning weapon hits */ + + if (thrown && on(*tp, NOFIRE) && (weap->o_flags & CANBURN)) + msg("The %s laughs as the %s bounces.", + monsters[tp->t_index].m_name, + weaps[weap->o_which].w_name); + + /* sharp weapons have no effect on NOSHARP monsters */ + + if (on(*tp, NOSHARP) && (weap != NULL) && + (weap->o_flags & ISSHARP)) + { + msg("The %s has no effect on the %s!", + weaps[weap->o_which].w_name, + monsters[tp->t_index].m_name); + + fighting = FALSE; + } + + /* metal weapons pass through NOMETAL monsters */ + + if (on(*tp, NOMETAL) && (weap != NULL) && + (weap->o_flags & ISMETAL)) + { + msg("The %s passes through the %s!", + weaps[weap->o_which].w_name, + monsters[tp->t_index].m_name); + + fighting = FALSE; + } + + /* + * If the player hit something that shrieks, wake the + * dungeon + */ + + if (on(*tp, CANSHRIEK)) + { + turn_off(*tp, CANSHRIEK); + + if (on(player, CANHEAR)) + { + msg("You are stunned by the %s's shriek.", mname); + no_command += 4 + rnd(8); + } + else if (off(player, ISDEAF)) + msg("The %s emits a piercing shriek.", mname); + else + msg("The %s seems to be trying to make some noise.", mname); + + aggravate(); + + if (rnd(wizard ? 3 : 39) == 0 && cur_armor + != NULL + && cur_armor->o_which == CRYSTAL_ARMOR) + { + struct linked_list *itm; + struct object *obj; + + for (itm = pack; itm != NULL; itm = next(itm)) + { + obj = OBJPTR(itm); + + if (obj == cur_armor) + break; + } + + if (itm == NULL) + debug("Can't find crystalline armor being worn."); + else + { + msg("Your armor shatters from the shriek."); + cur_armor = NULL; + del_pack(itm); + } + } + } + + /* + * If the player hit something that can surprise, it + * can't now + */ + + if (on(*tp, CANSURPRISE)) + turn_off(*tp, CANSURPRISE); + + /* + * If the player hit something that can summon, it + * will try to + */ + + summon_help(tp, NOFORCE); + + /* Can the player confuse? */ + + if (on(player, CANHUH) && !thrown) + { + seemsg("Your hands stop glowing red!"); + seemsg("The %s appears confused.", mname); + turn_on(*tp, ISHUH); + turn_off(player, CANHUH); + } + + /* Merchants just disappear if hit */ + + /* + * increases prices and curses objects from now on + * though + */ + + if (on(*tp, CANSELL)) + { + msg("The %s disappears with his wares with a BOOM and a flash.", + mname); + killed(NULL, item, NOMESSAGE, NOPOINTS); + aggravate(); + luck++; + } + else if (tp->t_stats.s_hpt <= 0) + killed(&player, item, MESSAGE, POINTS); + + /* + * If the monster is fairly intelligent and about to + * die, it may turn tail and run. + */ + + else if ((tp->t_stats.s_hpt < max(10, tp->maxstats.s_hpt / 10)) && + (rnd(25) < tp->t_stats.s_intel)) + { + turn_on(*tp, ISFLEE); + + /* If monster was suffocating, stop it */ + + if (on(*tp, DIDSUFFOCATE)) + { + turn_off(*tp, DIDSUFFOCATE); + extinguish_fuse(FUSE_SUFFOCATE); + } + + /* If monster held us, stop it */ + + if (on(*tp, DIDHOLD) && (--hold_count == 0)) + turn_off(player, ISHELD); + + turn_off(*tp, DIDHOLD); + + if (on(*tp, CANTELEPORT)) + { + int rm; + + /* + * Erase the monster from the old + * position + */ + + if (isalpha(mvwinch(cw, tp->t_pos.y, tp->t_pos.x))) + mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch); + + mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' '); + + /* Get a new position */ + + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &tp->t_pos); + } + while (winat(tp->t_pos.y, tp->t_pos.x) != FLOOR); + + /* Put it there */ + + mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type); + tp->t_oldch = CCHAR( mvwinch(cw, tp->t_pos.y, tp->t_pos.x) ); + seemsg("The %s seems to have disappeared!", mname); + } + } + } + else if (thrown) + bounce(weap, mname); + else + miss(mname); + } + + if (curr_mons) + chase_it(mp,&player); /* after so that backstabbing can happen */ + + count = 0; + + return(did_hit); +} + +/* + attack() + The monster attacks the player +*/ + +int +attack(struct thing *mp, struct object *weapon, int thrown) +{ + char *mname; + int did_hit = FALSE; + + /* If the monster is in a wall, it cannot attack */ + + if (on(*mp, ISINWALL)) + return (FALSE); + + /* If two monsters start to gang up on our hero, stop fight mode */ + + if (fighting) + { + if (beast == NULL) + beast = mp; + else if (beast != mp) + fighting = FALSE; + } + + /* + Since this is an attack, stop running and any healing that was + going on at the time. + */ + + running = FALSE; + player.t_rest_hpt = player.t_rest_pow = 0; + mp->t_rest_hpt = mp->t_rest_pow = 0; + + if (on(*mp, ISDISGUISE) && off(player, ISBLIND)) + turn_off(*mp, ISDISGUISE); + mname = on(player, ISBLIND) ? "the monster" : + monsters[mp->t_index].m_name; + + if (roll_em(mp, &player, weapon, thrown, wield_weap(weapon, mp)) && + (!thrown || off(player, HASMSHIELD))) + { + did_hit = TRUE; + + m_thunk(weapon, mname); + + if (pstats.s_hpt <= 0) + { + death(mp->t_index); /* Bye bye life ... */ + return TRUE; + } + + /* surprising monsters appear after they shoot at you */ + + if (thrown && on(*mp, CANSURPRISE)) + turn_off(*mp, CANSURPRISE); + else if (!thrown) + { + + /* + If a vampire hits, it may take half your hit + points + */ + + if ( on(*mp, CANSUCK) && !save(VS_MAGIC) ) + { + if (pstats.s_hpt == 1) + { + death(mp->t_index); + return TRUE; + } + else + { + pstats.s_hpt /= 2; + msg("You feel your life force being drawn from you."); + } + } + + /* + strong monsters can shatter or gong crystalline + armor + */ + + if (cur_armor != NULL && cur_armor->o_which == CRYSTAL_ARMOR) + { + if (rnd(mp->t_stats.s_str + (cur_armor->o_ac / 2)) > 20) + { + struct linked_list *item; + struct object *obj; + + for (item = pack; item != NULL; item = next(item)) + { + obj = OBJPTR(item); + + if (obj == cur_armor) + break; + } + + if (item == NULL) + debug("Can't find crystalline armor being worn."); + else + { + msg("Your armor is shattered by the blow."); + cur_armor = NULL; + del_pack(item); + } + } + else if (rnd(mp->t_stats.s_str) > 15) + { + msg("Your armor rings from the blow."); + aggravate(); + } + } + + /* Stinking monsters reduce the player's strength */ + + if (on(*mp, CANSTINK)) + { + turn_off(*mp, CANSTINK); + + if (player.t_ctype != C_PALADIN + && !(player.t_ctype == C_NINJA && pstats.s_lvl > 12) + && !save(VS_POISON)) + { + if (on(player, CANSCENT)) + { + msg("You pass out from the stench of the %s.", mname); + no_command += 4 + rnd(8); + } + else if (off(player, ISUNSMELL)) + msg("The stench of the %s sickens you.", mname); + + if (on(player, HASSTINK)) + lengthen_fuse(FUSE_UNSTINK, STINKTIME); + else + { + turn_on(player, HASSTINK); + light_fuse(FUSE_UNSTINK, 0, STINKTIME, AFTER); + } + } + } + + /* chilling monster reduces strength permanently */ + + if (on(*mp, CANCHILL) && + (cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR)) + { + msg("You cringe at the %s's chilling touch.", mname); + + if (!is_wearing(R_SUSABILITY)) + { + chg_str(-1, FALSE, TRUE); + + if (lost_str == 0) + light_fuse(FUSE_RES_STRENGTH, 0, CHILLTIME, AFTER); + else + lengthen_fuse(FUSE_RES_STRENGTH, CHILLTIME); + } + } + + /* itching monsters reduce dexterity (temporarily) */ + + if (on(*mp, CANITCH) && player.t_ctype != C_PALADIN + && !(player.t_ctype == C_NINJA && pstats.s_lvl > 12) + && !save(VS_POISON)) + { + msg("The claws of the %s scratch you!", mname); + + if (is_wearing(R_SUSABILITY)) + msg("The scratch has no effect."); + else + { + msg("You feel a burning itch."); + turn_on(player, HASITCH); + chg_dext(-1, FALSE, TRUE); + light_fuse(FUSE_UNITCH, 0, roll(4, 6), AFTER); + } + } + + /* a hugging monster may SQUEEEEEEEZE */ + + if (on(*mp, CANHUG) && + (cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR)) + { + if (roll(1, 20) >= 18 || roll(1, 20) >= 18) + { + msg("The %s squeezes you against itself.", mname); + + if ((pstats.s_hpt -= roll(2, 8)) <= 0) + { + death(mp->t_index); + return TRUE; + } + } + } + + /* a trampling monster may step on the player */ + + if (on(*mp, CANTRAMPLE)) + { + if (roll(1, 20) >= 16 || roll(1, 20) >= 16) + { + msg("The %s steps on you.", mname); + + if ((pstats.s_hpt -= roll(3, mp->t_stats.s_lvl)) <= 0) + { + death(mp->t_index); + return TRUE; + } + } + } + + /* a disease-carrying monster may transmit the disease */ + + if (on(*mp, CANDISEASE) && + (rnd(pstats.s_const) < mp->t_stats.s_lvl) && + off(player, HASDISEASE)) + { + + if (is_wearing(R_HEALTH) + || (player.t_ctype == C_PALADIN) + || (player.t_ctype == C_NINJA && + pstats.s_lvl > 6)) + msg("The wound heals quickly."); + else + { + turn_on(player, HASDISEASE); + light_fuse(FUSE_CURE_DISEASE,0,roll(4,4) * SICKTIME, AFTER); + msg("You have contracted a disease!"); + } + } + + /* a rust monster will weaken your armor */ + + if (on(*mp, CANRUST)) + { + if (cur_armor != NULL && + cur_armor->o_which != SOFT_LEATHER && + cur_armor->o_which != HEAVY_LEATHER && + cur_armor->o_which != CUIRBOLILLI && + cur_armor->o_which != PADDED_ARMOR && + cur_armor->o_which != CRYSTAL_ARMOR && + cur_armor->o_which != MITHRIL && + !(cur_armor->o_flags & ISPROT) && + cur_armor->o_ac < pstats.s_arm + 1) + { + msg("Your armor weakens!"); + cur_armor->o_ac++; + } + else if (cur_armor != NULL && + (cur_armor->o_flags & ISPROT) && + cur_armor->o_which != SOFT_LEATHER && + cur_armor->o_which != HEAVY_LEATHER && + cur_armor->o_which != CUIRBOLILLI && + cur_armor->o_which != PADDED_ARMOR && + cur_armor->o_which != CRYSTAL_ARMOR && + cur_armor->o_which != MITHRIL) + msg("The rust vanishes instantly!"); + } + + /* If a surprising monster hit you, you can see it now */ + + if (on(*mp, CANSURPRISE)) + turn_off(*mp, CANSURPRISE); + + /* an infesting monster will give you a parasite or rot */ + + if (on(*mp, CANINFEST) && rnd(pstats.s_const) < mp->t_stats.s_lvl) + { + if (is_wearing(R_HEALTH) || (player.t_ctype == C_PALADIN) + || (player.t_ctype == C_NINJA && pstats.s_lvl > 6)) + msg("The wound quickly heals."); + else + { + turn_off(*mp, CANINFEST); + msg("You have contracted a parasitic infestation!"); + infest_dam++; + turn_on(player, HASINFEST); + } + } + + /* Some monsters have poisonous bites */ + + if (on(*mp, CANPOISON) && !save(VS_POISON)) + { + if (is_wearing(R_SUSABILITY) || (player.t_ctype == C_PALADIN) + || (player.t_ctype == C_NINJA && pstats.s_lvl > 12)) + msg("The sting has no effect on you!"); + else + { + chg_str(-1, FALSE, FALSE); + msg("You feel a sting in your arm and now feel weaker."); + } + } + + /* a hideous monster may cause fear by touching */ + + if (on(*mp, TOUCHFEAR)) + { + turn_off(*mp, TOUCHFEAR); + + if (!save(VS_WAND)&&!(on(player,ISFLEE)&&(player.t_chasee==mp))) + { + if (off(player, SUPERHERO) + && (player.t_ctype != C_PALADIN)) + { + turn_on(player, ISFLEE); + player.t_ischasing = FALSE; + player.t_chasee = mp; + msg("The %s's touch terrifies you.", mname); + } + else + msg("The %s's touch feels cold and clammy.", mname); + } + } + + /* some monsters will suffocate our hero */ + + if (on(*mp, CANSUFFOCATE) && (rnd(100) < 15) && + (find_slot(FUSE_SUFFOCATE, FUSE) == NULL)) + { + turn_on(*mp, DIDSUFFOCATE); + msg("The %s is beginning to suffocate you.", + mname); + light_fuse(FUSE_SUFFOCATE, 0, roll(4, 2), AFTER); + } + + /* don't look now, you will get turned to stone */ + + if (on(*mp, TOUCHSTONE)) + { + turn_off(*mp, TOUCHSTONE); + + if (on(player, CANINWALL)) + msg("The %s's touch has no effect.", mname); + else + { + if (!save(VS_PETRIFICATION) && rnd(100) < 3) + { + msg("Your body begins to solidify."); + msg("You are turned to stone !!! --More--"); + wait_for(' '); + death(D_PETRIFY); + return TRUE; + } + else + { + msg("The %s's touch stiffens your limbs.", mname); + no_command = rnd(STONETIME) + 2; + } + } + } + + /* Undead might drain energy levels */ + + if ((on(*mp, CANDRAIN) || on(*mp, DOUBLEDRAIN)) && rnd(100) < 15) + { + if (is_carrying(TR_AMULET)) + msg("The Amulet protects you from the %s's negative energy!", + mname); + else + { + lower_level(mp->t_index); + + if (on(*mp, DOUBLEDRAIN)) + lower_level(mp->t_index); + } + + turn_on(*mp, DIDDRAIN); + } + + /* permanently drain a wisdom point */ + + if (on(*mp, DRAINWISDOM) && rnd(100) < 15) + { + int ring_str; /* Value of ring strengths */ + + /* Undo any ring changes */ + + ring_str = ring_value(R_ADDWISDOM) + + (on(player, POWERWISDOM) ? 10 : 0); + + pstats.s_wisdom -= ring_str; + + msg("You feel slightly less wise now."); + + pstats.s_wisdom = max(pstats.s_wisdom - 1, 3); + max_stats.s_wisdom = pstats.s_wisdom; + + /* Now put back the ring changes */ + + pstats.s_wisdom += ring_str; + + } + + /* permanently drain a intelligence point */ + + if (on(*mp, DRAINBRAIN) && rnd(100) < 15) + { + int ring_str; /* Value of ring strengths */ + + /* Undo any ring changes */ + + ring_str = ring_value(R_ADDINTEL) + + (on(player, POWERINTEL) ? 10 : 0); + + pstats.s_intel -= ring_str; + + msg("You feel slightly less intelligent now."); + pstats.s_intel = max(pstats.s_intel - 1, 3); + max_stats.s_intel = pstats.s_intel; + + /* Now put back the ring changes */ + + pstats.s_intel += ring_str; + } + + /* Violet fungi and others hold the hero */ + + if (on(*mp, CANHOLD) && off(*mp, DIDHOLD) + && !is_wearing(R_FREEDOM)) + { + turn_on(player, ISHELD); + turn_on(*mp, DIDHOLD); + hold_count++; + } + + /* suckers will suck blood and run away */ + + if (on(*mp, CANDRAW)) + { + turn_off(*mp, CANDRAW); + turn_on(*mp, ISFLEE); + msg("The %s sates itself with your blood.", mname); + + if ((pstats.s_hpt -= 12) <= 0) + { + death(mp->t_index); + return TRUE; + } + } + + /* el stinkos will force a reduction in strength */ + + if (on(*mp, CANSMELL)) + { + turn_off(*mp, CANSMELL); + + if (save(VS_MAGIC) || is_wearing(R_SUSABILITY)) + msg("You smell an unpleasant odor."); + else + { + int odor_str = -(rnd(6) + 1); + + if (on(player, CANSCENT)) + { + msg("You pass out from a foul odor."); + no_command += 4 + rnd(8); + } + else if (off(player, ISUNSMELL)) + msg("You are overcome by a foul odor."); + + if (lost_str == 0) + { + chg_str(odor_str, FALSE, TRUE); + light_fuse(FUSE_RES_STRENGTH, 0, SMELLTIME, AFTER); + } + else + lengthen_fuse(FUSE_RES_STRENGTH, SMELLTIME); + } + } + + /* Paralyzation */ + + if (on(*mp, CANPARALYZE)) + { + turn_off(*mp, CANPARALYZE); + + if (!save(VS_PARALYZATION) && no_command == 0) + { + if (on(player, CANINWALL)) + msg("The %s's touch has no effect.", mname); + else + { + msg("The %s's touch paralyzes you.", mname); + no_command = FREEZETIME; + } + } + } + + /* Rotting */ + + if (on(*mp, CANROT)) + { + turn_off(*mp, CANROT); + turn_on(*mp, DOROT); + } + + /* some monsters steal gold */ + + if (on(*mp, STEALGOLD)) + { + long lastpurse; + struct linked_list *item; + struct object *obj; + + lastpurse = purse; + purse = (purse > GOLDCALC) ? purse - GOLDCALC : 0L; + + if (!save(VS_MAGIC)) + purse = (purse > (4*GOLDCALC)) ? purse-(4*GOLDCALC) : 0L; + + if (purse != lastpurse) + { + msg("Your purse feels lighter."); + + /* Give the gold to the thief */ + + for (item = mp->t_pack; item != NULL; item = next(item)) + { + obj = OBJPTR(item); + + if (obj->o_type == GOLD) + { + obj->o_count += lastpurse - purse; + break; + } + } + + /* Did we do it? */ + + if (item == NULL) /* Then make some */ + { + item = new_item(sizeof *obj); + obj = OBJPTR(item); + obj->o_type = GOLD; + obj->o_count = lastpurse - purse; + obj->o_hplus = obj->o_dplus = 0; + obj->o_damage = obj->o_hurldmg = "0d0"; + obj->o_ac = 11; + obj->o_group = 0; + obj->o_flags = 0; + obj->o_mark[0] = '\0'; + obj->o_pos = mp->t_pos; + + attach(mp->t_pack, item); + } + } + + if (rnd(2)) + turn_on(*mp, ISFLEE); + + turn_on(*mp, ISINVIS); + } + + /* other monsters steal magic */ + + if (on(*mp, STEALMAGIC)) + { + struct linked_list *list, *stealit; + struct object *obj; + int worth = 0; + + stealit = NULL; + + for (list = pack; list != NULL; list = next(list)) + { + obj = OBJPTR(list); + + if (rnd(33) == 0) /* some stuff degrades */ + { + if (obj->o_flags & ISBLESSED) + obj->o_flags &= ~ISBLESSED; + else + obj->o_flags |= ISCURSED; + + msg("You feel nimble fingers reach into you pack."); + } + + if ((obj != cur_armor && + obj != cur_weapon && + obj != cur_ring[LEFT_1] && + obj != cur_ring[LEFT_2] && + obj != cur_ring[LEFT_3] && + obj != cur_ring[LEFT_4] && + obj != cur_ring[LEFT_5] && + obj != cur_ring[RIGHT_1] && + obj != cur_ring[RIGHT_2] && + obj != cur_ring[RIGHT_3] && + obj != cur_ring[RIGHT_4] && + obj != cur_ring[RIGHT_5] && + !(obj->o_flags & ISPROT) && + is_magic(obj) + || level > 45) + && get_worth(obj) > worth) + { + stealit = list; + worth = get_worth(obj); + } + } + + if (stealit != NULL) + { + struct object *newobj; + + newobj = OBJPTR(stealit); + + if (newobj->o_count > 1 && newobj->o_group == 0) + { + int oc; + struct linked_list *nitem; + struct object *op; + + oc = --(newobj->o_count); + newobj->o_count = 1; + nitem = new_item(sizeof *newobj); + op = OBJPTR(nitem); + *op = *newobj; + + msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE)); + newobj->o_count = oc; + attach(mp->t_pack, nitem); + } + else + { + msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE)); + newobj->o_flags &= ~ISCURSED; + dropcheck(newobj); + rem_pack(newobj); + attach(mp->t_pack, stealit); + + if (newobj->o_type == ARTIFACT) + has_artifact &= ~(1 << newobj->o_which); + } + + if (newobj->o_flags & ISOWNED) + { + turn_on(*mp, NOMOVE); + msg("The %s is transfixed by your ownership spell.", + mname); + } + + if (rnd(2)) + turn_on(*mp, ISFLEE); + + turn_on(*mp, ISINVIS); + updpack(); + } + } + } + } + else /* missed */ + { + /* If the thing was trying to surprise, no good */ + + if (on(*mp, CANSURPRISE)) + turn_off(*mp, CANSURPRISE); + + m_bounce(weapon, mname); + } + + count = 0; + + status(FALSE); + + return(did_hit); +} + + +/* + mon_mon_attack() + A monster attacks another monster +*/ + +int +mon_mon_attack(struct thing *attacker, struct linked_list *mon, struct object *weapon, int thrown) +{ + struct thing *attackee = THINGPTR(mon); + int did_hit = FALSE; + int ee_visible = cansee(attackee->t_pos.y, attackee->t_pos.x); + int er_visible = cansee(attacker->t_pos.y, attacker->t_pos.x); + char *mname1 = monsters[attacker->t_index].m_name; + char *mname2 = monsters[attackee->t_index].m_name; + + /* Similar monsters don't hit each other */ + + if (attacker->t_index == attackee->t_index) + { + if (attacker == THINGPTR(fam_ptr) && er_visible) + msg("Master, I cannot hit one of my brethren."); + if (!thrown && rnd(100) - attacker->t_stats.s_charisma + luck < 0) + { + if (er_visible) + msg("Your %s has made a new ally.", mname1); + + turn_on(*attackee, ISCHARMED); + } + + return(FALSE); + } + + /* stop running and any healing */ + + attackee->t_rest_hpt = attackee->t_rest_pow = 0; + attacker->t_rest_hpt = attacker->t_rest_pow = 0; + + if (roll_em(attacker, attackee, weapon, thrown, + wield_weap(weapon, attacker))) + { + did_hit = TRUE; + + if (ee_visible && on(*attackee, CANSURPRISE)) + turn_off(*attackee, CANSURPRISE); + + if (ee_visible && er_visible && weapon != NULL) + msg("The %s's %s hits the %s.", mname1, + weaps[weapon->o_which].w_name, mname2); + else if (ee_visible && er_visible) + msg("The %s hits the %s.", mname1, mname2); + + if (attackee->t_stats.s_hpt <= 0) + { + killed(attacker, mon, MESSAGE, + on(*attacker, ISFAMILIAR) ? POINTS : NOPOINTS); + return(TRUE); + } + } + else /* missed */ + { + did_hit = FALSE; + + if (ee_visible && er_visible && weapon != NULL) + msg("The %s's %s misses the %s.", mname1, + weaps[weapon->o_which].w_name, mname2); + else if (ee_visible && er_visible) + msg("The %s misses the %s.", mname1, mname2); + } + + if (er_visible && !ee_visible) + msg("The %s struggles with something.",mname1); + + if (off(*attackee, ISMEAN) && off(*attackee, ISFAMILIAR)) + turn_on(*attackee, ISRUN); + + count = 0; + + status(FALSE); + + return(did_hit); +} + + +/* + swing() + returns true if the swing hits +*/ + +int +swing(int class, int at_lvl, int op_arm, int wplus) +{ + int res = rnd(20) + 1; + int need; + + need = att_mat[class].base - + att_mat[class].factor * + ((min(at_lvl, att_mat[class].max_lvl) - + att_mat[class].offset) / att_mat[class].range) + + (10 - op_arm); + + if (need > 20 && need <= 25) + need = 20; + + return(res + wplus >= need); +} + +/* + init_exp() + set up initial experience level change threshold +*/ + +void +init_exp(void) +{ + max_stats.s_exp = e_levels[player.t_ctype]; +} + +/* + next_exp_level() + Do the next level arithmetic Returns number of levels to jump +*/ + +int +next_exp_level(int print_message) +{ + int level_jump = 0; + + while (pstats.s_exp >= max_stats.s_exp) + { + pstats.s_exp -= max_stats.s_exp; /* excess experience points */ + level_jump++; + + if (max_stats.s_exp < 0x3fffffffL) /* 2^30 - 1 */ + max_stats.s_exp *= 2L; /* twice as many for next */ + } + + if (print_message) + msg("You need %d more points to attain the %stitle of %s.", + max_stats.s_exp - pstats.s_exp, + (pstats.s_lvl > 14 ? "next " : ""), + cnames[player.t_ctype][min(pstats.s_lvl, 14)]); + + return(level_jump); +} + +/* + check_level() + Check to see if the guy has gone up a level. +*/ + +void +check_level(void) +{ + int num_jumped, j, add; + int nsides; + + if ((num_jumped = next_exp_level(NOMESSAGE)) <= 0) + return; + + pstats.s_lvl += num_jumped; /* new experience level */ + + switch (player.t_ctype) + { + case C_MAGICIAN: + case C_ILLUSION: nsides = 4; + break; + case C_THIEF: + case C_ASSASIN: + case C_NINJA: + case C_MONSTER: + default: nsides = 6; + break; + case C_CLERIC: + case C_DRUID: nsides = 8; + break; + + case C_FIGHTER: + case C_PALADIN: + case C_RANGER: + nsides = 12; + break; + } + + /* Take care of multi-level jumps */ + + for (add = 0, j = 0; j < num_jumped; j++) + { + int increase = roll(1, nsides) + const_bonus(); + + add += max(1, increase); + } + + max_stats.s_hpt += add; + pstats.s_hpt += add; + + msg("Welcome, %s, to level %d.", + cnames[player.t_ctype][min(pstats.s_lvl - 1, 14)], pstats.s_lvl); + + next_exp_level(MESSAGE); + + /* Now add new spell points and learn new spells */ + + nsides = 16 - nsides; + + for (add = 0, j = 0; j < num_jumped; j++) + { + int increase = roll(1, nsides) + int_wis_bonus(); + + add += max(1, increase); + } + + max_stats.s_power += add; + pstats.s_power += add; + + learn_new_spells(); + + /* Create a more powerful familiar (if player has one) */ + + if (on(player, HASFAMILIAR) && on(player, CANSUMMON)) + summon_monster((short) 0, FAMILIAR, NOMESSAGE); +} + +/* + roll_em() + Roll several attacks +*/ + +int +roll_em(struct thing *att_er, struct thing *def_er, struct object *weap, int thrown, struct object *my_weapon) +{ + struct stats *att = &att_er->t_stats; + struct stats *def = &def_er->t_stats; + int ndice, nsides, nplus, def_arm; + char *cp; + int prop_hplus = 0, prop_dplus = 0; + int is_player = (att_er == &player); + int did_hit = FALSE; + + if (weap == NULL) + cp = att->s_dmg; + else if (!thrown) + cp = weap->o_damage; + else if ((weap->o_flags & ISMISL) && my_weapon != NULL && + my_weapon->o_which == weap->o_launch) + { + cp = weap->o_hurldmg; + prop_hplus = my_weapon->o_hplus; + prop_dplus = my_weapon->o_dplus; + } + else + cp = (weap->o_flags & ISMISL ? weap->o_damage : + weap->o_hurldmg); + + for (;;) + { + int damage; + int hplus = prop_hplus + (weap == NULL ? 0 : weap->o_hplus); + int dplus = prop_dplus + (weap == NULL ? 0 : weap->o_dplus); + + /* Is attacker weak? */ + + if (on(*att_er, HASSTINK)) + hplus -= 2; + + if (is_player) + { + hplus += hitweight(); /* adjust for encumberence */ + dplus += hung_dam(); /* adjust damage for hungry player */ + dplus += ring_value(R_ADDDAM); + } + + ndice = atoi(cp); + + if (cp == NULL || (cp = strchr(cp, 'd')) == NULL) + break; + + nsides = atoi(++cp); + + if (cp != NULL && (cp = strchr(cp, '+')) != NULL) + nplus = atoi(++cp); + else + nplus = 0; + + if (def == &pstats) + { + if (on(*att_er, NOMETAL) && cur_armor != NULL && + (cur_armor->o_which == RING_MAIL || + cur_armor->o_which == SCALE_MAIL || + cur_armor->o_which == CHAIN_MAIL || + cur_armor->o_which == SPLINT_MAIL || + cur_armor->o_which == BANDED_MAIL || + cur_armor->o_which == GOOD_CHAIN || + cur_armor->o_which == PLATE_MAIL || + cur_armor->o_which == PLATE_ARMOR)) + def_arm = def->s_arm; + else if (cur_armor != NULL) + def_arm = cur_armor->o_ac - 10 + pstats.s_arm; + else + def_arm = def->s_arm; + def_arm -= ring_value(R_PROTECT); + } + else + def_arm = def->s_arm; + + if ((weap != NULL && weap->o_type == WEAPON && + (weap->o_flags & ISSILVER) && + !save_throw(VS_MAGIC, def_er)) || + swing(att_er->t_ctype, att->s_lvl, + def_arm - dext_prot(def->s_dext), + hplus + str_plus(att->s_str) + dext_plus(att->s_dext))) + { + damage = roll(ndice, nsides) + dplus + nplus + + add_dam(att->s_str); + + /* Rangers do +1/lvl vs. ISLARGE */ + + if (att_er->t_ctype == C_RANGER && on(*def_er, ISLARGE)) + damage += pstats.s_lvl; + + /* Ninja do +1 per lvl/2 */ + + if (att_er->t_ctype == C_NINJA) + damage += pstats.s_lvl / 2; + + /* Check for half damage monsters */ + + if (on(*def_er, HALFDAMAGE) && (weap != NULL) && + !((weap->o_flags & CANBURN) && + on(*def_er, CANBBURN))) + damage /= 2; + + /* undead get twice damage from silver weapons */ + + if (on(*def_er, ISUNDEAD) && + (weap != NULL) && (weap->o_flags & ISSILVER)) + damage *= 2; + + /* Check for fireproof monsters */ + + if (on(*def_er, NOFIRE) && (weap != NULL) && + (weap->o_flags & CANBURN)) + damage = 0; + + /* Check for metal proof monsters */ + + if (on(*def_er, NOMETAL) && (weap != NULL) && + (weap->o_flags & ISMETAL)) + damage = 0; + + /* Check for monsters that ignore sharp weapons */ + + if (on(*def_er, NOSHARP) && (weap != NULL) && + (weap->o_flags & ISSHARP)) + damage = 0; + + /* Check for poisoned weapons */ + + if ((weap != NULL) && (weap->o_flags & ISPOISON) + && off(*def_er, ISUNDEAD) + && !save_throw(VS_POISON, def_er)) + damage = max(damage, (def->s_hpt / 2) + 5); + + /* Check for no-damage and division */ + + if (on(*def_er, BLOWDIVIDE) && rnd(3) == 0 && + !((weap != NULL) && (weap->o_flags & CANBURN))) + { + damage = 0; + creat_mons(def_er, def_er->t_index, NOMESSAGE); + } + + damage = max(0, damage); + + /* + * sleeping monsters are backstabbed by certain + * player classes, but only when they can see + */ + + if (is_player && !thrown && damage > 0 && + (off(*def_er, ISRUN) || def_er->t_no_move > 0) && + (player.t_ctype == C_THIEF || + player.t_ctype == C_NINJA || + player.t_ctype == C_ASSASIN) && + off(player,ISBLIND) + && (wield_ok(&player, my_weapon, NOMESSAGE)) + && (wear_ok(&player, cur_armor, NOMESSAGE))) + { + damage *= (pstats.s_lvl / 4 + 2); + + msg("You backstabbed the %s %d times!", + monsters[def_er->t_index].m_name, + (pstats.s_lvl / 4) + 2); + + if (player.t_ctype == C_NINJA || + player.t_ctype == C_ASSASIN) + pstats.s_exp += def_er->t_stats.s_exp + / 2; + } + + def->s_hpt -= damage; /* Do the damage */ + + debug("Hit %s for %d (%d) ", + monsters[def_er->t_index].m_name, damage, + def_er->t_stats.s_hpt); + + if (is_player && is_wearing(R_VREGEN)) + { + damage = (ring_value(R_VREGEN) * damage) / 3; + pstats.s_hpt = min(max_stats.s_hpt, + pstats.s_hpt + damage); + } + + /* stun monsters when taking more than 1/3 their max hpts */ + + if (is_player && !thrown && !did_hit && + (player.t_ctype == C_FIGHTER) && + (damage > def_er->maxstats.s_hpt / 3) ) + { + if (def->s_hpt > 0) + { + msg("The %s has been stunned!", + monsters[def_er->t_index].m_name); + def_er->t_no_move += rnd(4) + 1; + } + pstats.s_exp += def_er->t_stats.s_exp / 4; + } + + did_hit = TRUE; + } + + if (cp == NULL || (cp = strchr(cp, '/')) == NULL) + break; + + cp++; + } + + return(did_hit); +} + +/* + prname() + Figure out the monsters name +*/ + +const char * +prname(char *who) +{ + if (on(player, ISBLIND)) + return(monstern); + else + return(who); +} + +/* + hit() + Print a message to indicate a succesful hit +*/ + +void +hit(char *ee) +{ + char *s; + + if (fighting) + return; + + switch (rnd(15)) + { + default: s = "hit"; break; + case 1: s = "score an excellent hit on"; break; + case 2: s = "injure"; break; + case 3: s = "swing and hit"; break; + case 4: s = "damage"; break; + case 5: s = "barely nick"; break; + case 6: s = "scratch"; break; + case 7: s = "gouge a chunk out of"; break; + case 8: s = "severely wound"; break; + case 9: s = "counted coup on"; break; + case 10: s = "drew blood from"; break; + case 11: s = "nearly decapitate"; break; + case 12: s = "deal a wacking great blow to"; break; + } + + msg("You %s the %s.", s, prname(ee)); +} + +/* + miss() + Print a message to indicate a poor swing +*/ + +void +miss(char *ee) +{ + char *s; + + if (fighting) + return; + + switch (rnd(10)) + { + default: s = "miss"; break; + case 1: s = "swing and miss"; break; + case 2: s = "barely miss"; break; + case 3: s = "don't hit"; break; + case 4: s = "wildly windmill around"; break; + case 5: s = "almost fumble while missing"; break; + } + + msg("You %s the %s.", s, prname(ee)); +} + +/* + save_throw() + See if a creature save against something +*/ + +int +save_throw(int which, struct thing *tp) +{ + int need; + int ring_bonus = 0; + int armor_bonus = 0; + int class_bonus = 0; + + if (tp == &player) + { + if (player.t_ctype == C_PALADIN) + class_bonus = 2; + + ring_bonus = ring_value(R_PROTECT); + + if (cur_armor != NULL && (which == VS_WAND || + which == VS_MAGIC)) + { + if (cur_armor->o_which == MITHRIL) + armor_bonus += 5; + armor_bonus += (armors[cur_armor->o_which].a_class + - cur_armor->o_ac); + } + } + + need = 14 + which - tp->t_stats.s_lvl / 2 - ring_bonus - + armor_bonus - class_bonus; + + /* Roll of 1 always fails; 20 always saves */ + + if (need < 1) + need = 1; + else if (need > 20) + need = 20; + + return(roll(1, 20) >= need); +} + +/* + save() + See if he saves against various nasty things +*/ + +int +save(int which) +{ + return save_throw(which, &player); +} + +/* + dext_plus() + compute to-hit bonus for dexterity +*/ + +int +dext_plus(int dexterity) +{ + return ((dexterity - 10) / 3); +} + +/* + * dext_prot: compute armor class bonus for dexterity + */ + +int +dext_prot(int dexterity) +{ + return ((dexterity - 9) / 2); +} + +/* + str_plus() + compute bonus/penalties for strength on the "to hit" roll +*/ + +static const int strtohit[] = +{ + 0, 0, 0, -3, -2, -2, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 3, 3, 4, 4, 5, 6, 7 +}; + +int +str_plus(int str) +{ + int ret_val = str; + + if (str < 3) + ret_val = 3; + else if (str > 25) + ret_val = 25; + + return(strtohit[ret_val]); +} + +/* + add_dam() + compute additional damage done for exceptionally high or low strength +*/ + +static const int str_damage[] = +{ + 0, 0, 0, -1, -1, -1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 2, 7, 8, 9, 10, 11, 12, 14 +}; + +int +add_dam(int str) +{ + int ret_val = str; + + if (str < 3) + ret_val = 3; + else if (str > 25) + ret_val = 25; + + return(str_damage[ret_val]); +} + +/* + hung_dam() + Calculate damage depending on players hungry state +*/ + +int +hung_dam(void) +{ + int howmuch = 0; + + switch (hungry_state) + { + case F_OK: + case F_HUNGRY: howmuch = 0; break; + case F_WEAK: howmuch = -1; break; + case F_FAINT: howmuch = -2; break; + } + + return(howmuch); +} + +/* + raise_level() + The guy just magically went up a level. +*/ + +void +raise_level(void) +{ + pstats.s_exp = max_stats.s_exp; + check_level(); +} + +/* + thunk() + A missile hits a monster +*/ + +void +thunk(struct object *weap, char *mname) +{ + if (fighting) + return; + + if (weap->o_type == WEAPON) + msg("The %s hits the %s.", weaps[weap->o_which].w_name, prname(mname)); + else + msg("You hit the %s.", prname(mname)); +} + +/* + m_thunk() + A missile from a monster hits the player +*/ + +void +m_thunk(struct object *weap, char *mname) +{ + if (fighting) + return; + + if (weap != NULL && weap->o_type == WEAPON) + msg("The %s's %s hits you.",prname(mname),weaps[weap->o_which].w_name); + else + msg("The %s hits you.", prname(mname)); +} + +/* + bounce() + A missile misses a monster +*/ + +void +bounce(struct object *weap, char *mname) +{ + if (fighting) + return; + + if (weap->o_type == WEAPON) + msg("The %s misses the %s.",weaps[weap->o_which].w_name,prname(mname)); + else + msg("You missed the %s.", prname(mname)); +} + +/* + m_bounce() + A missile from a monster misses the player +*/ + +void +m_bounce(struct object *weap, char *mname) +{ + if (fighting) + return; + + if (weap != NULL && weap->o_type == WEAPON) + msg("The %s's %s misses you.", prname(mname), + weaps[weap->o_which].w_name); + else + msg("The %s misses you.", prname(mname)); +} + +/* + remove_monster() + remove a monster from the screen +*/ + +void +remove_monster(coord *mp, struct linked_list *item) +{ + struct thing *tp = THINGPTR(item); + char ch = tp->t_oldch; + + mvwaddch(mw, mp->y, mp->x, ' '); + + if (ch < 33 || ch == ' ') + ch = CCHAR( mvwinch(stdscr, mp->y, mp->x) ); + + if (cansee(mp->y, mp->x)) + mvwaddch(cw, mp->y, mp->x, ch); + + detach(mlist, item); + discard(item); +} + +/* + is_magic() + Returns true if an object radiates magic +*/ + +int +is_magic(struct object *obj) +{ + switch (obj->o_type) + { + case ARMOR: + return(obj->o_ac != armors[obj->o_which].a_class); + + case WEAPON: + return(obj->o_hplus != 0 || obj->o_dplus != 0); + + case POTION: + case SCROLL: + case STICK: + case RING: + case ARTIFACT: + return(TRUE); + } + + return(FALSE); +} + +/* + killed() + Called to put a monster to death +*/ + +void +killed(struct thing *killer, struct linked_list *item, int print_message, + int give_points) +{ + struct linked_list *pitem, *nitem; + struct thing *tp = THINGPTR(item); + int visible = cansee(tp->t_pos.y, tp->t_pos.x); + int is_player = (killer == (&player)); + + if (item == curr_mons) + curr_mons = NULL; + else if (item == next_mons) + next_mons = next(next_mons); + + if (on(*tp, WASSUMMONED)) + { + extinguish_fuse(FUSE_UNSUMMON); + turn_off(player, HASSUMMONED); + } + + if (print_message && visible) + { + if (is_player) + addmsg("You have defeated "); + else + addmsg("The %s has defeated ", + monsters[killer->t_index].m_name); + + if (on(player, ISBLIND)) + msg("it."); + else + msg("the %s.", monsters[tp->t_index].m_name); + } + debug("Removing %s", monsters[tp->t_index].m_name); + if (killer != NULL && item == fam_ptr) /* The player's familiar died */ + { + turn_off(player, HASFAMILIAR); + fam_ptr = NULL; + msg("An incredible wave of sadness sweeps over you."); + } + + check_residue(tp); + + if (is_player) + { + fighting = FALSE; + + if (on(*tp, ISFRIENDLY)) + { + msg("You feel a slight chill run up and down your spine."); + luck++; + } + } + + if (give_points) + { + if (killer != NULL) + { + killer->t_stats.s_exp += tp->t_stats.s_exp; + + if (on(*killer, ISFAMILIAR)) + pstats.s_exp += tp->t_stats.s_exp; + } + + if (is_player) + { + switch (player.t_ctype) + { + case C_CLERIC: + case C_PALADIN: + if (on(*tp, ISUNDEAD) || on(*tp, ISUNIQUE)) + { + pstats.s_exp += tp->t_stats.s_exp / 2; + msg("You are to be commended for smiting the ungodly."); + } + break; + + case C_DRUID: + case C_RANGER: + if (on(*tp, ISLARGE)) + { + pstats.s_exp += tp->t_stats.s_exp / 2; + msg("Congratulations on smiting a dangerous monster."); + } + break; + + case C_MAGICIAN: + case C_ILLUSION: + if (on(*tp, DRAINBRAIN)) + { + pstats.s_exp += tp->t_stats.s_exp / 2; + msg("Congratulations on smiting a dangerous monster."); + } + + } + } + check_level(); + } + + /* Empty the monsters pack */ + + for (pitem = tp->t_pack; pitem != NULL; pitem = nitem) + { + struct object *obj = OBJPTR(pitem); + + nitem = next(pitem); + + obj->o_pos = tp->t_pos; + detach(tp->t_pack, pitem); + + if (killer == NULL) + discard(pitem); + else + fall(killer, pitem, FALSE, FALSE); + } + + remove_monster(&tp->t_pos, item); +} + + +/* + wield_weap() + Returns a pointer to the weapon the monster is wielding corresponding to the given thrown weapon +*/ + +struct object * +wield_weap(struct object *weapon, struct thing *mp) +{ + int look_for; + struct linked_list *pitem; + + if (weapon == NULL) + return (NULL); + + switch (weapon->o_which) + { + case BOLT: + look_for = CROSSBOW; + break; + + case ARROW: + look_for = BOW; + break; + + case SILVERARROW: + case FLAMEARROW: + look_for = BOW; + break; + + case ROCK: + case BULLET: + look_for = SLING; + break; + + default: + return(NULL); + } + + for (pitem = mp->t_pack; pitem; pitem = next(pitem)) + if ((OBJPTR(pitem))->o_which == look_for) + return(OBJPTR(pitem)); + + return (NULL); +} + +/* + summon_help() + Summon - see whether to summon help Returns TRUE if help comes, FALSE + otherwise +*/ + +void +summon_help(struct thing *mons, int force) +{ + char *helpname; + int which, i; + char *mname = monsters[mons->t_index].m_name; + + /* Try to summon if less than 1/3 max hit points */ + + if (on(*mons, CANSUMMON) && + (force == FORCE || + (mons->t_stats.s_hpt < mons->maxstats.s_hpt / 3) && + (rnd(40 * 10) < (mons->t_stats.s_lvl * mons->t_stats.s_intel)))) + { + turn_off(*mons, CANSUMMON); + msg("The %s summons its attendants!", mname); + helpname = monsters[mons->t_index].m_typesum; + + for (which = 1; which < nummonst; which++) + { + if (strcmp(helpname, monsters[which].m_name) == 0) + break; + } + + if (which >= nummonst) + { + debug("Couldn't find summoned one."); + return; + } + + /* summoned monster was genocided */ + + if (!monsters[which].m_normal) + { + msg("The %s becomes very annoyed at you!", mname); + + if (on(*mons, ISSLOW)) + turn_off(*mons, ISSLOW); + else + turn_on(*mons, ISHASTE); + + return; + } + else + for (i = 0; i < monsters[mons->t_index].m_numsum; i++) + { + struct linked_list *ip; + struct thing *tp; + + if ((ip = creat_mons(mons, which, NOMESSAGE)) != NULL) + { + tp = THINGPTR(ip); + turn_off(*tp, ISFRIENDLY); + } + } + } + + return; +} + +/* + maxdamage() + return the max damage a weapon can do +*/ + +int +maxdamage(char *cp) +{ + int ndice, nsides, nplus; + + ndice = atoi(cp); + + if (cp == NULL || (cp = strchr(cp, 'd')) == NULL) + return(0); + + nsides = atoi(++cp); + + if (cp != NULL && (cp = strchr(cp, '+')) != NULL) + nplus = atoi(++cp); + else + nplus = 0; + + return(ndice * nsides + nplus); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/getplay.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/getplay.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,350 @@ +/* + getplay.c - Procedures for saving and retrieving a characters starting + attributes, armour, and weapon. + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/* 11/08/83 ???, S.A. Hester */ + +#include +#include +#include +#include "rogue.h" + +#define I_STR 0 +#define I_INTEL 1 +#define I_WISDOM 2 +#define I_DEXT 3 +#define I_CONST 4 +#define I_CHARISMA 5 +#define I_HPT 6 +#define I_POWER 7 +#define I_CTYPE 8 +#define MAXPATT 9 /* Total Number of above defines. */ +#define MAXPDEF 10 /* Maximum number of pre-defined chars */ + +static int def_array[MAXPDEF][MAXPATT]; /* Pre-def'd chars */ + +static void get_chr_filename(char *filename, int size) +{ + const char *home; + + home = getenv("HOME"); + + if (home) { + if ((int)strlen(home) < (size - 12) ) + { + strcpy(filename, home); + strcat(filename,"/urogue.chr"); + } + else + strncpy(filename,"urogue.chr",size); + } + else + strcpy(filename, "urogue.chr"); +} + +int +geta_player(void) +{ + int i; + FILE *fd; + char pbuf[2 * LINELEN]; + char filename[200]; + + get_chr_filename(filename, sizeof(filename)); + + if ((fd = fopen(filename, "r")) == NULL) + return(FALSE); + + fread(def_array, sizeof(def_array), 1, fd); + fclose(fd); + + wclear(hw); + touchwin(hw); + + print_stored(); + mvwaddstr(hw, 0, 0, "Do you wish to select a character? "); + wrefresh(hw); + + if (readcharw(hw) != 'y') + return FALSE; + + do + { + wmove(hw, LINES - 1, 0); + wclrtoeol(hw); + mvwaddstr(hw, 0, 0, "Enter the number of a pre-defined character: "); + wclrtoeol(hw); + wrefresh(hw); + get_string(pbuf, hw); + i = atoi(pbuf) - 1; + + if (i < 0 || i > MAXPDEF - 1) + { + wstandout(hw); + mvwaddstr(hw, 1, 0, "Please use the range 1 to"); + wprintw(hw, " %d.", MAXPDEF); + wstandend(hw); + wclrtoeol(hw); + wrefresh(hw); + } + else if (def_array[i][I_STR] == 0) + { + wstandout(hw); + mvwaddstr(hw,1,0,"Please enter the number of a known character: "); + wstandend(hw); + wclrtoeol(hw); + } + else + { + mvwaddstr(hw, 1, 0, ""); + wclrtoeol(hw); + } + + } + while (i < 0 || i > MAXPDEF - 1 || (def_array[i][I_STR] == 0)); + + pstats.s_str = def_array[i][I_STR]; + pstats.s_intel = def_array[i][I_INTEL]; + pstats.s_wisdom = def_array[i][I_WISDOM]; + pstats.s_dext = def_array[i][I_DEXT]; + pstats.s_const = def_array[i][I_CONST]; + pstats.s_charisma = def_array[i][I_CHARISMA]; + pstats.s_hpt = def_array[i][I_HPT]; + pstats.s_power = def_array[i][I_POWER]; + player.t_ctype = char_type = def_array[i][I_CTYPE]; + max_stats = pstats; + + return(TRUE); +} + +void +puta_player(void) +{ + FILE *fd; + char pbuf[2 * LINELEN]; + char filename[200]; + int i; + char *class = which_class(player.t_ctype); + + sprintf(pbuf, "You have a %s with the following attributes:", class); + mvwaddstr(hw, 2, 0, pbuf); + wclrtoeol(hw); + + sprintf(pbuf, + "Int: %d Str: %d Wis: %d Dex: %d Con: %d Cha: %d Pow: %d Hpt: %d", + pstats.s_intel, + pstats.s_str, + pstats.s_wisdom, + pstats.s_dext, + pstats.s_const, + pstats.s_charisma, + pstats.s_power, + pstats.s_hpt ); + + mvwaddstr(hw, 3, 0, ""); + wclrtoeol(hw); + mvwaddstr(hw, 4, 0, pbuf); + wclrtoeol(hw); + mvwaddstr(hw, 5, 0, ""); + wclrtoeol(hw); + mvwaddstr(hw, 0, 0, "Would you like to save this character?"); + wclrtoeol(hw); + + + wrefresh(hw); + + if ((readcharw(hw) & 0177) != 'y') + return; + + do + { + mvwaddstr(hw, 0, 0, "Overwrite which number? "); + wclrtoeol(hw); + wrefresh(hw); + get_string(pbuf, hw); + i = atoi(pbuf) - 1; + + if (i < 0 || i > MAXPDEF - 1) + { + wstandout(hw); + mvwaddstr(hw, 1, 0, "Use the range 1 to"); + wprintw(hw, " %d!", MAXPDEF); + wstandend(hw); + wclrtoeol(hw); + wrefresh(hw); + } + } + while (i < 0 || i > MAXPDEF - 1); + + /* Set some global stuff */ + + def_array[i][I_STR] = pstats.s_str; + def_array[i][I_INTEL] = pstats.s_intel; + def_array[i][I_WISDOM] = pstats.s_wisdom; + def_array[i][I_DEXT] = pstats.s_dext; + def_array[i][I_CONST] = pstats.s_const; + def_array[i][I_CHARISMA] = pstats.s_charisma; + def_array[i][I_HPT] = pstats.s_hpt; + def_array[i][I_POWER] = pstats.s_power; + def_array[i][I_CTYPE] = player.t_ctype; + + /* OK. Now let's write this stuff out! */ + + get_chr_filename(filename, sizeof(filename)); + + + if ((fd = fopen(filename, "w")) == NULL) + { + sprintf(pbuf, "I can't seem to open/create urogue.chr."); + mvwaddstr(hw, 5, 5, pbuf); + mvwaddstr(hw, 6, 5, "However I'll let you play it anyway!"); + mvwaddstr(hw, LINES - 1, 0, spacemsg); + wrefresh(hw); + wait_for(' '); + + return; + } + + fwrite(def_array, sizeof(def_array), 1, fd); + fclose(fd); + return; +} + +void +do_getplayer(void) +{ + print_stored(); + + if (char_type == C_NOTSET) + do + { + /* See what type character will be */ + + mvwaddstr(hw, 3, 0, "[a] Fighter\t" + "[b] Paladin\t" + "[c] Ranger\n" + "[d] Cleric\t" + "[e] Druid\t" + "[f] Magician\n" + "[g] Illusionist\t" + "[h] Thief\t" + "[i] Assasin\t" + "[j] Ninja"); + + mvwaddstr(hw, 0, 0, "What character class do you desire? "); + wrefresh(hw); + char_type = readcharw(hw) - 'a'; + + if (char_type < C_FIGHTER || char_type >= C_MONSTER) + { + wstandout(hw); + mvwaddstr(hw, 1, 0, "Please enter a letter from a - j"); + wstandend(hw); + wclrtoeol(hw); + wrefresh(hw); + } + else + { + mvwaddstr(hw, 1, 0, ""); + wclrtoeol(hw); + } + } + while (char_type < C_FIGHTER || char_type >= C_MONSTER); + + player.t_ctype = char_type; +} + +void +print_stored(void) +{ + int i; + char *class; + char pbuf[2 * LINELEN]; + + wstandout(hw); + mvwaddstr(hw, 9, 0, "YOUR CURRENT CHARACTERS:"); + wstandend(hw); + wclrtoeol(hw); + + for (i = 0; i < MAXPDEF; i++) + { + if (def_array[i][I_STR]) + { + class = which_class(def_array[i][I_CTYPE]); + + sprintf(pbuf, + "%2d. (%s): Int: %d Str: %d Wis: %d Dex: %d Con: %d Cha: %d" + " Pow: %d Hpt: %d", + i + 1, + class, + def_array[i][I_INTEL], + def_array[i][I_STR], + def_array[i][I_WISDOM], + def_array[i][I_DEXT], + def_array[i][I_CONST], + def_array[i][I_CHARISMA], + def_array[i][I_POWER], + def_array[i][I_HPT]); + + mvwaddstr(hw, 11 + i, 0, pbuf); + + } + else + { + sprintf(pbuf, "%2d. ### NONE ###", i + 1); + mvwaddstr(hw, 11 + i, 0, pbuf); + } + } +} + +char * +which_class(int c_class) +{ + char *class; + + switch (c_class) + { + case C_FIGHTER: + class = "Fighter"; + break; + case C_MAGICIAN: + class = "Magician"; + break; + case C_CLERIC: + class = "Cleric"; + break; + case C_THIEF: + class = "Thief"; + break; + case C_PALADIN: + class = "Paladin"; + break; + case C_RANGER: + class = "Ranger"; + break; + case C_DRUID: + class = "Druid"; + break; + case C_ILLUSION: + class = "Illusionist"; + break; + case C_ASSASIN: + class = "Assasin"; + break; + case C_NINJA: + class = "Ninja"; + break; + default: + class = "Monster"; + break; + } + + return (class); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/history.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/history.txt Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1469 @@ +# +# history.txt +# +# UltraRogue: The Ultimate Adventure in the Dungeons of Doom +# Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong +# All rights reserved. +# +# See the file LICENSE.TXT for full copyright and licensing information. +# + +2.01 - Dec 17/84 +1) MAXTRAPS set to 20, up from 15. the dungeon gets nastier +2) hitpoints gained per level change/loss increased by 50%. + this will be essential when new monsters added and amulet + level is increased to 50 or so. + +2.01 - Dec 21/84 +1) MAXPDEF set to 4 (maximum number of saved characters). + +2.01 - Dec 23/84 +1) all load control and time code now non-optional. controlled + by external variables initialized in tunable.c. load average + is now read by a separate program and obtained via popen(3). +2) 2 new armors added, mithril and crystalline. +3) when wearing crystalline armor, a wielded weapon can be turned + into a wand of lightning by bolts of lightning from monsters + (75% chance) +4) blessed (enchanted) food lasts 3 times as long as regular food + up to 6000 turns (regular food is 2000 max) +5) tidied up messages so they all have periods on the end, etc. +6) not being able to play because of holiday() code is distinguished + by a separate message +7) when wearing crystalline armor, monsters that normally hug won't + +2.01 - Dec 24/84 +1) fixed handling of being hit by lightning shot by yourself +2) fixed inventory message for sticks with one charge +3) a highly charged (>50) stick or weapon of lightning can do extra damage + +2.01 - Dec 25/84 +1) mithril armor decreases by 25% the chance of being hit by magic + or wands (VS_MAGIC and VS_WAND -5) except by silver arrows +2) all forms of teleportation cause confusion unless you are currently + affected by a clear thought potion. wizard teleportation via ^X + is exempt +3) falling through a maze or normal trapdoor can cause damage and you can + die from it. also, you are confused unless currently affected by a + clear thought potion +4) when askme is set, more prompting for names is made for scrolls and + potions + +2.01 - Dec 26/84 +1) fixed bug for when wizard or after reading scroll of creation, making + an object of which there is only a single type does not redisplay the + level +2) ring of teleportation can also cause confusion. oversight in changes + made in other code +3) 19 new weapons added. list of weapons used to start with has not + changed +4) calculation of worth of multiply occuring objects changed to include + multiplying by the count of the number of objects +5) silver arrows are aimed by magic and so miss only if monster saves + against magic and the arrow otherwise would have missed anyway +6) fixed bug if entering wizard's password from the command line and is + typed in wrong, then a game restore is attempted +7) changed format of asking for object creation to allow more objects + to be chosen +8) format of name being saved in the score file is changed to indicate + level number and level name of the person + +2.01 - Dec 27/84 +1) added 17 new monsters. the scorefile name of the monster that killed + you is incompatible in indexing, but that's life. + +2.01 - Dec 30/84 +1) wererats can now summon giant rats, up to 4 of them +2) ogres are now greedy + +2.01 - Jan 2/85 +1) elves that carry bows have a 10% chance of carrying silver arrows +2) slightly higher probabilities of traps being successfully set +3) format of top ten adventurers output changed to two lines for + neatness since more stuff added + +2.01 - Jan 3/85 +1) a cursed scroll of create monster now creates 3 to 6 monsters + around you. a normal one and a blessed one create just one + +2.01 - Jan 6/85 +1) amulet moved up to level 50 for safety :-) +2) added new weapons, including a claymore and a footbow +3) some of new weapons now available during initial selection + +2.01 - Jan 7/84 +1) more weapons added +2) code to handle creation of objects completely rewritten to no longer + have a limit on the number of items +3) fixed bug in wizard monster creation routine to allow ESC to terminate + without selection + +2.01 - Jan 11/85 +1) fixed code in chase.c so that a monster can actually use a footbow + if it's carrying one +2) fixed bug in chase.c that wrong pointer is used to point to a + monster's silver arrow. it turns out that the code executes as + expected but the comments are then wrong + +2.01 - Jan 13/85 +1) added fire traps as another type of trap. needed for burning oil +2) changed name of molotov cocktails to burning oil. when thrown and + misses monster, it makes a fire trap and also lights up the room +3) 6 new rings added - carrying - pack loses 1/3 of its weight + - adornment - worth 1000 gp, but nothing else + - levitation - avoids traps and things like that + - fire resistance, lightning resistance, and + cold resistance - obvious +4) certain monsters will die instantly when hit by burning oil + +2.01 - Jan 14/85 +1) full damage is always done with burning oils except on fireproof + monsters +2) monsters that divide will not do so when hit by burning oil +3) fixed bugs in messages for firetraps + +0.00 Alpha - Jan 15/85 +1) flameproof monsters aren't burnt by burning oil +2) version and name of game changed to UltraRogue. hopefully, + it will live up to it's name +3) format of scoring output changed to remove redundant information +4) crysknife can poison now. if the monster doesn't save against + poison, it loses half its hit points +5) flameproof monsters ignore firetraps and can run through them +6) many special properties of weapons changed from hardcoded names + to flags +7) boomerangs and other weapons that return do so now +8) maximum attempts to set traps per level changed from 8 to 16 +9) maximum transactions at a trading post changed from 4 to 8 + +0.00 Alpha - Jan 16/85 +1) corrected code for handling of silver and poisoned weapons + +1.00 Alpha - Feb 1/85 +1) it's now possible to roll crystalline armor at start +2) enchanting armor causes it to weigh 20% less for each + and 20% more + for each - + +1.00 Alpha - Feb 4/85 +1) quitting uses experience point score instead of gold +2) corrected handling of weight of enchanted armor +3) discovering a firetrap lights the room +4) it's now possible to die from fire traps + +1.00 Alpha - Feb 5/85 +1) changed weight of enchanted objects in wrong place. now fixed +2) minor fix to messages in trader.c +3) changed the effect of a scroll of acquirement. a blessed scroll + now works as in old version, a normal or cursed scroll will + allow acquirement of an object of random type. +4) split rogue.c into rogue.c and monsdata.c + +1.00 Alpha - Feb 10/85 +1) armor doesn't rust if you're not wearing any +2) added 8 artifacts, now trying to make rogue compile again +3) time window for restored games changed to 60s instead of 25s. + +1.00 Alpha - Feb 13/85 +1) misc. tidying up of messages. some changed to be wizard only + messages +2) one unique monster added, have not yet tested nastiness of beast +3) changed chasing code to hopefully remove bug that stops running + in a room even though monster is invisible or can surprise +4) seeing distance is now a variable so that later enhancements can + be made +5) added apply command ('A') to make an artifact work (does nothing + right now) +6) it is now possible to trip and fall down the stairs, and die + in the process +7) blessed objects weigh 20% less and cursed weigh 20% more +8) fighting (but not to death) allowed until 1/3 of max hit points +9) time of stiffening by basilisks, etc., is now randomized +10) tidied up code to handle signals and autosaving +11) poison pool traps implemented +12) quaffing a potion of haste self when already hasted lasts a + longer time than before +13) if strength is high enough, the hero can break free of + a hold +14) creation of artifacts now supported (sort of) +15) scoring routines changed to track gold and experience separately. + now i have to figure out how to set the gold to zero without + resetting the score file +16) gold is counted (including worth of objects) even for quitting and + dying. however, position in scorefile is determined by experience + points only +17) three new scrolls 1) nothing - does nothing + 2) silver plating - magic aimed weapons seldom miss + 3) ownership - make weapons return +18) 6 types of food now - details still to be fixed up +19) corrected handling of normal and cursed scrolls of acquirement + +1.00 Alpha Feb 14/85 +1) creation of artifacts mostly working now. no code to support + their special functions yet +2) corrected names of artifacts and food so the inventory routine + gives correct case and plural forms of names +3) handling of duration of enchanted food fixed + +1.00 Alpha Feb 15/85 +1) creation, picking up, dropping, and throwing artifacts now apparently + correct. no functions yet implemented yet +2) mad wizard changed to mad sorceress and is no longer confused. + level, intelligence, experience points and damage increased +3) nymphs try to steal the most valuable magic item carried now +4) picking up an artifact gives experience equal to 0.1 of it's + worth in gold +5) new wizard command 'V' added to find the worth of an object + +1.00 Alpha Feb 16/85 +1) monsters that steal magic and gold no longer vanish when they've + stolen an item. instead, it turns invisible and tries to run away. + a nymph isn't so bad, but watch out for the mad sorceress. + the monster will carry around the item until killed +2) picking up things with a full pack in a trading post no longer + identifies it +3) falling into a poison trap does not automatically poison current + weapon and there is a 75% probability of losing 1/3 hit points + and 2 strength +4) buying anything in a trading post tells you what it is and all others + of the same type +5) quaffing a potion of healing at or near max hit points increases + maximum by more +6) a wand of nothing does nothing +7) cursed scroll of ownership causes thrown weapon to always disappear + +1.00 Alpha Feb 17/85 +1) corrected handling of wizard scorefile manipulation commands +2) corrected code to create objects via the scroll of acquirement +3) a cursed scroll of acquirement creates a cursed object +4) wearing a ring of levitation allows you to move over any kind of + trap without being harmed +5) poisoned weapons now identified as such in inventory name + +1.00 Alpha Feb 25/85 +1) corrected handling of scoring of gold for total winners +2) changed mad sorceress to be non-unique to make dungeon really nasty +3) added code to calculate cost of silver, poisoned, and owned weapons +4) code to stop running when a monster appears corrected for when + monster can surprise or is invisible + +1.00 Alpha Mar 1/85 +1) cursed potion of see invisible does nothing when wearing ring + of see invisible. +2) putting on a ring of see invisible while blind cures blindness + +1.01 Alpha Mar 4/85 +1) changed encryption scheme used by read/write routines to code + supplied by Michael Mauldin (mlm@cmu-cs-cad.arpa) +2) eating too much causes paralysis for a short time +3) changed rnd (random integer in a certain range) routine to use + a better algorithm +4) changed version string for internal checking to something more + useful + +1.01 Alpha Mar 8/85 +1) changed inv_name to show silver weapons properly +2) changed name of "silver weapon" scroll to "magic hitting" +3) scrolls of ownership and magic hitting now prompt for weapon to + apply scroll to +4) rnd changed back to old algorithm because new one is SLOW + +1.01 Alpha Mar 20/85 +1) picking up an artifact more than once does not add more experience + points +2) new unique and VERY NASTY monster added, Lucifer. only appears + on level one when you have an artifact on you. he is NOT + pleasant. i am certain that he will prevent total winners until + i can tune his parameters. + +1.01 Alpha Mar 22/85 +1) changed maximum objects per normal level from 9 to 5 +2) changed monster window to half it's current value to stretch + out the levels new monsters can appear over +3) increased traps per level again from 20 to 25 + +1.01 Alpha Mar 28/85 +1) decreased abilities of Lucifer to try things out +2) changed probability of food up and other stuff down +3) changed probability of potion of gain level down + +1.01 Alpha Apr 1/85 +1) changed traps per level up to 30 +2) fixed bug in wearing ring of cold resistance +3) changed window for saving to 180s because save/restore with + new algorithm is much slower +4) added support for lairs of unique monsters, 1 in 15 chance of + being summoned while going up the stairs +5) trading posts can't appear until level 15, up from level 5 +6) you can't be blinded when wearing a ring of extra sight +7) hit points per level gained/lost reduced to original values +8) deleted some wizard commands and re-arranged some of the rest +9) changed how wizard teleportation specifies type of level +10) modified parameters of sucessfully striking a monster +11) Lucifer devalued again + +1.02 Alpha Apr 2/85 +1) reduced probability of being summoned at random while running around +2) restored probability of hitting parameter +3) doubled requirements for experience level changes + +1.02 Alpha Apr 4/85 +1) restored requirements for experience level changes +2) increased probability that a monster hits on an attack +3) changed probability of food downwards and other stuff upwards +4) changed probability of some scroll's cursed and blessed probabilities + +1.02 Alpha Apr 5/85 +1) throne room monsters no longer hasted +2) fixed messages for scrolls of magic hitting and ownership +3) infinite loops in wanderer() due to trying to place a monster + in a different room than the hero on a THRONE level +4) changed format of scorefile to include what artifacts were + retrieved +5) initial attributes of a fighter increased +6) Lucifer downgraded again (sigh) + +1.02 Alpha Apr 12/85 +1) food probability adjusted upwards +2) size of string to hold fruit food name increased +3) monsters per treasure room increased to 20 from 15 +4) maximum number of magic objects per level increased from 5 to 7 +5) added code to differentiate a winner carrying all artifacts + and some +6) reduced probability of summoning + +1.02 Alpha Apr 16/85 +1) monsters that can walk through stone are not affected by wand of + antimatter. so much for an easy kill of Lucifer +2) throne rooms always contain treasure, up to 3 times as much as normal + +1.02 Alpha Apr 23/85 +1) max monsters/treasures in a party room upped again from 20 to 30 + +1.02 Alpha Apr 26/85 +1) improved wizard outfitting command to start with more and better stuff +2) fighters gain and lose 12 instead of 10 hit points max per level +3) throne rooms always contain lots of treasure +4) fixed message in experience level changes to include period + +1.02 Alpha Apr 28/85 +1) monsters that can breathe can do so more than once with 50% chance + after each use of losing (or retaining) ability +2) catch zapping and bolts into darkened rooms, passages, and phased + players in walls and flag as error for wizard +3) mad sorceress improved in ability +4) changing name of fruit in options now also changes name in rest of game +5) handling of SIGINT and SIGQUIT corrected for when escaping to shell + and returning. used to exit with endit(). now does quit() to + prompt before exiting +6) options listing now includes name of option for the environment + variable +7) throne rooms made nastier by increasing hit points further and also + decreasing AC of monster even more +8) monsters below level 80 start getting nastier in a lot of ways + and is level dependent (deeper == nastier) + +1.02 Alpha Apr 29/85 +1) all monsters in throne rooms can now walk through walls +2) past level 80, all monsters that can use breath weapons will never + lose that ability +3) Lucifer upgraded + +1.02 Alpha Apr 30/85 +1) algorithm for nastier monsters below level 80 changed (nastier) +2) potion of raise level made much rarer +3) being drained a level is restored only half the time +4) monsters in thone rooms can see invisible heros +5) scrolls of acquirement are rarer and are more likely to be cursed + +1.02 Alpha May 1/85 +1) Lucifer hit points upgraded +2) Lucifer never loses ability to zap with breath weapon +3) fixed bug in returning of weapons with blessed scroll of + ownership read on them +4) circular buffer implemented for messages and each line of buffer + increased in size +5) rings, weapons, sticks, and armor can explode if enchanted too much +6) owned things show up that way in inventory + +1.02 Alpha May 2/85 +1) length of time that all food lasts increased +2) throwing a scare monster scroll curses it + +1.02 Alpha May 3/85 +1) attacking rust monsters with bare hands causes segmentation faults + fixed by checking for whether hero wields a weapon or not +2) evil sorceresses (and nymphs) will steal anything, including + what you're wearing, below level 95 +3) it's now imposssible to enchant a ring of slow digestion beyond 3 + since food consumption code is not designed to handle it + +1.02 Alpha May 6/85 +1) adjusted probability of things exploding when enchanted too much from + 1 in 8 to 1 in 5 +2) being summoned while just wandering in the dungeon is changed + to 1 in 99999 to make life interesting +3) stick of fire now kills monsters that are flammable +4) fixed bug in handling firetraps for monsters +5) corrected new code to handle monsters running through fire traps + +1.02 Alpha May 8/85 +1) fixed bug in wizard dungeon level change command +2) total daemons changed from 30 to 60 to allow for new things +3) fighting while blinded changed from "it" to "the monster" +4) algorithm for making monsters nastier below level 80 made + even nastier +5) fire traps always burn even when levitated +6) fixed monster name lookup in shoot_bolt when bolt misses +7) monster lair traps implemented for levels below 50 +8) rust traps implemented to rust armor +9) messages for CANRUST monsters hitting you after armor is + destroyed is changed +10) placement of player in a throne room corrected +11) unique monsters are always awakened by the hero entering the room +12) a monster with enough strength can shatter crystalline armor with + a single blow +13) a monster with enough strength can make the armor ring when hit, + aggravating all monsters on the level +14) monsters that can shriek can shatter crystalline armor +15) strength of monsters below level 80 also increases with level +16) check more often for null pointers when refering to current armor + in fight.c and monster.c +17) selling or dropping an artifact in a post loses it forever, but + you can still go up the stairs as if you did have it. same with + it being stolen by a monster + +1.02 Alpha May 10/85 +1) creation of firetraps done only when there is room in the traps array +2) potion of extra hearing - cursed, normal and blessed +3) potion of extra scent - cursed, normal, and blessed +4) hero can smell or hear monsters whenever they appear in dungeon + or possibly when he enters a room, always when potions have been + quaffed + +1.02 Alpha May 11/85 +1) scroll of food detection implemented +2) corrected algorithm for blessed food and added message when + eating it so people trying will know to try it again +3) code to handle super food consumption added +4) changed algorithm for ring of slow digestion food consumption + to not use hardwired enchantment levels +5) hearing and smelling new monster messages changed to reflect + distance and rooms somewhat +6) partial implementation of artifact powers completed +7) many of minor malevolent and side effects implemented +8) some major malevolent effects implemented +9) Phial of Galadriel implemented +10) Palantir of Might implemented + +1.02 Alpha May 12/85 +1) fixed dying by fire traps to remove seg fault while trying to + print reason for dying +2) selling an artifact or having it stolen and vanishing when + monster that stole it is killed loses the artifact permanently + and the ability to go upwards is lost if it's the only artifact + in the hero's possesion +3) login name included in scorefile +4) stick of invisibility - makes monsters appear or disappear + cursed (all in room), normal(one in direction), blessed(one + appears in direction) +5) implemented Amulet of Yendor, Silmaril of Ea, Sceptre of Might, + Wand of Orcus +6) partial implementation of Magic Purse of Yendor +7) 20 units of food are consumed every time a artifact power is attempted +8) code for blessed scrolls of magic mapping, gold detection, + food detection, potions of magic detection and monster detection + in place +9) minimal code for wand of invisibility implemented + +1.02 Alpha May 13/85 +1) minimal code for potions of super heroism and disguise added +2) no healing when phased and in rock +3) changed "owned" to "claimed" in inv_name() for weapons +4) wand of invisibility implemented +5) bug in removing rings of add ability fixed when unwearing one +6) leaving rooms when wearing ring of illumination darkens it + +1.02 Alpha May 14/85 +1) rewrote all code to handle changes in the five major player + abilities +2) completed implementation of the Crown of Might +3) implemented blessed mapping and detection abilities +4) more minor effects +5) more major effects +6) copy over login name to scorefile name entry only if it's + not the same as the name entry started with +7) falling down stairs probability shifted according to encumberance +8) undead monsters are not affected by poison +9) ring of regeneration restores hit points faster +10) ring of sustain health stops disease and parasitic infections, but + doesn't cure them + +1.02 Alpha May 15/85 +1) fixed bugs in handling power boosted strength and dexterity +2) implemented potions of disguise and superheroism + +1.02 Alpha May 16/85 +1) changed potion of disguise to be like a scare monster scroll and + shortened length of time it lasts +2) implemented more minor effects +3) cursed scroll of magic mapping done - forgets map +4) fighting stops "hits" and "misses" messages to stop having to + hit the spacebar all the time +5) 1 in 50 chance when fighting to stop fighting completely at random. + a neater way of stopping fighting when neither side is hitting the + other +6) moving over some traps using the 'm' command can result in no + trapping, especially if player is thief +7) asleep on top of a sleep trap doesn't print many messages + about what the trap is +8) reduced nastiness of level-dependent monster characteristics +9) make probability of losing breath weapon dungeon level dependent +10) fixed bug in stopping running when a monster enters room +11) Magic Purse of Yendor code in place but not tested + +1.02 Beta May 17/85 +1) fixed bag of holding code for Magic Purse of Yendor +2) can't put Magic Purse inside itself +3) lowered probability of a nonthief moving over a trap successfully +4) add contents of Purse to score +5) added more major and minor effects +6) lowered probability of an artifact being deactivated by a minor + effect + +1.02 Beta May 18/85 +1) check for no wielded weapon or no armor worn when being hit + by breath weapon +2) check for no wielded weapon when blasting it in default major + effect of artifact activation +3) changed many messages in minor effects to check for blindness +4) it is possible to trip and stumble over your weapon, and die in + the process. poisoned weapons do a lot of damage to the hero +5) fighting or shooting some wands at the quartermaster increases + the cost of things and probability of things being cursed + by other quartermasters and in trading posts. monsters on level + are aggravated +6) fighters using the 'f' or 'F' commands are get bonus number + of attacks dependent upon level per turn +7) when not under influence of potions of extra scent or extra hearing, + thieves have higher probability of hearing or smelling a monster +8) cursed detection scrolls and potions implemented +9) scroll of electrification implemented + +1.02 Beta May 19/85 +1) five new rings added +2) worth of artifacts increased by a factor of 10 +3) when msgline is blank and last message is requested via ^P, + don't decrement message buffer index the same way since it was + not incremented to clear the line +4) initial hit point bonus for constitutions above 15 +5) new command listen ('=') to listen for nearby monsters +6) ring of breathing - player is unaffected by gases +7) ring of free action - player can't be held or slowed except by an + artifact side effect +8) reduced duration of confusion after teleportation +9) ring of wizardry - doubles spell and prayer ability for players + who are not clerics nor magicians +10) listen for monsters nearby with higher probability for thief +11) ring of resurrection - you might come back from the grave: cursed, + normal, and blessed versions +12) ring of teleport control - get to choose position and might actually + end up there: cursed, normal, and blessed versions +13) corrected multi-attack code to allow only fighters +14) cursed detection magic identifies itself for later in the dungeon +15) silver weapons do twice damage to undead monsters +16) source frozen for version as reference. further updates sent + out will be via context diffs + +1.02 Beta May 20/85 +1) modified saving throw for resurrection to take into account current + constitution adjusted for luck and ring values +2) rings of resurrection and teleport control now has enchantment + amount printed +3) a failed resurrection now waits for the user to read the message + before continuing with termination +4) off-by-one error in counting resurrection attempts +5) added diagonal moves to teleport control (sort of) +6) successful control of teleport modified by luck and ring values +7) corrected initialization of resurrection count +8) moved tripping, summoning, and other things inside the test for + valid commands +9) changed algorithm for determining successful resurection with + everything +10) bug in listen command changed you to a thief when using it + +1.02 Beta May 21/85 + +1) a blessed scroll acquirement allows creation of an artifact or + monster even if not wizard +2) infestation is not supposed to be immediately fatal + +1.02 Beta May 22/85 +1) moved new.things (this file) into directory rogue to make updates + via context diffs more easy to apply +2) corrected message of deactivation of artifacts +3) changed definition of .rog_defs to an external variable in + tunable.c for customization purposes + +1.02 Beta May 23/85 +1) termination after load average is too high does a save instead + of just quitting + +1.02 Beta May 25/85 +1) corrected code for adding userid after name in scorefile +2) redisplaying of messages after being cleared by msg("") fixed + +1.02 Beta May 27/85 +1) dungeon level dependent monster ability algorithm made less + nasty +2) throne room monster ability modification algorithm made less + nasty +3) some of Lucifer's abilities made less nasty +4) levitated and phased allows upward movement through rock +5) discovered trading post stays lit when exiting room + +1.02 Beta May 28/85 +1) fixed firetraps combined with rings of levitation +2) fixed messages for major effects blasting of weapon +3) disguise can be seen through by monsters with high experience + for hitting, zapping, and throwing things at the hero +4) increased safe amount to enchant ring from 4 to 5 + +1.02 Beta May 29/85 +1) documented 'f' and 'F' commands under help command + +1.02 Beta June 1/85 +1) increased the value of some items + +1.02 Beta June 3/85 +1) made leprechauns and nymphs harder to kill and more likely + to stay around +2) nymphs now attack whenever possible +3) monsters ignore rust traps now +4) changed help for 'f' and 'F' commands slightly + +1.02 Beta June 3/85 +1) missed change of ".rog_defs" to ROGDEFS + (thanx jason%ucblilac.CC@berkeley.arpa) +2) anything can be claimed now. inventory names and worth of objects + changed to reflect this + +1.02 Beta June 4/85 +1) check for monster wielding a weapon didn't check for null pointer +2) changed #include "stdio.h" to in save.c +3) include mach_dep.h in io.c, save.c, and wizard.c + +1.02 Beta June 6/85 +1) shattering of crystalline armor when hit hard is less likely + when armor is well enchanted + +1.02 Beta June 7/85 +1) check for null pointer to damage string in determining how much + damage a blow or weapon striking does +2) added code from jason@ucblilac.CC@berkeley.arpa for support + of job control and tty interfacing for 4.x bsd systems +3) quaffing non-cursed potion of super heroism removes fear and + also prevents it until super heroism wears off +4) corrected oversights in jason's code for when terminating + by various legal means +5) generalized message for selling/dropping artifact in trading post +6) prices of objects in trading posts are now fixed at object creation + time +7) algorithm for randomizing prices by quartermasters and in trading + posts changed +8) prices as determined by bad luck made worse + +1.02 Beta June 8, 85 +1) corrected tty and job control handling for urogue -s and + urogue restore of saved games option + +1.02 Beta June 9, 85 +1) too much movement when disguised turns it off +2) reduced gain in monster strength below level 80 +3) improved resistance of crystalline armor to shattering when hit +4) confused monsters can lose their confusion +5) food detection also looks in monster's packs too +6) evil sorceresses and nymphs can curse something in your pack + while they are looking through it +7) fix tty characteristics when restoring a game +8) improved chances of taking it with you when you are resurrected +9) corrected author and SIGQUIT handling + +1.02 Beta June 10, 85 +1) selling an object in a trading post marks the value in a private + location in case hero wants to buy it back +2) reduced slightly the chance of nasty side effects when using + artifacts +3) shattering armor removal algorithm fixed +4) lightning proof and being zapped by lightning checks for no armor + and/or no wielded weapons +5) change Makefile to be more efficient when maintaining distribution + and private copies by linking only once instead of twice +6) going to a new level and not wearing any armor causes seg faults + when testing for greedy monsters +7) null pointers when calculating damage caused by weapons handled + incorrectly +8) setting traps, traps due to falling objects that create traps, + and artifact side effects have more room by making trap array + bigger + +1.02 Gamma June 19, 85 +1) put in changes and moved things around in preparation for + changes to be received from dan@ciprico for sys3 (aka USGV3) + unix. generalized conditional compilation code to allow + further extension to multiple system source code versions. no + recompilation is neccesary with these changes. + +1.02 Gamma June 21, 85 +1) fixed bug in quaffing gain ability when dexterity or strength + has been lost +2) wearing a ring of wizardry doubles spell points for casting and + praying for all player classes provided that they have enough + ability to cast or pray + +1.02 Gamma June 24, 85 +1) gazing monsters have no effect when hero is invisible +2) reduced probability of losing disguise when moving about +3) leprechauns now attack + +1.02 Gamma July 7, 85 +1) can't read scrolls when blind +2) test for null pointers when refering to weapons and armor + in do_minor() in artifact.c +3) taking off armor removes stone from shoe + +1.02 Gamma July 8, 85 +1) corrected flag handling for nymphs and evil sorceresses + cursing an object that hero is carrying + +1.02 Gamma July 10, 85 +1) handling of ring of wizardry and spell points for clerics and + magicians fixed + +1.02 Gamma July 12, 85 +1) fixed handling of wizard spell points because of limitations in + certain C compilers. thanx to dan@ciprico +2) reset CBREAK mode in rip.c routine score() so that showpack() can + prompt and wait for a blank to continue displaying items +3) reduced slightly the probability of a monster hitting back to + original values as received from edjames@ucbshadow +4) incomplete changes to handle the ring of wizardry in casting + and praying + +1.02 Gamma July 18, 85 +1) make dexterity of a monster level dependent past level 60 of + dungeon + +1.02 Gamma July 21, 85 +1) silver weapons can't rust no matter what +2) hitting a rusting monster with a non-metallic weapon no longer + tells you the rust vanishes +3) no message of rust vanishing from your armor if it is + inherently rustproof +4) fixed grammar on messages about falling through various traps + +1.02 Gamma July 23, 85 +1) changed code to ensure quartermasters sell things at positive + cost only +2) quaffing a blessed potion of gain ability sometimes increases + the player's body AC as well +3) fixed initialization code for fighters to give them extra + body AC and also more damage from bare handed attack + +1.02 Gamma July 26, 85 +1) wearing a ring of adornment also causes greedy monsters to run + after you +2) monster's level dependent attributes are dependent upon the maximum + level you have been +3) a blessed scroll of magic mapping show traps and such too +4) monsters stepping into firetraps while hero isn't in room + also marks the room as lit + +1.02 Delta July 28, 85 +1) sys3 support added, courtesy dan@ciprico +2) new random number routines added to the optional files + for people without srandom and random + +1.02 Delta, July 29, 85 +1) teleporting turns off being held by a bear trap +2) being hit by a breath weapon while running stops you running +3) replaced Makefile.install with one supplied by jason@ucblilac +4) fighting mummies with no weapon usually causes seg faults so + pointer must be checked (dan@ciprico) +5) entering a room through a secret door while phased is supposed to + light the room (dan@ciprico) +6) trying to dip where there's no pool gives wrong message + +1.02 Delta, July 30, 85 +1) added support for news option in command line +2) quit signals produce core files only in wizard mode +3) wearing a ring of adornment adds 4 to number of transactions + allowed in trading posts +4) ring of burden deleted and replaced by ring of vampiric regeneration +5) ring of carrying comes in various degrees now and cursed is like + the ring of burden used to be +6) ring of vampiric regeneration restores amount of damage done to + monster to player, sort of like a vampire does +7) ring of regeneration hit points now made somewhat experience + level dependent +8) food consumption of the regeneration rings make experience + level dependent +9) wand of blasting and grenades implemented + +1.02 August 9, 85 +1) baseline release for urogue +2) friendly fiend wants a ring of adornment +3) player startup body AC initialized wrong +4) negative transaction count after selling ring of adornment fixed +5) fixed opening of authors file + +1.02 August 11, 85 +1) inventory name of a claimed ring had blanks in wrong place +2) fixed printing of inventory and counting of things inside + the Magic Purse of Yendor + +1.02 August 12, 85 +1) Closes PERMOK and PASSCTL after they are no longer needed. This is + a good practice, and was part of the reason for screwing up our server. +2) Quaffing a restoration after superheroism had nasty effects. +3) Screen no longer clears right after score file is displayed on death. +4) Changing your Ac from 10 would not affect status line. +5) Expression tree overflowed on trader.c using the 4.1 compiler. + + above changes by tecot@cmu-cs-k.arpa. the system they run has + a "networked" filesystem under 4.1 bsd. + +6) a thrown grenade hitting a monster also explodes and makes a lot + of noise +7) probabilities of magic items of various kinds changed to make + things harder +8) costs of things re-arranged to reflect value to player more + accurately + +1.02 August 13, 85 +1) being zapped by a breath weapon while not wearing armor causes + core dumps + +1.03 Alpha, February 16, 86 +1) added 100+ monsters to the monster table with new attributes + supported +2) added mike cooper's new character classes +3) added mike cooper's changes to inventory display management to + be more like Berkeley rogue 5.x +4) changed experience level management to allow effectively no + limit on experience level +5) spell and prayer points are now taken from the same pool +6) healing rate reduced to make life harder +7) code to correct sun compiler laziness that is handled by + other compilers +8) redo flag structure to extend to 400+ monster flags +9) display remaining spell points in menu of spells to cast/pray +10) healing rates reduced +11) control rewritten to do what it says in README +12) README updated to tell about the environment variable +13) MOTD, PAGER, NOPLAY, and NEWS files supported for various + messages and display of same +14) uptime output is used for load control +15) control uses the login name rather than uid for authorization +16) code for RTU2 and 4.2 RT PC versions added +17) fixed up some sun problems +18) FLYing and FAST monsters move twice as quickly when not next + to hero +19) melee attack bonus for various character classes computed + by a function instead of code in the if statement +20) CTRL- keys deleted and option used for determining + type of running +21) fixed saving throw modification for mithril armor +22) changed message for monsters stealing magic +23) changed environment variable to UROGUE +24) dungeon-level-dependent attributes start sooner now +25) more weapon flags +26) silver weapons become metal ones too +27) monsters appear more often + +1.03 Alpha, February 21, 86 +1) added changes to circumvent compiler bug in 4.2BSD on the RT PC +2) corrected maximum number of levels displayed in player level + calculation +3) corrected calculation of needed experience points to gain a + new level after losing a level +4) thrown weapons at gas spores kill them +5) synchronized messages and experience level numbers when + gaining levels + +1.03 Alpha, February 22, 86 +1) Friendly Fiend appears in a trading post and follows you around +2) minimal code for support of CHARMED and FRIENDLY monsters +3) Friendly Fiend gets upset if you cast spells in his place + +1.03 Alpha, February 22, 86 +1) fixed up Friendly Fiend handling when he has been hit or + "killed" +2) changed ninja experience level names +3) added paladin praying abilities at level 9 no matter what wisdom +4) reduced Friendly Fiends abilities +5) shops become normal levels for picking up objects when Friendly Fiend + is "killed" +6) handle a one room ordinary level properly +7) once Friendly is "dead", shop items are identified when picked up + +1.03 Alpha, February 28, 86 +1) applied bug fix in inventory code sent by Mike Cooper +2) added AT&T 7300 sys5r2 support +3) implemented Jason Venner's improvements to handling getting the + load average + +1.03 Alpha, March 9, 86 +1) applied fix to too many saves running out of file descriptors + submitted by Web Dove +2) applied fixes to messages and stuff for the quartermaster from + Mike Cooper + +1.03 Alpha, March 14, 86 +1) applied bug fix by mike cooper in display of quartermaster's + wares to sell +2) applied mike cooper's patches for sys5 curses support + +1.03 Alpha, March 15, 86 +1) added flags for size of monster and size of weapon and initialization +2) changed whatis command to display all possible monsters that match + the characters + +1.03 Alpha, April 14, 86 +1) incorporated random() and srandom() from 4.2BSD + +1.03 Alpha, April 18, 86 +1) fixed segv in control.c using patch from mike cooper + +1.03 Alpha, April 19, 86 +1) fixed screen clearing in initialization and termination for sys5 +2) added option to print version number +3) doubled healing rate +4) potion of gain ability is much rarer now +5) Tiamat and Bahamut can now summon dragons (suggested by carl hommel) + +1.03 Alpha, April 20, 86 +1) fixed up terminal state when exiting +2) removed wizard scorefile commands from rip.c +3) fixed testing for valid initialization of player class + +1.03 Alpha, April 28, 86 +1) added SAVETIME and WARNTIME to allow installations to tune + how strict they are on load checking +2) changed treasure room generation and room darkness constants + to create them less often and starting deeper +3) require return at game termination to handle windowing + systems +4) changed #ifdef and #ifndef to #if defined() and #if !defined() + +1.03 Alpha, May 2, 86 +1) fixed up missed #ifdef changes +2) fruit name initialization screws up on very long names +3) monsters with multiple turns getting killed on their first + turn should never get a second turn (Mike Laman fix) +4) dropping and picking up multiple stuff sometimes screws up count + (another Mike Laman fix) +5) enhancement by Carl Hommel to zapping: save = 1/2 damage, + rather than no damage. bolt damage peters off towards the end + of the zap + +1.03 Alpha May 5, 86 +1) changed order of message and flag clearing for normal scroll + of remove curse to have a more consistent message +2) the gold sense command has been removed and it is automatically + done for thieves whenever a new level is created + +1.03 Alpha May 11, 86 +1) fixed minor formatting error in last prompt for carriage return + before exiting +2) tons of minor and major bug fixes by Mike Laman, some pointed to + by lint and others from looking at code, with more for me to fix + (sigh!), including sys5 curses things and temporary absence of + brain + +1.03 Alpha May 12, 86 +1) putting on a ring of free action turns off being held and those + monsters can never hold hero again (ever) +2) diddled around some more with format of prompt before exiting +3) changed define of "reg" in curses.h to have less warning messages +4) added a single new monster +5) changed name of NUMUNIQUE define in rogue.h to NUMSUMMON +6) use vowelstr() in giving name of monster summoning hero + +1.03 ALpha May 19, 86 +1) added a whole lot of things by Carl Hommel including different + algorithms for shooting bolts, new armor and weapons, and + general tidying up of things + +1.03 Alpha, May 20, 86 +1) added 67 new monsters from Carl Hommel and miscellaneous bug fixes + +1.03 Alpha, May 21, 86 +1) initial support for SCO/Microsoft Xenix implementation of sys5r2 + supplied by Tom Haapanen +2) fixed monster flags in new monsters supplied by Carl +3) changed number of new monsters/level constant from 3 to 4 + (this number is too high given the actual number of monsters in + the monster table and the levels one has to go down. there + should be 320 non-summoning monsters to be about right) +4) healing rate restored to original value + +1.03 Alpha, May 22, 86 +1) protected and claimed objects stay with the hero when he + resurrects +2) no saving throw against magic missile +3) magic missile damage depends upon player exp level +4) electrification bug fixed and range depends on hero exp. level +5) stepping into a pool when electrified is painful +6) monsters summon only when low on hit points and damage taken + above changes by Carl Hommel +7) number of magic items per level increased +8) bunches of monsters created less frequently +9) summoning while running around reduced in probability +10) don't kick any daemons off when being summoned to give initiative + to the player +11) increase the number of objects in a throne room + +1.03 Alpha, May 24, 86 +1) changed algorithm for creating monsters with CANSHOOT weapons + and increased probability slightly +2) added new monsters from Carl Hommel and a bunch of my own +3) rearranged the hero-summoning unique monsters to be strictly + in order of hit points +4) rearranged some of ordinary monsters to make progession of + experience points for killing monster more monotonic +5) fix handling of extra long monster names in wizard monster creation + and monster genocide + +1.03 Alpha, May 25, 86 +1) more modifications in and around chase.c, command.c, and fight.c + to remove the electrification bug. Carl's fixes removed one, + the one that Jason reported is still there. +2) maximum traps/dungeon level increased +3) massively rewrite Makefiles, installation procedure, and + various sources to reorganize sources + +1.03 Alpha, May 28, 86 +1) charmed monsters, new spells for druid and illusionist, + monsters striking each other, etc. by Nick Flor +2) save throw handling changed to be centralized in one routine, + blessed/cursed stick handling, and wizard ^O command improved + by Carl Hommel +3) fixed save throw test for monster and hero differentiation +4) monster is not allowed to hit itself + +1.03 Alpha, May 29, 86 +1) fixed pointer to struct bug save_throw() in fight.c + +1.03 Alpha, June 4, 86 +1) check for NULL pointer when zapping with wand +2) potions have some food value +3) fix body AC for ninja +4) fix ucount for RTU3 + obove fixes by Carl Hommel +5) including files in wrong order in main.c and rip.c pointed + out by Mike Cooper + +1.03 Alpha, June 5, 86 +1) new program to replace all the print_* commands by Mike Cooper +2) fixed bug in urprint to use right flag word size and to use + correct number of flag words in monster initialization structure +3) experiment with allowing many familiars but only one at a time + +1.03 Alpha, June 6, 86 +1) fixed format of scorefile output +2) familiars are never confused +3) changed algorithm for generating a summoned familiar + +1.03 Alpha, June 7, 86 +1) yet another change to generating summoned familiar +2) not clearing familiar_ptr when familiar is killed +3) handling of monsters that may be friendly with some + probability +4) handling of monsters that do not breathe air +5) algorithm for blessed scroll of charm monster changed +6) groups of monsters will all be friendly or none +7) all familiars can use armor and weapons +8) familiars have extra hit point and better ability scores + based upon the hero's experience level +9) fixed up a few messages here and there + +1.03 Alpha, June 9, 86 +1) handling of quartermaster selling stuff that is overpriced fixed +2) enchanting a stick might bless/curse it +3) new potions and powers to go with them and other misc. modifications + such as food value for potions, change in damage done by a firetrap, + and other miscellaneous stuff + above by Carl Hommel +4) set familiar_ptr to NULL when going to a new level +5) use ISGOD flag in monsdata.c to give special abilities to gods + dynamically +6) move trading post and throne rooms display lower on screen +7) changed ISBIG flag to ISTWOH for weapons +8) added various degrees of friendliness into monster table +9) make temporary buffer in inventory() bigger + +1.03 Alpha, June 10, 86 +1) added yet another fix for summoning when scroll of charm monster + been read and monsters are still alive +2) handle passing of pack to next familiar when going to a new level +3) CANWIELD and CANSHOOT monsters pick up things on the dungeon + floor too if they are weapons or armor + +1.03 Alpha, June 11, 86 +1) ISSCAVENGE is not turned on automatically for familiars +2) monsters created by a normal and cursed scroll of create monster + are not friendly +3) monsters summoned by another monster are never friendly +4) normal and blessed wand of polymorph leave friendly monsters + friendly + +1.03 Alpha, June 11, 86 +1) fix for updating mw correctly by Pat Place +2) remove Dr. C. reference in vers.c +3) remove extra turn wizard message from do_chase() +4) fixed rogue/Makefile to find hdrs correctly for lint + +1.03 Alpha, June 13, 86 +1) potion of continuous breathing R_BREATHE, turns on HASOXYGEN +2) potion of flying CANFLY. hero is levitated, and have rnd(2) + of getting an extra attack +3) CANFLY means never setting off non-fire traps +4) HASOXYGEN means never getting zapped by a pool or gas trap, + or breath weapons +5) NOSHARP not affected by arrow or dart damage but affected poison +6) ISUNDEAD and CANPOISON not affected by dart poison +7) you don't automatically know what cursed potions are +8) wizards get a higher chance to ressurect +9) prayer command changed + above by Carl Hommel + +1.03 Alpha, June 14, 86 +1) wearing a ring of wizardry gives spell abilities no matter what + intelligence or wisdom ability is +2) implemented armor and weapon restrictions by player class +3) preparations for having shield/cloak/charms and eventually + rings as all objects to wear +4) renamed short pike to ranseur and long pike to pike to be more + historically accurate +5) corrected testing of genocided monsters in summon() +6) create lots of monsters when making them standing still in + the dungeon +7) use killed() to throw away the monster list when generating a + new level of the dungeon because it recovers storage from + objects that monsters are carrying + +1.03 Alpha, June 15, 86 +1) changed startup procedure to enforce armor and weapon restrictions +2) hero goes through trading post at startup to outfit pack +3) no more rolling of player characteristics +4) druid charm monster prayer now a very early and cheap spell +5) hero may have more things in pack depending upon player class +6) charm monster depends slightly on hero experience level too + +1.03 Alpha, June 16, 86 +1) fixed up pointer usage in discarding monster list +2) increased purse that hero starts up with +3) fixed up format of display of saved heros and new ones at startup +4) increased maximum number of saved heros +5) changed how wizard commands are started up to get around + all the control character restrictions (Mike Cooper) +6) added keypad support for terminals that have them (Mike Cooper) + +1.03 Alpha, June 17, 86 +1) fixed up a lot of bugs in handling familiars (Nick Flor) +2) improved steal() routine from Carl Hommel + +1.03 Alpha, June 18, 86 +1) fixed up garbage display on screen left over from throwing away + monsters using killed() +2) fixed multiple messages for killing only one gas spore +3) added HASOXYGEN flags to monsters that are not affected by + gas breath weapons + +1.03 Alpha, June 19, 86 +1) removed code to curse all things bought from a quartermaster +2) fixed up FRIENDLY and CHARMED monsters not moving +3) fixed up monsters picking up things and their display +4) killed() called with NULL killer from shoot_bolt() shouldn't be +5) hopefully fixed up stuff for killing Friendly Fiend in new_level() +6) allow more than 1 digit to indicate saved player number + +1.03 Alpha, June 22, 86 +1) check order of inclusion of rogue.h and mach_dep.h and curses.h +2) fixed up message in Friendly Fiend's place +3) simplified expression too complex in move.c +4) fixed up display of objects picked up by monsters +5) probability of major side effect of artifacts reduced +6) urogue save file name made definable by installer +7) prompting to save character done only when new one rolled +8) friendly monsters next to hero run around when '.' is being hit +9) character displayed when monster killed updated from dungeon + level map instead of using abritrary character '.' +10) lighting of room when monsters and weapons are just outside + doorway fixed +11) paladin's get better saving throws against anything +12) rangers and ninjas are hardly ever surprised +13) paladins never get diseases or infections + other misc player class enhancements by Carl Hommel + +1.03 Alpha June 24, 86 +1) yet another null pointer usage bug when monster can't be found + fixed by Pat Place +2) when a missile fired by a monster misses another one, it wakes + up anyways +3) garbage characters when killing a monster are displayed using + unctrl() in wizard message +4) paladin's have no fear +5) fixed up setting of hero AC during startup +6) fixed up range of numbers testing in geta_player() and puta_player() +7) don't add anything to familiar's pack if there is nothing to add +8) & no longer used as suspend in BSD versions +9) more cash given to heros at startup time + +1.03 Alpha, June 25, 86 +1) deleted some macros from rogue.h that are used only in a few places +2) turn off ISMEAN flag from familiars and friendly monsters + +1.03 Alpha, June 26, 86 +1) changed deletion of monster in killed() to after monster's pack + has been deleted +2) fixed up room lighting problem permanently +3) minor bug fixes +4) yet more removal of macros + +1.03 Alpha, June 28, 86 +1) created new include files and deleted some macros +2) implementation of ring of carrying changed to reduce weight + by 150 gold pieces per enchantment +3) amount of gold generated increased +4) backstabbing for thief, ninja and assassin character classes +5) monsters can wield weapons against hero +6) amount of gold that hero starts with increased again +7) initialization of sticks for hero at startup fixed +8) frequency of monster creation reduced +9) pluses of weapons used by monsters increased by a random + function of the experience level of the monster + +1.03 Alpha, June 29, 86 +1) fixed fighting of monster carrying no weapons +2) message in wrong place for monster thrown weapon that misses +3) more fixing of room lighting and darkening bugs +4) fixed lighting of rooms during new level creation +5) increased starting gold once again + +1.03 Alpha, June 30, 86 +1) changed monster that Friendly Fiend summons +2) monsters that CANSHOOT pick from a wider variety of weapons +3) monsters that CANWIELD don't always +4) monsters zapped run toward the hero +5) split creat_mons() into creat_mons() and place_mons() routines + so that other programs can use it too +6) renamed familiar_* to fam_* +7) reduced probability of monsters in a given room when level is + generated + +1.03 Alpha, July 1, 86 +1) algorithm for assignment of wielded weapons changed +2) fixed stupid typo error in place_mons() in scrolls.c +3) monsters that CANTELEPORT do so when running away + +1.03 Alpha, July 2, 86 +1) electricity zapping a friendly or charmed monster loses the + hero a friend +2) fixed bug in place_mons() in scrolls.c; stupid typo + +1.03 Alpha, July 5, 86 +1) undid changes in killed() in fight.c to discard monster pack +2) corrected setting of fighting in killed() +3) removed useless variables in rogue.c +4) fixed initialization of character type in geta_player() +5) fixed typo in secretdoor() in misc.c causing them to show as 'p' +6) all remaining usage of cfree replaced with free +7) make a group of monsters all friendly or all non-friendly +8) removed debugging message in place_mons() in scrolls.c + +1.03 Alpha, July 6, 86 +1) fixed up more bad pointer references in wanderer() in monsters.c +2) fixed bad test for player class in wield_ok() in weapons.c +3) make sure every new monster starts with ISRUN off + +1.03 Alpha, July 8, 86 +1) new magic system installed. magic.c, magic_item.c files. Still buggy. +2) rogue.c and rogue.h worked over +3) various formatting improved +4) new wear_ok() that allows players to wear anything, but + penalizes their class-specific special abilities. wield_ok() coming up. +5) scroll of regeneration duplicates monster's regenerative abilities +6) familiar summoning made a scroll +7) quaff(), read_scroll(), do_zap() calling changed. +8) the Artifacts of Might do something if you just have them +9) ring of piety analogous to ring of wizardry introduced + above by Carl Hommel + +1.03 Alpha, July 9, 86 +1) changed format of status lines +2) fixed bug in increasing the hero's power and hpt in quaff() + +1.03 Alpha, July 10, 86 +1) spiffed up message for familiar nearly hitting hero +2) changed when familiars are gotten rid of when changing dungeon levels +3) fix monsters getting attacks when dead + above by Henry Chai +4) fix nonagressive familiars to beat on unfriendly monsters +5) changed some weapons for ninja and ranger at startup + +1.03 Alpha, July 11, 86 +1) some fixes for casting of spells for non-MU player classes by + Carl Hommel +2) fixed up replacement of familiar when going to a new dungeon + level by Henry Chai + +1.03 Alpha, July 12, 86 +1) increased spell point regeneration rate +2) replaced testing code for whether hero wants to try a hard spell +3) fixed up the ranger's starting kit a bit +4) improved ninja capabilities slightly +5) increase duration of disguise spell +6) attempt to fix monsters and heros in same spot +7) moving through a friendly monster wakes it up + +1.03 Alpha, July 13, 86 +1) fixed misc monster movement problems moving onto hero +2) some tidying up of formats +3) fixed up names of things carried by ranger +4) improved ninja character abilities and make experience level change + point higher + +1.03 Alpha, July 14, 86 +1) fighters can stun their opponents by doing greater than 1/3 of + remaining hit points in one blow +2) fixed up messages in new hit() routine + +1.03 Alpha, July 24, 86 +1) Change NUMMONST, MAXPOTIONS, etc to variables +2) Change some potion, stick, wand define names for spellcasting +3) P_SHIELD, S_MSHIELD WS_KNOCK, WS_CLOSE added +4) WS_MDEG changed to WS_XENOHEAL +5) Added defines to replace TRUE and FALSE values passed + to various subroutines) +6) Made P_REGEN cause SUPEREAT. Made it fusable. +7) The usual formatting changes to artifact.c, maze.c, monsters.c, wizard.c +8) Dropping armor and weapon to avoid artifact major + effects is no longer so good an idea +9) PHIAL does total healing, not light spell +10) Low-level monsters might break a hold spell +11) Everyone gets their hpts/pow changed against their armor/weapon, + not just paladins +12) Changed summon() to summon_help(), and added FORCE flag +13) Reordered hpt/pow regeneration in doctor() +14) Changed effect of R_CARRYING - still buggy +15) PURSE intinsically allows more carrying +16) Reworked logic in fight() +17) Reordered status line to put Pow after Hpt +18) Some monster spell casting implemented +19) Created nothing_message() to print cryptic failure messages +20) Twiddled do_throne() to create just UNIQUE monster + attendants +21) Clerics get bonus exp. for turning/destroying undead +22) do_zap() logic changed +23) D_GODWRATH, D_CLUMSY deaths added + above by Carl Hommel + +1.03 Alpha, July 27, 86 +1) added wizard debug message flag as well as wizard mode (Henry Chai) +2) stunning of monsters only when struck for 1/3 or more of + max hpt in one blow +3) various bug fixes by Mike Laman and Mike Cooper +4) saving a game clears the screen after prompt + +1.03 Alpha, July 29, 86 +1) checking for penalties for wrong armor and weapons fixed + by Carl Hommel + +1.03 Alpha, July 30, 86 +1) monsters now have probabilities of being a MU +2) fixed up readchar() to handle errors in a saner way +3) a monster type that is capable of using magic and is part + of a group will always use have magical abilities +4) the leader of a group of monsters will always be a bit better + than the run of the mill monster +5) patches by Mike Cooper to shorten names of variables and + various defines to support ATT 7300 + +1.03 Alpha, Aug 5, 86 +1) Len Picard's bag code is ready +2) changed ISWEARING to a function instead of a macro +3) fixed minor bugs in sticks.c + +1.03 Alpha, Aug 9, 86 +1) yet another attempt at fixing monster killing flag fixes by Mike Laman +2) applied fix by Carl Hommel to magic casting code +3) changed inventory command display format +4) updated readchar() handling of read() system call + +1.03 Alpha, Aug 10, 86 +1) added flag for summoned monsters that disappear +2) fixed up mapping of internal types of objects to things structure + in create_obj() +3) correct loop to pick object type to select in create_obj() +4) yet another attempt at getting rid of "invalid command ^@" message + +1.03 Alpha, Aug 11, 86 +1) "leaders" of a group of monsters not so improved over others +2) remove last reference to namefinder in Makefile.INST + +1.03 Alpha, Aug 12, 86 +1) The usual formatting changes in command.c, fight.c +2) Implemented '~' experience-to-next-level command. + Has next_exp_level() called from check_level) +3) is_carrying(TR_AMULET) protects vs CANDRAIN. +4) Made more "hit" and "miss" messages. +5) Moved extra exp code for clerics from affect() to killed(). +6) Reworked spell costs. +7) Give MUs extra exp for casting spells. +8) Made fumbles less frequent. +9) Created feel_message(). +10) Renamed spell abbreviations. +11) Corrected R_PIETY ordering. +12) Made some extra-planar monsters undead, fixed some monster letters. +13) Thieves get extra exp for picking up gold. +14) 'p'raying asks if you really want to, and always maxes your hpt + and pow to max) +15) Casting 'HEAL' no longer increases pow. +16) Reworked P_GAINABIL as they occur less frequently. +17) Fixed S_SUMFAMILIAR. +18) Prelim fix to helpful monsters casting spells. + above by Carl Hommel +19) upgraded leaders of packs a bit more and also added experience + points for killing them +20) BMAGICHIT now requires only a +3 weapon, not a +4 +21) druids can wield silver weapons even if they are metallic + +1.03 Alpha, Aug 13, 86 +1) fix up keypad code to allow daemons to run after each turn + +1.04 Alpha, October 24, 1992 +1) Update for modern compilation with ANSI C friendly compilers + +1.05 Alpha, September 1, 1993 +1) Update save/restore code to not be simple core dumps so + that they can work under modern memory management systems. + +1.06 Alpha, July 21, 1995 + +1) Many changes made to fix remaining parameter passing related bugs. +2) Also, changes made to toughen game. Entire dungeon is now only 50 + levels deep instead of 100 so that monsters get harder faster. +3) No more trading posts except at beginning or if entrance to + trading post is found. +4) The hero now regains spell points more slowly, has a tougher time with + some things, and generally is weaker than he used to be. +5) The game is not as hard as it needs to be yet, although i can make it + to about level 20 or so most times I play it. + +1.06 Alpha, August 3, 1995 + +1) Many more changes to allow dropping of stuff even where monsters or the + hero is, so there should almost never be messages of objects vanishing + in a puff of smoke. Also more error checking, debugging messages, and + generally much more bullet proof. +2) Restored ability to zap with charged weapons. diff -r d9badb9c0179 -r c495a4f288c6 urogue/ident.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/ident.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,136 @@ +/* + ident.c - routines to associate an identifier with an object + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1986, 1991, 1993, 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/* + * ident + * + * This file contains routines to associate an identifier with an object. The + * identifiers are organized by type. Once an identifier is attached to an + * object, it remains with that object until the object is removed from the + * game. The identifiers are small integers, and they are assigned merely by + * counting objects of the same type. Allocation picks the next available + * integer. + * + * It is required that the linked list be sorted within types so that gaps can + * easily be detected. + */ + +#include "rogue.h" + +/* + * Index of 0 is invalid (unused state) + */ + +char print_letters[] = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +linked_list *ident_list = NULL; /* master list of all items */ + +/* + get_ident() + + Gets the identifier for the given object. If an identifier exists, it is + returned. If not, one is allocated and returned to the user. The + identifier remains constant as long as the object is in the game. +*/ + +int +get_ident(struct object *obj_p) +{ + int obj_type = obj_p->o_type; + linked_list *list_p; /* pointer into ident_list */ + int new_id = 1; /* in case we have to allocate */ + struct object *tmp_obj_p; + struct linked_list *new_place_p = NULL; + + if (identifier(obj_p) != 0) + return (identifier(obj_p)); + + /* no identifier - must allocate one */ + + for (list_p = ident_list; list_p != NULL; list_p = next(list_p)) + { + tmp_obj_p = OBJPTR(list_p); + + if (tmp_obj_p->o_type == obj_type) + { + if (identifier(tmp_obj_p) == new_id) + { + /* if this id is taken, try next */ + new_place_p = list_p; + new_id++; + } + } + } + + /* + * If we get here, the object is not in the list, and we need to add + * it. The proper id is in new_id, and the place to put it is right + * after new_place_p. + */ + + list_p = new_list(); + _attach_after(&ident_list, new_place_p, list_p); + identifier(obj_p) = new_id; + list_p->data.obj = obj_p; + return(new_id); +} + +/* + free_ident() + + Frees up an identifier by removing the list entry that contains that item. + If the item isn't found, nothing is done. +*/ + +void +free_ident(struct object *obj_p) +{ + linked_list *list_p; + + for (list_p = ident_list; list_p != NULL; list_p = next(list_p)) + { + if (obj_p == OBJPTR(list_p)) + { + _detach(&ident_list, list_p); /* unlink it from the list */ + ur_free(list_p); /* release link structure */ + break; + } + } +} + +/* + unprint_id() + + Converts a printable id from print_letters to the real thing by getting the + index. +*/ + +int +unprint_id(char *print_id) +{ + char *id_p; + + for (id_p = print_letters; id_p != NULL; id_p++) + if (*id_p == *print_id) + break; + + return( (int) (id_p - print_letters) ); +} + +/* + max_print() + + returns the size of the print list +*/ + +int +max_print(void) +{ + return(sizeof(print_letters) - 2); /* 1 for blank and 1 for EOS string */ +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/init.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/init.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1075 @@ +/* + init.c - global variable initializaton + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/* + Notes + + Need to add ring of maintain armor (same as ring of prot, armor only) + Resplit file into one just for data, one just for functions +*/ + +#define _ALL_SOURCE /* Need to remove need for this AIXism */ + +#include +#include +#include +#include "rogue.h" + +static char *rainbow[] = +{ + "Red", "Blue", "Green", "Yellow", + "Black", "Brown", "Orange", "Pink", + "Purple", "Grey", "White", "Silver", + "Gold", "Violet", "Clear", "Vermilion", + "Ecru", "Turquoise", "Magenta", "Amber", + "Topaz", "Plaid", "Tan", "Tangerine", + "Aquamarine", "Scarlet", "Khaki", "Crimson", + "Indigo", "Beige", "Lavender", "Saffron" +}; + +#define NCOLORS (sizeof rainbow / sizeof (char *)) + +static char *sylls[] = +{ + "a", "ab", "ag", "aks", "ala", "an", "ankh", "app", "arg", "arze", + "ash", "ban", "bar", "bat", "bek", "bie", "bin", "bit", "bjor", + "blu", "bot", "bu", "byt", "comp", "con", "cos", "cre", "dalf", + "dan", "den", "do", "e", "eep", "el", "eng", "er", "ere", "erk", + "esh", "evs", "fa", "fid", "for", "fri", "fu", "gan", "gar", + "glen", "gop", "gre", "ha", "he", "hyd", "i", "ing", "ion", "ip", + "ish", "it", "ite", "iv", "jo", "kho", "kli", "klis", "la", "lech", + "man", "mar", "me", "mi", "mic", "mik", "mon", "mung", "mur", + "nej", "nelg", "nep", "ner", "nes", "nes", "nih", "nin", "o", "od", + "ood", "org", "orn", "ox", "oxy", "pay", "pet", "ple", "plu", "po", + "pot", "prok", "re", "rea", "rhov", "ri", "ro", "rog", "rok", "rol", + "sa", "san", "sat", "see", "sef", "seh", "shu", "ski", "sna", + "sne", "snik", "sno", "so", "sol", "sri", "sta", "sun", "ta", + "tab", "tem", "ther", "ti", "tox", "trol", "tue", "turs", "u", + "ulk", "um", "un", "uni", "ur", "val", "viv", "vly", "vom", "wah", + "wed", "werg", "wex", "whon", "wun", "xo", "y", "yot", "yu", + "zant", "zap", "zeb", "zim", "zok", "zon", "zum" +}; + +static char *stones[] = +{ + "Agate", "Alexandrite", "Amethyst", + "Azurite", "Carnelian", "Chrysoberyl", + "Chrysoprase", "Citrine", "Diamond", + "Emerald", "Garnet", "Hematite", + "Jacinth", "Jade", "Kryptonite", + "Lapus lazuli", "Malachite", "Moonstone", + "Obsidian", "Olivine", "Onyx", + "Opal", "Pearl", "Peridot", + "Quartz", "Rhodochrosite", "Ruby", + "Sapphire", "Sardonyx", "Serpentine", + "Spinel", "Tiger eye", "Topaz", + "Tourmaline", "Turquoise" +}; + +#define NSTONES (sizeof stones / sizeof (char *)) + +static char *wood[] = +{ + "Avocado wood", "Balsa", "Banyan", "Birch", + "Cedar", "Cherry", "Cinnibar", "Dogwood", + "Driftwood", "Ebony", "Eucalyptus", "Hemlock", + "Ironwood", "Mahogany", "Manzanita", "Maple", + "Oak", "Pine", "Redwood", "Rosewood", + "Teak", "Walnut", "Zebra wood", "Persimmon wood" +}; + +#define NWOOD (sizeof wood / sizeof (char *)) + +static char *metal[] = +{ + "Aluminium", "Bone", "Brass", "Bronze", + "Copper", "Chromium", "Iron", "Lead", + "Magnesium", "Pewter", "Platinum", "Steel", + "Tin", "Titanium", "Zinc", "Carbon", + "Plastic", "Glass", "Ice", "Chocolate", + "Gold", "Silver", "Invisible" +}; + +#define NMETAL (sizeof metal / sizeof (char *)) + +const char *monstern = "monster"; +char *spacemsg = "--Press SPACE to continue--"; +char *morestr = "--More--"; +char *retstr = "[Press RETURN to continue]"; + +/* 15 named levels */ + +const char *cnames[C_NOTSET][15] = +{ + { "Veteran", "Warrior", + "Swordsman", "Hero", /* Fighter */ + "Swashbuckler", "Myrmidon", + "Champion", "Superhero", + "Lord", "Lord", + "Lord", "Lord", + "Lord", "Lord", + "Lord" + }, + + { "Gallant", "Keeper", + "Protector", "Defender", /* Paladin */ + "Warder", "Guardian", + "Chevalier", "Justiciar", + "Paladin", "Paladin", + "Paladin", "Paladin", + "Paladin", "Paladin", + "Paladin" + }, + + { "Runner", "Strider", + "Scout", "Courser", /* Ranger */ + "Tracker", "Guide", + "Pathfinder", "Ranger", + "Ranger Knight", "Ranger Lord", + "Ranger Lord", "Ranger Lord", + "Ranger Lord", "Ranger Lord", + "Ranger Lord" + }, + + { "Acolyte", "Adept", + "Priest", "Curate", /* Cleric */ + "Prefect", "Canon", + "Lama", "Patriarch", + "High Priest", "High Priest", + "High Priest", "High Priest", + "High Priest", "High Priest", + "High Priest" + }, + + { "Aspirant", "Ovate", /* Druid */ + "Initiate of the 1st Circle", "Initiate of the 2nd Circle", + "Initiate of the 3rd Circle", "Initiate of the 4th Circle", + "Initiate of the 5th Circle", "Initiate of the 6th Circle", + "Initiate of the 7th Circle", "Initiate of the 8th Circle", + "Initiate of the 9th Circle", "Druid", + "Archdruid", "The Great Druid", + "The Grand Druid" + }, + + { "Prestidigitator", "Evoker", + "Conjurer", "Theurgist", /* Magic User */ + "Thaumaturgist", "Magician", + "Enchanter", "Warlock", + "Sorcerer", "Necromancer", + "Wizard", "Wizard", + "Wizard", "Wizard", + "Wizard" + }, + + { "Prestidigitator", "Minor Trickster", + "Trickster", "Master Trickster", /* Illusionist */ + "Cabalist", "Visionist", + "Phantasmist", "Apparitionist", + "Spellbinder", "Illusionist", + "Illusionist", "Illusionist", + "Illusionist", "Illusionist", + "Illusionist" + }, + + { "Rogue", "Footpad", + "Cutpurse", "Robber", /* Thief */ + "Burglar", "Filcher", + "Sharper", "Magsman", + "Thief", "Master Thief", + "Master Thief", "Master Thief", + "Master Thief", "Master Thief", + "Master Thief" + }, + + { "Bravo", "Rutterkin", + "Waghalter", "Murderer", /* Assasin */ + "Thug", "Killer", + "Cutthroat", "Executioner", + "Assassin", "Expert Assassin", + "Senior Assassin", "Chief Assassin", + "Prime Assassin", "Guildmaster Assassin", + "Grandfather of Assassins" + }, + + { "Ninja", "Ninja", + "Ninja", "Ninja", /* Ninja */ + "Ninja", "Ninja", + "Ninja", "Ninja", + "Ninja", "Ninja", + "Ninja", "Ninja", + "Ninja", "Ninja", + "Ninja" + } +}; + +const struct h_list helpstr[] = +{ + { '?', " prints help" }, + { '/', " identify object" }, + { 'h', " left" }, + { 'j', " down" }, + { 'k', " up" }, + { 'l', " right" }, + { 'y', " up & left" }, + { 'u', " up & right" }, + { 'b', " down & left" }, + { 'n', " down & right" }, + { '<', "SHIFT> run that way" }, + { 'm', " move onto without picking up" }, + { 't', " throw something" }, + { 'z', " zap a wand or staff" }, + { '>', " go down a staircase" }, + { 's', " search for trap/secret door" }, + { '.', " rest for a while" }, + { ',', " pick up an object" }, + { 'i', " inventory all items" }, + { 'I', " inventory type of item" }, + { 'q', " quaff potion" }, + { 'r', " read paper" }, + { 'e', " eat food" }, + { 'w', " wield a weapon" }, + { 'W', " wear armor" }, + { 'T', " take armor off" }, + { 'P', " put on ring" }, + { 'R', " remove ring" }, + { 'A', " activate/apply an artifact" }, + { 'd', " drop object" }, + { 'C', " call object (generic)" }, + { 'M', " mark object (specific)" }, + { 'o', " examine/set options" }, + { 'c', " cast a spell/say a prayer" }, + { 'p', " pray for help (risky)" }, + { 'a', " affect the undead" }, + { '^', " set a trap" }, + { 'D', " dip something (into a pool)" }, + { 20, " take (steal) from (direction)" }, /* ctrl-t */ + { 18, " redraw screen" }, /* ctrl-r */ + { 16, " back up to 10 previous messages" }, /* ctrl-p */ + { ESCAPE, " cancel command" }, + { 'v', " print program version number" }, + { 'S', " save game" }, + { 'Q', " quit" }, + { '=', " listen for monsters" }, + { 'f', " fight monster" }, + { 'F', " fight monster to the death" }, + + /* Wizard commands. Identified by (h_ch != 0 && h_desc == 0). */ + + {'-', 0 }, + { 23, " enter wizard mode" }, /* ctrl-w */ + { 23, "v toggle wizard verbose mode" }, + { 23, "e exit wizard mode" }, + { 23, "r random number check" }, + { 23, "s system statistics" }, + { 23, "F food statistics" }, + { 23, "f floor map" }, + { 23, "m see monster" }, + { 23, "M create monster" }, + { 23, "c create item" }, + { 23, "i inventory level" }, + { 23, "I identify item" }, + { 23, "t random teleport" }, + { 23, "g goto level" }, + { 23, "C charge item" }, + { 23, "w print worth of object" }, + { 23, "o improve stats and pack" }, + { 0, 0 } +}; + +struct magic_item things[] = +{ + {"potion", "POTION", 250, 5}, /* potion */ + {"scroll", "SCROLL", 260, 30},/* scroll */ + {"ring", "RING", 70, 5}, /* ring */ + {"stick", "STICK", 60, 0}, /* stick */ + {"food", "FOOD", 210, 7}, /* food */ + {"weapon", "WEAPON", 60, 0}, /* weapon */ + {"armor", "ARMOR", 90, 0}, /* armor */ + {"artifact","ARTIFACT", 0, 0} /* special artifacts*/ +}; + +int numthings = NUMTHINGS; + +struct magic_item s_magic[] = +{ + {"monster confusion", "CON", 50, 125, 0, 0 }, + {"magic mapping", "MAP", 45, 150, 20, 10 }, + {"light", "WATT", 0, 0, 0, 0 }, + {"hold monster", "HOLD", 25, 200, 33, 10 }, + {"sleep", "SNOOZE", 23, 50, 20, 10 }, + {"enchantment", "ENCHANT", 110,400, 15, 10 }, + {"identify", "ID", 150,50, 0, 15 }, + {"scare monster", "SCARE", 35, 250, 27, 21 }, + {"detect gold", "GOLD", 0, 0, 0, 0 }, + {"teleportation", "TELEP", 50, 165, 10, 20 }, + {"create monster", "CREATE", 25, 75, 30, 0 }, + {"remove curse", "REM", 75, 220, 10, 15 }, + {"petrification", "PET", 25, 285, 0, 0 }, + {"genocide", "GEN", 10, 1200, 0, 0 }, + {"cure disease", "CURE", 70, 80, 0, 0 }, + {"acquirement", "MAKE", 5, 2500, 50, 15 }, + {"protection", "PROT", 50, 1150, 0, 0 }, + {"nothing", "NOTHING", 75, 50, 50, 50 }, + {"magic hitting", "SILVER", 25, 1875, 45, 10 }, + {"ownership", "OWN", 15, 1550, 45, 10 }, + {"detect food", "FOOD", 0, 0, 0, 0 }, + {"electrification", "ELECTRIFY",20, 1450, 0, 0 }, + {"charm monster", "CHARM", 26, 1500, 25, 15 }, + {"summon monster", "SUMMON", 26, 1500, 25, 15 }, + {"gaze reflection", "REFLECT", 25, 400, 25, 15 }, + {"summon familiar", "SUMFAM", 0, 0, 0, 0 }, + {"fear", "FEAR", 20, 200, 20, 10 }, + {"missile protection", "MSHIELD", 20, 300, 20, 10 } +}; + +int maxscrolls = MAXSCROLLS; + +struct magic_item p_magic[] = +{ + {"clear thought", "CLEAR", 90, 380, 27, 15 }, + {"gain ability", "GAINABIL", 40, 1250, 15, 15 }, + {"see invisible", "SEE", 0, 0, 0, 0 }, + {"healing", "HEAL", 120,330, 27, 27 }, + {"detect monster", "MON", 0, 0, 0, 0 }, + {"detect magic", "MAG", 0, 0, 0, 0 }, + {"raise level", "RAISE", 1, 1900, 11, 10 }, + {"haste self", "HASTE", 140,300, 30, 5 }, + {"restore abilities", "RESTORE", 130,120, 0, 0 }, + {"phasing", "PHASE", 45, 340, 21, 20 }, + {"invisibility", "INVIS", 30, 300, 0, 15 }, + {"acute scent", "SMELL", 30, 100, 20, 15 }, + {"acute hearing", "HEAR", 30, 100, 20, 15 }, + {"super heroism", "SUPER", 10, 800, 20, 15 }, + {"disguise", "DISGUISE", 30, 500, 0, 15 }, + {"fire resistance", "NOFIRE", 40, 350, 20, 15 }, + {"cold resistance", "NOCOLD", 40, 300, 20, 15 }, + {"continuous breathing","BREATHE", 10, 200, 20, 15 }, + {"flying", "FLY", 30, 300, 20, 15 }, + {"regeneration", "REGEN", 20, 500, 20, 15 }, + {"shield", "SHIELD", 100,200, 20, 10 }, + {"true sight", "TRUESEE", 64, 570, 25, 15 } +}; + +int maxpotions = MAXPOTIONS; + +struct magic_item r_magic[] = +{ + {"protection", "", 70, 500, 33, 25 }, + {"add strength", "", 65, 300, 33, 25 }, + {"sustain ability", "", 40, 380, 10, 0 }, + {"searching", "", 65, 250, 10, 0 }, + {"see invisible", "", 30, 175, 10, 0 }, + {"alertness", "", 40, 190, 10, 0 }, + {"aggravate monster", "", 35, 100, 100,0 }, + {"dexterity", "", 65, 220, 33, 25 }, + {"increase damage", "", 65, 320, 33, 25 }, + {"regeneration", "", 35, 860, 10, 0 }, + {"slow digestion", "", 40, 340, 15, 10 }, + {"teleportation", "", 35, 100, 100,0 }, + {"stealth", "", 50, 700, 10, 0 }, + {"add intelligence", "", 60, 540, 33, 25 }, + {"increase wisdom", "", 60, 540, 33, 25 }, + {"sustain health", "", 60, 250, 10, 0 }, + {"vampiric regeneration", "", 20, 900, 25, 10 }, + {"illumination", "", 20, 300, 10, 0 }, + {"delusion", "", 20, 100, 75, 0 }, + {"carrying", "", 20, 400, 30, 30 }, + {"adornment", "", 15, 10000, 10, 0 }, + {"levitation", "", 20, 450, 30, 0 }, + {"fire resistance", "", 10, 750, 10, 0 }, + {"cold resistance", "", 10, 650, 10, 0 }, + {"lightning resistance", "", 10, 750, 10, 0 }, + {"resurrection", "", 1, 8000, 10, 0 }, + {"breathing", "", 10, 250, 10, 0 }, + {"free action", "", 10, 225, 10, 0 }, + {"wizardry", "", 2, 1950, 10, 0 }, + {"piety", "", 2, 1950, 10, 0 }, + {"teleport control", "", 5, 450, 10, 0 }, + {"true sight", "", 10, 775, 10, 0 } +}; + +int maxrings = MAXRINGS; + +struct magic_item ws_magic[] = +{ + {"light", "LIGHT", 90, 150, 20, 20 }, + {"striking", "HIT", 58, 400, 0, 0 }, + {"lightning", "BOLT", 25, 800, 0, 0 }, + {"fire", "FIRE", 25, 600, 0, 0 }, + {"cold", "COLD", 30, 600, 0, 0 }, + {"polymorph", "POLY", 90, 210, 0, 0 }, + {"magic missile", "MLE", 90, 500, 0, 0 }, + {"slow monster", "SLOW", 76, 320, 25, 20 }, + {"drain life", "DRAIN", 90, 310, 20, 0 }, + {"charging", "CHARGE", 70, 1100, 0, 0 }, + {"teleport monster","RANDOM", 90, 240, 25, 20 }, + {"cancellation", "CANCEL", 38, 230, 0, 0 }, + {"confuse monster", "CONFMON", 50, 200, 0, 0 }, + {"disintegration", "KILL-O-ZAP", 10, 1550, 33, 0 }, + {"anti-matter", "BLACKHOLE", 30, 980, 0, 0 }, + {"paralyze monster","PARAL", 38, 200, 0, 0 }, + {"heal monster", "XENOHEAL", 30, 200, 40, 10 }, + {"nothing", "NOTHING", 30, 100, 0, 0 }, + {"invisibility", "WS_INVIS", 30, 150, 30, 5 }, + {"blasting", "BLAST", 10, 220, 0, 0 }, + {"webbing", "WEB", 0, 0, 0, 0 }, + {"door opening", "KNOCK", 0, 0, 0, 0 }, + {"hold portal", "CLOSE", 0, 0, 0, 0 } +}; + +int maxsticks = MAXSTICKS; + +struct magic_item fd_data[] = +{ + {"food ration", "RATION", 400, 20, 20, 20 }, + {"random fruit","FRUIT", 300, 10, 0, 0}, + {"cram", "CRAM", 120, 30, 0, 0 }, + {"honey cake", "CAKES", 80, 10, 0, 0 }, + {"lemba", "LEMBA", 50, 80, 0, 0 }, + {"miruvor", "MIRUVOR", 50, 200, 0, 0 } +}; + +int maxfoods = MAXFOODS; + +/* + * weapons and their attributes + * Average Damage = (min_damage + max_damage) / 2) + * AD of 2D5+3 = (5 + 13) / 2 = 9 + * AD of 3D6 = (3 + 18) / 2 = 10.5 + */ + +#define ISSHARPMETAL (ISSHARP | ISMETAL) +#define ISCRYSKNIFE (ISSHARP | ISPOISON | ISMANY | ISLITTLE) + +struct init_weps weaps[] = +{ + /* Missile weapons */ + {"sling", "0d0", "0d0", NONE, 5, 1, + ISLAUNCHER | ISLITTLE, }, + {"rock", "1d2", "1d4", SLING, 5, 1, + ISMANY | ISMISL | ISLITTLE }, + {"sling bullet", "1d1", "1d8", SLING, 3, 1, + ISSHARP | ISMANY | ISMISL | ISMETAL | ISLITTLE }, + {"short bow", "1d1", "1d1", NONE, 40, 75, + ISLAUNCHER }, + {"arrow", "1d1", "2d3", BOW, 5, 1, + ISSHARP | ISMANY | ISMISL | ISLITTLE }, + {"arrow", "1d2", "2d8", BOW, 10, 5, + ISSHARP | ISSILVER | ISMANY | ISMISL | ISLITTLE }, + {"fire arrow", "1d2", "2d8", BOW, 10, 3, + ISSHARP | CANBURN | ISMANY | ISMISL | ISLITTLE }, + {"footbow", "1d1", "1d1", NONE, 90, 125, + ISLAUNCHER }, + {"footbow bolt", "1d2", "1d10", FOOTBOW, 5, 1, + ISSHARP | ISMANY | ISMISL | ISLITTLE }, + {"crossbow", "1d1", "1d1", NONE, 100,175, + ISLAUNCHER }, + {"crossbow bolt", "1d2", "2d5", CROSSBOW, 7, 3, + ISSHARP | ISMANY | ISMISL | ISLITTLE }, + + /* Useful throwing weapons */ + {"dart", "1d1", "1d3", NONE, 5, 1, + ISSHARP | ISMANY | ISMISL | ISLITTLE }, + {"dagger", "1d6", "1d4", NONE, 10, 2, + ISSHARP | ISMETAL | ISMANY | ISMISL | ISLITTLE }, + {"hammer", "1d3", "1d5", NONE, 50, 3, + ISMETAL | ISMISL }, + {"leuku", "1d6", "1d5", NONE, 40, 4, + ISSHARP | ISMETAL | ISTWOH }, + {"javelin", "1d4", "1d6", NONE, 10, 5, + ISSHARP | ISMISL | ISTWOH }, + {"tomahawk", "1d6", "1d6", NONE, 45, 7, + ISSHARP | ISMISL }, + {"machete", "1d7", "1d6", NONE, 45, 4, + ISSHARP | ISMETAL | ISMISL }, + {"throwing axe","1d3", "1d6+2", NONE, 50, 8, + ISSHARP | ISMETAL | ISMISL }, + {"spear", "2d3", "1d6", NONE, 50, 2, + ISSHARP | ISMETAL | ISMISL }, + {"boomerang", "1d1", "1d8", NONE, 10, 13, + CANRETURN | ISMANY | ISMISL | ISLITTLE }, + {"long spear", "1d8", "1d10", NONE, 50, 20, + ISSHARP | ISMETAL | ISMISL | ISTWOH }, + {"shuriken", "1d1", "2d5", NONE, 4, 20, + ISSHARP | ISMETAL | ISMANY | ISMISL | ISLITTLE }, + {"burning oil", "0d0", "2d10+5", NONE, 20, 30, + CANBURN | ISMANY | ISMISL | ISLITTLE }, + {"grenade", "1d1", "1d2/4d8", NONE, 10, 50, + ISMANY | ISSMALL }, + + /* other weapons */ + {"club", "1d4", "1d2", NONE, 30, 2, 0 }, + {"pitchfork", "1d5", "2d2", NONE, 15, 5, ISSHARPMETAL }, + {"short sword", "1d6", "1d2", NONE, 50, 10, ISSHARPMETAL }, + {"hand axe", "1d6", "1d2", NONE, 40, 15, ISSHARPMETAL }, + {"partisan", "1d6", "1d2", NONE, 75, 4, ISSHARPMETAL | ISTWOH }, + {"grain flail", "1d6", "1d4", NONE, 100, 2, ISSHARPMETAL }, + {"singlestick", "1d4+2", "1d2", NONE, 30, 20, 0 }, + {"rapier", "1d6+1", "1d2", NONE, 7, 75, ISSHARPMETAL }, + {"sickle", "1d6+1", "1d2", NONE, 30, 15, ISSHARPMETAL }, + {"hatchet", "1d6+1", "1d4", NONE, 50, 10, ISSHARPMETAL }, + {"scimitar", "1d8", "1d2", NONE, 40, 10, ISSHARPMETAL }, + {"mace", "2d4", "1d3", NONE, 100, 40, 0 }, + {"morning star", "2d4", "1d3", NONE, 125, 35, ISMETAL }, + {"broad sword", "2d4", "1d3", NONE, 75, 50, ISSHARPMETAL }, + {"miner's pick", "2d4", "1d2", NONE, 85, 40, ISSHARPMETAL }, + {"guisarme", "2d4", "1d3", NONE, 100, 25, ISSHARPMETAL | ISTWOH }, + {"war flail", "1d6+2", "1d4", NONE, 150, 50, ISSHARPMETAL | ISTWOH }, + {"crysknife", "3d3", "1d3", NONE, 12, 100, ISCRYSKNIFE }, + {"battle axe", "1d8+2", "1d3", NONE, 80, 100, ISSHARPMETAL }, + {"cutlass", "1d10", "1d2", NONE, 55, 120, ISSHARPMETAL }, + {"glaive", "1d10", "1d3", NONE, 80, 80, ISSHARPMETAL | ISTWOH }, + {"pertuska", "2d5", "1d3", NONE, 130, 100, ISSHARPMETAL | ISTWOH }, + {"long sword", "3d4", "1d2", NONE, 100, 150, ISSHARPMETAL }, + {"lance", "1d12", "1d8", NONE, 80, 140, ISSHARP | ISTWOH }, + {"ranseur", "1d12", "1d8", NONE, 100, 130, ISSHARPMETAL | ISTWOH }, + {"sabre", "2d6", "1d3", NONE, 50, 200, ISSHARPMETAL }, + {"spetum", "2d6", "1d3", NONE, 50, 180, ISSHARPMETAL | ISTWOH }, + {"halberd", "2d6", "1d3", NONE, 150, 125, ISSHARPMETAL | ISTWOH }, + {"trident", "3d4", "1d4", NONE, 50, 200, ISSHARPMETAL | ISTWOH }, + {"war pick", "3d4", "1d2", NONE, 75, 175, ISSHARPMETAL | ISTWOH }, + {"bardiche", "3d4", "1d2", NONE, 125, 125, ISSHARPMETAL | ISTWOH }, + {"heavy mace", "3d4", "1d3", NONE, 200, 50, ISTWOH }, + {"great scythe", "2d6+2", "1d2", NONE, 100, 200, ISSHARP | ISTWOH }, + {"quarter staff", "3d5", "1d2", NONE, 70, 250, 0 }, + {"bastard sword", "2d8", "1d2", NONE, 150, 300, ISSHARPMETAL }, + {"pike", "2d8", "2d6", NONE, 200, 275, ISSHARPMETAL | ISTWOH }, + {"great flail", "2d6+2", "1d4", NONE, 200, 275, ISSHARPMETAL | ISTWOH }, + {"great maul", "4d4", "1d3", NONE, 400, 250, ISTWOH }, + {"great pick", "2d9", "1d2", NONE, 175, 330, ISSHARPMETAL | ISTWOH }, + {"two handed sword","4d4", "1d2", NONE, 250, 300, ISSHARPMETAL | ISTWOH }, + {"claymore", "3d7", "1d2", NONE, 200, 500, ISSHARPMETAL | ISTWOH } +}; + +int maxweapons = MAXWEAPONS; + +struct init_armor armors[] = +{ + { "soft leather", 75, 20, 9, 50 }, + { "cuirboilli", 150, 30, 8, 130 }, + { "leather armor", 175, 40, 8, 100 }, + { "ring mail", 350, 49, 7, 250 }, + { "studded leather armor",400,58, 7, 200 }, + { "scale mail", 500, 66, 6, 250 }, + { "padded armor", 550, 72, 6, 150 }, + { "chain mail", 750, 78, 5, 300 }, + { "brigandine", 800, 84, 5, 280 }, + { "splint mail", 1000, 88, 4, 350 }, + { "banded mail", 1250, 90, 4, 300 }, + { "superior chain", 1500, 93, 3, 350 }, + { "plate mail", 1400, 96, 3, 400 }, + { "plate armor", 1650, 98, 2, 450 }, + { "mithril", 30000, 99, 2, 200 }, + { "crystalline armor", 15000, 100, 0, 300 } +}; + +int maxarmors = MAXARMORS; + +struct init_artifact arts[] = { + { "Magic Purse of Yendor", 15, 1, 1, 1, 1, 4600L, 50 }, + { "Phial of Galadriel", 20, 2, 2, 2, 1, 12500L, 10 }, + { "Amulet of Yendor", 25, 4, 1, 1, 2, 16000L, 10 }, + { "Palantir of Might", 30, 1, 4, 1, 2, 18500L, 70 }, + { "Crown of Might", 35, 6, 2, 1, 1, 23500L, 50 }, + { "Sceptre of Might", 40, 2, 2, 1, 6, 38000L, 50 }, + { "Silmaril of Ea", 45, 4, 2, 5, 1, 50000L, 50 }, + { "Wand of Yendor", 50, 4, 2, 3, 10, 80000L, 50 } +}; + +int maxartifact = MAXARTIFACT; + +/* + init_player() + roll up the rogue +*/ + +void +init_player(void) +{ + int special = rnd(100) < 20; + struct linked_list *item; + struct object *obj; + int which_armor = 0, which_weapon = 0; + int other_weapon_flags = 0; + + pstats.s_lvl = 1; + pstats.s_exp = 0L; + pstats.s_arm = 10; + + if (!geta_player()) + { + do_getplayer(); /* get character class */ + pstats.s_str = 8 + rnd(5); + pstats.s_intel = 8 + rnd(5); + pstats.s_wisdom = 8 + rnd(5); + pstats.s_dext = 8 + rnd(5); + pstats.s_const = 10 + rnd(8); + pstats.s_charisma = 8 + rnd(4); + pstats.s_power = 5 + rnd(5); + pstats.s_hpt = 15 + rnd(5); + + switch (char_type) /* Class-specific abilities */ + { + case C_FIGHTER: + pstats.s_str = 17; + pstats.s_const += rnd(4) + 1; + if (special) + { + pstats.s_str += rnd(3); + pstats.s_dext += rnd(4); + } + pstats.s_const = max(pstats.s_const, 16); + break; + + case C_PALADIN: + pstats.s_charisma = 17; + + if (special) + { + pstats.s_charisma += rnd(3); + pstats.s_wisdom += rnd(4); + pstats.s_str += rnd(5); + } + + pstats.s_str = max(pstats.s_str, 16); + pstats.s_wisdom = max(pstats.s_wisdom, 16); + break; + + case C_RANGER: + if (special) + { + pstats.s_wisdom += rnd(4); + pstats.s_intel += rnd(4); + pstats.s_str += rnd(5); + } + + pstats.s_str = max(pstats.s_str, 16); + pstats.s_wisdom = max(pstats.s_wisdom, 16); + pstats.s_const = max(pstats.s_const, 14); + break; + + case C_MAGICIAN: + pstats.s_intel = (special) ? 18 : 16; + pstats.s_power += 5 + rnd(10); + break; + + case C_ILLUSION: + pstats.s_intel = (special) ? 18 : 16; + pstats.s_dext = (special) ? 18 : 14; + pstats.s_power += 5 + rnd(10); + break; + + case C_CLERIC: + pstats.s_wisdom = (special) ? 18 : 16; + pstats.s_str += rnd(4); + pstats.s_power += 5 + rnd(10); + break; + + case C_DRUID: + if (special) + { + pstats.s_wisdom += rnd(5); + pstats.s_charisma += rnd(4); + } + pstats.s_str += rnd(4); + pstats.s_power += 5 + rnd(10); + pstats.s_wisdom = max(pstats.s_wisdom, 16); + break; + + case C_THIEF: + pstats.s_dext = 18; + if (special) + pstats.s_const += rnd(4) + 1; + break; + + case C_ASSASIN: + pstats.s_dext = (special) ? 18 : 16; + pstats.s_intel = (special) ? 18 : 16; + break; + + case C_NINJA: + if (special) + { + pstats.s_dext += rnd(5); + pstats.s_str += rnd(4); + pstats.s_intel += rnd(3); + pstats.s_wisdom += rnd(3); + } + pstats.s_dext = max(pstats.s_dext, 16); + pstats.s_str = max(pstats.s_str, 15); + pstats.s_wisdom = max(pstats.s_wisdom, 15); + pstats.s_const = max(pstats.s_const, 15); + pstats.s_charisma = max(pstats.s_charisma, 11); + } + + puta_player(); + } + + if (char_type == C_ASSASIN || char_type == C_NINJA + || char_type == C_FIGHTER) + pstats.s_dmg = "2d6"; + else + pstats.s_dmg = "1d4"; + + if (char_type == C_NINJA || char_type == C_FIGHTER) + pstats.s_arm = 8; + + if (pstats.s_const > 15) + pstats.s_hpt += pstats.s_const - 15; + + max_stats = pstats; + player.t_rest_hpt = player.t_rest_pow = 0; + player.t_praycnt = 0; + pstats.s_carry = totalenc(); + pack = NULL; + + switch (player.t_ctype) /* now outfit pack */ + { + case C_PALADIN: + purse = roll(20, 60); + which_armor = CHAIN_MAIL; + which_weapon = LONG_SWORD; + break; + + case C_FIGHTER: + purse = roll(50, 60); + which_armor = SCALE_MAIL; + which_weapon = BROAD_SWORD; + break; + + case C_RANGER: + purse = roll(50, 60); + which_armor = PADDED_ARMOR; + which_weapon = LONG_SPEAR; + other_weapon_flags |= ISOWNED | CANRETURN; + break; + + case C_CLERIC: + purse = roll(30, 80); + which_armor = RING_MAIL; + which_weapon = MORNINGSTAR; + break; + + case C_DRUID: + purse = roll(30, 80); + which_armor = STUDDED_LEATHER; + which_weapon = LIGHT_MACE; + break; + + case C_THIEF: + purse = roll(40, 80); + which_armor = HEAVY_LEATHER; + which_weapon = CUTLASS; + break; + + case C_ASSASIN: + purse = roll(20, 80); + which_armor = CUIRBOLILLI; + which_weapon = SABRE; + other_weapon_flags |= ISPOISON; + break; + + case C_NINJA: + purse = roll(20, 80); + which_armor = CUIRBOLILLI; + which_weapon = CRYSKNIFE; + other_weapon_flags |= ISPOISON; + item = spec_item(WEAPON, SHURIKEN, 1, 1); + obj = OBJPTR(item); + obj->o_count = 1; + obj->o_flags |= ISKNOW | ISPOISON | ISOWNED | CANRETURN; + add_pack(item, NOMESSAGE); + break; + + case C_MAGICIAN: + case C_ILLUSION: + purse = roll(20, 60); + which_armor = SOFT_LEATHER; + which_weapon = SINGLESTICK; + break; + + default: + break; + } + + /* Add weapon to pack */ + + item = spec_item(WEAPON, which_weapon, 1, 1); + obj = OBJPTR(item); + + obj->o_flags |= ISKNOW; + obj->o_flags |= other_weapon_flags; + obj->o_count = 1; + add_pack(item, NOMESSAGE); + cur_weapon = obj; + + /* Add armor to pack */ + + item = spec_item(ARMOR, which_armor, 0, 0); + obj = OBJPTR(item); + + obj->o_flags |= ISKNOW; + obj->o_weight = armors[which_armor].a_wght; + add_pack(item, NOMESSAGE); + cur_armor = obj; + + /* Add some food to pack */ + + item = spec_item(FOOD, FD_CRAM, 0, 0); + obj = OBJPTR(item); + + obj->o_weight = things[TYP_FOOD].mi_wght; + obj->o_count = 3; + add_pack(item, NOMESSAGE); +} + +/* + init_flags() + Initialize flags on startup +*/ + +void +init_flags(void) +{ + switch (player.t_ctype) + { + case C_MAGICIAN: + case C_ILLUSION: + case C_CLERIC: + case C_DRUID: + case C_RANGER: + case C_PALADIN: + turn_on(player, CANSUMMON); + break; + + default: + break; + } + + turn_on(player, CANCAST); + turn_on(player, CANWIELD); +} + +/* + * Contains definitions and functions for dealing with things like potions + * and scrolls + */ + +/* + init_things() + Initialize the probabilities for types of things +*/ + +void +init_things(void) +{ + struct magic_item *mp; + + for (mp = &things[1]; mp < &things[numthings]; mp++) + mp->mi_prob += (mp - 1)->mi_prob; + + badcheck("things", things, numthings); +} + +/* + init_fd() + Initialize the probabilities for types of food +*/ + +void +init_fd(void) +{ + struct magic_item *mp; + + for (mp = &fd_data[1]; mp < &fd_data[maxfoods]; mp++) + mp->mi_prob += (mp - 1)->mi_prob; + + badcheck("food", fd_data, maxfoods); +} + +/* + init_colors() + Initialize the potion color scheme for this time +*/ + +void +init_colors(void) +{ + int i; + char *str; + + for (i = 0; i < maxpotions; i++) + { + do + str = rainbow[rnd(NCOLORS)]; + while( !isupper(*str) ); + + p_colors[i] = md_strdup(str); + p_colors[i][0] = (char) tolower(p_colors[i][0]); + + know_items[TYP_POTION][i] = FALSE; + guess_items[TYP_POTION][i] = NULL; + + if (i > 0) + p_magic[i].mi_prob += p_magic[i - 1].mi_prob; + } + + badcheck("potions", p_magic, maxpotions); +} + +/* + init_names() + Generate the names of the various scrolls +*/ + +void +init_names(void) +{ + int nsyl; + char *cp, *sp; + int i, nwords; + + for (i = 0; i < maxscrolls; i++) + { + cp = prbuf; + nwords = rnd(COLS / 20) + 1 + (COLS > 40 ? 1 : 0); + + while (nwords--) + { + nsyl = rnd(3) + 1; + + while (nsyl--) + { + sp = sylls[rnd((sizeof sylls) / + (sizeof(char *)))]; + + while (*sp) + *cp++ = *sp++; + } + *cp++ = ' '; + } + + *--cp = '\0'; + s_names[i] = (char *) new_alloc(strlen(prbuf) + 1); + + know_items[TYP_SCROLL][i] = FALSE; + guess_items[TYP_SCROLL][i] = 0; + + strcpy(s_names[i], prbuf); + + if (i > 0) + s_magic[i].mi_prob += s_magic[i - 1].mi_prob; + } + + badcheck("scrolls", s_magic, maxscrolls); +} + +/* + init_stones() + Initialize the ring stone setting scheme for this time +*/ + +void +init_stones(void) +{ + int i; + char *str; + + for (i = 0; i < maxrings; i++) + { + do + str = stones[rnd(NSTONES)]; + while(!isupper(*str)); + + r_stones[i] = md_strdup(str); + r_stones[i][0] = (char) tolower( r_stones[i][0] ); + + know_items[TYP_RING][i] = FALSE; + guess_items[TYP_RING][i] = NULL; + + if (i > 0) + r_magic[i].mi_prob += r_magic[i - 1].mi_prob; + } + + badcheck("rings", r_magic, maxrings); +} + +/* + init_materials() + Initialize the construction materials for wands and staffs +*/ + +void +init_materials(void) +{ + int i; + char *str; + + for (i = 0; i < maxsticks; i++) + { + do + { + if (rnd(100) > 50) + { + str = metal[rnd(NMETAL)]; + + if (isupper(*str)) + ws_type[i] = "wand"; + } + else + { + str = wood[rnd(NWOOD)]; + + if (isupper(*str)) + ws_type[i] = "staff"; + } + } + while(!isupper(*str)); + + ws_made[i] = md_strdup(str); + ws_made[i][0] = (char) tolower( ws_made[i][0] ); + + know_items[TYP_STICK][i] = FALSE; + guess_items[TYP_STICK][i] = 0; + + if (i > 0) + ws_magic[i].mi_prob += ws_magic[i - 1].mi_prob; + } + + badcheck("sticks", ws_magic, maxsticks); +} + +void +badcheck(char *name, struct magic_item *magic, int bound) +{ + struct magic_item *end; + + if (magic[bound - 1].mi_prob == 1000) + return; + + printf("\nBad percentages for %s:\n", name); + + for (end = &magic[bound]; magic < end; magic++) + printf("%3d%% %s\n", magic->mi_prob, magic->mi_name); + + printf("[Hit RETURN to continue]"); + fflush(stdout); + + while ((readchar() & 0177) != '\n') + continue; +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/io.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/io.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,471 @@ +/* + io.c - Various input/output functions + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +char prbuf[2 * LINELEN]; /* Buffer for sprintfs */ +static char mbuf[2*LINELEN]; /* Current message buffer */ +static int newpos = 0; /* index in mbuf to end of msg */ + +int mpos = 0; /* 0 = Overwrite existing message */ + /* >0 = print --More-- at this pos */ + /* and wait for key */ + +int line_cnt = 0; +int newpage = FALSE; + +/* + msg() + Display a message at the top of the screen. +*/ + +void +msg(const char *fmt, ...) +{ + va_list ap; + + /* if the string is "", just clear the line */ + + if (*fmt == '\0') + { + wmove(cw, 0, 0); + wclrtoeol(cw); + mpos = 0; + return; + } + + /* otherwise add to the message and flush it out */ + + va_start(ap, fmt); + doadd(fmt, ap); + va_end(ap); + + endmsg(); +} + +void +vmsg(const char *fmt, va_list ap) +{ + /* if the string is "", just clear the line */ + + if (*fmt == '\0') + { + wmove(cw, 0, 0); + wclrtoeol(cw); + mpos = 0; + return; + } + + /* otherwise add to the message and flush it out */ + + doadd(fmt, ap); + endmsg(); +} + + +/* + addmsg() + add things to the current message +*/ + +void +addmsg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doadd(fmt,ap); + va_end(ap); +} + +/* + endmsg() + Display a new msg (giving him a chance to see the previous one + if it is up there with the --More--) +*/ + +void +endmsg(void) +{ + strcpy(msgbuf[msg_index], mbuf); + + msg_index = ++msg_index % 10; + + if (mpos) + { + wmove(cw, 0, mpos); + wprintw(cw, (char *) morestr); + wrefresh(cw); + wait_for(' '); + } + + mvwprintw(cw, 0, 0, mbuf); + wclrtoeol(cw); + + mpos = newpos; + newpos = 0; + + wrefresh(cw); +} + +void +doadd(const char *fmt, va_list ap) +{ + vsprintf(&mbuf[newpos], fmt, ap); + newpos = (int) strlen(mbuf); +} + +/* + status() + Display the important stats line. Keep the cursor where it was. +*/ + +void +status(int display) +{ + static char buf[1024]; /* Temporary buffer */ + struct stats *stat_ptr, *max_ptr; + int oy, ox; + + stat_ptr = &pstats; + max_ptr = &max_stats; + + getyx(cw, oy, ox); + sprintf(buf, + "Int:%d(%d) Str:%d(%d) Wis:%d(%d) Dex:%d(%d) Con:%d(%d) Carry:%d(%d) %d", + stat_ptr->s_intel, max_ptr->s_intel, + stat_ptr->s_str, max_ptr->s_str, + stat_ptr->s_wisdom, max_ptr->s_wisdom, + stat_ptr->s_dext, max_ptr->s_dext, + stat_ptr->s_const, max_ptr->s_const, + stat_ptr->s_pack / 10, stat_ptr->s_carry / 10, foodlev ); + + mvwaddstr(cw, LINES - 2, 0, buf); + wclrtoeol(cw); + + sprintf(buf, "Lvl:%d Au:%d Hpt:%3d(%3d) Pow:%d(%d) Ac:%d Exp:%d+%ld %s", + level, + purse, + stat_ptr->s_hpt, max_ptr->s_hpt, + stat_ptr->s_power, max_ptr->s_power, + (cur_armor != NULL ? (cur_armor->o_ac - 10 + stat_ptr->s_arm) + : stat_ptr->s_arm) - ring_value(R_PROTECT), + stat_ptr->s_lvl, + stat_ptr->s_exp, + cnames[player.t_ctype][min(stat_ptr->s_lvl - 1, 14)]); + + mvwaddstr(cw, LINES - 1, 0, buf); + + switch(hungry_state) + { + case F_OK: break; + case F_HUNGRY: waddstr(cw, " Hungry"); + break; + case F_WEAK: waddstr(cw, " Weak"); + break; + case F_FAINT: waddstr(cw, " Fainting"); + break; + } + + wclrtoeol(cw); + wmove(cw, oy, ox); + + if (display) + wrefresh(cw); +} + +/* + * readchar: + * Flushes stdout so that screen is up to date and then returns + * getchar(). + */ + +char +readcharw(WINDOW *win) +{ + char ch; + + ch = (char) md_readchar(win); + + if ((ch == 3) || (ch == 0)) + { + quit(); + return(27); + } + + return(ch); +} + +char +readchar() +{ + return( readcharw(cw) ); +} + +/* + wait_for() + Sit around until the guy types the right key +*/ + +void +w_wait_for(WINDOW *w, int ch) +{ + char c; + + if (ch == '\n') + while ((c = readcharw(w)) != '\n' && c != '\r') + continue; + else + while (readcharw(w) != ch) + continue; +} + +void +wait_for(int ch) +{ + w_wait_for(cw, ch); +} + +/* + show_win() + function used to display a window and wait before returning +*/ + +void +show_win(WINDOW *scr, char *message) +{ + mvwaddstr(scr, 0, 0, message); + touchwin(scr); + wmove(scr, hero.y, hero.x); + wrefresh(scr); + wait_for(' '); + clearok(cw, TRUE); + touchwin(cw); +} + +/* + restscr() + Restores the screen to the terminal +*/ + +void +restscr(WINDOW *scr) +{ + clearok(scr, TRUE); + touchwin(scr); +} + +/* + add_line() + Add a line to the list of discoveries +*/ + +void +add_line(const char *fmt, ...) +{ + WINDOW *tw; + va_list ap; + + va_start(ap, fmt); + + if (line_cnt == 0) + { + wclear(hw); + + if (inv_type == INV_SLOW) + mpos = 0; + } + + if (inv_type == INV_SLOW) + { + if ( (fmt != NULL) && (*fmt != '\0') ) + vmsg(fmt, ap); + line_cnt++; + } + else + { + if ( (line_cnt >= LINES - 2) || (fmt == NULL)) /* end 'o page */ + { + if (fmt == NULL && !newpage && inv_type == INV_OVER) + { + tw = newwin(line_cnt + 2, COLS, 0, 0); + overwrite(hw, tw); + wstandout(tw); + mvwaddstr(tw, line_cnt, 0, spacemsg); + wstandend(tw); + touchwin(tw); + wrefresh(tw); + wait_for(' '); + delwin(tw); + touchwin(cw); + } + else + { + wstandout(hw); + mvwaddstr(hw, LINES - 1, 0, spacemsg); + wstandend(hw); + wrefresh(hw); + w_wait_for(hw, ' '); + touchwin(cw); + wclear(hw); + } + newpage = TRUE; + line_cnt = 0; + } + + /* draw line */ + if (fmt != NULL && !(line_cnt == 0 && *fmt == '\0')) + { + static char tmpbuf[1024]; + + vsprintf(tmpbuf, fmt, ap); + mvwprintw(hw, line_cnt++, 0, tmpbuf); + } + } +} + +/* + end_line() + End the list of lines +*/ + +void +end_line(void) +{ + if (inv_type != INV_SLOW) + add_line(NULL); + + line_cnt = 0; + newpage = FALSE; +} + +/* + hearmsg() + Call msg() only if you are not deaf +*/ + +void +hearmsg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + if (off(player, ISDEAF)) + vmsg(fmt, ap); + else if (wizard) + { + msg("Couldn't hear: "); + vmsg(fmt, ap); + } + + va_end(ap); +} + +/* + seemsg() + Call msg() only if you are not blind +*/ + +void +seemsg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + if (off(player, ISBLIND)) + vmsg(fmt, ap); + else if (wizard) + { + msg("Couldn't see: "); + vmsg(fmt, ap); + } + + va_end(ap); +} + +int +get_string(char *buffer, WINDOW *win) +{ + char *sp, c; + int oy, ox; + char buf[2 * LINELEN]; + + wrefresh(win); + getyx(win, oy, ox); + + /* loop reading in the string, and put it in a temporary buffer */ + + for (sp = buf; (c = readcharw(win)) != '\n' && + c != '\r' && + c != '\033' && + c != '\007' && + sp < &buf[LINELEN - 1]; + wclrtoeol(win), wrefresh(win)) + { + if ((c == '\b') || (c == 0x7f)) + { + if (sp > buf) + { + size_t i; + + sp--; + + for (i = strlen(unctrl(*sp)); i; i--) + waddch(win, '\b'); + } + continue; + } + else if (c == '\0') + { + sp = buf; + wmove(win, oy, ox); + continue; + } + else if (sp == buf && c == '-' && win == hw) + break; + + *sp++ = c; + waddstr(win, unctrl(c)); + } + + *sp = '\0'; + + if (sp > buf) /* only change option if something has been typed */ + strncpy(buffer, buf, strlen(buf)+1); + + wmove(win, oy, ox); + waddstr(win, buffer); + waddch(win, '\n'); + wrefresh(win); + + if (win == cw) + mpos += (int)(sp - buf); + + if (c == '-') + return(MINUS); + else if (c == '\033' || c == '\007') + return(QUIT); + else + return(NORM); +} + diff -r d9badb9c0179 -r c495a4f288c6 urogue/lint-curses.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/lint-curses.h Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,69 @@ +/* + lint-curses.h + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1993, 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + + +/* Sufficient info to pass lint */ +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#define getyx(win,y,x) y = win->_cury, x = win->_curx +struct screen { int opque_data_type; }; +typedef struct { int _cury; int _curx; } WINDOW; +extern WINDOW *stdscr; +extern WINDOW *curscr; +extern int LINES; +extern int COLS; +extern char *unctrl(char c); +extern void initscr(void); +extern int wmove(WINDOW *window, int Line, int Column); +extern int move(int Line, int Column); +extern int addch(char c); +extern int mvaddch(int y, int x, char c); +extern int waddch(WINDOW *window, char c); +extern int mvwaddch(WINDOW *window, int y, int x, char c); +extern int mvwinch(WINDOW *window, int y, int x); +extern int winch(WINDOW *window); +extern int mvinch(int y, int x); +extern int getch(void); +extern int wgetch(WINDOW *window); +extern void clear(void); +extern void wclear(WINDOW *window); +extern void refresh(void); +extern void wrefresh(WINDOW *window); +extern void clearok(WINDOW *window, int flag); +extern void endwin(void); +extern void touchwin(WINDOW *window); +extern void overlay(WINDOW *w1, WINDOW *w2); +extern void wclrtoeol(WINDOW *window); +extern void wprintw(WINDOW *window, const char *fmt, ...); +extern void mvprintw(int line, int col, char *fmt, ...); +extern void mvwprintw(WINDOW *window, int line, int col, char *fmt, ...); +extern int mvwaddstr(WINDOW *window, int y, int x, const char *str); +extern int mvaddstr(int y, int x, char *str); +extern int waddstr(WINDOW *window, char *str); +extern int addstr(char *str); +extern void standout(void); +extern void wstandout(WINDOW *window); +extern void standend(void); +extern void wstandend(WINDOW *window); +extern void noecho(void); +extern void cbreak(void); +extern void crmode(void); +extern void nonl(void); +extern void nl(void); +extern int wgetch(WINDOW *window); +extern WINDOW *newwin(int lines, int cols, int y, int x); +extern void overwrite(WINDOW *w1, WINDOW *w2); +extern void delwin(WINDOW *window); +extern void printw(char *fmt, ...); + diff -r d9badb9c0179 -r c495a4f288c6 urogue/list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/list.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,222 @@ +/* + list.c - Functions for dealing with linked lists of goodies + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +static char errbuf[2 * LINELEN]; + +/* + ur_alloc() + ur_free() + + These are just calls to the system alloc and free, and they also adjust + the totals. The buffer is cleared out because idents need to be zero + before going into the pack, or they will be used as indices! +*/ + +void * +ur_alloc(size_t size) +{ + char *buf_p; + + total++; + + buf_p = mem_malloc(size); + + if (buf_p == NULL) + return(NULL); + + memset(buf_p,0,size); + + return(buf_p); +} + +void +ur_free(void *buf_p) +{ + mem_free(buf_p); + total--; +} + +/* + detach() + Takes an item out of whatever linked list it might be in + .... function needs to be renamed.... +*/ + +void +_detach(struct linked_list **list, struct linked_list *item) +{ + if (*list == item) + *list = next(item); + + if (prev(item) != NULL) + item->l_prev->l_next = next(item); + + if (next(item) != NULL) + item->l_next->l_prev = prev(item); + + item->l_next = NULL; + item->l_prev = NULL; +} + +/* + _attach() + add an item to the head of a list + ... this needs to be renamed as well ... +*/ + +void +_attach(struct linked_list **list, struct linked_list *item) +{ + if (*list != NULL) + { + item->l_next = *list; + (*list)->l_prev = item; + item->l_prev = NULL; + } + else + { + item->l_next = NULL; + item->l_prev = NULL; + } + + *list = item; +} + +/* + _attach_after() + + Attaches the given item after the supplied one in the list. If the listed + item is NULL, the new item is attached at the head of the list. +*/ + +void +_attach_after(linked_list **list_pp, linked_list *list_p, linked_list *new_p) +{ + if (list_p == NULL) + { + _attach(list_pp, new_p); /* stuff it at the beginning */ + return; + } + + if (next(list_p) != NULL) /* something after this one? */ + { + new_p->l_next = next(list_p); + list_p->l_next->l_prev = new_p; + } + else + new_p->l_next = NULL; + + list_p->l_next = new_p; + new_p->l_prev = list_p; +} + +/* + _free_list() + Throw the whole blamed thing away +*/ + +void +_free_list(linked_list **ptr) +{ + linked_list *item; + + while(*ptr != NULL) + { + item = *ptr; + *ptr = next(item); + discard(item); + } +} + +/* + discard() + free up an item +*/ + +void +discard(struct linked_list *item) +{ + throw_away(item->data.obj); + ur_free(item); +} + +/* + throw_away() + toss out something (like discard, but without the link_list) +*/ + +void +throw_away(struct object *ptr) +{ + free_ident(ptr); + ur_free(ptr); +} + +/* + new_item() + get a new item with a specified size +*/ + +struct linked_list * +new_item(int size) +{ + struct linked_list *item; + + if ((item = new_list()) == NULL) + msg("Ran out of memory for header after %d items.", total); + + if ((item->data.l_data = new_alloc(size)) == NULL) + msg("Ran out of memory for data after %d items.", total); + + item->l_next = item->l_prev = NULL; + + return(item); +} + +void * +new_alloc(size_t size) +{ + void *space = ur_alloc(size); + + if (space == NULL) + { + sprintf(errbuf, "Rogue ran out of memory."); + fatal(errbuf); + } + + return(space); +} + +struct linked_list * +new_list(void) +{ + union /* ugly_lint_hack */ + { + struct linked_list *ll; + void *vptr; + } newp; + + newp.vptr = mem_malloc(sizeof(struct linked_list)); + memset(newp.vptr,0,sizeof(struct linked_list)); + return(newp.ll); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/magic.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/magic.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,878 @@ +/* + magic.c - This file contains functions for casting magic spells + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +/* + Cost for each level of spells level: +*/ + +static const int spell_cost[] = {1, 5, 17, 29, 53, 91, 159, 247, 396}; + +static struct spells monst_spells[] = +{ + {5, S_SELFTELEP, SCR_MAGIC}, + {4, P_HEALING, POT_MAGIC | _TWO_}, + {3, P_REGENERATE, POT_MAGIC}, + {2, P_HEALING, POT_MAGIC}, + {4, P_HASTE, POT_MAGIC}, + {2, P_SEEINVIS, POT_MAGIC}, + {3, P_SHERO, POT_MAGIC}, + {5, P_PHASE, POT_MAGIC}, + {4, P_INVIS, POT_MAGIC}, + {4, WS_CANCEL, ZAP_MAGIC}, + + /* In reverse order of damage ability */ + {6, WS_ELECT, ZAP_MAGIC | _TWO_}, + {6, WS_FIRE, ZAP_MAGIC | _TWO_}, + {6, WS_COLD, ZAP_MAGIC | _TWO_}, + {6, WS_MISSILE, ZAP_MAGIC | _TWO_}, + {5, WS_ELECT, ZAP_MAGIC}, + {5, WS_FIRE, ZAP_MAGIC}, + {5, WS_COLD, ZAP_MAGIC}, + {4, WS_ELECT, ZAP_MAGIC | ISCURSED}, + {4, WS_FIRE, ZAP_MAGIC | ISCURSED}, + {4, WS_COLD, ZAP_MAGIC | ISCURSED}, + {3, WS_MISSILE, ZAP_MAGIC}, + {1, WS_MISSILE, ZAP_MAGIC | ISCURSED}, + + {-1, -1, 0} +}; + +/* + Spells that a player can cast Non-mus only know ISKNOW spells until found + in the dungeon. Special classes know their spells one level lower, and + blessed one above. +*/ + +static struct spells player_spells[] = +{ + {1, WS_KNOCK, ZAP_MAGIC | ISKNOW}, + {1, S_SUMFAMILIAR, SCR_MAGIC | SP_DRUID | SP_MAGIC | SP_CLERIC, SP_ILLUSION }, + {1, S_GFIND, SCR_MAGIC | ISKNOW}, + {1, P_MONSTDET, POT_MAGIC | ISKNOW | SP_DRUID}, + {1, P_TREASDET, POT_MAGIC | ISKNOW | SP_MAGIC}, + {1, S_FOODDET, SCR_MAGIC | ISKNOW | SP_CLERIC}, + {1, S_LIGHT, SCR_MAGIC | ISKNOW | SP_ILLUSION}, + + {2, WS_CLOSE, ZAP_MAGIC | ISKNOW}, + {2, S_IDENTIFY, SCR_MAGIC | ISKNOW}, + {2, WS_HIT, ZAP_MAGIC | ISKNOW | SP_PRAYER}, + {2, P_SHIELD, POT_MAGIC | ISKNOW | SP_MAGIC}, + {2, P_COLDRESIST, POT_MAGIC | SP_WIZARD}, + {2, P_SEEINVIS, POT_MAGIC | SP_ILLUSION}, + {2, S_CONFUSE, SCR_MAGIC | SP_CLERIC}, + {2, P_SMELL, POT_MAGIC | SP_DRUID}, + {2, WS_MISSILE, ZAP_MAGIC | SP_MAGIC}, + {2, P_HEAR, POT_MAGIC}, + + {3, P_CLEAR, POT_MAGIC | ISKNOW}, + {3, P_HEALING, POT_MAGIC | ISKNOW | SP_PRAYER}, + {3, S_CURING, SCR_MAGIC | ISKNOW | SP_PRAYER}, + {3, WS_MONSTELEP, ZAP_MAGIC | SP_MAGIC}, + {3, WS_CANCEL, ZAP_MAGIC | SP_WIZARD}, + {3, S_SELFTELEP, SCR_MAGIC | SP_WIZARD}, + {3, P_FIRERESIST, POT_MAGIC | SP_WIZARD | SP_DRUID}, + {3, S_MAP, SCR_MAGIC | SP_ILLUSION | SP_DRUID}, + {3, S_REMOVECURSE, SCR_MAGIC | SP_PRAYER}, + {3, S_HOLD, SCR_MAGIC | SP_CLERIC}, + {3, S_SLEEP, SCR_MAGIC | SP_DRUID}, + {3, P_HASOXYGEN, POT_MAGIC | SP_DRUID}, + {3, WS_XENOHEALING, ZAP_MAGIC | SP_DRUID}, + {3, P_RESTORE, POT_MAGIC}, + + {4, S_MSHIELD, SCR_MAGIC | ISKNOW | SP_ILLUSION}, + {4, P_INVIS, POT_MAGIC | SP_ILLUSION}, + {4, S_REFLECT, SCR_MAGIC | SP_ILLUSION}, + {4, P_TRUESEE, POT_MAGIC | SP_ILLUSION}, + {4, P_REGENERATE, POT_MAGIC | SP_CLERIC}, + {4, WS_DRAIN, ZAP_MAGIC | SP_CLERIC}, + {4, P_HASTE, POT_MAGIC | SP_ILLUSION | SP_CLERIC}, + {4, P_LEVITATION, POT_MAGIC | SP_WIZARD | SP_DRUID}, + {4, WS_WEB, ZAP_MAGIC | SP_MAGIC}, + {4, P_PHASE, POT_MAGIC}, + + {5, P_SHERO, POT_MAGIC | ISKNOW}, + {5, S_PETRIFY, SCR_MAGIC | SP_MAGIC}, + {5, S_SCARE, SCR_MAGIC | _TWO_ | SP_PRAYER}, + {5, WS_COLD, ZAP_MAGIC | SP_DRUID}, + {5, WS_FIRE, ZAP_MAGIC | SP_CLERIC}, + {5, WS_ELECT, ZAP_MAGIC | SP_WIZARD}, + {5, WS_ANTIMATTER, ZAP_MAGIC | SP_ILLUSION}, + {5, S_ELECTRIFY, SCR_MAGIC | SP_ILLUSION}, + + {6, WS_DISINTEGRATE, ZAP_MAGIC | ISKNOW}, + {6, S_OWNERSHIP, SCR_MAGIC | SP_ALL}, + + {7, S_ENCHANT, SCR_MAGIC | SP_MAGIC}, + + {-1, -1, 0} +}; + +/* + incant() + Cast a spell +*/ + +void +incant(struct thing *caster, coord dir) +{ + int i; + struct stats *curp; + struct stats *maxp; + int is_player = (caster == &player); + int points_casters; + char *casters_name = (on(player, ISBLIND)) ? "it" : + monsters[caster->t_index].m_name; + struct spells *sp; + char *cast_name; /* = spell_name(sp) */ + char *spell_type; /* spell or prayer */ + int casting_cost; /* from spell_cost[] */ + int spell_roll; /* sucess/fail 1D100 die roll */ + int fumble_chance; /* Spell fumble chance */ + int num_fumbles = 0; /* for fumble_spell() */ + int bless_or_curse = ISNORMAL; /* blessed or cursed? */ + int message_flags = CAST_NORMAL; /* which message to print out */ + int class_casters; /* For determining ISKNOW */ + int stat_casters; /* s_intel or s_wisdom */ + int level_casters; /* spellcasting level */ + char buf[2 * LINELEN]; + struct spells sorted_spells[MAX_SPELLS]; + char spellbuf[2 * LINELEN]; + char spellbuf2[2 * LINELEN]; + + curp = &(caster->t_stats); + maxp = &(caster->maxstats); + points_casters = curp->s_power; + + if (points_casters <= 0) + { + if (is_player) + msg("You don't have any spell points."); + + return; + } + + /* + * Paladins, Rangers, ringwearers, and monsters cast at 4 levels + * below. Other non-specialists at 8 below + */ + + level_casters = curp->s_lvl; + + switch (caster->t_ctype) + { + case C_PALADIN: + level_casters -= 4; + /* fallthrough */ + case C_CLERIC: + class_casters = SP_CLERIC; + stat_casters = curp->s_wisdom; + break; + case C_RANGER: + level_casters -= 4; + /* fallthrough */ + case C_DRUID: + class_casters = SP_DRUID; + stat_casters = curp->s_wisdom; + break; + case C_MAGICIAN: + class_casters = SP_WIZARD; + stat_casters = curp->s_intel; + break; + case C_ILLUSION: + class_casters = SP_ILLUSION; + stat_casters = curp->s_intel; + break; + case C_MONSTER: + if (off(*caster, ISUNIQUE)) + level_casters -= 4; + class_casters = 0x0; + stat_casters = curp->s_intel; + break; + + default: + if (is_wearing(R_WIZARD)) + { + level_casters -= 4; + class_casters = (rnd(4) ? SP_WIZARD : SP_ILLUSION); + stat_casters = curp->s_intel; + } + else if (is_wearing(R_PIETY)) + { + level_casters -= 4; + class_casters = (rnd(4) ? SP_CLERIC : SP_DRUID); + stat_casters = curp->s_wisdom; + } + else + { + level_casters -= 8; + class_casters = 0x0; + stat_casters = (rnd(2) ? curp->s_wisdom : curp->s_intel); + } + } + + /* Bug - What about when WIS == INT? */ + + spell_type = (stat_casters == curp->s_intel) ? "spell" : "prayer"; + + if (!is_player && (sp = pick_monster_spell(caster)) == NULL) + return; + else if (is_player) + { + int num_spells = -1; /* num of spells cheap enough */ + + sorted_spells[0].sp_cost = -1; + + for (sp = player_spells; sp->sp_level != -1; sp++) + { + if (sp->sp_flags & class_casters) /* Does class know spell? */ + { + int rnd_number = rnd(2 * sp->sp_level) - sp->sp_level; + + /* Knows normal spell one level below others */ + + casting_cost = spell_cost[sp->sp_level - 1] + rnd_number; + + if (points_casters >= casting_cost) + { + sorted_spells[++num_spells] = *sp; + sorted_spells[num_spells].sp_cost = casting_cost; + sorted_spells[num_spells].sp_level = sp->sp_level - 1; + } + + /* Knows blessed spell one level above others */ + + casting_cost = spell_cost[sp->sp_level + 1] + rnd_number; + + if (points_casters >= casting_cost) + { + sorted_spells[++num_spells] = *sp; + sorted_spells[num_spells].sp_level = sp->sp_level + 1; + sorted_spells[num_spells].sp_cost = casting_cost; + sorted_spells[num_spells].sp_flags |= ISBLESSED; + } + } /* If class doesn't know spell, see if its a ISKNOW */ + else if (sp->sp_flags & ISKNOW) + { + int rnd_number = rnd(4 * sp->sp_level) - sp->sp_level; + + casting_cost = spell_cost[sp->sp_level] + rnd_number; + + if (points_casters >= casting_cost) + { + sorted_spells[++num_spells] = *sp; + sorted_spells[num_spells].sp_cost = casting_cost; + } + } + /* else this spell is unknown */ + } + + if (sorted_spells[0].sp_cost == -1) + { + msg("You don't have enough %s points.", spell_type); + after = FALSE; + return; + } + + qsort(sorted_spells,num_spells + 1,sizeof(struct spells),sort_spells); + + do /* Prompt for spells */ + { + struct spells *which_spell = NULL; + + buf[0] = '\0'; + msg("");/* Get rid of --More-- */ + msg("Which %s are you casting [%d points left] (* for list)? ", + spell_type, points_casters); + + switch(get_string(buf, cw)) + { + case NORM: break; + case QUIT: return; /* ESC - lose turn */ + default: continue; + } + + if (buf[0] == '*') /* print list */ + { + add_line("Cost Abbreviation Full Name"); + + for (i = 0; i <= num_spells; i++) + { + sp = &sorted_spells[i]; + sprintf(buf, "[%3d] %-12s\t%s", + sp->sp_cost, spell_abrev(sp,spellbuf2), + spell_name(sp,spellbuf)); + add_line(buf); + } + end_line(); + sp = NULL; + continue; + } + + if (isupper(buf[0])) /* Uppercase Abbreviation */ + { + for (i = 0; i <= num_spells; i++) + { + sp = &sorted_spells[i]; + + if ((strcmp(spell_abrev(sp,spellbuf2), buf) == 0)) + { + which_spell = sp; + break; + } + } + } + else /* Full Spell Name */ + { + for (i = 0; i <= num_spells; i++) + { + sp = &sorted_spells[i]; + + if ((strcmp(spell_name(sp,spellbuf), buf) == 0)) + { + which_spell = sp; + break; + } + } + } + + sp = which_spell; + } + while (sp == NULL); + } + + /* Common monster and player code */ + + cast_name = spell_name(sp,spellbuf); + + fumble_chance = (10 * sp->sp_level / 4 - 10 * level_casters / 13) * 5; + + if (cur_weapon != NULL && wield_ok(caster, cur_weapon, FALSE) == FALSE) + { + switch (caster->t_ctype) + { + case C_MAGICIAN: + case C_ILLUSION: + msg("You should have both hands free."); + fumble_chance += rnd(level_casters) * 5; + break; + + case C_CLERIC: + case C_DRUID: + case C_PALADIN: + msg("Your god looks askance at the weapon you wield."); + fumble_chance += rnd(level_casters) * 5; + break; + + default: + break; + } + } + + if (fumble_chance >= MAX_FUMBLE_CHANCE) + fumble_chance = MAX_FUMBLE_CHANCE; + else if (fumble_chance <= MIN_FUMBLE_CHANCE + sp->sp_level) + fumble_chance = MIN_FUMBLE_CHANCE + sp->sp_level; + + if (fumble_chance > (30 + rnd(50))) + { + if (is_player) + { + int answer; + + msg("Are you sure you want to try for that hard a %s? [n]", + spell_type); + + answer = readchar(); + + if (tolower(answer) != 'y') + { + after = FALSE; + return; + } + else + msg("Here goes..."); + } + else /* Only if the monster is desperate */ + { + if (curp->s_hpt > maxp->s_hpt / 2) + return; + } + } + + /* casting costs food energy */ + + food_left -= sp->sp_cost; + + spell_roll = rnd(100); + + debug("%s(%d) cast '%s' fumble %%%d (rolled %d) ", + monsters[caster->t_index].m_name, curp->s_power, cast_name, + fumble_chance, spell_roll); + + caster->t_rest_hpt = caster->t_rest_pow = 0; + + if (!is_player) /* Stop running. */ + { + running = FALSE; + msg("The %s is casting '%s'.", casters_name, cast_name); + } + + /* The Crown of Might insures that your spells never fumble */ + + if (spell_roll < fumble_chance) + { + if (is_carrying(TR_CROWN)) + message_flags |= CAST_CROWN; + else + { + message_flags |= CAST_CURSED; + + curp->s_power -= min(curp->s_power, + (2 * sp->sp_cost)); /* 2x cost */ + num_fumbles = rnd(((fumble_chance - spell_roll) / 10) + + 1) + rnd(sp->sp_level) + rnd(curp->s_lvl); + num_fumbles = min(10, max(0, num_fumbles)); + + if (num_fumbles >= 6 && rnd(1) == 0) + bless_or_curse = ISCURSED; + else if (num_fumbles < 4) + { + if (is_player) + msg("Your %s fails.", spell_type); + return; + } + } + } + else if (spell_roll > MAX_FUMBLE_CHANCE) + { + if (is_player) + { + message_flags |= CAST_BLESSED; + pstats.s_exp += 3 * sp->sp_cost * curp->s_lvl; + check_level(); + } + + maxp->s_power += sp->sp_cost; + bless_or_curse = ISBLESSED; + } + else + { + if (is_player) /* extra exp for sucessful spells */ + { + if (player.t_ctype == C_MAGICIAN || player.t_ctype == C_ILLUSION) + { + pstats.s_exp += sp->sp_cost * curp->s_lvl; + check_level(); + } + } + + bless_or_curse = sp->sp_flags & ISBLESSED; + curp->s_power -= sp->sp_cost; + } + + /* The Sceptre of Might blesses all your spells */ + + if (is_player && ((bless_or_curse & ISBLESSED) == 0) && + is_carrying(TR_SCEPTRE)) + { + message_flags |= CAST_SEPTRE; + bless_or_curse = ISBLESSED; + } + + if (sp->sp_flags & POT_MAGIC) + quaff(caster, sp->sp_which, bless_or_curse); + else if (sp->sp_flags & SCR_MAGIC) + read_scroll(caster, sp->sp_which, bless_or_curse); + else if (sp->sp_flags & ZAP_MAGIC) + { + if (is_player) + { + do /* Must pick a direction */ + { + msg("Which direction?"); + } + while (get_dir() == FALSE); + } + else + { + delta.x = dir.x; + delta.y = dir.y; + } + do_zap(caster, sp->sp_which, bless_or_curse); + } + else + msg("What a strange %s!", spell_type); + + /* + * Print messages and take fumbles *after* spell has gone off. This + * makes ENCHANT, etc more dangerous + */ + + if (is_player) + { + if (message_flags & CAST_SEPTRE) + msg("The Sceptre enhanced your %s.", spell_type); + if (message_flags & CAST_CROWN) + msg("The Crown wordlessly corrected your %s.", + spell_type); + + switch (message_flags & 0x1) + { + case CAST_CURSED: + msg("You botched your '%s' %s.", cast_name, + spell_type); + fumble_spell(caster, num_fumbles); + break; + case CAST_NORMAL: + msg("You sucessfully cast your '%s' %s.", + cast_name, spell_type); + break; + + case CAST_BLESSED: + msg("Your '%s' %s went superbly.", cast_name, + spell_type); + break; + } + } +} + +/* + spell_name() + returns pointer to spell name +*/ + +char * +spell_name(struct spells *sp, char *buf) +{ + if (buf == NULL) + return("UltraRogue Bug #105"); + + if (sp->sp_flags & POT_MAGIC) + strcpy(buf, p_magic[sp->sp_which].mi_name); + else if (sp->sp_flags & SCR_MAGIC) + strcpy(buf, s_magic[sp->sp_which].mi_name); + else if (sp->sp_flags & ZAP_MAGIC) + strcpy(buf, ws_magic[sp->sp_which].mi_name); + else + strcpy(buf, "unknown spell type"); + + if (sp->sp_flags & ISBLESSED) + strcat(buf, " 2"); + + return(buf); +} + +/* + spell_abrev() + returns pointer to capital letter spell abbreviation +*/ + +char * +spell_abrev(struct spells *sp, char *buf) +{ + if (buf == NULL) + return("UltraRogue Bug #106"); + + if (sp->sp_flags & POT_MAGIC) + strcpy(buf, p_magic[sp->sp_which].mi_abrev); + else if (sp->sp_flags & SCR_MAGIC) + strcpy(buf, s_magic[sp->sp_which].mi_abrev); + else if (sp->sp_flags & ZAP_MAGIC) + strcpy(buf, ws_magic[sp->sp_which].mi_abrev); + else + strcpy(buf, "?????"); + + if (sp->sp_flags & ISBLESSED) + strcat(buf, " 2"); + + return(buf); +} + +/* + fumble_spell() + he blew it. Make him pay +*/ + +void +fumble_spell(struct thing *caster, int num_fumbles) +{ + struct stats *curp = &(caster->t_stats); + struct stats *maxp = &(caster->maxstats); + int is_player = (caster == &player); + + debug("Taking %d fumbles.", num_fumbles); + + switch (num_fumbles) + { + case 10: /* Lose ability */ + if (rnd(5) == 0) + quaff(caster, P_GAINABIL, ISCURSED); + break; + + case 9: /* Lose max spell points */ + + if (rnd(4) == 0) + { + maxp->s_power -= rnd(10); + + if (maxp->s_power <= 5) + maxp->s_power = 5; + } + break; + + case 8: /* Lose all current spell points */ + + if (rnd(3) == 0) + curp->s_power = 0; + else + curp->s_power /= 2; + break; + + case 7: /* Freeze */ + + if (rnd(2) == 0) + { + if (is_player) + no_command++; + else + caster->t_no_move++; + } + break; + + case 6: /* Cast a cursed spell - see below */ + break; + + case 5: /* Become dazed and confused */ + + if (rnd(5) == 0) + quaff(caster, P_CLEAR, ISCURSED); + break; + + case 4: /* Lose hit points */ + + if (is_player) + feel_message(); + if ((curp->s_hpt -= rnd(10)) <= 0) + { + if (is_player) + death(D_SPELLFUMBLE); + else + killed(caster, find_mons(caster->t_pos.y, caster->t_pos.x), + NOMESSAGE, NOPOINTS); + return; + } + break; + + case 3: /* Spell fails */ + break; + + case 2: /* Freeze */ + + if (is_player) + no_command++; + else + caster->t_no_move++; + + break; + + default: + case 1: /* Take double spell points - handled in incant() */ + break; + } +} + +/* + learn_new_spells() + go through player_spells and ISKNOW identified potions, + scrolls, and sticks +*/ + +void +learn_new_spells(void) +{ + struct spells *sp; + int kludge = 0; + char spellbuf[2*LINELEN]; + + for (sp = player_spells; sp->sp_level != -1; sp++) + { + if (sp->sp_flags & POT_MAGIC) + kludge = TYP_POTION; + else if (sp->sp_flags & SCR_MAGIC) + kludge = TYP_SCROLL; + else if (sp->sp_flags & ZAP_MAGIC) + kludge = TYP_STICK; + + if (know_items[kludge][sp->sp_which]) + { + if ((sp->sp_flags & ISKNOW) == FALSE) + debug("Learned new spell '%s'", spell_name(sp,spellbuf)); + sp->sp_flags |= ISKNOW; + } + } +} + +/* + pick_monster_spell() + decide which spell from monst_spells will be cast + returns pointer to spell in monst_spells +*/ + +struct spells * +pick_monster_spell(struct thing *caster) +{ + struct spells *sp = NULL; + struct stats *curp = &(caster->t_stats); + int points_casters = curp->s_power; + + /* Discover castable spells */ + + for (sp = monst_spells; sp->sp_level != -1; sp++) + { + int rnd_number = rnd(2 * sp->sp_level) - sp->sp_level; + int casting_cost = spell_cost[sp->sp_level] + rnd_number; + + if (points_casters >= casting_cost) + sp->sp_flags |= ISKNOW; + } + + /* Decide which spell to cast */ + + if (curp->s_hpt < rnd(caster->maxstats.s_hpt)) /* think defense */ + { + int i; + static const int run_or_heal[NUM_RUN] = + { M_SELFTELEP, M_HLNG2, M_HLNG, M_REGENERATE }; + + for (i = 0; i < NUM_RUN; i++) + { + sp = &monst_spells[run_or_heal[i]]; + + if ((sp->sp_flags & ISKNOW) && rnd(1)) + return(sp); + } + } + + if (on(*caster, ISSLOW)) /* cancel a slow */ + { + sp = &monst_spells[M_HASTE]; + + if (sp->sp_flags & ISKNOW) + return (sp); + } + + if (on(*caster, ISFLEE)) /* stop running away */ + { + sp = &monst_spells[M_SHERO]; + + if (sp->sp_flags & ISKNOW) + return (sp); + } + + if (on(player, ISINVIS) || on(player, ISDISGUISE)) + { + if (off(*caster, CANSEE)) /* look for him */ + { + sp = &monst_spells[M_SEEINVIS]; + + if (sp->sp_flags & ISKNOW) + return (sp); + } + else if (off(*caster, ISINVIS)) /* go invisible */ + { + sp = &monst_spells[M_INVIS]; + + if (sp->sp_flags & ISKNOW) + return (sp); + } + } + + if (on(player, CANINWALL) && (off(*caster, CANINWALL)) && + (rnd(5) == 0)) + { + sp = &monst_spells[M_PHASE]; + + if (sp->sp_flags & ISKNOW) + return (sp); + } + + if (rnd(5) == 0 && has_defensive_spell(player)) + { + sp = &monst_spells[M_CANCEL]; + + if (sp->sp_flags & ISKNOW) + return (sp); + } + + /* Cast an offensive spell */ + + for (sp = &monst_spells[M_OFFENSE]; sp->sp_level != 1; sp++) + { + if ((rnd(3) == 0) && (sp->sp_flags & ISKNOW)) + return (sp); + + if ((rnd(3) == 0) && (sp->sp_flags & ISKNOW)) + { + if (sp->sp_which != WS_MISSILE && + DISTANCE(caster->t_pos, hero) > BOLT_LENGTH) + continue; + else + return(sp); + } + } + + return(NULL); +} + +/* + sort_spells() + called by qsort() +*/ + +int +sort_spells(const void *a, const void *b) +{ + struct spells *sp1, *sp2; + int diff; + char spellbuf[2 * LINELEN]; + char spellbuf2[2 * LINELEN]; + + union /* hack to prevent 'lint' from complaining */ + { + struct spells *s; + const void *vptr; + } s1,s2; + + s1.vptr = a; + s2.vptr = b; + + sp1 = s1.s; + sp2 = s2.s; + + diff = sp1->sp_cost - sp2->sp_cost; + + if (diff != 0) + return(diff); + else + return(strcmp(spell_name(sp1,spellbuf), spell_name(sp1,spellbuf2))); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/main.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,278 @@ +/* + main.c - setup code + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#define _ALL_SOURCE + +#include +#include +#include +#include +#include +#include "rogue.h" + +FILE *fd_score = NULL; + +/* Command line options */ + +int prscore; /* Print scores */ +int prversion; /* Print version info */ + +int +main(int argc, char *argv[]) +{ + int x; + char *env; + time_t lowtime; + time_t now; + int rflag = 0; + char *nm; + float scale; + + for (x = 1; x < argc; x++) + { + if (argv[x][0] != '-') + break; + + switch (argv[x][1]) + { + case 's': + prscore = TRUE; + break; + + case 'v': + prversion = TRUE; + break; + + case 'r': + rflag = TRUE; + break; + + default: + fprintf(stderr,"%s: Unknown option '%c'.\n",argv[0],argv[x][1]); + exit(1); + } + } + + if (!rflag) + { + argc -= (x - 1); + argv += (x - 1); + } + + /* Get default score file */ + + strcpy(score_file, "urogue.scr"); + + fd_score = fopen(score_file, "r+"); + + if (fd_score == NULL) + fd_score = fopen(score_file, "a+"); + + if ((env = getenv("OPTIONS")) != NULL) + parse_opts(env); + + nm = getenv("USER"); + + if (nm != NULL) + strcpy(whoami,nm); + else + strcpy(whoami,"anonymous"); + + lowtime = time(&now); + + dnum = (wizard && getenv("SEED") != NULL ? atoi( getenv("SEED")) : (int)lowtime); + + ur_srandom(dnum); + + if (env == NULL || fruit[0] == '\0') + { + static const char *funfruit[] = + { + "candleberry", "caprifig", "dewberry", "elderberry", + "gooseberry", "guanabana", "hagberry", "ilama", "imbu", + "jaboticaba", "jujube", "litchi", "mombin", "pitanga", + "prickly pear", "rambutan", "sapodilla", "soursop", + "sweetsop", "whortleberry" + }; + + strcpy(fruit, funfruit[rnd(sizeof(funfruit) / sizeof(funfruit[0]))]); + } + + /* put a copy of fruit in the right place */ + + fd_data[1].mi_name = md_strdup(fruit); + + /* print scores */ + + if (prscore) + { + waswizard = TRUE; + score(0L, 0, SCOREIT, 0); + exit(0); + } + + /* check for version option */ + + if (prversion) + { + printf("UltraRogue Version %s.\n", release); + exit(0); + } + + if (wizard) + printf("Hello %s, welcome to dungeon #%d", whoami, dnum); + else + printf("Hello %s, just a moment while I dig the dungeon...", whoami); + + mem_debug(2); + mem_tracking(1); + + fflush(stdout); + + init_things(); /* Set up probabilities of things */ + init_fd(); /* Set up food probabilities */ + init_colors(); /* Set up colors of potions */ + init_stones(); /* Set up stone settings of rings */ + init_materials(); /* Set up materials of wands */ + initscr(); /* Start up cursor package */ + refresh(); + init_names(); /* Set up names of scrolls */ + cbreak(); + crmode(); /* Cbreak mode */ + noecho(); /* Echo off */ + nonl(); + + scale = (float) (LINES * COLS) / (80.0F * 25.0F); /* get food right for */ + /* different screen sizes */ + + food_left = (int) (food_left * scale); + + /* Set up windows */ + + cw = newwin(LINES, COLS, 0, 0); + mw = newwin(LINES, COLS, 0, 0); + hw = newwin(LINES, COLS, 0, 0); + + if (argc == 2 && argv[1][0] != '\0' && !restore(argv[1])) + /* Note: restore returns on error only */ + exit(1); + + waswizard = wizard; /* set wizard flags */ + + init_player(); /* look up things and outfit pack */ + + resurrect = pstats.s_const; + init_exp(); /* set first experience level change */ + init_flags(); /* set initial flags */ + wclear(hw); + wrefresh(hw); + new_level(POSTLEV,0); /* Draw current level */ + + /* Start up daemons and fuses */ + + start_daemon(DAEMON_DOCTOR, &player, AFTER); + + light_fuse(FUSE_SWANDER, 0, WANDERTIME, AFTER); + + start_daemon(DAEMON_STOMACH, 0, AFTER); + start_daemon(DAEMON_RUNNERS, 0, AFTER); + + char_type = player.t_ctype; + player.t_oldpos = hero; + oldrp = roomin(hero); + after = TRUE; + + signal(SIGINT, quit_handler); + + while(playing) + { + do_daemons(BEFORE); + do_fuses(BEFORE); + + command(); /* Command execution */ + + if (after) + do_after_effects(); + } + + fatal(""); + + return(0); +} + +/* + fatal() + Exit the program, printing a message. +*/ + +void +fatal(char *s) +{ + clear(); + move(LINES - 2, 0); + printw("%s", s); + wrefresh(stdscr); + endwin(); + printf("\n"); /* So the cursor doesn't stop at the end of the line */ + exit(100); +} + +/* + rnd() + Pick a very random number. +*/ + +unsigned char +ucrnd(unsigned char range) +{ + return (unsigned char)(range <= 0 ? 0 : (ur_random() & 0x7fffffffL) % range); +} + +short +srnd(short range) +{ + return (short)(range <= 0 ? 0 : (ur_random() & 0x7fffffffL) % range); +} + +int +rnd(int range) +{ + return (range <= 0 ? 0 : (ur_random() & 0x7fffffffL) % range); +} + +unsigned long +ulrnd(unsigned long range) +{ + return(range <= 0 ? 0 : (ur_random() & 0x7fffffffL) % range); +} + +/* + roll() + roll a number of dice +*/ + +int +roll(int number, int sides) +{ + int dtotal = 0; + + while (number--) + dtotal += rnd(sides) + 1; + + return(dtotal); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/maze.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/maze.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,333 @@ +/* + maze.c - functions for dealing with armor + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include "rogue.h" + +static char *frontier; +static char *bits; +static int urlines; +static int urcols; + +/* + domaze() + Draw the maze on this level. +*/ + +void +do_maze(void) +{ + int i, least; + struct room *rp; + struct linked_list *item; + struct object *obj; + struct thing *mp; + int treas; + coord tp; + + for (rp = rooms; rp < &rooms[MAXROOMS]; rp++) + { + rp->r_nexits = 0; /* no exits */ + rp->r_flags = ISGONE; /* kill all rooms */ + rp->r_fires = 0; /* no fires */ + } + + rp = &rooms[0]; /* point to only room */ + rp->r_flags = ISDARK; /* mazes always dark */ + rp->r_pos.x = 0; /* room fills whole screen */ + rp->r_pos.y = 1; + rp->r_max.x = COLS - 1; + rp->r_max.y = LINES - 3; + draw_maze(); /* put maze into window */ + + /* add some gold to make it worth looking for */ + + item = spec_item(GOLD, 0, 0, 0); + obj = OBJPTR(item); + obj->o_count *= (rnd(10) + 5); /* add in one large hunk */ + + do + { + rnd_pos(rp, &tp); + } + while( mvwinch(stdscr, tp.y, tp.x) != FLOOR); + + obj->o_pos = tp; + add_obj(item, tp.y, tp.x); + + /* add in some food to make sure he has enough */ + + item = spec_item(FOOD, 0, 0, 0); + obj = OBJPTR(item); + + do + { + rnd_pos(rp, &tp); + } + while( mvwinch(stdscr, tp.y, tp.x) != FLOOR); + + obj->o_pos = tp; + add_obj(item, tp.y, tp.x); + + if (rnd(100) < 5) /* 5% for treasure maze level */ + { + treas = TRUE; + least = 20; + debug("Treasure maze."); + } + else /* normal maze level */ + { + least = 1; + treas = FALSE; + } + + for (i = 0; i < level + least; i++) + { + if (!treas && rnd(100) < 50) /* put in some little buggers */ + continue; + + /* Put the monster in */ + + item = new_item(sizeof *mp); + + mp = THINGPTR(item); + + do + { + rnd_pos(rp, &tp); + } + while(mvwinch(stdscr, tp.y, tp.x) != FLOOR); + + new_monster(item, randmonster(NOWANDER, NOGRAB), &tp, NOMAXSTATS); + + /* See if we want to give it a treasure to carry around. */ + + if (rnd(100) < monsters[mp->t_index].m_carry) + attach(mp->t_pack, new_thing()); + + /* If it carries gold, give it some */ + + if (on(*mp, CARRYGOLD)) + { + item = spec_item(GOLD, 0, 0, 0); + obj = OBJPTR(item); + obj->o_count = GOLDCALC + GOLDCALC + GOLDCALC; + obj->o_pos = mp->t_pos; + attach(mp->t_pack, item); + } + + } +} + +/* + draw_maze() + Generate and draw the maze on the screen +*/ + +void +draw_maze(void) +{ + int i, j, more; + char *ptr; + + urlines = (LINES - 3) / 2; + urcols = (COLS - 1) / 2; + + bits = ur_alloc((unsigned int) ((LINES - 3) * (COLS - 1))); + frontier = ur_alloc((unsigned int) (urlines * urcols)); + ptr = frontier; + + while (ptr < (frontier + (urlines * urcols))) + *ptr++ = TRUE; + + for (i = 0; i < LINES - 3; i++) + { + for (j = 0; j < COLS - 1; j++) + { + if (i % 2 == 1 && j % 2 == 1) + *moffset(i, j) = FALSE; /* floor */ + else + *moffset(i, j) = TRUE; /* wall */ + } + } + + for (i = 0; i < urlines; i++) + { + for (j = 0; j < urcols; j++) + { + do + more = findcells(i, j); + while (more != 0); + } + } + + crankout(); + ur_free(frontier); + ur_free(bits); +} + +/* + moffset() + Calculate memory address for bits +*/ + +char * +moffset(int y, int x) +{ + return (bits + (y * (COLS - 1)) + x); +} + +/* + foffset() + Calculate memory address for frontier +*/ + +char * +foffset(int y, int x) +{ + return (frontier + (y * urcols) + x); +} + +/* + findcells() + Figure out cells to open up +*/ + +int +findcells(int y, int x) +{ + int rtpos, i; + + struct + { + int num_pos; /* number of frontier cells next to you */ + + struct + { + int y_pos; + int x_pos; + } conn[4]; /* the y,x position of above cell */ + } mborder; + + *foffset(y, x) = FALSE; + mborder.num_pos = 0; + + if (y < urlines - 1) { /* look below */ + if (*foffset(y + 1, x)) + { + mborder.conn[mborder.num_pos].y_pos = y + 1; + mborder.conn[mborder.num_pos].x_pos = x; + mborder.num_pos += 1; + } + } + + if (y > 0) /* look above */ + { + if (*foffset(y - 1, x)) + { + mborder.conn[mborder.num_pos].y_pos = y - 1; + mborder.conn[mborder.num_pos].x_pos = x; + mborder.num_pos += 1; + } + } + + if (x < urcols - 1) /* look right */ + { + if (*foffset(y, x + 1)) + { + mborder.conn[mborder.num_pos].y_pos = y; + mborder.conn[mborder.num_pos].x_pos = x + 1; + mborder.num_pos += 1; + } + } + + if (x > 0) /* look left */ + { + if (*foffset(y, x - 1)) + { + mborder.conn[mborder.num_pos].y_pos = y; + mborder.conn[mborder.num_pos].x_pos = x - 1; + mborder.num_pos += 1; + } + } + + if (mborder.num_pos == 0)/* no neighbors available */ + return(0); + else + { + i = rnd(mborder.num_pos); + rtpos = mborder.num_pos - 1; + rmwall(mborder.conn[i].y_pos, mborder.conn[i].x_pos, y, x); + return(rtpos); + } +} + +/* + rmwall() + Removes appropriate walls from the maze +*/ + +void +rmwall(int newy, int newx, int oldy, int oldx) +{ + int xdif, ydif; + + xdif = newx - oldx; + ydif = newy - oldy; + + *moffset((oldy * 2) + ydif + 1, (oldx * 2) + xdif + 1) = FALSE; + + findcells(newy, newx); +} + + +/* + crankout() + Does actual drawing of maze to window +*/ + +void +crankout(void) +{ + int x, y; + + for (y = 0; y < LINES - 3; y++) + { + move(y + 1, 0); + + for (x = 0; x < COLS - 1; x++) + { + if (*moffset(y, x)) /* here is a wall */ + { + if (y == 0 || y == LINES - 4) /* top or bottom line */ + addch('-'); + else if (x == 0 || x == COLS - 2) /* left | right side */ + addch('|'); + else if (y % 2 == 0 && x % 2 == 0) + { + if (*moffset(y, x - 1) || *moffset(y, x + 1)) + addch('-'); + else + addch('|'); + } + else if (y % 2 == 0) + addch('-'); + else + addch('|'); + } + else + addch(FLOOR); + } + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/mdport.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/mdport.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1270 @@ +/* + mdport.c - Machine Dependent Code for Porting Unix/Curses games + + Copyright (C) 2005 Nicholas J. Kisseberth + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +#if defined(_WIN32) +#include +#include +#include +#pragma warning( disable: 4201 ) +#include +#pragma warning( default: 4201 ) +#include +#include +#include +#undef MOUSE_MOVED +#elif defined(__DJGPP__) +#include +#else +#include +#include +#include +#include +#endif + +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) +#define PATH_MAX MAX_PATH +#endif + +#include + +#if defined(__INTERIX) || defined(__MSYS__) +#include +#else +#ifdef NCURSES_VERSION +#include +#endif +#endif + +#include +#include +#include +#include +#include + +#define MOD_MOVE(c) (toupper(c) ) + +void +md_init() +{ +#ifdef __INTERIX + char *term; + + term = getenv("TERM"); + + if (term == NULL) + setenv("TERM","interix"); +#endif +#if defined(__DJGPP__) || defined(_WIN32) + _fmode = _O_BINARY; +#endif +#if defined(__CYGWIN__) || defined(__MSYS__) + ESCDELAY=250; +#endif +} + +int +md_hasclreol() +{ +#ifndef attron + return(!CE); +#elif !defined(__PDCURSES__) + return(clr_eol != NULL); +#else + return(TRUE); +#endif +} + +#ifdef attron +# define _puts(s) tputs(s, 0, md_putchar); +# define SO enter_standout_mode +# define SE exit_standout_mode +#endif + +void +md_putchar(int c) +{ + putchar(c); +} + +static int md_standout_mode = 0; + +void +md_raw_standout() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + HANDLE hStdout; + WORD fgattr,bgattr; + + if (md_standout_mode == 0) + { + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hStdout, &csbiInfo); + fgattr = (csbiInfo.wAttributes & 0xF); + bgattr = (csbiInfo.wAttributes & 0xF0); + SetConsoleTextAttribute(hStdout,(fgattr << 4) | (bgattr >> 4)); + md_standout_mode = 1; + } +#elif !defined(__PDCURSES__) + _puts(SO); + fflush(stdout); +#endif +} + +void +md_raw_standend() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + HANDLE hStdout; + WORD fgattr,bgattr; + + if (md_standout_mode == 1) + { + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hStdout, &csbiInfo); + fgattr = (csbiInfo.wAttributes & 0xF); + bgattr = (csbiInfo.wAttributes & 0xF0); + SetConsoleTextAttribute(hStdout,(fgattr << 4) | (bgattr >> 4)); + md_standout_mode = 0; + } +#elif !defined(__PDCURSES__) + _puts(SE); + fflush(stdout); +#endif +} + +int +md_unlink_open_file(char *file, int inf) +{ +#ifdef _WIN32 + _close(inf); + _chmod(file, 0600); + return( _unlink(file) ); +#else + return(unlink(file)); +#endif +} + +int +md_unlink(char *file) +{ +#ifdef _WIN32 + _chmod(file, 0600); + return( _unlink(file) ); +#else + return(unlink(file)); +#endif +} + +int +md_creat(char *file, int mode) +{ + int fd; +#ifdef _WIN32 + mode = _S_IREAD | _S_IWRITE; + fd = _open(file,O_CREAT | O_EXCL | O_WRONLY, mode); +#else + fd = open(file,O_CREAT | O_EXCL | O_WRONLY, mode); +#endif + + return(fd); +} + + +void +md_normaluser() +{ +#ifndef _WIN32 + setuid(getuid()); + setgid(getgid()); +#endif +} + +int +md_getuid() +{ +#ifndef _WIN32 + return( getuid() ); +#else + return(42); +#endif +} + +char * +md_getusername(int uid) +{ + static char login[80]; + char *l = NULL; +#ifdef _WIN32 + LPSTR mybuffer; + DWORD size = UNLEN + 1; + TCHAR buffer[UNLEN + 1]; + + mybuffer = buffer; + if (uid != md_getuid()) + strcpy(mybuffer, "someone"); + else + GetUserName(mybuffer,&size); + l = mybuffer; +#endif +#if !defined(_WIN32) && !defined(DJGPP) + struct passwd *pw; + + pw = getpwuid(getuid()); + + l = pw->pw_name; +#endif + + if ((l == NULL) || (*l == '\0')) + if ( (l = getenv("USERNAME")) == NULL ) + if ( (l = getenv("LOGNAME")) == NULL ) + if ( (l = getenv("USER")) == NULL ) + l = "nobody"; + + strncpy(login,l,80); + login[79] = 0; + + return(login); +} + +char * +md_gethomedir() +{ + static char homedir[PATH_MAX]; + char *h = NULL; + size_t len; +#if defined(_WIN32) + TCHAR szPath[PATH_MAX]; +#endif +#if defined(_WIN32) || defined(DJGPP) + char slash = '\\'; +#else + char slash = '/'; + struct passwd *pw; + pw = getpwuid(getuid()); + + h = pw->pw_dir; + + if (strcmp(h,"/") == 0) + h = NULL; +#endif + homedir[0] = 0; +#ifdef _WIN32 + if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) + h = szPath; +#endif + + if ( (h == NULL) || (*h == '\0') ) + if ( (h = getenv("HOME")) == NULL ) + if ( (h = getenv("HOMEDRIVE")) == NULL) + h = ""; + else + { + strncpy(homedir,h,PATH_MAX-1); + homedir[PATH_MAX-1] = 0; + + if ( (h = getenv("HOMEPATH")) == NULL) + h = ""; + } + + + len = strlen(homedir); + strncat(homedir,h,PATH_MAX-len-1); + len = strlen(homedir); + + if ((len > 0) && (homedir[len-1] != slash)) { + homedir[len] = slash; + homedir[len+1] = 0; + } + + return(homedir); +} + +void +md_sleep(int s) +{ +#ifdef _WIN32 + Sleep(s); +#else + sleep(s); +#endif +} + +char * +md_getshell() +{ + static char shell[PATH_MAX]; + char *s = NULL; +#ifdef _WIN32 + char *def = "C:\\WINDOWS\\SYSTEM32\\CMD.EXE"; +#elif defined(__DJGPP__) + char *def = "C:\\COMMAND.COM"; +#else + char *def = "/bin/sh"; + struct passwd *pw; + pw = getpwuid(getuid()); + s = pw->pw_shell; +#endif + if ((s == NULL) || (*s == '\0')) + if ( (s = getenv("COMSPEC")) == NULL) + if ( (s = getenv("SHELL")) == NULL) + if ( (s = getenv("SystemRoot")) == NULL) + s = def; + + strncpy(shell,s,PATH_MAX); + shell[PATH_MAX-1] = 0; + + return(shell); +} + +int +md_shellescape() +{ +#if (!defined(_WIN32) && !defined(__DJGPP__)) + int ret_status; + int pid; + void (*myquit)(int); + void (*myend)(int); +#endif + char *sh; + + sh = md_getshell(); + +#if defined(_WIN32) + return((int)_spawnl(_P_WAIT,sh,"shell",NULL,0)); +#elif defined(__DJGPP__) + return ( spawnl(P_WAIT,sh,"shell",NULL,0) ); +#else + while((pid = fork()) < 0) + sleep(1); + + if (pid == 0) /* Shell Process */ + { + /* + * Set back to original user, just in case + */ + setuid(getuid()); + setgid(getgid()); + execl(sh == NULL ? "/bin/sh" : sh, "shell", "-i", 0); + perror("No shelly"); + _exit(-1); + } + else /* Application */ + { + myend = signal(SIGINT, SIG_IGN); +#ifdef SIGQUIT + myquit = signal(SIGQUIT, SIG_IGN); +#endif + while (wait(&ret_status) != pid) + continue; + + signal(SIGINT, myquit); +#ifdef SIGQUIT + signal(SIGQUIT, myend); +#endif + } + + return(ret_status); +#endif +} + +int +directory_exists(char *dirname) +{ + struct stat sb; + + if (stat(dirname, &sb) == 0) /* path exists */ + return (sb.st_mode & S_IFDIR); + + return(0); +} + +char * +md_getroguedir() +{ + static char path[1024]; + char *end,*home; + + if ( (home = getenv("ROGUEHOME")) != NULL) + { + if (*home) + { + strncpy(path, home, PATH_MAX - 20); + + end = &path[strlen(path)-1]; + + while( (end >= path) && ((*end == '/') || (*end == '\\'))) + *end-- = '\0'; + + if (directory_exists(path)) + return(path); + } + } + + if (directory_exists("/var/games/roguelike")) + return("/var/games/roguelike"); + if (directory_exists("/var/lib/roguelike")) + return("/var/lib/roguelike"); + if (directory_exists("/var/roguelike")) + return("/var/roguelike"); + if (directory_exists("/usr/games/lib")) + return("/usr/games/lib"); + if (directory_exists("/games/roguelik")) + return("/games/roguelik"); + if (directory_exists(md_gethomedir())) + return(md_gethomedir()); + return(""); +} + +char * +md_getrealname(int uid) +{ + static char uidstr[20]; +#if !defined(_WIN32) && !defined(DJGPP) + struct passwd *pp; + + if ((pp = getpwuid(uid)) == NULL) + { + sprintf(uidstr,"%d", uid); + return(uidstr); + } + else + return(pp->pw_name); +#else + sprintf(uidstr,"%d", uid); + return(uidstr); +#endif +} + +extern char *xcrypt(char *key, char *salt); + +char * +md_crypt(char *key, char *salt) +{ + return( xcrypt(key,salt) ); +} + +char * +md_getpass(char *prompt) +{ +#ifdef _WIN32 + static char password_buffer[9]; + char *p = password_buffer; + int c, count = 0; + int max_length = 9; + + fflush(stdout); + /* If we can't prompt, abort */ + if (fputs(prompt, stderr) < 0) + { + *p = '\0'; + return NULL; + } + + for(;;) + { + /* Get a character with no echo */ + c = _getch(); + + /* Exit on interrupt (^c or ^break) */ + if (c == '\003' || c == 0x100) + exit(1); + + /* Terminate on end of line or file (^j, ^m, ^d, ^z) */ + if (c == '\r' || c == '\n' || c == '\004' || c == '\032') + break; + + /* Back up on backspace */ + if (c == '\b') + { + if (count) + count--; + else if (p > password_buffer) + p--; + continue; + } + + /* Ignore DOS extended characters */ + if ((c & 0xff) != c) + continue; + + /* Add to password if it isn't full */ + if (p < password_buffer + max_length - 1) + *p++ = (char) c; + else + count++; + } + *p = '\0'; + + fputc('\n', stderr); + + return password_buffer; +#else + return( (char *) getpass(prompt) ); +#endif +} + + +int md_endian = 0x01020304; + +unsigned long int +md_ntohl(unsigned long int x) +{ +#ifdef _WIN32 + if ( *((char *)&md_endian) == 0x01 ) + return(x); + else + return( ((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24) ); +#else + return( ntohl(x) ); +#endif +} + +unsigned long int +md_htonl(unsigned long int x) +{ +#ifdef _WIN32 + if ( *((char *)&md_endian) == 0x01 ) + return(x); + else + return( ((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24) ); +#else + return( htonl(x) ); +#endif +} + +int +md_ucount() +{ +#ifdef __DJGPP__ + return(1); +#elif defined(_WIN32) + return(1); +#else + struct utmpx *up=NULL; + int count=0; + + setutxent(); + do + { + up = getutxent(); + if (up && up->ut_type == USER_PROCESS) + count++; + } while(up != NULL); + + endutxent(); + + return(count); +#endif +} + +int +md_getloadavg(double *avg) +{ +#if defined(__GLIBC__) || defined(_BSD) + if (getloadavg(avg, 3) == -1) +#endif + { + avg[0] = avg[1] = avg[2] = 0.0; + return -1; + } +} + +long +md_random() +{ +#ifdef _WIN32 + return(rand()); +#else + return( random() ); +#endif +} + +void +md_srandom(unsigned x) +{ +#ifdef _WIN32 + srand(x); +#else + srandom(x); +#endif +} + +int +md_rand() +{ +#ifdef _WIN32 + return(rand()); +#else + return(lrand48() & 0x7fffffff); +#endif +} + +void +md_srand(int seed) +{ +#ifdef _WIN32 + srand(seed); +#else + srand48(seed); +#endif +} + +char * +md_strdup(const char *s) +{ +#ifdef _WIN32 + return( _strdup(s) ); +#else + return(strdup(s)); +#endif +} + +long +md_memused() +{ +#ifdef _WIN32 + MEMORYSTATUS stat; + + GlobalMemoryStatus(&stat); + + return((long)stat.dwTotalPageFile); +#else + return( (long)sbrk(0) ); +#endif +} + +char * +md_gethostname() +{ + static char nodename[80]; + char *n = NULL; +#if !defined(_WIN32) && !defined(__DJGPP__) + struct utsname ourname; + + if (uname(&ourname) == 0) + n = ourname.nodename; +#endif + if ((n == NULL) || (*n == '\0')) + if ( (n = getenv("COMPUTERNAME")) == NULL) + if ( (n = getenv("HOSTNAME")) == NULL) + n = "localhost"; + + strncpy(nodename, n, 80); + nodename[79] = 0; + + return(nodename); +} + +int +md_erasechar() +{ +#ifdef BSD + return(_tty.sg_erase); /* process erase character */ +#elif defined(USG5_0) + return(_tty.c_cc[VERASE]); /* process erase character */ +#else /* USG5_2 .... curses */ + return( erasechar() ); /* process erase character */ +#endif +} + +int +md_killchar() +{ +#ifdef BSD + return(_tty.sg_kill); +#elif defined(USG5_0) + return(_tty.c_cc[VKILL]); +#else /* USG5_2 ..... curses */ + return( killchar() ); +#endif +} + +/* + * unctrl: + * Print a readable version of a certain character + */ + +char * +md_unctrl(char ch) +{ +#if USG5_0 + extern char *_unctrl[]; /* Defined in curses library */ + + return _unctrl[ch&0177]; +#else + return( unctrl(ch) ); +#endif +} + +void +md_flushinp() +{ +#ifdef BSD + ioctl(0, TIOCFLUSH); +#elif defined(USG5_0) + ioctl(_tty_ch,TCFLSH,0) +#else /* USG5_2.... curses */ + flushinp(); +#endif +} + +/* + Cursor/Keypad Support + + Sadly Cursor/Keypad support is less straightforward than it should be. + + The various terminal emulators/consoles choose to differentiate the + cursor and keypad keys (with modifiers) in different ways (if at all!). + Furthermore they use different code set sequences for each key only + a subset of which the various curses libraries recognize. Partly due + to incomplete termcap/terminfo entries and partly due to inherent + limitations of those terminal capability databases. + + I give curses first crack at decoding the sequences. If it fails to decode + it we check for common ESC-prefixed sequences. + + All cursor/keypad results are translated into standard rogue movement + commands. + + Unmodified keys are translated to walk commands: hjklyubn + Modified (shift,control,alt) are translated to run commands: HJKLYUBN + + Console and supported (differentiated) keys + Interix: Cursor Keys, Keypad, Ctl-Keypad + Cygwin: Cursor Keys, Keypad, Alt-Cursor Keys + MSYS: Cursor Keys, Keypad, Ctl-Cursor Keys, Ctl-Keypad + Win32: Cursor Keys, Keypad, Ctl/Shift/Alt-Cursor Keys, Ctl/Alt-Keypad + DJGPP: Cursor Keys, Keypad, Ctl/Shift/Alt-Cursor Keys, Ctl/Alt-Keypad + + Interix Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC [D, ESC F^, ESC [D, ESC [D /# Left #/ + ESC [C, ESC F$, ESC [C, ESC [C /# Right #/ + ESC [A, ESC F-, local win, ESC [A /# Up #/ + ESC [B, ESC F+, local win, ESC [B /# Down #/ + ESC [H, ESC [H, ESC [H, ESC [H /# Home #/ + ESC [S, local win, ESC [S, ESC [S /# Page Up #/ + ESC [T, local win, ESC [T, ESC [T /# Page Down #/ + ESC [U, ESC [U, ESC [U, ESC [U /# End #/ + ESC [D, ESC F^, ESC [D, O /# Keypad Left #/ + ESC [C, ESC F$, ESC [C, O /# Keypad Right #/ + ESC [A, ESC [A, ESC [-1, O /# Keypad Up #/ + ESC [B, ESC [B, ESC [-2, O /# Keypad Down #/ + ESC [H, ESC [H, ESC [-263, O /# Keypad Home #/ + ESC [S, ESC [S, ESC [-19, O /# Keypad PgUp #/ + ESC [T, ESC [T, ESC [-20, O /# Keypad PgDn #/ + ESC [U, ESC [U, ESC [-21, O /# Keypad End #/ + nothing, nothing, nothing, O /# Kaypad 5 #/ + + Interix Console (term=interix, ncurses) + ============================== + KEY_LEFT, ESC F^, KEY_LEFT, KEY_LEFT /# Left #/ + KEY_RIGHT, ESC F$, KEY_RIGHT, KEY_RIGHT /# Right #/ + KEY_UP, 0x146, local win, KEY_UP /# Up #/ + KEY_DOWN, 0x145, local win, KEY_DOWN /# Down #/ + ESC [H, ESC [H, ESC [H, ESC [H /# Home #/ + KEY_PPAGE, local win, KEY_PPAGE, KEY_PPAGE /# Page Up #/ + KEY_NPAGE, local win, KEY_NPAGE, KEY_NPAGE /# Page Down #/ + KEY_LL, KEY_LL, KEY_LL, KEY_LL /# End #/ + KEY_LEFT, ESC F^, ESC [-4, O /# Keypad Left #/ + KEY_RIGHT, ESC F$, ESC [-3, O /# Keypad Right #/ + KEY_UP, KEY_UP, ESC [-1, O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, ESC [-2, O /# Keypad Down #/ + ESC [H, ESC [H, ESC [-263, O /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, ESC [-19, O /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, ESC [-20, O /# Keypad PgDn #/ + KEY_LL, KEY_LL, ESC [-21, O /# Keypad End #/ + nothing, nothing, nothing, O /# Keypad 5 #/ + + Cygwin Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC [D, ESC ESC [D /# Left #/ + ESC [C, ESC [C, ESC [C, ESC ESC [C /# Rght #/ + ESC [A, ESC [A, ESC [A, ESC ESC [A /# Up #/ + ESC [B, ESC [B, ESC [B, ESC ESC [B /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC ESC [1~ /# Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC ESC [5~ /# Page Up #/ + ESC [6~, ESC [6~, ESC [6~, ESC ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, ESC ESC [D,O /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, ESC ESC [C,O /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, ESC ESC [A,O /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, ESC ESC [B,O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC ESC [1~,O /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC ESC [5~,O /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, ESC ESC [6~,O /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, ESC ESC [4~,O /# Keypad End #/ + ESC [-71, nothing, nothing, O /# Keypad 5 #/ + + Cygwin Console (term=cygwin, ncurses) + ============================== + KEY_LEFT, KEY_LEFT, KEY_LEFT, ESC-260 /# Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, ESC-261 /# Rght #/ + KEY_UP, KEY_UP, KEY_UP, ESC-259 /# Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, ESC-258 /# Down #/ + KEY_HOME, KEY_HOME, KEY_HOME, ESC-262 /# Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, ESC-339 /# Page Up #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, ESC-338 /# Page Down #/ + KEY_END, KEY_END, KEY_END, ESC-360 /# End #/ + KEY_LEFT, KEY_LEFT, KEY_LEFT, ESC-260,O /# Keypad Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, ESC-261,O /# Keypad Right #/ + KEY_UP, KEY_UP, KEY_UP, ESC-259,O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, ESC-258,O /# Keypad Down #/ + KEY_HOME, KEY_HOME, KEY_HOME, ESC-262,O /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, ESC-339,O /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, ESC-338,O /# Keypad PgDn #/ + KEY_END, KEY_END, KEY_END, ESC-360,O /# Keypad End #/ + ESC [G, nothing, nothing, O /# Keypad 5 #/ + + MSYS Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC OD, ESC [d, ESC Od nothing /# Left #/ + ESC OE, ESC [e, ESC Oe, nothing /# Right #/ + ESC OA, ESC [a, ESC Oa, nothing /# Up #/ + ESC OB, ESC [b, ESC Ob, nothing /# Down #/ + ESC [7~, ESC [7$, ESC [7^, nothing /# Home #/ + ESC [5~, local window, ESC [5^, nothing /# Page Up #/ + ESC [6~, local window, ESC [6^, nothing /# Page Down #/ + ESC [8~, ESC [8$, ESC [8^, nothing /# End #/ + ESC OD, ESC [d, ESC Od O /# Keypad Left #/ + ESC OE, ESC [c, ESC Oc, O /# Keypad Right #/ + ESC OA, ESC [a, ESC Oa, O /# Keypad Up #/ + ESC OB, ESC [b, ESC Ob, O /# Keypad Down #/ + ESC [7~, ESC [7$, ESC [7^, O /# Keypad Home #/ + ESC [5~, local window, ESC [5^, O /# Keypad PgUp #/ + ESC [6~, local window, ESC [6^, O /# Keypad PgDn #/ + ESC [8~, ESC [8$, ESC [8^, O /# Keypad End #/ + 11, 11, 11, O /# Keypad 5 #/ + + MSYS Console (term=rxvt, ncurses) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_SLEFT, 514 nothing /# Left #/ + KEY_RIGHT, KEY_SRIGHT, 516, nothing /# Right #/ + KEY_UP, 518, 519, nothing /# Up #/ + KEY_DOWN, 511, 512, nothing /# Down #/ + KEY_HOME, KEY_SHOME, ESC [7^, nothing /# Home #/ + KEY_PPAGE, local window, ESC [5^, nothing /# Page Up #/ + KEY_NPAGE, local window, ESC [6^, nothing /# Page Down #/ + KEY_END, KEY_SEND, KEY_EOL, nothing /# End #/ + KEY_LEFT, KEY_SLEFT, 514 O /# Keypad Left #/ + KEY_RIGHT, KEY_SRIGHT, 516, O /# Keypad Right #/ + KEY_UP, 518, 519, O /# Keypad Up #/ + KEY_DOWN, 511, 512, O /# Keypad Down #/ + KEY_HOME, KEY_SHOME, ESC [7^, O /# Keypad Home #/ + KEY_PPAGE, local window, ESC [5^, O /# Keypad PgUp #/ + KEY_NPAGE, local window, ESC [6^, O /# Keypad PgDn #/ + KEY_END, KEY_SEND, KEY_EOL, O /# Keypad End #/ + 11, 11, 11, O /# Keypad 5 #/ + + Win32 Console (raw, pdcurses) + DJGPP Console (raw, pdcurses) + ============================== + normal shift ctrl alt + 260, 391, 443, 493 /# Left #/ + 261, 400, 444, 492 /# Right #/ + 259, 547, 480, 490 /# Up #/ + 258, 548, 481, 491 /# Down #/ + 262, 388, 447, 524 /# Home #/ + 339, 396, 445, 526 /# Page Up #/ + 338, 394, 446, 520 /# Page Down #/ + 358, 384, 448, 518 /# End #/ + 452, 52('4'), 511, 521 /# Keypad Left #/ + 454, 54('6'), 513, 523 /# Keypad Right #/ + 450, 56('8'), 515, 525 /# Keypad Up #/ + 456, 50('2'), 509, 519 /# Keypad Down #/ + 449, 55('7'), 514, 524 /# Keypad Home #/ + 451, 57('9'), 516, 526 /# Keypad PgUp #/ + 457, 51('3'), 510, 520 /# Keypad PgDn #/ + 455, 49('1'), 508, 518 /# Keypad End #/ + 453, 53('5'), 512, 522 /# Keypad 5 #/ + + Win32 Console (pdcurses, MSVC/MingW32) + DJGPP Console (pdcurses) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_SLEFT, CTL_LEFT, ALT_LEFT /# Left #/ + KEY_RIGHT, KEY_SRIGHT, CTL_RIGHT, ALT_RIGHT /# Right #/ + KEY_UP, KEY_SUP, CTL_UP, ALT_UP /# Up #/ + KEY_DOWN, KEY_SDOWN, CTL_DOWN, ALT_DOWN /# Down #/ + KEY_HOME, KEY_SHOME, CTL_HOME, ALT_HOME /# Home #/ + KEY_PPAGE, KEY_SPREVIOUS, CTL_PGUP, ALT_PGUP /# Page Up #/ + KEY_NPAGE, KEY_SNEXTE, CTL_PGDN, ALT_PGDN /# Page Down #/ + KEY_END, KEY_SEND, CTL_END, ALT_END /# End #/ + KEY_B1, 52('4'), CTL_PAD4, ALT_PAD4 /# Keypad Left #/ + KEY_B3, 54('6'), CTL_PAD6, ALT_PAD6 /# Keypad Right #/ + KEY_A2, 56('8'), CTL_PAD8, ALT_PAD8 /# Keypad Up #/ + KEY_C2, 50('2'), CTL_PAD2, ALT_PAD2 /# Keypad Down #/ + KEY_A1, 55('7'), CTL_PAD7, ALT_PAD7 /# Keypad Home #/ + KEY_A3, 57('9'), CTL_PAD9, ALT_PAD9 /# Keypad PgUp #/ + KEY_C3, 51('3'), CTL_PAD3, ALT_PAD3 /# Keypad PgDn #/ + KEY_C1, 49('1'), CTL_PAD1, ALT_PAD1 /# Keypad End #/ + KEY_B2, 53('5'), CTL_PAD5, ALT_PAD5 /# Keypad 5 #/ + + Windows Telnet (raw) + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC [D, ESC [D /# Left #/ + ESC [C, ESC [C, ESC [C, ESC [C /# Right #/ + ESC [A, ESC [A, ESC [A, ESC [A /# Up #/ + ESC [B, ESC [B, ESC [B, ESC [B /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC [5~ /# Page Up #/ + ESC [6~, ESC [6~, ESC [6~, ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, ESC [D /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, ESC [C /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, ESC [A /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, ESC [B /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC [5~ /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, ESC [6~ /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# Keypad End #/ + nothing, nothing, nothing, nothing /# Keypad 5 #/ + + Windows Telnet (term=xterm) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_LEFT, KEY_LEFT, KEY_LEFT /# Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, KEY_RIGHT /# Right #/ + KEY_UP, KEY_UP, KEY_UP, KEY_UP /# Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, KEY_DOWN /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, KEY_PPAGE /# Page Up #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, KEY_NPAGE /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# End #/ + KEY_LEFT, KEY_LEFT, KEY_LEFT, O /# Keypad Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, O /# Keypad Right #/ + KEY_UP, KEY_UP, KEY_UP, O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, KEY_PPAGE /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, KEY_NPAGE /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, O /# Keypad End #/ + ESC [-71, nothing, nothing, O /# Keypad 5 #/ + + PuTTY + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC OD, ESC [D /# Left #/ + ESC [C, ESC [C, ESC OC, ESC [C /# Right #/ + ESC [A, ESC [A, ESC OA, ESC [A /# Up #/ + ESC [B, ESC [B, ESC OB, ESC [B /# Down #/ + ESC [1~, ESC [1~, local win, ESC [1~ /# Home #/ + ESC [5~, local win, local win, ESC [5~ /# Page Up #/ + ESC [6~, local win, local win, ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, local win, ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, O /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, O /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, O /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, O /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, O /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, O /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, O /# Keypad End #/ + nothing, nothing, nothing, O /# Keypad 5 #/ + + PuTTY + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_LEFT, ESC OD, ESC KEY_LEFT /# Left #/ + KEY_RIGHT KEY_RIGHT, ESC OC, ESC KEY_RIGHT /# Right #/ + KEY_UP, KEY_UP, ESC OA, ESC KEY_UP /# Up #/ + KEY_DOWN, KEY_DOWN, ESC OB, ESC KEY_DOWN /# Down #/ + ESC [1~, ESC [1~, local win, ESC ESC [1~ /# Home #/ + KEY_PPAGE local win, local win, ESC KEY_PPAGE /# Page Up #/ + KEY_NPAGE local win, local win, ESC KEY_NPAGE /# Page Down #/ + ESC [4~, ESC [4~, local win, ESC ESC [4~ /# End #/ + ESC Ot, ESC Ot, ESC Ot, O /# Keypad Left #/ + ESC Ov, ESC Ov, ESC Ov, O /# Keypad Right #/ + ESC Ox, ESC Ox, ESC Ox, O /# Keypad Up #/ + ESC Or, ESC Or, ESC Or, O /# Keypad Down #/ + ESC Ow, ESC Ow, ESC Ow, O /# Keypad Home #/ + ESC Oy, ESC Oy, ESC Oy, O /# Keypad PgUp #/ + ESC Os, ESC Os, ESC Os, O /# Keypad PgDn #/ + ESC Oq, ESC Oq, ESC Oq, O /# Keypad End #/ + ESC Ou, ESC Ou, ESC Ou, O /# Keypad 5 #/ +*/ + +#define M_NORMAL 0 +#define M_ESC 1 +#define M_KEYPAD 2 +#define M_TRAIL 3 + +int +md_readchar(WINDOW *win) +{ + int ch = 0; + int lastch = 0; + int mode = M_NORMAL; + int mode2 = M_NORMAL; + + for(;;) + { + ch = wgetch(win); + + if (ch == ERR) /* timed out waiting for valid sequence */ + { /* flush input so far and start over */ + mode = M_NORMAL; + nocbreak(); + raw(); + ch = 27; + break; + } + + if (mode == M_TRAIL) + { + if (ch == '^') /* msys console : 7,5,6,8: modified*/ + ch = MOD_MOVE( toupper(lastch) ); + + if (ch == '~') /* cygwin console: 1,5,6,4: normal */ + ch = tolower(lastch); /* windows telnet: 1,5,6,4: normal */ + /* msys console : 7,5,6,8: normal */ + + if (mode2 == M_ESC) /* cygwin console: 1,5,6,4: modified*/ + ch = MOD_MOVE( toupper(ch) ); + + break; + } + + if (mode == M_ESC) + { + if (ch == 27) + { + mode2 = M_ESC; + continue; + } + + if ((ch == 'F') || (ch == 'O') || (ch == '[')) + { + mode = M_KEYPAD; + continue; + } + + + switch(ch) + { + /* Cygwin Console */ + /* PuTTY */ + case KEY_LEFT : ch = MOD_MOVE('H'); break; + case KEY_RIGHT: ch = MOD_MOVE('L'); break; + case KEY_UP : ch = MOD_MOVE('K'); break; + case KEY_DOWN : ch = MOD_MOVE('J'); break; + case KEY_HOME : ch = MOD_MOVE('Y'); break; + case KEY_PPAGE: ch = MOD_MOVE('U'); break; + case KEY_NPAGE: ch = MOD_MOVE('N'); break; + case KEY_END : ch = MOD_MOVE('B'); break; + + default: break; + } + + break; + } + + if (mode == M_KEYPAD) + { + switch(ch) + { + /* ESC F - Interix Console codes */ + case '^': ch = MOD_MOVE('H'); break; /* Shift-Left */ + case '$': ch = MOD_MOVE('L'); break; /* Shift-Right */ + + /* ESC [ - Interix Console codes */ + case 'H': ch = 'y'; break; /* Home */ + case 1: ch = MOD_MOVE('K'); break; /* Ctl-Keypad Up */ + case 2: ch = MOD_MOVE('J'); break; /* Ctl-Keypad Down */ + case 3: ch = MOD_MOVE('L'); break; /* Ctl-Keypad Right */ + case 4: ch = MOD_MOVE('H'); break; /* Ctl-Keypad Left */ + case 263: ch = MOD_MOVE('Y'); break; /* Ctl-Keypad Home */ + case 19: ch = MOD_MOVE('U'); break; /* Ctl-Keypad PgUp */ + case 20: ch = MOD_MOVE('N'); break; /* Ctl-Keypad PgDn */ + case 21: ch = MOD_MOVE('B'); break; /* Ctl-Keypad End */ + + /* ESC [ - Cygwin Console codes */ + case 'G': ch = '.'; break; /* Keypad 5 */ + case '7': lastch = 'Y'; mode=M_TRAIL; break; /* Ctl-Home */ + case '5': lastch = 'U'; mode=M_TRAIL; break; /* Ctl-PgUp */ + case '6': lastch = 'N'; mode=M_TRAIL; break; /* Ctl-PgDn */ + + /* ESC [ - Win32 Telnet, PuTTY */ + case '1': lastch = 'y'; mode=M_TRAIL; break; /* Home */ + case '4': lastch = 'b'; mode=M_TRAIL; break; /* End */ + + /* ESC O - PuTTY */ + case 'D': ch = MOD_MOVE('H'); break; + case 'C': ch = MOD_MOVE('L'); break; + case 'A': ch = MOD_MOVE('K'); break; + case 'B': ch = MOD_MOVE('J'); break; + case 't': ch = 'h'; break; + case 'v': ch = 'l'; break; + case 'x': ch = 'k'; break; + case 'r': ch = 'j'; break; + case 'w': ch = 'y'; break; + case 'y': ch = 'u'; break; + case 's': ch = 'n'; break; + case 'q': ch = 'b'; break; + case 'u': ch = '.'; break; + } + + if (mode != M_KEYPAD) + continue; + } + + if (ch == 27) + { + halfdelay(1); + mode = M_ESC; + continue; + } + + switch(ch) + { + case KEY_LEFT : ch = 'h'; break; + case KEY_DOWN : ch = 'j'; break; + case KEY_UP : ch = 'k'; break; + case KEY_RIGHT : ch = 'l'; break; + case KEY_HOME : ch = 'y'; break; + case KEY_PPAGE : ch = 'u'; break; + case KEY_END : ch = 'b'; break; +#ifdef KEY_LL + case KEY_LL : ch = 'b'; break; +#endif + case KEY_NPAGE : ch = 'n'; break; + +#ifdef KEY_B1 + case KEY_B1 : ch = 'h'; break; + case KEY_C2 : ch = 'j'; break; + case KEY_A2 : ch = 'k'; break; + case KEY_B3 : ch = 'l'; break; +#endif + case KEY_A1 : ch = 'y'; break; + case KEY_A3 : ch = 'u'; break; + case KEY_C1 : ch = 'b'; break; + case KEY_C3 : ch = 'n'; break; + /* next should be '.', but for problem with putty/linux */ + case KEY_B2 : ch = 'u'; break; + +#ifdef KEY_SLEFT + case KEY_SRIGHT : ch = MOD_MOVE('L'); break; + case KEY_SLEFT : ch = MOD_MOVE('H'); break; +#ifdef KEY_SUP + case KEY_SUP : ch = MOD_MOVE('K'); break; + case KEY_SDOWN : ch = MOD_MOVE('J'); break; +#endif + case KEY_SHOME : ch = MOD_MOVE('Y'); break; + case KEY_SPREVIOUS:ch = MOD_MOVE('U'); break; + case KEY_SEND : ch = MOD_MOVE('B'); break; + case KEY_SNEXT : ch = MOD_MOVE('N'); break; +#endif + case 0x146 : ch = MOD_MOVE('K'); break; /* Shift-Up */ + case 0x145 : ch = MOD_MOVE('J'); break; /* Shift-Down */ + +#ifdef CTL_RIGHT + case CTL_RIGHT : ch = MOD_MOVE('L'); break; + case CTL_LEFT : ch = MOD_MOVE('H'); break; + case CTL_UP : ch = MOD_MOVE('K'); break; + case CTL_DOWN : ch = MOD_MOVE('J'); break; + case CTL_HOME : ch = MOD_MOVE('Y'); break; + case CTL_PGUP : ch = MOD_MOVE('U'); break; + case CTL_END : ch = MOD_MOVE('B'); break; + case CTL_PGDN : ch = MOD_MOVE('N'); break; +#endif +#ifdef KEY_EOL + case KEY_EOL : ch = MOD_MOVE('B'); break; +#endif + +#ifndef CTL_PAD1 + /* MSYS rxvt console */ + case 511 : ch = MOD_MOVE('J'); break; /* Shift Dn */ + case 512 : ch = MOD_MOVE('J'); break; /* Ctl Down */ + case 514 : ch = MOD_MOVE('H'); break; /* Ctl Left */ + case 516 : ch = MOD_MOVE('L'); break; /* Ctl Right*/ + case 518 : ch = MOD_MOVE('K'); break; /* Shift Up */ + case 519 : ch = MOD_MOVE('K'); break; /* Ctl Up */ +#endif + +#ifdef CTL_PAD1 + case CTL_PAD1 : ch = MOD_MOVE('B'); break; + case CTL_PAD2 : ch = MOD_MOVE('J'); break; + case CTL_PAD3 : ch = MOD_MOVE('N'); break; + case CTL_PAD4 : ch = MOD_MOVE('H'); break; + case CTL_PAD5 : ch = '.'; break; + case CTL_PAD6 : ch = MOD_MOVE('L'); break; + case CTL_PAD7 : ch = MOD_MOVE('Y'); break; + case CTL_PAD8 : ch = MOD_MOVE('K'); break; + case CTL_PAD9 : ch = MOD_MOVE('U'); break; +#endif + +#ifdef ALT_RIGHT + case ALT_RIGHT : ch = MOD_MOVE('L'); break; + case ALT_LEFT : ch = MOD_MOVE('H'); break; + case ALT_DOWN : ch = MOD_MOVE('J'); break; + case ALT_HOME : ch = MOD_MOVE('Y'); break; + case ALT_PGUP : ch = MOD_MOVE('U'); break; + case ALT_END : ch = MOD_MOVE('B'); break; + case ALT_PGDN : ch = MOD_MOVE('N'); break; +#endif + +#ifdef ALT_PAD1 + case ALT_PAD1 : ch = MOD_MOVE('B'); break; + case ALT_PAD2 : ch = MOD_MOVE('J'); break; + case ALT_PAD3 : ch = MOD_MOVE('N'); break; + case ALT_PAD4 : ch = MOD_MOVE('H'); break; + case ALT_PAD5 : ch = '.'; break; + case ALT_PAD6 : ch = MOD_MOVE('L'); break; + case ALT_PAD7 : ch = MOD_MOVE('Y'); break; + case ALT_PAD8 : ch = MOD_MOVE('K'); break; + case ALT_PAD9 : ch = MOD_MOVE('U'); break; +#endif + } + + break; + } + + nocbreak(); /* disable halfdelay mode if on */ + raw(); + + return(ch & 0x7F); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/memory.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/memory.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,420 @@ +/* + memory.c + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include + +#include "dict.h" +#include "memory.h" +#include "rogue.h" + +static char sccsid[] = "%W%\t%G%"; + +/* Debugging memory allocation code that tries to trap common memory problems + like overwriting storage and stepping on memory pointer chains. If code + doesn't use malloc, free, and realloc a lot, these routines can be left in + as added protection against undetected storage bugs. +*/ + +/* FENCE_SIZE should be a multiple of sizeof(size_t) to prevent alignment problems. + The code assumes that malloc and realloc return pointers aligned at least on size_t + sized boundaries and that a pointer needs alignment no more strict than that of an + object needed to hold a size_t. +*/ + +#define FENCE_SIZE (sizeof(size_t) * 1024) + +static int memdebug_level = 0; +static DICTIONARY *allocations = NULL; +static FILE *trace_file = NULL; + +/* set the debug level */ +void mem_debug(const int level) +{ + memdebug_level = level; + + if (trace_file == NULL) + trace_file = fopen("trace", "w"); + + /* all except 0, 1, and unknown fall through */ + switch(memdebug_level) { + case 2: + fprintf(trace_file, "+++ Memory tracking possible, "); + case 1: + fprintf(trace_file, "+++ Memory debugging enabled, "); + break; + case 0: + fprintf(trace_file, "+++ Memory debugging disabled, "); + break; + default: + fprintf(trace_file, "!!! Unknown memory debug level set, enabling level 1, "); + memdebug_level = 1; + break; + } + fprintf(trace_file, "fence size = %d\n", FENCE_SIZE); +} + +/* set memory tracking on or off */ +/* turning it off deletes all tracking data */ +void mem_tracking(int flag) +{ + /* do nothing if debuglevel is too low */ + if (memdebug_level < 2) + return; + + /* turn on tracking */ + if (flag > 0) { + if (allocations != NULL) { + dict_destroy(allocations); + allocations = NULL; + } + allocations = dict_create(8, 100, 4, 20); + if (allocations == NULL) { + fprintf(trace_file, "!!! Unable to allocate tracking table!\n"); + abort(); + } + } + /* turn off tracking */ + else if (allocations != NULL) { + dict_destroy(allocations); + allocations = NULL; + } +} + +/* go through all pointers and see if they are OK, aborting if not */ +/* always returns 1 if not aborting so that it can be included in */ +/* if statement boolean expressions */ +int mem_check(char *fname, int linenum) +{ + STRING_ENTRY *se; + + /* scan of a NULL dictionary always succeeds */ + if (allocations == NULL) + return TRUE; + + if (!dict_scan_begin(allocations)) { + fprintf(trace_file, "!!! Dictionary scan initialization failed!\n"); + abort(); + } + + fprintf(trace_file, "\n+++ --- Starting pointer scan\n"); + fprintf(trace_file, "+++ --- At %s, %d\n", fname, linenum); + + /* mem_validate aborts if there is a problem */ + while((se = dict_scan_next(allocations)) != NULL) + mem_validate(se->any_ptr); + + fprintf(trace_file, "+++ --- Done pointer scan\n\n"); + + /* always return a good value if execution arrives here */ + return 1; +} + +/* allocate some memory and initialize header and trailer */ +void *mem_malloc(const size_t bytes) +{ + char *mem_temp; + size_t real_size = bytes + (FENCE_SIZE << 1); + + /* allocate including guard bytes to detect some ways of overwriting of memory areas */ + mem_temp = (void *)malloc(real_size); + if (memdebug_level > 0) { + fprintf(trace_file, "+++ Requested size of %ld bytes\n", bytes); + fprintf(trace_file, "+++ Actual malloc of %ld bytes located at %p\n", real_size, mem_temp); + } + + /* if allocation succeeded, set management data */ + if (mem_temp != NULL) { + size_t i; + char *end; + + /* do beginning marker bytes */ + for (i = 0; i < FENCE_SIZE - sizeof(size_t); i++) + *mem_temp++ = 145; + + /* save size in header too */ + if (memdebug_level > 0) + fprintf(trace_file, "*** Requested memory size stored at %p\n", mem_temp); + *(size_t *)mem_temp = bytes; + + /* finally, point to storage we are going to hand out */ + mem_temp += sizeof(size_t); + + /* now, point to trailer bytes and do them */ + end = mem_temp + bytes; + for (i = 0; i < FENCE_SIZE; i++) + *end++ = 145; + + /* now zap contents to zero */ + for (i = 0; i < bytes; i++) + mem_temp[i] = 0; + } + + /* track pointer if needed */ + if (memdebug_level > 1 && allocations != NULL) { + char key[16]; + long temp; + + sprintf(key, "%p", mem_temp); + if (dict_insert(allocations, key, 1, (const unsigned long) bytes, mem_temp, &temp) == NULL) { + fprintf(trace_file, "!!! Insert of pointer tracking info failed\n"); + abort(); + } + } + + /* allow caller to do error handling */ + if (memdebug_level > 0) { + fprintf(trace_file, "--- Returning pointer of %p\n", mem_temp); + fflush(trace_file); + } + return (void *)mem_temp; +} + +/* release some memory, making sure that it was properly allocated */ +void mem_free(const void *ptr) +{ + char *mem_temp; + size_t mem_size; + size_t i; + + if (memdebug_level > 0) + fprintf(trace_file, "+++ Free of memory located at %p\n", ptr); + if (ptr == NULL) { + if (memdebug_level > 0) { + fprintf(trace_file, "!!! Freeing NULL pointer\n"); + fflush(trace_file); + } + abort(); + } + + mem_validate(ptr); /* doesn't return on error */ + + /* get location of size of area */ + mem_temp = (char *)ptr - sizeof(size_t); + + /* get and calculate real size */ + mem_size = *(size_t *)mem_temp + (FENCE_SIZE << 1); + + /* if doing memory tracking */ + if (memdebug_level > 1 && allocations != NULL) { + char key[16]; + STRING_ENTRY *se; + long temp; + + sprintf(key, "%p", ptr); + + if ((se = dict_search(allocations, key, &temp)) == NULL) { + fprintf(trace_file, "!!! Deleting pointer not found in tracking info\n"); + abort(); + } + + if (se->count == 0) { + fprintf(trace_file, "!!! Freeing a pointer that has already been freed!\n"); + abort(); + } + else if (se->flags != mem_size - (FENCE_SIZE << 1)) { + fprintf(trace_file, "!!! Stored size different from tracking size!\n"); + abort(); + } + + /* remember deleted stuff by zeroing the allocation count */ + se->count = 0; + se->flags = 0; + } + + /* zap bytes being freed */ + for (i = 0, mem_temp = (char *)ptr - FENCE_SIZE; i < mem_size; i++, mem_temp++) + *mem_temp = 243; + + if (memdebug_level > 0) + fflush(trace_file); + + mem_temp = (char *)ptr - FENCE_SIZE; + free((void *)mem_temp); +} + +/* reallocate some memory, making sure that it was properly allocated */ +void *mem_realloc(const void *ptr, const size_t new_size) +{ + char *mem_temp = (char *)ptr; + size_t real_size = new_size + (FENCE_SIZE << 1); + size_t mem_size; + long i; + + if (memdebug_level > 0) { + fprintf(trace_file, "+++ Requested size of %ld bytes\n", new_size); + fprintf(trace_file, "+++ Actual realloc of %ld bytes located at %p\n", real_size, mem_temp); + } + if (ptr == NULL) { + if (memdebug_level > 0) { + fprintf(trace_file, "!!! Reallocating NULL pointer\n"); + fflush(trace_file); + } + abort(); + } + + mem_validate(ptr); /* doesn't return on error */ + + /* if doing memory tracking */ + if (memdebug_level > 1 && allocations != NULL) { + char key[16]; + STRING_ENTRY *se; + long temp; + + sprintf(key, "%p", ptr); + + if ((se = dict_search(allocations, key, &temp)) == NULL) { + fprintf(trace_file, "!!! Deleting a pointer not found in tracking info!\n"); + abort(); + } + + /* point to size bytes */ + mem_temp = (char *)ptr - sizeof(size_t); + + /* get user size */ + mem_size = *(size_t *)mem_temp; + + if (se->count == 0) { + fprintf(trace_file, "!!! Freeing a pointer that has already been freed!\n"); + abort(); + } + else if (se->flags != mem_size) { + fprintf(trace_file, "!!! Stored size different from tracking size!\n"); + abort(); + } + + /* remember deleted stuff by zeroing the allocation count */ + se->count = 0; + se->flags = 0; + } + + + /* header marker bytes will be copied by the realloc */ + mem_temp = (char *)ptr - FENCE_SIZE; + mem_temp = realloc((void *)mem_temp, real_size); + + if (mem_temp != NULL) { + char *end; + + /* save size in header too */ + mem_temp += FENCE_SIZE - sizeof(size_t); + if (memdebug_level > 0) + fprintf(trace_file, "*** Requested memory size stored at %p\n", mem_temp); + *(size_t *)mem_temp = new_size; + + /* finally, point to storage we are going to hand out */ + mem_temp += sizeof(size_t); + + /* now, point to trailer bytes and do them */ + end = mem_temp + new_size; + for (i = 0; i < FENCE_SIZE; i++) + *end++ = 145; + } + + if (memdebug_level > 1 && allocations != NULL) { + char key[16]; + long temp; + + sprintf(key, "%p", mem_temp); + if (dict_insert(allocations, key, 1, (const unsigned long)new_size, mem_temp, &temp) == NULL) { + fprintf(trace_file, "!!! Insert of pointer tracking info failed\n"); + abort(); + } + } + + if (memdebug_level > 0) { + fprintf(trace_file, "--- Returning pointer of %p\n", mem_temp); + fflush(trace_file); + } + return (void *)mem_temp; +} + +/* check a pointer to be sure all check bytes are OK. abort if not */ +/* always returns 1 if not aborting so that it can be included in */ +/* if statement boolean expressions */ +int mem_validate(void *ptr) +{ + unsigned char *mem_temp = (unsigned char *)ptr; + size_t mem_size; + size_t i; + + /* NULL pointers are always valid */ + if (ptr == NULL) + return 1; + + if (memdebug_level > 0) + fprintf(trace_file, "+++ Checking %p as pointer\n", ptr); + + + if (memdebug_level > 1 && allocations != NULL) { + char key[16]; + STRING_ENTRY *se; + long temp; + + sprintf(key, "%p", ptr); + + if ((se = dict_search(allocations, key, &temp)) == NULL) { + fprintf(trace_file, "!!! Pointer not found in tracking info!\n"); + abort(); + } + + /* point to size bytes */ + mem_temp = (unsigned char *)ptr - sizeof(size_t); + + /* get user size */ + mem_size = *(size_t *)mem_temp; + + if (se->count == 0) { + fprintf(trace_file, "!!! Checking pointer has been freed!\n"); + abort(); + } + else if (se->flags != mem_size) { + fprintf(trace_file, "!!! Stored size different from tracking size!\n"); + abort(); + } + } + + /* check the header bytes */ + mem_temp = (unsigned char *) ptr - FENCE_SIZE; + if (memdebug_level > 0) + fprintf(trace_file, "+++ Real pointer at %p\n", mem_temp); + + + for (i = 0; i < FENCE_SIZE - sizeof(size_t); i++) + if (*mem_temp++ != 145) { + if (memdebug_level > 0) { + fprintf(trace_file, "!!! The user pointer at %p has been overwritten\n", ptr); + fprintf(trace_file, "!!! Header offset %ld has been changed\n", i - 1); + fflush(trace_file); + } + abort(); + } + + /* check size */ + i = *(size_t *)mem_temp; + if (memdebug_level > 0) + fprintf(trace_file, "*** Stored memory size of %ld bytes in header\n", i); + + + /* now point to where trailer should be */ + mem_temp = (unsigned char *)ptr + i; + for (i = 0; i < FENCE_SIZE; i++) + if (*mem_temp++ != 145) { + if (memdebug_level > 0) { + fprintf(trace_file, "!!! The user pointer at %p has been overwritten\n", ptr); + fprintf(trace_file, "!!! Trailer offset %ld has been changed\n", i - 1); + fflush(trace_file); + } + abort(); + } + if (memdebug_level > 0) + fflush(trace_file); + return 1; +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/misc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/misc.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1208 @@ +/* + misc.c - all sorts of miscellaneous routines + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +/* + tr_name() + print the name of a trap +*/ + +char * +tr_name(char ch, char *trname) +{ + const char *s = NULL; + + if (trname == NULL) + return(" Your found an error in UltraRogue #100."); + + switch(ch) + { + case TRAPDOOR: + s = "trapdoor."; + break; + case BEARTRAP: + s = "beartrap."; + break; + case SLEEPTRAP: + s = "sleeping gas trap."; + break; + case ARROWTRAP: + s = "arrow trap."; + break; + case TELTRAP: + s = "teleport trap."; + break; + case DARTTRAP: + s = "dart trap."; + break; + case POOL: + s = "shimmering pool."; + break; + case MAZETRAP: + s = "maze entrance."; + break; + case FIRETRAP: + s = "fire trap."; + break; + case POISONTRAP: + s = "poison pool trap."; + break; + case LAIR: + s = "monster lair."; + break; + case RUSTTRAP: + s = "rust trap."; + break; + default: + ; + } + + sprintf(trname, "You found a %s.", s); + + return(trname); +} + +/* + look() + A quick glance all around the player +*/ + +void +look(int wakeup) +{ + int x, y; + char ch, och; + int oldx, oldy; + int inpass, horizontal, vertical, do_light = FALSE, do_blank = FALSE; + int passcount = 0; + struct room *rp; + int ey, ex; + + /* Are we moving vertically or horizontally? */ + + if (runch == 'h' || runch == 'l') + horizontal = TRUE; + else + horizontal = FALSE; + + if (runch == 'j' || runch == 'k') + vertical = TRUE; + else + vertical = FALSE; + + getyx(cw, oldy, oldx); /* Save current position */ + + /* + * Blank out the floor around our last position and check for moving + * out of a corridor in a maze. + */ + + if (oldrp != NULL && (oldrp->r_flags & ISDARK) && + !(oldrp->r_flags & HASFIRE) && off(player, ISBLIND)) + do_blank = TRUE; + + for (x = player.t_oldpos.x - 1; x <= player.t_oldpos.x + 1; x++) + for (y = player.t_oldpos.y - 1; y <= player.t_oldpos.y + 1; + y++) + { + ch = show(y, x); + + if (do_blank && (y != hero.y || x != hero.x) && ch == FLOOR) + mvwaddch(cw, y, x, ' '); + + /* Moving out of a corridor? */ + + if (levtype == MAZELEV && + (ch != '|' && ch != '-') && /* Not a wall */ + ((vertical && x != player.t_oldpos.x && + y == player.t_oldpos.y) || + (horizontal && y != player.t_oldpos.y && + x == player.t_oldpos.x))) + do_light = TRUE; /* Just came to a turn */ + } + + inpass = ((rp = roomin(hero)) == NULL); /* Are we in a passage? */ + + /* Are we coming out of a wall into a corridor in a maze? */ + och = show(player.t_oldpos.y, player.t_oldpos.x); + ch = show(hero.y, hero.x); + + if (levtype == MAZELEV && (och == '|' || och == '-' || + och == SECRETDOOR) && (ch != '|' && ch != '-' && ch != SECRETDOOR)) + { + do_light = off(player, ISBLIND); /* Light it up if not blind */ + } + + /* Look around the player */ + + ey = hero.y + 1; + ex = hero.x + 1; + + for (x = hero.x - 1; x <= ex; x++) + if (x >= 0 && x < COLS) + for (y = hero.y - 1; y <= ey; y++) + { + if (y <= 0 || y >= LINES - 2) + continue; + + if (isalpha(mvwinch(mw, y, x))) + { + struct linked_list *it; + struct thing *tp; + + if (wakeup) + it = wake_monster(y, x); + else + it = find_mons(y, x); + + if (it == NULL) + continue; + + tp = THINGPTR(it); + tp->t_oldch = CCHAR( mvinch(y, x) ); + + if (isatrap(tp->t_oldch)) + { + struct trap *trp = trap_at(y, x); + + tp->t_oldch = (trp->tr_flags & ISFOUND) ? tp->t_oldch + : trp->tr_show; + } + + if (tp->t_oldch == FLOOR && + (rp->r_flags & ISDARK) + && !(rp->r_flags & HASFIRE) && + off(player, ISBLIND)) + tp->t_oldch = ' '; + } + + /* Secret doors show as walls */ + + if ((ch = show(y, x)) == SECRETDOOR) + ch = secretdoor(y, x); + + /* + * Don't show room walls if he is in a + * passage and check for maze turns + */ + + if (off(player, ISBLIND)) + { + if (y == hero.y && x == hero.x || (inpass && (ch == '-' || + ch == '|'))) + continue; + + /* Are we at a crossroads in a maze? */ + + if (levtype == MAZELEV && (ch != '|' && ch != '-') && + /* Not a wall */ + ((vertical && x != hero.x && y == hero.y) || + (horizontal && y != hero.y && x == hero.x))) + do_light = TRUE; + /* Just came to a turn */ + } + else if (y != hero.y || x != hero.x) + continue; + + wmove(cw, y, x); + waddch(cw, ch); + + if (door_stop && !firstmove && running) + { + switch (runch) + { + case 'h': + if (x == ex) + continue; + break; + case 'j': + if (y == hero.y - 1) + continue; + break; + case 'k': + if (y == ey) + continue; + break; + case 'l': + if (x == hero.x - 1) + continue; + break; + case 'y': + if ((x + y) - (hero.x + hero.y) >= 1) + continue; + break; + case 'u': + if ((y - x) - (hero.y - hero.x) >= 1) + continue; + break; + case 'n': + if ((x + y) - (hero.x + hero.y) <= -1) + continue; + break; + case 'b': + if ((y - x) - (hero.y - hero.x) <= -1) + continue; + break; + } + + switch (ch) + { + case DOOR: + if (x == hero.x || y == hero.y) + running = FALSE; + break; + case PASSAGE: + if (x == hero.x || y == hero.y) + passcount++; + break; + case FLOOR: + + /* + * Stop by new passages in a + * maze (floor next to us) + */ + if ((levtype == MAZELEV) && + ((horizontal && x == hero.x && y != hero.y) || + (vertical && y == hero.y && x != hero.x))) + running = FALSE; + + case '|': + case '-': + case ' ': + break; + + default: + running = FALSE; + break; + } + } + } + + if (door_stop && !firstmove && passcount > 1) + running = FALSE; + + /* + * Do we have to light up the area (just stepped into a new + * corridor)? + */ + + if (do_light && wakeup && /* wakeup will be true on a normal move */ + !(rp->r_flags & ISDARK) && /* We have some light */ + !ce(hero, player.t_oldpos)) /* Don't do anything if we didn't move */ + light(&hero); + + mvwaddch(cw, hero.y, hero.x, PLAYER); + wmove(cw, oldy, oldx); + + if (wakeup) + { + player.t_oldpos = hero; /* Don't change if we didn't move */ + oldrp = rp; + } +} + +/* + secret_door() + Figure out what a secret door looks like. +*/ + +char +secretdoor(int y, int x) +{ + struct room *rp; + coord cp; + + cp.x = x; + cp.y = y; + + if ((rp = roomin(cp)) != NULL) + { + if (y == rp->r_pos.y || y == rp->r_pos.y + rp->r_max.y - 1) + return ('-'); + else + return ('|'); + } + return ('p'); +} + +/* + find_obj() + find the unclaimed object at y, x +*/ + +struct linked_list * +find_obj(int y, int x) +{ + struct linked_list *obj, *sobj; + struct object *op; + + sobj = lvl_obj; + + for (obj = sobj; obj != NULL; obj = next(obj)) + { + op = OBJPTR(obj); + + if (op && op->o_pos.y == y && op->o_pos.x == x) + return(obj); + } + + return(NULL); +} + +/* + eat() + He wants to eat something, so let him try +*/ + +void +eat(void) +{ + struct object *obj; + int amount; + float scale = (float) (LINES * COLS) / (25.0F * 80.0F); + + if ((obj = get_object(pack, "eat", FOOD, NULL)) == NULL) + return; + + switch (obj->o_which) + { + case FD_RATION: + amount = (int)(scale * (HUNGERTIME + rnd(400) - 200)); + + if (rnd(100) > 70) + { + msg("Yuk, this food tastes awful."); + pstats.s_exp++; + check_level(); + } + else + msg("Yum, that tasted good."); + break; + + case FD_FRUIT: + amount = (int)(scale * (200 + rnd(HUNGERTIME))); + msg("My, that was a yummy %s.", fruit); + break; + + case FD_CRAM: + amount = (int)(scale * (rnd(HUNGERTIME / 2) + 600)); + msg("The cram tastes dry in your mouth."); + break; + + case FD_CAKES: + amount = (int)(scale * ((HUNGERTIME / 3) + rnd(600))); + msg("Yum, the honey cakes tasted good."); + break; + + case FD_LEMBA: + amount = (int)(scale * ((HUNGERTIME / 2) + rnd(900))); + quaff(&player, P_HEALING, ISNORMAL); + break; + + case FD_MIRUVOR: + amount = (int)(scale * ((HUNGERTIME / 3) + rnd(500))); + quaff(&player, P_HEALING, ISNORMAL); + quaff(&player, P_RESTORE, ISNORMAL); + break; + + default: + msg("What a strange thing to eat!"); + amount = (int)(scale * HUNGERTIME); + } + + food_left += amount; + + if (obj->o_flags & ISBLESSED) + { + food_left += 2 * amount; + msg("You have a tingling feeling in your mouth."); + } + else if (food_left > scale * STOMACHSIZE) + { + food_left = (int)(scale * STOMACHSIZE); + msg("You feel satiated and too full to move."); + no_command = HOLDTIME; + } + + hungry_state = F_OK; + updpack(); + + if (obj == cur_weapon) + cur_weapon = NULL; + + if (--obj->o_count <= 0) /* Remove this pack entry if last of food */ + discard_pack(obj); +} + +/* + * Used to modify the player's strength it keeps track of the highest it has + * been, just in case + */ + +void +chg_str(int amt, int both, int lost) +{ + int ring_str; /* ring strengths */ + struct stats *ptr; /* for speed */ + + ptr = &pstats; + + ring_str = ring_value(R_ADDSTR) + (on(player, POWERSTR) ? 10 : 0) + + (on(player, SUPERHERO) ? 10 : 0); + + ptr->s_str -= ring_str; + ptr->s_str += amt; + + if (ptr->s_str < 3) + { + ptr->s_str = 3; + lost = FALSE; + } + else if (ptr->s_str > 25) + ptr->s_str = 25; + + if (both) + max_stats.s_str = ptr->s_str; + + if (lost) + lost_str -= amt; + + ptr->s_str += ring_str; + + if (ptr->s_str < 0) + ptr->s_str = 0; + + updpack(); +} + +/* + * Used to modify the player's dexterity it keeps track of the highest it has + * been, just in case + */ + +void +chg_dext(int amt, int both, int lost) +{ + int ring_dext; /* ring strengths */ + struct stats *ptr; /* for speed */ + + ptr = &pstats; + + ring_dext = ring_value(R_ADDHIT) + (on(player, POWERDEXT) ? 10 : 0) + + (on(player, SUPERHERO) ? 5 : 0); + + ptr->s_dext -= ring_dext; + ptr->s_dext += amt; + + if (ptr->s_dext < 3) + { + ptr->s_dext = 3; + lost = FALSE; + } + else if (ptr->s_dext > 25) + ptr->s_dext = 25; + + if (both) + max_stats.s_dext = ptr->s_dext; + + if (lost) + lost_dext -= amt; + + ptr->s_dext += ring_dext; + + if (ptr->s_dext < 0) + ptr->s_dext = 0; +} + +/* + add_haste() + add a haste to the player +*/ + +void +add_haste(int blessed) +{ + short hasttime; + + if (blessed) + hasttime = 10; + else + hasttime = 6; + + if (on(player, ISSLOW)) /* Is person slow? */ + { + extinguish_fuse(FUSE_NOSLOW); + noslow(NULL); + + if (blessed) + hasttime = 4; + else + return; + } + + if (on(player, ISHASTE)) + { + msg("You faint from exhaustion."); + no_command += rnd(hasttime); + lengthen_fuse(FUSE_NOHASTE, rnd(hasttime) + (roll(1, 4) * hasttime)); + } + else + { + turn_on(player, ISHASTE); + light_fuse(FUSE_NOHASTE, 0, roll(hasttime, hasttime), AFTER); + } +} + +/* + aggravate() + aggravate all the monsters on this level +*/ + +void +aggravate(void) +{ + struct linked_list *mi; + struct thing *tp; + + for (mi = mlist; mi != NULL; mi = next(mi)) + { + tp = THINGPTR(mi); + chase_it(&tp->t_pos, &player); + } +} + +/* + vowelstr() + for printfs: if string starts with a vowel, return "n" for an "an" +*/ + +char * +vowelstr(char *str) +{ + switch (*str) + { + case 'a': + case 'A': + case 'e': + case 'E': + case 'i': + case 'I': + case 'o': + case 'O': + case 'u': + case 'U': + return "n"; + default: + return ""; + } +} + +/* + is_object() + see if the object is one of the currently used items +*/ + +int +is_current(struct object *obj) +{ + if (obj == NULL) + return FALSE; + + if (obj == cur_armor || obj == cur_weapon || + obj == cur_ring[LEFT_1] || obj == cur_ring[LEFT_2] || + obj == cur_ring[LEFT_3] || obj == cur_ring[LEFT_4] || + obj == cur_ring[LEFT_5] || + obj == cur_ring[RIGHT_1] || obj == cur_ring[RIGHT_2] || + obj == cur_ring[RIGHT_3] || obj == cur_ring[RIGHT_4] || + obj == cur_ring[RIGHT_5]) { + msg("That's already in use."); + return TRUE; + } + + return FALSE; +} + +/* + get_dir() + set up the direction co_ordinate for use in varios "prefix" commands +*/ + +int +get_dir(void) +{ + char *prompt; + int gotit; + + prompt = "Which direction? "; + msg(prompt); + + do + { + gotit = TRUE; + + switch (readchar()) + { + case 'h': + case 'H': + delta.y = 0; + delta.x = -1; + break; + + case 'j': + case 'J': + delta.y = 1; + delta.x = 0; + break; + + case 'k': + case 'K': + delta.y = -1; + delta.x = 0; + break; + + case 'l': + case 'L': + delta.y = 0; + delta.x = 1; + break; + + case 'y': + case 'Y': + delta.y = -1; + delta.x = -1; + break; + + case 'u': + case 'U': + delta.y = -1; + delta.x = 1; + break; + + case 'b': + case 'B': + delta.y = 1; + delta.x = -1; + break; + + case 'n': + case 'N': + delta.y = 1; + delta.x = 1; + break; + + case ESCAPE: + return FALSE; + + default: + mpos = 0; + msg(prompt); + gotit = FALSE; + } + } + while(!gotit); + + if (on(player, ISHUH) && rnd(100) > 80) + do + { + delta.y = rnd(3) - 1; + delta.x = rnd(3) - 1; + } + while (delta.y == 0 && delta.x == 0); + + mpos = 0; + + return(TRUE); +} + +/* + is_wearing() + is the hero wearing a particular ring +*/ + +int +is_wearing(int type) +{ +#define ISRING(h, r) (cur_ring[h] != NULL && cur_ring[h]->o_which == r) + + return( + ISRING(LEFT_1, type) || ISRING(LEFT_2, type) || + ISRING(LEFT_3, type) || ISRING(LEFT_4, type) || + ISRING(LEFT_5, type) || + ISRING(RIGHT_1, type) || ISRING(RIGHT_2, type) || + ISRING(RIGHT_3, type) || ISRING(RIGHT_4, type) || + ISRING(RIGHT_5, type) ); +} + +/* + maze_view() + Returns true if the player can see the specified location + within the confines of a maze (within one column or row) +*/ + +int +maze_view(int y, int x) +{ + int start, goal, delt, ycheck = 0, xcheck = 0, absy, absx; + int row; + + /* Get the absolute value of y and x differences */ + + absy = hero.y - y; + absx = hero.x - x; + + if (absy < 0) + absy = -absy; + + if (absx < 0) + absx = -absx; + + /* Must be within one row or column */ + + if (absy > 1 && absx > 1) + return(FALSE); + + if (absy <= 1) /* Go along row */ + { + start = hero.x; + goal = x; + row = TRUE; + ycheck = hero.y; + } + else /* Go along column */ + { + start = hero.y; + goal = y; + row = FALSE; + xcheck = hero.x; + } + + if (start <= goal) + delt = 1; + else + delt = -1; + + while (start != goal) + { + if (row) + xcheck = start; + else + ycheck = start; + + switch(CCHAR(winat(ycheck, xcheck))) + { + case '|': + case '-': + case WALL: + case DOOR: + case SECRETDOOR: + return(FALSE); + + default: + break; + } + start += delt; + } + + return(TRUE); +} + +/* + listen() + listen for monsters less than 5 units away +*/ + +void +listen(void) +{ + struct linked_list *item; + struct thing *tp; + int thief_bonus = -50; + int mcount = 0; + + if (player.t_ctype == C_THIEF) + thief_bonus = 10; + + for (item = mlist; item != NULL; item = next(item)) + { + tp = THINGPTR(item); + + if (DISTANCE(hero, tp->t_pos) < 81 + && rnd(70) < (thief_bonus + 4 * pstats.s_dext + + 6 * pstats.s_lvl)) + { + msg("You hear a%s %s nearby.", + vowelstr(monsters[tp->t_index].m_name), + monsters[tp->t_index].m_name); + mcount++; + } + } + + if (mcount == 0) + msg("You hear nothing."); +} + +/* + * nothing_message - print out "Nothing happens." + */ + +static const char *nothings[] = +{ + "", + "unusual ", + "seems to ", + "at all ", + "really ", + "noticeable ", + "different ", + "strange ", + "wierd ", + "bizzare ", + "wonky ", + "" +}; + +void +nothing_message(int flags) +{ + int adverb = rnd(sizeof(nothings) / sizeof(char *)); + + NOOP(flags); + + msg("Nothing %shappens.", nothings[adverb]); +} + +/* + feel_message() + print out "You feel ." +*/ + +void +feel_message(void) +{ + char *charp; + + switch (rnd(25)) + { + case 1: charp = "bad"; break; + case 2: charp = "hurt"; break; + case 3: charp = "sick"; break; + case 4: charp = "faint"; break; + case 5: charp = "yucky"; break; + case 6: charp = "wonky"; break; + case 7: charp = "wierd"; break; + case 8: charp = "queasy"; break; + case 9: charp = "wounded"; break; + case 11: charp = "unusual"; break; + case 12: charp = "no pain"; break; + case 13: charp = "strange"; break; + case 14: charp = "noticable"; break; + case 15: charp = "bizzare"; break; + case 16: charp = "distressed";break; + case 17: charp = "different"; break; + case 18: charp = "a touch of ague"; break; + case 19: charp = "a migrane coming on"; break; + case 20: charp = "Excedrin headache #666"; break; + case 21: charp = "a disturbance in the force"; break; + case 22: charp = "like someone dropped a house on you"; break; + case 23: charp = "as if every nerve in your body is on fire"; break; + case 24: charp = "like thousands of red-hot army ants are crawling under your skin"; + break; + + default: + charp = "ill"; + break; + } + msg("You feel %s.", charp); +} + +/* + const_bonus() + Hit point adjustment for changing levels +*/ + +int +const_bonus(void) +{ + int ret_val = -2; + + if (pstats.s_const > 12) + ret_val = pstats.s_const - 12; + else if (pstats.s_const > 6) + ret_val = 0; + else if (pstats.s_const > 3) + ret_val = -1; + + return(ret_val); +} + +/* + int_wis_bonus() + Spell point adjustment for changing levels +*/ + +int +int_wis_bonus(void) +{ + int ret_val = -2; + int casters_stat; + + switch (player.t_ctype) + { + case C_PALADIN: + case C_CLERIC: + casters_stat = pstats.s_wisdom; + break; + case C_RANGER: + case C_DRUID: + casters_stat = pstats.s_wisdom; + break; + case C_MAGICIAN: + casters_stat = pstats.s_intel; + break; + case C_ILLUSION: + casters_stat = pstats.s_intel; + break; + + default: + if (is_wearing(R_WIZARD)) + casters_stat = pstats.s_intel; + else if (is_wearing(R_PIETY)) + casters_stat = pstats.s_wisdom; + else + casters_stat = (rnd(2) ? pstats.s_wisdom : + pstats.s_intel); + } + + if (casters_stat > 12) + ret_val = casters_stat - 12; + else if (casters_stat > 6) + ret_val = 0; + else if (casters_stat > 3) + ret_val = -1; + + return(ret_val); +} + +void +electrificate(void) +{ + int affect_dist = 4 + player.t_stats.s_lvl / 4; + struct linked_list *item, *nitem; + + for (item = mlist; item != NULL; item = nitem) + { + struct thing *tp = THINGPTR(item); + char *mname = monsters[tp->t_index].m_name; + + nitem = next(item); + + if (DISTANCE(tp->t_pos, hero) < affect_dist) + { + int damage = roll(2, player.t_stats.s_lvl); + + debug("Charge does %d (%d)", damage, tp->t_stats.s_hpt - damage); + + if (on(*tp, NOBOLT)) + continue; + + if ((tp->t_stats.s_hpt -= damage) <= 0) + { + msg("The %s is killed by an electric shock.", mname); + killed(&player, item, NOMESSAGE, POINTS); + continue; + } + + if (rnd(tp->t_stats.s_intel / 5) == 0) + { + turn_on(*tp, ISFLEE); + msg("The %s is shocked by electricity.", mname); + } + else + msg("The %s is zapped by your electricity.", mname); + + summon_help(tp, NOFORCE); + turn_off(*tp, ISFRIENDLY); + turn_off(*tp, ISCHARMED); + turn_on(*tp, ISRUN); + turn_off(*tp, ISDISGUISE); + chase_it(&tp->t_pos, &player); + fighting = after = running = FALSE; + } + } +} + +/* + feed_me - Print out interesting messages about food consumption +*/ + +static char *f_hungry[] = +{ + "want a cookie", + "feel like a snack", + "feel like some fruit", + "start having the munchies", + "are starting to get hungry" +}; + +static char *f_weak[] = +{ + "are really hungry", + "could eat a horse", + "want some food - now", + "are starting to feel weak", + "feel a gnawing in your stomach", + "are even willing to eat up cram", + "feel lightheaded from not eating" +}; + +static char *f_faint[] = +{ + "get dizzy from not eating", + "are starving for nutrients", + "feel too weak from lack of food", + "see a mirage of an incredible banquet", + "have incredible cramps in your stomach" +}; + +static char *f_plop[] = +{ + "faint", + "pass out", + "keel over", + "black out" +}; + +void +feed_me(int hunger) +{ + char *charp = NULL, *charp2 = NULL; + + switch (hunger) + { + case F_OK: + default: + debug("feed_me(%d) called.", hunger); + break; + + case F_HUNGRY: + charp = f_hungry[rnd(sizeof(f_hungry) / + sizeof(char *))]; + break; + + case F_WEAK: + charp = f_weak[rnd(sizeof(f_weak) / sizeof(char *))]; + break; + + case F_FAINT: + charp = f_faint[rnd(sizeof(f_faint) / sizeof(char *))]; + charp2 = f_plop[rnd(sizeof(f_plop) / sizeof(char *))]; + break; + } + + msg("You %s.", charp); + + if (hunger == F_FAINT) + msg("You %s.", charp2); +} + + +/* + get_monster_number() + prompt player for a monster on list returns 0 if none selected +*/ + +int +get_monster_number(char *message) +{ + int i; + int pres_monst = 1; + int ret_val = -1; + char buf[2 * LINELEN]; + char monst_name[2 * LINELEN]; + + while (ret_val == -1) + { + msg("Which monster do you wish to %s? (* for list)", message); + + if ((get_string(buf, cw)) != NORM) + return(0); + + if ((i = atoi(buf)) != 0) + ret_val = i; + else if (buf[0] != '*') + { + for (i = 1; i < nummonst; i++) + if ((strcmp(monsters[i].m_name, buf) == 0)) + ret_val = i; + } + /* The following hack should be redone by the windowing code */ + else + while (pres_monst < nummonst) /* Print out the monsters */ + { + int num_lines = LINES - 3; + + wclear(hw); + touchwin(hw); + + wmove(hw, 2, 0); + + for (i = 0; i < num_lines && pres_monst < nummonst; i++) + { + sprintf(monst_name, "[%d] %s\n", pres_monst, + monsters[pres_monst].m_name); + waddstr(hw, monst_name); + pres_monst++; + } + + if (pres_monst < nummonst) + { + mvwaddstr(hw, LINES - 1, 0, morestr); + wrefresh(hw); + wait_for(' '); + } + else + { + mvwaddstr(hw, 0, 0, "Which monster"); + waddstr(hw, "? "); + wrefresh(hw); + } + } + +get_monst: + get_string(monst_name, hw); + ret_val = atoi(monst_name); + + if ((ret_val < 1 || ret_val > nummonst - 1)) + { + mvwaddstr(hw, 0, 0, "Please enter a number in the displayed range -- "); + wrefresh(hw); + goto get_monst; + } + + /* Set up for redraw */ + + clearok(cw, TRUE); + touchwin(cw); + } + + return(ret_val); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/monsdata.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/monsdata.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,2878 @@ +/* + monsdata.c - monster data initializer + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include "rogue.h" + +#define HPT(x) x + +/* {"Name", + CARRY_PROB, NORMAL, WANDER, SYMBOL, INTELLIGENCE, + {ATTRIBUTE_FLAGS}, + CREATURE_SUMMONED, NUMBER_SUMMONED, + ADDED_EXPERIENCE_PER_HIT_POINT, + {str, exp_pts, exp_level, armor_class, hit_points, + "damage"} +}, +*/ + +struct monster monsters[] = +{ +{"the player", + 0, FALSE, FALSE, '\0', "", + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + 0, 0, + 0, + {10, 0, 0, 0, HPT(""), ""} +}, +{"bat", + 0, TRUE, FALSE, 'B', "2-4", + {ISMEAN, CANFLY, ISHUH, CANDISEASE, ISFLOCK, ISSMALL}, + 0, 0, + 0, + {10, 5, 2, 1, HPT("1d4"), "1d2"} +}, +{"xvart", + 0, TRUE, TRUE, 'x', "8-12", + {ISMEAN, ISSWARM, ISSMALL, CANWIELD}, + 0, 0, + 1, + {8, 5, 1, 7, HPT("1d4"), "1d4+1"} +}, +{"giant rat", + 0, TRUE, TRUE, 'R', "2-4", + {ISMEAN, CANDISEASE, ISFLOCK, ISSMALL}, + 0, 0, + 1, + {10, 7, 1, 7, HPT("1d4"), "1d3"} +}, +{"jackal", + 0, TRUE, TRUE, 'J', "2-5", + {ISMEAN, ISSMALL}, + 0, 0, + 1, + {8, 5, 1, 7, HPT("1d4"), "1d2"} +}, +{"snake", + 0, TRUE, FALSE, 'S', "2-5", + {ISMEAN, ISSMALL}, + 0, 0, + 1, + {8, 5, 1, 5, HPT("1d4"), "1d3"} +}, +{"kobold", + 0, TRUE, TRUE, 'K', "8-8", + {ISMEAN, CANSHOOT, CANWIELD, ISSWARM, ISSMALL}, + 0, 0, + 1, + {9, 5, 1, 7, HPT("1d4"), "1d4"} +}, +{"vilstrak", + 5, TRUE, FALSE, 'V', "5-8", + {ISMEAN, CANINWALL}, + 0, 0, + 1, + {10, 7, 1, 2, HPT("1d6"), "1d4+1/1d4+1"} +}, +{"gnome", + 10, TRUE, TRUE, 'G', "11-12", + {CANSHOOT, CANWIELD, ISSWARM, ISSMALL, MEDFRIENDLY}, + 0, 0, + 1, + {10, 8, 1, 5, HPT("1d6"), "1d6"} +}, +{"halfling", + 10, TRUE, TRUE, 'H', "11-12", + {CANSHOOT, CANWIELD, ISSWARM, ISSMALL, HIGHFRIENDLY}, + 0, 0, + 1, + {8, 9, 1, 4, HPT("1d6"), "1d6"} +}, +{"dwarf", + 15, TRUE, TRUE, 'D', "11-12", + {CANSHOOT, CANWIELD, ISSMALL, ISSWARM, MEDFRIENDLY}, + 0, 0, + 1, + {14, 10, 1, 4, HPT("1d8"), "1d8"} +}, +{"orc", + 15, TRUE, TRUE, 'O', "8-8", + {ISMEAN, CANSHOOT, CANWIELD, ISSWARM}, + 0, 0, + 1, + {12, 10, 1, 6, HPT("1d8"), "1d8"} +}, +{"larva", + 0, TRUE, TRUE, 'l', "5-8", + {ISMEAN, ISFLOCK}, + 0, 0, + 1, + {6, 10, 1, 7, HPT("1d8"), "1d4+1"} +}, +{"skeleton", + 0, TRUE, TRUE, 's', "0-0", + {ISMEAN, ISSWARM, ISUNDEAD}, + 0, 0, + 1, + {6, 14, 1, 7, HPT("1d8"), "1d6"} +}, +{"carrion crawler", + 0, TRUE, TRUE, 'c', "2-4", + {ISMEAN, CANPARALYZE, ISLARGE}, + 0, 0, + 1, + {8, 20, 1, 7, HPT("1d8+1"), "1d2"} +}, +{"manes", + 0, TRUE, TRUE, 'M', "2-4", + {ISMEAN, MAGICHIT, ISUNDEAD, ISSMALL}, + 0, 0, + 1, + {10, 18, 1, 7, HPT("1d8"), "1d2/1d2/1d4"} +}, +{"elf", + 50, TRUE, TRUE, 'E', "13-20", + {CANSHOOT, CANWIELD, CANBARGAIN, ISSWARM, HIGHFRIENDLY}, + 0, 0, + 2, + {12, 20, 1, 5, HPT("1d8+1"), "1d10"} +}, +{"hobgoblin", + 0, TRUE, TRUE, 'H', "8-10", + {ISMEAN, CANSHOOT, CANWIELD, ISSWARM}, + 0, 0, + 2, + {14, 20, 1, 5, HPT("1d8+1"), "1d8"} +}, +{"wild dog", + 0, TRUE, TRUE, 'w', "2-5", + {ISMEAN, ISFAST, ISSMALL, ISFLOCK}, + 0, 0, + 2, + {10, 20, 1, 7, HPT("1d8+1"), "1d4"} +}, +{"baboon", + 0, TRUE, TRUE, 'b', "4-7", + {ISMEAN, ISSMALL, ISSWARM}, + 0, 0, + 2, + {10, 20, 1, 7, HPT("1d8+1"), "1d4"} +}, +{"fire beetle", + 0, TRUE, TRUE, 'B', "0-0", + {ISMEAN, HASFIRE, ISFLOCK, ISSMALL}, + 0, 0, + 2, + {10, 20, 1, 4, HPT("1d8+2"), "2d4"} +}, +{"badger", + 0, TRUE, TRUE, 'k', "4-6", + {CANSURPRISE, ISSMALL}, + 0, 0, + 3, + {10, 30, 2, 4, HPT("1d8+2"), "2d2/1d3"} +}, +{"giant ant", + 0, TRUE, TRUE, 'A', "1-1", + {ISMEAN, CANPOISON, ISSMALL, ISSWARM}, + 0, 0, + 3, + {10, 40, 2, 3, HPT("2d8"), "1d6"} +}, +{"war dog", + 0, TRUE, TRUE, 'D', "3-5", + {ISMEAN, ISFAST}, + 0, 0, + 3, + {10, 35, 2, 6, HPT("2d8+2"), "2d4"} +}, +{"zombie", + 0, TRUE, TRUE, 'Z', "0-0", + {ISMEAN, ISUNDEAD, ISFLOCK}, + 0, 0, + 2, + {10, 20, 2, 8, HPT("2d8"), "1d8"} +}, +{"wuccubi", + 0, TRUE, TRUE, 'w', "0-0", + {ISMEAN}, + 0, 0, + 2, + {10, 20, 2, 8, HPT("2d8"), "1d4/1d10"} +}, +{"aarakocra", + 5, TRUE, TRUE, 'a', "8-12", + {CANFLY, CANWIELD, ISFLOCK, LOWFRIENDLY}, + 0, 0, + 2, + {11, 28, 2, 7, "1d8+2", "1d3/1d3"} +}, +{"gnoll", + 0, TRUE, TRUE, 'g', "7-11", + {ISMEAN, CANWIELD, CANSHOOT, ISLARGE, ISSWARM}, + 0, 0, + 2, + {10, 28, 2, 5, HPT("2d8"), "2d4"} +}, +{"warthog", + 0, TRUE, TRUE, 'w', "3-5", + {ISMEAN}, + 0, 0, + 3, + {14, 35, 2, 7, HPT("3d8"), "2d8/2d8"} +}, +{"black bear", + 0, TRUE, FALSE, 'U', "5-8", + {CANHUG}, + 0, 0, + 3, + {10, 30, 3, 7, HPT("2d8+3"), "2d3"} +}, +{"ear seeker", + 0, TRUE, TRUE, 'e', "0-0", + {ISMEAN, CANINFEST, ISSMALL}, + 0, 0, + 0, + {10, 0, 1, 9, HPT("1d1"), "0d0"} +}, +{"floating eye", + 0, TRUE, TRUE, 'E', "0-0", + {CANHUH, ISSMALL}, + 0, 0, + 1, + {7, 30, 1, 9, HPT("1d4"), "0d0"} +}, +{"brownie", + 0, TRUE, TRUE, 'B', "12-15", + {LOWCAST, CANSHOOT, CANWIELD, CANSURPRISE, STEALGOLD, CANBARGAIN, ISSMALL, ISFLOCK, HIGHFRIENDLY}, + 0, 0, + 0, + {6, 31, 10, 3, HPT("1d4"), "1d3"} +}, +{"shrieker", + 0, TRUE, FALSE, 'S', "0-0", + {CANSHRIEK, NOMOVE, ISFLOCK}, + 0, 0, + 1, + {10, 5, 3, 7, HPT("3d8"), "0d0"} +}, +{"gas spore", + 0, TRUE, FALSE, 'g', "0-0", + {CANSPORE, NOMOVE, ISLARGE}, + 0, 0, + 0, + {8, 33, 2, 9, HPT("1d1"), "0d0"} +}, +{"hyena", + 0, TRUE, TRUE, 'h', "3-5", + {ISMEAN}, + 0, 0, + 3, + {10, 35, 2, 7, HPT("3d8"), "2d4"} +}, +{"mind maggot", + 0, TRUE, TRUE, 'm', "1-1", + {ISMEAN, NOMOVE, DRAINBRAIN, ISFLOCK, CANSURPRISE, ISSMALL}, + 0, 0, + 3, + {10, 40, 4, 8, HPT("1d8+1"), "1d3"} +}, +{"giant beetle", + 0, TRUE, FALSE, 'b', "0-0", + {ISFLOCK}, + 0, 0, + 4, + {10, 40, 3, 4, HPT("2d8"), "4d4"} +}, +{"bombadier beetle", + 0, TRUE, FALSE, 'B', "0-0", + {CANBACID, ISFLOCK}, + 0, 0, + 4, + {10, 40, 2, 4, HPT("2d8+2"), "2d6"} +}, +{"stirge", + 0, TRUE, TRUE, 's', "1-1", + {ISMEAN, CANFLY, CANDRAW, ISSMALL}, + 0, 0, + 2, + {10, 36, 4, 8, HPT("1d8+1"), "1d3"} +}, +{"wild camel", + 0, TRUE, TRUE, 'w', "3-7", + {ISMEAN, CANTRAMPLE, ISLARGE, ISFLOCK}, + 0, 0, + 3, + {12, 35, 2, 7, HPT("3d8"), "1d4"} +}, +{"wolf", + 0, TRUE, TRUE, 'W', "3-7", + {ISMEAN, ISLARGE, ISSWARM, ISFAST}, + 0, 0, + 3, + {12, 35, 2, 7, HPT("2d8+2"), "1d4+1"} +}, +{"troglodyte", + 5, TRUE, TRUE, 'T', "5-7", + {ISMEAN, CANSMELL, CANSHOOT, CANWIELD, ISSWARM}, + 0, 0, + 2, + {10, 36, 2, 5, HPT("2d8"), "1d3/1d3/2d5"} +}, +{"killer frog", + 0, TRUE, FALSE, 'f', "2-4", + {ISMEAN}, + 0, 0, + 4, + {10, 40, 3, 6, HPT("3d8"), "2d3/1d4"} +}, +{"axe beak", + 0, TRUE, FALSE, 'a', "2-4", + {ISMEAN, ISLARGE}, + 0, 0, + 4, + {10, 40, 3, 6, HPT("3d8"), "2d3/2d4"} +}, +{"spinning tick", + 0, TRUE, TRUE, 't', "2-6", + {ISMEAN, CANFRIGHTEN, ISSMALL, ISFAST, ISFLOCK}, + 0, 0, + 5, + {10, 50, 2, 3, HPT("2d8+2"), "1d2/1d2/1d4"} +}, +{"giant centipede", + 0, TRUE, TRUE, 'c', "1-2", + {ISMEAN, CANPOISON, ISLARGE, ISFLOCK}, + 0, 0, + 1, + {6, 40, 2, 9, HPT("2d2"), "2d3"} +}, +{"pegasus", + 0, TRUE, TRUE, 'p', "9-12", + {CANFLY, ISFAST, ISLARGE, LOWFRIENDLY}, + 0, 0, + 4, + {15, 50, 3, 6, HPT("4d8"), "1d3/1d8/1d8"} +}, +{"lemure", + 0, TRUE, FALSE, 'L', "2-4", + {ISMEAN, ISREGEN, MAGICHIT, ISUNDEAD, ISFLOCK}, + 0, 0, + 3, + {10, 65, 3, 7, HPT("3d8"), "1d3"} +}, +{"zemure", + 0, TRUE, FALSE, 'z', "2-4", + {ISMEAN, ISREGEN, MAGICHIT, ISUNDEAD, ISFLOCK}, + 0, 0, + 3, + {10, 65, 4, 7, HPT("3d8"), "1d4"} +}, +{"giant beaver", + 0, TRUE, TRUE, 'B', "6-9", + {ISMEAN, CANSWIM, ISSWARM}, + 0, 0, + 4, + {10, 60, 1, 6, HPT("4d8"), "4d4"} +}, +{"crocodile", + 0, TRUE, TRUE, 'c', "2-5", + {ISMEAN, CANSWIM, CANSURPRISE, ISFLOCK, ISLARGE}, + 0, 0, + 4, + {15, 60, 3, 5, HPT("3d8"), "1d3/2d4"} +}, +{"hipogriff", + 0, TRUE, TRUE, 'h', "3-6", + {CANFLY, ISMEAN}, + 0, 0, + 4, + {12, 60, 3, 5, HPT("3d8+3"), "1d6/1d6/1d10"} +}, +{"giant goat", + 0, TRUE, TRUE, 'G', "3-5", + {ISMEAN, ISFAST, ISLARGE}, + 0, 0, + 4, + {10, 85, 3, 7, HPT("3d8+1"), "2d8"} +}, +{"wererat", + 20, TRUE, TRUE, 'r', "11-12", + {ISMEAN, CANSUMMON, MAGICHIT, ISFLOCK}, + "giant rat", 4, + 4, + {10, 90, 3, 6, HPT("3d8+1"), "1d8"} +}, +{"ulodyte", + 0, TRUE, TRUE, 'u', "5-8", + {ISLARGE}, + 0, 0, + 3, + {10, 80, 3, 6, HPT("3d8"), "1d3/1d3"} +}, +{"brown bear", + 0, TRUE, TRUE, 'U', "5-8", + {CANHUG, ISLARGE}, + 0, 0, + 3, + {10, 80, 3, 6, HPT("4d8+5"), "2d6/1d8"} +}, +{"ghoul", + 0, TRUE, TRUE, 'g', "5-7", + {ISMEAN, CANPARALYZE, ISUNDEAD, ISFLOCK}, + 0, 0, + 2, + {10, 65, 2, 6, HPT("2d8"), "1d3/1d3/1d6"} +}, +{"giant hyena", + 0, TRUE, TRUE, 'H', "3-5", + {ISMEAN, ISLARGE}, + 0, 0, + 5, + {10, 90, 2, 7, HPT("3d8"), "3d4"} +}, +{"huorn", + 0, TRUE, TRUE, 'h', "6-10", + {CANBBURN, ISSHADOW, CANHUG}, + 0, 0, + 3, + {18, 60, 3, 7, HPT("5d6"), "3d3"} +}, +{"piercer", + 0, TRUE, FALSE, 'P', "0-0", + {NOMOVE, CANSURPRISE, ISSWARM}, + 0, 0, + 4, + {10, 60, 3, 3, HPT("4d8"), "4d6"} +}, +{"ape", + 0, TRUE, FALSE, 'A', "4-6", + {CANHUG}, + 0, 0, + 1, + {10, 50, 3, 6, HPT("4d8+1"), "2d3"} +}, +{"homonculous", + 0, TRUE, TRUE, 'H', "9-15", + {CANFLY, ISSMALL, LOWFRIENDLY}, + 0, 0, + 2, + {10, 81, 3, 6, HPT("2d8"), "1d3"} +}, +{"leprechaun", + 0, TRUE, FALSE, 'L', "15-16", + {ISMEAN, CARRYGOLD, STEALGOLD, CANBARGAIN, MEDFRIENDLY}, + 0, 0, + 1, + {10, 80, 8, -6, HPT("4d8+1"), "1d1"} +}, +{"ogre", + 50, TRUE, TRUE, 'o', "5-7", + {ISMEAN, ISGREED, ISLARGE, ISFLOCK}, + 0, 0, + 5, + {18, 90, 4, 5, HPT("4d8+1"), "2d6"} +}, +{"bull", + 0, TRUE, TRUE, 'B', "3-5", + {ISMEAN, CANTRAMPLE, ISFLOCK, ISLARGE}, + 0, 0, + 4, + {14, 85, 2, 7, HPT("4d8"), "1d6"} +}, +{"wild boar", + 0, TRUE, TRUE, 'w', "3-5", + {ISMEAN, ISFLOCK}, + 0, 0, + 4, + {15, 85, 3, 7, HPT("3d8+3"), "3d4"} +}, +{"centaur", + 15, TRUE, TRUE, 'C', "5-10", + {CANSHOOT, CANWIELD, ISFLOCK, ISLARGE, LOWFRIENDLY}, + 0, 0, + 4, + {10, 85, 4, 4, HPT("4d8"), "1d6/1d6"} +}, +{"pseudo dragon", + 10, TRUE, FALSE, 'p', "15-16", + {ISMEAN, ISGREED}, + 0, 0, + 9, + {10, 100, 3, -1, HPT("3d9"), "2d3/1d6"} +}, +{"very young dragon", + 10, TRUE, FALSE, 'd', "15-16", + {ISMEAN, CANBRANDOM, ISGREED}, + 0, 0, + 9, + {10, 100, 3, -1, HPT("3d9"), "1d8/1d8/3d10"} +}, +{"batarang", + 20, TRUE, TRUE, 'B', "4-6", + {ISMEAN, CANFRIGHTEN, CANFLY, CANSUMMON, ISFLOCK, ISSMALL}, + "bat", 4, + 5, + {10, 100, 3, 1, HPT("3d8"), "1d4/1d4"} +}, +{"carnivorous ape", + 5, TRUE, FALSE, 'A', "7-10", + {CANHUG, ISMEAN, ISLARGE}, + 0, 0, + 4, + {12, 120, 4, 6, HPT("5d8"), "2d4"} +}, +{"pixie", + 0, TRUE, TRUE, 'p', "12-15", + {LOWCAST, CANSHOOT, CANWIELD, ISINVIS, ISSMALL, ISFLOCK, LOWFRIENDLY}, + 0, 0, + 1, + {6, 105, 3, 3, HPT("1d4"), "1d2"} +}, +{"mountain lion", + 0, TRUE, TRUE, 'L', "3-5", + {ISMEAN, ISFAST}, + 0, 0, + 6, + {12, 110, 3, 6, HPT("3d8+1"), "1d3/1d3/1d6"} +}, +{"bugbear", + 5, TRUE, TRUE, 'U', "5-8", + {ISMEAN, CANSHOOT, CANWIELD, CANSURPRISE, ISFLOCK, ISLARGE}, + "black bear", 2, + 6, + {16, 135, 3, 5, HPT("3d8+1"), "2d4"} +}, +{"giant lizard", + 0, TRUE, TRUE, 'G', "2-4", + {ISFAST, ISLARGE}, + 0, 0, + 4, + {13, 125, 4, 5, HPT("3d8+1"), "1d8"} +}, +{"harpy", + 0, TRUE, TRUE, 'H', "6-9", + {CANFLY, CANWIELD}, + 0, 0, + 3, + {10, 145, 4, 7, HPT("3d8"), "1d3/1d3/1d6"} +}, +{"iguanadon", + 0, TRUE, TRUE, 'i', "0-0", + {ISMEAN, ISLARGE, ISFLOCK}, + 0, 0, + 6, + {12, 150, 4, 4, HPT("6d8"), "1d3/1d3/2d4"} +}, +{"leopard", + 0, TRUE, TRUE, 'L', "3-5", + {ISMEAN, ISFAST, CANSURPRISE}, + 0, 0, + 4, + {10, 150, 4, 6, HPT("3d8+2"), "1d3/1d3/1d6"} +}, +{"nymph", + 100, TRUE, FALSE, 'N', "15-16", + {STEALMAGIC, MEDCAST, LOWFRIENDLY}, + 0, 0, + 3, + {10, 350, 8, -5, HPT("3d8"), "0d0"} +}, +{"giant ram", + 0, TRUE, TRUE, 'R', "3-5", + {ISFLOCK, CANSUMMON, ISFAST}, + "giant goat", 1, + 4, + {10, 285, 3, 6, HPT("4d8"), "2d6"} +}, +{"rot grub", + 0, TRUE, TRUE, 'r', "0-0", + {ISMEAN, CANINFEST, ISSWARM, ISSMALL, ISSLOW}, + 0, 0, + 0, + {10, 0, 1, 9, HPT("1d1"), "0d0"} +}, +{"triffid", + 0, TRUE, FALSE, 'T', "12-18", + {ISMEAN, CANHOLD, ISSLOW, CANBBURN, CANPOISON}, + "huorn", 1, + 4, + {10, 135, 10, 7, HPT("2d8"), "1d6"} +}, +{"violet fungi", + 0, TRUE, FALSE, 'F', "0-0", + {ISMEAN, CANHOLD, NOMOVE, CANROT, CANINFEST, CANPOISON}, + 0, 0, + 4, + {10, 135, 3, 7, HPT("3d8"), "5d1"} +}, +{"giant tick", + 0, TRUE, TRUE, 't', "0-0", + {ISMEAN, CANDRAW, CANDISEASE, ISSMALL}, + 0, 0, + 2, + {10, 105, 3, 3, HPT("3d8"), "1d4"} +}, +{"giant eagle", + 0, TRUE, TRUE, 'e', "0-0", + {ISMEAN, CANFLY, CANSPEAK, ISFLOCK, MEDFRIENDLY}, + 0, 0, + 4, + {10, 150, 3, 7, HPT("4d8"), "1d6/1d6/2d6"} +}, +{"peryton", + 0, TRUE, TRUE, 'P', "0-0", + {ISMEAN, CANFLY, CANSPEAK, ISFLOCK}, + 0, 0, + 4, + {10, 150, 3, 7, HPT("4d8"), "4d4"} +}, +{"gelatinous cube", + 90, TRUE, TRUE, 'c', "0-0", + {ISSLOW, ISMEAN, ISSCAVENGE, CANPARALYZE, ISLARGE}, + 0, 0, + 4, + {10, 150, 4, 8, HPT("4d8"), "2d4"} +}, +{"giant owl", + 0, TRUE, TRUE, 'O', "0-0", + {ISMEAN, CANFLY}, + 0, 0, + 4, + {10, 150, 4, 6, HPT("4d8"), "2d4/2d4/1d4+1"} +}, +{"giant skunk", + 0, TRUE, TRUE, 's', "3-5", + {CANSTINK, ISFLEE}, + 0, 0, + 5, + {10, 165, 4, 7, HPT("5d8"), "1d6"} +}, +{"blink dog", + 0, TRUE, TRUE, 'B', "8-10", + {ISMEAN, CANBLINK, ISFLOCK, HIGHFRIENDLY}, + 0, 0, + 5, + {10, 170, 4, 5, HPT("4d8"), "1d6"} +}, +{"rust monster", + 0, TRUE, TRUE, 'R', "1-1", + {ISMEAN, CANRUST}, + 0, 0, + 4, + {10, 185, 5, 2, HPT("3d8"), "0d0/0d0"} +}, +{"ghast", + 0, TRUE, TRUE, 'G', "11-12", + {CANPARALYZE, CANSTINK, ISMEAN, ISUNDEAD}, + 0, 0, + 4, + {10, 190, 4, 4, HPT("4d8"), "1d4/1d4/1d8"} +}, +{"blindheim", + 0, TRUE, FALSE, 'b', "1", + {ISMEAN, CANBLIND}, + 0, 0, + 4, + {8, 200, 2, 1, HPT("4d8+2"), "1d8"} +}, +{"jaguar", + 0, TRUE, TRUE, 'j', "3-5", + {ISMEAN, CANSURPRISE, ISLARGE}, + 0, 0, + 5, + {12, 205, 2, 7, HPT("4d8+1"), "2d3/2d5"} +}, +{"dryad", + 100, TRUE, FALSE, 'D', "15-16", + {STEALMAGIC, LOWFRIENDLY}, + 0, 0, + 3, + {8, 325, 8, -2, HPT("2d8"), "1d1"} +}, +{"anhkheg", + 5, TRUE, FALSE, 'a', "2-4", + {ISMEAN,CANSURPRISE, ISLARGE}, + 0, 0, + 2, + {10, 300, 3, 2, HPT("1d6+2"), "3d6"} +}, +{"shadow", + 0, TRUE, TRUE, 's', "5-7", + {ISSHADOW, ISMEAN, CANCHILL, ISUNDEAD, ISFLOCK}, + 0, 0, + 4, + {10, 255, 3, 7, HPT("3d8+3"), "1d6"} +}, +{"gargoyle", + 5, TRUE, TRUE, 'g', "5-7", + {ISMEAN, MAGICHIT, ISFLOCK}, + 0, 0, + 5, + {10, 165, 4, 5, HPT("4d8+4"), "1d3/1d3/1d6/1d4"} +}, +{"su-monster", + 10, TRUE, TRUE, 's', "8-10", + {ISMEAN}, + 0, 0, + 6, + {10, 225, 5, 6, HPT("5d8+5"), "4d4/2d4"} +}, +{"gray ooze", + 50, TRUE, FALSE, 'o', "1-1", + {ISMEAN, NOMOVE, CANRUST, ISSCAVENGE, BOLTDIVIDE, BLOWDIVIDE, NOFIRE, NOCOLD}, + 0, 0, + 5, + {10, 200, 3, 8, HPT("3d8+3"), "2d8"} +}, +{"psuedo-dragon", + 0, TRUE, TRUE, 'P', "8-12", + {CANSURPRISE, CANSEE, CANFLY, CANPOISON, LOWFRIENDLY}, + 0, 0, + 2, + {10, 200, 3, 2, HPT("2d8"), "1d3"} +}, +{"lava child", + 0, TRUE, TRUE, 'l', "8-12", + {NOMETAL, NOFIRE, LOWFRIENDLY}, + 0, 0, + 5, + {11, 205, 4, 4, HPT("5d8"), "1d6/1d6/2d6"} +}, +{"hell hound", + 0, TRUE, TRUE, 'h', "5-8", + {ISMEAN, NOFIRE, CANBFIRE, CANSURPRISE, CANSEE}, + 0, 0, + 8, + {10, 250, 4, 4, HPT("5d8+4"), "1d10"} +}, +{"pech", + 60, TRUE, FALSE, 'p', "8-14", + {CANINWALL, LOWFRIENDLY}, + 0, 0, + 4, + {10, 240, 4, 3, HPT("4d8"), "1d6"} +}, +{"winter wolf", + 0, TRUE, TRUE, 'w', "8-12", + {ISMEAN, ISFAST, CANBICE, NOCOLD, ISFLOCK}, + "wolf", 6, + 5, + {10, 245, 4, 5, HPT("6d8"), "2d4"} +}, +{"sylph", + 100, TRUE, FALSE, 's', "15-16", + {STEALMAGIC, LOWFRIENDLY}, + 0, 0, + 3, + {8, 325, 8, -2, HPT("2d8"), "1d1"} +}, +{"lion", + 0, TRUE, TRUE, 'L', "3-5", + {ISMEAN, ISLARGE}, + 0, 0, + 6, + {12, 300, 4, 6, HPT("5d8+2"), "1d4/1d4/1d10"} +}, +{"undine", + 30, TRUE, TRUE, 'u', "12-18", + {ISMEAN, MAGICHIT, NOBOLT, NOCOLD, CANSUMMON, CANSEE, CANSWIM, ISFAST}, + "crocodile", 3, + 10, + {10, 200, 9, 7, HPT("7d8+3"), "1d6/1d6/1d6/1d4"} +}, +{"ochre jelly", + 0, TRUE, FALSE, 'O', "1-1", + {ISMEAN, ISSLOW, BOLTDIVIDE, CANROT}, + "gelatinous cube", 2, + 10, + {10, 250, 4, 8, HPT("6d8"), "3d4"} +}, +{"phoenix", + 5, TRUE, FALSE, 'X', "5-7", + {NOFIRE, NOBOLT, CANSPEAK, CANBFIRE, ISLARGE, MEDFRIENDLY}, + 0, 0, + 8, + {10, 200, 4, 2, HPT("4d8"), "1d4/1d8"} +}, +{"owlbear", + 5, TRUE, TRUE, 'U', "5-7", + {ISMEAN, CANHUG, CANSUMMON, ISLARGE}, + "giant owl", 2, + 12, + {10, 225, 5, 5, HPT("5d8+2"), "1d6/1d6/2d6"} +}, +{"phycomid", + 3, TRUE, FALSE, 'P', "0-0", + {CANPOISON, CANINFEST, CANBACID}, + 0, 0, + 5, + {10, 280, 5, 5, HPT("4d8"), "3d2/3d2"} +}, +{"slithering tracker", + 90, TRUE, TRUE, 't', "0-0", + {ISMEAN, ISSCAVENGE, CANPARALYZE, ISSMALL, ISSHADOW, CANDRAW}, + 0, 0, + 5, + {10, 280, 5, 5, HPT("5d8"), "2d4"} +}, +{"satyr", + 0, TRUE, TRUE, 's', "10-14", + {CANSURPRISE, LOWFRIENDLY}, + 0, 0, + 5, + {10, 280, 5, 5, HPT("5d8"), "2d4"} +}, +{"imp", + 25, TRUE, TRUE, 'i', "8-10", + {ISMEAN, ISREGEN, MAGICHIT, CANPOISON, CANSURPRISE, ISSMALL, ISUNDEAD}, + 0, 0, + 3, + {10, 275, 2, 2, HPT("2d8+2"), "1d4"} +}, +{"quellit", + 30, TRUE, TRUE, 'q', "7-11", + {ISMEAN, ISREGEN, MAGICHIT }, + 0, 0, + 3, + {10, 400, 7, 2, HPT("4d8"), "2d10/2d6"} +}, +{"quasit", + 30, TRUE, TRUE, 'Q', "5-7", + {ISMEAN, ISREGEN, MAGICHIT, CANSURPRISE, CANITCH, ISSMALL, ISUNDEAD}, + 0, 0, + 3, + {10, 325, 7, 2, HPT("3d8"), "1d2/1d2/1d4"} +}, +{"doppleganger", + 0, TRUE, TRUE, 'D', "11-12", + {ISMEAN, CANSURPRISE}, + 0, 0, + 4, + {10, 330, 10, 5, HPT("4d8"), "1d12"} +}, +{"subterranean lizard", + 0, TRUE, TRUE, 's', "0-0", + {ISFAST, ISLARGE}, + 0, 0, + 6, + {14, 350, 6, 5, HPT("6d8"), "2d6"} +}, +{"giant porcupine", + 0, TRUE, FALSE, 'p', "0-0", + {ISFLEE, CANSTICK}, + 0, 0, + 6, + {10, 350, 6, 5, HPT("6d8"), "2d4"} +}, +{"plateosaurus", + 0, TRUE, TRUE, 'P', "0-0", + {ISFLEE, CANTRAMPLE, ISLARGE}, + 0, 0, + 10, + {18, 375, 9, 5, HPT("8d8"), "0d0"} +}, +{"buffalo", + 0, TRUE, TRUE, 'b', "3-5", + {ISMEAN, CANTRAMPLE, ISSWARM, ISLARGE}, + 0, 0, + 8, + {16, 350, 2, 7, HPT("5d8"), "1d8"} +}, +{"cockatrice", + 0, TRUE, TRUE, 'c', "1-1", + {ISMEAN, CANFLY, TOUCHSTONE, ISSMALL}, + 0, 0, + 5, + {10, 315, 5, 6, HPT("5d8"), "1d3"} +}, +{"yeti", + 30, TRUE, TRUE, 'Y', "8-10", + {ISMEAN, CANPARALYZE,CANHUG, NOCOLD, CANSURPRISE, ISLARGE, LOWFRIENDLY}, + "winter wolf", 2, + 12, + {13, 500, 6, 6, HPT("4d8+4"), "1d6/1d6"} +}, +{"lonchu", + 0, TRUE, FALSE, 'l', "8-10", + {ISMEAN}, + 0, 0, + 8, + {10, 475, 6, 4, HPT("6d8+1"), "1d4/1d4"} +}, +{"leucrotta", + 0, TRUE, FALSE, 'L', "8-10", + {ISMEAN, ISLARGE}, + 0, 0, + 8, + {10, 475, 6, 4, HPT("6d8+1"), "3d6/1d6/1d6"} +}, +{"giant crocodile", + 0, TRUE, TRUE, 'g', "2-4", + {CANSWIM, CANSUMMON, CANSURPRISE, ISLARGE}, + "undine", 1, + 8, + {18, 400, 5, 4, HPT("7d8"), "3d6/2d10"} +}, +{"griffon", + 0, TRUE, TRUE, 'g', "0-0", + {CANFLY, ISMEAN, ISLARGE}, + 0, 0, + 10, + {12, 375, 4, 3, HPT("7d8"), "1d4/1d4/2d8"} +}, +{"warg", + 10, TRUE, TRUE, 'W', "5-9", + {ISFAST, CANSUMMON, ISFLOCK}, + "wolf", 3, + 8, + {12, 400, 6, 4, HPT("3d8+3"), "1d4+4"} +}, +{"unicorn", + 10, TRUE, TRUE, 'u', "8-12", + {ISFAST, CANSURPRISE, CANBLINK, ISLARGE, MEDFRIENDLY}, + 0, 0, + 5, + {12, 400, 6, 2, HPT("4d8+4"), "1d6/1d6/1d12"} +}, +{"entwife", + 35, TRUE, FALSE, 'w', "13-15", + {CANSUMMON, CANBBURN, ISSWARM, ISLARGE, HIGHFRIENDLY}, + "ent", 1, + 8, + {16, 400, 6, 4, HPT("3d8+3"), "1d4+4"} +}, +{"minotaur", + 0, TRUE, TRUE, 'm', "7-9", + {ISMEAN, CANWIELD, ISLARGE}, + 0, 0, + 8, + {14, 400, 5, 6, HPT("6d8+3"), "1d3/2d4"} +}, +{"displacer beast", + 0, TRUE, TRUE, 'D', "3-5", + {CANBLINK, ISSHADOW, ISLARGE}, + 0, 0, + 8, + {12, 475, 6, 4, HPT("6d8"), "2d4/2d4"} +}, +{"giant lynx", + 0, TRUE, TRUE, 'L', "9-13", + {ISMEAN, CANSURPRISE}, + 0, 0, + 3, + {10, 420, 4, 2, HPT("2d8+2"), "1d2/1d2/1d4"} +}, +{"young dragon", + 25, TRUE, FALSE, 'd', "12-15", + {ISMEAN, CANBRANDOM, ISGREED, ISLARGE, LOWFRIENDLY}, + 0, 0, + 9, + {10, 800, 6, 1, HPT("30d1"), "1d4/1d4/3d8"} +}, +{"ceratosaurus", + 0, TRUE, TRUE, 'c', "0-0", + {ISMEAN, ISFAST, ISLARGE}, + 0, 0, + 12, + {18, 600, 4, 5, HPT("8d8"), "1d6/1d6/4d4"} +}, +{"wight", + 35, TRUE, TRUE, 'W', "8-10", + {ISMEAN, CANSUMMON, CANDRAIN, MAGICHIT, ISUNDEAD, ISFLOCK}, + "skeleton", 2, + 7, + {10, 540, 4, 5, HPT("4d8+3"), "1d4"} +}, +{"monoclonius", + 0, TRUE, TRUE, 'M', "0-0", + {ISMEAN, ISFAST, CANTRAMPLE, ISLARGE}, + 0, 0, + 18, + {18, 550, 4, 3, HPT("8d8"), "2d8"} +}, +{"olog-hai troll", + 45, TRUE, TRUE, 'T', "12-18", + {ISMEAN, ISFLOCK, NOCOLD, CANWIELD, ISREGEN, CANSUMMON}, + "troll", 2, + 6, + {15, 640, 5, 3, HPT("5d6"), "2d8+4"} +}, +{"manticore", + 20, TRUE, TRUE, 'm', "7-9", + {ISMEAN, CANFLY}, + 0, 0, + 6, + {12, 525, 4, 4, HPT("6d8+3"), "1d3/1d3/1d8"} +}, +{"uruk-hai orc", + 45, TRUE, TRUE, 'O', "12-18", + {ISMEAN, ISSWARM, CANWIELD, CANSHOOT, CANSUMMON}, + "orc", 3, + 6, + {15, 540, 3, 5, HPT("5d4+3"), "3d4"} +}, +{"kazad dwarf", + 75, TRUE, FALSE, 'D', "12-18", + {ISFLOCK, CANWIELD, CANSHOOT, CANSUMMON, LOWFRIENDLY}, + "dwarf", 3, + 6, + {17, 540, 5, 3, HPT("5d8+3"), "3d4"} +}, +{"noldor elf", + 75, TRUE, FALSE, 'E', "18-20", + {ISFLOCK, CANWIELD, MEDCAST, CANSHOOT, CANSUMMON, LOWFRIENDLY}, + "elf", 3, + 6, + {14, 600, 6, 2, HPT("5d8+3"), "3d4"} +}, +{"ent", + 80, TRUE, FALSE, 'e', "15-17", + {ISREGEN, CANSUMMON, CANBBURN, HIGHCAST, ISLARGE, HIGHFRIENDLY}, + "huorn", 3, + 10, + {22, 600, 6, 4, HPT("6d8+6"), "1d4+4/1d4+4/2d6"} +}, +{"nightmare", + 0, TRUE, FALSE, 'n', "11-13", + {ISMEAN, ISFAST, NOFIRE, ISLARGE, ISUNDEAD}, + 0, 0, + 8, + {14, 600, 6, -4, HPT("6d8+6"), "2d4/1d6+4/1d6+4"} +}, +{"troll", + 50, TRUE, FALSE, 'T', "5-7", + {ISMEAN, ISREGEN, ISLARGE}, + 0, 0, + 8, + {18, 600, 6, 4, HPT("6d8+6"), "1d8/1d8/2d6"} +}, +{"wraith", + 0, TRUE, TRUE, 'W', "11-12", + {ISMEAN, CANDRAIN, CANSUMMON, MAGICHIT, ISUNDEAD}, + "zombie", 2, + 8, + {10, 575, 5, 4, HPT("5d8+3"), "1d6"} +}, +{"archer bush", + 50, TRUE, FALSE, 'a', "0-0", + {ISMEAN, CANPOISON, CANSUMMON, ISFLOCK, NOMOVE, CANBBURN, ISLARGE}, + "dryad", 2, + 20, + {10, 600, 8, 8, HPT("2d8"), "1d10"} +}, +{"green slime", + 0, TRUE, TRUE, 's', "0-0", + {NOMOVE, ISSCAVENGE, BOLTDIVIDE, BLOWDIVIDE, CANRUST, NOMOVE, CANINFEST, CANROT, ISSMALL}, + 0, 0, + 2, + {8, 610, 5, 9, HPT("2d8"), "1d1"} +}, +{"blink saber tooth tiger", + 0, TRUE, TRUE, 'T', "8-10", + {ISMEAN, CANSUMMON, CANBLINK, ISLARGE}, + "blink dog", 2, + 10, + {10, 670, 8, 3, HPT("8d8+6"), "1d10/1d10/1d8/1d8"} +}, +{"djinni", + 0, TRUE, TRUE, 'd', "10-15", + {CANCAST, ISFAST, CANSPEAK, ISLARGE, MEDFRIENDLY}, + 0, 0, + 5, + {12, 725, 6, 4, HPT("7d8+3"), "2d8"} +}, +{"stag beetle", + 0, TRUE, TRUE, 'B', "0-0", + {ISMEAN, ISFLOCK}, + 0, 0, + 8, + {10, 700, 5, 3, HPT("7d8"), "4d4/2d10"} +}, +{"gibbering ghoul", + 15, TRUE, TRUE, 'g', "3-15", + {ISMEAN, CANPARALYZE, CANDRAIN, ISUNDEAD, CANSUMMON}, + "ghoul", 2, + 10, + {10, 750, 6, 4, HPT("6d10"), "1d6+2/1d4+1/1d4+1/1d8"} +}, +{"catoblepas", + 10, TRUE, TRUE, 'c', "3-5", + {ISMEAN, LOOKSTONE, CANSTINK, ISLARGE}, + 0, 0, + 8, + {16, 700, 6, 7, HPT("6d8+2"), "1d6/1d8"} +}, +{"jackalwere", + 50, TRUE, TRUE, 'J', "11-12", + {ISMEAN, CANSHOOT, CANWIELD, CANSNORE, MAGICHIT, CANSUMMON}, + "jackal", 2, + 4, + {10, 800, 4, 4, HPT("4d8"), "2d4"} +}, +{"werebear", + 10, TRUE, TRUE, 'U', "14-16", + {ISMEAN, CANHUG, ISLARGE, MAGICHIT, CANSUMMON}, + "brown bear", 2, + 10, + {16, 825, 6, 2, HPT("7d8+3"), "1d3/1d3/2d8"} +}, +{"salamander", + 50, TRUE, TRUE, 's', "14-16", + {ISMEAN, NOFIRE, CANHUG, MAGICHIT, CANWIELD}, + "fire beetle", 2, + 14, + {13, 825, 6, 4, HPT("7d8+7"), "2d6/1d6"} +}, +{"ankylosaurus", + 0, TRUE, TRUE, 'a', "0-0", + {ISMEAN, CANTRAMPLE, ISLARGE}, + 0, 0, + 12, + {18, 900, 9, 0, HPT("9d8"), "3d6"} +}, +{"yeenoghu", + 0, TRUE, TRUE, 'y', "5-10", + {ISMEAN, CANTRAMPLE, ISLARGE}, + 0, 0, + 12, + {18, 900, 11, 0, HPT("9d8"), "3d6"} +}, +{"erinyes", + 25, TRUE, TRUE, 'E', "8-10", + {ISMEAN, CANFRIGHTEN, CANSUMMON, ISUNDEAD}, + "lemure", 3, + 8, + {10, 875, 7, 2, HPT("6d8+6"), "2d4"} +}, +{"spotted lion", + 0, TRUE, TRUE, 'L', "3-5", + {ISMEAN, ISLARGE}, + "lion", 2, + 10, + {12, 700, 6, 5, HPT("6d8+2"), "1d4/1d4/1d12"} +}, +{"killer bee", + 0, TRUE, TRUE, 'z', "2-4", + {ISMEAN, CANPOISON, CANFLY, ISMANY, ISSMALL}, + 0, 0, + 20, + {6, 800, 4, 0, HPT("3d8"), "1d10"} +}, +{"minotaur lizard", + 40, TRUE, TRUE, 'm', "0-0", + {ISMEAN, ISLARGE}, + 0, 0, + 10, + {14, 875, 7, 5, HPT("8d8"), "2d6/2d6/3d6"} +}, +{"lammasu", + 0, TRUE, TRUE, 'l', "14-18", + {CANFLY, MEDCAST, CANSPEAK, CANBARGAIN, ISLARGE, HIGHFRIENDLY}, + 0, 0, + 10, + {10, 850, 7, 6, HPT("7d8+7"), "1d6/1d6"} +}, +{"teratosaurus", + 0, TRUE, TRUE, 'T', "0-0", + {ISMEAN, ISFAST, ISLARGE}, + 0, 0, + 14, + {18, 900, 8, 5, HPT("10d8"), "1d3/1d3/3d6"} +}, +{"rhynosphinx", + 0, TRUE, TRUE, 'r', "3-5", + {ISMEAN, ISFAST, CANTRAMPLE, ISLARGE}, + 0, 0, + 12, + {18, 925, 8, 6, HPT("9d8"), "2d10/2d8"} +}, +{"rhinoceros", + 0, TRUE, TRUE, 'R', "3-5", + {ISMEAN, ISFAST, CANTRAMPLE, ISLARGE}, + 0, 0, + 12, + {18, 900, 8, 6, HPT("8d8"), "2d4/2d6"} +}, +{"slyph", + 30, TRUE, TRUE, 'S', "12-18", + {ISMEAN, HALFDAMAGE, MAGICHIT, CANSUMMON, NOBOLT, NOCOLD, CANSEE, CANFLY, ISSHADOW}, + "giant crocodile", 3, + 10, + {10, 800, 9, 7, HPT("6d8+3"), "1d6/1d6/1d6/1d4"} +}, +{"rakshasa", + 20, TRUE, TRUE, 'r', "12-14", + {ISMEAN, MEDCAST, BMAGICHIT}, + 0, 0, + 8, + {13, 925, 8, -4, HPT("7d8"), "1d3/1d3/1d4+1"} +}, +{"wyvern", + 5, TRUE, TRUE, 'w', "6-8", + {ISMEAN, CANSURPRISE, CANSEE, CANFLY, CANPOISON}, + 0, 0, + 10, + {14, 925, 8, 3, HPT("7d8+7"), "1d6/2d8"} +}, +{"mummy", + 20, TRUE, FALSE, 'm', "5-7", + {ISMEAN, CANINFEST, CANSUMMON, MAGICHIT, CANFRIGHTEN, HALFDAMAGE, CANBBURN, ISUNDEAD}, + "ghast", 2, + 8, + {10, 1150, 6, 3, HPT("6d8+3"), "1d12"} +}, +{"basilisk", + 0, TRUE, TRUE, 'B', "1-1", + {ISMEAN, LOOKSTONE}, + 0, 0, + 8, + {10, 1000, 6, 4, HPT("6d8+1"), "1d10"} +}, +{"medusa", + 0, TRUE, FALSE, 'M', "11-14", + {LOOKSTONE, CANPOISON}, + 0, 0, + 10, + {10, 1000, 7, 5, HPT("6d8"), "1d4"} +}, +{"polar bear", + 0, TRUE, TRUE, 'U', "5-8", + {ISMEAN, CANSUMMON, CANHUG, ISLARGE}, + "winter wolf", 2, + 12, + {10, 900, 6, 6, HPT("8d8"), "2d6/2d10"} +}, +{"otyugh", + 0, TRUE, TRUE, 'o', "5-10", + {ISMEAN, CANDISEASE}, + 0, 0, + 8, + {10, 700, 7, 3, HPT("7d8"), "1d8/1d8/1d4+1"} +}, +{"adult dragon", + 30, TRUE, FALSE, 'd', "15-16", + {ISMEAN, CANBRANDOM, ISGREED, CANFRIGHTEN, ISLARGE, MEDFRIENDLY}, + 0, 0, + 9, + {10, 1000, 8, -1, HPT("45d1"), "1d8/1d8/3d10"} +}, +{"invisible stalker", + 0, TRUE, TRUE, 'I', "13-14", + {ISMEAN, ISINVIS, ISLARGE}, + 0, 0, + 10, + {10, 1090, 8, 3, HPT("8d8"), "4d4"} +}, +{"xorn", + 0, TRUE, TRUE, 'X', "8-10", + {ISMEAN, CANINWALL, CANSUMMON, NOCOLD, NOFIRE, CANSURPRISE}, + "vilstrak", 2, + 10, + {10, 1275, 7, -2, HPT("7d8+7"), "1d3/1d3/1d3/4d6"} +}, +{"will-o-wisp", + 100, TRUE, FALSE, 'W', "15-16", + {ISMEAN, BMAGICHIT, CANSURPRISE, ISSMALL}, + 0, 0, + 12, + {10, 1200, 9, -8, HPT("9d8"), "2d8"} +}, +{"chimera", + 0, TRUE, FALSE, 'c', "2-4", + {ISMEAN, CANFLY, NOFIRE, CANBFIRE, ISLARGE}, + 0, 0, + 12, + {10, 1000, 9, 6, HPT("9d8"), "1d3/1d3/1d4/1d4/2d4/3d4"} +}, +{"barrow wight", + 100, TRUE, FALSE, 'W', "14-18", + {ISMEAN, CANPARALYZE, CANSUMMON, CANDRAIN, BMAGICHIT, ISUNDEAD}, + "wight", 2, + 10, + {10, 975, 10, 0, HPT("10d6"), "3d6/1d8"} +}, +{"anatosaurus", + 0, TRUE, FALSE, 'a', "0-0", + {ISFLEE, CANTRAMPLE, ISLARGE}, + 0, 0, + 16, + {18, 1300, 9, 5, HPT("12d8"), "1d4"} +}, +{"wooly rhinoceros", + 0, TRUE, TRUE, 'R', "3-5", + {ISMEAN, ISFAST, CANSUMMON, CANTRAMPLE, ISLARGE}, + "rhinoceros", 2, + 14, + {18, 1350, 9, 5, HPT("10d8"), "2d6"} +}, +{"air squid", + 40, TRUE, TRUE, 'S', "2-8", + {ISMEAN, CANHUG, CANFLY, CANBBURN, ISLARGE, LOWFRIENDLY}, + 0, 0, + 16, + {10, 1400, 9, 7, HPT("12d8"), "1d8/1d8/1d8/1d8/1d8/1d8/1d8/1d8/1d10"} +}, +{"octorilla (snake ape)", + 80, TRUE, TRUE, 'o', "2-8", + {ISMEAN, CANSUMMON, CANHUG, ISFLOCK}, + "carnivorous ape", 2, + 20, + {14, 1500, 6, 6, HPT("6d8"), "1d10/1d10/1d10/1d10/2d6"} +}, +{"cave bear", + 0, TRUE, TRUE, 'U', "5-8", + {ISMEAN, CANSUMMON, CANHUG, ISLARGE}, + "bugbear", 2, + 14, + {10, 850, 6, 6, HPT("6d8+6"), "2d8/1d12"} +}, +{"elasmosaurus", + 0, TRUE, TRUE, 'e', "4-6", + {ISMEAN}, + 0, 0, + 10, + {10, 1700, 4, -4, HPT("4d7"), "4d6"} +}, +{"electric eel", + 0, TRUE, TRUE, 'E', "4-6", + {ISMEAN, CANBBOLT, NOBOLT, NOFIRE, CANSWIM}, + 0, 0, + 10, + {10, 1700, 4, -4, HPT("4d8"), "4d6"} +}, +{"remorhaz", + 0, TRUE, TRUE, 'r', "4-6", + {ISMEAN, CANBFIRE, CANSUMMON, NOFIRE, ISFAST, ISLARGE}, + "winter wolf", 2, + 16, + {18, 1700, 6, 0, HPT("9d8"), "6d6"} +}, +{"airfang", + 0, TRUE, TRUE, 'S', "2-8", + {ISMEAN, CANFLY, ISMANY, ISSMALL}, + 0, 0, + 10, + {10, 1200, 3, -4, HPT("8d6"), "3d4/3d4"} +}, +{"megalosaurus", + 0, TRUE, TRUE, 'M', "0-0", + {ISMEAN, ISFAST, ISLARGE}, + 0, 0, + 16, + {18, 1300, 9, 5, HPT("12d8"), "3d6"} +}, +{"lambeosaurus", + 0, TRUE, FALSE, 'l', "0-0", + {ISFLEE, CANTRAMPLE, ISFLOCK, ISLARGE}, + 0, 0, + 16, + {18, 1300, 9, 5, HPT("12d8"), "2d6"} +}, +{"dragonne", + 15, TRUE, TRUE, 'd', "6-8", + {ISMEAN, CANFLY, CANSHRIEK, ISLARGE}, + 0, 0, + 14, + {13, 1400, 9, 4, HPT("9d8"), "1d8/1d8/3d6"} +}, +{"giant slug", + 0, TRUE, TRUE, 's', "0-0", + {ISMEAN, CANPOISON}, + 0, 0, + 15, + {10, 1500, 9, 8, HPT("10d8"), "1d12"} +}, +{"fire lizard", + 25, TRUE, TRUE, 'F', "2-4", + {NOFIRE, CANBFIRE, ISLARGE}, + 0, 0, + 14, + {13, 1350, 9, 3, HPT("10d8"), "1d8/1d8/2d8"} +}, +{"paleoscincus", + 0, TRUE, TRUE, 'p', "0-0", + {ISMEAN, CANTRAMPLE, ISLARGE}, + 0, 0, + 12, + {18, 1300, 9, -3, HPT("9d8"), "2d6"} +}, +{"bulette", + 0, TRUE, TRUE, 'u', "2-4", + {ISMEAN, CANSURPRISE, ISLARGE}, + 0, 0, + 10, + {10, 1300, 5, -2, HPT("9d8"), "4d12/6d6"} +}, +{"mimic", + 30, TRUE, FALSE, 'M', "2-10", + {ISDISGUISE, CANHOLD, ISLARGE}, + 0, 0, + 12, + {10, 1300, 9, 7, HPT("9d8"), "3d4"} +}, +{"xonoclon", + 30, TRUE, FALSE, 'x', "2-10", + {ISDISGUISE, ISLARGE}, + 0, 0, + 12, + {10, 1300, 15, 7, HPT("9d8"), "3d8"} +}, +{"achaierai", + 0, TRUE, TRUE, 'A', "8-12", + {ISLARGE}, + 0, 0, + 14, + {15, 1300, 7, 8, HPT("0d8+100"), "1d8/1d8/1d10"} +}, +{"succubus (Servant of Errtu)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Errtu)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"horned devil", + 5, TRUE, TRUE, 'H', "13-14", + {ISMEAN, CANFLY, CANFRIGHTEN, CANINFEST, CANPOISON, MAGICHIT, CANSUMMON, ISLARGE, ISUNDEAD}, + "imp", 3, + 6, + {10, 1320, 7, -3, HPT("5d8+5"), "1d4/1d4/1d4+1/1d3"} +}, +{"pentacerotops", + 0, TRUE, TRUE, 'P', "0-0", + {ISMEAN, CANTRAMPLE, ISLARGE}, + 0, 0, + 18, + {18, 1300, 9, 4, HPT("12d8"), "1d6/1d10/1d10"} +}, +{"spectre", + 0, TRUE, TRUE, 'S', "13-14", + {ISMEAN, CANSUMMON, DOUBLEDRAIN, ISUNDEAD, ISSHADOW}, + "shadow", 2, + 10, + {10, 1650, 7, 2, HPT("7d8+3"), "1d8"} +}, +{"neotyugh", + 0, TRUE, TRUE, 'n', "10-12", + {ISMEAN, CANDISEASE, ISLARGE}, + 0, 0, + 10, + {12, 1500, 10, 0, HPT("12d8"), "1d8/1d8/2d3"} +}, +{"intellect devourer", + 0, TRUE, TRUE, 'r', "11-13", + {ISMEAN, DRAINBRAIN, CANSURPRISE, ISFAST}, + 0, 0, + 8, + {14, 1510, 10, 4, HPT("6d8+6"), "1d4/1d4/1d4/1d4"} +}, +{"heffalump", + 0, TRUE, TRUE, 'h', "4-6", + {ISMEAN, CANTRAMPLE, NOBOLT, NOFIRE, CANHUG, ISFLOCK, ISLARGE}, + 0, 0, + 14, + {18, 1500, 9, 1, HPT("10d8"), "3d10/3d10/2d8"} +}, +{"elephant", + 0, TRUE, TRUE, 'e', "4-6", + {ISMEAN, CANTRAMPLE, CANHUG, ISFLOCK, ISLARGE}, + 0, 0, + 14, + {18, 1500, 9, 6, HPT("10d8"), "2d6/2d6/2d6/2d6/2d6"} +}, +{"succubus (Servant of Ndulu)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Ndulu)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"barbed devil", + 0, TRUE, TRUE, 'B', "11-12", + {ISMEAN, CANFLY, TOUCHFEAR, CANSUMMON, CANHOLD, ISUNDEAD}, + "quasit", 3, + 10, + {10, 1425, 8, 0, HPT("8d8"), "2d4/2d4/3d4"} +}, +{"vrock", + 10, TRUE, TRUE, 'v', "5-7", + {ISMEAN, CANSUMMON, CANSEE, ISLARGE}, + "vilstrak", 2, + 10, + {10, 1500, 8, 0, HPT("8d8"), "1d4/1d6"} +}, +{"lamia", + 20, TRUE, TRUE, 'L', "11-14", + {CANFLY, MEDCAST, DRAINWISDOM, CANSPEAK, CANBARGAIN}, + 0, 0, + 12, + {12, 1700, 8, 3, HPT("9d8"), "1d4"} +}, +{"shambling mound", + 25, TRUE, TRUE, 's', "5-7", + {ISSLOW, ISMEAN, CANSUFFOCATE, NOCOLD, NOFIRE, CANHOLD, ISLARGE}, + 0, 0, + 10, + {10, 1800, 9, 0, HPT("9d8"), "2d8/2d8"} +}, +{"succubus (Servant of Bilwhr)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Bilwhr)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"mind flayer", + 25, TRUE, TRUE, 'M', "15-17", + {ISMEAN, DRAINBRAIN}, + 0, 0, + 12, + {10, 1800, 9, 5, HPT("8d8+4"), "2d2/2d2"} +}, +{"gorgon", + 5, TRUE, TRUE, 'g', "3-5", + {ISMEAN, CANTRAMPLE, NOFIRE, CANBFIRE, ISLARGE}, + 0, 0, + 10, + {14, 1750, 9, 2, HPT("8d8"), "2d6"} +}, +{"night hag", + 5, TRUE, TRUE, 'n', "13-15", + {ISMEAN, CANSNORE, CANSUMMON, ISINVIS, BMAGICHIT, NOFIRE, NOCOLD, ISUNDEAD}, + "nightmare", 2, + 12, + {14, 1750, 9, 9, HPT("8d8"), "2d6"} +}, +{"umber hulk", + 40, TRUE, TRUE, 'U', "8-10", + {ISSLOW, ISMEAN, CANHUH, ISLARGE}, + 0, 0, + 12, + {10, 1700, 8, 2, HPT("8d8+8"), "3d4/3d4/2d5"} +}, +{"old dragon", + 30, TRUE, FALSE, 'd', "15-16", + {ISMEAN, CANBRANDOM, CANSUMMON, ISGREED, CANFRIGHTEN, ISLARGE, LOWFRIENDLY}, + "very young dragon", 1, + 11, + {10, 1800, 8, 2, HPT("55d1"), "1d10/1d10/3d12"} +}, +{"succubus (Servant of Nalfeshnee)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Nalfeshnee)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"gorgosaurus", + 0, TRUE, TRUE, 'G', "0-0", + {ISMEAN, ISFAST, ISLARGE}, + 0, 0, + 18, + {18, 1800, 9, 5, HPT("13d8"), "1d3/1d3/7d4"} +}, +{"efreeti", + 10, TRUE, TRUE, 'E', "0-0", + {NOFIRE, ISMEAN, CANBFIRE, CANFLY, CANSPEAK, ISLARGE}, + 0, 0, + 14, + {18, 1950, 9, 2, HPT("10d8"), "3d8"} +}, +{"succubus (Servant of Johud)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Johud)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"treant", + 0, TRUE, TRUE, 'T', "10-13", + {CANBBURN, CANSUMMON, CANSPEAK, ISLARGE, LOWFRIENDLY}, + "ent", 3, + 14, + {18, 1950, 9, 0, HPT("10d8"), "2d8/3d6/4d6"} +}, +{"giant toad", + 0, TRUE, TRUE, 't', "1-4", + {ISMEAN, CANPOISON, ISLARGE}, + 0, 0, + 14, + {10, 3950, 15, 6, HPT("15d8"), "15d4"} +}, +{"ettin", + 0, TRUE, TRUE, 'e', "0-0", + {ISMEAN, CANSHOOT, CANWIELD, ISLARGE}, + 0, 0, + 14, + {10, 1950, 10, 3, HPT("10d8"), "2d8/3d6"} +}, +{"succubus (Servant of Alzoll)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Alzoll)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"hero-mage (Keoghtom)", + 100, TRUE, TRUE, 'k', "19", + {CANCAST, ISUNIQUE, CANBARGAIN, CANWIELD, CANSHOOT, CANSUMMON, ISFAST, HIGHFRIENDLY}, + "lesser god (Celestian the Far Wanderer)", 1, + 45, + {12, 42000L, 25, -8, HPT("0d8+77"), "3d4"} +}, +{"arch-mage (Heward)", + 100, TRUE, TRUE, 'H', "20", + {CANCAST, ISUNIQUE, CANBARGAIN, CANWIELD, CANSHOOT, CANSUMMON, ISFAST, HIGHFRIENDLY}, + "arch-mage (Mordenkainen)", 1, + 50, + {18, 50000L, 25, -3, HPT("0d8+96"), "3d4"} +}, +{"arch-mage (Mordenkainen)", + 100, TRUE, TRUE, 'M', "20", + {CANCAST, ISUNIQUE, CANBARGAIN, CANWIELD, CANSHOOT, CANSUMMON, ISFAST, HIGHFRIENDLY}, + "hero-mage (Murlynd)", 1, + 50, + {18, 50000L, 25, -3, HPT("0d8+96"), "3d4"} +}, +{"hero-mage (Murlynd)", + 100, TRUE, TRUE, 'm', "20", + {CANCAST, ISUNIQUE, CANBARGAIN, CANWIELD, CANSHOOT, CANSUMMON, ISFAST, HIGHFRIENDLY}, + "hero-mage (Keoghtom)", 1, + 50, + {19, 56000L, 25, -2, HPT("0d8+135"), "3d4"} +}, +{"hero (Kelanen, Prince of Swords)", + 100, TRUE, TRUE, 'k', "17", + {ISUNIQUE, CANBARGAIN, CANWIELD, CANSHOOT, CANSUMMON, ISFAST, HIGHFRIENDLY}, + "lesser goddess (Xan Yae, Lady of Perfection)", 1, + 50, + {19, 53000L, 25, -5, HPT("0d8+159"), "3d4"} +}, +{"styracosaurus", + 0, TRUE, TRUE, 'S', "0-0", + {ISMEAN, CANTRAMPLE, ISLARGE}, + 0, 0, + 14, + {18, 1950, 9, 3, HPT("10d8"), "2d8"} +}, +{"shedu", + 0, TRUE, TRUE, 's', "13-18", + {CANFLY, MEDCAST, CANBARGAIN, ISFRIENDLY}, + 0, 0, + 14, + {13, 1950, 9, 4, HPT("9d8+9"), "1d6/1d6"} +}, +{"black pudding", + 70, TRUE, FALSE, 'P', "0-0", + {ISSLOW, ISMEAN, CANRUST, NOCOLD, BOLTDIVIDE, BLOWDIVIDE, ISSCAVENGE}, + 0, 0, + 14, + {10, 2000, 10, 6, HPT("10d8"), "3d8"} +}, +{"mastadon", + 0, TRUE, TRUE, 'm', "4-6", + {ISMEAN, CANTRAMPLE, CANHUG, ISFLOCK, ISLARGE}, + 0, 0, + 16, + {19, 2000, 9, 6, HPT("12d8"), "2d8/2d8/2d6/2d6/2d6"} +}, +{"succubus (Servant of Aishapra)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Aishapra)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"giant scorpion", + 30, TRUE, FALSE, 'S', "0-0", + {ISFAST, CANPOISON, ISLARGE}, + 0, 0, + 10, + {12, 2000, 8, 4, HPT("8d8"), "1d10/1d10/1d4"} +}, +{"genii", + 30, TRUE, TRUE, 'g', "12-18", + {HIGHCAST, CANSUMMON, ISFAST, CANSPEAK, MAGICHIT, ISLARGE, LOWFRIENDLY}, + "djinni", 2, + 10, + {12, 3000, 7, 3, HPT("7d8+3"), "2d6/2d6"} +}, +{"mammoth", + 0, TRUE, TRUE, 'M', "4-6", + {ISMEAN, CANTRAMPLE, CANHUG, ISFLOCK, ISLARGE}, + 0, 0, + 18, + {20, 3000, 8, 5, HPT("13d8"), "3d6/3d6/2d8/2d6/2d6"} +}, +{"gelatinous blue horror", + 100, TRUE, TRUE, 'g', "0-0", + {ISMEAN, CANBACID, NOSHARP, HALFDAMAGE, NOBOLT, ISLARGE}, + 0, 0, + 55, + {10, 7000, 10, 6, HPT("10d8"), "1d5+4"} +}, +{"stone gargoyle", + 25, TRUE, TRUE, 'g', "6-12", + {ISMEAN, CANSUMMON, MAGICHIT, NOBOLT, NOCOLD, NOFIRE}, + "gargoyle", 3, + 10, + {18, 2500, 7, 1, HPT("4d8+4"), "1d6/1d6/2d6/2d4"} +}, +{"succubus (Servant of Kevokulli)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Kevokulli)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"koppleganger", + 10, TRUE, TRUE, 'k', "5-8", + {ISMEAN, CANSURPRISE, CANSEE}, + 0, 0, + 10, + {7, 2000, 15, 2, HPT("4d4"), "1d12"} +}, +{"kittyhawk", + 10, TRUE, TRUE, 'K', "5-8", + {ISMEAN, CANFLY, CANSURPRISE, ISSMALL, ISMANY, CANSEE}, + 0, 0, + 10, + {7, 2000, 15, 2, HPT("4d4"), "1d2/1d2/1d2/1d3/1d3/1d3"} +}, +{"rock hound", + 0, TRUE, TRUE, 'R', "5-8", + {ISMEAN, CANINWALL, CANSUMMON, TOUCHSTONE, CANSURPRISE, CANSEE}, + "vilstrak", 1, + 20, + {10, 2250, 5, 3, HPT("7d8"), "3d10"} +}, +{"succubus (Servant of Balor)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Balor)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"kodiac bear", + 0, TRUE, TRUE, 'U', "5-8", + {ISMEAN, CANSUMMON, CANHUG, ISLARGE}, + "cave bear", 2, + 20, + {18, 2000, 10, -1, HPT("12d8"), "3d8/2d6/2d6"} +}, +{"very old dragon", + 30, TRUE, FALSE, 'd', "15-16", + {ISMEAN, CANBRANDOM, CANSUMMON, ISGREED, CANFRIGHTEN, ISLARGE, LOWFRIENDLY}, + "young dragon", 1, + 11, + {10, 2000, 8, 2, HPT("55d1"), "1d10/1d10/3d12"} +}, +{"hezrou", + 15, TRUE, TRUE, 'h', "5-7", + {ISMEAN, CANFRIGHTEN, CANSEE, CANSUMMON, ISUNDEAD, ISLARGE}, + "horned devil", 2, + 12, + {10, 2000, 9, -2, HPT("9d8"), "1d3/1d3/4d4"} +}, +{"loxodant", + 0, TRUE, TRUE, 'L', "4-6", + {ISMEAN, CANTRAMPLE, CANHUG, ISFAST}, + 0, 0, + 16, + {18, 2150, 9, 6, HPT("11d8"), "2d8/2d8/2d6/2d6/2d6"} +}, +{"flesh golem", + 0, TRUE, TRUE, 'f', "0-0", + {ISMEAN, MAGICHIT, ISLARGE}, + 0, 0, + 0, + {18, 2380, 9, 9, HPT("0d0+40"), "2d8/2d8"} +}, +{"glabrezu", + 25, TRUE, FALSE, 'G', "8-10", + {ISMEAN, CANFRIGHTEN, CANSEE, CANSUMMON, ISUNDEAD, ISLARGE}, + "barbed devil", 2, + 14, + {10, 2400, 10, -4, HPT("10d8"), "2d6/2d6/1d3/1d3/1d4+1"} +}, +{"succubus (Servant of Ter-soth)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Ter-soth)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"allosaurus", + 0, TRUE, TRUE, 'a', "0-0", + {ISMEAN, CANTRAMPLE, ISFAST, ISLARGE}, + 0, 0, + 20, + {18, 2400, 10, 5, HPT("15d8"), "1d4/1d4/6d4"} +}, +{"couatl", + 15, TRUE, FALSE, 'c', "15-18", + {ISMEAN, HIGHCAST, CANHUG, CANPOISON, HIGHFRIENDLY}, + 0, 0, + 12, + {16, 2400, 10, 5, HPT("9d8"), "2d4/1d3"} +}, +{"succubus (Servant of Rehnaremme)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Rehnaremme)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"banshee", + 0, TRUE, FALSE, 'B', "15-20", + {ISSHADOW, CANSUMMON, CANSHRIEK, CANSURPRISE, CANFRIGHTEN, NOCOLD, NOBOLT, MAGICHIT, ISUNDEAD}, + "shrieker", 10, + 10, + {10, 2450, 10, 0, HPT("7d8"), "1d8"} +}, +{"spirit naga", + 25, TRUE, FALSE, 's', "13-16", + {ISMEAN, CANPOISON, CANSNORE, HIGHCAST, ISUNDEAD}, + 0, 0, + 14, + {10, 2700, 10, 4, HPT("9d8"), "1d3"} +}, +{"dire wolf", + 10, TRUE, TRUE, 'w', "3-9", + {ISMEAN, CANSUMMON, ISLARGE}, + "warg", 3, + 5, + {10, 1440, 8, 0, HPT("8d8"), "1d8"} +}, +{"succubus (Servant of Wendonai)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Wendonai)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"roper", + 80, TRUE, FALSE, 'r', "13-16", + {ISMEAN, ISSLOW, CANHUG, CANHOLD, NOBOLT, CANBBURN, ISLARGE}, + 0, 0, + 16, + {19, 2750, 10, 0, HPT("11d8"), "5d4"} +}, +{"bone devil", + 0, TRUE, TRUE, 'd', "11-12", + {ISMEAN, CANFLY, CANFRIGHTEN, CANSEE, CANSUMMON, CANSURPRISE, CANCHILL, ISUNDEAD, ISLARGE}, + "hezrou", 2, + 12, + {10, 2800, 9, -1, HPT("9d8"), "5d4"} +}, +{"air elemental", + 0, TRUE, TRUE, 'a', "3-5", + {ISMEAN, CANFLY, ISSHADOW, BMAGICHIT, ISLARGE}, + 0, 0, + 15, + {18, 2850, 9, 2, HPT("12d8"), "2d10"} +}, +{"earth elemental", + 0, TRUE, TRUE, 'e', "3-5", + {ISMEAN, CANINWALL, CANSURPRISE, ISSLOW, BMAGICHIT, ISLARGE}, + 0, 0, + 15, + {18, 2850, 9, 2, HPT("12d8"), "4d8"} +}, +{"fire elemental", + 0, TRUE, TRUE, 'f', "3-5", + {ISMEAN, NOFIRE, CANBFIRE, BMAGICHIT, ISLARGE}, + 0, 0, + 15, + {18, 2850, 9, 2, HPT("12d8"), "3d8"} +}, +{"cold elemental", + 0, TRUE, TRUE, 'c', "3-5", + {ISMEAN, NOCOLD, CANBICE, BMAGICHIT, ISLARGE}, + 0, 0, + 15, + {18, 2850, 9, 2, HPT("12d8"), "3d8"} +}, +{"succubus (Servant of Marilith)", + 10, TRUE, TRUE, 's', "15-18", + {ISMEAN, HIGHCAST, CANSUMMON, BMAGICHIT, CANDARKEN, CANBARGAIN}, + "lesser demon (Marilith)", 1, + 6, + {11, 2100, 13, 0, HPT("6d8"), "1d3/1d3"} +}, +{"tarry demodand (farastu)", + 0, TRUE, TRUE, 'T', "18", + {ISMEAN, MAGICHIT, CANSTICK}, + 0, 0, + 16, + {19, 2850, 9, -1, HPT("11d8"), "1d6+1/1d6+1/3d4"} +}, +{"wood elemental", + 0, TRUE, TRUE, 'w', "3-5", + {ISMEAN, CANBBURN, CANSUMMON, BMAGICHIT, ISLARGE}, + "entwife", 1, + 15, + {18, 2850, 9, 2, HPT("12d8"), "3d8"} +}, +{"greater basilisk", + 70, TRUE, FALSE, 'B', "5-8", + {ISGREED, CANSUMMON, LOOKSTONE, CANPOISON, LOOKSTONE}, + "basilisk", 2, + 16, + {10, 3000, 7, 2, HPT("10d8"), "2d6/2d8"} +}, +{"lesser demon (Bilwhr)", + 0, TRUE, TRUE, 'b', "12-14", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "hezrou", 3, + 16, + {14, 3000, 8, -1, HPT("11d8"), "1d4/1d4/2d4"} +}, +{"lesser demon (Johud)", + 0, TRUE, TRUE, 'j', "12-14", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "hezrou", 3, + 16, + {14, 3000, 8, -1, HPT("11d8"), "1d4/1d4/2d4"} +}, +{"lesser demon (Nalfeshnee)", + 0, TRUE, TRUE, 'b', "12-14", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "hezrou", 3, + 16, + {14, 3000, 8, -1, HPT("11d8"), "1d4/1d4/2d4"} +}, +{"stegosaurus", + 0, TRUE, TRUE, 'S', "0-0", + {ISMEAN, ISLARGE}, + 0, 0, + 25, + {18, 3000, 9, 3, HPT("18d8"), "5d4"} +}, +{"lesser demon (Aishapra)", + 0, TRUE, TRUE, 'a', "12-14", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANSEE, BMAGICHIT, CANSUMMON, ISLARGE}, + "hezrou", 3, + 12, + {12, 3000, 8, -7, HPT("7d8+7"), "2d4"} +}, +{"lesser demon (Kevokulli)", + 0, TRUE, TRUE, 'k', "12-14", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANSEE, BMAGICHIT, CANSUMMON, ISLARGE}, + "hezrou", 3, + 12, + {12, 3000, 8, -7, HPT("7d8+7"), "2d4"} +}, +{"lesser demon (Marilith)", + 0, TRUE, TRUE, 'm', "12-14", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANSEE, BMAGICHIT, CANSUMMON, ISLARGE}, + "hezrou", 3, + 12, + {12, 3000, 8, -7, HPT("7d8+7"), "2d4"} +}, +{"lesser demon (Rehnaremme)", + 0, TRUE, TRUE, 'r', "12-14", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANSEE, BMAGICHIT, CANSUMMON, ISLARGE}, + "hezrou", 3, + 12, + {12, 3000, 8, -7, HPT("7d8+7"), "2d4"} +}, +{"guardian naga", + 100, TRUE, FALSE, 'G', "16-18", + {CANPOISON, NOMOVE, HIGHCAST, HIGHFRIENDLY}, + 0, 0, + 16, + {10, 3550, 8, 3, HPT("11d8+4"), "1d6/2d4"} +}, +{"lesser demon (Alzoll)", + 0, TRUE, TRUE, 'A', "14-16", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "glabrezu", 3, + 12, + {16, 3600, 8, -2, HPT("8d8+8"), "1d12+1"} +}, +{"clay golem", + 0, TRUE, TRUE, 'C', "0-0", + {NOSHARP, ISMEAN, ISLARGE}, + 0, 0, + 0, + {18, 3600, 8, 7, HPT("0d0+50"), "3d10"} +}, +{"shade", + 0, TRUE, TRUE, 's', "15-20", + {ISSHADOW, CANSUMMON, ISUNDEAD, ISMEAN, CANDRAIN, MAGICHIT}, + "night hag", 2, + 15, + {10, 4250, 4, 4, HPT("4d10"), "1d6"} +}, +{"lesser demon (Balor)", + 0, TRUE, TRUE, 'B', "14-16", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "glabrezu", 3, + 12, + {16, 3600, 8, -2, HPT("8d8+8"), "1d12+1"} +}, +{"lesser demon (Errtu)", + 0, TRUE, TRUE, 'E', "14-16", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "glabrezu", 3, + 12, + {16, 3600, 8, -2, HPT("8d8+8"), "1d12+1"} +}, +{"wyvergon", + 5, TRUE, TRUE, 'w', "3-10", + {ISMEAN, CANPOISON, LOOKSTONE, NOFIRE, ISLARGE}, + 0, 0, + 10, + {14, 1750, 9, 2, HPT("8d8+1"), "2d8/2d6/1d6"} +}, +{"lesser demon (Ndulu)", + 0, TRUE, TRUE, 'N', "14-16", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "glabrezu", 3, + 12, + {16, 3600, 8, -2, HPT("8d8+8"), "1d12+1"} +}, +{"lesser demon (Ter-soth)", + 0, TRUE, TRUE, 'T', "14-16", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "glabrezu", 3, + 12, + {16, 3600, 8, -2, HPT("8d8+8"), "1d12+1"} +}, +{"lesser demon (Wendonai)", + 0, TRUE, TRUE, 'T', "14-16", + {ISUNIQUE, CANFLY, ISMEAN, CANWIELD, CANCAST, CANFRIGHTEN, BMAGICHIT, CANSUMMON, CANDARKEN, ISLARGE}, + "glabrezu", 3, + 12, + {16, 3600, 8, -2, HPT("8d8+8"), "1d12+1"} +}, +{"lesser demon (Cerberus)", /* Three heads - fire, clorine, acid */ + 0, TRUE, TRUE, 'T', "14-16", + {ISUNIQUE, ISMEAN, MAGICHIT, CANSUMMON, CANBPGAS, CANBACID, CANBFIRE, CANSEE, NOFIRE, ISLARGE, HASOXYGEN}, + "hell hound", 6, + 15, + {16, 3600, 9, 1, HPT("8d8+8"), "2d4/2d4/2d4"} +}, +{"slime demodand (kelubar)", + 0, TRUE, TRUE, 'S', "14-20", + {MAGICHIT, CANPOISON, CANWIELD, CANBARGAIN}, + 0, 0, + 18, + {20, 3959, 9, -2, HPT("13d8"), "2d4/2d4/4d4"} +}, +{"nazgul (Ringwraith)", + 0, TRUE, TRUE, 'z', "14-16", + {ISMEAN, CANPOISON, ISSHADOW, CANRUST, CANHUH, CANFRIGHTEN, CANDRAIN, MAGICHIT, ISFLOCK, CANSUMMON}, + "dire wolf", 2, + 15, + {16, 4000, 10, 0, HPT("8d8+8"), "1d10"} +}, +{"vampire", + 20, TRUE, TRUE, 'V', "15-16", + {ISMEAN, CANFLY, CANSUMMON, ISREGEN, CANSUCK, ISUNDEAD}, + "barrow wight", 2, + 16, + {20, 3800, 8, 1, HPT("8d8+3"), "1d10"} +}, +{"camarasaurus", + 0, TRUE, FALSE, 'c', "0-0", + {ISFLEE, CANTRAMPLE, ISLARGE}, + 0, 0, + 30, + {24, 4000, 9, 6, HPT("20d8"), "3d4"} +}, +{"triceratops", + 0, TRUE, TRUE, 'T', "0-0", + {ISMEAN, CANTRAMPLE, ISLARGE}, + 0, 0, + 20, + {18, 4000, 9, 3, HPT("16d8"), "1d8/1d12/1d12"} +}, +{"ghost", + 0, TRUE, TRUE, 'g', "12-14", + {ISSHADOW, CANSUMMON, ISMEAN, CANFRIGHTEN, MAGICHIT, ISUNDEAD}, + "spectre", 2, + 14, + {10, 4050, 9, 0, HPT("10d8"), "1d4"} +}, +{"baluchitherium", + 0, TRUE, TRUE, 'B', "3-5", + {ISMEAN, CANTRAMPLE, ISLARGE}, + 0, 0, + 18, + {18, 4200, 9, 5, HPT("14d8"), "4d5"} +}, +{"nazgul (Witch-King of Angmar)", + 0, TRUE, FALSE, 'z', "19", + {ISUNIQUE, ISMEAN, CANINFEST, ISSHADOW, CANRUST, CANHUH, CANFRIGHTEN, DOUBLEDRAIN, BMAGICHIT, CANCAST, CANBARGAIN, CANSUMMON}, + "nazgul (Ringwraith)", 8, + 30, + {18, 50000L, 30, -2, HPT("0d8+100"), "2d10"} +}, +{"maiar (Melian, wife of Thingol)", + 0, TRUE, FALSE, 'M', "19", + {ISUNIQUE, BMAGICHIT, CANSEE, CANSUMMON, MEDFRIENDLY}, + "noldor elf", 10, + 30, + {10, 55000L, 45, 4, HPT("0d8+60"), "1d10"} +}, +{"maiar (Eonwe, Herald of Manwe)", + 0, TRUE, FALSE, 'E', "19", + {ISUNIQUE, BMAGICHIT, CANSEE, CANSUMMON, MEDFRIENDLY}, + "valar (Manwe, Lord of Arda)", 1, + 0, + {20, 55000L, 45, 4, HPT("0d8+120"), "2d10"} +}, +{"maiar (Sauron, Lord of the Rings)", + 0, TRUE, FALSE, 'S', "19", + {ISUNIQUE, ISMEAN, ISSHADOW, BMAGICHIT, CANSEE, CANSUMMON}, + "nazgul (Witch-King of Angmar)", 1, + 0, + {20, 55000L, 35, -1, HPT("0d8+120"), "2d10"} +}, +{"maiar (Irmo, Master of Spirit)", + 0, TRUE, FALSE, 'I', "19", + {ISUNIQUE, ISMEAN, BMAGICHIT, CANSEE, CANSUMMON, MEDFRIENDLY}, + "spectre", 3, + 0, + {20, 55000L, 35, 5, HPT("0d8+120"), "2d10"} +}, +{"maiar (Namo, Master of Spirit)", + 0, TRUE, FALSE, 'N', "19", + {ISUNIQUE, ISMEAN, BMAGICHIT, CANSEE, CANSUMMON, MEDFRIENDLY}, + "ghost", 3, + 0, + {20, 55000L, 35, 5, HPT("0d8+120"), "2d10"} +}, +{"maiar (Uinen, Lady of the Seas)", + 0, TRUE, FALSE, 'U', "19", + {ISUNIQUE, BMAGICHIT, CANSEE, CANSUMMON, MEDFRIENDLY}, + "valar (Ulmo, God of Waters)", 1, + 0, + {10, 55000L, 35, 5, HPT("0d8+120"), "2d10"} +}, +{"maiar (Ilmare, Handmaiden of Varda)", + 0, TRUE, FALSE, 'I', "19", + {ISUNIQUE, BMAGICHIT, CANSEE, CANSUMMON, MEDFRIENDLY}, + "valar (Varda, Elbereth Gilthoniel)", 1, + 0, + {10, 55000L, 35, 5, HPT("0d8+60"), "1d10"} +}, +{"maiar (Balrog)", + 100, TRUE, TRUE, 'B', "20", + {ISMEAN, BMAGICHIT, CANWIELD, CANCAST, HASFIRE, ISUNDEAD}, + "pit fiend", 3, + 50, + {22, 39000L, 20, 0, HPT("16d8"), "4d8"} +}, +{"ice devil", + 30, TRUE, FALSE, 'I', "13-14", + {CANFLY, ISMEAN, CANSEE, ISREGEN, CANFRIGHTEN, CANSUMMON, CANBICE, NOCOLD, ISLARGE, ISUNDEAD}, + "glabrezu", 2, + 16, + {20, 4400, 11, -4, HPT("11d8"), "1d4/1d4/2d4/3d4"} +}, +{"purple worm", + 70, TRUE, FALSE, 'P', "0-0", + {ISMEAN, ISLARGE}, + 0, 0, + 20, + {10, 4900, 15, 6, HPT("15d8"), "2d12/2d4"} +}, +{"diplodocus", + 0, TRUE, TRUE, 'd', "0-0", + {CANTRAMPLE, ISLARGE}, + 0, 0, + 35, + {23, 5000, 15, 5, HPT("24d8"), "3d6"} +}, +{"brontosaurus", + 0, TRUE, TRUE, 'b', "0-0", + {CANTRAMPLE, ISLARGE}, + 0, 0, + 35, + {23, 5000, 15, 5, HPT("30d8"), "3d6"} +}, +{"roc", + 0, TRUE, TRUE, 'r', "3-5", + {ISMEAN, CANFLY, ISLARGE}, + 0, 0, + 25, + {23, 5000, 15, 4, HPT("18d8"), "3d6/3d6/4d6"} +}, +{"silver cloud", + 100, TRUE, TRUE, 'c', "0-0", + {CANFLY, HALFDAMAGE, CANBBOLT, NOBOLT, ISLARGE}, + 0, 0, + 10, + {10, 4000, 13, 9, HPT("6d8"), "1d4"} +}, +{"coachman of death", + 100, TRUE, TRUE, 'C', "12-22", + {ISMEAN, ISUNDEAD, CANFRIGHTEN, MAGICHIT }, + 0, 0, + 50, + {10, 4000, 10, -2, HPT("10d8"), "1d8+2"} +}, +{"blue pool horror", + 100, TRUE, FALSE, 'b', "0-0", + {ISMEAN, CANBACID, NOMETAL, NOSHARP, HALFDAMAGE, NOBOLT, ISLARGE}, + 0, 0, + 35, + {10, 5000, 8, 8, HPT("8d8"), "1d5+4"} +}, +{"cetiosaurus", + 0, TRUE, TRUE, 'c', "0-0", + {CANTRAMPLE, ISLARGE}, + 0, 0, + 35, + {23, 5000, 15, 6, HPT("24d8"), "3d6"} +}, +{"brachiosaurus", + 0, TRUE, TRUE, 'B', "0-0", + {CANTRAMPLE, ISLARGE}, + 0, 0, + 35, + {23, 5000, 15, 5, HPT("36d8"), "5d4"} +}, +{"shaggy demodand (shator)", + 0, TRUE, TRUE, 's', "14-25", + {CANSUMMON, BMAGICHIT, LOWCAST, CANWIELD, CANSEE, CANSTINK}, + "tarry demodand (farastu)", 8, + 20, + {21, 5250, 13, -3, HPT("15d8"), "1d8+1/1d8+1/5d4"} +}, +{"tyranosaurus rex", + 0, TRUE, TRUE, 'T', "0-0", + {ISMEAN, ISFAST, ISLARGE}, + 0, 0, + 25, + {18, 6550, 12, 5, HPT("18d8"), "1d6/1d6/5d8"} +}, +{"pit fiend", + 100, TRUE, TRUE, 'f', "15-16", + {ISMEAN, CANSEE, BMAGICHIT, CANFRIGHTEN, CANHOLD, CANSUMMON, CANBFIRE, NOFIRE, ISLARGE, ISUNDEAD}, + "erinyes", 6, + 18, + {22, 7900, 13, -3, HPT("13d8"), "1d4+4/1d6+6"} +}, +{"hound (Nemoud, Servant of Trithereon)", + 0, TRUE, TRUE, 'N', "5", + {ISFAST, MAGICHIT, ISUNIQUE, ISLARGE, ISFRIENDLY}, + "lesser god (Trithereon the Summoner)", 1, + 16, + {18, 8100, 13, 0, HPT("0d8+64"), "4d4"} +}, +{"ki-rin", + 25, TRUE, TRUE, 'k', "16-22", + {CANFLY, MEDCAST, CANSPEAK, DRAINBRAIN, CANBARGAIN, ISLARGE, ISFRIENDLY}, + 0, 0, + 16, + {13, 8500, 13, -5, HPT("12d8"), "2d4/2d4/3d6"} +}, +{"stone golem", + 0, TRUE, TRUE, 'S', "0-0", + {ISMEAN, CANBSGAS, BMAGICHIT, ISLARGE, HASOXYGEN}, + 0, 0, + 0, + {22, 8950, 13, 5, HPT("0d0+60"), "3d8"} +}, +{"titanothere", + 100, TRUE, FALSE, 't', "17-20", + {ISLARGE}, + 0, 0, + 30, + {10, 9000, 19, 0, HPT("9d8"), "2d8/1d6"} +}, +{"titan", + 100, TRUE, FALSE, 't', "17-20", + {ISSHADOW, ISLARGE, LOWFRIENDLY}, + 0, 0, + 30, + {10, 9000, 19, 0, HPT("19d8"), "7d6"} +}, +{"diamond golem", + 0, TRUE, TRUE, 'D', "0-0", + {ISMEAN, NOBOLT, CANBLIND, BMAGICHIT, ISLARGE}, + 0, 0, + 0, + {22, 9500, 13, -2, HPT("0d0+80"), "3d8"} +}, +{"lich", + 100, TRUE, TRUE, 'l', "19-20", + {ISMEAN, CANSUMMON, CANPARALYZE, CANFRIGHTEN, MAGICHIT, ISUNDEAD, NOBOLT, CANCAST}, + "vampire", 4, + 16, + {10, 10500, 11, 0, HPT("11d8"), "1d10"} +}, +{"falcon (Harrus, Servant of Trithereon)", + 0, TRUE, TRUE, 'H', "6", + {CANFLY, MAGICHIT, ISUNIQUE, ISLARGE, ISFRIENDLY}, + "lesser god (Trithereon the Summoner)", 1, + 16, + {18, 11000, 12, 2, HPT("0d8+72"), "1d4+4/1d4+4"} +}, +{"beholder", + 0, TRUE, FALSE, 'b', "14-16", + {ISMEAN, ISSLOW, LOOKSTONE, CANFRIGHTEN, LOOKSLOW, CANSNORE, ISLARGE}, + 0, 0, + 20, + {6, 12900, 18, 1, HPT("5d8+35"), "2d4"} +}, +{"hill giant", + 30, TRUE, TRUE, 'h', "6-8", + {ISMEAN, CANSUMMON, ISLARGE}, + "ogre", 4, + 30, + {25, 13000, 10, 4, HPT("8d8+2"), "2d8"} +}, +{"cyclops", + 50, TRUE, TRUE, 'c', "9-18", + {ISMEAN, LOWCAST, CANWIELD, CANSUMMON, ISLARGE}, + "cave bear", 3, + 45, + {28, 15000, 14, 3, HPT("12d8"), "2d8"} +}, +{"stone giant", + 50, TRUE, TRUE, 's', "9-13", + {ISMEAN, CANSURPRISE, CANSUMMON, ISLARGE, LOWFRIENDLY}, + "werebear", 3, + 35, + {28, 14000, 13, 4, HPT("9d8+3"), "2d8"} +}, +{"iron golem", + 0, TRUE, TRUE, 'i', "0-0", + {ISMEAN, BMAGICHIT, CANBPGAS, ISLARGE, HASOXYGEN}, + 0, 0, + 0, + {25, 14550, 13, 3, HPT("0d0+80"), "4d10"} +}, +{"ancient brass dragon", + 100, TRUE, FALSE, 'r', "13-14", + {CANBSGAS, CANBFGAS, ISGREED, CANBARGAIN, CANFLY, ISLARGE, LOWFRIENDLY, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 13, 2, HPT("0d8+64"), "1d4/1d4/4d4"} +}, +{"ancient chrome dragon", + 100, TRUE, TRUE, 'C', "8-20", + {CANCAST, CANBPGAS, CANBARGAIN, NOBOLT, NOCOLD, NOFIRE, CANFLY, ISLARGE, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 13, 0, HPT("0d8+75"), "4d8/1d6/1d6"} +}, +{"ancient crystal dragon", + 100, TRUE, TRUE, 'C', "6-12", + {ISMEAN, ISGREED, CANCAST, CANBBOLT, NOBOLT, CANFLY, ISLARGE}, + 0, 0, + 50, + {10, 20000, 13, 0, HPT("0d8+75"), "4d8/1d6/1d6"} +}, +{"ancient white dragon", + 100, TRUE, TRUE, 'W', "8-9", + {ISMEAN, CANBICE, ISGREED, CANBARGAIN, NOCOLD, CANFLY, ISLARGE}, + 0, 0, + 50, + {10, 20000, 13, 3, HPT("0d8+56"), "1d4/1d4/2d8"} +}, +{"ancient black dragon", + 100, TRUE, TRUE, 'a', "8-10", + {ISMEAN, CANBACID, ISGREED, CANBARGAIN, CANFLY, ISLARGE}, + 0, 0, + 50, + {10, 20000, 14, 3, HPT("0d8+64"), "1d4/1d4/3d6"} +}, +{"ancient copper dragon", + 100, TRUE, FALSE, 'c', "13-14", + {CANBACID, CANBSLGAS, ISGREED, CANBARGAIN, CANFLY, ISLARGE, MEDFRIENDLY, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 15, 1, HPT("0d8+72"), "1d4/1d4/5d4"} +}, +{"ancient green dragon", + 100, TRUE, TRUE, 'g', "8-12", + {ISMEAN, CANBGAS, ISGREED, CANBARGAIN, CANFLY, ISLARGE, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 15, 2, HPT("0d8+72"), "1d6/1d6/2d10"} +}, +{"ancient bronze dragon", + 100, TRUE, FALSE, 'L', "15-16", + {CANBBOLT, CANBFGAS, ISGREED, CANBARGAIN, NOBOLT, CANFLY, ISLARGE, MEDFRIENDLY, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 16, 0, HPT("0d8+80"), "1d6/1d6/4d6"} +}, +{"ancient blue dragon", + 100, TRUE, TRUE, 'u', "11-12", + {ISMEAN, CANBBOLT, ISGREED, CANBARGAIN, NOBOLT, CANFLY, ISLARGE}, + 0, 0, + 50, + {10, 20000, 16, 2, HPT("0d8+80"), "1d6/1d6/3d8"} +}, +{"ancient silver dragon", + 100, TRUE, FALSE, 'S', "15-16", + {CANBICE, NOCOLD, CANBPGAS, ISGREED, CANBARGAIN, CANFLY, ISLARGE, MEDFRIENDLY, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 17, -1, HPT("0d8+88"), "1d6/1d6/5d6"} +}, +{"frost giant", + 50, TRUE, TRUE, 'F', "5-10", + {ISMEAN, CARRYGOLD, NOCOLD, ISLARGE}, + 0, 0, + 40, + {25, 20000, 15, 4, HPT("10d8+4"), "4d6"} +}, +{"ancient red dragon", + 100, TRUE, TRUE, 'D', "15-16", + {ISMEAN, CANBFIRE, ISGREED, CANBARGAIN, NOFIRE, CANFLY, ISLARGE}, + 0, 0, + 50, + {10, 20000, 17, -1, HPT("0d8+88"), "1d8/1d8/3d10"} +}, +{"ancient gold dragon", + 100, TRUE, FALSE, 'G', "17-18", + {CANBFIRE, CANBGAS, ISGREED, CANBARGAIN, NOFIRE, CANFLY, ISLARGE, HIGHFRIENDLY, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 18, -2, HPT("0d8+96"), "1d8/1d8/6d6"} +}, +{"ancient night dragon", /* Dragon #74 */ + 100, TRUE, TRUE, 'N', "13-15", + {CANBRANDOM, CANBLIND, ISLARGE, ISGREED, CANFLY, CANCAST, CANBARGAIN}, + 0, 0, + 50, + {10, 20000, 18, 2, HPT("8d8"), "4d8"} +}, +{"ancient electrum dragon", /* Dragon #74 */ + 100, TRUE, TRUE, 'E', "17-18", + {CANBPGAS, CANHUH, ISLARGE, CANFLY, CANCAST, CANBARGAIN, HASOXYGEN}, + 0, 0, + 50, + {10, 20000, 18, 2, HPT("9d8"), "1d4/1d4/3d8"} +}, +{"fire giant", + 50, TRUE, TRUE, 'f', "6-10", + {ISMEAN, CARRYGOLD, NOFIRE, ISLARGE}, + 0, 0, + 45, + {27, 26000, 15, 3, HPT("11d8+5"), "5d6"} +}, +{"cloud giant", + 30, TRUE, TRUE, 'c', "8-14", + {ISMEAN, NOBOLT, CANBBOLT, CANBARGAIN, ISLARGE, MEDFRIENDLY}, + 0, 0, + 45, + {30, 27000, 15, 2, HPT("12d8+8"), "6d6"} +}, +{"aerial servant", + 0, TRUE, TRUE, 'a', "3-5", + {ISMEAN, BMAGICHIT, ISSHADOW, LOWFRIENDLY}, + 0, 0, + 50, + {22, 29000, 10, 3, HPT("16d8"), "4d8"} +}, +{"storm giant", + 50, TRUE, TRUE, 's', "8-10", + {ISMEAN, NOBOLT, CANBBOLT, CANCAST, CANBARGAIN, ISLARGE, MEDFRIENDLY}, + 0, 0, + 50, + {30, 30000, 15, 1, HPT("15d8+8"), "7d6"} +}, +{"valkyrie", + 0, TRUE, FALSE, 'v', "14-16", + {CANFLY, CANSHOOT, CANWIELD, CANFRIGHTEN, MAGICHIT, ISFLOCK, CANSUMMON}, + "greater god (Odin, All Father)", 1, + 50, + {21, 27000, 25, -2, HPT("0d8+100"), "3d6+3"} +}, +{"evil sorceress", + 95, TRUE, TRUE, 'z', "17-19", + {ISMEAN, STEALGOLD, STEALMAGIC, ISREGEN, CARRYGOLD, ISSCAVENGE, CANSUMMON, CANCAST, CANBARGAIN}, + "nymph", 4, + 60, + {10, 42000L, 25, -10, HPT("10d8+50"), "3d8"} +}, +{"evil sorcerer", + 95, TRUE, TRUE, 'Z', "17-19", + {ISMEAN, ISREGEN, CARRYGOLD, ISSCAVENGE, CANSUMMON, CANCAST, CANBARGAIN, STEALGOLD, STEALMAGIC}, + "evil sorceress", 1, + 40, + {10, 44000L, 25, -10, HPT("10d9+50"), "3d10"} +}, +{"time elemental", /* Dragon #69 - a recursive monster... */ + 50, TRUE, TRUE, 'T', "10-30", + {ISMEAN, BMAGICHIT, ISFAST, CANBSLGAS, ISSMALL, HALFDAMAGE, NOBOLT, NOFIRE, NOCOLD, NOSHARP, NOMETAL, HASOXYGEN}, + "time elemental", 2, + 100, + {10, 62000L, 21, 2, HPT("15d8"), "4d8"} +}, +{"demon prince (Jubilex)", + 100, TRUE, FALSE, 'J', "17-18", + {ISMEAN, ISUNIQUE, ISREGEN, ISSHADOW, CANHOLD, CANDISEASE, CANSUMMON, ISGOD}, + "black pudding", 3, + 0, + {10, 47280L, 20, -7, HPT("0d8+88"), "4d10"} +}, +{"demon prince (Yeenoghu)", + 100, TRUE, FALSE, 'Y', "15-16", + {ISMEAN, ISREGEN, ISUNIQUE, ISSHADOW, CANHOLD, CANPARALYZE, CANSUMMON, ISGOD}, + "ghoul", 10, + 0, + {10, 54500L, 23, -5, HPT("0d8+100"), "3d6/3d6"} +}, +{"demon prince (Orcus)", + 100, TRUE, FALSE, 'O', "19-20", + {ISMEAN, ISUNIQUE, CANPOISON, CANBBOLT, CANSUMMON, ISGOD}, + "vampire", 4, + 0, + {13, 63900L, 27, -6, HPT("0d8+120"), "1d10+3/2d4"} +}, +{"demi-god (Zagyg the Unpredictable)", /* Dragon #70 */ + 100, TRUE, TRUE, 'Z', "23", + {ISUNIQUE, CANBRANDOM, CANSUMMON, CANWIELD, HIGHFRIENDLY, ISGOD}, + "greater god (Boccob)", 1, + 110, + {23, 59000L, 26, -2, HPT("0d8+121"), "4d8"} +}, +{"demi-god (Wastri, the Hopping Prophet)", /* Dragon # 71 */ + 100, TRUE, TRUE, 'W', "10", + {ISUNIQUE, CANSWIM, CANSUMMON, CANPOISON, CANWIELD, LOWFRIENDLY, ISGOD}, + "giant toad", 4, + 100, + {18, 61000L, 20, -3, HPT("0d8+121"), "2d6/1d6/3d8"} +}, +{"chromatic dragon (Tiamat)", + 100, TRUE, FALSE, 'T', "17-18", + {ISMEAN, ISUNIQUE, CANBFIRE, CANBACID, CANBBOLT, CANBICE, CANBGAS, ISGREED, CANSUMMON, ISGOD, HASOXYGEN}, + "adult dragon", 5, + 0, + {10, 63580L, 29, 0, HPT("0d8+128"), "2d8/3d6/2d10/3d8/3d10/1d6"} +}, +{"arch devil (Geryon)", + 100, TRUE, FALSE, 'g', "15-16", + {ISMEAN, ISUNIQUE, ISSHADOW, CANHUH, CANPOISON, CANSUMMON, ISGOD}, + "ice devil", 2, + 0, + {13, 61500L, 30, -3, HPT("0d8+133"), "3d6/3d6/2d4"} +}, +{"lesser god (Ralishaz the Unlooked For)", /* Dragon # 71 */ + 100, TRUE, TRUE, 'R', "20", + {ISUNIQUE, CANSNORE, CANWIELD, ISFAST, LOWFRIENDLY, ISGOD}, + 0, 0, + 100, + {18, 65000L, 22, -6, HPT("5d10+140"), "1d20/1d20/1d20/1d20"} +}, +{"lesser god (Obad-hai the Shalm)", /* Dragon #69 */ + 100, TRUE, TRUE, 'O', "17", + {ISUNIQUE, ISREGEN, CANSUMMON, LOWFRIENDLY, ISGOD}, + "centaur", 10, + 100, + {18, 62000L, 18, -2, HPT("0d8+144"), "4d8"} +}, +{"arch devil (Dispater)", + 100, TRUE, FALSE, 'd', "17-18", + {ISMEAN, ISUNIQUE, CANHUH, CANSUMMON, ISGOD}, + "pit fiend", 2, + 0, + {10, 48040L, 36, -2, HPT("0d8+144"), "4d6"} +}, +{"lesser god (Trithereon the Summoner)", + 100, TRUE, TRUE, 'T', "18", + {ISUNIQUE, CANWIELD, CANSUMMON, ISFRIENDLY, ISGOD}, + "hound (Nemoud, Servant of Trithereon)", 1, + 0, + {19, 55000L, 35, -4, HPT("0d8+163"), "1d8+12"} +}, +{"demi-god (Iuz the Old)", /* Dragon #67 - enemy of St Cuthbert */ + 100, TRUE, TRUE, 'I', "18", + {ISMEAN, CANHUH, CANBPGAS, ISUNIQUE, CANSUMMON, ISGOD, HASOXYGEN}, + "shade", 2, + 0, + {21, 55000L, 35, -8, HPT("0d8+165"), "1d8+12"} +}, +{"arch devil (Baalzebul)", + 100, TRUE, FALSE, 'B', "17-18", + {ISMEAN, ISSHADOW, ISUNIQUE, CANHOLD, CANPOISON, CANHUH, CANSUMMON, ISGOD}, + "horned devil", 4, + 0, + {10, 61410L, 37, -5, HPT("0d8+166"), "2d6"} +}, +{"platinum dragon (Bahamut)", + 100, TRUE, FALSE, 'B', "19-20", + {ISUNIQUE, CANBICE, CANBGAS, CANBBOLT, ISGREED, CANSUMMON, ISFRIENDLY, ISGOD, HASOXYGEN}, + "ancient gold dragon", 7, + 0, + {10, 58080L, 38, -3, HPT("0d8+168"), "2d6/2d6/6d8"} +}, +{"lesser goddess (Xan Yae, Lady of Perfection)", /* Dragon #68 */ + 100, TRUE, FALSE, 'X', "19", + {CANSHOOT, ISUNIQUE, ISGOD, LOWFRIENDLY}, + 0, 0, + 0, + {22, 55000L, 35, -4, HPT("0d8+172"), "6d6"} +}, +{"lesser goddess (Ehlonna of the Forests)", /* Dragon #68 */ + 100, TRUE, FALSE, 'E', "19", + {CANHUH, CANSHOOT, ISUNIQUE, CANSUMMON, ISGOD, HIGHFRIENDLY}, + "noldor elf", 30, + 0, + {22, 65000L, 35, -6, HPT("0d8+180"), "3d6+6/1d10"} +}, +{"lesser god (Pholtus of the Blinding Light)", /* Dragon #68 */ + 100, TRUE, FALSE, 'P', "19", + {HASFIRE, CANBLIND, CANSHOOT, ISUNIQUE, CANWIELD, CANSUMMON, ISGOD, ISFRIENDLY}, + "phoenix", 2, + 0, + {22, 65000L, 35, -6, HPT("0d8+180"), "3d6+6/1d10"} +}, +{"demi-god (Vaprak \"The Destroyer\")", + 0, TRUE, TRUE, 'v', "18", + {ISMEAN, ISUNIQUE, ISREGEN, CANSUMMON, ISGOD}, + "troll", 5, + 0, + {16, 56000L, 26, 0, HPT("0d8+198"), "2d10/2d10/1d12"} +}, +{"lesser god (Olidammara the Laughing Rogue)", /* Dragon #70 */ + 100, TRUE, TRUE, 'O', "18", + {ISUNIQUE, CANINWALL, CANSUMMON, ISGOD, LOWFRIENDLY}, + "halfling", 10, + 110, + {21, 59000L, 30, -9, HPT("0d8+199"), "4d8"} +}, +{"devil Asmodeus", + 100, TRUE, FALSE, 'A', "19-20", + {ISMEAN, ISUNIQUE, ISSHADOW, CANHOLD, CANHUH, CANCHILL, CANSUMMON, ISGOD}, + "pit fiend", 3, + 0, + {10, 80965L, 45, -7, HPT("0d8+199"), "4d10/4d10"} +}, +{"valar (Yavanna, Giver of Fruits)", + 100, TRUE, FALSE, 'Y', "20", + {ISUNIQUE, CANSUMMON, ISGOD, ISFRIENDLY}, + "entwife", 10, + 0, + {15, 64000L, 45, -8, HPT("0d8+200"), "1d8+4"} +}, +{"valar (Varda, Elbereth Gilthoniel)", + 100, TRUE, FALSE, 'V', "30", + {ISUNIQUE, HASFIRE, CANSUMMON, ISGOD, ISFRIENDLY}, + "noldor elf", 20, + 0, + {15, 64000L, 45, -8, HPT("0d8+200"), "1d8+4"} +}, +{"lesser god (Hextor, Champion of Evil)", + 100, TRUE, FALSE, 'H', "18", + {ISMEAN, CANHUH, ISUNIQUE, CANWIELD, CANSUMMON, ISGOD}, + "zombie", 5, + 0, + {25, 64000L, 45, -8, HPT("0d8+200"), "2d6/2d6"} +}, +{"demon prince (Demogorgon)", + 100, TRUE, FALSE, 'D', "19-20", + {ISMEAN, CANHUH, DOUBLEDRAIN, CANINFEST, ISUNIQUE, CANSUMMON, ISGOD}, + "glabrezu", 3, + 0, + {10, 74000L, 45, -8, HPT("0d8+200"), "1d6/1d6"} +}, +{"lesser god (Heironeous the Invincible)", /* Dragon #67 - enemy of Hextor */ + 100, TRUE, FALSE, 'H', "18", + {CANHUH, ISUNIQUE, CANWIELD, CANSUMMON, NOBOLT, CANBBOLT, ISGOD, ISFRIENDLY}, + "ki-rin", 2, + 0, + {21, 64000L, 45, -9, HPT("0d8+217"), "1d4+8/1d4+8"} +}, +{"lesser god (Kurtulmak)", + 50, TRUE, TRUE, 'K', "19", + {ISMEAN, CANPOISON, ISUNIQUE, ISGOD}, + 0, 0, + 0, + {16, 55000L, 27, 0, HPT("0d8+219"), "2d12/1d6"} +}, +{"lesser god (Hruggek)", + 50, TRUE, FALSE, 'H', "17", + {ISMEAN, ISUNIQUE, CANSUMMON, ISGOD}, + "bugbear", 9, + 0, + {13, 55000L, 25, 0, HPT("0d8+221"), "2d8/2d8"} +}, +{"lesser god (St Cuthbert of the Cudgel)", /* Dragon #67 */ + 100, TRUE, FALSE, 'C', "10", + {CANHUH, ISUNIQUE, CANWIELD, CANSUMMON, ISGOD, ISFRIENDLY}, + "lammasu", 2, + 0, + {20, 65000L, 45, -8, HPT("0d8+224"), "2d6+5/1d10+5"} +}, +{"lesser god (Celestian the Far Wanderer)", /* Dragon #68 */ + 100, TRUE, FALSE, 'c', "20", + {CANHUH, CANSHOOT, CANBFIRE, CANBBOLT, CANBICE, ISUNIQUE, CANSUMMON, ISGOD, ISFRIENDLY}, + "air squid", 2, + 0, + {23, 65000L, 45, -5, HPT("0d8+242"), "1d6+20"} +}, +{"lesser god (Raxivort, Night Flutterer)", + 100, TRUE, TRUE, 'R', "18", + {CANSHOOT, ISUNIQUE, CANBACID, CANWIELD, CANSUMMON, LOWFRIENDLY, ISGOD}, + "xvart", 50, + 0, + {18, 65000L, 45, -1, HPT("0d8+246"), "3d6+6"} +}, +{"lesser god (Fharlanghn, Dweller on the Horizon)", /* Dragon #68 - brother to Celestian */ + 100, TRUE, FALSE, 'f', "18", + {CANHUH, CANBLINK, HASFIRE, ISUNIQUE, CANWIELD, CANSUMMON, ISGOD, ISFRIENDLY}, + "earth elemental", 1, + 0, + {18, 65000L, 45, -6, HPT("0d8+262"), "3d6+6"} +}, +{"lesser god (Nyarlathotep)", + 50, TRUE, FALSE, 'N', "25", + {ISMEAN, ISUNIQUE, ISGOD}, + "kodiac bear", 9, + 0, + {15, 49000L, 25, -5, HPT("0d8+284"), "2d8/2d8"} +}, +{"lesser god (Erythnul the Many)", /* Dragon # 71 */ + 100, TRUE, TRUE, 'E', "16", + {ISMEAN, ISUNIQUE, CANSUMMON, ISGOD}, + "troll", 4, + 100, + {22, 62000L, 45, -3, HPT("0d8+320"), "4d6+10"} +}, +{"greater goddess (Ulaa, Mistress of the Mountains)", + 100, TRUE, FALSE, 'U', "18", + {ISUNIQUE, CANSUMMON, CANWIELD, ISREGEN, ISGOD, HIGHFRIENDLY}, + "kazad dwarf", 15, + 110, + {24, 85000L, 45, -7, HPT("0d8+321"), "4d8"} +}, +{"greater god (Maglubiyet)", + 0, TRUE, FALSE, 'M', "19", + {ISMEAN, ISUNIQUE, ISGOD}, + 0, 0, + 0, + {10, 80000L, 45, -1, HPT("0d8+350"), "4d10"} +}, +{"greater god (Gruumsh)", + 100, TRUE, FALSE, 'G', "19", + {ISMEAN, ISUNIQUE, ISGOD}, + 0, 0, + 0, + {22, 82000L, 45, -1, HPT("0d8+350"), "4d10"} +}, +{"greater god (Boccob the Uncaring)", + 100, TRUE, FALSE, 'B', "26", + {ISUNIQUE, CANWIELD, ISGOD, LOWFRIENDLY}, + 0, 0, + 0, + {18, 95000L, 45, -8, HPT("0d8+354"), "4d10"} +}, +{"greater goddess (Istus, Lady of Fate)", /* Dragon #69 */ + 100, TRUE, FALSE, 'I', "23", + {ISUNIQUE, CANSUMMON, CANWIELD, CANHOLD, ISGOD, LOWFRIENDLY}, + "time elemental", 3, + 110, + {15, 88000L, 45, -8, HPT("0d8+377"), "4d8"} +}, +{"lesser god (Skoraeus Stonebones)", + 0, TRUE, FALSE, 'S', "19", + {ISUNIQUE, ISREGEN, ISGOD, LOWFRIENDLY}, + 0, 0, + 0, + {10, 85000L, 45, -1, HPT("0d8+380"), "6d10"} +}, +{"greater god (Incabulos, God of Evil Sendings)", /* Dragon # 71 */ + 100, TRUE, TRUE, 'I', "20", + {ISMEAN, ISUNIQUE, CANSUMMON, CANWIELD, CANSNORE, ISGOD}, + "night hag", 4, + 100, + {18, 92000L, 25, -9, HPT("0d8+383"), "5d6"} +}, +{"greater god (Nerull the Grim Reaper)", /* Dragon # 71 */ + 100, TRUE, TRUE, 'N', "21", + {ISMEAN, ISUNIQUE, CANBACID, CANSUMMON, ISGOD}, + "shaggy demodand (shator)", 2, + 100, + {14, 92000L, 30, -6, HPT("0d8+400"), "10d6"} +}, +{"greater god (Odin, All Father)", + 100, TRUE, TRUE, 'O', "25", + {ISUNIQUE, CANSUMMON, CANWIELD, ISGOD, LOWFRIENDLY}, + "valkyrie", 50, + 100, + {25, 105000L, 30, -6, HPT("0d8+400"), "5d8"} +}, +{"valar (Orome, Lord of Forests)", + 100, TRUE, FALSE, 'o', "36", + {ISUNIQUE, CANSUMMON, ISGOD, LOWFRIENDLY}, + "elephant", 10, + 0, + {18, 95000L, 45, -5, HPT("0d8+460"), "4d8+10"} +}, +{"valar (Aule, Lord of Smithing)", + 100, TRUE, FALSE, 'A', "36", + {ISUNIQUE, CANSUMMON, ISGOD, LOWFRIENDLY}, + "kazad dwarf", 15, + 0, + {20, 95000L, 45, -5, HPT("0d8+460"), "4d8+10"} +}, +{"valar (Ulmo, God of Waters)", + 100, TRUE, FALSE, 'U', "36", + {ISUNIQUE, CANSUMMON, CANWIELD, ISGOD, LOWFRIENDLY}, + "giant crocodile", 15, + 0, + {18, 95000L, 45, -5, HPT("0d8+460"), "4d8+10"} +}, +{"valar (Melkor, the Dark Lord)", + 100, TRUE, FALSE, 'M', "36", + {ISMEAN, ISUNIQUE, CANHUH, CANSUMMON, ISGOD}, + "maiar (Balrog)", 15, + 0, + {18, 95000L, 45, -10, HPT("0d8+460"), "4d8+10"} +}, +{"valar (Manwe, Lord of Arda)", + 100, TRUE, FALSE, 'M', "36", + {ISUNIQUE, CANSUMMON, CANWIELD, ISGOD, LOWFRIENDLY}, + "aerial servant", 15, + 0, + {18, 95000L, 45, -10, HPT("0d8+460"), "4d8+10"} +}, +{"valar (Tulkas the Valiant)", + 100, TRUE, FALSE, 'T', "36", + {ISUNIQUE, CANSUMMON, ISGOD, LOWFRIENDLY}, + "noone, haste anyway", 1, + 0, + {28, 145000L, 65, -15, HPT("0d8+800"), "10d4/4d10"} +}, +{"quartermaster", + 0, FALSE, TRUE, 'q', "18-20", + {CANSELL}, + 0, 0, + 2, + {12, 20, 100, -11, HPT("1d8+1"), "1d10"} +}, +{"Lord of All Darkness (Lucifer)", + 100, TRUE, TRUE, 'L', "40-60", + {ISMEAN, ISUNIQUE, CANSUMMON, ISGOD}, + "evil sorcerer", 15, + 225, + {45, 1465000L, 100, -13, HPT("18d60+700"), "3d8/3d8/3d8"}} +}; + +int nummonst = NUMMONST; diff -r d9badb9c0179 -r c495a4f288c6 urogue/monsters.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/monsters.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1497 @@ +/* + monsters.c - File with various monster functions in it + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +/* + summon_monster() + Summon a monster. +*/ + +struct linked_list * +summon_monster(int type, int familiar, int print_message) +{ + struct linked_list *mp; + struct thing *tp; + int monster; + + if (familiar && !is_wearing(R_WIZARD) && off(player, CANSUMMON)) + { + msg("Only spellcasters can summon familiars!"); + return(NULL); + } + + if (type == 0) /* Random monster modified by level */ + { + int ndice = min(pstats.s_lvl, (nummonst - NUMSUMMON) / 8); + + monster = min(nummonst, roll(ndice, pstats.s_charisma)); + + /* + * if a familiar exists, and it is higher in level, make it + * again + */ + + if (fam_ptr != NULL) + { + struct thing *fp = THINGPTR(fam_ptr); + + monster = max(fp->t_index, monster); + } + } + else + monster = type; + + turn_on(player, SUMMONING); + + mp = creat_mons(&player, monster, NOMESSAGE); + + if (!mp) + { + msg("Summon failed."); + turn_off(player, SUMMONING); + return(NULL); + } + + if (print_message == MESSAGE) + { + msg("A %s appears out of nowhere!", monsters[monster].m_name); + + if (familiar) + msg("I am here to serve %s.", whoami); + else + { + msg("My goodness, are you Yendor?"); + ++mons_summoned; + debug("%d monsters now summoned.", mons_summoned); + } + } + + tp = THINGPTR(mp); + turn_on(*tp, ISCHARMED); /* Summoned monsters are always charmed */ + + if (familiar) + { + int i; + static const unsigned long fam_on[]= {ISREGEN,CANSHOOT,CANWIELD,HASARMOR,ISFAMILIAR,0}; + static const unsigned long fam_off[]={ISMEAN, ISHUH, ISINVIS, + CANSURPRISE, NOMOVE, + ISSLOW, ISSHADOW, ISGREED, ISFAST, + CANFLY, ISFLEE, 0}; + + for (i = 0; fam_on[i]; i++) + turn_on(*tp, fam_on[i]); + + for (i = 0; fam_off[i]; i++) + turn_off(*tp, fam_off[i]); + + if (fam_ptr != NULL) /* Get rid of old familiar */ + { + struct thing *fp = THINGPTR(fam_ptr); + struct linked_list *fpack = fp->t_pack; + struct linked_list *item; + + if (fpack != NULL) /* Transfer pack */ + { + if (tp->t_pack == NULL) + tp->t_pack = fpack; + else + { + for(item=tp->t_pack; item->l_next != NULL; item=next(item)) + ; /* find last item in list */ + + item->l_next = fpack; + fpack->l_prev = item; + } + } + + fpack = NULL; + killed(NULL, fam_ptr, NOMESSAGE, NOPOINTS); + } + + fam_ptr = mp; + fam_type = monster; + + /* improve their abilities a bit */ + + tp->t_stats.s_hpt += roll(2, pstats.s_lvl); + tp->t_stats.s_lvl += roll(2, (pstats.s_lvl / 4) + 1); + tp->t_stats.s_arm -= roll(2, (pstats.s_lvl / 4) + 1); + tp->t_stats.s_str += roll(2, (pstats.s_lvl / 4) + 1); + tp->t_stats.s_intel += roll(2, (pstats.s_lvl / 4) + 1); + tp->t_stats.s_wisdom += roll(2, (pstats.s_lvl / 4) + 1); + tp->t_stats.s_dext += roll(2, (pstats.s_lvl / 4) + 1); + tp->t_stats.s_const += roll(2, (pstats.s_lvl / 4) + 1); + tp->t_stats.s_charisma += roll(2, (pstats.s_lvl / 4) + 1); + + /* some monsters do no damage by default */ + + if (strcmp(tp->t_stats.s_dmg, "0d0") == 0) + tp->t_stats.s_dmg = "1d8"; + + tp->maxstats = tp->t_stats; /* structure assignment */ + } + + turn_off(player, SUMMONING); + + return(mp); +} + +/* + randmonster() + wander - wandering monster allowed + grab - a throne room monster allowed +*/ + +int +randmonster(int wander, int grab) +{ + int mons_number, cur_level, range, i; + + /* Do we want a merchant? */ + + if (wander == WANDER && monsters[nummonst].m_wander && rnd(5000) < 3) + return(nummonst); + + cur_level = level; + range = 4 * NLEVMONS; + i = 0; + + do + { + if (i++ > range * 10) /* just in case all have be genocided */ + { + i = 0; + + if (--cur_level <= 0) + fatal("Rogue could not find a monster to make"); + } + + mons_number = NLEVMONS * (cur_level - 1) + + (rnd(range) - (range - 1 - NLEVMONS)); + + if (mons_number < 1) + mons_number = rnd(NLEVMONS) + 1; + else if (mons_number > nummonst - NUMSUMMON - 1) + { + if (grab == GRAB) + mons_number = rnd(range + NUMSUMMON) + + (nummonst - 1) - + (range + NUMSUMMON - 1); + else if (mons_number > nummonst - 1) + mons_number = rnd(range) + + (nummonst - NUMSUMMON - 1) - + (range - 1); + } + } + while (wander == WANDER ? !monsters[mons_number].m_wander || + !monsters[mons_number].m_normal : + !monsters[mons_number].m_normal); + + return((short)mons_number); +} + +/* + new_monster() + Pick a new monster and add it to the list +*/ + +void +new_monster(struct linked_list *item, int type, coord *cp, int max_monster) +{ + struct thing *tp; + struct monster *mp; + char *ip, *hitp; + int i, min_intel, max_intel; + int num_dice, num_sides = 8, num_extra = 0; + int eff_charisma = pstats.s_charisma; + int eff_intel = pstats.s_intel; + + attach(mlist, item); + tp = THINGPTR(item); + tp->t_index = type; + tp->t_wasshot = FALSE; + tp->t_type = monsters[type].m_appear; + tp->t_ctype = C_MONSTER; + tp->t_no_move = 0; + tp->t_doorgoal = -1; + tp->t_pos = *cp; + tp->t_oldpos = *cp; + tp->t_oldch = CCHAR( mvwinch(cw, cp->y, cp->x) ); + mvwaddch(mw, cp->y, cp->x, tp->t_type); + mp = &monsters[tp->t_index]; + + /* Figure out monster's hit points */ + + hitp = mp->m_stats.s_hpt; + num_dice = atoi(hitp); + + if ((hitp = strchr(hitp, 'd')) != NULL) + { + num_sides = atoi(++hitp); + + if ((hitp = strchr(hitp, '+')) != NULL) + num_extra = atoi(++hitp); + } + + if (max_monster == MAXSTATS) + tp->t_stats.s_hpt = num_dice * num_sides + num_extra; + else + tp->t_stats.s_hpt = roll(num_dice, num_sides) + num_extra; + + tp->t_stats.s_lvl = mp->m_stats.s_lvl; + tp->t_stats.s_arm = mp->m_stats.s_arm; + tp->t_stats.s_dmg = mp->m_stats.s_dmg; + tp->t_stats.s_exp = mp->m_stats.s_exp + mp->m_add_exp * tp->t_stats.s_hpt; + tp->t_stats.s_str = mp->m_stats.s_str; + + if (max_level > 30) + { + tp->t_stats.s_hpt += roll(4, (max_level - 60) * 2); + tp->t_stats.s_lvl += roll(4, (max_level - 60) / 8); + tp->t_stats.s_arm -= roll(2, (max_level - 60) / 8); + tp->t_stats.s_str += roll(2, (max_level - 60) / 12); + tp->t_stats.s_exp += roll(4, (max_level - 60) * 2) * mp->m_add_exp; + } + + /* + * just initailize others values to something reasonable for now + * maybe someday will *really* put these in monster table + */ + + tp->t_stats.s_wisdom = 8 + rnd(4); + tp->t_stats.s_dext = 8 + rnd(4); + tp->t_stats.s_const = 8 + rnd(4); + tp->t_stats.s_charisma = 8 + rnd(4); + + if (max_level > 45) + tp->t_stats.s_dext += roll(2, (max_level - 50) / 8); + + /* Set the initial flags */ + + for (i = 0; i < 16; i++) + tp->t_flags[i] = 0; + + for (i = 0; i < 16; i++) + turn_on(*tp, mp->m_flags[i]); + + /* suprising monsters don't always surprise you */ + + if (!max_monster && on(*tp, CANSURPRISE) && rnd(100) < 20) + turn_off(*tp, CANSURPRISE); + + /* If this monster is unique, genocide it */ + + if (on(*tp, ISUNIQUE)) + mp->m_normal = FALSE; + + /* gods automatically get special abilities */ + + if (on(*tp, ISGOD)) + { + turn_on(*tp, CANFRIGHTEN); + turn_on(*tp, CANCAST); + turn_on(*tp, CANFLY); + turn_on(*tp, CANBARGAIN); + turn_on(*tp, ISLARGE); + turn_on(*tp, CANTELEPORT); + turn_on(*tp, CANSPEAK); + turn_on(*tp, CANDARKEN); + turn_on(*tp, CANSEE); + turn_on(*tp, CANLIGHT); + turn_on(*tp, BMAGICHIT); + } + + tp->t_turn = TRUE; + tp->t_pack = NULL; + + /* Figure intelligence */ + + min_intel = atoi(mp->m_intel); + + if ((ip = (char *) strchr(mp->m_intel, '-')) == NULL) + tp->t_stats.s_intel = min_intel; + else + { + max_intel = atoi(++ip); + + if (max_monster) + tp->t_stats.s_intel = max_intel; + else + tp->t_stats.s_intel = min_intel + rnd(max_intel - min_intel); + } + + tp->t_stats.s_power = (rnd(tp->t_stats.s_lvl / 5) + 1) * tp->t_stats.s_intel; + + tp->maxstats = tp->t_stats; /* structure assignment */ + + /* If the monster can shoot, it may have a weapon */ + + if (on(*tp, CANSHOOT) && (max_monster || rnd(9) < 6)) + { + struct linked_list *thrower_item, *missile_item; + struct object *thrower, *a_missile; + + thrower_item = new_item(sizeof *thrower); + thrower = OBJPTR(thrower_item); + carried_weapon(tp, thrower); + + missile_item = new_item(sizeof *a_missile); + a_missile = OBJPTR(missile_item); + carried_weapon(tp, a_missile); + + /* The monster may use a crossbow, sling, footbow, or an arrow */ + /* Take racial preferences into account */ + + if ((strcmp(mp->m_name, "elf") == 0) || + (strcmp(mp->m_name, "noldor elf") == 0)) + { + thrower->o_which = BOW; + + if (rnd(5) == 0) + a_missile->o_which = SILVERARROW; + else + a_missile->o_which = ARROW; + } + else if ((strcmp(mp->m_name, "dwarf") == 0) || + (strcmp(mp->m_name, "kazad dwarf") == 0)) + { + thrower->o_which = CROSSBOW; + a_missile->o_which = BOLT; + } + else if (on(*tp, ISSMALL)) + { + switch (rnd(3)) + { + case 0: + thrower->o_which = SLING; + a_missile->o_which = BULLET; + break; + default: + thrower->o_which = SLING; + a_missile->o_which = ROCK; + } + } + else if (on(*tp, ISLARGE)) + { + switch (rnd(4)) + { + case 0: + thrower->o_which = CROSSBOW; + a_missile->o_which = BOLT; + break; + + case 1: + thrower->o_which = FOOTBOW; + a_missile->o_which = FBBOLT; + break; + + default: + thrower->o_which = BOW; + + if (rnd(5) == 0) + a_missile->o_which = FLAMEARROW; + else + a_missile->o_which = ARROW; + + break; + } + } + else + { + switch (rnd(6)) + { + case 1: + thrower->o_which = SLING; + a_missile->o_which = ROCK; + break; + + case 2: + thrower->o_which = CROSSBOW; + a_missile->o_which = BOLT; + break; + + case 3: + thrower->o_which = FOOTBOW; + a_missile->o_which = FBBOLT; + break; + + case 4: + thrower->o_which = BOW; + a_missile->o_which = ARROW; + break; + + default: + thrower->o_which = SLING; + a_missile->o_which = BULLET; + break; + } + } + + init_weapon(thrower, thrower->o_which); + init_weapon(a_missile, a_missile->o_which); + + attach(tp->t_pack, thrower_item); + attach(tp->t_pack, missile_item); + } + + /* monsters that wield weapons */ + + if (on(*tp, CANWIELD)) + { + if (max_monster || rnd(3)) + { + struct linked_list *wield_item; + struct object *wielded; + + wield_item = new_item(sizeof *wielded); + wielded = OBJPTR(wield_item); + carried_weapon(tp, wielded); + + i = rnd(CLAYMORE - CLUB) + rnd(2 * tp->t_stats.s_lvl); + i = min(i, CLAYMORE); + wielded->o_which = i; + init_weapon(wielded, wielded->o_which); + + /* Is it too heavy? */ + + if (itemweight(wielded) > 8 * tp->t_stats.s_str) + discard(wield_item); + else + attach(tp->t_pack, wield_item); + } + } + + if (is_wearing(R_AGGR)) + chase_it(cp, &player); + else + { + turn_off(*tp, ISRUN); + + if (on(*tp, ISFLEE) && (rnd(4) == 0)) + turn_off(*tp, ISFLEE); + + if (rnd(luck) == 0) + switch (player.t_ctype) + { + case C_MAGICIAN: + case C_ILLUSION: + eff_intel = 2 * pstats.s_intel; + break; + case C_DRUID: + eff_intel = 2 * pstats.s_intel; + case C_RANGER: + eff_charisma = 2 * pstats.s_charisma; + break; + case C_ASSASIN: + case C_THIEF: + case C_NINJA: + eff_charisma = pstats.s_charisma / 2; + break; + } + + /* LOWFRIENDLY monsters might be friendly */ + + i = roll(1,100); + + if (i == 0 || (on(*tp, LOWFRIENDLY) && i < eff_charisma) || + (on(*tp, MEDFRIENDLY) && i < 3 * eff_charisma) || + (on(*tp, HIGHFRIENDLY) && i < 5 * eff_charisma)) + { + turn_on(*tp, ISFRIENDLY); + turn_off(*tp, ISMEAN); + } + + i = roll(1,100); + + if (i == 0 || (on(*tp, LOWCAST) && i < eff_intel) || + (on(*tp, MEDCAST) && i < 3 * eff_intel) || + (on(*tp, HIGHCAST) && i < 5 * eff_intel)) + { + turn_on(*tp, CANCAST); + } + + if (on(*tp, ISDISGUISE)) + { + char mch = 0; + + if (tp->t_pack != NULL) + mch = (OBJPTR(tp->t_pack))->o_type; + else + switch (rnd(level > arts[0].ar_level ? 10 : 9)) + { + case 0: mch = GOLD; break; + case 1: mch = POTION; break; + case 2: mch = SCROLL; break; + case 3: mch = FOOD; break; + case 4: mch = WEAPON; break; + case 5: mch = ARMOR; break; + case 6: mch = RING; break; + case 7: mch = STICK; break; + case 8: mch = monsters[randmonster(NOWANDER, NOGRAB)].m_appear; + break; + case 9: mch = ARTIFACT; break; + } + + tp->t_disguise = mch; + } + } +} + +/* + wanderer() + A wandering monster has awakened and is headed for the player +*/ + +void +wanderer(void) +{ + int i, cnt = 0; + struct room *hr = roomin(hero); + struct linked_list *item; + struct thing *tp; + coord cp; + char *loc; + int which; + + /* Find a place for it -- avoid the player's room */ + + do + { + do + { + cnt++; + i = rnd_room(); + } + while (!(hr != &rooms[i] || levtype == MAZELEV + || levtype == THRONE || cnt > 5000)); + + rnd_pos(&rooms[i], &cp); + } + while(!step_ok(cp.y, cp.x, NOMONST, NULL)); + + /* Create a new wandering monster */ + + item = new_item(sizeof *tp); + which = randmonster(TRUE, FALSE); + new_monster(item, which, &cp, FALSE); + + tp = THINGPTR(item); + tp->t_pos = cp; /* Assign the position to the monster */ + + chase_it(&tp->t_pos, &player); + + i = rnd(7); + + if (on(*tp, ISSWARM) && i < 5) + cnt = roll(2, 4); + else if (on(*tp, ISFLOCK) && i < 5) + cnt = roll(1, 4); + else + cnt = 0; + + for (i = 1; i <= cnt; i++) + { + struct linked_list *ip = creat_mons(tp, which, NOMESSAGE); + + if (ip != NULL) + { + struct thing *mp = THINGPTR(ip); + + if (on(*tp, ISFRIENDLY)) + turn_on(*mp, ISFRIENDLY); + else + turn_off(*mp, ISFRIENDLY); + } + } + + if (cnt > 0) + { + if (on(*tp, LOWCAST) || on(*tp, MEDCAST) || on(*tp, HIGHCAST)) + turn_on(*tp, CANCAST); + + tp->t_stats.s_hpt += roll(2, 8); + tp->t_stats.s_lvl += roll(2, 3); + tp->t_stats.s_arm -= roll(1, 6); + tp->t_stats.s_str += roll(2, 3); + tp->t_stats.s_intel += roll(2, 3); + tp->t_stats.s_exp += roll(2, 8) * monsters[which].m_add_exp; + } + + i = DISTANCE(cp, hero); + + if (i < 20) + loc = "very close to you"; + else if (i < 400) + loc = "nearby"; + else + loc = "in the distance"; + + if (wizard) + msg("Started a wandering %s.", monsters[tp->t_index].m_name); + else if (on(*tp, ISUNDEAD) && (player.t_ctype == C_CLERIC || + player.t_ctype == C_PALADIN || is_wearing(R_PIETY))) + msg("You sense a new ungodly monster %s.", loc); + else if (on(player, CANHEAR) || (player.t_ctype == C_THIEF && + rnd(20) == 0)) + msg("You hear a new %s moving %s.", + monsters[tp->t_index].m_name, loc); + else if (on(player, CANSCENT) || (player.t_ctype == C_THIEF && + rnd(20) == 0)) + msg("You smell a new %s %s.", monsters[tp->t_index].m_name, + loc); +} + +/* + wake_monster + + what to do when the hero steps next to a monster +*/ + +struct linked_list * +wake_monster(int y, int x) +{ + struct thing *tp; + struct linked_list *it; + struct room *trp; + char *mname; + + if ((it = find_mons(y, x)) == NULL) + { + debug("Can't find monster in show."); + return(NULL); + } + + tp = THINGPTR(it); + + if ((good_monster(*tp)) || on(player, SUMMONING)) + { + chase_it(&tp->t_pos, &player); + turn_off(*tp, ISINVIS); + turn_off(*tp, CANSURPRISE); + return(it); + } + + trp = roomin(tp->t_pos); /* Current room for monster */ + mname = monsters[tp->t_index].m_name; + + /* Let greedy ones guard gold */ + + if (on(*tp, ISGREED) && off(*tp, ISRUN)) + if ((trp != NULL) && (lvl_obj != NULL)) + { + struct linked_list *item; + struct object *cur; + + for (item = lvl_obj; item != NULL; item = next(item)) + { + cur = OBJPTR(item); + + if ((cur->o_type == GOLD) && + (roomin(cur->o_pos) == trp)) + { + /* Run to the gold */ + tp->t_horde = cur; + turn_on(*tp, ISRUN); + turn_off(*tp, ISDISGUISE); + tp->t_ischasing = FALSE; + /* Make it worth protecting */ + cur->o_count += roll(2, 3) * GOLDCALC; + break; + } + } + } + + /* + * Every time he sees mean monster, it might start chasing him unique + * monsters always do + */ + + if ( (on(*tp, ISUNIQUE)) || + ( (rnd(100) > 33) && + on(*tp, ISMEAN) && + off(*tp, ISHELD) && + off(*tp, ISRUN) && + !is_stealth(&player) && + (off(player, ISINVIS) || on(*tp, CANSEE)) + ) + ) + { + chase_it(&tp->t_pos, &player); + } + + /* Handle gaze attacks */ + + if (on(*tp, ISRUN) && cansee(tp->t_pos.y, tp->t_pos.x) && + off(player, ISINVIS)) + { + if (on(*tp, CANHUH)) /* Confusion */ + { + if (on(player, CANREFLECT)) + { + msg("You reflect the bewildering stare of the %s.", mname); + + if (save_throw(VS_MAGIC, tp)) + { + msg("The %s is confused!", mname); + turn_on(*tp, ISHUH); + } + else + msg("The %s staggers for a moment.", mname); + } + else if (save(VS_MAGIC)) + { + msg("You feel dizzy for a moment, but it quickly passes."); + + if (rnd(100) < 67) + turn_off(*tp, CANHUH); + } + else if (off(player, ISCLEAR)) + { + if (off(player, ISHUH)) + { + light_fuse(FUSE_UNCONFUSE, 0, rnd(20) + HUHDURATION, AFTER); + msg("The %s's gaze has confused you.", mname); + turn_on(player, ISHUH); + } + else + lengthen_fuse(FUSE_UNCONFUSE, rnd(20) + HUHDURATION); + } + } + + if (on(*tp, CANSNORE)) /* Sleep */ + { + if (on(player, CANREFLECT)) + { + msg("You reflect the lethargic glance of the %s", mname); + + if (save_throw(VS_PARALYZATION, tp)) + { + msg("The %s falls asleep!", mname); + tp->t_no_move += SLEEPTIME; + } + } + else if (no_command == 0 && !save(VS_PARALYZATION)) + { + if (is_wearing(R_ALERT)) + msg("You feel slightly drowsy for a moment."); + else + { + msg("The %s's gaze puts you to sleep.", mname); + no_command = SLEEPTIME; + + if (rnd(100) < 50) + turn_off(*tp, CANSNORE); + } + } + } + + if (on(*tp, CANFRIGHTEN)) /* Fear */ + { + turn_off(*tp, CANFRIGHTEN); + + if (on(player, CANREFLECT)) + { + msg("The %s sees its reflection. ", mname); + + if (save_throw(VS_MAGIC,tp)) + { + msg("The %s is terrified by its reflection!", mname); + turn_on(*tp, ISFLEE); + } + } + else + { + if (!save(VS_WAND) && !(on(player, ISFLEE) && + (player.t_chasee==tp))) + { + if ((player.t_ctype != C_PALADIN) && + off(player, SUPERHERO)) + { + turn_on(player, ISFLEE); + player.t_ischasing = FALSE; + player.t_chasee = tp; + msg("The sight of the %s terrifies you.", mname); + } + else + msg("My, the %s looks ugly.", mname); + } + } + } + + if (on(*tp, LOOKSLOW)) /* Slow */ + { + turn_off(*tp, LOOKSLOW); + + if (on(player, CANREFLECT)) + { + msg("You reflect the mournful glare of the %s.", mname); + + if (save_throw(VS_MAGIC,tp)) + { + msg("The %s is slowing down!", mname); + turn_on(*tp, ISSLOW); + } + } + else if (is_wearing(R_FREEDOM) || save(VS_MAGIC)) + msg("You feel run-down for a moment."); + else + { + if (on(player, ISHASTE)) /* Already sped up */ + { + extinguish_fuse(FUSE_NOHASTE); + nohaste(NULL); + } + else + { + msg("You feel yourself moving %sslower.", + on(player, ISSLOW) ? "even " : ""); + + if (on(player, ISSLOW)) + lengthen_fuse(FUSE_NOSLOW, rnd(4) + 4); + else + { + turn_on(player, ISSLOW); + player.t_turn = TRUE; + light_fuse(FUSE_NOSLOW, 0, rnd(4) + 4, AFTER); + } + } + } + } + + if (on(*tp, CANBLIND)) /* Blinding */ + { + turn_off(*tp, CANBLIND); + + if (on(player, CANREFLECT)) + { + msg("You reflect the blinding stare of the %s.", mname); + + if (save_throw(VS_WAND, tp)) + { + msg("The %s is blinded!", mname); + turn_on(*tp, ISHUH); + } + } + else if (off(player, ISBLIND)) + if (save(VS_WAND) || is_wearing(R_TRUESEE) || is_wearing(R_SEEINVIS)) + msg("Your eyes film over for a moment."); + else + { + msg("The gaze of the %s blinds you.", mname); + turn_on(player, ISBLIND); + light_fuse(FUSE_SIGHT, 0, rnd(30) + 20, AFTER); + look(FALSE); + } + } + + if (on(*tp, LOOKSTONE)) /* Stoning */ + { + turn_off(*tp, LOOKSTONE); + + if (on(player, CANREFLECT)) + { + msg("You reflect the flinty look of the %s.", mname); + + if (save_throw(VS_PETRIFICATION,tp)) + { + msg("The %s suddenly stiffens", mname); + tp->t_no_move += STONETIME; + } + else + { + msg("The %s is turned to stone!", mname); + killed(&player, it, NOMESSAGE, POINTS); + } + } + else + { + if (on(player, CANINWALL)) + msg("The %s cannot focus on you.", mname); + else + { + msg("The gaze of the %s stiffens your limbs.", mname); + + if (save(VS_PETRIFICATION)) + no_command = STONETIME; + else if (rnd(100)) + no_command = STONETIME * 3; + else + { + msg("The gaze of the %s petrifies you.", mname); + msg("You are turned to stone!!! --More--"); + wait_for(' '); + death(D_PETRIFY); + return(it); + } + } + } + } + } + + /* + * True Sight sees all Never see ISINWALL or CANSURPRISE See ISSHADOW + * 80% See ISINVIS with See Invisibilty + */ + + if (off(player, CANTRUESEE) && + on(*tp, ISINWALL) || on(*tp, CANSURPRISE) || + (on(*tp, ISSHADOW) && rnd(100) < 80) || + (on(*tp, ISINVIS) && off(player, CANSEE))) + { + /* + TODO: incomplete - need to finish logic + int ch = mvwinch(stdscr, y, x); + */ + } + + + /* hero might be able to hear or smell monster if he can't see it */ + + if ((rnd(player.t_ctype == C_THIEF ? 40 : 200) == 0 || + on(player, CANHEAR)) && !cansee(tp->t_pos.y, tp->t_pos.x)) + msg("You hear a %s nearby.", mname); + else if ((rnd(player.t_ctype == C_THIEF ? 40 : 200) == 0 || + on(player, CANSCENT)) && !cansee(tp->t_pos.y, tp->t_pos.x)) + msg("You smell a %s nearby.", mname); + + return(it); +} + +/* + genocide() + wipe out hated monsters flags: ISBLESSED, ISCURSED +*/ + +void +genocide(int flags) +{ + struct linked_list *ip; + struct thing *mp; + struct linked_list *nip; + int which_monst; + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + + while ((which_monst = get_monster_number("genocide")) == 0) + ; + + if (cursed) /* oops... */ + { + new_level(THRONE, which_monst); + msg("What's this I hear about you trying to wipe me out?"); + fighting = running = after = FALSE; + return; + } + + /* Remove this monster from the present level */ + + for (ip = mlist; ip; ip = nip) + { + mp = THINGPTR(ip); + nip = next(ip); + + if (mp->t_index == which_monst) + { + check_residue(mp); /* Check for special features before removing */ + remove_monster(&mp->t_pos, ip); + } + } + + /* Remove from available monsters */ + + monsters[which_monst].m_normal = FALSE; + monsters[which_monst].m_wander = FALSE; + mpos = 0; + msg("You have wiped out the %s.", monsters[which_monst].m_name); + + if (blessed) + genocide(ISNORMAL); +} + + +/* + id_monst() + lists the monsters with the displayed by the character unless + there is only one in which case it is returned as the string +*/ + +void +id_monst(int monster) +{ + int i; + + for (i = 1; i <= nummonst + 2; i++) + if (monsters[i].m_appear == monster) + add_line("A %s ", monsters[i].m_name); + + end_line(); +} + + +/* + check_residue() + takes care of any effect of the monster +*/ + +void +check_residue(struct thing *tp) +{ + /* Take care of special abilities */ + + if (on(*tp, DIDHOLD) && (--hold_count == 0)) + turn_off(player, ISHELD); + + /* If it has lowered player, give him back a level, maybe */ + + if (on(*tp, DIDDRAIN) && rnd(3) == 0) + raise_level(); + + /* If frightened of this monster, stop */ + + if (on(player, ISFLEE) && (player.t_chasee==tp)) + turn_off(player, ISFLEE); + + /* If monster was suffocating player, stop it */ + if (on(*tp, DIDSUFFOCATE)) + extinguish_fuse(FUSE_SUFFOCATE); + + /* If something with fire, may darken */ + if (on(*tp, HASFIRE)) + { + struct room *rp = roomin(tp->t_pos); + + if (rp && (--(rp->r_fires) <= 0)) + { + rp->r_flags &= ~HASFIRE; + light(&tp->t_pos); + } + } +} + +/* + sell() + displays a menu of goods from which the player may choose to + purchase something. +*/ + +#define SELL_ITEMS 10 /* How many things 'q' might carry */ + +void +sell(struct thing *tp) +{ + struct linked_list *item; + int i, j, min_worth, nitems, chance, which_item, w; + char goods; + struct object *obj; + char buffer[2 * LINELEN]; + char dbuf[2 * LINELEN]; + + struct + { + int which; + int plus1, plus2; + int count; + int worth; + int flags; + char *name; + } + selection[SELL_ITEMS]; + + int effective_purse = ((player.t_ctype == C_PALADIN) ? + (9 * purse / 10) : purse); + + min_worth = -1; /* hope item is never worth less than this */ + item = find_mons(tp->t_pos.y, tp->t_pos.x); /* Get pointer to monster */ + + /* Select the items */ + + nitems = rnd(6) + 5; + + switch (rnd(6)) + { + /* Armor */ + case 0: + case 1: + goods = ARMOR; + for (i = 0; i < nitems; i++) + { + chance = rnd(100); + + for (j = 0; j < maxarmors; j++) + if (chance < armors[j].a_prob) + break; + + if (j == maxarmors) + { + debug("Picked a bad armor %d", chance); + j = 0; + } + + selection[i].which = j; + selection[i].count = 1; + + if (rnd(100) < 40) + selection[i].plus1 = rnd(5) + 1; + else + selection[i].plus1 = 0; + + selection[i].name = armors[j].a_name; + + switch (luck) + { + case 0: break; + case 1: + if (rnd(3) == 0) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = -1 - rnd(5); + } + break; + + default: + if (rnd(luck)) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = -1 - rnd(5); + } + break; + } + + /* Calculate price */ + + w = armors[j].a_worth; + w *= (1 + luck + (10 * selection[i].plus1)); + w = (w / 2) + (roll(6, w) / 6); + selection[i].worth = max(w, 25); + + if (min_worth > selection[i].worth || i == 1) + min_worth = selection[i].worth; + } + break; + + /* Weapon */ + case 2: + case 3: + goods = WEAPON; + for (i = 0; i < nitems; i++) + { + selection[i].which = rnd(maxweapons); + selection[i].count = 1; + + if (rnd(100) < 35) + { + selection[i].plus1 = rnd(3); + selection[i].plus2 = rnd(3); + } + else + { + selection[i].plus1 = 0; + selection[i].plus2 = 0; + } + + if (weaps[selection[i].which].w_flags & ISMANY) + selection[i].count = rnd(15) + 8; + + selection[i].name = weaps[selection[i].which].w_name; + + switch (luck) + { + case 0: break; + case 1: + if (rnd(3) == 0) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = -rnd(3); + selection[i].plus2 = -rnd(3); + } + break; + + default: + if (rnd(luck)) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = -rnd(3); + selection[i].plus2 = -rnd(3); + } + break; + } + + w = weaps[selection[i].which].w_worth * selection[i].count; + w *= (1 + luck + (10 * selection[i].plus1 + + 10 * selection[i].plus2)); + w = (w / 2) + (roll(6, w) / 6); + selection[i].worth = max(w, 25); + + if (min_worth > selection[i].worth || i == 1) + min_worth = selection[i].worth; + } + break; + + /* Staff or wand */ + case 4: + goods = STICK; + + for (i = 0; i < nitems; i++) + { + selection[i].which = pick_one(ws_magic, maxsticks); + selection[i].plus1 = rnd(11) + 5; + selection[i].count = 1; + selection[i].name = ws_magic[selection[i].which].mi_name; + + switch (luck) + { + case 0: break; + case 1: + if (rnd(3) == 0) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = 1; + } + break; + + default: + if (rnd(luck)) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = 1; + } + } + + w = ws_magic[selection[i].which].mi_worth; + w += (luck + 1) * 20 * selection[i].plus1; + w = (w / 2) + (roll(6, w) / 6); + selection[i].worth = max(w, 25); + + if (min_worth > selection[i].worth || i == 1) + min_worth = selection[i].worth; + } + break; + + /* Ring */ + + case 5: + goods = RING; + for (i = 0; i < nitems; i++) + { + selection[i].which = pick_one(r_magic, maxrings); + selection[i].plus1 = rnd(2) + 1; + selection[i].count = 1; + + if (rnd(100) < r_magic[selection[i].which].mi_bless + 10) + selection[i].plus1 += rnd(2) + 1; + + selection[i].name = r_magic[selection[i].which].mi_name; + + switch (luck) + { + case 0: break; + case 1: + if (rnd(3) == 0) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = -1 - rnd(2); + } + break; + + default: + if (rnd(luck)) + { + selection[i].flags |= ISCURSED; + selection[i].plus1 = -1 - rnd(2); + } + } + + w = r_magic[selection[i].which].mi_worth; + + switch(selection[i].which) + { + case R_DIGEST: + if (selection[i].plus1 > 2) + selection[i].plus1 = 2; + else if (selection[i].plus1 < 1) + selection[i].plus1 = 1; + /* fall thru here to other cases */ + case R_ADDSTR: + case R_ADDDAM: + case R_PROTECT: + case R_ADDHIT: + case R_ADDINTEL: + case R_ADDWISDOM: + if (selection[i].plus1 > 0) + w += selection[i].plus1 * 50; + } + + w *= (1 + luck); + w = (w / 2) + (roll(6, w) / 6); + selection[i].worth = max(w, 25); + + if (min_worth > selection[i].worth * selection[i].count) + min_worth = selection[i].worth; + } + } + + /* See if player can afford an item */ + + if (min_worth > effective_purse) + { + msg("The %s eyes your small purse and departs.", + monsters[nummonst].m_name); + + /* Get rid of the monster */ + + killed(NULL, item, NOMESSAGE, NOPOINTS); + + return; + } + + /* Display the goods */ + + msg("The %s shows you his wares.", monsters[nummonst].m_name); + wstandout(cw); + mvwaddstr(cw, 0, mpos, morestr); + wstandend(cw); + wrefresh(cw); + wait_for(' '); + msg(""); + clearok(cw, TRUE); + touchwin(cw); + + wclear(hw); + touchwin(hw); + + for (i = 0; i < nitems; i++) + { + if (selection[i].worth > effective_purse) + continue; + + wmove(hw, i + 2, 0); + sprintf(dbuf, "[%c] ", ('a' + i)); + + switch(goods) + { + case ARMOR: + strcat(dbuf, "Some "); + break; + case WEAPON: + if (selection[i].count == 1) + strcat(dbuf, "A "); + else + { + sprintf(buffer, "%2d ", selection[i].count); + strcat(dbuf, buffer); + } + break; + + case STICK: + strcat(dbuf, "A "); + strcat(dbuf, ws_type[selection[i].which]); + strcat(dbuf, " of "); + break; + + case RING: + strcat(dbuf, "A ring of "); + break; + } + + strcat(dbuf, selection[i].name); + + if (selection[i].count > 1) + strcat(dbuf, "s"); + + sprintf(buffer, "%-50s Price: %d", dbuf, selection[i].worth); + waddstr(hw, buffer); + } + + sprintf(buffer, "Purse: %d", purse); + mvwaddstr(hw, nitems + 3, 0, buffer); + mvwaddstr(hw, 0, 0, "How about one of the following goods? "); + wrefresh(hw); + + /* Get rid of the monster */ + + killed(NULL, item, NOMESSAGE, NOPOINTS); + + which_item = (short) ((readchar() & 0177) - 'a'); + + while (which_item < 0 || which_item >= nitems || + selection[which_item].worth > effective_purse) + { + if (which_item == (short) ESCAPE - (short) 'a') + return; + + mvwaddstr(hw, 0, 0, "Please enter one of the listed items: "); + wrefresh(hw); + which_item = (short) ((readchar() & 0177) - 'a'); + } + + if (purse > selection[which_item].worth) + purse -= selection[which_item].worth; + else + purse = 0L; + + item = spec_item(goods, selection[which_item].which, + selection[which_item].plus1, selection[which_item].plus2); + + obj = OBJPTR(item); + + if (selection[which_item].count > 1) + { + obj->o_count = selection[which_item].count; + obj->o_group = ++group; + } + + /* If a stick or ring, let player know the type */ + + switch (goods) + { + case STICK: know_items[TYP_STICK][selection[which_item].which] = TRUE; + break; + case RING: know_items[TYP_RING][selection[which_item].which] = TRUE; + break; + } + + if (add_pack(item, MESSAGE) == FALSE) + { + obj->o_pos = hero; + fall(&player, item, TRUE, FALSE); + } +} + +void +carried_weapon(struct thing *owner, struct object *weapon) +{ + weapon->o_hplus = (rnd(4) < 3) ? 0 : (rnd(3) + 1) * ((rnd(3) < 2) ? 1 : -1); + weapon->o_dplus = (rnd(4) < 3) ? 0 : (rnd(3) + 1) * ((rnd(3) < 2) ? 1 : -1); + weapon->o_hplus += rnd(owner->t_stats.s_lvl / 3 + 1); + weapon->o_hplus += rnd(owner->t_stats.s_lvl / 3 + 1); + weapon->o_damage = weapon->o_hurldmg = "0d0"; + weapon->o_ac = 11; + weapon->o_count = 1; + weapon->o_group = 0; + + if ((weapon->o_hplus <= 0) && (weapon->o_dplus <= 0)) + weapon->o_flags = ISCURSED; + + weapon->o_flags = 0; + weapon->o_type = WEAPON; + weapon->o_mark[0] = '\0'; +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/move.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/move.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1837 @@ +/* + move.c - Hero movement commands + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +/* + do_run() + Start the hero running +*/ + +void +do_run(char ch) +{ + running = TRUE; + after = FALSE; + runch = ch; + + if (doorstop && !on(player, ISBLIND)) + { + door_stop = TRUE; + firstmove = TRUE; + } +} + +/* + step_ok() + returns true if it is ok for type to step on ch flgptr will be + NULL if we don't know what the monster is yet! +*/ + +int +step_ok(int y, int x, int can_on_monst, struct thing *flgptr) +{ + struct linked_list *item; + char ch; + + /* What is here? Don't check monster window if MONSTOK is set */ + + if (can_on_monst == MONSTOK) + ch = CCHAR( mvinch(y, x) ); + else + ch = winat(y, x); + + switch (ch) + { + case ' ': + case '|': + case '-': + case SECRETDOOR: + if (flgptr && on(*flgptr, CANINWALL)) + return(TRUE); + + return(FALSE); + + case SCROLL: + /* + * If it is a scroll, it might be a scare monster scroll so + * we need to look it up to see what type it is. + */ + + if (flgptr && flgptr->t_ctype == C_MONSTER) + { + item = find_obj(y, x); + + if (item != NULL && (OBJPTR(item))->o_type == SCROLL + && (OBJPTR(item))->o_which == S_SCARE + && rnd(flgptr->t_stats.s_intel) < 12) + return(FALSE); /* All but smart ones are scared */ + } + return(TRUE); + + default: + return(!isalpha(ch)); + } +} + +/* + corr_move() + Check to see that a move is legal. If so, return correct + character. If not, if player came from a legal place, then try to turn + him. +*/ + +void +corr_move(int dy, int dx) +{ + char ch; + short legal = 0; /* Number of legal alternatives */ + int y = 0, x = 0; /* Holds legal new position */ + int *ny, *nx; /* Point to which direction to change */ + + /* New position */ + + player.t_nxtpos.y = hero.y + dy; + player.t_nxtpos.x = hero.x + dx; + + /* A bad diagonal move is illegal */ + + if (!diag_ok(&hero, &player.t_nxtpos, &player)) + return; + + /* If it is a legal move, just return */ + + if (player.t_nxtpos.x >= 0 && player.t_nxtpos.x < COLS && player.t_nxtpos.y > 0 && player.t_nxtpos.y < LINES - 2) + { + ch = winat(player.t_nxtpos.y, player.t_nxtpos.x); + + switch (ch) + { + case ' ': + case '|': + case '-': + break; + default: + return; + } + } + + /* Check the legal alternatives */ + + if (dy == 0) + { + ny = &dy; + nx = &dx; + } + else + { + ny = &dx; + nx = &dy; + } + + for (*nx = 0, *ny = -1; *ny < 2; *ny += 2) + { + /* New position */ + player.t_nxtpos.y = hero.y + dy; + player.t_nxtpos.x = hero.x + dx; + + if (player.t_nxtpos.x < 0 || player.t_nxtpos.x > COLS - 1 || player.t_nxtpos.y < 1 || player.t_nxtpos.y > LINES - 3) + continue; + + ch = winat(player.t_nxtpos.y, player.t_nxtpos.x); + + switch (ch) + { + case ' ': + case '|': + case '-': + break; + default: + legal++; + y = dy; + x = dx; + } + } + + /* If we have 2 legal moves, make no change */ + + if (legal != 1) + return; + + /* Make the change */ + + if (y == 0) /* Move horizontally */ + { + if (x == 1) + runch = 'l'; + else + runch = 'h'; + } + else /* Move vertically */ + { + if (y == 1) + runch = 'j'; + else + runch = 'k'; + } + + return; +} + + +/* + do_move() + Check to see that a move is legal. If it is handle the + consequences (fighting, picking up, etc.) +*/ + +void +do_move(int dy, int dx) +{ + char ch; + coord old_hero; + char hch; + + firstmove = FALSE; + + if (player.t_no_move) + { + player.t_no_move--; + msg("You are still stuck in the bear trap."); + return; + } + + /* Do a confused move (maybe) */ + + if ((rnd(100) < 80 && on(player, ISHUH)) || + (is_wearing(R_DELUSION) && rnd(100) < 25) || + on(player, STUMBLER) && rnd(40) == 0) + player.t_nxtpos = rndmove(&player); + else + { + player.t_nxtpos.y = hero.y + dy; + player.t_nxtpos.x = hero.x + dx; + } + + /* + * Check if he tried to move off the screen or make an illegal + * diagonal move, and stop him if he did. + */ + + if (player.t_nxtpos.x < 0 || player.t_nxtpos.x > COLS - 1 || player.t_nxtpos.y < 1 || player.t_nxtpos.y >= LINES - 2 + || !diag_ok(&hero, &player.t_nxtpos, &player)) + { + after = fighting = running = FALSE; + return; + } + + if (running && ce(hero, player.t_nxtpos)) + after = running = FALSE; + + ch = winat(player.t_nxtpos.y, player.t_nxtpos.x); + + if (isalpha(ch)) + debug("Moving onto monster %c",ch); + + /* Take care of hero trying to move close to something frightening */ + + if (on(player, ISFLEE)) + { + if (rnd(10) < 1) + { + turn_off(player, ISFLEE); + msg("You regain your composure."); + } + else if (DISTANCE(player.t_nxtpos, player.t_chasee->t_pos) < + DISTANCE(hero,player.t_chasee->t_pos)) + return; + } + + /* Take care of hero being held */ + + if (on(player, ISHELD) && !isalpha(ch)) + { + if (rnd(pstats.s_str) > 14) + { + msg("You break free of the hold."); + + if (--hold_count == 0) + turn_off(player, ISHELD); + } + else + { + msg("You are being held."); + return; + } + } + + /* Might lose disguise */ + + if (on(player, ISDISGUISE) && rnd(11 * pstats.s_dext) == 0) + { + extinguish_fuse(FUSE_UNDISGUISE); + undisguise(NULL); + } + + /* assume he's not in a wall */ + + if (!isalpha(ch)) + turn_off(player, ISINWALL); + + hch = CCHAR( mvinch(hero.y, hero.x) ); /* Where hero was */ + old_hero = hero; /* Save hero's old position */ + + switch (ch) + { + case ' ': + case '|': + case '-': + case SECRETDOOR: + if (off(player, CANINWALL)) + { + after = running = FALSE; + return; + } + else if (running) + { + after = running = FALSE; + return; + } + turn_on(player, ISINWALL); + break; + + case TRAPDOOR: + case TELTRAP: + case BEARTRAP: + case SLEEPTRAP: + case ARROWTRAP: + case DARTTRAP: + case POOL: + case MAZETRAP: + case FIRETRAP: + case POISONTRAP: + case LAIR: + case RUSTTRAP: + ch = be_trapped(&player, player.t_nxtpos); + + if (!is_wearing(R_LEVITATION) && off(player, CANFLY) && + (old_hero.x != hero.x || old_hero.y != hero.y + || pool_teleport)) + { + pool_teleport = FALSE; + return; + } + + break; + + case GOLD: + case POTION: + case SCROLL: + case FOOD: + case WEAPON: + case ARMOR: + case RING: + case ARTIFACT: + case STICK: + running = FALSE; + take = ch; + break; + + default: + break; + } + + if (ch == FIRETRAP) + light(&hero); + + hero = player.t_nxtpos; /* Move the hero */ + + /* adjust lighting */ + + if (roomin(hero) == NULL && (hch == '-' || hch == '|' || + hch == DOOR || hch == SECRETDOOR)) + { + /* Leaving a room -- darken it */ + struct room *rp = roomin(old_hero); + int is_lit = FALSE; + + if (!(rp->r_flags & ISDARK)) + is_lit = TRUE; + + rp->r_flags |= ISDARK; /* Fake darkness */ + light(&old_hero); + + if (is_lit) + rp->r_flags &= ~ISDARK; /* Restore light state */ + } + else if (ch == DOOR || ch == SECRETDOOR || ch == '|' || ch == '-') + { + /* Entering a room */ + running = FALSE; + if (hch != '|' && hch != '-') + light(&hero); /* knows whether the hero can see things in */ + } + + /* handle other situations */ + + if (ch == STAIRS) + running = FALSE; + else if (ch == POST) + { + running = FALSE; + new_level(POSTLEV,0); + return; + } + else if (isalpha(ch)) + { + struct linked_list *mp; + struct thing *tp; + char t; + + running = FALSE; + + mp = find_mons(hero.y, hero.x); + + if (mp == NULL) + return; + + tp = THINGPTR(mp); + + if (good_monster(*tp)) /* Exchange places with your buddy */ + { + mvwaddch(cw, old_hero.y, old_hero.x, ch); + mvwaddch(mw, old_hero.y, old_hero.x, ch); + mvwaddch(mw, hero.y, hero.x, ' '); + mvwaddch(cw, hero.y, hero.x, tp->t_oldch); + + (*tp).t_pos.x = old_hero.x; /* Update monster position variables */ + (*tp).t_pos.y = old_hero.y; + (*tp).t_oldpos.x = old_hero.x; + (*tp).t_oldpos.y = old_hero.y; + + t = (*tp).t_oldch; + (*tp).t_oldch = player.t_oldch; + player.t_oldch = t; + + turn_on(*tp, ISRUN); + + mvwaddch(cw, hero.y, hero.x, PLAYER); + + /* make sure that the room shows OK */ + + light(&hero); + + wrefresh(cw); + return; + } + else + { + hero = old_hero; /* Restore hero -- we'll fight instead of move */ + + /* make sure that the room shows OK */ + light(&hero); + + fight(&player.t_nxtpos, cur_weapon, NOTHROWN); + + return; + } + } + else + fighting = FALSE; + + ch = winat(old_hero.y, old_hero.x); + mvwaddch(cw, old_hero.y, old_hero.x, ch); + mvwaddch(cw, hero.y, hero.x, PLAYER); +} + +/* + light() + Called to illuminate a room. If it is dark, remove anything that might + move. +*/ + +void +light(coord *cp) +{ + struct room *rp; + int j, k, x, y; + char ch, rch; + struct linked_list *item; + int jlow, jhigh, klow, khigh; /* Boundaries of lit area */ + + if ((rp = roomin(*cp)) != NULL && !on(player, ISBLIND)) + { + + /* is he wearing ring of illumination and in same room? */ + + if ((is_wearing(R_LIGHT) || on(player, ISELECTRIC)) && + cp == &hero) + rp->r_flags &= ~ISDARK; + + /* If we are in a maze, don't look at the whole room (level) */ + + if (levtype == MAZELEV) + { + jlow = max(0, hero.y - 2 - rp->r_pos.y); + jhigh = min(rp->r_max.y, hero.y + 2 - rp->r_pos.y + 1); + klow = max(0, hero.x - 2 - rp->r_pos.x); + khigh = min(rp->r_max.x, hero.x + 2 - rp->r_pos.x + 1); + } + else + { + jlow = klow = 0; + jhigh = rp->r_max.y; + khigh = rp->r_max.x; + } + + for (j = 0; j < rp->r_max.y; j++) + { + for (k = 0; k < rp->r_max.x; k++) + { + /* Is this in the given area -- needed for maze */ + + if ((j < jlow || j >= jhigh) && (k < klow || k >= khigh)) + continue; + + y = rp->r_pos.y + j; + x = rp->r_pos.x + k; + + ch = show(y, x); + wmove(cw, y, x); + + /* Figure out how to display a secret door */ + + if (ch == SECRETDOOR) + { + if (j == 0 || j == rp->r_max.y - 1) + ch = '-'; + else + ch = '|'; + } + + /* + * For monsters, if they were previously not + * seen and now can be seen, or vice-versa, + * make sure that will happen. + */ + + if (isalpha(ch)) + { + struct thing *tp; + + item = wake_monster(y, x); + + if (item == NULL) + continue; + + tp = THINGPTR(item); + + /* Previously not seen -- now can see it */ + + if (tp->t_oldch == ' ' && cansee(tp->t_pos.y, tp->t_pos.x)) + tp->t_oldch = CCHAR( mvinch(y, x) ); + + /* Previously seen -- now can't see it */ + + else if (off(player, ISBLIND) && tp->t_oldch != ' ' && + !cansee(tp->t_pos.y, tp->t_pos.x)) + tp->t_oldch = ' '; + } + + /* + * If the room is a dark room, we might want + * to remove monsters and the like from it + * (since they might move). A dark room or + * not in line-of-sight in a maze. + */ + + if (((rp->r_flags & ISDARK) && + !(rp->r_flags & HASFIRE)) || + (levtype == MAZELEV && + !maze_view(y, x))) + { + rch = CCHAR( mvwinch(cw, y, x) ); + + switch (rch) + { + case DOOR: + case STAIRS: + case TRAPDOOR: + case TELTRAP: + case BEARTRAP: + case SLEEPTRAP: + case ARROWTRAP: + case DARTTRAP: + case POOL: + case MAZETRAP: + case FIRETRAP: + case POISONTRAP: + case LAIR: + case RUSTTRAP: + case POST: + case '|': + case '-': + case ' ': + ch = rch; + break; + + case FLOOR: + ch = (on(player, ISBLIND) ? FLOOR : ' '); + break; + default: + ch = ' '; + break; + } + } + mvwaddch(cw, y, x, ch); + } + } + } +} + +/* + blue_light() + magically light up a room (or level or make it dark) +*/ + +int +blue_light(int flags) +{ + struct room *rp; + int blessed = (flags & ISBLESSED); + int cursed = (flags & ISCURSED); + int ret_val = FALSE; /* Whether or not affect is known */ + + rp = roomin(hero); /* What room is hero in? */ + + /* Darken the room if the magic is cursed */ + + if (cursed) + { + if ((rp == NULL) || (rp->r_flags & ISDARK)) + nothing_message(flags); + else + { + if (!(rp->r_flags & HASFIRE)) + msg("The room suddenly goes dark."); + else + nothing_message(flags); + + rp->r_flags |= ISDARK; + ret_val = TRUE; + } + } + else + { + ret_val = TRUE; + + if (rp && (rp->r_flags & ISDARK) && !(rp->r_flags & HASFIRE)) + { + msg("The room is lit by a %s blue light.", + blessed ? "bright" : "shimmering"); + } + else if (winat(hero.y, hero.x) == PASSAGE) + msg("The corridor glows %sand then fades.", blessed ? "brightly " : ""); + else + { + ret_val = FALSE; + nothing_message(flags); + } + + if (blessed) + { + short i; /* Index through rooms */ + + for (i = 0; i < MAXROOMS; i++) + rooms[i].r_flags &= ~ISDARK; + } + else if (rp) + rp->r_flags &= ~ISDARK; + } + + /* Light the room and put the player back up */ + + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + + return (ret_val); +} + +/* + show() + returns what a certain thing will display as to the un-initiated +*/ + +char +show(int y, int x) +{ + char ch = winat(y, x); + struct linked_list *it; + struct thing *tp; + + if (isatrap(ch)) + { + struct trap *trp = trap_at(y, x); + return (trp->tr_flags & ISFOUND) ? ch : trp->tr_show; + } + else if (isalpha(ch)) + { + if ((it = find_mons(y, x)) == NULL) + { + debug("Can't find monster in move."); + return ' '; + } + tp = THINGPTR(it); + + if (on(*tp, ISDISGUISE)) + ch = tp->t_disguise; /* As a mimic */ + else if (on(*tp, ISINVIS) || (on(*tp, ISSHADOW) && + rnd(100) < 90) || on(*tp, CANSURPRISE)) + { + if (off(player, CANSEE) || on(*tp, CANSURPRISE)) + ch = CCHAR( mvwinch(stdscr, y, x) ); /* Invisible */ + } + else if (on(*tp, CANINWALL)) + { + char tch; + + tch = CCHAR( mvwinch(stdscr, y, x) ); + + if (tch == WALL || tch == '-' || tch == '|') + ch = CCHAR( winch(stdscr) ); /* As Xorn */ + } + } + return(ch); +} + +/* + be_trapped() + The guy stepped on a trap.... Make him pay. +*/ + +char +be_trapped(struct thing *th, coord tc) +{ + struct trap *tp; + char ch, *mname = NULL; + int is_player = (th == &player), can_see = cansee(tc.y, tc.x); + struct linked_list *mitem = NULL; + + tp = trap_at(tc.y, tc.x); + ch = tp->tr_type; + + if (!is_player) + { + mitem = find_mons(th->t_pos.y, th->t_pos.x); + mname = monsters[th->t_index].m_name; + + /* Flying monsters do not set off traps */ + + if (!mitem || (on(*th, CANFLY) && + (ch == BEARTRAP || ch == MAZETRAP || ch == TRAPDOOR + || ch == ARROWTRAP || ch == DARTTRAP))) + { + debug("%s avoided trap.", mname); + return(ch); + } + } + else + { + short thief_bonus = -50; + + count = running = FALSE; + mvwaddch(cw, tp->tr_pos.y, tp->tr_pos.x, tp->tr_type); + + if (no_command) + return(ch); + + if (player.t_ctype == C_THIEF || player.t_ctype == C_NINJA) + thief_bonus = 10; + + if (((is_wearing(R_LEVITATION) || on(player, CANFLY)) && + (ch != FIRETRAP || + (ch == FIRETRAP && !(tp->tr_flags & ISFOUND)))) + || (moving && (tp->tr_flags & ISFOUND) && rnd(100) < + thief_bonus + 2 * pstats.s_dext + 5 * pstats.s_lvl) && + (ch == BEARTRAP || ch == MAZETRAP || ch == TRAPDOOR + || ch == ARROWTRAP || ch == DARTTRAP)) + { + static char trname[1024]; + msg(tr_name(ch,trname)); + tp->tr_flags |= ISFOUND; + return(ch); + } + + if (moving) + msg("Your attempt fails."); + } + + tp->tr_flags |= ISFOUND; + + switch(ch) + { + case TRAPDOOR: + if (is_player) + { + level++; + new_level(NORMLEV,0); + addmsg("You fell into a trap"); + + if (player.t_ctype != C_THIEF + && player.t_ctype != C_ASSASIN + && player.t_ctype != C_NINJA + && rnd(pstats.s_dext) < 4) + { + addmsg(" and were damaged by the fall"); + + if ((pstats.s_hpt -= roll(1, 6)) <= 0) + { + addmsg("! The fall killed you."); + endmsg(); + death(D_FALL); + + return(ch); + } + } + + addmsg("!"); + endmsg(); + + if (off(player, ISCLEAR) && rnd(4) < 3) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE,0,rnd(8)+HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + } + else + { + if (can_see) + msg("The %s fell into a trap!", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL,mitem,NOMESSAGE,NOPOINTS); + } + break; + + case BEARTRAP: + if (is_stealth(th)) + { + if (is_player) + msg("You pass a bear trap."); + else if (can_see) + msg("The %s passes a bear trap.", mname); + } + else + { + th->t_no_move += BEARTIME; + + if (is_player) + msg("You are caught in a bear trap."); + else if (can_see) + msg("The %s is caught in a bear trap.", mname); + } + break; + + case SLEEPTRAP: + if (is_player) + { + msg("A strange white mist envelops you."); + + if (!is_wearing(R_ALERT)) + { + if (!is_wearing(R_BREATHE) && off(player, HASOXYGEN)) + { + msg("You fall asleep."); + no_command += SLEEPTIME; + } + } + } + else + { + if (can_see) + msg("A strange white mist envelops the %s.", mname); + + if (on(*th, ISUNDEAD)) + { + if (can_see) + msg("The mist doesn't seem to affect the %s.", mname); + } + + if (on(*th, ISUNDEAD) || on(*th, HASOXYGEN)) + { + if (can_see) + msg("The mist doesn't seem to affect the %s.", mname); + } + else + { + th->t_no_move += SLEEPTIME; + } + } + break; + + case ARROWTRAP: + if (swing(th->t_ctype, th->t_stats.s_lvl - 1, th->t_stats.s_arm, 1)) + { + if (is_player) + { + msg("Oh no! An arrow shot you."); + + if ((pstats.s_hpt -= roll(1, 6)) <= 0) + { + msg("The arrow killed you."); + death(D_ARROW); + return(ch); + } + } + else + { + if (can_see) + msg("An arrow shot the %s.", mname); + + if (on(*th, NOSHARP)) + { + if (can_see) + msg("The arrow has no effect!"); + } + else + { + if ((th->t_stats.s_hpt -= roll(1, 6)) <= 0) + { + if (can_see) + msg("The arrow killed the %s.", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + } + } + } + else + { + struct linked_list *itm; + struct object *an_arrow; + + if (is_player) + msg("An arrow shoots past you."); + else if (can_see) + msg("An arrow shoots by the %s.", mname); + + itm = new_item(sizeof *an_arrow); + an_arrow = OBJPTR(itm); + an_arrow->o_type = WEAPON; + an_arrow->o_which = ARROW; + an_arrow->o_hplus = rnd(3) - 1; + an_arrow->o_dplus = rnd(3) - 1; + init_weapon(an_arrow, ARROW); + an_arrow->o_count = 1; + an_arrow->o_pos = tc; + an_arrow->o_mark[0] = '\0'; + fall(&player, itm, FALSE, FALSE); + } + break; + + case TELTRAP: + if (is_player) + { + teleport(); + + if (off(player, ISCLEAR)) + { + msg("Wait, what's going on here. Huh? What? Who?"); + + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE,0,rnd(8)+HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + } + else + { + int rm; + + /* Erase the monster from the old position */ + + if (isalpha(mvwinch(cw, th->t_pos.y, th->t_pos.x))) + mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); + + mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); + + /* Get a new position */ + + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &th->t_pos); + } + while(winat(th->t_pos.y, th->t_pos.x) != FLOOR); + + /* Put it there */ + + mvwaddch(mw, th->t_pos.y, th->t_pos.x, th->t_type); + th->t_oldch = CCHAR( mvwinch(cw, th->t_pos.y, th->t_pos.x) ); + + if (can_see) + msg("The %s seems to have disappeared!", mname); + } + break; + + case DARTTRAP: + + if (swing(th->t_ctype, th->t_stats.s_lvl + 1,th->t_stats.s_arm, 1)) + { + if (is_player) + { + msg("A small dart just hit you in the shoulder."); + + if ((pstats.s_hpt -= roll(1, 4)) <= 0) + { + msg("The dart killed you."); + death(D_DART); + return(ch); + } + + /* Now the poison */ + + if (player.t_ctype != C_PALADIN + && !(player.t_ctype == C_NINJA && + pstats.s_lvl > 12) && !save(VS_POISON)) + { + + /* + * 75% chance it will do point + * damage - else strength + */ + + if (rnd(100) < 75) + { + pstats.s_hpt /= 2; + + if (pstats.s_hpt == 0) + { + death(D_POISON); + return(ch); + } + } + else if (!is_wearing(R_SUSABILITY)) + chg_str(-1, FALSE, FALSE); + } + } + else + { + int orig_hp = th->t_stats.s_hpt; + + if (can_see) + msg("A small dart just hit the %s.", mname); + + /* + * Poison has no effect on poisonous or + * undead monsters + */ + + if (off(*th, CANPOISON) && + off(*th, ISUNDEAD) && + !save_throw(VS_POISON, th)) + th->t_stats.s_hpt /= 2; + + /* Now the dart damage */ + + if (off(*th, NOSHARP)) + th->t_stats.s_hpt -= roll(1, 4); + + if (orig_hp == th->t_stats.s_hpt) + if (can_see) + msg("The dart has not effect!"); + else if (th->t_stats.s_hpt < 0) + { + if (can_see) + msg("The dart killed the %s.", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + } + } + else + { + if (is_player) + msg("A small dart whizzes by your ear and vanishes."); + else if (can_see) + msg("A small dart whizzes by the %s and vanishes.", mname); + } + break; + + case POOL: + { + int i; + + i = rnd(100); + + if (is_player) + { + if (on(player, ISELECTRIC)) + { + msg("Oh no!!! The water shorts you out"); + extinguish_fuse(FUSE_UNELECTRIFY); + turn_off(player, ISELECTRIC); + + if (!is_wearing(R_ELECTRESIST)) + { + if ((pstats.s_hpt -= roll(1, 10)) <= 0) + { + addmsg("! The shock killed you."); + endmsg(); + death(D_DROWN); + return(ch); + } + } + } + + if ((tp->tr_flags & ISGONE)) + { + if (i < 30) + { + teleport(); /* teleport away */ + + if (off(player, ISCLEAR)) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE,rnd(8)+HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE,0,rnd(8)+HUHDURATION,AFTER); + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + + pool_teleport = TRUE; + } + else if ((i < 45) && level > 2) + { + level -= rnd(2) + 1; + new_level(NORMLEV,0); + pool_teleport = TRUE; + msg("You here a faint groan from below."); + + if (off(player, ISCLEAR)) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE,rnd(8)+HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + } + else if (i < 70) + { + level += rnd(4) + 1; + new_level(NORMLEV,0); + pool_teleport = TRUE; + msg("You find yourself in strange surroundings."); + + if (off(player, ISCLEAR)) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + } + else if (i > 95) + { + if (is_wearing(R_BREATHE) || on(player, HASOXYGEN)) + msg("You splash in the pool unharmed."); + else + { + msg("Oh no!!! You drown in the pool!!! --More--"); + wait_for(' '); + death(D_DROWN); + return(ch); + } + } + } + } + else + { + if (can_see) + msg("The %s fell into the pool!", mname); + + if (i < 15) + { + if (off(*th, HASOXYGEN)) + { + if (can_see) + msg("The %s has drowned!", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + } + } + } + break; + + case MAZETRAP: + + if (is_player) + { + level++; + new_level(MAZELEV,0); + addmsg("You are surrounded by twisty passages"); + + if (rnd(4) < 1) + { + addmsg(" and were damaged by the fall"); + + if ((pstats.s_hpt -= roll(1, 6)) <= 0) + { + addmsg("! The fall killed you."); + endmsg(); + death(D_FALL); + return(ch); + } + } + + addmsg("!"); + endmsg(); + + if (off(player, ISCLEAR)) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + { + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + turn_on(player, ISHUH); + } + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + } + else + { + if (can_see) + msg("The %s fell into a trap!", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + break; + + case FIRETRAP: + { + struct room *rp = roomin(hero); + + if (is_player) + { + if (is_wearing(R_FIRERESIST) || on(player, NOFIRE)) + msg("You pass through the flames unharmed."); + else + { + addmsg("You are burned by the flames"); + + if ((pstats.s_hpt -= roll(pstats.s_lvl, 2)) <= 0) + { + addmsg("! The flames killed you."); + endmsg(); + death(D_FIRE); + return(ch); + } + + addmsg("!"); + + endmsg(); + } + } + else + { + if (on(*th, CANBBURN)) + { + if (can_see) + msg("The %s is burned to death by the flames.", mname); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + else if (on(*th, NOFIRE)) + { + if (can_see) + msg("The %s passes through the flames unharmed.", mname); + } + else + { + if (can_see) + msg("The %s is burned by the flames.", mname); + + if ((th->t_stats.s_hpt -= roll(th->t_stats.s_lvl, 3)) < 0) + { + if (can_see) + msg("The %s is burned to death by the flames.", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + else if (th->t_stats.s_intel < rnd(20)) + { + if (can_see) + msg("The %s turns and runs away in fear.", mname); + + turn_on(*th, ISFLEE); + } + } + } + + if (rp != NULL) + { + rp->r_flags &= ~ISDARK; + light(&hero); + } + } + break; + + case POISONTRAP: + if (is_player) + { + msg("You fall into a pool of poison."); + + if (rnd(4) > 0) + { + msg("You swallow some of the liquid and feel very sick."); + pstats.s_hpt -= pstats.s_hpt / 3; + + if (player.t_ctype != C_PALADIN + && !(player.t_ctype == C_NINJA && + pstats.s_lvl > 12) + && !is_wearing(R_SUSABILITY)) + chg_str(-2, FALSE, FALSE); + } + else + msg("The stuff tastes horrible."); + } + else + { + if (can_see) + msg("The %s falls into the pool of poison.", mname); + + if (rnd(4) > 0 && off(*th, ISUNDEAD)) + if (th->t_stats.s_hpt *= 2.0 / 3.0 < 0) + { + if (can_see) + msg("The %s dies from the poison.", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + } + break; + + case LAIR: + if (is_player) + { + msg("You found a monster lair!"); + mpos = 0; + new_level(THRONE,0); + } + else + { + if (can_see) + msg("The %s fell into a trap!", mname); + + if (on(*th, ISFAMILIAR)) + turn_off(player, HASFAMILIAR); + + killed(NULL, mitem, NOMESSAGE, NOPOINTS); + } + break; + + case RUSTTRAP: + if (is_player) + { + msg("You are splashed by water."); + + if (cur_armor != NULL && + cur_armor->o_which != SOFT_LEATHER && + cur_armor->o_which != HEAVY_LEATHER && + cur_armor->o_which != CUIRBOLILLI && + cur_armor->o_which != PADDED_ARMOR && + cur_armor->o_which != CRYSTAL_ARMOR && + cur_armor->o_which != MITHRIL && + !(cur_armor->o_flags & ISPROT) && + cur_armor->o_ac < pstats.s_arm + 1) + { + msg("Your armor weakens!"); + cur_armor->o_ac++; + } + else if (cur_armor != NULL && (cur_armor->o_flags & ISPROT)) + msg("The rust vanishes instantly!"); + } + else + { + if (can_see) + msg("The %s is splashed by water.", mname); + } + } + + return(ch); +} + +/* + dip_it() + Dip an object into a magic pool +*/ + +void +dip_it(void) +{ + struct linked_list *what; + struct object *ob; + struct trap *tp; + int wh, i; + + tp = trap_at(hero.y, hero.x); + + if (tp == NULL || !(tp->tr_type == POOL || tp->tr_type == POISONTRAP)) + { + msg("I see no pools here."); + return; + } + + if (tp->tr_flags & ISGONE) + { + msg("This %s appears to have been used once already.", + (tp->tr_type == POOL ? "shimmering pool" : "poison pool")); + return; + } + + if ((what = get_item("dip", 0)) == NULL) + { + msg(""); + after = FALSE; + return; + } + + ob = OBJPTR(what); + mpos = 0; + + if (ob == cur_armor) + { + msg("You have to take off your armor before you can dip it."); + return; + } + else if (ob == cur_ring[LEFT_1] || ob == cur_ring[LEFT_2] || + ob == cur_ring[LEFT_3] || ob == cur_ring[LEFT_4] || + ob == cur_ring[RIGHT_1] || ob == cur_ring[RIGHT_2] || + ob == cur_ring[RIGHT_3] || ob == cur_ring[RIGHT_4]) + { + msg("You have to take that ring off before you can dip it."); + return; + } + + tp->tr_flags |= ISGONE; + + if (ob != NULL && tp->tr_type == POOL) + { + wh = ob->o_which; + ob->o_flags |= ISKNOW; + i = rnd(100); + + switch (ob->o_type) + { + case WEAPON: + if (i < 50) + { + if (!(ob->o_flags & ISCURSED)) + { + ob->o_hplus += 1; + ob->o_dplus += 1; + } + else + { + ob->o_hplus = rnd(2); + ob->o_dplus = rnd(2); + } + + ob->o_flags &= ~ISCURSED; + msg("The %s glows blue for a moment.", weaps[wh].w_name); + } + else if (i < 70) /* curse weapon here */ + { + if (!(ob->o_flags & ISCURSED)) + { + ob->o_hplus = -(rnd(2) + 1); + ob->o_dplus = -(rnd(2) + 1); + } + else /* if already cursed */ + { + ob->o_hplus--; + ob->o_dplus--; + } + + ob->o_flags |= ISCURSED; + msg("The %s glows red for a moment.", weaps[wh].w_name); + } + else + msg("Nothing seems to happen."); + break; + + case ARMOR: + + if (i < 50) /* enchant armor */ + { + if (!(ob->o_flags & ISCURSED)) + ob->o_ac -= rnd(2) + 1; + else + ob->o_ac = -rnd(3) + armors[wh].a_class; + + ob->o_flags &= ~ISCURSED; + msg("The %s glows blue for a moment.", armors[wh].a_name); + } + else if (i < 75) /* curse armor */ + { + if (!(ob->o_flags & ISCURSED)) + ob->o_ac = rnd(3) + armors[wh].a_class; + else + ob->o_ac += rnd(2) + 1; + + ob->o_flags |= ISCURSED; + msg("The %s glows red for a moment.", armors[wh].a_name); + } + else + msg("Nothing seems to happen"); + break; + + case STICK: + { + int j; + + j = rnd(8) + 1; + + if (i < 50) /* add charges */ + { + ob->o_charges += j; + know_items[TYP_STICK][wh] = TRUE; + + if (ob->o_flags & ISCURSED) + ob->o_flags &= ~ISCURSED; + + msg("The %s %s glows blue for a moment.", + ws_made[wh], ws_type[wh]); + } + else if (i < 65) /* remove charges */ + { + if ((ob->o_charges -= i) < 0) + ob->o_charges = 0; + + know_items[TYP_STICK][wh] = TRUE; + + if (ob->o_flags & ISBLESSED) + ob->o_flags &= ~ISBLESSED; + else + ob->o_flags |= ISCURSED; + + msg("The %s %s glows red for a moment.", + ws_made[wh], ws_type[wh]); + } + else + msg("Nothing seems to happen."); + } + break; + + case SCROLL: + + know_items[TYP_SCROLL][wh] = TRUE; + msg("The '%s' scroll unfurls.", s_names[wh]); + break; + + case POTION: + + know_items[TYP_POTION][wh] = TRUE; + msg("The %s potion bubbles for a moment.", p_colors[wh]); + break; + + case RING: + if (i < 50) /* enchant ring */ + { + if (!(ob->o_flags & ISCURSED)) + ob->o_ac += rnd(2) + 1; + else + ob->o_ac = rnd(2) + 1; + + ob->o_flags &= ~ISCURSED; + } + else if (i < 80) /* curse ring */ + { + if (!(ob->o_flags & ISCURSED)) + ob->o_ac = -(rnd(2) + 1); + else + ob->o_ac -= (rnd(2) + 1); + + ob->o_flags |= ISCURSED; + } + + know_items[TYP_RING][wh] = TRUE; + msg("The %s ring vibrates for a moment.", r_stones[wh]); + break; + + default: + msg("The pool bubbles for a moment."); + } + } + else if (ob != NULL && tp->tr_type == POISONTRAP) + { + if ((player.t_ctype == C_PALADIN) || + (player.t_ctype == C_CLERIC && rnd(2))) + { + msg("Trying to use poison is evil."); + luck += 2; + } + + if (ob->o_type != WEAPON || rnd(10) > 0) + msg("Nothing seems to happen."); + else + { + msg("Your %s is covered with a black sticky liquid.", + weaps[ob->o_which].w_name); + ob->o_flags |= ISPOISON; + } + } + else + msg("Nothing seems to happen."); +} + +/* + trap_at() + find the trap at (y,x) on screen. +*/ + +struct trap * +trap_at(int y, int x) +{ + struct trap *tp, *ep; + + ep = &traps[ntraps]; + + for (tp = traps; tp < ep; tp++) + if (tp->tr_pos.y == y && tp->tr_pos.x == x) + break; + + if (tp == ep) + { + debug((sprintf(prbuf, "Trap at %d,%d not in array", y, x), prbuf)); + tp = NULL; + } + + return(tp); +} + +/* + set_trap() + set a trap at (y, x) on screen. +*/ + +void +set_trap(struct thing *tp, int y, int x) +{ + int is_player = (tp == &player); + int selection = rnd(7) + 1; + char ch = 0, och; + int thief_bonus = 0; + + switch(och = CCHAR( mvinch(y, x) )) + { + case WALL: + case FLOOR: + case PASSAGE: + break; + default: + msg("The trap failed!"); + return; + } + + if (is_player && (player.t_ctype == C_THIEF || + player.t_ctype == C_NINJA)) + thief_bonus = 10; + + if (ntraps >= 2 * MAXTRAPS || ++trap_tries >= MAXTRPTRY || + rnd(60) >= (tp->t_stats.s_dext + tp->t_stats.s_lvl / 2 + + thief_bonus)) + { + if (is_player) + msg("The trap failed!"); + + return; + } + + /* Set up for redraw */ + + clearok(cw, TRUE); + touchwin(cw); + + if (is_player) + { + add_line("[1] Trap Door"); + add_line("[2] Bear Trap"); + add_line("[3] Sleep Trap"); + add_line("[4] Arrow Trap"); + add_line("[5] Teleport Trap"); + add_line("[6] Dart Trap"); + add_line("[7] Fire Trap"); + end_line(); + msg("Which trap? "); + + selection = (short) ((readchar() & 0177) - '0'); + + while (selection < 1 || selection > 7) + { + if (selection == (short) ESCAPE - (short) '0') + { + after = FALSE; + return; + } + + msg(""); + msg("Please enter a selection between 1 and 7: "); + selection = (short) ((readchar() & 0177) - '0'); + } + } + + switch (selection) + { + case 1: ch = TRAPDOOR; break; + case 2: ch = BEARTRAP; break; + case 3: ch = SLEEPTRAP;break; + case 4: ch = ARROWTRAP;break; + case 5: ch = TELTRAP; break; + case 6: ch = DARTTRAP; break; + case 7: ch = FIRETRAP; break; + } + + mvaddch(y, x, ch); + + traps[ntraps].tr_type = ch; + traps[ntraps].tr_flags = ISTHIEFSET; + traps[ntraps].tr_show = och; + traps[ntraps].tr_pos.y = y; + traps[ntraps++].tr_pos.x = x; +} + + +/* + rndmove() + move in a random direction if the monster/person is confused +*/ + +coord +rndmove(struct thing *who) +{ + int x, y; + int ex, ey, nopen = 0; + coord ret; /* what we will be returning */ + coord dest; + + ret = who->t_pos; + + /* + * Now go through the spaces surrounding the player and set that + * place in the array to true if the space can be moved into + */ + + ey = ret.y + 1; + ex = ret.x + 1; + + for (y = who->t_pos.y - 1; y <= ey; y++) + if (y > 0 && y < LINES - 2) + for (x = who->t_pos.x - 1; x <= ex; x++) + { + if (x < 0 || x >= COLS) + continue; + + if (step_ok(y, x, NOMONST, who)) + { + dest.y = y; + dest.x = x; + + if (!diag_ok(&who->t_pos, &dest, who)) + continue; + + if (rnd(++nopen) == 0) + ret = dest; + } + } + + return(ret); +} + +/* + isatrap() + Returns TRUE if this character is some kind of trap +*/ + +int +isatrap(int ch) +{ + switch(ch) + { + case DARTTRAP: + case TELTRAP: + case TRAPDOOR: + case ARROWTRAP: + case SLEEPTRAP: + case POOL: + case MAZETRAP: + case FIRETRAP: + case POISONTRAP: + case LAIR: + case RUSTTRAP: + case BEARTRAP: + return (TRUE); + default: + return (FALSE); + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/newlvl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/newlvl.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,676 @@ +/* + newlvl.c - Dig and draw a new level + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/* + Notes + + Add treasure room code from Rogue 5.2, + put in #ifdef 0/#endif bracket at end of code +*/ + +#include "rogue.h" + +/* + new_level() + Dig and draw a new level +*/ + +void +new_level(LEVTYPE ltype, int special) +{ + int rm, i, cnt; + struct linked_list *item, *nitem; + struct thing *tp; + struct linked_list *fpack = NULL; + int going_down = TRUE; + coord stairs; + + /* Start player off right */ + + turn_off(player, ISHELD); + turn_off(player, ISFLEE); + extinguish_fuse(FUSE_SUFFOCATE); + hold_count = 0; + trap_tries = 0; + no_food++; + + if (level >= max_level) + max_level = level; + else + going_down = FALSE; + + /* Free up the monsters on the last level */ + + if (fam_ptr != NULL) /* save what familiar is carrying */ + { + fpack = (THINGPTR(fam_ptr))->t_pack; + (THINGPTR(fam_ptr))->t_pack = NULL; + fam_ptr = NULL; /* just in case */ + } + + for (i = 1; i <= mons_summoned; i++) + extinguish_fuse(FUSE_UNSUMMON); + + mons_summoned = 0; + + for (item = mlist; item != NULL; item = nitem) + { + tp = THINGPTR(item); + nitem = next(item); + + if (on(*tp, ISUNIQUE)) /* Put alive UNIQUE on next level */ + monsters[tp->t_index].m_normal = TRUE; + + killed(NULL, item, NOMESSAGE, NOPOINTS); + } + + free_list(lvl_obj); /* Free up previous objects (if any) */ + + wclear(cw); + wclear(mw); + clear(); + refresh(); + levtype = ltype; + + switch (ltype) + { + case THRONE: + do_throne(special); /* do monster throne stuff */ + break; + + case MAZELEV: + do_maze(); + break; + + case POSTLEV: + do_post(); + levtype = ltype = NORMLEV; + level++; + + default: + do_rooms(); /* Draw rooms */ + do_passages(); /* Draw passages */ + break; + } + + /* Place the staircase down. */ + + cnt = 0; + + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &stairs); + } + while (!(mvinch(stairs.y, stairs.x) == FLOOR || cnt++ > 5000)); + + addch(STAIRS); + + put_things(ltype); /* Place objects (if any) */ + + if (has_artifact && level == 1) + create_lucifer(&stairs); + + /* Place the traps */ + + ntraps = 0; /* No traps yet */ + + if (levtype == NORMLEV) + { + if (rnd(10) < level) + { + char ch = 0; + + i = ntraps = min(MAXTRAPS, rnd(level / 2) + 1); + + /* maybe a lair */ + + if (level > 35 && ltype == NORMLEV && rnd(wizard ? 3 : 10) == 0) + { + cnt = 0; + + do + { + rm = rnd_room(); + + if (rooms[rm].r_flags & ISTREAS) + continue; + + rnd_pos(&rooms[rm], &stairs); + } + while (!(mvinch(stairs.y, stairs.x) == FLOOR || cnt++ > 5000)); + + addch(LAIR); + i--; + traps[i].tr_flags = 0; + traps[i].tr_type = LAIR; + traps[i].tr_show = FLOOR; + traps[i].tr_pos = stairs; + } + + while (i--) + { + cnt = 0; + + do + { + rm = rnd_room(); + + if (rooms[rm].r_flags & ISTREAS) + continue; + + rnd_pos(&rooms[rm], &stairs); + } + while (!(mvinch(stairs.y, stairs.x) == FLOOR || cnt++ > 5000)); + + traps[i].tr_flags = 0; + + switch (rnd(11)) + { + case 0: + ch = TRAPDOOR; + break; + + case 1: + ch = BEARTRAP; + break; + + case 2: + ch = SLEEPTRAP; + break; + + case 3: + ch = ARROWTRAP; + break; + + case 4: + ch = TELTRAP; + break; + + case 5: + ch = DARTTRAP; + break; + + case 6: + ch = POOL; + + if (rnd(10)) + traps[i].tr_flags = ISFOUND; + + break; + + case 7: + ch = MAZETRAP; + break; + + case 8: + ch = FIRETRAP; + break; + + case 9: + ch = POISONTRAP; + break; + + case 10: + ch = RUSTTRAP; + break; + } + + addch(ch); + traps[i].tr_type = ch; + traps[i].tr_show = FLOOR; + traps[i].tr_pos = stairs; + } + } + } + + do /* Position hero */ + { + rm = rnd_room(); + + if (levtype != THRONE && (rooms[rm].r_flags & ISTREAS)) + continue; + + rnd_pos(&rooms[rm], &hero); + } + while(!(winat(hero.y, hero.x) == FLOOR && + DISTANCE(hero, stairs) > 16)); + + oldrp = &rooms[rm]; /* Set the current room */ + player.t_oldpos = player.t_pos; /* Set the current position */ + + if (levtype != POSTLEV && levtype != THRONE) + { + if (on(player, BLESSMAP) && rnd(5) == 0) + { + read_scroll(&player, S_MAP, ISNORMAL); + + if (rnd(3) == 0) + turn_off(player, BLESSMAP); + } + + if (player.t_ctype == C_THIEF || on(player, BLESSGOLD) && rnd(5) == 0) + { + read_scroll(&player, S_GFIND, ISNORMAL); + + if (rnd(3) == 0) + turn_off(player, BLESSGOLD); + } + + if (player.t_ctype == C_RANGER || on(player, BLESSFOOD) && rnd(5) == 0) + { + read_scroll(&player, S_FOODDET, ISNORMAL); + + if (rnd(3) == 0) + turn_off(player, BLESSFOOD); + } + + if (player.t_ctype == C_MAGICIAN || player.t_ctype == C_ILLUSION || + on(player, BLESSMAGIC) && rnd(5) == 0) + { + quaff(&player, P_TREASDET, ISNORMAL); + + if (rnd(3) == 0) + turn_off(player, BLESSMAGIC); + } + + if (player.t_ctype == C_DRUID || on(player, BLESSMONS) && rnd(5) == 0) + { + quaff(&player, P_MONSTDET, ISNORMAL); + + if (rnd(3) == 0) + turn_off(player, BLESSMONS); + } + else if (player.t_ctype == C_CLERIC || + player.t_ctype == C_PALADIN || is_wearing(R_PIETY)) + undead_sense(); + } + + if (is_wearing(R_AGGR)) + aggravate(); + + if (is_wearing(R_ADORNMENT) || + cur_armor != NULL && cur_armor->o_which == MITHRIL) + { + int greed = FALSE; + + for (item = mlist; item != NULL; item = next(item)) + { + tp = THINGPTR(item); + + if (on(*tp, ISGREED)) + { + turn_on(*tp, ISRUN); + turn_on(*tp, ISMEAN); + tp->t_ischasing = TRUE; + tp->t_chasee = &player; + greed = TRUE; + } + } + + if (greed) + msg("An uneasy feeling comes over you."); + } + + if (is_carrying(TR_PALANTIR)) /* Palantir shows all */ + { + msg("The Palantir reveals all!"); + + overlay(stdscr, cw); /* Wizard mode 'f' command */ + overlay(mw, cw); /* followed by 'm' command */ + } + + if (is_carrying(TR_PHIAL)) /* Phial lights your way */ + { + if (!is_carrying(TR_PALANTIR)) + msg("The Phial banishes the darkness!"); + + for (i = 0; i < MAXROOMS; i++) + rooms[i].r_flags &= ~ISDARK; + } + + if (is_carrying(TR_AMULET)) /* Amulet describes the level */ + { + level_eval(); + } + + + wmove(cw, hero.y, hero.x); + waddch(cw, PLAYER); + light(&hero); + + /* Summon familiar if player has one */ + + if (on(player, HASFAMILIAR)) + { + summon_monster((short) fam_type, FAMILIAR, MESSAGE); + + if (fam_ptr != NULL) /* add old pack to new */ + { + tp = THINGPTR(fam_ptr); + + if (tp->t_pack == NULL) + tp->t_pack = fpack; + else if (fpack != NULL) + { + for (item = tp->t_pack; item->l_next != NULL;item = next(item)) + ; + + item->l_next = fpack; + debug("new_level: l_prev = %p",item); + fpack->l_prev = item; + } + } + else + free_list(fpack); + } + + mem_check(__FILE__,__LINE__); + status(TRUE); +} + +/* + put_things() + put potions and scrolls on this level +*/ + +void +put_things(LEVTYPE ltype) +{ + int i, rm, cnt; + struct linked_list *item; + struct object *cur; + int got_unique = FALSE; + int length, width, maxobjects; + coord tp; + + /* + * Once you have found an artifact, the only way to get new stuff is + * go down into the dungeon. + */ + + if (has_artifact && level < max_level && ltype != THRONE) + return; + + /* + * There is a chance that there is a treasure room on this level + * Increasing chance after level 10 + */ + + if (ltype != MAZELEV && rnd(50) < level - 10) + { + int n, j; + struct room *rp; + + /* Count the number of free spaces */ + n = 0; /* 0 tries */ + + do + { + rp = &rooms[rnd_room()]; + width = rp->r_max.y - 2; + length = rp->r_max.x - 2; + } + while(!((width * length <= MAXTREAS) || (n++ > MAXROOMS * 4))); + + /* Mark the room as a treasure room */ + + rp->r_flags |= ISTREAS; + + /* Make all the doors secret doors */ + + for (n = 0; n < rp->r_nexits; n++) + { + move(rp->r_exit[n].y, rp->r_exit[n].x); + addch(SECRETDOOR); + } + + /* Put in the monsters and treasures */ + + for (j = 1; j < rp->r_max.y - 1; j++) + for (n = 1; n < rp->r_max.x - 1; n++) + { + coord trp; + + trp.y = rp->r_pos.y + j; + trp.x = rp->r_pos.x + n; + + /* Monsters */ + + if ((rnd(100) < (MAXTREAS * 100) / + (width * length)) && + (mvwinch(mw, rp->r_pos.y + j, + rp->r_pos.x + n) == ' ')) + { + struct thing *th; + + /* Make a monster */ + + item = new_item(sizeof *th); + th = THINGPTR(item); + + /* + * Put it there and aggravate it + * (unless it can escape) only put + * one UNIQUE per treasure room at + * most + */ + + if (got_unique) + new_monster(item, randmonster(NOWANDER, GRAB), &trp, + NOMAXSTATS); + else + { + new_monster(item, randmonster(NOWANDER, NOGRAB), &trp, + NOMAXSTATS); + + if (on(*th, ISUNIQUE)) + got_unique = TRUE; + } + + turn_off(*th, ISFRIENDLY); + turn_on(*th, ISMEAN); + + if (off(*th, CANINWALL)) + { + th->t_ischasing = TRUE; + th->t_chasee = &player; + turn_on(*th, ISRUN); + } + } + + /* Treasures */ + + if ((rnd(100) < (MAXTREAS * 100) / + (width * length)) && + (mvinch(rp->r_pos.y + j, + rp->r_pos.x + n) == FLOOR)) + { + item = new_thing(); + cur = OBJPTR(item); + cur->o_pos = trp; + add_obj(item, trp.y, trp.x); + } + } + } + + /* Do MAXOBJ attempts to put things on a level, maybe */ + + maxobjects = (ltype == THRONE) ? rnd(3 * MAXOBJ) + 35 : MAXOBJ; + + for (i = 0; i < maxobjects; i++) + if (rnd(100) < 40 || ltype == THRONE) + { + /* Pick a new object and link it in the list */ + + item = new_thing(); + cur = OBJPTR(item); + + /* Put it somewhere */ + + cnt = 0; + + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &tp); + } + while(!(winat(tp.y, tp.x) == FLOOR || cnt++ > 500)); + + cur->o_pos = tp; + add_obj(item, tp.y, tp.x); + } + + /* + * If he is really deep in the dungeon and he hasn't found an + * artifact yet, put it somewhere on the ground + */ + + if (make_artifact()) + { + item = new_item(sizeof *cur); + cur = OBJPTR(item); + new_artifact(-1, cur); + cnt = 0; + + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &tp); + } + while(!(winat(tp.y, tp.x) == FLOOR || cnt++ > 500)); + + cur->o_pos = tp; + add_obj(item, tp.y, tp.x); + } +} + +/* + do_throne() + Put a monster's throne room and monsters on the screen +*/ + +void +do_throne(int special) +{ + coord mp; + int save_level; + int i; + struct room *rp; + struct thing *tp; + struct linked_list *item; + int throne_monster; + + for (rp = rooms; rp < &rooms[MAXROOMS]; rp++) + { + rp->r_nexits = 0; /* no exits */ + rp->r_flags = ISGONE; /* kill all rooms */ + } + + rp = &rooms[0]; /* point to only room */ + rp->r_flags = 0; /* this room NOT gone */ + rp->r_max.x = 40; + rp->r_max.y = 10; /* 10 * 40 room */ + rp->r_pos.x = (COLS - rp->r_max.x) / 2; /* center horizontal */ + rp->r_pos.y = 3; /* 2nd line */ + draw_room(rp); /* draw the only room */ + + save_level = level; + level = max(2 * level, level + roll(4, 6)); + + if (special == 0) /* Who has he offended? */ + do + throne_monster = nummonst - roll(1, NUMSUMMON); + while(!monsters[throne_monster].m_normal); + else + throne_monster = special; + + /* Create summoning monster */ + + item = new_item(sizeof *tp); + + tp = THINGPTR(item); + + do + { + rnd_pos(rp, &mp); + } + while(mvwinch(stdscr, mp.y, mp.x) != FLOOR); + + new_monster(item, throne_monster, &mp, MAXSTATS); + turn_on(*tp, CANSEE); + turn_off(*tp, ISFRIENDLY); + + if (on(*tp, CANSUMMON)) /* summon his helpers */ + summon_help(tp, FORCE); + else + { + for (i = roll(4, 10); i >= 0; i--) + { + item = new_item(sizeof *tp); + tp = THINGPTR(item); + + do + { + rnd_pos(rp, &mp); + } + while(mvwinch(stdscr, mp.y, mp.x) != FLOOR); + + new_monster(item, randmonster(NOWANDER, NOGRAB), &mp, MAXSTATS); + turn_on(*tp, CANSEE); + turn_off(*tp, ISFRIENDLY); + } + } + + level = save_level + roll(2, 3); /* send the hero down */ + aggravate(); +} + +/* + create_lucifer() + special surprise on the way back up create Lucifer + with more than the usual god abilities +*/ + +void +create_lucifer(coord *stairs) +{ + struct linked_list *item = new_item(sizeof(struct thing)); + struct thing *tp = THINGPTR(item); + + new_monster(item, nummonst + 1, stairs, MAXSTATS); + turn_on(*tp, CANINWALL); + turn_on(*tp, CANHUH); + turn_on(*tp, CANBLINK); + turn_on(*tp, CANSNORE); + turn_on(*tp, CANDISEASE); + turn_on(*tp, NOCOLD); + turn_on(*tp, TOUCHFEAR); + turn_on(*tp, BMAGICHIT); + turn_on(*tp, NOFIRE); + turn_on(*tp, NOBOLT); + turn_on(*tp, CANBLIND); + turn_on(*tp, CANINFEST); + turn_on(*tp, CANSMELL); + turn_on(*tp, CANPARALYZE); + turn_on(*tp, CANSTINK); + turn_on(*tp, CANCHILL); + turn_on(*tp, CANFRIGHTEN); + turn_on(*tp, CANHOLD); + turn_on(*tp, CANBRANDOM); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/options.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/options.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,519 @@ +/* + options.c - This file has all the code for the option command + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +#define NUM_OPTS (sizeof optlist / sizeof (OPTION)) +#define EQSTR(a, b, c) (strncmp(a, b, c) == 0) + +/* description of an option and what to do with it */ +static OPTION optlist[] = +{ +{"jump","Show position only at end of run (jump): ", &jump,put_bool,get_bool}, +{"inven","Style of inventories (inven): ", &inv_type, put_inv, get_inv}, +{"askme","Ask me about unidentified things (askme): ",&askme,put_bool,get_bool}, +{"doorstop","Stop running when adjacent (doorstop): ",&doorstop,put_bool,get_bool}, +{"name", "Name (name): ", &whoami, put_str, get_str}, +{"fruit", "Fruit (fruit): ", &fruit, put_str, get_str}, +{"file", "Save file (file): ", &file_name, put_str, get_str}, +{"score", "Score file (score): ", &score_file, put_str, get_str}, +{"class", "Character class (class): ",&char_type, put_abil, get_abil} +}; + +/* + option() + print and then set options from the terminal +*/ + +void +option(void) +{ + OPTION *op; + int retval; + + wclear(hw); + touchwin(hw); + + /* Display current values of options */ + + for (op = optlist; op < &optlist[NUM_OPTS]; op++) + { + waddstr(hw, op->o_prompt); + (*op->o_putfunc)(&op->o_opt, hw); + waddch(hw, '\n'); + } + + /* Set values */ + + wmove(hw, 0, 0); + + for (op = optlist; op < &optlist[NUM_OPTS]; op++) + { + waddstr(hw, op->o_prompt); + + retval = (*op->o_getfunc)(&op->o_opt, hw); + + if (retval) + if (retval == QUIT) + break; + else if (op > optlist) /* MINUS */ + { + wmove(hw, (int)(op - optlist) - 1, 0); + op -= 2; + } + else /* trying to back up beyond the top */ + { + putchar('\007'); + wmove(hw, 0, 0); + op--; + } + } + + /* Switch back to original screen */ + + mvwaddstr(hw, LINES - 1, 0, spacemsg); + wrefresh(hw); + wait_for(' '); + clearok(cw, TRUE); + touchwin(cw); +} + +/* + put_bool() + put out a boolean +*/ + +void +put_bool(opt_arg *opt, WINDOW *win) +{ + waddstr(win, *opt->iarg ? "True" : "False"); +} + +/* + put_str() + put out a string +*/ + +void +put_str(opt_arg *opt, WINDOW *win) +{ + waddstr(win, opt->str); +} + +/* + put_abil() + print the character type +*/ + +void +put_abil(opt_arg *opt, WINDOW *win) +{ + char *abil; + + switch(*opt->iarg) + { + case C_FIGHTER: + abil = "Fighter"; + break; + case C_MAGICIAN: + abil = "Magic User"; + break; + case C_CLERIC: + abil = "Cleric"; + break; + case C_THIEF: + abil = "Thief"; + break; + case C_PALADIN: + abil = "Paladin"; + break; + case C_RANGER: + abil = "Ranger"; + break; + case C_ILLUSION: + abil = "Illusionist"; + break; + case C_ASSASIN: + abil = "Assasin"; + break; + case C_NINJA: + abil = "Ninja"; + break; + case C_DRUID: + abil = "Druid"; + break; + default: + abil = "(unknown)"; + } + waddstr(win, abil); +} + + +/* + get_bool() + allow changing a boolean option and print it out +*/ + +int +get_bool(opt_arg *opt, WINDOW *win) +{ + int oy, ox; + int op_bad; + + op_bad = TRUE; + getyx(win, oy, ox); + waddstr(win, *opt->iarg ? "True" : "False"); + + while(op_bad) + { + wmove(win, oy, ox); + wrefresh(win); + + switch (readcharw(win)) + { + case 't': + case 'T': + *opt->iarg = TRUE; + op_bad = FALSE; + break; + + case 'f': + case 'F': + *opt->iarg = FALSE; + op_bad = FALSE; + break; + + case '\n': + case '\r': + op_bad = FALSE; + break; + + case '\033': + case '\007': + return QUIT; + + case '-': + return MINUS; + + default: + mvwaddstr(win, oy, ox + 10, "(T or F)"); + } + } + + wmove(win, oy, ox); + wclrtoeol(win); + waddstr(win, *opt->iarg ? "True" : "False"); + waddch(win, '\n'); + + return(NORM); +} + +/* + get_str() + set a string option +*/ + +int +get_str(opt_arg *opt, WINDOW *win) +{ + return( get_string(opt->str, win) ); +} + +/* + get_abil() + The ability field is read-only +*/ + +int +get_abil(opt_arg *opt, WINDOW *win) +{ + int oy, ox, ny, nx; + int op_bad; + + op_bad = TRUE; + getyx(win, oy, ox); + put_abil(opt, win); + getyx(win, ny, nx); + + while(op_bad) + { + wmove(win, oy, ox); + wrefresh(win); + + switch(readcharw(win)) + { + case '\n': + case '\r': + op_bad = FALSE; + break; + + case '\033': + case '\007': + return(QUIT); + + case '-': + return(MINUS); + + default: + mvwaddstr(win, ny, nx + 5, "(no change allowed)"); + } + } + + wmove(win, ny, nx + 5); + wclrtoeol(win); + wmove(win, ny, nx); + waddch(win, '\n'); + + return(NORM); +} + + +/* + parse_opts() + parse options from string, usually taken from the environment. the + string is a series of comma seperated values, with booleans being + stated as "name" (true) or "noname" (false), and strings being + "name=....", with the string being defined up to a comma or the + end of the entire option string. + */ + +void +parse_opts(char *str) +{ + char *sp; + const OPTION *op; + size_t len; + + while (*str) + { + for (sp = str; isalpha(*sp); sp++) + continue; + + len = sp - str; + + /* Look it up and deal with it */ + + for (op = optlist; op < &optlist[NUM_OPTS]; op++) + if (EQSTR(str, op->o_name, len)) + { + if (op->o_putfunc == put_bool) + *op->o_opt.iarg = TRUE; + else /* string option */ + { + char *start; + char value[80]; + + /* Skip to start of string value */ + + for (str = sp + 1; *str == '='; str++) + continue; + + start = (char *) value; + + /* Skip to end of string value */ + + for (sp = str + 1; *sp && *sp != ','; sp++) + continue; + + strncpy(start, str, sp - str); + + /* Put the value into the option field */ + + if (op->o_putfunc != put_abil && + op->o_putfunc != put_inv) + strcpy(op->o_opt.str, value); + + if (op->o_putfunc == put_inv) + { + int *opt = op->o_opt.iarg; + + len = strlen(value); + + if (isupper(value[0])) + value[0] = (char) tolower(value[0]); + if (EQSTR(value, "overwrite",len)) + *opt = INV_OVER; + if (EQSTR(value, "slow", len)) + *opt = INV_SLOW; + if (EQSTR(value, "clear", len)) + *opt = INV_CLEAR; + } + else if (*op->o_opt.iarg == -1) + { + int *opt = op->o_opt.iarg; + + len = strlen(value); + + if (isupper(value[0])) + value[0] = (char) tolower(value[0]); + if (EQSTR(value, "fighter", len)) + *opt = C_FIGHTER; + else if (EQSTR(value, "magic", min(len, 5))) + *opt = C_MAGICIAN; + else if (EQSTR(value, "illus", min(len, 5))) + *opt = C_ILLUSION; + else if (EQSTR(value, "cleric", len)) + *opt = C_CLERIC; + else if (EQSTR(value, "thief", len)) + *opt = C_THIEF; + else if (EQSTR(value, "paladin", len)) + *opt = C_PALADIN; + else if (EQSTR(value, "ranger", len)) + *opt = C_RANGER; + else if (EQSTR(value, "assasin", len)) + *opt = C_ASSASIN; + else if (EQSTR(value, "druid", len)) + *opt = C_DRUID; + else if (EQSTR(value, "ninja", len)) + *opt = C_NINJA; + } + } + break; + } + else if (op->o_putfunc == put_bool + && EQSTR(str, "no", 2) && + EQSTR(str + 2, op->o_name, len - 2)) + { + *op->o_opt.iarg = FALSE; + break; + } + + /* skip to start of next option name */ + + while (*sp && !isalpha(*sp)) + sp++; + + str = sp; + } +} + +/* + put_inv() + print the inventory type +*/ + +void +put_inv(opt_arg *opt, WINDOW *win) +{ + char *style; + + switch(*opt->iarg) + { + case INV_OVER: + style = "Overwrite"; + break; + + case INV_SLOW: + style = "Slow"; + break; + + case INV_CLEAR: + style = "Clear Screen"; + break; + + default: + style = "(unknown)"; + } + + waddstr(win, style); +} + +/* + get_inv() + The inventory field. +*/ + +int +get_inv(opt_arg *opt, WINDOW *win) +{ + int oy, ox, ny, nx; + int op_bad; + + op_bad = TRUE; + getyx(win, oy, ox); + put_inv(opt, win); + getyx(win, ny, nx); + + while(op_bad) + { + wmove(win, oy, ox); + wrefresh(win); + + switch(readcharw(win)) + { + case '\n': + case '\r': + op_bad = FALSE; + break; + + case '\033': + case '\007': + return(QUIT); + + case '-': + return(MINUS); + + case 'O': + case 'o': + *opt->iarg = INV_OVER; + op_bad = FALSE; + break; + + case 'S': + case 's': + *opt->iarg = INV_SLOW; + op_bad = FALSE; + break; + + case 'C': + case 'c': + *opt->iarg = INV_CLEAR; + op_bad = FALSE; + break; + + default: + mvwaddstr(win, ny, nx + 5, "(Use: o, s, or c)"); + } + } + + wmove(win, oy, ox); + wclrtoeol(win); + + switch(*opt->iarg) + { + case INV_SLOW: + waddstr(win, "Slow\n"); + break; + + case INV_CLEAR: + waddstr(win, "Clear Screen\n"); + break; + + case INV_OVER: + waddstr(win, "Overwrite\n"); + break; + + default: + waddstr(win, "Unknown\n"); + break; + } + + return(NORM); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/pack.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/pack.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,683 @@ +/* + pack.c - Routines to deal with the pack. + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +/* + Notes + + The new pack is implemented through the use of bags, + and items are classed by their types (see rogue.h) which also + happen to be their display character. +*/ + +#include +#include +#include "rogue.h" + +#define ESCAPE_EXIT(x) if (x == ESCAPE) {after = FALSE; msg(""); return(NULL);} +#define BAD_NEWS -1 +#define BAD_LIST ((struct linked_list *) BAD_NEWS) +#define GOOD_NEWS 0 + +static char type_list[] = "!?])/=:,"; /* things to inventory */ + +/* + swap_top() + Takes an stacked object and exchanges places with the top + object. must belong to the 's stacked object list. +*/ + +void +swap_top(struct linked_list *top, struct linked_list *node) +{ + struct object *obt, *obn; + + obt = OBJPTR(top); + obn = OBJPTR(node); + + detach((obt->next_obj), node); /* Take it out of the stack */ + attach(lvl_obj, node); /* and put it into the level */ + detach(lvl_obj, top); /* Remove item from level */ + + obn->next_obj = obt->next_obj; + + if (obn->next_obj) + obn->next_obj->l_prev = NULL; + + attach((obn->next_obj), top); +} + + +/* + get_all() + Get as many stacked items as possible. +*/ + +void +get_all(struct linked_list *top) +{ + struct linked_list *node; + struct object *obt; + + while (top) + { + obt = OBJPTR(top); + node = obt->next_obj; + + rem_obj(top, FALSE); + + if (!add_pack(top, FALSE)) + return; + + top = node; + } +} + + +/* + get_stack() + Allows the user to chose from a stack of items. +*/ + +struct linked_list * +get_stack(struct linked_list *item) +{ + struct object *obj; + char buf[2 * LINELEN]; + int i = 0, j; + struct linked_list *ll; +mpos = 0; + obj = OBJPTR(item); + + ll = obj->next_obj; + + sprintf(buf, "You are standing on top of the following items: "); + add_line(buf); + sprintf(buf, "%d) -- %s", i, inv_name(obj, TRUE)); + add_line(buf); + + while (ll) + { + i++; + obj = OBJPTR(ll); + sprintf(buf, "%d) -- %s", i, inv_name(obj, TRUE)); + add_line(buf); + ll = next(ll); + } + + end_line(); + + msg("Which one do you want to pick up? [* for all] "); + + switch(get_string(buf, cw)) + { + case NORM: + break; + case QUIT: /* pick up nothing */ + msg(""); + return (NULL); + } + + if (buf[0] == '*') + { + get_all(item); + msg(""); + return(NULL); + } + else + { + i = atoi(buf); + + if (i) + { + obj = OBJPTR(item); + ll = obj->next_obj; + j = 1; + + while (ll && (j != i)) + { + ll = next(ll); + j++; + } + + if (ll) + { + swap_top(item, ll); + return(ll); + } + else + { + debug("Got past last item while picking up."); + return(item); + } + } + else + return (item); + } +} + +/* + add_pack() + Pick up an object and add it to the pack. If the argument is + non-null use it as the linked_list pointer instead of getting it off the + ground. +*/ + +int +add_pack(struct linked_list *item, int print_message) +{ + struct object *obj, *op; + int from_floor; + + if (item == NULL) + { + from_floor = TRUE; + + if ((item = find_obj(hero.y, hero.x)) == NULL) + { + msg("Nothing to pick up."); + return(FALSE); + } + } + else + from_floor = FALSE; + + if (from_floor) + { + item = get_stack(item); + + if (!item) + return(FALSE); + } + + obj = OBJPTR(item); + + /* If it is gold, just add its value to rogue's purse and get rid of */ + + if (obj->o_type == GOLD) + { + struct linked_list *mitem; + struct thing *tp; + + if (print_message) + { + if (!terse) + addmsg("You found "); + + msg("%d gold pieces.", obj->o_count); + } + + /* + * First make sure no greedy monster is after this gold. If + * so, make the monster run after the rogue instead. + */ + + for (mitem = mlist; mitem != NULL; mitem = next(mitem)) + { + tp = THINGPTR(mitem); + + if (tp->t_horde==obj) + { + tp->t_ischasing = TRUE; + tp->t_chasee = &player; + tp->t_horde = NULL; + } + } + + /* + * This will cause problems if people are able to drop and + * pick up gold, or when GOLDSTEAL monsters are killed. + */ + + /* Thieves get EXP for gold they pick up */ + + if (player.t_ctype == C_THIEF) + { + pstats.s_exp += obj->o_count / 4; + check_level(); + } + + purse += obj->o_count; + + if (from_floor) + rem_obj(item, TRUE); /* Remove object from the level */ + + return (TRUE); + } + + /* see if he can carry any more weight */ + + if (itemweight(obj) + pstats.s_pack > pstats.s_carry) + { + msg("Too much for you to carry."); + + if (print_message) + { + msg("%s onto %s", terse ? "Moved" : "You moved", + inv_name(obj, LOWERCASE)); + } + + return(FALSE); + } + + /* + * Link it into the pack. If the item can be grouped, try to find its + * neighbors and bump the count. A special case is food, which can't + * be grouped, but an exact match allows the count to get + * incremented. + */ + + if ((op = apply_to_bag(pack, obj->o_type, bff_group, NULL, obj)) != NULL) + { + op->o_count += obj->o_count; /* add it to the rest */ + + if (from_floor) + rem_obj(item, FALSE); + + pack_report(op, print_message, "You now have "); + + return(TRUE); + } + + /* Check for and deal with scare monster scrolls */ + + if (obj->o_type == SCROLL && obj->o_which == S_SCARE) + if (obj->o_flags & ISCURSED) + { + msg("The scroll turns to dust as you pick it up."); + rem_obj(item, TRUE); + return(TRUE); + } + + /* Check if there is room */ + + if (count_bag(pack, obj->o_type, NULL) == max_print()) + { + msg("You have no room for more %s.", name_type(obj->o_type)); + + if (print_message) + { + obj = OBJPTR(item); + msg("%s onto %s.", terse ? "Moved" : "You moved", + inv_name(obj, LOWERCASE)); + } + + return(FALSE); + } + + /* + * finally, add the new item to the bag, and free up the linked list + * on top of it. + */ + + if (from_floor) + rem_obj(item, FALSE); + + push_bag(&pack, obj); + pack_report(obj, print_message, "You now have "); + ur_free(item); + + return(TRUE); /* signal success */ +} + +/* + pack_report() + Notify the user about the results of the pack operation and do some + post processing. +*/ + +void +pack_report(object *obj, int print_message, char *message) +{ + /* Notify the user */ + + if (print_message) + { + if (!terse) + addmsg(message); + + msg("(%c%c) %s.", obj->o_type, print_letters[get_ident(obj)], + inv_name(obj, !terse)); + } + + if (obj->o_type == ARTIFACT) + { + has_artifact |= (1 << obj->o_which); + picked_artifact |= (1 << obj->o_which); + + if (!(obj->ar_flags & ISUSED)) + { + obj->ar_flags |= ISUSED; + pstats.s_exp += arts[obj->o_which].ar_worth / 10; + check_level(); + } + } + + updpack(); + + return; +} + +/* + inventory() + list what is in the pack +*/ + +void +inventory(struct linked_list *container, int type) +{ + int cnt; + + if (type == 0) + { + msg("What kind of item <%s> to inventory (* for all)?", type_list); + + type = readchar(); + + if (type == ESCAPE) + { + after = FALSE; + msg(""); + return; + } + } + + /* + * Get a list of items to print out. If the user selects '*', list + * them all. + */ + + if (type == '*') + type = 0; /* no type passed ->use them all */ + + mpos = 0; + + if ((cnt = count_bag(container, type, NULL)) == 0) + msg("You don't have any %s.", name_type(type)); + else + { + apply_to_bag(container, type, NULL, baf_print_item, &type); + end_line(); + msg(""); + } + + return; +} + +/* + pick_up() + Add something to characters pack. +*/ + +void +pick_up(char ch) +{ + switch(ch) + { + default: + debug("Where did you pick that up???"); + break; + + case GOLD: + case ARMOR: + case POTION: + case FOOD: + case WEAPON: + case SCROLL: + case ARTIFACT: + case RING: + case STICK: + add_pack(NULL, MESSAGE); + break; + } +} + +/* + get_object() + + Pick something out of a pack for a purpose. The basic idea is to + list all the possibilities, let the user select one, get that item + from the container, and pass it back to the calling routine. +*/ + +struct object * +get_object(struct linked_list *container, char *purpose, int type, int (*bff_p)(struct object *obj, bag_arg *junk)) +/* char *container; what container has what we want */ +/* char *purpose; a message (verb) to print if we cant find any */ +/* char type; type (o_type) to pick out (NULL = any) */ +/* int (*bff_p) (); bag filter function to test item */ +{ + struct object *obj_p = NULL; + char sel_id; /* selected type and id */ + int sel_type; + char response; + + if (container == NULL) + { + msg("There isn't anything in there."); + after = FALSE; + return(NULL); + } + + /* Make sure we have at least one item that qualifies! */ + + if (apply_to_bag(container, type, bff_p, NULL, NULL) == NULL) + { + msg("You seem to have nothing to %s.", purpose); + after = FALSE; + return(NULL); + } + + while (obj_p == NULL) + { + if (type == 0) + { + msg("What kind of item <%s> do you want to %s (* for list)?", type_list, purpose); + + response = readchar(); + ESCAPE_EXIT(response); + msg(""); + + if (response == '*') + { + add_line("! Potion"); + add_line("? Scroll"); + add_line("= Ring"); + add_line("/ Stick"); + add_line("] Armor"); + add_line(") Weapon"); + add_line(": Food"); + end_line(); + continue; + } + + + if (!is_member(type_list, response)) { beep(); + continue; } + + + if (count_bag(container, response, NULL) == 0) + { + msg("You don't have any %s.", name_type(response)); + continue; + } + + type = response; + } + + while(obj_p == NULL) + { + msg("What item do you want to %s (* for list)?", purpose); + response = readchar(); + msg(""); + ESCAPE_EXIT(response); + + if (response == '*') + { + mpos = 0; + apply_to_bag(container, type, bff_p, baf_print_item, &type); + end_line(); + continue; + } + + sel_type = type; + sel_id = response; + + obj_p = scan_bag(container, sel_type,unprint_id(&sel_id)); + } + } + + mpos = 0; + msg(""); + return(obj_p); +} + +/* + get_item() + + This is only an interim function that serves as an interface to + the old function get_item and its replacement get_object. It + assumes a NULL action routine and allocates a linked_list + structure on top of the object pointer. +*/ + +struct linked_list * +get_item(char *purpose, int type) +{ + struct object *obj_p; + + if ((obj_p = get_object(pack, purpose, type, NULL)) == NULL) + return(NULL); + + return(make_item(obj_p)); +} + +/* + del_pack() + Take something out of the hero's pack and throw it away. +*/ + +void +del_pack(struct linked_list *what) +{ + rem_pack(OBJPTR(what)); + discard(what); +} + +/* + discard_pack + take an object from the pack and throw it away (like del_pack, + but without the linked_list structure) +*/ + +void +discard_pack(struct object *obj_p) +{ + rem_pack(obj_p); + throw_away(obj_p); +} + +/* + rem_pack() + Removes an item from the pack. +*/ + +void +rem_pack(struct object *obj_p) +{ + cur_null(obj_p); /* check for current stuff */ + pop_bag(&pack, obj_p); + updpack(); + return; /* tell caller an item has been removed */ +} + +/* + cur_null() + This updates cur_weapon etc for dropping things +*/ + +void +cur_null(struct object *op) +{ + if (op == cur_weapon) + cur_weapon = NULL; + else if (op == cur_armor) + cur_armor = NULL; + else if (op == cur_ring[LEFT_1]) + cur_ring[LEFT_1] = NULL; + else if (op == cur_ring[LEFT_2]) + cur_ring[LEFT_2] = NULL; + else if (op == cur_ring[LEFT_3]) + cur_ring[LEFT_3] = NULL; + else if (op == cur_ring[LEFT_4]) + cur_ring[LEFT_4] = NULL; + else if (op == cur_ring[LEFT_5]) + cur_ring[LEFT_5] = NULL; + else if (op == cur_ring[RIGHT_1]) + cur_ring[RIGHT_1] = NULL; + else if (op == cur_ring[RIGHT_2]) + cur_ring[RIGHT_2] = NULL; + else if (op == cur_ring[RIGHT_3]) + cur_ring[RIGHT_3] = NULL; + else if (op == cur_ring[RIGHT_4]) + cur_ring[RIGHT_4] = NULL; + else if (op == cur_ring[RIGHT_5]) + cur_ring[RIGHT_5] = NULL; +} + +/* + idenpack() + Identify all the items in the pack +*/ + +void +idenpack(void) +{ + apply_to_bag(pack, 0, NULL, baf_identify, NULL); +} + +/* + show_floor() + Print out the item on the floor. Used by the move command. +*/ + +void +show_floor(void) +{ + struct linked_list *item; + struct object *obj; + + item = find_obj(hero.y, hero.x); + + if (item != NULL) + { + addmsg("%s onto ", terse ? "Moved" : "You moved"); + + obj = OBJPTR(item); + + if (obj->next_obj != NULL) + msg("a stack of things."); + else if (obj->o_type == GOLD) + msg("%d gold pieces.", obj->o_count); + else + { + addmsg(inv_name(obj, TRUE)); + addmsg("."); + endmsg(); + } + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/passages.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/passages.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,320 @@ +/* + passages.c - Draw the connecting passages + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include "rogue.h" + +#define cmov(xy) move((xy).y, (xy).x) + +/* + do_passages() + Draw all the passages on a level. +*/ + +void +do_passages(void) +{ + struct rdes *r1, *r2 = NULL; + int i, j; + int roomcount; + + static struct rdes rdes[MAXROOMS] = + { + {{ 0, 1, 0, 1, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 1, 0, 1, 0, 1, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 0, 1, 0, 0, 0, 1, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 1, 0, 0, 0, 1, 0, 1, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 0, 1, 0, 1, 0, 1, 0, 1, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 0, 0, 1, 0, 1, 0, 0, 0, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 0, 0, 0, 1, 0, 0, 0, 1, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 0, 0, 0, 0, 1, 0, 1, 0, 1}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0}, + {{ 0, 0, 0, 0, 0, 1, 0, 1, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0} + }; + + /* reinitialize room graph description */ + + for (r1 = rdes; r1 < &rdes[MAXROOMS]; r1++) + { + for (j = 0; j < MAXROOMS; j++) + r1->isconn[j] = FALSE; + + r1->ingraph = FALSE; + } + + /* + * starting with one room, connect it to a random adjacent room and + * then pick a new room to start with. + */ + + roomcount = 1; + r1 = &rdes[rnd(MAXROOMS)]; + r1->ingraph = TRUE; + + do + { + j = 0; + + for (i = 0; i < MAXROOMS; i++) + if (r1->conn[i] && !rdes[i].ingraph && rnd(++j) == 0) + r2 = &rdes[i]; + + /* + * if no adjacent rooms are outside the graph, pick a new + * room to look from + */ + + if (j == 0) + do + { + r1 = &rdes[rnd(MAXROOMS)]; + } + while (!r1->ingraph); + + /* + * otherwise, connect new room to the graph, and draw a + * tunnel to it + */ + else + { + r2->ingraph = TRUE; + i = (int)(r1 - rdes); + j = (int)(r2 - rdes); + conn(i, j); + r1->isconn[j] = TRUE; + r2->isconn[i] = TRUE; + roomcount++; + } + } + while (roomcount < MAXROOMS); + + /* + * attempt to add passages to the graph a random number of times so + * that there isn't just one unique passage through it. + */ + + for (roomcount = rnd(5); roomcount > 0; roomcount--) + { + r1 = &rdes[rnd(MAXROOMS)]; /* a random room to look from */ + + /* + * find an adjacent room not already connected + */ + + j = 0; + for (i = 0; i < MAXROOMS; i++) + if (r1->conn[i] && !r1->isconn[i] && rnd(++j) == 0) + r2 = &rdes[i]; + + /* + * if there is one, connect it and look for the next added + * passage + */ + + if (j != 0) + { + i = (int)(r1 - rdes); + j = (int)(r2 - rdes); + conn(i, j); + r1->isconn[j] = TRUE; + r2->isconn[i] = TRUE; + } + } +} + +/* + conn() + Draw a corridor from a room in a certain direction. +*/ + +void +conn(int r1, int r2) +{ + struct room *rpf, *rpt = NULL; + int rmt; + int distance = 0, turn_spot = 0, turn_distance = 0; + int rm; + char direc; + coord delt = {0,0}, curr, turn_delta = {0,0}, spos = {0,0}, epos = {0,0}; + + if (r1 < r2) + { + rm = r1; + + if (r1 + 1 == r2) + direc = 'r'; + else + direc = 'd'; + } + else + { + rm = r2; + + if (r2 + 1 == r1) + direc = 'r'; + else + direc = 'd'; + } + + rpf = &rooms[rm]; + + /* + * Set up the movement variables, in two cases: first drawing one + * down. + */ + + if (direc == 'd') + { + rmt = rm + 3; /* room # of dest */ + rpt = &rooms[rmt]; /* room pointer of dest */ + delt.x = 0; /* direction of move */ + delt.y = 1; + spos.x = rpf->r_pos.x; /* start of move */ + spos.y = rpf->r_pos.y; + epos.x = rpt->r_pos.x; /* end of move */ + epos.y = rpt->r_pos.y; + + if (!(rpf->r_flags & ISGONE)) /* if not gone pick door pos */ + { + spos.x += rnd(rpf->r_max.x - 2) + 1; + spos.y += rpf->r_max.y - 1; + } + + if (!(rpt->r_flags & ISGONE)) + epos.x += rnd(rpt->r_max.x - 2) + 1; + + distance = abs(spos.y - epos.y) - 1; /* distance to move */ + turn_delta.y = 0; /* direction to turn */ + turn_delta.x = (spos.x < epos.x ? 1 : -1); + turn_distance = abs(spos.x - epos.x); /* how far to turn */ + turn_spot = rnd(distance - 1) + 1; /* where turn starts */ + } + else if (direc == 'r') /* setup for moving right */ + { + rmt = rm + 1; + rpt = &rooms[rmt]; + delt.x = 1; + delt.y = 0; + spos.x = rpf->r_pos.x; + spos.y = rpf->r_pos.y; + epos.x = rpt->r_pos.x; + epos.y = rpt->r_pos.y; + + if (!(rpf->r_flags & ISGONE)) + { + spos.x += rpf->r_max.x - 1; + spos.y += rnd(rpf->r_max.y - 2) + 1; + } + + if (!(rpt->r_flags & ISGONE)) + epos.y += rnd(rpt->r_max.y - 2) + 1; + + distance = abs(spos.x - epos.x) - 1; + turn_delta.y = (spos.y < epos.y ? 1 : -1); + turn_delta.x = 0; + turn_distance = abs(spos.y - epos.y); + turn_spot = rnd(distance - 1) + 1; + } + else + debug("Error in connection tables."); + + /* + * Draw in the doors on either side of the passage or just put #'s if + * the rooms are gone. + */ + + if (!(rpf->r_flags & ISGONE)) + door(rpf, &spos); + else + { + cmov(spos); + addch('#'); + } + + if (!(rpt->r_flags & ISGONE)) + door(rpt, &epos); + else + { + cmov(epos); + addch('#'); + } + + /* Get ready to move... */ + + curr.x = spos.x; + curr.y = spos.y; + + while (distance) + { + /* Move to new position */ + + curr.x += delt.x; + curr.y += delt.y; + + /* Check if we are at the turn place, if so do the turn */ + + if (distance == turn_spot && turn_distance > 0) + while (turn_distance--) + { + cmov(curr); + addch(PASSAGE); + curr.x += turn_delta.x; + curr.y += turn_delta.y; + } + + /* Continue digging along */ + + cmov(curr); + addch(PASSAGE); + distance--; + } + + curr.x += delt.x; + curr.y += delt.y; + + if (!ce(curr, epos)) + msg("Warning, connectivity problem on this level."); +} + +/* + door() + Add a door or possibly a secret door also enters the door in the exits + array of the room. +*/ + +void +door(struct room *rm, coord *cp) +{ + char a_door; + + cmov(*cp); + + a_door = (rnd(10)r_exit[rm->r_nexits++] = *cp; +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/player.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/player.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,538 @@ +/* + player.c - functions for dealing with special player abilities + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +/* + * Pray to a deity + * + * 00-10 Good stuff happens + * 11-40 A good deity answers + * 41-60 Nothing happens + * 61-90 A bad deity answers, but with good results + * 91-99 You were better off before + */ + +void +prayer(void) +{ + int chance, i, times; + char num_str[20]; + int ch; + struct linked_list *item; + struct thing *tp; + int is_godly; + + if (player.t_praycnt > pstats.s_lvl) + { + msg("Are you sure you want to bother the gods?"); + ch = readchar(); + + if (tolower(ch) != 'y') + { + after = FALSE; + return; + } + else + msg("Here goes..."); + } + + msg("You are surrounded by orange smoke..."); + + if (rnd(3) == 0) + luck--; + + if (is_wearing(R_PIETY) || (rnd(luck) == 0 && + (player.t_ctype == C_DRUID || player.t_ctype == C_CLERIC || + ((player.t_ctype == C_PALADIN || player.t_ctype == C_RANGER) + && pstats.s_lvl > 8)))) + is_godly = ISBLESSED; + else + is_godly = ISNORMAL; + + if (is_wearing(R_PIETY)) + player.t_praycnt += rnd(2); + else + player.t_praycnt++; + + if (wizard) + { + msg("What roll?[0..%d] ", 99); + ch = get_string(num_str, cw); + + if (ch == QUIT) + { + msg(""); + return; + } + chance = atoi(num_str); + } + else + { + chance = rnd(100) + roll(10, luck) - 5; + + if (player.t_praycnt > pstats.s_lvl) + chance += 50; + + if (is_godly) + chance -= 50; + } + + chance = max(0, min(chance, 100)); + + if (chance == 0) + { + msg("The heavens open and glorious radiance surrounds you!"); + + pstats.s_hpt = max_stats.s_hpt; + pstats.s_power = max_stats.s_power; + + if (is_godly) + times = 8; + else + times = 1; + + /* + * kill all monsters surrounding the hero except unique ones + * This will change when I implement the deity option. If + * The deity is "stronger" than the unique monster, then the + * monster will be killed. + */ + + for (i = 0; i < times; i++) + { + item = f_mons_a(player.t_pos.y, player.t_pos.x, TRUE); + + if (item) + { + tp = THINGPTR(item); + + msg("A bolt of eldritch energy strikes down the %s!", + monsters[tp->t_index].m_name); + + killed(NULL, item, NOMESSAGE, POINTS); + } + } + } + else if (chance == 2) + { + msg("Aule, Lord of Crafts, hears your call."); + read_scroll(&player, S_MAKEITEMEM, is_godly); + } + + /* Save 3-9 for other wonderful stuff */ + else if (chance < 15) + { + msg("Orome, Lord of Forests, hears your call."); + read_scroll(&player, S_SUMMON, is_godly); + } + else if (chance < 20) + { + msg("Hermes, the Winged Messenger, hears your call."); + quaff(&player, P_HASTE, is_godly); + } + else if (chance < 25) + { + msg("Lorien, Master of Dreams, hears your call."); + read_scroll(&player, S_SLEEP, is_godly); + } + else if (chance < 30) + { + msg("Este, Lady of Healing, hears your call."); + quaff(&player, P_RESTORE, is_godly); + quaff(&player, P_HEALING, is_godly); + } + else if (chance < 35) + { + msg("Thor, God of Thunder, hears your call."); + msg("A bolt of lighting strikes you!"); + read_scroll(&player, S_ELECTRIFY, is_godly); + } + else if (chance < 40) + { + msg("Lorien, Master of Illusion, hears your call."); + quaff(&player, P_DISGUISE, is_godly); + } + else if (chance < 60) /* Nothing happens */ + { + msg("Boccob, the Uncaring, ignores you."); + } + + /* You don't really want one of these gods answering your call */ + + else if (chance < 65) + { + msg("Jubilex, Master of Slimes and Oozes, hears your call."); + read_scroll(&player, S_HOLD, is_godly); + luck++; + } + else if (chance < 70) + { + msg("Sauron, Lord of the Ring, hears your call."); + quaff(&player, P_INVIS, is_godly); + luck++; + } + else if (chance < 75) + { + msg("Orcus, Lord of Undead, hears your call."); + quaff(&player, P_PHASE, is_godly); + luck++; + } + else if (chance < 80) + { + msg("Incabulos, God of Evil Sendings, hears your call."); + quaff(&player, P_CLEAR, is_godly); + luck++; + } + else if (chance < 85) + { + msg("Raxivort, Night Flutterer, hears your call."); + quaff(&player, P_SEEINVIS, is_godly); + luck++; + } + else if (chance < 90) + { + msg("Morgoth, Lord of Fire, hears your call."); + quaff(&player, P_FIRERESIST, is_godly); + luck++; + } + else if (chance < 100) /* You are in for it now! */ + { + msg("You fall into a horrible trance-like state."); + no_command += SLEEPTIME; + } + if (chance == 100) + { + msg("The heavens open - but wait!"); + msg("A bolt of eldritch energy strikes you!"); + + if (pstats.s_hpt > 1) + pstats.s_hpt /= 2; + + msg("The gods must be angry with you."); + } +} + +/* Routines for thieves */ + +/* + gsense() + Sense gold returns TRUE if gold was detected +*/ + +int +gsense(void) +{ + if (lvl_obj != NULL) + { + struct linked_list *gitem; + struct object *cur; + int gtotal = 0; + + wclear(hw); + + for (gitem = lvl_obj; gitem != NULL; gitem = next(gitem)) + { + cur = OBJPTR(gitem); + + if (cur->o_type == GOLD) + { + gtotal += cur->o_count; + mvwaddch(hw, cur->o_pos.y, cur->o_pos.x, GOLD); + + } + } + + if (gtotal) + { + msg("You sense gold!"); + overlay(hw, cw); + return(TRUE); + } + } + + nothing_message(ISNORMAL); + + return(FALSE); +} + + +/* + is_stealth() + is player quiet about something +*/ + +int +is_stealth(struct thing *tp) +{ + return (rnd(25) < tp->t_stats.s_dext || + (tp == &player && is_wearing(R_STEALTH))); +} + +/* + steal() + Steal in direction given in delta +*/ + +void +steal(void) +{ + struct linked_list *item; + struct thing *tp; + coord new_pos; + short thief_bonus; + char *unsuccess = ""; + char *gain = ""; + char *notice = "is not"; + + new_pos.y = hero.y + delta.y; + new_pos.x = hero.x + delta.x; + + /* Anything there? */ + + if (new_pos.y < 0 || new_pos.y > LINES - 3 || + new_pos.x < 0 || new_pos.x > COLS - 1 || + mvwinch(mw, new_pos.y, new_pos.x) == ' ') + { + msg("There is no one to steal from."); + return; + } + + if ((item = find_mons(new_pos.y, new_pos.x)) == NULL) + return; + + tp = THINGPTR(item); + + /* Can player steal something unnoticed? */ + + if (player.t_ctype == C_THIEF || player.t_ctype == C_NINJA) + thief_bonus = 10; + else + thief_bonus = -50; + + if (rnd(50) >= 3 * pstats.s_dext + thief_bonus) + { + chase_it(&new_pos, &player); + turn_off(*tp, ISFRIENDLY); + notice = "is"; + } + + if (rnd(100) < + (thief_bonus + 2 * pstats.s_dext + 5 * pstats.s_lvl - + 5 * (tp->t_stats.s_lvl - 3))) + { + struct linked_list *s_item, *pack_ptr; + int cnt = 0; + + s_item = NULL; /* Start stolen goods out as nothing */ + + /* Find a good item to take */ + + if (tp->t_pack != NULL) + { + /* Count up the number of items in the monster's pack */ + + for (pack_ptr = tp->t_pack; pack_ptr != NULL; pack_ptr = next(pack_ptr)) + cnt++; + + /* Pick one */ + cnt = rnd(cnt); + + /* Take it from the monster */ + + for (pack_ptr = tp->t_pack; --cnt == 0; pack_ptr = next(pack_ptr)) + ; + + s_item = pack_ptr; + detach(tp->t_pack, s_item); + + /* Give it to player */ + + if (add_pack(s_item, MESSAGE) == FALSE) + { + (OBJPTR(s_item))->o_pos = hero; + fall(&player, s_item, TRUE, FALSE); + } + + /* Get points for stealing from unfriendly monsters */ + + if (off(*tp, ISFRIENDLY)) + { + if (player.t_ctype == C_THIEF) + pstats.s_exp += 2 * tp->t_stats.s_exp / 3; + else + pstats.s_exp += tp->t_stats.s_exp / min(pstats.s_lvl, 10); + + check_level(); + } + } + else + { + gain = " gains you nothing and"; + } + } + else + { + unsuccess = " unsuccessful"; + } + + msg("Your%s attempt%s %s noticed.", unsuccess, gain, notice); +} + +/* + affect() + cleric affecting undead +*/ + +void +affect(void) +{ + struct linked_list *item; + struct thing *tp; + char *mname; + coord new_pos; + int is_godly; + int effective_level; + + if (player.t_ctype != C_CLERIC && player.t_ctype != C_PALADIN && + !is_wearing(R_PIETY)) + { + msg("Only clerics and paladins can affect undead."); + return; + } + + is_godly = (player.t_ctype == C_CLERIC || player.t_ctype == C_PALADIN); + + if (is_godly && is_wearing(R_PIETY)) + effective_level = 2 * pstats.s_lvl; + else + effective_level = pstats.s_lvl; + + new_pos.y = hero.y + delta.y; + new_pos.x = hero.x + delta.x; + + /* Anything there? */ + + if (new_pos.y < 0 || new_pos.y > LINES - 3 || + new_pos.x < 0 || new_pos.x > COLS - 1 || + mvwinch(mw, new_pos.y, new_pos.x) == ' ') + { + msg("Nothing to affect."); + return; + } + + if ((item = find_mons(new_pos.y, new_pos.x)) == NULL) + { + debug("Affect what @ %d,%d?", new_pos.y, new_pos.x); + return; + } + + tp = THINGPTR(item); + mname = monsters[tp->t_index].m_name; + + if (off(*tp, ISUNDEAD)) + { + msg("Your holy symbol has no effect on the %s.", mname); + goto annoy; + } + + if (on(*tp, WASTURNED)) + { + msg("Your holy symbol merely enrages the %s.", mname); + goto annoy; + } + + /* Can cleric destroy it? */ + + if (effective_level >= 3 * tp->t_stats.s_lvl) + { + msg("You have destroyed the %s.", mname); + killed(&player, item, NOMESSAGE, POINTS); + return; + } + + /* Can cleric turn it? */ + + if (rnd(100) + 1 > + (100 * ((2 * tp->t_stats.s_lvl) - effective_level)) / + effective_level) + { + msg("You have turned the %s.", mname); + turn_on(*tp, WASTURNED); /* One turn per monster */ + turn_on(*tp, ISRUN); + turn_on(*tp, ISFLEE); + + /* If monster was suffocating, stop it */ + if (on(*tp, DIDSUFFOCATE)) + { + turn_off(*tp, DIDSUFFOCATE); + extinguish_fuse(FUSE_SUFFOCATE); + } + + /* If monster held us, stop it */ + if (on(*tp, DIDHOLD) && (--hold_count == 0)) + turn_off(player, ISHELD); + + turn_off(*tp, DIDHOLD); + + return; + } + + msg("The %s momentarily recoils from your holy symbol.", mname); + +annoy: + + if (off(*tp, WASTURNED)) + chase_it(&new_pos, &player); +} + +/* + undead_sense() + cleric or paladin finding the ungodly +*/ + +void +undead_sense(void) +{ + struct linked_list *item; + struct thing *tp; + int showit = FALSE; + + wclear(hw); + + for (item = mlist; item != NULL; item = next(item)) + { + tp = THINGPTR(item); + + if (on(*tp, ISUNDEAD)) + { + mvwaddch(hw, tp->t_pos.y, tp->t_pos.x, '&'); + showit = TRUE; + } + } + + if (showit) + { + msg("You feel the presense of the ungodly."); + overlay(hw, cw); + wrefresh(cw); + wclear(hw); + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/potions.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/potions.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1516 @@ +/* + potions.c - Functions for dealing with potions + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +/* + quaff - drink a potion (or effect a potion-like spell) + + quaffer: who does it + which: which P_POTION (-1 means ask from pack) + flags: ISBLESSED, ISCURSED +*/ + +void +quaff(struct thing *quaffer, int which, int flags) +{ + struct object *obj; + struct thing *th; + struct stats *curp = &(quaffer->t_stats); + struct stats *maxp = &(quaffer->maxstats); + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + int is_potion = (which < 0 ? TRUE : FALSE); + + struct linked_list *item, *titem; + char buf[2 * LINELEN]; + + if (quaffer != &player) + { + monquaff(quaffer, which, flags); + return; + } + + if (is_potion) /* A regular potion */ + { + if ((item = get_item("quaff", POTION)) == NULL) + return; + + obj = OBJPTR(item); + + if (obj->o_type != POTION) + { + msg("You can't drink that!"); + return; + } + + /* Calculate its effect */ + + flags = obj->o_flags; + cursed = obj->o_flags & ISCURSED; + blessed = obj->o_flags & ISBLESSED; + which = obj->o_which; + + /* remove it from the pack */ + + rem_pack(obj); + discard(item); + updpack(); + } + + switch(which) + { + case P_CLEAR: + if (cursed) + { + if (off(player, ISCLEAR)) + { + msg("Wait, what's going on here. Huh? What? Who?"); + + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + + turn_on(player, ISHUH); + } + else + msg("You feel dizzy for a moment, but it passes."); + } + else + { + if (blessed) /* Make player immune for the whole game */ + { + extinguish_fuse(FUSE_UNCLRHEAD); /* If we have a fuse, put it out */ + msg("A strong blue aura surrounds your head."); + } + else /* Just light a fuse for how long player is safe */ + { + if (off(player, ISCLEAR)) + { + light_fuse(FUSE_UNCLRHEAD, 0, CLRDURATION, AFTER); + msg("A faint blue aura surrounds your head."); + } + else /* If have fuse lengthen, else permanently clear */ + { + if (find_slot(FUSE_UNCLRHEAD,FUSE) == NULL) + msg("Your blue aura continues to glow strongly."); + else + { + lengthen_fuse(FUSE_UNCLRHEAD, CLRDURATION); + msg("Your blue aura brightens for a moment."); + } + } + } + + turn_on(player, ISCLEAR); + + /* If player is confused, unconfuse him */ + + if (on(player, ISHUH)) + { + extinguish_fuse(FUSE_UNCONFUSE); + unconfuse(NULL); + } + } + break; + + case P_HEALING: + if (cursed) + { + if (player.t_ctype != C_PALADIN + && !(player.t_ctype == C_NINJA + && curp->s_lvl > 12) + && !save(VS_POISON)) + { + feel_message(); + curp->s_hpt /= 2; + curp->s_power /= 2; + + if ((curp->s_hpt -= 1) <= 0) + { + death(D_POISON); + return; + } + } + else + msg("You feel momentarily sick."); + } + else + { + int nsides = (blessed ? 8 : 4); + int hpt_gain = roll(curp->s_lvl, nsides); + int power_gain = roll(curp->s_lvl, nsides); + + if (blessed && on(player, ISHUH)) + { + extinguish_fuse(FUSE_UNCONFUSE); + unconfuse(NULL); + } + + curp->s_hpt = min(curp->s_hpt + hpt_gain, maxp->s_hpt); + + if (is_potion) /* Do not bump power or maximums if spell */ + { + know_items[TYP_POTION][P_HEALING] = TRUE; + curp->s_power = min(curp->s_power + power_gain, maxp->s_power); + + if (maxp->s_hpt == curp->s_hpt) + maxp->s_hpt = curp->s_hpt += roll(1, nsides); + + if (maxp->s_power == curp->s_power) + maxp->s_power = curp->s_power += roll(1, nsides); + } + + msg("You begin to feel %sbetter.", blessed ? "much " : ""); + + if (off(player, PERMBLIND)) + sight(NULL); + } + break; + + case P_GAINABIL: + { + int ctype; + + if (!is_potion || pstats.s_arm <= 0) + feel_message(); + else + { + if (blessed) /* add to all attributes */ + { + add_intelligence(FALSE); + add_dexterity(FALSE); + add_strength(FALSE); + add_wisdom(FALSE); + add_const(FALSE); + } + else + { + if (rnd(100) < 70) + /* probably change own ability */ + ctype = player.t_ctype; + else + switch(rnd(4)) + { + case 0: ctype = C_FIGHTER; break; + case 1: ctype = C_MAGICIAN; break; + case 2: ctype = C_CLERIC; break; + case 3: ctype = C_THIEF; break; + } + switch (ctype) + { + case C_FIGHTER:add_strength(cursed); break; + case C_PALADIN:add_strength(cursed); break; + case C_RANGER:add_strength(cursed); break; + case C_MAGICIAN:add_intelligence(cursed); break; + case C_ILLUSION:add_intelligence(cursed); break; + case C_CLERIC:add_wisdom(cursed); break; + case C_DRUID:add_wisdom(cursed); break; + case C_THIEF:add_dexterity(cursed); break; + case C_ASSASIN:add_dexterity(cursed); break; + case C_NINJA:add_dexterity(cursed); break; + default: msg("You're a strange type!"); break; + } + } + + if (rnd(100) < 10) + add_const(cursed); + + if (rnd(100) < 60) + curp->s_arm += (cursed ? 1 : -1); + + if (!cursed) + know_items[TYP_POTION][P_GAINABIL] = TRUE; + } + } + break; + + case P_MONSTDET: + + /* + * Potion of monster detection, if there are monsters, + * detect them + */ + + if (is_potion) + know_items[TYP_POTION][P_MONSTDET] = TRUE; + + if (cursed) + { + int nm = roll(3, 6); + int i; + char ch; + struct room *rp; + coord pos; + + msg("You begin to sense the presence of monsters."); + wclear(hw); + + for (i = 1; i < nm; i++) + { + rp = &rooms[rnd_room()]; + rnd_pos(rp, &pos); + + if (rnd(2)) + ch = 'a' + ucrnd(26); + else + ch = 'A' + ucrnd(26); + + mvwaddch(hw, pos.y, pos.x, ch); + } + waddstr(cw, morestr); + overlay(hw, cw); + wrefresh(cw); + wait_for(' '); + msg(""); + } + else if (mlist != NULL) + { + msg("You begin to sense the presence of monsters."); + waddstr(cw, morestr); + overlay(mw, cw); + wrefresh(cw); + wait_for(' '); + msg(""); + + if (blessed) + turn_on(player, BLESSMONS); + } + else + nothing_message(flags); + break; + + case P_TREASDET: + + /* Potion of magic detection. Show the potions and scrolls */ + + if (cursed) + { + int nm = roll(3, 3); + int i; + char ch; + struct room *rp; + coord pos; + + msg("You sense the presence of magic on this level."); + wclear(hw); + + for (i = 1; i < nm; i++) + { + rp = &rooms[rnd_room()]; + rnd_pos(rp, &pos); + + if (rnd(9) == 0) + ch = BMAGIC; + else if (rnd(9) == 0) + ch = CMAGIC; + else + ch = MAGIC; + + mvwaddch(hw, pos.y, pos.x, ch); + } + waddstr(cw, morestr); + + overlay(hw, cw); + wrefresh(cw); + wait_for(' '); + msg(""); + + if (is_potion) + know_items[TYP_POTION][P_TREASDET] = TRUE; + + break; + } + + if (blessed) + turn_on(player, BLESSMAGIC); + + if (lvl_obj != NULL) + { + struct linked_list *mobj; + struct object *tp; + int showit; + + showit = FALSE; + wclear(hw); + + for (mobj = lvl_obj; mobj != NULL; mobj = next(mobj)) + { + tp = OBJPTR(mobj); + + if (is_magic(tp)) + { + char mag_type = MAGIC; + + if (blessed) + if (tp->o_flags & ISCURSED) + mag_type = CMAGIC; + else if (tp->o_flags & ISBLESSED) + mag_type = BMAGIC; + + showit = TRUE; + mvwaddch(hw, tp->o_pos.y, tp->o_pos.x, mag_type); + } + } + + for (titem = mlist; titem != NULL; titem = next(titem)) + { + struct linked_list *pitem; + + th = THINGPTR(titem); + + for (pitem = th->t_pack; pitem != NULL; pitem = next(pitem)) + { + if (is_magic(OBJPTR(pitem))) + { + showit = TRUE; + mvwaddch(hw, th->t_pos.y, th->t_pos.x,MAGIC); + } + } + } + + if (showit) + { + msg("You sense the presence of magic on this level."); + + if (is_potion) + know_items[TYP_POTION][P_TREASDET] = TRUE; + + waddstr(cw, morestr); + overlay(hw, cw); + wrefresh(cw); + wait_for(' '); + msg(""); + break; + } + } + nothing_message(flags); + break; + + case P_SEEINVIS: + if (cursed) + { + if (off(player, ISBLIND) && !is_wearing(R_SEEINVIS)) + { + msg("A cloak of darkness falls around you."); + turn_on(player, ISBLIND); + light_fuse(FUSE_SIGHT, 0, SEEDURATION, AFTER); + look(FALSE); + } + else + msg("Your eyes stop tingling for a moment."); + } + else if (off(player, PERMBLIND)) + { + if (is_potion) + know_items[TYP_POTION][P_SEEINVIS] = TRUE; + + if (off(player, CANSEE)) + { + turn_on(player, CANSEE); + msg("Your eyes begin to tingle."); + light_fuse(FUSE_UNSEE, 0, blessed ? SEEDURATION * 3 : SEEDURATION, AFTER); + light(&hero); + } + else if (find_slot(FUSE_UNSEE,FUSE) != NULL) + { + nothing_message(ISNORMAL); + lengthen_fuse(FUSE_UNSEE, blessed ? SEEDURATION * 3 : SEEDURATION); + } + sight(NULL); + } + break; + + case P_PHASE: + + if (cursed) + { + msg("You can't move."); + no_command = HOLDTIME; + } + else + { + short duration = (blessed ? 3 : 1); + + if (is_potion) + know_items[TYP_POTION][P_PHASE] = TRUE; + + if (on(player, CANINWALL)) + lengthen_fuse(FUSE_UNPHASE, duration * PHASEDURATION); + else + { + light_fuse(FUSE_UNPHASE, 0, duration * PHASEDURATION, AFTER); + turn_on(player, CANINWALL); + } + msg("You feel %slight-headed!", blessed ? "very " : ""); + } + break; + + case P_RAISELEVEL: + + if (cursed || (!is_potion && pstats.s_lvl > 20)) + lower_level(D_POTION); + else + { + msg("You suddenly feel %smore skillful.", blessed ? "much " : ""); + know_items[TYP_POTION][P_RAISELEVEL] = TRUE; + raise_level(); + + if (blessed) + raise_level(); + } + break; + + case P_HASTE: + if (cursed) /* Slow player down */ + { + if (on(player, ISHASTE)) + { + extinguish_fuse(FUSE_NOHASTE); + nohaste(NULL); + } + else + { + msg("You feel yourself moving %sslower.", + on(player, ISSLOW) ? "even " : ""); + + if (on(player, ISSLOW)) + lengthen_fuse(FUSE_NOSLOW, rnd(4) + 4); + else if (!is_wearing(R_FREEDOM)) + { + turn_on(player, ISSLOW); + player.t_turn = TRUE; + light_fuse(FUSE_NOSLOW, 0, rnd(4) + 4, AFTER); + } + } + } + else + { + if (off(player, ISSLOW)) + msg("You feel yourself moving %sfaster.", + blessed ? "much " : ""); + + add_haste(blessed); + + if (is_potion) + know_items[TYP_POTION][P_HASTE] = TRUE; + } + break; + + case P_RESTORE: + { + int i; + + msg("You are surrounded by an orange mist."); + + if (is_potion) + know_items[TYP_POTION][P_RESTORE] = TRUE; + + if (lost_str) + { + for (i = 0; i < lost_str; i++) + extinguish_fuse(FUSE_RES_STRENGTH); + + lost_str = 0; + } + res_strength(NULL); + + if (lost_dext) + { + for (i = 0; i < lost_dext; i++) + extinguish_fuse(FUSE_UNITCH); + + lost_dext = 0; + } + + res_dexterity(); + res_wisdom(); + res_intelligence(); + curp->s_const = maxp->s_const; + } + break; + + case P_INVIS: + + if (cursed) + { + msg("You feel very noticable."); + quaff(&player, P_SHIELD, ISCURSED); + } + else if (off(player, ISINVIS)) + { + turn_on(player, ISINVIS); + + if (on(player, ISDISGUISE)) + { + turn_off(player, ISDISGUISE); + extinguish_fuse(FUSE_UNDISGUISE); + msg("Your skin feels itchy for a moment."); + } + msg("You have a tingling feeling all over your body."); + light_fuse(FUSE_APPEAR, 0, blessed ? WANDERTIME * 3 : WANDERTIME, AFTER); + PLAYER = IPLAYER; + light(&hero); + + if (is_potion) + know_items[TYP_POTION][P_INVIS] = TRUE; + } + else + lengthen_fuse(FUSE_APPEAR, blessed ? WANDERTIME * 3 : WANDERTIME); + + break; + + case P_SMELL: + + if (cursed) + { + if (on(player, CANSCENT)) + { + turn_off(player, CANSCENT); + extinguish_fuse(FUSE_UNSCENT); + msg("You no longer smell monsters around you."); + } + else if (on(player, ISUNSMELL)) + { + lengthen_fuse(FUSE_UNSCENT, PHASEDURATION); + msg("You feel your nose tingle."); + } + else + { + turn_on(player, ISUNSMELL); + light_fuse(FUSE_SCENT, 0, PHASEDURATION, AFTER); + msg("You can't smell anything now."); + } + } + else + { + short duration = (blessed ? 3 : 1); + + if (is_potion) + know_items[TYP_POTION][P_SMELL] = TRUE; + + if (on(player, CANSCENT)) + lengthen_fuse(FUSE_UNSCENT, duration * PHASEDURATION); + else + { + light_fuse(FUSE_UNSCENT, 0, duration * PHASEDURATION, AFTER); + turn_on(player, CANSCENT); + } + msg("You begin to smell monsters all around you."); + } + break; + + case P_HEAR: + + if (cursed) + { + if (on(player, CANHEAR)) + { + turn_off(player, CANHEAR); + extinguish_fuse(FUSE_HEAR); + msg("You no longer hear monsters around you."); + } + else if (on(player, ISDEAF)) + { + lengthen_fuse(FUSE_HEAR, PHASEDURATION); + msg("You feel your ears burn."); + } + else + { + light_fuse(FUSE_HEAR, 0, PHASEDURATION, AFTER); + turn_on(player, ISDEAF); + msg("You are surrounded by a sudden silence."); + } + } + else + { + short duration = (blessed ? 3 : 1); + + if (is_potion) + know_items[TYP_POTION][P_HEAR] = TRUE; + + if (on(player, CANHEAR)) + lengthen_fuse(FUSE_UNHEAR, duration * PHASEDURATION); + else + { + light_fuse(FUSE_UNHEAR, 0, duration * PHASEDURATION, AFTER); + turn_on(player, CANHEAR); + } + msg("You begin to hear monsters all around you."); + } + break; + + case P_SHERO: + if (cursed) + { + if (on(player, SUPERHERO)) + { + msg("You feel ordinary again."); + turn_off(player, SUPERHERO); + extinguish_fuse(FUSE_UNSHERO); + extinguish_fuse(FUSE_UNBHERO); + } + else if (on(player, ISUNHERO)) + { + msg("Your feeling of vulnerability increases."); + lengthen_fuse(FUSE_SHERO, 5 + rnd(5)); + } + else + { + msg("You feel suddenly vulnerable."); + + if (curp->s_hpt == 1) + { + death(D_POTION); + return; + } + + curp->s_hpt /= 2; + chg_str(-2, FALSE, TRUE); + chg_dext(-2, FALSE, TRUE); + no_command = 3 + rnd(HEROTIME); + turn_on(player, ISUNHERO); + light_fuse(FUSE_SHERO, 0, HEROTIME + rnd(HEROTIME), AFTER); + } + } + else + { + if (on(player, ISFLEE)) + { + turn_off(player, ISFLEE); + msg("You regain your composure."); + } + + if (on(player, ISUNHERO)) + { + extinguish_fuse(FUSE_SHERO); + shero(NULL); + } + else if (on(player, SUPERHERO)) + { + if (find_slot(FUSE_UNBHERO,FUSE)) + lengthen_fuse(FUSE_UNBHERO, HEROTIME+2*rnd(HEROTIME)); + else if (find_slot(FUSE_UNSHERO,FUSE) && !blessed) + lengthen_fuse(FUSE_UNSHERO,HEROTIME+2*rnd(HEROTIME)); + else + { + extinguish_fuse(FUSE_UNSHERO); + unshero(NULL); + light_fuse(FUSE_UNBHERO,0, 2 * (HEROTIME + rnd(HEROTIME)), AFTER); + } + msg("Your feeling of invulnerablity grows stronger."); + } + else + { + turn_on(player, SUPERHERO); + chg_str(10, FALSE, FALSE); + chg_dext(5, FALSE, FALSE); + quaff(quaffer, P_HASTE, ISBLESSED); + quaff(quaffer, P_CLEAR, ISNORMAL); + + if (blessed) + { + light_fuse(FUSE_UNBHERO, 0, HEROTIME + rnd(HEROTIME), AFTER); + msg("You suddenly feel invincible."); + } + else + { + light_fuse(FUSE_UNSHERO, 0, HEROTIME + rnd(HEROTIME), AFTER); + msg("You suddenly feel invulnerable."); + } + + if (is_potion) + know_items[TYP_POTION][P_SHERO] = TRUE; + } + } + break; + + case P_DISGUISE: + if (off(player, ISDISGUISE) && off(player, ISINVIS)) + { + turn_on(player, ISDISGUISE); + msg("Your body shimmers a moment and then changes."); + light_fuse(FUSE_UNDISGUISE, 0, blessed ? GONETIME * 3 : GONETIME, AFTER); + + if (rnd(2)) + PLAYER = 'a' + ucrnd(26); + else + PLAYER = 'A' + ucrnd(26); + + light(&hero); + + if (is_potion) + know_items[TYP_POTION][P_DISGUISE] = TRUE; + } + else if (off(player, ISINVIS)) + lengthen_fuse(FUSE_UNDISGUISE,blessed?GONETIME * 3 : GONETIME); + else + msg("You have an itchy feeling under your skin."); + + break; + + case P_FIRERESIST: + if (cursed) + { + if (!is_wearing(R_FIRERESIST)) + { + msg("Your teeth start clattering."); + + if (on(player, ISHASTE)) + { + extinguish_fuse(FUSE_NOHASTE); + nohaste(NULL); + } + else + { + msg("You feel yourself moving %sslower.", + on(player, ISSLOW) ? "even " : ""); + + if (on(player, ISSLOW)) + lengthen_fuse(FUSE_NOSLOW, rnd(4) + 4); + else if (!is_wearing(R_FREEDOM)) + { + turn_on(player, ISSLOW); + player.t_turn = TRUE; + light_fuse(FUSE_NOSLOW, 0, rnd(4) + 4, AFTER); + } + } + } + else + msg("You feel a brief chill."); + } + else + { + if (is_potion) + know_items[TYP_POTION][P_FIRERESIST] = TRUE; + + if (blessed) + { + extinguish_fuse(FUSE_UNHOT); + msg("You feel a strong continuous warm glow."); + } + else + { + if (off(player, NOFIRE)) + { + light_fuse(FUSE_UNHOT, 0, PHASEDURATION, AFTER); + msg("You feel a warm glow."); + } + else + { + if (find_slot(FUSE_UNHOT,FUSE) == NULL) + msg("Your warm glow continues."); + else + { + lengthen_fuse(FUSE_UNHOT, PHASEDURATION); + msg("Your feel a hot flush."); + } + } + } + + turn_on(player, NOFIRE); + + if (on(player, NOCOLD)) + { + turn_off(player, NOCOLD); + extinguish_fuse(FUSE_UNCOLD); + } + } + break; + + case P_COLDRESIST: + if (cursed) + { + if (!is_wearing(R_COLDRESIST)) + { + msg("Your feel feverishly hot."); + + if (on(player, ISHASTE)) + { + extinguish_fuse(FUSE_NOHASTE); + nohaste(NULL); + } + else + { + msg("You feel yourself moving %sslower.", + on(player, ISSLOW) ? "even " : ""); + + if (on(player, ISSLOW)) + lengthen_fuse(FUSE_NOSLOW, rnd(4) + 4); + else if (!is_wearing(R_FREEDOM)) + { + turn_on(player, ISSLOW); + player.t_turn = TRUE; + light_fuse(FUSE_NOSLOW, 0, rnd(4) + 4, AFTER); + } + } + } + else + msg("You feel a brief touch of heat rash."); + } + else + { + if (is_potion) + know_items[TYP_POTION][P_COLDRESIST] = TRUE; + + if (blessed) + { + extinguish_fuse(FUSE_UNCOLD); + msg("You feel a strong continuous cool breeze."); + } + else + { + if (off(player, NOCOLD)) + { + light_fuse(FUSE_UNCOLD, 0, PHASEDURATION, AFTER); + msg("You feel a cool breeze."); + } + else + { + if (find_slot(FUSE_UNCOLD,FUSE) == NULL) + msg("Your cool feeling continues."); + else + { + lengthen_fuse(FUSE_UNCOLD, PHASEDURATION); + msg("The cool breeze blows more strongly."); + } + } + } + + turn_on(player, NOCOLD); + + if (on(player, NOFIRE)) + { + extinguish_fuse(FUSE_UNHOT); + turn_off(player, NOFIRE); + } + } + break; + + case P_HASOXYGEN: + if (cursed) + { + if (!is_wearing(R_BREATHE)) + { + msg("You can't breathe."); + no_command = HOLDTIME; + } + else + { + msg("You feel a momentary shortness of breath."); + } + } + else + { + short duration = (blessed ? 3 : 1); + + if (is_potion) + know_items[TYP_POTION][P_HASOXYGEN] = TRUE; + + if (on(player, HASOXYGEN)) + lengthen_fuse(FUSE_UNBREATHE, duration * PHASEDURATION); + else + { + light_fuse(FUSE_UNBREATHE, 0, duration * PHASEDURATION, AFTER); + turn_on(player, HASOXYGEN); + } + + if (!is_wearing(R_BREATHE)) + msg("The air seems %sless polluted.", + blessed ? "much " : ""); + } + break; + + case P_LEVITATION: + if (cursed) + { + msg("You can't move."); + no_command = HOLDTIME; + } + else + { + short duration = (blessed ? 3 : 1); + + if (is_potion) + know_items[TYP_POTION][P_LEVITATION] = TRUE; + + if (on(player, CANFLY)) + lengthen_fuse(FUSE_UNFLY, duration * WANDERTIME); + else + { + light_fuse(FUSE_UNFLY, 0, duration * WANDERTIME, AFTER); + turn_on(player, CANFLY); + } + + if (!is_wearing(R_LEVITATION)) + msg("You %sbegin to float in the air!", + blessed ? "quickly " : ""); + } + break; + + case P_REGENERATE: + if (cursed) + { + quaff(quaffer, P_HEALING, ISCURSED); + quaff(quaffer, P_HASTE, ISCURSED); + } + else + { + short duration = (blessed ? 3 : 1) * HUHDURATION; + + if (is_potion) + know_items[TYP_POTION][P_REGENERATE] = TRUE; + + if (on(player, SUPEREAT)) + lengthen_fuse(FUSE_UNSUPEREAT, duration); + else + { + light_fuse(FUSE_UNSUPEREAT, 0, duration, AFTER); + turn_on(player, SUPEREAT); + } + + if (on(player, ISREGEN)) + lengthen_fuse(FUSE_UNREGEN, duration); + else + { + light_fuse(FUSE_UNREGEN, 0, duration, AFTER); + turn_on(player, ISREGEN); + } + + if (!is_wearing(R_REGEN)) + msg("You feel %shealthier!", blessed ? "much " : ""); + } + + case P_SHIELD: + { + int adjustment = 0; + + if (on(player, HASSHIELD)) /* cancel old spell */ + { + extinguish_fuse(FUSE_UNSHIELD); + unshield(NULL); + } + + if (cursed) + adjustment = 2; + else if (blessed) + { + msg("Your skin feels very rigid."); + + switch (player.t_ctype) + { + case C_FIGHTER: + case C_PALADIN: + case C_RANGER: + adjustment = -3; + break; + default: + adjustment = -5; + } + } + else + { + msg("Your skin hardens."); + adjustment = -2; + } + + pstats.s_arm += adjustment; + pstats.s_acmod += adjustment; + turn_on(player, HASSHIELD); + light_fuse(FUSE_UNSHIELD,0,(blessed ? 3 : 1) * SEEDURATION, AFTER); + + if (is_potion) + know_items[TYP_POTION][P_SHIELD] = TRUE; + } + break; + + case P_TRUESEE: + if (cursed) + { + turn_on(player, PERMBLIND); + + if (on(player, ISBLIND)) + { + msg("The gloom around you thickens."); + lengthen_fuse(FUSE_SIGHT, SEEDURATION); + } + else + { + msg("A mantle of darkness falls around you."); + turn_on(player, ISBLIND); + light_fuse(FUSE_SIGHT, 0, SEEDURATION, AFTER); + look(FALSE); + } + look(FALSE); + } + else if (on(player, PERMBLIND)) + { + if (blessed || is_potion) + { + turn_off(player, PERMBLIND); + sight(NULL); + goto let_there_be_light; + } + else + nothing_message(ISBLESSED); + } + else + let_there_be_light: + if (off(player, CANSEE)) + { + turn_on(player, CANSEE); + msg("You feel especially perceptive."); + light_fuse(FUSE_UNTRUESEE, 0, blessed ? SEEDURATION * 3 + : SEEDURATION, AFTER); + light(&hero); + } + else if (find_slot(FUSE_UNSEE,FUSE) != NULL) + { + nothing_message(ISNORMAL); + lengthen_fuse(FUSE_UNTRUESEE, blessed ? SEEDURATION * 3 + : SEEDURATION); + } + + break; + + default: + msg("What an odd tasting potion!"); + return; + } + + status(FALSE); + + if (is_potion) + { + if (!cursed && know_items[TYP_POTION][which] && + guess_items[TYP_POTION][which]) + { + ur_free(guess_items[TYP_POTION][which]); + guess_items[TYP_POTION][which] = NULL; + } + else if (askme && !know_items[TYP_POTION][which] && + guess_items[TYP_POTION][which] == NULL) + { + msg(terse ? "Call it: " : "What do you want to call it? "); + + if (get_string(buf, cw) == NORM) + { + guess_items[TYP_POTION][which] = + new_alloc(strlen(buf) + 1); + strcpy(guess_items[TYP_POTION][which], buf); + } + } + food_left += (blessed ? rnd(100) : (cursed ? -rnd(100) : rnd(50))); + } +} + +/* + lower_level() + Lower a level of experience +*/ + +void +lower_level(int who) +{ + int fewer, nsides = 0, i; + + if (--pstats.s_lvl == 0) + { + death(who); /* All levels gone */ + return; + } + + msg("You suddenly feel less skillful."); + pstats.s_exp = 1L; + init_exp(); + + for (i = 2; i <= pstats.s_lvl; i++) + { + if (max_stats.s_exp < 0x3fffffffL) /* 2^30 - 1 */ + max_stats.s_exp *= 2L; /* twice as many for next */ + } + + switch (player.t_ctype) + { + case C_FIGHTER: nsides = 12;break; + case C_PALADIN: nsides = 12;break; + case C_RANGER: nsides = 12; break; + case C_MAGICIAN: nsides = 4;break; + case C_ILLUSION: nsides = 4;break; + case C_CLERIC: nsides = 8; break; + case C_DRUID: nsides = 8; break; + case C_THIEF: nsides = 6; break; + case C_ASSASIN: nsides = 6; break; + case C_NINJA: nsides = 6; break; + } + + fewer = max(1, roll(1, 16 - nsides) + int_wis_bonus()); + pstats.s_power -= fewer; + max_stats.s_power -= fewer; + + fewer = max(1, roll(1, nsides) + const_bonus()); + pstats.s_hpt -= fewer; + max_stats.s_hpt -= fewer; + + if (pstats.s_hpt < 1) + pstats.s_hpt = 1; + + if (max_stats.s_hpt < 1) + { + death(who); + return; + } +} + +void +res_dexterity(void) +{ + if (lost_dext) + { + chg_dext(lost_dext, FALSE, FALSE); + lost_dext = 0; + } + else + { + pstats.s_dext = max_stats.s_dext + ring_value(R_ADDHIT) + + (on(player, POWERDEXT) ? 10 : 0) + + (on(player, SUPERHERO) ? 5 : 0); + } + +} + + +/* + res_wisdom() + Restore player's wisdom +*/ + +void +res_wisdom(void) +{ + int ring_str; + + /* Discount the ring value */ + + ring_str = ring_value(R_ADDWISDOM) + (on(player, POWERWISDOM) ? 10 : 0); + pstats.s_wisdom -= ring_str; + + if (pstats.s_wisdom < max_stats.s_wisdom) + pstats.s_wisdom = max_stats.s_wisdom; + + /* Redo the rings */ + + pstats.s_wisdom += ring_str; +} + +/* + res_intelligence() + Restore player's intelligence +*/ + +void +res_intelligence(void) +{ + int ring_str; + + /* Discount the ring value */ + + ring_str = ring_value(R_ADDINTEL) + (on(player, POWERINTEL) ? 10 : 0); + pstats.s_intel -= ring_str; + + if (pstats.s_intel < max_stats.s_intel) + pstats.s_intel = max_stats.s_intel; + + /* Redo the rings */ + + pstats.s_intel += ring_str; +} + + +/* + add_strength() + Increase player's strength +*/ + +void +add_strength(int cursed) +{ + + if (cursed) + { + msg("You feel slightly weaker now."); + chg_str(-1, FALSE, FALSE); + } + else + { + msg("You feel stronger now. What bulging muscles!"); + + if (lost_str != 0) + { + lost_str--; + chg_str(1, FALSE, FALSE); + } + else + chg_str(1, TRUE, FALSE); + } +} + + +/* + add_intelligence() + Increase player's intelligence +*/ + +void +add_intelligence(int cursed) +{ + int ring_str; /* Value of ring strengths */ + + /* Undo any ring changes */ + + ring_str = ring_value(R_ADDINTEL) + (on(player, POWERINTEL) ? 10 : 0); + pstats.s_intel -= ring_str; + + /* Now do the potion */ + + if (cursed) + { + msg("You feel slightly less intelligent now."); + pstats.s_intel = max(pstats.s_intel - 1, 3); + } + else + { + msg("You feel more intelligent now. What a mind!"); + pstats.s_intel = min(pstats.s_intel + 1, 25); + } + + /* Adjust the maximum */ + + if (max_stats.s_intel < pstats.s_intel) + max_stats.s_intel = pstats.s_intel; + + /* Now put back the ring changes */ + pstats.s_intel += ring_str; +} + + +/* + add_wisdom() + Increase player's wisdom +*/ + +void +add_wisdom(int cursed) +{ + int ring_str; /* Value of ring strengths */ + + /* Undo any ring changes */ + + ring_str = ring_value(R_ADDWISDOM) + (on(player, POWERWISDOM) ? 10 : 0); + pstats.s_wisdom -= ring_str; + + /* Now do the potion */ + + if (cursed) + { + msg("You feel slightly less wise now."); + pstats.s_wisdom = max(pstats.s_wisdom - 1, 3); + } + else + { + msg("You feel wiser now. What a sage!"); + pstats.s_wisdom = min(pstats.s_wisdom + 1, 25); + } + + /* Adjust the maximum */ + + if (max_stats.s_wisdom < pstats.s_wisdom) + max_stats.s_wisdom = pstats.s_wisdom; + + /* Now put back the ring changes */ + pstats.s_wisdom += ring_str; +} + + +/* + add_dexterity() + Increase player's dexterity +*/ + +void +add_dexterity(int cursed) +{ + /* Now do the potion */ + + if (cursed) + { + msg("You feel less dextrous now."); + chg_dext(-1, FALSE, TRUE); + } + else + { + msg("You feel more dextrous now. Watch those hands!"); + + if (lost_dext != 0) + { + lost_dext--; + chg_dext(1, FALSE, FALSE); + } + else + chg_dext(1, TRUE, FALSE); + } +} + + +/* + Increase player's constitution +*/ + +void +add_const(int cursed) +{ + /* Do the potion */ + + if (cursed) + { + msg("You feel slightly less healthy now."); + pstats.s_const = max(pstats.s_const - 1, 3) + + (on(player, POWERCONST) ? 10 : 0); + } + else + { + msg("You feel healthier now."); + pstats.s_const = min(pstats.s_const + 1, 25) + + (on(player, POWERCONST) ? 10 : 0); + } + + /* Adjust the maximum */ + + if (max_stats.s_const < pstats.s_const - (on(player, POWERCONST) ? 10 : 0)) + max_stats.s_const = pstats.s_const; +} + +/* + monquaff() + monster gets the effect +*/ + +void +monquaff(struct thing *quaffer, int which, int flags) +{ + struct stats *curp = &(quaffer->t_stats); + struct stats *maxp = &(quaffer->maxstats); + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + + switch(which) + { + case P_SEEINVIS: + if (cursed) + turn_on(*quaffer, ISHUH); + else + turn_on(*quaffer, CANSEE); + break; + + case P_GAINABIL: + if (cursed) + curp->s_intel /= 2; + else + curp->s_power = maxp->s_power; + break; + + case P_CLEAR: + if (cursed) + turn_on(*quaffer, ISHUH); + else + turn_on(*quaffer, ISHUH); + break; + + case P_HEALING: + if (cursed) + { + curp->s_hpt /= 2; + curp->s_power /= 2; + } + else + { + int nsides = (blessed ? 8 : 4); + int hpt_gain = roll(curp->s_lvl, nsides); + int power_gain = roll(curp->s_lvl, nsides); + + curp->s_hpt = min(curp->s_hpt + hpt_gain, maxp->s_hpt); + curp->s_power = min(curp->s_power + power_gain, maxp->s_power); + } + break; + + case P_HASTE: + if (cursed) + { + if (on(*quaffer, ISHASTE)) + turn_off(*quaffer, ISHASTE); + else + turn_on(*quaffer, ISSLOW); + } + else + turn_on(*quaffer, ISHASTE); + break; + + case P_INVIS: + turn_on(*quaffer, ISINVIS); + + if (cansee(quaffer->t_pos.y, quaffer->t_pos.x)) + seemsg("The monster dissappears!"); + + break; + + case P_REGENERATE: + if (cursed) + { + quaff(quaffer, P_HEALING, ISCURSED); + quaff(quaffer, P_HASTE, ISCURSED); + } + else + turn_on(*quaffer, ISREGEN); + break; + + case P_SHERO: + if (on(*quaffer, ISFLEE)) + turn_off(*quaffer, ISFLEE); + else + { + turn_on(*quaffer, SUPERHERO); + quaff(quaffer, P_HASTE, ISBLESSED); + quaff(quaffer, P_CLEAR, ISNORMAL); + } + break; + + case P_PHASE: + if (cursed) + quaffer->t_no_move += HOLDTIME; + else + turn_on(*quaffer, CANINWALL); + break; + + default: + debug("'%s' is a strange potion for a monster to quaff!", + p_magic[which].mi_name); + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/random.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/random.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,23 @@ +/* + random.c - random and associated routines + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1992, 1993, 1995 Herb Chong + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include "rogue.h" + +void +ur_srandom(unsigned x) +{ + md_srandom(x); +} + +long +ur_random(void) +{ + return( md_random() ); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/rings.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/rings.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,354 @@ +/* + rings.c - Routines dealing specificaly with rings + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +void +ring_on(void) +{ + struct object *obj; + struct linked_list *item; + int ring; + char buf[2 * LINELEN]; + + if ((item = get_item("put on", RING)) == NULL) + return; + + obj = OBJPTR(item); + + if (obj->o_type != RING) + { + msg("You can't put that on!"); + return; + } + + /* find out which hand to put it on */ + + if (is_current(obj)) + { + msg("Already wearing that!"); + return; + } + + if (cur_ring[LEFT_1] == NULL) + ring = LEFT_1; + else if (cur_ring[LEFT_2] == NULL) + ring = LEFT_2; + else if (cur_ring[LEFT_3] == NULL) + ring = LEFT_3; + else if (cur_ring[LEFT_4] == NULL) + ring = LEFT_4; + else if (cur_ring[LEFT_5] == NULL) + ring = LEFT_5; + else if (cur_ring[RIGHT_1] == NULL) + ring = RIGHT_1; + else if (cur_ring[RIGHT_2] == NULL) + ring = RIGHT_2; + else if (cur_ring[RIGHT_3] == NULL) + ring = RIGHT_3; + else if (cur_ring[RIGHT_4] == NULL) + ring = RIGHT_4; + else if (cur_ring[RIGHT_5] == NULL) + ring = RIGHT_5; + else + { + msg("You already have on ten rings."); + return; + } + + cur_ring[ring] = obj; + + /* Calculate the effect it has on the poor guy. */ + + switch (obj->o_which) + { + case R_ADDSTR: + pstats.s_str += obj->o_ac; + break; + case R_ADDHIT: + pstats.s_dext += obj->o_ac; + break; + case R_ADDINTEL: + pstats.s_intel += obj->o_ac; + break; + case R_ADDWISDOM: + pstats.s_wisdom += obj->o_ac; + break; + case R_FREEDOM: + turn_off(player, ISHELD); + hold_count = 0; + break; + case R_TRUESEE: + if (off(player, PERMBLIND)) + { + turn_on(player, CANTRUESEE); + msg("You become more aware of your surroundings."); + sight(NULL); + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + break; + + case R_SEEINVIS: + if (off(player, PERMBLIND)) + { + turn_on(player, CANTRUESEE); + msg("Your eyes begin to tingle."); + sight(NULL); + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + break; + + case R_AGGR: + aggravate(); + break; + + case R_CARRYING: + updpack(); + break; + + case R_LEVITATION: + msg("You begin to float in the air!"); + break; + + case R_LIGHT: + if (roomin(hero) != NULL) + { + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + } + + status(FALSE); + + if (know_items[TYP_RING][obj->o_which] && + guess_items[TYP_RING][obj->o_which]) + { + mem_free(guess_items[TYP_RING][obj->o_which]); + guess_items[TYP_RING][obj->o_which] = NULL; + } + else if (!know_items[TYP_RING][obj->o_which] && + askme && + (obj->o_flags & ISKNOW) == 0 && + guess_items[TYP_RING][obj->o_which] == NULL) + { + mpos = 0; + msg("What do you want to call it? "); + + if (get_string(buf, cw) == NORM) + { + guess_items[TYP_RING][obj->o_which] = + new_alloc(strlen(buf) + 1); + strcpy(guess_items[TYP_RING][obj->o_which], buf); + } + msg(""); + } +} + +void +ring_off(void) +{ + struct object *obj; + struct linked_list *item; + + if (cur_ring[LEFT_1] == NULL && cur_ring[LEFT_2] == NULL && + cur_ring[LEFT_3] == NULL && cur_ring[LEFT_4] == NULL && + cur_ring[LEFT_5] == NULL && + cur_ring[RIGHT_1] == NULL && cur_ring[RIGHT_2] == NULL && + cur_ring[RIGHT_3] == NULL && cur_ring[RIGHT_4] == NULL && + cur_ring[RIGHT_5] == NULL) + { + msg("You aren't wearing any rings."); + return; + } + else if ((item = get_item("remove", RING)) == NULL) + return; + + mpos = 0; + obj = OBJPTR(item); + + if ((obj = OBJPTR(item)) == NULL) + msg("You are not wearing that!"); + + if (dropcheck(obj)) + { + switch (obj->o_which) + { + case R_SEEINVIS: + msg("Your eyes stop tingling."); + break; + + case R_CARRYING: + updpack(); + break; + + case R_LEVITATION: + msg("You float gently to the ground."); + break; + + case R_LIGHT: + if (roomin(hero) != NULL) + { + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + break; + + case R_TRUESEE: + msg("Your sensory perceptions return to normal."); + break; + } + + msg("Was wearing %s.", inv_name(obj, LOWERCASE)); + } +} + +/* + ring_eat() + how much food does this ring use up? +*/ + +int +ring_eat(int hand) +{ + int ret_val = 0; + int ac; + + if (cur_ring[hand] != NULL) + { + switch (cur_ring[hand]->o_which) + { + case R_REGEN: + case R_VREGEN: + ret_val = rnd(pstats.s_lvl > 10 ? 10 : pstats.s_lvl); + + case R_DIGEST: + + ac = cur_ring[hand]->o_ac; + + if (ac < 0 && rnd(1 - (ac / 3)) == 0) + ret_val = -ac + 1; + else if (rnd((ac / 2) + 2) == 0) + ret_val = -1 - ac; + break; + + case R_SEARCH: + ret_val = rnd(100) < 33; + break; + + default: + ret_val = 1; + } + } + + ret_val += rnd(luck); + + return(ret_val); +} + +/* + ring_num() + print ring bonuses +*/ + +char * +ring_num(struct object *obj, char *buf) +{ + char buffer[1024]; + + if (buf == NULL) + return("A bug in UltraRogue #101"); + + buf[0] = 0; + + if (obj->o_flags & ISKNOW) + { + switch (obj->o_which) + { + case R_SEARCH: + case R_PROTECT: + case R_ADDSTR: + case R_ADDDAM: + case R_ADDHIT: + case R_ADDINTEL: + case R_ADDWISDOM: + case R_CARRYING: + case R_VREGEN: + case R_RESURRECT: + case R_TELCONTROL: + case R_REGEN: + case R_PIETY: + case R_WIZARD: + buf[0] = ' '; + strcpy(&buf[1], num(obj->o_ac, 0,buffer)); + break; + + case R_DIGEST: + buf[0] = ' '; + strcpy(&buf[1], num(obj->o_ac < 0 ? + obj->o_ac : obj->o_ac - 1, 0, buffer)); + break; + + default: + if (obj->o_flags & ISCURSED) + strcpy(buf, " cursed"); + break; + } + } + + return(buf); +} + +/* + ring_value() + Return the effect of the specified ring +*/ + +#define ISRING(h, r) (cur_ring[h] != NULL && cur_ring[h]->o_which == r) + +int +ring_value(int type) +{ + int result = 0; + + if (ISRING(LEFT_1, type)) + result += cur_ring[LEFT_1]->o_ac; + if (ISRING(LEFT_2, type)) + result += cur_ring[LEFT_2]->o_ac; + if (ISRING(LEFT_3, type)) + result += cur_ring[LEFT_3]->o_ac; + if (ISRING(LEFT_4, type)) + result += cur_ring[LEFT_4]->o_ac; + if (ISRING(LEFT_5, type)) + result += cur_ring[LEFT_5]->o_ac; + if (ISRING(RIGHT_1, type)) + result += cur_ring[RIGHT_1]->o_ac; + if (ISRING(RIGHT_2, type)) + result += cur_ring[RIGHT_2]->o_ac; + if (ISRING(RIGHT_3, type)) + result += cur_ring[RIGHT_3]->o_ac; + if (ISRING(RIGHT_4, type)) + result += cur_ring[RIGHT_4]->o_ac; + if (ISRING(RIGHT_5, type)) + result += cur_ring[RIGHT_5]->o_ac; + + return(result); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/rip.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/rip.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,661 @@ +/* + rip.c - File for the fun ends Death or a total win + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include +#include "rogue.h" + +static struct sc_ent +{ + int sc_lvl; + long sc_score; + char sc_name[76]; + long sc_gold; + int sc_flags; + int sc_level; + int sc_artifacts; + int sc_monster; +} top_ten[10]; + + +static const char *rip[] = +{ + " __________", + " / \\", + " / REST \\", + " / IN \\", + " / PEACE \\", + " / \\", + " | |", + " | |", + " | killed by |", + " | |", + " | 1993 |", + " *| * * * | *", + " ________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______", + 0 +}; + +/* + death() + Do something really fun when he dies +*/ + +void +death(int monst) +{ + char **dp = (char **) rip, *killer; + struct tm *lt; + time_t date; + char buf[80]; + int c; + + if (is_wearing(R_RESURRECT) || rnd(wizard ? 3 : 67) == 0) + { + int die = TRUE; + + if (resurrect-- == 0) + msg("You've run out of lives."); + else if (!save_resurrect(ring_value(R_RESURRECT))) + msg("Your attempt to return from the grave fails."); + else + { + struct linked_list *item; + struct linked_list *next_item; + struct object *obj; + int rm, flags; + coord pos; + + die = FALSE; + msg("You feel a sudden warmth and then nothingness."); + teleport(); + + if (ring_value(R_RESURRECT) > 1 && rnd(10)) + { + pstats.s_hpt = 2 * pstats.s_const; + pstats.s_const = max(pstats.s_const - 1, 3); + } + else + { + for (item = pack; item != NULL; item = next_item) + { + obj = OBJPTR(item); + + if (obj->o_flags & ISOWNED || obj->o_flags & ISPROT) + { + next_item = next(item); + continue; + } + + flags = obj->o_flags; + obj->o_flags &= ~ISCURSED; + dropcheck(obj); + obj->o_flags = flags; + next_item = next(item); + rem_pack(obj); + + if (obj->o_type == ARTIFACT) + has_artifact &= ~(1 << obj->o_which); + + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &pos); + } + while(winat(pos.y, pos.x) != FLOOR); + + obj->o_pos = pos; + add_obj(item, obj->o_pos.y, obj->o_pos.x); + } + + pstats.s_hpt = pstats.s_const; + pstats.s_const = max(pstats.s_const - roll(2, 2), 3); + } + + chg_str(roll(1, 4), TRUE, FALSE); + pstats.s_lvl = max(pstats.s_lvl, 1); + no_command += 2 + rnd(4); + + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION); + else + light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER); + + turn_on(player, ISHUH); + light(&hero); + } + + if (die) + { + wmove(cw, mpos, 0); + waddstr(cw, morestr); + wrefresh(cw); + wait_for(' '); + } + else + return; + } + + time(&date); + lt = localtime(&date); + clear(); + wclear(cw); + move(8, 0); + + while (*dp) + printw("%s\n", *dp++); + + mvaddstr(14, 28 - ((int)(strlen(whoami) + 1) / 2), whoami); + sprintf(buf, "%d+%ld Points", pstats.s_lvl, pstats.s_exp); + mvaddstr(15, 28 - ((int)(strlen(buf) + 1) / 2), buf); + killer = killname(monst,buf); + mvaddstr(17, 28 - ((int)(strlen(killer) + 1) / 2), killer); + mvaddstr(18, 28, (sprintf(prbuf, "%2d", lt->tm_year), prbuf)); + move(LINES - 1, 0); + + mvaddstr(LINES - 1, 0, retstr); + + while ((c = readcharw(stdscr)) != '\n' && c != '\r') + continue; + idenpack(); + wrefresh(cw); + refresh(); + + score(pstats.s_exp, pstats.s_lvl, KILLED, monst); + byebye(); +} + +/* + score() + figure score and post it. +*/ + +void +score(long amount, int lvl, int flags, int monst) /*ARGSUSED*/ +{ + struct sc_ent *scp=NULL, *sc2=NULL; + int i; + char *killer; + char buf[1024]; + + static const char *reason[] = + { + "killed", + "quit", + "a winner", + "a total winner" + }; + + char *packend; + + if (flags != WINNER && flags != TOTAL && flags != SCOREIT) + { + if (flags == CHICKEN) + packend = "when you quit"; + else + packend = "at your untimely demise"; + + noecho(); + nl(); + refresh(); + showpack(packend); + } + + /* Open file and read list */ + + if (fd_score == NULL) + { + printf("No score file opened\n"); + return; + } + + for (scp = top_ten; scp < &top_ten[10]; scp++) + { + scp->sc_lvl = 0L; + scp->sc_score = 0L; + + for (i = 0; i < 76; i++) + scp->sc_name[i] = ucrnd(255); + + scp->sc_gold = 0L; + scp->sc_flags = rnd(10); + scp->sc_level = rnd(10); + scp->sc_monster = srnd(10); + scp->sc_artifacts = 0; + } + + if (flags != SCOREIT) + { + mvaddstr(LINES - 1, 0, retstr); + refresh(); + fflush(stdout); + wait_for('\n'); + } + + fseek(fd_score, 0L, SEEK_SET); + fread(top_ten, sizeof(top_ten), 1, fd_score); + + /* Insert player in list if need be */ + + if (!waswizard) + { + for (scp = top_ten; scp < &top_ten[10]; scp++) + { + if (lvl > scp->sc_lvl) + break; + + if (lvl == scp->sc_lvl && amount > scp->sc_score) + break; + } + + if (scp < &top_ten[10]) + { + if (flags == WINNER) + sc2 = &top_ten[9]; /* LAST WINNER ALWAYS MAKES IT */ + + while (sc2 > scp) + { + *sc2 = sc2[-1]; + sc2--; + } + + scp->sc_lvl = lvl; + scp->sc_gold = purse; + scp->sc_score = amount; + strcpy(scp->sc_name, whoami); + strcat(scp->sc_name,", "); + strcat(scp->sc_name, which_class(player.t_ctype)); + scp->sc_flags = flags; + + if (flags == WINNER) + scp->sc_level = max_level; + else + scp->sc_level = level; + + scp->sc_monster = monst; + scp->sc_artifacts = has_artifact; + + sc2 = scp; + } + } + + if (flags != SCOREIT) + { + clear(); + refresh(); + endwin(); + } + + /* Print the list */ + + printf("\nTop Ten Adventurers:\n%4s %15s %10s %s\n", + "Rank", "Score", "Gold", "Name"); + + for (scp = top_ten; scp < &top_ten[10]; scp++) + { + if (scp->sc_score) + { + char lev[20]; + + sprintf(lev, "%ld+%ld", scp->sc_lvl, scp->sc_score); + printf("%4d %15s %10ld %s:", scp - top_ten + 1, + lev, + scp->sc_gold, + scp->sc_name); + + if (scp->sc_artifacts) + { + char thangs[80]; + int n; + int first = TRUE; + + thangs[0] = '\0'; + + for (n = 0; n <= maxartifact; n++) + { + if (scp->sc_artifacts & (1 << n)) + { + if (strlen(thangs)) + strcat(thangs, ", "); + + if (first) + { + strcat(thangs, "retrieved "); + first = FALSE; + } + + if (45 - strlen(thangs) < strlen(arts[n].ar_name)) + { + printf("%s\n%32s", thangs," "); + thangs[0] = '\0'; + } + strcat(thangs, arts[n].ar_name); + } + } + + if (strlen(thangs)) + printf("%s,", thangs); + + printf("\n%32s"," "); + } + + printf("%s on level %d",reason[scp->sc_flags],scp->sc_level); + + if (scp->sc_flags == 0) + { + printf(" by \n%32s"," "); + killer = killname(scp->sc_monster, buf); + printf(" %s", killer); + } + + putchar('\n'); + } + } + + if (sc2 != NULL) + { + fseek(fd_score, 0L, SEEK_SET); + /* Update the list file */ + fwrite(top_ten, sizeof(top_ten), 1, fd_score); + } + + fclose(fd_score); +} + +void +total_winner(void) +{ + struct linked_list *item; + struct object *obj; + int worth, oldpurse; + char c; + struct linked_list *bag = NULL; + + clear(); + standout(); + addstr(" \n"); + addstr(" @ @ @ @ @ @@@ @ @ \n"); + addstr(" @ @ @@ @@ @ @ @ @ \n"); + addstr(" @ @ @@@ @ @ @ @ @ @@@ @@@@ @@@ @ @@@ @ \n"); + addstr(" @@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ \n"); + addstr(" @ @ @ @ @ @ @ @@@@ @ @ @@@@@ @ @ @ \n"); + addstr(" @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ \n"); + addstr(" @@@ @@@ @@ @ @ @ @@@@ @@@@ @@@ @@@ @@ @ \n"); + addstr(" \n"); + addstr(" Congratulations, you have made it to the light of day! \n"); + standend(); + addstr("\nYou have joined the elite ranks of those who have \n"); + addstr("escaped the Dungeons of Doom alive. You journey home \n"); + addstr("and sell all your loot at a great profit.\n"); + addstr("The White Council approves the recommendation of\n"); + + if (player.t_ctype == C_FIGHTER) + addstr("the fighters guild and appoints you Lord Protector\n"); + else if (player.t_ctype == C_ASSASIN) + addstr("the assassins guild and appoints you Master Murderer\n"); + else if (player.t_ctype == C_NINJA) + addstr("the ninja guild and appoints you Master of the Wind\n"); + else if (player.t_ctype == C_ILLUSION) + addstr("the illusionists guild and appoints you Master Wizard\n"); + else if (player.t_ctype == C_MAGICIAN) + addstr("the magicians guild and appoints you Master Wizard\n"); + else if (player.t_ctype == C_CLERIC) + addstr("the temple priests and appoints you Master of the Flowers\n"); + else if (player.t_ctype == C_DRUID) + addstr("the temple priests and appoints you Master of the Flowers\n"); + else if (player.t_ctype == C_RANGER) + addstr("the rangers guild and appoints you Master Ranger\n"); + else if (player.t_ctype == C_PALADIN) + addstr("the paladins guild and appoints you Master Paladin\n"); + else if (player.t_ctype == C_THIEF) + { + addstr("the thieves guild under protest and appoints you\n"); + addstr("Master of the Highways\n"); + } + + addstr("of the Land Between the Mountains.\n"); + mvaddstr(LINES - 1, 0, spacemsg); + refresh(); + wait_for(' '); + clear(); + idenpack(); + oldpurse = purse; + mvaddstr(0, 0, " Worth Item"); + + for (c = 'a', item = pack; item != NULL; c++, item = next(item)) + { + obj = OBJPTR(item); + worth = get_worth(obj); + purse += worth; + + if (obj->o_type == ARTIFACT && obj->o_which == TR_PURSE) + bag = obj->o_bag; + + mvprintw(c - 'a' + 1, 0, "%c) %8d %s", c, + worth, inv_name(obj, UPPERCASE)); + } + + if (bag != NULL) + { + mvaddstr(LINES - 1, 0, morestr); + refresh(); + wait_for(' '); + clear(); + mvprintw(0, 0, "Contents of the Magic Purse of Yendor:\n"); + + for (c = 'a', item = bag; item != NULL; c++, item = next(item)) + { + obj = OBJPTR(item); + worth = get_worth(obj); + whatis(item); + purse += worth; + mvprintw(c - 'a' + 1, 0, "%c) %8d %s\n", c, + worth, inv_name(obj, UPPERCASE)); + } + } + + mvprintw(c - 'a' + 1, 0, " %6d Gold Pieces ", oldpurse); + refresh(); + + if (has_artifact == 255) + score(pstats.s_exp, pstats.s_lvl, TOTAL, 0); + else + score(pstats.s_exp, pstats.s_lvl, WINNER, 0); + + byebye(); +} + +char * +killname(int monst, char *buf) +{ + if (buf == NULL) + return("A bug in UltraRogue #102"); + + if (monst >= 0) + { + switch (monsters[monst].m_name[0]) + { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': + sprintf(buf, "an %s", monsters[monst].m_name); + break; + default: + sprintf(buf, "a %s", monsters[monst].m_name); + } + + return(buf); + } + else + switch(monst) + { + case D_ARROW: + strcpy(buf, "an arrow"); break; + case D_DART: + strcpy(buf, "a dart"); break; + case D_BOLT: + strcpy(buf, "a bolt"); break; + case D_POISON: + strcpy(buf, "poison"); break; + case D_POTION: + strcpy(buf, "a cursed potion"); break; + case D_PETRIFY: + strcpy(buf, "petrification"); break; + case D_SUFFOCATION: + strcpy(buf, "suffocation"); break; + case D_INFESTATION: + strcpy(buf, "a parasite"); break; + case D_DROWN: + strcpy(buf, "drowning"); break; + case D_FALL: + strcpy(buf, "falling"); break; + case D_FIRE: + strcpy(buf, "slow boiling in oil"); break; + case D_SPELLFUMBLE: + strcpy(buf, "a botched spell"); break; + case D_DRAINLIFE: + strcpy(buf, "a drain life spell"); break; + case D_ARTIFACT: + strcpy(buf, "an artifact of the gods"); break; + case D_GODWRATH: + strcpy(buf, "divine retribution"); break; + case D_CLUMSY: + strcpy(buf, "excessive clumsyness"); break; + default: + strcpy(buf, "stupidity"); break; + } + + return(buf); +} + +/* + showpack() + Display the contents of the hero's pack +*/ + +void +showpack(char *howso) +{ + char *iname; + unsigned int worth; + int cnt, ch, oldpurse; + struct linked_list *item; + struct object *obj; + struct linked_list *bag = NULL; + + cnt = 1; + clear(); + mvprintw(0, 0, "Contents of your pack %s:\n", howso); + ch = 0; + oldpurse = purse; + purse = 0; + + for (item = pack; item != NULL; item = next(item)) + { + obj = OBJPTR(item); + worth = get_worth(obj); + whatis(item); + purse += worth; + + if (obj->o_type == ARTIFACT && obj->o_which == TR_PURSE) + bag = obj->o_bag; + + iname = inv_name(obj, UPPERCASE); + mvprintw(cnt, 0, "%d) %s\n", ch, iname); + ch += 1; + + if (++cnt > LINES - 5 && next(item) != NULL) + { + cnt = 1; + mvaddstr(LINES - 1, 0, morestr); + refresh(); + wait_for(' '); + clear(); + } + } + + if (bag != NULL) + { + mvaddstr(LINES - 1, 0, morestr); + refresh(); + wait_for(' '); + clear(); + cnt = 1; + ch = 0; + + mvprintw(0, 0, "Contents of the Magic Purse of Yendor %s:\n", howso); + + for (item = bag; item != NULL; item = next(item)) + { + obj = OBJPTR(item); + worth = get_worth(obj); + whatis(item); + purse += worth; + mvprintw(cnt, 0, "%d) %s\n", ch, inv_name(obj, UPPERCASE)); + ch += 1; + + if (++cnt > LINES - 5 && next(item) != NULL) + { + cnt = 1; + mvaddstr(LINES - 1, 0, morestr); + refresh(); + wait_for(' '); + clear(); + } + } + } + + mvprintw(cnt + 1, 0, "Carrying %d gold pieces", oldpurse); + mvprintw(cnt + 2, 0, "Carrying objects worth %d gold pieces", purse); + purse += oldpurse; + refresh(); +} + +void +byebye(void) +{ + endwin(); + printf("\n"); + exit(0); +} + +/* + save_resurrect() + chance of resurrection according to modifed D&D probabilities +*/ + +int +save_resurrect(int bonus) +{ + int need, adjust; + + adjust = pstats.s_const + bonus - luck; + + if (adjust > 17) + return(TRUE); + else if (adjust < 14) + need = 5 * (adjust + 5); + else + need = 90 + 2 * (adjust - 13); + + return(roll(1, 100) < need); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/rogue.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/rogue.h Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1815 @@ +/* + rogue.h - A very large header file + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1984, 1991, 1997 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include + +#ifndef LINT +#include +#else +#include "lint-curses.h" +#endif + +#define SHOTPENALTY 2 /* In line of sight of missile */ +#define DOORPENALTY 1 /* Moving out of current room */ + +/* Maximum number of different things */ +#define MAXROOMS 9 /* max rooms per normal level */ +#define MAXDOORS 4 /* max doors to a room */ +#define MAXOBJ 6 /* max number of items to find on a level */ +#define MAXTREAS 30 /* max number monsters/treasure in treasure room */ +#define MAXTRAPS 80 /* max traps per level */ +#define MAXTRPTRY 16 /* max attempts/level allowed for setting traps */ +#define MAXPURCH 8 /* max purchases per trading post visit */ +#define NUMMONST (sizeof(monsters) / sizeof(struct monster) - 2) +#define NUMSUMMON 48 /* number of creatures that can summon hero */ +#define NLEVMONS 8 /* number of new monsters per level */ +#define LINELEN 512 /* characters in a buffer */ + +/* The character types */ +#define C_FIGHTER 0 +#define C_PALADIN 1 +#define C_RANGER 2 +#define C_CLERIC 3 +#define C_DRUID 4 +#define C_MAGICIAN 5 +#define C_ILLUSION 6 +#define C_THIEF 7 +#define C_ASSASIN 8 +#define C_NINJA 9 +#define C_MONSTER 10 +#define C_NOTSET 11 /* Must not be a value from above */ + +/* used for ring stuff */ +#define LEFT_1 0 +#define LEFT_2 1 +#define LEFT_3 2 +#define LEFT_4 3 +#define LEFT_5 4 +#define RIGHT_1 5 +#define RIGHT_2 6 +#define RIGHT_3 7 +#define RIGHT_4 8 +#define RIGHT_5 9 + +/* All the fun defines */ +#define next(ptr) ((ptr)?(ptr->l_next):NULL) +#define prev(ptr) ((ptr)?(ptr->l_prev):NULL) +#define identifier(ptr) (ptr->o_ident) +#define winat(y,x) ( (char) \ + ((mvwinch(mw,y,x) == ' ') ? mvwinch(stdscr,y,x) : winch(mw)) ) + +#define debug if (wizard && wiz_verbose) msg +#define verify(b) if (b) verify_function(__FILE__, __LINE__); +#define DISTANCE(c1, c2) ( ((c2).x - (c1).x)*((c2).x - (c1).x) + \ + ((c2).y - (c1).y)*((c2).y - (c1).y) ) + +#define xyDISTANCE(y1, x1, y2, x2) ((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)) +#define OBJPTR(what) ((*what).data.obj) +#define THINGPTR(what) ((*what).data.th) +#define ce(a, b) ((a).x == (b).x && (a).y == (b).y) +#define hero player.t_pos +#define pstats player.t_stats +#define max_stats player.maxstats +#define pack player.t_pack +#define attach(a, b) _attach(&a, b) +#define detach(a, b) _detach(&a, b) +#define free_list(a) _free_list(&a) +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#define GOLDCALC (rnd(50 + 30 * level) + 2) +#define o_charges o_ac +#define mi_wght mi_worth + +/* Things that appear on the screens */ +#define WALL ' ' +#define PASSAGE '#' +#define DOOR '+' +#define FLOOR '.' +#define VPLAYER '@' +#define IPLAYER '_' +#define POST '^' +#define LAIR '(' +#define RUSTTRAP ';' +#define TRAPDOOR '>' +#define ARROWTRAP '{' +#define SLEEPTRAP '$' +#define BEARTRAP '}' +#define TELTRAP '~' +#define DARTTRAP '`' +#define POOL '"' +#define MAZETRAP '\\' +#define FIRETRAP '<' +#define POISONTRAP '[' +#define ARTIFACT ',' +#define SECRETDOOR '&' +#define STAIRS '%' +#define GOLD '*' +#define POTION '!' +#define SCROLL '?' +#define MAGIC '$' +#define BMAGIC '>' /* Blessed magic */ +#define CMAGIC '<' /* Cursed magic */ +#define FOOD ':' +#define WEAPON ')' +#define ARMOR ']' +#define RING '=' +#define STICK '/' + +/* Various constants */ +#define HOLDTIME 2 +#define BEARTIME 3 +#define SLEEPTIME 4 +#define FREEZETIME 6 +#define STINKTIME 6 +#define CHILLTIME (roll(2, 4)) +#define STONETIME 8 +#define SICKTIME 10 +#define CLRDURATION 15 +#define HUHDURATION 20 +#define SMELLTIME 20 +#define HEROTIME 20 +#define HEALTIME 30 +#define WANDERTIME 140 +#define GONETIME 200 +#define PHASEDURATION 300 +#define SEEDURATION 850 + +#define STPOS 0 +#define BEFORE 1 +#define AFTER 2 + +#define MORETIME 150 +#define HUNGERTIME 1300 +#define STOMACHSIZE 2000 + +#define BOLT_LENGTH 10 +#define MARKLEN 20 + +#define LINEFEED 10 +#define CARRIAGE_RETURN 13 +#define ESCAPE 27 + +/* Adjustments for save against things */ +#define VS_POISON 0 +#define VS_PARALYZATION 0 +#define VS_DEATH 0 +#define VS_PETRIFICATION 1 +#define VS_WAND 2 +#define VS_BREATH 3 +#define VS_MAGIC 4 + +/*attributes for treasures in dungeon */ +#define ISNORMAL 0x00000000UL /* Neither blessed nor cursed */ +#define ISCURSED 0x00000001UL /* cursed */ +#define ISKNOW 0x00000002UL /* has been identified */ +#define ISPOST 0x00000004UL /* object is in a trading post */ +#define ISMETAL 0x00000008UL /* is metallic */ +#define ISPROT 0x00000010UL /* object is protected */ +#define ISBLESSED 0x00000020UL /* blessed */ +#define ISZAPPED 0x00000040UL /* weapon has been charged by dragon */ +#define ISVORPED 0x00000080UL /* vorpalized weapon */ +#define ISSILVER 0x00000100UL /* silver weapon */ +#define ISPOISON 0x00000200UL /* poisoned weapon */ +#define CANRETURN 0x00000400UL /* weapon returns if misses */ +#define ISOWNED 0x00000800UL /* weapon returns always */ +#define ISLOST 0x00001000UL /* weapon always disappears */ +#define ISMISL 0x00002000UL /* missile weapon */ +#define ISMANY 0x00004000UL /* show up in a group */ +#define CANBURN 0x00008000UL /* burns monsters */ +#define ISSHARP 0x00010000UL /* cutting edge */ +#define ISTWOH 0x00020000UL /* needs two hands to wield */ +#define ISLITTLE 0x00040000UL /* small weapon */ +#define ISLAUNCHER 0x00080000UL /* used to throw other weapons */ +#define TYP_MAGIC_MASK 0x0f000000UL +#define POT_MAGIC 0x01000000UL +#define SCR_MAGIC 0x02000000UL +#define ZAP_MAGIC 0x04000000UL +#define SP_WIZARD 0x10000000UL /* only wizards */ +#define SP_ILLUSION 0x20000000UL /* only illusionists */ +#define SP_CLERIC 0x40000000UL /* only clerics/paladins */ +#define SP_DRUID 0x80000000UL /* only druids/rangers */ +#define SP_MAGIC 0x30000000UL /* wizard or illusionist */ +#define SP_PRAYER 0xc0000000UL /* cleric or druid */ +#define SP_ALL 0xf0000000UL /* all special classes */ +#define _TWO_ ISBLESSED /* more powerful spell */ + +/* Various flag bits */ +#define ISDARK 0x00000001UL +#define ISGONE 0x00000002UL +#define ISTREAS 0x00000004UL +#define ISFOUND 0x00000008UL +#define ISTHIEFSET 0x00000010UL +#define WASDARK 0x00000020UL + +/* struct thing t_flags (might include player) for monster attributes */ +#define ISBLIND 0x00000001UL +#define ISINWALL 0x00000002UL +#define ISRUN 0x00000004UL +#define ISFLEE 0x00000008UL +#define ISINVIS 0x00000010UL +#define ISMEAN 0x00000020UL +#define ISGREED 0x00000040UL +#define CANSHOOT 0x00000080UL +#define ISHELD 0x00000100UL +#define ISHUH 0x00000200UL +#define ISREGEN 0x00000400UL +#define CANHUH 0x00000800UL +#define CANSEE 0x00001000UL +#define HASFIRE 0x00002000UL +#define ISSLOW 0x00004000UL +#define ISHASTE 0x00008000UL +#define ISCLEAR 0x00010000UL +#define CANINWALL 0x00020000UL +#define ISDISGUISE 0x00040000UL +#define CANBLINK 0x00080000UL +#define CANSNORE 0x00100000UL +#define HALFDAMAGE 0x00200000UL +#define CANSUCK 0x00400000UL +#define CANRUST 0x00800000UL +#define CANPOISON 0x01000000UL +#define CANDRAIN 0x02000000UL +#define ISUNIQUE 0x04000000UL +#define STEALGOLD 0x08000000UL +#define STEALMAGIC 0x10000001UL +#define CANDISEASE 0x10000002UL +#define HASDISEASE 0x10000004UL +#define CANSUFFOCATE 0x10000008UL +#define DIDSUFFOCATE 0x10000010UL +#define BOLTDIVIDE 0x10000020UL +#define BLOWDIVIDE 0x10000040UL +#define NOCOLD 0x10000080UL +#define TOUCHFEAR 0x10000100UL +#define BMAGICHIT 0x10000200UL +#define NOFIRE 0x10000400UL +#define NOBOLT 0x10000800UL +#define CARRYGOLD 0x10001000UL +#define CANITCH 0x10002000UL +#define HASITCH 0x10004000UL +#define DIDDRAIN 0x10008000UL +#define WASTURNED 0x10010000UL +#define CANSELL 0x10020000UL +#define CANBLIND 0x10040000UL +#define CANBBURN 0x10080000UL +#define ISCHARMED 0x10100000UL +#define CANSPEAK 0x10200000UL +#define CANFLY 0x10400000UL +#define ISFRIENDLY 0x10800000UL +#define CANHEAR 0x11000000UL +#define ISDEAF 0x12000000UL +#define CANSCENT 0x14000000UL +#define ISUNSMELL 0x18000000UL +#define WILLRUST 0x20000001UL +#define WILLROT 0x20000002UL +#define SUPEREAT 0x20000004UL +#define PERMBLIND 0x20000008UL +#define MAGICHIT 0x20000010UL +#define CANINFEST 0x20000020UL +#define HASINFEST 0x20000040UL +#define NOMOVE 0x20000080UL +#define CANSHRIEK 0x20000100UL +#define CANDRAW 0x20000200UL +#define CANSMELL 0x20000400UL +#define CANPARALYZE 0x20000800UL +#define CANROT 0x20001000UL +#define ISSCAVENGE 0x20002000UL +#define DOROT 0x20004000UL +#define CANSTINK 0x20008000UL +#define HASSTINK 0x20010000UL +#define ISSHADOW 0x20020000UL +#define CANCHILL 0x20040000UL +#define CANHUG 0x20080000UL +#define CANSURPRISE 0x20100000UL +#define CANFRIGHTEN 0x20200000UL +#define CANSUMMON 0x20400000UL +#define TOUCHSTONE 0x20800000UL +#define LOOKSTONE 0x21000000UL +#define CANHOLD 0x22000000UL +#define DIDHOLD 0x24000000UL +#define DOUBLEDRAIN 0x28000000UL +#define ISUNDEAD 0x30000001UL +#define BLESSMAP 0x30000002UL +#define BLESSGOLD 0x30000004UL +#define BLESSMONS 0x30000008UL +#define BLESSMAGIC 0x30000010UL +#define BLESSFOOD 0x30000020UL +#define CANBRANDOM 0x30000040UL /* Types of breath */ +#define CANBACID 0x30000080UL +#define CANBFIRE 0x30000100UL +#define CANBBOLT 0x30000200UL +#define CANBGAS 0x30000400UL +#define CANBICE 0x30000800UL +#define CANBPGAS 0x30001000UL /* Paralyze gas */ +#define CANBSGAS 0x30002000UL /* Sleeping gas */ +#define CANBSLGAS 0x30004000UL /* Slow gas */ +#define CANBFGAS 0x30008000UL /* Fear gas */ +#define CANBREATHE 0x3000ffc0UL /* Can it breathe at all? */ +#define STUMBLER 0x30010000UL +#define POWEREAT 0x30020000UL +#define ISELECTRIC 0x30040000UL +#define HASOXYGEN 0x30080000UL /* Doesn't need to breath air */ +#define POWERDEXT 0x30100000UL +#define POWERSTR 0x30200000UL +#define POWERWISDOM 0x30400000UL +#define POWERINTEL 0x30800000UL +#define POWERCONST 0x31000000UL +#define SUPERHERO 0x32000000UL +#define ISUNHERO 0x34000000UL +#define CANCAST 0x38000000UL +#define CANTRAMPLE 0x40000001UL +#define CANSWIM 0x40000002UL +#define LOOKSLOW 0x40000004UL +#define CANWIELD 0x40000008UL +#define CANDARKEN 0x40000010UL +#define ISFAST 0x40000020UL +#define CANBARGAIN 0x40000040UL +#define NOMETAL 0x40000080UL +#define CANSPORE 0x40000100UL +#define NOSHARP 0x40000200UL +#define DRAINWISDOM 0x40000400UL +#define DRAINBRAIN 0x40000800UL +#define ISLARGE 0x40001000UL +#define ISSMALL 0x40002000UL +#define CANSTAB 0x40004000UL +#define ISFLOCK 0x40008000UL +#define ISSWARM 0x40010000UL +#define CANSTICK 0x40020000UL +#define CANTANGLE 0x40040000UL +#define DRAINMAGIC 0x40080000UL +#define SHOOTNEEDLE 0x40100000UL +#define CANZAP 0x40200000UL +#define HASARMOR 0x40400000UL +#define CANTELEPORT 0x40800000UL +#define ISBERSERK 0x41000000UL +#define ISFAMILIAR 0x42000000UL +#define HASFAMILIAR 0x44000000UL +#define SUMMONING 0x48000000UL +#define CANREFLECT 0x50000001UL +#define LOWFRIENDLY 0x50000002UL +#define MEDFRIENDLY 0x50000004UL +#define HIGHFRIENDLY 0x50000008UL +#define MAGICATTRACT 0x50000010UL +#define ISGOD 0x50000020UL +#define CANLIGHT 0x50000040UL +#define HASSHIELD 0x50000080UL +#define HASMSHIELD 0x50000100UL +#define LOWCAST 0x50000200UL +#define MEDCAST 0x50000400UL +#define HIGHCAST 0x50000800UL +#define WASSUMMONED 0x50001000UL +#define HASSUMMONED 0x50002000UL +#define CANTRUESEE 0x50004000UL + + +#define FLAGSHIFT 28UL +#define FLAGINDEX 0x0000000fL +#define FLAGMASK 0x0fffffffL + +/* on - check if a monster flag is on */ +#define on(th, flag) \ + ((th).t_flags[(flag >> FLAGSHIFT) & FLAGINDEX] & (flag & FLAGMASK)) + +/* off - check if a monster flag is off */ +#define off(th, flag) \ + (!((th).t_flags[(flag >> FLAGSHIFT) & FLAGINDEX] & (flag & FLAGMASK))) + +/* turn_on - turn on a monster flag */ +#define turn_on(th, flag) \ + ( (th).t_flags[(flag >> FLAGSHIFT) & FLAGINDEX] |= (flag & FLAGMASK)) + +/* turn_off - turn off a monster flag */ +#define turn_off(th, flag) \ + ( (th).t_flags[(flag >> FLAGSHIFT) & FLAGINDEX] &= ~(flag & FLAGMASK)) + +#define SAME_POS(c1,c2) ( (c1.x == c2.x) && (c1.y == c2.y) ) + +/* types of things */ +/* All magic spells duplicate a potion, scroll, or stick effect */ + +#define TYP_POTION 0 +#define TYP_SCROLL 1 +#define TYP_RING 2 +#define TYP_STICK 3 +#define MAXMAGICTYPES 4 /* max number of items in magic class */ +#define MAXMAGICITEMS 50 /* max number of items in magic class */ +#define TYP_FOOD 4 +#define TYP_WEAPON 5 +#define TYP_ARMOR 6 +#define TYP_ARTIFACT 7 +#define NUMTHINGS (sizeof(things) / sizeof(struct magic_item)) + +/* Artifact types */ +#define TR_PURSE 0 +#define TR_PHIAL 1 +#define TR_AMULET 2 +#define TR_PALANTIR 3 +#define TR_CROWN 4 +#define TR_SCEPTRE 5 +#define TR_SILMARIL 6 +#define TR_WAND 7 +#define MAXARTIFACT (sizeof(arts) / sizeof(struct init_artifact)) + +/* Artifact flags */ +#define ISUSED 01 +#define ISACTIVE 02 + +/* Potion types - add also to magic_item.c and potions.c */ +#define P_CLEAR 0 +#define P_GAINABIL 1 +#define P_SEEINVIS 2 +#define P_HEALING 3 +#define P_MONSTDET 4 +#define P_TREASDET 5 +#define P_RAISELEVEL 6 +#define P_HASTE 7 +#define P_RESTORE 8 +#define P_PHASE 9 +#define P_INVIS 10 +#define P_SMELL 11 +#define P_HEAR 12 +#define P_SHERO 13 +#define P_DISGUISE 14 +#define P_FIRERESIST 15 +#define P_COLDRESIST 16 +#define P_HASOXYGEN 17 +#define P_LEVITATION 18 +#define P_REGENERATE 19 +#define P_SHIELD 20 +#define P_TRUESEE 21 +#define MAXPOTIONS 22 + +/* Scroll types - add also to magic_item.c and scrolls.c */ +#define S_CONFUSE 0 +#define S_MAP 1 +#define S_LIGHT 2 +#define S_HOLD 3 +#define S_SLEEP 4 +#define S_ENCHANT 5 +#define S_IDENTIFY 6 +#define S_SCARE 7 +#define S_GFIND 8 +#define S_SELFTELEP 9 +#define S_CREATE 10 +#define S_REMOVECURSE 11 +#define S_PETRIFY 12 +#define S_GENOCIDE 13 +#define S_CURING 14 +#define S_MAKEITEMEM 15 +#define S_PROTECT 16 +#define S_NOTHING 17 +#define S_SILVER 18 +#define S_OWNERSHIP 19 +#define S_FOODDET 20 +#define S_ELECTRIFY 21 +#define S_CHARM 22 +#define S_SUMMON 23 +#define S_REFLECT 24 +#define S_SUMFAMILIAR 25 +#define S_FEAR 26 +#define S_MSHIELD 27 +#define MAXSCROLLS 28 + +/* Rod/Wand/Staff types - add also to magic_item.c and sticks.c */ +#define WS_LIGHT 0 +#define WS_HIT 1 +#define WS_ELECT 2 +#define WS_FIRE 3 +#define WS_COLD 4 +#define WS_POLYMORPH 5 +#define WS_MISSILE 6 +#define WS_SLOW_M 7 +#define WS_DRAIN 8 +#define WS_CHARGE 9 +#define WS_MONSTELEP 10 +#define WS_CANCEL 11 +#define WS_CONFMON 12 +#define WS_DISINTEGRATE 13 +#define WS_ANTIMATTER 14 +#define WS_PARALYZE 15 +#define WS_XENOHEALING 16 +#define WS_NOTHING 17 +#define WS_INVIS 18 +#define WS_BLAST 19 +#define WS_WEB 20 +#define WS_KNOCK 21 +#define WS_CLOSE 22 +#define MAXSTICKS 23 + +/* Ring types */ +#define R_PROTECT 0 +#define R_ADDSTR 1 +#define R_SUSABILITY 2 +#define R_SEARCH 3 +#define R_SEEINVIS 4 +#define R_ALERT 5 +#define R_AGGR 6 +#define R_ADDHIT 7 +#define R_ADDDAM 8 +#define R_REGEN 9 +#define R_DIGEST 10 +#define R_TELEPORT 11 +#define R_STEALTH 12 +#define R_ADDINTEL 13 +#define R_ADDWISDOM 14 +#define R_HEALTH 15 +#define R_VREGEN 16 +#define R_LIGHT 17 +#define R_DELUSION 18 +#define R_CARRYING 19 +#define R_ADORNMENT 20 +#define R_LEVITATION 21 +#define R_FIRERESIST 22 +#define R_COLDRESIST 23 +#define R_ELECTRESIST 24 +#define R_RESURRECT 25 +#define R_BREATHE 26 +#define R_FREEDOM 27 +#define R_WIZARD 28 +#define R_PIETY 29 +#define R_TELCONTROL 30 +#define R_TRUESEE 31 +#define MAXRINGS 32 + +/* Weapon types */ +#define SLING 0 /* sling */ +#define ROCK 1 /* rocks */ +#define BULLET 2 /* sling bullet */ +#define BOW 3 /* short bow */ +#define ARROW 4 /* arrow */ +#define SILVERARROW 5 /* silver arrows */ +#define FLAMEARROW 6 /* flaming arrows */ +#define FOOTBOW 7 /* footbow */ +#define FBBOLT 8 /* footbow bolt */ +#define CROSSBOW 9 /* crossbow */ +#define BOLT 10 /* crossbow bolt */ + +#define DART 11 /* darts */ +#define DAGGER 12 /* dagger */ +#define HAMMER 13 /* hammer */ +#define LEUKU 14 /* leuku */ +#define JAVELIN 15 /* javelin */ +#define TOMAHAWK 16 /* tomahawk */ +#define MACHETE 17 /* machete */ +#define THROW_AXE 18 /* throwing axe */ +#define SHORT_SPEAR 19 /* spear */ +#define BOOMERANG 20 /* boomerangs */ +#define LONG_SPEAR 21 /* spear */ +#define SHURIKEN 22 /* shurikens */ +#define MOLOTOV 23 /* molotov cocktails */ +#define GRENADE 24 /* grenade for explosions */ +#define CLUB 25 /* club */ +#define PITCHFORK 26 /* pitchfork */ +#define SHORT_SWORD 27 /* short sword */ +#define HAND_AXE 28 /* hand axe */ +#define PARTISAN 29 /* partisan */ +#define GRAIN_FLAIL 30 /* grain flail */ +#define SINGLESTICK 31 /* singlestick */ +#define RAPIER 32 /* rapier */ +#define SICKLE 33 /* sickle */ +#define HATCHET 34 /* hatchet */ +#define SCIMITAR 35 /* scimitar */ +#define LIGHT_MACE 36 /* mace */ +#define MORNINGSTAR 37 /* morning star */ +#define BROAD_SWORD 38 /* broad sword */ +#define MINER_PICK 39 /* miner's pick */ +#define GUISARME 40 /* guisarme */ +#define WAR_FLAIL 41 /* war flail */ +#define CRYSKNIFE 42 /* crysknife */ +#define BATTLE_AXE 43 /* battle axe */ +#define CUTLASS 44 /* cutlass sword */ +#define GLAIVE 45 /* glaive */ +#define PERTUSKA 46 /* pertuska */ +#define LONG_SWORD 47 /* long sword */ +#define LANCE 48 /* lance */ +#define RANSEUR 49 /* ranseur */ +#define SABRE 50 /* sabre */ +#define SPETUM 51 /* spetum */ +#define HALBERD 52 /* halberd */ +#define TRIDENT 53 /* trident */ +#define WAR_PICK 54 /* war pick */ +#define BARDICHE 55 /* bardiche */ +#define HEAVY_MACE 56 /* mace */ +#define SCYTHE 57 /* great scythe */ +#define QUARTERSTAFF 58 /* quarter staff */ +#define BAST_SWORD 59 /* bastard sword */ +#define PIKE 60 /* pike */ +#define TWO_FLAIL 61 /* two-handed flail */ +#define TWO_MAUL 62 /* two-handed maul */ +#define TWO_PICK 63 /* two-handed pick */ +#define TWO_SWORD 64 /* two-handed sword */ +#define CLAYMORE 65 /* claymore sword */ +#define MAXWEAPONS (sizeof(weaps) / sizeof(struct init_weps)) +#define NONE 100 /* no weapon */ + +/* Armor types */ +#define SOFT_LEATHER 0 +#define CUIRBOLILLI 1 +#define HEAVY_LEATHER 2 +#define RING_MAIL 3 +#define STUDDED_LEATHER 4 +#define SCALE_MAIL 5 +#define PADDED_ARMOR 6 +#define CHAIN_MAIL 7 +#define BRIGANDINE 8 +#define SPLINT_MAIL 9 +#define BANDED_MAIL 10 +#define GOOD_CHAIN 11 +#define PLATE_MAIL 12 +#define PLATE_ARMOR 13 +#define MITHRIL 14 +#define CRYSTAL_ARMOR 15 +#define MAXARMORS (sizeof(armors) / sizeof(struct init_armor)) + +/* Food types */ +#define FD_RATION 0 +#define FD_FRUIT 1 +#define FD_CRAM 2 +#define FD_CAKES 3 +#define FD_LEMBA 4 +#define FD_MIRUVOR 5 +#define MAXFOODS (sizeof(fd_data) / sizeof(struct magic_item)) + +/* stuff to do with encumberance */ +#define F_OK 0 /* have plenty of food in stomach */ +#define F_HUNGRY 1 /* player is hungry */ +#define F_WEAK 2 /* weak from lack of food */ +#define F_FAINT 3 /* fainting from lack of food */ + +/* return values for get functions */ +#define NORM 0 /* normal exit */ +#define QUIT 1 /* quit option setting */ +#define MINUS 2 /* back up one option */ + +/* These are the types of inventory styles. */ +#define INV_SLOW 0 +#define INV_OVER 1 +#define INV_CLEAR 2 + +/* These will eventually become enumerations */ +#define MESSAGE TRUE +#define NOMESSAGE FALSE +#define POINTS TRUE +#define NOPOINTS FALSE +#define LOWERCASE TRUE +#define UPPERCASE FALSE +#define WANDER TRUE +#define NOWANDER FALSE +#define GRAB TRUE +#define NOGRAB FALSE +#define FAMILIAR TRUE +#define NOFAMILIAR FALSE +#define MAXSTATS TRUE +#define NOMAXSTATS FALSE +#define FORCE TRUE +#define NOFORCE FALSE +#define THROWN TRUE +#define NOTHROWN FALSE + +/* Ways to die */ +#define D_PETRIFY -1 +#define D_ARROW -2 +#define D_DART -3 +#define D_POISON -4 +#define D_BOLT -5 +#define D_SUFFOCATION -6 +#define D_POTION -7 +#define D_INFESTATION -8 +#define D_DROWN -9 +#define D_FALL -10 +#define D_FIRE -11 +#define D_SPELLFUMBLE -12 +#define D_DRAINLIFE -13 +#define D_ARTIFACT -14 +#define D_GODWRATH -15 +#define D_CLUMSY -16 + +/* values for games end */ +#define SCOREIT -1 +#define KILLED 0 +#define CHICKEN 1 +#define WINNER 2 +#define TOTAL 3 + +/* + * definitions for function step_ok: MONSTOK indicates it is OK to step on a + * monster -- it is only OK when stepping diagonally AROUND a monster + */ + +#define MONSTOK 1 +#define NOMONST 2 + + + +#define good_monster(m) (on(m, ISCHARMED) || \ + on(m, ISFRIENDLY) || \ + on(m, ISFAMILIAR)) + +/* Now we define the structures and types */ + +/* level types */ +typedef enum +{ + NORMLEV, /* normal level */ + POSTLEV, /* trading post level */ + MAZELEV, /* maze level */ + THRONE /* unique monster's throne room */ +} LEVTYPE; + +/* Help list */ + +struct h_list +{ + char h_ch; + char *h_desc; +}; + +/* Coordinate data type */ +typedef struct +{ + int x; + int y; +} coord; + +/* Linked list data type */ +typedef struct linked_list +{ + struct linked_list *l_next; + struct linked_list *l_prev; + + union + { + struct object *obj; + struct thing *th; + void *l_data; + } data; + +} linked_list; + +/* Stuff about magic items */ + +struct magic_item +{ + char *mi_name; + char *mi_abrev; + int mi_prob; + long mi_worth; + int mi_curse; + int mi_bless; +}; + +/* Room structure */ +struct room +{ + coord r_pos; /* Upper left corner */ + coord r_max; /* Size of room */ + coord r_exit[MAXDOORS]; /* Where the exits are */ + int r_flags; /* Info about the room */ + int r_nexits; /* Number of exits */ + short r_fires; /* Number of fires in room */ +}; + +/* Initial artifact stats */ +struct init_artifact +{ + char *ar_name; /* name of the artifact */ + int ar_level; /* first level where it appears */ + int ar_rings; /* number of ring effects */ + int ar_potions; /* number of potion effects */ + int ar_scrolls; /* number of scroll effects */ + int ar_wands; /* number of wand effects */ + int ar_worth; /* gold pieces */ + int ar_weight; /* weight of object */ +}; + +/* Array of all traps on this level */ + +struct trap +{ + coord tr_pos; /* Where trap is */ + long tr_flags; /* Info about trap (i.e. ISFOUND) */ + char tr_type; /* What kind of trap */ + char tr_show; /* What disguised trap looks like */ +}; + +/* Structure describing a fighting being */ + +struct stats +{ + char *s_dmg; /* String describing damage done */ + long s_exp; /* Experience */ + long s_hpt; /* Hit points */ + int s_pack; /* current weight of his pack */ + int s_carry; /* max weight he can carry */ + int s_lvl; /* Level of mastery */ + int s_arm; /* Armor class */ + int s_acmod; /* Armor clss modifier */ + int s_power; /* Spell points */ + int s_str; /* Strength */ + int s_intel; /* Intelligence */ + int s_wisdom; /* Wisdom */ + int s_dext; /* Dexterity */ + int s_const; /* Constitution */ + int s_charisma; /* Charisma */ +}; + +/* Structure describing a fighting being (monster at initialization) */ + +struct mstats +{ + short s_str; /* Strength */ + long s_exp; /* Experience */ + int s_lvl; /* Level of mastery */ + int s_arm; /* Armor class */ + char *s_hpt; /* Hit points */ + char *s_dmg; /* String describing damage done */ +}; + +/* Structure for monsters and player */ + +/* + NOTE: In initial v1.04 code the t_chasee variable will be + reset during the save/restore process. Eventually + need to implement a "thing" manager where I can + refer to "things" by number such that references + to them can be saved. Problems exist with regard + to removing references to "things" that have been + killed. +*/ + +typedef struct thing +{ + long chasee_index, horde_index; /* Used in save/rest process */ + struct linked_list *t_pack; /* What the thing is carrying */ + struct stats t_stats; /* Physical description */ + struct stats maxstats; /* maximum(or initial) stats */ + int t_ischasing; /* Are we chasing someone? */ + struct thing *t_chasee; /* Who are we chasing */ + struct object *t_horde; /* What are we hordeing */ + coord t_pos; /* Cuurent Position */ + coord t_oldpos; /* Last Position */ + coord t_nxtpos; /* Next Position */ + long t_flags[16]; /* State word */ + int t_praycnt; /* Times prayed... */ + int t_trans; /* # of transactions at post */ + int t_turn; /* If slowed, is it a turn to move */ + int t_wasshot; /* Was character shot last round? */ + int t_ctype; /* Character type */ + int t_index; /* Index into monster table */ + int t_no_move; /* How long the thing can't move */ + int t_rest_hpt; /* used in hit point regeneration */ + int t_rest_pow; /* used in spell point regeneration */ + int t_doorgoal; /* What door are we heading to? */ + char t_type; /* What it is */ + char t_disguise; /* What mimic looks like */ + char t_oldch; /* Character that was where it was */ +} thing; + +/* Array containing information on all the various types of monsters */ + +struct monster +{ + char *m_name; /* What to call the monster */ + short m_carry; /* Probability of carrying something */ + int m_normal; /* Does monster exist? */ + int m_wander; /* Does monster wander? */ + char m_appear; /* What does monster look like? */ + char *m_intel; /* Intelligence range */ + unsigned long m_flags[16]; /* Things about the monster */ + char *m_typesum; /* type of creature can he summon */ + short m_numsum; /* how many creatures can he summon */ + short m_add_exp; /* Added experience per hit point */ + struct mstats m_stats; /* Initial stats */ +}; + +/* Structure for a thing that the rogue can carry */ + +typedef struct object +{ + coord o_pos; /* Where it lives on the screen */ + struct linked_list *next_obj; /* The next obj. for stacked objects */ + struct linked_list *o_bag; /* bag linked list pointer */ + char *o_text; /* What it says if you read it */ + char *o_damage; /* Damage if used like sword */ + char *o_hurldmg; /* Damage if thrown */ + long o_flags; /* Information about objects */ + long ar_flags; /* general flags */ + char o_type; /* What kind of object it is */ + int o_ident; /* identifier for object */ + unsigned int o_count; /* Count for plural objects */ + int o_which; /* Which object of a type it is */ + int o_hplus; /* Plusses to hit */ + int o_dplus; /* Plusses to damage */ + int o_ac; /* Armor class */ + int o_group; /* Group number for this object */ + int o_weight; /* weight of this object */ + char o_launch; /* What you need to launch it */ + char o_mark[MARKLEN]; /* Mark the specific object */ + long o_worth; +} object; + +/* weapon structure */ + +struct init_weps +{ + char *w_name; /* name of weapon */ + char *w_dam; /* hit damage */ + char *w_hrl; /* hurl damage */ + char w_launch; /* need to launch it */ + int w_wght; /* weight of weapon */ + long w_worth; /* worth of this weapon */ + long w_flags; /* flags */ +}; + +/* armor structure */ + +struct init_armor +{ + char *a_name; /* name of armor */ + long a_worth; /* worth of armor */ + int a_prob; /* chance of getting armor */ + int a_class; /* normal armor class */ + int a_wght; /* weight of armor */ +}; + +struct matrix +{ + int base; /* Base to-hit value (AC 10) */ + int max_lvl;/* Maximum level for changing value */ + int factor; /* Amount base changes each time */ + int offset; /* What to offset level */ + int range; /* Range of levels for each offset */ +}; + +/* + ANSI C Prototype Additions +*/ + +/* armor.c */ +extern void wear(void); +extern void take_off(void); +extern void waste_time(void); +extern int wear_ok(struct thing *th, struct object *obj, int print_message); + +/* artifact.c */ +extern void apply(void); +extern int possessed(int artifact); +extern int is_carrying(int artifact); +extern int make_artifact(void); +extern struct object *new_artifact(int which, struct object *cur); +extern void do_minor(struct object *obj); +extern void do_major(void); +extern void do_phial(void); +extern void do_palantir(void); +extern void do_sceptre(void); +extern void do_silmaril(void); +extern void do_amulet(void); +extern void do_bag(struct object *obj); +extern void do_wand(void); +extern void do_crown(void); +extern void level_eval(void); + +/* bag.c */ + +typedef union +{ + void *varg; + int *iarg; + struct object *obj; +} bag_arg; + + +extern struct object *apply_to_bag(struct linked_list *bag_p, int type, + int (*bff_p)(struct object *obj, bag_arg *arg), + int (*baf_p)(struct object *obj, bag_arg *arg, int id), + void *user_arg); +extern int count_bag(linked_list *bag_p, int type, + int (*bff_p)(struct object *obj, bag_arg *arg)); +extern void del_bag(linked_list *bag_p, object *obj_p); +extern struct object *pop_bag(linked_list **bag_pp, object *obj_p); +extern void push_bag(linked_list **bag_pp, object *obj_p); +extern struct object *scan_bag(linked_list *bag_p, int type, int id); +extern struct object *select_bag(linked_list *bag_p, int type, + int (*bff_p)(struct object *obj, bag_arg *arg), int index); + +/* chase.c */ + +extern void do_chase(struct thing *th, int flee); +extern void chase_it(coord *runner, struct thing *th); +extern void runto(void);/* coord *runner, coord *spot); */ +extern int chase(struct thing *tp, coord *ee, int flee); +extern struct room *roomin(coord cp); +extern struct linked_list *find_mons(int y, int x); +extern struct linked_list *f_mons_a(int y, int x, int hit_bad); +extern int diag_ok(coord *sp, coord *ep, struct thing *flgptr); +extern int cansee(int y, int x); +extern coord *find_shoot(struct thing *tp, coord *dir); +extern coord *can_shoot(coord *er, coord *ee, coord *dir); +extern int straight_shot(int ery, int erx, int eey, int eex, coord *dir); +extern struct linked_list *get_hurl(struct thing *tp); +extern struct object *pick_weap(struct thing *tp); +extern int can_blink(struct thing *tp); + +/* command.c */ + +extern char fight_ch; +extern char countch; +extern void command(void); +extern void do_after_effects(void); +extern void make_omnipotent(void); +extern void quit_handler(int sig); +extern void quit(void); +extern void search(int is_thief); +extern void help(void); +extern void identify(void); +extern void d_level(void); +extern void u_level(void); +extern void call(int mark); +extern int att_bonus(void); + +/* daemon.c */ + +extern int demoncnt; + +struct delayed_action +{ + int d_type; + int d_when; + int d_id; + void *d_arg; + int d_time; +}; + +#define EMPTY 0 +#define DAEMON 1 +#define FUSE 2 + +typedef void fuse; +typedef void daemon; + +typedef union +{ + void *varg; + int *iarg; + struct linked_list *ll; +} fuse_arg; + +typedef union +{ + void *varg; + int *iarg; + struct thing *thingptr; +} daemon_arg; + +struct fuse +{ + int index; + fuse (*func)(fuse_arg *arg); +}; + +struct daemon +{ + int index; + daemon (*func)(daemon_arg *arg); +}; + +#define MAXDAEMONS 60 + +#define FUSE_NULL 0 +#define FUSE_SWANDER 1 +#define FUSE_UNCONFUSE 2 +#define FUSE_UNSCENT 3 +#define FUSE_SCENT 4 +#define FUSE_UNHEAR 5 +#define FUSE_HEAR 6 +#define FUSE_UNSEE 7 +#define FUSE_UNSTINK 8 +#define FUSE_UNCLRHEAD 9 +#define FUSE_UNPHASE 10 +#define FUSE_SIGHT 11 +#define FUSE_RES_STRENGTH 12 +#define FUSE_NOHASTE 13 +#define FUSE_NOSLOW 14 +#define FUSE_SUFFOCATE 15 +#define FUSE_CURE_DISEASE 16 +#define FUSE_UNITCH 17 +#define FUSE_APPEAR 18 +#define FUSE_UNELECTRIFY 19 +#define FUSE_UNBHERO 20 +#define FUSE_UNSHERO 21 +#define FUSE_UNXRAY 22 +#define FUSE_UNDISGUISE 23 +#define FUSE_SHERO 24 +#define FUSE_WGHTCHK 25 +#define FUSE_UNSUMMON 26 +#define FUSE_UNGAZE 27 +#define FUSE_UNCOLD 28 +#define FUSE_UNHOT 29 +#define FUSE_UNFLY 30 +#define FUSE_UNBREATHE 31 +#define FUSE_UNREGEN 32 +#define FUSE_UNSUPEREAT 33 +#define FUSE_UNSHIELD 34 +#define FUSE_UNMSHIELD 35 +#define FUSE_UNTRUESEE 36 +#define FUSE_MAX 37 + +#define DAEMON_NULL 0 +#define DAEMON_DOCTOR 1 +#define DAEMON_ROLLWAND 2 +#define DAEMON_STOMACH 3 +#define DAEMON_RUNNERS 4 +#define DAEMON_MAX 5 + +extern struct delayed_action d_list[MAXDAEMONS]; +extern struct daemon daemons[DAEMON_MAX]; +extern struct fuse fuses[FUSE_MAX]; + +extern struct delayed_action *d_slot(void); +extern struct delayed_action *find_slot(int id, int type); +extern void start_daemon(int id, void *arg, int whendo); +extern void kill_daemon(int id); +extern void do_daemons(int now); +extern void light_fuse(int id, void *arg, int time, int whendo); +extern void lengthen_fuse(int id, int xtime); +extern void extinguish_fuse(int id); +extern void do_fuses(int flag); +extern void activity(void); + +/* daemons.c */ + +extern void doctor_spell_points(struct thing *tp); +extern daemon runners(daemon_arg *arg); +extern daemon doctor(daemon_arg *tp); +extern daemon rollwand(daemon_arg *arg); +extern daemon stomach(daemon_arg *arg); +extern fuse swander(fuse_arg *arg); +extern fuse unconfuse(fuse_arg *arg); +extern fuse unscent(fuse_arg *arg); +extern fuse scent(fuse_arg *arg); +extern fuse unhear(fuse_arg *arg); +extern fuse hear(fuse_arg *arg); +extern fuse unsee(fuse_arg *arg); +extern fuse unstink(fuse_arg *arg); +extern fuse unclrhead(fuse_arg *arg); +extern fuse unphase(fuse_arg *arg); +extern fuse sight(fuse_arg *arg); +extern fuse res_strength(fuse_arg *arg); +extern fuse nohaste(fuse_arg *arg); +extern fuse noslow(fuse_arg *arg); +extern fuse suffocate(fuse_arg *arg); +extern fuse cure_disease(fuse_arg *arg); +extern fuse un_itch(fuse_arg *arg); +extern fuse appear(fuse_arg *arg); +extern fuse unelectrify(fuse_arg *arg); +extern fuse unshero(fuse_arg *arg); +extern fuse unbhero(fuse_arg *arg); +extern fuse undisguise(fuse_arg *arg); +extern fuse unsummon(fuse_arg *monst); +extern fuse ungaze(fuse_arg *arg); +extern fuse shero(fuse_arg *arg); +extern fuse uncold(fuse_arg *arg); +extern fuse unhot(fuse_arg *arg); +extern fuse unfly(fuse_arg *arg); +extern fuse unbreathe(fuse_arg *arg); +extern fuse unregen(fuse_arg *arg); +extern fuse unsupereat(fuse_arg *arg); +extern fuse unshield(fuse_arg *arg); +extern fuse unmshield(fuse_arg *arg); +extern fuse untruesee(fuse_arg *arg); +extern fuse wghtchk(fuse_arg *arg); + +/* encumb.h */ + +extern void updpack(void); +extern int packweight(void); +extern int itemweight(struct object *wh); +extern int playenc(void); +extern int totalenc(void); +extern int hitweight(void); + +/* fight.c */ + +extern void do_fight(coord dir, int tothedeath); +extern int fight(coord *mp, struct object *weap, int thrown); +extern int attack(struct thing *mp, struct object *weapon, int thrown); +extern int mon_mon_attack(struct thing *attacker, struct linked_list *mon, + struct object *weapon, int thrown); +extern int swing(int class, int at_lvl, int op_arm, int wplus); +extern void init_exp(void); +extern int next_exp_level(int print_message); +extern void check_level(void); +extern int roll_em(struct thing *att_er, struct thing *def_er, + struct object *weap, int thrown, struct object *cur_weapon); +extern const char *prname(char *who); +extern void hit(char *ee); +extern void miss(char *ee); +extern int save_throw(int which, struct thing *tp); +extern int save(int which); +extern int dext_plus(int dexterity); +extern int dext_prot(int dexterity); +extern int str_plus(int str); +extern int add_dam(int str); +extern int hung_dam(void); +extern void raise_level(void); +extern void thunk(struct object *weap, char *mname); +extern void m_thunk(struct object *weap, char *mname); +extern void bounce(struct object *weap, char *mname); +extern void m_bounce(struct object *weap, char *mname); +extern void remove_monster(coord *mp, struct linked_list *item); +extern int is_magic(struct object *obj); +extern void killed(struct thing *killer,struct linked_list *item, + int print_message, int give_points); +extern struct object *wield_weap(struct object *weapon, struct thing *mp); +extern void summon_help(struct thing *mons, int force); +extern int maxdamage(char *cp); + +/* getplay.c */ + +extern int geta_player(void); +extern void puta_player(void); +extern void do_getplayer(void); +extern void print_stored(void); +extern char *which_class(int c_class); + +/* ident.c */ + +extern char print_letters[]; +extern int get_ident(struct object *obj_p); +extern void free_ident(struct object *obj_p); +extern int unprint_id(char *print_id); +extern int max_print(void); + +/* init.c */ + +extern void init_materials(void); +extern void init_player(void); +extern void init_flags(void); +extern void init_things(void); +extern void init_fd(void); +extern void init_colors(void); +extern void init_names(void); +extern void init_stones(void); +extern void badcheck(char *name, struct magic_item *magic, int bound); + +/* io.c */ + +extern int get_string(char *buffer, WINDOW *win); +extern void msg(const char *fmt, ...); +extern void addmsg(const char *fmt, ...); +extern void endmsg(void); +extern void doadd(const char *fmt, va_list ap); +extern char readchar(void); +extern char readcharw(WINDOW *scr); +extern void status(int display); +extern void wait_for(int ch); +extern void show_win(WINDOW *scr, char *message); +extern void restscr(WINDOW *scr); +extern void add_line(const char *fmt, ...); +extern void end_line(void); +extern void hearmsg(const char *fmt, ...); +extern void seemsg(const char *fmt, ...); + +/* list.c */ + +extern void *ur_alloc(size_t size); +extern void ur_free(void *buf_p); +extern void _detach(struct linked_list **list, struct linked_list *item); +extern void _attach(struct linked_list **list, struct linked_list *item); +extern void _attach_after(linked_list **list_pp, linked_list *list_p, linked_list *new_p); +extern void _free_list(struct linked_list **ptr); +extern void discard(struct linked_list *item); +extern void throw_away(struct object *ptr); +extern struct linked_list *new_item(int size); +extern void *new_alloc(size_t size); +extern struct linked_list *new_list(void); + +/* magic.c */ + +/* for printing out messages */ + +#define CAST_NORMAL 0x000 /* cast normal version */ +#define CAST_CURSED 0x001 /* cast cursed version */ +#define CAST_BLESSED 0x002 /* cast blessed version */ +#define CAST_CROWN 0x010 /* crown helped out */ +#define CAST_SEPTRE 0x020 /* septre helped out */ + +#define MAX_SPELLS 100 /* Max # sorted_spells */ +#define MIN_FUMBLE_CHANCE 5 +#define MAX_FUMBLE_CHANCE 95 + +/* Spells that a monster can cast */ + +#define M_SELFTELEP 0 +#define M_HLNG2 1 +#define M_REGENERATE 2 +#define M_HLNG 3 +#define NUM_RUN 4 +#define M_HASTE 4 +#define M_SEEINVIS 5 +#define M_SHERO 6 +#define M_PHASE 7 +#define M_INVIS 8 +#define M_CANCEL 9 +#define M_OFFENSE 10 + + +struct spells +{ + int sp_level; /* level of casting spell */ + int sp_which; /* which scroll or potion */ + unsigned long sp_flags; /* scroll, blessed, known */ + int sp_cost; /* generated in incant() */ +}; + + +extern void incant(struct thing *caster, coord shoot_dir); +extern char *spell_name(struct spells *sp, char *buf); +extern char *spell_abrev(struct spells *sp, char *buf); +extern void fumble_spell(struct thing *caster, int num_fumbles); +extern void learn_new_spells(void); +extern struct spells *pick_monster_spell(struct thing *caster); +extern int sort_spells(const void *a, const void *b); + +/* magicitm.c */ + +extern struct magic_item things[]; /* Chances for each type of item*/ +extern struct magic_item s_magic[]; /* Names and chances for scrolls*/ +extern struct magic_item p_magic[]; /* Names and chances for potions*/ +extern struct magic_item r_magic[]; /* Names and chances for rings */ +extern struct magic_item ws_magic[]; /* Names and chances for sticks */ +extern struct magic_item fd_data[]; /* Names and chances for food */ +extern struct init_weps weaps[]; /* weapons and attributes */ +extern struct init_armor armors[]; /* armors and attributes */ +extern struct init_artifact arts[]; /* artifacts and attributes */ +extern int maxarmors; +extern int maxartifact; +extern int maxfoods; +extern int maxpotions; +extern int maxrings; +extern int maxscrolls; +extern int maxsticks; +extern int maxweapons; +extern int numthings; +extern char *s_names[]; /* Names of the scrolls */ +extern char *p_colors[]; /* Colors of the potions */ +extern char *r_stones[]; /* Stone settings of the rings */ +extern char *guess_items[MAXMAGICTYPES][MAXMAGICITEMS]; + /* Players guess at what magic is */ +extern int know_items[MAXMAGICTYPES][MAXMAGICITEMS]; + /* Does he know what a magic item does */ +extern char *ws_type[]; /* Is it a wand or a staff */ +extern char *ws_made[]; /* What sticks are made of */ + +/* main.c */ + +extern FILE *fd_score; +extern int summoned; +extern coord dta; +extern int main(int argc, char *argv[]); +extern void fatal(char *s); +extern int rnd(int range); +extern unsigned char ucrnd(unsigned char range); +extern short srnd(short range); +extern unsigned long ulrnd(unsigned long range); +extern int roll(int number, int sides); + +/* maze.c */ + +extern void do_maze(void); +extern void draw_maze(void); +extern char *moffset(int y, int x); +extern char *foffset(int y, int x); +extern int findcells(int y, int x); +extern void rmwall(int newy, int newx, int oldy, int oldx); +extern void crankout(void); + +/* memory.c */ + +extern void mem_debug(const int level); +extern void mem_tracking(int flag); +extern int mem_check(char *fname, int line); +extern void *mem_malloc(const size_t bytes); +extern void mem_free(const void *ptr); +extern void *mem_realloc(const void *ptr, const size_t new_size); +extern int mem_validate(const void *ptr); + +/* misc.c */ + +extern char *tr_name(char ch, char *buf); +extern void look(int wakeup); +extern char secretdoor(int y, int x); +extern struct linked_list *find_obj(int y, int x); +extern void eat(void); +extern void chg_str(int amt, int both, int lost); +extern void chg_dext(int amt, int both, int lost); +extern void add_haste(int blessed); +extern void aggravate(void); +extern char *vowelstr(char *str); +extern int is_current(struct object *obj); +extern int get_dir(void); +extern int is_wearing(int type); +extern int maze_view(int y, int x); +extern void listen(void); +extern void nothing_message(int flags); +extern void feel_message(void); +extern int const_bonus(void); +extern int int_wis_bonus(void); +extern void electrificate(void); +extern void feed_me(int hungry_state); +extern int get_monster_number(char *message); + +/* monsdata.c */ + +extern struct monster monsters[]; +extern int nummonst; + +/* monsters.c */ + +extern struct linked_list *summon_monster(int type, int familiar, int print_message); +extern int randmonster(int wander, int grab); +extern void new_monster(struct linked_list *item, int type, coord *cp, int max_monster); +extern void wanderer(void); +extern struct linked_list *wake_monster(int y, int x); +extern void genocide(int flags); +extern void id_monst(int monster); +extern void check_residue(struct thing *tp); +extern void sell(struct thing *tp); +extern void carried_weapon(struct thing *owner, struct object *weapon); + +/* move.c */ + +extern coord nh; +extern void do_run(char ch); +extern int step_ok(int y, int x, int can_on_monst, struct thing *flgptr); +extern void corr_move(int dy, int dx); +extern void do_move(int dy, int dx); +extern void light(coord *cp); +extern int blue_light(int flags); +extern char show(int y, int x); +extern char be_trapped(struct thing *th, coord tc); +extern void dip_it(void); +extern struct trap *trap_at(int y, int x); +extern void set_trap(struct thing *tp, int y, int x); +extern coord rndmove(struct thing *who); +extern int isatrap(int ch); + +/* newlvl.c */ + +extern void new_level(LEVTYPE ltype, int special); +extern void put_things(LEVTYPE ltype); +extern int throne_monster; +extern void do_throne(int special); +extern void create_lucifer(coord *stairs); + +/* options.c */ + +typedef union +{ + void *varg; + char *str; + int *iarg; +} opt_arg; + +struct optstruct +{ + char *o_name; /* option name */ + char *o_prompt; /* prompt for interactive entry */ + opt_arg o_opt; /* pointer to thing to set */ + void (*o_putfunc)(opt_arg *arg,WINDOW *win);/* func to print value */ + int (*o_getfunc)(opt_arg *arg,WINDOW *win);/* func to get value */ +}; + +typedef struct optstruct OPTION; + +extern void parse_opts(char *str); +extern void option(void); +extern void put_bool(opt_arg *iarg, WINDOW *win); +extern void put_str(opt_arg *str, WINDOW *win); +extern void put_abil(opt_arg *ability, WINDOW *win); +extern void put_inv(opt_arg *inv, WINDOW *win); +extern int get_bool(opt_arg *bp, WINDOW *win); +extern int get_str(opt_arg *opt, WINDOW *win); +extern int get_abil(opt_arg *abil, WINDOW *win); +extern int get_inv(opt_arg *inv, WINDOW *win); + +/* pack.c */ + +extern void swap_top(struct linked_list *top, struct linked_list *node); +extern void get_all(struct linked_list *top); +extern struct linked_list *get_stack(struct linked_list *item); +extern int add_pack(struct linked_list *item, int print_message); +extern void pack_report(object *obj, int print_message, char *message); +extern void inventory(struct linked_list *container, int type); +extern void pick_up(char ch); +extern struct object *get_object(struct linked_list *container, char *purpose, int type, int (*bff_p)(struct object *obj, bag_arg *junk) ); +extern struct linked_list *get_item(char *purpose, int type); +extern void del_pack(struct linked_list *what); +extern void discard_pack(struct object *obj_p); +extern void rem_pack(struct object *obj_p); +extern void cur_null(struct object *op); +extern void idenpack(void); +extern void show_floor(void); + +/* passages.c */ + +struct rdes +{ + int conn[MAXROOMS]; /* possible to connect to room i? */ + int isconn[MAXROOMS]; /* connection been made to room i? */ + int ingraph; /* this room in graph already? */ +}; + +extern void do_passages(void); +extern void conn(int r1, int r2); +extern void door(struct room *rm, coord *cp); + +/* player.c */ + +extern void prayer(void); +extern int gsense(void); +extern int is_stealth(struct thing *tp); +extern void steal(void); +extern void affect(void); +extern void undead_sense(void); + +/* potions.c */ + +extern void quaff(struct thing *quaffer, int which, int flag); +extern void lower_level(int who); +extern void res_dexterity(void); +extern void res_wisdom(void); +extern void res_intelligence(void); +extern void add_strength(int cursed); +extern void add_intelligence(int cursed); +extern void add_wisdom(int cursed); +extern void add_dexterity(int cursed); +extern void add_const(int cursed); +extern void monquaff(struct thing *quaffer, int which, int flag); + +/* properti.c */ + +extern int baf_print_item(struct object *obj_p, bag_arg *type, int id); +extern int baf_identify(struct object *obj_p, bag_arg *junk, int id); +extern int baf_curse(struct object *obj_p, bag_arg *junk, int id); +extern int baf_decrement_test(struct object *obj_p, bag_arg *count_p, int id); +extern int baf_increment(struct object *obj_p, bag_arg *count_p, int id); +extern int bafcweapon(struct object *obj_p, bag_arg *junk, int id); +extern int bafcarmor(struct object *obj_p, bag_arg *junk, int id); +extern int bff_group(struct object *obj_p, bag_arg *new_obj_p); +extern int bff_callable(struct object *obj_p, bag_arg *junk); +extern int bff_markable(struct object *obj_p, bag_arg *junk); +extern int bffron(object *obj_p, bag_arg *junk); +extern int bff_zappable(struct object *obj_p, bag_arg *junk); + +/* random.c */ + +extern void ur_srandom(unsigned x); +extern long ur_random(void); + +/* rings.c */ + +extern void ring_on(void); +extern void ring_off(void); +extern int ring_eat(int hand); +extern char *ring_num(struct object *obj, char *buf); +extern int ring_value(int type); + +/* rip.c */ + +extern void death(int monst); +extern void score(long amount, int lvl, int flags, int monst); +extern void total_winner(void); +extern char *killname(int monst, char *buf); +extern void showpack(char *howso); +extern void byebye(void); +extern int save_resurrect(int bonus); + +/* rogue.c */ + +extern const char *monstern; +extern const struct h_list helpstr[]; +extern const char *cnames[][15]; /* Character level names */ +extern char *spacemsg; +extern char *morestr; +extern char *retstr; + +/* state.c */ + +extern int mpos; /* Where cursor is on top line */ +extern struct trap traps[]; +extern struct room rooms[]; /* One for each room -- A level */ +extern struct thing player; /* The rogue */ +extern struct thing *beast; /* The last monster attacking */ +extern struct object *cur_armor; /* What a well dresssed rogue wears */ +extern struct object *cur_weapon; /* Which weapon he is wielding */ +extern struct object *cur_ring[]; /* What rings are being worn */ +extern struct linked_list *fam_ptr; /* A ptr to the familiar */ +extern struct linked_list *lvl_obj; /* List of objects on this level */ +extern struct linked_list *mlist; /* List of monsters on the level */ +extern struct linked_list *curr_mons; /* The mons. currently moving */ +extern struct linked_list *next_mons; /* The mons. after curr_mons */ + +extern int prscore; /* Print scores */ +extern int prversion; /* Print version info */ + +extern WINDOW *cw; /* Window that the player sees */ +extern WINDOW *hw; /* Used for the help command */ +extern WINDOW *mw; /* Used to store mosnters */ +extern LEVTYPE levtype; +extern coord delta; /* Change indicated to get_dir() */ + +extern char *release; /* Release number of rogue */ +extern char *lastfmt; +extern char *lastarg; +extern unsigned long total; /* Total dynamic memory bytes */ +extern long purse; /* How much gold the rogue has */ +extern int line_cnt; /* Counter for inventory style */ +extern int newpage; +extern int resurrect; /* resurrection counter */ +extern int foodlev; /* how fast he eats food */ +extern int see_dist; /* (how far he can see)^2 */ +extern int level; /* What level rogue is on */ +extern int ntraps; /* Number of traps on this level */ +extern int no_command; /* Number of turns asleep */ +extern int no_food; /* Number of levels without food */ +extern int count; /* Number of times to repeat command */ +extern int dnum; /* Dungeon number */ +extern int max_level; /* Deepest player has gone */ +extern int food_left; /* Amount of food in hero's stomach */ +extern int group; /* Current group number */ +extern int hungry_state; /* How hungry is he */ +extern int infest_dam; /* Damage from parasites */ +extern int lost_str; /* Amount of strength lost */ +extern int lost_dext; /* amount of dexterity lost */ +extern int hold_count; /* Number of monsters holding player */ +extern int trap_tries; /* Number of attempts to set traps */ +extern int has_artifact; /* set for possesion of artifacts */ +extern int picked_artifact; /* set for any artifacts picked up */ +extern int msg_index; /* pointer to current message buffer */ +extern int luck; /* how expensive things to buy thing */ +extern int fam_type; /* Type of familiar */ +extern int times_prayed; /* The number of time prayed */ +extern int mons_summoned; /* Number of summoned monsters */ +extern int char_type; /* what type of character is player */ +extern int pool_teleport; /* just teleported from a pool */ +extern int inwhgt; /* true if from wghtchk() */ +extern int running; /* True if player is running */ +extern int fighting; /* True if player is fighting */ +extern int playing; /* True until he quits */ +extern int wizard; /* True if allows wizard commands */ +extern int wiz_verbose; /* True if show debug messages */ +extern int after; /* True if we want after daemons */ +extern int fight_flush; /* True if toilet input */ +extern int terse; /* True if we should be short */ +extern int doorstop; /* Stop running when we pass a door */ +extern int jump; /* Show running as series of jumps */ +extern int door_stop; /* Current status of doorstop */ +extern int firstmove; /* First move after setting door_stop */ +extern int waswizard; /* Was a wizard sometime */ +extern int canwizard; /* Will be permitted to do this */ +extern int askme; /* Ask about unidentified things */ +extern int moving; /* move using 'm' command */ + +extern int inv_type; /* Inven style. Bool so options works */ +extern char take; /* Thing the rogue is taking */ +extern char PLAYER; /* what the player looks like */ +extern char prbuf[]; /* Buffer for sprintfs */ +extern char runch; /* Direction player is running */ +extern char whoami[]; /* Name of player */ +extern char fruit[]; /* Favorite fruit */ +extern char msgbuf[10][2 * LINELEN]; /* message buffer */ +extern char file_name[]; /* Save file name */ +extern char score_file[]; /* Score file name */ + +extern struct linked_list *arrow, *bolt, *rock, *silverarrow, *fbbolt; +extern struct linked_list *bullet, *firearrow, *dart, *dagger, *shuriken; +extern struct linked_list *oil, *grenade; +extern struct room *oldrp; /* Roomin(&oldpos) */ + +extern void ur_write_thing(FILE *savef, struct thing *t); +extern struct thing *ur_read_thing(FILE *savef); +extern void ur_write_object_stack(FILE *savef, struct linked_list *l); +extern void ur_write_bag(FILE *savef, struct linked_list *l); +extern struct linked_list *ur_read_bag(FILE *savef); +extern struct linked_list *ur_read_object_stack(FILE *savef); +extern int restore_file(FILE *savef); + +/* rooms.c */ + +extern void do_rooms(void); +extern void draw_room(struct room *rp); +extern void horiz(int cnt); +extern void vert(int cnt); +extern void rnd_pos(struct room *rp, coord *cp); +extern int rnd_room(void); + +/* save.c */ + +extern int restore(char *file); +extern int save_game(void); +extern void save_file(FILE *savefd); + +/* scrolls.c */ + +extern void read_scroll(struct thing *reader, int which, int flags); +extern struct linked_list *creat_mons(struct thing *person,int monster,int message); +extern int place_mons(int y, int x, coord *pos); +extern int is_r_on(struct object *obj); +extern void monread(struct thing *reader, int which, int flags); + +/* status.c */ + +extern int has_defensive_spell(struct thing th); + +/* sticks.c */ + +extern void fix_stick(struct object *cur); +extern void do_zap(struct thing *zapper, int which, unsigned long flags); +extern void drain(int ymin, int ymax, int xmin, int xmax); +extern char *charge_str(struct object *obj, char *buf); +extern void shoot_bolt(struct thing *shooter, coord start, coord dir, int get_points, int reason, char *name, int damage); +extern void monster_do_zap(struct thing *zapper, int which, int flags); +extern void cancel_player(int not_unique); + +/* things.c */ + +extern char *inv_name(struct object *obj, int lowercase); +extern void rem_obj(struct linked_list *item, int dis); +extern void add_obj(struct linked_list *item, int y, int x); +extern int drop(struct linked_list *item); +extern int dropcheck(struct object *op); +extern struct linked_list *new_thing(void); +extern struct linked_list *spec_item(char type, int which, int hit, int damage); +extern int pick_one(struct magic_item *magic, int nitems); +extern char *blesscurse(long flags); +extern int extras(void); +extern char *name_type(int type); +extern linked_list *make_item(object *obj_p); +extern int is_member(char *list_p, int member); + +/* trader.c */ + +extern void do_post(void); +extern void buy_it(char itemtype, int flags); +extern void sell_it(void); +extern void describe_it(struct object *obj); +extern int open_market(void); +extern int get_worth(struct object *obj); +extern void trans_line(void); + +/* verify.c */ + +extern void verify_function(const char *file, const int line); + +/* vers.c */ + +extern char *save_format; +extern char *version; +extern char *release; + +/* weapons.c */ + +extern coord do_motion(int ob, int ydelta, int xdelta, struct thing *tp); +extern void fall(struct thing *tp, struct linked_list *item, int pr, int +player_owned); +extern int hit_monster(int y, int x, struct object *weapon, struct thing *thrower); +extern int wield_ok(struct thing *wieldee, struct object *obj, int print_message); +extern int shoot_ok(int ch); +extern int fallpos(coord pos, coord *newpos); +extern void wield(void); +extern char *num(int n1, int n2, char *buf); +extern void init_weapon(struct object *weap, int type); +extern void missile(int ydelta, int xdelta, struct linked_list *item, struct thing *tp); + +/* wizard.c */ + +extern void whatis(struct linked_list *what); +extern void teleport(void); +extern int passwd(void); + +/* mdport.c */ +char *md_strdup(const char *s); +long md_random(void); +void md_srandom(long seed); +int md_readchar(WINDOW *win); + +#define NOOP(x) (x += 0) +#define CCHAR(x) ( (char) (x & A_CHARTEXT) ) \ No newline at end of file diff -r d9badb9c0179 -r c495a4f288c6 urogue/rooms.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/rooms.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,315 @@ +/* + rooms.c - Draw the nine rooms on the screen + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include "rogue.h" + +void +do_rooms(void) +{ + int i; + struct room *rp; + struct linked_list *item; + struct thing *tp; + int left_out; + coord top; + coord bsze; + coord mp; + + /* bsze is the maximum room size */ + + bsze.x = COLS / 3; + bsze.y = (LINES - 2) / 3; + + /* Clear things for a new level */ + + for (rp = rooms; rp < &rooms[MAXROOMS]; rp++) + rp->r_nexits = rp->r_flags = rp->r_fires = 0; + + /* Put the gone rooms, if any, on the level */ + + left_out = rnd(3); + + for (i = 0; i < left_out; i++) + rooms[rnd_room()].r_flags |= ISGONE; + + /* dig and populate all the rooms on the level */ + + for (i = 0, rp = rooms; i < MAXROOMS; rp++, i++) + { + int has_gold = FALSE; + + /* Find upper left corner of box that this room goes in */ + + top.x = (i % 3) * bsze.x; + top.y = i / 3 * bsze.y + 1; + + if (rp->r_flags & ISGONE) + { + /* + * Place a gone room. Make certain that there is a + * blank line for passage drawing. + */ + + do + { + rp->r_pos.x = top.x + rnd(bsze.x - 2) + 1; + rp->r_pos.y = top.y + rnd(bsze.y - 2) + 1; + rp->r_max.x = -COLS; + rp->r_max.x = -LINES; + } + while (rp->r_pos.y < 1 || rp->r_pos.y > LINES - 3); + + continue; + } + + if (rnd(40) < level - 5) + rp->r_flags |= ISDARK; + + /* Find a place and size for a random room */ + + do + { + rp->r_max.x = rnd(bsze.x - 4) + 4; + rp->r_max.y = rnd(bsze.y - 4) + 4; + rp->r_pos.x = top.x + rnd(bsze.x - rp->r_max.x); + rp->r_pos.y = top.y + rnd(bsze.y - rp->r_max.y); + } + while (rp->r_pos.y == 0); + + /* Draw the room */ + + draw_room(rp); + + /* Put the gold in */ + + if (rnd(10) < 3 && (!has_artifact || level >= max_level)) + { + struct linked_list *itm; + struct object *cur; + coord tpos; + + has_gold = TRUE; /* This room has gold in it */ + + itm = spec_item(GOLD, 0, 0, 0); + cur = OBJPTR(itm); + + /* Put it somewhere */ + + rnd_pos(rp, &tpos); + cur->o_pos = tpos; + + /* Put the gold into the level list of items */ + + add_obj(itm, tpos.y, tpos.x); + + if (roomin(tpos) != rp) + { + endwin(); + abort(); + } + } + + /* Put the monster in */ + + if (rnd(100) < (has_gold ? 80 : 40)) + { + int which; + int n, cnt; + + item = new_item(sizeof *tp); + tp = THINGPTR(item); + + do + rnd_pos(rp, &mp); + while (mvwinch(stdscr, mp.y, mp.x) != FLOOR); + + which = randmonster(NOWANDER, NOGRAB); + new_monster(item, which, &mp, NOMAXSTATS); + + /* See if we want to give it a treasure to carry around. */ + + if (rnd(100) < monsters[tp->t_index].m_carry) + attach(tp->t_pack, new_thing()); + + /* If it has a fire, mark it */ + + if (on(*tp, HASFIRE)) + { + rp->r_flags |= HASFIRE; + rp->r_fires++; + } + + /* If it carries gold, give it some */ + + if (on(*tp, CARRYGOLD)) + { + struct object *cur; + + item = spec_item(GOLD, 0, 0, 0); + cur = OBJPTR(item); + cur->o_count = GOLDCALC + GOLDCALC + GOLDCALC; + cur->o_pos = tp->t_pos; + attach(tp->t_pack, item); + } + + n = rnd(7); + + if (on(*tp, ISSWARM) && n < 5) + cnt = roll(2, 4); + else if (on(*tp, ISFLOCK) && n < 5) + cnt = roll(1, 4); + else + cnt = 0; + + for (n = 1; n <= cnt; n++) + { + coord pos; + + if (place_mons(mp.y, mp.x, &pos)!= FALSE) + { + struct linked_list *nitem; + + nitem = new_item(sizeof(struct thing)); + new_monster(nitem, which, &pos, NOMAXSTATS); + + /* If the monster is on a trap, trap it */ + + if (isatrap(mvinch(pos.y, pos.x))) + be_trapped(THINGPTR(nitem), mp); + + if (on(*tp, ISFRIENDLY)) + turn_on(*(THINGPTR(nitem)), ISFRIENDLY); + else + turn_off(*(THINGPTR(nitem)), ISFRIENDLY); + } + } + + if (cnt > 0) + { + int boost = rnd(3) + 1; + + if (on(*tp, LOWCAST) || on(*tp, MEDCAST) || on(*tp, HIGHCAST)) + turn_on(*tp, CANCAST); + + tp->t_stats.s_hpt += 3 * boost; + tp->t_stats.s_arm -= 2 * boost; + tp->t_stats.s_lvl += 2 * boost; + tp->t_stats.s_str += 2 * boost; + tp->t_stats.s_intel += 2 * boost; + tp->t_stats.s_exp += 4 * boost * monsters[which].m_add_exp; + } + } + } +} + +/* + draw_room() + Draw a box around a room +*/ + +void +draw_room(struct room *rp) +{ + int j, k; + + move(rp->r_pos.y, rp->r_pos.x + 1); + vert(rp->r_max.y - 2); /* Draw left side */ + move(rp->r_pos.y + rp->r_max.y - 1, rp->r_pos.x); + horiz(rp->r_max.x); /* Draw bottom */ + move(rp->r_pos.y, rp->r_pos.x); + horiz(rp->r_max.x); /* Draw top */ + vert(rp->r_max.y - 2); /* Draw right side */ + + /* Put the floor down */ + + for (j = 1; j < rp->r_max.y - 1; j++) + { + move(rp->r_pos.y + j, rp->r_pos.x + 1); + + for (k = 1; k < rp->r_max.x - 1; k++) + addch(FLOOR); + } +} + +/* + horiz() + draw a horizontal line +*/ + +void +horiz(int cnt) +{ + while(cnt--) + addch('-'); +} + +/* + vert() + draw a vertical line +*/ + +void +vert(int cnt) +{ + int x, y; + + getyx(stdscr, y, x); + + x--; + + while(cnt--) + { + move(++y, x); + addch('|'); + } +} + +/* + rnd_pos() + pick a random spot in a room +*/ + +void +rnd_pos(struct room *rp, coord *cp) +{ + cp->x = rp->r_pos.x + rnd(rp->r_max.x - 2) + 1; + cp->y = rp->r_pos.y + rnd(rp->r_max.y - 2) + 1; +} + +/* + rnd_room() + Pick a room that is really there +*/ + +int +rnd_room(void) +{ + int rm; + + if (levtype != NORMLEV) + rm = 0; + else + do + { + rm = rnd(MAXROOMS); + } + while (rooms[rm].r_flags & ISGONE); + + return(rm); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/save.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/save.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,135 @@ +/* + save.c - save and restore routines + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#define _ALL_SOURCE /* need to remove need for this AIXism */ + +#include +#include +#include +#include +#include +#include "rogue.h" + +int +save_game(void) +{ + FILE *savefd; + char buf[2 * LINELEN]; + char oldfile[2*LINELEN]; + + /* get file name */ + + strcpy(oldfile,file_name); + + do + { + mpos = 0; + + if (oldfile[0] != '\0') + msg("Save file [%s]: ", file_name); + else + msg("Save file as: "); + + mpos = 0; + buf[0] = '\0'; + + if (get_string(buf, cw) == QUIT) + { + msg(""); + return(FALSE); + } + + if ( (buf[0] == 0) && (oldfile[0] != 0) ) + strcpy(file_name, oldfile); + else if (buf[0] != 0) + strcpy(file_name, buf); + else + { + msg(""); + return(FALSE); + } + + wclear(hw); + wmove(hw, LINES - 1, 0); + wrefresh(hw); + + if ((savefd = fopen(file_name, "w")) == NULL) + msg(strerror(errno)); /* fake perror() */ + } + while (savefd == NULL); + + /* write out [compressed?] file */ + + save_file(savefd); + return(TRUE); +} + +int +restore(char *file) +{ + FILE *infd; + char *sp; + + if (strcmp(file, "-r") == 0) + file = file_name; + + if ((infd = fopen(file, "r")) == NULL) + { + perror(file); + return(FALSE); + } + + if ( restore_file(infd) == FALSE ) + return(FALSE); + + /* + * we do not close the file so that we will have a hold of the inode + * for as long as possible + */ + + if (remove(file) < 0) + { + printf("Cannot unlink file\n"); + return(FALSE); + } + + if ((sp = getenv("OPTIONS")) != NULL) + parse_opts(sp); + + strcpy(file_name, file); + + clearok(cw, TRUE); + touchwin(cw); + noecho(); + nonl(); + + while(playing) + { + do_daemons(BEFORE); + do_fuses(BEFORE); + + command(); /* Command execution */ + + if (after) + do_after_effects(); + } + + fatal(""); + + return(FALSE); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/scrolls.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/scrolls.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1491 @@ +/* + scrolls.c - Functions for dealing with scrolls + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +/* + read_scroll - read a scroll (or effect a scroll-like spell) + reader: who does it + which: which S_SCROLL (-1 means ask from pack) + flags: ISBLESSED, ISCURSED +*/ + +void +read_scroll(struct thing *reader, int which, int flags) +{ + struct object *obj; + struct linked_list *item, *nitem; + int i, j, charm_power; + char ch, nch; + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + int is_scroll = (which < 0 ? TRUE : FALSE); + char buf[2 * LINELEN]; + + if (reader != &player) + { + monread(reader, which, flags); + return; + } + + if (is_scroll) /* A regular scroll */ + { + if ((item = get_item("read", SCROLL)) == NULL) + return; + + obj = OBJPTR(item); + + if (obj->o_type != SCROLL) + { + msg("It says 'Made in Yugoslavia'!"); + return; + } + + if (on(player, ISBLIND)) + { + msg("You can't see to read anything."); + return; + } + + /* Calculate its effect */ + + cursed = obj->o_flags & ISCURSED; + blessed = obj->o_flags & ISBLESSED; + flags = obj->o_flags; + which = obj->o_which; + + /* remove it from the pack */ + + rem_pack(obj); + discard(item); + updpack(); + } + + switch (which) + { + case S_CONFUSE: /* Touch causes monster confusion. */ + if (cursed) + quaff(reader, P_CLEAR, ISCURSED); + else + { + msg("Your hands begin to glow red."); + turn_on(player, CANHUH); + /* if blessed... */ + } + break; + + case S_CURING: /* A cure disease spell */ + if (on(player, HASINFEST) || on(player, HASDISEASE)) + { + if (!cursed && on(player, HASDISEASE)) + { + extinguish_fuse(FUSE_CURE_DISEASE); + cure_disease(NULL); + } + + if (on(player, HASINFEST)) + { + msg("You begin to feel yourself improving again."); + turn_off(player, HASINFEST); + infest_dam = 0; + } + + if (is_scroll) + know_items[TYP_SCROLL][S_CURING] = TRUE; + } + else + nothing_message(flags); + break; + + case S_LIGHT: + if (blue_light(flags) && is_scroll) + know_items[TYP_SCROLL][S_LIGHT] = TRUE; + break; + + case S_HOLD: + if (cursed) + { + /* + * This scroll aggravates all the monsters on the + * current level and sets them running towards the + * hero + */ + aggravate(); + hearmsg("You hear a high pitched humming noise."); + } + else if (blessed) /* Hold all monsters on level */ + { + if (mlist == NULL) + nothing_message(flags); + else + { + struct linked_list *mon; + struct thing *th; + + for (mon = mlist; mon != NULL; mon = next(mon)) + { + th = THINGPTR(mon); + turn_off(*th, ISRUN); + turn_on(*th, ISHELD); + } + msg("A sudden peace comes over the dungeon."); + } + } + else + { + /* + * Hold monster scroll. Stop all monsters within two + * spaces from chasing after the hero. + */ + int x, y; + struct linked_list *mon; + int gotone = FALSE; + + for (x = hero.x - 2; x <= hero.x + 2; x++) + { + for (y = hero.y - 2; y <= hero.y + 2; y++) + { + if (y > 0 && x > 0 && isalpha(mvwinch(mw, y, x))) + { + if ((mon = find_mons(y, x)) != NULL) + { + struct thing *th; + + gotone = TRUE; + th = THINGPTR(mon); + turn_off(*th, ISRUN); + turn_on(*th, ISHELD); + } + } + } + } + + if (gotone) + msg("A sudden peace surrounds you."); + else + nothing_message(flags); + } + break; + + case S_SLEEP: + + /* if cursed, you fall asleep */ + + if (cursed) + { + if (is_wearing(R_ALERT)) + msg("You feel drowsy for a moment."); + else + { + msg("You fall asleep."); + no_command += 4 + rnd(SLEEPTIME); + } + } + else + { + /* + * sleep monster scroll. puts all monsters within 2 + * spaces asleep + */ + int x, y; + struct linked_list *mon; + int gotone = FALSE; + + for (x = hero.x - 2; x <= hero.x + 2; x++) + { + for (y = hero.y - 2; y <= hero.y + 2; y++) + { + if (y > 0 && x > 0 && isalpha(mvwinch(mw, y, x))) + { + if ((mon = find_mons(y, x)) != NULL) + { + struct thing *th; + th = THINGPTR(mon); + + if (on(*th, ISUNDEAD)) + continue; + + gotone = TRUE; + th->t_no_move += SLEEPTIME; + } + } + } + } + + if (gotone) + msg("The monster(s) around you seem to have fallen asleep."); + else + nothing_message(flags); + } + break; + + case S_CREATE: + { + /* + * Create a monster. First look in a circle around + * him, next try his room otherwise give up + */ + + struct thing *tp; + struct linked_list *ip; + + if (blessed) + summon_monster((short) 0, NOFAMILIAR, MESSAGE); + else if (cursed) + { + i = rnd(4) + 3; + for (j = 0; j < i; j++) + { + if ((ip = creat_mons(&player, (short) 0, MESSAGE)) != NULL) + { + tp = THINGPTR(ip); + turn_off(*tp, ISFRIENDLY); + } + } + } + else if ((ip = creat_mons(&player, (short) 0, MESSAGE)) != NULL) + { + tp = THINGPTR(ip); + turn_off(*tp, ISFRIENDLY); + } + } + break; + + case S_IDENTIFY: + if (cursed) + msg("You identify this scroll as an identify scroll"); + else if (blessed) /* identify everything in the pack */ + { + msg("You feel more Knowledgeable!"); + idenpack(); + } + else + { + /* Identify, let the rogue figure something out */ + + if (is_scroll && know_items[TYP_SCROLL][S_IDENTIFY] != TRUE) + { + msg("This scroll is an identify scroll."); + know_items[TYP_SCROLL][S_IDENTIFY] = TRUE; + } + whatis(NULL); + } + break; + + case S_MAP: + + /* Scroll of magic mapping. */ + + if (cursed) + { + msg("Your mind goes blank for a moment."); + wclear(cw); + light(&hero); + status(TRUE); + break; + } + + if (is_scroll && know_items[TYP_SCROLL][S_MAP] != TRUE) + { + msg("Oh! This scroll has a map on it!!"); + know_items[TYP_SCROLL][S_MAP] = TRUE; + } + + if (blessed) + turn_on(player, BLESSMAP); + + overwrite(stdscr, hw); + + /* Take all the things we want to keep hidden out of the window */ + + for (i = 0; i < LINES; i++) + for (j = 0; j < COLS; j++) + { + switch (nch = ch = CCHAR(mvwinch(hw, i, j))) + { + case SECRETDOOR: + nch = DOOR; + mvaddch(i, j, nch); + break; + + case '-': + case '|': + case DOOR: + case PASSAGE: + case ' ': + case STAIRS: + if (mvwinch(mw, i, j) != ' ') + { + struct thing *it; + struct linked_list *lit; + + lit = find_mons(i, j); + + if (lit) { + it = THINGPTR(lit); + + if (it && it->t_oldch == ' ') + it->t_oldch = nch; + } + } + break; + + default: + if (!blessed || !isatrap(ch)) + nch = ' '; + else + { + struct trap *tp; + struct room *rp; + + tp = trap_at(i, j); + rp = roomin(hero); + + if (tp->tr_type == FIRETRAP && rp != NULL) + { + rp->r_flags &= ~ISDARK; + light(&hero); + } + + tp->tr_flags |= ISFOUND; + } + } + if (nch != ch) + waddch(hw, nch); + } + + /* Copy in what he has discovered */ + overlay(cw, hw); + + /* And set up for display */ + overwrite(hw, cw); + + break; + + case S_GFIND: + /* Scroll of gold detection */ + + if (cursed) + { + int n = roll(3, 6); + int k; + struct room *rp; + coord pos; + + msg("You begin to feel greedy and you sense gold."); + wclear(hw); + + for (k = 1; k < n; k++) + { + rp = &rooms[rnd_room()]; + rnd_pos(rp, &pos); + mvwaddch(hw, pos.y, pos.x, GOLD); + } + overlay(hw, cw); + + break; + } + + if (blessed) + turn_on(player, BLESSGOLD); + + if (gsense() && is_scroll) + know_items[TYP_SCROLL][S_GFIND] = TRUE; + + break; + + case S_SELFTELEP: + + /* Scroll of teleportation: Make him disappear and reappear */ + + if (cursed) + { + level += 5 + rnd(5); + new_level(NORMLEV,0); + mpos = 0; + msg("You are banished to the lower regions."); + } + else if (blessed) + { + level -= rnd(3) + 1; + + if (level < 1) + level = 1; + + mpos = 0; + new_level(NORMLEV,0); /* change levels */ + status(TRUE); + msg("You are whisked away to another region."); + } + else + { + teleport(); + + if (is_scroll) + know_items[TYP_SCROLL][S_SELFTELEP] = TRUE; + } + + if (off(player, ISCLEAR)) + { + if (on(player, ISHUH)) + lengthen_fuse(FUSE_UNCONFUSE, rnd(4) + 4); + else + { + light_fuse(FUSE_UNCONFUSE, 0, rnd(4) + 4, AFTER); + turn_on(player, ISHUH); + } + } + else + msg("You feel dizzy for a moment, but it quickly passes."); + + break; + + case S_SCARE: + + /* + * A blessed scroll of scare monster automatically transports + * itself to the hero's feet + * + */ + + if (blessed) + { + ch = CCHAR( mvwinch(stdscr, hero.y, hero.x) ); + + if (ch != FLOOR && ch != PASSAGE) + { + msg("Your feet tickle for a moment"); + return; + } + + item = spec_item(SCROLL, S_SCARE, 0, 0); + + obj = OBJPTR(item); + obj->o_flags = ISCURSED; + obj->o_pos = hero; + add_obj(item, hero.y, hero.x); + msg("A wave of terror sweeps throughout the room"); + } + else + { + /* + * A monster will refuse to step on a scare monster + * scroll if it is dropped. Thus reading it is a + * mistake and produces laughter at the poor rogue's + * boo boo. + */ + + msg("You hear maniacal laughter in the distance."); + + if (cursed) /* If cursed, monsters get mad */ + aggravate(); + } + break; + + case S_REMOVECURSE: + if (cursed) /* curse a player's possession */ + { + for (nitem = pack; nitem != NULL; nitem = next(nitem)) + { + obj = OBJPTR(nitem); + + if (rnd(5) == 0) + if (obj->o_flags & ISBLESSED) + obj->o_flags &= ~ISBLESSED; + else + obj->o_flags |= ISCURSED; + } + msg("The smell of fire and brimstone comes from your pack."); + } + else if (blessed) + { + for (nitem = pack; nitem != NULL; nitem = next(nitem)) + (OBJPTR(nitem))->o_flags &= ~ISCURSED; + + msg("Your pack glistens brightly."); + } + else + { + if ((nitem = get_item("remove the curse on", 0)) != NULL) + { + obj = OBJPTR(nitem); + msg("Removed the curse from %s.", inv_name(obj, LOWERCASE)); + obj->o_flags &= ~ISCURSED; + + if (is_scroll) + know_items[TYP_SCROLL][S_REMOVECURSE] = TRUE; + } + } + break; + + case S_PETRIFY: + switch(CCHAR(mvinch(hero.y, hero.x))) + { + case TRAPDOOR: + case DARTTRAP: + case TELTRAP: + case ARROWTRAP: + case SLEEPTRAP: + case BEARTRAP: + case FIRETRAP: + { + int n; + + /* Find the right trap */ + for (n = 0; n < ntraps && !ce(traps[n].tr_pos, hero); n++) + ; + + ntraps--; + + if (!ce(traps[n].tr_pos, hero)) + msg("What a strange trap!"); + else + { + while (n < ntraps) + { + traps[n] = traps[n + 1]; + n++; + } + } + + msg("The dungeon begins to rumble and shake!"); + addch(WALL); + + if (on(player, CANINWALL)) + { + extinguish_fuse(FUSE_UNPHASE); + turn_off(player, CANINWALL); + msg("Your dizzy feeling leaves you."); + } + + turn_on(player, ISINWALL); + } + break; + + case DOOR: + case SECRETDOOR: + { + struct room *rp = roomin(hero); + short n; + + /* Find the right door */ + + for (n=0; nr_nexits && !ce(rp->r_exit[n], hero); n++) + /* do nothing */ ; + + rp->r_nexits--; + + if (!ce(rp->r_exit[n], hero)) + msg("What a strange door!"); + else + { + while (n < rp->r_nexits) + { + rp->r_exit[n] = rp->r_exit[n + 1]; + n++; + } + } + /* No break - fall through */ + } + case FLOOR: + case PASSAGE: + msg("The dungeon begins to rumble and shake!"); + addch(WALL); + + if (on(player, CANINWALL)) + { + extinguish_fuse(FUSE_UNPHASE); + turn_off(player, CANINWALL); + msg("Your dizzy feeling leaves you."); + } + + turn_on(player, ISINWALL); + break; + + default: + nothing_message(flags); + break; + } + break; + + case S_GENOCIDE: + msg("You have been granted the boon of genocide!--More--"); + + wait_for(' '); + msg(""); + + genocide(flags); + + if (is_scroll) + know_items[TYP_SCROLL][S_GENOCIDE] = TRUE; + + break; + + case S_PROTECT: + if (is_scroll && know_items[TYP_SCROLL][S_PROTECT] == FALSE) + { + msg("You can protect something from rusting or theft."); + know_items[TYP_SCROLL][S_PROTECT] = TRUE; + } + + if ((item = get_item("protect", 0)) != NULL) + { + struct object *lb = OBJPTR(item); + + if (cursed) + { + lb->o_flags &= ~ISPROT; + mpos = 0; + msg("Unprotected %s.", inv_name(lb, LOWERCASE)); + } + else + { + lb->o_flags |= ISPROT; + mpos = 0; + msg("Protected %s.", inv_name(lb, LOWERCASE)); + } + } + break; + + case S_MAKEITEMEM: + if (!is_scroll || rnd(luck)) + feel_message(); + else + { + char itemtype; + + if (is_scroll) + know_items[TYP_SCROLL][S_MAKEITEMEM] = TRUE; + + msg("You have been endowed with the power of creation."); + + if (blessed) + itemtype = '\0'; + else + switch (rnd(6)) + { + case 0: itemtype = RING; break; + case 1: itemtype = POTION; break; + case 2: itemtype = SCROLL; break; + case 3: itemtype = ARMOR; break; + case 4: itemtype = WEAPON; break; + case 5: itemtype = STICK; break; + } + + flags |= SCR_MAGIC; + buy_it(itemtype, flags); + } + break; + + case S_ENCHANT: + { + struct linked_list *ll; + struct object *lb; + int howmuch, flg=0; + + if (is_scroll && know_items[TYP_SCROLL][S_ENCHANT] == FALSE) + { + msg("You are granted the power of enchantment."); + msg("You may enchant anything(weapon,ring,armor,scroll,potion)"); + know_items[TYP_SCROLL][S_ENCHANT] = TRUE; + } + + if ((ll = get_item("enchant", 0)) != NULL) + { + lb = OBJPTR(ll); + lb->o_flags &= ~ISCURSED; + + if (blessed) + howmuch = 2; + else if (cursed) + howmuch = -1; + else + { + howmuch = 1; + flg |= ISBLESSED; + } + + switch (lb->o_type) + { + case RING: + lb->o_ac += howmuch; + + if (lb->o_ac > 5 && rnd(5) == 0) + { + msg("Your ring explodes in a cloud of smoke."); + lb->o_flags &= ~ISCURSED; + dropcheck(lb); + + switch (lb->o_which) + { + case R_ADDSTR: + chg_str(-2, TRUE, FALSE); + break; + case R_ADDHIT: + chg_dext(-2, TRUE, FALSE); + break; + case R_ADDINTEL: + pstats.s_intel -= 2; + max_stats.s_intel -= 2; + break; + case R_ADDWISDOM: + pstats.s_wisdom -= 2; + max_stats.s_wisdom -= 2; + break; + } + + del_pack(ll); + lb = NULL; + } + else if (is_r_on(lb)) + switch (lb->o_which) + { + case R_ADDSTR: + pstats.s_str += howmuch; + break; + case R_ADDHIT: + pstats.s_dext += howmuch; + break; + case R_ADDINTEL: + pstats.s_intel += howmuch; + break; + case R_ADDWISDOM: + pstats.s_wisdom += howmuch; + break; + case R_CARRYING: + updpack(); + break; + } + + break; + + case ARMOR: + lb->o_ac -= howmuch; + + if (armors[lb->o_which].a_class - lb->o_ac > 5 && rnd(5) == 0) + { + msg("Your %s explodes in a cloud of dust.", + inv_name(lb, LOWERCASE)); + + lb->o_flags &= ~ISCURSED; + + if (lb == cur_armor) + pstats.s_hpt /= 2; + + dropcheck(lb); + del_pack(ll); + lb = NULL; + } + break; + + case STICK: + if (wizard || howmuch != 1 && rnd(5) == 0) + lb->o_flags |= flg; + + lb->o_charges += howmuch + 10; + + if (lb->o_charges < 0) + lb->o_charges = 0; + + if (lb->o_charges > 50 && rnd(5) == 0) + { + msg("Your %s explodes into splinters.", + inv_name(lb, LOWERCASE)); + + lb->o_flags &= ~ISCURSED; + dropcheck(lb); + del_pack(ll); + lb = NULL; + } + break; + + case WEAPON: + if (rnd(100) < 50) + lb->o_hplus += howmuch; + else + lb->o_dplus += howmuch; + + if (lb->o_hplus + lb->o_dplus > 10 && rnd(5) == 0) + { + msg("Your %s explodes in a cloud of shrapnel", + inv_name(lb, LOWERCASE)); + + lb->o_flags &= ~ISCURSED; + + if (lb == cur_weapon) + chg_dext(-2, FALSE, TRUE); + + dropcheck(lb); + del_pack(ll); + lb = NULL; + + } + break; + + case POTION: + case SCROLL: + default: + lb->o_flags |= flg; + break; + } + + mpos = 0; + + if (lb != NULL) + msg("Enchanted %s.", inv_name(lb, LOWERCASE)); + } + } + break; + + case S_NOTHING: + nothing_message(flags); + break; + + case S_SILVER: + { + struct object *lb; + + if (is_scroll && know_items[TYP_SCROLL][S_SILVER] == FALSE) + { + msg("You are granted the power of magic hitting."); + know_items[TYP_SCROLL][S_SILVER] = TRUE; + } + + if ((item = get_item("annoint", WEAPON)) != NULL) + { + lb = OBJPTR(item); + + if (blessed && !(lb->o_flags & ISSILVER)) + { + lb->o_hplus += rnd(3) + 1; + lb->o_flags |= ISSILVER; + lb->o_flags |= ISMETAL; + msg("Your weapon has turned to silver!"); + } + else if (cursed && (lb->o_flags & ISSILVER)) + { + lb->o_hplus -= (rnd(3) + 1); + lb->o_flags &= ~ISSILVER; + msg("Your silver weapon has turned to steel."); + } + else if (lb->o_flags & ISSILVER) + { + msg("Your silver weapon glitters briefly."); + lb->o_hplus += rnd(2); + } + else + { + lb->o_hplus += rnd(3); + lb->o_flags |= ISSILVER; + lb->o_flags |= ISMETAL; + msg("Your weapon has turned to silver."); + } + } + } + break; + case S_OWNERSHIP: + { + struct linked_list *ll; + struct object *lb; + + if (is_scroll && know_items[TYP_SCROLL][S_OWNERSHIP] == FALSE) + { + msg("You are granted the power of ownership."); + know_items[TYP_SCROLL][S_OWNERSHIP] = TRUE; + } + + if ((ll = get_item("claim", 0)) != NULL) + { + lb = OBJPTR(ll); + + if (cursed && lb->o_flags & (ISOWNED | CANRETURN)) + { + lb->o_flags &= ~(ISOWNED | CANRETURN); + msg("The gods seem to have forgotten you."); + } + else if (cursed && !(lb->o_flags & ISLOST)) + { + lb->o_flags |= ISLOST; + msg("The gods look the other way."); + } + else if (blessed && lb->o_flags & ISLOST) + { + lb->o_flags |= CANRETURN; + msg("The gods seem to have remembered you."); + } + else if (blessed && !(lb->o_flags & ISOWNED)) + { + lb->o_flags |= (ISOWNED | CANRETURN); + msg("The gods smile upon you."); + } + else if (blessed | cursed) + { + nothing_message(flags); + } + else + { + lb->o_flags |= CANRETURN; + msg("The gods look upon you."); + } + } + } + break; + + case S_FOODDET: + + /* Scroll of food detection */ + + if (cursed) + { + int n = roll(3, 6); + int k; + struct room *rp; + coord pos; + + msg("You begin to feel hungry and you smell food."); + wclear(hw); + + for (k = 1; k < n; k++) + { + rp = &rooms[rnd_room()]; + rnd_pos(rp, &pos); + mvwaddch(hw, pos.y, pos.x, FOOD); + } + + overlay(hw, cw); + + if (is_scroll) + know_items[TYP_SCROLL][S_FOODDET] = TRUE; + + break; + } + + if (blessed) + turn_on(player, BLESSFOOD); + + if (off(player, ISUNSMELL) && lvl_obj != NULL) + { + struct linked_list *itm; + struct object *cur; + struct thing *th; + int fcount = 0; + int same_room = FALSE; + struct room *rp = roomin(hero); + + wclear(hw); + + for (itm = lvl_obj; itm != NULL; itm = next(itm)) + { + cur = OBJPTR(itm); + + if (cur->o_type == FOOD) + { + fcount += cur->o_count; + mvwaddch(hw, cur->o_pos.y, cur->o_pos.x, FOOD); + + if (roomin(cur->o_pos) == rp) + same_room = TRUE; + } + } + + for (itm = mlist; itm != NULL; itm = next(itm)) + { + struct linked_list *pitem; + + th = THINGPTR(itm); + + for (pitem = th->t_pack; pitem != NULL; pitem = next(pitem)) + { + cur = OBJPTR(pitem); + + if (cur->o_type == FOOD) + { + fcount += cur->o_count; + mvwaddch(hw, th->t_pos.y, th->t_pos.x, FOOD); + + if (roomin(th->t_pos) == rp) + same_room = TRUE; + } + } + } + + if (fcount) + { + if (is_scroll) + know_items[TYP_SCROLL][S_FOODDET] = TRUE; + + if (same_room) + msg("FOOOOD!!"); + else + msg("You begin to feel hungry and you smell food."); + + overlay(hw, cw); + break; + } + } + + if (off(player, ISUNSMELL)) + msg("You can't smell anything."); + else + nothing_message(flags); + + break; + + case S_ELECTRIFY: + if (on(player, ISELECTRIC)) + { + msg("Your violet glow brightens for an instant."); + lengthen_fuse(FUSE_UNELECTRIFY, 4 + rnd(8)); + } + else + { + msg("Your body begins to glow violet and shoot sparks."); + turn_on(player, ISELECTRIC); + light_fuse(FUSE_UNELECTRIFY,0,(blessed?3:1)*WANDERTIME, AFTER); + light(&hero); + } + + if (is_scroll) + know_items[TYP_SCROLL][S_ELECTRIFY] = TRUE; + + break; + + case S_CHARM: + + /* + * Beauty, brains and experience make a person charming. + * Unique monsters can never be charmed. + */ + + charm_power = pstats.s_charisma / 2 + pstats.s_lvl / 3 + max(0, pstats.s_intel - 15); + + if (cursed) + { + msg("You hear harsh, dissonant twanging throughout the dungeon."); + aggravate(); + } + else if (blessed) /* Charm entire level */ + { + struct linked_list *mon; + + msg("You hear ringingly meliflous music all around you."); + + for (mon = mlist; mon != NULL; mon = next(mon)) + { + struct thing *th = THINGPTR(mon); + + if (th->t_stats.s_intel < charm_power && off(*th, ISUNIQUE)) + { + turn_on(*th, ISCHARMED); + chase_it(&th->t_pos, &player); + } + } + } + else /* Charm all monsters within two spaces of the hero */ + { + int x, y; + struct linked_list *mon; + + msg("You hear soft, lyrical music all around you."); + + for (x = hero.x - 2; x <= hero.x + 2; x++) + for (y = hero.y - 2; y <= hero.y + 2; y++) + if (y > 0 && x > 0 && isalpha(mvwinch(mw, y, x))) + { + if ((mon = find_mons(y, x)) != NULL) + { + struct thing *th; + + th = THINGPTR(mon); + + if (th->t_stats.s_intel < charm_power && off(*th, ISUNIQUE)) + { + turn_on(*th, ISCHARMED); + chase_it(&th->t_pos, &player); + } + } + } + } + break; + + case S_SUMMON: + { + struct linked_list *llp; + struct thing *tp; + int mon_type; + + if (on(player, HASSUMMONED)) + { + nothing_message(flags); + break; + } + + if (cursed) + { + creat_mons(&player, (short) 0, MESSAGE); + break; + } + + if (blessed) /* Summon a possibly very high monster */ + { + int nsides = max(2, max(pstats.s_lvl, 12) - luck); + + mon_type = roll(nsides, rnd(pstats.s_charisma + 15) + 8); + mon_type = min(mon_type, nummonst); + } + else + mon_type = 0; + + llp = summon_monster((short) mon_type, NOFAMILIAR, NOMESSAGE); + + if (llp) + { + tp = THINGPTR(llp); + turn_on(*tp, WASSUMMONED); + turn_on(player, HASSUMMONED); + msg("You have summoned a %s.", monsters[tp->t_index].m_name); + light_fuse(FUSE_UNSUMMON, llp, WANDERTIME + rnd(pstats.s_lvl), AFTER); + } + } + break; + + case S_REFLECT: + if (on(player, CANREFLECT)) + { + msg("The sparkling around you brightens momentarily."); + lengthen_fuse(FUSE_UNGAZE, 5 + rnd(10)); + } + else + { + msg("Shiny particles sparkle all around you."); + turn_on(player, CANREFLECT); + light_fuse(FUSE_UNGAZE, 0, (blessed ? 3 : 1) * WANDERTIME, AFTER); + } + break; + + case S_SUMFAMILIAR: + { + int type = 0; + + if (on(player, HASFAMILIAR)) + { + msg("But you already have a familiar - somewhere..."); + return; + } + + if (wizard) + type = get_monster_number("be your familiar"); + else if (blessed) /* Summon a possibly very high monster */ + { + int nsides = max(2, max(pstats.s_lvl, 12) - luck); + + type = roll(nsides, rnd(pstats.s_charisma + 15) + 8); + type = min(type, nummonst); + } + else if (cursed) /* Summon a bat, maggot, eye, etc */ + { + type = rnd(20) + 1; + + if (summon_monster(type, FAMILIAR, MESSAGE)) + turn_on(player, HASFAMILIAR); + } + } + break; + + case S_FEAR: + + /* if cursed, you get frightened */ + + if (cursed) + { + if (off(player, SUPERHERO) && (player.t_ctype != C_PALADIN) && !save(VS_DEATH)) + msg("A momentary wave of panic sweeps over you."); + else + { + msg("Panicstricken, you fall into a coma."); + no_command += roll(2, SLEEPTIME); + } + } + else + { + /* + * terrify monster scroll. frightens all monsters + * within 2 spaces + */ + int x, y; + struct linked_list *mon; + int gotone = FALSE; + + for (x = hero.x - 2; x <= hero.x + 2; x++) + { + for (y = hero.y - 2; y <= hero.y + 2; y++) + { + if (y > 0 && x > 0 && isalpha(mvwinch(mw, y, x))) + { + if ((mon = find_mons(y, x)) != NULL) + { + struct thing *th; + + th = THINGPTR(mon); + + if (on(*th, ISUNDEAD) || on(*th, ISUNIQUE)) + continue; + + gotone = TRUE; + turn_on(*th, ISFLEE); + th->t_chasee = &player; + th->t_ischasing = FALSE; + th->t_horde = NULL; + } + } + } + } + + if (gotone) + seemsg("The monster(s) around you recoil in horror."); + else + nothing_message(flags); + } + break; + + case S_MSHIELD: /* deal with blessed/cursed later */ + if (on(player, HASMSHIELD)) + { + seemsg("The fog around you thickens."); + lengthen_fuse(FUSE_UNMSHIELD, (blessed ? 3 : 1) * HEROTIME); + } + else + { + seemsg("A fog forms around you."); + turn_on(player, HASMSHIELD); + light_fuse(FUSE_UNMSHIELD, 0, (blessed ? 3 : 1) * HEROTIME, AFTER); + } + + if (is_scroll) + know_items[TYP_SCROLL][S_MSHIELD] = TRUE; + + break; + + default: + msg("What a puzzling scroll!"); + return; + } + + look(TRUE); /* put the result of the scroll on the screen */ + status(FALSE); + + if (is_scroll) + { + if (know_items[TYP_SCROLL][which] && guess_items[TYP_SCROLL][which]) + { + ur_free(guess_items[TYP_SCROLL][which]); + guess_items[TYP_SCROLL][which] = NULL; + } + else if (askme && !know_items[TYP_SCROLL][which] && guess_items[TYP_SCROLL][which] == NULL) + { + msg("What do you want to call it? "); + + if (get_string(buf, cw) == NORM) + { + guess_items[TYP_SCROLL][which] = new_alloc(strlen(buf) + 1); + strcpy(guess_items[TYP_SCROLL][which], buf); + } + } + } +} + +/* + creat_mons() + creates the specified monster -- any if 0 +*/ + +struct linked_list * +creat_mons(struct thing *person, int monster, int message) +{ + coord mp; + + /* Search for an open place */ + + debug("Creator @(%d, %d) ", person->t_pos.y, person->t_pos.x); + + if ((place_mons(person->t_pos.y, person->t_pos.x, &mp)) != FALSE) + { + struct linked_list *nitem; + + nitem = new_item(sizeof(struct thing)); + new_monster(nitem, monster == 0 ? randmonster(NOWANDER, NOGRAB) + : monster, &mp, MAXSTATS); + chase_it(&mp, &player); + + /* If the monster is on a trap, trap it */ + + if (isatrap(mvinch(mp.y, mp.x))) + { + debug("Monster trapped during creat_mons."); + be_trapped(THINGPTR(nitem), mp); + } + + light(&hero); + return(nitem); + } + + if (message) + hearmsg("You hear a faint cry of anguish in the distance."); + + return(NULL); +} + +/* + place_mons() + finds a place to put the monster +*/ + +int +place_mons(int y, int x, coord *pos) +{ + int distance, xx, yy, appears; + + for (distance = 1; distance <= 10; distance++) + { + appears = 0; + + for (yy = y - distance; yy <= y + distance; yy++) + for (xx = x - distance; xx <= x + distance; xx++) + { + /* Don't put a monster in top of the creator or player */ + + if (xx < 0 || yy < 0) + continue; + + if (yy == y && xx == x) + continue; + + if (yy == hero.y && xx == hero.x) + continue; + + /* Or anything else nasty */ + + if (step_ok(yy, xx, NOMONST, FALSE)) + { + if (rnd(max(1, (10 * distance - ++appears))) == 0) + { + pos->y = yy; + pos->x = xx; + debug("Make monster dist %d appear %d @(%d, %d) ", + distance, appears, pos->y, pos->x); + return(TRUE); + } + } + } + } + return(FALSE); +} + +/* + is_t_on() + This subroutine determines if an object that is a ring is being + worn by the hero by Bruce Dautrich 4/3/84 + */ + +int +is_r_on(struct object *obj) +{ + if (obj == cur_ring[LEFT_1] || obj == cur_ring[LEFT_2] || + obj == cur_ring[LEFT_3] || obj == cur_ring[LEFT_4] || + obj == cur_ring[LEFT_5] || + obj == cur_ring[RIGHT_1] || obj == cur_ring[RIGHT_2] || + obj == cur_ring[RIGHT_3] || obj == cur_ring[RIGHT_4] || + obj == cur_ring[RIGHT_5]) + { + return(TRUE); + } + + return(FALSE); +} + +/* + monread() + monster gets the effect +*/ + +void +monread(struct thing *reader, int which, int flags) +{ + struct stats *curp = &(reader->t_stats); + struct stats *maxp = &(reader->maxstats); + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + + switch (which) + { + case S_SELFTELEP: + /* If monster was suffocating, stop it */ + if (on(*reader, DIDSUFFOCATE)) + { + turn_off(*reader, DIDSUFFOCATE); + extinguish_fuse(FUSE_SUFFOCATE); + } + + /* If monster held us, stop it */ + + if (on(*reader, DIDHOLD) && (hold_count == 0)) + turn_off(player, ISHELD); + + turn_off(*reader, DIDHOLD); + + if (cursed) + reader->t_no_move++; + else + { + int rm; + + if (blessed) /* Give him his hit points */ + curp->s_hpt = maxp->s_hpt; + + /* Erase the monster from the old position */ + + if (isalpha(mvwinch(cw, reader->t_pos.y, reader->t_pos.x))) + mvwaddch(cw, reader->t_pos.y, reader->t_pos.x, reader->t_oldch); + + mvwaddch(mw, reader->t_pos.y, reader->t_pos.x, ' '); + + /* Get a new position */ + + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &reader->t_pos); + } + while (winat(reader->t_pos.y, reader->t_pos.x) != FLOOR); + + /* Put it there */ + + mvwaddch(mw, reader->t_pos.y, reader->t_pos.x, reader->t_type); + reader->t_oldch = CCHAR( mvwinch(cw, reader->t_pos.y, reader->t_pos.x) ); + } + break; + + default: + debug("'%s' is a strange scroll for a monster to read!", + r_magic[which].mi_name); + break; + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/state.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/state.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,1445 @@ +/* + state.c - Portable Rogue Save State Code + + Copyright (C) 1993, 1995 Nicholas J. Kisseberth + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +/* + Notes + + Should move all game variables into one place + Should move save/restore code into save.c or some such +*/ + +#include +#include +#include +#include "rogue.h" + +/* + Variables for global game state. + + All variables that need to get saved when saving a game + are defined in this file. Long term goal is to move many + of these variables into a "struct level" data type of some + kind... perhaps not, maybe struct game... + + Other global variables that don't need to get saved are + kept in main.c. + + Other global variables that don't change during the course + of a game are kept in urogue.c, monsdata.c, data.c. +*/ + +#define _X_ { 0, 0, 0, 0, 0 } + +struct delayed_action +d_list[MAXDAEMONS] = /* daemon/fuse list */ +{ + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, +}; + +#undef _X_ + +char *s_names[MAXSCROLLS]; /* Names of the scrolls */ +char *p_colors[MAXPOTIONS]; /* Colors of the potions */ +char *r_stones[MAXRINGS]; /* Stone settings of the rings */ +char *ws_made[MAXSTICKS]; /* What sticks are made of */ +char *ws_type[MAXSTICKS]; /* Is it a wand or a staff */ + +char *guess_items[MAXMAGICTYPES][MAXMAGICITEMS]; /* guess magic is */ +int know_items[MAXMAGICTYPES][MAXMAGICITEMS]; /* flag knowlede of magic */ + /* object data */ + +struct trap traps[2 * MAXTRAPS]; /* 2x for special effects */ +struct room rooms[MAXROOMS]; /* One for each room -- A level */ +struct room *oldrp = NULL; /* Roomin(&player.t_oldpos) */ +struct thing player; /* The rogue */ +struct linked_list *lvl_obj = NULL; /* Treasure on this level */ +struct linked_list *fam_ptr = NULL; /* A ptr to the familiar */ +struct linked_list *mlist = NULL; /* Monsters on this level */ +struct thing *beast; /* The last beast that attacked */ +struct object *cur_armor = NULL; /* what rogue wears */ +struct object *cur_weapon = NULL; /* ... and wields */ +struct object *cur_ring[10]; /* His rings */ +struct linked_list *curr_mons = NULL; /* The mons. currently moving */ +struct linked_list *next_mons = NULL; /* The mons. after curr_mons */ + +/* Misc. game state info */ +char dummybuf1[50000]; +char dummybuf2[50000]; +char msgbuf[10][2*LINELEN]; /* message buffer history */ +int msg_index = 0; /* index in msg history buffer for nxt msg */ +int foodlev = 1; /* how fast he eats food */ +int ntraps = 0; /* Number of traps on this level */ +int dnum = 0; /* Dungeon number */ +int max_level = 0; /* Deepest player has gone */ +int lost_dext = 0; /* amount of lost dexterity */ +int no_command = 0; +int level = 0; +int see_dist = 3; +int no_food = 0; +int count = 0; +int food_left = HUNGERTIME; +int group = 1; +int hungry_state = F_OK; +int infest_dam = 0; +int lost_str = 0; +int hold_count = 0; +int trap_tries = 0; +int has_artifact = 0; +int picked_artifact = 0; +int luck = 0; +int resurrect = 0; +int fam_type = 0; /* The type of familiar */ +int mons_summoned = 0; /* Number of summoned monsters */ +char PLAYER = VPLAYER; /* what the player looks like */ +char take = 0; /* Thing the rogue is taking */ +char runch = 0; /* Direction player is running */ +int char_type = C_NOTSET; /* what type of character is player */ +int inv_type = INV_CLEAR; /* Overwrite style of inventory */ +int pool_teleport = FALSE; /* just teleported from a pool */ +int inwhgt = FALSE; /* true if from wghtchk() */ +int after = 0; /* True if we want after daemons */ +int waswizard = 0; /* Was a wizard sometime */ +int canwizard = 1; /* Will be permitted to do this */ +int playing = TRUE; +int running = FALSE; +int fighting = FALSE; +int wizard = FALSE; +int wiz_verbose = TRUE; +int moving = FALSE; +coord delta; /* Change indicated to get_dir() */ +LEVTYPE levtype; /* type of level i'm on */ +long purse = 0; +unsigned long total = 0; + +WINDOW *cw; /* Window that the player sees */ +WINDOW *hw; /* Used for the help command */ +WINDOW *mw; /* Used to store mosnters */ + +/* options.o */ +/* game options */ + +int terse = FALSE; +int door_stop = FALSE; +int jump = TRUE; +int doorstop = TRUE; +int firstmove = FALSE; +int askme = FALSE; +char whoami[2 * LINELEN]; /* Name of player */ +char fruit[2 * LINELEN]; /* Favorite fruit */ +char file_name[2 * LINELEN]; /* Save file name */ +char score_file[2 * LINELEN]; /* Score file name */ + +/****************************************************************************/ +/* Portable Save State Code */ +/* */ +/* UltraRogue v1.04 */ +/****************************************************************************/ + +#define URS_STATS 0xABCD0001 +#define URS_THING 0xABCD0002 +#define URS_OBJECT 0xABCD0003 +#define URS_MAGIC 0xABCD0004 +#define URS_KNOWS 0xABCD0005 +#define URS_GUESSES 0xABCD0006 +#define URS_STACKOBJECT 0xABCD0007 +#define URS_BAGOBJECT 0xABCD0008 +#define URS_MONSTERLIST 0xABCD0009 +#define URS_MONSTERSTATS 0xABCD000A +#define URS_MONSTER 0xABCD000B +#define URS_TRAP 0xABCD000C +#define URS_WINDOW 0xABCD000D +#define URS_DAEMONS 0xABCD000E + +void +ur_write(FILE *savef, void *ptr, size_t size) +{ + if (size == 0) + return; + + fwrite(ptr,size,1,savef); +} + +void +ur_read(FILE *savef, void *ptr, size_t size) +{ + if (size == 0) + return; + + fread(ptr,size,1,savef); +} + +void +ur_write_int(FILE *savef, int c) +{ + ur_write(savef,&c,sizeof(int)); +} + +int +ur_read_int(FILE *savef) +{ + int i; + + ur_read(savef, &i, sizeof(int)); + + return(i); +} + +void +ur_write_short(FILE *savef, short c) +{ + ur_write(savef,&c,sizeof(short)); +} + +short +ur_read_short(FILE *savef) +{ + short s; + + ur_read(savef, &s, sizeof(short)); + + return(s); +} + +void +ur_write_long(FILE *savef, long c) +{ + ur_write(savef,&c,sizeof(long)); +} + +long +ur_read_long(FILE *savef) +{ + long l; + + ur_read(savef, &l, sizeof(long)); + + return(l); +} + +void +ur_write_ulong(FILE *savef, unsigned long c) +{ + ur_write(savef,&c,sizeof(unsigned long)); +} + +unsigned long +ur_read_ulong(FILE *savef) +{ + long l; + + ur_read(savef, &l, sizeof(unsigned long)); + + return(l); +} + +void +ur_unread_long(FILE *savef) +{ + fseek(savef, -(long)sizeof(long), SEEK_CUR); +} + +void +ur_write_char(FILE *savef, char c) +{ + ur_write(savef,&c,sizeof(char)); +} + +char +ur_read_char(FILE *savef) +{ + char c; + + ur_read(savef, &c, sizeof(char)); + + return(c); +} + +void +ur_write_string(FILE *savef, char *s) +{ + size_t len; + + len = (s == NULL) ? 0L : strlen(s) + 1 ; + + ur_write_long(savef, (long) len); + ur_write(savef,s,len); +} + + +char * +ur_read_string(FILE *savef) +{ + size_t len; + char *buf; + + len = ur_read_long(savef); + + if (len == 0) + return(NULL); + + buf = ur_alloc(len); + + if (buf == NULL) /* Should flag a global error condition... */ + return(NULL); + + ur_read(savef,buf,len); + + return(buf); +} + +void +ur_write_coord(FILE *savef, coord c) +{ + ur_write_int(savef, c.x); + ur_write_int(savef, c.y); +} + +coord +ur_read_coord(FILE *savef) +{ + coord c; + + c.x = ur_read_int(savef); + c.y = ur_read_int(savef); + + return(c); +} + +void +ur_write_room(FILE *savef, struct room *r) +{ + int i; + + ur_write_coord(savef, r->r_pos); + ur_write_coord(savef, r->r_max); + + for(i=0; ir_exit[i]); + + ur_write_int(savef, r->r_flags); + ur_write_int(savef, r->r_nexits); + ur_write_int(savef, r->r_flags); +} + +struct room * +ur_read_room(FILE *savef) +{ + struct room *r; + int i; + + r = ur_alloc( sizeof(struct room) ); + + r->r_pos = ur_read_coord(savef); + r->r_max = ur_read_coord(savef); + + for(i=0; ir_exit[i] = ur_read_coord(savef); + + r->r_flags = ur_read_int(savef); + r->r_nexits = ur_read_int(savef); + r->r_flags = ur_read_short(savef); + + return(r); +} + +void +ur_write_object(FILE *savef, struct object *o) +{ + int other; + + ur_write_long(savef, URS_OBJECT); + ur_write_coord(savef, o->o_pos); + ur_write_string(savef, o->o_text); + ur_write_string(savef, o->o_damage); + ur_write_string(savef, o->o_hurldmg); + ur_write_long(savef, o->o_flags); + ur_write_long(savef, o->ar_flags); + ur_write_char(savef, o->o_type); + ur_write_int(savef, o->o_ident); + ur_write_int(savef, o->o_count); + ur_write_int(savef, o->o_which); + ur_write_int(savef, o->o_hplus); + ur_write_int(savef, o->o_dplus); + ur_write_int(savef, o->o_ac); + ur_write_int(savef, o->o_group); + ur_write_int(savef, o->o_weight); + ur_write_char(savef, o->o_launch); + ur_write(savef, &o->o_mark[0], MARKLEN); + ur_write_long(savef, o->o_worth); + + other = 0; + + if (o->o_bag) + other = 1; + else if (o->next_obj) + other |= 2; + + ur_write_int(savef,other); + + if (o->o_bag) + ur_write_bag(savef,o->o_bag); + if (o->next_obj && (o->next_obj->l_prev == NULL) ) + ur_write_object_stack(savef, o->next_obj); +} + +struct object * +ur_read_object(FILE *savef) +{ + struct object *o; + long id; + int other; + + o = ur_alloc(sizeof(struct object)); + + if (o == NULL) + return(NULL); + + memset(o,0,sizeof(struct object)); + + id = ur_read_long(savef); + + assert(id == URS_OBJECT); + + o->o_pos = ur_read_coord(savef); + o->o_text = ur_read_string(savef); + o->o_damage = ur_read_string(savef); + o->o_hurldmg = ur_read_string(savef); + o->o_flags = ur_read_long(savef); + o->ar_flags = ur_read_long(savef); + o->o_type = ur_read_char(savef); + o->o_ident = ur_read_int(savef); + o->o_count = ur_read_int(savef); + o->o_which = ur_read_int(savef); + o->o_hplus = ur_read_int(savef); + o->o_dplus = ur_read_int(savef); + o->o_ac = ur_read_int(savef); + o->o_group = ur_read_int(savef); + o->o_weight = ur_read_int(savef); + o->o_launch = ur_read_char(savef); + ur_read(savef, &o->o_mark[0], MARKLEN); + o->o_worth = ur_read_long(savef); + + other = ur_read_int(savef); + + if (other & 1) + o->o_bag = ur_read_bag(savef); + if (other & 2) + o->next_obj = ur_read_object_stack(savef); + + return(o); +} + +int +list_size(struct linked_list *l) +{ + int cnt=0; + + if (l == NULL) + return(0); + + while(l != NULL) + { + cnt++; + l = l->l_next; + } + + return(cnt); +} + +int +find_thing_index(struct linked_list *l, struct thing *item) +{ + int cnt=0; + + if (l == NULL) + return(-1); + + while(l != NULL) + { + if (item == l->data.th) + return(cnt+1); + + cnt++; + l = l->l_next; + } + + return(0); +} + + +int +find_list_index(struct linked_list *l, struct object *item) +{ + int cnt=0; + + if (l == NULL) + return(-1); + + while(l != NULL) + { + if (item == l->data.obj) + return(cnt+1); + + cnt++; + l = l->l_next; + } + + return(0); +} + +struct object * +find_object(struct linked_list *list, int num) +{ + int cnt = 0; + struct linked_list *l = list; + + if ( (num < 1) || (list == NULL) ) + return(NULL); + + num--; + + for(cnt = 0; cnt < num; cnt++) + { + if ( l == NULL ) + return(NULL); + + l = l->l_next; + } + + return(l->data.obj); +} + +struct thing * +find_thing(struct linked_list *list, int num) +{ + int cnt = 0; + struct linked_list *l = list; + + if ( (num < 1) || (list == NULL) ) + return(NULL); + num--; + + for(cnt = 0; cnt < num; cnt++) + { + if (l == NULL) + return(NULL); + + l = l->l_next; + } + + return(l->data.th); +} + + +void +ur_write_object_stack(FILE *savef, struct linked_list *l) +{ + int cnt; + + ur_write_long(savef, URS_STACKOBJECT); + + ur_write_int(savef, cnt = list_size(l) ); + + if (cnt == 0) + return; + + while(l != NULL) + { + ur_write_object(savef, l->data.obj); + l = l->l_next; + } +} + +void +ur_write_bag(FILE *savef, struct linked_list *l) +{ + int cnt; + + ur_write_long(savef, URS_BAGOBJECT); + + ur_write_int(savef, cnt = list_size(l) ); + + if (cnt == 0) + return; + + while(l != NULL) + { + ur_write_object(savef, l->data.obj); + l = l->l_next; + } +} + +struct linked_list * +ur_read_object_stack(FILE *savef) +{ + long id; + int i,cnt; + struct linked_list *l = NULL, *previous = NULL, *head = NULL; + + id = ur_read_long(savef); + + assert(id == URS_STACKOBJECT); + + cnt = ur_read_int(savef); + + for(i = 0; i < cnt; i++) + { + l = new_list(); + l->l_prev = previous; + + if (previous != NULL) + previous->l_next = l; + + l->data.obj = ur_read_object(savef); + + if (previous == NULL) + head = l; + + previous = l; + } + + if (l != NULL) + l->l_next = NULL; + + return(head); +} + + +struct linked_list * +ur_read_bag(FILE *savef) +{ + long id; + int i,cnt; + struct linked_list *l = NULL, *previous = NULL, *head = NULL; + + id = ur_read_long(savef); + + assert( id == URS_BAGOBJECT ); + + cnt = ur_read_int(savef); + + for(i = 0; i < cnt; i++) + { + l = new_list(); + l->l_prev = previous; + + if (previous != NULL) + previous->l_next = l; + + l->data.obj = ur_read_object(savef); + + if (previous == NULL) + head = l; + + previous = l; + } + + if (l != NULL) + l->l_next = NULL; + + return(head); +} + +void +ur_fixup_monsters(struct linked_list *l) +{ + while(l != NULL) + { + if (l->data.th->t_chasee == (void *) -1L) + l->data.th->t_chasee = &player; + else + l->data.th->t_chasee = find_thing(mlist, l->data.th->chasee_index); + + l->data.th->t_horde = find_object(lvl_obj, l->data.th->horde_index); + + l = l->l_next; + } +} + +void +ur_write_monsters(FILE *savef, struct linked_list *l) +{ + int cnt; + + ur_write_long(savef, URS_MONSTERLIST); + + cnt = list_size(l); + + ur_write_int(savef, cnt); + + if (cnt < 1) + return; + + while(l != NULL) + { + ur_write_thing(savef, l->data.th); + l = l->l_next; + } +} + +struct linked_list * +ur_read_monsters(FILE *savef) +{ + long id; + int i,cnt; + struct linked_list *l=NULL, *previous = NULL, *head = NULL; + + id = ur_read_long(savef); + + assert(id == URS_MONSTERLIST); + + cnt = ur_read_int(savef); + + if (cnt == 0) + return(NULL); + + for(i = 0; i < cnt; i++) + { + l = new_list(); + + l->l_prev = previous; + + if (previous != NULL) + previous->l_next = l; + + l->data.th = ur_read_thing(savef); + + if (previous == NULL) + head = l; + + previous = l; + } + + if (l != NULL) + l->l_next = NULL; + + return(head); +} + +void +ur_write_monster_stats(FILE *savef, struct mstats *m) +{ + ur_write_long(savef, URS_MONSTERSTATS); + ur_write_short(savef, m->s_str); + ur_write_long(savef, m->s_exp); + ur_write_int(savef, m->s_lvl); + ur_write_int(savef, m->s_arm); + ur_write_string(savef, m->s_hpt); + ur_write_string(savef, m->s_dmg); +} + +struct mstats * +ur_read_monster_stats(FILE *savef) +{ + long id; + struct mstats *m; + + id = ur_read_long(savef); + + assert(id == URS_MONSTERSTATS); + + m = ur_alloc( sizeof(struct mstats) ); + + m->s_str = ur_read_short(savef); + m->s_exp = ur_read_long(savef); + m->s_lvl = ur_read_int(savef); + m->s_arm = ur_read_int(savef); + m->s_hpt = ur_read_string(savef); + m->s_dmg = ur_read_string(savef); + + return(m); +} + +void +ur_write_monster(FILE *savef, struct monster *m) +{ + int i; + + ur_write_long(savef, URS_MONSTER); + ur_write_string(savef, m->m_name); + ur_write_short(savef, m->m_carry); + ur_write_int(savef, m->m_normal); + ur_write_int(savef, m->m_wander); + ur_write_char(savef, m->m_appear); + ur_write_string(savef, m->m_intel); + + for(i = 0; i < 10; i++) + ur_write_long(savef, m->m_flags[i]); + + ur_write_string(savef, m->m_typesum); + ur_write_short(savef, m->m_numsum); + ur_write_short(savef, m->m_add_exp); + ur_write_monster_stats(savef, &m->m_stats); +} + +struct monster * +ur_read_monster(FILE *savef) +{ + struct monster *m; + struct mstats *mstats; + + m = ur_alloc( sizeof(struct monster) ); + + m->m_name = ur_read_string(savef); + m->m_carry = ur_read_short(savef); + m->m_normal = ur_read_int(savef); + m->m_wander = ur_read_int(savef); + m->m_appear = ur_read_char(savef); + m->m_intel = ur_read_string(savef); + ur_read(savef, &m->m_flags[0], 10*sizeof(long)); + m->m_typesum = ur_read_string(savef); + m->m_numsum = ur_read_short(savef); + m->m_add_exp = ur_read_short(savef); + + mstats = ur_read_monster_stats(savef); + + m->m_stats = *mstats; + ur_free(mstats); + + return(m); +} + +void +ur_write_trap(FILE *savef, struct trap *t) +{ + ur_write_long(savef, URS_TRAP); + ur_write_coord(savef, t->tr_pos); + ur_write_long(savef, t->tr_flags); + ur_write_char(savef, t->tr_type); + ur_write_char(savef, t->tr_show); +} + +struct trap * +ur_read_trap(FILE *savef) +{ + struct trap *t; + long id; + + id = ur_read_long(savef); + + assert(id == URS_TRAP); + + t = ur_alloc( sizeof(struct trap)); + + t->tr_pos = ur_read_coord(savef); + t->tr_flags = ur_read_long(savef); + t->tr_type = ur_read_char(savef); + t->tr_show = ur_read_char(savef); + + return(t); +} + +void +ur_write_stats(FILE *savef, struct stats *s) +{ + ur_write_long(savef, URS_STATS); + ur_write_string(savef, s->s_dmg); + ur_write_long(savef, s->s_exp); + ur_write_int(savef, s->s_hpt); + ur_write_int(savef, s->s_pack); + ur_write_int(savef, s->s_carry); + ur_write_int(savef, s->s_lvl); + ur_write_int(savef, s->s_arm); + ur_write_int(savef, s->s_acmod); + ur_write_int(savef, s->s_power); + ur_write_int(savef, s->s_str); + ur_write_int(savef, s->s_intel); + ur_write_int(savef, s->s_wisdom); + ur_write_int(savef, s->s_dext); + ur_write_int(savef, s->s_const); + ur_write_int(savef, s->s_charisma); +} + +struct stats * +ur_read_stats(FILE *savef) +{ + struct stats *s; + long id; + + id = ur_read_long(savef); + + assert(id == URS_STATS); + + s = ur_alloc(sizeof(struct stats)); + + s->s_dmg = ur_read_string(savef); + s->s_exp = ur_read_long(savef); + s->s_hpt = ur_read_int(savef); + s->s_pack = ur_read_int(savef); + s->s_carry = ur_read_int(savef); + s->s_lvl = ur_read_int(savef); + s->s_arm = ur_read_int(savef); + s->s_acmod = ur_read_int(savef); + s->s_power = ur_read_int(savef); + s->s_str = ur_read_int(savef); + s->s_intel = ur_read_int(savef); + s->s_wisdom = ur_read_int(savef); + s->s_dext = ur_read_int(savef); + s->s_const = ur_read_int(savef); + s->s_charisma = ur_read_int(savef); + + return(s); +} + +void +ur_write_thing(FILE *savef, struct thing *t) +{ + int i; + + ur_write_long(savef, URS_THING); + ur_write_bag(savef, t->t_pack); + ur_write_stats(savef, &t->t_stats); + ur_write_stats(savef, &t->maxstats); + ur_write_int(savef, t->t_ischasing); + + if (t->t_chasee == &player) + ur_write_long(savef, -1L); + else if (t->t_chasee == NULL) + ur_write_long(savef, 0L); + else + { + long m; + + m = find_thing_index(mlist, t->t_chasee); + + ur_write_long(savef,m); + } + + ur_write_long(savef, find_list_index(lvl_obj, t->t_horde)); + ur_write_coord(savef, t->t_pos); + ur_write_coord(savef, t->t_oldpos); + ur_write_coord(savef, t->t_nxtpos); + + for(i = 0; i < 16; i++) + ur_write_long(savef, t->t_flags[i]); + + ur_write_int(savef, t->t_praycnt); + ur_write_int(savef, t->t_trans); + ur_write_int(savef, t->t_turn); + ur_write_int(savef, t->t_wasshot); + ur_write_int(savef, t->t_ctype); + ur_write_int(savef, t->t_index); + ur_write_int(savef, t->t_no_move); + ur_write_int(savef, t->t_rest_hpt); + ur_write_int(savef, t->t_rest_pow); + ur_write_int(savef, t->t_doorgoal); + ur_write_char(savef, t->t_type); + ur_write_char(savef, t->t_disguise); + ur_write_char(savef, t->t_oldch); +} + +struct thing * +ur_read_thing(FILE *savef) +{ + long id; + int i; + struct thing *t; + struct stats *s; + + id = ur_read_long(savef); + + assert(id == URS_THING); + + t = ur_alloc( sizeof(struct thing) ); + + t->t_pack = ur_read_bag(savef); + + s = ur_read_stats(savef); + t->t_stats = *s; + ur_free(s); + + s = ur_read_stats(savef); + t->maxstats = *s; + ur_free(s); + + t->t_ischasing = ur_read_int(savef); + t->chasee_index = ur_read_long(savef); + t->horde_index = ur_read_long(savef); + t->t_pos = ur_read_coord(savef); + t->t_oldpos = ur_read_coord(savef); + t->t_nxtpos = ur_read_coord(savef); + + for(i = 0; i < 16; i++) + t->t_flags[i] = ur_read_long(savef); + + t->t_praycnt = ur_read_int(savef); + t->t_trans = ur_read_int(savef); + t->t_turn = ur_read_int(savef); + t->t_wasshot = ur_read_int(savef); + t->t_ctype = ur_read_int(savef); + t->t_index = ur_read_int(savef); + t->t_no_move = ur_read_int(savef); + t->t_rest_hpt = ur_read_int(savef); + t->t_rest_pow = ur_read_int(savef); + t->t_doorgoal = ur_read_int(savef); + t->t_type = ur_read_char(savef); + t->t_disguise = ur_read_char(savef); + t->t_oldch = ur_read_char(savef); + + return(t); +} + +void +ur_write_window(FILE *savef, WINDOW *win) +{ + int i,j; + + ur_write_long(savef, URS_WINDOW); + + ur_write_int(savef, win->_maxy); + ur_write_int(savef, win->_maxx); + + for(i=0; i < win->_maxy; i++) + for(j = 0; j < win->_maxx; j++) + ur_write_ulong(savef, mvwinch(win,i,j)); +} + +void +ur_read_window(FILE *savef, WINDOW *win) +{ + int i,j; + int maxy, maxx; + long id; + + id = ur_read_long(savef); + + assert(id == URS_WINDOW); + + maxy = ur_read_int(savef); + maxx = ur_read_int(savef); + + for(i=0; i < maxy; i++) + for(j = 0; j < maxx; j++) + mvwaddch(win,i,j,ur_read_long(savef)); +} + +void +ur_write_daemons(FILE *savef) +{ + int i; + int id=0; + + ur_write_long(savef, URS_DAEMONS); + + for(i = 0; i < MAXDAEMONS; i++) + { + ur_write_int(savef, d_list[i].d_type ); + ur_write_int(savef, d_list[i].d_when ); + ur_write_int(savef, d_list[i].d_id); + + if (d_list[i].d_id == FUSE_UNSUMMON) + id = find_thing_index(mlist, d_list[i].d_arg); + + if (d_list[i].d_id == DAEMON_DOCTOR) + id = find_thing_index(mlist, d_list[i].d_arg); + + ur_write_int(savef, id); + ur_write_int(savef, d_list[i].d_time ); + } +} + +void +ur_read_daemons(FILE *savef) +{ + long id; + int i; + demoncnt = 0; + + id = ur_read_long(savef); + + assert(id == URS_DAEMONS); + + for(i = 0; i < MAXDAEMONS; i++) + { + d_list[i].d_type = ur_read_int(savef); + d_list[i].d_when = ur_read_int(savef); + d_list[i].d_id = ur_read_int(savef); + id = ur_read_int(savef); + d_list[i].d_time = ur_read_int(savef); + + if ((d_list[i].d_type != EMPTY) && (d_list[i].d_id == FUSE_UNSUMMON)) + { + d_list[i].d_arg = find_thing(mlist,id); + + if (d_list[i].d_arg == NULL) + d_list[i].d_type = EMPTY; + } + + if ((d_list[i].d_type != EMPTY) && (d_list[i].d_id == DAEMON_DOCTOR) ) + { + if (id == 0) + d_list[i].d_arg = &player; + else + d_list[i].d_arg = find_thing(mlist, id); + + if (d_list[i].d_arg == NULL) + d_list[i].d_type = EMPTY; + } + + if (d_list[i].d_type != EMPTY) + demoncnt++; + } +} + +void +save_file(FILE *savef) +{ + int i,weapon,armor,ring=0,room= -1,monster; + + ur_write_string(savef, save_format); + + ur_write_string(savef,"\nScroll Names\n"); + for(i = 0; i < MAXSCROLLS; i++) + ur_write_string(savef,s_names[i]); + + ur_write_string(savef,"\nPotion Colors\n"); + for(i = 0; i < MAXPOTIONS; i++) + ur_write_string(savef,p_colors[i]); + + ur_write_string(savef,"\nRing Stones\n"); + for(i = 0; i < MAXRINGS; i++) + ur_write_string(savef,r_stones[i]); + + ur_write_string(savef,"\nStick types\n"); + for(i = 0; i < MAXSTICKS; i++) + ur_write_string(savef,ws_made[i]); + + ur_write_string(savef,"\nStick types\n"); + for(i = 0; i < MAXSTICKS; i++) + ur_write_string(savef,ws_type[i]); + + ur_write_string(savef, "\nTraps on this level\n"); + ur_write_int(savef, MAXTRAPS); + for(i = 0; i < MAXTRAPS; i++) + ur_write_trap(savef, &traps[i]); + + ur_write_string(savef,"\nRooms on this level\n"); + ur_write_int(savef, MAXROOMS); + for(i = 0; i < MAXROOMS; i++) + { + ur_write_room(savef, &rooms[i]); + + if (&rooms[i] == oldrp) + room = i; + } + ur_write_int(savef,room); /* save for recovery of oldrp */ + + ur_write_string(savef,"\nThe Rogue\n"); + ur_write_thing(savef, &player); + + ur_write_string(savef,"\nObjects on this level\n"); + ur_write_bag(savef, lvl_obj); + + ur_write_string(savef,"\nRogue's Familiar, if any \n"); + ur_write_monsters(savef, fam_ptr); + + ur_write_string(savef,"\nMonsters on this level\n"); + ur_write_monsters(savef, mlist); + + monster = find_thing_index(mlist, beast); + ur_write_int(savef, monster); + + ur_write_string(savef,"\nItems in use by rogue\n"); + weapon = find_list_index(player.t_pack, cur_weapon); + armor = find_list_index(player.t_pack, cur_armor); + ur_write_int(savef, weapon); + ur_write_int(savef, armor); + + for(i=0; i < 10; i++) + { + if (cur_ring[i] == NULL) + ring = find_list_index(player.t_pack, cur_ring[i]); + + ur_write_int(savef, ring); + } + + ur_write_string(savef,"\nActive Daemons and Fuses\n"); + ur_write_daemons(savef); + + ur_write_string(savef, "\nMisc\n"); + + for(i = 0; i < 10; i++) + ur_write_string(savef, msgbuf[i]); + + ur_write_int(savef, msg_index); + ur_write_int(savef, foodlev); + ur_write_int(savef, ntraps); + ur_write_int(savef, dnum); + ur_write_int(savef, max_level); + ur_write_int(savef, lost_dext); + ur_write_int(savef, no_command); + ur_write_int(savef, level); + ur_write_int(savef, see_dist); + ur_write_int(savef, no_food); + ur_write_int(savef, count); + ur_write_int(savef, food_left); + ur_write_int(savef, group); + ur_write_int(savef, hungry_state); + ur_write_int(savef, infest_dam); + ur_write_int(savef, lost_str); + ur_write_int(savef, hold_count); + ur_write_int(savef, trap_tries); + ur_write_int(savef, has_artifact); + ur_write_int(savef, picked_artifact); + ur_write_int(savef, luck); + ur_write_int(savef, resurrect); + ur_write_int(savef, fam_type); + ur_write_int(savef, mons_summoned); + ur_write_char(savef, PLAYER); + ur_write_char(savef, take); + ur_write_char(savef, runch); + ur_write_int(savef, char_type); + ur_write_int(savef, inv_type); + ur_write_int(savef, pool_teleport); + ur_write_int(savef, inwhgt); + ur_write_int(savef, after); + ur_write_int(savef, waswizard); + ur_write_int(savef, canwizard); + ur_write_int(savef, playing); + ur_write_int(savef, running); + ur_write_int(savef, fighting); + ur_write_int(savef, wizard); + ur_write_int(savef, wiz_verbose); + ur_write_int(savef, moving); + ur_write_coord(savef, delta); + ur_write_int(savef, levtype); + ur_write_int(savef, purse); + ur_write_int(savef, total); + ur_write_window(savef, cw); + ur_write_window(savef, hw); + ur_write_window(savef, mw); + ur_write_window(savef, stdscr); + + ur_write_string(savef,"\nGame Options\n"); + ur_write_int(savef, terse); + ur_write_int(savef, door_stop); + ur_write_int(savef, doorstop); + ur_write_int(savef, jump); + ur_write_int(savef, firstmove); + ur_write_int(savef, askme); + ur_write_string(savef,whoami); + ur_write_string(savef,fruit); + ur_write_string(savef,file_name); + ur_write_string(savef,score_file); + + ur_write_string(savef,"\nEnd of UltraRogue Game State\n"); +} + +#define DUMPSTRING { str = ur_read_string(savef); /*printf("%s",str);*/ ur_free(str); } + +int +restore_file(FILE *savef) +{ + int i,j; + char *str; + struct trap *t; + struct room *r; + struct thing *p; + + str = ur_read_string(savef); + + if (strcmp(str, save_format) != 0) + { + printf("Save Game Version: %s\n", str); + printf("Real Game Version: %s\n", save_format); + printf("Sorry, versions don't match.\n"); + return(FALSE); + } + + DUMPSTRING + for(i=0; i < MAXSCROLLS; i++) + s_names[i] = ur_read_string(savef); + + DUMPSTRING + for(i=0; i < MAXPOTIONS; i++) + p_colors[i] = ur_read_string(savef); + + DUMPSTRING + for(i=0; i < MAXRINGS; i++) + r_stones[i] = ur_read_string(savef); + + DUMPSTRING + for(i=0; i < MAXSTICKS; i++) + ws_made[i] = ur_read_string(savef); + + DUMPSTRING + for(i=0; i < MAXSTICKS; i++) + ws_type[i] = ur_read_string(savef); + + DUMPSTRING + i = ur_read_int(savef); + assert(i == MAXTRAPS); + + for(i=0;i +#include +#include +#include "rogue.h" + +/* for WS_HIT, WS_WEB, etc */ + +static struct object null_stick = +{ + {0,0},NULL,NULL,"",0,"0d0",0,0,'X',0,0,0,0,0,0,0,0,0,{0} +}; + +/* + * Mask for cancelling special abilities The flags listed here will be the + * ones left on after the cancellation takes place + */ + +#define CANC0MASK ( ISBLIND | ISINWALL | ISRUN | \ + ISFLEE | ISMEAN | ISGREED | \ + CANSHOOT | ISHELD | ISHUH | \ + ISSLOW | ISHASTE | ISCLEAR | \ + ISUNIQUE) + +#define CANC1MASK ( HASDISEASE | DIDSUFFOCATE | CARRYGOLD | \ + HASITCH | CANSELL | CANBBURN | \ + CANSPEAK | CANFLY | ISFRIENDLY) + +#define CANC2MASK ( HASINFEST | NOMOVE | ISSCAVENGE | \ + DOROT | HASSTINK | DIDHOLD) + +#define CANC3MASK ( ISUNDEAD | CANBREATHE | CANCAST | \ + HASOXYGEN) + +#define CANC4MASK ( CANTRAMPLE | CANSWIM | CANWIELD | \ + ISFAST | CANBARGAIN | CANSPORE | \ + ISLARGE | ISSMALL | ISFLOCK | \ + ISSWARM | CANSTICK | CANTANGLE | \ + SHOOTNEEDLE | CANZAP | HASARMOR | \ + CANTELEPORT | ISBERSERK | ISFAMILIAR | \ + HASFAMILIAR | SUMMONING) + +#define CANC5MASK ( CANREFLECT | MAGICATTRACT | HASSHIELD | HASMSHIELD) + +#define CANC6MASK ( 0 ) +#define CANC7MASK ( 0 ) +#define CANC8MASK ( 0 ) +#define CANC9MASK ( 0 ) +#define CANCAMASK ( 0 ) +#define CANCBMASK ( 0 ) +#define CANCCMASK ( 0 ) +#define CANCDMASK ( 0 ) +#define CANCEMASK ( 0 ) +#define CANCFMASK ( 0 ) + +void +fix_stick(struct object *cur) +{ + if (strcmp(ws_type[cur->o_which], "staff") == 0) + { + cur->o_weight = 100; + cur->o_charges = 5 + rnd(10); + cur->o_damage = "2d3"; + + switch (cur->o_which) + { + case WS_HIT: + cur->o_hplus = 3; + cur->o_dplus = 3; + cur->o_damage = "2d8"; + break; + + case WS_LIGHT: + cur->o_charges = 20 + rnd(10); + break; + } + } + else /* A wand */ + { + cur->o_damage = "1d3"; + cur->o_weight = 60; + cur->o_charges = 3 + rnd(5); + + switch (cur->o_which) + { + case WS_HIT: + cur->o_hplus = 3; + cur->o_dplus = 3; + cur->o_damage = "1d8"; + break; + + case WS_LIGHT: + cur->o_charges = 10 + rnd(10); + break; + } + } + + cur->o_hurldmg = "1d1"; +} + +/* + do_zap() + zap a stick (or effect a stick-like spell) + zapper: who does it + got_dir: need to ask for direction? + which: which WS_STICK (-1 means ask from pack) + flags: ISBLESSED, ISCURSED +*/ + +void +do_zap(struct thing *zapper, int which, unsigned long flags) +{ + struct linked_list *item = NULL, *nitem; + struct object *obj, *nobj; + struct room *rp; + struct thing *tp = NULL; + char *mname = NULL; + int y, x; + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + int is_stick = (which < 0 ? TRUE : FALSE); + int got_one = TRUE; + + if (zapper != &player) + { + monster_do_zap(zapper, which, flags); + return; + } + + if (is_stick) + { + if ((obj = get_object(pack, "zap with", 0, bff_zappable)) == NULL) + return; + + if (obj->o_type != STICK && !(obj->o_flags & ISZAPPED)) + { + msg("You can't zap with that!"); + return; + } + + if (obj->o_type != STICK) /* an electrified weapon */ + which = WS_ELECT; + else + which = obj->o_which; + + if (obj->o_charges < 1) + { + nothing_message(flags); + return; + } + + obj->o_charges--; + + if (delta.y == 0 && delta.x == 0) + do + { + delta.y = rnd(3) - 1; + delta.x = rnd(3) - 1; + } + while (delta.y == 0 && delta.x == 0); + + flags = obj->o_flags; + cursed = obj->o_flags & ISCURSED; + blessed = obj->o_flags & ISBLESSED; + } + else + obj = &null_stick; + + /* Find out who the target is */ + + y = hero.y; + x = hero.x; + + while(shoot_ok(CCHAR(winat(y, x)))) + { + y += delta.y; + x += delta.x; + } + + if (x >= 0 && x < COLS && y >= 1 && y < LINES - 2 && + isalpha(mvwinch(mw, y, x))) + { + item = find_mons(y, x); + tp = THINGPTR(item); + mname = on(player, ISBLIND) ? "monster" : + monsters[tp->t_index].m_name; + + if (on(*tp, CANSELL)) + { + luck++; + aggravate(); + } + } + else + got_one = FALSE; + + debug("Zapping with %d", which); + + switch(which) + { + case WS_LIGHT: + /* Reddy Kilowat wand. Light up the room */ + if (blue_light(flags) && is_stick) + if (is_stick) + know_items[TYP_STICK][WS_LIGHT] = TRUE; + break; + + case WS_DRAIN: + + /* + * Take away 1/2 of hero's hit points, then take it away + * evenly from the monsters in the room or next to hero if he + * is in a passage. Leave the monsters alone if the stick is + * cursed. Drain 1/3rd of hero's hit points if blessed. + */ + + if (pstats.s_hpt < 2) + { + death(D_DRAINLIFE); + return; + } + + if (cursed) + pstats.s_hpt /= 2; + else if ((rp = roomin(hero)) == NULL) + drain(hero.y - 1, hero.y + 1, hero.x - 1, hero.x + 1); + else + drain(rp->r_pos.y, rp->r_pos.y + rp->r_max.y, + rp->r_pos.x, rp->r_pos.x + rp->r_max.x); + + if (blessed) + pstats.s_hpt = (int) (pstats.s_hpt * 2.0 / 3.0); + + break; + + case WS_POLYMORPH: + case WS_MONSTELEP: + case WS_CANCEL: + { + char oldch; + int rm; + int save_adj = 0; + + if (got_one) + { + /* if the monster gets the saving throw, leave the case */ + + if (blessed) + save_adj = -5; + + if (cursed) + if (which == WS_POLYMORPH) + save_adj = -5; /* not save vs becoming tougher */ + else + save_adj = 5; + + if (save_throw(VS_MAGIC - save_adj, tp)) + { + nothing_message(flags); + break; + } + else if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + /* Unhold player */ + + if (on(*tp, DIDHOLD)) + { + turn_off(*tp, DIDHOLD); + + if (--hold_count == 0) + turn_off(player, ISHELD); + } + + /* unsuffocate player */ + + if (on(*tp, DIDSUFFOCATE)) + { + turn_off(*tp, DIDSUFFOCATE); + extinguish_fuse(FUSE_SUFFOCATE); + } + + if (which == WS_POLYMORPH) + { + int which_new; + int charmed; + + detach(mlist, item); + charmed = on(*tp, ISCHARMED); + oldch = tp->t_oldch; + delta.y = y; + delta.x = x; + + if (!blessed && !cursed) + which_new = randmonster(WANDER, GRAB); + else + { + /* duplicate randmonster() for now */ + /* Eventually fix to take level */ + + int cur_level = 0, range, i; + + if (blessed) + cur_level = level / 2; + + if (cursed) + cur_level = level * 2; + + range = 4 * NLEVMONS; + i = 0; + + do + { + if (i++ > range * 10) /* just in case all have */ + { /* been genocided */ + i = 0; + + if (--cur_level <= 0) + fatal("Rogue could not find a monster to make"); + } + + which_new = NLEVMONS * (cur_level - 1) + (rnd(range) - (range - 1 - NLEVMONS)); + + if (which_new < 1) + which_new = rnd(NLEVMONS) + 1; + + if (which_new > nummonst - NUMSUMMON - 1) + { + if (blessed) + which_new = rnd(range) + (nummonst - NUMSUMMON - 1) - (range - 1); + else if (which_new > nummonst - 1) + which_new = rnd(range + NUMSUMMON) + (nummonst - 1) - (range + NUMSUMMON - 1); + } + } + while (!monsters[which_new].m_normal); + } + + new_monster(item, which_new, &delta, NOMAXSTATS); + mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name; + + if (!cursed && charmed) + turn_on(*tp, ISCHARMED); + + if (off(*tp, ISRUN)) + chase_it(&delta, &player); + + if (isalpha(mvwinch(cw, y, x))) + mvwaddch(cw, y, x, tp->t_type); + + tp->t_oldch = oldch; + seemsg("You have created a new %s!", mname); + } + else if (which == WS_CANCEL) + { + tp->t_flags[0] &= CANC0MASK; + tp->t_flags[1] &= CANC1MASK; + tp->t_flags[2] &= CANC2MASK; + tp->t_flags[3] &= CANC3MASK; + tp->t_flags[4] &= CANC4MASK; + tp->t_flags[5] &= CANC5MASK; + tp->t_flags[6] &= CANC5MASK; + tp->t_flags[7] &= CANC7MASK; + tp->t_flags[8] &= CANC8MASK; + tp->t_flags[9] &= CANC9MASK; + tp->t_flags[10] &= CANCAMASK; + tp->t_flags[11] &= CANCBMASK; + tp->t_flags[12] &= CANCCMASK; + tp->t_flags[13] &= CANCDMASK; + tp->t_flags[14] &= CANCEMASK; + tp->t_flags[15] &= CANCFMASK; + } + else /* A teleport stick */ + { + if (cursed) /* Teleport monster to */ + { /* player */ + if ((y == (hero.y + delta.y)) && + (x == (hero.x + delta.x))) + nothing_message(flags); + else + { + tp->t_pos.y = hero.y + delta.y; + tp->t_pos.x = hero.x + delta.x; + } + } + else if (blessed) /* Get rid of monster */ + { + killed(NULL, item, NOMESSAGE, NOPOINTS); + return; + } + else + { + int i = 0; + + do /* Move monster to */ + { /* another room */ + rm = rnd_room(); + rnd_pos(&rooms[rm], &tp->t_pos); + } + while (winat(tp->t_pos.y, tp->t_pos.x) != + FLOOR && i++ < 500); + } + + /* Now move the monster */ + + if (isalpha(mvwinch(cw, y, x))) + mvwaddch(cw, y, x, tp->t_oldch); + + chase_it(&tp->t_pos, &player); + mvwaddch(mw, y, x, ' '); + mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch); + + if (tp->t_pos.y != y || tp->t_pos.x != x) + tp->t_oldch = CCHAR(mvwinch(cw, tp->t_pos.y, tp->t_pos.x)); + } + } + } + break; + + case WS_MISSILE: + { + int damage; + int nsides = 4; + int ch; + coord pos; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + /* Magic Missiles *always* hit, no saving throw */ + + pos = do_motion('*', delta.y, delta.x, &player); + ch = winat(pos.y, pos.x); + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + damage = roll(pstats.s_lvl, nsides); + + if (isalpha(ch)) + { + debug("Missiled %s for %d (%d)", + mname, damage, tp->t_stats.s_hpt - damage); + + if ((tp->t_stats.s_hpt -= damage) <= 0) + { + seemsg("The missile kills the %s.", mname); + killed(&player, item, NOMESSAGE, POINTS); + } + else + { + seemsg("The missile hits the %s", mname); + chase_it(&pos, &player); + summon_help(tp, NOFORCE); + } + } + + if (pos.y >= 0 && pos.x >= 0 && + pos.y < LINES && pos.x < COLS) + mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x)); + } + break; + + case WS_HIT: + { + coord pos; + int ch; + struct object strike; /* don't want to change sticks attributes */ + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + pos = do_motion('@', delta.y, delta.x, &player); + ch = winat(pos.y, pos.x); + + if (isalpha(ch)) + { + static char buf[20]; + int nsides, ndice; + + strike = *obj; + + if (blessed) + strike.o_hplus = 12; + else if (cursed) + strike.o_hplus = 3; + else + strike.o_hplus = 6; + + if (!is_stick || strcmp(ws_type[which], "staff") == 0) + ndice = 3; + else + ndice = 2; + + if (blessed) + nsides = 16; + else if (cursed) + nsides = 4; + else + nsides = 8; + + sprintf(buf, "%dd%d", ndice, nsides); + + strike.o_damage = buf; + fight(&tp->t_pos, &strike, NOTHROWN); + } + } + break; + + case WS_SLOW_M: + if (got_one) + { + if (cursed) + { + if (off(*tp, ISSLOW)) + turn_on(*tp, ISHASTE); + else + turn_off(*tp, ISSLOW); + } + else if (blessed) + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + turn_off(*tp, ISRUN); + turn_on(*tp, ISHELD); + return; + } + else + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + if (off(*tp, ISHASTE)) + turn_on(*tp, ISSLOW); + else + turn_off(*tp, ISHASTE); + + tp->t_turn = TRUE; + } + + delta.y = y; + delta.x = x; + chase_it(&delta, &player); + } + break; + + case WS_CHARGE: + if (know_items[TYP_STICK][which] != TRUE && is_stick) + { + msg("This is a wand of charging."); + know_items[TYP_STICK][which] = TRUE; + } + + if ((nitem = get_item("charge", STICK)) != NULL) + { + nobj = OBJPTR(nitem); + + if ((nobj->o_charges == 0) && (nobj->o_which != WS_CHARGE)) + { + fix_stick(nobj); + + if (blessed) + nobj->o_charges *= 2; + else if (cursed) + nobj->o_charges /= 2; + } + else + { + if (blessed) + nobj->o_charges += 2; + else if (cursed) + nobj->o_charges -= 1; + else + nobj->o_charges += 1; + } + } + break; + + case WS_ELECT: + case WS_FIRE: + case WS_COLD: + { + char *name = NULL; + int damage; + int ndice = 3 + pstats.s_lvl; + int nsides; + + if (is_stick) + { + know_items[TYP_STICK][which] = TRUE; + + if (strcmp(ws_type[which], "staff") == 0) + nsides = 8; + else + nsides = 4; + } + else + nsides = 6; + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + switch (which) + { + case WS_ELECT: + name = "lightning bolt"; + + if (rnd(2)) + ndice += rnd(obj->o_charges / 10); + else + nsides += rnd(obj->o_charges / 10); + + break; + + case WS_FIRE: + name = "flame"; + break; + + case WS_COLD: + name = "ice"; + break; + } + + damage = roll(ndice, nsides); + shoot_bolt(&player, hero, delta, POINTS, D_BOLT, name, damage); + } + break; + + case WS_ANTIMATTER: + { + int m1, m2, x1, y1; + char ch; + struct linked_list *ll; + struct thing *lt; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + y1 = hero.y; + x1 = hero.x; + + do + { + y1 += delta.y; + x1 += delta.x; + ch = winat(y1, x1); + } + while (ch == PASSAGE || ch == FLOOR); + + for (m1 = x1 - 1; m1 <= x1 + 1; m1++) + { + for (m2 = y1 - 1; m2 <= y1 + 1; m2++) + { + ch = winat(m2, m1); + + if (m1 == hero.x && m2 == hero.y) + continue; + + if (ch != ' ') + { + ll = find_obj(m2, m1); + + while (ll != NULL) + { + rem_obj(ll, TRUE); + ll = find_obj(m2, m1); + } + + ll = find_mons(m2, m1); + + if (ll != NULL) + { + lt = THINGPTR(ll); + if (on(*lt, CANSELL)) + { + luck++; + aggravate(); + } + + if (off(*lt, CANINWALL)) + { + check_residue(lt); + detach(mlist, ll); + discard(ll); + mvwaddch(mw, m2, m1, ' '); + } + } + + mvaddch(m2, m1, ' '); + mvwaddch(cw, m2, m1, ' '); + } + } + } + + touchwin(cw); + touchwin(mw); + } + break; + + case WS_CONFMON: + case WS_PARALYZE: + if (got_one) + { + if (save_throw(VS_MAGIC - (blessed ? 5 : (cursed ? -5 : 0)), tp)) + nothing_message(flags); + else + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + switch(which) + { + case WS_CONFMON: + seemsg("The %s looks bewildered.", mname); + turn_on(*tp, ISHUH); + break; + + case WS_PARALYZE: + seemsg("The %s looks stunned.", mname); + tp->t_no_move = FREEZETIME; + break; + } + } + + delta.y = y; + delta.x = x; + chase_it(&delta, &player); + } + else + nothing_message(flags); + + break; + + case WS_XENOHEALING: + { + int hpt_gain = roll(zapper->t_stats.s_lvl, (blessed ? 8 : 4)); + int mons_hpt = tp->t_stats.s_hpt; + + if (got_one) + { + if (cursed) + { + if (!save_throw(VS_MAGIC, tp)) + { + if ((mons_hpt -= hpt_gain) <= 0) { + seemsg("The %s shrivels up and dies!", mname); + killed(&player, item, NOMESSAGE, POINTS); + } + else + seemsg("Wounds appear all over the %s.", mname); + } + else + nothing_message(flags); + + delta.y = y; + delta.x = x; + chase_it(&delta, &player); + } + else + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + mons_hpt = min(mons_hpt + hpt_gain, tp->maxstats.s_hpt); + seemsg("The %s appears less wounded!", mname); + } + } + else + nothing_message(flags); + } + break; + + case WS_DISINTEGRATE: + if (got_one) + { + if (cursed) + { + if (on(*tp, ISUNIQUE)) + { + if (on(*tp, CANSUMMON)) + summon_help(tp, FORCE); + else + msg("The %s is very upset.", mname); + + turn_on(*tp, ISHASTE); + } + else + { + int m1, m2; + coord mp; + struct linked_list *titem; + int ch; + struct thing *th; + + for (m1 = tp->t_pos.x - 1; m1 <= tp->t_pos.x + 1; m1++) + { + for (m2 = tp->t_pos.y - 1; m2 <= tp->t_pos.y + 1; m2++) + { + ch = winat(m2, m1); + + if (shoot_ok(ch) && ch != PLAYER) + { + mp.x = m1; /* create it */ + mp.y = m2; + titem = new_item(sizeof(struct thing)); + new_monster(titem, (short) tp->t_index, &mp, NOMAXSTATS); + th = THINGPTR(titem); + turn_on(*th, ISMEAN); + chase_it(&mp, &player); + } + } + } + } + + delta.y = y; + delta.x = x; + turn_on(*tp, ISMEAN); + chase_it(&delta, &player); + } + else /* if its a UNIQUE it might still live */ + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + if (on(*tp, ISUNIQUE) && + save_throw(VS_MAGIC - (blessed ? -5 : 0), tp)) + { + tp->t_stats.s_hpt = tp->t_stats.s_hpt / 2 - 1; + + if (tp->t_stats.s_hpt < 1) + { + killed(&player, item, NOMESSAGE, POINTS); + seemsg("The %s fades away.", mname); + } + else + { + delta.y = y; + delta.x = x; + + chase_it(&delta, &player); + + if (on(*tp, CANSUMMON)) + summon_help(tp, FORCE); + else + seemsg("The %s is very upset.", mname); + } + } + else + { + if (on(*tp, CANSELL)) + { + luck++; + aggravate(); + } + seemsg("The %s turns to dust and blows away.", mname); + killed(&player, item, NOMESSAGE, POINTS); + } + } + } + break; + + case WS_NOTHING: + nothing_message(flags); + break; + + case WS_INVIS: + { + if (cursed) + { + int x1, y1, x2, y2; + int zapped = FALSE; + + if ((rp = roomin(hero)) == NULL) + { + x1 = max(hero.x - 1, 0); + y1 = max(hero.y - 1, 0); + x2 = min(hero.x + 1, COLS - 1); + y2 = min(hero.y + 1, LINES - 3); + } + else + { + x1 = rp->r_pos.x; + y1 = rp->r_pos.y; + x2 = rp->r_pos.x + rp->r_max.x; + y2 = rp->r_pos.y + rp->r_max.y; + } + + for (item = mlist; item != NULL; item = next(item)) + { + tp = THINGPTR(item); + + if (tp->t_pos.x >= x1 && tp->t_pos.x <= x2 && + tp->t_pos.y >= y1 && tp->t_pos.y <= y2) + { + turn_on(*tp, ISINVIS); + turn_on(*tp, ISRUN); + turn_off(*tp, ISDISGUISE); + chase_it(&tp->t_pos, &player); + zapped = TRUE; + } + } + + if (zapped) + seemsg("The monsters seem to have all disappeared."); + else + nothing_message(flags); + } + else + { + if (got_one) + { + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + if (blessed) + { + turn_off(*tp, ISINVIS); + turn_off(*tp, ISSHADOW); + seemsg("The %s appears.", mname); + } + else + { + turn_on(*tp, ISINVIS); + seemsg("The %s disappears.", mname); + } + } + else + nothing_message(flags); + } + light(&hero); + } + break; + + case WS_BLAST: + { + int ch; + struct linked_list *itm, *ip; + struct object *ob; + struct trap *trp; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + itm = spec_item(WEAPON, GRENADE, 0, 0); + ob = OBJPTR(itm); + ob->o_count = 1; + ob->o_flags |= ISKNOW; + hearmsg("BOOOM!"); + aggravate(); + ob->o_pos.x = hero.x; + ob->o_pos.y = hero.y; + + for (;;) + { + ob->o_pos.y += delta.y; + ob->o_pos.x += delta.x; + + if (!ce(ob->o_pos, hero) && + cansee(ob->o_pos.y, ob->o_pos.x) && + mvwinch(cw, ob->o_pos.y, ob->o_pos.x) != ' ') + { + mvwaddch(cw, ob->o_pos.y, ob->o_pos.x, + show(ob->o_pos.y, ob->o_pos.x)); + } + + if (shoot_ok(ch = winat(ob->o_pos.y, ob->o_pos.x)) + && ch != DOOR && !ce(ob->o_pos, hero)) + { + if (cansee(ob->o_pos.y, ob->o_pos.x) && + ntraps + 1 < 2 * MAXTRAPS && + mvwinch(cw, ob->o_pos.y, ob->o_pos.x) != ' ') + { + mvwaddch(cw, ob->o_pos.y, ob->o_pos.x,TRAPDOOR); + wrefresh(cw); + } + + if (isatrap(ch)) + { + trp = trap_at(ob->o_pos.y, ob->o_pos.x); + + if (trp != NULL) + { + trp->tr_type = TRAPDOOR; + trp->tr_flags |= ISFOUND; + trp->tr_show = TRAPDOOR; + } + } + else if (isalpha(ch)) + hit_monster(ob->o_pos.y, ob->o_pos.x, ob, &player); + else if ((ch == FLOOR || ch == PASSAGE) + && ntraps + 1 < 2 * MAXTRAPS) + { + mvaddch(ob->o_pos.y, ob->o_pos.x, TRAPDOOR); + traps[ntraps].tr_type = TRAPDOOR; + traps[ntraps].tr_flags = ISFOUND; + traps[ntraps].tr_show = TRAPDOOR; + traps[ntraps].tr_pos.y = ob->o_pos.y; + traps[ntraps++].tr_pos.x = ob->o_pos.x; + } + else if (ch == POTION || ch == SCROLL || ch == FOOD + || ch == WEAPON || ch == RING || ch == ARMOR + || ch == STICK || ch == ARTIFACT || ch == GOLD) + { + ip = find_obj(ob->o_pos.y, ob->o_pos.x); + + while (ip) /* BOOM destroys all stacked objects */ + { + rem_obj(ip, TRUE); + ip = find_obj(ob->o_pos.y, ob->o_pos.x); + } + + mvaddch(ob->o_pos.y, ob->o_pos.x, + (roomin(ob->o_pos) == NULL ? PASSAGE : FLOOR) ); + } + continue; + } + break; + } + discard(itm); + } + break; + + case WS_WEB: + { + int ch; + coord pos; + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + + pos = do_motion('#', delta.y, delta.x, &player); + ch = winat(pos.y, pos.x); + + if (isalpha(ch)) + { + if (save_throw(VS_MAGIC, tp)) + seemsg("The %s evades your web.", mname); + else + { + seemsg("The %s is webbed.", mname); + turn_off(*tp, ISRUN); + turn_on(*tp, ISHELD); + } + } + + if (pos.y >= 0 && pos.x >= 0 && + pos.y < LINES && pos.x < COLS) + mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x)); + } + break; + + case WS_KNOCK: + case WS_CLOSE: + if (blessed) /* Do all doors in room */ + { + char new_door = (which == WS_KNOCK ? DOOR : SECRETDOOR); + struct room *rmp = roomin(hero); + + if (rmp != NULL) + for (x = 0; x < rmp->r_nexits; x++) + { + move(rmp->r_exit[x].y, rmp->r_exit[x].x); + addch(new_door); + } + else /* Do all adjacent doors */ + for (x = hero.x - 1; x <= hero.x + 1; x++) + for (y = hero.y - 1; y <= hero.y + 1; y++) + switch(CCHAR( winat(y, x) )) + { + case DOOR: + case SECRETDOOR: + addch(new_door); + break; + } + light(&hero); + } + else if (cursed)/* do opposite spell */ + do_zap(zapper, (which == WS_KNOCK ? WS_CLOSE : WS_KNOCK), ISBLESSED); + else + { + coord zap_loc; + char loc; + + zap_loc.y = hero.y + delta.y; + zap_loc.x = hero.x + delta.x; + loc = winat(zap_loc.y, zap_loc.x); + + switch (loc) + { + case SECRETDOOR: + if (which == WS_KNOCK) + { + mvaddch(zap_loc.y, zap_loc.x, DOOR); + seemsg("A secret door stands revealed before you!"); + + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + } + else + nothing_message(flags); + break; + + case DOOR: + + if (which == WS_CLOSE) + { + mvaddch(zap_loc.y, zap_loc.x, SECRETDOOR); + msg("You sucessfully block off the door.") +; + if (is_stick) + know_items[TYP_STICK][which] = TRUE; + } + else + nothing_message(flags); + + break; + + default: + nothing_message(flags); + } + } + + break; + default: + msg("What a bizarre schtick!"); + } +} + +/* + drain() + Do drain hit points from player shtick +*/ + +void +drain(int ymin, int ymax, int xmin, int xmax) +{ + int i, j, cnt; + struct thing *ick; + struct linked_list *item; + + /* First count how many things we need to spread the hit points among */ + + for (cnt = 0, i = ymin; i <= ymax; i++) + for (j = xmin; j <= xmax; j++) + if (isalpha(mvwinch(mw, i, j))) + cnt++; + + if (cnt == 0) + { + msg("You have a tingling feeling."); + return; + } + else + msg("You feel weaker."); + + cnt = pstats.s_hpt / cnt; + pstats.s_hpt /= 2; + + /* Now zot all of the monsters */ + + for (i = ymin; i <= ymax; i++) + for (j = xmin; j <= xmax; j++) + if (isalpha(mvwinch(mw, i, j)) && ((item = find_mons(i, j)) != NULL)) + { + ick = THINGPTR(item); + + if (on(*ick, CANSELL)) + { + luck++; + aggravate(); + } + + if ((ick->t_stats.s_hpt -= cnt) < 1) + killed(&player, item, + cansee(i, j) && !on(*ick, ISINVIS), POINTS); + } +} + +/* + charge_str() + charge a wand for wizards. +*/ + +char * +charge_str(struct object *obj, char *buf) +{ + if (buf == NULL) + return( "[UltraRogue Bug #103]" ); + + if (!(obj->o_flags & ISKNOW)) + buf[0] = '\0'; + else if (obj->o_charges == 1) + sprintf(buf, " [%d charge]", obj->o_charges); + else + sprintf(buf, " [%d charges]", obj->o_charges); + + return(buf); +} + + +/* + shoot_bolt() + fires a bolt from the given starting point in the given direction + struct thing *shooter; + coord start, dir; + int get_points; should player get exp points? + short reason; reason for dying + char *name; fire, nerve, cold, etc + int damage; make zapee suffer +*/ + +void +shoot_bolt(struct thing *shooter, coord start, coord dir, int get_points, int reason, char *name, int damage) +{ + int ch; + char dirch; + int change; + short y, x; + coord pos; + coord spotpos[BOLT_LENGTH + 1]; + int ret_val = FALSE;/* True if breathing monster gets killed */ + struct linked_list *item; + struct thing *tp; + char *mname; + int bounced; /* where along BOLT_LENGTH it hit a wall */ + int player_damage; /* damage if player saved */ + int no_effect; /* zap does not effect zappee */ + int take_that[BOLT_LENGTH + 1]; /* damage to each monster */ + + /* last spot for player */ + + debug("%s does %d damage", name, damage); + + switch (dir.y + dir.x) + { + case 0: + dirch = '/'; + break; + + case 1: + case -1: + dirch = (dir.y == 0 ? '-' : '|'); + break; + + case 2: + case -2: + dirch = '\\'; + break; + } + + pos.y = start.y + dir.y; + pos.x = start.x + dir.x; + change = FALSE; + + for (y = 0; y < BOLT_LENGTH + 1; y++) + take_that[y] = 0; + + bounced = 0; + + for (y = 0; y < BOLT_LENGTH; y++) + { + no_effect = FALSE; + ch = winat(pos.y, pos.x); + spotpos[y] = pos; + + /* Bolt damage dimishes over space */ + + damage = max(1, damage - (y / 3)); + + /* Are we at hero? */ + + if (ce(pos, hero)) + goto at_hero; + + switch(ch) + { + case SECRETDOOR: + case '|': + case '-': + case ' ': + bounced = y; + if (dirch == '-' || dirch == '|') + { + dir.y = -dir.y; + dir.x = -dir.x; + } + else + switch (ch) + { + case '|': + case '-': + case SECRETDOOR: + { + struct room *rp; + + rp = roomin(pos); + + if (pos.y == rp->r_pos.y || + pos.y == rp->r_pos.y + rp->r_max.y - 1) + { + dir.y = -dir.y; + change ^= TRUE; + } + + if (pos.x == rp->r_pos.x || + pos.x == rp->r_pos.x + rp->r_max.x - 1) + { + dir.x = -dir.x; + change ^= TRUE; + } + } + default: /* A wall */ + { + char chy = CCHAR( mvinch(pos.y - dir.y, pos.x + dir.x)), chx = CCHAR( mvinch(pos.y + dir.y, pos.x - dir.x) ); + + if (chy != WALL && chy != SECRETDOOR && + chy != '-' && chy != '|') + { + dir.y = -dir.y; + change = TRUE; + } + else if (chx != WALL && chx != SECRETDOOR && + chx != '-' && chx != '|') + { + dir.x = -dir.x; + change = TRUE; + } + else + { + dir.y = -dir.y; + dir.x = -dir.x; + } + } + } + + /* Do we change how the bolt looks? */ + + if (change) + { + change = FALSE; + + if (dirch == '\\') + dirch = '/'; + else if (dirch == '/') + dirch = '\\'; + } + + break; + + default: + if (isalpha(ch)) /* hit a monster */ + { + item = find_mons(pos.y, pos.x); + + if (item == NULL) + { + debug("Can't find monster %c @ %d %d.", + ch, pos.y, pos.x); + + continue; + } + + tp = THINGPTR(item); + mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name; + + /* Disguised monsters stay hidden if they save */ + + if (on(*tp, ISDISGUISE) && save_throw(VS_MAGIC, tp) && + (tp->t_type != tp->t_disguise) && off(player, ISBLIND)) + { + msg("Wait! That's a %s!", mname); + turn_off(*tp, ISDISGUISE); + } + + tp->t_wasshot = TRUE; + + if (on(*tp, CANSELL)) + { + luck++; + aggravate(); + } + + /* Hit the monster -- does it do damage? */ + + if (strcmp(name, "ice") == 0) + { + if (on(*tp, NOCOLD) || on(*tp, ISUNDEAD)) + no_effect = TRUE; + } + else if (strcmp(name, "flame") == 0) + { + if (on(*tp, NOFIRE)) + no_effect = TRUE; + + if (on(*tp, CANBBURN)) + { + seemsg("The %s is burned to death by the flame.", + mname); + + take_that[y] += tp->t_stats.s_hpt + 1; + ret_val = TRUE; + } + } + else if (strcmp(name, "lightning bolt") == 0) + { + if (on(*tp, NOBOLT)) + no_effect = TRUE; + + if (on(*tp, BOLTDIVIDE)) + { + no_effect = TRUE; + + if (creat_mons(tp, tp->t_index, NOMESSAGE)) + seemsg("The %s divides the %s.", name, mname); + } + } + + if (no_effect == TRUE) + { + msg("The %s has no effect on the %s.", name, + on(player, ISBLIND) ? "monster" : mname); + } + else + { + take_that[(bounced ? bounced-- : y)] += + save_throw(VS_MAGIC, tp) ? damage / 2 : damage; + } + } + else if (pos.y == hero.y && pos.x == hero.x) + { + at_hero: + player_damage = damage; + running = fighting = FALSE; + bounced = 0; + + if (cur_armor != NULL && + cur_armor->o_which == CRYSTAL_ARMOR && + (strcmp(name, "acid") == 0)) + { + player_damage = 0; + msg("The acid splashes harmlessly against your armor!"); + } + else if (((cur_armor != NULL && + cur_armor->o_which == CRYSTAL_ARMOR) || + (on(player, ISELECTRIC)) || + is_wearing(R_ELECTRESIST)) && + (strcmp(name, "lightning bolt") == 0)) + { + player_damage = 0; + + if (rnd(100) < 75 + && cur_weapon != NULL + && shooter != &player) + { + cur_weapon->o_flags |= ISZAPPED; + cur_weapon->o_charges += (10 + rnd(15)); + } + + if (cur_weapon != NULL && cur_armor != NULL) + msg("Your armor and %s are covered with dancing blue lights!", weaps[cur_weapon->o_which].w_name); + else if (cur_armor != NULL) + msg("Your armor is covered with dancing blue lights!"); + else if (cur_weapon != NULL) + msg("Your %s is covered with dancing blue lights!", weaps[cur_weapon->o_which].w_name); + else + msg("You are momentarily covered with dancing blue lights."); + } + else if ((is_wearing(R_FIRERESIST) || on(player, NOFIRE)) && + (strcmp(name, "flame") == 0)) + { + player_damage = 0; + msg("The flame bathes you harmlessly."); + } + else if ((is_wearing(R_COLDRESIST) || on(player, NOCOLD)) && + (strcmp(name, "ice") == 0)) + { + player_damage = 0; + msg("The ice cracks and quickly melts around you."); + } + else if (save(VS_MAGIC)) + { + take_that[BOLT_LENGTH] += player_damage / 2; + debug("Player dodges %s for %d.", name, player_damage / 2); + } + else + { + debug("Player zapped by %s for %d.", name, player_damage); + take_that[BOLT_LENGTH] += player_damage; + } + + /* Check for gas with special effects */ + + if (off(player, HASOXYGEN) && !is_wearing(R_BREATHE) && !save(VS_BREATH)) + { + if (strcmp(name, "nerve gas") == 0) + { + if (no_command == 0) + { + msg("The nerve gas paralyzes you."); + no_command = FREEZETIME; + } + } + else if (strcmp(name, "sleeping gas") == 0) + { + if (no_command == 0) + { + msg("The sleeping gas puts you to sleep."); + no_command = SLEEPTIME; + } + } + else if (strcmp(name, "slow gas") == 0 + && !is_wearing(R_FREEDOM)) + { + msg("You feel yourself moving %sslower.", + on(player, ISSLOW) ? "even " : ""); + + if (on(player, ISSLOW)) + lengthen_fuse(FUSE_NOSLOW, rnd(10) + 4); + else + { + turn_on(player, ISSLOW); + player.t_turn = TRUE; + light_fuse(FUSE_NOSLOW, 0, rnd(10) + 4, AFTER); + } + } + else if (strcmp(name, "fear gas") == 0) + { + if (!(on(player, ISFLEE) && + (player.t_chasee==shooter)) && + (shooter != &player)) + { + if (off(player, SUPERHERO) + && player.t_ctype != C_PALADIN) + { + turn_on(player, ISFLEE); + player.t_chasee = shooter; + player.t_ischasing = FALSE; + msg("The fear gas terrifies you."); + } + else + msg("The fear gas has no effect."); + } + } + } + } + mvwaddch(cw, pos.y, pos.x, dirch); + wrefresh(cw); + } + pos.y += dir.y; + pos.x += dir.x; + } + + /* + * Now that we have determined the damage for the length of the bolt, + * apply it to each monster (and then the player) in turn + */ + + for (x = 0; x < BOLT_LENGTH; x++) + { + ch = winat(spotpos[x].y, spotpos[x].x); + + if (take_that[x] != 0 && isalpha(ch)) + { + if ((item = find_mons(spotpos[x].y, spotpos[x].x)) == NULL) + { + debug("Can't find monster %c @ %d %d.", + ch, spotpos[x].y, spotpos[x].x); + + continue; + } + else + tp = THINGPTR(item); + + mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name; + + debug("Zapped %s for %d (%d)", + mname, take_that[x], tp->t_stats.s_hpt - take_that[x]); + + if ((tp->t_stats.s_hpt -= take_that[x]) <= 0) + { + if (cansee(tp->t_pos.y, tp->t_pos.x)) + msg("The %s kills the %s.", name, + on(player, ISBLIND) ? "monster" : mname); + + killed(shooter, item, NOMESSAGE, get_points); + ret_val = TRUE; + } + else if (get_points) + { + chase_it(&spotpos[x], &player); + summon_help(tp, NOFORCE); + } + } + + if (spotpos[x].y >= 0 && spotpos[x].x >= 0 && + spotpos[x].y < LINES && spotpos[x].x < COLS) + mvwaddch(cw, spotpos[x].y, spotpos[x].x, + show(spotpos[x].y, spotpos[x].x)); + } + + if (take_that[BOLT_LENGTH] != 0) + { + if (tp != THINGPTR(fam_ptr)) + { + msg("You are hit by the %s.", name); + + if ((pstats.s_hpt -= take_that[BOLT_LENGTH]) <= 0) + { + death(reason); + return; + } + } + } + + return; +} + +/* + monster_do_zap() + monster gets the effect +*/ + +void +monster_do_zap(struct thing *zapper, int which, int flags) +{ + struct stats *curp = &(zapper->t_stats); + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + int shoot = FALSE; + int damage; + int ndice, nsides; + int ch; + char *bolt_name = NULL; + coord pos; + + switch(which) + { + case WS_MISSILE: + bolt_name = "magic missile"; + pos = do_motion('*', delta.y, delta.x, zapper); + ch = winat(pos.y, pos.x); + ndice = curp->s_lvl; + nsides = 4; + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + damage = roll(ndice, nsides); + + if (ch == PLAYER) + { + if (save(VS_MAGIC)) /* help the player */ + msg("You evade the %s.", bolt_name); + else + { + msg("You are hit by the %s.", bolt_name); + + if ((pstats.s_hpt -= damage) <= 0) + death(D_BOLT); + } + } + else if (isalpha(ch)) + { + struct linked_list *item = find_mons(pos.y, pos.x); + + if (item != NULL) + { + struct thing *tp = THINGPTR(item); + int see_it = cansee(pos.y, pos.x); + + if ((tp->t_stats.s_hpt -= damage) <= 0) + killed(zapper, item, see_it, (zapper == THINGPTR(fam_ptr))); + else if (see_it) + msg("The %s hits the monster.", bolt_name); + } + } + + if (pos.y >= 0 && pos.x >= 0 && + pos.y < LINES && pos.x < COLS) + mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x)); + + break; + + case WS_CANCEL: + cancel_player(off(*zapper, ISUNIQUE)); + break; + + case WS_COLD: + shoot = TRUE; + bolt_name = "cold"; + break; + + case WS_FIRE: + shoot = TRUE; + bolt_name = "fire"; + break; + + case WS_ELECT: + shoot = TRUE; + bolt_name = "lightning"; + break; + + default: + debug("'%s' is a strange stick for a monster to zap!", + ws_magic[which].mi_name); + break; + } + + if (shoot) + { + ndice = curp->s_lvl / 2 + 1; + nsides = 6; + + if (cursed) + nsides /= 2; + else if (blessed) + nsides *= 2; + + damage = roll(ndice, nsides); + shoot_bolt(zapper, zapper->t_pos, delta, + (zapper == THINGPTR(fam_ptr)), D_BOLT, bolt_name, damage); + } +} + +struct powers +{ + unsigned long p_flag; + int fuse_id; +}; + +/* Order in which powers will attempt to be cancelled */ + +struct powers player_powers[] = +{ + { ISHASTE, FUSE_NOHASTE }, + { ISELECTRIC, FUSE_UNELECTRIFY }, + { CANINWALL, FUSE_UNPHASE }, + { CANFLY, FUSE_UNFLY }, + { ISINVIS, FUSE_APPEAR }, + { CANREFLECT, FUSE_UNGAZE }, + { ISDISGUISE, FUSE_UNDISGUISE }, + { HASMSHIELD, FUSE_UNMSHIELD }, + { ISREGEN, FUSE_UNREGEN }, + { CANSEE, FUSE_UNSEE }, + { NOCOLD, FUSE_UNCOLD }, + { NOFIRE, FUSE_UNHOT }, + { HASOXYGEN, FUSE_UNBREATHE }, + { HASSHIELD, FUSE_UNSHIELD }, + { ULONG_MAX, FUSE_NULL } +}; + +void +cancel_player(int not_unique) +{ + struct powers *pp; + int no_effect = TRUE; + + for(pp = player_powers; pp->p_flag != ULONG_MAX; pp++) + { + if (on(player, pp->p_flag) && !save(VS_MAGIC)) + { + fuses[pp->fuse_id].func(NULL); + extinguish_fuse(pp->fuse_id); + no_effect = FALSE; + + if (not_unique) /* Gods might cancel everything */ + break; + } + } + + if (no_effect) + nothing_message(ISNORMAL); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/things.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/things.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,935 @@ +/* + things.c - functions for dealing with things like potions and scrolls + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +/* + inv_name() + return the name of something as it would appear in an inventory. +*/ + +char * +inv_name(struct object *obj, int lowercase) +{ + char *pb; + char buf[1024]; + + switch(obj->o_type) + { + case SCROLL: + if (obj->o_count == 1) + sprintf(prbuf, "A %s%sscroll ", + obj->o_flags & CANRETURN ? "claimed " : "", + blesscurse(obj->o_flags)); + else + sprintf(prbuf, "%d %s%sscrolls ", + obj->o_count, + obj->o_flags & CANRETURN ? "claimed " : "", + blesscurse(obj->o_flags)); + + pb = &prbuf[strlen(prbuf)]; + + if (know_items[TYP_SCROLL][obj->o_which]) + sprintf(pb, "of %s", s_magic[obj->o_which].mi_name); + else if (guess_items[TYP_SCROLL][obj->o_which]) + sprintf(pb, "called %s", guess_items[TYP_SCROLL][obj->o_which]); + else + sprintf(pb, "titled '%s'", s_names[obj->o_which]); + break; + + case POTION: + if (obj->o_count == 1) + sprintf(prbuf, "A %s%spotion ", + obj->o_flags & CANRETURN ? "claimed " : "", + blesscurse(obj->o_flags)); + else + sprintf(prbuf, "%d %s%spotions ", + obj->o_count, + obj->o_flags & CANRETURN ? "claimed " : "", + blesscurse(obj->o_flags)); + + pb = &prbuf[strlen(prbuf)]; + + if (know_items[TYP_POTION][obj->o_which]) + sprintf(pb, "of %s(%s)", p_magic[obj->o_which].mi_name, + p_colors[obj->o_which]); + else if (guess_items[TYP_POTION][obj->o_which]) + sprintf(pb, "called %s(%s)", guess_items[TYP_POTION][obj->o_which], + p_colors[obj->o_which]); + else + { + if (obj->o_count == 1) + sprintf(prbuf, "A%s %s potion", + obj->o_flags & CANRETURN ? " claimed" : + (char *) vowelstr(p_colors[obj->o_which]), + p_colors[obj->o_which]); + else + sprintf(prbuf, "%d %s%s potions", + obj->o_count, + obj->o_flags & CANRETURN ? "claimed " : "", + (char *) p_colors[obj->o_which]); + } + break; + + case FOOD: + if (obj->o_count == 1) + sprintf(prbuf, "A%s %s", + obj->o_flags & CANRETURN ? " claimed" : + (char *) vowelstr(fd_data[obj->o_which].mi_name), + fd_data[obj->o_which].mi_name); + else + sprintf(prbuf, "%d %s%ss", obj->o_count, + obj->o_flags & CANRETURN ? "claimed " : "", + fd_data[obj->o_which].mi_name); + break; + + case WEAPON: + if (obj->o_count > 1) + sprintf(prbuf, "%d ", obj->o_count); + else if ((obj->o_flags & (ISZAPPED | CANRETURN | ISPOISON | ISSILVER | ISTWOH)) || + ((obj->o_flags & (ISKNOW | ISZAPPED)) == (ISKNOW | ISZAPPED))) + strcpy(prbuf, "A "); + else + sprintf(prbuf, "A%s ", vowelstr(weaps[obj->o_which].w_name)); + + pb = &prbuf[strlen(prbuf)]; + + if ((obj->o_flags & ISKNOW) && (obj->o_flags & ISZAPPED)) + sprintf(pb, "charged%s ", charge_str(obj,buf)); + + pb = &prbuf[strlen(prbuf)]; + + if (obj->o_flags & CANRETURN) + sprintf(pb, "claimed "); + + pb = &prbuf[strlen(prbuf)]; + + if (obj->o_flags & ISPOISON) + sprintf(pb, "poisoned "); + + pb = &prbuf[strlen(prbuf)]; + + if (obj->o_flags & ISSILVER) + sprintf(pb, "silver "); + + if (obj->o_flags & ISTWOH) + sprintf(pb, "two-handed "); + + pb = &prbuf[strlen(prbuf)]; + + if (obj->o_flags & ISKNOW) + sprintf(pb, "%s %s", num(obj->o_hplus, obj->o_dplus, buf), + weaps[obj->o_which].w_name); + else + sprintf(pb, "%s", weaps[obj->o_which].w_name); + + if (obj->o_count > 1) + strcat(prbuf, "s"); + + break; + + case ARMOR: + if (obj->o_flags & ISKNOW) + sprintf(prbuf, "%s%s %s", + obj->o_flags & CANRETURN ? "claimed " : "", + num(armors[obj->o_which].a_class - obj->o_ac, 0, buf), + armors[obj->o_which].a_name); + else + sprintf(prbuf, "%s%s", + obj->o_flags & CANRETURN ? "claimed " : "", + armors[obj->o_which].a_name); + + break; + + case ARTIFACT: + sprintf(prbuf, "the %s", arts[obj->o_which].ar_name); + + if (obj->o_flags & CANRETURN) + strcat(prbuf, " (claimed)"); + + break; + + case STICK: + sprintf(prbuf, "A %s%s%s ", + obj->o_flags & CANRETURN ? "claimed " : "", + blesscurse(obj->o_flags), ws_type[obj->o_which]); + + pb = &prbuf[strlen(prbuf)]; + + if (know_items[TYP_STICK][obj->o_which]) + sprintf(pb, "of %s%s(%s)", ws_magic[obj->o_which].mi_name, + charge_str(obj,buf), ws_made[obj->o_which]); + else if (guess_items[TYP_STICK][obj->o_which]) + sprintf(pb, "called %s(%s)", guess_items[TYP_STICK][obj->o_which], + ws_made[obj->o_which]); + else + sprintf(&prbuf[2], "%s%s %s", + obj->o_flags & CANRETURN ? "claimed " : "", + ws_made[obj->o_which], + ws_type[obj->o_which]); + + break; + + case RING: + if (know_items[TYP_RING][obj->o_which]) + sprintf(prbuf, "A%s%s ring of %s(%s)", + obj->o_flags & CANRETURN ? " claimed" : "", ring_num(obj,buf), + r_magic[obj->o_which].mi_name, r_stones[obj->o_which]); + else if (guess_items[TYP_RING][obj->o_which]) + sprintf(prbuf, "A %sring called %s(%s)", + obj->o_flags & CANRETURN ? "claimed " : "", + guess_items[TYP_RING][obj->o_which], r_stones[obj->o_which]); + else + sprintf(prbuf, "A%s %s ring", + obj->o_flags & CANRETURN ? "claimed " : + (char *)vowelstr(r_stones[obj->o_which]), + r_stones[obj->o_which]); + break; + + case GOLD: + sprintf(prbuf, "%d gold pieces", obj->o_count); + break; + + default: + debug("Picked up something funny."); + sprintf(prbuf, "Something bizarre %s", unctrl(obj->o_type)); + break; + } + + /* Is it marked? */ + + if (obj->o_mark[0]) + { + pb = &prbuf[strlen(prbuf)]; + sprintf(pb, " <%s>", obj->o_mark); + } + + if (obj == cur_armor) + strcat(prbuf, " (being worn)"); + + if (obj == cur_weapon) + strcat(prbuf, " (weapon in hand)"); + + if (obj == cur_ring[LEFT_1]) + strcat(prbuf, " (on left hand)"); + else if (obj == cur_ring[LEFT_2]) + strcat(prbuf, " (on left hand)"); + else if (obj == cur_ring[LEFT_3]) + strcat(prbuf, " (on left hand)"); + else if (obj == cur_ring[LEFT_4]) + strcat(prbuf, " (on left hand)"); + else if (obj == cur_ring[LEFT_5]) + strcat(prbuf, " (on left hand)"); + else if (obj == cur_ring[RIGHT_1]) + strcat(prbuf, " (on right hand)"); + else if (obj == cur_ring[RIGHT_2]) + strcat(prbuf, " (on right hand)"); + else if (obj == cur_ring[RIGHT_3]) + strcat(prbuf, " (on right hand)"); + else if (obj == cur_ring[RIGHT_4]) + strcat(prbuf, " (on right hand)"); + else if (obj == cur_ring[RIGHT_5]) + strcat(prbuf, " (on right hand)"); + + if (obj->o_flags & ISPROT) + strcat(prbuf, " [protected]"); + + if (lowercase && isupper(prbuf[0])) + prbuf[0] = (char) tolower(prbuf[0]); + else if (!lowercase && islower(*prbuf)) + *prbuf = (char) toupper(*prbuf); + + if (!lowercase) + strcat(prbuf, "."); + + return(prbuf); +} + +/* + rem_obj() + Remove an object from the level. +*/ + +void +rem_obj(struct linked_list *item, int dis) +{ + struct linked_list *llptr; + struct object *objptr, *op; + int x, y; + + if (item == NULL) + return; + + detach(lvl_obj, item); + objptr = OBJPTR(item); + + if ( (llptr = objptr->next_obj) != NULL ) + { + detach((objptr->next_obj), llptr); + attach(lvl_obj, llptr); + + op = OBJPTR(llptr); + + op->next_obj = objptr->next_obj; + + if (op->next_obj) + objptr->next_obj->l_prev = NULL; + + y = op->o_pos.y; + x = op->o_pos.x; + + if (cansee(y, x)) + mvwaddch(cw, y, x, op->o_type); + + mvaddch(y, x, op->o_type); + } + else + { + y = objptr->o_pos.y; + x = objptr->o_pos.x; + + /* problems if dropped in rock */ + + mvaddch(y,x,(roomin((objptr->o_pos)) == NULL ? PASSAGE : FLOOR)); + } + + if (dis) + discard(item); +} + +/* + add_obj() + adds an object to the level +*/ + +void +add_obj(struct linked_list *item, int y, int x) +{ + struct linked_list *llptr; + struct object*objptr; + + llptr = find_obj(y, x); + + if (llptr) + { + objptr = OBJPTR(llptr); + attach((objptr->next_obj), item); + } + else + { + attach(lvl_obj, item); + objptr = OBJPTR(item); + objptr->next_obj = NULL; + objptr->o_pos.y = y; + objptr->o_pos.x = x; + mvaddch(y, x, objptr->o_type); + } +} + +/* + drop() + put something down +*/ + +int +drop(struct linked_list *item) +{ + char ch = CCHAR( mvwinch(stdscr, hero.y, hero.x) ); + struct object *obj; + + switch (ch) + { + case GOLD: + case POTION: + case SCROLL: + case FOOD: + case WEAPON: + case ARMOR: + case RING: + case STICK: + case FLOOR: + case PASSAGE: + case POOL: + case ARTIFACT: + if (item == NULL && (item = get_item("drop", 0)) == NULL) + return(FALSE); + break; + + default: + msg("You can't drop something here."); + return(FALSE); + } + + obj = OBJPTR(item); + + if (!dropcheck(obj)) + return(FALSE); + + /* Curse a dropped scare monster scroll */ + + if (obj->o_type == SCROLL && obj->o_which == S_SCARE) + { + if (obj->o_flags & ISBLESSED) + obj->o_flags &= ~ISBLESSED; + else + obj->o_flags |= ISCURSED; + } + + /* Take it out of the pack */ + + if (obj->o_count >= 2 && obj->o_group == 0) + { + struct linked_list *nitem = new_item(sizeof *obj); + + obj->o_count--; + obj = OBJPTR(nitem); + *obj = *(OBJPTR(item)); + obj->o_count = 1; + item = nitem; + } + else + rem_pack(obj); + + if (ch == POOL) + { + msg("The pool bubbles briefly as your %s sinks out of sight.", + inv_name(obj, TRUE)); + discard(item); + item = NULL; + } + else /* Link it into the level object list */ + { + add_obj(item, hero.y, hero.x); + mvaddch(hero.y, hero.x, obj->o_type); + } + + if (obj->o_type == ARTIFACT) + has_artifact &= ~(1 << obj->o_which); + + updpack(); + return(TRUE); +} + +/* + dropcheck() + do special checks for dropping or unweilding|unwearing|unringing +*/ + +int +dropcheck(struct object *op) +{ + if (op == NULL) + return(TRUE); + + if (op != cur_armor && op != cur_weapon && + op != cur_ring[LEFT_1] && op != cur_ring[LEFT_2] && + op != cur_ring[LEFT_3] && op != cur_ring[LEFT_4] && + op != cur_ring[LEFT_5] && + op != cur_ring[RIGHT_1] && op != cur_ring[RIGHT_2] && + op != cur_ring[RIGHT_3] && op != cur_ring[RIGHT_4] && + op != cur_ring[RIGHT_5]) + return(TRUE); + + if (op->o_flags & ISCURSED) + { + msg("You can't. It appears to be cursed."); + return(FALSE); + } + + if (op == cur_weapon) + cur_weapon = NULL; + else if (op == cur_armor) + { + waste_time(); + cur_armor = NULL; + } + else if (op == cur_ring[LEFT_1] || op == cur_ring[LEFT_2] || + op == cur_ring[LEFT_3] || op == cur_ring[LEFT_4] || + op == cur_ring[LEFT_5] || + op == cur_ring[RIGHT_1] || op == cur_ring[RIGHT_2] || + op == cur_ring[RIGHT_3] || op == cur_ring[RIGHT_4] || + op == cur_ring[RIGHT_5]) + { + if (op == cur_ring[LEFT_1]) + cur_ring[LEFT_1] = NULL; + else if (op == cur_ring[LEFT_2]) + cur_ring[LEFT_2] = NULL; + else if (op == cur_ring[LEFT_3]) + cur_ring[LEFT_3] = NULL; + else if (op == cur_ring[LEFT_4]) + cur_ring[LEFT_4] = NULL; + else if (op == cur_ring[LEFT_5]) + cur_ring[LEFT_5] = NULL; + else if (op == cur_ring[RIGHT_1]) + cur_ring[RIGHT_1] = NULL; + else if (op == cur_ring[RIGHT_2]) + cur_ring[RIGHT_2] = NULL; + else if (op == cur_ring[RIGHT_3]) + cur_ring[RIGHT_3] = NULL; + else if (op == cur_ring[RIGHT_4]) + cur_ring[RIGHT_4] = NULL; + else if (op == cur_ring[RIGHT_5]) + cur_ring[RIGHT_5] = NULL; + + switch (op->o_which) + { + case R_ADDSTR: + chg_str(-op->o_ac, FALSE, FALSE); + break; + case R_ADDHIT: + chg_dext(-op->o_ac, FALSE, FALSE); + break; + case R_ADDINTEL: + pstats.s_intel -= op->o_ac; + break; + case R_ADDWISDOM: + pstats.s_wisdom -= op->o_ac; + break; + + case R_SEEINVIS: + if (find_slot(FUSE_UNSEE,FUSE) == NULL) + { + turn_off(player, CANSEE); + msg("The tingling feeling leaves your eyes."); + } + + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + break; + + case R_LIGHT: + if (roomin(hero) != NULL) + { + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + break; + } + } + return(TRUE); +} + +/* + new_thing() + return a new thing +*/ + +struct linked_list * +new_thing(void) +{ + int j, k; + struct linked_list *item; + struct object *cur; + short blesschance = srnd(100); + short cursechance = srnd(100); + + item = new_item(sizeof *cur); + cur = OBJPTR(item); + cur->o_hplus = cur->o_dplus = 0; + cur->o_damage = cur->o_hurldmg = "0d0"; + cur->o_ac = 11; + cur->o_count = 1; + cur->o_group = 0; + cur->o_flags = 0; + cur->o_weight = 0; + cur->o_mark[0] = '\0'; + + /* + * Decide what kind of object it will be If we haven't had food for a + * while, let it be food. + */ + + switch (no_food > 3 ? TYP_FOOD : pick_one(things, numthings)) + { + case TYP_POTION: + cur->o_type = POTION; + cur->o_which = pick_one(p_magic, maxpotions); + cur->o_weight = things[TYP_POTION].mi_wght; + + if (cursechance < p_magic[cur->o_which].mi_curse) + cur->o_flags |= ISCURSED; + else if (blesschance < p_magic[cur->o_which].mi_bless) + cur->o_flags |= ISBLESSED; + break; + + case TYP_SCROLL: + cur->o_type = SCROLL; + cur->o_which = pick_one(s_magic, maxscrolls); + cur->o_weight = things[TYP_SCROLL].mi_wght; + + if (cursechance < s_magic[cur->o_which].mi_curse) + cur->o_flags |= ISCURSED; + else if (blesschance < s_magic[cur->o_which].mi_bless) + cur->o_flags |= ISBLESSED; + break; + + case TYP_FOOD: + no_food = 0; + cur->o_type = FOOD; + cur->o_which = pick_one(fd_data, maxfoods); + cur->o_weight = 2; + cur->o_count += extras(); + break; + + case TYP_WEAPON: + cur->o_type = WEAPON; + cur->o_which = rnd(maxweapons); + init_weapon(cur, cur->o_which); + + if (cursechance < 10) + { + short bad = (rnd(10) < 1) ? 2 : 1; + + cur->o_flags |= ISCURSED; + cur->o_hplus -= bad; + cur->o_dplus -= bad; + } + else if (blesschance < 15) + { + short good = (rnd(10) < 1) ? 2 : 1; + + cur->o_hplus += good; + cur->o_dplus += good; + } + break; + + case TYP_ARMOR: + cur->o_type = ARMOR; + + for (j = 0; j < maxarmors; j++) + if (blesschance < armors[j].a_prob) + break; + + if (j == maxarmors) + { + debug("Picked a bad armor %d", blesschance); + j = 0; + } + + cur->o_which = j; + cur->o_ac = armors[j].a_class; + + if (((k = rnd(100)) < 20) && j != MITHRIL) + { + cur->o_flags |= ISCURSED; + cur->o_ac += rnd(3) + 1; + } + else if (k < 28 || j == MITHRIL) + cur->o_ac -= rnd(3) + 1; + + if (j == MITHRIL) + cur->o_flags |= ISPROT; + + cur->o_weight = armors[j].a_wght; + + break; + + case TYP_RING: + cur->o_type = RING; + cur->o_which = pick_one(r_magic, maxrings); + cur->o_weight = things[TYP_RING].mi_wght; + + if (cursechance < r_magic[cur->o_which].mi_curse) + cur->o_flags |= ISCURSED; + else if (blesschance < r_magic[cur->o_which].mi_bless) + cur->o_flags |= ISBLESSED; + + switch (cur->o_which) + { + case R_ADDSTR: + case R_ADDWISDOM: + case R_ADDINTEL: + case R_PROTECT: + case R_ADDHIT: + case R_ADDDAM: + case R_CARRYING: + cur->o_ac = rnd(2) + 1; /* From 1 to 3 */ + + if (cur->o_flags & ISCURSED) + cur->o_ac = -cur->o_ac; + + if (cur->o_flags & ISBLESSED) + cur->o_ac++; + + break; + + case R_RESURRECT: + case R_TELCONTROL: + case R_VREGEN: + case R_REGEN: + case R_PIETY: + case R_WIZARD: + cur->o_ac = 0; + + if (cur->o_flags & ISCURSED) + cur->o_ac = -1; + + if (cur->o_flags & ISBLESSED) + cur->o_ac = 1; + + break; + + case R_DIGEST: + + if (cur->o_flags & ISCURSED) + cur->o_ac = -1; + else if (cur->o_flags & ISBLESSED) + cur->o_ac = 2; + else + cur->o_ac = 1; + break; + + default: + cur->o_ac = 0; + break; + } + break; + + case TYP_STICK: + cur->o_type = STICK; + cur->o_which = pick_one(ws_magic, maxsticks); + fix_stick(cur); + + if (cursechance < ws_magic[cur->o_which].mi_curse) + cur->o_flags |= ISCURSED; + else if (blesschance < ws_magic[cur->o_which].mi_bless) + cur->o_flags |= ISBLESSED; + + break; + + default: + debug("Picked a bad kind of object"); + wait_for(' '); + } + + return(item); +} + +/* + spec_item() + provide a new item tailored to specification +*/ + +struct linked_list * +spec_item(char type, int which, int hitp, int damage) +{ + struct linked_list *item; + struct object *obj; + + item = new_item(sizeof *obj); + obj = OBJPTR(item); + + obj->o_count = 1; + obj->o_group = 0; + obj->o_type = type; + obj->o_which = which; + obj->o_damage = obj->o_hurldmg = "0d0"; + obj->o_hplus = 0; + obj->o_dplus = 0; + obj->o_flags = 0; + obj->o_mark[0] = '\0'; + obj->o_text = NULL; + obj->o_launch = 0; + obj->o_weight = 0; + + switch(type) /* Handle special characteristics */ + { + case WEAPON: + init_weapon(obj, which); + obj->o_hplus = hitp; + obj->o_dplus = damage; + obj->o_ac = 10; + break; + + case ARMOR: + obj->o_ac = armors[which].a_class - hitp; + break; + + case RING: + obj->o_ac = hitp; + break; + + case STICK: + fix_stick(obj); + obj->o_charges = hitp; + break; + + case GOLD: + obj->o_type = GOLD; + obj->o_count = GOLDCALC; + obj->o_ac = 11; + break; + } + + if (hitp > 0 || damage > 0) + obj->o_flags |= ISBLESSED; + else if (hitp < 0 || damage < 0) + obj->o_flags |= ISCURSED; + + return(item); +} + +/* + pick_one() + pick an item out of a list of nitems possible magic items +*/ + +int +pick_one(struct magic_item *magic, int nitems) +{ + int i; + struct magic_item *end; + struct magic_item *start = magic; + + for (end = &magic[nitems], i = rnd(1000); magic < end; magic++) + if (i <= magic->mi_prob) + break; + + if (magic == end) + { + if (wizard) + { + add_line("Bad pick_one: %d from %d items", i, nitems); + + for (magic = start; magic < end; magic++) + add_line("%s: %d%%", magic->mi_name, magic->mi_prob); + + end_line(); + } + magic = start; + } + + return((int)(magic - start)); +} + + +/* + blesscurse() + returns whether the object is blessed or cursed +*/ + +char * +blesscurse(long flags) +{ + if (flags & ISKNOW) + { + if (flags & ISCURSED) + return("cursed "); + + if (flags & ISBLESSED) + return("blessed "); + + return("normal "); + } + + return(""); +} + +/* + extras() + Return the number of extra food items to be created +*/ + +int +extras(void) +{ + int i = rnd(100); + + if (i < 10) + return(2); + else if (i < 20) + return(1); + else + return(0); +} + +/* + name_type() + Returns a string identifying a pack item's type +*/ + +char * +name_type(int type) +{ + switch(type) + { + case ARMOR: + return ("armor"); + + case WEAPON: + return ("weapons"); + + case SCROLL: + return ("scrolls"); + + case RING: + return ("rings"); + + case STICK: + return ("staffs"); + + case POTION: + return ("potions"); + + case FOOD: + return ("food"); + + case GOLD: + return ("gold"); + + default: + return ("items"); + } +} + +/* + make_item() + adds a linked_list structure to the top of an object for interfacing + with other routines +*/ + +linked_list * +make_item(object *obj_p) +{ + linked_list *item_p = new_list(); + + item_p->l_next = item_p->l_prev = NULL; + + item_p->data.obj = obj_p; + + return(item_p); +} + +/* + is_member() + Checks to see if a character is a member of an array +*/ + +int +is_member(char *list_p, int member) +{ + while (*list_p != 0) + { + if (*list_p++ == member) + return(TRUE); + } + + return(FALSE); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/trader.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/trader.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,808 @@ +/* + trader.c - Anything to do with trading posts + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include +#include "rogue.h" + +#define EFFECTIVE_PURSE ((player.t_ctype==C_PALADIN)?(9 * purse / 10) : purse) + +/* + do_post() + Buy and sell things in a trading post +*/ + +void +display_postinfo(void) +{ + wclear(hw); + wstandout(hw); + mvwaddstr(hw, 0, COLS / 2 - 30, + "Welcome to Friendly Fiend's Flea Market" ); + wstandend(hw); + wclrtoeol(hw); + trans_line(); +} + +void +do_post(void) +{ + int bad_letter = FALSE; + + player.t_trans = 0; + + for (;;) + { + if (!open_market()) + return; + + display_postinfo(); + + if (bad_letter) + { + bad_letter = FALSE; + wstandout(hw); + mvwaddstr(hw, 7, 0, "Type 'i' or 'I' to inventory, " + "'l' to leave, 'b' to buy, or 's' to sell"); + wstandend(hw); + } + + mvwaddstr(hw, 6, 0, "Do you wish to buy, sell, inventory, or leave?"); + wrefresh(hw); + + switch(readcharw(hw)) + { + case 'b': + mvwaddstr(hw, 7, 0, + "Lets go into the buying section of the store..."); + touchwin(hw); + wrefresh(hw); + buy_it('\0', ISNORMAL); + break; + + case 's': + mvwaddstr(hw, 7, 0, + "Lets go into the selling section of the store..."); + touchwin(hw); + wrefresh(hw); + sell_it(); + break; + + case 'i': + inventory(pack, '*'); + break; + + case 'I': + wclear(hw); + wrefresh(hw); + inventory(pack, 0); + msg(" "); + msg(""); + break; + + case 'l': + wclear(hw); + wrefresh(hw); + return; + + default: + bad_letter = TRUE; + break; + } + } +} + +void +buy_it(char itemtype, int flags) +{ + int i; + int blessed = flags & ISBLESSED; + int cursed = flags & ISCURSED; + int is_spell = flags & SCR_MAGIC; + int array_size; /* # of items within type */ + int which_type = 0; /* Which type to buy */ + int which_one; /* Which one within type */ + int plus_or_minus = 0; /* for magic items */ + const struct magic_item *magic_array = NULL; + struct linked_list *item; + struct object *obj; + char buf[2 * LINELEN]; + +buy_more: + + display_postinfo(); + + do + { + array_size = 0; + + if (itemtype == '\0') + { + mpos = 0; + wmove(hw,10,0); + wclrtoeol(hw); + mvwaddstr(hw, 11, 0, "WHAT\tTYPE\n! Potion\n? Scroll\n" + "= Ring\n/ Stick\n] Armor\n) Weapon\n: Food"); + + if (wizard) + mvwaddstr(hw, 19, 0, ", Artifact"); + + mvwaddstr(hw, 9, 0, "What type of item do you want? "); + wclrtoeol(hw); + touchwin(hw); + wrefresh(hw); + itemtype = readcharw(hw); + } + + switch(itemtype) + { + case POTION: + which_type = TYP_POTION; + array_size = maxpotions; + magic_array = p_magic; + break; + + case SCROLL: + which_type = TYP_SCROLL; + array_size = maxscrolls; + magic_array = s_magic; + break; + + case FOOD: + which_type = TYP_FOOD; + array_size = maxfoods; + magic_array = fd_data; + break; + + case WEAPON: + which_type = TYP_WEAPON; + array_size = maxweapons; + break; + + case ARMOR: + which_type = TYP_ARMOR; + array_size = maxarmors; + break; + + case RING: + which_type = TYP_RING; + array_size = maxrings; + magic_array = r_magic; + break; + + case STICK: + which_type = TYP_STICK; + array_size = maxsticks; + magic_array = ws_magic; + break; + + case ARTIFACT: + if (!wizard) + { + itemtype = '\0'; + continue; + } + + which_type = TYP_ARTIFACT; + array_size = maxartifact; + break; + + case ESCAPE: + return; + + default: + wstandout(hw); + mvwaddstr(hw, 10, 0, "We don't stock any of those."); + wstandend(hw); + itemtype = '\0'; + continue; + } + } + while (array_size == 0); + + which_one = array_size; + + do + { + const struct magic_item *m_item; + + display_postinfo(); + + mpos = 0; + sprintf(buf, "Which kind of %s do you wish to have (* for list)? ", + things[which_type].mi_name); + + mvwaddstr(hw, 9, 0, buf); + + touchwin(hw); + wrefresh(hw); + buf[0] = '\0'; + + switch (get_string(buf, hw)) + { + case QUIT: + case ESCAPE: + itemtype = '\0'; + goto buy_more; + } + + if (buf[0] == '*') /* print list */ + { + add_line(" ID BASECOST NAME"); + + switch (which_type) + { + case TYP_RING: + case TYP_POTION: + case TYP_STICK: + case TYP_SCROLL: + case TYP_FOOD: + + for(i=0,m_item=magic_array; i < array_size; i++, m_item++) + if (!is_spell && m_item->mi_worth > 0) + { + sprintf(buf, "%3d) %8d %s", i, m_item->mi_worth, + m_item->mi_name); + add_line(buf); + } + break; + + case TYP_ARMOR: + for (i = 0; i < array_size; i++) + if (!is_spell && armors[i].a_worth > 0) + { + sprintf(buf, "%3d) %8d %s", i, armors[i].a_worth, + armors[i].a_name); + + add_line(buf); + } + break; + + case TYP_WEAPON: + for (i = 0; i < array_size; i++) + if (!is_spell && weaps[i].w_worth > 0) + { + sprintf(buf, "%3d) %8d %s", i, weaps[i].w_worth, + weaps[i].w_name); + add_line(buf); + } + break; + + case TYP_ARTIFACT: + for (i = 0; i < array_size; i++) + { + sprintf(buf, "%3d) %8d %s", i, arts[i].ar_worth, + arts[i].ar_name); + add_line(buf); + } + break; + + default: + add_line("What a strange type."); + } + + end_line(); + touchwin(hw); + wrefresh(hw); + continue; + } + + if (isdigit(buf[0])) + which_one = atoi(buf); + else + switch (which_type) + { + case TYP_RING: + case TYP_POTION: + case TYP_STICK: + case TYP_SCROLL: + case TYP_FOOD: + for (i=0,m_item=magic_array; i < array_size; i++, m_item++) + if (strcmp(buf, m_item->mi_name) == 0) + which_one = i; + break; + + case TYP_ARMOR: + for (i = 0; i < array_size; i++) + if (strcmp(buf, armors[i].a_name) == 0) + which_one = i; + break; + + case TYP_WEAPON: + for (i = 0; i < array_size; i++) + if (strcmp(buf, weaps[i].w_name) == 0) + which_one = i; + break; + + case TYP_ARTIFACT: + for (i = 0; i < array_size; i++) + if (strcmp(buf, arts[i].ar_name) == 0) + which_one = i; + break; + + default: + msg("What a strange type."); + } + + if (which_one < 0 || which_one >= array_size) + { + wstandout(hw); + mvwaddstr(hw, 10, 0, "Type the name or an ID number."); + wstandend(hw); + } + } + while (which_one < 0 || which_one >= array_size); + + item = new_item(sizeof *obj); + obj = OBJPTR(item); + + if (which_type == TYP_ARTIFACT) + { + new_artifact(which_one, obj); + add_pack(item, NOMESSAGE); + itemtype = '\0'; + goto buy_more; + } + + obj->o_type = itemtype; + obj->o_which = which_one; + obj->o_mark[0] = '\0'; + obj->o_group = 0; + obj->o_count = 1; + obj->o_weight = 0; + obj->o_dplus = obj->o_hplus = 0; + obj->o_worth = 0; + + if (!is_spell) + { + plus_or_minus = -100; + + do + { + mvwaddstr(hw, 10, 0, "Do you want the cursed, blessed, or normal" + " version? (c, b, n) [n]"); + touchwin(hw); + wrefresh(hw); + + blessed = cursed = FALSE; + switch (readcharw(hw)) + { + case ESCAPE: + discard(item); + itemtype = '\0'; + goto buy_more; + + case 'c': + cursed = TRUE; + plus_or_minus = 0; + break; + + case 'b': + blessed = TRUE; + plus_or_minus = 0; + break; + + case 'n': + case ' ': + plus_or_minus = 0; + break; + + default: + wstandout(hw); + mvwaddstr(hw,11,0,"Type 'c' for cursed, 'b' for blessed, " + "or 'n' for normal"); + wstandend(hw); + } + } + while (plus_or_minus == -100); + } + + /* else used blessed, cursed from flags parameter */ + + if (which_type == TYP_WEAPON) + init_weapon(obj, which_one); + + obj->o_flags |= ISKNOW; + + if (cursed) + { + plus_or_minus = -(rnd(2) + 1); + obj->o_flags |= ISCURSED; + } + else if (blessed) + { + plus_or_minus = (rnd(3) + 1); + obj->o_flags |= ISBLESSED; + } + else + { + plus_or_minus = 0; + obj->o_flags |= ISNORMAL; + } + + switch (which_type) + { + case TYP_WEAPON: + obj->o_hplus += plus_or_minus; + obj->o_dplus += plus_or_minus; + break; + + case TYP_ARMOR: + obj->o_weight = armors[which_one].a_wght; + obj->o_ac = armors[which_one].a_class - plus_or_minus; + break; + + case TYP_STICK: + fix_stick(obj); + break; + + case TYP_RING: + obj->o_ac = plus_or_minus; + break; + + case TYP_SCROLL: + case TYP_POTION: + obj->o_weight = things[which_type].mi_wght; + break; + + case TYP_FOOD: + break; + + default: + msg("That's a strange thing to try to own."); + discard(item); + itemtype = '\0'; + goto buy_more; + } + + obj->o_worth = get_worth(obj) * (luck + level / 15 + 1); + describe_it(obj); + + if (!wizard && obj->o_worth > EFFECTIVE_PURSE) + { + wstandout(hw); + mvwaddstr(hw, 12, 0, "Unfortunately, you can't afford it."); + wstandend(hw); + wclrtoeol(hw); + touchwin(hw); + wrefresh(hw); + wait_for(' '); + discard(item); + itemtype = '\0'; + goto buy_more; + } + + mvwaddstr(hw, 12, 0, "Do you want it? [y] "); + wclrtoeol(hw); + touchwin(hw); + wrefresh(hw); + + switch (readcharw(hw)) + { + case ESCAPE: + case 'n': + msg(""); + discard(item); + itemtype = '\0'; + goto buy_more; + } + + /* The hero bought the item here */ + + mpos = 0; + + if (add_pack(item, NOMESSAGE) && !is_spell) + { + if (!wizard) + { + purse -= obj->o_worth; /* take his money */ + ++player.t_trans; + } + + trans_line(); /* show remaining deals */ + + switch(which_type) + { + case TYP_RING: + case TYP_STICK: + case TYP_SCROLL: + case TYP_POTION: + know_items[which_type][which_one] = TRUE; + } + } +} + +/* + sell_it() + Sell an item to the trading post +*/ + +void +sell_it(void) +{ + struct object *obj; + struct linked_list *item; + char buf[2 * LINELEN]; + + wclear(cw); + + if ((item = get_item("sell", 0)) == NULL) + return; + + obj = OBJPTR(item); + msg(""); + display_postinfo(); + touchwin(hw); + wrefresh(hw); + + if ((obj->o_type == ARTIFACT) || (obj->o_worth = get_worth(obj)) == 0) + { + mpos = 0; + msg("We don't buy those."); + + if (is_wearing(R_ADORNMENT) && rnd(10) < 4) + msg("How about that %s ring instead?", r_stones[R_ADORNMENT]); + + return; + } + + describe_it(obj); + mvwaddstr(hw, 12, 0, "Do you want to sell it? [n] "); + touchwin(hw); + wrefresh(hw); + + switch( readcharw(hw) ) + { + case 'y': + break; + default: + msg(""); + if (is_wearing(R_ADORNMENT)) + msg("How about that %s ring instead?", + r_stones[R_ADORNMENT]); + return; + } + + rem_pack(obj); + purse += obj->o_worth; /* give him his money */ + ++player.t_trans; + + sprintf(buf, "Sold %s. Hit space to continue.", + inv_name(obj, LOWERCASE)); + discard(item); + + mvwaddstr(hw, 13, 0, buf); + touchwin(hw); + wrefresh(hw); + wait_for(' '); +} + +/* + describe_it() + Laud or condemn the object +*/ + +extern char *inv_name(); + +void +describe_it(struct object *obj) +{ + static char *cursed_d[] = + { + "worthless hunk of junk", + "shoddy piece of trash", + "piece of rusty garbage", + "example of terrible workmanship", + "cheap hack" + }; + + static char *normal_d[] = + { + "journeyman's piece", + "fine deal", + "great bargain", + "good find", + "real value", + "piece of honest workmanship", + "steal", + "purchase worth making", + "inexpensive product" + }; + + static char *blessed_d[] = + { + "magnificant masterpiece", + "quality product", + "exceptional find", + "unbeatable value", + "rare beauty", + "superior product", + "well-crafted item" + }; + + char *charp; + char buf[2 * LINELEN]; + + if (obj->o_flags & ISBLESSED) + charp = blessed_d[rnd(sizeof(blessed_d) / sizeof(char *))]; + else if (obj->o_flags & ISCURSED) + charp = cursed_d[rnd(sizeof(cursed_d) / sizeof(char *))]; + else + charp = normal_d[rnd(sizeof(normal_d) / sizeof(char *))]; + + sprintf(buf, "It's a%s %s worth %d pieces of gold.", + vowelstr(charp), charp, obj->o_worth); + + mvwaddstr(hw, 10, 0, inv_name(obj, TRUE)); + mvwaddstr(hw, 11, 0, buf); + wclrtoeol(hw); +} + +/* + open_market() + Retruns TRUE when ok do to transacting +*/ + +int +open_market(void) +{ + int maxtrans = is_wearing(R_ADORNMENT) ? MAXPURCH + 4 : MAXPURCH; + + if (wizard || player.t_trans < maxtrans || (level == 0)) + return(TRUE); + else + { + msg("The market is closed. The stairs are that-a-way."); + return(FALSE); + } +} + +/* + get_worth() + Calculate an objects worth in gold +*/ + +int +get_worth(struct object *obj) +{ + long worth = 0; + int wh = obj->o_which; + int blessed = obj->o_flags & ISBLESSED; + int cursed = obj->o_flags & ISCURSED; + + switch (obj->o_type) + { + case FOOD: + if (wh < maxfoods) + { + worth = obj->o_count * fd_data[wh].mi_worth; + if (blessed) + worth *= 2; + } + break; + + case WEAPON: + if (wh < maxweapons) + { + worth = weaps[wh].w_worth; + worth *= obj->o_count * (2 + + (4 * obj->o_hplus + + 4 * obj->o_dplus)); + + if (obj->o_flags & ISSILVER) + worth *= 2; + + if (obj->o_flags & ISPOISON) + worth *= 2; + + if (obj->o_flags & ISZAPPED) + worth += 20 * obj->o_charges; + } + break; + + case ARMOR: + if (wh < maxarmors) + { + int plusses = armors[wh].a_class - obj->o_ac; + + worth = armors[wh].a_worth; + + if (plusses > 0) + worth *= (1 + (10 * + (armors[wh].a_class - obj->o_ac))); + } + break; + + case SCROLL: + if (wh < maxscrolls) + worth = s_magic[wh].mi_worth; + break; + + case POTION: + if (wh < maxpotions) + worth = p_magic[wh].mi_worth; + break; + + case RING: + if (wh < maxrings) + { + worth = r_magic[wh].mi_worth; + worth += obj->o_ac * 40; + } + break; + + case STICK: + if (wh < maxsticks) + { + worth = ws_magic[wh].mi_worth; + worth += 20 * obj->o_charges; + } + break; + + case ARTIFACT: + if (wh < maxartifact) + worth = arts[wh].ar_worth; + break; + + default: + worth = 0; + } + + if (obj->o_flags & ISPROT) /* 300% more for protected */ + worth *= 3; + + if (blessed) /* 250% more for blessed */ + worth = 5 * worth / 2; + else if (cursed) /* half for cursed */ + worth /= 2; + + if (obj->o_flags & (CANRETURN | ISOWNED)) + worth *= 4; + else if (obj->o_flags & CANRETURN) + worth *= 2; + else if (obj->o_flags & ISLOST) + worth /= 3; + + return(max(0, worth)); /* anything is worth at least one gold piece */ +} + +/* + trans_line() + Show how many transactions the hero has left +*/ + +void +trans_line(void) +{ + char buf[2 * LINELEN]; + int adorned = is_wearing(R_ADORNMENT); + + if (level == 0 && purse > 0) + sprintf(buf, "You still have %d pieces of gold left.", purse); + else if (purse == 0) + sprintf(buf, "You have no money left."); + else if (!wizard) + sprintf(buf, "You have %d transactions and %d gold pieces remaining.", + max(0, (adorned ? MAXPURCH + 4 : MAXPURCH) - player.t_trans), + EFFECTIVE_PURSE); + else + sprintf(buf, "You have infinite transactions remaining."); + + mvwaddstr(hw, LINES - 2, 0, buf); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/urogue.6 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/urogue.6 Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,288 @@ +.\" +.\" Last Modified: 06/16/86 +.\" +.TH UROGUE 6 "1.03 Draft" +.SH NAME +urogue \- UltraRogue \- Exploring the dungeons of doom. +.SH SYNTAX +.B urogue +[ +.I save-file +] +[ +.B \-k +] +[ +.B \-s +] +[ +.B \-n +] +[ +.B \-v +] +[ +.B \-r +] +.SH DESCRIPTION +.PP +.I UltraRogue +is a advanced version of one of the more standard rogue games. +It is much more difficult and interesting to play than the +original version on which it is based (Advanced Rogue 2.0). +Some of it's unique features include 10 player character classes, +several hundred monsters, charmable monsters, etc. +.PP +To win the game +you must locate 8 artifacts which start appearing from level 25 +down. You must then bring all artifacts back up out of the dungeon. +These artifacts also may be +.I applied +to use some of their great powers! Be warned, though, sometimes these +powers back-fire on you. +.SH ENVIRONMENT +The environment variable +.B $UROGUE +(the person installing +.I UltraRogue +may have changed this for your site) +is checked to set certain default options for +.I UltraRogue. +The following may be set in the +.B $UROGUE: +.IP "\fBterse\fR (\fIboolean\fR)" +Short output messages. +.IP "\fBflush\fR (\fIboolean\fR)" +Flush typeahead when fighting. +.IP "\fBjump\fR (\fIboolean\fR)" +Show position only at the end of running. +.IP "\fBinven\fR (\fIstring\fR)" +Inventory style. +.IP "\fBaskme\fR (\fIboolean\fR)" +Ask about unidentified things. +.IP "\fBstopdoor\fR (\fIboolean\fR)" +Stop running when next to something interesting. +.IP "\fBname\fR (\fIstring\fR)" +The rogue's name. +.IP "\fBfruit\fR (\fIstring\fR)" +The funny fruit used by the rogue. +.IP "\fBfile\fR (\fIstring\fR)" +Default savefile name. +.IP "\fBscore\fR (\fIstring\fR)" +Default scorefile name. +.IP "\fBclass\fR (\fIstring\fR)" +Default player class. +.sp +.PP +Something like +.nf +.sp +.in +.5in +setenv UROGUE "name=VAX Killer,fruit=Peach,noterse,jump" +.in -.5in +.sp +.fi +does the obvious. The ``score'' option is ignored unless you start +urogue in wizard mode. The class option string can have the following +values: ``fighter'', ``illus'', ``paladin'', ``ranger'', ``cleric'', ``magic'', +``assasin'', ``druid'', ``ninja'', and ``thief'' and is initialized only at +startup time. The ``inven'' option can take the values ``slow'', ``clear'', +and ``overwrite''. +.PP +You may change most of these values while playing with the +.B ``o'' +command. +.SH OPTIONS +.PP +If a +.I save_file +is specified, +urogue will be restored from the specified saved game file. +If the save file is named ``-r'', the default saved game file of +.I ~/rogue.save +is restored. +The other options are: +.IP \-k +Place the terminal's kaypad into +.I auxilary +or +.I applications +mode. Once this is done, the keypad may be used for movement instead +of the normal letter movement keys. +The directions are ``1'' move left and down, ``2'' move down, ``3'' move +right and down, ``6'' move right, ``9'' move right and up, ``8'' move up, +``7'' move up, and ``4'' move left. If the user hits the key +(key ``5'') and then a direction, then the rogue will run in the specified +direction. +When the game ends the keypad is restored +to normal +.I numeric +mode if \-k was specified. The user may also place the keypad into +.I auxilary +or +.I applications +mode manually and then may use the keypad without specifing the +\-k option. +.IP \-s +Display the list of scores. +.IP \-n +Display the urogue news file. +This is a file containing information on currently known bugs, +changes, fixes, and enhancements to look for from the last version. +.IP \-v +Display the urogue version information. +.SH COMMANDS +The following is a list of commands for +.I UltraRogue. +You may see a similiar list during a +.I UltraRogue +game by using the +.B ``?'' +command. +A +.I +is one of +\fB``h'', ``j'', ``k'', ``l'', ``y'', ``u'', ``b'', \fRor\fB +``n''.\fR +.IP \fB?\fR +Print out a list of commands. +.IP \fB/\fR +Identify objects. +.IP \fBh\fR +Move left. +.IP \fBj\fR +Move down. +.IP \fBk\fR +Move up. +.IP \fBl\fR +Move right. +.IP \fBy\fR +Move up and left. +.IP \fBu\fR +Move up and right. +.IP \fBb\fR +Move down and left. +.IP \fBn\fR +Move down and right. +.IP \fB\fR +Run in +.I . +.IP \fBm\fR +Move in +.I +without picking anything up. +.IP \fBt\fR +Throw something in +.I . +.IP \fBz\fR +Zap a wand or staff in +.I . +.IP \fB>\fR +Go down a staircase. +.IP \fB<\fR +Go up a staircase. +You must possess at least one Artifact to go up a staircase. +.IP \fBs\fR +Search for a trap/secrect door. +.IP \fB.\fR +Rest (do nothing) for one turn. +.IP \fBi\fR +General inventory. +.IP \fBI\fR +Inventory a single item. +.IP \fBq\fR +Quaff a potion. +.IP \fBr\fR +Read paper. +.IP \fBe\fR +Eat one ration of food. +.IP \fBw\fR +Wield a weapon. +.IP \fBW\fR +Wear armor. +.IP \fBT\fR +Take armor off. +.IP \fBP\fR +Put on a ring. +.IP \fBR\fR +Remove a ring. +.IP \fBA\fR +Apply an Artifact. +.IP \fBd\fR +Drop an object. +.IP \fBc\fR +Call object (generic). +.IP \fBM\fR +Mark object (specific). +.IP \fBo\fR +Examine/set options. +.IP \fBC\fR +Cast a spell/say a prayer. +.IP \fBp\fR +Pray to a deity. +.IP \fBa\fR +Affect the undead. +.IP \fB^\fR +Set a trap. +.IP \fBD\fR +Dip something in a pool. +.IP \fB^T\fR +Take (steal) from +.I . +.IP \fB^R\fR +Redraw screen. +.IP \fB^P\fR +Print last message. +May go up to the last 10 messages. +.IP \fB\fR +Cancel current command. +.IP \fBv\fR +Print +.I UltraRogue +version information. +.IP \fB!\fR +Create a shell. Uses $SHELL if present in your environment. +.IP \fBS\fR +Save the current game. +.IP \fBQ\fR +Quit the current game. +.IP \fB=\fR +Listen for monsters. +.IP \fBf\fR +Fight monster in +.I . +.IP \fBF\fR +Fight monster to death in +.I . +.IP \fB#\fR +Buy the object the rogue is standing on. +Used when in Friendly Fiend's Flea Market. +.IP \fB$\fR +Price the object the rogue is standing on. +Used when in Friendly Fiend's Flea Market. +.IP \fB%\fR +Sell an object from the rogue's pack. +Used when in Friendly Fiend's Flea Market. +.SH FILES +.DT +.ta \w'/usr/games/lib/urogue/scorefile\ \ \ 'u +/usr/games/lib/urogue/scorefile \- Score file +.br +.ta \w'/usr/games/lib/urogue/motd\ \ \ 'u +/usr/games/lib/urogue/motd \- Message of the day +.br +.ta \w'/usr/games/lib/urogue/news\ \ \ 'u +/usr/games/lib/urogue/news \- News file +.br +\fB~\fP/rogue.save \- Default save file +.ST +.SH SEE ALSO +rogue(6), +.br +.I "A Guide to the Dungeons of Doom." +.SH COPYRIGHT +UltraRogue: The Ultimate Adventure in the Dungeons of Doom +.br +Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong +.br +All rights reserved. diff -r d9badb9c0179 -r c495a4f288c6 urogue/urogue.sln --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/urogue.sln Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual C++ Express 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "urogue", "urogue.vcproj", "{A428D711-A9BE-4A4E-9013-3AC25A18502D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A428D711-A9BE-4A4E-9013-3AC25A18502D}.Debug|Win32.ActiveCfg = Debug|Win32 + {A428D711-A9BE-4A4E-9013-3AC25A18502D}.Debug|Win32.Build.0 = Debug|Win32 + {A428D711-A9BE-4A4E-9013-3AC25A18502D}.Release|Win32.ActiveCfg = Release|Win32 + {A428D711-A9BE-4A4E-9013-3AC25A18502D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff -r d9badb9c0179 -r c495a4f288c6 urogue/urogue.vcproj --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/urogue.vcproj Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r d9badb9c0179 -r c495a4f288c6 urogue/verify.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/verify.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,19 @@ +/* + verify.c - exiting functions + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1995 Herb Chong + All rights reserved. +*/ + +static char sccsid[] = "%W% %G%"; + +#include "rogue.h" + +void verify_function(const char *file, const int line) +{ + char s[80]; + + sprintf(s, "Verify failure in %s at line %d\n", file, line); + fatal(s); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/vers.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/vers.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,21 @@ +/* + vers.c - version number + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +const char *save_format = "UltraRogue Portable Save File Release 001\04"; +const char *version = "UltraRogue 1.06a October 1995"; +const char *release = "1.06 Alpha (October 1995)"; diff -r d9badb9c0179 -r c495a4f288c6 urogue/weapons.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/weapons.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,556 @@ +/* + weapons.c - Functions for dealing with problems brought about by weapons + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +/* + missile() + Fire a missile in a given direction +*/ + +void +missile(int ydelta, int xdelta, struct linked_list *item, struct thing *tp) +{ + struct object *obj; + struct linked_list *nitem; + + if (item == NULL) /* Get which thing we are hurling */ + return; + + obj = OBJPTR(item); + + if (!dropcheck(obj) || is_current(obj)) + return; + + /* + * Get rid of the thing. If it is a non-multiple item object, or if + * it is the last thing, just drop it. Otherwise, create a new item + * with a count of one. + */ + + if (obj->o_count < 2) + { + if (tp->t_pack == pack) + rem_pack(obj); + else + detach(tp->t_pack, item); + } + else + { + obj->o_count--; + nitem = (struct linked_list *) new_item(sizeof *obj); + obj = OBJPTR(nitem); + *obj = *(OBJPTR(item)); + obj->o_count = 1; + item = nitem; + } + + switch (obj->o_type) + { + case ARTIFACT: + has_artifact &= ~(1 << obj->o_which); + break; + + case SCROLL: + if (obj->o_which == S_SCARE && obj->o_flags & ISBLESSED) + obj->o_flags &= ~ISBLESSED; + else + obj->o_flags |= ISCURSED; + } + + updpack(); + obj->o_pos = do_motion(obj->o_type, ydelta, xdelta, tp); + + /* + * AHA! Here it has hit something. If it is a wall or a door, or if + * it misses (combat) the monster, put it on the floor + */ + + if (!hit_monster(obj->o_pos.y, obj->o_pos.x, obj, tp)) + { + if (obj->o_type == WEAPON && obj->o_which == GRENADE) + { + hearmsg("BOOOM!"); + aggravate(); + + if (ntraps + 1 < 2 * MAXTRAPS && + fallpos(obj->o_pos, &traps[ntraps].tr_pos)) + { + mvaddch(traps[ntraps].tr_pos.y, traps[ntraps].tr_pos.x, + TRAPDOOR); + traps[ntraps].tr_type = TRAPDOOR; + traps[ntraps].tr_flags = ISFOUND; + traps[ntraps].tr_show = TRAPDOOR; + ntraps++; + light(&hero); + } + discard(item); + } + else if (obj->o_flags & ISLOST) + { + if (obj->o_type == WEAPON) + addmsg("The %s", weaps[obj->o_which].w_name); + else + addmsg(inv_name(obj, LOWERCASE)); + + msg(" vanishes in a puff of greasy smoke."); + discard(item); + } + else + { + fall(&player, item, TRUE, TRUE); + + if (obj->o_flags & CANRETURN) + msg("You have %s.", inv_name(obj, LOWERCASE)); + } + } + else if (obj->o_flags & ISOWNED) + { + add_pack(item, NOMESSAGE); + msg("You have %s.", inv_name(obj, LOWERCASE)); + } + + mvwaddch(cw, hero.y, hero.x, PLAYER); +} + +/* + do_motion() + do the actual motion on the screen done by an object + traveling across the room +*/ + +coord +do_motion(int ob, int ydelta, int xdelta, struct thing *tp) +{ + coord pos; + /* Come fly with us ... */ + + pos = tp->t_pos; + + for (;;) + { + int ch; + + /* Erase the old one */ + + if (!ce(pos, tp->t_pos) && + cansee(pos.y, pos.x) && + mvwinch(cw, pos.y, pos.x) != ' ') + { + mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x)); + } + + /* Get the new position */ + + pos.y += ydelta; + pos.x += xdelta; + + if (shoot_ok(ch = winat(pos.y, pos.x)) && + ch != DOOR && !ce(pos, hero)) + { + /* It hasn't hit anything yet, so display it if it alright. */ + + if (cansee(pos.y, pos.x) && + mvwinch(cw, pos.y, pos.x) != ' ') + { + mvwaddch(cw, pos.y, pos.x, ob); + wrefresh(cw); + } + + continue; + + } + break; + } + + return(pos); +} + +/* + fall() + Drop an item someplace around here. +*/ + +void +fall(struct thing *tp, struct linked_list *item, int pr, int player_owned) +{ + struct object *obj; + struct room *rp; + coord fpos; + + obj = OBJPTR(item); + rp = roomin(tp->t_pos); + + if (player_owned && obj->o_flags & CANRETURN) + { + add_pack(item, NOMESSAGE); + msg("You have %s.", inv_name(obj, LOWERCASE)); + return; + } + else if (fallpos(obj->o_pos, &fpos)) + { + if (obj->o_flags & CANBURN && obj->o_type == WEAPON + && obj->o_which == MOLOTOV + && ntraps + 1 < 2 * MAXTRAPS) + { + mvaddch(fpos.y, fpos.x, FIRETRAP); + traps[ntraps].tr_type = FIRETRAP; + traps[ntraps].tr_flags = ISFOUND; + traps[ntraps].tr_show = FIRETRAP; + traps[ntraps].tr_pos = fpos; + ntraps++; + + if (rp != NULL) + rp->r_flags &= ~ISDARK; + } + else + { + obj->o_pos = fpos; + add_obj(item, fpos.y, fpos.x); + } + + if (rp != NULL && + (!(rp->r_flags & ISDARK) || + (rp->r_flags & HASFIRE))) + { + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + } + return; + } + + /* get here only if there isn't a place to put it */ + + if (pr) + { + if (cansee(obj->o_pos.y, obj->o_pos.x)) + { + if (obj->o_type == WEAPON) + addmsg("The %s", weaps[obj->o_which].w_name); + else + addmsg(inv_name(obj, LOWERCASE)); + + msg(" vanishes as it hits the ground."); + } + } + discard(item); +} + +/* + init_weapon() + Set up the initial goodies for a weapon +*/ + +void +init_weapon(struct object *weap, int type) +{ + struct init_weps *iwp = &weaps[type]; + + weap->o_damage = iwp->w_dam; + weap->o_hurldmg = iwp->w_hrl; + weap->o_launch = iwp->w_launch; + weap->o_flags = iwp->w_flags; + weap->o_weight = iwp->w_wght; + + if (weap->o_flags & ISMANY) + { + weap->o_count = rnd(8) + 8; + weap->o_group = ++group; + } + else + weap->o_count = 1; +} + +/* + hit_monster() + does the missile hit the target? +*/ + +int +hit_monster(int y, int x, struct object *weapon, struct thing *thrower) +{ + struct linked_list *mon; + coord target; + + target.y = y; + target.x = x; + + if (thrower == &player) + return(fight(&target, weapon, THROWN)); + + if (ce(target, hero)) + { + if (good_monster(*thrower)) + { + if (on(*thrower, ISFAMILIAR)) + msg("Please get out of the way, Master! I nearly hit you."); + else + msg("Get out of the way %s!", whoami); + + return(FALSE); + } + + return(attack(thrower, weapon, THROWN)); + } + + if ((mon = find_mons(y, x)) != NULL) + return(mon_mon_attack(thrower, mon, weapon, THROWN)); + else + return(FALSE); +} + + +/* + num() + Figure out the plus number for armor/weapons +*/ + +char * +num(int n1, int n2, char *buf) +{ + if (buf == NULL) + return("UltraRogue Error #104"); + + if (n1 == 0 && n2 == 0) + { + strcpy(buf,"+0"); + return(buf); + } + + if (n2 == 0) + sprintf(buf, "%s%d", n1 < 0 ? "" : "+", n1); + else + sprintf(buf, "%s%d, %s%d", n1 < 0 ? "" : "+", + n1, n2 < 0 ? "" : "+", n2); + + return(buf); +} + +/* + wield() + Pull out a certain weapon +*/ + +void +wield(void) +{ + struct linked_list *item; + struct object *obj, *oweapon; + + oweapon = cur_weapon; + + if (!dropcheck(cur_weapon)) + { + cur_weapon = oweapon; + return; + } + + cur_weapon = oweapon; + + if ((item = get_item("wield", WEAPON)) == NULL) + { + after = FALSE; + return; + } + + obj = OBJPTR(item); + + if (is_current(obj)) + { + after = FALSE; + return; + } + + wield_ok(&player, obj, TRUE); + + msg("You are now wielding %s.", inv_name(obj, LOWERCASE)); + + cur_weapon = obj; +} + +/* + fallpos() + pick a random position around the given (y, x) coordinates +*/ + +int +fallpos(coord pos, coord *newpos) /*ARGSUSED*/ +{ + int y, x, cnt; + coord places[9]; + + cnt = 0; + + /* look for all the places that qualify */ + + for (y = pos.y - 1; y <= pos.y + 1; y++) + { + for (x = pos.x - 1; x <= pos.x + 1; x++) + { + switch(CCHAR(mvwinch(stdscr,y,x))) + { + case GOLD: + case POTION: + case SCROLL: + case FOOD: + case WEAPON: + case ARMOR: + case RING: + case STICK: + case FLOOR: + case PASSAGE: + case ARTIFACT: + places[cnt].y = y; + places[cnt].x = x; + cnt++; + } + } + } + + /* now, pick one of the places, if there are any */ + + if (cnt > 0) + { + int which = rnd(cnt); + + newpos->y = places[which].y; + newpos->x = places[which].x; + + debug("Dropping object at %d, %d", newpos->y, newpos->x); + } + + return(cnt); +} + +/* + wield_ok() + enforce player class weapons restrictions +*/ + +int +wield_ok(struct thing *wieldee, struct object *obj, int print_message) +{ + int ret_val = TRUE; + int class_type = wieldee->t_ctype; + + if (obj->o_type != WEAPON) + { + ret_val = FALSE; + return(ret_val); + } + else + switch (class_type) + { + case C_MAGICIAN: /* need one hand free */ + case C_ILLUSION: + if (obj->o_flags & ISTWOH) + ret_val = FALSE; + break; + + case C_THIEF: /* need portable weapon */ + case C_ASSASIN: + case C_NINJA: + if (obj->o_flags & ISTWOH) + ret_val = FALSE; + break; + + case C_CLERIC: /* No sharp weapons */ + if (obj->o_flags & ISSHARP) + ret_val = FALSE; + break; + + case C_DRUID: /* No non-silver metal weapons */ + if (obj->o_flags & ISMETAL && !(obj->o_flags & ISSILVER)) + ret_val = FALSE; + break; + + case C_PALADIN: /* must wield sharp stuff */ + if ((obj->o_flags & ISSHARP) == FALSE) + ret_val = FALSE; + break; + + case C_FIGHTER: /* wield anything */ + case C_RANGER: + case C_MONSTER: + break; + + default: /* Unknown class */ + debug("Unknown class %d.", class_type); + break; + } + + if (itemweight(obj) > 18 * pstats.s_str) + { + if (wieldee == &player && print_message == TRUE) + msg("That is too heavy for you to swing effectively!"); + + ret_val = FALSE; + return(ret_val); + } + + if (ret_val == FALSE && print_message == MESSAGE) + switch (class_type) + { + case C_MAGICIAN: + case C_ILLUSION: + msg("You'll find it hard to cast spells while wielding that!"); + break; + + case C_THIEF: + case C_ASSASIN: + case C_NINJA: + msg("Don't expect to backstab anyone while wielding that!"); + break; + + case C_CLERIC: + case C_DRUID: + case C_PALADIN: + msg("Your god strongly disapproves of your wielding that!"); + break; + + case C_FIGHTER: + case C_RANGER: + case C_MONSTER: + break; + } + + return(ret_val); +} + +/* + shoot_ok() + returns true if it is ok for type to shoot over ch +*/ + +int +shoot_ok(int ch) +{ + switch(ch) + { + case ' ': + case '|': + case '-': + case SECRETDOOR: + return(FALSE); + + default: + return(!isalpha(ch)); + } +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/wizard.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/wizard.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,238 @@ +/* + wizard.c - Special wizard commands + + UltraRogue: The Ultimate Adventure in the Dungeons of Doom + Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong + All rights reserved. + + Based on "Advanced Rogue" + Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka + All rights reserved. + + Based on "Rogue: Exploring the Dungeons of Doom" + Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman + All rights reserved. + + See the file LICENSE.TXT for full copyright and licensing information. +*/ + +#include +#include +#include "rogue.h" + +/* + whatis() + What a certain object is +*/ + +void +whatis(struct linked_list *what) +{ + struct object *obj; + int kludge; + int print_message = FALSE; + + if (what == NULL) + { + print_message = TRUE; + + while ((what = get_item("identify", 0)) == NULL) + ; + } + + obj = OBJPTR(what); + obj->o_flags |= ISKNOW; + + switch (obj->o_type) + { + case SCROLL: kludge = TYP_SCROLL; break; + case POTION: kludge = TYP_POTION; break; + case STICK: kludge = TYP_STICK; break; + case RING: kludge = TYP_RING; break; + case WEAPON: + case ARMOR: + default: kludge = -1; break; + } + + if (kludge != -1) + { + know_items[kludge][obj->o_which] = TRUE; + + if (guess_items[kludge][obj->o_which]) + { + ur_free(guess_items[kludge][obj->o_which]); + guess_items[kludge][obj->o_which] = NULL; + } + } + + if (print_message) + msg(inv_name(obj, UPPERCASE)); +} + +/* + teleport() + Bamf the hero someplace else +*/ + +void +teleport(void) +{ + struct room *new_rp = NULL, *old_rp = roomin(hero); + + int rm, which; + coord c; + int is_lit = FALSE; /* For saving room light state */ + int rand_position = TRUE; + + c = hero; + + mvwaddch(cw, hero.y, hero.x, mvwinch(stdscr, hero.y, hero.x)); + + if (is_wearing(R_TELCONTROL)) + { + msg("Where do you wish to teleport to? (* for help)"); + wmove(cw, hero.y, hero.x); + wrefresh(cw); + + which = (short) (readchar() & 0177); + + while (which != (short) ESCAPE && which != (short) LINEFEED + && which != (short) CARRIAGE_RETURN) + { + switch(which) + { + case 'h': c.x--; break; + case 'j': c.y++; break; + case 'k': c.y--; break; + case 'l': c.x++; break; + case 'y': c.x--; + c.y--; break; + case 'u': c.x++; + c.y--; break; + case 'b': c.x--; + c.y++; break; + case 'n': c.x++; + c.y++; break; + case '*': + msg("Use h,j,k,l,y,u,b,n to position cursor, then hit" + "return."); + } + + c.y = max(c.y, 1); + c.y = min(c.y, LINES - 3); + c.x = max(c.x, 1); + c.x = min(c.x, COLS - 1); + wmove(cw, c.y, c.x); + wrefresh(cw); + which = (short) (readchar() & 0177); + } + + which = winat(c.y, c.x); + + if ((which == FLOOR || which == PASSAGE || which == DOOR) && + ((ring_value(R_TELCONTROL) == 0 && rnd(10) < 6) + || (ring_value(R_TELCONTROL) > 0 && rnd(10) < 9))) + { + rand_position = FALSE; + msg("You attempt succeeds."); + hero = c; + new_rp = roomin(hero); + } + else + msg("Your attempt fails."); + } + + if (rand_position) + { + do + { + rm = rnd_room(); + rnd_pos(&rooms[rm], &hero); + } + while (winat(hero.y, hero.x) != FLOOR); + + new_rp = &rooms[rm]; + } + + /* If hero gets moved, darken old room */ + + if (old_rp && old_rp != new_rp) + { + if (!(old_rp->r_flags & ISDARK)) + is_lit = TRUE; + + old_rp->r_flags |= ISDARK; /* Fake darkness */ + light(&c); + + if (is_lit) + old_rp->r_flags &= ~ISDARK; /* Restore light state */ + } + + light(&hero); + mvwaddch(cw, hero.y, hero.x, PLAYER); + + /* turn off ISHELD in case teleportation was done while fighting */ + + if (on(player, ISHELD)) + { + struct linked_list *ip, *nip; + struct thing *mp; + + turn_off(player, ISHELD); + hold_count = 0; + + for (ip = mlist; ip; ip = nip) + { + mp = THINGPTR(ip); + nip = next(ip); + + if (on(*mp, DIDHOLD)) + { + turn_off(*mp, DIDHOLD); + turn_on(*mp, CANHOLD); + } + + turn_off(*mp, DIDSUFFOCATE); + } + } + + extinguish_fuse(FUSE_SUFFOCATE); + player.t_no_move = 0; /* not trapped anymore */ + count = 0; + running = FALSE; + + return; +} + +/* + passwd() + see if user knows password +*/ + +int +passwd(void) +{ + char *sp, c; + char buf[2 * LINELEN]; + + msg("Wizard's Password:"); + mpos = 0; + sp = buf; + + while ((c = (readchar() & 0177)) != '\n' && c != '\r' && c != '\033') + { + if (c == '\0') + sp = buf; + else if (c == '\b' && sp > buf) + sp--; + else + *sp++ = c; + } + + if (sp == buf) + return(FALSE); + + *sp = '\0'; + + return(TRUE); +} diff -r d9badb9c0179 -r c495a4f288c6 urogue/xcrypt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/urogue/xcrypt.c Tue Jan 31 19:56:04 2017 -0500 @@ -0,0 +1,685 @@ +/* + * FreeSec: libcrypt + * + * Copyright (C) 1994 David Burren + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name(s) of the author(s) nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * This is an original implementation of the DES and the crypt(3) interfaces + * by David Burren . + * + * An excellent reference on the underlying algorithm (and related + * algorithms) is: + * + * B. Schneier, Applied Cryptography: protocols, algorithms, + * and source code in C, John Wiley & Sons, 1994. + * + * Note that in that book's description of DES the lookups for the initial, + * pbox, and final permutations are inverted (this has been brought to the + * attention of the author). A list of errata for this book has been + * posted to the sci.crypt newsgroup by the author and is available for FTP. + * + * NOTE: + * This file has a static version of des_setkey() so that crypt.o exports + * only the crypt() interface. This is required to make binaries linked + * against crypt.o exportable or re-exportable from the USA. + */ + +#include +#include + +extern unsigned long int md_ntohl(unsigned long int x); +extern unsigned long int md_htonl(unsigned long int x); + +#define _PASSWORD_EFMT1 '_' + +static unsigned char IP[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; + +static unsigned char inv_key_perm[64]; +static unsigned char key_perm[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +static unsigned char key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static unsigned char inv_comp_perm[56]; +static unsigned char comp_perm[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * No E box is used, as it's replaced by some ANDs, shifts, and ORs. + */ + +static unsigned char u_sbox[8][64]; +static unsigned char sbox[8][64] = { + { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + }, + { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + }, + { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + }, + { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + }, + { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + }, + { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + }, + { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + }, + { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + } +}; + +static unsigned char un_pbox[32]; +static unsigned char pbox[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static unsigned int bits32[32] = +{ + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static unsigned char bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + +static unsigned int saltbits; +static int old_salt; +static unsigned int *bits28, *bits24; +static unsigned char init_perm[64], final_perm[64]; +static unsigned int en_keysl[16], en_keysr[16]; +static unsigned int de_keysl[16], de_keysr[16]; +static int des_initialised = 0; +static unsigned char m_sbox[4][4096]; +static unsigned int psbox[4][256]; +static unsigned int ip_maskl[8][256], ip_maskr[8][256]; +static unsigned int fp_maskl[8][256], fp_maskr[8][256]; +static unsigned int key_perm_maskl[8][128], key_perm_maskr[8][128]; +static unsigned int comp_maskl[8][128], comp_maskr[8][128]; +static unsigned int old_rawkey0, old_rawkey1; + +static unsigned char ascii64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +/* 0000000000111111111122222222223333333333444444444455555555556666 */ +/* 0123456789012345678901234567890123456789012345678901234567890123 */ + +static __inline int +ascii_to_bin(char ch) +{ + if (ch > 'z') + return(0); + if (ch >= 'a') + return(ch - 'a' + 38); + if (ch > 'Z') + return(0); + if (ch >= 'A') + return(ch - 'A' + 12); + if (ch > '9') + return(0); + if (ch >= '.') + return(ch - '.'); + return(0); +} + +static void +des_init() +{ + int j, b, k, inbit, obit; + unsigned int *p, *il, *ir, *fl, *fr; + unsigned char i; + + old_rawkey0 = old_rawkey1 = 0; + saltbits = 0; + old_salt = 0; + bits24 = (bits28 = bits32 + 4) + 4; + + /* + * Invert the S-boxes, reordering the input bits. + */ + for (i = 0; i < 8; i++) + for (j = 0; j < 64; j++) { + b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf); + u_sbox[i][j] = sbox[i][b]; + } + + /* + * Convert the inverted S-boxes into 4 arrays of 8 bits. + * Each will handle 12 bits of the S-box input. + */ + for (b = 0; b < 4; b++) + for (i = 0; i < 64; i++) + for (j = 0; j < 64; j++) + m_sbox[b][(i << 6) | j] = + (u_sbox[(b << 1)][i] << 4) | + u_sbox[(b << 1) + 1][j]; + + /* + * Set up the initial & final permutations into a useful form, and + * initialise the inverted key permutation. + */ + for (i = 0; i < 64; i++) { + init_perm[final_perm[i] = IP[i] - 1] = i; + inv_key_perm[i] = 255; + } + + /* + * Invert the key permutation and initialise the inverted key + * compression permutation. + */ + for (i = 0; i < 56; i++) { + inv_key_perm[key_perm[i] - 1] = i; + inv_comp_perm[i] = 255; + } + + /* + * Invert the key compression permutation. + */ + for (i = 0; i < 48; i++) { + inv_comp_perm[comp_perm[i] - 1] = i; + } + + /* + * Set up the OR-mask arrays for the initial and final permutations, + * and for the key initial and compression permutations. + */ + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(il = &ip_maskl[k][i]) = 0; + *(ir = &ip_maskr[k][i]) = 0; + *(fl = &fp_maskl[k][i]) = 0; + *(fr = &fp_maskr[k][i]) = 0; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = init_perm[inbit]) < 32) + *il |= bits32[obit]; + else + *ir |= bits32[obit-32]; + if ((obit = final_perm[inbit]) < 32) + *fl |= bits32[obit]; + else + *fr |= bits32[obit - 32]; + } + } + } + for (i = 0; i < 128; i++) { + *(il = &key_perm_maskl[k][i]) = 0; + *(ir = &key_perm_maskr[k][i]) = 0; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + if ((obit = inv_key_perm[inbit]) == 255) + continue; + if (obit < 28) + *il |= bits28[obit]; + else + *ir |= bits28[obit - 28]; + } + } + *(il = &comp_maskl[k][i]) = 0; + *(ir = &comp_maskr[k][i]) = 0; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + if ((obit=inv_comp_perm[inbit]) == 255) + continue; + if (obit < 24) + *il |= bits24[obit]; + else + *ir |= bits24[obit - 24]; + } + } + } + } + + /* + * Invert the P-box permutation, and convert into OR-masks for + * handling the output of the S-box arrays setup above. + */ + for (i = 0; i < 32; i++) + un_pbox[pbox[i] - 1] = i; + + for (b = 0; b < 4; b++) + for (i = 0; i < 256; i++) { + *(p = &psbox[b][i]) = 0; + for (j = 0; j < 8; j++) { + if (i & bits8[j]) + *p |= bits32[un_pbox[8 * b + j]]; + } + } + + des_initialised = 1; +} + +static void +setup_salt(int salt) +{ + unsigned int obit, saltbit; + int i; + + if (salt == old_salt) + return; + old_salt = salt; + + saltbits = 0; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } +} + +static int +des_setkey(const unsigned char *key) +{ + unsigned int k0, k1, rawkey0, rawkey1; + int shifts, round; + + if (!des_initialised) + des_init(); + + rawkey0 = md_ntohl(*(unsigned int *) key); + rawkey1 = md_ntohl(*(unsigned int *) (key + 4)); + + if ((rawkey0 | rawkey1) + && rawkey0 == old_rawkey0 + && rawkey1 == old_rawkey1) { + /* + * Already setup for this key. + * This optimisation fails on a zero key (which is weak and + * has bad parity anyway) in order to simplify the starting + * conditions. + */ + return(0); + } + old_rawkey0 = rawkey0; + old_rawkey1 = rawkey1; + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + k0 = key_perm_maskl[0][rawkey0 >> 25] + | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskl[4][rawkey1 >> 25] + | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; + k1 = key_perm_maskr[0][rawkey0 >> 25] + | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskr[4][rawkey1 >> 25] + | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + unsigned int t0, t1; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + + de_keysl[15 - round] = + en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] + | comp_maskl[1][(t0 >> 14) & 0x7f] + | comp_maskl[2][(t0 >> 7) & 0x7f] + | comp_maskl[3][t0 & 0x7f] + | comp_maskl[4][(t1 >> 21) & 0x7f] + | comp_maskl[5][(t1 >> 14) & 0x7f] + | comp_maskl[6][(t1 >> 7) & 0x7f] + | comp_maskl[7][t1 & 0x7f]; + + de_keysr[15 - round] = + en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] + | comp_maskr[1][(t0 >> 14) & 0x7f] + | comp_maskr[2][(t0 >> 7) & 0x7f] + | comp_maskr[3][t0 & 0x7f] + | comp_maskr[4][(t1 >> 21) & 0x7f] + | comp_maskr[5][(t1 >> 14) & 0x7f] + | comp_maskr[6][(t1 >> 7) & 0x7f] + | comp_maskr[7][t1 & 0x7f]; + } + return(0); +} + +static int +do_des(unsigned int l_in, unsigned int r_in, unsigned int *l_out, + unsigned int *r_out, int count) +{ + /* + * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. + */ + unsigned int l, r, *kl, *kr, *kl1, *kr1; + unsigned int f = 0, r48l, r48r; + int round; + + if (count == 0) { + return(1); + } else if (count > 0) { + /* + * Encrypting + */ + kl1 = en_keysl; + kr1 = en_keysr; + } else { + /* + * Decrypting + */ + count = -count; + kl1 = de_keysl; + kr1 = de_keysr; + } + + /* + * Do initial permutation (IP). + */ + l = ip_maskl[0][l_in >> 24] + | ip_maskl[1][(l_in >> 16) & 0xff] + | ip_maskl[2][(l_in >> 8) & 0xff] + | ip_maskl[3][l_in & 0xff] + | ip_maskl[4][r_in >> 24] + | ip_maskl[5][(r_in >> 16) & 0xff] + | ip_maskl[6][(r_in >> 8) & 0xff] + | ip_maskl[7][r_in & 0xff]; + r = ip_maskr[0][l_in >> 24] + | ip_maskr[1][(l_in >> 16) & 0xff] + | ip_maskr[2][(l_in >> 8) & 0xff] + | ip_maskr[3][l_in & 0xff] + | ip_maskr[4][r_in >> 24] + | ip_maskr[5][(r_in >> 16) & 0xff] + | ip_maskr[6][(r_in >> 8) & 0xff] + | ip_maskr[7][r_in & 0xff]; + + while (count--) { + /* + * Do each round. + */ + kl = kl1; + kr = kr1; + round = 16; + while (round--) { + /* + * Expand R to 48 bits (simulate the E-box). + */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do sbox lookups (which shrink it back to 32 bits) + * and do the pbox permutation at the same time. + */ + f = psbox[0][m_sbox[0][r48l >> 12]] + | psbox[1][m_sbox[1][r48l & 0xfff]] + | psbox[2][m_sbox[2][r48r >> 12]] + | psbox[3][m_sbox[3][r48r & 0xfff]]; + /* + * Now that we've permuted things, complete f(). + */ + f ^= l; + l = r; + r = f; + } + r = l; + l = f; + } + /* + * Do final permutation (inverse of IP). + */ + *l_out = fp_maskl[0][l >> 24] + | fp_maskl[1][(l >> 16) & 0xff] + | fp_maskl[2][(l >> 8) & 0xff] + | fp_maskl[3][l & 0xff] + | fp_maskl[4][r >> 24] + | fp_maskl[5][(r >> 16) & 0xff] + | fp_maskl[6][(r >> 8) & 0xff] + | fp_maskl[7][r & 0xff]; + *r_out = fp_maskr[0][l >> 24] + | fp_maskr[1][(l >> 16) & 0xff] + | fp_maskr[2][(l >> 8) & 0xff] + | fp_maskr[3][l & 0xff] + | fp_maskr[4][r >> 24] + | fp_maskr[5][(r >> 16) & 0xff] + | fp_maskr[6][(r >> 8) & 0xff] + | fp_maskr[7][r & 0xff]; + return(0); +} + +static int +des_cipher(const unsigned char *in, unsigned char *out, int salt, int count) +{ + unsigned int l_out, r_out, rawl, rawr; + unsigned int x[2]; + int retval; + + if (!des_initialised) + des_init(); + + setup_salt(salt); + + memcpy(x, in, sizeof x); + rawl = md_ntohl(x[0]); + rawr = md_ntohl(x[1]); + retval = do_des(rawl, rawr, &l_out, &r_out, count); + + x[0] = md_htonl(l_out); + x[1] = md_htonl(r_out); + memcpy(out, x, sizeof x); + return(retval); +} + +char * +xcrypt(const char *key, const char *setting) +{ + int i; + unsigned int count, salt, l, r0, r1, keybuf[2]; + unsigned char *p, *q; + static unsigned char output[21]; + + if (!des_initialised) + des_init(); + + /* + * Copy the key, shifting each character up by one bit + * and padding with zeros. + */ + q = (unsigned char *) keybuf; + while ((q - (unsigned char *) keybuf) < sizeof(keybuf)) { + if ((*q++ = *key << 1)) + key++; + } + if (des_setkey((unsigned char *) keybuf)) + return(NULL); + + if (*setting == _PASSWORD_EFMT1) { + /* + * "new"-style: + * setting - underscore, 4 bytes of count, 4 bytes of salt + * key - unlimited characters + */ + for (i = 1, count = 0; i < 5; i++) + count |= ascii_to_bin(setting[i]) << (i - 1) * 6; + + for (i = 5, salt = 0; i < 9; i++) + salt |= ascii_to_bin(setting[i]) << (i - 5) * 6; + + while (*key) { + /* + * Encrypt the key with itself. + */ + if (des_cipher((unsigned char*)keybuf, (unsigned char*)keybuf, 0, 1)) + return(NULL); + /* + * And XOR with the next 8 characters of the key. + */ + q = (unsigned char *) keybuf; + while (((q - (unsigned char *) keybuf) < sizeof(keybuf)) && + *key) + *q++ ^= *key++ << 1; + + if (des_setkey((unsigned char *) keybuf)) + return(NULL); + } + strncpy((char *)output, setting, 9); + + /* + * Double check that we weren't given a short setting. + * If we were, the above code will probably have created + * wierd values for count and salt, but we don't really care. + * Just make sure the output string doesn't have an extra + * NUL in it. + */ + output[9] = '\0'; + p = output + strlen((const char *)output); + } else { + /* + * "old"-style: + * setting - 2 bytes of salt + * key - up to 8 characters + */ + count = 25; + + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + /* + * If the encrypted password that the salt was extracted from + * is only 1 character long, the salt will be corrupted. We + * need to ensure that the output string doesn't have an extra + * NUL in it! + */ + output[1] = setting[1] ? setting[1] : output[0]; + + p = output + 2; + } + setup_salt(salt); + /* + * Do it. + */ + if (do_des(0, 0, &r0, &r1, count)) + return(NULL); + /* + * Now encode the result... + */ + l = (r0 >> 8); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = (r0 << 16) | ((r1 >> 16) & 0xffff); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = r1 << 2; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + *p = 0; + + return((char *)output); +}