There should only be two changes in behavior: arogue7/fight.c, arogue7/fight.c: a to-hit bonus is now correctly applied to characters who are not monks instead of monks who are not empty-handed. urogue/fight.c: fixed an interaction with the "debug" macro that could cause the wrong message to be displayed.
2178 lines
59 KiB
C
2178 lines
59 KiB
C
/*
|
|
fight.c - All the fighting gets done here
|
|
|
|
UltraRogue: The Ultimate Adventure in the Dungeons of Doom
|
|
Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
|
|
All rights reserved.
|
|
|
|
Based on "Advanced Rogue"
|
|
Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
|
|
All rights reserved.
|
|
|
|
Based on "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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include "rogue.h"
|
|
|
|
/*
|
|
* This are the beginning experience levels for all players all further
|
|
* experience levels are computed by multiplying by 2
|
|
*/
|
|
|
|
static long e_levels[10] =
|
|
{
|
|
143L, /* Fighter */
|
|
182L, /* Paladin */
|
|
169L, /* Ranger */
|
|
127L, /* Cleric */
|
|
154L, /* Druid */
|
|
185L, /* Magician */
|
|
169L, /* Illusionist */
|
|
112L, /* Thief */
|
|
126L, /* Assasin */
|
|
319L /* Ninja */
|
|
};
|
|
|
|
static struct matrix att_mat[11] =
|
|
{
|
|
/* Base, Max_lvl, Factor, Offset, Range */
|
|
|
|
{ 10, 17, 2, 1, 2 }, /* fi */
|
|
{ 10, 17, 2, 1, 2 }, /* pa */
|
|
{ 10, 17, 2, 1, 2 }, /* ra */
|
|
{ 10, 19, 2, 1, 3 }, /* cl */
|
|
{ 10, 19, 2, 1, 3 }, /* dr */
|
|
{ 9, 21, 2, 1, 5 }, /* mu */
|
|
{ 9, 21, 2, 1, 5 }, /* il */
|
|
{ 10, 21, 2, 1, 4 }, /* th */
|
|
{ 10, 21, 2, 1, 4 }, /* as */
|
|
{ 10, 21, 2, 1, 4 }, /* nj */
|
|
{ 7, 25, 1, 0, 2 } /* mn */
|
|
};
|
|
|
|
void
|
|
do_fight(coord dir, int tothedeath)
|
|
{
|
|
int x,y;
|
|
|
|
x = dir.x;
|
|
y = dir.y;
|
|
|
|
if (!tothedeath && pstats.s_hpt < max_stats.s_hpt / 3)
|
|
{
|
|
msg("That's not wise.");
|
|
|
|
after = fighting = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (isalpha(CCHAR(winat(hero.y + y, hero.x + x))))
|
|
{
|
|
after = fighting = TRUE;
|
|
do_move(y, x);
|
|
}
|
|
else
|
|
{
|
|
if (fighting == FALSE)
|
|
msg("Nothing there.");
|
|
|
|
after = fighting = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
fight()
|
|
The player attacks the monster.
|
|
*/
|
|
|
|
int
|
|
fight(coord *mp, struct object *weap, int thrown)
|
|
{
|
|
struct thing *tp;
|
|
struct linked_list *item;
|
|
int did_hit = TRUE;
|
|
char *mname;
|
|
|
|
/* 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 = THINGPTR(item);
|
|
|
|
mname = (on(player, ISBLIND)) ? "it" : monsters[tp->t_index].m_name;
|
|
|
|
/* Since we are fighting, things are not quiet so no healing takes place */
|
|
|
|
player.t_rest_hpt = player.t_rest_pow = 0;
|
|
tp->t_rest_hpt = tp->t_rest_pow = 0;
|
|
|
|
/* Let him know it was really a mimic (if it was one). */
|
|
|
|
if (off(player, ISBLIND))
|
|
{
|
|
if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise))
|
|
{
|
|
msg("Wait! That's a %s!", mname);
|
|
turn_off(*tp, ISDISGUISE);
|
|
did_hit = thrown;
|
|
}
|
|
|
|
if (on(*tp, CANSURPRISE))
|
|
{
|
|
turn_off(*tp, CANSURPRISE);
|
|
if ((player.t_ctype == C_RANGER && rnd(6) != 0) ||
|
|
(player.t_ctype == C_NINJA && rnd(pstats.s_lvl / 2)
|
|
!= 0))
|
|
msg("You notice a %s trying to hide!", mname);
|
|
else
|
|
{
|
|
msg("Wait! There's a %s!", mname);
|
|
did_hit = thrown;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Protection from Normal Missiles */
|
|
|
|
if (thrown && on(*tp, HASMSHIELD))
|
|
{
|
|
msg("The %s slows as it approaches %s.",
|
|
weaps[weap->o_which].w_name, mname);
|
|
|
|
did_hit = FALSE;
|
|
}
|
|
|
|
if (did_hit)
|
|
{
|
|
did_hit = FALSE;
|
|
|
|
if (!can_blink(tp) &&
|
|
(off(*tp, MAGICHIT) || (weap != NULL &&
|
|
(weap->o_hplus > 0 || weap->o_dplus > 0))) &&
|
|
(off(*tp, BMAGICHIT) || (weap != NULL &&
|
|
(weap->o_hplus > 2 || weap->o_dplus > 2))) &&
|
|
roll_em(&player, tp, weap, thrown, cur_weapon))
|
|
{
|
|
did_hit = TRUE;
|
|
tp->t_wasshot = TRUE;
|
|
|
|
if (thrown)
|
|
{
|
|
if (weap != NULL && weap->o_type == WEAPON
|
|
&& weap->o_which == GRENADE)
|
|
{
|
|
hearmsg("BOOOM!");
|
|
aggravate();
|
|
}
|
|
|
|
thunk(weap, mname);
|
|
}
|
|
else
|
|
hit(mname);
|
|
|
|
/* hitting a friendly monster is curtains */
|
|
|
|
if (on(*tp, ISFRIENDLY))
|
|
{
|
|
turn_off(*tp, ISFRIENDLY);
|
|
turn_on(*tp, ISMEAN);
|
|
}
|
|
|
|
/* Charmed monsters become uncharmed */
|
|
|
|
if (on(*tp, ISCHARMED))
|
|
{
|
|
turn_off(*tp, ISCHARMED);
|
|
turn_on(*tp, ISMEAN);
|
|
}
|
|
|
|
/*
|
|
* If the player hit a rust monster, he better have a
|
|
* + weapon
|
|
*/
|
|
|
|
if (on(*tp, CANRUST))
|
|
{
|
|
if (!thrown && (weap != NULL) &&
|
|
(weap->o_flags & ISMETAL) &&
|
|
!(weap->o_flags & ISPROT) &&
|
|
!(weap->o_flags & ISSILVER) &&
|
|
(weap->o_hplus < 1) && (weap->o_dplus < 1))
|
|
{
|
|
if (rnd(100) < 50)
|
|
weap->o_hplus--;
|
|
else
|
|
weap->o_dplus--;
|
|
|
|
msg("Your %s weakens!", weaps[weap->o_which].w_name);
|
|
}
|
|
else if (!thrown && weap != NULL && (weap->o_flags & ISMETAL))
|
|
msg("The rust vanishes from your %s!",
|
|
weaps[weap->o_which].w_name);
|
|
}
|
|
|
|
/* flammable monsters die from burning weapons */
|
|
|
|
if (thrown && on(*tp, CANBBURN) &&
|
|
(weap->o_flags & CANBURN) &&
|
|
!save_throw(VS_WAND, tp))
|
|
{
|
|
msg("The %s vanishes in a ball of flame.",
|
|
monsters[tp->t_index].m_name);
|
|
|
|
tp->t_stats.s_hpt = 0;
|
|
}
|
|
|
|
/* spores explode and infest hero */
|
|
|
|
if (on(*tp, CANSPORE))
|
|
{
|
|
msg("The %s explodes in a cloud of dust.",
|
|
monsters[tp->t_index].m_name);
|
|
|
|
if (is_wearing(R_HEALTH) ||
|
|
player.t_ctype == C_PALADIN ||
|
|
(player.t_ctype == C_NINJA && pstats.s_lvl > 6) ||
|
|
(thrown && rnd(50) > 0) ||
|
|
rnd(20) > 0)
|
|
{
|
|
msg("The dust makes it hard to breath.");
|
|
}
|
|
else
|
|
{
|
|
msg("You have contracted a parasitic infestation!");
|
|
|
|
infest_dam++;
|
|
turn_on(player, HASINFEST);
|
|
}
|
|
|
|
tp->t_stats.s_hpt = 0;
|
|
}
|
|
|
|
/* fireproof monsters laugh at you when burning weapon hits */
|
|
|
|
if (thrown && on(*tp, NOFIRE) && (weap->o_flags & CANBURN))
|
|
msg("The %s laughs as the %s bounces.",
|
|
monsters[tp->t_index].m_name,
|
|
weaps[weap->o_which].w_name);
|
|
|
|
/* sharp weapons have no effect on NOSHARP monsters */
|
|
|
|
if (on(*tp, NOSHARP) && (weap != NULL) &&
|
|
(weap->o_flags & ISSHARP))
|
|
{
|
|
msg("The %s has no effect on the %s!",
|
|
weaps[weap->o_which].w_name,
|
|
monsters[tp->t_index].m_name);
|
|
|
|
fighting = FALSE;
|
|
}
|
|
|
|
/* metal weapons pass through NOMETAL monsters */
|
|
|
|
if (on(*tp, NOMETAL) && (weap != NULL) &&
|
|
(weap->o_flags & ISMETAL))
|
|
{
|
|
msg("The %s passes through the %s!",
|
|
weaps[weap->o_which].w_name,
|
|
monsters[tp->t_index].m_name);
|
|
|
|
fighting = FALSE;
|
|
}
|
|
|
|
/*
|
|
* If the player hit something that shrieks, wake the
|
|
* dungeon
|
|
*/
|
|
|
|
if (on(*tp, CANSHRIEK))
|
|
{
|
|
turn_off(*tp, CANSHRIEK);
|
|
|
|
if (on(player, CANHEAR))
|
|
{
|
|
msg("You are stunned by the %s's shriek.", mname);
|
|
no_command += 4 + rnd(8);
|
|
}
|
|
else if (off(player, ISDEAF))
|
|
msg("The %s emits a piercing shriek.", mname);
|
|
else
|
|
msg("The %s seems to be trying to make some noise.", mname);
|
|
|
|
aggravate();
|
|
|
|
if (rnd(wizard ? 3 : 39) == 0 && cur_armor
|
|
!= NULL
|
|
&& cur_armor->o_which == CRYSTAL_ARMOR)
|
|
{
|
|
struct linked_list *itm;
|
|
struct object *obj;
|
|
|
|
for (itm = pack; itm != NULL; itm = next(itm))
|
|
{
|
|
obj = OBJPTR(itm);
|
|
|
|
if (obj == cur_armor)
|
|
break;
|
|
}
|
|
|
|
if (itm == NULL)
|
|
{
|
|
debug("Can't find crystalline armor being worn.");
|
|
}
|
|
else
|
|
{
|
|
msg("Your armor shatters from the shriek.");
|
|
cur_armor = NULL;
|
|
del_pack(itm);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the player hit something that can surprise, it
|
|
* can't now
|
|
*/
|
|
|
|
if (on(*tp, CANSURPRISE))
|
|
turn_off(*tp, CANSURPRISE);
|
|
|
|
/*
|
|
* If the player hit something that can summon, it
|
|
* will try to
|
|
*/
|
|
|
|
summon_help(tp, NOFORCE);
|
|
|
|
/* Can the player confuse? */
|
|
|
|
if (on(player, CANHUH) && !thrown)
|
|
{
|
|
seemsg("Your hands stop glowing red!");
|
|
seemsg("The %s appears confused.", mname);
|
|
turn_on(*tp, ISHUH);
|
|
turn_off(player, CANHUH);
|
|
}
|
|
|
|
/* Merchants just disappear if hit */
|
|
|
|
/*
|
|
* increases prices and curses objects from now on
|
|
* though
|
|
*/
|
|
|
|
if (on(*tp, CANSELL))
|
|
{
|
|
msg("The %s disappears with his wares with a BOOM and a flash.",
|
|
mname);
|
|
killed(NULL, item, NOMESSAGE, NOPOINTS);
|
|
aggravate();
|
|
luck++;
|
|
}
|
|
else if (tp->t_stats.s_hpt <= 0)
|
|
killed(&player, item, MESSAGE, POINTS);
|
|
|
|
/*
|
|
* If the monster is fairly intelligent and about to
|
|
* die, it may turn tail and run.
|
|
*/
|
|
|
|
else if ((tp->t_stats.s_hpt < max(10, tp->maxstats.s_hpt / 10)) &&
|
|
(rnd(25) < tp->t_stats.s_intel))
|
|
{
|
|
turn_on(*tp, ISFLEE);
|
|
|
|
/* If monster was suffocating, stop it */
|
|
|
|
if (on(*tp, DIDSUFFOCATE))
|
|
{
|
|
turn_off(*tp, DIDSUFFOCATE);
|
|
extinguish_fuse(FUSE_SUFFOCATE);
|
|
}
|
|
|
|
/* If monster held us, stop it */
|
|
|
|
if (on(*tp, DIDHOLD) && (--hold_count == 0))
|
|
turn_off(player, ISHELD);
|
|
|
|
turn_off(*tp, DIDHOLD);
|
|
|
|
if (on(*tp, CANTELEPORT))
|
|
{
|
|
int rm;
|
|
|
|
/*
|
|
* Erase the monster from the old
|
|
* position
|
|
*/
|
|
|
|
if (isalpha(mvwinch(cw, tp->t_pos.y, tp->t_pos.x)))
|
|
mvwaddch(cw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
|
|
|
|
mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, ' ');
|
|
|
|
/* Get a new position */
|
|
|
|
do
|
|
{
|
|
rm = rnd_room();
|
|
rnd_pos(&rooms[rm], &tp->t_pos);
|
|
}
|
|
while (winat(tp->t_pos.y, tp->t_pos.x) != FLOOR);
|
|
|
|
/* Put it there */
|
|
|
|
mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type);
|
|
tp->t_oldch = CCHAR( mvwinch(cw, tp->t_pos.y, tp->t_pos.x) );
|
|
seemsg("The %s seems to have disappeared!", mname);
|
|
}
|
|
}
|
|
}
|
|
else if (thrown)
|
|
bounce(weap, mname);
|
|
else
|
|
miss(mname);
|
|
}
|
|
|
|
if (curr_mons)
|
|
chase_it(mp,&player); /* after so that backstabbing can happen */
|
|
|
|
count = 0;
|
|
|
|
return(did_hit);
|
|
}
|
|
|
|
/*
|
|
attack()
|
|
The monster attacks the player
|
|
*/
|
|
|
|
int
|
|
attack(struct thing *mp, struct object *weapon, int thrown)
|
|
{
|
|
char *mname;
|
|
int did_hit = FALSE;
|
|
|
|
/* If the monster is in a wall, it cannot attack */
|
|
|
|
if (on(*mp, ISINWALL))
|
|
return (FALSE);
|
|
|
|
/* If two monsters start to gang up on our hero, stop fight mode */
|
|
|
|
if (fighting)
|
|
{
|
|
if (beast == NULL)
|
|
beast = mp;
|
|
else if (beast != mp)
|
|
fighting = FALSE;
|
|
}
|
|
|
|
/*
|
|
Since this is an attack, stop running and any healing that was
|
|
going on at the time.
|
|
*/
|
|
|
|
running = FALSE;
|
|
player.t_rest_hpt = player.t_rest_pow = 0;
|
|
mp->t_rest_hpt = mp->t_rest_pow = 0;
|
|
|
|
if (on(*mp, ISDISGUISE) && off(player, ISBLIND))
|
|
turn_off(*mp, ISDISGUISE);
|
|
mname = on(player, ISBLIND) ? "the monster" :
|
|
monsters[mp->t_index].m_name;
|
|
|
|
if (roll_em(mp, &player, weapon, thrown, wield_weap(weapon, mp)) &&
|
|
(!thrown || off(player, HASMSHIELD)))
|
|
{
|
|
did_hit = TRUE;
|
|
|
|
m_thunk(weapon, mname);
|
|
|
|
if (pstats.s_hpt <= 0)
|
|
{
|
|
death(mp->t_index); /* Bye bye life ... */
|
|
return TRUE;
|
|
}
|
|
|
|
/* surprising monsters appear after they shoot at you */
|
|
|
|
if (thrown && on(*mp, CANSURPRISE))
|
|
turn_off(*mp, CANSURPRISE);
|
|
else if (!thrown)
|
|
{
|
|
|
|
/*
|
|
If a vampire hits, it may take half your hit
|
|
points
|
|
*/
|
|
|
|
if ( on(*mp, CANSUCK) && !save(VS_MAGIC) )
|
|
{
|
|
if (pstats.s_hpt == 1)
|
|
{
|
|
death(mp->t_index);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
pstats.s_hpt /= 2;
|
|
msg("You feel your life force being drawn from you.");
|
|
}
|
|
}
|
|
|
|
/*
|
|
strong monsters can shatter or gong crystalline
|
|
armor
|
|
*/
|
|
|
|
if (cur_armor != NULL && cur_armor->o_which == CRYSTAL_ARMOR)
|
|
{
|
|
if (rnd(mp->t_stats.s_str + (cur_armor->o_ac / 2)) > 20)
|
|
{
|
|
struct linked_list *item;
|
|
struct object *obj;
|
|
|
|
for (item = pack; item != NULL; item = next(item))
|
|
{
|
|
obj = OBJPTR(item);
|
|
|
|
if (obj == cur_armor)
|
|
break;
|
|
}
|
|
|
|
if (item == NULL)
|
|
{
|
|
debug("Can't find crystalline armor being worn.");
|
|
}
|
|
else
|
|
{
|
|
msg("Your armor is shattered by the blow.");
|
|
cur_armor = NULL;
|
|
del_pack(item);
|
|
}
|
|
}
|
|
else if (rnd(mp->t_stats.s_str) > 15)
|
|
{
|
|
msg("Your armor rings from the blow.");
|
|
aggravate();
|
|
}
|
|
}
|
|
|
|
/* Stinking monsters reduce the player's strength */
|
|
|
|
if (on(*mp, CANSTINK))
|
|
{
|
|
turn_off(*mp, CANSTINK);
|
|
|
|
if (player.t_ctype != C_PALADIN
|
|
&& !(player.t_ctype == C_NINJA && pstats.s_lvl > 12)
|
|
&& !save(VS_POISON))
|
|
{
|
|
if (on(player, CANSCENT))
|
|
{
|
|
msg("You pass out from the stench of the %s.", mname);
|
|
no_command += 4 + rnd(8);
|
|
}
|
|
else if (off(player, ISUNSMELL))
|
|
msg("The stench of the %s sickens you.", mname);
|
|
|
|
if (on(player, HASSTINK))
|
|
lengthen_fuse(FUSE_UNSTINK, STINKTIME);
|
|
else
|
|
{
|
|
turn_on(player, HASSTINK);
|
|
light_fuse(FUSE_UNSTINK, 0, STINKTIME, AFTER);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* chilling monster reduces strength permanently */
|
|
|
|
if (on(*mp, CANCHILL) &&
|
|
(cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR))
|
|
{
|
|
msg("You cringe at the %s's chilling touch.", mname);
|
|
|
|
if (!is_wearing(R_SUSABILITY))
|
|
{
|
|
chg_str(-1, FALSE, TRUE);
|
|
|
|
if (lost_str == 0)
|
|
light_fuse(FUSE_RES_STRENGTH, 0, CHILLTIME, AFTER);
|
|
else
|
|
lengthen_fuse(FUSE_RES_STRENGTH, CHILLTIME);
|
|
}
|
|
}
|
|
|
|
/* itching monsters reduce dexterity (temporarily) */
|
|
|
|
if (on(*mp, CANITCH) && player.t_ctype != C_PALADIN
|
|
&& !(player.t_ctype == C_NINJA && pstats.s_lvl > 12)
|
|
&& !save(VS_POISON))
|
|
{
|
|
msg("The claws of the %s scratch you!", mname);
|
|
|
|
if (is_wearing(R_SUSABILITY))
|
|
msg("The scratch has no effect.");
|
|
else
|
|
{
|
|
msg("You feel a burning itch.");
|
|
turn_on(player, HASITCH);
|
|
chg_dext(-1, FALSE, TRUE);
|
|
light_fuse(FUSE_UNITCH, 0, roll(4, 6), AFTER);
|
|
}
|
|
}
|
|
|
|
/* a hugging monster may SQUEEEEEEEZE */
|
|
|
|
if (on(*mp, CANHUG) &&
|
|
(cur_armor == NULL || cur_armor->o_which != CRYSTAL_ARMOR))
|
|
{
|
|
if (roll(1, 20) >= 18 || roll(1, 20) >= 18)
|
|
{
|
|
msg("The %s squeezes you against itself.", mname);
|
|
|
|
if ((pstats.s_hpt -= roll(2, 8)) <= 0)
|
|
{
|
|
death(mp->t_index);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* a trampling monster may step on the player */
|
|
|
|
if (on(*mp, CANTRAMPLE))
|
|
{
|
|
if (roll(1, 20) >= 16 || roll(1, 20) >= 16)
|
|
{
|
|
msg("The %s steps on you.", mname);
|
|
|
|
if ((pstats.s_hpt -= roll(3, mp->t_stats.s_lvl)) <= 0)
|
|
{
|
|
death(mp->t_index);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* a disease-carrying monster may transmit the disease */
|
|
|
|
if (on(*mp, CANDISEASE) &&
|
|
(rnd(pstats.s_const) < mp->t_stats.s_lvl) &&
|
|
off(player, HASDISEASE))
|
|
{
|
|
|
|
if (is_wearing(R_HEALTH)
|
|
|| (player.t_ctype == C_PALADIN)
|
|
|| (player.t_ctype == C_NINJA &&
|
|
pstats.s_lvl > 6))
|
|
msg("The wound heals quickly.");
|
|
else
|
|
{
|
|
turn_on(player, HASDISEASE);
|
|
light_fuse(FUSE_CURE_DISEASE,0,roll(4,4) * SICKTIME, AFTER);
|
|
msg("You have contracted a disease!");
|
|
}
|
|
}
|
|
|
|
/* a rust monster will weaken your armor */
|
|
|
|
if (on(*mp, CANRUST))
|
|
{
|
|
if (cur_armor != NULL &&
|
|
cur_armor->o_which != SOFT_LEATHER &&
|
|
cur_armor->o_which != HEAVY_LEATHER &&
|
|
cur_armor->o_which != CUIRBOLILLI &&
|
|
cur_armor->o_which != PADDED_ARMOR &&
|
|
cur_armor->o_which != CRYSTAL_ARMOR &&
|
|
cur_armor->o_which != MITHRIL &&
|
|
!(cur_armor->o_flags & ISPROT) &&
|
|
cur_armor->o_ac < pstats.s_arm + 1)
|
|
{
|
|
msg("Your armor weakens!");
|
|
cur_armor->o_ac++;
|
|
}
|
|
else if (cur_armor != NULL &&
|
|
(cur_armor->o_flags & ISPROT) &&
|
|
cur_armor->o_which != SOFT_LEATHER &&
|
|
cur_armor->o_which != HEAVY_LEATHER &&
|
|
cur_armor->o_which != CUIRBOLILLI &&
|
|
cur_armor->o_which != PADDED_ARMOR &&
|
|
cur_armor->o_which != CRYSTAL_ARMOR &&
|
|
cur_armor->o_which != MITHRIL)
|
|
msg("The rust vanishes instantly!");
|
|
}
|
|
|
|
/* If a surprising monster hit you, you can see it now */
|
|
|
|
if (on(*mp, CANSURPRISE))
|
|
turn_off(*mp, CANSURPRISE);
|
|
|
|
/* an infesting monster will give you a parasite or rot */
|
|
|
|
if (on(*mp, CANINFEST) && rnd(pstats.s_const) < mp->t_stats.s_lvl)
|
|
{
|
|
if (is_wearing(R_HEALTH) || (player.t_ctype == C_PALADIN)
|
|
|| (player.t_ctype == C_NINJA && pstats.s_lvl > 6))
|
|
msg("The wound quickly heals.");
|
|
else
|
|
{
|
|
turn_off(*mp, CANINFEST);
|
|
msg("You have contracted a parasitic infestation!");
|
|
infest_dam++;
|
|
turn_on(player, HASINFEST);
|
|
}
|
|
}
|
|
|
|
/* Some monsters have poisonous bites */
|
|
|
|
if (on(*mp, CANPOISON) && !save(VS_POISON))
|
|
{
|
|
if (is_wearing(R_SUSABILITY) || (player.t_ctype == C_PALADIN)
|
|
|| (player.t_ctype == C_NINJA && pstats.s_lvl > 12))
|
|
msg("The sting has no effect on you!");
|
|
else
|
|
{
|
|
chg_str(-1, FALSE, FALSE);
|
|
msg("You feel a sting in your arm and now feel weaker.");
|
|
}
|
|
}
|
|
|
|
/* a hideous monster may cause fear by touching */
|
|
|
|
if (on(*mp, TOUCHFEAR))
|
|
{
|
|
turn_off(*mp, TOUCHFEAR);
|
|
|
|
if (!save(VS_WAND)&&!(on(player,ISFLEE)&&(player.t_chasee==mp)))
|
|
{
|
|
if (off(player, SUPERHERO)
|
|
&& (player.t_ctype != C_PALADIN))
|
|
{
|
|
turn_on(player, ISFLEE);
|
|
player.t_ischasing = FALSE;
|
|
player.t_chasee = mp;
|
|
msg("The %s's touch terrifies you.", mname);
|
|
}
|
|
else
|
|
msg("The %s's touch feels cold and clammy.", mname);
|
|
}
|
|
}
|
|
|
|
/* some monsters will suffocate our hero */
|
|
|
|
if (on(*mp, CANSUFFOCATE) && (rnd(100) < 15) &&
|
|
(find_slot(FUSE_SUFFOCATE, FUSE) == NULL))
|
|
{
|
|
turn_on(*mp, DIDSUFFOCATE);
|
|
msg("The %s is beginning to suffocate you.",
|
|
mname);
|
|
light_fuse(FUSE_SUFFOCATE, 0, roll(4, 2), AFTER);
|
|
}
|
|
|
|
/* don't look now, you will get turned to stone */
|
|
|
|
if (on(*mp, TOUCHSTONE))
|
|
{
|
|
turn_off(*mp, TOUCHSTONE);
|
|
|
|
if (on(player, CANINWALL))
|
|
msg("The %s's touch has no effect.", mname);
|
|
else
|
|
{
|
|
if (!save(VS_PETRIFICATION) && rnd(100) < 3)
|
|
{
|
|
msg("Your body begins to solidify.");
|
|
msg("You are turned to stone !!! --More--");
|
|
wait_for(' ');
|
|
death(D_PETRIFY);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
msg("The %s's touch stiffens your limbs.", mname);
|
|
no_command = rnd(STONETIME) + 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Undead might drain energy levels */
|
|
|
|
if ((on(*mp, CANDRAIN) || on(*mp, DOUBLEDRAIN)) && rnd(100) < 15)
|
|
{
|
|
if (is_carrying(TR_AMULET))
|
|
msg("The Amulet protects you from the %s's negative energy!",
|
|
mname);
|
|
else
|
|
{
|
|
lower_level(mp->t_index);
|
|
|
|
if (on(*mp, DOUBLEDRAIN))
|
|
lower_level(mp->t_index);
|
|
}
|
|
|
|
turn_on(*mp, DIDDRAIN);
|
|
}
|
|
|
|
/* permanently drain a wisdom point */
|
|
|
|
if (on(*mp, DRAINWISDOM) && rnd(100) < 15)
|
|
{
|
|
int ring_str; /* Value of ring strengths */
|
|
|
|
/* Undo any ring changes */
|
|
|
|
ring_str = ring_value(R_ADDWISDOM) +
|
|
(on(player, POWERWISDOM) ? 10 : 0);
|
|
|
|
pstats.s_wisdom -= ring_str;
|
|
|
|
msg("You feel slightly less wise now.");
|
|
|
|
pstats.s_wisdom = max(pstats.s_wisdom - 1, 3);
|
|
max_stats.s_wisdom = pstats.s_wisdom;
|
|
|
|
/* Now put back the ring changes */
|
|
|
|
pstats.s_wisdom += ring_str;
|
|
|
|
}
|
|
|
|
/* permanently drain a intelligence point */
|
|
|
|
if (on(*mp, DRAINBRAIN) && rnd(100) < 15)
|
|
{
|
|
int ring_str; /* Value of ring strengths */
|
|
|
|
/* Undo any ring changes */
|
|
|
|
ring_str = ring_value(R_ADDINTEL) +
|
|
(on(player, POWERINTEL) ? 10 : 0);
|
|
|
|
pstats.s_intel -= ring_str;
|
|
|
|
msg("You feel slightly less intelligent now.");
|
|
pstats.s_intel = max(pstats.s_intel - 1, 3);
|
|
max_stats.s_intel = pstats.s_intel;
|
|
|
|
/* Now put back the ring changes */
|
|
|
|
pstats.s_intel += ring_str;
|
|
}
|
|
|
|
/* Violet fungi and others hold the hero */
|
|
|
|
if (on(*mp, CANHOLD) && off(*mp, DIDHOLD)
|
|
&& !is_wearing(R_FREEDOM))
|
|
{
|
|
turn_on(player, ISHELD);
|
|
turn_on(*mp, DIDHOLD);
|
|
hold_count++;
|
|
}
|
|
|
|
/* suckers will suck blood and run away */
|
|
|
|
if (on(*mp, CANDRAW))
|
|
{
|
|
turn_off(*mp, CANDRAW);
|
|
turn_on(*mp, ISFLEE);
|
|
msg("The %s sates itself with your blood.", mname);
|
|
|
|
if ((pstats.s_hpt -= 12) <= 0)
|
|
{
|
|
death(mp->t_index);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* el stinkos will force a reduction in strength */
|
|
|
|
if (on(*mp, CANSMELL))
|
|
{
|
|
turn_off(*mp, CANSMELL);
|
|
|
|
if (save(VS_MAGIC) || is_wearing(R_SUSABILITY))
|
|
msg("You smell an unpleasant odor.");
|
|
else
|
|
{
|
|
int odor_str = -(rnd(6) + 1);
|
|
|
|
if (on(player, CANSCENT))
|
|
{
|
|
msg("You pass out from a foul odor.");
|
|
no_command += 4 + rnd(8);
|
|
}
|
|
else if (off(player, ISUNSMELL))
|
|
msg("You are overcome by a foul odor.");
|
|
|
|
if (lost_str == 0)
|
|
{
|
|
chg_str(odor_str, FALSE, TRUE);
|
|
light_fuse(FUSE_RES_STRENGTH, 0, SMELLTIME, AFTER);
|
|
}
|
|
else
|
|
lengthen_fuse(FUSE_RES_STRENGTH, SMELLTIME);
|
|
}
|
|
}
|
|
|
|
/* Paralyzation */
|
|
|
|
if (on(*mp, CANPARALYZE))
|
|
{
|
|
turn_off(*mp, CANPARALYZE);
|
|
|
|
if (!save(VS_PARALYZATION) && no_command == 0)
|
|
{
|
|
if (on(player, CANINWALL))
|
|
msg("The %s's touch has no effect.", mname);
|
|
else
|
|
{
|
|
msg("The %s's touch paralyzes you.", mname);
|
|
no_command = FREEZETIME;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Rotting */
|
|
|
|
if (on(*mp, CANROT))
|
|
{
|
|
turn_off(*mp, CANROT);
|
|
turn_on(*mp, DOROT);
|
|
}
|
|
|
|
/* some monsters steal gold */
|
|
|
|
if (on(*mp, STEALGOLD))
|
|
{
|
|
long lastpurse;
|
|
struct linked_list *item;
|
|
struct object *obj;
|
|
|
|
lastpurse = purse;
|
|
purse = (purse > GOLDCALC) ? purse - GOLDCALC : 0L;
|
|
|
|
if (!save(VS_MAGIC))
|
|
purse = (purse > (4*GOLDCALC)) ? purse-(4*GOLDCALC) : 0L;
|
|
|
|
if (purse != lastpurse)
|
|
{
|
|
msg("Your purse feels lighter.");
|
|
|
|
/* Give the gold to the thief */
|
|
|
|
for (item = mp->t_pack; item != NULL; item = next(item))
|
|
{
|
|
obj = OBJPTR(item);
|
|
|
|
if (obj->o_type == GOLD)
|
|
{
|
|
obj->o_count += lastpurse - purse;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Did we do it? */
|
|
|
|
if (item == NULL) /* Then make some */
|
|
{
|
|
item = new_item(sizeof *obj);
|
|
obj = OBJPTR(item);
|
|
obj->o_type = GOLD;
|
|
obj->o_count = lastpurse - purse;
|
|
obj->o_hplus = obj->o_dplus = 0;
|
|
obj->o_damage = obj->o_hurldmg = "0d0";
|
|
obj->o_ac = 11;
|
|
obj->o_group = 0;
|
|
obj->o_flags = 0;
|
|
obj->o_mark[0] = '\0';
|
|
obj->o_pos = mp->t_pos;
|
|
|
|
attach(mp->t_pack, item);
|
|
}
|
|
}
|
|
|
|
if (rnd(2))
|
|
turn_on(*mp, ISFLEE);
|
|
|
|
turn_on(*mp, ISINVIS);
|
|
}
|
|
|
|
/* other monsters steal magic */
|
|
|
|
if (on(*mp, STEALMAGIC))
|
|
{
|
|
struct linked_list *list, *stealit;
|
|
struct object *obj;
|
|
int worth = 0;
|
|
|
|
stealit = NULL;
|
|
|
|
for (list = pack; list != NULL; list = next(list))
|
|
{
|
|
obj = OBJPTR(list);
|
|
|
|
if (rnd(33) == 0) /* some stuff degrades */
|
|
{
|
|
if (obj->o_flags & ISBLESSED)
|
|
obj->o_flags &= ~ISBLESSED;
|
|
else
|
|
obj->o_flags |= ISCURSED;
|
|
|
|
msg("You feel nimble fingers reach into you pack.");
|
|
}
|
|
|
|
if (((obj != cur_armor &&
|
|
obj != cur_weapon &&
|
|
obj != cur_ring[LEFT_1] &&
|
|
obj != cur_ring[LEFT_2] &&
|
|
obj != cur_ring[LEFT_3] &&
|
|
obj != cur_ring[LEFT_4] &&
|
|
obj != cur_ring[LEFT_5] &&
|
|
obj != cur_ring[RIGHT_1] &&
|
|
obj != cur_ring[RIGHT_2] &&
|
|
obj != cur_ring[RIGHT_3] &&
|
|
obj != cur_ring[RIGHT_4] &&
|
|
obj != cur_ring[RIGHT_5] &&
|
|
!(obj->o_flags & ISPROT) &&
|
|
is_magic(obj))
|
|
|| level > 45)
|
|
&& get_worth(obj) > worth)
|
|
{
|
|
stealit = list;
|
|
worth = get_worth(obj);
|
|
}
|
|
}
|
|
|
|
if (stealit != NULL)
|
|
{
|
|
struct object *newobj;
|
|
|
|
newobj = OBJPTR(stealit);
|
|
|
|
if (newobj->o_count > 1 && newobj->o_group == 0)
|
|
{
|
|
int oc;
|
|
struct linked_list *nitem;
|
|
struct object *op;
|
|
|
|
oc = --(newobj->o_count);
|
|
newobj->o_count = 1;
|
|
nitem = new_item(sizeof *newobj);
|
|
op = OBJPTR(nitem);
|
|
*op = *newobj;
|
|
|
|
msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE));
|
|
newobj->o_count = oc;
|
|
attach(mp->t_pack, nitem);
|
|
}
|
|
else
|
|
{
|
|
msg("The %s stole %s!",mname,inv_name(newobj,LOWERCASE));
|
|
newobj->o_flags &= ~ISCURSED;
|
|
dropcheck(newobj);
|
|
rem_pack(newobj);
|
|
attach(mp->t_pack, stealit);
|
|
|
|
if (newobj->o_type == ARTIFACT)
|
|
has_artifact &= ~(1 << newobj->o_which);
|
|
}
|
|
|
|
if (newobj->o_flags & ISOWNED)
|
|
{
|
|
turn_on(*mp, NOMOVE);
|
|
msg("The %s is transfixed by your ownership spell.",
|
|
mname);
|
|
}
|
|
|
|
if (rnd(2))
|
|
turn_on(*mp, ISFLEE);
|
|
|
|
turn_on(*mp, ISINVIS);
|
|
updpack();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else /* missed */
|
|
{
|
|
/* If the thing was trying to surprise, no good */
|
|
|
|
if (on(*mp, CANSURPRISE))
|
|
turn_off(*mp, CANSURPRISE);
|
|
|
|
m_bounce(weapon, mname);
|
|
}
|
|
|
|
count = 0;
|
|
|
|
status(FALSE);
|
|
|
|
return(did_hit);
|
|
}
|
|
|
|
|
|
/*
|
|
mon_mon_attack()
|
|
A monster attacks another monster
|
|
*/
|
|
|
|
int
|
|
mon_mon_attack(struct thing *attacker, struct linked_list *mon, struct object *weapon, int thrown)
|
|
{
|
|
struct thing *attackee = THINGPTR(mon);
|
|
int did_hit = FALSE;
|
|
int ee_visible = cansee(attackee->t_pos.y, attackee->t_pos.x);
|
|
int er_visible = cansee(attacker->t_pos.y, attacker->t_pos.x);
|
|
char *mname1 = monsters[attacker->t_index].m_name;
|
|
char *mname2 = monsters[attackee->t_index].m_name;
|
|
|
|
/* Similar monsters don't hit each other */
|
|
|
|
if (attacker->t_index == attackee->t_index)
|
|
{
|
|
if (fam_ptr && attacker == THINGPTR(fam_ptr) && er_visible)
|
|
msg("Master, I cannot hit one of my brethren.");
|
|
if (!thrown && rnd(100) - attacker->t_stats.s_charisma + luck < 0)
|
|
{
|
|
if (er_visible)
|
|
msg("Your %s has made a new ally.", mname1);
|
|
|
|
turn_on(*attackee, ISCHARMED);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/* stop running and any healing */
|
|
|
|
attackee->t_rest_hpt = attackee->t_rest_pow = 0;
|
|
attacker->t_rest_hpt = attacker->t_rest_pow = 0;
|
|
|
|
if (roll_em(attacker, attackee, weapon, thrown,
|
|
wield_weap(weapon, attacker)))
|
|
{
|
|
did_hit = TRUE;
|
|
|
|
if (ee_visible && on(*attackee, CANSURPRISE))
|
|
turn_off(*attackee, CANSURPRISE);
|
|
|
|
if (ee_visible && er_visible && weapon != NULL)
|
|
msg("The %s's %s hits the %s.", mname1,
|
|
weaps[weapon->o_which].w_name, mname2);
|
|
else if (ee_visible && er_visible)
|
|
msg("The %s hits the %s.", mname1, mname2);
|
|
|
|
if (attackee->t_stats.s_hpt <= 0)
|
|
{
|
|
killed(attacker, mon, MESSAGE,
|
|
on(*attacker, ISFAMILIAR) ? POINTS : NOPOINTS);
|
|
return(TRUE);
|
|
}
|
|
}
|
|
else /* missed */
|
|
{
|
|
did_hit = FALSE;
|
|
|
|
if (ee_visible && er_visible && weapon != NULL)
|
|
msg("The %s's %s misses the %s.", mname1,
|
|
weaps[weapon->o_which].w_name, mname2);
|
|
else if (ee_visible && er_visible)
|
|
msg("The %s misses the %s.", mname1, mname2);
|
|
}
|
|
|
|
if (er_visible && !ee_visible)
|
|
msg("The %s struggles with something.",mname1);
|
|
|
|
if (off(*attackee, ISMEAN) && off(*attackee, ISFAMILIAR))
|
|
turn_on(*attackee, ISRUN);
|
|
|
|
count = 0;
|
|
|
|
status(FALSE);
|
|
|
|
return(did_hit);
|
|
}
|
|
|
|
|
|
/*
|
|
swing()
|
|
returns true if the swing hits
|
|
*/
|
|
|
|
int
|
|
swing(int class, int at_lvl, int op_arm, int wplus)
|
|
{
|
|
int res = rnd(20) + 1;
|
|
int need;
|
|
|
|
need = att_mat[class].base -
|
|
att_mat[class].factor *
|
|
((min(at_lvl, att_mat[class].max_lvl) -
|
|
att_mat[class].offset) / att_mat[class].range) +
|
|
(10 - op_arm);
|
|
|
|
if (need > 20 && need <= 25)
|
|
need = 20;
|
|
|
|
return(res + wplus >= need);
|
|
}
|
|
|
|
/*
|
|
init_exp()
|
|
set up initial experience level change threshold
|
|
*/
|
|
|
|
void
|
|
init_exp(void)
|
|
{
|
|
max_stats.s_exp = e_levels[player.t_ctype];
|
|
}
|
|
|
|
/*
|
|
next_exp_level()
|
|
Do the next level arithmetic Returns number of levels to jump
|
|
*/
|
|
|
|
int
|
|
next_exp_level(int print_message)
|
|
{
|
|
int level_jump = 0;
|
|
|
|
while (pstats.s_exp >= max_stats.s_exp)
|
|
{
|
|
pstats.s_exp -= max_stats.s_exp; /* excess experience points */
|
|
level_jump++;
|
|
|
|
if (max_stats.s_exp < 0x3fffffffL) /* 2^30 - 1 */
|
|
max_stats.s_exp *= 2L; /* twice as many for next */
|
|
}
|
|
|
|
if (print_message)
|
|
msg("You need %d more points to attain the %stitle of %s.",
|
|
max_stats.s_exp - pstats.s_exp,
|
|
(pstats.s_lvl > 14 ? "next " : ""),
|
|
cnames[player.t_ctype][min(pstats.s_lvl, 14)]);
|
|
|
|
return(level_jump);
|
|
}
|
|
|
|
/*
|
|
check_level()
|
|
Check to see if the guy has gone up a level.
|
|
*/
|
|
|
|
void
|
|
check_level(void)
|
|
{
|
|
int num_jumped, j, add;
|
|
int nsides;
|
|
|
|
if ((num_jumped = next_exp_level(NOMESSAGE)) <= 0)
|
|
return;
|
|
|
|
pstats.s_lvl += num_jumped; /* new experience level */
|
|
|
|
switch (player.t_ctype)
|
|
{
|
|
case C_MAGICIAN:
|
|
case C_ILLUSION: nsides = 4;
|
|
break;
|
|
case C_THIEF:
|
|
case C_ASSASIN:
|
|
case C_NINJA:
|
|
case C_MONSTER:
|
|
default: nsides = 6;
|
|
break;
|
|
case C_CLERIC:
|
|
case C_DRUID: nsides = 8;
|
|
break;
|
|
|
|
case C_FIGHTER:
|
|
case C_PALADIN:
|
|
case C_RANGER:
|
|
nsides = 12;
|
|
break;
|
|
}
|
|
|
|
/* Take care of multi-level jumps */
|
|
|
|
for (add = 0, j = 0; j < num_jumped; j++)
|
|
{
|
|
int increase = roll(1, nsides) + const_bonus();
|
|
|
|
add += max(1, increase);
|
|
}
|
|
|
|
max_stats.s_hpt += add;
|
|
pstats.s_hpt += add;
|
|
|
|
msg("Welcome, %s, to level %d.",
|
|
cnames[player.t_ctype][min(pstats.s_lvl - 1, 14)], pstats.s_lvl);
|
|
|
|
next_exp_level(MESSAGE);
|
|
|
|
/* Now add new spell points and learn new spells */
|
|
|
|
nsides = 16 - nsides;
|
|
|
|
for (add = 0, j = 0; j < num_jumped; j++)
|
|
{
|
|
int increase = roll(1, nsides) + int_wis_bonus();
|
|
|
|
add += max(1, increase);
|
|
}
|
|
|
|
max_stats.s_power += add;
|
|
pstats.s_power += add;
|
|
|
|
learn_new_spells();
|
|
|
|
/* Create a more powerful familiar (if player has one) */
|
|
|
|
if (on(player, HASFAMILIAR) && on(player, CANSUMMON))
|
|
summon_monster((short) 0, FAMILIAR, NOMESSAGE);
|
|
}
|
|
|
|
/*
|
|
roll_em()
|
|
Roll several attacks
|
|
*/
|
|
|
|
int
|
|
roll_em(struct thing *att_er, struct thing *def_er, struct object *weap, int thrown, struct object *my_weapon)
|
|
{
|
|
struct stats *att = &att_er->t_stats;
|
|
struct stats *def = &def_er->t_stats;
|
|
int ndice, nsides, nplus, def_arm;
|
|
char *cp;
|
|
int prop_hplus = 0, prop_dplus = 0;
|
|
int is_player = (att_er == &player);
|
|
int did_hit = FALSE;
|
|
|
|
if (weap == NULL)
|
|
cp = att->s_dmg;
|
|
else if (!thrown)
|
|
cp = weap->o_damage;
|
|
else if ((weap->o_flags & ISMISL) && my_weapon != NULL &&
|
|
my_weapon->o_which == weap->o_launch)
|
|
{
|
|
cp = weap->o_hurldmg;
|
|
prop_hplus = my_weapon->o_hplus;
|
|
prop_dplus = my_weapon->o_dplus;
|
|
}
|
|
else
|
|
cp = (weap->o_flags & ISMISL ? weap->o_damage :
|
|
weap->o_hurldmg);
|
|
|
|
for (;;)
|
|
{
|
|
int damage;
|
|
int hplus = prop_hplus + (weap == NULL ? 0 : weap->o_hplus);
|
|
int dplus = prop_dplus + (weap == NULL ? 0 : weap->o_dplus);
|
|
|
|
/* Is attacker weak? */
|
|
|
|
if (on(*att_er, HASSTINK))
|
|
hplus -= 2;
|
|
|
|
if (is_player)
|
|
{
|
|
hplus += hitweight(); /* adjust for encumberence */
|
|
dplus += hung_dam(); /* adjust damage for hungry player */
|
|
dplus += ring_value(R_ADDDAM);
|
|
}
|
|
|
|
ndice = atoi(cp);
|
|
|
|
if (cp == NULL || (cp = strchr(cp, 'd')) == NULL)
|
|
break;
|
|
|
|
nsides = atoi(++cp);
|
|
|
|
if (cp != NULL && (cp = strchr(cp, '+')) != NULL)
|
|
nplus = atoi(++cp);
|
|
else
|
|
nplus = 0;
|
|
|
|
if (def == &pstats)
|
|
{
|
|
if (on(*att_er, NOMETAL) && cur_armor != NULL &&
|
|
(cur_armor->o_which == RING_MAIL ||
|
|
cur_armor->o_which == SCALE_MAIL ||
|
|
cur_armor->o_which == CHAIN_MAIL ||
|
|
cur_armor->o_which == SPLINT_MAIL ||
|
|
cur_armor->o_which == BANDED_MAIL ||
|
|
cur_armor->o_which == GOOD_CHAIN ||
|
|
cur_armor->o_which == PLATE_MAIL ||
|
|
cur_armor->o_which == PLATE_ARMOR))
|
|
def_arm = def->s_arm;
|
|
else if (cur_armor != NULL)
|
|
def_arm = cur_armor->o_ac - 10 + pstats.s_arm;
|
|
else
|
|
def_arm = def->s_arm;
|
|
def_arm -= ring_value(R_PROTECT);
|
|
}
|
|
else
|
|
def_arm = def->s_arm;
|
|
|
|
if ((weap != NULL && weap->o_type == WEAPON &&
|
|
(weap->o_flags & ISSILVER) &&
|
|
!save_throw(VS_MAGIC, def_er)) ||
|
|
swing(att_er->t_ctype, att->s_lvl,
|
|
def_arm - dext_prot(def->s_dext),
|
|
hplus + str_plus(att->s_str) + dext_plus(att->s_dext)))
|
|
{
|
|
damage = roll(ndice, nsides) + dplus + nplus +
|
|
add_dam(att->s_str);
|
|
|
|
/* Rangers do +1/lvl vs. ISLARGE */
|
|
|
|
if (att_er->t_ctype == C_RANGER && on(*def_er, ISLARGE))
|
|
damage += pstats.s_lvl;
|
|
|
|
/* Ninja do +1 per lvl/2 */
|
|
|
|
if (att_er->t_ctype == C_NINJA)
|
|
damage += pstats.s_lvl / 2;
|
|
|
|
/* Check for half damage monsters */
|
|
|
|
if (on(*def_er, HALFDAMAGE) && (weap != NULL) &&
|
|
!((weap->o_flags & CANBURN) &&
|
|
on(*def_er, CANBBURN)))
|
|
damage /= 2;
|
|
|
|
/* undead get twice damage from silver weapons */
|
|
|
|
if (on(*def_er, ISUNDEAD) &&
|
|
(weap != NULL) && (weap->o_flags & ISSILVER))
|
|
damage *= 2;
|
|
|
|
/* Check for fireproof monsters */
|
|
|
|
if (on(*def_er, NOFIRE) && (weap != NULL) &&
|
|
(weap->o_flags & CANBURN))
|
|
damage = 0;
|
|
|
|
/* Check for metal proof monsters */
|
|
|
|
if (on(*def_er, NOMETAL) && (weap != NULL) &&
|
|
(weap->o_flags & ISMETAL))
|
|
damage = 0;
|
|
|
|
/* Check for monsters that ignore sharp weapons */
|
|
|
|
if (on(*def_er, NOSHARP) && (weap != NULL) &&
|
|
(weap->o_flags & ISSHARP))
|
|
damage = 0;
|
|
|
|
/* Check for poisoned weapons */
|
|
|
|
if ((weap != NULL) && (weap->o_flags & ISPOISON)
|
|
&& off(*def_er, ISUNDEAD)
|
|
&& !save_throw(VS_POISON, def_er))
|
|
damage = max(damage, (def->s_hpt / 2) + 5);
|
|
|
|
/* Check for no-damage and division */
|
|
|
|
if (on(*def_er, BLOWDIVIDE) && rnd(3) == 0 &&
|
|
!((weap != NULL) && (weap->o_flags & CANBURN)))
|
|
{
|
|
damage = 0;
|
|
creat_mons(def_er, def_er->t_index, NOMESSAGE);
|
|
}
|
|
|
|
damage = max(0, damage);
|
|
|
|
/*
|
|
* sleeping monsters are backstabbed by certain
|
|
* player classes, but only when they can see
|
|
*/
|
|
|
|
if (is_player && !thrown && damage > 0 &&
|
|
(off(*def_er, ISRUN) || def_er->t_no_move > 0) &&
|
|
(player.t_ctype == C_THIEF ||
|
|
player.t_ctype == C_NINJA ||
|
|
player.t_ctype == C_ASSASIN) &&
|
|
off(player,ISBLIND)
|
|
&& (wield_ok(&player, my_weapon, NOMESSAGE))
|
|
&& (wear_ok(&player, cur_armor, NOMESSAGE)))
|
|
{
|
|
damage *= (pstats.s_lvl / 4 + 2);
|
|
|
|
msg("You backstabbed the %s %d times!",
|
|
monsters[def_er->t_index].m_name,
|
|
(pstats.s_lvl / 4) + 2);
|
|
|
|
if (player.t_ctype == C_NINJA ||
|
|
player.t_ctype == C_ASSASIN)
|
|
pstats.s_exp += def_er->t_stats.s_exp
|
|
/ 2;
|
|
}
|
|
|
|
def->s_hpt -= damage; /* Do the damage */
|
|
|
|
debug("Hit %s for %d (%d) ",
|
|
monsters[def_er->t_index].m_name, damage,
|
|
def_er->t_stats.s_hpt);
|
|
|
|
if (is_player && is_wearing(R_VREGEN))
|
|
{
|
|
damage = (ring_value(R_VREGEN) * damage) / 3;
|
|
pstats.s_hpt = min(max_stats.s_hpt,
|
|
pstats.s_hpt + damage);
|
|
}
|
|
|
|
/* stun monsters when taking more than 1/3 their max hpts */
|
|
|
|
if (is_player && !thrown && !did_hit &&
|
|
(player.t_ctype == C_FIGHTER) &&
|
|
(damage > def_er->maxstats.s_hpt / 3) )
|
|
{
|
|
if (def->s_hpt > 0)
|
|
{
|
|
msg("The %s has been stunned!",
|
|
monsters[def_er->t_index].m_name);
|
|
def_er->t_no_move += rnd(4) + 1;
|
|
}
|
|
pstats.s_exp += def_er->t_stats.s_exp / 4;
|
|
}
|
|
|
|
did_hit = TRUE;
|
|
}
|
|
|
|
if (cp == NULL || (cp = strchr(cp, '/')) == NULL)
|
|
break;
|
|
|
|
cp++;
|
|
}
|
|
|
|
return(did_hit);
|
|
}
|
|
|
|
/*
|
|
prname()
|
|
Figure out the monsters name
|
|
*/
|
|
|
|
const char *
|
|
prname(char *who)
|
|
{
|
|
if (on(player, ISBLIND))
|
|
return(monstern);
|
|
else
|
|
return(who);
|
|
}
|
|
|
|
/*
|
|
hit()
|
|
Print a message to indicate a succesful hit
|
|
*/
|
|
|
|
void
|
|
hit(char *ee)
|
|
{
|
|
char *s;
|
|
|
|
if (fighting)
|
|
return;
|
|
|
|
switch (rnd(15))
|
|
{
|
|
default: s = "hit"; break;
|
|
case 1: s = "score an excellent hit on"; break;
|
|
case 2: s = "injure"; break;
|
|
case 3: s = "swing and hit"; break;
|
|
case 4: s = "damage"; break;
|
|
case 5: s = "barely nick"; break;
|
|
case 6: s = "scratch"; break;
|
|
case 7: s = "gouge a chunk out of"; break;
|
|
case 8: s = "severely wound"; break;
|
|
case 9: s = "counted coup on"; break;
|
|
case 10: s = "drew blood from"; break;
|
|
case 11: s = "nearly decapitate"; break;
|
|
case 12: s = "deal a wacking great blow to"; break;
|
|
}
|
|
|
|
msg("You %s the %s.", s, prname(ee));
|
|
}
|
|
|
|
/*
|
|
miss()
|
|
Print a message to indicate a poor swing
|
|
*/
|
|
|
|
void
|
|
miss(char *ee)
|
|
{
|
|
char *s;
|
|
|
|
if (fighting)
|
|
return;
|
|
|
|
switch (rnd(10))
|
|
{
|
|
default: s = "miss"; break;
|
|
case 1: s = "swing and miss"; break;
|
|
case 2: s = "barely miss"; break;
|
|
case 3: s = "don't hit"; break;
|
|
case 4: s = "wildly windmill around"; break;
|
|
case 5: s = "almost fumble while missing"; break;
|
|
}
|
|
|
|
msg("You %s the %s.", s, prname(ee));
|
|
}
|
|
|
|
/*
|
|
save_throw()
|
|
See if a creature save against something
|
|
*/
|
|
|
|
int
|
|
save_throw(int which, struct thing *tp)
|
|
{
|
|
int need;
|
|
int ring_bonus = 0;
|
|
int armor_bonus = 0;
|
|
int class_bonus = 0;
|
|
|
|
if (tp == &player)
|
|
{
|
|
if (player.t_ctype == C_PALADIN)
|
|
class_bonus = 2;
|
|
|
|
ring_bonus = ring_value(R_PROTECT);
|
|
|
|
if (cur_armor != NULL && (which == VS_WAND ||
|
|
which == VS_MAGIC))
|
|
{
|
|
if (cur_armor->o_which == MITHRIL)
|
|
armor_bonus += 5;
|
|
armor_bonus += (armors[cur_armor->o_which].a_class
|
|
- cur_armor->o_ac);
|
|
}
|
|
}
|
|
|
|
need = 14 + which - tp->t_stats.s_lvl / 2 - ring_bonus -
|
|
armor_bonus - class_bonus;
|
|
|
|
/* Roll of 1 always fails; 20 always saves */
|
|
|
|
if (need < 1)
|
|
need = 1;
|
|
else if (need > 20)
|
|
need = 20;
|
|
|
|
return(roll(1, 20) >= need);
|
|
}
|
|
|
|
/*
|
|
save()
|
|
See if he saves against various nasty things
|
|
*/
|
|
|
|
int
|
|
save(int which)
|
|
{
|
|
return save_throw(which, &player);
|
|
}
|
|
|
|
/*
|
|
dext_plus()
|
|
compute to-hit bonus for dexterity
|
|
*/
|
|
|
|
int
|
|
dext_plus(int dexterity)
|
|
{
|
|
return ((dexterity - 10) / 3);
|
|
}
|
|
|
|
/*
|
|
* dext_prot: compute armor class bonus for dexterity
|
|
*/
|
|
|
|
int
|
|
dext_prot(int dexterity)
|
|
{
|
|
return ((dexterity - 9) / 2);
|
|
}
|
|
|
|
/*
|
|
str_plus()
|
|
compute bonus/penalties for strength on the "to hit" roll
|
|
*/
|
|
|
|
static const int strtohit[] =
|
|
{
|
|
0, 0, 0, -3, -2, -2, -1, -1,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 1, 3, 3, 4, 4, 5, 6, 7
|
|
};
|
|
|
|
int
|
|
str_plus(int str)
|
|
{
|
|
int ret_val = str;
|
|
|
|
if (str < 3)
|
|
ret_val = 3;
|
|
else if (str > 25)
|
|
ret_val = 25;
|
|
|
|
return(strtohit[ret_val]);
|
|
}
|
|
|
|
/*
|
|
add_dam()
|
|
compute additional damage done for exceptionally high or low strength
|
|
*/
|
|
|
|
static const int str_damage[] =
|
|
{
|
|
0, 0, 0, -1, -1, -1, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 1,
|
|
1, 2, 7, 8, 9, 10, 11, 12, 14
|
|
};
|
|
|
|
int
|
|
add_dam(int str)
|
|
{
|
|
int ret_val = str;
|
|
|
|
if (str < 3)
|
|
ret_val = 3;
|
|
else if (str > 25)
|
|
ret_val = 25;
|
|
|
|
return(str_damage[ret_val]);
|
|
}
|
|
|
|
/*
|
|
hung_dam()
|
|
Calculate damage depending on players hungry state
|
|
*/
|
|
|
|
int
|
|
hung_dam(void)
|
|
{
|
|
int howmuch = 0;
|
|
|
|
switch (hungry_state)
|
|
{
|
|
case F_OK:
|
|
case F_HUNGRY: howmuch = 0; break;
|
|
case F_WEAK: howmuch = -1; break;
|
|
case F_FAINT: howmuch = -2; break;
|
|
}
|
|
|
|
return(howmuch);
|
|
}
|
|
|
|
/*
|
|
raise_level()
|
|
The guy just magically went up a level.
|
|
*/
|
|
|
|
void
|
|
raise_level(void)
|
|
{
|
|
pstats.s_exp = max_stats.s_exp;
|
|
check_level();
|
|
}
|
|
|
|
/*
|
|
thunk()
|
|
A missile hits a monster
|
|
*/
|
|
|
|
void
|
|
thunk(struct object *weap, char *mname)
|
|
{
|
|
if (fighting)
|
|
return;
|
|
|
|
if (weap->o_type == WEAPON)
|
|
msg("The %s hits the %s.", weaps[weap->o_which].w_name, prname(mname));
|
|
else
|
|
msg("You hit the %s.", prname(mname));
|
|
}
|
|
|
|
/*
|
|
m_thunk()
|
|
A missile from a monster hits the player
|
|
*/
|
|
|
|
void
|
|
m_thunk(struct object *weap, char *mname)
|
|
{
|
|
if (fighting)
|
|
return;
|
|
|
|
if (weap != NULL && weap->o_type == WEAPON)
|
|
msg("The %s's %s hits you.",prname(mname),weaps[weap->o_which].w_name);
|
|
else
|
|
msg("The %s hits you.", prname(mname));
|
|
}
|
|
|
|
/*
|
|
bounce()
|
|
A missile misses a monster
|
|
*/
|
|
|
|
void
|
|
bounce(struct object *weap, char *mname)
|
|
{
|
|
if (fighting)
|
|
return;
|
|
|
|
if (weap->o_type == WEAPON)
|
|
msg("The %s misses the %s.",weaps[weap->o_which].w_name,prname(mname));
|
|
else
|
|
msg("You missed the %s.", prname(mname));
|
|
}
|
|
|
|
/*
|
|
m_bounce()
|
|
A missile from a monster misses the player
|
|
*/
|
|
|
|
void
|
|
m_bounce(struct object *weap, char *mname)
|
|
{
|
|
if (fighting)
|
|
return;
|
|
|
|
if (weap != NULL && weap->o_type == WEAPON)
|
|
msg("The %s's %s misses you.", prname(mname),
|
|
weaps[weap->o_which].w_name);
|
|
else
|
|
msg("The %s misses you.", prname(mname));
|
|
}
|
|
|
|
/*
|
|
remove_monster()
|
|
remove a monster from the screen
|
|
*/
|
|
|
|
void
|
|
remove_monster(coord *mp, struct linked_list *item)
|
|
{
|
|
struct thing *tp = THINGPTR(item);
|
|
char ch = tp->t_oldch;
|
|
|
|
mvwaddch(mw, mp->y, mp->x, ' ');
|
|
|
|
if (ch < 33 || ch == ' ')
|
|
ch = CCHAR( mvwinch(stdscr, mp->y, mp->x) );
|
|
|
|
if (cansee(mp->y, mp->x))
|
|
mvwaddch(cw, mp->y, mp->x, ch);
|
|
|
|
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 != armors[obj->o_which].a_class);
|
|
|
|
case WEAPON:
|
|
return(obj->o_hplus != 0 || obj->o_dplus != 0);
|
|
|
|
case POTION:
|
|
case SCROLL:
|
|
case STICK:
|
|
case RING:
|
|
case ARTIFACT:
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/*
|
|
killed()
|
|
Called to put a monster to death
|
|
*/
|
|
|
|
void
|
|
killed(struct thing *killer, struct linked_list *item, int print_message,
|
|
int give_points)
|
|
{
|
|
struct linked_list *pitem, *nitem;
|
|
struct thing *tp = THINGPTR(item);
|
|
int visible = cansee(tp->t_pos.y, tp->t_pos.x);
|
|
int is_player = (killer == (&player));
|
|
|
|
if (item == curr_mons)
|
|
curr_mons = NULL;
|
|
else if (item == next_mons)
|
|
next_mons = next(next_mons);
|
|
|
|
if (on(*tp, WASSUMMONED))
|
|
{
|
|
extinguish_fuse(FUSE_UNSUMMON);
|
|
turn_off(player, HASSUMMONED);
|
|
}
|
|
|
|
if (print_message && visible)
|
|
{
|
|
if (is_player)
|
|
addmsg("You have defeated ");
|
|
else
|
|
addmsg("The %s has defeated ",
|
|
monsters[killer->t_index].m_name);
|
|
|
|
if (on(player, ISBLIND))
|
|
msg("it.");
|
|
else
|
|
msg("the %s.", monsters[tp->t_index].m_name);
|
|
}
|
|
debug("Removing %s", monsters[tp->t_index].m_name);
|
|
if (killer != NULL && item == fam_ptr) /* The player's familiar died */
|
|
{
|
|
turn_off(player, HASFAMILIAR);
|
|
fam_ptr = NULL;
|
|
msg("An incredible wave of sadness sweeps over you.");
|
|
}
|
|
|
|
check_residue(tp);
|
|
|
|
if (is_player)
|
|
{
|
|
fighting = FALSE;
|
|
|
|
if (on(*tp, ISFRIENDLY))
|
|
{
|
|
msg("You feel a slight chill run up and down your spine.");
|
|
luck++;
|
|
}
|
|
}
|
|
|
|
if (give_points)
|
|
{
|
|
if (killer != NULL)
|
|
{
|
|
killer->t_stats.s_exp += tp->t_stats.s_exp;
|
|
|
|
if (on(*killer, ISFAMILIAR))
|
|
pstats.s_exp += tp->t_stats.s_exp;
|
|
}
|
|
|
|
if (is_player)
|
|
{
|
|
switch (player.t_ctype)
|
|
{
|
|
case C_CLERIC:
|
|
case C_PALADIN:
|
|
if (on(*tp, ISUNDEAD) || on(*tp, ISUNIQUE))
|
|
{
|
|
pstats.s_exp += tp->t_stats.s_exp / 2;
|
|
msg("You are to be commended for smiting the ungodly.");
|
|
}
|
|
break;
|
|
|
|
case C_DRUID:
|
|
case C_RANGER:
|
|
if (on(*tp, ISLARGE))
|
|
{
|
|
pstats.s_exp += tp->t_stats.s_exp / 2;
|
|
msg("Congratulations on smiting a dangerous monster.");
|
|
}
|
|
break;
|
|
|
|
case C_MAGICIAN:
|
|
case C_ILLUSION:
|
|
if (on(*tp, DRAINBRAIN))
|
|
{
|
|
pstats.s_exp += tp->t_stats.s_exp / 2;
|
|
msg("Congratulations on smiting a dangerous monster.");
|
|
}
|
|
|
|
}
|
|
}
|
|
check_level();
|
|
}
|
|
|
|
/* Empty the monsters pack */
|
|
|
|
for (pitem = tp->t_pack; pitem != NULL; pitem = nitem)
|
|
{
|
|
struct object *obj = OBJPTR(pitem);
|
|
|
|
nitem = next(pitem);
|
|
|
|
obj->o_pos = tp->t_pos;
|
|
detach(tp->t_pack, pitem);
|
|
|
|
if (killer == NULL)
|
|
discard(pitem);
|
|
else
|
|
fall(killer, pitem, FALSE, FALSE);
|
|
}
|
|
|
|
remove_monster(&tp->t_pos, item);
|
|
}
|
|
|
|
|
|
/*
|
|
wield_weap()
|
|
Returns a pointer to the weapon the monster is wielding corresponding to the given thrown weapon
|
|
*/
|
|
|
|
struct object *
|
|
wield_weap(struct object *weapon, struct thing *mp)
|
|
{
|
|
int look_for;
|
|
struct linked_list *pitem;
|
|
|
|
if (weapon == NULL)
|
|
return (NULL);
|
|
|
|
switch (weapon->o_which)
|
|
{
|
|
case BOLT:
|
|
look_for = CROSSBOW;
|
|
break;
|
|
|
|
case ARROW:
|
|
look_for = BOW;
|
|
break;
|
|
|
|
case SILVERARROW:
|
|
case FLAMEARROW:
|
|
look_for = BOW;
|
|
break;
|
|
|
|
case ROCK:
|
|
case BULLET:
|
|
look_for = SLING;
|
|
break;
|
|
|
|
default:
|
|
return(NULL);
|
|
}
|
|
|
|
for (pitem = mp->t_pack; pitem; pitem = next(pitem))
|
|
if ((OBJPTR(pitem))->o_which == look_for)
|
|
return(OBJPTR(pitem));
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
summon_help()
|
|
Summon - see whether to summon help Returns TRUE if help comes, FALSE
|
|
otherwise
|
|
*/
|
|
|
|
void
|
|
summon_help(struct thing *mons, int force)
|
|
{
|
|
char *helpname;
|
|
int which, i;
|
|
char *mname = monsters[mons->t_index].m_name;
|
|
|
|
/* Try to summon if less than 1/3 max hit points */
|
|
|
|
if (on(*mons, CANSUMMON) &&
|
|
(force == FORCE ||
|
|
((mons->t_stats.s_hpt < mons->maxstats.s_hpt / 3) &&
|
|
(rnd(40 * 10) < (mons->t_stats.s_lvl * mons->t_stats.s_intel)))))
|
|
{
|
|
turn_off(*mons, CANSUMMON);
|
|
msg("The %s summons its attendants!", mname);
|
|
helpname = monsters[mons->t_index].m_typesum;
|
|
|
|
for (which = 1; which < nummonst; which++)
|
|
{
|
|
if (strcmp(helpname, monsters[which].m_name) == 0)
|
|
break;
|
|
}
|
|
|
|
if (which >= nummonst)
|
|
{
|
|
debug("Couldn't find summoned one.");
|
|
return;
|
|
}
|
|
|
|
/* summoned monster was genocided */
|
|
|
|
if (!monsters[which].m_normal)
|
|
{
|
|
msg("The %s becomes very annoyed at you!", mname);
|
|
|
|
if (on(*mons, ISSLOW))
|
|
turn_off(*mons, ISSLOW);
|
|
else
|
|
turn_on(*mons, ISHASTE);
|
|
|
|
return;
|
|
}
|
|
else
|
|
for (i = 0; i < monsters[mons->t_index].m_numsum; i++)
|
|
{
|
|
struct linked_list *ip;
|
|
struct thing *tp;
|
|
|
|
if ((ip = creat_mons(mons, which, NOMESSAGE)) != NULL)
|
|
{
|
|
tp = THINGPTR(ip);
|
|
turn_off(*tp, ISFRIENDLY);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
maxdamage()
|
|
return the max damage a weapon can do
|
|
*/
|
|
|
|
int
|
|
maxdamage(char *cp)
|
|
{
|
|
int ndice, nsides, nplus;
|
|
|
|
ndice = atoi(cp);
|
|
|
|
if (cp == NULL || (cp = strchr(cp, 'd')) == NULL)
|
|
return(0);
|
|
|
|
nsides = atoi(++cp);
|
|
|
|
if (cp != NULL && (cp = strchr(cp, '+')) != NULL)
|
|
nplus = atoi(++cp);
|
|
else
|
|
nplus = 0;
|
|
|
|
return(ndice * nsides + nplus);
|
|
}
|