/* -*- LPC -*- */ /* * $Locker: $ * $Id: general_shop.c,v 1.19 2002/08/03 05:09:38 presto Exp $ * */ /** * This the base shop. It buys and sells stuff form players. * <p> * A storeroom must be set for the shop! * <p> * Original made who knows when. * @author Pinkfish * @see set_storeroom() * @change bil * to make the list a lot nicer. * @change Pinkfish * to give shops types and make them send out * reps to sell/buy things from other shops. * @change Ceres * to add burglable storerooms. * @change 23-11-97, Gototh * to add buy, sell, list, browse, value, * cost_too_muchy and not_worthy functions. */ #include <armoury.h> #include <money.h> #include <move_failures.h> #include <shop.h> #include <tasks.h> inherit "/std/room/basic_room"; inherit "/std/shops/inherit/open_shop"; private mixed our_storeroom; private mixed buy_mess; private mixed sell_mess; private mixed list_mess; private mixed value_mess; private mixed too_costly_mess; private mixed not_worthy_mess; private mixed browse_mess; private mixed *other_shops; private int amount_sold; private int amount_bought; private int strict_shop; private int no_steal; private int sell_stolen; private int steal_difficulty; private int min_amount; private int max_inventory; private int sell_large; private mixed shop_type; private mixed buy_func; private mixed sell_func; private mixed value_func; private mixed too_costly_func; private mixed cannot_sell_func; private mixed browse_func; private mixed list_func; private nosave string shoplift_handler; private string original_storeroom; private int _strict_currency; private nosave mapping elist = (["north" : "south", "south" : "north", "east" : "west", "west" : "east", "up" : "down", "down" : "up"]); string shop_list(mixed arr, int detail); string shop_parse(string str, mixed ob, object client, string money, string extra, string which); string find_free_exit(); int do_buy(object *ob); int do_list_these(object *obs); void do_buy_things( object *obs, int cost, object pl ); void do_parse(mixed arr, mixed ob, object client, string money, string extra); object create_mercenary(object rep); void event_shoplift(object command_ob, object thief, object victim); object query_store_room(); int creator_object (object); void create() { buy_mess = ({ "You buy $ob$ for $money$.\n", "$client$ buys $ob$.\n"}); sell_mess = ({ "You sell $ob$ for $money$.\n", "$client$ sells $ob$.\n"}); list_mess = "$extra$"; value_mess = "$ob$ is valued at $money$.\n"; too_costly_mess = "$ob$ is worth too much to be sold here.\n"; not_worthy_mess = "$ob$ is not worth enough to be sold here.\n"; browse_mess = "$ob$ costs $money$, it looks like:\n$extra$"; other_shops = ({ }); max_inventory = MAX_INVENTORY; min_amount = 50; add_help_file("shop"); _strict_currency = 1; ::create(); } /* create() */ /** @ignore yes */ void reset() { if(!random(3)) { remove_property("inventory_loss"); // This is incremented by shoplifting } call_out("send_out_reps", 2); } /* reset() */ /** * This method sets the function to call when buying something. The * function will be called with two elements, the first being the * player doing the buying and the second being the array * of objects being bought. * <p> * If the function is a string then the function of that name * will be called on the shop, if it is a function pointer * then the function pointer will be evaluated. * @param func the function to call * @see set_sell_function() * @see set_value_function() * @see set_too_costly_function() * @see set_cannot_sell_function() * @see set_browse_function() * @see set_list_function() */ void set_buy_function(mixed func) { if (stringp(func) || functionp(func)) { buy_func = func; } } /* set_buy_function() */ /** * This method sets the function to call when selling something. The * function will be called with two elements, the first being the * player doing the selling and the second being the array * of objects being sold. * <p> * If the function is a string then the function of that name * will be called on the shop, if it is a function pointer * then the function pointer will be evaluated. * @param func the function to call * @see set_buy_function() * @see set_value_function() * @see set_too_costly_function() * @see set_cannot_sell_function() * @see set_browse_function() * @see set_list_function() */ void set_sell_function(mixed func) { if (stringp(func) || functionp(func)) { sell_func = func; } } /* set_sell_function() */ /** * This method sets the function to call when an item(*s) are being * valued. The * function will be called with three elements, the first being the * player doing the valueing and the second being the array * of objects being valued and the third being the string * value of the objects. * <p> * If the function is a string then the function of that name * will be called on the shop, if it is a function pointer * then the function pointer will be evaluated. * @param func the function to call * @see set_sell_function() * @see set_buy_function() * @see set_too_costly_function() * @see set_cannot_sell_function() * @see set_browse_function() * @see set_list_function() */ void set_value_function(mixed func) { if (stringp(func) || functionp(func)) { value_func = func; } } /* set_value_function() */ /** * This method sets the function to call when buying something and it * costs too much. The * function will be called with two elements, the first being the * player doing the buying and the second being the array * of objects which cost too much. * <p> * If the function is a string then the function of that name * will be called on the shop, if it is a function pointer * then the function pointer will be evaluated. * @param func the function to call * @see set_sell_function() * @see set_value_function() * @see set_buy_function() * @see set_cannot_sell_function() * @see set_browse_function() * @see set_list_function() */ void set_too_costly_function(mixed func) { if (stringp(func) || functionp(func)) { too_costly_func = func; } } /* set_too_costly_function() */ /** * This method sets the function to call when selling something * which fails for some reason. The * function will be called with two elements, the first being the * player doing the selling and the second being the array * of objects being sold. * <p> * If the function is a string then the function of that name * will be called on the shop, if it is a function pointer * then the function pointer will be evaluated. * @param func the function to call * @see set_sell_function() * @see set_value_function() * @see set_too_costly_function() * @see set_buy_function() * @see set_browse_function() * @see set_list_function() */ void set_cannot_sell_function(mixed func) { if (stringp(func) || functionp(func)) { cannot_sell_func = func; } } /* set_cannot_sell_function() */ /** * This method sets the function to call when browsing something. The * function will be called with two elements, the first being the * player doing the browsing and the second being the array * of objects being browsed. * <p> * If the function is a string then the function of that name * will be called on the shop, if it is a function pointer * then the function pointer will be evaluated. * @param func the function to call * @see set_sell_function() * @see set_value_function() * @see set_too_costly_function() * @see set_cannot_sell_function() * @see set_browse_function() * @see set_list_function() */ void set_browse_function(mixed func) { if (stringp(func) || functionp(func)) { browse_func = func; } } /* set_browse_function() */ /** * This method sets the function to call when listing something. The * function will be called with two elements, the first being the * player doing the listing and the second being the array * of objects being listed. * <p> * If the function is a string then the function of that name * will be called on the shop, if it is a function pointer * then the function pointer will be evaluated. * @param func the function to call * @see set_sell_function() * @see set_value_function() * @see set_too_costly_function() * @see set_cannot_sell_function() * @see set_browse_function() * @see set_list_function() */ void set_list_function(mixed func) { if (stringp(func) || functionp(func)) { list_func = func; } } /* set_list_function() */ /** * This sets the message which will be told to the players when * they sell something at the shop. If the parameter is a string * then that message is sent to the player and nothing is sent to * the other people in the room. If the message is a two element * array, the first element is sent to the player and the second * element is sent to the others in the room. In both these * cases the pattersn $ob$ will be replaces with the objects * being sold, $client$ will be replaced with the client, $money$ * will be replaced with the money information, $extra$ will be replaces * with any extra information. * <p> * If the parameter is a function pointer, then this is called with * the parameter func(obs, client, money, extra); * @param str the message to print * @see set_buy_message() * @see query_sell_message() * @see set_set_value_message() * @see set_too_costly_message() * @see set_no_worthy_message() * @see set_browse_message() * @see set_list_message() * @example * set_sell_message("You sell $ob$ for $money$.\n"); * @example * set_sell_message( ({ "You sell $ob$ for $money$.\n", * "$client$ sells $ob$.\n"}); */ void set_sell_message(mixed str) { sell_mess = str; } /* set_sell_message() */ /** * This sets the message which will be told to the players when * they buy something at the shop. If the parameter is a string * then that message is sent to the player and nothing is sent to * the other people in the room. If the message is a two element * array, the first element is sent to the player and the second * element is sent to the others in the room. In both these * cases the pattersn $ob$ will be replaces with the objects * being sold, $client$ will be replaced with the client, $money$ * will be replaced with the money information, $extra$ will be replaces * with any extra information. * <p> * If the parameter is a function pointer, then this is called with * the parameter func(obs, client, money, extra); * @param str the message to print * @see set_sell_message() * @see query_buy_message() * @see set_set_value_message() * @see set_too_costly_message() * @see set_no_worthy_message() * @see set_browse_message() * @see set_list_message() * @example * set_buy_message("You buy $ob$ for $money$.\n"); * @example * set_buy_message( ({ "You buy $ob$ for $money$.\n", * "$client$ buys $ob$.\n"}); */ void set_buy_message(mixed str) { buy_mess = str; } /* set_buy_message() */ /** * This sets the message which will be told to the players when * they value something at the shop. If the parameter is a string * then that message is sent to the player and nothing is sent to * the other people in the room. If the message is a two element * array, the first element is sent to the player and the second * element is sent to the others in the room. In both these * cases the pattersn $ob$ will be replaces with the objects * being sold, $client$ will be replaced with the client, $money$ * will be replaced with the money information, $extra$ will be replaces * with any extra information. * <p> * If the parameter is a function pointer, then this is called with * the parameter func(obs, client, money, extra); * @param str the message to print * @see set_buy_message() * @see query_value_message() * @see set_set_value_message() * @see set_too_costly_message() * @see set_no_worthy_message() * @see set_browse_message() * @see set_list_message() * @example * set_value_message("$ob$ is valued at $money$.\n"); * @example * set_sell_message( ({ "$ob$ is valued at $money$.\n", * "$client$ values $ob$.\n"}); */ void set_value_message(mixed str) { value_mess = str; } /* set_value_message() */ /** * This sets the message which will be told to the players when * they buy something at the shop and it costs too much. * If the parameter is a string * then that message is sent to the player and nothing is sent to * the other people in the room. If the message is a two element * array, the first element is sent to the player and the second * element is sent to the others in the room. In both these * cases the pattersn $ob$ will be replaces with the objects * being sold, $client$ will be replaced with the client, $money$ * will be replaced with the money information, $extra$ will be replaces * with any extra information. * <p> * If the parameter is a function pointer, then this is called with * the parameter func(obs, client, money, extra); * @param str the message to print * @see set_buy_message() * @see query_too_costly_message() * @see set_set_value_message() * @see set_browse_message() * @see set_no_worthy_message() * @see set_sell_message() * @see set_list_message() * @example * set_browse_message("$ob$ is worth too much to be sold here.\n"); * @example * set_browse_message( ({ "$ob$ is worth too much to be sold here.\n", * "$client$ tries to sell the terribly expensive $ob$.\n"}); */ void set_too_costly_message(mixed str) { too_costly_mess = str; } /* set_too_costly_message() */ /** * This sets the message which will be told to the players when * they sell soemthign that is not worth enough at the shop. * If the parameter is a string * then that message is sent to the player and nothing is sent to * the other people in the room. If the message is a two element * array, the first element is sent to the player and the second * element is sent to the others in the room. In both these * cases the pattersn $ob$ will be replaces with the objects * being sold, $client$ will be replaced with the client, $money$ * will be replaced with the money information, $extra$ will be replaces * with any extra information. * <p> * If the parameter is a function pointer, then this is called with * the parameter func(obs, client, money, extra); * @param str the message to print * @see set_buy_message() * @see query_not_worthy_message() * @see set_set_value_message() * @see set_too_costly_message() * @see set_browse_message() * @see set_sell_message() * @see set_list_message() * @example * set_not_worthy_message("$ob$ is not worth enough to be sold here.\n"); * @example * set_not_worthy_message( ({ "$ob$ is not worth enough to be sold here.\n", * "$client$ tries to sell the rubbishy $ob$.\n"}); */ void set_not_worthy_message(mixed str) { not_worthy_mess = str; } /* set_not_worthy_message() */ /** * This sets the message which will be told to the players when * they browse something at the shop. If the parameter is a string * then that message is sent to the player and nothing is sent to * the other people in the room. If the message is a two element * array, the first element is sent to the player and the second * element is sent to the others in the room. In both these * cases the pattersn $ob$ will be replaces with the objects * being sold, $client$ will be replaced with the client, $money$ * will be replaced with the money information, $extra$ will be replaces * with any extra information. In the case oif a browse the * extra information is the long description of the object. * <p> * If the parameter is a function pointer, then this is called with * the parameter func(obs, client, money, extra); * @param str the message to print * @see set_buy_message() * @see query_browse_message() * @see set_set_value_message() * @see set_too_costly_message() * @see set_no_worthy_message() * @see set_sell_message() * @see set_list_message() * @example * set_browse_message("$ob$ costs $money$, it looks like:\n$extra$"); * @example * set_browse_message( ({ "$ob$ costs $money$, it looks like:\n$extra$", * "$client$ browses $ob$.\n"}); */ void set_browse_message(mixed str) { browse_mess = str; } /* set_browse_message() */ /** * This sets the message which will be told to the players when * they list something at the shop. If the parameter is a string * then that message is sent to the player and nothing is sent to * the other people in the room. If the message is a two element * array, the first element is sent to the player and the second * element is sent to the others in the room. In both these * cases the pattersn $ob$ will be replaces with the objects * being sold, $client$ will be replaced with the client, $money$ * will be replaced with the money information, $extra$ will be replaces * with any extra information. In the case of a list, * the extra information *is* the list. * <p> * If the parameter is a function pointer, then this is called with * the parameter func(obs, client, money, extra); * @param str the message to print * @see set_buy_message() * @see query_buy_message() * @see set_set_value_message() * @see set_too_costly_message() * @see set_no_worthy_message() * @see set_browse_message() * @see set_list_message() * @example * set_list_message("$extra$.\n"); * @example * set_list_message( ({ "You list $ob$ for $money$.\n", * "$client$ lists $ob$.\n"}); */ void set_list_message(mixed str) { list_mess = str; } /* set_list_message() */ /** * This sets the conditons which the shop will be open during. If this * is set to an integer the shop will always be in that state, so * if you set the open condition to 0, it would always be closed. * If it is set to a string, then that function will be called on * this object to test to see if it is open. If it iset to * function pointer, the function pointer will be evaluated. If it * is set to an array, the first element specifies the object and the * second specifies the function to call. * @see query_open_condition() * @see is_open() */ void set_open_condition(mixed str) { if (functionp(str)) { ::set_open_function(str); } else if (intp(str)) { ::set_open_function( (: $(str) :) ); } else if (pointerp(str)) { ::set_open_function( (: call_other($(str[0]), $(str[1]), $1) :) ); } if ( stringp(str) ) { ::set_open_function( (: call_other( this_object(), $(str), $1 ) :) ); } } /* set_open_condition() */ /** * This method sets the no steal property. If a shop is set * as no steal, then it cannot be shoplifted. * @param i the new value of the no_steal property * @see set_steal_difficulty() * @see query_no_steal() * @see set_sell_stolen() */ void set_no_steal(int i) { no_steal = i; } /* set_no_steal() */ /** * This method sets the difficulty at which to steal stuff from * this shop. * @see set_no_steal() * @see set_sell_stolen() * @see query_steal_difficulty() * @param i the new value for the steal difficulty */ void set_steal_difficulty(int i) { steal_difficulty = i; } /* set_steal_difficulty() */ /** * This method sets the shop as a place which will receive and * seel stolen goods. It automaticly sets the no_steal property * to 1. * @see set_no_steal() * @see set_steal_difficulty() * @see query_sell_stolen() * @param i the new value for the stolen property */ void set_sell_stolen(int i) { sell_stolen = i; no_steal = 1; } /* set_sell_stolen() */ /** * This method sets the shop to sell very large objects. ie. If a player * cannot carry items they are placed in the room for the player rather * than not being sold to the player. */ void set_sell_large(int i) { sell_large = i; } /** * This method returns the current sell message of the shop. * @see set_sell_mess() * @return the current sell message of the shop */ mixed query_sell_mess() { return sell_mess; } /* query_sell_mess() */ /** * This method returns the current list message of the shop. * @see set_list_mess() * @return the current list message of the shop */ mixed query_list_mess() { return list_mess; } /* query_list_mess() */ /** * This method returns the current value message of the shop. * @see set_value_mess() * @return the current value message of the shop */ mixed query_value_mess() { return value_mess; } /* query_value_mess() */ /** * This method returns the current too costly message of the shop. * @see set_too costly_mess() * @return the current too costly message of the shop */ mixed query_too_costly_mess() { return too_costly_mess; } /* query_too_costly_mess() */ /** * This method returns the current not worthy message of the shop. * @see set_not worthy_mess() * @return the current not worthy message of the shop */ mixed query_not_worthy_mess() { return not_worthy_mess; } /* query_not_worthy_mess() */ /** * This method returns the current buy message of the shop. * @see set_buy_mess() * @return the current buy message of the shop */ mixed query_buy_mess() { return buy_mess; } /* query_buy_mess() */ /** * This method returns the current browse message of the shop. * @see set_browse_mess() * @return the current browse message of the shop */ mixed query_browse_mess() { return browse_mess; } /* query_browse_mess() */ /** * This method returns the current no steal property. * @see set_no_steal_mess() * @return the current no steal properyt of the shop */ int query_no_steal() { return no_steal; } /* query_no_steal() */ /** * This method returns the current steal difficulty of the shop. * @see set_steal_difficulty_mess() * @return the current steal difficulty message of the shop */ int query_steal_difficulty(int i) { return steal_difficulty; } /* query_steal_difficulty() */ /** * This method returns the shop lift response handler for the shop. * This allows the shop to respond in someway to someone shop * lifting stuff, like the heavys in Ankh-Morpork. * @returns the shop lift response handler * @see set_shoplift_response_handler() * @see set_no_steal() * @see set_sell_stolen() * @see set_steal_difficulty() */ string query_shoplift_response_handler() { return shoplift_handler; } /* query_shoplift_response_handler() */ /** * This method tells us if the shop is really a shop or not. * @return 1 always */ int query_shop() { return 1; } /* query_shop() */ /** * This method returns the items which can potentially be shop lifted * with the passed in string. * @param str the name for the object to attempt to shop lift * @return the array of matching objects * @see query_steal_difficulty() * @see query_shoplift_response_handler() */ object *query_shop_lift_items(string str, object player) { return match_objects_for_existence(str, ({ query_store_room() })); } /* query_shop_lift_items() */ /** * This method turns the objects into real objects (if that is * nessessary, it is not with a normal shop). * @param ob the object to turn into a normal object */ object shoplift_success(object ob) { return ob; } /* shoplift_success() */ /** * This method sets the shop lift response handler for the shop. * This allows the shop to respond in someway to shop lifting * stuff, like the heavies in Ankh-Morpork. The function * 'handle_shoplift' will be called on the handler when the * shop lift is attempted. It will be passed two arguments * the first is the thief, the second is the room being * shoplifted. This can be set to afucntion pointer * which will be evaluated and passed in two arguemtns * when a shoplift occurs. * @see query_shoplift_response_handler() * @see set_no_steal() * @see set_sell_stolen() * @see set_steal_difficulty() * @param word the new shop lift response handler */ void set_shoplift_response_handler(string word) { shoplift_handler = word; } /* set_shoplift_response_handler() */ /** * This method sets the minimum value of items that can be sold here. * @see query_min_amount() */ void set_min_amount(int i) { min_amount = i; } /** * This method sets the maximum number of inventory items this shop will * take before it starts to refuse to purchase items from players and * deleting items from its inventory. * The default for this is defined as MAX_INVENTORY in shop.h */ void set_max_inventory(int i) { max_inventory = i; } /** @ignore yes */ void init() { string room; ::init(); if(stringp(our_storeroom)) { room = our_storeroom; } else if(our_storeroom) { room = file_name(our_storeroom); } else { tell_room(this_object(), "Oh dear, we don't seem to have a storeroom.\n"); } add_command("sell", "<indirect:object:me>"); add_command("buy", "<indirect:object:"+room+">"); add_command("list", "[all]"); add_command("list", "<indirect:object:"+room+">", (: do_list_these($1) :)); add_command("browse", "<indirect:object:"+room+">"); add_command("value", "<indirect:object:me>"); } /* init() */ /** * This method returns the maximum value oif an object that can * be sold here. * @see query_min_amount() * @return the maximum amount * @see /obj/handlers/money_handler.c */ int query_max_amount() { return MAX_AMOUNT; } /* query_max_amount() */ /** * This method returns the minimum value oif an object that can * be sold here. * @see query_max_amount() * @return the minimum amount * @see /obj/handlers/money_handler.c */ int query_min_amount() { return min_amount; } /* query_min_amount() */ /** * This method returns the value of the object in this shop. * @return the value of the object here * @param thing the thing to value * @param sell are we selling it? * @see query_max_amount() * @see query_min_amount() */ varargs int query_value(object thing, int sell) { // Only use the cost here property if we're not selling. if ( thing->query_property("cost here") && !sell ) { return (int)thing->query_property("cost here"); } else { return (int)thing->query_value_at( this_object() ); } } /* query_value() */ /** * This returns the amount of money you can sell and object for * which is less than the amount it will be sold for). * @param n the value to scale * @return the sell value */ int scaled_value(int n) { int i, tot, *fish; fish = PAY_RATES; if(n < fish[0]) { return n; } else { tot = fish[0]; n -= fish[0]; } i = 0; while(n && ((i + 1) < sizeof(fish))) { if(n >= fish[i]) { tot += fish[i + 1] * fish[i] / 100; n -= fish[i]; } else { tot += fish[i + 1] * n / 100; n = 0; } i += 2; } if(n) { tot += (n * fish[sizeof(fish) - 1]) / 100; } return tot; } /* scaled_value() */ /** * This method does the actual selling. * @param in_obs the objects to sell * @return 1 on success, 0 on failure */ int do_sell(object *in_obs) { int i, amt, total_amt; string place, filename, *text = ({ }); object money, *obs, *selling, *cannot, *stolen, storeob, one_item, *cre; object thing; mixed *m_array; if(!is_open(this_player(), 0)) { return 0; } in_obs = uniq_array( in_obs ); obs = filter_array(in_obs, (: !$1->query_keep() :)); cre = filter (obs, (: creator_object ($1) :)); if (sizeof (cre) && strsrch (file_name (this_object()), "_dev") == -1) { if (!this_player()->query_creator()) { tell_object (this_player(), "Oh dear, you shouldn't have " + query_multiple_short (cre) + "! They disappear with a flash " "of octarine light.\n"); cre->move ("/room/rubbish"); } else { tell_object (this_player(), "Oh dear, you shouldn't be trying to sell " + query_multiple_short (cre) + "!\n"); } foreach (thing in cre) { if ( thing->query_property( "virtual name" ) ) { filename = thing->query_property( "virtual name" ); } else { filename = base_name (thing); } text += ({filename}); } log_file ("ILLEGAL_OBJECT", sprintf ("%s: %s tried to sell %s at " "%s.\n\n", ctime (time()), this_player()->query_name(), query_multiple_short (text), file_name (this_object()))); obs -= cre; } if(!sizeof(obs)) { this_player()->add_failed_mess(this_object(), "You previously decided to keep $I.\n", in_obs); return 0; } in_obs = this_player()->query_holding() + this_player()->query_armours(); cannot = filter(obs, (: member_array($1, $2) != -1 :), in_obs); if (sizeof(cannot)) { obs -= cannot; tell_object(this_player(), "You decide not to sell " + query_multiple_short(cannot, "the") + ", because " "you are wearing or holding $V$0=it,them$V$.\n"); /* add_failed_mess("You cannot sell $I since you are wearing or holding " "them.\n", cannot); return 0; */ } // If there are more items in the storeroom than max_inventory allows // return a failure message. if(objectp(our_storeroom)) { storeob = our_storeroom; } else { our_storeroom->rabbit_away(); storeob = find_object(our_storeroom); } if(sizeof(all_inventory(storeob)) > max_inventory) { this_player()->add_failed_mess(this_object(), "Sorry, the shop is full up and isn't buying items!\n", ({ })); call_out("tidy_inventory", random(20)); return 0; } if(sizeof(obs) > MAX_OBS) { write("The shopkeeper can't cope with all those objects.\n"); obs = obs[0..MAX_OBS - 1]; } selling = cannot = stolen = ({ }); place = query_property("place"); if(!place || (place == "")) { place = "default"; } for(i = 0; i < sizeof(obs); i++) { if(!sell_stolen && obs[i]->query_property("stolen")) { stolen += ({obs[i]}); continue; } else { obs[i]->remove_property("stolen"); } if((this_object()->query_value(obs[i], 1) > 0) && !obs[i]->do_not_sell() && !this_object()->do_not_buy(obs[i]) && (!strict_shop || shop_type == obs[i]->query_property("shop type")) && environment(obs[i]) == this_player()) { if(obs[i]->move(our_storeroom)) { if(obs[i]->short()) { cannot += ({obs[i]}); } continue; } amt = (int)this_object()->query_value(obs[i], 1); if(!(obs[i]->query_property("sale_value"))) { amt = scaled_value( amt ); } if((amt <= (int)this_object()->query_max_amount()) && (amt >= (int)this_object()->query_min_amount())) { if(shop_type != obs[i]->query_property("shop type")) { amt = (amt * 90) / 100; } total_amt += amt; selling += ({obs[i]}); obs[i]->being_sold(); } else { if(obs[i]->short()) cannot += ({obs[i]}); obs[i]->move(this_player()); } } else { if(obs[i]->short()) { cannot += ({obs[i]}); } } } if (!sizeof(selling)) { if (sizeof(cannot)) { if (stringp(cannot_sell_func)) { call_other(this_object(), cannot_sell_func, this_player(), cannot); } else if (functionp(cannot_sell_func)) { evaluate(cannot_sell_func, this_player(), cannot); } this_player()->add_failed_mess(this_object(), "You cannot sell $I.\n", cannot); } else if(sizeof(stolen)) { if(sizeof(stolen) > 1) { this_player()->add_failed_mess(this_object(), "You cannot sell $I because they're stolen!\n", stolen); } else { this_player()->add_failed_mess(this_object(), "You cannot sell $I because it's stolen!\n", stolen); } foreach(one_item in obs) { if(one_item->query_property("stolen") == this_player()->query_name()) { event_shoplift(this_object(), this_player(), this_object()); break; } } } else { this_player()->add_failed_mess(this_object(), "You have nothing to sell.\n", ({ })); } return 0; } if(this_object()->cannot_afford(total_amt)) { selling->move(this_player()); this_player()->add_failed_mess(this_object(), "The shop cannot afford to buy $I from you.\n", selling); return 0; } amount_sold += total_amt; m_array = (mixed *)MONEY_HAND->create_money_array(total_amt, place); money = clone_object(MONEY_OBJECT); money->set_money_array(m_array); if(sizeof(cannot)) { if(stringp(cannot_sell_func)) { call_other(this_object(), cannot_sell_func, this_player(), cannot); } else if (functionp(cannot_sell_func)) { evaluate(cannot_sell_func, this_player(), cannot); } write("You cannot sell "+query_multiple_short(cannot)+".\n"); cannot->move(this_player()); } if(stringp(sell_func)) { call_other(this_object(), sell_func, this_player(), obs); } else if (functionp(sell_func)) { evaluate(sell_func, this_player(), obs); } do_parse(sell_mess, selling, this_player(), (string)MONEY_HAND->money_string(m_array), ""); if((int)money->move(this_player()) != MOVE_OK) { tell_object(this_player(), "You're too heavily burdened to accept all that money, " "so the shopkeeper puts it on the floor.\n"); money->move(this_object()); } this_object()->made_transaction(-total_amt, selling); return 1; } /* do_sell() */ /** * This method does the actual buying. * @param obs the objects to buy * @return 1 on success, 0 on failure */ /** * This method determines whether or not a given object is a creator object * and should be in this shop or not. * @param ob the object to check * @return 1 if the object is not allowed, 0 if it is. */ int creator_object( object ob ) { string path; if ( file_name( ob )[0..2] == "/w/" ) { return 1; } if ( ( path = ob->query_property( "virtual name" ) ) && path[0..2] == "/w/" ) { return 1; } return 0; } /* creator_object() */ int do_buy(object *obs) { int i, amt, ob_amt, total_cost; string place; object money, *to_buy, *cannot, *too_much; object *creator_obs; if(!is_open(this_player(), 0)) { return 0; } if(sizeof(obs) > MAX_OBS) { write("The shopkeeper can't cope with all those objects.\n"); obs = obs[0..MAX_OBS-1]; } creator_obs = filter( obs, (: creator_object( $1 ) :) ); if ( sizeof( creator_obs ) && file_name()[0..2] != "/w/" ) { tell_object( this_player(), "You cannot buy " + query_multiple_short( creator_obs, "the" ) + " because they shouldn't be in the game!\n" ); obs -= creator_obs; } to_buy = too_much = cannot = ({ }); place = query_property("place"); if(!place || (place == "")) { place = "default"; } money = present(MONEY_ALIAS, this_player()); if(!money) { if(stringp(too_costly_func)) { call_other(this_object(), too_costly_func, this_player(), obs); } else if (functionp(too_costly_func)) { evaluate(too_costly_func, this_player(), obs); } this_player()->add_failed_mess(this_object(), "You have no money.\n", obs); return 0; } amt = money->query_value_in(place); if(place != "default" && !_strict_currency ) { amt += money->query_value_in("default"); } while(i < sizeof(obs)) { ob_amt = this_object()->query_value(obs[i], 0); if(ob_amt > amt) { if(obs[i]->short()) { too_much += ({obs[i]}); } obs = delete(obs, i, 1); continue; } if(obs[i]->move(this_player())) { if(!sell_large) { if(obs[i]->short()) { cannot += ({obs[i]}); } i++; continue; } else { obs[i]->move(this_object()); } } amt -= ob_amt; total_cost += ob_amt; to_buy += ({obs[i]}); i++; } amount_bought += total_cost; if(sizeof(cannot)) { this_player()->add_failed_mess(this_object(), "You cannot pick up $I.\n", cannot); } if(sizeof(too_much)) { if(stringp(too_costly_func)) { call_other(this_object(), too_costly_func, this_player(), cannot); } else if (functionp(too_costly_func)) { evaluate(too_costly_func, this_player(), cannot); } this_player()->add_failed_mess(this_object(), "$I costs too much.\n", too_much); } if(!sizeof(to_buy)) { return 0; } do_buy_things(to_buy, total_cost, this_player()); return 1; } /* do_buy() */ /** @ignore yes */ void do_buy_things(object *obs, int cost, object pl) { int i, j; string place; object money, change; mixed m_array, p_array; place = query_property("place"); if(!place || (place == "")) { place = "default"; } money = present(MONEY_ALIAS, pl); if(!money) { if(stringp(too_costly_func)) { call_other(this_object(), too_costly_func, this_player(), obs); } else if (functionp(too_costly_func)) { evaluate(too_costly_func, this_object(), obs); } this_player()->add_failed_mess(this_object(), "You don't have any money.\n", obs); return 0; } change = clone_object(MONEY_OBJECT); m_array = (int)MONEY_HAND->create_money_array(cost, place); for(i = 0; i < sizeof(m_array); i += 2) { p_array = (mixed *)MONEY_HAND->make_payment(m_array[i], m_array[i + 1], money, place); if(!pointerp(p_array)) { continue; } for(j = 0; j < sizeof(p_array[0]); j += 2) { money->adjust_money(-p_array[0][j + 1], p_array[0][j]); } change->adjust_money(p_array[1]); } do_parse(buy_mess, obs, pl, (string)MONEY_HAND->money_string(m_array), ""); if(stringp(buy_func)) { call_other(this_object(), buy_func, pl, obs); } else if (functionp(buy_func)) { evaluate(buy_func, pl, obs); } if((int)change->move(pl) != MOVE_OK) { tell_object(pl, "You are too heavily burdened to accept " "your change, so the shopkeeper puts it on the floor.\n"); change->move(this_object()); } this_object()->made_transaction(cost, obs); } /* do_buy() */ /** * This method lists all the objects in stock. * @return 1 on succes, 0 on failure * @param do_list_these() */ int do_list() { object ob; if(!is_open(this_player(), 0)) { return 0; } if(objectp(our_storeroom)) { ob = our_storeroom; } else { if ( original_storeroom ) { our_storeroom = load_object( original_storeroom ); ob = our_storeroom; } else { add_failed_mess( "Please notify a creator: the storeroom for " "this shop cannot load or has gone missing.\n" ); return 0; } } if(stringp(list_func)) { call_other(this_object(), list_func, this_player()); } else if (functionp(list_func)) { evaluate(list_func, this_player()); } do_parse(list_mess, ({ this_object() }), this_player(), "", shop_list(all_inventory(ob), 0)); return 1; } /* do_list() */ /** * This method lists only the specified objects * @return 1 on success, 0 on failure * @see do_list() */ int do_list_these(object *obs) { if(!is_open(this_player(), 0)) { return 0; } do_parse(list_mess, ({this_object()}), this_player(), "", shop_list(obs, 1)); return 1; } /* do_list_these() */ /** * This method is called when the player is browseing stuff. * @param obs the objects to browse * @return 1 on success, 0 on failure */ int do_browse(object *obs) { int i, value; string place; if (!is_open(this_player(), 0)) { return 0; } place = query_property("place"); if(!place || (place == "")) { place = "default"; } if(stringp(browse_func)) { call_other(this_object(), browse_func, this_player(), obs); } else if (functionp(browse_func)) { evaluate(browse_func, this_player(), obs); } for(i = 0; i < sizeof(obs); i++) { value = (int)this_object()->query_value(obs[i], 0); do_parse(browse_mess, obs[i..i], this_player(), (string)MONEY_HAND->money_value_string(value, place), (string)obs[i]->long()); } return 1; } /* do_browse() */ /** * This method is called when the player is valueing stuff. * @param obs the objects to value * @return 1 on success, 0 on failure */ int do_value(object *obs) { int i; int val; int total; string place; int obnum; if(!is_open(this_player(), 0)) { return 0; } place = query_property("place"); if(!place || (place == "")) { place = "default"; } for(i = 0; i < sizeof(obs); i++) { if(obs[i]->do_not_sell() || this_object()->do_not_buy(obs[i]) || (environment(obs[i] ) != this_player()) || (strict_shop && (shop_type != (string)obs[i]->query_property("shop type")))) { val = 0; } else { val = (int)this_object()->query_value(obs[i], 1); if (shop_type != (string)obs[i]->query_property("shop type")) { val = (val * 90) / 100; } } val = scaled_value(val); total += val; if(val > (int)this_object()->query_max_amount()) { do_parse(too_costly_mess, obs[i..i], this_player(), "", (string)obs[i]->do_not_sell()); total -= val; } else if(val < (int)this_object()->query_min_amount()) { do_parse(not_worthy_mess, obs[i..i], this_player(), "", (string)obs[i]->do_not_sell()); total -= val; } else { do_parse(value_mess, obs[i..i], this_player(), (string)MONEY_HAND->money_value_string(val, place), (string)obs[i]->do_not_sell()); if(stringp(value_func)) { call_other(this_object(), value_func, this_player(), obs, MONEY_HAND->money_string(MONEY_HAND->create_money_array(val, place))); } else if (functionp(value_func)) { evaluate(value_func, this_player(), obs, MONEY_HAND->money_string(MONEY_HAND->create_money_array(val, place))); } obnum++; } } if (obnum > 1) { write("This gives you a total value of " + MONEY_HAND->money_value_string(total, place) + ".\n"); return 1; } this_player()->add_failed_mess( this_object(), "None of your items are " "suitable for sale here!\n", obs ); return obnum; } /* do_value() */ /** * This method creates the list for the shop. * @param arr the array of objecsts to list * @param detail display them in detail? * @return the string list */ string shop_list(mixed arr, int detail) { int i, j, value, num; string s, mon, place, *shorts, *vals; object *list; mapping inv, costs; mixed ind; if(pointerp(arr)) { list = arr; } else { list = all_inventory(this_object()); } // Only keep track of things with shorts. inv = ([ ]); for(i = 0; i < sizeof(list); i++) { s = (string)list[i]->short(); if(!s || !this_object()->query_value(list[i], 1)) { continue; } if(!stringp(s)) { s = "get a creator for this one!"; } if(inv[s]) { inv[s] += ({list[i]}); } else { inv[s] = ({list[i]}); } } // Okay, print it. s = ""; shorts = m_indices(inv); if(!sizeof(shorts)) { if(detail) { return "The shop is all out of what you wanted.\n"; } else { return "The shop is totally out of stock.\n"; } } s = "You find on offer:\n"; place = query_property("place"); if(!place || (place == "")) { place = "default"; } for(i = 0; i < sizeof(shorts); i++) { ind = inv[shorts[i]]; num = sizeof(ind); if(num == 1 && ind[0]->query_collective()) num = ind[0]->query_amount(); switch(num) { case 1: s += "Our very last " + shorts[i]; break; case 2..5 : s += capitalize(query_num(num, 0)+" "+ (string)ind[0]->query_plural()); break; default: if(detail) { s += capitalize(query_num(num, 0) + " " + (string)ind[0]->query_plural()); } else { s += "A large selection of "+ (string)ind[0]->query_plural(); } } if(detail) { costs = ([ ]); for(j = 0; j < sizeof(ind); j++) { value = (int)this_object()->query_value(ind[j], 0); mon = (string)MONEY_HAND->money_value_string(value, place); if(!costs[mon]) { costs[mon] = ({""+(j + 1)}); } else { costs[mon] += ({""+(j + 1)}); } } if(m_sizeof(costs) == 1) { s += " for "+m_indices(costs)[0]; if(sizeof(m_values(costs)[0]) > 1) { s += " each.\n"; } else { s += ".\n"; } } else { s += ":-\n"; vals = m_indices(costs); for(j = 0; j < sizeof(vals); j++) { s += " [#"+implode(costs[vals[j]], ",")+"] for " +vals[j]+".\n"; } } } else { s += ".\n"; } } return s; } /* shop_list() */ /** * This method sets the current store room associated with * the shop. This is important! A shop needs a storeroom. * @param ob the storeroom to set * @example * set_store_room(PATH+"store_room"); */ void set_store_room(mixed ob) { if(stringp(ob)) { original_storeroom = ob; our_storeroom = find_object(ob); if(!our_storeroom) { our_storeroom = load_object(ob); } } else our_storeroom = ob; } /* set_store_room() */ /** @ignore yes */ void guards(object tp) { object ob; if(environment(tp) != this_object() && environment(tp) != our_storeroom) { return; } while(!random(6)) { ob = create_mercenary(0); ob->move(environment(tp)); ob->attack_ob(tp); } } /* guards() */ /** * This method returns the storeroom associated with the shop. * @see set_store_room() * @return the current store room */ object query_store_room() { return our_storeroom; } /* query_store_room() */ /** * This method is the major message processing function for * the buye messages, sell messages etc. It handles calling * the functions and setting the results back onto the * player. * @param arr the value of the message * @param ob the objects to process * @param money the money string * @param extra the extra string */ void do_parse(mixed arr, object *ob, object client, string money, string extra) { if(stringp(arr)) { this_player()->show_message("$P$List$P$"+ this_player()->convert_message(replace(arr, ({"$ob$", query_multiple_short(ob), "$client$", this_player()->short(), "$money$", money, "$extra$", extra})))); this_player()->add_succeeded_mess(this_object(), "", ob); } else if (functionp(arr)) { evaluate(arr, ob, client, money, extra); } else { this_player()->show_message("$P$List$P$"+ this_player()->convert_message(replace(arr[0], ({"$ob$", query_multiple_short(ob), "$client$", this_player()->short(), "$money$", money, "$extra$", extra})))); this_player()->add_succeeded_mess(this_object(), ({"", replace(arr[1], ({"$ob$", "$I", "$client$", "$N", "$money$", money, "$extra$", extra}))}), ob); } } /* do_parse() */ /** * This is a minature version of do_parse(), which can be used when you * set the money array to a function pointer in order to make message * processing go smoother. * @param str the value of the message * @param ob the objects to process * @param money the money string * @param extra the extra string * @return a parsed string */ string shop_parse(string str, mixed ob, object client, string money, string extra, string which) { if(sizeof(ob)) { str = replace(str, "$ob$", query_multiple_short(ob, which)); } else { str = replace(str, "$ob$", call_other(ob, which +"_short")); } if(client) { str = replace(str, "$client$", client->the_short()); } str = replace(str, ({"$money$", money, "$extra$", extra})); return str; } /* shop_parse() */ /** * This method adds a shop with shich we will exchange inventories * of certain types. So the main shop can sell off its swords and * stuff to the sword shop. * @param shop the other shop */ void add_other_shop(mixed shop) { // Should give a nice string telling us the other room. other_shops += ({shop}); } /* add_other_shop() */ /** * This method returns the type of the shop. This conttrols what sort * of merchandise the shop will buy and sell. * <p>The types of allowed shops are: * <ul> * <li> jewelery * <li> armoury * <li> clothes * <li> magic * <li> none set (ie: 0, general type) * </ul> * @return the shop type * @see set_shop_type() * @see set_strict_shop() */ string query_shop_type() { return shop_type; } /* query_shop_type() */ /** * This method sets the type of the shop. This controls what sort of * mechandise the shop will buy and sell. * <p>The types of allowed shops are: * <ul> * <li> jewelery * <li> armoury * <li> clothes * <li> magic * <li> none set (ie: 0, general type) * </ul> * @see query_shop_type() * @see set_strict_shop() * @param ty the type of the shop */ void set_shop_type(string ty) { shop_type = ty; } /* set_shop_type() */ /** * This method sets the strictness of the shop, if the shop is strict * it will not deal in items of other types at all. * @param i the new strict value * @see set_shop_type() * @see query_strict_shop() */ void set_strict_shop(int i) { strict_shop = i; } /* set_strict_shop() */ /** * This method returns the structness of the shop, if the shop is strict * it will not deal in items of other types at all. * @return the current strict value * @see set_shop_type() * @see set_strict_shop() */ int query_strict_shop() { return strict_shop; } /* query_strict_shop() */ /** * This method creates the sales representative which is sent off * to exchange goods with other shops. * @return the sales representative * @see add_other_shop() * @see send_out_reps() */ object create_rep() { object ob; ob = clone_object("/obj/monster"); ob->set_name("rep"); ob->set_short("sales rep"); ob->add_adjective("sales"); ob->set_long("This is tall strong looking sales rep. He " "stares at you with bright piercing eyes.\n"); ob->add_alias("Sales rep alias"); ob->set_guild("fighter"); ob->set_race("human"); ob->adjust_bon_str(15); ob->set_level(60); ARMOURY->request_weapon("dagger", 100)->move(ob); ARMOURY->request_armour("cloth robe", 100)->move(ob); ob->init_equip(); ob->add_property("rep type", shop_type); all_inventory(ob)->add_property("mine", 1); return ob; } /* create_rep() */ // Send out the reps. /** * This method checks to see if there are any other shops * associated with ours and sends out representatives to them * to exchange goods. * @see create_rep() * @see add_other_shop() */ void send_out_reps() { int i; object ob; for(i = 0; i < sizeof(other_shops); i++) { ob = (object)this_object()->create_rep(); ob->add_property("goto destination", other_shops[i]); ob->add_property("goto property", "shop"); ob->move(this_object(), "$N stride$s determinedly into the room."); ob->add_triggered_action("froggy", "goto_destination", file_name(this_object()), "rep_made_it"); } } /* send_out_reps() */ /** * This method is called onces the representative * reaches its destination. * @param bing we mkde it ok */ void rep_made_it(int bing) { object *obs, rep; int i, cost; if(!bing) { previous_object()->init_command("'Oh no! I am utterly lost!"); previous_object()->init_command("sigh"); call_out("set_up_return", 5, previous_object()); return ; } rep = present("Sales rep alias", (object)previous_object()->query_current_room()); obs = (previous_object()->query_current_room())->query_stock(shop_type); if(!obs) { obs = ({ }); } if(!sizeof(obs)) { this_object()->none_to_sell(); call_out("set_up_return", 5, previous_object()); return ; } for(i = 0; i < sizeof(obs); i++) { if(obs[i]) { cost += (int)this_object()->query_value( obs[ i ], 1 ) * 2 / 3; } } call_out("do_rep_buy", 5, ({previous_object(), obs, cost})); cost += (int)this_object()->query_value(obs[i], 1) * 2 / 3; call_out("do_rep_buy", 5, ({previous_object(), obs, cost})); previous_object()->adjust_value(cost); } /* rep_made_it() */ /** * This method creates a mercenaries to wander along with the * sales rep to get the stuff to the destination. * @param rep the representative to protect * @return the new mercenary */ object create_mercenary(object rep) { object ob; string nam; if(rep) { nam = implode(rep->query_adjectives(), " ")+" "+rep->query_name(); } ob = clone_object("/obj/monster"); ob->set_name("mercenary"); ob->add_alias("troll"); ob->add_adjective("troll"); ob->set_short("troll mercenary"); ob->set_main_plural("troll mercenaries"); ob->set_race("troll"); ob->set_class("fighter"); ob->set_level(200 + random(200)); ob->set_long("This is a large, hulking troll. He looks " "quite competent and capable of mashing you with or " "without a weapon.\n"); if(rep) { ob->move(environment(rep)); ob->do_command("follow "+nam); ob->do_command("protect "+nam); ob->add_property("merchant", rep); } else { ARMOURY->request_weapon("spiked club", 100)->move(ob); } ob->set_natural(1); ob->init_equip(); ob->set_join_fights("Troll yells something incomprehensible.\n"); ob->set_join_fight_type(0); // So they only beat up players. return ob; } /* create_mercenry() */ /* The shop types are: * jewelery * armoury * clothes * magic * none set (ie: 0, general type) */ /** * This method returns alkl the stock in the shop of various * types. The types are: * <ul> * <li> jewelery * <li> armoury * <li> clothes * <li> magic * <li> none set (ie: 0, general type) * </ul> * @param type the type of stock to return * @return the array of objects of the type */ object *query_stock(string type) { mapping blue; blue = (mapping)our_storeroom->query_shop_type_mapping(); if(!blue[type]) { return ({ }); } return blue[type]; } /* query_stock() */ /** * This method is called when the rep tries to buy stuff. * @param bing the stuff to buy */ void do_rep_buy(mixed *bing) { object rep, *obs; int cost; rep = bing[0]; obs = bing[1]; cost = bing[2]; rep->adjust_money(cost, "brass"); rep->query_current_room()->do_buy(obs, cost, rep); obs->move(present("Sales rep alias", rep->query_current_room())); call_out("set_up_return", 5, rep); } /* do_rep_buy() */ /** * This method sets up the rep to return home. * @param rep the rep to return home */ void set_up_return(object rep) { rep->add_property("goto destination", file_name(this_object())); rep->add_triggered_action("froggy", "goto_destination", this_object(), "rep_came_back"); } /* set_up_return() */ /** * This method is called when the rep gets back home. */ void rep_came_back() { int i; object *obs, *obs2, rep; obs = previous_object()->find_inv_match("all", previous_object()); obs2 = ({ }); for(i = 0; i < sizeof(obs); i++) { if(obs[i]->query_property("mine")) { continue; } if(obs[i]->query_property("money")) { continue; } obs[i]->move(our_storeroom); obs2 += ({obs[i]}); } if(sizeof(obs2)) { tell_room(this_object(), previous_object()->short()+" puts "+ query_multiple_short(obs2)+" into the stock.\n"); } rep = present("Sales rep alias", (object)previous_object()->query_current_room()); obs = all_inventory(this_object()); obs2 = ({ }); for(i = 0; i < sizeof(obs); i++) { if((object)obs[i]->query_property("merchant") == rep) { obs2 += ({ obs[i] }); } } if(sizeof(obs2)) { tell_room(this_object(), query_multiple_short(obs2+({previous_object()}), "one")+" go away.\n"); } else { tell_room(this_object(), previous_object()->short()+" goes away.\n"); } obs2->dest_me(); previous_object()->dest_me(); } /* rep_came_back() */ // Used to create guards to protect the shop. /** * This method is yused to handle guards to protect the shop. * @param tp the object which is shop lifting * @see event_shoplift() * @see set_shoplift_handler() */ void summon_guards(object tp) { object ob; int i; if(environment(tp) != this_object()) { return; } ob = environment(tp)->create_mercenary(0); if(!ob) { return; } ob->move(this_object(), "$N charge$s in to protect the shop!"); ob->attack_ob(tp); for(i = 0; i < random(5); i++) { ob = create_mercenary(0); ob->move(this_object(), "$N charge$s in to protect the shop!"); ob->attack_ob(tp); } } /* summon_guards() */ /** * This method controls whether or not a shop will accept currency * from the "default" area. * @param new_strict set this to 1 if the shop should only accept currency * from the current area it is in (determined by the "place" property). */ void set_strict_currency( int new_strict ) { _strict_currency = new_strict; } /* set_strict_currency() */ /** * This returns the value of the strict currency variable. * @return 0 if default currency is accepted, 1 if it is not. */ int query_strict_currency() { return _strict_currency; } /* query_strict_currency() */ /** * This method is called when a shop lift is done on the shop. * @param command_ob the command object * @param thief the theif doing the shop lifting * @param victim the victim of the shoplifiting, us I guess :) * @see event_shoplift() * @see set_shoplift_handler() */ void event_shoplift(object command_ob, object thief, object victim) { if(stringp(shoplift_handler)) { if(shoplift_handler != "none") { shoplift_handler->handle_shoplift(thief, victim); } } else if (functionp(shoplift_handler)) { evaluate(shoplift_handler, thief, victim); } else { "/obj/handlers/theft_handler"->handle_shoplift(thief, victim); } } /* event_shoplift() */ /** * This method is used by the shop to tidyup its inventory. It does this by * desting objects at random until it has reduced the inventory to 3/4 of its * maximum. */ void tidy_inventory() { object storeob; object *inventory; int i, inv_to_leave; int count; inv_to_leave = max_inventory - (max_inventory / 4); if(objectp(our_storeroom)) { storeob = our_storeroom; } else { our_storeroom->rabbit_away(); storeob = find_object(our_storeroom); } if(sizeof(all_inventory(storeob)) < inv_to_leave) return; inventory = all_inventory(storeob); while(sizeof(inventory) > inv_to_leave) { i = random(sizeof(inventory)); if(inventory[i]) inventory[i]->dest_me(); // safety code. if(count++ > 500) break; inventory = all_inventory(storeob); } } /** @ignore yes */ void dest_me() { if(our_storeroom) our_storeroom->dest_me(); ::dest_me(); } /** @ignore yes */ mixed *stats() { return ::stats()+({ ({"total sold", amount_sold}), ({"total bought", amount_bought}), ({"shop type", shop_type}), ({"shoplift handler", shoplift_handler }), ({"strict shop", strict_shop})}); } /* stats() */