]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - phantasia/misc.c
s/saftey/safety/
[bsdgames-darwin.git] / phantasia / misc.c
1 /* $NetBSD: misc.c,v 1.22 2019/02/03 03:19:25 mrg Exp $ */
2
3 /*
4 * misc.c Phantasia miscellaneous support routines
5 */
6
7 #include <errno.h>
8 #include <math.h>
9 #include <setjmp.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #include "macros.h"
17 #include "phantdefs.h"
18 #include "phantstruct.h"
19 #include "phantglobs.h"
20 #include "pathnames.h"
21
22 #undef bool
23 #include <curses.h>
24
25
26 static double explevel(double);
27
28 static void
29 movelevel(void)
30 {
31 const struct charstats *statptr; /* for pointing into Stattable */
32 double new; /* new level */
33 double inc; /* increment between new and old levels */
34
35 Changed = TRUE;
36
37 if (Player.p_type == C_EXPER)
38 /* roll a type to use for increment */
39 statptr = &Stattable[(int) ROLL(C_MAGIC, C_HALFLING - C_MAGIC + 1)];
40 else
41 statptr = Statptr;
42
43 new = explevel(Player.p_experience);
44 inc = new - Player.p_level;
45 Player.p_level = new;
46
47 /* add increments to statistics */
48 Player.p_strength += statptr->c_strength.increase * inc;
49 Player.p_mana += statptr->c_mana.increase * inc;
50 Player.p_brains += statptr->c_brains.increase * inc;
51 Player.p_magiclvl += statptr->c_magiclvl.increase * inc;
52 Player.p_maxenergy += statptr->c_energy.increase * inc;
53
54 /* rest to maximum upon reaching new level */
55 Player.p_energy = Player.p_maxenergy + Player.p_shield;
56
57 if (Player.p_crowns > 0 && Player.p_level >= 1000.0)
58 /* no longer able to be king -- turn crowns into cash */
59 {
60 Player.p_gold += ((double) Player.p_crowns) * 5000.0;
61 Player.p_crowns = 0;
62 }
63 if (Player.p_level >= 3000.0 && Player.p_specialtype < SC_COUNCIL)
64 /* make a member of the council */
65 {
66 mvaddstr(6, 0, "You have made it to the Council of the Wise.\n");
67 addstr("Good Luck on your search for the Holy Grail.\n");
68
69 Player.p_specialtype = SC_COUNCIL;
70
71 /* no rings for council and above */
72 Player.p_ring.ring_type = R_NONE;
73 Player.p_ring.ring_duration = 0;
74
75 Player.p_lives = 3; /* three extra lives */
76 }
77 if (Player.p_level > 9999.0 && Player.p_specialtype != SC_VALAR)
78 death("Old age");
79 }
80
81 const char *
82 descrlocation(struct player *playerp, phbool shortflag)
83 {
84 double circle; /* corresponding circle for coordinates */
85 int quadrant; /* quandrant of grid */
86 const char *label; /* pointer to place name */
87 static const char *const nametable[4][4] = /* names of places */
88 {
89 {"Anorien", "Ithilien", "Rohan", "Lorien"},
90 {"Gondor", "Mordor", "Dunland", "Rovanion"},
91 {"South Gondor", "Khand", "Eriador", "The Iron Hills"},
92 {"Far Harad", "Near Harad", "The Northern Waste", "Rhun"}
93 };
94
95 if (playerp->p_specialtype == SC_VALAR)
96 return (" is in Valhala");
97 else
98 if ((circle = CIRCLE(playerp->p_x, playerp->p_y)) >= 1000.0) {
99 if (MAX(fabs(playerp->p_x), fabs(playerp->p_y)) > D_BEYOND)
100 label = "The Point of No Return";
101 else
102 label = "The Ashen Mountains";
103 } else
104 if (circle >= 55)
105 label = "Morannon";
106 else
107 if (circle >= 35)
108 label = "Kennaquahair";
109 else
110 if (circle >= 20)
111 label = "The Dead Marshes";
112 else
113 if (circle >= 9)
114 label = "The Outer Waste";
115 else
116 if (circle >= 5)
117 label = "The Moors Adventurous";
118 else {
119 if (playerp->p_x == 0.0 && playerp->p_y == 0.0)
120 label = "The Lord's Chamber";
121 else {
122 /* this
123 *
124 * expr
125 * essi
126 * on
127 * is
128 * spli
129 * t
130 * to
131 * prev
132 * ent
133 * comp
134 * iler
135 *
136 * loop
137 *
138 * with
139 *
140 * some
141 *
142 * comp
143 * iler
144 * s */
145 quadrant = ((playerp->p_x > 0.0) ? 1 : 0);
146 quadrant += ((playerp->p_y >= 0.0) ? 2 : 0);
147 label = nametable[((int) circle) - 1][quadrant];
148 }
149 }
150
151 if (shortflag)
152 snprintf(Databuf, SZ_DATABUF, "%.29s", label);
153 else
154 snprintf(Databuf, SZ_DATABUF,
155 " is in %s (%.0f,%.0f)",
156 label, playerp->p_x, playerp->p_y);
157
158 return (Databuf);
159 }
160
161 void
162 tradingpost(void)
163 {
164 double numitems; /* number of items to purchase */
165 double cost; /* cost of purchase */
166 double blessingcost; /* cost of blessing */
167 int ch; /* input */
168 int size; /* size of the trading post */
169 int loop; /* loop counter */
170 int cheat = 0; /* number of times player has tried to cheat */
171 bool dishonest = FALSE; /* set when merchant is dishonest */
172
173 Player.p_status = S_TRADING;
174 writerecord(&Player, Fileloc);
175
176 clear();
177 addstr("You are at a trading post. All purchases must be made with gold.");
178
179 size = sqrt(fabs(Player.p_x / 100)) + 1;
180 size = MIN(7, size);
181
182 /* set up cost of blessing */
183 blessingcost = 1000.0 * (Player.p_level + 5.0);
184
185 /* print Menu */
186 move(7, 0);
187 for (loop = 0; loop < size; ++loop)
188 /* print Menu */
189 {
190 if (loop == 6)
191 cost = blessingcost;
192 else
193 cost = Menu[loop].cost;
194 printw("(%d) %-12s: %6.0f\n", loop + 1, Menu[loop].item, cost);
195 }
196
197 mvprintw(5, 0, "L:Leave P:Purchase S:Sell Gems ? ");
198
199 for (;;) {
200 adjuststats(); /* truncate any bad values */
201
202 /* print some important statistics */
203 mvprintw(1, 0, "Gold: %9.0f Gems: %9.0f Level: %6.0f Charms: %6d\n",
204 Player.p_gold, Player.p_gems, Player.p_level, Player.p_charms);
205 printw("Shield: %9.0f Sword: %9.0f Quicksilver:%3.0f Blessed: %s\n",
206 Player.p_shield, Player.p_sword, Player.p_quksilver,
207 (Player.p_blessing ? " True" : "False"));
208 printw("Brains: %9.0f Mana: %9.0f", Player.p_brains, Player.p_mana);
209
210 move(5, 36);
211 ch = getanswer("LPS", FALSE);
212 move(15, 0);
213 clrtobot();
214 switch (ch) {
215 case 'L': /* leave */
216 case '\n':
217 altercoordinates(0.0, 0.0, A_NEAR);
218 return;
219
220 case 'P': /* make purchase */
221 mvaddstr(15, 0, "What what would you like to buy ? ");
222 ch = getanswer(" 1234567", FALSE);
223 move(15, 0);
224 clrtoeol();
225
226 if (ch - '0' > size)
227 addstr("Sorry, this merchant doesn't have that.");
228 else
229 switch (ch) {
230 case '1':
231 printw("Mana is one per %.0f gold piece. How many do you want (%.0f max) ? ",
232 Menu[0].cost, floor(Player.p_gold / Menu[0].cost));
233 cost = (numitems = floor(infloat())) * Menu[0].cost;
234
235 if (cost > Player.p_gold || numitems < 0)
236 ++cheat;
237 else {
238 cheat = 0;
239 Player.p_gold -= cost;
240 if (drandom() < 0.02)
241 dishonest = TRUE;
242 else
243 Player.p_mana += numitems;
244 }
245 break;
246
247 case '2':
248 printw("Shields are %.0f per +1. How many do you want (%.0f max) ? ",
249 Menu[1].cost, floor(Player.p_gold / Menu[1].cost));
250 cost = (numitems = floor(infloat())) * Menu[1].cost;
251
252 if (numitems == 0.0)
253 break;
254 else
255 if (cost > Player.p_gold || numitems < 0)
256 ++cheat;
257 else
258 if (numitems < Player.p_shield)
259 NOBETTER();
260 else {
261 cheat = 0;
262 Player.p_gold -= cost;
263 if (drandom() < 0.02)
264 dishonest = TRUE;
265 else
266 Player.p_shield = numitems;
267 }
268 break;
269
270 case '3':
271 printw("A book costs %.0f gp. How many do you want (%.0f max) ? ",
272 Menu[2].cost, floor(Player.p_gold / Menu[2].cost));
273 cost = (numitems = floor(infloat())) * Menu[2].cost;
274
275 if (cost > Player.p_gold || numitems < 0)
276 ++cheat;
277 else {
278 cheat = 0;
279 Player.p_gold -= cost;
280 if (drandom() < 0.02)
281 dishonest = TRUE;
282 else
283 if (drandom() * numitems > Player.p_level / 10.0
284 && numitems != 1) {
285 printw("\nYou blew your mind!\n");
286 Player.p_brains /= 5;
287 } else {
288 Player.p_brains += floor(numitems) * ROLL(20, 8);
289 }
290 }
291 break;
292
293 case '4':
294 printw("Swords are %.0f gp per +1. How many + do you want (%.0f max) ? ",
295 Menu[3].cost, floor(Player.p_gold / Menu[3].cost));
296 cost = (numitems = floor(infloat())) * Menu[3].cost;
297
298 if (numitems == 0.0)
299 break;
300 else
301 if (cost > Player.p_gold || numitems < 0)
302 ++cheat;
303 else
304 if (numitems < Player.p_sword)
305 NOBETTER();
306 else {
307 cheat = 0;
308 Player.p_gold -= cost;
309 if (drandom() < 0.02)
310 dishonest = TRUE;
311 else
312 Player.p_sword = numitems;
313 }
314 break;
315
316 case '5':
317 printw("A charm costs %.0f gp. How many do you want (%.0f max) ? ",
318 Menu[4].cost, floor(Player.p_gold / Menu[4].cost));
319 cost = (numitems = floor(infloat())) * Menu[4].cost;
320
321 if (cost > Player.p_gold || numitems < 0)
322 ++cheat;
323 else {
324 cheat = 0;
325 Player.p_gold -= cost;
326 if (drandom() < 0.02)
327 dishonest = TRUE;
328 else
329 Player.p_charms += numitems;
330 }
331 break;
332
333 case '6':
334 printw("Quicksilver is %.0f gp per +1. How many + do you want (%.0f max) ? ",
335 Menu[5].cost, floor(Player.p_gold / Menu[5].cost));
336 cost = (numitems = floor(infloat())) * Menu[5].cost;
337
338 if (numitems == 0.0)
339 break;
340 else
341 if (cost > Player.p_gold || numitems < 0)
342 ++cheat;
343 else
344 if (numitems < Player.p_quksilver)
345 NOBETTER();
346 else {
347 cheat = 0;
348 Player.p_gold -= cost;
349 if (drandom() < 0.02)
350 dishonest = TRUE;
351 else
352 Player.p_quksilver = numitems;
353 }
354 break;
355
356 case '7':
357 if (Player.p_blessing) {
358 addstr("You already have a blessing.");
359 break;
360 }
361 printw("A blessing requires a %.0f gp donation. Still want one ? ", blessingcost);
362 ch = getanswer("NY", FALSE);
363
364 if (ch == 'Y') {
365 if (Player.p_gold < blessingcost)
366 ++cheat;
367 else {
368 cheat = 0;
369 Player.p_gold -= blessingcost;
370 if (drandom() < 0.02)
371 dishonest = TRUE;
372 else
373 Player.p_blessing = TRUE;
374 }
375 }
376 break;
377 }
378 break;
379
380 case 'S': /* sell gems */
381 mvprintw(15, 0, "A gem is worth %.0f gp. How many do you want to sell (%.0f max) ? ",
382 (double) N_GEMVALUE, Player.p_gems);
383 numitems = floor(infloat());
384
385 if (numitems > Player.p_gems || numitems < 0)
386 ++cheat;
387 else {
388 cheat = 0;
389 Player.p_gems -= numitems;
390 Player.p_gold += numitems * N_GEMVALUE;
391 }
392 }
393
394 if (cheat == 1)
395 mvaddstr(17, 0, "Come on, merchants aren't stupid. Stop cheating.\n");
396 else
397 if (cheat == 2) {
398 mvaddstr(17, 0, "You had your chance. This merchant happens to be\n");
399 printw("a %.0f level magic user, and you made %s mad!\n",
400 ROLL(Circle * 20.0, 40.0), (drandom() < 0.5) ? "him" : "her");
401 altercoordinates(0.0, 0.0, A_FAR);
402 Player.p_energy /= 2.0;
403 ++Player.p_sin;
404 more(23);
405 return;
406 } else
407 if (dishonest) {
408 mvaddstr(17, 0, "The merchant stole your money!");
409 refresh();
410 altercoordinates(Player.p_x - Player.p_x / 10.0,
411 Player.p_y - Player.p_y / 10.0, A_SPECIFIC);
412 sleep(2);
413 return;
414 }
415 }
416 }
417
418 void
419 displaystats(void)
420 {
421 mvprintw(0, 0, "%s%s\n", Player.p_name, descrlocation(&Player, FALSE));
422 mvprintw(1, 0, "Level :%7.0f Energy :%9.0f(%9.0f) Mana :%9.0f Users:%3d\n",
423 Player.p_level, Player.p_energy, Player.p_maxenergy + Player.p_shield,
424 Player.p_mana, Users);
425 mvprintw(2, 0, "Quick :%3.0f(%3.0f) Strength:%9.0f(%9.0f) Gold :%9.0f %s\n",
426 Player.p_speed, Player.p_quickness + Player.p_quksilver, Player.p_might,
427 Player.p_strength + Player.p_sword, Player.p_gold, descrstatus(&Player));
428 }
429
430 void
431 allstatslist(void)
432 {
433 static const char *const flags[] = /* to print value of some bools */
434 {
435 "False",
436 " True"
437 };
438
439 mvprintw(8, 0, "Type: %s\n", descrtype(&Player, FALSE));
440
441 mvprintw(10, 0, "Experience: %9.0f", Player.p_experience);
442 mvprintw(11, 0, "Brains : %9.0f", Player.p_brains);
443 mvprintw(12, 0, "Magic Lvl : %9.0f", Player.p_magiclvl);
444 mvprintw(13, 0, "Sin : %9.5f", Player.p_sin);
445 mvprintw(14, 0, "Poison : %9.5f", Player.p_poison);
446 mvprintw(15, 0, "Gems : %9.0f", Player.p_gems);
447 mvprintw(16, 0, "Age : %9ld", Player.p_age);
448 mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater);
449 mvprintw(11, 40, "Amulets : %9d", Player.p_amulets);
450 mvprintw(12, 40, "Charms : %9d", Player.p_charms);
451 mvprintw(13, 40, "Crowns : %9d", Player.p_crowns);
452 mvprintw(14, 40, "Shield : %9.0f", Player.p_shield);
453 mvprintw(15, 40, "Sword : %9.0f", Player.p_sword);
454 mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver);
455
456 mvprintw(18, 0, "Blessing: %s Ring: %s Virgin: %s Palantir: %s",
457 flags[(int)Player.p_blessing],
458 flags[Player.p_ring.ring_type != R_NONE],
459 flags[(int)Player.p_virgin],
460 flags[(int)Player.p_palantir]);
461 }
462
463 const char *
464 descrtype(struct player *playerp, phbool shortflag)
465 {
466 int type; /* for caluculating result subscript */
467 static const char *const results[] =/* description table */
468 {
469 " Magic User", " MU",
470 " Fighter", " F ",
471 " Elf", " E ",
472 " Dwarf", " D ",
473 " Halfling", " H ",
474 " Experimento", " EX",
475 " Super", " S ",
476 " King", " K ",
477 " Council of Wise", " CW",
478 " Ex-Valar", " EV",
479 " Valar", " V ",
480 " ? ", " ? "
481 };
482
483 type = playerp->p_type;
484
485 switch (playerp->p_specialtype) {
486 case SC_NONE:
487 type = playerp->p_type;
488 break;
489
490 case SC_KING:
491 type = 7;
492 break;
493
494 case SC_COUNCIL:
495 type = 8;
496 break;
497
498 case SC_EXVALAR:
499 type = 9;
500 break;
501
502 case SC_VALAR:
503 type = 10;
504 break;
505 }
506
507 type *= 2; /* calculate offset */
508
509 if (type > 20)
510 /* error */
511 type = 22;
512
513 if (shortflag)
514 /* use short descriptions */
515 ++type;
516
517 if (playerp->p_crowns > 0) {
518 strcpy(Databuf, results[type]);
519 Databuf[0] = '*';
520 return (Databuf);
521 } else
522 return (results[type]);
523 }
524
525 long
526 findname(const char *name, struct player *playerp)
527 {
528 long loc = 0; /* location in the file */
529
530 fseek(Playersfp, 0L, SEEK_SET);
531 while (fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
532 if (strcmp(playerp->p_name, name) == 0) {
533 if (playerp->p_status != S_NOTUSED || Wizard)
534 /* found it */
535 return (loc);
536 }
537 loc += SZ_PLAYERSTRUCT;
538 }
539
540 return (-1);
541 }
542
543 long
544 allocrecord(void)
545 {
546 long loc = 0L; /* location in file */
547
548 fseek(Playersfp, 0L, SEEK_SET);
549 while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
550 if (Other.p_status == S_NOTUSED)
551 /* found an empty record */
552 return (loc);
553 else
554 loc += SZ_PLAYERSTRUCT;
555 }
556
557 /* make a new record */
558 initplayer(&Other);
559 Player.p_status = S_OFF;
560 writerecord(&Other, loc);
561
562 return (loc);
563 }
564
565 void
566 freerecord(struct player *playerp, long loc)
567 {
568 playerp->p_name[0] = CH_MARKDELETE;
569 playerp->p_status = S_NOTUSED;
570 writerecord(playerp, loc);
571 }
572
573 void
574 leavegame(void)
575 {
576
577 if (Player.p_level < 1.0)
578 /* delete character */
579 freerecord(&Player, Fileloc);
580 else {
581 Player.p_status = S_OFF;
582 writerecord(&Player, Fileloc);
583 }
584
585 cleanup(TRUE);
586 /* NOTREACHED */
587 }
588
589 void
590 death(const char *how)
591 {
592 FILE *fp; /* for updating various files */
593 int ch; /* input */
594 static const char *const deathmesg[] =
595 /* add more messages here, if desired */
596 {
597 "You have been wounded beyond repair. ",
598 "You have been disemboweled. ",
599 "You've been mashed, mauled, and spit upon. (You're dead.)\n",
600 "You died! ",
601 "You're a complete failure -- you've died!!\n",
602 "You have been dealt a fatal blow! "
603 };
604
605 clear();
606
607 if (strcmp(how, "Stupidity") != 0) {
608 if (Player.p_level > 9999.0)
609 /* old age */
610 addstr("Characters must be retired upon reaching level 10000. Sorry.");
611 else
612 if (Player.p_lives > 0)
613 /* extra lives */
614 {
615 addstr("You should be more cautious. You've been killed.\n");
616 printw("You only have %d more chance(s).\n", --Player.p_lives);
617 more(3);
618 Player.p_energy = Player.p_maxenergy;
619 return;
620 } else
621 if (Player.p_specialtype == SC_VALAR) {
622 addstr("You had your chances, but Valar aren't totally\n");
623 addstr("immortal. You are now left to wither and die . . .\n");
624 more(3);
625 Player.p_brains = Player.p_level / 25.0;
626 Player.p_energy = Player.p_maxenergy /= 5.0;
627 Player.p_quksilver = Player.p_sword = 0.0;
628 Player.p_specialtype = SC_COUNCIL;
629 return;
630 } else
631 if (Player.p_ring.ring_inuse &&
632 (Player.p_ring.ring_type == R_DLREG || Player.p_ring.ring_type == R_NAZREG))
633 /* good ring in use - saved
634 * from death */
635 {
636 mvaddstr(4, 0, "Your ring saved you from death!\n");
637 refresh();
638 Player.p_ring.ring_type = R_NONE;
639 Player.p_energy = Player.p_maxenergy / 12.0 + 1.0;
640 if (Player.p_crowns > 0)
641 --Player.p_crowns;
642 return;
643 } else
644 if (Player.p_ring.ring_type == R_BAD
645 || Player.p_ring.ring_type == R_SPOILED)
646 /* bad ring in
647 * possession; name
648 * idiot after player */
649 {
650 mvaddstr(4, 0,
651 "Your ring has taken control of you and turned you into a monster!\n");
652 fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
653 fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
654 strcpy(Curmonster.m_name, Player.p_name);
655 fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
656 fwrite((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
657 fflush(Monstfp);
658 }
659 }
660 enterscore(); /* update score board */
661
662 /* put info in last dead file */
663 fp = fopen(_PATH_LASTDEAD, "w");
664 fprintf(fp, "%s (%s, run by %s, level %.0f, killed by %s)",
665 Player.p_name, descrtype(&Player, TRUE),
666 Player.p_login, Player.p_level, how);
667 fclose(fp);
668
669 /* let other players know */
670 fp = fopen(_PATH_MESS, "w");
671 fprintf(fp, "%s was killed by %s.", Player.p_name, how);
672 fclose(fp);
673
674 freerecord(&Player, Fileloc);
675
676 clear();
677 move(10, 0);
678 addstr(deathmesg[(int) ROLL(0.0, (double) sizeof(deathmesg) / sizeof(char *))]);
679 addstr("Care to give it another try ? ");
680 ch = getanswer("NY", FALSE);
681
682 if (ch == 'Y') {
683 cleanup(FALSE);
684 execl(_PATH_GAMEPROG, "phantasia", "-s",
685 (Wizard ? "-S" : (char *) NULL), (char *) NULL);
686 exit(0);
687 /* NOTREACHED */
688 }
689 cleanup(TRUE);
690 /* NOTREACHED */
691 }
692
693 void
694 writerecord(struct player *playerp, long place)
695 {
696 fseek(Playersfp, place, SEEK_SET);
697 fwrite((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
698 fflush(Playersfp);
699 }
700
701 static double
702 explevel(double experience)
703 {
704 if (experience < 1.1e7)
705 return (floor(pow((experience / 1000.0), 0.4875)));
706 else
707 return (floor(pow((experience / 1250.0), 0.4865)));
708 }
709
710 void
711 truncstring(char *string)
712 {
713 int length; /* length of string */
714
715 length = strlen(string);
716 while (string[--length] == ' ')
717 string[length] = '\0';
718 }
719
720 void
721 altercoordinates(double xnew, double ynew, int operation)
722 {
723 switch (operation) {
724 case A_FORCED: /* move with no checks */
725 break;
726
727 case A_NEAR: /* pick random coordinates near */
728 xnew = Player.p_x + ROLL(1.0, 5.0);
729 ynew = Player.p_y - ROLL(1.0, 5.0);
730 /* fall through for check */
731
732 /* FALLTHROUGH */
733 case A_SPECIFIC: /* just move player */
734 if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND)
735 /*
736 * cannot move back from point of no return
737 * pick the largest coordinate to remain unchanged
738 */
739 {
740 if (fabs(xnew) > fabs(ynew))
741 xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND);
742 else
743 ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND);
744 }
745 break;
746
747 case A_FAR: /* pick random coordinates far */
748 xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle);
749 ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle);
750 break;
751 }
752
753 /* now set location flags and adjust coordinates */
754 Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew));
755
756 /* set up flags based upon location */
757 Throne = Marsh = Beyond = FALSE;
758
759 if (Player.p_x == 0.0 && Player.p_y == 0.0)
760 Throne = TRUE;
761 else
762 if (Circle < 35 && Circle >= 20)
763 Marsh = TRUE;
764 else
765 if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND)
766 Beyond = TRUE;
767
768 Changed = TRUE;
769 }
770
771 void
772 readrecord(struct player *playerp, long loc)
773 {
774 fseek(Playersfp, loc, SEEK_SET);
775 fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
776 }
777
778 void
779 adjuststats(void)
780 {
781 double dtemp; /* for temporary calculations */
782
783 if (explevel(Player.p_experience) > Player.p_level)
784 /* move one or more levels */
785 {
786 movelevel();
787 if (Player.p_level > 5.0)
788 Timeout = TRUE;
789 }
790 if (Player.p_specialtype == SC_VALAR)
791 /* valar */
792 Circle = Player.p_level / 5.0;
793
794 /* calculate effective quickness */
795 dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote
796 - Player.p_level;
797 dtemp = MAX(0.0, dtemp);/* gold slows player down */
798 Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp;
799
800 /* calculate effective strength */
801 if (Player.p_poison > 0.0)
802 /* poison makes player weaker */
803 {
804 dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0;
805 dtemp = MAX(0.1, dtemp);
806 } else
807 dtemp = 1.0;
808 Player.p_might = dtemp * Player.p_strength + Player.p_sword;
809
810 /* insure that important things are within limits */
811 Player.p_quksilver = MIN(99.0, Player.p_quksilver);
812 Player.p_mana = MIN(Player.p_mana,
813 Player.p_level * Statptr->c_maxmana + 1000.0);
814 Player.p_brains = MIN(Player.p_brains,
815 Player.p_level * Statptr->c_maxbrains + 200.0);
816 Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0);
817
818 /*
819 * some implementations have problems with floating point compare
820 * we work around it with this stuff
821 */
822 Player.p_gold = floor(Player.p_gold) + 0.1;
823 Player.p_gems = floor(Player.p_gems) + 0.1;
824 Player.p_mana = floor(Player.p_mana) + 0.1;
825
826 if (Player.p_ring.ring_type != R_NONE)
827 /* do ring things */
828 {
829 /* rest to max */
830 Player.p_energy = Player.p_maxenergy + Player.p_shield;
831
832 if (Player.p_ring.ring_duration <= 0)
833 /* clean up expired rings */
834 switch (Player.p_ring.ring_type) {
835 case R_BAD: /* ring drives player crazy */
836 Player.p_ring.ring_type = R_SPOILED;
837 Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0);
838 break;
839
840 case R_NAZREG: /* ring disappears */
841 Player.p_ring.ring_type = R_NONE;
842 break;
843
844 case R_SPOILED: /* ring kills player */
845 death("A cursed ring");
846 break;
847
848 case R_DLREG: /* this ring doesn't expire */
849 Player.p_ring.ring_duration = 0;
850 break;
851 }
852 }
853 if (Player.p_age / N_AGE > Player.p_degenerated)
854 /* age player slightly */
855 {
856 ++Player.p_degenerated;
857 if (Player.p_quickness > 23.0)
858 Player.p_quickness *= 0.99;
859 Player.p_strength *= 0.97;
860 Player.p_brains *= 0.95;
861 Player.p_magiclvl *= 0.97;
862 Player.p_maxenergy *= 0.95;
863 Player.p_quksilver *= 0.95;
864 Player.p_sword *= 0.93;
865 Player.p_shield *= 0.93;
866 }
867 }
868
869 void
870 initplayer(struct player *playerp)
871 {
872 playerp->p_experience =
873 playerp->p_level =
874 playerp->p_strength =
875 playerp->p_sword =
876 playerp->p_might =
877 playerp->p_energy =
878 playerp->p_maxenergy =
879 playerp->p_shield =
880 playerp->p_quickness =
881 playerp->p_quksilver =
882 playerp->p_speed =
883 playerp->p_magiclvl =
884 playerp->p_mana =
885 playerp->p_brains =
886 playerp->p_poison =
887 playerp->p_gems =
888 playerp->p_sin =
889 playerp->p_1scratch =
890 playerp->p_2scratch = 0.0;
891
892 playerp->p_gold = ROLL(50.0, 75.0) + 0.1; /* give some gold */
893
894 playerp->p_x = ROLL(-125.0, 251.0);
895 playerp->p_y = ROLL(-125.0, 251.0); /* give random x, y */
896
897 /* clear ring */
898 playerp->p_ring.ring_type = R_NONE;
899 playerp->p_ring.ring_duration = 0;
900 playerp->p_ring.ring_inuse = FALSE;
901
902 playerp->p_age = 0L;
903
904 playerp->p_degenerated = 1; /* don't degenerate initially */
905
906 playerp->p_type = C_FIGHTER; /* default */
907 playerp->p_specialtype = SC_NONE;
908 playerp->p_lives =
909 playerp->p_crowns =
910 playerp->p_charms =
911 playerp->p_amulets =
912 playerp->p_holywater =
913 playerp->p_lastused = 0;
914 playerp->p_status = S_NOTUSED;
915 playerp->p_tampered = T_OFF;
916 playerp->p_istat = I_OFF;
917
918 playerp->p_palantir =
919 playerp->p_blessing =
920 playerp->p_virgin =
921 playerp->p_blindness = FALSE;
922
923 playerp->p_name[0] =
924 playerp->p_password[0] =
925 playerp->p_login[0] = '\0';
926 }
927
928 void
929 readmessage(void)
930 {
931 move(3, 0);
932 clrtoeol();
933 fseek(Messagefp, 0L, SEEK_SET);
934 if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL)
935 addstr(Databuf);
936 }
937
938 void
939 error(const char *whichfile)
940 {
941 int (*funcp)(const char *,...);
942
943 if (Windows) {
944 funcp = printw;
945 clear();
946 } else
947 funcp = printf;
948
949 (*funcp) ("An unrecoverable error has occurred reading %s. (%s)\n", whichfile, strerror(errno));
950 (*funcp) ("Please run 'setup' to determine the problem.\n");
951 cleanup(TRUE);
952 /* NOTREACHED */
953 }
954
955 double
956 distance(double x_1, double x_2, double y_1, double y_2)
957 {
958 double deltax, deltay;
959
960 deltax = x_1 - x_2;
961 deltay = y_1 - y_2;
962 return (sqrt(deltax * deltax + deltay * deltay));
963 }
964
965 void
966 ill_sig(int whichsig)
967 {
968 clear();
969 if (!(whichsig == SIGINT || whichsig == SIGQUIT))
970 printw("Error: caught signal # %d.\n", whichsig);
971 cleanup(TRUE);
972 /* NOTREACHED */
973 }
974
975 const char *
976 descrstatus(struct player *playerp)
977 {
978 switch (playerp->p_status) {
979 case S_PLAYING:
980 if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield))
981 return ("Low Energy");
982 else
983 if (playerp->p_blindness)
984 return ("Blind");
985 else
986 return ("In game");
987
988 case S_CLOAKED:
989 return ("Cloaked");
990
991 case S_INBATTLE:
992 return ("In Battle");
993
994 case S_MONSTER:
995 return ("Encounter");
996
997 case S_TRADING:
998 return ("Trading");
999
1000 case S_OFF:
1001 return ("Off");
1002
1003 case S_HUNGUP:
1004 return ("Hung up");
1005
1006 default:
1007 return ("");
1008 }
1009 }
1010
1011 double
1012 drandom(void)
1013 {
1014 if (sizeof(int) != 2)
1015 /* use only low bits */
1016 return ((double) (random() & 0x7fff) / 32768.0);
1017 else
1018 return ((double) random() / 32768.0);
1019 }
1020
1021 void
1022 collecttaxes(double gold, double gems)
1023 {
1024 FILE *fp; /* to update Goldfile */
1025 double dtemp; /* for temporary calculations */
1026 double taxes; /* tax liability */
1027
1028 /* add to cache */
1029 Player.p_gold += gold;
1030 Player.p_gems += gems;
1031
1032 /* calculate tax liability */
1033 taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold);
1034
1035 if (Player.p_gold < taxes)
1036 /* not enough gold to pay taxes, must convert some gems to
1037 * gold */
1038 {
1039 dtemp = floor(taxes / N_GEMVALUE + 1.0); /* number of gems to
1040 * convert */
1041
1042 if (Player.p_gems >= dtemp)
1043 /* player has enough to convert */
1044 {
1045 Player.p_gems -= dtemp;
1046 Player.p_gold += dtemp * N_GEMVALUE;
1047 } else
1048 /* take everything; this should never happen */
1049 {
1050 Player.p_gold += Player.p_gems * N_GEMVALUE;
1051 Player.p_gems = 0.0;
1052 taxes = Player.p_gold;
1053 }
1054 }
1055 Player.p_gold -= taxes;
1056
1057 if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
1058 /* update taxes */
1059 {
1060 dtemp = 0.0;
1061 fread((char *) &dtemp, sizeof(double), 1, fp);
1062 dtemp += floor(taxes);
1063 fseek(fp, 0L, SEEK_SET);
1064 fwrite((char *) &dtemp, sizeof(double), 1, fp);
1065 fclose(fp);
1066 }
1067 }