1 /* $OpenBSD: misc.c,v 1.22 2023/10/10 09:43:52 tb Exp $ */
2 /* $NetBSD: misc.c,v 1.2 1995/03/24 03:59:03 cgd Exp $ */
3
4 /*
5 * misc.c Phantasia miscellaneous support routines
6 */
7
8 #include <curses.h>
9 #include <err.h>
10 #include <math.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16
17 #include "macros.h"
18 #include "pathnames.h"
19 #include "phantdefs.h"
20 #include "phantglobs.h"
21
22 /************************************************************************
23 /
24 / FUNCTION NAME: movelevel()
25 /
26 / FUNCTION: move player to new level
27 /
28 / AUTHOR: E. A. Estes, 12/4/85
29 /
30 / ARGUMENTS: none
31 /
32 / RETURN VALUE: none
33 /
34 / MODULES CALLED: death(), floor(), wmove(), drandom(), waddstr(), explevel()
35 /
36 / GLOBAL INPUTS: Player, *stdscr, *Statptr, Stattable[]
37 /
38 / GLOBAL OUTPUTS: Player, Changed
39 /
40 / DESCRIPTION:
41 / Use lookup table to increment important statistics when
42 / progressing to new experience level.
43 / Players are rested to maximum as a bonus for making a new
44 / level.
45 / Check for council of wise, and being too big to be king.
46 /
47 *************************************************************************/
48
49 void
movelevel(void)50 movelevel(void)
51 {
52 struct charstats *statptr; /* for pointing into Stattable */
53 double new; /* new level */
54 double inc; /* increment between new and old levels */
55
56 Changed = TRUE;
57
58 if (Player.p_type == C_EXPER)
59 /* roll a type to use for increment */
60 statptr = &Stattable[(int) ROLL(C_MAGIC, C_HALFLING - C_MAGIC + 1)];
61 else
62 statptr = Statptr;
63
64 new = explevel(Player.p_experience);
65 inc = new - Player.p_level;
66 Player.p_level = new;
67
68 /* add increments to statistics */
69 Player.p_strength += statptr->c_strength.increase * inc;
70 Player.p_mana += statptr->c_mana.increase * inc;
71 Player.p_brains += statptr->c_brains.increase * inc;
72 Player.p_magiclvl += statptr->c_magiclvl.increase * inc;
73 Player.p_maxenergy += statptr->c_energy.increase * inc;
74
75 /* rest to maximum upon reaching new level */
76 Player.p_energy = Player.p_maxenergy + Player.p_shield;
77
78 if (Player.p_crowns > 0 && Player.p_level >= 1000.0)
79 /* no longer able to be king -- turn crowns into cash */
80 {
81 Player.p_gold += ((double) Player.p_crowns) * 5000.0;
82 Player.p_crowns = 0;
83 }
84 if (Player.p_level >= 3000.0 && Player.p_specialtype < SC_COUNCIL)
85 /* make a member of the council */
86 {
87 mvaddstr(6, 0, "You have made it to the Council of the Wise.\n");
88 addstr("Good Luck on your search for the Holy Grail.\n");
89
90 Player.p_specialtype = SC_COUNCIL;
91
92 /* no rings for council and above */
93 Player.p_ring.ring_type = R_NONE;
94 Player.p_ring.ring_duration = 0;
95
96 Player.p_lives = 3; /* three extra lives */
97 }
98 if (Player.p_level > 9999.0 && Player.p_specialtype != SC_VALAR)
99 death("Old age");
100 }
101 /**/
102 /************************************************************************
103 /
104 / FUNCTION NAME: descrlocation()
105 /
106 / FUNCTION: return a formatted description of location
107 /
108 / AUTHOR: E. A. Estes, 12/4/85
109 /
110 / ARGUMENTS:
111 / struct player playerp - pointer to player structure
112 / bool shortflag - set if short form is desired
113 /
114 / RETURN VALUE: pointer to string containing result
115 /
116 / MODULES CALLED: fabs(), floor(), snprintf(), distance()
117 /
118 / GLOBAL INPUTS: Databuf[]
119 /
120 / GLOBAL OUTPUTS: none
121 /
122 / DESCRIPTION:
123 / Look at coordinates and return an appropriately formatted
124 / string.
125 /
126 *************************************************************************/
127
128 char *
descrlocation(struct player * playerp,bool shortflag)129 descrlocation(struct player *playerp, bool shortflag)
130 {
131 double circle; /* corresponding circle for coordinates */
132 int quadrant; /* quandrant of grid */
133 char *label; /* pointer to place name */
134 static char *nametable[4][4] = /* names of places */
135 {
136 {"Anorien", "Ithilien", "Rohan", "Lorien"},
137 {"Gondor", "Mordor", "Dunland", "Rovanion"},
138 {"South Gondor", "Khand", "Eriador", "The Iron Hills"},
139 {"Far Harad", "Near Harad", "The Northern Waste", "Rhun"}
140 };
141
142 if (playerp->p_specialtype == SC_VALAR)
143 return (" is in Valhala");
144 else if ((circle = CIRCLE(playerp->p_x, playerp->p_y)) >= 1000.0) {
145 if (MAX(fabs(playerp->p_x), fabs(playerp->p_y)) > D_BEYOND)
146 label = "The Point of No Return";
147 else
148 label = "The Ashen Mountains";
149 } else if (circle >= 55)
150 label = "Morannon";
151 else if (circle >= 35)
152 label = "Kennaquahair";
153 else if (circle >= 20)
154 label = "The Dead Marshes";
155 else if (circle >= 9)
156 label = "The Outer Waste";
157 else if (circle >= 5)
158 label = "The Moors Adventurous";
159 else {
160 if (playerp->p_x == 0.0 && playerp->p_y == 0.0)
161 label = "The Lord's Chamber";
162 else {
163 /* this expression is split to prevent compiler
164 * loop with some compilers */
165 quadrant = ((playerp->p_x > 0.0) ? 1 : 0);
166 quadrant += ((playerp->p_y >= 0.0) ? 2 : 0);
167 label = nametable[((int) circle) - 1][quadrant];
168 }
169 }
170
171 if (shortflag)
172 snprintf(Databuf, sizeof Databuf, "%.29s", label);
173 else
174 snprintf(Databuf, sizeof Databuf,
175 " is in %s (%.0f,%.0f)", label, playerp->p_x, playerp->p_y);
176
177 return (Databuf);
178 }
179 /**/
180 /************************************************************************
181 /
182 / FUNCTION NAME: tradingpost()
183 /
184 / FUNCTION: do trading post stuff
185 /
186 / AUTHOR: E. A. Estes, 12/4/85
187 /
188 / ARGUMENTS: none
189 /
190 / RETURN VALUE: none
191 /
192 / MODULES CALLED: writerecord(), adjuststats(), fabs(), more(), sqrt(),
193 / sleep(), floor(), wmove(), drandom(), wclear(), printw(),
194 / altercoordinates(), infloat(), waddstr(), wrefresh(), mvprintw(), getanswer(),
195 / wclrtoeol(), wclrtobot()
196 /
197 / GLOBAL INPUTS: Menu[], Circle, Player, *stdscr, Fileloc, Nobetter[]
198 /
199 / GLOBAL OUTPUTS: Player
200 /
201 / DESCRIPTION:
202 / Different trading posts have different items.
203 / Merchants cannot be cheated, but they can be dishonest
204 / themselves.
205 /
206 / Shields, swords, and quicksilver are not cumulative. This is
207 / one major area of complaint, but there are two reasons for this:
208 / 1) It becomes MUCH too easy to make very large versions
209 / of these items.
210 / 2) In the real world, one cannot simply weld two swords
211 / together to make a bigger one.
212 /
213 / At one time, it was possible to sell old weapons at half the purchase
214 / price. This resulted in huge amounts of gold floating around,
215 / and the game lost much of its challenge.
216 /
217 / Also, purchasing gems defeats the whole purpose of gold. Gold
218 / is small change for lower level players. They really shouldn't
219 / be able to accumulate more than enough gold for a small sword or
220 / a few books. Higher level players shouldn't even bother to pick
221 / up gold, except maybe to buy mana once in a while.
222 /
223 *************************************************************************/
224
225 void
tradingpost(void)226 tradingpost(void)
227 {
228 double numitems; /* number of items to purchase */
229 double cost; /* cost of purchase */
230 double blessingcost; /* cost of blessing */
231 int ch; /* input */
232 int size; /* size of the trading post */
233 int loop; /* loop counter */
234 int cheat = 0; /* number of times player has tried to cheat */
235 bool dishonest = FALSE; /* set when merchant is dishonest */
236
237 Player.p_status = S_TRADING;
238 writerecord(&Player, Fileloc);
239
240 clear();
241 addstr("You are at a trading post. All purchases must be made with gold.");
242
243 size = sqrt(fabs(Player.p_x / 100)) + 1;
244 size = MIN(7, size);
245
246 /* set up cost of blessing */
247 blessingcost = 1000.0 * (Player.p_level + 5.0);
248
249 /* print Menu */
250 move(7, 0);
251 for (loop = 0; loop < size; ++loop)
252 /* print Menu */
253 {
254 if (loop == 6)
255 cost = blessingcost;
256 else
257 cost = Menu[loop].cost;
258 printw("(%d) %-12s: %6.0f\n", loop + 1, Menu[loop].item, cost);
259 }
260
261 mvprintw(5, 0, "L:Leave P:Purchase S:Sell Gems ? ");
262
263 for (;;) {
264 adjuststats(); /* truncate any bad values */
265
266 /* print some important statistics */
267 mvprintw(1, 0, "Gold: %9.0f Gems: %9.0f Level: %6.0f Charms: %6d\n",
268 Player.p_gold, Player.p_gems, Player.p_level, Player.p_charms);
269 printw("Shield: %9.0f Sword: %9.0f Quicksilver:%3.0f Blessed: %s\n",
270 Player.p_shield, Player.p_sword, Player.p_quksilver,
271 (Player.p_blessing ? " True" : "False"));
272 printw("Brains: %9.0f Mana: %9.0f", Player.p_brains, Player.p_mana);
273
274 move(5, 36);
275 ch = getanswer("LPS", FALSE);
276 move(15, 0);
277 clrtobot();
278 switch (ch) {
279 case 'L': /* leave */
280 case '\n':
281 altercoordinates(0.0, 0.0, A_NEAR);
282 return;
283
284 case 'P': /* make purchase */
285 mvaddstr(15, 0, "What what would you like to buy ? ");
286 ch = getanswer(" 1234567", FALSE);
287 move(15, 0);
288 clrtoeol();
289
290 if (ch - '0' > size)
291 addstr("Sorry, this merchant doesn't have that.");
292 else
293 switch (ch) {
294 case '1':
295 printw("Mana is one per %.0f gold piece. How many do you want (%.0f max) ? ",
296 Menu[0].cost, floor(Player.p_gold / Menu[0].cost));
297 cost = (numitems = floor(infloat())) * Menu[0].cost;
298
299 if (cost > Player.p_gold || numitems < 0)
300 ++cheat;
301 else {
302 cheat = 0;
303 Player.p_gold -= cost;
304 if (drandom() < 0.02)
305 dishonest = TRUE;
306 else
307 Player.p_mana += numitems;
308 }
309 break;
310
311 case '2':
312 printw("Shields are %.0f per +1. How many do you want (%.0f max) ? ",
313 Menu[1].cost, floor(Player.p_gold / Menu[1].cost));
314 cost = (numitems = floor(infloat())) * Menu[1].cost;
315
316 if (numitems == 0.0)
317 break;
318 else if (cost > Player.p_gold || numitems < 0)
319 ++cheat;
320 else if (numitems < Player.p_shield)
321 NOBETTER();
322 else {
323 cheat = 0;
324 Player.p_gold -= cost;
325 if (drandom() < 0.02)
326 dishonest = TRUE;
327 else
328 Player.p_shield = numitems;
329 }
330 break;
331
332 case '3':
333 printw("A book costs %.0f gp. How many do you want (%.0f max) ? ",
334 Menu[2].cost, floor(Player.p_gold / Menu[2].cost));
335 cost = (numitems = floor(infloat())) * Menu[2].cost;
336
337 if (cost > Player.p_gold || numitems < 0)
338 ++cheat;
339 else {
340 cheat = 0;
341 Player.p_gold -= cost;
342 if (drandom() < 0.02)
343 dishonest = TRUE;
344 else if (drandom() * numitems > Player.p_level / 10.0
345 && numitems != 1) {
346 printw("\nYou blew your mind!\n");
347 Player.p_brains /= 5;
348 } else {
349 Player.p_brains += floor(numitems) * ROLL(20, 8);
350 }
351 }
352 break;
353
354 case '4':
355 printw("Swords are %.0f gp per +1. How many + do you want (%.0f max) ? ",
356 Menu[3].cost, floor(Player.p_gold / Menu[3].cost));
357 cost = (numitems = floor(infloat())) * Menu[3].cost;
358
359 if (numitems == 0.0)
360 break;
361 else if (cost > Player.p_gold || numitems < 0)
362 ++cheat;
363 else if (numitems < Player.p_sword)
364 NOBETTER();
365 else {
366 cheat = 0;
367 Player.p_gold -= cost;
368 if (drandom() < 0.02)
369 dishonest = TRUE;
370 else
371 Player.p_sword = numitems;
372 }
373 break;
374
375 case '5':
376 printw("A charm costs %.0f gp. How many do you want (%.0f max) ? ",
377 Menu[4].cost, floor(Player.p_gold / Menu[4].cost));
378 cost = (numitems = floor(infloat())) * Menu[4].cost;
379
380 if (cost > Player.p_gold || numitems < 0)
381 ++cheat;
382 else {
383 cheat = 0;
384 Player.p_gold -= cost;
385 if (drandom() < 0.02)
386 dishonest = TRUE;
387 else
388 Player.p_charms += numitems;
389 }
390 break;
391
392 case '6':
393 printw("Quicksilver is %.0f gp per +1. How many + do you want (%.0f max) ? ",
394 Menu[5].cost, floor(Player.p_gold / Menu[5].cost));
395 cost = (numitems = floor(infloat())) * Menu[5].cost;
396
397 if (numitems == 0.0)
398 break;
399 else if (cost > Player.p_gold || numitems < 0)
400 ++cheat;
401 else if (numitems < Player.p_quksilver)
402 NOBETTER();
403 else {
404 cheat = 0;
405 Player.p_gold -= cost;
406 if (drandom() < 0.02)
407 dishonest = TRUE;
408 else
409 Player.p_quksilver = numitems;
410 }
411 break;
412
413 case '7':
414 if (Player.p_blessing) {
415 addstr("You already have a blessing.");
416 break;
417 }
418 printw("A blessing requires a %.0f gp donation. Still want one ? ", blessingcost);
419 ch = getanswer("NY", FALSE);
420
421 if (ch == 'Y') {
422 if (Player.p_gold < blessingcost)
423 ++cheat;
424 else {
425 cheat = 0;
426 Player.p_gold -= blessingcost;
427 if (drandom() < 0.02)
428 dishonest = TRUE;
429 else
430 Player.p_blessing = TRUE;
431 }
432 }
433 break;
434 }
435 break;
436
437 case 'S': /* sell gems */
438 mvprintw(15, 0, "A gem is worth %.0f gp. How many do you want to sell (%.0f max) ? ",
439 (double) N_GEMVALUE, Player.p_gems);
440 numitems = floor(infloat());
441
442 if (numitems > Player.p_gems || numitems < 0)
443 ++cheat;
444 else {
445 cheat = 0;
446 Player.p_gems -= numitems;
447 Player.p_gold += numitems * N_GEMVALUE;
448 }
449 }
450
451 if (cheat == 1)
452 mvaddstr(17, 0, "Come on, merchants aren't stupid. Stop cheating.\n");
453 else if (cheat == 2) {
454 mvaddstr(17, 0, "You had your chance. This merchant happens to be\n");
455 printw("a %.0f level magic user, and you made %s mad!\n",
456 ROLL(Circle * 20.0, 40.0), (drandom() < 0.5) ? "him" : "her");
457 altercoordinates(0.0, 0.0, A_FAR);
458 Player.p_energy /= 2.0;
459 ++Player.p_sin;
460 more(23);
461 return;
462 } else if (dishonest) {
463 mvaddstr(17, 0, "The merchant stole your money!");
464 refresh();
465 altercoordinates(Player.p_x - Player.p_x / 10.0,
466 Player.p_y - Player.p_y / 10.0, A_SPECIFIC);
467 sleep(2);
468 return;
469 }
470 }
471 }
472 /**/
473 /************************************************************************
474 /
475 / FUNCTION NAME: displaystats()
476 /
477 / FUNCTION: print out important player statistics
478 /
479 / AUTHOR: E. A. Estes, 12/4/85
480 /
481 / ARGUMENTS: none
482 /
483 / RETURN VALUE: none
484 /
485 / MODULES CALLED: descrstatus(), descrlocation(), mvprintw()
486 /
487 / GLOBAL INPUTS: Users, Player
488 /
489 / GLOBAL OUTPUTS: none
490 /
491 / DESCRIPTION:
492 / Important player statistics are printed on the screen.
493 /
494 *************************************************************************/
495
496 void
displaystats(void)497 displaystats(void)
498 {
499 mvprintw(0, 0, "%s%s\n", Player.p_name, descrlocation(&Player, FALSE));
500 mvprintw(1, 0, "Level :%7.0f Energy :%9.0f(%9.0f) Mana :%9.0f Users:%3d\n",
501 Player.p_level, Player.p_energy, Player.p_maxenergy + Player.p_shield,
502 Player.p_mana, Users);
503 mvprintw(2, 0, "Quick :%3.0f(%3.0f) Strength:%9.0f(%9.0f) Gold :%9.0f %s\n",
504 Player.p_speed, Player.p_quickness + Player.p_quksilver, Player.p_might,
505 Player.p_strength + Player.p_sword, Player.p_gold, descrstatus(&Player));
506 }
507 /**/
508 /************************************************************************
509 /
510 / FUNCTION NAME: allstatslist()
511 /
512 / FUNCTION: show player items
513 /
514 / AUTHOR: E. A. Estes, 12/4/85
515 /
516 / ARGUMENTS: none
517 /
518 / RETURN VALUE: none
519 /
520 / MODULES CALLED: mvprintw(), descrtype()
521 /
522 / GLOBAL INPUTS: Player
523 /
524 / GLOBAL OUTPUTS: none
525 /
526 / DESCRIPTION:
527 / Print out some player statistics of lesser importance.
528 /
529 *************************************************************************/
530
531 void
allstatslist(void)532 allstatslist(void)
533 {
534 static char *flags[] = /* to print value of some bools */
535 {
536 "False",
537 " True"
538 };
539
540 mvprintw(8, 0, "Type: %s\n", descrtype(&Player, FALSE));
541
542 mvprintw(10, 0, "Experience: %9.0f", Player.p_experience);
543 mvprintw(11, 0, "Brains : %9.0f", Player.p_brains);
544 mvprintw(12, 0, "Magic Lvl : %9.0f", Player.p_magiclvl);
545 mvprintw(13, 0, "Sin : %9.5f", Player.p_sin);
546 mvprintw(14, 0, "Poison : %9.5f", Player.p_poison);
547 mvprintw(15, 0, "Gems : %9.0f", Player.p_gems);
548 mvprintw(16, 0, "Age : %9ld", Player.p_age);
549 mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater);
550 mvprintw(11, 40, "Amulets : %9d", Player.p_amulets);
551 mvprintw(12, 40, "Charms : %9d", Player.p_charms);
552 mvprintw(13, 40, "Crowns : %9d", Player.p_crowns);
553 mvprintw(14, 40, "Shield : %9.0f", Player.p_shield);
554 mvprintw(15, 40, "Sword : %9.0f", Player.p_sword);
555 mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver);
556
557 mvprintw(18, 0, "Blessing: %s Ring: %s Virgin: %s Palantir: %s",
558 flags[(int)Player.p_blessing],
559 flags[Player.p_ring.ring_type != R_NONE],
560 flags[(int)Player.p_virgin],
561 flags[(int)Player.p_palantir]);
562 }
563 /**/
564 /************************************************************************
565 /
566 / FUNCTION NAME: descrtype()
567 /
568 / FUNCTION: return a string specifying player type
569 /
570 / AUTHOR: E. A. Estes, 12/4/85
571 /
572 / ARGUMENTS:
573 / struct player playerp - pointer to structure for player
574 / bool shortflag - set if short form is desired
575 /
576 / RETURN VALUE: pointer to string describing player type
577 /
578 / MODULES CALLED: strlcpy()
579 /
580 / GLOBAL INPUTS: Databuf[]
581 /
582 / GLOBAL OUTPUTS: Databuf[]
583 /
584 / DESCRIPTION:
585 / Return a string describing the player type.
586 / King, council, valar, supersedes other types.
587 / The first character of the string is '*' if the player
588 / has a crown.
589 / If 'shortflag' is TRUE, return a 3 character string.
590 /
591 *************************************************************************/
592
593 char *
descrtype(struct player * playerp,bool shortflag)594 descrtype(struct player *playerp, bool shortflag)
595 {
596 int type; /* for caluculating result subscript */
597 static char *results[] =/* description table */
598 {
599 " Magic User", " MU",
600 " Fighter", " F ",
601 " Elf", " E ",
602 " Dwarf", " D ",
603 " Halfling", " H ",
604 " Experimento", " EX",
605 " Super", " S ",
606 " King", " K ",
607 " Council of Wise", " CW",
608 " Ex-Valar", " EV",
609 " Valar", " V ",
610 " ? ", " ? "
611 };
612
613 type = playerp->p_type;
614
615 switch (playerp->p_specialtype) {
616 case SC_NONE:
617 type = playerp->p_type;
618 break;
619
620 case SC_KING:
621 type = 7;
622 break;
623
624 case SC_COUNCIL:
625 type = 8;
626 break;
627
628 case SC_EXVALAR:
629 type = 9;
630 break;
631
632 case SC_VALAR:
633 type = 10;
634 break;
635 }
636
637 type *= 2; /* calculate offset */
638
639 if (type > 20)
640 /* error */
641 type = 22;
642
643 if (shortflag)
644 /* use short descriptions */
645 ++type;
646
647 if (playerp->p_crowns > 0) {
648 strlcpy(Databuf, results[type], sizeof Databuf);
649 Databuf[0] = '*';
650 return (Databuf);
651 } else
652 return (results[type]);
653 }
654 /**/
655 /************************************************************************
656 /
657 / FUNCTION NAME: findname()
658 /
659 / FUNCTION: find location in player file of given name
660 /
661 / AUTHOR: E. A. Estes, 12/4/85
662 /
663 / ARGUMENTS:
664 / char *name - name of character to look for
665 / struct player *playerp - pointer of structure to fill
666 /
667 / RETURN VALUE: location of player if found, -1 otherwise
668 /
669 / MODULES CALLED: fread(), fseek(), strcmp()
670 /
671 / GLOBAL INPUTS: Wizard, *Playersfp
672 /
673 / GLOBAL OUTPUTS: none
674 /
675 / DESCRIPTION:
676 / Search the player file for the player of the given name.
677 / If player is found, fill structure with player data.
678 /
679 *************************************************************************/
680
681 long
findname(char * name,struct player * playerp)682 findname(char *name, struct player *playerp)
683 {
684 long loc = 0; /* location in the file */
685
686 fseek(Playersfp, 0L, SEEK_SET);
687 while (fread(playerp, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
688 if (strcmp(playerp->p_name, name) == 0) {
689 if (playerp->p_status != S_NOTUSED || Wizard)
690 /* found it */
691 return (loc);
692 }
693 loc += SZ_PLAYERSTRUCT;
694 }
695
696 return (-1);
697 }
698 /**/
699 /************************************************************************
700 /
701 / FUNCTION NAME: allocrecord()
702 /
703 / FUNCTION: find space in the player file for a new character
704 /
705 / AUTHOR: E. A. Estes, 12/4/85
706 /
707 / ARGUMENTS: none
708 /
709 / RETURN VALUE: location of free space in file
710 /
711 / MODULES CALLED: initplayer(), writerecord(), fread(), fseek()
712 /
713 / GLOBAL INPUTS: Other, *Playersfp
714 /
715 / GLOBAL OUTPUTS: Player
716 /
717 / DESCRIPTION:
718 / Search the player file for an unused entry. If none are found,
719 / make one at the end of the file.
720 /
721 *************************************************************************/
722
723 long
allocrecord(void)724 allocrecord(void)
725 {
726 long loc = 0L; /* location in file */
727
728 fseek(Playersfp, 0L, SEEK_SET);
729 while (fread(&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
730 if (Other.p_status == S_NOTUSED)
731 /* found an empty record */
732 return (loc);
733 else
734 loc += SZ_PLAYERSTRUCT;
735 }
736
737 /* make a new record */
738 initplayer(&Other);
739 Player.p_status = S_OFF;
740 writerecord(&Other, loc);
741
742 return (loc);
743 }
744 /**/
745 /************************************************************************
746 /
747 / FUNCTION NAME: freerecord()
748 /
749 / FUNCTION: free up a record on the player file
750 /
751 / AUTHOR: E. A. Estes, 2/7/86
752 /
753 / ARGUMENTS:
754 / struct player playerp - pointer to structure to free
755 / long loc - location in file to free
756 /
757 / RETURN VALUE: none
758 /
759 / MODULES CALLED: writerecord()
760 /
761 / GLOBAL INPUTS: none
762 /
763 / GLOBAL OUTPUTS: none
764 /
765 / DESCRIPTION:
766 / Mark structure as not used, and update player file.
767 /
768 *************************************************************************/
769
770 void
freerecord(struct player * playerp,long loc)771 freerecord(struct player *playerp, long loc)
772 {
773 playerp->p_name[0] = CH_MARKDELETE;
774 playerp->p_status = S_NOTUSED;
775 writerecord(playerp, loc);
776 }
777 /**/
778 /************************************************************************
779 /
780 / FUNCTION NAME: leavegame()
781 /
782 / FUNCTION: leave game
783 /
784 / AUTHOR: E. A. Estes, 12/4/85
785 /
786 / ARGUMENTS: none
787 /
788 / RETURN VALUE: none
789 /
790 / MODULES CALLED: freerecord(), writerecord(), cleanup()
791 /
792 / GLOBAL INPUTS: Player, Fileloc
793 /
794 / GLOBAL OUTPUTS: Player
795 /
796 / DESCRIPTION:
797 / Mark player as inactive, and cleanup.
798 / Do not save players below level 1.
799 /
800 *************************************************************************/
801
802 void
leavegame(void)803 leavegame(void)
804 {
805
806 if (Player.p_level < 1.0)
807 /* delete character */
808 freerecord(&Player, Fileloc);
809 else {
810 Player.p_status = S_OFF;
811 writerecord(&Player, Fileloc);
812 }
813
814 cleanup(TRUE);
815 }
816 /**/
817 /************************************************************************
818 /
819 / FUNCTION NAME: death()
820 /
821 / FUNCTION: death routine
822 /
823 / AUTHOR: E. A. Estes, 12/4/85
824 /
825 / ARGUMENTS:
826 / char *how - pointer to string describing cause of death
827 /
828 / RETURN VALUE: none
829 /
830 / MODULES CALLED: freerecord(), enterscore(), more(), exit(), fread(),
831 / fseek(), execl(), fopen(), floor(), wmove(), drandom(), wclear(), strcmp(),
832 / fwrite(), fflush(), printw(), strlcpy(), fclose(), waddstr(), cleanup(),
833 / fprintf(), wrefresh(), getanswer(), descrtype()
834 /
835 / GLOBAL INPUTS: Curmonster, Wizard, Player, *stdscr, Fileloc, *Monstfp
836 /
837 / GLOBAL OUTPUTS: Player
838 /
839 / DESCRIPTION:
840 / Kill off current player.
841 / Handle rings, and multiple lives.
842 / Print an appropriate message.
843 / Update scoreboard, lastdead, and let other players know about
844 / the demise of their comrade.
845 /
846 *************************************************************************/
847
848 void
death(char * how)849 death(char *how)
850 {
851 FILE *fp; /* for updating various files */
852 int ch; /* input */
853 static char *deathmesg[] =
854 /* add more messages here, if desired */
855 {
856 "You have been wounded beyond repair. ",
857 "You have been disemboweled. ",
858 "You've been mashed, mauled, and spit upon. (You're dead.)\n",
859 "You died! ",
860 "You're a complete failure -- you've died!!\n",
861 "You have been dealt a fatal blow! "
862 };
863
864 clear();
865
866 if (strcmp(how, "Stupidity") != 0) {
867 if (Player.p_level > 9999.0)
868 /* old age */
869 addstr("Characters must be retired upon reaching level 10000. Sorry.");
870 else if (Player.p_lives > 0) {
871 /* extra lives */
872 addstr("You should be more cautious. You've been killed.\n");
873 printw("You only have %d more chance(s).\n", --Player.p_lives);
874 more(3);
875 Player.p_energy = Player.p_maxenergy;
876 return;
877 } else if (Player.p_specialtype == SC_VALAR) {
878 addstr("You had your chances, but Valar aren't totally\n");
879 addstr("immortal. You are now left to wither and die . . .\n");
880 more(3);
881 Player.p_brains = Player.p_level / 25.0;
882 Player.p_energy = Player.p_maxenergy /= 5.0;
883 Player.p_quksilver = Player.p_sword = 0.0;
884 Player.p_specialtype = SC_COUNCIL;
885 return;
886 } else if (Player.p_ring.ring_inuse &&
887 (Player.p_ring.ring_type == R_DLREG || Player.p_ring.ring_type == R_NAZREG))
888 /* good ring in use - saved from death */
889 {
890 mvaddstr(4, 0, "Your ring saved you from death!\n");
891 refresh();
892 Player.p_ring.ring_type = R_NONE;
893 Player.p_energy = Player.p_maxenergy / 12.0 + 1.0;
894 if (Player.p_crowns > 0)
895 --Player.p_crowns;
896 return;
897 } else if (Player.p_ring.ring_type == R_BAD
898 || Player.p_ring.ring_type == R_SPOILED)
899 /* bad ring in possession; name idiot after player */
900 {
901 mvaddstr(4, 0,
902 "Your ring has taken control of you and turned you into a monster!\n");
903 fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
904 fread(&Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
905 strlcpy(Curmonster.m_name, Player.p_name,
906 sizeof Curmonster.m_name);
907 fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
908 fwrite(&Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
909 fflush(Monstfp);
910 }
911 }
912 enterscore(); /* update score board */
913
914 /* put info in last dead file */
915 fp = fopen(_PATH_LASTDEAD, "w");
916 fprintf(fp, "%s (%s, run by %s, level %.0f, killed by %s)",
917 Player.p_name, descrtype(&Player, TRUE),
918 Player.p_login, Player.p_level, how);
919 fclose(fp);
920
921 /* let other players know */
922 fp = fopen(_PATH_MESS, "w");
923 fprintf(fp, "%s was killed by %s.", Player.p_name, how);
924 fclose(fp);
925
926 freerecord(&Player, Fileloc);
927
928 clear();
929 move(10, 0);
930 addstr(deathmesg[(int) ROLL(0.0, (double) sizeof(deathmesg) / sizeof(char *))]);
931 addstr("Care to give it another try ? ");
932 ch = getanswer("NY", FALSE);
933
934 if (ch == 'Y') {
935 cleanup(FALSE);
936 execl(_PATH_GAMEPROG, "phantasia", "-s",
937 (Wizard ? "-S" : (char *)NULL), (char *)NULL);
938 exit(0);
939 }
940 cleanup(TRUE);
941 }
942 /**/
943 /************************************************************************
944 /
945 / FUNCTION NAME: writerecord()
946 /
947 / FUNCTION: update structure in player file
948 /
949 / AUTHOR: E. A. Estes, 12/4/85
950 /
951 / ARGUMENTS:
952 / struct player *playerp - pointer to structure to write out
953 / long place - location in file to updata
954 /
955 / RETURN VALUE: none
956 /
957 / MODULES CALLED: fseek(), fwrite(), fflush()
958 /
959 / GLOBAL INPUTS: *Playersfp
960 /
961 / GLOBAL OUTPUTS: none
962 /
963 / DESCRIPTION:
964 / Update location in player file with given structure.
965 /
966 *************************************************************************/
967
968 void
writerecord(struct player * playerp,long place)969 writerecord(struct player *playerp, long place)
970 {
971 fseek(Playersfp, place, SEEK_SET);
972 fwrite(playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
973 fflush(Playersfp);
974 }
975 /**/
976 /************************************************************************
977 /
978 / FUNCTION NAME: explevel()
979 /
980 / FUNCTION: calculate level based upon experience
981 /
982 / AUTHOR: E. A. Estes, 12/4/85
983 /
984 / ARGUMENTS:
985 / double experience - experience to calculate experience level from
986 /
987 / RETURN VALUE: experience level
988 /
989 / MODULES CALLED: pow(), floor()
990 /
991 / GLOBAL INPUTS: none
992 /
993 / GLOBAL OUTPUTS: none
994 /
995 / DESCRIPTION:
996 / Experience level is a geometric progression. This has been finely
997 / tuned over the years, and probably should not be changed.
998 /
999 *************************************************************************/
1000
1001 double
explevel(double experience)1002 explevel(double experience)
1003 {
1004 if (experience < 1.1e7)
1005 return (floor(pow((experience / 1000.0), 0.4875)));
1006 else
1007 return (floor(pow((experience / 1250.0), 0.4865)));
1008 }
1009 /**/
1010 /************************************************************************
1011 /
1012 / FUNCTION NAME: truncstring()
1013 /
1014 / FUNCTION: truncate trailing blanks off a string
1015 /
1016 / AUTHOR: E. A. Estes, 12/4/85
1017 /
1018 / ARGUMENTS:
1019 / char *string - pointer to null terminated string
1020 /
1021 / RETURN VALUE: none
1022 /
1023 / MODULES CALLED: strlen()
1024 /
1025 / GLOBAL INPUTS: none
1026 /
1027 / GLOBAL OUTPUTS: none
1028 /
1029 / DESCRIPTION:
1030 / Put nul characters in place of spaces at the end of the string.
1031 /
1032 *************************************************************************/
1033
1034 void
truncstring(char * string)1035 truncstring(char *string)
1036 {
1037 int length; /* length of string */
1038
1039 length = strlen(string);
1040 while (string[--length] == ' ')
1041 string[length] = '\0';
1042 }
1043 /**/
1044 /************************************************************************
1045 /
1046 / FUNCTION NAME: altercoordinates()
1047 /
1048 / FUNCTION: Alter x, y coordinates and set/check location flags
1049 /
1050 / AUTHOR: E. A. Estes, 12/16/85
1051 /
1052 / ARGUMENTS:
1053 / double xnew, ynew - new x, y coordinates
1054 / int operation - operation to perform with coordinates
1055 /
1056 / RETURN VALUE: none
1057 /
1058 / MODULES CALLED: fabs(), floor(), drandom(), distance()
1059 /
1060 / GLOBAL INPUTS: Circle, Beyond, Player
1061 /
1062 / GLOBAL OUTPUTS: Marsh, Circle, Beyond, Throne, Player, Changed
1063 /
1064 / DESCRIPTION:
1065 / This module is called whenever the player's coordinates are altered.
1066 / If the player is beyond the point of no return, he/she is forced
1067 / to stay there.
1068 /
1069 *************************************************************************/
1070
1071 void
altercoordinates(double xnew,double ynew,int operation)1072 altercoordinates(double xnew, double ynew, int operation)
1073 {
1074 switch (operation) {
1075 case A_FORCED: /* move with no checks */
1076 break;
1077
1078 case A_NEAR: /* pick random coordinates near */
1079 xnew = Player.p_x + ROLL(1.0, 5.0);
1080 ynew = Player.p_y - ROLL(1.0, 5.0);
1081 /* fall through for check */
1082
1083 case A_SPECIFIC: /* just move player */
1084 if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND)
1085 /*
1086 * cannot move back from point of no return
1087 * pick the largest coordinate to remain unchanged
1088 */
1089 {
1090 if (fabs(xnew) > fabs(ynew))
1091 xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND);
1092 else
1093 ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND);
1094 }
1095 break;
1096
1097 case A_FAR: /* pick random coordinates far */
1098 xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle);
1099 ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle);
1100 break;
1101 }
1102
1103 /* now set location flags and adjust coordinates */
1104 Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew));
1105
1106 /* set up flags based upon location */
1107 Throne = Marsh = Beyond = FALSE;
1108
1109 if (Player.p_x == 0.0 && Player.p_y == 0.0)
1110 Throne = TRUE;
1111 else if (Circle < 35 && Circle >= 20)
1112 Marsh = TRUE;
1113 else if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND)
1114 Beyond = TRUE;
1115
1116 Changed = TRUE;
1117 }
1118 /**/
1119 /************************************************************************
1120 /
1121 / FUNCTION NAME: readrecord()
1122 /
1123 / FUNCTION: read a player structure from file
1124 /
1125 / AUTHOR: E. A. Estes, 12/4/85
1126 /
1127 / ARGUMENTS:
1128 / struct player *playerp - pointer to structure to fill
1129 / int loc - location of record to read
1130 /
1131 / RETURN VALUE: none
1132 /
1133 / MODULES CALLED: fread(), fseek()
1134 /
1135 / GLOBAL INPUTS: *Playersfp
1136 /
1137 / GLOBAL OUTPUTS: none
1138 /
1139 / DESCRIPTION:
1140 / Read structure information from player file.
1141 /
1142 *************************************************************************/
1143
1144 void
readrecord(struct player * playerp,long loc)1145 readrecord(struct player *playerp, long loc)
1146 {
1147 fseek(Playersfp, loc, SEEK_SET);
1148 fread(playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
1149 }
1150 /**/
1151 /************************************************************************
1152 /
1153 / FUNCTION NAME: adjuststats()
1154 /
1155 / FUNCTION: adjust player statistics
1156 /
1157 / AUTHOR: E. A. Estes, 12/4/85
1158 /
1159 / ARGUMENTS: none
1160 /
1161 / RETURN VALUE: none
1162 /
1163 / MODULES CALLED: death(), floor(), drandom(), explevel(), movelevel()
1164 /
1165 / GLOBAL INPUTS: Player, *Statptr
1166 /
1167 / GLOBAL OUTPUTS: Circle, Player, Timeout
1168 /
1169 / DESCRIPTION:
1170 / Handle adjustment and maximums on various player characteristics.
1171 /
1172 *************************************************************************/
1173
1174 void
adjuststats(void)1175 adjuststats(void)
1176 {
1177 double dtemp; /* for temporary calculations */
1178
1179 if (explevel(Player.p_experience) > Player.p_level)
1180 /* move one or more levels */
1181 {
1182 movelevel();
1183 if (Player.p_level > 5.0)
1184 Timeout = TRUE;
1185 }
1186 if (Player.p_specialtype == SC_VALAR)
1187 /* valar */
1188 Circle = Player.p_level / 5.0;
1189
1190 /* calculate effective quickness */
1191 dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote
1192 - Player.p_level;
1193 dtemp = MAX(0.0, dtemp);/* gold slows player down */
1194 Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp;
1195
1196 /* calculate effective strength */
1197 if (Player.p_poison > 0.0)
1198 /* poison makes player weaker */
1199 {
1200 dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0;
1201 dtemp = MAX(0.1, dtemp);
1202 } else
1203 dtemp = 1.0;
1204 Player.p_might = dtemp * Player.p_strength + Player.p_sword;
1205
1206 /* insure that important things are within limits */
1207 Player.p_quksilver = MIN(99.0, Player.p_quksilver);
1208 Player.p_mana = MIN(Player.p_mana,
1209 Player.p_level * Statptr->c_maxmana + 1000.0);
1210 Player.p_brains = MIN(Player.p_brains,
1211 Player.p_level * Statptr->c_maxbrains + 200.0);
1212 Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0);
1213
1214 /*
1215 * some implementations have problems with floating point compare
1216 * we work around it with this stuff
1217 */
1218 Player.p_gold = floor(Player.p_gold) + 0.1;
1219 Player.p_gems = floor(Player.p_gems) + 0.1;
1220 Player.p_mana = floor(Player.p_mana) + 0.1;
1221
1222 if (Player.p_ring.ring_type != R_NONE)
1223 /* do ring things */
1224 {
1225 /* rest to max */
1226 Player.p_energy = Player.p_maxenergy + Player.p_shield;
1227
1228 if (Player.p_ring.ring_duration <= 0)
1229 /* clean up expired rings */
1230 switch (Player.p_ring.ring_type) {
1231 case R_BAD: /* ring drives player crazy */
1232 Player.p_ring.ring_type = R_SPOILED;
1233 Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0);
1234 break;
1235
1236 case R_NAZREG: /* ring disappears */
1237 Player.p_ring.ring_type = R_NONE;
1238 break;
1239
1240 case R_SPOILED: /* ring kills player */
1241 death("A cursed ring");
1242 break;
1243
1244 case R_DLREG: /* this ring doesn't expire */
1245 Player.p_ring.ring_duration = 0;
1246 break;
1247 }
1248 }
1249 if (Player.p_age / N_AGE > Player.p_degenerated)
1250 /* age player slightly */
1251 {
1252 ++Player.p_degenerated;
1253 if (Player.p_quickness > 23.0)
1254 Player.p_quickness *= 0.99;
1255 Player.p_strength *= 0.97;
1256 Player.p_brains *= 0.95;
1257 Player.p_magiclvl *= 0.97;
1258 Player.p_maxenergy *= 0.95;
1259 Player.p_quksilver *= 0.95;
1260 Player.p_sword *= 0.93;
1261 Player.p_shield *= 0.93;
1262 }
1263 }
1264 /**/
1265 /************************************************************************
1266 /
1267 / FUNCTION NAME: initplayer()
1268 /
1269 / FUNCTION: initialize a character
1270 /
1271 / AUTHOR: E. A. Estes, 12/4/85
1272 /
1273 / ARGUMENTS:
1274 / struct player *playerp - pointer to structure to init
1275 /
1276 / RETURN VALUE: none
1277 /
1278 / MODULES CALLED: floor(), drandom()
1279 /
1280 / GLOBAL INPUTS: none
1281 /
1282 / GLOBAL OUTPUTS: none
1283 /
1284 / DESCRIPTION:
1285 / Put a bunch of default values in the given structure.
1286 /
1287 *************************************************************************/
1288
1289 void
initplayer(struct player * playerp)1290 initplayer(struct player *playerp)
1291 {
1292 playerp->p_experience =
1293 playerp->p_level =
1294 playerp->p_strength =
1295 playerp->p_sword =
1296 playerp->p_might =
1297 playerp->p_energy =
1298 playerp->p_maxenergy =
1299 playerp->p_shield =
1300 playerp->p_quickness =
1301 playerp->p_quksilver =
1302 playerp->p_speed =
1303 playerp->p_magiclvl =
1304 playerp->p_mana =
1305 playerp->p_brains =
1306 playerp->p_poison =
1307 playerp->p_gems =
1308 playerp->p_sin =
1309 playerp->p_1scratch =
1310 playerp->p_2scratch = 0.0;
1311
1312 playerp->p_gold = ROLL(50.0, 75.0) + 0.1; /* give some gold */
1313
1314 playerp->p_x = ROLL(-125.0, 251.0);
1315 playerp->p_y = ROLL(-125.0, 251.0); /* give random x, y */
1316
1317 /* clear ring */
1318 playerp->p_ring.ring_type = R_NONE;
1319 playerp->p_ring.ring_duration = 0;
1320 playerp->p_ring.ring_inuse = FALSE;
1321
1322 playerp->p_age = 0L;
1323
1324 playerp->p_degenerated = 1; /* don't degenerate initially */
1325
1326 playerp->p_type = C_FIGHTER; /* default */
1327 playerp->p_specialtype = SC_NONE;
1328 playerp->p_lives =
1329 playerp->p_crowns =
1330 playerp->p_charms =
1331 playerp->p_amulets =
1332 playerp->p_holywater =
1333 playerp->p_lastused = 0;
1334 playerp->p_status = S_NOTUSED;
1335 playerp->p_tampered = T_OFF;
1336 playerp->p_istat = I_OFF;
1337
1338 playerp->p_palantir =
1339 playerp->p_blessing =
1340 playerp->p_virgin =
1341 playerp->p_blindness = FALSE;
1342
1343 playerp->p_name[0] =
1344 playerp->p_password[0] =
1345 playerp->p_login[0] = '\0';
1346 }
1347 /**/
1348 /************************************************************************
1349 /
1350 / FUNCTION NAME: readmessage()
1351 /
1352 / FUNCTION: read message from other players
1353 /
1354 / AUTHOR: E. A. Estes, 12/4/85
1355 /
1356 / ARGUMENTS: none
1357 /
1358 / RETURN VALUE: none
1359 /
1360 / MODULES CALLED: fseek(), fgets(), wmove(), waddstr(), wclrtoeol()
1361 /
1362 / GLOBAL INPUTS: *stdscr, Databuf[], *Messagefp
1363 /
1364 / GLOBAL OUTPUTS: none
1365 /
1366 / DESCRIPTION:
1367 / If there is a message from other players, print it.
1368 /
1369 *************************************************************************/
1370
1371 void
readmessage(void)1372 readmessage(void)
1373 {
1374 move(3, 0);
1375 clrtoeol();
1376 fseek(Messagefp, 0L, SEEK_SET);
1377 if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL)
1378 addstr(Databuf);
1379 }
1380 /**/
1381 /************************************************************************
1382 /
1383 / FUNCTION NAME: error()
1384 /
1385 / FUNCTION: process environment error
1386 /
1387 / AUTHOR: E. A. Estes, 12/4/85
1388 /
1389 / ARGUMENTS:
1390 / char *whichfile - pointer to name of file which caused error
1391 /
1392 / RETURN VALUE: none
1393 /
1394 / MODULES CALLED: wclear(), cleanup()
1395 /
1396 / GLOBAL INPUTS: errno, *stdscr, printf(), Windows
1397 /
1398 / GLOBAL OUTPUTS: none
1399 /
1400 / DESCRIPTION:
1401 / Print message about offending file, and exit.
1402 /
1403 *************************************************************************/
1404
1405 __dead void
error(char * whichfile)1406 error(char *whichfile)
1407 {
1408
1409 if (Windows)
1410 clear();
1411 cleanup(FALSE);
1412
1413 warn("%s", whichfile);
1414 fprintf(stderr, "Please run 'setup' to determine the problem.\n");
1415 exit(1);
1416 }
1417 /**/
1418 /************************************************************************
1419 /
1420 / FUNCTION NAME: distance()
1421 /
1422 / FUNCTION: calculate distance between two points
1423 /
1424 / AUTHOR: E. A. Estes, 12/4/85
1425 /
1426 / ARGUMENTS:
1427 / double x1, y1 - x, y coordinates of first point
1428 / double x2, y2 - x, y coordinates of second point
1429 /
1430 / RETURN VALUE: distance between the two points
1431 /
1432 / MODULES CALLED: sqrt()
1433 /
1434 / GLOBAL INPUTS: none
1435 /
1436 / GLOBAL OUTPUTS: none
1437 /
1438 / DESCRIPTION:
1439 / This function is provided because someone's hypot() library function
1440 / fails if x1 == x2 && y1 == y2.
1441 /
1442 *************************************************************************/
1443
1444 double
distance(double x1,double x2,double y1,double y2)1445 distance(double x1, double x2, double y1, double y2)
1446 {
1447 double deltax, deltay;
1448
1449 deltax = x1 - x2;
1450 deltay = y1 - y2;
1451 return (sqrt(deltax * deltax + deltay * deltay));
1452 }
1453 /************************************************************************
1454 /
1455 / FUNCTION NAME: descrstatus()
1456 /
1457 / FUNCTION: return a string describing the player status
1458 /
1459 / AUTHOR: E. A. Estes, 3/3/86
1460 /
1461 / ARGUMENTS:
1462 / struct player playerp - pointer to player structure to describe
1463 /
1464 / RETURN VALUE: string describing player's status
1465 /
1466 / MODULES CALLED: none
1467 /
1468 / GLOBAL INPUTS: none
1469 /
1470 / GLOBAL OUTPUTS: none
1471 /
1472 / DESCRIPTION:
1473 / Return verbal description of player status.
1474 / If player status is S_PLAYING, check for low energy and blindness.
1475 /
1476 *************************************************************************/
1477
1478 char *
descrstatus(struct player * playerp)1479 descrstatus(struct player *playerp)
1480 {
1481 switch (playerp->p_status) {
1482 case S_PLAYING:
1483 if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield))
1484 return ("Low Energy");
1485 else if (playerp->p_blindness)
1486 return ("Blind");
1487 else
1488 return ("In game");
1489
1490 case S_CLOAKED:
1491 return ("Cloaked");
1492
1493 case S_INBATTLE:
1494 return ("In Battle");
1495
1496 case S_MONSTER:
1497 return ("Encounter");
1498
1499 case S_TRADING:
1500 return ("Trading");
1501
1502 case S_OFF:
1503 return ("Off");
1504
1505 case S_HUNGUP:
1506 return ("Hung up");
1507
1508 default:
1509 return ("");
1510 }
1511 }
1512 /**/
1513 /************************************************************************
1514 /
1515 / FUNCTION NAME: drandom()
1516 /
1517 / FUNCTION: return a random floating point number from 0.0 < 1.0
1518 /
1519 / AUTHOR: E. A. Estes, 2/7/86
1520 /
1521 / ARGUMENTS: none
1522 /
1523 / RETURN VALUE: none
1524 /
1525 / MODULES CALLED: arc4random()
1526 /
1527 / GLOBAL INPUTS: none
1528 /
1529 / GLOBAL OUTPUTS: none
1530 /
1531 / DESCRIPTION:
1532 / Convert random integer from library routine into a floating
1533 / point number, and divide by the largest possible random number.
1534 /
1535 *************************************************************************/
1536
1537 double
drandom(void)1538 drandom(void)
1539 {
1540 return ((double) arc4random() / (UINT32_MAX + 1.0));
1541 }
1542 /**/
1543 /************************************************************************
1544 /
1545 / FUNCTION NAME: collecttaxes()
1546 /
1547 / FUNCTION: collect taxes from current player
1548 /
1549 / AUTHOR: E. A. Estes, 2/7/86
1550 /
1551 / ARGUMENTS:
1552 / double gold - amount of gold to tax
1553 / double gems - amount of gems to tax
1554 /
1555 / RETURN VALUE: none
1556 /
1557 / MODULES CALLED: fread(), fseek(), fopen(), floor(), fwrite(), fclose()
1558 /
1559 / GLOBAL INPUTS: Player
1560 /
1561 / GLOBAL OUTPUTS: Player
1562 /
1563 / DESCRIPTION:
1564 / Pay taxes on gold and gems. If the player does not have enough
1565 / gold to pay taxes on the added gems, convert some gems to gold.
1566 / Add taxes to tax data base; add remaining gold and gems to
1567 / player's cache.
1568 /
1569 *************************************************************************/
1570
1571 void
collecttaxes(double gold,double gems)1572 collecttaxes(double gold, double gems)
1573 {
1574 FILE *fp; /* to update Goldfile */
1575 double dtemp; /* for temporary calculations */
1576 double taxes; /* tax liability */
1577
1578 /* add to cache */
1579 Player.p_gold += gold;
1580 Player.p_gems += gems;
1581
1582 /* calculate tax liability */
1583 taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold);
1584
1585 if (Player.p_gold < taxes)
1586 /* not enough gold to pay taxes, must convert some gems to
1587 * gold */
1588 {
1589 dtemp = floor(taxes / N_GEMVALUE + 1.0); /* number of gems to
1590 * convert */
1591
1592 if (Player.p_gems >= dtemp)
1593 /* player has enough to convert */
1594 {
1595 Player.p_gems -= dtemp;
1596 Player.p_gold += dtemp * N_GEMVALUE;
1597 } else
1598 /* take everything; this should never happen */
1599 {
1600 Player.p_gold += Player.p_gems * N_GEMVALUE;
1601 Player.p_gems = 0.0;
1602 taxes = Player.p_gold;
1603 }
1604 }
1605 Player.p_gold -= taxes;
1606
1607 if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
1608 /* update taxes */
1609 {
1610 dtemp = 0.0;
1611 fread(&dtemp, sizeof(double), 1, fp);
1612 dtemp += floor(taxes);
1613 fseek(fp, 0L, SEEK_SET);
1614 fwrite(&dtemp, sizeof(double), 1, fp);
1615 fclose(fp);
1616 }
1617 }
1618