summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/abl-show.cc74
-rw-r--r--crawl-ref/source/acr.cc4
-rw-r--r--crawl-ref/source/beam.cc41
-rw-r--r--crawl-ref/source/dat/descript/gods.txt8
-rw-r--r--crawl-ref/source/defines.h1
-rw-r--r--crawl-ref/source/describe.cc7
-rw-r--r--crawl-ref/source/directn.cc7
-rw-r--r--crawl-ref/source/effects.cc366
-rw-r--r--crawl-ref/source/effects.h7
-rw-r--r--crawl-ref/source/enum.h20
-rw-r--r--crawl-ref/source/externs.h1
-rw-r--r--crawl-ref/source/food.cc8
-rw-r--r--crawl-ref/source/invent.cc10
-rw-r--r--crawl-ref/source/invent.h4
-rw-r--r--crawl-ref/source/itemprop.cc24
-rw-r--r--crawl-ref/source/itemprop.h1
-rw-r--r--crawl-ref/source/luadgn.cc8
-rw-r--r--crawl-ref/source/message.cc3
-rw-r--r--crawl-ref/source/mon-util.cc1
-rw-r--r--crawl-ref/source/monstuff.cc5
-rw-r--r--crawl-ref/source/player.cc26
-rw-r--r--crawl-ref/source/religion.cc55
-rw-r--r--crawl-ref/source/rltiles/dc-dngn.txt3
-rw-r--r--crawl-ref/source/rltiles/dc-dngn/altars/dngn_altar_feawn.pngbin0 -> 961 bytes
-rw-r--r--crawl-ref/source/spells2.cc686
-rw-r--r--crawl-ref/source/spells2.h14
-rw-r--r--crawl-ref/source/tilepick.cc2
-rw-r--r--crawl-ref/source/view.cc14
28 files changed, 1286 insertions, 114 deletions
diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc
index 3bd2be8fe6..7cc0d1df7f 100644
--- a/crawl-ref/source/abl-show.cc
+++ b/crawl-ref/source/abl-show.cc
@@ -146,7 +146,10 @@ ability_type god_abilities[MAX_NUM_GODS][MAX_GOD_ABILITIES] =
ABIL_BEOGH_RECALL_ORCISH_FOLLOWERS, ABIL_NON_ABILITY },
// Jiyva
{ ABIL_JIYVA_CALL_JELLY, ABIL_NON_ABILITY, ABIL_NON_ABILITY,
- ABIL_JIYVA_SLIMIFY, ABIL_JIYVA_CURE_BAD_MUTATION }
+ ABIL_JIYVA_SLIMIFY, ABIL_JIYVA_CURE_BAD_MUTATION },
+ // Feawn
+ {ABIL_FEAWN_SUNLIGHT, ABIL_FEAWN_PLANT_RING, ABIL_FEAWN_RAIN,
+ ABIL_FEAWN_SPAWN_SPORES, ABIL_FEAWN_EVOLUTION },
};
// The description screen was way out of date with the actual costs.
@@ -328,6 +331,15 @@ static const ability_def Ability_List[] =
{ ABIL_JIYVA_CURE_BAD_MUTATION, "Cure Bad Mutation",
8, 0, 200, 15, ABFLAG_NONE },
+ // Feawn
+ { ABIL_FEAWN_FUNGAL_BLOOM, "Decomposition", 0, 0, 0, 0, ABFLAG_NONE },
+ { ABIL_FEAWN_SUNLIGHT, "Sunlight", 2, 0, 0, 0, ABFLAG_NONE},
+ { ABIL_FEAWN_PLANT_RING, "Growth", 2, 0, 0, 1, ABFLAG_NONE},
+ { ABIL_FEAWN_RAIN, "Rain", 4, 0, 100, 2, ABFLAG_NONE},
+ { ABIL_FEAWN_SPAWN_SPORES, "Reproduction", 4, 0, 50, 2, ABFLAG_NONE},
+ { ABIL_FEAWN_EVOLUTION, "Evolution", 4, 0, 50, 2, ABFLAG_NONE},
+
+
{ ABIL_HARM_PROTECTION, "Protection From Harm", 0, 0, 0, 0, ABFLAG_NONE },
{ ABIL_HARM_PROTECTION_II, "Reliable Protection From Harm",
0, 0, 0, 0, ABFLAG_PIETY },
@@ -671,12 +683,14 @@ static talent _get_talent(ability_type ability, bool check_confused)
case ABIL_ELYVILON_LESSER_HEALING_OTHERS:
case ABIL_LUGONU_ABYSS_EXIT:
case ABIL_JIYVA_CALL_JELLY:
+ case ABIL_FEAWN_SUNLIGHT:
invoc = true;
failure = 30 - (you.piety / 20) - (6 * you.skills[SK_INVOCATIONS]);
break;
// destroying stuff doesn't train anything
case ABIL_ELYVILON_DESTROY_WEAPONS:
+ case ABIL_FEAWN_FUNGAL_BLOOM:
invoc = true;
failure = 0;
break;
@@ -719,6 +733,7 @@ static talent _get_talent(ability_type ability, bool check_confused)
case ABIL_ELYVILON_GREATER_HEALING_OTHERS:
case ABIL_LUGONU_BEND_SPACE:
case ABIL_JIYVA_SLIMIFY:
+ case ABIL_FEAWN_PLANT_RING:
invoc = true;
failure = 40 - (you.piety / 20) - (5 * you.skills[SK_INVOCATIONS]);
break;
@@ -739,6 +754,8 @@ static talent _get_talent(ability_type ability, bool check_confused)
break;
case ABIL_MAKHLEB_MAJOR_DESTRUCTION:
+ case ABIL_FEAWN_SPAWN_SPORES:
+ case ABIL_FEAWN_RAIN:
case ABIL_YRED_DRAIN_LIFE:
invoc = true;
failure = 60 - (you.piety / 25) - (you.skills[SK_INVOCATIONS] * 4);
@@ -749,6 +766,7 @@ static talent _get_talent(ability_type ability, bool check_confused)
case ABIL_OKAWARU_HASTE:
case ABIL_MAKHLEB_GREATER_SERVANT_OF_MAKHLEB:
case ABIL_LUGONU_CORRUPT:
+ case ABIL_FEAWN_EVOLUTION:
invoc = true;
failure = 70 - (you.piety / 25) - (you.skills[SK_INVOCATIONS] * 4);
break;
@@ -1892,6 +1910,57 @@ static bool _do_ability(const ability_def& abil)
exercise(SK_INVOCATIONS, 1);
break;
+ case ABIL_FEAWN_FUNGAL_BLOOM:
+ {
+ int count = fungal_bloom();
+
+ // We are following the blood god sacrifice piety gain model, given as:
+ // if (random2(level + 10) > 5)
+ // piety_change = 1;
+ // where level = 10
+ // so the chance of gaining 1 piety from a corpse sacrifice to a blood
+ // god is (14/20 == 70/100)
+
+ int piety_gain = binomial_generator(count,70);
+
+ gain_piety(piety_gain);
+ break;
+ }
+ case ABIL_FEAWN_SUNLIGHT:
+
+ if(!sunlight())
+ return false;
+
+ exercise(SK_INVOCATIONS, 2 + random2(3));
+ break;
+
+ case ABIL_FEAWN_PLANT_RING:
+ if(!plant_ring_from_fruit())
+ return false;
+
+ exercise(SK_INVOCATIONS, 2 + random2(3));
+ break;
+
+ case ABIL_FEAWN_RAIN:
+
+ rain(you.pos() );
+
+ exercise(SK_INVOCATIONS, 2 + random2(3));
+ break;
+
+ case ABIL_FEAWN_SPAWN_SPORES:
+ corpse_spores();
+
+ exercise(SK_INVOCATIONS, 2 + random2(3));
+ break;
+
+ case ABIL_FEAWN_EVOLUTION:
+ if(!evolve_flora())
+ return false;
+
+ exercise(SK_INVOCATIONS, 2 + random2(3));
+ break;
+
case ABIL_TRAN_BAT:
if (!transform(100, TRAN_BAT))
{
@@ -1956,6 +2025,7 @@ static bool _do_ability(const ability_def& abil)
canned_msg(MSG_OK);
break;
+
case ABIL_NON_ABILITY:
mpr("Sorry, you can't do that.");
break;
@@ -2237,6 +2307,8 @@ std::vector<talent> your_talents(bool check_confused)
_add_talent(talents, ABIL_ELYVILON_DESTROY_WEAPONS, check_confused);
else if (you.religion == GOD_TROG)
_add_talent(talents, ABIL_TROG_BURN_SPELLBOOKS, check_confused);
+ else if (you.religion == GOD_FEAWN)
+ _add_talent(talents, ABIL_FEAWN_FUNGAL_BLOOM, check_confused);
// Gods take abilities away until penance completed. -- bwr
// God abilities generally don't work while silenced (they require
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index a8e12ad76f..62c7127b33 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -385,6 +385,10 @@ static void _god_greeting_message(bool game_start)
case GOD_JIYVA:
god_speaks(you.religion, "Slime for the Slime God!");
break;
+ case GOD_FEAWN:
+ simple_god_message(" says: Spread life and death.");
+ break;
+
case GOD_NO_GOD:
case NUM_GODS:
case GOD_RANDOM:
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index 0b11a7c131..657b38e1ac 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -3586,29 +3586,8 @@ void bolt::affect_player_enchantment()
break;
case BEAM_BACKLIGHT:
- if (!you.duration[DUR_INVIS])
- {
- if (you.duration[DUR_BACKLIGHT])
- mpr("You glow brighter.");
- else
- mpr("You are outlined in light.");
-
- you.duration[DUR_BACKLIGHT] += random_range(15, 35);
- if (you.duration[DUR_BACKLIGHT] > 250)
- you.duration[DUR_BACKLIGHT] = 250;
-
- obvious_effect = true;
- }
- else
- {
- mpr("You feel strangely conspicuous.");
-
- you.duration[DUR_BACKLIGHT] += random_range(3, 5);
- if (you.duration[DUR_BACKLIGHT] > 250)
- you.duration[DUR_BACKLIGHT] = 250;
-
- obvious_effect = true;
- }
+ you.backlight();
+ obvious_effect = true;
break;
case BEAM_POLYMORPH:
@@ -4096,6 +4075,21 @@ 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.
+ if (!is_tracer)
+ simple_god_message(" protects your plant from harm.", GOD_FEAWN);
+ return (false);
+ }
+
+
// preac: damage before AC modifier
// postac: damage after AC modifier
// final: damage after AC and resists
@@ -4192,6 +4186,7 @@ void bolt::tracer_nonenchantment_affect_monster(monsters* mon)
{
foe_info.power += 2 * final * mons_power(mon->type) / preac;
foe_info.count++;
+
}
else
{
diff --git a/crawl-ref/source/dat/descript/gods.txt b/crawl-ref/source/dat/descript/gods.txt
index 06e5257b31..e45a82463f 100644
--- a/crawl-ref/source/dat/descript/gods.txt
+++ b/crawl-ref/source/dat/descript/gods.txt
@@ -74,6 +74,10 @@ Jiyva
Jiyva is the chaotic ancient deity of the slimes.
%%%%
+Feawn
+
+Feawn is the god of plant and fungal life.
+%%%%
Zin powers
Zin grants followers the ability to preach to the unenlightened masses. With sufficient piety, a starving follower can pray for nutrition. Later, followers will gain powers to purify and strengthen their bodies, and can eventually find temporary safety in a divine refuge. As piety grows, followers will be protected from mutations. Apart from that, Zin may occasionally directly intervene to save a follower's life.
@@ -128,3 +132,7 @@ Beogh powers
Beogh encourages followers to make use of orcish gear found in the dungeon. Followers can smite their foes, and will even gain orcish followers of their own, who may be blessed by Beogh in battle and can be recalled within a level as needed. Eventually, they'll gain the power to walk on water. Beogh will sometimes directly intervene to save a follower's life.
%%%%
+Feawn powers
+
+Feawn encourages followers to promote the growth of plant life.
+%%%%
diff --git a/crawl-ref/source/defines.h b/crawl-ref/source/defines.h
index 42f083ffb6..9bea7f9c65 100644
--- a/crawl-ref/source/defines.h
+++ b/crawl-ref/source/defines.h
@@ -289,5 +289,6 @@ enum mouse_mode
MOUSE_MODE_MACRO
};
+#define PI 3.14159265359f
#endif
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 25c5a0784a..4049fd7ef3 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -3211,7 +3211,12 @@ const char *divine_title[NUM_GODS][8] =
// Jiyva -- slime and jelly theme
{"Scum", "Jelly", "Squelcher", "Dissolver",
- "Putrid Slime", "Consuming %s", "Archjelly", "Royal Jelly"}
+ "Putrid Slime", "Consuming %s", "Archjelly", "Royal Jelly"},
+
+ // Feawn -- nature theme, titles could use some work but the progression
+ // is generally from nature lover to walking disaster. -CAO
+ {"Walking Fertilizer", "Green %s", "Tree Hugger", "Conservationist",
+ "Floral Guardian", "Eco-Terrorist", "Green Death", "Force of Nature"}
};
static int _piety_level()
diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc
index db4cbb7848..84662df593 100644
--- a/crawl-ref/source/directn.cc
+++ b/crawl-ref/source/directn.cc
@@ -2733,6 +2733,8 @@ static std::string _base_feature_desc(dungeon_feature_type grid,
return ("staircase back to the Crypt");
case DNGN_RETURN_FROM_ZOT:
return ("gate leading back out of this place");
+
+ // altars
case DNGN_ALTAR_ZIN:
return ("glowing white marble altar of Zin");
case DNGN_ALTAR_SHINING_ONE:
@@ -2763,6 +2765,9 @@ static std::string _base_feature_desc(dungeon_feature_type grid,
return ("roughly hewn altar of Beogh");
case DNGN_ALTAR_JIYVA:
return ("viscous altar of Jiyva");
+ case DNGN_ALTAR_FEAWN:
+ return ("blossoming altar of Feawn");
+
case DNGN_FOUNTAIN_BLUE:
return ("fountain of clear blue water");
case DNGN_FOUNTAIN_SPARKLING:
@@ -3146,7 +3151,7 @@ static std::string _get_monster_desc(const monsters *mon)
text += pronoun + " is looking in ";
std::string name = foe->atype() == ACT_PLAYER
? "your" : (foe->name(DESC_NOCAP_THE) + "'s");
- text += name + "general direction.\n";
+ text += name + " general direction.\n";
}
}
else if (!foe || mons_is_fleeing(mon))
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());
+ }
}
//---------------------------------------------------------------
diff --git a/crawl-ref/source/effects.h b/crawl-ref/source/effects.h
index 2249572fe5..23cfe2b726 100644
--- a/crawl-ref/source/effects.h
+++ b/crawl-ref/source/effects.h
@@ -48,6 +48,13 @@ bool lose_stat(unsigned char which_stat, unsigned char stat_loss,
bool lose_stat(unsigned char which_stat, unsigned char stat_loss,
const item_def &cause, bool removed, bool force = false);
+// called from spells2 -cao
+int mushroom_prob(item_def & corpse);
+
+int spawn_corpse_mushrooms(item_def &corpse,
+ int target_count,
+ int & seen_targers,
+ bool distance_as_time = false);
// last updated 12may2000 {dlb}
/* ***********************************************************************
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index b1d476bd15..4d942ddb2d 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -107,11 +107,17 @@ enum ability_type
ABIL_JIYVA_JELLY_SHIELD, // 240
ABIL_JIYVA_SLIMIFY,
ABIL_JIYVA_CURE_BAD_MUTATION,
-
- ABIL_TRAN_BAT = 244,
+ ABIL_FEAWN_FUNGAL_BLOOM,
+ ABIL_FEAWN_SUNLIGHT,
+ ABIL_FEAWN_RAIN, // 245
+ ABIL_FEAWN_PLANT_RING,
+ ABIL_FEAWN_SPAWN_SPORES,
+ ABIL_FEAWN_EVOLUTION,
+
+ ABIL_TRAN_BAT = 250,
ABIL_HARM_PROTECTION,
- ABIL_HARM_PROTECTION_II, // 246
- ABIL_RENOUNCE_RELIGION = 250 // 250
+ ABIL_HARM_PROTECTION_II, // 252
+ ABIL_RENOUNCE_RELIGION = 260 // 260
};
enum activity_interrupt_type
@@ -1104,7 +1110,8 @@ enum dungeon_feature_type
DNGN_ALTAR_LUGONU,
DNGN_ALTAR_BEOGH,
DNGN_ALTAR_JIYVA,
- DNGN_ALTAR_LAST_GOD = DNGN_ALTAR_JIYVA,
+ DNGN_ALTAR_FEAWN,
+ DNGN_ALTAR_LAST_GOD = DNGN_ALTAR_FEAWN,
DNGN_FOUNTAIN_BLUE = 200, // 200
DNGN_FOUNTAIN_SPARKLING, // aka 'Magic Fountain' {dlb}
@@ -1344,7 +1351,8 @@ enum god_type
GOD_ELYVILON,
GOD_LUGONU,
GOD_BEOGH,
- GOD_JIYVA,
+ GOD_JIYVA, // 15
+ GOD_FEAWN,
NUM_GODS, // always after last god
GOD_RANDOM = 100,
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 07d8d699c1..0dba9858ef 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -1079,6 +1079,7 @@ public:
bool can_safely_mutate() const;
bool can_bleed() const;
bool mutate();
+ void backlight();
void banish(const std::string &who = "");
void blink(bool allow_partial_control = true);
void teleport(bool right_now = false, bool abyss_shift = false);
diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc
index e056390a20..c3f5e2bd27 100644
--- a/crawl-ref/source/food.cc
+++ b/crawl-ref/source/food.cc
@@ -2439,6 +2439,10 @@ static int _player_likes_food_type(int type)
// be eaten (respecting species and mutations set).
bool is_inedible(const item_def &item)
{
+ // Mummies don't eat.
+ if (you.is_undead == US_UNDEAD)
+ return (true);
+
if (food_is_rotten(item)
&& !player_mutation_level(MUT_SAPROVOROUS))
{
@@ -2466,6 +2470,10 @@ bool is_inedible(const item_def &item)
// still be edible or even delicious.
bool is_preferred_food(const item_def &food)
{
+ // Mummies don't eat.
+ if (you.is_undead == US_UNDEAD)
+ return (false);
+
// Vampires don't really have a preferred food type, but they really
// like blood potions.
if (you.species == SP_VAMPIRE)
diff --git a/crawl-ref/source/invent.cc b/crawl-ref/source/invent.cc
index c7f4e9726f..ac3e7e8dd8 100644
--- a/crawl-ref/source/invent.cc
+++ b/crawl-ref/source/invent.cc
@@ -997,6 +997,16 @@ static bool _item_class_selected(const item_def &i, int selector)
case OBJ_ARMOUR:
return (itype == OBJ_ARMOUR && you_tran_can_wear(i));
+ case OSEL_FRUIT:
+ return is_fruit(i);
+
+ // This is really dumb. I don't want to talk about it. -cao
+ case OSEL_SOME_FRUIT:
+
+ return is_fruit(i) && i.quantity >= 2;
+
+
+
case OSEL_WORN_ARMOUR:
return (itype == OBJ_ARMOUR && item_is_equipped(i));
diff --git a/crawl-ref/source/invent.h b/crawl-ref/source/invent.h
index 8f94ef26a7..8eab5f2310 100644
--- a/crawl-ref/source/invent.h
+++ b/crawl-ref/source/invent.h
@@ -29,7 +29,9 @@ enum object_selector
OSEL_THROWABLE = -9,
OSEL_BUTCHERY = -10,
OSEL_EVOKABLE = -11,
- OSEL_WORN_ARMOUR = -12
+ OSEL_WORN_ARMOUR = -12,
+ OSEL_FRUIT = -13,
+ OSEL_SOME_FRUIT = -14
};
#define PROMPT_ABORT -1
diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc
index d2372d11a1..3a152f679d 100644
--- a/crawl-ref/source/itemprop.cc
+++ b/crawl-ref/source/itemprop.cc
@@ -2331,6 +2331,30 @@ bool can_cut_meat(const item_def &item)
return (does_damage_type(item, DAM_SLICE));
}
+bool is_fruit(const item_def & item)
+{
+ if(item.base_type != OBJ_FOOD)
+ return false;
+
+ switch (item.sub_type)
+ {
+ case FOOD_APPLE:
+ case FOOD_APRICOT:
+ case FOOD_BANANA:
+ case FOOD_CHOKO:
+ case FOOD_GRAPE:
+ case FOOD_LEMON:
+ case FOOD_LYCHEE:
+ case FOOD_ORANGE:
+ case FOOD_PEAR:
+ case FOOD_RAMBUTAN:
+ case FOOD_STRAWBERRY:
+ case FOOD_SULTANA:
+ return true;
+ };
+
+ return false;
+}
bool food_is_rotten(const item_def &item)
{
return (item.special < 100) && (item.base_type == OBJ_CORPSES
diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h
index 1809da359e..461376cb92 100644
--- a/crawl-ref/source/itemprop.h
+++ b/crawl-ref/source/itemprop.h
@@ -723,6 +723,7 @@ int food_turns(const item_def &item);
bool can_cut_meat(const item_def &item);
bool food_is_rotten(const item_def &item);
int corpse_freshness(const item_def &item);
+bool is_fruit(const item_def & item);
// generic item property functions:
bool is_tool(const item_def &item);
diff --git a/crawl-ref/source/luadgn.cc b/crawl-ref/source/luadgn.cc
index b894a6be66..b092fd246c 100644
--- a/crawl-ref/source/luadgn.cc
+++ b/crawl-ref/source/luadgn.cc
@@ -1395,10 +1395,10 @@ const char *dngn_feature_names[] =
"altar_yredelemnul", "altar_xom", "altar_vehumet",
"altar_okawaru", "altar_makhleb", "altar_sif_muna", "altar_trog",
"altar_nemelex_xobeh", "altar_elyvilon", "altar_lugonu",
- "altar_beogh", "altar_jiyva", "", "", "", "", "", "fountain_blue",
- "fountain_sparkling", "fountain_blood", "dry_fountain_blue",
- "dry_fountain_sparkling", "dry_fountain_blood", "permadry_fountain",
- "abandoned_shop"
+ "altar_beogh", "altar_jiyva", "altar_feawn", "", "", "", "",
+ "fountain_blue", "fountain_sparkling", "fountain_blood",
+ "dry_fountain_blue", "dry_fountain_sparkling", "dry_fountain_blood",
+ "permadry_fountain", "abandoned_shop"
};
dungeon_feature_type dungeon_feature_by_name(const std::string &name)
diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc
index f39fbd89ab..e25830f62d 100644
--- a/crawl-ref/source/message.cc
+++ b/crawl-ref/source/message.cc
@@ -211,6 +211,9 @@ static char god_message_altar_colour( god_type god )
case GOD_KIKUBAAQUDGHA:
return (DARKGREY);
+ case GOD_FEAWN:
+ return (coinflip() ? BROWN : GREEN);
+
case GOD_XOM:
return (random2(15) + 1);
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index fc5de18c16..18dfa149b6 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -2569,6 +2569,7 @@ bool mons_friendly_real(const monsters *m)
return (m->attitude == ATT_FRIENDLY || m->has_ench(ENCH_CHARM));
}
+
bool mons_neutral(const monsters *m)
{
return (m->attitude == ATT_NEUTRAL || m->has_ench(ENCH_NEUTRAL)
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index ccec24b804..cf433e9b75 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -4197,13 +4197,14 @@ static void _handle_behaviour(monsters *mon)
if (isFriendly
&& you.pet_target != MHITNOT
&& (mon->foe == MHITNOT || mon->foe == MHITYOU)
- && !mon->has_ench(ENCH_BERSERK))
+ && !mon->has_ench(ENCH_BERSERK)
+ && mon->mons_species() != MONS_GIANT_SPORE )
{
mon->foe = you.pet_target;
}
// Instead, berserkers attack nearest monsters.
- if (mon->has_ench(ENCH_BERSERK)
+ if ((mon->has_ench(ENCH_BERSERK) || mon->mons_species() == MONS_GIANT_SPORE)
&& (mon->foe == MHITNOT || isFriendly && mon->foe == MHITYOU))
{
// Intelligent monsters prefer to attack the player,
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index e4922298df..8b1f27f332 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -7265,6 +7265,32 @@ bool player::backlit(bool check_haloed) const
|| (check_haloed ? haloed() : false));
}
+// This is the imperative version.
+void player::backlight()
+{
+ if (!you.duration[DUR_INVIS])
+ {
+ if (you.duration[DUR_BACKLIGHT])
+ mpr("You glow brighter.");
+ else
+ mpr("You are outlined in light.");
+
+ you.duration[DUR_BACKLIGHT] += random_range(15, 35);
+ if (you.duration[DUR_BACKLIGHT] > 250)
+ you.duration[DUR_BACKLIGHT] = 250;
+ }
+ else
+ {
+ mpr("You feel strangely conspicuous.");
+
+ you.duration[DUR_BACKLIGHT] += random_range(3, 5);
+ if (you.duration[DUR_BACKLIGHT] > 250)
+ you.duration[DUR_BACKLIGHT] = 250;
+
+
+ }
+}
+
bool player::haloed() const
{
return (halo_radius());
diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc
index d0045e60bc..28fa6abd9e 100644
--- a/crawl-ref/source/religion.cc
+++ b/crawl-ref/source/religion.cc
@@ -290,7 +290,13 @@ const char* god_gain_power_messages[NUM_GODS][MAX_GOD_ABILITIES] =
"",
"turn your foes to slime",
"call upon Jiyva to remove your harmful mutations"
- }
+ },
+ // Feawn
+ { "call sunshine",
+ "cause a ring of plants to grow",
+ "control the weather",
+ "spawn explosive spores",
+ "induce evolution"}
};
const char* god_lose_power_messages[NUM_GODS][MAX_GOD_ABILITIES] =
@@ -383,7 +389,13 @@ const char* god_lose_power_messages[NUM_GODS][MAX_GOD_ABILITIES] =
"",
"turn your foes to slime",
"call upon Jiyva to remove your harmful mutations"
- }
+ },
+ // Feawn
+ { "call sunshine",
+ "cause a ring of plants to grow",
+ "control the weather",
+ "spawn explosive spores",
+ "induce evolution"}
};
static bool _holy_beings_attitude_change();
@@ -506,6 +518,12 @@ std::string get_god_likes(god_type which_god, bool verbose)
likes.push_back("you train your various spell casting skills");
break;
+ case GOD_FEAWN:
+ snprintf(info,INFO_SIZE,"you promote decomposition%s",
+ verbose ? " via the <w>f</w> command" : "");
+ likes.push_back(info);
+ break;
+
case GOD_TROG:
snprintf(info, INFO_SIZE, "you destroy spellbooks (especially ones "
"you've never read)%s",
@@ -776,6 +794,12 @@ std::string get_god_dislikes(god_type which_god, bool /*verbose*/)
dislikes.push_back("you attack your fellow slimes");
break;
+ case GOD_FEAWN:
+ dislikes.push_back("you destroy plants");
+ dislikes.push_back("allied flora die");
+ dislikes.push_back("you practice necromancy");
+ break;
+
default:
break;
}
@@ -2478,6 +2502,7 @@ std::string god_name( god_type which_god, bool long_name )
return god_name_jiyva(true) + " the Shapeless";
}
return god_name_jiyva(false);
+ case GOD_FEAWN: return (long_name ? "Feawn the Arborial" : "Feawn");
case GOD_XOM:
if (!long_name)
@@ -2653,6 +2678,18 @@ bool did_god_conduct(conduct_type thing_done, int level, bool known,
case DID_NECROMANCY:
case DID_UNHOLY:
+ if (you.religion == GOD_FEAWN && known)
+ {
+ piety_change = -level;
+ penance = level;
+ retval = true;
+ }
+ else
+ {
+ simple_god_message(" forgives your blasphemy, just this once.");
+ break;
+ }
+
case DID_ATTACK_HOLY:
switch (you.religion)
{
@@ -5180,6 +5217,7 @@ static bool _nemelex_retribution()
return (true);
}
+
static bool _jiyva_retribution()
{
const god_type god = GOD_JIYVA;
@@ -5226,6 +5264,12 @@ static bool _jiyva_retribution()
return (true);
}
+static bool _feawn_retribution()
+{
+ // to be implemented -CAO
+ return true;
+}
+
bool divine_retribution(god_type god)
{
ASSERT(god != GOD_NO_GOD);
@@ -5264,6 +5308,7 @@ bool divine_retribution(god_type god)
case GOD_SIF_MUNA: do_more = _sif_muna_retribution(); break;
case GOD_ELYVILON: do_more = _elyvilon_retribution(); break;
case GOD_JIYVA: do_more = _jiyva_retribution(); break;
+ case GOD_FEAWN: do_more = _feawn_retribution(); break;
default:
#if DEBUG_DIAGNOSTICS || DEBUG_RELIGION
@@ -7475,6 +7520,11 @@ void handle_god_time()
lose_piety(1);
break;
+ case GOD_FEAWN:
+ // Feawn's piety is stable over time but we need a case here to
+ // avoid the error message below.
+ break;
+
default:
DEBUGSTR("Bad god, no bishop!");
return;
@@ -7500,6 +7550,7 @@ int god_colour(god_type god) // mv - added
case GOD_SHINING_ONE:
case GOD_ELYVILON:
case GOD_OKAWARU:
+ case GOD_FEAWN:
return(CYAN);
case GOD_YREDELEMNUL:
diff --git a/crawl-ref/source/rltiles/dc-dngn.txt b/crawl-ref/source/rltiles/dc-dngn.txt
index 0d95b38b37..66aebfa1ca 100644
--- a/crawl-ref/source/rltiles/dc-dngn.txt
+++ b/crawl-ref/source/rltiles/dc-dngn.txt
@@ -456,6 +456,9 @@ dngn_altar_elyvilon DNGN_ALTAR_ELYVILON
dngn_altar_lugonu DNGN_ALTAR_LUGONU
dngn_altar_beogh DNGN_ALTAR_BEOGH
dngn_altar_jiyva DNGN_ALTAR_JIYVA
+%rim 1
+dngn_altar_feawn DNGN_ALTAR_FEAWN
+%rim 0
%sdir dc-dngn
dngn_blue_fountain DNGN_BLUE_FOUNTAIN
diff --git a/crawl-ref/source/rltiles/dc-dngn/altars/dngn_altar_feawn.png b/crawl-ref/source/rltiles/dc-dngn/altars/dngn_altar_feawn.png
new file mode 100644
index 0000000000..9278407d2e
--- /dev/null
+++ b/crawl-ref/source/rltiles/dc-dngn/altars/dngn_altar_feawn.png
Binary files differ
diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc
index efa10e4b85..2a6b8e332c 100644
--- a/crawl-ref/source/spells2.cc
+++ b/crawl-ref/source/spells2.cc
@@ -16,6 +16,7 @@ REVISION("$Rev$");
#include <string.h>
#include <sstream>
#include <algorithm>
+#include <queue>
#include "externs.h"
@@ -1723,3 +1724,688 @@ bool cast_conjure_ball_lightning(int pow, god_type god)
return (success);
}
+
+// Turns corpses in LOS into skeletons and grows toadstools on them.
+// returns the number of corpses consumed
+int fungal_bloom()
+{
+ int seen_mushrooms=0;
+ int seen_corpses=0;
+
+ int processed_count=0;
+ for(radius_iterator i(you.position, LOS_RADIUS);i;++i)
+ {
+ // going to ignore squares that are already occupied by non-fungi
+ if(actor_at(*i) && !actor_at(*i)->mons_species() == MONS_TOADSTOOL)
+ continue;
+
+
+ for(stack_iterator j(*i); j; ++j)
+ {
+ bool corpse_on_pos = false;
+ if(j->base_type == OBJ_CORPSES && j->sub_type == CORPSE_BODY)
+ {
+ corpse_on_pos = true;
+ int trial_prob = mushroom_prob(*j);
+
+ processed_count++;
+ int target_count = 1 + binomial_generator(20,trial_prob);
+
+ int seen_per;
+ spawn_corpse_mushrooms(*j, target_count, seen_per, true);
+
+ seen_mushrooms += seen_per;
+
+ // either turn this corpse into a skeleton or destroy it
+ if(mons_skeleton(j->plus))
+ turn_corpse_into_skeleton(*j);
+ else
+ destroy_item(j->index());
+ }
+
+ if(corpse_on_pos && see_grid(*i))
+ seen_corpses++;
+
+ }
+ }
+
+
+ if(seen_mushrooms > 0)
+ {
+ std::string base=seen_mushrooms > 1 ? "Some toadstools" : "A toadstool";
+
+ // we obviously saw some corpses since we only processed squares in LOS
+ if(seen_corpses>1)
+ {
+ mprf("%s grow from nearby corpses.", base.c_str());
+ }
+ else
+ mprf("%s grow from a nearby corpse.", base.c_str());
+ }
+
+
+ return processed_count;
+}
+
+int create_plant(coord_def & target)
+{
+ if(actor_at(target) || !mons_class_can_pass(MONS_PLANT, grd(target) ))
+ return 0;
+
+ const int plant = create_monster(mgen_data
+ (MONS_PLANT,
+ BEH_FRIENDLY,
+ 0,
+ 0,
+ target,
+ MHITNOT,
+ MG_FORCE_PLACE, GOD_FEAWN));
+
+
+ if(plant != -1 && see_grid(target) )
+ mpr("A plant grows up from the ground.");
+
+ return plant != -1;
+}
+
+bool sunlight()
+{
+
+ int c_size = 5;
+ int x_offset[] = {-1, 0, 0, 0, 1};
+ int y_offset[] = { 0,-1, 0, 1, 0};
+
+ dist spelld;
+
+ bolt temp_bolt;
+
+ temp_bolt.colour = YELLOW;
+ direction( spelld, DIR_TARGET, TARG_ENEMY, LOS_RADIUS, false, false,
+ false, true, "Select sunlight destination", NULL,
+ true);
+
+ if(!spelld.isValid)
+ return false;
+
+ coord_def base = spelld.target;
+
+ int evap_count=0;
+ int plant_count=0;
+
+ // uncomfortable level of code duplication here but the explosion code in
+ // bolt subjects the input radius to r*(r+1) for the threshold and
+ // since r is an integer we can never get just the 4-connected neighbors.
+ // Anyway the bolt code doesn't seem to be well set up to handle the
+ // 'occasional plant' gimmick.
+ for(int i=0;i<c_size;i++)
+ {
+ coord_def target=base;
+ target.x+=x_offset[i];
+ target.y+=y_offset[i];
+
+ if(!in_bounds(target) || grid_is_solid(grd(target)))
+ continue;
+
+ temp_bolt.explosion_draw_cell(target);
+
+ actor * victim = actor_at(target);
+
+ // If this is a water square we will evaporate it
+ dungeon_feature_type ftype = grd(target);
+
+ switch (int(ftype))
+ {
+ case DNGN_SHALLOW_WATER:
+ ftype=DNGN_FLOOR;
+ break;
+
+ case DNGN_DEEP_WATER:
+ ftype = DNGN_SHALLOW_WATER;
+ break;
+ }
+ if(grd(target)!=ftype)
+ {
+ grd(target) = ftype;
+ if(see_grid(target))
+ evap_count++;
+ }
+
+ monsters * monster_vic = monster_at(target);
+
+ // Pop submerged status (this may be a little too nice
+ // because it will affect trapdoor spiders not just fish).
+ if(monster_vic)
+ monster_vic->del_ench(ENCH_SUBMERGED);
+
+ if(victim)
+ {
+ if(!monster_vic)
+ you.backlight();
+ else
+ backlight_monsters(target,1,0);
+ }
+ else if(one_chance_in(100)
+ && ftype >= DNGN_FLOOR_MIN
+ && ftype <= DNGN_FLOOR_MAX )
+ {
+ // create a plant.
+ int plant=create_monster(mgen_data
+ (MONS_PLANT,
+ BEH_HOSTILE,
+ 0,
+ 0,
+ target,
+ MHITNOT,
+ MG_FORCE_PLACE, GOD_FEAWN));
+
+ if(plant!=-1 && see_grid(target))
+ {
+ plant_count++;
+ }
+ }
+ }
+ delay(50);
+
+ update_screen();
+
+ if(plant_count)
+ mprf("%s grows in the sunlight.",
+ (plant_count > 1 ? "Some plants": "A plant"));
+
+ if(evap_count)
+ mprf("Some water evaporates in the bright sunlight.");
+
+ return true;
+}
+
+template<typename T>
+bool less_second(const T & left, const T & right)
+{
+ return left.second < right.second;
+}
+
+typedef std::pair<coord_def, int> point_distance;
+
+// dfs starting at origin, find the distance from the origin to the targets
+// (not leaving LOS, not crossing monsters or solid walls) and store that in
+// distances
+void path_distance(coord_def & origin,
+ std::vector<coord_def> & targets,
+ std::vector<int> & distances)
+{
+ std::set<unsigned> exclusion;
+ std::queue<point_distance> fringe;
+ fringe.push(point_distance(origin,0));
+
+ int idx=origin.x+origin.y*X_WIDTH;
+ exclusion.insert(idx);
+
+ while(!fringe.empty() )
+ {
+ point_distance current = fringe.front();
+ fringe.pop();
+
+
+ // did we hit a target?
+ for(unsigned i=0;i<targets.size();i++)
+ {
+ if(current.first == targets[i])
+ {
+ distances[i]=current.second;
+ break;
+ }
+ }
+
+ for(adjacent_iterator adj_it(current.first); adj_it;++adj_it)
+ {
+ idx=adj_it->x+adj_it->y*X_WIDTH;
+ if(see_grid(*adj_it)
+ && !grid_is_solid(env.grid(*adj_it))
+ && exclusion.insert(idx).second)
+ {
+ monsters * temp = monster_at(*adj_it);
+ if(!temp || (temp->attitude==ATT_HOSTILE
+ && temp->mons_species()!=MONS_PLANT
+ && temp->mons_species()!=MONS_TOADSTOOL
+ && temp->mons_species()!=MONS_FUNGUS))
+ {
+
+ fringe.push(point_distance(*adj_it, current.second+1));
+ }
+ }
+ }
+ } // end while
+}
+
+// so we are basically going to compute point to point distance between
+// the points of origin and the end points (origins and targets respecitvely)
+// We will return a vector consisting of the minimum distances along one
+// dimension of the distance matrix.
+void point_point(std::vector<coord_def> & origins,
+ std::vector<coord_def> & targets,
+ bool origin_to_target,
+ std::vector<int> & distances)
+{
+
+ distances.clear();
+ // consider a matrix where the points of origin form the rows and
+ // the target points form the column, we want to take the minimum along
+ // one of those dimensions.
+ if(origin_to_target)
+ distances.resize(origins.size(), INT_MAX);
+ else
+ distances.resize(targets.size(), INT_MAX);
+
+ std::vector<int> current_distances(targets.size(),0);
+ for(unsigned i=0;i<origins.size();i++)
+ {
+
+ for(unsigned j=0;j<current_distances.size();j++)
+ current_distances[j]=INT_MAX;
+
+ path_distance(origins[i], targets, current_distances);
+
+ // so we got the distance from a point of origin to one of the
+ // targets. What should we do with it?
+ if(origin_to_target)
+ {
+ // the minimum of current_distances is points(i)
+ int min_dist=current_distances[0];
+ for(unsigned j=1;i<current_distances.size();i++)
+ {
+ if(current_distances[j] < min_dist)
+ min_dist = current_distances[j];
+ }
+ distances[i]=min_dist;
+ }
+ else
+ {
+ for(unsigned j=0;j< targets.size();j++)
+ {
+ if(i==0)
+ distances[j]=current_distances[j];
+ else
+ {
+ if(current_distances[j] < distances[j])
+ distances[j] = current_distances[j];
+ }
+ }
+ }
+ }
+}
+
+// So the idea is we want to decide which adjacent tiles are in the most 'danger'
+// We claim danger is proportional to the minimum distances from the point to a
+// (hostile) monster. This function carries out at most 8 depth-first searches
+// 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)
+{
+ radius_iterator los_it(target, LOS_RADIUS, true, true, true);
+
+ std::vector<coord_def> mons_positions;
+ // collect hostile monster positions in LOS
+ for( ; los_it; ++los_it)
+ {
+ monsters * hostile = monster_at(*los_it);
+
+ if(hostile && hostile->attitude == ATT_HOSTILE)
+ mons_positions.push_back(hostile->pos());
+ }
+
+ mprf("foudn %d hostiles", mons_positions.size());
+
+ if(mons_positions.empty())
+ {
+ std::random_shuffle(candidates.begin(), candidates.end());
+ return true;
+ }
+
+ bool squares_to_monsters = mons_positions.size() > candidates.size();
+
+ std::vector<int> distances;
+
+ // So the idea is we will search from either possible plant locations to
+ // monsters or from monsters to possible plant locations, but honestly the
+ // implementation is unnecessarily tense and doing plants to monsters all
+ // the time would be fine. Yet I'm reluctant to change it because it does
+ // work.
+ if(squares_to_monsters)
+ point_point(candidates, mons_positions, squares_to_monsters, distances);
+ else
+ point_point(mons_positions, candidates, squares_to_monsters, distances);
+
+ std::vector<point_distance> possible_moves(candidates.size());
+
+ for(unsigned i=0;i<possible_moves.size();i++)
+ {
+ possible_moves[i].first = candidates[i];
+ possible_moves[i].second = distances[i];
+ }
+
+ std::sort(possible_moves.begin(), possible_moves.end(), less_second<point_distance>);
+
+ for(unsigned i=0;i<candidates.size();i++)
+ candidates[i]=possible_moves[i].first;
+
+ return true;
+}
+
+// Create a ring or partial ring around the caster
+// User is prompted to select a stack of fruit then plants are placed on open
+// squares adjacent to the caster, of course 1 piece of fruit is consumed per
+// plant so a complete ring may not be formed.
+bool plant_ring_from_fruit()
+{
+ int possible_count;
+ int created_count=0;
+ int rc = prompt_invent_item("Use which fruit?",
+ MT_INVLIST,
+ OSEL_FRUIT,
+ true,
+ true,
+ true,
+ '\0',
+ -1,
+ &possible_count);
+
+ if(prompt_failed(rc))
+ return 0;
+
+ std::vector<coord_def> adjacent;
+
+ for(adjacent_iterator adj_it(you.pos()); adj_it; ++adj_it)
+ {
+ if(mons_class_can_pass(MONS_PLANT, env.grid(*adj_it))
+ && !actor_at(*adj_it))
+ adjacent.push_back(*adj_it);
+ }
+
+ if(int(adjacent.size()) > possible_count)
+ {
+ prioritize_adjacent(you.pos(), adjacent);
+
+ //::update_screen();
+ }
+
+ unsigned target_count = possible_count < int(adjacent.size()) ? possible_count : adjacent.size();
+
+ for(unsigned i=0;i<target_count;i++)
+ {
+ if(create_plant(adjacent[i]))
+ created_count++;
+ }
+
+ dec_inv_item_quantity(rc, created_count);
+
+ return created_count;
+}
+
+
+// Creates a circle of water around the target (radius is approximately 2)
+// Turns normal floor tiles into shallow water and turns (unoccupied) shallow
+// water into deep water.
+// Chance of spawning plants or fungus on unoccupied dry floor tiles outside
+// of the rainfall area
+// Returns the number of plants/fungus created
+int rain(coord_def & target)
+{
+ radius_iterator rad(target, LOS_RADIUS, true, true, true);
+
+ int spawned_count=0;
+ for (; rad; ++rad)
+ {
+ // adjusting the shape of the rainfall slightly to make it look nicer.
+ // I want a threshold of 2.5 on the euclidean distance so a threshold
+ // of 6 prior to the sqrt is close enough.
+ int rain_thresh=6;
+ coord_def local=*rad-target;
+
+ dungeon_feature_type ftype = grd(*rad);
+
+ if(local.abs() > rain_thresh)
+ {
+ // maybe spawn a plant on (dry, open) squares that are in LOS but
+ // outside the rainfall area.
+ // In open space there are 213 squares in LOS, and we are
+ // going to drop water on (25-4) of those, so if we want x plants
+ // to spawn on average in open space the trial probability should
+ // be x/192
+
+ if(x_chance_in_y(5,192)
+ && !actor_at(*rad)
+ && ftype >= DNGN_FLOOR_MIN
+ && ftype <= DNGN_FLOOR_MAX )
+ {
+ int plant=create_monster(mgen_data
+ (coinflip() ? MONS_PLANT : MONS_FUNGUS,
+ BEH_HOSTILE,
+ 0,
+ 0,
+ *rad,
+ MHITNOT,
+ MG_FORCE_PLACE, GOD_FEAWN));
+
+ if(plant!=-1)
+ spawned_count++;
+ }
+
+ continue;
+ }
+
+ // Turn regular floor squares only into shallow water
+ if(ftype>=DNGN_FLOOR_MIN && ftype<=DNGN_FLOOR_MAX)
+ {
+ grd(*rad) = DNGN_SHALLOW_WATER;
+ // Remove blood stains as well
+ env.map(*rad).property &= ~(FPROP_BLOODY);
+ }
+ // We can also turn shallow water into deep water, but we're just going
+ // to skip cases where there is something on the shallow water.
+ // Destroying items will probably annoy people and insta-killing
+ // monsters is clearly out of the question.
+ else if(!actor_at(*rad)
+ && igrd(*rad) == NON_ITEM
+ && ftype == DNGN_SHALLOW_WATER)
+ {
+ grd(*rad) = DNGN_DEEP_WATER;
+ }
+ }
+
+ if(spawned_count>0)
+ {
+ mprf("%s grow in the rain.",
+ (spawned_count > 1 ? "Some plants" : "A plant"));
+ }
+
+ return spawned_count;
+}
+
+
+void corpse_spores()
+{
+ radius_iterator rad(you.pos(),LOS_RADIUS, true,true,true);
+
+ for( ; rad; ++rad)
+ {
+ for(stack_iterator stack_it(*rad); stack_it; ++stack_it)
+ {
+ if(stack_it->base_type == OBJ_CORPSES
+ && stack_it->sub_type == CORPSE_BODY)
+ {
+
+ create_monster(mgen_data
+ (MONS_GIANT_SPORE,
+ BEH_FRIENDLY,
+ 0,
+ 0,
+ *rad,
+ MHITNOT,
+ MG_FORCE_PLACE));
+
+
+ if(mons_skeleton(stack_it->plus))
+ turn_corpse_into_skeleton(*stack_it);
+ else
+ destroy_item(stack_it->index());
+
+ break;
+ }
+
+ }
+
+ }
+}
+
+typedef std::pair<monsters *, int> monster_cost;
+
+struct lesser_second
+{
+ bool operator()(const monster_cost & left, const monster_cost & right)
+ {
+ // explicitly making this comparison unstable. I'm not clear on the
+ // complete implications of this but it should be ok for a heap.
+ if(left.second == right.second)
+ return coinflip();
+
+ return left.second < right.second;
+ }
+
+};
+
+bool evolve_flora()
+{
+ int needed_fruit = 2;
+
+
+ std::priority_queue<monster_cost,
+ std::vector<monster_cost>,
+ lesser_second > available_targets;
+
+ int points=15;
+ int plant_cost = 10;
+ int toadstool_cost = 1;
+ int fungus_cost = 5;
+
+ radius_iterator rad(you.pos(), LOS_RADIUS, true, true, true);
+ for ( ; rad; ++rad)
+ {
+ monsters * target=monster_at(*rad);
+ int cost=0;
+
+ if(!target)
+ continue;
+
+ switch(target->mons_species())
+ {
+ case MONS_PLANT:
+ cost = plant_cost;
+
+ break;
+ case MONS_FUNGUS:
+ cost = fungus_cost;
+ break;
+
+ case MONS_TOADSTOOL:
+ cost = toadstool_cost;
+ break;
+
+ };
+ if(cost!=0)
+ available_targets.push(std::pair<monsters * ,int>(target,cost));
+ }
+
+ if(available_targets.empty() )
+ return false;
+
+ int rc;
+ int available_count;
+
+ rc = prompt_invent_item("Use which fruit (must have at least 2)?",
+ MT_INVLIST, OSEL_SOME_FRUIT, true, true, true,
+ '\0', -1, &available_count);
+
+ if(prompt_failed(rc))
+ return false;
+
+ dec_inv_item_quantity(rc, needed_fruit);
+
+ int plants_evolved = 0;
+ int toadstools_evolved = 0;
+ int fungi_evolved = 0;
+
+ while(!available_targets.empty() && points > 0)
+ {
+ monster_cost current_target = available_targets.top();
+
+ monsters * current_plant = current_target.first;
+ available_targets.pop();
+
+ // can we afford this thing?
+ if(current_target.second > points)
+ continue;
+
+ points-=current_target.second;
+
+ int base_species = current_plant->mons_species();
+ coord_def target_square = current_plant->pos();
+
+ // remove the original plant
+ monster_die(current_plant, KILL_MISC, NON_MONSTER, true);
+
+ monster_type new_species;
+ switch(base_species)
+ {
+ case MONS_PLANT:
+ new_species = MONS_OKLOB_PLANT;
+ plants_evolved++;
+ break;
+
+ case MONS_FUNGUS:
+ new_species = MONS_WANDERING_MUSHROOM;
+ fungi_evolved++;
+ break;
+
+ case MONS_TOADSTOOL:
+ new_species = MONS_FUNGUS;
+ toadstools_evolved++;
+ break;
+ };
+
+ rc=create_monster(mgen_data(new_species,
+ BEH_FRIENDLY, 0, 0, target_square,
+ MHITNOT, MG_FORCE_PLACE, GOD_FEAWN));
+
+
+ // we can potentially upgrade toadstools a second time
+ if(base_species == MONS_TOADSTOOL && rc != -1)
+ available_targets.push(monster_cost(&env.mons[rc], fungus_cost));
+ }
+
+ // messaging...
+ if(plants_evolved > 0)
+ {
+ mprf("%s can now spit acid.",
+ (plants_evolved == 1 ? "A plant" : "Some plants"));
+ }
+
+ if(toadstools_evolved>0)
+ {
+ bool plural = toadstools_evolved > 1;
+ std::string plural_s = toadstools_evolved > 1 ? "s" : "";
+
+ mprf("%s toadstool%s gain%s stability.", (plural ? "Some" : "A"),
+ plural_s.c_str(), plural_s.c_str() );
+ }
+
+ if(fungi_evolved > 0)
+ {
+ bool multiple = fungi_evolved > 1;
+ mprf("The fungal %s can now pick up %s mycelia and move.",
+ (multiple ? "colonies" : "colony"),
+ (multiple ? "their" : "its"));
+ }
+
+ return true;
+}
diff --git a/crawl-ref/source/spells2.h b/crawl-ref/source/spells2.h
index a30756f0c1..5401bf5fc1 100644
--- a/crawl-ref/source/spells2.h
+++ b/crawl-ref/source/spells2.h
@@ -28,6 +28,20 @@ int detect_traps(int pow);
void cast_refrigeration(int pow);
void cast_toxic_radiance(void);
void drain_life(int pow);
+
+int fungal_bloom();
+int create_plant(coord_def & target);
+//bool plant_from_fruit();
+bool sunlight();
+
+
+bool plant_ring_from_fruit();
+
+int rain(coord_def & target);
+void corpse_spores();
+bool evolve_flora();
+
+
bool restore_stat(unsigned char which_stat, unsigned char stat_gain,
bool suppress_msg, bool recovery = false);
diff --git a/crawl-ref/source/tilepick.cc b/crawl-ref/source/tilepick.cc
index 800fb0f650..50ccb40183 100644
--- a/crawl-ref/source/tilepick.cc
+++ b/crawl-ref/source/tilepick.cc
@@ -2511,6 +2511,8 @@ int tileidx_feature(int object, int gx, int gy)
return TILE_DNGN_ALTAR_BEOGH;
case DNGN_ALTAR_JIYVA:
return TILE_DNGN_ALTAR_JIYVA;
+ case DNGN_ALTAR_FEAWN:
+ return TILE_DNGN_ALTAR_FEAWN;
case DNGN_FOUNTAIN_BLUE:
return TILE_DNGN_BLUE_FOUNTAIN;
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index 8e348b24f4..77ab5a2618 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -3019,6 +3019,7 @@ bool is_feature(int feature, const coord_def& where)
case DNGN_ALTAR_LUGONU:
case DNGN_ALTAR_BEOGH:
case DNGN_ALTAR_JIYVA:
+ case DNGN_ALTAR_FEAWN:
return (true);
default:
return (false);
@@ -4710,11 +4711,11 @@ void init_feature_table( void )
break;
case DNGN_ALTAR_LUGONU:
- Feature[i].colour = GREEN;
+ Feature[i].colour = MAGENTA;
Feature[i].dchar = DCHAR_ALTAR;
Feature[i].flags |= FFT_NOTABLE;
Feature[i].map_colour = DARKGREY;
- Feature[i].seen_colour = GREEN;
+ Feature[i].seen_colour = MAGENTA;
Feature[i].minimap = MF_FEATURE;
break;
@@ -4736,6 +4737,15 @@ void init_feature_table( void )
Feature[i].minimap = MF_FEATURE;
break;
+ case DNGN_ALTAR_FEAWN:
+ Feature[i].colour = GREEN;
+ Feature[i].dchar = DCHAR_ALTAR;
+ Feature[i].flags |= FFT_NOTABLE;
+ Feature[i].map_colour = DARKGREY;
+ Feature[i].seen_colour = GREEN;
+ Feature[i].minimap = MF_FEATURE;
+ break;
+
case DNGN_FOUNTAIN_BLUE:
Feature[i].colour = BLUE;
Feature[i].dchar = DCHAR_FOUNTAIN;