From c42a7b4b46ae145299bea6082d308e979f6a4c8a Mon Sep 17 00:00:00 2001 From: zelgadis Date: Sun, 14 Dec 2008 07:42:37 +0000 Subject: Dragons and draconians now give a breath message of "breathes @beam@ at @target@." instead of merely "breathes." If an invisible monster on a square visible to the player fires a visible beam then give a message to the player about it. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7829 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/beam.cc | 168 +++++++++++++++++++++++++++-- crawl-ref/source/beam.h | 9 ++ crawl-ref/source/dat/database/monspell.txt | 4 +- crawl-ref/source/mstuff2.cc | 106 ++++++++++-------- 4 files changed, 234 insertions(+), 53 deletions(-) (limited to 'crawl-ref/source') diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index 1f03b7930a..817e6ab89c 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -214,6 +214,7 @@ static void _beam_set_default_values(bolt &beam, int power) beam.dropped_item = false; // no item droped yet beam.reflections = 0; // no reflections yet beam.bounces = 0; // no bounces yet + beam.seen = false; // not seen yet beam.aux_source.clear(); // additional source info, unused } @@ -288,6 +289,7 @@ bool player_tracer( zap_type ztype, int power, bolt &pbolt, int range) // Clear misc pbolt.dropped_item = false; + pbolt.seen = false; pbolt.reflections = 0; pbolt.bounces = 0; @@ -312,6 +314,9 @@ bool player_tracer( zap_type ztype, int power, bolt &pbolt, int range) pbolt.reflections = 0; pbolt.bounces = 0; + // Haven't seen it yet in the actual firing. + pbolt.seen = false; + // Set to non-tracing for actual firing. pbolt.is_tracer = false; return (true); @@ -1526,20 +1531,39 @@ static void _munge_bounced_bolt(bolt &old_bolt, bolt &new_bolt, void fire_beam(bolt &pbolt) { + ASSERT(!pbolt.name.empty()); + ASSERT(pbolt.flavour > BEAM_NONE && pbolt.flavour < NUM_BEAMS); + ASSERT(!pbolt.drop_item || pbolt.item); + ASSERT(!pbolt.dropped_item); + pbolt.real_flavour = pbolt.flavour; const int reflections = pbolt.reflections; if (reflections == 0) { - // We aren't being recursively called. + // We aren't being recursively called, so initialize some stuff. beam_message_cache.clear(); pbolt.range_used = 0; + + // pbolt.seen might be set by caller to supress this. + if (!pbolt.seen && see_grid(pbolt.source) && pbolt.range > 0 + && pbolt.type != 0 && pbolt.name[0] != '0') + { + pbolt.seen = true; + int midx = mgrd(pbolt.source); + + if (!pbolt.is_tracer && !YOU_KILL(pbolt.thrower) + && !crawl_state.is_god_acting() + && (midx == NON_MONSTER || !you.can_see(&menv[midx]))) + { + mprf("%s appears from out of thin air!", + article_a(pbolt.name, false).c_str()); + } + } } ASSERT(pbolt.range >= 0 && pbolt.range_used >=0); ASSERT(pbolt.range_used <= pbolt.range); - ASSERT(!pbolt.drop_item || pbolt.item); - ASSERT(!pbolt.dropped_item); if (pbolt.range == pbolt.range_used && pbolt.range > 0) { @@ -1720,6 +1744,19 @@ void fire_beam(bolt &pbolt) if (grid_is_solid(grd(testpos))) break; + const bool was_seen = pbolt.seen; + if (!was_seen && pbolt.range > 0 && pbolt.type != 0 + && pbolt.name[0] != '0' && see_grid(testpos)) + { + pbolt.seen = true; + } + + if (!was_seen && pbolt.seen && !pbolt.is_tracer) + { + mprf("%s appears from out of your range of vision.", + article_a(pbolt.name, false).c_str()); + } + // Check for "target termination" // occurs when beam can be targetted at empty // cell (e.g. a mage wants an explosion to happen @@ -2713,6 +2750,9 @@ void fire_tracer(const monsters *monster, bolt &pbolt, bool explode_only) pbolt.reflections = 0; pbolt.bounces = 0; + // Hasn't been seen yet in the real firing. + pbolt.seen = false; + // Unset tracer flag (convenience). pbolt.is_tracer = false; } @@ -5695,18 +5735,18 @@ bolt::bolt() : range(0), type('*'), flavour(BEAM_MAGIC), real_flavour(BEAM_MAGIC), drop_item(false), item(NULL), source(), target(), pos(), damage(0,0), ench_power(0), hit(0), thrower(KILL_MISC), ex_size(0), - beam_source(MHITNOT), name(), is_beam(false), + beam_source(MHITNOT), name(), short_name(), is_beam(false), is_explosion(false), is_big_cloud(false), aimed_at_spot(false), aux_source(), affects_nothing(false), effect_known(true), obvious_effect(false), fr_count(0), foe_count(0), fr_power(0), foe_power(0), fr_hurt(0), foe_hurt(0), fr_helped(0),foe_helped(0), dropped_item(false), item_pos(), item_index(NON_ITEM), - range_used(0), is_tracer(false), aimed_at_feet(false), - msg_generated(false), in_explosion_phase(false), - smart_monster(false), can_see_invis(false), - attitude(ATT_HOSTILE), foe_ratio(0), chose_ray(false), - beam_cancelled(false), dont_stop_foe(false), + seen(false), range_used(0), is_tracer(false), + aimed_at_feet(false), msg_generated(false), + in_explosion_phase(false), smart_monster(false), + can_see_invis(false), attitude(ATT_HOSTILE), foe_ratio(0), + chose_ray(false), beam_cancelled(false), dont_stop_foe(false), dont_stop_fr(false), dont_stop_player(false), bounces(false), bounce_pos(), reflections(false), reflector(-1) { @@ -5776,3 +5816,113 @@ bool bolt::is_enchantment() const return (this->flavour >= BEAM_FIRST_ENCHANTMENT && this->flavour <= BEAM_LAST_ENCHANTMENT); } + +std::string bolt::get_short_name() +{ + if (!short_name.empty()) + return (short_name); + + if (item != NULL && is_valid_item(*item)) + return item->name(DESC_NOCAP_A, false, false, false, false, + ISFLAG_IDENT_MASK | ISFLAG_COSMETIC_MASK + | ISFLAG_RACIAL_MASK); + + if (real_flavour == BEAM_RANDOM || real_flavour == BEAM_CHAOS) + flavour = real_flavour; + + if (flavour == BEAM_FIRE && name == "sticky fire") + return ("sticky fire"); + + if (flavour == BEAM_ELECTRICITY && is_beam) + return ("lightning"); + + if (flavour == BEAM_NONE || flavour == BEAM_MISSILE + || flavour == BEAM_MMISSILE) + { + return (name); + } + + return beam_type_name(flavour); +} + +std::string beam_type_name(beam_type type) +{ + switch(type) + { + case BEAM_NONE: return("none"); + case BEAM_MISSILE: return("missile"); + case BEAM_MMISSILE: return("magic missile"); + + case BEAM_POTION_FIRE: + case BEAM_FIRE: return("fire"); + + case BEAM_POTION_COLD: + case BEAM_COLD: return("cold"); + + case BEAM_MAGIC: return("magic"); + case BEAM_ELECTRICITY: return("electricity"); + + case BEAM_POTION_STINKING_CLOUD: + case BEAM_POTION_POISON: + case BEAM_POISON: return("poison"); + + case BEAM_NEG: return("negative energy"); + case BEAM_ACID: return("acid"); + + case BEAM_MIASMA: + case BEAM_POTION_MIASMA: return("miasma"); + + case BEAM_SPORE: return("spores"); + case BEAM_POISON_ARROW: return("poison arrow"); + case BEAM_HELLFIRE: return("hellfire"); + case BEAM_NAPALM: return("sticky fire"); + + case BEAM_POTION_STEAM: + case BEAM_STEAM: return("steam"); + + case BEAM_HELLFROST: return("hellfrost"); + case BEAM_ENERGY: return("energy"); + case BEAM_HOLY: return("holy power"); + case BEAM_FRAG: return("fragments"); + case BEAM_LAVA: return("magma"); + case BEAM_ICE: return("ice"); + case BEAM_NUKE: return("nuke"); + case BEAM_RANDOM: return("random"); + case BEAM_CHAOS: return("chaos"); + case BEAM_SLOW: return("slow"); + case BEAM_HASTE: return("haste"); + case BEAM_HEALING: return("healing"); + case BEAM_PARALYSIS: return("paralysis"); + case BEAM_CONFUSION: return("confusion"); + case BEAM_INVISIBILITY: return("invisibility"); + case BEAM_DIGGING: return("digging"); + case BEAM_TELEPORT: return("teleportation"); + case BEAM_POLYMORPH: return("polymorph"); + case BEAM_CHARM: return("enslave"); + case BEAM_BANISH: return("banishment"); + case BEAM_DEGENERATE: return("degenration"); + case BEAM_ENSLAVE_UNDEAD: return("enslave undead"); + case BEAM_ENSLAVE_SOUL: return("enslave soul"); + case BEAM_PAIN: return("pain"); + case BEAM_DISPEL_UNDEAD: return("dispel undead"); + case BEAM_DISINTEGRATION: return("disintegration"); + case BEAM_ENSLAVE_DEMON: return("enlsave demon"); + case BEAM_BLINK: return("blink"); + case BEAM_PETRIFY: return("petrify"); + case BEAM_BACKLIGHT: return("backlight"); + case BEAM_SLEEP: return("sleep"); + case BEAM_POTION_BLACK_SMOKE: return("black smoke"); + case BEAM_POTION_GREY_SMOKE: return("grey smoke"); + case BEAM_POTION_BLUE_SMOKE: return("blue smoke"); + case BEAM_POTION_PURP_SMOKE: return("purple smoke"); + case BEAM_POTION_RANDOM: return("random potion"); + case BEAM_TORMENT_DAMAGE: return("torment damage"); + case BEAM_STEAL_FOOD: return("steal food"); + case BEAM_LINE_OF_SIGHT: return("line of sight"); + case NUM_BEAMS: + DEBUGSTR("invalid beam type"); + return("INVALID"); + } + DEBUGSTR("unknown beam type"); + return("UNKNOWN"); +} diff --git a/crawl-ref/source/beam.h b/crawl-ref/source/beam.h index c805611783..f8968e3967 100644 --- a/crawl-ref/source/beam.h +++ b/crawl-ref/source/beam.h @@ -54,6 +54,7 @@ struct bolt char ex_size; // explosion radius (0==none) int beam_source; // NON_MONSTER or monster index # std::string name; + std::string short_name; bool is_beam; // beams? (can hits multiple targets?) bool is_explosion; bool is_big_cloud; // expands into big_cloud at endpoint @@ -76,6 +77,8 @@ struct bolt coord_def item_pos; // position item was dropped at int item_index; // mitm[index] of item + bool seen; // Has player seen the beam? + // INTERNAL use - should not usually be set outside of beam.cc int range_used; bool is_tracer; // is this a tracer? @@ -117,6 +120,10 @@ public: killer_type killer() const; actor* agent() const; + + // Returns member short_name if set, otherwise some reasonble string + // for a short name, most likely the name of the beam's flavour. + std::string get_short_name(); }; dice_def calc_dice( int num_dice, int max_damage ); @@ -163,4 +170,6 @@ int affect(bolt &beam, const coord_def& p = coord_def(), void beam_drop_object( bolt &beam, item_def *item = NULL, const coord_def& where = coord_def() ); +std::string beam_type_name(beam_type type); + #endif diff --git a/crawl-ref/source/dat/database/monspell.txt b/crawl-ref/source/dat/database/monspell.txt index 9e232d65d3..bbf76a8b83 100644 --- a/crawl-ref/source/dat/database/monspell.txt +++ b/crawl-ref/source/dat/database/monspell.txt @@ -12,7 +12,7 @@ You hear a spitting sound. %%%% Draconian Breath cast -@The_monster@ breathes. +@The_monster@ breathes @beam@ at @target@. %%%% Draconian Breath cast unseen @@ -78,7 +78,7 @@ demon cast %%%% dragon cast -@The_monster@ breathes. +@The_monster@ breathes @beam@ at @target@. %%%% dragon cast unseen diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc index 477d886d1f..5c9407f0fe 100644 --- a/crawl-ref/source/mstuff2.cc +++ b/crawl-ref/source/mstuff2.cc @@ -651,7 +651,6 @@ void mons_cast_noise(monsters *monster, bolt &pbolt, spell_type spell_cast) } std::string target = "something"; - if (pbolt.target == you.pos()) target = "you"; else if (see_grid(pbolt.target)) @@ -669,6 +668,22 @@ void mons_cast_noise(monsters *monster, bolt &pbolt, spell_type spell_cast) msg = replace_all(msg, "@target@", target); + // Don't check for pbolt.seen, since we get called before the beam is + // fired. + std::string beam_name; + if (pbolt.flavour <= BEAM_NONE + || pbolt.flavour >= NUM_BEAMS + || pbolt.name.empty()) + { + beam_name = "INVALID BEAM"; + } + else if (pbolt.type == 0 || pbolt.name[0] == '0') + beam_name = "INVISIBLE BEAM"; + else + beam_name = pbolt.get_short_name(); + + msg = replace_all(msg, "@beam@", beam_name); + const msg_channel_type chan = (unseen ? MSGCH_SOUND : mons_friendly(monster) ? MSGCH_FRIEND_SPELL : @@ -782,6 +797,7 @@ void setup_mons_cast(monsters *monster, bolt &pbolt, pbolt.flavour = theBeam.flavour; pbolt.thrower = theBeam.thrower; pbolt.name = theBeam.name; + pbolt.short_name = theBeam.short_name; pbolt.is_beam = theBeam.is_beam; pbolt.source = monster->pos(); pbolt.is_tracer = false; @@ -1754,14 +1770,15 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power ) break; case SPELL_ISKENDERUNS_MYSTIC_BLAST: // mystic blast - beam.colour = LIGHTMAGENTA; - beam.name = "orb of energy"; - beam.damage = dice_def( 3, 7 + (power / 14) ); - beam.hit = 20 + (power / 20); - beam.type = dchar_glyph(DCHAR_FIRED_ZAP); - beam.thrower = KILL_MON_MISSILE; - beam.flavour = BEAM_MMISSILE; - beam.is_beam = false; + beam.colour = LIGHTMAGENTA; + beam.name = "orb of energy"; + beam.short_name = "energy"; + beam.damage = dice_def( 3, 7 + (power / 14) ); + beam.hit = 20 + (power / 20); + beam.type = dchar_glyph(DCHAR_FIRED_ZAP); + beam.thrower = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; + beam.is_beam = false; break; case SPELL_STEAM_BALL: @@ -1810,14 +1827,15 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power ) break; case SPELL_ENERGY_BOLT: // eye of devastation - beam.colour = YELLOW; - beam.name = "bolt of energy"; - beam.damage = dice_def( 3, 20 ); - beam.hit = 15 + power / 30; - beam.type = dchar_glyph(DCHAR_FIRED_ZAP); - beam.thrower = KILL_MON_MISSILE; - beam.flavour = BEAM_NUKE; // a magical missile which destroys walls - beam.is_beam = true; + beam.colour = YELLOW; + beam.name = "bolt of energy"; + beam.short_name = "energy"; + beam.damage = dice_def( 3, 20 ); + beam.hit = 15 + power / 30; + beam.type = dchar_glyph(DCHAR_FIRED_ZAP); + beam.thrower = KILL_MON_MISSILE; + beam.flavour = BEAM_NUKE; // a magical missile which destroys walls + beam.is_beam = true; break; case SPELL_STING: // sting @@ -1911,14 +1929,15 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power ) break; case SPELL_QUICKSILVER_BOLT: // Quicksilver dragon - beam.colour = random_colour(); - beam.name = "bolt of energy"; - beam.damage = dice_def( 3, 25 ); - beam.hit = 16 + power / 25; - beam.type = dchar_glyph(DCHAR_FIRED_ZAP); - beam.thrower = KILL_MON_MISSILE; - beam.flavour = BEAM_MMISSILE; - beam.is_beam = false; + beam.colour = random_colour(); + beam.name = "bolt of energy"; + beam.short_name = "energy"; + beam.damage = dice_def( 3, 25 ); + beam.hit = 16 + power / 25; + beam.type = dchar_glyph(DCHAR_FIRED_ZAP); + beam.thrower = KILL_MON_MISSILE; + beam.flavour = BEAM_MMISSILE; + beam.is_beam = false; break; case SPELL_HELLFIRE: // fiend's hellfire @@ -1935,14 +1954,15 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power ) break; case SPELL_METAL_SPLINTERS: - beam.name = "spray of metal splinters"; - beam.damage = dice_def( 3, 20 + power / 20 ); - beam.colour = CYAN; - beam.type = dchar_glyph(DCHAR_FIRED_ZAP); - beam.thrower = KILL_MON; - beam.flavour = BEAM_FRAG; - beam.hit = 19 + power / 30; - beam.is_beam = true; + beam.name = "spray of metal splinters"; + beam.short_name = "metal splinters"; + beam.damage = dice_def( 3, 20 + power / 20 ); + beam.colour = CYAN; + beam.type = dchar_glyph(DCHAR_FIRED_ZAP); + beam.thrower = KILL_MON; + beam.flavour = BEAM_FRAG; + beam.hit = 19 + power / 30; + beam.is_beam = true; break; case SPELL_BANISHMENT: @@ -1974,15 +1994,16 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power ) break; case SPELL_COLD_BREATH: - beam.name = "blast of cold"; - beam.aux_source = "blast of icy breath"; - beam.damage = dice_def( 3, (mons->hit_dice * 2) ); - beam.colour = WHITE; - beam.type = dchar_glyph(DCHAR_FIRED_ZAP); - beam.thrower = KILL_MON; - beam.hit = 30; - beam.flavour = BEAM_COLD; - beam.is_beam = true; + beam.name = "blast of cold"; + beam.aux_source = "blast of icy breath"; + beam.short_name = "frost"; + beam.damage = dice_def( 3, (mons->hit_dice * 2) ); + beam.colour = WHITE; + beam.type = dchar_glyph(DCHAR_FIRED_ZAP); + beam.thrower = KILL_MON; + beam.hit = 30; + beam.flavour = BEAM_COLD; + beam.is_beam = true; break; case SPELL_DRACONIAN_BREATH: @@ -2011,6 +2032,7 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power ) case MONS_WHITE_DRACONIAN: beam.name = "chilling blast"; beam.aux_source = "blast of chilling breath"; + beam.short_name = "frost"; scaling = 65; break; -- cgit v1.2.3-54-g00ecf