summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCharles Otto <ottochar@gmail.com>2009-11-06 20:46:30 -0500
committerCharles Otto <ottochar@gmail.com>2009-11-06 21:32:10 -0500
commiteef51d7a1ec7ce7e471d76f82154d2b147d6228b (patch)
tree562312f79c96587659e0f23bafff59dffcdb33f9
parent5ba6135921dfa9b9359466e40a2caec431d2cc58 (diff)
downloadcrawl-ref-eef51d7a1ec7ce7e471d76f82154d2b147d6228b.tar.gz
crawl-ref-eef51d7a1ec7ce7e471d76f82154d2b147d6228b.zip
Move Feawn's abilities to godabil.h/cc
-rw-r--r--crawl-ref/source/godabil.cc870
-rw-r--r--crawl-ref/source/godabil.h8
-rw-r--r--crawl-ref/source/godwrath.cc1
-rw-r--r--crawl-ref/source/spells2.cc860
-rw-r--r--crawl-ref/source/spells2.h13
5 files changed, 879 insertions, 873 deletions
diff --git a/crawl-ref/source/godabil.cc b/crawl-ref/source/godabil.cc
index eaaa52e861..b176f06005 100644
--- a/crawl-ref/source/godabil.cc
+++ b/crawl-ref/source/godabil.cc
@@ -5,6 +5,9 @@
#include "AppHdr.h"
+#include <queue>
+
+#include "beam.h"
#include "cloud.h"
#include "colour.h"
#include "coord.h"
@@ -20,6 +23,7 @@
#include "misc.h"
#include "mon-act.h"
#include "mon-behv.h"
+#include "monplace.h"
#include "monstuff.h"
#include "mon-util.h"
#include "mutation.h"
@@ -319,6 +323,872 @@ bool feawn_passthrough(const monsters * target)
|| target->attitude != ATT_HOSTILE));
}
+
+// Turns corpses in LOS into skeletons and grows toadstools on them.
+// Can also turn zombies into skeletons and destroy ghoul-type monsters.
+// Returns the number of corpses consumed.
+int fungal_bloom()
+{
+ int seen_mushrooms = 0;
+ int seen_corpses = 0;
+
+ int processed_count = 0;
+ bool kills = false;
+
+ for (radius_iterator i(you.pos(), LOS_RADIUS); i; ++i)
+ {
+ actor *target = actor_at(*i);
+ if (target && (target->atype() == ACT_PLAYER
+ || target->is_summoned()))
+ {
+ continue;
+ }
+
+ monsters * mons = monster_at(*i);
+
+ if (mons && mons->mons_species() != MONS_TOADSTOOL)
+ {
+ switch (mons_genus(mons->mons_species()))
+ {
+ case MONS_ZOMBIE_SMALL:
+ // Maybe turn a zombie into a skeleton.
+ if (mons_skeleton(mons_zombie_base(mons)))
+ {
+ processed_count++;
+
+ monster_type skele_type = MONS_SKELETON_LARGE;
+ if (mons_zombie_size(mons_zombie_base(mons)) == Z_SMALL)
+ skele_type = MONS_SKELETON_SMALL;
+
+ simple_monster_message(mons, "'s flesh rots away.");
+
+ mons->upgrade_type(skele_type, true, true);
+ behaviour_event(mons, ME_ALERT, MHITYOU);
+
+ continue;
+ }
+ // Else fall through and destroy the zombie.
+ // Ghoul-type monsters are always destroyed.
+ case MONS_GHOUL:
+ {
+ simple_monster_message(mons, " rots away and dies.");
+
+ coord_def pos = mons->pos();
+ int colour = mons->colour;
+ int corpse = monster_die(mons, KILL_MISC, NON_MONSTER, true);
+ kills = true;
+
+ // If a corpse didn't drop, create a toadstool.
+ // If one did drop, we will create toadstools from it as usual
+ // later on.
+ if (corpse < 0)
+ {
+ const int mushroom = create_monster(
+ mgen_data(MONS_TOADSTOOL,
+ BEH_FRIENDLY,
+ 0,
+ 0,
+ pos,
+ MHITNOT,
+ MG_FORCE_PLACE,
+ GOD_NO_GOD,
+ MONS_NO_MONSTER,
+ 0,
+ colour),
+ false);
+
+ if (mushroom != -1)
+ seen_mushrooms++;
+
+ processed_count++;
+
+ continue;
+ }
+ break;
+ }
+
+ default:
+ continue;
+ }
+ }
+
+ for (stack_iterator j(*i); j; ++j)
+ {
+ bool corpse_on_pos = false;
+ if (j->base_type == OBJ_CORPSES && j->sub_type == CORPSE_BODY)
+ {
+ corpse_on_pos = true;
+ int trial_prob = mushroom_prob(*j);
+
+ processed_count++;
+ int target_count = 1 + binomial_generator(20, trial_prob);
+
+ int seen_per;
+ spawn_corpse_mushrooms(*j, target_count, seen_per,
+ BEH_FRIENDLY, true);
+
+ seen_mushrooms += seen_per;
+
+ // Either turn this corpse into a skeleton or destroy it.
+ if (mons_skeleton(j->plus))
+ turn_corpse_into_skeleton(*j);
+ else
+ destroy_item(j->index());
+ }
+
+ if (corpse_on_pos && observe_cell(*i))
+ seen_corpses++;
+ }
+ }
+
+ if (seen_mushrooms > 0)
+ {
+ mushroom_spawn_message(seen_mushrooms, seen_corpses);
+ }
+
+ if (kills)
+ mprf("That felt like a moral victory.");
+
+ return (processed_count);
+}
+
+static int _create_plant(coord_def & target)
+{
+ if (actor_at(target) || !mons_class_can_pass(MONS_PLANT, grd(target)))
+ return (0);
+
+ const int plant = create_monster(mgen_data
+ (MONS_PLANT,
+ BEH_FRIENDLY,
+ 0,
+ 0,
+ target,
+ MHITNOT,
+ MG_FORCE_PLACE, GOD_FEAWN));
+
+
+ if (plant != -1)
+ {
+ env.mons[plant].flags |= MF_ATT_CHANGE_ATTEMPT;
+ if (observe_cell(target))
+ mpr("A plant grows up from the ground.");
+ }
+
+
+ return (plant != -1);
+}
+
+bool sunlight()
+{
+ int c_size = 5;
+ int x_offset[] = {-1, 0, 0, 0, 1};
+ int y_offset[] = { 0,-1, 0, 1, 0};
+
+ dist spelld;
+
+ bolt temp_bolt;
+
+ temp_bolt.colour = YELLOW;
+ direction(spelld, DIR_TARGET, TARG_HOSTILE, LOS_RADIUS, false, false,
+ false, true, "Select sunlight destination", NULL,
+ true);
+
+ if (!spelld.isValid)
+ return (false);
+
+ coord_def base = spelld.target;
+
+ int evap_count = 0;
+ int plant_count = 0;
+
+ // FIXME: Uncomfortable level of code duplication here but the explosion
+ // code in bolt subjects the input radius to r*(r+1) for the threshold and
+ // since r is an integer we can never get just the 4-connected neighbours.
+ // Anyway the bolt code doesn't seem to be well set up to handle the
+ // 'occasional plant' gimmick.
+ for (int i = 0;i < c_size; ++i)
+ {
+ coord_def target = base;
+ target.x += x_offset[i];
+ target.y += y_offset[i];
+
+ if (!in_bounds(target) || feat_is_solid(grd(target)))
+ continue;
+
+ temp_bolt.explosion_draw_cell(target);
+
+ actor *victim = actor_at(target);
+
+ // If this is a water square we will evaporate it.
+ dungeon_feature_type ftype = grd(target);
+
+ switch (ftype)
+ {
+ case DNGN_SHALLOW_WATER:
+ ftype = DNGN_FLOOR;
+ break;
+
+ case DNGN_DEEP_WATER:
+ ftype = DNGN_SHALLOW_WATER;
+ break;
+
+ default:
+ break;
+ }
+
+ if (grd(target) != ftype)
+ {
+ grd(target) = ftype;
+ if (observe_cell(target))
+ evap_count++;
+ }
+
+ monsters *mons = monster_at(target);
+
+ // Pop submerged status.
+ if (mons && mons_habitat(mons) == HT_WATER)
+ {
+ mons->del_ench(ENCH_SUBMERGED);
+ if (ftype == DNGN_FLOOR)
+ mons->add_ench(mon_enchant(ENCH_AQUATIC_LAND, 0, KC_YOU));
+ }
+
+ if (victim)
+ {
+ if (!mons)
+ you.backlight();
+ else
+ backlight_monsters(target, 1, 0);
+ }
+ else if (one_chance_in(100)
+ && ftype >= DNGN_FLOOR_MIN
+ && ftype <= DNGN_FLOOR_MAX)
+ {
+ // Create a plant.
+ const int plant = create_monster(mgen_data(MONS_PLANT,
+ BEH_HOSTILE,
+ 0,
+ 0,
+ target,
+ MHITNOT,
+ MG_FORCE_PLACE,
+ GOD_FEAWN));
+
+ if (plant != -1 && observe_cell(target))
+ plant_count++;
+ }
+ }
+ // move the cursor out of the way (it looks weird ok)
+#ifndef USE_TILE
+ cgotoxy(base.x, base.y, GOTO_DNGN);
+#endif
+ delay(200);
+
+ if (plant_count)
+ {
+ mprf("%s grow%s in the sunlight.",
+ (plant_count > 1 ? "Some plants": "A plant"),
+ (plant_count > 1 ? "": "s"));
+ }
+
+ if (evap_count)
+ mprf("Some water evaporates in the bright sunlight.");
+
+ return (true);
+}
+
+template<typename T>
+bool less_second(const T & left, const T & right)
+{
+ return left.second < right.second;
+}
+
+typedef std::pair<coord_def, int> point_distance;
+
+// dfs starting at origin, find the distance from the origin to the targets
+// (not leaving LOS, not crossing monsters or solid walls) and store that in
+// distances
+static void _path_distance(coord_def & origin,
+ std::vector<coord_def> & targets,
+ std::vector<int> & distances)
+{
+ std::set<unsigned> exclusion;
+ std::queue<point_distance> fringe;
+ fringe.push(point_distance(origin,0));
+
+ int idx = origin.x + origin.y * X_WIDTH;
+ exclusion.insert(idx);
+
+ while (!fringe.empty())
+ {
+ point_distance current = fringe.front();
+ fringe.pop();
+
+ // did we hit a target?
+ for (unsigned i = 0; i < targets.size(); ++i)
+ {
+ if (current.first == targets[i])
+ {
+ distances[i] = current.second;
+ break;
+ }
+ }
+
+ for (adjacent_iterator adj_it(current.first); adj_it; ++adj_it)
+ {
+ idx = adj_it->x + adj_it->y * X_WIDTH;
+ if (you.see_cell(*adj_it)
+ && !feat_is_solid(env.grid(*adj_it))
+ && exclusion.insert(idx).second)
+ {
+ monsters * temp = monster_at(*adj_it);
+ if (!temp || (temp->attitude == ATT_HOSTILE
+ && temp->mons_species() != MONS_PLANT
+ && temp->mons_species() != MONS_TOADSTOOL
+ && temp->mons_species() != MONS_FUNGUS
+ && temp->mons_species() != MONS_BALLISTOMYCETE))
+ {
+ fringe.push(point_distance(*adj_it, current.second+1));
+ }
+ }
+ }
+ }
+}
+
+// so we are basically going to compute point to point distance between
+// the points of origin and the end points (origins and targets respecitvely)
+// We will return a vector consisting of the minimum distances along one
+// dimension of the distance matrix.
+static void _point_point_distance(std::vector<coord_def> & origins,
+ std::vector<coord_def> & targets,
+ bool origin_to_target,
+ std::vector<int> & distances)
+{
+
+ distances.clear();
+ // Consider a matrix where the points of origin form the rows and
+ // the target points form the column, we want to take the minimum along
+ // one of those dimensions.
+ if (origin_to_target)
+ distances.resize(origins.size(), INT_MAX);
+ else
+ distances.resize(targets.size(), INT_MAX);
+
+ std::vector<int> current_distances(targets.size(), 0);
+ for (unsigned i = 0; i < origins.size(); ++i)
+ {
+ for (unsigned j = 0; j < current_distances.size(); ++j)
+ current_distances[j] = INT_MAX;
+
+ _path_distance(origins[i], targets, current_distances);
+
+ // So we got the distance from a point of origin to one of the
+ // targets. What should we do with it?
+ if (origin_to_target)
+ {
+ // The minimum of current_distances is points(i)
+ int min_dist = current_distances[0];
+ for (unsigned j = 1; i < current_distances.size(); ++i)
+ if (current_distances[j] < min_dist)
+ min_dist = current_distances[j];
+
+ distances[i] = min_dist;
+ }
+ else
+ {
+ for (unsigned j = 0; j < targets.size(); ++j)
+ {
+ if (i == 0)
+ distances[j] = current_distances[j];
+ else if (current_distances[j] < distances[j])
+ distances[j] = current_distances[j];
+ }
+ }
+ }
+}
+
+// So the idea is we want to decide which adjacent tiles are in the most 'danger'
+// We claim danger is proportional to the minimum distances from the point to a
+// (hostile) monster. This function carries out at most 8 depth-first searches
+// 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 prioritise_adjacent(const coord_def &target, std::vector<coord_def> & candidates)
+{
+ radius_iterator los_it(target, LOS_RADIUS, true, true, true);
+
+ std::vector<coord_def> mons_positions;
+ // collect hostile monster positions in LOS
+ for ( ; los_it; ++los_it)
+ {
+ monsters *hostile = monster_at(*los_it);
+
+ if (hostile && hostile->attitude == ATT_HOSTILE)
+ mons_positions.push_back(hostile->pos());
+ }
+
+ if (mons_positions.empty())
+ {
+ std::random_shuffle(candidates.begin(), candidates.end());
+ return (true);
+ }
+
+ bool squares_to_monsters = mons_positions.size() > candidates.size();
+
+ std::vector<int> distances;
+
+ // So the idea is we will search from either possible plant locations to
+ // monsters or from monsters to possible plant locations, but honestly the
+ // implementation is unnecessarily tense and doing plants to monsters all
+ // the time would be fine. Yet I'm reluctant to change it because it does
+ // work.
+ if (squares_to_monsters)
+ {
+ _point_point_distance(candidates, mons_positions,
+ squares_to_monsters, distances);
+ }
+ else
+ {
+ _point_point_distance(mons_positions, candidates,
+ squares_to_monsters, distances);
+ }
+
+ std::vector<point_distance> possible_moves(candidates.size());
+
+ for (unsigned i = 0; i < possible_moves.size(); ++i)
+ {
+ possible_moves[i].first = candidates[i];
+ possible_moves[i].second = distances[i];
+ }
+
+ std::sort(possible_moves.begin(), possible_moves.end(),
+ less_second<point_distance>);
+
+ for (unsigned i = 0; i < candidates.size(); ++i)
+ candidates[i] = possible_moves[i].first;
+
+ return (true);
+}
+
+// Prompt the user to select a stack of fruit from their inventory. The
+// user can optionally select only a partial stack of fruit (the count
+// variable will store the number of fruit the user wants). Return the
+// index of the item selected in the user's inventory, or a negative
+// number if the prompt failed (user cancelled or had no fruit).
+int _prompt_for_fruit(int & count, const char * prompt_string)
+{
+ int rc = prompt_invent_item(prompt_string,
+ MT_INVLIST,
+ OSEL_FRUIT,
+ true,
+ true,
+ true,
+ '\0',
+ -1,
+ &count);
+
+ if (prompt_failed(rc))
+ return (rc);
+
+ // Return PROMPT_INAPPROPRIATE if the 'object selected isn't
+ // actually fruit.
+ if (!is_fruit(you.inv[rc]))
+ return (PROMPT_INAPPROPRIATE);
+
+ // Handle it if the user lies about the amount of fruit available.
+ if (count > you.inv[rc].quantity)
+ count = you.inv[rc].quantity;
+
+ return (rc);
+}
+
+// Create a ring or partial ring around the caster. The user is
+// prompted to select a stack of fruit, and then plants are placed on open
+// squares adjacent to the user. Of course, one piece of fruit is
+// consumed per plant, so a complete ring may not be formed.
+bool plant_ring_from_fruit()
+{
+ int possible_count;
+ int created_count = 0;
+ int rc = _prompt_for_fruit(possible_count,
+ "Use which fruit? [0-9] specify amount");
+
+ // Prompt failed?
+ if (rc < 0)
+ return (false);
+
+ std::vector<coord_def> adjacent;
+
+ for (adjacent_iterator adj_it(you.pos()); adj_it; ++adj_it)
+ {
+ if (mons_class_can_pass(MONS_PLANT, env.grid(*adj_it))
+ && !actor_at(*adj_it))
+ {
+ adjacent.push_back(*adj_it);
+ }
+ }
+
+ if ((int)adjacent.size() > possible_count)
+ {
+ prioritise_adjacent(you.pos(), adjacent);
+ }
+
+ unsigned target_count =
+ (possible_count < (int)adjacent.size()) ? possible_count
+ : adjacent.size();
+
+ for (unsigned i = 0; i < target_count; ++i)
+ {
+ if (_create_plant(adjacent[i]))
+ created_count++;
+ }
+
+ dec_inv_item_quantity(rc, created_count);
+
+ return (created_count);
+}
+
+// Create a circle of water around the target, with a radius of
+// approximately 2. This turns normal floor tiles into shallow water
+// and turns (unoccupied) shallow water into deep water. There is a
+// chance of spawning plants or fungus on unoccupied dry floor tiles
+// outside of the rainfall area. Return the number of plants/fungi
+// created.
+int rain(const coord_def &target)
+{
+ int spawned_count = 0;
+ for (radius_iterator rad(target, LOS_RADIUS, true, true, true); rad; ++rad)
+ {
+ // Adjust the shape of the rainfall slightly to make it look
+ // nicer. I want a threshold of 2.5 on the euclidean distance,
+ // so a threshold of 6 prior to the sqrt is close enough.
+ int rain_thresh = 6;
+ coord_def local = *rad - target;
+
+ dungeon_feature_type ftype = grd(*rad);
+
+ if (local.abs() > rain_thresh)
+ {
+ // Maybe spawn a plant on (dry, open) squares that are in
+ // LOS but outside the rainfall area. In open space, there
+ // are 213 squares in LOS, and we are going to drop water on
+ // (25-4) of those, so if we want x plants to spawn on
+ // average in open space, the trial probability should be
+ // x/192.
+ if (x_chance_in_y(5, 192)
+ && !actor_at(*rad)
+ && ftype >= DNGN_FLOOR_MIN
+ && ftype <= DNGN_FLOOR_MAX)
+ {
+ const int plant = create_monster(mgen_data
+ (coinflip() ? MONS_PLANT : MONS_FUNGUS,
+ BEH_GOOD_NEUTRAL,
+ 0,
+ 0,
+ *rad,
+ MHITNOT,
+ MG_FORCE_PLACE, GOD_FEAWN));
+
+ if (plant != -1)
+ spawned_count++;
+ }
+
+ continue;
+ }
+
+ // Turn regular floor squares only into shallow water.
+ if (ftype >= DNGN_FLOOR_MIN && ftype <= DNGN_FLOOR_MAX)
+ {
+ grd(*rad) = DNGN_SHALLOW_WATER;
+ // Remove blood stains as well.
+ env.map(*rad).property &= ~(FPROP_BLOODY);
+
+ monsters *mon = monster_at(*rad);
+ if (mon && mon->has_ench(ENCH_AQUATIC_LAND))
+ mon->del_ench(ENCH_AQUATIC_LAND);
+ }
+ // We can also turn shallow water into deep water, but we're
+ // just going to skip cases where there is something on the
+ // shallow water. Destroying items will probably be annoying,
+ // and insta-killing monsters is clearly out of the question.
+ else if (!actor_at(*rad)
+ && igrd(*rad) == NON_ITEM
+ && ftype == DNGN_SHALLOW_WATER)
+ {
+ grd(*rad) = DNGN_DEEP_WATER;
+ }
+
+ if (ftype >= DNGN_MINMOVE)
+ {
+ // Maybe place a raincloud.
+ //
+ // The rainfall area is 20 (5*5 - 4 (corners) - 1 (center));
+ // the expected number of clouds generated by a fixed chance
+ // per tile is 20 * p = expected. Say an Invocations skill
+ // of 27 gives expected 5 clouds.
+ int max_expected = 5;
+ int expected = div_rand_round(max_expected
+ * you.skills[SK_INVOCATIONS], 27);
+
+ if (x_chance_in_y(expected, 20))
+ place_cloud(CLOUD_RAIN, *rad, 10, KC_YOU);
+ }
+
+
+ }
+
+ if (spawned_count > 0)
+ {
+ mprf("%s grow%s in the rain.",
+ (spawned_count > 1 ? "Some plants" : "A plant"),
+ (spawned_count > 1 ? "" : "s"));
+ }
+
+ return (spawned_count);
+}
+
+// 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)
+ {
+ for (stack_iterator stack_it(*rad); stack_it; ++stack_it)
+ {
+ if (stack_it->base_type == OBJ_CORPSES
+ && stack_it->sub_type == CORPSE_BODY)
+ {
+ count++;
+
+ int rc = create_monster(mgen_data(MONS_GIANT_SPORE,
+ behavior,
+ 0,
+ 0,
+ *rad,
+ MHITNOT,
+ MG_FORCE_PLACE));
+
+ if (rc!=-1)
+ env.mons[rc].flags |= MF_ATT_CHANGE_ATTEMPT;
+
+ if (mons_skeleton(stack_it->plus))
+ turn_corpse_into_skeleton(*stack_it);
+ else
+ destroy_item(stack_it->index());
+
+ break;
+ }
+ }
+ }
+
+ return (count);
+}
+
+struct monster_conversion
+{
+ monsters * base_monster;
+ int cost;
+ monster_type new_type;
+};
+
+bool operator<(const monster_conversion & left,
+ const monster_conversion & right)
+{
+ if (left.cost == right.cost)
+ return (coinflip());
+
+ return (left.cost < right.cost);
+}
+
+// Given a monster (which should be a plant/fungus), see if
+// evolve_flora() can upgrade it, and set up a monster_conversion
+// structure for it. Return true (and fill in possible_monster) if the
+// monster can be upgraded, and return false otherwise.
+bool _possible_evolution(monsters * input,
+ monster_conversion & possible_monster)
+{
+ int plant_cost = 10;
+ int toadstool_cost = 1;
+ int fungus_cost = 5;
+
+ possible_monster.base_monster = input;
+ switch (input->mons_species())
+ {
+ case MONS_PLANT:
+ possible_monster.cost = plant_cost;
+ possible_monster.new_type = MONS_OKLOB_PLANT;
+ break;
+
+ case MONS_FUNGUS:
+ case MONS_BALLISTOMYCETE:
+ possible_monster.cost = fungus_cost;
+ possible_monster.new_type = MONS_WANDERING_MUSHROOM;
+ break;
+
+ case MONS_TOADSTOOL:
+ possible_monster.cost = toadstool_cost;
+ possible_monster.new_type = MONS_BALLISTOMYCETE;
+ break;
+
+ default:
+ return (false);
+ }
+
+ return (true);
+}
+
+bool evolve_flora()
+{
+ int rc;
+ int available_count;
+
+ int points_per_fruit = 8;
+ int oklob_cost = 10;
+
+ float approx_oklob_rate = float(points_per_fruit)/float(oklob_cost);
+
+ char prompt_string[100];
+ memset(prompt_string,0,100);
+ sprintf(prompt_string,
+ "Use which fruit? %1.1f oklob plants per fruit, [0-9] specify amount",
+ approx_oklob_rate);
+
+ rc = _prompt_for_fruit(available_count, prompt_string);
+
+ // Prompt failed?
+ if (rc < 0)
+ return (false);
+
+ int points = points_per_fruit * available_count;
+ int starting_points = points;
+
+ std::priority_queue<monster_conversion> available_targets;
+
+ monster_conversion temp_conversion;
+
+ for (radius_iterator rad(you.pos(), LOS_RADIUS, true, true, true);
+ rad; ++rad)
+ {
+ monsters * target = monster_at(*rad);
+
+ if (!target)
+ continue;
+
+ if (_possible_evolution(target, temp_conversion))
+ available_targets.push(temp_conversion);
+ }
+
+ // Nothing available to upgrade.
+ if (available_targets.empty())
+ {
+ mpr("No flora in sight can be evolved.");
+ return (false);
+ }
+
+ int plants_evolved = 0;
+ int toadstools_evolved = 0;
+ int fungi_evolved = 0;
+
+ while (!available_targets.empty() && points > 0)
+ {
+ monster_conversion current_target = available_targets.top();
+
+ monsters * current_plant = current_target.base_monster;
+ available_targets.pop();
+
+ // Can we afford this thing?
+ if (current_target.cost > points)
+ continue;
+
+ points -= current_target.cost;
+
+ switch (current_plant->mons_species())
+ {
+ case MONS_PLANT:
+ plants_evolved++;
+ break;
+
+ case MONS_FUNGUS:
+ case MONS_BALLISTOMYCETE:
+ fungi_evolved++;
+ break;
+
+ case MONS_TOADSTOOL:
+ toadstools_evolved++;
+ break;
+ };
+
+ current_plant->upgrade_type(current_target.new_type, true, true);
+ current_plant->god = GOD_FEAWN;
+ current_plant->attitude = ATT_FRIENDLY;
+ current_plant->flags |= MF_CREATED_FRIENDLY;
+ current_plant->flags |= MF_ATT_CHANGE_ATTEMPT;
+
+ // Try to remove slowly dying in case we are upgrading a
+ // toadstool, and spore production in case we are upgrading a
+ // fungus.
+ current_plant->del_ench(ENCH_SLOWLY_DYING);
+ current_plant->del_ench(ENCH_SPORE_PRODUCTION);
+
+ if (current_plant->mons_species() == MONS_BALLISTOMYCETE)
+ current_plant->add_ench(ENCH_SPORE_PRODUCTION);
+
+ // Maybe we can upgrade it again?
+ if (_possible_evolution(current_plant, temp_conversion)
+ && temp_conversion.cost <= points)
+ {
+ available_targets.push(temp_conversion);
+ }
+ }
+
+ // How many pieces of fruit did we use up?
+ int points_used = starting_points - points;
+ int fruit_used = points_used / points_per_fruit;
+ if (points_used % points_per_fruit)
+ fruit_used++;
+
+ // The player didn't have enough points to upgrade anything (probably
+ // supplied only one fruit).
+ if (!fruit_used)
+ {
+ mpr("Not enough fruit to cause evolution.");
+ return (false);
+ }
+
+ dec_inv_item_quantity(rc, fruit_used);
+
+ // Mention how many plants were used.
+ if (fruit_used > 1)
+ mprf("%d pieces of fruit are consumed!", fruit_used);
+ else
+ mpr("A piece of fruit is consumed!");
+
+ // Messaging for generated plants.
+ if (plants_evolved > 1)
+ mprf("%d plants can now spit acid.", plants_evolved);
+ else if (plants_evolved == 1)
+ mpr("A plant can now spit acid.");
+
+ if (toadstools_evolved > 1)
+ mprf("%d toadstools gained stability.", toadstools_evolved);
+ else if (toadstools_evolved == 1)
+ mpr("A toadstool gained stability.");
+
+ if (fungi_evolved > 1)
+ {
+ mprf("%d fungal colonies can now pick up their mycelia and move.",
+ fungi_evolved);
+ }
+ else if (fungi_evolved == 1)
+ mpr("A fungal colony can now pick up its mycelia and move.");
+
+ return (true);
+}
+
bool ponderousify_armour()
{
int item_slot = -1;
diff --git a/crawl-ref/source/godabil.h b/crawl-ref/source/godabil.h
index 81cd805150..02f90c5baa 100644
--- a/crawl-ref/source/godabil.h
+++ b/crawl-ref/source/godabil.h
@@ -19,6 +19,14 @@ bool beogh_water_walk();
void yred_make_enslaved_soul(monsters *mon, bool force_hostile = false,
bool quiet = false, bool unrestricted = false);
bool feawn_passthrough(const monsters * target);
+int fungal_bloom();
+bool sunlight();
+bool prioritise_adjacent(const coord_def &target,
+ std::vector<coord_def> &candidates);
+bool plant_ring_from_fruit();
+int rain(const coord_def &target);
+int corpse_spores(beh_type behavior = BEH_FRIENDLY);
+bool evolve_flora();
bool vehumet_supports_spell(spell_type spell);
diff --git a/crawl-ref/source/godwrath.cc b/crawl-ref/source/godwrath.cc
index a17ce1b727..088d5083de 100644
--- a/crawl-ref/source/godwrath.cc
+++ b/crawl-ref/source/godwrath.cc
@@ -15,6 +15,7 @@
#include "effects.h"
#include "enum.h"
#include "food.h"
+#include "godabil.h"
#include "goditem.h"
#include "it_use2.h"
#include "message.h"
diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc
index bbeb789948..034e891c6c 100644
--- a/crawl-ref/source/spells2.cc
+++ b/crawl-ref/source/spells2.cc
@@ -13,7 +13,6 @@
#include <string.h>
#include <sstream>
#include <algorithm>
-#include <queue>
#include "externs.h"
@@ -1716,862 +1715,3 @@ bool cast_conjure_ball_lightning(int pow, god_type god)
return (success);
}
-
-// Turns corpses in LOS into skeletons and grows toadstools on them.
-// Can also turn zombies into skeletons and destroy ghoul-type monsters.
-// Returns the number of corpses consumed.
-int fungal_bloom()
-{
- int seen_mushrooms = 0;
- int seen_corpses = 0;
-
- int processed_count = 0;
- bool kills = false;
-
- for (radius_iterator i(you.pos(), LOS_RADIUS); i; ++i)
- {
- actor *target = actor_at(*i);
- if (target && (target->atype() == ACT_PLAYER
- || target->is_summoned()))
- {
- continue;
- }
-
- monsters * mons = monster_at(*i);
-
- if (mons && mons->mons_species() != MONS_TOADSTOOL)
- {
- switch (mons_genus(mons->mons_species()))
- {
- case MONS_ZOMBIE_SMALL:
- // Maybe turn a zombie into a skeleton.
- if (mons_skeleton(mons_zombie_base(mons)))
- {
- processed_count++;
-
- monster_type skele_type = MONS_SKELETON_LARGE;
- if (mons_zombie_size(mons_zombie_base(mons)) == Z_SMALL)
- skele_type = MONS_SKELETON_SMALL;
-
- simple_monster_message(mons, "'s flesh rots away.");
-
- mons->upgrade_type(skele_type, true, true);
- behaviour_event(mons, ME_ALERT, MHITYOU);
-
- continue;
- }
- // Else fall through and destroy the zombie.
- // Ghoul-type monsters are always destroyed.
- case MONS_GHOUL:
- {
- simple_monster_message(mons, " rots away and dies.");
-
- coord_def pos = mons->pos();
- int colour = mons->colour;
- int corpse = monster_die(mons, KILL_MISC, NON_MONSTER, true);
- kills = true;
-
- // If a corpse didn't drop, create a toadstool.
- // If one did drop, we will create toadstools from it as usual
- // later on.
- if (corpse < 0)
- {
- const int mushroom = create_monster(
- mgen_data(MONS_TOADSTOOL,
- BEH_FRIENDLY,
- 0,
- 0,
- pos,
- MHITNOT,
- MG_FORCE_PLACE,
- GOD_NO_GOD,
- MONS_NO_MONSTER,
- 0,
- colour),
- false);
-
- if (mushroom != -1)
- seen_mushrooms++;
-
- processed_count++;
-
- continue;
- }
- break;
- }
-
- default:
- continue;
- }
- }
-
- for (stack_iterator j(*i); j; ++j)
- {
- bool corpse_on_pos = false;
- if (j->base_type == OBJ_CORPSES && j->sub_type == CORPSE_BODY)
- {
- corpse_on_pos = true;
- int trial_prob = mushroom_prob(*j);
-
- processed_count++;
- int target_count = 1 + binomial_generator(20, trial_prob);
-
- int seen_per;
- spawn_corpse_mushrooms(*j, target_count, seen_per,
- BEH_FRIENDLY, true);
-
- seen_mushrooms += seen_per;
-
- // Either turn this corpse into a skeleton or destroy it.
- if (mons_skeleton(j->plus))
- turn_corpse_into_skeleton(*j);
- else
- destroy_item(j->index());
- }
-
- if (corpse_on_pos && observe_cell(*i))
- seen_corpses++;
- }
- }
-
- if (seen_mushrooms > 0)
- {
- mushroom_spawn_message(seen_mushrooms, seen_corpses);
- }
-
- if (kills)
- mprf("That felt like a moral victory.");
-
- return (processed_count);
-}
-
-int create_plant(coord_def & target)
-{
- if (actor_at(target) || !mons_class_can_pass(MONS_PLANT, grd(target)))
- return (0);
-
- const int plant = create_monster(mgen_data
- (MONS_PLANT,
- BEH_FRIENDLY,
- 0,
- 0,
- target,
- MHITNOT,
- MG_FORCE_PLACE, GOD_FEAWN));
-
-
- if (plant != -1)
- {
- env.mons[plant].flags |= MF_ATT_CHANGE_ATTEMPT;
- if (observe_cell(target))
- mpr("A plant grows up from the ground.");
- }
-
-
- return (plant != -1);
-}
-
-bool sunlight()
-{
- int c_size = 5;
- int x_offset[] = {-1, 0, 0, 0, 1};
- int y_offset[] = { 0,-1, 0, 1, 0};
-
- dist spelld;
-
- bolt temp_bolt;
-
- temp_bolt.colour = YELLOW;
- direction(spelld, DIR_TARGET, TARG_HOSTILE, LOS_RADIUS, false, false,
- false, true, "Select sunlight destination", NULL,
- true);
-
- if (!spelld.isValid)
- return (false);
-
- coord_def base = spelld.target;
-
- int evap_count = 0;
- int plant_count = 0;
-
- // FIXME: Uncomfortable level of code duplication here but the explosion
- // code in bolt subjects the input radius to r*(r+1) for the threshold and
- // since r is an integer we can never get just the 4-connected neighbours.
- // Anyway the bolt code doesn't seem to be well set up to handle the
- // 'occasional plant' gimmick.
- for (int i = 0;i < c_size; ++i)
- {
- coord_def target = base;
- target.x += x_offset[i];
- target.y += y_offset[i];
-
- if (!in_bounds(target) || feat_is_solid(grd(target)))
- continue;
-
- temp_bolt.explosion_draw_cell(target);
-
- actor *victim = actor_at(target);
-
- // If this is a water square we will evaporate it.
- dungeon_feature_type ftype = grd(target);
-
- switch (ftype)
- {
- case DNGN_SHALLOW_WATER:
- ftype = DNGN_FLOOR;
- break;
-
- case DNGN_DEEP_WATER:
- ftype = DNGN_SHALLOW_WATER;
- break;
-
- default:
- break;
- }
-
- if (grd(target) != ftype)
- {
- grd(target) = ftype;
- if (observe_cell(target))
- evap_count++;
- }
-
- monsters *mons = monster_at(target);
-
- // Pop submerged status.
- if (mons && mons_habitat(mons) == HT_WATER)
- {
- mons->del_ench(ENCH_SUBMERGED);
- if (ftype == DNGN_FLOOR)
- mons->add_ench(mon_enchant(ENCH_AQUATIC_LAND, 0, KC_YOU));
- }
-
- if (victim)
- {
- if (!mons)
- you.backlight();
- else
- backlight_monsters(target, 1, 0);
- }
- else if (one_chance_in(100)
- && ftype >= DNGN_FLOOR_MIN
- && ftype <= DNGN_FLOOR_MAX)
- {
- // Create a plant.
- const int plant = create_monster(mgen_data(MONS_PLANT,
- BEH_HOSTILE,
- 0,
- 0,
- target,
- MHITNOT,
- MG_FORCE_PLACE,
- GOD_FEAWN));
-
- if (plant != -1 && observe_cell(target))
- plant_count++;
- }
- }
- // move the cursor out of the way (it looks weird ok)
-#ifndef USE_TILE
- cgotoxy(base.x, base.y, GOTO_DNGN);
-#endif
- delay(200);
-
- if (plant_count)
- {
- mprf("%s grow%s in the sunlight.",
- (plant_count > 1 ? "Some plants": "A plant"),
- (plant_count > 1 ? "": "s"));
- }
-
- if (evap_count)
- mprf("Some water evaporates in the bright sunlight.");
-
- return (true);
-}
-
-template<typename T>
-bool less_second(const T & left, const T & right)
-{
- return left.second < right.second;
-}
-
-typedef std::pair<coord_def, int> point_distance;
-
-// dfs starting at origin, find the distance from the origin to the targets
-// (not leaving LOS, not crossing monsters or solid walls) and store that in
-// distances
-void path_distance(coord_def & origin,
- std::vector<coord_def> & targets,
- std::vector<int> & distances)
-{
- std::set<unsigned> exclusion;
- std::queue<point_distance> fringe;
- fringe.push(point_distance(origin,0));
-
- int idx = origin.x + origin.y * X_WIDTH;
- exclusion.insert(idx);
-
- while (!fringe.empty())
- {
- point_distance current = fringe.front();
- fringe.pop();
-
- // did we hit a target?
- for (unsigned i = 0; i < targets.size(); ++i)
- {
- if (current.first == targets[i])
- {
- distances[i] = current.second;
- break;
- }
- }
-
- for (adjacent_iterator adj_it(current.first); adj_it; ++adj_it)
- {
- idx = adj_it->x + adj_it->y * X_WIDTH;
- if (you.see_cell(*adj_it)
- && !feat_is_solid(env.grid(*adj_it))
- && exclusion.insert(idx).second)
- {
- monsters * temp = monster_at(*adj_it);
- if (!temp || (temp->attitude == ATT_HOSTILE
- && temp->mons_species() != MONS_PLANT
- && temp->mons_species() != MONS_TOADSTOOL
- && temp->mons_species() != MONS_FUNGUS
- && temp->mons_species() != MONS_BALLISTOMYCETE))
- {
- fringe.push(point_distance(*adj_it, current.second+1));
- }
- }
- }
- }
-}
-
-// so we are basically going to compute point to point distance between
-// the points of origin and the end points (origins and targets respecitvely)
-// We will return a vector consisting of the minimum distances along one
-// dimension of the distance matrix.
-void point_point(std::vector<coord_def> & origins,
- std::vector<coord_def> & targets,
- bool origin_to_target,
- std::vector<int> & distances)
-{
-
- distances.clear();
- // Consider a matrix where the points of origin form the rows and
- // the target points form the column, we want to take the minimum along
- // one of those dimensions.
- if (origin_to_target)
- distances.resize(origins.size(), INT_MAX);
- else
- distances.resize(targets.size(), INT_MAX);
-
- std::vector<int> current_distances(targets.size(), 0);
- for (unsigned i = 0; i < origins.size(); ++i)
- {
- for (unsigned j = 0; j < current_distances.size(); ++j)
- current_distances[j] = INT_MAX;
-
- path_distance(origins[i], targets, current_distances);
-
- // So we got the distance from a point of origin to one of the
- // targets. What should we do with it?
- if (origin_to_target)
- {
- // The minimum of current_distances is points(i)
- int min_dist = current_distances[0];
- for (unsigned j = 1; i < current_distances.size(); ++i)
- if (current_distances[j] < min_dist)
- min_dist = current_distances[j];
-
- distances[i] = min_dist;
- }
- else
- {
- for (unsigned j = 0; j < targets.size(); ++j)
- {
- if (i == 0)
- distances[j] = current_distances[j];
- else if (current_distances[j] < distances[j])
- distances[j] = current_distances[j];
- }
- }
- }
-}
-
-// So the idea is we want to decide which adjacent tiles are in the most 'danger'
-// We claim danger is proportional to the minimum distances from the point to a
-// (hostile) monster. This function carries out at most 8 depth-first searches
-// 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 prioritise_adjacent(const coord_def &target, std::vector<coord_def> & candidates)
-{
- radius_iterator los_it(target, LOS_RADIUS, true, true, true);
-
- std::vector<coord_def> mons_positions;
- // collect hostile monster positions in LOS
- for ( ; los_it; ++los_it)
- {
- monsters *hostile = monster_at(*los_it);
-
- if (hostile && hostile->attitude == ATT_HOSTILE)
- mons_positions.push_back(hostile->pos());
- }
-
- if (mons_positions.empty())
- {
- std::random_shuffle(candidates.begin(), candidates.end());
- return (true);
- }
-
- bool squares_to_monsters = mons_positions.size() > candidates.size();
-
- std::vector<int> distances;
-
- // So the idea is we will search from either possible plant locations to
- // monsters or from monsters to possible plant locations, but honestly the
- // implementation is unnecessarily tense and doing plants to monsters all
- // the time would be fine. Yet I'm reluctant to change it because it does
- // work.
- if (squares_to_monsters)
- point_point(candidates, mons_positions, squares_to_monsters, distances);
- else
- point_point(mons_positions, candidates, squares_to_monsters, distances);
-
- std::vector<point_distance> possible_moves(candidates.size());
-
- for (unsigned i = 0; i < possible_moves.size(); ++i)
- {
- possible_moves[i].first = candidates[i];
- possible_moves[i].second = distances[i];
- }
-
- std::sort(possible_moves.begin(), possible_moves.end(),
- less_second<point_distance>);
-
- for (unsigned i = 0; i < candidates.size(); ++i)
- candidates[i] = possible_moves[i].first;
-
- return (true);
-}
-
-// Prompt the user to select a stack of fruit from their inventory. The
-// user can optionally select only a partial stack of fruit (the count
-// variable will store the number of fruit the user wants). Return the
-// index of the item selected in the user's inventory, or a negative
-// number if the prompt failed (user cancelled or had no fruit).
-int _prompt_for_fruit(int & count, const char * prompt_string)
-{
- int rc = prompt_invent_item(prompt_string,
- MT_INVLIST,
- OSEL_FRUIT,
- true,
- true,
- true,
- '\0',
- -1,
- &count);
-
- if (prompt_failed(rc))
- return (rc);
-
- // Return PROMPT_INAPPROPRIATE if the 'object selected isn't
- // actually fruit.
- if (!is_fruit(you.inv[rc]))
- return (PROMPT_INAPPROPRIATE);
-
- // Handle it if the user lies about the amount of fruit available.
- if (count > you.inv[rc].quantity)
- count = you.inv[rc].quantity;
-
- return (rc);
-}
-
-// Create a ring or partial ring around the caster. The user is
-// prompted to select a stack of fruit, and then plants are placed on open
-// squares adjacent to the user. Of course, one piece of fruit is
-// consumed per plant, so a complete ring may not be formed.
-bool plant_ring_from_fruit()
-{
- int possible_count;
- int created_count = 0;
- int rc = _prompt_for_fruit(possible_count,
- "Use which fruit? [0-9] specify amount");
-
- // Prompt failed?
- if (rc < 0)
- return (false);
-
- std::vector<coord_def> adjacent;
-
- for (adjacent_iterator adj_it(you.pos()); adj_it; ++adj_it)
- {
- if (mons_class_can_pass(MONS_PLANT, env.grid(*adj_it))
- && !actor_at(*adj_it))
- {
- adjacent.push_back(*adj_it);
- }
- }
-
- if ((int)adjacent.size() > possible_count)
- {
- prioritise_adjacent(you.pos(), adjacent);
- }
-
- unsigned target_count =
- (possible_count < (int)adjacent.size()) ? possible_count
- : adjacent.size();
-
- for (unsigned i = 0; i < target_count; ++i)
- {
- if (create_plant(adjacent[i]))
- created_count++;
- }
-
- dec_inv_item_quantity(rc, created_count);
-
- return (created_count);
-}
-
-// Create a circle of water around the target, with a radius of
-// approximately 2. This turns normal floor tiles into shallow water
-// and turns (unoccupied) shallow water into deep water. There is a
-// chance of spawning plants or fungus on unoccupied dry floor tiles
-// outside of the rainfall area. Return the number of plants/fungi
-// created.
-int rain(const coord_def &target)
-{
- int spawned_count = 0;
- for (radius_iterator rad(target, LOS_RADIUS, true, true, true); rad; ++rad)
- {
- // Adjust the shape of the rainfall slightly to make it look
- // nicer. I want a threshold of 2.5 on the euclidean distance,
- // so a threshold of 6 prior to the sqrt is close enough.
- int rain_thresh = 6;
- coord_def local = *rad - target;
-
- dungeon_feature_type ftype = grd(*rad);
-
- if (local.abs() > rain_thresh)
- {
- // Maybe spawn a plant on (dry, open) squares that are in
- // LOS but outside the rainfall area. In open space, there
- // are 213 squares in LOS, and we are going to drop water on
- // (25-4) of those, so if we want x plants to spawn on
- // average in open space, the trial probability should be
- // x/192.
- if (x_chance_in_y(5, 192)
- && !actor_at(*rad)
- && ftype >= DNGN_FLOOR_MIN
- && ftype <= DNGN_FLOOR_MAX)
- {
- const int plant = create_monster(mgen_data
- (coinflip() ? MONS_PLANT : MONS_FUNGUS,
- BEH_GOOD_NEUTRAL,
- 0,
- 0,
- *rad,
- MHITNOT,
- MG_FORCE_PLACE, GOD_FEAWN));
-
- if (plant != -1)
- spawned_count++;
- }
-
- continue;
- }
-
- // Turn regular floor squares only into shallow water.
- if (ftype >= DNGN_FLOOR_MIN && ftype <= DNGN_FLOOR_MAX)
- {
- grd(*rad) = DNGN_SHALLOW_WATER;
- // Remove blood stains as well.
- env.map(*rad).property &= ~(FPROP_BLOODY);
-
- monsters *mon = monster_at(*rad);
- if (mon && mon->has_ench(ENCH_AQUATIC_LAND))
- mon->del_ench(ENCH_AQUATIC_LAND);
- }
- // We can also turn shallow water into deep water, but we're
- // just going to skip cases where there is something on the
- // shallow water. Destroying items will probably be annoying,
- // and insta-killing monsters is clearly out of the question.
- else if (!actor_at(*rad)
- && igrd(*rad) == NON_ITEM
- && ftype == DNGN_SHALLOW_WATER)
- {
- grd(*rad) = DNGN_DEEP_WATER;
- }
-
- if (ftype >= DNGN_MINMOVE)
- {
- // Maybe place a raincloud.
- //
- // The rainfall area is 20 (5*5 - 4 (corners) - 1 (center));
- // the expected number of clouds generated by a fixed chance
- // per tile is 20 * p = expected. Say an Invocations skill
- // of 27 gives expected 5 clouds.
- int max_expected = 5;
- int expected = div_rand_round(max_expected
- * you.skills[SK_INVOCATIONS], 27);
-
- if (x_chance_in_y(expected, 20))
- place_cloud(CLOUD_RAIN, *rad, 10, KC_YOU);
- }
-
-
- }
-
- if (spawned_count > 0)
- {
- mprf("%s grow%s in the rain.",
- (spawned_count > 1 ? "Some plants" : "A plant"),
- (spawned_count > 1 ? "" : "s"));
- }
-
- return (spawned_count);
-}
-
-// 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)
- {
- for (stack_iterator stack_it(*rad); stack_it; ++stack_it)
- {
- if (stack_it->base_type == OBJ_CORPSES
- && stack_it->sub_type == CORPSE_BODY)
- {
- count++;
-
- int rc = create_monster(mgen_data(MONS_GIANT_SPORE,
- behavior,
- 0,
- 0,
- *rad,
- MHITNOT,
- MG_FORCE_PLACE));
-
- if (rc!=-1)
- env.mons[rc].flags |= MF_ATT_CHANGE_ATTEMPT;
-
- if (mons_skeleton(stack_it->plus))
- turn_corpse_into_skeleton(*stack_it);
- else
- destroy_item(stack_it->index());
-
- break;
- }
- }
- }
-
- return (count);
-}
-
-struct monster_conversion
-{
- monsters * base_monster;
- int cost;
- monster_type new_type;
-};
-
-bool operator<(const monster_conversion & left,
- const monster_conversion & right)
-{
- if (left.cost == right.cost)
- return (coinflip());
-
- return (left.cost < right.cost);
-}
-
-// Given a monster (which should be a plant/fungus), see if
-// evolve_flora() can upgrade it, and set up a monster_conversion
-// structure for it. Return true (and fill in possible_monster) if the
-// monster can be upgraded, and return false otherwise.
-bool _possible_evolution(monsters * input,
- monster_conversion & possible_monster)
-{
- int plant_cost = 10;
- int toadstool_cost = 1;
- int fungus_cost = 5;
-
- possible_monster.base_monster = input;
- switch (input->mons_species())
- {
- case MONS_PLANT:
- possible_monster.cost = plant_cost;
- possible_monster.new_type = MONS_OKLOB_PLANT;
- break;
-
- case MONS_FUNGUS:
- case MONS_BALLISTOMYCETE:
- possible_monster.cost = fungus_cost;
- possible_monster.new_type = MONS_WANDERING_MUSHROOM;
- break;
-
- case MONS_TOADSTOOL:
- possible_monster.cost = toadstool_cost;
- possible_monster.new_type = MONS_BALLISTOMYCETE;
- break;
-
- default:
- return (false);
- }
-
- return (true);
-}
-
-bool evolve_flora()
-{
- int rc;
- int available_count;
-
- int points_per_fruit = 8;
- int oklob_cost = 10;
-
- float approx_oklob_rate = float(points_per_fruit)/float(oklob_cost);
-
- char prompt_string[100];
- memset(prompt_string,0,100);
- sprintf(prompt_string,
- "Use which fruit? %1.1f oklob plants per fruit, [0-9] specify amount",
- approx_oklob_rate);
-
- rc = _prompt_for_fruit(available_count, prompt_string);
-
- // Prompt failed?
- if (rc < 0)
- return (false);
-
- int points = points_per_fruit * available_count;
- int starting_points = points;
-
- std::priority_queue<monster_conversion> available_targets;
-
- monster_conversion temp_conversion;
-
- for (radius_iterator rad(you.pos(), LOS_RADIUS, true, true, true);
- rad; ++rad)
- {
- monsters * target = monster_at(*rad);
-
- if (!target)
- continue;
-
- if (_possible_evolution(target, temp_conversion))
- available_targets.push(temp_conversion);
- }
-
- // Nothing available to upgrade.
- if (available_targets.empty())
- {
- mpr("No flora in sight can be evolved.");
- return (false);
- }
-
- int plants_evolved = 0;
- int toadstools_evolved = 0;
- int fungi_evolved = 0;
-
- while (!available_targets.empty() && points > 0)
- {
- monster_conversion current_target = available_targets.top();
-
- monsters * current_plant = current_target.base_monster;
- available_targets.pop();
-
- // Can we afford this thing?
- if (current_target.cost > points)
- continue;
-
- points -= current_target.cost;
-
- switch (current_plant->mons_species())
- {
- case MONS_PLANT:
- plants_evolved++;
- break;
-
- case MONS_FUNGUS:
- case MONS_BALLISTOMYCETE:
- fungi_evolved++;
- break;
-
- case MONS_TOADSTOOL:
- toadstools_evolved++;
- break;
- };
-
- current_plant->upgrade_type(current_target.new_type, true, true);
- current_plant->god = GOD_FEAWN;
- current_plant->attitude = ATT_FRIENDLY;
- current_plant->flags |= MF_CREATED_FRIENDLY;
- current_plant->flags |= MF_ATT_CHANGE_ATTEMPT;
-
- // Try to remove slowly dying in case we are upgrading a
- // toadstool, and spore production in case we are upgrading a
- // fungus.
- current_plant->del_ench(ENCH_SLOWLY_DYING);
- current_plant->del_ench(ENCH_SPORE_PRODUCTION);
-
- if (current_plant->mons_species() == MONS_BALLISTOMYCETE)
- current_plant->add_ench(ENCH_SPORE_PRODUCTION);
-
- // Maybe we can upgrade it again?
- if (_possible_evolution(current_plant, temp_conversion)
- && temp_conversion.cost <= points)
- {
- available_targets.push(temp_conversion);
- }
- }
-
- // How many pieces of fruit did we use up?
- int points_used = starting_points - points;
- int fruit_used = points_used / points_per_fruit;
- if (points_used % points_per_fruit)
- fruit_used++;
-
- // The player didn't have enough points to upgrade anything (probably
- // supplied only one fruit).
- if (!fruit_used)
- {
- mpr("Not enough fruit to cause evolution.");
- return (false);
- }
-
- dec_inv_item_quantity(rc, fruit_used);
-
- // Mention how many plants were used.
- if (fruit_used > 1)
- mprf("%d pieces of fruit are consumed!", fruit_used);
- else
- mpr("A piece of fruit is consumed!");
-
- // Messaging for generated plants.
- if (plants_evolved > 1)
- mprf("%d plants can now spit acid.", plants_evolved);
- else if (plants_evolved == 1)
- mpr("A plant can now spit acid.");
-
- if (toadstools_evolved > 1)
- mprf("%d toadstools gained stability.", toadstools_evolved);
- else if (toadstools_evolved == 1)
- mpr("A toadstool gained stability.");
-
- if (fungi_evolved > 1)
- {
- mprf("%d fungal colonies can now pick up their mycelia and move.",
- fungi_evolved);
- }
- else if (fungi_evolved == 1)
- mpr("A fungal colony can now pick up its mycelia and move.");
-
- return (true);
-}
diff --git a/crawl-ref/source/spells2.h b/crawl-ref/source/spells2.h
index dcc02dfed2..943b85e98e 100644
--- a/crawl-ref/source/spells2.h
+++ b/crawl-ref/source/spells2.h
@@ -27,19 +27,6 @@ void cast_refrigeration(int pow);
void cast_toxic_radiance(void);
void drain_life(int pow);
-int fungal_bloom();
-int create_plant(coord_def & target);
-//bool plant_from_fruit();
-bool sunlight();
-
-bool prioritise_adjacent(const coord_def &target,
- std::vector<coord_def> &candidates);
-bool plant_ring_from_fruit();
-
-int rain(const coord_def &target);
-int corpse_spores(beh_type behavior = BEH_FRIENDLY);
-bool evolve_flora();
-
bool restore_stat(unsigned char which_stat, unsigned char stat_gain,
bool suppress_msg, bool recovery = false);