[comp.sources.games] v07i020: omega3 - rogue like dungeon exploration

billr@saab.CNA.TEK.COM (Bill Randle) (07/13/89)

Submitted-by: "Laurence R. Brothers"   <brothers@paul.rutgers.edu>
Posting-number: Volume 7, Issue 20
Archive-name: omega3/Part01
Supersedes: omega2: Volume 5, Issue 11-29,38-40

	[For all you omega fans, here's the latest version.  -br]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 20)."
# Contents:  README MANIFEST README2 README3 README4 ohelp10.txt oinv.c
# Wrapped by billr@saab on Thu Jun 29 08:13:55 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(532 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XNote that omega is copyrighted. See the public license in olicense.txt in
Xthis directory for what you can do with the code. 
X
XFor directions on how to compile, see README2 (pc people see dosread.me)
X
XFor information on how omega was written see README3
X
XSome tips on maintaining the game are in README4
X
XFor directions how to play, see ohelp*.txt files (cat them all
Xtogether for a full document; this can be done from within
Xthe program).
X
XA manifest of omega files is in the file MANIFEST
X
XHave Fun, and Be Careful....
X
X-Laurence
END_OF_FILE
if test 532 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(3630 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	
X Makefile                  20	
X Makefile.dos              19	
X Makefile.notsun           20	
X README                     1	
X README2                    1	
X README3                    1	
X README4                    1	
X doscomp.txt               19	
X dosread.me                19	
X fixstr.c                  19	
X o.c                       17	
X oabyss.c                  18	
X oabyss.dat                20	
X oabyss.txt                20	
X oarena.dat                20	
X oaux1.c                   13	
X oaux2.c                    3	
X oaux3.c                    6	
X ochar.c                   16	
X ocircle.dat               20	
X ocity.c                   13	
X ocity.dat                 19	
X ocom1.c                   18	
X ocom2.c                   11	
X ocom3.c                   10	
X ocountry.c                17	
X ocountry.dat              19	
X ocourt.dat                20	
X odate.h                    6	
X odefs.h                    9	
X odlair.dat                20	
X oeffect1.c                12	
X oeffect2.c                14	
X oeffect3.c                 5	
X oenv.c                    13	
X oetc.c                    17	
X oextern.h                 14	
X ofile.c                   10	
X ogen1.c                    2	
X ogen2.c                   17	
X oglob.h                   12	
X oguild1.c                 15	
X oguild2.c                  8	
X ohelp1.txt                19	
X ohelp10.txt                1	
X ohelp11.txt               20	
X ohelp12.txt               19	
X ohelp13.txt               20	
X ohelp2.txt                19	
X ohelp3.txt                19	
X ohelp4.txt                20	
X ohelp5.txt                15	
X ohelp6.txt                20	
X ohelp7.txt                20	
X ohelp8.txt                10	
X ohelp9.txt                19	
X ohome1.dat                20	
X ohome2.dat                20	
X ohome3.dat                20	
X ohouse.c                  11	
X oiinit.h                   3	
X oinit.c                   11	
X ointro.txt                20	
X oinv.c                     1	
X oitem.c                    9	
X oitemf1.c                 11	
X oitemf2.c                 18	
X oitemf3.c                 15	
X olev.c                    15	
X olicense.txt               5	
X omaze1.dat                20	
X omaze2.dat                20	
X omaze3.dat                16	
X omaze4.dat                20	
X omega.ad                  20	
X omega.hi                  20	
X omega.log                 20	
X omegahi.bak               12	
X ominit.h                   4	
X omisle.dat                20	
X ommelee.c                 16	
X ommove.c                  19	
X omon.c                     7	
X omotd.txt                  9	
X omove.c                    4	
X omovef.c                  18	
X omspec.c                   7	
X omstrike.c                20	
X omtalk.c                   5	
X opriest.c                 16	
X osave.c                    6	
X oscr.c                     2	
X oscroll1.txt              19	
X oscroll2.txt              20	
X oscroll3.txt              20	
X oscroll4.txt              20	
X osite1.c                   8	
X osite2.c                  12	
X ospeak.dat                20	
X ospell.c                  14	
X otemple.dat               20	
X othanks.txt                4	
X otime.c                    7	
X otrap.c                   18	
X oupdate.txt               14	
X outil.c                   16	
X ovillage.c                18	
X ovillage1.dat             20	
X ovillage2.dat             20	
X ovillage3.dat             20	
X ovillage4.dat             20	
X ovillage5.dat             20	
X ovillage6.dat             20	
END_OF_FILE
if test 3630 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'README2' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README2'\"
else
echo shar: Extracting \"'README2'\" \(2458 characters\)
sed "s/^X//" >'README2' <<'END_OF_FILE'
Xomega compilation notes.
X
XVERSION 0.75:
X
XFirst I should note that omega is copyrighted; you are using this
Xprogram under the terms of the license, hopefully included in the file
Xomega.license.
X
Xomega is set up for compilation under Sun's unix; some changes may be
Xnecessary for correct functioning under other unixes. I have included
Xsome ifdef lines in odefs.h that ought to have some positive influence.
XHowever, not having anything besides a sun to test omega on, I will
Xhave to rely on your feedback as to the effect.
X
XTo implement omega on your system:
X
X-1) Make sure you have the files listed in the file called MANIFEST.
XIf you don't have these files, you lose! Get them from somewhere. In
Xparticular, it is a violation of the license not to have a copy of
Xolicense.txt.
X
X0) Feel free to change omotd.txt to say whatever you like, keeping
Xin mind the constraints of the Sedition Act.
X
X1) Set up a directory for omega's data files. Protection must allow
Xthe general public to write to specified files. My personal prejudice
Xis against setuid, but use it if you like. There is
Xno need to have a special directory, but it is neater that way.
X
X2) Change the first few #define's in odefs.h to conform to the 
Xdata file directory, your user name, etc., as the comments in the file
Xdiscuss.
X
X3) If you are compiling on a sun, just use the given Makefile. If not,
Xtake a look at makefile.notsun for a clue what to do. Basically, you
Xjust have to compile everything together, remembering to -lcurses and
X-ltermlib (termcap on some systems) where appropriate.  With Makefile,
X*.h, and *.c in one directory, make should produce an executable file
Xomega. If you get errors, you'll have to work them out for
Xyourself.... My Makefile uses -g; unless you are going to hack omega,
Xremove the -g. If you like your optimizer, feel free to try -O.  I
Xrecommend using either cc and -g or gcc and -O; gcc really does seem
Xto optimize pretty well on omega, but at least our version causes dbx to
Xcrash with -g....
X
X4) All the files which are not source code should have at
Xthe least public read access. The following files should have write
Xaccess as well:
X
Xomega.hi
Xomega.log
X
XThese files should be placed in the OMEGALIB directory defined in
Xodefs.h
X
X5) At this point, omega should be ready to run. Some notes on maintaining
Xthe game can be found in README4.
X
X6) Send any questions to brothers@paul.rutgers.edu.
X
X7) Good luck -- and let's be careful out there, shall we?
END_OF_FILE
if test 2458 -ne `wc -c <'README2'`; then
    echo shar: \"'README2'\" unpacked with wrong size!
