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