Skip to content

Commit

Permalink
Rework various negative energy checks
Browse files Browse the repository at this point in the history
A single level of rN no longer grants immunity to various effects, and instead
increasing levels of rN reduce the effect to 50%/25%/0%, as was already the
case for the main rN checks (BEAM_NEG damage, and the amount of draining
applied).

Pain brand, staff of death, vampiric brand, vampiric attack flavour, pain
spell, vampiric draining spell, Yredelemnul drain life all now scale in the
same way against rN, and only rN+++ grants full immunity.

Agony is reworked to actually be MR-resistible single-target torment - its
damage is reduced by rN in the same way as torment (5% per level), Kiku can
protect against it, and only fully torment-immune creatures are unaffected.
  • Loading branch information
Chris Campbell committed Aug 6, 2016
1 parent 9c76b8a commit 3c1ee9c
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 105 deletions.
55 changes: 27 additions & 28 deletions crawl-ref/source/attack.cc
Expand Up @@ -559,20 +559,19 @@ static actor* _pain_weapon_user(actor* attacker)

void attack::pain_affects_defender()
{
if (defender->res_negative_energy())
return;

actor* user = _pain_weapon_user(attacker);
if (!one_chance_in(user->skill_rdiv(SK_NECROMANCY) + 1))
{
if (defender_visible)
special_damage += resist_adjust_damage(defender, BEAM_NEG,
random2(1 + user->skill_rdiv(SK_NECROMANCY)));

if (special_damage && defender_visible)
{
special_damage_message =
make_stringf("%s %s in agony.",
defender->name(DESC_THE).c_str(),
defender->conj_verb("writhe").c_str());
}
special_damage += random2(1 + user->skill_rdiv(SK_NECROMANCY));
}
}

