/** * Handler to handle the gathering of implicitly existing * objects that require some skill to find, such as herbs. * Recognition (ie, knowing what it is once you've found it) may be * added later.<p> * The data used for gathering comes from two places: the handler's * database of gatherable items, and the room's local information.<p> * The handler's database contains a class entry for each gatherable:<br> * <dl> * <dt> skill (string) * <dd> The skill needed to find the item. * <dt> difficulty (int) * <dd> The taskmaster difficulty. * <dt> upper (int) * <dd> The taskmaster "upper" parameter. * <dt> extra (mixed) * <dd> The taskmaster "extra" parameter. * <dt> season (string *) * <dd> The seasons during which the gatherable is available (eg, plants). * If this is unset, the default is all seasons. * Note that, currently, it is the same season everywhere on the Disc. * <dt> quant (int or function pointer returning an int) * <dd> The quantity that will be gathered. The function pointer is * passed three parameters: the room, the player, and the name of the * gathered item. This allows, for example, a function to be called * on the room to calculate the quantity, such as: * <pre><br> * <code>(: $1->room_func($2, $3) :)</code> * </pre> * <dt> ob (string) * <dd> The pathname of the object to be created. If the object is * continuous, the <i>amount</i> of ob is set to <i>quant</i>; otherwise, * <i>quant</i> copies of the object are cloned. * </dl><p> * The room's local information is set when the gatherable is added * via add_item(), and consists of:<p> * <dl> * <dt> item name (string or function pointer returning a string) * <dd> The name that will be used to look up the gatherable in the * handler's database. The function pointer is passed two parameters * when evaluated: the room, and the player. * <dt> scarcity (int or function pointer returning an int) * <dd> The percent chance of finding any quantity of the gatherable. * This is an additional * constraint on finding the gatherable, in addition to any skill * requirements and quantity calculations. A scarcity of 100 (the default) * indicates no scarcity constraint. A scarcity of 0 means that the * gatherable will never be found. * </dl> * @author Jeremy */ #include <weather.h> #include <tasks.h> #define INIT_FILE "/obj/gatherables/handler.dat" class item_data { string skill; // skill needed to find this item int difficulty; // taskmaster difficulty string *season; // available season(s) mixed quant; // quantity (int or (int)(:<code>:)) string ob; // filename of object to be created } mapping items = ([ ]); void create() { seteuid("/secure/master"->creator_file(file_name(this_object()))); items = "/obj/handlers/data"->compile_data(({ INIT_FILE })); } /* create() */ /** * This method will return an array of the information associated with * the item. The array consists of: * <pre> * ({ * skill, // skill needed to find this item (string) * difficulty, // taskmaster difficulty (int) * season, // available season(s) (string *) * quant, // quantity (int or (int)(:\<code\>:)) (mixed) * ob // filename of object to be created (string) * }) * </pre> * @return the item array as detailed above. * @param name the name of the item to query * @see add_item() */ mixed query_item( string name ) { class item_data h; if (!(items[name])) { return 0; } h = (class item_data)items[name]; return ({ h->skill, h->difficulty, h->season, h->quant, h->ob }); } /* query_item() */ /** * This method adds an item into the current list of gatherable items. * @param name the name of the item to add * @param skill skill needed to find this item * @param diff taskmaster difficulty * @param season available season(s) * @param quant quantity (int or function pointer returning an int) * @param ob filename of object to be created * @see query_item() * @see gather_item() */ void add_item( string name, string skill, int diff, string *season, mixed quant, string ob ) { class item_data h; h = new( class item_data ); h->skill = skill; h->difficulty = diff; h->season = season; h->quant = quant; h->ob = ob; items[name] = h; } /* add_item() */ /** * This method returns all of the current gatherable items. It returns * this as a mapping of a class, so probably not overly useful except * for debugging. * @return mapping of a locally defined class * @see query_item() * @see add_item() */ mapping query_items() { return items; } /* query_items() */ /** * This method attempts to gather some items in the environment of * the specified player. * @param word the item to try and gather * @param player the player doing the gathering * @return an array of gathered objects * @see query_item() * @see add_item() */ object *gather_item( string word, object player ) { object *basket = ({ }); //The basket that our gathered items go into. object *matched = ({ }); //Objects that match our word. object item; //Object we are building. mixed *current_item; //Data on current gatherable. class item_data info; //Info on the gatherable we're looking at. string temp_name; //Name information. int temp_scarcity; //Scarcity information. int result; //Skill check for results. int quantity; //Amount! if ( !objectp( player ) || !environment( player ) ) return ({ }); matched = match_objects_for_existence( word, ({ environment( player ) }) ); if ( !sizeof( matched ) ) return ({ }); foreach( object temp in matched ) { //If it's in the room, just pick it up. if ( environment( temp ) == environment( player ) ) basket += ({ temp }); //If it's an add item, like most gatherables, we have to be more fancy. current_item = temp->query_gather(); //I don't like doing it this way, I wish it was a class. temp_name = ""; temp_scarcity = 0; //Whatever they matched isn't gatherable. if ( !sizeof( current_item ) ) continue; if ( sizeof( current_item ) != 1 ) { error( "When Terano recoded this, he assumed that this array would " "only contain one array inside it. This turned out to be " "wrong.\n" ); } for ( int x = 0; x < sizeof( current_item[0] ); x = x + 2 ) { //I don't know why this is an array, but it is. switch ( current_item[ 0 ][ x ] ) { case "item name": case "item_name": temp_name = evaluate( current_item[ 0 ][ x+1 ], environment( player ), player ); if ( !stringp( temp_name ) ) temp_name = 0; break; case "scarcity": temp_scarcity = evaluate(current_item[0][ x+1 ], environment( player ), player, temp_name); if ( !intp( temp_scarcity ) ) temp_scarcity = 0; break; } } tell_creator( player, "Started to check for %s.\n", temp_name ); if ( random( 100 ) >= temp_scarcity) { tell_creator( player, "Not found %s due to scarcity (%d).\n", temp_name, temp_scarcity ); continue; } if (!items[temp_name]) { tell_creator( player, "Not found %s due to no info!\n", temp_name ); continue; } info = items[ temp_name ]; if ( sizeof( info->season ) ) { if (member_array( WEATHER->query_season(), info->season ) == -1) { tell_creator( player, "Not found %s: Out of season!\n", temp_name ); continue; } } if ( stringp( info->skill ) && strlen( info->skill ) ) { result = TASKER->perform_task( player, info->skill ,info->difficulty, TM_FREE ); switch( result ) { case FAIL: case BARF: tell_creator( player, "Not found %s: Skillcheck failed!\n", temp_name ); continue; case AWARD: tell_object(player, "%^YELLOW%^You have learned something new about gathering!%^RESET%^\n" ); case SUCCEED: //We just use the result further down the line. } } quantity = evaluate( info->quant, environment( player ), player, temp_name ); if ( !quantity || !intp( quantity ) ) continue; item = clone_object( info->ob ); if (item->query_continuous()) { item->set_amount( quantity ); basket += ({ item }); } else { item->dest_me(); basket += allocate( quantity, (: clone_object( $(info->ob) ) :) ); } continue; //Continue the foreach loop. } return basket; } /* gather_item() */