From 5b618acbca35621d699dc4ba3064f2a3228a5131 Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Mon, 20 Aug 2007 12:23:38 +0000 Subject: 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 --- crawl-ref/source/acr.cc | 32 +++++ crawl-ref/source/beam.cc | 13 ++ crawl-ref/source/describe.cc | 25 +++- crawl-ref/source/direct.cc | 5 + crawl-ref/source/dungeon.cc | 3 + crawl-ref/source/enum.h | 4 + crawl-ref/source/externs.h | 5 +- crawl-ref/source/item_use.cc | 19 +++ crawl-ref/source/itemprop.cc | 5 +- crawl-ref/source/items.cc | 13 +- crawl-ref/source/makeitem.cc | 6 + crawl-ref/source/misc.cc | 225 ++++++++++++++++++++++++++++++- crawl-ref/source/misc.h | 8 +- crawl-ref/source/mon-util.cc | 161 +++++++++++++++++++++- crawl-ref/source/mon-util.h | 2 + crawl-ref/source/monspeak.cc | 83 +++++++++++- crawl-ref/source/monstuff.cc | 312 ++++++++++++++++++++++++++----------------- crawl-ref/source/mstuff2.cc | 45 +++++++ crawl-ref/source/player.cc | 5 + crawl-ref/source/spells1.cc | 6 + crawl-ref/source/spells4.cc | 11 ++ crawl-ref/source/travel.cc | 3 +- 22 files changed, 857 insertions(+), 134 deletions(-) (limited to 'crawl-ref/source') 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& 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 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 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; -- cgit v1.2.3-54-g00ecf