and mermaids. The latter are more interesting. ;)
Whenever a mermaid sings there's a chance you get beheld, meaning you cannot move away from the mermaid, that is cannot increase the distance between you and her. (Because of the distance pecularities this means at dist 1 you can only be NSW or E of the mermaid but not the diagonal directions.) If you manage to be beheld by several mermaids at the same time, your movements have to respect the distance to all of them.
I've added a vector beheld_by that keeps track of beholding monsters and makes checking distance and updating beholding status easier.
Merfolk are immune to the mermaid song.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2453 c06c8d41-db1a-0410-9941-cceddc491573
LJK4ZQATLSB4MKZG3ARZX5V6RFGTN3NLCN6GTCUGJQKU26SOXMUAC
VSIPMW33QR6LF34ZLWDH5WAIM6JYBH6PODMNSRW7EHEXEDESJPQAC
7V4DCKFJCNBXFODMKJ6H3MCDUTSD4XVQ7D4D6XFCD4JNF4HCE4KAC
4JJ6TWOZOPXBIOEQA2CXASS2TO5AMNXNY7LG67NXIVLDVJTJW6KAC
3BJ2OOF4F524G6UKVGOZVT6W3FSTSHHTKRJADUBZCHDXZWV3KANQC
22QSFI3GQEJGETOKQHXI6JHR5VQV7GUC76VCLZCFYECSTSUIDRBQC
RCLZVH5IZLUUJU3XDIW4GUDJ5G73DOI3EEMRKNMCNPSSEDBU5YFAC
OSGS3PH2L5CBTDVZCZS6OCFQNA4A7RMEXBYJQB7DDZBYYJW7QSSAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
ZJLJGSB2XSBQU42OFQMXL3EG4CXAQGOYAU6YTV2SAWZEJIPFH2CAC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC
GBUB77EAYHOFY6GQ5IY3ZSBC7FSQFZZKYNBD5QMCQFIKFLYLWHOQC
IPQ63XIUPHFMCQOZZAVSGCJOZFDRDWZTUUJSAUMARNDUFLBEMYIAC
TZ55IZNANEJO2WDTKYWVLY2W2VV6BR7WKIN7XLNISAMMFT6LG2WQC
DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC
SFT2ECCWJJWIMK7V3POVHXECNQIGTIOPA7BEIKHBLWLJMUJ4FFTAC
X76YXE6RFL7QY5WL6MSSS44WHVA5FQMDQY3XTYP2UL6UZB4E6XMQC
TV3ZC6WOZKSQQJQN26JIVKCHK6UK7WMDBYZDUYRWEAZ4JB4YVNAAC
BIZDHHK5LIO57S5AKHEPJFLWV5DAFKZIKYBGOUNGICSWTX7DCXKAC
I2B33Z7NZGC33AMDSSK446AZZYWKPHWLAGULVHKKZU4MVB4BNJOAC
QYQKV4R47PTERXVFQNNWWQVICGSOMBHW6WM5TAZAKLIYOLLPUAJAC
LKLHWERPXMDTRNYPEXVDVB6PE7CT5PZ6WMNJJDTBBE5FBHOCEK5AC
ILOED4VB4I6VPAUTR75ZWX6MXDYXB5DO2EDK2UH67O3HNKWV23RQC
}
// returns true if player is beheld by a given monster
bool player_beheld_by( const monsters *mon )
{
if (!you.duration[DUR_BEHELD])
return false;
// can this monster even behold you?
if (mon->type != MONS_MERMAID)
return false;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "beheld_by.size: %d, DUR_BEHELD: %d, current mon: %d",
you.beheld_by.size(), you.duration[DUR_BEHELD],
monster_index(mon));
#endif
if (you.beheld_by.empty()) // shouldn't happen
{
you.duration[DUR_BEHELD] = 0;
return false;
}
for (unsigned int i = 0; i < you.beheld_by.size(); i++)
{
unsigned int which_mon = you.beheld_by[i];
if (monster_index(mon) == which_mon)
return true;
}
return false;
}
// removes a monster from the list of beholders
// if force == true (e.g. monster dead) or one of
// several cases is met
void update_beholders(const monsters *mon, bool force)
{
if (!player_beheld_by(mon)) // not in list?
return;
// is an update even necessary?
if (force || !mons_near(mon) || mons_friendly(mon) || mon->submerged()
|| mon->has_ench(ENCH_CONFUSION) || mons_is_paralysed(mon) || mon->asleep()
|| silenced(you.x_pos, you.y_pos) || silenced(mon->x, mon->y))
{
const std::vector<int> help = you.beheld_by;
you.beheld_by.clear();
for (unsigned int i = 0; i < help.size(); i++)
{
unsigned int which_mon = help[i];
if (monster_index(mon) != which_mon)
you.beheld_by.push_back(i);
}
if (you.beheld_by.empty())
{
mpr("You are no longer entranced.", MSGCH_RECOVERY);
you.duration[DUR_BEHELD] = 0;
}
}
case MONS_MERMAID:
{
// won't sing if either of you silenced, or it's friendly or confused
if (monster->has_ench(ENCH_CONFUSION) || mons_friendly(monster)
|| silenced(monster->x, monster->y) || silenced(you.x_pos, you.y_pos))
{
break;
}
// reduce probability because of spamminess
if (you.species == SP_MERFOLK && !one_chance_in(4))
break;
// a wounded invisible mermaid is less likely to give away her position
if (monster->invisible()
&& monster->hit_points <= monster->max_hit_points / 2
&& !one_chance_in(3))
{
break;
}
bool already_beheld = player_beheld_by(monster);
if (one_chance_in(5)
|| monster->foe == MHITYOU && !already_beheld && coinflip())
{
if (player_monster_visible(monster))
{
simple_monster_message(monster,
make_stringf(" chants %s song.",
already_beheld? "her luring" : "a haunting").c_str(),
MSGCH_MONSTER_SPELL);
}
else
{
// if you're already beheld by an invisible mermaid she can
// still prolong the enchantment; otherwise you "resist"
if (already_beheld)
mpr("You hear a luring song.", MSGCH_SOUND);
else
{
if (one_chance_in(4)) // reduce spamminess
{
if (coinflip())
mpr("You hear a haunting song.", MSGCH_SOUND);
else
mpr("You hear an eerie melody.", MSGCH_SOUND);
canned_msg(MSG_YOU_RESIST); // flavour only
}
break;
}
}
// once beheld by a particular monster, cannot resist anymore
if (!already_beheld
&& (you.species == SP_MERFOLK || you_resist_magic(100)))
{
canned_msg(MSG_YOU_RESIST);
break;
}
if (!you.duration[DUR_BEHELD])
{
you.duration[DUR_BEHELD] = 7;
you.beheld_by.push_back(monster_index(monster));
mpr("You are beheld!", MSGCH_WARN);
}
else
{
you.duration[DUR_BEHELD] += 5;
if (!already_beheld)
you.beheld_by.push_back(monster_index(monster));
}
used = true;
if (you.duration[DUR_BEHELD] > 12)
you.duration[DUR_BEHELD] = 12;
}
break;
}
{
MONS_MERFOLK, 'm', LIGHTBLUE, "merfolk fighter",
M_HUMANOID | M_WARM_BLOOD | M_AMPHIBIOUS,
MR_RES_POISON | MR_RES_COLD,
500, 10, MONS_MERFOLK, MONS_MERFOLK, MH_NATURAL, -3,
{ {AT_HIT, AF_PLAIN, 14}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
{ 8, 2, 4, 0 },
4, 12, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH,
MONUSE_WEAPONS_ARMOUR, SIZE_MEDIUM
}
,
{
MONS_MERMAID, 'm', LIGHTCYAN, "mermaid",
M_SPELLCASTER | M_HUMANOID | M_WARM_BLOOD | M_AMPHIBIOUS,
MR_RES_POISON | MR_RES_COLD,
500, 10, MONS_MERMAID, MONS_MERMAID, MH_NATURAL, -5,
{ {AT_HIT, AF_PLAIN, 10}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
{ 8, 2, 4, 0 },
4, 12, MST_NO_SPELLS, CE_CONTAMINATED, Z_SMALL, S_SHOUT, I_HIGH,
MONUSE_WEAPONS_ARMOUR, SIZE_MEDIUM
}
,
}
break;
case MONS_MERMAID:
if (one_chance_in(3))
{
item_race = MAKE_ITEM_NO_RACE;
item.base_type = OBJ_WEAPONS;
item.sub_type = WPN_SPEAR;
}
break;
case MONS_MERFOLK:
item_race = MAKE_ITEM_NO_RACE;
item.base_type = OBJ_WEAPONS;
// 1/3 each for trident, spears and javelins
if (one_chance_in(3))
item.sub_type = WPN_TRIDENT;
else if (coinflip())
{
item.sub_type = WPN_SPEAR;
iquan = 1 + random2(3);
}
else
{
item.base_type = OBJ_MISSILES;
item.sub_type = MI_JAVELIN;
iquan = 3 + random2(6);
const int targ_x = you.x_pos + move_x;
const int targ_y = you.y_pos + move_y;
const dungeon_feature_type targ_grid = grd[ targ_x ][ targ_y ];
const unsigned short targ_monst = mgrd[ targ_x ][ targ_y ];
const bool targ_solid = grid_is_solid(targ_grid);
// cannot move away from mermaid but you CAN fight neighbouring squares
if (you.duration[DUR_BEHELD] && !you.duration[DUR_CONF]
&& (targ_monst == NON_MONSTER || mons_is_submerged(&menv[targ_monst])))
{
for (unsigned int i = 0; i < you.beheld_by.size(); i++)
{
coord_def pos = menv[you.beheld_by[i]].pos();
int olddist = distance(you.x_pos, you.y_pos, pos.x, pos.y);
int newdist = distance(you.x_pos + move_x, you.y_pos + move_y, pos.x, pos.y);
if (olddist < newdist)
{
mprf("You cannot move away from %s!",
(menv[you.beheld_by[i]]).name(DESC_NOCAP_THE, true).c_str());
move_x = 0;
move_y = 0;
return;
}
}
} // end of beholding check
const int targ_x = you.x_pos + move_x;
const int targ_y = you.y_pos + move_y;
const dungeon_feature_type targ_grid = grd[ targ_x ][ targ_y ];
const unsigned short targ_monst = mgrd[ targ_x ][ targ_y ];
const bool targ_solid = grid_is_solid(targ_grid);