diff options
-rw-r--r-- | crawl-ref/source/beam.cc | 332 | ||||
-rw-r--r-- | crawl-ref/source/beam.h | 29 | ||||
-rw-r--r-- | crawl-ref/source/item_use.cc | 653 | ||||
-rw-r--r-- | crawl-ref/source/item_use.h | 5 | ||||
-rw-r--r-- | crawl-ref/source/itemname.cc | 38 | ||||
-rw-r--r-- | crawl-ref/source/itemprop.cc | 8 | ||||
-rw-r--r-- | crawl-ref/source/itemprop.h | 22 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 10 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/mstuff2.cc | 100 | ||||
-rw-r--r-- | crawl-ref/source/mstuff2.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/religion.cc | 9 |
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 ©) +{ + // 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; |