/** * This is a inheritable that provides an efficient way of storing * lots of items. Only one copy of the item is effectively in storage * and everytime one is removed, an exact copy is duplicated and * put back in the store. Objects are added to the store via * add_object(). The actual storage container object * can be returned by query_cont() and this container should be * searched when you want to find what objects the store contains. * <p> * Any object can inherit this, and methods * should be put in place in the inheriting file that end up calling * create_real_object() which will sort out duplicating the item * and returning an object pointer to the one you can deal with. * @example * inherit "clone_on_demand"; * int do_buy( objects *obs ); * * void setup(){ * set_name("shop"); * set_short("widget shop"); * add_object( "sprocket" ); * } * * object create_object( string arg ){ * if( arg == "sprocket" ) * return clone_object( "/path/of/sprocket" ); * } * * void init(){ * add_command("buy", "<indirect:object:" + * base_name( query_cont() ) + ">"); * } * * int do_buy( object *obs ){ * object ob; * foreach(ob in obs){ * widget = create_real_object(ob); * widget->move( this_player() ); * } * add_succeeded_mess( "$N buy$s $I.\n", obs ); * return 1; * } * * @author Pinkfish Aquilo * @see add_object */ #include <armoury.h> #define INFINITE_LEFT 10000 #define MAX_PROP "max per reset" #define NUM_REMOVED "number removed this reset" #define REFERENCE "name we are reference by" #define DISPLAY "display in shop" nosave object _cont; nosave string _domain; void create(){ } /** * This method returns the container which is used to keep one copy of * each items in storage. * @return the object container */ object query_cont(){ return _cont; } /** @ignore yes */ void check_cont() { if (!_cont) { _cont = clone_object("/std/container"); _cont->set_name( "clone_on_demand-store" ); } } /** @ignore yes */ void reset() { object ob; check_cont(); foreach( ob in all_inventory(_cont) ){ ob->remove_property(NUM_REMOVED); } } /** * This method sets the domain the objects will be created from. * The default objects will always be matched as well. So setting * this will allow objects from the default of the specified armoury * domain. * @param domain the domain to try and create from * @example * set_object_domain("cwc"); */ void set_object_domain(string domain) { _domain = domain; } /* set_object_domain() */ /** * This method returns the domain the objects will be created from. * @return the domain the objects are created from */ string query_object_domain() { return _domain; } /* query_object_domain() */ /** * This method is used to add an item to the storage. * When this method is called, create_object() is called * (with the object name as an arg) in the inheriting file. * If no object is returned by that function, * the name is cloned with clone_object(), and failing that * request_item() is called in the armoury against the name. * * This method makes add_weapon() and add_armour() obsolete. * * @example * add_object( "frog", 1 + random( 3 ) ); * // This will try and create an object called frog, in the order mentioned * // above * @example * add_object( "/obj/food/apple.food", 0 ); * // Add unlimited numbers of apples. * @param name the name of the object to add. * @param max_per_reset the maximum number of items to be available at any one time * @param display_as the name that this item is displayed as in shops * @return 1 if the item was added successfully to the store, 0 if it was not. */ varargs int add_object( string name, int max_per_reset, string display_as ) { object ob; if(!_cont) { _cont = clone_object("/std/container"); _cont->set_name( "clone_on_demand-store" ); } if (!(ob = this_object()->create_object(name))) { if(!ob = clone_object(name)) { ob = ARMOURY->request_item(name, 100, _domain); } } if(ob) { if (ob->query_decays()) { ob->set_decay_speed(0); // Stop decaying objects decaying in inventories. } ob->move(_cont); if (display_as) { ob->add_property( DISPLAY, display_as); } ob->add_property( REFERENCE, name); ob->add_property( MAX_PROP, max_per_reset ); return 1; } return 0; } /** * Returns how more times object ob can be duplicated * @param ob the object to test * @return how many more times */ int query_num_items_left(object ob) { int max; int num; max = ob->query_property( MAX_PROP ); num = ob->query_property( NUM_REMOVED ); if( max ){ return max - num; } return INFINITE_LEFT; } /** * This function returns the quantity of particular object available * to be cloned on demand. In matching which object is the one in * question it uses the short name of the object, which is passed as * an argument to the function. * @param name is the short name of the object you wish to query. * @return the number left, returns INFINITE_LEFT if the shop has an infinite * number, returns -1 if the item isn't stocked. */ int query_number_left(string name) { object * things; things = filter( all_inventory(_cont), (: $1->query_short() == $(name) :) ); if( !sizeof(things) ) { return -1; // we don't have that item. } return query_num_items_left( things[0] ); } /* query_number_left() */ /** * This function can be used to check the quantity left of an array * of items. It returns a parallel array of integers. In other words * the array it returns contains the numbers of stock in array positions * corresponding to the array positions of the objects it was passed. * @example * query_items_left( ({ "banana" , "melon" }) ) * would return ({ 12 , 6 }) if there were 12 bananas and 6 melons left. * @param names an array of the short names of the items you wish to query * @return an array of integers, each one returning like query_number_left * would for the object in that position of the object array. * @see query_number_left */ int * query_items_left( string *names ) { if( !names ) return ({ }); return map( names, (: query_number_left($1) :) ); } /** * This function transfers certain tracking properties from the * original item to the new item which replaces it in storage. To * transfer any additional properties, have switch_extra_properties() * defined in your inheriting object and return an array of extra * properties to transfer */ private void switch_properties(object newone, object original) { mixed *extra, *props, prop; props = ({ MAX_PROP, NUM_REMOVED, REFERENCE }); extra = this_object()->switch_extra_properties(); if( sizeof(extra) ) props += extra; foreach( prop in props ){ newone->add_property( prop, original->query_property( prop ) ); original->remove_property( prop ); } if (newone->query_decay_speed()) { original->set_decay_speed( newone->query_decay_speed() ); newone->set_decay_speed(0); // Stop decaying objects decaying in inventories. } } /** * The main point of entry. 'thing' should be an object already placed * in the clone_on_demand store container via 'add_object'. This * method then duplicates that object, replaces the original copy in * the container with this new one, and returns the original which can * be delt with as normal. * @param thing an object in the store * @return the original object * @see add_object */ protected object create_real_object(object thing) { string name; object new_thing, *continv, temp_cont; int j, max, num; name = thing->query_property( REFERENCE ); if(!name) return 0; new_thing = this_object()->create_object( name ); if ( !new_thing ) { new_thing = clone_object(name); } if ( !new_thing ) { new_thing = ARMOURY->request_item( name, 80 + random( 20 ), _domain ); } if( new_thing ){ switch_properties( new_thing, thing ); /* Restore contents order */ continv = all_inventory(_cont); j = member_array( thing, continv ); // new_thing->move("/room/void"); temp_cont = clone_object("/std/container"); thing->move("/room/void"); continv[j] = new_thing; new_thing->move(_cont); for (j=sizeof(continv)-1;j>=0;j--) { continv[j]->move(temp_cont); continv[j]->move(_cont); } temp_cont->dest_me(); /* Decrement store if applicable */ max = new_thing->query_property( MAX_PROP ); num = new_thing->query_property( NUM_REMOVED ); if ( max ) { new_thing->add_property( NUM_REMOVED, num + 1 ); } } return thing; } /** @ignore yes */ void dest_me(){ if( _cont ) _cont->dest_me(); } /* Old functions, use add_object instead */ /* ps 0, means infinite */ /** @ignore yes */ varargs int add_armour( string name, int max_per_reset, string display_as ) { return add_object(name, max_per_reset, display_as); } /* add_armour() */ /** @ignore yes */ varargs int add_weapon( string name, int max_per_reset, string display_as ) { return add_object(name, max_per_reset, display_as); } /* add_weapon() */