mons_spells(), and added the new spells Fire Breath, Cold Breath and Draconian Breath to trigger that functionality. Also added the new spell Acid Splash to replace monstuff's _plant_spit(), and Sticky Flame Splash, which is exactly the same as Sticky Flame except for the messages it gives and when it makes noise (monsters now spit sticky flame instead of breathing it). All things that were handled as monster special abilities are still handled as such, and were just changed to manually invoke mons_cast().
The spell messages in dat/database/monspell.txt can now take advantage of a new substitution, "@target@", which is expanded into the spell's target.
Added the spell flags SPFLAG_INNATE, for monster spells which are innate even when the monster is a priest or wizard, and which can be used by them when silenced, and SPFLAG_NOISY, for spells which produce noise even when used by monsters other than priests or wizards.
Added the monster class flags M_SPELL_NO_SILENT, for monsters which aren't wizards or priests, yet still can't use spells if silenced (currently only used for Geryon blowing his horn to summon beasts), and M_NOISY_SPELLS, for monsters which can cast spells when silenced, yet whose spells make noise when not silenced (currently only used by curse skulls and Murray).
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7828 c06c8d41-db1a-0410-9941-cceddc491573
JWJGOMVBPZRSP2VSHLFFFDIF2CS6UPBA6AHL7DAJWGBCHAV3PJDQC YCKUKLTWICHK32BNDLUBCJXKQWR6ESYWZIQWSCKTDKMLMOSTFPVQC 4M56FGNV3IDCB7I4H7TMK3EWSQKWEJ5Z2AKIMHED272TOB34DO4QC FWNNTOEERPUKXPE4OC52UABFZLKIU3O5GRNNLDK4QI4HR2IOU36QC ZEFGFQHN6J2S6EIPX7EPDG22C5YXTI6DMKQHHRCLWN5MQC44KY3AC CHO4U5JC3RNTLXVIDXXJYZMOBZJ4VXW2GVJWDOTBRKK3AJ36LDLQC 46MRRHVYJ3BS74R2BEVUWEWJCI4FCRBSLZ3NWMQCE6FUCNE5P73QC K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC SUWIERONPDATHPDMZRYO6GYIXSW6XIS5V5MK5IV23DWQH2LL7VIAC DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC UAJN2CFA2QHYDHW2UFAVPPHDQFCD54RKM6V2UC4AMEDJUBBLNWIQC TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC 3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC LS3DAZVRDCXVN2BKBC6RGCKO3R43Z7HKG4GXJWLBK4AKBL2G6QDQC NG53L53MSFQZAIVKHG54IEMXNJ33AYVPF2OZM4MMJFDKEJCGPBSAC EO4FXWNFJRHPOSDHWH2Y6QNUP7KB5ANLX43GA3TJLXR3QOOJZ7VQC NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC KEANRIMF5CGFVZ2XJYNFPOAKLXOSOJUOVA73IWBWOG576265ERHAC HMC247EGUJ3Q25DQ3VKUCIGLIO4SZORFQQWAPAF6S2WLQY3WU5TQC 4NBPZKMZBKB3QYX4FFUAKDXQS43NJCBDLMHKDJFVXHQLX4MQDINAC IE3INS4WUXZOBVXB5VWRBYPVPXADD2U2W5H3TBTOYNWJ3EGRSGQQC MI3QIGA6VDUMXL7NO3Q2NWCJN5FGX5CDMDYLEKOMD6SALHEINW5QC Y2NYY7HWFZ2LQDK3ACSLGS37F2J2IJ5LRGCIMZYXLEOSVPD3A4DAC KAOE5HB3THUKVGFZRO5EZESHEB3Q34WUO5DFMLWIKOBF47LZTIYAC 542UIZKI65UDRNEMGFFDBWYD5XC7AYLTZ3JZQRR2GHYJALD3YY6QC WQIEW3O4MANA2KKYRUWEZP44KHVJ4RRHEZTDXSF4EDELX66LO26QC 5JS3QSE3EIXSBVI4DATH2EIFD7QN3POAFEUM7MK4NRMPH5JOPAAQC G2IBQUJ2V2OGM4TR6VKC4CLNVLALTDLB5STOBN7637GOUX2SH4ZAC FEBNNCNH6C44FRFAJKLVC4QF6JLXVEPYUNMCVUJK3KA3ALKSOC3QC T2AYVN57EFJQLFUFLAZDXKDAFDGTDLQIEQWQZNYFWJZBYSTYH4QQC ZBPS5ZTPF3DVTR5WET4XEFHYXU26CRHU2OHX3YO6PD4MTM2DUXAQC SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC KS4WBDOLZ45T5Q742JZQIRN3H7XZTYDPM4TCIAILIRO3OV7ANR2AC ABYG7KPBG5FZFQQB5QYJKZCEDJLQBJDBS6KAV2ZFKQU5QY4WDGWQC 3WHI3KM43ZCN4ITJLFQQBQBC4OJPRS7QTBPIQ6QBCUVKRSK476SAC BGJ7P65JV2OFVXMGAJDHV5Y36TR7JOFDWJUZJBHUBD7SCQMDRBEAC SPELL_STICKY_FLAME_SPLASH, "Sticky Flame Splash",SPTYP_CONJURATION | SPTYP_FIRE,SPFLAG_DIR_OR_TARGET | SPFLAG_MONSTER | SPFLAG_INNATE | SPFLAG_NOISY,4,100,5, 5,0,NULL,true,false},{SPELL_FIRE_BREATH, "Fire Breath",SPTYP_CONJURATION | SPTYP_FIRE,SPFLAG_DIR_OR_TARGET | SPFLAG_MONSTER | SPFLAG_INNATE | SPFLAG_NOISY,5,0,6, 6,0,NULL,true,false},{SPELL_COLD_BREATH, "Cold Breath",SPTYP_CONJURATION | SPTYP_ICE,SPFLAG_DIR_OR_TARGET | SPFLAG_MONSTER | SPFLAG_INNATE | SPFLAG_NOISY,5,0,6, 6,0,NULL,true,false},{SPELL_DRACONIAN_BREATH, "Draconian Breath",SPTYP_CONJURATION,SPFLAG_DIR_OR_TARGET | SPFLAG_MONSTER | SPFLAG_INNATE | SPFLAG_NOISY,8,0,LOS_RADIUS, LOS_RADIUS,0,NULL,true,false},{
SPFLAG_TESTING = 0x08000, // a testing/debugging spellSPFLAG_DEVEL = 0x10000 // a spell under development
SPFLAG_INNATE = 0x08000, // an innate spell, even if// use by a priest/wizardSPFLAG_NOISY = 0x10000, // makes noise, even if innateSPFLAG_TESTING = 0x20000, // a testing/debugging spellSPFLAG_DEVEL = 0x40000 // a spell under development
bool force_silent = false;spell_type real_spell = spell_cast;if (spell_cast == SPELL_DRACONIAN_BREATH){int type = monster->type;if (mons_genus(type) == MONS_DRACONIAN)type = draco_subspecies(monster);switch(type){case MONS_MOTTLED_DRACONIAN:real_spell = SPELL_STICKY_FLAME_SPLASH;break;case MONS_YELLOW_DRACONIAN:real_spell = SPELL_ACID_SPLASH;break;case MONS_PLAYER_GHOST:// Draining breath is silent.force_silent = true;break;default:break;}}else if (monster->type == MONS_SHADOW_DRAGON)// Draining breath is silent.force_silent = true;
}std::string target = "something";if (pbolt.target == you.pos())target = "you";else if (see_grid(pbolt.target)){int midx = mgrd(pbolt.target);if (midx != NON_MONSTER){monsters* mtarg = &menv[midx];if (you.can_see(mtarg))target = mtarg->name(mons_friendly(mtarg) ? DESC_NOCAP_YOUR :DESC_NOCAP_THE);}
void setup_dragon(struct monsters *monster, bolt &pbolt){const int type = (mons_genus(monster->type) == MONS_DRACONIAN)? draco_subspecies(monster) : monster->type;int scaling = 100;pbolt.name.clear();switch (type){case MONS_FIREDRAKE:case MONS_HELL_HOUND:case MONS_DRAGON:case MONS_LINDWURM:case MONS_XTAHUA:pbolt.name += "blast of flame";pbolt.aux_source = "blast of fiery breath";pbolt.flavour = BEAM_FIRE;pbolt.colour = RED;pbolt.range = 6;break;case MONS_ICE_DRAGON:pbolt.name += "blast of cold";pbolt.aux_source = "blast of icy breath";pbolt.flavour = BEAM_COLD;pbolt.colour = WHITE;pbolt.range = 7;break;case MONS_RED_DRACONIAN:pbolt.name += "searing blast";pbolt.aux_source = "blast of searing breath";pbolt.flavour = BEAM_FIRE;pbolt.colour = RED;pbolt.range = 6;scaling = 65;break;case MONS_WHITE_DRACONIAN:pbolt.name += "chilling blast";pbolt.aux_source = "blast of chilling breath";pbolt.flavour = BEAM_COLD;pbolt.colour = WHITE;pbolt.range = 7;scaling = 65;break;case MONS_PLAYER_GHOST: // draconians onlypbolt.name += "blast of negative energy";pbolt.aux_source = "blast of draining breath";pbolt.flavour = BEAM_NEG;pbolt.colour = DARKGREY;pbolt.range = LOS_RADIUS;scaling = 65;break;default:DEBUGSTR("Bad monster class in setup_dragon()");break;}#ifdef DEBUG_DIAGNOSTICSmprf( MSGCH_DIAGNOSTICS, "bolt name: '%s'", pbolt.name.c_str() );#endifpbolt.damage = dice_def( 3, (monster->hit_dice * 2) );pbolt.damage.size = scaling * pbolt.damage.size / 100;pbolt.type = dchar_glyph(DCHAR_FIRED_ZAP);pbolt.hit = 30;pbolt.beam_source = monster_index(monster);pbolt.thrower = KILL_MON;pbolt.is_beam = true;// Accuracy is lowered by one quarter if the dragon is attacking// a target that is wielding a weapon of dragon slaying (which// makes the dragon/draconian avoid looking at the foe).// FIXME: This effect is not yet implemented for player draconians// or characters in dragon form breathing at monsters wielding a// weapon with this brand.if (is_dragonkind(monster, monster)){if (actor *foe = monster->get_foe()){if (const item_def *weapon = foe->weapon()){if (get_weapon_brand(*weapon) == SPWPN_DRAGON_SLAYING){pbolt.hit *= 3;pbolt.hit /= 4;}}}}}
const int drac_type = (mons_genus(mons->type) == MONS_DRACONIAN)? draco_subspecies(mons) : mons->type;int real_spell = spell_cast;if (spell_cast == SPELL_DRACONIAN_BREATH){switch(drac_type){case MONS_BLACK_DRACONIAN:real_spell = SPELL_LIGHTNING_BOLT;break;case MONS_MOTTLED_DRACONIAN:real_spell = SPELL_STICKY_FLAME_SPLASH;break;case MONS_YELLOW_DRACONIAN:real_spell = SPELL_ACID_SPLASH;break;case MONS_GREEN_DRACONIAN:real_spell = SPELL_POISONOUS_CLOUD;break;
switch (spell_cast)
case MONS_PURPLE_DRACONIAN:real_spell = SPELL_ISKENDERUNS_MYSTIC_BLAST;break;case MONS_RED_DRACONIAN:real_spell = SPELL_FIRE_BREATH;break;case MONS_WHITE_DRACONIAN:real_spell = SPELL_COLD_BREATH;break;case MONS_PALE_DRACONIAN:real_spell = SPELL_STEAM_BALL;break;case MONS_PLAYER_GHOST:// Handled laterbreak;default:DEBUGSTR("Invalid monster using draconian breath spell");break;}}switch (real_spell)
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;break;case SPELL_DRACONIAN_BREATH:beam.damage = dice_def( 3, (mons->hit_dice * 2) );beam.type = dchar_glyph(DCHAR_FIRED_ZAP);beam.thrower = KILL_MON;beam.hit = 30;beam.is_beam = true;
if (spell_cast == SPELL_DRACONIAN_BREATH){int scaling = 100;switch(drac_type){case MONS_RED_DRACONIAN:beam.name = "searing blast";beam.aux_source = "blast of searing breath";scaling = 65;break;case MONS_WHITE_DRACONIAN:beam.name = "chilling blast";beam.aux_source = "blast of chilling breath";scaling = 65;break;case MONS_PLAYER_GHOST: // draconians onlybeam.name = "blast of negative energy";beam.aux_source = "blast of draining breath";beam.flavour = BEAM_NEG;beam.colour = DARKGREY;scaling = 65;break;}beam.damage.size = scaling * beam.damage.size / 100;}// Accuracy is lowered by one quarter if the dragon is attacking// a target that is wielding a weapon of dragon slaying (which// makes the dragon/draconian avoid looking at the foe).// FIXME: This effect is not yet implemented for player draconians// or characters in dragon form breathing at monsters wielding a// weapon with this brand.if (is_dragonkind(mons, mons)){if (actor *foe = mons->get_foe()){if (const item_def *weapon = foe->weapon()){if (get_weapon_brand(*weapon) == SPWPN_DRAGON_SLAYING){beam.hit *= 3;beam.hit /= 4;}}}}
case MONS_BLACK_DRACONIAN:draco_breath = SPELL_LIGHTNING_BOLT;break;case MONS_PALE_DRACONIAN:draco_breath = SPELL_STEAM_BALL;break;case MONS_GREEN_DRACONIAN:draco_breath = SPELL_POISONOUS_CLOUD;break;case MONS_PURPLE_DRACONIAN:draco_breath = SPELL_ISKENDERUNS_MYSTIC_BLAST;break;case MONS_MOTTLED_DRACONIAN:draco_breath = SPELL_STICKY_FLAME;break;
static bool _mons_announce_cast(monsters *monster, bool nearby,spell_type spell_cast,spell_type draco_breath){const msg_channel_type spl = (mons_friendly(monster) ? MSGCH_FRIEND_SPELL: MSGCH_MONSTER_SPELL);if (nearby){if (spell_cast == draco_breath){if (simple_monster_message(monster, " breathes.", spl))return (true);else{if (!silenced(monster->pos())&& !silenced(you.pos())){mpr("You hear a roar.", MSGCH_SOUND);return (true);}}}else if (monster->type == -1)monster->hit_points = -1;}return (false);}
static void _setup_plant_spit(monsters *monster, bolt &pbolt){pbolt.name = "acid";pbolt.type = dchar_glyph(DCHAR_FIRED_ZAP);// immobile plants get long-range spit...pbolt.range = LOS_RADIUS;pbolt.colour = YELLOW;pbolt.flavour = BEAM_ACID;pbolt.beam_source = monster_index(monster);pbolt.damage = dice_def( 3, 7 );pbolt.hit = 20 + (3 * monster->hit_dice);pbolt.thrower = KILL_MON_MISSILE;pbolt.aux_source.clear();}static bool _plant_spit(monsters *monster, bolt &pbolt){bool did_spit = false;char spit_string[INFO_SIZE];_setup_plant_spit(monster, pbolt);// Fire tracer.fire_tracer(monster, pbolt);if (mons_should_fire(pbolt)){_make_mons_stop_fleeing(monster);strcpy( spit_string, " spits" );if (pbolt.target == you.pos())strcat( spit_string, " at you" );strcat( spit_string, "." );simple_monster_message( monster, spit_string );fire_beam( pbolt );did_spit = true;}return (did_spit);}
M_NO_SKELETON = (1<<29), // boneless corpses
M_SPELL_NO_SILENT = (1<<28), // cannot cast spells when silenced,// even though it's not a priest or// wizardM_NOISY_SPELLS = (1<<29), // can cast spells when silenced, but// casting makes noise when not// silencedM_NO_SKELETON = (1<<30), // boneless corpses