Traps now remember how much ammo they have. The ammo quantities (from David) are rather tentative.
Breaks savefiles.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7076 c06c8d41-db1a-0410-9941-cceddc491573
CK7CT5TUFUL2AQY7FUHB5JI3FC2KSPWUWHXC6VEUJJ7G4OWUQFTAC
GMHNPQVDPSYULW7PQC6HNDQDCSFBQH7CFAATVQBFJGCOIJI77C2QC
7QKUHGWIEHOXALP3ZE4UZO435KJX2REQE4WGO6EF5RKZEFIMZ6IQC
FA4GXCEU3UQ7ODF3FOL2ZWCGIPKYDPI4DGOGY6EZK2GA2JOWQPZAC
YXIS433HOV7EZPNYWGA4ZXOKLTQWU55G3HFEN22IBI5ZXTCRIKAAC
NZBX4WLFXYQYYA5WEAXANO4QOUJQ6CUIACWZ7FPRNA2ABJ4UBWKQC
XKQ2XYVSHYRTEKIYXFLXCPULEEO6IQUSKQHX32UZGNMAMQN2CTMQC
INJZPX5A2B2A7P7TDYSAFJ2TPVHLMEY3H6CKJFHY4UMZHAQXI6ZQC
3TGQCOCG4VDWVRILHFOH45Z7KTSLPGP4R3OLPGFAFLWDHXRVJDHQC
QD72QU3HMLD7JO2ZD6BAQQNEFKTDOH7BDVMVGL4TD65JGOZ5P7IAC
33TIRHHIR5EJQI67N2MQJHNY3RC43WZRZX6GAU2PX7T6RSAQXBNAC
RWCCZ64BG3HSOTM54ANIGENC3F3AIR42LJFRYSAKMCPCIUSOZY5QC
K7J2IGELTIQL5KAMBV62MFVIVMEZEWCOLZ7GHFXHYPANYIF7BRZAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC
VJDLV4PF2ZJ46NERO4LZUX5JE2CD5XBOBQCYYPTFWTHLUEYDUOYAC
TFZ4TER7O2Z4FOGF2RCPEPYIHBTUA4LG3ECXLR7XGLCC6GO6OOTAC
BBQWA33DPXWEDYOOPO45VAYD4JQDZQVMZSEJ4RUI5LLC733IMIIQC
QEEJFAETO6B2J4IWDIDCJ5UNIFNNHHG22IWF2CUJRTJJBNE47CWQC
UOUDDVHCP2526KI2KRMYS5NDRG5AL3FLPSAHKTVAQOXYVI5XHSVAC
FZKMVCODMWQEVVBBQHTTXFBRO4LZEYLB646ZPVUMLHH6EER7FAOAC
HAMUM5IA4BJMLRIME7KVVVYLRHVS55RIYMGLLQZL3F7QEP7FJ5KAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
PHBACPMH3F34GODHVDKNCMXWU373RJQGVTDLBFCCDLLWDXVYOLTAC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
LGBOBJZ37D3LFSP62KD4IUA37O5ON4UDP724JHE7HZRTBSB27JZQC
ZCRK2DJ5VKECRQXZTWT4NUDL2VT5ZHUK7NT6NQPLRJ56TDX5PJSAC
5XNQ3SSNBFXFNWA6DPM74W6FH65NX665P3DMH6YCWVFOPZTJSYCQC
KEANRIMF5CGFVZ2XJYNFPOAKLXOSOJUOVA73IWBWOG576265ERHAC
MJWFTUS66PTCNEYXEJA3CUJFXNWXIKDD6H3V24PW7HK64NSVOFSAC
AVCMVFA3MKCXHO6H44UK5KJNIHTGQV7UA7GYXM26VI6TXXU5ZN6QC
QUHEENPY2PYTEUS7C4R3CKE4FHSHGR2BIXB6EW676ESX3727PA3AC
S2LIBA2CLTZ6ZU66AUZ2CCNLCDOBSGWQGTZ6HFAFP2XSWAALGLSQC
3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC
JAYS2VMYWPH2YKIDWEXW2NOK4ATTIXTGZTXUVDDIQGCXLB5W4OUQC
L57WEZMVIHTFRLWVIPWOIOWHCF2I53TVUVYJ2V6IZ64R56FVTZZAC
DHXIQXK5VTL5JDDEERAVLZK4KBQLXFTBN5TQ6MH3PRBLG5ULOS4AC
T7CUIVICB74342RA32BR37T36FOX4RBSQIB5PNOHTGTGUYGDKSTQC
JA465VRRBEMSFQNUXRHTMUQ7OEBUE4GBPXYDQO5JXMAM3TDKQBLAC
Q3ESS2G3M4LBHAIGM66QE2GXWVC67KSR2AXU3MNJEKSP4XKUGXWQC
45EMD3KLQPMERNMIKU5G76H6556XOMIW352TSBP7VLWJX2YYGS7AC
SELQ6AD2GNYLGHJVONUUFDHSCMGYMMNQF5D72D7KTAYOV36T2BBAC
AS2IQQJNNCEQNXXKTGYHLB7RO3ZKCF4F7GK6FJH66BOOKDDRGNIQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
ILOED4VB4I6VPAUTR75ZWX6MXDYXB5DO2EDK2UH67O3HNKWV23RQC
PSCYVKJ7DGXAL3V5U4O6AJTRV6Q3N3SHQWAZ73VIPRTE4W64F2XAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
LDBTCT5WIPLJPZWXS2RUQ26QKISCUUTLO77M464WOE6VSYSNPKYAC
S5CIJLSFFXV445G6CH2AQ3YDU3P7RLSIHWV7GBJ4MZ4C26BWCY6AC
7YUGK5Q64KG5O7GJGTUBRRLHAHBCJ5YOE23YUPT6UBKUSB67CYAQC
SRYIUTTRZYQDYPLLXYKEHMYWC4EGBYPOT65M2HZEIGFG6SDMV7SQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
X7X6JKZXOCG6NVLZLGNCQDFFXW4H3S77BVAJ5LS7ZHZ4GQVLMOMAC
GJAFXCDBIGFUMCD6IJ523WAT754VOJSUXPNN7ZBECYRRP76CARPAC
NWUWP5QCMJ7ZZMLCVB74PW7HDEFB6ICEQHNDHCGG7UWDQNNLGVMQC
PL6I2CMSTHY5ZHWVMIQE5YTM5S5VPKBNZM6QJVHZSSKOJGIJ5W4AC
XI7U6RL6MFTH6SZQMLO4XFWGXGIFJBCRT6ZFCJFR2B2MGRSKDDCAC
RM2JXW3ATVYRYHF3NMG5ALGI64OJ7IP2F3MDUDPUT5TBKSSN4KVQC
ILO5P5FRDUY4SEQEXMTUDKOPXAUBJRSORHHUVTLLCYOYSXBXOVRAC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
XIXBLWIVUPWM3P6XQIB7CS45JOOIYE6OBYYYERCFAYYO4VOWOYUQC
VQZEJTCSRZ5RVLCCKUCIS3IVZPFYRC26I74X4WIJAPEY7NYID2UQC
JFIJGK5PIN2ZQZWPXD4JDT3LAGZ567TOSTGESLA5UDUABEJODKUAC
Y2NYY7HWFZ2LQDK3ACSLGS37F2J2IJ5LRGCIMZYXLEOSVPD3A4DAC
DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC
OK6A6PMHOYIQIPXK32AKOH55QRFWQA43BJWHUOCZA2VIJ6NROHMQC
SJDOBXECX7F3ZPPVR7FPURE53D47PP2TFIF4SB6XPS456PZV3DQAC
ICPELTV6YVJTUITAKA4JXBHQPKG5OC3Z2V2KRCTNM7AEKHN6I2GAC
FIYBXLWALQINNQTHG2KNDUUTAQAZRDDLXW2XOVSKDKBADJ3XCJ4AC
XRZPPYWPWUOM4SFNI6BHKH2UKJQNLKOV6Y7XIEPEZXE5QYRT26PAC
RSIUBEQUGNU4LO6KH4PKVROWQS33DAKSY4XFVGN7T3CEKSXABCSAC
E5DMZFW6WCFAKTKKOQPYTQXZ2CGLWMVH64LRXDUI2UIG4VYUHIVQC
S26C7QFN3OBMIHF5XRB5PKRCQVBY22OLUIJKTD4CEMHKWCWE457AC
4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC
X76YXE6RFL7QY5WL6MSSS44WHVA5FQMDQY3XTYP2UL6UZB4E6XMQC
BRGAZR5AXWC2IALBVXO5SB354IRQEIRHSK55RZPGFI4AGIOD4LUQC
OVDIHWR5FEPTBZCTEAXNPB4MYP5NUNPQMC6UM4F6SC4EBGWH3DSQC
YCL3W2PFE6ILTGBFODCSXNPDIA46KVSZP2TI7HDMYAOEJT65RIEAC
KN5WFFRSNGHIGI5IUYRBOA4R4K7XK3AEAAMN6ELDFTSYKPN7QX7AC
FJQ2RENS2ATHQZUNEE5ZTSTHZ5VI6B36TQPAG3IUVBTULGVCCQ7AC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
NV34PXZWSDIIPPJ3HHQIHB4M5UV66QIHOCUEHCSAACMBM765GX4QC
G5G4JTXF6JP3GN7ESZTDLEDNJMTLCCE6BCDBORNAEBOUO5WYHAPQC
LOEVXFCKSHE3TCQSUSHOIHHVDJT3RFLE6U3VDI6JXAQTQI5KCJ4AC
T2AYVN57EFJQLFUFLAZDXKDAFDGTDLQIEQWQZNYFWJZBYSTYH4QQC
2SFHLF7KGHAKSXERQRCHYY3BFDOZCU5RB6KTSHMFMAF76R7DV4KQC
NW6P7VRKBFBQ2FRDZBXN7APJZPYC4SYCIA3MOOE2KZGX43IXNK2AC
KZIHM6RUX43HHKXG6HGJHVEEYUPVVNBFIWMT4SKPD2GAH5ZMA3KAC
F4FAPEZ5P5CPHZIHUSRYULB3LY4LOJCUC7DZAYVL77LFFBUBCUNAC
LTX72QGIPNUGWQN5ULPOMFCOPZTK7472DQY4AYX5WM3WHSUVXI5QC
EHP6PYCIPYQ3KF4JFGBTZXEUQHN3FVAH4NUWEOWDDNKGPYVOTOJQC
3HGELZU7NELOQ635HZO6IJIYLBSNCJ5VPH46IE22KA3OSLEFK7AQC
R2DQBWKIW7YUJB5SOQ7J274JIYRVX4H3ISFRPAL5RG2RVVP4G2KAC
GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC
WDEFQ6YABDQIGJXW5KT3OGR3EO6FZHXZELIRVIXQ4XDYTVOV5V6AC
MDFQRJ6QZNFUBVSFWLXUJ6EBXOU47T3CVDI2XKBGNNRF4DXDKESQC
5DIBLX3RXK752JKG2BQQM76GYEOMQ5E2YKTLROH3QQGHCVLAGRJQC
Y4NA3JSN63RLATF4NNBPSR5CWF5Z7UEMWCGVX4B6NOAR47CGM4GQC
D7SLVLRNCYCBDYYRANHDG3JYEF25CFCSUY5FMF5KXVD5D4UZSDDAC
32PXX2XJVV7YSLLYNAVS7RYKYRAOQ565TZMTITSEPSSXOYPB5M2AC
AV3TMWHWB3XBXQCT34UPMZBSIIKVXIGWQPNEFU4CZSBS3ZOF2CUQC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
WT66JDIRTLLP37SHTV4GI3V64JFJ4D25LNRLGCHFG6CLEFKJ3QGQC
67WH6I2XIMRRL26UM4FJJJZEMJIDPW5Q77FFNZGKWGTWS7GTCJ6QC
W7KGGF2VUXLD6YH55EPIRQ5SF5VKVKT33P6RNKCFCVQ4QXOLQE7AC
ZBPS5ZTPF3DVTR5WET4XEFHYXU26CRHU2OHX3YO6PD4MTM2DUXAQC
WRPGGZRVGWWYCBWL5W5HMAM7CWLYICKZ6A36ZGQ3UEDW5O25FMOQC
H55P74Y6NHAPF3VPWXIAP7DODA3DV7NCT3SK6VVLWNNWZ7JIMBTAC
D27U7RT2C77NEUBP6JCSQJ2DRCJVHOXUO2PFZ45VFYMEVMKI4TSAC
UEI5JAVCMN7Y2SACTEZPZSNFJWOJTC55G24Q6LKQCT4XNDH5ZQIAC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
4FQAKUKUO6PCAZ3N4HUR5XL6E4VA5UQUZ3AEDGRBLVY7W2LMWI7QC
3KAINFIXO7WNWGUGZB43EUNFRS2ZPBLQZDTY456QACMRHYIJ7WDAC
VXMRWJ2CPHIB2NAHMZKUV7DAFA7GLVA32WDS53WNT3BIDHK6GOZQC
7THPBJV4J3WDSXWL2XOYYN7RZQILOCPKFWQRYKBA74J7WWBFRLSQC
X5OHFTHRXTI5KHFIYFJ6DB3W5MY3JUJKNTL4HF37MKABYNCOBOZAC
77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC
C3OUSRCHDUOJ6ELVDX6YWMQQVXDJLNEXTXWCDDAYWTKNPFPNSONAC
475LL4U4ND6PTNV4XKC7WQAOJC7RF2VCCVX3DRILP2PKIBFYWE6QC
UZ6N6HOUPGVSPC5NQROEEDWMEGJA5XUWUY2AKH5QG65AZ25PVXDAC
void itrap(struct bolt &pbolt, int trapped);
void destroy_trap( const coord_def& pos );
dungeon_feature_type trap_category(trap_type type);
int trap_at_xy(const coord_def& xy);
trap_type trap_type_at_xy(const coord_def& xy);
void destroy_trap(const coord_def& pos);
trap_def* find_trap(const coord_def& where);
trap_type get_trap_type(const coord_def& where);
static void dart_trap(bool trap_known, int trapped, bolt &pbolt, bool poison);
bool trap_def::active() const
{
return (this->type != TRAP_UNASSIGNED);
}
bool trap_def::type_has_ammo() const
{
bool rc = false;
switch (this->type)
{
case TRAP_DART: case TRAP_ARROW: case TRAP_BOLT:
case TRAP_NEEDLE: case TRAP_SPEAR: case TRAP_AXE:
rc = true;
default:
break;
}
return rc;
}
void trap_def::message_trap_entry()
{
if (this->type == TRAP_TELEPORT)
mpr("You enter a teleport trap!");
}
void trap_def::disarm()
{
if (this->type_has_ammo() && this->ammo_qty > 0)
{
item_def trap_item = this->generate_trap_item();
trap_item.quantity = this->ammo_qty;
copy_item_to_grid(trap_item, this->pos);
}
this->destroy();
}
void trap_def::destroy()
{
grd(this->pos) = DNGN_FLOOR;
this->ammo_qty = 0;
this->pos = coord_def(-1,-1);
this->type = TRAP_UNASSIGNED;
}
void trap_def::hide()
{
grd(this->pos) = DNGN_UNDISCOVERED_TRAP;
}
void trap_def::prepare_ammo()
{
switch (this->type)
{
case TRAP_DART:
case TRAP_ARROW:
case TRAP_BOLT:
case TRAP_NEEDLE:
this->ammo_qty = 3 + random2avg(9, 3);
break;
case TRAP_SPEAR:
case TRAP_AXE:
this->ammo_qty = 2 + random2avg(6, 3);
break;
default:
this->ammo_qty = 0;
break;
}
}
void trap_def::reveal()
{
const dungeon_feature_type cat = this->category();
grd(this->pos) = cat;
set_envmap_obj(this->pos, cat);
}
std::string trap_def::name(description_level_type desc) const
{
if (this->type >= NUM_TRAPS)
return ("buggy");
const char* basename = trap_name(this->type);
if (desc == DESC_CAP_A || desc == DESC_NOCAP_A)
{
std::string prefix = (desc == DESC_CAP_A ? "A" : "a");
if (is_vowel(basename[0]))
prefix += 'n';
prefix += ' ';
return (prefix + basename);
}
else if (desc == DESC_CAP_THE)
return (std::string("The ") + basename);
else if (desc == DESC_NOCAP_THE)
return (std::string("the ") + basename);
else // everything else
return (basename);
}
bool trap_def::is_known(const actor* act) const
{
bool rc = false;
const bool player_knows = (grd(pos) != DNGN_UNDISCOVERED_TRAP);
if (act == NULL || act->atype() == ACT_PLAYER)
rc = player_knows;
else if (act->atype() == ACT_MONSTER)
{
const monsters* monster = static_cast<const monsters*>(act);
const bool mechanical = (this->category() == DNGN_TRAP_MECHANICAL);
const int intel = mons_intel(monster->type);
// Smarter trap handling for intelligent monsters
// * monsters native to a branch can be assumed to know the trap
// locations and thus be able to avoid them
// * friendlies and good neutrals can be assumed to have been warned
// by the player about all traps s/he knows about
// * very intelligent monsters can be assumed to have a high T&D
// skill (or have memorised part of the dungeon layout ;) )
rc = (intel >= I_NORMAL && mechanical
&& (mons_is_native_in_branch(monster)
|| (mons_wont_attack(monster) && player_knows)
|| (intel >= I_HIGH && one_chance_in(3))));
}
return rc;
}
if (random2( 20 + 5 * you.shield_blocks * you.shield_blocks )
< player_shield_class())
// Only magical traps affect flying critters.
if (triggerer.airborne() && this->category() != DNGN_TRAP_MAGICAL)
msg += "hits you!";
mpr(msg.c_str());
// Alarm traps aren't set off by hostile monsters, because
// that would be way too nasty for the player.
const char* message_here = "An alarm trap emits a blaring wail!";
const char* message_near = "You hear a blaring wail!";
const char* message_far = "You hear a distant blaring wail!";
noisy(12, this->pos, (you_trigger ? message_here :
(in_sight ? message_near : message_far)));
}
break;
if (poison && x_chance_in_y(50 - (3 * player_AC()) / 2, 100)
&& !player_res_poison())
case TRAP_BLADE:
if (you_trigger)
{
if (trig_knows && one_chance_in(3))
mpr("You avoid triggering a blade trap.");
else if (random2limit(player_evasion(), 40)
+ (random2(you.dex) / 3) + (trig_knows ? 3 : 0) > 8)
damage_taken = roll_dice( pbolt.damage );
damage_taken -= random2( player_AC() + 1 );
if (damage_taken > 0)
else
ouch(damage_taken, NON_MONSTER, KILLED_BY_TRAP,
pbolt.name.c_str());
mpr("A huge blade swings out and slices into you!");
const int damage = (you.your_level * 2) + random2avg(29, 2)
- random2(1 + player_AC());
ouch(damage, NON_MONSTER, KILLED_BY_TRAP, " blade");
bleed_onto_floor(you.pos(), -1, damage, true);
msg += "misses you.";
mpr(msg.c_str());
}
if (player_light_armour(true) && coinflip())
exercise( SK_DODGING, 1 );
}
pbolt.target = you.pos();
if (coinflip())
itrap( pbolt, trapped );
} // end dart_trap()
//
// itrap takes location from target_x, target_y of bolt strcture.
//
if (one_chance_in(5) || (trig_knows && coinflip()))
{
// Trap doesn't trigger. Don't reveal it.
if (you_know)
{
simple_monster_message(m,
" fails to trigger a blade trap.");
}
else
this->hide();
}
else if (random2(m->ev) > 8 || (trig_knows && random2(m->ev) > 8))
{
if (in_sight &&
!simple_monster_message(m,
" avoids a huge, swinging blade."))
{
mpr("A huge blade swings out!");
}
}
else
{
if (in_sight)
{
std::string msg = "A huge blade swings out";
if (player_monster_visible( m ))
{
msg += " and slices into ";
msg += m->name(DESC_NOCAP_THE);
}
msg += "!";
mpr(msg.c_str());
}
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;
if (damage_taken < 0)
damage_taken = 0;
if (!mons_is_summoned(m))
bleed_onto_floor(m->pos(), m->type, damage_taken, true);
hurt_monster(m, damage_taken);
if (m->hit_points < 1)
monster_die(m, KILL_MISC, NON_MONSTER);
}
}
base_type = OBJ_MISSILES;
sub_type = MI_THROWING_NET;
break;
default:
return;
}
if (you_trigger)
{
if (trig_knows && one_chance_in(3))
mpr("A net swings high above you.");
else
{
if (random2limit(player_evasion(), 40)
+ (random2(you.dex) / 3) + (trig_knows ? 3 : 0) > 12)
{
mpr("A net drops to the ground!");
}
else
{
mpr("A large net falls onto you!");
player_caught_in_net();
}
void handle_traps(trap_type trt, int i, bool trap_known)
{
struct bolt beam;
bool branchtype = false;
// Mark traps as racial, if applicable. See the list of racial
// restrictions in _determine_weapon_race() and
// _determine_missile_race() in makeitem.cc.
if (trap_category(trt) == DNGN_TRAP_MECHANICAL && trt != TRAP_BLADE
&& trt != TRAP_NET)
{
if (you.where_are_you == BRANCH_ORCISH_MINES)
{
beam.name = "n orcish";
branchtype = true;
this->destroy();
}
beam.name = "n elven";
branchtype = true;
}
else
beam.name = "";
}
bool triggered = true;
if (one_chance_in(3) || (trig_knows && coinflip()))
{
triggered = false;
if (you_know)
simple_monster_message(m, " fails to trigger a net trap.");
}
if (random2(m->ev) > 8
|| (trig_knows && random2(m->ev) > 8))
{
if (in_sight)
{
if (!simple_monster_message(m,
" nimbly jumps out of the way "
"of a falling net."))
{
mpr("A large net falls down!");
}
}
else
{
// FIXME: net traps don't trigger unless you can see
// them? Preserving old behaviour here.
if (in_sight)
{
msg::stream << "A large net falls down";
if (player_monster_visible(m))
msg::stream << " onto " << m->name(DESC_NOCAP_THE);
msg::stream << "!" << std::endl;
}
// FIXME: Fake a beam for monster_caught_in_net.
bolt beam;
beam.flavour = BEAM_MISSILE;
beam.thrower = KILL_MISC;
beam.beam_source = NON_MONSTER;
monster_caught_in_net(m, 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;
if (triggered)
{
item_def item = this->generate_trap_item();
copy_item_to_grid(item, triggerer.pos());
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 += (branchtype? "" : "n");
beam.name += " 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 += (branchtype? "" : "n");
beam.name += " 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_ALARM:
if (silenced(you.pos()))
{
if (trap_known)
mpr("The alarm is silenced.");
else
grd(you.pos()) = DNGN_UNDISCOVERED_TRAP;
return;
if (mons_is_caught(m))
mark_net_trapping(m->pos());
this->destroy();
}
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)
case TRAP_ZOT:
if (you_trigger)
mpr("A huge blade swings out and slices into you!");
int damage = (you.your_level * 2) + random2avg(29, 2)
- random2(1 + player_AC());
ouch(damage, NON_MONSTER, KILLED_BY_TRAP, " blade");
bleed_onto_floor(you.pos(), -1, damage, true);
}
break;
// Zot traps are out to get *the player*! Hostile monsters
// benefit and friendly monsters suffer. Such is life.
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)
// Preserving original functionality: don't reveal location.
if (!you_know)
this->hide();
if (mons_wont_attack(m))
{
MiscastEffect( m, ZOT_TRAP_MISCAST, SPTYP_RANDOM,
3, "the power of Zot" );
}
else if (in_sight)
trap_item( OBJ_MISSILES, MI_THROWING_NET, env.trap[i].pos);
if (you.attribute[ATTR_HELD])
mark_net_trapping(you.pos());
grd(env.trap[i].pos) = DNGN_FLOOR;
env.trap[i].type = TRAP_UNASSIGNED;
if (trap_known && !you.airborne())
mpr("You don't fall through the shaft.");
if (!trap_known)
grd(you.pos()) = DNGN_UNDISCOVERED_TRAP;
return;
if (!you_know)
this->hide();
else if (!triggerer.airborne())
{
if (you_trigger)
{
mpr("You don't fall through the shaft.");
}
else if (m)
{
simple_monster_message(m,
" doesn't fall through the shaft.");
}
}
learned_something_new(TUT_SEEN_TRAP, you.pos());
if (you_trigger)
{
learned_something_new(TUT_SEEN_TRAP, this->pos);
// Exercise T&D if the trap revealed itself.
if (!you_know && this->is_known())
exercise(SK_TRAPS_DOORS, ((coinflip()) ? 2 : 1));
}
}
if (!trap_known) // Now you know...
exercise(SK_TRAPS_DOORS, ((coinflip()) ? 2 : 1));
int trap_def::shot_damage(actor& act)
{
if (act.atype() == ACT_PLAYER)
{
switch (this->type)
{
case TRAP_NEEDLE: return 0;
case TRAP_DART: return random2( 4 + you.your_level/2) + 1;
case TRAP_ARROW: return random2( 7 + you.your_level) + 1;
case TRAP_SPEAR: return random2(10 + you.your_level) + 1;
case TRAP_BOLT: return random2(13 + you.your_level) + 1;
case TRAP_AXE: return random2(15 + you.your_level) + 1;
default: return 0;
}
}
else if (act.atype() == ACT_MONSTER)
{
// Trap damage to monsters is not a function of level, because
// they are fairly stupid and tend to have fewer hp than
// players -- this choice prevents traps from easily killing
// large monsters fairly deep within the dungeon.
switch (this->type)
{
case TRAP_NEEDLE: return 0;
case TRAP_DART: return random2( 4) + 1;
case TRAP_ARROW: return random2( 7) + 1;
case TRAP_SPEAR: return random2(10) + 1;
case TRAP_BOLT: return random2(13) + 1;
case TRAP_AXE: return random2(15) + 1;
default: return 0;
}
}
return (0);
{
grd(pos) = DNGN_FLOOR;
env.trap[i].type = TRAP_UNASSIGNED;
return;
}
}
return (&env.trap[i]);
return (NULL);
}
trap_type get_trap_type(const coord_def& pos)
{
if (trap_def* ptrap = find_trap(pos))
return (ptrap->type);
return (TRAP_UNASSIGNED);
for (i = 0; i < MAX_TRAPS; i++)
{
if (env.trap[i].pos == you.pos() + disa.delta)
break;
if (i == MAX_TRAPS - 1)
{
mpr("Error - couldn't find that trap.", MSGCH_ERROR);
return;
}
}
trap_def& trap = *find_trap(where);
mpr("You have disarmed the trap.");
bolt beam;
beam.target = you.pos() + disa.delta;
if (env.trap[i].type == TRAP_NET)
trap_item( OBJ_MISSILES, MI_THROWING_NET, beam.target );
else if (env.trap[i].type != TRAP_BLADE
&& trap_category(env.trap[i].type) == DNGN_TRAP_MECHANICAL)
else
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);
}
mpr("You have disarmed the trap.");
trap.disarm();
exercise(SK_TRAPS_DOORS, 1 + random2(5) + (you.your_level/5));
grd(you.pos() + disa.delta) = 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));
item.base_type = base_type;
item.sub_type = sub_type;
item.plus = 0;
item.plus2 = 0;
item.flags = 0;
item.special = 0;
object_class_type base;
int sub;
switch (this->type)
{
case TRAP_DART: base = OBJ_MISSILES; sub = MI_DART; break;
case TRAP_ARROW: base = OBJ_MISSILES; sub = MI_ARROW; break;
case TRAP_BOLT: base = OBJ_MISSILES; sub = MI_BOLT; break;
case TRAP_SPEAR: base = OBJ_WEAPONS; sub = WPN_SPEAR; break;
case TRAP_AXE: base = OBJ_WEAPONS; sub = WPN_HAND_AXE; break;
case TRAP_NEEDLE: base = OBJ_MISSILES; sub = MI_NEEDLE; break;
case TRAP_NET: base = OBJ_MISSILES; sub = MI_THROWING_NET; break;
default: return item;
}
item.base_type = base;
item.sub_type = sub;
// give appropriate racial flag for Orcish Mines and Elven Halls
// should we ever allow properties of dungeon features, we could use that
if (you.where_are_you == BRANCH_ORCISH_MINES)
set_equip_race( item, ISFLAG_ORCISH );
else if (you.where_are_you == BRANCH_ELVEN_HALLS)
set_equip_race( item, ISFLAG_ELVEN );
if (items_stack( item, mitm[ igrd(where) ] ))
if (act.atype() == ACT_PLAYER)
mpr("The trap is out of ammunition!");
else if (player_can_hear(this->pos))
mpr("You hear a soft click.");
this->disarm();
}
else
{
item_def shot = this->generate_trap_item();
bool poison = (this->type == TRAP_NEEDLE);
int damage_taken =
this->shot_damage(act) - random2(act.armour_class()+1);
if (damage_taken < 0)
damage_taken = 0;
if (act.atype() == ACT_PLAYER)
inc_mitm_item_quantity( igrd(where), 1 );
return (false);
if (one_chance_in(5) || (was_known && !one_chance_in(4)))
{
mprf( "You avoid triggering %s trap.",
this->name(DESC_NOCAP_A).c_str() );
return; // no ammo generated either
}
// Start constructing the message.
std::string msg = shot.name(DESC_CAP_A) + " shoots out and ";
// Check for shield blocking.
// Exercise only if the trap was unknown (to prevent scumming.)
if (!was_known && you.shield() && one_chance_in(3))
exercise(SK_SHIELDS, 1);
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.
int trap_hit = (20 + (you.your_level*2)) * random2(200) / 100;
int your_dodge = player_evasion() + random2(you.dex) / 3
- 2 + (you.duration[DUR_REPEL_MISSILES] * 10);
// Check if it got past dodging. Deflect Missiles provides
// immunity to such traps.
if (trap_hit >= your_dodge
&& you.duration[DUR_DEFLECT_MISSILES] == 0)
{
// OK, we've been hit.
msg += "hits you!";
mpr(msg.c_str());
// Needle traps can poison.
if (poison && !player_res_poison()
&& x_chance_in_y(50 - (3*player_AC()) / 2, 100))
{
poison_player(1 + random2(3));
}
ouch(damage_taken, NON_MONSTER, KILLED_BY_TRAP,
shot.name(DESC_PLAIN).c_str());
}
else // trap dodged
{
msg += "misses you.";
mpr(msg.c_str());
}
// Exercise only if the trap was unknown (to prevent scumming.)
if (!was_known && player_light_armour(true) && coinflip())
exercise(SK_DODGING, 1);
}
// 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(where) ].link != NON_ITEM
&& (item.base_type != OBJ_MISSILES
|| item.sub_type != MI_THROWING_NET))
{
if (mitm[ mitm[ igrd(where) ].link ].link != NON_ITEM)
return (false);
monsters* monster = static_cast<monsters *>(&act);
// Determine whether projectile hits.
bool hit = (((20+(you.your_level*2))*random2(200))/100
>= monster->ev);
// Check whether to poison.
if (poison)
poison = (hit && x_chance_in_y(50 - (3*monster->ac)/2, 100));
if (see_grid(act.pos()))
{
mprf("%s %s %s%s!",
shot.name(DESC_CAP_A).c_str(),
hit ? "hits" : "misses",
monster->name(DESC_NOCAP_THE).c_str(),
(damage_taken == 0
&& !poison) ? ", but does no damage" : "");
}
if (poison)
poison_monster(monster, KC_OTHER);
// Apply damage.
hurt_monster(monster, damage_taken);
if (monster->hit_points < 1)
monster_die(monster, KILL_MISC, NON_MONSTER);
// give appropriate racial flag for Orcish Mines and Elven Halls
// should we ever allow properties of dungeon features, we could use that
if (you.where_are_you == BRANCH_ORCISH_MINES)
set_equip_race( item, ISFLAG_ORCISH );
else if (you.where_are_you == BRANCH_ELVEN_HALLS)
set_equip_race( item, ISFLAG_ELVEN );
// Drop the item (sometimes.)
if (coinflip())
copy_item_to_grid(shot, act.pos());
trap = trap_at_xy(spd.target);
if (trap != -1
&& trap_category(env.trap[trap].type) != DNGN_TRAP_MECHANICAL)
{
trap_def* ptrap = find_trap(spd.target);
if (ptrap && ptrap->category() != DNGN_TRAP_MECHANICAL)
// mechanical traps are destroyed {dlb}:
int which_trap = trap_at_xy(*ai);
if ( which_trap != -1 )
{
trap_struct& trap(env.trap[which_trap]);
if (trap_category(trap.type) == DNGN_TRAP_MECHANICAL)
{
trap.type = TRAP_UNASSIGNED;
trap.pos.set(1,1);
}
}
// All traps are destroyed
if (trap_def* ptrap = find_trap(*ai))
ptrap->destroy();
grd(p) = trap_category( env.trap[i].type );
set_envmap_obj(p, grd(p));
set_terrain_mapped(p);
trap.reveal();
set_envmap_obj(trap.pos, grd(trap.pos));
set_terrain_mapped(trap.pos);
if (new_grid >= DNGN_TRAP_MECHANICAL && new_grid <= DNGN_UNDISCOVERED_TRAP)
{
int id = trap_at_xy( you.pos() );
if (id != -1)
{
bool trap_known = true;
if (new_grid == DNGN_UNDISCOVERED_TRAP)
{
trap_known = false;
const dungeon_feature_type type =
trap_category( env.trap[id].type );
grd(you.pos()) = type;
set_envmap_obj(you.pos(), type);
}
// It's not easy to blink onto a trap without setting it off.
if (!stepped)
trap_known = false;
if (trap_def* ptrap = find_trap(you.pos()))
ptrap->trigger(you, !stepped); // blinking makes it hard to evade
// mechanical traps and shafts cannot be set off if the
// player is flying or levitating
if (!player_is_airborne()
|| trap_category( env.trap[id].type ) == DNGN_TRAP_MAGICAL)
{
handle_traps(env.trap[id].type, id, trap_known);
}
}
}
// NB: only works because grid location already verified
// to be some sort of trap prior to function call: {dlb}
void mons_trap(monsters *monster)
{
if (!is_trap_square(grd(monster->pos())))
return;
// single calculation permissible {dlb}
bool monsterNearby = mons_near(monster);
const int which_trap = trap_at_xy(monster->pos());
if (which_trap == -1)
return;
trap_struct& trap(env.trap[which_trap]);
bool trapKnown = (grd(monster->pos()) != DNGN_UNDISCOVERED_TRAP);
bool revealTrap = false; // more sophisticated trap uncovering {dlb}
bool projectileFired = false; // <sigh> I had to do it, I swear {dlb}
int damage_taken = -1; // must initialize at -1 for this f(x) {dlb}
bolt beem;
// flying monsters neatly avoid mechanical traps
// and may actually exit this function early: {dlb}
if (mons_flies(monster))
{
if (trap_category(trap.type) == DNGN_TRAP_MECHANICAL)
{
if (trapKnown)
simple_monster_message(monster, " flies safely over a trap.");
return; // early return {dlb}
}
}
// Trap damage to monsters is not a function of level, because they
// are fairly stupid and tend to have fewer hp than players -- this
// choice prevents traps from easily killing large monsters fairly
// deep within the dungeon.
switch (trap.type)
{
case TRAP_DART:
projectileFired = true;
beem.name = " dart";
beem.damage = dice_def( 1, 4 );
beem.colour = OBJ_MISSILES;
beem.type = MI_DART;
break;
case TRAP_NEEDLE:
projectileFired = true;
beem.name = " needle";
beem.damage = dice_def( 1, 0 );
beem.colour = OBJ_MISSILES;
beem.type = MI_NEEDLE;
break;
case TRAP_ARROW:
projectileFired = true;
beem.name = "n arrow";
beem.damage = dice_def( 1, 7 );
beem.colour = OBJ_MISSILES;
beem.type = MI_ARROW;
break;
case TRAP_SPEAR:
projectileFired = true;
beem.name = " spear";
beem.damage = dice_def( 1, 10 );
beem.colour = OBJ_WEAPONS;
beem.type = WPN_SPEAR;
break;
case TRAP_BOLT:
projectileFired = true;
beem.name = " bolt";
beem.damage = dice_def( 1, 13 );
beem.colour = OBJ_MISSILES;
beem.type = MI_BOLT;
break;
case TRAP_AXE:
projectileFired = true;
beem.name = "n axe";
beem.damage = dice_def( 1, 15 );
beem.colour = OBJ_WEAPONS;
beem.type = WPN_HAND_AXE;
break;
// teleport traps are *never* revealed through
// the triggering action of a monster, as any
// number of factors could have been in play: {dlb}
case TRAP_TELEPORT:
monster_teleport(monster, true);
break;
// Alarm traps aren't set off by hostile monsters, because that would
// be way too nasty for the player.
case TRAP_ALARM:
if (!mons_friendly(monster) || silenced(monster->pos()))
{
if (trapKnown && you.can_see(monster) && !silenced(you.pos()))
{
mpr("The alarm trap makes no noise.");
}
return;
}
noisy(12, monster->pos());
if (!silenced(you.pos()))
{
if (monsterNearby)
{
mpr("You hear a blaring wail!", MSGCH_SOUND);
if (you.can_see(monster))
revealTrap = true;
}
else
mpr("You hear a distant blaring wail!", MSGCH_SOUND);
}
break;
// blade traps sometimes fail to trigger altogether,
// resulting in an "early return" from this f(x) for
// some - otherwise, blade *always* revealed: {dlb}
case TRAP_BLADE:
if (one_chance_in(5)
|| trapKnown && intelligent_ally(monster) && coinflip())
{
if (trapKnown)
{
simple_monster_message(monster,
" fails to trigger a blade trap.");
}
return; // early return {dlb}
}
if (random2(monster->ev) > 8
|| trapKnown && intelligent_ally(monster)
&& random2(monster->ev) > 8)
{
if (monsterNearby && !simple_monster_message(monster,
" avoids a huge, swinging blade."))
{
mpr("A huge blade swings out!");
}
damage_taken = -1; // just to be certain {dlb}
}
else
{
if (monsterNearby)
{
std::string msg = "A huge blade swings out";
if (player_monster_visible( monster ))
{
msg += " and slices into ";
msg += monster->name(DESC_NOCAP_THE);
}
msg += "!";
mpr(msg.c_str());
}
damage_taken = 10 + random2avg(29, 2);
damage_taken -= random2(1 + monster->ac);
if (damage_taken < 0)
damage_taken = 0;
if (!mons_is_summoned(monster))
{
bleed_onto_floor(monster->pos(), monster->type,
damage_taken, true);
}
}
revealTrap = true;
break;
case TRAP_NET:
{
if (one_chance_in(3)
|| trapKnown && intelligent_ally(monster) && coinflip())
{
if (trapKnown)
{
simple_monster_message(monster,
" fails to trigger a net trap.");
}
return;
}
if (random2(monster->ev) > 8
|| trapKnown && intelligent_ally(monster)
&& 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, beem);
}
}
trap_item( OBJ_MISSILES, MI_THROWING_NET, trap.pos );
if (mons_is_caught(monster))
mark_net_trapping(monster->pos());
grd(trap.pos) = DNGN_FLOOR;
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
// an "early return" - zot traps are *never* revealed - instead,
// enchantment messages serve as clues to the trap's presence: {dlb}
case TRAP_ZOT:
if (mons_friendly(monster) || mons_good_neutral(monster))
{
MiscastEffect( monster, ZOT_TRAP_MISCAST, SPTYP_RANDOM,
3, "the power of Zot" );
return; // early return
}
else if (monsterNearby)
{
if (one_chance_in(5))
{
mpr("The power of Zot is invoked against you!");
MiscastEffect( &you, ZOT_TRAP_MISCAST, SPTYP_RANDOM,
3, "the power of Zot" );
return; // early return {dlb}
}
}
// output triggering message to player, where appropriate: {dlb}
if (!silenced(monster->pos()) && !silenced(you.pos()))
{
if (monsterNearby)
mpr("You hear a loud \"Zot\"!", MSGCH_SOUND);
else
mpr("You hear a distant \"Zot\"!", MSGCH_SOUND);
}
// XXX: It seem that back when a beam's colour determined its
// flavour that Zot traps would heal, haste or make invisible
// hostile monsters. The code has been fixed to work but
// commented out.
#if 0
if (!mons_friendly(monster) && !mons_good_neutral(monster))
{
int temp_rand = random2(16);
beem.thrower = KILL_MON; // probably unnecessary
beem.aux_source.clear();
beem.flavour = ((temp_rand < 3) ? BEAM_HASTE : // 3 in 16 {dlb}
(temp_rand < 7) ? BEAM_INVISIBILITY //4 in 16 {dlb}
: BEAM_HEALING); // 9 in 16 {dlb}
mons_ench_f2(monster, beem);
}
#endif
damage_taken = 0; // just to be certain {dlb}
break;
case TRAP_SHAFT:
// Paranoia
if (!is_valid_shaft_level())
{
if (trapKnown && monsterNearby)
mpr("The shaft disappears in a puff of logic!");
grd(trap.pos) = DNGN_FLOOR;
trap.type = TRAP_UNASSIGNED;
return;
}
if (!monster->will_trigger_shaft()
|| trapKnown && intelligent_ally(monster))
{
if (trapKnown && !monster->airborne())
simple_monster_message(monster,
" doesn't fall through the shaft.");
return;
}
if (monster->do_shaft())
revealTrap = true;
break;
default:
break;
}
// go back and handle projectile traps: {dlb}
bool apply_poison = false;
if (projectileFired)
{
// projectile traps *always* revealed after "firing": {dlb}
revealTrap = true;
// determine whether projectile hits, calculate damage: {dlb}
if (((20 + (you.your_level * 2)) * random2(200)) / 100
>= monster->ev)
{
damage_taken = roll_dice( beem.damage );
damage_taken -= random2(1 + monster->ac);
if (damage_taken < 0)
damage_taken = 0;
if (beem.colour == OBJ_MISSILES
&& beem.type == MI_NEEDLE
&& x_chance_in_y(50 - (3 * monster->ac)/ 2, 100))
{
apply_poison = true;
}
}
else
{
damage_taken = -1; // negative damage marks a miss
}
if (monsterNearby)
{
mprf("A%s %s %s%s!",
beem.name.c_str(),
(damage_taken >= 0) ? "hits" : "misses",
monster->name(DESC_NOCAP_THE).c_str(),
(damage_taken == 0) ? ", but does no damage" : "");
}
if (apply_poison)
poison_monster( monster, KC_OTHER );
// Generate "fallen" projectile, where appropriate. {dlb}
if (x_chance_in_y(7, 10))
{
beem.target = monster->pos();
itrap(beem, which_trap);
}
}
// reveal undiscovered traps, where appropriate: {dlb}
if (monsterNearby && !trapKnown && revealTrap)
grd(trap.pos) = trap_category(trap.type);
// apply damage and handle death, where appropriate: {dlb}
if (damage_taken > 0)
{
hurt_monster(monster, damage_taken);
if (monster->hit_points < 1)
{
monster_die(monster, KILL_MISC, NON_MONSTER);
monster->speed_increment = 1;
}
}
}
const bool player_knows_trap = (grd(where) != DNGN_UNDISCOVERED_TRAP);
// Smarter trap handling for intelligent monsters
// * monsters native to a branch can be assumed to know the trap
// locations and thus be able to avoid them
// * friendlies and good neutrals can be assumed to have been warned
// by the player about all traps s/he knows about
// * very intelligent monsters can be assumed to have a high T&D
// skill (or have memorised part of the dungeon layout ;) )
if (intel >= I_NORMAL && mechanical
&& (mons_is_native_in_branch(monster)
|| mons_wont_attack(monster)
&& player_knows_trap
|| intel >= I_HIGH && one_chance_in(3)))
if (trap.is_known(monster))
int trap = trap_at_xy(pos);
if (trap >= 0)
{
if (!_can_place_on_trap(mon_type, env.trap[trap].type))
continue;
}
const trap_def* ptrap = find_trap(pos);
if (ptrap && !_can_place_on_trap(mon_type, ptrap->type))
continue;
int trap = trap_at_xy(mg_pos);
if (trap >= 0)
{
if (!_can_place_on_trap(mg.cls, env.trap[trap].type))
return (false);
}
const trap_def* ptrap = find_trap(mg_pos);
if (ptrap && !_can_place_on_trap(mg.cls, ptrap->type))
return (false);
// A monster can be considered to know a trap if
// a) they're hostile
// b) they're friendly and *you* know about the trap (and told them)
// c) they're friendly and know the terrain
bool knows_trap = (!mons_friendly(mons)
|| grd(npos) != DNGN_UNDISCOVERED_TRAP
|| mons_intel(mons->type) >= I_NORMAL
&& mons_is_native_in_branch(mons));
trap_type tt = env.trap[trap].type;
const bool knows_trap = ptrap->is_known(mons);
const trap_type tt = ptrap->type;
trap_type type;
trap_type type;
int ammo_qty;
dungeon_feature_type category() const;
std::string name(description_level_type desc = DESC_PLAIN) const;
bool is_known(const actor* act = 0) const;
void trigger(actor& triggerer, bool flat_footed = false);
void disarm();
void destroy();
void hide();
void reveal();
void prepare_ammo();
bool type_has_ammo() const;
bool active() const;
private:
void message_trap_entry();
void shoot_ammo(actor& act, bool was_known);
item_def generate_trap_item();
int shot_damage(actor& act);
{
if (grd(*ai) == DNGN_FLOOR
&& trap_at_xy(*ai) == -1
&& one_chance_in(++count))
{
if (grd(*ai) == DNGN_FLOOR && find_trap(*ai) && one_chance_in(++count))
{
const int i = trap_at_xy(f);
if (i != -1) // should always happen
grd(f) = trap_category(env.trap[i].type);
}
find_trap(f)->reveal();