diff options
43 files changed, 2572 insertions, 929 deletions
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index 7580b544b9..24d3eb172f 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -2985,7 +2985,8 @@ static void _beam_explodes(bolt &beam, const coord_def& p) // cloud producer -- POISON BLAST if (beam.name == "blast of poison") { - big_cloud(CLOUD_POISON, _whose_kill(beam), p.x, p.y, 0, 7 + random2(5)); + big_cloud(CLOUD_POISON, _whose_kill(beam), beam.killer(), p.x, p.y, + 0, 7 + random2(5)); return; } @@ -2993,13 +2994,13 @@ static void _beam_explodes(bolt &beam, const coord_def& p) if (beam.name == "foul vapour") { cl_type = (beam.flavour == BEAM_MIASMA) ? CLOUD_MIASMA : CLOUD_STINK; - big_cloud( cl_type, _whose_kill(beam), p.x, p.y, 0, 9 ); + big_cloud( cl_type, _whose_kill(beam), beam.killer(), p.x, p.y, 0, 9 ); return; } if (beam.name == "freezing blast") { - big_cloud( CLOUD_COLD, _whose_kill(beam), p.x, p.y, + big_cloud( CLOUD_COLD, _whose_kill(beam), beam.killer(), p.x, p.y, random_range(10, 15), 9 ); return; } @@ -3302,7 +3303,8 @@ static int _affect_wall(bolt &beam, const coord_def& p) else if (player_can_smell()) _beam_mpr(MSGCH_PLAIN, "You smell burning wax."); - place_cloud(CLOUD_FIRE, p, random2(10) + 15, _whose_kill(beam)); + place_cloud(CLOUD_FIRE, p, random2(10) + 15, _whose_kill(beam), + beam.killer()); beam.obvious_effect = true; @@ -3406,37 +3408,43 @@ static int _affect_place_clouds(bolt &beam, const coord_def& p) // POISON BLAST if (beam.name == "blast of poison") - place_cloud( CLOUD_POISON, p, random2(4) + 2, _whose_kill(beam) ); + place_cloud( CLOUD_POISON, p, random2(4) + 2, _whose_kill(beam), + beam.killer() ); // FIRE/COLD over water/lava if (grd(p) == DNGN_LAVA && beam.flavour == BEAM_COLD || grid_is_watery(grd(p)) && _is_fiery(beam)) { - place_cloud( CLOUD_STEAM, p, 2 + random2(5), _whose_kill(beam) ); + place_cloud( CLOUD_STEAM, p, 2 + random2(5), _whose_kill(beam), + beam.killer() ); } if (grid_is_watery(grd(p)) && beam.flavour == BEAM_COLD && beam.damage.num * beam.damage.size > 35) { place_cloud( CLOUD_COLD, p, beam.damage.num * beam.damage.size / 30 + 1, - _whose_kill(beam) ); + _whose_kill(beam), beam.killer() ); } // GREAT BLAST OF COLD if (beam.name == "great blast of cold") - place_cloud( CLOUD_COLD, p, random2(5) + 3, _whose_kill(beam) ); + place_cloud( CLOUD_COLD, p, random2(5) + 3, _whose_kill(beam), + beam.killer() ); // BALL OF STEAM if (beam.name == "ball of steam") - place_cloud( CLOUD_STEAM, p, random2(5) + 2, _whose_kill(beam) ); + place_cloud( CLOUD_STEAM, p, random2(5) + 2, _whose_kill(beam), + beam.killer() ); if (beam.flavour == BEAM_MIASMA) - place_cloud( CLOUD_MIASMA, p, random2(5) + 2, _whose_kill(beam) ); + place_cloud( CLOUD_MIASMA, p, random2(5) + 2, _whose_kill(beam), + beam.killer() ); // POISON GAS if (beam.name == "poison gas") - place_cloud( CLOUD_POISON, p, random2(4) + 3, _whose_kill(beam) ); + place_cloud( CLOUD_POISON, p, random2(4) + 3, _whose_kill(beam), + beam.killer() ); return (0); } @@ -5731,6 +5739,9 @@ killer_type bolt::killer() const case KILL_MON_MISSILE: return (KILL_MON_MISSILE); + case KILL_YOU_CONF: + return (KILL_YOU_CONF); + default: return (KILL_MON_MISSILE); } diff --git a/crawl-ref/source/cloud.cc b/crawl-ref/source/cloud.cc index 2e5c912bb7..a0f5b180df 100644 --- a/crawl-ref/source/cloud.cc +++ b/crawl-ref/source/cloud.cc @@ -46,24 +46,47 @@ static unsigned char _actual_spread_rate(cloud_type type, int spread_rate) } } +static bool _killer_whose_match(kill_category whose, killer_type killer) +{ + switch(whose) + { + case KC_YOU: + return (killer == KILL_YOU_MISSILE || killer == KILL_YOU_CONF); + + case KC_FRIENDLY: + return (killer == KILL_MON_MISSILE || killer == KILL_YOU_CONF); + + case KC_OTHER: + return (killer == KILL_MON_MISSILE || killer == KILL_MISC); + + case KC_NCATEGORIES: + ASSERT(false); + } + return (false); +} + static void _new_cloud( int cloud, cloud_type type, const coord_def& p, - int decay, kill_category whose, + int decay, kill_category whose, killer_type killer, unsigned char spread_rate ) { ASSERT( env.cloud[ cloud ].type == CLOUD_NONE ); + ASSERT(_killer_whose_match(whose, killer)); + env.cloud[ cloud ].type = type; env.cloud[ cloud ].decay = decay; env.cloud[ cloud ].x = p.x; env.cloud[ cloud ].y = p.y; env.cloud[ cloud ].whose = whose; + env.cloud[ cloud ].killer = killer; env.cloud[ cloud ].spread_rate = spread_rate; env.cgrid(p) = cloud; env.cloud_no++; } static void _place_new_cloud(cloud_type cltype, const coord_def& p, int decay, - kill_category whose, int spread_rate) + kill_category whose, killer_type killer, + int spread_rate) { if (env.cloud_no >= MAX_CLOUDS) return; @@ -73,7 +96,7 @@ static void _place_new_cloud(cloud_type cltype, const coord_def& p, int decay, { if (env.cloud[ci].type == CLOUD_NONE) // i.e., is empty { - _new_cloud( ci, cltype, p, decay, whose, spread_rate ); + _new_cloud( ci, cltype, p, decay, whose, killer, spread_rate ); break; } } @@ -102,7 +125,7 @@ static int _spread_cloud(const cloud_struct &cloud) if (newdecay >= cloud.decay) newdecay = cloud.decay - 1; - _place_new_cloud( cloud.type, *ai, newdecay, cloud.whose, + _place_new_cloud( cloud.type, *ai, newdecay, cloud.whose, cloud.killer, cloud.spread_rate ); extra_decay += 8; @@ -170,6 +193,7 @@ void delete_cloud( int cloud ) env.cloud[ cloud ].x = 0; env.cloud[ cloud ].y = 0; env.cloud[ cloud ].whose = KC_OTHER; + env.cloud[ cloud ].killer = KILL_NONE; env.cloud[ cloud ].spread_rate = 0; env.cgrid(cloud_pos) = EMPTY_CLOUD; env.cloud_no--; @@ -197,10 +221,30 @@ void move_cloud( int cloud, const coord_def& newpos ) void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, kill_category whose, int spread_rate ) { + check_place_cloud(cl_type, p, lifetime, whose, + cloud_struct::whose_to_killer(whose), spread_rate); +} + +// Places a cloud with the given stats assuming one doesn't already +// exist at that point. +void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, + killer_type killer, int spread_rate ) +{ + check_place_cloud(cl_type, p, lifetime, + cloud_struct::killer_to_whose(killer), killer, + spread_rate); +} + +// Places a cloud with the given stats assuming one doesn't already +// exist at that point. +void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, + kill_category whose, killer_type killer, + int spread_rate ) +{ if (!in_bounds(p) || env.cgrid(p) != EMPTY_CLOUD) return; - place_cloud( cl_type, p, lifetime, whose, spread_rate ); + place_cloud( cl_type, p, lifetime, whose, killer, spread_rate ); } int steam_cloud_damage(const cloud_struct &cloud) @@ -221,6 +265,26 @@ int steam_cloud_damage(const cloud_struct &cloud) void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, kill_category whose, int _spread_rate) { + place_cloud(cl_type, ctarget, cl_range, whose, + cloud_struct::whose_to_killer(whose), _spread_rate); +} + +// Places a cloud with the given stats. May delete old clouds to +// make way if there are too many on level. Will overwrite an old +// cloud under some circumstances. +void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, + killer_type killer, int _spread_rate) +{ + place_cloud(cl_type, ctarget, cl_range, + cloud_struct::killer_to_whose(killer), killer, _spread_rate); +} + +// Places a cloud with the given stats. May delete old clouds to +// make way if there are too many on level. Will overwrite an old +// cloud under some circumstances. +void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, + kill_category whose, killer_type killer, int _spread_rate) +{ if (is_sanctuary(ctarget) && !is_harmless_cloud(cl_type)) return; @@ -277,7 +341,7 @@ void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, if (cl_new != -1) { _new_cloud( cl_new, cl_type, ctarget, cl_range * 10, - whose, spread_rate ); + whose, killer, spread_rate ); } else { @@ -287,7 +351,7 @@ void place_cloud(cloud_type cl_type, const coord_def& ctarget, int cl_range, if (env.cloud[ci].type == CLOUD_NONE) // ie is empty { _new_cloud( ci, cl_type, ctarget, cl_range * 10, - whose, spread_rate ); + whose, killer, spread_rate ); break; } } @@ -635,9 +699,62 @@ std::string cloud_name(cloud_type type) //////////////////////////////////////////////////////////////////////// // cloud_struct -killer_type cloud_struct::beam_thrower() const +kill_category cloud_struct::killer_to_whose(killer_type killer) { - return (whose == KC_YOU ? KILL_YOU_MISSILE : KILL_MON_MISSILE); + switch(killer) + { + case KILL_YOU: + case KILL_YOU_MISSILE: + case KILL_YOU_CONF: + return (KC_YOU); + + case KILL_MON: + case KILL_MON_MISSILE: + case KILL_MISC: + return (KC_OTHER); + + default: + ASSERT(false); + } + return (KC_OTHER); +} + +killer_type cloud_struct::whose_to_killer(kill_category whose) +{ + switch(whose) + { + case KC_YOU: return(KILL_YOU_MISSILE); + case KC_FRIENDLY: return(KILL_MON_MISSILE); + case KC_OTHER: return(KILL_MON_MISSILE); + case KC_NCATEGORIES: ASSERT(false); + } + return (KILL_NONE); +} + +void cloud_struct::set_whose(kill_category _whose) +{ + whose = _whose; + killer = whose_to_killer(whose); +} + +void cloud_struct::set_killer(killer_type _killer) +{ + killer = _killer; + whose = killer_to_whose(killer); + + switch(killer) + { + case KILL_YOU: + killer = KILL_YOU_MISSILE; + break; + + case KILL_MON: + killer = KILL_MON_MISSILE; + break; + + default: + break; + } } ////////////////////////////////////////////////////////////////////////// // Fog machine stuff diff --git a/crawl-ref/source/cloud.h b/crawl-ref/source/cloud.h index b4e5d229bf..1485f3782c 100644 --- a/crawl-ref/source/cloud.h +++ b/crawl-ref/source/cloud.h @@ -42,8 +42,18 @@ void move_cloud( int cloud, const coord_def& newpos ); void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, kill_category whose, int spread_rate = -1 ); +void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, + killer_type killer, int spread_rate = -1 ); +void check_place_cloud( cloud_type cl_type, const coord_def& p, int lifetime, + kill_category whose, killer_type killer, + int spread_rate = -1 ); void place_cloud( cloud_type cl_type, const coord_def& ctarget, int cl_range, kill_category whose, int spread_rate = -1 ); +void place_cloud( cloud_type cl_type, const coord_def& ctarget, + int cl_range, killer_type killer, int spread_rate = -1 ); +void place_cloud( cloud_type cl_type, const coord_def& ctarget, + int cl_range, kill_category whose, killer_type killer, + int spread_rate = -1 ); void manage_clouds(void); diff --git a/crawl-ref/source/cmd-keys.h b/crawl-ref/source/cmd-keys.h index 041daef752..b495e60414 100644 --- a/crawl-ref/source/cmd-keys.h +++ b/crawl-ref/source/cmd-keys.h @@ -129,6 +129,8 @@ {'g', CMD_TARGET_WIZARD_GIVE_ITEM}, {'m', CMD_TARGET_WIZARD_MOVE}, {'w', CMD_TARGET_WIZARD_PATHFIND}, +{'G', CMD_TARGET_WIZARD_GAIN_LEVEL}, +{'M', CMD_TARGET_WIZARD_MISCAST}, #endif {'v', CMD_TARGET_DESCRIBE}, {'?', CMD_TARGET_HELP}, diff --git a/crawl-ref/source/cmd-name.h b/crawl-ref/source/cmd-name.h index 5ed02439ba..5e4330deef 100644 --- a/crawl-ref/source/cmd-name.h +++ b/crawl-ref/source/cmd-name.h @@ -184,6 +184,8 @@ {CMD_TARGET_WIZARD_GIVE_ITEM, "CMD_TARGET_WIZARD_GIVE_ITEM"}, {CMD_TARGET_WIZARD_MOVE, "CMD_TARGET_WIZARD_MOVE"}, {CMD_TARGET_WIZARD_PATHFIND, "CMD_TARGET_WIZARD_PATHFIND"}, +{CMD_TARGET_WIZARD_GAIN_LEVEL, "CMD_TARGET_WIZARD_GAIN_LEVEL"}, +{CMD_TARGET_WIZARD_MISCAST, "CMD_TARGET_WIZARD_MISCAST"}, {CMD_TARGET_MOUSE_MOVE, "CMD_TARGET_MOUSE_MOVE"}, {CMD_TARGET_MOUSE_SELECT, "CMD_TARGET_MOUSE_SELECT"}, {CMD_TARGET_HELP, "CMD_TARGET_HELP"}, diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index c3bb854955..796c7182aa 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -765,6 +765,7 @@ static const char *targeting_help_1 = "<w>F</w>: cycle monster friendly/good neutral/neutral/hostile\n" "<w>P</w>: apply divine blessing to monster\n" "<w>m</w>: move monster or player\n" + "<w>M</w>: cause spell miscast for monster or player\n" "<w>w</w>: calculate shortest path to any point on the map\n" #endif ; diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index bc7b14a405..aea6851a9d 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -1,7 +1,7 @@ /* * File: debug.cc * Summary: Debug and wizard related functions. - * Written by: Linley Henzell and Jesse Jones +* Written by: Linley Henzell and Jesse Jones * * Modified for Crawl Reference by $Author$ on $Date$ * @@ -89,6 +89,7 @@ #include "skills2.h" #include "spl-book.h" #include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -4249,6 +4250,186 @@ void debug_pathfind(int mid) mprf("-> #waypoints: %d", path.size()); } } + +static void _miscast_screen_update() +{ + viewwindow(true, false); + + you.redraw_status_flags = + REDRAW_LINE_1_MASK | REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK; + print_stats(); + +#ifndef USE_TILE + update_monster_pane(); +#endif +} + +void debug_miscast( int target_index ) +{ + crawl_state.cancel_cmd_repeat(); + + actor* target; + if (target_index == NON_MONSTER) + target = dynamic_cast<actor*>(&you); + else + target = dynamic_cast<actor*>(&menv[target_index]); + + if (!target->alive()) + { + mpr("Can't make already dead target miscast."); + return; + } + + char specs[100]; + mpr( "Miscast which school or spell, by name? ", MSGCH_PROMPT ); + if (cancelable_get_line(specs, sizeof specs) || !*specs) + { + canned_msg(MSG_OK); + return; + } + + spell_type spell = spell_by_name(specs, true); + spschool_flag_type school = school_by_name(specs); + + // Prefer exact matches for school name over partial matches for + // spell name. + if (school != SPTYP_NONE + && (strcasecmp(specs, spelltype_short_name(school)) == 0 + || strcasecmp(specs, spelltype_long_name(school)) == 0)) + { + spell = SPELL_NO_SPELL; + } + + if (spell == SPELL_NO_SPELL && school == SPTYP_NONE) + { + mpr("No matching spell or spell school."); + return; + } + else if (spell != SPELL_NO_SPELL && school != SPTYP_NONE) + { + mprf("Ambiguous: can be spell '%s' or school '%s'.", + spell_title(spell), spelltype_short_name(school)); + return; + } + + int disciplines = 0; + if (spell != SPELL_NO_SPELL) + { + disciplines = get_spell_disciplines(spell); + + if (disciplines == 0) + { + mprf("Spell '%s' has no disciplines.", spell_title(spell)); + return; + } + } + + if (school == SPTYP_HOLY || (disciplines & SPTYP_HOLY)) + { + mpr("Can't miscast holy spells."); + return; + } + + if (spell != SPELL_NO_SPELL) + mprf("Miscasting spell %s.", spell_title(spell)); + else + mprf("Miscasting school %s.", spelltype_long_name(school)); + + if (spell != SPELL_NO_SPELL) + mpr( "Enter spell_power,spell_failure: ", + MSGCH_PROMPT ); + else + mpr( "Enter miscast_level or spell_power,spell_failure: ", + MSGCH_PROMPT ); + + if (cancelable_get_line(specs, sizeof specs) || !*specs) + { + canned_msg(MSG_OK); + return; + } + + int level = -1, pow = -1, fail = -1; + + if (strchr(specs, ',')) + { + std::vector<std::string> nums = split_string(",", specs); + pow = atoi(nums[0].c_str()); + fail = atoi(nums[1].c_str()); + + if (pow <= 0 || fail <= 0) + { + canned_msg(MSG_OK); + return; + } + } + else + { + if (spell != SPELL_NO_SPELL) + { + mpr("Can only enter fixed miscast level for schools, not spells."); + return; + } + + level = atoi(specs); + if (level < 0) + { + canned_msg(MSG_OK); + return; + } + else if (level > 3) + { + mpr("Miscast level can be at most 3."); + return; + } + } + + // Handle repeats ourselves since miscasts are likely to interrupt + // command repetions, especially if the player is the target. + int repeats = _debug_prompt_for_int("Number of repitions? ", true); + if (repeats < 1) + { + canned_msg(MSG_OK); + return; + } + + // Supress "nothing happens" message for monster miscasts which are + // only harmless messages, since a large number of those are missing + // monster messages. + nothing_happens_when_type nothing = NH_DEFAULT; + if (target_index != NON_MONSTER && level == 0) + nothing = NH_NEVER; + + MiscastEffect *miscast; + + if (spell != SPELL_NO_SPELL) + miscast = new MiscastEffect(target, target_index, spell, pow, fail, + "", nothing); + else + { + if (level != -1) + miscast = new MiscastEffect(target, target_index, school, + level, "wizard testing miscast", + nothing); + else + miscast = new MiscastEffect(target, target_index, school, + pow, fail, "wizard testing miscast", + nothing); + } + // Merely creating the miscast object causes one miscast effect to + // happen. + repeats--; + if (level != 0) + _miscast_screen_update(); + + while (target->alive() && repeats-- > 0) + { + miscast->do_miscast(); + if (level != 0) + _miscast_screen_update(); + } + + delete miscast; +} #endif #ifdef DEBUG_DIAGNOSTICS diff --git a/crawl-ref/source/debug.h b/crawl-ref/source/debug.h index ca73dcbbcd..1edf54f960 100644 --- a/crawl-ref/source/debug.h +++ b/crawl-ref/source/debug.h @@ -119,6 +119,7 @@ void wizard_give_monster_item(monsters* mon); void wizard_move_player_or_monster(int x, int y); void debug_make_monster_shout(monsters* mon); void debug_pathfind(int mid); +void debug_miscast( int target ); #ifdef DEBUG_DIAGNOSTICS diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc index 6f6983c276..46848623ef 100644 --- a/crawl-ref/source/decks.cc +++ b/crawl-ref/source/decks.cc @@ -44,6 +44,7 @@ #include "spells3.h" #include "spells4.h" #include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -2737,6 +2738,10 @@ bool card_effect(card_type which_card, deck_rarity_type rarity, bool rc = true; const int power = _card_power(rarity); + const god_type god = + (crawl_state.is_god_acting()) ? crawl_state.which_god_acting() + : GOD_NO_GOD; + #ifdef DEBUG_DIAGNOSTICS msg::streams(MSGCH_DIAGNOSTICS) << "Card power: " << power << ", rarity: " << static_cast<int>(rarity) @@ -2844,8 +2849,9 @@ bool card_effect(card_type which_card, deck_rarity_type rarity, case CARD_WILD_MAGIC: // Yes, high power is bad here. - miscast_effect( SPTYP_RANDOM, random2(power/15) + 5, - random2(power), 0, "a card of wild magic" ); + MiscastEffect( &you, god == GOD_NO_GOD ? NON_MONSTER : -god, + SPTYP_RANDOM, random2(power/15) + 5, random2(power), + "a card of wild magic" ); break; case CARD_FAMINE: diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc index 8d62b42c81..3ec1dd79d9 100644 --- a/crawl-ref/source/directn.cc +++ b/crawl-ref/source/directn.cc @@ -1077,6 +1077,19 @@ void direction(dist& moves, targeting_type restricts, debug_pathfind(mid); break; + + case CMD_TARGET_WIZARD_GAIN_LEVEL: + break; + + case CMD_TARGET_WIZARD_MISCAST: + if (!you.wizard || !in_bounds(moves.tx, moves.ty)) + break; + mid = mgrd[moves.tx][moves.ty]; + if (mid == NON_MONSTER && you.pos() != moves.target()) + break; + + debug_miscast(mid); + break; #endif case CMD_TARGET_DESCRIBE: full_describe_square(moves.target()); diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index baaecf2767..03196f3c49 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -53,7 +53,7 @@ #include "spells2.h" #include "spells3.h" #include "spl-book.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -404,10 +404,16 @@ void banished(dungeon_feature_type gate_type, const std::string &who) } else if (who == "drawing a card") you.entry_cause = EC_SELF_RISKY; - else if (who.find("miscast") != std::string::npos) + else if (who.find("you miscast") != std::string::npos) you.entry_cause = EC_MISCAST; else if (who == "wizard command") you.entry_cause = EC_SELF_EXPLICIT; + else if (who.find("effects of Hell") != std::string::npos) + you.entry_cause = EC_ENVIRONMENT; + else if (who.find("Zot") != std::string::npos) + you.entry_cause = EC_TRAP; + else if (who.find("trap") != std::string::npos) + you.entry_cause = EC_TRAP; else you.entry_cause = EC_MONSTER; @@ -2147,8 +2153,9 @@ static void _hell_effects() else // 1 in 8 odds {dlb} which_miscast = SPTYP_ENCHANTMENT; - miscast_effect(which_miscast, 4 + random2(6), random2avg(97, 3), - 100, "the effects of Hell"); + MiscastEffect(&you, MISC_KNOWN_MISCAST, which_miscast, + 4 + random2(6), random2avg(97, 3), + "the effects of Hell"); } else if (temp_rand > 7) // 10 in 27 odds {dlb} { @@ -2202,8 +2209,9 @@ static void _hell_effects() } else { - miscast_effect(which_miscast, 4 + random2(6), - random2avg(97, 3), 100, "the effects of Hell"); + MiscastEffect(&you, MISC_KNOWN_MISCAST, which_miscast, + 4 + random2(6), random2avg(97, 3), + "the effects of Hell"); } } diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 92984e860c..44852ac7ee 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -619,6 +619,8 @@ enum command_type CMD_TARGET_WIZARD_GIVE_ITEM, CMD_TARGET_WIZARD_MOVE, CMD_TARGET_WIZARD_PATHFIND, + CMD_TARGET_WIZARD_GAIN_LEVEL, + CMD_TARGET_WIZARD_MISCAST, CMD_TARGET_MOUSE_MOVE, CMD_TARGET_MOUSE_SELECT, CMD_TARGET_HELP, @@ -1428,6 +1430,8 @@ enum entry_cause_type EC_GOD_RETRIBUTION, EC_GOD_ACT, // Xom sending the player somewhere for amusement. EC_MONSTER, + EC_TRAP, // Zot traps + EC_ENVIRONMENT, // Hell effects. NUM_ENTRY_CAUSE_TYPES }; diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index c1734d0aa6..3edf66accc 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -134,9 +134,15 @@ public: { } - virtual std::string name(description_level_type type) const = 0; - virtual std::string pronoun(pronoun_type which_pronoun) const = 0; + virtual std::string name(description_level_type type, + bool force_visible = false) const = 0; + virtual std::string pronoun(pronoun_type which_pronoun, + bool force_visible = false) const = 0; virtual std::string conj_verb(const std::string &verb) const = 0; + virtual std::string hand_name(bool plural, + bool *can_plural = NULL) const = 0; + virtual std::string foot_name(bool plural, + bool *can_plural = NULL) const = 0; virtual bool fumbles_attack(bool verbose = true) = 0; @@ -157,7 +163,7 @@ public: virtual int hurt(const actor *attacker, int amount) = 0; virtual void heal(int amount, bool max_too = false) = 0; virtual void banish(const std::string &who = "") = 0; - virtual void blink() = 0; + virtual void blink(bool allow_partial_control = true) = 0; virtual void teleport(bool right_now = false, bool abyss_shift = false) = 0; virtual void poison(actor *attacker, int amount = 1) = 0; virtual bool sicken(int amount) = 0; @@ -199,6 +205,7 @@ public: virtual int res_poison() const = 0; virtual int res_negative_energy() const = 0; virtual int res_rotting() const = 0; + virtual int res_torment() const = 0; virtual flight_type flight_mode() const = 0; virtual bool is_levitating() const = 0; @@ -834,9 +841,12 @@ public: item_def *weapon(int which_attack = -1); item_def *shield(); - std::string name(description_level_type type) const; - std::string pronoun(pronoun_type pro) const; + std::string name(description_level_type type, + bool force_visible = false) const; + std::string pronoun(pronoun_type pro, bool force_visible = false) const; std::string conj_verb(const std::string &verb) const; + std::string hand_name(bool plural, bool *can_plural = NULL) const; + std::string foot_name(bool plural, bool *can_plural = NULL) const; bool fumbles_attack(bool verbose = true); bool cannot_fight() const; @@ -849,7 +859,7 @@ public: bool can_safely_mutate() const; bool mutate(); void banish(const std::string &who = ""); - void blink(); + void blink(bool allow_partial_control = true); void teleport(bool right_now = false, bool abyss_shift = false); void drain_stat(int stat, int amount, actor* attacker); @@ -881,6 +891,7 @@ public: int res_poison() const; int res_negative_energy() const; int res_rotting() const; + int res_torment() const; bool confusable() const; bool slowable() const; @@ -1067,6 +1078,8 @@ public: // AI_SEE_MONSTER public: + mon_attitude_type temp_attitude() const; + // Returns true if the monster is named with a proper name, or is // a player ghost. bool is_named() const; @@ -1183,16 +1196,18 @@ public: bool can_use_missile(const item_def &item) const; - std::string name(description_level_type type) const; - std::string name(description_level_type type, bool force_visible) const; + std::string name(description_level_type type, + bool force_visible = false) const; // Base name of the monster, bypassing any mname setting. For an orc priest // named Arbolt, name() will return "Arbolt", but base_name() will return // "orc priest". std::string base_name(description_level_type type, bool force_visible) const; - std::string pronoun(pronoun_type pro) const; + std::string pronoun(pronoun_type pro, bool force_visible = false) const; std::string conj_verb(const std::string &verb) const; + std::string hand_name(bool plural, bool *can_plural = NULL) const; + std::string foot_name(bool plural, bool *can_plural = NULL) const; bool fumbles_attack(bool verbose = true); bool cannot_fight() const; @@ -1219,6 +1234,7 @@ public: int res_poison() const; int res_negative_energy() const; int res_rotting() const; + int res_torment() const; flight_type flight_mode() const; bool is_levitating() const; @@ -1232,6 +1248,7 @@ public: bool cannot_move() const; bool cannot_act() const; bool confused() const; + bool confused_by_you() const; bool caught() const; bool asleep() const; bool backlit(bool check_haloed = true) const; @@ -1253,7 +1270,7 @@ public: void rot(actor *agent, int rotlevel, int immed_rot); int hurt(const actor *agent, int amount); void heal(int amount, bool max_too = false); - void blink(); + void blink(bool allow_partial_control = true); void teleport(bool right_now = false, bool abyss_shift = false); void put_to_sleep(int power = 0); @@ -1314,8 +1331,14 @@ struct cloud_struct int decay; unsigned char spread_rate; kill_category whose; + killer_type killer; + + void set_whose(kill_category _whose); + void set_killer(killer_type _killer); + + static kill_category killer_to_whose(killer_type killer); + static killer_type whose_to_killer(kill_category whose); - killer_type beam_thrower() const; coord_def pos() const { return coord_def(x,y); } }; diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index b2ea2259bf..8ae70f1a74 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -71,7 +71,7 @@ #include "spells1.h" #include "spells3.h" #include "spells4.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "stuff.h" #include "transfor.h" @@ -498,12 +498,10 @@ void melee_attack::check_special_wield_effects() break; case SPWLD_WUCAD_MU: - // XXX At some distant point in the future, allow - // miscast_effect to operate on any actor. - if (one_chance_in(9) && attacker->atype() == ACT_PLAYER) + if (one_chance_in(9)) { - miscast_effect( SPTYP_DIVINATION, random2(9), random2(70), 100, - "the Staff of Wucad Mu" ); + MiscastEffect(attacker, MELEE_MISCAST, SPTYP_DIVINATION, + random2(9), random2(70), "the Staff of Wucad Mu"); } break; default: diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc index 6267202d22..a4af6be2b3 100644 --- a/crawl-ref/source/hiscores.cc +++ b/crawl-ref/source/hiscores.cc @@ -1629,8 +1629,12 @@ std::string scorefile_entry::death_description(death_desc_verbosity verbosity) needs_damage = true; break; - case KILLED_BY_XOM: // only used for old Xom kills - desc += terse? "xom" : "Killed for Xom's enjoyment"; + case KILLED_BY_XOM: + if (terse) + desc += "xom"; + else + desc += auxkilldata.empty() ? "Killed for Xom's enjoyment" + : auxkilldata; needs_damage = true; break; @@ -1699,6 +1703,14 @@ std::string scorefile_entry::death_description(death_desc_verbosity verbosity) desc += terse? "bleeding" : " bled to death"; break; + case KILLED_BY_DIVINE_WRATH: + if (terse) + desc += "divine wrath"; + else + desc += auxkilldata.empty() ? "Divine wrath" : auxkilldata; + needs_damage = true; + break; + default: desc += terse? "program bug" : "Nibbled to death by software bugs"; break; diff --git a/crawl-ref/source/it_use2.cc b/crawl-ref/source/it_use2.cc index ad53a89f49..542e9adbf1 100644 --- a/crawl-ref/source/it_use2.cc +++ b/crawl-ref/source/it_use2.cc @@ -38,7 +38,7 @@ #include "religion.h" #include "skills2.h" #include "spells2.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "stuff.h" #include "tutorial.h" @@ -437,8 +437,8 @@ bool unwield_item(bool showMsgs) case SPWPN_STAFF_OF_WUCAD_MU: item.plus = 0; item.plus2 = 0; - miscast_effect( SPTYP_DIVINATION, 9, 90, 100, - "the Staff of Wucad Mu" ); + MiscastEffect( &you, WIELD_MISCAST, SPTYP_DIVINATION, 9, 90, + "the Staff of Wucad Mu" ); break; default: break; @@ -502,8 +502,8 @@ bool unwield_item(bool showMsgs) // if it's to be allowed as a player spell. -- bwr // int effect = 9 - random2avg( you.skills[SK_TRANSLOCATIONS] * 2, 2 ); - miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, - "distortion unwield" ); + MiscastEffect( &you, WIELD_MISCAST, SPTYP_TRANSLOCATION, 9, 90, + "distortion unwield" ); break; // NOTE: When more are added here, *must* duplicate unwielding diff --git a/crawl-ref/source/it_use3.cc b/crawl-ref/source/it_use3.cc index a4bdde12f1..6d085b389e 100644 --- a/crawl-ref/source/it_use3.cc +++ b/crawl-ref/source/it_use3.cc @@ -50,6 +50,7 @@ #include "spells3.h" #include "spl-book.h" #include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -534,8 +535,10 @@ bool evoke_wielded() if (one_chance_in(3)) { - miscast_effect( SPTYP_DIVINATION, random2(9), - random2(70), 100, "the Staff of Wucad Mu" ); + // NH_NEVER prevents "nothing happens" messages. + MiscastEffect( &you, NON_MONSTER, SPTYP_DIVINATION, + random2(9), random2(70), + "the Staff of Wucad Mu", NH_NEVER ); } break; diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index ee78a30170..31955957f9 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -72,6 +72,7 @@ #include "spells3.h" #include "spl-book.h" #include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -738,8 +739,8 @@ void wield_effects(int item_wield_2, bool showMsgs) break; case SPWPN_STAFF_OF_WUCAD_MU: - miscast_effect( SPTYP_DIVINATION, 9, 90, 100, - "the Staff of Wucad Mu" ); + MiscastEffect(&you, WIELD_MISCAST, SPTYP_DIVINATION, 9, 90, + "the Staff of Wucad Mu" ); you.special_wield = SPWLD_WUCAD_MU; break; } @@ -3895,8 +3896,8 @@ static bool affix_weapon_enchantment() mprf("%s twongs alarmingly.", itname.c_str()); // from unwield_item - miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, - "distortion affixation" ); + MiscastEffect( &you, NON_MONSTER, SPTYP_TRANSLOCATION, 9, 90, + "distortion affixation" ); success = false; break; diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc index 8d225b7cd3..7aa9b4ef12 100644 --- a/crawl-ref/source/libutil.cc +++ b/crawl-ref/source/libutil.cc @@ -339,10 +339,14 @@ std::string pluralise(const std::string &name, // knife -> knives return name.substr(0, name.length() - 2) + "ves"; } - else if (ends_with(name, "elf") || ends_with(name, "olf") - || ends_with(name, "arf")) + else if (ends_with(name, "ff")) { - // Elf, wolf, dwarf. + // staff -> staves + return name.substr(0, name.length() - 2) + "ves"; + } + else if (ends_with(name, "f")) + { + // elf -> elves return name.substr(0, name.length() - 1) + "ves"; } else if (ends_with(name, "mage")) @@ -373,10 +377,6 @@ std::string pluralise(const std::string &name, // efreet -> efreeti. Not sure this is correct. return name + "i"; } - else if (ends_with(name, "staff")) - { - return name.substr(0, name.length() - 2) + "ves"; - } return name + "s"; } diff --git a/crawl-ref/source/luadgn.cc b/crawl-ref/source/luadgn.cc index 0804d859c7..beab44ba13 100644 --- a/crawl-ref/source/luadgn.cc +++ b/crawl-ref/source/luadgn.cc @@ -1572,13 +1572,15 @@ static int lua_cloud_pow_max; static int lua_cloud_pow_rolls; static int make_a_lua_cloud(int x, int y, int garbage, int spread_rate, - cloud_type ctype, kill_category whose) + cloud_type ctype, kill_category whose, + killer_type killer) { UNUSED( garbage ); + const int pow = random_range(lua_cloud_pow_min, lua_cloud_pow_max, lua_cloud_pow_rolls); - place_cloud( ctype, coord_def(x, y), pow, whose, spread_rate ); + place_cloud( ctype, coord_def(x, y), pow, whose, killer, spread_rate ); return 1; } @@ -1663,7 +1665,8 @@ static int dgn_apply_area_cloud(lua_State *ls) } apply_area_cloud(make_a_lua_cloud, x, y, 0, size, - ctype, kc, spread_rate); + ctype, kc, cloud_struct::whose_to_killer(kc), + spread_rate); return (0); } diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 2ee0dbe997..cc156fd9e0 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -2101,6 +2101,16 @@ size_type mons_size(const monsters *m) return m->body_size(); } +mon_attitude_type monsters::temp_attitude() const +{ + if (has_ench(ENCH_CHARM)) + return ATT_FRIENDLY; + else if (has_ench(ENCH_NEUTRAL)) + return ATT_NEUTRAL; + else + return attitude; +} + bool mons_friendly(const monsters *m) { return (m->attitude == ATT_FRIENDLY || m->has_ench(ENCH_CHARM)); @@ -2681,7 +2691,8 @@ bool mons_has_ranged_attack( const monsters *mon ) // 3 : It sticks to her sword! (lower case possessive) // ... as needed -const char *mons_pronoun(monster_type mon_type, pronoun_type variant) +const char *mons_pronoun(monster_type mon_type, pronoun_type variant, + bool visible) { gender_type gender = GENDER_NEUTER; @@ -2711,6 +2722,9 @@ const char *mons_pronoun(monster_type mon_type, pronoun_type variant) } } + if (!visible) + gender = GENDER_NEUTER; + switch(variant) { case PRONOUN_CAP: @@ -4309,11 +4323,6 @@ bool monsters::has_base_name() const return (!mname.empty() && !ghost.get()); } -std::string monsters::name(description_level_type desc) const -{ - return this->name(desc, false); -} - std::string monsters::name(description_level_type desc, bool force_vis) const { const bool possessive = @@ -4340,9 +4349,10 @@ std::string monsters::base_name(description_level_type desc, bool force_vis) } } -std::string monsters::pronoun(pronoun_type pro) const +std::string monsters::pronoun(pronoun_type pro, bool force_visible) const { - return (mons_pronoun(static_cast<monster_type>(type), pro)); + return (mons_pronoun(static_cast<monster_type>(type), pro, + force_visible || you.can_see(this))); } std::string monsters::conj_verb(const std::string &verb) const @@ -4356,6 +4366,213 @@ std::string monsters::conj_verb(const std::string &verb) const return (pluralise(verb)); } +std::string monsters::hand_name(bool plural, bool *can_plural) const +{ + bool _can_plural; + if (can_plural == NULL) + can_plural = &_can_plural; + *can_plural = true; + + std::string str; + char ch = mons_char(type); + + switch(get_mon_shape(this)) + { + case MON_SHAPE_CENTAUR: + case MON_SHAPE_NAGA: + // Defaults to "hand" + break; + case MON_SHAPE_HUMANOID: + case MON_SHAPE_HUMANOID_WINGED: + case MON_SHAPE_HUMANOID_TAILED: + case MON_SHAPE_HUMANOID_WINGED_TAILED: + if (ch == 'T' || ch == 'd' || ch == 'n' || mons_is_demon(type)) + str = "claw"; + break; + + case MON_SHAPE_QUADRUPED: + case MON_SHAPE_QUADRUPED_TAILLESS: + case MON_SHAPE_QUADRUPED_WINGED: + case MON_SHAPE_ARACHNID: + if (type == MONS_SCORPION) + str = "pincer"; + else + { + str = "front "; + return (str + foot_name(plural, can_plural)); + } + break; + + case MON_SHAPE_BLOB: + case MON_SHAPE_SNAKE: + case MON_SHAPE_FISH: + return foot_name(plural); + + case MON_SHAPE_BAT: + str = "wing"; + break; + + case MON_SHAPE_INSECT: + case MON_SHAPE_INSECT_WINGED: + case MON_SHAPE_CENTIPEDE: + str = "antena"; + break; + + case MON_SHAPE_SNAIL: + str = "eye-stalk"; + break; + + case MON_SHAPE_PLANT: + str = "leaf"; + break; + + case MON_SHAPE_MISC: + if (ch == 'x' || ch == 'X') + { + str = "tentacle"; + break; + } + // Deliberate fallthrough. + case MON_SHAPE_FUNGUS: + str = "body"; + *can_plural = false; + break; + + case MON_SHAPE_ORB: + switch(type) + { + case MONS_GIANT_SPORE: + str = "rhizome"; + break; + + case MONS_GIANT_EYEBALL: + case MONS_EYE_OF_DRAINING: + case MONS_SHINING_EYE: + case MONS_EYE_OF_DEVASTATION: + *can_plural = false; + // Deliberate fallthrough. + case MONS_GREAT_ORB_OF_EYES: + str = "pupil"; + break; + + case MONS_GIANT_ORANGE_BRAIN: + default: + str = "body"; + can_plural = false; + break; + } + } + + if (str.empty()) + str = "hand"; + + if (plural && *can_plural) + str = pluralise(str); + + return (str); +} + +std::string monsters::foot_name(bool plural, bool *can_plural) const +{ + bool _can_plural; + if (can_plural == NULL) + can_plural = &_can_plural; + *can_plural = true; + + std::string str; + char ch = mons_char(type); + + switch(get_mon_shape(this)) + { + case MON_SHAPE_INSECT: + case MON_SHAPE_INSECT_WINGED: + case MON_SHAPE_ARACHNID: + case MON_SHAPE_CENTIPEDE: + str = "leg"; + break; + + case MON_SHAPE_HUMANOID: + case MON_SHAPE_HUMANOID_WINGED: + case MON_SHAPE_HUMANOID_TAILED: + case MON_SHAPE_HUMANOID_WINGED_TAILED: + if (type == MONS_MINOTAUR) + str = "hoof"; + else if (swimming() && (type == MONS_MERFOLK || type == MONS_MERMAID)) + { + str = "tail"; + *can_plural = false; + } + break; + + case MON_SHAPE_CENTAUR: + str = "hoof"; + break; + + case MON_SHAPE_QUADRUPED: + case MON_SHAPE_QUADRUPED_TAILLESS: + case MON_SHAPE_QUADRUPED_WINGED: + if (ch == 'h') + str = "paw"; + else if (ch == 'l' || ch == 'D') + str = "talon"; + else if (type == MONS_YAK || type == MONS_DEATH_YAK) + str = "hoof"; + else if (ch == 'H') + { + if (type == MONS_MANTICORE || type == MONS_SPHINX) + str = "paw"; + else + str = "talon"; + } + break; + + case MON_SHAPE_BAT: + str = "claw"; + break; + + case MON_SHAPE_SNAKE: + case MON_SHAPE_FISH: + str = "tail"; + *can_plural = false; + break; + + case MON_SHAPE_PLANT: + str = "root"; + break; + + case MON_SHAPE_FUNGUS: + str = "stem"; + *can_plural = false; + break; + + case MON_SHAPE_BLOB: + str = "pseudopod"; + break; + + case MON_SHAPE_MISC: + if (ch == 'x' || ch == 'X') + { + str = "tentacle"; + break; + } + // Deliberate fallthrough. + case MON_SHAPE_SNAIL: + case MON_SHAPE_NAGA: + case MON_SHAPE_ORB: + str = "underside"; + *can_plural = false; + break; + } + + if (str.empty()) + return (plural ? "feet" : "foot"); + + if (plural && *can_plural) + str = pluralise(str); + + return (str); +} + int monsters::id() const { return (type); @@ -4459,6 +4676,15 @@ bool monsters::confused() const return (mons_is_confused(this)); } +bool monsters::confused_by_you() const +{ + if (mons_class_flag(type, M_CONFUSED)) + return false; + + const mon_enchant me = get_ench(ENCH_CONFUSION); + return (me.ench == ENCH_CONFUSION && me.who == KC_YOU); +} + bool monsters::paralysed() const { return (mons_is_paralysed(this)); @@ -4591,6 +4817,15 @@ int monsters::res_rotting() const return (mons_holiness(this) == MH_NATURAL ? 0 : 1); } +int monsters::res_torment() const +{ + mon_holy_type holy = mons_holiness(this); + if (holy == MH_UNDEAD || holy == MH_DEMONIC || holy == MH_NONLIVING) + return (1); + + return (0); +} + flight_type monsters::flight_mode() const { return (mons_flies(this)); @@ -4631,7 +4866,7 @@ int monsters::skill(skill_type sk, bool) const } } -void monsters::blink() +void monsters::blink(bool) { monster_blink(this); } diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h index e4dd300dd9..aabc2142c7 100644 --- a/crawl-ref/source/mon-util.h +++ b/crawl-ref/source/mon-util.h @@ -669,7 +669,8 @@ bool mons_has_ranged_attack( const monsters *mon ); /* *********************************************************************** * called from: * *********************************************************************** */ -const char *mons_pronoun(monster_type mon_type, pronoun_type variant); +const char *mons_pronoun(monster_type mon_type, pronoun_type variant, + bool visible = true); // last updated 14mar2001 (gdl) /* *********************************************************************** diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index f1aeaccf6e..2d4f98eae6 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -60,7 +60,7 @@ #include "player.h" #include "randart.h" #include "religion.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "spells3.h" #include "spells4.h" @@ -696,12 +696,77 @@ static void _fire_monster_death_event(monsters *monster, } } -void monster_die(monsters *monster, killer_type killer, int i, bool silent) +static void _mummy_curse(monsters* monster, killer_type killer, int index) +{ + int pow; + + switch(killer) + { + // Mummy killed by trap or something other than the player or + // another monster, so no curse. + case KILL_MISC: + // Mummy sent to the Abyss wasn't actually killed, so no curse. + case KILL_RESET: + case KILL_DISMISSED: + return; + + default: + break; + } + + switch(monster->type) + { + case MONS_MUMMY: pow = 1; break; + case MONS_GUARDIAN_MUMMY: pow = 3; break; + case MONS_MUMMY_PRIEST: pow = 8; break; + case MONS_GREATER_MUMMY: pow = 11; break; + + default: + mpr("Unkown mummy type.", MSGCH_DIAGNOSTICS); + return; + } + + // Killed by a Zot trap, a god, etc + if (index != NON_MONSTER && invalid_monster_index(killer)) + return; + + actor* target; + if (index == NON_MONSTER) + target = dynamic_cast<actor*>(&you); + else + { + // Mummies committing suicide don't cause a death curse. + if (index == (int) monster_index(monster)) + return; + target = dynamic_cast<actor*>(&menv[index]); + } + + // Mummy was killed by a giant spore or ball lightning? + if (!target->alive()) + return; + + if (monster->type == MONS_MUMMY && killer == NON_MONSTER) + curse_an_item(true); + else + { + if (index == NON_MONSTER) + mpr("You feel extremely nervous for a moment...", + MSGCH_MONSTER_SPELL); + else if (you.can_see(target)) + mprf(MSGCH_MONSTER_SPELL, "A malignant arua surrounds %s.", + target->name(DESC_NOCAP_THE).c_str()); + MiscastEffect(target, monster_index(monster), SPTYP_NECROMANCY, + pow, random2avg(88, 3), "a mummy death curse"); + } +} + +void monster_die(monsters *monster, killer_type killer, + int killer_index, bool silent) { if (monster->type == -1) return; - if (!silent && _monster_avoided_death(monster, killer, i)) + if (!silent && _monster_avoided_death(monster, killer, killer_index)) return; mons_clear_trapping_net(monster); @@ -717,15 +782,14 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) bool drop_items = !hard_reset && !mons_is_holy(monster); #ifdef DGL_MILESTONES - _check_kill_milestone(monster, killer, i); + _check_kill_milestone(monster, killer, killer_index); #endif // Award experience for suicide if the suicide was caused by the // player. - if (MON_KILL(killer) && monster_killed == i) + if (MON_KILL(killer) && monster_killed == killer_index) { - const mon_enchant me = monster->get_ench(ENCH_CONFUSION); - if (me.ench == ENCH_CONFUSION && me.who == KC_YOU) + if (monster->confused_by_you()) killer = KILL_YOU_CONF; } else if (MON_KILL(killer) && monster->has_ench(ENCH_CHARM)) @@ -781,7 +845,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) if (killer == KILL_YOU) crawl_state.cancel_cmd_repeat(); - const bool pet_kill = _is_pet_kill(killer, i); + const bool pet_kill = _is_pet_kill(killer, killer_index); if (monster->type == MONS_GIANT_SPORE || monster->type == MONS_BALL_LIGHTNING) @@ -1020,10 +1084,11 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) && pet_kill) { bool notice = false; - const bool anon = (i == ANON_FRIENDLY_MONSTER); + const bool anon = (killer_index == ANON_FRIENDLY_MONSTER); const mon_holy_type targ_holy = mons_holiness(monster), - attacker_holy = anon ? MH_NATURAL : mons_holiness(&menv[i]); + attacker_holy = anon ? MH_NATURAL + : mons_holiness(&menv[killer_index]); if (attacker_holy == MH_UNDEAD) { @@ -1034,7 +1099,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) // "slave", and I think it's okay that Yredelemnul // ignores kills done by confused monsters as opposed // to enslaved or friendly ones. (jpeg) - if (mons_friendly(&menv[i])) + if (mons_friendly(&menv[killer_index])) { notice |= did_god_conduct(DID_LIVING_KILLED_BY_UNDEAD_SLAVE, @@ -1052,7 +1117,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) || you.religion == GOD_VEHUMET || you.religion == GOD_MAKHLEB || you.religion == GOD_LUGONU - || !anon && mons_is_god_gift(&menv[i])) + || !anon && mons_is_god_gift(&menv[killer_index])) { // Yes, we are splitting undead pets from the others // as a way to focus Necromancy vs Summoning (ignoring @@ -1114,9 +1179,9 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) && mons_is_evil_or_unholy(monster) && !player_under_penance() && random2(you.piety) >= piety_breakpoint(0) - && !invalid_monster_index(i)) + && !invalid_monster_index(killer_index)) { - monsters *mon = &menv[i]; + monsters *mon = &menv[killer_index]; // Randomly bless the follower who killed. if (!one_chance_in(3) && mon->alive() @@ -1139,9 +1204,9 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) && random2(you.piety) >= piety_breakpoint(2) && !player_under_penance() && !one_chance_in(3) - && !invalid_monster_index(i)) + && !invalid_monster_index(killer_index)) { - bless_follower(&menv[i]); + bless_follower(&menv[killer_index]); } } @@ -1221,25 +1286,9 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) } else if (!mons_is_summoned(monster)) { - if (monster->type == MONS_MUMMY) + if (mons_genus(monster->type) == MONS_MUMMY) { - if (YOU_KILL(killer) && killer != KILL_YOU_CONF) - curse_an_item(true); - } - else if (monster->type == MONS_GUARDIAN_MUMMY - || monster->type == MONS_GREATER_MUMMY - || monster->type == MONS_MUMMY_PRIEST) - { - if (YOU_KILL(killer) && killer != KILL_YOU_CONF) - { - mpr("You feel extremely nervous for a moment...", - MSGCH_MONSTER_SPELL); - - miscast_effect( SPTYP_NECROMANCY, - 3 + (monster->type == MONS_GREATER_MUMMY) * 8 - + (monster->type == MONS_MUMMY_PRIEST) * 5, - random2avg(88, 3), 100, "a mummy death curse" ); - } + _mummy_curse(monster, killer, killer_index); } } @@ -1253,7 +1302,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) KC_OTHER; unsigned int exp_gain = 0, avail_gain = 0; - _give_adjusted_experience(monster, killer, pet_kill, i, + _give_adjusted_experience(monster, killer, pet_kill, killer_index, &exp_gain, &avail_gain); PlaceInfo& curr_PlaceInfo = you.get_place_info(); @@ -1288,7 +1337,7 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent) } } - _fire_monster_death_event(monster, killer, i); + _fire_monster_death_event(monster, killer, killer_index); const coord_def mwhere = monster->pos(); if (drop_items) @@ -7504,7 +7553,7 @@ static void _mons_in_cloud(monsters *monster) return; beam.flavour = BEAM_CONFUSION; - beam.thrower = cloud.beam_thrower(); + beam.thrower = cloud.killer; if (cloud.whose == KC_FRIENDLY) beam.beam_source = ANON_FRIENDLY_MONSTER; @@ -7608,7 +7657,7 @@ static void _mons_in_cloud(monsters *monster) if (monster->hit_points < 1) { mon_enchant death_ench(ENCH_NONE, 0, cloud.whose); - monster_die(monster, death_ench.killer(), death_ench.kill_agent()); + monster_die(monster, cloud.killer, death_ench.kill_agent()); } } } diff --git a/crawl-ref/source/monstuff.h b/crawl-ref/source/monstuff.h index ab5eabbc14..eb3b90e846 100644 --- a/crawl-ref/source/monstuff.h +++ b/crawl-ref/source/monstuff.h @@ -89,7 +89,7 @@ bool monster_polymorph(monsters *monster, monster_type targetc, * spells1 - spells2 - spells3 - spells4 * *********************************************************************** */ void monster_die(monsters *monster, killer_type killer, - int i, bool silent = false); + int killer_index, bool silent = false); void mons_check_pool(monsters *monster, killer_type killer = KILL_NONE, int killnum = -1); @@ -112,7 +112,6 @@ void behaviour_event(monsters *mon, int event_type, int src = MHITNOT, * *********************************************************************** */ bool curse_an_item(bool decay_potions, bool quiet = false); - /* *********************************************************************** * called from: fight * *********************************************************************** */ diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc index 1db8c63795..5c83c00577 100644 --- a/crawl-ref/source/mstuff2.cc +++ b/crawl-ref/source/mstuff2.cc @@ -44,7 +44,7 @@ #include "spells1.h" #include "spells3.h" #include "spells4.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "stuff.h" #include "traps.h" @@ -59,8 +59,6 @@ void mons_trap(monsters *monster) if (!is_trap_square(grd[monster->x][monster->y])) return; - int temp_rand = 0; // probability determination {dlb} - // single calculation permissible {dlb} bool monsterNearby = mons_near(monster); @@ -282,13 +280,19 @@ void mons_trap(monsters *monster) // an "early return" - zot traps are *never* revealed - instead, // enchantment messages serve as clues to the trap's presence: {dlb} case TRAP_ZOT: - if (monsterNearby) + if (mons_friendly(monster) || mons_good_neutral(monster)) + { + MiscastEffect( monster, ZOT_TRAP_MISCAST, SPTYP_RANDOM, + 3, "the power of Zot" ); + return; // early return + } + else if (monsterNearby) { if (one_chance_in(5)) { mpr("The power of Zot is invoked against you!"); - miscast_effect( SPTYP_RANDOM, 10 + random2(30), - 75 + random2(100), 0, "the power of Zot" ); + MiscastEffect( &you, ZOT_TRAP_MISCAST, SPTYP_RANDOM, + 3, "the power of Zot" ); return; // early return {dlb} } @@ -303,30 +307,24 @@ void mons_trap(monsters *monster) mpr("You hear a distant \"Zot\"!", MSGCH_SOUND); } - // Determine trap effects upon monster, based upon - // whether it is naughty or nice to the player. {dlb} - - // NB: beem[].colour values are mislabeled as colours (?) - - // cf. mons_ench_f2() [which are also mislabeled] {dlb} - temp_rand = random2(16); - - beem.thrower = KILL_MON; // probably unnecessary - beem.aux_source.clear(); - - if (mons_friendly(monster)) + // XXX: It seem that back when a beam's colour determined its + // flavour that Zot traps would heal, haste or make invisible + // hostile monsters. The code has been fixed to work but + // commented out. +#if 0 + if (!mons_friendly(monster) && !mons_good_neutral(monster)) { - beem.colour = ((temp_rand < 3) ? CYAN : //paralyze - 3 in 16 - (temp_rand < 7) ? RED // confuse - 4 in 16 - : BLACK); // slow - 9 in 16 - } - else - { - beem.colour = ((temp_rand < 3) ? BLUE : //haste - 3 in 16 {dlb} - (temp_rand < 7) ? MAGENTA //invis - 4 in 16 {dlb} - : GREEN); // heal - 9 in 16 {dlb} + int temp_rand = random2(16); + + beem.thrower = KILL_MON; // probably unnecessary + beem.aux_source.clear(); + beem.flavour = ((temp_rand < 3) ? BEAM_HASTE : // 3 in 16 {dlb} + (temp_rand < 7) ? BEAM_INVISIBILITY //4 in 16 {dlb} + : BEAM_HEALING); // 9 in 16 {dlb} + mons_ench_f2(monster, beem); } +#endif - mons_ench_f2(monster, beem); damage_taken = 0; // just to be certain {dlb} break; @@ -2454,7 +2452,8 @@ bool orange_statue_effects(monsters *mons) { mpr("A hostile presence attacks your mind!", MSGCH_WARN); - miscast_effect(SPTYP_DIVINATION, random2(15), random2(150), 100, + MiscastEffect( &you, monster_index(mons), SPTYP_DIVINATION, + random2(15), random2(150), "an orange crystal statue"); return (true); } diff --git a/crawl-ref/source/ouch.h b/crawl-ref/source/ouch.h index 9408b60a6c..b949de14d6 100644 --- a/crawl-ref/source/ouch.h +++ b/crawl-ref/source/ouch.h @@ -58,7 +58,8 @@ enum kill_method_type KILLED_BY_CURARE, // 30 KILLED_BY_MELTING, KILLED_BY_BLEEDING, - KILLED_BY_BEOGH_SMITING, // 33 + KILLED_BY_BEOGH_SMITING, + KILLED_BY_DIVINE_WRATH, // 34 NUM_KILLBY }; diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index 23ca730271..3b5e0d7d7d 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -5954,12 +5954,12 @@ item_def *player::shield() return slot_item(EQ_SHIELD); } -std::string player::name(description_level_type type) const +std::string player::name(description_level_type type, bool) const { return (pronoun_you(type)); } -std::string player::pronoun(pronoun_type pro) const +std::string player::pronoun(pronoun_type pro, bool) const { switch (pro) { @@ -5977,6 +5977,54 @@ std::string player::conj_verb(const std::string &verb) const return (verb); } +std::string player::hand_name(bool plural, bool *can_plural) const +{ + if (can_plural != NULL) + *can_plural = true; + return your_hand(plural); +} + +std::string player::foot_name(bool plural, bool *can_plural) const +{ + bool _can_plural; + if (can_plural == NULL) + can_plural = &_can_plural; + *can_plural = true; + + std::string str; + + if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR) + { + str = "lowest portion"; + *can_plural = false; + } + else if (!transform_changed_physiology()) + { + if (player_mutation_level(MUT_HOOVES)) + str = "hoof"; + else if (player_mutation_level(MUT_TALONS)) + str = "talon"; + else if (you.species == SP_NAGA) + { + str = "underbelly"; + *can_plural = false; + } + else if (you.species == SP_MERFOLK && player_is_swimming()) + { + str = "tail"; + *can_plural = false; + } + } + + if (str.empty()) + return (plural ? "feet" : "foot"); + + if (plural && *can_plural) + str = pluralise(str); + + return str; +} + int player::id() const { return (-1); @@ -6335,6 +6383,11 @@ int player::res_rotting() const return 0; } +int player::res_torment() const +{ + return player_res_torment(); +} + bool player::confusable() const { return (player_mental_clarity() == 0); @@ -6423,9 +6476,9 @@ void player::expose_to_element(beam_type element, int st) ::expose_player_to_element(element, st); } -void player::blink() +void player::blink(bool allow_partial_control) { - random_blink(true); + random_blink(allow_partial_control); } void player::teleport(bool now, bool abyss_shift) diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index 08ecfd5d8c..0cc03f80bd 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -75,7 +75,7 @@ #include "spells3.h" #include "spells4.h" #include "spl-book.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "stash.h" #include "state.h" @@ -3257,7 +3257,7 @@ bool trog_burn_books() mpr( "The fire roars with new energy!" ); const int extra_dur = count + random2(rarity/2); env.cloud[cloud].decay += extra_dur * 5; - env.cloud[cloud].whose = KC_YOU; + env.cloud[cloud].set_whose(KC_YOU); continue; } @@ -3617,7 +3617,7 @@ static bool _elyvilon_retribution() break; case 2: // mostly flavour messages - miscast_effect(SPTYP_POISON, 0, 0, one_chance_in(3), + MiscastEffect(&you, -god, SPTYP_POISON, one_chance_in(3) ? 1 : 0, "the will of Elyvilon"); break; @@ -3703,8 +3703,8 @@ static bool _kikubaaqudgha_retribution() god_speaks(god, (coinflip()) ? "You hear Kikubaaqudgha cackling." : "Kikubaaqudgha's malice focuses upon you."); - miscast_effect(SPTYP_NECROMANCY, 5 + you.experience_level, - random2avg(88, 3), 100, "the malice of Kikubaaqudgha"); + MiscastEffect(&you, -god, SPTYP_NECROMANCY, 5 + you.experience_level, + random2avg(88, 3), "the malice of Kikubaaqudgha"); } return (true); @@ -3739,8 +3739,8 @@ static bool _yredelemnul_retribution() else { simple_god_message("'s anger turns toward you for a moment.", god); - miscast_effect( SPTYP_NECROMANCY, 5 + you.experience_level, - random2avg(88, 3), 100, "the anger of Yredelemnul" ); + MiscastEffect( &you, -god, SPTYP_NECROMANCY, 5 + you.experience_level, + random2avg(88, 3), "the anger of Yredelemnul" ); } return (true); @@ -3833,8 +3833,8 @@ static bool _trog_retribution() // fire magic. -- bwr dec_penance(god, 2); mpr( "You feel Trog's fiery rage upon you!", MSGCH_WARN ); - miscast_effect( SPTYP_FIRE, 8 + you.experience_level, - random2avg(98, 3), 100, "the fiery rage of Trog" ); + MiscastEffect( &you, -god, SPTYP_FIRE, 8 + you.experience_level, + random2avg(98, 3), "the fiery rage of Trog" ); } return (true); @@ -3849,7 +3849,7 @@ static bool _beogh_retribution() { case 0: // smiting (25%) case 1: - god_smites_you(GOD_BEOGH, KILLED_BY_BEOGH_SMITING); + god_smites_you(GOD_BEOGH); break; case 2: // send out one or two dancing weapons (12.5%) @@ -4014,7 +4014,8 @@ static bool _sif_muna_retribution() case 5: case 6: - miscast_effect(SPTYP_DIVINATION, 9, 90, 100, "the will of Sif Muna"); + MiscastEffect(&you, -god, SPTYP_DIVINATION, 9, 90, + "the will of Sif Muna"); break; case 7: @@ -4046,7 +4047,8 @@ static bool _lugonu_retribution() if (coinflip()) { simple_god_message("'s wrath finds you!", god); - miscast_effect( SPTYP_TRANSLOCATION, 9, 90, 100, "Lugonu's touch" ); + MiscastEffect( &you, -god, SPTYP_TRANSLOCATION, 9, 90, + "Lugonu's touch" ); // No return - Lugonu's touch is independent of other effects. } @@ -4108,9 +4110,10 @@ static bool _vehumet_retribution() const god_type god = GOD_VEHUMET; simple_god_message("'s vengeance finds you.", god); - miscast_effect( coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING, - 8 + you.experience_level, random2avg(98, 3), 100, - "the wrath of Vehumet" ); + MiscastEffect( &you, -god, + coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING, + 8 + you.experience_level, random2avg(98, 3), + "the wrath of Vehumet" ); return (true); } @@ -4641,7 +4644,7 @@ void beogh_idol_revenge() else revenge = _get_beogh_speech("idol other").c_str(); - god_smites_you(GOD_BEOGH, KILLED_BY_BEOGH_SMITING, revenge); + god_smites_you(GOD_BEOGH, revenge); if (you.religion == GOD_BEOGH) { @@ -4802,28 +4805,32 @@ void excommunication(god_type new_god) break; case GOD_KIKUBAAQUDGHA: - miscast_effect(SPTYP_NECROMANCY, 5 + you.experience_level, - random2avg(88, 3), 100, "the malice of Kikubaaqudgha"); + MiscastEffect(&you, -old_god, SPTYP_NECROMANCY, + 5 + you.experience_level, random2avg(88, 3), + "the malice of Kikubaaqudgha"); _inc_penance(old_god, 30); break; case GOD_YREDELEMNUL: - miscast_effect(SPTYP_NECROMANCY, 5 + you.experience_level, - random2avg(88, 3), 100, "the anger of Yredelemnul"); + MiscastEffect(&you, -old_god, SPTYP_NECROMANCY, + 5 + you.experience_level, random2avg(88, 3), + "the anger of Yredelemnul"); _inc_penance(old_god, 30); break; case GOD_VEHUMET: - miscast_effect((coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING), - 8 + you.experience_level, random2avg(98, 3), 100, - "the wrath of Vehumet"); + MiscastEffect(&you, -old_god, + (coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING), + 8 + you.experience_level, random2avg(98, 3), + "the wrath of Vehumet"); _inc_penance(old_god, 25); break; case GOD_MAKHLEB: - miscast_effect((coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING), - 8 + you.experience_level, random2avg(98, 3), 100, - "the fury of Makhleb"); + MiscastEffect(&you, -old_god, + (coinflip() ? SPTYP_CONJURATION : SPTYP_SUMMONING), + 8 + you.experience_level, random2avg(98, 3), + "the fury of Makhleb"); _inc_penance(old_god, 25); break; @@ -5665,9 +5672,12 @@ harm_protection_type god_protects_from_harm(god_type god, bool actual) return HPT_NONE; } -void god_smites_you(god_type god, kill_method_type death_type, - const char *message) +void god_smites_you(god_type god, const char *message, + kill_method_type death_type) + { + ASSERT(god != GOD_NO_GOD); + // Your god won't protect you from his own smiting, and Xom is too // capricious to protect you from any god's smiting. if (you.religion != god && you.religion != GOD_XOM @@ -5679,6 +5689,29 @@ void god_smites_you(god_type god, kill_method_type death_type, } else { + if (death_type == NUM_KILLBY) + switch(god) + { + case GOD_BEOGH: + death_type = KILLED_BY_BEOGH_SMITING; + break; + case GOD_SHINING_ONE: + death_type = KILLED_BY_TSO_SMITING; + break; + default: + death_type = KILLED_BY_DIVINE_WRATH; + break; + } + + std::string aux; + + if (death_type != KILLED_BY_BEOGH_SMITING + && death_type != KILLED_BY_TSO_SMITING) + { + aux = "smited by "; + aux += god_name(god); + } + // If there's a message, display it before smiting. if (message) god_speaks(god, message); @@ -5689,7 +5722,7 @@ void god_smites_you(god_type god, kill_method_type death_type, divine_hurt += random2( you.experience_level ); simple_god_message( " smites you!", god ); - ouch( divine_hurt, 0, death_type ); + ouch( divine_hurt, 0, death_type, aux.c_str() ); dec_penance( god, 1 ); } } diff --git a/crawl-ref/source/religion.h b/crawl-ref/source/religion.h index 2ff9afb3bf..fc46af69c1 100644 --- a/crawl-ref/source/religion.h +++ b/crawl-ref/source/religion.h @@ -95,8 +95,8 @@ bool god_hates_killing(god_type god, const monsters* mon); bool god_likes_butchery(god_type god); bool god_hates_butchery(god_type god); harm_protection_type god_protects_from_harm(god_type god, bool actual = true); -void god_smites_you(god_type god, kill_method_type death_type, - const char *message = NULL); +void god_smites_you(god_type god, const char *message = NULL, + kill_method_type death_type = NUM_KILLBY); void divine_retribution(god_type god); bool beogh_water_walk(); diff --git a/crawl-ref/source/spells1.cc b/crawl-ref/source/spells1.cc index e7d1d3715b..4164092c1f 100644 --- a/crawl-ref/source/spells1.cc +++ b/crawl-ref/source/spells1.cc @@ -537,7 +537,7 @@ bool conjure_flame(int pow) mpr( "The fire roars with new energy!" ); const int extra_dur = 2 + std::min(random2(pow) / 2, 20); env.cloud[cloud].decay += extra_dur * 5; - env.cloud[cloud].whose = KC_YOU; + env.cloud[cloud].set_whose(KC_YOU); return (true); } @@ -608,8 +608,22 @@ int cast_big_c(int pow, cloud_type cty, kill_category whose, bolt &beam) void big_cloud(cloud_type cl_type, kill_category whose, int cl_x, int cl_y, int pow, int size, int spread_rate) { + big_cloud(cl_type, whose, cloud_struct::whose_to_killer(whose), + cl_x, cl_y, pow, size, spread_rate); +} + +void big_cloud(cloud_type cl_type, killer_type killer, + int cl_x, int cl_y, int pow, int size, int spread_rate) +{ + big_cloud(cl_type, cloud_struct::killer_to_whose(killer), killer, + cl_x, cl_y, pow, size, spread_rate); +} + +void big_cloud(cloud_type cl_type, kill_category whose, killer_type killer, + int cl_x, int cl_y, int pow, int size, int spread_rate) +{ apply_area_cloud(make_a_normal_cloud, cl_x, cl_y, pow, size, - cl_type, whose, spread_rate); + cl_type, whose, killer, spread_rate); } static bool _mons_hostile(const monsters *mon) diff --git a/crawl-ref/source/spells1.h b/crawl-ref/source/spells1.h index 692fb5b73a..d2a2af9246 100644 --- a/crawl-ref/source/spells1.h +++ b/crawl-ref/source/spells1.h @@ -44,7 +44,10 @@ int cast_vitalisation(int pow); * *********************************************************************** */ void big_cloud(cloud_type cl_type, kill_category whose, int cl_x, int cl_y, int pow, int size, int spread_rate = -1); - +void big_cloud(cloud_type cl_type, killer_type killer, int cl_x, int cl_y, + int pow, int size, int spread_rate = -1); +void big_cloud(cloud_type cl_type, kill_category whose, killer_type killer, + int cl_x, int cl_y, int pow, int size, int spread_rate = -1); // last updated 24may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/spells2.cc b/crawl-ref/source/spells2.cc index 2736a57266..378832cd63 100644 --- a/crawl-ref/source/spells2.cc +++ b/crawl-ref/source/spells2.cc @@ -50,7 +50,7 @@ #include "randart.h" #include "religion.h" #include "spells4.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "stuff.h" #include "tiles.h" #include "terrain.h" diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc index 3cd219f7ff..4c7cdf143f 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -51,7 +51,7 @@ #include "skills.h" #include "spells1.h" #include "spells4.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "stuff.h" #include "terrain.h" @@ -887,13 +887,13 @@ static int _distortion_monsters(coord_def where, int pow, int message) { if (you.skills[SK_TRANSLOCATIONS] < random2(8)) { - miscast_effect( SPTYP_TRANSLOCATION, pow / 9 + 1, pow, 100, - "cast bend on self" ); + MiscastEffect( &you, NON_MONSTER, SPTYP_TRANSLOCATION, + pow / 9 + 1, pow, "cast bend on self" ); } else { - miscast_effect( SPTYP_TRANSLOCATION, 1, 1, 100, - "cast bend on self" ); + MiscastEffect( &you, NON_MONSTER, SPTYP_TRANSLOCATION, 1, 1, + "cast bend on self" ); } return 1; @@ -1079,11 +1079,15 @@ static int _make_a_rot_cloud(const coord_def& where, int pow, cloud_type ctype) } int make_a_normal_cloud(int x, int y, int pow, int spread_rate, - cloud_type ctype, kill_category whose) + cloud_type ctype, kill_category whose, + killer_type killer) { + if (killer == KILL_NONE) + killer = cloud_struct::whose_to_killer(whose); + place_cloud( ctype, coord_def(x, y), (3 + random2(pow / 4) + random2(pow / 4) + random2(pow / 4)), - whose, spread_rate ); + whose, killer, spread_rate ); return 1; } @@ -1681,7 +1685,7 @@ void do_monster_rot(int mon) if (mons_holiness(&menv[mon]) == MH_UNDEAD && !one_chance_in(5)) { apply_area_cloud(make_a_normal_cloud, menv[mon].x, menv[mon].y, - 10, 1, CLOUD_MIASMA, KC_YOU); + 10, 1, CLOUD_MIASMA, KC_YOU, KILL_YOU_MISSILE); } player_hurt_monster( mon, damage ); diff --git a/crawl-ref/source/spells4.h b/crawl-ref/source/spells4.h index aea2d92342..a98d295e4e 100644 --- a/crawl-ref/source/spells4.h +++ b/crawl-ref/source/spells4.h @@ -20,7 +20,8 @@ struct bolt; bool backlight_monsters(coord_def where, int pow, int garbage); int make_a_normal_cloud(int x, int y, int pow, int spread_rate, - cloud_type ctype, kill_category); + cloud_type ctype, kill_category, + killer_type killer = KILL_NONE); int disperse_monsters(coord_def where, int pow, int message); void cast_bend(int pow); diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index 2a1b9bf5a3..a1d0f448e6 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -39,6 +39,7 @@ #include "player.h" #include "religion.h" #include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -1326,20 +1327,23 @@ bool learn_spell(int book) if (you.inv[ book ].sub_type == BOOK_NECRONOMICON) { mpr("The pages of the Necronomicon glow with a dark malevolence..."); - miscast_effect( SPTYP_NECROMANCY, 8, random2avg(88, 3), 100, - "reading the Necronomicon" ); + MiscastEffect( &you, MISC_KNOWN_MISCAST, SPTYP_NECROMANCY, + 8, random2avg(88, 3), + "reading the Necronomicon" ); } else if (you.inv[ book ].sub_type == BOOK_DEMONOLOGY) { mpr("This book does not appreciate being disturbed by one of your ineptitude!"); - miscast_effect( SPTYP_SUMMONING, 7, random2avg(88, 3), 100, - "reading the book of Demonology" ); + MiscastEffect( &you, MISC_KNOWN_MISCAST, SPTYP_SUMMONING, + 7, random2avg(88, 3), + "reading the book of Demonology" ); } else if (you.inv[ book ].sub_type == BOOK_ANNIHILATIONS) { mpr("This book does not appreciate being disturbed by one of your ineptitude!"); - miscast_effect( SPTYP_CONJURATION, 8, random2avg(88, 3), 100, - "reading the book of Annihilations" ); + MiscastEffect( &you, MISC_KNOWN_MISCAST, SPTYP_CONJURATION, + 8, random2avg(88, 3), + "reading the book of Annihilations" ); } #ifdef WIZARD diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index dc0609259e..aebcbba27b 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -54,6 +54,7 @@ #include "spells3.h" #include "spells4.h" #include "spl-book.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -1125,14 +1126,6 @@ spret_type your_spells(spell_type spell, int powc, bool allow_fail) return (SPRET_FAIL); } - unsigned int sptype = 0; - - do - { - sptype = 1 << (random2(SPTYP_LAST_EXPONENT+1)); - } - while (!spell_typematch(spell, sptype)); - // all spell failures give a bit of magical radiation.. // failure is a function of power squared multiplied // by how badly you missed the spell. High power @@ -1146,8 +1139,8 @@ spret_type your_spells(spell_type spell, int powc, bool allow_fail) // miscasts are uncontrolled contaminate_player( cont_points ); - miscast_effect( sptype, spell_mana(spell), - spfail_chance - spfl, 100 ); + MiscastEffect( &you, NON_MONSTER, spell, spell_mana(spell), + spfail_chance - spfl ); return (SPRET_FAIL); } @@ -2214,80 +2207,617 @@ void exercise_spell( spell_type spell, bool spc, bool success ) did_god_conduct( DID_SPELL_PRACTISE, exer_norm ); } -static bool _send_abyss(const char *cause) +MiscastEffect::MiscastEffect(actor* _target, int _source, spell_type _spell, + int _pow, int _fail, std::string _cause, + nothing_happens_when_type _nothing_happens) : + target(_target), source(_source), cause(_cause), spell(_spell), + school(SPTYP_NONE), pow(_pow), fail(_fail), level(-1), kc(KC_NCATEGORIES), + kt(KILL_NONE), mon_target(NULL), mon_source(NULL), + nothing_happens_when(_nothing_happens) +{ + ASSERT(is_valid_spell(_spell)); + unsigned int schools = get_spell_disciplines(_spell); + ASSERT(schools != SPTYP_NONE); + ASSERT(!(schools & SPTYP_HOLY)); + + init(); + do_miscast(); +} + +MiscastEffect::MiscastEffect(actor *_target, int _source, + spschool_flag_type _school, int _level, + std::string _cause, + nothing_happens_when_type _nothing_happens) : + target(_target), source(_source), cause(_cause), spell(SPELL_NO_SPELL), + school(_school), pow(-1), fail(-1), level(_level), kc(KC_NCATEGORIES), + kt(KILL_NONE), mon_target(NULL), mon_source(NULL), + nothing_happens_when(_nothing_happens) +{ + ASSERT(!_cause.empty()); + ASSERT(count_bits(_school) == 1); + ASSERT(_school < SPTYP_HOLY || _school == SPTYP_RANDOM); + ASSERT(level >= 0 && level <= 3); + + init(); + do_miscast(); +} + +MiscastEffect::MiscastEffect(actor *_target, int _source, + spschool_flag_type _school, int _pow, int _fail, + std::string _cause, + nothing_happens_when_type _nothing_happens) : + target(_target), source(_source), cause(_cause), spell(SPELL_NO_SPELL), + school(_school), pow(_pow), fail(_fail), level(-1), kc(KC_NCATEGORIES), + kt(KILL_NONE), mon_target(NULL), mon_source(NULL), + nothing_happens_when(_nothing_happens) +{ + ASSERT(!_cause.empty()); + ASSERT(count_bits(_school) == 1); + ASSERT(_school < SPTYP_HOLY || _school == SPTYP_RANDOM); + + init(); + do_miscast(); +} + +void MiscastEffect::init() { - if (you.level_type != LEVEL_ABYSS) + ASSERT(spell != SPELL_NO_SPELL && school == SPTYP_NONE + || spell == SPELL_NO_SPELL && school != SPTYP_NONE); + ASSERT(pow != -1 && fail != -1 && level == -1 + || pow == -1 && fail == -1 && level >= 0 && level <= 3); + + ASSERT(target != NULL); + // A dead but not-yet-exploded giant spore or ball lightning *might* + // be the target of a miscast effect. + ASSERT(target->alive() || target->id() == MONS_GIANT_SPORE + || target->id() == MONS_BALL_LIGHTNING); + + source_known = target_known = false; + + mon_target = mon_source = NULL; + act_source = NULL; + + const bool death_curse = (cause.find("death curse") != std::string::npos); + + if (target->atype() == ACT_MONSTER) { - you.banish(cause ? cause : ""); - return (true); + mon_target = dynamic_cast<monsters*>(target); + target_known = you.can_see(mon_target); } else + target_known = true; + + kill_source = source; + if (source == WIELD_MISCAST || source == MELEE_MISCAST) { - mpr("The world appears momentarily distorted."); - return (false); + if (target->atype() == ACT_MONSTER) + { + mon_source = dynamic_cast<monsters*>(target); + kill_source = monster_index(mon_source); + } + else + kill_source = NON_MONSTER; + } + + if (kill_source == NON_MONSTER) + { + kc = KC_YOU; + kt = KILL_YOU_MISSILE; + act_source = dynamic_cast<actor*>(&you); + source_known = true; + } + else if (kill_source >= 0 && kill_source < NON_MONSTER) + { + mon_source = &menv[kill_source]; + act_source = dynamic_cast<actor*>(mon_source); + ASSERT(mon_source->type != -1); + + if (death_curse && target->atype() == ACT_MONSTER + && mon_target->confused_by_you()) + { + kt = KILL_YOU_CONF; + } + else if (!death_curse && mon_source->confused_by_you() + && !mons_friendly(mon_source)) + { + kt = KILL_YOU_CONF; + } + else + kt = KILL_MON_MISSILE; + + if (mons_friendly(mon_source)) + kc = KC_FRIENDLY; + else + kc = KC_OTHER; + + source_known = you.can_see(mon_source); + + if (target_known && death_curse) + source_known = true; + } + else + { + ASSERT(source == ZOT_TRAP_MISCAST + || source == MISC_KNOWN_MISCAST + || source == MISC_UNKNOWN_MISCAST + || (source < 0 && -source < NUM_GODS)); + + kc = KC_OTHER; + kt = KILL_MISC; + + if (source == ZOT_TRAP_MISCAST) + { + source_known = target_known; + + if (target->atype() == ACT_MONSTER + && mon_target->confused_by_you()) + { + kt = KILL_YOU_CONF; + } + } + else if (source == MISC_KNOWN_MISCAST) + source_known = true; + else if (source == MISC_UNKNOWN_MISCAST) + source_known = false; + else + source_known = true; + } + + ASSERT(kc != KC_NCATEGORIES && kt != KILL_NONE); + + if (death_curse && !invalid_monster_index(kill_source)) + { + if (starts_with(cause, "a ")) + cause.replace(cause.begin(), cause.begin() + 1, "an indirect"); + if (starts_with(cause, "an ")) + cause.replace(cause.begin(), cause.begin() + 2, "an indirect"); + else + cause = replace_all(cause, "death curse", "indirect death curse"); + } + + // source_known = false for MELEE_MISCAST so that melee miscasts + // won't give a "nothing happens" message. + if (source == MELEE_MISCAST) + source_known = false; + + beam.is_beam = false; + beam.is_explosion = true; + + if (cause.empty()) + cause = get_default_cause(); + beam.aux_source = cause; + beam.beam_source = kill_source; + beam.thrower = kt; +} + +std::string MiscastEffect::get_default_cause() +{ + // This is only for true miscasts, which means both a spell and that + // the source of the miscast is the same as the target of the miscast. + ASSERT(source >= 0 && source <= NON_MONSTER); + ASSERT(spell != SPELL_NO_SPELL); + ASSERT(school == SPTYP_NONE); + + if (source == NON_MONSTER) + { + ASSERT(target->atype() == ACT_PLAYER); + std::string str = "you miscasting "; + str += spell_title(spell); + return str; + } + + ASSERT(mon_source != NULL); + ASSERT(mon_source == mon_target); + + if (you.can_see(mon_source)) + { + std::string str = mon_source->base_name(DESC_PLAIN, false); + + str += "'s spell miscasting"; + str = replace_all(str, "s's", "s'"); + return str; + } + else + return "something's spell miscasting"; +} + +bool MiscastEffect::neither_end_silenced() +{ + return (!silenced(you.pos()) && !silenced(target->pos())); +} + +void MiscastEffect::do_miscast() +{ + // Repeated calls to do_miscast() on a single object instance have + // killed a target which was alive when the object was created, + // or the target is a dead but not-yet-exploded giant spore or + // ball lightning. + if (!target->alive()) + { + mprf(MSGCH_DIAGNOSTICS, "Miscast target '%s' already dead", + target->name(DESC_PLAIN, true).c_str()); + return; + } + + spschool_flag_type sp_type; + int severity; + + if (spell != SPELL_NO_SPELL) + { + std::vector<int> school_list; + for (int i = 0; i < SPTYP_LAST_EXPONENT; i++) + if (spell_typematch(spell, 1 << i)) + school_list.push_back(i); + + unsigned int _school = school_list[random2(school_list.size())]; + sp_type = static_cast<spschool_flag_type>(1 << _school); + } + else + { + sp_type = school; + if (sp_type == SPTYP_RANDOM) + { + int exp = (random2(SPTYP_LAST_EXPONENT)); + sp_type = (spschool_flag_type) (1 << exp); + } + } + + if (level != -1) + severity = level; + else + { + severity = (pow * fail * (10 + pow) / 7 * WILD_MAGIC_NASTINESS) / 100; + +#if DEBUG_DIAGNOSTICS || DEBUG_MISCAT + mprf(MSGCH_DIAGNOSTICS, "'%s' miscast power: %d", + spell != SPELL_NO_SPELL ? spell_title(spell) + : spelltype_short_name(sp_type), + severity); +#endif + + if (random2(40) > severity && random2(40) > severity) + { + if (target->atype() == ACT_PLAYER) + canned_msg(MSG_NOTHING_HAPPENS); + return; + } + + severity /= 100; + severity = random2(severity); + if (severity > 3) + severity = 3; + else if (severity < 0) + severity = 0; + } + +#if DEBUG_DIAGNOSTICS || DEBUG_MISCAT + mprf(MSGCH_DIAGNOSTICS, "Sptype: %u, severity: %d", + spelltype_short_name(sp_type), severity ); +#endif + + beam.ex_size = 1; + beam.name = ""; + beam.damage = dice_def(0, 0); + beam.flavour = BEAM_NONE; + beam.msg_generated = false; + beam.in_explosion_phase = false; + + // Do this here since multiple miscasts (wizmode testing) might move + // the target around. + beam.source_x = target->pos().x; + beam.source_y = target->pos().y; + beam.target_x = target->pos().x; + beam.target_y = target->pos().y; + beam.pos = target->pos(); + + all_msg = you_msg = mon_msg = mon_msg_seen = mon_msg_unseen = ""; + msg_ch = MSGCH_PLAIN; + + switch (sp_type) + { + case SPTYP_CONJURATION: _conjuration(severity); break; + case SPTYP_ENCHANTMENT: _enchantment(severity); break; + case SPTYP_TRANSLOCATION: _translocation(severity); break; + case SPTYP_SUMMONING: _summoning(severity); break; + case SPTYP_NECROMANCY: _necromancy(severity); break; + case SPTYP_TRANSMIGRATION: _transmigration(severity); break; + case SPTYP_FIRE: _fire(severity); break; + case SPTYP_ICE: _ice(severity); break; + case SPTYP_EARTH: _earth(severity); break; + case SPTYP_AIR: _air(severity); break; + case SPTYP_POISON: _poison(severity); break; + case SPTYP_DIVINATION: + // Divination miscasts have nothing in common between the player + // and monsters. + if (target->atype() == ACT_PLAYER) + _divination_you(severity); + else + _divination_mon(severity); + break; + + default: + ASSERT(false); + } + + if (target->atype() == ACT_PLAYER) + xom_is_stimulated(severity * 50); +} + +void MiscastEffect::do_msg(bool suppress_nothing_happnes) +{ + if (mon_target != NULL && !mons_near(mon_target)) + return; + + std::string msg; + + if (!all_msg.empty()) + msg = all_msg; + else if (target->atype() == ACT_PLAYER) + msg = you_msg; + else if (!mon_msg.empty()) + { + msg = mon_msg; + // Monster might be unseen with hands that can't be seen. + ASSERT(msg.find("@hand") == std::string::npos); + } + else + { + if (you.can_see(target)) + msg = mon_msg_seen; + else + { + msg = mon_msg_unseen; + // Can't see the hands of invisible monsters. + ASSERT(msg.find("@hand") == std::string::npos); + } + } + + if (msg.empty()) + { + if (!suppress_nothing_happnes + && (nothing_happens_when == NH_ALWAYS + || (nothing_happens_when == NH_DEFAULT && source_known + && target_known))) + { + canned_msg(MSG_NOTHING_HAPPENS); + } + + return; + } + + msg = replace_all(msg, "@hand@", target->hand_name(false)); + msg = replace_all(msg, "@hands@", target->hand_name(true)); + + if (target->atype() == ACT_MONSTER) + msg = do_mon_str_replacements(msg, mon_target, S_SILENT); + + mpr(msg.c_str(), msg_ch); +} + +void MiscastEffect::_ouch(int dam, beam_type flavour) +{ + do_msg(true); + + if (target->atype() == ACT_MONSTER) + { + bolt beem; + + beem.flavour = flavour; + dam = mons_adjust_flavoured(mon_target, beem, dam, true); + hurt_monster(mon_target, dam); + + if (mon_target->hit_points < 1) + monster_die(mon_target, kt, kill_source); + } + else + { + kill_method_type method; + + if (source == NON_MONSTER && spell != SPELL_NO_SPELL) + method = KILLED_BY_WILD_MAGIC; + else if (source == ZOT_TRAP_MISCAST) + method = KILLED_BY_TRAP; + else if (source < 0 && -source < NUM_GODS) + { + god_type god = static_cast<god_type>(-source); + + if (god == GOD_XOM && you.penance[GOD_XOM] == 0) + method = KILLED_BY_XOM; + else + { + method = KILLED_BY_DIVINE_WRATH; + + if (you.penance[god] == 0) + mpr("God making you miscast, but not under penance.", + MSGCH_DIAGNOSTICS); + } + } + else if (source >= 0 && source < NON_MONSTER) + method = KILLED_BY_MONSTER; + else + method = KILLED_BY_SOMETHING; + + dam = check_your_resists(dam, flavour); + + bool see_source = false; + if (kill_source != NON_MONSTER) + see_source = you.can_see(&menv[kill_source]); + + ouch(dam, kill_source, method, cause.c_str(), see_source); + } +} + +void MiscastEffect::_explosion() +{ + ASSERT(!beam.name.empty()); + ASSERT(beam.damage.num != 0 && beam.damage.size != 0); + ASSERT(beam.flavour != BEAM_NONE); + + do_msg(true); + explosion(beam, false, true); +} + +void MiscastEffect::_potion_effect(int pot_eff, int pot_pow) +{ + if (target->atype() == ACT_PLAYER) + { + potion_effect(static_cast<potion_type>(pot_eff), pot_pow, false); + return; + } + + switch(pot_eff) + { + case POT_LEVITATION: + // There's no levitation enchantment for monsters, and anyways + // it's not nearly as inconveniencing for monsters as for the + // player, so backlight them instead. + mon_target->add_ench( mon_enchant(ENCH_BACKLIGHT, pot_pow, kc) ); + break; + case POT_BERSERK_RAGE: + if (target->can_go_berserk()) + { + target->go_berserk(false); + break; + } + // Intentional fallthrough if that didn't work. + case POT_SLOWING: + target->slow_down(pot_pow); + break; + case POT_PARALYSIS: + target->paralyse(pot_pow); + break; + case POT_CONFUSION: + target->confuse(pot_pow); + break; + + default: + ASSERT(false); + } +} + +void MiscastEffect::send_abyss() +{ + if (you.level_type == LEVEL_ABYSS) + { + you_msg = "The world appears momentarily distorted."; + mon_msg_seen = "@The_monster@ wobbles for a moment."; + mon_msg_unseen = "An empty piece of space momentarily distorts."; + do_msg(); + return; + } + + target->banish(cause); +} + +bool MiscastEffect::_create_monster(monster_type what, int abj_deg, + bool alert) +{ + const god_type god = + (crawl_state.is_god_acting()) ? crawl_state.which_god_acting() + : GOD_NO_GOD; + + mgen_data data = mgen_data::hostile_at(what, target->pos(), + abj_deg, 0, alert, god); + + // hostile_at() assumes the monster is hostile to the player, + // but should be hostile to the target monster unless the miscast + // is a result of either divine wrath or a Zot trap. + if (target->atype() == ACT_MONSTER && you.penance[god] == 0 + && source != ZOT_TRAP_MISCAST) + { + switch(mon_target->temp_attitude()) + { + case ATT_FRIENDLY: data.behaviour = BEH_HOSTILE; break; + case ATT_HOSTILE: data.behaviour = BEH_FRIENDLY; break; + case ATT_GOOD_NEUTRAL: + case ATT_NEUTRAL: + data.behaviour = BEH_NEUTRAL; + break; + } + + if (alert) + data.foe = monster_index(mon_target); + + // No permanent allies from miscasts. + if (data.behaviour == BEH_FRIENDLY && abj_deg == 0) + data.abjuration_duration = 6; } + + return (create_monster(data) != -1); } -static void _miscast_conjuration(int severity, const char* cause) +void MiscastEffect::_conjuration(int severity) { - bolt beam; switch (severity) { case 0: // just a harmless message switch (random2(10)) { case 0: - msg::stream << "Sparks fly from your " << your_hand(true) - << '!' << std::endl; + you_msg = "Sparks fly from your @hands@!"; + mon_msg_seen = "Sparks fly from @the_monster@'s @hands@!"; break; case 1: - mpr("The air around you crackles with energy!"); + you_msg = "The air around you crackles with energy!"; + mon_msg_seen = "The air around @the_monster@ crackles " + "with energy!"; break; case 2: - msg::stream << "Wisps of smoke drift from your " - << your_hand(true) << '.' << std::endl; + you_msg = "Wisps of smoke drift from your @hands@."; + mon_msg_seen = "Wisps of smoke drift from @the_monster@'s " + "@hands@."; break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - mpr("You are momentarily dazzled by a flash of light!"); + you_msg = "You are momentarily dazzled by a flash of light!"; + mon_msg_seen = "@The_monster@ emits a flash of light!"; break; case 5: - mpr("Strange energies run through your body."); + you_msg = "Strange energies run through your body."; + // Monster messages needed. break; case 6: - mpr("Your skin tingles."); + you_msg = "Your skin tingles."; + // Monster messages needed. break; case 7: - mpr("Your skin glows momentarily."); + you_msg = "Your skin glows momentarily."; + mon_msg_seen = "@The_monster@ glows momentarily."; + // A smal glow isn't going to make it past invisibility. break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: if (player_can_smell()) - mpr("You smell something strange."); + all_msg = "You smell something strange."; else if (you.species == SP_MUMMY) - mpr("Your bandages flutter."); - else - canned_msg(MSG_NOTHING_HAPPENS); + you_msg = "Your bandages flutter."; } + do_msg(); break; case 1: // a bit less harmless stuff switch (random2(2)) { case 0: - msg::stream << "Smoke pours from your " << your_hand(true) - << '!' << std::endl; - big_cloud( CLOUD_GREY_SMOKE, (cause ? KC_OTHER : KC_YOU), - you.x_pos, you.y_pos, 20, - 7 + random2(7) ); + you_msg = "Smoke pours from your @hands@!"; + mon_msg_seen = "Smoke pours from @the_monster@'s @hands@!"; + mon_msg_unseen = "Smoke appears from out of nowhere!"; + + do_msg(); + big_cloud( CLOUD_GREY_SMOKE, kc, kt, + target->pos().x, target->pos().y, + 20, 7 + random2(7) ); break; case 1: - mpr("A wave of violent energy washes through your body!"); - ouch(6 + random2avg(7, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "A wave of violent energy washes through your body!"; + // Monster messages needed. + _ouch(6 + random2avg(7, 2)); break; } break; @@ -2296,29 +2826,24 @@ static void _miscast_conjuration(int severity, const char* cause) switch (random2(2)) { case 0: - mpr("Energy rips through your body!"); - ouch(9 + random2avg(17, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "Energy rips through your body!"; + // Monster messages needed. + _ouch(9 + random2avg(17, 2)); break; case 1: - mpr("You are caught in a violent explosion!"); - beam.type = dchar_glyph(DCHAR_FIRED_BURST); - beam.damage = dice_def( 3, 12 ); + you_msg = "You are caught in a violent explosion!"; + mon_msg_seen = "@The_monster@ is caugh in a violent explosion!"; + mon_msg_unseen = "A violent explosion happens from " + "out of thin air!"; + + beam.damage = dice_def( 3, 12 ); beam.flavour = BEAM_MISSILE; // unsure about this // BEAM_EXPLOSION instead? {dlb} - beam.target_x = you.x_pos; - beam.target_y = you.y_pos; - beam.name = "explosion"; + beam.name = "explosion"; beam.colour = random_colour(); - beam.beam_source = NON_MONSTER; - beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source.clear(); - if (cause) - beam.aux_source = cause; - beam.ex_size = 1; - beam.is_explosion = true; - explosion(beam); + _explosion(); break; } break; @@ -2327,34 +2852,53 @@ static void _miscast_conjuration(int severity, const char* cause) switch (random2(2)) { case 0: - mpr("You are blasted with magical energy!"); - ouch(12 + random2avg(29, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "You are blasted with magical energy!"; + mon_msg_seen = "@The_monster@ is blasted with magical energy!"; + // No message for invis monsters? + _ouch(12 + random2avg(29, 2)); break; case 1: - mpr("There is a sudden explosion of magical energy!"); - beam.type = dchar_glyph(DCHAR_FIRED_BURST); - beam.damage = dice_def( 3, 20 ); + all_msg = "There is a sudden explosion of magical energy!"; + + beam.type = dchar_glyph(DCHAR_FIRED_BURST); + beam.damage = dice_def( 3, 20 ); beam.flavour = BEAM_MISSILE; // unsure about this // BEAM_EXPLOSION instead? {dlb} - beam.target_x = you.x_pos; - beam.target_y = you.y_pos; - beam.name = "explosion"; - beam.colour = random_colour(); - beam.beam_source = NON_MONSTER; - beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source.clear(); - if (cause) - beam.aux_source = cause; + beam.name = "explosion"; + beam.colour = random_colour(); beam.ex_size = coinflip() ? 1 : 2; - beam.is_explosion = true; - explosion(beam); + _explosion(); break; } } } -static void _miscast_enchantment(int severity, const char* cause) +static void _your_hands_glow(actor* target, std::string& you_msg, + std::string& mon_msg_seen) +{ + you_msg = "Your @hands@ "; + mon_msg_seen = "@The_monster@'s @hands@ "; + // No message for invisible monsters. + + bool pluralized = true; + target->hand_name(true, &pluralized); + + if (pluralized) + { + you_msg += "glow"; + mon_msg_seen += "glow"; + } + else + { + you_msg += "glows"; + mon_msg_seen += "glows"; + } + you_msg += " momentarily."; + mon_msg_seen += " momentarily."; +} + +void MiscastEffect::_enchantment(int severity) { switch (severity) { @@ -2362,51 +2906,67 @@ static void _miscast_enchantment(int severity, const char* cause) switch (random2(10)) { case 0: - msg::stream << "Your " << your_hand(true) - << " glow momentarily." << std::endl; + _your_hands_glow(target, you_msg, mon_msg_seen); break; case 1: - mpr("The air around you crackles with energy!"); + you_msg = "The air around you crackles with energy!"; + mon_msg_seen = "The air around @the_monster@ crackles with" + " energy!"; break; case 2: - mpr("Multicolored lights dance before your eyes!"); + you_msg = "Multicolored lights dance before your eyes!"; + // Monster messages needed. break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - mpr("Waves of light ripple over your body."); + you_msg = "Waves of light ripple over your body."; + mon_msg_seen = "Waves of light ripple over @the_monster@'s body."; break; case 5: - mpr("Strange energies run through your body."); + you_msg = "Strange energies run through your body."; + // Monster messages needed. break; case 6: - mpr("Your skin tingles."); + you_msg = "Your skin tingles."; + // Monster messages needed. break; case 7: - mpr("Your skin glows momentarily."); + you_msg = "Your skin glows momentarily."; + mon_msg_seen = "@The_monster@'s body glows momentarily."; break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - if (!silenced(you.pos())) - mpr("You hear something strange.", MSGCH_SOUND); - else if (you.attribute[ATTR_TRANSFORMATION] != TRAN_AIR) - mpr("Your skull vibrates slightly."); - else - canned_msg(MSG_NOTHING_HAPPENS); + if (neither_end_silenced()) + { + // XXX: Should use noisy(). + all_msg = "You hear something strange."; + msg_ch = MSGCH_SOUND; + return; + } + else if (target->atype() == ACT_PLAYER + && you.attribute[ATTR_TRANSFORMATION] != TRAN_AIR) + { + you_msg = "Your skull vibrates slightly."; + } break; } + do_msg(); break; case 1: // slightly annoying switch (random2(2)) { case 0: - potion_effect(POT_LEVITATION, 20); + _potion_effect(POT_LEVITATION, 20); break; case 1: + // XXX: Something else for monsters? random_uselessness(); break; } @@ -2418,24 +2978,39 @@ static void _miscast_enchantment(int severity, const char* cause) case 0: case 1: case 2: - mpr("You sense a malignant aura."); - curse_an_item(false); - break; + if (target->atype() == ACT_PLAYER) + { + mpr("You sense a malignant aura."); + curse_an_item(false); + break; + } + // Intentional fall-through for monsters. case 3: case 4: case 5: - potion_effect(POT_SLOWING, 10); + _potion_effect(POT_SLOWING, 10); break; case 6: - potion_effect(POT_BERSERK_RAGE, 10); + _potion_effect(POT_BERSERK_RAGE, 10); break; } break; case 3: // potentially lethal - switch (random2(4)) + // Only use first two cases for monsters. + switch (random2(target->atype() == ACT_PLAYER ? 4 : 2)) { case 0: + _potion_effect(POT_PARALYSIS, 10); + break; + case 1: + _potion_effect(POT_CONFUSION, 10); + break; + case 2: + mpr("You feel saturated with unharnessed energies!"); + you.magic_contamination += random2avg(19,3); + break; + case 3: do { curse_an_item(false); @@ -2444,63 +3019,61 @@ static void _miscast_enchantment(int severity, const char* cause) mpr("You sense an overwhelmingly malignant aura!"); break; - case 1: - potion_effect(POT_PARALYSIS, 10); - break; - case 2: - potion_effect(POT_CONFUSION, 10); - break; - case 3: - mpr("You feel saturated with unharnessed energies!"); - you.magic_contamination += random2avg(19,3); - break; } break; } } -static void _miscast_translocation(int severity, const char* cause) +void MiscastEffect::_translocation(int severity) { - const god_type god = - (crawl_state.is_god_acting()) ? crawl_state.which_god_acting() - : GOD_NO_GOD; - switch (severity) { case 0: // harmless messages only switch (random2(10)) { case 0: - mpr("Space warps around you."); + you_msg = "Space warps around you."; + mon_msg_seen = "Space warps around @the_monster@."; break; case 1: - mpr("The air around you crackles with energy!"); + you_msg = "The air around you crackles with energy!"; + mon_msg_seen = "The air around @the_monster@ crackles with " + "energy!"; break; case 2: - mpr("You feel a wrenching sensation."); + you_msg = "You feel a wrenching sensation."; + // Monster messages needed. break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - mpr("You spin around."); + you_msg = "You spin around."; + mon_msg_seen = "@The_monster@ spins around."; break; case 5: - mpr("Strange energies run through your body."); + you_msg = "Strange energies run through your body."; + // Monster messages needed. break; case 6: - mpr("Your skin tingles."); + you_msg = "Your skin tingles."; + // Monster messages needed. break; case 7: - mpr("The world appears momentarily distorted!"); + you_msg = "The world appears momentarily distorted!"; + mon_msg_seen = "@The_monster@ appears momentarily distorted!"; break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - mpr("You feel uncomfortable."); + you_msg = "You feel uncomfortable."; + // Monster messages needed. break; } + do_msg(); break; case 1: // mostly harmless @@ -2509,24 +3082,25 @@ static void _miscast_translocation(int severity, const char* cause) case 0: case 1: case 2: - mpr("You are caught in a localised field of spatial distortion."); - ouch(4 + random2avg(9, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "You are caught in a localised field of spatial " + "distortion."; + mon_msg_seen = "@The_monster@ is caught in a localised field of " + "spatial distortion."; + mon_msg_unseen = "A piece of empty space twists and distorts."; + _ouch(4 + random2avg(9, 2)); break; case 3: case 4: - mpr("Space bends around you!"); - random_blink(false); - ouch(4 + random2avg(7, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "Space bends around you!"; + mon_msg_seen = "Space bends around @the_monster@!"; + mon_msg_unseen = "A piece of empty space twists and distorts."; + _ouch(4 + random2avg(7, 2)); + target->blink(false); break; case 5: - if (create_monster( - mgen_data::hostile_at(MONS_SPATIAL_VORTEX, - you.pos(), 3, 0, false, god)) != -1) - { - mpr("Space twists in upon itself!"); - } - else - canned_msg(MSG_NOTHING_HAPPENS); + if (_create_monster(MONS_SPATIAL_VORTEX, 3)) + all_msg = "Space twists in upon itself!"; + do_msg(); break; } break; @@ -2537,20 +3111,27 @@ static void _miscast_translocation(int severity, const char* cause) case 0: case 1: case 2: - mpr("You are caught in a strong localised spatial distortion."); - ouch(9 + random2avg(23, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "You are caught in a strong localised spatial " + "distortion."; + mon_msg_seen = "@The_monster@ is caught in a strong localised " + "spatial distortion."; + mon_msg_unseen = "A piece of empty space twists and writhes."; + _ouch(9 + random2avg(23, 2)); break; case 3: case 4: - mpr("Space warps around you!"); + you_msg = "Space warps around you!"; + mon_msg_seen = "Space warps around @the_monster!"; + mon_msg_unseen = "A piece of empty space twists and writhes."; + + _ouch(5 + random2avg(9, 2)); if (one_chance_in(3)) - you_teleport_now( true ); + target->teleport(true); else - random_blink( false ); + target->blink(false); - ouch(5 + random2avg(9, 2), 0, KILLED_BY_WILD_MAGIC, cause); - potion_effect(POT_CONFUSION, 40); + _potion_effect(POT_CONFUSION, 40); break; case 5: { @@ -2558,42 +3139,43 @@ static void _miscast_translocation(int severity, const char* cause) for (int i = 1 + random2(3); i >= 0; --i) { - if (create_monster( - mgen_data::hostile_at(MONS_SPATIAL_VORTEX, - you.pos(), 3, 0, false, god)) != -1) - { + if (_create_monster(MONS_SPATIAL_VORTEX, 3)) success = true; - } } if (success) - mpr("Space twists in upon itself!"); - else - canned_msg(MSG_NOTHING_HAPPENS); + all_msg = "Space twists in upon itself!"; break; } case 6: - _send_abyss(cause); + send_abyss(); break; } break; case 3: // much less harmless - switch (random2(4)) + // Don't use the last case for monsters. + switch (random2(target->atype() == ACT_PLAYER ? 4 : 3)) { case 0: - mpr("You are caught in an extremely strong localised spatial distortion!"); - ouch(15 + random2avg(29, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "You are caught in an extremely strong localised " + "spatial distortion!"; + mon_msg_seen = "@The_monster@ is caught in an extremely strong " + "localised spatial distortion!"; + mon_msg_unseen = "A rift temporarily opens in the fabric of space!"; + _ouch(15 + random2avg(29, 2)); break; case 1: - mpr("Space warps crazily around you!"); - you_teleport_now( true ); + you_msg = "Space warps crazily around you!"; + mon_msg_seen = "Space warps crazily around @the_monster@!"; + mon_msg_unseen = "A rift temporarily opens in the fabric of space!"; - ouch(9 + random2avg(17, 2), 0, KILLED_BY_WILD_MAGIC, cause); + _ouch(9 + random2avg(17, 2)); + you_teleport_now( true ); potion_effect(POT_CONFUSION, 60); break; case 2: - _send_abyss(cause); + send_abyss(); break; case 3: mpr("You feel saturated with unharnessed energies!"); @@ -2604,51 +3186,68 @@ static void _miscast_translocation(int severity, const char* cause) } } -static void _miscast_summoning(int severity, const char* cause) +void MiscastEffect::_summoning(int severity) { - const god_type god = - (crawl_state.is_god_acting()) ? crawl_state.which_god_acting() - : GOD_NO_GOD; - switch (severity) { case 0: // harmless messages only switch (random2(10)) { case 0: - mpr("Shadowy shapes form in the air around you, then vanish."); + you_msg = "Shadowy shapes form in the air around you, " + "then vanish."; + mon_msg_seen = "Shadowy shapes form in the air around " + "@the_monster@, then vanish."; break; case 1: - if (!silenced(you.pos())) - mpr("You hear strange voices.", MSGCH_SOUND); - else - mpr("You feel momentarily dizzy."); + if (neither_end_silenced()) + { + all_msg = "You hear strange voices."; + msg_ch = MSGCH_SOUND; + } + else if (target->atype() == ACT_PLAYER) + you_msg = "You feel momentarily dizzy."; break; case 2: - mpr("Your head hurts."); + you_msg = "Your head hurts."; + // Monster messages needed. break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - mpr("Your brain hurts!"); + you_msg = "Your brain hurts!"; + // Monster messages needed. break; case 5: - mpr("Strange energies run through your body."); + you_msg = "Strange energies run through your body."; + // Monster messages needed. break; case 6: - mpr("The world appears momentarily distorted."); + you_msg = "The world appears momentarily distorted."; + mon_msg_seen = "@The_monster@ appears momentarily distorted."; break; case 7: - mpr("Space warps around you."); + you_msg = "Space warps around you."; + mon_msg_seen = "Space warps around @the_monster@."; break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - mpr("Distant voices call out to you!"); + if (neither_end_silenced()) + { + you_msg = "Distant voices call out to you!"; + mon_msg_seen = "Distant voices call out to @the_monster@!"; + msg_ch = MSGCH_SOUND; + } + else if (target->atype() == ACT_PLAYER) + you_msg = "You feel watched."; break; } + do_msg(); break; case 1: // a little bad @@ -2657,30 +3256,23 @@ static void _miscast_summoning(int severity, const char* cause) case 0: // identical to translocation case 1: case 2: - mpr("You are caught in a localised spatial distortion."); - ouch(5 + random2avg(9, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "You are caught in a localised spatial " + "distortion."; + mon_msg_seen = "@The_monster@ is caught in a localised spatial " + "distortion."; + mon_msg_unseen = "An empty piece of space distorts and twists."; + _ouch(5 + random2avg(9, 2)); break; case 3: - if (create_monster( - mgen_data::hostile_at(MONS_SPATIAL_VORTEX, - you.pos(), 3, 0, false, god)) != -1) - { - mpr("Space twists in upon itself!"); - } - else - canned_msg(MSG_NOTHING_HAPPENS); + if (_create_monster(MONS_SPATIAL_VORTEX, 3)) + all_msg = "Space twists in upon itself!"; + do_msg(); break; case 4: case 5: - if (create_monster( - mgen_data::hostile_at( - summon_any_demon(DEMON_LESSER), - you.pos(), 5, 0, true, god)) != -1) - { - mpr("Something appears in a flash of light!"); - } - else - canned_msg(MSG_NOTHING_HAPPENS); + if (_create_monster(summon_any_demon(DEMON_LESSER), 5, true)) + all_msg = "Something appears in a flash of light!"; + do_msg(); break; } break; @@ -2694,32 +3286,21 @@ static void _miscast_summoning(int severity, const char* cause) for (int i = 1 + random2(3); i >= 0; --i) { - if (create_monster( - mgen_data::hostile_at(MONS_SPATIAL_VORTEX, - you.pos(), 3, 0, false, god)) != -1) - { + if (_create_monster(MONS_SPATIAL_VORTEX, 3)) success = true; - } } if (success) - mpr("Space twists in upon itself!"); - else - canned_msg(MSG_NOTHING_HAPPENS); + all_msg = "Space twists in upon itself!"; + do_msg(); break; } case 1: case 2: - if (create_monster( - mgen_data::hostile_at( - summon_any_demon(DEMON_COMMON), - you.pos(), 5, 0, true, god)) != -1) - { - mpr("Something forms out of thin air!"); - } - else - canned_msg(MSG_NOTHING_HAPPENS); + if (_create_monster(summon_any_demon(DEMON_COMMON), 5, true)) + all_msg = "Something forms from out of thin air!"; + do_msg(); break; case 3: @@ -2730,19 +3311,17 @@ static void _miscast_summoning(int severity, const char* cause) for (int i = 1 + random2(2); i >= 0; --i) { - if (create_monster( - mgen_data::hostile_at( - summon_any_demon(DEMON_LESSER), - you.pos(), 5, 0, true, god)) != -1) - { + if (_create_monster(summon_any_demon(DEMON_LESSER), 5, true)) success = true; - } } - if (success) - mpr("A chorus of chattering voices calls out to you!"); - else - canned_msg(MSG_NOTHING_HAPPENS); + if (success && neither_end_silenced()) + { + you_msg = "A chorus of chattering voices calls out to you!"; + mon_msg = "A chorus of chattering voices calls out!"; + msg_ch = MSGCH_SOUND; + } + do_msg(); break; } } @@ -2752,26 +3331,15 @@ static void _miscast_summoning(int severity, const char* cause) switch (random2(4)) { case 0: - if (create_monster( - mgen_data::hostile_at(MONS_ABOMINATION_SMALL, - you.pos(), 0, 0, true, god)) != -1) - { - mpr("Something forms out of thin air."); - } - else - canned_msg(MSG_NOTHING_HAPPENS); + if (_create_monster(MONS_ABOMINATION_SMALL, 0, true)) + all_msg = "Something forms from out of thin air."; + do_msg(); break; case 1: - if (create_monster( - mgen_data::hostile_at( - summon_any_demon(DEMON_GREATER), - you.pos(), 0, 0, true, god)) != -1) - { - mpr("You sense a hostile presence."); - } - else - canned_msg(MSG_NOTHING_HAPPENS); + if (_create_monster(summon_any_demon(DEMON_GREATER), 0, true)) + all_msg = "You sense a hostile presence."; + do_msg(); break; case 2: @@ -2780,31 +3348,29 @@ static void _miscast_summoning(int severity, const char* cause) for (int i = 1 + random2(2); i >= 0; --i) { - if (create_monster( - mgen_data::hostile_at( - summon_any_demon(DEMON_COMMON), - you.pos(), 3, 0, true, god)) != -1) - { + if (_create_monster(summon_any_demon(DEMON_COMMON), 3, true)) success = true; - } } if (success) - mpr("Something turns its malign attention towards you..."); - else - canned_msg(MSG_NOTHING_HAPPENS); + { + you_msg = "Something turns its malign attention towards " + "you..."; + mon_msg = "You sense a malign presence."; + } + do_msg(); break; } case 3: - _send_abyss(cause); + send_abyss(); break; } break; } } -static void _miscast_divination(int severity, const char* cause) +void MiscastEffect::_divination_you(int severity) { switch (severity) { @@ -2912,9 +3478,14 @@ static void _miscast_divination(int severity, const char* cause) } } -static void _miscast_necromancy(int severity, const char* cause) +// XXX: Monster divination miscasts. +void MiscastEffect::_divination_mon(int severity) +{ +} + +void MiscastEffect::_necromancy(int severity) { - if (you.religion == GOD_KIKUBAAQUDGHA + if (target->atype() == ACT_PLAYER && you.religion == GOD_KIKUBAAQUDGHA && !player_under_penance() && you.piety >= piety_breakpoint(1) && x_chance_in_y(you.piety, 150)) { @@ -2922,10 +3493,6 @@ static void _miscast_necromancy(int severity, const char* cause) return; } - const god_type god = - (crawl_state.is_god_acting()) ? crawl_state.which_god_acting() - : GOD_NO_GOD; - switch (severity) { case 0: @@ -2933,41 +3500,50 @@ static void _miscast_necromancy(int severity, const char* cause) { case 0: if (player_can_smell()) - mpr("You smell decay."); + all_msg = "You smell decay."; else if (you.species == SP_MUMMY) - mpr("Your bandages flutter."); - else - canned_msg(MSG_NOTHING_HAPPENS); + you_msg = "Your bandages flutter."; break; case 1: - if (!silenced(you.pos())) - mpr("You hear strange and distant voices.", MSGCH_SOUND); - else - mpr("You feel homesick."); + if (neither_end_silenced()) + { + all_msg = "You hear strange and distant voices."; + msg_ch = MSGCH_SOUND; + } + else if (target->atype() == ACT_PLAYER) + you_msg = "You feel homesick."; break; case 2: - mpr("Pain shoots through your body."); + you_msg = "Pain shoots through your body."; + // Monster messages needed. break; case 3: - mpr("Your bones ache."); + you_msg = "Your bones ache."; + // Monster messages needed. break; case 4: - mpr("The world around you seems to dim momentarily."); + you_msg = "The world around you seems to dim momentarily."; + mon_msg_seen = "@The_monster@ seems to dim momentarily."; break; case 5: - mpr("Strange energies run through your body."); + you_msg = "Strange energies run through your body."; + // Monster messages needed. break; case 6: - mpr("You shiver with cold."); + you_msg = "You shiver with cold."; + // Monster messages needed. break; case 7: - mpr("You sense a malignant aura."); + you_msg = "You sense a malignant aura."; + // Monster messages needed. break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - mpr("You feel very uncomfortable."); + you_msg = "You feel very uncomfortable."; + // Monster messages needed. break; } break; @@ -2976,29 +3552,37 @@ static void _miscast_necromancy(int severity, const char* cause) switch (random2(3)) { case 0: - if (player_res_torment()) + // Monster messages needed. + if (target->res_torment()) { - mpr("You feel weird for a moment."); + you_msg = "You feel weird for a moment."; + do_msg(); break; } - mpr("Pain shoots through your body!"); - ouch(5 + random2avg(15, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "Pain shoots through your body!"; + _ouch(5 + random2avg(15, 2)); break; case 1: - mpr("You feel horribly lethargic."); - potion_effect(POT_SLOWING, 15); + you_msg = "You feel horribly lethargic."; + // Monster messages needed. + _potion_effect(POT_SLOWING, 15); break; case 2: // josh declares mummies cannot smell {dlb} - if (player_can_smell() && you.res_rotting() == 0) + if (target->res_rotting() == 0) { - mpr("You smell decay."); // identical to a harmless message - you.rotting++; + if (player_can_smell()) + // identical to a harmless message + all_msg = "You smell decay."; + if (target->atype() == ACT_PLAYER) + you.rotting++; + else + mon_target->add_ench( mon_enchant(ENCH_ROT, 1, kc) ); } else if (you.species == SP_MUMMY) - mpr("Your bandages flutter."); - else - canned_msg(MSG_NOTHING_HAPPENS); + // Monster messages needed. + you_msg = "Your bandages flutter."; + do_msg(); break; } break; @@ -3012,97 +3596,100 @@ static void _miscast_necromancy(int severity, const char* cause) for (int i = random2(3); i >= 0; --i) { - if (create_monster( - mgen_data::hostile_at(MONS_SHADOW, - you.pos(), 2, 0, true, god)) != -1) - { + if (_create_monster(MONS_SHADOW, 2, true)) success = true; - } } if (success) - mpr("Flickering shadows surround you."); - else - canned_msg(MSG_NOTHING_HAPPENS); + { + you_msg = "Flickering shadows surround you."; + mon_msg_seen = "Flickering shadows surround @the_monster@."; + mon_msg_unseen = "Shadows flicker in the thin air."; + } + do_msg(); break; } case 1: - if (!player_prot_life() && one_chance_in(3)) + if (target->atype() == ACT_PLAYER && !player_prot_life() + && one_chance_in(3)) { drain_exp(); break; } // otherwise it just flows through... case 2: - if (player_res_torment()) + // Monster messages needed. + if (target->res_torment()) { - mpr("You feel weird for a moment."); + you_msg = "You feel weird for a moment."; + do_msg(); break; } - mpr("You convulse helplessly as pain tears through your body!"); - ouch(15 + random2avg(23, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "You convulse helplessly as pain tears through " + "your body!"; + _ouch(15 + random2avg(23, 2)); break; } break; case 3: // even nastier - switch (random2(6)) + // Don't use two last cases for monsters. + switch (random2(target->atype() == ACT_PLAYER ? 6 : 4)) { case 0: - if (you.is_undead) + if (target->holiness() == MH_UNDEAD) { - mpr("Something just walked over your grave. No, really!"); + // Monster messages needed. + you_msg = "Something just walked over your grave. No, really!"; + do_msg(); break; } - torment_monsters(you.pos(), 0, TORMENT_GENERIC); + torment_monsters(target->pos(), 0, TORMENT_GENERIC); break; case 1: - mpr("You are engulfed in negative energy!"); - - if (!player_prot_life()) - { - drain_exp(); - break; - } // otherwise it just flows through... + target->rot(act_source, random2avg(7, 2) + 1, 0); + break; case 2: - lose_stat(STAT_RANDOM, 1 + random2avg(7, 2), false, cause); + if (_create_monster(MONS_SOUL_EATER, 4, true)) + { + // Monster messages needed. + you_msg = "Something reaches out for you..."; + } + do_msg(); break; case 3: - rot_player( random2avg(7, 2) + 1 ); + if (_create_monster(MONS_REAPER, 4, true)) + { + // Monster messages needed. + you_msg = "Death has come for you..."; + } + do_msg(); break; case 4: - if (create_monster( - mgen_data::hostile_at(MONS_SOUL_EATER, - you.pos(), 4, 0, true, god)) != -1) + mpr("You are engulfed in negative energy!"); + + if (!player_prot_life()) { - mpr("Something reaches out for you..."); - } - else - canned_msg(MSG_NOTHING_HAPPENS); - break; + drain_exp(); + break; + } // otherwise it just flows through... case 5: - if (create_monster( - mgen_data::hostile_at(MONS_REAPER, - you.pos(), 4, 0, true, god)) != -1) - { - mpr("Death has come for you..."); - } - else - canned_msg(MSG_NOTHING_HAPPENS); + lose_stat(STAT_RANDOM, 1 + random2avg(7, 2), false, cause); break; + } break; } } -static void _miscast_transmigration(int severity, const char* cause) +void MiscastEffect::_transmigration(int severity) { switch (severity) { @@ -3110,50 +3697,60 @@ static void _miscast_transmigration(int severity, const char* cause) switch (random2(10)) { case 0: - msg::stream << "Your " << your_hand(true) - << " glow momentarily." << std::endl; + _your_hands_glow(target, you_msg, mon_msg_seen); break; case 1: - mpr("The air around you crackles with energy!"); + you_msg = "The air around you crackles with energy!"; + mon_msg_seen = "The air around @the_monster@ crackles with" + " energy!"; + mon_msg_unseen = "The thin air crackles with energy!"; break; case 2: - mpr("Multicolored lights dance before your eyes!"); + you_msg = "Multicolored lights dance before your eyes!"; + // Monster messages needed. break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - mpr("Waves of light ripple over your body."); + you_msg = "Waves of light ripple over your body."; + mon_msg_seen = "Waves of light ripple over @the_monster@'s body."; + mon_msg_unseen = "Waves of light ripple in the air."; break; case 5: - mpr("Strange energies run through your body."); + you_msg = "Strange energies run through your body."; + // Monster messages needed. break; case 6: - mpr("Your skin tingles."); + you_msg = "Your skin tingles."; + // Monster messages needed. break; case 7: - mpr("Your skin glows momentarily."); + you_msg = "Your skin glows momentarily."; + mon_msg_seen = "@The_monster@'s body glows momentarily."; break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: if (player_can_smell()) - mpr("You smell something strange."); + all_msg = "You smell something strange."; else if (you.species == SP_MUMMY) - mpr("Your bandages flutter."); - else - canned_msg(MSG_NOTHING_HAPPENS); + you_msg = "Your bandages flutter."; break; } + do_msg(); break; case 1: // slightly annoying switch (random2(2)) { case 0: - mpr("Your body is twisted painfully."); - ouch(1 + random2avg(11, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "Your body is twisted painfully."; + mon_msg_seen = "@The_monster@'s body twists unnaturally."; + _ouch(1 + random2avg(11, 2)); break; case 1: random_uselessness(); @@ -3162,123 +3759,147 @@ static void _miscast_transmigration(int severity, const char* cause) break; case 2: // much more annoying - switch (random2(4)) + // Last case for players only. + switch (random2(target->atype() == ACT_PLAYER ? 4 : 3)) { case 0: - mpr("Your body is twisted very painfully!"); - ouch(3 + random2avg(23, 2), 0, KILLED_BY_WILD_MAGIC, cause); + you_msg = "Your body is twisted very painfully!"; + mon_msg_seen = "@The_monster@'s body twists and writhes."; + _ouch(3 + random2avg(23, 2)); break; case 1: - mpr("You feel saturated with unharnessed energies!"); - you.magic_contamination += random2avg(19,3); + _potion_effect(POT_PARALYSIS, 10); break; case 2: - potion_effect(POT_PARALYSIS, 10); + _potion_effect(POT_CONFUSION, 10); break; case 3: - potion_effect(POT_CONFUSION, 10); + mpr("You feel saturated with unharnessed energies!"); + you.magic_contamination += random2avg(19,3); break; } break; case 3: // even nastier + if (target->atype() == ACT_MONSTER) + target->mutate(); // Polymorph the monster, if possible. switch (random2(3)) { case 0: - mpr("Your body is flooded with distortional energies!"); - you.magic_contamination += random2avg(35, 3); + if (target->atype() == ACT_PLAYER) + { + mpr("Your body is flooded with distortional energies!"); + you.magic_contamination += random2avg(35, 3); + } - ouch(3 + random2avg(18, 2), 0, KILLED_BY_WILD_MAGIC, cause); + _ouch(3 + random2avg(18, 2)); break; case 1: - mpr("You feel very strange."); - delete_mutation(RANDOM_MUTATION); - ouch(5 + random2avg(23, 2), 0, KILLED_BY_WILD_MAGIC, cause); + if (target->atype() == ACT_PLAYER) + { + mpr("You feel very strange."); + delete_mutation(RANDOM_MUTATION); + } + _ouch(5 + random2avg(23, 2)); break; case 2: - mpr("Your body is distorted in a weirdly horrible way!"); + if (target->atype() == ACT_PLAYER) { + mpr("Your body is distorted in a weirdly horrible way!"); const bool failMsg = !give_bad_mutation(); if (coinflip()) give_bad_mutation(failMsg); - ouch(5 + random2avg(23, 2), 0, KILLED_BY_WILD_MAGIC, cause); } + _ouch(5 + random2avg(23, 2)); break; } break; } } -static void _miscast_fire(int severity, const char* cause) +void MiscastEffect::_fire(int severity) { - bolt beam; switch (severity) { case 0: // just a harmless message switch (random2(10)) { case 0: - msg::stream << "Sparks fly from your " << your_hand(true) - << '!' << std::endl; + you_msg = "Sparks fly from your @hands@!"; + mon_msg_seen = "Sparks fly from @the_monster@'s @hands@!"; break; case 1: - mpr("The air around you burns with energy!"); + you_msg = "The air around you burns with energy!"; + mon_msg_seen = "The air around @the_monster@ burns with energy!"; break; case 2: - msg::stream << "Wisps of smoke drift from your " - << your_hand(true) << '.' << std::endl; + you_msg = "Wisps of smoke drift from your @hands@."; + mon_msg_seen = "Wisps of smoke drift from @the_monster@'s @hands@."; break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: if (player_can_smell()) - mpr("You smell smoke."); + all_msg = "You smell smoke."; else if (you.species == SP_MUMMY) - mpr("Your bandages flutter."); - else - canned_msg(MSG_NOTHING_HAPPENS); + you_msg = "Your bandages flutter."; break; case 5: - mpr("Heat runs through your body."); + you_msg = "Heat runs through your body."; + // Monster messages needed. break; case 6: - mpr("You feel uncomfortably hot."); + you_msg = "You feel uncomfortably hot."; + // Monster messages needed. break; case 7: - mpr("Lukewarm flames ripple over your body."); + you_msg = "Lukewarm flames ripple over your body."; + mon_msg_seen = "Dim flames ripple over @the_monster@'s body."; break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - if (!silenced(you.pos())) - mpr("You hear a sizzling sound.", MSGCH_SOUND); - else - mpr("You feel like you have heartburn."); + if (neither_end_silenced()) + { + all_msg = "You hear a sizzling sound."; + msg_ch = MSGCH_SOUND; + } + else if (target->atype() == ACT_PLAYER) + you_msg = "You feel like you have heartburn."; break; } + do_msg(); break; case 1: // a bit less harmless stuff switch (random2(2)) { case 0: - msg::stream << "Smoke pours from your " - << your_hand(true) << "!" << std::endl; - big_cloud( random_smoke_type(), (cause ? KC_OTHER : KC_YOU), - you.x_pos, you.y_pos, 20, 7 + random2(7) ); + you_msg = "Smoke pours from your @hands@!"; + mon_msg_seen = "Smoke pours from @the_monster@'s @hands@!"; + mon_msg_unseen = "Smoke appears out of nowhere!"; + + do_msg(); + big_cloud( random_smoke_type(), kc, kt, + target->pos().x, target->pos().y, 20, 7 + random2(7) ); break; case 1: - mpr("Flames sear your flesh."); - expose_player_to_element(BEAM_FIRE, 3); + you_msg = "Flames sear your flesh."; + mon_msg_seen = "Flames sear @the_monster@."; - if (player_res_fire() < 0) - ouch(2 + random2avg(13, 2), 0, KILLED_BY_WILD_MAGIC, cause); + if (target->res_fire() < 0) + _ouch(2 + random2avg(13, 2)); + else + do_msg(); + target->expose_to_element(BEAM_FIRE, 3); break; } @@ -3288,31 +3909,25 @@ static void _miscast_fire(int severity, const char* cause) switch (random2(2)) { case 0: - mpr("You are blasted with fire."); - - ouch( check_your_resists( 5 + random2avg(29, 2), BEAM_FIRE ), 0, - KILLED_BY_WILD_MAGIC, cause ); + you_msg = "You are blasted with fire."; + mon_msg_seen = "@The_monster@ is blasted with fire."; + mon_msg_unseen = "A flame briefly burns in thin air."; - expose_player_to_element(BEAM_FIRE, 5); + _ouch(5 + random2avg(29, 2), BEAM_FIRE ); + target->expose_to_element(BEAM_FIRE, 5); break; case 1: - mpr("You are caught in a fiery explosion!"); - beam.type = dchar_glyph(DCHAR_FIRED_BURST); - beam.damage = dice_def( 3, 14 ); + you_msg = "You are caught in a fiery explosion!"; + mon_msg_seen = "@The_monster@ is caught in a fiery explosion!"; + mon_msg_unseen = "Fire explodes from out of thin air!"; + + beam.type = dchar_glyph(DCHAR_FIRED_BURST); + beam.damage = dice_def( 3, 14 ); beam.flavour = BEAM_FIRE; - beam.target_x = you.x_pos; - beam.target_y = you.y_pos; - beam.name = "explosion"; - beam.colour = RED; - beam.beam_source = NON_MONSTER; - beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source.clear(); - if (cause) - beam.aux_source = cause; - beam.ex_size = 1; - beam.is_explosion = true; - explosion(beam); + beam.name = "explosion"; + beam.colour = RED; + _explosion(); break; } break; @@ -3321,85 +3936,100 @@ static void _miscast_fire(int severity, const char* cause) switch (random2(3)) { case 0: - mpr("You are blasted with searing flames!"); + you_msg = "You are blasted with searing flames!"; + mon_msg_seen = "@The_monster@ is blasted with searing flames!"; + mon_msg_unseen = "A large flame burns hotly for a moment in the " + "thin air."; - ouch( check_your_resists( 9 + random2avg(33, 2), BEAM_FIRE ), 0, - KILLED_BY_WILD_MAGIC, cause ); + _ouch(9 + random2avg(33, 2), BEAM_FIRE); - expose_player_to_element(BEAM_FIRE, 10); + target->expose_to_element(BEAM_FIRE, 10); break; case 1: - mpr("There is a sudden and violent explosion of flames!"); - beam.type = dchar_glyph(DCHAR_FIRED_BURST); - beam.damage = dice_def( 3, 20 ); + all_msg = "There is a sudden and violent explosion of flames!"; + + beam.type = dchar_glyph(DCHAR_FIRED_BURST); + beam.damage = dice_def( 3, 20 ); beam.flavour = BEAM_FIRE; - beam.target_x = you.x_pos; - beam.target_y = you.y_pos; - beam.name = "fireball"; - beam.colour = RED; - beam.beam_source = NON_MONSTER; - beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source.clear(); - if (cause) - beam.aux_source = cause; + beam.name = "fireball"; + beam.colour = RED; beam.ex_size = coinflip()?1:2; - beam.is_explosion = true; - explosion(beam); + _explosion(); break; case 2: - mpr("You are covered in liquid flames!"); - you.duration[DUR_LIQUID_FLAMES] += random2avg(7, 3) + 1; + { + you_msg = "You are covered in liquid flames!"; + mon_msg_seen = "@The_monster@ is covered in liquid flames!"; + do_msg(); + + int dur = random2avg(7, 3) + 1; + if (target->atype() == ACT_PLAYER) + you.duration[DUR_LIQUID_FLAMES] += dur; + else + mon_target->add_ench( mon_enchant(ENCH_STICKY_FLAME, dur, kc) ); break; + } } break; } } -static void _miscast_ice(int severity, const char* cause) +void MiscastEffect::_ice(int severity) { - bolt beam; switch (severity) { case 0: // just a harmless message switch (random2(10)) { case 0: - mpr("You shiver with cold."); + you_msg = "You shiver with cold."; + // Monster messages needed. break; case 1: - mpr("A chill runs through your body."); + you_msg = "A chill runs through your body."; + // Monster messages needed. break; case 2: - msg::stream << "Wisps of condensation drift from your " - << your_hand(true) << "." << std::endl; + you_msg = "Wisps of condensation drift from your @hands@."; + mon_msg_seen = "Wisps of condensation drift from @the_monster@'s " + "@hands@."; break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - msg::stream << "Your " << your_hand(true) - << " feel numb with cold." << std::endl; + you_msg = "Your @hands@ feel numb with cold."; + // Monster messages needed. break; case 5: - mpr("A chill runs through your body."); + you_msg = "A chill runs through your body."; + // Monster messages needed. break; case 6: - mpr("You feel uncomfortably cold."); + you_msg = "You feel uncomfortably cold."; + // Monster messages needed. break; case 7: - mpr("Frost covers your body."); + you_msg = "Frost covers your body."; + mon_msg_seen = "Frost covers @the_monster@'s body."; break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - if (!silenced(you.pos())) - mpr("You hear a crackling sound.", MSGCH_SOUND); - else - mpr("A snowflake lands on your nose."); + if (neither_end_silenced()) + { + all_msg = "You hear a crackling sound."; + msg_ch = MSGCH_SOUND; + } + else if (target->atype() == ACT_PLAYER) + you_msg = "A snowflake lands on your nose."; break; } + do_msg(); break; case 1: // a bit less harmless stuff @@ -3407,13 +4037,17 @@ static void _miscast_ice(int severity, const char* cause) { case 0: mpr("You feel extremely cold."); + // Monster messages needed. break; case 1: - mpr("You are covered in a thin layer of ice."); - expose_player_to_element(BEAM_COLD, 2); + you_msg = "You are covered in a thin layer of ice."; + mon_msg_seen = "@The_monster@ is covered in a thin layer of ice."; - if (player_res_cold() < 0) - ouch(4 + random2avg(5, 2), 0, KILLED_BY_WILD_MAGIC, cause); + if (target->res_cold() < 0) + _ouch(4 + random2avg(5, 2)); + else + do_msg(); + target->expose_to_element(BEAM_COLD, 2); break; } break; @@ -3422,32 +4056,27 @@ static void _miscast_ice(int severity, const char* cause) switch (random2(2)) { case 0: - mpr("Heat is drained from your body."); + you_msg = "Heat is drained from your body."; + // Monster messages needed. - ouch(check_your_resists(5 + random2(6) + random2(7), BEAM_COLD), 0, - KILLED_BY_WILD_MAGIC, cause); + _ouch(5 + random2(6) + random2(7), BEAM_COLD); - expose_player_to_element(BEAM_COLD, 4); + target->expose_to_element(BEAM_COLD, 4); break; case 1: - mpr("You are caught in an explosion of ice and frost!"); - beam.type = dchar_glyph(DCHAR_FIRED_BURST); - beam.damage = dice_def( 3, 11 ); + you_msg = "You are caught in an explosion of ice and frost!"; + mon_msg_seen = "@The_monster@ is caught in an explosion of " + "ice and frost!"; + mon_msg_unseen = "Ice and frost explode from out of thin air!"; + + beam.type = dchar_glyph(DCHAR_FIRED_BURST); + beam.damage = dice_def( 3, 11 ); beam.flavour = BEAM_COLD; - beam.target_x = you.x_pos; - beam.target_y = you.y_pos; - beam.name = "explosion"; - beam.colour = WHITE; - beam.beam_source = NON_MONSTER; - beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source.clear(); - if (cause) - beam.aux_source = cause; - beam.ex_size = 1; - beam.is_explosion = true; + beam.name = "explosion"; + beam.colour = WHITE; - explosion(beam); + _explosion(); break; } break; @@ -3456,27 +4085,29 @@ static void _miscast_ice(int severity, const char* cause) switch (random2(2)) { case 0: - mpr("You are blasted with ice!"); + you_msg = "You are blasted with ice!"; + mon_msg_seen = "@The_monster@ is blasted with ice!"; - ouch(check_your_resists(9 + random2avg(23, 2), BEAM_ICE), 0, - KILLED_BY_WILD_MAGIC, cause); + _ouch(9 + random2avg(23, 2), BEAM_ICE); - expose_player_to_element(BEAM_COLD, 9); + target->expose_to_element(BEAM_COLD, 9); break; case 1: - msg::stream << "Freezing gasses pour from your " - << your_hand(true) << "!" << std::endl; - big_cloud(CLOUD_COLD, (cause ? KC_OTHER : KC_YOU), - you.x_pos, you.y_pos, 20, 8 + random2(4)); + you_msg = "Freezing gasses pour from your @hands@!"; + mon_msg_seen = "Freezing gasses pour from @the_monsters@'s " + " @hands@!"; + + do_msg(); + big_cloud(CLOUD_COLD, kc, kt, + target->pos().x, target->pos().y, 20, 8 + random2(4)); break; } break; } } -static void _miscast_earth(int severity, const char* cause) +void MiscastEffect::_earth(int severity) { - bolt beam; switch (severity) { case 0: // just a harmless message @@ -3484,50 +4115,62 @@ static void _miscast_earth(int severity, const char* cause) switch (random2(10)) { case 0: - mpr("You feel earthy."); + you_msg = "You feel earthy."; + // Monster messages needed. break; case 1: - mpr("You are showered with tiny particles of grit."); + you_msg = "You are showered with tiny particles of grit."; + mon_msg_seen = "@The_monster@ is showered with tiny particles " + "of grit."; break; case 2: - msg::stream << "Sand pours from your " - << your_hand(true) << "." << std::endl; + you_msg = "Sand pours from your @hands@."; + mon_msg_seen = "Sand pours from @the_monster@'s @hands@."; break; case 3: - mpr("You feel a surge of energy from the ground."); + you_msg = "You feel a surge of energy from the ground."; + // Monster messages needed. break; case 4: - if (!silenced(you.pos())) - mpr("You hear a distant rumble.", MSGCH_SOUND); - else - mpr("You sympathise with the stones."); + if (neither_end_silenced()) + { + all_msg = "You hear a distant rumble."; + msg_ch = MSGCH_SOUND; + } + else if (target->atype() == ACT_PLAYER) + you_msg = "You sympathise with the stones."; break; case 5: - mpr("You feel gritty."); + you_msg = "You feel gritty."; + // Monster messages needed. break; case 6: - mpr("You feel momentarily lethargic."); + you_msg = "You feel momentarily lethargic."; + // Monster messages needed. break; case 7: - mpr("Motes of dust swirl before your eyes."); + you_msg = "Motes of dust swirl before your eyes."; + // Monster messages needed. break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - mprf("Your %s warm.", - (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR) - ? "lowest portion feels" : - (!transform_changed_physiology() ? - (player_mutation_level(MUT_HOOVES)) ? "hooves feel" : - (player_mutation_level(MUT_TALONS)) ? "talons feel" : - (you.species == SP_NAGA) ? "underbelly feels" : - (you.species == SP_MERFOLK - && player_is_swimming()) ? "tail feels" - : "feet feel" - : "feet feel")); + { + bool pluralized = true; + std::string feet = you.foot_name(true, &pluralized); + std::ostringstream str; + + str << "Your " << feet << (pluralized ? " feel" : " feels") + << " warm"; + + you_msg = str.str(); + // Monster messages needed. break; } + } + do_msg(); break; case 2: // slightly less harmless stuff @@ -3537,17 +4180,23 @@ static void _miscast_earth(int severity, const char* cause) switch (random2(3)) { case 0: - mpr("You are hit by flying rocks!"); + you_msg = "You are hit by flying rocks!"; + mon_msg_seen = "@The_monster@ is hit by flying rocks!"; + mon_msg_unseen = "Flying rocks appear out of thin air!"; break; case 1: - mpr("You are blasted with sand!"); + you_msg = "You are blasted with sand!"; + mon_msg_seen = "@The_monster@ is blasted with sand!"; + mon_msg_unseen = "A minature sandstorm briefly appears!"; break; case 2: - mpr("Rocks fall onto you out of nowhere!"); + you_msg = "Rocks fall onto you out of nowhere!"; + mon_msg_seen = "Rocks fall onto @the_monster@ out of " + "nowhere!"; + mon_msg_unseen = "Rocks fall out of nowhere!"; break; } - ouch( random2avg(13,2) + 10 - random2(1 + player_AC()), - 0, KILLED_BY_WILD_MAGIC, cause); + _ouch(random2avg(13,2) + 10 - random2(1 + target->armour_class())); break; } break; @@ -3556,12 +4205,14 @@ static void _miscast_earth(int severity, const char* cause) switch (random2(1)) { case 0: - mpr("You are caught in an explosion of flying shrapnel!"); + you_msg = "You are caught in an explosion of flying " + "shrapnel!"; + mon_msg_seen = "@The_monster@ is caught in an explosion of " + "flying shrapnel!"; + mon_msg_unseen = "Flying shrapnel explodes from the thin air!"; beam.type = dchar_glyph(DCHAR_FIRED_BURST); beam.damage = dice_def( 3, 15 ); beam.flavour = BEAM_FRAG; - beam.target_x = you.x_pos; - beam.target_y = you.y_pos; beam.name = "explosion"; beam.colour = CYAN; @@ -3570,141 +4221,170 @@ static void _miscast_earth(int severity, const char* cause) if (one_chance_in(5)) beam.colour = LIGHTCYAN; - beam.beam_source = NON_MONSTER; - beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source.clear(); - if (cause) - beam.aux_source = cause; - beam.ex_size = 1; - beam.is_explosion = true; - - explosion(beam); + _explosion(); break; } break; } } -static void _miscast_air(int severity, const char* cause) +void MiscastEffect::_air(int severity) { - bolt beam; switch (severity) { case 0: // just a harmless message switch (random2(10)) { case 0: - mpr("Ouch! You gave yourself an electric shock."); + you_msg = "Ouch! You gave yourself an electric shock."; + // Monster messages needed. break; case 1: - mpr("You feel momentarily weightless."); + you_msg = "You feel momentarily weightless."; + // Monster messages needed. break; case 2: - msg::stream << "Wisps of vapour drift from your " - << your_hand(true) << "." << std::endl; + you_msg = "Wisps of vapour drift from your @hands@."; + mon_msg_seen = "Wisps of vapour drift from @the_monster@'s " + "@hands@."; break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - mpr("You feel electric!"); + you_msg = "You feel electric!"; + // Monster messages needed. break; case 5: - msg::stream << "Sparks of electricity dance between your " - << your_hand(true) << "." << std::endl; + { + bool pluralized = true; + target->hand_name(true, &pluralized); + + if (pluralized) + { + you_msg = "Sparks of electricity dance between your " + "@hands@."; + mon_msg_seen = "Sparks of electricity dance between " + "@the_monster@'s @hands@."; + } + else + { + you_msg = "Sparks of electricity dance over your " + "@hand@."; + mon_msg_seen = "Sparks of electricity dance over " + "@the_monster@'s @hand@."; + } break; + } case 6: - mpr("You are blasted with air!"); + you_msg = "You are blasted with air!"; + // Monster messages needed. break; case 7: - if (!silenced(you.pos())) - mpr("You hear a whooshing sound.", MSGCH_SOUND); + if (neither_end_silenced()) + { + all_msg = "You hear a whooshing sound."; + msg_ch = MSGCH_SOUND; + } else if (player_can_smell()) - mpr("You smell ozone."); + all_msg = "You smell ozone."; else if (you.species == SP_MUMMY) - mpr("Your bandages flutter."); - else - canned_msg(MSG_NOTHING_HAPPENS); + you_msg = "Your bandages flutter."; break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - if (!silenced(you.pos())) - mpr("You hear a crackling sound.", MSGCH_SOUND); + if (neither_end_silenced()) + { + all_msg = "You hear a crackling sound."; + msg_ch = MSGCH_SOUND; + } else if (player_can_smell()) - mpr("You smell something musty."); + all_msg = "You smell something musty."; else if (you.species == SP_MUMMY) - mpr("Your bandages flutter."); - else - canned_msg(MSG_NOTHING_HAPPENS); + you_msg = "Your bandages flutter."; break; } + do_msg(); break; case 1: // a bit less harmless stuff switch (random2(2)) { case 0: - mpr("There is a short, sharp shower of sparks."); + you_msg = "There is a short, sharp shower of sparks."; + mon_msg_seen = "@The_monster@ is briefly showered in sparks."; break; case 1: - mprf("The wind %s around you!", - silenced(you.pos()) ? "whips" : "howls"); + if (silenced(you.pos())) + all_msg = "The wind whips around you!"; + else + all_msg = "The wind howls around you!"; break; } + do_msg(); break; case 2: // rather less harmless stuff switch (random2(2)) { case 0: - mpr("Electricity courses through your body."); - ouch(check_your_resists(4 + random2avg(9, 2), BEAM_ELECTRICITY), 0, - KILLED_BY_WILD_MAGIC, cause); + you_msg = "Electricity courses through your body."; + // Monster messages needed. + _ouch(4 + random2avg(9, 2), BEAM_ELECTRICITY); break; case 1: - msg::stream << "Noxious gasses pour from your " - << your_hand(true) << "!" << std::endl; - big_cloud(CLOUD_STINK, (cause ? KC_OTHER : KC_YOU), - you.x_pos, you.y_pos, 20, 9 + random2(4)); + you_msg = "Noxious gasses pour from your @hands@!"; + mon_msg_seen = "Noxious gasses pour from @the_monster@'s " + "@hands@!"; + mon_msg_unseen = "Noxious gasses appear from out of thin air!"; + + do_msg(); + big_cloud(CLOUD_STINK, kc, kt, + target->pos().x, target->pos().y, 20, 9 + random2(4)); break; } break; - case 3: // less harmless stuff + case 3: // musch less harmless stuff switch (random2(2)) { case 0: - mpr("You are caught in an explosion of electrical discharges!"); - beam.type = dchar_glyph(DCHAR_FIRED_BURST); - beam.damage = dice_def( 3, 8 ); + you_msg = "You are caught in an explosion of electrical " + "discharges!"; + mon_msg_seen = "@The_monster@ is caught in an explosion of " + "electrical discharges!"; + mon_msg_unseen = "Elecectrical discharges explodes from out of " + "thin air!"; + + beam.type = dchar_glyph(DCHAR_FIRED_BURST); + beam.damage = dice_def( 3, 8 ); beam.flavour = BEAM_ELECTRICITY; - beam.target_x = you.x_pos; - beam.target_y = you.y_pos; - beam.name = "explosion"; - beam.colour = LIGHTBLUE; - beam.beam_source = NON_MONSTER; - beam.thrower = (cause) ? KILL_MISC : KILL_YOU; - beam.aux_source.clear(); - if (cause) - beam.aux_source = cause; + beam.name = "explosion"; + beam.colour = LIGHTBLUE; beam.ex_size = one_chance_in(4)?1:2; - beam.is_explosion = true; - explosion(beam); + _explosion(); break; case 1: - msg::stream << "Venomous gasses pour from your " - << your_hand(true) << "!" << std::endl; - big_cloud( CLOUD_POISON, (cause ? KC_OTHER : KC_YOU), - you.x_pos, you.y_pos, 20, 8 + random2(5) ); + you_msg = "Venomous gasses pour from your @hands@!"; + mon_msg_seen = "Venomous gasses pour from @the_monster@'s " + "@hands@!"; + mon_msg_unseen = "Venomous gasses pour forth from the thin air!"; + + do_msg(); + big_cloud( CLOUD_POISON, kc, kt, + target->pos().x, target->pos().y, 20, 8 + random2(5) ); break; } } } -static void _miscast_poison(int severity, const char* cause) +// XXX MATT +void MiscastEffect::_poison(int severity) { switch (severity) { @@ -3712,83 +4392,101 @@ static void _miscast_poison(int severity, const char* cause) switch (random2(10)) { case 0: - mpr("You feel mildly nauseous."); + you_msg = "You feel mildly nauseous."; + // Monster messages needed. break; case 1: - mpr("You feel slightly ill."); + you_msg = "You feel slightly ill."; + // Monster messages needed. break; case 2: - msg::stream << "Wisps of poison gas drift from your " - << your_hand(true) << "." << std::endl; + you_msg = "Wisps of poison gas drift from your @hands@."; + mon_msg_seen = "Wisps of poison gas drift from @the_monster@'s " + "@hands@."; break; case 3: - mpr("You feel a strange surge of energy!"); + you_msg = "You feel a strange surge of energy!"; + // Monster messages needed. break; case 4: - mpr("You feel faint for a moment."); + you_msg = "You feel faint for a moment."; + // Monster messages needed. break; case 5: - mpr("You feel sick."); + you_msg = "You feel sick."; + // Monster messages needed. break; case 6: - mpr("You feel odd."); + you_msg = "You feel odd."; + // Monster messages needed. break; case 7: - mpr("You feel weak for a moment."); + you_msg = "You feel weak for a moment."; + // Monster messages needed. break; case 8: - canned_msg(MSG_NOTHING_HAPPENS); + // Set nothing; canned_msg(MSG_NOTHING_HAPPENS) will be taken + // care of elsewhere. break; case 9: - if (!silenced(you.pos())) - mpr("You hear a slurping sound.", MSGCH_SOUND); + if (neither_end_silenced()) + { + all_msg = "You hear a slurping sound."; + msg_ch = MSGCH_SOUND; + } else if (you.species != SP_MUMMY) - mpr("You taste almonds."); - else - canned_msg(MSG_NOTHING_HAPPENS); + you_msg = "You taste almonds."; break; } + do_msg(); break; case 1: // a bit less harmless stuff switch (random2(2)) { case 0: - if (player_res_poison()) - canned_msg(MSG_NOTHING_HAPPENS); - else + if (target->res_poison() <= 0) { - mpr("You feel sick."); - poison_player( 2 + random2(3) ); + you_msg = "You feel sick."; + // Monster messages needed. + target->poison( act_source, 2 + random2(3) ); } + do_msg(); break; case 1: - msg::stream << "Noxious gasses pour from your " - << your_hand(true) << "!" << std::endl; - place_cloud(CLOUD_STINK, you.pos(), - 2 + random2(4), (cause ? KC_OTHER : KC_YOU) ); + you_msg = "Noxious gasses pour from your @hands@!"; + mon_msg_seen = "Noxious gasses pour from @the_monster@'s " + "@hands@!"; + mon_msg_unseen = "Noxious gasses pour forth from the thin air!"; + place_cloud(CLOUD_STINK, target->pos(), 2 + random2(4), kc, kt ); break; } break; case 2: // rather less harmless stuff - switch (random2(3)) + // Don't use last case for monsters. + switch (random2(target->atype() == ACT_PLAYER ? 3 : 2)) { case 0: - if (player_res_poison()) - canned_msg(MSG_NOTHING_HAPPENS); - else + if (target->res_poison() <= 0) { - mpr("You feel very sick."); - poison_player( 3 + random2avg(9, 2) ); + you_msg = "You feel very sick."; + // Monster messages needed. + target->poison( act_source, 3 + random2avg(9, 2) ); } + do_msg(); break; case 1: - mpr("Noxious gasses pour from your hands!"); - big_cloud(CLOUD_STINK, (cause ? KC_OTHER : KC_YOU), - you.x_pos, you.y_pos, 20, 8 + random2(5)); + you_msg = "Noxious gasses pour from your @hands@!"; + mon_msg_seen = "Noxious gasses pour from @the_monster@'s " + "@hands@!"; + mon_msg_unseen = "Noxious gasses pour forth from the thin air!"; + + do_msg(); + big_cloud(CLOUD_STINK, kc, kt, + target->pos().x, target->pos().y, 20, 8 + random2(5)); break; case 2: @@ -3801,22 +4499,26 @@ static void _miscast_poison(int severity, const char* cause) break; case 3: // less harmless stuff - switch (random2(3)) + // Don't use last case for monsters. + switch (random2(target->atype() == ACT_PLAYER ? 3 : 2)) { case 0: - if (player_res_poison()) - canned_msg(MSG_NOTHING_HAPPENS); - else + if (target->res_poison() <= 0) { - mpr("You feel incredibly sick."); - poison_player( 10 + random2avg(19, 2) ); + you_msg = "You feel incredibly sick."; + // Monster messages needed. + target->poison( act_source, 10 + random2avg(19, 2) ); } + do_msg(); break; case 1: - msg::stream << "Venomous gasses pour from your " - << your_hand(true) << "!" << std::endl; - big_cloud(CLOUD_POISON, (cause ? KC_OTHER : KC_YOU), - you.x_pos, you.y_pos, 20, 7 + random2(7)); + you_msg = "Venomous gasses pour from your @hands@!"; + mon_msg_seen = "Venomous gasses pour from @the_monster@'s @hands@!"; + mon_msg_unseen = "Venomous gasses pour forth from the thin air!"; + + do_msg(); + big_cloud(CLOUD_POISON, kc, kt, + target->pos().x, target->pos().y, 20, 7 + random2(7)); break; case 2: if (player_res_poison()) @@ -3829,71 +4531,6 @@ static void _miscast_poison(int severity, const char* cause) } } -// sp_type: The type of the spell. -// mag_pow: The overall power of the spell or effect (i.e. its level). -// mag_fail: The degree to which you failed. -// force_effect: This forces a certain severity of effect to occur. It -// can be disabled by being set to 100. -// -// If a god is making you miscast, any monsters produced will count as -// god gifts. -void miscast_effect(unsigned int sp_type, int mag_pow, int mag_fail, - int force_effect, const char *cause) -{ - if (sp_type == SPTYP_RANDOM) - sp_type = 1 << (random2(SPTYP_LAST_EXPONENT)); - - int sever = (mag_pow*mag_fail*(10+mag_pow)/7 * WILD_MAGIC_NASTINESS)/100; - - if (force_effect == 100 && random2(40) > sever && random2(40) > sever) - { - canned_msg(MSG_NOTHING_HAPPENS); - return; - } - - if (cause == NULL || strlen(cause) == 0) - cause = "spell miscasting"; - - sever /= 100; - -#if DEBUG_DIAGNOSTICS - const int old_fail = sever; -#endif - - sever = random2(sever); - - if (sever > 3) - sever = 3; - else if (sever < 0) - sever = 0; - -#if DEBUG_DIAGNOSTICS - mprf(MSGCH_DIAGNOSTICS, "Sptype: %u, failure1: %d, failure2: %d", - sp_type, old_fail, sever ); -#endif - - if (force_effect != 100) - sever = force_effect; - - switch (sp_type) - { - case SPTYP_CONJURATION: _miscast_conjuration(sever, cause); break; - case SPTYP_ENCHANTMENT: _miscast_enchantment(sever, cause); break; - case SPTYP_TRANSLOCATION: _miscast_translocation(sever, cause); break; - case SPTYP_SUMMONING: _miscast_summoning(sever, cause); break; - case SPTYP_DIVINATION: _miscast_divination(sever, cause); break; - case SPTYP_NECROMANCY: _miscast_necromancy(sever, cause); break; - case SPTYP_TRANSMIGRATION: _miscast_transmigration(sever, cause); break; - case SPTYP_FIRE: _miscast_fire(sever, cause); break; - case SPTYP_ICE: _miscast_ice(sever, cause); break; - case SPTYP_EARTH: _miscast_earth(sever, cause); break; - case SPTYP_AIR: _miscast_air(sever, cause); break; - case SPTYP_POISON: _miscast_poison(sever, cause); break; - } - - xom_is_stimulated(sever); -} - const char* failure_rate_to_string( int fail ) { return diff --git a/crawl-ref/source/spl-cast.h b/crawl-ref/source/spl-cast.h index a2fbf69c53..71c40b38fe 100644 --- a/crawl-ref/source/spl-cast.h +++ b/crawl-ref/source/spl-cast.h @@ -71,14 +71,6 @@ void inspect_spells(); * *********************************************************************** */ spret_type your_spells(spell_type spell, int powc = 0, bool allow_fail = true); -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: acr - decks - fight - it_use2 - it_use3 - item_use - items - - * misc - mstuff2 - religion - spell - spl-book - spells4 - * *********************************************************************** */ -void miscast_effect(unsigned int sp_type, int mag_pow, int mag_fail, - int force_effect, const char *cause = NULL); - const char* failure_rate_to_string( int fail ); int spell_power_colour(spell_type spell); diff --git a/crawl-ref/source/spl-mis.h b/crawl-ref/source/spl-mis.h new file mode 100644 index 0000000000..ec6c06ff7e --- /dev/null +++ b/crawl-ref/source/spl-mis.h @@ -0,0 +1,126 @@ +/* + * File: spl-mis.h + * Summary: Spell miscast class. + * Written by: Matthew Cline + * + * Modified for Crawl Reference by $Author$ on $Date: 2008-06-28 22: +16:39 -0700 (Sat, 28 Jun 2008) $ + * + * Change History (most recent first): + * + * <1> -/--/-- LRH Created + */ + +#ifndef SPL_MIS_H +#define SPL_MIS_H + +// last updated 23jul2008 {mpc} +/* *********************************************************************** + * called from: decks - effects - fight - item_use - it_use2 - it_use3 - + * item_use - monstuff - religion - spells2 - spells4 - + * spl-book - spl-cast - traps - xom + * *********************************************************************** */ + +#include "enum.h" + +#include "beam.h" +#include "mpr.h" +#include "spl-util.h" + +#define ZOT_TRAP_MISCAST (NON_MONSTER + 1) +#define WIELD_MISCAST (NON_MONSTER + 2) +#define MELEE_MISCAST (NON_MONSTER + 3) +#define MISC_KNOWN_MISCAST (NON_MONSTER + 4) +#define MISC_UNKNOWN_MISCAST (NON_MONSTER + 5) + +enum nothing_happens_when_type +{ + NH_DEFAULT, + NH_NEVER, + NH_ALWAYS +}; + +class actor; + +class MiscastEffect +{ +public: + MiscastEffect(actor* _target, int _source, spell_type _spell, int _pow, + int _fail, std::string _cause = "", + nothing_happens_when_type _nothing_happens = NH_DEFAULT); + MiscastEffect(actor *_target, int _source, spschool_flag_type _school, + int _level, std::string _cause, + nothing_happens_when_type _nothing_happens = NH_DEFAULT); + MiscastEffect(actor *_target, int _source, spschool_flag_type _school, + int _pow, int _fail, std::string _cause, + nothing_happens_when_type _nothing_happens = NH_DEFAULT); + + void do_miscast(); + +private: + actor* target; + int source; + + std::string cause; + + spell_type spell; + spschool_flag_type school; + + int pow; + int fail; + int level; + +private: + kill_category kc; + killer_type kt; + + monsters* mon_target; + monsters* mon_source; + + nothing_happens_when_type nothing_happens_when; + + int kill_source; + actor* act_source; + + bool source_known; + bool target_known; + + bolt beam; + + std::string all_msg; + std::string you_msg; + std::string mon_msg; + std::string mon_msg_seen; + std::string mon_msg_unseen; + + msg_channel_type msg_ch; + +private: + void init(); + std::string get_default_cause(); + + bool neither_end_silenced(); + + void do_msg(bool suppress_nothing_happens = false); + void _ouch(int dam, beam_type flavour = BEAM_NONE); + void _explosion(); + void _potion_effect(int pot_eff, int pow); + bool _create_monster(monster_type what, int abj_deg, bool alert = false); + void send_abyss(); + + void _conjuration(int severity); + void _enchantment(int severity); + void _translocation(int severity); + void _summoning(int severity); + void _divination_you(int severity); + void _divination_mon(int severity); + void _necromancy(int severity); + void _transmigration(int severity); + void _fire(int severity); + void _ice(int severity); + void _earth(int severity); + void _air(int severity); + void _poison(int severity); +}; + +#endif diff --git a/crawl-ref/source/spl-util.cc b/crawl-ref/source/spl-util.cc index 73d0b16f84..9b6f978ebd 100644 --- a/crawl-ref/source/spl-util.cc +++ b/crawl-ref/source/spl-util.cc @@ -54,9 +54,9 @@ static int spell_list[NUM_SPELLS]; static struct spell_desc *_seekspell(spell_type spellid); static bool _cloud_helper(int (*func)(int, int, int, int, cloud_type, - kill_category), + kill_category, killer_type), int x, int y, int pow, int spread_rate, - cloud_type ctype, kill_category ); + cloud_type ctype, kill_category, killer_type ); // // BEGIN PUBLIC FUNCTIONS @@ -134,6 +134,52 @@ spell_type spell_by_name(std::string name, bool partial_match) : SPELL_NO_SPELL); } +spschool_flag_type school_by_name(std::string name) +{ + spschool_flag_type short_match, long_match; + int short_matches, long_matches; + + short_match = long_match = SPTYP_NONE; + short_matches = long_matches = 0; + + for (int i = 0; i <= SPTYP_RANDOM; i++) + { + spschool_flag_type type = (spschool_flag_type) (1 << i); + + const char* short_name = spelltype_short_name(type); + const char* long_name = spelltype_long_name(type); + + if (strcasecmp(short_name, name.c_str()) == 0) + return type; + if (strcasecmp(long_name, name.c_str()) == 0) + return type; + + if (strcasestr(short_name, name.c_str())) + { + short_match = type; + short_matches++; + } + if (strcasestr(long_name, name.c_str())) + { + long_match = type; + long_matches++; + } + } + + if (short_matches != 1 && long_matches != 1) + return SPTYP_NONE; + + if (short_matches == 1 && long_matches != 1) + return short_match; + if (short_matches != 1 && long_matches == 1) + return long_match; + + if (short_match == long_match) + return short_match; + + return SPTYP_NONE; +} + int get_spell_slot_by_letter( char letter ) { ASSERT( isalpha( letter ) ); @@ -558,10 +604,11 @@ int apply_area_within_radius( cell_func cf, const coord_def& where, // to do a (shallow) breadth-first-search of the dungeon floor. // This ought to work okay for small clouds. void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, - kill_category), + kill_category, killer_type), int x, int y, int pow, int number, cloud_type ctype, - kill_category whose, int spread_rate ) + kill_category whose, killer_type killer, + int spread_rate ) { int spread, clouds_left = number; int good_squares = 0, neighbours[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -569,7 +616,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, bool x_first; if (clouds_left && _cloud_helper(func, x, y, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) clouds_left--; if (!clouds_left) @@ -585,7 +632,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, if (x_first) { if (clouds_left && _cloud_helper(func, x + dx, y, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -593,7 +640,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x - dx, y, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -601,7 +648,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x, y + dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -609,7 +656,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x, y - dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -619,7 +666,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, else { if (clouds_left && _cloud_helper(func, x, y + dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -627,7 +674,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x, y - dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -635,7 +682,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x + dx, y, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -643,7 +690,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x - dx, y, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -653,7 +700,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, // Mow diagonals; we could randomize dx & dy again here. if (clouds_left && _cloud_helper(func, x + dx, y + dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -661,7 +708,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x - dx, y + dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -669,7 +716,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x + dx, y - dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -677,7 +724,7 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, } if (clouds_left && _cloud_helper(func, x - dx, y - dy, pow, spread_rate, - ctype, whose)) + ctype, whose, killer)) { clouds_left--; good_squares++; @@ -699,36 +746,36 @@ void apply_area_cloud( int (*func) (int, int, int, int, cloud_type, switch (i) { case 0: - apply_area_cloud(func, x + dx, y, pow, spread, ctype, whose, + apply_area_cloud(func, x + dx, y, pow, spread, ctype, whose, killer, spread_rate); break; case 1: - apply_area_cloud(func, x - dx, y, pow, spread, ctype, whose, + apply_area_cloud(func, x - dx, y, pow, spread, ctype, whose, killer, spread_rate); break; case 2: - apply_area_cloud(func, x, y + dy, pow, spread, ctype, whose, + apply_area_cloud(func, x, y + dy, pow, spread, ctype, whose, killer, spread_rate); break; case 3: - apply_area_cloud(func, x, y - dy, pow, spread, ctype, whose, + apply_area_cloud(func, x, y - dy, pow, spread, ctype, whose, killer, spread_rate); break; case 4: apply_area_cloud(func, x + dx, y + dy, pow, spread, ctype, whose, - spread_rate); + killer, spread_rate); break; case 5: apply_area_cloud(func, x - dx, y + dy, pow, spread, ctype, whose, - spread_rate); + killer, spread_rate); break; case 6: apply_area_cloud(func, x + dx, y - dy, pow, spread, ctype, whose, - spread_rate); + killer, spread_rate); break; case 7: apply_area_cloud(func, x - dx, y - dy, pow, spread, ctype, whose, - spread_rate); + killer, spread_rate); break; } } @@ -793,6 +840,45 @@ const char* spelltype_short_name( int which_spelltype ) return ("Erth"); case SPTYP_AIR: return ("Air"); + case SPTYP_RANDOM: + return ("Rndm"); + default: + return "Bug"; + } +} + +const char* spelltype_long_name( int which_spelltype ) +{ + switch (which_spelltype) + { + case SPTYP_CONJURATION: + return ("Conjuration"); + case SPTYP_ENCHANTMENT: + return ("Enchantment"); + case SPTYP_FIRE: + return ("Fire"); + case SPTYP_ICE: + return ("Ice"); + case SPTYP_TRANSMIGRATION: + return ("Transmigration"); + case SPTYP_NECROMANCY: + return ("Necromancy"); + case SPTYP_HOLY: + return ("Holy"); + case SPTYP_SUMMONING: + return ("Summoning"); + case SPTYP_DIVINATION: + return ("Divination"); + case SPTYP_TRANSLOCATION: + return ("Translocation"); + case SPTYP_POISON: + return ("Poison"); + case SPTYP_EARTH: + return ("Earth"); + case SPTYP_AIR: + return ("Air"); + case SPTYP_RANDOM: + return ("Random"); default: return "Bug"; } @@ -882,13 +968,14 @@ bool is_valid_spell(spell_type spell) } static bool _cloud_helper(int (*func)(int, int, int, int, cloud_type, - kill_category), + kill_category, killer_type), int x, int y, int pow, int spread_rate, - cloud_type ctype, kill_category whose ) + cloud_type ctype, kill_category whose, + killer_type killer ) { if (!grid_is_solid(grd[x][y]) && env.cgrid[x][y] == EMPTY_CLOUD) { - func(x, y, pow, spread_rate, ctype, whose); + func(x, y, pow, spread_rate, ctype, whose, killer); return (true); } diff --git a/crawl-ref/source/spl-util.h b/crawl-ref/source/spl-util.h index 315e84c697..3734b8d2d4 100644 --- a/crawl-ref/source/spl-util.h +++ b/crawl-ref/source/spl-util.h @@ -66,6 +66,8 @@ void init_spell_descs(void); void init_spell_name_cache(); spell_type spell_by_name(std::string name, bool partial_match = false); +spschool_flag_type school_by_name(std::string name); + int get_spell_slot_by_letter( char letter ); spell_type get_spell_by_letter( char letter ); @@ -95,6 +97,7 @@ int count_bits( unsigned int bits ); const char *spell_title(spell_type which_spell); const char* spelltype_short_name( int which_spelltype ); +const char* spelltype_long_name( int which_spelltype ); typedef int cell_func(coord_def where, int pow, int aux); int apply_area_visible(cell_func cf, int power, @@ -120,9 +123,10 @@ bool spell_direction( dist &spelld, bolt &pbolt, bool cancel_at_self = false ); void apply_area_cloud(int (*func) (int, int, int, int, cloud_type, - kill_category), + kill_category, killer_type), int x, int y, int pow, int number, cloud_type ctype, - kill_category kc, int spread_rate = -1); + kill_category kc, killer_type killer, + int spread_rate = -1); const char *spelltype_name(unsigned int which_spelltype); diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index d33daf981b..d165de7b09 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -1648,7 +1648,8 @@ static void tag_construct_level(writer &th) marshallByte(th, env.cloud[i].type); marshallShort(th, env.cloud[i].decay); marshallByte(th, (char) env.cloud[i].spread_rate); - marshallShort(th, env.cloud[i].whose); + marshallByte(th, env.cloud[i].whose); + marshallByte(th, env.cloud[i].killer); } // how many shops? @@ -1984,7 +1985,8 @@ static void tag_read_level( reader &th, char minorVersion ) env.cloud[i].type = static_cast<cloud_type>(unmarshallByte(th)); env.cloud[i].decay = unmarshallShort(th); env.cloud[i].spread_rate = (unsigned char) unmarshallByte(th); - env.cloud[i].whose = static_cast<kill_category>(unmarshallShort(th)); + env.cloud[i].whose = static_cast<kill_category>(unmarshallByte(th)); + env.cloud[i].killer = static_cast<killer_type>(unmarshallByte(th)); } // how many shops? diff --git a/crawl-ref/source/traps.cc b/crawl-ref/source/traps.cc index b154f9fdf2..ce581054d5 100644 --- a/crawl-ref/source/traps.cc +++ b/crawl-ref/source/traps.cc @@ -35,7 +35,7 @@ #include "randart.h" #include "skills.h" #include "spells3.h" -#include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "terrain.h" #include "transfor.h" @@ -498,8 +498,8 @@ void handle_traps(trap_type trt, int i, bool trap_known) default: mpr((trap_known) ? "You enter the Zot trap." : "Oh no! You have blundered into a Zot trap!"); - miscast_effect( SPTYP_RANDOM, random2(30) + you.your_level, - 75 + random2(100), 3, "a Zot trap" ); + MiscastEffect( &you, ZOT_TRAP_MISCAST, SPTYP_RANDOM, + 3, "a Zot trap" ); break; } learned_something_new(TUT_SEEN_TRAP, you.x_pos, you.y_pos); diff --git a/crawl-ref/source/xom.cc b/crawl-ref/source/xom.cc index d91f1e42a8..bd25195742 100644 --- a/crawl-ref/source/xom.cc +++ b/crawl-ref/source/xom.cc @@ -29,6 +29,7 @@ #include "religion.h" #include "spells2.h" #include "spl-cast.h" +#include "spl-mis.h" #include "spl-util.h" #include "state.h" #include "stuff.h" @@ -739,7 +740,8 @@ static bool _xom_is_bad(int sever) { god_speaks(GOD_XOM, _get_xom_speech("zero miscast effect").c_str()); - miscast_effect(SPTYP_RANDOM, 0, 0, 0, "the mischief of Xom"); + MiscastEffect(&you, -GOD_XOM, SPTYP_RANDOM, 0, + "the mischief of Xom"); done = true; } @@ -747,7 +749,7 @@ static bool _xom_is_bad(int sever) { god_speaks(GOD_XOM, _get_xom_speech("minor miscast effect").c_str()); - miscast_effect(SPTYP_RANDOM, 0, 0, random2(2), + MiscastEffect(&you, -GOD_XOM, SPTYP_RANDOM, random2(2), "the capriciousness of Xom"); done = true; @@ -765,7 +767,7 @@ static bool _xom_is_bad(int sever) { god_speaks(GOD_XOM, _get_xom_speech("medium miscast effect").c_str()); - miscast_effect(SPTYP_RANDOM, 0, 0, random2(3), + MiscastEffect(&you, -GOD_XOM, SPTYP_RANDOM, random2(3), "the capriciousness of Xom"); done = true; @@ -894,10 +896,11 @@ static bool _xom_is_bad(int sever) } else if (x_chance_in_y(11, sever)) { - god_speaks(GOD_XOM, _get_xom_speech("major miscast effect").c_str()); + god_speaks(GOD_XOM, + _get_xom_speech("major miscast effect").c_str()); - miscast_effect(SPTYP_RANDOM, 0, 0, random2(4), - "the severe capriciousness of Xom"); + MiscastEffect(&you, -GOD_XOM, SPTYP_RANDOM, random2(4), + "the severe capriciousness of Xom"); done = true; } |