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