summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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: