From bcced3608bdd120315d4ed1d9b9478f7caa4a2f1 Mon Sep 17 00:00:00 2001 From: jtc Date: Fri, 21 Oct 1994 21:19:39 +0000 Subject: phantasia(6), from 44lite --- phantasia/COPYRIGHT | 24 + phantasia/Makefile | 30 + phantasia/OWNER | 6 + phantasia/README | 82 +++ phantasia/convert.c | 210 ++++++ phantasia/fight.c | 1688 ++++++++++++++++++++++++++++++++++++++++++++++ phantasia/gamesupport.c | 722 ++++++++++++++++++++ phantasia/include.h | 17 + phantasia/interplayer.c | 1208 +++++++++++++++++++++++++++++++++ phantasia/io.c | 436 ++++++++++++ phantasia/macros.h | 16 + phantasia/main.c | 1288 +++++++++++++++++++++++++++++++++++ phantasia/map.c | 160 +++++ phantasia/misc.c | 1703 +++++++++++++++++++++++++++++++++++++++++++++++ phantasia/monsters.asc | 100 +++ phantasia/oldplayer.h | 54 ++ phantasia/pathnames.h | 44 ++ phantasia/phantasia.6 | 1220 +++++++++++++++++++++++++++++++++ phantasia/phantdefs.h | 139 ++++ phantasia/phantglobs.c | 113 ++++ phantasia/phantglobs.h | 86 +++ phantasia/phantstruct.h | 124 ++++ phantasia/setup.c | 261 ++++++++ 23 files changed, 9731 insertions(+) create mode 100644 phantasia/COPYRIGHT create mode 100644 phantasia/Makefile create mode 100644 phantasia/OWNER create mode 100644 phantasia/README create mode 100644 phantasia/convert.c create mode 100644 phantasia/fight.c create mode 100644 phantasia/gamesupport.c create mode 100644 phantasia/include.h create mode 100644 phantasia/interplayer.c create mode 100644 phantasia/io.c create mode 100644 phantasia/macros.h create mode 100644 phantasia/main.c create mode 100644 phantasia/map.c create mode 100644 phantasia/misc.c create mode 100644 phantasia/monsters.asc create mode 100644 phantasia/oldplayer.h create mode 100644 phantasia/pathnames.h create mode 100644 phantasia/phantasia.6 create mode 100644 phantasia/phantdefs.h create mode 100644 phantasia/phantglobs.c create mode 100644 phantasia/phantglobs.h create mode 100644 phantasia/phantstruct.h create mode 100644 phantasia/setup.c (limited to 'phantasia') 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 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, "") == 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(<ime); + today = localtime(<ime)->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 +#include +#include +#include +#include +#include +#include + +#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 +#include + +/* + * 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 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 +#include +#include +/* */ +/************************************************************************ +/ +/ 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); +} -- cgit v1.2.3-56-ge451