/* -*- LPC -*- */
/*
* $Locker: $
* $Id: auto_load.c,v 1.22 2003/07/15 13:09:06 taffyd Exp $
*/
/**
* Handles the automatic loading and saving of objects into the
* players inventories on log in/out.
* @author Pinkfish
* @change Ember 1992 sometime
* Make it use greco and all the other exciting missing item receipt stuff
*/
#include <move_failures.h>
#include <virtual.h>
#include <player.h>
#define AUTO_STR_LENGTH 3
#define AUTO_LOAD_TYPE 0
#define AUTO_LOAD_NAME 1
#define AUTO_LOAD_DATA 2
/* Stuff for the data elements themselves */
#define AUTO_LOAD_STATIC_ARG 0
#define AUTO_LOAD_DYNAMIC_ARG 1
private mixed auto_load;
private nosave int _no_calls;
private nosave function _finished;
private void auto_clone_alt( object thing, mixed *load_info,
object dest, object tell_pl, function move_f, string name);
private object *int_auto_load(int type, string name, mixed *load_info,
object dest, object tell_pl, int now,
function move_f);
private void move_to_destination(object thing, object dest, object tell_pl,
function move_f);
/** @ignore yes */
string *fragile_auto_str_ob(object ob) {
/* this function was added by Ember 30-Oct-93 */
/* it is a copy of auto_str_ob below, with all the catches taken out */
/* it is intended for the newer style of saving/loading with receipts */
mixed static_arg;
mixed dynamic_arg;
string fname;
static_arg = ob->query_static_auto_load();
dynamic_arg = ob->query_dynamic_auto_load();
if (!static_arg && !dynamic_arg) {
return ({ });
}
if ( sscanf( file_name( ob ), "%s#%*d", fname) == 2 ) {
return ({ 1, fname, ({ static_arg, dynamic_arg }) });
} else {
return ({ 0, file_name(ob), ({ static_arg, dynamic_arg }) });
}
} /* fragile_auto_str_ob() */
/**
* This method figures out the auto load string for the given object.
* The returned array contains 3 elements. The first element is a
* flag which tells if the object is a clone or the original object.
* The second element is the name of the file and the third is the array
* containing the saved information. The third array contains three
* elements. The first is the static auto load information, the
* second is the dynamic autoload information and the third is the shadow
* auto load information. The shadow autoload information is reduant these days
* effects should be used instead.
* @param ob the object to get the auto load information for
* @return the array containing the auto load information
* @see help::effects
*/
private string *auto_str_ob(object ob) {
string static_arg;
string dynamic_arg;
string catch_static;
string catch_dynamic;
string fname;
int value;
string ob_name;
mixed* tmp;
catch_static = catch(static_arg = (string)ob->query_static_auto_load());
catch_dynamic = catch(dynamic_arg = (string)ob->query_dynamic_auto_load());
if (!static_arg && !dynamic_arg) {
//
// create receipts?
//
if (catch_static || catch_dynamic) {
catch(value = ob->query_value());
ob_name = "unknown object";
catch(ob_name = ob->short(1));
catch(PLAYER_RECEIPT->set_object(file_name(ob)));
PLAYER_RECEIPT->set_obname(ob_name);
PLAYER_RECEIPT->set_value(value);
catch(PLAYER_RECEIPT->set_static_data(({ static_arg, dynamic_arg })));
tmp = fragile_auto_str_ob(find_object("/obj/misc/al_receipt"));
tmp[0] = 1;
// This is a bit of a hack. It could easily break if the receipt
// inheritance changes.
if (!undefinedp(tmp[2][1]["::"]["cloned by"])) {
tmp[2][1]["::"]["cloned by"] = "greco";
}
} else {
tmp = ({ });
}
return tmp;
}
if ( sscanf( file_name( ob ), "%s#%*d", fname ) == 2 ) {
return ({ 1, fname, ({ static_arg, dynamic_arg }) });
} else {
return ({ 0, file_name( ob ), ({ static_arg, dynamic_arg }) });
}
} /* auto_str_ob() */
/**
* Creates the complete auto load array from the array of objects
* passed into this object. It returns an array of elements
* as specified in auto_str_ob. The arrays from auto_str_ob() are
* added together, so every 3 elemnts in the array is a new
* autoload object.
* @param obs the objects to create an autoload string for
* @param into_array place the results directly iunto the auto_load array
* @return the auto load array
* @see auto_str_ob()
*/
string *create_auto_load(object *obs, int into_array) {
int i;
string *tmp;
string *al_tmp;
if (into_array) {
auto_load = ({ });
} else {
tmp = ({ });
}
/*
* For some reason inventories are regenerated backwards, if we go from the
* the bottom of this array, so go from the top.
*/
for (i = sizeof(obs) - 1; i >= 0; i--) {
reset_eval_cost();
if (!objectp(obs[i])) {
continue;
}
if (!catch(al_tmp = auto_str_ob(obs[i]))) {
if (into_array) {
auto_load += al_tmp;
} else {
tmp += al_tmp;
}
}
}
return tmp;
} /* create_auto_load() */
/**
* The new method of handling auto loading of objects. This determines
* if the object failed to load and gives the player a recipt if it does
* not.
* <p>
* The finished variable should <b>only</b> be set in outer
* level calls to this. If you set it inside containers when they call
* this it will cause errors. This will only be called from within
* the player object itself.
* @param auto_string the values specifing the objects to auto load
* @param dest the destination to put the objects in
* @param tell who to tell about errors
* @param finished the function to call when the auto loading has completed
* @see create_auto_load()
* @see load_auto_load()
*/
void load_auto_load_alt( mixed *auto_string, object dest,
object tell_pl, function finished) {
int i;
reset_eval_cost();
if ( !auto_string || !sizeof( auto_string ) ) {
//tell_object(this_object(), "%^RED%^Warning! Auto string is empty!%^RESET%^\n");
return;
}
if ( !pointerp( auto_string ) ) {
//tell_object(this_object(), "%^RED%^Warning! Auto string is not an array!%^RESET%^\n");
return;
}
/*
* This is to try to handle the slight over-weight problems that people
* close to capacity may have due to mergers, e.g. money.
*/
if ( ( dest == this_object() ) &&
( tell_pl == this_object() ) &&
!this_object()->query_no_check() ) {
this_object()->set_no_check( 1 );
this_object()->set_max_weight( 100 + this_object()->query_max_weight() );
}
_finished = finished;
reset_eval_cost();
for ( i = 0; i < sizeof( auto_string ); i += AUTO_STR_LENGTH) {
_no_calls++;
call_out( (: int_auto_load :), i / 6,
auto_string[ i + AUTO_LOAD_TYPE ],
auto_string[ i + AUTO_LOAD_NAME ],
auto_string[ i + AUTO_LOAD_DATA ],
dest, tell_pl, 0, (: $1->move($2) :));
}
} /* load_auto_load_alt() */
/**
* This method creates all the objects and sets them up now. This will
* not move any of the objects anywhere... Relying on the calling
* code to handle this.
* @param auto_string the values specifing the objects to auto load
* @param dest the destination to put the objects in
* @param tell who to tell about errors
* @param finished the function to call when the auto loading has completed
* @see create_auto_load()
* @see load_auto_load()
*/
object *load_auto_load_to_array( mixed *auto_string, object tell_pl ) {
object *obs;
int i;
obs = ({ });
if (!tell_pl) {
tell_pl = this_player();
}
if (!tell_pl) {
tell_pl = this_object();
}
for ( i = 0; i < sizeof( auto_string ); i += AUTO_STR_LENGTH) {
_no_calls++;
/* Set a null function so it always appears to succeed. */
obs += int_auto_load(auto_string[ i + AUTO_LOAD_TYPE ],
auto_string[ i + AUTO_LOAD_NAME ],
auto_string[ i + AUTO_LOAD_DATA ],
0, tell_pl, 1, (: MOVE_OK :));
}
return obs - ({ 0 });
} /* load_auto_load_to_array() */
/**
* This method loads all the objects from the array and places them
* into the inventory of the specified object.
* @param auto_string the values specifing the objects to auto load
* @param dest the destination to put the objects in
* @param tell who to tell about errors
* @param move_f the function to call to move the object to it's destination
* @see create_auto_load()
* @see load_auto_load()
*/
void load_auto_load_to_inventory(mixed *auto_string, object dest,
object tell_pl, function move_f) {
int i;
if (!tell_pl) {
tell_pl = this_player();
}
if (!tell_pl) {
tell_pl = this_object();
}
for ( i = 0; i < sizeof( auto_string ); i += AUTO_STR_LENGTH) {
_no_calls++;
int_auto_load(auto_string[ i + AUTO_LOAD_TYPE ],
auto_string[ i + AUTO_LOAD_NAME ],
auto_string[ i + AUTO_LOAD_DATA ],
dest, tell_pl, 0, move_f);
}
} /* load_auto_load_to_inventory() */
private object create_auto_load_object( string name,
mixed *load_info, object dest,
object tell_pl, function move_f) {
object thing;
catch(name = (string)CLONER->other_file( name ));
if ( !catch( thing = (object)CLONER->clone( name ) ) ) {
if ( thing ) {
if ( base_name( thing ) == name ) {
auto_clone_alt(thing, load_info, dest, tell_pl, move_f, name);
} else {
move_to_destination(thing, dest, tell_pl, move_f);
//catch(thing->move( dest ));
}
} else {
tell_object( tell_pl, "%^RED%^Could not clone "+ name +
".%^RESET%^\n" );
thing = clone_object( PLAYER_RECEIPT );
thing->set_object( name );
thing->set_static_save( load_info );
//thing->move( dest );
move_to_destination(thing, dest, tell_pl, move_f);
}
} else {
tell_object( tell_pl, "%^RED%^Error in loading "+ name +
".%^RESET%^\n" );
thing = clone_object( PLAYER_RECEIPT );
thing->set_object( name );
thing->set_static_save( load_info );
//thing->move( dest );
move_to_destination(thing, dest, tell_pl, move_f);
}
return thing;
} /* create_auto_load_object() */
private object *int_auto_load(int type, string name, mixed *load_info,
object dest, object tell_pl, int now,
function move_f) {
object thing;
object new_thing;
mixed *stuff;
int reduce;
_no_calls--;
if ( type ) {
//
// Do this first so we don't leak receipts.
//
thing = create_auto_load_object(name, load_info, dest, tell_pl, move_f);
if ( name == PLAYER_RECEIPT) {
/* Try and reload the receipt */
stuff = thing->query_static_save();
if (stuff) {
new_thing = create_auto_load_object(name, load_info, dest,
tell_pl, move_f);
if (new_thing) {
thing->dest_me();
thing = new_thing;
}
}
}
} else {
if ( !find_object( name ) ) {
if ( !catch( load_object( name ) ) ) {
thing = find_object( name );
if ( thing ) {
auto_clone_alt(thing, load_info, dest, tell_pl, move_f, name);
} else {
tell_object( tell_pl, "%^RED%^Could not load "+ name +
".%^RESET%^\n" );
}
} else {
tell_object( tell_pl, "%^RED%^Error in loading "+ name +
".%^RESET%^\n" );
}
} else {
tell_object( tell_pl, "%^RED%^Object "+ name +
" exists.%^RESET%^\n" );
}
}
/* Check to see if we have finished */
if (_no_calls == 0 && _finished) {
/*
* NB: This has to be run at the end of the setup... Because a
* container may attempt to reload some stuff.
*/
evaluate(_finished, this_object());
_finished = 0;
this_object()->set_no_check( 0 );
/*
* This is to handle stuffing about with weights so players don't
* loose stuff due to things like merging objects.
*/
reduce = (int)this_object()->query_max_weight() -
(int)this_object()->query_loc_weight();
if ( reduce > 1 ) {
if ( reduce > 100 ) {
reduce = 100;
}
this_object()->set_max_weight( (int)this_object()->query_max_weight() -
reduce );
}
this_object()->calc_burden();
}
if (thing) {
return ({ thing });
}
return ({ });
} /* int_auto_load() */
/** @ignore yes */
private string ident( object thing ) {
mixed word;
catch(word = (string)thing->query_short());
if ( stringp( word ) ) {
return "\""+ word +"\"";
}
return file_name( thing );
} /* ident() */
/**
* This moves the cloned object into the correct location and
* loads up the arguments. The arguments array contains three elements
* the first is the object being cloned, the second is the auto load
* string and the third is the destination to move the object too. This is
* used by the alternate system of loading objects.
* @param arg the arg array
* @see load_auto_load_alt()
* @see create_auto_load()
* @see auto_clone()
*/
private void auto_clone_alt( object thing, mixed *load_info,
object dest, object tell_pl, function move_f, string name) {
string ob_path;
/*
* The setting up the object was previously done after this...
* Not sure why? Moved it to here... Oh! I have an idea. The
* object which fail might not end up being moved into the right place
* or somewhere the player can see at all. Changed the movement stuff
* to also try and move into the telling players environment if we
* run out of options.
*
* This is a moot problem now however, since with the inventory
* generation one the contents is generated slowly after a while
* anyway...
*/
reset_eval_cost();
if ( load_info[ AUTO_LOAD_STATIC_ARG ] ) {
catch( thing->init_static_arg( load_info[ AUTO_LOAD_STATIC_ARG ],
tell_pl ) );
}
catch( thing->set_player( tell_pl ) );
if ( load_info[ AUTO_LOAD_DYNAMIC_ARG ] ) {
catch( thing->init_dynamic_arg( load_info[ AUTO_LOAD_DYNAMIC_ARG ],
tell_pl ));
}
//
// We check to see if the virtual object exists or not.
//
ob_path = thing->query_property(VIRTUAL_NAME_PROP);
if (ob_path) {
ob_path = CLONER->other_file( ob_path );
if (file_size(ob_path) < 0) {
// Turn it into a receipt.
thing->dest_me();
thing = clone_object( PLAYER_RECEIPT );
thing->set_object( name );
thing->set_static_save( load_info );
thing->set_virtobname( ob_path );
} else {
thing->add_property(VIRTUAL_NAME_PROP, ob_path);
}
}
/*
* This gets rid of illegal objects that don't have their own file
* and gives the player a replacement of some kind
*/
ob_path = CLONER->illegal_thing(base_name(thing), thing->query_short());
if(ob_path) {
thing->dest_me();
thing = clone_object( ob_path );
}
move_to_destination(thing, dest, tell_pl, move_f);
} /* auto_clone_alt() */
private void move_to_destination(object thing, object dest, object tell_pl,
function move_f) {
object place;
int ret_val;
string catch_str;
/* Move it into its correct location. */
place = dest;
ret_val = MOVE_OK - 1;
while ( place && ret_val != MOVE_OK) {
catch_str = catch( ret_val = evaluate(move_f, thing, place ) );
if(catch_str) {
tell_object(tell_pl, "%^RED%^Error moving object: " + catch_str +
".%^RESET%^\n");
ret_val = MOVE_OK - 1;
}
if (ret_val != MOVE_OK) {
if ( environment( place ) ) {
tell_object( tell_pl, "%^RED%^Cannot move "+ ident( thing ) +
" into "+
ident( place ) +" (move returned " +
catch_str + "); attempting to move it into "+
ident( environment( place ) ) +".%^RESET%^\n" );
place = environment( place );
/*
* I don't think this case is needed. I think it just
* adds confusion to the issue. Who thinks it just adds confusiong
* to the issue, was this me? - pf
*/
} else if ( ( place != environment(tell_pl) ) &&
environment( tell_pl ) ) {
tell_object( tell_pl, "%^RED%^Cannot move "+ ident( thing ) +
" into "+
ident( place ) +" (move returned " +
ret_val + "); attempting to move it into "
"environment of " + tell_pl->query_name() +
".%^RESET%^\n" );
place = environment( tell_pl );
} else if (place != find_object("/room/broken")) {
tell_object( tell_pl, "%^RED%^Cannot move "+ ident( thing ) +
" into "+
ident( place ) +" (move returned " + ret_val +
"); moving it to the room for broken objects "+
"--- please ask a creator for help.%^RESET%^\n" );
load_object("/room/broken");
place = find_object( "/room/broken" );
} else {
// Error! Error!
tell_object( tell_pl, "%^RED%^Cannot move "+ ident( thing ) +
" into "+
" the room for broken objects "+
"--- This is a serious errror! Please tell a "
"creator immediately.%^RESET%^\n");
place = 0;
}
/* Only use the strange move function for the first move. */
move_f = (: $1->move($2) :);
}
}
} /* auto_clone_alt() */
/**
* This method tells us if the player is in the inventory regeneration
* phase still.
* @return 1 if the inventory is being regenerated, 0 otherwise.
*/
int query_auto_loading() {
return _no_calls > 0;
} /* query_auto_load_over() */
/**
* This method retrusn the auto load string which is used to
* regenerate the players inventory.
* @return the array of the auto load string
* @see set_auto_load_string()
*/
protected mixed query_auto_load_string() {
return auto_load;
} /* query_auto_load_string() */
/**
* This method sets the current auto load string to the specified value.
* @see query_auto_load_string()
*/
protected void set_auto_load_string(mixed str) {
auto_load = str;
} /* set_auto_load_string() */