Mercurial > hg > early-roguelike
comparison arogue7/util.c @ 125:adfa37e67084
Import Advanced Rogue 7.7 from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Fri, 08 May 2015 15:24:40 -0400 |
parents | |
children | b786053d2f37 |
comparison
equal
deleted
inserted
replaced
124:d10fc4a065ac | 125:adfa37e67084 |
---|---|
1 /* | |
2 * util.c - all sorts of miscellaneous routines | |
3 * | |
4 * Advanced Rogue | |
5 * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T | |
6 * All rights reserved. | |
7 * | |
8 * Based on "Rogue: Exploring the Dungeons of Doom" | |
9 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman | |
10 * All rights reserved. | |
11 * | |
12 * See the file LICENSE.TXT for full copyright and licensing information. | |
13 */ | |
14 | |
15 #include "curses.h" | |
16 #include "rogue.h" | |
17 #include <ctype.h> | |
18 #ifdef PC7300 | |
19 #include <track.h> | |
20 #include <kcodes.h> | |
21 #include <sys/window.h> | |
22 extern struct uwdata wdata; | |
23 #endif | |
24 | |
25 /* | |
26 * all sorts of miscellaneous routines | |
27 * | |
28 */ | |
29 | |
30 | |
31 | |
32 | |
33 /* | |
34 * this routine computes the players current AC without dex bonus's | |
35 */ | |
36 int | |
37 ac_compute(ignoremetal) | |
38 bool ignoremetal; | |
39 { | |
40 register int ac; | |
41 | |
42 ac = pstats.s_arm; /* base armor of "skin" */ | |
43 if (cur_armor) { | |
44 if (!ignoremetal || | |
45 (cur_armor->o_which != LEATHER && | |
46 cur_armor->o_which != STUDDED_LEATHER && | |
47 cur_armor->o_which != PADDED_ARMOR)) | |
48 ac -= (10 - cur_armor->o_ac); | |
49 } | |
50 if (player.t_ctype == C_MONK) | |
51 ac -= pstats.s_lvl * 2 / 3; | |
52 ac -= ring_value(R_PROTECT); | |
53 if (cur_misc[WEAR_BRACERS] != NULL) | |
54 ac -= cur_misc[WEAR_BRACERS]->o_ac; | |
55 if (cur_misc[WEAR_CLOAK] != NULL) | |
56 ac -= cur_misc[WEAR_CLOAK]->o_ac; | |
57 | |
58 /* If player has the cloak, must be wearing it */ | |
59 if (cur_relic[EMORI_CLOAK]) ac -= 5; | |
60 | |
61 if (ac > 10) | |
62 ac = 10; | |
63 return(ac); | |
64 } | |
65 | |
66 /* | |
67 * aggravate: | |
68 * aggravate all the monsters on this level | |
69 */ | |
70 | |
71 aggravate(do_uniques, do_good) | |
72 bool do_uniques, do_good; | |
73 { | |
74 register struct linked_list *mi; | |
75 register struct thing *thingptr; | |
76 | |
77 for (mi = mlist; mi != NULL; mi = next(mi)) { | |
78 thingptr = THINGPTR(mi); | |
79 if (do_good == FALSE && off(*thingptr, ISMEAN)) continue; | |
80 if (do_uniques || off(*thingptr, ISUNIQUE)) runto(thingptr, &hero); | |
81 } | |
82 } | |
83 | |
84 /* | |
85 * cansee: | |
86 * returns true if the hero can see a certain coordinate. | |
87 */ | |
88 | |
89 cansee(y, x) | |
90 register int y, x; | |
91 { | |
92 register struct room *rer; | |
93 register int radius; | |
94 coord tp; | |
95 | |
96 if (on(player, ISBLIND)) | |
97 return FALSE; | |
98 | |
99 tp.y = y; | |
100 tp.x = x; | |
101 rer = roomin(&tp); | |
102 | |
103 /* How far can we see? */ | |
104 if (levtype == OUTSIDE) { | |
105 if (daytime) radius = 36; | |
106 else if (lit_room(rer)) radius = 9; | |
107 else radius = 3; | |
108 } | |
109 else radius = 3; | |
110 | |
111 /* | |
112 * We can only see if the hero in the same room as | |
113 * the coordinate and the room is lit or if it is close. | |
114 */ | |
115 return ((rer != NULL && | |
116 levtype != OUTSIDE && | |
117 (levtype != MAZELEV || /* Maze level needs direct line */ | |
118 maze_view(tp.y, tp.x)) && | |
119 rer == roomin(&hero) && | |
120 lit_room(rer)) || | |
121 DISTANCE(y, x, hero.y, hero.x) < radius); | |
122 } | |
123 | |
124 /* | |
125 * check_level: | |
126 * Check to see if the guy has gone up a level. | |
127 * | |
128 * Return points needed to obtain next level. | |
129 * | |
130 * These are certain beginning experience levels for all players. | |
131 * All further experience levels are computed by muliplying by 2 | |
132 * up through MAXDOUBLE. Then the cap is added in to compute | |
133 * further levels | |
134 */ | |
135 long | |
136 check_level() | |
137 { | |
138 register int i, j, add = 0; | |
139 register unsigned long exp; | |
140 long retval; /* Return value */ | |
141 int nsides; | |
142 | |
143 pstats.s_lvl -= pstats.s_lvladj; /* correct for level adjustment */ | |
144 /* See if we are past the doubling stage */ | |
145 exp = char_class[player.t_ctype].cap; | |
146 if (pstats.s_exp >= exp) { | |
147 i = pstats.s_exp/exp; /* First get amount above doubling area */ | |
148 retval = exp + i * exp; /* Compute next higher boundary */ | |
149 i += MAXDOUBLE; /* Add in the previous doubled levels */ | |
150 } | |
151 else { | |
152 i = 0; | |
153 exp = char_class[player.t_ctype].start_exp; | |
154 while (exp <= pstats.s_exp) { | |
155 i++; | |
156 exp <<= 1; | |
157 } | |
158 retval = exp; | |
159 } | |
160 if (++i > pstats.s_lvl) { | |
161 nsides = char_class[player.t_ctype].hit_pts; | |
162 for (j=0; j<(i-pstats.s_lvl); j++) /* Take care of multi-level jumps */ | |
163 add += max(1, roll(1,nsides) + const_bonus()); | |
164 max_stats.s_hpt += add; | |
165 if ((pstats.s_hpt += add) > max_stats.s_hpt) | |
166 pstats.s_hpt = max_stats.s_hpt; | |
167 msg("Welcome, %s, to level %d", | |
168 cnames[player.t_ctype][min(i-1, NUM_CNAMES-1)], i); | |
169 } | |
170 pstats.s_lvl = i; | |
171 pstats.s_lvl += pstats.s_lvladj; /* correct for level adjustment */ | |
172 return(retval); | |
173 } | |
174 | |
175 /* | |
176 * Used to modify the players strength | |
177 * it keeps track of the highest it has been, just in case | |
178 */ | |
179 | |
180 chg_str(amt) | |
181 register int amt; | |
182 { | |
183 register int ring_str; /* ring strengths */ | |
184 register struct stats *ptr; /* for speed */ | |
185 | |
186 ptr = &pstats; | |
187 ring_str = ring_value(R_ADDSTR); | |
188 ptr->s_str -= ring_str; | |
189 ptr->s_str += amt; | |
190 if (ptr->s_str > 25) | |
191 ptr->s_str = 25; | |
192 if (ptr->s_str > max_stats.s_str) | |
193 max_stats.s_str = ptr->s_str; | |
194 ptr->s_str += ring_str; | |
195 if (ptr->s_str <= 0) | |
196 death(D_STRENGTH); | |
197 updpack(TRUE, &player); | |
198 } | |
199 | |
200 /* | |
201 * let's confuse the player | |
202 */ | |
203 confus_player() | |
204 { | |
205 if (off(player, ISCLEAR)) | |
206 { | |
207 msg("Wait, what's going on here. Huh? What? Who?"); | |
208 if (find_slot(unconfuse)) | |
209 lengthen(unconfuse, HUHDURATION); | |
210 else | |
211 fuse(unconfuse, 0, HUHDURATION, AFTER); | |
212 turn_on(player, ISHUH); | |
213 } | |
214 else msg("You feel dizzy for a moment, but it quickly passes."); | |
215 } | |
216 | |
217 /* | |
218 * this routine computes the players current dexterity | |
219 */ | |
220 dex_compute() | |
221 { | |
222 if (cur_misc[WEAR_GAUNTLET] != NULL && | |
223 cur_misc[WEAR_GAUNTLET]->o_which == MM_G_DEXTERITY) { | |
224 if (cur_misc[WEAR_GAUNTLET]->o_flags & ISCURSED) | |
225 return (3); | |
226 else | |
227 return (18); | |
228 } | |
229 else | |
230 return (pstats.s_dext); | |
231 } | |
232 | |
233 /* | |
234 * diag_ok: | |
235 * Check to see if the move is legal if it is diagonal | |
236 */ | |
237 | |
238 diag_ok(sp, ep, flgptr) | |
239 register coord *sp, *ep; | |
240 struct thing *flgptr; | |
241 { | |
242 register int numpaths = 0; | |
243 | |
244 /* Horizontal and vertical moves are always ok */ | |
245 if (ep->x == sp->x || ep->y == sp->y) | |
246 return TRUE; | |
247 | |
248 /* Diagonal moves are not allowed if there is a horizontal or | |
249 * vertical path to the destination | |
250 */ | |
251 if (step_ok(ep->y, sp->x, MONSTOK, flgptr)) numpaths++; | |
252 if (step_ok(sp->y, ep->x, MONSTOK, flgptr)) numpaths++; | |
253 return(numpaths != 1); | |
254 } | |
255 | |
256 /* | |
257 * pick a random position around the give (y, x) coordinates | |
258 */ | |
259 coord * | |
260 fallpos(pos, be_clear, range) | |
261 register coord *pos; | |
262 bool be_clear; | |
263 int range; | |
264 { | |
265 register int tried, i, j; | |
266 register char ch; | |
267 static coord ret; | |
268 static short masks[] = { | |
269 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100 }; | |
270 | |
271 /* | |
272 * Pick a spot at random centered on the position given by 'pos' and | |
273 * up to 'range' squares away from 'pos' | |
274 * | |
275 * If 'be_clear' is TRUE, the spot must be either FLOOR or PASSAGE | |
276 * inorder to be considered valid | |
277 * | |
278 * | |
279 * Generate a number from 0 to 8, representing the position to pick. | |
280 * Note that this DOES include the positon 'pos' itself | |
281 * | |
282 * If this position is not valid, mark it as 'tried', and pick another. | |
283 * Whenever a position is picked that has been tried before, | |
284 * sequentially find the next untried position. This eliminates costly | |
285 * random number generation | |
286 */ | |
287 | |
288 tried = 0; | |
289 while( tried != 0x1ff ) { | |
290 i = rnd(9); | |
291 while( tried & masks[i] ) | |
292 i = (i + 1) % 9; | |
293 | |
294 tried |= masks[i]; | |
295 | |
296 for( j = 1; j <= range; j++ ) { | |
297 ret.x = pos->x + j*grid[i].x; | |
298 ret.y = pos->y + j*grid[i].y; | |
299 | |
300 if (ret.x == hero.x && ret.y == hero.y) | |
301 continue; /* skip the hero */ | |
302 | |
303 if (ret.x < 0 || ret.x > cols - 1 || | |
304 ret.y < 1 || ret.y > lines - 3) | |
305 continue; /* off the screen? */ | |
306 | |
307 ch = CCHAR( winat(ret.y, ret.x) ); | |
308 | |
309 /* | |
310 * Check to make certain the spot is valid | |
311 */ | |
312 switch( ch ) { | |
313 case FLOOR: | |
314 case PASSAGE: | |
315 return( &ret ); | |
316 case GOLD: | |
317 case SCROLL: | |
318 case POTION: | |
319 case STICK: | |
320 case RING: | |
321 case WEAPON: | |
322 case ARMOR: | |
323 case MM: | |
324 case FOOD: | |
325 if(!be_clear && levtype != POSTLEV) | |
326 return( &ret ); | |
327 default: | |
328 break; | |
329 } | |
330 } | |
331 } | |
332 return( NULL ); | |
333 } | |
334 | |
335 | |
336 /* | |
337 * findmindex: | |
338 * Find the index into the monster table of a monster given its name. | |
339 */ | |
340 | |
341 findmindex(name) | |
342 char *name; | |
343 { | |
344 int which; | |
345 | |
346 for (which=1; which<NUMMONST; which++) { | |
347 if (strcmp(name, monsters[which].m_name) == 0) | |
348 break; | |
349 } | |
350 if (which >= NUMMONST) { | |
351 debug("couldn't find monster index"); | |
352 which = 1; | |
353 } | |
354 return(which); | |
355 } | |
356 | |
357 /* | |
358 * find_mons: | |
359 * Find the monster from his coordinates | |
360 */ | |
361 | |
362 struct linked_list * | |
363 find_mons(y, x) | |
364 register int y; | |
365 register int x; | |
366 { | |
367 register struct linked_list *item; | |
368 register struct thing *th; | |
369 | |
370 for (item = mlist; item != NULL; item = next(item)) | |
371 { | |
372 th = THINGPTR(item); | |
373 if (th->t_pos.y == y && th->t_pos.x == x) | |
374 return item; | |
375 } | |
376 return NULL; | |
377 } | |
378 | |
379 /* | |
380 * find_obj: | |
381 * find the unclaimed object at y, x | |
382 */ | |
383 | |
384 struct linked_list * | |
385 find_obj(y, x) | |
386 register int y; | |
387 register int x; | |
388 { | |
389 register struct linked_list *obj; | |
390 register struct object *op; | |
391 | |
392 for (obj = lvl_obj; obj != NULL; obj = next(obj)) | |
393 { | |
394 op = OBJPTR(obj); | |
395 if (op->o_pos.y == y && op->o_pos.x == x) | |
396 return obj; | |
397 } | |
398 return NULL; | |
399 } | |
400 | |
401 /* | |
402 * get coordinates from the player using the cursor keys (or mouse) | |
403 */ | |
404 coord | |
405 get_coordinates() | |
406 { | |
407 register int which; | |
408 coord c; | |
409 #ifdef PC7300 | |
410 struct umdata startmouse, listenmouse; /* Mouse parameters */ | |
411 int xmouse, /* x-position of mouse */ | |
412 ymouse, /* y-position of mouse */ | |
413 bmouse, /* button value of mouse */ | |
414 rmouse; /* reason for mouse change */ | |
415 #endif | |
416 | |
417 c = hero; | |
418 wmove(cw, hero.y, hero.x); | |
419 draw(cw); | |
420 #ifdef PC7300 | |
421 keypad(0, 1); /* Turn on escape sequences */ | |
422 ioctl(0, WIOCGETMOUSE, &startmouse); /* Get current mouse parameters */ | |
423 listenmouse = startmouse; /* Make a copy */ | |
424 listenmouse.um_flags |= MSDOWN; /* Enable detection of button down */ | |
425 ioctl(0, WIOCSETMOUSE, &listenmouse); /* Make the change */ | |
426 #endif | |
427 for (;;) { | |
428 #ifdef PC7300 | |
429 which = wgetc(0); | |
430 #else | |
431 which = (getchar() & 0177); | |
432 #endif | |
433 switch(which) { | |
434 #ifdef PC7300 | |
435 case Home: | |
436 c.x = 0; c.y = 1; | |
437 when Beg: | |
438 c.x = 0; | |
439 when End: | |
440 c.x = cols - 1; | |
441 when Mouse: | |
442 case ESCAPE: | |
443 case Cancl: | |
444 case s_Cancl: | |
445 if (which == Mouse) { | |
446 if (wreadmouse(0,&xmouse,&ymouse,&bmouse,&rmouse) != 0 || | |
447 (rmouse & MSDOWN) == 0) | |
448 break; | |
449 c.y = ymouse / wdata.uw_vs; | |
450 c.x = xmouse / wdata.uw_hs; | |
451 c.y = max(c.y, 1); | |
452 c.y = min(c.y, lines - 3); | |
453 c.x = max(c.x, 0); | |
454 c.x = min(c.x, cols - 1); | |
455 } | |
456 else c = hero; | |
457 wmove(cw, c.y, c.x); | |
458 draw(cw); | |
459 case '\n': | |
460 case '\c': | |
461 keypad(0, 0); /* Turn off escape interpretation */ | |
462 ioctl(0, WIOCSETMOUSE, &startmouse); /* No mouse tracking */ | |
463 return(c); | |
464 when 'h': | |
465 case 'H': | |
466 case Back: | |
467 case s_Back: | |
468 c.x--; | |
469 when 'j': | |
470 case 'J': | |
471 case Down: | |
472 case RollDn: | |
473 c.y++; | |
474 when 'k': | |
475 case 'K': | |
476 case Up: | |
477 case RollUp: | |
478 c.y--; | |
479 when 'l': | |
480 case 'L': | |
481 case Forward: | |
482 case s_Forward: | |
483 c.x++; | |
484 when 'y': | |
485 case 'Y': | |
486 case Prev: | |
487 c.x--; c.y--; | |
488 when 'u': | |
489 case 'U': | |
490 case Next: | |
491 c.x++; c.y--; | |
492 when 'b': | |
493 case 'B': | |
494 case s_Prev: | |
495 c.x--; c.y++; | |
496 when 'n': | |
497 case 'N': | |
498 case s_Next: | |
499 c.x++; c.y++; | |
500 when '*': | |
501 msg("Select location via mouse, or"); | |
502 msg("Use cursor keys,h,j,k,l,y,u,b,or n, then hit return."); | |
503 #else | |
504 case ESCAPE: | |
505 c = hero; | |
506 wmove(cw, c.y, c.x); | |
507 draw(cw); | |
508 case '\n': | |
509 case '\r': | |
510 return(c); | |
511 when 'h': | |
512 case 'H': | |
513 c.x--; | |
514 when 'j': | |
515 case 'J': | |
516 c.y++; | |
517 when 'k': | |
518 case 'K': | |
519 c.y--; | |
520 when 'l': | |
521 case 'L': | |
522 c.x++; | |
523 when 'y': | |
524 case 'Y': | |
525 c.x--; c.y--; | |
526 when 'u': | |
527 case 'U': | |
528 c.x++; c.y--; | |
529 when 'b': | |
530 case 'B': | |
531 c.x--; c.y++; | |
532 when 'n': | |
533 case 'N': | |
534 c.x++; c.y++; | |
535 when '*': | |
536 msg("Use h,j,k,l,y,u,b,n to position cursor, then hit return."); | |
537 #endif | |
538 } | |
539 c.y = max(c.y, 1); | |
540 c.y = min(c.y, lines - 3); | |
541 c.x = max(c.x, 0); | |
542 c.x = min(c.x, cols - 1); | |
543 wmove(cw, c.y, c.x); | |
544 draw(cw); | |
545 } | |
546 } | |
547 | |
548 /* | |
549 * set up the direction co_ordinate for use in various "prefix" commands | |
550 */ | |
551 bool | |
552 get_dir(direction) | |
553 coord *direction; | |
554 { | |
555 register char *prompt; | |
556 register bool gotit; | |
557 int x,y; | |
558 | |
559 prompt = terse ? "Direction?" : "Which direction? "; | |
560 msg(prompt); | |
561 do | |
562 { | |
563 gotit = TRUE; | |
564 switch (md_readchar(msgw)) | |
565 { | |
566 case 'h': case'H': direction->y = 0; direction->x = -1; | |
567 when 'j': case'J': direction->y = 1; direction->x = 0; | |
568 when 'k': case'K': direction->y = -1; direction->x = 0; | |
569 when 'l': case'L': direction->y = 0; direction->x = 1; | |
570 when 'y': case'Y': direction->y = -1; direction->x = -1; | |
571 when 'u': case'U': direction->y = -1; direction->x = 1; | |
572 when 'b': case'B': direction->y = 1; direction->x = -1; | |
573 when 'n': case'N': direction->y = 1; direction->x = 1; | |
574 case 0: | |
575 when 3: quit(0); | |
576 return(FALSE); | |
577 when ESCAPE: return (FALSE); | |
578 otherwise: | |
579 mpos = 0; | |
580 msg(prompt); | |
581 gotit = FALSE; | |
582 } | |
583 } until (gotit); | |
584 if ((on(player, ISHUH) || on(player, ISDANCE)) && rnd(100) > 20) { | |
585 do | |
586 { | |
587 *direction = grid[rnd(9)]; | |
588 } while (direction->y == 0 && direction->x == 0); | |
589 } | |
590 else if (on(player, ISFLEE)) { | |
591 y = hero.y; | |
592 x = hero.x; | |
593 while (shoot_ok(winat(y, x))) { | |
594 y += direction->y; | |
595 x += direction->x; | |
596 } | |
597 if (isalpha(mvwinch(mw, y, x))) { | |
598 if (y == player.t_dest->y && x == player.t_dest->x) { | |
599 mpos = 0; | |
600 msg("You are too frightened to!"); | |
601 return(FALSE); | |
602 } | |
603 } | |
604 } | |
605 mpos = 0; | |
606 return TRUE; | |
607 } | |
608 | |
609 /* | |
610 * see if the object is one of the currently used items | |
611 */ | |
612 is_current(obj) | |
613 register struct object *obj; | |
614 { | |
615 if (obj == NULL) | |
616 return FALSE; | |
617 if (obj == cur_armor || obj == cur_weapon || | |
618 obj == cur_ring[LEFT_1] || obj == cur_ring[LEFT_2] || | |
619 obj == cur_ring[LEFT_3] || obj == cur_ring[LEFT_4] || | |
620 obj == cur_ring[RIGHT_1] || obj == cur_ring[RIGHT_2] || | |
621 obj == cur_ring[RIGHT_3] || obj == cur_ring[RIGHT_4] || | |
622 obj == cur_misc[WEAR_BOOTS] || obj == cur_misc[WEAR_JEWEL] || | |
623 obj == cur_misc[WEAR_BRACERS] || obj == cur_misc[WEAR_CLOAK] || | |
624 obj == cur_misc[WEAR_GAUNTLET] || obj == cur_misc[WEAR_NECKLACE]) { | |
625 | |
626 return TRUE; | |
627 } | |
628 | |
629 /* Is it a "current" relic? */ | |
630 if (obj->o_type == RELIC) { | |
631 switch (obj->o_which) { | |
632 case MUSTY_DAGGER: | |
633 case EMORI_CLOAK: | |
634 case HEIL_ANKH: | |
635 case YENDOR_AMULET: | |
636 case STONEBONES_AMULET: | |
637 case HRUGGEK_MSTAR: | |
638 case AXE_AKLAD: | |
639 case YEENOGHU_FLAIL: | |
640 case SURTUR_RING: | |
641 if (cur_relic[obj->o_which]) return TRUE; | |
642 } | |
643 } | |
644 | |
645 return FALSE; | |
646 } | |
647 | |
648 | |
649 /* | |
650 * Look: | |
651 * A quick glance all around the player | |
652 */ | |
653 | |
654 look(wakeup, runend) | |
655 bool wakeup; /* Should we wake up monsters */ | |
656 bool runend; /* At end of a run -- for mazes */ | |
657 { | |
658 register int x, y, radius; | |
659 register char ch, och; | |
660 register int oldx, oldy; | |
661 register bool inpass, horiz, vert, do_light = FALSE, do_blank = FALSE; | |
662 register int passcount = 0, curfloorcount = 0, nextfloorcount = 0; | |
663 register struct room *rp; | |
664 register int ey, ex; | |
665 | |
666 inpass = ((rp = roomin(&hero)) == NULL); /* Are we in a passage? */ | |
667 | |
668 /* Are we moving vertically or horizontally? */ | |
669 if (runch == 'h' || runch == 'l') horiz = TRUE; | |
670 else horiz = FALSE; | |
671 if (runch == 'j' || runch == 'k') vert = TRUE; | |
672 else vert = FALSE; | |
673 | |
674 /* How far around himself can the player see? */ | |
675 if (levtype == OUTSIDE) { | |
676 if (daytime) radius = 6; | |
677 else if (lit_room(rp)) radius = 3; | |
678 else radius = 1; | |
679 } | |
680 else radius = 1; | |
681 | |
682 getyx(cw, oldy, oldx); /* Save current position */ | |
683 | |
684 /* Blank out the floor around our last position and check for | |
685 * moving out of a corridor in a maze. | |
686 */ | |
687 if (levtype == OUTSIDE) do_blank = !daytime; | |
688 else if (oldrp != NULL && !lit_room(oldrp) && off(player, ISBLIND)) | |
689 do_blank = TRUE; | |
690 | |
691 /* Now move around the old position and blank things out */ | |
692 ey = player.t_oldpos.y + radius; | |
693 ex = player.t_oldpos.x + radius; | |
694 for (x = player.t_oldpos.x - radius; x <= ex; x++) | |
695 if (x >= 0 && x < cols) | |
696 for (y = player.t_oldpos.y - radius; y <= ey; y++) { | |
697 struct linked_list *it; | |
698 coord here; /* Current <x,y> coordinate */ | |
699 char savech; /* Saves character in monster window */ | |
700 bool in_room; /* Are we in a room? */ | |
701 | |
702 if (y < 1 || y > lines - 3) continue; | |
703 | |
704 /* See what's there -- ignore monsters, just see what they're on */ | |
705 savech = CCHAR( mvwinch(mw, y, x) ); | |
706 waddch(mw, ' '); | |
707 ch = show(y, x); | |
708 mvwaddch(mw, y, x, savech); /* Restore monster */ | |
709 | |
710 /* | |
711 * If we have a monster that we can't see anymore, make sure | |
712 * that we can note that fact. | |
713 */ | |
714 if (isalpha(savech) && | |
715 (y < hero.y - radius || y > hero.y + radius || | |
716 x < hero.x - radius || x > hero.x + radius)) { | |
717 /* Find the monster */ | |
718 it = find_mons(y, x); | |
719 } | |
720 else it = NULL; | |
721 | |
722 /* Are we in a room? */ | |
723 here.y = y; | |
724 here.x = x; | |
725 in_room = (roomin(&here) != NULL); | |
726 | |
727 if ((do_blank || !in_room) && (y != hero.y || x != hero.x)) | |
728 switch (ch) { | |
729 case DOOR: | |
730 case SECRETDOOR: | |
731 case PASSAGE: | |
732 case STAIRS: | |
733 case TRAPDOOR: | |
734 case TELTRAP: | |
735 case BEARTRAP: | |
736 case SLEEPTRAP: | |
737 case ARROWTRAP: | |
738 case DARTTRAP: | |
739 case MAZETRAP: | |
740 case POOL: | |
741 case POST: | |
742 case '|': | |
743 case '-': | |
744 case WALL: | |
745 /* If there was a monster showing, make it disappear */ | |
746 if (isalpha(savech)) { | |
747 mvwaddch(cw, y, x, ch); | |
748 | |
749 /* | |
750 * If we found it (we should!), set it to | |
751 * the right character! | |
752 */ | |
753 if (it) (THINGPTR(it))->t_oldch = ch; | |
754 } | |
755 break; | |
756 when FLOOR: | |
757 case FOREST: | |
758 default: | |
759 mvwaddch(cw, y, x, in_room ? ' ' : PASSAGE); | |
760 | |
761 /* If we found a monster, set it to darkness! */ | |
762 if (it) (THINGPTR(it))->t_oldch = CCHAR( mvwinch(cw, y, x) ); | |
763 } | |
764 | |
765 /* Moving out of a corridor? */ | |
766 if (levtype == MAZELEV && !ce(hero, player.t_oldpos) && | |
767 !running && !isrock(ch) && /* Not running and not a wall */ | |
768 ((vert && x != player.t_oldpos.x && y==player.t_oldpos.y) || | |
769 (horiz && y != player.t_oldpos.y && x==player.t_oldpos.x))) | |
770 do_light = off(player, ISBLIND); | |
771 } | |
772 | |
773 /* Take care of unlighting a corridor */ | |
774 if (do_light && lit_room(rp)) light(&player.t_oldpos); | |
775 | |
776 /* Are we coming or going between a wall and a corridor in a maze? */ | |
777 och = show(player.t_oldpos.y, player.t_oldpos.x); | |
778 ch = show(hero.y, hero.x); | |
779 if (levtype == MAZELEV && | |
780 ((isrock(och) && !isrock(ch)) || (isrock(ch) && !isrock(och)))) { | |
781 do_light = off(player, ISBLIND); /* Light it up if not blind */ | |
782 | |
783 /* Unlight what we just saw */ | |
784 if (do_light && lit_room(&rooms[0])) light(&player.t_oldpos); | |
785 } | |
786 | |
787 /* Look around the player */ | |
788 ey = hero.y + radius; | |
789 ex = hero.x + radius; | |
790 for (x = hero.x - radius; x <= ex; x++) | |
791 if (x >= 0 && x < cols) for (y = hero.y - radius; y <= ey; y++) { | |
792 if (y < 1 || y >= lines - 2) | |
793 continue; | |
794 if (isalpha(mvwinch(mw, y, x))) | |
795 { | |
796 register struct linked_list *it; | |
797 register struct thing *tp; | |
798 | |
799 if (wakeup) | |
800 it = wake_monster(y, x); | |
801 else | |
802 it = find_mons(y, x); | |
803 | |
804 if (it) { | |
805 tp = THINGPTR(it); | |
806 tp->t_oldch = CCHAR( mvinch(y, x) ); | |
807 if (isatrap(tp->t_oldch)) { | |
808 register struct trap *trp = trap_at(y, x); | |
809 | |
810 tp->t_oldch = (trp->tr_flags & ISFOUND) ? tp->t_oldch | |
811 : trp->tr_show; | |
812 } | |
813 if (tp->t_oldch == FLOOR && !lit_room(rp) && | |
814 off(player, ISBLIND)) | |
815 tp->t_oldch = ' '; | |
816 } | |
817 } | |
818 | |
819 /* | |
820 * Secret doors show as walls | |
821 */ | |
822 if ((ch = show(y, x)) == SECRETDOOR) | |
823 ch = secretdoor(y, x); | |
824 /* | |
825 * Don't show room walls if he is in a passage and | |
826 * check for maze turns | |
827 */ | |
828 if (off(player, ISBLIND)) | |
829 { | |
830 if (y == hero.y && x == hero.x | |
831 || (inpass && (ch == '-' || ch == '|'))) | |
832 continue; | |
833 | |
834 /* Did we come to a crossroads in a maze? */ | |
835 if (levtype == MAZELEV && | |
836 (runend || !ce(hero, player.t_oldpos)) && | |
837 !isrock(ch) && /* Not a wall */ | |
838 ((vert && x != hero.x && y == hero.y) || | |
839 (horiz && y != hero.y && x == hero.x))) | |
840 /* Just came to a turn */ | |
841 do_light = off(player, ISBLIND); | |
842 } | |
843 else if (y != hero.y || x != hero.x) | |
844 continue; | |
845 | |
846 wmove(cw, y, x); | |
847 waddch(cw, ch); | |
848 if (door_stop && !firstmove && running) | |
849 { | |
850 switch (runch) | |
851 { | |
852 case 'h': | |
853 if (x == hero.x + 1) | |
854 continue; | |
855 when 'j': | |
856 if (y == hero.y - 1) | |
857 continue; | |
858 when 'k': | |
859 if (y == hero.y + 1) | |
860 continue; | |
861 when 'l': | |
862 if (x == hero.x - 1) | |
863 continue; | |
864 when 'y': | |
865 if ((x + y) - (hero.x + hero.y) >= 1) | |
866 continue; | |
867 when 'u': | |
868 if ((y - x) - (hero.y - hero.x) >= 1) | |
869 continue; | |
870 when 'n': | |
871 if ((x + y) - (hero.x + hero.y) <= -1) | |
872 continue; | |
873 when 'b': | |
874 if ((y - x) - (hero.y - hero.x) <= -1) | |
875 continue; | |
876 } | |
877 switch (ch) | |
878 { | |
879 case DOOR: | |
880 if (x == hero.x || y == hero.y) | |
881 running = FALSE; | |
882 break; | |
883 case PASSAGE: | |
884 if (x == hero.x || y == hero.y) | |
885 passcount++; | |
886 break; | |
887 case FLOOR: | |
888 /* Stop by new passages in a maze (floor next to us) */ | |
889 if ((levtype == MAZELEV) && | |
890 !(hero.y == y && hero.x == x)) { | |
891 if (vert) { /* Moving vertically */ | |
892 /* We have a passage on our row */ | |
893 if (y == hero.y) curfloorcount++; | |
894 | |
895 /* Some passage on the next row */ | |
896 else if (y != player.t_oldpos.y) | |
897 nextfloorcount++; | |
898 } | |
899 else { /* Moving horizontally */ | |
900 /* We have a passage on our column */ | |
901 if (x == hero.x) curfloorcount++; | |
902 | |
903 /* Some passage in the next column */ | |
904 else if (x != player.t_oldpos.x) | |
905 nextfloorcount++; | |
906 } | |
907 } | |
908 case '|': | |
909 case '-': | |
910 case ' ': | |
911 break; | |
912 default: | |
913 running = FALSE; | |
914 break; | |
915 } | |
916 } | |
917 } | |
918 | |
919 /* Have we passed a side passage, with multiple choices? */ | |
920 if (curfloorcount > 0 && nextfloorcount > 0) running = FALSE; | |
921 | |
922 else if (door_stop && !firstmove && passcount > 1) | |
923 running = FALSE; | |
924 | |
925 /* Do we have to light up the area (just stepped into a new corridor)? */ | |
926 if (do_light && !running && lit_room(rp)) light(&hero); | |
927 | |
928 mvwaddch(cw, hero.y, hero.x, PLAYER); | |
929 wmove(cw, oldy, oldx); | |
930 if (!ce(player.t_oldpos, hero)) { | |
931 player.t_oldpos = hero; /* Don't change if we didn't move */ | |
932 oldrp = rp; | |
933 } | |
934 } | |
935 | |
936 /* | |
937 * Lower a level of experience | |
938 */ | |
939 | |
940 lower_level(who) | |
941 short who; | |
942 { | |
943 int fewer, nsides; | |
944 unsigned int exp; | |
945 | |
946 msg("You suddenly feel less skillful."); | |
947 if (--pstats.s_lvl == 0) | |
948 death(who); /* All levels gone */ | |
949 if (pstats.s_lvladj > 0) { /* lose artificial levels first */ | |
950 pstats.s_lvladj--; | |
951 return; | |
952 } | |
953 exp = char_class[player.t_ctype].cap; | |
954 if (pstats.s_exp >= exp*2) | |
955 pstats.s_exp -= exp; | |
956 else | |
957 pstats.s_exp /= 2; | |
958 | |
959 nsides = char_class[player.t_ctype].hit_pts; | |
960 fewer = max(1, roll(1,nsides) + const_bonus()); | |
961 pstats.s_hpt -= fewer; | |
962 max_stats.s_hpt -= fewer; | |
963 if (pstats.s_hpt < 1) | |
964 pstats.s_hpt = 1; | |
965 if (max_stats.s_hpt < 1) | |
966 death(who); | |
967 } | |
968 | |
969 /* | |
970 * print out the name of a monster | |
971 */ | |
972 char * | |
973 monster_name(tp) | |
974 register struct thing *tp; | |
975 { | |
976 prbuf[0] = '\0'; | |
977 if (on(*tp, ISFLEE) || on(*tp, WASTURNED)) | |
978 strcat(prbuf, "terrified "); | |
979 if (on(*tp, ISHUH)) | |
980 strcat(prbuf, "confused "); | |
981 if (on(*tp, ISCHARMED)) | |
982 strcat(prbuf, "charmed "); | |
983 else if (on(*tp, ISFLY)) | |
984 strcat(prbuf, "flying "); | |
985 | |
986 /* If it is sleeping or stoned, write over any of the above attributes */ | |
987 if (off(*tp, ISRUN)) | |
988 strcpy(prbuf, "sleeping "); | |
989 if (on(*tp, ISSTONE)) | |
990 strcpy(prbuf, "petrified "); | |
991 | |
992 if (tp->t_name) strcat(prbuf, tp->t_name); | |
993 else strcat(prbuf, monsters[tp->t_index].m_name); | |
994 | |
995 return(prbuf); | |
996 } | |
997 | |
998 /* | |
999 * move_hero: | |
1000 * Try to move the hero somplace besides next to where he is. We ask him | |
1001 * where. There can be restrictions based on why he is moving. | |
1002 */ | |
1003 | |
1004 bool | |
1005 move_hero(why) | |
1006 int why; | |
1007 { | |
1008 char *action; | |
1009 char which; | |
1010 coord c; | |
1011 | |
1012 switch (why) { | |
1013 case H_TELEPORT: | |
1014 action = "teleport"; | |
1015 } | |
1016 | |
1017 msg("Where do you wish to %s to? (* for help) ", action); | |
1018 c = get_coordinates(); | |
1019 mpos = 0; | |
1020 which = CCHAR( winat(c.y, c.x) ); | |
1021 switch (which) { | |
1022 default: | |
1023 if (!isrock(which) || off(player, CANINWALL)) break; | |
1024 | |
1025 case FLOOR: | |
1026 case PASSAGE: | |
1027 case DOOR: | |
1028 case STAIRS: | |
1029 case POST: | |
1030 case GOLD: | |
1031 case POTION: | |
1032 case SCROLL: | |
1033 case FOOD: | |
1034 case WEAPON: | |
1035 case ARMOR: | |
1036 case RING: | |
1037 case MM: | |
1038 case RELIC: | |
1039 case STICK: | |
1040 hero = c; | |
1041 return(TRUE); | |
1042 } | |
1043 return(FALSE); | |
1044 } | |
1045 | |
1046 /* | |
1047 * raise_level: | |
1048 * The guy just magically went up a level. | |
1049 */ | |
1050 | |
1051 raise_level() | |
1052 { | |
1053 unsigned long test; /* Next level -- be sure it is not an overflow */ | |
1054 | |
1055 test = check_level(); /* Get next boundary */ | |
1056 | |
1057 /* Be sure it is higher than what we have no -- else overflow */ | |
1058 if (test > pstats.s_exp) pstats.s_exp = test; | |
1059 check_level(); | |
1060 } | |
1061 | |
1062 /* | |
1063 * saving throw matrix for character saving throws | |
1064 * this table is indexed by char type and saving throw type | |
1065 */ | |
1066 static int st_matrix[NUM_CHARTYPES][5] = { | |
1067 /* Poison, Petrify, wand, Breath, Magic */ | |
1068 { 14, 15, 16, 16, 17 }, | |
1069 { 14, 15, 16, 16, 17 }, | |
1070 { 14, 15, 16, 16, 17 }, | |
1071 { 14, 13, 11, 15, 12 }, | |
1072 { 10, 13, 14, 16, 15 }, | |
1073 { 13, 12, 14, 16, 15 }, | |
1074 { 13, 12, 14, 16, 15 }, | |
1075 { 10, 13, 14, 16, 15 }, | |
1076 { 13, 12, 14, 16, 15 }, | |
1077 { 14, 15, 16, 16, 17 } | |
1078 }; | |
1079 | |
1080 /* | |
1081 * save: | |
1082 * See if a creature saves against something | |
1083 */ | |
1084 save(which, who, adj) | |
1085 int which; /* which type of save */ | |
1086 struct thing *who; /* who is saving */ | |
1087 int adj; /* saving throw adjustment */ | |
1088 { | |
1089 register int need, level, protect; | |
1090 | |
1091 protect = 0; | |
1092 level = who->t_stats.s_lvl; | |
1093 need = st_matrix[who->t_ctype][which]; | |
1094 switch (who->t_ctype) { | |
1095 case C_FIGHTER: | |
1096 case C_RANGER: | |
1097 case C_PALADIN: | |
1098 case C_MONSTER: | |
1099 need -= (level-1) / 2; | |
1100 when C_MAGICIAN: | |
1101 need -= 2 * (level-1) / 5; | |
1102 when C_CLERIC: | |
1103 case C_DRUID: | |
1104 need -= (level-1) / 3; | |
1105 when C_THIEF: | |
1106 case C_ASSASIN: | |
1107 case C_MONK: | |
1108 need -= 2 * (level-1) / 4; | |
1109 } | |
1110 /* | |
1111 * add in pluses against poison for execeptional constitution | |
1112 */ | |
1113 if (which == VS_POISON && who->t_stats.s_const > 18) | |
1114 need -= (who->t_stats.s_const - 17) / 2; | |
1115 if (who == &player) { | |
1116 /* | |
1117 * does the player have a ring of protection on? | |
1118 */ | |
1119 protect += ring_value(R_PROTECT); | |
1120 /* | |
1121 * does the player have a cloak of protection on? | |
1122 */ | |
1123 if (cur_misc[WEAR_CLOAK]) | |
1124 protect += cur_misc[WEAR_CLOAK]->o_ac; | |
1125 | |
1126 protect = min(protect, 4);/* no more than a +4 bonus */ | |
1127 need -= protect; | |
1128 } | |
1129 need -= adj; | |
1130 /* | |
1131 * always miss or save on a 1 (except for UNIQUEs | |
1132 */ | |
1133 if (who == &player || off(*who, ISUNIQUE)) | |
1134 need = max(need, 2); | |
1135 need = min(20, need); /* always make our save on a 20 */ | |
1136 debug("need a %d to save", need); | |
1137 return (roll(1, 20) >= need); | |
1138 } | |
1139 | |
1140 | |
1141 /* | |
1142 * secret_door: | |
1143 * Figure out what a secret door looks like. | |
1144 */ | |
1145 | |
1146 secretdoor(y, x) | |
1147 register int y, x; | |
1148 { | |
1149 register int i; | |
1150 register struct room *rp; | |
1151 register coord *cpp; | |
1152 static coord cp; | |
1153 | |
1154 cp.y = y; | |
1155 cp.x = x; | |
1156 cpp = &cp; | |
1157 for (rp = rooms, i = 0; i < MAXROOMS; rp++, i++) | |
1158 if (inroom(rp, cpp)) | |
1159 if (y == rp->r_pos.y || y == rp->r_pos.y + rp->r_max.y - 1) | |
1160 return('-'); | |
1161 else | |
1162 return('|'); | |
1163 | |
1164 return('p'); | |
1165 } | |
1166 | |
1167 /* | |
1168 * this routine computes the players current strength | |
1169 */ | |
1170 str_compute() | |
1171 { | |
1172 if (cur_misc[WEAR_GAUNTLET] != NULL && | |
1173 cur_misc[WEAR_GAUNTLET]->o_which == MM_G_OGRE) { | |
1174 if (cur_misc[WEAR_GAUNTLET]->o_flags & ISCURSED) | |
1175 return (3); | |
1176 else | |
1177 return (18); | |
1178 } | |
1179 else | |
1180 return (pstats.s_str); | |
1181 } | |
1182 | |
1183 /* | |
1184 * copy string using unctrl for things | |
1185 */ | |
1186 strucpy(s1, s2, len) | |
1187 register char *s1, *s2; | |
1188 register int len; | |
1189 { | |
1190 register char *sp; | |
1191 | |
1192 while (len--) | |
1193 { | |
1194 strcpy(s1, (sp = unctrl(*s2++))); | |
1195 s1 += strlen(sp); | |
1196 } | |
1197 *s1 = '\0'; | |
1198 } | |
1199 | |
1200 /* | |
1201 * tr_name: | |
1202 * print the name of a trap | |
1203 */ | |
1204 | |
1205 char * | |
1206 tr_name(ch) | |
1207 char ch; | |
1208 { | |
1209 register char *s; | |
1210 | |
1211 switch (ch) | |
1212 { | |
1213 case TRAPDOOR: | |
1214 s = terse ? "A trapdoor." : "You found a trapdoor."; | |
1215 when BEARTRAP: | |
1216 s = terse ? "A beartrap." : "You found a beartrap."; | |
1217 when SLEEPTRAP: | |
1218 s = terse ? "A sleeping gas trap.":"You found a sleeping gas trap."; | |
1219 when ARROWTRAP: | |
1220 s = terse ? "An arrow trap." : "You found an arrow trap."; | |
1221 when TELTRAP: | |
1222 s = terse ? "A teleport trap." : "You found a teleport trap."; | |
1223 when DARTTRAP: | |
1224 s = terse ? "A dart trap." : "You found a poison dart trap."; | |
1225 when POOL: | |
1226 s = terse ? "A shimmering pool." : "You found a shimmering pool"; | |
1227 when MAZETRAP: | |
1228 s = terse ? "A maze entrance." : "You found a maze entrance"; | |
1229 } | |
1230 return s; | |
1231 } | |
1232 | |
1233 /* | |
1234 * for printfs: if string starts with a vowel, return "n" for an "an" | |
1235 */ | |
1236 char * | |
1237 vowelstr(str) | |
1238 register char *str; | |
1239 { | |
1240 switch (*str) | |
1241 { | |
1242 case 'a': | |
1243 case 'e': | |
1244 case 'i': | |
1245 case 'o': | |
1246 case 'u': | |
1247 return "n"; | |
1248 default: | |
1249 return ""; | |
1250 } | |
1251 } | |
1252 | |
1253 /* | |
1254 * wake up a room full (hopefully) of creatures | |
1255 */ | |
1256 wake_room(rp) | |
1257 register struct room *rp; | |
1258 { | |
1259 register struct linked_list *item; | |
1260 register struct thing *tp; | |
1261 | |
1262 for (item=mlist; item!=NULL; item=next(item)) { | |
1263 tp = THINGPTR(item); | |
1264 if (off(*tp,ISRUN) && on(*tp,ISMEAN) && roomin(&tp->t_pos) == rp) | |
1265 runto(tp, &hero); | |
1266 } | |
1267 } | |
1268 | |
1269 | |
1270 /* | |
1271 * waste_time: | |
1272 * Do nothing but let other things happen | |
1273 */ | |
1274 | |
1275 waste_time() | |
1276 { | |
1277 if (inwhgt) /* if from wghtchk then done */ | |
1278 return; | |
1279 do_daemons(BEFORE); | |
1280 do_fuses(BEFORE); | |
1281 do_daemons(AFTER); | |
1282 do_fuses(AFTER); | |
1283 } |