Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* |
2 | * interplayer.c - player to player routines for Phantasia | |
3 | * | |
4 | * $FreeBSD: src/games/phantasia/interplayer.c,v 1.6 1999/11/16 02:57:33 billf Exp $ | |
313fa7d1 | 5 | * $DragonFly: src/games/phantasia/interplayer.c,v 1.3 2005/05/31 00:06:26 swildner Exp $ |
984263bc MD |
6 | */ |
7 | ||
8 | #include <string.h> | |
9 | #include "include.h" | |
10 | ||
313fa7d1 SW |
11 | /* functions which we need to know about */ |
12 | /* fight.c */ | |
13 | extern void encounter(int); | |
14 | /* io.c */ | |
15 | extern int getanswer(const char *, bool); | |
16 | extern void getstring(char *, int); | |
17 | extern double infloat(void); | |
18 | extern int inputoption(void); | |
19 | extern void more(int); | |
20 | /* misc.c */ | |
21 | extern void altercoordinates(double, double, int); | |
22 | extern void collecttaxes(double, double); | |
23 | extern void death(const char *); | |
24 | extern const char *descrlocation(struct player *, bool); | |
25 | extern const char *descrstatus(struct player *); | |
26 | extern const char *descrtype(struct player *, bool); | |
27 | extern void displaystats(void); | |
28 | extern double distance(double, double, double, double); | |
29 | extern long findname(char *, struct player *); | |
30 | extern void readmessage(void); | |
31 | extern void readrecord(struct player *, long); | |
32 | extern void truncstring(char *); | |
33 | extern void writerecord(struct player *, long); | |
34 | /* phantglobs.c */ | |
35 | extern double drandom(void); | |
36 | ||
6693db17 | 37 | size_t allocvoid(void); |
313fa7d1 | 38 | void battleplayer(long); |
6693db17 | 39 | void checkbattle(void); |
313fa7d1 | 40 | void checktampered(void); |
6693db17 SW |
41 | void dotampered(void); |
42 | void myturn(void); | |
313fa7d1 | 43 | void tampered(int, double, double); |
313fa7d1 | 44 | void throneroom(void); |
6693db17 | 45 | void userlist(bool); |
313fa7d1 | 46 | void writevoid(struct energyvoid *, long); |
313fa7d1 | 47 | |
6693db17 SW |
48 | /* |
49 | * FUNCTION: check to see if current player should battle another | |
50 | * | |
51 | * GLOBAL INPUTS: Other, Users, Player, Fileloc, *Playersfp | |
52 | * | |
53 | * GLOBAL OUTPUTS: Users | |
54 | * | |
55 | * DESCRIPTION: | |
56 | * Seach player file for a foe at the same coordinates as the | |
57 | * current player. | |
58 | * Also update user count. | |
59 | */ | |
984263bc | 60 | |
313fa7d1 SW |
61 | void |
62 | checkbattle(void) | |
984263bc | 63 | { |
6693db17 SW |
64 | long foeloc = 0L; /* location in file of person to fight */ |
65 | ||
66 | Users = 0; | |
67 | fseek(Playersfp, 0L, SEEK_SET); | |
68 | ||
69 | while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) { | |
70 | if (Other.p_status != S_OFF | |
71 | && Other.p_status != S_NOTUSED | |
72 | && Other.p_status != S_HUNGUP | |
73 | && (Other.p_status != S_CLOAKED || Other.p_specialtype != SC_VALAR)) { | |
74 | /* player is on and not a cloaked valar */ | |
75 | ++Users; | |
76 | ||
77 | if (Player.p_x == Other.p_x | |
78 | && Player.p_y == Other.p_y | |
79 | /* same coordinates */ | |
80 | && foeloc != Fileloc | |
81 | /* not self */ | |
82 | && Player.p_status == S_PLAYING | |
83 | && (Other.p_status == S_PLAYING || Other.p_status == S_INBATTLE) | |
84 | /* both are playing */ | |
85 | && Other.p_specialtype != SC_VALAR | |
86 | && Player.p_specialtype != SC_VALAR) { | |
87 | /* neither is valar */ | |
88 | battleplayer(foeloc); | |
89 | return; | |
90 | } | |
984263bc | 91 | } |
6693db17 | 92 | foeloc += SZ_PLAYERSTRUCT; |
984263bc MD |
93 | } |
94 | } | |
6693db17 SW |
95 | |
96 | /* | |
97 | * FUNCTION: inter-terminal battle with another player | |
98 | * | |
99 | * ARGUMENTS: | |
100 | * long foeplace - location in player file of person to battle | |
101 | * | |
102 | * GLOBAL INPUTS: Foestrikes, LINES, Lines, Other, Shield, Player, *stdscr, | |
103 | * Fileloc, *Enemyname | |
104 | * | |
105 | * GLOBAL OUTPUTS: Foestrikes, Lines, Shield, Player, Luckout, *Enemyname | |
106 | * | |
107 | * DESCRIPTION: | |
108 | * Inter-terminal battle is a very fragile and slightly klugy thing. | |
109 | * At any time, one player is master and the other is slave. | |
110 | * We pick who is master first by speed and level. After that, | |
111 | * the slave waits for the master to relinquish its turn, and | |
112 | * the slave becomes master, and so on. | |
113 | * | |
114 | * The items in the player structure which control the handshake are: | |
115 | * p_tampered: | |
116 | * master increments this to relinquish control | |
117 | * p_istat: | |
118 | * master sets this to specify particular action | |
119 | * p_1scratch: | |
120 | * set to total damage inflicted so far; changes to indicate action | |
121 | */ | |
984263bc | 122 | |
313fa7d1 SW |
123 | void |
124 | battleplayer(long foeplace) | |
984263bc | 125 | { |
6693db17 SW |
126 | double dtemp; /* for temporary calculations */ |
127 | double oldhits = 0.0; /* previous damage inflicted by foe */ | |
128 | int loop; /* for timing out */ | |
129 | int ch; /* input */ | |
130 | short oldtampered; /* old value of foe's p_tampered */ | |
131 | ||
132 | Lines = 8; | |
133 | Luckout = FALSE; | |
134 | mvaddstr(4, 0, "Preparing for battle!\n"); | |
135 | refresh(); | |
984263bc MD |
136 | |
137 | #ifdef SYS5 | |
6693db17 | 138 | flushinp(); |
984263bc MD |
139 | #endif |
140 | ||
6693db17 SW |
141 | /* set up variables, file, etc. */ |
142 | Player.p_status = S_INBATTLE; | |
143 | Shield = Player.p_energy; | |
984263bc | 144 | |
6693db17 SW |
145 | /* if p_tampered is not 0, someone else may try to change it (king, etc.) */ |
146 | Player.p_tampered = oldtampered = 1; | |
147 | Player.p_1scratch = 0.0; | |
148 | Player.p_istat = I_OFF; | |
984263bc | 149 | |
984263bc | 150 | readrecord(&Other, foeplace); |
6693db17 SW |
151 | if (fabs(Player.p_level - Other.p_level) > 20.0) { |
152 | /* see if players are greatly mismatched */ | |
153 | dtemp = (Player.p_level - Other.p_level) / MAX(Player.p_level, Other.p_level); | |
154 | if (dtemp < -0.5) | |
155 | /* foe outweighs this one */ | |
156 | Player.p_speed *= 2.0; | |
984263bc | 157 | } |
6693db17 | 158 | writerecord(&Player, Fileloc); /* write out all our info */ |
984263bc | 159 | |
6693db17 SW |
160 | if (Player.p_blindness) |
161 | Enemyname = "someone"; | |
162 | else | |
163 | Enemyname = Other.p_name; | |
164 | ||
165 | mvprintw(6, 0, "You have encountered %s Level: %.0f\n", Enemyname, Other.p_level); | |
166 | refresh(); | |
167 | ||
168 | for (loop = 0; Other.p_status != S_INBATTLE && loop < 30; ++loop) { | |
169 | /* wait for foe to respond */ | |
170 | readrecord(&Other, foeplace); | |
171 | sleep(1); | |
984263bc | 172 | } |
6693db17 SW |
173 | |
174 | if (Other.p_status != S_INBATTLE) { | |
175 | /* foe did not respond */ | |
176 | mvprintw(5, 0, "%s is not responding.\n", Enemyname); | |
177 | goto LEAVE; | |
984263bc | 178 | } |
6693db17 | 179 | /* else, we are ready to battle */ |
984263bc | 180 | |
6693db17 SW |
181 | move(4, 0); |
182 | clrtoeol(); | |
984263bc | 183 | |
6693db17 SW |
184 | /* |
185 | * determine who is first master | |
186 | * if neither player is faster, check level | |
187 | * if neither level is greater, battle is not allowed | |
188 | * (this should never happen, but we have to handle it) | |
189 | */ | |
190 | if (Player.p_speed > Other.p_speed) | |
191 | Foestrikes = FALSE; | |
192 | else if (Other.p_speed > Player.p_speed) | |
193 | Foestrikes = TRUE; | |
194 | else if (Player.p_level > Other.p_level) | |
195 | Foestrikes = FALSE; | |
196 | else if (Other.p_level > Player.p_level) | |
197 | Foestrikes = TRUE; | |
198 | else { | |
199 | /* no one is faster */ | |
200 | printw("You can't fight %s yet.", Enemyname); | |
201 | goto LEAVE; | |
202 | } | |
203 | ||
204 | for (;;) { | |
205 | displaystats(); | |
206 | readmessage(); | |
207 | mvprintw(1, 26, "%20.0f", Shield); /* overprint energy */ | |
208 | ||
209 | if (!Foestrikes) | |
210 | /* take action against foe */ | |
211 | myturn(); | |
212 | else { | |
213 | /* wait for foe to take action */ | |
214 | mvaddstr(4, 0, "Waiting...\n"); | |
215 | clrtoeol(); | |
216 | refresh(); | |
217 | ||
218 | for (loop = 0; loop < 20; ++loop) { | |
219 | /* wait for foe to act */ | |
220 | readrecord(&Other, foeplace); | |
221 | if (Other.p_1scratch != oldhits) | |
222 | /* p_1scratch changes to indicate action */ | |
223 | break; | |
224 | else { | |
225 | /* wait and try again */ | |
226 | sleep(1); | |
227 | addch('.'); | |
228 | refresh(); | |
229 | } | |
230 | } | |
231 | ||
232 | if (Other.p_1scratch == oldhits) { | |
233 | /* timeout */ | |
234 | mvaddstr(22, 0, "Timeout: waiting for response. Do you want to wait ? "); | |
235 | ch = getanswer("NY", FALSE); | |
236 | move(22, 0); | |
237 | clrtobot(); | |
238 | if (ch == 'Y') | |
239 | continue; | |
240 | else | |
241 | break; | |
242 | } else { | |
243 | /* foe took action */ | |
244 | switch (Other.p_istat) { | |
245 | case I_RAN: /* foe ran away */ | |
246 | mvprintw(Lines++, 0, "%s ran away!", Enemyname); | |
247 | break; | |
248 | ||
249 | case I_STUCK: /* foe tried to run, but couldn't */ | |
250 | mvprintw(Lines++, 0, "%s tried to run away.", Enemyname); | |
251 | break; | |
252 | ||
253 | case I_BLEWIT: /* foe tried to luckout, but didn't */ | |
254 | mvprintw(Lines++, 0, "%s tried to luckout!", Enemyname); | |
255 | break; | |
256 | ||
257 | default: | |
258 | dtemp = Other.p_1scratch - oldhits; | |
259 | mvprintw(Lines++, 0, "%s hit you %.0f times!", Enemyname, dtemp); | |
260 | Shield -= dtemp; | |
261 | break; | |
262 | } | |
263 | ||
264 | oldhits = Other.p_1scratch; /* keep track of old hits */ | |
265 | ||
266 | if (Other.p_tampered != oldtampered) { | |
267 | /* p_tampered changes to relinquish turn */ | |
268 | oldtampered = Other.p_tampered; | |
269 | Foestrikes = FALSE; | |
270 | } | |
271 | } | |
984263bc MD |
272 | } |
273 | ||
6693db17 SW |
274 | /* decide what happens next */ |
275 | refresh(); | |
276 | if (Lines > LINES - 2) { | |
277 | more(Lines); | |
278 | move(Lines = 8, 0); | |
279 | clrtobot(); | |
984263bc | 280 | } |
984263bc | 281 | |
6693db17 SW |
282 | if (Other.p_istat == I_KILLED || Shield < 0.0) { |
283 | /* we died */ | |
284 | Shield = -2.0; /* insure this value is negative */ | |
984263bc | 285 | break; |
6693db17 | 286 | } |
984263bc | 287 | |
6693db17 SW |
288 | if (Player.p_istat == I_KILLED) { |
289 | /* we killed foe; award treasre */ | |
290 | mvprintw(Lines++, 0, "You killed %s!", Enemyname); | |
291 | Player.p_experience += Other.p_experience; | |
292 | Player.p_crowns += (Player.p_level < 1000.0) ? Other.p_crowns : 0; | |
293 | Player.p_amulets += Other.p_amulets; | |
294 | Player.p_charms += Other.p_charms; | |
295 | collecttaxes(Other.p_gold, Other.p_gems); | |
296 | Player.p_sword = MAX(Player.p_sword, Other.p_sword); | |
297 | Player.p_shield = MAX(Player.p_shield, Other.p_shield); | |
298 | Player.p_quksilver = MAX(Player.p_quksilver, Other.p_quksilver); | |
299 | if (Other.p_virgin && !Player.p_virgin) { | |
300 | mvaddstr(Lines++, 0, "You have rescued a virgin. Will you be honorable ? "); | |
301 | if ((ch = getanswer("YN", FALSE)) == 'Y') | |
302 | Player.p_virgin = TRUE; | |
303 | else { | |
304 | ++Player.p_sin; | |
305 | Player.p_experience += 8000.0; | |
306 | } | |
307 | } | |
308 | sleep(3); /* give other person time to die */ | |
984263bc | 309 | break; |
6693db17 SW |
310 | } else if (Player.p_istat == I_RAN || Other.p_istat == I_RAN) |
311 | /* either player ran away */ | |
984263bc | 312 | break; |
984263bc MD |
313 | } |
314 | ||
315 | LEAVE: | |
6693db17 SW |
316 | /* clean up things and leave */ |
317 | writerecord(&Player, Fileloc); /* update a final time */ | |
318 | altercoordinates(0.0, 0.0, A_NEAR); /* move away from battle site */ | |
319 | Player.p_energy = Shield; /* set energy to actual value */ | |
320 | Player.p_tampered = T_OFF; /* clear p_tampered */ | |
984263bc | 321 | |
6693db17 | 322 | more(Lines); /* pause */ |
984263bc | 323 | |
6693db17 SW |
324 | move(4, 0); |
325 | clrtobot(); /* clear bottom area of screen */ | |
984263bc | 326 | |
6693db17 SW |
327 | if (Player.p_energy < 0.0) |
328 | /* we are dead */ | |
329 | death("Interterminal battle"); | |
984263bc | 330 | } |
6693db17 SW |
331 | |
332 | /* | |
333 | * FUNCTION: process players action against foe in battle | |
334 | * | |
335 | * GLOBAL INPUTS: Lines, Other, Player, *stdscr, Fileloc, Luckout, | |
336 | * *Enemyname | |
337 | * | |
338 | * GLOBAL OUTPUTS: Foestrikes, Lines, Player, Luckout | |
339 | * | |
340 | * DESCRIPTION: | |
341 | * Take action action against foe, and decide who is master | |
342 | * for next iteration. | |
343 | */ | |
984263bc | 344 | |
313fa7d1 SW |
345 | void |
346 | myturn(void) | |
984263bc | 347 | { |
6693db17 SW |
348 | double dtemp; /* for temporary calculations */ |
349 | int ch; /* input */ | |
984263bc | 350 | |
6693db17 SW |
351 | mvaddstr(7, 0, "1:Fight 2:Run Away! 3:Power Blast "); |
352 | if (Luckout) | |
353 | clrtoeol(); | |
354 | else | |
355 | addstr("4:Luckout "); | |
984263bc | 356 | |
6693db17 SW |
357 | ch = inputoption(); |
358 | move(Lines = 8, 0); | |
359 | clrtobot(); | |
984263bc | 360 | |
6693db17 SW |
361 | switch (ch) { |
362 | default: /* fight */ | |
363 | dtemp = ROLL(2.0, Player.p_might); | |
984263bc | 364 | HIT: |
6693db17 SW |
365 | mvprintw(Lines++, 0, "You hit %s %.0f times!", Enemyname, dtemp); |
366 | Player.p_sin += 0.5; | |
367 | Player.p_1scratch += dtemp; | |
368 | Player.p_istat = I_OFF; | |
369 | break; | |
984263bc | 370 | |
6693db17 SW |
371 | case '2': /* run away */ |
372 | /* change this to indicate action */ | |
984263bc | 373 | Player.p_1scratch -= 1.0; |
6693db17 SW |
374 | if (drandom() > 0.25) { |
375 | mvaddstr(Lines++, 0, "You got away!"); | |
376 | Player.p_istat = I_RAN; | |
377 | } else { | |
378 | mvprintw(Lines++, 0, "%s is still after you!", Enemyname); | |
379 | Player.p_istat = I_STUCK; | |
984263bc | 380 | } |
6693db17 SW |
381 | break; |
382 | ||
383 | case '3': /* power blast */ | |
384 | dtemp = MIN(Player.p_mana, Player.p_level * 5.0); | |
385 | Player.p_mana -= dtemp; | |
386 | dtemp *= (drandom() + 0.5) * Player.p_magiclvl * 0.2 + 2.0; | |
387 | mvprintw(Lines++, 0, "You blasted %s !", Enemyname); | |
388 | goto HIT; | |
389 | ||
390 | case '4': /* luckout */ | |
391 | if (Luckout || drandom() > 0.1) { | |
392 | if (Luckout) | |
393 | mvaddstr(Lines++, 0, "You already tried that!"); | |
394 | else { | |
395 | mvaddstr(Lines++, 0, "Not this time . . ."); | |
396 | Luckout = TRUE; | |
397 | } | |
398 | ||
399 | Player.p_1scratch -= 1.0; | |
400 | Player.p_istat = I_BLEWIT; | |
401 | } else { | |
402 | mvaddstr(Lines++, 0, "You just lucked out!"); | |
403 | Player.p_1scratch = Other.p_energy * 1.1; | |
984263bc | 404 | } |
6693db17 | 405 | break; |
984263bc MD |
406 | } |
407 | ||
6693db17 SW |
408 | refresh(); |
409 | Player.p_1scratch = floor(Player.p_1scratch); /* clean up any mess */ | |
410 | ||
411 | if (Player.p_1scratch > Other.p_energy) | |
412 | Player.p_istat = I_KILLED; | |
413 | else if (drandom() * Player.p_speed < drandom() * Other.p_speed) { | |
414 | /* relinquish control */ | |
415 | ++Player.p_tampered; | |
416 | Foestrikes = TRUE; | |
984263bc MD |
417 | } |
418 | ||
6693db17 | 419 | writerecord(&Player, Fileloc); /* let foe know what we did */ |
984263bc | 420 | } |
6693db17 SW |
421 | |
422 | /* | |
423 | * FUNCTION: check if current player has been tampered with | |
424 | * | |
425 | * GLOBAL INPUTS: *Energyvoidfp, Other, Player, Fileloc, Enrgyvoid | |
426 | * | |
427 | * GLOBAL OUTPUTS: Enrgyvoid | |
428 | * | |
429 | * DESCRIPTION: | |
430 | * Check for energy voids, holy grail, and tampering by other | |
431 | * players. | |
432 | */ | |
984263bc | 433 | |
313fa7d1 SW |
434 | void |
435 | checktampered(void) | |
984263bc | 436 | { |
6693db17 SW |
437 | long loc = 0L; /* location in energy void file */ |
438 | ||
439 | /* first check for energy voids */ | |
440 | fseek(Energyvoidfp, 0L, SEEK_SET); | |
441 | while (fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp) == 1) | |
442 | if (Enrgyvoid.ev_active | |
443 | && Enrgyvoid.ev_x == Player.p_x | |
444 | && Enrgyvoid.ev_y == Player.p_y) { | |
445 | /* sitting on one */ | |
446 | if (loc > 0L) { | |
447 | /* not the holy grail; inactivate energy void */ | |
448 | Enrgyvoid.ev_active = FALSE; | |
449 | writevoid(&Enrgyvoid, loc); | |
450 | tampered(T_NRGVOID, 0.0, 0.0); | |
451 | } else if (Player.p_status != S_CLOAKED) | |
452 | /* holy grail */ | |
453 | tampered(T_GRAIL, 0.0, 0.0); | |
454 | break; | |
455 | } else | |
456 | loc += SZ_VOIDSTRUCT; | |
984263bc | 457 | |
6693db17 SW |
458 | /* now check for other things */ |
459 | readrecord(&Other, Fileloc); | |
460 | if (Other.p_tampered != T_OFF) | |
461 | tampered(Other.p_tampered, Other.p_1scratch, Other.p_2scratch); | |
984263bc | 462 | } |
6693db17 SW |
463 | |
464 | /* | |
465 | * FUNCTION: take care of tampering by other players | |
466 | * | |
467 | * ARGUMENTS: | |
468 | * int what - what type of tampering | |
469 | * double arg1, arg2 - rest of tampering info | |
470 | * | |
471 | * GLOBAL INPUTS: Other, Player, *stdscr, Enrgyvoid, *Playersfp | |
472 | * | |
473 | * GLOBAL OUTPUTS: Other, Player, Changed, Enrgyvoid | |
474 | * | |
475 | * DESCRIPTION: | |
476 | * Take care of energy voids, holy grail, decree and intervention | |
477 | * action on current player. | |
478 | */ | |
984263bc | 479 | |
313fa7d1 SW |
480 | void |
481 | tampered(int what, double arg1, double arg2) | |
984263bc | 482 | { |
6693db17 | 483 | long loc; /* location in file of other players */ |
984263bc | 484 | |
6693db17 SW |
485 | Changed = TRUE; |
486 | move(4, 0); | |
984263bc | 487 | |
6693db17 | 488 | Player.p_tampered = T_OFF; /* no longer tampered with */ |
984263bc | 489 | |
6693db17 | 490 | switch (what) { |
984263bc | 491 | case T_NRGVOID: |
6693db17 SW |
492 | addstr("You've hit an energy void !\n"); |
493 | Player.p_mana /= 3.0; | |
494 | Player.p_energy /= 2.0; | |
495 | Player.p_gold = floor(Player.p_gold / 1.25) + 0.1; | |
496 | altercoordinates(0.0, 0.0, A_NEAR); | |
497 | break; | |
984263bc MD |
498 | |
499 | case T_TRANSPORT: | |
6693db17 SW |
500 | addstr("The king transported you ! "); |
501 | if (Player.p_charms > 0) { | |
502 | addstr("But your charm saved you. . .\n"); | |
503 | --Player.p_charms; | |
504 | } else { | |
505 | altercoordinates(0.0, 0.0, A_FAR); | |
506 | addch('\n'); | |
984263bc | 507 | } |
6693db17 | 508 | break; |
984263bc MD |
509 | |
510 | case T_BESTOW: | |
6693db17 SW |
511 | printw("The king has bestowed %.0f gold pieces on you !\n", arg1); |
512 | Player.p_gold += arg1; | |
513 | break; | |
984263bc MD |
514 | |
515 | case T_CURSED: | |
6693db17 SW |
516 | addstr("You've been cursed ! "); |
517 | if (Player.p_blessing) { | |
518 | addstr("But your blessing saved you. . .\n"); | |
519 | Player.p_blessing = FALSE; | |
520 | } else { | |
521 | addch('\n'); | |
522 | Player.p_poison += 2.0; | |
523 | Player.p_energy = 10.0; | |
524 | Player.p_maxenergy *= 0.95; | |
525 | Player.p_status = S_PLAYING; /* no longer cloaked */ | |
984263bc | 526 | } |
6693db17 | 527 | break; |
984263bc MD |
528 | |
529 | case T_VAPORIZED: | |
6693db17 SW |
530 | addstr("You have been vaporized!\n"); |
531 | more(7); | |
532 | death("Vaporization"); | |
533 | break; | |
984263bc MD |
534 | |
535 | case T_MONSTER: | |
6693db17 SW |
536 | addstr("The Valar zapped you with a monster!\n"); |
537 | more(7); | |
538 | encounter((int)arg1); | |
539 | return; | |
984263bc MD |
540 | |
541 | case T_BLESSED: | |
6693db17 SW |
542 | addstr("The Valar has blessed you!\n"); |
543 | Player.p_energy = (Player.p_maxenergy *= 1.05) + Player.p_shield; | |
544 | Player.p_mana += 500.0; | |
545 | Player.p_strength += 0.5; | |
546 | Player.p_brains += 0.5; | |
547 | Player.p_magiclvl += 0.5; | |
548 | Player.p_poison = MIN(0.5, Player.p_poison); | |
549 | break; | |
984263bc MD |
550 | |
551 | case T_RELOCATE: | |
6693db17 SW |
552 | addstr("You've been relocated. . .\n"); |
553 | altercoordinates(arg1, arg2, A_FORCED); | |
554 | break; | |
984263bc MD |
555 | |
556 | case T_HEAL: | |
6693db17 SW |
557 | addstr("You've been healed!\n"); |
558 | Player.p_poison -= 0.25; | |
559 | Player.p_energy = Player.p_maxenergy + Player.p_shield; | |
560 | break; | |
984263bc MD |
561 | |
562 | case T_EXVALAR: | |
6693db17 SW |
563 | addstr("You are no longer Valar!\n"); |
564 | Player.p_specialtype = SC_COUNCIL; | |
565 | break; | |
984263bc MD |
566 | |
567 | case T_GRAIL: | |
6693db17 SW |
568 | addstr("You have found The Holy Grail!!\n"); |
569 | if (Player.p_specialtype < SC_COUNCIL) { | |
570 | /* must be council of wise to behold grail */ | |
571 | addstr("However, you are not experienced enough to behold it.\n"); | |
572 | Player.p_sin *= Player.p_sin; | |
573 | Player.p_mana += 1000; | |
574 | } else if (Player.p_specialtype == SC_VALAR || | |
575 | Player.p_specialtype == SC_EXVALAR) { | |
576 | addstr("You have made it to the position of Valar once already.\n"); | |
577 | addstr("The Grail is of no more use to you now.\n"); | |
578 | } else { | |
579 | addstr("It is now time to see if you are worthy to behold it. . .\n"); | |
580 | refresh(); | |
581 | sleep(4); | |
582 | ||
583 | if (drandom() / 2.0 < Player.p_sin) { | |
584 | addstr("You have failed!\n"); | |
585 | Player.p_strength = | |
586 | Player.p_mana = | |
587 | Player.p_energy = | |
588 | Player.p_maxenergy = | |
589 | Player.p_magiclvl = | |
590 | Player.p_brains = | |
591 | Player.p_experience = | |
592 | Player.p_quickness = 1.0; | |
593 | ||
594 | altercoordinates(1.0, 1.0, A_FORCED); | |
595 | Player.p_level = 0.0; | |
596 | } else { | |
597 | addstr("You made to position of Valar!\n"); | |
598 | Player.p_specialtype = SC_VALAR; | |
599 | Player.p_lives = 5; | |
600 | fseek(Playersfp, 0L, SEEK_SET); | |
601 | loc = 0L; | |
602 | while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) | |
603 | /* search for existing valar */ | |
604 | if (Other.p_specialtype == SC_VALAR && | |
605 | Other.p_status != S_NOTUSED) { | |
606 | /* found old valar */ | |
607 | Other.p_tampered = T_EXVALAR; | |
608 | writerecord(&Other, loc); | |
609 | break; | |
610 | } else | |
611 | loc += SZ_PLAYERSTRUCT; | |
612 | } | |
984263bc MD |
613 | } |
614 | ||
6693db17 SW |
615 | /* move grail to new location */ |
616 | Enrgyvoid.ev_active = TRUE; | |
617 | Enrgyvoid.ev_x = ROLL(-1.0e6, 2.0e6); | |
618 | Enrgyvoid.ev_y = ROLL(-1.0e6, 2.0e6); | |
619 | writevoid(&Enrgyvoid, 0L); | |
620 | break; | |
984263bc | 621 | } |
6693db17 SW |
622 | refresh(); |
623 | sleep(2); | |
984263bc | 624 | } |
6693db17 SW |
625 | |
626 | /* | |
627 | * FUNCTION: print list of players and locations | |
628 | * | |
629 | * ARGUMENTS: | |
630 | * bool ingameflag - set if called while playing | |
631 | * | |
632 | * GLOBAL INPUTS: LINES, Other, Circle, Wizard, Player, *stdscr, *Playersfp | |
633 | * | |
634 | * DESCRIPTION: | |
635 | * We can only see the coordinate of those closer to the origin | |
636 | * from us. | |
637 | * Kings and council of the wise can see and can be seen by everyone. | |
638 | * Palantirs are good for seeing everyone; and the valar can use | |
639 | * one to see through a 'cloak' spell. | |
640 | * The valar has no coordinates, and is completely invisible if | |
641 | * cloaked. | |
642 | */ | |
984263bc | 643 | |
313fa7d1 SW |
644 | void |
645 | userlist(bool ingameflag) | |
984263bc | 646 | { |
6693db17 | 647 | int numusers = 0; /* number of users on file */ |
984263bc | 648 | |
6693db17 SW |
649 | if (ingameflag && Player.p_blindness) { |
650 | mvaddstr(8, 0, "You cannot see anyone.\n"); | |
651 | return; | |
984263bc MD |
652 | } |
653 | ||
6693db17 SW |
654 | fseek(Playersfp, 0L, SEEK_SET); |
655 | mvaddstr(8, 0, | |
656 | "Name X Y Lvl Type Login Status\n"); | |
657 | ||
658 | while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) { | |
659 | if (Other.p_status == S_NOTUSED | |
660 | /* record is unused */ | |
661 | || (Other.p_specialtype == SC_VALAR && Other.p_status == S_CLOAKED)) { | |
662 | /* cloaked valar */ | |
663 | if (!Wizard) | |
664 | /* wizard can see everything on file */ | |
665 | continue; | |
666 | } | |
667 | ||
668 | ++numusers; | |
669 | ||
670 | if (ingameflag && | |
984263bc | 671 | /* must be playing for the rest of these conditions */ |
6693db17 | 672 | (Player.p_specialtype >= SC_KING |
984263bc | 673 | /* kings and higher can see others */ |
6693db17 | 674 | || Other.p_specialtype >= SC_KING |
984263bc | 675 | /* kings and higher can be seen by others */ |
6693db17 | 676 | || Circle >= CIRCLE(Other.p_x, Other.p_y) |
984263bc | 677 | /* those nearer the origin can be seen */ |
6693db17 | 678 | || Player.p_palantir) |
984263bc | 679 | /* palantir enables one to see others */ |
6693db17 SW |
680 | && (Other.p_status != S_CLOAKED |
681 | || (Player.p_specialtype == SC_VALAR && Player.p_palantir)) | |
984263bc | 682 | /* not cloaked; valar can see through cloak with a palantir */ |
6693db17 SW |
683 | && Other.p_specialtype != SC_VALAR) |
684 | /* not a valar */ | |
685 | /* coordinates should be printed */ | |
686 | printw("%-20s %8.0f %8.0f ", | |
687 | Other.p_name, Other.p_x, Other.p_y); | |
688 | else | |
689 | /* cannot see player's coordinates */ | |
690 | printw("%-20s %19.19s ", | |
691 | Other.p_name, descrlocation(&Other, TRUE)); | |
692 | ||
693 | printw("%6.0f %s %-9.9s%s\n", Other.p_level, descrtype(&Other, TRUE), | |
694 | Other.p_login, descrstatus(&Other)); | |
695 | ||
696 | if ((numusers % (LINES - 10)) == 0) { | |
697 | more(LINES - 1); | |
698 | move(9, 0); | |
699 | clrtobot(); | |
700 | } | |
984263bc MD |
701 | } |
702 | ||
6693db17 SW |
703 | printw("Total players on file = %d\n", numusers); |
704 | refresh(); | |
984263bc | 705 | } |
6693db17 SW |
706 | |
707 | /* | |
708 | * FUNCTION: king stuff upon entering throne | |
709 | * | |
710 | * GLOBAL INPUTS: *Energyvoidfp, Other, Player, *stdscr, | |
711 | * Enrgyvoid, *Playersfp | |
712 | * | |
713 | * GLOBAL OUTPUTS: Other, Player, Changed | |
714 | * | |
715 | * DESCRIPTION: | |
716 | * If player is not already king, make him/her so if the old king | |
717 | * is not playing. | |
718 | * Clear energy voids with new king. | |
719 | * Print 'decree' prompt. | |
720 | */ | |
984263bc | 721 | |
313fa7d1 SW |
722 | void |
723 | throneroom(void) | |
984263bc | 724 | { |
6693db17 SW |
725 | FILE *fp; /* to clear energy voids */ |
726 | long loc = 0L; /* location of old king in player file */ | |
727 | ||
728 | if (Player.p_specialtype < SC_KING) { | |
729 | /* not already king -- assumes crown */ | |
730 | fseek(Playersfp, 0L, SEEK_SET); | |
731 | while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) | |
732 | if (Other.p_specialtype == SC_KING && Other.p_status != S_NOTUSED) { | |
733 | /* found old king */ | |
734 | if (Other.p_status != S_OFF) { | |
735 | /* old king is playing */ | |
736 | mvaddstr(4, 0, "The king is playing, so you cannot steal his throne\n"); | |
737 | altercoordinates(0.0, 0.0, A_NEAR); | |
738 | move(6, 0); | |
739 | return; | |
740 | } else { | |
741 | /* old king is not playing - remove him/her */ | |
742 | Other.p_specialtype = SC_NONE; | |
743 | if (Other.p_crowns) | |
744 | --Other.p_crowns; | |
745 | writerecord(&Other, loc); | |
746 | break; | |
747 | } | |
748 | } else | |
749 | loc += SZ_PLAYERSTRUCT; | |
750 | ||
751 | /* make player new king */ | |
752 | Changed = TRUE; | |
753 | Player.p_specialtype = SC_KING; | |
754 | mvaddstr(4, 0, "You have become king!\n"); | |
755 | ||
756 | /* let everyone else know */ | |
757 | fp = fopen(_PATH_MESS, "w"); | |
758 | fprintf(fp, "All hail the new king!"); | |
759 | fclose(fp); | |
760 | ||
761 | /* clear all energy voids; retain location of holy grail */ | |
762 | fseek(Energyvoidfp, 0L, SEEK_SET); | |
763 | fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp); | |
764 | fp = fopen(_PATH_VOID, "w"); | |
765 | fwrite((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, fp); | |
766 | fclose(fp); | |
984263bc MD |
767 | } |
768 | ||
6693db17 | 769 | mvaddstr(6, 0, "0:Decree "); |
984263bc | 770 | } |
6693db17 SW |
771 | |
772 | /* | |
773 | * FUNCTION: king and valar special options | |
774 | * | |
775 | * GLOBAL INPUTS: *Energyvoidfp, Other, Illcmd[], Wizard, Player, *stdscr, | |
776 | * Databuf[], Enrgyvoid | |
777 | * | |
778 | * GLOBAL OUTPUTS: Other, Player, Enrgyvoid | |
779 | * | |
780 | * DESCRIPTION: | |
781 | * Tamper with other players. Handle king/valar specific options. | |
782 | */ | |
984263bc | 783 | |
313fa7d1 SW |
784 | void |
785 | dotampered(void) | |
984263bc | 786 | { |
6693db17 SW |
787 | short tamper; /* value for tampering with other players */ |
788 | const char *option; /* pointer to option description */ | |
789 | double temp1 = 0.0, temp2 = 0.0; /* other tampering values */ | |
790 | int ch; /* input */ | |
791 | long loc; /* location in energy void file */ | |
792 | FILE *fp; /* for opening gold file */ | |
793 | ||
984263bc MD |
794 | move(6, 0); |
795 | clrtoeol(); | |
6693db17 SW |
796 | if (Player.p_specialtype < SC_COUNCIL && !Wizard) { |
797 | /* king options */ | |
798 | addstr("1:Transport 2:Curse 3:Energy Void 4:Bestow 5:Collect Taxes "); | |
799 | ||
800 | ch = getanswer(" ", TRUE); | |
801 | move(6, 0); | |
802 | clrtoeol(); | |
803 | move(4, 0); | |
804 | switch (ch) { | |
805 | case '1': /* transport someone */ | |
806 | tamper = T_TRANSPORT; | |
807 | option = "transport"; | |
808 | break; | |
984263bc | 809 | |
6693db17 SW |
810 | case '2': /* curse another */ |
811 | tamper = T_CURSED; | |
812 | option = "curse"; | |
813 | break; | |
984263bc | 814 | |
6693db17 SW |
815 | case '3': /* create energy void */ |
816 | if ((loc = allocvoid()) > 20L * (long)SZ_VOIDSTRUCT) | |
817 | /* can only have 20 void active at once */ | |
818 | mvaddstr(5, 0, "Sorry, void creation limit reached.\n"); | |
819 | else { | |
820 | addstr("Enter the X Y coordinates of void ? "); | |
821 | getstring(Databuf, SZ_DATABUF); | |
822 | sscanf(Databuf, "%lf %lf", &temp1, &temp2); | |
823 | Enrgyvoid.ev_x = floor(temp1); | |
824 | Enrgyvoid.ev_y = floor(temp2); | |
825 | Enrgyvoid.ev_active = TRUE; | |
826 | writevoid(&Enrgyvoid, loc); | |
827 | mvaddstr(5, 0, "It is done.\n"); | |
828 | } | |
829 | return; | |
830 | ||
831 | case '4': /* bestow gold to subject */ | |
832 | tamper = T_BESTOW; | |
833 | addstr("How much gold to bestow ? "); | |
834 | temp1 = infloat(); | |
835 | if (temp1 > Player.p_gold || temp1 < 0) { | |
836 | mvaddstr(5, 0, "You don't have that !\n"); | |
837 | return; | |
838 | } | |
839 | ||
840 | /* adjust gold after we are sure it will be given to someone */ | |
841 | option = "give gold to"; | |
842 | break; | |
984263bc | 843 | |
6693db17 SW |
844 | case '5': /* collect accumulated taxes */ |
845 | if ((fp = fopen(_PATH_GOLD, "r+")) != NULL) { | |
846 | /* collect taxes */ | |
847 | fread((char *)&temp1, sizeof(double), 1, fp); | |
848 | fseek(fp, 0L, SEEK_SET); | |
849 | /* clear out value */ | |
850 | temp2 = 0.0; | |
851 | fwrite((char *)&temp2, sizeof(double), 1, fp); | |
852 | fclose(fp); | |
853 | } | |
854 | ||
855 | mvprintw(4, 0, "You have collected %.0f in gold.\n", temp1); | |
856 | Player.p_gold += floor(temp1); | |
857 | return; | |
858 | ||
859 | default: | |
860 | return; | |
984263bc | 861 | } |
6693db17 SW |
862 | /* end of king options */ |
863 | } else { | |
864 | /* council of wise, valar, wizard options */ | |
865 | addstr("1:Heal "); | |
866 | if (Player.p_palantir || Wizard) | |
867 | addstr("2:Seek Grail "); | |
868 | if (Player.p_specialtype == SC_VALAR || Wizard) | |
869 | addstr("3:Throw Monster 4:Relocate 5:Bless "); | |
870 | if (Wizard) | |
871 | addstr("6:Vaporize "); | |
872 | ||
873 | ch = getanswer(" ", TRUE); | |
874 | if (!Wizard) { | |
875 | if (ch > '2' && Player.p_specialtype != SC_VALAR) { | |
876 | ILLCMD(); | |
877 | return; | |
878 | } | |
879 | ||
880 | if (Player.p_mana < MM_INTERVENE) { | |
881 | mvaddstr(5, 0, "No mana left.\n"); | |
882 | return; | |
883 | } else | |
884 | Player.p_mana -= MM_INTERVENE; | |
984263bc | 885 | } |
984263bc | 886 | |
6693db17 SW |
887 | switch (ch) { |
888 | case '1': /* heal another */ | |
889 | tamper = T_HEAL; | |
890 | option = "heal"; | |
891 | break; | |
984263bc | 892 | |
6693db17 SW |
893 | case '2': /* seek grail */ |
894 | if (Player.p_palantir) { | |
895 | /* need a palantir to seek */ | |
896 | fseek(Energyvoidfp, 0L, SEEK_SET); | |
897 | fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp); | |
898 | temp1 = distance(Player.p_x, Enrgyvoid.ev_x, Player.p_y, Enrgyvoid.ev_y); | |
899 | temp1 += ROLL(-temp1 / 10.0, temp1 / 5.0); /* add some error */ | |
900 | mvprintw(5, 0, "The palantir says the Grail is about %.0f away.\n", temp1); | |
901 | } else | |
902 | /* no palantir */ | |
903 | mvaddstr(5, 0, "You need a palantir to seek the Grail.\n"); | |
904 | return; | |
905 | ||
906 | case '3': /* lob monster at someone */ | |
907 | mvaddstr(4, 0, "Which monster [0-99] ? "); | |
908 | temp1 = infloat(); | |
909 | temp1 = MAX(0.0, MIN(99.0, temp1)); | |
910 | tamper = T_MONSTER; | |
911 | option = "throw a monster at"; | |
912 | break; | |
984263bc | 913 | |
6693db17 SW |
914 | case '4': /* move another player */ |
915 | mvaddstr(4, 0, "New X Y coordinates ? "); | |
916 | getstring(Databuf, SZ_DATABUF); | |
917 | sscanf(Databuf, "%lf %lf", &temp1, &temp2); | |
918 | tamper = T_RELOCATE; | |
919 | option = "relocate"; | |
920 | break; | |
984263bc | 921 | |
6693db17 SW |
922 | case '5': /* bless a player */ |
923 | tamper = T_BLESSED; | |
924 | option = "bless"; | |
925 | break; | |
984263bc | 926 | |
6693db17 SW |
927 | case '6': /* kill off a player */ |
928 | if (Wizard) { | |
929 | tamper = T_VAPORIZED; | |
930 | option = "vaporize"; | |
931 | break; | |
932 | } else | |
933 | return; | |
984263bc | 934 | |
6693db17 SW |
935 | default: |
936 | return; | |
937 | } | |
984263bc | 938 | |
6693db17 SW |
939 | /* adjust age after we are sure intervention will be done */ |
940 | /* end of valar, etc. options */ | |
984263bc MD |
941 | } |
942 | ||
6693db17 SW |
943 | for (;;) { |
944 | /* prompt for player to affect */ | |
945 | mvprintw(4, 0, "Who do you want to %s ? ", option); | |
946 | getstring(Databuf, SZ_DATABUF); | |
947 | truncstring(Databuf); | |
984263bc | 948 | |
6693db17 SW |
949 | if (Databuf[0] == '\0') |
950 | userlist(TRUE); | |
984263bc | 951 | else |
6693db17 | 952 | break; |
984263bc | 953 | } |
6693db17 SW |
954 | |
955 | if (strcmp(Player.p_name, Databuf) != 0) { | |
956 | /* name other than self */ | |
957 | if ((loc = findname(Databuf, &Other)) >= 0L) { | |
958 | if (Other.p_tampered != T_OFF) { | |
959 | mvaddstr(5, 0, "That person has something pending already.\n"); | |
960 | return; | |
961 | } else { | |
962 | if (tamper == T_RELOCATE | |
963 | && CIRCLE(temp1, temp2) < CIRCLE(Other.p_x, Other.p_y) | |
964 | && !Wizard) | |
965 | mvaddstr(5, 0, "Cannot move someone closer to the Lord's Chamber.\n"); | |
966 | else { | |
967 | if (tamper == T_BESTOW) | |
968 | Player.p_gold -= floor(temp1); | |
969 | if (!Wizard && (tamper == T_HEAL || tamper == T_MONSTER || | |
970 | tamper == T_RELOCATE || tamper == T_BLESSED)) | |
971 | Player.p_age += N_AGE; /* age penalty */ | |
972 | Other.p_tampered = tamper; | |
973 | Other.p_1scratch = floor(temp1); | |
974 | Other.p_2scratch = floor(temp2); | |
975 | writerecord(&Other, loc); | |
976 | mvaddstr(5, 0, "It is done.\n"); | |
977 | } | |
978 | return; | |
979 | } | |
980 | } else | |
981 | /* player not found */ | |
982 | mvaddstr(5, 0, "There is no one by that name.\n"); | |
983 | } else | |
984 | /* self */ | |
985 | mvaddstr(5, 0, "You may not do it to yourself!\n"); | |
984263bc | 986 | } |
6693db17 SW |
987 | |
988 | /* | |
989 | * FUNCTION: update energy void entry in energy void file | |
990 | * | |
991 | * ARGUMENTS: | |
992 | * struct energyvoid *vp - pointer to structure to write to file | |
993 | * long loc - location in file to update | |
994 | * | |
995 | * GLOBAL INPUTS: *Energyvoidfp | |
996 | * | |
997 | * DESCRIPTION: | |
998 | * Write out energy void structure at specified location. | |
999 | */ | |
984263bc | 1000 | |
313fa7d1 SW |
1001 | void |
1002 | writevoid(struct energyvoid *vp, long loc) | |
984263bc | 1003 | { |
6693db17 SW |
1004 | fseek(Energyvoidfp, loc, SEEK_SET); |
1005 | fwrite((char *)vp, SZ_VOIDSTRUCT, 1, Energyvoidfp); | |
1006 | fflush(Energyvoidfp); | |
1007 | fseek(Energyvoidfp, 0L, SEEK_SET); | |
984263bc | 1008 | } |
6693db17 SW |
1009 | |
1010 | /* | |
1011 | * FUNCTION: allocate space for a new energy void | |
1012 | * | |
1013 | * RETURN VALUE: location of new energy void space | |
1014 | * | |
1015 | * GLOBAL INPUTS: *Energyvoidfp, Enrgyvoid | |
1016 | * | |
1017 | * DESCRIPTION: | |
1018 | * Search energy void file for an inactive entry and return its | |
1019 | * location. | |
1020 | * If no inactive ones are found, return one more than last location. | |
1021 | */ | |
984263bc | 1022 | |
313fa7d1 SW |
1023 | size_t |
1024 | allocvoid(void) | |
984263bc | 1025 | { |
6693db17 | 1026 | size_t loc = 0; /* location of new energy void */ |
984263bc | 1027 | |
6693db17 SW |
1028 | fseek(Energyvoidfp, 0L, SEEK_SET); |
1029 | while (fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp) == 1) | |
1030 | if (Enrgyvoid.ev_active) | |
1031 | loc += SZ_VOIDSTRUCT; | |
1032 | else | |
1033 | break; | |
984263bc | 1034 | |
6693db17 | 1035 | return (loc); |
984263bc | 1036 | } |