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
KZWOHH536QBWAPWROR32EOXEK5LRY6I5VHVL7HHI7GOFKCMQTCSQC
OVYFNBS6TZ7DGYA3TYM7N244T4M6JE6ISAPOZKOMRRAQEEIVPXBQC
LHYTGOCNDWX3CVD2HSQ6LAYC6NLKKI6ZKKNWZ5IQWP6YP5PQEVWQC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
5XNQ3SSNBFXFNWA6DPM74W6FH65NX665P3DMH6YCWVFOPZTJSYCQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SH6NU4H5TG4O7CRXRHZ7MDHABZLWWXQ77NJDBHI7KCVHXHQYK46QC
N5FAAVHNKQZJV2G3JFRW7WKTXB3A4YY6GTVIMBCG5RSA65TKVBGQC
ASH5CK6CPBKMLGGIRJ5GKTWMS5W3OBVHTL66RTYZIPFM6KFBYA3QC
PDOFPXD2X6VI23AHKCGQ5RVDBG74CNP2E3YOHKXLOARHHBXEK3HQC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC
2G7MZ653N3JUHJ4DA5Q7VRO3S5T27DLPKDCJEKB6DGYSTXULUVWAC
5XSXMOBGXFLTIQE6WDXWWFVDOTUPZSIQ2FWT3YI5QMVU6D76IUYQC
3WHI3KM43ZCN4ITJLFQQBQBC4OJPRS7QTBPIQ6QBCUVKRSK476SAC
KQNMFSLV62B4ANDKTUZ7LQH2MD2NDGNCP55MKM5YAATQ4T52H2PQC
T4FNOPMWYYJHJBTTY33PB43HTJPKEC46L62YERTWIX73HYZSELXQC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
J7GPW2YXLT6FGSKJ24FGQ24GTRZ6A2BDYM6CIXV4R6YBEHP6BGPAC
OONYLF4DAPLIYLBNNRW74IVT5BBTWI4XHQBXSNSPVRX3FTKJBTRAC
HIRKGUMNJPWKSVTR6TVBPD3MWNA63CEHCLCIPWEMGDFHVB3NPLDQC
QCUMH3C7GXV7ZW444WT5SFAXQOJKJSE2YCQCEHMRYXCWF4QI7UMAC
I7QLYOTE6DLQZM7YWUWYLKHRJRB2A3STQ42ALSRGQICEWKD2QTEQC
4RFKVDJKTCRBZU6WPJ2E5OVI5IRPY3UTRPOBLC5QHY4CQJJTLZKQC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
// 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;
}
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);
}
{
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
},
}
// 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);
}