summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/traps.cc
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-15 23:33:50 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-15 23:33:50 +0000
commitfa763ba1bc7285247a5b1438d59633383a80cf6c (patch)
treef4b632fea66f43dc6c1415fdaa4feead0b6ff90d /crawl-ref/source/traps.cc
parent4d88632cb99d368956dec86732f7d275ffb941e8 (diff)
downloadcrawl-ref-fa763ba1bc7285247a5b1438d59633383a80cf6c.tar.gz
crawl-ref-fa763ba1bc7285247a5b1438d59633383a80cf6c.zip
Split off portions of externs.h and enum.h into other files. The
crawl_environment, player and monsters classes have been left in externs.h, which necessitates that all of the enums references by those classes stay in enums.h, since you can't forward declare an enum. However, it's a start. Also, portions of misc.{cc,h} have been split off into traps.{cc,h}, place.{cc,h} and terrain.{cc,h} git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2095 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/traps.cc')
-rw-r--r--crawl-ref/source/traps.cc701
1 files changed, 701 insertions, 0 deletions
diff --git a/crawl-ref/source/traps.cc b/crawl-ref/source/traps.cc
new file mode 100644
index 0000000000..9168f1e585
--- /dev/null
+++ b/crawl-ref/source/traps.cc
@@ -0,0 +1,701 @@
+/*
+ * File: traps.cc
+ * Summary: Traps related functions.
+ * Written by: Linley Henzell
+ *
+ * Modified for Crawl Reference by $Author: j-p-e-g $ on $Date: 2007-09-03 06:41:30 -0700 (Mon, 03 Sep 2007) $
+ *
+ * Change History (most recent first):
+ *
+ * <1> 9/11/07 MPC Split from misc.cc
+ */
+
+#include "externs.h"
+#include "traps.h"
+
+#include "beam.h"
+#include "direct.h"
+#include "it_use2.h"
+#include "items.h"
+#include "itemprop.h"
+#include "makeitem.h"
+#include "mon-util.h"
+#include "monstuff.h"
+#include "ouch.h"
+#include "player.h"
+#include "randart.h"
+#include "skills.h"
+#include "spells3.h"
+#include "spl-cast.h"
+#include "spl-util.h"
+#include "terrain.h"
+#include "tutorial.h"
+#include "view.h"
+
+static void dart_trap(bool trap_known, int trapped, bolt &pbolt, bool poison);
+
+// returns the number of a net on a given square
+// if trapped only stationary ones are counted
+// otherwise the first net found is returned
+int get_trapping_net(int x, int y, bool trapped)
+{
+ 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
+ && (!trapped || item_is_stationary(mitm[net])))
+ {
+ return (net);
+ }
+ }
+ return (NON_ITEM);
+}
+
+// if there are more than one net on this square
+// split off one of them for checking/setting values
+static void maybe_split_nets(item_def &item, int x, int y)
+{
+ if (item.quantity == 1)
+ {
+ set_item_stationary(item);
+ return;
+ }
+
+ item_def it;
+
+ it.base_type = item.base_type;
+ it.sub_type = item.sub_type;
+ it.plus = item.plus;
+ it.plus2 = item.plus2;
+ it.flags = item.flags;
+ it.special = item.special;
+ it.quantity = --item.quantity;
+ item_colour(it);
+
+ item.quantity = 1;
+ set_item_stationary(item);
+
+ copy_item_to_grid( it, x, y );
+}
+
+void mark_net_trapping(int x, int y)
+{
+ int net = get_trapping_net(x,y);
+ if (net == NON_ITEM)
+ {
+ net = get_trapping_net(x,y, false);
+ if (net != NON_ITEM)
+ maybe_split_nets(mitm[net], x, y);
+ }
+}
+
+void monster_caught_in_net(monsters *mon, bolt &pbolt)
+{
+ if (mon->body_size(PSIZE_BODY) >= SIZE_GIANT)
+ return;
+
+ if (mons_is_insubstantial(mon->type))
+ {
+ if (mons_near(mon) && player_monster_visible(mon))
+ mprf("The net passes right through %s!", mon->name(DESC_NOCAP_THE).c_str());
+ return;
+ }
+
+ const monsters* mons = static_cast<const monsters*>(mon);
+ bool mon_flies = mons->flies();
+ if (mon_flies && !mons_is_confused(mons))
+ {
+ simple_monster_message(mon, " darts out from under the net!");
+ return;
+ }
+
+ if (mons->type == MONS_OOZE || mons->type == MONS_PULSATING_LUMP)
+ {
+ simple_monster_message(mon, " oozes right through the net!");
+ return;
+ }
+
+ if (!mons_is_caught(mon) && mon->add_ench(ENCH_HELD))
+ {
+ 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!");
+
+ if (mon_flies)
+ {
+ simple_monster_message(mon, " falls like a stone!");
+ mons_check_pool(mon, pbolt.killer(), pbolt.beam_source);
+ }
+ }
+}
+
+void player_caught_in_net()
+{
+ if (you.body_size(PSIZE_BODY) >= SIZE_GIANT)
+ return;
+
+ if (you.flies() && !you.confused())
+ {
+ mpr("You dart out from under the net!");
+ return;
+ }
+
+ if (!you.attribute[ATTR_HELD])
+ {
+ you.attribute[ATTR_HELD] = 10;
+ mpr("You become entangled in the net!");
+
+ // I guess levitation works differently, keeping both you
+ // and the net hovering above the floor
+ if (you.flies())
+ {
+ mpr("You fall like a stone!");
+ fall_into_a_pool(you.x_pos, you.y_pos, false, grd[you.x_pos][you.y_pos]);
+ }
+ }
+
+}
+
+static void dart_trap(bool trap_known, int trapped, bolt &pbolt, bool poison)
+{
+ int damage_taken = 0;
+ int trap_hit, your_dodge;
+
+ if (one_chance_in(5) || (trap_known && !one_chance_in(4)))
+ {
+ mprf( "You avoid triggering a%s trap.", pbolt.name.c_str() );
+ return;
+ }
+
+ if (you.equip[EQ_SHIELD] != -1 && one_chance_in(3))
+ exercise( SK_SHIELDS, 1 );
+
+ std::string msg = "A" + pbolt.name + " shoots out and ";
+
+ if (random2( 20 + 5 * you.shield_blocks * you.shield_blocks )
+ < player_shield_class())
+ {
+ you.shield_blocks++;
+ msg += "hits your shield.";
+ mpr(msg.c_str());
+ }
+ else
+ {
+ // note that this uses full ( not random2limit(foo,40) )
+ // player_evasion.
+ trap_hit = (20 + (you.your_level * 2)) * random2(200) / 100;
+
+ your_dodge = player_evasion() + random2(you.dex) / 3
+ - 2 + (you.duration[DUR_REPEL_MISSILES] * 10);
+
+ if (trap_hit >= your_dodge && you.duration[DUR_DEFLECT_MISSILES] == 0)
+ {
+ msg += "hits you!";
+ mpr(msg.c_str());
+
+ if (poison && random2(100) < 50 - (3 * player_AC()) / 2
+ && !player_res_poison())
+ {
+ poison_player( 1 + random2(3) );
+ }
+
+ damage_taken = roll_dice( pbolt.damage );
+ damage_taken -= random2( player_AC() + 1 );
+
+ if (damage_taken > 0)
+ ouch( damage_taken, 0, KILLED_BY_TRAP, pbolt.name.c_str() );
+ }
+ else
+ {
+ msg += "misses you.";
+ mpr(msg.c_str());
+ }
+
+ if (player_light_armour(true) && coinflip())
+ exercise( SK_DODGING, 1 );
+ }
+
+ pbolt.target_x = you.x_pos;
+ pbolt.target_y = you.y_pos;
+
+ if (coinflip())
+ itrap( pbolt, trapped );
+} // end dart_trap()
+
+//
+// itrap takes location from target_x, target_y of bolt strcture.
+//
+
+void itrap( struct bolt &pbolt, int trapped )
+{
+ object_class_type base_type = OBJ_MISSILES;
+ int sub_type = MI_DART;
+
+ switch (env.trap[trapped].type)
+ {
+ case TRAP_DART:
+ base_type = OBJ_MISSILES;
+ sub_type = MI_DART;
+ break;
+ case TRAP_ARROW:
+ base_type = OBJ_MISSILES;
+ sub_type = MI_ARROW;
+ break;
+ case TRAP_BOLT:
+ base_type = OBJ_MISSILES;
+ sub_type = MI_BOLT;
+ break;
+ case TRAP_SPEAR:
+ base_type = OBJ_WEAPONS;
+ sub_type = WPN_SPEAR;
+ break;
+ case TRAP_AXE:
+ base_type = OBJ_WEAPONS;
+ sub_type = WPN_HAND_AXE;
+ break;
+ case TRAP_NEEDLE:
+ base_type = OBJ_MISSILES;
+ sub_type = MI_NEEDLE;
+ break;
+ case TRAP_NET:
+ base_type = OBJ_MISSILES;
+ sub_type = MI_THROWING_NET;
+ break;
+ default:
+ return;
+ }
+
+ trap_item( base_type, sub_type, pbolt.target_x, pbolt.target_y );
+
+ return;
+} // end itrap()
+
+void handle_traps(char trt, int i, bool trap_known)
+{
+ struct bolt beam;
+
+ switch (trt)
+ {
+ case TRAP_DART:
+ beam.name = " dart";
+ beam.damage = dice_def( 1, 4 + (you.your_level / 2) );
+ dart_trap(trap_known, i, beam, false);
+ break;
+
+ case TRAP_NEEDLE:
+ beam.name = " needle";
+ beam.damage = dice_def( 1, 0 );
+ dart_trap(trap_known, i, beam, true);
+ break;
+
+ case TRAP_ARROW:
+ beam.name = "n arrow";
+ beam.damage = dice_def( 1, 7 + you.your_level );
+ dart_trap(trap_known, i, beam, false);
+ break;
+
+ case TRAP_BOLT:
+ beam.name = " bolt";
+ beam.damage = dice_def( 1, 13 + you.your_level );
+ dart_trap(trap_known, i, beam, false);
+ break;
+
+ case TRAP_SPEAR:
+ beam.name = " spear";
+ beam.damage = dice_def( 1, 10 + you.your_level );
+ dart_trap(trap_known, i, beam, false);
+ break;
+
+ case TRAP_AXE:
+ beam.name = "n axe";
+ beam.damage = dice_def( 1, 15 + you.your_level );
+ dart_trap(trap_known, i, beam, false);
+ break;
+
+ case TRAP_TELEPORT:
+ mpr("You enter a teleport trap!");
+
+ if (scan_randarts(RAP_PREVENT_TELEPORTATION))
+ mpr("You feel a weird sense of stasis.");
+ else
+ you_teleport_now( true );
+ break;
+
+ case TRAP_AMNESIA:
+ mpr("You feel momentarily disoriented.");
+ if (!wearing_amulet(AMU_CLARITY))
+ forget_map(random2avg(100, 2));
+ break;
+
+ case TRAP_BLADE:
+ if (trap_known && one_chance_in(3))
+ mpr("You avoid triggering a blade trap.");
+ else if (random2limit(player_evasion(), 40)
+ + (random2(you.dex) / 3) + (trap_known ? 3 : 0) > 8)
+ {
+ mpr("A huge blade swings just past you!");
+ }
+ else
+ {
+ mpr("A huge blade swings out and slices into you!");
+ ouch( (you.your_level * 2) + random2avg(29, 2)
+ - random2(1 + player_AC()), 0, KILLED_BY_TRAP, " blade" );
+ }
+ 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 );
+ if (you.attribute[ATTR_HELD])
+ mark_net_trapping(you.x_pos, you.y_pos);
+
+ 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."
+ : "Oh no! You have blundered into a Zot trap!");
+ miscast_effect( SPTYP_RANDOM, random2(30) + you.your_level,
+ 75 + random2(100), 3, "a Zot trap" );
+ break;
+ }
+ learned_something_new(TUT_SEEN_TRAP, you.x_pos, you.y_pos);
+} // end handle_traps()
+
+void disarm_trap( struct dist &disa )
+{
+ if (you.duration[DUR_BERSERKER])
+ {
+ canned_msg(MSG_TOO_BERSERK);
+ return;
+ }
+
+ int i, j;
+
+ for (i = 0; i < MAX_TRAPS; i++)
+ {
+ if (env.trap[i].x == you.x_pos + disa.dx
+ && env.trap[i].y == you.y_pos + disa.dy)
+ {
+ break;
+ }
+
+ if (i == MAX_TRAPS - 1)
+ {
+ mpr("Error - couldn't find that trap.");
+ return;
+ }
+ }
+
+ if (trap_category(env.trap[i].type) == DNGN_TRAP_MAGICAL)
+ {
+ mpr("You can't disarm that trap.");
+ return;
+ }
+
+ if (random2(you.skills[SK_TRAPS_DOORS] + 2) <= random2(you.your_level + 5))
+ {
+ mpr("You failed to disarm the trap.");
+
+ you.turn_is_over = true;
+
+ if (random2(you.dex) > 5 + random2(5 + you.your_level))
+ exercise(SK_TRAPS_DOORS, 1 + random2(you.your_level / 5));
+ else
+ {
+ 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);
+ }
+
+ return;
+ }
+
+ mpr("You have disarmed the trap.");
+
+ struct bolt beam;
+
+ beam.target_x = you.x_pos + disa.dx;
+ beam.target_y = you.y_pos + disa.dy;
+
+ 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]);
+ for (j = 0; j < num_to_make; j++)
+ {
+ // places items (eg darts), which will automatically stack
+ itrap(beam, i);
+ }
+ }
+
+ grd[you.x_pos + disa.dx][you.y_pos + disa.dy] = DNGN_FLOOR;
+ env.trap[i].type = TRAP_UNASSIGNED;
+ you.turn_is_over = true;
+
+ // reduced from 5 + random2(5)
+ 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 = get_trapping_net(mon->x, mon->y);
+
+ if (net == NON_ITEM)
+ {
+ mon->del_ench(ENCH_HELD, 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!");
+ mon->del_ench(ENCH_HELD, true);
+ destroy_item(net);
+ net_destroyed = 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_HELD, true);
+ remove_item_stationary(mitm[net]);
+
+ 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 = get_trapping_net(you.x_pos, you.y_pos);
+
+ if (net == NON_ITEM) // really shouldn't happen!
+ {
+ you.attribute[ATTR_HELD] = 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_HELD] = 0;
+ return;
+ }
+ }
+ else // you try to escape
+ {
+ mpr("You struggle to escape from the net.");
+ you.attribute[ATTR_HELD]--;
+
+ if (you.body_size(PSIZE_BODY) < SIZE_MEDIUM)
+ you.attribute[ATTR_HELD]--;
+
+ if (hold < 0 && !one_chance_in(-hold/2))
+ you.attribute[ATTR_HELD]--;
+
+ if (you.attribute[ATTR_HELD] <= 0)
+ {
+ mpr("You break free from the net!");
+ you.attribute[ATTR_HELD] = 0;
+ remove_item_stationary(mitm[net]);
+ return;
+ }
+ }
+}
+
+bool trap_item(object_class_type base_type, char sub_type,
+ char beam_x, char beam_y)
+{
+ item_def item;
+
+ item.base_type = base_type;
+ item.sub_type = sub_type;
+ item.plus = 0;
+ item.plus2 = 0;
+ item.flags = 0;
+ item.special = 0;
+ item.quantity = 1;
+
+ if (base_type == OBJ_MISSILES)
+ {
+ if (sub_type == MI_NEEDLE)
+ set_item_ego_type( item, OBJ_MISSILES, SPMSL_POISONED );
+ else
+ set_item_ego_type( item, OBJ_MISSILES, SPMSL_NORMAL );
+ }
+ else
+ {
+ set_item_ego_type( item, OBJ_WEAPONS, SPWPN_NORMAL );
+ }
+
+ item_colour(item);
+
+ if (igrd[beam_x][beam_y] != NON_ITEM)
+ {
+ if (items_stack( item, mitm[ igrd[beam_x][beam_y] ] ))
+ {
+ inc_mitm_item_quantity( igrd[beam_x][beam_y], 1 );
+ return (false);
+ }
+
+ // don't want to go overboard here. Will only generate up to three
+ // separate trap items, or less if there are other items present.
+ if (mitm[ igrd[beam_x][beam_y] ].link != NON_ITEM
+ && (item.base_type != OBJ_MISSILES || item.sub_type != MI_THROWING_NET))
+ {
+ if (mitm[ mitm[ igrd[beam_x][beam_y] ].link ].link != NON_ITEM)
+ return (false);
+ }
+ } // end of if igrd != NON_ITEM
+
+ return (!copy_item_to_grid( item, beam_x, beam_y, 1 ));
+} // end trap_item()
+
+// returns appropriate trap symbol for a given trap type {dlb}
+dungeon_feature_type trap_category(trap_type type)
+{
+ switch (type)
+ {
+ case TRAP_TELEPORT:
+ case TRAP_AMNESIA:
+ case TRAP_ZOT:
+ return (DNGN_TRAP_MAGICAL);
+
+ case TRAP_DART:
+ case TRAP_ARROW:
+ case TRAP_SPEAR:
+ case TRAP_AXE:
+ case TRAP_BLADE:
+ case TRAP_BOLT:
+ case TRAP_NEEDLE:
+ case TRAP_NET:
+ default: // what *would* be the default? {dlb}
+ return (DNGN_TRAP_MECHANICAL);
+ }
+} // end trap_category()
+
+// returns index of the trap for a given (x,y) coordinate pair {dlb}
+int trap_at_xy(int which_x, int which_y)
+{
+
+ for (int which_trap = 0; which_trap < MAX_TRAPS; which_trap++)
+ {
+ if (env.trap[which_trap].x == which_x &&
+ env.trap[which_trap].y == which_y &&
+ env.trap[which_trap].type != TRAP_UNASSIGNED)
+ {
+ return (which_trap);
+ }
+ }
+
+ // no idea how well this will be handled elsewhere: {dlb}
+ return (-1);
+} // end trap_at_xy()
+
+trap_type trap_type_at_xy(int x, int y)
+{
+ const int idx = trap_at_xy(x, y);
+ return (idx == -1? NUM_TRAPS : env.trap[idx].type);
+}
+