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 consumedint 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-fungiif(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 itif(mons_skeleton(j->plus))turn_corpse_into_skeleton(*j);elsedestroy_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 LOSif(seen_corpses>1){mprf("%s grow from nearby corpses.", base.c_str());}elsemprf("%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 itdungeon_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();elsebacklight_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// distancesvoid 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);elsedistances.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 LOSfor( ; 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);elsepoint_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 createdint 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/192if(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 waterif(ftype>=DNGN_FLOOR_MIN && ftype<=DNGN_FLOOR_MAX){grd(*rad) = DNGN_SHALLOW_WATER;// Remove blood stains as wellenv.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);elsedestroy_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 plantmonster_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 timeif(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.");elsempr("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, // 246ABIL_RENOUNCE_RELIGION = 250 // 250
ABIL_HARM_PROTECTION_II, // 252ABIL_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 ringunsigned min_spawn = 2;// highest radius we will allow. 8 is LOSconst int max_radius = LOS_RADIUS;seen_count = 0;// Just want to associate a point with a distance here for conveniencetypedef 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.");elsempr("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;