summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/effects.cc
diff options
context:
space:
mode:
authorj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2009-07-20 18:07:51 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2009-07-20 18:07:51 +0000
commit3a83e92a99ce7a161f36e0bd2f1412580ef9bca0 (patch)
treefd7ccbbc3c039f7dac530a0822797913e6dbb7cc /crawl-ref/source/effects.cc
parent0bcbd855b8b1b0a9291aa6b78f2785ffc9987dce (diff)
downloadcrawl-ref-3a83e92a99ce7a161f36e0bd2f1412580ef9bca0.tar.gz
crawl-ref-3a83e92a99ce7a161f36e0bd2f1412580ef9bca0.zip
Apply caotto's plant god patch, for now named "Feawn".
Also fix potions of porridge's menu colour being yellow for Mummies. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10355 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/effects.cc')
-rw-r--r--crawl-ref/source/effects.cc366
1 files changed, 295 insertions, 71 deletions
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 7675562ce3..ef1bfcb3c2 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -1991,7 +1991,9 @@ static void _set_friendly_foes(bool allow_patrol = false)
for (int i = 0; i < MAX_MONSTERS; ++i)
{
monsters *mon(&menv[i]);
- if (!mon->alive() || !mons_near(mon) || !mons_friendly_real(mon))
+ if (!mon->alive() || !mons_near(mon) || !mons_friendly_real(mon)
+ || mon->has_ench(ENCH_BERSERK)
+ || mon->mons_species() == MONS_GIANT_SPORE)
continue;
// Berserking monsters cannot be ordered around.
@@ -3798,37 +3800,211 @@ static void _maybe_restart_fountain_flow(const coord_def& where,
}
}
-// Spawn a ring of mushrooms around the input corpse (radius=1).
-// Could try different radii/check for largest available but doesn't right now.
-// Maybe there should be a message for this.
-static int _mushroom_ring(item_def &corpse)
+
+// A comparison struct for use in an stl priority queue.
+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.
+ bool operator()(const T & left, const T & right)
+ {
+ 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)
+{
+ float theta = atan2(pos.y, pos.x);
+
+ if(pos.x == 0 && pos.y != 0)
+ theta = pos.y > 0 ? PI / 2 : -PI / 2;
+
+ if(theta < 0)
+ theta += 2*PI;
+
+ float arc_angle = 2*PI / n_arcs;
+
+ theta += arc_angle / 2.0f;
+
+ 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)
{
- ::adjacent_iterator adj(corpse.pos);
- int spawned_count=0;
- for ( ; adj; ++adj)
+ // minimum number of mushrooms spawned on a given ring
+ unsigned min_spawn = 2;
+
+ // highest radius we will allow. 8 is LOS
+ const int max_radius = LOS_RADIUS;
+
+ seen_count = 0;
+
+ // 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);
+ std::priority_queue<coord_dist,
+ std::vector<coord_dist>,
+ greater_second<coord_dist> > fringe;
+
+ coord_dist origin(corpse.pos, 0);
+
+ fringe.push(origin);
+
+ std::set<int> visited_indices;
+
+ int max_distance = max_radius * max_radius - 1;
+
+ std::vector<coord_def> radius_points[max_radius];
+
+ int max_visited = 0;
+ while (!fringe.empty())
{
- if (mons_class_can_pass(MONS_TOADSTOOL, grd(*adj))
- && !actor_at(*adj))
+ 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
+ // origin than our maximum permissible radius.
+ if(current.second > max_distance)
+ break;
+
+ if(current.second > max_visited)
+ max_visited = current.second;
+
+ float current_distance =current.second ? sqrtf(current.second + 1) : 0;
+
+ int bound = static_cast<int> (current_distance + 0.5);
+
+ // 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))
{
- const int mushroom = create_monster(
- mgen_data(MONS_TOADSTOOL,
- BEH_HOSTILE,
- 0,
- 0,
- *adj,
- MHITNOT,
- MG_FORCE_PLACE,
- GOD_NO_GOD,
- MONS_PROGRAM_BUG,
- 0,
- corpse.colour),
- false);
+ radius_points[bound - 1].push_back(current.first);
+ }
- if (mushroom != -1)
- spawned_count++;
+ for (adjacent_iterator i(current.first); i; ++i)
+ {
+ coord_dist temp(*i, current.second);
+
+ coord_def local = temp.first - origin.first;
+
+ temp.second = local.x * local.x + local.y * local.y - 1;
+
+ // 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;
+
+ 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)))
+ fringe.push(temp);
+ }
+
+ }
+
+ // 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(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++)
+ {
+ chosen_idx = chosen_idx % max_radius;
+
+ if(chosen_idx >= min_idx
+ && radius_points[chosen_idx].size() >= min_spawn)
+ break;
+ }
+
+ // Couldn't find enough valid points at any radius?
+ if(radius_points[chosen_idx].size() < min_spawn)
+ return 0;
+
+ std::random_shuffle(radius_points[chosen_idx].begin(),
+ radius_points[chosen_idx].end());
+
+ int target_amount = radius_points[chosen_idx].size();
+
+ int spawned_count = 0;
+
+ float target_arc_len=2*sqrtf(2.0f);
+ //float target_arc_len = 2;
+
+ 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;
+
+ arc_counts[direction]--;
+
+ 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;
}
@@ -3837,14 +4013,17 @@ static int _mushroom_ring(item_def &corpse)
// 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 = 1)
+int spawn_corpse_mushrooms(item_def &corpse,
+ int target_count,
+ int & seen_targets,
+ bool distance_as_time)
{
+ seen_targets = 0;
if (target_count == 0)
return 0;
- int x_offset[] = {-1,-1,-1, 0, 0, 1, 1, 1};
- int y_offset[] = {-1, 0, 1,-1, 1,-1, 0, 1};
-
+ int c_size=8;
+ int permutation[] = {0, 1, 2, 3, 4, 5, 6, 7};
int placed_targets = 0;
@@ -3855,16 +4034,29 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1)
// skeletonizing it) if the corpse square is unoccupied.
if (!actor_at(corpse.pos) && one_chance_in(100))
{
- if (see_grid(corpse.pos))
- mpr("A ring of toadstools grow before your very eyes.");
+ 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);
- corpse.special = 0;
- return _mushroom_ring(corpse);
+ if(res)
+ {
+ 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)
+ mpr("A toadstool grows.");
+
+ seen_targets = -1;
+
+
+ return res;
+ }
}
- // Can't figure out how to query the size of the xdim of the grid but who
- // cares.
- visited_indices.insert(10000*corpse.pos.y + corpse.pos.x);
+ visited_indices.insert(X_WIDTH*corpse.pos.y + corpse.pos.x);
fringe.push(corpse.pos);
while (!fringe.empty())
@@ -3899,13 +4091,30 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1)
if (mushroom != -1)
{
+ // Going to expliclty override the die-off timer in this case
+ // (this condition means we got called from fungal_bloom or
+ // similar and are creating a lot of toadstools at once that
+ // should die off quickly).
+ if(distance_as_time)
+ {
+ coord_def offset = corpse.pos - current;
+
+ int dist = static_cast<int> (sqrtf(offset.abs()) + 0.5);
+
+ int time_left = random2(8) + dist * 8 + 1;
+
+ time_left *= 10;
+
+ mon_enchant temp_en(ENCH_SLOWLY_DYING, 1, KC_OTHER,
+ time_left);
+
+ env.mons[mushroom].update_ench(temp_en);
+ }
+
placed_targets++;
if (see_grid(current))
{
- if (see_grid(corpse.pos))
- mpr("A toadstool grows from a nearby corpse.");
- else
- mpr("A toadstool springs up from the ground.");
+ seen_targets++;
}
}
else
@@ -3916,16 +4125,14 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1)
if (placed_targets == target_count)
break;
- // Randomize the order in which children are processed to a certain
- // extent.
- int idx = rand() % 8;
+ // wish adjacent_iterator had a random traversal
+ std::random_shuffle(permutation, permutation+c_size);
- for (int count = 0; count < 8; ++count)
+ for (int count = 0; count < c_size; ++count)
{
- idx= (idx + 1) % 8;
- coord_def temp(current.x + x_offset[idx], current.y + y_offset[idx]);
+ coord_def temp=current + Compass[permutation[count]];
- int index = temp.x + temp.y * 10000;
+ int index = temp.x + temp.y * X_WIDTH;
if (visited_indices.find(index) == visited_indices.end()
&& in_bounds(temp)
@@ -3941,6 +4148,34 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1)
return placed_targets;
}
+int mushroom_prob(item_def & corpse)
+{
+ int low_threshold = 5;
+ int high_threshold = FRESHEST_CORPSE - 5;
+
+ // Expect this many trials over a corpse's lifetime since this function
+ // is called once for every 10 units of rot_time.
+ 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;
+
+ 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
+ // base factor here.
+ float weight_factor = item_mass(corpse)/550.0f;
+
+ trial_prob_f *= weight_factor;
+
+ int trial_prob = static_cast<int> (100* trial_prob_f );
+
+ 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.
@@ -3961,37 +4196,26 @@ static void _maybe_spawn_mushroom(item_def & corpse, int rot_time)
if (spawn_time > high_threshold)
spawn_time = high_threshold;
- // So we're going to spawn one or more mushrooms over the lifetime of a
- // corpse here. For convenience we follow a binomial distribution. I think
- // the most useful analysis is in terms of the probability of a corpse
- // producing no mushrooms, although trial probability should just be hard
- // coded once a value is agreed on.
-
-
- // Expect this many trials over a corpse's lifetime since this function
- // is called once for every 10 units of rot_time.
int step_size = 10;
- float total_trials = (high_threshold - low_threshold) / step_size;
- // chance of producing no mushrooms
- float p_failure = .5;
+ int current_trials = spawn_time / step_size;
- float trial_prob_f = 1 - powf(p_failure,1.0f/total_trials);
+ int trial_prob = mushroom_prob(corpse);
- // The chance of producing mushrooms depends on the weight of the
- // corpse involved. Humans weigh 550 so we will take that as the
- // base factor here.
- float weight_factor = item_mass(corpse)/550.0f;
-
- trial_prob_f *= weight_factor;
-
- int trial_prob = static_cast<int>(100*trial_prob_f);
+ int success_count = binomial_generator(current_trials, trial_prob);
- int current_trials = spawn_time/step_size;
+ int seen_spawns;
+ spawn_corpse_mushrooms(corpse, success_count, seen_spawns);
- int success_count = binomial_generator(current_trials, trial_prob);
+ if(seen_spawns > 0)
+ {
+ std::string base = seen_spawns > 1 ? "Some toadstools" : "A toadstool";
- spawn_corpse_mushrooms(corpse, success_count);
+ if(see_grid(corpse.pos))
+ mprf("%s grows from a nearby corpse.", base.c_str());
+ else
+ mprf("%s springs up from the ground.", base.c_str());
+ }
}
//---------------------------------------------------------------