comparison rogue5/fight.c @ 33:f502bf60e6e4

Import Rogue 5.4 from the Roguelike Restoration Project (r1490)
author elwin
date Mon, 24 May 2010 20:10:59 +0000
parents
children e7aab31362af
comparison
equal deleted inserted replaced
32:2dcd75e6a736 33:f502bf60e6e4
1 /*
2 * All the fighting gets done here
3 *
4 * @(#)fight.c 4.67 (Berkeley) 09/06/83
5 *
6 * Rogue: Exploring the Dungeons of Doom
7 * Copyright (C) 1980-1983, 1985, 1999 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 <stdlib.h>
14 #include <curses.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include "rogue.h"
18
19 #define EQSTR(a, b) (strcmp(a, b) == 0)
20
21 static const char *h_names[] = { /* strings for hitting */
22 " scored an excellent hit on ",
23 " hit ",
24 " have injured ",
25 " swing and hit ",
26 " scored an excellent hit on ",
27 " hit ",
28 " has injured ",
29 " swings and hits "
30 };
31
32 static const char *m_names[] = { /* strings for missing */
33 " miss",
34 " swing and miss",
35 " barely miss",
36 " don't hit",
37 " misses",
38 " swings and misses",
39 " barely misses",
40 " doesn't hit",
41 };
42
43 /*
44 * adjustments to hit probabilities due to strength
45 */
46 static int str_plus[] = {
47 -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
48 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
49 };
50
51 /*
52 * adjustments to damage done due to strength
53 */
54 static int add_dam[] = {
55 -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3,
56 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6
57 };
58
59 /*
60 * fight:
61 * The player attacks the monster.
62 */
63 int
64 fight(const coord *mp, const THING *weap, int thrown)
65 {
66 THING *tp;
67 int did_hit = TRUE;
68 const char *mname;
69 int ch;
70
71 /*
72 * Find the monster we want to fight
73 */
74 if ((tp = moat(mp->y, mp->x)) == NULL)
75 {
76 #ifdef MASTER
77 debug("Fight what @ %d,%d", mp->y, mp->x);
78 #endif
79 return FALSE;
80 }
81 /*
82 * Since we are fighting, things are not quiet so no healing takes
83 * place.
84 */
85 count = 0;
86 quiet = 0;
87 runto(mp);
88 /*
89 * Let him know it was really a xeroc (if it was one).
90 */
91 ch = '\0';
92 if (tp->t_type == 'X' && tp->t_disguise != 'X' && !on(player, ISBLIND))
93 {
94 tp->t_disguise = 'X';
95 if (on(player, ISHALU)) {
96 ch = rnd(26) + 'A';
97 mvaddch(tp->t_pos.y, tp->t_pos.x, ch);
98 }
99 msg(choose_str("heavy! That's a nasty critter!",
100 "wait! That's a xeroc!"));
101 if (!thrown)
102 return FALSE;
103 }
104 mname = set_mname(tp);
105 did_hit = FALSE;
106 has_hit = (terse && !to_death);
107 if (roll_em(&player, tp, weap, thrown))
108 {
109 did_hit = FALSE;
110 if (thrown)
111 thunk(weap, mname, terse);
112 else
113 hit(NULL, mname, terse);
114 if (on(player, CANHUH))
115 {
116 did_hit = TRUE;
117 tp->t_flags |= ISHUH;
118 player.t_flags &= ~CANHUH;
119 endmsg();
120 has_hit = FALSE;
121 msg("your hands stop glowing %s", pick_color("red"));
122 }
123 if (tp->t_stats.s_hpt <= 0)
124 killed(tp, TRUE);
125 else if (did_hit && !on(player, ISBLIND))
126 msg("%s appears confused", mname);
127 did_hit = TRUE;
128 }
129 else
130 if (thrown)
131 bounce(weap, mname, terse);
132 else
133 miss(NULL, mname, terse);
134 return did_hit;
135 }
136
137 /*
138 * attack:
139 * The monster attacks the player
140 */
141 int
142 attack(THING *mp)
143 {
144 const char *mname;
145 int oldhp;
146
147 /*
148 * Since this is an attack, stop running and any healing that was
149 * going on at the time.
150 */
151 running = FALSE;
152 count = 0;
153 quiet = 0;
154 if (to_death && !on(*mp, ISTARGET))
155 {
156 to_death = FALSE;
157 kamikaze = FALSE;
158 }
159 if (mp->t_type == 'X' && mp->t_disguise != 'X' && !on(player, ISBLIND))
160 {
161 mp->t_disguise = 'X';
162 if (on(player, ISHALU))
163 mvaddch(mp->t_pos.y, mp->t_pos.x, rnd(26) + 'A');
164 }
165 mname = set_mname(mp);
166 oldhp = pstats.s_hpt;
167 if (roll_em(mp, &player, NULL, FALSE))
168 {
169 if (mp->t_type != 'I')
170 {
171 if (has_hit)
172 addmsg(". ");
173 hit(mname, NULL, FALSE);
174 }
175 else
176 if (has_hit)
177 endmsg();
178 has_hit = FALSE;
179 if (pstats.s_hpt <= 0)
180 death(mp->t_type); /* Bye bye life ... */
181 else if (!kamikaze)
182 {
183 oldhp -= pstats.s_hpt;
184 if (oldhp > max_hit)
185 max_hit = oldhp;
186 if (pstats.s_hpt <= max_hit)
187 to_death = FALSE;
188 }
189 if (!on(*mp, ISCANC))
190 switch (mp->t_type)
191 {
192 case 'A':
193 /*
194 * If an aquator hits, you can lose armor class.
195 */
196 rust_armor(cur_armor);
197 when 'I':
198 /*
199 * The ice monster freezes you
200 */
201 player.t_flags &= ~ISRUN;
202 if (!no_command)
203 {
204 addmsg("you are frozen");
205 if (!terse)
206 addmsg(" by the %s", mname);
207 endmsg();
208 }
209 no_command += rnd(2) + 2;
210 if (no_command > BORE_LEVEL)
211 death('h');
212 when 'R':
213 /*
214 * Rattlesnakes have poisonous bites
215 */
216 if (!save(VS_POISON))
217 {
218 if (!ISWEARING(R_SUSTSTR))
219 {
220 chg_str(-1);
221 if (!terse)
222 msg("you feel a bite in your leg and now feel weaker");
223 else
224 msg("a bite has weakened you");
225 }
226 else if (!to_death)
227 {
228 if (!terse)
229 msg("a bite momentarily weakens you");
230 else
231 msg("bite has no effect");
232 }
233 }
234 when 'W':
235 case 'V':
236 /*
237 * Wraiths might drain energy levels, and Vampires
238 * can steal max_hp
239 */
240 if (rnd(100) < (mp->t_type == 'W' ? 15 : 30))
241 {
242 int fewer;
243
244 if (mp->t_type == 'W')
245 {
246 if (pstats.s_exp == 0)
247 death('W'); /* All levels gone */
248 if (--pstats.s_lvl == 0)
249 {
250 pstats.s_exp = 0;
251 pstats.s_lvl = 1;
252 }
253 else
254 pstats.s_exp = e_levels[pstats.s_lvl-1]+1;
255 fewer = roll(1, 10);
256 }
257 else
258 fewer = roll(1, 3);
259 pstats.s_hpt -= fewer;
260 max_hp -= fewer;
261 if (pstats.s_hpt <= 0)
262 pstats.s_hpt = 1;
263 if (max_hp <= 0)
264 death(mp->t_type);
265 msg("you suddenly feel weaker");
266 }
267 when 'F':
268 /*
269 * Venus Flytrap stops the poor guy from moving
270 */
271 player.t_flags |= ISHELD;
272 sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dx1", ++vf_hit);
273 if (--pstats.s_hpt <= 0)
274 death('F');
275 when 'L':
276 {
277 /*
278 * Leperachaun steals some gold
279 */
280 int lastpurse;
281
282 lastpurse = purse;
283 purse -= GOLDCALC;
284 if (!save(VS_MAGIC))
285 purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC;
286 if (purse < 0)
287 purse = 0;
288 remove_mon(&mp->t_pos, mp, FALSE);
289 mp=NULL;
290 if (purse != lastpurse)
291 msg("your purse feels lighter");
292 }
293 when 'N':
294 {
295 THING *obj, *steal;
296 int nobj;
297
298 /*
299 * Nymph's steal a magic item, look through the pack
300 * and pick out one we like.
301 */
302 steal = NULL;
303 for (nobj = 0, obj = pack; obj != NULL; obj = next(obj))
304 if (obj != cur_armor && obj != cur_weapon
305 && obj != cur_ring[LEFT] && obj != cur_ring[RIGHT]
306 && is_magic(obj) && rnd(++nobj) == 0)
307 steal = obj;
308 if (steal != NULL)
309 {
310 remove_mon(&mp->t_pos, moat(mp->t_pos.y, mp->t_pos.x), FALSE);
311 mp=NULL;
312 steal = leave_pack(steal, TRUE, FALSE);
313 msg("she stole %s!", inv_name(steal, TRUE));
314 discard(steal);
315 }
316 }
317 otherwise:
318 break;
319 }
320 }
321 else if (mp->t_type != 'I')
322 {
323 if (has_hit)
324 {
325 addmsg(". ");
326 has_hit = FALSE;
327 }
328 if (mp->t_type == 'F')
329 {
330 pstats.s_hpt -= vf_hit;
331 if (pstats.s_hpt <= 0)
332 death(mp->t_type); /* Bye bye life ... */
333 }
334 miss(mname, NULL, FALSE);
335 }
336 if (fight_flush && !to_death)
337 flush_type();
338 count = 0;
339 status();
340 if (mp == NULL)
341 return(-1);
342 else
343 return(0);
344 }
345
346 /*
347 * set_mname:
348 * return the monster name for the given monster
349 */
350 const char *
351 set_mname(const THING *tp)
352 {
353 int ch;
354 const char *mname;
355 static char tbuf[MAXSTR] = { 't', 'h', 'e', ' ' };
356
357 if (!see_monst(tp) && !on(player, SEEMONST))
358 return (terse ? "it" : "something");
359 else if (on(player, ISHALU))
360 {
361 move(tp->t_pos.y, tp->t_pos.x);
362 ch = toascii(CCHAR(inch()));
363 if (!isupper(ch))
364 ch = rnd(26);
365 else
366 ch -= 'A';
367 mname = monsters[ch].m_name;
368 }
369 else
370 mname = monsters[tp->t_type - 'A'].m_name;
371 strcpy(&tbuf[4], mname);
372 return tbuf;
373 }
374
375 /*
376 * swing:
377 * Returns true if the swing hits
378 */
379 int
380 swing(int at_lvl, int op_arm, int wplus)
381 {
382 int res = rnd(20);
383 int need = (20 - at_lvl) - op_arm;
384
385 return (res + wplus >= need);
386 }
387
388 /*
389 * roll_em:
390 * Roll several attacks
391 */
392 int
393 roll_em(const THING *thatt, THING *thdef, const THING *weap, int hurl)
394 {
395 const struct stats *att;
396 struct stats *def;
397 const char *cp;
398 int ndice, nsides, def_arm;
399 int did_hit = FALSE;
400 int hplus;
401 int dplus;
402 int damage;
403
404 att = &thatt->t_stats;
405 def = &thdef->t_stats;
406 if (weap == NULL)
407 {
408 cp = att->s_dmg;
409 dplus = 0;
410 hplus = 0;
411 }
412 else
413 {
414 hplus = (weap == NULL ? 0 : weap->o_hplus);
415 dplus = (weap == NULL ? 0 : weap->o_dplus);
416 if (weap == cur_weapon)
417 {
418 if (ISRING(LEFT, R_ADDDAM))
419 dplus += cur_ring[LEFT]->o_arm;
420 else if (ISRING(LEFT, R_ADDHIT))
421 hplus += cur_ring[LEFT]->o_arm;
422 if (ISRING(RIGHT, R_ADDDAM))
423 dplus += cur_ring[RIGHT]->o_arm;
424 else if (ISRING(RIGHT, R_ADDHIT))
425 hplus += cur_ring[RIGHT]->o_arm;
426 }
427 cp = weap->o_damage;
428 if (hurl)
429 {
430 if ((weap->o_flags&ISMISL) && cur_weapon != NULL &&
431 cur_weapon->o_which == weap->o_launch)
432 {
433 cp = weap->o_hurldmg;
434 hplus += cur_weapon->o_hplus;
435 dplus += cur_weapon->o_dplus;
436 }
437 else if (weap->o_launch < 0)
438 cp = weap->o_hurldmg;
439 }
440 }
441 /*
442 * If the creature being attacked is not running (alseep or held)
443 * then the attacker gets a plus four bonus to hit.
444 */
445 if (!on(*thdef, ISRUN))
446 hplus += 4;
447 def_arm = def->s_arm;
448 if (def == &pstats)
449 {
450 if (cur_armor != NULL)
451 def_arm = cur_armor->o_arm;
452 if (ISRING(LEFT, R_PROTECT))
453 def_arm -= cur_ring[LEFT]->o_arm;
454 if (ISRING(RIGHT, R_PROTECT))
455 def_arm -= cur_ring[RIGHT]->o_arm;
456 }
457 while(cp != NULL && *cp != '\0')
458 {
459 ndice = atoi(cp);
460 if ((cp = strchr(cp, 'x')) == NULL)
461 break;
462 nsides = atoi(++cp);
463 if (swing(att->s_lvl, def_arm, hplus + str_plus[att->s_str]))
464 {
465 int proll;
466
467 proll = roll(ndice, nsides);
468 #ifdef MASTER
469 if (ndice + nsides > 0 && proll <= 0)
470 debug("Damage for %dx%d came out %d, dplus = %d, add_dam = %d, def_arm = %d", ndice, nsides, proll, dplus, add_dam[att->s_str], def_arm);
471 #endif
472 damage = dplus + proll + add_dam[att->s_str];
473 def->s_hpt -= max(0, damage);
474 did_hit = TRUE;
475 }
476 if ((cp = strchr(cp, '/')) == NULL)
477 break;
478 cp++;
479 }
480 return did_hit;
481 }
482
483 /*
484 * prname:
485 * The print name of a combatant
486 */
487 char *
488 prname(const char *mname, int upper)
489 {
490 static char tbuf[MAXSTR];
491
492 *tbuf = '\0';
493 if (mname == 0)
494 strcpy(tbuf, "you");
495 else
496 strcpy(tbuf, mname);
497 if (upper)
498 *tbuf = (char) toupper(*tbuf);
499 return tbuf;
500 }
501
502 /*
503 * thunk:
504 * A missile hits a monster
505 */
506 void
507 thunk(const THING *weap, const char *mname, int noend)
508 {
509 if (to_death)
510 return;
511 if (weap->o_type == WEAPON)
512 addmsg("the %s hits ", weap_info[weap->o_which].oi_name);
513 else
514 addmsg("you hit ");
515 addmsg("%s", mname);
516 if (!noend)
517 endmsg();
518 }
519
520 /*
521 * hit:
522 * Print a message to indicate a succesful hit
523 */
524
525 void
526 hit(const char *er, const char *ee, int noend)
527 {
528 int i;
529 const char *s;
530
531 if (to_death)
532 return;
533 addmsg(prname(er, TRUE));
534 if (terse)
535 s = " hit";
536 else
537 {
538 i = rnd(4);
539 if (er != NULL)
540 i += 4;
541 s = h_names[i];
542 }
543 addmsg(s);
544 if (!terse)
545 addmsg(prname(ee, FALSE));
546 if (!noend)
547 endmsg();
548 }
549
550 /*
551 * miss:
552 * Print a message to indicate a poor swing
553 */
554 void
555 miss(const char *er, const char *ee, int noend)
556 {
557 int i;
558
559 if (to_death)
560 return;
561 addmsg(prname(er, TRUE));
562 if (terse)
563 i = 0;
564 else
565 i = rnd(4);
566 if (er != NULL)
567 i += 4;
568 addmsg(m_names[i]);
569 if (!terse)
570 addmsg(" %s", prname(ee, FALSE));
571 if (!noend)
572 endmsg();
573 }
574
575 /*
576 * bounce:
577 * A missile misses a monster
578 */
579 void
580 bounce(const THING *weap, const char *mname, int noend)
581 {
582 if (to_death)
583 return;
584 if (weap->o_type == WEAPON)
585 addmsg("the %s misses ", weap_info[weap->o_which].oi_name);
586 else
587 addmsg("you missed ");
588 addmsg(mname);
589 if (!noend)
590 endmsg();
591 }
592
593 /*
594 * remove_mon:
595 * Remove a monster from the screen
596 */
597 void
598 remove_mon(const coord *mp, THING *tp, int waskill)
599 {
600 THING *obj, *nexti;
601
602 for (obj = tp->t_pack; obj != NULL; obj = nexti)
603 {
604 nexti = next(obj);
605 obj->o_pos = tp->t_pos;
606 detach(tp->t_pack, obj);
607 if (waskill)
608 fall(obj, FALSE);
609 else
610 discard(obj);
611 }
612 moat(mp->y, mp->x) = NULL;
613 mvaddch(mp->y, mp->x, tp->t_oldch);
614 detach(mlist, tp);
615 if (on(*tp, ISTARGET))
616 {
617 kamikaze = FALSE;
618 to_death = FALSE;
619 if (fight_flush)
620 flush_type();
621 }
622 discard(tp);
623 }
624
625 /*
626 * killed:
627 * Called to put a monster to death
628 */
629 void
630 killed(THING *tp, int pr)
631 {
632 const char *mname;
633
634 pstats.s_exp += tp->t_stats.s_exp;
635
636 /*
637 * If the monster was a venus flytrap, un-hold him
638 */
639 switch (tp->t_type)
640 {
641 case 'F':
642 player.t_flags &= ~ISHELD;
643 vf_hit = 0;
644 strcpy(monsters['F'-'A'].m_stats.s_dmg, "000x0");
645 when 'L':
646 {
647 THING *gold;
648
649 if (fallpos(&tp->t_pos, &tp->t_room->r_gold) && level >= max_level)
650 {
651 gold = new_item();
652 gold->o_type = GOLD;
653 gold->o_goldval = GOLDCALC;
654 if (save(VS_MAGIC))
655 gold->o_goldval += GOLDCALC + GOLDCALC
656 + GOLDCALC + GOLDCALC;
657 attach(tp->t_pack, gold);
658 }
659 }
660 }
661 /*
662 * Get rid of the monster.
663 */
664 mname = set_mname(tp);
665 remove_mon(&tp->t_pos, tp, TRUE);
666 if (pr)
667 {
668 if (has_hit)
669 {
670 addmsg(". Defeated ");
671 has_hit = FALSE;
672 }
673 else
674 {
675 if (!terse)
676 addmsg("you have ");
677 addmsg("defeated ");
678 }
679 msg(mname);
680 }
681 /*
682 * Do adjustments if he went up a level
683 */
684 check_level();
685 if (fight_flush)
686 flush_type();
687 }