summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source
diff options
context:
space:
mode:
authorj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2009-06-29 19:44:39 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2009-06-29 19:44:39 +0000
commite66c77b50df3c0c91837b52b9e61c8235a96c685 (patch)
treee780b4b6696564f4cf3a5763987bd776ff064537 /crawl-ref/source
parent4dc30336a62ec7e8261253fc0b9c0674efda1555 (diff)
downloadcrawl-ref-e66c77b50df3c0c91837b52b9e61c8235a96c685.tar.gz
crawl-ref-e66c77b50df3c0c91837b52b9e61c8235a96c685.zip
Apply caotto's neat patch to make toadstools grow on or around corpses.
Obviously, this might affect food balance and Necromancy. Tweaks may be necessary, but overall this looks good. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10077 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source')
-rw-r--r--crawl-ref/source/dat/descript/monsters.txt4
-rw-r--r--crawl-ref/source/effects.cc202
-rw-r--r--crawl-ref/source/enum.h4
-rw-r--r--crawl-ref/source/mon-data.h11
-rw-r--r--crawl-ref/source/mon-util.cc26
-rw-r--r--crawl-ref/source/monplace.cc10
-rw-r--r--crawl-ref/source/monstuff.cc3
-rw-r--r--crawl-ref/source/stuff.cc15
-rw-r--r--crawl-ref/source/stuff.h2
-rw-r--r--crawl-ref/source/tilepick.cc2
10 files changed, 276 insertions, 3 deletions
diff --git a/crawl-ref/source/dat/descript/monsters.txt b/crawl-ref/source/dat/descript/monsters.txt
index a5f0684ac6..987b029e7b 100644
--- a/crawl-ref/source/dat/descript/monsters.txt
+++ b/crawl-ref/source/dat/descript/monsters.txt
@@ -1369,6 +1369,10 @@ toenail golem
A huge animated statue made entirely from toenail clippings. Some people just have too much time on their hands.
%%%%
+toadstool
+
+A short-lived species of fungus typically found on or around decaying organic matter.
+%%%%
tormentor
This malign devil is covered in all manner of claws, spines and cruel hooks.
diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc
index 2244402d48..7ef9ec7a2a 100644
--- a/crawl-ref/source/effects.cc
+++ b/crawl-ref/source/effects.cc
@@ -15,6 +15,9 @@ REVISION("$Rev$");
#include <string.h>
#include <stdio.h>
#include <algorithm>
+#include <queue>
+#include <set>
+#include <cmath>
#include "externs.h"
@@ -3733,6 +3736,202 @@ 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)
+{
+ ::adjacent_iterator adj(corpse.pos);
+
+ int spawned_count=0;
+ for ( ; adj; ++adj)
+ {
+ if (mons_class_can_pass(MONS_TOADSTOOL, grd(*adj))
+ && !actor_at(*adj))
+ {
+ 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);
+
+ if (mushroom != -1)
+ 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).
+int spawn_corpse_mushrooms(item_def &corpse, int target_count = 1)
+{
+ 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 placed_targets = 0;
+
+ std::queue<coord_def> fringe;
+ 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.
+ if (!actor_at(corpse.pos) && one_chance_in(100))
+ {
+ if (see_grid(corpse.pos))
+ mpr("A ring of toadstools grow before your very eyes.");
+
+ corpse.special = 0;
+ return _mushroom_ring(corpse);
+ }
+
+ // 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);
+ fringe.push(corpse.pos);
+
+ while (!fringe.empty())
+ {
+ coord_def current = fringe.front();
+
+ fringe.pop();
+
+ actor * occupant = NULL;
+ // is this square occupied by a non mushroom?
+ if((occupant = actor_at(current))
+ && occupant->mons_species() != MONS_TOADSTOOL)
+ {
+ continue;
+ }
+
+ if (!occupant)
+ {
+ const int mushroom = create_monster(
+ mgen_data(MONS_TOADSTOOL,
+ BEH_HOSTILE,
+ 0,
+ 0,
+ current,
+ MHITNOT,
+ MG_FORCE_PLACE,
+ GOD_NO_GOD,
+ MONS_PROGRAM_BUG,
+ 0,
+ corpse.colour),
+ false);
+
+ if (mushroom != -1)
+ {
+ 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.");
+ }
+ }
+ else
+ continue;
+ }
+
+ // We're done here if we place the desired number of mushrooms.
+ if (placed_targets == target_count)
+ break;
+
+ // Randomize the order in which children are processed to a certain
+ // extent.
+ int idx = rand() % 8;
+
+ for (int count = 0; count < 8; ++count)
+ {
+ idx= (idx + 1) % 8;
+ coord_def temp(current.x + x_offset[idx], current.y + y_offset[idx]);
+
+ int index = temp.x + temp.y * 10000;
+
+ if (visited_indices.find(index) == visited_indices.end()
+ && in_bounds(temp)
+ && mons_class_can_pass(MONS_TOADSTOOL, grd(temp)))
+ {
+
+ visited_indices.insert(index);
+ fringe.push(temp);
+ }
+ }
+ }
+
+ return placed_targets;
+}
+
+
+// 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
+ // or rotting away.
+ int low_threshold = 5;
+ int high_threshold = FRESHEST_CORPSE - 5;
+
+ if (corpse.special < low_threshold)
+ return;
+
+ int spawn_time = (rot_time > corpse.special ? corpse.special : 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;
+
+ 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);
+
+ int current_trials = spawn_time/step_size;
+
+ int success_count = binomial_generator(current_trials, trial_prob);
+
+ spawn_corpse_mushrooms(corpse, success_count);
+}
+
//---------------------------------------------------------------
//
// update_corpses
@@ -3762,6 +3961,9 @@ void update_corpses(double elapsedTime)
continue;
}
+ if (it.sub_type == CORPSE_BODY)
+ _maybe_spawn_mushroom(it, rot_time);
+
if (rot_time >= it.special && !is_being_butchered(it))
{
if (it.base_type == OBJ_FOOD)
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 378e695cbb..9905fb912f 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -1239,6 +1239,7 @@ enum enchant_type
ENCH_PETRIFYING,
ENCH_PETRIFIED,
ENCH_LOWERED_MR,
+ ENCH_SLOWLY_DYING,
// Update enchantment names in mon-util.cc when adding or removing
// enchantments.
@@ -1795,7 +1796,8 @@ enum monster_type // (int) menv[].type
MONS_MERMAID,
MONS_SIREN, // 195
MONS_FLAMING_CORPSE,
- MONS_HARPY, // 197
+ MONS_HARPY,
+ MONS_TOADSTOOL, // 198
//jmf: end new monsters
MONS_WHITE_IMP = 220, // 220
MONS_LEMURE,
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index bb43d11f3e..ef3c9e90c7 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -666,6 +666,17 @@ static monsterentry mondata[] = {
HT_LAND, 10, DEFAULT_ENERGY, MONUSE_NOTHING, SIZE_TINY
},
+{
+ MONS_TOADSTOOL, 'f', BROWN, "toadstool",
+ M_NO_EXP_GAIN | M_STATIONARY,
+ MR_RES_POISON,
+ 0, 10, MONS_PLANT, MONS_TOADSTOOL, MH_PLANT, MAG_IMMUNE,
+ { AT_NO_ATK, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK },
+ { 1, 2,2, 0 },
+ 1, 0, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, I_PLANT,
+ HT_LAND, 10, DEFAULT_ENERGY, MONUSE_NOTHING, SIZE_TINY
+},
+
// goblins ('g')
{
MONS_GOBLIN, 'g', LIGHTGREY, "goblin",
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 2ee32ba969..bdcdbd4646 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -7105,6 +7105,13 @@ void monsters::timeout_enchantments(int levels)
del_ench(i->first);
break;
+ case ENCH_SLOWLY_DYING:
+ {
+ const int actdur = speed_to_duration(speed)*levels;
+ if (lose_ench_duration(i->first, actdur))
+ monster_die(this,KILL_MISC,-1,true);
+ break;
+ }
default:
break;
}
@@ -7481,6 +7488,18 @@ void monsters::apply_enchantment(const mon_enchant &me)
hit_points = -1;
break;
+ case ENCH_SLOWLY_DYING:
+
+ // If you are no longer dying you must be dead
+ if (decay_enchantment(me))
+ {
+ if (see_grid(this->position))
+ mprf("A nearby %s withers and dies.", this->name(DESC_PLAIN, false).c_str());
+
+ monster_die(this,KILL_MISC,-1,true);
+ }
+ break;
+
case ENCH_GLOWING_SHAPESHIFTER: // This ench never runs out!
// Number of actions is fine for shapeshifters.
if (type == MONS_GLOWING_SHAPESHIFTER || one_chance_in(4))
@@ -8211,7 +8230,7 @@ static const char *enchant_names[] =
"gloshifter", "shifter", "tp", "wary", "submerged",
"short-lived", "paralysis", "sick", "sleep", "fatigue", "held",
"blood-lust", "neutral", "petrifying", "petrified", "magic-vulnerable",
- "bug"
+ "decay", "bug"
};
static const char *_mons_enchantment_name(enchant_type ench)
@@ -8357,6 +8376,11 @@ int mon_enchant::calc_duration(const monsters *mons,
case ENCH_SHORT_LIVED:
cturn = 1000 / _mod_speed(200, mons->speed);
break;
+ case ENCH_SLOWLY_DYING:
+ // This may be a little too direct but the randomization at the end
+ // of this function is excessive for corpse mold. -cao
+ return (2*FRESHEST_CORPSE + random2(10)) * speed_to_duration(mons->speed)
+ * mons->speed/10;
case ENCH_ABJ:
if (deg >= 6)
cturn = 1000 / _mod_speed(10, mons->speed);
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 4e56354e58..93d59b44a8 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -1101,6 +1101,16 @@ static int _place_monster_aux(const mgen_data &mg,
if (mg.cls == MONS_GLOWING_SHAPESHIFTER)
menv[id].add_ench(ENCH_GLOWING_SHAPESHIFTER);
+ if (mg.cls == MONS_TOADSTOOL)
+ {
+ // This enchantment is a timer that counts down until death.
+ // These mushrooms should last longer than the lifespan of a corpse
+ // (to avoid spawning mushrooms in the same place over and over), aside
+ // from that the value is slightly randomized to avoid simultaneous
+ // die-offs of mushroom rings.
+ menv[id].add_ench(ENCH_SLOWLY_DYING);
+ }
+
if (monster_can_submerge(&menv[id], grd(fpos)) && !one_chance_in(5))
menv[id].add_ench(ENCH_SUBMERGED);
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index ccc1526130..d0fee93312 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -2704,7 +2704,8 @@ void behaviour_event(monsters *mon, mon_event_type event, int src,
&& !mons_is_fleeing(mon) && !mons_is_panicking(mon)))
{
// (Plain) plants and fungi cannot fight back.
- if (mon->type == MONS_FUNGUS || mon->type == MONS_PLANT)
+ if (mon->type == MONS_FUNGUS || mon->type == MONS_PLANT
+ || mon->type == MONS_TOADSTOOL)
return;
mon->foe = src;
diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc
index 43bcdb7a77..6d3d0ec719 100644
--- a/crawl-ref/source/stuff.cc
+++ b/crawl-ref/source/stuff.cc
@@ -689,6 +689,21 @@ int random2limit(int max, int limit)
return (sum);
}
+// Generate samples from a binomial distribution with n_trials and trial_prob
+// probability of success per trial. trial_prob is a integer less than 100
+// representing the % chancee of success.
+// This just evaluates all n trials, there is probably an efficient way of
+// doing this but I'm not much of a statistician. -CAO
+int binomial_generator(unsigned n_trials, unsigned trial_prob)
+{
+ int count = 0;
+ for (unsigned i = 0; i < n_trials; ++i)
+ if (::x_chance_in_y(trial_prob, 100))
+ count++;
+
+ return count;
+}
+
void cio_init()
{
crawl_state.io_inited = true;
diff --git a/crawl-ref/source/stuff.h b/crawl-ref/source/stuff.h
index 96d54af6e6..11a6cff013 100644
--- a/crawl-ref/source/stuff.h
+++ b/crawl-ref/source/stuff.h
@@ -43,6 +43,8 @@ int bestroll(int max, int rolls);
int roll_dice(int num, int size);
void scale_dice(dice_def &dice, int threshold = 24);
+int binomial_generator(unsigned n_trials, unsigned trial_prob);
+
// Various ways to iterate over things.
// stack_iterator guarantees validity so long as you don't manually
diff --git a/crawl-ref/source/tilepick.cc b/crawl-ref/source/tilepick.cc
index 1d413ac86a..21b085a08b 100644
--- a/crawl-ref/source/tilepick.cc
+++ b/crawl-ref/source/tilepick.cc
@@ -198,6 +198,8 @@ int tileidx_monster_base(const monsters *mon, bool detected)
return TILEP_MONS_FUNGUS;
case MONS_WANDERING_MUSHROOM:
return TILEP_MONS_WANDERING_MUSHROOM;
+ case MONS_TOADSTOOL:
+ return TILEP_MONS_FUNGUS;
// goblins ('g')
case MONS_GOBLIN: