// Original /std/shops/inherit/item_shop /** * Item shop inherit. * @author Pinkfish * @change /17/03/00 Aquilo Separated the storing and duplicating of * items which was moved to /std/shops/inherit/clone_on_demand * @see /std/shops/inherit/clone_on_demand */ #include <armoury.h> #include <money.h> #include <move_failures.h> #include <shops/item_shop.h> #define INFINITE_LEFT 10000 #define TYPE "item shop type" #define DISPLAY "display in shop" #define SHOP_INHERIT "/std/shops/inherit/" inherit SHOP_INHERIT + "clone_on_demand"; inherit SHOP_INHERIT + "open_shop"; inherit SHOP_INHERIT + "shop_event"; private nosave int _no_steal; private nosave int _steal_difficulty; private nosave string _shoplift_handler; private nosave function _list_func; private nosave function _browse_func; private nosave function _buy_func; private nosave function _sort_func; void setup_safe(); void do_banking(); int do_word_buy(string str); int do_word_browse(string str); int do_buy( object *things ); int do_browse( object *things ); void remove_property(string name); mixed query_property(string name); void set_max_float( int ); int query_max_float(); private nosave object _safe; private nosave int _hidden_safe; private nosave int _max_float; void create() { shop_event::create(); clone_on_demand::create(); this_object()->add_help_file("item_shop"); // Create default safe. The safe can be customised from the shop. setup_safe(); // Set default float. This can be customised from the shop. set_max_float( 20000 + random( 50000 ) ); } /* create() */ /** * query_safe - This method returns the object currently being used * as a safe in the shop. **/ object query_safe() { return _safe; } /** * This method creates the safe in the room. The safe is then used for * various thief related activities. */ void setup_safe() { if( !_safe ) { _safe = clone_object( ITEM_SHOP_ITEMS + "item_shop_safe" ); _safe->set_difficulty( 2 + random( 7 ) ); _safe->set_ownership( "shop" ); if ( this_object()->query_hidden_safe( _safe ) ) { this_object()->add_hidden_object( _safe ); } else { _safe->move( this_object() ); } } } /** * This allows the creators to set their own safe. */ void set_safe(object safe) { _safe = safe; if ( _safe ) { _safe->set_ownership( "shop" ); if ( this_object()->query_hidden_safe( _safe ) ) { this_object()->add_hidden_object( _safe ); } else { _safe->move( this_object() ); } } } /* set_safe() */ /** @ignore yes */ void reset() { ::reset(); if(!random(3)) { remove_property("inventory_loss"); // This is incremented by shoplifting } // Float gets used to buy stock. do_banking(); } mixed *switch_extra_properties(){ return ({ TYPE, DISPLAY }); } /** * 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 filter(match_objects_for_existence(str, ({ query_cont() })), (: query_num_items_left($1) > 0 :)); } /* 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) { object real_ob; real_ob = create_real_object(ob); return real_ob; } /* shoplift_success() */ string query_shoplift_response_handler() { return _shoplift_handler; } void set_shoplift_response_handler( string word ) { _shoplift_handler = word; } /** * This method sets the function to evaluate to detmine if the shop * is open or not. * @param func the function to evaluate for openness * @see query_open_func() * @see set_open_condition() */ void set_open_func(function func) { ::set_open_function(func); } /* set_open_func() */ /** * This method makes it compatible with the way that the open stuff works in * the normal shops. * @param func the function to evaluate for openness * @see set_open_func() * @see query_open_func() */ void set_open_condition(function func) { ::set_open_function(func); } /* set_open_condition() */ /** * This method sets the function to use when buying something from * the shop. The function will be called with two arguements, the * first is the player, the second is the array of objects being * bought. * @param func the function to set * @see set_list_function() * @see set_buy_function() * @see set_browse_function() * @see query_buy_function() */ void set_buy_function(function func) { _buy_func = func; } /* set_buy_function() */ /** * This method returns the functionto be used when buying something. * @return the function to be used when buying something * @see query_list_function() * @see query_browse_function() * @see set_buy_function() */ function query_buy_function() { return _buy_func; } /* query_buy_function() */ /** * This method sets the function to use when browseing something from * the shop. The function will be called with two arguements, the * first is the player, the second is the array of objects being * browsed. * @param func the function to set * @see set_list_function() * @see set_buy_function() * @see set_browse_function() * @see query_browse_function() */ void set_browse_function(function func) { _browse_func = func; } /* set_browse_function() */ /** * This method returns the functionto be used when browseing something. * @return the function to be used when browseing something * @see query_list_function() * @see query_browse_function() * @see set_buy_function() */ function query_browse_function() { return _browse_func; } /* query_browse_function() */ /** * This method sets the function to use when listing something from * the shop. The function will be called with two arguements, the * first is the player, the second is the array of objects being * bought. * @param func the function to set * @see set_list_function() * @see set_buy_function() * @see set_browse_function() * @see query_list_function() */ void set_list_function(function func) { _list_func = func; } /* set_list_function() */ /** * This method returns the functionto be used when listing something. * @return the function to be used when listing something * @see query_buy_function() * @see query_browse_function() * @see set_list_function() */ function query_list_function() { return _list_func; } /* query_list_function() */ /** * This method sets a function to be used to sort the inventory when it * is printed out. * @param func the function to use to sort the inventory * @see query_sort_function() */ void set_sort_function(function func) { _sort_func = func; } /* set_sort_function() */ /** * This method returns the function used to sort the inventory of the * shop when it is printed out. * @return the function to use to sort the inventory * @see set_sort_function() */ function query_sort_function() { return _sort_func; } /* query_sort_function() */ /** * Sets this room as being unstealable. * @param i 1 if the room is unable to be stolen from * @see query_no_steal() */ void set_no_steal(int i) { _no_steal = i; } /** * Queries to see if the room is unstealable. * @return 1 for making the room unstealable * @see set_no_steal() */ int query_no_steal() { return _no_steal; } /** * Sets the steal difficulty of the room. * @param i the steal difficulty of the room */ void set_steal_difficulty(int i) { _steal_difficulty = i; } /** * This method returns the steal difficulty of the room. * @return the steal difficulty of the room */ int query_steal_difficulty(int i) { return _steal_difficulty; } /** @ignore yes */ void init() { if (query_cont()) { add_command( "list", "[all]" ); add_command( "browse", "<indirect:object:"+file_name(query_cont())+">" ); add_command( "browse", "<word>", (: do_word_browse($4[0]) :) ); add_command( "buy", "<indirect:object:"+file_name(query_cont())+">" ); add_command( "buy", "<word>", (: do_word_buy($4[0]) :) ); } } /* init() */ private object find_object_from_number(int number) { object *things; things = all_inventory( query_cont() ); if ( number >= sizeof( things ) ) { add_failed_mess( "There is no item with that label.\n" ); return 0; } if (_sort_func) { things = sort_array(things, _sort_func); } return things[number]; } /* find_object_from_number() */ private string letter_name(int letter, object* things) { string bing; if (sizeof(things) > 26) { bing = "AA"; bing[0] = 'A' + (letter / 26); bing[1] = 'A' + (letter % 26); return bing; } bing = "A"; bing[0] = 'A' + letter; return bing; } /* letter_name() */ private string start_letter() { object *things; things = all_inventory( query_cont() ); return letter_name(0, things); } /* start_letter() */ private string end_letter() { object *things; things = all_inventory( query_cont() ); return letter_name(sizeof(things) - 1, things); } /* end_letter() */ private int query_number_from_string(string name) { object *things; int pos; things = all_inventory( query_cont() ); if (sizeof(things) > 26) { if (strlen(name) != 2) { return -1; } name = lower_case(name); if (name[0] < 'a' || name[0] > 'z') { return -1; } if (name[1] < 'a' || name[1] > 'z') { return -1; } pos = (name[0] - 'a') * 26 + name[1] - 'a'; if (pos >= sizeof(things)) { return -1; } return pos; } if (strlen(name) != 1) { return -1; } name = lower_case(name); if (name[0] < 'a' || name[0] > 'z') { return -1; } pos = name[0] - 'a'; if (pos >= sizeof(things)) { return -1; } return pos; } /* query_number_from_string() */ /** * This method returns the cost of the item shop object being talked about. * @param thing the thing to get the cost of * @return the cost of the thing */ int query_cost( object thing ) { int cost; string place; if (!thing) { return 0; } if ( environment( thing ) == query_cont() ) { cost = (int)thing->query_property( "cost here" ); if ( cost ) { return cost; } } cost = (int)thing->query_value_at( this_object() ); place = query_property( "place" ); if ( !place ) { return cost; } /* This avoids problems with rounding errors. */ cost = (int)MONEY_HAND->query_total_value( (mixed *)MONEY_HAND->create_money_array( cost, place ), place ); if ( environment( thing ) == query_cont() ) { thing->add_property( "cost here", cost ); } return cost; } /* query_cost() */ /** @ignore yes */ int do_list( string str ) { int i; int left; string list; string place; string display; object thing; object *listed; object *things; if (!is_open(this_player(), 0)) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_CLOSE, this_player())) { add_failed_mess("The shop is not open.\n"); } return 0; } listed = ({ }); list = ""; place = query_property( "place" ); if ( !place || ( place == "" ) ) { place = "default"; } if ( !query_cont() ) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_NOTHING, this_player())) { add_failed_mess( "There is nothing to buy here.\n" ); } return 0; } things = all_inventory( query_cont() ); if (_sort_func) { things = sort_array(things, _sort_func); } foreach ( thing in things ) { left = query_num_items_left(thing); if (!left) { i++; continue; } display = (string)thing->query_property( DISPLAY ); if ( !stringp( display ) && thing->short()) { display = (string)thing->a_short(); } if (display) { list += "$I$6= "+ letter_name(i, things) +": "+ display +" for "+ (string)MONEY_HAND->money_value_string( (int)this_object()->query_cost( thing ), place ); if ( left == INFINITE_LEFT ) { list += " (plenty left).$I$0=\n"; } else { list += " (" + query_num( left, 0 ) +" left).\n"; } listed += ({ thing }); } i++; } if ( list == "" ) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_NOTHING, this_player())) { add_failed_mess( "There is nothing to buy here.\n" ); } return 0; } if (!broadcast_shop_event(ITEM_SHOP_EVENT_LIST, this_player(), list)) { list = "/global/events"->convert_message(list); write("$P$item list$P$" + "The following items are for sale:\n" + list); add_succeeded_mess(({ "", "$N list$s the inventory.\n" }), ({ })); } if (_list_func) { evaluate(_list_func, this_player(), listed); } return 1; } /* do_list() */ /** @ignore yes */ int do_word_buy(string str) { object thing; int i; if (!is_open(this_player(), 0)) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_CLOSE, this_player())) { add_failed_mess("The shop is not open.\n"); } return 0; } if ( strlen( str ) <= 2 ) { i = query_number_from_string( str ); if ( i == -1) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_BAD_LABEL, this_player(), str)) { add_failed_mess( "The item label must be from " + start_letter() + " to " + end_letter() + ".\n" ); } return 0; } thing = find_object_from_number(i); if ( !thing) { return 0; } return do_buy( ({ thing }) ); } return 0; } /* do_word_buy() */ /** @ignore yes */ int do_buy( object *things ) { int i, cost, value, ret, money_in_safe; string place; object thing, money, *sold, ob, *obs; mixed *money_arr, temp; if (!is_open(this_player(), 0)) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_CLOSE, this_player())) { add_failed_mess("The shop is not open.\n"); } return 0; } sold = ({ }); for (i = 0; i < sizeof(things); i++) { if (query_num_items_left(things[i]) <= 0) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_SOLD_OUT, this_player(), things[i..i])) { add_failed_mess( "The shop is sold out of "+ (string)things[ i ]->query_plural() +".\n" ); } return 0; } place = query_property( "place" ); if ( !place || ( place == "" ) ) { place = "default"; } value = (int)this_player()->query_value_in( place ); /* if ( place != "default" ) { value += (int)this_player()->query_value_in( "default" ); } */ cost = (int)this_object()->query_cost( things[i] ); if ( cost > value ) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_TOO_EXPENSIVE, this_player(), things[i], cost, value)) { add_failed_mess("You cannot afford to buy $I.\n", ({ things[i] })); } } else { thing = create_real_object(things[i]); if (!thing){ add_failed_mess( "Something is hosed. Please file a bug " "report. Thank you.\n" ); return 0; } ret = 1; this_player()->pay_money( (mixed *)MONEY_HAND->create_money_array( cost, place ), place ); // Put the money in the safe and lock it again. if( _safe ) { money_arr = MONEY_HAND->create_money_array( cost, place ); money = clone_object( "/obj/money" ); money->set_money_array( money_arr ); money->move( _safe ); _safe->set_closed(); _safe->set_locked(); // Check if banking should be done. obs = all_inventory( _safe ); money_in_safe = 0; foreach( ob in obs ) { if( ob->query_property( "money" ) ) { temp = ob->query_money_array(); money_in_safe += MONEY_HAND->query_total_value( temp, place ); } } if( money_in_safe >= query_max_float() ) do_banking(); } if (!broadcast_shop_event(ITEM_SHOP_EVENT_BUY_THING, this_player(), thing)) { add_succeeded_mess("$N $V $I.\n", ({ thing }) ); } this_object()->something_bought( thing, this_player(), cost ); if ( query_property( "package items" ) ) { thing = (object)this_object()->package_item( thing ); } if ( (int)thing->move( this_player() ) != MOVE_OK ) { thing->move( this_object() ); write( "You cannot pick "+ (string)thing->a_short() + " up. It's left on the floor for you.\n" ); } sold += ({ thing }); } } if (_buy_func) { evaluate(_buy_func, this_player(), sold); } return ret; } /* do_buy() */ /** @ignore yes */ int do_word_browse( string words ) { int i; object thing; if (!is_open(this_player(), 0)) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_CLOSE, this_player())) { add_failed_mess("The shop is not open.\n"); } return 0; } if ( strlen( words ) <= 2 ) { i = query_number_from_string( words ); if ( i == -1) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_BAD_LABEL, this_player(), words)) { add_failed_mess( "The item label must be from " + start_letter() + " to " + end_letter() + ".\n" ); } return 0; } thing = find_object_from_number(i); if ( !thing) { return 0; } return do_browse( ({ thing }) ); } return 0; } /* do_browse_word() */ /** @ignore yes */ int do_browse( object *things ) { int i; int num; string place; string mess; if (!is_open(this_player(), 0)) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_CLOSE, this_player())) { add_failed_mess("The shop is not open.\n"); } return 0; } place = query_property( "place" ); if ( !place || ( place == "" ) ) { place = "default"; } mess = ""; for (i = 0; i < sizeof(things); i++) { if (query_num_items_left(things[i])) { mess += things[i]->the_short() + " is priced at " + MONEY_HAND->money_value_string( this_object()->query_cost( things[ i ] ), place ) + " and looks like:\n" + things[ i ]->long(); num++; } } if (!num) { if (!broadcast_shop_event(ITEM_SHOP_EVENT_SOLD_OUT, this_player(), things)) { add_failed_mess("The shop is sold out of $I.\n", things); } return 0; } if (!broadcast_shop_event(ITEM_SHOP_EVENT_BROWSE, this_player(), things, mess)) { write("$P$Browse things$P$" + mess); add_succeeded_mess(({ "", "$N $V $I.\n", }), things); } if (_browse_func) { evaluate(_browse_func, this_player(), things); } return 1; } /* do_browse() */ /** @ignore yes */ void dest_me() { ::dest_me(); } /* dest_me() */ /** @ignore yes */ void event_theft( object command_ob, object thief, object victim, object *stolen ) { int stolen_from, difficulty; if( _safe ) { if( victim == _safe ) { // Record how many times the safe has been stolen from. stolen_from = _safe->query_property( "stolen from" ); if( !stolen_from ) _safe->add_property( "stolen from", 1 ); else _safe->add_property( "stolen from", stolen_from + 1 ); // Up the difficulty but only up to 10 (crack uses a the relative // difficulty scheme). difficulty = _safe->query_difficulty(); if( difficulty + 1 <= 10 ) _safe->set_difficulty( difficulty + 2 ); else if( difficulty <= 10 ) _safe->set_difficulty( difficulty + 1 ); } } } /** @ignore yes */ 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 { "/obj/handlers/theft_handler"->handle_shoplift(thief, victim); } } /* event_shoplift() */ void do_banking() { object ob, *obs; debug_printf( "Doing banking.\n" ); if( _safe ) { obs = all_inventory( _safe ); foreach( ob in obs ) { if( ob->query_property( "money" ) ) { ob->move( "/room/rubbish" ); } } } } void set_max_float( int f ) { _max_float = f; } int query_max_float() { return _max_float; }