summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/fight.cc
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-01 04:00:00 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2008-12-01 04:00:00 +0000
commit04f5058cac8e12d3b85834bda4589239932f371a (patch)
tree20863dfb1b0a87c9643f0ca98ce2cb43cf195416 /crawl-ref/source/fight.cc
parent6e3985b473d0b26c028820b3d0a3808d3f3dd7ee (diff)
downloadcrawl-ref-04f5058cac8e12d3b85834bda4589239932f371a.tar.gz
crawl-ref-04f5058cac8e12d3b85834bda4589239932f371a.zip
Add some chaos attacks/weapons. Missiles and launchers of chaos fire a bolt of
a random type (including enchantments, which isn't so good since they don't have a visible beam). Might want to add BEAM_CHAOS and make it a beam of that. Weapons of chaos either does a random brand effect (fire, poison, etc) or a random chaos effect (which includes cloning the attack victim). The AF_CHAOS monster attack flavour either does a random monster flavour or chaos effect (same chaos effects as for weapons). The relative frequency of all the different effects/brands/flavours no doubt needs adjustment. All of this is currently only available via Xom. 10% of all common-type demons sent in by Xom will be chaos spawn (the only kind that use AF_CHAOS, and never randomly generated otherwise). All item gifts from Xom which are generated with a brand will have their brand switched to chaos (this should probably be made to happen less than 100% of the time). And finally one of Xom's bad acts is to upgrade a non-branded weapon of a nearby hostile monster to a chaos brand (this might need to be made less (or more) common). Oh, and if a randart has a brand which would be identified if it were merely an ego weapon, it now identifies the RAP_BRAND property of the randart. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7704 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/fight.cc')
-rw-r--r--crawl-ref/source/fight.cc433
1 files changed, 415 insertions, 18 deletions
diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc
index 465afc5ed3..d7dedf3a99 100644
--- a/crawl-ref/source/fight.cc
+++ b/crawl-ref/source/fight.cc
@@ -1215,7 +1215,7 @@ bool melee_attack::player_apply_aux_unarmed()
}
if (def->hit_points < 1)
- monster_die(def, KILL_YOU, NON_MONSTER);
+ _monster_die(def, KILL_YOU, NON_MONSTER);
return (!def->alive());
}
@@ -1849,6 +1849,25 @@ bool melee_attack::player_monattk_hit_effects(bool mondied)
return (!def->alive());
}
+void melee_attack::_monster_die(monsters* monster, killer_type killer,
+ int killer_index)
+{
+ const bool chaos = damage_brand == SPWPN_CHAOS;
+
+ // Copy defender before it gets reset by monster_die()
+ monsters* def_copy = NULL;
+ if (chaos)
+ def_copy = new monsters(*monster);
+
+ monster_die(monster, killer, killer_index);
+
+ if (chaos)
+ {
+ chaos_killed_defender(def_copy);
+ delete def_copy;
+ }
+}
+
static bool is_boolean_resist(beam_type flavour)
{
switch (flavour)
@@ -2078,13 +2097,17 @@ bool melee_attack::distortion_affects_defender()
emit_nodmg_hit_message();
if (defender->atype() == ACT_PLAYER && attacker_visible
- && weapon != NULL && !is_artefact(*weapon))
+ && weapon != NULL && !is_unrandom_artefact(*weapon)
+ && !is_fixed_artefact(*weapon))
{
// If the player is being sent to the Abyss by being attacked
// with a distortion weapon, then we have to ID it before
// the player goes to Abyss, while the weapon object is
// still in memory.
- set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
+ if (is_random_artefact(*weapon))
+ randart_wpn_learn_prop(*weapon, RAP_BRAND);
+ else
+ set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
}
else if (defender_visible)
obvious_effect = true;
@@ -2097,8 +2120,331 @@ bool melee_attack::distortion_affects_defender()
return (false);
}
+static bool _can_clone(const actor *defender, coord_def *pos, int *midx)
+{
+ pos->set(-1, -1);
+ *midx = NON_MONSTER;
+
+ // Maybe create a player ghost?
+ if (defender->atype() == ACT_PLAYER)
+ return (false);
+
+ const monsters* mon = dynamic_cast<const monsters*>(defender);
+
+ // No uniques, pandemonium lords or player ghosts. Also, figuring
+ // out the name for the clone of a named monster isn't worth it.
+ if (mons_is_unique(mon->type) || mon->is_named() || mon->ghost.get())
+ return (false);
+
+ // Holy beings can't be duplicated by chaotic means.
+ if (mons_is_holy(mon))
+ return (false);
+
+ // Is there space for the clone?
+ int squares = 0;
+ for (int i = 0; i < 8; i++)
+ {
+ const coord_def p = mon->pos() + Compass[i];
+
+ if (in_bounds(p) && p != you.pos() && mgrd(p) == NON_MONSTER
+ && monster_habitable_grid(mon, grd(p)))
+ {
+ if (one_chance_in(++squares))
+ *pos = p;
+ }
+ }
+ if (squares == 0)
+ return (false);
+
+ // Is there an open slot in menv?
+ for (int i = 0; i < MAX_MONSTERS; i++)
+ if (menv[i].type == -1)
+ {
+ *midx = i;
+ break;
+ }
+
+ if (*midx == NON_MONSTER)
+ return (false);
+
+ // Is the monster carrying an artefact?
+ for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
+ {
+ const int index = mon->inv[i];
+
+ if (index == NON_ITEM)
+ continue;
+
+ if (is_artefact(mitm[index]))
+ return (false);
+ }
+
+ return (true);
+}
+
+static bool _do_clone(monsters* orig, coord_def pos, int midx)
+{
+ bool obvious = false;
+
+ monsters &mon(menv[midx]);
+
+ mon = *orig;
+
+ mon.position = pos;
+ mgrd(pos) = midx;
+
+ // Duplicate objects, or unequip them if they can't be duplicated.
+ for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
+ {
+ const int old_index = orig->inv[i];
+
+ if (old_index == NON_ITEM)
+ continue;
+
+ const int new_index = get_item_slot(0);
+ if (new_index == NON_ITEM)
+ {
+ mon.unequip(mitm[old_index], i, 0, true);
+ mon.inv[i] = NON_ITEM;
+ continue;
+ }
+
+ mon.inv[i] = new_index;
+ mitm[new_index] = mitm[old_index];
+ }
+
+ // The player shouldn't get new permanent followers from cloning.
+ if (mon.attitude == ATT_FRIENDLY && !mons_is_summoned(&mon))
+ mon.mark_summoned(6, true);
+
+ if (you.can_see(orig) && you.can_see(&mon))
+ {
+ simple_monster_message(orig, " is duplicated!");
+ obvious = true;
+ }
+
+ mark_interesting_monst(&mon, mon.behaviour);
+ if (you.can_see(&mon))
+ {
+ seen_monster(&mon);
+ viewwindow(true, false);
+ }
+
+ return (obvious);
+}
+
+enum chaos_type
+{
+ CHAOS_CLONE,
+ CHAOS_POLY,
+ CHAOS_POLY_UP,
+ CHAOS_MAKE_SHIFTER,
+ CHAOS_HEAL,
+ CHAOS_HASTE,
+ CHAOS_INVIS,
+ CHAOS_SLOW,
+ CHAOS_PARA,
+ CHAOS_PETRIFY,
+ NUM_CHAOS_TYPES
+};
+
+// XXX: We might want to vary the probabilites for the various effects
+// based on whether the source is weapon of chaos or a monster with
+// AF_CHAOS
+void melee_attack::chaos_affects_defender()
+{
+ coord_def clone_pos;
+ int clone_midx;
+ const bool mon = defender->atype() == ACT_MONSTER;
+ const bool immune = mon && mons_immune_magic(def);
+ const bool is_shifter = mon && mons_is_shapeshifter(def);
+ const bool is_chaotic = mon && mons_is_chaotic(def);
+ const bool can_clone = _can_clone(defender, &clone_pos, &clone_midx);
+ const bool can_poly = is_shifter || (defender->can_safely_mutate()
+ && !immune);
+
+ int clone_chance = can_clone ? 1 : 0;
+ int poly_chance = can_poly ? 1 : 0;
+ int poly_up_chance = can_poly ? 1 : 0;
+ int shifter_chance = can_poly ? 1 : 0;
+
+ if (is_chaotic)
+ {
+ // Polymorphing might reduce amount of chaos in the world.
+ poly_chance = 0;
+ poly_up_chance = 0;
+
+ // Chaos wants more chaos.
+ clone_chance *= 2;
+
+ // Chaos loves shifters.
+ if (is_shifter)
+ {
+ clone_chance *= 2;
+ poly_up_chance = 4;
+
+ // Already a shifter
+ shifter_chance = 0;
+ }
+ }
+
+ // NOTE: Must appear in exact same order as in chaos_type enumeration.
+ int probs[NUM_CHAOS_TYPES] =
+ {
+ clone_chance, // CHAOS_CLONE
+ poly_chance, // CHAOS_POLY
+ poly_up_chance, // CHAOS_POLY_UP
+ shifter_chance, // CHAOS_MAKE_SHIFTER
+
+ 5, // CHAOS_HEAL
+ 5, // CHAOS_HASTE
+ 5, // CHAOS_INVIS
+
+ 15, // CHAOS_SLOW
+ 15, // CHAOS_PARA
+ 15, // CHAOS_PETRIFY
+ };
+
+ bolt beam;
+ beam.flavour = BEAM_NONE;
+
+ int choice = choose_random_weighted(probs, probs + NUM_CHAOS_TYPES);
+ switch(static_cast<chaos_type>(choice))
+ {
+ case CHAOS_CLONE:
+ ASSERT(can_clone);
+ ASSERT(defender->atype() == ACT_MONSTER);
+ obvious_effect = _do_clone(def, clone_pos, clone_midx);
+ break;
+
+ case CHAOS_POLY:
+ ASSERT(can_poly);
+ beam.flavour = BEAM_POLYMORPH;
+ break;
+
+ case CHAOS_POLY_UP:
+ ASSERT(can_poly);
+ ASSERT(defender->atype() == ACT_MONSTER);
+
+ obvious_effect = you.can_see(defender);
+ monster_polymorph(def, RANDOM_MONSTER, PPT_MORE, true);
+ break;
+
+ case CHAOS_MAKE_SHIFTER:
+ ASSERT(can_poly);
+ ASSERT(!is_shifter);
+ ASSERT(defender->atype() == ACT_MONSTER);
+
+ obvious_effect = you.can_see(defender);
+ def->add_ench(one_chance_in(3) ?
+ ENCH_GLOWING_SHAPESHIFTER : ENCH_SHAPESHIFTER);
+ // Immediately polymorph monster, just to make the effect obvious.
+ monster_polymorph(def, RANDOM_MONSTER, PPT_SAME, true);
+ break;
+
+ case CHAOS_HEAL:
+ beam.flavour = BEAM_HEALING;
+ break;
+
+ case CHAOS_HASTE:
+ beam.flavour = BEAM_HASTE;
+ break;
+
+ case CHAOS_INVIS:
+ beam.flavour = BEAM_INVISIBILITY;
+ break;
+
+ case CHAOS_SLOW:
+ beam.flavour = BEAM_SLOW;
+ break;
+
+ case CHAOS_PARA:
+ beam.flavour = BEAM_PARALYSIS;
+ break;
+
+ case CHAOS_PETRIFY:
+ beam.flavour = BEAM_PETRIFY;
+ break;
+
+ default:
+ ASSERT(!"Invalid chaos effect type");
+ break;
+ }
+
+ if (beam.flavour != BEAM_NONE)
+ {
+ beam.name = atk_name(DESC_CAP_THE);
+ beam.range = 1;
+ beam.colour = BLACK;
+ beam.is_beam = false;
+ beam.is_explosion = false;
+ beam.is_big_cloud = false;
+ beam.effect_known = false;
+
+ beam.thrower = (attacker->atype() == ACT_PLAYER) ? KILL_YOU
+ : def->confused_by_you() ? KILL_YOU_CONF
+ : KILL_MON;
+ beam.beam_source =
+ (attacker->atype() == ACT_PLAYER) ? MHITYOU : monster_index(atk);
+
+ beam.source = attacker->pos();
+ beam.target = defender->pos();
+ beam.pos = defender->pos();
+
+ beam.damage = dice_def(damage_done + special_damage + aux_damage, 1);
+
+ beam.ench_power = beam.damage.num;
+
+ fire_beam(beam);
+
+ if (you.can_see(defender))
+ obvious_effect = beam.obvious_effect;
+ }
+
+ if (!you.can_see(attacker))
+ obvious_effect = false;
+}
+
+void melee_attack::chaos_affects_attacker()
+{
+}
+
+// NOTE: Isn't called if monster dies from poisoning caused by chaos.
+void melee_attack::chaos_killed_defender(monsters* def_copy)
+{
+}
+
+// NOTE: random_chaos_brand() and random_chaos_attack_flavour() should
+// return a set of effects that are roughly the same, to make it easy
+// for chaos_affects_defender() not to do duplicate effects caused
+// by the non-chaos brands/flavours they return.
+int melee_attack::random_chaos_brand()
+{
+ int brands[] = {SPWPN_FLAMING, SPWPN_FREEZING, SPWPN_ELECTROCUTION,
+ SPWPN_VENOM, SPWPN_DRAINING, SPWPN_VAMPIRICISM,
+ SPWPN_PAIN, SPWPN_DISTORTION, SPWPN_CONFUSE,
+ SPWPN_CHAOS};
+ return (RANDOM_ELEMENT(brands));
+}
+
+mon_attack_flavour melee_attack::random_chaos_attack_flavour()
+{
+ mon_attack_flavour flavours[] =
+ {AF_FIRE, AF_COLD, AF_ELEC, AF_POISON_NASTY, AF_VAMPIRIC, AF_DISTORT,
+ AF_CONFUSE, AF_CHAOS};
+ return (RANDOM_ELEMENT(flavours));
+}
+
bool melee_attack::apply_damage_brand()
{
+ bool brand_was_known = false;
+
+ if (weapon)
+ if (is_random_artefact(*weapon))
+ brand_was_known = randart_known_wpn_property(*weapon, RAP_BRAND);
+ else
+ brand_was_known = item_type_known(*weapon);
+
bool ret = false;
// Monster resistance to the brand.
@@ -2106,7 +2452,14 @@ bool melee_attack::apply_damage_brand()
special_damage = 0;
obvious_effect = false;
- switch (damage_brand)
+
+ int brand;
+ if (damage_brand == SPWPN_CHAOS)
+ brand = random_chaos_brand();
+ else
+ brand = damage_brand;
+
+ switch (brand)
{
case SPWPN_FLAMING:
res = fire_res_apply_cerebov_downgrade( defender->res_fire() );
@@ -2306,8 +2659,6 @@ bool melee_attack::apply_damage_brand()
{
emit_nodmg_hit_message();
- // FIXME Currently Confusing Touch is the *only* way to get
- // here. Generalise.
const int hdcheck =
(defender->holiness() == MH_NATURAL? random2(30) : random2(22));
@@ -2323,26 +2674,42 @@ bool melee_attack::apply_damage_brand()
(attacker->atype() == ACT_PLAYER) ? MHITYOU
: monster_index(atk);
mons_ench_f2( def, beam_temp );
+ obvious_effect = beam_temp.obvious_effect;
}
- if (attacker->atype() == ACT_PLAYER)
+ if (attacker->atype() == ACT_PLAYER && damage_brand == SPWPN_CONFUSE)
{
+ ASSERT(you.duration[DUR_CONFUSING_TOUCH]);
you.duration[DUR_CONFUSING_TOUCH] -= roll_dice(3, 5);
if (you.duration[DUR_CONFUSING_TOUCH] < 1)
you.duration[DUR_CONFUSING_TOUCH] = 1;
+ obvious_effect = false;
}
break;
}
+
+ case SPWPN_CHAOS:
+ chaos_affects_defender();
+ break;
}
+ if (attacker->atype() == ACT_PLAYER && damage_brand == SPWPN_CHAOS)
+ // If your god objects to using chaos then it makes the
+ // brand obvious.
+ if (did_god_conduct(DID_CHAOS, 2 + random2(3), brand_was_known))
+ obvious_effect = true;
+
if (!obvious_effect)
obvious_effect = !special_damage_message.empty();
if (obvious_effect && attacker_visible && weapon != NULL
- && !is_artefact(*weapon))
+ && !is_unrandom_artefact(*weapon) && !is_fixed_artefact(*weapon))
{
- set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
+ if (is_random_artefact(*weapon))
+ randart_wpn_learn_prop(*weapon, RAP_BRAND);
+ else
+ set_ident_flags(*weapon, ISFLAG_KNOW_TYPE);
}
return (ret);
@@ -2621,7 +2988,7 @@ bool melee_attack::player_check_monster_died()
player_monattk_hit_effects(true);
- monster_die(def, KILL_YOU, NON_MONSTER);
+ _monster_die(def, KILL_YOU, NON_MONSTER);
return (true);
}
@@ -3571,7 +3938,11 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk)
{
// Most of this is from BWR 4.1.2.
- switch (attk.flavour)
+ mon_attack_flavour flavour = attk.flavour;
+ if (flavour == AF_CHAOS)
+ flavour = random_chaos_attack_flavour();
+
+ switch (flavour)
{
default:
break;
@@ -3811,6 +4182,10 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk)
case AF_NAPALM:
mons_do_napalm();
break;
+
+ case AF_CHAOS:
+ chaos_affects_defender();
+ break;
}
}
@@ -3822,6 +4197,7 @@ void melee_attack::mons_perform_attack_rounds()
// Melee combat, tell attacker to wield its melee weapon.
atk->wield_melee_weapon();
+ monsters* def_copy = NULL;
for (attack_number = 0; attack_number < nrounds; ++attack_number)
{
// Monster went away?
@@ -3855,6 +4231,13 @@ void melee_attack::mons_perform_attack_rounds()
mons_set_weapon(attk);
to_hit = mons_to_hit();
+ const bool chaos_attack = attk.flavour == AF_CHAOS
+ || damage_brand == SPWPN_CHAOS;
+
+ // Make copy of monster before monster_die() resets it.
+ if (chaos_attack && defender->atype() == ACT_MONSTER && !def_copy)
+ def_copy = new monsters(*def);
+
final_attack_delay = mons_attk_delay();
if (damage_brand == SPWPN_SPEED)
final_attack_delay = final_attack_delay / 2 + 1;
@@ -3945,13 +4328,17 @@ void melee_attack::mons_perform_attack_rounds()
defender->hurt(attacker, damage_done + special_damage);
- // Yredelemnul's injury mirroring can kill the attacker.
- if (!attacker->alive() || !defender->alive()
- || attacker == defender)
+ if (!defender->alive())
{
- return;
+ if (chaos_attack && defender->atype() == ACT_MONSTER)
+ chaos_killed_defender(def_copy);
+ break;
}
+ // Yredelemnul's injury mirroring can kill the attacker.
+ if (!attacker->alive() || attacker == defender)
+ break;
+
special_damage = 0;
special_damage_message.clear();
apply_damage_brand();
@@ -3962,18 +4349,28 @@ void melee_attack::mons_perform_attack_rounds()
if (special_damage > 0)
defender->hurt(attacker, special_damage);
+ if (!defender->alive())
+ {
+ if (chaos_attack && defender->atype() == ACT_MONSTER)
+ chaos_killed_defender(def_copy);
+ break;
+ }
+
// Yredelemnul's injury mirroring can kill the attacker.
if (!attacker->alive())
- return;
+ break;
}
item_def *weap = atk->mslot_item(MSLOT_WEAPON);
- if (weap && weap->cursed() && is_range_weapon(*weap)
- && !(weap->flags & ISFLAG_KNOW_CURSE))
+ if (weap && you.can_see(atk) && weap->cursed()
+ && is_range_weapon(*weap))
{
set_ident_flags( *weap, ISFLAG_KNOW_CURSE );
}
}
+
+ if (def_copy)
+ delete def_copy;
}
bool melee_attack::mons_perform_attack()