cleanups added by me.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10442 c06c8d41-db1a-0410-9941-cceddc491573
T3R2D5M4YSC6NFZ6YO3AEG4VWORECX72CHUM5UBADV4UPAAZG7WQC
RQUK5MTEO75EINGOW75UCH463UZVCQXJA3WG4YTUGYTDHN2ORYTAC
JLPJ6G5QJTAHFF5RINBFO4UBV44WD5FVTPOO5RGLMIRILRKGTA6AC
EXPQHDUJDH3J47STNSABE3SNNZWCVRSMANAZVBYQY3W7E2KQUP7AC
KIAIGHATPEOUGEWQXS2IR4635E53WPEENC5UJ3I3MASTJ7SHBU5AC
FW3PIRSTFPX7VNUAVASCBB7VPYJHGPRSHXC4S6RXCLSLJMGWS6QQC
SDZYI64673GM344DHCDQSWOJTX7EVW6VXRYA7DHGFVNYR5J5RYGQC
IXO5VNCDYDYI46Y5QWNJMD7TURKYO526CJA4TIMH5PDDAYHMK5YQC
MONYKYOVBBZBJQNDH4QNDCC4TKFPTGTDVTYWVSC24K7DGOMJ7MEQC
A6DIDV2RENPSX6CQ7NJMTDPPGRLQXMPLX3WXLZUG4QTCZHLUSFIQC
5WVUTEZLEZEML54CKPR6GACQBYY3EMVNXMLJOREN6SSEUZGC47AQC
RDS3NSFOLBAEGHBFEQ4TODN5QSJ2DLDM2SIVVUMA6IVRHAZLRVKQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
PFEJ4LMDNEKLMGRCMWQ7EIRVU4JMYGICI4G7X4WVWOROVXQCBZ7QC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
DKISJVKAJJK2SVZSXGJ37K5SOVTDARVX6RPK3MAKVNGC36Z4YAZQC
UU5JLKAKZSTWFQJLUTPQZJ3ZQY7BEU2KROZETUNSKC6LFG6XVSZQC
NDKS43E4XVAU3FDY276OF5JW7E5SF6F5EJD2XLV5JLKZ5W6N554QC
KHYL5UOMGS7HLVEQNVIOYWNP2ZH2THKZ5CGGIZFI6NN5V2YD3YEAC
SA36K3OJ75PNVR2F4QAPNU2LUGFTVHNIAZVSXCBXBT2NLW2RTUPAC
NJW656RNYNY7VZVOK52U3U5AVJR2E33N4I4KBEEJY3MZIPG35QCAC
SQFABNVS6OSVRBMJAIV6KIIZW2TYIOQ5BZGLPYJ6BPFD2HMRH4MAC
UFKLHUYL7WAQ3CI3D42T4C6KBGAUR63DSQAUQTTZG7GJMXSCVJWAC
I2ZR5QRPQEDTO5V5ILIJUA5KIEOGJY3BRHRNPXS5BJG7GXTBXJSQC
4A3Y2BLAYXUFAAT6EK5GYT5ZIMQXQX64ZVJCKNPGEZY7IGTUWWIAC
ENQRCBNN7MKP6FFQAZ62FYXEVWCRJB5747LTYMZGRGINXQL6HT5AC
IGNQ3YSGMW6NG7GPYYXWXW6XPM4YWKNUJ5IU55QCWZ2AEHHO3L3QC
GK6AOQQV6RU2F6FQRT6O62FD633MH6SQNPLRL4U3KQ6UMXX6UQLQC
B47CVG54WZNP3CQ7HOTKTWQA26GCOIKQADX2EK4ZOEYRRQ4T5W2AC
JEW6ECARJC535IBLAUTKDKRRMRX26MRH3PHR3XWICCCBYWRTWLYAC
NLQNXH3SVJ52CWXEV35FSSZP32VHC4QFGN3HINF4KO5GZHZMOBKQC
CQD3RU44235F3CYFDZBC3JRT2H3AE7PXJNVBBMCRF65Q5LPA3EHQC
OWERGKLVPNPGIIS23FZ7ZDOBWUIXCKYAFG3URXU75JAUDX3N5ENAC
6PAG7GHXHIYXJPPTEK4KZQZT4CL2SJDAGTVIUDB4KK66PVSTWUMAC
IYMUEJG2VI3TV7HVAZIU2L4DVQY7VNSIFVV4I3QIVP7H66EVJISAC
F2X5SIIXWXXEL2FVMYDC4GL2ODZ4O7RA46U66VGJ5K2EZHHAQGUAC
U3KGUJJQWQORJIIFH3ADVNIEEX5HOX6KEOXO7DJSL7L3Z6GG3PAQC
LOC46PHUFJ3MDWOYUEH4R24TGGE4ADG7C44O2ZNB4EBF2YD6VX7QC
4RWYH3CQPAAOK2OM2POLRTY6A5CPHYZJ4B55VAVSCWTSCOPWMF6AC
ASH5CK6CPBKMLGGIRJ5GKTWMS5W3OBVHTL66RTYZIPFM6KFBYA3QC
B4YYKEZVHLGRFAHEP5ATEV4MYRPGXQPWTUXGGG7ITZH3ZGV3VMBQC
AA5RRYINGLYJHZNRSQUHI6K4GIT4KZBFRFZ43EJJPOMJAMXWULMAC
KZWOHH536QBWAPWROR32EOXEK5LRY6I5VHVL7HHI7GOFKCMQTCSQC
OONYLF4DAPLIYLBNNRW74IVT5BBTWI4XHQBXSNSPVRX3FTKJBTRAC
HIRKGUMNJPWKSVTR6TVBPD3MWNA63CEHCLCIPWEMGDFHVB3NPLDQC
UADYVV3UD5ERJTZZJGY4EUQ4NJ2JSBG7YYUJ75ZRBIXRQXQKOJPAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
7YUGK5Q64KG5O7GJGTUBRRLHAHBCJ5YOE23YUPT6UBKUSB67CYAQC
TWYB52EBZRZEABTKQ7D6OWKMDKIGACWDUGNH5LP47QX4SKC3SFYAC
FAVME2A2U4OUKN2BTISAG5PCI5Y4BN6YVFKCSQQHHZLEVVVF4LKAC
S2FTVAH7NLTLI5CEINIAMJIW3PTZUE22APSK633Y4PLDZ4L6UK4QC
stop_running();
}
}
}
void feawn_neutralise(monsters* monster)
{
if (you.religion == GOD_FEAWN && mons_is_plant(monster)
&& !mons_is_summoned(monster)
&& !mons_wont_attack(monster)
&& !testbits(monster->flags, MF_ATT_CHANGE_ATTEMPT))
{
if (!player_under_penance())
{
// We must call remove_auto_exclude before neutralizing the
// plant because remove_auto_exclude only removes exclusions
// it thinks were caused by auto-exclude, and
// auto-exclusions now check for ATT_HOSTILE. Oh, what a
// tangled web, etc.
remove_auto_exclude(monster, false);
monster->flags |= MF_ATT_CHANGE_ATTEMPT;
feawn_neutralise_plant(monster);
void corpse_spores()
// Destroy corpses in the player's LOS (first corpse on a stack only)
// and make 1 giant spore per corpse. Spores are given the input as
// their starting behavior; the function returns the number of corpses
// processed.
int corpse_spores(beh_type behavior)
// Remove the original plant.
// XXX: Why do we destroy the old and create a new plant
// rather than simply upgrade the old plant?
monster_die(current_plant, KILL_MISC, NON_MONSTER, true);
rc = create_monster(mgen_data(new_species,
BEH_FRIENDLY, 0, 0, target_square,
MHITNOT, MG_FORCE_PLACE, GOD_FEAWN));
current_plant->upgrade_type(new_species, true, true);
current_plant->god = GOD_FEAWN;
current_plant->attitude = ATT_FRIENDLY;
current_plant->flags |= MF_CREATED_FRIENDLY;
case GOD_ZIN: // in contrast to TSO, who doesn't mind martyrs
case GOD_ZIN:
// Converted allies (marked as TSOites) can be martyrs.
if (victim->god == GOD_SHINING_ONE)
break;
case GOD_FEAWN: // plant god only cares about plants
if (!mons_is_plant(victim))
break;
// to be implemented -CAO
const god_type god = GOD_FEAWN;
// We have 3 forms of retribution, but players under penance will be
// spared the 'you are now surrounded by oklob plants, please die' one.
const int retribution_options = you.religion == GOD_FEAWN ? 2 : 3;
switch (random2(retribution_options))
{
case 0:
// Try and spawn some hostile giant spores, if none are created
// fall through to the elemental miscast effects.
if (corpse_spores(BEH_HOSTILE))
{
simple_god_message(" produces spores.", GOD_FEAWN);
break;
}
case 1:
{
// Elemental miscast effects.
simple_god_message(" invokes the elements against you.", GOD_FEAWN);
spschool_flag_type stype = SPTYP_NONE;
switch (random2(4))
{
case 0:
stype= SPTYP_ICE;
break;
case 1:
stype = SPTYP_EARTH;
break;
case 2:
stype = SPTYP_FIRE;
break;
case 3:
stype = SPTYP_AIR;
break;
};
MiscastEffect(&you, -god, stype, 5 + you.experience_level,
random2avg(88, 3), "the wrath of Feawn");
break;
}
case 2:
// We are going to spawn some oklobs but first we need to find
// out a little about the situation.
std::vector<std::vector<coord_def> > radius_points;
collect_radius_points(radius_points,you.pos(),env.no_trans_show);
unsigned free_thresh = 30;
mgen_data temp(MONS_OKLOB_PLANT,
BEH_HOSTILE, 0, 0,
coord_def(),
MHITNOT,
MG_FORCE_PLACE,
GOD_FEAWN);
// If we have a lot of space to work with (the circle with
// radius 6 is substantially unoccupied), we can do something
// flashy.
if (radius_points[5].size() > free_thresh)
{
int seen_count;
temp.cls = MONS_PLANT;
place_ring(radius_points[0],
you.pos(),
temp,
1, radius_points[0].size(),
seen_count);
temp.cls = MONS_OKLOB_PLANT;
place_ring(radius_points[5],
you.pos(),
temp,
random_range(3, 8), 1,
seen_count);
}
// Otherwise we do something with the nearest neighbors
// (assuming the player isn't already surrounded).
else if (!radius_points[0].empty())
{
unsigned target_count = random_range(2, 8);
if (target_count < radius_points[0].size())
prioritise_adjacent(you.pos(), radius_points[0]);
else
target_count = radius_points[0].size();
unsigned i = radius_points[0].size() - target_count;
for(; i < radius_points[0].size(); ++i)
{
temp.pos = radius_points[0].at(i);
temp.cls = coinflip() ?
MONS_WANDERING_MUSHROOM : MONS_OKLOB_PLANT;
create_monster(temp,false);
}
god_speaks(god, "Plants grow around you in an ominous manner.");
return (false);
}
}
}
return (true);
}
return (false);
}
static bool _feawn_plants_on_level_hostile()
{
for (int i = 0; i < MAX_MONSTERS; ++i)
{
monsters *monster = &menv[i];
if (monster->alive()
&& mons_is_plant(monster))
{
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Plant hostility: %s on level %d, branch %d",
monster->name(DESC_PLAIN).c_str(),
static_cast<int>(you.your_level),
static_cast<int>(you.where_are_you));
#endif
// You can potentially turn an oklob or whatever neutral
// again by going back to Feawn.
if (testbits(monster->flags, MF_ATT_CHANGE_ATTEMPT))
monster->flags &= ~MF_ATT_CHANGE_ATTEMPT;
monster->attitude = ATT_HOSTILE;
monster->del_ench(ENCH_CHARM, true);
behaviour_event(monster, ME_ALERT, MHITYOU);
// For now WAS_NEUTRAL stays.
static bool _feawn_plants_on_level_neutral()
{
for (int i = 0; i < MAX_MONSTERS; ++i)
{
monsters *monster = &menv[i];
feawn_neutralise_plant(monster);
}
return (true);
}
static bool _feawn_plants_neutral()
{
if (apply_to_all_dungeons(_feawn_plants_on_level_neutral))
{
mpr("The plants of the dungeon cease their hostilities." , MSGCH_GOD);
return (true);
}
return (false);
}
}
void feawn_neutralise_plant(monsters *plant)
{
if (plant->type != MONS_OKLOB_PLANT
&& plant->type != MONS_WANDERING_MUSHROOM
&& !testbits(plant->flags, MF_ATT_CHANGE_ATTEMPT))
{
return;
}
if (you.can_see(plant))
{
mprf(MSGCH_GOD, "%s ignores you.",
plant->name(DESC_CAP_THE).c_str());
}
plant->attitude = ATT_GOOD_NEUTRAL;
plant->flags |= MF_WAS_NEUTRAL;
return (mons_genus(species) == MONS_JELLY
|| mons_genus(species) == MONS_GIANT_EYEBALL
|| species == MONS_GIANT_SPORE
|| species == MONS_GIANT_ORANGE_BRAIN);
return (mons_class_is_slime(species));
case GOD_FEAWN:
return (mons_class_is_plant(species));
// fight back.
if (mons_class_flag(mon->type, M_NO_EXP_GAIN))
// fight back, so don't bother having them do so. If you
// worship Feawn, create a ring of friendly plants, and try
// to break out of the ring by killing a plant, you'll get
// a warning prompt and penance only once. Without the
// hostility check, the plant will remain friendly until it
// dies, and you'll get a warning prompt and penance once
// *per hit*. This may not be the best way to address the
// issue, though. -cao
if (mons_class_flag(mon->type, M_NO_EXP_GAIN)
&& mon->attitude != ATT_FRIENDLY)
{
return (mons_genus(mon->type) == MONS_JELLY
|| mons_genus(mon->type) == MONS_GIANT_EYEBALL
|| mons_genus(mon->type) == MONS_GIANT_ORANGE_BRAIN);
return (mons_class_is_slime(mon->type));
}
// Monsters Feawn cares about - plants and fungi except for giant spores
// and toadstools.
bool mons_class_is_plant(int mc)
{
return (mc != MONS_GIANT_SPORE
&& mc != MONS_TOADSTOOL
&& (mons_genus(mc) == MONS_PLANT
|| mons_genus(mc) == MONS_FUNGUS));
}
bool mons_is_plant(const monsters *mon)
{
return (mons_class_is_plant(mon->type));
// 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)
int place_ring(std::vector<coord_def> & ring_points,
coord_def & origin,
mgen_data & prototype,
int n_arcs,
int arc_occupancy,
int & seen_count)
// minimum number of mushrooms spawned on a given ring
unsigned min_spawn = 2;
int target_amount = ring_points.size();
int spawned_count = 0;
seen_count = 0;
std::vector<int> arc_counts(n_arcs, arc_occupancy);
for (unsigned i = 0;
spawned_count < target_amount && i < ring_points.size();
i++)
{
int direction = _arc_decomposition(ring_points.at(i)
- origin, n_arcs);
// 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);
// larger radii. We will visit points in order of increasing euclidean
// distance from the origin (not path distance).
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
// We're done here once we hit a point that is farther away from the
// 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))
while (current.second > current_thresh)
// We don't include radius 0. This is also a good place to check if
// the squares are already occupied since we want to search past
// occupied squares but don't want to consider them valid targets.
if (current.second && !actor_at(current.first))
radius_points[current_r - 1].push_back(current.first);
// 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;
temp.second = local.abs();
}
// 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)
{
// minimum number of mushrooms spawned on a given ring
unsigned min_spawn = 2;
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++)
unsigned max_size = 0;
for (unsigned i = 0; i < LOS_RADIUS; ++i)
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;
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);
int spawned_count = place_ring(radius_points[chosen_idx], corpse.pos, temp,
n_arcs, 1, seen_count);
if (mushroom != -1)
{
spawned_count++;
if (see_grid(radius_points[chosen_idx].at(i)))
seen_count++;
}
}
return 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).
// 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).
// 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.
// 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.
// 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)
// Feawn worshippers can fire through monsters of the same alignment.
// This means Feawn-worshipping players can fire through allied plants,
// and also means that feawn worshiping oklob plants can fire through
// plants with the same attitude.
bool originator_worships_feawn=false;
// Checking beam_source to decide whether the player or a monster
// fired the beam (so we can check their religion). This is complicated
// by the fact that this beam may in fact be an explosion caused by a
// miscast effect. In that case the value of beam_source may be negative
// (god induced miscast) or greater than NON_MONSTER (various other miscast
// sources). So we check whether or not this is an explosion, and also the
// range of beam_source before attempting to reference env.mons with it.
// -cao
if (!is_explosion && this->beam_source == NON_MONSTER)
originator_worships_feawn = (you.religion == GOD_FEAWN);
else if (!is_explosion && beam_source >= 0 && beam_source < MAX_MONSTERS)
originator_worships_feawn = (env.mons[beam_source].god == GOD_FEAWN);
if (!is_enchantment()
&& attitude == mon->attitude
&& originator_worships_feawn
&& mons_is_plant(mon))
simple_god_message(" protects your plant from harm.", GOD_FEAWN);
{
// FIXME: Could use a better message, something about dodging that
// doesn't sound excessively weird would be nice.
mprf(MSGCH_GOD, "Feawn protects %s plant from harm.",
attitude == ATT_FRIENDLY ? "your" : "a");
}
// We'll say giant spore explosions don't trigger the ally attack conduct
// for Feawn worshipers. Mostly because you can accidentally blow up a
// group of 8 plants and get placed under penance until the end of time
// otherwise. I'd prefer to do this elsewhere but the beam information
// goes out of scope. -cao
if (you.religion == GOD_FEAWN && flavour == BEAM_SPORE)
conducts[0].enabled = false;