Conjure Flame for monsters.
authorSteve Melenchuk <smelenchuk@gmail.com>
Tue, 4 Nov 2014 02:48:56 +0000 (19:48 -0700)
committerSteve Melenchuk <smelenchuk@gmail.com>
Tue, 4 Nov 2014 02:50:34 +0000 (19:50 -0700)
They try to block your lines of retreat into hallways with it.

This time around, it's a ghost exclusive, but I can think of a few
enemies needing new spellsets that might benefit from this.

crawl-ref/source/mon-cast.cc
crawl-ref/source/spl-cast.cc
crawl-ref/source/spl-clouds.cc
crawl-ref/source/spl-clouds.h

index 72cb948..e5e2c18 100644 (file)
@@ -63,6 +63,7 @@
 #include "tiledef-dngn.h"
 #endif
 #include "traps.h"
+#include "travel.h"
 #include "view.h"
 #include "viewchar.h"
 #include "xom.h"
@@ -73,6 +74,7 @@ static int  _mons_mesmerise(monster* mons, bool actual = true);
 static int  _mons_cause_fear(monster* mons, bool actual = true);
 static int  _mons_mass_confuse(monster* mons, bool actual = true);
 static coord_def _mons_fragment_target(monster *mons);
+static coord_def _mons_conjure_flame_pos(monster* mon, actor* foe);
 static bool _mons_consider_tentacle_throwing(const monster &mons);
 static bool _tentacle_toss(const monster &thrower, actor &victim, int pow);
 static int _throw_site_score(const monster &thrower, const actor &victim,
@@ -848,6 +850,7 @@ bolt mons_spell_beam(monster* mons, spell_type spell_cast, int power,
     case SPELL_PORTAL_PROJECTILE:     // ditto
     case SPELL_GLACIATE:              // ditto
     case SPELL_CLOUD_CONE:            // ditto
+    case SPELL_CONJURE_FLAME:         // ditto
         beam.flavour  = BEAM_DEVASTATION;
         beam.is_beam  = true;
         // Doesn't take distance into account, but this is just a tracer so
@@ -1375,6 +1378,11 @@ bool setup_mons_cast(monster* mons, bolt &pbolt, spell_type spell_cast,
             pbolt.target = _mons_fragment_target(mons);
             pbolt.aimed_at_spot = true; // to get noise to work properly
         }
+        else if (spell_cast == SPELL_CONJURE_FLAME)
+        {
+            pbolt.target = _mons_conjure_flame_pos(mons, mons->get_foe());
+            pbolt.aimed_at_spot = true; // ditto
+        }
      }
 
     return true;
@@ -2851,6 +2859,75 @@ static bool _spray_tracer(monster *caster, int pow, bolt parent_beam, spell_type
     return mons_should_fire(beam);
 }
 
+/**
+ * Pick a target for conjuring a flame.
+ *
+ * @param[in] mon The monster casting this.
+ * @param[in] foe The victim whose movement we are trying to impede.
+ * @return A position for conjuring a cloud.
+ */
+static coord_def _mons_conjure_flame_pos(monster* mon, actor* foe)
+{
+    // Don't bother if our target is sufficiently fire-resistant.
+    if (foe->res_fire() >= 3)
+        return coord_def();
+
+    const int pow = 6 * mon->spell_hd(SPELL_CONJURE_FLAME);
+    const coord_def foe_pos = foe->pos();
+    const coord_def a = foe_pos - mon->pos();
+    vector<coord_def> targets;
+
+    const int range = spell_range(SPELL_CONJURE_FLAME, pow, false);
+    for (distance_iterator di(mon->pos(), true, true, range); di; ++di)
+    {
+        // Our target needs to be in LOS, and we can't have a creature or
+        // cloud there. Technically we can target flame clouds, but that's
+        // usually not very constructive where monsters are concerned.
+        if (!cell_see_cell(mon->pos(), *di, LOS_SOLID)
+            || cell_is_solid(*di)
+            || (actor_at(*di) && mon->can_see(actor_at(*di))))
+        {
+            continue;
+        }
+
+        const int cloud = env.cgrid(*di);
+        if (cloud != EMPTY_CLOUD)
+            continue;
+
+        // Conjure flames *behind* the target; blocking the target from
+        // accessing us just lets them run away, which is bad if our target
+        // is usually the player.
+        coord_def b = *di - foe_pos;
+        int dot = (a.x * b.x + a.y * b.y);
+        if (dot < 0)
+            continue;
+
+        // Try to block off a hallway.
+        int floor_count = 0;
+        for (adjacent_iterator ai(*di); ai; ++ai)
+        {
+            if (feat_is_traversable(grd(*ai))
+                && (env.cgrid(*ai) == EMPTY_CLOUD
+                    || !is_damaging_cloud(env.cloud[env.cgrid(*ai)].type)))
+            {
+                floor_count++;
+            }
+        }
+
+        if (floor_count != 2)
+            continue;
+
+        targets.push_back(*di);
+    }
+
+    // If we found something, pick a square at random to block.
+    const int count = targets.size();
+    if (!count)
+        return coord_def();
+
+    return targets[random2(count)];
+}
+
 /** Chooses a matching spell from this spell list, based on frequency.
  *
  *  @param[in]  spells     the monster spell list to search
@@ -3216,7 +3293,8 @@ bool handle_mon_spell(monster* mons, bolt &beem)
                 continue;
             }
 
-            if (spell_cast == SPELL_LRD && !in_bounds(beem.target))
+            if ((spell_cast == SPELL_LRD || spell_cast == SPELL_CONJURE_FLAME)
+                && !in_bounds(beem.target))
             {
                 spell_cast = SPELL_NO_SPELL;
                 continue;
@@ -4012,8 +4090,8 @@ static coord_def _mons_fragment_target(monster *mons)
     {
         bool temp;
         bolt beam;
-        if (!setup_fragmentation_beam(beam, pow, mons, mons->target, false,
-                                      true, true, NULL, temp, temp))
+                if (!setup_fragmentation_beam(beam, pow, mons, mons->target, false,
+                                              true, true, NULL, temp, temp))
         {
             return target;
         }
@@ -5913,6 +5991,22 @@ void mons_cast(monster* mons, bolt &pbolt, spell_type spell_cast,
 
         return;
     }
+
+    case SPELL_CONJURE_FLAME:
+    {
+        if (in_bounds(pbolt.target))
+        {
+            if (conjure_flame(mons, 6 * mons->spell_hd(spell_cast),
+                              pbolt.target, false) != SPRET_SUCCESS)
+            {
+                canned_msg(MSG_NOTHING_HAPPENS);
+            }
+        }
+        else if (you.can_see(mons))
+            canned_msg(MSG_NOTHING_HAPPENS);
+
+        return;
+    }
     }
 
     // If a monster just came into view and immediately cast a spell,
index 5fd3069..2fa2347 100644 (file)
@@ -1839,7 +1839,7 @@ static spret_type _do_cast(spell_type spell, int powc,
         return cast_controlled_blink(powc, fail);
 
     case SPELL_CONJURE_FLAME:
-        return conjure_flame(powc, beam.target, fail);
+        return conjure_flame(&you, powc, beam.target, fail);
 
     case SPELL_PASSWALL:
         return cast_passwall(spd.delta, powc, fail);
index bbc9ba9..2483c84 100644 (file)
 #include "terrain.h"
 #include "viewchar.h"
 
-spret_type conjure_flame(int pow, const coord_def& where, bool fail)
+spret_type conjure_flame(const actor *agent, int pow, const coord_def& where,
+                         bool fail)
 {
     // FIXME: This would be better handled by a flag to enforce max range.
-    if (distance2(where, you.pos()) > dist_range(spell_range(SPELL_CONJURE_FLAME,
-                                                      pow))
+    if (distance2(where, agent->pos()) > dist_range(
+            spell_range(SPELL_CONJURE_FLAME, pow))
         || !in_bounds(where))
     {
-        mpr("That's too far away.");
+        if (agent->is_player())
+            mpr("That's too far away.");
         return SPRET_ABORT;
     }
 
     if (cell_is_solid(where))
     {
-        const char *feat = feat_type_name(grd(where));
-        mprf("You can't place the cloud on %s.", article_a(feat).c_str());
+        if (agent->is_player())
+        {
+            const char *feat = feat_type_name(grd(where));
+            mprf("You can't place the cloud on %s.", article_a(feat).c_str());
+        }
         return SPRET_ABORT;
     }
 
@@ -51,45 +56,59 @@ spret_type conjure_flame(int pow, const coord_def& where, bool fail)
 
     if (cloud != EMPTY_CLOUD && env.cloud[cloud].type != CLOUD_FIRE)
     {
-        mpr("There's already a cloud there!");
+        if (agent->is_player())
+            mpr("There's already a cloud there!");
         return SPRET_ABORT;
     }
 
-    // Note that self-targeting is handled by SPFLAG_NOT_SELF.
-    monster* mons = monster_at(where);
-    if (mons)
+    actor* victim = actor_at(where);
+    if (victim)
     {
-        if (you.can_see(mons))
+        if (agent->can_see(victim))
         {
-            mpr("You can't place the cloud on a creature.");
+            if (agent->is_player())
+                mpr("You can't place the cloud on a creature.");
             return SPRET_ABORT;
         }
 
         fail_check();
 
         // FIXME: maybe should do _paranoid_option_disable() here?
-        mpr("You see a ghostly outline there, and the spell fizzles.");
+        if (agent->is_player())
+            mpr("You see a ghostly outline there, and the spell fizzles.");
         return SPRET_SUCCESS;      // Don't give free detection!
     }
 
     fail_check();
 
-    did_god_conduct(DID_FIRE, min(5 + pow/2, 23));
+    if (agent->is_player())
+        did_god_conduct(DID_FIRE, min(5 + pow/2, 23));
 
     if (cloud != EMPTY_CLOUD)
     {
         // Reinforce the cloud - but not too much.
         // It must be a fire cloud from a previous test.
-        mpr("The fire roars with new energy!");
+        if (you.see_cell(where))
+            mpr("The fire roars with new energy!");
         const int extra_dur = 2 + min(random2(pow) / 2, 20);
         env.cloud[cloud].decay += extra_dur * 5;
-        env.cloud[cloud].set_whose(KC_YOU);
+        env.cloud[cloud].source = agent->mid;
+        if (agent->is_player())
+            env.cloud[cloud].set_whose(KC_YOU);
+        else
+            env.cloud[cloud].set_killer(KILL_MON_MISSILE);
     }
     else
     {
         const int durat = min(5 + (random2(pow)/2) + (random2(pow)/2), 23);
-        place_cloud(CLOUD_FIRE, where, durat, &you);
-        mpr("The fire roars!");
+        place_cloud(CLOUD_FIRE, where, durat, agent);
+        if (you.see_cell(where))
+        {
+            if (agent->is_player())
+                mpr("The fire roars!");
+            else
+                mpr("A cloud of flames roars to life!");
+        }
     }
     noisy(spell_effect_noise(SPELL_CONJURE_FLAME), where);
 
index 079219c..76da2c7 100644 (file)
@@ -5,7 +5,8 @@
 
 struct bolt;
 
-spret_type conjure_flame(int pow, const coord_def& where, bool fail);
+spret_type conjure_flame(const actor *agent, int pow, const coord_def& where,
+                         bool fail);
 spret_type stinking_cloud(int pow, bolt &beam, bool fail);
 
 void big_cloud(cloud_type cl_type, const actor *agent, const coord_def& where,