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