diff options
author | dolorous <dolorous@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-07-28 03:39:01 +0000 |
---|---|---|
committer | dolorous <dolorous@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-07-28 03:39:01 +0000 |
commit | 72c1249ae7f4841dc8b335cb3a0a5ce81cb45887 (patch) | |
tree | 6b65ec871edba333b3ea18274025f52e4b384a24 | |
parent | 102ecc248252562f59d7b7baf445cd5cdb2e3162 (diff) | |
download | crawl-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.cc | 10 | ||||
-rw-r--r-- | crawl-ref/source/beam.cc | 48 | ||||
-rw-r--r-- | crawl-ref/source/beam.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/effects.cc | 307 | ||||
-rw-r--r-- | crawl-ref/source/effects.h | 14 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/misc.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 26 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.h | 3 | ||||
-rw-r--r-- | crawl-ref/source/monstuff.cc | 27 | ||||
-rw-r--r-- | crawl-ref/source/religion.cc | 287 | ||||
-rw-r--r-- | crawl-ref/source/religion.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/spells2.cc | 34 | ||||
-rw-r--r-- | crawl-ref/source/spells3.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/view.cc | 24 |
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); } } } |