summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source
diff options
context:
space:
mode:
authorj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2007-10-13 13:50:13 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2007-10-13 13:50:13 +0000
commit6b3f400a54d573521f844f34ad8a8b223c57c968 (patch)
tree2e916ac4de6cabc5b52ab5dcc2e426a2a65ecd7d /crawl-ref/source
parent884fcefad29a596466ac963ab0aedfc902185960 (diff)
downloadcrawl-ref-6b3f400a54d573521f844f34ad8a8b223c57c968.tar.gz
crawl-ref-6b3f400a54d573521f844f34ad8a8b223c57c968.zip
Implementing merfolk monsters. There are two types, merfolk fighters,
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
Diffstat (limited to 'crawl-ref/source')
-rw-r--r--crawl-ref/source/acr.cc46
-rw-r--r--crawl-ref/source/direct.cc3
-rw-r--r--crawl-ref/source/dungeon.cc7
-rw-r--r--crawl-ref/source/enum.h3
-rw-r--r--crawl-ref/source/externs.h4
-rw-r--r--crawl-ref/source/makeitem.cc32
-rw-r--r--crawl-ref/source/misc.cc7
-rw-r--r--crawl-ref/source/mon-data.h24
-rw-r--r--crawl-ref/source/mon-pick.cc14
-rw-r--r--crawl-ref/source/mon-util.cc6
-rw-r--r--crawl-ref/source/monplace.cc6
-rw-r--r--crawl-ref/source/monstuff.cc107
-rw-r--r--crawl-ref/source/output.cc6
-rw-r--r--crawl-ref/source/player.cc68
-rw-r--r--crawl-ref/source/player.h2
-rw-r--r--crawl-ref/source/spells4.cc7
-rw-r--r--crawl-ref/source/tags.cc10
-rw-r--r--crawl-ref/source/view.cc9
18 files changed, 349 insertions, 12 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 636a86d41d..09f70c8742 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -648,6 +648,12 @@ static void handle_wizard_command( void )
case 'H':
you.rotting = 0;
you.duration[DUR_POISONING] = 0;
+ if (you.duration[DUR_BEHELD])
+ {
+ you.duration[DUR_BEHELD] = 0;
+ you.beheld_by.clear();
+ }
+ you.duration[DUR_CONF] = 0;
you.disease = 0;
inc_hp( 10, true );
set_hp( you.hp_max, false );
@@ -2458,6 +2464,12 @@ static void decrement_durations()
decrement_a_duration( DUR_SURE_BLADE,
"The bond with your blade fades away." );
+ if ( decrement_a_duration( DUR_BEHELD, "You break out of your daze.",
+ -1, 0, NULL, MSGCH_RECOVERY ))
+ {
+ you.beheld_by.clear();
+ }
+
dec_slow_player();
dec_haste_player();
@@ -3602,6 +3614,34 @@ static void move_player(int move_x, int move_y)
}
} // end of if you.duration[DUR_CONF]
+ 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
+
if (you.running.check_stop_running())
{
move_x = 0;
@@ -3611,12 +3651,6 @@ static void move_player(int move_x, int move_y)
return;
}
- 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);
-
if (targ_monst != NON_MONSTER && !mons_is_submerged(&menv[targ_monst]))
{
struct monsters *mon = &menv[targ_monst];
diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc
index 5c98b25623..4315930792 100644
--- a/crawl-ref/source/direct.cc
+++ b/crawl-ref/source/direct.cc
@@ -1844,6 +1844,9 @@ static void describe_monster(const monsters *mon)
text += ".";
print_formatted_paragraph(text, get_number_of_cols());
+ if (player_beheld_by(mon))
+ mpr("You are beheld by her song.", MSGCH_EXAMINE);
+
if (mon->type == MONS_HYDRA)
{
mprf(MSGCH_EXAMINE, "It has %d head%s.", mon->number,
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 14233e9b1d..cc9c4f4f46 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -2546,6 +2546,13 @@ static void place_aquatic_monsters(int level_number, char level_type)
swimming_things[i] = MONS_BIG_FISH + random2(4);
if (player_in_branch( BRANCH_SWAMP ) && !one_chance_in(3))
swimming_things[i] = MONS_SWAMP_WORM;
+ else if (player_in_branch( BRANCH_SHOALS ))
+ {
+ if (one_chance_in(3))
+ swimming_things[i] = MONS_MERFOLK;
+ else if (one_chance_in(5))
+ swimming_things[i] = MONS_MERMAID;
+ }
}
if (level_number >= 25 && one_chance_in(5))
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 01048e2340..b2895d7eaa 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -983,6 +983,7 @@ enum duration_type
DUR_CONF,
DUR_PARALYSIS,
DUR_SLOW,
+ DUR_BEHELD,
DUR_HASTE,
DUR_MIGHT,
DUR_LEVITATION,
@@ -1557,6 +1558,8 @@ enum monster_type // (int) menv[].type
MONS_BLACK_BEAR, // 189
MONS_SIMULACRUM_SMALL,
MONS_SIMULACRUM_LARGE,
+ MONS_MERFOLK,
+ MONS_MERMAID, // 193
//jmf: end new monsters
MONS_WHITE_IMP = 220, // 220
MONS_LEMURE,
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index cd145c41be..ade001aaee 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -493,11 +493,13 @@ class player : public actor
{
public:
bool turn_is_over; // flag signaling that player has performed a timed action
-
+
// If true, player is headed to the Abyss.
bool banished;
std::string banished_by;
+ std::vector<int> beheld_by; // monsters beholding player
+
bool just_autoprayed; // autopray just kicked in
unsigned char prev_targ;
diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc
index d251b071e7..75e6e33508 100644
--- a/crawl-ref/source/makeitem.cc
+++ b/crawl-ref/source/makeitem.cc
@@ -3420,6 +3420,34 @@ static item_make_species_type give_weapon(monsters *mon, int level,
}
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);
+ }
+ break;
+
case MONS_CENTAUR:
case MONS_CENTAUR_WARRIOR:
item_race = MAKE_ITEM_NO_RACE;
@@ -3672,6 +3700,9 @@ static item_make_species_type give_weapon(monsters *mon, int level,
return (item_race);
}
+ if (iquan > 1 && !force_item)
+ mitm[thing_created].quantity = iquan;
+
give_monster_item(mon, thing_created, force_item);
if (give_aux_melee && (i.base_type != OBJ_WEAPONS || is_range_weapon(i)))
@@ -3751,6 +3782,7 @@ static void give_ammo(monsters *mon, int level,
case MONS_DRACONIAN_KNIGHT:
case MONS_GNOLL:
case MONS_HILL_GIANT:
+ case MONS_MERFOLK:
if (!one_chance_in(20))
break;
// fall through
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 8fa092b0a0..8351616c1c 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -1243,6 +1243,13 @@ void down_stairs( int old_level, dungeon_feature_type force_stair,
new_level();
+ // clear list of beholding monsters
+ if (you.duration[DUR_BEHELD])
+ {
+ you.beheld_by.clear();
+ you.duration[DUR_BEHELD] = 0;
+ }
+
viewwindow(1, true);
if (you.skills[SK_TRANSLOCATIONS] > 0 && !allow_control_teleport( true ))
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index cd585b36b9..b021392e7d 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -3985,6 +3985,30 @@
},
{
+ 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
+}
+,
+
+{
MONS_GIANT_NEWT, 'l', LIGHTGREEN, "giant newt",
M_COLD_BLOOD | M_AMPHIBIOUS,
MR_NO_FLAGS,
diff --git a/crawl-ref/source/mon-pick.cc b/crawl-ref/source/mon-pick.cc
index cf4f03fcd4..d7989c10c2 100644
--- a/crawl-ref/source/mon-pick.cc
+++ b/crawl-ref/source/mon-pick.cc
@@ -2029,6 +2029,8 @@ int mons_shoals_level(int mcls)
case MONS_GIANT_BAT:
break;
+ case MONS_MERFOLK:
+ case MONS_MERMAID:
case MONS_CENTAUR:
case MONS_ETTIN:
case MONS_SHEEP:
@@ -2069,8 +2071,12 @@ int mons_shoals_rare(int mcls)
case MONS_ETTIN:
case MONS_SHEEP:
+ case MONS_MERFOLK:
return 50;
+ case MONS_MERMAID:
+ return 40;
+
case MONS_HIPPOGRIFF:
case MONS_GIANT_BAT:
case MONS_BUTTERFLY:
@@ -2112,6 +2118,8 @@ int mons_swamp_level(int mcls)
case MONS_SWAMP_DRAKE:
case MONS_WORM:
case MONS_SWAMP_WORM:
+ case MONS_MERFOLK:
+ case MONS_MERMAID:
mlev++;
break;
@@ -2189,6 +2197,7 @@ int mons_swamp_rare(int mcls)
return 61;
case MONS_SLIME_CREATURE:
+ case MONS_MERFOLK:
return 54;
case MONS_SNAKE:
@@ -2216,6 +2225,7 @@ int mons_swamp_rare(int mcls)
case MONS_KOMODO_DRAGON:
case MONS_VERY_UGLY_THING:
case MONS_VAPOUR:
+ case MONS_MERMAID:
return 15;
case MONS_PHANTOM:
@@ -2669,6 +2679,8 @@ int mons_standard_level(int mcls)
case MONS_DEEP_ELF_MASTER_ARCHER:
return 33;
+ case MONS_MERFOLK:
+ case MONS_MERMAID:
case MONS_BIG_FISH:
case MONS_ELECTRICAL_EEL:
case MONS_GIANT_GOLDFISH:
@@ -2691,6 +2703,8 @@ int mons_standard_rare(int mcls)
{
// "another lava thing" has no stats! (GDL)
// case MONS_ANOTHER_LAVA_THING:
+ case MONS_MERFOLK:
+ case MONS_MERMAID:
case MONS_BIG_FISH:
case MONS_ELECTRICAL_EEL:
case MONS_GIANT_GOLDFISH:
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 764de91b12..8428a4f0a7 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -488,6 +488,11 @@ bool mons_is_humanoid( int mc )
case 'T': // trolls
return (true);
+ case 'm': // merfolk
+ if (mc == MONS_MERFOLK || mc == MONS_MERMAID)
+ return (true);
+ return (false);
+
case 'g': // goblines, hobgoblins, gnolls, boggarts -- but not gargoyles
if (mc != MONS_GARGOYLE
&& mc != MONS_METAL_GARGOYLE
@@ -2169,6 +2174,7 @@ const char *mons_pronoun(monster_type mon_type, pronoun_type variant)
case MONS_EROLCHA:
case MONS_ERICA:
case MONS_TIAMAT:
+ case MONS_MERMAID:
gender = GENDER_FEMALE;
break;
default:
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 3327b51253..067b825e38 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -104,7 +104,9 @@ bool monster_habitable_grid(int monster_class, int actual_grid, int flies,
// And water elementals are native to the water but happy on land
// as well.
- || (monster_class == MONS_WATER_ELEMENTAL
+ || ((monster_class == MONS_WATER_ELEMENTAL
+ || monster_class == MONS_MERFOLK
+ || monster_class == MONS_MERMAID)
&& grid_compatible(DNGN_FLOOR, actual_grid)));
}
@@ -113,6 +115,8 @@ bool monster_can_submerge(int monster_class, int grid)
{
switch (monster_class)
{
+ case MONS_MERFOLK:
+ case MONS_MERMAID:
case MONS_BIG_FISH:
case MONS_GIANT_GOLDFISH:
case MONS_ELECTRICAL_EEL:
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 322cc9163c..b3a2eb4995 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -422,6 +422,8 @@ void monster_die(monsters *monster, killer_type killer, int i, bool silent)
if (net != NON_ITEM)
remove_item_stationary(mitm[net]);
}
+ // update list of monsters beholding player
+ update_beholders(monster, true);
const int monster_killed = monster_index(monster);
const bool death_message =
@@ -2321,6 +2323,8 @@ static void handle_nearby_ability(monsters *monster)
case MONS_LAVA_FISH:
case MONS_LAVA_SNAKE:
case MONS_SALAMANDER:
+ case MONS_MERFOLK:
+ case MONS_MERMAID:
case MONS_BIG_FISH:
case MONS_GIANT_GOLDFISH:
case MONS_ELECTRICAL_EEL:
@@ -2328,18 +2332,22 @@ static void handle_nearby_ability(monsters *monster)
case MONS_WATER_ELEMENTAL:
case MONS_SWAMP_WORM:
if (monster_can_submerge(monster->type, grd[monster->x][monster->y])
- && (one_chance_in(5) || (grid_distance( monster->x, monster->y,
- you.x_pos, you.y_pos ) > 1
+ && ( !player_beheld_by(monster) // no submerging if player entranced
+ && (one_chance_in(5) || (grid_distance( monster->x, monster->y,
+ you.x_pos, you.y_pos ) > 1
// FIXME This is better expressed as a
// function such as
// monster_has_ranged_attack:
&& monster->type != MONS_ELECTRICAL_EEL
&& monster->type != MONS_LAVA_SNAKE
- && !one_chance_in(20))
+ && (monster->type != MONS_MERMAID
+ || you.species == SP_MERFOLK)
+ && !one_chance_in(20)) )
|| monster->hit_points <= monster->max_hit_points / 2)
|| env.cgrid[monster->x][monster->y] != EMPTY_CLOUD)
{
monster->add_ench(ENCH_SUBMERGED);
+ update_beholders(monster);
}
break;
@@ -2668,6 +2676,87 @@ static bool handle_special_ability(monsters *monster, bolt & beem)
}
break;
+ 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;
+ }
default:
break;
}
@@ -4241,6 +4330,8 @@ static void handle_monster_move(int i, monsters *monster)
if (!monster_move(monster))
monster->speed_increment -= non_move_energy;
}
+ update_beholders(monster);
+
// reevaluate behaviour, since the monster's
// surroundings have changed (it may have moved,
// or died for that matter. Don't bother for
@@ -4306,7 +4397,15 @@ void handle_monsters(void)
// If the player got banished, discard pending monster actions.
if (you.banished)
+ {
+ // clear list of beholding monsters
+ if (you.duration[DUR_BEHELD])
+ {
+ you.beheld_by.clear();
+ you.duration[DUR_BEHELD] = 0;
+ }
break;
+ }
} // end of for loop
// Clear any summoning flags so that lower indiced
@@ -5493,6 +5592,8 @@ dungeon_feature_type monster_habitat(int which_class)
{
switch (which_class)
{
+ case MONS_MERFOLK:
+ case MONS_MERMAID:
case MONS_BIG_FISH:
case MONS_GIANT_GOLDFISH:
case MONS_ELECTRICAL_EEL:
diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc
index a6aac29f88..27b9a16a17 100644
--- a/crawl-ref/source/output.cc
+++ b/crawl-ref/source/output.cc
@@ -541,6 +541,12 @@ void print_stats(void)
textcolor( RED ); // no different levels
cprintf( "Conf " );
}
+
+ if (you.duration[DUR_BEHELD])
+ {
+ textcolor( RED ); // no different levels
+ cprintf( "Bhld " );
+ }
if (you.duration[DUR_LIQUID_FLAMES])
{
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index 674e2df54c..02266afb2a 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -2324,7 +2324,7 @@ int player_see_invis(bool calc_unid)
// This does NOT do line of sight! It checks the monster's visibility
// with repect to the players perception, but doesn't do walls or range...
-// to find if the square the monster is in is visible see mons_near().
+// to find if the square the monster is in los see mons_near().
bool player_monster_visible( const monsters *mon )
{
if (mon->has_ench(ENCH_SUBMERGED)
@@ -2336,6 +2336,69 @@ bool player_monster_visible( const monsters *mon )
return (true);
}
+// 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;
+ }
+ }
+}
+
int player_sust_abil(bool calc_unid)
{
int sa = 0;
@@ -3362,6 +3425,9 @@ void display_char_status()
if (you.duration[DUR_CONF])
mpr( "You are confused." );
+ if (you.duration[DUR_BEHELD])
+ mpr( "You are beheld." );
+
if (you.duration[DUR_PARALYSIS])
mpr( "You are paralysed." );
diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h
index 390ce4e505..95e35193f9 100644
--- a/crawl-ref/source/player.h
+++ b/crawl-ref/source/player.h
@@ -263,6 +263,8 @@ int slaying_bonus(char which_affected);
int player_see_invis(bool calc_unid = true);
bool player_monster_visible( const monsters *mon );
+bool player_beheld_by( const monsters *mon );
+void update_beholders( const monsters *mon, bool force = false);
/* ***********************************************************************
* called from: acr - decks - it_use2 - ouch
diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc
index f7dde163dc..2358498568 100644
--- a/crawl-ref/source/spells4.cc
+++ b/crawl-ref/source/spells4.cc
@@ -1232,6 +1232,13 @@ void cast_silence(int pow)
if (you.duration[DUR_SILENCE] > 100)
you.duration[DUR_SILENCE] = 100;
+
+ if (you.duration[DUR_BEHELD])
+ {
+ mpr("You break out of your daze!", MSGCH_RECOVERY);
+ you.duration[DUR_BEHELD] = 0;
+ you.beheld_by.clear();
+ }
} // end cast_silence()
diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc
index e4631d3f6d..818da4051c 100644
--- a/crawl-ref/source/tags.cc
+++ b/crawl-ref/source/tags.cc
@@ -928,6 +928,11 @@ static void tag_construct_you(struct tagHeader &th)
marshallShort(th, you.transit_stair);
marshallByte(th, you.entering_level);
+
+ // list of currently beholding monsters (usually empty)
+ marshallByte(th, you.beheld_by.size());
+ for (unsigned int k = 0; k < you.beheld_by.size(); k++)
+ marshallByte(th, you.beheld_by[k]);
}
static void tag_construct_you_items(struct tagHeader &th)
@@ -1263,6 +1268,11 @@ static void tag_read_you(struct tagHeader &th, char minorVersion)
you.transit_stair = static_cast<dungeon_feature_type>(unmarshallShort(th));
you.entering_level = unmarshallByte(th);
+
+ // list of currently beholding monsters (usually empty)
+ count_c = unmarshallByte(th);
+ for (i = 0; i < count_c; i++)
+ you.beheld_by.push_back(unmarshallByte(th));
}
static void tag_read_you_items(struct tagHeader &th, char minorVersion)
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index 80612fda71..c7018aa032 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -1359,6 +1359,15 @@ bool noisy( int loudness, int nois_x, int nois_y, const char *msg )
you.check_awaken(dist - player_distance);
+ if (loudness >= 20 && you.duration[DUR_BEHELD])
+ {
+ mprf("For a moment, you cannot hear the mermaid%s!",
+ you.beheld_by.size() == 1? "" : "s");
+ mpr("You break out of your daze!", MSGCH_DURATION);
+ you.duration[DUR_BEHELD] = 0;
+ you.beheld_by.clear();
+ }
+
ret = true;
}