added, with the shaft traps changed as per comments on SF; shafts aren't randomly generated yet, so this doesn't change gameplay. Changed DNGN_TRAP_III to DNGN_TRAP_NATURAL, of which trap type the shaft traps are the only current member.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2328 c06c8d41-db1a-0410-9941-cceddc491573
AVCMVFA3MKCXHO6H44UK5KJNIHTGQV7UA7GYXM26VI6TXXU5ZN6QC
RC5SAVDRSD6X75HG3SEEFC444LXYMHASQFLOJMH6D6VTYHBQAJEQC
WDEFQ6YABDQIGJXW5KT3OGR3EO6FZHXZELIRVIXQ4XDYTVOV5V6AC
TTHOIZMSG2GWUO5IQWOPYK5V5DS6HIPD7ULFI6P6E7LQ6MMKPMHAC
IVVRQ52VA3MAD25ZMPID3GCRBBFYR54CH33DKXYTRAGUBLTRAZNAC
FMSYEPHQUC3RGEOEJ77OZ43JTIFITY56SOOKYPU4CKAVB4VP65FAC
JM7UAK777RAVDAVLQLEOBRTGNW2B47S5G55XITJXO243IUNZHVYQC
Z6Q7JVMFQ32SC7FRGOB7CE7JS2HEOPAO3B2VLU3YR3UCUDZFIPFQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
5ASC3STDYCNLZFEBN6UTMUCGDETHBR2OCBZCF5VIAZ5RRWLOTDYQC
7AMQN7MITMXBNVDAK5VOXTQ4TZIAOD6ZLOFJG7GQMBTY23Y2BKSAC
DOZORMA366M4HB5JKSS27BMCR6ET7QNZNND2B7KV3NVEEPR5H7EAC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
R22TTMI6WXWULC7ODKFF3QCB7MOTETQQ6IR4BUCUPOCQKQNCTT5AC
I2B33Z7NZGC33AMDSSK446AZZYWKPHWLAGULVHKKZU4MVB4BNJOAC
P2ZCF3BBG523ZEOD6XQA4X5YEHBTWH3IM33YVHXP2SQ5POXZIH4QC
SJOTTUZMA3UTGT5D6LKUTBDP2CZFXT24FB5IAWCUFHSHLLACM75QC
6RJVKSL7RVZIZIZIC4TWIYJQ24RUSB346ODS3NYGDHUIRTC4Z5FQC
7ZMJTRJP6PQW4OLFDNTYLMVAZLYQ3IEBIQZVBZN2BSRJEADHUZ7AC
PM2AFCUMNJD4UBWQ4GQPETHSE4T2C75JB5B4RHN3DBD6KTX3VBLQC
PTB7I4WQ3NTF7BE3O6WKXDSJD6QRWSZIEWPBSZGCJJZVNKT4OL5AC
5UVDIVD4NSXA52U4QMQIVST3GSZJ2A2YZK3RUEXKPM43YVQ7LI5AC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
GACH6PWPGGUBEE7PFEPQMOZKSR7HTQGL2WLGF2AQPJD3FCCSKZNQC
FLKXPXQ7SRFZPJPDM35D5CG6M52WP4SL32QNEZNSHGBNZYTKYGYAC
ILOED4VB4I6VPAUTR75ZWX6MXDYXB5DO2EDK2UH67O3HNKWV23RQC
NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC
T2AYVN57EFJQLFUFLAZDXKDAFDGTDLQIEQWQZNYFWJZBYSTYH4QQC
UZ6N6HOUPGVSPC5NQROEEDWMEGJA5XUWUY2AKH5QG65AZ25PVXDAC
3PY3L3A4QRW3Z5Y7SHO4TMVOOP2VNCO27X2MX4DTOP2SADLBQUOAC
T4IH76FA5TWHFOZUJFHLQXQJENJHWTUZZP4EGNA7D4GTZY7D4ZKAC
W5VEC2PBIM5DMU5233HOWAZUEPTGWJRZZIA3H35YYQQW6BTP6XUAC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
UDYVF65OZSNPANLHDI3ODBEGUAKAVZ4KH4OZFAKR2CQJPO4AXU6QC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
ZP2KE7A2LE7Z2S7AC45WE4CXDSEVDTWIMV2EM4IBUKXYJIDU6R7QC
KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC
7KWDC7XFNMBLSUO2HISIROBINZBX5T67LJEEXTAORXW2YZ7VWFGAC
HH3HFWVXABJ4IRMN22PPJCREMULZSN6DA7VYKOGECGMNUQTZ5QNQC
CDKRLJIGVWQE2PMHCSLJBLYQEK7JYC4LQM7H2X3O6NMJMCCDRVIAC
IIN7AVA6JYRBXH6ZYRR7BY7TV6PW7ANAQ2A3PD55FKBKKQFEEF2AC
TLO257LZSB6ZO36STDUEWJBO2LETXFKTFGXELA6Y4BZBVAEIIINAC
4UXFU3FZOCBSLDQ4S7MJKAE2H7VUHCNRDQMIY6NJ3PHYXWNGISDQC
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
I7QLYOTE6DLQZM7YWUWYLKHRJRB2A3STQ42ALSRGQICEWKD2QTEQC
6HG6JFO47Y3BZLU7Y6G3R2CX6JFGN4X5PKK6S5IGUXUYQ5GVZYFQC
KKROXTUPBNEXXEUUDJNADATK3BCQPSQWFZ6L4VTKBPTYXJLYUHDQC
TZ55IZNANEJO2WDTKYWVLY2W2VV6BR7WKIN7XLNISAMMFT6LG2WQC
WWR4IDWLXP4XLBWDZBA5GFG7CRKUJQNRK7FFUFOISK6OJTMYQPFQC
77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC
3YK4G4IQBXW63HPGU5WRTV6L2FCMKAK4DOTCHFK2FNSB5B3Y3PVQC
// If we don't trigger the shaft, and the player doesn't
// already know about it, don't let him/her notice it.
case TRAP_SHAFT:
{
// Paranoia
if (!is_valid_shaft_level())
{
if (trap_known);
mpr("The shaft disappears in a puff of logic!");
grd[env.trap[i].x][env.trap[i].y] = DNGN_FLOOR;
env.trap[i].type = TRAP_UNASSIGNED;
return;
}
if (!you.will_trigger_shaft())
{
if (trap_known && !you.airborne())
mpr("You fail to trigger the shaft.");
if (!trap_known)
grd[you.x_pos][you.y_pos] = DNGN_UNDISCOVERED_TRAP;
return;
}
if (!you.do_shaft())
if (!trap_known)
{
grd[you.x_pos][you.y_pos] = DNGN_UNDISCOVERED_TRAP;
return;
}
break;
}
{
}
bool actor::will_trigger_shaft() const
{
return (!airborne() && total_weight() >= 300
&& is_valid_shaft_level());
}
level_id actor::shaft_dest() const
{
if (you.level_type != LEVEL_DUNGEON)
return level_id::current();
level_id lev = level_id::current();
int curr_depth = subdungeon_depth(you.where_are_you, you.your_level);
lev.depth += ((pos().x + pos().y) % 3) + 1;
if (lev.depth > your_branch().depth)
lev.depth = your_branch().depth;
if (lev.depth == curr_depth)
return lev;
// Only shafts on the level immediately above a dangeorus branch
// bottom will take you to that dangerous bottom, and shafts can't
// be created during level generation time.
if (your_branch().dangerous_bottom_level
&& lev.depth == your_branch().depth
&& (your_branch().depth - curr_depth) > 1)
{
lev.depth--;
}
return lev;
}
bool actor::airborne() const
return (attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON ||
attribute[ATTR_TRANSFORMATION] == TRAN_BAT ||
duration[DUR_LEVITATION]);
return (duration[DUR_LEVITATION]);
}
int player::body_weight() const
{
if (attribute[ATTR_TRANSFORMATION] == TRAN_AIR)
return 0;
int weight;
switch(body_size(PSIZE_BODY))
{
case SIZE_TINY:
weight = 150;
break;
case SIZE_LITTLE:
weight = 300;
break;
case SIZE_SMALL:
weight = 425;
break;
case SIZE_MEDIUM:
weight = 550;
break;
case SIZE_LARGE:
weight = 1300;
break;
case SIZE_BIG:
weight = 1500;
break;
case SIZE_GIANT:
weight = 1800;
break;
case SIZE_HUGE:
weight = 2200;
break;
default:
mpr("ERROR: invalid player body weight");
perror("player::body_weight(): invalid player body weight");
end(0);
}
switch(attribute[ATTR_TRANSFORMATION])
{
case TRAN_STATUE:
weight *= 2;
break;
case TRAN_LICH:
weight /= 2;
break;
default:
break;
}
return (weight);
}
int player::total_weight() const
{
return (body_weight() + burden);
if ( !is_levitating() )
return (FL_NONE);
else
if (attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON ||
attribute[ATTR_TRANSFORMATION] == TRAN_BAT)
{
return FL_FLY;
}
else if (is_levitating())
if (!is_valid_shaft_level())
return (false);
// Handle instances of do_shaft() being invoked magically when
// the player isn't standing over a shaft.
if (trap_type_at_xy(x_pos, y_pos) != TRAP_SHAFT)
{
switch(grd[x_pos][y_pos])
{
case DNGN_FLOOR:
case DNGN_OPEN_DOOR:
case DNGN_TRAP_MECHANICAL:
case DNGN_TRAP_MAGICAL:
case DNGN_TRAP_NATURAL:
case DNGN_UNDISCOVERED_TRAP:
case DNGN_ENTER_SHOP:
break;
default:
return (false);
}
if (airborne() || total_weight() == 0)
{
mpr("A shaft briefly opens up underneath you!");
return (true);
}
force_stair = DNGN_TRAP_NATURAL;
}
down_stairs(your_level, force_stair);
return (true);
}
///////////////////////////////////////////////////////////
void set_level_annotation(std::string str,
level_id li = level_id::current());
void clear_level_annotation(level_id li = level_id::current());
std::string get_level_annotation(level_id li = level_id::current());
bool level_annotation_has(std::string str,
level_id li = level_id::current());
}
bool notes_exist = false;
bool has_notes[NUM_BRANCHES];
for (int i = 0; i < NUM_BRANCHES; ++i)
{
Branch branch = branches[i];
has_notes[i] = false;
for (int depth = 1; depth <= branch.depth; depth++)
{
const level_id li(branch.id, depth);
if (get_level_annotation(li).length() > 0)
{
notes_exist = true;
has_notes[i] = true;
break;
}
}
if (notes_exist)
{
disp += "\n\n <white>Level Annotations</white>\n" ;
for (int i = 0; i < NUM_BRANCHES; ++i)
{
if (!has_notes[i])
continue;
Branch branch = branches[i];
disp += "\n<yellow>";
disp += branch.shortname;
disp += "</yellow>\n";
for (int depth = 1; depth <= branch.depth; depth++)
{
const level_id li(branch.id, depth);
if (get_level_annotation(li).length() > 0)
{
char depth_str[3];
sprintf(depth_str, "%2d", depth);
disp += "<white>";
disp += depth_str;
disp += ":</white> ";
disp += get_level_annotation(li);
disp += + "\n";
}
}
}
}
////////////////////////////////////////////////////////////////////////
void set_level_annotation(std::string str,
level_id li)
{
if (str == "")
{
clear_level_annotation(li);
return;
}
level_annotations[li] = str;
}
void clear_level_annotation(level_id li)
{
level_annotations.erase(li);
}
std::string get_level_annotation(level_id li)
{
annotation_map_type::const_iterator i = level_annotations.find(li);
if (i == level_annotations.end())
return "";
return (i->second);
}
bool level_annotation_has(std::string find,
level_id li)
{
std::string str = get_level_annotation(li);
return (str.find(find) != std::string::npos);
}
void annotate_level()
{
level_id li = level_id::current();
level_id li2 = level_id::current();
if (is_stair(grd[you.x_pos][you.y_pos]))
{
li2 = level_id::get_next_level_id(you.pos());
if (li2.level_type != LEVEL_DUNGEON || li2.depth <= 0)
li2 = level_id::current();
}
if (you.level_type != LEVEL_DUNGEON && li2.level_type != LEVEL_DUNGEON)
{
mpr("You can't annotate this level.");
return;
}
if (you.level_type != LEVEL_DUNGEON)
li = li2;
else if (li2 != level_id::current())
{
if (yesno("Annotate level on other end of current stairs?"))
li = li2;
}
if (get_level_annotation(li).length() > 0)
{
mpr("Current level annotation is:", MSGCH_PROMPT);
mpr(get_level_annotation(li).c_str() );
}
mpr( "Set level annotation to what? ", MSGCH_PROMPT );
char buf[77];
get_input_line( buf, sizeof(buf) );
if (strlen(buf) == 0)
{
if (get_level_annotation(li).length() > 0)
{
if (!yesno("Really clear the annotation?"))
return;
}
else
{
canned_msg(MSG_OK);
return;
}
}
set_level_annotation(buf, li);
}
break;
case TRAP_SHAFT:
{
// Paranoia
if (!is_valid_shaft_level())
{
if (trapKnown && monsterNearby)
mpr("The shaft disappears in a puff of logic!");
grd[env.trap[which_trap].x][env.trap[which_trap].y] = DNGN_FLOOR;
env.trap[which_trap].type = TRAP_UNASSIGNED;
return;
}
if (!monster->will_trigger_shaft())
{
if (trapKnown && !monster->airborne())
simple_monster_message(monster,
" fails to trigger the shaft.");
return;
}
revealTrap = monster->do_shaft();
}
static bool can_place_on_trap(int mon_type, trap_type trap)
{
if (trap == TRAP_TELEPORT)
return (false);
if (trap == TRAP_SHAFT)
{
if (mon_type == RANDOM_MONSTER)
return (false);
return (mons_class_flag(mon_type, M_FLIES)
|| mons_class_flag(mon_type, M_LEVITATE)
|| get_monster_data(mon_type)->size == SIZE_TINY);
}
return (true);
int monsters::body_weight() const
{
int mclass = type;
switch(mclass)
{
case MONS_SPECTRAL_THING:
case MONS_SPECTRAL_WARRIOR:
case MONS_ELECTRIC_GOLEM:
case MONS_RAKSHASA_FAKE:
return 0;
case MONS_ZOMBIE_SMALL:
case MONS_ZOMBIE_LARGE:
case MONS_SKELETON_SMALL:
case MONS_SKELETON_LARGE:
case MONS_SIMULACRUM_SMALL:
case MONS_SIMULACRUM_LARGE:
mclass = number;
break;
default:
break;
}
int weight = mons_weight(mclass);
// Water elementals are "insubstantial", but still have weight.
if (weight == 0 && type == MONS_WATER_ELEMENTAL)
weight = 1500;
// weight == 0 in the monster entry indicates "no corpse". Can't
// use CE_NOCORPSE, because the corpse-effect field is used for
// corpseless monsters to indicate what happens if their blood
// is sucked. Grrrr.
if (weight == 0 && !mons_is_insubstantial(type))
{
const monsterentry *entry = get_monster_data(mclass);
switch(entry->size)
{
case SIZE_TINY:
weight = 150;
break;
case SIZE_LITTLE:
weight = 300;
break;
case SIZE_SMALL:
weight = 425;
break;
case SIZE_MEDIUM:
weight = 550;
break;
case SIZE_LARGE:
weight = 1300;
break;
case SIZE_BIG:
weight = 1500;
break;
case SIZE_GIANT:
weight = 1800;
break;
case SIZE_HUGE:
weight = 2200;
break;
default:
mpr("ERROR: invalid monster body weight");
perror("monsters::body_weight(): invalid monster body weight");
end(0);
}
switch(mclass)
{
case MONS_IRON_DEVIL:
weight += 550;
break;
case MONS_STONE_GOLEM:
case MONS_EARTH_ELEMENTAL:
case MONS_CRYSTAL_GOLEM:
weight *= 2;
break;
case MONS_IRON_DRAGON:
case MONS_IRON_GOLEM:
weight *= 3;
break;
case MONS_QUICKSILVER_DRAGON:
case MONS_SILVER_STATUE:
weight *= 4;
break;
case MONS_WOOD_GOLEM:
weight *= 2;
weight /= 3;
break;
case MONS_FLYING_SKULL:
case MONS_CURSE_SKULL:
case MONS_SKELETAL_DRAGON:
case MONS_SKELETAL_WARRIOR:
weight /= 2;
break;
case MONS_SHADOW_FIEND:
case MONS_SHADOW_IMP:
case MONS_SHADOW_DEMON:
weight /= 3;
break;
}
switch(monster_symbols[mclass].glyph)
{
case 'L':
weight /= 2;
break;
case 'p':
weight = 0;
break;
}
}
if (type == MONS_SKELETON_SMALL || type == MONS_SKELETON_LARGE)
weight /= 2;
return (weight);
}
int monsters::total_weight() const
{
int burden = 0;
for (int i = 0; i < NUM_MONSTER_SLOTS; i++)
if (inv[i] != NON_ITEM)
burden += item_mass(mitm[inv[i]]) * mitm[inv[i]].quantity;
return (body_weight() + burden);
}
bool monsters::do_shaft()
{
if (!is_valid_shaft_level())
return (false);
bool nearby = mons_near(this);
bool vis = player_monster_visible(this);
// Handle instances of do_shaft() being invoked magically when
// the monster isn't standing over a shaft.
if (trap_type_at_xy(x, y) != TRAP_SHAFT)
{
switch(grd[x][y])
{
case DNGN_FLOOR:
case DNGN_OPEN_DOOR:
case DNGN_TRAP_MECHANICAL:
case DNGN_TRAP_MAGICAL:
case DNGN_TRAP_NATURAL:
case DNGN_UNDISCOVERED_TRAP:
case DNGN_ENTER_SHOP:
break;
default:
return (false);
}
if (airborne() || total_weight() == 0)
{
if (nearby)
{
if (vis)
mprf("A shaft briefly opens up underneath %s!",
name(DESC_NOCAP_THE).c_str());
else
mpr("A shaft briefly opens up in the floor!");
}
return (true);
}
}
level_id lev = shaft_dest();
if (lev == level_id::current())
return (false);
set_transit(lev);
if (nearby)
{
if (vis)
mprf("%s falls through a shaft!",
name(DESC_CAP_THE).c_str());
else
mpr("A shaft briefly opens up in the floor!");
}
// Monster is no longer on this level
destroy_inventory();
monster_cleanup(this);
return true;
}
}
level_id old_level_id = level_id::current();
LevelInfo &old_level_info = travel_cache.get_level_info(old_level_id);
// Does the next level have a warning annotation?
coord_def pos(you.x_pos, you.y_pos);
level_id next_level_id = level_id::get_next_level_id(pos);
crawl_state.level_annotation_shown = false;
if (level_annotation_has("WARN", next_level_id)
&& next_level_id != level_id::current()
&& next_level_id.level_type == LEVEL_DUNGEON && !force_stair)
{
mpr("Warning: level annotation for next level is:", MSGCH_PROMPT);
mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PROMPT);
if (!yesno("Enter next level anyways?", true, 0, true, false))
{
interrupt_activity( AI_FORCE_INTERRUPT );
return;
}
crawl_state.level_annotation_shown = true;
}
if (shaft)
{
bool known_trap = (grd[you.x_pos][you.y_pos] != DNGN_UNDISCOVERED_TRAP
&& !force_stair);
if (you.airborne() && !known_trap && !force_stair)
{
mpr("You can't go down here!");
return;
}
if (you.airborne() && you.flies() != FL_FLY && !force_stair)
{
if (known_trap)
mpr("You must have controlled flight to dive through "
"a shaft.");
return;
}
if (!is_valid_shaft_level())
{
if (known_trap)
mpr("Strange, the shaft doesn't seem to lead anywhere.");
return;
}
shaft_dest = you.shaft_dest();
if (shaft_dest == level_id::current())
{
if (known_trap)
mpr("Strange, the shaft doesn't seem to lead anywhere.");
return;
}
shaft_level = absdungeon_depth(shaft_dest.branch,
shaft_dest.depth);
if (you.flies() != FL_FLY || force_stair)
mpr("You fall through a shaft!");
return;
}
}
// Does the next level have a warning annotation?
coord_def pos = you.pos();
level_id next_level_id = level_id::get_next_level_id(pos);
crawl_state.level_annotation_shown = false;
if (level_annotation_has("WARN", next_level_id)
&& next_level_id != level_id::current()
&& next_level_id.level_type == LEVEL_DUNGEON && !force_stair)
{
mpr("Warning: level annotation for next level is:", MSGCH_PROMPT);
mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PROMPT);
if (!yesno("Enter next level anyways?", true, 0, true, false))
{
interrupt_activity( AI_FORCE_INTERRUPT );
bool is_valid_shaft_level()
{
if (you.level_type != LEVEL_DUNGEON)
return (false);
// Don't generate shafts in branches where teleport control
// is prevented. Prevents player from going down levels without
// reaching stairs, and also keeps player from getting stuck
// on lower levels with the innability to use teleport control to
// get back up.
if (testbits(get_branch_flags(), LFLAG_NO_TELE_CONTROL))
{
return (false);
}
int depth = subdungeon_depth(you.where_are_you, you.your_level);
// When generating levels, don't place a shaft on the level
// immediately above the bottom of a branch if that branch is
// significantly more dangerous than normal.
int min_delta = 1;
if (env.turns_on_level == -1 && your_branch().dangerous_bottom_level)
min_delta = 2;
if (spec_type == TRAP_RANDOM)
spec_type = static_cast<trap_type>( random2(NUM_TRAPS) );
if (spec_type == TRAP_RANDOM || spec_type == TRAP_NONTELEPORT)
{
trap_type forbidden1 = NUM_TRAPS;
trap_type forbidden2 = NUM_TRAPS;
if (spec_type == TRAP_NONTELEPORT)
{
forbidden1 = TRAP_SHAFT;
forbidden2 = TRAP_TELEPORT;
}
else if (!is_valid_shaft_level())
forbidden1 = TRAP_SHAFT;
do
{
spec_type = static_cast<trap_type>( random2(NUM_TRAPS) );
} while (spec_type == forbidden1 || spec_type == forbidden2);
}
}
static bool can_go_down_shaft()
{
// Not a shaft
if (trap_type_at_xy(you.x_pos, you.y_pos) != TRAP_SHAFT)
return (false);
// Undiscovered shaft
if (grd[you.x_pos][you.y_pos] == DNGN_UNDISCOVERED_TRAP)
return (false);
// Have to fly to dive through it.
if (you.flies() != FL_FLY)
return (false);
return (true);