/* * All the fighting gets done here * * @(#)fight.c 3.28 (Berkeley) 6/15/81 * * Rogue: Exploring the Dungeons of Doom * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman * All rights reserved. * * See the file LICENSE.TXT for full copyright and licensing information. */ #include "curses.h" #include #include #include #include "rogue.h" int e_levels[] = { 10,20,40,80,160,320,640,1280,2560,5120,10240,20480, 40920, 81920, 163840, 327680, 655360, 1310720, 2621440, 0 }; /* * fight: * The player attacks the monster. */ int fight(coord *mp, int mn, struct object *weap, int thrown) { struct thing *tp; struct linked_list *item; int did_hit = TRUE; /* * Find the monster we want to fight */ if ((item = find_mons(mp->y, mp->x)) == NULL) { debug("Fight what @ %d,%d", mp->y, mp->x); return(0); } tp = (struct thing *) ldata(item); /* * Since we are fighting, things are not quiet so no healing takes * place. */ quiet = 0; runto(mp, &hero); /* * Let him know it was really a mimic (if it was one). */ if (tp->t_type == 'M' && tp->t_disguise != 'M' && off(player, ISBLIND)) { msg("Wait! That's a mimic!"); tp->t_disguise = 'M'; did_hit = thrown; } if (did_hit) { char *mname; did_hit = FALSE; if (on(player, ISBLIND)) mname = "it"; else mname = monsters[mn-'A'].m_name; if (roll_em(&pstats, &tp->t_stats, weap, thrown)) { did_hit = TRUE; if (thrown) thunk(weap, mname); else hit(NULL, mname); if (on(player, CANHUH)) { msg("Your hands stop glowing red"); msg("The %s appears confused.", mname); tp->t_flags |= ISHUH; player.t_flags &= ~CANHUH; } if (tp->t_stats.s_hpt <= 0) killed(item, TRUE); } else if (thrown) bounce(weap, mname); else miss(NULL, mname); } count = 0; return did_hit; } /* * attack: * The monster attacks the player */ int attack(struct thing *mp) { char *mname; /* * Since this is an attack, stop running and any healing that was * going on at the time. */ running = FALSE; quiet = 0; if (mp->t_type == 'F') fung_hit = atoi(mp->t_stats.s_dmg); if (mp->t_type == 'M' && off(player, ISBLIND)) mp->t_disguise = 'M'; if (on(player, ISBLIND)) mname = "it"; else mname = monsters[mp->t_type-'A'].m_name; if (roll_em(&mp->t_stats, &pstats, NULL, FALSE)) { if (mp->t_type != 'E') hit(mname, NULL); if (pstats.s_hpt <= 0) death(mp->t_type); /* Bye bye life ... */ if (off(*mp, ISCANC)) switch (mp->t_type) { case 'R': /* * If a rust monster hits, you lose armor */ if (cur_armor != NULL && cur_armor->o_ac < 9) { if (!terse) msg("Your armor appears to be weaker now. Oh my!"); else msg("Your armor weakens"); cur_armor->o_ac++; } when 'E': /* * The gaze of the floating eye hypnotizes you */ if (on(player, ISBLIND)) break; if (!no_command) { addmsg("You are transfixed"); if (!terse) addmsg(" by the gaze of the floating eye."); endmsg(); } no_command += rnd(2)+2; if (no_command > 100 && food_left <= 0) death('E'); when 'A': /* * Ants have poisonous bites */ if (!save(VS_POISON)) if (!ISWEARING(R_SUSTSTR)) { chg_str(-1); if (!terse) msg("You feel a sting in your arm and now feel weaker"); else msg("A sting has weakened you"); } else if (!terse) msg("A sting momentarily weakens you"); else msg("Sting has no effect"); when 'W': /* * Wraiths might drain energy levels */ if (rnd(100) < 15) { int fewer; if (pstats.s_exp == 0) death('W'); /* All levels gone */ msg("You suddenly feel weaker."); if (--pstats.s_lvl == 0) { pstats.s_exp = 0; pstats.s_lvl = 1; } else pstats.s_exp = e_levels[pstats.s_lvl-1]+1; fewer = roll(1, 10); pstats.s_hpt -= fewer; max_hp -= fewer; if (pstats.s_hpt < 1) pstats.s_hpt = 1; if (max_hp < 1) death('W'); } when 'F': /* * Violet fungi stops the poor guy from moving */ player.t_flags |= ISHELD; sprintf(mp->t_stats.s_dmg, "%dd1", ++fung_hit); when 'L': { /* * Leperachaun steals some gold */ int lastpurse; lastpurse = purse; purse -= GOLDCALC; if (!save(VS_MAGIC)) purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; if (purse < 0) purse = 0; if (purse != lastpurse) msg("Your purse feels lighter"); remove_monster(&mp->t_pos, find_mons(mp->t_pos.y, mp->t_pos.x)); mp = NULL; } when 'N': { struct linked_list *list, *steal; struct object *obj; int nobj; /* * Nymph's steal a magic item, look through the pack * and pick out one we like. */ steal = NULL; for (nobj = 0, list = pack; list != NULL; list = next(list)) { obj = (struct object *) ldata(list); if (obj != cur_armor && obj != cur_weapon && obj != cur_ring[LEFT] && obj != cur_ring[RIGHT] && /* Nymph bug fix */ is_magic(obj) && rnd(++nobj) == 0) steal = list; } if (steal != NULL) { struct object *sobj; sobj = (struct object *) ldata(steal); remove_monster(&mp->t_pos, find_mons(mp->t_pos.y, mp->t_pos.x)); mp = NULL; if (sobj->o_count > 1 && sobj->o_group == 0) { int oc; oc = sobj->o_count; sobj->o_count = 1; msg("She stole %s!", inv_name(sobj, TRUE)); sobj->o_count = oc - 1; } else { msg("She stole %s!", inv_name(sobj, TRUE)); detach(pack, steal); discard(steal); } inpack--; } } otherwise: break; } } else if (mp->t_type != 'E') { if (mp->t_type == 'F') { pstats.s_hpt -= fung_hit; if (pstats.s_hpt <= 0) death(mp->t_type); /* Bye bye life ... */ } miss(mname, NULL); } /* * Check to see if this is a regenerating monster and let it heal if * it is. */ if ((mp != NULL) && (on(*mp, ISREGEN) && rnd(100) < 33)) mp->t_stats.s_hpt++; if (fight_flush) { flush_type(); /* flush typeahead */ } count = 0; status(); if (mp == NULL) return(-1); else return(0); } /* * swing: * returns true if the swing hits */ int swing(int at_lvl, int op_arm, int wplus) { int res = rnd(20)+1; int need = (21-at_lvl)-op_arm; return (res+wplus >= need); } /* * check_level: * Check to see if the guy has gone up a level. */ void check_level() { int i, add; for (i = 0; e_levels[i] != 0; i++) if (e_levels[i] > pstats.s_exp) break; i++; if (i > pstats.s_lvl) { add = roll(i-pstats.s_lvl,10); max_hp += add; if ((pstats.s_hpt += add) > max_hp) pstats.s_hpt = max_hp; msg("Welcome to level %d", i); } pstats.s_lvl = i; } /* * roll_em: * Roll several attacks */ int roll_em(struct stats *att, struct stats *def, struct object *weap, int hurl) { char *cp; int ndice, nsides, def_arm; int did_hit = FALSE; int prop_hplus, prop_dplus; prop_hplus = prop_dplus = 0; if (weap == NULL) cp = att->s_dmg; else if (hurl) if ((weap->o_flags&ISMISL) && cur_weapon != NULL && cur_weapon->o_which == weap->o_launch) { cp = weap->o_hurldmg; prop_hplus = cur_weapon->o_hplus; prop_dplus = cur_weapon->o_dplus; } else cp = (weap->o_flags&ISMISL ? weap->o_damage : weap->o_hurldmg); else { cp = weap->o_damage; /* * Drain a staff of striking */ if (weap->o_type == STICK && weap->o_which == WS_HIT && weap->o_charges == 0) { strcpy(weap->o_damage,"0d0"); weap->o_hplus = weap->o_dplus = 0; } } for (;;) { int damage; int hplus = prop_hplus + (weap == NULL ? 0 : weap->o_hplus); int dplus = prop_dplus + (weap == NULL ? 0 : weap->o_dplus); if (weap == cur_weapon) { if (ISRING(LEFT, R_ADDDAM)) dplus += cur_ring[LEFT]->o_ac; else if (ISRING(LEFT, R_ADDHIT)) hplus += cur_ring[LEFT]->o_ac; if (ISRING(RIGHT, R_ADDDAM)) dplus += cur_ring[RIGHT]->o_ac; else if (ISRING(RIGHT, R_ADDHIT)) hplus += cur_ring[RIGHT]->o_ac; } ndice = atoi(cp); if ((cp = strchr(cp, 'd')) == NULL) break; nsides = atoi(++cp); if (def == &pstats) { if (cur_armor != NULL) def_arm = cur_armor->o_ac; else def_arm = def->s_arm; if (ISRING(LEFT, R_PROTECT)) def_arm -= cur_ring[LEFT]->o_ac; else if (ISRING(RIGHT, R_PROTECT)) def_arm -= cur_ring[RIGHT]->o_ac; } else def_arm = def->s_arm; if (swing(att->s_lvl, def_arm, hplus+str_plus(&att->s_str))) { int proll; proll = roll(ndice, nsides); if (ndice + nsides > 0 && proll < 1) debug("Damage for %dd%d came out %d.", ndice, nsides, proll); damage = dplus + proll + add_dam(&att->s_str); def->s_hpt -= max(0, damage); did_hit = TRUE; } if ((cp = strchr(cp, '/')) == NULL) break; cp++; } return did_hit; } /* * prname: * The print name of a combatant */ char * prname(who, upper) char *who; int upper; { static char tbuf[80]; *tbuf = '\0'; if (who == 0) strcpy(tbuf, "you"); else if (on(player, ISBLIND)) strcpy(tbuf, "it"); else { strcpy(tbuf, "the "); strcat(tbuf, who); } if (upper) *tbuf = toupper(*tbuf); return tbuf; } /* * hit: * Print a message to indicate a succesful hit */ void hit(char *er, char *ee) { char *s = ""; addmsg(prname(er, TRUE)); if (terse) s = " hit."; else switch (rnd(4)) { case 0: s = " scored an excellent hit on "; when 1: s = " hit "; when 2: s = (er == 0 ? " have injured " : " has injured "); when 3: s = (er == 0 ? " swing and hit " : " swings and hits "); } addmsg(s); if (!terse) addmsg(prname(ee, FALSE)); endmsg(); } /* * miss: * Print a message to indicate a poor swing */ void miss(char *er, char *ee) { char *s = ""; addmsg(prname(er, TRUE)); switch (terse ? 0 : rnd(4)) { case 0: s = (er == 0 ? " miss" : " misses"); when 1: s = (er == 0 ? " swing and miss" : " swings and misses"); when 2: s = (er == 0 ? " barely miss" : " barely misses"); when 3: s = (er == 0 ? " don't hit" : " doesn't hit"); } addmsg(s); if (!terse) addmsg(" %s", prname(ee, FALSE)); endmsg(); } /* * save_throw: * See if a creature save against something */ int save_throw(int which, struct thing *tp) { int need; need = 14 + which - tp->t_stats.s_lvl / 2; return (roll(1, 20) >= need); } /* * save: * See if he saves against various nasty things */ int save(int which) { return save_throw(which, &player); } /* * str_plus: * compute bonus/penalties for strength on the "to hit" roll */ int str_plus(str_t *str) { if (str->st_str == 18) { if (str->st_add == 100) return 3; if (str->st_add > 50) return 2; } if (str->st_str >= 17) return 1; if (str->st_str > 6) return 0; return str->st_str - 7; } /* * add_dam: * compute additional damage done for exceptionally high or low strength */ int add_dam(str_t *str) { if (str->st_str == 18) { if (str->st_add == 100) return 6; if (str->st_add > 90) return 5; if (str->st_add > 75) return 4; if (str->st_add != 0) return 3; return 2; } if (str->st_str > 15) return 1; if (str->st_str > 6) return 0; return str->st_str - 7; } /* * raise_level: * The guy just magically went up a level. */ void raise_level() { pstats.s_exp = e_levels[pstats.s_lvl-1] + 1L; check_level(); } /* * thunk: * A missile hits a monster */ void thunk(struct object *weap, char *mname) { if (weap->o_type == WEAPON) msg("The %s hits the %s", w_names[weap->o_which], mname); else msg("You hit the %s.", mname); } /* * bounce: * A missile misses a monster */ void bounce(struct object *weap, char *mname) { if (weap->o_type == WEAPON) msg("The %s misses the %s", w_names[weap->o_which], mname); else msg("You missed the %s.", mname); } /* * remove a monster from the screen */ void remove_monster(coord *mp, struct linked_list *item) { mvwaddch(mw, mp->y, mp->x, ' '); mvwaddch(cw, mp->y, mp->x, ((struct thing *) ldata(item))->t_oldch); detach(mlist, item); discard(item); } /* * is_magic: * Returns true if an object radiates magic */ int is_magic(struct object *obj) { switch (obj->o_type) { case ARMOR: return obj->o_ac != a_class[obj->o_which]; when WEAPON: return obj->o_hplus != 0 || obj->o_dplus != 0; when POTION: case SCROLL: case STICK: case RING: case AMULET: return TRUE; } return FALSE; } /* * killed: * Called to put a monster to death */ void killed(struct linked_list *item, int pr) { struct thing *tp; struct linked_list *pitem, *nexti; tp = (struct thing *) ldata(item); if (pr) { addmsg(terse ? "Defeated " : "You have defeated "); if (on(player, ISBLIND)) msg("it."); else { if (!terse) addmsg("the "); msg("%s.", monsters[tp->t_type-'A'].m_name); } } pstats.s_exp += tp->t_stats.s_exp; /* * Do adjustments if he went up a level */ check_level(); /* * If the monster was a violet fungi, un-hold him */ switch (tp->t_type) { case 'F': player.t_flags &= ~ISHELD; fung_hit = 0; when 'L': { struct room *rp; if ((rp = roomin(&tp->t_pos)) == NULL) break; if (rp->r_goldval != 0 || fallpos(&tp->t_pos,&rp->r_gold,FALSE)) { rp->r_goldval += GOLDCALC; if (save(VS_MAGIC)) rp->r_goldval += GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; mvwaddch(stdscr, rp->r_gold.y, rp->r_gold.x, GOLD); if (!(rp->r_flags & ISDARK)) { light(&hero); mvwaddch(cw, hero.y, hero.x, PLAYER); } } } } /* * Empty the monsters pack */ pitem = tp->t_pack; while (pitem != NULL) { struct object *obj; nexti = next(tp->t_pack); obj = (struct object *) ldata(pitem); obj->o_pos = tp->t_pos; detach(tp->t_pack, pitem); fall(pitem, FALSE); pitem = nexti; } /* * Get rid of the monster. */ remove_monster(&tp->t_pos, item); }