Advanced Rogue family: overhaul privilege handling.
Advanced Rogue 5 and 7, and XRogue, now open the scorefile and logfile
at startup and then drop any set[ug]id privileges if the savedir is not
being used.
line source
+ − /*
+ − * All the fighting gets done here
+ − *
+ − * @(#)fight.c 4.67 (Berkeley) 09/06/83
+ − *
+ − * Rogue: Exploring the Dungeons of Doom
+ − * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman
+ − * All rights reserved.
+ − *
+ − * See the file LICENSE.TXT for full copyright and licensing information.
+ − */
+ −
+ − #include <stdlib.h>
+ − #include <curses.h>
+ − #include <string.h>
+ − #include <ctype.h>
+ − #include "rogue.h"
+ −
+ − #define EQSTR(a, b) (strcmp(a, b) == 0)
+ −
+ − static const char *h_names[] = { /* strings for hitting */
+ − " scored an excellent hit on ",
+ − " hit ",
+ − " have injured ",
+ − " swing and hit ",
+ − " scored an excellent hit on ",
+ − " hit ",
+ − " has injured ",
+ − " swings and hits "
+ − };
+ −
+ − static const char *m_names[] = { /* strings for missing */
+ − " miss",
+ − " swing and miss",
+ − " barely miss",
+ − " don't hit",
+ − " misses",
+ − " swings and misses",
+ − " barely misses",
+ − " doesn't hit",
+ − };
+ −
+ − /*
+ − * adjustments to hit probabilities due to strength
+ − */
+ − static int str_plus[] = {
+ − -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ − 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
+ − };
+ −
+ − /*
+ − * adjustments to damage done due to strength
+ − */
+ − static int add_dam[] = {
+ − -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3,
+ − 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6
+ − };
+ −
+ − /*
+ − * fight:
+ − * The player attacks the monster.
+ − */
+ − int
+ − fight(const coord *mp, const THING *weap, int thrown)
+ − {
+ − THING *tp;
+ − int did_hit = TRUE;
+ − const char *mname;
+ − int ch;
+ −
+ − /*
+ − * Find the monster we want to fight
+ − */
+ − if ((tp = moat(mp->y, mp->x)) == NULL)
+ − {
+ − #ifdef MASTER
+ − debug("Fight what @ %d,%d", mp->y, mp->x);
+ − #endif
+ − return FALSE;
+ − }
+ − /*
+ − * Since we are fighting, things are not quiet so no healing takes
+ − * place.
+ − */
+ − count = 0;
+ − quiet = 0;
+ − runto(mp);
+ − /*
+ − * Let him know it was really a xeroc (if it was one).
+ − */
+ − ch = '\0';
+ − if (tp->t_type == 'X' && tp->t_disguise != 'X' && !on(player, ISBLIND))
+ − {
+ − tp->t_disguise = 'X';
+ − if (on(player, ISHALU)) {
+ − ch = rnd(26) + 'A';
+ − mvaddch(tp->t_pos.y, tp->t_pos.x, ch);
+ − }
+ − msg(choose_str("heavy! That's a nasty critter!",
+ − "wait! That's a xeroc!"));
+ − if (!thrown)
+ − return FALSE;
+ − }
+ − mname = set_mname(tp);
+ − did_hit = FALSE;
+ − has_hit = (terse && !to_death);
+ − if (roll_em(&player, tp, weap, thrown))
+ − {
+ − did_hit = FALSE;
+ − if (thrown)
+ − thunk(weap, mname, terse);
+ − else
+ − hit(NULL, mname, terse);
+ − if (on(player, CANHUH))
+ − {
+ − did_hit = TRUE;
+ − tp->t_flags |= ISHUH;
+ − player.t_flags &= ~CANHUH;
+ − endmsg();
+ − has_hit = FALSE;
+ − msg("your hands stop glowing %s", pick_color("red"));
+ − }
+ − if (tp->t_stats.s_hpt <= 0)
+ − killed(tp, TRUE);
+ − else if (did_hit && !on(player, ISBLIND))
+ − msg("%s appears confused", mname);
+ − did_hit = TRUE;
+ − }
+ − else
+ − if (thrown)
+ − bounce(weap, mname, terse);
+ − else
+ − miss(NULL, mname, terse);
+ − return did_hit;
+ − }
+ −
+ − /*
+ − * attack:
+ − * The monster attacks the player
+ − */
+ − int
+ − attack(THING *mp)
+ − {
+ − const char *mname;
+ − int oldhp;
+ −
+ − /*
+ − * Since this is an attack, stop running and any healing that was
+ − * going on at the time.
+ − */
+ − running = FALSE;
+ − count = 0;
+ − quiet = 0;
+ − if (to_death && !on(*mp, ISTARGET))
+ − {
+ − to_death = FALSE;
+ − kamikaze = FALSE;
+ − }
+ − if (mp->t_type == 'X' && mp->t_disguise != 'X' && !on(player, ISBLIND))
+ − {
+ − mp->t_disguise = 'X';
+ − if (on(player, ISHALU))
+ − mvaddch(mp->t_pos.y, mp->t_pos.x, rnd(26) + 'A');
+ − }
+ − mname = set_mname(mp);
+ − oldhp = pstats.s_hpt;
+ − if (roll_em(mp, &player, NULL, FALSE))
+ − {
+ − if (mp->t_type != 'I')
+ − {
+ − if (has_hit)
+ − addmsg(". ");
+ − hit(mname, NULL, FALSE);
+ − }
+ − else
+ − if (has_hit)
+ − endmsg();
+ − has_hit = FALSE;
+ − if (pstats.s_hpt <= 0)
+ − death(mp->t_type); /* Bye bye life ... */
+ − else if (!kamikaze)
+ − {
+ − oldhp -= pstats.s_hpt;
+ − if (oldhp > max_hit)
+ − max_hit = oldhp;
+ − if (pstats.s_hpt <= max_hit)
+ − to_death = FALSE;
+ − }
+ − if (!on(*mp, ISCANC))
+ − switch (mp->t_type)
+ − {
+ − case 'A':
+ − /*
+ − * If an aquator hits, you can lose armor class.
+ − */
+ − rust_armor(cur_armor);
+ − when 'I':
+ − /*
+ − * The ice monster freezes you
+ − */
+ − player.t_flags &= ~ISRUN;
+ − if (!no_command)
+ − {
+ − addmsg("you are frozen");
+ − if (!terse)
+ − addmsg(" by the %s", mname);
+ − endmsg();
+ − }
+ − no_command += rnd(2) + 2;
+ − if (no_command > BORE_LEVEL)
+ − death('h');
+ − when 'R':
+ − /*
+ − * Rattlesnakes have poisonous bites
+ − */
+ − if (!save(VS_POISON))
+ − {
+ − if (!ISWEARING(R_SUSTSTR))
+ − {
+ − chg_str(-1);
+ − if (!terse)
+ − msg("you feel a bite in your leg and now feel weaker");
+ − else
+ − msg("a bite has weakened you");
+ − }
+ − else if (!to_death)
+ − {
+ − if (!terse)
+ − msg("a bite momentarily weakens you");
+ − else
+ − msg("bite has no effect");
+ − }
+ − }
+ − when 'W':
+ − case 'V':
+ − /*
+ − * Wraiths might drain energy levels, and Vampires
+ − * can steal max_hp
+ − */
+ − if (rnd(100) < (mp->t_type == 'W' ? 15 : 30))
+ − {
+ − int fewer;
+ −
+ − if (mp->t_type == 'W')
+ − {
+ − if (pstats.s_exp == 0)
+ − death('W'); /* All levels gone */
+ − 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);
+ − }
+ − else
+ − fewer = roll(1, 3);
+ − pstats.s_hpt -= fewer;
+ − max_hp -= fewer;
+ − if (pstats.s_hpt <= 0)
+ − pstats.s_hpt = 1;
+ − if (max_hp <= 0)
+ − death(mp->t_type);
+ − msg("you suddenly feel weaker");
+ − }
+ − when 'F':
+ − /*
+ − * Venus Flytrap stops the poor guy from moving
+ − */
+ − player.t_flags |= ISHELD;
+ − sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dx1", ++vf_hit);
+ − if (--pstats.s_hpt <= 0)
+ − death('F');
+ − 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;
+ − remove_mon(&mp->t_pos, mp, FALSE);
+ − mp=NULL;
+ − if (purse != lastpurse)
+ − msg("your purse feels lighter");
+ − }
+ − when 'N':
+ − {
+ − THING *obj, *steal;
+ − int nobj;
+ −
+ − /*
+ − * Nymph's steal a magic item, look through the pack
+ − * and pick out one we like.
+ − */
+ − steal = NULL;
+ − for (nobj = 0, obj = pack; obj != NULL; obj = next(obj))
+ − if (obj != cur_armor && obj != cur_weapon
+ − && obj != cur_ring[LEFT] && obj != cur_ring[RIGHT]
+ − && is_magic(obj) && rnd(++nobj) == 0)
+ − steal = obj;
+ − if (steal != NULL)
+ − {
+ − remove_mon(&mp->t_pos, moat(mp->t_pos.y, mp->t_pos.x), FALSE);
+ − mp=NULL;
+ − steal = leave_pack(steal, TRUE, FALSE);
+ − msg("she stole %s!", inv_name(steal, TRUE));
+ − discard(steal);
+ − }
+ − }
+ − otherwise:
+ − break;
+ − }
+ − }
+ − else if (mp->t_type != 'I')
+ − {
+ − if (has_hit)
+ − {
+ − addmsg(". ");
+ − has_hit = FALSE;
+ − }
+ − if (mp->t_type == 'F')
+ − {
+ − pstats.s_hpt -= vf_hit;
+ − if (pstats.s_hpt <= 0)
+ − death(mp->t_type); /* Bye bye life ... */
+ − }
+ − miss(mname, NULL, FALSE);
+ − }
+ − if (fight_flush && !to_death)
+ − flush_type();
+ − count = 0;
+ − status();
+ − if (mp == NULL)
+ − return(-1);
+ − else
+ − return(0);
+ − }
+ −
+ − /*
+ − * set_mname:
+ − * return the monster name for the given monster
+ − */
+ − const char *
+ − set_mname(const THING *tp)
+ − {
+ − int ch;
+ − const char *mname;
+ − static char tbuf[MAXSTR] = { 't', 'h', 'e', ' ' };
+ −
+ − if (!see_monst(tp) && !on(player, SEEMONST))
+ − return (terse ? "it" : "something");
+ − else if (on(player, ISHALU))
+ − {
+ − move(tp->t_pos.y, tp->t_pos.x);
+ − ch = toascii(CCHAR(inch()));
+ − if (!isupper(ch))
+ − ch = rnd(26);
+ − else
+ − ch -= 'A';
+ − mname = monsters[ch].m_name;
+ − }
+ − else
+ − mname = monsters[tp->t_type - 'A'].m_name;
+ − strcpy(&tbuf[4], mname);
+ − return tbuf;
+ − }
+ −
+ − /*
+ − * swing:
+ − * Returns true if the swing hits
+ − */
+ − int
+ − swing(int at_lvl, int op_arm, int wplus)
+ − {
+ − int res = rnd(20);
+ − int need = (20 - at_lvl) - op_arm;
+ −
+ − return (res + wplus >= need);
+ − }
+ −
+ − /*
+ − * roll_em:
+ − * Roll several attacks
+ − */
+ − int
+ − roll_em(const THING *thatt, THING *thdef, const THING *weap, int hurl)
+ − {
+ − const struct stats *att;
+ − struct stats *def;
+ − const char *cp;
+ − int ndice, nsides, def_arm;
+ − int did_hit = FALSE;
+ − int hplus;
+ − int dplus;
+ − int damage;
+ −
+ − att = &thatt->t_stats;
+ − def = &thdef->t_stats;
+ − if (weap == NULL)
+ − {
+ − cp = att->s_dmg;
+ − dplus = 0;
+ − hplus = 0;
+ − }
+ − else
+ − {
+ − hplus = (weap == NULL ? 0 : weap->o_hplus);
+ − dplus = (weap == NULL ? 0 : weap->o_dplus);
+ − if (weap == cur_weapon)
+ − {
+ − if (ISRING(LEFT, R_ADDDAM))
+ − dplus += cur_ring[LEFT]->o_arm;
+ − else if (ISRING(LEFT, R_ADDHIT))
+ − hplus += cur_ring[LEFT]->o_arm;
+ − if (ISRING(RIGHT, R_ADDDAM))
+ − dplus += cur_ring[RIGHT]->o_arm;
+ − else if (ISRING(RIGHT, R_ADDHIT))
+ − hplus += cur_ring[RIGHT]->o_arm;
+ − }
+ − cp = weap->o_damage;
+ − if (hurl)
+ − {
+ − if ((weap->o_flags&ISMISL) && cur_weapon != NULL &&
+ − cur_weapon->o_which == weap->o_launch)
+ − {
+ − cp = weap->o_hurldmg;
+ − hplus += cur_weapon->o_hplus;
+ − dplus += cur_weapon->o_dplus;
+ − }
+ − else if (weap->o_launch < 0)
+ − cp = weap->o_hurldmg;
+ − }
+ − }
+ − /*
+ − * If the creature being attacked is not running (alseep or held)
+ − * then the attacker gets a plus four bonus to hit.
+ − */
+ − if (!on(*thdef, ISRUN))
+ − hplus += 4;
+ − def_arm = def->s_arm;
+ − if (def == &pstats)
+ − {
+ − if (cur_armor != NULL)
+ − def_arm = cur_armor->o_arm;
+ − if (ISRING(LEFT, R_PROTECT))
+ − def_arm -= cur_ring[LEFT]->o_arm;
+ − if (ISRING(RIGHT, R_PROTECT))
+ − def_arm -= cur_ring[RIGHT]->o_arm;
+ − }
+ − while(cp != NULL && *cp != '\0')
+ − {
+ − ndice = atoi(cp);
+ − if ((cp = strchr(cp, 'x')) == NULL)
+ − break;
+ − nsides = atoi(++cp);
+ − if (swing(att->s_lvl, def_arm, hplus + str_plus[att->s_str]))
+ − {
+ − int proll;
+ −
+ − proll = roll(ndice, nsides);
+ − #ifdef MASTER
+ − if (ndice + nsides > 0 && proll <= 0)
+ − 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);
+ − #endif
+ − 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(const char *mname, int upper)
+ − {
+ − static char tbuf[MAXSTR];
+ −
+ − *tbuf = '\0';
+ − if (mname == 0)
+ − strcpy(tbuf, "you");
+ − else
+ − strcpy(tbuf, mname);
+ − if (upper)
+ − *tbuf = (char) toupper(*tbuf);
+ − return tbuf;
+ − }
+ −
+ − /*
+ − * thunk:
+ − * A missile hits a monster
+ − */
+ − void
+ − thunk(const THING *weap, const char *mname, int noend)
+ − {
+ − if (to_death)
+ − return;
+ − if (weap->o_type == WEAPON)
+ − addmsg("the %s hits ", weap_info[weap->o_which].oi_name);
+ − else
+ − addmsg("you hit ");
+ − addmsg("%s", mname);
+ − if (!noend)
+ − endmsg();
+ − }
+ −
+ − /*
+ − * hit:
+ − * Print a message to indicate a succesful hit
+ − */
+ −
+ − void
+ − hit(const char *er, const char *ee, int noend)
+ − {
+ − int i;
+ − const char *s;
+ −
+ − if (to_death)
+ − return;
+ − addmsg(prname(er, TRUE));
+ − if (terse)
+ − s = " hit";
+ − else
+ − {
+ − i = rnd(4);
+ − if (er != NULL)
+ − i += 4;
+ − s = h_names[i];
+ − }
+ − addmsg(s);
+ − if (!terse)
+ − addmsg(prname(ee, FALSE));
+ − if (!noend)
+ − endmsg();
+ − }
+ −
+ − /*
+ − * miss:
+ − * Print a message to indicate a poor swing
+ − */
+ − void
+ − miss(const char *er, const char *ee, int noend)
+ − {
+ − int i;
+ −
+ − if (to_death)
+ − return;
+ − addmsg(prname(er, TRUE));
+ − if (terse)
+ − i = 0;
+ − else
+ − i = rnd(4);
+ − if (er != NULL)
+ − i += 4;
+ − addmsg(m_names[i]);
+ − if (!terse)
+ − addmsg(" %s", prname(ee, FALSE));
+ − if (!noend)
+ − endmsg();
+ − }
+ −
+ − /*
+ − * bounce:
+ − * A missile misses a monster
+ − */
+ − void
+ − bounce(const THING *weap, const char *mname, int noend)
+ − {
+ − if (to_death)
+ − return;
+ − if (weap->o_type == WEAPON)
+ − addmsg("the %s misses ", weap_info[weap->o_which].oi_name);
+ − else
+ − addmsg("you missed ");
+ − addmsg(mname);
+ − if (!noend)
+ − endmsg();
+ − }
+ −
+ − /*
+ − * remove_mon:
+ − * Remove a monster from the screen
+ − */
+ − void
+ − remove_mon(const coord *mp, THING *tp, int waskill)
+ − {
+ − THING *obj, *nexti;
+ −
+ − for (obj = tp->t_pack; obj != NULL; obj = nexti)
+ − {
+ − nexti = next(obj);
+ − obj->o_pos = tp->t_pos;
+ − detach(tp->t_pack, obj);
+ − if (waskill)
+ − fall(obj, FALSE);
+ − else
+ − discard(obj);
+ − }
+ − moat(mp->y, mp->x) = NULL;
+ − mvaddch(mp->y, mp->x, tp->t_oldch);
+ − detach(mlist, tp);
+ − if (on(*tp, ISTARGET))
+ − {
+ − kamikaze = FALSE;
+ − to_death = FALSE;
+ − if (fight_flush)
+ − flush_type();
+ − }
+ − discard(tp);
+ − }
+ −
+ − /*
+ − * killed:
+ − * Called to put a monster to death
+ − */
+ − void
+ − killed(THING *tp, int pr)
+ − {
+ − const char *mname;
+ −
+ − pstats.s_exp += tp->t_stats.s_exp;
+ −
+ − /*
+ − * If the monster was a venus flytrap, un-hold him
+ − */
+ − switch (tp->t_type)
+ − {
+ − case 'F':
+ − player.t_flags &= ~ISHELD;
+ − vf_hit = 0;
+ − strcpy(monsters['F'-'A'].m_stats.s_dmg, "000x0");
+ − when 'L':
+ − {
+ − THING *gold;
+ −
+ − if (fallpos(&tp->t_pos, &tp->t_room->r_gold) && level >= max_level)
+ − {
+ − gold = new_item();
+ − gold->o_type = GOLD;
+ − gold->o_goldval = GOLDCALC;
+ − if (save(VS_MAGIC))
+ − gold->o_goldval += GOLDCALC + GOLDCALC
+ − + GOLDCALC + GOLDCALC;
+ − attach(tp->t_pack, gold);
+ − }
+ − }
+ − }
+ − /*
+ − * Get rid of the monster.
+ − */
+ − mname = set_mname(tp);
+ − remove_mon(&tp->t_pos, tp, TRUE);
+ − if (pr)
+ − {
+ − if (has_hit)
+ − {
+ − addmsg(". Defeated ");
+ − has_hit = FALSE;
+ − }
+ − else
+ − {
+ − if (!terse)
+ − addmsg("you have ");
+ − addmsg("defeated ");
+ − }
+ − msg(mname);
+ − }
+ − /*
+ − * Do adjustments if he went up a level
+ − */
+ − check_level();
+ − if (fight_flush)
+ − flush_type();
+ − }