fi
# end of 'README2'
fi
if test -f 'README3' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README3'\"
else
echo shar: Extracting \"'README3'\" \(9280 characters\)
sed "s/^X//" >'README3' <<'END_OF_FILE'
Xomega implementor's notes.
X
XVersion 0.75
X
XThis version is a bugfixed version of 0.71. Bugs still remain. For
Xexample, the high score routines should really be completely
Xrewritten. However, they probably will not be, as the next version
Xwill be a total rewrite, probably based on xview (don't hold
Xyour breath, out there....)
X
XNotes on Game Structure:
X
XI'm now keeping track of the last edit in odate.h -- if you hack
Xomega, you might conceivably want to do the same. The #definition
Xof LAST_OMEGA_EDIT_DATE is printed by the 'V' command....
X
XRight now the structure of guilds, dungeons, quests, etc. is not
Xreally the way I want it. The player winds up doing basically all the
Xsame things each game, no matter which guild he may be in, because
Xthere is not enough detail to the world of omega. It is a LOT of
Xpainstaking work to sort out threads of narrative structure (such as
Xfor example the beginning attempts I've made with the LawBringer and
Xthe way he interacts with the Circle of Sorcerors and the Order of
XPaladins).
X
XTheoretically, one need not do any questing at all to become a Total
XWinner (ie, become an adept); this is also not desirable.
X
XThere are any number of ways to mechanically "crack" the game, i.e.,
Xto do something to take advantage of omega for the player's benefit in
Xsome deviant way. At least you can't drop a 32 bit number of negative
Xgold pieces like in hack.... Anyhow, making sure these loopholes are
Xfilled is a low priority except when they are really blatant; I'd much
Xrather enhance game play for people who don't cheat....
X
XAt the moment I have several goals for the next major version of
Xomega (0.80):
X
X	* Enhanced game structure -- more dungeons, more detail
X	within guilds, pristhoods, etc. that will make the game
X	very different for different characters.
X
X	* More player interaction. I still don't intend to have
X	omega be an online multiplayer game, but it would be nice
X	to have more effect on the game from the actions of 
X	previous characters.
X
X	* X and/or NeWS and/or Sunview II front-end(s). It would be nice
X	to have a separate inventory window, be able to click destinations,
X	have monster and terrain fonts, etc. If I get really
X	overexcited, how about a perspective view of the dungeon....
X
XAs always, I welcome suggestions. It is very easy to add more monsters,
Xmagic items, magic spells, etc....
X
XNotes on the Code:
X
XAs far as I can tell, all this should work on any Unix* system, but
XI've never tried it on anything but a Sun, not having enough disk
Xspace to my name anywhere else to try.... It uses curses for I/O, and
Xtries to use as few OS-dependent functions as it can elsewhere. It
Xshould be pretty easy to port to any Unix* variant that has curses.
Xomega has now been implemented not only on Suns, Vaxen, and Pyramids,
Xbut even on machines such as Amdahls running unix over the normal
Xoperating system. The current version has #ifdef's for MSDOS,
Xcourtesy nathan@brokaw.lcs.mit.edu. Unfortunately, this makes
Xthe code harder to read, but such is life.
X
XIdentifier Convention:
XUPPERCASE CONSTANTS are all #defined in odefs.h
XCapitalized Globals are all defined in o.c and externally declared in oglob.h
Xother identifiers are lowercase
X
XI have attempted to keep extremely system-dependent stuff segregated
Xfrom the rest of the code.
X
XFor example, all screen manipulation functions are in oscr.c, for
Xthose of you who want to port omega to something that doesn't have
Xcurses. (Well, there's a little in ofile.c)
X
XAlmost all file manipulation is done in ofile.c, and I have attempted
Xto keep this simple and consistent.
X
XA few other system calls are in o.c and outil.c, and that's about it.
X
XUnfortunately, commenting is pretty minimal, mostly stupid things like:
X
X  /* this routine shows a monster */
X  void showmonster(m)
X
X...and abstruse reminders to myself. As I clean the code up for the
Xfinal release version, this will improve. I usually try to avoid
Xcleverness in my c code, preferring two assignments and an if
Xstatement to a single complex line with three nested conditional
Xexpressions. Still, there are some places where the code is pretty
Xopaque... sorry about that. Also, I will probably switch over
Xto ANSI style c pretty soon.
X
XEach module has a short description of its contents at the top, and
Xmostly functions tend to stay where it is reasonable to find them.
XThere are a few grab-bag modules, though, such as oaux1, oaux2 and
Xoaux3 (utility functions for ocom and other modules), oetc (total
Xgrab-bag), and outil (mostly more general utility functions used by
Xeverything).  To save on module size, I have broken a couple of
Xmodules (such as ocom and osite) into pieces. I never got around to
Xstating precisely which functions that used to be in ocom are now in
Xocom1, ocom2, or ocom3, but grep and ^s always work.... Anyhow,
Xone of these days I will clean up. Really.
X
Xoextern.h has all the function declarations. Unfortunately, some of
Xthese are incorrect as to which module the function is contained
Xin -- I split a number of modules and rearranged others, so
Xwhile all the declarations are in fact correct, the comments stating
Xwhere things are are sometimes off. I'll get around to fixing things
Xone of these days. 
X
Xodefs.h has all the CPP stuff, structure definitions, and typedefs.
X
Xoglob.h has all the external global variable declarations, and
Xalso includes the preceding two header files.
X
XThose of you wishing to port omega to machines without virtual memory
Xwill probably want to keep only one dungeon level in memory at a time;
Xyou ought to be able to use the save_level and restore_level routines
Xin olev.c to get that working. A lot of memory (primary and
Xsecondary), is currently required to play a full game; you could
Xprobably run for a while with a good deal less, though. However,
Xyou'll need a good deal of disk space if you don't have a lot of
Xmemory; it's just not practicable to play with under several meg of
Xcombined memory and disk space. Actually, I should probably make
Xup standard #define to save all levels but the current one to disk
Xas an option (rendering the moria-like lossage of previously entered
Xdungeons nugatory), but see the previous note about laziness....
X
XKNOWN BUGS AND MISFEATURES: 
X
X	* When you exit to the countryside, there is often one extraneous
X	character drawn somewhere on the screen. I have no idea why.
X
X	* Sometimes monsters leave echos behind as they move. This
X	occurs sufficiently rarely that I have not yet felt a need
X	to rewrite my display code in oscr.c; for some reason it happens
X	less when compiled with gcc than with cc.... Actually,
X	this problem has hopefully just gone away, but one never knows....
X
X	* It is apparently occasionally possible to juggle two-handed
X	weapons so as to either duplicate or destroy an object. For
X	some reason this seems to happen to other people than me,
X	mostly. I am not sure if it is still possible to do this.
X
X	* There are still some circumstances where the message
X	display overwrites a line too fast to read it; there are other
X	places where you have to hit the space bar an extra time.
X	This is partly due to the fact that I have not fully converted
X	to the three-line message display area, and partly due to 
X	laziness in tracking down the instances where this occurs.
X
X	* It is occasionally possible to destroy a game on save/restore;
X	you save, and on restore you get a core dump signal. Sometimes
X	when you save again it goes away; sometimes it doesn't.
X
X	* Sometimes there seems to be an (unintentional) party room
X	effect in some rooms in dungeon levels, ie, a bunch of monsters
X	are having a party. I am not certain why this happens, but
X	I guess it's just another one of the risks the adventurer takes...
X
X	* For some bizarre reason, it is sometimes possible to have
X	strange behavior from boots. Some boots will grant permanent
X	effects, even when taken off -- this is pretty intermittent.
X	7 league boots sometimes just stop working.
X
X	* As far as I know (ha ha), there aren't any core dump bugs
X	in actual play at the moment.
X	
X
XUNKNOWN BUGS:
X
XThere are an awful lot of features and the number of game states you
Xcan get into is very large. In addition, many of the more
X"interesting" bugs will probably only occur after long periods of play
Xsince the "higher level" a feature, effect, or state is, the less
Xit's probably been tested. While I have not encountered any
Xcore-dump bugs recently, there undoubtably are some, not to mention
Xlesser problems such as hacks to get high scores mechanically,
Xmisfeatures, poor game interface occasions, etc.
X
XI am always anxious to hear about any problems you have had with
Xomega, be they problems with compilation, gameplay, or whatever.
XSuggestions for fixes are always welcome, as are wish-lists for
Xadditional features. The most useful things are new monsters, new
Xitems, and new magical effects, as these can be added without
Xdisrupting existing features.
X
XI hope you enjoy the game.
X
X-Laurence,
X
X			 Laurence R. Brothers
X		      brothers@paul.rutgers.edu
X            {anywhere}!rutgers!paul.rutgers.edu!brothers
X		       "One life -- one arrow."
X
X
X* Ha, I bet you were expecting some different message. Well, this is not
Xa commercial document or a public net announcement, so I don't see
Xwhy I have to credit Bell Labs or UCB. So there.
END_OF_FILE
if test 9280 -ne `wc -c <'README3'`; then
    echo shar: \"'README3'\" unpacked with wrong size!
fi
# end of 'README3'
fi
if test -f 'README4' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README4'\"
else
echo shar: Extracting \"'README4'\" \(1775 characters\)
sed "s/^X//" >'README4' <<'END_OF_FILE'
Xomega wizard's guide.
X
XOMEGA MAINTENANCE
X=================
X
XThere should be little need for the wizard to actively maintain the
Xgame. But.....
X
XAfter years of play, the file omega.log may get unwieldy and overlarge.
XThis file can simply be truncated as follows. Log records are now
Xone line at a time, so just delete as many lines as you like, making
Xsure there are no blank lines, and that the file ends on an end of
Xline.
X
XYou can even remove omega.log completely as long as you replace it
Xwith a null file of the same name. omega.log must be publically
Xwriteable.
X
XThe file omega.hi is a high score list. This file can't be truncated
Xor removed, but you can edit it as you feel to be appropriate. If you
Xlook in ofile.c you can figure out what those numbers in the
Xfile actually mean; usually one is NPC level and one is NPC behavior,
Xbut some entries have another entry such as alignment. The
Xdistribution comes with a "safety" version of the original high
Xscore file; if anything happens to the original you can just
Xcopy omegahi.bak to omega.hi, making sure of course it has
Xpublic write access.
X
XAnyone can mung the log and hiscore files simply by writing to them
X(unless you used setuid, anyhow). In my opinion, people who hack
Xscore files can very easily be taken care of by deleting
Xtheir accounts; if that doesn't work, shoot them.
X
XWIZARD MODE
X===========
X
XIf you are the WIZARD as defined in odefs.h, you can enter wizard mode
Xby hitting ^g. If you ever set wizard mode, your high scores and npc
Xwill not be saved.
X
XIn wizard mode, ^x gives you a wish. ^w draws the current level, ^k
Xallows you to set an arbitrary game status bit, meanings of the bits
Xare given in odefs.h. The possible wishes are listed in oscroll3.txt,
Xand the code for them is in oeffect1.c
END_OF_FILE
if test 1775 -ne `wc -c <'README4'`; then
    echo shar: \"'README4'\" unpacked with wrong size!
fi
# end of 'README4'
fi
if test -f 'ohelp10.txt' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ohelp10.txt'\"
else
echo shar: Extracting \"'ohelp10.txt'\" \(1085 characters\)
sed "s/^X//" >'ohelp10.txt' <<'END_OF_FILE'
XSAVING AND RESTORING
X==================== 
XSince omega's dungeon is quite large, only the current dungeon level
Xand the city level will be saved; others will be regenerated as you
Xre-enter them. You might simply consider that the "actual" levels are
Xfar larger than is apparent, and you are just traversing a different
Xpart on each restore.... If you know the spell of Return, however,
X(learnable at the Explorers' Club) you will be able to warp to your
Xdeepest excursion in the most recently entered dungeon without having
Xto retraverse the old levels in between. Some other shortcuts exist
Xfor "warping" from one locale or level to another.
X
XGames can be restored by giving the save file as a command line argument
Xas in:
X
X% omega quasar.sav
X
XTo at least simulate the continuity of character in the game, saved
Xfiles will be unlinked on restoration. Of course, you *can* copy them.
X
XSave files are automatically compressed unless the flag
XCOMPRESS_SAVE_FILES is undefined in odefs.h. This doubles the amount
Xof time taken to save, but reduces the typical save file from 150K to
X15K.
END_OF_FILE
if test 1085 -ne `wc -c <'ohelp10.txt'`; then
    echo shar: \"'ohelp10.txt'\" unpacked with wrong size!
fi
# end of 'ohelp10.txt'
fi
if test -f 'oinv.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'oinv.c'\"
else
echo shar: Extracting \"'oinv.c'\" \(31995 characters\)
sed "s/^X//" >'oinv.c' <<'END_OF_FILE'
X/* omega copyright (C) by Laurence Raphael Brothers, 1987,1988,1989 */
X/* oinv.c */
X/* functions having to do with player item inventory */
X
X#include "oglob.h"
X#ifdef MSDOS
X#include <curses.h>
X#endif
X
X
X/* drops money, heh heh */
Xvoid drop_money()
X{
X  pob money;
X
X  money = detach_money();
X  if (money != NULL) {
X    if (Current_Environment == E_CITY) {
X      print1("As soon as the money leaves your hand,");
X      print2("a horde of scrofulous beggars snatch it up and are gone!");
X    }
X    else drop_at(Player.x,Player.y,money);
X  }
X  else setgamestatus(SKIP_MONSTERS);
X}
X
X
X
X/* returns some money from player back into "money" item.
X   for giving and dropping money */
Xpob detach_money()
X{
X#ifndef MSDOS
X  int c;
X#else
X  long c;
X#endif
X  pob cash=NULL;
X  c = get_money(Player.cash);
X  if (c != ABORT) {
X    Player.cash -= c;
X    cash = ((pob) malloc(sizeof(objtype)));
X    make_cash(cash,difficulty());
X    cash->basevalue = c;
X  }
X  return(cash);
X}
X
X
X/* gets a legal amount of money or ABORT */
X#ifndef MSDOS
Xint get_money(limit)
Xint limit;
X#else
Xlong get_money(limit)
Xlong limit;
X#endif
X{
X#ifndef MSDOS
X  int c;
X#else
X  long c;
X#endif
X  print1("How much? ");
X  c = parsenum();
X  if (c > limit) {
X    print3("Forget it, buddy.");
X    return(ABORT);
X  }
X  else return(c);
X}
X
X
X
X/* pick up from some location x,y */
X/* Lift entire itemlist off ground, pass it to inventory control, which
X   may drop things back onto the now null ground */
Xvoid pickup_at(x,y)
Xint x,y;
X{
X  int quit = FALSE;
X  char response;
X  pol ol = Level->site[x][y].things;
X  pol temp;
X
X  resetgamestatus(FAST_MOVE);
X
X  Level->site[x][y].things = NULL;
X
X  while (ol != NULL) {
X    if (! quit) {
X      clearmsg1();
X      print1("Pick up: ");
X      nprint1(itemid(ol->thing));
X      nprint1(" [ynq]: ");
X      response = ynq1();
X      quit = (response == 'q');
X    }
X    if (response == 'y') gain_item(ol->thing);
X    else drop_at(Player.x,Player.y,ol->thing);
X    temp = ol;
X    ol = ol->next;
X    temp->thing = NULL;
X    temp->next = NULL;
X    free((char *) temp);
X  }
X}
X
X
X/* put all of o on objlist at x,y on Level->depth */
X/* Not necessarily dropped by character; just dropped... */
Xvoid drop_at(x,y,o)
Xint x,y;
Xpob o;
X{
X  pol tmp;
X  pob cpy; 
X  
X  if (Current_Environment != E_COUNTRYSIDE) {
X    if ((Level->site[x][y].locchar != ' ') &&
X	(Level->site[x][y].locchar != '0')) {
X      cpy = ((pob) malloc(sizeof(objtype)));
X      tmp = ((pol) malloc(sizeof(oltype)));
X      *cpy = *o;
X      cpy->used = FALSE;
X      tmp->thing = cpy;
X      tmp->next = Level->site[x][y].things;
X      Level->site[x][y].things = tmp;
X    }
X    else if (Level->site[x][y].p_locf == L_VOID_STATION)
X      setgamestatus(PREPARED_VOID);
X  }
X}  
X
X/* put n of o on objlist at x,y on Level->depth */
Xvoid p_drop_at(x,y,n,o)
Xint x,y;
Xint n;
Xpob o;
X{
X  pol tmp; 
X  if (Current_Environment != E_COUNTRYSIDE) 
X    if ((Level->site[x][y].locchar != ' ') &&
X	(Level->site[x][y].locchar != '0')) {
X      tmp = ((pol) malloc(sizeof(oltype)));
X      tmp->thing = ((pob) malloc(sizeof(objtype)));
X      *(tmp->thing) = *o;
X      tmp->thing->used = FALSE;
X      tmp->thing->number = n;
X      print2("Dropped ");
X      nprint2(itemid(tmp->thing));
X      morewait();
X      tmp->next = Level->site[x][y].things;
X      Level->site[x][y].things = tmp;
X    }
X    else if (Level->site[x][y].p_locf == L_VOID_STATION)
X      setgamestatus(PREPARED_VOID);
X}  
X
X
X/* returns a string for identified items */
Xchar *itemid(obj)
Xpob obj;
X{
X  char tstr[80];
X  if (obj->objchar==CASH){
X    strcpy(Str4,obj->truename);
X    return(Str4);
X  }
X  else {
X    if (Objects[obj->id].known > obj->known)
X      obj->known = Objects[obj->id].known;
X    
X    setnumstr(obj,tstr);
X    strcpy(Str4,tstr);
X    if (obj->known == 0)
X      strcat(Str4,obj->objstr);
X    else if (obj->known == 1)
X      strcat(Str4,obj->truename);
X    else {
X      if (obj->blessing < 0) {
X	strcat(Str4, "cursed ");
X	strcat(Str4, obj->cursestr);
X      }
X      else if (obj->blessing > 0) {
X	strcat(Str4, "blessed ");
X	strcat(Str4, obj->truename);
X      }
X      else strcat(Str4,obj->truename);
X      if (obj->number > 1) strcat(Str4,"s");
X      switch (obj->objchar) {
X      case STICK: 
X        setchargestr(obj,tstr);
X	strcat(Str4,tstr);
X	break;
X      case MISSILEWEAPON:
X      case ARMOR:
X      case RING:
X      case SHIELD:
X      case WEAPON: 
X	setplustr(obj,tstr);
X	strcat(Str4, tstr);
X	break;
X      default: strcat(Str4,""); break;
X      }
X    }
X    return(Str4);
X  }
X}
X
Xchar *cashstr()
X{
X  if (difficulty() < 3) return("copper pieces");
X  else if (difficulty() < 5) return("silver pieces");
X  else if (difficulty() < 7) return("gold pieces");
X  else if (difficulty() < 8) return("semiprecious gems");
X  else if (difficulty() < 9) return("mithril pieces");
X  else if (difficulty() < 10) return("precious gems"); 
X  else return("orichalc pieces");
X}
X
X/* return an object's plus as a string */
Xvoid setplustr(obj,pstr)
Xpob obj;
Xchar *pstr;
X{
X  pstr[0] = ' ';
X  pstr[1] = (obj->plus < 0 ? '-' : '+' );
X  if (abs(obj->plus) < 10) {
X    pstr[2] = '0' + abs(obj->plus);
X    pstr[3] = 0;
X  }
X  else {
X    pstr[2] = '0' + abs(obj->plus / 10);
X    pstr[3] = '0' + abs(obj->plus % 10);
X    pstr[4] = 0;
X  }
X}
X
X/* return an object's number as a string */
Xvoid setnumstr(obj,nstr)
Xpob obj;
Xchar *nstr;
X{
X  if (obj->number < 2)
X    nstr[0] = 0;
X  else if (obj->number < 10) {
X    nstr[0] = '0' + obj->number;
X    nstr[1] = 'x';
X    nstr[2] = ' ';
X    nstr[3] = 0;
X  }
X  else if (obj->number < 41) {
X    nstr[0] = '0' + ((int)(obj->number / 10));
X    nstr[1] = '0' + (obj->number % 10);
X    nstr[2] = 'x';
X    nstr[3] = ' ';
X    nstr[4] = 0;
X  }
X  else strcpy(nstr,"lots of ");
X}
X
X
X
X
X/* return object with charges */
Xvoid setchargestr(obj,cstr)
Xpob obj;
Xchar *cstr;
X{
X  cstr[0] = ' ';
X  cstr[1] = '[';
X  if (obj->charge < 0) {
X    cstr[2]='d';
X    cstr[3]='e';
X    cstr[4]='a';
X    cstr[5]='d';
X    cstr[6]=']';
X    cstr[7]=0;
X  }
X  else if (obj->charge < 10) {
X    cstr[2] = '0' + obj->charge;
X    cstr[3] = ']';
X    cstr[4] = 0;
X  }
X  else {
X    cstr[2] = '0' + ((int)(obj->charge / 10));
X    cstr[3] = '0' + (obj->charge % 10);
X    cstr[4] = ']';
X    cstr[5] = 0;
X  }
X}
X
X
Xvoid give_money(m)
Xstruct monster *m;
X{
X  pob cash;
X
X  cash = detach_money();
X  if (cash == NULL)
X    setgamestatus(SKIP_MONSTERS);
X  else givemonster(m,cash);
X}
X    
X    
X
Xvoid givemonster(m,o)
Xstruct monster *m;
Xstruct object *o;
X{
X  /* special case -- give gem to LawBringer */
X  if ((m->id == ML10+2) && (o->id == ARTIFACTID+21)) {
X    clearmsg();
X    print1("The LawBringer accepts the gem reverently.");
X    print2("He raises it above his head, where it bursts into lambent flame!");
X    morewait();
X    print1("You are bathed in a shimmering golden light.");
X    print2("You feel embedded in an infinite matrix of ordered energy.");
X    morewait();
X    if (Imprisonment > 0)
X      Imprisonment = 0;
X    if (Player.rank[ORDER] == -1) {
X      print2("You have been forgiven. You feel like a Paladin....");
X      Player.rank[ORDER] = 1;
X    }
X    Player.alignment += 200;
X    Player.pow = Player.maxpow = Player.pow * 2;
X    gain_experience(2000);
X    setgamestatus(GAVE_STARGEM);
X  }
X  else {
X    if (m->uniqueness == COMMON) {
X      strcpy(Str3,"The ");
X      strcat(Str3,m->monstring);
X    }
X    else strcpy(Str3,m->monstring);
X    
X    if (m_statusp(m,GREEDY) || m_statusp(m,NEEDY)) {
X      m_pickup(m,o);
X      strcat(Str3," takes your gift");
X      print1(Str3);
X      Player.alignment++;
X      if (m_statusp(m,GREEDY) && (true_item_value(o) < m->level*100))
X      nprint1("...but does not appear satisfied.");
X      else if (m_statusp(m,NEEDY) && 
X	       (true_item_value(o) < Level->depth*Level->depth))
X	nprint1("...and looks chasteningly at you.");
X      else {
X	nprint1("...and seems happy with it.");
X	m_status_reset(m,HOSTILE);
X	m_status_reset(m,GREEDY);
X	m_status_reset(m,NEEDY);
X      }
X    }
X    else if (m_statusp(m,HUNGRY)) {
X      
X      if (((m->id == HORSE) && (o->id == FOODID+15)) || /* grain */
X	  ((m->id != HORSE) &&
X	   ((o->usef == I_FOOD) || (o->usef == I_POISON_FOOD)))) {
X	strcat(Str3," wolfs down your food ... ");
X	print1(Str3);
X	m_status_reset(m,HUNGRY);
X	m_status_reset(m,HOSTILE);
X	if  (o->usef == I_POISON_FOOD) {
X	  Player.alignment -= 2;
X	  nprint1("...and chokes on the poisoned ration!");
X	  morewait();
X	  m_status_set(m,HOSTILE);
X	  m_damage(m,100,POISON);
X	}
X	else nprint1("...and now seems satiated.");
X	morewait();
X	free((char *)o);
X      }
X      else {
X	strcat(Str3," spurns your offering and leaves it on the ground.");
X	print1(Str3);
X	drop_at(m->x,m->y,o);
X      }
X    }
X    else {
X      strcat(Str3," doesn't care for your offering and drops it.");
X      print1(Str3);
X      drop_at(m->x,m->y,o);
X    }
X  }
X}
X
X
X
X
X/* will clear all, not just one of an object. */
Xvoid conform_lost_object(obj)
Xpob obj;
X{
X  if (obj != NULL) conform_lost_objects(obj->number,obj);
X}
X
X
X
X/* removes n of object from inventory; frees object if appropriate. */
X
Xvoid dispose_lost_objects(n,obj)
Xint n;
Xpob obj;
X{
X  int i,conformed=FALSE,subtracted=FALSE,freetrue,freecurse;
X  if (obj != NULL) for(i=0;i<MAXITEMS;i++) 
X    if (Player.possessions[i] == obj) {
X      if (! subtracted) {
X	obj->number -= n;
X	subtracted = TRUE;
X      }
X      if (obj->number < 1) {
X	if (!conformed) {
X	  conform_unused_object(obj);
X	  conformed = TRUE;
X	}
X	Player.possessions[i] = NULL;
X      }
X    }
X  if (obj->number < 1) {
X#if 0
X    freetrue=(obj->objstr != obj->truename);
X    freecurse=((obj->objstr != obj->cursestr)&&
X	       (obj->cursestr != obj->truename));
X    free(obj->objstr);
X    if (freetrue) free(obj->truename);
X    if (freecurse) free(obj->cursestr);
X#endif
X    free((char *) obj);
X  }
X}
X
X
X
X
X/* removes n of object from inventory without freeing object.
X   Removes all instances of pointer (might be 2 handed weapon, etc) */
Xvoid conform_lost_objects(n,obj)
Xint n;
Xpob obj;
X{
X  int i,conformed=FALSE,subtracted=FALSE;
X  if (obj != NULL) for(i=0;i<MAXITEMS;i++) 
X    if (Player.possessions[i] == obj) {
X      if (! subtracted) {
X	obj->number -= n;
X	subtracted = TRUE;
X      }
X      if (obj->number < 1) {
X	if (!conformed) {
X	  conform_unused_object(obj);
X	  conformed = TRUE;
X	}
X	Player.possessions[i] = NULL;
X      }
X    }
X}
X
X
X/* clears unused possession */
Xvoid conform_unused_object(obj)
Xpob obj;
X{
X  if (obj->used) {
X    obj->used = FALSE;
X    item_use(obj);
X  }
X  calc_melee();
X}
X
X
X/* select an item from inventory */
X/* if itype is NULL, any kind of item is acceptable.
X   if itype is CASHVALUE, any kind of item or '$' (cash) is acceptable.
X   if itype is FOOD, CORPSE or FOOD is acceptable, but only FOOD is
Xlisted in the possibilities.
X   if itype is any other object type (eg SCROLL, POTION, etc.), only
Xthat type of item is acceptable or is listed */
X
Xint getitem(itype)
Xchar itype;
X{
X  char invstr[64];
X  char index;
X  int i,k=0,ok=FALSE,drewmenu=FALSE,found=FALSE;
X
X  found = ((itype == NULL) || ((itype == CASHVALUE) && (Player.cash > 0)));
X  invstr[0]=0;
X  for(i=1;i<MAXITEMS;i++)
X    if (Player.possessions[i] != NULL)
X      if ((itype == NULL) ||
X	  (itype == CASHVALUE) ||
X	  (Player.possessions[i]->objchar == itype) ||
X	  ((itype == FOOD) && (Player.possessions[i]->objchar == CORPSE))) {
X	   found = TRUE;
X	   invstr[k++] = 'a'+i-1;
X	   invstr[k] = 0;
X	 }
X  if ((itype == CASHVALUE) && found) {
X    invstr[k++] = '$';
X    invstr[k] = 0;
X  }
X  if (! found) {
X    print3("Nothing appropriate.");
X    return(ABORT);
X  }
X  else {
X    print2("Select an item [");
X    nprint2(invstr);
X    nprint2(",?] ");
X    while (! ok) {
X      index = mcigetc();
X      if (index == '?') {
X	drewmenu = TRUE;
X	menuclear();
X	for (i=1;i<MAXITEMS;i++)
X	  if (Player.possessions[i] != NULL)
X	    if ((itype == NULL) || 
X		(itype == CASHVALUE) ||
X		(Player.possessions[i]->objchar == itype) ||
X		((itype == FOOD) && 
X		 (Player.possessions[i]->objchar == CORPSE)))
X	      display_inventory_slot(i,FALSE);
X      }
X      else if (index == ESCAPE) ok = TRUE;
X      else if (index == CASH) {
X	if (itype == CASHVALUE) ok = TRUE;
X	else {
X	  print3("You cannot select cash now.");
X	  ok = FALSE;
X	}
X      }
X      else if (badobject(index) || (! strmem(index,invstr)))
X	print3("Nope! Try again [? for inventory, ESCAPE to quit]:");
X      else ok = TRUE;
X    }
X    if (drewmenu) xredraw();
X    if (index == ESCAPE) return(ABORT);
X    else if (index == CASH) return(CASHVALUE);
X    else return(index+1-'a');
X  }
X}
X
X
X/* true if the numerical index based on 'a' == 1 turns out to be either
Xout of the range of the possessions array or a null item */
Xint badobject(slotchar)
Xchar slotchar;
X{
X  int slot = slotchar + 1 - 'a';
X  if ((slot<1) || (slot >= MAXITEMS)) return(TRUE);
X  else return(Player.possessions[slot] == NULL);
X}
X
X
X#ifndef MSDOS
X/* this takes the numerical index directly for the same effect as badobject*/
Xint baditem(slotnum)
Xint slotnum;
X{
X  if ((slotnum<1) || (slotnum >= MAXITEMS)) return(TRUE);
X  else return(Player.possessions[slotnum] == NULL);
X}
X#endif
X    
X
X
X/* formerly add_item_to_pack */
Xvoid gain_item(o)
Xstruct object *o;
X{
X  if (o->objchar == CASH) {
X    print2("You gained some cash.");
X    Player.cash += o->basevalue;
X    free((char *)o->objstr);
X    free((char *)o);
X    dataprint();
X  }
X  else if (optionp(PACKADD)) {
X    if (! get_to_pack(o)) {
X      Player.possessions[O_UP_IN_AIR] = o;
X      top_inventory_control();
X    }
X  }
X  else {
X    Player.possessions[O_UP_IN_AIR] = o;
X    top_inventory_control();
X  }
X}
X
X
X/* Adds item to pack list */
Xvoid add_to_pack(o)
Xpob o;
X{
X  if (Player.packptr >= MAXPACK) {
X    print3("Your pack is full. The item drops to the ground.");
X    drop_at(Player.x,Player.y,o);
X  }
X  else {
X    Player.pack[Player.packptr++] = o;
X    print3("Putting item in pack.");
X  }
X}
X
X
X/* Adds item to pack list, maybe going into inventory mode if pack is full */
Xint get_to_pack(o)
Xpob o;
X{
X  if (Player.packptr >= MAXPACK) {
X    print3("Your pack is full.");
X    morewait();
X    return(FALSE);
X  }
X  else {
X    Player.pack[Player.packptr++] = o;
X    print3("Putting item in pack.");
X    return(TRUE);
X  }
X}
X
X
X
X/* takes something from pack, puts to slot, 
Xor to 'up-in-air', one of which at least must be empty */
Xvoid take_from_pack(slot,display)
Xint slot,display;
X{
X  char response;
X  int i,quit = FALSE,ok=TRUE,displayed=FALSE;
X  pob item;
X  if (Player.possessions[slot] != NULL) 
X    slot = O_UP_IN_AIR;
X  if (Player.possessions[slot] != NULL) 
X    print3("slot is not empty!");
X  else if (Player.packptr == 0)
X    print3("Pack is empty!");
X  else {
X    do {
X      ok = TRUE;
X      print1("Enter pack slot letter, or ? to show pack, or ESCAPE to quit.");
X      response = mgetc();
X      if (response == '?') {
X	display_pack();
X	displayed = TRUE;
X	ok = FALSE;
X      }
X      else if (response == ESCAPE) quit = TRUE;
X      else{
X	ok = ((response >= 'A') && (response < 'A'+Player.packptr));
X	if (ok) ok = slottable(Player.pack[response-'A'],slot);
X      }
X    } while (! ok);
X    if (! quit) {
X      if (response - 'A' > 10) {
X	print1("You begin to rummage through your pack.");
X	morewait();
X	Command_Duration += 10;
X      }
X      if (response - 'A' > 5) {
X	print1("You search your pack for the item.");
X	Command_Duration += 5;
X	morewait();
X      }
X      print1("You take the item from your pack.");
X      morewait();
X      Command_Duration += 2;
X      item = Player.possessions[slot] = Player.pack[response-'A'];
X      for(i=response-'A';i<Player.packptr-1;i++)
X	Player.pack[i] = Player.pack[i+1];
X      Player.pack[--Player.packptr] = NULL;
X      
X      if (item_useable(item,slot)) {
X	item->used = TRUE;
X	item_use(item);
X	morewait();
X	if (item->number > 1) pack_extra_items(item);
X      }
X    }
X  }
X  if (displayed) {
X    xredraw();
X    if (display) display_possessions();
X  }
X}
X
X
X
X
X#ifndef MSDOS
X/* General interface to inventory */
Xvoid item_inventory(topline)
Xint topline;
X{
X  if (topline) {
X    menuclear();
X    display_possessions();
X    inventory_control();
X  }
X  else top_inventory_control();
X}
X#endif
X
X
X
X
X
X/* inventory_control assumes a few setup things have been done,
X   like displaying the slots, loading the O_UP_IN_AIR item, etc.
X
X   Each action uses up a little time. If only inspection actions
X   are taken, no time is used up. */
X
X
Xvoid inventory_control()
X{
X  int slot = 0,done=FALSE;
X#ifndef MSDOS
X  char response;
X#else
X  int response;
X  int simple = 0;
X#endif
X  clearmsg3();
X  do {
X    checkclear();
X    print1("Action [^l,d,e,p,s,t,x,>,<,?,ESCAPE]:");
X    display_inventory_slot(slot,FALSE);
X    if (slot == O_WEAPON_HAND)
X      display_inventory_slot(O_READY_HAND,FALSE);
X    else if (slot == O_READY_HAND)
X      display_inventory_slot(O_WEAPON_HAND,FALSE);
X    display_inventory_slot(O_UP_IN_AIR,FALSE);
X    move_slot(slot,slot,MAXITEMS);
X    response = mcigetc();
X
X    switch(response) {
X    case 12:
X    case 18: /* ^l, ^r */
X      menuclear();
X      display_possessions();
X      break;
X    case 'd':
X      if (Player.possessions[O_UP_IN_AIR] != NULL) 
X	drop_from_slot(O_UP_IN_AIR);
X      else if (Player.possessions[slot] != NULL) 
X	drop_from_slot(slot);
X      else print3("Nothing in selected slot!");
X      Command_Duration++;
X      break; 
X    case 'p': 
X      if (Player.possessions[slot] != NULL) 
X	put_to_pack(slot);
X      Command_Duration+=5;
X      break;
X    case 's':
X      display_pack();
X      morewait();
X      xredraw();
X      display_possessions();
X      Command_Duration+=5;
X      break;
X    case 't': 
X      take_from_pack(slot,TRUE); 
X      Command_Duration+=5;
X      break;
X    case 'e':
X      switch_to_slot(slot);
X      Command_Duration+=2;
X      break;
X    case 'x':
X      switch_to_slot(slot);
X      Command_Duration+=2;
X      done = (Player.possessions[O_UP_IN_AIR] == NULL);
X      break;
X    case 'j':
X    case '>':
X#ifdef MSDOS
X    case KEY_DOWN:
X      simple = 1;
X#endif
X      slot = move_slot(slot,slot+1,MAXITEMS);
X      break;
X    case 'k':
X    case '<':
X#ifdef MSDOS
X    case KEY_UP:
X      simple = 1;
X#endif
X      slot = move_slot(slot,slot-1,MAXITEMS);
X      break;
X#ifdef MSDOS
X    case KEY_HOME:
X      slot = move_slot(slot,0,MAXITEMS);
X      break;
X    case KEY_LL:
X      slot = move_slot(slot,MAXITEMS-1,MAXITEMS);
X      break;
X#endif
X    case '?':
X      inv_help();
X      display_possessions();
X      break;
X    case ESCAPE:
X      if (Player.possessions[O_UP_IN_AIR] != NULL) {
X	drop_at(Player.x,Player.y,Player.possessions[O_UP_IN_AIR]);
X	Player.possessions[O_UP_IN_AIR] = NULL;
X	print3("Object 'up in air' dropped.");
X      }
X      done = TRUE;
X      break;
X    }
X#ifndef MSDOS
X    calc_melee();
X#else
X    if (!simple)
X      calc_melee();
X    else
X      simple = 0;
X#endif
X  } while (! done);
X  xredraw();
X}
X
X
X
X
X
X
X
X/* same as inventory_control, but only uses msg window for i/o*/
X
X
Xvoid top_inventory_control()
X{
X  int slot = 0,done=FALSE,usedmenu=FALSE;
X  char response;
X  clearmsg3();
X  do {
X    clearmsg1();
X    print1("Action [d,e,p,s,t,x,~,?,ESCAPE]:");
X    print2("'Up in air': ");
X    if (Player.possessions[O_UP_IN_AIR] == NULL) nprint2("NOTHING");
X    else nprint2(itemid(Player.possessions[O_UP_IN_AIR]));
X    response = mcigetc();
X
X    switch(response) {
X    case 'd':
X      if (Player.possessions[O_UP_IN_AIR] != NULL) 
X	drop_from_slot(O_UP_IN_AIR);
X      else {
X	slot = get_inventory_slot();
X	if (Player.possessions[slot] != NULL) 
X	  drop_from_slot(slot);
X	else print3("Nothing in selected slot!");
X      }
X      Command_Duration++;
X      break; 
X    case 'p': 
X      if (Player.possessions[O_UP_IN_AIR] == NULL)
X	slot = get_inventory_slot();
X      else slot = O_UP_IN_AIR;
X      put_to_pack(slot); 
X      Command_Duration+=5;
X      break;
X    case 's':
X      display_pack();
X      usedmenu = TRUE;      
X      Command_Duration+=5;
X      break;
X    case 't': 
X      slot = get_inventory_slot();
X      take_from_pack(slot,FALSE); 
X      Command_Duration+=5;
X      break;
X    case 'e':
X      slot = get_inventory_slot();
X      switch_to_slot(slot);
X      Command_Duration+=2;
X      break;
X    case 'x':
X      slot = get_inventory_slot();
X      switch_to_slot(slot);
X      Command_Duration+=2;
X      done = (Player.possessions[O_UP_IN_AIR] == NULL);
X      break;
X    case '~':
X      menuclear();
X      display_possessions();
X      inventory_control();
X      usedmenu = TRUE;
X      done = TRUE;
X      break;
X    case '?':
X      inv_help();
X      usedmenu=TRUE;
X      break;
X    case ESCAPE:
X      if (Player.possessions[O_UP_IN_AIR] != NULL) {
X	drop_at(Player.x,Player.y,Player.possessions[O_UP_IN_AIR]);
X	Player.possessions[O_UP_IN_AIR] = NULL;
X	print3("Object 'up in air' dropped.");
X      }
X      done = TRUE;
X      break;
X    }
X    calc_melee();
X  } while (! done);
X  if (usedmenu) 
X    xredraw();
X}
X
X
X
X
X
Xint get_inventory_slot()
X{
X  int ok;
X  char response;
X  do {
X    clearmsg1();
X    print1("Which inventory slot [a..o,*='up-in-air' slot]?");
X    response = mcigetc(); 
X    ok = ((response == '*') ||
X	  ((response >= 'a') && (response < 'a' + MAXITEMS - 1)));
X  } while (! ok);
X  return((response == '*') ? 0 : ((int) (response + 1 - 'a')));
X}
X    
X
X
X/* returns some number between 0 and o->number */
Xint get_item_number(o)
Xpob o;
X{
X  int n=0;
X  do {
X    print1("How many? -- max");
X    mnumprint(o->number);
X    nprint1(" :");
X    n = parsenum();
X    if (n>o->number) print3("Too many!");
X    else if (n<1) n = 0;
X  } while (n > o->number);
X  if (n < 1) n = 0;
X  return(n);
X}
X  
Xvoid drop_from_slot(slot)
Xint slot;
X{
X  int n,waitflag;
X  if (Player.possessions[slot] != NULL) {
X    if(cursed(Player.possessions[slot]) == TRUE + TRUE)
X      print3("It sticks to your fingers!");
X    else {
X      n = Player.possessions[slot]->number;
X      if (n > 1) n = get_item_number(Player.possessions[slot]);
X      if (n > 0) {
X	p_drop_at(Player.x,Player.y,n,Player.possessions[slot]);
X	waitflag = (Player.possessions[slot]->used && 
X		    (Player.possessions[slot]->number == n));
X	conform_lost_objects(n,Player.possessions[slot]);
X	if (waitflag) morewait();
X      }
X      else print3("Didn't drop anything.");
X    }
X  }
X  else print3("Didn't drop anything.");
X}
X
X
Xvoid put_to_pack(slot)
Xint slot;
X{
X  int waitflag,num = 1;
X  pob temp,oslot = Player.possessions[slot];
X  if (oslot == NULL) 
X    print3("Slot is empty!");
X  else if (cursed(oslot) == TRUE+TRUE)
X    print3("Item is cursed!");
X  else {
X    if (oslot->number > 1)
X      num = get_item_number(oslot);
X    if (num > 0) {
X      temp = split_item(num,oslot);
X      waitflag = (oslot->used && (oslot->number == num));
X      conform_lost_objects(num,oslot);
X      if (waitflag) morewait();
X      add_to_pack(temp);
X    }
X  }
X}
X
X
X/* splits num off of item to make newitem which is returned */
X/* something else (conform_lost_objects) has to reduce the actual
X   number value of item and Player.itemweight */
Xpob split_item(num,item)
Xint num;
Xpob item;
X{
X  pob newitem=NULL;
X  if (item != NULL) {
X    newitem = ((pob) malloc(sizeof(objtype)));
X    *newitem = *item;
X    if (num <= item->number)
X      newitem->number = num;
X    /* else num > item->number, so return NULL on this error condition */
X  }
X  return(newitem);
X}
X
X
X
X/* Trades contents of "up in air" slot with selected slot. One or both
Xmay be null. If both slots are 'objequal' merges two groups into one
Xin the selected slot. If one slot is null and the number of the other
Xis greater than one, requests how many to move. */
X
Xvoid switch_to_slot(slot)
Xint slot;
X{
X  pob oslot = Player.possessions[slot];
X  pob oair = Player.possessions[O_UP_IN_AIR];
X  pob otemp = NULL;
X  int slotnull,airnull,num=1,trade=FALSE,put=FALSE,take=FALSE,merge=FALSE;
X  int s2h=FALSE,a2h=FALSE;
X
X  /* ie, is cursed and in use */
X  if (slot == O_UP_IN_AIR)
X    print3("This action makes no sense!");
X  else if (cursed(oslot)==TRUE+TRUE)
X    print3("The object in that slot is cursed -- you can't get rid of it!");
X  else {
X
X    slotnull = (oslot == NULL); 
X    airnull = (oair == NULL);
X
X    if  (!slotnull)
X      s2h = (Player.possessions[O_READY_HAND] ==
X	     Player.possessions[O_WEAPON_HAND]);
X    
X    if (! airnull)
X      a2h = (twohandedp(oair->id) && 
X	     ((slot == O_READY_HAND) || (slot == O_WEAPON_HAND)));
X    
X    
X    /* figure out which action to take */
X
X    /* merge if both are same kind of object */
X    merge = objequal(oslot,oair);
X    
X    take = ((!merge) && (!slotnull) && airnull);
X    
X    put = ((!merge) && slotnull && (!airnull) && slottable(oair,slot));
X    
X    trade = ((!merge) && (!slotnull) && (!airnull) && slottable(oair,slot));
X    
X    if (merge) merge_item(slot);
X    
X    else if (put) {
X
X      /* deal with a 2-handed weapon */
X      if (a2h) {
X	if (Player.possessions[O_READY_HAND] == NULL)
X	  Player.possessions[O_READY_HAND] = oair;
X	if (Player.possessions[O_WEAPON_HAND] == NULL)
X	  Player.possessions[O_WEAPON_HAND] = oair;
X      }
X      else Player.possessions[slot] = oair;
X      Player.possessions[O_UP_IN_AIR] = NULL;
X      if (item_useable(oair,slot)) {
X	oair->used = TRUE;
X	item_use(oair);
X	morewait();
X	if (oair->number > 1) pack_extra_items(oair);
X      }
X      Player.possessions[O_UP_IN_AIR] = NULL;
X    }
X
X    else if (take) {
X      if (oslot->number > 1) {
X	num = get_item_number(oslot);
X      }
X      if (num > 0) {
X	otemp = split_item(num,oslot);
X	dispose_lost_objects(num,oslot);
X	Player.possessions[O_UP_IN_AIR] = otemp;
X	otemp->used = FALSE;
X      }
X    }
X
X    else if (trade) {
X      
X      /* first remove item from slot */
X      num = oslot->number;
X      conform_lost_objects(oslot->number,oslot);
X      oslot->number = num;
X      
X      Player.possessions[O_UP_IN_AIR] = oslot;
X
X      Player.possessions[slot] = oair;
X      
X      if (a2h) {
X	if (Player.possessions[O_READY_HAND] == NULL)
X	  Player.possessions[O_READY_HAND] = oair;
X	if (Player.possessions[O_WEAPON_HAND] == NULL)
X	  Player.possessions[O_WEAPON_HAND] = oair;
X      }
X      
X      if (item_useable(oair,slot)) {
X	oair->used = TRUE;
X	item_use(oair);
X	morewait();
X	if (oair->number > 1) pack_extra_items(oair);
X      }
X    }
X  }
X}
X
X
X
X
X/* merges the up-in-air items into the selected items */
X
Xvoid merge_item(slot)
Xint slot;
X{
X  Player.possessions[slot]->number +=
X    Player.possessions[O_UP_IN_AIR]->number;
X  Player.possessions[O_UP_IN_AIR] = NULL;
X}
X
X
X/* are two objects equal except for their number field? */
X/* returns false if either object is null */
Xint objequal(o,p)
Xstruct object *o,*p;
X{
X  if ((o == NULL) || (p == NULL)) return(FALSE);
X  else return(
X	 (o->id == p->id) &&
X	 (o->plus == p->plus) &&
X	 (o->charge == 0) &&
X	 (p->charge == 0) &&
X	 (o->dmg == p->dmg) &&
X	 (o->hit == p->hit) &&
X	 (o->aux == p->aux) &&
X	 (o->known == p->known) &&
X	 (o->blessing == p->blessing) &&
X	 (o->usef == p->usef)
X	 );
X
X}
X
X/* criteria for being able to put some item in some slot */
Xint slottable(o,slot)
Xpob o;
Xint slot;
X{
X  int ok = TRUE;
X  if (o == NULL) ok = FALSE;
X  else if (slot == O_ARMOR) {
X    if (o->objchar != ARMOR) {
X      print3("Only armor can go in the armor slot!");
X      ok = FALSE;
X    }
X  }
X  else if (slot == O_SHIELD) {
X    if (o->objchar != SHIELD) {
X      print3("Only a shield can go in the shield slot!");
X      ok = FALSE;
X    }
X  }
X  else if (slot == O_BOOTS) {
X    if (o->objchar != BOOTS) {
X      print3("Only boots can go in the boots slot!");
X      ok = FALSE;
X    }
X  }
X  else if (slot == O_CLOAK) {
X    if (o->objchar != CLOAK) {
X      print3("Only a cloak can go in the cloak slot!");
X      ok = FALSE;
X    }
X  }
X  else if (slot >= O_RING1) {
X    if (o->objchar != RING) {
X      print3("Only a ring can go in a ring slot!");
X      ok = FALSE;
X    }
X  }
X  return(ok);
X}
X
X
X
X/* whether or not an item o can be used in a slot. Assumes o can in
X   fact be placed in the slot. */
Xint item_useable(o,slot)
Xpob o;
Xint slot;
X{
X  /* don't have to check the object in the first if since only armor
X  can go in armor slot, cloak in cloak slot, etc */
X
X  if ((slot == O_ARMOR) || 
X      (slot == O_CLOAK) ||
X      (slot == O_SHIELD) ||
X      (slot == O_BOOTS) ||
X      (slot >= O_RING1))
X    return(TRUE);
X
X  /* weapon is useable if it is put in weapon hand or if it is two-handed
X     and put in either hand when the other also holds the weapon */
X
X  else if ((o->objchar == WEAPON) ||
X	   (o->objchar == MISSILEWEAPON)) {
X    if (twohandedp(o->id) && 
X	((slot==O_READY_HAND)||(slot==O_WEAPON_HAND))) {
X      if (Player.possessions[O_READY_HAND] == 
X	  Player.possessions[O_WEAPON_HAND]) {
X	print1("You heft the weapon and find you must use both hands.");
X	morewait();
X	return(TRUE);
X      }
X      else {
X	print1("This weapon is two-handed, so at the moment, ");
X	print2("you are just lugging it around....");
X	morewait();
X	return(FALSE);
X      }	
X    }
X    else return(slot == O_WEAPON_HAND);
X  }
X  else return(FALSE);
X}
X
X
X
X
X
X
X
X
X/* returns FALSE if not cursed, TRUE if cursed but not used,
X   TRUE + TRUE if cursed and used */
Xint cursed(obj)
Xpob obj;
X{
X  return((obj == NULL) ? 
X	 FALSE : 
X	 ((obj->blessing < 0) ? 
X	  (obj->used == TRUE) + TRUE : 
X	  FALSE));
X}
X
X
X
X
X/* returns true if item with id and charge is found in pack or in
X   inventory slot. charge is used to differentiate
X   corpses instead of aux, which is their food value. */
Xint find_item(o,id,chargeval)
Xint id,chargeval;
Xpob *o;
X{
X  int i,found=FALSE;
X  *o=NULL;
X  for(i=1;((i<MAXITEMS)&&(! found));i++)
X    if (Player.possessions[i] != NULL)
X      if ((Player.possessions[i]->id == id) &&
X	  ((chargeval == -1) ||
X	   (Player.possessions[i]->charge == chargeval))) {
X	*o = Player.possessions[i];
X	found = TRUE;
X      }
X  if (! found)
X    for(i=0;((i<Player.packptr)&&(! found));i++)
X      if (Player.pack[i] != NULL)
X	if ((Player.pack[i]->id == id) &&
X	    ((chargeval == -1) ||
X	     (Player.pack[i]->charge == chargeval))) {
X	  *o = Player.pack[i];
X	  found = TRUE;
X	}
X  return(found);
X}
X
X
X
X/* returns true if item with id and charge is found in pack or in
X   inventory slot. Destroys item. charge is used to differentiate
X   corpses instead of aux, which is their food value. */
Xint find_and_remove_item(id,chargeval)
Xint id,chargeval;
X{
X  int i,found=FALSE;
X  pob o=NULL;
X
X  for(i=1;((i<MAXITEMS)&&(! found));i++)
X    if (Player.possessions[i] != NULL)
X      if ((Player.possessions[i]->id == id) &&
X	  ((chargeval == -1) ||
X	   (Player.possessions[i]->charge == chargeval))) {
X	o = Player.possessions[i];
X	conform_lost_object(o);
X	found = TRUE;
X      }
X  if (! found) for(i=0;((i<Player.packptr)&&(! found));i++)
X    if (Player.pack[i] != NULL)
X      if ((Player.pack[i]->id == id) &&
X	  ((chargeval == -1) ||
X	   (Player.pack[i]->charge == chargeval))) {
X	free((char *)Player.pack[i]);
X	Player.pack[i] = NULL;
X	found = TRUE;
X      }
X  fixpack();
X  return(found);
X}
X
X
X
X
Xvoid lose_all_items()
X{
X  int i;
X  print1("You notice that you are completely devoid of all possessions.");
X  morewait();
X  for(i=0;i<MAXITEMS;i++)
X    if (Player.possessions[i] != NULL) {
X      dispose_lost_objects(Player.possessions[i]->number,
X			   Player.possessions[i]);
X      Player.possessions[i] = NULL;
X    }
X  for(i=0;i<MAXPACK;i++) {
X    if (Player.pack[i] != NULL) 
X      free((char *) Player.pack[i]);
X    Player.pack[i] = NULL;
X  }
X  Player.packptr = 0;
X  calc_melee();
X  morewait();
X}
X
X
X/* prevents people from wielding 3 short swords, etc. */
Xvoid pack_extra_items(item)
Xpob item;
X{
X  pob extra=((pob) malloc(sizeof(objtype)));
X  if (Player.packptr < MAXPACK) {
X    print3("Putting extra items back in pack.");
X    morewait();
X    *extra = *item;
X    extra->number = item->number-1;
X    item->number = 1;
X    Player.pack[Player.packptr++] = extra;
X  }
X  else if (Player.possessions[O_UP_IN_AIR] == NULL) {
X    print3("Extra copies of item are 'up in the air'");
X    Player.possessions[O_UP_IN_AIR] = extra;
X  }
X  else {
X    print3("No room for extra copies of item -- dropping them.");
X    drop_at(Player.x,Player.y,extra);
X  }
X  calc_melee();
X}
X
X
X/* makes sure Player.pack is OK, (used after sale from pack) */
Xvoid fixpack()
X{
X  pob tpack[MAXPACK];
X  int i,tctr=0;
X  for(i=0;i<MAXPACK;i++) tpack[i] = NULL;
X  for(i=0;i<MAXPACK;i++)
X    if (Player.pack[i]!=NULL) 
X      tpack[tctr++] = Player.pack[i];
X  for(i=0;i<MAXPACK;i++)
X    Player.pack[i]=tpack[i];
X  Player.packptr = tctr;
X
X}
END_OF_FILE
if test 31995 -ne `wc -c <'oinv.c'`; then
    echo shar: \"'oinv.c'\" unpacked with wrong size!
fi
# end of 'oinv.c'
fi
echo shar: End of archive 1 \(of 20\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 20 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0