diff options
author | j-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-07-20 18:07:51 +0000 |
---|---|---|
committer | j-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573> | 2009-07-20 18:07:51 +0000 |
commit | 3a83e92a99ce7a161f36e0bd2f1412580ef9bca0 (patch) | |
tree | fd7ccbbc3c039f7dac530a0822797913e6dbb7cc /crawl-ref/source/effects.cc | |
parent | 0bcbd855b8b1b0a9291aa6b78f2785ffc9987dce (diff) | |
download | crawl-ref-3a83e92a99ce7a161f36e0bd2f1412580ef9bca0.tar.gz crawl-ref-3a83e92a99ce7a161f36e0bd2f1412580ef9bca0.zip |
Apply caotto's plant god patch, for now named "Feawn".
Also fix potions of porridge's menu colour being yellow for Mummies.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10355 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/effects.cc')
-rw-r--r-- | crawl-ref/source/effects.cc | 366 |
1 files changed, 295 insertions, 71 deletions
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index 7675562ce3..ef1bfcb3c2 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -1991,7 +1991,9 @@ static void _set_friendly_foes(bool allow_patrol = false) for (int i = 0; i < MAX_MONSTERS; ++i) { monsters *mon(&menv[i]); - if (!mon->alive() || !mons_near(mon) || !mons_friendly_real(mon)) + if (!mon->alive() || !mons_near(mon) || !mons_friendly_real(mon) + || mon->has_ench(ENCH_BERSERK) + || mon->mons_species() == MONS_GIANT_SPORE) continue; // Berserking monsters cannot be ordered around. @@ -3798,37 +3800,211 @@ static void _maybe_restart_fountain_flow(const coord_def& where, } } -// Spawn a ring of mushrooms around the input corpse (radius=1). -// Could try different radii/check for largest available but doesn't right now. -// Maybe there should be a message for this. -static int _mushroom_ring(item_def &corpse) + +// A comparison struct for use in an stl priority queue. +template<typename T> +struct greater_second +{ + // The stl priority queue is a max queue and uses < as the default + // comparison. We want a min queue so we have to use a > operation here. + bool operator()(const T & left, const T & right) + { + return left.second > right.second; + } +}; + + +// Basically we want to break a circle into n_arcs equal sized arcs and find +// out which arc the input point pos falls on. +static int _arc_decomposition(const coord_def & pos, int n_arcs) +{ + float theta = atan2(pos.y, pos.x); + + if(pos.x == 0 && pos.y != 0) + theta = pos.y > 0 ? PI / 2 : -PI / 2; + + if(theta < 0) + theta += 2*PI; + + float arc_angle = 2*PI / n_arcs; + + theta += arc_angle / 2.0f; + + if(theta >= 2*PI) + theta -= 2*PI; + + return static_cast<int> (theta / arc_angle); +} + +// Place a partial ring of toadstools around the given corpse. returns the +// number of mushrooms spawned. A return of 0 indicates no mushrooms were +// placed -> some sort of failure mode was reached. +static int _mushroom_ring(item_def &corpse, int & seen_count) { - ::adjacent_iterator adj(corpse.pos); - int spawned_count=0; - for ( ; adj; ++adj) + // minimum number of mushrooms spawned on a given ring + unsigned min_spawn = 2; + + // highest radius we will allow. 8 is LOS + const int max_radius = LOS_RADIUS; + + seen_count = 0; + + // Just want to associate a point with a distance here for convenience + typedef std::pair<coord_def, int> coord_dist; + + + // Using a priority queue because squares don't make very good circles at + // larger radii. + // Generally we will visit points in order of distance to the origin + // (not path distance) under distance=sqrt(x^2+y^2-1); + std::priority_queue<coord_dist, + std::vector<coord_dist>, + greater_second<coord_dist> > fringe; + + coord_dist origin(corpse.pos, 0); + + fringe.push(origin); + + std::set<int> visited_indices; + + int max_distance = max_radius * max_radius - 1; + + std::vector<coord_def> radius_points[max_radius]; + + int max_visited = 0; + while (!fringe.empty()) { - if (mons_class_can_pass(MONS_TOADSTOOL, grd(*adj)) - && !actor_at(*adj)) + coord_dist current = fringe.top(); + fringe.pop(); + + int idx = current.first.x + current.first.y * X_WIDTH; + + if(!visited_indices.insert(idx).second) + continue; + + // we're done here once we hit a point that is farther away from the + // origin than our maximum permissible radius. + if(current.second > max_distance) + break; + + if(current.second > max_visited) + max_visited = current.second; + + float current_distance =current.second ? sqrtf(current.second + 1) : 0; + + int bound = static_cast<int> (current_distance + 0.5); + + // We don't include radius 0 as an option. This is also a good place + // to check if the squares is already occupied since we want to search + // past occupied squares but don't want to place toadstools on them. + if(bound > 0 && !actor_at(current.first)) { - const int mushroom = create_monster( - mgen_data(MONS_TOADSTOOL, - BEH_HOSTILE, - 0, - 0, - *adj, - MHITNOT, - MG_FORCE_PLACE, - GOD_NO_GOD, - MONS_PROGRAM_BUG, - 0, - corpse.colour), - false); + radius_points[bound - 1].push_back(current.first); + } - if (mushroom != -1) - spawned_count++; + for (adjacent_iterator i(current.first); i; ++i) + { + coord_dist temp(*i, current.second); + + coord_def local = temp.first - origin.first; + + temp.second = local.x * local.x + local.y * local.y - 1; + + // Don't link to nodes with a smaller absolute distance from the + // origin than the current node. This is some sort of connectivity + // constraint, in general we don't want parts of a ring to be + // connected via a higher radius since rings were supposedly + // created by outwards expansion. + if(temp.second < max_visited) + continue; + + idx = temp.first.x + temp.first.y * X_WIDTH; + + if(visited_indices.find(idx) == visited_indices.end() + && in_bounds(temp.first) + && mons_class_can_pass(MONS_TOADSTOOL, grd(temp.first))) + fringe.push(temp); + } + + } + + // So what we have done so far is collect the set of points at each radius + // reachable from the origin with (somewhat constrained) 8 connectivity, + // now we will choose one of those radii and spawn mushrooms at some + // of the points along it. + + int chosen_idx = random2(max_radius); + + // Excluding radius 1 rings because they don't look particularly good + // (or at least they don't look good with target_arc_len=2sqrt(2) + // they look ok with arc_len=2, but that doesn't seem very good for + // higher radii, maybe there should be a more complicated relationship + // between radius and target arc length, but whatever). + int min_idx=1; + + for (int i = 2; i < max_radius; i++, chosen_idx++) + { + chosen_idx = chosen_idx % max_radius; + + if(chosen_idx >= min_idx + && radius_points[chosen_idx].size() >= min_spawn) + break; + } + + // Couldn't find enough valid points at any radius? + if(radius_points[chosen_idx].size() < min_spawn) + return 0; + + std::random_shuffle(radius_points[chosen_idx].begin(), + radius_points[chosen_idx].end()); + + int target_amount = radius_points[chosen_idx].size(); + + int spawned_count = 0; + + float target_arc_len=2*sqrtf(2.0f); + //float target_arc_len = 2; + + int n_arcs = static_cast<int> (ceilf(2*PI * (chosen_idx + 1) + / target_arc_len)); + + int mushrooms_per_arc = 1; + + std::vector<int> arc_counts(n_arcs, mushrooms_per_arc); + + for (unsigned i = 0; + spawned_count < target_amount && i < radius_points[chosen_idx].size(); + i++) + { + int direction = _arc_decomposition(radius_points[chosen_idx].at(i) + - origin.first, n_arcs); + + if(arc_counts[direction] <= 0) + continue; + + arc_counts[direction]--; + + const int mushroom = create_monster( + mgen_data(MONS_TOADSTOOL, + BEH_HOSTILE, 0, 0, + radius_points[chosen_idx].at(i), + MHITNOT, + MG_FORCE_PLACE, + GOD_NO_GOD, + MONS_PROGRAM_BUG, + 0, + corpse.colour), + false); + + if(mushroom != -1) + { + spawned_count++; + if(see_grid(radius_points[chosen_idx].at(i))) + seen_count++; } } + return spawned_count; } @@ -3837,14 +4013,17 @@ static int _mushroom_ring(item_def &corpse) // Mushrooms radiate outwards from the corpse following bfs with 8-connectivity. // Could change the expansion pattern by using a priority queue for // sequencing (priority = distance from origin under some metric). -int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1) +int spawn_corpse_mushrooms(item_def &corpse, + int target_count, + int & seen_targets, + bool distance_as_time) { + seen_targets = 0; if (target_count == 0) return 0; - int x_offset[] = {-1,-1,-1, 0, 0, 1, 1, 1}; - int y_offset[] = {-1, 0, 1,-1, 1,-1, 0, 1}; - + int c_size=8; + int permutation[] = {0, 1, 2, 3, 4, 5, 6, 7}; int placed_targets = 0; @@ -3855,16 +4034,29 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1) // skeletonizing it) if the corpse square is unoccupied. if (!actor_at(corpse.pos) && one_chance_in(100)) { - if (see_grid(corpse.pos)) - mpr("A ring of toadstools grow before your very eyes."); + int ring_seen; + // It's possible no reasonable ring can be found, in that case we'll + // give up and just place a toadstool on top of the corpse (probably) + int res=_mushroom_ring(corpse, ring_seen); - corpse.special = 0; - return _mushroom_ring(corpse); + if(res) + { + corpse.special=0; + if(see_grid(corpse.pos)) + mpr("A ring of toadstools grow before your very eyes."); + else if(ring_seen > 1) + mpr("Some toadstools grow in a peculiar arc."); + else if (ring_seen >0) + mpr("A toadstool grows."); + + seen_targets = -1; + + + return res; + } } - // Can't figure out how to query the size of the xdim of the grid but who - // cares. - visited_indices.insert(10000*corpse.pos.y + corpse.pos.x); + visited_indices.insert(X_WIDTH*corpse.pos.y + corpse.pos.x); fringe.push(corpse.pos); while (!fringe.empty()) @@ -3899,13 +4091,30 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1) if (mushroom != -1) { + // Going to expliclty override the die-off timer in this case + // (this condition means we got called from fungal_bloom or + // similar and are creating a lot of toadstools at once that + // should die off quickly). + if(distance_as_time) + { + coord_def offset = corpse.pos - current; + + int dist = static_cast<int> (sqrtf(offset.abs()) + 0.5); + + int time_left = random2(8) + dist * 8 + 1; + + time_left *= 10; + + mon_enchant temp_en(ENCH_SLOWLY_DYING, 1, KC_OTHER, + time_left); + + env.mons[mushroom].update_ench(temp_en); + } + placed_targets++; if (see_grid(current)) { - if (see_grid(corpse.pos)) - mpr("A toadstool grows from a nearby corpse."); - else - mpr("A toadstool springs up from the ground."); + seen_targets++; } } else @@ -3916,16 +4125,14 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1) if (placed_targets == target_count) break; - // Randomize the order in which children are processed to a certain - // extent. - int idx = rand() % 8; + // wish adjacent_iterator had a random traversal + std::random_shuffle(permutation, permutation+c_size); - for (int count = 0; count < 8; ++count) + for (int count = 0; count < c_size; ++count) { - idx= (idx + 1) % 8; - coord_def temp(current.x + x_offset[idx], current.y + y_offset[idx]); + coord_def temp=current + Compass[permutation[count]]; - int index = temp.x + temp.y * 10000; + int index = temp.x + temp.y * X_WIDTH; if (visited_indices.find(index) == visited_indices.end() && in_bounds(temp) @@ -3941,6 +4148,34 @@ int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1) return placed_targets; } +int mushroom_prob(item_def & corpse) +{ + int low_threshold = 5; + int high_threshold = FRESHEST_CORPSE - 5; + + // Expect this many trials over a corpse's lifetime since this function + // is called once for every 10 units of rot_time. + int step_size = 10; + float total_trials = (high_threshold - low_threshold) / step_size; + + // chance of producing no mushrooms (not really because of weight_factor + // below) + float p_failure = .5; + + float trial_prob_f = 1 - powf(p_failure, 1.0f / total_trials); + + // The chance of producing mushrooms depends on the weight of the + // corpse involved. Humans weigh 550 so we will take that as the + // base factor here. + float weight_factor = item_mass(corpse)/550.0f; + + trial_prob_f *= weight_factor; + + int trial_prob = static_cast<int> (100* trial_prob_f ); + + return trial_prob; +} + // Randomly decide whether or not to spawn a mushroom over the given corpse // Assumption: this is called before the rotting away logic in update_corpses. @@ -3961,37 +4196,26 @@ static void _maybe_spawn_mushroom(item_def & corpse, int rot_time) if (spawn_time > high_threshold) spawn_time = high_threshold; - // So we're going to spawn one or more mushrooms over the lifetime of a - // corpse here. For convenience we follow a binomial distribution. I think - // the most useful analysis is in terms of the probability of a corpse - // producing no mushrooms, although trial probability should just be hard - // coded once a value is agreed on. - - - // Expect this many trials over a corpse's lifetime since this function - // is called once for every 10 units of rot_time. int step_size = 10; - float total_trials = (high_threshold - low_threshold) / step_size; - // chance of producing no mushrooms - float p_failure = .5; + int current_trials = spawn_time / step_size; - float trial_prob_f = 1 - powf(p_failure,1.0f/total_trials); + int trial_prob = mushroom_prob(corpse); - // The chance of producing mushrooms depends on the weight of the - // corpse involved. Humans weigh 550 so we will take that as the - // base factor here. - float weight_factor = item_mass(corpse)/550.0f; - - trial_prob_f *= weight_factor; - - int trial_prob = static_cast<int>(100*trial_prob_f); + int success_count = binomial_generator(current_trials, trial_prob); - int current_trials = spawn_time/step_size; + int seen_spawns; + spawn_corpse_mushrooms(corpse, success_count, seen_spawns); - int success_count = binomial_generator(current_trials, trial_prob); + if(seen_spawns > 0) + { + std::string base = seen_spawns > 1 ? "Some toadstools" : "A toadstool"; - spawn_corpse_mushrooms(corpse, success_count); + if(see_grid(corpse.pos)) + mprf("%s grows from a nearby corpse.", base.c_str()); + else + mprf("%s springs up from the ground.", base.c_str()); + } } //--------------------------------------------------------------- |