/**
* A transport NPC, this will be a horse or a camel or whatever.
* @author Pinkfish
* @started Tue Sep 21 18:21:13 PDT 1999
* @changed Fixed up a bunch of things and changed a fair bit
* - Sandoz, Feb. 2002.
*/
inherit NPC_OBJ;
#include <move_failures.h>
#include <npc/transport.h>
#include <dirs.h>
#include <room.h>
#include <tasks.h>
/** @ignore yes */
#define DO_RUSTLE
/** @ignore yes */
#define WEAR_CMD "/cmds/living/wea_r"
/** @ignore yes */
#define GLANCE_CMD "/cmds/living/glance"
#define ADD_RIDE_COMMAND(xxx,yyy) { \
TP->add_command( xxx, TO, "["+query_transport_type()+"] <string'direction'>", \
(: do_ride( $4[0], yyy, "once") :) ); \
TP->add_command( xxx, TO, "["+query_transport_type()+"] <string'direction'> "\
"{once|forever|route|roughly|intersection|roughly-intersection}", \
(: do_ride( $4[0], yyy, $4[1] ) :) ); \
}
/** @ignore yes */
#define IS_ALLOWED(xxx) \
if( !is_allowed_to_use( TP->query_name() ) ) { \
TP->add_failed_mess( TO, xxx ); \
return 0; \
}
/**
* This is the skill base for all the riding skills.
*/
#define TRANSPORT_SKILL_BASE "general.riding."
/**
* This is the multiplier for the panicing in battle.
*/
#define TRANSPORT_PANIC_IN_BATTLE_MULTIPLIER 4
/**
* This is the panicing for when the transport sees a battle
* multiplier.
*/
#define TRANSPORT_PANIC_NORMAL_MULTIPLIER 1
/**
* This is the panicing divisor for if the horse is riding past. The
* faster the horse rides the less the chance. So we add the speed onto
* this.
*/
#define TRANSPORT_PANIC_RIDING_DIVISOR 1
/**
* This is the maximum length of the name the creature can be given.
*/
#define MAX_NAME_LEN 16
/**
* @ignore yes
* These are the move flags for riding the transport.
*/
#define PATH_FOLLOW 1
#define STOP_AT_INTERSECTION 2
#define STOP_AFTER_MOVING_ONCE 4
void set_teathered_to( object ob );
object query_teathered_to();
int do_ride( string direction, int speed, string type );
void setup_movement();
int do_allow( string str );
int do_disallow( string str );
int do_list();
int do_name( string name );
int do_offer( object *food );
int do_dress( object *dress );
int do_teather( object *poles );
object* query_riders();
string query_transport_type();
int query_default_direction_difficulty();
int query_default_mount_difficulty();
int query_default_fight_difficulty();
void panic_horse( int multiplier );
void tell_riders( string str );
void set_transport_name( string name );
private int _cur_speed;
private int _cur_exhaustion;
private int _riding_flags;
private int _transport_id;
private string _owner;
private string _last_location;
private string _transport_name;
private string *_allowed;
private string *_route;
private string _cur_direction;
private string _teathered_to_short;
private nosave object _teathered_to;
private nosave object _in_long;
private nosave object _env_repeater;
private nosave object _me_repeater;
private nosave object *_riding;
private nosave int _transport_speed;
private nosave int _max_speed;
private nosave int _max_exhaustion;
private nosave int _move_id;
private nosave int _default_mount_difficulty;
private nosave int _default_direction_difficulty;
private nosave int _default_fight_difficulty;
private nosave int _panic;
private nosave int _last_move;
private nosave int _last_panic;
private nosave string _transport_type;
private nosave string _inside_long;
void create() {
_max_speed = RIDE_WALK;
_cur_speed = RIDE_NOT_MOVING;
_transport_speed = 4;
_max_exhaustion = 100;
_cur_exhaustion = 100;
_riding = ({ });
_default_direction_difficulty = 500;
_default_mount_difficulty = 300;
_default_fight_difficulty = 50;
add_property("unique", 1 );
add_property("no_teleport", 1 );
::create();
add_extra_look(TO);
} /* create() */
/** @ignore yes */
string *query_help_files() {
string *ret;
if( pointerp( ret = ::query_help_files() ) )
return ret + ({"mount"});
return ({"mount"});
} /* query_help_files() */
/** @ignore yes */
int test_add( object ob, int flag ) { return 1; }
/** @ignore yes */
int test_remove( object ob, int flag, mixed dest ) { return 1; }
/** @ignore yes */
void init() {
string str;
::init();
str = query_name();
TP->add_command("name", TO, "<direct:living'"+str+"'> as <string'name'>",
(: do_name($4[1]) :) );
TP->add_command("mount", TO, "<direct:living>'"+str+"'");
TP->add_command("dismount", TO, "["+query_transport_type()+"]");
TP->add_command("allow", TO,
"<word'player'> to use <direct:living'"+str+"'>",
(: do_allow($4[0]) :) );
TP->add_command("allow", TO, "list <direct:living'"+str+"'>",
(: do_list() :) );
TP->add_command("disallow", TO,
"<word'player'> from using <direct:living'"+str+"'>",
(: do_disallow($4[0]) :) );
ADD_RIDE_COMMAND("walk", RIDE_WALK );
if( _max_speed >= RIDE_TROT )
ADD_RIDE_COMMAND("trot", RIDE_TROT );
if( _max_speed >= RIDE_CANTER )
ADD_RIDE_COMMAND("canter", RIDE_CANTER );
if( _max_speed >= RIDE_GALLOP )
ADD_RIDE_COMMAND("gallop", RIDE_GALLOP );
TP->add_command("stop", TO, str);
TP->add_command("lead", TO, "<direct:living'"+str+"'>");
TP->add_command("teather", TO,
"<direct:living'"+str+"'> to <indirect:object>",
(: do_teather($1) :) );
TP->add_command("unteather", TO, "<direct:living'"+str+"'>");
TP->add_command("untie", TO, "<direct:living'"+str+"'>");
TP->add_command("dress", TO,
"<direct:living'"+str+"'> {with|in} <indirect:object'garment'>",
(: do_dress($1) :) );
TP->add_command("offer", TO,
"<indirect:object:me'food'> to <direct:living'"+str+"'>",
(: do_offer($1) :) );
#ifdef DO_RUSTLE
if( _owner )
TP->add_command("rustle", TO, "<direct:living'"+str+"'>");
#endif
} /* init() */
/** @ignore yes */
varargs mixed query_contents( string start, object *things, int ignore_liv ) {
if( !arrayp( things ) ) {
things = TO->find_inv_match("", TP ) - ({ TP });
things = filter_array( things, (: ENV($1) == TO :) );
}
if( !sizeof( things ) )
return "";
if( _in_long )
things -= ({ _in_long });
if( !sizeof(things) )
return 0;
return ({ ::query_contents( start, things, ignore_liv ), "Ridden by" });
} /* query_contents() */
/**
* This method returns the string giving some indication about how tired
* the horse it.
* @return the exhaustion string of the horse
*/
string query_exhaustion_string() {
string start;
start = CAP( HE );
switch (_cur_exhaustion * 6 / _max_exhaustion) {
case 5 :
return start + " looks wonderfully happy and totally rested.\n";
case 4 :
return start + " looks a little tired.\n";
case 3 :
return start + " looks quite tired.\n";
case 2 :
return start + " looks really tired.\n";
case 1 :
return start + " looks exhausted.\n";
case 0 :
return start + " is totally exhausted.\n";
}
} /* query_exhaustion_string() */
/** @ignore yes */
string extra_look() { return query_exhaustion_string(); }
/** @ignore yes */
string long( string fluff, int dark ) {
string str;
str = "";
if( ENV(TP) == TO ) {
// They are in the horse...
_in_long = TP;
if( _inside_long ) {
str += "You are riding on:\n"+::long( fluff, dark );
} else {
str += "You are riding on:\n"+_inside_long+
query_exhaustion_string();
}
_in_long = 0;
str += "Around " + the_short() + " is:\n";
str += ( ENV(TO) ? ENV(TO)->long( fluff, dark ) :
"%^RED%^Limbo! Wandered into an inherit, did we?%^RESET%^\n");
} else {
str += ::long( fluff, dark );
}
return str;
} /* long() */
/** @ignore yes */
string pretty_short( object player, int no_riding ) {
string str;
if( sizeof( _riding - ({ player }) ) && !no_riding ) {
str = query_multiple_short( _riding - ({ player }) );
str = player->convert_message(str);
if( _transport_name ) {
return str + " riding " + ::pretty_short();
} else {
return str + " riding " + add_a( ::pretty_short() );
}
}
return ::pretty_short();
} /* pretty_short() */
/** @ignore yes */
object query_mirror_room() { return ENV(TO); }
/**
* This method sets the owner of the transport. This should be used
* when the owner is changed.
* @param owner the new owner of the transport
*/
void set_owner( string owner ) {
int *ids, i;
string classif;
object ob;
// Remove the effect off the old owner and add it to the new one.
if( _owner && ( ob = find_player(_owner) ) ) {
classif = TRANSPORT_OWNER_EFFECT->query_classification();
ids = ob->effects_matching(classif);
foreach( i in ids )
TRANSPORT_OWNER_EFFECT->remove_owner( ob, ob->arg_of( i ), i, TO );
}
_owner = owner;
if( _owner && ( ob = find_player(_owner) ) )
ob->add_effect( TRANSPORT_OWNER_EFFECT, TO );
} /* set_owner() */
/**
* This method sets the id of the transpot.
* @param id the id of the transport
*/
void set_transport_id( int id ) { _transport_id = id; }
/**
* This method returns the id of the transport.
* @return the id of the transport
*/
int query_transport_id() { return _transport_id; }
/**
* This method returns the current owner of the transport.
* @return the current owner of the transport
*/
string query_owner() { return _owner; }
/**
* This method returns the current owner of the transport, it is used by the
* pk checking code to make sure that this horse can be used in exciting
* pk ways.
* @return the owner
*/
string query_owner_pk_check() { return query_owner(); }
/**
* This method sets the inside long of the horse. This is what people
* will see when they are inside a horse.
* @param long the inside of the long of the horse
*/
void set_inside_long( string long ) { _inside_long = long; }
/**
* This method returns the inside long of the horse.
* @return the inside long of the horse
*/
string query_inside_long() { return _inside_long; }
/**
* This method sets the maximum exhaustion points for the transport.
* @param exhaust the maximum exhaustion points
*/
void set_maximum_exhaustion( int num ) {
_max_exhaustion = num;
_cur_exhaustion = num;
} /* set_maximum_exhaustion() */
/**
* This method returns the maximum exhaustion points for the transport.
* @return the maximum exhaustion points
*/
int query_maximum_exhaustion() { return _max_exhaustion; }
/**
* This method returns the current exhaustion points for the transport.
* @return the current exhaustion points
*/
int query_current_exhaustion() { return _cur_exhaustion; }
/**
* This method sets the current exhaustion points for the transport.
* @param points the current exhaustion points
*/
int set_current_exhaustion( int points ) { _cur_exhaustion = points; }
/**
* This method sets the maximum speed for the transport.
* @param the maximum speed
* @return 1 if successful, 0 on invalid speed
*/
int set_maximum_speed( int speed ) {
if( speed < RIDE_WALK || speed > RIDE_GALLOP )
return 0;
_max_speed = speed;
return 1;
} /* set_maximum_speed() */
/**
* This method queries the maximum speed for the transport.
* @return the maximum speed for the transport
*/
int query_maximum_speed() { return _max_speed; }
/**
* This method returns the string associated with the specified speed.
* @param speed the speed to check
* @param panic if the horse is panicing
* @return the name of the speed
*/
string query_speed_string( int speed, int panic ) {
if( panic )
return "flee";
switch( speed ) {
case RIDE_WALK :
return "walk";
case RIDE_CANTER :
return "canter";
case RIDE_GALLOP :
return "gallop";
case RIDE_TROT :
return "trot";
default :
return "unknown speed";
}
} /* query_speed_string() */
/**
* This method expands the name of the direction.
* @param dir the direction to expand
* @return the expanded name
*/
string expand_direction( string dir ) { return LENGTHEN[dir] || dir; }
/**
* This method checks to see if the specified person is allowed to do
* things to this transport.
* @param name the name of the person to check
* @return 1 if they can do things, 0 if not
*/
int is_allowed_to_use( string name ) {
if( _owner )
return name == _owner || ( sizeof(_allowed) &&
member_array( name, _allowed ) != -1 );
return 0;
} /* is_allowed_to_use() */
/**
* Mount the horse!
* @return 1 on success, 0 on failure
*/
int do_mount() {
class task_class_result result;
int ret, difficulty;
object worn;
string *str, pre;
IS_ALLOWED("You are not the owner and are not allowed to use $D.\n");
if( ENV(TP) == TO ) {
TP->add_failed_mess( TO, "You are already mounted on $D.\n");
return 0;
}
// Diffulty is the default plus any modifiers for what the horse is
// wearing (ie: saddle).
difficulty = query_default_mount_difficulty();
foreach( worn in query_wearing() )
if( worn->query_mount_difficulty_adjustment() )
difficulty -= worn->query_mount_difficulty_adjustment();
if( difficulty < 1 )
difficulty = 1;
// First lets do a skill check.
result = TASKMASTER_H->perform_task( TP,
TRANSPORT_SKILL_BASE + query_transport_type(),
difficulty, TM_FREE, 1 );
switch( result->result ) {
case AWARD :
write("%^YELLOW%^You feel more able to climb onto "+the_short()+
".\n%^RESET%^");
case SUCCEED :
if( ret = TP->move(TO) ) {
switch( ret ) {
case MOVE_TOO_HEAVY :
TP->add_failed_mess( TO, "You are unable to mount $D because "
"you are too heavy for "+HIM+".\n");
break;
default :
TP->add_failed_mess( TO, "You are unable to mount $D for some "
"reason ("+ret+").\n");
break;
}
return 0;
}
switch( result->degree ) {
case 0..10 :
str = ({ "You grab the back of $D and heave yourself onto the "
"creature with some difficulty, barely managing to keep "
"your balance.\n",
"$N grab$s the back of $D and heave$s $oself onto the "
"creature in a display of uncertain movements, then "
"wobble$s for a moment, but regains $p balance.\n"});
break;
case 11..40 :
str = ({ "You grab the back of $D and heave yourself onto the "
"creature.\n",
"$N grab$s the back of $D and heave$s $oself onto the "
"creature.\n"});
break;
case 41..80 :
str = ({ "You grab the back of $D and effortlessly heave yourself "
"onto the creature.\n",
"$N grab$s the back of $D and effortlessly heave$s "
"$oself onto the creature.\n"});
break;
default :
str = ({ "You grab the back of $D and swing yourself onto the "
"creature in a display of skill.\n",
"$N grab$s the back of $D and swing$s $oself onto the "
"creature in a display of skill.\n"});
}
TP->add_effect( TRANSPORT_EFFECT, TO );
TP->add_succeeded_mess( TO, str );
return 1;
case FAIL :
pre = "You attempt to get onto $D, but ";
switch( result->degree ) {
case -1000..-71 :
str = ({ pre + "fail to figure out how to even begin.\n",
"$N stare$s at $D with a stupefied expression.\n"});
break;
case -70..-41 :
str = ({ pre + "your grasp of the back of the creature slips "
"before you even start to climb.\n",
"$N grasp$s the back of $D as if attempting to mount "+
HIM+", but fail$s to get a proper hold.\n"});
break;
case -40..-16 :
str = ({ "You grab the back of $D, but don't quite manage to "
"heave yourself onto the creature.\n",
"$N grab$s the back of $D, but fail$s to heave $oself "
"onto the creature.\n"});
break;
default :
str = ({ "You grab the back of $D and heave yourself onto the "
"creature, but just as you are done, you lose your "
"balance and slide down the other side.\n",
"$N grab$s the back of $D and heave$s $oself onto the "
"creature, but slide$s down the other side after a "
"moment of wobbling.\n"});
}
TP->add_succeeded_mess( TO, str );
return 1;
}
} /* do_mount() */
/**
* This method returns whether or not we are following a path.
* @return 1 if we are following a path, 0 if not
*/
int query_path_follow() { return _riding_flags & PATH_FOLLOW; }
/**
* This method returns whether or not we should stop at an intersection.
* @return 1 if we should stop at an intersection, 0 if not
*/
int query_stop_at_intersection() {
return _riding_flags & STOP_AT_INTERSECTION;
} /* query_stop_at_intersection() */
/**
* This method returns whether or not we should stop after moving once.
* @return 1 if we should stop after moving once, 0 if not
*/
int query_stop_after_moving_once() {
return _riding_flags & STOP_AFTER_MOVING_ONCE;
} /* query_stop_at_intersection() */
/**
* Ride the horse off into the sunset.
* @return 1 on success, 0 on failure
*/
int do_ride( string dir, int speed, string type ) {
string *route;
if( ENV(TP) != TO ) {
TP->add_failed_mess( TO, "You must mount $D before you "
"can ride anywhere on "+HIM+".\n");
return 0;
}
dir = expand_direction(dir);
_cur_direction = dir;
_cur_speed = speed;
_last_location = 0;
_riding_flags = 0;
_route = 0;
switch( type ) {
case "intersection" :
case "once" :
case "forever" :
// Check to see if the exit exists in that direction.
if( ENV(TO)->query_destination(dir) == VOID_ROOM ) {
TP->add_failed_mess( TO, "There is no exit in the direction '"+
dir+"'.\n");
return 0;
}
break;
case "route" :
route = map( explode( replace_string( dir, " ", ""), ",") - ({ 0 }),
(: expand_direction($1) :) );
if( !sizeof(route) ) {
TP->add_failed_mess( TO, "Who are you kidding? $C$$D is only "
"capable of following sensible routes.\n");
return 0;
}
if( ENV(TO)->query_destination(route[0]) == VOID_ROOM ) {
TP->add_failed_mess( TO, "There is no exit in the direction '"+
route[0]+"'.\n");
return 0;
}
_route = route;
break;
case "roughly" :
case "roughly-intersection" :
if( member_array( dir, ({"north", "northeast", "east", "southeast",
"south", "southwest", "west", "northwest"}) ) == -1 ) {
TP->add_failed_mess( TO, "You can only $V roughly towards the "
"north, northeast, east, southeast, south, southwest, west "
"or northwest.\n");
return 0;
}
break;
default :
}
switch( type ) {
case "roughly" :
_riding_flags = PATH_FOLLOW;
break;
case "intersection" :
_riding_flags = STOP_AT_INTERSECTION;
break;
case "once" :
_riding_flags = STOP_AFTER_MOVING_ONCE;
break;
case "roughly-intersection" :
_riding_flags = PATH_FOLLOW | STOP_AT_INTERSECTION;
break;
case "route" :
_cur_direction = 0;
break;
case "forever" :
break;
default :
_riding_flags = STOP_AFTER_MOVING_ONCE;
}
TP->add_succeeded_mess( TO, ({"You start to "+
query_speed_string( _cur_speed, _panic )+" $D.\n", ""}) );
setup_movement();
return 1;
} /* do_ride() */
/**
* Stop the transport from running off.
* @return 1 on success, 0 on failure
*/
int do_stop() {
if( _cur_speed == RIDE_NOT_MOVING ) {
TP->add_failed_mess( TO, "$D is not moving.\n");
return 0;
}
// I want a skill check here, perhaps we would succeed in calming
// down the mount. - Sandoz
if( _panic ) {
TP->add_failed_mess( TO, "$D is currently panicing and unable "
"to be stopped.\n");
return 0;
}
_cur_speed = RIDE_NOT_MOVING;
remove_call_out(_move_id);
TP->add_succeeded_mess( TO, "$N $V $D.\n");
return 1;
} /* do_stop() */
/**
* This is used to get off the horse the easy way.
* @return 1 on success, 0 on failure
*/
int do_dismount() {
int ret;
if( ENV(TP) != TO ) {
TP->add_failed_mess( TO, "You need to be mounted on $D to "
"dismount.\n");
return 0;
}
if( !ENV(TO) ) {
TP->add_failed_mess( TO, "You cannot dismount $D for "+
HE+" is in limbo. Looks like you need professional help.\n");
return 0;
}
if( ret = TP->move( ENV(TO) ) ) {
TP->add_failed_mess( TO, "You failed to dismount $D for some "
"reason ("+ret+").\n");
return 0;
}
if( _cur_speed )
do_stop();
if( _panic ) {
tell_object( TP, "You throw yourself from "+the_short()+" who is "
"still panicing.\n");
tell_room( ENV(TO), TP->the_short()+" throws "+HIM+"self from "+
the_short()+" who is panicing.\n", ({ TP }) );
} else {
tell_object( TP, "You dismount from "+the_short()+".\n");
tell_room( ENV(TO), TP->the_short()+" dismounts from "+
the_short()+".\n", ({ TP }) );
}
TP->add_succeeded_mess( TO, "");
return 1;
} /* do_dismount() */
#ifdef DO_RUSTLE
/**
* This method will attempt to get the horse rustled by some nasty
* pk person.
* @return 1 on success, 0 on failure
*/
int do_rustle() {
object *bing;
// We need skill checks here.
// - Sandoz.
if( !_owner ) {
set_owner( TP->query_name() );
write("You successfuly rustle "+the_short()+".\n");
do_mount();
return 1;
}
if( _owner == TP->query_name() ) {
TP->add_failed_mess( TO, "You already own $D.\n");
return 0;
}
if( !pk_check( _owner, TP, 1 ) ) {
// Ok, so they are pkable...
if( sizeof(query_riders()) ) {
bing = query_riders();
TP->add_failed_mess( TO, "It is rather hard to rustle $D when $I "+
( sizeof(bing) > 1 ? "are" : "is")+" sitting on "+HIM+".\n", bing );
return 0;
}
// Do the rustle...
// event( ENV(TP), "theft", TO, TP, find_player(_owner), ({ TO }) );
TO->set_owner( TP->query_name() );
return 1;
}
TP->add_failed_mess( TO, "Both you and the owner of $D must be "
"playerkillers in order to rustle the creature.\n");
return 0;
} /* do_rustle() */
#endif
/**
* This method puts stuff on the horse, like saddle bags, saddles and things.
* @param obs the objects to try and dress the horse in
*/
int do_dress( object* obs ) {
object *good, *bad, blue, old_env;
mapping errors;
string result;
IS_ALLOWED("You are not the owner and are not allowed to use $D.\n");
// First check to make sure they are for the right race.
good = filter( obs, (: $1->query_wearable() &&
$1->query_worn_by_race() == query_race() :) );
if( !sizeof(good) ) {
TP->add_failed_mess( TO, "$I cannot be worn by $D.\n", obs );
return 0;
}
bad = ({ });
errors = ([ ]);
foreach( blue in good ) {
// Make sure they can wear it first.
if( result = WEAR_CMD->can_wear_or_remove( blue, TO ) ) {
result = replace_string( result, "you ", HE+" ");
if( !errors[result] )
errors[result] = ({ blue });
else
errors[result] += ({ blue });
good -= ({ blue });
} else {
old_env = ENV(blue);
if( !blue->move(TO) ) {
if( result = wear_armour( blue, 0 ) ) {
result = replace_string( result, "you ", HE+" ");
if( !errors[result] )
errors[result] = ({ blue });
else
errors[result] += ({ blue });
blue->move(old_env);
good -= ({ blue });
}
} else {
result = "could not be moved into "+HIS+" inventory";
if( !errors[result] )
errors[result] = ({ blue });
else
errors[result] += ({ blue });
good -= ({ blue });
}
}
}
if( !sizeof(good) ) {
foreach( result, bad in errors )
TP->add_failed_mess( TO, "You cannot dress $D with $I, because "+
( query_group( bad ) ? "they" : "it")+" "+result+".\n", bad );
} else {
TP->add_succeeded_mess( TO, "$N $V $D with $I.\n", good );
}
return sizeof(good) > 0;
} /* do_dress() */
/**
* This sets up the horse for being led. Basically it just makes the
* horse follow you.
* @return 1 on success, 0 on failure
*/
int do_lead() {
IS_ALLOWED("You are not the owner and are not allowed to lead $D.\n");
return command("follow " + TP->query_name() );
} /* do_lead() */
/**
* This sets the horses name.
* @param name the name to set the horse to
* @return 1 on success, 0 on failure
*/
int do_name( string name ) {
string str;
if( query_owner() != TP->query_name() ) {
TP->add_failed_mess( TO, "Sorry, only the owner can change the name "
"of $D.\n");
return 0;
}
str = lower_case(name);
if( sizeof(name) > MAX_NAME_LEN ) {
TP->add_failed_mess("Sorry, the maximum length of the name you can "
"give $D is "+query_num(MAX_NAME_LEN)+" characters.\n");
return 0;
}
if( sscanf( str, "%*scyber%*s") == 2 ||
sscanf( str, "%*spenis%*s") == 2 ||
sscanf( str, "%*scock%*s") == 2 ||
sscanf( str, "%*spussy%*s") == 2 ||
sscanf( str, "%*sfuck%*s") == 2 ||
sscanf( str, "%*sshit%*s") == 2 ||
sscanf( str, "%*sdragon%*s") == 2 ||
sscanf( str, "%*sfish%*s") == 2 ||
sscanf( str, "%*spimp%*s") == 2 ||
sscanf( str, "%*skiller%*s") == 2 ||
sscanf( str, "%*sslayer%*s") == 2 ) {
TP->add_failed_mess( TO, "Sorry, you cannot name $D as '"+
name+"'.\n");
return 0;
}
if( PLAYER_H->test_user(str) ) {
TP->add_failed_mess( TO, "You cannot name $D as '"+name+"', because "
"it is the name of a player.\n");
return 0;
}
if( PLAYER_H->test_banished(str) ) {
TP->add_failed_mess( TO, "You cannot name $D as '"+name+"'.\n");
return 0;
}
str = TP->convert_message( the_short() );
set_transport_name(name);
TP->add_succeeded_mess( TO, "$N set$s the name of "+str+" to "+
name+".\n");
return 1;
} /* do_name() */
/**
* This sets the horses name.
* @param name the name to set the horse to
* @return 1 on success, 0 on failure
*/
int do_offer( object *food ) {
object *ok, *last, ob;
IS_ALLOWED("Sorry, only the owner can offer food to $D.\n");
ok = filter( food, (: $1->query_edible() ||
( $1->query_liquid() && $1->query_continuous() ) :) );
last = ({ });
foreach( ob in ok ) {
if( ob->do_eat(1) == 2 ) {
last += ({ ob });
ok -= ({ ob });
}
}
if( !sizeof(ok) && !sizeof(last) ) {
TP->add_failed_mess( TO, "You cannot offer $I to $D, because it is "
"not food.\n", food );
return 0;
}
if( sizeof(last) )
TP->add_succeeded_mess( TO, "$N $V $I to $D, who munches happily on "
"the last of it.\n", last );
if( sizeof(ok) )
TP->add_succeeded_mess( TO, "$N $V $I to $D, who munches on it "
"happily.\n", ok );
return 1;
} /* do_offer() */
/**
* This allows someone to use the horse.
* @param person the person to allow
* @return 1 if successful, 0 if not
*/
int do_allow( string person ) {
int i;
if( TP->query_name() != _owner ) {
TP->add_failed_mess( TO, "You are not the owner of $D and cannot "
"change "+HIS+" allow list.\n");
return 0;
}
person = lower_case(person);
if( person == _owner ) {
TP->add_failed_mess( TO, "You are the owner of $D and don't need to "
"be added to "+HIS+" allow list.\n");
return 0;
}
if( sizeof( person ) > 20 || !PLAYER_H->test_user( person ) ) {
TP->add_failed_mess( TO, "There is no such player - "+
CAP(person)+".\n");
return 0;
}
if( !i = sizeof( _allowed ) ) {
_allowed = ({ person });
} else {
if( member_array( person, _allowed ) != -1 ) {
TP->add_failed_mess( TO, CAP(person)+" is already allowed to "
"ride $D.\n");
return 0;
}
if( i >= 10 ) {
TP->add_failed_mess( TO, "Sorry, you cannot allow more than ten "
"people to ride $D.\n");
return 0;
}
_allowed += ({ person });
}
TP->add_succeeded_mess( TO, ({"You allow "+CAP(person)+" to ride $D.\n",
""}) );
return 1;
} /* do_allow() */
/**
* This disallows someone to use the horse.
* @param person the person to disallow
* @return 1 if successful, 0 if not
*/
int do_disallow( string person ) {
if( TP->query_name() != _owner ) {
TP->add_failed_mess( TO, "You are not the owner of $D and cannot "
"change "+HIS+" allow list.\n");
return 0;
}
person = lower_case(person);
if( person == _owner ) {
TP->add_failed_mess( TO, "You cannot disallow yourself from riding "
"$D, because you are the owner of the creature.\n");
return 0;
}
if( !sizeof(_allowed) ) {
TP->add_failed_mess( TO, "No-one but you are allowed to ride $D.\n");
return 0;
}
if( member_array( person, _allowed ) == -1 ) {
TP->add_failed_mess( TO, CAP(person)+" is already not allowed to "
"ride $D.\n");
return 0;
}
_allowed -= ({ person });
TP->add_succeeded_mess( TO, ({"You disallow "+CAP(person)+" from "
"riding $D.\n", ""}) );
return 1;
} /* do_disallow() */
/**
* This method lists the people currently able to use your transports.
* @return 1 on success, 0 on failure
*/
int do_list() {
if( TP->query_name() != _owner ) {
TP->add_failed_mess( TO, "You are not the owner of $D and cannot "
"view "+HIS+" allow list.\n");
return 0;
}
if( !sizeof(_allowed) )
TP->add_succeeded_mess( TO, ({"No-one but you are currently allowed "
"to ride $D.\n", ""}) );
else
TP->add_succeeded_mess( TO, ({ query_multiple_short( map(
asort( _allowed ), (: CAP($1) :) ) )+" "+( sizeof(_allowed) == 1 ?
"is" : "are")+" currently allowed to ride $D.\n", ""}) );
return 1;
} /* do_list() */
/**
* This method is used to teather the mount to something.
* The horse must be wearing something that responds to the id 'teather'.
* @param obs the thing to teather us too
* @return 1 on success, 0 on failure
*/
int do_teather( object *obs ) {
object *teathers, *to;
IS_ALLOWED("Sorry, only the owner can teather $D.\n");
teathers = filter( query_wearing(),
(: $1->query_property(TRANSPORT_TEATHER_PROP) :) );
if( !sizeof(teathers) ) {
TP->add_failed_mess( TO, "$D $V$0=does,do$V$ not have anything for "
"you to teather "+verbalize("them", HIM )+" with.\n");
return 0;
}
if( sizeof(obs) > 1 ) {
TP->add_failed_mess( TO, "You cannot teather "+the_short()+" to more "
"than one thing.\n", obs );
return 0;
}
to = filter( obs, (: !living($1) && ENV($1) == ENV(TO) &&
( $1->query_property(TRANSPORT_TEATHER_PROP) ||
$1->id("pole") ) :) );
if( !sizeof(to) ) {
TP->add_failed_mess( TO, "You cannot teather "+the_short()+" to "
"$I.\n", obs );
return 0;
}
// Set us up to be teathered to something.
set_teathered_to( to[0] );
TP->add_succeeded_mess( TO, "$N $V $D to $I.\n", to );
return 1;
} /* do_teather() */
/**
* This method unteathers the mount if it is teathered to something.
* @return 1 on success, 0 on failure
*/
int do_unteather() {
object teather;
if( !teather = query_teathered_to() ) {
TP->add_failed_mess( TO, "You cannot unteather $D because "+
verbalize("they aren't", HE+" isn't")+" teathered.\n");
return 0;
}
IS_ALLOWED("Sorry, only the owner can unteather $D.\n");
set_teathered_to(0);
TP->add_succeeded_mess("$N $V $D from $I.\n", ({ teather }) );
return 1;
} /* do_unteather() */
/**
* This method is called when ever someone we are following causes us to
* move.
*/
int check_doing_follow( object thing, string verb, string special ) {
int exhaust;
exhaust = time() - _last_move;
if( !exhaust ) {
exhaust = _max_speed;
} else if( exhaust < _transport_speed ) {
exhaust = _transport_speed / exhaust;
} else {
exhaust = 1;
}
// Let the exhaustion go down a lot slower when being led as opposed
// to ridden.
exhaust--;
// Try and figure out how much exhaustion this move should give the
// horse..
if( _cur_exhaustion < exhaust * exhaust ) {
tell_room( TO, the_short()+" looks too tired to move.\n");
tell_object( thing, the_short()+" is too tired to follow you.\n");
return 0;
}
_cur_exhaustion -= exhaust * exhaust;
return 1;
} /* check_doing_follow() */
/** @ignore yes */
protected void inform_of_move( string dir ) {
object ob;
int dark;
foreach( ob in query_riders() ) {
dark = ob->check_dark( ENV(TO)->query_light() );
tell_object( ob, "You "+query_speed_string( _cur_speed, _panic )+" "+
dir+" on "+the_short()+".\n");
if( ob->query_verbose("look") ) {
tell_object( ob, ENV(TO)->long( 0, dark ) );
} else {
tell_object( ob, GLANCE_CMD->room_glance( ob, ENV(TO), dark ) );
}
}
} /* inform_of_move() */
/**
* This returns the list of directions the transport can currently
* move from this location. It takes in account things like size and
* if the exit is obvious or not.
* @param no_last don't go back where we came from
*/
string *query_allowed_directions( int no_last ) {
mixed dest_other, data;
string *ret;
int i;
dest_other = ENV(TO)->query_dest_other();
ret = ({ });
for( i = sizeof( dest_other ) - 2; i > -1; i -= 2 ) {
data = dest_other[ i + 1 ];
if( data[ROOM_OBV] && !data[ROOM_REL] &&
data[ROOM_SIZE] >= query_height() &&
!( no_last && data[ROOM_DEST] == _last_location ) )
ret += ({ dest_other[ i ] });
}
return ret;
} /* query_allowed_directions() */
private string do_move_direction( string direction ) {
int difficulty, result;
object worn;
string *allowed, new_direction;
allowed = query_allowed_directions(1);
// No such exit at all.
if( member_array( direction, allowed ) == -1 )
return 0;
// They could go in a different direction!
if( sizeof(allowed) > 1 )
new_direction = choice( allowed );
if( new_direction && !_panic ) {
// Difficulty is the default plus any modifiers for what the horse is
// wearing (ie: saddle).
difficulty = query_default_direction_difficulty();
foreach( worn in query_wearing() ) {
if( worn->query_direction_difficulty_adjustment() )
difficulty -= worn->query_mount_difficulty_adjustment();
}
if( difficulty < 1 )
difficulty = 1;
// First lets do a skill check.
result = TASKMASTER_H->perform_task( TP,
TRANSPORT_SKILL_BASE + query_transport_type(),
difficulty, TM_FREE );
switch( result ) {
case AWARD :
tell_object( TP, "%^YELLOW%^You feel more able to direct "+
the_short()+" than ever before.\n%^RESET%^");
case SUCCEED :
break;
case FAIL :
if( new_direction == direction ) {
write("You fail totally in your attempt to direct "+
the_short()+" to the "+direction+", but "+
HE+" decides to wander there anyway.\n");
} else {
write("You attempt to move "+the_short()+" to the "+
direction+", but "+HE+" attempts to wander off to the "+
new_direction+" instead.\n");
direction = new_direction;
}
break;
}
}
command(direction);
return direction;
} /* do_move_direction() */
/**
* This method turns a standard direction into a number.
* @param direction the direction to turn into a number
* @return the number associated with the direction
*/
int query_direction_as_number( string direction ) {
switch( direction ) {
case "north" :
return 0;
case "northeast" :
return 1;
case "east" :
return 2;
case "southeast" :
return 3;
case "south" :
return 4;
case "southwest" :
return 5;
case "west" :
return 6;
case "northwest" :
return 7;
default :
return -1;
}
} /* query_direction_as_number() */
private int query_direction_weight( string direction ) {
int weight, direction_num, dest_num, dist;
mixed stuff;
stuff = ENV(TO)->query_dest_other(direction);
weight = stuff[ROOM_SIZE];
if( stuff[ROOM_DEST] == _last_location )
weight -= 100;
direction_num = query_direction_as_number(direction);
dest_num = query_direction_as_number(_cur_direction);
if( direction_num != -1 && dest_num != -1 ) {
if( dest_num > direction_num ) {
dist = dest_num - direction_num;
if( dist > 4 )
dist = direction_num + 8 - dest_num;
} else {
dist = direction_num - dest_num;
if( dist > 4 )
dist = dest_num + 8 - direction_num;
}
weight += ( 4 - dist ) * 75;
}
return weight;
} /* query_direction_weight() */
private int do_actual_move() {
string *exits, highest_exit, str;
int highest_weight, weight;
object old;
old = ENV(TO);
// A route to follow.
if( sizeof(_route) && !_cur_direction && !_panic ) {
str = _route[0];
_route = _route[1..];
if( str = do_move_direction(str) ) {
// Tell all the people here about it.
_last_location = file_name(old);
inform_of_move(str);
if( sizeof(_route) )
return 1;
}
tell_object( TP, the_short()+" finishes following "+
HIS+" route and stops, waiting for more wild commands.\n");
return 0;
}
// Attempting to sort of follow a path. Heuristics, don't go back the
// way we came. Try and follow the biggest exit. Choose an exit in
// roughly the direction we are going.
if( query_path_follow() || _panic ) {
exits = query_allowed_directions(0);
do {
foreach( str in exits ) {
weight = query_direction_weight(str);
if( weight > highest_weight ) {
highest_weight = weight;
highest_exit = str;
}
}
// If the best exit takes us back where we were... Stop...
if( ENV(TO)->query_destination(highest_exit) == _last_location ) {
if( _panic ) {
_panic--;
exits -= ({ highest_exit });
} else {
tell_object( TP, the_short()+" could not find anywhere "
"else to go and stops, waiting for you to do "
"something.\n");
return 0;
}
} else {
if( str = do_move_direction(highest_exit) ) {
// Tell all the people about it.
_last_location = file_name(old);
inform_of_move(str);
if( _panic ) {
if( !--_panic ) {
tell_riders( the_short()+" calms down and stops "
"panicing.\n");
return 0;
}
}
return 1;
} else {
exits -= ({ highest_exit });
}
}
} while( _panic && sizeof(exits) );
tell_object( TP, the_short()+" could not find anywhere else to go "
"and stops, waiting for you to do something.\n");
return 0;
}
// A specific direction.
if( str = do_move_direction(_cur_direction) ) {
// Tell all the people about it.
_last_location = file_name(old);
inform_of_move(str);
return 1;
}
tell_object( TP, the_short()+" stops since there is no "+
_cur_direction+" exit here, or "+HE+" was unable to go that way.\n");
return 0;
} /* do_actual_move() */
/**
* This method moves the horse.
*/
void do_move_transport() {
int fluff, divide, i, ok;
string *exits;
if( _cur_speed == RIDE_NOT_MOVING )
return;
// Figure out how many bits to move per section.
divide = _transport_speed / 2;
if( divide == 0 )
divide = 1;
if( _cur_speed < divide )
fluff = 1;
else
fluff = _cur_speed / divide;
// They don't stop when they are panicing.
if( _cur_exhaustion <= _cur_speed * _cur_speed && !_panic ) {
tell_riders( the_short()+" is too exhausted to move any further.\n");
return;
}
// Decrease the exhaustion first.
_cur_exhaustion -= _cur_speed * _cur_speed;
// Add in the call_out first to make it less likely for the horse to get
// stuck panicing (for instance).
if( _cur_speed < divide )
_move_id = call_out( (: do_move_transport :),
divide * 2 / _cur_speed );
else
_move_id = call_out( (: do_move_transport :), 2 );
// Do the move, see if it succeeded...
// See how fast we are supposed to be moving.
for( i = 0; i < fluff; i++ ) {
ok = do_actual_move();
// See if we should stop after moving once.
if( query_stop_after_moving_once() ) {
_cur_speed = RIDE_NOT_MOVING;
remove_call_out(_move_id);
_move_id = 0;
return;
}
// See if we are at an intersection.
// If we are, then stop...
if( query_stop_at_intersection() ) {
exits = query_allowed_directions(1);
if( sizeof(exits) > 1 ) {
// Inform the player and stop.
tell_riders( the_short()+" stops and waits for your next "
"direction as you arrive at an intersection.\n");
_cur_speed = RIDE_NOT_MOVING;
remove_call_out(_move_id);
_move_id = 0;
return;
}
}
if( !ok ) {
_cur_speed = RIDE_NOT_MOVING;
remove_call_out(_move_id);
_move_id = 0;
return;
}
}
} /* do_move_transport() */
/**
* Sets up the call_out if needed.
*/
void setup_movement() {
if( _cur_speed == RIDE_NOT_MOVING )
return;
if( find_call_out(_move_id) == -1 ) {
int divide;
// Figure out how many bits to move per section.
divide = _transport_speed / 2;
if( divide == 0 )
divide = 1;
if( _cur_speed < divide )
_move_id = call_out( (: do_move_transport :),
divide * 2 / _cur_speed );
else
_move_id = call_out( (: do_move_transport :), 2 );
}
} /* setup_movement() */
/**
* This sets the speed at which the transport defaults to moving. This is
* how long it takes to move one room by default.
* @param the default number of action points
*/
void set_transport_speed( int num ) { _transport_speed = num; }
/**
* This method returns the amount of time it takes to move the transport
* one room.
* @return the amount of time to move one room
*/
int query_transport_speed() { return _transport_speed; }
/**
* This method sets the default mount difficulty for the transport.
* @parm difficulty The default difficulty
*/
void set_default_mount_difficulty( int difficulty ) {
_default_mount_difficulty = difficulty;
} /* set_default_mount_difficulty() */
/**
* This method returns the default mount difficulty for the transport.
* @return the default difficulty
*/
int query_default_mount_difficulty() {
return _default_mount_difficulty;
} /* query_default_mount_difficulty() */
/**
* This method determines the default directional control of the transport.
* (no reins? Where do you think your going?)
* @param difficulty the default directional difficulty
*/
void set_default_direction_difficulty( int difficulty ) {
_default_direction_difficulty = difficulty;
} /* set_default_direction_difficulty() */
/**
* This method determines the default directional control of the transport.
* (no reins? Where do you think your going?)
* @return the default directional difficulty
*/
int query_default_direction_difficulty() {
return _default_direction_difficulty;
} /* query_default_direction_difficulty() */
/**
* This method determines the default difficulty of controlling the
* transport in combat.
* @param difficulty the default difficulty of controlling the mount in combat
*/
void set_default_fight_difficulty( int difficulty ) {
_default_fight_difficulty = difficulty;
} /* set_default_fight_difficulty() */
/**
* This method returns the default difficulty of controlling the
* mount in combat.
* @return the default difficulty of controlling the mount in combat
*/
int query_default_fight_difficulty() {
return _default_fight_difficulty;
} /* query_default_fight_difficulty() */
/**
* This method returns the type of transport that this is.
* @return the type of transport
*/
string query_transport_type() { return _transport_type; }
/**
* This method returns the type of transport that this is.
* @param type the type of transport
*/
string set_transport_type( string type ) { _transport_type = type; }
/**
* This method sets the name of the transport.
* @param name sets the name of the transport
*/
void set_transport_name( string name ) { _transport_name = name; }
/**
* This method returns the name of the transport.
* @return the name of the transport
*/
string query_transport_name() { return _transport_name; }
/** @ignore yes */
string query_quit_handler() { return "/room/transport_start"; }
/**
* This method returns the current list of people riding the
* transport.
* @return the list of people riding the transport
*/
object *query_riders() { return _riding; }
/**
* This method checks to see if the person specified is actually a
* rider.
* @return 1 if they are a rider, 0 if not
*/
int is_rider( object ob ) { return member_array( ob, _riding ) != -1; }
/**
* This method tells the riders about something.
* @param str the message to tell the riders
*/
void tell_riders( string str ) {
object thing;
foreach( thing in query_riders() )
tell_object( thing, str );
} /* tell_riders() */
/**
* Fixes up the current list of people riding the transport.
*/
void fixup_riders() {
object *old_riders, ob;
int *ids, fluff;
old_riders = _riding;
_riding = filter( INV(TO), (: living($1) &&
sizeof( $1->effects_matching(
TRANSPORT_EFFECT->query_classification() ) ) :) );
old_riders -= _riding + ({ 0 });
foreach( ob in old_riders ) {
ids = ob->effects_matching( TRANSPORT_EFFECT->query_classification() );
foreach( fluff in ids )
ob->delete_effect(fluff);
}
} /* fixup_riders() */
/**
* This method sets the object we are teathered to.
* @param ob the object we are teathered to, 0 if we are not teathered
*/
void set_teathered_to( object ob ) {
_teathered_to = ob;
if( ob ) {
_teathered_to_short = ob->query_short();
set_position("teathered");
set_position_type("to");
set_position_on(_teathered_to);
} else {
_teathered_to_short = 0;
set_position(0);
set_position_type(0);
set_position_on(0);
// return_to_default_position(0);
}
} /* set_teathered_to() */
/**
* This method returns the object we are currently teathetred to.
* @return the object we are teathered to
*/
object query_teathered_to() { return _teathered_to; }
/**
* This method is called to make the object slide off.
* @param ob the object sliding off
*/
void do_slide_off( object thing ) {
if( ENV(thing) == TO && ( !living(thing) ||
!sizeof( thing->effects_matching(
TRANSPORT_EFFECT->query_classification() ) ) ) &&
thing != _me_repeater && thing != _env_repeater &&
member_array( thing, query_wearing() ) == -1 ) {
if( thing->move( ENV(TO) ) ) {
tell_creator("sandoz", "Failed to move "+
thing->the_short()+" from "+the_short()+" to "+
ENV(TO)->the_short()+".\n");
} else {
tell_room( TO, thing->the_short()+" slides off "+
the_short()+".\n");
}
} else if( living(thing) ) {
fixup_riders();
}
} /* do_slide_off() */
/** @ignore yes */
void repeat_event_person_say( object caller, object ob, string start,
string mess, string lang ) {
object *obs;
if( ob == TO ) {
obs = INV(TO) - ({ _me_repeater });
obs->event_person_say( ob, start, mess, lang );
} else if( caller == _me_repeater ) {
// Here, so echo it to the environment.
obs = INV( ENV(TO) ) - ({ TO, _env_repeater });
obs->event_person_say( ob, "From the "+pretty_short( TP, 1 )+" "+start,
mess, lang );
} else {
obs = INV(TO) - ({ _me_repeater });
obs->event_person_say( ob, "From the room "+start, mess, lang );
}
} /* repeat_event_person_say() */
/** @ignore yes */
void repeat_event_emote( object caller, object ob, string mess ) {
object *obs;
if( ob == TO ) {
obs = INV(TO) - ({ _me_repeater });
obs->event_emote( ob, mess );
} else if( caller == _me_repeater ) {
// Here, so echo it to the environment.
obs = INV( ENV(TO) ) - ({ TO, _env_repeater });
obs->event_emote( ob, "From the "+pretty_short( TP, 1 )+" "+mess );
} else {
obs = INV(TO) - ({ _me_repeater });
obs->event_emote( ob, "From the room "+mess );
}
} /* repeat_event_emote() */
/** @ignore yes */
void repeat_event_say( object caller, object ob, string str,
mixed avoid ) {
object *obs;
// The environment repeater is actually in us, creates a loop.
// - Sandoz.
if( caller == _env_repeater && ENV( _env_repeater ) == TO )
return;
if( ob == TO ) {
obs = INV(TO) - ({ _me_repeater });
obs->event_say( ob, str, avoid );
} else if( caller == _me_repeater ) {
// Here, so echo it to the environment.
obs = INV( ENV(TO) ) - ({ TO, _env_repeater });
obs->event_say( ob, str, avoid );
} else {
obs = INV(TO) - ({ _me_repeater });
obs->event_say( ob, str, avoid );
}
} /* repeat_event_say() */
/** @ignore yes */
void repeat_event_enter( object caller, object thing, string mess,
object prev ) {
object *obs;
// Things should slide off...
if( ENV(thing) == TO )
call_out( (: do_slide_off :), 2, thing );
if( !stringp(mess) && thing != TO )
return;
if( caller == _env_repeater ) {
// Here, so echo it to the environment.
obs = INV(TO) - ({ _me_repeater });
obs->event_enter( thing, mess, prev );
}
} /* repeat_event_enter() */
/** @ignore yes */
void repeat_event_exit( object caller, object thing, string mess,
object dest ) {
object *obs;
if( ENV(thing) == TO && living(thing) )
call_out( (: fixup_riders :), 2 );
if( !stringp(mess) && thing != TO )
return;
if( caller == _env_repeater ) {
// Here, so echo it to the environment.
obs = INV(TO) - ({ _me_repeater });
obs->event_enter( thing, mess, dest );
}
} /* repeat_event_exit() */
/** @ignore yes */
void repeat_event_soul( object caller, object thing, string mess,
object *avoid ) {
object *obs;
if( thing == TO ) {
obs = INV(TO) - ({ _me_repeater });
obs->event_soul( thing, mess, avoid );
} else if( caller == _me_repeater ) {
// Here, so echo it to the environment.
obs = INV( ENV(TO) ) - ({ TO, _env_repeater });
obs->event_soul( thing, mess, avoid );
} else {
obs = INV(TO) - ({ _me_repeater });
obs->event_soul( thing, mess, avoid );
}
} /* repeat_event_soul() */
/**
* This method creates the repeaters used for delivering the event
* messages to us.
*/
void create_repeaters() {
if( !_me_repeater ) {
_me_repeater = clone_object(TRANSPORT_REPEATER);
_me_repeater->set_repeater_owner(TO);
_me_repeater->move(TO);
}
if( !_env_repeater ) {
_env_repeater = clone_object(TRANSPORT_REPEATER);
_env_repeater->set_repeater_owner(TO);
_env_repeater->move( ENV(TO) );
}
} /* create_repeater() */
/** @ignore yes */
private void check_teathered_to() {
object* obs;
obs = match_objects_for_existence( _teathered_to_short, ENV(TO) );
if( sizeof(obs) )
set_teathered_to( obs[0] );
else
set_teathered_to(0);
} /* check_teathered_to() */
/** @ignore yes */
varargs int move( mixed dest, string messin, string messout ) {
int ret;
_last_move = time();
ret = ::move( dest, messin, messout );
if( ret == MOVE_OK ) {
if( _env_repeater )
_env_repeater->move(dest);
if( !_env_repeater || !_me_repeater )
call_out( (: create_repeaters :), 2 );
}
return ret;
} /* move() */
/**
* This method is called by the ownership effect when the player logs off.
*/
void retire() {
// So that we wouldn't dest players and whatnot.
// - Sandoz
if( ENV(TO) ) {
foreach( object ob in INV(TO) ) {
if( living(ob) ) {
tell_object( ob, "You jump off "+the_short()+".\n");
ob->move_with_look( ENV(TO),
"$N jump$s off "+the_short()+".");
}
}
}
move("/room/rubbish", 0, "$N disappear$s in a puff of fluff.");
} /* retire() */
/** @ignore yes */
void dest_me() {
if( _env_repeater )
_env_repeater->dest_me();
if( _me_repeater )
_me_repeater->dest_me();
if( _owner )
set_owner(0);
// So that we wouldn't dest players and whatnot.
// - Sandoz
if( ENV(TO) ) {
string str = PLAYER_OBJ->convert_message( the_short() );
foreach( object ob in INV(TO) ) {
if( living(ob) ) {
tell_object( ob, "You jump off "+str+".\n");
ob->move_with_look( ENV(TO),
"$N jump$s off "+str+".");
} else {
ob->move("/room/rubbish");
}
}
} else {
INV(TO)->move("/room/rubbish");
}
::dest_me();
} /* dest_me() */
/** @ignore yes */
int attack_ob( object ob ) {
ob->stop_all_fight();
TO->stop_all_fight();
return 0;
} /* attack_ob() */
/** @ignore yes */
int second_life() {
// If we die, move the riders into our environment.
query_riders()->move( ENV(TO) );
return 0;
} /* second_life() */
/** @ignore yes */
int query_ok_turn_off_heart_beat() {
return _cur_exhaustion < _max_exhaustion;
} /* query_ok_turn_off_heart_beat() */
/** @ignore yes */
void heart_beat() {
if( _cur_exhaustion < _max_exhaustion )
_cur_exhaustion++;
::heart_beat();
} /* heart_beat() */
/**
* @ignore yes
* Stuff to mess with light levels...
*/
int query_light() {
if( PO == ENV(TO) || !ENV(TO) )
return ::query_light();
return ENV(TO)->query_light();
} /* query_light() */
/**
* This method panics the horse when it encounters a fight.
* @param multiplier make the difficulty harder
*/
void panic_horse( int multiplier ) {
object main_rider, *riders, rider;
int result, panic, bonus;
if( !multiplier )
multiplier = 1;
if( sizeof( query_riders() ) ) {
main_rider = query_riders()[0];
// First lets do a skill check.
bonus = _default_fight_difficulty * multiplier;
if( _cur_speed != RIDE_NOT_MOVING )
bonus = bonus / ( _cur_speed + TRANSPORT_PANIC_RIDING_DIVISOR );
result = TASKMASTER_H->perform_task( main_rider,
TRANSPORT_SKILL_BASE + query_transport_type(),
_default_fight_difficulty * multiplier, TM_FREE );
switch( result ) {
case AWARD :
tell_object( main_rider, "%^YELLOW%^You feel more able to "
"control "+the_short()+" in combat than you did "
"before.\n%^RESET%^");
case SUCCEED :
tell_object( main_rider, "You manage to keep "+
the_short()+" calm in the fight going on around you.\n");
tell_room( ENV(TO), main_rider->the_short()+" manages to keep "+
the_short()+" calm in the fight going on around you.\n",
({ main_rider }) );
return;
case FAIL :
tell_object( main_rider, "Despite your efforts to keep "+
the_short()+" calm in the fight going on around you, "
"you lose control over "+HIM+".\n");
tell_room( ENV(TO), main_rider->the_short()+" loses control "
"over "+the_short()+" in the fight going on around you.\n",
({ main_rider }) );
// Check and see if they get tossed off...
result = TASKMASTER_H->perform_task( main_rider,
TRANSPORT_SKILL_BASE + query_transport_type(),
( _default_fight_difficulty * multiplier ) / 2, TM_FREE );
switch( result ) {
case AWARD :
tell_object( main_rider, "%^YELLOW%^You feel more able to "
"stay on the horse and not be thrown off.\n%^RESET%^");
case 1 :
tell_object( main_rider, the_short()+" tries to throw you "
"off, but you manage to keep your seating.\n");
tell_room( ENV(TO), the_short()+" tries to throw "+
main_rider->the_short()+" off, but "+
main_rider->HE+" manages to keep "+
main_rider->HIS+" seating.\n", ({ main_rider }) );
break;
case 0 :
// Throw off all the riders.
riders = query_riders();
foreach( rider in riders ) {
tell_object( rider, the_short()+" throws you off "+
HIS+" back.\n");
rider->move( ENV(TO) );
}
tell_room( ENV(TO), the_short()+" throws "+
query_multiple_short(riders)+" off "+HIS+" back.\n",
riders );
break;
}
break;
}
}
// Panic!
panic = _panic;
_panic += 4 * multiplier;
_cur_speed++;
if( _cur_speed > _max_speed )
_cur_speed = _max_speed;
setup_movement();
if( !panic )
tell_room( ENV(TO), the_short()+" panics and makes a run for it!\n");
_last_panic = time();
} /* panic_horse() */
/**
* If there is a fight nearby the horsey gets a little upset.
* @ignore yes
*/
void event_fight_in_progress( object attacker, object attackee ) {
if( time() - _last_panic > 10 ) {
panic_horse(TRANSPORT_PANIC_NORMAL_MULTIPLIER);
_last_panic = time();
}
} /* event_fight_in_progress() */
/**
* @ignore yes
* This method allows the container to have stuff inside it checked.
* @param looker the person doing the checking
* @return 1 on success, 0 on failur
*/
int can_find_match_reference_inside_object( object thing, object looker ) {
if( is_rider(thing) )
return 1;
if( ::can_find_match_reference_inside_object( thing, looker ) )
return 1;
return 0;
} /* can_find_match_recurse_into() */
/**
* @ignore yes
* We mess with the followers array so that anything riding the horse that
* has stuff following it, they follow the horse...
* @return all the followers
*/
object *query_followers() {
object *ret, ob;
// No one can follow us as we zip along. This should probably check
// for other horses...
if( _cur_speed > RIDE_WALK )
return ({ });
ret = ::query_followers();
foreach( ob in query_riders() )
ret |= ob->query_followers();
return ret;
} /* query_followers() */
/**
* This method will tell if the object is a transport or not.
* @return 1 if it is a transport
*/
int query_transport() { return 1; }
/** @ignore yes */
int adjust_hp( int damage, object attacker, object weapon, string type ) {
int ret, chance, ac;
object rider;
string zone;
// Do the pk checks and panicing first.
if( attacker && damage < 0 && _owner && pk_check( attacker, _owner ) )
return 0;
if( damage < 0 )
panic_horse(TRANSPORT_PANIC_IN_BATTLE_MULTIPLIER);
// Figure out the attack zone... We will assume that the player
// is more likely to try and hit the person on the horse.
if( !sizeof( query_riders() ) || damage >= 0 )
return ::adjust_hp( damage, attacker );
// If the type is anything but one of the standard ones, pass on all the
// damage.
switch (type) {
case "blunt" :
case "sharp" :
case "pierce" :
// Figure out armour defenses...
ret = ::adjust_hp( damage, attacker );
damage = damage / 2;
// Do AC stuff and so on. More likely to hit legs than torso...
switch( random(40) ) {
case 0..1:
zone = "head";
break;
case 2:
zone = "neck";
break;
case 3..6:
zone="chest";
break;
case 7..16:
zone="abdomen";
break;
case 17..19:
zone = "arms";
break;
case 20..21:
zone = "hands";
break;
case 22..37:
zone = "legs";
break;
case 38..39:
zone = "feet";
break;
}
foreach( rider in query_riders() ) {
ac = rider->query_ac( type, damage, zone );
if( damage - ac > 0 )
rider->adjust_hp( damage - ac, attacker );
// Now check to see if they get tossed off.
chance = damage - ac * weapon->query_weight();
switch( TASKMASTER_H->perform_task( TP,
TRANSPORT_SKILL_BASE + query_transport_type(), chance,
TM_FREE ) ) {
case 2 :
tell_object( rider, "%^YELLOW%^You feel more able to stay "
"mounted on "+the_short()+" while being hit.\n");
case 1 :
break;
case 0 :
tell_object( rider, attacker->poss_short()+" blow make you lose "
"balance and you fall off "+the_short()+".\n");
tell_room( ENV(TO), attacker->poss_short()+" blow makes "+
rider->the_short()+" lose balance and "+rider->HE+" falls "
"off "+the_short()+".\n");
rider->move( ENV(TO) );
break;
}
}
break;
default :
query_riders()->adjust_hp( damage, attacker );
ret = ::adjust_hp( damage, attacker );
break;
}
return ret;
} /* adjust_hp() */
/** @ignore yes */
int attack_by( object ob ) {
object controller;
if( !_owner )
return ::attack_by( ob );
if( ob->query_summoned() && ( controller = ob->query_owner() ) ) {
if( pk_check( _owner, controller ) ) {
tell_object( controller, "%^YELLOW%^$C$"+ob->one_short()+" tells "
"you: I don't want to attack "+the_short()+"%^RESET%^.\n" );
TO->stop_all_fight();
ob->stop_fight(TO);
ob->go_away();
return 0;
}
}
if( pk_check( _owner, ob ) ) {
tell_object( ob, "You feel it would be wrong to attack "+
the_short()+".\n");
TO->stop_all_fight();
ob->stop_all_fight();
return 0;
}
panic_horse(TRANSPORT_PANIC_IN_BATTLE_MULTIPLIER);
query_riders()->attack_by(ob);
return ::attack_by( ob );
} /* attack_by() */
/**
* @ignore yes
* We are still in the same zone as the horse.
*/
string *query_zones() {
if( ENV(TO) )
return ENV(TO)->query_zones();
return ({ });
} /* query_zones() */
/** @ignore yes */
mapping query_dynamic_auto_load() {
mapping map;
// Do this so we won't save the repeaters with our inventory.
// - Sandoz.
if( _me_repeater )
_me_repeater->move(VOID_ROOM);
if( _env_repeater && ENV(_env_repeater) == TO )
_env_repeater->move(VOID_ROOM);
map = (["::" : ::query_dynamic_auto_load() ]);
if( _me_repeater )
_me_repeater->move(TO);
if( _env_repeater )
_env_repeater->move(ENV(TO));
if( _owner )
map["owner"] = _owner;
if( _route )
map["route"] = _route;
if( _transport_name )
map["name"] = _transport_name;
if( _cur_direction )
map["cur direction"] = _cur_direction;
if( _cur_speed )
map["cur speed"] = _cur_speed;
if( _last_location )
map["last location"] = _last_location;
if( _riding_flags )
map["riding flags"] = _riding_flags;
if( _panic )
map["panic"] = _panic;
if( _cur_exhaustion )
map["cur exhaustion"] = _cur_exhaustion;
if( _transport_id )
map["transport id"] = _transport_id;
if( _teathered_to_short )
map["teathered to"] = _teathered_to_short;
if( _allowed )
map["allowed"] = _allowed;
return map;
} /* query_dynamic_auto_load() */
/** @ignore yes */
void init_dynamic_arg( mapping map ) {
if( map["::"] )
::init_dynamic_arg(map);
// Do not use set_owner() here, because it will add the owner effect to
// the player again, and thus breaking their transports array.
// - Sandoz
_owner = map["owner"];
// Set them up to zip off if they were when they exited.
_cur_direction = map["cur direction"];
_route = map["route"];
_transport_name = map["name"];
_cur_speed = map["cur speed"];
_last_location = map["last location"];
_panic = map["panic"];
_riding_flags = map["riding flags"];
_cur_exhaustion = map["cur exhaustion"];
_transport_id = map["transport id"];
_teathered_to_short = map["teathered to"];
_allowed = map["allowed"];
if( _teathered_to_short )
call_out( (: check_teathered_to :), 2 );
setup_movement();
} /* init_dynamic_arg() */
/** @ignore yes */
mixed stats() {
mixed arr;
int sz, i;
arr = allocate( sz = sizeof(_riding), (: allocate( 2 ) :) );
for( ; i < sz; i++ ) {
arr[i][0] = "rider #"+i;
arr[i][1] = sprintf("%s (%s)", _riding[i]->query_cap_name(),
file_name(_riding[i]) );
}
return ::stats() + arr + ({
({"owner", _owner }),
({"allowed", _allowed }),
({"transport name", _transport_name }),
({"transport id", _transport_id }),
({"transport type", _transport_type }),
({"teathered to", _teathered_to }),
({"teathered to short", _teathered_to_short }),
({"me repeater", _me_repeater }),
({"env repeater", _env_repeater }),
({"current speed", _cur_speed }),
({"current exhaustion", _cur_exhaustion }),
({"following path", query_path_follow() }),
({"last location", _last_location }),
({"route", _route }),
({"current direction", _cur_direction }),
({"transport speed", _transport_speed }),
({"max speed", _max_speed }),
({"max exhaustion", _max_exhaustion }),
({"last move", _last_move }),
({"last panic", _last_panic }),
});
} /* stats() */