/*****************************************************************************
* DikuMUD (C) 1990, 1991 by: *
* Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen, *
* and Katja Nyboe. *
*---------------------------------------------------------------------------*
* MERC 2.1 (C) 1992, 1993 by: *
* Michael Chastain, Michael Quan, and Mitchell Tse. *
*---------------------------------------------------------------------------*
* SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider. *
* Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
* gorog, Grishnakh, Nivek, Tricops, and Fireblade. *
*---------------------------------------------------------------------------*
* SMAUG 1.7 FUSS by: Samson and others of the SMAUG community. *
* Their contributions are greatly appreciated. *
*---------------------------------------------------------------------------*
* LoP (C) 2006, 2007, 2008 by: the LoP team. *
*---------------------------------------------------------------------------*
* Player movement module *
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "h/mud.h"
bool sneaking_char;
bool stop_fishing( CHAR_DATA *ch );
void handle_stat( CHAR_DATA *ch, short stat, bool silence, short chance );
const short movement_loss[SECT_MAX] =
{
1, 2, 2, 3, 4, 6, 4, 1, 6, 10, 6, 5, 7, 4
};
const char *dir_name[] =
{
"north", "east", "south", "west",
"up", "down", "northeast", "northwest",
"southeast", "southwest", "somewhere"
};
const int trap_door[] =
{
TRAP_N, TRAP_E, TRAP_S, TRAP_W,
TRAP_U, TRAP_D, TRAP_NE, TRAP_NW,
TRAP_SE, TRAP_SW
};
const short rev_dir[] =
{
2, 3, 0, 1, 5, 4, 9, 8, 7, 6, 10
};
/* Local functions. */
OBJ_DATA *has_key( CHAR_DATA *ch, int key );
const char *rev_exit( short vdir )
{
switch( vdir )
{
default:
return "somewhere";
case 0:
return "the south";
case 1:
return "the west";
case 2:
return "the north";
case 3:
return "the east";
case 4:
return "below";
case 5:
return "above";
case 6:
return "the southwest";
case 7:
return "the southeast";
case 8:
return "the northwest";
case 9:
return "the northeast";
}
}
/*
* Function to get the equivelant exit of DIR 0-MAXDIR out of linked list.
* Made to allow old-style diku-merc exit functions to work. -Thoric
*/
EXIT_DATA *get_exit( ROOM_INDEX_DATA *room, short dir )
{
EXIT_DATA *xit;
if( !room )
{
bug( "%s: NULL room", __FUNCTION__ );
return NULL;
}
for( xit = room->first_exit; xit; xit = xit->next )
if( xit->vdir == dir )
return xit;
return NULL;
}
/* Function to get an exit, leading the the specified room */
EXIT_DATA *get_exit_to( ROOM_INDEX_DATA *room, short dir, int vnum )
{
EXIT_DATA *xit;
if( !room )
{
bug( "%s: NULL room", __FUNCTION__ );
return NULL;
}
for( xit = room->first_exit; xit; xit = xit->next )
if( xit->vdir == dir && xit->vnum == vnum )
return xit;
return NULL;
}
/* Function to get the nth exit of a room - Thoric */
EXIT_DATA *get_exit_num( ROOM_INDEX_DATA *room, short count )
{
EXIT_DATA *xit;
int cnt;
if( !room )
{
bug( "%s: NULL room", __FUNCTION__ );
return NULL;
}
for( cnt = 0, xit = room->first_exit; xit; xit = xit->next )
if( ++cnt == count )
return xit;
return NULL;
}
/* Modify movement due to encumbrance - Thoric */
short encumbrance( CHAR_DATA *ch, short move )
{
int cur, max;
max = can_carry_w( ch );
cur = ch->carry_weight;
if( cur >= max )
return move * 4;
else if( cur >= max * 0.95 )
return ( short )( move * 3.5 );
else if( cur >= max * 0.90 )
return move * 3;
else if( cur >= max * 0.85 )
return ( short )( move * 2.5 );
else if( cur >= max * 0.80 )
return move * 2;
else if( cur >= max * 0.75 )
return ( short )( move * 1.5 );
else
return move;
}
/* Check to see if a character can fall down, checks for looping - Thoric */
bool will_fall( CHAR_DATA *ch, int fall )
{
if( xIS_SET( ch->in_room->room_flags, ROOM_NOFLOOR )
&& can_go( ch->in_room, DIR_DOWN )
&& ( !IS_AFFECTED( ch, AFF_FLYING ) || ( ch->mount && !IS_AFFECTED( ch->mount, AFF_FLYING ) ) ) )
{
if( fall > 80 )
{
bug( "Falling (in a loop?) more than 80 rooms: vnum %d", ch->in_room->vnum );
char_from_room( ch );
char_to_room( ch, get_room_index( sysdata.room_temple ) );
fall = 0;
return true;
}
set_char_color( AT_FALLING, ch );
send_to_char( "You're falling down...\r\n", ch );
move_char( ch, get_exit( ch->in_room, DIR_DOWN ), ++fall );
return true;
}
return false;
}
ch_ret move_char( CHAR_DATA *ch, EXIT_DATA *pexit, int fall )
{
ROOM_INDEX_DATA *in_room, *to_room, *from_room;
OBJ_DATA *boat;
char buf[MSL];
const char *txt, *dtxt;
ch_ret retcode;
short door;
bool drunk = false, brief = false;
if( !is_npc( ch ) )
{
if( is_drunk( ch, 2 ) && ( ch->position != POS_SHOVE ) && ( ch->position != POS_DRAG ) )
drunk = true;
}
if( drunk && !fall )
{
door = number_door( );
pexit = get_exit( ch->in_room, door );
}
retcode = rNONE;
txt = NULL;
if( is_npc( ch ) && xIS_SET( ch->act, ACT_MOUNTED ) )
return retcode;
in_room = ch->in_room;
from_room = in_room;
if( !pexit || !( to_room = pexit->to_room ) )
{
if( drunk && ch->position != POS_MOUNTED
&& ch->in_room->sector_type != SECT_WATER_SWIM
&& ch->in_room->sector_type != SECT_WATER_NOSWIM
&& ch->in_room->sector_type != SECT_UNDERWATER
&& ch->in_room->sector_type != SECT_OCEANFLOOR )
{
switch( number_bits( 4 ) )
{
default:
act( AT_ACTION, "You drunkenly stumble into some obstacle.", ch, NULL, NULL, TO_CHAR );
act( AT_ACTION, "$n drunkenly stumbles into a nearby obstacle.", ch, NULL, NULL, TO_ROOM );
break;
case 3:
act( AT_ACTION, "In your drunken stupor you trip over your own feet and tumble to the ground.", ch, NULL, NULL, TO_CHAR );
act( AT_ACTION, "$n stumbles drunkenly, trips and tumbles to the ground.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_RESTING;
break;
case 4:
act( AT_SOCIAL, "You utter a string of slurred obscenities.", ch, NULL, NULL, TO_CHAR );
act( AT_ACTION, "Something blurry and immovable has intercepted you as you stagger along.", ch, NULL, NULL, TO_CHAR );
act( AT_HURT, "Oh geez... THAT really hurt. Everything slowly goes dark and numb...", ch, NULL, NULL, TO_CHAR );
act( AT_ACTION, "$n drunkenly staggers into something.", ch, NULL, NULL, TO_ROOM );
act( AT_SOCIAL, "$n utters a string of slurred obscenities: @*&^%@*&!", ch, NULL, NULL, TO_ROOM );
act( AT_ACTION, "$n topples to the ground with a thud.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_INCAP;
break;
}
}
else if( drunk )
act( AT_ACTION, "You stare around trying to make sense of things through your drunken stupor.", ch, NULL, NULL, TO_CHAR );
else
send_to_char( "Alas, you can't go that way.\r\n", ch );
return rNONE;
}
door = pexit->vdir;
/*
* Exit is only a "window", there is no way to travel in that direction
* unless it's a door with a window in it -Thoric
*/
if( xIS_SET( pexit->exit_info, EX_WINDOW ) && !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
{
send_to_char( "Alas, you can't go that way.\r\n", ch );
return rNONE;
}
if( is_npc( ch ) )
{
if( xIS_SET( pexit->exit_info, EX_PORTAL ) )
{
act( AT_PLAIN, "Mobs can't use portals.", ch, NULL, NULL, TO_CHAR );
return rNONE;
}
if( xIS_SET( pexit->exit_info, EX_NOMOB ) || xIS_SET( to_room->room_flags, ROOM_NO_MOB ) )
{
act( AT_PLAIN, "Mobs can't enter there.", ch, NULL, NULL, TO_CHAR );
return rNONE;
}
}
if( xIS_SET( pexit->exit_info, EX_CLOSED ) && ( !IS_AFFECTED( ch, AFF_PASS_DOOR ) || xIS_SET( pexit->exit_info, EX_NOPASSDOOR ) ) )
{
if( !xIS_SET( pexit->exit_info, EX_SECRET ) && !xIS_SET( pexit->exit_info, EX_DIG ) )
{
if( drunk )
{
act( AT_PLAIN, "$n runs into the $d in $s drunken state.", ch, NULL, pexit->keyword, TO_ROOM );
act( AT_PLAIN, "You run into the $d in your drunken state.", ch, NULL, pexit->keyword, TO_CHAR );
}
else
act( AT_PLAIN, "The $d is closed.", ch, NULL, pexit->keyword, TO_CHAR );
}
else
{
if( drunk )
send_to_char( "You stagger around in your drunken state.\r\n", ch );
else
send_to_char( "Alas, you can't go that way.\r\n", ch );
}
return rNONE;
}
if( !fall && IS_AFFECTED( ch, AFF_CHARM ) && ch->master && in_room == ch->master->in_room )
{
send_to_char( "What? And leave your beloved master?\r\n", ch );
return rNONE;
}
if( room_is_private( to_room ) )
{
send_to_char( "That room is private right now.\r\n", ch );
return rNONE;
}
if( room_is_dnd( ch, to_room ) )
{
send_to_char( "That room is \"do not disturb\" right now.\r\n", ch );
return rNONE;
}
if( !is_immortal( ch ) && !is_npc( ch ) && ch->in_room->area != to_room->area )
{
if( ch->level < to_room->area->low_hard_range )
{
set_char_color( AT_TELL, ch );
switch( to_room->area->low_hard_range - ch->level )
{
case 1:
send_to_char( "A voice in your mind says, 'You're nearly ready to go that way...'\r\n", ch );
break;
case 2:
send_to_char( "A voice in your mind says, 'Soon you shall be ready to travel down this path... soon.'\r\n", ch );
break;
case 3:
send_to_char( "A voice in your mind says, 'You aren't ready to go down that path... yet.'\r\n", ch );
break;
default:
send_to_char( "A voice in your mind says, 'You aren't ready to go down that path.'\r\n", ch );
break;
}
return rNONE;
}
else if( ch->level > to_room->area->hi_hard_range )
{
set_char_color( AT_TELL, ch );
send_to_char( "A voice in your mind says, 'There is nothing more for you down that path.'\r\n", ch );
return rNONE;
}
}
if( !fall && !is_npc( ch ) )
{
int move;
if( xIS_SET( to_room->area->flags, AFLAG_NOPKILL )
&& !xIS_SET( ch->in_room->area->flags, AFLAG_NOPKILL )
&& ( is_pkill( ch ) && !is_immortal( ch ) ) )
{
set_char_color( AT_MAGIC, ch );
send_to_char( "\r\nA godly force forbids deadly characters from entering that area...\r\n", ch );
return rNONE;
}
if( in_room->sector_type == SECT_AIR || to_room->sector_type == SECT_AIR
|| xIS_SET( pexit->exit_info, EX_FLY ) )
{
if( ch->mount && !IS_AFFECTED( ch->mount, AFF_FLYING ) )
{
send_to_char( "Your mount can't fly.\r\n", ch );
return rNONE;
}
if( !ch->mount && !IS_AFFECTED( ch, AFF_FLYING ) )
{
send_to_char( "You'd need to fly to go there.\r\n", ch );
return rNONE;
}
}
if( in_room->sector_type == SECT_WATER_NOSWIM || to_room->sector_type == SECT_WATER_NOSWIM )
{
if( ( ch->mount && !is_floating( ch->mount ) ) || !is_floating( ch ) )
{
/*
* Look for a boat.
* We can use the boat obj for a more detailed description.
*/
if( ( boat = get_objtype( ch, ITEM_BOAT ) ) )
{
if( drunk )
txt = "paddles unevenly";
else
txt = "paddles";
}
else
{
if( ch->mount )
send_to_char( "Your mount would drown!\r\n", ch );
else
send_to_char( "You'd need a boat to go there.\r\n", ch );
return rNONE;
}
}
}
if( xIS_SET( pexit->exit_info, EX_CLIMB ) )
{
bool found;
found = false;
if( ch->mount && IS_AFFECTED( ch->mount, AFF_FLYING ) )
found = true;
else if( IS_AFFECTED( ch, AFF_FLYING ) )
found = true;
if( !found && !ch->mount )
{
if( ( !is_npc( ch ) && number_percent( ) > LEARNED( ch, gsn_climb ) ) || drunk || ch->mental_state < -90 )
{
send_to_char( "You start to climb... but lose your grip and fall!\r\n", ch );
learn_from_failure( ch, gsn_climb );
if( pexit->vdir == DIR_DOWN )
{
retcode = move_char( ch, pexit, 1 );
return retcode;
}
set_char_color( AT_HURT, ch );
send_to_char( "OUCH! You hit the ground!\r\n", ch );
wait_state( ch, 20 );
retcode = damage( ch, ch, ( pexit->vdir == DIR_UP ? 10 : 5 ), TYPE_UNDEFINED, true );
return retcode;
}
found = true;
learn_from_success( ch, gsn_climb );
wait_state( ch, skill_table[gsn_climb]->beats );
txt = "climbs";
}
if( !found )
{
send_to_char( "You can't climb.\r\n", ch );
return rNONE;
}
}
if( ch->mount )
{
if( is_npc( ch->mount ) )
{
if( xIS_SET( pexit->exit_info, EX_PORTAL ) )
{
act( AT_PLAIN, "Your mount can't use portals.", ch, NULL, NULL, TO_CHAR );
return rNONE;
}
if( xIS_SET( pexit->exit_info, EX_NOMOB ) || xIS_SET( to_room->room_flags, ROOM_NO_MOB ) )
{
act( AT_PLAIN, "Your mount can't enter there.", ch, NULL, NULL, TO_CHAR );
return rNONE;
}
}
switch( ch->mount->position )
{
case POS_DEAD:
send_to_char( "Your mount is dead!\r\n", ch );
return rNONE;
break;
case POS_MORTAL:
case POS_INCAP:
send_to_char( "Your mount is hurt far too badly to move.\r\n", ch );
return rNONE;
break;
case POS_STUNNED:
send_to_char( "Your mount is too stunned to do that.\r\n", ch );
return rNONE;
break;
case POS_SLEEPING:
send_to_char( "Your mount is sleeping.\r\n", ch );
return rNONE;
break;
case POS_RESTING:
send_to_char( "Your mount is resting.\r\n", ch );
return rNONE;
break;
case POS_SITTING:
send_to_char( "Your mount is sitting down.\r\n", ch );
return rNONE;
break;
default:
break;
}
if( !is_floating( ch->mount ) )
move = movement_loss[ URANGE( 0, in_room->sector_type, SECT_MAX - 1 ) ];
else
move = 1;
if( ch->mount->move < move )
{
send_to_char( "Your mount is too exhausted.\r\n", ch );
return rNONE;
}
}
else
{
if( !is_floating( ch ) )
move = encumbrance( ch, movement_loss[ URANGE( 0, in_room->sector_type, SECT_MAX - 1 ) ] );
else
move = 1;
if( ch->move < move )
{
send_to_char( "You're too exhausted.\r\n", ch );
return rNONE;
}
}
wait_state( ch, move );
if( ch->mount )
ch->mount->move -= move;
else
ch->move -= move;
}
/* Check if player can fit in the room */
if( to_room->tunnel > 0 )
{
CHAR_DATA *ctmp;
int count = ch->mount ? 1 : 0;
for( ctmp = to_room->first_person; ctmp; ctmp = ctmp->next_in_room )
{
if( ++count >= to_room->tunnel )
{
if( ch->mount && count == to_room->tunnel )
send_to_char( "There is no room for both you and your mount there.\r\n", ch );
else
send_to_char( "There is no room for you there.\r\n", ch );
return rNONE;
}
}
}
if( stop_fishing( ch ) )
send_to_char( "You pull your line out of the water.\r\n", ch );
if( is_npc( ch ) || !xIS_SET( ch->act, PLR_WIZINVIS ) )
{
if( fall )
txt = "falls";
else if( !txt )
{
if( ch->mount )
{
if( IS_AFFECTED( ch->mount, AFF_FLOATING ) )
txt = "floats";
else if( IS_AFFECTED( ch->mount, AFF_FLYING ) )
txt = "flies";
else
txt = "rides";
}
else
{
if( IS_AFFECTED( ch, AFF_FLOATING ) )
{
if( drunk )
txt = "floats unsteadily";
else
txt = "floats";
}
else if( IS_AFFECTED( ch, AFF_FLYING ) )
{
if( drunk )
txt = "flies shakily";
else
txt = "flies";
}
else if( ch->position == POS_SHOVE )
txt = "is shoved";
else if( ch->position == POS_DRAG )
txt = "is dragged";
else
{
if( drunk )
txt = "stumbles drunkenly";
else
txt = "leaves";
}
}
}
if( ch->mount )
{
snprintf( buf, sizeof( buf ), "$n %s %s upon $N.", txt, dir_name[door] );
if( IS_AFFECTED( ch->mount, AFF_SNEAK ) )
sneaking_char = true;
act( AT_ACTION, buf, ch, NULL, ch->mount, TO_NOTVICT );
sneaking_char = false;
}
else
{
snprintf( buf, sizeof( buf ), "$n %s $T.", txt );
if( IS_AFFECTED( ch, AFF_SNEAK ) )
sneaking_char = true;
act( AT_ACTION, buf, ch, NULL, (char *)dir_name[door], TO_ROOM );
sneaking_char = false;
}
}
rprog_leave_trigger( ch );
if( char_died( ch ) )
return global_retcode;
if( ch->in_room->first_content )
retcode = check_room_for_traps( ch, TRAP_LEAVE_ROOM );
if( retcode != rNONE )
return retcode;
char_from_room( ch );
char_to_room( ch, to_room );
if( ch->mount )
{
rprog_leave_trigger( ch->mount );
if( char_died( ch->mount ) )
return global_retcode;
if( ch->mount )
{
char_from_room( ch->mount );
char_to_room( ch->mount, to_room );
}
}
if( is_npc( ch ) || !xIS_SET( ch->act, PLR_WIZINVIS ) )
{
if( fall )
txt = "falls";
else if( ch->mount )
{
if( IS_AFFECTED( ch->mount, AFF_FLOATING ) )
txt = "floats in";
else if( IS_AFFECTED( ch->mount, AFF_FLYING ) )
txt = "flies in";
else
txt = "rides in";
}
else
{
if( IS_AFFECTED( ch, AFF_FLOATING ) )
{
if( drunk )
txt = "floats in unsteadily";
else
txt = "floats in";
}
else if( IS_AFFECTED( ch, AFF_FLYING ) )
{
if( drunk )
txt = "flies in shakily";
else
txt = "flies in";
}
else if( ch->position == POS_SHOVE )
txt = "is shoved in";
else if( ch->position == POS_DRAG )
txt = "is dragged in";
else
{
if( drunk )
txt = "stumbles drunkenly in";
else
txt = "arrives";
}
}
dtxt = rev_exit( door );
if( ch->mount )
{
snprintf( buf, sizeof( buf ), "$n %s from %s upon $N.", txt, dtxt );
if( IS_AFFECTED( ch->mount, AFF_SNEAK ) )
sneaking_char = true;
act( AT_ACTION, buf, ch, NULL, ch->mount, TO_ROOM );
sneaking_char = false;
}
else
{
snprintf( buf, sizeof( buf ), "$n %s from %s.", txt, dtxt );
if( IS_AFFECTED( ch, AFF_SNEAK ) )
sneaking_char = true;
act( AT_ACTION, buf, ch, NULL, NULL, TO_ROOM );
sneaking_char = false;
}
}
if( !is_immortal( ch ) && !is_npc( ch ) && ch->in_room->area != to_room->area )
{
if( ch->level < to_room->area->low_soft_range )
{
set_char_color( AT_MAGIC, ch );
send_to_char( "You feel uncomfortable being in this strange land...\r\n", ch );
}
else if( ch->level > to_room->area->hi_soft_range )
{
set_char_color( AT_MAGIC, ch );
send_to_char( "You feel there is not much to gain visiting this place...\r\n", ch );
}
}
/* Make sure everyone sees the room description of death traps. */
if( xIS_SET( ch->in_room->room_flags, ROOM_DEATH ) && !is_immortal( ch ) )
{
if( xIS_SET( ch->act, PLR_BRIEF ) )
brief = true;
xREMOVE_BIT( ch->act, PLR_BRIEF );
}
do_look( ch, (char *)"auto" );
if( brief )
xSET_BIT( ch->act, PLR_BRIEF );
/* Put good-old EQ-munching death traps back in! - Thoric */
if( xIS_SET( ch->in_room->room_flags, ROOM_DEATH ) && !is_immortal( ch ) )
{
act( AT_DEAD, "$n falls prey to a terrible death!", ch, NULL, NULL, TO_ROOM );
act( AT_DEAD, "Oopsie... you're dead!\r\n", ch, NULL, NULL, TO_CHAR );
snprintf( buf, sizeof( buf ), "%s hit a DEATH TRAP in room %d!", ch->name, ch->in_room->vnum );
log_string( buf );
to_channel( buf, "monitor", PERM_IMM );
if( is_npc( ch ) )
extract_char( ch, true );
else
extract_char( ch, false );
return rCHAR_DIED;
}
/*
* BIG ugly looping problem here when the character is mptransed back
* to the starting room. To avoid this, check how many chars are in
* the room at the start and stop processing followers after doing
* the right number of them. -- Narn
*/
if( !fall )
{
CHAR_DATA *fch, *nextinroom;
int chars = 0, count = 0;
for( fch = from_room->first_person; fch; fch = fch->next_in_room )
chars++;
for( fch = from_room->first_person; fch && ( count < ( chars + 5 ) ); fch = nextinroom )
{
nextinroom = fch->next_in_room;
count++;
if( fch != ch && fch->master == ch
&& ( fch->position == POS_STANDING || fch->position == POS_MOUNTED )
&& !xIS_SET( ch->act, PLR_SOLO ) && !xIS_SET( fch->act, PLR_SOLO ) )
{
act( AT_ACTION, "You follow $N.", fch, NULL, ch, TO_CHAR );
move_char( fch, get_exit( from_room, door ), 0 );
}
}
}
if( ch->in_room->first_content )
retcode = check_room_for_traps( ch, TRAP_ENTER_ROOM );
if( retcode != rNONE || char_died( ch ) )
return retcode;
mprog_entry_trigger( ch );
if( char_died( ch ) )
return retcode;
rprog_enter_trigger( ch );
if( char_died( ch ) )
return retcode;
mprog_greet_trigger( ch );
if( char_died( ch ) )
return retcode;
oprog_greet_trigger( ch );
if( char_died( ch ) )
return retcode;
if( !will_fall( ch, fall ) && fall > 0 )
{
if( !IS_AFFECTED( ch, AFF_FLOATING ) || ( ch->mount && !IS_AFFECTED( ch->mount, AFF_FLOATING ) ) )
{
if( ch->mount )
{
set_char_color( AT_HURT, ch->mount );
send_to_char( "OUCH! You hit the ground!\r\n", ch->mount );
wait_state( ch->mount, 20 );
retcode = damage( ch->mount, ch->mount, 20 * fall, TYPE_UNDEFINED, true );
}
else
{
set_char_color( AT_HURT, ch );
send_to_char( "OUCH! You hit the ground!\r\n", ch );
wait_state( ch, 20 );
retcode = damage( ch, ch, 20 * fall, TYPE_UNDEFINED, true );
}
}
else
{
set_char_color( AT_MAGIC, ch );
if( ch->mount )
send_to_char( "You and your mount lightly float down to the ground.\r\n", ch );
else
send_to_char( "You lightly float down to the ground.\r\n", ch );
}
}
/* Very small chance of getting a dex increase through moving around */
handle_stat( ch, STAT_DEX, true, 1 );
return retcode;
}
CMDF( do_north )
{
move_char( ch, get_exit( ch->in_room, DIR_NORTH ), 0 );
}
CMDF( do_east )
{
move_char( ch, get_exit( ch->in_room, DIR_EAST ), 0 );
}
CMDF( do_south )
{
move_char( ch, get_exit( ch->in_room, DIR_SOUTH ), 0 );
}
CMDF( do_west )
{
move_char( ch, get_exit( ch->in_room, DIR_WEST ), 0 );
}
CMDF( do_up )
{
move_char( ch, get_exit( ch->in_room, DIR_UP ), 0 );
}
CMDF( do_down )
{
move_char( ch, get_exit( ch->in_room, DIR_DOWN ), 0 );
}
CMDF( do_northeast )
{
move_char( ch, get_exit( ch->in_room, DIR_NORTHEAST ), 0 );
}
CMDF( do_northwest )
{
move_char( ch, get_exit( ch->in_room, DIR_NORTHWEST ), 0 );
}
CMDF( do_southeast )
{
move_char( ch, get_exit( ch->in_room, DIR_SOUTHEAST ), 0 );
}
CMDF( do_southwest )
{
move_char( ch, get_exit( ch->in_room, DIR_SOUTHWEST ), 0 );
}
EXIT_DATA *find_door( CHAR_DATA *ch, char *arg, bool quiet )
{
EXIT_DATA *pexit;
int door;
if( !arg || !str_cmp( arg, "" ) )
return NULL;
pexit = NULL;
if( !str_cmp( arg, "n" ) || !str_cmp( arg, "north" ) )
door = 0;
else if( !str_cmp( arg, "e" ) || !str_cmp( arg, "east" ) )
door = 1;
else if( !str_cmp( arg, "s" ) || !str_cmp( arg, "south" ) )
door = 2;
else if( !str_cmp( arg, "w" ) || !str_cmp( arg, "west" ) )
door = 3;
else if( !str_cmp( arg, "u" ) || !str_cmp( arg, "up" ) )
door = 4;
else if( !str_cmp( arg, "d" ) || !str_cmp( arg, "down" ) )
door = 5;
else if( !str_cmp( arg, "ne" ) || !str_cmp( arg, "northeast" ) )
door = 6;
else if( !str_cmp( arg, "nw" ) || !str_cmp( arg, "northwest" ) )
door = 7;
else if( !str_cmp( arg, "se" ) || !str_cmp( arg, "southeast" ) )
door = 8;
else if( !str_cmp( arg, "sw" ) || !str_cmp( arg, "southwest" ) )
door = 9;
else
{
for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
{
if( ( quiet || xIS_SET( pexit->exit_info, EX_ISDOOR ) ) && pexit->keyword && nifty_is_name( arg, pexit->keyword ) )
return pexit;
}
if( !quiet )
act( AT_PLAIN, "You see no $T here.", ch, NULL, arg, TO_CHAR );
return NULL;
}
if( !( pexit = get_exit( ch->in_room, door ) ) )
{
if( !quiet )
act( AT_PLAIN, "You see no $T here.", ch, NULL, arg, TO_CHAR );
return NULL;
}
if( quiet )
return pexit;
if( xIS_SET( pexit->exit_info, EX_SECRET ) )
{
act( AT_PLAIN, "You see no $T here.", ch, NULL, arg, TO_CHAR );
return NULL;
}
if( !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
{
send_to_char( "You can't do that.\r\n", ch );
return NULL;
}
return pexit;
}
void set_bexit_flag( EXIT_DATA *pexit, int flag )
{
EXIT_DATA *pexit_rev;
xSET_BIT( pexit->exit_info, flag );
if( ( pexit_rev = pexit->rexit ) && pexit_rev != pexit )
xSET_BIT( pexit_rev->exit_info, flag );
}
void remove_bexit_flag( EXIT_DATA *pexit, int flag )
{
EXIT_DATA *pexit_rev;
xREMOVE_BIT( pexit->exit_info, flag );
if( ( pexit_rev = pexit->rexit ) && pexit_rev != pexit )
xREMOVE_BIT( pexit_rev->exit_info, flag );
}
void toggle_bexit_flag( EXIT_DATA *pexit, int flag )
{
EXIT_DATA *pexit_rev;
xTOGGLE_BIT( pexit->exit_info, flag );
if( ( pexit_rev = pexit->rexit ) && pexit_rev != pexit )
xTOGGLE_BIT( pexit_rev->exit_info, flag );
}
void do_open( CHAR_DATA *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj;
EXIT_DATA *pexit;
int door;
one_argument( argument, arg );
if( arg == NULL || arg[0] == '\0' )
{
send_to_char( "Open what?\r\n", ch );
return;
}
if( ( pexit = find_door( ch, arg, true ) ) )
{
/* 'open door' */
EXIT_DATA *pexit_rev;
if( xIS_SET( pexit->exit_info, EX_SECRET ) && pexit->keyword && !nifty_is_name( arg, pexit->keyword ) )
{
ch_printf( ch, "You see no %s here.\r\n", arg );
return;
}
if( !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
{
send_to_char( "You can't do that.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_CLOSED ) )
{
send_to_char( "It's already open.\r\n", ch );
return;
}
if( xIS_SET( pexit->exit_info, EX_LOCKED ) && xIS_SET( pexit->exit_info, EX_BOLTED ) )
{
send_to_char( "The bolts locked.\r\n", ch );
return;
}
if( xIS_SET( pexit->exit_info, EX_BOLTED ) )
{
send_to_char( "It's bolted.\r\n", ch );
return;
}
if( xIS_SET( pexit->exit_info, EX_LOCKED ) )
{
send_to_char( "It's locked.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_SECRET ) || ( pexit->keyword && nifty_is_name( arg, pexit->keyword ) ) )
{
act( AT_ACTION, "$n opens the $d.", ch, NULL, pexit->keyword, TO_ROOM );
act( AT_ACTION, "You open the $d.", ch, NULL, pexit->keyword, TO_CHAR );
if( ( pexit_rev = pexit->rexit ) && pexit_rev->to_room == ch->in_room )
{
CHAR_DATA *rch;
for( rch = pexit->to_room->first_person; rch; rch = rch->next_in_room )
act( AT_ACTION, "The $d opens.", rch, NULL, pexit_rev->keyword, TO_CHAR );
}
remove_bexit_flag( pexit, EX_CLOSED );
if( ( door = pexit->vdir ) >= 0 && door < 10 )
check_room_for_traps( ch, trap_door[door] );
return;
}
}
if( ( obj = get_obj_here( ch, arg ) ) )
{
/* 'open object' */
if( obj->item_type != ITEM_CONTAINER )
{
ch_printf( ch, "%s is not a container.\r\n", capitalize( obj->short_descr ) );
return;
}
if( !IS_SET( obj->value[1], CONT_CLOSED ) )
{
ch_printf( ch, "%s is already open.\r\n", capitalize( obj->short_descr ) );
return;
}
if( !IS_SET( obj->value[1], CONT_CLOSEABLE ) )
{
ch_printf( ch, "%s can't be opened or closed.\r\n", capitalize( obj->short_descr ) );
return;
}
if( IS_SET( obj->value[1], CONT_LOCKED ) )
{
ch_printf( ch, "%s is locked.\r\n", capitalize( obj->short_descr ) );
return;
}
REMOVE_BIT( obj->value[1], CONT_CLOSED );
act( AT_ACTION, "You open $p.", ch, obj, NULL, TO_CHAR );
act( AT_ACTION, "$n opens $p.", ch, obj, NULL, TO_ROOM );
oprog_open_trigger( ch, obj );
if( !char_died( ch ) )
check_for_trap( ch, obj, TRAP_OPEN );
return;
}
ch_printf( ch, "You see no %s here.\r\n", arg );
}
void do_close( CHAR_DATA *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj;
EXIT_DATA *pexit;
int door;
one_argument( argument, arg );
if( arg == NULL || arg[0] == '\0' )
{
send_to_char( "Close what?\r\n", ch );
return;
}
if( ( pexit = find_door( ch, arg, true ) ) )
{
/* 'close door' */
EXIT_DATA *pexit_rev;
if( !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
{
send_to_char( "You can't do that.\r\n", ch );
return;
}
if( xIS_SET( pexit->exit_info, EX_CLOSED ) )
{
send_to_char( "It's already closed.\r\n", ch );
return;
}
act( AT_ACTION, "$n closes the $d.", ch, NULL, pexit->keyword, TO_ROOM );
act( AT_ACTION, "You close the $d.", ch, NULL, pexit->keyword, TO_CHAR );
/* close the other side */
if( ( pexit_rev = pexit->rexit ) && pexit_rev->to_room == ch->in_room )
{
CHAR_DATA *rch;
xSET_BIT( pexit_rev->exit_info, EX_CLOSED );
for( rch = pexit->to_room->first_person; rch; rch = rch->next_in_room )
act( AT_ACTION, "The $d closes.", rch, NULL, pexit_rev->keyword, TO_CHAR );
}
set_bexit_flag( pexit, EX_CLOSED );
if( ( door = pexit->vdir ) >= 0 && door < 10 )
check_room_for_traps( ch, trap_door[door] );
return;
}
if( ( obj = get_obj_here( ch, arg ) ) )
{
/* 'close object' */
if( obj->item_type != ITEM_CONTAINER )
{
ch_printf( ch, "%s is not a container.\r\n", capitalize( obj->short_descr ) );
return;
}
if( IS_SET( obj->value[1], CONT_CLOSED ) )
{
ch_printf( ch, "%s is already closed.\r\n", capitalize( obj->short_descr ) );
return;
}
if( !IS_SET( obj->value[1], CONT_CLOSEABLE ) )
{
ch_printf( ch, "%s can't be opened or closed.\r\n", capitalize( obj->short_descr ) );
return;
}
SET_BIT( obj->value[1], CONT_CLOSED );
act( AT_ACTION, "You close $p.", ch, obj, NULL, TO_CHAR );
act( AT_ACTION, "$n closes $p.", ch, obj, NULL, TO_ROOM );
oprog_close_trigger( ch, obj );
if( !char_died( ch ) )
check_for_trap( ch, obj, TRAP_CLOSE );
return;
}
ch_printf( ch, "You see no %s here.\r\n", arg );
}
/*
* Keyring support added by Thoric
* Idea suggested by Onyx <MtRicmer@worldnet.att.net> of Eldarion
*
* New: returns pointer to key/NULL instead of true/false
*
* If you want a feature like having immortals always have a key... you'll
* need to code in a generic key, and make sure extract_obj doesn't extract it
*/
OBJ_DATA *has_key( CHAR_DATA *ch, int key )
{
OBJ_DATA *obj, *obj2;
for( obj = ch->first_carrying; obj; obj = obj->next_content )
{
if( obj->pIndexData->vnum == key || ( obj->item_type == ITEM_KEY && obj->value[0] == key ) )
return obj;
else if( obj->item_type == ITEM_KEYRING )
{
for( obj2 = obj->first_content; obj2; obj2 = obj2->next_content )
if( obj2->pIndexData->vnum == key || ( obj2->item_type == ITEM_KEY && obj2->value[0] == key ) )
return obj2;
}
}
return NULL;
}
void do_lock( CHAR_DATA *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj, *key;
EXIT_DATA *pexit;
int count;
one_argument( argument, arg );
if( arg == NULL || arg[0] == '\0' )
{
send_to_char( "Lock what?\r\n", ch );
return;
}
if( ( pexit = find_door( ch, arg, true ) ) )
{
/* 'lock door' */
if( !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
{
send_to_char( "You can't do that.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_CLOSED ) )
{
send_to_char( "It's not closed.\r\n", ch );
return;
}
if( pexit->key < 0 )
{
send_to_char( "It can't be locked.\r\n", ch );
return;
}
if( !( key = has_key( ch, pexit->key ) ) )
{
send_to_char( "You lack the key.\r\n", ch );
return;
}
if( xIS_SET( pexit->exit_info, EX_LOCKED ) )
{
send_to_char( "It's already locked.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_SECRET ) || ( pexit->keyword && nifty_is_name( arg, pexit->keyword ) ) )
{
send_to_char( "*Click*\r\n", ch );
count = key->count;
key->count = 1;
act( AT_ACTION, "$n locks the $d with $p.", ch, key, pexit->keyword, TO_ROOM );
key->count = count;
set_bexit_flag( pexit, EX_LOCKED );
return;
}
}
if( ( obj = get_obj_here( ch, arg ) ) )
{
/* 'lock object' */
if( obj->item_type != ITEM_CONTAINER )
{
send_to_char( "That's not a container.\r\n", ch );
return;
}
if( !IS_SET( obj->value[1], CONT_CLOSED ) )
{
send_to_char( "It's not closed.\r\n", ch );
return;
}
if( obj->value[2] < 0 )
{
send_to_char( "It can't be locked.\r\n", ch );
return;
}
if( !( key = has_key( ch, obj->value[2] ) ) )
{
send_to_char( "You lack the key.\r\n", ch );
return;
}
if( IS_SET( obj->value[1], CONT_LOCKED ) )
{
send_to_char( "It's already locked.\r\n", ch );
return;
}
SET_BIT( obj->value[1], CONT_LOCKED );
send_to_char( "*Click*\r\n", ch );
count = key->count;
key->count = 1;
act( AT_ACTION, "$n locks $p with $P.", ch, obj, key, TO_ROOM );
key->count = count;
return;
}
ch_printf( ch, "You see no %s here.\r\n", arg );
}
void do_unlock( CHAR_DATA *ch, char *argument )
{
char arg[MIL];
OBJ_DATA *obj, *key;
EXIT_DATA *pexit;
int count;
one_argument( argument, arg );
if( arg == NULL || arg[0] == '\0' )
{
send_to_char( "Unlock what?\r\n", ch );
return;
}
if( ( pexit = find_door( ch, arg, true ) ) )
{
/* 'unlock door' */
if( !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
{
send_to_char( "You can't do that.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_CLOSED ) )
{
send_to_char( "It's not closed.\r\n", ch );
return;
}
if( pexit->key < 0 )
{
send_to_char( "It can't be unlocked.\r\n", ch );
return;
}
if( !( key = has_key( ch, pexit->key ) ) )
{
send_to_char( "You lack the key.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_LOCKED ) )
{
send_to_char( "It's already unlocked.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_SECRET ) || ( pexit->keyword && nifty_is_name( arg, pexit->keyword ) ) )
{
send_to_char( "*Click*\r\n", ch );
count = key->count;
key->count = 1;
act( AT_ACTION, "$n unlocks the $d with $p.", ch, key, pexit->keyword, TO_ROOM );
key->count = count;
if( xIS_SET( pexit->exit_info, EX_EATKEY ) )
{
separate_obj( key );
extract_obj( key );
}
remove_bexit_flag( pexit, EX_LOCKED );
return;
}
}
if( ( obj = get_obj_here( ch, arg ) ) )
{
/* 'unlock object' */
if( obj->item_type != ITEM_CONTAINER )
{
send_to_char( "That's not a container.\r\n", ch );
return;
}
if( !IS_SET( obj->value[1], CONT_CLOSED ) )
{
send_to_char( "It's not closed.\r\n", ch );
return;
}
if( obj->value[2] < 0 )
{
send_to_char( "It can't be unlocked.\r\n", ch );
return;
}
if( !( key = has_key( ch, obj->value[2] ) ) )
{
send_to_char( "You lack the key.\r\n", ch );
return;
}
if( !IS_SET( obj->value[1], CONT_LOCKED ) )
{
send_to_char( "It's already unlocked.\r\n", ch );
return;
}
REMOVE_BIT( obj->value[1], CONT_LOCKED );
send_to_char( "*Click*\r\n", ch );
count = key->count;
key->count = 1;
act( AT_ACTION, "$n unlocks $p with $P.", ch, obj, key, TO_ROOM );
key->count = count;
if( IS_SET( obj->value[1], CONT_EATKEY ) )
{
separate_obj( key );
extract_obj( key );
}
return;
}
ch_printf( ch, "You see no %s here.\r\n", arg );
}
CMDF( do_bashdoor )
{
EXIT_DATA *pexit;
char arg[MIL];
char *keyword = (char *)"wall";
one_argument( argument, arg );
if( arg == NULL || arg[0] == '\0' )
{
send_to_char( "Bash what?\r\n", ch );
return;
}
if( ch->fighting )
{
send_to_char( "You can't break off your fight.\r\n", ch );
return;
}
if( ( pexit = find_door( ch, arg, true ) ) )
{
ROOM_INDEX_DATA *to_room;
EXIT_DATA *pexit_rev;
int schance;
if( !xIS_SET( pexit->exit_info, EX_CLOSED ) )
{
send_to_char( "Calm down. It is already open.\r\n", ch );
return;
}
wait_state( ch, skill_table[gsn_bashdoor]->beats );
if( !xIS_SET( pexit->exit_info, EX_SECRET ) )
keyword = pexit->keyword;
if( !is_npc( ch ) )
schance = LEARNED( ch, gsn_bashdoor ) / 2;
else
schance = 90;
if( xIS_SET( pexit->exit_info, EX_LOCKED ) )
schance /= 3;
if( !xIS_SET( pexit->exit_info, EX_BASHPROOF )
&& ch->move >= 15 && number_percent( ) < ( schance + 4 * ( get_curr_str( ch ) - 19 ) ) )
{
xREMOVE_BIT( pexit->exit_info, EX_CLOSED );
if( xIS_SET( pexit->exit_info, EX_LOCKED ) )
xREMOVE_BIT( pexit->exit_info, EX_LOCKED );
xSET_BIT( pexit->exit_info, EX_BASHED );
act( AT_SKILL, "Crash! You bashed open the $d!", ch, NULL, keyword, TO_CHAR );
act( AT_SKILL, "$n bashes open the $d!", ch, NULL, keyword, TO_ROOM );
learn_from_success( ch, gsn_bashdoor );
if( ( to_room = pexit->to_room ) && ( pexit_rev = pexit->rexit ) && pexit_rev->to_room == ch->in_room )
{
CHAR_DATA *rch;
xREMOVE_BIT( pexit_rev->exit_info, EX_CLOSED );
if( xIS_SET( pexit_rev->exit_info, EX_LOCKED ) )
xREMOVE_BIT( pexit_rev->exit_info, EX_LOCKED );
xSET_BIT( pexit_rev->exit_info, EX_BASHED );
for( rch = to_room->first_person; rch; rch = rch->next_in_room )
act( AT_SKILL, "The $d crashes open!", rch, NULL, pexit_rev->keyword, TO_CHAR );
}
damage( ch, ch, ( ch->max_hit / 20 ), gsn_bashdoor, true );
return;
}
}
act( AT_SKILL, "WHAAAAM!!! You bash against the $d, but it doesn't budge.", ch, NULL, keyword, TO_CHAR );
act( AT_SKILL, "WHAAAAM!!! $n bashes against the $d, but it holds strong.", ch, NULL, keyword, TO_ROOM );
damage( ch, ch, ( ch->max_hit / 20 ) + 10, gsn_bashdoor, true );
learn_from_failure( ch, gsn_bashdoor );
}
void do_stand( CHAR_DATA *ch, char *argument )
{
switch( ch->position )
{
case POS_SLEEPING:
if( IS_AFFECTED( ch, AFF_SLEEP ) )
{
send_to_char( "You can't seem to wake up!\r\n", ch );
return;
}
send_to_char( "You wake and climb quickly to your feet.\r\n", ch );
act( AT_ACTION, "$n arises from $s slumber.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_STANDING;
break;
case POS_RESTING:
send_to_char( "You gather yourself and stand up.\r\n", ch );
act( AT_ACTION, "$n rises from $s rest.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_STANDING;
break;
case POS_SITTING:
send_to_char( "You move quickly to your feet.\r\n", ch );
act( AT_ACTION, "$n rises up.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_STANDING;
break;
case POS_STANDING:
send_to_char( "You're already standing.\r\n", ch );
break;
case POS_FIGHTING:
case POS_EVASIVE:
case POS_DEFENSIVE:
case POS_AGGRESSIVE:
case POS_BERSERK:
send_to_char( "You're already fighting!\r\n", ch );
break;
}
}
void do_sit( CHAR_DATA *ch, char *argument )
{
switch( ch->position )
{
case POS_SLEEPING:
if( IS_AFFECTED( ch, AFF_SLEEP ) )
{
send_to_char( "You can't seem to wake up!\r\n", ch );
return;
}
send_to_char( "You wake and sit up.\r\n", ch );
act( AT_ACTION, "$n wakes and sits up.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_SITTING;
break;
case POS_RESTING:
send_to_char( "You stop resting and sit up.\r\n", ch );
act( AT_ACTION, "$n stops resting and sits up.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_SITTING;
break;
case POS_STANDING:
send_to_char( "You sit down.\r\n", ch );
act( AT_ACTION, "$n sits down.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_SITTING;
break;
case POS_SITTING:
send_to_char( "You're already sitting.\r\n", ch );
return;
case POS_FIGHTING:
case POS_EVASIVE:
case POS_DEFENSIVE:
case POS_AGGRESSIVE:
case POS_BERSERK:
send_to_char( "You're busy fighting!\r\n", ch );
return;
case POS_MOUNTED:
send_to_char( "You're already sitting - on your mount.\r\n", ch );
return;
}
}
CMDF( do_rest )
{
switch( ch->position )
{
case POS_SLEEPING:
if( IS_AFFECTED( ch, AFF_SLEEP ) )
{
send_to_char( "You can't seem to wake up!\r\n", ch );
return;
}
send_to_char( "You rouse from your slumber.\r\n", ch );
act( AT_ACTION, "$n rouses from $s slumber.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_RESTING;
break;
case POS_RESTING:
send_to_char( "You're already resting.\r\n", ch );
return;
case POS_STANDING:
send_to_char( "You sprawl out haphazardly.\r\n", ch );
act( AT_ACTION, "$n sprawls out haphazardly.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_RESTING;
break;
case POS_SITTING:
send_to_char( "You lie back and sprawl out to rest.\r\n", ch );
act( AT_ACTION, "$n lies back and sprawls out to rest.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_RESTING;
break;
case POS_FIGHTING:
case POS_EVASIVE:
case POS_DEFENSIVE:
case POS_AGGRESSIVE:
case POS_BERSERK:
send_to_char( "You're busy fighting!\r\n", ch );
return;
case POS_MOUNTED:
send_to_char( "You'd better dismount first.\r\n", ch );
return;
}
if( is_npc( ch ) && ch->mounter ) /* This char has a rider */
{
act( AT_SKILL, "You dismount $N.", ch->mounter, NULL, ch, TO_CHAR );
xREMOVE_BIT( ch->act, ACT_MOUNTED );
ch->mounter->position = POS_STANDING;
ch->mounter->mount = NULL;
ch->mounter = NULL;
}
rprog_rest_trigger( ch );
}
void do_sleep( CHAR_DATA *ch, char *argument )
{
switch( ch->position )
{
case POS_SLEEPING:
send_to_char( "You're already sleeping.\r\n", ch );
return;
case POS_RESTING:
if( ch->mental_state > 30 && ( number_percent( ) + 10 ) < ch->mental_state )
{
send_to_char( "You just can't seem to calm yourself down enough to sleep.\r\n", ch );
act( AT_ACTION, "$n closes $s eyes for a few moments, but just can't seem to go to sleep.", ch, NULL, NULL,
TO_ROOM );
return;
}
send_to_char( "You close your eyes and drift into slumber.\r\n", ch );
act( AT_ACTION, "$n closes $s eyes and drifts into a deep slumber.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_SLEEPING;
break;
case POS_SITTING:
if( ch->mental_state > 30 && ( number_percent( ) + 5 ) < ch->mental_state )
{
send_to_char( "You just can't seem to calm yourself down enough to sleep.\r\n", ch );
act( AT_ACTION, "$n closes $s eyes for a few moments, but just can't seem to go to sleep.", ch, NULL, NULL,
TO_ROOM );
return;
}
send_to_char( "You slump over and fall dead asleep.\r\n", ch );
act( AT_ACTION, "$n nods off and slowly slumps over, dead asleep.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_SLEEPING;
break;
case POS_STANDING:
if( ch->mental_state > 30 && number_percent( ) < ch->mental_state )
{
send_to_char( "You just can't seem to calm yourself down enough to sleep.\r\n", ch );
act( AT_ACTION, "$n closes $s eyes for a few moments, but just can't seem to go to sleep.", ch, NULL, NULL,
TO_ROOM );
return;
}
send_to_char( "You collapse into a deep sleep.\r\n", ch );
act( AT_ACTION, "$n collapses into a deep sleep.", ch, NULL, NULL, TO_ROOM );
ch->position = POS_SLEEPING;
break;
case POS_FIGHTING:
case POS_EVASIVE:
case POS_DEFENSIVE:
case POS_AGGRESSIVE:
case POS_BERSERK:
send_to_char( "You're busy fighting!\r\n", ch );
return;
case POS_MOUNTED:
send_to_char( "You really should dismount first.\r\n", ch );
return;
}
if( is_npc( ch ) && ch->mounter ) /* This char has a rider */
{
act( AT_SKILL, "You dismount $N.", ch->mounter, NULL, ch, TO_CHAR );
xREMOVE_BIT( ch->act, ACT_MOUNTED );
ch->mounter->position = POS_STANDING;
ch->mounter->mount = NULL;
ch->mounter = NULL;
}
rprog_sleep_trigger( ch );
}
CMDF( do_wake )
{
CHAR_DATA *victim;
if( !argument || argument[0] == '\0' )
{
do_stand( ch, (char *)"" );
return;
}
if( !is_awake( ch ) )
{
send_to_char( "You're asleep yourself!\r\n", ch );
return;
}
if( !( victim = get_char_room( ch, argument ) ) )
{
send_to_char( "They aren't here.\r\n", ch );
return;
}
if( is_awake( victim ) )
{
act( AT_PLAIN, "$N is already awake.", ch, NULL, victim, TO_CHAR );
return;
}
if( IS_AFFECTED( victim, AFF_SLEEP ) || victim->position < POS_SLEEPING )
{
act( AT_PLAIN, "You can't seem to wake $M!", ch, NULL, victim, TO_CHAR );
return;
}
act( AT_ACTION, "You wake $M.", ch, NULL, victim, TO_CHAR );
victim->position = POS_STANDING;
act( AT_ACTION, "$n wakes you.", ch, NULL, victim, TO_VICT );
act( AT_ACTION, "$n wakes $N.", ch, NULL, victim, TO_ROOM );
}
/* teleport a character to another room */
void teleportch( CHAR_DATA *ch, ROOM_INDEX_DATA *room, bool show )
{
char buf[MSL];
if( room_is_private( room ) )
return;
act( AT_ACTION, "$n disappears suddenly!", ch, NULL, NULL, TO_ROOM );
char_from_room( ch );
char_to_room( ch, room );
act( AT_ACTION, "$n arrives suddenly!", ch, NULL, NULL, TO_ROOM );
if( show )
do_look( ch, (char *)"auto" );
if( xIS_SET( ch->in_room->room_flags, ROOM_DEATH ) && !is_immortal( ch ) )
{
act( AT_DEAD, "$n falls prey to a terrible death!", ch, NULL, NULL, TO_ROOM );
set_char_color( AT_DEAD, ch );
send_to_char( "Oopsie... you're dead!\r\n", ch );
snprintf( buf, sizeof( buf ), "%s hit a DEATH TRAP in room %d!", ch->name, ch->in_room->vnum );
log_string( buf );
to_channel( buf, "monitor", PERM_IMM );
extract_char( ch, false );
}
}
void teleport( CHAR_DATA *ch, int room, int flags )
{
CHAR_DATA *nch, *nch_next;
ROOM_INDEX_DATA *start = ch->in_room, *dest;
bool show;
if( !( dest = get_room_index( room ) ) )
{
bug( "%s: bad room vnum %d", __FUNCTION__, room );
return;
}
if( IS_SET( flags, TELE_SHOWDESC ) )
show = true;
else
show = false;
if( !IS_SET( flags, TELE_TRANSALL ) )
{
teleportch( ch, dest, show );
return;
}
/* teleport everybody in the room */
for( nch = start->first_person; nch; nch = nch_next )
{
nch_next = nch->next_in_room;
teleportch( nch, dest, show );
}
/* teleport the objects on the ground too */
if( IS_SET( flags, TELE_TRANSALLPLUS ) )
{
OBJ_DATA *obj, *obj_next;
for( obj = start->first_content; obj; obj = obj_next )
{
obj_next = obj->next_content;
obj_from_room( obj );
obj_to_room( obj, dest );
}
}
}
/* "Climb" in a certain direction. - Thoric */
CMDF( do_climb )
{
EXIT_DATA *pexit;
bool found;
found = false;
if( !argument || argument[0] == '\0' )
{
for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
{
if( xIS_SET( pexit->exit_info, EX_xCLIMB ) )
{
move_char( ch, pexit, 0 );
return;
}
}
send_to_char( "You can't climb here.\r\n", ch );
return;
}
if( ( pexit = find_door( ch, argument, true ) ) && xIS_SET( pexit->exit_info, EX_xCLIMB ) )
{
move_char( ch, pexit, 0 );
return;
}
send_to_char( "You can't climb there.\r\n", ch );
}
/* "enter" something (moves through an exit) - Thoric */
CMDF( do_enter )
{
EXIT_DATA *pexit;
bool found;
found = false;
if( !argument || argument[0] == '\0' )
{
for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
{
if( xIS_SET( pexit->exit_info, EX_xENTER ) )
{
move_char( ch, pexit, 0 );
return;
}
}
if( ch->in_room->sector_type != SECT_INSIDE && is_outside( ch ) )
{
for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
{
if( pexit->to_room
&& ( pexit->to_room->sector_type == SECT_INSIDE
|| xIS_SET( pexit->to_room->room_flags, ROOM_INDOORS ) ) )
{
move_char( ch, pexit, 0 );
return;
}
}
}
send_to_char( "You can't find an entrance here.\r\n", ch );
return;
}
if( ( pexit = find_door( ch, argument, true ) ) && xIS_SET( pexit->exit_info, EX_xENTER ) )
{
move_char( ch, pexit, 0 );
return;
}
send_to_char( "You can't enter that.\r\n", ch );
}
/* Leave through an exit. - Thoric */
CMDF( do_leave )
{
EXIT_DATA *pexit;
bool found;
found = false;
if( !argument || argument[0] == '\0' )
{
for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
{
if( xIS_SET( pexit->exit_info, EX_xLEAVE ) )
{
move_char( ch, pexit, 0 );
return;
}
}
if( ch->in_room->sector_type == SECT_INSIDE || !is_outside( ch ) )
{
for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
{
if( pexit->to_room && pexit->to_room->sector_type != SECT_INSIDE
&& !xIS_SET( pexit->to_room->room_flags, ROOM_INDOORS ) )
{
move_char( ch, pexit, 0 );
return;
}
}
}
send_to_char( "You can't find an exit here.\r\n", ch );
return;
}
if( ( pexit = find_door( ch, argument, true ) ) && xIS_SET( pexit->exit_info, EX_xLEAVE ) )
{
move_char( ch, pexit, 0 );
return;
}
send_to_char( "You can't leave that way.\r\n", ch );
}
/*
* Check to see if an exit in the room is pulling (or pushing) players around.
* Some types may cause damage. -Thoric
*
* People kept requesting currents (like SillyMUD has), so I went all out
* and added the ability for an exit to have a "pull" or a "push" force
* and to handle different types much beyond a simple water current.
*
* This check is called by violence_update(). I'm not sure if this is the
* best way to do it, or if it should be handled by a special queue.
*
* Future additions to this code may include equipment being blown away in
* the wind (mostly headwear), and people being hit by flying objects
*
* TODO:
* handle more pulltypes
* give "entrance" messages for players and objects
* proper handling of player resistance to push/pulling
*/
ch_ret pullcheck( CHAR_DATA *ch, int pulse )
{
ROOM_INDEX_DATA *room;
EXIT_DATA *xtmp, *xit = NULL;
OBJ_DATA *obj, *obj_next;
const char *tochar = NULL, *toroom = NULL, *objmsg = NULL;
const char *destrm = NULL, *destob = NULL;
const char *dtxt = "somewhere";
int pullfact, pull, resistance;
bool move = false, moveobj = true, showroom = true;
if( !( room = ch->in_room ) )
{
bug( "pullcheck: %s not in a room?!?", ch->name );
return rNONE;
}
/* Find the exit with the strongest force (if any) */
for( xtmp = room->first_exit; xtmp; xtmp = xtmp->next )
if( xtmp->pull && xtmp->to_room && ( !xit || abs( xtmp->pull ) > abs( xit->pull ) ) )
xit = xtmp;
if( !xit )
return rNONE;
pull = xit->pull;
/* strength also determines frequency */
pullfact = URANGE( 1, 20 - ( abs( pull ) / 5 ), 20 );
/* strongest pull not ready yet... check for one that is */
if( ( pulse % pullfact ) != 0 )
{
for( xit = room->first_exit; xit; xit = xit->next )
if( xit->pull && xit->to_room )
{
pull = xit->pull;
pullfact = URANGE( 1, 20 - ( abs( pull ) / 5 ), 20 );
if( ( pulse % pullfact ) != 0 )
break;
}
if( !xit )
return rNONE;
}
/* negative pull = push... get the reverse exit if any */
if( pull < 0 )
if( !( xit = get_exit( room, rev_dir[xit->vdir] ) ) )
return rNONE;
dtxt = rev_exit( xit->vdir );
/*
* First determine if the player should be moved or not
* Check various flags, spells, the players position and strength vs.
* the pull, etc... any kind of checks you like.
*/
switch( xit->pulltype )
{
case PULL_CURRENT:
case PULL_WHIRLPOOL:
if( room->sector_type == SECT_WATER_SWIM || room->sector_type == SECT_WATER_NOSWIM )
move = true;
else if( room->sector_type == SECT_UNDERWATER || room->sector_type == SECT_OCEANFLOOR )
move = true;
break;
case PULL_GEYSER:
case PULL_WAVE:
move = true;
break;
case PULL_WIND:
case PULL_STORM:
move = true;
break;
case PULL_COLDWIND:
move = true;
break;
case PULL_HOTAIR:
move = true;
break;
case PULL_BREEZE:
move = false;
break;
/*
* exits with these pulltypes should also be blocked from movement
* ie: a secret locked pickproof door with the name "_sinkhole_", etc
*/
case PULL_EARTHQUAKE:
case PULL_SINKHOLE:
case PULL_QUICKSAND:
case PULL_LANDSLIDE:
case PULL_SLIP:
case PULL_LAVA:
if( ( ch->mount && !is_floating( ch->mount ) ) || ( !ch->mount && !is_floating( ch ) ) )
move = true;
break;
/* as if player moved in that direction him/herself */
case PULL_UNDEFINED:
return move_char( ch, xit, 0 );
/* all other cases ALWAYS move */
default:
move = true;
break;
}
/* assign some nice text messages */
switch( xit->pulltype )
{
case PULL_MYSTERIOUS:
/* no messages to anyone */
showroom = false;
break;
case PULL_WHIRLPOOL:
case PULL_VACUUM:
tochar = "You're sucked $T!";
toroom = "$n is sucked $T!";
destrm = "$n is sucked in from $T!";
objmsg = "$p is sucked $T.";
destob = "$p is sucked in from $T!";
break;
case PULL_CURRENT:
case PULL_LAVA:
tochar = "You drift $T.";
toroom = "$n drifts $T.";
destrm = "$n drifts in from $T.";
objmsg = "$p drifts $T.";
destob = "$p drifts in from $T.";
break;
case PULL_BREEZE:
tochar = "You drift $T.";
toroom = "$n drifts $T.";
destrm = "$n drifts in from $T.";
objmsg = "$p drifts $T in the breeze.";
destob = "$p drifts in from $T.";
break;
case PULL_GEYSER:
case PULL_WAVE:
tochar = "You're pushed $T!";
toroom = "$n is pushed $T!";
destrm = "$n is pushed in from $T!";
destob = "$p floats in from $T.";
break;
case PULL_EARTHQUAKE:
tochar = "The earth opens up and you fall $T!";
toroom = "The earth opens up and $n falls $T!";
destrm = "$n falls from $T!";
objmsg = "$p falls $T.";
destob = "$p falls from $T.";
break;
case PULL_SINKHOLE:
tochar = "The ground suddenly gives way and you fall $T!";
toroom = "The ground suddenly gives way beneath $n!";
destrm = "$n falls from $T!";
objmsg = "$p falls $T.";
destob = "$p falls from $T.";
break;
case PULL_QUICKSAND:
tochar = "You begin to sink $T into the quicksand!";
toroom = "$n begins to sink $T into the quicksand!";
destrm = "$n sinks in from $T.";
objmsg = "$p begins to sink $T into the quicksand.";
destob = "$p sinks in from $T.";
break;
case PULL_LANDSLIDE:
tochar = "The ground starts to slide $T, taking you with it!";
toroom = "The ground starts to slide $T, taking $n with it!";
destrm = "$n slides in from $T.";
objmsg = "$p slides $T.";
destob = "$p slides in from $T.";
break;
case PULL_SLIP:
tochar = "You lose your footing!";
toroom = "$n loses $s footing!";
destrm = "$n slides in from $T.";
objmsg = "$p slides $T.";
destob = "$p slides in from $T.";
break;
case PULL_VORTEX:
tochar = "You're sucked into a swirling vortex of colors!";
toroom = "$n is sucked into a swirling vortex of colors!";
toroom = "$n appears from a swirling vortex of colors!";
objmsg = "$p is sucked into a swirling vortex of colors!";
objmsg = "$p appears from a swirling vortex of colors!";
break;
case PULL_HOTAIR:
tochar = "A blast of hot air blows you $T!";
toroom = "$n is blown $T by a blast of hot air!";
destrm = "$n is blown in from $T by a blast of hot air!";
objmsg = "$p is blown $T.";
destob = "$p is blown in from $T.";
break;
case PULL_COLDWIND:
tochar = "A bitter cold wind forces you $T!";
toroom = "$n is forced $T by a bitter cold wind!";
destrm = "$n is forced in from $T by a bitter cold wind!";
objmsg = "$p is blown $T.";
destob = "$p is blown in from $T.";
break;
case PULL_WIND:
tochar = "A strong wind pushes you $T!";
toroom = "$n is blown $T by a strong wind!";
destrm = "$n is blown in from $T by a strong wind!";
objmsg = "$p is blown $T.";
destob = "$p is blown in from $T.";
break;
case PULL_STORM:
tochar = "The raging storm drives you $T!";
toroom = "$n is driven $T by the raging storm!";
destrm = "$n is driven in from $T by a raging storm!";
objmsg = "$p is blown $T.";
destob = "$p is blown in from $T.";
break;
default:
if( pull > 0 )
{
tochar = "You're pulled $T!";
toroom = "$n is pulled $T.";
destrm = "$n is pulled in from $T.";
objmsg = "$p is pulled $T.";
objmsg = "$p is pulled in from $T.";
}
else
{
tochar = "You're pushed $T!";
toroom = "$n is pushed $T.";
destrm = "$n is pushed in from $T.";
objmsg = "$p is pushed $T.";
objmsg = "$p is pushed in from $T.";
}
break;
}
/* Do the moving */
if( move )
{
/* display an appropriate exit message */
if( tochar )
{
act( AT_PLAIN, tochar, ch, NULL, (char *)dir_name[xit->vdir], TO_CHAR );
send_to_char( "\r\n", ch );
}
if( toroom )
act( AT_PLAIN, toroom, ch, NULL, (char *)dir_name[xit->vdir], TO_ROOM );
/* display an appropriate entrance message */
if( destrm && xit->to_room->first_person )
{
act( AT_PLAIN, (char *)destrm, xit->to_room->first_person, NULL, (char *)dtxt, TO_CHAR );
act( AT_PLAIN, (char *)destrm, xit->to_room->first_person, NULL, (char *)dtxt, TO_ROOM );
}
/* move the char */
if( xit->pulltype == PULL_SLIP )
return move_char( ch, xit, 1 );
char_from_room( ch );
char_to_room( ch, xit->to_room );
if( showroom )
do_look( ch, (char *)"auto" );
/* move the mount too */
if( ch->mount )
{
char_from_room( ch->mount );
char_to_room( ch->mount, xit->to_room );
if( showroom )
do_look( ch->mount, (char *)"auto" );
}
}
/* move objects in the room */
if( moveobj )
{
for( obj = room->first_content; obj; obj = obj_next )
{
obj_next = obj->next_content;
if( is_obj_stat( obj, ITEM_BURIED ) || can_wear( obj, ITEM_NO_TAKE ) )
continue;
resistance = get_obj_weight( obj );
if( is_obj_stat( obj, ITEM_METAL ) )
resistance = ( resistance * 6 ) / 5;
switch( obj->item_type )
{
case ITEM_SCROLL:
case ITEM_TRASH:
resistance >>= 2;
break;
case ITEM_SCRAPS:
case ITEM_CONTAINER:
resistance >>= 1;
break;
case ITEM_WAND:
resistance = ( resistance * 5 ) / 6;
break;
case ITEM_CORPSE_PC:
case ITEM_CORPSE_NPC:
case ITEM_FOUNTAIN:
resistance <<= 2;
break;
}
/* is the pull greater than the resistance of the object? */
if( ( abs( pull ) * 10 ) > resistance )
{
if( objmsg && room->first_person )
{
act( AT_PLAIN, (char *)objmsg, room->first_person, obj, (char *)dir_name[xit->vdir], TO_CHAR );
act( AT_PLAIN, (char *)objmsg, room->first_person, obj, (char *)dir_name[xit->vdir], TO_ROOM );
}
if( destob && xit->to_room->first_person )
{
act( AT_PLAIN, (char *)destob, xit->to_room->first_person, obj, (char *)dtxt, TO_CHAR );
act( AT_PLAIN, (char *)destob, xit->to_room->first_person, obj, (char *)dtxt, TO_ROOM );
}
obj_from_room( obj );
obj_to_room( obj, xit->to_room );
}
}
}
return rNONE;
}
bool check_bolt( CHAR_DATA *ch, EXIT_DATA *pexit )
{
if( !ch || !pexit )
return false;
if( !xIS_SET( pexit->exit_info, EX_ISDOOR ) )
{
send_to_char( "You can't do that.\r\n", ch );
return false;
}
if( !xIS_SET( pexit->exit_info, EX_CLOSED ) )
{
send_to_char( "It's not closed.\r\n", ch );
return false;
}
if( !xIS_SET( pexit->exit_info, EX_ISBOLT ) )
{
send_to_char( "You don't see a bolt.\r\n", ch );
return false;
}
return true;
}
/* This function bolts a door. Written by Blackmane */
CMDF( do_bolt )
{
EXIT_DATA *pexit;
if( !argument || argument[0] == '\0' )
{
send_to_char( "Bolt what?\r\n", ch );
return;
}
if( !( pexit = find_door( ch, argument, true ) ) )
{
ch_printf( ch, "You see no %s here.\r\n", argument );
return;
}
if( !check_bolt( ch, pexit ) )
return;
if( xIS_SET( pexit->exit_info, EX_BOLTED ) )
{
send_to_char( "It's already bolted.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_SECRET ) || ( pexit->keyword && nifty_is_name( argument, pexit->keyword ) ) )
{
send_to_char( "*Clunk*\r\n", ch );
act( AT_ACTION, "$n bolts the $d.", ch, NULL, pexit->keyword, TO_ROOM );
set_bexit_flag( pexit, EX_BOLTED );
}
}
/* This function unbolts a door. Written by Blackmane */
CMDF( do_unbolt )
{
EXIT_DATA *pexit;
if( !argument || argument[0] == '\0' )
{
send_to_char( "Unbolt what?\r\n", ch );
return;
}
if( !( pexit = find_door( ch, argument, true ) ) )
{
ch_printf( ch, "You see no %s here.\r\n", argument );
return;
}
if( !check_bolt( ch, pexit ) )
return;
if( !xIS_SET( pexit->exit_info, EX_BOLTED ) )
{
send_to_char( "It's already unbolted.\r\n", ch );
return;
}
if( !xIS_SET( pexit->exit_info, EX_SECRET ) || ( pexit->keyword && nifty_is_name( argument, pexit->keyword ) ) )
{
send_to_char( "*Clunk*\r\n", ch );
act( AT_ACTION, "$n unbolts the $d.", ch, NULL, pexit->keyword, TO_ROOM );
remove_bexit_flag( pexit, EX_BOLTED );
}
}