summaryrefslogtreecommitdiffstats
path: root/phantasia
diff options
context:
space:
mode:
authorjtc <jtc@NetBSD.org>1994-10-21 21:19:39 +0000
committerjtc <jtc@NetBSD.org>1994-10-21 21:19:39 +0000
commitbcced3608bdd120315d4ed1d9b9478f7caa4a2f1 (patch)
tree1b68a06d7ec808c1e35e59a8f41e3e609e18490a /phantasia
parent77e00dbff29bb84ca1625007d722e38a00e6f601 (diff)
downloadbsdgames-darwin-bcced3608bdd120315d4ed1d9b9478f7caa4a2f1.tar.gz
bsdgames-darwin-bcced3608bdd120315d4ed1d9b9478f7caa4a2f1.tar.zst
bsdgames-darwin-bcced3608bdd120315d4ed1d9b9478f7caa4a2f1.zip
phantasia(6), from 44lite
Diffstat (limited to 'phantasia')
-rw-r--r--phantasia/COPYRIGHT24
-rw-r--r--phantasia/Makefile30
-rw-r--r--phantasia/OWNER6
-rw-r--r--phantasia/README82
-rw-r--r--phantasia/convert.c210
-rw-r--r--phantasia/fight.c1688
-rw-r--r--phantasia/gamesupport.c722
-rw-r--r--phantasia/include.h17
-rw-r--r--phantasia/interplayer.c1208
-rw-r--r--phantasia/io.c436
-rw-r--r--phantasia/macros.h16
-rw-r--r--phantasia/main.c1288
-rw-r--r--phantasia/map.c160
-rw-r--r--phantasia/misc.c1703
-rw-r--r--phantasia/monsters.asc100
-rw-r--r--phantasia/oldplayer.h54
-rw-r--r--phantasia/pathnames.h44
-rw-r--r--phantasia/phantasia.61220
-rw-r--r--phantasia/phantdefs.h139
-rw-r--r--phantasia/phantglobs.c113
-rw-r--r--phantasia/phantglobs.h86
-rw-r--r--phantasia/phantstruct.h124
-rw-r--r--phantasia/setup.c261
23 files changed, 9731 insertions, 0 deletions
diff --git a/phantasia/COPYRIGHT b/phantasia/COPYRIGHT
new file mode 100644
index 00000000..71a58b3b
--- /dev/null
+++ b/phantasia/COPYRIGHT
@@ -0,0 +1,24 @@
+This entire subtree is explicitly not copyrighted.
+The following notice applies to all files found here. None of
+these files contain AT&T proprietary source code.
+_____________________________________________________________________________
+
+/* DISCLAIMER:
+ *
+ * This game is distributed for free as is. It is not guaranteed to work
+ * in every conceivable environment. It is not even guaranteed to work
+ * in ANY environment.
+ *
+ * This game is distributed without notice of copyright, therefore it
+ * may be used in any manner the recipient sees fit. However, the
+ * author assumes no responsibility for maintaining or revising this
+ * game, in its original form, or any derivitives thereof.
+ *
+ * The author shall not be responsible for any loss, cost, or damage,
+ * including consequential damage, caused by reliance on this material.
+ *
+ * The author makes no warranties, express or implied, including warranties
+ * of merchantability or fitness for a particular purpose or use.
+ *
+ * AT&T is in no way connected with this game.
+ */
diff --git a/phantasia/Makefile b/phantasia/Makefile
new file mode 100644
index 00000000..2c39e47c
--- /dev/null
+++ b/phantasia/Makefile
@@ -0,0 +1,30 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+
+PROG= phantasia
+SRCS= main.c fight.c io.c interplayer.c gamesupport.c misc.c phantglobs.c
+DPADD= ${LIBM} ${LIBCURSES} ${LIBTERM} ${LIBCOMPAT}
+LDADD= -lm -lcurses -ltermlib -lcompat
+HIDEGAME=hidegame
+MAN6= phantasia.0
+CLEANFILES+=map setup setup.o
+
+all: setup phantasia ${MAN6}
+
+setup: phantglobs.o setup.o monsters.asc ${LIBM}
+ ${CC} phantglobs.o setup.o -o ${.TARGET} -lm
+
+beforeinstall:
+ ./setup -m ${.CURDIR}/monsters.asc
+ chown games.bin /var/games/phantasia/*
+
+# Make Phantasia map. Change the map commands reflect your installation.
+# PLOTDEVICE is used for plotting the map. Change as appropriate.
+
+map: map.c
+ ${CC} -O ${.CURDIR}/map.c -lplot -o ${.TARGET}
+ ./map | plot > /dev/tty
+
+phantasia.0: phantasia.6
+ tbl ${.CURDIR}/phantasia.6 | nroff -man > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/phantasia/OWNER b/phantasia/OWNER
new file mode 100644
index 00000000..28387fee
--- /dev/null
+++ b/phantasia/OWNER
@@ -0,0 +1,6 @@
+ Edward Estes
+ AT&T
+ 5555 Touhy Ave.
+ Skokie, IL 60077
+ (312) 982-3969
+ ihnp4!ttrde!estes
diff --git a/phantasia/README b/phantasia/README
new file mode 100644
index 00000000..d56a6216
--- /dev/null
+++ b/phantasia/README
@@ -0,0 +1,82 @@
+June 25, 1986
+
+
+This is a much modified version of Phantasia. It is intended to fix
+all reported bug fixes, enhance the game, and speed up the game.
+
+I have to thank Chris Robertson for many ideas which have made the game
+faster, and more user-friendly. Most of her changes/additions are
+incorporated in this latest versions, although perhaps not in the exact
+manner of her design. I left out a few items which were not in keeping
+with the spirit of the game. (For example, I didn't like the extra lives
+and the pausing of the game. I think it's too easy even WITHOUT that stuff.)
+
+CHANGES:
+
+ - Wormholes have been deleted (I never liked them anyway).
+ - The source code has been greatly enhanced for speed, size, readability,
+ and maintainability.
+ fight.c should no longer cause optimizers to run out of space.
+ - A few loopholes have been tightened to make the game more enjoyable.
+ (Except for those who are in the habit of exercising those loopholes.)
+ - Chris' map is enclosed.
+ - The "charac" file is not compatible with older versions of Phantasia
+ (3.3.1 and 3.3.1+). A 'convert' program is provided to convert your
+ old file to the new format. See Makefile for details.
+ - Movements can be made with HJKL for WSNE, respectively.
+ - Players may examine others while playing ('x') option.
+ - Monsters are now stored in a binary data base, to speed calling
+ monsters, and to ease formatting of monster listings.
+ - Taxes are collected on all gold and gems.
+ - Dead players can be resurrected by the 'wizard'.
+ - 'setup' is smarter, although not as smart as it should be.
+ - Players can change their names and passwords
+
+PORTABILTY:
+
+ I have tried to make this as non-machine/system specific as possible.
+
+ All identifiers are unique to 7 characters or less, dual case.
+
+ The code WILL NOT fit on a 16-bit machine without separate I/D.
+
+ Stdio MUST support fopen() with mode "r+". I think this is true
+ for all Version 7 and later.
+
+ 'curses' library functions are required.
+
+ All problems/solutions with portability should be reported to me,
+ and fixes will be included in subsequent versions of this software.
+
+
+Please send me any bugs, (of which I am sure there are many), you may find,
+but PLEASE be specific. I cannot correct a bug which is described as:
+
+ "When I choose a character type, it blows up."
+
+ (What blows up? What exactly was printed at the terminal?
+ Which character type was chosen? Etc. . . ?)
+
+Also, please tell me which version of UN*X you are running, and upon
+which type of hardware.
+
+I will also do my best to help anyone with problems just trying to
+get the game running. Again, I need to know which version of UN*X
+and what type of CPU. Also, a copy of the output from 'make'
+would be extremely useful.
+
+Any and all ideas/suggestions/additions are more than welcome. If
+you feel strongly enough about it, write the change and send it to me,
+and I will do my best to incorporate it in the next version of Phantasia.
+Otherwise, I will give serious thought to adding it myself.
+
+Follow the directions in the Makefile CAREFULLY to set up the game.
+Read the comments at the beginning of 'main.c', if you haven't already.
+
+Enjoy.
+
+Ted Estes
+AT&T Information Systems
+Skokie, IL 60077
+
+...!ihnp4!ttrdc!ttrda!estes
diff --git a/phantasia/convert.c b/phantasia/convert.c
new file mode 100644
index 00000000..f46343f2
--- /dev/null
+++ b/phantasia/convert.c
@@ -0,0 +1,210 @@
+/*
+ * Convert Phantasia 3.3.1 and 3.3.1+ characs file format to 3.3.2
+ *
+ */
+
+#include "include.h"
+#include "oldplayer.h"
+
+struct oldplayer Oldplayer; /* old format structure */
+struct player Newplayer; /* new format structure */
+
+char Oldpfile[] = DEST/characs"; /* old format file */
+char Newpfile[] = DEST/newcharacs"; /* new format file */
+
+/************************************************************************
+/
+/ FUNCTION NAME: main()
+/
+/ FUNCTION: convert old Phantasia player file to new format
+/
+/ AUTHOR: C. Robertson, 9/1/85 E. A. Estes, 3/12/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: time(), exit(), fread(), fopen(), srandom(), floor(),
+/ random(), strcmp(), fwrite(), strcpy(), fclose(), fprintf()
+/
+/ GLOBAL INPUTS: _iob[], Oldplayer, Newplayer
+/
+/ GLOBAL OUTPUTS: Oldplayer, Newplayer
+/
+/ DESCRIPTION:
+/ Read in old player structures and write out to new file in
+/ new format.
+/ Old player file is unmodified.
+/ New file is "DEST/newcharacs".
+/ #define PHANTPLUS to convert from 3.3.1+.
+/
+/************************************************************************/
+
+main()
+{
+FILE *oldcharac, *newcharac; /* to open old and new files */
+
+ if ((oldcharac = fopen(Oldpfile, "r")) == NULL)
+ {
+ fprintf(stderr, "Cannot open original character file!\n");
+ exit(1);
+ }
+
+ if ((newcharac = fopen(Newpfile, "w")) == NULL)
+ {
+ fprintf(stderr, "Cannot create new character file!\n");
+ exit(1);
+ }
+
+ srandom((unsigned) time((long *) NULL)); /* prime random numbers */
+
+ while (fread((char *) &Oldplayer, sizeof(struct oldplayer), 1, oldcharac) == 1)
+ /* read and convert old structures into new */
+ {
+ Newplayer.p_experience = Oldplayer.o_experience;
+ Newplayer.p_level = (double) Oldplayer.o_level;
+ Newplayer.p_strength = Oldplayer.o_strength;
+ Newplayer.p_sword = Oldplayer.o_sword;
+ Newplayer.p_might = 0.0; /* game will calculate */
+ Newplayer.p_energy = Oldplayer.o_energy;
+ Newplayer.p_maxenergy = Oldplayer.o_maxenergy;
+ Newplayer.p_shield = Oldplayer.o_shield;
+ Newplayer.p_quickness = (double) Oldplayer.o_quickness;
+ Newplayer.p_quksilver = (double) Oldplayer.o_quksilver;
+ Newplayer.p_speed = 0.0; /* game will calculate */
+ Newplayer.p_magiclvl = Oldplayer.o_magiclvl;
+ Newplayer.p_mana = Oldplayer.o_mana;
+ Newplayer.p_brains = Oldplayer.o_brains;
+ Newplayer.p_poison = Oldplayer.o_poison;
+ Newplayer.p_gold = Oldplayer.o_gold;
+ Newplayer.p_gems = Oldplayer.o_gems;
+ Newplayer.p_sin = Oldplayer.o_sin;
+ Newplayer.p_x = Oldplayer.o_x;
+ Newplayer.p_y = Oldplayer.o_y;
+ Newplayer.p_1scratch = Oldplayer.o_1scratch;
+ Newplayer.p_2scratch = Oldplayer.o_2scratch;
+
+ Newplayer.p_ring.ring_type = Oldplayer.o_ring.ring_type;
+ Newplayer.p_ring.ring_duration = Oldplayer.o_ring.ring_duration;
+ Newplayer.p_ring.ring_inuse = FALSE;
+
+ Newplayer.p_age = (long) Oldplayer.o_degenerated * N_AGE;
+
+ Newplayer.p_degenerated = Oldplayer.o_degenerated + 1;
+
+ /* convert character type into character type and special type */
+
+ if (Oldplayer.o_type < 0)
+ /* player with crown */
+ Oldplayer.o_type = -Oldplayer.o_type;
+
+ if (Oldplayer.o_type == 99)
+ /* valar */
+ {
+ Newplayer.p_specialtype = SC_VALAR;
+ Newplayer.p_type = (short) ROLL(C_MAGIC, C_EXPER - C_MAGIC + 1);
+ Newplayer.p_lives = Oldplayer.o_ring.ring_duration;
+ }
+ else if (Oldplayer.o_type == 90)
+ /* ex-valar */
+ {
+ Newplayer.p_specialtype = SC_EXVALAR;
+ Newplayer.p_type = (short) ROLL(C_MAGIC, C_EXPER - C_MAGIC + 1);
+ Newplayer.p_lives = 0;
+ }
+ else if (Oldplayer.o_type > 20)
+ /* council of wise */
+ {
+ Newplayer.p_specialtype = SC_COUNCIL;
+ Newplayer.p_type = Oldplayer.o_type - 20;
+ Newplayer.p_lives = Oldplayer.o_ring.ring_duration;
+ }
+ else if (Oldplayer.o_type > 10)
+ /* king */
+ {
+ Newplayer.p_specialtype = SC_KING;
+ Newplayer.p_type = Oldplayer.o_type - 10;
+ Newplayer.p_lives = 0;
+ }
+ else
+ /* normal player */
+ {
+ Newplayer.p_specialtype = SC_NONE;
+ Newplayer.p_type = Oldplayer.o_type;
+ Newplayer.p_lives = 0;
+ }
+
+ Newplayer.p_lives = 0;
+ Newplayer.p_crowns = Oldplayer.o_crowns;
+ Newplayer.p_charms = Oldplayer.o_charms;
+ Newplayer.p_amulets = Oldplayer.o_amulets;
+ Newplayer.p_holywater = Oldplayer.o_holywater;
+ Newplayer.p_lastused = Oldplayer.o_lastused;
+
+ /* convert status and name into status */
+
+ Newplayer.p_status = Oldplayer.o_status + S_OFF;
+ if (strcmp(Oldplayer.m_name, "<null>") == 0)
+ /* unused recored */
+ Newplayer.p_status = S_NOTUSED;
+ if (Oldplayer.o_quickness < 0)
+ /* hung up player */
+ {
+ Newplayer.p_quickness = (double) Oldplayer.o_tampered;
+ Oldplayer.o_tampered = T_OFF;
+ Newplayer.p_status = S_HUNGUP;
+ }
+
+ Newplayer.p_tampered = Oldplayer.o_tampered + T_OFF;
+ Newplayer.p_istat = I_OFF;
+
+ Newplayer.p_palantir = Oldplayer.o_palantir;
+ Newplayer.p_blessing = Oldplayer.o_blessing;
+ Newplayer.p_virgin = Oldplayer.o_virgin;
+ Newplayer.p_blindness = Oldplayer.o_blindness;
+
+ strcpy(Newplayer.p_name, Oldplayer.o_name);
+ strcpy(Newplayer.p_password, Oldplayer.o_password);
+ strcpy(Newplayer.p_login, Oldplayer.o_login);
+
+ /* write new structure */
+ fwrite((char *) &Newplayer, sizeof(Newplayer), 1, newcharac);
+ }
+
+ fclose(oldcharac); /* close files */
+ fclose(newcharac);
+
+ exit(0);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: drandom()
+/
+/ FUNCTION: return a random number between 0.0 < 1.0
+/
+/ AUTHOR: E. A. Estes, 2/7/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: random number
+/
+/ MODULES CALLED: random()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Return a random number.
+/
+/************************************************************************/
+
+double
+drandom()
+{
+ if (sizeof(int) != 2)
+ return((double) (random() & 0x7fff) / 32768.0);
+ else
+ return((double) random() / 32768.0);
+}
diff --git a/phantasia/fight.c b/phantasia/fight.c
new file mode 100644
index 00000000..4038d1db
--- /dev/null
+++ b/phantasia/fight.c
@@ -0,0 +1,1688 @@
+/*
+ * fight.c Phantasia monster fighting routines
+ */
+
+#include "include.h"
+
+/************************************************************************
+/
+/ FUNCTION NAME: encounter()
+/
+/ FUNCTION: monster battle routine
+/
+/ AUTHOR: E. A. Estes, 2/20/86
+/
+/ ARGUMENTS:
+/ int particular - particular monster to fight if >= 0
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: monsthits(), playerhits(), readmessage(), callmonster(),
+/ writerecord(), pickmonster(), displaystats(), pow(), cancelmonster(),
+/ awardtreasure(), more(), death(), wmove(), setjmp(), drandom(), printw(),
+/ longjmp(), wrefresh(), mvprintw(), wclrtobot()
+/
+/ GLOBAL INPUTS: Curmonster, Whichmonster, LINES, Lines, Circle, Shield,
+/ Player, *stdscr, Fileloc, Fightenv[], *Enemyname
+/
+/ GLOBAL OUTPUTS: Curmonster, Whichmonster, Lines, Shield, Player, Luckout
+/
+/ DESCRIPTION:
+/ Choose a monster and check against some special types.
+/ Arbitrate between monster and player. Watch for either
+/ dying.
+/
+/************************************************************************/
+
+encounter(particular)
+int particular;
+{
+bool firsthit = Player.p_blessing; /* set if player gets the first hit */
+int flockcnt = 1; /* how many time flocked */
+
+ /* let others know what we are doing */
+ Player.p_status = S_MONSTER;
+ writerecord(&Player, Fileloc);
+
+#ifdef SYS5
+ flushinp();
+#endif
+
+ Shield = 0.0; /* no shield up yet */
+
+ if (particular >= 0)
+ /* monster is specified */
+ Whichmonster = particular;
+ else
+ /* pick random monster */
+ Whichmonster = pickmonster();
+
+ setjmp(Fightenv); /* this is to enable changing fight state */
+
+ move(6, 0);
+ clrtobot(); /* clear bottom area of screen */
+
+ Lines = 9;
+ callmonster(Whichmonster); /* set up monster to fight */
+
+ Luckout = FALSE; /* haven't tried to luckout yet */
+
+ if (Curmonster.m_type == SM_MORGOTH)
+ mvprintw(4, 0, "You've encountered %s, Bane of the Council and Valar.\n",
+ Enemyname);
+
+ if (Curmonster.m_type == SM_UNICORN)
+ {
+ if (Player.p_virgin)
+ {
+ printw("You just subdued %s, thanks to the virgin.\n", Enemyname);
+ Player.p_virgin = FALSE;
+ }
+ else
+ {
+ printw("You just saw %s running away!\n", Enemyname);
+ Curmonster.m_experience = 0.0;
+ Curmonster.m_treasuretype = 0;
+ }
+ }
+ else
+ /* not a special monster */
+ for (;;)
+ /* print header, and arbitrate between player and monster */
+ {
+ mvprintw(6, 0, "You are being attacked by %s, EXP: %.0f (Size: %.0f)\n",
+ Enemyname, Curmonster.m_experience, Circle);
+
+ displaystats();
+ mvprintw(1, 26, "%20.0f", Player.p_energy + Shield); /* overprint energy */
+ readmessage();
+
+ if (Curmonster.m_type == SM_DARKLORD
+ && Player.p_blessing
+ && Player.p_charms > 0)
+ /* overpower Dark Lord with blessing and charm */
+ {
+ mvprintw(7, 0, "You just overpowered %s!", Enemyname);
+ Lines = 8;
+ Player.p_blessing = FALSE;
+ --Player.p_charms;
+ break;
+ }
+
+ /* allow paralyzed monster to wake up */
+ Curmonster.m_speed = MIN(Curmonster.m_speed + 1.0, Curmonster.m_maxspeed);
+
+ if (drandom() * Curmonster.m_speed > drandom() * Player.p_speed
+ /* monster is faster */
+ && Curmonster.m_type != SM_DARKLORD
+ /* not D. L. */
+ && Curmonster.m_type != SM_SHRIEKER
+ /* not mimic */
+ && !firsthit)
+ /* monster gets a hit */
+ monsthits();
+ else
+ /* player gets a hit */
+ {
+ firsthit = FALSE;
+ playerhits();
+ }
+
+ refresh();
+
+ if (Lines > LINES - 2)
+ /* near bottom of screen - pause */
+ {
+ more(Lines);
+ move(Lines = 8, 0);
+ clrtobot();
+ }
+
+ if (Player.p_energy <= 0.0)
+ /* player died */
+ {
+ more(Lines);
+ death(Enemyname);
+ cancelmonster();
+ break; /* fight ends if the player is saved from death */
+ }
+
+ if (Curmonster.m_energy <= 0.0)
+ /* monster died */
+ break;
+ }
+
+ /* give player credit for killing monster */
+ Player.p_experience += Curmonster.m_experience;
+
+ if (drandom() < Curmonster.m_flock / 100.0)
+ /* monster flocks */
+ {
+ more(Lines);
+ ++flockcnt;
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+ }
+ else if (Circle > 1.0
+ && Curmonster.m_treasuretype > 0
+ && drandom() > 0.2 + pow(0.4, (double) (flockcnt / 3 + Circle / 3.0)))
+ /* monster has treasure; this takes # of flocks and size into account */
+ {
+ more(Lines);
+ awardtreasure();
+ }
+
+ /* pause before returning */
+ getyx(stdscr, Lines, flockcnt);
+ more(Lines + 1);
+
+ Player.p_ring.ring_inuse = FALSE; /* not using ring */
+
+ /* clean up the screen */
+ move(4, 0);
+ clrtobot();
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: pickmonster()
+/
+/ FUNCTION: choose a monster based upon where we are
+/
+/ AUTHOR: E. A. Estes, 2/20/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: monster number to call
+/
+/ MODULES CALLED: floor(), drandom()
+/
+/ GLOBAL INPUTS: Marsh, Circle, Player
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Certain monsters can be found in certain areas of the grid.
+/ We take care of rolling them here.
+/ Unfortunately, this routine assumes that the monster data
+/ base is arranged in a particular order. If the data base
+/ is altered (to add monsters, or make them tougher), this
+/ routine may also need to be changed.
+/
+/************************************************************************/
+
+pickmonster()
+{
+ if (Player.p_specialtype == SC_VALAR)
+ /* even chance of any monster */
+ return((int) ROLL(0.0, 100.0));
+
+ if (Marsh)
+ /* water monsters */
+ return((int) ROLL(0.0, 15.0));
+
+ else if (Circle > 24)
+ /* even chance of all non-water monsters */
+ return((int) ROLL(14.0, 86.0));
+
+ else if (Circle > 15)
+ /* chance of all non-water monsters, weighted toward middle */
+ return((int) (ROLL(0.0, 50.0) + ROLL(14.0, 37.0)));
+
+ else if (Circle > 8)
+ /* not all non-water monsters, weighted toward middle */
+ return((int) (ROLL(0.0, 50.0) + ROLL(14.0, 26.0)));
+
+ else if (Circle > 3)
+ /* even chance of some tamer non-water monsters */
+ return((int) ROLL(14.0, 50.0));
+
+ else
+ /* even chance of some of the tamest non-water monsters */
+ return((int) ROLL(14.0, 25.0));
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: playerhits()
+/
+/ FUNCTION: prompt player for action in monster battle, and process
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: hitmonster(), throwspell(), inputoption(), cancelmonster(),
+/ floor(), wmove(), drandom(), altercoordinates(), waddstr(), mvprintw(),
+/ wclrtoeol(), wclrtobot()
+/
+/ GLOBAL INPUTS: Curmonster, Lines, Player, *stdscr, Luckout, *Enemyname
+/
+/ GLOBAL OUTPUTS: Curmonster, Lines, Player, Luckout
+/
+/ DESCRIPTION:
+/ Process all monster battle options.
+/
+/************************************************************************/
+
+playerhits()
+{
+double inflict; /* damage inflicted */
+int ch; /* input */
+
+ mvaddstr(7, 0, "1:Melee 2:Skirmish 3:Evade 4:Spell 5:Nick ");
+
+ if (!Luckout)
+ /* haven't tried to luckout yet */
+ if (Curmonster.m_type == SM_MORGOTH)
+ /* cannot luckout against Morgoth */
+ addstr("6:Ally ");
+ else
+ addstr("6:Luckout ");
+
+ if (Player.p_ring.ring_type != R_NONE)
+ /* player has a ring */
+ addstr("7:Use Ring ");
+ else
+ clrtoeol();
+
+ ch = inputoption();
+
+ move(8, 0);
+ clrtobot(); /* clear any messages from before */
+ Lines = 9;
+ mvaddstr(4, 0, "\n\n"); /* clear status area */
+
+ switch (ch)
+ {
+ case 'T': /* timeout; lose turn */
+ break;
+
+ case ' ':
+ case '1': /* melee */
+ /* melee affects monster's energy and strength */
+ inflict = ROLL(Player.p_might / 2.0 + 5.0, 1.3 * Player.p_might)
+ + (Player.p_ring.ring_inuse ? Player.p_might : 0.0);
+
+ Curmonster.m_melee += inflict;
+ Curmonster.m_strength = Curmonster.m_o_strength
+ - Curmonster.m_melee / Curmonster.m_o_energy
+ * Curmonster.m_o_strength / 4.0;
+ hitmonster(inflict);
+ break;
+
+ case '2': /* skirmish */
+ /* skirmish affects monter's energy and speed */
+ inflict = ROLL(Player.p_might / 3.0 + 3.0, 1.1 * Player.p_might)
+ + (Player.p_ring.ring_inuse ? Player.p_might : 0.0);
+
+ Curmonster.m_skirmish += inflict;
+ Curmonster.m_maxspeed = Curmonster.m_o_speed
+ - Curmonster.m_skirmish / Curmonster.m_o_energy
+ * Curmonster.m_o_speed / 4.0;
+ hitmonster(inflict);
+ break;
+
+ case '3': /* evade */
+ /* use brains and speed to try to evade */
+ if ((Curmonster.m_type == SM_DARKLORD
+ || Curmonster.m_type == SM_SHRIEKER
+ /* can always run from D. L. and shrieker */
+ || drandom() * Player.p_speed * Player.p_brains
+ > drandom() * Curmonster.m_speed * Curmonster.m_brains)
+ && (Curmonster.m_type != SM_MIMIC))
+ /* cannot run from mimic */
+ {
+ mvaddstr(Lines++, 0, "You got away!");
+ cancelmonster();
+ altercoordinates(0.0, 0.0, A_NEAR);
+ }
+ else
+ mvprintw(Lines++, 0, "%s is still after you!", Enemyname);
+
+ break;
+
+ case 'M':
+ case '4': /* magic spell */
+ throwspell();
+ break;
+
+ case '5': /* nick */
+ /* hit 1 plus sword; give some experience */
+ inflict = 1.0 + Player.p_sword;
+ Player.p_experience += floor(Curmonster.m_experience / 10.0);
+ Curmonster.m_experience *= 0.92;
+ /* monster gets meaner */
+ Curmonster.m_maxspeed += 2.0;
+ Curmonster.m_speed = (Curmonster.m_speed < 0.0) ? 0.0 : Curmonster.m_speed + 2.0;
+ if (Curmonster.m_type == SM_DARKLORD)
+ /* Dark Lord; doesn't like to be nicked */
+ {
+ mvprintw(Lines++, 0,
+ "You hit %s %.0f times, and made him mad!", Enemyname, inflict);
+ Player.p_quickness /= 2.0;
+ altercoordinates(0.0, 0.0, A_FAR);
+ cancelmonster();
+ }
+ else
+ hitmonster(inflict);
+ break;
+
+ case 'B':
+ case '6': /* luckout */
+ if (Luckout)
+ mvaddstr(Lines++, 0, "You already tried that.");
+ else
+ {
+ Luckout = TRUE;
+ if (Curmonster.m_type == SM_MORGOTH)
+ /* Morgoth; ally */
+ {
+ if (drandom() < Player.p_sin / 100.0)
+ {
+ mvprintw(Lines++, 0, "%s accepted!", Enemyname);
+ cancelmonster();
+ }
+ else
+ mvaddstr(Lines++, 0, "Nope, he's not interested.");
+ }
+ else
+ /* normal monster; use brains for success */
+ {
+ if ((drandom() + 0.333) * Player.p_brains
+ < (drandom() + 0.333) * Curmonster.m_brains)
+ mvprintw(Lines++, 0, "You blew it, %s.", Player.p_name);
+ else
+ {
+ mvaddstr(Lines++, 0, "You made it!");
+ Curmonster.m_energy = 0.0;
+ }
+ }
+ }
+ break;
+
+ case '7': /* use ring */
+ if (Player.p_ring.ring_type != R_NONE)
+ {
+ mvaddstr(Lines++, 0, "Now using ring.");
+ Player.p_ring.ring_inuse = TRUE;
+ if (Player.p_ring.ring_type != R_DLREG)
+ /* age ring */
+ --Player.p_ring.ring_duration;
+ }
+ break;
+ }
+
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: monsthits()
+/
+/ FUNCTION: process a monster hitting the player
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: cancelmonster(), scramblestats(), more(), floor(), wmove(),
+/ drandom(), altercoordinates(), longjmp(), waddstr(), mvprintw(),
+/ getanswer()
+/
+/ GLOBAL INPUTS: Curmonster, Lines, Circle, Shield, Player, *stdscr,
+/ Fightenv[], *Enemyname
+/
+/ GLOBAL OUTPUTS: Curmonster, Whichmonster, Lines, Shield, Player,
+/ *Enemyname
+/
+/ DESCRIPTION:
+/ Handle all special monsters here. If the monster is not a special
+/ one, simply roll a hit against the player.
+/
+/************************************************************************/
+
+monsthits()
+{
+double inflict; /* damage inflicted */
+int ch; /* input */
+
+ switch (Curmonster.m_type)
+ /* may be a special monster */
+ {
+ case SM_DARKLORD:
+ /* hits just enough to kill player */
+ inflict = (Player.p_energy + Shield) * 1.02;
+ goto SPECIALHIT;
+
+ case SM_SHRIEKER:
+ /* call a big monster */
+ mvaddstr(Lines++, 0,
+ "Shrieeeek!! You scared it, and it called one of its friends.");
+ more(Lines);
+ Whichmonster = (int) ROLL(70.0, 30.0);
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+
+ case SM_BALROG:
+ /* take experience away */
+ inflict = ROLL(10.0, Curmonster.m_strength);
+ inflict = MIN(Player.p_experience, inflict);
+ mvprintw(Lines++, 0,
+ "%s took away %.0f experience points.", Enemyname, inflict);
+ Player.p_experience -= inflict;
+ return;
+
+ case SM_FAERIES:
+ if (Player.p_holywater > 0)
+ /* holy water kills when monster tries to hit */
+ {
+ mvprintw(Lines++, 0, "Your holy water killed it!");
+ --Player.p_holywater;
+ Curmonster.m_energy = 0.0;
+ return;
+ }
+ break;
+
+ case SM_NONE:
+ /* normal hit */
+ break;
+
+ default:
+ if (drandom() > 0.2)
+ /* normal hit */
+ break;
+
+ /* else special things */
+ switch (Curmonster.m_type)
+ {
+ case SM_LEANAN:
+ /* takes some of the player's strength */
+ inflict = ROLL(1.0, (Circle - 1.0) / 2.0);
+ inflict = MIN(Player.p_strength, inflict);
+ mvprintw(Lines++, 0, "%s sapped %0.f of your strength!",
+ Enemyname, inflict);
+ Player.p_strength -= inflict;
+ Player.p_might -= inflict;
+ break;
+
+ case SM_SARUMAN:
+ if (Player.p_palantir)
+ /* take away palantir */
+ {
+ mvprintw(Lines++, 0, "Wormtongue stole your palantir!");
+ Player.p_palantir = FALSE;
+ }
+ else if (drandom() > 0.5)
+ /* gems turn to gold */
+ {
+ mvprintw(Lines++, 0,
+ "%s transformed your gems into gold!", Enemyname);
+ Player.p_gold += Player.p_gems;
+ Player.p_gems = 0.0;
+ }
+ else
+ /* scramble some stats */
+ {
+ mvprintw(Lines++, 0, "%s scrambled your stats!", Enemyname);
+ scramblestats();
+ }
+ break;
+
+ case SM_THAUMATURG:
+ /* transport player */
+ mvprintw(Lines++, 0, "%s transported you!", Enemyname);
+ altercoordinates(0.0, 0.0, A_FAR);
+ cancelmonster();
+ break;
+
+ case SM_VORTEX:
+ /* suck up some mana */
+ inflict = ROLL(0, 7.5 * Circle);
+ inflict = MIN(Player.p_mana, floor(inflict));
+ mvprintw(Lines++, 0,
+ "%s sucked up %.0f of your mana!", Enemyname, inflict);
+ Player.p_mana -= inflict;
+ break;
+
+ case SM_NAZGUL:
+ /* try to take ring if player has one */
+ if (Player.p_ring.ring_type != R_NONE)
+ /* player has a ring */
+ {
+ mvaddstr(Lines++, 0, "Will you relinguish your ring ? ");
+ ch = getanswer("YN", FALSE);
+ if (ch == 'Y')
+ /* take ring away */
+ {
+ Player.p_ring.ring_type = R_NONE;
+ Player.p_ring.ring_inuse = FALSE;
+ cancelmonster();
+ break;
+ }
+ }
+
+ /* otherwise, take some brains */
+ mvprintw(Lines++, 0,
+ "%s neutralized 1/5 of your brain!", Enemyname);
+ Player.p_brains *= 0.8;
+ break;
+
+ case SM_TIAMAT:
+ /* take some gold and gems */
+ mvprintw(Lines++, 0,
+ "%s took half your gold and gems and flew off.", Enemyname);
+ Player.p_gold /= 2.0;
+ Player.p_gems /= 2.0;
+ cancelmonster();
+ break;
+
+ case SM_KOBOLD:
+ /* steal a gold piece and run */
+ mvprintw(Lines++, 0,
+ "%s stole one gold piece and ran away.", Enemyname);
+ Player.p_gold = MAX(0.0, Player.p_gold - 1.0);
+ cancelmonster();
+ break;
+
+ case SM_SHELOB:
+ /* bite and (medium) poison */
+ mvprintw(Lines++, 0,
+ "%s has bitten and poisoned you!", Enemyname);
+ Player.p_poison -= 1.0;
+ break;
+
+ case SM_LAMPREY:
+ /* bite and (small) poison */
+ mvprintw(Lines++, 0, "%s bit and poisoned you!", Enemyname);
+ Player.p_poison += 0.25;
+ break;
+
+ case SM_BONNACON:
+ /* fart and run */
+ mvprintw(Lines++, 0, "%s farted and scampered off.", Enemyname);
+ Player.p_energy /= 2.0; /* damage from fumes */
+ cancelmonster();
+ break;
+
+ case SM_SMEAGOL:
+ if (Player.p_ring.ring_type != R_NONE)
+ /* try to steal ring */
+ {
+ mvprintw(Lines++, 0,
+ "%s tried to steal your ring, ", Enemyname);
+ if (drandom() > 0.1)
+ addstr("but was unsuccessful.");
+ else
+ {
+ addstr("and ran away with it!");
+ Player.p_ring.ring_type = R_NONE;
+ cancelmonster();
+ }
+ }
+ break;
+
+ case SM_SUCCUBUS:
+ /* inflict damage through shield */
+ inflict = ROLL(15.0, Circle * 10.0);
+ inflict = MIN(inflict, Player.p_energy);
+ mvprintw(Lines++, 0, "%s sapped %.0f of your energy.",
+ Enemyname, inflict);
+ Player.p_energy -= inflict;
+ break;
+
+ case SM_CERBERUS:
+ /* take all metal treasures */
+ mvprintw(Lines++, 0,
+ "%s took all your metal treasures!", Enemyname);
+ Player.p_crowns = 0;
+ Player.p_sword =
+ Player.p_shield =
+ Player.p_gold = 0.0;
+ cancelmonster();
+ break;
+
+ case SM_UNGOLIANT:
+ /* (large) poison and take a quickness */
+ mvprintw(Lines++, 0,
+ "%s poisoned you, and took one quik.", Enemyname);
+ Player.p_poison += 5.0;
+ Player.p_quickness -= 1.0;
+ break;
+
+ case SM_JABBERWOCK:
+ /* fly away, and leave either a Jubjub bird or Bonnacon */
+ mvprintw(Lines++, 0,
+ "%s flew away, and left you to contend with one of its friends.",
+ Enemyname);
+ Whichmonster = 55 + (drandom() > 0.5) ? 22 : 0;
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+
+ case SM_TROLL:
+ /* partially regenerate monster */
+ mvprintw(Lines++, 0,
+ "%s partially regenerated his energy.!", Enemyname);
+ Curmonster.m_energy +=
+ floor((Curmonster.m_o_energy - Curmonster.m_energy) / 2.0);
+ Curmonster.m_strength = Curmonster.m_o_strength;
+ Curmonster.m_melee = Curmonster.m_skirmish = 0.0;
+ Curmonster.m_maxspeed = Curmonster.m_o_speed;
+ break;
+
+ case SM_WRAITH:
+ if (!Player.p_blindness)
+ /* make blind */
+ {
+ mvprintw(Lines++, 0, "%s blinded you!", Enemyname);
+ Player.p_blindness = TRUE;
+ Enemyname = "A monster";
+ }
+ break;
+ }
+ return;
+ }
+
+ /* fall through to here if monster inflicts a normal hit */
+ inflict = drandom() * Curmonster.m_strength + 0.5;
+SPECIALHIT:
+ mvprintw(Lines++, 0, "%s hit you %.0f times!", Enemyname, inflict);
+
+ if ((Shield -= inflict) < 0)
+ {
+ Player.p_energy += Shield;
+ Shield = 0.0;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: cancelmonster()
+/
+/ FUNCTION: mark current monster as no longer active
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: none
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: Curmonster
+/
+/ DESCRIPTION:
+/ Clear current monster's energy, experience, treasure type, and
+/ flock. This is the same as having the monster run away.
+/
+/************************************************************************/
+
+cancelmonster()
+{
+ Curmonster.m_energy = 0.0;
+ Curmonster.m_experience = 0.0;
+ Curmonster.m_treasuretype = 0;
+ Curmonster.m_flock = 0.0;
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: hitmonster()
+/
+/ FUNCTION: inflict damage upon current monster
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ double inflict - damage to inflict upon monster
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: monsthits(), wmove(), strcmp(), waddstr(), mvprintw()
+/
+/ GLOBAL INPUTS: Curmonster, Lines, Player, *stdscr, *Enemyname
+/
+/ GLOBAL OUTPUTS: Curmonster, Lines
+/
+/ DESCRIPTION:
+/ Hit monster specified number of times. Handle when monster dies,
+/ and a few special monsters.
+/
+/************************************************************************/
+
+hitmonster(inflict)
+double inflict;
+{
+ mvprintw(Lines++, 0, "You hit %s %.0f times!", Enemyname, inflict);
+ Curmonster.m_energy -= inflict;
+ if (Curmonster.m_energy > 0.0)
+ {
+ if (Curmonster.m_type == SM_DARKLORD || Curmonster.m_type == SM_SHRIEKER)
+ /* special monster didn't die */
+ monsthits();
+ }
+ else
+ /* monster died. print message. */
+ {
+ if (Curmonster.m_type == SM_MORGOTH)
+ mvaddstr(Lines++, 0, "You have defeated Morgoth, but he may return. . .");
+ else
+ /* all other types of monsters */
+ {
+ mvprintw(Lines++, 0, "You killed it. Good work, %s.", Player.p_name);
+
+ if (Curmonster.m_type == SM_MIMIC
+ && strcmp(Curmonster.m_name, "A Mimic") != 0
+ && !Player.p_blindness)
+ mvaddstr(Lines++, 0, "The body slowly changes into the form of a mimic.");
+ }
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: throwspell()
+/
+/ FUNCTION: throw a magic spell
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: hitmonster(), cancelmonster(), sqrt(), floor(), wmove(),
+/ drandom(), altercoordinates(), longjmp(), infloat(), waddstr(), mvprintw(),
+/ getanswer()
+/
+/ GLOBAL INPUTS: Curmonster, Whichmonster, Nomana[], Player, *stdscr,
+/ Fightenv[], Illspell[], *Enemyname
+/
+/ GLOBAL OUTPUTS: Curmonster, Whichmonster, Shield, Player
+/
+/ DESCRIPTION:
+/ Prompt player and process magic spells.
+/
+/************************************************************************/
+
+throwspell()
+{
+double inflict; /* damage inflicted */
+double dtemp; /* for dtemporary calculations */
+int ch; /* input */
+
+ mvaddstr(7, 0, "\n\n"); /* clear menu area */
+
+ if (Player.p_magiclvl >= ML_ALLORNOTHING)
+ mvaddstr(7, 0, "1:All or Nothing ");
+ if (Player.p_magiclvl >= ML_MAGICBOLT)
+ addstr("2:Magic Bolt ");
+ if (Player.p_magiclvl >= ML_FORCEFIELD)
+ addstr("3:Force Field ");
+ if (Player.p_magiclvl >= ML_XFORM)
+ addstr("4:Transform ");
+ if (Player.p_magiclvl >= ML_INCRMIGHT)
+ addstr("5:Increase Might\n");
+ if (Player.p_magiclvl >= ML_INVISIBLE)
+ mvaddstr(8, 0, "6:Invisibility ");
+ if (Player.p_magiclvl >= ML_XPORT)
+ addstr("7:Transport ");
+ if (Player.p_magiclvl >= ML_PARALYZE)
+ addstr("8:Paralyze ");
+ if (Player.p_specialtype >= SC_COUNCIL)
+ addstr("9:Specify");
+ mvaddstr(4, 0, "Spell ? ");
+
+ ch = getanswer(" ", TRUE);
+
+ mvaddstr(7, 0, "\n\n"); /* clear menu area */
+
+ if (Curmonster.m_type == SM_MORGOTH && ch != '3')
+ /* can only throw force field against Morgoth */
+ ILLSPELL();
+ else
+ switch (ch)
+ {
+ case '1': /* all or nothing */
+ if (drandom() < 0.25)
+ /* success */
+ {
+ inflict = Curmonster.m_energy * 1.01 + 1.0;
+
+ if (Curmonster.m_type == SM_DARKLORD)
+ /* all or nothing doesn't quite work against D. L. */
+ inflict *= 0.9;
+ }
+ else
+ /* failure -- monster gets stronger and quicker */
+ {
+ Curmonster.m_o_strength = Curmonster.m_strength *= 2.0;
+ Curmonster.m_maxspeed *= 2.0;
+ Curmonster.m_o_speed *= 2.0;
+
+ /* paralyzed monsters wake up a bit */
+ Curmonster.m_speed = MAX(1.0, Curmonster.m_speed * 2.0);
+ }
+
+ if (Player.p_mana >= MM_ALLORNOTHING)
+ /* take a mana if player has one */
+ Player.p_mana -= MM_ALLORNOTHING;
+
+ hitmonster(inflict);
+ break;
+
+ case '2': /* magic bolt */
+ if (Player.p_magiclvl < ML_MAGICBOLT)
+ ILLSPELL();
+ else
+ {
+ do
+ /* prompt for amount to expend */
+ {
+ mvaddstr(4, 0, "How much mana for bolt? ");
+ dtemp = floor(infloat());
+ }
+ while (dtemp < 0.0 || dtemp > Player.p_mana);
+
+ Player.p_mana -= dtemp;
+
+ if (Curmonster.m_type == SM_DARKLORD)
+ /* magic bolts don't work against D. L. */
+ inflict = 0.0;
+ else
+ inflict = dtemp * ROLL(15.0, sqrt(Player.p_magiclvl / 3.0 + 1.0));
+ mvaddstr(5, 0, "Magic Bolt fired!\n");
+ hitmonster(inflict);
+ }
+ break;
+
+ case '3': /* force field */
+ if (Player.p_magiclvl < ML_FORCEFIELD)
+ ILLSPELL();
+ else if (Player.p_mana < MM_FORCEFIELD)
+ NOMANA();
+ else
+ {
+ Player.p_mana -= MM_FORCEFIELD;
+ Shield = (Player.p_maxenergy + Player.p_shield) * 4.2 + 45.0;
+ mvaddstr(5, 0, "Force Field up.\n");
+ }
+ break;
+
+ case '4': /* transform */
+ if (Player.p_magiclvl < ML_XFORM)
+ ILLSPELL();
+ else if (Player.p_mana < MM_XFORM)
+ NOMANA();
+ else
+ {
+ Player.p_mana -= MM_XFORM;
+ Whichmonster = (int) ROLL(0.0, 100.0);
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+ }
+ break;
+
+ case '5': /* increase might */
+ if (Player.p_magiclvl < ML_INCRMIGHT)
+ ILLSPELL();
+ else if (Player.p_mana < MM_INCRMIGHT)
+ NOMANA();
+ else
+ {
+ Player.p_mana -= MM_INCRMIGHT;
+ Player.p_might +=
+ (1.2 * (Player.p_strength + Player.p_sword)
+ + 5.0 - Player.p_might) / 2.0;
+ mvprintw(5, 0, "New strength: %.0f\n", Player.p_might);
+ }
+ break;
+
+ case '6': /* invisible */
+ if (Player.p_magiclvl < ML_INVISIBLE)
+ ILLSPELL();
+ else if (Player.p_mana < MM_INVISIBLE)
+ NOMANA();
+ else
+ {
+ Player.p_mana -= MM_INVISIBLE;
+ Player.p_speed +=
+ (1.2 * (Player.p_quickness + Player.p_quksilver)
+ + 5.0 - Player.p_speed) / 2.0;
+ mvprintw(5, 0, "New quickness: %.0f\n", Player.p_speed);
+ }
+ break;
+
+ case '7': /* transport */
+ if (Player.p_magiclvl < ML_XPORT)
+ ILLSPELL();
+ else if (Player.p_mana < MM_XPORT)
+ NOMANA();
+ else
+ {
+ Player.p_mana -= MM_XPORT;
+ if (Player.p_brains + Player.p_magiclvl
+ < Curmonster.m_experience / 200.0 * drandom())
+ {
+ mvaddstr(5, 0, "Transport backfired!\n");
+ altercoordinates(0.0, 0.0, A_FAR);
+ cancelmonster();
+ }
+ else
+ {
+ mvprintw(5, 0, "%s is transported.\n", Enemyname);
+ if (drandom() < 0.3)
+ /* monster didn't drop its treasure */
+ Curmonster.m_treasuretype = 0;
+
+ Curmonster.m_energy = 0.0;
+ }
+ }
+ break;
+
+ case '8': /* paralyze */
+ if (Player.p_magiclvl < ML_PARALYZE)
+ ILLSPELL();
+ else if (Player.p_mana < MM_PARALYZE)
+ NOMANA();
+ else
+ {
+ Player.p_mana -= MM_PARALYZE;
+ if (Player.p_magiclvl >
+ Curmonster.m_experience / 1000.0 * drandom())
+ {
+ mvprintw(5, 0, "%s is held.\n", Enemyname);
+ Curmonster.m_speed = -2.0;
+ }
+ else
+ mvaddstr(5, 0, "Monster unaffected.\n");
+ }
+ break;
+
+ case '9': /* specify */
+ if (Player.p_specialtype < SC_COUNCIL)
+ ILLSPELL();
+ else if (Player.p_mana < MM_SPECIFY)
+ NOMANA();
+ else
+ {
+ Player.p_mana -= MM_SPECIFY;
+ mvaddstr(5, 0, "Which monster do you want [0-99] ? ");
+ Whichmonster = (int) infloat();
+ Whichmonster = MAX(0, MIN(99, Whichmonster));
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+ }
+ break;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: callmonster()
+/
+/ FUNCTION: read monster from file, and fill structure
+/
+/ AUTHOR: E. A. Estes, 2/25/86
+/
+/ ARGUMENTS:
+/ int which - which monster to call
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: truncstring(), fread(), fseek(), floor(), drandom(),
+/ strcpy()
+/
+/ GLOBAL INPUTS: Curmonster, Circle, Player, *Monstfp
+/
+/ GLOBAL OUTPUTS: Curmonster, Player, *Enemyname
+/
+/ DESCRIPTION:
+/ Read specified monster from monster database and fill up
+/ current monster structure.
+/ Adjust statistics based upon current size.
+/ Handle some special monsters.
+/
+/************************************************************************/
+
+callmonster(which)
+int which;
+{
+struct monster Othermonster; /* to find a name for mimics */
+
+ which = MIN(which, 99); /* make sure within range */
+
+ /* fill structure */
+ fseek(Monstfp, (long) which * (long) SZ_MONSTERSTRUCT, 0);
+ fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
+
+ /* handle some special monsters */
+ if (Curmonster.m_type == SM_MODNAR)
+ {
+ if (Player.p_specialtype < SC_COUNCIL)
+ /* randomize some stats */
+ {
+ Curmonster.m_strength *= drandom() + 0.5;
+ Curmonster.m_brains *= drandom() + 0.5;
+ Curmonster.m_speed *= drandom() + 0.5;
+ Curmonster.m_energy *= drandom() + 0.5;
+ Curmonster.m_experience *= drandom() + 0.5;
+ Curmonster.m_treasuretype =
+ (int) ROLL(0.0, (double) Curmonster.m_treasuretype);
+ }
+ else
+ /* make Modnar into Morgoth */
+ {
+ strcpy(Curmonster.m_name, "Morgoth");
+ Curmonster.m_strength = drandom() * (Player.p_maxenergy + Player.p_shield) / 1.4
+ + drandom() * (Player.p_maxenergy + Player.p_shield) / 1.5;
+ Curmonster.m_brains = Player.p_brains;
+ Curmonster.m_energy = Player.p_might * 30.0;
+ Curmonster.m_type = SM_MORGOTH;
+ Curmonster.m_speed = Player.p_speed * 1.1
+ + (Player.p_specialtype == SC_EXVALAR) ? Player.p_speed : 0.0;
+ Curmonster.m_flock = 0.0;
+ Curmonster.m_treasuretype = 0;
+ Curmonster.m_experience = 0.0;
+ }
+ }
+ else if (Curmonster.m_type == SM_MIMIC)
+ /* pick another name */
+ {
+ which = (int) ROLL(0.0, 100.0);
+ fseek(Monstfp, (long) which * (long) SZ_MONSTERSTRUCT, 0);
+ fread(&Othermonster, SZ_MONSTERSTRUCT, 1, Monstfp);
+ strcpy(Curmonster.m_name, Othermonster.m_name);
+ }
+
+ truncstring(Curmonster.m_name);
+
+ if (Curmonster.m_type != SM_MORGOTH)
+ /* adjust stats based on which circle player is in */
+ {
+ Curmonster.m_strength *= (1.0 + Circle / 2.0);
+ Curmonster.m_brains *= Circle;
+ Curmonster.m_speed += Circle * 1.e-9;
+ Curmonster.m_energy *= Circle;
+ Curmonster.m_experience *= Circle;
+ }
+
+ if (Player.p_blindness)
+ /* cannot see monster if blind */
+ Enemyname = "A monster";
+ else
+ Enemyname = Curmonster.m_name;
+
+ if (Player.p_speed <= 0.0)
+ /* make Player.p_speed positive */
+ {
+ Curmonster.m_speed += -Player.p_speed;
+ Player.p_speed = 1.0;
+ }
+
+ /* fill up the rest of the structure */
+ Curmonster.m_o_strength = Curmonster.m_strength;
+ Curmonster.m_o_speed = Curmonster.m_maxspeed = Curmonster.m_speed;
+ Curmonster.m_o_energy = Curmonster.m_energy;
+ Curmonster.m_melee = Curmonster.m_skirmish = 0.0;
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: awardtreasure()
+/
+/ FUNCTION: select a treasure
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: pickmonster(), collecttaxes(), more(), cursedtreasure(),
+/ floor(), wmove(), drandom(), sscanf(), printw(), altercoordinates(),
+/ longjmp(), infloat(), waddstr(), getanswer(), getstring(), wclrtobot()
+/
+/ GLOBAL INPUTS: Somebetter[], Curmonster, Whichmonster, Circle, Player,
+/ *stdscr, Databuf[], *Statptr, Fightenv[]
+/
+/ GLOBAL OUTPUTS: Whichmonster, Shield, Player
+/
+/ DESCRIPTION:
+/ Roll up a treasure based upon monster type and size, and
+/ certain player statistics.
+/ Handle cursed treasure.
+/
+/************************************************************************/
+
+awardtreasure()
+{
+register int whichtreasure; /* calculated treasure to grant */
+int temp; /* temporary */
+int ch; /* input */
+double treasuretype; /* monster's treasure type */
+double gold = 0.0; /* gold awarded */
+double gems = 0.0; /* gems awarded */
+double dtemp; /* for temporary calculations */
+
+ whichtreasure = (int) ROLL(1.0, 3.0); /* pick a treasure */
+ treasuretype = (double) Curmonster.m_treasuretype;
+
+ move(4, 0);
+ clrtobot();
+ move(6, 0);
+
+ if (drandom() > 0.65)
+ /* gold and gems */
+ {
+ if (Curmonster.m_treasuretype > 7)
+ /* gems */
+ {
+ gems = ROLL(1.0, (treasuretype - 7.0)
+ * (treasuretype - 7.0) * (Circle - 1.0) / 4.0);
+ printw("You have discovered %.0f gems!", gems);
+ }
+ else
+ /* gold */
+ {
+ gold = ROLL(treasuretype * 10.0, treasuretype
+ * treasuretype * 10.0 * (Circle - 1.0));
+ printw("You have found %.0f gold pieces.", gold);
+ }
+
+ addstr(" Do you want to pick them up ? ");
+ ch = getanswer("NY", FALSE);
+ addstr("\n\n");
+
+ if (ch == 'Y')
+ if (drandom() < treasuretype / 35.0 + 0.04)
+ /* cursed */
+ {
+ addstr("They were cursed!\n");
+ cursedtreasure();
+ }
+ else
+ collecttaxes(gold, gems);
+
+ return;
+ }
+ else
+ /* other treasures */
+ {
+ addstr("You have found some treasure. Do you want to inspect it ? ");
+ ch = getanswer("NY", FALSE);
+ addstr("\n\n");
+
+ if (ch != 'Y')
+ return;
+ else
+ if (drandom() < 0.08 && Curmonster.m_treasuretype != 4)
+ {
+ addstr("It was cursed!\n");
+ cursedtreasure();
+ return;
+ }
+ else
+ switch (Curmonster.m_treasuretype)
+ {
+ case 1: /* treasure type 1 */
+ switch (whichtreasure)
+ {
+ case 1:
+ addstr("You've discovered a power booster!\n");
+ Player.p_mana += ROLL(Circle * 4.0, Circle * 30.0);
+ break;
+
+ case 2:
+ addstr("You have encountered a druid.\n");
+ Player.p_experience +=
+ ROLL(0.0, 2000.0 + Circle * 400.0);
+ break;
+
+ case 3:
+ addstr("You have found a holy orb.\n");
+ Player.p_sin = MAX(0.0, Player.p_sin - 0.25);
+ break;
+ }
+ break;
+ /* end treasure type 1 */
+
+ case 2: /* treasure type 2 */
+ switch (whichtreasure)
+ {
+ case 1:
+ addstr("You have found an amulet.\n");
+ ++Player.p_amulets;
+ break;
+
+ case 2:
+ addstr("You've found some holy water!\n");
+ ++Player.p_holywater;
+ break;
+
+ case 3:
+ addstr("You've met a hermit!\n");
+ Player.p_sin *= 0.75;
+ Player.p_mana += 12.0 * Circle;
+ break;
+ }
+ break;
+ /* end treasure type 2 */
+
+ case 3: /* treasure type 3 */
+ switch (whichtreasure)
+ {
+ case 1:
+ dtemp = ROLL(7.0, 30.0 + Circle / 10.0);
+ printw("You've found a +%.0f shield!\n", dtemp);
+ if (dtemp >= Player.p_shield)
+ Player.p_shield = dtemp;
+ else
+ SOMEBETTER();
+ break;
+
+ case 2:
+ addstr("You have rescued a virgin. Will you be honorable ? ");
+ ch = getanswer("NY", FALSE);
+ addstr("\n\n");
+ if (ch == 'Y')
+ Player.p_virgin = TRUE;
+ else
+ {
+ Player.p_experience += 2000.0 * Circle;
+ ++Player.p_sin;
+ }
+ break;
+
+ case 3:
+ addstr("You've discovered some athelas!\n");
+ --Player.p_poison;
+ break;
+ }
+ break;
+ /* end treasure type 3 */
+
+ case 4: /* treasure type 4 */
+ addstr("You've found a scroll. Will you read it ? ");
+ ch = getanswer("NY", FALSE);
+ addstr("\n\n");
+
+ if (ch == 'Y')
+ switch ((int) ROLL(1, 6))
+ {
+ case 1:
+ addstr("It throws up a shield for you next monster.\n");
+ getyx(stdscr, whichtreasure, ch);
+ more(whichtreasure);
+ Shield =
+ (Player.p_maxenergy + Player.p_energy) * 5.5 + Circle * 50.0;
+ Whichmonster = pickmonster();
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+
+ case 2:
+ addstr("It makes you invisible for you next monster.\n");
+ getyx(stdscr, whichtreasure, ch);
+ more(whichtreasure);
+ Player.p_speed = 1e6;
+ Whichmonster = pickmonster();
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+
+ case 3:
+ addstr("It increases your strength ten fold to fight your next monster.\n");
+ getyx(stdscr, whichtreasure, ch);
+ more(whichtreasure);
+ Player.p_might *= 10.0;
+ Whichmonster = pickmonster();
+ longjmp(Fightenv, 0);
+ /*NOTREACHED*/
+
+ case 4:
+ addstr("It is a general knowledge scroll.\n");
+ Player.p_brains += ROLL(2.0, Circle);
+ Player.p_magiclvl += ROLL(1.0, Circle / 2.0);
+ break;
+
+ case 5:
+ addstr("It tells you how to pick your next monster.\n");
+ addstr("Which monster do you want [0-99] ? ");
+ Whichmonster = (int) infloat();
+ Whichmonster = MIN(99, MAX(0, Whichmonster));
+ longjmp(Fightenv, 0);
+
+ case 6:
+ addstr("It was cursed!\n");
+ cursedtreasure();
+ break;
+ }
+ break;
+ /* end treasure type 4 */
+
+ case 5: /* treasure type 5 */
+ switch (whichtreasure)
+ {
+ case 1:
+ dtemp = ROLL(Circle / 4.0 + 5.0, Circle / 2.0 + 9.0);
+ printw("You've discovered a +%.0f dagger.\n", dtemp);
+ if (dtemp >= Player.p_sword)
+ Player.p_sword = dtemp;
+ else
+ SOMEBETTER();
+ break;
+
+ case 2:
+ dtemp = ROLL(7.5 + Circle * 3.0, Circle * 2.0 + 160.0);
+ printw("You have found some +%.0f armour!\n", dtemp);
+ if (dtemp >= Player.p_shield)
+ Player.p_shield = dtemp;
+ else
+ SOMEBETTER();
+ break;
+
+ case 3:
+ addstr("You've found a tablet.\n");
+ Player.p_brains += 4.5 * Circle;
+ break;
+ }
+ break;
+ /* end treasure type 5 */
+
+ case 6: /* treasure type 6 */
+ switch (whichtreasure)
+ {
+ case 1:
+ addstr("You've found a priest.\n");
+ Player.p_energy = Player.p_maxenergy + Player.p_shield;
+ Player.p_sin /= 2.0;
+ Player.p_mana += 24.0 * Circle;
+ Player.p_brains += Circle;
+ break;
+
+ case 2:
+ addstr("You have come upon Robin Hood!\n");
+ Player.p_shield += Circle * 2.0;
+ Player.p_strength += Circle / 2.5 + 1.0;
+ break;
+
+ case 3:
+ dtemp = ROLL(2.0 + Circle / 4.0, Circle / 1.2 + 10.0);
+ printw("You have found a +%.0f axe!\n", dtemp);
+ if (dtemp >= Player.p_sword)
+ Player.p_sword = dtemp;
+ else
+ SOMEBETTER();
+ break;
+ }
+ break;
+ /* end treasure type 6 */
+
+ case 7: /* treasure type 7 */
+ switch (whichtreasure)
+ {
+ case 1:
+ addstr("You've discovered a charm!\n");
+ ++Player.p_charms;
+ break;
+
+ case 2:
+ addstr("You have encountered Merlyn!\n");
+ Player.p_brains += Circle + 5.0;
+ Player.p_magiclvl += Circle / 3.0 + 5.0;
+ Player.p_mana += Circle * 10.0;
+ break;
+
+ case 3:
+ dtemp = ROLL(5.0 + Circle / 3.0, Circle / 1.5 + 20.0);
+ printw("You have found a +%.0f war hammer!\n", dtemp);
+ if (dtemp >= Player.p_sword)
+ Player.p_sword = dtemp;
+ else
+ SOMEBETTER();
+ break;
+ }
+ break;
+ /* end treasure type 7 */
+
+ case 8: /* treasure type 8 */
+ switch (whichtreasure)
+ {
+ case 1:
+ addstr("You have found a healing potion.\n");
+ Player.p_poison = MIN(-2.0, Player.p_poison - 2.0);
+ break;
+
+ case 2:
+ addstr("You have discovered a transporter. Do you wish to go anywhere ? ");
+ ch = getanswer("NY", FALSE);
+ addstr("\n\n");
+ if (ch == 'Y')
+ {
+ double x, y;
+
+ addstr("X Y Coordinates ? ");
+ getstring(Databuf, SZ_DATABUF);
+ sscanf(Databuf, "%lf %lf", &x, &y);
+ altercoordinates(x, y, A_FORCED);
+ }
+ break;
+
+ case 3:
+ dtemp = ROLL(10.0 + Circle / 1.2, Circle * 3.0 + 30.0);
+ printw("You've found a +%.0f sword!\n", dtemp);
+ if (dtemp >= Player.p_sword)
+ Player.p_sword = dtemp;
+ else
+ SOMEBETTER();
+ break;
+ }
+ break;
+ /* end treasure type 8 */
+
+ case 10:
+ case 11:
+ case 12:
+ case 13: /* treasure types 10 - 13 */
+ if (drandom() < 0.33)
+ {
+ if (Curmonster.m_treasuretype == 10)
+ {
+ addstr("You've found a pair of elven boots!\n");
+ Player.p_quickness += 2.0;
+ break;
+ }
+ else if (Curmonster.m_treasuretype == 11
+ && !Player.p_palantir)
+ {
+ addstr("You've acquired Saruman's palantir.\n");
+ Player.p_palantir = TRUE;
+ break;
+ }
+ else if (Player.p_ring.ring_type == R_NONE
+ && Player.p_specialtype < SC_COUNCIL
+ && (Curmonster.m_treasuretype == 12
+ || Curmonster.m_treasuretype == 13))
+ /* roll up a ring */
+ {
+ if (drandom() < 0.8)
+ /* regular rings */
+ {
+ if (Curmonster.m_treasuretype == 12)
+ {
+ whichtreasure = R_NAZREG;
+ temp = 35;
+ }
+ else
+ {
+ whichtreasure = R_DLREG;
+ temp = 0;
+ }
+ }
+ else
+ /* bad rings */
+ {
+ whichtreasure = R_BAD;
+ temp = 15 + Statptr->c_ringduration + (int) ROLL(0,5);
+ }
+
+ addstr("You've discovered a ring. Will you pick it up ? ");
+ ch = getanswer("NY", FALSE);
+ addstr("\n\n");
+
+ if (ch == 'Y')
+ {
+ Player.p_ring.ring_type = whichtreasure;
+ Player.p_ring.ring_duration = temp;
+ }
+
+ break;
+ }
+ }
+ /* end treasure types 10 - 13 */
+ /* fall through to treasure type 9 if no treasure from above */
+
+ case 9: /* treasure type 9 */
+ switch (whichtreasure)
+ {
+ case 1:
+ if (Player.p_level <= 1000.0
+ && Player.p_crowns <= 3
+ && Player.p_level >= 10.0)
+ {
+ addstr("You have found a golden crown!\n");
+ ++Player.p_crowns;
+ break;
+ }
+ /* fall through otherwise */
+
+ case 2:
+ addstr("You've been blessed!\n");
+ Player.p_blessing = TRUE;
+ Player.p_sin /= 3.0;
+ Player.p_energy = Player.p_maxenergy + Player.p_shield;
+ Player.p_mana += 100.0 * Circle;
+ break;
+
+ case 3:
+ dtemp = ROLL(1.0, Circle / 5.0 + 5.0);
+ dtemp = MIN(dtemp, 99.0);
+ printw("You have discovered some +%.0f quicksilver!\n",dtemp);
+ if (dtemp >= Player.p_quksilver)
+ Player.p_quksilver = dtemp;
+ else
+ SOMEBETTER();
+ break;
+ }
+ break;
+ /* end treasure type 9 */
+ }
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: cursedtreasure()
+/
+/ FUNCTION: take care of cursed treasure
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: waddstr()
+/
+/ GLOBAL INPUTS: Player, *stdscr
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Handle cursed treasure. Look for amulets and charms to save
+/ the player from the curse.
+/
+/************************************************************************/
+
+cursedtreasure()
+{
+ if (Player.p_charms > 0)
+ {
+ addstr("But your charm saved you!\n");
+ --Player.p_charms;
+ }
+ else if (Player.p_amulets > 0)
+ {
+ addstr("But your amulet saved you!\n");
+ --Player.p_amulets;
+ }
+ else
+ {
+ Player.p_energy = (Player.p_maxenergy + Player.p_shield) / 10.0;
+ Player.p_poison += 0.25;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: scramblestats()
+/
+/ FUNCTION: scramble some selected statistics
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: floor(), drandom()
+/
+/ GLOBAL INPUTS: Player
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Swap a few player statistics randomly.
+/
+/************************************************************************/
+
+scramblestats()
+{
+double dbuf[6]; /* to put statistic in */
+double dtemp1, dtemp2; /* for swapping values */
+register int first, second; /* indices for swapping */
+register double *dptr; /* pointer for filling and emptying buf[] */
+
+ /* fill buffer */
+ dptr = &dbuf[0];
+ *dptr++ = Player.p_strength;
+ *dptr++ = Player.p_mana;
+ *dptr++ = Player.p_brains;
+ *dptr++ = Player.p_magiclvl;
+ *dptr++ = Player.p_energy;
+ *dptr = Player.p_sin;
+
+ /* pick values to swap */
+ first = (int) ROLL(0, 5);
+ second = (int) ROLL(0, 5);
+
+ /* swap values */
+ dptr = &dbuf[0];
+ dtemp1 = dptr[first];
+ /* this expression is split to prevent a compiler loop on some compilers */
+ dtemp2 = dptr[second];
+ dptr[first] = dtemp2;
+ dptr[second] = dtemp1;
+
+ /* empty buffer */
+ Player.p_strength = *dptr++;
+ Player.p_mana = *dptr++;
+ Player.p_brains = *dptr++;
+ Player.p_magiclvl = *dptr++;
+ Player.p_energy = *dptr++;
+ Player.p_sin = *dptr;
+}
diff --git a/phantasia/gamesupport.c b/phantasia/gamesupport.c
new file mode 100644
index 00000000..c7481ef7
--- /dev/null
+++ b/phantasia/gamesupport.c
@@ -0,0 +1,722 @@
+/*
+ * gamesupport.c - auxiliary routines for support of Phantasia
+ */
+
+#include "include.h"
+
+/************************************************************************
+/
+/ FUNCTION NAME: changestats()
+/
+/ FUNCTION: examine/change statistics for a player
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ bool ingameflag - set if called while playing game (Wizard only)
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: freerecord(), writerecord(), descrstatus(), truncstring(),
+/ time(), more(), wmove(), wclear(), strcmp(), printw(), strcpy(),
+/ infloat(), waddstr(), cleanup(), findname(), userlist(), mvprintw(),
+/ localtime(), getanswer(), descrtype(), getstring()
+/
+/ GLOBAL INPUTS: LINES, *Login, Other, Wizard, Player, *stdscr, Databuf[],
+/ Fileloc
+/
+/ GLOBAL OUTPUTS: Echo
+/
+/ DESCRIPTION:
+/ Prompt for player name to examine/change.
+/ If the name is NULL, print a list of all players.
+/ If we are called from within the game, check for the
+/ desired name being the same as the current player's name.
+/ Only the 'Wizard' may alter players.
+/ Items are changed only if a non-zero value is specified.
+/ To change an item to 0, use 0.1; it will be truncated later.
+/
+/ Players may alter their names and passwords, if the following
+/ are true:
+/ - current login matches the character's logins
+/ - the password is known
+/ - the player is not in the middle of the game (ingameflag == FALSE)
+/
+/ The last condition is imposed for two reasons:
+/ - the game could possibly get a bit hectic if a player were
+/ continually changing his/her name
+/ - another player structure would be necessary to check for names
+/ already in use
+/
+/************************************************************************/
+
+changestats(ingameflag)
+bool ingameflag;
+{
+static char flag[2] = /* for printing values of bools */
+ {'F', 'T'};
+register struct player *playerp;/* pointer to structure to alter */
+register char *prompt; /* pointer to prompt string */
+int c; /* input */
+int today; /* day of year of today */
+int temp; /* temporary variable */
+long loc; /* location in player file */
+long now; /* time now */
+double dtemp; /* temporary variable */
+bool *bptr; /* pointer to bool item to change */
+double *dptr; /* pointer to double item to change */
+short *sptr; /* pointer to short item to change */
+
+ clear();
+
+ for (;;)
+ /* get name of player to examine/alter */
+ {
+ mvaddstr(5, 0, "Which character do you want to look at ? ");
+ getstring(Databuf, SZ_DATABUF);
+ truncstring(Databuf);
+
+ if (Databuf[0] == '\0')
+ userlist(ingameflag);
+ else
+ break;
+ }
+
+ loc = -1L;
+
+ if (!ingameflag)
+ /* use 'Player' structure */
+ playerp = &Player;
+ else if (strcmp(Databuf, Player.p_name) == 0)
+ /* alter/examine current player */
+ {
+ playerp = &Player;
+ loc = Fileloc;
+ }
+ else
+ /* use 'Other' structure */
+ playerp = &Other;
+
+ /* find player on file */
+ if (loc < 0L && (loc = findname(Databuf, playerp)) < 0L)
+ /* didn't find player */
+ {
+ clear();
+ mvaddstr(11, 0, "Not found.");
+ return;
+ }
+
+ time(&now);
+ today = localtime(&now)->tm_yday;
+
+ clear();
+
+ for (;;)
+ /* print player structure, and prompt for action */
+ {
+ mvprintw(0, 0,"A:Name %s\n", playerp->p_name);
+
+ if (Wizard)
+ printw("B:Password %s\n", playerp->p_password);
+ else
+ addstr("B:Password XXXXXXXX\n");
+
+ printw(" :Login %s\n", playerp->p_login);
+
+ printw("C:Experience %.0f\n", playerp->p_experience);
+ printw("D:Level %.0f\n", playerp->p_level);
+ printw("E:Strength %.0f\n", playerp->p_strength);
+ printw("F:Sword %.0f\n", playerp->p_sword);
+ printw(" :Might %.0f\n", playerp->p_might);
+ printw("G:Energy %.0f\n", playerp->p_energy);
+ printw("H:Max-Energy %.0f\n", playerp->p_maxenergy);
+ printw("I:Shield %.0f\n", playerp->p_shield);
+ printw("J:Quickness %.0f\n", playerp->p_quickness);
+ printw("K:Quicksilver %.0f\n", playerp->p_quksilver);
+ printw(" :Speed %.0f\n", playerp->p_speed);
+ printw("L:Magic Level %.0f\n", playerp->p_magiclvl);
+ printw("M:Mana %.0f\n", playerp->p_mana);
+ printw("N:Brains %.0f\n", playerp->p_brains);
+
+ if (Wizard || playerp->p_specialtype != SC_VALAR)
+ mvaddstr(0, 40, descrstatus(playerp));
+
+ mvprintw(1, 40, "O:Poison %0.3f\n", playerp->p_poison);
+ mvprintw(2, 40, "P:Gold %.0f\n", playerp->p_gold);
+ mvprintw(3, 40, "Q:Gem %.0f\n", playerp->p_gems);
+ mvprintw(4, 40, "R:Sin %0.3f\n", playerp->p_sin);
+ if (Wizard)
+ {
+ mvprintw(5, 40, "S:X-coord %.0f\n", playerp->p_x);
+ mvprintw(6, 40, "T:Y-coord %.0f\n", playerp->p_y);
+ }
+ else
+ {
+ mvaddstr(5, 40, "S:X-coord ?\n");
+ mvaddstr(6, 40, "T:Y-coord ?\n");
+ }
+
+ mvprintw(7, 40, "U:Age %ld\n", playerp->p_age);
+ mvprintw(8, 40, "V:Degenerated %d\n", playerp->p_degenerated);
+
+ mvprintw(9, 40, "W:Type %d (%s)\n",
+ playerp->p_type, descrtype(playerp, FALSE) + 1);
+ mvprintw(10, 40, "X:Special Type %d\n", playerp->p_specialtype);
+ mvprintw(11, 40, "Y:Lives %d\n", playerp->p_lives);
+ mvprintw(12, 40, "Z:Crowns %d\n", playerp->p_crowns);
+ mvprintw(13, 40, "0:Charms %d\n", playerp->p_charms);
+ mvprintw(14, 40, "1:Amulets %d\n", playerp->p_amulets);
+ mvprintw(15, 40, "2:Holy Water %d\n", playerp->p_holywater);
+
+ temp = today - playerp->p_lastused;
+ if (temp < 0)
+ /* last year */
+ temp += 365;
+ mvprintw(16, 40, "3:Lastused %d (%d)\n", playerp->p_lastused, temp);
+
+ mvprintw(18, 8, "4:Palantir %c 5:Blessing %c 6:Virgin %c 7:Blind %c",
+ flag[playerp->p_palantir],
+ flag[playerp->p_blessing],
+ flag[playerp->p_virgin],
+ flag[playerp->p_blindness]);
+
+ if (!Wizard)
+ mvprintw(19, 8, "8:Ring %c",
+ flag[playerp->p_ring.ring_type != R_NONE]);
+ else
+ mvprintw(19, 8, "8:Ring %d 9:Duration %d",
+ playerp->p_ring.ring_type, playerp->p_ring.ring_duration);
+
+ if (!Wizard
+ /* not wizard */
+ && (ingameflag || strcmp(Login, playerp->p_login) != 0))
+ /* in game or not examining own character */
+ {
+ if (ingameflag)
+ {
+ more(LINES - 1);
+ clear();
+ return;
+ }
+ else
+ cleanup(TRUE);
+ /*NOTREACHED*/
+ }
+
+ mvaddstr(20, 0, "!:Quit ?:Delete");
+ mvaddstr(21, 0, "What would you like to change ? ");
+
+ if (Wizard)
+ c = getanswer(" ", TRUE);
+ else
+ /* examining own player; allow to change name and password */
+ c = getanswer("!BA", FALSE);
+
+ switch (c)
+ {
+ case 'A': /* change name */
+ case 'B': /* change password */
+ if (!Wizard)
+ /* prompt for password */
+ {
+ mvaddstr(23, 0, "Password ? ");
+ Echo = FALSE;
+ getstring(Databuf, 9);
+ Echo = TRUE;
+ if (strcmp(Databuf, playerp->p_password) != 0)
+ continue;
+ }
+
+ if (c == 'A')
+ /* get new name */
+ {
+ mvaddstr(23, 0, "New name: ");
+ getstring(Databuf, SZ_NAME);
+ truncstring(Databuf);
+ if (Databuf[0] != '\0')
+ if (Wizard || findname(Databuf, &Other) < 0L)
+ strcpy(playerp->p_name, Databuf);
+ }
+ else
+ /* get new password */
+ {
+ if (!Wizard)
+ Echo = FALSE;
+
+ do
+ /* get two copies of new password until they match */
+ {
+ /* get first copy */
+ mvaddstr(23, 0, "New password ? ");
+ getstring(Databuf, SZ_PASSWORD);
+ if (Databuf[0] == '\0')
+ break;
+
+ /* get second copy */
+ mvaddstr(23, 0, "One more time ? ");
+ getstring(playerp->p_password, SZ_PASSWORD);
+ }
+ while (strcmp(playerp->p_password, Databuf) != 0);
+
+ Echo = TRUE;
+ }
+
+ continue;
+
+ case 'C': /* change experience */
+ prompt = "experience";
+ dptr = &playerp->p_experience;
+ goto DALTER;
+
+ case 'D': /* change level */
+ prompt = "level";
+ dptr = &playerp->p_level;
+ goto DALTER;
+
+ case 'E': /* change strength */
+ prompt = "strength";
+ dptr = &playerp->p_strength;
+ goto DALTER;
+
+ case 'F': /* change swords */
+ prompt = "sword";
+ dptr = &playerp->p_sword;
+ goto DALTER;
+
+ case 'G': /* change energy */
+ prompt = "energy";
+ dptr = &playerp->p_energy;
+ goto DALTER;
+
+ case 'H': /* change maximum energy */
+ prompt = "max energy";
+ dptr = &playerp->p_maxenergy;
+ goto DALTER;
+
+ case 'I': /* change shields */
+ prompt = "shield";
+ dptr = &playerp->p_shield;
+ goto DALTER;
+
+ case 'J': /* change quickness */
+ prompt = "quickness";
+ dptr = &playerp->p_quickness;
+ goto DALTER;
+
+ case 'K': /* change quicksilver */
+ prompt = "quicksilver";
+ dptr = &playerp->p_quksilver;
+ goto DALTER;
+
+ case 'L': /* change magic */
+ prompt = "magic level";
+ dptr = &playerp->p_magiclvl;
+ goto DALTER;
+
+ case 'M': /* change mana */
+ prompt = "mana";
+ dptr = &playerp->p_mana;
+ goto DALTER;
+
+ case 'N': /* change brains */
+ prompt = "brains";
+ dptr = &playerp->p_brains;
+ goto DALTER;
+
+ case 'O': /* change poison */
+ prompt = "poison";
+ dptr = &playerp->p_poison;
+ goto DALTER;
+
+ case 'P': /* change gold */
+ prompt = "gold";
+ dptr = &playerp->p_gold;
+ goto DALTER;
+
+ case 'Q': /* change gems */
+ prompt = "gems";
+ dptr = &playerp->p_gems;
+ goto DALTER;
+
+ case 'R': /* change sin */
+ prompt = "sin";
+ dptr = &playerp->p_sin;
+ goto DALTER;
+
+ case 'S': /* change x coord */
+ prompt = "x";
+ dptr = &playerp->p_x;
+ goto DALTER;
+
+ case 'T': /* change y coord */
+ prompt = "y";
+ dptr = &playerp->p_y;
+ goto DALTER;
+
+ case 'U': /* change age */
+ mvprintw(23, 0, "age = %ld; age = ", playerp->p_age);
+ dtemp = infloat();
+ if (dtemp != 0.0)
+ playerp->p_age = (long) dtemp;
+ continue;
+
+ case 'V': /* change degen */
+ mvprintw(23, 0, "degen = %d; degen = ", playerp->p_degenerated);
+ dtemp = infloat();
+ if (dtemp != 0.0)
+ playerp->p_degenerated = (int) dtemp;
+ continue;
+
+ case 'W': /* change type */
+ prompt = "type";
+ sptr = &playerp->p_type;
+ goto SALTER;
+
+ case 'X': /* change special type */
+ prompt = "special type";
+ sptr = &playerp->p_specialtype;
+ goto SALTER;
+
+ case 'Y': /* change lives */
+ prompt = "lives";
+ sptr = &playerp->p_lives;
+ goto SALTER;
+
+ case 'Z': /* change crowns */
+ prompt = "crowns";
+ sptr = &playerp->p_crowns;
+ goto SALTER;
+
+ case '0': /* change charms */
+ prompt = "charm";
+ sptr = &playerp->p_charms;
+ goto SALTER;
+
+ case '1': /* change amulet */
+ prompt = "amulet";
+ sptr = &playerp->p_amulets;
+ goto SALTER;
+
+ case '2': /* change holy water */
+ prompt = "holy water";
+ sptr = &playerp->p_holywater;
+ goto SALTER;
+
+ case '3': /* change last-used */
+ prompt = "last-used";
+ sptr = &playerp->p_lastused;
+ goto SALTER;
+
+ case '4': /* change palantir */
+ prompt = "palantir";
+ bptr = &playerp->p_palantir;
+ goto BALTER;
+
+ case '5': /* change blessing */
+ prompt = "blessing";
+ bptr = &playerp->p_blessing;
+ goto BALTER;
+
+ case '6': /* change virgin */
+ prompt = "virgin";
+ bptr = &playerp->p_virgin;
+ goto BALTER;
+
+ case '7': /* change blindness */
+ prompt = "blindness";
+ bptr = &playerp->p_blindness;
+ goto BALTER;
+
+ case '8': /* change ring type */
+ prompt = "ring-type";
+ sptr = &playerp->p_ring.ring_type;
+ goto SALTER;
+
+ case '9': /* change ring duration */
+ prompt = "ring-duration";
+ sptr = &playerp->p_ring.ring_duration;
+ goto SALTER;
+
+ case '!': /* quit, update */
+ if (Wizard &&
+ (!ingameflag || playerp != &Player))
+ /* turn off status if not modifying self */
+ {
+ playerp->p_status = S_OFF;
+ playerp->p_tampered = T_OFF;
+ }
+
+ writerecord(playerp, loc);
+ clear();
+ return;
+
+ case '?': /* delete player */
+ if (ingameflag && playerp == &Player)
+ /* cannot delete self */
+ continue;
+
+ freerecord(playerp, loc);
+ clear();
+ return;
+
+ default:
+ continue;
+ }
+DALTER:
+ mvprintw(23, 0, "%s = %f; %s = ", prompt, *dptr, prompt);
+ dtemp = infloat();
+ if (dtemp != 0.0)
+ *dptr = dtemp;
+ continue;
+
+SALTER:
+ mvprintw(23, 0, "%s = %d; %s = ", prompt, *sptr, prompt);
+ dtemp = infloat();
+ if (dtemp != 0.0)
+ *sptr = (short) dtemp;
+ continue;
+
+BALTER:
+ mvprintw(23, 0, "%s = %c; %s = ", prompt, flag[*bptr], prompt);
+ c = getanswer("\nTF", TRUE);
+ if (c == 'T')
+ *bptr = TRUE;
+ else if (c == 'F')
+ *bptr = FALSE;
+ continue;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: monstlist()
+/
+/ FUNCTION: print a monster listing
+/
+/ AUTHOR: E. A. Estes, 2/27/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: puts(), fread(), fseek(), printf()
+/
+/ GLOBAL INPUTS: Curmonster, *Monstfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Read monster file, and print a monster listing on standard output.
+/
+/************************************************************************/
+
+monstlist()
+{
+register int count = 0; /* count in file */
+
+ puts(" #) Name Str Brain Quick Energy Exper Treas Type Flock%\n");
+ fseek(Monstfp, 0L, 0);
+ while (fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp) == 1)
+ printf("%2d) %-20.20s%4.0f %4.0f %2.0f %5.0f %5.0f %2d %2d %3.0f\n", count++,
+ Curmonster.m_name, Curmonster.m_strength, Curmonster.m_brains,
+ Curmonster.m_speed, Curmonster.m_energy, Curmonster.m_experience,
+ Curmonster.m_treasuretype, Curmonster.m_type, Curmonster.m_flock);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: scorelist()
+/
+/ FUNCTION: print player score board
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fread(), fopen(), printf(), fclose()
+/
+/ GLOBAL INPUTS:
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Read the scoreboard file and print the contents.
+/
+/************************************************************************/
+
+scorelist()
+{
+struct scoreboard sbuf; /* for reading entries */
+register FILE *fp; /* to open the file */
+
+ if ((fp = fopen(_PATH_SCORE, "r")) != NULL)
+ {
+ while (fread((char *) &sbuf, SZ_SCORESTRUCT, 1, fp) == 1)
+ printf("%-20s (%-9s) Level: %6.0f Type: %s\n",
+ sbuf.sb_name, sbuf.sb_login, sbuf.sb_level, sbuf.sb_type);
+ fclose(fp);
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: activelist()
+/
+/ FUNCTION: print list of active players to standard output
+/
+/ AUTHOR: E. A. Estes, 3/7/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: descrstatus(), fread(), fseek(), printf(), descrtype()
+/
+/ GLOBAL INPUTS: Other, *Playersfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Read player file, and print list of active records to standard output.
+/
+/************************************************************************/
+
+activelist()
+{
+ fseek(Playersfp, 0L, 0);
+ printf("Current characters on file are:\n\n");
+
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ if (Other.p_status != S_NOTUSED)
+ printf("%-20s (%-9s) Level: %6.0f %s (%s)\n",
+ Other.p_name, Other.p_login, Other.p_level,
+ descrtype(&Other, FALSE), descrstatus(&Other));
+
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: purgeoldplayers()
+/
+/ FUNCTION: purge inactive players from player file
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: freerecord(), time(), fread(), fseek(), localtime()
+/
+/ GLOBAL INPUTS: Other, *Playersfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Delete characters which have not been used with the last
+/ three weeks.
+/
+/************************************************************************/
+
+purgeoldplayers()
+{
+int today; /* day of year for today */
+int daysold; /* how many days since the character has been used */
+long ltime; /* time in seconds */
+long loc = 0L; /* location in file */
+
+ time(&ltime);
+ today = localtime(&ltime)->tm_yday;
+
+ for (;;)
+ {
+ fseek(Playersfp, loc, 0);
+ if (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) != 1)
+ break;
+
+ daysold = today - Other.p_lastused;
+ if (daysold < 0)
+ daysold += 365;
+
+ if (daysold > N_DAYSOLD)
+ /* player hasn't been used in a while; delete */
+ freerecord(&Other, loc);
+
+ loc += SZ_PLAYERSTRUCT;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: enterscore()
+/
+/ FUNCTION: enter player into scoreboard
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fread(), fseek(), fopen(), error(), strcmp(), fclose(),
+/ strcpy(), fwrite(), descrtype()
+/
+/ GLOBAL INPUTS: Player
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ The scoreboard keeps track of the highest character on a
+/ per-login basis.
+/ Search the scoreboard for an entry for the current login,
+/ if an entry is found, and it is lower than the current player,
+/ replace it, otherwise create an entry.
+/
+/************************************************************************/
+
+enterscore()
+{
+struct scoreboard sbuf; /* buffer to read in scoreboard entries */
+FILE *fp; /* to open scoreboard file */
+long loc = 0L; /* location in scoreboard file */
+bool found = FALSE; /* set if we found an entry for this login */
+
+ if ((fp = fopen(_PATH_SCORE, "r+")) != NULL)
+ {
+ while (fread((char *) &sbuf, SZ_SCORESTRUCT, 1, fp) == 1)
+ if (strcmp(Player.p_login, sbuf.sb_login) == 0)
+ {
+ found = TRUE;
+ break;
+ }
+ else
+ loc += SZ_SCORESTRUCT;
+ }
+ else
+ {
+ error(_PATH_SCORE);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * At this point, 'loc' will either indicate a point beyond
+ * the end of file, or the place where the previous entry
+ * was found.
+ */
+
+ if ((!found) || Player.p_level > sbuf.sb_level)
+ /* put new entry in for this login */
+ {
+ strcpy(sbuf.sb_login, Player.p_login);
+ strcpy(sbuf.sb_name, Player.p_name);
+ sbuf.sb_level = Player.p_level;
+ strcpy(sbuf.sb_type, descrtype(&Player, TRUE));
+ }
+
+ /* update entry */
+ fseek(fp, loc, 0);
+ fwrite((char *) &sbuf, SZ_SCORESTRUCT, 1, fp);
+ fclose(fp);
+}
diff --git a/phantasia/include.h b/phantasia/include.h
new file mode 100644
index 00000000..bfaa8a7e
--- /dev/null
+++ b/phantasia/include.h
@@ -0,0 +1,17 @@
+/*
+ * include.h - includes all important files for Phantasia
+ */
+
+#include <ctype.h>
+#include <curses.h>
+#include <math.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+
+#include "macros.h"
+#include "phantdefs.h"
+#include "phantstruct.h"
+#include "phantglobs.h"
+#include "pathnames.h"
diff --git a/phantasia/interplayer.c b/phantasia/interplayer.c
new file mode 100644
index 00000000..0c923f0c
--- /dev/null
+++ b/phantasia/interplayer.c
@@ -0,0 +1,1208 @@
+/*
+ * interplayer.c - player to player routines for Phantasia
+ */
+
+#include "include.h"
+
+/************************************************************************
+/
+/ FUNCTION NAME: checkbattle()
+/
+/ FUNCTION: check to see if current player should battle another
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: battleplayer(), fread(), fseek()
+/
+/ GLOBAL INPUTS: Other, Users, Player, Fileloc, *Playersfp
+/
+/ GLOBAL OUTPUTS: Users
+/
+/ DESCRIPTION:
+/ Seach player file for a foe at the same coordinates as the
+/ current player.
+/ Also update user count.
+/
+/************************************************************************/
+
+checkbattle()
+{
+long foeloc = 0L; /* location in file of person to fight */
+
+ Users = 0;
+ fseek(Playersfp, 0L, 0);
+
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ {
+ if (Other.p_status != S_OFF
+ && Other.p_status != S_NOTUSED
+ && Other.p_status != S_HUNGUP
+ && (Other.p_status != S_CLOAKED || Other.p_specialtype != SC_VALAR))
+ /* player is on and not a cloaked valar */
+ {
+ ++Users;
+
+ if (Player.p_x == Other.p_x
+ && Player.p_y == Other.p_y
+ /* same coordinates */
+ && foeloc != Fileloc
+ /* not self */
+ && Player.p_status == S_PLAYING
+ && (Other.p_status == S_PLAYING || Other.p_status == S_INBATTLE)
+ /* both are playing */
+ && Other.p_specialtype != SC_VALAR
+ && Player.p_specialtype != SC_VALAR)
+ /* neither is valar */
+ {
+ battleplayer(foeloc);
+ return;
+ }
+ }
+ foeloc += SZ_PLAYERSTRUCT;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: battleplayer()
+/
+/ FUNCTION: inter-terminal battle with another player
+/
+/ AUTHOR: E. A. Estes, 2/15/86
+/
+/ ARGUMENTS:
+/ long foeplace - location in player file of person to battle
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: readrecord(), readmessage(), writerecord(), collecttaxes(),
+/ displaystats(), fabs(), more(), death(), sleep(), wmove(), waddch(), printw(),
+/ myturn(), altercoordinates(), waddstr(), wrefresh(), mvprintw(),
+/ getanswer(), wclrtoeol(), wclrtobot()
+/
+/ GLOBAL INPUTS: Foestrikes, LINES, Lines, Other, Shield, Player, *stdscr,
+/ Fileloc, *Enemyname
+/
+/ GLOBAL OUTPUTS: Foestrikes, Lines, Shield, Player, Luckout, *Enemyname
+/
+/ DESCRIPTION:
+/ Inter-terminal battle is a very fragile and slightly klugy thing.
+/ At any time, one player is master and the other is slave.
+/ We pick who is master first by speed and level. After that,
+/ the slave waits for the master to relinquish its turn, and
+/ the slave becomes master, and so on.
+/
+/ The items in the player structure which control the handshake are:
+/ p_tampered:
+/ master increments this to relinquish control
+/ p_istat:
+/ master sets this to specify particular action
+/ p_1scratch:
+/ set to total damage inflicted so far; changes to indicate action
+/
+/************************************************************************/
+
+battleplayer(foeplace)
+long foeplace;
+{
+double dtemp; /* for temporary calculations */
+double oldhits = 0.0; /* previous damage inflicted by foe */
+register int loop; /* for timing out */
+int ch; /* input */
+short oldtampered; /* old value of foe's p_tampered */
+
+ Lines = 8;
+ Luckout = FALSE;
+ mvaddstr(4, 0, "Preparing for battle!\n");
+ refresh();
+
+#ifdef SYS5
+ flushinp();
+#endif
+
+ /* set up variables, file, etc. */
+ Player.p_status = S_INBATTLE;
+ Shield = Player.p_energy;
+
+ /* if p_tampered is not 0, someone else may try to change it (king, etc.) */
+ Player.p_tampered = oldtampered = 1;
+ Player.p_1scratch = 0.0;
+ Player.p_istat = I_OFF;
+
+ readrecord(&Other, foeplace);
+ if (fabs(Player.p_level - Other.p_level) > 20.0)
+ /* see if players are greatly mismatched */
+ {
+ dtemp = (Player.p_level - Other.p_level) / MAX(Player.p_level, Other.p_level);
+ if (dtemp < -0.5)
+ /* foe outweighs this one */
+ Player.p_speed *= 2.0;
+ }
+
+ writerecord(&Player, Fileloc); /* write out all our info */
+
+ if (Player.p_blindness)
+ Enemyname = "someone";
+ else
+ Enemyname = Other.p_name;
+
+ mvprintw(6, 0, "You have encountered %s Level: %.0f\n", Enemyname, Other.p_level);
+ refresh();
+
+ for (loop = 0; Other.p_status != S_INBATTLE && loop < 30; ++loop)
+ /* wait for foe to respond */
+ {
+ readrecord(&Other, foeplace);
+ sleep(1);
+ }
+
+ if (Other.p_status != S_INBATTLE)
+ /* foe did not respond */
+ {
+ mvprintw(5, 0, "%s is not responding.\n", Enemyname);
+ goto LEAVE;
+ }
+ /* else, we are ready to battle */
+
+ move(4, 0);
+ clrtoeol();
+
+ /*
+ * determine who is first master
+ * if neither player is faster, check level
+ * if neither level is greater, battle is not allowed
+ * (this should never happen, but we have to handle it)
+ */
+ if (Player.p_speed > Other.p_speed)
+ Foestrikes = FALSE;
+ else if (Other.p_speed > Player.p_speed)
+ Foestrikes = TRUE;
+ else if (Player.p_level > Other.p_level)
+ Foestrikes = FALSE;
+ else if (Other.p_level > Player.p_level)
+ Foestrikes = TRUE;
+ else
+ /* no one is faster */
+ {
+ printw("You can't fight %s yet.", Enemyname);
+ goto LEAVE;
+ }
+
+ for (;;)
+ {
+ displaystats();
+ readmessage();
+ mvprintw(1, 26, "%20.0f", Shield); /* overprint energy */
+
+ if (!Foestrikes)
+ /* take action against foe */
+ myturn();
+ else
+ /* wait for foe to take action */
+ {
+ mvaddstr(4, 0, "Waiting...\n");
+ clrtoeol();
+ refresh();
+
+ for (loop = 0; loop < 20; ++loop)
+ /* wait for foe to act */
+ {
+ readrecord(&Other, foeplace);
+ if (Other.p_1scratch != oldhits)
+ /* p_1scratch changes to indicate action */
+ break;
+ else
+ /* wait and try again */
+ {
+ sleep(1);
+ addch('.');
+ refresh();
+ }
+ }
+
+ if (Other.p_1scratch == oldhits)
+ {
+ /* timeout */
+ mvaddstr(22, 0, "Timeout: waiting for response. Do you want to wait ? ");
+ ch = getanswer("NY", FALSE);
+ move(22, 0);
+ clrtobot();
+ if (ch == 'Y')
+ continue;
+ else
+ break;
+ }
+ else
+ /* foe took action */
+ {
+ switch (Other.p_istat)
+ {
+ case I_RAN: /* foe ran away */
+ mvprintw(Lines++, 0, "%s ran away!", Enemyname);
+ break;
+
+ case I_STUCK: /* foe tried to run, but couldn't */
+ mvprintw(Lines++, 0, "%s tried to run away.", Enemyname);
+ break;
+
+ case I_BLEWIT: /* foe tried to luckout, but didn't */
+ mvprintw(Lines++, 0, "%s tried to luckout!", Enemyname);
+ break;
+
+ default:
+ dtemp = Other.p_1scratch - oldhits;
+ mvprintw(Lines++, 0, "%s hit you %.0f times!", Enemyname, dtemp);
+ Shield -= dtemp;
+ break;
+ }
+
+ oldhits = Other.p_1scratch; /* keep track of old hits */
+
+ if (Other.p_tampered != oldtampered)
+ /* p_tampered changes to relinquish turn */
+ {
+ oldtampered = Other.p_tampered;
+ Foestrikes = FALSE;
+ }
+ }
+ }
+
+ /* decide what happens next */
+ refresh();
+ if (Lines > LINES - 2)
+ {
+ more(Lines);
+ move(Lines = 8, 0);
+ clrtobot();
+ }
+
+ if (Other.p_istat == I_KILLED || Shield < 0.0)
+ /* we died */
+ {
+ Shield = -2.0; /* insure this value is negative */
+ break;
+ }
+
+ if (Player.p_istat == I_KILLED)
+ /* we killed foe; award treasre */
+ {
+ mvprintw(Lines++, 0, "You killed %s!", Enemyname);
+ Player.p_experience += Other.p_experience;
+ Player.p_crowns += (Player.p_level < 1000.0) ? Other.p_crowns : 0;
+ Player.p_amulets += Other.p_amulets;
+ Player.p_charms += Other.p_charms;
+ collecttaxes(Other.p_gold, Other.p_gems);
+ Player.p_sword = MAX(Player.p_sword, Other.p_sword);
+ Player.p_shield = MAX(Player.p_shield, Other.p_shield);
+ Player.p_quksilver = MAX(Player.p_quksilver, Other.p_quksilver);
+ if (Other.p_virgin && !Player.p_virgin)
+ {
+ mvaddstr(Lines++, 0, "You have rescued a virgin. Will you be honorable ? ");
+ if ((ch = getanswer("YN", FALSE)) == 'Y')
+ Player.p_virgin = TRUE;
+ else
+ {
+ ++Player.p_sin;
+ Player.p_experience += 8000.0;
+ }
+ }
+ sleep(3); /* give other person time to die */
+ break;
+ }
+ else if (Player.p_istat == I_RAN || Other.p_istat == I_RAN)
+ /* either player ran away */
+ break;
+ }
+
+LEAVE:
+ /* clean up things and leave */
+ writerecord(&Player, Fileloc); /* update a final time */
+ altercoordinates(0.0, 0.0, A_NEAR); /* move away from battle site */
+ Player.p_energy = Shield; /* set energy to actual value */
+ Player.p_tampered = T_OFF; /* clear p_tampered */
+
+ more(Lines); /* pause */
+
+ move(4, 0);
+ clrtobot(); /* clear bottom area of screen */
+
+ if (Player.p_energy < 0.0)
+ /* we are dead */
+ death("Interterminal battle");
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: myturn()
+/
+/ FUNCTION: process players action against foe in battle
+/
+/ AUTHOR: E. A. Estes, 2/7/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: writerecord(), inputoption(), floor(), wmove(), drandom(),
+/ waddstr(), wrefresh(), mvprintw(), wclrtoeol(), wclrtobot()
+/
+/ GLOBAL INPUTS: Lines, Other, Player, *stdscr, Fileloc, Luckout,
+/ *Enemyname
+/
+/ GLOBAL OUTPUTS: Foestrikes, Lines, Player, Luckout
+/
+/ DESCRIPTION:
+/ Take action action against foe, and decide who is master
+/ for next iteration.
+/
+/************************************************************************/
+
+myturn()
+{
+double dtemp; /* for temporary calculations */
+int ch; /* input */
+
+ mvaddstr(7, 0, "1:Fight 2:Run Away! 3:Power Blast ");
+ if (Luckout)
+ clrtoeol();
+ else
+ addstr("4:Luckout ");
+
+ ch = inputoption();
+ move(Lines = 8, 0);
+ clrtobot();
+
+ switch (ch)
+ {
+ default: /* fight */
+ dtemp = ROLL(2.0, Player.p_might);
+HIT:
+ mvprintw(Lines++, 0, "You hit %s %.0f times!", Enemyname, dtemp);
+ Player.p_sin += 0.5;
+ Player.p_1scratch += dtemp;
+ Player.p_istat = I_OFF;
+ break;
+
+ case '2': /* run away */
+ Player.p_1scratch -= 1.0; /* change this to indicate action */
+ if (drandom() > 0.25)
+ {
+ mvaddstr(Lines++, 0, "You got away!");
+ Player.p_istat = I_RAN;
+ }
+ else
+ {
+ mvprintw(Lines++, 0, "%s is still after you!", Enemyname);
+ Player.p_istat = I_STUCK;
+ }
+ break;
+
+ case '3': /* power blast */
+ dtemp = MIN(Player.p_mana, Player.p_level * 5.0);
+ Player.p_mana -= dtemp;
+ dtemp *= (drandom() + 0.5) * Player.p_magiclvl * 0.2 + 2.0;
+ mvprintw(Lines++, 0, "You blasted %s !", Enemyname);
+ goto HIT;
+
+ case '4': /* luckout */
+ if (Luckout || drandom() > 0.1)
+ {
+ if (Luckout)
+ mvaddstr(Lines++, 0, "You already tried that!");
+ else
+ {
+ mvaddstr(Lines++, 0, "Not this time . . .");
+ Luckout = TRUE;
+ }
+
+ Player.p_1scratch -= 1.0;
+ Player.p_istat = I_BLEWIT;
+ }
+ else
+ {
+ mvaddstr(Lines++, 0, "You just lucked out!");
+ Player.p_1scratch = Other.p_energy * 1.1;
+ }
+ break;
+ }
+
+ refresh();
+ Player.p_1scratch = floor(Player.p_1scratch); /* clean up any mess */
+
+ if (Player.p_1scratch > Other.p_energy)
+ Player.p_istat = I_KILLED;
+ else if (drandom() * Player.p_speed < drandom() * Other.p_speed)
+ /* relinquish control */
+ {
+ ++Player.p_tampered;
+ Foestrikes = TRUE;
+ }
+
+ writerecord(&Player, Fileloc); /* let foe know what we did */
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: checktampered()
+/
+/ FUNCTION: check if current player has been tampered with
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: readrecord(), fread(), fseek(), tampered(), writevoid()
+/
+/ GLOBAL INPUTS: *Energyvoidfp, Other, Player, Fileloc, Enrgyvoid
+/
+/ GLOBAL OUTPUTS: Enrgyvoid
+/
+/ DESCRIPTION:
+/ Check for energy voids, holy grail, and tampering by other
+/ players.
+/
+/************************************************************************/
+
+checktampered()
+{
+long loc = 0L; /* location in energy void file */
+
+ /* first check for energy voids */
+ fseek(Energyvoidfp, 0L, 0);
+ while (fread((char *) &Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp) == 1)
+ if (Enrgyvoid.ev_active
+ && Enrgyvoid.ev_x == Player.p_x
+ && Enrgyvoid.ev_y == Player.p_y)
+ /* sitting on one */
+ {
+ if (loc > 0L)
+ /* not the holy grail; inactivate energy void */
+ {
+ Enrgyvoid.ev_active = FALSE;
+ writevoid(&Enrgyvoid, loc);
+ tampered(T_NRGVOID, 0.0, 0.0);
+ }
+ else if (Player.p_status != S_CLOAKED)
+ /* holy grail */
+ tampered(T_GRAIL, 0.0, 0.0);
+ break;
+ }
+ else
+ loc += SZ_VOIDSTRUCT;
+
+ /* now check for other things */
+ readrecord(&Other, Fileloc);
+ if (Other.p_tampered != T_OFF)
+ tampered(Other.p_tampered, Other.p_1scratch, Other.p_2scratch);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: tampered()
+/
+/ FUNCTION: take care of tampering by other players
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ int what - what type of tampering
+/ double arg1, arg2 - rest of tampering info
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: writerecord(), more(), fread(), death(), fseek(), sleep(),
+/ floor(), wmove(), waddch(), drandom(), printw(), altercoordinates(),
+/ waddstr(), wrefresh(), encounter(), writevoid()
+/
+/ GLOBAL INPUTS: Other, Player, *stdscr, Enrgyvoid, *Playersfp
+/
+/ GLOBAL OUTPUTS: Other, Player, Changed, Enrgyvoid
+/
+/ DESCRIPTION:
+/ Take care of energy voids, holy grail, decree and intervention
+/ action on current player.
+/
+/************************************************************************/
+
+tampered(what, arg1, arg2)
+int what;
+double arg1;
+double arg2;
+{
+long loc; /* location in file of other players */
+
+ Changed = TRUE;
+ move(4,0);
+
+ Player.p_tampered = T_OFF; /* no longer tampered with */
+
+ switch (what)
+ {
+ case T_NRGVOID:
+ addstr("You've hit an energy void !\n");
+ Player.p_mana /= 3.0;
+ Player.p_energy /= 2.0;
+ Player.p_gold = floor(Player.p_gold/1.25) + 0.1;
+ altercoordinates(0.0, 0.0, A_NEAR);
+ break;
+
+ case T_TRANSPORT:
+ addstr("The king transported you ! ");
+ if (Player.p_charms > 0)
+ {
+ addstr("But your charm saved you. . .\n");
+ --Player.p_charms;
+ }
+ else
+ {
+ altercoordinates(0.0, 0.0, A_FAR);
+ addch('\n');
+ }
+ break;
+
+ case T_BESTOW:
+ printw("The king has bestowed %.0f gold pieces on you !\n", arg1);
+ Player.p_gold += arg1;
+ break;
+
+ case T_CURSED:
+ addstr("You've been cursed ! ");
+ if (Player.p_blessing)
+ {
+ addstr("But your blessing saved you. . .\n");
+ Player.p_blessing = FALSE;
+ }
+ else
+ {
+ addch('\n');
+ Player.p_poison += 2.0;
+ Player.p_energy = 10.0;
+ Player.p_maxenergy *= 0.95;
+ Player.p_status = S_PLAYING; /* no longer cloaked */
+ }
+ break;
+
+ case T_VAPORIZED:
+ addstr("You have been vaporized!\n");
+ more(7);
+ death("Vaporization");
+ break;
+
+ case T_MONSTER:
+ addstr("The Valar zapped you with a monster!\n");
+ more(7);
+ encounter((int) arg1);
+ return;
+
+ case T_BLESSED:
+ addstr("The Valar has blessed you!\n");
+ Player.p_energy = (Player.p_maxenergy *= 1.05) + Player.p_shield;
+ Player.p_mana += 500.0;
+ Player.p_strength += 0.5;
+ Player.p_brains += 0.5;
+ Player.p_magiclvl += 0.5;
+ Player.p_poison = MIN(0.5, Player.p_poison);
+ break;
+
+ case T_RELOCATE:
+ addstr("You've been relocated. . .\n");
+ altercoordinates(arg1, arg2, A_FORCED);
+ break;
+
+ case T_HEAL:
+ addstr("You've been healed!\n");
+ Player.p_poison -= 0.25;
+ Player.p_energy = Player.p_maxenergy + Player.p_shield;
+ break;
+
+ case T_EXVALAR:
+ addstr("You are no longer Valar!\n");
+ Player.p_specialtype = SC_COUNCIL;
+ break;
+
+ case T_GRAIL:
+ addstr("You have found The Holy Grail!!\n");
+ if (Player.p_specialtype < SC_COUNCIL)
+ /* must be council of wise to behold grail */
+ {
+ addstr("However, you are not experienced enough to behold it.\n");
+ Player.p_sin *= Player.p_sin;
+ Player.p_mana += 1000;
+ }
+ else if (Player.p_specialtype == SC_VALAR
+ || Player.p_specialtype == SC_EXVALAR)
+ {
+ addstr("You have made it to the position of Valar once already.\n");
+ addstr("The Grail is of no more use to you now.\n");
+ }
+ else
+ {
+ addstr("It is now time to see if you are worthy to behold it. . .\n");
+ refresh();
+ sleep(4);
+
+ if (drandom() / 2.0 < Player.p_sin)
+ {
+ addstr("You have failed!\n");
+ Player.p_strength =
+ Player.p_mana =
+ Player.p_energy =
+ Player.p_maxenergy =
+ Player.p_magiclvl =
+ Player.p_brains =
+ Player.p_experience =
+ Player.p_quickness = 1.0;
+
+ altercoordinates(1.0, 1.0, A_FORCED);
+ Player.p_level = 0.0;
+ }
+ else
+ {
+ addstr("You made to position of Valar!\n");
+ Player.p_specialtype = SC_VALAR;
+ Player.p_lives = 5;
+ fseek(Playersfp, 0L, 0);
+ loc = 0L;
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ /* search for existing valar */
+ if (Other.p_specialtype == SC_VALAR
+ && Other.p_status != S_NOTUSED)
+ /* found old valar */
+ {
+ Other.p_tampered = T_EXVALAR;
+ writerecord(&Other, loc);
+ break;
+ }
+ else
+ loc += SZ_PLAYERSTRUCT;
+ }
+ }
+
+ /* move grail to new location */
+ Enrgyvoid.ev_active = TRUE;
+ Enrgyvoid.ev_x = ROLL(-1.0e6, 2.0e6);
+ Enrgyvoid.ev_y = ROLL(-1.0e6, 2.0e6);
+ writevoid(&Enrgyvoid, 0L);
+ break;
+ }
+ refresh();
+ sleep(2);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: userlist()
+/
+/ FUNCTION: print list of players and locations
+/
+/ AUTHOR: E. A. Estes, 2/28/86
+/
+/ ARGUMENTS:
+/ bool ingameflag - set if called while playing
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: descrstatus(), descrlocation(), more(), fread(), fseek(),
+/ floor(), wmove(), printw(), waddstr(), distance(), wrefresh(),
+/ descrtype(), wclrtobot()
+/
+/ GLOBAL INPUTS: LINES, Other, Circle, Wizard, Player, *stdscr, *Playersfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ We can only see the coordinate of those closer to the origin
+/ from us.
+/ Kings and council of the wise can see and can be seen by everyone.
+/ Palantirs are good for seeing everyone; and the valar can use
+/ one to see through a 'cloak' spell.
+/ The valar has no coordinates, and is completely invisible if
+/ cloaked.
+/
+/************************************************************************/
+
+userlist(ingameflag)
+bool ingameflag;
+{
+register int numusers = 0; /* number of users on file */
+
+ if (ingameflag && Player.p_blindness)
+ {
+ mvaddstr(8, 0, "You cannot see anyone.\n");
+ return;
+ }
+
+ fseek(Playersfp, 0L, 0);
+ mvaddstr(8, 0,
+ "Name X Y Lvl Type Login Status\n");
+
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ {
+ if (Other.p_status == S_NOTUSED
+ /* record is unused */
+ || (Other.p_specialtype == SC_VALAR && Other.p_status == S_CLOAKED))
+ /* cloaked valar */
+ {
+ if (!Wizard)
+ /* wizard can see everything on file */
+ continue;
+ }
+
+ ++numusers;
+
+ if (ingameflag &&
+ /* must be playing for the rest of these conditions */
+ (Player.p_specialtype >= SC_KING
+ /* kings and higher can see others */
+ || Other.p_specialtype >= SC_KING
+ /* kings and higher can be seen by others */
+ || Circle >= CIRCLE(Other.p_x, Other.p_y)
+ /* those nearer the origin can be seen */
+ || Player.p_palantir)
+ /* palantir enables one to see others */
+ && (Other.p_status != S_CLOAKED
+ || (Player.p_specialtype == SC_VALAR && Player.p_palantir))
+ /* not cloaked; valar can see through cloak with a palantir */
+ && Other.p_specialtype != SC_VALAR)
+ /* not a valar */
+ /* coordinates should be printed */
+ printw("%-20s %8.0f %8.0f ",
+ Other.p_name, Other.p_x, Other.p_y);
+ else
+ /* cannot see player's coordinates */
+ printw("%-20s %19.19s ",
+ Other.p_name, descrlocation(&Other, TRUE));
+
+ printw("%6.0f %s %-9.9s%s\n", Other.p_level, descrtype(&Other, TRUE),
+ Other.p_login, descrstatus(&Other));
+
+ if ((numusers % (LINES - 10)) == 0)
+ {
+ more(LINES - 1);
+ move(9, 0);
+ clrtobot();
+ }
+ }
+
+ printw("Total players on file = %d\n", numusers);
+ refresh();
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: throneroom()
+/
+/ FUNCTION: king stuff upon entering throne
+/
+/ AUTHOR: E. A. Estes, 12/16/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: writerecord(), fread(), fseek(), fopen(), wmove(), fclose(),
+/ fwrite(), altercoordinates(), waddstr(), fprintf()
+/
+/ GLOBAL INPUTS: *Energyvoidfp, Other, Player, *stdscr,
+/ Enrgyvoid, *Playersfp
+/
+/ GLOBAL OUTPUTS: Other, Player, Changed
+/
+/ DESCRIPTION:
+/ If player is not already king, make him/her so if the old king
+/ is not playing.
+/ Clear energy voids with new king.
+/ Print 'decree' prompt.
+/
+/************************************************************************/
+
+throneroom()
+{
+FILE *fp; /* to clear energy voids */
+long loc = 0L; /* location of old king in player file */
+
+ if (Player.p_specialtype < SC_KING)
+ /* not already king -- assumes crown */
+ {
+ fseek(Playersfp, 0L, 0);
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ if (Other.p_specialtype == SC_KING && Other.p_status != S_NOTUSED)
+ /* found old king */
+ {
+ if (Other.p_status != S_OFF)
+ /* old king is playing */
+ {
+ mvaddstr( 4, 0, "The king is playing, so you cannot steal his throne\n");
+ altercoordinates(0.0, 0.0, A_NEAR);
+ move(6, 0);
+ return;
+ }
+ else
+ /* old king is not playing - remove him/her */
+ {
+ Other.p_specialtype = SC_NONE;
+ if (Other.p_crowns)
+ --Other.p_crowns;
+ writerecord(&Other, loc);
+ break;
+ }
+ }
+ else
+ loc += SZ_PLAYERSTRUCT;
+
+ /* make player new king */
+ Changed = TRUE;
+ Player.p_specialtype = SC_KING;
+ mvaddstr(4, 0, "You have become king!\n");
+
+ /* let everyone else know */
+ fp = fopen(_PATH_MESS, "w");
+ fprintf(fp, "All hail the new king!");
+ fclose(fp);
+
+ /* clear all energy voids; retain location of holy grail */
+ fseek(Energyvoidfp, 0L, 0);
+ fread((char *) &Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp);
+ fp = fopen(_PATH_VOID, "w");
+ fwrite((char *) &Enrgyvoid, SZ_VOIDSTRUCT, 1, fp);
+ fclose(fp);
+ }
+
+ mvaddstr(6, 0, "0:Decree ");
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: dotampered()
+/
+/ FUNCTION: king and valar special options
+/
+/ AUTHOR: E. A. Estes, 2/28/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: writerecord(), truncstring(), fread(), fseek(), fopen(),
+/ floor(), wmove(), drandom(), fclose(), fwrite(), sscanf(), strcmp(),
+/ infloat(), waddstr(), findname(), distance(), userlist(), mvprintw(),
+/ allocvoid(), getanswer(), getstring(), wclrtoeol(), writevoid()
+/
+/ GLOBAL INPUTS: *Energyvoidfp, Other, Illcmd[], Wizard, Player, *stdscr,
+/ Databuf[], Enrgyvoid
+/
+/ GLOBAL OUTPUTS: Other, Player, Enrgyvoid
+/
+/ DESCRIPTION:
+/ Tamper with other players. Handle king/valar specific options.
+/
+/************************************************************************/
+
+dotampered()
+{
+short tamper; /* value for tampering with other players */
+char *option; /* pointer to option description */
+double temp1 = 0.0, temp2 = 0.0; /* other tampering values */
+int ch; /* input */
+long loc; /* location in energy void file */
+FILE *fp; /* for opening gold file */
+
+ move(6, 0);
+ clrtoeol();
+ if (Player.p_specialtype < SC_COUNCIL && !Wizard)
+ /* king options */
+ {
+ addstr("1:Transport 2:Curse 3:Energy Void 4:Bestow 5:Collect Taxes ");
+
+ ch = getanswer(" ", TRUE);
+ move(6, 0);
+ clrtoeol();
+ move(4, 0);
+ switch (ch)
+ {
+ case '1': /* transport someone */
+ tamper = T_TRANSPORT;
+ option = "transport";
+ break;
+
+ case '2': /* curse another */
+ tamper = T_CURSED;
+ option = "curse";
+ break;
+
+ case '3': /* create energy void */
+ if ((loc = allocvoid()) > 20L * SZ_VOIDSTRUCT)
+ /* can only have 20 void active at once */
+ mvaddstr(5, 0, "Sorry, void creation limit reached.\n");
+ else
+ {
+ addstr("Enter the X Y coordinates of void ? ");
+ getstring(Databuf, SZ_DATABUF);
+ sscanf(Databuf, "%lf %lf", &temp1, &temp2);
+ Enrgyvoid.ev_x = floor(temp1);
+ Enrgyvoid.ev_y = floor(temp2);
+ Enrgyvoid.ev_active = TRUE;
+ writevoid(&Enrgyvoid, loc);
+ mvaddstr(5, 0, "It is done.\n");
+ }
+ return;
+
+ case '4': /* bestow gold to subject */
+ tamper = T_BESTOW;
+ addstr("How much gold to bestow ? ");
+ temp1 = infloat();
+ if (temp1 > Player.p_gold || temp1 < 0)
+ {
+ mvaddstr(5, 0, "You don't have that !\n");
+ return;
+ }
+
+ /* adjust gold after we are sure it will be given to someone */
+ option = "give gold to";
+ break;
+
+ case '5': /* collect accumulated taxes */
+ if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
+ /* collect taxes */
+ {
+ fread((char *) &temp1, sizeof(double), 1, fp);
+ fseek(fp, 0L, 0);
+ /* clear out value */
+ temp2 = 0.0;
+ fwrite((char *) &temp2, sizeof(double), 1, fp);
+ fclose(fp);
+ }
+
+ mvprintw(4, 0, "You have collected %.0f in gold.\n", temp1);
+ Player.p_gold += floor(temp1);
+ return;
+
+ default:
+ return;
+ }
+ /* end of king options */
+ }
+ else
+ /* council of wise, valar, wizard options */
+ {
+ addstr("1:Heal ");
+ if (Player.p_palantir || Wizard)
+ addstr("2:Seek Grail ");
+ if (Player.p_specialtype == SC_VALAR || Wizard)
+ addstr("3:Throw Monster 4:Relocate 5:Bless ");
+ if (Wizard)
+ addstr("6:Vaporize ");
+
+ ch = getanswer(" ", TRUE);
+ if (!Wizard)
+ {
+ if (ch > '2' && Player.p_specialtype != SC_VALAR)
+ {
+ ILLCMD();
+ return;
+ }
+
+ if (Player.p_mana < MM_INTERVENE)
+ {
+ mvaddstr(5, 0, "No mana left.\n");
+ return;
+ }
+ else
+ Player.p_mana -= MM_INTERVENE;
+ }
+
+ switch (ch)
+ {
+ case '1': /* heal another */
+ tamper = T_HEAL;
+ option = "heal";
+ break;
+
+ case '2': /* seek grail */
+ if (Player.p_palantir)
+ /* need a palantir to seek */
+ {
+ fseek(Energyvoidfp, 0L, 0);
+ fread((char *) &Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp);
+ temp1 = distance(Player.p_x, Enrgyvoid.ev_x, Player.p_y, Enrgyvoid.ev_y);
+ temp1 += ROLL(-temp1 / 10.0, temp1 / 5.0); /* add some error */
+ mvprintw(5, 0, "The palantir says the Grail is about %.0f away.\n", temp1);
+ }
+ else
+ /* no palantir */
+ mvaddstr(5, 0, "You need a palantir to seek the Grail.\n");
+ return;
+
+ case '3': /* lob monster at someone */
+ mvaddstr(4, 0, "Which monster [0-99] ? ");
+ temp1 = infloat();
+ temp1 = MAX(0.0, MIN(99.0, temp1));
+ tamper = T_MONSTER;
+ option = "throw a monster at";
+ break;
+
+ case '4': /* move another player */
+ mvaddstr(4, 0, "New X Y coordinates ? ");
+ getstring(Databuf, SZ_DATABUF);
+ sscanf(Databuf, "%lf %lf", &temp1, &temp2);
+ tamper = T_RELOCATE;
+ option = "relocate";
+ break;
+
+ case '5': /* bless a player */
+ tamper = T_BLESSED;
+ option = "bless";
+ break;
+
+ case '6': /* kill off a player */
+ if (Wizard)
+ {
+ tamper = T_VAPORIZED;
+ option = "vaporize";
+ break;
+ }
+ else
+ return;
+
+ default:
+ return;
+ }
+
+ /* adjust age after we are sure intervention will be done */
+ /* end of valar, etc. options */
+ }
+
+ for (;;)
+ /* prompt for player to affect */
+ {
+ mvprintw(4, 0, "Who do you want to %s ? ", option);
+ getstring(Databuf, SZ_DATABUF);
+ truncstring(Databuf);
+
+ if (Databuf[0] == '\0')
+ userlist(TRUE);
+ else
+ break;
+ }
+
+ if (strcmp(Player.p_name, Databuf) != 0)
+ /* name other than self */
+ {
+ if ((loc = findname(Databuf, &Other)) >= 0L)
+ {
+ if (Other.p_tampered != T_OFF)
+ {
+ mvaddstr(5, 0, "That person has something pending already.\n");
+ return;
+ }
+ else
+ {
+ if (tamper == T_RELOCATE
+ && CIRCLE(temp1, temp2) < CIRCLE(Other.p_x, Other.p_y)
+ && !Wizard)
+ mvaddstr(5, 0, "Cannot move someone closer to the Lord's Chamber.\n");
+ else
+ {
+ if (tamper == T_BESTOW) Player.p_gold -= floor(temp1);
+ if (!Wizard && (tamper == T_HEAL || tamper == T_MONSTER ||
+ tamper == T_RELOCATE || tamper == T_BLESSED))
+ Player.p_age += N_AGE; /* age penalty */
+ Other.p_tampered = tamper;
+ Other.p_1scratch = floor(temp1);
+ Other.p_2scratch = floor(temp2);
+ writerecord(&Other, loc);
+ mvaddstr(5, 0, "It is done.\n");
+ }
+ return;
+ }
+ }
+ else
+ /* player not found */
+ mvaddstr(5, 0, "There is no one by that name.\n");
+ }
+ else
+ /* self */
+ mvaddstr(5, 0, "You may not do it to yourself!\n");
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: writevoid()
+/
+/ FUNCTION: update energy void entry in energy void file
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ struct energyvoid *vp - pointer to structure to write to file
+/ long loc - location in file to update
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fseek(), fwrite(), fflush()
+/
+/ GLOBAL INPUTS: *Energyvoidfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Write out energy void structure at specified location.
+/
+/************************************************************************/
+
+writevoid(vp, loc)
+register struct energyvoid *vp;
+long loc;
+{
+
+ fseek(Energyvoidfp, loc, 0);
+ fwrite((char *) vp, SZ_VOIDSTRUCT, 1, Energyvoidfp);
+ fflush(Energyvoidfp);
+ fseek(Energyvoidfp, 0L, 0);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: allocvoid()
+/
+/ FUNCTION: allocate space for a new energy void
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: location of new energy void space
+/
+/ MODULES CALLED: fread(), fseek()
+/
+/ GLOBAL INPUTS: *Energyvoidfp, Enrgyvoid
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Search energy void file for an inactive entry and return its
+/ location.
+/ If no inactive ones are found, return one more than last location.
+/
+/************************************************************************/
+
+long
+allocvoid()
+{
+long loc = 0L; /* location of new energy void */
+
+ fseek(Energyvoidfp, 0L, 0);
+ while (fread((char *) &Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp) == 1)
+ if (Enrgyvoid.ev_active)
+ loc += SZ_VOIDSTRUCT;
+ else
+ break;
+
+ return(loc);
+}
diff --git a/phantasia/io.c b/phantasia/io.c
new file mode 100644
index 00000000..addf1c9b
--- /dev/null
+++ b/phantasia/io.c
@@ -0,0 +1,436 @@
+/*
+ * io.c - input/output routines for Phantasia
+ */
+
+#include "include.h"
+
+/************************************************************************
+/
+/ FUNCTION NAME: getstring()
+/
+/ FUNCTION: read a string from operator
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ char *cp - pointer to buffer area to fill
+/ int mx - maximum number of characters to put in buffer
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
+/ wclrtoeol()
+/
+/ GLOBAL INPUTS: Echo, _iob[], Wizard, *stdscr
+/
+/ GLOBAL OUTPUTS: _iob[]
+/
+/ DESCRIPTION:
+/ Read a string from the keyboard.
+/ This routine is specially designed to:
+/
+/ - strip non-printing characters (unless Wizard)
+/ - echo, if desired
+/ - redraw the screen if CH_REDRAW is entered
+/ - read in only 'mx - 1' characters or less characters
+/ - nul-terminate string, and throw away newline
+/
+/ 'mx' is assumed to be at least 2.
+/
+/************************************************************************/
+
+getstring(cp, mx)
+register char *cp;
+register int mx;
+{
+register char *inptr; /* pointer into string for next string */
+int x, y; /* original x, y coordinates on screen */
+int ch; /* input */
+
+ getyx(stdscr, y, x); /* get coordinates on screen */
+ inptr = cp;
+ *inptr = '\0'; /* clear string to start */
+ --mx; /* reserve room in string for nul terminator */
+
+ do
+ /* get characters and process */
+ {
+ if (Echo)
+ mvaddstr(y, x, cp); /* print string on screen */
+ clrtoeol(); /* clear any data after string */
+ refresh(); /* update screen */
+
+ ch = getchar(); /* get character */
+
+ switch (ch)
+ {
+ case CH_ERASE: /* back up one character */
+ if (inptr > cp)
+ --inptr;
+ break;
+
+ case CH_KILL: /* back up to original location */
+ inptr = cp;
+ break;
+
+ case CH_NEWLINE: /* terminate string */
+ break;
+
+ case CH_REDRAW: /* redraw screen */
+ clearok(stdscr, TRUE);
+ continue;
+
+ default: /* put data in string */
+ if (ch >= ' ' || Wizard)
+ /* printing char; put in string */
+ *inptr++ = ch;
+ }
+
+ *inptr = '\0'; /* terminate string */
+ }
+ while (ch != CH_NEWLINE && inptr < cp + mx);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: more()
+/
+/ FUNCTION: pause and prompt player
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ int where - line on screen on which to pause
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: wmove(), waddstr(), getanswer()
+/
+/ GLOBAL INPUTS: *stdscr
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Print a message, and wait for a space character.
+/
+/************************************************************************/
+
+more(where)
+int where;
+{
+ mvaddstr(where, 0, "-- more --");
+ getanswer(" ", FALSE);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: infloat()
+/
+/ FUNCTION: input a floating point number from operator
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: floating point number from operator
+/
+/ MODULES CALLED: sscanf(), getstring()
+/
+/ GLOBAL INPUTS: Databuf[]
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Read a string from player, and scan for a floating point
+/ number.
+/ If no valid number is found, return 0.0.
+/
+/************************************************************************/
+
+double
+infloat()
+{
+double result; /* return value */
+
+ getstring(Databuf, SZ_DATABUF);
+ if (sscanf(Databuf, "%lf", &result) < 1)
+ /* no valid number entered */
+ result = 0.0;
+
+ return(result);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: inputoption()
+/
+/ FUNCTION: input an option value from player
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: floor(), drandom(), getanswer()
+/
+/ GLOBAL INPUTS: Player
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Age increases with every move.
+/ Refresh screen, and get a single character option from player.
+/ Return a random value if player's ring has gone bad.
+/
+/************************************************************************/
+
+inputoption()
+{
+ ++Player.p_age; /* increase age */
+
+ if (Player.p_ring.ring_type != R_SPOILED)
+ /* ring ok */
+ return(getanswer("T ", TRUE));
+ else
+ /* bad ring */
+ {
+ getanswer(" ", TRUE);
+ return((int) ROLL(0.0, 5.0) + '0');
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: interrupt()
+/
+/ FUNCTION: handle interrupt from operator
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fork(), exit(), wait(), death(), alarm(), execl(), wmove(),
+/ getgid(), signal(), getenv(), wclear(), setuid(), getuid(), setgid(),
+/ crmode(), clearok(), waddstr(), cleanup(), wrefresh(), leavegame(),
+/ getanswer()
+/
+/ GLOBAL INPUTS: Player, *stdscr
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Allow player to quit upon hitting the interrupt key.
+/ If the player wants to quit while in battle, he/she automatically
+/ dies.
+/
+/************************************************************************/
+
+interrupt()
+{
+char line[81]; /* a place to store data already on screen */
+register int loop; /* counter */
+int x, y; /* coordinates on screen */
+int ch; /* input */
+unsigned savealarm; /* to save alarm value */
+
+#ifdef SYS3
+ signal(SIGINT, SIG_IGN);
+#endif
+#ifdef SYS5
+ signal(SIGINT, SIG_IGN);
+#endif
+
+ savealarm = alarm(0); /* turn off any alarms */
+
+ getyx(stdscr, y, x); /* save cursor location */
+
+ for (loop = 0; loop < 80; ++loop) /* save line on screen */
+ {
+ move(4, loop);
+ line[loop] = inch();
+ }
+ line[80] = '\0'; /* nul terminate */
+
+ if (Player.p_status == S_INBATTLE || Player.p_status == S_MONSTER)
+ /* in midst of fighting */
+ {
+ mvaddstr(4, 0, "Quitting now will automatically kill your character. Still want to ? ");
+ ch = getanswer("NY", FALSE);
+ if (ch == 'Y')
+ death("Bailing out");
+ /*NOTREACHED*/
+ }
+ else
+ {
+ mvaddstr(4, 0, "Do you really want to quit ? ");
+ ch = getanswer("NY", FALSE);
+ if (ch == 'Y')
+ leavegame();
+ /*NOTREACHED*/
+ }
+
+ mvaddstr(4, 0, line); /* restore data on screen */
+ move(y, x); /* restore cursor */
+ refresh();
+
+#ifdef SYS3
+ signal(SIGINT, interrupt);
+#endif
+#ifdef SYS5
+ signal(SIGINT, interrupt);
+#endif
+
+ alarm(savealarm); /* restore alarm */
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: getanswer()
+/
+/ FUNCTION: get an answer from operator
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ char *choices - string of (upper case) valid choices
+/ bool def - set if default answer
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: alarm(), wmove(), waddch(), signal(), setjmp(), strchr(),
+/ _filbuf(), clearok(), toupper(), wrefresh(), mvprintw(), wclrtoeol()
+/
+/ GLOBAL INPUTS: catchalarm(), Echo, _iob[], _ctype[], *stdscr, Timeout,
+/ Timeoenv[]
+/
+/ GLOBAL OUTPUTS: _iob[]
+/
+/ DESCRIPTION:
+/ Get a single character answer from operator.
+/ Timeout waiting for response. If we timeout, or the
+/ answer in not in the list of valid choices, print choices,
+/ and wait again, otherwise return the first character in ths
+/ list of choices.
+/ Give up after 3 tries.
+/
+/************************************************************************/
+
+getanswer(choices, def)
+char *choices;
+bool def;
+{
+int ch; /* input */
+int loop; /* counter */
+int oldx, oldy; /* original coordinates on screen */
+
+ getyx(stdscr, oldy, oldx);
+ alarm(0); /* make sure alarm is off */
+
+ for (loop = 3; loop; --loop)
+ /* try for 3 times */
+ {
+ if (setjmp(Timeoenv) != 0)
+ /* timed out waiting for response */
+ {
+ if (def || loop <= 1)
+ /* return default answer */
+ break;
+ else
+ /* prompt, and try again */
+ goto YELL;
+ }
+ else
+ /* wait for response */
+ {
+ clrtoeol();
+ refresh();
+#ifdef BSD41
+ sigset(SIGALRM, catchalarm);
+#else
+ signal(SIGALRM, catchalarm);
+#endif
+ /* set timeout */
+ if (Timeout)
+ alarm(7); /* short */
+ else
+ alarm(600); /* long */
+
+ ch = getchar();
+
+ alarm(0); /* turn off timeout */
+
+ if (ch < 0)
+ /* caught some signal */
+ {
+ ++loop;
+ continue;
+ }
+ else if (ch == CH_REDRAW)
+ /* redraw screen */
+ {
+ clearok(stdscr, TRUE); /* force clear screen */
+ ++loop; /* don't count this input */
+ continue;
+ }
+ else if (Echo)
+ {
+ addch(ch); /* echo character */
+ refresh();
+ }
+
+ if (islower(ch))
+ /* convert to upper case */
+ ch = toupper(ch);
+
+ if (def || strchr(choices, ch) != NULL)
+ /* valid choice */
+ return(ch);
+ else if (!def && loop > 1)
+ /* bad choice; prompt, and try again */
+ {
+YELL: mvprintw(oldy + 1, 0, "Please choose one of : [%s]\n", choices);
+ move(oldy, oldx);
+ clrtoeol();
+ continue;
+ }
+ else
+ /* return default answer */
+ break;
+ }
+ }
+
+ return(*choices);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: catchalarm()
+/
+/ FUNCTION: catch timer when waiting for input
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: longjmp()
+/
+/ GLOBAL INPUTS: Timeoenv[]
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Come here when the alarm expires while waiting for input.
+/ Simply longjmp() into getanswer().
+/
+/************************************************************************/
+
+void
+catchalarm()
+{
+ longjmp(Timeoenv, 1);
+}
diff --git a/phantasia/macros.h b/phantasia/macros.h
new file mode 100644
index 00000000..e2327421
--- /dev/null
+++ b/phantasia/macros.h
@@ -0,0 +1,16 @@
+/*
+ * macros.h - macro definitions for Phantasia
+ */
+
+#define ROLL(BASE,INTERVAL) floor((BASE) + (INTERVAL) * drandom())
+#define SGN(X) ((X) < 0 ? -1 : 1)
+#define CIRCLE(X, Y) floor(distance(X, 0.0, Y, 0.0) / 125.0 + 1)
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define ILLCMD() mvaddstr(5, 0, Illcmd)
+#define MAXMOVE() (Player.p_level * 1.5 + 1)
+#define ILLMOVE() mvaddstr(5, 0, Illmove)
+#define ILLSPELL() mvaddstr(5, 0, Illspell)
+#define NOMANA() mvaddstr(5, 0, Nomana)
+#define SOMEBETTER() addstr(Somebetter)
+#define NOBETTER() mvaddstr(17, 0, Nobetter)
diff --git a/phantasia/main.c b/phantasia/main.c
new file mode 100644
index 00000000..89d56e89
--- /dev/null
+++ b/phantasia/main.c
@@ -0,0 +1,1288 @@
+/*
+ * Phantasia 3.3.2 -- Interterminal fantasy game
+ *
+ * Edward A. Estes
+ * AT&T, March 12, 1986
+ */
+
+/* DISCLAIMER:
+ *
+ * This game is distributed for free as is. It is not guaranteed to work
+ * in every conceivable environment. It is not even guaranteed to work
+ * in ANY environment.
+ *
+ * This game is distributed without notice of copyright, therefore it
+ * may be used in any manner the recipient sees fit. However, the
+ * author assumes no responsibility for maintaining or revising this
+ * game, in its original form, or any derivitives thereof.
+ *
+ * The author shall not be responsible for any loss, cost, or damage,
+ * including consequential damage, caused by reliance on this material.
+ *
+ * The author makes no warranties, express or implied, including warranties
+ * of merchantability or fitness for a particular purpose or use.
+ *
+ * AT&T is in no way connected with this game.
+ */
+
+#include <sys/types.h>
+#include <pwd.h>
+
+/*
+ * The program allocates as much file space as it needs to store characters,
+ * so the possibility exists for the character file to grow without bound.
+ * The file is purged upon normal entry to try to avoid that problem.
+ * A similar problem exists for energy voids. To alleviate the problem here,
+ * the void file is cleared with every new king, and a limit is placed
+ * on the size of the energy void file.
+ */
+
+/*
+ * Put one line of text into the file 'motd' for announcements, etc.
+ */
+
+/*
+ * The scoreboard file is updated when someone dies, and keeps track
+ * of the highest character to date for that login.
+ * Being purged from the character file does not cause the scoreboard
+ * to be updated.
+ */
+
+/*
+ * All source files are set up for 'vi' with shiftwidth=4, tabstop=8.
+ */
+
+/* */
+
+/*
+ * main.c Main routines for Phantasia
+ */
+
+#include "include.h"
+
+/***************************************************************************
+/ FUNCTION NAME: main()
+/
+/ FUNCTION: initialize state, and call main process
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ int argc - argument count
+/ char **argv - argument vector
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: monstlist(), checkenemy(), activelist(),
+/ throneroom(), checkbattle(), readmessage(), changestats(), writerecord(),
+/ tradingpost(), adjuststats(), recallplayer(), displaystats(), checktampered(),
+/ fabs(), rollnewplayer(), time(), exit(), sqrt(), floor(), wmove(),
+/ signal(), strcat(), purgeoldplayers(), getuid(), isatty(), wclear(),
+/ strcpy(), system(), altercoordinates(), cleanup(), waddstr(), procmain(),
+/ playinit(), leavegame(), localtime(), getanswer(), neatstuff(), initialstate(),
+/ scorelist(), titlelist()
+/
+/ GLOBAL INPUTS: *Login, Throne, Wizard, Player, *stdscr, Changed, Databuf[],
+/ Fileloc, Stattable[]
+/
+/ GLOBAL OUTPUTS: Wizard, Player, Changed, Fileloc, Timeout, *Statptr
+/
+/ DESCRIPTION:
+/ Process arguments, initialize program, and loop forever processing
+/ player input.
+/
+/***************************************************************************/
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+bool noheader = FALSE; /* set if don't want header */
+bool headeronly = FALSE; /* set if only want header */
+bool examine = FALSE; /* set if examine a character */
+long seconds; /* for time of day */
+double dtemp; /* for temporary calculations */
+
+ initialstate(); /* init globals */
+
+ /* process arguments */
+ while (--argc && (*++argv)[0] == '-')
+ switch ((*argv)[1])
+ {
+ case 's': /* short */
+ noheader = TRUE;
+ break;
+
+ case 'H': /* Header */
+ headeronly = TRUE;
+ break;
+
+ case 'a': /* all users */
+ activelist();
+ cleanup(TRUE);
+ /*NOTREACHED*/
+
+ case 'p': /* purge old players */
+ purgeoldplayers();
+ cleanup(TRUE);
+ /*NOTREACHED*/
+
+ case 'S': /* set 'Wizard' */
+ Wizard = !getuid();
+ break;
+
+ case 'x': /* examine */
+ examine = TRUE;
+ break;
+
+ case 'm': /* monsters */
+ monstlist();
+ cleanup(TRUE);
+ /*NOTREACHED*/
+
+ case 'b': /* scoreboard */
+ scorelist();
+ cleanup(TRUE);
+ /*NOTREACHED*/
+ }
+
+ if (!isatty(0)) /* don't let non-tty's play */
+ cleanup(TRUE);
+ /*NOTREACHED*/
+
+ playinit(); /* set up to catch signals, init curses */
+
+ if (examine)
+ {
+ changestats(FALSE);
+ cleanup(TRUE);
+ /*NOTREACHED*/
+ }
+
+ if (!noheader)
+ {
+ titlelist();
+ purgeoldplayers(); /* clean up old characters */
+ }
+
+ if (headeronly)
+ cleanup(TRUE);
+ /*NOTREACHED*/
+
+ do
+ /* get the player structure filled */
+ {
+ Fileloc = -1L;
+
+ mvaddstr(22, 17, "Do you have a character to run [Q = Quit] ? ");
+
+ switch (getanswer("NYQ", FALSE))
+ {
+ case 'Y':
+ Fileloc = recallplayer();
+ break;
+
+ case 'Q':
+ cleanup(TRUE);
+ /*NOTREACHED*/
+
+ default:
+ Fileloc = rollnewplayer();
+ break;
+ }
+ clear();
+ }
+ while (Fileloc < 0L);
+
+ if (Player.p_level > 5.0)
+ /* low level players have long timeout */
+ Timeout = TRUE;
+
+ /* update some important player statistics */
+ strcpy(Player.p_login, Login);
+ time(&seconds);
+ Player.p_lastused = localtime(&seconds)->tm_yday;
+ Player.p_status = S_PLAYING;
+ writerecord(&Player, Fileloc);
+
+ Statptr = &Stattable[Player.p_type]; /* initialize pointer */
+
+ /* catch interrupts */
+#ifdef BSD41
+ sigset(SIGINT, interrupt);
+#endif
+#ifdef BSD42
+ signal(SIGINT, interrupt);
+#endif
+#ifdef SYS3
+ signal(SIGINT, interrupt);
+#endif
+#ifdef SYS5
+ signal(SIGINT, interrupt);
+#endif
+
+ altercoordinates(Player.p_x, Player.p_y, A_FORCED); /* set some flags */
+
+ clear();
+
+ for (;;)
+ /* loop forever, processing input */
+ {
+
+ adjuststats(); /* cleanup stats */
+
+ if (Throne && Player.p_crowns == 0 && Player.p_specialtype != SC_KING)
+ /* not allowed on throne -- move */
+ {
+ mvaddstr(5,0,"You're not allowed in the Lord's Chamber without a crown.\n");
+ altercoordinates(0.0, 0.0, A_NEAR);
+ }
+
+ checktampered(); /* check for energy voids, etc. */
+
+ if (Player.p_status != S_CLOAKED
+ /* not cloaked */
+ && (dtemp = fabs(Player.p_x)) == fabs(Player.p_y)
+ /* |x| = |y| */
+ && !Throne)
+ /* not on throne */
+ {
+ dtemp = sqrt(dtemp / 100.0);
+ if (floor(dtemp) == dtemp)
+ /* |x| / 100 == n*n; at a trading post */
+ {
+ tradingpost();
+ clear();
+ }
+ }
+
+ checkbattle(); /* check for player to player battle */
+ neatstuff(); /* gurus, medics, etc. */
+
+ if (Player.p_status == S_CLOAKED)
+ /* costs 3 mana per turn to be cloaked */
+ if (Player.p_mana > 3.0)
+ Player.p_mana -= 3.0;
+ else
+ /* ran out of mana, uncloak */
+ {
+ Player.p_status = S_PLAYING;
+ Changed = TRUE;
+ }
+
+ if (Player.p_status != S_PLAYING && Player.p_status != S_CLOAKED)
+ /* change status back to S_PLAYING */
+ {
+ Player.p_status = S_PLAYING;
+ Changed = TRUE;
+ }
+
+ if (Changed)
+ /* update file only if important stuff has changed */
+ {
+ writerecord(&Player, Fileloc);
+ Changed = FALSE;
+ continue;
+ }
+
+ readmessage(); /* read message, if any */
+
+ displaystats(); /* print statistics */
+
+ move(6, 0);
+
+ if (Throne)
+ /* maybe make king, print prompt, etc. */
+ throneroom();
+
+ /* print status line */
+ addstr("1:Move 2:Players 3:Talk 4:Stats 5:Quit ");
+ if (Player.p_level >= MEL_CLOAK && Player.p_magiclvl >= ML_CLOAK)
+ addstr("6:Cloak ");
+ if (Player.p_level >= MEL_TELEPORT && Player.p_magiclvl >= ML_TELEPORT)
+ addstr("7:Teleport ");
+ if (Player.p_specialtype >= SC_COUNCIL || Wizard)
+ addstr("8:Intervene ");
+
+ procmain(); /* process input */
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: initialstate()
+/
+/ FUNCTION: initialize some important global variable
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: time(), fopen(), srandom(), error(), getuid(), getlogin(),
+/ getpwuid()
+/
+/ GLOBAL INPUTS:
+/
+/ GLOBAL OUTPUTS: *Energyvoidfp, Echo, Marsh, *Login, Users, Beyond,
+/ Throne, Wizard, Changed, Okcount, Timeout, Windows, *Monstfp, *Messagefp,
+/ *Playersfp
+/
+/ DESCRIPTION:
+/ Set global flags, and open files which remain open.
+/
+/************************************************************************/
+
+initialstate()
+{
+ Beyond = FALSE;
+ Marsh = FALSE;
+ Throne = FALSE;
+ Changed = FALSE;
+ Wizard = FALSE;
+ Timeout = FALSE;
+ Users = 0;
+ Windows = FALSE;
+ Echo = TRUE;
+
+ /* setup login name */
+ if ((Login = getlogin()) == NULL)
+ Login = getpwuid(getuid())->pw_name;
+
+ /* open some files */
+ if ((Playersfp = fopen(_PATH_PEOPLE, "r+")) == NULL)
+ error(_PATH_PEOPLE);
+ /*NOTREACHED*/
+
+ if ((Monstfp = fopen(_PATH_MONST, "r+")) == NULL)
+ error(_PATH_MONST);
+ /*NOTREACHED*/
+
+ if ((Messagefp = fopen(_PATH_MESS, "r")) == NULL)
+ error(_PATH_MESS);
+ /*NOTREACHED*/
+
+ if ((Energyvoidfp = fopen(_PATH_VOID, "r+")) == NULL)
+ error(_PATH_VOID);
+ /*NOTREACHED*/
+
+ srandom((unsigned) time((long *) NULL)); /* prime random numbers */
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: rollnewplayer()
+/
+/ FUNCTION: roll up a new character
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: initplayer(), allocrecord(), truncstring(), fabs(), wmove(),
+/ wclear(), sscanf(), strcmp(), genchar(), waddstr(), findname(), mvprintw(),
+/ getanswer(), getstring()
+/
+/ GLOBAL INPUTS: Other, Wizard, Player, *stdscr, Databuf[]
+/
+/ GLOBAL OUTPUTS: Echo
+/
+/ DESCRIPTION:
+/ Prompt player, and roll up new character.
+/
+/************************************************************************/
+
+long
+rollnewplayer()
+{
+int chartype; /* character type */
+int ch; /* input */
+
+ initplayer(&Player); /* initialize player structure */
+
+ clear();
+ mvaddstr(4, 21, "Which type of character do you want:");
+ mvaddstr(8, 4, "1:Magic User 2:Fighter 3:Elf 4:Dwarf 5:Halfling 6:Experimento ");
+ if (Wizard) {
+ addstr("7:Super ? ");
+ chartype = getanswer("1234567", FALSE);
+ }
+ else {
+ addstr("? ");
+ chartype = getanswer("123456", FALSE);
+ }
+
+ do
+ {
+ genchar(chartype); /* roll up a character */
+
+ /* print out results */
+ mvprintw(12, 14,
+ "Strength : %2.0f Quickness: %2.0f Mana : %2.0f\n",
+ Player.p_strength, Player.p_quickness, Player.p_mana);
+ mvprintw(13, 14,
+ "Energy Level: %2.0f Brains : %2.0f Magic Level: %2.0f\n",
+ Player.p_energy, Player.p_brains, Player.p_magiclvl);
+
+ if (Player.p_type == C_EXPER || Player.p_type == C_SUPER)
+ break;
+
+ mvaddstr(14, 14, "Type '1' to keep >");
+ ch = getanswer(" ", TRUE);
+ }
+ while (ch != '1');
+
+ if (Player.p_type == C_EXPER || Player.p_type == C_SUPER)
+ /* get coordinates for experimento */
+ for (;;)
+ {
+ mvaddstr(16, 0, "Enter the X Y coordinates of your experimento ? ");
+ getstring(Databuf, SZ_DATABUF);
+ sscanf(Databuf, "%lf %lf", &Player.p_x, &Player.p_y);
+
+ if (fabs(Player.p_x) > D_EXPER || fabs(Player.p_y) > D_EXPER)
+ mvaddstr(17, 0, "Invalid coordinates. Try again.\n");
+ else
+ break;
+ }
+
+ for (;;)
+ /* name the new character */
+ {
+ mvprintw(18, 0,
+ "Give your character a name [up to %d characters] ? ", SZ_NAME - 1);
+ getstring(Player.p_name, SZ_NAME);
+ truncstring(Player.p_name); /* remove trailing blanks */
+
+ if (Player.p_name[0] == '\0')
+ /* no null names */
+ mvaddstr(19, 0, "Invalid name.");
+ else if (findname(Player.p_name, &Other) >= 0L)
+ /* cannot have duplicate names */
+ mvaddstr(19, 0, "Name already in use.");
+ else
+ /* name is acceptable */
+ break;
+
+ addstr(" Pick another.\n");
+ }
+
+ /* get a password for character */
+ Echo = FALSE;
+
+ do
+ {
+ mvaddstr(20, 0, "Give your character a password [up to 8 characters] ? ");
+ getstring(Player.p_password, SZ_PASSWORD);
+ mvaddstr(21, 0, "One more time to verify ? ");
+ getstring(Databuf, SZ_PASSWORD);
+ }
+ while (strcmp(Player.p_password, Databuf) != 0);
+
+ Echo = TRUE;
+
+ return(allocrecord());
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: procmain()
+/
+/ FUNCTION: process input from player
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: dotampered(), changestats(), inputoption(), allstatslist(),
+/ fopen(), wmove(), drandom(), sscanf(), fclose(), altercoordinates(),
+/ waddstr(), fprintf(), distance(), userlist(), leavegame(), encounter(),
+/ getstring(), wclrtobot()
+/
+/ GLOBAL INPUTS: Circle, Illcmd[], Throne, Wizard, Player, *stdscr,
+/ Databuf[], Illmove[]
+/
+/ GLOBAL OUTPUTS: Player, Changed
+/
+/ DESCRIPTION:
+/ Process main menu options.
+/
+/************************************************************************/
+
+procmain()
+{
+int ch; /* input */
+double x; /* desired new x coordinate */
+double y; /* desired new y coordinate */
+double temp; /* for temporary calculations */
+FILE *fp; /* for opening files */
+register int loop; /* a loop counter */
+bool hasmoved = FALSE; /* set if player has moved */
+
+ ch = inputoption();
+ mvaddstr(4, 0, "\n\n"); /* clear status area */
+
+ move(7, 0);
+ clrtobot(); /* clear data on bottom area of screen */
+
+ if (Player.p_specialtype == SC_VALAR && (ch == '1' || ch == '7'))
+ /* valar cannot move */
+ ch = ' ';
+
+ switch (ch)
+ {
+ case 'K': /* move up/north */
+ case 'N':
+ x = Player.p_x;
+ y = Player.p_y + MAXMOVE();
+ hasmoved = TRUE;
+ break;
+
+ case 'J': /* move down/south */
+ case 'S':
+ x = Player.p_x;
+ y = Player.p_y - MAXMOVE();
+ hasmoved = TRUE;
+ break;
+
+ case 'L': /* move right/east */
+ case 'E':
+ x = Player.p_x + MAXMOVE();
+ y = Player.p_y;
+ hasmoved = TRUE;
+ break;
+
+ case 'H': /* move left/west */
+ case 'W':
+ x = Player.p_x - MAXMOVE();
+ y = Player.p_y;
+ hasmoved = TRUE;
+ break;
+
+ default: /* rest */
+ Player.p_energy += (Player.p_maxenergy + Player.p_shield) / 15.0
+ + Player.p_level / 3.0 + 2.0;
+ Player.p_energy =
+ MIN(Player.p_energy, Player.p_maxenergy + Player.p_shield);
+
+ if (Player.p_status != S_CLOAKED)
+ /* cannot find mana if cloaked */
+ {
+ Player.p_mana += (Circle + Player.p_level) / 4.0;
+
+ if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne)
+ /* wandering monster */
+ encounter(-1);
+ }
+ break;
+
+ case 'X': /* change/examine a character */
+ changestats(TRUE);
+ break;
+
+ case '1': /* move */
+ for (loop = 3; loop; --loop)
+ {
+ mvaddstr(4, 0, "X Y Coordinates ? ");
+ getstring(Databuf, SZ_DATABUF);
+
+ if (sscanf(Databuf, "%lf %lf", &x, &y) != 2)
+ mvaddstr(5, 0, "Try again\n");
+ else if (distance(Player.p_x, x, Player.p_y, y) > MAXMOVE())
+ ILLMOVE();
+ else
+ {
+ hasmoved = TRUE;
+ break;
+ }
+ }
+ break;
+
+ case '2': /* players */
+ userlist(TRUE);
+ break;
+
+ case '3': /* message */
+ mvaddstr(4, 0, "Message ? ");
+ getstring(Databuf, SZ_DATABUF);
+ /* we open the file for writing to erase any data which is already there */
+ fp = fopen(_PATH_MESS, "w");
+ if (Databuf[0] != '\0')
+ fprintf(fp, "%s: %s", Player.p_name, Databuf);
+ fclose(fp);
+ break;
+
+ case '4': /* stats */
+ allstatslist();
+ break;
+
+ case '5': /* good-bye */
+ leavegame();
+ /*NOTREACHED*/
+
+ case '6': /* cloak */
+ if (Player.p_level < MEL_CLOAK || Player.p_magiclvl < ML_CLOAK)
+ ILLCMD();
+ else if (Player.p_status == S_CLOAKED)
+ Player.p_status = S_PLAYING;
+ else if (Player.p_mana < MM_CLOAK)
+ mvaddstr(5, 0, "No mana left.\n");
+ else
+ {
+ Changed = TRUE;
+ Player.p_mana -= MM_CLOAK;
+ Player.p_status = S_CLOAKED;
+ }
+ break;
+
+ case '7': /* teleport */
+ /*
+ * conditions for teleport
+ * - 20 per (level plus magic level)
+ * - OR council of the wise or valar or ex-valar
+ * - OR transport from throne
+ * transports from throne cost no mana
+ */
+ if (Player.p_level < MEL_TELEPORT || Player.p_magiclvl < ML_TELEPORT)
+ ILLCMD();
+ else
+ for (loop = 3; loop; --loop)
+ {
+ mvaddstr(4, 0, "X Y Coordinates ? ");
+ getstring(Databuf, SZ_DATABUF);
+
+ if (sscanf(Databuf, "%lf %lf", &x, &y) == 2)
+ {
+ temp = distance(Player.p_x, x, Player.p_y, y);
+ if (!Throne
+ /* can transport anywhere from throne */
+ && Player.p_specialtype <= SC_COUNCIL
+ /* council, valar can transport anywhere */
+ && temp > (Player.p_level + Player.p_magiclvl) * 20.0)
+ /* can only move 20 per exp. level + mag. level */
+ ILLMOVE();
+ else
+ {
+ temp = (temp / 75.0 + 1.0) * 20.0; /* mana used */
+
+ if (!Throne && temp > Player.p_mana)
+ mvaddstr(5, 0, "Not enough power for that distance.\n");
+ else
+ {
+ if (!Throne)
+ Player.p_mana -= temp;
+ hasmoved = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case 'C':
+ case '9': /* monster */
+ if (Throne)
+ /* no monsters while on throne */
+ mvaddstr(5, 0, "No monsters in the chamber!\n");
+ else if (Player.p_specialtype != SC_VALAR)
+ /* the valar cannot call monsters */
+ {
+ Player.p_sin += 1e-6;
+ encounter(-1);
+ }
+ break;
+
+ case '0': /* decree */
+ if (Wizard || Player.p_specialtype == SC_KING && Throne)
+ /* kings must be on throne to decree */
+ dotampered();
+ else
+ ILLCMD();
+ break;
+
+ case '8': /* intervention */
+ if (Wizard || Player.p_specialtype >= SC_COUNCIL)
+ dotampered();
+ else
+ ILLCMD();
+ break;
+ }
+
+ if (hasmoved)
+ /* player has moved -- alter coordinates, and do random monster */
+ {
+ altercoordinates(x, y, A_SPECIFIC);
+
+ if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne)
+ encounter(-1);
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: titlelist()
+/
+/ FUNCTION: print title page
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fread(), fseek(), fopen(), fgets(), wmove(), strcpy(),
+/ fclose(), strlen(), waddstr(), sprintf(), wrefresh()
+/
+/ GLOBAL INPUTS: Lines, Other, *stdscr, Databuf[], *Playersfp
+/
+/ GLOBAL OUTPUTS: Lines
+/
+/ DESCRIPTION:
+/ Print important information about game, players, etc.
+/
+/************************************************************************/
+
+titlelist()
+{
+register FILE *fp; /* used for opening various files */
+bool councilfound = FALSE; /* set if we find a member of the council */
+bool kingfound = FALSE; /* set if we find a king */
+double hiexp, nxtexp; /* used for finding the two highest players */
+double hilvl, nxtlvl; /* used for finding the two highest players */
+char hiname[21], nxtname[21];/* used for finding the two highest players */
+
+ mvaddstr(0, 14, "W e l c o m e t o P h a n t a s i a (vers. 3.3.2)!");
+
+ /* print message of the day */
+ if ((fp = fopen(_PATH_MOTD, "r")) != NULL
+ && fgets(Databuf, SZ_DATABUF, fp) != NULL)
+ {
+ mvaddstr(2, 40 - strlen(Databuf) / 2, Databuf);
+ fclose(fp);
+ }
+
+ /* search for king */
+ fseek(Playersfp, 0L, 0);
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ if (Other.p_specialtype == SC_KING && Other.p_status != S_NOTUSED)
+ /* found the king */
+ {
+ sprintf(Databuf, "The present ruler is %s Level:%.0f",
+ Other.p_name, Other.p_level);
+ mvaddstr(4, 40 - strlen(Databuf) / 2, Databuf);
+ kingfound = TRUE;
+ break;
+ }
+
+ if (!kingfound)
+ mvaddstr(4, 24, "There is no ruler at this time.");
+
+ /* search for valar */
+ fseek(Playersfp, 0L, 0);
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ if (Other.p_specialtype == SC_VALAR && Other.p_status != S_NOTUSED)
+ /* found the valar */
+ {
+ sprintf(Databuf, "The Valar is %s Login: %s", Other.p_name, Other.p_login);
+ mvaddstr(6, 40 - strlen(Databuf) / 2 , Databuf);
+ break;
+ }
+
+ /* search for council of the wise */
+ fseek(Playersfp, 0L, 0);
+ Lines = 10;
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ if (Other.p_specialtype == SC_COUNCIL && Other.p_status != S_NOTUSED)
+ /* found a member of the council */
+ {
+ if (!councilfound)
+ {
+ mvaddstr(8, 30, "Council of the Wise:");
+ councilfound = TRUE;
+ }
+
+ /* This assumes a finite (<=5) number of C.O.W.: */
+ sprintf(Databuf, "%s Login: %s", Other.p_name, Other.p_login);
+ mvaddstr(Lines++, 40 - strlen(Databuf) / 2, Databuf);
+ }
+
+ /* search for the two highest players */
+ nxtname[0] = hiname[0] = '\0';
+ hiexp = 0.0;
+ nxtlvl = hilvl = 0;
+
+ fseek(Playersfp, 0L, 0);
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ if (Other.p_experience > hiexp && Other.p_specialtype <= SC_KING && Other.p_status != S_NOTUSED)
+ /* highest found so far */
+ {
+ nxtexp = hiexp;
+ hiexp = Other.p_experience;
+ nxtlvl = hilvl;
+ hilvl = Other.p_level;
+ strcpy(nxtname, hiname);
+ strcpy(hiname, Other.p_name);
+ }
+ else if (Other.p_experience > nxtexp
+ && Other.p_specialtype <= SC_KING
+ && Other.p_status != S_NOTUSED)
+ /* next highest found so far */
+ {
+ nxtexp = Other.p_experience;
+ nxtlvl = Other.p_level;
+ strcpy(nxtname, Other.p_name);
+ }
+
+ mvaddstr(15, 28, "Highest characters are:");
+ sprintf(Databuf, "%s Level:%.0f and %s Level:%.0f",
+ hiname, hilvl, nxtname, nxtlvl);
+ mvaddstr(17, 40 - strlen(Databuf) / 2, Databuf);
+
+ /* print last to die */
+ if ((fp = fopen(_PATH_LASTDEAD,"r")) != NULL
+ && fgets(Databuf, SZ_DATABUF, fp) != NULL)
+ {
+ mvaddstr(19, 25, "The last character to die was:");
+ mvaddstr(20, 40 - strlen(Databuf) / 2,Databuf);
+ fclose(fp);
+ }
+
+ refresh();
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: recallplayer()
+/
+/ FUNCTION: find a character on file
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: writerecord(), truncstring(), more(), death(), wmove(),
+/ wclear(), strcmp(), printw(), cleanup(), waddstr(), findname(), mvprintw(),
+/ getanswer(), getstring()
+/
+/ GLOBAL INPUTS: Player, *stdscr, Databuf[]
+/
+/ GLOBAL OUTPUTS: Echo, Player
+/
+/ DESCRIPTION:
+/ Search for a character of a certain name, and check password.
+/
+/************************************************************************/
+
+long
+recallplayer()
+{
+long loc = 0L; /* location in player file */
+register int loop; /* loop counter */
+int ch; /* input */
+
+ clear();
+ mvprintw(10, 0, "What was your character's name ? ");
+ getstring(Databuf, SZ_NAME);
+ truncstring(Databuf);
+
+ if ((loc = findname(Databuf, &Player)) >= 0L)
+ /* found character */
+ {
+ Echo = FALSE;
+
+ for (loop = 0; loop < 2; ++loop)
+ {
+ /* prompt for password */
+ mvaddstr(11, 0, "Password ? ");
+ getstring(Databuf, SZ_PASSWORD);
+ if (strcmp(Databuf, Player.p_password) == 0)
+ /* password good */
+ {
+ Echo = TRUE;
+
+ if (Player.p_status != S_OFF)
+ /* player did not exit normally last time */
+ {
+ clear();
+ addstr("Your character did not exit normally last time.\n");
+ addstr("If you think you have good cause to have your character saved,\n");
+ printw("you may quit and mail your reason to 'root'.\n");
+ addstr("Otherwise, continuing spells certain death.\n");
+ addstr("Do you want to quit ? ");
+ ch = getanswer("YN", FALSE);
+ if (ch == 'Y')
+ {
+ Player.p_status = S_HUNGUP;
+ writerecord(&Player, loc);
+ cleanup(TRUE);
+ /*NOTREACHED*/
+ }
+ death("Stupidity");
+ /*NOTREACHED*/
+ }
+ return(loc);
+ }
+ else
+ mvaddstr(12, 0, "No good.\n");
+ }
+
+ Echo = TRUE;
+ }
+ else
+ mvaddstr(11, 0, "Not found.\n");
+
+ more(13);
+ return(-1L);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: neatstuff()
+/
+/ FUNCTION: do random stuff
+/
+/ AUTHOR: E. A. Estes, 3/3/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: collecttaxes(), floor(), wmove(), drandom(), infloat(),
+/ waddstr(), mvprintw(), getanswer()
+/
+/ GLOBAL INPUTS: Player, *stdscr, *Statptr
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Handle gurus, medics, etc.
+/
+/************************************************************************/
+
+neatstuff()
+{
+double temp; /* for temporary calculations */
+int ch; /* input */
+
+ switch ((int) ROLL(0.0, 100.0))
+ {
+ case 1:
+ case 2:
+ if (Player.p_poison > 0.0)
+ {
+ mvaddstr(4, 0, "You've found a medic! How much will you offer to be cured ? ");
+ temp = floor(infloat());
+ if (temp < 0.0 || temp > Player.p_gold)
+ /* negative gold, or more than available */
+ {
+ mvaddstr(6, 0, "He was not amused, and made you worse.\n");
+ Player.p_poison += 1.0;
+ }
+ else if (drandom() / 2.0 > (temp + 1.0) / MAX(Player.p_gold, 1))
+ /* medic wants 1/2 of available gold */
+ mvaddstr(5, 0, "Sorry, he wasn't interested.\n");
+ else
+ {
+ mvaddstr(5, 0, "He accepted.");
+ Player.p_poison = MAX(0.0, Player.p_poison - 1.0);
+ Player.p_gold -= temp;
+ }
+ }
+ break;
+
+ case 3:
+ mvaddstr(4, 0, "You've been caught raping and pillaging!\n");
+ Player.p_experience += 4000.0;
+ Player.p_sin += 0.5;
+ break;
+
+ case 4:
+ temp = ROLL(10.0, 75.0);
+ mvprintw(4, 0, "You've found %.0f gold pieces, want them ? ", temp);
+ ch = getanswer("NY", FALSE);
+
+ if (ch == 'Y')
+ collecttaxes(temp, 0.0);
+ break;
+
+ case 5:
+ if (Player.p_sin > 1.0)
+ {
+ mvaddstr(4, 0, "You've found a Holy Orb!\n");
+ Player.p_sin -= 0.25;
+ }
+ break;
+
+ case 6:
+ if (Player.p_poison < 1.0)
+ {
+ mvaddstr(4, 0, "You've been hit with a plague!\n");
+ Player.p_poison += 1.0;
+ }
+ break;
+
+ case 7:
+ mvaddstr(4, 0, "You've found some holy water.\n");
+ ++Player.p_holywater;
+ break;
+
+ case 8:
+ mvaddstr(4, 0, "You've met a Guru. . .");
+ if (drandom() * Player.p_sin > 1.0)
+ addstr("You disgusted him with your sins!\n");
+ else if (Player.p_poison > 0.0)
+ {
+ addstr("He looked kindly upon you, and cured you.\n");
+ Player.p_poison = 0.0;
+ }
+ else
+ {
+ addstr("He rewarded you for your virtue.\n");
+ Player.p_mana += 50.0;
+ Player.p_shield += 2.0;
+ }
+ break;
+
+ case 9:
+ mvaddstr(4, 0, "You've found an amulet.\n");
+ ++Player.p_amulets;
+ break;
+
+ case 10:
+ if (Player.p_blindness)
+ {
+ mvaddstr(4, 0, "You've regained your sight!\n");
+ Player.p_blindness = FALSE;
+ }
+ break;
+
+ default: /* deal with poison */
+ if (Player.p_poison > 0.0)
+ {
+ temp = Player.p_poison * Statptr->c_weakness
+ * Player.p_maxenergy / 600.0;
+ if (Player.p_energy > Player.p_maxenergy / 10.0
+ && temp + 5.0 < Player.p_energy)
+ Player.p_energy -= temp;
+ }
+ break;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: genchar()
+/
+/ FUNCTION: generate a random character
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ int type - ASCII value of character type to generate
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: floor(), drandom()
+/
+/ GLOBAL INPUTS: Wizard, Player, Stattable[]
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Use the lookup table for rolling stats.
+/
+/************************************************************************/
+
+genchar(type)
+int type;
+{
+register int subscript; /* used for subscripting into Stattable */
+register struct charstats *statptr;/* for pointing into Stattable */
+
+ subscript = type - '1';
+
+ if (subscript < C_MAGIC || subscript > C_EXPER)
+ if (subscript != C_SUPER || !Wizard)
+ /* fighter is default */
+ subscript = C_FIGHTER;
+
+ statptr = &Stattable[subscript];
+
+ Player.p_quickness =
+ ROLL(statptr->c_quickness.base, statptr->c_quickness.interval);
+ Player.p_strength =
+ ROLL(statptr->c_strength.base, statptr->c_strength.interval);
+ Player.p_mana =
+ ROLL(statptr->c_mana.base, statptr->c_mana.interval);
+ Player.p_maxenergy =
+ Player.p_energy =
+ ROLL(statptr->c_energy.base, statptr->c_energy.interval);
+ Player.p_brains =
+ ROLL(statptr->c_brains.base, statptr->c_brains.interval);
+ Player.p_magiclvl =
+ ROLL(statptr->c_magiclvl.base, statptr->c_magiclvl.interval);
+
+ Player.p_type = subscript;
+
+ if (Player.p_type == C_HALFLING)
+ /* give halfling some experience */
+ Player.p_experience = ROLL(600.0, 200.0);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: playinit()
+/
+/ FUNCTION: initialize for playing game
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: signal(), wclear(), noecho(), crmode(), initscr(),
+/ wrefresh()
+/
+/ GLOBAL INPUTS: *stdscr, ill_sig()
+/
+/ GLOBAL OUTPUTS: Windows
+/
+/ DESCRIPTION:
+/ Catch a bunch of signals, and turn on curses stuff.
+/
+/************************************************************************/
+
+playinit()
+{
+ /* catch/ingnore signals */
+
+#ifdef BSD41
+ sigignore(SIGQUIT);
+ sigignore(SIGALRM);
+ sigignore(SIGTERM);
+ sigignore(SIGTSTP);
+ sigignore(SIGTTIN);
+ sigignore(SIGTTOU);
+ sighold(SIGINT);
+ sigset(SIGHUP, ill_sig);
+ sigset(SIGTRAP, ill_sig);
+ sigset(SIGIOT, ill_sig);
+ sigset(SIGEMT, ill_sig);
+ sigset(SIGFPE, ill_sig);
+ sigset(SIGBUS, ill_sig);
+ sigset(SIGSEGV, ill_sig);
+ sigset(SIGSYS, ill_sig);
+ sigset(SIGPIPE, ill_sig);
+#endif
+#ifdef BSD42
+ signal(SIGQUIT, ill_sig);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGINT, ill_sig);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTRAP, ill_sig);
+ signal(SIGIOT, ill_sig);
+ signal(SIGEMT, ill_sig);
+ signal(SIGFPE, ill_sig);
+ signal(SIGBUS, ill_sig);
+ signal(SIGSEGV, ill_sig);
+ signal(SIGSYS, ill_sig);
+ signal(SIGPIPE, ill_sig);
+#endif
+#ifdef SYS3
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, ill_sig);
+ signal(SIGTRAP, ill_sig);
+ signal(SIGIOT, ill_sig);
+ signal(SIGEMT, ill_sig);
+ signal(SIGFPE, ill_sig);
+ signal(SIGBUS, ill_sig);
+ signal(SIGSEGV, ill_sig);
+ signal(SIGSYS, ill_sig);
+ signal(SIGPIPE, ill_sig);
+#endif
+#ifdef SYS5
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, ill_sig);
+ signal(SIGTRAP, ill_sig);
+ signal(SIGIOT, ill_sig);
+ signal(SIGEMT, ill_sig);
+ signal(SIGFPE, ill_sig);
+ signal(SIGBUS, ill_sig);
+ signal(SIGSEGV, ill_sig);
+ signal(SIGSYS, ill_sig);
+ signal(SIGPIPE, ill_sig);
+#endif
+
+ initscr(); /* turn on curses */
+ noecho(); /* do not echo input */
+ crmode(); /* do not process erase, kill */
+ clear();
+ refresh();
+ Windows = TRUE; /* mark the state */
+}
+
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: cleanup()
+/
+/ FUNCTION: close some files, and maybe exit
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ bool doexit - exit flag
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: exit(), wmove(), fclose(), endwin(), nocrmode(), wrefresh()
+/
+/ GLOBAL INPUTS: *Energyvoidfp, LINES, *stdscr, Windows, *Monstfp,
+/ *Messagefp, *Playersfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Close all open files. If we are "in curses" terminate curses.
+/ If 'doexit' is set, exit, otherwise return.
+/
+/************************************************************************/
+
+cleanup(doexit)
+bool doexit;
+{
+ if (Windows)
+ {
+ move(LINES - 2, 0);
+ refresh();
+ nocrmode();
+ endwin();
+ }
+
+ fclose(Playersfp);
+ fclose(Monstfp);
+ fclose(Messagefp);
+ fclose(Energyvoidfp);
+
+ if (doexit)
+ exit(0);
+ /*NOTREACHED*/
+}
diff --git a/phantasia/map.c b/phantasia/map.c
new file mode 100644
index 00000000..be035df1
--- /dev/null
+++ b/phantasia/map.c
@@ -0,0 +1,160 @@
+#define minusminus plusplus
+#define minusplus plusminus
+
+main()
+{
+ /* Set up */
+
+ openpl();
+ space(-1400, -1000, 1200, 1200);
+
+ /* Big box */
+
+ move(-1400, -1000);
+ cont(-1400, 1000);
+ cont(600, 1000);
+ cont(600, -1000);
+ cont(-1400, -1000);
+
+ /* Grid -- horizontal lines every 200 */
+
+ linemod("dotted");
+ line(600, -800, -1400, -800);
+ line(-1400, -600, 600, -600);
+ line(600, -400, -1400, -400);
+ line(-1400, -200, 600, -200);
+ linemod("solid");
+ line(600, 0, -1400, 0);
+ linemod("dotted");
+ line(-1400, 200, 600, 200);
+ line(600, 400, -1400, 400);
+ line(-1400, 600, 600, 600);
+ line(600, 800, -1400, 800);
+
+ /* Grid -- vertical lines every 200 */
+
+ line(-1200, 1000, -1200, -1000);
+ line(-1000, 1000, -1000, -1000);
+ line(-800, 1000, -800, -1000);
+ line(-600, 1000, -600, -1000);
+ linemod("solid");
+ line(-400, 1000, -400, -1000);
+ linemod("dotted");
+ line(-200, 1000, -200, -1000);
+ line(0, 1000, 0, -1000);
+ line(200, 1000, 200, -1000);
+ line(400, 1000, 400, -1000);
+
+ /* Circles radius +250 on "center" */
+
+ linemod("solid");
+ circle(-400, 0, 250);
+ circle(-400, 0, 500);
+ circle(-400, 0, 750);
+ circle(-400, 0, 1000);
+
+ /* A few labels */
+
+ move(-670, 1075);
+ label("- THE PHANTASIA UNIVERSE -");
+ line(-630, 1045, -115, 1045);
+ move(-360, 80);
+ label("Lorien");
+ move(-385, -100);
+ label("Ithilien");
+ move(-560, 80);
+ label("Rohan");
+ move(-580, -100);
+ label("Anorien");
+ plusplus("Rovanion", -250, 320);
+ plusplus("The Iron Hills", -100, 560);
+ plusplus("Rhun", 250, 570);
+ minusplus("Dunland", -700, 160);
+ minusplus("Eriador", -920, 300);
+ minusplus("The Northern Waste", -1240, 320);
+ minusminus("Gondor", -720, -180);
+ minusminus("South Gondor", -940, -270);
+ minusminus("Far Harad", -1100, -500);
+ plusminus("Mordor", -180, -300);
+ plusminus("Khand", 0, -500);
+ plusminus("Near Harad", 40, -780);
+ move(340, 900);
+ label("The Moors");
+ move(300, 840);
+ label("Adventurous");
+ move(340, -840);
+ label("The Moors");
+ move(300, -900);
+ label("Adventurous");
+ move(-1340, 900);
+ label("The Moors");
+ move(-1340, 840);
+ label("Adventurous");
+ move(-1340, -840);
+ label("The Moors");
+ move(-1340, -900);
+ label("Adventurous");
+ move(700, 1000);
+ label("OUTER CIRCLES:");
+ line(690, 970, 1000, 970);
+ move(700, 900);
+ label("> 9: The Outer Waste");
+ move(700, 800);
+ label("> 20: The Dead Marshes");
+ move(700, 700);
+ label("> 35: Kennaquhair");
+ move(700, 600);
+ label("> 55: Morannon");
+ move(700, 300);
+ label("(0,0): The Lord's Chamber");
+
+ move(700, -400);
+ label("Grid squares are 100 x 100");
+ move(700, -800);
+ label("Created by Ted Estes");
+ move(700, -860);
+ label("Plotted by Chris Robertson");
+ move(700, -920);
+ label(" c 1985");
+ circle(723, -923, 20);
+
+ /* Close down */
+
+ move(-1380, 1180);
+ closepl();
+ exit(0);
+}
+
+plusplus(s, x, y) /* draw strings in plus plus quadrant */
+char *s;
+int x, y;
+{
+char s1[2];
+
+ while (*s)
+ {
+ move(x, y);
+ s1[0] = *s++;
+ s1[1] = '\0';
+ label(s1);
+ x += 25;
+ y -= 30;
+ }
+}
+
+plusminus(s, x, y) /* draw strings in plus minus quadrant */
+char *s;
+int x, y;
+{
+char s1[2];
+
+ while (*s)
+ {
+ move(x, y);
+ s1[0] = *s++;
+ s1[1] = '\0';
+ label(s1);
+ x += 25;
+ y += 30;
+ }
+}
diff --git a/phantasia/misc.c b/phantasia/misc.c
new file mode 100644
index 00000000..42c039d2
--- /dev/null
+++ b/phantasia/misc.c
@@ -0,0 +1,1703 @@
+/*
+ * misc.c Phantasia miscellaneous support routines
+ */
+
+#include "include.h"
+
+
+/************************************************************************
+/
+/ FUNCTION NAME: movelevel()
+/
+/ FUNCTION: move player to new level
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: death(), floor(), wmove(), drandom(), waddstr(), explevel()
+/
+/ GLOBAL INPUTS: Player, *stdscr, *Statptr, Stattable[]
+/
+/ GLOBAL OUTPUTS: Player, Changed
+/
+/ DESCRIPTION:
+/ Use lookup table to increment important statistics when
+/ progressing to new experience level.
+/ Players are rested to maximum as a bonus for making a new
+/ level.
+/ Check for council of wise, and being too big to be king.
+/
+/************************************************************************/
+
+movelevel()
+{
+register struct charstats *statptr; /* for pointing into Stattable */
+double new; /* new level */
+double inc; /* increment between new and old levels */
+
+ Changed = TRUE;
+
+ if (Player.p_type == C_EXPER)
+ /* roll a type to use for increment */
+ statptr = &Stattable[(int) ROLL(C_MAGIC, C_HALFLING - C_MAGIC + 1)];
+ else
+ statptr = Statptr;
+
+ new = explevel(Player.p_experience);
+ inc = new - Player.p_level;
+ Player.p_level = new;
+
+ /* add increments to statistics */
+ Player.p_strength += statptr->c_strength.increase * inc;
+ Player.p_mana += statptr->c_mana.increase * inc;
+ Player.p_brains += statptr->c_brains.increase * inc;
+ Player.p_magiclvl += statptr->c_magiclvl.increase * inc;
+ Player.p_maxenergy += statptr->c_energy.increase * inc;
+
+ /* rest to maximum upon reaching new level */
+ Player.p_energy = Player.p_maxenergy + Player.p_shield;
+
+ if (Player.p_crowns > 0 && Player.p_level >= 1000.0)
+ /* no longer able to be king -- turn crowns into cash */
+ {
+ Player.p_gold += ((double) Player.p_crowns) * 5000.0;
+ Player.p_crowns = 0;
+ }
+
+ if (Player.p_level >= 3000.0 && Player.p_specialtype < SC_COUNCIL)
+ /* make a member of the council */
+ {
+ mvaddstr(6, 0, "You have made it to the Council of the Wise.\n");
+ addstr("Good Luck on your search for the Holy Grail.\n");
+
+ Player.p_specialtype = SC_COUNCIL;
+
+ /* no rings for council and above */
+ Player.p_ring.ring_type = R_NONE;
+ Player.p_ring.ring_duration = 0;
+
+ Player.p_lives = 3; /* three extra lives */
+ }
+
+ if (Player.p_level > 9999.0 && Player.p_specialtype != SC_VALAR)
+ death("Old age");
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: descrlocation()
+/
+/ FUNCTION: return a formatted description of location
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ struct player playerp - pointer to player structure
+/ bool shortflag - set if short form is desired
+/
+/ RETURN VALUE: pointer to string containing result
+/
+/ MODULES CALLED: fabs(), floor(), sprintf(), distance()
+/
+/ GLOBAL INPUTS: Databuf[]
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Look at coordinates and return an appropriately formatted
+/ string.
+/
+/************************************************************************/
+
+char *
+descrlocation(playerp, shortflag)
+struct player *playerp;
+bool shortflag;
+{
+double circle; /* corresponding circle for coordinates */
+register int quadrant; /* quandrant of grid */
+register char *label; /* pointer to place name */
+static char *nametable[4][4] = /* names of places */
+ {
+ "Anorien", "Ithilien", "Rohan", "Lorien",
+ "Gondor", "Mordor", "Dunland", "Rovanion",
+ "South Gondor", "Khand", "Eriador", "The Iron Hills",
+ "Far Harad", "Near Harad", "The Northern Waste", "Rhun"
+ };
+
+ if (playerp->p_specialtype == SC_VALAR)
+ return(" is in Valhala");
+ else if ((circle = CIRCLE(playerp->p_x, playerp->p_y)) >= 1000.0)
+ {
+ if (MAX(fabs(playerp->p_x), fabs(playerp->p_y)) > D_BEYOND)
+ label = "The Point of No Return";
+ else
+ label = "The Ashen Mountains";
+ }
+ else if (circle >= 55)
+ label = "Morannon";
+ else if (circle >= 35)
+ label = "Kennaquahair";
+ else if (circle >= 20)
+ label = "The Dead Marshes";
+ else if (circle >= 9)
+ label = "The Outer Waste";
+ else if (circle >= 5)
+ label = "The Moors Adventurous";
+ else
+ {
+ if (playerp->p_x == 0.0 && playerp->p_y == 0.0)
+ label = "The Lord's Chamber";
+ else
+ {
+ /* this expression is split to prevent compiler loop with some compilers */
+ quadrant = ((playerp->p_x > 0.0) ? 1 : 0);
+ quadrant += ((playerp->p_y >= 0.0) ? 2 : 0);
+ label = nametable[((int) circle) - 1][quadrant];
+ }
+ }
+
+ if (shortflag)
+ sprintf(Databuf, "%.29s", label);
+ else
+ sprintf(Databuf, " is in %s (%.0f,%.0f)", label, playerp->p_x, playerp->p_y);
+
+ return(Databuf);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: tradingpost()
+/
+/ FUNCTION: do trading post stuff
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: writerecord(), adjuststats(), fabs(), more(), sqrt(),
+/ sleep(), floor(), wmove(), drandom(), wclear(), printw(),
+/ altercoordinates(), infloat(), waddstr(), wrefresh(), mvprintw(), getanswer(),
+/ wclrtoeol(), wclrtobot()
+/
+/ GLOBAL INPUTS: Menu[], Circle, Player, *stdscr, Fileloc, Nobetter[]
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Different trading posts have different items.
+/ Merchants cannot be cheated, but they can be dishonest
+/ themselves.
+/
+/ Shields, swords, and quicksilver are not cumulative. This is
+/ one major area of complaint, but there are two reasons for this:
+/ 1) It becomes MUCH too easy to make very large versions
+/ of these items.
+/ 2) In the real world, one cannot simply weld two swords
+/ together to make a bigger one.
+/
+/ At one time, it was possible to sell old weapons at half the purchase
+/ price. This resulted in huge amounts of gold floating around,
+/ and the game lost much of its challenge.
+/
+/ Also, purchasing gems defeats the whole purpose of gold. Gold
+/ is small change for lower level players. They really shouldn't
+/ be able to accumulate more than enough gold for a small sword or
+/ a few books. Higher level players shouldn't even bother to pick
+/ up gold, except maybe to buy mana once in a while.
+/
+/************************************************************************/
+
+tradingpost()
+{
+double numitems; /* number of items to purchase */
+double cost; /* cost of purchase */
+double blessingcost; /* cost of blessing */
+int ch; /* input */
+register int size; /* size of the trading post */
+register int loop; /* loop counter */
+int cheat = 0; /* number of times player has tried to cheat */
+bool dishonest = FALSE;/* set when merchant is dishonest */
+
+ Player.p_status = S_TRADING;
+ writerecord(&Player, Fileloc);
+
+ clear();
+ addstr("You are at a trading post. All purchases must be made with gold.");
+
+ size = sqrt(fabs(Player.p_x / 100)) + 1;
+ size = MIN(7, size);
+
+ /* set up cost of blessing */
+ blessingcost = 1000.0 * (Player.p_level + 5.0);
+
+ /* print Menu */
+ move(7, 0);
+ for (loop = 0; loop < size; ++loop)
+ /* print Menu */
+ {
+ if (loop == 6)
+ cost = blessingcost;
+ else
+ cost = Menu[loop].cost;
+ printw("(%d) %-12s: %6.0f\n", loop + 1, Menu[loop].item, cost);
+ }
+
+ mvprintw(5, 0, "L:Leave P:Purchase S:Sell Gems ? ");
+
+ for (;;)
+ {
+ adjuststats(); /* truncate any bad values */
+
+ /* print some important statistics */
+ mvprintw(1, 0, "Gold: %9.0f Gems: %9.0f Level: %6.0f Charms: %6d\n",
+ Player.p_gold, Player.p_gems, Player.p_level, Player.p_charms);
+ printw("Shield: %9.0f Sword: %9.0f Quicksilver:%3.0f Blessed: %s\n",
+ Player.p_shield, Player.p_sword, Player.p_quksilver,
+ (Player.p_blessing ? " True" : "False"));
+ printw("Brains: %9.0f Mana: %9.0f", Player.p_brains, Player.p_mana);
+
+ move(5, 36);
+ ch = getanswer("LPS", FALSE);
+ move(15, 0);
+ clrtobot();
+ switch(ch)
+ {
+ case 'L': /* leave */
+ case '\n':
+ altercoordinates(0.0, 0.0, A_NEAR);
+ return;
+
+ case 'P': /* make purchase */
+ mvaddstr(15, 0, "What what would you like to buy ? ");
+ ch = getanswer(" 1234567", FALSE);
+ move(15, 0);
+ clrtoeol();
+
+ if (ch - '0' > size)
+ addstr("Sorry, this merchant doesn't have that.");
+ else
+ switch (ch)
+ {
+ case '1':
+ printw("Mana is one per %.0f gold piece. How many do you want (%.0f max) ? ",
+ Menu[0].cost, floor(Player.p_gold / Menu[0].cost));
+ cost = (numitems = floor(infloat())) * Menu[0].cost;
+
+ if (cost > Player.p_gold || numitems < 0)
+ ++cheat;
+ else
+ {
+ cheat = 0;
+ Player.p_gold -= cost;
+ if (drandom() < 0.02)
+ dishonest = TRUE;
+ else
+ Player.p_mana += numitems;
+ }
+ break;
+
+ case '2':
+ printw("Shields are %.0f per +1. How many do you want (%.0f max) ? ",
+ Menu[1].cost, floor(Player.p_gold / Menu[1].cost));
+ cost = (numitems = floor(infloat())) * Menu[1].cost;
+
+ if (numitems == 0.0)
+ break;
+ else if (cost > Player.p_gold || numitems < 0)
+ ++cheat;
+ else if (numitems < Player.p_shield)
+ NOBETTER();
+ else
+ {
+ cheat = 0;
+ Player.p_gold -= cost;
+ if (drandom() < 0.02)
+ dishonest = TRUE;
+ else
+ Player.p_shield = numitems;
+ }
+ break;
+
+ case '3':
+ printw("A book costs %.0f gp. How many do you want (%.0f max) ? ",
+ Menu[2].cost, floor(Player.p_gold / Menu[2].cost));
+ cost = (numitems = floor(infloat())) * Menu[2].cost;
+
+ if (cost > Player.p_gold || numitems < 0)
+ ++cheat;
+ else
+ {
+ cheat = 0;
+ Player.p_gold -= cost;
+ if (drandom() < 0.02)
+ dishonest = TRUE;
+ else if (drandom() * numitems > Player.p_level / 10.0
+ && numitems != 1)
+ {
+ printw("\nYou blew your mind!\n");
+ Player.p_brains /= 5;
+ }
+ else
+ {
+ Player.p_brains += floor(numitems) * ROLL(20, 8);
+ }
+ }
+ break;
+
+ case '4':
+ printw("Swords are %.0f gp per +1. How many + do you want (%.0f max) ? ",
+ Menu[3].cost, floor(Player.p_gold / Menu[3].cost));
+ cost = (numitems = floor(infloat())) * Menu[3].cost;
+
+ if (numitems == 0.0)
+ break;
+ else if (cost > Player.p_gold || numitems < 0)
+ ++cheat;
+ else if (numitems < Player.p_sword)
+ NOBETTER();
+ else
+ {
+ cheat = 0;
+ Player.p_gold -= cost;
+ if (drandom() < 0.02)
+ dishonest = TRUE;
+ else
+ Player.p_sword = numitems;
+ }
+ break;
+
+ case '5':
+ printw("A charm costs %.0f gp. How many do you want (%.0f max) ? ",
+ Menu[4].cost, floor(Player.p_gold / Menu[4].cost));
+ cost = (numitems = floor(infloat())) * Menu[4].cost;
+
+ if (cost > Player.p_gold || numitems < 0)
+ ++cheat;
+ else
+ {
+ cheat = 0;
+ Player.p_gold -= cost;
+ if (drandom() < 0.02)
+ dishonest = TRUE;
+ else
+ Player.p_charms += numitems;
+ }
+ break;
+
+ case '6':
+ printw("Quicksilver is %.0f gp per +1. How many + do you want (%.0f max) ? ",
+ Menu[5].cost, floor(Player.p_gold / Menu[5].cost));
+ cost = (numitems = floor(infloat())) * Menu[5].cost;
+
+ if (numitems == 0.0)
+ break;
+ else if (cost > Player.p_gold || numitems < 0)
+ ++cheat;
+ else if (numitems < Player.p_quksilver)
+ NOBETTER();
+ else
+ {
+ cheat = 0;
+ Player.p_gold -= cost;
+ if (drandom() < 0.02)
+ dishonest = TRUE;
+ else
+ Player.p_quksilver = numitems;
+ }
+ break;
+
+ case '7':
+ if (Player.p_blessing)
+ {
+ addstr("You already have a blessing.");
+ break;
+ }
+
+ printw("A blessing requires a %.0f gp donation. Still want one ? ", blessingcost);
+ ch = getanswer("NY", FALSE);
+
+ if (ch == 'Y')
+ if (Player.p_gold < blessingcost)
+ ++cheat;
+ else
+ {
+ cheat = 0;
+ Player.p_gold -= blessingcost;
+ if (drandom() < 0.02)
+ dishonest = TRUE;
+ else
+ Player.p_blessing = TRUE;
+ }
+ break;
+ }
+ break;
+
+ case 'S': /* sell gems */
+ mvprintw(15, 0, "A gem is worth %.0f gp. How many do you want to sell (%.0f max) ? ",
+ (double) N_GEMVALUE, Player.p_gems);
+ numitems = floor(infloat());
+
+ if (numitems > Player.p_gems || numitems < 0)
+ ++cheat;
+ else
+ {
+ cheat = 0;
+ Player.p_gems -= numitems;
+ Player.p_gold += numitems * N_GEMVALUE;
+ }
+ }
+
+ if (cheat == 1)
+ mvaddstr(17, 0, "Come on, merchants aren't stupid. Stop cheating.\n");
+ else if (cheat == 2)
+ {
+ mvaddstr(17, 0, "You had your chance. This merchant happens to be\n");
+ printw("a %.0f level magic user, and you made %s mad!\n",
+ ROLL(Circle * 20.0, 40.0), (drandom() < 0.5) ? "him" : "her");
+ altercoordinates(0.0, 0.0, A_FAR);
+ Player.p_energy /= 2.0;
+ ++Player.p_sin;
+ more(23);
+ return;
+ }
+ else if (dishonest)
+ {
+ mvaddstr(17, 0, "The merchant stole your money!");
+ refresh();
+ altercoordinates(Player.p_x - Player.p_x / 10.0,
+ Player.p_y - Player.p_y / 10.0, A_SPECIFIC);
+ sleep(2);
+ return;
+ }
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: displaystats()
+/
+/ FUNCTION: print out important player statistics
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: descrstatus(), descrlocation(), mvprintw()
+/
+/ GLOBAL INPUTS: Users, Player
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Important player statistics are printed on the screen.
+/
+/************************************************************************/
+
+displaystats()
+{
+ mvprintw(0, 0, "%s%s\n", Player.p_name, descrlocation(&Player, FALSE));
+ mvprintw(1, 0, "Level :%7.0f Energy :%9.0f(%9.0f) Mana :%9.0f Users:%3d\n",
+ Player.p_level, Player.p_energy, Player.p_maxenergy + Player.p_shield,
+ Player.p_mana, Users);
+ mvprintw(2, 0, "Quick :%3.0f(%3.0f) Strength:%9.0f(%9.0f) Gold :%9.0f %s\n",
+ Player.p_speed, Player.p_quickness + Player.p_quksilver, Player.p_might,
+ Player.p_strength + Player.p_sword, Player.p_gold, descrstatus(&Player));
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: allstatslist()
+/
+/ FUNCTION: show player items
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: mvprintw(), descrtype()
+/
+/ GLOBAL INPUTS: Player
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Print out some player statistics of lesser importance.
+/
+/************************************************************************/
+
+allstatslist()
+{
+static char *flags[] = /* to print value of some bools */
+ {
+ "False",
+ " True"
+ };
+
+ mvprintw( 8, 0, "Type: %s\n", descrtype(&Player, FALSE));
+
+ mvprintw(10, 0, "Experience: %9.0f", Player.p_experience);
+ mvprintw(11, 0, "Brains : %9.0f", Player.p_brains);
+ mvprintw(12, 0, "Magic Lvl : %9.0f", Player.p_magiclvl);
+ mvprintw(13, 0, "Sin : %9.5f", Player.p_sin);
+ mvprintw(14, 0, "Poison : %9.5f", Player.p_poison);
+ mvprintw(15, 0, "Gems : %9.0f", Player.p_gems);
+ mvprintw(16, 0, "Age : %9d", Player.p_age);
+ mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater);
+ mvprintw(11, 40, "Amulets : %9d", Player.p_amulets);
+ mvprintw(12, 40, "Charms : %9d", Player.p_charms);
+ mvprintw(13, 40, "Crowns : %9d", Player.p_crowns);
+ mvprintw(14, 40, "Shield : %9.0f", Player.p_shield);
+ mvprintw(15, 40, "Sword : %9.0f", Player.p_sword);
+ mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver);
+
+ mvprintw(18, 0, "Blessing: %s Ring: %s Virgin: %s Palantir: %s",
+ flags[Player.p_blessing], flags[Player.p_ring.ring_type != R_NONE],
+ flags[Player.p_virgin], flags[Player.p_palantir]);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: descrtype()
+/
+/ FUNCTION: return a string specifying player type
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ struct player playerp - pointer to structure for player
+/ bool shortflag - set if short form is desired
+/
+/ RETURN VALUE: pointer to string describing player type
+/
+/ MODULES CALLED: strcpy()
+/
+/ GLOBAL INPUTS: Databuf[]
+/
+/ GLOBAL OUTPUTS: Databuf[]
+/
+/ DESCRIPTION:
+/ Return a string describing the player type.
+/ King, council, valar, supercedes other types.
+/ The first character of the string is '*' if the player
+/ has a crown.
+/ If 'shortflag' is TRUE, return a 3 character string.
+/
+/************************************************************************/
+
+char *
+descrtype(playerp, shortflag)
+struct player *playerp;
+bool shortflag;
+{
+register int type; /* for caluculating result subscript */
+static char *results[] = /* description table */
+ {
+ " Magic User", " MU",
+ " Fighter", " F ",
+ " Elf", " E ",
+ " Dwarf", " D ",
+ " Halfling", " H ",
+ " Experimento", " EX",
+ " Super", " S ",
+ " King", " K ",
+ " Council of Wise", " CW",
+ " Ex-Valar", " EV",
+ " Valar", " V ",
+ " ? ", " ? "
+ };
+
+ type = playerp->p_type;
+
+ switch (playerp->p_specialtype)
+ {
+ case SC_NONE:
+ type = playerp->p_type;
+ break;
+
+ case SC_KING:
+ type = 7;
+ break;
+
+ case SC_COUNCIL:
+ type = 8;
+ break;
+
+ case SC_EXVALAR:
+ type = 9;
+ break;
+
+ case SC_VALAR:
+ type = 10;
+ break;
+ }
+
+ type *= 2; /* calculate offset */
+
+ if (type > 20)
+ /* error */
+ type = 22;
+
+ if (shortflag)
+ /* use short descriptions */
+ ++type;
+
+ if (playerp->p_crowns > 0)
+ {
+ strcpy(Databuf, results[type]);
+ Databuf[0] = '*';
+ return(Databuf);
+ }
+ else
+ return(results[type]);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: findname()
+/
+/ FUNCTION: find location in player file of given name
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ char *name - name of character to look for
+/ struct player *playerp - pointer of structure to fill
+/
+/ RETURN VALUE: location of player if found, -1 otherwise
+/
+/ MODULES CALLED: fread(), fseek(), strcmp()
+/
+/ GLOBAL INPUTS: Wizard, *Playersfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Search the player file for the player of the given name.
+/ If player is found, fill structure with player data.
+/
+/************************************************************************/
+
+long
+findname(name, playerp)
+register char *name;
+register struct player *playerp;
+{
+long loc = 0; /* location in the file */
+
+ fseek(Playersfp, 0L, 0);
+ while (fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ {
+ if (strcmp(playerp->p_name, name) == 0)
+ {
+ if (playerp->p_status != S_NOTUSED || Wizard)
+ /* found it */
+ return(loc);
+ }
+ loc += SZ_PLAYERSTRUCT;
+ }
+
+ return(-1);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: allocrecord()
+/
+/ FUNCTION: find space in the player file for a new character
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: location of free space in file
+/
+/ MODULES CALLED: initplayer(), writerecord(), fread(), fseek()
+/
+/ GLOBAL INPUTS: Other, *Playersfp
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Search the player file for an unused entry. If none are found,
+/ make one at the end of the file.
+/
+/************************************************************************/
+
+long
+allocrecord()
+{
+long loc = 0L; /* location in file */
+
+ fseek(Playersfp, 0L, 0);
+ while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
+ {
+ if (Other.p_status == S_NOTUSED)
+ /* found an empty record */
+ return(loc);
+ else
+ loc += SZ_PLAYERSTRUCT;
+ }
+
+ /* make a new record */
+ initplayer(&Other);
+ Player.p_status = S_OFF;
+ writerecord(&Other, loc);
+
+ return(loc);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: freerecord()
+/
+/ FUNCTION: free up a record on the player file
+/
+/ AUTHOR: E. A. Estes, 2/7/86
+/
+/ ARGUMENTS:
+/ struct player playerp - pointer to structure to free
+/ long loc - location in file to free
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: writerecord()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Mark structure as not used, and update player file.
+/
+/************************************************************************/
+
+freerecord(playerp, loc)
+struct player *playerp;
+long loc;
+{
+ playerp->p_name[0] = CH_MARKDELETE;
+ playerp->p_status = S_NOTUSED;
+ writerecord(playerp, loc);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: leavegame()
+/
+/ FUNCTION: leave game
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: freerecord(), writerecord(), cleanup()
+/
+/ GLOBAL INPUTS: Player, Fileloc
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Mark player as inactive, and cleanup.
+/ Do not save players below level 1.
+/
+/************************************************************************/
+
+leavegame()
+{
+
+ if (Player.p_level < 1.0)
+ /* delete character */
+ freerecord(&Player, Fileloc);
+ else
+ {
+ Player.p_status = S_OFF;
+ writerecord(&Player, Fileloc);
+ }
+
+ cleanup(TRUE);
+ /*NOTREACHED*/
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: death()
+/
+/ FUNCTION: death routine
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ char *how - pointer to string describing cause of death
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: freerecord(), enterscore(), more(), exit(), fread(),
+/ fseek(), execl(), fopen(), floor(), wmove(), drandom(), wclear(), strcmp(),
+/ fwrite(), fflush(), printw(), strcpy(), fclose(), waddstr(), cleanup(),
+/ fprintf(), wrefresh(), getanswer(), descrtype()
+/
+/ GLOBAL INPUTS: Curmonster, Wizard, Player, *stdscr, Fileloc, *Monstfp
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Kill off current player.
+/ Handle rings, and multiple lives.
+/ Print an appropriate message.
+/ Update scoreboard, lastdead, and let other players know about
+/ the demise of their comrade.
+/
+/************************************************************************/
+
+death(how)
+char *how;
+{
+FILE *fp; /* for updating various files */
+int ch; /* input */
+static char *deathmesg[] =
+ /* add more messages here, if desired */
+ {
+ "You have been wounded beyond repair. ",
+ "You have been disemboweled. ",
+ "You've been mashed, mauled, and spit upon. (You're dead.)\n",
+ "You died! ",
+ "You're a complete failure -- you've died!!\n",
+ "You have been dealt a fatal blow! "
+ };
+
+ clear();
+
+ if (strcmp(how, "Stupidity") != 0)
+ {
+ if (Player.p_level > 9999.0)
+ /* old age */
+ addstr("Characters must be retired upon reaching level 10000. Sorry.");
+ else if (Player.p_lives > 0)
+ /* extra lives */
+ {
+ addstr("You should be more cautious. You've been killed.\n");
+ printw("You only have %d more chance(s).\n", --Player.p_lives);
+ more(3);
+ Player.p_energy = Player.p_maxenergy;
+ return;
+ }
+ else if (Player.p_specialtype == SC_VALAR)
+ {
+ addstr("You had your chances, but Valar aren't totally\n");
+ addstr("immortal. You are now left to wither and die . . .\n");
+ more(3);
+ Player.p_brains = Player.p_level / 25.0;
+ Player.p_energy = Player.p_maxenergy /= 5.0;
+ Player.p_quksilver = Player.p_sword = 0.0;
+ Player.p_specialtype = SC_COUNCIL;
+ return;
+ }
+ else if (Player.p_ring.ring_inuse &&
+ (Player.p_ring.ring_type == R_DLREG || Player.p_ring.ring_type == R_NAZREG))
+ /* good ring in use - saved from death */
+ {
+ mvaddstr(4, 0, "Your ring saved you from death!\n");
+ refresh();
+ Player.p_ring.ring_type = R_NONE;
+ Player.p_energy = Player.p_maxenergy / 12.0 + 1.0;
+ if (Player.p_crowns > 0)
+ --Player.p_crowns;
+ return;
+ }
+ else if (Player.p_ring.ring_type == R_BAD
+ || Player.p_ring.ring_type == R_SPOILED)
+ /* bad ring in possession; name idiot after player */
+ {
+ mvaddstr(4, 0,
+ "Your ring has taken control of you and turned you into a monster!\n");
+ fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, 0);
+ fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
+ strcpy(Curmonster.m_name, Player.p_name);
+ fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, 0);
+ fwrite((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
+ fflush(Monstfp);
+ }
+ }
+
+ enterscore(); /* update score board */
+
+ /* put info in last dead file */
+ fp = fopen(_PATH_LASTDEAD, "w");
+ fprintf(fp,"%s (%s, run by %s, level %.0f, killed by %s)",
+ Player.p_name, descrtype(&Player, TRUE),
+ Player.p_login, Player.p_level, how);
+ fclose(fp);
+
+ /* let other players know */
+ fp = fopen(_PATH_MESS, "w");
+ fprintf(fp, "%s was killed by %s.", Player.p_name, how);
+ fclose(fp);
+
+ freerecord(&Player, Fileloc);
+
+ clear();
+ move(10, 0);
+ addstr(deathmesg[(int) ROLL(0.0, (double) sizeof(deathmesg) / sizeof(char *))]);
+ addstr("Care to give it another try ? ");
+ ch = getanswer("NY", FALSE);
+
+ if (ch == 'Y')
+ {
+ cleanup(FALSE);
+ execl(_PATH_GAMEPROG, "phantasia", "-s",
+ (Wizard ? "-S": (char *) NULL), 0);
+ exit(0);
+ /*NOTREACHED*/
+ }
+
+ cleanup(TRUE);
+ /*NOTREACHED*/
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: writerecord()
+/
+/ FUNCTION: update structure in player file
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ struct player *playerp - pointer to structure to write out
+/ long place - location in file to updata
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fseek(), fwrite(), fflush()
+/
+/ GLOBAL INPUTS: *Playersfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Update location in player file with given structure.
+/
+/************************************************************************/
+
+writerecord(playerp, place)
+register struct player *playerp;
+long place;
+{
+ fseek(Playersfp, place, 0);
+ fwrite((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
+ fflush(Playersfp);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: explevel()
+/
+/ FUNCTION: calculate level based upon experience
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ double experience - experience to calculate experience level from
+/
+/ RETURN VALUE: experience level
+/
+/ MODULES CALLED: pow(), floor()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Experience level is a geometric progression. This has been finely
+/ tuned over the years, and probably should not be changed.
+/
+/************************************************************************/
+
+double
+explevel(experience)
+double experience;
+{
+ if (experience < 1.1e7)
+ return(floor(pow((experience / 1000.0), 0.4875)));
+ else
+ return(floor(pow((experience / 1250.0), 0.4865)));
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: truncstring()
+/
+/ FUNCTION: truncate trailing blanks off a string
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ char *string - pointer to null terminated string
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: strlen()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Put nul characters in place of spaces at the end of the string.
+/
+/************************************************************************/
+
+truncstring(string)
+register char *string;
+{
+register int length; /* length of string */
+
+ length = strlen(string);
+ while (string[--length] == ' ')
+ string[length] = '\0';
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: altercoordinates()
+/
+/ FUNCTION: Alter x, y coordinates and set/check location flags
+/
+/ AUTHOR: E. A. Estes, 12/16/85
+/
+/ ARGUMENTS:
+/ double xnew, ynew - new x, y coordinates
+/ int operation - operation to perform with coordinates
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fabs(), floor(), drandom(), distance()
+/
+/ GLOBAL INPUTS: Circle, Beyond, Player
+/
+/ GLOBAL OUTPUTS: Marsh, Circle, Beyond, Throne, Player, Changed
+/
+/ DESCRIPTION:
+/ This module is called whenever the player's coordinates are altered.
+/ If the player is beyond the point of no return, he/she is forced
+/ to stay there.
+/
+/************************************************************************/
+
+altercoordinates(xnew, ynew, operation)
+double xnew;
+double ynew;
+int operation;
+{
+ switch (operation)
+ {
+ case A_FORCED: /* move with no checks */
+ break;
+
+ case A_NEAR: /* pick random coordinates near */
+ xnew = Player.p_x + ROLL(1.0, 5.0);
+ ynew = Player.p_y - ROLL(1.0, 5.0);
+ /* fall through for check */
+
+ case A_SPECIFIC: /* just move player */
+ if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND)
+ /*
+ * cannot move back from point of no return
+ * pick the largest coordinate to remain unchanged
+ */
+ {
+ if (fabs(xnew) > fabs(ynew))
+ xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND);
+ else
+ ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND);
+ }
+ break;
+
+ case A_FAR: /* pick random coordinates far */
+ xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle);
+ ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle);
+ break;
+ }
+
+ /* now set location flags and adjust coordinates */
+ Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew));
+
+ /* set up flags based upon location */
+ Throne = Marsh = Beyond = FALSE;
+
+ if (Player.p_x == 0.0 && Player.p_y == 0.0)
+ Throne = TRUE;
+ else if (Circle < 35 && Circle >= 20)
+ Marsh = TRUE;
+ else if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND)
+ Beyond = TRUE;
+
+ Changed = TRUE;
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: readrecord()
+/
+/ FUNCTION: read a player structure from file
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ struct player *playerp - pointer to structure to fill
+/ int loc - location of record to read
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fread(), fseek()
+/
+/ GLOBAL INPUTS: *Playersfp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Read structure information from player file.
+/
+/************************************************************************/
+
+readrecord(playerp, loc)
+register struct player *playerp;
+long loc;
+{
+ fseek(Playersfp, loc, 0);
+ fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: adjuststats()
+/
+/ FUNCTION: adjust player statistics
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: death(), floor(), drandom(), explevel(), movelevel()
+/
+/ GLOBAL INPUTS: Player, *Statptr
+/
+/ GLOBAL OUTPUTS: Circle, Player, Timeout
+/
+/ DESCRIPTION:
+/ Handle adjustment and maximums on various player characteristics.
+/
+/************************************************************************/
+
+adjuststats()
+{
+double dtemp; /* for temporary calculations */
+
+ if (explevel(Player.p_experience) > Player.p_level)
+ /* move one or more levels */
+ {
+ movelevel();
+ if (Player.p_level > 5.0)
+ Timeout = TRUE;
+ }
+
+ if (Player.p_specialtype == SC_VALAR)
+ /* valar */
+ Circle = Player.p_level / 5.0;
+
+ /* calculate effective quickness */
+ dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote
+ - Player.p_level;;
+ dtemp = MAX(0.0, dtemp); /* gold slows player down */
+ Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp;
+
+ /* calculate effective strength */
+ if (Player.p_poison > 0.0)
+ /* poison makes player weaker */
+ {
+ dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0;
+ dtemp = MAX(0.1, dtemp);
+ }
+ else
+ dtemp = 1.0;
+ Player.p_might = dtemp * Player.p_strength + Player.p_sword;
+
+ /* insure that important things are within limits */
+ Player.p_quksilver = MIN(99.0, Player.p_quksilver);
+ Player.p_mana = MIN(Player.p_mana,
+ Player.p_level * Statptr->c_maxmana + 1000.0);
+ Player.p_brains = MIN(Player.p_brains,
+ Player.p_level * Statptr->c_maxbrains + 200.0);
+ Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0);
+
+ /*
+ * some implementations have problems with floating point compare
+ * we work around it with this stuff
+ */
+ Player.p_gold = floor(Player.p_gold) + 0.1;
+ Player.p_gems = floor(Player.p_gems) + 0.1;
+ Player.p_mana = floor(Player.p_mana) + 0.1;
+
+ if (Player.p_ring.ring_type != R_NONE)
+ /* do ring things */
+ {
+ /* rest to max */
+ Player.p_energy = Player.p_maxenergy + Player.p_shield;
+
+ if (Player.p_ring.ring_duration <= 0)
+ /* clean up expired rings */
+ switch (Player.p_ring.ring_type)
+ {
+ case R_BAD: /* ring drives player crazy */
+ Player.p_ring.ring_type = R_SPOILED;
+ Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0);
+ break;
+
+ case R_NAZREG: /* ring disappears */
+ Player.p_ring.ring_type = R_NONE;
+ break;
+
+ case R_SPOILED: /* ring kills player */
+ death("A cursed ring");
+ break;
+
+ case R_DLREG: /* this ring doesn't expire */
+ Player.p_ring.ring_duration = 0;
+ break;
+ }
+ }
+
+ if (Player.p_age / N_AGE > Player.p_degenerated)
+ /* age player slightly */
+ {
+ ++Player.p_degenerated;
+ if (Player.p_quickness > 23.0)
+ Player.p_quickness *= 0.99;
+ Player.p_strength *= 0.97;
+ Player.p_brains *= 0.95;
+ Player.p_magiclvl *= 0.97;
+ Player.p_maxenergy *= 0.95;
+ Player.p_quksilver *= 0.95;
+ Player.p_sword *= 0.93;
+ Player.p_shield *= 0.93;
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: initplayer()
+/
+/ FUNCTION: initialize a character
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ struct player *playerp - pointer to structure to init
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: floor(), drandom()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Put a bunch of default values in the given structure.
+/
+/************************************************************************/
+
+initplayer(playerp)
+register struct player *playerp;
+{
+ playerp->p_experience =
+ playerp->p_level =
+ playerp->p_strength =
+ playerp->p_sword =
+ playerp->p_might =
+ playerp->p_energy =
+ playerp->p_maxenergy =
+ playerp->p_shield =
+ playerp->p_quickness =
+ playerp->p_quksilver =
+ playerp->p_speed =
+ playerp->p_magiclvl =
+ playerp->p_mana =
+ playerp->p_brains =
+ playerp->p_poison =
+ playerp->p_gems =
+ playerp->p_sin =
+ playerp->p_1scratch =
+ playerp->p_2scratch = 0.0;
+
+ playerp->p_gold = ROLL(50.0, 75.0) + 0.1; /* give some gold */
+
+ playerp->p_x = ROLL(-125.0, 251.0);
+ playerp->p_y = ROLL(-125.0, 251.0); /* give random x, y */
+
+ /* clear ring */
+ playerp->p_ring.ring_type = R_NONE;
+ playerp->p_ring.ring_duration = 0;
+ playerp->p_ring.ring_inuse = FALSE;
+
+ playerp->p_age = 0L;
+
+ playerp->p_degenerated = 1; /* don't degenerate initially */
+
+ playerp->p_type = C_FIGHTER; /* default */
+ playerp->p_specialtype = SC_NONE;
+ playerp->p_lives =
+ playerp->p_crowns =
+ playerp->p_charms =
+ playerp->p_amulets =
+ playerp->p_holywater =
+ playerp->p_lastused = 0;
+ playerp->p_status = S_NOTUSED;
+ playerp->p_tampered = T_OFF;
+ playerp->p_istat = I_OFF;
+
+ playerp->p_palantir =
+ playerp->p_blessing =
+ playerp->p_virgin =
+ playerp->p_blindness = FALSE;
+
+ playerp->p_name[0] =
+ playerp->p_password[0] =
+ playerp->p_login[0] = '\0';
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: readmessage()
+/
+/ FUNCTION: read message from other players
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fseek(), fgets(), wmove(), waddstr(), wclrtoeol()
+/
+/ GLOBAL INPUTS: *stdscr, Databuf[], *Messagefp
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ If there is a message from other players, print it.
+/
+/************************************************************************/
+
+readmessage()
+{
+ move(3, 0);
+ clrtoeol();
+ fseek(Messagefp, 0L, 0);
+ if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL)
+ addstr(Databuf);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: error()
+/
+/ FUNCTION: process evironment error
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ char *whichfile - pointer to name of file which caused error
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: wclear(), cleanup()
+/
+/ GLOBAL INPUTS: errno, *stdscr, printw(), printf(), Windows
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Print message about offending file, and exit.
+/
+/************************************************************************/
+
+error(whichfile)
+ char *whichfile;
+{
+ int (*funcp) __P((const char *, ...));
+
+ if (Windows)
+ {
+ funcp = printw;
+ clear();
+ }
+ else
+ funcp = printf;
+
+ (*funcp)("An unrecoverable error has occurred reading %s. (errno = %d)\n", whichfile, errno);
+ (*funcp)("Please run 'setup' to determine the problem.\n");
+ cleanup(TRUE);
+ /*NOTREACHED*/
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: distance()
+/
+/ FUNCTION: calculate distance between two points
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ double x1, y1 - x, y coordinates of first point
+/ double x2, y2 - x, y coordinates of second point
+/
+/ RETURN VALUE: distance between the two points
+/
+/ MODULES CALLED: sqrt()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ This function is provided because someone's hypot() library function
+/ fails if x1 == x2 && y1 == y2.
+/
+/************************************************************************/
+
+double
+distance(x1, x2, y1, y2)
+double x1, x2, y1, y2;
+{
+double deltax, deltay;
+
+ deltax = x1 - x2;
+ deltay = y1 - y2;
+ return(sqrt(deltax * deltax + deltay * deltay));
+}
+
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: ill_sig()
+/
+/ FUNCTION: exit upon trapping an illegal signal
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ int whichsig - signal which occured to cause jump to here
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: wclear(), printw(), cleanup()
+/
+/ GLOBAL INPUTS: *stdscr
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ When an illegal signal is caught, print a message, and cleanup.
+/
+/************************************************************************/
+
+ill_sig(whichsig)
+int whichsig;
+{
+ clear();
+ if (!(whichsig == SIGINT || whichsig == SIGQUIT))
+ printw("Error: caught signal # %d.\n", whichsig);
+ cleanup(TRUE);
+ /*NOTREACHED*/
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: descrstatus()
+/
+/ FUNCTION: return a string describing the player status
+/
+/ AUTHOR: E. A. Estes, 3/3/86
+/
+/ ARGUMENTS:
+/ struct player playerp - pointer to player structure to describe
+/
+/ RETURN VALUE: string describing player's status
+/
+/ MODULES CALLED: none
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Return verbal description of player status.
+/ If player status is S_PLAYING, check for low energy and blindness.
+/
+/************************************************************************/
+
+char *
+descrstatus(playerp)
+register struct player *playerp;
+{
+ switch (playerp->p_status)
+ {
+ case S_PLAYING:
+ if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield))
+ return("Low Energy");
+ else if (playerp->p_blindness)
+ return("Blind");
+ else
+ return("In game");
+
+ case S_CLOAKED:
+ return("Cloaked");
+
+ case S_INBATTLE:
+ return("In Battle");
+
+ case S_MONSTER:
+ return("Encounter");
+
+ case S_TRADING:
+ return("Trading");
+
+ case S_OFF:
+ return("Off");
+
+ case S_HUNGUP:
+ return("Hung up");
+
+ default:
+ return("");
+ }
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: drandom()
+/
+/ FUNCTION: return a random floating point number from 0.0 < 1.0
+/
+/ AUTHOR: E. A. Estes, 2/7/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: random()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Convert random integer from library routine into a floating
+/ point number, and divide by the largest possible random number.
+/ We mask large integers with 32767 to handle sites that return
+/ 31 bit random integers.
+/
+/************************************************************************/
+
+double
+drandom()
+{
+ if (sizeof(int) != 2)
+ /* use only low bits */
+ return((double) (random() & 0x7fff) / 32768.0);
+ else
+ return((double) random() / 32768.0);
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: collecttaxes()
+/
+/ FUNCTION: collect taxes from current player
+/
+/ AUTHOR: E. A. Estes, 2/7/86
+/
+/ ARGUMENTS:
+/ double gold - amount of gold to tax
+/ double gems - amount of gems to tax
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: fread(), fseek(), fopen(), floor(), fwrite(), fclose()
+/
+/ GLOBAL INPUTS: Player
+/
+/ GLOBAL OUTPUTS: Player
+/
+/ DESCRIPTION:
+/ Pay taxes on gold and gems. If the player does not have enough
+/ gold to pay taxes on the added gems, convert some gems to gold.
+/ Add taxes to tax data base; add remaining gold and gems to
+/ player's cache.
+/
+/************************************************************************/
+
+collecttaxes(gold, gems)
+double gold;
+double gems;
+{
+FILE *fp; /* to update Goldfile */
+double dtemp; /* for temporary calculations */
+double taxes; /* tax liability */
+
+ /* add to cache */
+ Player.p_gold += gold;
+ Player.p_gems += gems;
+
+ /* calculate tax liability */
+ taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold);
+
+ if (Player.p_gold < taxes)
+ /* not enough gold to pay taxes, must convert some gems to gold */
+ {
+ dtemp = floor(taxes / N_GEMVALUE + 1.0); /* number of gems to convert */
+
+ if (Player.p_gems >= dtemp)
+ /* player has enough to convert */
+ {
+ Player.p_gems -= dtemp;
+ Player.p_gold += dtemp * N_GEMVALUE;
+ }
+ else
+ /* take everything; this should never happen */
+ {
+ Player.p_gold += Player.p_gems * N_GEMVALUE;
+ Player.p_gems = 0.0;
+ taxes = Player.p_gold;
+ }
+ }
+
+ Player.p_gold -= taxes;
+
+ if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
+ /* update taxes */
+ {
+ dtemp = 0.0;
+ fread((char *) &dtemp, sizeof(double), 1, fp);
+ dtemp += floor(taxes);
+ fseek(fp, 0L, 0);
+ fwrite((char *) &dtemp, sizeof(double), 1, fp);
+ fclose(fp);
+ }
+}
diff --git a/phantasia/monsters.asc b/phantasia/monsters.asc
new file mode 100644
index 00000000..3d426a9b
--- /dev/null
+++ b/phantasia/monsters.asc
@@ -0,0 +1,100 @@
+A Water Leaper 12 14 16 24 59 0 0 62
+A Leech 4 19 29 30 66 0 0 73
+An Urisk 13 30 15 46 127 1 0 3
+Shellycoat 28 21 18 63 226 2 0 0
+A Naiad 21 62 27 58 378 2 0 11
+A Nixie 22 58 28 108 604 3 0 6
+A Glaistig 21 106 25 127 1002 3 0 0
+A Mermaid 18 116 22 108 809 3 0 0
+A Merman 24 115 23 109 808 4 0 0
+A Siren 22 128 31 89 915 4 0 24
+A Lamprey 14 67 33 156 1562 4 15 37
+A Kopoacinth 26 36 26 206 2006 5 0 20
+A Kelpie 61 25 24 223 4025 5 0 0
+An Aspidchelone 114 104 19 898 10041 7 0 2
+An Idiot 13 14 16 28 49 0 0 0
+Some Green Slime 1 5 45 100 57 0 0 26
+A Pixie 11 29 23 26 64 0 0 32
+A Serpent 10 18 25 25 79 0 0 10
+A Cluricaun 12 27 20 30 81 0 14 5
+An Imp 22 30 14 40 92 0 0 1
+A Centipede 3 8 18 15 33 0 0 61
+A Beetle 2 11 21 26 44 0 0 48
+A Fir Darrig 18 22 17 35 107 0 14 1
+Modnar 15 23 20 40 101 7 2 12
+A Gnome 7 45 26 23 111 0 0 21
+A Sprite 9 37 25 31 132 1 0 43
+A Mimic 11 55 29 47 213 1 3 2
+A Kobold 13 10 14 21 121 1 12 68
+A Spider 6 11 28 28 124 1 0 57
+An Uldra 14 37 21 32 93 1 0 6
+A Gnoll 20 25 15 40 166 1 0 61
+A Bogie 23 28 19 57 189 1 0 57
+A Fachan 9 40 15 45 139 1 14 10
+A Moron 3 1 10 10 28 0 0 100
+An Orc 25 13 16 26 141 1 0 92
+A Ghillie Dhu 12 16 13 28 104 2 14 2
+A Bogle 19 15 16 35 157 2 14 15
+A Shrieker 2 62 27 9 213 2 16 0
+A Carrion Crawler 12 20 20 65 142 2 0 42
+A Trow 15 17 23 51 136 2 0 36
+A Warg 20 10 17 45 152 2 0 88
+A Stirge 2 6 35 25 153 2 0 95
+A Crebain 5 11 31 31 82 2 0 81
+A Killmoulis 30 19 8 75 175 3 14 22
+A Hob-goblin 35 20 15 72 246 3 0 18
+A Unicorn 27 57 27 57 627 3 1 0
+A Fenoderee 16 6 21 65 222 3 0 42
+An Ogre 42 14 16 115 409 3 0 19
+A Dodo 62 12 11 76 563 3 0 3
+A Hydra 14 27 33 99 599 3 0 27
+A Hamadryad 23 47 26 62 426 3 0 12
+A Bwca 21 17 19 55 387 3 14 1
+An Owlbear 35 16 18 100 623 4 0 22
+Black Annis 37 52 15 65 786 4 0 2
+A Jello Blob 100 25 7 264 1257 4 0 13
+A Jubjub Bird 45 23 12 114 1191 4 0 0
+A Wichtlein 13 40 25 61 800 4 0 8
+A Cocodrill 39 28 24 206 1438 4 0 38
+A Troll 75 12 20 185 1013 4 24 29
+A Bonnacon 89 26 9 255 1661 4 17 14
+A Gargoyle 22 21 29 200 1753 5 0 7
+A Chaladrius 8 49 37 172 1929 5 0 20
+A Gwyllion 27 73 20 65 1888 5 0 4
+A Cinomulgus 23 2 10 199 263 5 0 18
+A Peridexion 26 32 24 98 1300 5 0 2
+Smeagol 41 33 27 373 2487 5 18 0
+A Wraith 52 102 22 200 3112 5 25 13
+A Snotgurgle 143 19 26 525 4752 6 0 3
+A Phooka 42 63 21 300 4125 5 0 12
+A Vortex 101 30 31 500 6992 6 9 4
+Shelob 147 64 28 628 5003 7 13 0
+A Thaumaturgist 35 200 23 400 7628 6 7 0
+Smaug 251 76 26 1022 9877 7 0 0
+A Cold-drake 301 102 24 1222 10888 7 0 0
+A Red Dragon 342 141 23 1299 11649 8 0 0
+Scatha the Worm 406 208 20 1790 11999 8 0 0
+Tiamat 506 381 29 2000 13001 9 11 0
+A Bandersnatch 105 98 22 450 7981 6 0 3
+A Harpy 103 49 24 263 7582 6 0 2
+A Tigris 182 38 17 809 7777 6 0 3
+A Gryphon 201 45 19 813 8888 7 0 1
+A Coblynau 205 46 18 585 8333 6 0 2
+A Chimaera 173 109 28 947 12006 7 0 0
+A Jack-in-Irons 222 36 12 1000 9119 7 0 0
+Saruman 55 373 17 1500 17101 11 6 0
+A Balrog 500 100 25 705 8103 7 8 0
+Argus 201 87 14 1500 10010 8 0 0
+A Titan 302 1483 12 1625 11011 8 0 0
+Cacus 256 43 19 1750 12012 8 0 0
+Begion 403 154 10 1875 13013 8 0 0
+Grendel 197 262 23 2000 14014 8 0 0
+A Nazgul 250 251 26 1011 9988 12 10 9
+A Succubus 186 1049 27 2007 19984 9 19 0
+Red Cap 143 50 35 1965 23456 9 0 0
+A Nuckelavee 300 75 20 2185 11111 8 0 0
+Cerberus 236 96 29 2600 25862 9 20 0
+A Jabberwock 185 136 25 2265 23256 9 22 0
+Ungoliant 399 2398 37 2784 27849 10 21 0
+Leanan-Sidhe 486 5432 46 3000 30004 9 5 0
+The Dark Lord 9999 9999 31 19999 30005 13 4 0
diff --git a/phantasia/oldplayer.h b/phantasia/oldplayer.h
new file mode 100644
index 00000000..ce8661c4
--- /dev/null
+++ b/phantasia/oldplayer.h
@@ -0,0 +1,54 @@
+/*
+ * oldplayer.h - old player structure
+ */
+
+struct oldplayer /* player statistics */
+ {
+ char o_name[21]; /* name */
+ char o_password[9]; /* password */
+ char o_login[10]; /* login */
+ double o_x; /* x coord */
+ double o_y; /* y coord */
+ double o_experience; /* experience */
+ int o_level; /* level */
+ short o_quickness; /* quickness */
+ double o_strength; /* strength */
+ double o_sin; /* sin */
+ double o_mana; /* mana */
+ double o_gold; /* gold */
+ double o_energy; /* energy */
+ double o_maxenergy; /* maximum energy */
+ double o_magiclvl; /* magic level */
+ double o_brains; /* brains */
+ short o_crowns; /* crowns */
+ struct
+ {
+ short ring_type; /* type of ring */
+ short ring_duration; /* duration of ring */
+ } o_ring; /* ring stuff */
+ bool o_palantir; /* palantir */
+ double o_poison; /* poison */
+ short o_holywater; /* holy water */
+ short o_amulets; /* amulets */
+ bool o_blessing; /* blessing */
+ short o_charms; /* charms */
+ double o_gems; /* gems */
+ short o_quksilver; /* quicksilver */
+ double o_sword; /* sword */
+ double o_shield; /* shield */
+ short o_type; /* character type */
+ bool o_virgin; /* virgin */
+ short o_lastused; /* day of year last used */
+ short o_status; /* playing, cloaked, etc. */
+ short o_tampered; /* decree'd, etc. flag */
+ double o_1scratch,
+ o_2scratch; /* variables used for decree, player battle */
+ bool o_blindness; /* blindness */
+ int o_notused; /* not used */
+ long o_age; /* age in seconds */
+ short o_degenerated; /* age/2500 last degenerated */
+ short o_istat; /* used for inter-terminal battle */
+#ifdef PHANTPLUS
+ short o_lives;
+#endif
+ };
diff --git a/phantasia/pathnames.h b/phantasia/pathnames.h
new file mode 100644
index 00000000..9e4d46b6
--- /dev/null
+++ b/phantasia/pathnames.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.2 (Berkeley) 4/2/94
+ */
+
+#define _PATH_GAMEPROG "/usr/games/phantasia"
+#define _PATH_GOLD "/var/games/phantasia/gold"
+#define _PATH_LASTDEAD "/var/games/phantasia/lastdead"
+#define _PATH_MESS "/var/games/phantasia/mess"
+#define _PATH_MONST "/var/games/phantasia/monsters"
+#define _PATH_MOTD "/var/games/phantasia/motd"
+#define _PATH_PEOPLE "/var/games/phantasia/characs"
+#define _PATH_SCORE "/var/games/phantasia/scoreboard"
+#define _PATH_VOID "/var/games/phantasia/void"
diff --git a/phantasia/phantasia.6 b/phantasia/phantasia.6
new file mode 100644
index 00000000..a7cb7f1a
--- /dev/null
+++ b/phantasia/phantasia.6
@@ -0,0 +1,1220 @@
+.de sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.TH PHANTASIA 6 "April 19, 1994"
+.UC 4
+.SH NAME
+phantasia \- an interterminal fantasy game
+.SH SYNOPSIS
+phantasia [ \-HSabmpsx ]
+.SH DESCRIPTION
+.I Phantasia
+is a role playing game
+which allows players to roll up characters of various types to fight
+monsters and other players.
+Progression of characters is based upon gaining experience from fighting
+monsters (and other players).
+.PP
+Most of the game is menu driven and self-explanatory (more or less).
+The screen is cursor updated, so be sure to set up the
+.B TERM
+variable in your environment.
+.PP
+The options provide for a variety of functions to support the game.
+They are:
+.PP
+.TP .5i
+.B \-s
+Invokes
+.I phantasia
+without header information.
+.TP .5i
+.B \-m
+Get a monster listing.
+.TP .5i
+.B \-a
+Get a listing of all character names on file.
+.TP .5i
+.B \-x
+Examine/change a particular character on file.
+.TP .5i
+.B \-H
+Print header only.
+.TP .5i
+.B \-p
+Purge old characters.
+.TP .5i
+.B \-b
+Show scoreboard of top characters per login.
+.TP .5i
+.B \-S
+Turn on wizard options, if allowed, if running as ``root''.
+.PP
+The characters are saved on a common file, in order to make the game
+interactive between players. The characters are given a password
+in order to retrieve them later. Only characters above
+.B level
+zero are saved. Characters unused for awhile will be purged.
+Characters are only placed on the scoreboard when they die.
+.SH AUTHOR
+Edward Estes, AT&T Information Systems, Skokie, IL
+.SH PARTICULARS
+.sh "Normal Play"
+A number of the player's more important statistics are almost always
+displayed on the screen, with maximums (where applicable) in
+parentheses.
+.PP
+The character is placed randomly near the center of a cartesian
+system.
+Most commands are selected with a single letter or digit.
+For example, one may move by hitting 'W', 'S', 'N', or 'E',
+(lower case may also be used, at no time is the game case dependent).
+One may also use 'H', 'J', 'K', 'L',
+for movement, similar to
+.IR vi (1).
+To move to a specific (x, y) coordinate, use the
+.B move
+('1') command. The distance a character can move is calculated by
+1 plus 1.5 per
+.B level.
+Moving in a compass direction will move the player the maximum
+allowed distance in that direction.
+.PP
+A player may see who else is playing by using the
+.B players
+('2') option. One may see the coordinates of those who are the same
+distance or closer to the origin as he/she.
+.B Kings,
+and
+.B council of the wise
+can see and can be seen by everyone. A
+.B palantir
+removes these restrictions.
+.PP
+One can talk to other players with the
+.B talk
+('3') option. In general, this is a line or so of text. To remove a current
+message, just type <return> when prompted for a message.
+.PP
+The
+.B stats
+('4') option shows additional characteristics of a player.
+.PP
+One may leave the game either with the
+.B quit
+('5') option.
+.PP
+One may rest by default. Resting lets one regain maximum
+.B energy level,
+and also lets one find
+.B mana
+(more is found for larger levels and further distances from the origin).
+.PP
+One may call a monster by hitting '9' or 'C'.
+.PP
+Use 'X' to examine other players.
+.PP
+One may quit or execute a sub-shell by hitting interrupt.
+Quitting during battle results in death for obvious reasons.
+.PP
+Several other options become available as the player progresses in
+.B level
+and
+.B magic,
+or to other stations in the game (
+.B valar, council of the wise, king
+).
+These are described elsewhere.
+In general, a control-L will force the redrawing of the screen.
+.PP
+Other things which may happen are more or less self-explanatory.
+.sh "Fighting Monsters"
+A player has several options while fighting monsters. They are as follows:
+.TP 1.5i
+.B melee
+Inflicts damage on the monster, based upon
+.B strength.
+Also decreases the monster's
+.B strength
+some.
+.TP 1.5i
+.B skirmish
+Inflicts a little less damage than
+.B melee,
+but decreases the monster's
+.B quickness
+instead.
+.TP 1.5i
+.B evade
+Attempt to run away. Success is based upon both the player's and
+the monster's
+.B brains
+and
+.B quickness.
+.TP 1.5i
+.B spell
+Several options for throwing spells (described elsewhere).
+.TP 1.5i
+.B nick
+Hits the monster one plus the player's
+.B sword,
+and gives the player 10% of the monster's
+.B experience.
+Decreases the monster's
+.B experience
+an amount proportional to the amount granted.
+This also increases the monster's quickness.
+Paralyzed monsters wake up very fast when nicked.
+.TP 1.5i
+.B luckout
+This is essentially a battle of wits with the monster. Success is based
+upon the player's and the monster's
+.B brains.
+The player gets credit for slaying the monster if he/she succeeds.
+Otherwise, nothing happens, and the chance to
+.B luckout
+is lost.
+.sh "Character Statistics"
+.TP 1.5i
+.B strength
+determines how much damage a character can inflict.
+.TP 1.5i
+.B quickness
+determines how many chances a character gets to make decisions while
+fighting.
+.TP 1.5i
+.B energy level
+specifies how much damage a character may endure before dying.
+.TP 1.5i
+.B magic level
+determines which spells a character may throw, and how effective those
+spells will be.
+.TP 1.5i
+.B brains
+basically, the character's intelligence; used for various fighting options
+and spells.
+.TP 1.5i
+.B mana
+used as a power source for throwing spells.
+.TP 1.5i
+.B experience
+gained by fighting monsters and other characters.
+.TP 1.5i
+.B level
+indicative of how much experience a character has accumulated; progresses
+geometrically as
+.B experience
+increases.
+.TP 1.5i
+.B poison
+sickness which degrades a character's performance (affects
+.B energy level
+and
+.B strength
+).
+.TP 1.5i
+.B sin
+accumulated as a character does certain nasty things; used only rarely
+in normal play of the game.
+.TP 1.5i
+.B age
+of player; roughly equivalent to number of turns.
+As
+.B age
+increases, many personal statistics degenerate.
+.sh "Character Types"
+Character statistics are rolled randomly from the above list, according
+to character type. The types are as follows:
+.TP 1.5i
+.B magic user
+strong in
+.B magic level
+and
+.B brains
+, weak in other areas. Must rely on wits and magic to survive.
+.TP 1.5i
+.B fighter
+good in
+.B strength
+and
+.B energy level
+, fairly good in other areas. This adds up to a well-equipped fighter.
+.TP 1.5i
+.B elf
+very high
+.B quickness
+and above average
+.B magic level
+are
+.B elves
+selling points.
+.TP 1.5i
+.B dwarf
+very high
+.B strength
+and
+.B energy level
+, but with a tendency to be rather slow and not too bright.
+.TP 1.5i
+.B halfling
+rather quick and smart, with high
+.B energy level
+, but poor in
+.B magic
+and
+.B strength.
+Born with some
+.B experience.
+.TP 1.5i
+.B experimento
+very mediocre in all areas. However, the
+.B experimento
+may be placed almost anywhere within the playing grid.
+.PP
+The possible ranges for starting statistics are summarized in
+the following table.
+.PP
+.TS
+l c c c c c c
+l c c c c c c.
+Type Strength Quick Mana Energy Brains Magic
+_
+Mag. User 10-15 30-35 50-100 30-45 60-85 5-9
+Fighter 40-55 30-35 30-50 45-70 25-45 3-6
+Elf 35-45 32-38 45-90 30-50 40-65 4-7
+Dwarf 50-70 25-30 25-45 60-100 20-40 2-5
+Halfling 20-25 34 25-45 55-90 40-75 1-4
+Experimento 25 27 100 35 25 2
+.TE
+.PP
+Not only are the starting characteristics different for the different
+character types, the characteristics progress at different rates for the
+different types as the character goes up in
+.B level. Experimentoes'
+characteristics progress randomly as one of the other types.
+The progression as characters increase in
+.B level
+is summarized in the following table.
+.PP
+.TS
+l c c c c c
+l n n n n n.
+Type Strength Mana Energy Brains Magic
+_
+Mag. User 2.0 75 20 6 2.75
+Fighter 3.0 40 30 3.0 1.5
+Elf 2.5 65 25 4.0 2.0
+Dwarf 5 30 35 2.5 1
+Halfling 2.0 30 30 4.5 1
+.TE
+.PP
+The character type also determines how much gold a player may
+carry, how long until
+.B rings
+can overcome the player, and how much
+.B poison
+the player can withstand.
+.sh "Spells"
+During the course of the game, the player may exercise his/her
+magic powers. These cases are described below.
+.TP 1.5i
+.B cloak
+.I magic level necessary:
+20 (plus level 7)
+.br
+.I mana used:
+35 plus 3 per rest period
+.br
+Used during normal play. Prevents monsters from finding the character,
+as well as hiding the player from other players. His/her coordinates
+show up as '?' in the
+.B players
+option. Players cannot collect
+.B mana,
+find trading posts, or discover the
+.B grail
+while cloaked. Calling a monster uncloaks, as well as choosing
+this option while cloaked.
+.br
+.TP 1.5i
+.B teleport
+.I magic level necessary:
+40 (plus level 12)
+.br
+.I mana used:
+30 per 75 moved
+.br
+Used during normal play. Allows the player too move with much more freedom
+than with the
+.B move
+option, at the price of expending mana. The maximum distance possible
+to move is based upon
+.B level
+and
+.B magic level.
+.TP 1.5i
+.B power blast
+.I magic level necessary:
+none
+.br
+.I mana used:
+5 times
+.B level
+.br
+Used during inter-terminal battle. Damage is based upon
+.B magic level
+and
+.B strength.
+Hits much harder than a normal hit.
+.TP 1.5i
+.B all or nothing
+.I magic level necessary:
+none
+.br
+.I mana used:
+1
+.br
+Used while combating monsters.
+Has a 25% chance of working. If it works it hits the monster just enough
+to kill it. If it fails, it doesn't hit the monster, and doubles the
+monster's
+.B quickness
+and
+.B strength.
+Paralyzed monsters wake up much quicker as a result of this spell.
+.TP 1.5i
+.B magic bolt
+.I magic level necessary:
+5
+.br
+.I mana used:
+variable
+.br
+Used while combating monsters. Hits the monster based upon the amount
+of
+.B mana
+expended and
+.B magic level.
+Guaranteed to hit at least 10 per
+.B mana.
+.TP 1.5i
+.B force field
+.I magic level necessary:
+15
+.br
+.I mana used:
+30
+.br
+Used during monster combat. Throws up a shield to protect from damage.
+The shield is added to actual energy level, and is a fixed number, based
+upon maximum energy. Normally, damage occurs first to the shield, and
+then to the players actual
+.B energy level.
+.TP 1.5i
+.B transform
+.I magic level necessary:
+25
+.br
+.I mana used:
+50
+.br
+Used during monster combat. Transforms the monster randomly into one
+of the 100 monsters from the monster file.
+.TP 1.5i
+.B increase might
+.I magic level necessary:
+35
+.br
+.I mana used:
+75
+.br
+Used during combat with monsters. Increases strength up to a maximum.
+.TP 1.5i
+.B invisibility
+.I magic level necessary:
+45
+.br
+.I mana used:
+90
+.br
+Used while fighting monsters. Makes it harder for the monster to hit,
+by temporarily increasing the player's
+.B quickness.
+This spell may be thrown several times, but a maximum level will be reached.
+.TP 1.5i
+.B transport
+.I magic level necessary:
+60
+.br
+.I mana used:
+125
+.br
+Used during monster combat. Transports the monster away from the
+player. Success is base upon player's
+.B magic
+and
+.B brains,
+and the monster's
+.B experience.
+If it fails the player is transported instead. 60% of the time, the monster
+will drop any treasure it was carrying.
+.TP 1.5i
+.B paralyze
+.I magic level necessary:
+75
+.br
+.I mana used:
+150
+.br
+Used during monster combat. "Freezes" the monster by putting its
+.B quickness
+slightly negative. The monster will slowly wake up. Success is based
+upon player's
+.B magic
+and the monster's
+.B experience.
+If it fails, nothing happens.
+.TP 1.5i
+.B specify
+.I magic level necessary:
+none
+.br
+.I mana used:
+1000
+.br
+Used during monster combat only by
+.B valar
+or
+.B council of the wise.
+Allows the player to pick which monster to fight.
+.sh "Monsters"
+Monsters get bigger as one moves farther from the origin (0,0). Rings of
+distance 125 from the origin determine the size. A monster's
+.B experience, energy level,
+and
+.B brains
+are multiplied by the size.
+.B Strength
+is increase 50% per size over one, and
+.B quickness
+remains the same, regardless of size.
+.PP
+Also, nastier monsters are found as one progress farther out
+from the origin. Monsters also may flock. The percent chance of that
+happening is designated as
+.B flock%
+in the monster listing. Monsters outside the first ring
+may carry treasure, as determined by their treasure type.
+Flocking monsters, and bigger monsters increase the chances of treasure.
+.PP
+Certain monsters have special abilities; they are as follows:
+.TP 1.5i
+.B Unicorn
+can only be subdued if the player is in possession of a
+.B virgin.
+.TP 1.5i
+.B Modnar
+has random characteristics, including treasure type.
+.TP 1.5i
+.B Mimic
+will pick another name from the list of monsters in order to
+confuse.
+.TP 1.5i
+.B Dark Lord
+very nasty person. Does not like to be hit (especially nicked),
+and many spells do not work well (or at all) against him.
+One can always
+.B evade
+from the
+.B Dark Lord.
+.TP 1.5i
+.B Leanan-Sidhe
+also a very nasty person. She will permanently sap
+.B strength
+from someone.
+.TP 1.5i
+.B Saruman
+wanders around with
+.B Wormtongue
+, who can steal a
+.B palantir.
+Also,
+.B Saruman
+may turn a player's gems into gold pieces,
+or scramble her/his stats.
+.TP 1.5i
+.B Thaumaturgist
+can transport a player.
+.TP 1.5i
+.B Balrog
+inflicts damage by taking away
+.B experience
+, not
+.B energy.
+.TP 1.5i
+.B Vortex
+may take some
+.B mana.
+.TP 1.5i
+.B Nazgul
+may try to steal a
+.B ring
+or neutralize part of one's
+.B brains.
+.TP 1.5i
+.B Tiamat
+may take half a player's
+.B gold
+and
+.B gems
+and escape.
+.TP 1.5i
+.B Kobold
+may get nasty and steal one gold piece and run away.
+.TP 1.5i
+.B Shelob
+may bite, inflicting the equivalent of one
+.B poison.
+.TP 1.5i
+.B Assorted Faeries
+These are killed if attacking someone carrying
+.B holy water.
+These are
+.B Cluricaun, Fir Darrig, Fachan,
+.B Ghille Dhu, Bogle, Killmoulis,
+and
+.B Bwca.
+.TP 1.5i
+.B Lamprey
+may bite, inflicting 1/2 of a
+.B poison.
+.TP 1.5i
+.B Shrieker
+will call one of its (much bigger) buddies if picked upon.
+.TP 1.5i
+.B Bonnacon
+will become bored with battle, fart, and run off.
+.TP 1.5i
+.B Smeagol
+will try to steal a
+.B ring
+from a player, if given the chance.
+.TP 1.5i
+.B Succubus
+may inflict damage through a
+.B force field.
+This subtracts from
+.B energy level
+instead of any shield the player may have thrown up.
+This is a very easy way to die.
+.TP 1.5i
+.B Cerberus
+loves metal and will steal all the metal treasures from
+a player if able.
+.TP 1.5i
+.B Ungoliant
+can bite and poison. This inflicts five
+.B poisons
+, and also takes one from the player's
+.B quickness.
+.TP 1.5i
+.B Jabberwock
+may tire of battle, and leave after calling one of his friends
+(
+.B Jubjub Bird
+or
+.B Bandersnatch
+).
+.TP 1.5i
+.B Morgoth
+actually
+.B Modnar
+, but reserved for
+.B council of the wise, valar,
+and
+.B ex-valar.
+Fights with
+.B Morgoth
+end when either he or the player dies. His characteristics
+are calculated based upon the player's. The player is given
+the chance to ally with him. No magic, except
+.B force field
+works when battling
+.B Morgoth.
+.TP 1.5i
+.B Troll
+may regenerate its
+.B energy
+and
+.B strength
+while in battle.
+.TP 1.5i
+.B Wraith
+may make a player blind.
+.sh "Treasures"
+The various treasure types are as follows:
+.TP 1.5i
+.B Type zero
+.I none
+.TP 1.5i
+.B Type one
+.I power booster
+\- adds mana.
+.br
+.I druid
+\- adds experience.
+.br
+.I holy orb
+\- subtracts 0.25 sin.
+.TP 1.5i
+.B Type two
+.I amulet
+\- protects from cursed treasure.
+.br
+.I holy water
+\- kills
+.B assorted faeries.
+.br
+.I hermit
+\- reduces sin by 25% and adds some mana.
+.TP 1.5i
+.B Type three
+.I shield
+\- adds to maximum
+.B energy level
+.br
+.I virgin
+\- used to subdue a
+.B unicorn
+, or to give much
+.B experience
+(and some
+.B sin
+).
+.br
+.I athelas
+\- subtracts one
+.B poison.
+.TP 1.5i
+.B Type four (scrolls)
+.I shield
+\- throws a bigger than normal
+.B force field.
+.br
+.I invisible
+\- temporarily puts the finder's
+.B quickness
+to one million.
+.br
+.I ten fold strength
+\- multiplies finder's strength by ten.
+.br
+.I pick monster
+\- allows finder to pick next monster to battle.
+.br
+.I general knowledge
+\- adds to finder's
+.B brains
+and
+.B magic level.
+.PP
+All the scrolls except
+.B general knowledge
+automatically call a monster. These preserve any
+spells that were already in effect, but are only in
+effect while in battle.
+.TP 1.5i
+.B Type five
+.I dagger
+\- adds to
+.B strength.
+.br
+.I armour
+\- same as a
+.B shield,
+but bigger.
+.br
+.I tablet
+\- adds brains.
+.TP 1.5i
+.B Type six
+.I priest
+\- rests to maximum; adds
+.B mana, brains;
+and halves
+.B sin.
+.br
+.I Robin Hood
+\- increases
+.B shield
+and adds permanently to
+.B strength.
+.br
+.I axe
+\- like
+.B dagger,
+but bigger.
+.TP 1.5i
+.B Type seven
+.I charm
+\- protects from cursed treasure (used before
+.B amulet
+); used in conjunction with
+.B blessing
+to battle
+.B Dark Lord.
+.br
+.I Merlyn
+\- adds
+.B brains, magic,
+and
+.B mana.
+.br
+.I war hammer
+\- like an
+.B axe,
+but bigger.
+.TP 1.5i
+.B Type eight
+.I healing potion
+\- sets
+.B poison
+to -2, or subtracts two from
+.B poison,
+whichever is better.
+.br
+.I transporter
+\- allows finder to move anywhere.
+.br
+.I sword
+\- like a
+.B war hammer
+, but bigger.
+.TP 1.5i
+.B Type nine
+.I golden crown
+\- allows the player to become
+.B king,
+by going to (0,0).
+.br
+.I blessing
+\- cuts
+.B sin
+to 1/3, adds
+.B mana,
+rests to max., kills
+.B Dark Lord
+with a
+.B charm,
+and gives bearer first hit on all monsters.
+.br
+.I quicksilver
+\- adds to
+.B quickness.
+.TP 1.5i
+.B Type ten
+.I elven boots
+\- adds permanently to
+.B quickness.
+.TP 1.5i
+.B Type eleven
+.I palantir
+\- allows one to see all the other players; used by
+.B council of the wise
+to seek the
+.B grail.
+.TP 1.5i
+.B Type twelve/thirteen
+.I ring
+\- allows one to hit much harder in battle, etc.
+.PP
+Any treasure type 10-13 monsters may instead carry a type nine treasure.
+.PP
+A monster may also be carrying
+.B gold
+or
+.B gems.
+These are used at
+.B trading posts
+to buy things. A
+.B gem
+is worth 1000 gold pieces. Too much
+.B gold
+will slow a player down. One may carry 1000 plus 200 per
+.B level
+of
+.B gold.
+A
+.B gem
+weighs one half a gold piece.
+Monsters of treasure type 7 or higher may carry
+.B gems.
+.PP
+The chance of a cursed treasure is based upon treasure type.
+The more valuable treasures have a greater chance of being cursed.
+A cursed treasure knocks
+.B energy level
+very low, and adds 0.25
+.B poison.
+.sh "Rings"
+.B Rings
+are only carried by
+.B nazguls
+and
+.B Dark Lord.
+They come in four different flavors.
+All
+.B rings
+rest the player to maximum and cause him/her to hit much harder
+in battle with monsters (assuming one has chosen to use the
+.B ring
+for battle.)
+.PP
+Two types of
+.B rings
+are cursed and come either from
+.B nazguls
+or
+.B Dark Lord.
+After a few times of using these types, the player falls
+under the control of the
+.B ring,
+and strange, random things will occur.
+Eventually, the player dies, and gives his/her name to a monster
+on the file.
+Dying before the
+.B ring
+is used up also renames the monster.
+.PP
+The two remaining types of
+.B rings
+are much more benign.
+The one from a
+.B nazgul
+is good for a limited number of battle rounds, and will save
+the player from death if it was being used when he/she died.
+The one from
+.B Dark Lord
+is the same, except that it never is used up.
+.B rings
+disappear after saving someone from death.
+In general, cursed
+.B rings
+occur much more often than normal ones.
+It is usually not a good idea to pick one up.
+The only way to get rid of a
+.B ring
+is to have a monster steal it.
+.sh "King"
+A player may become
+.B king by finding a
+.I crown
+and going to (0,0). Players must have a
+.B level
+in the range of 10 to 1000 to be able to find a
+.I crown.
+When a player with one or more
+.I crowns
+reaches
+.B level
+1000, the
+.I crowns
+are converted to
+.I gold.
+.PP
+Once a player is king, he/she may do certain things while in
+the Lord's Chamber (0,0). These are exercised with the
+.B decree
+('0') option.
+.TP 1.5i
+.I transport
+This is done to another player. It randomly moves the affected
+player about. A
+.B charm
+protects from transports.
+.TP 1.5i
+.I curse
+This is done to another player. It is analogous to cursed treasure,
+but worse. It inflicts two
+.B poison,
+knocks
+.B energy level
+very low, and degrades the maximum energy. It also
+removes a
+.B cloak.
+A
+.B blessing
+protects from king's curses.
+.TP 1.5i
+.I energy void
+The king may put a number of these scattered about
+his/her kingdom as he/she pleases.
+If a player hits one, he/she loses
+.B mana, energy,
+and
+.B gold.
+The energy void disappears after being hit.
+.TP 1.5i
+.I bestow
+This is also done to another player. The king may
+wish to reward one or more loyal subjects by sharing his/her
+riches (
+.B gold
+). Or it is a convenient way to dispose of some unwanted
+deadweight.
+.TP 1.5i
+.I collect taxes
+Everyone pays 7% tax on all
+.B gold
+and
+.B gems
+acquired, regardless of the existence of a
+.B king.
+The king collects the accrued taxes with this option.
+.PP
+The
+.B king
+may also
+.B teleport
+anywhere for free by using the origin as a starting place.
+.sh "Council of the Wise, Valar"
+A player automatically becomes a member of the
+.B council of the wise
+upon reaching level 3000. Members of the council cannot have
+.B rings.
+Members of the council have a few extra options which they can exercise.
+These are exercised
+.B intervene
+('8') option.
+All
+.B intervene
+options cost 1000 mana.
+One
+.B intervene
+option is to
+.I heal
+another player. This is just a quick way for that player to be rested
+to maximum and lose a little
+.B poison.
+The main purpose in life for members of the council is to seek the
+.B Holy Grail.
+This is done with a
+.B palantir
+under the
+.I seek grail
+option. The distance cited by the seek is accurate within 10%, in order
+not to make it too easy to find the grail.
+A player must have infinitesimally small
+.B sin,
+or else it's all over upon finding the grail.
+In order to help members of the council on their quest, they
+may
+.I teleport
+with greater ease.
+.PP
+Upon finding the grail, the player advances to position of
+.B valar.
+He/she may then exercise more and niftier options under
+.I intervention.
+These include all of the council members' options plus the
+ability to move other players about, bless them, and throw monsters at
+them.
+A
+.BR valar 's
+blessing has the same effect as the treasure
+.I blessing,
+except that the affected player does not get his/her
+.I blessing
+flag set.
+All
+.I intervention
+options which affect other players age the player
+who uses them.
+.B Valars
+are essentially immortal, but are actually given five lives.
+If these are used up, the player is left to die, and becomes an
+.B ex-valar.
+A
+.B valar
+cannot
+.I move, teleport,
+or call monsters.
+(An exception to this is if the
+.I valar
+finds a
+.I transporter.
+This is to allow him/her to dispose of excess
+.I gold.
+Any monsters which a
+.B valar
+encounters are based upon his/her size.
+Only one valar may exist at a time.
+The current valar is replaced when another player finds the grail.
+The valar is then bumped back to the council of the wise.
+.sh "Wizard"
+The
+.I wizard
+is usually the owner of the game, and the one who maintains
+the associated files.
+The
+.I wizard
+is granted special powers within the game, if it is invoked
+with the '\-S' option.
+Otherwise, the
+.I wizard
+plays no different from other players.
+The
+.I wizard
+abilities are outlined below.
+.TP
+.I change players
+When examining a player, (game invoked with '-x', or use 'X' from within game),
+the
+.I wizard
+may also change the player.
+.TP
+.I intervention
+The
+.I wizard
+may do all the
+.I intervention
+options. One extra option,
+.I vaporize,
+is added to kill any offensive players.
+.TP
+.I super character type
+An extra character type is added. This character starts with the
+maximum possible in all statistics, selected from the other character types.
+A
+.B super
+character's statistics also progress at the maximum possible rate, selected
+from the other character types.
+.sh "Special Places"
+Certain regions of the playing grid have different names.
+In general, this is only to give the player some idea of
+his/her present location. Some special places do exist.
+.TP 1.5i
+.I Trading Posts
+These are located at |x| == |y| == n*n*100 for n = 1, 2...1000.
+Trading posts farther out have more things for sale.
+Be careful about cheating the merchants there, as they have short
+tempers.
+Merchants are dishonest about 5% of the time.
+.TP 1.5i
+.I Lord's Chamber
+This is located at (0,0). Only players with
+.B crowns
+may enter.
+.TP 1.5i
+.I Point of No Return
+This is located beyond 1.2e+6 in any direction.
+The only way to return from here is a
+.B transporter
+or to have a
+.B valar
+relocate the player.
+.TP 1.5i
+.I Dead Marshes
+This is a band located fairly distant from the origin. The first
+fourteen monsters (water monsters) can normally only be found here.
+.TP 1.5i
+.I Valhala
+This place is where the
+.B valar
+resides. It is associated with no particular coordinate on the
+playing grid.
+.TP 1.5i
+.sh "Miscellaneous"
+Once a player reaches
+.B level
+5, the game will start to time out waiting for input.
+This is to try to keep the game a bit faster paced.
+.PP
+A
+.I guru
+will never be disgusted with your
+.B sins
+if they are less than one.
+.PP
+A
+.I medic
+wants half of a player's
+.B gold
+to be happy. Offering more than one has, or a negative amount
+will anger the
+.I medic,
+who will make the player worse (add one
+.B poison
+).
+.PP
+The
+.B Holy Grail
+does little for those who are not ready to behold it.
+Whenever anyone finds it, it moves.
+It is always located within 1e+6 in any compass direction of the origin.
+.PP
+There is a maximum amount of
+.B mana
+and
+.B charms
+a player may posses, based upon
+.B level.
+.I Quicksilver
+is always limited to to a maximum of 99.
+.PP
+.I Books
+bought at a
+.B trading post
+increase
+.B brains,
+based upon the number bought.
+It is unwise, however to buy more than 1/10 of one's
+.B level
+in books at a time.
+.PP
+Players over level 10000 are automatically retired.
+.PP
+A
+.I blindness
+goes away in random time.
+.PP
+Players with
+.I crowns
+are identified with a '*' before their character type.
+.sh "Inter-terminal Battle"
+When two player's coordinates correspond, they may engage in battle.
+In general, the player with the highest
+.B quickness
+gets the first hit.
+If the two players are severely mis-matched, the stronger player
+is drastically handicapped for the battle.
+In order to protect from being stuck in an infinite loop,
+the player waiting for response may time out. Options for battle are:
+.TP 1.5i
+.I fight
+Inflicts damage upon other person.
+.TP 1.5i
+.I run away
+Escape from battle. Has a 75% chance of working.
+.TP 1.5i
+.I power blast
+Battle spell.
+.TP 1.5i
+.I luckout
+One-time chance to try to win against the foe. Has a 10% chance of working.
+.PP
+Sometimes waits for the other player may be excessive, because
+he/she may be battling a monster. Upon slaying a player in battle
+the winner gets the other's
+.B experience
+and treasures.
+.B Rings
+do not work for inter-terminal battle.
+.SH BUGS
+All screen formats assume at least 24 lines by at least 80 columns.
+No provisions are made for when any of the data items get too big
+for the allotted space on the screen.
diff --git a/phantasia/phantdefs.h b/phantasia/phantdefs.h
new file mode 100644
index 00000000..9e597e51
--- /dev/null
+++ b/phantasia/phantdefs.h
@@ -0,0 +1,139 @@
+/*
+ * phantdefs.h - important constants for Phantasia
+ */
+
+/* ring constants */
+#define R_NONE 0 /* no ring */
+#define R_NAZREG 1 /* regular Nazgul ring (expires) */
+#define R_DLREG 2 /* regular Dark Lord ring (does not expire) */
+#define R_BAD 3 /* bad ring */
+#define R_SPOILED 4 /* ring which has gone bad */
+
+/* status constants */
+#define S_NOTUSED 0 /* record not in use */
+#define S_OFF 1 /* not playing */
+#define S_PLAYING 2 /* playing - nothing else */
+#define S_CLOAKED 3 /* playing - cloaked */
+#define S_INBATTLE 4 /* playing - in battle */
+#define S_MONSTER 5 /* playing - fighting monster */
+#define S_TRADING 6 /* playing - at a trading post */
+#define S_HUNGUP 7 /* error occured with character */
+
+/* tampered constants */
+#define T_OFF 0 /* nothing */
+#define T_NRGVOID 1 /* hit an energy void */
+#define T_GRAIL 2 /* landed on the holy grail */
+#define T_TRANSPORT 3 /* transported by king */
+#define T_BESTOW 4 /* gold bestowed by king */
+#define T_CURSED 5 /* cursed by king */
+#define T_MONSTER 6 /* monster lobbed by valar */
+#define T_BLESSED 7 /* blessed by valar */
+#define T_RELOCATE 8 /* moved by valar */
+#define T_HEAL 9 /* healed by valar */
+#define T_VAPORIZED 10 /* vaporized by wizard */
+#define T_EXVALAR 11 /* no longer valar */
+
+/* inter-terminal battle status constants */
+#define I_OFF 0 /* nothing */
+#define I_RAN 1 /* ran away */
+#define I_STUCK 2 /* tried to run unsuccessfully */
+#define I_BLEWIT 3 /* tried to luckout unsuccessfully */
+#define I_KILLED 4 /* killed foe */
+
+/* constants for altering coordinates */
+#define A_SPECIFIC 0 /* coordinates specified */
+#define A_FORCED 1 /* coordinates specified, ignore Beyond */
+#define A_NEAR 2 /* coordinates not specified, move near */
+#define A_FAR 3 /* coordinates not specified, move far */
+
+/* constants for character types */
+#define C_MAGIC 0 /* magic user */
+#define C_FIGHTER 1 /* fighter */
+#define C_ELF 2 /* elf */
+#define C_DWARF 3 /* dwarf */
+#define C_HALFLING 4 /* halfling */
+#define C_EXPER 5 /* experimento */
+#define C_SUPER 6 /* super being */
+
+/* constants for special character types */
+#define SC_NONE 0 /* not a special character */
+#define SC_KING 1 /* king */
+#define SC_COUNCIL 2 /* council of the wise */
+#define SC_VALAR 3 /* valar */
+#define SC_EXVALAR 4 /* ex-valar */
+
+/* special monster constants */
+#define SM_NONE 0 /* nothing special */
+#define SM_UNICORN 1 /* unicorn */
+#define SM_MODNAR 2 /* Modnar */
+#define SM_MIMIC 3 /* mimic */
+#define SM_DARKLORD 4 /* Dark Lord */
+#define SM_LEANAN 5 /* Leanan-Sidhe */
+#define SM_SARUMAN 6 /* Saruman */
+#define SM_THAUMATURG 7 /* thaumaturgist */
+#define SM_BALROG 8 /* balrog */
+#define SM_VORTEX 9 /* vortex */
+#define SM_NAZGUL 10 /* nazgul */
+#define SM_TIAMAT 11 /* Tiamat */
+#define SM_KOBOLD 12 /* kobold */
+#define SM_SHELOB 13 /* Shelob */
+#define SM_FAERIES 14 /* assorted faeries */
+#define SM_LAMPREY 15 /* lamprey */
+#define SM_SHRIEKER 16 /* shrieker */
+#define SM_BONNACON 17 /* bonnacon */
+#define SM_SMEAGOL 18 /* Smeagol */
+#define SM_SUCCUBUS 19 /* succubus */
+#define SM_CERBERUS 20 /* Cerberus */
+#define SM_UNGOLIANT 21 /* Ungoliant */
+#define SM_JABBERWOCK 22 /* jabberwock */
+#define SM_MORGOTH 23 /* Morgoth */
+#define SM_TROLL 24 /* troll */
+#define SM_WRAITH 25 /* wraith */
+
+/* constants for spells */
+#define ML_ALLORNOTHING 0.0 /* magic level for 'all or nothing' */
+#define MM_ALLORNOTHING 1.0 /* mana used for 'all or nothing' */
+#define ML_MAGICBOLT 5.0 /* magic level for 'magic bolt' */
+#define ML_FORCEFIELD 15.0 /* magic level for 'force field' */
+#define MM_FORCEFIELD 30.0 /* mana used for 'force field' */
+#define ML_XFORM 25.0 /* magic level for 'transform' */
+#define MM_XFORM 50.0 /* mana used for 'transform' */
+#define ML_INCRMIGHT 35.0 /* magic level for 'increase might' */
+#define MM_INCRMIGHT 75.0 /* mana used for 'increase might' */
+#define ML_INVISIBLE 45.0 /* magic level for 'invisibility' */
+#define MM_INVISIBLE 90.0 /* mana used for 'invisibility' */
+#define ML_XPORT 60.0 /* magic level for 'transport' */
+#define MM_XPORT 125.0 /* mana used for 'transport' */
+#define ML_PARALYZE 75.0 /* magic level for 'paralyze' */
+#define MM_PARALYZE 150.0 /* mana used for 'paralyze' */
+#define MM_SPECIFY 1000.0 /* mana used for 'specify' */
+#define ML_CLOAK 20.0 /* magic level for 'cloak' */
+#define MEL_CLOAK 7.0 /* experience level for 'cloak' */
+#define MM_CLOAK 35.0 /* mana used for 'cloak' */
+#define ML_TELEPORT 40.0 /* magic level for 'teleport' */
+#define MEL_TELEPORT 12.0 /* experience level for 'teleport' */
+#define MM_INTERVENE 1000.0 /* mana used to 'intervene' */
+
+/* some miscellaneous constants */
+#define SZ_DATABUF 100 /* size of input buffer */
+#define SZ_PLAYERSTRUCT sizeof(struct player) /* size of player structure */
+#define SZ_VOIDSTRUCT sizeof(struct energyvoid) /* size of energy void struct */
+#define SZ_SCORESTRUCT sizeof(struct scoreboard) /* size of score board entry */
+#define SZ_MONSTERSTRUCT sizeof(struct monster) /* size of monster structure */
+#define SZ_NAME 21 /* size of player name (incl. trailing nul) */
+#define SZ_PASSWORD 9 /* size of password (incl. trailing nul) */
+#define SZ_LOGIN 9 /* size of login (incl. trailing nul) */
+
+#define N_DAYSOLD 21 /* number of days old for purge */
+#define N_AGE 500 /* age to degenerate ratio */
+#define N_GEMVALUE (1000.0) /* number of gold pieces to gem ratio */
+#define N_TAXAMOUNT (7.0) /* tax percent */
+
+#define D_BEYOND (1.1e6) /* distance to beyond point of no return */
+#define D_EXPER (2000.0) /* distance experimentos are allowed */
+
+#define CH_MARKDELETE '\001' /* used to alter name of deleted players */
+#define CH_KILL '\030' /* kill character (ctrl-X) */
+#define CH_ERASE '\010' /* erase character (ctrl-H) */
+#define CH_NEWLINE '\n' /* newline */
+#define CH_REDRAW '\014' /* redraw screen character (ctrl-L) */
diff --git a/phantasia/phantglobs.c b/phantasia/phantglobs.c
new file mode 100644
index 00000000..65ab6306
--- /dev/null
+++ b/phantasia/phantglobs.c
@@ -0,0 +1,113 @@
+/*
+ * phantglobs.c - globals for Phantasia
+ */
+
+#include "include.h"
+
+double Circle; /* which circle player is in */
+double Shield; /* force field thrown up in monster battle */
+
+bool Beyond; /* set if player is beyond point of no return */
+bool Marsh; /* set if player is in dead marshes */
+bool Throne; /* set if player is on throne */
+bool Changed; /* set if important player stats have changed */
+bool Wizard; /* set if player is the 'wizard' of the game */
+bool Timeout; /* set if short timeout waiting for input */
+bool Windows; /* set if we are set up for curses stuff */
+bool Luckout; /* set if we have tried to luck out in fight */
+bool Foestrikes; /* set if foe gets a chance to hit in battleplayer() */
+bool Echo; /* set if echo input to terminal */
+
+int Users; /* number of users currently playing */
+int Whichmonster; /* which monster we are fighting */
+int Lines; /* line on screen counter for fight routines */
+
+jmp_buf Fightenv; /* used to jump into fight routine */
+jmp_buf Timeoenv; /* used for timing out waiting for input */
+
+long Fileloc; /* location in file of player statistics */
+
+char *Login; /* pointer to login of player */
+char *Enemyname; /* pointer name of monster/player we are battling*/
+
+struct player Player; /* stats for player */
+struct player Other; /* stats for another player */
+
+struct monster Curmonster;/* stats for current monster */
+
+struct energyvoid Enrgyvoid;/* energy void buffer */
+
+struct charstats *Statptr;/* pointer into Stattable[] */
+
+/* lookup table for character type dependent statistics */
+struct charstats Stattable[7] =
+ {
+ /* MAGIC USER */
+ /* max brains, max mana, weakness, gold tote, ring duration */
+ 15.0, 200.0, 18.0, 175.0, 10,
+ /* quickness strength mana energy brains magic lvl */
+ 30, 6, 0.0, 10, 6, 2.0, 50,51,75.0, 30,16,20.0, 60,26, 6.0, 5, 5,2.75,
+
+ /* FIGHTER */
+ /* max brains, max mana, weakness, gold tote, ring duration */
+ 10.0, 110.0, 15.0, 220.0, 20,
+ /* quickness strength mana energy brains magic lvl */
+ 30, 6, 0.0, 40,16, 3.0, 30,21,40.0, 45,26,30.0, 25,21, 3.0, 3, 4, 1.5,
+
+ /* ELF */
+ /* max brains, max mana, weakness, gold tote, ring duration */
+ 12.0, 150.0, 17.0, 190.0, 13,
+ /* quickness strength mana energy brains magic lvl */
+ 32, 7, 0.0, 35,11, 2.5, 45,46,65.0, 30,21,25.0, 40,26, 4.0, 4, 4, 2.0,
+
+ /* DWARF */
+ /* max brains, max mana, weakness, gold tote, ring duration */
+ 7.0, 80.0, 13.0, 255.0, 25,
+ /* quickness strength mana energy brains magic lvl */
+ 25, 6, 0.0, 50,21, 5.0, 25,21,30.0, 60,41,35.0, 20,21, 2.5, 2, 4, 1.0,
+
+ /* HALFLING */
+ /* max brains, max mana, weakness, gold tote, ring duration */
+ 11.0, 80.0, 10.0, 125.0, 40,
+ /* quickness strength mana energy brains magic lvl */
+ 34, 0, 0.0, 20, 6, 2.0, 25,21,30.0, 55,36,30.0, 40,36, 4.5, 1, 4, 1.0,
+
+ /* EXPERIMENTO */
+ /* max brains, max mana, weakness, gold tote, ring duration */
+ 9.0, 90.0, 16.0, 160.0, 20,
+ /* quickness strength mana energy brains magic lvl */
+ 27, 0, 0.0, 25, 0, 0.0, 100,0, 0.0, 35, 0, 0.0, 25, 0, 0.0, 2, 0, 0.0,
+
+ /* SUPER */
+ /* max brains, max mana, weakness, gold tote, ring duration */
+ 15.0, 200.0, 10.0, 225.0, 40,
+ /* quickness strength mana energy brains magic lvl */
+ 38, 0, 0.0, 65, 0, 5.0, 100,0,75.0, 80, 0,35.0, 85, 0, 6.0, 9, 0,2.75
+ };
+
+/* menu of items for purchase */
+struct menuitem Menu[] =
+ {
+ "Mana", 1,
+ "Shield", 5,
+ "Book", 200,
+ "Sword", 500,
+ "Charm", 1000,
+ "Quicksilver", 2500,
+ "Blessing", 1000,
+ };
+
+FILE *Playersfp; /* pointer to open player file */
+FILE *Monstfp; /* pointer to open monster file */
+FILE *Messagefp; /* pointer to open message file */
+FILE *Energyvoidfp; /* pointer to open energy void file */
+
+char Databuf[SZ_DATABUF]; /* a place to read data into */
+
+/* some canned strings for messages */
+char Illcmd[] = "Illegal command.\n";
+char Illmove[] = "Too far.\n";
+char Illspell[] = "Illegal spell.\n";
+char Nomana[] = "Not enought mana for that spell.\n";
+char Somebetter[] = "But you already have something better.\n";
+char Nobetter[] = "That's no better than what you already have.\n";
diff --git a/phantasia/phantglobs.h b/phantasia/phantglobs.h
new file mode 100644
index 00000000..4a10cc99
--- /dev/null
+++ b/phantasia/phantglobs.h
@@ -0,0 +1,86 @@
+/*
+ * phantglobs.h - global declarations for Phantasia
+ */
+
+extern double Circle; /* which circle player is in */
+extern double Shield; /* force field thrown up in monster battle */
+
+extern bool Beyond; /* set if player is beyond point of no return */
+extern bool Marsh; /* set if player is in dead marshes */
+extern bool Throne; /* set if player is on throne */
+extern bool Changed; /* set if important player stats have changed */
+extern bool Wizard; /* set if player is the 'wizard' of the game */
+extern bool Timeout; /* set if short timeout waiting for input */
+extern bool Windows; /* set if we are set up for curses stuff */
+extern bool Luckout; /* set if we have tried to luck out in fight */
+extern bool Foestrikes; /* set if foe gets a chance to hit in battleplayer()*/
+extern bool Echo; /* set if echo input to terminal */
+
+extern int Users; /* number of users currently playing */
+extern int Whichmonster; /* which monster we are fighting */
+extern int Lines; /* line on screen counter for fight routines */
+
+extern jmp_buf Fightenv; /* used to jump into fight routine */
+extern jmp_buf Timeoenv; /* used for timing out waiting for input */
+
+extern long Fileloc; /* location in file of player statistics */
+
+extern char *Login; /* pointer to login of current player */
+extern char *Enemyname; /* pointer name of monster/player we are battling*/
+
+extern struct player Player; /* stats for player */
+extern struct player Other; /* stats for another player */
+
+extern struct monster Curmonster;/* stats for current monster */
+
+extern struct energyvoid Enrgyvoid;/* energy void buffer */
+
+extern struct charstats Stattable[];/* used for rolling and changing player stats*/
+
+extern struct charstats *Statptr;/* pointer into Stattable[] */
+
+extern struct menuitem Menu[]; /* menu of items for purchase */
+
+extern FILE *Playersfp; /* pointer to open player file */
+extern FILE *Monstfp; /* pointer to open monster file */
+extern FILE *Messagefp; /* pointer to open message file */
+extern FILE *Energyvoidfp; /* pointer to open energy void file */
+
+extern char Databuf[]; /* a place to read data into */
+
+/* some canned strings for messages */
+extern char Illcmd[];
+extern char Illmove[];
+extern char Illspell[];
+extern char Nomana[];
+extern char Somebetter[];
+extern char Nobetter[];
+
+/* library functions and system calls */
+extern long time();
+extern char *getlogin();
+extern char *getpass();
+extern char *strchr();
+extern char *strcat();
+extern char *strcpy();
+extern char *strncpy();
+extern char *getenv();
+struct passwd *getpwuid();
+extern char *fgets();
+
+/* functions which we need to know about */
+extern int interrupt();
+extern int ill_sig();
+extern void catchalarm();
+extern long recallplayer();
+extern long findname();
+extern long allocrecord();
+extern long rollnewplayer();
+extern long allocvoid();
+extern double drandom();
+extern double distance();
+extern double infloat();
+extern double explevel();
+extern char *descrlocation();
+extern char *descrtype();
+extern char *descrstatus();
diff --git a/phantasia/phantstruct.h b/phantasia/phantstruct.h
new file mode 100644
index 00000000..05c6da57
--- /dev/null
+++ b/phantasia/phantstruct.h
@@ -0,0 +1,124 @@
+/*
+ * phantstruct.h - structure definitions for Phantasia
+ */
+
+struct player /* player statistics */
+ {
+ double p_experience; /* experience */
+ double p_level; /* level */
+ double p_strength; /* strength */
+ double p_sword; /* sword */
+ double p_might; /* effect strength */
+ double p_energy; /* energy */
+ double p_maxenergy; /* maximum energy */
+ double p_shield; /* shield */
+ double p_quickness; /* quickness */
+ double p_quksilver; /* quicksilver */
+ double p_speed; /* effective quickness */
+ double p_magiclvl; /* magic level */
+ double p_mana; /* mana */
+ double p_brains; /* brains */
+ double p_poison; /* poison */
+ double p_gold; /* gold */
+ double p_gems; /* gems */
+ double p_sin; /* sin */
+ double p_x; /* x coord */
+ double p_y; /* y coord */
+ double p_1scratch,
+ p_2scratch; /* variables used for decree, player battle */
+
+ struct
+ {
+ short ring_type; /* type of ring */
+ short ring_duration; /* duration of ring */
+ bool ring_inuse; /* ring in use flag */
+ } p_ring; /* ring stuff */
+
+ long p_age; /* age of player */
+
+ int p_degenerated; /* age/3000 last degenerated */
+
+ short p_type; /* character type */
+ short p_specialtype; /* special character type */
+ short p_lives; /* multiple lives for council, valar */
+ short p_crowns; /* crowns */
+ short p_charms; /* charms */
+ short p_amulets; /* amulets */
+ short p_holywater; /* holy water */
+ short p_lastused; /* day of year last used */
+ short p_status; /* playing, cloaked, etc. */
+ short p_tampered; /* decree'd, etc. flag */
+ short p_istat; /* used for inter-terminal battle */
+
+ bool p_palantir; /* palantir */
+ bool p_blessing; /* blessing */
+ bool p_virgin; /* virgin */
+ bool p_blindness; /* blindness */
+
+ char p_name[SZ_NAME]; /* name */
+ char p_password[SZ_PASSWORD];/* password */
+ char p_login[SZ_LOGIN]; /* login */
+ };
+
+struct monster /* monster stats */
+ {
+ double m_strength; /* strength */
+ double m_brains; /* brains */
+ double m_speed; /* speed */
+ double m_energy; /* energy */
+ double m_experience; /* experience */
+ double m_flock; /* % chance of flocking */
+
+ double m_o_strength; /* original strength */
+ double m_o_speed; /* original speed */
+ double m_maxspeed; /* maximum speed */
+ double m_o_energy; /* original energy */
+ double m_melee; /* melee damage */
+ double m_skirmish; /* skirmish damage */
+
+ int m_treasuretype; /* treasure type */
+ int m_type; /* special type */
+
+ char m_name[26]; /* name */
+ };
+
+struct energyvoid /* energy void */
+ {
+ double ev_x; /* x coordinate */
+ double ev_y; /* y coordinate */
+ bool ev_active; /* active or not */
+ };
+
+struct scoreboard /* scoreboard entry */
+ {
+ double sb_level; /* level of player */
+ char sb_type[4]; /* character type of player */
+ char sb_name[SZ_NAME]; /* name of player */
+ char sb_login[SZ_LOGIN]; /* login of player */
+ };
+
+struct charstats /* character type statistics */
+ {
+ double c_maxbrains; /* max brains per level */
+ double c_maxmana; /* max mana per level */
+ double c_weakness; /* how strongly poison affects player */
+ double c_goldtote; /* how much gold char can carry */
+ int c_ringduration; /* bad ring duration */
+ struct
+ {
+ double base; /* base for roll */
+ double interval; /* interval for roll */
+ double increase; /* increment per level */
+ } c_quickness, /* quickness */
+ c_strength, /* strength */
+ c_mana, /* mana */
+ c_energy, /* energy level */
+ c_brains, /* brains */
+ c_magiclvl; /* magic level */
+ };
+
+struct menuitem /* menu item for purchase */
+ {
+ char *item; /* menu item name */
+ double cost; /* cost of item */
+ };
diff --git a/phantasia/setup.c b/phantasia/setup.c
new file mode 100644
index 00000000..f916727f
--- /dev/null
+++ b/phantasia/setup.c
@@ -0,0 +1,261 @@
+/*
+ * setup.c - set up all files for Phantasia
+ */
+#include "include.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: main()
+/
+/ FUNCTION: setup files for Phantasia 3.3.2
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: time(), exit(), stat(), Error(), creat(), close(), fopen(),
+/ fgets(), floor(), srandom(), umask(), drandom(), strcpy(), getuid(),
+/ unlink(), fwrite(), fclose(), sscanf(), printf(), strlen(), fprintf()
+/
+/ GLOBAL INPUTS: Curmonster, _iob[], Databuf[], *Monstfp, Enrgyvoid
+/
+/ GLOBAL OUTPUTS: Curmonster, Databuf[], *Monstfp, Enrgyvoid
+/
+/ DESCRIPTION:
+/
+/ This program tries to verify the parameters specified in
+/ the Makefile.
+/
+/ Create all necessary files. Note that nothing needs to be
+/ put in these files.
+/ Also, the monster binary data base is created here.
+/
+/************************************************************************/
+
+static char *files[] = { /* all files to create */
+ _PATH_MONST,
+ _PATH_PEOPLE,
+ _PATH_MESS,
+ _PATH_LASTDEAD,
+ _PATH_MOTD,
+ _PATH_GOLD,
+ _PATH_VOID,
+ _PATH_SCORE,
+ NULL,
+};
+
+char *monsterfile="monsters.asc";
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char **filename; /* for pointing to file names */
+ register int fd; /* file descriptor */
+ FILE *fp; /* for opening files */
+ struct stat fbuf; /* for getting files statistics */
+ int ch;
+
+ while ((ch = getopt(argc, argv, "m:")) != EOF)
+ switch(ch) {
+ case 'm':
+ monsterfile = optarg;
+ break;
+ case '?':
+ default:
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ srandom((unsigned) time((long *) NULL)); /* prime random numbers */
+
+ umask(0117); /* only owner can read/write created files */
+
+ /* try to create data files */
+ filename = &files[0];
+ while (*filename != NULL)
+ /* create each file */
+ {
+ if (stat(*filename, &fbuf) == 0)
+ /* file exists; remove it */
+ {
+ if (!strcmp(*filename, _PATH_PEOPLE))
+ /* do not reset character file if it already exists */
+ {
+ ++filename;
+ continue;
+ }
+
+ if (unlink(*filename) < 0)
+ Error("Cannot unlink %s.\n", *filename);
+ /*NOTREACHED*/
+ }
+
+ if ((fd = creat(*filename, 0660)) < 0)
+ Error("Cannot create %s.\n", *filename);
+ /*NOTREACHED*/
+
+ close(fd); /* close newly created file */
+
+ ++filename; /* process next file */
+ }
+
+ /* put holy grail info into energy void file */
+ Enrgyvoid.ev_active = TRUE;
+ Enrgyvoid.ev_x = ROLL(-1.0e6, 2.0e6);
+ Enrgyvoid.ev_y = ROLL(-1.0e6, 2.0e6);
+ if ((fp = fopen(_PATH_VOID, "w")) == NULL)
+ Error("Cannot update %s.\n", _PATH_VOID);
+ else
+ {
+ fwrite(&Enrgyvoid, SZ_VOIDSTRUCT, 1, fp);
+ fclose(fp);
+ }
+
+ /* create binary monster data base */
+ if ((Monstfp = fopen(_PATH_MONST, "w")) == NULL)
+ Error("Cannot update %s.\n", _PATH_MONST);
+ else
+ {
+ if ((fp = fopen(monsterfile, "r")) == NULL)
+ {
+ fclose(Monstfp);
+ Error("cannot open %s to create monster database.\n", "monsters.asc");
+ }
+ else
+ {
+ Curmonster.m_o_strength =
+ Curmonster.m_o_speed =
+ Curmonster.m_maxspeed =
+ Curmonster.m_o_energy =
+ Curmonster.m_melee =
+ Curmonster.m_skirmish = 0.0;
+
+ while (fgets(Databuf, SZ_DATABUF, fp) != NULL)
+ /* read in text file, convert to binary */
+ {
+ sscanf(&Databuf[24], "%lf%lf%lf%lf%lf%d%d%lf",
+ &Curmonster.m_strength, &Curmonster.m_brains,
+ &Curmonster.m_speed, &Curmonster.m_energy,
+ &Curmonster.m_experience, &Curmonster.m_treasuretype,
+ &Curmonster.m_type, &Curmonster.m_flock);
+ Databuf[24] = '\0';
+ strcpy(Curmonster.m_name, Databuf);
+ fwrite((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
+ }
+ fclose(fp);
+ fclose(Monstfp);
+ }
+ }
+
+#ifdef MAKE_INSTALLS_THIS_AND_DOESNT_WANT_TO_HEAR_ABOUT_IT
+ /* write to motd file */
+ printf("One line 'motd' ? ");
+ if (fgets(Databuf, SZ_DATABUF, stdin) == NULL)
+ Databuf[0] = '\0';
+ if ((fp = fopen(_PATH_MOTD, "w")) == NULL)
+ Error("Cannot update %s.\n", _PATH_MOTD);
+ else
+ {
+ fwrite(Databuf, sizeof(char), strlen(Databuf), fp);
+ fclose(fp);
+ }
+
+ /* report compile-time options */
+ printf("Compiled options:\n\n");
+ printf("Phantasia destination directory: %s\n", _PATH_PHANTDIR);
+ printf("Wizard: root UID: 0\n");
+
+#ifdef BSD41
+ printf("Compiled for BSD 4.1\n");
+#endif
+
+#ifdef BSD42
+ printf("Compiled for BSD 4.2\n");
+#endif
+
+#ifdef SYS3
+ printf("Compiled for System III\n");
+#endif
+
+#ifdef SYS5
+ printf("Compiled for System V\n");
+#endif
+#endif
+
+ exit(0);
+ /*NOTREACHED*/
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: Error()
+/
+/ FUNCTION: print an error message, and exit
+/
+/ AUTHOR: E. A. Estes, 12/4/85
+/
+/ ARGUMENTS:
+/ char *str - format string for printf()
+/ char *file - file which caused error
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: exit(), perror(), fprintf()
+/
+/ GLOBAL INPUTS: _iob[]
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/ Print an error message, then exit.
+/
+/************************************************************************/
+
+Error(str, file)
+char *str, *file;
+{
+ fprintf(stderr, "Error: ");
+ fprintf(stderr, str, file);
+ perror(file);
+ exit(1);
+ /*NOTREACHED*/
+}
+/* */
+/************************************************************************
+/
+/ FUNCTION NAME: drandom()
+/
+/ FUNCTION: return a random number
+/
+/ AUTHOR: E. A. Estes, 2/7/86
+/
+/ ARGUMENTS: none
+/
+/ RETURN VALUE: none
+/
+/ MODULES CALLED: random()
+/
+/ GLOBAL INPUTS: none
+/
+/ GLOBAL OUTPUTS: none
+/
+/ DESCRIPTION:
+/
+/************************************************************************/
+
+double
+drandom()
+{
+ if (sizeof(int) != 2)
+ return((double) (random() & 0x7fff) / 32768.0);
+ else
+ return((double) random() / 32768.0);
+}