rogue

Ken Arnold's Rogue
git clone https://git.woozle.org/neale/rogue.git

Neale Pickett  ·  2011-10-13

continue.c

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