/** * 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> #define DEBUG // This is the minimum time between saves. #define SAVE_TIME 300 #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; private nosave int _inventory_loaded; private nosave int _last_saved = time(); private nosave int _save_call; private nosave int _door_opened, _door_unlocked; /** @ignore yes */ void create() { _save_file = ""; details = ([ ]); } /* create() */ /** * Set the filename that this object should use to save its inventory to. * @param file The file. */ void set_save_file( string file) { _save_file = file; 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(TO), "save"); */ void event_save(object thing) { #ifdef DEBUG debug_printf("Save event generated by %O.", thing); #endif // Certain conditions must be met in order to generate a save callout. if(!thing || !_inventory_loaded || !_save_file || _save_file == "") return; #ifdef DEBUG debug_printf("call_out %d", _save_call); #endif // If thing is empty or this object then save. Alternatively, if // it's something this room should save then do a save. if(thing == TO || base_name(thing) == "/std/room/basic/door" || test_save(thing)) { // Figure out when to schedule the callout for if necessary. if(!_save_call) { if(_last_saved > time() - SAVE_TIME) _save_call = call_out("do_save", SAVE_TIME - (time() - _last_saved)); else _save_call = call_out("do_save", SAVE_TIME); #ifdef DEBUG debug_printf("Adding call_out"); #endif } } } /** @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(TO); _door_opened = 0; _door_unlocked = 0; } /** @ignore yes */ void event_open(object thing) { _door_opened++; if(find_call_out("door_action") != -1) remove_call_out("door_action"); call_out("door_action", 15); } /** @ignore yes */ void event_close(object thing) { _door_opened--; if(find_call_out("door_action") != -1) remove_call_out("door_action"); call_out("door_action", 15); } /** @ignore yes */ void event_unlock(object thing) { _door_unlocked++; if(find_call_out("door_action") != -1) remove_call_out("door_action"); call_out("door_action", 15); } /** @ignore yes */ void event_lock(object thing) { _door_unlocked--; if(find_call_out("door_action") != -1) remove_call_out("door_action"); call_out("door_action", 15); } /** @ignore yes */ void do_save() { object ob; // No save file, no save. if(!_save_file || _save_file == "") { #ifdef DEBUG log_file("ROOM_SAVE", "%s no save file for %s\n", ctime(time())[4..18], base_name(TO)); #endif return; } details = ([ ]); details = query_dynamic_auto_load(); #ifdef DEBUG if(unguarded((: save_object, _save_file :))) log_file(base_name(TO) + ".log", "%s saved.\n", ctime(time())[4..18]); else log_file(base_name(TO) + ".log", "%s failed to save.\n", ctime(time())[4..18]); foreach(ob in INV(TO)) { if(test_save(ob)) log_file(base_name(TO) + ".log", "%s %s contained %d items.\n", ctime(time())[4..18], ob->query_short(), sizeof(deep_inventory(ob))); } #endif _last_saved = time(); if(_save_call) remove_call_out(_save_call); _save_call = 0; } /** @ignore yes * This causes the inventory to be loaded */ private void do_load( object thing ) { if ( _save_file ) { if ( file_size( _save_file + ".o" ) > 0 ) { unguarded((: restore_object, _save_file :)); if ( sizeof( details ) ) init_dynamic_arg( details ); } } // prevent us doing a save. _last_saved = time(); if(_save_call) remove_call_out("do_save"); _save_call = 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(TO, "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(test_save(ob)) event(TO, "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 = ([ ]); if( sizeof( TO->query_effs() ) ) { TO->effect_freeze(); TO->effects_saving(); map["effects"] = ({ TO->query_effs(), TO->query_eeq() }); TO->effect_unfreeze(); } obs = filter( INV(TO), "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() */ /** @ignore yes */ void init_dynamic_arg( mapping bing ) { object ob; #ifdef DEBUG log_file(base_name(TO) + ".log", "%s Loading.\n", ctime(time())[4..18]); #endif if ( !mappingp( bing ) ) { #ifdef DEBUG log_file(base_name(TO) + ".log", "%s no mapping to load.\n", ctime(time())[4..18]); #endif return; } if( bing[ "effects" ] ) { TO->set_effs( bing[ "effects" ][ 0 ] ); TO->set_eeq( bing[ "effects" ][ 1 ] ); TO->init_after_save(); } #ifdef DEBUG log_file(base_name(TO) + ".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 log_file(base_name(TO) + ".log", "%s starting inventory.\n", ctime(time())[4..18]); #endif AUTO_LOAD_OB->load_auto_load_to_inventory( bing["inv"], TO, TP, (: $1->move(TO) :) ); _inventory_loaded = 1; } else { #ifdef DEBUG log_file(base_name(TO) + ".log", "%s inventory_loaded already set.\n", ctime(time())[4..18]); #endif } #ifdef DEBUG log_file(base_name(TO) + ".log", "%s done inventory.\n", ctime(time())[4..18]); foreach(ob in INV(TO)) { if(test_save(ob)) log_file(base_name(TO) + ".log", "%s %s contains %d items.\n", ctime(time())[4..18], ob->query_short(), sizeof(deep_inventory(ob))); } if(!sizeof(INV(TO))) log_file(base_name(TO) + ".log", "%s room has no inventory.\n", ctime(time())[4..18]); #endif } /* init_dynamic_arg() */ /** @ignore yes */ void check_euid() { if ( PO ) { seteuid( geteuid( PO ) ); } } /* check_euid() */