/**
* This is the handler which deals with parcels that are being sent
* from player to player, or, as it may be, from post office to
* post office.
*
* These are the things I want to do here:<br>
* - Each PO should have coordinates.
* <br>
* - One should be able to pick which PO to send the parcel to, and
* the cost of posting a parcel should depend on the weight of the
* parcel and the distance between this PO and the destination PO.
* <br>
* - It should take time for the parcel to arrive at the destination
* based on the distance between the starting PO and destination PO.
* <br>
* - If no-one has collected the parcel within 30 days from the arrival,
* send it back to the sender, if there is one. Then if the sender
* doesn't come to collect it within a month, discard the parcel.
* <br>
* - We are going to wrap the things in a special envelope/packet,
* and use that as a container, which will disappear when unwrapped.
* <br>
* @see /std/shops/parcel_office.c
* @author Sandoz, 2002.
*/
#define AUTO_LOAD_H "/global/player/auto_load"
/** This is the minimum shipping cost. */
#define MIN_SHIPPING_COST 2000
/** This value is used to round all the cost calculations. */
#define ROUND_DENOMINATOR 100
#define DAY (60*60*24)
#define MONTH (30*DAY)
#define FAIL(x) do_fail_mess( npc, x )
#define MS(x,y) (string)MONEY_H->money_value_string(x,y)
#define PARCEL_SAVE_DIR SAVE_DIR "/parcels/"
#define SHIP_DIR PARCEL_SAVE_DIR "shipping/"
/**
* This class stores the data for a single parcel.
* @member from the name of the player the parcel is from
* @member from_office the parcel office the parcel was sent from
* @member autoload the autoload info of the parcel
* @member expiry_time the time the parcel should be either
* discarded or returned to the sender
* @member returned whether or not the parcel is being returned
*/
class parcel_data {
string from;
string from_office;
mixed autoload;
int expiry_time;
int returned;
}
/**
* This class stores the data for a single parcel that is being shipped.
* @member to the person this parcel is for
* @member to_office the post office this parcel was sent to
* @member due the time the parcel should reach its destination
*/
class shipping_data {
string to;
string to_office;
int due;
}
/**
* This class stores the data for a single post office.
* You can make a restricted office by setting the restricted flag,
* this is good for testing purposes, so that players can't send
* parcels to offices you're still in the middle of setting up.
* @member coords the coordinates of the office
* @member data the mapping of parcel expiry times for players
* @member restricted a flag that can be set to make the office restricted
*/
class office_data {
int *coords;
mapping data;
int restricted;
}
nosave string cur_player, cur_office;
nosave class parcel_data *player_data;
private mapping offices, shipping;
private int check_time;
private void do_hourly_check();
/** @ignore */
private void create() {
function f;
if( !unguarded( (: dir_exists, PARCEL_SAVE_DIR :) ) )
unguarded( (: mkdir, PARCEL_SAVE_DIR :) );
if( !unguarded( (: dir_exists, SHIP_DIR :) ) )
unguarded( (: mkdir, SHIP_DIR :) );
if( unguarded( (: file_exists, PARCEL_SAVE_DIR+"main.o" :) ) )
unguarded( (: restore_object, PARCEL_SAVE_DIR+"main.o" :) );
if( !mapp(offices) )
offices = ([ ]);
if( !mapp(shipping) )
shipping = ([ ]);
f = function( function func ) {
call_out( func, 3600, func );
do_hourly_check();
};
call_out( f, 60, f );
} /* create() */
/** @ignore */
private void save_me() {
unguarded( (: save_object, PARCEL_SAVE_DIR+"main.o" :) );
} /* save_me() */
/**
* This method returns the mapping of all post office specific data.
* @return a mapping which contains the office specific data
*/
mapping query_offices() { return offices;}
/**
* This method returns the array of all restricted post offices.
* @return all restricted post offices
*/
string *query_restricted_offices() {
return keys( filter( offices, (: $2->restricted :) ) );
} /* query_restricted_offices() */
/**
* This method returns the array of all unrestricted post offices.
* @return all unrestricted post offices
*/
string *query_unrestricted_offices() {
return keys( filter( offices, (: !$2->restricted :) ) );
} /* query_restricted_offices() */
/**
* This method returns the mapping of all shipment specific data.
* @return a mapping which contains the shipment specific data
*/
mapping query_shipping() { return shipping; }
/** @ignore yes */
private int compare_coords( int *i, int *j ) {
return i[0] == j[0] && i[1] == j[1] && i[2] == j[2];
} /* compare_coords() */
/**
* This method queries whether or not the input string is a valid
* post office.
* @param name the name of the post office to test
*/
int query_post_office( string name ) { return !undefinedp( offices[name] ); }
/**
* This method adds a post office into the handler.
* It will make sense to have a 'main' post office with
* 0, 0, 0 coords, relative to which all the other offices'
* coordinates should be set.
* @param name the name of the post office
* @param coords the coordinates of the post office
* @param restricted whether or not this is a restricted office
* @return 1 upon success, 0 upon failure
* @see remove_office()
* @see change_coords()
*/
int add_office( string name, int *coords, int restricted ) {
if( !adminp( previous_object(-1) ) ) {
printf("Sorry, only admin can add post offices.\n");
return 0;
}
if( !stringp(name) || !sizeof(name) ) {
printf("Invalid office name.\n");
return 0;
}
if( query_post_office(name) ) {
printf("A post office by that name already exists.\n");
return 0;
}
if( !pointerp(coords) || sizeof(coords) != 3 ||
sizeof( filter( coords, (: !intp($1) :) ) ) ) {
printf("Invalid coordinates; must be an integer array of three "
"elements.\n");
return 0;
}
if( sizeof( filter( values(offices),
(: compare_coords( $1->coords, $2 ) :), coords ) ) ) {
printf("An office with those coordinates already exists.\n");
return 0;
}
offices[name] = new( class office_data,
coords : coords,
data : ([ ]),
restricted : restricted );
save_me();
return 1;
} /* add_office() */
/**
* This method changes a post office's coordinates.
* @param name the name of the post office
* @param coords the new coordinates of the post office
* @return 1 upon success, 0 upon failure
* @see remove_office()
*/
int change_coords( string name, int *coords ) {
if( !adminp( previous_object(-1) ) ) {
printf("Sorry, only admin can change post office coordinates.\n");
return 0;
}
if( !stringp(name) || !sizeof(name) ) {
printf("Invalid office name.\n");
return 0;
}
if( !query_post_office(name) ) {
printf("There is no post office by that name.\n");
return 0;
}
if( !pointerp(coords) || sizeof(coords) != 3 ||
sizeof( filter( coords, (: !intp($1) :) ) ) ) {
printf("Invalid coordinates; must be an integer array of three "
"elements.\n");
return 0;
}
if( compare_coords( offices[name]->coords, coords ) ) {
printf("That office already has those coordinates.\n");
return 0;
}
if( sizeof( filter( values(offices),
(: compare_coords( $1->coords, $2 ) :), coords ) ) ) {
printf("An office with those coordinates already exists.\n");
return 0;
}
offices[name]->coords = coords;
save_me();
return 1;
} /* change_coords() */
/**
* This method sets the restricted flag of the post office.
* @param name the name of the office to set the restricted flag for
* @param restrict whether or not we should be a restricted office
* @see add_office()
* @see remove_office()
* @see change_coords()
*/
int restrict_office( string name, int restrict ) {
if( !adminp( previous_object(-1) ) ) {
printf("Sorry, only admin can change the restricted flag of post "
"offices.\n");
return 0;
}
if( !stringp(name) || !sizeof(name) ) {
printf("Invalid office name.\n");
return 0;
}
if( !query_post_office(name) ) {
printf("There is no post office by that name.\n");
return 0;
}
offices[name]->restricted = restrict;
save_me();
return 1;
} /* restrict_office() */
/**
* This method changes a post office's coordinates.
* @param name the name of the post office
* @return 1 upon success, 0 upon failure
* @see add_office()
*/
int remove_office( string name ) {
if( !adminp( previous_object(-1) ) ) {
printf("Sorry, only admin can remove post offices.\n");
return 0;
}
if( !query_post_office( name) ) {
printf("There is no post office by that name.\n");
return 0;
}
map_delete( offices, name );
save_me();
return 1;
} /* remove_office() */
/** @ignore yes */
string normalize_name( string name ) {
int i;
name = lower_case(name);
for( i = 0; i < strlen(name); i++ ) {
if( name[i] == ''' ) {
name = name[0..i-1] + name[i+1..];
i--;
continue;
}
if( name[i] < 'a' || name[i] > 'z')
name[i] = '_';
}
return implode( explode( name, "_") - ({ 0, "" }), "_");
} /* normalize_name() */
/** @ignore yes */
string query_real_office_name( string name ) {
string bit, tmp;
if( query_post_office( name ) )
return name;
tmp = lower_case(name);
foreach( bit in keys( offices ) )
if( tmp == lower_case(bit) )
return bit;
return name;
} /* query_real_office_name() */
private string query_office_save_dir( string office ) {
office = normalize_name( office );
return PARCEL_SAVE_DIR+office+"/";
} /* query_office_save_fir() */
/** @ignore yes */
private void reset_player_data() {
cur_office = 0;
cur_player = 0;
player_data = 0;
} /* reset_player_data() */
/** @ignore yes */
private void save_player_data() {
string dir;
if( !cur_player || !cur_office || !player_data ) {
error("Attempted to save with no cur_player, cur_office or "
"player_data.\n");
return;
}
dir = query_office_save_dir(cur_office);
if( !unguarded( (: dir_exists, dir :) ) )
unguarded( (: mkdir, dir :) );
unguarded( (: write_file, dir+cur_player+".o",
save_variable(player_data), 1 :) );
} /* save_player_data() */
/** @ignore yes */
private void load_player_data( string office, string player ) {
string data;
if( cur_office == office && cur_player == player )
return;
data = query_office_save_dir(office)+player+".o";
if( unguarded( (: file_exists, data :) ) ) {
data = unguarded( (: read_file, data :) );
if( stringp(data) )
player_data = restore_variable(data);
}
if( !pointerp(player_data) )
player_data = ({ });
cur_player = player;
cur_office = office;
} /* load_player_data() */
/**
* This method calculates the distance between two post offices
* based on their coordinates.
* @param from the starting post office
* @param to the destination post office
* @return the distance between the post offices
*/
int query_distance( string from, string to ) {
int *i, *j;
i = offices[from]->coords;
j = offices[to]->coords;
return 10 * sqrt( ( ( i[0] - j[0] ) * ( i[0] - j[0] ) ) +
( ( i[1] - j[1] ) * ( i[1] - j[1] ) ) +
( ( i[2] - j[2] ) * ( i[2] - j[2] ) ) );
} /* query_distance() */
/**
* This method calculates the shipping cost of a parcel from one
* post office to another based on their coordinates and the
* weight of the parcel. This will be rounded up using the
* ROUND_DENOMINATOR define.
* @param ob the object we are trying to ship
* @param from the starting post office
* @param to the destination post office
* @return the shipping cost
*/
int query_shipping_cost( object ob, string from, string to ) {
int i, j, k;
if( k = query_distance( from, to ) ) {
if( k < MIN_SHIPPING_COST )
k = MIN_SHIPPING_COST;
if( j = ( k % ROUND_DENOMINATOR ) )
k += ROUND_DENOMINATOR - j;
}
i = ( ob->query_complete_weight() + 1 ) * 20;
if( i < MIN_SHIPPING_COST )
i = MIN_SHIPPING_COST;
if( j = ( i % ROUND_DENOMINATOR ) )
i += ROUND_DENOMINATOR - j;
return i + k;
} /* query_shipping_cost() */
/**
* This method calculates the shipping time from one
* post office to another based on their coordinates.
* @param from the starting post office
* @param to the destination post office
* @return the shipping time in seconds, or 0 if there
* are no such post offices
*/
int query_shipping_time( string from, string to ) {
return 60 * query_distance( from, to );
} /* query_shipping_time() */
/** @ignore yes */
private int do_fail_mess( object servant, string mess ) {
if( servant ) {
servant->do_command("'"+mess );
return notify_fail("");
}
add_failed_mess( mess+"\n");
return 0;
} /* do_fail_mess() */
/**
* This method deals with the actual shipping command.
* If the command is successful, event_parcel_sent()
* will be called on the post office room, that should
* give a message to the player about success.
* The arguments passed to the function are the name of the
* person who we sent the parcel to, the name of the post
* office we sent the parcel to, and the time they should
* receive the parcel.
* Note : the objects we send MUST have query_is_postage_parcel()
* return 1 on them, so that we can know they are properly
* packed packages.
* @param obs the things we are trying to send
* @param to the person the parcel is to
* @param where the name of the post office to send it to
* @param from the post office we are sending the parcel from
* @param loc the location for currency calculations
* @param npc the NPC serving the player, if there is any
* @return 1 upon success, 0 upon failure
*/
int do_send( object *obs, string to, string where, string from,
string loc, object npc ) {
class parcel_data data;
int ship, id, cost;
string mess;
object ob;
if( !PO->query_parcel_office() ) {
add_failed_mess("You can only send parcels from post offices.\n");
return 0;
}
if( sizeof(obs) > 1 )
return FAIL("You can only send one parcel at a time.");
if( !query_post_office( where ) ||
( !creatorp(TP) && offices[where]->restricted ) )
return FAIL( where+" doesn't appear to be a valid post office. "
"Please 'list offices' to get a list of post offices you can "
"send parcels to.");
ob = obs[0];
to = lower_case(to);
if( to == TP->query_name() )
return FAIL("You can't send "+ob->the_short()+" to yourself, silly!");
if( !PLAYER_H->test_user( to ) )
return FAIL("There is no player by that name!");
if( MULTIPLAYER_H->member_denied_parcel( TP->query_name() ) )
return FAIL("You are not allowed to send parcels to anyone.");
if( MULTIPLAYER_H->member_denied_parcel( to ) )
return FAIL( CAP(to)+" is not allowed to receive parcels from "
"anyone.");
if( !ob->query_is_postage_parcel() )
return FAIL( ob->the_short()+" "+( query_group(ob) ?
"do not appear to be proper parcels." :
"does not appear to be a proper parcel.")+" You can only send "
"things in packets, sealed envelopes or similar containers.");
cost = query_shipping_cost( ob, from, where );
if( TP->query_value_in(loc) < cost )
return FAIL("You cannot afford to send "+ob->the_short()+" to "+
where+" for "+CAP(to)+". It would cost "+MS( cost, loc )+".");
ship = query_shipping_time( from, where ) + time();
mess = sprintf("%'* '=*s\n*%' '=*s\n", 78, "", 76, "*");
mess += sprintf("%*-s*\n", 76, sprintf("*%+=*s%-=*s", 10, "TO : ", 76,
CAP(to)+" "+where ) );
mess += sprintf("%*-s*\n", 76, sprintf("*%+=*s%-=*s", 10, "FROM : ", 76,
TP->query_cap_name()+" "+from ) );
mess += sprintf("*%' '=*s\n%'* '=*s\n", 76, "*", 78, "");
obs->add_read_mess(mess);
data = new( class parcel_data,
from : TP->query_name(),
from_office : from,
autoload : AUTO_LOAD_H->create_auto_load( ({ ob }), 0 ),
expiry_time : ship + MONTH,
returned : 0 );
if( ob->move("/room/rubbish") ) {
ob->remove_read_mess(mess);
return FAIL("You cannot let go of "+ob->the_short()+" for some "
"reason.");
}
while( !undefinedp( shipping[++id] ) );
if( !unguarded( (: write_file, SHIP_DIR+id+".o",
save_variable(data), 1 :) ) ) {
ob->remove_read_mess(mess);
ob->move(TP);
return FAIL("Something went wrong with sending "+
ob->the_short()+" to "+CAP(to)+", sorry.");
}
if( npc )
npc->do_command("'That will be "+MS( cost, loc )+", please.");
else
tell_object( TP, "That will cost "+MS( cost, loc )+".\n");
TP->pay_money( MONEY_H->create_money_array( cost, loc ), loc );
mess = ( npc ? npc->the_short() : "the parcel clerk");
tell_object( TP, "You hand the money to "+mess+".\n");
tell_room( ENV(TP), TP->the_short()+" $V$0=hands,hand$V$ some money to "+
mess+".\n", TP );
shipping[id] = new( class shipping_data,
to : to,
to_office : where,
due : ship );
save_me();
PO->event_parcel_sent( to, where, ship );
return 1;
} /* do_send() */
/**
* This method prints the cost of sending a specific parcel to
* somewhere for the player.
* @param obs the objects to get the shipping costs for
* @param from the office we are sending from
* @param to the destination we are sending to
* @param loc the location for currency calculations
* @param npc the servant NPC, if there is one
* @return 1 upon success, 0 upon failure
*/
int do_cost( object *obs, string from, string to, string loc, object npc ) {
object ob;
if( !PO->query_parcel_office() ) {
add_failed_mess("You can only send parcels from post offices.\n");
return 0;
}
if( sizeof(obs) > 1 )
return FAIL("You can only check the shipping costs of one parcel "
"at a time.");
if( !query_post_office( to ) ||
( !creatorp(TP) && offices[to]->restricted ) )
return FAIL( to+" doesn't appear to be a valid post office. "
"Please 'list offices' to get a list of post offices you can "
"send parcels to.");
ob = obs[0];
if( !ob->query_is_postage_parcel() )
return FAIL( ob->the_short()+" "+( query_group(ob) ?
"do not appear to be proper parcels." :
"does not appear to be a proper parcel.")+" You can only send "
"things in packets, sealed envelopes or similar containers.");
to = "It would cost "+MS( query_shipping_cost( ob, from, to ), loc )+" "+
"to send "+ob->the_short()+" to "+to+".";
if( npc )
npc->do_command("'"+to );
else
tell_object( TP, to+"\n");
return 1;
} /* do_cost() */
/** @ignore yes */
private int sort_classes( int one, int two ) {
if( one > two )
return 1;
if( one < two )
return -1;
return 0;
} /* sort_classes() */
/** @ignore yes */
private void log_and_save_current_player() {
string file;
class parcel_data parcel;
map_delete( offices[cur_office]->data, cur_player );
file = query_office_save_dir(cur_office)+cur_player+".o";
if( !sizeof( player_data ) ) {
log_file("PARCELS", "%s - removed - %s - no data left.\n",
ctime(time()), file );
unguarded( (: rm, file :) );
reset_player_data();
} else {
log_file("PARCELS", "%s - saved - %s - %i parcels.\n",
ctime(time()), file, sizeof(player_data) );
foreach( parcel in player_data )
if( !offices[cur_office]->data[cur_player] ||
offices[cur_office]->data[cur_player] > parcel->expiry_time )
offices[cur_office]->data[cur_player] = parcel->expiry_time;
save_player_data();
}
} /* log_and_save_current_player() */
/**
* This method collects a parcel for a player, it will collect the
* parcel with the earliest expiry date.
* This is done one parcel at a time so that players wouldn't
* fumble stuff.
* If the command is successful, event_parcel_collected()
* will be called on the post office room, that should
* give a message to the player about success.
* The arguments passed to the function are an array of
* objects that should be moved to the player (or a shelf/their
* env if they're too burdened) and the number of parcels that
* are left for the player.
* @param office the name of the parcel office we are at
* @param npc the servant NPC, if we have one
* @return 1 if there are parcels, and 0 if there are not
*/
int do_collect( string office, object npc ) {
object *obs;
string name;
class parcel_data parcel;
if( !PO->query_parcel_office() ) {
add_failed_mess("You can only collect parcels at post offices.\n");
return 0;
}
name = (string)TP->query_name();
if( MULTIPLAYER_H->member_denied_parcel( name ) )
return FAIL("You are not allowed to collect parcels.\n");
if( undefinedp( offices[office]->data[name] ) )
return FAIL("There are no parcels waiting for you here.");
load_player_data( office, name );
if( !sizeof(player_data) )
return FAIL("Something has gone wrong with storing your parcel data, "
"please contact a creator immediately.");
player_data = sort_array( player_data,
(: sort_classes( $1->expiry_time, $2->expiry_time ) :) );
parcel = player_data[0];
player_data = player_data[1..];
obs = AUTO_LOAD_H->load_auto_load_to_array( parcel->autoload, TP );
PO->event_parcel_collected( obs, sizeof(player_data) );
if( npc && parcel->expiry_time < time() )
npc->do_command("'You are lucky, "+TP->query_cap_name()+", for we "
"were just about to throw away that parcel.");
log_file("PARCELS", "%s - %s collected a parcel from %s.\n",
ctime(time()), CAP(name), office );
log_and_save_current_player();
save_me();
return 1;
} /* do_collect() */
/**
* This method prints all the post offices they can send parcels
* to for a player.
* @param npc the servant NPC, if there is one
* @return always returns 1
*/
int do_list( object npc ) {
string *str;
if( !creatorp(TP) )
str = query_unrestricted_offices();
else
str = keys(offices);
str = sort_array( str, 1 );
if( npc ) {
npc->do_command("'The post offices to which you can send parcels "
"from here are the following - "+query_multiple_short( str )+
".");
} else {
printf("The post offices to which you can send parcels from here "
"are:\n%-*#s\n", (int)TP->query_cols(), implode( str, "\n") );
}
return 1;
} /* do_list() */
/** @ignore yes */
private void do_office_check( string office ) {
int expiry_time;
string player;
class parcel_data data;
foreach( player, expiry_time in offices[office]->data ) {
if( expiry_time < time() ) {
load_player_data( office, player );
foreach( data in player_data ) {
if( data->expiry_time < time() ) {
if( data->returned ) {
MAIL_H->do_mail_message( player, "the parcel clerk",
"Parcel Discard Notification", "", "Dear "+
CAP(player)+",\n\nWe are sorry to inform you "
"that the parcel at "+office+" returned to you "
"from "+data->from_office+" has been discarded."
"\n\nThe Parcel Clerk,\n"+office, 0, 0 );
log_file("PARCELS", "%s - discarded returned parcel "
"in %s - %s.\n", ctime(time()), office,
CAP(player) );
player_data -= ({ data });
continue;
}
if( PLAYER_H->test_user(data->from) ) {
int ship, id;
class parcel_data parcel;
MAIL_H->do_mail_message( player, "the parcel clerk",
"Parcel Return Notification", "", "Dear "+
CAP(player)+",\n\nWe are sorry to inform you "
"that the parcel at "+office+" sent to you from "+
data->from_office+" by "+CAP(data->from)+" has "
"been returned to the sender.\n\n"
"The Parcel Clerk,\n"+office, 0, 0 );
ship = query_shipping_time( office,
data->from_office ) + time();
parcel = new( class parcel_data,
from : player,
from_office : office,
autoload : data->autoload,
expiry_time : ship + MONTH,
returned : 1 );
while( !undefinedp( shipping[++id] ) );
unguarded( (: write_file, SHIP_DIR+id+".o",
save_variable(parcel), 1 :) );
shipping[id] = new( class shipping_data,
to : data->from,
to_office : data->from_office,
due : ship );
log_file("PARCELS", "%s - returned parcel from %s to "
"%s (%s).\n", ctime(time()), office,
data->from_office, CAP(data->from) );
player_data -= ({ data });
continue;
}
MAIL_H->do_mail_message( player, "the parcel clerk",
"Parcel Discard Notification", "", "Dear "+
CAP(player)+",\n\nWe are sorry to inform you "
"that the parcel at "+office+" sent to you from "+
data->from_office+" by "+CAP(data->from)+" has "
"been discarded.\n\n"
"The Parcel Clerk,\n"+office, 0, 0 );
log_file("PARCELS", "%s - discarded parcel from %s (%s) "
"to %s (%s) - no user to return to.\n", ctime(time()),
data->from_office, CAP(data->from), office,
CAP(player) );
player_data -= ({ data });
continue;
}
}
log_and_save_current_player();
}
}
} /* do_office_check() */
/** @ignore yes */
private void do_hourly_check() {
int id;
class shipping_data data;
class parcel_data parcel;
foreach( id, data in shipping ) {
if( !PLAYER_H->test_user( data->to ) ) {
unguarded( (: rm, SHIP_DIR+id+".o" :) );
map_delete( shipping, id );
continue;
}
if( data->due < time() ) {
string player, office;
player = data->to;
office = data->to_office;
parcel = restore_variable( unguarded( (: read_file,
SHIP_DIR+id+".o" :) ) );
load_player_data( office, player );
player_data += ({ parcel });
if( !offices[office]->data[player] ||
offices[office]->data[player] > parcel->expiry_time )
offices[office]->data[player] = parcel->expiry_time;
save_player_data();
if( !parcel->returned ) {
MAIL_H->do_mail_message( player, "the parcel clerk",
"Parcel Deposit Notification", "", "Dear "+
CAP(player)+",\n\nPlease come to the "+office+" parcel "
"counter to collect the parcel which has been sent to "
"you from "+parcel->from_office+" by "+
CAP(parcel->from)+". If you have not collected the "
"parcel by "+ctime(parcel->expiry_time)+", it will be "+
( PLAYER_H->test_user(parcel->from) ?
"returned to the sender" : "discarded")+".\n\n"
"The Parcel Clerk,\n"+office, 0, 0 );
log_file("PARCELS", "%s - parcel from %s (%s) arrived at "
"%s (%s).\n", ctime(time()), parcel->from_office,
CAP(parcel->from), office, CAP(player) );
} else {
MAIL_H->do_mail_message( player, "the parcel clerk",
"Parcel Deposit Notification", "", "Dear "+
CAP(player)+",\n\nPlease come to the "+office+" parcel "
"counter to collect the parcel which has been returned "
"to you from "+parcel->from_office+". If you have not "
"collected the parcel by "+ctime(parcel->expiry_time)+", "
"it will be discarded.\n\n"
"The Parcel Clerk,\n"+office, 0, 0 );
log_file("PARCELS", "%s - parcel to %s (%s) arrived back to "
"%s (%s) - RETURNED.\n", ctime(time()),
parcel->from_office, CAP(parcel->from), office,
CAP(player) );
}
unguarded( (: rm, SHIP_DIR+id+".o" :) );
map_delete( shipping, id );
}
}
if( check_time < time() ) {
check_time = time() + DAY;
map( keys(offices), (: do_office_check($1) :) );
}
save_me();
} /* do_hourly_check() */
/**
* This method should be called by the refresh handler each
* time a player refreshes, or is deleted. This will delete
* all parcels meant for the player.
* @param player the player being refreshed or deleted
*/
void player_refreshed( string player ) {
class shipping_data data;
string office;
int id, flag;
if( PO != find_object(REFRESH_H) )
return;
foreach( id, data in shipping ) {
if( data->to == player ) {
flag = 1;
unguarded( (: rm, SHIP_DIR+id+".o" :) );
map_delete( shipping, id );
}
}
foreach( office in keys(offices) ) {
if( !undefinedp( offices[office]->data[player] ) ) {
flag = 1;
unguarded( (: rm, query_office_save_dir(office)+player+".o" :) );
map_delete( offices[office]->data, player );
}
}
if( cur_player == player )
reset_player_data();
save_me();
if( flag )
log_file("PARCELS", "%s - %s refreshed.\n", ctime(time()),
CAP(player) );
} /* player_refreshed() */
/** @ignore yes */
int query_check_time() { return check_time; }
/** @ignore yes */
void reset_check_time() {
if( adminp( previous_object(-1) ) )
check_time = 0;
} /* reset_check_time() */
/** @ignore yes */
void dest_me() { destruct(TO); }
/** @ignore yes */
mixed stats() {
return ({
({"post offices", sizeof(offices) }),
({"restricted", sizeof( query_restricted_offices() ) }),
({"shipping", sizeof(shipping) }),
({"next check", ctime(check_time)+" ("+check_time+")" }),
});
} /* stats() */