Expand Down Expand Up @@ -1687,7 +1686,6 @@ bool attack::apply_damage_brand(const char *what)
{
if (!weapon
|| !(defender->holiness() & MH_NATURAL)
|| defender->res_negative_energy()
|| damage_done < 1
|| attacker->stat_hp() == attacker->stat_maxhp()
|| !defender->is_player()
Expand All @@ -1698,33 +1696,34 @@ bool attack::apply_damage_brand(const char *what)
break;
}

obvious_effect = true;
int hp_boost = is_unrandom_artefact(*weapon, UNRAND_VAMPIRES_TOOTH)
? damage_done : 1 + random2(damage_done);
hp_boost = resist_adjust_damage(defender, BEAM_NEG, hp_boost);

// Handle weapon effects.
// We only get here if we've done base damage, so no
// worries on that score.
if (attacker->is_player())
canned_msg(MSG_GAIN_HEALTH);
else if (attacker_visible)
if (hp_boost)
{
if (defender->is_player())
{
mprf("%s draws strength from your injuries!",
attacker->name(DESC_THE).c_str());
}
else
obvious_effect = true;

if (attacker->is_player())
canned_msg(MSG_GAIN_HEALTH);
else if (attacker_visible)
{
mprf("%s is healed.",
attacker->name(DESC_THE).c_str());
if (defender->is_player())
{
mprf("%s draws strength from your injuries!",
attacker->name(DESC_THE).c_str());
}
else
{
mprf("%s is healed.",
attacker->name(DESC_THE).c_str());
}
}
}

int hp_boost = is_unrandom_artefact(*weapon, UNRAND_VAMPIRES_TOOTH)
? damage_done : 1 + random2(damage_done);

dprf(DIAG_COMBAT, "Vampiric Healing: damage %d, healed %d",
damage_done, hp_boost);
attacker->heal(hp_boost);
dprf(DIAG_COMBAT, "Vampiric Healing: damage %d, healed %d",
damage_done, hp_boost);
attacker->heal(hp_boost);
}
break;
}
case SPWPN_PAIN:
Expand Down
71 changes: 38 additions & 33 deletions crawl-ref/source/beam.cc
Expand Up @@ -200,6 +200,7 @@ static void _ench_animation(int flavour, const monster* mon, bool force)
break;
case BEAM_INFESTATION:
case BEAM_PAIN:
case BEAM_AGONY:
elem = ETC_UNHOLY;
break;
case BEAM_DISPEL_UNDEAD:
Expand Down Expand Up @@ -2428,7 +2429,7 @@ static void _malign_offering_effect(actor* victim, const actor* agent, int damag
coord_def c = victim->pos();

mprf("%s life force is offered up.", victim->name(DESC_ITS).c_str());
damage = victim->hurt(agent, damage, BEAM_NEG, KILLED_BY_BEAM,
damage = victim->hurt(agent, damage, BEAM_MALIGN_OFFERING, KILLED_BY_BEAM,
"", "by a malign offering");

// Actors that had LOS to the victim (blocked by glass, clouds, etc),
Expand Down Expand Up @@ -3621,34 +3622,24 @@ void bolt::affect_player_enchantment(bool resistible)
break;

case BEAM_PAIN:
if (player_res_torment())
{
canned_msg(MSG_YOU_UNAFFECTED);
break;
}

{
if (aux_source.empty())
aux_source = "by nerve-wracking pain";

if (origin_spell == SPELL_AGONY)
const int dam = resist_adjust_damage(&you, flavour, damage.roll());
if (dam)
{
if (you.res_negative_energy()) // Agony has no effect with rN.
{
canned_msg(MSG_YOU_UNAFFECTED);
break;
}

mpr("Your body is wracked with pain!");

// On the player, Agony acts like single-target torment.
internal_ouch(max(0, you.hp / 2 - 1));
mpr("Pain shoots through your body!");
internal_ouch(dam);
obvious_effect = true;
}
else
{
mpr("Pain shoots through your body!");
canned_msg(MSG_YOU_UNAFFECTED);
break;
}

internal_ouch(damage.roll());
}
case BEAM_AGONY:
torment_player(agent(), TORMENT_AGONY);
obvious_effect = true;
break;

Expand Down Expand Up @@ -3740,7 +3731,6 @@ void bolt::affect_player_enchantment(bool resistible)
}

case BEAM_VIRULENCE:

// Those completely immune cannot be made more susceptible this way
if (you.res_poison(false) >= 3)
{
Expand Down Expand Up @@ -5234,7 +5224,11 @@ bool ench_flavour_affects_monster(beam_type flavour, const monster* mon,
break;

case BEAM_PAIN:
rc = !mon->res_negative_energy(intrinsic_only);
rc = mon->res_negative_energy(intrinsic_only) < 3;
break;

case BEAM_AGONY:
rc = !mon->res_torment();
break;

case BEAM_HIBERNATION:
Expand Down Expand Up @@ -5436,14 +5430,22 @@ mon_resist_type bolt::apply_enchantment_to_monster(monster* mon)
return MON_AFFECTED;
}

case BEAM_PAIN: // pain/agony
if (simple_monster_message(mon, " convulses in agony!"))
obvious_effect = true;
case BEAM_PAIN:
{
const int dam = resist_adjust_damage(mon, flavour, damage.roll());
if (dam)
{
if (simple_monster_message(mon, " writhes in agony!"))
obvious_effect = true;
mon->hurt(agent(), dam, flavour);
return MON_AFFECTED;
}
return MON_UNAFFECTED;
}

if (origin_spell == SPELL_AGONY)
mon->hurt(agent(), min((mon->hit_points+1)/2, mon->hit_points-1), BEAM_TORMENT_DAMAGE);
else // pain
mon->hurt(agent(), damage.roll(), flavour);
case BEAM_AGONY:
torment_cell(mon->pos(), agent(), TORMENT_AGONY);
obvious_effect = you.can_see(*mon);
return MON_AFFECTED;

case BEAM_DISINTEGRATION: // disrupt/disintegrate
Expand Down Expand Up @@ -6325,9 +6327,11 @@ bool bolt::nasty_to(const monster* mon) const
if (flavour == BEAM_DISPEL_UNDEAD)
return bool(mon->holiness() & MH_UNDEAD);

// pain / agony
if (flavour == BEAM_PAIN)
return !mon->res_negative_energy();
return mon->res_negative_energy() < 3;

if (flavour == BEAM_AGONY)
return !mon->res_torment();

if (flavour == BEAM_TUKIMAS_DANCE)
return tukima_affects(*mon);
Expand Down Expand Up @@ -6561,6 +6565,7 @@ static string _beam_type_name(beam_type type)
case BEAM_BANISH: return "banishment";
case BEAM_ENSLAVE_SOUL: return "enslave soul";
case BEAM_PAIN: return "pain";
case BEAM_AGONY: return "agony";
case BEAM_DISPEL_UNDEAD: return "dispel undead";
case BEAM_DISINTEGRATION: return "disintegration";
case BEAM_BLINK: return "blink";
Expand Down
2 changes: 1 addition & 1 deletion crawl-ref/source/describe.cc
Expand Up @@ -2946,7 +2946,7 @@ static const char* _describe_attack_flavour(attack_flavour flavour)
case AF_POISON: return "cause poisoning";
case AF_POISON_STRONG: return "cause strong poisoning";
case AF_ROT: return "cause rotting";
case AF_VAMPIRIC: return "drain health";
case AF_VAMPIRIC: return "drain health from the living";
case AF_KLOWN: return "cause random powerful effects";
case AF_DISTORT: return "cause wild translocation effects";
case AF_RAGE: return "cause berserking";
Expand Down
4 changes: 3 additions & 1 deletion crawl-ref/source/enum.h
Expand Up @@ -671,7 +671,8 @@ enum beam_type // bolt::flavour
BEAM_SHARED_PAIN,
BEAM_IRRESISTIBLE_CONFUSION,
BEAM_INFESTATION,
BEAM_LAST_ENCHANTMENT = BEAM_INFESTATION,
BEAM_AGONY,
BEAM_LAST_ENCHANTMENT = BEAM_AGONY,

BEAM_MEPHITIC,
BEAM_INK,
Expand Down Expand Up @@ -4714,6 +4715,7 @@ enum torment_source_type
TORMENT_XOM = -6, // Xom effect
TORMENT_KIKUBAAQUDGHA = -7, // Kikubaaqudgha effect
TORMENT_MISCAST = -8,
TORMENT_AGONY = -9, // SPELL_AGONY
};

enum trap_type
Expand Down
9 changes: 8 additions & 1 deletion crawl-ref/source/fight.cc
Expand Up @@ -481,6 +481,7 @@ static int _beam_to_resist(const actor* defender, beam_type flavour)
case BEAM_ELECTRICITY:
return defender->res_elec();
case BEAM_NEG:
case BEAM_PAIN:
case BEAM_MALIGN_OFFERING:
return defender->res_negative_energy();
case BEAM_ACID:
Expand Down Expand Up @@ -527,6 +528,8 @@ int resist_adjust_damage(const actor* defender, beam_type flavour, int rawdamage
{
const bool immune_at_3_res = is_mon
|| flavour == BEAM_NEG
|| flavour == BEAM_PAIN
|| flavour == BEAM_MALIGN_OFFERING
|| flavour == BEAM_POISON
// just the resistible part
|| flavour == BEAM_POISON_ARROW;
Expand All @@ -541,8 +544,12 @@ int resist_adjust_damage(const actor* defender, beam_type flavour, int rawdamage
// Monster resistances are stronger than player versions.
if (is_mon)
resistible /= 1 + bonus_res + res * res;
else if (flavour == BEAM_NEG)
else if (flavour == BEAM_NEG
|| flavour == BEAM_PAIN
|| flavour == BEAM_MALIGN_OFFERING)
{
resistible /= res * 2;
}
else
resistible /= (3 * res + 1) / 2 + bonus_res;
}
Expand Down
37 changes: 19 additions & 18 deletions crawl-ref/source/melee_attack.cc
Expand Up @@ -2209,18 +2209,18 @@ void melee_attack::apply_staff_damage()
}

case STAFF_DEATH:
if (defender->res_negative_energy())
break;

special_damage = staff_damage(SK_NECROMANCY);
special_damage =
resist_adjust_damage(defender,
BEAM_NEG,
staff_damage(SK_NECROMANCY));

if (special_damage)
{
special_damage_message =
make_stringf(
"%s convulse%s in agony!",
"%s %s in agony!",
defender->name(DESC_THE).c_str(),
defender->is_player() ? "" : "s");
defender->conj_verb("writhe").c_str());

attacker->god_conduct(DID_EVIL, 4);
}
Expand Down Expand Up @@ -2737,26 +2737,27 @@ void melee_attack::mons_apply_attack_flavour()

// deliberate fall-through
case AF_VAMPIRIC:
// Only may bite non-vampiric monsters (or player) capable of bleeding.
if (!defender->can_bleed())
if (!(defender->holiness() & MH_NATURAL))
break;

// Disallow draining of summoned monsters since they can't bleed.
// XXX: Is this too harsh?
// Disallow draining of summoned monsters.
if (defender->is_summoned())
break;

if (defender->res_negative_energy())
break;

if (defender->stat_hp() < defender->stat_maxhp())
{
if (attacker->heal(1 + random2(damage_done)) && needs_message)
int healed = resist_adjust_damage(defender, BEAM_NEG,
1 + random2(damage_done));
if (healed)
{
mprf("%s %s strength from %s injuries!",
atk_name(DESC_THE).c_str(),
attacker->conj_verb("draw").c_str(),
def_name(DESC_ITS).c_str());
attacker->heal(healed);
if (needs_message)
{
mprf("%s %s strength from %s injuries!",
atk_name(DESC_THE).c_str(),
attacker->conj_verb("draw").c_str(),
def_name(DESC_ITS).c_str());
}
}
}
break;
Expand Down
11 changes: 6 additions & 5 deletions crawl-ref/source/mon-cast.cc
Expand Up @@ -1060,8 +1060,7 @@ bolt mons_spell_beam(monster* mons, spell_type spell_cast, int power,
{
beam.hit = AUTOMATIC_HIT;
beam.glyph = 0;
if (spell_cast != SPELL_AGONY)
beam.name = "";
beam.name = "";
}

return beam;
Expand Down Expand Up @@ -3907,8 +3906,8 @@ static bool _mons_vampiric_drain(monster *mons)

hp_cost = min(hp_cost, target->stat_hp());
hp_cost = min(hp_cost, mons->max_hit_points - mons->hit_points);
if (target->res_negative_energy())
hp_cost = 0;

hp_cost = resist_adjust_damage(target, BEAM_NEG, hp_cost);

if (!hp_cost)
{
Expand Down Expand Up @@ -7482,11 +7481,13 @@ static bool _ms_waste_of_time(monster* mon, mon_spell_slot slot)
}
// fall through
case SPELL_BOLT_OF_DRAINING:
case SPELL_AGONY:
case SPELL_MALIGN_OFFERING:
case SPELL_GHOSTLY_FIREBALL:
return !foe || _foe_should_res_negative_energy(foe);

case SPELL_AGONY:
return !foe || !_torment_vulnerable(foe);

case SPELL_MIASMA_BREATH:
return !foe || foe->res_rotting();

Expand Down

0 comments on commit 3c1ee9c

Please sign in to comment.