Also fix potions of porridge's menu colour being yellow for Mummies.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@10355 c06c8d41-db1a-0410-9941-cceddc491573
DKISJVKAJJK2SVZSXGJ37K5SOVTDARVX6RPK3MAKVNGC36Z4YAZQC
PJFHS5U26APU37C7UFRK4NEDHCMAKBRBX36CFL33P6D4E237ZKSQC
GJBUM2B6VECYVSAWUQAG64MDJ4MUHTACSTDDVPEKRDBGBX4HPQLQC
LGYD5PPNLYMI22NFESV7BJ6OI7WYFX6UXYSLMBX4R4MUNQQZI65QC
GGSRKIVJJUMCOYNMOCRDLP7Z2JIWHZZY2MPVUNM6VL5WVQ3APUOAC
UNXGLYIYBZMWR6GX3LRNAA2UPZD7MNBH7TYGYQTTFHPCJRDZXWPQC
4ZP2AEI7WCXZCK2KFWGKCUMEOG4GEB5JM32YVQGH7PMTQRFS5FYQC
5WVUTEZLEZEML54CKPR6GACQBYY3EMVNXMLJOREN6SSEUZGC47AQC
L57WEZMVIHTFRLWVIPWOIOWHCF2I53TVUVYJ2V6IZ64R56FVTZZAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
LOJYD6QZRNLNDDZJKVBMKQIBPTKSRN2ETCYGNVV47M7L3QLUJUJAC
NG53L53MSFQZAIVKHG54IEMXNJ33AYVPF2OZM4MMJFDKEJCGPBSAC
WHY6LRRJ5T2NSBE3IUCR4X3TOAH7TTK5NPUPUIFT7TPNJ6J4HBDAC
J7VTRSN2BLWL7QJMHG73LBUOUL6WNNT2ZAVEDMGYB6SDX3L5GGYQC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
H6ZPCZ7HN5UZTOS6QFAE4WUJR6BCDU4OB7ONQ7ELXIK65ASPJOTAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
YF6CE2VBFK6K4V34PKBVYVQUTJRDDDCF2M5RMUGW6V6N2M4SUPLAC
22ZPAVDZLX4R66AODFVA56HH4T3XU62UUON6LYZG3GGTXCTD3YVAC
JW2KRJHES33W7UTWZ6NDO4TLMK4EFU4HKZXBWR2UJOMPCCOTR4CQC
DGFWFFHCEBQK2TSMHN7RYPRNRVGEAYXM7GYJ4L37Z2ZNSIIUWQ4QC
DF5LOTJFSXOT7UBDHLBDLGA22OY4L7ZF6CVFP6H3KL4N5CHB5C5QC
XDJGQNFELURGXMUAOOVALQMSLAJVHMG63KPKVA33HTJFVZROGZ4AC
SA36K3OJ75PNVR2F4QAPNU2LUGFTVHNIAZVSXCBXBT2NLW2RTUPAC
H55P74Y6NHAPF3VPWXIAP7DODA3DV7NCT3SK6VVLWNNWZ7JIMBTAC
U3KGUJJQWQORJIIFH3ADVNIEEX5HOX6KEOXO7DJSL7L3Z6GG3PAQC
RBAGQ2PB7V5YAM5KSHSZR2E3MLKDSRVM5XYGI2TIXP5QMVBOQHDQC
NUYXKJP5YXHRDUQW5QW7UC3D5U3VPANIOZAOHFCPWMSRYGMA3GCAC
PSCYVKJ7DGXAL3V5U4O6AJTRV6Q3N3SHQWAZ73VIPRTE4W64F2XAC
UZ6N6HOUPGVSPC5NQROEEDWMEGJA5XUWUY2AKH5QG65AZ25PVXDAC
AFE345BJ7IX2YYYX3I5I6RYLXNWJCFE4WMH6F5JMIR6X7WUP75CAC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
TPZWAV3USKO7RX4IGHLZKVPRN36K33PJPSZYL6FZMX4XBHTYOQYAC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
5MK5JJ2KDIJKKASJBW56K7WPDS3SXKBFON7D4BU73Q3BGNDYPDVQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
5TG5LXU4DX65KMWCZ7YJHOB3VAETQAVBUHEUSQTPMA327XV2HQWAC
4NNN5LKBZLDXMDN2322PBG7WFHF4TWOEMVODSO7BXXCM3AZKQN4QC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
LY7DLLD7IKL6ZQPVROLDJ46XRM5CMAAEBRFDKJ4M53CPC5GFCGSQC
IP4A3VRYFYIVLRUAU4DF4KDNP6E4UISWJX3LI2F4EGSSMIJYRJXAC
ZCSO4HPSQOVAOPCF3QBRAFHHS6QP65LVRZ4WE3E4IMCGGDTDIOOQC
4GYZYBY7FFORRNPIEFTV4ZM2C7Z6D2KTQOM537ZCC2YBXT2TNSHAC
NGW2XPEX2XRK3CYC37DNUZSSB5IQLECMKRB6NX257I2X3G35LMPAC
57E4T664CXRFPVW2BZBYI33UIHFS4PSEZURZYPSYSZK5DRLJJ5CQC
EQDLV5OMIFO5ZPYNE27VQLLZEIRMSALGNEFWVSCFXJN22A43GCWQC
TV3ZC6WOZKSQQJQN26JIVKCHK6UK7WMDBYZDUYRWEAZ4JB4YVNAAC
JV7RFSC2MQ3X2ZZB24SLNIWRWGYVLEMPG3G3ZVZHON7S3HXC7OZQC
UXYQEI6GLKABRWITOJVAFK5QETIMVRRBJO3DCHFUAC5A6YTVI64QC
WLBSQ2ESMTOPKZUFZL6KPKKTRPW5Y2LDJO6F34K6WSNE7H77KFKAC
CQ24AVAI6SW3AHTIDMLPSTRRBEU6FHRF5I5FD6G5QIYE6PO4BQMQC
XZTGZ7MOPX7ZOHQ4IQPXOHGUH5WPRNOYAQ6IQOMZDZYNVSAYFTNAC
KZWOHH536QBWAPWROR32EOXEK5LRY6I5VHVL7HHI7GOFKCMQTCSQC
OONYLF4DAPLIYLBNNRW74IVT5BBTWI4XHQBXSNSPVRX3FTKJBTRAC
HIRKGUMNJPWKSVTR6TVBPD3MWNA63CEHCLCIPWEMGDFHVB3NPLDQC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
EPRT5LWIQULNZW5XP3ZMAGMEZ5KC4PQTW2BDTEXNH367LDL5NE5QC
33QMQXTWFPR4LUBHEWTFGR74L5QJJDQTT5JZRBFTPDQBPXIHQIUQC
TDXC5VWE2B5VVGNXEF7V5E6UCKCSAIZVLJXFIOQ4G4A2PK5L7BBQC
VXWDDKBETFFMU7LMGN4UGB74NNKXW456KFJMWWEVOTWH35NZZZHAC
W2KRIXSCRJPS6WDIYTHVF5IRMF3V5DWECRAWUPVTB7VZ6A2BLD4QC
QKV7G4ZWQTIYP3WYIJMSYZOYPIOXM5EJCQMNJKPNW5O3KLSCG5NAC
UADYVV3UD5ERJTZZJGY4EUQ4NJ2JSBG7YYUJ75ZRBIXRQXQKOJPAC
5BJPWUPLJFS34FUTFJVKA4A52YMIGV6EWDXLNSDCWBJWBGVSQFGQC
KAOE5HB3THUKVGFZRO5EZESHEB3Q34WUO5DFMLWIKOBF47LZTIYAC
7YUGK5Q64KG5O7GJGTUBRRLHAHBCJ5YOE23YUPT6UBKUSB67CYAQC
FCZSQBKDNMJZRJS2LWQQWLUFGOXSKXDJZQIHC7L5S7HXCXQPOMMAC
6GT5JAWOIIL4SQ5MWIID6ZVO3KKQFWDQDZNVFHZ6DNK5QCBXJ4UAC
HRBCN6CYP36HGNB3IDETM2UXWSANJM3QZA2HAM4P4B62QB4KBKXAC
CH7JECYYH35H4TRHRNRTRJCQTQLZ2WRH62TKV72SUIU2RTK5OH7AC
WXSNNK2RXP3DQFAEQGQUZJHFWXJC7ZKG2WURZGL566UDM4YXFSWQC
WZNB427K3EUNV3FMVXLQTM4UIHER4FIKQXWLUUXRNQC3OQ33VQYAC
GE5BVIRQVT4PARCIODIUK5UXW4AYLFR55UCA4J2DIJU4AMYWQIZAC
LL4FGIKVSJDKSCBNGS4ANIJGVTR6Q3LZPAJWGX3MY6V42P3BMZ6QC
P2ZCF3BBG523ZEOD6XQA4X5YEHBTWH3IM33YVHXP2SQ5POXZIH4QC
YBWBS2W5H36C46SVIDCHYCUDONUUSBBZLJHCQY2ESO3ZBFNBWWEQC
}
// 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;
}
}
// 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;
}
"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"
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;
};
ABIL_HARM_PROTECTION_II, // 246
ABIL_RENOUNCE_RELIGION = 250 // 250
ABIL_HARM_PROTECTION_II, // 252
ABIL_RENOUNCE_RELIGION = 260 // 260
// 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
::adjacent_iterator adj(corpse.pos);
// 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;
}
};
int spawned_count=0;
for ( ; adj; ++adj)
// 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)
{
// 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))
{
radius_points[bound - 1].push_back(current.first);
}
for (adjacent_iterator i(current.first); i; ++i)
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);
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());
if (mushroom != -1)
spawned_count++;
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++;
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;
}
// 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);
}
// 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;
}
// 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.
// 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;
int success_count = binomial_generator(current_trials, trial_prob);
"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"}
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;
// 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);
}
// 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},
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));
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;