Mercurial > hg > early-roguelike
comparison rogue3/fight.c @ 0:527e2150eaf0
Import Rogue 3.6 from the Roguelike Restoration Project (r1490)
author | edwarj4 |
---|---|
date | Tue, 13 Oct 2009 13:33:34 +0000 |
parents | |
children | e7aab31362af |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:527e2150eaf0 |
---|---|
1 /* | |
2 * All the fighting gets done here | |
3 * | |
4 * @(#)fight.c 3.28 (Berkeley) 6/15/81 | |
5 * | |
6 * Rogue: Exploring the Dungeons of Doom | |
7 * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman | |
8 * All rights reserved. | |
9 * | |
10 * See the file LICENSE.TXT for full copyright and licensing information. | |
11 */ | |
12 | |
13 #include "curses.h" | |
14 #include <ctype.h> | |
15 #include <string.h> | |
16 #include <stdlib.h> | |
17 #include "rogue.h" | |
18 | |
19 int e_levels[] = { | |
20 10,20,40,80,160,320,640,1280,2560,5120,10240,20480, | |
21 40920, 81920, 163840, 327680, 655360, 1310720, 2621440, 0 }; | |
22 | |
23 /* | |
24 * fight: | |
25 * The player attacks the monster. | |
26 */ | |
27 | |
28 int | |
29 fight(coord *mp, int mn, struct object *weap, int thrown) | |
30 { | |
31 struct thing *tp; | |
32 struct linked_list *item; | |
33 int did_hit = TRUE; | |
34 | |
35 /* | |
36 * Find the monster we want to fight | |
37 */ | |
38 if ((item = find_mons(mp->y, mp->x)) == NULL) | |
39 { | |
40 debug("Fight what @ %d,%d", mp->y, mp->x); | |
41 return(0); | |
42 } | |
43 tp = (struct thing *) ldata(item); | |
44 /* | |
45 * Since we are fighting, things are not quiet so no healing takes | |
46 * place. | |
47 */ | |
48 quiet = 0; | |
49 runto(mp, &hero); | |
50 /* | |
51 * Let him know it was really a mimic (if it was one). | |
52 */ | |
53 if (tp->t_type == 'M' && tp->t_disguise != 'M' && off(player, ISBLIND)) | |
54 { | |
55 msg("Wait! That's a mimic!"); | |
56 tp->t_disguise = 'M'; | |
57 did_hit = thrown; | |
58 } | |
59 if (did_hit) | |
60 { | |
61 char *mname; | |
62 | |
63 did_hit = FALSE; | |
64 if (on(player, ISBLIND)) | |
65 mname = "it"; | |
66 else | |
67 mname = monsters[mn-'A'].m_name; | |
68 if (roll_em(&pstats, &tp->t_stats, weap, thrown)) | |
69 { | |
70 did_hit = TRUE; | |
71 if (thrown) | |
72 thunk(weap, mname); | |
73 else | |
74 hit(NULL, mname); | |
75 if (on(player, CANHUH)) | |
76 { | |
77 msg("Your hands stop glowing red"); | |
78 msg("The %s appears confused.", mname); | |
79 tp->t_flags |= ISHUH; | |
80 player.t_flags &= ~CANHUH; | |
81 } | |
82 if (tp->t_stats.s_hpt <= 0) | |
83 killed(item, TRUE); | |
84 } | |
85 else | |
86 if (thrown) | |
87 bounce(weap, mname); | |
88 else | |
89 miss(NULL, mname); | |
90 } | |
91 count = 0; | |
92 return did_hit; | |
93 } | |
94 | |
95 /* | |
96 * attack: | |
97 * The monster attacks the player | |
98 */ | |
99 | |
100 int | |
101 attack(struct thing *mp) | |
102 { | |
103 char *mname; | |
104 | |
105 /* | |
106 * Since this is an attack, stop running and any healing that was | |
107 * going on at the time. | |
108 */ | |
109 running = FALSE; | |
110 quiet = 0; | |
111 if (mp->t_type == 'M' && off(player, ISBLIND)) | |
112 mp->t_disguise = 'M'; | |
113 if (on(player, ISBLIND)) | |
114 mname = "it"; | |
115 else | |
116 mname = monsters[mp->t_type-'A'].m_name; | |
117 if (roll_em(&mp->t_stats, &pstats, NULL, FALSE)) | |
118 { | |
119 if (mp->t_type != 'E') | |
120 hit(mname, NULL); | |
121 if (pstats.s_hpt <= 0) | |
122 death(mp->t_type); /* Bye bye life ... */ | |
123 if (off(*mp, ISCANC)) | |
124 switch (mp->t_type) | |
125 { | |
126 case 'R': | |
127 /* | |
128 * If a rust monster hits, you lose armor | |
129 */ | |
130 if (cur_armor != NULL && cur_armor->o_ac < 9) | |
131 { | |
132 if (!terse) | |
133 msg("Your armor appears to be weaker now. Oh my!"); | |
134 else | |
135 msg("Your armor weakens"); | |
136 cur_armor->o_ac++; | |
137 } | |
138 when 'E': | |
139 /* | |
140 * The gaze of the floating eye hypnotizes you | |
141 */ | |
142 if (on(player, ISBLIND)) | |
143 break; | |
144 if (!no_command) | |
145 { | |
146 addmsg("You are transfixed"); | |
147 if (!terse) | |
148 addmsg(" by the gaze of the floating eye."); | |
149 endmsg(); | |
150 } | |
151 no_command += rnd(2)+2; | |
152 if (no_command > 100 && food_left <= 0) | |
153 death('E'); | |
154 when 'A': | |
155 /* | |
156 * Ants have poisonous bites | |
157 */ | |
158 if (!save(VS_POISON)) | |
159 if (!ISWEARING(R_SUSTSTR)) | |
160 { | |
161 chg_str(-1); | |
162 if (!terse) | |
163 msg("You feel a sting in your arm and now feel weaker"); | |
164 else | |
165 msg("A sting has weakened you"); | |
166 } | |
167 else | |
168 if (!terse) | |
169 msg("A sting momentarily weakens you"); | |
170 else | |
171 msg("Sting has no effect"); | |
172 when 'W': | |
173 /* | |
174 * Wraiths might drain energy levels | |
175 */ | |
176 if (rnd(100) < 15) | |
177 { | |
178 int fewer; | |
179 | |
180 if (pstats.s_exp == 0) | |
181 death('W'); /* All levels gone */ | |
182 msg("You suddenly feel weaker."); | |
183 if (--pstats.s_lvl == 0) | |
184 { | |
185 pstats.s_exp = 0; | |
186 pstats.s_lvl = 1; | |
187 } | |
188 else | |
189 pstats.s_exp = e_levels[pstats.s_lvl-1]+1; | |
190 fewer = roll(1, 10); | |
191 pstats.s_hpt -= fewer; | |
192 max_hp -= fewer; | |
193 if (pstats.s_hpt < 1) | |
194 pstats.s_hpt = 1; | |
195 if (max_hp < 1) | |
196 death('W'); | |
197 } | |
198 when 'F': | |
199 /* | |
200 * Violet fungi stops the poor guy from moving | |
201 */ | |
202 player.t_flags |= ISHELD; | |
203 sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dd1",++fung_hit); | |
204 when 'L': | |
205 { | |
206 /* | |
207 * Leperachaun steals some gold | |
208 */ | |
209 int lastpurse; | |
210 | |
211 lastpurse = purse; | |
212 purse -= GOLDCALC; | |
213 if (!save(VS_MAGIC)) | |
214 purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; | |
215 if (purse < 0) | |
216 purse = 0; | |
217 if (purse != lastpurse) | |
218 msg("Your purse feels lighter"); | |
219 remove_monster(&mp->t_pos, find_mons(mp->t_pos.y, mp->t_pos.x)); | |
220 mp = NULL; | |
221 } | |
222 when 'N': | |
223 { | |
224 struct linked_list *list, *steal; | |
225 struct object *obj; | |
226 int nobj; | |
227 | |
228 /* | |
229 * Nymph's steal a magic item, look through the pack | |
230 * and pick out one we like. | |
231 */ | |
232 steal = NULL; | |
233 for (nobj = 0, list = pack; list != NULL; list = next(list)) | |
234 { | |
235 obj = (struct object *) ldata(list); | |
236 if (obj != cur_armor && | |
237 obj != cur_weapon && | |
238 obj != cur_ring[LEFT] && | |
239 obj != cur_ring[RIGHT] && /* Nymph bug fix */ | |
240 is_magic(obj) && | |
241 rnd(++nobj) == 0) | |
242 steal = list; | |
243 } | |
244 if (steal != NULL) | |
245 { | |
246 struct object *sobj; | |
247 | |
248 sobj = (struct object *) ldata(steal); | |
249 remove_monster(&mp->t_pos, find_mons(mp->t_pos.y, mp->t_pos.x)); | |
250 mp = NULL; | |
251 if (sobj->o_count > 1 && sobj->o_group == 0) | |
252 { | |
253 int oc; | |
254 | |
255 oc = sobj->o_count; | |
256 sobj->o_count = 1; | |
257 msg("She stole %s!", inv_name(sobj, TRUE)); | |
258 sobj->o_count = oc - 1; | |
259 } | |
260 else | |
261 { | |
262 msg("She stole %s!", inv_name(sobj, TRUE)); | |
263 detach(pack, steal); | |
264 discard(steal); | |
265 } | |
266 inpack--; | |
267 } | |
268 } | |
269 otherwise: | |
270 break; | |
271 } | |
272 } | |
273 else if (mp->t_type != 'E') | |
274 { | |
275 if (mp->t_type == 'F') | |
276 { | |
277 pstats.s_hpt -= fung_hit; | |
278 if (pstats.s_hpt <= 0) | |
279 death(mp->t_type); /* Bye bye life ... */ | |
280 } | |
281 miss(mname, NULL); | |
282 } | |
283 /* | |
284 * Check to see if this is a regenerating monster and let it heal if | |
285 * it is. | |
286 */ | |
287 if ((mp != NULL) && (on(*mp, ISREGEN) && rnd(100) < 33)) | |
288 mp->t_stats.s_hpt++; | |
289 if (fight_flush) | |
290 { | |
291 flush_type(); /* flush typeahead */ | |
292 } | |
293 count = 0; | |
294 status(); | |
295 | |
296 if (mp == NULL) | |
297 return(-1); | |
298 else | |
299 return(0); | |
300 } | |
301 | |
302 /* | |
303 * swing: | |
304 * returns true if the swing hits | |
305 */ | |
306 | |
307 int | |
308 swing(int at_lvl, int op_arm, int wplus) | |
309 { | |
310 int res = rnd(20)+1; | |
311 int need = (21-at_lvl)-op_arm; | |
312 | |
313 return (res+wplus >= need); | |
314 } | |
315 | |
316 /* | |
317 * check_level: | |
318 * Check to see if the guy has gone up a level. | |
319 */ | |
320 | |
321 void | |
322 check_level() | |
323 { | |
324 int i, add; | |
325 | |
326 for (i = 0; e_levels[i] != 0; i++) | |
327 if (e_levels[i] > pstats.s_exp) | |
328 break; | |
329 i++; | |
330 if (i > pstats.s_lvl) | |
331 { | |
332 add = roll(i-pstats.s_lvl,10); | |
333 max_hp += add; | |
334 if ((pstats.s_hpt += add) > max_hp) | |
335 pstats.s_hpt = max_hp; | |
336 msg("Welcome to level %d", i); | |
337 } | |
338 pstats.s_lvl = i; | |
339 } | |
340 | |
341 /* | |
342 * roll_em: | |
343 * Roll several attacks | |
344 */ | |
345 | |
346 int | |
347 roll_em(struct stats *att, struct stats *def, struct object *weap, int hurl) | |
348 { | |
349 char *cp; | |
350 int ndice, nsides, def_arm; | |
351 int did_hit = FALSE; | |
352 int prop_hplus, prop_dplus; | |
353 | |
354 prop_hplus = prop_dplus = 0; | |
355 if (weap == NULL) | |
356 cp = att->s_dmg; | |
357 else if (hurl) | |
358 if ((weap->o_flags&ISMISL) && cur_weapon != NULL && | |
359 cur_weapon->o_which == weap->o_launch) | |
360 { | |
361 cp = weap->o_hurldmg; | |
362 prop_hplus = cur_weapon->o_hplus; | |
363 prop_dplus = cur_weapon->o_dplus; | |
364 } | |
365 else | |
366 cp = (weap->o_flags&ISMISL ? weap->o_damage : weap->o_hurldmg); | |
367 else | |
368 { | |
369 cp = weap->o_damage; | |
370 /* | |
371 * Drain a staff of striking | |
372 */ | |
373 if (weap->o_type == STICK && weap->o_which == WS_HIT | |
374 && weap->o_charges == 0) | |
375 { | |
376 strcpy(weap->o_damage,"0d0"); | |
377 weap->o_hplus = weap->o_dplus = 0; | |
378 } | |
379 } | |
380 for (;;) | |
381 { | |
382 int damage; | |
383 int hplus = prop_hplus + (weap == NULL ? 0 : weap->o_hplus); | |
384 int dplus = prop_dplus + (weap == NULL ? 0 : weap->o_dplus); | |
385 | |
386 if (weap == cur_weapon) | |
387 { | |
388 if (ISRING(LEFT, R_ADDDAM)) | |
389 dplus += cur_ring[LEFT]->o_ac; | |
390 else if (ISRING(LEFT, R_ADDHIT)) | |
391 hplus += cur_ring[LEFT]->o_ac; | |
392 if (ISRING(RIGHT, R_ADDDAM)) | |
393 dplus += cur_ring[RIGHT]->o_ac; | |
394 else if (ISRING(RIGHT, R_ADDHIT)) | |
395 hplus += cur_ring[RIGHT]->o_ac; | |
396 } | |
397 ndice = atoi(cp); | |
398 if ((cp = strchr(cp, 'd')) == NULL) | |
399 break; | |
400 nsides = atoi(++cp); | |
401 if (def == &pstats) | |
402 { | |
403 if (cur_armor != NULL) | |
404 def_arm = cur_armor->o_ac; | |
405 else | |
406 def_arm = def->s_arm; | |
407 if (ISRING(LEFT, R_PROTECT)) | |
408 def_arm -= cur_ring[LEFT]->o_ac; | |
409 else if (ISRING(RIGHT, R_PROTECT)) | |
410 def_arm -= cur_ring[RIGHT]->o_ac; | |
411 } | |
412 else | |
413 def_arm = def->s_arm; | |
414 if (swing(att->s_lvl, def_arm, hplus+str_plus(&att->s_str))) | |
415 { | |
416 int proll; | |
417 | |
418 proll = roll(ndice, nsides); | |
419 if (ndice + nsides > 0 && proll < 1) | |
420 debug("Damage for %dd%d came out %d.", ndice, nsides, proll); | |
421 damage = dplus + proll + add_dam(&att->s_str); | |
422 def->s_hpt -= max(0, damage); | |
423 did_hit = TRUE; | |
424 } | |
425 if ((cp = strchr(cp, '/')) == NULL) | |
426 break; | |
427 cp++; | |
428 } | |
429 return did_hit; | |
430 } | |
431 | |
432 /* | |
433 * prname: | |
434 * The print name of a combatant | |
435 */ | |
436 | |
437 char * | |
438 prname(who, upper) | |
439 char *who; | |
440 int upper; | |
441 { | |
442 static char tbuf[80]; | |
443 | |
444 *tbuf = '\0'; | |
445 if (who == 0) | |
446 strcpy(tbuf, "you"); | |
447 else if (on(player, ISBLIND)) | |
448 strcpy(tbuf, "it"); | |
449 else | |
450 { | |
451 strcpy(tbuf, "the "); | |
452 strcat(tbuf, who); | |
453 } | |
454 if (upper) | |
455 *tbuf = toupper(*tbuf); | |
456 return tbuf; | |
457 } | |
458 | |
459 /* | |
460 * hit: | |
461 * Print a message to indicate a succesful hit | |
462 */ | |
463 | |
464 void | |
465 hit(char *er, char *ee) | |
466 { | |
467 char *s = ""; | |
468 | |
469 addmsg(prname(er, TRUE)); | |
470 if (terse) | |
471 s = " hit."; | |
472 else | |
473 switch (rnd(4)) | |
474 { | |
475 case 0: s = " scored an excellent hit on "; | |
476 when 1: s = " hit "; | |
477 when 2: s = (er == 0 ? " have injured " : " has injured "); | |
478 when 3: s = (er == 0 ? " swing and hit " : " swings and hits "); | |
479 } | |
480 addmsg(s); | |
481 if (!terse) | |
482 addmsg(prname(ee, FALSE)); | |
483 endmsg(); | |
484 } | |
485 | |
486 /* | |
487 * miss: | |
488 * Print a message to indicate a poor swing | |
489 */ | |
490 | |
491 void | |
492 miss(char *er, char *ee) | |
493 { | |
494 char *s = ""; | |
495 | |
496 addmsg(prname(er, TRUE)); | |
497 switch (terse ? 0 : rnd(4)) | |
498 { | |
499 case 0: s = (er == 0 ? " miss" : " misses"); | |
500 when 1: s = (er == 0 ? " swing and miss" : " swings and misses"); | |
501 when 2: s = (er == 0 ? " barely miss" : " barely misses"); | |
502 when 3: s = (er == 0 ? " don't hit" : " doesn't hit"); | |
503 } | |
504 addmsg(s); | |
505 if (!terse) | |
506 addmsg(" %s", prname(ee, FALSE)); | |
507 endmsg(); | |
508 } | |
509 | |
510 /* | |
511 * save_throw: | |
512 * See if a creature save against something | |
513 */ | |
514 int | |
515 save_throw(int which, struct thing *tp) | |
516 { | |
517 int need; | |
518 | |
519 need = 14 + which - tp->t_stats.s_lvl / 2; | |
520 return (roll(1, 20) >= need); | |
521 } | |
522 | |
523 /* | |
524 * save: | |
525 * See if he saves against various nasty things | |
526 */ | |
527 | |
528 int | |
529 save(int which) | |
530 { | |
531 return save_throw(which, &player); | |
532 } | |
533 | |
534 /* | |
535 * str_plus: | |
536 * compute bonus/penalties for strength on the "to hit" roll | |
537 */ | |
538 | |
539 int | |
540 str_plus(str_t *str) | |
541 { | |
542 if (str->st_str == 18) | |
543 { | |
544 if (str->st_add == 100) | |
545 return 3; | |
546 if (str->st_add > 50) | |
547 return 2; | |
548 } | |
549 if (str->st_str >= 17) | |
550 return 1; | |
551 if (str->st_str > 6) | |
552 return 0; | |
553 return str->st_str - 7; | |
554 } | |
555 | |
556 /* | |
557 * add_dam: | |
558 * compute additional damage done for exceptionally high or low strength | |
559 */ | |
560 | |
561 int | |
562 add_dam(str_t *str) | |
563 { | |
564 if (str->st_str == 18) | |
565 { | |
566 if (str->st_add == 100) | |
567 return 6; | |
568 if (str->st_add > 90) | |
569 return 5; | |
570 if (str->st_add > 75) | |
571 return 4; | |
572 if (str->st_add != 0) | |
573 return 3; | |
574 return 2; | |
575 } | |
576 if (str->st_str > 15) | |
577 return 1; | |
578 if (str->st_str > 6) | |
579 return 0; | |
580 return str->st_str - 7; | |
581 } | |
582 | |
583 /* | |
584 * raise_level: | |
585 * The guy just magically went up a level. | |
586 */ | |
587 | |
588 void | |
589 raise_level() | |
590 { | |
591 pstats.s_exp = e_levels[pstats.s_lvl-1] + 1L; | |
592 check_level(); | |
593 } | |
594 | |
595 /* | |
596 * thunk: | |
597 * A missile hits a monster | |
598 */ | |
599 | |
600 void | |
601 thunk(struct object *weap, char *mname) | |
602 { | |
603 if (weap->o_type == WEAPON) | |
604 msg("The %s hits the %s", w_names[weap->o_which], mname); | |
605 else | |
606 msg("You hit the %s.", mname); | |
607 } | |
608 | |
609 /* | |
610 * bounce: | |
611 * A missile misses a monster | |
612 */ | |
613 | |
614 void | |
615 bounce(struct object *weap, char *mname) | |
616 { | |
617 if (weap->o_type == WEAPON) | |
618 msg("The %s misses the %s", w_names[weap->o_which], mname); | |
619 else | |
620 msg("You missed the %s.", mname); | |
621 } | |
622 | |
623 /* | |
624 * remove a monster from the screen | |
625 */ | |
626 void | |
627 remove_monster(coord *mp, struct linked_list *item) | |
628 { | |
629 mvwaddch(mw, mp->y, mp->x, ' '); | |
630 mvwaddch(cw, mp->y, mp->x, ((struct thing *) ldata(item))->t_oldch); | |
631 detach(mlist, item); | |
632 discard(item); | |
633 } | |
634 | |
635 /* | |
636 * is_magic: | |
637 * Returns true if an object radiates magic | |
638 */ | |
639 | |
640 int | |
641 is_magic(struct object *obj) | |
642 { | |
643 switch (obj->o_type) | |
644 { | |
645 case ARMOR: | |
646 return obj->o_ac != a_class[obj->o_which]; | |
647 when WEAPON: | |
648 return obj->o_hplus != 0 || obj->o_dplus != 0; | |
649 when POTION: | |
650 case SCROLL: | |
651 case STICK: | |
652 case RING: | |
653 case AMULET: | |
654 return TRUE; | |
655 } | |
656 return FALSE; | |
657 } | |
658 | |
659 /* | |
660 * killed: | |
661 * Called to put a monster to death | |
662 */ | |
663 | |
664 void | |
665 killed(struct linked_list *item, int pr) | |
666 { | |
667 struct thing *tp; | |
668 struct linked_list *pitem, *nexti; | |
669 | |
670 tp = (struct thing *) ldata(item); | |
671 if (pr) | |
672 { | |
673 addmsg(terse ? "Defeated " : "You have defeated "); | |
674 if (on(player, ISBLIND)) | |
675 msg("it."); | |
676 else | |
677 { | |
678 if (!terse) | |
679 addmsg("the "); | |
680 msg("%s.", monsters[tp->t_type-'A'].m_name); | |
681 } | |
682 } | |
683 pstats.s_exp += tp->t_stats.s_exp; | |
684 /* | |
685 * Do adjustments if he went up a level | |
686 */ | |
687 check_level(); | |
688 /* | |
689 * If the monster was a violet fungi, un-hold him | |
690 */ | |
691 switch (tp->t_type) | |
692 { | |
693 case 'F': | |
694 player.t_flags &= ~ISHELD; | |
695 fung_hit = 0; | |
696 strcpy(monsters['F'-'A'].m_stats.s_dmg, "000d0"); | |
697 when 'L': | |
698 { | |
699 struct room *rp; | |
700 | |
701 if ((rp = roomin(&tp->t_pos)) == NULL) | |
702 break; | |
703 if (rp->r_goldval != 0 || fallpos(&tp->t_pos,&rp->r_gold,FALSE)) | |
704 { | |
705 rp->r_goldval += GOLDCALC; | |
706 if (save(VS_MAGIC)) | |
707 rp->r_goldval += GOLDCALC + GOLDCALC | |
708 + GOLDCALC + GOLDCALC; | |
709 mvwaddch(stdscr, rp->r_gold.y, rp->r_gold.x, GOLD); | |
710 if (!(rp->r_flags & ISDARK)) | |
711 { | |
712 light(&hero); | |
713 mvwaddch(cw, hero.y, hero.x, PLAYER); | |
714 } | |
715 } | |
716 } | |
717 } | |
718 /* | |
719 * Empty the monsters pack | |
720 */ | |
721 pitem = tp->t_pack; | |
722 while (pitem != NULL) | |
723 { | |
724 struct object *obj; | |
725 | |
726 nexti = next(tp->t_pack); | |
727 obj = (struct object *) ldata(pitem); | |
728 obj->o_pos = tp->t_pos; | |
729 detach(tp->t_pack, pitem); | |
730 fall(pitem, FALSE); | |
731 pitem = nexti; | |
732 } | |
733 /* | |
734 * Get rid of the monster. | |
735 */ | |
736 remove_monster(&tp->t_pos, item); | |
737 } |