the various chaos effects, to make chaos brand less powerful.
Added berserk and miscast effects for chaos effects, plus chaos weapons will occasionally give a message-only miscast effect if it would otherwise have done nothing.
Added several effects that have a chance of happening to an attacker every time it uses a chaos brand/AF_CHAOS: dropping through temporary a shaft, having the stairs move out from under them, the weapon making a loud noise.
Monsters killed by a chaos weapon/AF_CHAOS have a chance of immediately turning into a zombie (assuming the monster didn't leave a corpse; chaos effects don't (yet) use up corpses).
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7804 c06c8d41-db1a-0410-9941-cceddc491573
SOCJXX6MMOXLBEWBID4QN5FW2YNYULNNN7K3IRL7RSWK5EUNAZLQC J4C7374KUQQZXUJNL22HGMZUCISCBLNPGO2FHQKS7STOTPLNY2WAC HF3CSMAKAOHWJA6CU4PTZSM6G7LVUV5MHE4ZWGEHCCNRBU43UUUQC 43ZYZLF7JFSCKTLQMBYIHNGGDMN3OBDX4X4PLXLIUFD5DPYSDCXQC BY6T2KQBNMPI54QVEI4QRI6GOCMKAPDEY543VTX7PWXFNNQF25MQC CCRQESB4ADT4WA7FGLNZZXAJ6G5QMCTYCZIWORBN45P6ZPILC34AC J4CLLL5AUB264TTFGM66PFWF4KL6LXCQ4TWATOXHNLDQ7OIVTMDQC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC UKN6HTZXDUUOWKNWNKWPHKGUGL474JIAQN5JU3DM3DU26WGMNP4AC 3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC AREBCIU2RU2RNHBWD4GARWEBKSL7HDFGDLII22H56OJO2AQUOMLQC SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC JGTKZP6HCXDHEJLAONL3FNLNIZ7MUBYKXZ4CRTL46YC53TW7CBEAC 5TG5LXU4DX65KMWCZ7YJHOB3VAETQAVBUHEUSQTPMA327XV2HQWAC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC FLAGBNUNSIQNFDN53CDWABJRTTFWDL4PG34AI474ZKPXDEPYHOAQC VCG3BRIYRTNNWYC3LOXD6KFGXOX37HAFW2HNV7WXVG2V7EUHLDZQC BWAQ3FHBBM6G3K3KYP75CRTR343RDQZJRYX5ZGYUEXYBAC3APDLAC R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC ACKNLTFL2RI3PMRWLNRVLRWGQAMLRFKNGNS5LED6NFE5GVGFIHFAC QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC 77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC OSRZEPPGBIMSZBWIVBTZTTIMV6TEUGVZRZ5AI2ZJW7CVZZQBUIMQC IHLGRQOXBGZE3COMNKBKMIDQPJ7HRY4PT74ZUN7HD4ADDPDOX2NQC OMTU7OMVWDVAGJCQGQJDZ3YU252T6IM2LPS2ZMPWB7MIXCJK62AQC AS2IQQJNNCEQNXXKTGYHLB7RO3ZKCF4F7GK6FJH66BOOKDDRGNIQC 2WVP47RBNL5OVYMAZH7TKRYD7F2TGSZ5X74PWVGAYCQP26G3JUHQC FWR2SQFT5VG5BG7DJQQ7NKT2O5OMS3ID2OKLSR4L2CSZUL3RJJYQC T4FNOPMWYYJHJBTTY33PB43HTJPKEC46L62YERTWIX73HYZSELXQC KBNY5FWKTEAKABFCLPC3QFKFSVZKAGXINPCIFV6WDSWFO4VCKNTAC ODQ7LIJ2UROGGENIORRXZFWII3ZM2N45YD53FKWDQB7LLRXR4PHAC msg = replace_all(msg, "@hand@", target->hand_name(false));msg = replace_all(msg, "@hands@", target->hand_name(true));
if (hand_str.empty()){msg = replace_all(msg, "@hand@", target->hand_name(false));msg = replace_all(msg, "@hands@", target->hand_name(true));}else{msg = replace_all(msg, "@hand@", hand_str);if (can_plural_hand)msg = replace_all(msg, "@hands@", pluralise(hand_str));elsemsg = replace_all(msg, "@hands@", hand_str);}
}std::string melee_attack::wep_name(description_level_type desc,unsigned long ignore_flags) const{ASSERT(weapon != NULL);if (attacker->atype() == ACT_PLAYER)return weapon->name(desc, false, false, false, false, ignore_flags);std::string name;bool possessive = false;if (desc == DESC_CAP_YOUR){desc = DESC_CAP_THE;possessive = true;}else if (desc == DESC_NOCAP_YOUR){desc = DESC_NOCAP_THE;possessive = true;}if (possessive){name = atk_name(desc);name += "'s ";// Proper English-language possessive.name = replace_all(name, "s's ", "s' ");}name += weapon->name(desc, false, false, false, false, ignore_flags);return (name);
int clone_chance = can_clone ? 1 : 0;int poly_chance = can_poly ? 1 : 0;int poly_up_chance = can_poly && mon ? 1 : 0;int shifter_chance = can_poly && mon ? 1 : 0;
int clone_chance = can_clone ? 1 : 0;int poly_chance = can_poly ? 1 : 0;int poly_up_chance = can_poly && mon ? 1 : 0;int shifter_chance = can_poly && mon ? 1 : 0;int rage_chance = can_rage ? 10 : 0;int miscast_chance = 10;
break;case CHAOS_MISCAST:{int level = defender->get_experience_level();// At level == 27 there's a 20.3% chance of a level 3 miscast.int level1_chance = level;int level2_chance = std::max( 0, level - 7);int level3_chance = std::max( 0, level - 15);level = random_choose_weighted(level1_chance, 1,level2_chance, 2,level3_chance, 3,0);miscast_level = level;miscast_type = SPTYP_RANDOM;miscast_target = coinflip() ? attacker : defender;
static bool _move_stairs(const actor* attacker, const actor* defender){const coord_def orig_pos = attacker->pos();const dungeon_feature_type stair_feat = grd(orig_pos);if (grid_stair_direction(stair_feat) == CMD_NO_CMD)return (false);// Moving shops is too much trouble, and anyways the player can't// use them to escape.if (stair_feat == DNGN_ENTER_SHOP)return false;const bool stair_is_marker = env.markers.find(orig_pos, MAT_ANY);coord_def dest(-1, -1);// Prefer to send it under the defender.if (defender->alive() && defender->pos() != attacker->pos()){dungeon_feature_type feat = grd(defender->pos());if (!grid_destroys_items(feat) && !grid_is_solid(feat)&& !grid_is_water(feat) && !grid_is_trap(feat, true)){dest = defender->pos();}// Don't try to swap two markers.if (stair_is_marker && env.markers.find(defender->pos(), MAT_ANY))dest.set(-1, -1);}if (!in_bounds(dest)){radius_iterator ri(attacker->pos(), 1, true, false, true);int squares = 0;for (; ri; ++ri){// Don't try to swap two markers.if (stair_is_marker && env.markers.find(*ri, MAT_ANY))continue;dungeon_feature_type feat = grd(defender->pos());if (!grid_destroys_items(feat) && !grid_is_solid(feat)&& !grid_is_water(feat) && !grid_is_trap(feat, true)){if (one_chance_in(++squares))dest = *ri;}}}if (!in_bounds(dest))return (false);ASSERT(dest != orig_pos);const bool dest_is_marker = env.markers.find(dest, MAT_ANY);const dungeon_feature_type dest_feat = grd(defender->pos());ASSERT(!(dest_is_marker && stair_is_marker));dungeon_terrain_changed(orig_pos, dest_feat);dungeon_terrain_changed(dest, stair_feat);if (stair_is_marker){env.markers.move(orig_pos, dest);dungeon_events.move_listeners(orig_pos, dest);}else if (dest_is_marker){env.markers.move(dest, orig_pos);dungeon_events.move_listeners(dest, orig_pos);}if (!see_grid(orig_pos) && !see_grid(dest))return (true);std::string orig_actor, dest_actor;if (orig_pos == you.pos())orig_actor = "you";else if (mgrd(orig_pos) != NON_MONSTER){monsters &mon(menv[mgrd(orig_pos)]);if (you.can_see(&mon))orig_actor = mon.name(DESC_NOCAP_THE);}if (dest == you.pos())dest_actor = "you";else if (mgrd(dest) != NON_MONSTER){monsters &mon(menv[mgrd(dest)]);if (you.can_see(&mon))dest_actor = mon.name(DESC_NOCAP_THE);}std::string stair_name =feature_description(dest, false,see_grid(orig_pos) ? DESC_CAP_THE : DESC_CAP_A,false);std::string prep;if (grid_stair_direction(stair_feat) == CMD_GO_DOWNSTAIRS&& (stair_name.find("stair") || grid_is_escape_hatch(stair_feat))){prep = "beneath";}else if (grid_is_escape_hatch(stair_feat))prep = "above";elseprep = "beside";std::ostringstream str;str << stair_name << " ";if (see_grid(orig_pos) && !see_grid(dest)){str << "suddenly disappears";if (!orig_actor.empty())str << " from " << prep << " " << orig_actor;}else if (!see_grid(orig_pos) && see_grid(dest)){str << "suddenly appears";if (!dest_actor.empty())str << " " << prep << " " << dest_actor;}else{str << "moves";if (!orig_actor.empty())str << " from " << prep << " " << orig_actor;if (!dest_actor.empty())str << " to " << prep << " " << dest_actor;}str << "!";mpr(str.str().c_str());return (true);}#define DID_AFFECT() \{ \if (miscast_level == 0) \miscast_level = -1; \return; \}
if (miscast_level >= 1 || !attacker->alive())return;// Move stairs out from under the attacker.if (one_chance_in(100) && _move_stairs(attacker, defender))DID_AFFECT();// Dump attacker or items under attacker to another level.if (is_valid_shaft_level()&& (attacker->will_trigger_shaft()|| igrd(attacker->pos()) != NON_ITEM)&& one_chance_in(1000)){(void) attacker->do_shaft();DID_AFFECT();}// Make a loud noise.if (weapon && player_can_hear(attacker->pos())&& one_chance_in(1000)){std::string msg = wep_name(DESC_CAP_YOUR);msg += " twangs alarmingly!";if (!you.can_see(attacker))msg = "You hear a loud twang.";noisy(15, attacker->pos(), msg.c_str());DID_AFFECT();}return;
static void _find_remains(monsters* mon, int &corpse_class, int &corpse,int &last_item, std::vector<int> items){for (int i = 0; i < NUM_MONSTER_SLOTS; i++){const int idx = mon->inv[i];if (idx == NON_ITEM)continue;item_def &item(mitm[idx]);if (!is_valid_item(item) || item.pos != mon->pos())continue;items.push_back(idx);}corpse = NON_ITEM;last_item = NON_ITEM;corpse_class = mons_species(mon->type);if (corpse_class == MONS_DRACONIAN)corpse_class = draco_subspecies(mon);if (mon->has_ench(ENCH_SHAPESHIFTER))corpse_class = MONS_SHAPESHIFTER;else if (mon->has_ench(ENCH_GLOWING_SHAPESHIFTER))corpse_class = MONS_GLOWING_SHAPESHIFTER;
// Stop at first non-matching corpse, since the freshest corpse will// be at the top of the stack.for (stack_iterator si(mon->pos()); si; ++si){if (si->base_type == OBJ_CORPSES && si->sub_type == CORPSE_BODY){if (si->plus != corpse_class)break;// It should have just been dropped.if (corpse_freshness(*si) != FRESHEST_CORPSE)break;// If there was an opportunity to butcher it then it can't// have just been dropped.if (si->plus2 != 0)break;// It can't have been just made if it was picked up or// dropped.if (si->flags & (ISFLAG_DROPPED | ISFLAG_BEEN_IN_INV))break;// If it's a hydra the number of heads must match.if ((int) mon->number != si->props[MONSTER_NUMBER].get_long())break;// Got it!corpse = si.link();break;}else{// Last item which we're sure belonded to the monster.for (unsigned int i = 0; i < items.size(); i++){if (items[i] == si.link())last_item = si.link();}}}}static bool _make_zombie(monsters* mon, int corpse_class, int corpse,int last_item){// If the monster dropped a corpse then don't waste it by turning// it into a zombie.if (corpse != NON_ITEM || !mons_class_can_be_zombified(corpse_class))return (false);int idx = get_item_slot();if (idx != NON_ITEM && last_item != NON_ITEM){// Fake a corpseitem_def &corpse_item(mitm[idx]);corpse_item.base_type = OBJ_CORPSES;corpse_item.sub_type = CORPSE_BODY;corpse_item.plus = corpse_class;corpse_item.orig_monnum = mon->type + 1;corpse_item.pos = mon->pos();corpse_item.quantity = 1;corpse_item.props[MONSTER_NUMBER] = short(mon->number);// Insert it in the item stack right after the monster's// last item, so it will be equipped with all the monster's// items.corpse_item.link = mitm[last_item].link;mitm[last_item].link = idx;if (animate_remains(mon->pos(), CORPSE_BODY, mon->behaviour,mon->foe, mon->god, true, true)){if (you.can_see(mon))simple_monster_message(mon," instantly turns into a zombie!");else if (see_grid(mon->pos()))mpr("A zombie appears out of nowhere!");return (true);}}return (false);}
void melee_attack::chaos_killed_defender(monsters* def_copy)
void melee_attack::chaos_killed_defender(monsters* mon){ASSERT(mon->type != -1 && mon->type != MONS_PROGRAM_BUG);ASSERT(in_bounds(mon->pos()));ASSERT(!defender->alive());if (!attacker->alive())return;int corpse_class, corpse, last_item;std::vector<int> items;_find_remains(mon, corpse_class, corpse, last_item, items);if (one_chance_in(100) &&_make_zombie(mon, corpse_class, corpse, last_item)){DID_AFFECT();}}void melee_attack::do_miscast()
if (miscast_level == -1)return;ASSERT(miscast_target != NULL);ASSERT(miscast_level >= 0 && miscast_level <= 3);ASSERT(count_bits(miscast_type) == 1);if (!miscast_target->alive())return;const bool chaos_brand =weapon && get_weapon_brand(*weapon) == SPWPN_CHAOS;// If the miscast is happening on the attacker's side and is due to// a chaos weapon then make smoke/sand/etc pour out of the weapon// instead of the attacker's hands.std::string hand_str;std::string cause = atk_name(DESC_NOCAP_THE);int source;const int ignore_mask = ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES;if (attacker->atype() == ACT_PLAYER){source = NON_MONSTER;if (chaos_brand){cause = "a chaos effect from ";// Ignore a lot of item flags to make cause as short as possible,// so it will (hopefully) fit onto a single line in the death// cause screen.cause += wep_name(DESC_NOCAP_YOUR,ignore_mask| ISFLAG_COSMETIC_MASK | ISFLAG_RACIAL_MASK);if (miscast_target == attacker)hand_str = wep_name(DESC_PLAIN, ignore_mask);}}else{source = attacker->mindex();if (chaos_brand && miscast_target == attacker&& you.can_see(attacker)){hand_str = wep_name(DESC_PLAIN, ignore_mask);}}MiscastEffect(miscast_target, source, (spschool_flag_type) miscast_type,miscast_level, cause, NH_NEVER, hand_str, false);// Don't do miscast twice for one attack.miscast_level = -1;
int brands[] = {SPWPN_FLAMING, SPWPN_FREEZING, SPWPN_ELECTROCUTION,SPWPN_VENOM, SPWPN_DRAINING, SPWPN_VAMPIRICISM,SPWPN_PAIN, SPWPN_DISTORTION, SPWPN_CONFUSE,SPWPN_CHAOS, SPWPN_NORMAL};return (RANDOM_ELEMENT(brands));
return (random_choose_weighted(15, SPWPN_NORMAL,10, SPWPN_FLAMING,10, SPWPN_FREEZING,10, SPWPN_ELECTROCUTION,10, SPWPN_VENOM,10, SPWPN_CHAOS,5, SPWPN_VORPAL,5, SPWPN_DRAINING,5, SPWPN_VAMPIRICISM,2, SPWPN_CONFUSE,2, SPWPN_DISTORTION,0));
if (!attacker->alive() || attacker == defender)
// Also, bail if the monster is attacking itself without a weapon// since intrinsic monster attack flavours aren't applied for// self attacks.if (!attacker->alive() || (attacker == defender && !weapon)){if (miscast_target == defender)do_miscast();