summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordolorous <dolorous@c06c8d41-db1a-0410-9941-cceddc491573>2009-07-28 03:39:01 +0000
committerdolorous <dolorous@c06c8d41-db1a-0410-9941-cceddc491573>2009-07-28 03:39:01 +0000
commit72c1249ae7f4841dc8b335cb3a0a5ce81cb45887 (patch)
tree6b65ec871edba333b3ea18274025f52e4b384a24
parent102ecc248252562f59d7b7baf445cd5cdb2e3162 (diff)
downloadcrawl-ref-72c1249ae7f4841dc8b335cb3a0a5ce81cb45887.tar.gz
crawl-ref-72c1249ae7f4841dc8b335cb3a0a5ce81cb45887.zip
Apply caotto's patch in [2827204] to finish the plant god, with some
cleanups added by me. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10442 c06c8d41-db1a-0410-9941-cceddc491573
-rw-r--r--crawl-ref/source/artefact.cc10
-rw-r--r--crawl-ref/source/beam.cc48
-rw-r--r--crawl-ref/source/beam.h4
-rw-r--r--crawl-ref/source/effects.cc307
-rw-r--r--crawl-ref/source/effects.h14
-rw-r--r--crawl-ref/source/enum.h2
-rw-r--r--crawl-ref/source/misc.cc3
-rw-r--r--crawl-ref/source/mon-util.cc26
-rw-r--r--crawl-ref/source/mon-util.h3
-rw-r--r--crawl-ref/source/monstuff.cc27
-rw-r--r--crawl-ref/source/religion.cc287
-rw-r--r--crawl-ref/source/religion.h2
-rw-r--r--crawl-ref/source/spells2.cc34
-rw-r--r--crawl-ref/source/spells3.cc3
-rw-r--r--crawl-ref/source/view.cc24
15 files changed, 586 insertions, 208 deletions
diff --git a/crawl-ref/source/artefact.cc b/crawl-ref/source/artefact.cc
index f0f097e69b..e6e9263755 100644
--- a/crawl-ref/source/artefact.cc
+++ b/crawl-ref/source/artefact.cc
@@ -184,6 +184,16 @@ static bool _god_fits_artefact(const god_type which_god, const item_def &item,
return (false);
break;
+ case GOD_FEAWN:
+ // Anti-necromancy god: nothing involving necromancy or
+ // necromantic spell use.
+ if (brand == SPWPN_DRAINING || brand == SPWPN_PAIN
+ || brand == SPWPN_VAMPIRICISM)
+ {
+ return (false);
+ }
+ break;
+
default:
break;
}
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index ee0d28b963..1f16b1a6d6 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -4084,17 +4084,37 @@ void bolt::tracer_enchantment_affect_monster(monsters* mon)
bool bolt::determine_damage(monsters* mon, int& preac, int& postac, int& final,
std::vector<std::string>& messages)
{
- // Worshippers of Feawn may shoot past friendly plants.
- if (!is_explosion && !is_enchantment()
- && this->attitude == ATT_FRIENDLY
- && you.religion == GOD_FEAWN
- && mons_genus(mon->mons_species()) == MONS_PLANT
- && mon->mons_species() != MONS_GIANT_SPORE
- && mon->attitude == ATT_FRIENDLY)
- {
- // FIXME: Messaging is kind of problematic here.
+ // Feawn worshippers can fire through monsters of the same alignment.
+ // This means Feawn-worshipping players can fire through allied plants,
+ // and also means that feawn worshiping oklob plants can fire through
+ // plants with the same attitude.
+ bool originator_worships_feawn=false;
+
+ // Checking beam_source to decide whether the player or a monster
+ // fired the beam (so we can check their religion). This is complicated
+ // by the fact that this beam may in fact be an explosion caused by a
+ // miscast effect. In that case the value of beam_source may be negative
+ // (god induced miscast) or greater than NON_MONSTER (various other miscast
+ // sources). So we check whether or not this is an explosion, and also the
+ // range of beam_source before attempting to reference env.mons with it.
+ // -cao
+ if (!is_explosion && this->beam_source == NON_MONSTER)
+ originator_worships_feawn = (you.religion == GOD_FEAWN);
+ else if (!is_explosion && beam_source >= 0 && beam_source < MAX_MONSTERS)
+ originator_worships_feawn = (env.mons[beam_source].god == GOD_FEAWN);
+
+ if (!is_enchantment()
+ && attitude == mon->attitude
+ && originator_worships_feawn
+ && mons_is_plant(mon))
+ {
if (!is_tracer)
- simple_god_message(" protects your plant from harm.", GOD_FEAWN);
+ {
+ // FIXME: Could use a better message, something about dodging that
+ // doesn't sound excessively weird would be nice.
+ mprf(MSGCH_GOD, "Feawn protects %s plant from harm.",
+ attitude == ATT_FRIENDLY ? "your" : "a");
+ }
return (false);
}
@@ -4581,6 +4601,14 @@ void bolt::affect_monster(monsters* mon)
update_hurt_or_helped(mon);
enable_attack_conducts(conducts);
+ // We'll say giant spore explosions don't trigger the ally attack conduct
+ // for Feawn worshipers. Mostly because you can accidentally blow up a
+ // group of 8 plants and get placed under penance until the end of time
+ // otherwise. I'd prefer to do this elsewhere but the beam information
+ // goes out of scope. -cao
+ if (you.religion == GOD_FEAWN && flavour == BEAM_SPORE)
+ conducts[0].enabled = false;
+
// The beam hit.
if (mons_near(mon))
{
diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h
index a87283766b..94c0e432d0 100644
--- a/crawl-ref/source/beam.h
+++ b/crawl-ref/source/beam.h
@@ -77,6 +77,10 @@ struct bolt
int ench_power, hit;
killer_type thrower; // what kind of thing threw this?
int ex_size; // explosion radius (0==none)
+
+ // beam_source can be -GOD_ENUM_VALUE or one of the miscast sources from
+ // spl-mis.h (values are strictly greater than NON_MONSTER in that case)
+ // for explosions generated by miscast effects. -cao
int beam_source; // NON_MONSTER or monster index #
std::string name;
std::string short_name;
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index bc40ef4a7e..02240e1c3e 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -1992,7 +1992,6 @@ static void _set_friendly_foes(bool allow_patrol = false)
{
monsters *mon(&menv[i]);
if (!mon->alive() || !mons_near(mon) || !mons_friendly_real(mon)
- || mon->has_ench(ENCH_BERSERK)
|| mon->mons_species() == MONS_GIANT_SPORE)
{
continue;
@@ -3808,14 +3807,14 @@ template<typename T>
struct greater_second
{
// The stl priority queue is a max queue and uses < as the default
- // comparison. We want a min queue so we have to use a > operation here.
+ // comparison. We want a min queue so we have to use a > operation
+ // here.
bool operator()(const T & left, const T & right)
{
- return left.second > right.second;
+ return (left.second > right.second);
}
};
-
// Basically we want to break a circle into n_arcs equal sized arcs and find
// out which arc the input point pos falls on.
static int _arc_decomposition(const coord_def & pos, int n_arcs)
@@ -3826,196 +3825,205 @@ static int _arc_decomposition(const coord_def & pos, int n_arcs)
theta = pos.y > 0 ? PI / 2 : -PI / 2;
if (theta < 0)
- theta += 2*PI;
+ theta += 2 * PI;
- float arc_angle = 2*PI / n_arcs;
+ float arc_angle = 2 * PI / n_arcs;
theta += arc_angle / 2.0f;
- if (theta >= 2*PI)
- theta -= 2*PI;
+ if (theta >= 2 * PI)
+ theta -= 2 * PI;
return static_cast<int> (theta / arc_angle);
}
-// Place a partial ring of toadstools around the given corpse. returns the
-// number of mushrooms spawned. A return of 0 indicates no mushrooms were
-// placed -> some sort of failure mode was reached.
-static int _mushroom_ring(item_def &corpse, int & seen_count)
+int place_ring(std::vector<coord_def> & ring_points,
+ coord_def & origin,
+ mgen_data & prototype,
+ int n_arcs,
+ int arc_occupancy,
+ int & seen_count)
{
+ std::random_shuffle(ring_points.begin(),
+ ring_points.end());
- // minimum number of mushrooms spawned on a given ring
- unsigned min_spawn = 2;
+ int target_amount = ring_points.size();
+ int spawned_count = 0;
+ seen_count = 0;
- // highest radius we will allow. 8 is LOS
- const int max_radius = LOS_RADIUS;
+ std::vector<int> arc_counts(n_arcs, arc_occupancy);
- seen_count = 0;
+ for (unsigned i = 0;
+ spawned_count < target_amount && i < ring_points.size();
+ i++)
+ {
+ int direction = _arc_decomposition(ring_points.at(i)
+ - origin, n_arcs);
- // Just want to associate a point with a distance here for convenience
- typedef std::pair<coord_def, int> coord_dist;
+ if (arc_counts[direction]-- <= 0)
+ continue;
+
+ prototype.pos = ring_points.at(i);
+ const int mushroom = create_monster(prototype, false);
+
+ if (mushroom != -1)
+ {
+ spawned_count++;
+ if (see_grid(ring_points.at(i)))
+ seen_count++;
+ }
+ }
+
+ return (spawned_count);
+}
+
+// Collect lists of points that are within LOS (under the given env map),
+// unoccupied, and not solid (walls/statues).
+void collect_radius_points(std::vector<std::vector<coord_def> > &radius_points,
+ coord_def & origin, env_show_grid & losgrid)
+{
+
+ radius_points.clear();
+ radius_points.resize(LOS_RADIUS);
+
+ // Just want to associate a point with a distance here for convenience.
+ typedef std::pair<coord_def, int> coord_dist;
// Using a priority queue because squares don't make very good circles at
- // larger radii.
- // Generally we will visit points in order of distance to the origin
- // (not path distance) under distance=sqrt(x^2+y^2-1);
+ // larger radii. We will visit points in order of increasing euclidean
+ // distance from the origin (not path distance).
std::priority_queue<coord_dist,
std::vector<coord_dist>,
greater_second<coord_dist> > fringe;
- coord_dist origin(corpse.pos, 0);
-
- fringe.push(origin);
+ fringe.push(coord_dist(origin, 0));
std::set<int> visited_indices;
- int max_distance = max_radius * max_radius - 1;
+ int current_r = 1;
+ int current_thresh = current_r * (current_r + 1);
- std::vector<coord_def> radius_points[max_radius];
+ int max_distance = LOS_RADIUS * LOS_RADIUS;
- int max_visited = 0;
while (!fringe.empty())
{
coord_dist current = fringe.top();
- fringe.pop();
-
- int idx = current.first.x + current.first.y * X_WIDTH;
-
- if (!visited_indices.insert(idx).second)
- continue;
-
- // we're done here once we hit a point that is farther away from the
+ // We're done here once we hit a point that is farther away from the
// origin than our maximum permissible radius.
if (current.second > max_distance)
break;
- if (current.second > max_visited)
- max_visited = current.second;
+ fringe.pop();
- float current_distance =current.second ? sqrtf(current.second + 1) : 0;
- int bound = static_cast<int> (current_distance + 0.5);
+ int idx = current.first.x + current.first.y * X_WIDTH;
+ if (!visited_indices.insert(idx).second)
+ continue;
- // We don't include radius 0 as an option. This is also a good place
- // to check if the squares is already occupied since we want to search
- // past occupied squares but don't want to place toadstools on them.
- if (bound > 0 && !actor_at(current.first))
+ while (current.second > current_thresh)
{
- radius_points[bound - 1].push_back(current.first);
+ current_r++;
+ current_thresh = current_r * (current_r + 1);
}
+ // We don't include radius 0. This is also a good place to check if
+ // the squares are already occupied since we want to search past
+ // occupied squares but don't want to consider them valid targets.
+ if (current.second && !actor_at(current.first))
+ radius_points[current_r - 1].push_back(current.first);
+
for (adjacent_iterator i(current.first); i; ++i)
{
coord_dist temp(*i, current.second);
- coord_def local = temp.first - origin.first;
+ // If the grid is out of LOS, skip it.
+ if (!see_grid(losgrid, origin, temp.first))
+ continue;
- temp.second = local.x * local.x + local.y * local.y - 1;
+ coord_def local = temp.first - origin;
- // Don't link to nodes with a smaller absolute distance from the
- // origin than the current node. This is some sort of connectivity
- // constraint, in general we don't want parts of a ring to be
- // connected via a higher radius since rings were supposedly
- // created by outwards expansion.
- if (temp.second < max_visited)
- continue;
+ temp.second = local.abs();
idx = temp.first.x + temp.first.y * X_WIDTH;
if (visited_indices.find(idx) == visited_indices.end()
&& in_bounds(temp.first)
- && mons_class_can_pass(MONS_TOADSTOOL, grd(temp.first)))
+ && !grid_is_solid(temp.first))
{
fringe.push(temp);
}
}
}
+}
+
+// Place a partial ring of toadstools around the given corpse. Returns
+// the number of mushrooms spawned. A return of 0 indicates no
+// mushrooms were placed -> some sort of failure mode was reached.
+static int _mushroom_ring(item_def &corpse, int & seen_count)
+{
+ // minimum number of mushrooms spawned on a given ring
+ unsigned min_spawn = 2;
+
+ seen_count = 0;
+
+ std::vector<std::vector<coord_def> > radius_points;
+
+ env_show_grid losgrid;
+ losight(losgrid, grd, corpse.pos, true);
+
+ collect_radius_points(radius_points,corpse.pos, losgrid);
// So what we have done so far is collect the set of points at each radius
// reachable from the origin with (somewhat constrained) 8 connectivity,
// now we will choose one of those radii and spawn mushrooms at some
// of the points along it.
+ int chosen_idx = random2(LOS_RADIUS);
- int chosen_idx = random2(max_radius);
-
- // Excluding radius 1 rings because they don't look particularly good
- // (or at least they don't look good with target_arc_len=2sqrt(2)
- // they look ok with arc_len=2, but that doesn't seem very good for
- // higher radii, maybe there should be a more complicated relationship
- // between radius and target arc length, but whatever).
- int min_idx=1;
-
- for (int i = 2; i < max_radius; i++, chosen_idx++)
+ unsigned max_size = 0;
+ for (unsigned i = 0; i < LOS_RADIUS; ++i)
{
- chosen_idx = chosen_idx % max_radius;
-
- if (chosen_idx >= min_idx
- && radius_points[chosen_idx].size() >= min_spawn)
+ if (radius_points[i].size() >= max_size)
{
- break;
+ max_size = radius_points[i].size();
+ chosen_idx = i;
}
}
- // Couldn't find enough valid points at any radius?
- if (radius_points[chosen_idx].size() < min_spawn)
- return 0;
+ chosen_idx = random2(chosen_idx + 1);
- std::random_shuffle(radius_points[chosen_idx].begin(),
- radius_points[chosen_idx].end());
+ // Not enough valid points?
+ if (radius_points[chosen_idx].size() < min_spawn)
+ return (0);
- int target_amount = radius_points[chosen_idx].size();
- int spawned_count = 0;
+ mgen_data temp(MONS_TOADSTOOL,
+ BEH_HOSTILE, 0, 0,
+ coord_def(),
+ MHITNOT,
+ MG_FORCE_PLACE,
+ GOD_NO_GOD,
+ MONS_PROGRAM_BUG,
+ 0,
+ corpse.colour);
- float target_arc_len=2*sqrtf(2.0f);
- //float target_arc_len = 2;
+ float target_arc_len = 2 * sqrtf(2.0f);
- int n_arcs = static_cast<int> (ceilf(2*PI * (chosen_idx + 1)
+ int n_arcs = static_cast<int> (ceilf(2 * PI * (chosen_idx + 1)
/ target_arc_len));
- int mushrooms_per_arc = 1;
-
- std::vector<int> arc_counts(n_arcs, mushrooms_per_arc);
-
- for (unsigned i = 0;
- spawned_count < target_amount && i < radius_points[chosen_idx].size();
- i++)
- {
- int direction = _arc_decomposition(radius_points[chosen_idx].at(i)
- - origin.first, n_arcs);
-
- if (arc_counts[direction]-- <= 0)
- continue;
+ int spawned_count = place_ring(radius_points[chosen_idx], corpse.pos, temp,
+ n_arcs, 1, seen_count);
- const int mushroom = create_monster(
- mgen_data(MONS_TOADSTOOL,
- BEH_HOSTILE, 0, 0,
- radius_points[chosen_idx].at(i),
- MHITNOT,
- MG_FORCE_PLACE,
- GOD_NO_GOD,
- MONS_PROGRAM_BUG,
- 0,
- corpse.colour),
- false);
-
- if (mushroom != -1)
- {
- spawned_count++;
- if (see_grid(radius_points[chosen_idx].at(i)))
- seen_count++;
- }
- }
-
- return spawned_count;
+ return (spawned_count);
}
-// Try to spawn 'target_count' mushrooms around the position of 'corpse'.
-// Returns the number of mushrooms actually spawned.
-// Mushrooms radiate outwards from the corpse following bfs with 8-connectivity.
-// Could change the expansion pattern by using a priority queue for
-// sequencing (priority = distance from origin under some metric).
+// Try to spawn 'target_count' mushrooms around the position of
+// 'corpse'. Returns the number of mushrooms actually spawned.
+// Mushrooms radiate outwards from the corpse following bfs with
+// 8-connectivity. Could change the expansion pattern by using a
+// priority queue for sequencing (priority = distance from origin under
+// some metric).
int spawn_corpse_mushrooms(item_def &corpse,
int target_count,
int & seen_targets,
@@ -4025,7 +4033,7 @@ int spawn_corpse_mushrooms(item_def &corpse,
if (target_count == 0)
return 0;
- int c_size=8;
+ int c_size = 8;
int permutation[] = {0, 1, 2, 3, 4, 5, 6, 7};
int placed_targets = 0;
@@ -4034,32 +4042,32 @@ int spawn_corpse_mushrooms(item_def &corpse,
std::set<int> visited_indices;
// Slight chance of spawning a ring of mushrooms around the corpse (and
- // skeletonizing it) if the corpse square is unoccupied.
+ // skeletonising it) if the corpse square is unoccupied.
if (!actor_at(corpse.pos) && one_chance_in(100))
{
int ring_seen;
// It's possible no reasonable ring can be found, in that case we'll
- // give up and just place a toadstool on top of the corpse (probably)
- int res=_mushroom_ring(corpse, ring_seen);
+ // give up and just place a toadstool on top of the corpse (probably).
+ int res = _mushroom_ring(corpse, ring_seen);
if (res)
{
- corpse.special=0;
+ corpse.special = 0;
+
if (see_grid(corpse.pos))
mpr("A ring of toadstools grow before your very eyes.");
else if (ring_seen > 1)
mpr("Some toadstools grow in a peculiar arc.");
- else if (ring_seen >0)
+ else if (ring_seen > 0)
mpr("A toadstool grows.");
seen_targets = -1;
-
- return res;
+ return (res);
}
}
- visited_indices.insert(X_WIDTH*corpse.pos.y + corpse.pos.x);
+ visited_indices.insert(X_WIDTH * corpse.pos.y + corpse.pos.x);
fringe.push(corpse.pos);
while (!fringe.empty())
@@ -4069,7 +4077,8 @@ int spawn_corpse_mushrooms(item_def &corpse,
fringe.pop();
actor * occupant = NULL;
- // is this square occupied by a non mushroom?
+
+ // Is this square occupied by a non mushroom?
if ((occupant = actor_at(current))
&& occupant->mons_species() != MONS_TOADSTOOL)
{
@@ -4102,7 +4111,7 @@ int spawn_corpse_mushrooms(item_def &corpse,
{
coord_def offset = corpse.pos - current;
- int dist = static_cast<int> (sqrtf(offset.abs()) + 0.5);
+ int dist = static_cast<int>(sqrtf(offset.abs()) + 0.5);
int time_left = random2(8) + dist * 8 + 1;
@@ -4116,24 +4125,22 @@ int spawn_corpse_mushrooms(item_def &corpse,
placed_targets++;
if (see_grid(current))
- {
seen_targets++;
- }
}
else
continue;
}
- // We're done here if we place the desired number of mushrooms.
+ // We're done here if we placed the desired number of mushrooms.
if (placed_targets == target_count)
break;
- // wish adjacent_iterator had a random traversal
+ // Wish adjacent_iterator had a random traversal.
std::random_shuffle(permutation, permutation+c_size);
for (int count = 0; count < c_size; ++count)
{
- coord_def temp=current + Compass[permutation[count]];
+ coord_def temp = current + Compass[permutation[count]];
int index = temp.x + temp.y * X_WIDTH;
@@ -4148,12 +4155,12 @@ int spawn_corpse_mushrooms(item_def &corpse,
}
}
- return placed_targets;
+ return (placed_targets);
}
int mushroom_prob(item_def & corpse)
{
- int low_threshold = 5;
+ int low_threshold = 5;
int high_threshold = FRESHEST_CORPSE - 5;
// Expect this many trials over a corpse's lifetime since this function
@@ -4161,32 +4168,32 @@ int mushroom_prob(item_def & corpse)
int step_size = 10;
float total_trials = (high_threshold - low_threshold) / step_size;
- // chance of producing no mushrooms (not really because of weight_factor
- // below)
- float p_failure = .5;
+ // Chance of producing no mushrooms (not really because of weight_factor
+ // below).
+ float p_failure = 0.5f;
float trial_prob_f = 1 - powf(p_failure, 1.0f / total_trials);
// The chance of producing mushrooms depends on the weight of the
- // corpse involved. Humans weigh 550 so we will take that as the
+ // corpse involved. Humans weigh 550 so we will take that as the
// base factor here.
- float weight_factor = item_mass(corpse)/550.0f;
+ float weight_factor = item_mass(corpse) / 550.0f;
trial_prob_f *= weight_factor;
- int trial_prob = static_cast<int> (100* trial_prob_f );
+ int trial_prob = static_cast<int>(100 * trial_prob_f);
- return trial_prob;
+ return (trial_prob);
}
-
-// Randomly decide whether or not to spawn a mushroom over the given corpse
-// Assumption: this is called before the rotting away logic in update_corpses.
-// Some conditions in this function may set the corpse timer to 0, assuming
-// that the corpse will be turned into a skeleton/destroyed on this update.
+// Randomly decide whether or not to spawn a mushroom over the given
+// corpse. Assumption: this is called before the rotting away logic in
+// update_corpses. Some conditions in this function may set the corpse
+// timer to 0, assuming that the corpse will be turned into a
+// skeleton/destroyed on this update.
static void _maybe_spawn_mushroom(item_def & corpse, int rot_time)
{
- // We won't spawn a mushroom within 10 turns of the corpse being created
+ // We won't spawn a mushroom within 10 turns of the corpse's being created
// or rotting away.
int low_threshold = 5;
int high_threshold = FRESHEST_CORPSE - 5;
diff --git a/crawl-ref/source/effects.h b/crawl-ref/source/effects.h
index 23cfe2b726..3580839ed0 100644
--- a/crawl-ref/source/effects.h
+++ b/crawl-ref/source/effects.h
@@ -56,6 +56,20 @@ int spawn_corpse_mushrooms(item_def &corpse,
int & seen_targers,
bool distance_as_time = false);
+struct mgen_data;
+int place_ring(std::vector<coord_def> & ring_points,
+ coord_def & origin,
+ mgen_data & prototype,
+ int n_arcs,
+ int arc_occupancy,
+ int & seen_count);
+
+// Collect lists of points that are within LOS (under the given env map),
+// unoccupied, and not solid (walls/statues).
+void collect_radius_points(std::vector<std::vector<coord_def> > &radius_points,
+ coord_def & origin, env_show_grid & losgrid);
+
+
// last updated 12may2000 {dlb}
/* ***********************************************************************
* called from: item_use - spell - spells
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index e168b64b5e..15029f6f2c 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -744,6 +744,8 @@ enum conduct_type
DID_DESTROY_ORCISH_IDOL, // Beogh
DID_CREATE_LIFE, // unused
DID_KILL_SLIME, // Jiyva
+ DID_KILL_PLANT, // Feawn
+ DID_ALLY_KILLED_PLANT, // Feawn
NUM_CONDUCTS
};
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 78eb8cfc89..d2db0cafae 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -2749,7 +2749,8 @@ bool need_auto_exclude(const monsters *mon, bool sleepy)
for (unsigned i = 0; i < Options.auto_exclude.size(); ++i)
if (Options.auto_exclude[i].matches(name)
- && _mon_needs_auto_exclude(mon, sleepy))
+ && _mon_needs_auto_exclude(mon, sleepy)
+ && mon->attitude == ATT_HOSTILE)
{
return (true);
}
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 9e5d7de673..5115102bc6 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -629,11 +629,31 @@ bool mons_is_icy(int mc)
}
// Monsters considered as "slime" for Jiyva.
+bool mons_class_is_slime(int mc)
+{
+ return (mons_genus(mc) == MONS_JELLY
+ || mons_genus(mc) == MONS_GIANT_EYEBALL
+ || mons_genus(mc) == MONS_GIANT_ORANGE_BRAIN);
+}
+
bool mons_is_slime(const monsters *mon)
{
- return (mons_genus(mon->type) == MONS_JELLY
- || mons_genus(mon->type) == MONS_GIANT_EYEBALL
- || mons_genus(mon->type) == MONS_GIANT_ORANGE_BRAIN);
+ return (mons_class_is_slime(mon->type));
+}
+
+// Monsters Feawn cares about - plants and fungi except for giant spores
+// and toadstools.
+bool mons_class_is_plant(int mc)
+{
+ return (mc != MONS_GIANT_SPORE
+ && mc != MONS_TOADSTOOL
+ && (mons_genus(mc) == MONS_PLANT
+ || mons_genus(mc) == MONS_FUNGUS));
+}
+
+bool mons_is_plant(const monsters *mon)
+{
+ return (mons_class_is_plant(mon->type));
}
bool mons_eats_items(const monsters *mon)
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 3c745e37db..0576b2c956 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -785,7 +785,10 @@ bool mons_is_unholy(const monsters *mon);
bool mons_is_evil_or_unholy(const monsters *mon);
bool mons_is_icy(int mc);
bool mons_is_skeletal(int mc);
+bool mons_class_is_slime(int mc);
bool mons_is_slime(const monsters *mon);
+bool mons_class_is_plant(int mc);
+bool mons_is_plant(const monsters *mon);
bool mons_eats_items(const monsters *mon);
bool mons_has_lifeforce(const monsters *mon);
bool mons_eats_corpses(const monsters *m);
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 446c4f119b..01a8d55272 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -1456,6 +1456,12 @@ int monster_die(monsters *monster, killer_type killer,
true, monster);
}
+ if (mons_is_plant(monster))
+ {
+ did_god_conduct(DID_KILL_PLANT, monster->hit_dice,
+ true, monster);
+ }
+
if (mons_is_slime(monster))
{
did_god_conduct(DID_KILL_SLIME, monster->hit_dice,
@@ -1553,13 +1559,18 @@ int monster_die(monsters *monster, killer_type killer,
// No piety loss if god gifts killed by other monsters.
// Also, dancing weapons aren't really friendlies.
if (mons_friendly(monster)
- && !mons_is_god_gift(monster)
&& monster->type != MONS_DANCING_WEAPON)
{
did_god_conduct(DID_FRIEND_DIED, 1 + (monster->hit_dice / 2),
true, monster);
}
+ if (pet_kill && mons_is_plant(monster))
+ {
+ did_god_conduct(DID_ALLY_KILLED_PLANT, 1 + (monster->hit_dice / 2),
+ true, monster);
+ }
+
// Trying to prevent summoning abuse here, so we're trying to
// prevent summoned creatures from being done_good kills. Only
// affects creatures which were friendly when summoned.
@@ -2846,9 +2857,19 @@ void behaviour_event(monsters *mon, mon_event_type event, int src,
&& !mons_is_fleeing(mon) && !mons_is_panicking(mon)))
{
// Monster types that you can't gain experience from cannot
- // fight back.
- if (mons_class_flag(mon->type, M_NO_EXP_GAIN))
+ // fight back, so don't bother having them do so. If you
+ // worship Feawn, create a ring of friendly plants, and try
+ // to break out of the ring by killing a plant, you'll get
+ // a warning prompt and penance only once. Without the
+ // hostility check, the plant will remain friendly until it
+ // dies, and you'll get a warning prompt and penance once
+ // *per hit*. This may not be the best way to address the
+ // issue, though. -cao
+ if (mons_class_flag(mon->type, M_NO_EXP_GAIN)
+ && mon->attitude != ATT_FRIENDLY)
+ {
return;
+ }
mon->foe = src;
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index 4f9de2980e..412c11dcf6 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -522,7 +522,7 @@ std::string get_god_likes(god_type which_god, bool verbose)
break;
case GOD_FEAWN:
- snprintf(info,INFO_SIZE,"you promote decomposition%s",
+ snprintf(info,INFO_SIZE, "you promote decomposition%s",
verbose ? " via the <w>a</w> command" : "");
likes.push_back(info);
break;
@@ -798,9 +798,9 @@ std::string get_god_dislikes(god_type which_god, bool /*verbose*/)
break;
case GOD_FEAWN:
- dislikes.push_back("you destroy plants");
+ dislikes.push_back("you or your allies destroy plants");
dislikes.push_back("allied flora die");
- dislikes.push_back("you practice necromancy");
+ dislikes.push_back("you use necromancy");
break;
default:
@@ -1434,6 +1434,12 @@ bool is_fellow_slime(const monsters* mon)
&& mons_is_god_gift(mon, GOD_JIYVA));
}
+bool is_neutral_plant(const monsters* mon)
+{
+ return (mon->alive() && mons_is_plant(mon)
+ && mon->attitude == ATT_GOOD_NEUTRAL);
+}
+
bool _has_jelly()
{
ASSERT(you.religion == GOD_JIYVA);
@@ -1448,7 +1454,6 @@ bool _has_jelly()
return (false);
}
-
bool is_good_lawful_follower(const monsters* mon)
{
return (mon->alive() && !mons_is_evil_or_unholy(mon)
@@ -1469,6 +1474,8 @@ bool is_follower(const monsters* mon)
return is_orcish_follower(mon);
else if (you.religion == GOD_JIYVA)
return is_fellow_slime(mon);
+ else if (you.religion == GOD_FEAWN)
+ return is_neutral_plant(mon);
else if (you.religion == GOD_ZIN)
return is_good_lawful_follower(mon);
else if (is_good_god(you.religion))
@@ -2683,7 +2690,6 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known,
break;
case DID_NECROMANCY:
- case DID_UNHOLY:
if (you.religion == GOD_FEAWN)
{
if (known)
@@ -2694,13 +2700,14 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known,
}
else
{
- simple_god_message(" forgives your blasphemy, just this "
- "once.");
+ simple_god_message(" forgives your inadvertent necromancy, "
+ "just this once.");
}
break;
}
// else fall-through
+ case DID_UNHOLY:
case DID_ATTACK_HOLY:
switch (you.religion)
{
@@ -2778,6 +2785,16 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known,
}
break;
+ case DID_KILL_PLANT:
+ case DID_ALLY_KILLED_PLANT:
+ // Piety loss but no penance for killing a plant.
+ if (you.religion == GOD_FEAWN)
+ {
+ retval = true;
+ piety_change = -level;
+ }
+ break;
+
case DID_ATTACK_NEUTRAL:
switch (you.religion)
{
@@ -2791,6 +2808,7 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known,
}
penance = level/2 + 1;
// deliberate fall through
+
case GOD_ZIN:
if (!known)
{
@@ -2837,16 +2855,30 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known,
switch (you.religion)
{
case GOD_ELYVILON: // healer god cares more about this
+ // Converted allies (marked as TSOites) can be martyrs.
+ if (victim->god == GOD_SHINING_ONE)
+ break;
+
if (player_under_penance())
penance = 1; // if already under penance smaller bonus
else
penance = level;
// fall through
- case GOD_ZIN: // in contrast to TSO, who doesn't mind martyrs
+
+ case GOD_ZIN:
+ // Converted allies (marked as TSOites) can be martyrs.
+ if (victim->god == GOD_SHINING_ONE)
+ break;
+
+ case GOD_FEAWN: // plant god only cares about plants
+ if (!mons_is_plant(victim))
+ break;
+
case GOD_OKAWARU:
piety_change = -level;
retval = true;
break;
+
default:
break;
}
@@ -5275,7 +5307,110 @@ static bool _jiyva_retribution()
static bool _feawn_retribution()
{
- // to be implemented -CAO
+ const god_type god = GOD_FEAWN;
+
+ // We have 3 forms of retribution, but players under penance will be
+ // spared the 'you are now surrounded by oklob plants, please die' one.
+ const int retribution_options = you.religion == GOD_FEAWN ? 2 : 3;
+
+ switch (random2(retribution_options))
+ {
+ case 0:
+ // Try and spawn some hostile giant spores, if none are created
+ // fall through to the elemental miscast effects.
+ if (corpse_spores(BEH_HOSTILE))
+ {
+ simple_god_message(" produces spores.", GOD_FEAWN);
+ break;
+ }
+ case 1:
+ {
+ // Elemental miscast effects.
+ simple_god_message(" invokes the elements against you.", GOD_FEAWN);
+
+ spschool_flag_type stype = SPTYP_NONE;
+ switch (random2(4))
+ {
+ case 0:
+ stype= SPTYP_ICE;
+ break;
+ case 1:
+ stype = SPTYP_EARTH;
+ break;
+ case 2:
+ stype = SPTYP_FIRE;
+ break;
+ case 3:
+ stype = SPTYP_AIR;
+ break;
+ };
+ MiscastEffect(&you, -god, stype, 5 + you.experience_level,
+ random2avg(88, 3), "the wrath of Feawn");
+ break;
+ }
+ case 2:
+ // We are going to spawn some oklobs but first we need to find
+ // out a little about the situation.
+ std::vector<std::vector<coord_def> > radius_points;
+ collect_radius_points(radius_points,you.pos(),env.no_trans_show);
+
+ unsigned free_thresh = 30;
+
+ mgen_data temp(MONS_OKLOB_PLANT,
+ BEH_HOSTILE, 0, 0,
+ coord_def(),
+ MHITNOT,
+ MG_FORCE_PLACE,
+ GOD_FEAWN);
+
+ // If we have a lot of space to work with (the circle with
+ // radius 6 is substantially unoccupied), we can do something
+ // flashy.
+ if (radius_points[5].size() > free_thresh)
+ {
+ int seen_count;
+
+ temp.cls = MONS_PLANT;
+
+ place_ring(radius_points[0],
+ you.pos(),
+ temp,
+ 1, radius_points[0].size(),
+ seen_count);
+
+ temp.cls = MONS_OKLOB_PLANT;
+
+ place_ring(radius_points[5],
+ you.pos(),
+ temp,
+ random_range(3, 8), 1,
+ seen_count);
+ }
+ // Otherwise we do something with the nearest neighbors
+ // (assuming the player isn't already surrounded).
+ else if (!radius_points[0].empty())
+ {
+ unsigned target_count = random_range(2, 8);
+ if (target_count < radius_points[0].size())
+ prioritise_adjacent(you.pos(), radius_points[0]);
+ else
+ target_count = radius_points[0].size();
+
+ unsigned i = radius_points[0].size() - target_count;
+
+ for(; i < radius_points[0].size(); ++i)
+ {
+ temp.pos = radius_points[0].at(i);
+ temp.cls = coinflip() ?
+ MONS_WANDERING_MUSHROOM : MONS_OKLOB_PLANT;
+
+ create_monster(temp,false);
+ }
+
+ god_speaks(god, "Plants grow around you in an ominous manner.");
+ return (false);
+ }
+ }
return (true);
}
@@ -5400,12 +5535,12 @@ static bool _holy_beings_on_level_attitude_change()
}
}
- return success;
+ return (success);
}
static bool _holy_beings_attitude_change()
{
- return apply_to_all_dungeons(_holy_beings_on_level_attitude_change);
+ return (apply_to_all_dungeons(_holy_beings_on_level_attitude_change));
}
static bool _evil_beings_on_level_attitude_change()
@@ -5440,12 +5575,12 @@ static bool _evil_beings_on_level_attitude_change()
}
}
- return success;
+ return (success);
}
static bool _evil_beings_attitude_change()
{
- return apply_to_all_dungeons(_evil_beings_on_level_attitude_change);
+ return (apply_to_all_dungeons(_evil_beings_on_level_attitude_change));
}
static bool _chaotic_beings_on_level_attitude_change()
@@ -5479,12 +5614,12 @@ static bool _chaotic_beings_on_level_attitude_change()
}
}
- return success;
+ return (success);
}
static bool _chaotic_beings_attitude_change()
{
- return apply_to_all_dungeons(_chaotic_beings_on_level_attitude_change);
+ return (apply_to_all_dungeons(_chaotic_beings_on_level_attitude_change));
}
static bool _magic_users_on_level_attitude_change()
@@ -5518,12 +5653,12 @@ static bool _magic_users_on_level_attitude_change()
}
}
- return success;
+ return (success);
}
static bool _magic_users_attitude_change()
{
- return apply_to_all_dungeons(_magic_users_on_level_attitude_change);
+ return (apply_to_all_dungeons(_magic_users_on_level_attitude_change));
}
// Make summoned (temporary) god gifts disappear on penance or when
@@ -5865,6 +6000,70 @@ static bool _yred_slaves_abandon_you()
return (false);
}
+static bool _feawn_plants_on_level_hostile()
+{
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *monster = &menv[i];
+ if (monster->alive()
+ && mons_is_plant(monster))
+ {
+#ifdef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_DIAGNOSTICS, "Plant hostility: %s on level %d, branch %d",
+ monster->name(DESC_PLAIN).c_str(),
+ static_cast<int>(you.your_level),
+ static_cast<int>(you.where_are_you));
+#endif
+
+ // You can potentially turn an oklob or whatever neutral
+ // again by going back to Feawn.
+ if (testbits(monster->flags, MF_ATT_CHANGE_ATTEMPT))
+ monster->flags &= ~MF_ATT_CHANGE_ATTEMPT;
+
+ monster->attitude = ATT_HOSTILE;
+ monster->del_ench(ENCH_CHARM, true);
+ behaviour_event(monster, ME_ALERT, MHITYOU);
+ // For now WAS_NEUTRAL stays.
+ }
+ }
+
+ return (true);
+}
+
+static bool _feawn_plants_hostile()
+{
+ if (apply_to_all_dungeons(_feawn_plants_on_level_hostile))
+ {
+ mpr("The plants of the dungeon turn on you!", MSGCH_GOD);
+ return (true);
+ }
+
+ return (false);
+}
+
+static bool _feawn_plants_on_level_neutral()
+{
+ for (int i = 0; i < MAX_MONSTERS; ++i)
+ {
+ monsters *monster = &menv[i];
+
+ feawn_neutralise_plant(monster);
+ }
+
+ return (true);
+}
+
+static bool _feawn_plants_neutral()
+{
+ if (apply_to_all_dungeons(_feawn_plants_on_level_neutral))
+ {
+ mpr("The plants of the dungeon cease their hostilities." , MSGCH_GOD);
+ return (true);
+ }
+
+ return (false);
+}
+
// Upon excommunication, ex-Beoghites lose all their orcish followers,
// plus all monsters created by their priestly orcish followers. When
// under penance, Beoghites can lose all orcish followers in sight,
@@ -6285,6 +6484,25 @@ void beogh_convert_orc(monsters *orc, bool emergency,
behaviour_event(orc, ME_ALERT, MHITNOT);
}
+void feawn_neutralise_plant(monsters *plant)
+{
+ if (plant->type != MONS_OKLOB_PLANT
+ && plant->type != MONS_WANDERING_MUSHROOM
+ && !testbits(plant->flags, MF_ATT_CHANGE_ATTEMPT))
+ {
+ return;
+ }
+
+ if (you.can_see(plant))
+ {
+ mprf(MSGCH_GOD, "%s ignores you.",
+ plant->name(DESC_CAP_THE).c_str());
+ }
+
+ plant->attitude = ATT_GOOD_NEUTRAL;
+ plant->flags |= MF_WAS_NEUTRAL;
+}
+
void jiyva_convert_slime(monsters* slime)
{
ASSERT(mons_is_slime(slime));
@@ -6303,9 +6521,9 @@ void jiyva_convert_slime(monsters* slime)
slime->name(DESC_CAP_THE).c_str());
}
}
+
slime->attitude = ATT_STRICT_NEUTRAL;
slime->flags |= MF_WAS_NEUTRAL;
- slime->god = GOD_JIYVA;
if (mons_itemuse(slime) != MONUSE_EATS_ITEMS)
{
@@ -6314,6 +6532,11 @@ void jiyva_convert_slime(monsters* slime)
slime->name(DESC_CAP_THE).c_str());
}
+ // Prevent assertion if the slime was previously worshipping a
+ // different god, rather than already worshipping Jiyva or being an
+ // atheist.
+ slime->god = GOD_NO_GOD;
+
mons_make_god_gift(slime, GOD_JIYVA);
}
@@ -6501,6 +6724,11 @@ void excommunication(god_type new_god)
_inc_penance(old_god, 30);
break;
+ case GOD_FEAWN:
+ _feawn_plants_hostile();
+ _inc_penance(old_god, 30);
+ divine_retribution(old_god);
+ break;
default:
_inc_penance(old_god, 25);
@@ -6712,11 +6940,9 @@ bool god_hates_attacking_friend(god_type god, int species)
case GOD_BEOGH: // added penance to avoid killings for loot
return (mons_genus(species) == MONS_ORC);
case GOD_JIYVA:
- return (mons_genus(species) == MONS_JELLY
- || mons_genus(species) == MONS_GIANT_EYEBALL
- || species == MONS_GIANT_SPORE
- || species == MONS_GIANT_ORANGE_BRAIN);
-
+ return (mons_class_is_slime(species));
+ case GOD_FEAWN:
+ return (mons_class_is_plant(species));
default:
return (false);
}
@@ -7071,6 +7297,10 @@ bool player_can_join_god(god_type which_god)
if (player_is_unholy() && is_good_god(which_god))
return (false);
+ // Feawn hates undead, but will accept demonspawn.
+ if (which_god == GOD_FEAWN && you.holiness() == MH_UNDEAD)
+ return (false);
+
if (which_god == GOD_BEOGH && you.species != SP_HILL_ORC)
return (false);
@@ -7183,6 +7413,12 @@ void god_pitch(god_type which_god)
mpr("You can now call upon Trog to burn spellbooks in your "
"surroundings.", MSGCH_GOD);
}
+ else if (you.religion == GOD_FEAWN)
+ {
+ mpr("You can call upon Feawn to speed up the decay of corpses.",
+ MSGCH_GOD);
+ _feawn_plants_neutral();
+ }
if (you.worshipped[you.religion] < 100)
you.worshipped[you.religion]++;
@@ -7293,7 +7529,10 @@ bool god_hates_killing(god_type god, const monsters* mon)
break;
}
- return retval;
+ if (god == GOD_FEAWN)
+ retval = (mons_is_plant(mon));
+
+ return (retval);
}
bool god_likes_butchery(god_type god)
diff --git a/crawl-ref/source/religion.h b/crawl-ref/source/religion.h
index d107bdf8ee..734c5840d8 100644
--- a/crawl-ref/source/religion.h
+++ b/crawl-ref/source/religion.h
@@ -115,6 +115,7 @@ void yred_make_enslaved_soul(monsters *mon, bool force_hostile = false,
void beogh_convert_orc(monsters *orc, bool emergency,
bool converted_by_follower = false);
void jiyva_convert_slime(monsters* slime);
+void feawn_neutralise_plant(monsters *plant);
bool is_holy_item(const item_def& item);
bool is_evil_item(const item_def& item);
bool is_chaotic_item(const item_def& item);
@@ -157,6 +158,7 @@ bool is_undead_slave(const monsters* mon);
bool is_yred_undead_slave(const monsters* mon);
bool is_orcish_follower(const monsters* mon);
bool is_fellow_slime(const monsters* mon);
+bool is_neutral_plant(const monsters* mon);
bool is_good_lawful_follower(const monsters* mon);
bool is_good_follower(const monsters* mon);
bool is_follower(const monsters* mon);
diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc
index 6d65a93ba9..2a0de0becd 100644
--- a/crawl-ref/source/spells2.cc
+++ b/crawl-ref/source/spells2.cc
@@ -2038,7 +2038,7 @@ void point_point(std::vector<coord_def> & origins,
// to calculate the distances in question. In practice it should be called for
// at most 7 searches since 8 (all adjacent free, > 8 monsters in view) can be
// special cased easily.
-bool prioritize_adjacent(coord_def & target, std::vector<coord_def> & candidates)
+bool prioritise_adjacent(coord_def & target, std::vector<coord_def> & candidates)
{
radius_iterator los_it(target, LOS_RADIUS, true, true, true);
@@ -2052,8 +2052,6 @@ bool prioritize_adjacent(coord_def & target, std::vector<coord_def> & candidates
mons_positions.push_back(hostile->pos());
}
- mprf("found %d hostiles", mons_positions.size());
-
if (mons_positions.empty())
{
std::random_shuffle(candidates.begin(), candidates.end());
@@ -2125,9 +2123,7 @@ bool plant_ring_from_fruit()
if ((int) adjacent.size() > possible_count)
{
- prioritize_adjacent(you.pos(), adjacent);
-
- //::update_screen();
+ prioritise_adjacent(you.pos(), adjacent);
}
unsigned target_count =
@@ -2224,8 +2220,13 @@ int rain(coord_def & target)
return (spawned_count);
}
-void corpse_spores()
+// Destroy corpses in the player's LOS (first corpse on a stack only)
+// and make 1 giant spore per corpse. Spores are given the input as
+// their starting behavior; the function returns the number of corpses
+// processed.
+int corpse_spores(beh_type behavior)
{
+ int count = 0;
for (radius_iterator rad(you.pos(), LOS_RADIUS, true, true, true); rad;
++rad)
{
@@ -2234,9 +2235,10 @@ void corpse_spores()
if (stack_it->base_type == OBJ_CORPSES
&& stack_it->sub_type == CORPSE_BODY)
{
+ count++;
create_monster(mgen_data(MONS_GIANT_SPORE,
- BEH_FRIENDLY,
+ behavior,
0,
0,
*rad,
@@ -2253,6 +2255,8 @@ void corpse_spores()
}
}
}
+
+ return (count);
}
typedef std::pair<monsters *, int> monster_cost;
@@ -2343,12 +2347,6 @@ bool evolve_flora()
points -= current_target.second;
int base_species = current_plant->mons_species();
- coord_def target_square = current_plant->pos();
-
- // Remove the original plant.
- // XXX: Why do we destroy the old and create a new plant
- // rather than simply upgrade the old plant?
- monster_die(current_plant, KILL_MISC, NON_MONSTER, true);
monster_type new_species = MONS_PLANT;
switch (base_species)
@@ -2369,9 +2367,11 @@ bool evolve_flora()
break;
};
- rc = create_monster(mgen_data(new_species,
- BEH_FRIENDLY, 0, 0, target_square,
- MHITNOT, MG_FORCE_PLACE, GOD_FEAWN));
+ current_plant->upgrade_type(new_species, true, true);
+
+ current_plant->god = GOD_FEAWN;
+ current_plant->attitude = ATT_FRIENDLY;
+ current_plant->flags |= MF_CREATED_FRIENDLY;
// We can potentially upgrade toadstools a second time.
if (base_species == MONS_TOADSTOOL && rc != -1)
diff --git a/crawl-ref/source/spells3.cc b/crawl-ref/source/spells3.cc
index 143c9555a0..6499232913 100644
--- a/crawl-ref/source/spells3.cc
+++ b/crawl-ref/source/spells3.cc
@@ -1971,6 +1971,9 @@ bool recall(char type_recalled)
if (!mons_friendly(monster))
continue;
+ if (mons_class_is_stationary(monster->type))
+ continue;
+
if (!monster_habitable_grid(monster, DNGN_FLOOR))
continue;
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index 3fb2de043f..7b8b544c21 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -993,6 +993,29 @@ void slime_convert(monsters* monster)
}
}
+void feawn_neutralise(monsters* monster)
+{
+ if (you.religion == GOD_FEAWN && mons_is_plant(monster)
+ && !mons_is_summoned(monster)
+ && !mons_wont_attack(monster)
+ && !testbits(monster->flags, MF_ATT_CHANGE_ATTEMPT))
+ {
+ if (!player_under_penance())
+ {
+ // We must call remove_auto_exclude before neutralizing the
+ // plant because remove_auto_exclude only removes exclusions
+ // it thinks were caused by auto-exclude, and
+ // auto-exclusions now check for ATT_HOSTILE. Oh, what a
+ // tangled web, etc.
+ remove_auto_exclude(monster, false);
+
+ monster->flags |= MF_ATT_CHANGE_ATTEMPT;
+ feawn_neutralise_plant(monster);
+ stop_running();
+ }
+ }
+}
+
void handle_seen_interrupt(monsters* monster)
{
if (mons_is_unknown_mimic(monster))
@@ -1341,6 +1364,7 @@ void monster_grid(bool do_updates)
_good_god_follower_attitude_change(monster);
beogh_follower_convert(monster);
slime_convert(monster);
+ feawn_neutralise(monster);
}
}
}