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-08-20 12:23:38 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2007-08-20 12:23:38 +0000
commit5b618acbca35621d699dc4ba3064f2a3228a5131 (patch)
treed5a68990291723d0252b29dd9193bce34929f50b /crawl-ref/source
parent7ca7077ed3e60d71c79c22e07fd0d2491b861ca3 (diff)
downloadcrawl-ref-5b618acbca35621d699dc4ba3064f2a3228a5131.tar.gz
crawl-ref-5b618acbca35621d699dc4ba3064f2a3228a5131.zip
Added throwing nets. These still need work, and thus
are not for 0.3. Obviously. Summary: New item type MI_THROWING_NET. The enchantment of a net describes its state, i.e. whether it's brand-new or almost falling apart (happens at -8). New attribute ATTR_CAUGHT (for monsters ENCH_CAUGHT) that means the victim cannot move and instead struggles against the net until it manages to wriggle out of it (takes a while depending on size) or it is destroyed. Monsters can still use items and spells when trapped. New trap type TRAP_NET that currently is the only source of throwing nets, though Gladiators (and some types of hunters maybe?) should start with a few, and David suggested also allowing the creation of nets for shops. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2020 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source')
-rw-r--r--crawl-ref/source/acr.cc32
-rw-r--r--crawl-ref/source/beam.cc13
-rw-r--r--crawl-ref/source/describe.cc25
-rw-r--r--crawl-ref/source/direct.cc5
-rw-r--r--crawl-ref/source/dungeon.cc3
-rw-r--r--crawl-ref/source/enum.h4
-rw-r--r--crawl-ref/source/externs.h5
-rw-r--r--crawl-ref/source/item_use.cc19
-rw-r--r--crawl-ref/source/itemprop.cc5
-rw-r--r--crawl-ref/source/items.cc13
-rw-r--r--crawl-ref/source/makeitem.cc6
-rw-r--r--crawl-ref/source/misc.cc225
-rw-r--r--crawl-ref/source/misc.h8
-rw-r--r--crawl-ref/source/mon-util.cc161
-rw-r--r--crawl-ref/source/mon-util.h2
-rw-r--r--crawl-ref/source/monspeak.cc83
-rw-r--r--crawl-ref/source/monstuff.cc312
-rw-r--r--crawl-ref/source/mstuff2.cc45
-rw-r--r--crawl-ref/source/player.cc5
-rw-r--r--crawl-ref/source/spells1.cc6
-rw-r--r--crawl-ref/source/spells4.cc11
-rw-r--r--crawl-ref/source/travel.cc3
22 files changed, 857 insertions, 134 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index aefa1b1d2b..306a9ab252 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -2572,6 +2572,14 @@ static void open_door(int move_x, int move_y, bool check_confused)
struct dist door_move;
int dx, dy; // door x, door y
+ if (you.attribute[ATTR_CAUGHT])
+ {
+ // struggles against net, damaging it
+ free_self_from_net(true);
+ you.turn_is_over = true;
+ return;
+ }
+
if (check_confused && you.duration[DUR_CONF] && !one_chance_in(3))
{
move_x = random2(3) - 1;
@@ -2591,6 +2599,22 @@ static void open_door(int move_x, int move_y, bool check_confused)
if (mon != NON_MONSTER && player_can_hit_monster(&menv[mon]))
{
+
+ if (mons_is_caught(&menv[mon]))
+ {
+
+ std::string prompt = "Do you want to try to take the net off ";
+ prompt += (&menv[mon])->name(DESC_NOCAP_THE);
+ prompt += '?';
+
+ if (yesno(prompt.c_str(), true, 'n'))
+ {
+ remove_net_from(&menv[mon]);
+ return;
+ }
+
+ }
+
you_attack(mgrd[dx][dy], true);
you.turn_is_over = true;
@@ -2932,6 +2956,14 @@ static void move_player(int move_x, int move_y)
bool moving = true; // used to prevent eventual movement (swap)
bool swap = false;
+ if (you.attribute[ATTR_CAUGHT])
+ {
+ // tries to escape from net (without damaging it, takes longer)
+ free_self_from_net(false);
+ you.turn_is_over = true;
+ return;
+ }
+
if (you.duration[DUR_CONF])
{
if (!one_chance_in(3))
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc
index df3c1dd027..6cc016e777 100644
--- a/crawl-ref/source/beam.cc
+++ b/crawl-ref/source/beam.cc
@@ -2389,6 +2389,13 @@ void beam_drop_object( bolt &beam, item_def *item, int x, int y )
return;
}
+ // doesn't get destroyed by throwing
+ if (item->sub_type == MI_THROWING_NET)
+ {
+ copy_item_to_grid( *item, x, y, 1 );
+ return;
+ }
+
if (YOU_KILL(beam.thrower)) // you threw it
{
int chance;
@@ -3342,6 +3349,9 @@ static int affect_player( bolt &beam )
poison_player( 1 + random2(3) );
}
}
+
+ if (beam.name.find("throwing net") != std::string::npos)
+ player_caught_in_net();
if (beam.name.find("curare") != std::string::npos)
{
@@ -3695,6 +3705,9 @@ static int affect_monster(bolt &beam, monsters *mon)
mprf(MSGCH_SOUND, "The %s hits something.", beam.name.c_str());
}
+ if (beam.name.find("throwing net") != std::string::npos)
+ monster_caught_in_net(mon);
+
// note that hurt_final was calculated above, so we don't need it again.
// just need to apply flavoured specials (since we called with
// doFlavouredEffects = false above)
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index b383f6cb99..704ec52e2e 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -334,7 +334,7 @@ static const char *trap_names[] =
{
"dart", "arrow", "spear", "axe",
"teleport", "amnesia", "blade",
- "bolt", "zot", "needle",
+ "bolt", "net", "zot", "needle",
};
const char *trap_name(trap_type trap)
@@ -1374,6 +1374,29 @@ static std::string describe_ammo( const item_def &item )
description += "Unfortunately, it is too long and awkward "
"for you to use.";
break;
+ case MI_THROWING_NET:
+ description += "A throwing net as used by gladiators. ";
+ if (!is_throwable(item, you.body_size()))
+ description += "Unfortunately, it is too large for you to throw. ";
+ if (item.plus < 0)
+ {
+ std::string how;
+ if (item.plus > -3)
+ how = "a little";
+ else if (item.plus > -5)
+ how = "somewhat";
+ else if (item.plus > -7)
+ how = "very";
+ else
+ how = "extremely";
+
+ description += "It looks ";
+ description += how;
+ description += " worn.";
+ }
+ else if (item.plus > 1)
+ description += "The net looks brand-new!";
+ break;
case MI_NONE: // was eggplant
description += "A purple vegetable. "
"The presence of this object in the game indicates a bug. ";
diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc
index 9389a0837b..b08f307a6b 100644
--- a/crawl-ref/source/direct.cc
+++ b/crawl-ref/source/direct.cc
@@ -1283,6 +1283,8 @@ std::string raw_feature_description(dungeon_feature_type grid,
return ("blade trap");
case TRAP_BOLT:
return ("bolt trap");
+ case TRAP_NET:
+ return ("net trap");
case TRAP_ZOT:
return ("Zot trap");
case TRAP_NEEDLE:
@@ -1559,6 +1561,9 @@ static void describe_mons_enchantment(const monsters &mons,
case ENCH_STICKY_FLAME:
msg += " is covered in liquid flames.";
break;
+ case ENCH_CAUGHT:
+ msg += " is entangled in a net.";
+ break;
default:
msg.clear();
break;
diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc
index 2bf5e3b0ea..725d833866 100644
--- a/crawl-ref/source/dungeon.cc
+++ b/crawl-ref/source/dungeon.cc
@@ -1851,6 +1851,9 @@ static trap_type random_trap_for_level(int level_number)
random2(1 + level_number) > 2
: one_chance_in(7))
type = TRAP_ARROW;
+
+ if ((type == TRAP_DART || type == TRAP_ARROW) && one_chance_in(15))
+ type = TRAP_NET;
if (random2(1 + level_number) > 7)
type = TRAP_BOLT;
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 6138e0bc0e..fe0ed78ae7 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -253,6 +253,7 @@ enum attribute_type
ATTR_WAS_SILENCED, //jmf: added for silenced messages
ATTR_GOD_GIFT_COUNT, //jmf: added to help manage god gift giving
ATTR_DELAYED_FIREBALL, // bwr: reserve fireballs
+ ATTR_CAUGHT, // caught in a net
NUM_ATTRIBUTES
};
@@ -1288,6 +1289,7 @@ enum enchant_type
ENCH_SICK,
ENCH_SLEEPY, // Monster can't wake until this wears off.
ENCH_FATIGUE, // Post-berserk fatigue.
+ ENCH_CAUGHT, // caught in a net
NUM_ENCHANTMENTS
};
@@ -1917,6 +1919,7 @@ enum missile_type
MI_LARGE_ROCK,
MI_SLING_BULLET,
MI_JAVELIN,
+ MI_THROWING_NET,
NUM_MISSILES,
MI_NONE // was MI_EGGPLANT... used for launch type detection
};
@@ -3763,6 +3766,7 @@ enum trap_type // env.trap_type[]
TRAP_AMNESIA, // 5
TRAP_BLADE,
TRAP_BOLT,
+ TRAP_NET,
TRAP_ZOT,
TRAP_NEEDLE,
NUM_TRAPS, // must remain last 'regular' member {dlb}
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 840d03f09c..5ddcd3fd6f 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -196,6 +196,7 @@ public:
virtual bool paralysed() const = 0;
virtual bool confused() const = 0;
+ virtual bool caught() const = 0;
virtual bool asleep() const { return (false); }
virtual bool backlit() const = 0;
@@ -203,7 +204,7 @@ public:
virtual bool incapacitated() const
{
- return paralysed() || confused();
+ return paralysed() || confused() || caught();
}
virtual int holy_aura() const
@@ -895,6 +896,7 @@ public:
bool paralysed() const;
bool confused() const;
+ bool caught() const;
bool backlit() const;
int armour_class() const;
@@ -1146,6 +1148,7 @@ public:
bool is_icy() const;
bool paralysed() const;
bool confused() const;
+ bool caught() const;
bool asleep() const;
bool backlit() const;
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 9b3515fa0f..e306963512 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -2046,6 +2046,21 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
// Javelins train throwing quickly.
exercise(SK_RANGED_COMBAT, 1 + coinflip());
break;
+ case MI_THROWING_NET:
+ // Nets use throwing and T&D skills
+ // They don't do any damage!
+ baseDam = 0;
+ exDamBonus = 0;
+
+ // but accuracy is important for this one
+ baseHit = 1;
+ exHitBonus += (skill_bump(SK_RANGED_COMBAT) * 7 / 2);
+ // Adjust for strength and dex.
+ exHitBonus = dex_adjust_thrown_tohit(exHitBonus);
+
+ // Nets train throwing
+ exercise(SK_RANGED_COMBAT, 1);
+ break;
}
}
@@ -2083,6 +2098,10 @@ bool throw_it(bolt &pbolt, int throw_2, bool teleport, int acc_bonus,
pbolt.rangeMax = pbolt.range;
}
+ else if (wepType == MI_THROWING_NET)
+ {
+ pbolt.rangeMax = pbolt.range = 2 + player_size(PSIZE_BODY);
+ }
else
{
pbolt.range = 12;
diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc
index 9e326a494f..c2fa3e1f9a 100644
--- a/crawl-ref/source/itemprop.cc
+++ b/crawl-ref/source/itemprop.cc
@@ -350,7 +350,8 @@ static missile_def Missile_prop[NUM_MISSILES] =
{ MI_BOLT, "bolt", 9, 5, false },
{ MI_LARGE_ROCK, "large rock", 20, 1000, true },
{ MI_SLING_BULLET, "sling bullet", 6, 4, false },
- { MI_JAVELIN, "javelin", 10, 40, true },
+ { MI_JAVELIN, "javelin", 10, 40, true },
+ { MI_THROWING_NET, "throwing net", 0, 30, true },
};
struct food_def
@@ -1765,6 +1766,8 @@ bool is_throwable( const item_def &wpn, size_type bodysize )
{
if (bodysize < SIZE_MEDIUM && wpn.sub_type == MI_JAVELIN)
return (false);
+ if (bodysize < SIZE_MEDIUM && wpn.sub_type == MI_THROWING_NET)
+ return (false);
return (Missile_prop[ Missile_index[wpn.sub_type] ].throwable);
}
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index 7b1f6f46eb..a33c1a41dc 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -547,7 +547,9 @@ static void item_list_on_square( std::vector<const item_def*>& items,
while ( obj != NON_ITEM ) {
/* add them to the items list if they qualify */
if ( !have_nonsquelched || !invisible_to_player(mitm[obj]) )
+ {
items.push_back( &mitm[obj] );
+ }
obj = mitm[obj].link;
}
}
@@ -1299,6 +1301,15 @@ int find_free_slot(const item_def &i)
// the player's inventory is full.
int move_item_to_player( int obj, int quant_got, bool quiet )
{
+ if (you.attribute[ATTR_CAUGHT] && mitm[obj].base_type == OBJ_MISSILES
+ && mitm[obj].sub_type == MI_THROWING_NET)
+ {
+ quant_got--;
+ mpr("You cannot pick up the net that traps you!");
+ if (!quant_got)
+ return (1);
+ }
+
int retval = quant_got;
// Gold has no mass, so we handle it first.
@@ -1340,7 +1351,7 @@ int move_item_to_player( int obj, int quant_got, bool quiet )
retval = part;
}
-
+
if (is_stackable_item( mitm[obj] ))
{
for (int m = 0; m < ENDOFPACK; m++)
diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc
index 380157719a..af5ec9787a 100644
--- a/crawl-ref/source/makeitem.cc
+++ b/crawl-ref/source/makeitem.cc
@@ -190,6 +190,9 @@ static int newwave_missile_colour(const item_def &item)
case MI_JAVELIN:
item_colour = RED;
break;
+ case MI_THROWING_NET:
+ item_colour = DARKGRAY;
+ break;
default:
// huh?
item_colour = LIGHTCYAN;
@@ -216,6 +219,9 @@ static int classic_missile_colour(const item_def &item)
case MI_NEEDLE:
item_colour = WHITE;
break;
+ case MI_THROWING_NET:
+ item_colour = DARKGRAY;
+ break;
default:
item_colour = LIGHTCYAN;
if (get_equip_race(item) == ISFLAG_DWARVEN)
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index d265432a02..c62e5db7f5 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -74,8 +74,6 @@
#include "view.h"
bool scramble(void);
-static bool trap_item(object_class_type base_type, char sub_type,
- char beam_x, char beam_y);
static void dart_trap(bool trap_known, int trapped, bolt &pbolt, bool poison);
// void place_chunks(int mcls, unsigned char rot_status, unsigned char chx,
@@ -814,6 +812,35 @@ void curare_hits_player(int agent, int degree)
}
}
+void monster_caught_in_net(monsters *mon)
+{
+ if (mon->body_size(PSIZE_BODY) >= SIZE_GIANT)
+ return;
+
+ if (mon->type == MONS_FIRE_VORTEX || mon->type == MONS_SPATIAL_VORTEX)
+ return;
+
+ if (!mons_is_caught(mon) && mon->add_ench(ENCH_CAUGHT))
+ {
+ if (mons_near(mon) && !player_monster_visible(mon))
+ mpr("Something gets caught in the net!");
+ else
+ simple_monster_message(mon, " is caught in the net!");
+ }
+}
+
+void player_caught_in_net()
+{
+ if (you.body_size(PSIZE_BODY) >= SIZE_GIANT)
+ return;
+
+ if (!you.attribute[ATTR_CAUGHT])
+ {
+ you.attribute[ATTR_CAUGHT] = 10;
+ mpr("You become entangled in the net!");
+ }
+}
+
void merfolk_start_swimming(void)
{
if (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE)
@@ -1573,6 +1600,10 @@ void itrap( struct bolt &pbolt, int trapped )
base_type = OBJ_MISSILES;
sub_type = MI_NEEDLE;
break;
+ case TRAP_NET:
+ base_type = OBJ_MISSILES;
+ sub_type = MI_THROWING_NET;
+ break;
default:
return;
}
@@ -1655,6 +1686,29 @@ void handle_traps(char trt, int i, bool trap_known)
}
break;
+ case TRAP_NET:
+
+ if (trap_known && one_chance_in(3))
+ mpr("A net swings high above you.");
+ else
+ {
+ if (random2limit(player_evasion(), 40)
+ + (random2(you.dex) / 3) + (trap_known ? 3 : 0) > 12)
+ {
+ mpr("A net drops to the ground!");
+ }
+ else
+ {
+ mpr("A large net falls onto you!");
+ player_caught_in_net();
+ }
+
+ trap_item( OBJ_MISSILES, MI_THROWING_NET, env.trap[i].x, env.trap[i].y );
+ grd[env.trap[i].x][env.trap[i].y] = DNGN_FLOOR;
+ env.trap[i].type = TRAP_UNASSIGNED;
+ }
+ break;
+
case TRAP_ZOT:
default:
mpr((trap_known) ? "You enter the Zot trap."
@@ -1707,7 +1761,17 @@ void disarm_trap( struct dist &disa )
exercise(SK_TRAPS_DOORS, 1 + random2(you.your_level / 5));
else
{
- handle_traps(env.trap[i].type, i, false);
+ if (env.trap[i].type == TRAP_NET &&
+ (env.trap[i].x != you.x_pos || env.trap[i].y != you.y_pos))
+ {
+ if (coinflip())
+ return;
+
+ mpr("You stumble into the trap!");
+ move_player_to_grid( env.trap[i].x, env.trap[i].y, true, false, true);
+ }
+ else
+ handle_traps(env.trap[i].type, i, false);
if (coinflip())
exercise(SK_TRAPS_DOORS, 1);
@@ -1723,7 +1787,9 @@ void disarm_trap( struct dist &disa )
beam.target_x = you.x_pos + disa.dx;
beam.target_y = you.y_pos + disa.dy;
- if (env.trap[i].type != TRAP_BLADE
+ if (env.trap[i].type == TRAP_NET)
+ trap_item( OBJ_MISSILES, MI_THROWING_NET, beam.target_x, beam.target_y );
+ else if (env.trap[i].type != TRAP_BLADE
&& trap_category(env.trap[i].type) == DNGN_TRAP_MECHANICAL)
{
const int num_to_make = 10 + random2(you.skills[SK_TRAPS_DOORS]);
@@ -1742,6 +1808,156 @@ void disarm_trap( struct dist &disa )
exercise(SK_TRAPS_DOORS, 1 + random2(5) + (you.your_level / 5));
} // end disarm_trap()
+// attempts to take a net off a given monster
+// Do not expect gratitude for this!
+// ----------------------------------
+void remove_net_from(monsters *mon)
+{
+ you.turn_is_over = true;
+
+ int net, next;
+
+ for (net = igrd[mon->x][mon->y]; net != NON_ITEM; net = next)
+ {
+ next = mitm[net].link;
+
+ if (mitm[net].base_type == OBJ_MISSILES
+ && mitm[net].sub_type == MI_THROWING_NET)
+ {
+ break;
+ }
+ }
+ if (net == NON_ITEM)
+ {
+ mon->del_ench(ENCH_CAUGHT, true);
+ return;
+ }
+
+ // factor in whether monster is paralysed or invisible
+ int paralys = 0;
+ if (mons_is_paralysed(mon)) // makes this easier
+ paralys = random2(5);
+
+ int invis = 0;
+ if (!player_monster_visible(mon)) // makes this harder
+ invis = 3 + random2(5);
+
+ bool net_destroyed = false;
+ if ( random2(you.skills[SK_TRAPS_DOORS] + 2) + paralys
+ <= random2( 2*mon->body_size(PSIZE_BODY) + 3 ) + invis)
+ {
+ if (one_chance_in(you.skills[SK_TRAPS_DOORS] + you.dex/2))
+ {
+ mitm[net].plus--;
+ mpr("You tear at the net.");
+ if (mitm[net].plus < -7)
+ {
+ mpr("Whoops! The net comes apart in your hands!");
+ net_destroyed = true;
+ dec_mitm_item_quantity( net, 1 );
+
+ mon->del_ench(ENCH_CAUGHT, true);
+
+ }
+ }
+
+ if (!net_destroyed)
+ {
+ if (player_monster_visible(mon))
+ {
+ mprf("You fail to remove the net from %s.",
+ mon->name(DESC_NOCAP_THE).c_str());
+ }
+ else
+ mpr("You fail to remove the net.");
+ }
+
+ if (random2(you.dex) > 5 + random2( 2*mon->body_size(PSIZE_BODY) ))
+ exercise(SK_TRAPS_DOORS, 1 + random2(mon->body_size(PSIZE_BODY)/2));
+ return;
+ }
+
+ mon->del_ench(ENCH_CAUGHT, true);
+ if (player_monster_visible(mon))
+ mprf("You free %s.", mon->name(DESC_NOCAP_THE).c_str());
+ else
+ mpr("You loosen the net.");
+
+}
+
+void free_self_from_net(bool damage_net)
+{
+ int net, next;
+
+ for (net = igrd[you.x_pos][you.y_pos]; net != NON_ITEM; net = next)
+ {
+ next = mitm[net].link;
+ if (mitm[net].base_type == OBJ_MISSILES
+ && mitm[net].sub_type == MI_THROWING_NET)
+ {
+ break;
+ }
+ }
+
+ if (net == NON_ITEM) // really shouldn't happen!
+ {
+ you.attribute[ATTR_CAUGHT] = 0;
+ return;
+ }
+ int hold = mitm[net].plus;
+
+ if (damage_net)
+ {
+ mpr("You struggle against the net.");
+ int damage = 1;
+
+ // extra damage for cutting weapons
+ if (you.equip[EQ_WEAPON] != -1
+ && can_cut_meat(you.inv[you.equip[EQ_WEAPON]]))
+ {
+ damage++;
+ }
+
+ if (you.body_size(PSIZE_BODY) > SIZE_MEDIUM)
+ damage++;
+
+ if (hold < 0 && !one_chance_in(-hold/2))
+ damage++;
+
+ if (you.duration[DUR_BERSERKER])
+ damage *= 2;
+
+ mitm[net].plus -= damage;
+
+ if (mitm[net].plus < -7)
+ {
+ mpr("You rip the net and break free!");
+ dec_mitm_item_quantity( net, 1 );
+
+ you.attribute[ATTR_CAUGHT] = 0;
+ return;
+ }
+ }
+ else // you try to escape
+ {
+ mpr("You struggle to escape from the net.");
+ you.attribute[ATTR_CAUGHT]--;
+
+ if (you.body_size(PSIZE_BODY) < SIZE_MEDIUM)
+ you.attribute[ATTR_CAUGHT]--;
+
+ if (hold < 0 && !one_chance_in(-hold/2))
+ you.attribute[ATTR_CAUGHT]--;
+
+ if (you.attribute[ATTR_CAUGHT] <= 0)
+ {
+ mpr("You break free from the net!");
+ you.attribute[ATTR_CAUGHT] = 0;
+ return;
+ }
+ }
+}
+
std::string weird_writing()
{
int temp_rand; // for probability determinations {dlb}
@@ -2056,6 +2272,7 @@ dungeon_feature_type trap_category(trap_type type)
case TRAP_BLADE:
case TRAP_BOLT:
case TRAP_NEEDLE:
+ case TRAP_NET:
default: // what *would* be the default? {dlb}
return (DNGN_TRAP_MECHANICAL);
}
diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h
index 8bc73de6fe..c8ddf6ffee 100644
--- a/crawl-ref/source/misc.h
+++ b/crawl-ref/source/misc.h
@@ -23,6 +23,8 @@
* called from: ability - decks - fight - it_use2 - spells1
* *********************************************************************** */
bool go_berserk(bool intentional);
+bool trap_item(object_class_type base_type, char sub_type,
+ char beam_x, char beam_y);
// last updated 12may2000 {dlb}
@@ -37,7 +39,8 @@ void search_around( bool only_adjacent = false );
* called from: acr
* *********************************************************************** */
void disarm_trap(struct dist &disa);
-
+void remove_net_from( monsters *mon );
+void free_self_from_net( bool damage_net = true );
// last updated 12may2000 {dlb}
/* ***********************************************************************
@@ -59,7 +62,8 @@ bool fall_into_a_pool( int entry_x, int entry_y, bool allow_shift,
* called from: acr - misc
* *********************************************************************** */
void handle_traps(char trt, int i, bool trap_known);
-
+void monster_caught_in_net(monsters *mon);
+void player_caught_in_net(void);
// last updated 12may2000 {dlb}
/* ***********************************************************************
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 08df90314e..dd2a7b36d0 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -1689,6 +1689,11 @@ bool mons_is_confused(const monsters *m)
!mons_class_flag(m->type, M_CONFUSED));
}
+bool mons_is_caught(const monsters *m)
+{
+ return (m->has_ench(ENCH_CAUGHT));
+}
+
bool mons_is_fleeing(const monsters *m)
{
return (m->behaviour == BEH_FLEE);
@@ -1727,7 +1732,8 @@ bool mons_looks_distracted(const monsters *m)
&& !mons_friendly(m)
&& ((m->foe != MHITYOU && !mons_is_batty(m))
|| mons_is_confused(m)
- || mons_is_fleeing(m)));
+ || mons_is_fleeing(m)
+ || mons_is_caught(m)));
}
bool mons_should_fire(struct bolt &beam)
@@ -1844,6 +1850,20 @@ bool ms_low_hitpoint_cast( const monsters *mon, spell_type monspell )
return (ret);
}
+// spells for a quick get-away
+// currently only used to get out of a net
+bool ms_quick_get_away( const monsters *mon, spell_type monspell )
+{
+ switch (monspell)
+ {
+ case SPELL_TELEPORT_SELF:
+ case SPELL_BLINK:
+ return true;
+ default:
+ return false;
+ }
+}
+
// Checks to see if a particular spell is worth casting in the first place.
bool ms_waste_of_time( const monsters *mon, spell_type monspell )
{
@@ -2708,6 +2728,11 @@ bool monsters::pickup_missile(item_def &item, int near, bool force)
// much work for now.
const item_def *miss = missiles();
+
+ // monster may not pick up trapping net
+ if (mons_is_caught(this) && item.sub_type == MI_THROWING_NET)
+ return (false);
+
if (miss && items_stack(*miss, item))
return (pickup(item, MSLOT_MISSILE, near));
@@ -3000,6 +3025,11 @@ bool monsters::backlit() const
return (has_ench(ENCH_BACKLIGHT));
}
+bool monsters::caught() const
+{
+ return (mons_is_caught(this));
+}
+
int monsters::shield_bonus() const
{
// XXX: Monsters don't actually get shields yet.
@@ -3032,6 +3062,8 @@ int monsters::melee_evasion(const actor *act) const
int evasion = ev;
if (paralysed() || asleep() || one_chance_in(20))
evasion = 0;
+ else if (caught())
+ evasion /= (body_size(PSIZE_BODY) + 2);
else if (confused())
evasion /= 2;
return (evasion);
@@ -3629,6 +3661,11 @@ void monsters::remove_enchantment_effect(const mon_enchant &me, bool quiet)
simple_monster_message(this, " is no longer rotting.");
break;
+ case ENCH_CAUGHT:
+ if (!quiet)
+ simple_monster_message(this, " breaks free.");
+ break;
+
case ENCH_ABJ:
case ENCH_SHORT_LIVED:
add_ench( mon_enchant(ENCH_ABJ) );
@@ -3748,6 +3785,10 @@ void monsters::timeout_enchantments(int levels)
blink();
break;
+ case ENCH_CAUGHT:
+ del_ench(i->first);
+ break;
+
default:
break;
}
@@ -3850,6 +3891,121 @@ void monsters::apply_enchantment(const mon_enchant &me)
decay_enchantment(me);
break;
+ case ENCH_CAUGHT:
+ {
+ if (mons_is_paralysed(this) || this->behaviour == BEH_SLEEP)
+ break;
+
+ int net, next;
+ for (net = igrd[x][y]; net != NON_ITEM; net = next)
+ {
+ next = mitm[net].link;
+
+ if (mitm[net].base_type == OBJ_MISSILES
+ && mitm[net].sub_type == MI_THROWING_NET)
+ {
+ break;
+ }
+ }
+
+ if (net == NON_ITEM) // really shouldn't happen!
+ {
+ del_ench(ENCH_CAUGHT);
+ break;
+ }
+
+ // handled in handle_pickup
+ if (mons_itemuse(type) == MONUSE_EATS_ITEMS)
+ break;
+
+ // the enchantment doubles as the durability of a net
+ // the more corroded it gets, the more easily it will break
+ int hold = mitm[net].plus; // this will usually be negative
+ int mon_size = body_size(PSIZE_BODY);
+
+ // smaller monsters can escape more quickly
+ if (mon_size < random2(SIZE_BIG) // BIG = 5
+ && !has_ench(ENCH_BERSERK))
+ {
+ if (mons_near(this) && !player_monster_visible(this))
+ mpr("Something wriggles in the net.");
+ else
+ simple_monster_message(this, " struggles to escape the net.");
+
+ // confused monsters have trouble finding the exit
+ if (has_ench(ENCH_CONFUSION) && !one_chance_in(5))
+ break;
+
+ decay_enchantment(me, 2*(NUM_SIZE_LEVELS - mon_size) - hold);
+
+ // frayed nets are easier to escape
+ if (mon_size <= -(hold-1)/2)
+ decay_enchantment(me, (NUM_SIZE_LEVELS - mon_size));
+ }
+ else // large (and above) monsters always thrash the net and destroy it
+ { // e.g. ogre, large zombie (large); centaur, nage, hydra (big)
+
+ if (mons_near(this) && !player_monster_visible(this))
+ mpr("Something wriggles in the net.");
+ else
+ simple_monster_message(this, " struggles against the net.");
+
+ // confused monsters more likely to struggle without result
+ if (has_ench(ENCH_CONFUSION) && one_chance_in(3))
+ break;
+
+ // nets get destroyed more quickly for larger monsters
+ // and if already strongly frayed
+ int damage = 0;
+
+ // tiny: 1/6, little: 2/5, small: 3/4, medium and above: always
+ if (random2(SIZE_GIANT - mon_size) <= mon_size)
+ damage++;
+
+ // extra damage for large (50%) and big (always)
+ if (mon_size == SIZE_BIG || mon_size == SIZE_LARGE && coinflip())
+ damage++;
+
+ // overall damage per struggle:
+ // tiny -> 1/6
+ // little -> 2/5
+ // small -> 3/4
+ // medium -> 1
+ // large -> 1,5
+ // big -> 2
+
+ // extra damage if already damaged
+ if (random2(body_size(PSIZE_BODY) - hold + 1) >= 4)
+ damage++;
+
+ // berserking doubles damage dealt
+ if (has_ench(ENCH_BERSERK))
+ damage *= 2;
+
+ mitm[net].plus -= damage;
+
+ if (mitm[net].plus < -7)
+ {
+ if (mons_near(this))
+ {
+ if (player_monster_visible(this))
+ {
+ mprf("The net rips apart, and %s comes free!",
+ name(DESC_NOCAP_THE).c_str());
+ }
+ else
+ {
+ mpr("All of a sudden the net rips apart!");
+ }
+ }
+ dec_mitm_item_quantity( net, 1 );
+
+ del_ench(ENCH_CAUGHT, true);
+ }
+
+ }
+ break;
+ }
case ENCH_CONFUSION:
if (!mons_class_flag(type, M_CONFUSED))
decay_enchantment(me);
@@ -4386,6 +4542,9 @@ int mon_enchant::calc_duration(const monsters *mons,
case ENCH_CONFUSION:
cturn = 120 / modded_speed(mons, 5);
break;
+ case ENCH_CAUGHT:
+ cturn = 90 / mod_speed(25, mons->speed);
+ break;
case ENCH_POISON:
cturn = 1000 * deg / mod_speed(125, mons->speed);
break;
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 9b8de81d0b..67ab7c8f82 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -302,6 +302,7 @@ bool ms_direct_nasty(spell_type monspell);
bool ms_requires_tracer(spell_type mons_spell);
bool ms_useful_fleeing_out_of_sight( const monsters *mon, spell_type monspell );
+bool ms_quick_get_away( const monsters *mon, spell_type monspell );
bool ms_waste_of_time( const monsters *mon, spell_type monspell );
bool ms_low_hitpoint_cast( const monsters *mon, spell_type monspell );
@@ -331,6 +332,7 @@ mon_attitude_type mons_attitude(const monsters *m);
bool mons_behaviour_perceptible(const monsters *mons);
bool mons_is_confused(const monsters *m);
+bool mons_is_caught(const monsters *m);
bool mons_is_fleeing(const monsters *m);
bool mons_is_sleeping(const monsters *m);
bool mons_is_batty(const monsters *m);
diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc
index 49fac6d8d1..8fd4e5e108 100644
--- a/crawl-ref/source/monspeak.cc
+++ b/crawl-ref/source/monspeak.cc
@@ -125,7 +125,7 @@ static bool say_specific_dialogue(const monsters *monster,
return (false);
const bool friendly = (monster->attitude == ATT_FRIENDLY);
-
+
if (mons_is_confused(monster))
return (say_dialogue(
monster,
@@ -373,6 +373,87 @@ bool mons_speaks(const monsters *monster)
}
}
+ else if (monster->has_ench(ENCH_CAUGHT))
+ {
+ if (mons_friendly(monster))
+ {
+ switch(random2(8))
+ {
+ case 0:
+ strcat(info, " says, \"Help me, ");
+ strcat(info, you.your_name);
+ strcat(info, ", please!\"");
+ break;
+ case 1:
+ strcat(info, " cries, \"MUMMY!\"");
+ break;
+ case 2:
+ strcat(info, " shouts, \"");
+ strcat(info, you.your_name);
+ strcat(info, "! Can't you see I need your help?\"");
+ break;
+ case 3:
+ strcat(info, " shouts, \"I could do with a little help here, you know.\"");
+ break;
+ case 4:
+ strcat(info, " mumbles something.");
+ break;
+ case 5:
+ strcat(info, " says, \"Umm, ");
+ strcat(info, you.your_name);
+ strcat(info, "? Help?\"");
+ break;
+ case 6:
+ strcat(info, " cries.");
+ break;
+ case 7:
+ strcat(info, " cries, \"Why me?");
+ break;
+ }
+ }
+ else // unfriendly monsters
+ {
+ switch(random2(12))
+ {
+ case 0:
+ strcat(info, " screams, \"HEY! This isn't fair!\"");
+ break;
+ case 1:
+ strcat(info, " screams, \"Help! Get me out of here!\"");
+ break;
+ case 2:
+ strcat(info, " begs, \"Could you help me? I swear I won't hurt you.\"");
+ break;
+ case 3:
+ strcat(info, " yells, \"LEMME GO!\"");
+ break;
+ case 4:
+ strcat(info, " cries, \"Please! I'll never do it again!\"");
+ break;
+ case 5:
+ strcat(info, " mutters, \"Just what did I do to deserve this?\"");
+ break;
+ case 6:
+ strcat(info, " asks, \"Hey, want to switch places?\"");
+ break;
+ case 7:
+ strcat(info, " cries, \"I hate you!\"");
+ break;
+ case 8:
+ strcat(info, " snarls, \"This is all your fault!\"");
+ break;
+ case 9:
+ strcat(info, " says, \"I meant to do this, just so you know.\"");
+ break;
+ case 10:
+ strcat(info, " shouts, \"This is all a huge misunderstanding!");
+ break;
+ case 11:
+ strcat(info, " cries, \"Why me?\"");
+ break;
+ }
+ }
+ }
else if (monster->behaviour == BEH_FLEE)
{
if (mons_holiness( monster ) == MH_DEMONIC
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 87b238f9e5..f722a5a23a 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -1123,6 +1123,7 @@ bool monster_polymorph( monsters *monster, monster_type targetc,
// the actual polymorphing:
const int old_hp = monster->hit_points;
const int old_hp_max = monster->max_hit_points;
+ const bool old_mon_caught = mons_is_caught(monster);
/* deal with mons_sec */
monster->type = targetc;
@@ -1166,6 +1167,40 @@ bool monster_polymorph( monsters *monster, monster_type targetc,
if (player_monster_visible(monster) && mons_near(monster))
seen_monster(monster);
+ if (old_mon_caught)
+ {
+ if (monster->body_size(PSIZE_BODY) >= SIZE_GIANT)
+ {
+ int net, next;
+ for (net = igrd[monster->x][monster->y]; net != NON_ITEM; net = next)
+ {
+ next = mitm[net].link;
+
+ if (mitm[net].base_type == OBJ_MISSILES
+ && mitm[net].sub_type == MI_THROWING_NET)
+ {
+ break;
+ }
+ }
+ dec_mitm_item_quantity( net, 1 );
+
+ if (see_grid(monster->x, monster->y))
+ {
+ if (player_monster_visible(monster))
+ {
+ mprf("The net rips apart, and %s comes free!",
+ monster->name(DESC_NOCAP_THE).c_str());
+ }
+ else
+ {
+ mpr("All of a sudden the net rips apart!");
+ }
+ }
+ }
+ else
+ monster->add_ench(ENCH_CAUGHT);
+ }
+
return (player_messaged);
} // end monster_polymorph()
@@ -1190,6 +1225,9 @@ bool monster_blink(monsters *monster)
monster->check_redraw(oldplace);
monster->apply_location_effects();
+
+ if (monster->has_ench(ENCH_CAUGHT))
+ monster->del_ench(ENCH_CAUGHT, true);
return (true);
} // end monster_blink()
@@ -2419,7 +2457,7 @@ static bool handle_special_ability(monsters *monster, bolt & beem)
case MONS_INSUBSTANTIAL_WISP:
case MONS_BLINK_FROG:
case MONS_KILLER_KLOWN:
- if (one_chance_in(7))
+ if (one_chance_in(7) || mons_is_caught(monster) && one_chance_in(3))
{
simple_monster_message(monster, " blinks.");
monster_blink(monster);
@@ -2686,7 +2724,7 @@ static bool handle_scroll(monsters *monster)
case SCR_TELEPORTATION:
if (!monster->has_ench(ENCH_TP))
{
- if (monster->behaviour == BEH_FLEE)
+ if (monster->behaviour == BEH_FLEE || monster->has_ench(ENCH_CAUGHT))
{
simple_monster_message(monster, " reads a scroll.");
monster_teleport(monster, false);
@@ -2696,7 +2734,7 @@ static bool handle_scroll(monsters *monster)
break;
case SCR_BLINKING:
- if (monster->behaviour == BEH_FLEE)
+ if (monster->behaviour == BEH_FLEE || monster->has_ench(ENCH_CAUGHT))
{
if (mons_near(monster))
{
@@ -2839,7 +2877,8 @@ static bool handle_wand(monsters *monster, bolt &beem)
return (false);
case WAND_TELEPORTATION:
- if (monster->hit_points <= monster->max_hit_points / 2)
+ if (monster->hit_points <= monster->max_hit_points / 2
+ || mons_is_caught(monster))
{
if (!monster->has_ench(ENCH_TP)
&& !one_chance_in(20))
@@ -3214,6 +3253,23 @@ static bool handle_spell( monsters *monster, bolt & beem )
}
}
+ // monsters caught in a net try to get away
+ // this is only urgent if you are around
+ if (!finalAnswer && monsterNearby && mons_is_caught(monster)
+ && one_chance_in(3))
+ {
+ if (ms_quick_get_away( monster, hspell_pass[6]))
+ {
+ spell_cast = hspell_pass[6];
+ finalAnswer = true;
+ }
+ else if (ms_quick_get_away( monster, hspell_pass[5]))
+ {
+ spell_cast = hspell_pass[5];
+ finalAnswer = true;
+ }
+ }
+
// Promote the casting of useful spells for low-HP monsters.
if (!finalAnswer
&& monster->hit_points < monster->max_hit_points / 4
@@ -3686,7 +3742,7 @@ static void handle_monster_move(int i, monsters *monster)
continue;
handle_behaviour(monster);
-
+
// submerging monsters will hide from clouds
if (monster_can_submerge(monster->type, grd[monster->x][monster->y])
&& env.cgrid[monster->x][monster->y] != EMPTY_CLOUD)
@@ -3721,71 +3777,73 @@ static void handle_monster_move(int i, monsters *monster)
}
}
- // calculates mmov_x, mmov_y based on monster target.
- handle_movement(monster);
-
- brkk = false;
-
- if (mons_is_confused( monster )
- || (monster->type == MONS_AIR_ELEMENTAL
- && monster->has_ench(ENCH_SUBMERGED)))
+ if (!mons_is_caught(monster))
{
- std::vector<coord_def> moves;
-
- int pfound = 0;
- for (int yi = -1; yi <= 1; ++yi)
- {
- for (int xi = -1; xi <= 1; ++xi)
- {
- coord_def c = monster->pos() + coord_def(xi, yi);
- if (in_bounds(c) && !grid_is_solid(grd(c))
- && one_chance_in(++pfound))
- {
- mmov_x = xi;
- mmov_y = yi;
- }
- }
- }
-
- if (random2(2 + pfound) < 2)
- mmov_x = mmov_y = 0;
-
- // bounds check: don't let confused monsters try to run
- // off the map
- if (monster->x + mmov_x < 0
- || monster->x + mmov_x >= GXM)
- {
- mmov_x = 0;
- }
-
- if (monster->y + mmov_y < 0
- || monster->y + mmov_y >= GYM)
- {
- mmov_y = 0;
- }
-
- if (grid_is_solid(
- grd[ monster->x + mmov_x ][ monster->y + mmov_y ]))
- {
- mmov_x = mmov_y = 0;
- }
-
- if (mgrd[monster->x + mmov_x][monster->y + mmov_y] != NON_MONSTER
- && (mmov_x != 0 || mmov_y != 0))
- {
- monsters_fight(
- i,
- mgrd[monster->x + mmov_x][monster->y + mmov_y]);
+ // calculates mmov_x, mmov_y based on monster target.
+ handle_movement(monster);
+
+ brkk = false;
+
+ if (mons_is_confused( monster )
+ || (monster->type == MONS_AIR_ELEMENTAL
+ && monster->has_ench(ENCH_SUBMERGED)))
+ {
+ std::vector<coord_def> moves;
+
+ int pfound = 0;
+ for (int yi = -1; yi <= 1; ++yi)
+ {
+ for (int xi = -1; xi <= 1; ++xi)
+ {
+ coord_def c = monster->pos() + coord_def(xi, yi);
+ if (in_bounds(c) && !grid_is_solid(grd(c))
+ && one_chance_in(++pfound))
+ {
+ mmov_x = xi;
+ mmov_y = yi;
+ }
+ }
+ }
+
+ if (random2(2 + pfound) < 2)
+ mmov_x = mmov_y = 0;
+
+ // bounds check: don't let confused monsters try to run
+ // off the map
+ if (monster->x + mmov_x < 0
+ || monster->x + mmov_x >= GXM)
+ {
+ mmov_x = 0;
+ }
+
+ if (monster->y + mmov_y < 0
+ || monster->y + mmov_y >= GYM)
+ {
+ mmov_y = 0;
+ }
+
+ if (grid_is_solid(
+ grd[ monster->x + mmov_x ][ monster->y + mmov_y ]))
+ {
+ mmov_x = mmov_y = 0;
+ }
+
+ if (mgrd[monster->x + mmov_x][monster->y + mmov_y] != NON_MONSTER
+ && (mmov_x != 0 || mmov_y != 0))
+ {
+ monsters_fight(
+ i,
+ mgrd[monster->x + mmov_x][monster->y + mmov_y]);
- brkk = true;
- mmov_x = 0;
- mmov_y = 0;
- }
+ brkk = true;
+ mmov_x = 0;
+ mmov_y = 0;
+ }
+ }
+
+ if (brkk)
+ continue;
}
-
- if (brkk)
- continue;
-
handle_nearby_ability( monster );
beem.target_x = monster->target_x;
@@ -3832,78 +3890,80 @@ static void handle_monster_move(int i, monsters *monster)
continue;
}
- // see if we move into (and fight) an unfriendly monster
- int targmon = mgrd[monster->x + mmov_x][monster->y + mmov_y];
- if (targmon != NON_MONSTER
- && targmon != i
- && !mons_aligned(i, targmon))
+ if (!mons_is_caught(monster))
{
- // figure out if they fight
- if (monsters_fight(i, targmon))
+ // see if we move into (and fight) an unfriendly monster
+ int targmon = mgrd[monster->x + mmov_x][monster->y + mmov_y];
+ if (targmon != NON_MONSTER
+ && targmon != i
+ && !mons_aligned(i, targmon))
{
- if (testbits(monster->flags, MF_BATTY))
+ // figure out if they fight
+ if (monsters_fight(i, targmon))
{
- monster->behaviour = BEH_WANDER;
- monster->target_x = 10 + random2(GXM - 10);
- monster->target_y = 10 + random2(GYM - 10);
- // monster->speed_increment -= monster->speed;
- }
+ if (testbits(monster->flags, MF_BATTY))
+ {
+ monster->behaviour = BEH_WANDER;
+ monster->target_x = 10 + random2(GXM - 10);
+ monster->target_y = 10 + random2(GYM - 10);
+ // monster->speed_increment -= monster->speed;
+ }
- mmov_x = 0;
- mmov_y = 0;
- brkk = true;
+ mmov_x = 0;
+ mmov_y = 0;
+ brkk = true;
+ }
}
- }
-
- if (brkk)
- continue;
- if (monster->x + mmov_x == you.x_pos
- && monster->y + mmov_y == you.y_pos)
- {
- bool isFriendly = mons_friendly(monster);
- bool attacked = false;
+ if (brkk)
+ continue;
- if (!isFriendly)
+ if (monster->x + mmov_x == you.x_pos
+ && monster->y + mmov_y == you.y_pos)
{
- monster_attack(i);
- attacked = true;
+ bool isFriendly = mons_friendly(monster);
+ bool attacked = false;
- if (testbits(monster->flags, MF_BATTY))
+ if (!isFriendly)
{
- monster->behaviour = BEH_WANDER;
- monster->target_x = 10 + random2(GXM - 10);
- monster->target_y = 10 + random2(GYM - 10);
+ monster_attack(i);
+ attacked = true;
+
+ if (testbits(monster->flags, MF_BATTY))
+ {
+ monster->behaviour = BEH_WANDER;
+ monster->target_x = 10 + random2(GXM - 10);
+ monster->target_y = 10 + random2(GYM - 10);
+ }
}
- }
- if ((monster->type == MONS_GIANT_SPORE
- || monster->type == MONS_BALL_LIGHTNING)
- && monster->hit_points < 1)
- {
+ if ((monster->type == MONS_GIANT_SPORE
+ || monster->type == MONS_BALL_LIGHTNING)
+ && monster->hit_points < 1)
+ {
- // detach monster from the grid first, so it
- // doesn't get hit by its own explosion (GDL)
- mgrd[monster->x][monster->y] = NON_MONSTER;
+ // detach monster from the grid first, so it
+ // doesn't get hit by its own explosion (GDL)
+ mgrd[monster->x][monster->y] = NON_MONSTER;
- spore_goes_pop(monster);
- monster_cleanup(monster);
- continue;
- }
+ spore_goes_pop(monster);
+ monster_cleanup(monster);
+ continue;
+ }
- if (attacked)
- {
- mmov_x = 0;
- mmov_y = 0;
- continue; //break;
+ if (attacked)
+ {
+ mmov_x = 0;
+ mmov_y = 0;
+ continue; //break;
+ }
}
- }
-
- if (invalid_monster(monster) || mons_is_stationary(monster))
- continue;
- monster_move(monster);
+ if (invalid_monster(monster) || mons_is_stationary(monster))
+ continue;
+ monster_move(monster);
+ }
// reevaluate behaviour, since the monster's
// surroundings have changed (it may have moved,
// or died for that matter. Don't bother for
@@ -4024,6 +4084,7 @@ static bool handle_pickup(monsters *monster)
int hps_gained = 0;
int max_eat = roll_dice( 1, 10 );
int eaten = 0;
+ bool eaten_net = false;
for (item = igrd[monster->x][monster->y];
item != NON_ITEM && eaten < max_eat && hps_gained < 50;
@@ -4041,6 +4102,13 @@ static bool handle_pickup(monsters *monster)
hps_gained += (quant * item_mass( mitm[item] )) / 20 + quant;
eaten += quant;
+
+ if (mons_is_caught(monster) && mitm[item].base_type == OBJ_MISSILES
+ && mitm[item].sub_type == MI_THROWING_NET)
+ {
+ monster->del_ench(ENCH_CAUGHT, true);
+ eaten_net = true;
+ }
}
else
{
@@ -4075,6 +4143,8 @@ static bool handle_pickup(monsters *monster)
mprf(MSGCH_SOUND, "You hear a%s slurping noise.",
monsterNearby ? "" : " distant");
}
+ if (eaten_net)
+ simple_monster_message(monster, " devours the net!");
if (mons_class_flag( monster->type, M_SPLITS ))
{
diff --git a/crawl-ref/source/mstuff2.cc b/crawl-ref/source/mstuff2.cc
index 1da999505c..6aa6820bac 100644
--- a/crawl-ref/source/mstuff2.cc
+++ b/crawl-ref/source/mstuff2.cc
@@ -198,6 +198,48 @@ void mons_trap(struct monsters *monster)
revealTrap = true;
break;
+ case TRAP_NET:
+ {
+ if (one_chance_in(3))
+ {
+ if (trapKnown)
+ {
+ simple_monster_message(monster,
+ " fails to trigger a net trap.");
+ }
+ return;
+ }
+
+ if (random2(monster->ev) > 8)
+ {
+ if (monsterNearby && !simple_monster_message(monster,
+ " nimbly jumps out of the way of a falling net."))
+ {
+ mpr("A large net falls down!");
+ }
+ }
+ else
+ {
+ if (monsterNearby)
+ {
+ std::string msg = "A large net falls down";
+ if (player_monster_visible( monster ))
+ {
+ msg += " onto ";
+ msg += monster->name(DESC_NOCAP_THE);
+ }
+ msg += "!";
+ mpr(msg.c_str());
+ monster_caught_in_net(monster);
+ }
+ }
+
+ trap_item( OBJ_MISSILES, MI_THROWING_NET,
+ env.trap[which_trap].x, env.trap[which_trap].y );
+ grd[env.trap[which_trap].x][env.trap[which_trap].y] = DNGN_FLOOR;
+ env.trap[which_trap].type = TRAP_UNASSIGNED;
+ break;
+ }
// zot traps are out to get *the player*! Hostile monsters
// benefit and friendly monsters suffer - such is life - on
// rare occasion, the trap affects nearby players, triggering
@@ -888,6 +930,9 @@ void monster_teleport(struct monsters *monster, bool instan, bool silent)
monster->check_redraw(oldplace);
monster->apply_location_effects();
+ if (monster->has_ench(ENCH_CAUGHT))
+ monster->del_ench(ENCH_CAUGHT, true);
+
// Teleporting mimics change form - if they reappear out of LOS, they are
// no longer known.
if (mons_is_mimic(monster->type))
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index 238be50b2d..2858bb44d1 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -5340,6 +5340,11 @@ bool player::confused() const
return (duration[DUR_CONF]);
}
+bool player::caught() const
+{
+ return (attribute[ATTR_CAUGHT]);
+}
+
int player::shield_block_penalty() const
{
return (5 * shield_blocks * shield_blocks);
diff --git a/crawl-ref/source/spells1.cc b/crawl-ref/source/spells1.cc
index 85107a9165..2ff342aa70 100644
--- a/crawl-ref/source/spells1.cc
+++ b/crawl-ref/source/spells1.cc
@@ -117,6 +117,9 @@ int blink(int pow, bool high_level_controlled_blink)
}
else
{
+ if (you.attribute[ATTR_CAUGHT])
+ you.attribute[ATTR_CAUGHT] = 0;
+
move_player_to_grid(beam.tx, beam.ty, false, true, true);
// controlling teleport contaminates the player -- bwr
@@ -164,6 +167,9 @@ void random_blink(bool allow_partial_control, bool override_abyss)
else
{
mpr("You blink.");
+
+ if (you.attribute[ATTR_CAUGHT])
+ you.attribute[ATTR_CAUGHT] = 0;
succ = true;
you.moveto(tx, ty);
diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc
index 6ad9864151..d1d7bf83f8 100644
--- a/crawl-ref/source/spells4.cc
+++ b/crawl-ref/source/spells4.cc
@@ -2989,6 +2989,17 @@ int cast_apportation(int pow)
(mitm[ item ].quantity > 1) ? "s" : "" );
}
done = 1;
+
+ // if we apport a net, free the monster under it
+ if (mitm[item].base_type == OBJ_MISSILES
+ && mitm[item].sub_type == MI_THROWING_NET)
+ {
+ int mon = mgrd[ beam.tx ][ beam.ty ];
+ if (mon != NON_MONSTER && mons_is_caught(&menv[mon]))
+ {
+ (&menv[mon])->del_ench(ENCH_CAUGHT, true);
+ }
+ }
}
else
mpr( "The spell fails." );
diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc
index 2e0dc0ac2a..951f0c5033 100644
--- a/crawl-ref/source/travel.cc
+++ b/crawl-ref/source/travel.cc
@@ -908,7 +908,8 @@ command_type travel()
command_type result = CMD_NO_CMD;
// Abort travel/explore if you're confused or a key was pressed.
- if (kbhit() || you.duration[DUR_CONF])
+ // Or if you got caught in a net.
+ if (kbhit() || you.duration[DUR_CONF] || you.attribute[ATTR_CAUGHT])
{
stop_running();
return CMD_NO_CMD;