/**
* A saving room inheritable. This handles rooms which save their inventory,
* or more specifically part of their inventory.
*
* Which objects should be saved or not saved can be controlled by overriding
* test_save.
*
* Containers in this room that need to cause it to save its inventory should
* generate a save event.
*
* @see test_save
* @see event_save
*
* @author ceres
*/
#include <move_failures.h>
#include <player.h>
inherit "/global/auto_load";
//#define DEBUG_SAVE
// This is the minimum time between saves.
#define SAVE_TIME 10
#if 0
#undef AUTO_LOAD_OB
#define AUTO_LOAD_OB "/global/auto_load_debug"
#endif
varargs void set_save_file( string file, object thing );
varargs void do_load(object thing);
mapping query_dynamic_auto_load();
void init_dynamic_arg(mapping bing);
private mapping details;
private nosave string _save_file, _ram_file;
private nosave object _effects;
private nosave int _inventory_loaded;
private nosave int _last_saved;
private nosave int _door_opened, _door_unlocked;
#ifdef DEBUG_SAVE
void debug_log(string fmt, mixed args ...) {
debug_printf(fmt + "\n", args ...);
#ifdef DISABLED
if(base_name(this_object()) == "/d/cwc/Bes_Pelargic/Shoo_Li/roads/pang_ho_street/housing/03flat") {
log_file("HOUSING_DEBUG", "%s (%s): " + fmt + "\n",
ctime(time())[4..18], base_name(this_object()), args ...);
}
#endif
}
#endif
/** @ignore yes */
void create() {
_save_file = "";
details = ([ ]);
_last_saved = time();
} /* create() */
/**
* Set the filename that this object should use to save its inventory to.
* @param file The file.
*/
void set_save_file( string file) {
#ifdef USE_RAMDISK
string *bits, fname;
int i;
#endif
_save_file = file;
#ifdef USE_RAMDISK
if(strsrch(_save_file, "/save/player_housing") != -1) {
_ram_file = replace_string(_save_file, "/save/player_housing",
"/save/ramdisk/player_housing");
fname = "";
bits = explode(_ram_file, "/");
for(i=0; i<sizeof(bits)-1; i++) {
fname += "/" + bits[i];
if(file_size(fname) == -1) {
debug_printf("Creating %s", fname);
mkdir(fname);
}
}
}
#endif
do_load();
} /* set_save_file() */
/**
* This method determines if a given object should be saved or not. When
* inheriting this room you should define your own test_save function and use
* it to decide which parts of the inventory are saved and which are not.
*
* @param ob The object to be tested.
* @return 1 for yes 0 for no.
*/
int test_save(object ob) { return 1; }
/**
* This method returns the current save file for the object.
* @return the current save file
*/
string query_save_file() {
return _save_file;
} /* query_save_file() */
/**
* Objects that need to make this room save such as containers in the room
* should generate a save event to make the room save its inventory.
* eg. event(environment(this_object()), "save");
*/
void event_save(object thing) {
// Certain conditions must be met in order to generate a save callout.
if(!thing || !_inventory_loaded || !_save_file || _save_file == "") {
if(find_call_out("do_save") != -1)
remove_call_out("do_save");
return;
}
// If thing is empty or this object then save. Alternatively, if
// it's something this room should save then do a save.
if(thing == this_object() || base_name(thing) == "/std/room/basic/door" ||
test_save(thing)) {
// Figure out when to schedule the callout for if necessary.
if (find_call_out("do_save") == -1)
call_out("do_save", SAVE_TIME);
}
}
/** @ignore yes */
/* The following functions attempt to prevent saving when someone opens
* and then closes a door.
*/
void door_action() {
if(_door_opened || _door_unlocked) {
event_save(this_object());
#ifdef DEBUG_SAVE
debug_log("saving opened %d unlocked %d", _door_opened,
_door_unlocked);
} else {
debug_log("not saving %s door has returned "
"to original state.\n", base_name(this_object()));
#endif
}
_door_opened = 0;
_door_unlocked = 0;
}
/** @ignore yes */
void event_open(object door, object opener) {
_door_opened++;
if(find_call_out("door_action") != -1)
remove_call_out("door_action");
call_out("door_action", 5);
}
/** @ignore yes */
void event_close(object door, object closer) {
_door_opened--;
if(find_call_out("door_action") != -1)
remove_call_out("door_action");
call_out("door_action", 5);
}
/** @ignore yes */
void event_unlock(object door, object unlocker) {
debug_printf("Event unlock called %O", door);
_door_unlocked++;
if(find_call_out("door_action") != -1)
remove_call_out("door_action");
call_out("door_action", 5);
}
/** @ignore yes */
void event_lock(object door, object locker) {
debug_printf("Event lock called %O", door);
_door_unlocked--;
if(find_call_out("door_action") != -1)
remove_call_out("door_action");
call_out("door_action", 5);
}
/** @ignore yes */
void do_save() {
int saved;
#ifdef DEBUG_SAVE
object ob;
#endif
remove_call_out("do_save");
// No save file, no save.
if(!_save_file || _save_file == "") {
#ifdef DEBUG_SAVE
log_file("ROOM_SAVE", "%s no save file for %s\n", ctime(time())[4..18],
base_name(this_object()));
#endif
return;
}
details = ([ ]);
details = query_dynamic_auto_load();
#ifdef USE_RAMDISK
if(_ram_file)
saved = unguarded((: save_object, _ram_file, 3 :));
else
#endif
saved = unguarded((: save_object, _save_file, 3 :));
#ifdef DEBUG_SAVE
if (saved) {
log_file("HOUSING_DEBUG",
"%s %s saved.\n", ctime(time())[4..18],
base_name(this_object()));
} else {
log_file("HOUSING_DEBUG",
"%s %s failed to save.\n", ctime(time())[4..18],
base_name(this_object()));
}
#endif
#ifdef DEBUG_INVENTORY
foreach(ob in all_inventory(this_object())) {
if(test_save(ob))
log_file(base_name(this_object()) + ".log",
"%s %s contained %d items.\n", ctime(time())[4..18],
ob->query_short(), sizeof(deep_inventory(ob)));
}
#endif
_last_saved = time();
}
/**
* @ignore yes
* This causes the inventory to be loaded
*/
void do_load( object thing ) {
mapping tmp;
string fname;
if(_save_file) {
if(_ram_file &&
(file_size(_ram_file + ".o.gz") > 0 || file_size(_ram_file + ".o") > 0))
fname = _ram_file;
else
fname = _save_file;
if(file_size(fname + ".o.gz") > 0 || file_size(fname + ".o") > 0) {
tmp = this_object()->query_properties();
unguarded((: restore_object, fname :));
this_object()->set_properties(tmp);
if ( sizeof( details ) ) {
init_dynamic_arg( details );
} else {
// If there is no save file then set out inventory as loaded.
_inventory_loaded = 1;
}
} else {
// If there is no save file then set out inventory as loaded.
_inventory_loaded = 1;
}
}
// prevent us doing a save.
_last_saved = time();
remove_call_out("do_save");
remove_call_out("door_action");
_door_opened = 0;
_door_unlocked = 0;
}
/**
* @ignore yes
* Makes sure furniture is removed from the save file
* when its removed from this room.
*/
int test_remove(object thing, int flag, mixed dest) {
if(test_save(thing))
event(this_object(), "save", thing);
return 1;
}
/**
* @ignore yes
* Makes sure furniture is saved when its put in this room.
*/
int test_add( object ob, int flag) {
if(!query_auto_loading() && test_save(ob))
event(this_object(), "save", ob);
return 1;
}
/** @ignore yes
* This container cannot be added into other containers.
*/
int query_prevent_insert() { return 1; }
/** @ignore yes */
mapping query_dynamic_auto_load() {
mapping map;
string *obs;
map = ([ ]);
#ifdef DISABLED
if ( _effects ) {
if ( sizeof( (mixed *)_effects->query_effs() ) ) {
_effects->effect_freeze();
_effects->effects_saving();
map += ([
"effects" : ({
(mixed *)_effects->query_effs(),
(int *)_effects->query_eeq()
})
]);
_effects->effect_unfreeze();
}
}
#endif
obs = filter(all_inventory(this_object()), "test_save");
// Try the autoload object.
catch(obs = AUTO_LOAD_OB->create_auto_load( obs, 0 ) );
map["inv"] = obs;
return map;
} /* query_dynamic_auto_load() */
/**
* We make sure that receipts are set to 'no get' when moved into the room.
*/
private int move_to_dest(object ob) {
if (base_name(ob) == PLAYER_RECEIPT) {
ob->reset_get();
}
return ob->move(this_object());
}
/** @ignore yes */
void init_dynamic_arg( mapping bing ) {
#ifdef DEBUG_INVENTORY
object ob;
log_file(base_name(this_object()) + ".log",
"%s Loading.\n", ctime(time())[4..18]);
#endif
if ( !mapp( bing ) ) {
#ifdef DEBUG_INVENTORY
log_file(base_name(this_object()) + ".log",
"%s no mapping to load.\n", ctime(time())[4..18]);
#endif
return;
}
if ( bing[ "effects" ] ) {
_effects->set_effs( bing[ "effects" ][ 0 ] );
_effects->set_eeq( bing[ "effects" ][ 1 ] );
if ( environment() && shadow( this_object(), 0 ) )
move_object( environment() );
_effects->init_after_save();
}
#ifdef DEBUG_INVENTORY
log_file(base_name(this_object()) + ".log",
"%s done effects.\n", ctime(time())[4..18]);
#endif
/*
* Potential order of inventory generation problem here... Where the
* upper parts of the container don't initialise until after we
* return...
*/
if ( bing[ "inv" ] && !_inventory_loaded) {
#ifdef DEBUG_INVENTORY
log_file(base_name(this_object()) + ".log",
"%s starting inventory.\n", ctime(time())[4..18]);
#endif
AUTO_LOAD_OB->load_auto_load_to_inventory( bing["inv"], this_object(),
this_player(),
(: move_to_dest($1) :) );
_inventory_loaded = 1;
} else {
#ifdef DEBUG_INVENTORY
log_file(base_name(this_object()) + ".log",
"%s inventory_loaded already set.\n", ctime(time())[4..18]);
#endif
}
#ifdef DEBUG_INVENTORY
log_file(base_name(this_object()) + ".log",
"%s done inventory.\n", ctime(time())[4..18]);
foreach(ob in all_inventory(this_object())) {
if(test_save(ob))
log_file(base_name(this_object()) + ".log",
"%s %s contains %d items.\n", ctime(time())[4..18],
ob->query_short(), sizeof(deep_inventory(ob)));
}
if(!sizeof(all_inventory(this_object())))
log_file(base_name(this_object()) + ".log",
"%s room has no inventory.\n", ctime(time())[4..18]);
#endif
} /* init_dynamic_arg() */
/** @ignore yes */
void check_euid() {
if ( previous_object() ) {
seteuid( geteuid( previous_object() ) );
}
} /* check_euid() */
/** @ignore yes
* Cleanup could screw us up so we stay loaded.
*/
int query_keep_room_loaded() { return 1; }
/** @ignore yes
* Do any outstanding saves.
*/
void dest_me() {
if(find_call_out("do_save") != -1)
do_save();
}
/** @ignore yes */
int query_inventory_loaded() { return _inventory_loaded; }