HardenedBSD/games/hack/hack.invent.c
Jordan K. Hubbard 554eb505f8 Bring in the 4.4 Lite games directory, modulo man page changes and segregation
of the x11 based games.  I'm not going to tag the originals with bsd_44_lite
and do this in two stages since it's just not worth it for this collection,
and I've got directory renames to deal with that way.  Bleah.
Submitted by:	jkh
1994-09-04 04:03:31 +00:00

864 lines
18 KiB
C

/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* hack.invent.c - version 1.0.3 */
#include "hack.h"
#include <stdio.h>
extern struct obj *splitobj();
extern struct obj zeroobj;
extern char morc;
extern char quitchars[];
static char *xprname();
#ifndef NOWORM
#include "def.wseg.h"
extern struct wseg *wsegs[32];
#endif NOWORM
#define NOINVSYM '#'
static int lastinvnr = 51; /* 0 ... 51 */
static
assigninvlet(otmp)
register struct obj *otmp;
{
boolean inuse[52];
register int i;
register struct obj *obj;
for(i = 0; i < 52; i++) inuse[i] = FALSE;
for(obj = invent; obj; obj = obj->nobj) if(obj != otmp) {
i = obj->invlet;
if('a' <= i && i <= 'z') inuse[i - 'a'] = TRUE; else
if('A' <= i && i <= 'Z') inuse[i - 'A' + 26] = TRUE;
if(i == otmp->invlet) otmp->invlet = 0;
}
if((i = otmp->invlet) &&
(('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z')))
return;
for(i = lastinvnr+1; i != lastinvnr; i++) {
if(i == 52) { i = -1; continue; }
if(!inuse[i]) break;
}
otmp->invlet = (inuse[i] ? NOINVSYM :
(i < 26) ? ('a'+i) : ('A'+i-26));
lastinvnr = i;
}
struct obj *
addinv(obj)
register struct obj *obj;
{
register struct obj *otmp;
/* merge or attach to end of chain */
if(!invent) {
invent = obj;
otmp = 0;
} else
for(otmp = invent; /* otmp */; otmp = otmp->nobj) {
if(merged(otmp, obj, 0))
return(otmp);
if(!otmp->nobj) {
otmp->nobj = obj;
break;
}
}
obj->nobj = 0;
if(flags.invlet_constant) {
assigninvlet(obj);
/*
* The ordering of the chain is nowhere significant
* so in case you prefer some other order than the
* historical one, change the code below.
*/
if(otmp) { /* find proper place in chain */
otmp->nobj = 0;
if((invent->invlet ^ 040) > (obj->invlet ^ 040)) {
obj->nobj = invent;
invent = obj;
} else
for(otmp = invent; ; otmp = otmp->nobj) {
if(!otmp->nobj ||
(otmp->nobj->invlet ^ 040) > (obj->invlet ^ 040)){
obj->nobj = otmp->nobj;
otmp->nobj = obj;
break;
}
}
}
}
return(obj);
}
useup(obj)
register struct obj *obj;
{
if(obj->quan > 1){
obj->quan--;
obj->owt = weight(obj);
} else {
setnotworn(obj);
freeinv(obj);
obfree(obj, (struct obj *) 0);
}
}
freeinv(obj)
register struct obj *obj;
{
register struct obj *otmp;
if(obj == invent)
invent = invent->nobj;
else {
for(otmp = invent; otmp->nobj != obj; otmp = otmp->nobj)
if(!otmp->nobj) panic("freeinv");
otmp->nobj = obj->nobj;
}
}
/* destroy object in fobj chain (if unpaid, it remains on the bill) */
delobj(obj) register struct obj *obj; {
freeobj(obj);
unpobj(obj);
obfree(obj, (struct obj *) 0);
}
/* unlink obj from chain starting with fobj */
freeobj(obj) register struct obj *obj; {
register struct obj *otmp;
if(obj == fobj) fobj = fobj->nobj;
else {
for(otmp = fobj; otmp->nobj != obj; otmp = otmp->nobj)
if(!otmp) panic("error in freeobj");
otmp->nobj = obj->nobj;
}
}
/* Note: freegold throws away its argument! */
freegold(gold) register struct gold *gold; {
register struct gold *gtmp;
if(gold == fgold) fgold = gold->ngold;
else {
for(gtmp = fgold; gtmp->ngold != gold; gtmp = gtmp->ngold)
if(!gtmp) panic("error in freegold");
gtmp->ngold = gold->ngold;
}
free((char *) gold);
}
deltrap(trap)
register struct trap *trap;
{
register struct trap *ttmp;
if(trap == ftrap)
ftrap = ftrap->ntrap;
else {
for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ;
ttmp->ntrap = trap->ntrap;
}
free((char *) trap);
}
struct wseg *m_atseg;
struct monst *
m_at(x,y)
register x,y;
{
register struct monst *mtmp;
#ifndef NOWORM
register struct wseg *wtmp;
#endif NOWORM
m_atseg = 0;
for(mtmp = fmon; mtmp; mtmp = mtmp->nmon){
if(mtmp->mx == x && mtmp->my == y)
return(mtmp);
#ifndef NOWORM
if(mtmp->wormno){
for(wtmp = wsegs[mtmp->wormno]; wtmp; wtmp = wtmp->nseg)
if(wtmp->wx == x && wtmp->wy == y){
m_atseg = wtmp;
return(mtmp);
}
}
#endif NOWORM
}
return(0);
}
struct obj *
o_at(x,y)
register x,y;
{
register struct obj *otmp;
for(otmp = fobj; otmp; otmp = otmp->nobj)
if(otmp->ox == x && otmp->oy == y) return(otmp);
return(0);
}
struct obj *
sobj_at(n,x,y)
register n,x,y;
{
register struct obj *otmp;
for(otmp = fobj; otmp; otmp = otmp->nobj)
if(otmp->ox == x && otmp->oy == y && otmp->otyp == n)
return(otmp);
return(0);
}
carried(obj) register struct obj *obj; {
register struct obj *otmp;
for(otmp = invent; otmp; otmp = otmp->nobj)
if(otmp == obj) return(1);
return(0);
}
carrying(type)
register int type;
{
register struct obj *otmp;
for(otmp = invent; otmp; otmp = otmp->nobj)
if(otmp->otyp == type)
return(TRUE);
return(FALSE);
}
struct obj *
o_on(id, objchn) unsigned int id; register struct obj *objchn; {
while(objchn) {
if(objchn->o_id == id) return(objchn);
objchn = objchn->nobj;
}
return((struct obj *) 0);
}
struct trap *
t_at(x,y)
register x,y;
{
register struct trap *trap = ftrap;
while(trap) {
if(trap->tx == x && trap->ty == y) return(trap);
trap = trap->ntrap;
}
return(0);
}
struct gold *
g_at(x,y)
register x,y;
{
register struct gold *gold = fgold;
while(gold) {
if(gold->gx == x && gold->gy == y) return(gold);
gold = gold->ngold;
}
return(0);
}
/* make dummy object structure containing gold - for temporary use only */
struct obj *
mkgoldobj(q)
register long q;
{
register struct obj *otmp;
otmp = newobj(0);
/* should set o_id etc. but otmp will be freed soon */
otmp->olet = '$';
u.ugold -= q;
OGOLD(otmp) = q;
flags.botl = 1;
return(otmp);
}
/*
* getobj returns:
* struct obj *xxx: object to do something with.
* (struct obj *) 0 error return: no object.
* &zeroobj explicitly no object (as in w-).
*/
struct obj *
getobj(let,word)
register char *let,*word;
{
register struct obj *otmp;
register char ilet,ilet1,ilet2;
char buf[BUFSZ];
char lets[BUFSZ];
register int foo = 0, foo2;
register char *bp = buf;
xchar allowcnt = 0; /* 0, 1 or 2 */
boolean allowgold = FALSE;
boolean allowall = FALSE;
boolean allownone = FALSE;
xchar foox = 0;
long cnt;
if(*let == '0') let++, allowcnt = 1;
if(*let == '$') let++, allowgold = TRUE;
if(*let == '#') let++, allowall = TRUE;
if(*let == '-') let++, allownone = TRUE;
if(allownone) *bp++ = '-';
if(allowgold) *bp++ = '$';
if(bp > buf && bp[-1] == '-') *bp++ = ' ';
ilet = 'a';
for(otmp = invent; otmp; otmp = otmp->nobj){
if(!*let || index(let, otmp->olet)) {
bp[foo++] = flags.invlet_constant ? otmp->invlet : ilet;
/* ugly check: remove inappropriate things */
if((!strcmp(word, "take off") &&
!(otmp->owornmask & (W_ARMOR - W_ARM2)))
|| (!strcmp(word, "wear") &&
(otmp->owornmask & (W_ARMOR | W_RING)))
|| (!strcmp(word, "wield") &&
(otmp->owornmask & W_WEP))) {
foo--;
foox++;
}
}
if(ilet == 'z') ilet = 'A'; else ilet++;
}
bp[foo] = 0;
if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0;
(void) strcpy(lets, bp); /* necessary since we destroy buf */
if(foo > 5) { /* compactify string */
foo = foo2 = 1;
ilet2 = bp[0];
ilet1 = bp[1];
while(ilet = bp[++foo2] = bp[++foo]){
if(ilet == ilet1+1){
if(ilet1 == ilet2+1)
bp[foo2 - 1] = ilet1 = '-';
else if(ilet2 == '-') {
bp[--foo2] = ++ilet1;
continue;
}
}
ilet2 = ilet1;
ilet1 = ilet;
}
}
if(!foo && !allowall && !allowgold && !allownone) {
pline("You don't have anything %sto %s.",
foox ? "else " : "", word);
return(0);
}
for(;;) {
if(!buf[0])
pline("What do you want to %s [*]? ", word);
else
pline("What do you want to %s [%s or ?*]? ",
word, buf);
cnt = 0;
ilet = readchar();
while(digit(ilet) && allowcnt) {
if (cnt < 100000000)
cnt = 10*cnt + (ilet - '0');
else
cnt = 999999999;
allowcnt = 2; /* signal presence of cnt */
ilet = readchar();
}
if(digit(ilet)) {
pline("No count allowed with this command.");
continue;
}
if(index(quitchars,ilet))
return((struct obj *)0);
if(ilet == '-') {
return(allownone ? &zeroobj : (struct obj *) 0);
}
if(ilet == '$') {
if(!allowgold){
pline("You cannot %s gold.", word);
continue;
}
if(!(allowcnt == 2 && cnt < u.ugold))
cnt = u.ugold;
return(mkgoldobj(cnt));
}
if(ilet == '?') {
doinv(lets);
if(!(ilet = morc)) continue;
/* he typed a letter (not a space) to more() */
} else if(ilet == '*') {
doinv((char *) 0);
if(!(ilet = morc)) continue;
/* ... */
}
if(flags.invlet_constant) {
for(otmp = invent; otmp; otmp = otmp->nobj)
if(otmp->invlet == ilet) break;
} else {
if(ilet >= 'A' && ilet <= 'Z') ilet += 'z'-'A'+1;
ilet -= 'a';
for(otmp = invent; otmp && ilet;
ilet--, otmp = otmp->nobj) ;
}
if(!otmp) {
pline("You don't have that object.");
continue;
}
if(cnt < 0 || otmp->quan < cnt) {
pline("You don't have that many! [You have %u]"
, otmp->quan);
continue;
}
break;
}
if(!allowall && let && !index(let,otmp->olet)) {
pline("That is a silly thing to %s.",word);
return(0);
}
if(allowcnt == 2) { /* cnt given */
if(cnt == 0) return(0);
if(cnt != otmp->quan) {
register struct obj *obj;
obj = splitobj(otmp, (int) cnt);
if(otmp == uwep) setuwep(obj);
}
}
return(otmp);
}
ckunpaid(otmp) register struct obj *otmp; {
return( otmp->unpaid );
}
/* interactive version of getobj - used for Drop and Identify */
/* return the number of times fn was called successfully */
ggetobj(word, fn, max)
char *word;
int (*fn)(), max;
{
char buf[BUFSZ];
register char *ip;
register char sym;
register int oletct = 0, iletct = 0;
register boolean allflag = FALSE;
char olets[20], ilets[20];
int (*ckfn)() = (int (*)()) 0;
xchar allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; /* BAH */
if(!invent && !allowgold){
pline("You have nothing to %s.", word);
return(0);
} else {
register struct obj *otmp = invent;
register int uflg = 0;
if(allowgold) ilets[iletct++] = '$';
ilets[iletct] = 0;
while(otmp) {
if(!index(ilets, otmp->olet)){
ilets[iletct++] = otmp->olet;
ilets[iletct] = 0;
}
if(otmp->unpaid) uflg = 1;
otmp = otmp->nobj;
}
ilets[iletct++] = ' ';
if(uflg) ilets[iletct++] = 'u';
if(invent) ilets[iletct++] = 'a';
ilets[iletct] = 0;
}
pline("What kinds of thing do you want to %s? [%s] ",
word, ilets);
getlin(buf);
if(buf[0] == '\033') {
clrlin();
return(0);
}
ip = buf;
olets[0] = 0;
while(sym = *ip++){
if(sym == ' ') continue;
if(sym == '$') {
if(allowgold == 1)
(*fn)(mkgoldobj(u.ugold));
else if(!u.ugold)
pline("You have no gold.");
allowgold = 2;
} else
if(sym == 'a' || sym == 'A') allflag = TRUE; else
if(sym == 'u' || sym == 'U') ckfn = ckunpaid; else
if(index("!%?[()=*/\"0", sym)){
if(!index(olets, sym)){
olets[oletct++] = sym;
olets[oletct] = 0;
}
}
else pline("You don't have any %c's.", sym);
}
if(allowgold == 2 && !oletct)
return(1); /* he dropped gold (or at least tried to) */
else
return(askchain(invent, olets, allflag, fn, ckfn, max));
}
/*
* Walk through the chain starting at objchn and ask for all objects
* with olet in olets (if nonNULL) and satisfying ckfn (if nonNULL)
* whether the action in question (i.e., fn) has to be performed.
* If allflag then no questions are asked. Max gives the max nr of
* objects to be treated. Return the number of objects treated.
*/
askchain(objchn, olets, allflag, fn, ckfn, max)
struct obj *objchn;
register char *olets;
int allflag;
int (*fn)(), (*ckfn)();
int max;
{
register struct obj *otmp, *otmp2;
register char sym, ilet;
register int cnt = 0;
ilet = 'a'-1;
for(otmp = objchn; otmp; otmp = otmp2){
if(ilet == 'z') ilet = 'A'; else ilet++;
otmp2 = otmp->nobj;
if(olets && *olets && !index(olets, otmp->olet)) continue;
if(ckfn && !(*ckfn)(otmp)) continue;
if(!allflag) {
pline(xprname(otmp, ilet));
addtopl(" [nyaq]? ");
sym = readchar();
}
else sym = 'y';
switch(sym){
case 'a':
allflag = 1;
case 'y':
cnt += (*fn)(otmp);
if(--max == 0) goto ret;
case 'n':
default:
break;
case 'q':
goto ret;
}
}
pline(cnt ? "That was all." : "No applicable objects.");
ret:
return(cnt);
}
obj_to_let(obj) /* should of course only be called for things in invent */
register struct obj *obj;
{
register struct obj *otmp;
register char ilet;
if(flags.invlet_constant)
return(obj->invlet);
ilet = 'a';
for(otmp = invent; otmp && otmp != obj; otmp = otmp->nobj)
if(++ilet > 'z') ilet = 'A';
return(otmp ? ilet : NOINVSYM);
}
prinv(obj)
register struct obj *obj;
{
pline(xprname(obj, obj_to_let(obj)));
}
static char *
xprname(obj,let)
register struct obj *obj;
register char let;
{
static char li[BUFSZ];
(void) sprintf(li, "%c - %s.",
flags.invlet_constant ? obj->invlet : let,
doname(obj));
return(li);
}
ddoinv()
{
doinv((char *) 0);
return(0);
}
/* called with 0 or "": all objects in inventory */
/* otherwise: all objects with (serial) letter in lets */
doinv(lets)
register char *lets;
{
register struct obj *otmp;
register char ilet;
int ct = 0;
char any[BUFSZ];
morc = 0; /* just to be sure */
if(!invent){
pline("Not carrying anything.");
return;
}
cornline(0, (char *) 0);
ilet = 'a';
for(otmp = invent; otmp; otmp = otmp->nobj) {
if(flags.invlet_constant) ilet = otmp->invlet;
if(!lets || !*lets || index(lets, ilet)) {
cornline(1, xprname(otmp, ilet));
any[ct++] = ilet;
}
if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A';
}
any[ct] = 0;
cornline(2, any);
}
dotypeinv () /* free after Robert Viduya */
/* Changed to one type only, so he doesnt have to type cr */
{
char c, ilet;
char stuff[BUFSZ];
register int stct;
register struct obj *otmp;
boolean billx = inshop() && doinvbill(0);
boolean unpd = FALSE;
if (!invent && !u.ugold && !billx) {
pline ("You aren't carrying anything.");
return(0);
}
stct = 0;
if(u.ugold) stuff[stct++] = '$';
stuff[stct] = 0;
for(otmp = invent; otmp; otmp = otmp->nobj) {
if (!index (stuff, otmp->olet)) {
stuff[stct++] = otmp->olet;
stuff[stct] = 0;
}
if(otmp->unpaid)
unpd = TRUE;
}
if(unpd) stuff[stct++] = 'u';
if(billx) stuff[stct++] = 'x';
stuff[stct] = 0;
if(stct > 1) {
pline ("What type of object [%s] do you want an inventory of? ",
stuff);
c = readchar();
if(index(quitchars,c)) return(0);
} else
c = stuff[0];
if(c == '$')
return(doprgold());
if(c == 'x' || c == 'X') {
if(billx)
(void) doinvbill(1);
else
pline("No used-up objects on the shopping bill.");
return(0);
}
if((c == 'u' || c == 'U') && !unpd) {
pline("You are not carrying any unpaid objects.");
return(0);
}
stct = 0;
ilet = 'a';
for (otmp = invent; otmp; otmp = otmp -> nobj) {
if(flags.invlet_constant) ilet = otmp->invlet;
if (c == otmp -> olet || (c == 'u' && otmp -> unpaid))
stuff[stct++] = ilet;
if(!flags.invlet_constant) if(++ilet > 'z') ilet = 'A';
}
stuff[stct] = '\0';
if(stct == 0)
pline("You have no such objects.");
else
doinv (stuff);
return(0);
}
/* look at what is here */
dolook() {
register struct obj *otmp, *otmp0;
register struct gold *gold;
char *verb = Blind ? "feel" : "see";
int ct = 0;
if(!u.uswallow) {
if(Blind) {
pline("You try to feel what is lying here on the floor.");
if(Levitation) { /* ab@unido */
pline("You cannot reach the floor!");
return(1);
}
}
otmp0 = o_at(u.ux, u.uy);
gold = g_at(u.ux, u.uy);
}
if(u.uswallow || (!otmp0 && !gold)) {
pline("You %s no objects here.", verb);
return(!!Blind);
}
cornline(0, "Things that are here:");
for(otmp = otmp0; otmp; otmp = otmp->nobj) {
if(otmp->ox == u.ux && otmp->oy == u.uy) {
ct++;
cornline(1, doname(otmp));
if(Blind && otmp->otyp == DEAD_COCKATRICE && !uarmg) {
pline("Touching the dead cockatrice is a fatal mistake ...");
pline("You die ...");
killer = "dead cockatrice";
done("died");
}
}
}
if(gold) {
char gbuf[30];
(void) sprintf(gbuf, "%ld gold piece%s",
gold->amount, plur(gold->amount));
if(!ct++)
pline("You %s here %s.", verb, gbuf);
else
cornline(1, gbuf);
}
if(ct == 1 && !gold) {
pline("You %s here %s.", verb, doname(otmp0));
cornline(3, (char *) 0);
}
if(ct > 1)
cornline(2, (char *) 0);
return(!!Blind);
}
stackobj(obj) register struct obj *obj; {
register struct obj *otmp = fobj;
for(otmp = fobj; otmp; otmp = otmp->nobj) if(otmp != obj)
if(otmp->ox == obj->ox && otmp->oy == obj->oy &&
merged(obj,otmp,1))
return;
}
/* merge obj with otmp and delete obj if types agree */
merged(otmp,obj,lose) register struct obj *otmp, *obj; {
if(obj->otyp == otmp->otyp &&
obj->unpaid == otmp->unpaid &&
obj->spe == otmp->spe &&
obj->dknown == otmp->dknown &&
obj->cursed == otmp->cursed &&
(index("%*?!", obj->olet) ||
(obj->known == otmp->known &&
(obj->olet == WEAPON_SYM && obj->otyp < BOOMERANG)))) {
otmp->quan += obj->quan;
otmp->owt += obj->owt;
if(lose) freeobj(obj);
obfree(obj,otmp); /* free(obj), bill->otmp */
return(1);
} else return(0);
}
/*
* Gold is no longer displayed; in fact, when you have a lot of money,
* it may take a while before you have counted it all.
* [Bug: d$ and pickup still tell you how much it was.]
*/
extern int (*occupation)();
extern char *occtxt;
static long goldcounted;
countgold(){
if((goldcounted += 100*(u.ulevel + 1)) >= u.ugold) {
long eps = 0;
if(!rn2(2)) eps = rnd((int) (u.ugold/100 + 1));
pline("You probably have about %ld gold pieces.",
u.ugold + eps);
return(0); /* done */
}
return(1); /* continue */
}
doprgold(){
if(!u.ugold)
pline("You do not carry any gold.");
else if(u.ugold <= 500)
pline("You are carrying %ld gold pieces.", u.ugold);
else {
pline("You sit down in order to count your gold pieces.");
goldcounted = 500;
occupation = countgold;
occtxt = "counting your gold";
}
return(1);
}
/* --- end of gold counting section --- */
doprwep(){
if(!uwep) pline("You are empty handed.");
else prinv(uwep);
return(0);
}
doprarm(){
if(!uarm && !uarmg && !uarms && !uarmh)
pline("You are not wearing any armor.");
else {
char lets[6];
register int ct = 0;
if(uarm) lets[ct++] = obj_to_let(uarm);
if(uarm2) lets[ct++] = obj_to_let(uarm2);
if(uarmh) lets[ct++] = obj_to_let(uarmh);
if(uarms) lets[ct++] = obj_to_let(uarms);
if(uarmg) lets[ct++] = obj_to_let(uarmg);
lets[ct] = 0;
doinv(lets);
}
return(0);
}
doprring(){
if(!uleft && !uright)
pline("You are not wearing any rings.");
else {
char lets[3];
register int ct = 0;
if(uleft) lets[ct++] = obj_to_let(uleft);
if(uright) lets[ct++] = obj_to_let(uright);
lets[ct] = 0;
doinv(lets);
}
return(0);
}
digit(c) char c; {
return(c >= '0' && c <= '9');
}