#include <ctype.h> #include <sys/types.h> #include <stdio.h> #include "define.h" #include "struct.h" /* * CONSTANTS */ #define MOVE_FLEE -1 #define MOVE_SLITHERS 0 #define MOVE_SNEAK 1 #define MOVE_WALK 2 #define MOVE_FLY 3 #define MOVE_SWIM 4 #define MOVE_FLOAT 5 #define MOVE_WADE 6 const char* leaving_verb [] = { "slithers", "sneaks", "leaves", "flies", "swims", "floats", "wades" }; const char* leaving_action [] = { "slither", "sneak", "leave", "fly", "swim", "float", "wade" }; const char* arriving_verb [] = { "slithers in", "sneaks in", "arrives", "flies in", "swims in", "floats in", "wades in" }; const direction_type dir_table [] = { { "north", 2, "the south", "to the north" }, { "east", 3, "the west", "to the east" }, { "south", 0, "the north", "to the south" }, { "west", 1, "the east", "to the west" }, { "up", 5, "below", "above you" }, { "down", 4, "above", "below you" }, { "extra", 6, "??", "??" } }; char_data* leader = NULL; /* * LOCAL FUNCTIONS */ bool passes_drunk ( char_data* ); bool trigger_entering ( char_data*, room_data*, int ); bool trigger_leaving ( char_data*, room_data*, int, action_data*& ); bool can_enter ( char_data*, room_data* ); bool can_leave ( char_data*, int, bool ); bool handle_terrain ( char_data*, room_data*, room_data*, int&, int& ); bool is_exhausted ( char_data*, int&, int ); int get_motion ( char_data* ); int find_door ( char_data*, char* ); exit_data* valid_exit ( char_data*, int ); void act_leader ( const char*, char_data* ); void add_delays ( char_data*, int& ); #define rd room_data #define cd char_data void arrival_message ( cd*, rd*, exit_data*, int, action_data* ); void leaving_message ( cd*, rd*, exit_data*, int, action_data* ); void leaving_self ( cd*, exit_data*, int, action_data* ); void leaving_other ( cd*, cd*, rd*, exit_data*, int, action_data* ); #undef rd #undef cd /* * MOVEMENT ABILITIES */ bool char_data :: can_float( ) { if( is_set( affected_by, AFF_FLOAT ) ) return TRUE; return FALSE; } bool char_data :: can_swim( ) { if( can_breath_underwater( ) ) return TRUE; if( species != NULL ) return is_set( &species->act_flags, ACT_CAN_SWIM ); return( shdata->skill[SKILL_SWIMMING] != 0 ); } bool char_data :: can_fly( ) { if( species == NULL ) return FALSE; return is_set( &species->act_flags, ACT_CAN_FLY ); } bool char_data :: can_breath_underwater( ) { if( species != NULL ) return is_set( &species->act_flags, ACT_CAN_SWIM ); return FALSE; } bool can_climb( char_data* ch ) { if( ch->species != NULL && !is_set( &ch->species->act_flags, ACT_CAN_CLIMB ) ) return FALSE; return TRUE; } /* * CAN MOVE ROUTINE */ bool Char_Data :: Can_Move( exit_data* exit ) { if( is_set( &exit->exit_info, EX_CLOSED ) && !is_set( affected_by, AFF_PASS_DOOR ) ) return FALSE; if( Size( ) > exit->to_room->size ) return FALSE; if( pcdata == NULL && ( is_set( &species->act_flags, ACT_SENTINEL ) || is_set( &exit->to_room->room_flags, RFLAG_NO_MOB ) || ( is_set( &species->act_flags, ACT_STAY_AREA ) && exit->to_room->area != in_room->area ) ) ) return FALSE; return TRUE; } /* * MAIN MOVEMENT ROUTINE */ void move_char( char_data* ch, int door, bool flee ) { action_data* action; char_data* follower; room_data* in_room = ch->in_room; room_data* to_room; exit_data* exit; char_array* list = NULL; int move; int type; ch->shown = 1; if( ch->rider != NULL ) { move_char( ch->rider, door, flee ); return; } if( ( exit = valid_exit( ch, door ) ) == NULL || !can_leave( ch, door, flee ) ) return; to_room = exit->to_room; if( !can_enter( ch, to_room ) || !handle_terrain( ch, in_room, to_room, move, type ) ) return; add_delays( ch, move ); if( is_exhausted( ch, move, type ) || !trigger_leaving( ch, in_room, door, action ) || !passes_drunk( ch ) ) return; if( ch->mount != NULL ) ch->mount->move -= move; else ch->move -= move; if( flee ) type = MOVE_FLEE; if( leader == NULL ) { leader = ch; list = followers( ch, ch->array ); } leaving_message( ch, in_room, exit, type, action ); make_tracks( ch, in_room, door ); ch->From( ); ch->To( to_room ); if( ch->mount != NULL ) { ch->mount->From( ); ch->mount->To( to_room ); } ch->room_position = dir_table[door].reverse; arrival_message( ch, to_room, exit, type, action ); if( list != NULL ) { for( int i = 0; i < *list; i++ ) if( ( follower = list->list[i] ) != leader && follower->leader->in_room != follower->in_room && follower->position == POS_STANDING ) move_char( follower, door, FALSE ); for( int i = 0; i < *list; i++ ) { follower = list->list[i]; if( follower->in_room == to_room ) { send( "\n\r", follower ); show_room( follower, to_room, TRUE, TRUE ); } } for( int i = 0; i < *list; i++ ) { follower = list->list[i]; if( follower->in_room == to_room ) trigger_entering( follower, to_room, dir_table[door].reverse ); remove_bit( &follower->status, STAT_FOLLOWER ); } delete list; } if( leader == ch ) leader = NULL; if( ch->active.time == -1 ) add_queue( &ch->active, 5 ); } /* * MESSAGE ROUTINES */ bool group_message( char_data* ch, char_data* actor ) { if( actor == leader || !is_set( &actor->status, STAT_FOLLOWER ) ) return FALSE; if( ch->pcdata == NULL || !is_set( &ch->pcdata->message, MSG_GROUP_MOVE ) ) return TRUE; if( actor->Seen( ch ) ) { if( actor->leader == ch ) send( ch, "%s follows you.\n\r", actor ); else send( ch, "%s follows %s.\n\r", actor, actor->leader ); } return TRUE; } void arrival_message( char_data* ch, room_data* room, exit_data* exit, int type, action_data* ) { char_data* rch; room_data* to_room; int back = dir_table[ exit->direction ].reverse; if( type == MOVE_FLEE ) { if( ch->mount != NULL ) fsend( *ch->array, "%s, riding %s, charges blindly in, fleeing something %s.\n\r", ch, ch->mount, dir_table[back].where ); else fsend( *ch->array, "%s arrives, obviously fleeing something %s.\n\r", ch, dir_table[back].where ); return; } for( int i = 0; i < room->contents; i++ ) if( ( rch = character( room->contents[i] ) ) != NULL && rch != ch && rch->link != NULL && rch->position > POS_SLEEPING && !group_message( rch, ch ) ) { if( ch->Seen( rch ) ) { if( ch->mount != NULL ) send( rch, "%s riding %s has arrived.\n\r", ch, ch->mount ); else send( rch, "%s %s from %s.\n\r", ch, arriving_verb[type], dir_table[ exit->direction ].arrival_msg ); } /* else if( can_hear( rch ) ) send( rch, "You hear someone or something arrive.\n\r" ); */ } for( int i = 0; i < room->exits; i++ ) { to_room = room->exits[i]->to_room; for( int j = 0; j < to_room->contents; j++ ) if( ( rch = character( to_room->contents[j] ) ) != NULL && is_set( rch->affected_by, AFF_SENSE_DANGER ) ) send( rch, "You sense %s %s.\n\r", ch, dir_table[ exit->direction ].where ); } return; } /* * LEAVING MESSAGES */ const char* leaving_msg [] = { "to_char", "You $t $T.\n\r", "to_room", "$1 $t $T.\n\r", "" }; void leaving_message( char_data* ch, room_data* room, exit_data* exit, int type, action_data* action ) { char_data* rch; if( type == MOVE_FLEE ) { if( ch->mount != NULL ) { send( ch, "Fleeing the battle, you ride %s %s.\n\r", ch->mount, dir_table[ exit->direction ].name ); send( *ch->array, "Fleeing the battle, %s rides %s %s.\n\r", ch, ch->mount, dir_table[ exit->direction ].name ); } else { send( ch, "You flee %s.\n\r", dir_table[ exit->direction ].name ); send( *ch->array, "%s blindly flees %s.\n\r", ch, dir_table[ exit->direction ].name ); } return; } leaving_self( ch, exit, type, action ); for( int i = 0; i < room->contents; i++ ) if( ( rch = character( room->contents[i] ) ) != NULL && ch != rch && rch->link != NULL && rch->position > POS_SLEEPING && !group_message( rch, ch ) ) leaving_other( rch, ch, room, exit, type, action ); } void leaving_self( char_data* ch, exit_data* exit, int type, action_data* action ) { if( ch->mount != NULL ) { send( ch, "You ride %s %s.\n\r", ch->mount, dir_table[ exit->direction ].name ); } else if( leader != NULL && leader != ch ) { send( ch, "You follow %s.\n\r", ch->leader ); } else if( is_set( &exit->exit_info, EX_CLOSED ) ) { send( ch, "You %s through %s!\n\r", exit->direction == DIR_UP || exit->direction == DIR_DOWN ? "climb" : "step", exit->name ); } else { act( ch, prog_msg( action, leaving_msg[0], leaving_msg[1] ), ch, NULL, leaving_action[type], dir_table[ exit->direction ].name ); } return; } void leaving_other( char_data* rch, char_data* ch, room_data* room, exit_data* exit, int type, action_data* action ) { if( !ch->Seen( rch ) ) { /* if( can_hear( rch, ch ) ) send( rch, "You hear someone or something leave %s.\n\r", dir_table[door].name ); */ return; } if( is_set( &exit->exit_info, EX_CLOSED ) ) { send( rch, "%s %s %s passing through %s!\n\r", ch, leaving_verb[type], dir_table[ exit->direction ].name, exit->name ); } else if( ch->mount == NULL ) { act( rch, prog_msg( action, leaving_msg[2], leaving_msg[3] ), ch, NULL,leaving_verb[type], dir_table[ exit->direction ].name ); } else { send( rch, "%s rides %s %s.\n\r", ch, ch->mount, dir_table[ exit->direction ].name ); } } /* * SUBROUTINES OF MAIN MOVEMENT FUNCTION */ bool can_leave( char_data* ch, int door, bool flee ) { char_data* rch; bool after; if( is_set( ch->affected_by, AFF_ENTANGLED ) ) { send( "You are entangled in a web and quite stuck.\n\r", ch ); act_leader( "** %s is entangled in a web and unable to follow you. **\n\r", ch ); return FALSE; } if( flee ) return TRUE; if( opponent( ch ) != NULL ) { send( "You can't walk away from a battle - use flee.\n\r", ch ); act_leader( "** %s is fighting and unable to follow you. **\n\r", ch ); return FALSE; } after = TRUE; for( int i = 0; i < *ch->array; i++ ) { if( ( rch = character( ch->array->list[i] ) ) != NULL ) { if( rch == ch ) { after = FALSE; continue; } if( includes( rch->aggressive, ch ) && rch->Size( ) > max( SIZE_GNOME, ch->Size( )-2 ) && rch->position >= POS_FIGHTING && ( ( after && rch->room_position == door ) || ( !after && ch->room_position != door ) ) && !is_set( ch->affected_by, AFF_ENTANGLED ) ) { send( ch, "%s is blocking your exit %s.\n\r", rch, dir_table[door].name ); return FALSE; } } } return TRUE; } exit_data* valid_exit( char_data* ch, int door ) { exit_data* exit = exit_direction( ch->in_room, door ); if( exit == NULL || !exit->Seen( ch ) ) { if( ch->in_room->Seen( ch ) ) send( ch, "You see no exit %s.\n\r", dir_table[ door ].where ); else { fsend( ch, "You attempt to move %s but find yourself unable to.\n\r", dir_table[door].name ); fsend_seen( ch, "%s attempts to move %s and runs straight into a wall.", ch, dir_table[door].name ); } return NULL; } if( is_set( &exit->exit_info, EX_CLOSED ) && !is_set( ch->affected_by, AFF_PASS_DOOR ) ) { send( ch, "%s is closed.\n\r", exit ); return NULL; } if( is_set( &exit->exit_info, EX_REQUIRES_CLIMB ) && !can_climb( ch ) && !ch->can_fly( ) ) { act_leader( "** %s is unable to climb so can't follow you. **\n\r", ch ); send( ch, "Leaving %s requires you to climb which you are incapable\ of.\n\r", dir_table[door].name ); return NULL; } return exit; } bool can_enter( char_data* ch, room_data* room ) { if( ch->pcdata != NULL && room->area->status != AREA_OPEN && room->area->status != AREA_IMMORTAL && !is_apprentice( ch ) ) { send( ch, "That area is not open to players yet.\n\r" ); return FALSE; } if( ch->Size( ) > room->size ) { send( ch, "You are too large to fit in there.\n\r" ); act_leader( "** %s is too large to follow you there. **\n\r", ch ); return FALSE; } if( ch->mount != NULL ) { if( ch->mount->Size( ) > room->size ) { send( ch, "%s is too large to fit in there.\n\r", ch->mount ); return FALSE; } if( IS_SET( room->room_flags, RFLAG_NO_MOUNT ) ) { send( "You can not go there while mounted.\n\r", ch ); return FALSE; } } return TRUE; } /* * DRUNK ROUTINE */ const char *drunk_message[] = { "You stumble and barely stay on your feet.\n\r", "$n stumbles and barely stays on $m feet.", "The ground moves quickly sending you reeling.\n\r", "$n staggers, obviously intoxicated.", "Your legs give way to gravity.\n\r", "$n suddenly sits down and looks quite surprised.", "You stumble.\n\r", "$n has had too much to drink and falls to the ground.", "You trip over your left foot.\n\r", "The feet of $n decide to fight and down $s falls." }; bool passes_drunk( char_data* ch ) { // int i; /* if( !IS_DRUNK( ch ) || number_range( 1, 20 ) < 18 || ch->mount != NULL ) return TRUE; i = number_range( 0, 4 ); send( ch, drunk_message[2*i] ); act_room( drunk_message[2*i+1], ch, NULL, NULL, TO_ROOM ); if( i <= 1 ) return TRUE; ch->position = POS_RESTING; ch->hit -= 2; update_pos( ch ); */ return TRUE; } /* * EXHAUSTION */ void add_delays( char_data* ch, int& move ) { int flag [] = { PLR_TRACK, PLR_SEARCHING, PLR_SNEAK }; int cost [] = { 2, 3, 2 }; int i; if( ch->pcdata == NULL ) return; for( i = 0; i < 3; i++ ) if( is_set( ch->pcdata->pfile->flags, flag[i] ) ) move += cost[i]; } bool is_exhausted( char_data* ch, int& move, int type ) { if( type == MOVE_SWIM && ch->pcdata != NULL ) move *= 12-ch->shdata->skill[ SKILL_SWIMMING ]; if( ch->mount != NULL ) { if( ch->mount->move < move ) { send( ch, "Your mount is exhausted.\n\r" ); return TRUE; } } else { if( ch->move < move ) { send( "You are too exhausted.\n\r", ch ); act_leader( "** %s is too exhausted to follow you. **\n\r", ch ); return TRUE; } } return FALSE; } /* * TERRAIN FUNCTIONS */ int get_motion( char_data* ch ) { if( ch->can_float( ) ) return MOVE_FLOAT; if( ch->can_fly( ) ) return MOVE_FLY; if( ch->species != NULL && is_set( &ch->species->act_flags, ACT_SLITHERS ) ) return MOVE_SLITHERS; if( ch->pcdata != NULL && is_set( ch->pcdata->pfile->flags, PLR_SNEAK ) ) { ch->improve_skill( SKILL_SNEAK ); return MOVE_SNEAK; } return MOVE_WALK; } bool handle_terrain( char_data* ch, room_data* from, room_data* to, int& move, int& type ) { char_data* mount = ch->mount; type = get_motion( mount == NULL ? ch : mount ); switch( type ) { case MOVE_FLOAT: move = 1; break; case MOVE_FLY: move = 3; break; default: move = int( ( terrain[ from->sector_type ].movement_cost +terrain[ to->sector_type ].movement_cost ) *( 1+5.*ch->contents.weight/ch->Capacity( ) )/3 ); } if( to->sector_type == SECT_AIR ) { if( type == MOVE_FLY ) return TRUE; if( mount != NULL ) { send( ch, "Your mount does not know how to fly.\n\r" ); } else { send( ch, "You can't fly.\n\r" ); act_leader( "** %s is unable to fly so does not follow you. **\n\r", ch ); } return FALSE; } if( to->sector_type == SECT_WATER_SURFACE || to->sector_type == SECT_RIVER ) { if( type <= MOVE_WALK ) { if( mount != NULL ) { if( !mount->can_swim( ) ) { send( ch, "Your mount does not know how to swim.\n\r" ); return FALSE; } } else { if( !ch->can_swim( ) ) { send( ch, "You don't know how to swim or fly.\n\r" ); act_leader( "** %s can not swim or fly so fails to follow you. **\n\r", ch ); return FALSE; } } type = MOVE_SWIM; ch->improve_skill( SKILL_SWIMMING ); } return TRUE; } if( to->sector_type == SECT_UNDERWATER ) { if( mount != NULL ) { send( ch, "You can't ride underwater.\n\r" ); return FALSE; } if( !ch->can_swim( ) ) { send( ch, "You don't how to swim.\n\r" ); act_leader( "** %s can not swim so fails to follow you. **\n\r", ch ); return FALSE; } type = MOVE_SWIM; return TRUE; } if( from->sector_type == SECT_SHALLOWS ) { type = ( ch->species != NULL && is_set( &ch->species->act_flags, ACT_CAN_SWIM ) ) ? MOVE_SWIM : MOVE_WADE; return TRUE; } return TRUE; } /* * ENTERING/LEAVNG TRIGGERS */ bool trigger_leaving( char_data *ch, room_data *room, int door, action_data*& action ) { char_data* npc; mprog_data* mprog; bool result = TRUE; for( int i = 0; i < *ch->array; i++ ) { if( ( npc = mob( ch->array->list[i] ) ) == NULL || npc->pcdata != NULL || npc->position < POS_RESTING || npc == ch ) continue; for( mprog = npc->species->mprog; mprog != NULL; mprog = mprog->next ) if( mprog->trigger == MPROG_TRIGGER_LEAVING && ( mprog->value == door || mprog->value == -1 ) ) { var_ch = ch; var_mob = npc; var_room = room; if( !execute( mprog ) || ch->in_room != room ) return FALSE; } } for( action = ch->in_room->action; action != NULL; action = action->next ) if( action->trigger == TRIGGER_LEAVING && is_set( &action->flags, door ) ) { var_ch = ch; var_room = room; result = execute( action ); if( ch->in_room != room ) return FALSE; if( result ) return TRUE; } return result; } bool trigger_entering( char_data *ch, room_data *room, int door ) { action_data* action; char_data* npc; mprog_data* mprog; // obj_data* obj; // obj_data* obj_next; // oprog_data* oprog; for( int i = 0; i < room->contents; i++ ) { if( ( npc = mob( room->contents[i] ) ) == NULL || npc == ch || npc->position < POS_RESTING ) continue; for( mprog = npc->species->mprog; mprog != NULL; mprog = mprog->next ) if( mprog->trigger == MPROG_TRIGGER_ENTRY && ( mprog->value == door || mprog->value == -1 ) ) { var_ch = ch; var_mob = npc; var_room = room; execute( mprog ); if( ch->in_room != room ) return FALSE; } } for( action = room->action; action != NULL; action = action->next ) if( action->trigger == TRIGGER_ENTERING && is_set( &action->flags, door ) ) { var_ch = ch; var_room = room; execute( action ); if( ch->in_room != room ) return FALSE; break; } /* for( obj = room->contents; obj != NULL; obj = obj_next ) { obj_next = obj->next_content; for( oprog = obj->pIndexData->oprog; oprog != NULL; oprog = oprog->next ) if( oprog->trigger == OPROG_TRIGGER_ENTERING ) { var_ch = ch; var_obj = obj; var_room = room; execute( oprog ); if( ch->in_room != room ) return FALSE; } } */ return TRUE; } /* * LEADER MESSAGE ROUTINES */ void act_leader( const char* text, char_data* follower ) { char buf [ MAX_INPUT_LENGTH ]; if( leader == NULL ) return; sprintf( buf, text, follower->Name( leader ) ); buf[3] = toupper( buf[3] ); send( buf, leader ); return; } /* * DO MOVE FUNCTIONS */ void do_north( char_data* ch, char* ) { move_char( ch, DIR_NORTH, FALSE ); return; } void do_east( char_data *ch, char* ) { move_char( ch, DIR_EAST, FALSE ); return; } void do_south( char_data *ch, char* ) { move_char( ch, DIR_SOUTH, FALSE ); return; } void do_west( char_data* ch, char* ) { move_char( ch, DIR_WEST, FALSE ); return; } void do_up( char_data *ch, char* ) { move_char( ch, DIR_UP, FALSE ); return; } void do_down( char_data *ch, char* ) { move_char( ch, DIR_DOWN, FALSE ); return; }; /* ------------------------------------------------------------------------- */ void do_search( char_data* ch, char* argument ) { action_data* action; room_data* room = ch->in_room; if( not_player( ch ) ) return; if( toggle( ch, argument, "Searching", ch->pcdata->pfile->flags, PLR_SEARCHING ) ) { send( "[Searching increases movement point cost by 3 per\ move.]\n\r", ch ); return; } for( action = ch->in_room->action; action != NULL; action = action->next ) if( action->trigger == TRIGGER_SEARCHING && ( ( *action->target == '\0' && *argument == '\0' ) || ( *argument != '\0' && is_name( argument, action->target ) ) ) ) { var_ch = ch; var_room = ch->in_room; if( !execute( action ) || ch->in_room != room ) return; break; } if( *argument == '\0' ) send( "You rummage around but find nothing interesting.\n\r", ch ); else send( "Whatever that is, searching it results in nothing\ interesting.\n\r", ch ); } /* * SPEED WALKING */ bool speed_walking( char_data* ch, char* argument ) { int i; for( i = 0; argument[i] != '\0'; i++ ) { switch( toupper( argument[i] ) ) { case 'S': case 'N': case 'U': case 'D': case 'E': case 'W': break; default: return FALSE; } } send( ch, "Speed walking is not yet implemented.\n\r" ); return TRUE; }