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