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