wizard/priest monsters now make noise, with the loudness depending upon level and spell disciplines: conjuration spells have a loudness equal to their level, spells containing only air and/or poison equal to 50% of their level (rounded up), and everything else to 75% of the level (rounded up). This undoudedtly needs rebalancing, and a better level+disciplines -> loudness formula. Also, each spell can have it's loudness tweaked from the default via the noise_mod field of the spell_desc field in spl-data.h, for those spells whose desired loudness don't quite fit into whatever formula is used.
The messages used to announce a spell have been moved out to source/dat/database/monspell.txt, which uses the same format and conventions as monspeak.txt. So far this has just been used to make invisible wizard/priest monsters give a message to the player if the player can hear them cast, and also to make curse skulls and Murray not "gesture wildly". However, it could be used to give a greater variety of spell announcement messages based upon the spell used or the monster's type/species/genus, and/or to give multi-part messages.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7820 c06c8d41-db1a-0410-9941-cceddc491573
ZEFGFQHN6J2S6EIPX7EPDG22C5YXTI6DMKQHHRCLWN5MQC44KY3AC
SUWIERONPDATHPDMZRYO6GYIXSW6XIS5V5MK5IV23DWQH2LL7VIAC
53BVN4ZHKGUAJE53HM6FC6GKUGXOU5QBUT7CLXV2ONS6S5NPW55AC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC
PR2XIEELO6UJWT3EXDHWCJZGIZCCF3D6KF6LC67R6RWWAVNWEHWAC
NQMXQ6OQVUSC7Y7F7IL252QW4A5JED224EECNHWAM4ZZYVNY745AC
7KVPF74ACO6Q5FXS2YBBJTJT4Y4YN2M3ZNIPXFI3QSQNGNTHD7AAC
CHO4U5JC3RNTLXVIDXXJYZMOBZJ4VXW2GVJWDOTBRKK3AJ36LDLQC
KHVK7HH7OIYOBZY52WQGGLC2O4SXTWQS2XAHNJZZ7JZ3QTOUTLNAC
JI4NDSOXGGZ7QHXXFB3ZTHAKHABXYBZXPDGLUFV5SKYEOL5FT7JQC
PMODADA7C3BWOFCPU37626DKMWENG6FAR5I6TOIQU5OBJBUJTSAQC
WG6O475IOLZFMUQSLVR2KHM7XTBF5HH276L2KDGF7UOSESDOAILQC
64LQALS66EFDRQUEN3NRAWD2PWE7VMNRSEUTMRKQSEQTS55ZRZXAC
4SWAT5KCKQV527NKELAXFQ5XA4Q5HONQXD4VBXMUZNPVPQKPCPNAC
TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC
UAJN2CFA2QHYDHW2UFAVPPHDQFCD54RKM6V2UC4AMEDJUBBLNWIQC
WLMMF4LM4KVLTRFJCM57UYLN6DS37L6UR7TS6TA3EYDGRCQGOUIAC
WQIEW3O4MANA2KKYRUWEZP44KHVJ4RRHEZTDXSF4EDELX66LO26QC
5JS3QSE3EIXSBVI4DATH2EIFD7QN3POAFEUM7MK4NRMPH5JOPAAQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
G2IBQUJ2V2OGM4TR6VKC4CLNVLALTDLB5STOBN7637GOUX2SH4ZAC
KAOE5HB3THUKVGFZRO5EZESHEB3Q34WUO5DFMLWIKOBF47LZTIYAC
542UIZKI65UDRNEMGFFDBWYD5XC7AYLTZ3JZQRR2GHYJALD3YY6QC
XOYDYIYKWQZIA5ZWV6FS5DCU2PDIANTIJFEAONEJOXHZJRMADBZQC
QV6FWOWE22V4GIDHNV5MJ36G5ETDCFBQSHGUHZLKIXQXMFOS35YAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
SG76BPJKTQGDFNP5QFMAVR6H72FMMAYCECVGSWWSCLMAVQX7E3FQC
3QLM46S44Z7GDLWPH3VHBMW2RSWZAOLGJMG2BDKNGUOZIM4IX6WAC
J6APXOT4QOGQFONWB7G546VTVF6QG42HVOROMHF7YBDJPR4K26OAC
AIIVH43Z5X3GTPFY4FXQRZPG6Y7QPH2KJ47VM2Q43PCGGD5MTMOAC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
3WHI3KM43ZCN4ITJLFQQBQBC4OJPRS7QTBPIQ6QBCUVKRSK476SAC
7C62IQ3PLAE7RLZ2ZNA3G6Z7LPXWAMK2OEHSBNY4WEKJ42BPZYQAC
Y56C5OMUQ5XF2G6DKDV4R5MED44UOIUPTBBQVWQBUHYIXYA5MOZAC
EJKHYV2Z6UPRVYUAL4WRW33GBNHYBFPMPA57HMBX2LQKXHIUO5VQC
struct spell_desc
{
int id;
const char *title;
unsigned int disciplines; // bitfield
unsigned int flags; // bitfield
unsigned int level;
int power_cap;
// At power 0, you get min_range. At power power_cap, you get max_range.
int min_range;
int max_range;
const char *target_prompt;
// If a monster is casting this, does it need a tracer?
bool ms_needs_tracer;
// The spell can be used no matter what the monster's foe is.
bool ms_utility;
};
struct spell_desc
{
int id;
const char *title;
unsigned int disciplines; // bitfield
unsigned int flags; // bitfield
unsigned int level;
int power_cap;
// At power 0, you get min_range. At power power_cap, you get max_range.
int min_range;
int max_range;
// How much louder or quieter the spell is than the default.
int noise_mod;
const char *target_prompt;
// If a monster is casting this, does it need a tracer?
bool ms_needs_tracer;
// The spell can be used no matter what the monster's foe is.
bool ms_utility;
};
}
int spell_noise(spell_type spell)
{
const spell_desc *desc = _seekspell(spell);
return desc->noise_mod + spell_noise(desc->disciplines, desc->level);
}
int spell_noise(unsigned int disciplines, int level)
{
if (disciplines == SPTYP_NONE)
return (0);
else if (disciplines & SPTYP_CONJURATION)
return (level);
else if (disciplines && !(disciplines & (SPTYP_POISON | SPTYP_AIR)))
return div_round_up(level * 3, 4);
else
return div_round_up(level, 2);
void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast);
void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast,
bool do_noise = true);
void mons_cast_noise(monsters *monster, bolt &pbolt, spell_type spell_cast);
}
void mons_cast_noise(monsters *monster, bolt &pbolt, spell_type spell_cast)
{
const bool unseen = !you.can_see(monster);
const bool silent = silenced(monster->pos());
if (unseen && silent)
return;
const bool priest = mons_class_flag(monster->type, M_PRIEST);
const bool wizard = mons_class_flag(monster->type, M_ACTUAL_SPELLS);
const bool innate = !(priest || wizard);
int noise;
if(silent || innate)
noise = 0;
else
noise = spell_noise(spell_cast);
const std::string cast_str = " cast";
std::string suffix;
if (silent)
suffix = " silent";
else if (unseen)
suffix = " unseen";
std::string key;
// First try the individual spell.
key = spell_title(spell_cast) + cast_str;
std::string msg = getSpeakString(key + suffix);
if (msg.empty() && silent)
msg = getSpeakString(key);
// Second, try the individual monster.
if (msg.empty())
{
key = mons_type_name(monster->type, DESC_PLAIN);
key += cast_str;
msg = getSpeakString(key + suffix);
if (msg.empty() && silent)
msg = getSpeakString(key);
}
// Third, try generic priest or wizard messages.
if (msg.empty() && !innate)
{
if (priest)
key = "priest";
else
key = "wizard";
key += cast_str;
msg = getSpeakString(key + suffix);
if (msg.empty() && silent)
msg = getSpeakString(key);
}
// Fourth, try the monster's species.
if (msg.empty())
{
key = mons_type_name(mons_species(monster->type), DESC_PLAIN);
key += cast_str;
msg = getSpeakString(key + suffix);
if (msg.empty() && silent)
msg = getSpeakString(key);
}
// Fifth, try the monster's genus.
if (msg.empty())
{
key = mons_type_name(mons_genus(monster->type), DESC_PLAIN);
key += cast_str;
msg = getSpeakString(key + suffix);
if (msg.empty() && silent)
msg = getSpeakString(key);
}
// Lastly, maybe it's a demon.
if (msg.empty() && mons_is_demon(monster->type))
{
key = "demon";
key += cast_str;
msg = getSpeakString(key + suffix);
if (msg.empty() && silent)
msg = getSpeakString(key);
}
if (msg.empty())
{
if (silent)
return;
noisy(noise, monster->pos());
return;
}
const msg_channel_type chan =
(unseen ? MSGCH_SOUND :
mons_friendly(monster) ? MSGCH_FRIEND_SPELL :
MSGCH_MONSTER_SPELL);
if (silent)
{
mons_speaks_msg(monster, msg, chan, true);
return;
}
if (msg.find(" roar") != std::string::npos)
noise = get_shout_noise_level(S_ROAR);
else if (msg.find(" breathes") != std::string::npos)
{
shout_type type = mons_shouts(monster->type);
if(type == S_SILENT)
type = S_ROAR;
noise = get_shout_noise_level(type);
}
// noisy() returns true if the player heard the noise.
if (noisy(noise, monster->pos()) || !unseen)
mons_speaks_msg(monster, msg, chan);
}
static const char *_orb_of_fire_glow()
{
static const char *orb_glows[] =
{
" glows yellow.",
" glows bright magenta.",
" glows deep purple.", // Smoke on the Water
" glows red.",
" emits a lurid red light.",
};
return RANDOM_ELEMENT(orb_glows);
if (silenced(monster->pos()))
return (false);
simple_monster_message( monster, " winds a great silver horn.",
spl );
}
else if (mons_is_demon( monster->type ))
{
simple_monster_message( monster, " gestures.", spl );
}
else
{
switch (monster->type)
if (simple_monster_message(monster, " breathes.", spl))
return (true);
else
default:
if (spell_cast == draco_breath)
{
if (!simple_monster_message(monster, " breathes.", spl))
{
if (!silenced(monster->pos())
&& !silenced(you.pos()))
{
mpr("You hear a roar.", MSGCH_SOUND);
}
}
break;
}
if (silenced(monster->pos()))
return (false);
if (mons_class_flag(monster->type, M_PRIEST))
{
switch (random2(3))
{
case 0:
simple_monster_message(monster, " prays.", spl);
break;
case 1:
simple_monster_message(monster,
" mumbles some strange prayers.",
spl);
break;
case 2:
default:
simple_monster_message(monster,
" utters an invocation.", spl);
break;
}
}
else // not a priest
{
switch (random2(3))
{
case 0:
// XXX: could be better, chosen to match the
// ones in monspeak.cc... has the problem
// that it doesn't suggest a vocal component. -- bwr
if (player_monster_visible(monster))
{
simple_monster_message(monster,
" gestures wildly.", spl );
}
break;
case 1:
simple_monster_message(monster,
" mumbles some strange words.",
spl);
break;
case 2:
default:
simple_monster_message(monster, " casts a spell.", spl);
break;
}
}
break;
case MONS_BALL_LIGHTNING:
monster->hit_points = -1;
break;
case MONS_STEAM_DRAGON:
case MONS_MOTTLED_DRAGON:
case MONS_STORM_DRAGON:
case MONS_GOLDEN_DRAGON:
case MONS_SHADOW_DRAGON:
case MONS_SWAMP_DRAGON:
case MONS_SWAMP_DRAKE:
case MONS_DEATH_DRAKE:
case MONS_HELL_HOG:
case MONS_SERPENT_OF_HELL:
case MONS_QUICKSILVER_DRAGON:
case MONS_IRON_DRAGON:
if (!simple_monster_message(monster, " breathes.", spl))
if (!silenced(monster->pos())
&& !silenced(you.pos()))
break;
case MONS_VAPOUR:
monster->add_ench(ENCH_SUBMERGED);
break;
case MONS_BRAIN_WORM:
case MONS_ELECTRIC_GOLEM:
case MONS_ICE_STATUE:
// These don't show any signs that they're casting a spell.
break;
case MONS_GREAT_ORB_OF_EYES:
case MONS_SHINING_EYE:
case MONS_EYE_OF_DEVASTATION:
simple_monster_message(monster, " gazes.", spl);
break;
case MONS_GIANT_ORANGE_BRAIN:
simple_monster_message(monster, " pulsates.", spl);
break;
case MONS_NAGA:
case MONS_NAGA_WARRIOR:
simple_monster_message(monster, " spits poison.", spl);
break;
case MONS_ORB_OF_FIRE:
simple_monster_message(monster, _orb_of_fire_glow(), spl);
break;
}
}
else
{
// Handle far-away monsters.
if (monster->type == MONS_GERYON
&& !silenced(you.pos()))
{
mpr("You hear a weird and mournful sound.", MSGCH_SOUND);
// Geryon can't summon beasts if he's silenced since he uses a horn,
// and we have to check for him explicitly since he's neither a priest
// nor a wizard.
if (silenced(monster->pos())
&& (monster->type == MONS_GERYON
|| mons_class_flag(monster->type, M_PRIEST | M_ACTUAL_SPELLS)))
{
return (false);
}
if (!_mons_announce_cast(monster, monsterNearby,
spell_cast, draco_breath))
{
return (false);
}
const bool did_noise =
_mons_announce_cast(monster, monsterNearby,
spell_cast, draco_breath);
#####################################################
# Individual spells.
#####################################################
%%%%
Poison Splash cast
@The_monster@ spits poison.
%%%%
Poison Splash cast unseen
You hear a spitting sound.
%%%%
Symbol of Torment cast
@The_monster@ calls on the powers of Hell!
%%%%
Symbol of Torment cast unseen
@The_something@ calls on the powers of Hell!
#####################################################
# Priest and wizard casting messages.
#####################################################
%%%%
priest cast
@The_monster@ prays.
@The_monster@ mumbles some strange prayers.
@The_monster@ utters an invocation.
%%%%
priest cast unseen
You hear some strange, mumbled prayers.
%%%%
wizard cast
@The_monster@ gestures wildly.
@The_monster@ mumbles some strange words.
@The_monster@ casts a spell.
%%%%
wizard cast unseen
You hear some strange, mumbled words.
########################################################################
# Monster genus and species messages.
########################################################################
%%%%
demon cast
@The_monster@ gestures.
%%%%
dragon cast
@The_monster@ breaths.
%%%%
dragon cast unseen
You hear a roar.
%%%%
curse skull cast
@The_monster@ rattles @possessive@ jaw.
@The_monster@ casts a spell.
%%%%
curse skull cast unseen
You hear the clattering of teeth.
%%%%
giant eyeball cast
@The_monster@ gazes.
########################################################################
# Individual monsters
########################################################################
%%%%
Geryon cast
@The_monster@ winds a great silver horn.
%%%%
Geryon cast unseen
You hear a weird and mournful sound.
%%%%
giant orange brain cast
@The_monster@ pulsates.
%%%%
orb of fire cast
@The_monster@ glows yellow.
@The_monster@ glows bright magenta.
@The_monster@ glows deep purple.
@The_monster@ glows red.
@The_monster@ emits a lurid red light.
%%%%