summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-01-14 12:00:48 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-01-14 12:00:48 +0000
commit85e11d2d3c892c2ffcd53b0bf55c0e83ce13fb9d (patch)
treec3a0a555a4e2e046ac138f75f1c3bfd76c4357f9 /crawl-ref
parentb83cbbcaed12c8df8422b0fda6465189757ce1c9 (diff)
downloadcrawl-ref-85e11d2d3c892c2ffcd53b0bf55c0e83ce13fb9d.tar.gz
crawl-ref-85e11d2d3c892c2ffcd53b0bf55c0e83ce13fb9d.zip
Implented some ranged brands from FR #2006917 and #1891231: shadow and
penetration (not phasing) for launchers and shadow, penetration, dispersal, exploding, steel and silver for ammo. Never randomly generated. If a launcher of venom is used to launch flame or ice ammo then the resulting bolt will be poisoned, just like poisoned ammo launched from a launcer of flame or frost. Put missile beam setup code that's common to monsters and the player in setup_missile_beam(). Removed mons_thrown_object_destroyed(), thrown_object_destroyed() is now used for both monsters and the player. The bolt struct has several new callback fields that can be set to alter the beam's behaviour; currently only used by the brands implemented in this commit, but they should be general enough to be used by anything. The bolt struct has the new field "special_explosion" which can be used to cause an explosion with flavour and/or damage dice different than the rest of the beam. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8449 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/beam.cc332
-rw-r--r--crawl-ref/source/beam.h29
-rw-r--r--crawl-ref/source/item_use.cc653
-rw-r--r--crawl-ref/source/item_use.h5
-rw-r--r--crawl-ref/source/itemname.cc38
-rw-r--r--crawl-ref/source/itemprop.cc8
-rw-r--r--crawl-ref/source/itemprop.h22
-rw-r--r--crawl-ref/source/monstuff.cc10
-rw-r--r--crawl-ref/source/monstuff.h4
-rw-r--r--crawl-ref/source/mstuff2.cc100
-rw-r--r--crawl-ref/source/mstuff2.h2
-rw-r--r--crawl-ref/source/religion.cc9
12 files changed, 839 insertions, 373 deletions
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index 1ee3838a4b..ec592b478c 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -82,6 +82,11 @@ void tracer_info::reset()
dont_stop = false;
}
+const tracer_info& tracer_info::operator+=(const tracer_info &other)
+{
+ return (*this);
+}
+
bool bolt::is_blockable() const
{
// BEAM_ELECTRICITY is added here because chain lighting is not
@@ -1445,6 +1450,15 @@ void bolt::initialize_fire()
in_explosion_phase = false;
use_target_as_pos = false;
+ if (special_explosion != NULL)
+ {
+ ASSERT(!is_explosion);
+ ASSERT(special_explosion->is_explosion);
+ ASSERT(special_explosion->special_explosion == NULL);
+ special_explosion->in_explosion_phase = false;
+ special_explosion->use_target_as_pos = false;
+ }
+
if (chose_ray)
{
ASSERT(in_bounds(ray.pos()));
@@ -1854,27 +1868,70 @@ void bolt::affect_cell()
affect_ground();
}
+bool bolt::apply_hit_funcs(actor* victim, int dmg, int corpse)
+{
+ bool affected = false;
+ for (unsigned int i = 0; i < hit_funcs.size(); i++)
+ affected = (*hit_funcs[i])(*this, victim, dmg, corpse) || affected;
+ return (affected);
+}
+
+bool bolt::apply_dmg_funcs(actor* victim, int &dmg,
+ std::vector<std::string> &messages)
+{
+ for (unsigned int i = 0; i < damage_funcs.size(); i++)
+ {
+ std::string dmg_msg;
+
+ if ( (*damage_funcs[i])(*this, victim, dmg, dmg_msg) )
+ return (false);
+ if (!dmg_msg.empty())
+ messages.push_back(dmg_msg);
+ }
+ return (true);
+}
+
+static void _undo_tracer(bolt &orig, bolt &copy)
+{
+ // FIXME: we should have a better idea of what gets changed!
+ orig.target = copy.target;
+ orig.source = copy.source;
+ orig.aimed_at_spot = copy.aimed_at_spot;
+ orig.range_used = copy.range_used;
+ orig.auto_hit = copy.auto_hit;
+ orig.ray = copy.ray;
+ orig.colour = copy.colour;
+ orig.flavour = copy.flavour;
+ orig.real_flavour = copy.real_flavour;
+ orig.seen = copy.seen;
+}
+
// This saves some important things before calling fire().
void bolt::fire()
{
path_taken.clear();
+ if (special_explosion)
+ special_explosion->is_tracer = is_tracer;
+
if (is_tracer)
{
bolt boltcopy = *this;
+ if (special_explosion != NULL)
+ boltcopy.special_explosion = new bolt(*special_explosion);
+
do_fire();
- // FIXME: we should have a better idea of what gets changed!
- target = boltcopy.target;
- source = boltcopy.source;
- aimed_at_spot = boltcopy.aimed_at_spot;
- range_used = boltcopy.range_used;
- auto_hit = boltcopy.auto_hit;
- ray = boltcopy.ray;
- colour = boltcopy.colour;
- flavour = boltcopy.flavour;
- real_flavour = boltcopy.real_flavour;
- seen = boltcopy.seen;
+ if (special_explosion != NULL)
+ {
+ seen = seen || special_explosion->seen;
+ foe_info += special_explosion->foe_info;
+ friend_info += special_explosion->friend_info;
+ _undo_tracer(*special_explosion, *boltcopy.special_explosion);
+ delete boltcopy.special_explosion;
+ }
+
+ _undo_tracer(*this, boltcopy);
}
else
do_fire();
@@ -2761,6 +2818,13 @@ static int _potion_beam_flavour_to_colour(beam_type flavour)
void bolt::affect_endpoint()
{
+ if (special_explosion)
+ {
+ special_explosion->refine_for_explosion();
+ special_explosion->target = pos();
+ special_explosion->explode();
+ }
+
// Leave an object, if applicable.
if (drop_item && item)
drop_object();
@@ -2768,6 +2832,7 @@ void bolt::affect_endpoint()
if (is_explosion)
{
refine_for_explosion();
+ target = pos();
explode();
return;
}
@@ -2782,6 +2847,8 @@ void bolt::affect_endpoint()
|| name == "great blast of cold"
|| name == "ball of vapour")
{
+ target = pos();
+ refine_for_explosion();
explode();
}
@@ -2838,9 +2905,7 @@ void bolt::drop_object()
return;
}
- if (YOU_KILL(thrower) && !thrown_object_destroyed(item, pos(), false)
- || MON_KILL(thrower)
- && !mons_thrown_object_destroyed(item, pos(), false, beam_source))
+ if (!thrown_object_destroyed(item, pos(), false))
{
if (item->sub_type == MI_THROWING_NET)
{
@@ -3337,7 +3402,18 @@ void bolt::tracer_affect_player()
foe_info.power += you.experience_level;
}
}
- range_used += range_used_on_hit();
+
+ std::vector<std::string> messages;
+ int dummy = 0;
+
+ apply_dmg_funcs(&you, dummy, messages);
+
+ for (unsigned int i = 0; i < messages.size(); i++)
+ mpr(messages[i].c_str(), MSGCH_WARN);
+
+ range_used += range_used_on_hit(&you);
+
+ apply_hit_funcs(&you, 0);
}
bool bolt::misses_player()
@@ -3460,7 +3536,7 @@ void bolt::affect_player_enchantment()
if (flavour == BEAM_TELEPORT && you.level_type == LEVEL_ABYSS)
xom_is_stimulated(255);
- range_used += range_used_on_hit();
+ range_used += range_used_on_hit(&you);
return;
}
@@ -3693,7 +3769,9 @@ void bolt::affect_player_enchantment()
// Regardless of effect, we need to know if this is a stopper
// or not - it seems all of the above are.
- range_used += range_used_on_hit();
+ range_used += range_used_on_hit(&you);
+
+ apply_hit_funcs(&you, 0);
}
@@ -3741,6 +3819,9 @@ void bolt::affect_player()
int roll = hurted;
#endif
+ std::vector<std::string> messages;
+ apply_dmg_funcs(&you, hurted, messages);
+
int armour_damage_reduction = random2( 1 + player_AC() );
if (flavour == BEAM_ELECTRICITY)
armour_damage_reduction /= 2;
@@ -3805,21 +3886,13 @@ void bolt::affect_player()
// handling of missiles
if (item && item->base_type == OBJ_MISSILES)
{
+ // SPMSL_POISONED handled via callback _poison_hit_victim() in
+ // item_use.cc
if (item->sub_type == MI_THROWING_NET)
{
player_caught_in_net();
was_affected = true;
}
- else if (item->special == SPMSL_POISONED)
- {
- if (!player_res_poison()
- && (hurted || ench_power == AUTOMATIC_HIT
- && x_chance_in_y(90 - 3 * player_AC(), 100)))
- {
- poison_player(1 + random2(3));
- was_affected = true;
- }
- }
else if (item->special == SPMSL_CURARE)
{
if (x_chance_in_y(90 - 3 * player_AC(), 100))
@@ -3868,6 +3941,8 @@ void bolt::affect_player()
mprf(MSGCH_DIAGNOSTICS, "Damage: %d", hurted );
#endif
+ was_affected = apply_hit_funcs(&you, hurted) || was_affected;
+
if (hurted > 0 || old_hp < you.hp || was_affected)
{
if (mons_att_wont_attack(attitude))
@@ -3889,9 +3964,15 @@ void bolt::affect_player()
foe_info.hurt++;
}
+ if (hurted > 0)
+ {
+ for (unsigned int i = 0; i < messages.size(); i++)
+ mpr(messages[i].c_str(), MSGCH_WARN);
+ }
+
internal_ouch(hurted);
- range_used += range_used_on_hit();
+ range_used += range_used_on_hit(&you);
}
int bolt::beam_source_as_target() const
@@ -3901,27 +3982,6 @@ int bolt::beam_source_as_target() const
: MHITYOU);
}
-static int _name_to_skill_level(const std::string& name)
-{
- skill_type type = SK_THROWING;
-
- if (name.find("dart") != std::string::npos)
- type = SK_DARTS;
- else if (name.find("needle") != std::string::npos)
- type = SK_DARTS;
- else if (name.find("bolt") != std::string::npos)
- type = SK_CROSSBOWS;
- else if (name.find("arrow") != std::string::npos)
- type = SK_BOWS;
- else if (name.find("stone") != std::string::npos)
- type = SK_SLINGS;
-
- if (type == SK_DARTS || type == SK_SLINGS)
- return (you.skills[type] + you.skills[SK_THROWING]);
-
- return (2 * you.skills[type]);
-}
-
void bolt::update_hurt_or_helped(monsters *mon)
{
if (!mons_atts_aligned(attitude, mons_attitude(mon)))
@@ -3953,11 +4013,15 @@ void bolt::tracer_enchantment_affect_monster(monsters* mon)
{
handle_stop_attack_prompt(mon);
if (!beam_cancelled)
- range_used += range_used_on_hit();
+ {
+ range_used += range_used_on_hit(mon);
+ apply_hit_funcs(mon, 0);
+ }
}
// Return false if we should skip handling this monster.
-bool bolt::determine_damage(monsters* mon, int& preac, int& postac, int& final)
+bool bolt::determine_damage(monsters* mon, int& preac, int& postac, int& final,
+ std::vector<std::string>& messages)
{
// preac: damage before AC modifier
// postac: damage after AC modifier
@@ -3970,6 +4034,9 @@ bool bolt::determine_damage(monsters* mon, int& preac, int& postac, int& final)
else
preac = damage.roll();
+ if (!apply_dmg_funcs(mon, preac, messages))
+ return (false);
+
// Submerged monsters get some perks.
if (mon->submerged())
{
@@ -4035,8 +4102,9 @@ void bolt::handle_stop_attack_prompt(monsters* mon)
void bolt::tracer_nonenchantment_affect_monster(monsters* mon)
{
+ std::vector<std::string> messages;
int preac, post, final;
- if ( !determine_damage(mon, preac, post, final) )
+ if ( !determine_damage(mon, preac, post, final, messages) )
return;
// Maybe the user wants to cancel at this point.
@@ -4058,10 +4126,14 @@ void bolt::tracer_nonenchantment_affect_monster(monsters* mon)
foe_info.power += 2 * final * mons_power(mon->type) / preac;
else
friend_info.power += 2 * final * mons_power(mon->type) / preac;
+
+ for (unsigned int i = 0; i < messages.size(); i++)
+ mpr(messages[i].c_str(), MSGCH_MONSTER_DAMAGE);
}
// Either way, we could hit this monster, so update range used.
- range_used += range_used_on_hit();
+ range_used += range_used_on_hit(mon);
+ apply_hit_funcs(mon, final);
}
void bolt::tracer_affect_monster(monsters* mon)
@@ -4075,7 +4147,10 @@ void bolt::tracer_affect_monster(monsters* mon)
// Ignore self-detonating monsters.
if (mons_self_destructs(mon))
+ {
+ apply_hit_funcs(mon, 0);
return;
+ }
// Update friend or foe encountered.
if (!mons_atts_aligned(attitude, mons_attitude(mon)))
@@ -4166,7 +4241,8 @@ void bolt::enchantment_affect_monster(monsters* mon)
beogh_follower_convert(mon, true);
}
- range_used += range_used_on_hit();
+ range_used += range_used_on_hit(mon);
+ apply_hit_funcs(mon, 0);
}
void bolt::monster_post_hit(monsters* mon, int dmg)
@@ -4191,38 +4267,9 @@ void bolt::monster_post_hit(monsters* mon, int dmg)
// Handle missile effects.
if (item && item->base_type == OBJ_MISSILES)
{
- if (item->special == SPMSL_POISONED)
- {
- int num_levels = 0;
- // ench_power == AUTOMATIC_HIT if this is a poisoned needle.
- if (ench_power == AUTOMATIC_HIT
- && x_chance_in_y(90 - 3 * mon->ac, 100))
- {
- num_levels = 2;
- }
- else if (random2(dmg) - random2(mon->ac) > 0)
- num_levels = 1;
-
- int num_success = 0;
- if (YOU_KILL(thrower))
- {
- const int skill_level = _name_to_skill_level(name);
- if (x_chance_in_y(skill_level + 25, 50))
- num_success++;
- if (x_chance_in_y(skill_level, 50))
- num_success++;
- }
- else
- num_success = 1;
-
- if (num_success)
- {
- if (num_success == 2)
- num_levels++;
- poison_monster(mon, whose_kill(), num_levels);
- }
- }
- else if (item->special == SPMSL_CURARE)
+ // SPMSL_POISONED handled via callback _poison_hit_victim() in
+ // item_use.cc
+ if (item->special == SPMSL_CURARE)
{
if (ench_power == AUTOMATIC_HIT
&& curare_hits_monster(agent(), mon, whose_kill(), 2)
@@ -4308,6 +4355,7 @@ bool bolt::handle_statue_disintegration(monsters* mon)
obvious_effect = true;
update_hurt_or_helped(mon);
mon->hurt(agent(), INSTANT_DEATH);
+ apply_hit_funcs(mon, INSTANT_DEATH);
// Stop here.
finish_beam();
}
@@ -4318,17 +4366,26 @@ void bolt::affect_monster(monsters* mon)
{
// Don't hit dead monsters.
if (!mon->alive())
+ {
+ apply_hit_funcs(mon, 0);
return;
+ }
// First some special cases.
// Digging doesn't affect monsters (should it harm earth elementals?)
if (flavour == BEAM_DIGGING)
+ {
+ apply_hit_funcs(mon, 0);
return;
+ }
// Fire storm creates these, so we'll avoid affecting them
if (name == "great blast of fire" && mon->type == MONS_FIRE_VORTEX)
+ {
+ apply_hit_funcs(mon, 0);
return;
+ }
// Handle tracers separately.
if (is_tracer)
@@ -4341,6 +4398,7 @@ void bolt::affect_monster(monsters* mon)
if (flavour == BEAM_VISUAL)
{
behaviour_event( mon, ME_DISTURB, beam_source, source );
+ apply_hit_funcs(mon, 0);
return;
}
@@ -4362,8 +4420,9 @@ void bolt::affect_monster(monsters* mon)
// We need to know how much the monster _would_ be hurt by this,
// before we decide if it actually hits.
+ std::vector<std::string> messages;
int preac, postac, final;
- if ( !determine_damage(mon, preac, postac, final) )
+ if ( !determine_damage(mon, preac, postac, final, messages) )
return;
#if DEBUG_DIAGNOSTICS
@@ -4466,6 +4525,12 @@ void bolt::affect_monster(monsters* mon)
monster_caught_in_net(mon, *this);
}
+ if (final > 0)
+ {
+ for (unsigned int i = 0; i < messages.size(); i++)
+ mpr(messages[i].c_str(), MSGCH_MONSTER_DAMAGE);
+ }
+
// Apply flavoured specials.
mons_adjust_flavoured(mon, *this, postac, true);
@@ -4484,12 +4549,24 @@ void bolt::affect_monster(monsters* mon)
// Now hurt monster.
mon->hurt(agent(), final, flavour, false);
+ int corpse = -1;
+ monsters orig = *mon;
+
if (mon->alive())
monster_post_hit(mon, final);
else
- monster_die(mon, thrower, beam_source_as_target());
+ corpse = monster_die(mon, thrower, beam_source_as_target());
+
+ // Give the callbacks a dead-but-valid monster object.
+ if (mon->type == -1)
+ {
+ orig.hit_points = -1;
+ mon = &orig;
+ }
+
+ range_used += range_used_on_hit(mon);
- range_used += range_used_on_hit();
+ apply_hit_funcs(mon, final, corpse);
}
bool bolt::has_saving_throw() const
@@ -4893,36 +4970,43 @@ mon_resist_type bolt::apply_enchantment_to_monster(monsters* mon)
// Extra range used on hit.
-int bolt::range_used_on_hit() const
+int bolt::range_used_on_hit(const actor* victim) const
{
+ int used = 0;
+
// Non-beams can only affect one thing (player/monster).
if (!is_beam)
- return (BEAM_STOP);
-
- if (is_enchantment())
- return (flavour == BEAM_DIGGING ? 0 : BEAM_STOP);
-
+ used = BEAM_STOP;
+ else if (is_enchantment())
+ used = (flavour == BEAM_DIGGING ? 0 : BEAM_STOP);
// Hellfire stops for nobody!
- if (name == "hellfire")
- return (0);
-
+ else if (name == "hellfire")
+ used = 0;
// Generic explosion.
- if (is_explosion || is_big_cloud)
- return (BEAM_STOP);
-
+ else if (is_explosion || is_big_cloud)
+ used = BEAM_STOP;
// Plant spit.
- if (flavour == BEAM_ACID)
- return (BEAM_STOP);
-
+ else if (flavour == BEAM_ACID)
+ used = BEAM_STOP;
// Lava doesn't go far, but it goes through most stuff.
- if (flavour == BEAM_LAVA)
- return (1);
-
+ else if (flavour == BEAM_LAVA)
+ used = 1;
// Lightning goes through things.
- if (flavour == BEAM_ELECTRICITY)
- return (0);
+ else if (flavour == BEAM_ELECTRICITY)
+ used = 0;
+ else
+ used = 2;
+
+ if (in_explosion_phase)
+ return (used);
- return (2);
+ for (unsigned int i = 0; i < range_funcs.size(); i++)
+ {
+ if ( (*range_funcs[i])(*this, victim, used) )
+ break;
+ }
+
+ return (used);
}
// Takes a bolt and refines it for use in the explosion function.
@@ -4930,14 +5014,32 @@ int bolt::range_used_on_hit() const
// immolation) bypass this function.
void bolt::refine_for_explosion()
{
- ex_size = 1;
+ ASSERT(!special_explosion);
+
const char *seeMsg = NULL;
const char *hearMsg = NULL;
+ if (ex_size == 0)
+ ex_size = 1;
+
// Assume that the player can see/hear the explosion, or
// gets burned by it anyway. :)
msg_generated = true;
+ // tmp needed so that what c_str() points to doesn't go out of scope
+ // before the function ends.
+ std::string tmp;
+ if (item != NULL)
+ {
+ tmp = "The " + item->name(DESC_PLAIN, false, false, false)
+ + " explodes!";
+
+ seeMsg = tmp.c_str();
+ hearMsg = "You hear an explosion.";
+
+ type = dchar_glyph(DCHAR_FIRED_BURST);
+ }
+
if (name == "hellfire")
{
seeMsg = "The hellfire explodes!";
@@ -5096,6 +5198,7 @@ static sweep_type _radial_sweep(int r)
// Returns true if we saw something happening.
bool bolt::explode(bool show_more, bool hole_in_the_middle)
{
+ ASSERT(!special_explosion);
ASSERT(!in_explosion_phase);
ASSERT(ex_size > 0);
@@ -5372,9 +5475,10 @@ bolt::bolt() : range(-2), type('*'),
beam_source(MHITNOT), name(), short_name(), is_beam(false),
is_explosion(false), is_big_cloud(false), aimed_at_spot(false),
aux_source(), affects_nothing(false), affects_items(true),
- effect_known(true), draw_delay(15), obvious_effect(false),
- seen(false), path_taken(), range_used(0), is_tracer(false),
- aimed_at_feet(false), msg_generated(false),
+ effect_known(true), draw_delay(15), special_explosion(NULL),
+ range_funcs(), damage_funcs(), hit_funcs(),
+ obvious_effect(false), seen(false), path_taken(), range_used(0),
+ is_tracer(false), aimed_at_feet(false), msg_generated(false),
in_explosion_phase(false), smart_monster(false),
can_see_invis(false), attitude(ATT_HOSTILE), foe_ratio(0),
chose_ray(false), beam_cancelled(false), dont_stop_player(false),
diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h
index 924e1cd7c4..48e77b0dd4 100644
--- a/crawl-ref/source/beam.h
+++ b/crawl-ref/source/beam.h
@@ -46,8 +46,19 @@ struct tracer_info
tracer_info();
void reset();
+
+ const tracer_info &operator += (const tracer_info &other);
};
+struct bolt;
+
+typedef bool (*range_used_func)(const bolt& beam, const actor* victim,
+ int &used);
+typedef bool (*beam_damage_func)(bolt& beam, actor* victim, int &dmg,
+ std::string &dmg_msg);
+typedef bool (*beam_hit_func)(bolt& beam, actor* victim, int dmg,
+ int corpse);
+
struct bolt
{
// INPUT parameters set by caller
@@ -82,6 +93,15 @@ struct bolt
int draw_delay; // delay used when drawing beam.
+ bolt* special_explosion; // For exploding with a different
+ // flavour/damage/etc than the beam
+ // itself.
+
+ // Various callbacks.
+ std::vector<range_used_func> range_funcs;
+ std::vector<beam_damage_func> damage_funcs;
+ std::vector<beam_hit_func> hit_funcs;
+
// OUTPUT parameters (tracing, ID)
bool obvious_effect; // did an 'obvious' effect happen?
@@ -173,7 +193,7 @@ private:
bool found_player() const;
int beam_source_as_target() const;
- int range_used_on_hit() const;
+ int range_used_on_hit(const actor* victim) const;
std::string zapper() const;
@@ -182,6 +202,10 @@ private:
void step();
void hit_wall();
+ bool apply_hit_funcs(actor* victim, int dmg, int corpse = -1);
+ bool apply_dmg_funcs(actor* victim, int &dmg,
+ std::vector<std::string> &messages);
+
// Functions which handle actually affecting things. They all
// operate on the beam's current position (i.e., whatever pos()
// returns.)
@@ -209,7 +233,8 @@ public:
void update_hurt_or_helped(monsters *mon);
bool attempt_block(monsters* mon);
void handle_stop_attack_prompt(monsters* mon);
- bool determine_damage(monsters* mon, int& preac, int& postac, int& final);
+ bool determine_damage(monsters* mon, int& preac, int& postac, int& final,
+ std::vector<std::string> &messages);
void monster_post_hit(monsters* mon, int dmg);
bool misses_player();
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 94a0c150b9..ee73745107 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -62,6 +62,7 @@ REVISION("$Rev$");
#include "stuff.h"
#include "tiles.h"
#include "transfor.h"
+#include "traps.h"
#include "tutorial.h"
#include "view.h"
#include "xom.h"
@@ -1669,6 +1670,461 @@ bool elemental_missile_beam(int launcher_brand, int ammo_brand)
return (element != 0);
}
+static int _item_to_skill_level(const item_def *item)
+{
+ skill_type type = range_skill(*item);
+
+ if (type == SK_DARTS || type == SK_SLINGS)
+ return (you.skills[type] + you.skills[SK_THROWING]);
+
+ return (2 * you.skills[type]);
+}
+
+static bool _poison_hit_victim(bolt& beam, actor* victim, int dmg, int corpse)
+{
+ if (!victim->alive() || victim->res_poison())
+ return (false);
+
+ if (beam.is_tracer)
+ return (true);
+
+ int levels = 0;
+
+ actor* agent = beam.agent();
+
+ if (agent->atype() == ACT_MONSTER)
+ {
+ if (dmg > 0 || beam.ench_power == AUTOMATIC_HIT
+ && x_chance_in_y(90 - 3 * victim->armour_class(), 100))
+ {
+ levels = 1 + random2(3);
+ }
+ }
+ else
+ {
+ if (beam.ench_power == AUTOMATIC_HIT
+ && x_chance_in_y(90 - 3 * victim->armour_class(), 100))
+ {
+ levels = 2;
+ }
+ else if (random2(dmg) > random2(victim->armour_class()))
+ levels = 1;
+
+ int num_success = 0;
+ if (YOU_KILL(beam.thrower))
+ {
+ const int skill_level = _item_to_skill_level(beam.item);
+ if (x_chance_in_y(skill_level + 25, 50))
+ num_success++;
+ if (x_chance_in_y(skill_level, 50))
+ num_success++;
+ }
+ else
+ num_success = 1;
+
+ if (num_success == 0)
+ return (false);
+ else
+ {
+ if (num_success == 2)
+ levels++;
+ }
+ }
+
+ if (levels <= 0)
+ return (false);
+
+ victim->poison(agent, levels);
+
+ return (true);
+}
+
+static bool _item_penetrates_victim(const bolt &beam, const actor *victim,
+ int &used)
+{
+ if (beam.aimed_at_feet)
+ return (false);
+
+ used = 0;
+
+ if (!beam.is_tracer && you.can_see(victim))
+ mprf("The %s passes through %s!", beam.name.c_str(),
+ victim->name(DESC_NOCAP_THE).c_str());
+
+ return (true);
+}
+
+static bool _silver_damages_victim(bolt &beam, actor* victim, int &dmg,
+ std::string &dmg_msg)
+{
+ bool shifter;
+
+ if (victim->atype() == ACT_MONSTER)
+ {
+ monsters* mon = dynamic_cast<monsters*>(victim);
+ shifter = mons_is_shapeshifter(mon);
+ }
+ else
+ shifter = transform_changed_physiology();
+
+ mon_holy_type holiness = victim->holiness();
+
+ if (shifter || holiness == MH_UNDEAD || holiness == MH_DEMONIC)
+ {
+ dmg *= 2;
+
+ if (!beam.is_tracer && you.can_see(victim))
+ dmg_msg = "The silver sears " + victim->name(DESC_NOCAP_THE) + "!";
+ }
+
+ return (false);
+}
+
+static bool _shadow_hit_victim(bolt& beam, actor* victim, int dmg, int corpse)
+{
+ if (beam.is_tracer || victim->alive() || corpse == -1
+ || corpse == NON_ITEM)
+ {
+ return (false);
+ }
+
+ actor* agent = beam.agent();
+ beh_type beh;
+ unsigned short hitting;
+
+ if (agent->atype() == ACT_PLAYER)
+ {
+ hitting = you.pet_target;
+ beh = BEH_FRIENDLY;
+ }
+ else
+ {
+ monsters *mon = dynamic_cast<monsters*>(agent);
+
+ beh = SAME_ATTITUDE(mon);
+
+ // Get a new foe for the zombie to target.
+ behaviour_event(mon, ME_EVAL);
+ hitting = mon->foe;
+ }
+
+ int midx = NON_MONSTER;
+ if (!animate_remains(victim->pos(), CORPSE_BODY, beh, hitting,
+ GOD_NO_GOD, true, true, &midx))
+ {
+ return (false);
+ }
+
+ monsters *zomb = &menv[midx];
+
+ if (you.can_see(victim))
+ mprf("%s turns into a zombie!", victim->name(DESC_CAP_THE).c_str());
+ else if (you.can_see(zomb))
+ mprf("%s appears out of thin air!", zomb->name(DESC_CAP_THE).c_str());
+
+ return (true);
+}
+
+static bool _dispersal_hit_victim(bolt& beam, actor* victim, int dmg,
+ int corpse)
+{
+ const actor* agent = beam.agent();
+
+ if (!victim->alive() || victim == agent)
+ return (false);
+
+ if (beam.is_tracer)
+ return (true);
+
+ const bool was_seen = you.can_see(victim);
+ const bool no_sanct = victim->kill_alignment() == KC_OTHER;
+
+ coord_def pos, pos2;
+
+ int tries = 0;
+ do
+ {
+ if (!random_near_space(victim->pos(), pos, false, true, no_sanct))
+ return (false);
+ } while (!victim->is_habitable(pos) && tries++ < 100);
+
+ if (!victim->is_habitable(pos))
+ return (false);
+
+ tries = 0;
+ do
+ {
+ random_near_space(victim->pos(), pos2, false, true, no_sanct);
+ } while (!victim->is_habitable(pos2) && tries++ < 100);
+
+ if (!victim->is_habitable(pos2))
+ return (false);
+
+ // Pick the square further away from the agent.
+ const coord_def from = agent->pos();
+ if (in_bounds(pos2)
+ && grid_distance(pos2, from) > grid_distance(pos, from))
+ {
+ pos = pos2;
+ }
+
+ if (pos == victim->pos())
+ return (false);
+
+ const coord_def oldpos = victim->pos();
+
+ if (victim->atype() == ACT_PLAYER)
+ {
+ clear_trapping_net();
+ victim->moveto(pos);
+ mpr("You blink!");
+ }
+ else
+ {
+ monsters *mon = dynamic_cast<monsters*>(victim);
+
+ mons_clear_trapping_net(mon);
+ mon->check_redraw(oldpos);
+ mon->move_to_pos(pos);
+ mon->apply_location_effects(pos);
+ mon->check_redraw(pos);
+
+ const bool seen = you.can_see(mon);
+ const std::string name = mon->name(DESC_CAP_THE);
+ if (was_seen && seen)
+ mprf("%s blinks!", name.c_str());
+ else if (was_seen && !seen)
+ mprf("%s vanishes!", name.c_str());
+ else if (!was_seen && seen)
+ mprf("%s appears from out of thin air!", name.c_str());
+ }
+
+ return (true);
+}
+
+void setup_missile_beam(const actor *agent, bolt &beam, item_def &item,
+ std::string &ammo_name, bool &returning)
+{
+ dungeon_char_type zapsym = DCHAR_SPACE;
+ switch (item.base_type)
+ {
+ case OBJ_WEAPONS: zapsym = DCHAR_FIRED_WEAPON; break;
+ case OBJ_MISSILES: zapsym = DCHAR_FIRED_MISSILE; break;
+ case OBJ_ARMOUR: zapsym = DCHAR_FIRED_ARMOUR; break;
+ case OBJ_WANDS: zapsym = DCHAR_FIRED_STICK; break;
+ case OBJ_FOOD: zapsym = DCHAR_FIRED_CHUNK; break;
+ case OBJ_UNKNOWN_I: zapsym = DCHAR_FIRED_BURST; break;
+ case OBJ_SCROLLS: zapsym = DCHAR_FIRED_SCROLL; break;
+ case OBJ_JEWELLERY: zapsym = DCHAR_FIRED_TRINKET; break;
+ case OBJ_POTIONS: zapsym = DCHAR_FIRED_FLASK; break;
+ case OBJ_UNKNOWN_II: zapsym = DCHAR_FIRED_ZAP; break;
+ case OBJ_BOOKS: zapsym = DCHAR_FIRED_BOOK; break;
+ case OBJ_STAVES: zapsym = DCHAR_FIRED_STICK; break;
+ default: break;
+ }
+
+ beam.type = dchar_glyph(zapsym);
+
+ returning = get_weapon_brand(item) == SPWPN_RETURNING
+ || get_ammo_brand(item) == SPMSL_RETURNING;
+
+ if (agent->atype() == ACT_PLAYER)
+ {
+ beam.attitude = ATT_FRIENDLY;
+ beam.beam_source = NON_MONSTER;
+ beam.smart_monster = true;
+ beam.thrower = KILL_YOU_MISSILE;
+ }
+ else
+ {
+ const monsters *mon = dynamic_cast<const monsters*>(agent);
+
+ beam.attitude = mons_attitude(mon);
+ beam.beam_source = mon->mindex();
+ beam.smart_monster = (mons_intel(mon) >= I_NORMAL);
+ beam.thrower = KILL_MON_MISSILE;
+ }
+
+ beam.item = &item;
+ beam.source = agent->pos();
+ beam.colour = item.colour;
+ beam.flavour = BEAM_MISSILE;
+ beam.is_beam = false;
+ beam.aux_source.clear();
+
+ beam.can_see_invis = agent->can_see_invisible();
+
+ item_def *launcher = const_cast<actor*>(agent)->weapon(0);
+ if (launcher && !item.launched_by(*launcher))
+ launcher = NULL;
+
+ int bow_brand = SPWPN_NORMAL;
+ if (launcher != NULL)
+ bow_brand = get_weapon_brand(*launcher);
+
+ int ammo_brand = get_ammo_brand(item);
+ bool poisoned = ammo_brand == SPMSL_POISONED;
+
+ if (bow_brand == SPWPN_VENOM && ammo_brand != SPMSL_CURARE)
+ {
+ if (ammo_brand == SPMSL_NORMAL)
+ item.special = SPMSL_POISONED;
+
+ poisoned = true;
+ }
+
+ const bool exploding = ammo_brand == SPMSL_EXPLODING;
+ const bool penetrating = !exploding
+ && (bow_brand == SPWPN_PENETRATION
+ || ammo_brand == SPMSL_PENETRATION);
+ const bool silver = ammo_brand == SPMSL_SILVER;
+ const bool disperses = ammo_brand == SPMSL_DISPERSAL;
+ const bool shadow = bow_brand == SPWPN_SHADOW
+ || ammo_brand == SPMSL_SHADOW;
+
+ ASSERT(!exploding || !is_artefact(item));
+
+ beam.name = item.name(DESC_PLAIN, false, false, false);
+
+ // Print type of item as influenced by launcher.
+ item_def ammo = item;
+
+ // The chief advantage here is the extra damage this does
+ // against susceptible creatures.
+
+ // Note: weapons & ammo of eg fire are not cumulative
+ // ammo of fire and weapons of frost don't work together,
+ // and vice versa.
+
+ // Note that bow_brand is known since the bow is equipped.
+
+ // Chaos overides flame and frost/ice.
+ if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
+ {
+ // Chaos can't be poisoned, since that might conflict with
+ // the random healing effect or overlap with the random
+ // poisoning effect.
+ poisoned = false;
+ if (item.special == SPWPN_VENOM || item.special == SPMSL_CURARE)
+ item.special = SPMSL_NORMAL;
+
+ beam.effect_known = false;
+
+ beam.flavour = BEAM_CHAOS;
+ beam.name = "chaos";
+ beam.colour = EC_RANDOM;
+
+ ammo.special = SPMSL_CHAOS;
+ }
+ else if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME)
+ && ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST)
+ {
+ beam.flavour = BEAM_FIRE;
+ beam.name = "flame";
+ beam.colour = RED;
+
+ ammo.special = SPMSL_FLAME;
+ }
+ else if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE)
+ && ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME)
+ {
+ beam.flavour = BEAM_COLD;
+ beam.name = "frost";
+ beam.colour = WHITE;
+
+ ammo.special = SPMSL_ICE;
+ }
+
+ ASSERT(beam.flavour == BEAM_MISSILE || !is_artefact(item));
+
+ ammo_name = ammo.name(DESC_PLAIN);
+
+ if (silver)
+ beam.damage_funcs.push_back(_silver_damages_victim);
+ if (poisoned)
+ beam.hit_funcs.push_back(_poison_hit_victim);
+ if (penetrating)
+ beam.range_funcs.push_back(_item_penetrates_victim);
+ if (shadow)
+ beam.hit_funcs.push_back(_shadow_hit_victim);
+ if (disperses)
+ beam.hit_funcs.push_back(_dispersal_hit_victim);
+
+ if (shadow && ammo.special != SPMSL_SHADOW)
+ {
+ beam.name = "shadowy " + beam.name;
+ ammo_name = "shadowy " + ammo_name;
+ }
+
+ if (disperses && ammo.special != SPMSL_DISPERSAL)
+ {
+ beam.name = "dispersing " + beam.name;
+ ammo_name = "dispersing " + ammo_name;
+ }
+
+ if (poisoned && ammo.special != SPMSL_POISONED)
+ {
+ beam.name = "poison " + beam.name;
+ ammo_name = "poisoned " + ammo_name;
+ }
+
+ if (penetrating && ammo.special != SPMSL_PENETRATION)
+ {
+ beam.name = "penetrating " + beam.name;
+ ammo_name = "penetrating " + ammo_name;
+ }
+
+ if (silver && ammo.special != SPMSL_SILVER)
+ {
+ beam.name = "silvery " + beam.name;
+ ammo_name = "silvery " + ammo_name;
+ }
+
+ // Do this here so that we get all the name mods except for a
+ // redundant "exploding".
+ if (exploding)
+ {
+ bolt *expl = new bolt(beam);
+
+ expl->is_explosion = true;
+ expl->damage = dice_def(2, 5);
+ expl->ex_size = 1;
+
+ if (beam.flavour == BEAM_MISSILE)
+ {
+ expl->flavour = BEAM_FRAG;
+ expl->name += " fragments";
+
+ const std::string short_name =
+ ammo.name(DESC_PLAIN, false, false, false, false,
+ ISFLAG_IDENT_MASK | ISFLAG_COSMETIC_MASK
+ | ISFLAG_RACIAL_MASK);
+
+ expl->name = replace_all(expl->name, ammo.name(DESC_PLAIN),
+ short_name);
+ }
+
+ beam.special_explosion = expl;
+ }
+
+ if (exploding && ammo.special != SPMSL_EXPLODING)
+ {
+ beam.name = "exploding " + beam.name;
+ ammo_name = "exploding " + ammo_name;
+ }
+
+ if (beam.flavour != BEAM_MISSILE)
+ {
+ returning = false;
+
+ beam.type = dchar_glyph(DCHAR_FIRED_BOLT);
+ beam.name = "bolt of " + beam.name;
+ }
+
+ if (!is_artefact(item))
+ ammo_name = article_a(ammo_name, true);
+}
+
// XXX This is a bit too generous, as it lets the player determine
// that the bolt of fire he just shot from a flaming bow is actually
// a poison arrow. Hopefully this isn't too abusable.
@@ -1682,6 +2138,10 @@ static bool determines_ammo_brand(int bow_brand, int ammo_brand)
return (false);
if (bow_brand == SPWPN_CHAOS && ammo_brand == SPMSL_CHAOS)
return (false);
+ if (bow_brand == SPWPN_PENETRATION && ammo_brand == SPMSL_PENETRATION)
+ return (false);
+ if (bow_brand == SPWPN_SHADOW && ammo_brand == SPMSL_SHADOW)
+ return (false);
return (true);
}
@@ -1798,9 +2258,18 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
pbolt.set_target(thr);
item_def& thrown = you.inv[throw_2];
+ ASSERT(is_valid_item(thrown));
+
+ // Making a copy of the item: changed only for venom launchers.
+ item_def item = thrown;
+ item.quantity = 1;
+ item.slot = index_to_letter(item.link);
+
+ std::string ammo_name;
+ setup_missile_beam(&you, pbolt, item, ammo_name, returning);
// Did we know the ammo's brand before throwing it?
- const bool ammon_brand_known = item_type_known(thrown);
+ const bool ammo_brand_known = item_type_known(thrown);
// Get the ammo/weapon type. Convenience.
const object_class_type wepClass = thrown.base_type;
@@ -1809,18 +2278,6 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
// Figure out if we're thrown or launched.
const launch_retval projected = is_launched(&you, you.weapon(), thrown);
- // Hack the quantity to 1 while getting the name.
- const int old_quantity = thrown.quantity;
- thrown.quantity = 1;
- pbolt.name = thrown.name(DESC_PLAIN, false, false, false);
- thrown.quantity = old_quantity;
-
- pbolt.thrower = KILL_YOU_MISSILE;
- pbolt.source = you.pos();
- pbolt.colour = thrown.colour;
- pbolt.flavour = BEAM_MISSILE;
- pbolt.aux_source.clear();
-
// Determine range.
int max_range = 0;
int range = 0;
@@ -1855,15 +2312,8 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
range = std::min(range, LOS_RADIUS);
max_range = std::min(max_range, LOS_RADIUS);
- pbolt.is_beam = false;
- pbolt.beam_source = 0;
- pbolt.can_see_invis = player_see_invis();
- pbolt.smart_monster = true;
- pbolt.attitude = ATT_FRIENDLY;
- pbolt.is_tracer = true;
-
// For the tracer, use max_range. For the actual shot, use range.
- pbolt.range = max_range;
+ pbolt.range = max_range;
// Don't do the tracing when using Portaled Projectile, or when confused.
if (!teleport && !you.confused())
@@ -1872,6 +2322,7 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
pbolt.foe_info.reset();
pbolt.friend_info.reset();
pbolt.foe_ratio = 100;
+ pbolt.is_tracer = true;
pbolt.fire();
@@ -1881,9 +2332,12 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
{
canned_msg(MSG_OK);
you.turn_is_over = false;
+ if (pbolt.special_explosion != NULL)
+ delete pbolt.special_explosion;
return (false);
}
}
+ pbolt.is_tracer = false;
// Use real range for firing.
pbolt.range = range;
@@ -1896,16 +2350,8 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
canned_msg(MSG_EMPTY_HANDED);
}
- // Making a copy of the item: changed only for venom launchers.
- item_def item = thrown;
- item.quantity = 1;
- item.slot = index_to_letter(item.link);
-
- pbolt.item = &item;
-
// Now start real firing!
origin_set_unknown(item);
- std::string ammo_name;
if (is_blood_potion(item) && thrown.quantity > 1)
{
@@ -1927,26 +2373,6 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
if (!teleport)
pbolt.set_target(thr);
- dungeon_char_type zapsym = DCHAR_SPACE;
- switch (item.base_type)
- {
- case OBJ_WEAPONS: zapsym = DCHAR_FIRED_WEAPON; break;
- case OBJ_MISSILES: zapsym = DCHAR_FIRED_MISSILE; break;
- case OBJ_ARMOUR: zapsym = DCHAR_FIRED_ARMOUR; break;
- case OBJ_WANDS: zapsym = DCHAR_FIRED_STICK; break;
- case OBJ_FOOD: zapsym = DCHAR_FIRED_CHUNK; break;
- case OBJ_UNKNOWN_I: zapsym = DCHAR_FIRED_BURST; break;
- case OBJ_SCROLLS: zapsym = DCHAR_FIRED_SCROLL; break;
- case OBJ_JEWELLERY: zapsym = DCHAR_FIRED_TRINKET; break;
- case OBJ_POTIONS: zapsym = DCHAR_FIRED_FLASK; break;
- case OBJ_UNKNOWN_II: zapsym = DCHAR_FIRED_ZAP; break;
- case OBJ_BOOKS: zapsym = DCHAR_FIRED_BOOK; break;
- case OBJ_STAVES: zapsym = DCHAR_FIRED_STICK; break;
- default: break;
- }
-
- pbolt.type = dchar_glyph(zapsym);
-
// Get the launcher class, type. Convenience.
if (!you.weapon())
lnchType = NUM_WEAPONS;
@@ -1972,7 +2398,6 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
bow_brand = get_weapon_brand(*you.weapon());
const int ammo_brand = get_ammo_brand( item );
- bool poisoned = (ammo_brand == SPMSL_POISONED);
// CALCULATIONS FOR LAUNCHED WEAPONS
if (projected == LRET_LAUNCHED)
@@ -2196,14 +2621,8 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
dice_mult = dice_mult * 130 / 100;
}
- // Special cases for flame, frost, poison, etc.
- // check for venom brand.
- if (bow_brand == SPWPN_VENOM && ammo_brand == SPMSL_NORMAL)
- {
- // Poison brand the ammo.
- set_item_ego_type( item, OBJ_MISSILES, SPMSL_POISONED );
- pbolt.name = item.name(DESC_PLAIN);
- }
+ if (ammo_brand == SPMSL_STEEL)
+ dice_mult = dice_mult * 150 / 100;
// ID check. Can't ID off teleported projectiles, uh, because
// it's too weird. Also it messes up the messages.
@@ -2236,8 +2655,7 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
// CALCULATIONS FOR THROWN WEAPONS
if (projected == LRET_THROWN)
{
- returning = (!teleport && (get_weapon_brand(item) == SPWPN_RETURNING
- || get_ammo_brand(item) == SPMSL_RETURNING));
+ returning = returning && !teleport;
if (returning && !one_chance_in(1 + skill_bump(SK_THROWING)))
did_return = true;
@@ -2399,73 +2817,10 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
}
}
- // The chief advantage here is the extra damage this does
- // against susceptible creatures.
-
- // Note: weapons & ammo of eg fire are not cumulative
- // ammo of fire and weapons of frost don't work together,
- // and vice versa.
-
- // Note that bow_brand is known since the bow is equipped.
-
- // Chaos overides flame and frost/ice.
- if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
- {
- // Chaos can't be poisoned, since that might conflict with
- // the random healing effect or overlap with the random
- // poisoning effect.
- poisoned = false;
-
- // [dshaligram] Branded arrows are much stronger.
- dice_mult = (dice_mult * 150) / 100;
-
- pbolt.effect_known = false;
-
- pbolt.flavour = BEAM_CHAOS;
- pbolt.name = "bolt of chaos";
-
- pbolt.colour = EC_RANDOM;
- pbolt.type = dchar_glyph(DCHAR_FIRED_BOLT);
- pbolt.thrower = KILL_YOU_MISSILE;
- pbolt.aux_source.clear();
- }
- else if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME)
- && ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST)
- {
- // [dshaligram] Branded arrows are much stronger.
- dice_mult = (dice_mult * 150) / 100;
-
- pbolt.flavour = BEAM_FIRE;
- pbolt.name = "bolt of ";
-
- if (poisoned)
- pbolt.name += "poison ";
-
- pbolt.name += "flame";
- pbolt.colour = RED;
- pbolt.type = dchar_glyph(DCHAR_FIRED_BOLT);
- pbolt.thrower = KILL_YOU_MISSILE;
- pbolt.aux_source.clear();
- }
- else if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE)
- && ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME)
- {
+ if (pbolt.flavour != BEAM_MISSILE)
// [dshaligram] Branded arrows are much stronger.
dice_mult = (dice_mult * 150) / 100;
- pbolt.flavour = BEAM_COLD;
- pbolt.name = "bolt of ";
-
- if (poisoned)
- pbolt.name += "poison ";
-
- pbolt.name += "frost";
- pbolt.colour = WHITE;
- pbolt.type = dchar_glyph(DCHAR_FIRED_BOLT);
- pbolt.thrower = KILL_YOU_MISSILE;
- pbolt.aux_source.clear();
- }
-
// Dexterity bonus, and possible skill increase for silly throwing.
if (projected)
{
@@ -2540,34 +2895,6 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
pbolt.hit, pbolt.damage.num, pbolt.damage.size );
#endif
- // Print type of item as influenced by launcher.
- item_def ammo = item;
- if (ammo.base_type == OBJ_MISSILES)
- {
- if (pbolt.flavour == BEAM_CHAOS)
- {
- ammo.special = SPMSL_CHAOS;
- }
- else if (pbolt.flavour == BEAM_FIRE)
- {
- if (ammo.special != SPMSL_FLAME)
- ammo.special = SPMSL_FLAME;
- }
- else if (pbolt.flavour == BEAM_COLD)
- {
- if (ammo.special != SPMSL_ICE)
- ammo.special = SPMSL_ICE;
- }
- else
- {
- if (ammo.special != SPMSL_NORMAL && ammo.special != SPMSL_POISONED)
- ammo.special = SPMSL_NORMAL;
- }
- }
-
- if (ammo_name.empty())
- ammo_name = ammo.name(DESC_NOCAP_A);
-
// Create message.
mprf( "%s %s%s %s.",
teleport ? "Magically, you" : "You",
@@ -2610,7 +2937,13 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
{
did_god_conduct(DID_CHAOS, 2 + random2(3),
- bow_brand == SPWPN_CHAOS || ammon_brand_known);
+ bow_brand == SPWPN_CHAOS || ammo_brand_known);
+ }
+
+ if (ammo_brand == SPMSL_SHADOW || bow_brand == SPWPN_SHADOW)
+ {
+ did_god_conduct(DID_NECROMANCY, 2,
+ bow_brand == SPWPN_SHADOW || ammo_brand_known);
}
if (did_return)
@@ -2655,6 +2988,9 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
you.turn_is_over = true;
+ if (pbolt.special_explosion != NULL)
+ delete pbolt.special_explosion;
+
return (hit);
}
@@ -2665,14 +3001,21 @@ bool thrown_object_destroyed(item_def *item, const coord_def& where,
int chance = 0;
+ std::string name = item->name(DESC_PLAIN, false, true, false);
+
+ // Exploding missiles are always destroyed.
+ if (name.find("explod") != std::string::npos)
+ return (true);
+
if (item->base_type == OBJ_MISSILES)
{
+ int brand = get_ammo_brand(*item);
// [dshaligram] Removed influence of Throwing on ammo preservation.
// The effect is nigh impossible to perceive.
switch (item->sub_type)
{
case MI_NEEDLE:
- chance = (get_ammo_brand(*item) == SPMSL_CURARE ? 3 : 6);
+ chance = (brand == SPMSL_CURARE ? 3 : 6);
break;
case MI_SLING_BULLET:
case MI_STONE: chance = 4; break;
@@ -2687,6 +3030,8 @@ bool thrown_object_destroyed(item_def *item, const coord_def& where,
chance = 25;
break;
}
+ if (brand == SPMSL_STEEL)
+ chance *= 10;
}
// Enchanted projectiles get an extra shot at avoiding
diff --git a/crawl-ref/source/item_use.h b/crawl-ref/source/item_use.h
index 31009b522e..545712a4ef 100644
--- a/crawl-ref/source/item_use.h
+++ b/crawl-ref/source/item_use.h
@@ -15,6 +15,8 @@
#include "externs.h"
#include "enum.h"
+struct bolt;
+
enum enchant_stat_type
{
ENCHANT_TO_HIT,
@@ -160,6 +162,9 @@ bool puton_item(int slot, bool prompt_finger = true);
bool enchant_weapon(enchant_stat_type which_stat, bool quiet, item_def &wpn);
bool enchant_armour(int &ac_change, bool quiet, item_def &arm);
+void setup_missile_beam(const actor *actor, bolt &beam, item_def &item,
+ std::string &ammo_name, bool &returning);
+
bool throw_it(bolt &pbolt, int throw_2, bool teleport = false,
int acc_bonus = 0, dist *target = NULL);
diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc
index 38c5eb159d..a6b918dc37 100644
--- a/crawl-ref/source/itemname.cc
+++ b/crawl-ref/source/itemname.cc
@@ -346,6 +346,8 @@ const char* weapon_brand_name(const item_def& item, bool terse)
// ranged weapon brands
case SPWPN_FLAME: return ((terse) ? " (flame)" : " of flame");
case SPWPN_FROST: return ((terse) ? " (frost)" : " of frost");
+ case SPWPN_PENETRATION: return ((terse) ? " (penet)" : " of penetration");
+ case SPWPN_SHADOW: return ((terse) ? " (shadow)" : " of shadows");
// both ranged and non-ranged
case SPWPN_CHAOS: return ((terse) ? " (chaos)" : " of chaos");
@@ -1105,11 +1107,28 @@ std::string item_def::name_aux( description_level_type desc,
if (know_brand)
{
- if (brand == SPMSL_POISONED)
+ switch (brand)
+ {
+ case SPMSL_POISONED:
buff << ((terse) ? "poison " : "poisoned ");
-
- if (brand == SPMSL_CURARE)
+ break;
+ case SPMSL_CURARE:
buff << ((terse) ? "curare " : "curare-tipped ");
+ break;
+ case SPMSL_EXPLODING:
+ buff << ((terse) ? "explode " : "exploding ");
+ break;
+ case SPMSL_STEEL:
+ buff << "steel ";
+ break;
+ case SPMSL_SILVER:
+ buff << "silver ";
+ break;
+
+ default:
+ break;
+ }
+
}
if (know_pluses)
@@ -1136,6 +1155,9 @@ std::string item_def::name_aux( description_level_type desc,
case SPMSL_NORMAL:
case SPMSL_POISONED:
case SPMSL_CURARE:
+ case SPMSL_EXPLODING:
+ case SPMSL_STEEL:
+ case SPMSL_SILVER:
break;
case SPMSL_RETURNING:
buff << ((terse) ? " (return)" : " of returning");
@@ -1143,6 +1165,16 @@ std::string item_def::name_aux( description_level_type desc,
case SPMSL_CHAOS:
buff << ((terse) ? " (chaos)" : " of chaos");
break;
+ case SPMSL_PENETRATION:
+ buff << ((terse) ? " (penet)" : " of penetration");
+ break;
+ case SPMSL_SHADOW:
+ buff << ((terse) ? " (shadow)" : " of shadows");
+ break;
+ case SPMSL_DISPERSAL:
+ buff << ((terse) ? " (disperse)" : " of dispersal");
+ break;
+
default:
buff << " (buggy)";
}
diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc
index ac3411bc0f..f441b8e089 100644
--- a/crawl-ref/source/itemprop.cc
+++ b/crawl-ref/source/itemprop.cc
@@ -2566,8 +2566,16 @@ int item_mass( const item_def &item )
break;
case OBJ_MISSILES:
+ {
unit_mass = Missile_prop[ Missile_index[item.sub_type] ].mass;
+ int brand = get_ammo_brand(item);
+
+ if (brand == SPMSL_SILVER)
+ unit_mass *= 2;
+ else if(brand == SPMSL_STEEL)
+ unit_mass *= 3;
break;
+ }
case OBJ_FOOD:
unit_mass = Food_prop[ Food_index[item.sub_type] ].mass;
diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h
index b617aa90c9..8d8b2fe4dc 100644
--- a/crawl-ref/source/itemprop.h
+++ b/crawl-ref/source/itemprop.h
@@ -12,6 +12,8 @@
#include "externs.h"
+struct bolt;
+
enum armour_type
{
ARM_ROBE, // 0
@@ -99,6 +101,8 @@ enum brand_type // equivalent to (you.inv[].special or mitm[].special) % 30
SPWPN_CHAOS,
SPWPN_CONFUSE, // 20
+ SPWPN_PENETRATION,
+ SPWPN_SHADOW,
SPWPN_RANDART_I = 25, // 25
SPWPN_RANDART_II,
SPWPN_RANDART_III,
@@ -351,13 +355,19 @@ enum special_missile_type // to separate from weapons in general {dlb}
{
SPMSL_FORBID_BRAND = -1, // -1
SPMSL_NORMAL, // 0
- SPMSL_FLAME, // 1
- SPMSL_ICE, // 2
- SPMSL_POISONED, // 3
- SPMSL_POISONED_II, // 4 - unused
+ SPMSL_FLAME,
+ SPMSL_ICE,
+ SPMSL_POISONED,
+ SPMSL_POISONED_II, // unused
SPMSL_CURARE, // 5
- SPMSL_RETURNING, // 6
- SPMSL_CHAOS // 7
+ SPMSL_RETURNING,
+ SPMSL_CHAOS,
+ SPMSL_PENETRATION,
+ SPMSL_SHADOW,
+ SPMSL_DISPERSAL, // 10
+ SPMSL_EXPLODING,
+ SPMSL_STEEL,
+ SPMSL_SILVER
};
enum special_ring_type // jewellery mitm[].special values
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 49f03b28f0..e787b38f4b 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -1010,11 +1010,11 @@ void _monster_die_cloud(const monsters* monster, bool corpse, bool silent,
}
}
-void monster_die(monsters *monster, killer_type killer,
- int killer_index, bool silent, bool wizard)
+int monster_die(monsters *monster, killer_type killer,
+ int killer_index, bool silent, bool wizard)
{
if (invalid_monster(monster))
- return;
+ return (-1);
// If a monster was banished to the Abyss and then killed there,
// then its death wasn't a banishment.
@@ -1022,7 +1022,7 @@ void monster_die(monsters *monster, killer_type killer,
monster->flags &= ~MF_BANISHED;
if (!silent && _monster_avoided_death(monster, killer, killer_index))
- return;
+ return (-1);
mons_clear_trapping_net(monster);
@@ -1659,6 +1659,8 @@ void monster_die(monsters *monster, killer_type killer,
view_update_at(mwhere);
update_screen();
}
+
+ return (corpse);
}
void monster_cleanup(monsters *monster)
diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h
index 3dea7bb035..36561e82a4 100644
--- a/crawl-ref/source/monstuff.h
+++ b/crawl-ref/source/monstuff.h
@@ -86,8 +86,8 @@ bool monster_polymorph(monsters *monster, monster_type targetc,
* called from: bang - beam - effects - fight - misc - monstuff - mstuff2 -
* spells1 - spells2 - spells3 - spells4
* *********************************************************************** */
-void monster_die(monsters *monster, killer_type killer,
- int killer_index, bool silent = false, bool wizard = false);
+int monster_die(monsters *monster, killer_type killer,
+ int killer_index, bool silent = false, bool wizard = false);
int fill_out_corpse(const monsters* monster, item_def& corpse,
bool allow_weightless = false);
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc
index 28bb03ab6b..586f670a64 100644
--- a/crawl-ref/source/mstuff2.cc
+++ b/crawl-ref/source/mstuff2.cc
@@ -1204,8 +1204,9 @@ void setup_generic_throw(struct monsters *monster, struct bolt &pbolt)
bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
{
- bool returning = (get_weapon_brand(mitm[hand_used]) == SPWPN_RETURNING
- || get_ammo_brand(mitm[hand_used]) == SPMSL_RETURNING);
+ std::string ammo_name;
+
+ bool returning = false;
int baseHit = 0, baseDam = 0; // from thrown or ammo
int ammoHitBonus = 0, ammoDamBonus = 0; // from thrown or ammo
@@ -1238,15 +1239,10 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
if (mons_friendly(monster))
item.flags |= ISFLAG_DROPPED_BY_ALLY;
+ setup_missile_beam(monster, pbolt, item, ammo_name, returning);
+
// FIXME we should actually determine a sensible range here
- pbolt.range = LOS_RADIUS;
- pbolt.beam_source = monster_index(monster);
- pbolt.type = dchar_glyph(DCHAR_FIRED_MISSILE);
- pbolt.colour = item.colour;
- pbolt.flavour = BEAM_MISSILE;
- pbolt.thrower = KILL_MON_MISSILE;
- pbolt.item = &item;
- pbolt.aux_source.clear();
+ pbolt.range = LOS_RADIUS;
pbolt.aimed_at_spot = returning;
const launch_retval projected =
@@ -1317,7 +1313,6 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
int bow_brand = SPWPN_NORMAL;
const int ammo_brand = get_ammo_brand(item);
- bool poison = (ammo_brand == SPMSL_POISONED);
if (projected == LRET_LAUNCHED)
{
@@ -1398,6 +1393,10 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
if (bow_brand == SPWPN_VORPAL)
diceMult = diceMult * 130 / 100;
+ // As do steel ammo.
+ if (ammo_brand == SPMSL_STEEL)
+ diceMult = diceMult * 150 / 100;
+
// Note: we already have throw_energy taken off. -- bwr
int speed_delta = 0;
if (lnchType == WPN_CROSSBOW)
@@ -1426,55 +1425,10 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
}
// Chaos overides flame and frost
- if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
+ if (pbolt.flavour != BEAM_MISSILE)
{
- // Chaos can't be poisoned, since that might conflict with
- // the random healing effect or overlap with the random
- // poisoning effect.
- poison = false;
-
baseHit += 2;
exDamBonus += 6;
-
- pbolt.flavour = BEAM_CHAOS;
- pbolt.name = "bolt of chaos";
-
- pbolt.colour = EC_RANDOM;
- pbolt.type = dchar_glyph(DCHAR_FIRED_ZAP);
- }
- // WEAPON or AMMO of FIRE
- else if (bow_brand == SPWPN_FLAME && ammo_brand != SPMSL_ICE
- || ammo_brand == SPMSL_FLAME && bow_brand != SPWPN_FROST)
- {
- baseHit += 2;
- exDamBonus += 6;
-
- pbolt.flavour = BEAM_FIRE;
- pbolt.name = "bolt of ";
-
- if (poison)
- pbolt.name += "poison ";
-
- pbolt.name += "flame";
- pbolt.colour = RED;
- pbolt.type = dchar_glyph(DCHAR_FIRED_ZAP);
- }
- // WEAPON or AMMO of FROST
- else if (bow_brand == SPWPN_FROST && ammo_brand != SPMSL_FLAME
- || ammo_brand == SPMSL_ICE && bow_brand != SPWPN_FLAME)
- {
- baseHit += 2;
- exDamBonus += 6;
-
- pbolt.flavour = BEAM_COLD;
- pbolt.name = "bolt of ";
-
- if (poison)
- pbolt.name += "poison ";
-
- pbolt.name += "frost";
- pbolt.colour = WHITE;
- pbolt.type = dchar_glyph(DCHAR_FIRED_ZAP);
}
// monster intelligence bonus
@@ -1582,8 +1536,7 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
pbolt.fire();
// The item can be destroyed before returning.
- if (really_returns && mons_thrown_object_destroyed(&item, pbolt.target,
- true, pbolt.beam_source))
+ if (really_returns && thrown_object_destroyed(&item, pbolt.target, true))
{
really_returns = false;
}
@@ -1612,33 +1565,10 @@ bool mons_throw(struct monsters *monster, struct bolt &pbolt, int hand_used)
else if (dec_mitm_item_quantity(hand_used, 1))
monster->inv[returning ? slot : MSLOT_MISSILE] = NON_ITEM;
- return (true);
-}
+ if (pbolt.special_explosion != NULL)
+ delete pbolt.special_explosion;
-bool mons_thrown_object_destroyed( item_def *item, const coord_def& where,
- bool returning, int midx )
-{
- ASSERT( item != NULL );
-
- bool destroyed = (item->base_type == OBJ_MISSILES
- && item->sub_type != MI_THROWING_NET && coinflip());
- bool hostile_grid = grid_destroys_items(grd(where));
-
- // Non-returning items thrown into item-destroying grids are always
- // destroyed. Returning items are only destroyed if they would have
- // been randomly destroyed anyway.
- if (returning && !destroyed)
- hostile_grid = false;
-
- // Summoned items go poof if they're not returning.
- if (hostile_grid || !returning && (item->flags & ISFLAG_SUMMONED))
- {
- // No destruction sound here. Too much message spam otherwise.
- item_was_destroyed(*item, midx);
- destroyed = true;
- }
-
- return destroyed;
+ return (true);
}
static void _scale_draconian_breath(bolt& beam, int drac_type)
diff --git a/crawl-ref/source/mstuff2.h b/crawl-ref/source/mstuff2.h
index ce2966bc31..f249d78aa3 100644
--- a/crawl-ref/source/mstuff2.h
+++ b/crawl-ref/source/mstuff2.h
@@ -22,8 +22,6 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
void mons_cast_noise(monsters *monster, bolt &pbolt, spell_type spell_cast);
void setup_mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast);
bool mons_throw(monsters *monster, bolt &pbolt, int hand_used);
-bool mons_thrown_object_destroyed( item_def *item, const coord_def& where,
- bool returning, int midx );
void setup_generic_throw(monsters *monster, bolt &pbolt);
void mons_trap(monsters *monster);
bool monster_random_space(const monsters *monster, coord_def& target,
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index 18d2dc91a4..5f865a63f4 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -3419,9 +3419,16 @@ bool is_evil_item(const item_def& item)
|| item_eff == SPWLD_ZONGULDROK
|| item_brand == SPWPN_DRAINING
|| item_brand == SPWPN_PAIN
- || item_brand == SPWPN_VAMPIRICISM);
+ || item_brand == SPWPN_VAMPIRICISM
+ || item_brand == SPWPN_SHADOW);
}
break;
+ case OBJ_MISSILES:
+ {
+ const int item_brand = get_ammo_brand(item);
+ retval = is_demonic(item) || item_brand == SPMSL_SHADOW;
+ break;
+ }
case OBJ_WANDS:
retval = (item.sub_type == WAND_DRAINING);
break;