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
4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC
5ZYOO2RVGH53ONV4U5NO6MTINQXET6GV6TETPVCFAVOX44LDNR4QC
RRFPZKCRHPOJH4LDHYMJIQLN2ASWVJBZQW6DDUTBNQRDD7KMEAYAC
557IY36VX2M4ERVCP5HNI3YTHHVTHPETZ5RWDX5BKDKXXIYCFUJAC
5XSXMOBGXFLTIQE6WDXWWFVDOTUPZSIQ2FWT3YI5QMVU6D76IUYQC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
ZJIGDAAKOHJWPD7TMNTNKP7ZT4K4GHD7UGSAWERTIJA3BGL5BETQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
I2B33Z7NZGC33AMDSSK446AZZYWKPHWLAGULVHKKZU4MVB4BNJOAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
QDWDUURSNLMT6AXNNJ3DEQCWAKCAIHV6MP5F7QGIBGXOG2BI2NPQC
MX2KOWAIE37VLSQJWXVEDVQVB3DL325Q7YFUSSHYMAIH53Y74IJQC
AOAJ6D3OKSELEYKAT55XCVU5LYJ7SMCZKC6DIEGLLB3TF2LEENWQC
NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC
X5WLJCJVW55SXZVP7IKP7ADCJIGNKN4PKAXFECVR6TNK7XSMZR7QC
GS2I6EO4OQZ5FHQJQ6YRUDPSJ5XWGLHN544MVQPSF2PATUSJFSSAC
3QLM46S44Z7GDLWPH3VHBMW2RSWZAOLGJMG2BDKNGUOZIM4IX6WAC
DDU4A3JGN5IUIPP5IASOODKPR2WBHSDSV4FITZ6HNXNSXXQACWAQC
QS3ZRS3E6KL3YJHPKYEWCWJYRBJSXD5OOYF6Y25HZVECGPJRDB5QC
6GT5JAWOIIL4SQ5MWIID6ZVO3KKQFWDQDZNVFHZ6DNK5QCBXJ4UAC
5KJCHLIUFKRPMIVWUAYT6EOF7SW4PTQF6Y5OPEFWXGLE7DUGYLZAC
5V47S4NNTHWTSAHV3YLO2VGH7JTUIYJ3GBPDN5ZM4UQALT2ZEXDQC
DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC
HJV7BZBM752K5I47ILBQJJXSPODBBLGKDX5DWJTRDXPJ3V7AEVWAC
OP6CTAKWCAU64JXQ3USQYR5E5IFHQHNCACII5UMVRXUTZXJQOAZAC
ILOED4VB4I6VPAUTR75ZWX6MXDYXB5DO2EDK2UH67O3HNKWV23RQC
L6O4LGZRKBURVWEY7XRVCSQLJ5RULNBEWMQJ6I2UYVWWB66FM3MQC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
VVEULZ7FMS53F6WZUJLNJ23URJMCCWHBUEDVGKI6R72JO2DLL5HQC
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;
}
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);
}
// 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;
}
}
if (mons_is_confused( monster )
|| (monster->type == MONS_AIR_ELEMENTAL
&& monster->has_ench(ENCH_SUBMERGED)))
{
std::vector<coord_def> moves;
if (mons_is_confused( monster )
|| (monster->type == MONS_AIR_ELEMENTAL
&& monster->has_ench(ENCH_SUBMERGED)))
{
std::vector<coord_def> moves;
int pfound = 0;
for (int yi = -1; yi <= 1; ++yi)
{
for (int xi = -1; xi <= 1; ++xi)
{
coord_def c = monster->pos() + coord_def(xi, yi);
if (in_bounds(c) && !grid_is_solid(grd(c))
&& one_chance_in(++pfound))
{
mmov_x = xi;
mmov_y = yi;
}
}
}
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;
}
}
}
// 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;
}
// 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 (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]);
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;
}
}
if (brkk)
continue;
brkk = true;
mmov_x = 0;
mmov_y = 0;
}
}
// 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))
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;
if (monster->x + mmov_x == you.x_pos
&& monster->y + mmov_y == you.y_pos)
{
bool isFriendly = mons_friendly(monster);
bool attacked = false;
if (!isFriendly)
if (monster->x + mmov_x == you.x_pos
&& monster->y + mmov_y == you.y_pos)
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);
}
// 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;
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;
}
}
}
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;
}
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!");
}
}
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();
}
// 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;
}
}
}
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;
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;