From eef51d7a1ec7ce7e471d76f82154d2b147d6228b Mon Sep 17 00:00:00 2001 From: Charles Otto Date: Fri, 6 Nov 2009 20:46:30 -0500 Subject: Move Feawn's abilities to godabil.h/cc --- crawl-ref/source/spells2.cc | 860 -------------------------------------------- 1 file changed, 860 deletions(-) (limited to 'crawl-ref/source/spells2.cc') 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 #include #include -#include #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 -bool less_second(const T & left, const T & right) -{ - return left.second < right.second; -} - -typedef std::pair 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 & targets, - std::vector & distances) -{ - std::set exclusion; - std::queue 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 & origins, - std::vector & targets, - bool origin_to_target, - std::vector & 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 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 & candidates) -{ - radius_iterator los_it(target, LOS_RADIUS, true, true, true); - - std::vector 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 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 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); - - 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 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 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); -} -- cgit v1.2.3-54-g00ecf